From 055adede183b02d107ed3939d5f50181d5fa33ad Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 30 Apr 2012 15:00:35 +0200 Subject: [PATCH] Fix detach from sleeping process, fix return from tail call The latter in a limited way, true tail recursion is still broken. --- ltrace-0.6.0-detach-sleeping.patch | 141 ++++++++++++++++ ltrace-0.6.0-tail-return.patch | 256 +++++++++++++++++++++++++++++ ltrace.spec | 6 +- 3 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 ltrace-0.6.0-detach-sleeping.patch create mode 100644 ltrace-0.6.0-tail-return.patch diff --git a/ltrace-0.6.0-detach-sleeping.patch b/ltrace-0.6.0-detach-sleeping.patch new file mode 100644 index 0000000..bac1b7e --- /dev/null +++ b/ltrace-0.6.0-detach-sleeping.patch @@ -0,0 +1,141 @@ +diff --git a/common.h b/common.h +index 2399e29..b6e10f2 100644 +--- a/common.h ++++ b/common.h +@@ -347,7 +347,6 @@ extern void continue_after_signal(pid_t pid, int signum); + extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); + extern void continue_after_breakpoint(Process * proc, struct breakpoint *sbp); + extern void continue_after_vfork(Process * proc); +-extern void ltrace_exiting(void); + extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); + extern void save_register_args(enum tof type, Process * proc); + extern int umovestr(Process * proc, void * addr, int len, void * laddr); +@@ -363,5 +362,21 @@ extern int task_kill (pid_t pid, int sig); + * any platform-specific knowledge of why it could be so. */ + void trace_fail_warning(pid_t pid); + ++/* A pair of functions called to initiate a detachment request when ++ * ltrace is about to exit. Their job is to undo any effects that ++ * tracing had and eventually detach process, perhaps by way of ++ * installing a process handler. ++ * ++ * OS_LTRACE_EXITING_SIGHANDLER is called from a signal handler ++ * context right after the signal was captured. It returns 1 if the ++ * request was handled or 0 if it wasn't. ++ * ++ * If the call to OS_LTRACE_EXITING_SIGHANDLER didn't handle the ++ * request, OS_LTRACE_EXITING is called when the next event is ++ * generated. Therefore it's called in "safe" context, without ++ * re-entrancy concerns, but it's only called after an even is ++ * generated. */ ++int os_ltrace_exiting_sighandler(void); ++void os_ltrace_exiting(void); + + extern struct ltelf main_lte; +diff --git a/handle_event.c b/handle_event.c +index e3d3d0a..725f50d 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -49,11 +49,12 @@ call_handler(Process * proc, Event * event) + } + + void +-handle_event(Event *event) { ++handle_event(Event *event) ++{ + if (exiting == 1) { +- exiting = 2; + debug(1, "ltrace about to exit"); +- ltrace_exiting(); ++ os_ltrace_exiting(); ++ exiting = 2; + } + debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", + event->proc ? event->proc->pid : -1, event->type); +diff --git a/libltrace.c b/libltrace.c +index 19bfafd..777ad1b 100644 +--- a/libltrace.c ++++ b/libltrace.c +@@ -48,9 +48,14 @@ signal_alarm(int sig) { + } + + static void +-signal_exit(int sig) { +- exiting = 1; ++signal_exit(int sig) ++{ + debug(1, "Received interrupt signal; exiting..."); ++ if (exiting != 0) ++ return; ++ ++ exiting = 1 + !!os_ltrace_exiting_sighandler(); ++ + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGALRM, signal_alarm); +diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c +index 021192f..b6c12ef 100644 +--- a/sysdeps/linux-gnu/events.c ++++ b/sysdeps/linux-gnu/events.c +@@ -104,6 +104,8 @@ next_qd_event(void) + return each_qd_event(&event_process_not_reenabling, NULL); + } + ++int linux_in_waitpid = 0; ++ + Event * + next_event(void) + { +@@ -124,7 +126,11 @@ next_event(void) + debug(DEBUG_EVENT, "event: No more traced programs: exiting"); + exit(0); + } ++ ++ linux_in_waitpid = 1; + pid = waitpid(-1, &status, __WALL); ++ linux_in_waitpid = 0; ++ + if (pid == -1) { + if (errno == ECHILD) { + debug(DEBUG_EVENT, "event: No more traced programs: exiting"); +diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c +index 82a4154..9ecea1e 100644 +--- a/sysdeps/linux-gnu/trace.c ++++ b/sysdeps/linux-gnu/trace.c +@@ -314,8 +314,8 @@ task_stopped(Process * task, void * data) + case ps_invalid: + case ps_tracing_stop: + case ps_zombie: +- case ps_sleeping: + return pcb_cont; ++ case ps_sleeping: + case ps_stop: + case ps_other: + return pcb_stop; +@@ -1005,7 +1005,7 @@ continue_after_syscall(Process * proc, int sysnum, int ret_p) + * detaches. + */ + void +-ltrace_exiting(void) ++os_ltrace_exiting(void) + { + struct opt_p_t * it; + for (it = opt_p; it != NULL; it = it->next) { +@@ -1019,6 +1019,17 @@ ltrace_exiting(void) + } + } + ++int ++os_ltrace_exiting_sighandler(void) ++{ ++ extern int linux_in_waitpid; ++ if (linux_in_waitpid) { ++ os_ltrace_exiting(); ++ return 1; ++ } ++ return 0; ++} ++ + size_t + umovebytes(Process *proc, void *addr, void *laddr, size_t len) { + diff --git a/ltrace-0.6.0-tail-return.patch b/ltrace-0.6.0-tail-return.patch new file mode 100644 index 0000000..1af5e61 --- /dev/null +++ b/ltrace-0.6.0-tail-return.patch @@ -0,0 +1,256 @@ +diff --git a/handle_event.c b/handle_event.c +index 725f50d..8c3c7ce 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -565,11 +565,21 @@ void *get_count_register (Process *proc); + } + + static void ++output_right_tos(struct Process *proc) ++{ ++ size_t d = proc->callstack_depth; ++ struct callstack_element *elem = &proc->callstack[d - 1]; ++ if (proc->state != STATE_IGNORED) ++ output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc->name); ++} ++ ++static void + handle_breakpoint(Event *event) + { + int i, j; + struct breakpoint *sbp; + Process *leader = event->proc->leader; ++ void *brk_addr = event->e_un.brk_addr; + + /* The leader has terminated. */ + if (leader == NULL) { +@@ -577,12 +587,14 @@ handle_breakpoint(Event *event) + return; + } + +- debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); +- debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); ++ debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", ++ event->proc->pid, brk_addr); ++ debug(2, "event: breakpoint (%p)", brk_addr); + + for (i = event->proc->callstack_depth - 1; i >= 0; i--) { +- if (event->e_un.brk_addr == +- event->proc->callstack[i].return_addr) { ++ if (brk_addr == event->proc->callstack[i].return_addr) { ++ struct library_symbol *libsym = ++ event->proc->callstack[i].c_un.libfunc; + #ifdef __powerpc__ + /* + * PPC HACK! (XXX FIXME TODO) +@@ -623,8 +625,6 @@ handle_breakpoint(Event *event) + * so be sure to re-enable the breakpoint. + */ + unsigned long a; +- struct library_symbol *libsym = +- event->proc->callstack[i].c_un.libfunc; + void *addr = sym2addr(event->proc, libsym); + + if (libsym->plt_type != LS_TOPLT_POINT) { +@@ -668,19 +679,37 @@ handle_breakpoint(Event *event) + calc_time_spent(event->proc); + } + } +- event->proc->return_addr = event->e_un.brk_addr; +- if (event->proc->state != STATE_IGNORED) { +- output_right(LT_TOF_FUNCTIONR, event->proc, +- event->proc->callstack[i].c_un.libfunc->name); +- } ++ event->proc->return_addr = brk_addr; ++ ++ output_right_tos(event->proc); + callstack_pop(event->proc); +- sbp = address2bpstruct(leader, event->e_un.brk_addr); ++ ++ /* Pop also any other entries that seem like ++ * they are linked to the current one: they ++ * have the same return address, but were made ++ * for different symbols. This should only ++ * happen for entry point tracing, i.e. for -x ++ * everywhere, or -x and -e on PPC64. */ ++ while (event->proc->callstack_depth > 0) { ++ struct callstack_element *prev; ++ size_t d = event->proc->callstack_depth; ++ prev = &event->proc->callstack[d - 1]; ++ ++ if (prev->c_un.libfunc == libsym ++ || prev->return_addr != brk_addr) ++ break; ++ ++ output_right_tos(event->proc); ++ callstack_pop(event->proc); ++ } ++ ++ sbp = address2bpstruct(leader, brk_addr); + continue_after_breakpoint(event->proc, sbp); + return; + } + } + +- if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { ++ if ((sbp = address2bpstruct(leader, brk_addr))) { + breakpoint_on_hit(sbp, event->proc); + + if (sbp->libsym == NULL) { +@@ -711,7 +740,7 @@ handle_breakpoint(Event *event) + + if (event->proc->state != STATE_IGNORED && !options.no_plt) { + output_line(event->proc, "unexpected breakpoint at %p", +- (void *)event->e_un.brk_addr); ++ brk_addr); + } + continue_process(event->proc->pid); + } +@@ -742,7 +771,7 @@ callstack_push_syscall(Process *proc, int sysnum) { + + static void + callstack_push_symfunc(Process *proc, struct library_symbol *sym) { +- struct callstack_element *elem, *prev; ++ struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); + /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ +@@ -752,8 +781,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + return; + } + +- prev = &proc->callstack[proc->callstack_depth-1]; +- elem = &proc->callstack[proc->callstack_depth]; ++ elem = &proc->callstack[proc->callstack_depth++]; + elem->is_syscall = 0; + elem->c_un.libfunc = sym; + +@@ -763,8 +791,6 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + } + + /* handle functions like atexit() on mips which have no return */ +- if (elem->return_addr != prev->return_addr) +- proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; + gettimeofday(&elem->time_spent, &tz); +diff --git a/testsuite/ltrace.main/branch_func.c b/testsuite/ltrace.main/branch_func.c +new file mode 100644 +index 0000000..0ce311d +--- /dev/null ++++ b/testsuite/ltrace.main/branch_func.c +@@ -0,0 +1,51 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++/* This is specially made to produce tail calls. We then trace them ++ * and see if ltrace handles it well, or whether its internal stack ++ * overflows. */ ++ ++__attribute__((noinline, optimize(3))) int ++func3(int i) ++{ ++ return i + 1; ++} ++ ++__attribute__((noinline, optimize(3))) int ++func2(int i) ++{ ++ return func3(i * 3); ++} ++ ++__attribute__((noinline, optimize(3))) int ++func1(int i) ++{ ++ return func2(i + 2); ++} ++ ++__attribute__((optimize(0))) int ++main(int argc, char **argv) ++{ ++ int counter = 0; ++ int i; ++ for (i = 0; i < 100; ++i) ++ counter += func1(i); ++ return counter; ++} +diff --git a/testsuite/ltrace.main/branch_func.exp b/testsuite/ltrace.main/branch_func.exp +new file mode 100644 +index 0000000..fec5700 +--- /dev/null ++++ b/testsuite/ltrace.main/branch_func.exp +@@ -0,0 +1,57 @@ ++# This file is part of ltrace. ++# Copyright (C) 2012 Petr Machata, Red Hat Inc. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License as ++# published by the Free Software Foundation; either version 2 of the ++# License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++# 02110-1301 USA ++ ++set testfile "branch_func" ++set srcfile ${testfile}.c ++set binfile ${testfile} ++ ++if [get_compiler_info $binfile] { ++ return -1 ++} ++ ++verbose "compiling source file now....." ++if { [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable {debug} ] != "" } { ++ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." ++} ++ ++# set options for ltrace. ++ltrace_options "-x" "func1" "-x" "func2" "-x" "func3" ++ ++# Run PUT for ltarce. ++set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] ++ ++# Check the output of this program. ++verbose "ltrace runtest output: $exec_output\n" ++if [regexp {ELF from incompatible architecture} $exec_output] { ++ fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" ++ return ++} elseif [ regexp {Couldn't get .hash data} $exec_output ] { ++ fail "Couldn't get .hash data!" ++ return ++} ++ ++set pattern "func1(.*unfinished" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 ++set pattern "func2(.*unfinished" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 ++set pattern "func3(.*)" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 ++set pattern "func2.resumed" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 ++set pattern "func1.resumed" ++ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 100 diff --git a/ltrace.spec b/ltrace.spec index f1c92a4..cb27ee3 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,7 +1,7 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace Version: 0.6.0 -Release: 10%{?dist} +Release: 11%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers @@ -29,6 +29,8 @@ Patch11: ltrace-0.6.0-vfork.patch Patch12: ltrace-0.6.0-thread-races.patch Patch13: ltrace-0.6.0-process-start.patch Patch14: ltrace-0.6.0-selinux.patch +Patch15: ltrace-0.6.0-detach-sleeping.patch +Patch16: ltrace-0.6.0-tail-return.patch %description Ltrace is a debugging program which runs a specified command until the @@ -56,6 +58,8 @@ execution of processes. %patch12 -p1 %patch13 -p1 %patch14 -p1 +%patch15 -p1 +%patch16 -p1 sed -i -e 's/-o root -g root//' Makefile.in %build