Blob Blame History Raw
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