diff --git a/.gitignore b/.gitignore index 6374c2e..1449a76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ ltrace-0.5-svn45.tar.gz ltrace-*/ /ltrace-0.6.0.tar.bz2 +/ltrace-0.7.0.tar.bz2 +/ltrace-0.7.2.tar.bz2 diff --git a/ltrace-0.5-etc-memmove.patch b/ltrace-0.5-etc-memmove.patch deleted file mode 100644 index 1e0ac28..0000000 --- a/ltrace-0.5-etc-memmove.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff -up ltrace-0.5/etc/ltrace.conf\~ ltrace-0.5/etc/ltrace.conf ---- ltrace-0.5/etc/ltrace.conf~ 2006-02-20 22:55:47.000000000 +0100 -+++ ltrace-0.5/etc/ltrace.conf 2010-12-09 12:24:31.000000000 +0100 -@@ -237,6 +237,7 @@ string basename(string); - string index(string,char); - addr memchr(string,char,ulong); - addr memcpy(addr,string3,ulong); -+addr memmove(addr,string3,ulong); - addr memset(addr,char,long); - string rindex(string,char); - addr stpcpy(addr,string); - -Diff finished. Thu Dec 9 12:24:37 2010 diff --git a/ltrace-0.5-ia64-sigill.patch b/ltrace-0.5-ia64-sigill.patch deleted file mode 100644 index 9dab07b..0000000 --- a/ltrace-0.5-ia64-sigill.patch +++ /dev/null @@ -1,84 +0,0 @@ -diff -Nurp ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.exp ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.exp ---- ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.exp 1969-12-31 19:00:00.000000000 -0500 -+++ ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.exp 2007-05-04 13:29:43.000000000 -0400 -@@ -0,0 +1,33 @@ -+# This file was written by Yao Qi . -+ -+set testfile "ia64-sigill" -+set srcfile ${testfile}.s -+set binfile ${testfile} -+ -+if { [istarget ia64-*] } then { -+ verbose "compiling source file now....." -+ # Build the shared libraries this test case needs. -+ if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.s" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." -+ } -+ -+ # Run PUT for ltarce. -+ set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$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 -+ } -+ -+ catch "exec sh -c {grep SIGILL ${srcdir}/${subdir}/${testfile}.ltrace | wc -l ;exit}" output -+ if { $output == 0 } then { -+ pass "ltrace did interpret SIGILL as breakpoint." -+ } else { -+ fail "ltrace failed to interpret SIGILL as breakpoint." -+ } -+} -diff -Nurp ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.s ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.s ---- ltrace-0.5-orig/testsuite/ltrace.torture/ia64-sigill.s 1969-12-31 19:00:00.000000000 -0500 -+++ ltrace-0.5/testsuite/ltrace.torture/ia64-sigill.s 2007-05-04 12:51:14.000000000 -0400 -@@ -0,0 +1,43 @@ -+ .file "pokus.c" -+ .pred.safe_across_calls p1-p5,p16-p63 -+ .section .rodata -+ .align 8 -+.LC0: -+ stringz "" -+ .text -+ .align 16 -+ .global main# -+ .proc main# -+main: -+ .prologue 14, 32 -+ .save ar.pfs, r33 -+ alloc r33 = ar.pfs, 0, 4, 1, 0 -+ .vframe r34 -+ mov r34 = r12 -+ mov r35 = r1 -+ .save rp, r32 -+ mov r32 = b0 -+ .body -+ addl r36 = @ltoffx(.LC0), r1 -+ ;; -+ ld8.mov r36 = [r36], .LC0 -+ br.call.sptk.many b0 = printf# -+ nop.b 0x0 -+ nop.b 0x1 -+ nop.b 0x2 -+ nop.b 0x0 -+ nop.b 0x1 -+ nop.b 0x2 -+ mov r1 = r35 -+ addl r14 = 234, r0 -+ ;; -+ mov r8 = r14 -+ mov ar.pfs = r33 -+ mov b0 = r32 -+ .restore sp -+ mov r12 = r34 -+ br.ret.sptk.many b0 -+ ;; -+ .endp main# -+ .section .note.GNU-stack,"",@progbits -+ .ident "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-3)" diff --git a/ltrace-0.6.0-clone-test.patch b/ltrace-0.6.0-clone-test.patch deleted file mode 100644 index c01221b..0000000 --- a/ltrace-0.6.0-clone-test.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/testsuite/ltrace.minor/trace-clone.c b/testsuite/ltrace.minor/trace-clone.c -index a1ccb22..6e1c809 100644 ---- a/testsuite/ltrace.minor/trace-clone.c -+++ b/testsuite/ltrace.minor/trace-clone.c -@@ -4,6 +4,7 @@ - - This file was written by Yao Qi . */ - -+#define _GNU_SOURCE - #include - #include - #include diff --git a/ltrace-0.6.0-endian.patch b/ltrace-0.6.0-endian.patch deleted file mode 100644 index 7c12043..0000000 --- a/ltrace-0.6.0-endian.patch +++ /dev/null @@ -1,203 +0,0 @@ -diff --git a/sysdeps/linux-gnu/alpha/arch.h b/sysdeps/linux-gnu/alpha/arch.h -index 1107b5f..2dfeec7 100644 ---- a/sysdeps/linux-gnu/alpha/arch.h -+++ b/sysdeps/linux-gnu/alpha/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE { 0x80, 0x00, 0x00, 0x00 } - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 4 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS64 - #define LT_ELF_MACHINE EM_ALPHA -diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h -index 8f2dfb3..e2a62ab 100644 ---- a/sysdeps/linux-gnu/arm/arch.h -+++ b/sysdeps/linux-gnu/arm/arch.h -@@ -6,6 +6,7 @@ - #define THUMB_BREAKPOINT_VALUE { 0x01, 0xde } - #define THUMB_BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_ARM -diff --git a/sysdeps/linux-gnu/i386/arch.h b/sysdeps/linux-gnu/i386/arch.h -index dc7383f..15f0d78 100644 ---- a/sysdeps/linux-gnu/i386/arch.h -+++ b/sysdeps/linux-gnu/i386/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE {0xcc} - #define BREAKPOINT_LENGTH 1 - #define DECR_PC_AFTER_BREAK 1 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_386 -diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h -index 673047c..d7e0d23 100644 ---- a/sysdeps/linux-gnu/ia64/arch.h -+++ b/sysdeps/linux-gnu/ia64/arch.h -@@ -4,6 +4,7 @@ - #define BREAKPOINT_LENGTH 16 - #define BREAKPOINT_VALUE {0} - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS64 - #define LT_ELF_MACHINE EM_IA_64 -diff --git a/sysdeps/linux-gnu/m68k/arch.h b/sysdeps/linux-gnu/m68k/arch.h -index 1790d09..44fad89 100644 ---- a/sysdeps/linux-gnu/m68k/arch.h -+++ b/sysdeps/linux-gnu/m68k/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE { 0x4e, 0x4f } - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 -+#define ARCH_ENDIAN_BIG - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_68K -diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h -index dd0ca35..45c4598 100644 ---- a/sysdeps/linux-gnu/mipsel/arch.h -+++ b/sysdeps/linux-gnu/mipsel/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE { 0x0d, 0x00, 0x00, 0x00 } - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_MIPS -diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h -index 711b4a3..8b955e8 100644 ---- a/sysdeps/linux-gnu/ppc/arch.h -+++ b/sysdeps/linux-gnu/ppc/arch.h -@@ -14,7 +14,7 @@ - #define PLT_REINITALISATION_BP "_start" - - /* Start of arch-specific functions. */ --#define ARCH_HAVE_UMOVELONG -+#define ARCH_ENDIAN_BIG - - #define PPC_NOP { 0x60, 0x00, 0x00, 0x00 } - #define PPC_NOP_LENGTH 4 -diff -up ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c\~ ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c ---- ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c~ 2011-02-14 16:48:25.000000000 +0100 -+++ ltrace-0.6.0/sysdeps/linux-gnu/ppc/trace.c 2011-09-03 00:18:14.518317665 +0200 -@@ -156,34 +156,3 @@ save_register_args(enum tof type, Proces - memcpy(&arch->regs_copy, &arch->regs, sizeof(arch->regs)); - memcpy(&arch->fpregs_copy, &arch->fpregs, sizeof(arch->fpregs)); - } -- --/* Read a single long from the process's memory address 'addr'. */ --int --arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { -- long pointed_to; -- -- errno = 0; -- -- pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0); -- -- if (pointed_to == -1 && errno) -- return -errno; -- -- /* Since int's are 4-bytes (long is 8-bytes) in length for ppc64, we -- need to shift the long values returned by ptrace to end up with -- the correct value. */ -- -- if (info) { -- if (info->type == ARGTYPE_INT || (proc->mask_32bit && (info->type == ARGTYPE_POINTER -- || info->type == ARGTYPE_STRING))) { -- pointed_to = pointed_to >> 32; -- -- /* Make sure we have nothing in the upper word so we can -- do a explicit cast from long to int later in the code. */ -- pointed_to &= 0x00000000ffffffff; -- } -- } -- -- *result = pointed_to; -- return 0; --} -diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h -index 5cf168c..c6fed74 100644 ---- a/sysdeps/linux-gnu/s390/arch.h -+++ b/sysdeps/linux-gnu/s390/arch.h -@@ -6,6 +6,7 @@ - #define BREAKPOINT_VALUE { 0x00, 0x01 } - #define BREAKPOINT_LENGTH 2 - #define DECR_PC_AFTER_BREAK 2 -+#define ARCH_ENDIAN_BIG - - #ifdef __s390x__ - #define LT_ELFCLASS ELFCLASS64 -diff --git a/sysdeps/linux-gnu/sparc/arch.h b/sysdeps/linux-gnu/sparc/arch.h -index 75251b8..bec22ce 100644 ---- a/sysdeps/linux-gnu/sparc/arch.h -+++ b/sysdeps/linux-gnu/sparc/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01} - #define BREAKPOINT_LENGTH 4 - #define DECR_PC_AFTER_BREAK 0 -+#define ARCH_ENDIAN_BIG - - #define LT_ELFCLASS ELFCLASS32 - #define LT_ELF_MACHINE EM_SPARC -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index e4be465..9b0b980 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -10,6 +10,7 @@ - #include - - #include "common.h" -+#include "config.h" - - /* If the system headers did not provide the constants, hard-code the normal - values. */ -@@ -54,15 +55,23 @@ umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - if (pointed_to == -1 && errno) - return -errno; - -- *result = pointed_to; -- if (info) { -- switch(info->type) { -- case ARGTYPE_INT: -- *result &= 0x00000000ffffffffUL; -- default: -- break; -- }; -+#if SIZEOF_LONG == 8 -+ if (info != NULL -+ && (info->type == ARGTYPE_INT -+ || (proc->mask_32bit -+ && (info->type == ARGTYPE_POINTER -+ || info->type == ARGTYPE_STRING)))) { -+#if defined (ARCH_ENDIAN_LITTLE) -+ pointed_to &= 0x00000000ffffffffUL; -+#elif defined (ARCH_ENDIAN_BIG) -+ pointed_to = (long)(((unsigned long)pointed_to) >> 32); -+#else -+# error arch.h has to define endianness -+#endif - } -+#endif -+ -+ *result = pointed_to; - return 0; - } - #endif -diff --git a/sysdeps/linux-gnu/x86_64/arch.h b/sysdeps/linux-gnu/x86_64/arch.h -index 255395c..34376c4 100644 ---- a/sysdeps/linux-gnu/x86_64/arch.h -+++ b/sysdeps/linux-gnu/x86_64/arch.h -@@ -1,6 +1,7 @@ - #define BREAKPOINT_VALUE {0xcc} - #define BREAKPOINT_LENGTH 1 - #define DECR_PC_AFTER_BREAK 1 -+#define ARCH_ENDIAN_LITTLE - - #define LT_ELFCLASS ELFCLASS64 - #define LT_ELF_MACHINE EM_X86_64 diff --git a/ltrace-0.6.0-exec-stripped.patch b/ltrace-0.6.0-exec-stripped.patch deleted file mode 100644 index 25bd121..0000000 --- a/ltrace-0.6.0-exec-stripped.patch +++ /dev/null @@ -1,63 +0,0 @@ -diff -up ltrace-0.6.0/ltrace-elf.c\~ ltrace-0.6.0/ltrace-elf.c ---- ltrace-0.6.0/ltrace-elf.c~ 2011-02-14 16:48:25.000000000 +0100 -+++ ltrace-0.6.0/ltrace-elf.c 2011-02-14 18:13:03.000000000 +0100 -@@ -609,6 +609,7 @@ read_elf(Process *proc) { - struct ltelf lte[MAX_LIBRARIES + 1]; - size_t i; - struct opt_x_t *xptr; -+ struct opt_x_t *opt_x_loc = opt_x; - struct library_symbol **lib_tail = NULL; - int exit_out = 0; - int count = 0; -@@ -722,11 +723,11 @@ read_elf(Process *proc) { - main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); - if (main_cheat == NULL) - error(EXIT_FAILURE, 0, "Couldn't allocate memory"); -- main_cheat->next = opt_x; -+ main_cheat->next = opt_x_loc; - main_cheat->found = 0; - main_cheat->name = PLTs_initialized_by_here; - -- for (xptr = opt_x; xptr; xptr = xptr->next) -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 - && main_cheat) { - free(main_cheat); -@@ -734,7 +735,7 @@ read_elf(Process *proc) { - break; - } - if (main_cheat) -- opt_x = main_cheat; -+ opt_x_loc = main_cheat; - } - #endif - } else { -@@ -756,7 +757,7 @@ read_elf(Process *proc) { - if (!addr) - continue; - -- for (xptr = opt_x; xptr; xptr = xptr->next) -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if (xptr->name && strcmp(xptr->name, name) == 0) { - /* FIXME: Should be able to use &library_symbols as above. But - when you do, none of the real library symbols cause breaks. */ -@@ -769,7 +770,7 @@ read_elf(Process *proc) { - - unsigned found_count = 0; - -- for (xptr = opt_x; xptr; xptr = xptr->next) { -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) { - if (xptr->found) - continue; - -@@ -791,7 +792,7 @@ read_elf(Process *proc) { - } - } - -- for (xptr = opt_x; xptr; xptr = xptr->next) -+ for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if ( ! xptr->found) { - char *badthing = "WARNING"; - #ifdef PLT_REINITALISATION_BP - -Diff finished. Mon Feb 14 18:13:11 2011 diff --git a/ltrace-0.6.0-ppc-args.patch b/ltrace-0.6.0-ppc-args.patch deleted file mode 100644 index 43bfa2e..0000000 --- a/ltrace-0.6.0-ppc-args.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c -index 20b3f5d..321e6ec 100644 ---- a/sysdeps/linux-gnu/ppc/trace.c -+++ b/sysdeps/linux-gnu/ppc/trace.c -@@ -87,10 +87,22 @@ gimme_arg_regset(enum tof type, Process *proc, int arg_num, arg_type_info *info, - } - else if (greg <= 10) - return (*regs)[greg++]; -- else -+ else { -+#ifdef __powerpc64__ -+ if (proc->mask_32bit) -+ return ptrace (PTRACE_PEEKDATA, proc->pid, -+ proc->stack_pointer + 8 + -+ sizeof (int) * (arg_num - 8), 0) >> 32; -+ else -+ return ptrace (PTRACE_PEEKDATA, proc->pid, -+ proc->stack_pointer + 112 + -+ sizeof (long) * (arg_num - 8), 0); -+#else - return ptrace (PTRACE_PEEKDATA, proc->pid, -- proc->stack_pointer + sizeof (long) * -- (arg_num - 8), 0); -+ proc->stack_pointer + 8 + -+ sizeof (long) * (arg_num - 8), 0); -+#endif -+ } - - return 0; - } diff --git a/ltrace-0.6.0-ppc-shift.patch b/ltrace-0.6.0-ppc-shift.patch deleted file mode 100644 index 27d8b95..0000000 --- a/ltrace-0.6.0-ppc-shift.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c -index 980d028..668f63d 100644 ---- a/sysdeps/linux-gnu/ppc/plt.c -+++ b/sysdeps/linux-gnu/ppc/plt.c -@@ -44,12 +44,16 @@ sym2addr(Process *proc, struct library_symbol *sym) { - - pt_ret = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); - -+#if SIZEOF_LONG == 8 - if (proc->mask_32bit) { - // Assume big-endian. - addr = (void *)((pt_ret >> 32) & 0xffffffff); - } else { - addr = (void *)pt_ret; - } -+#else -+ addr = (void *)pt_ret; -+#endif - - return addr; - } diff --git a/ltrace-0.6.0-process-start.patch b/ltrace-0.6.0-process-start.patch deleted file mode 100644 index 1a95e25..0000000 --- a/ltrace-0.6.0-process-start.patch +++ /dev/null @@ -1,1053 +0,0 @@ -diff --git a/breakpoint.h b/breakpoint.h -new file mode 100644 -index 0000000..ce6f501 ---- /dev/null -+++ b/breakpoint.h -@@ -0,0 +1,106 @@ -+/* -+ * This file is part of ltrace. -+ * Copyright (C) 2012 Petr Machata, Red Hat Inc. -+ * Copyright (C) 2009 Juan Cespedes -+ * -+ * 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 -+ */ -+ -+#ifndef BREAKPOINT_H -+#define BREAKPOINT_H -+ -+/* XXX This is currently a very weak abstraction. We would like to -+ * much expand this to allow things like breakpoints on SDT probes and -+ * such. -+ * -+ * In particular, we would like to add a tracepoint abstraction. -+ * Tracepoint is a traceable feature--e.g. an exact address, a DWARF -+ * symbol, an ELF symbol, a PLT entry, or an SDT probe. Tracepoints -+ * are named and the user can configure which of them he wants to -+ * enable. Realized tracepoints enable breakpoints, which are a -+ * low-level realization of high-level tracepoint. -+ * -+ * Tracepoints are provided by the main binary as well as by any -+ * opened libraries: every time an ELF file is mapped into the address -+ * space, a new set of tracepoints is extracted, and filtered -+ * according to user settings. Those tracepoints that are left are -+ * then realized, and the tracing starts. -+ * -+ * A scheme like this would take care of gradually introducing -+ * breakpoints when the library is mapped, and therefore ready, and -+ * would avoid certain hacks. For example on PPC64, we don't actually -+ * add breakpoints to PLT. Instead, we read the PLT (which contains -+ * addresses, not code), to figure out where to put the breakpoints. -+ * In prelinked code, that address is non-zero, and points to an -+ * address that's not yet mapped. ptrace then fails when we try to -+ * add the breakpoint. -+ * -+ * Ideally, return breakpoints would be just a special kind of -+ * tracepoint that has attached some magic. Or a feature of a -+ * tracepoint. Service breakpoints like the handling of dlopen would -+ * be a low-level breakpoint, likely without tracepoint attached. -+ * -+ * So that's for sometimes. -+ */ -+ -+#include "arch.h" -+ -+struct Process; -+struct breakpoint; -+ -+struct bp_callbacks { -+ void (*on_hit) (struct breakpoint *bp, struct Process *proc); -+ void (*on_destroy) (struct breakpoint *bp); -+}; -+ -+struct breakpoint { -+ struct bp_callbacks *cbs; -+ void *addr; -+ unsigned char orig_value[BREAKPOINT_LENGTH]; -+ int enabled; -+ struct library_symbol *libsym; -+#ifdef __arm__ -+ int thumb_mode; -+#endif -+}; -+ -+/* Call on-hit handler of BP, if any is set. */ -+void breakpoint_on_hit(struct breakpoint *bp, struct Process *proc); -+ -+/* Call on-destroy handler of BP, if any is set. */ -+void breakpoint_on_destroy(struct breakpoint *bp); -+ -+/* This is actually three functions rolled in one: -+ * - breakpoint_init -+ * - proc_insert_breakpoint -+ * - breakpoint_enable -+ * XXX I think it should be broken up somehow. */ -+struct breakpoint *insert_breakpoint(struct Process *proc, void *addr, -+ struct library_symbol *libsym, int enable); -+ -+/* */ -+void delete_breakpoint(struct Process *proc, void *addr); -+ -+/* XXX some of the following belongs to proc.h/proc.c. */ -+struct breakpoint *address2bpstruct(struct Process *proc, void *addr); -+void enable_all_breakpoints(struct Process *proc); -+void disable_all_breakpoints(struct Process *proc); -+int breakpoints_init(struct Process *proc, int enable); -+ -+void reinitialize_breakpoints(struct Process *proc); -+ -+ -+#endif /* BREAKPOINT_H */ -diff --git a/breakpoints.c b/breakpoints.c -index 387b2a5..5713fe4 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -8,12 +8,30 @@ - #include - #endif - -+#include "breakpoint.h" - #include "common.h" - -+void -+breakpoint_on_hit(struct breakpoint *bp, struct Process *proc) -+{ -+ assert(bp != NULL); -+ if (bp->cbs != NULL && bp->cbs->on_hit != NULL) -+ (bp->cbs->on_hit) (bp, proc); -+} -+ -+void -+breakpoint_on_destroy(struct breakpoint *bp) -+{ -+ assert(bp != NULL); -+ if (bp->cbs != NULL && bp->cbs->on_destroy != NULL) -+ (bp->cbs->on_destroy) (bp); -+} -+ - /*****************************************************************************/ - --Breakpoint * --address2bpstruct(Process *proc, void *addr) { -+struct breakpoint * -+address2bpstruct(Process *proc, void *addr) -+{ - assert(proc != NULL); - assert(proc->breakpoints != NULL); - assert(proc->leader == proc); -@@ -21,10 +39,11 @@ address2bpstruct(Process *proc, void *addr) { - return dict_find_entry(proc->breakpoints, addr); - } - --void -+struct breakpoint * - insert_breakpoint(Process *proc, void *addr, -- struct library_symbol *libsym, int enable) { -- Breakpoint *sbp; -+ struct library_symbol *libsym, int enable) -+{ -+ struct breakpoint *sbp; - - Process * leader = proc->leader; - -@@ -43,16 +62,16 @@ insert_breakpoint(Process *proc, void *addr, - debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); - - if (!addr) -- return; -+ return NULL; - - if (libsym) - libsym->needs_init = 0; - - sbp = dict_find_entry(leader->breakpoints, addr); -- if (!sbp) { -- sbp = calloc(1, sizeof(Breakpoint)); -- if (!sbp) { -- return; /* TODO FIXME XXX: error_mem */ -+ if (sbp == NULL) { -+ sbp = calloc(1, sizeof(*sbp)); -+ if (sbp == NULL) { -+ return NULL; /* TODO FIXME XXX: error_mem */ - } - dict_enter(leader->breakpoints, addr, sbp); - sbp->addr = addr; -@@ -67,11 +86,14 @@ insert_breakpoint(Process *proc, void *addr, - assert(proc->pid != 0); - enable_breakpoint(proc, sbp); - } -+ -+ return sbp; - } - - void --delete_breakpoint(Process *proc, void *addr) { -- Breakpoint *sbp; -+delete_breakpoint(Process *proc, void *addr) -+{ -+ struct breakpoint *sbp; - - debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); - -@@ -91,89 +113,67 @@ delete_breakpoint(Process *proc, void *addr) { - } - - static void --enable_bp_cb(void *addr, void *sbp, void *proc) { -+enable_bp_cb(void *addr, void *sbp, void *proc) -+{ - debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); -- if (((Breakpoint *)sbp)->enabled) { -+ if (((struct breakpoint *)sbp)->enabled) - enable_breakpoint(proc, sbp); -- } - } - - void --enable_all_breakpoints(Process *proc) { -+enable_all_breakpoints(Process *proc) -+{ - debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); -- if (proc->breakpoints_enabled <= 0) { --#ifdef __powerpc__ -- unsigned long a; - -+ debug(1, "Enabling breakpoints for pid %u...", proc->pid); -+ if (proc->breakpoints) { -+ dict_apply_to_all(proc->breakpoints, enable_bp_cb, -+ proc); -+ } -+#ifdef __mips__ -+ { - /* -- * PPC HACK! (XXX FIXME TODO) -- * If the dynamic linker hasn't populated the PLT then -- * dont enable the breakpoints -+ * I'm sure there is a nicer way to do this. We need to -+ * insert breakpoints _after_ the child has been started. - */ -- if (options.libcalls) { -- a = ptrace(PTRACE_PEEKTEXT, proc->pid, -- sym2addr(proc, proc->list_of_symbols), -- 0); -- if (a == 0x0) -- return; -- } --#endif -- -- debug(1, "Enabling breakpoints for pid %u...", proc->pid); -- if (proc->breakpoints) { -- dict_apply_to_all(proc->breakpoints, enable_bp_cb, -- proc); -- } --#ifdef __mips__ -- { -- /* -- * I'm sure there is a nicer way to do this. We need to -- * insert breakpoints _after_ the child has been started. -- */ -- struct library_symbol *sym; -- struct library_symbol *new_sym; -- sym=proc->list_of_symbols; -- while(sym){ -- void *addr= sym2addr(proc,sym); -- if(!addr){ -- sym=sym->next; -- continue; -- } -- if(dict_find_entry(proc->breakpoints,addr)){ -- sym=sym->next; -- continue; -- } -- debug(2,"inserting bp %p %s",addr,sym->name); -- new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); -- memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); -- new_sym->next=proc->list_of_symbols; -- proc->list_of_symbols=new_sym; -- insert_breakpoint(proc, addr, new_sym); -+ struct library_symbol *sym; -+ struct library_symbol *new_sym; -+ sym=proc->list_of_symbols; -+ while(sym){ -+ void *addr= sym2addr(proc,sym); -+ if(!addr){ - sym=sym->next; -+ continue; - } -+ if(dict_find_entry(proc->breakpoints,addr)){ -+ sym=sym->next; -+ continue; -+ } -+ debug(2,"inserting bp %p %s",addr,sym->name); -+ new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); -+ memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); -+ new_sym->next=proc->list_of_symbols; -+ proc->list_of_symbols=new_sym; -+ insert_breakpoint(proc, addr, new_sym); -+ sym=sym->next; - } --#endif - } -- proc->breakpoints_enabled = 1; -+#endif - } - - static void --disable_bp_cb(void *addr, void *sbp, void *proc) { -+disable_bp_cb(void *addr, void *sbp, void *proc) -+{ - debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); -- if (((Breakpoint *)sbp)->enabled) { -+ if (((struct breakpoint *)sbp)->enabled) - disable_breakpoint(proc, sbp); -- } - } - - void - disable_all_breakpoints(Process *proc) { - debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); - assert(proc->leader == proc); -- if (proc->breakpoints_enabled) { -- debug(1, "Disabling breakpoints for pid %u...", proc->pid); -- dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); -- } -- proc->breakpoints_enabled = 0; -+ dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); - } - - static void -@@ -183,11 +183,18 @@ free_bp_cb(void *addr, void *sbp, void *data) { - free(sbp); - } - -+static void -+entry_callback_hit(struct breakpoint *bp, struct Process *proc) -+{ -+ if (proc == NULL || proc->leader == NULL) -+ return; -+ delete_breakpoint(proc, bp->addr); // xxx -+ reinitialize_breakpoints(proc->leader); -+} -+ - int - breakpoints_init(Process *proc, int enable) - { -- struct library_symbol *sym; -- - debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); - if (proc->breakpoints) { /* let's remove that struct */ - dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); -@@ -206,9 +213,11 @@ breakpoints_init(Process *proc, int enable) - destroy_library_symbol_chain(proc->list_of_symbols); - proc->list_of_symbols = NULL; - -+ GElf_Addr entry; - if (options.libcalls && proc->filename) { -- proc->list_of_symbols = read_elf(proc); -+ proc->list_of_symbols = read_elf(proc, &entry); - if (proc->list_of_symbols == NULL) { -+ fail: - /* XXX leak breakpoints */ - return -1; - } -@@ -235,11 +244,19 @@ breakpoints_init(Process *proc, int enable) - } - } - -- for (sym = proc->list_of_symbols; sym; sym = sym->next) -- insert_breakpoint(proc, sym2addr(proc, sym), sym, enable); -+ struct breakpoint *entry_bp -+ = insert_breakpoint(proc, (void *)(uintptr_t)entry, NULL, 1); -+ if (entry_bp == NULL) { -+ fprintf(stderr, "fail!\n"); -+ goto fail; -+ } -+ -+ static struct bp_callbacks entry_callbacks = { -+ .on_hit = entry_callback_hit, -+ }; -+ entry_bp->cbs = &entry_callbacks; - - proc->callstack_depth = 0; -- proc->breakpoints_enabled = -1; - return 0; - } - -diff --git a/common.h b/common.h -index 715898d..a47cd89 100644 ---- a/common.h -+++ b/common.h -@@ -26,17 +26,6 @@ extern char * command; - - extern int exiting; /* =1 if we have to exit ASAP */ - --typedef struct Breakpoint Breakpoint; --struct Breakpoint { -- void * addr; -- unsigned char orig_value[BREAKPOINT_LENGTH]; -- int enabled; -- struct library_symbol * libsym; --#ifdef __arm__ -- int thumb_mode; --#endif --}; -- - enum arg_type { - ARGTYPE_UNKNOWN = -1, - ARGTYPE_VOID, -@@ -187,11 +176,10 @@ struct Process { - pid_t pid; - - /* Dictionary of breakpoints (which is a mapping -- * address->Breakpoint). This is NULL for non-leader -+ * address->breakpoint). This is NULL for non-leader - * processes. */ - Dict * breakpoints; - -- int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ - int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ - unsigned int personality; - int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ -@@ -296,14 +284,7 @@ extern void destroy_event_handler(Process * proc); - - extern pid_t execute_program(const char * command, char ** argv); - extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); --extern Breakpoint * address2bpstruct(Process * proc, void * addr); --extern int breakpoints_init(Process * proc, int enable); --extern void insert_breakpoint(Process * proc, void * addr, -- struct library_symbol * libsym, int enable); --extern void delete_breakpoint(Process * proc, void * addr); --extern void enable_all_breakpoints(Process * proc); - extern void disable_all_breakpoints(Process * proc); --extern void reinitialize_breakpoints(Process *); - - extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); - extern void open_pid(pid_t pid); -@@ -322,6 +303,8 @@ extern struct library_symbol * clone_library_symbol(struct library_symbol * s); - extern void destroy_library_symbol(struct library_symbol * s); - extern void destroy_library_symbol_chain(struct library_symbol * chain); - -+struct breakpoint; -+ - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); - extern pid_t process_leader(pid_t pid); -@@ -329,6 +312,7 @@ extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); - extern int process_stopped(pid_t pid); - extern enum process_status process_status(pid_t pid); - extern void trace_set_options(Process * proc, pid_t pid); -+extern void wait_for_proc(pid_t pid); - extern void trace_me(void); - extern int trace_pid(pid_t pid); - extern void untrace_pid(pid_t pid); -@@ -338,13 +322,13 @@ extern void set_instruction_pointer(Process * proc, void * addr); - extern void * get_stack_pointer(Process * proc); - extern void * get_return_addr(Process * proc, void * stack_pointer); - extern void set_return_addr(Process * proc, void * addr); --extern void enable_breakpoint(Process * proc, Breakpoint * sbp); --extern void disable_breakpoint(Process * proc, Breakpoint * sbp); -+extern void enable_breakpoint(Process * proc, struct breakpoint *sbp); -+extern void disable_breakpoint(Process * proc, struct breakpoint *sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - 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, Breakpoint * sbp); -+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); -diff --git a/execute_program.c b/execute_program.c -index 47f514d..859f32c 100644 ---- a/execute_program.c -+++ b/execute_program.c -@@ -89,6 +89,8 @@ execute_program(const char * command, char **argv) - _exit(1); - } - -+ wait_for_proc(pid); -+ - debug(1, "PID=%d", pid); - - return pid; -diff --git a/handle_event.c b/handle_event.c -index 203459c..c146eb9 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -1,6 +1,6 @@ -+#define _GNU_SOURCE - #include "config.h" - --#define _GNU_SOURCE - #include - #include - #include -@@ -9,12 +9,13 @@ - #include - #include - --#include "common.h" -- - #ifdef __powerpc__ - #include - #endif - -+#include "common.h" -+#include "breakpoint.h" -+ - static void handle_signal(Event *event); - static void handle_exit(Event *event); - static void handle_exit_signal(Event *event); -@@ -155,19 +156,18 @@ address_clone(void * addr, void * data) - } - - static void * --breakpoint_clone(void * bp, void * data) -+breakpoint_clone(void *bp, void *data) - { -- Breakpoint * b; -- Dict * map = data; -+ Dict *map = data; - debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); -- b = malloc(sizeof(Breakpoint)); -+ struct breakpoint *b = malloc(sizeof(*b)); - if (!b) { - perror("malloc()"); - exit(1); - } -- memcpy(b, bp, sizeof(Breakpoint)); -+ memcpy(b, bp, sizeof(*b)); - if (b->libsym != NULL) { -- struct library_symbol * sym = dict_find_entry(map, b->libsym); -+ struct library_symbol *sym = dict_find_entry(map, b->libsym); - if (b->libsym == NULL) { - fprintf(stderr, "Can't find cloned symbol %s.\n", - b->libsym->name); -@@ -452,9 +452,6 @@ handle_syscall(Event *event) { - output_left(LT_TOF_SYSCALL, event->proc, - sysname(event->proc, event->e_un.sysnum)); - } -- if (event->proc->breakpoints_enabled == 0) { -- enable_all_breakpoints(event->proc); -- } - } - continue_after_syscall(event->proc, event->e_un.sysnum, 0); - } -@@ -462,7 +459,6 @@ handle_syscall(Event *event) { - static void - handle_exec(Event * event) { - Process * proc = event->proc; -- pid_t saved_pid; - - debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); - if (proc->state == STATE_IGNORED) { -@@ -476,10 +472,7 @@ handle_exec(Event * event) { - proc->arch_ptr = NULL; - free(proc->filename); - proc->filename = pid2name(proc->pid); -- saved_pid = proc->pid; -- proc->pid = 0; - breakpoints_init(proc, 0); -- proc->pid = saved_pid; - proc->callstack_depth = 0; - continue_process(proc->pid); - } -@@ -493,9 +486,6 @@ handle_arch_syscall(Event *event) { - output_left(LT_TOF_SYSCALL, event->proc, - arch_sysname(event->proc, event->e_un.sysnum)); - } -- if (event->proc->breakpoints_enabled == 0) { -- enable_all_breakpoints(event->proc); -- } - } - continue_process(event->proc->pid); - } -@@ -559,14 +549,11 @@ handle_arch_sysret(Event *event) { - continue_process(event->proc->pid); - } - --#ifdef __powerpc__ --void *get_count_register (Process *proc); --#endif -- - static void --handle_breakpoint(Event *event) { -+handle_breakpoint(Event *event) -+{ - int i, j; -- Breakpoint *sbp; -+ struct breakpoint *sbp; - Process *leader = event->proc->leader; - - /* The leader has terminated. */ -@@ -578,28 +565,6 @@ handle_breakpoint(Event *event) { - 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); - --#ifdef __powerpc__ -- /* Need to skip following NOP's to prevent a fake function from being stacked. */ -- long stub_addr = (long) get_count_register(event->proc); -- Breakpoint *stub_bp = NULL; -- char nop_instruction[] = PPC_NOP; -- -- stub_bp = address2bpstruct(leader, event->e_un.brk_addr); -- -- if (stub_bp) { -- unsigned char *bp_instruction = stub_bp->orig_value; -- -- if (memcmp(bp_instruction, nop_instruction, -- PPC_NOP_LENGTH) == 0) { -- if (stub_addr != (long) event->e_un.brk_addr) { -- set_instruction_pointer (event->proc, event->e_un.brk_addr + 4); -- continue_process(event->proc->pid); -- return; -- } -- } -- } --#endif -- - for (i = event->proc->callstack_depth - 1; i >= 0; i--) { - if (event->e_un.brk_addr == - event->proc->callstack[i].return_addr) { -@@ -679,6 +644,8 @@ handle_breakpoint(Event *event) { - } - - if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { -+ breakpoint_on_hit(sbp, event->proc); -+ - if (sbp->libsym == NULL) { - continue_after_breakpoint(event->proc, sbp); - return; -@@ -696,12 +663,6 @@ handle_breakpoint(Event *event) { - callstack_push_symfunc(event->proc, sbp->libsym); - output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); - } --#ifdef PLT_REINITALISATION_BP -- if (event->proc->need_to_reinitialize_breakpoints -- && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == -- 0)) -- reinitialize_breakpoints(leader); --#endif - - continue_after_breakpoint(event->proc, sbp); - return; -diff --git a/ltrace-elf.c b/ltrace-elf.c -index 8dbc298..f7fc239 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -662,7 +662,8 @@ opd2addr(struct ltelf *lte, GElf_Addr addr) { - } - - struct library_symbol * --read_elf(Process *proc) { -+read_elf(Process *proc, GElf_Addr *entryp) -+{ - struct ltelf lte[MAX_LIBRARIES + 1]; - size_t i; - struct opt_x_t *xptr; -@@ -700,7 +701,6 @@ read_elf(Process *proc) { - #ifdef __mips__ - // MIPS doesn't use the PLT and the GOT entries get changed - // on startup. -- proc->need_to_reinitialize_breakpoints = 1; - for(i=lte->mips_gotsym; idynsym_count;i++){ - GElf_Sym sym; - const char *name; -@@ -745,11 +745,6 @@ read_elf(Process *proc) { - "Couldn't get relocation from \"%s\"", - proc->filename); - --#ifdef PLT_REINITALISATION_BP -- if (!sym.st_value && PLTs_initialized_by_here) -- proc->need_to_reinitialize_breakpoints = 1; --#endif -- - name = lte->dynstr + sym.st_name; - count = library_num ? library_num+1 : 0; - -@@ -772,30 +767,6 @@ read_elf(Process *proc) { - } - } - #endif // !__mips__ --#ifdef PLT_REINITALISATION_BP -- struct opt_x_t *main_cheat; -- -- if (proc->need_to_reinitialize_breakpoints) { -- /* Add "PLTs_initialized_by_here" to opt_x list, if not -- already there. */ -- main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); -- if (main_cheat == NULL) -- error(EXIT_FAILURE, 0, "Couldn't allocate memory"); -- main_cheat->next = opt_x_loc; -- main_cheat->found = 0; -- main_cheat->name = PLTs_initialized_by_here; -- -- for (xptr = opt_x_loc; xptr; xptr = xptr->next) -- if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 -- && main_cheat) { -- free(main_cheat); -- main_cheat = NULL; -- break; -- } -- if (main_cheat) -- opt_x_loc = main_cheat; -- } --#endif - } else { - lib_tail = &library_symbols; - } -@@ -850,16 +821,17 @@ read_elf(Process *proc) { - } - } - -+ if (lte->ehdr.e_entry != 0) { -+ *entryp = opd2addr(lte, lte->ehdr.e_entry); -+ } else { -+ } -+ - for (xptr = opt_x_loc; xptr; xptr = xptr->next) - if ( ! xptr->found) { - char *badthing = "WARNING"; - #ifdef PLT_REINITALISATION_BP - if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) { - if (lte->ehdr.e_entry) { -- add_library_symbol ( -- opd2addr (lte, lte->ehdr.e_entry), -- PLTs_initialized_by_here, -- lib_tail, 1, 0); - fprintf (stderr, "WARNING: Using e_ent" - "ry from elf header (%p) for " - "address of \"%s\"\n", (void*) -diff --git a/ltrace-elf.h b/ltrace-elf.h -index 3b675c5..4da8a0a 100644 ---- a/ltrace-elf.h -+++ b/ltrace-elf.h -@@ -45,7 +45,7 @@ extern size_t library_num; - extern char *library[MAX_LIBRARIES]; - - extern int open_elf(struct ltelf *lte, const char *filename); --extern struct library_symbol *read_elf(Process *); -+extern struct library_symbol *read_elf(Process *proc, GElf_Addr *entryp); - - extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); - -diff --git a/proc.c b/proc.c -index f4d3396..5febc3f 100644 ---- a/proc.c -+++ b/proc.c -@@ -14,6 +14,7 @@ - #include - - #include "common.h" -+#include "breakpoint.h" - - Process * - open_program(char *filename, pid_t pid, int enable) { -@@ -26,7 +27,6 @@ open_program(char *filename, pid_t pid, int enable) { - } - - proc->filename = strdup(filename); -- proc->breakpoints_enabled = -1; - proc->pid = pid; - #if defined(HAVE_LIBUNWIND) - proc->unwind_priv = _UPT_create(pid); -@@ -39,13 +39,15 @@ open_program(char *filename, pid_t pid, int enable) { - return NULL; - } - -- if (proc->leader == proc) -+ if (proc->leader == proc) { -+ trace_set_options(proc, proc->pid); - if (breakpoints_init(proc, enable)) { - fprintf(stderr, "failed to init breakpoints %d\n", - proc->pid); - remove_process(proc); - return NULL; - } -+ } - - return proc; - } -@@ -73,11 +75,10 @@ open_one_pid(pid_t pid) - return 0; - } - --enum pcb_status -+static enum pcb_status - start_one_pid(Process * proc, void * data) - { - continue_process(proc->pid); -- proc->breakpoints_enabled = 1; - return pcb_cont; - } - -@@ -116,7 +117,7 @@ open_pid(pid_t pid) - if (process_tasks(pid, &tasks, &ntasks) < 0) { - fprintf(stderr, "Cannot obtain tasks of pid %u: %s\n", - pid, strerror(errno)); -- goto start; -+ break; - } - - have_all = 1; -@@ -135,7 +136,6 @@ open_pid(pid_t pid) - /* Done. Now initialize breakpoints and then continue - * everyone. */ - Process * leader; --start: - leader = pid2proc(pid)->leader; - enable_all_breakpoints(leader); - -diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c -index 4e17940..493f973 100644 ---- a/sysdeps/linux-gnu/arm/breakpoint.c -+++ b/sysdeps/linux-gnu/arm/breakpoint.c -@@ -27,7 +27,8 @@ - #include "common.h" - - void --arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { -+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) -+{ - unsigned int i, j; - const unsigned char break_insn[] = BREAKPOINT_VALUE; - const unsigned char thumb_break_insn[] = THUMB_BREAKPOINT_VALUE; -@@ -59,7 +60,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - - void --arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) -+{ - unsigned int i, j; - - debug(1, "arch_disable_breakpoint(%d,%p)", pid, sbp->addr); -diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c -index 5a49e9d..b98374b 100644 ---- a/sysdeps/linux-gnu/breakpoint.c -+++ b/sysdeps/linux-gnu/breakpoint.c -@@ -5,12 +5,13 @@ - - #include "common.h" - #include "arch.h" -+#include "breakpoint.h" - - #ifdef ARCH_HAVE_ENABLE_BREAKPOINT --extern void arch_enable_breakpoint(pid_t, Breakpoint *); -+extern void arch_enable_breakpoint(pid_t, struct breakpoint *); - #else /* ARCH_HAVE_ENABLE_BREAKPOINT */ - void --arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) -+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) - { - static unsigned char break_insn[] = BREAKPOINT_VALUE; - unsigned int i, j; -@@ -38,7 +39,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) - #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ - - void --enable_breakpoint(Process * proc, Breakpoint *sbp) { -+enable_breakpoint(Process *proc, struct breakpoint *sbp) -+{ - if (sbp->libsym) { - debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); - } else { -@@ -48,10 +50,10 @@ enable_breakpoint(Process * proc, Breakpoint *sbp) { - } - - #ifdef ARCH_HAVE_DISABLE_BREAKPOINT --extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); -+extern void arch_disable_breakpoint(pid_t, const struct breakpoint *sbp); - #else /* ARCH_HAVE_DISABLE_BREAKPOINT */ - void --arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) -+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) - { - unsigned int i, j; - -@@ -78,7 +80,8 @@ arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) - #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ - - void --disable_breakpoint(Process * proc, Breakpoint *sbp) { -+disable_breakpoint(Process *proc, struct breakpoint *sbp) -+{ - if (sbp->libsym) { - debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); - } else { -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 021192f..3174c1a 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -12,6 +12,7 @@ - #include - - #include "common.h" -+#include "breakpoint.h" - - static Event event; - -@@ -166,19 +167,9 @@ next_event(void) - } - get_arch_dep(event.proc); - debug(3, "event from pid %u", pid); -- if (event.proc->breakpoints_enabled == -1) -- trace_set_options(event.proc, event.proc->pid); - Process *leader = event.proc->leader; - if (leader == event.proc) { -- if (event.proc->breakpoints_enabled == -1) { -- event.type = EVENT_NONE; -- enable_all_breakpoints(event.proc); -- continue_process(event.proc->pid); -- debug(DEBUG_EVENT, -- "event: NONE: pid=%d (enabling breakpoints)", -- pid); -- return &event; -- } else if (!event.proc->libdl_hooked) { -+ if (!event.proc->libdl_hooked) { - /* debug struct may not have been written yet.. */ - if (linkmap_init(event.proc, &main_lte) == 0) { - event.proc->libdl_hooked = 1; -diff --git a/sysdeps/linux-gnu/ia64/breakpoint.c b/sysdeps/linux-gnu/ia64/breakpoint.c -index 45ee11e..a0bfaf9 100644 ---- a/sysdeps/linux-gnu/ia64/breakpoint.c -+++ b/sysdeps/linux-gnu/ia64/breakpoint.c -@@ -150,7 +150,8 @@ union bundle_t { - }; - - void --arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { -+arch_enable_breakpoint(pid_t pid, struct breakpoint *sbp) -+{ - - unsigned long addr = (unsigned long)sbp->addr; - union bundle_t bundle; -@@ -187,7 +188,8 @@ arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - - void --arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+arch_disable_breakpoint(pid_t pid, const struct breakpoint *sbp) -+{ - - unsigned long addr = (unsigned long)sbp->addr; - int slotnum = (int)(addr & 0x0f) & 0x3; -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index a99593c..3350117 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -1,6 +1,5 @@ - #define _GNU_SOURCE /* For getline. */ - #include "config.h" --#include "common.h" - - #include - #include -@@ -17,6 +16,8 @@ - #include - #include - -+#include "common.h" -+#include "breakpoint.h" - - /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' - * couldn't open it to find the executable. So it may be necessary to -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index db18df0..b4d8fd1 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -11,6 +11,7 @@ - - #include "common.h" - #include "config.h" -+#include "breakpoint.h" - - /* If the system headers did not provide the constants, hard-code the normal - values. */ -@@ -77,6 +78,29 @@ trace_me(void) { - } - } - -+/* There's a (hopefully) brief period of time after the child process -+ * exec's when we can't trace it yet. Here we wait for kernel to -+ * prepare the process. */ -+void -+wait_for_proc(pid_t pid) -+{ -+ size_t i; -+ for (i = 0; i < 100; ++i) { -+ /* We read from memory address 0, but that shouldn't -+ * be a problem: the reading will just fail. We are -+ * looking for a particular reason of failure. */ -+ if (ptrace(PTRACE_PEEKTEXT, pid, 0, 0) != -1 -+ || errno != ESRCH) -+ return; -+ -+ usleep(1000); -+ } -+ -+ fprintf(stderr, "\ -+I consistently fail to read a word from the freshly launched process.\n\ -+I'll now try to proceed with tracing, but this shouldn't be happening.\n"); -+} -+ - int - trace_pid(pid_t pid) { - debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); -@@ -193,7 +217,7 @@ struct process_stopping_handler - Process * task_enabling_breakpoint; - - /* The pointer being re-enabled. */ -- Breakpoint * breakpoint_being_enabled; -+ struct breakpoint *breakpoint_being_enabled; - - enum { - /* We are waiting for everyone to land in t/T. */ -@@ -364,7 +388,7 @@ static void - ugly_workaround(Process * proc) - { - void * ip = get_instruction_pointer(proc); -- Breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip); -+ struct breakpoint *sbp = dict_find_entry(proc->leader->breakpoints, ip); - if (sbp != NULL) - enable_breakpoint(proc, sbp); - else -@@ -578,7 +602,7 @@ process_stopping_on_event(Event_Handler * super, Event * event) - struct process_stopping_handler * self = (void *)super; - Process * task = event->proc; - Process * leader = task->leader; -- Breakpoint * sbp = self->breakpoint_being_enabled; -+ struct breakpoint *sbp = self->breakpoint_being_enabled; - Process * teb = self->task_enabling_breakpoint; - - debug(DEBUG_PROCESS, -@@ -687,7 +711,7 @@ process_stopping_destroy(Event_Handler * super) - } - - void --continue_after_breakpoint(Process *proc, Breakpoint *sbp) -+continue_after_breakpoint(Process *proc, struct breakpoint *sbp) - { - set_instruction_pointer(proc, sbp->addr); - if (sbp->enabled == 0) { -@@ -851,7 +875,7 @@ static Event * - process_vfork_on_event(Event_Handler * super, Event * event) - { - struct process_vfork_handler * self = (void *)super; -- Breakpoint * sbp; -+ struct breakpoint *sbp; - assert(self != NULL); - - switch (event->type) { diff --git a/ltrace-0.6.0-return-string-n.patch b/ltrace-0.6.0-return-string-n.patch deleted file mode 100644 index a3c8320..0000000 --- a/ltrace-0.6.0-return-string-n.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff -up ltrace-0.6.0/output.c\~ ltrace-0.6.0/output.c ---- ltrace-0.6.0/output.c~ 2011-02-14 16:48:25.000000000 +0100 -+++ ltrace-0.6.0/output.c 2011-02-15 00:35:39.000000000 +0100 -@@ -202,7 +204,9 @@ output_left(enum tof type, Process *proc - current_column += fprintf(options.output, ", "); - } - } -- if (func->params_right) { -+ if (func->params_right -+ || func->return_info->type == ARGTYPE_STRING_N -+ || func->return_info->type == ARGTYPE_ARRAY) { - save_register_args(type, proc); - } - } - -Diff finished. Tue Feb 15 00:35:44 2011 diff --git a/ltrace-0.6.0-selinux.patch b/ltrace-0.6.0-selinux.patch deleted file mode 100644 index 8f2c380..0000000 --- a/ltrace-0.6.0-selinux.patch +++ /dev/null @@ -1,212 +0,0 @@ -From cec06ec8282c538a40bde968ae36fe8356daffaa Mon Sep 17 00:00:00 2001 -From: Petr Machata -Date: Tue, 10 Apr 2012 13:31:55 +0200 -Subject: [PATCH] Warn when we fail to trace and SELinux boolean deny_ptrace - is in effect - ---- - ChangeLog | 7 ++++++ - common.h | 4 +++ - configure.ac | 5 ++++ - proc.c | 1 + - sysdeps/linux-gnu/trace.c | 51 ++++++++++++++++++++++++++++++++++++-------- - 5 files changed, 58 insertions(+), 10 deletions(-) - -diff --git a/common.h b/common.h -index fa80076..2399e29 100644 ---- a/common.h -+++ b/common.h -@@ -359,2 +359,6 @@ extern int linkmap_init(Process *, struct ltelf *); - -+/* Called when trace_me or primary trace_pid fail. This may plug in -+ * any platform-specific knowledge of why it could be so. */ -+void trace_fail_warning(pid_t pid); -+ - -diff --git a/configure.ac b/configure.ac -index 7fcfda5..42d6158 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -82,6 +82,11 @@ AC_CHECK_LIB([supc++], [__cxa_demangle], [ - AC_SUBST(libstdcxx_LIBS) - - -+dnl Check security_get_boolean_active availability. -+AC_CHECK_HEADERS(selinux/selinux.h) -+AC_CHECK_LIB(selinux, security_get_boolean_active) -+ -+ - # HAVE_LIBUNWIND - AC_ARG_WITH(libunwind, - AS_HELP_STRING([--with-libunwind], [Use libunwind frame unwinding support]), -diff -up ltrace-0.6.0/configure\~ ltrace-0.6.0/configure ---- ltrace-0.6.0/configure~ 2012-04-11 12:56:00.688263027 +0200 -+++ ltrace-0.6.0/configure 2012-04-11 12:55:49.787935890 +0200 -@@ -11029,6 +11689,65 @@ fi - - - -+for ac_header in selinux/selinux.h -+do : -+ ac_fn_c_check_header_mongrel "$LINENO" "selinux/selinux.h" "ac_cv_header_selinux_selinux_h" "$ac_includes_default" -+if test "x$ac_cv_header_selinux_selinux_h" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_SELINUX_SELINUX_H 1 -+_ACEOF -+ -+fi -+ -+done -+ -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for security_get_boolean_active in -lselinux" >&5 -+$as_echo_n "checking for security_get_boolean_active in -lselinux... " >&6; } -+if ${ac_cv_lib_selinux_security_get_boolean_active+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lselinux $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char security_get_boolean_active (); -+int -+main () -+{ -+return security_get_boolean_active (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_selinux_security_get_boolean_active=yes -+else -+ ac_cv_lib_selinux_security_get_boolean_active=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_selinux_security_get_boolean_active" >&5 -+$as_echo "$ac_cv_lib_selinux_security_get_boolean_active" >&6; } -+if test "x$ac_cv_lib_selinux_security_get_boolean_active" = xyes; then : -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBSELINUX 1 -+_ACEOF -+ -+ LIBS="-lselinux $LIBS" -+ -+fi -+ -+ -+ - # HAVE_LIBUNWIND - - # Check whether --with-libunwind was given. -diff --git a/proc.c b/proc.c -index 106b6a0..ded0c95 100644 ---- a/proc.c -+++ b/proc.c -@@ -94,6 +94,7 @@ open_pid(pid_t pid) - if (open_one_pid(pid)) { - fprintf(stderr, "Cannot attach to pid %u: %s\n", - pid, strerror(errno)); -+ trace_fail_warning(pid); - return; - } - -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index 67e1f93..82a4154 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -10,2 +10,7 @@ - -+#include "config.h" -+#ifdef HAVE_LIBSELINUX -+# include -+#endif -+ - /* If the system headers did not provide the constants, hard-code the normal -@@ -69,10 +75,32 @@ umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - #endif - - void --trace_me(void) { -+trace_fail_warning(pid_t pid) -+{ -+ /* This was adapted from GDB. */ -+#ifdef HAVE_LIBSELINUX -+ static int checked = 0; -+ if (checked) -+ return; -+ checked = 1; -+ -+ /* -1 is returned for errors, 0 if it has no effect, 1 if -+ * PTRACE_ATTACH is forbidden. */ -+ if (security_get_boolean_active("deny_ptrace") == 1) -+ fprintf(stderr, -+"The SELinux boolean 'deny_ptrace' is enabled, which may prevent ltrace from\n" -+"tracing other processes. You can disable this process attach protection by\n" -+"issuing 'setsebool deny_ptrace=0' in the superuser context.\n"); -+#endif /* HAVE_LIBSELINUX */ -+} -+ -+void -+trace_me(void) -+{ - debug(DEBUG_PROCESS, "trace_me: pid=%d", getpid()); - if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { - perror("PTRACE_TRACEME"); -+ trace_fail_warning(getpid()); - exit(1); - } - } -@@ -101,11 +129,14 @@ I'll now try to proceed with tracing, but this shouldn't be happening.\n"); - } - - int --trace_pid(pid_t pid) { -+trace_pid(pid_t pid) -+{ - debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); -- if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) { -+ /* This shouldn't emit error messages, as there are legitimate -+ * reasons that the PID can't be attached: like it may have -+ * already ended. */ -+ if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) - return -1; -- } - - /* man ptrace: PTRACE_ATTACH attaches to the process specified - in pid. The child is sent a SIGSTOP, but will not --- -1.7.7.6 - -diff -up ./config.h.in~ ./config.h.in ---- ./config.h.in~ 2011-02-14 17:01:18.000000000 +0100 -+++ ./config.h.in 2012-04-11 13:19:10.000000000 +0200 -@@ -45,6 +45,9 @@ - /* we have libiberty */ - #undef HAVE_LIBIBERTY - -+/* Define to 1 if you have the `selinux' library (-lselinux). */ -+#undef HAVE_LIBSELINUX -+ - /* we have libstdc++ */ - #undef HAVE_LIBSTDC__ - -@@ -72,6 +75,9 @@ - /* Define to 1 if you have the `rmdir' function. */ - #undef HAVE_RMDIR - -+/* Define to 1 if you have the header file. */ -+#undef HAVE_SELINUX_SELINUX_H -+ - /* Define to 1 if you have the header file. */ - #undef HAVE_STDDEF_H - diff --git a/ltrace-0.6.0-thread-races.patch b/ltrace-0.6.0-thread-races.patch deleted file mode 100644 index 8250aae..0000000 --- a/ltrace-0.6.0-thread-races.patch +++ /dev/null @@ -1,406 +0,0 @@ -diff --git a/common.h b/common.h -index 2fff8dd..715898d 100644 ---- a/common.h -+++ b/common.h -@@ -343,6 +343,7 @@ extern void disable_breakpoint(Process * proc, Breakpoint * sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - 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, Breakpoint * sbp); - extern void continue_after_vfork(Process * proc); - extern void ltrace_exiting(void); -diff --git a/handle_event.c b/handle_event.c -index f56c537..203459c 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -70,7 +70,9 @@ handle_event(Event *event) { - - /* Note: the previous handler has a chance to alter - * the event. */ -- if (event->proc->leader != NULL) { -+ if (event->proc != NULL -+ && event->proc->leader != NULL -+ && event->proc != event->proc->leader) { - event = call_handler(event->proc->leader, event); - if (event == NULL) - return; -@@ -454,7 +456,7 @@ handle_syscall(Event *event) { - enable_all_breakpoints(event->proc); - } - } -- continue_process(event->proc->pid); -+ continue_after_syscall(event->proc, event->e_un.sysnum, 0); - } - - static void -@@ -533,9 +535,12 @@ handle_sysret(Event *event) { - output_right(LT_TOF_SYSCALLR, event->proc, - sysname(event->proc, event->e_un.sysnum)); - } -+ assert(event->proc->callstack_depth > 0); -+ unsigned d = event->proc->callstack_depth - 1; -+ assert(event->proc->callstack[d].is_syscall); - callstack_pop(event->proc); - } -- continue_process(event->proc->pid); -+ continue_after_syscall(event->proc, event->e_un.sysnum, 1); - } - - static void -@@ -639,7 +644,7 @@ handle_breakpoint(Event *event) { - struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; - struct library_symbol *new_sym; - assert(sym); -- addr = sym2addr(leader, sym); -+ addr = sym2addr(event->proc, sym); - sbp = dict_find_entry(leader->breakpoints, addr); - if (sbp) { - if (addr != sbp->addr) { -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 0685342..021192f 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - - #include "common.h" - -@@ -138,6 +139,26 @@ next_event(void) - } - event.proc = pid2proc(pid); - if (!event.proc || event.proc->state == STATE_BEING_CREATED) { -+ /* Work around (presumably) a bug on some kernels, -+ * where we are seeing a waitpid event even though the -+ * process is still reported to be running. Wait for -+ * the tracing stop to propagate. But don't get stuck -+ * here forever. -+ * -+ * We need the process in T, because there's a lot of -+ * ptracing going on all over the place, and these -+ * calls fail when the process is not in T. -+ * -+ * N.B. This was observed on RHEL 5 Itanium, but I'm -+ * turning this on globally, to save some poor soul -+ * down the road (which could well be me a year from -+ * now) the pain of figuring this out all over again. -+ * Petr Machata 2011-11-22. */ -+ int i = 0; -+ for (; i < 100 && process_status(pid) != ps_tracing_stop; ++i) { -+ debug(2, "waiting for %d to stop", pid); -+ usleep(10000); -+ } - event.type = EVENT_NEW; - event.e_un.newpid = pid; - debug(DEBUG_EVENT, "event: NEW: pid=%d", pid); -diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c -index 00df572..3f5d951 100644 ---- a/sysdeps/linux-gnu/ia64/regs.c -+++ b/sysdeps/linux-gnu/ia64/regs.c -@@ -2,6 +2,7 @@ - - #include - #include -+#include - - #include - #include -@@ -36,12 +37,18 @@ set_instruction_pointer(Process *proc, void *addr) { - - void * - get_stack_pointer(Process *proc) { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0); -+ long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0); -+ if (l == -1 && errno) -+ return NULL; -+ return (void *)l; - } - - void * - get_return_addr(Process *proc, void *stack_pointer) { -- return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PT_B0, 0); -+ long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_B0, 0); -+ if (l == -1 && errno) -+ return NULL; -+ return (void *)l; - } - - void -diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c -index 799e0ff..079ed55 100644 ---- a/sysdeps/linux-gnu/ia64/trace.c -+++ b/sysdeps/linux-gnu/ia64/trace.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - - #include "common.h" - -@@ -48,9 +49,10 @@ int - syscall_p(Process *proc, int status, int *sysnum) { - if (WIFSTOPPED(status) - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -- unsigned long slot = -- (ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0) >> 41) & -- 0x3; -+ long l = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0); -+ if (l == -1 && errno) -+ return -1; -+ unsigned long slot = ((unsigned long)l >> 41) & 0x3; - unsigned long ip = - ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IIP, 0); - -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index ba3806d..db18df0 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -146,8 +146,8 @@ continue_process(pid_t pid) - debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); - - /* Only really continue the process if there are no events in -- the queue for this process. Otherwise just for the other -- events to arrive. */ -+ the queue for this process. Otherwise just wait for the -+ other events to arrive. */ - if (!have_events_for(pid)) - /* We always trace syscalls to control fork(), - * clone(), execve()... */ -@@ -168,6 +168,7 @@ struct pid_task { - int got_event : 1; - int delivered : 1; - int vforked : 1; -+ int sysret : 1; - } * pids; - - struct pid_set { -@@ -259,10 +260,14 @@ task_stopped(Process * task, void * data) - case ps_invalid: - case ps_tracing_stop: - case ps_zombie: -+ case ps_sleeping: - return pcb_cont; -- default: -+ case ps_stop: -+ case ps_other: - return pcb_stop; - } -+ -+ abort (); - } - - /* Task is blocked if it's stopped, or if it's a vfork parent. */ -@@ -376,7 +381,8 @@ process_stopping_done(struct process_stopping_handler * self, Process * leader) - if (!self->exiting) { - for (i = 0; i < self->pids.count; ++i) - if (self->pids.tasks[i].pid != 0 -- && self->pids.tasks[i].delivered) -+ && (self->pids.tasks[i].delivered -+ || self->pids.tasks[i].sysret)) - continue_process(self->pids.tasks[i].pid); - continue_process(self->task_enabling_breakpoint->pid); - destroy_event_handler(leader); -@@ -469,7 +475,10 @@ handle_stopping_event(struct pid_task * task_info, Event ** eventp) - /* Some SIGSTOPs may have not been delivered to their respective tasks - * yet. They are still in the queue. If we have seen an event for - * that process, continue it, so that the SIGSTOP can be delivered and -- * caught by ltrace. */ -+ * caught by ltrace. We don't mind that the process is after -+ * breakpoint (and therefore potentially doesn't have aligned IP), -+ * because the signal will be delivered without the process actually -+ * starting. */ - static void - continue_for_sigstop_delivery(struct pid_set * pids) - { -@@ -549,6 +558,14 @@ all_stops_accountable(struct pid_set * pids) - return 1; - } - -+static void -+singlestep(Process * proc) -+{ -+ debug(1, "PTRACE_SINGLESTEP"); -+ if (ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0)) -+ perror("PTRACE_SINGLESTEP"); -+} -+ - /* This event handler is installed when we are in the process of - * stopping the whole thread group to do the pointer re-enablement for - * one of the threads. We pump all events to the queue for later -@@ -580,6 +597,17 @@ process_stopping_on_event(Event_Handler * super, Event * event) - if (event_exit_p(event) && task_info != NULL) - task_info->pid = 0; - -+ /* Always handle sysrets. Whether sysret occurred and what -+ * sys it rets from may need to be determined based on process -+ * stack, so we need to keep that in sync with reality. Note -+ * that we don't continue the process after the sysret is -+ * handled. See continue_after_syscall. */ -+ if (event != NULL && event->type == EVENT_SYSRET) { -+ debug(1, "%d LT_EV_SYSRET", event->proc->pid); -+ event_to_queue = 0; -+ task_info->sysret = 1; -+ } -+ - switch (state) { - case psh_stopping: - /* If everyone is stopped, singlestep. */ -@@ -588,16 +616,22 @@ process_stopping_on_event(Event_Handler * super, Event * event) - teb->pid); - if (sbp->enabled) - disable_breakpoint(teb, sbp); -- if (ptrace(PTRACE_SINGLESTEP, teb->pid, 0, 0)) -- perror("PTRACE_SINGLESTEP"); -+ singlestep(teb); - self->state = state = psh_singlestep; - } - break; - -- case psh_singlestep: { -+ case psh_singlestep: - /* In singlestep state, breakpoint signifies that we - * have now stepped, and can re-enable the breakpoint. */ - if (event != NULL && task == teb) { -+ -+ /* This is not the singlestep that we are waiting for. */ -+ if (event->type == EVENT_SIGNAL) { -+ singlestep(task); -+ break; -+ } -+ - /* Essentially we don't care what event caused - * the thread to stop. We can do the - * re-enablement now. */ -@@ -613,7 +647,6 @@ process_stopping_on_event(Event_Handler * super, Event * event) - event = NULL; // handled - } else - break; -- } - - /* fall-through */ - -@@ -806,9 +839,6 @@ ltrace_exiting_install_handler(Process * proc) - * with its parent, and handle it as a multi-threaded case, with the - * exception that we know that the parent is blocked, and don't - * attempt to stop it. When the child execs, we undo the setup. -- * -- * XXX The parent process could be un-suspended before ltrace gets -- * child exec/exit event. Make sure this is taken care of. - */ - - struct process_vfork_handler -@@ -840,9 +870,9 @@ process_vfork_on_event(Event_Handler * super, Event * event) - sbp = dict_find_entry(event->proc->leader->breakpoints, - self->bp_addr); - if (sbp != NULL) -- insert_breakpoint(event->proc->leader, -- self->bp_addr, sbp->libsym, -- 1); -+ insert_breakpoint(event->proc->parent, -+ self->bp_addr, -+ sbp->libsym, 1); - } - - continue_process(event->proc->parent->pid); -@@ -852,11 +882,6 @@ process_vfork_on_event(Event_Handler * super, Event * event) - change_process_leader(event->proc, event->proc); - destroy_event_handler(event->proc); - -- /* XXXXX this could happen in the middle of handling -- * multi-threaded breakpoint. We must be careful to -- * undo the effects that we introduced above (vforked -- * = 1 et.al.). */ -- - default: - ; - } -@@ -893,6 +918,27 @@ continue_after_vfork(Process * proc) - change_process_leader(proc, proc->parent->leader); - } - -+static int -+is_mid_stopping(Process *proc) -+{ -+ return proc != NULL -+ && proc->event_handler != NULL -+ && proc->event_handler->on_event == &process_stopping_on_event; -+} -+ -+void -+continue_after_syscall(Process * proc, int sysnum, int ret_p) -+{ -+ /* Don't continue if we are mid-stopping. */ -+ if (ret_p && (is_mid_stopping(proc) || is_mid_stopping(proc->leader))) { -+ debug(DEBUG_PROCESS, -+ "continue_after_syscall: don't continue %d", -+ proc->pid); -+ return; -+ } -+ continue_process(proc->pid); -+} -+ - /* If ltrace gets SIGINT, the processes directly or indirectly run by - * ltrace get it too. We just have to wait long enough for the signal - * to be delivered and the process terminated, which we notice and -diff --git a/testsuite/ltrace.main/hello-vfork.c b/testsuite/ltrace.main/hello-vfork.c -new file mode 100644 -index 0000000..228c052 ---- /dev/null -+++ b/testsuite/ltrace.main/hello-vfork.c -@@ -0,0 +1,11 @@ -+/* Copyright (C) 2008, Red Hat, Inc. -+ * Written by Denys Vlasenko */ -+#include -+#include -+ -+int main() { -+ int r = vfork(); -+ fprintf(stderr, "vfork():%d\n", r); -+ _exit(0); -+} -+ -diff --git a/testsuite/ltrace.main/hello-vfork.exp b/testsuite/ltrace.main/hello-vfork.exp -new file mode 100644 -index 0000000..12c9ca3 ---- /dev/null -+++ b/testsuite/ltrace.main/hello-vfork.exp -@@ -0,0 +1,35 @@ -+# This file was written by Yao Qi . -+ -+set testfile "hello-vfork" -+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 "-f" -+ -+# 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 -+} -+ -+# Verify the output by checking numbers of print in main-vfork.ltrace. -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "_exit" 2 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "vfork resumed" 2 diff --git a/ltrace-0.6.0-threads.patch b/ltrace-0.6.0-threads.patch deleted file mode 100644 index bf48fda..0000000 --- a/ltrace-0.6.0-threads.patch +++ /dev/null @@ -1,2797 +0,0 @@ -diff --git a/breakpoints.c b/breakpoints.c -index 1ea406a..1eff8b0 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -14,15 +14,25 @@ - - Breakpoint * - address2bpstruct(Process *proc, void *addr) { -+ assert(proc != NULL); -+ assert(proc->breakpoints != NULL); -+ assert(proc->leader == proc); - debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr); - return dict_find_entry(proc->breakpoints, addr); - } - - void - insert_breakpoint(Process *proc, void *addr, -- struct library_symbol *libsym) { -+ struct library_symbol *libsym, int enable) { - Breakpoint *sbp; - -+ Process * leader = proc->leader; -+ -+ /* Only the group leader should be getting the breakpoints and -+ * thus have ->breakpoint initialized. */ -+ assert(leader != NULL); -+ assert(leader->breakpoints != NULL); -+ - #ifdef __arm__ - int thumb_mode = (int)addr & 1; - if (thumb_mode) -@@ -38,13 +48,13 @@ insert_breakpoint(Process *proc, void *addr, - if (libsym) - libsym->needs_init = 0; - -- sbp = dict_find_entry(proc->breakpoints, addr); -+ sbp = dict_find_entry(leader->breakpoints, addr); - if (!sbp) { - sbp = calloc(1, sizeof(Breakpoint)); - if (!sbp) { - return; /* TODO FIXME XXX: error_mem */ - } -- dict_enter(proc->breakpoints, addr, sbp); -+ dict_enter(leader->breakpoints, addr, sbp); - sbp->addr = addr; - sbp->libsym = libsym; - } -@@ -53,8 +63,10 @@ insert_breakpoint(Process *proc, void *addr, - proc->thumb_mode = 0; - #endif - sbp->enabled++; -- if (sbp->enabled == 1 && proc->pid) -- enable_breakpoint(proc->pid, sbp); -+ if (sbp->enabled == 1 && enable) { -+ assert(proc->pid != 0); -+ enable_breakpoint(proc, sbp); -+ } - } - - void -@@ -63,7 +75,10 @@ delete_breakpoint(Process *proc, void *addr) { - - debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); - -- sbp = dict_find_entry(proc->breakpoints, addr); -+ Process * leader = proc->leader; -+ assert(leader != NULL); -+ -+ sbp = dict_find_entry(leader->breakpoints, addr); - assert(sbp); /* FIXME: remove after debugging has been done. */ - /* This should only happen on out-of-memory conditions. */ - if (sbp == NULL) -@@ -71,7 +86,7 @@ delete_breakpoint(Process *proc, void *addr) { - - sbp->enabled--; - if (sbp->enabled == 0) -- disable_breakpoint(proc->pid, sbp); -+ disable_breakpoint(proc, sbp); - assert(sbp->enabled >= 0); - } - -@@ -79,7 +94,7 @@ static void - enable_bp_cb(void *addr, void *sbp, void *proc) { - debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); - if (((Breakpoint *)sbp)->enabled) { -- enable_breakpoint(((Process *)proc)->pid, sbp); -+ enable_breakpoint(proc, sbp); - } - } - -@@ -146,13 +161,14 @@ static void - disable_bp_cb(void *addr, void *sbp, void *proc) { - debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); - if (((Breakpoint *)sbp)->enabled) { -- disable_breakpoint(((Process *)proc)->pid, sbp); -+ disable_breakpoint(proc, sbp); - } - } - - void - disable_all_breakpoints(Process *proc) { - debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); -+ assert(proc->leader == proc); - if (proc->breakpoints_enabled) { - debug(1, "Disabling breakpoints for pid %u...", proc->pid); - dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); -@@ -167,8 +183,9 @@ free_bp_cb(void *addr, void *sbp, void *data) { - free(sbp); - } - --void --breakpoints_init(Process *proc) { -+int -+breakpoints_init(Process *proc, int enable) -+{ - struct library_symbol *sym; - - debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); -@@ -177,19 +194,41 @@ breakpoints_init(Process *proc) { - dict_clear(proc->breakpoints); - proc->breakpoints = NULL; - } -- proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int); -+ -+ /* Only the thread group leader should hold the breakpoints. -+ * (N.B. PID may be set to 0 temporarily when called by -+ * handle_exec). */ -+ assert(proc->leader == proc); -+ -+ proc->breakpoints = dict_init(dict_key2hash_int, -+ dict_key_cmp_int); -+ -+ if (proc->list_of_symbols != NULL) { -+ struct library_symbol * sym = proc->list_of_symbols; -+ while (sym != NULL) { -+ struct library_symbol * next = sym->next; -+ free(sym); -+ sym = next; -+ } -+ } -+ proc->list_of_symbols = NULL; - - if (options.libcalls && proc->filename) { -- /* FIXME: memory leak when called by exec(): */ - proc->list_of_symbols = read_elf(proc); -+ if (proc->list_of_symbols == NULL) { -+ /* XXX leak breakpoints */ -+ return -1; -+ } -+ - if (opt_e) { -- struct library_symbol **tmp1 = &(proc->list_of_symbols); -+ struct library_symbol **tmp1 = &proc->list_of_symbols; - while (*tmp1) { - struct opt_e_t *tmp2 = opt_e; - int keep = !opt_e_enable; - - while (tmp2) { -- if (!strcmp((*tmp1)->name, tmp2->name)) { -+ if (!strcmp((*tmp1)->name, -+ tmp2->name)) { - keep = opt_e_enable; - } - tmp2 = tmp2->next; -@@ -201,15 +240,14 @@ breakpoints_init(Process *proc) { - } - } - } -- } else { -- proc->list_of_symbols = NULL; -- } -- for (sym = proc->list_of_symbols; sym; sym = sym->next) { -- /* proc->pid==0 delays enabling. */ -- insert_breakpoint(proc, sym2addr(proc, sym), sym); - } -+ -+ for (sym = proc->list_of_symbols; sym; sym = sym->next) -+ insert_breakpoint(proc, sym2addr(proc, sym), sym, enable); -+ - proc->callstack_depth = 0; - proc->breakpoints_enabled = -1; -+ return 0; - } - - void -@@ -222,8 +260,7 @@ reinitialize_breakpoints(Process *proc) { - - while (sym) { - if (sym->needs_init) { -- insert_breakpoint(proc, sym2addr(proc, sym), -- sym); -+ insert_breakpoint(proc, sym2addr(proc, sym), sym, 1); - if (sym->needs_init && !sym->is_weak) { - fprintf(stderr, - "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", -diff --git a/common.h b/common.h -index 70e4a5a..49861cf 100644 ---- a/common.h -+++ b/common.h -@@ -1,3 +1,4 @@ -+#include - #if defined(HAVE_LIBUNWIND) - #include - #endif /* defined(HAVE_LIBUNWIND) */ -@@ -161,12 +162,32 @@ enum Process_State { - STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ - }; - -+typedef struct Event_Handler Event_Handler; -+struct Event_Handler { -+ /* Event handler that overrides the default one. Should -+ * return NULL if the event was handled, otherwise the -+ * returned event is passed to the default handler. */ -+ Event * (* on_event)(Event_Handler * self, Event * event); -+ -+ /* Called when the event handler removal is requested. */ -+ void (* destroy)(Event_Handler * self); -+}; -+ -+/* XXX We would rather have this all organized a little differently, -+ * have Process for the whole group and Task for what's there for -+ * per-thread stuff. But for now this is the less invasive way of -+ * structuring it. */ - struct Process { - Process_State state; - Process * parent; /* needed by STATE_BEING_CREATED */ - char * filename; - pid_t pid; -+ -+ /* Dictionary of breakpoints (which is a mapping -+ * address->Breakpoint). This is NULL for non-leader -+ * processes. */ - Dict * breakpoints; -+ - int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ - int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ - unsigned int personality; -@@ -183,7 +204,6 @@ struct Process { - void * instruction_pointer; - void * stack_pointer; /* To get return addr, args... */ - void * return_addr; -- Breakpoint * breakpoint_being_enabled; - void * arch_ptr; - short e_machine; - short need_to_reinitialize_breakpoints; -@@ -191,16 +211,28 @@ struct Process { - int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ - #endif - -- /* output: */ -- enum tof type_being_displayed; -- - #if defined(HAVE_LIBUNWIND) - /* libunwind address space */ - unw_addr_space_t unwind_as; - void *unwind_priv; - #endif /* defined(HAVE_LIBUNWIND) */ - -+ /* Set in leader. */ -+ Event_Handler * event_handler; -+ -+ -+ /** -+ * Process chaining. -+ **/ - Process * next; -+ -+ /* LEADER points to the leader thread of the POSIX.1 process. -+ If X->LEADER == X, then X is the leader thread and the -+ Process structures chained by NEXT represent other threads, -+ up until, but not including, the next leader thread. -+ LEADER may be NULL after the leader has already exited. In -+ that case this process is waiting to be collected. */ -+ Process * leader; - }; - - struct opt_c_struct { -@@ -216,27 +248,64 @@ struct opt_c_struct { - - extern Dict * dict_opt_c; - --extern Process * list_of_processes; -+enum process_status { -+ ps_invalid, /* Failure. */ -+ ps_stop, /* Job-control stop. */ -+ ps_tracing_stop, -+ ps_zombie, -+ ps_other, /* Necessary other states can be added as needed. */ -+}; - --extern Event * next_event(void); -+enum pcb_status { -+ pcb_stop, /* The iteration should stop. */ -+ pcb_cont, /* The iteration should continue. */ -+}; -+ -+/* Process list */ - extern Process * pid2proc(pid_t pid); -+extern void add_process(Process * proc); -+extern void remove_process(Process * proc); -+extern Process *each_process(Process * start, -+ enum pcb_status (* cb)(Process * proc, void * data), -+ void * data); -+extern Process *each_task(Process * start, -+ enum pcb_status (* cb)(Process * proc, void * data), -+ void * data); -+ -+/* Events */ -+enum ecb_status { -+ ecb_cont, /* The iteration should continue. */ -+ ecb_yield, /* The iteration should stop, yielding this -+ * event. */ -+ ecb_deque, /* Like ecb_stop, but the event should be removed -+ * from the queue. */ -+}; -+extern Event * next_event(void); -+extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data), -+ void * data); -+extern void enque_event(Event * event); - extern void handle_event(Event * event); --extern void execute_program(Process *, char **); -+ -+extern void install_event_handler(Process * proc, Event_Handler * handler); -+extern void destroy_event_handler(Process * proc); -+ -+extern pid_t execute_program(const char * command, char ** argv); - extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); - extern Breakpoint * address2bpstruct(Process * proc, void * addr); --extern void breakpoints_init(Process * proc); --extern void insert_breakpoint(Process * proc, void * addr, struct library_symbol * libsym); -+extern int breakpoints_init(Process * proc, int enable); -+extern void insert_breakpoint(Process * proc, void * addr, -+ struct library_symbol * libsym, int enable); - extern void delete_breakpoint(Process * proc, void * addr); - extern void enable_all_breakpoints(Process * proc); - extern void disable_all_breakpoints(Process * proc); - extern void reinitialize_breakpoints(Process *); - --extern Process * open_program(char * filename, pid_t pid); -+extern Process * open_program(char * filename, pid_t pid, int init_breakpoints); - extern void open_pid(pid_t pid); - extern void show_summary(void); - extern arg_type_info * lookup_prototype(enum arg_type at); - --extern void do_init_elf(struct ltelf *lte, const char *filename); -+extern int do_init_elf(struct ltelf *lte, const char *filename); - extern void do_close_elf(struct ltelf *lte); - extern int in_load_libraries(const char *name, struct ltelf *lte, size_t count, GElf_Sym *sym); - extern struct library_symbol *library_symbols; -@@ -246,6 +315,10 @@ extern void add_library_symbol(GElf_Addr addr, const char *name, - - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); -+extern pid_t process_leader(pid_t pid); -+extern int process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n); -+extern int process_stopped(pid_t pid); -+extern enum process_status process_status(pid_t pid); - extern void trace_set_options(Process * proc, pid_t pid); - extern void trace_me(void); - extern int trace_pid(pid_t pid); -@@ -256,13 +329,13 @@ extern void set_instruction_pointer(Process * proc, void * addr); - extern void * get_stack_pointer(Process * proc); - extern void * get_return_addr(Process * proc, void * stack_pointer); - extern void set_return_addr(Process * proc, void * addr); --extern void enable_breakpoint(pid_t pid, Breakpoint * sbp); --extern void disable_breakpoint(pid_t pid, const Breakpoint * sbp); -+extern void enable_breakpoint(Process * proc, Breakpoint * sbp); -+extern void disable_breakpoint(Process * proc, Breakpoint * sbp); - extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); --extern void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); -+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); -@@ -272,5 +345,7 @@ extern int ffcheck(void * maddr); - extern void * sym2addr(Process *, struct library_symbol *); - extern int linkmap_init(Process *, struct ltelf *); - extern void arch_check_dbg(Process *proc); -+extern int task_kill (pid_t pid, int sig); -+ - - extern struct ltelf main_lte; -diff --git a/execute_program.c b/execute_program.c -index 3651b66..47f514d 100644 ---- a/execute_program.c -+++ b/execute_program.c -@@ -17,7 +17,8 @@ - #include "common.h" - - static void --change_uid(Process *proc) { -+change_uid(const char * command) -+{ - uid_t run_uid, run_euid; - gid_t run_gid, run_egid; - -@@ -49,7 +50,7 @@ change_uid(Process *proc) { - run_euid = run_uid; - run_egid = run_gid; - -- if (!stat(proc->filename, &statbuf)) { -+ if (!stat(command, &statbuf)) { - if (statbuf.st_mode & S_ISUID) { - run_euid = statbuf.st_uid; - } -@@ -68,32 +69,27 @@ change_uid(Process *proc) { - } - } - --void --execute_program(Process *sp, char **argv) { -+pid_t -+execute_program(const char * command, char **argv) -+{ - pid_t pid; - -- debug(1, "Executing `%s'...", sp->filename); -+ debug(1, "Executing `%s'...", command); - - pid = fork(); - if (pid < 0) { - perror("ltrace: fork"); - exit(1); - } else if (!pid) { /* child */ -- change_uid(sp); -+ change_uid(command); - trace_me(); -- execvp(sp->filename, argv); -- fprintf(stderr, "Can't execute `%s': %s\n", sp->filename, -+ execvp(command, argv); -+ fprintf(stderr, "Can't execute `%s': %s\n", command, - strerror(errno)); - _exit(1); - } - - debug(1, "PID=%d", pid); - -- sp->pid = pid; -- --#if defined(HAVE_LIBUNWIND) -- sp->unwind_priv = _UPT_create(pid); --#endif /* defined(HAVE_LIBUNWIND) */ -- -- return; -+ return pid; - } -diff --git a/handle_event.c b/handle_event.c -index 01309ff..0aa40f7 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -25,7 +25,6 @@ static void handle_clone(Event *event); - static void handle_exec(Event *event); - static void handle_breakpoint(Event *event); - static void handle_new(Event *event); --static void remove_proc(Process *proc); - - static void callstack_push_syscall(Process *proc, int sysnum); - static void callstack_push_symfunc(Process *proc, -@@ -38,7 +37,26 @@ static char * arch_sysname(Process *proc, int sysnum); - - void - handle_event(Event *event) { -- debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", event->proc ? event->proc->pid : -1, event->type); -+ if (exiting == 1) { -+ exiting = 2; -+ debug(1, "ltrace about to exit"); -+ ltrace_exiting(); -+ } -+ debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", -+ event->proc ? event->proc->pid : -1, event->type); -+ /* If the thread group defines an overriding event handler, -+ give it a chance to kick in. */ -+ if (event->proc != NULL -+ && event->proc->leader != NULL) { -+ Event_Handler * handler = event->proc->leader->event_handler; -+ if (handler != NULL) { -+ event = (*handler->on_event) (handler, event); -+ if (event == NULL) -+ /* It was handled. */ -+ return; -+ } -+ } -+ - switch (event->type) { - case EVENT_NONE: - debug(1, "event: none"); -@@ -202,24 +220,24 @@ handle_clone(Event * event) { - p->pid = event->e_un.newpid; - p->parent = event->proc; - -+ /* We save register values to the arch pointer, and these need -+ to be per-thread. */ -+ p->arch_ptr = NULL; -+ - if (pending_new(p->pid)) { - pending_new_remove(p->pid); -- if (p->breakpoint_being_enabled) { -- enable_breakpoint(p->pid, p->breakpoint_being_enabled); -- p->breakpoint_being_enabled = NULL; -- } -+ if (p->event_handler != NULL) -+ destroy_event_handler(p); - if (event->proc->state == STATE_ATTACHED && options.follow) { - p->state = STATE_ATTACHED; - } else { - p->state = STATE_IGNORED; - } - continue_process(p->pid); -- p->next = list_of_processes; -- list_of_processes = p; -+ add_process(p); - } else { - p->state = STATE_BEING_CREATED; -- p->next = list_of_processes; -- list_of_processes = p; -+ add_process(p); - } - continue_process(event->proc->pid); - } -@@ -235,10 +253,8 @@ handle_new(Event * event) { - pending_new_insert(event->e_un.newpid); - } else { - assert(proc->state == STATE_BEING_CREATED); -- if (proc->breakpoint_being_enabled) { -- enable_breakpoint(proc->pid, proc->breakpoint_being_enabled); -- proc->breakpoint_being_enabled = NULL; -- } -+ if (proc->event_handler != NULL) -+ destroy_event_handler(proc); - if (options.follow) { - proc->state = STATE_ATTACHED; - } else { -@@ -323,13 +339,6 @@ arch_sysname(Process *proc, int sysnum) { - static void - handle_signal(Event *event) { - debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); -- if (exiting && event->e_un.signum == SIGSTOP) { -- pid_t pid = event->proc->pid; -- disable_all_breakpoints(event->proc); -- untrace_pid(pid); -- remove_proc(event->proc); -- return; -- } - if (event->proc->state != STATE_IGNORED && !options.no_signals) { - output_line(event->proc, "--- %s (%s) ---", - shortsignal(event->proc, event->e_un.signum), -@@ -345,7 +354,7 @@ handle_exit(Event *event) { - output_line(event->proc, "+++ exited (status %d) +++", - event->e_un.ret_val); - } -- remove_proc(event->proc); -+ remove_process(event->proc); - } - - static void -@@ -355,31 +364,7 @@ handle_exit_signal(Event *event) { - output_line(event->proc, "+++ killed by %s +++", - shortsignal(event->proc, event->e_un.signum)); - } -- remove_proc(event->proc); --} -- --static void --remove_proc(Process *proc) { -- Process *tmp, *tmp2; -- -- debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); -- -- if (list_of_processes == proc) { -- tmp = list_of_processes; -- list_of_processes = list_of_processes->next; -- free(tmp); -- return; -- } -- tmp = list_of_processes; -- while (tmp->next) { -- if (tmp->next == proc) { -- tmp2 = tmp->next; -- tmp->next = tmp->next->next; -- free(tmp2); -- continue; -- } -- tmp = tmp->next; -- } -+ remove_process(event->proc); - } - - static void -@@ -389,7 +374,7 @@ handle_syscall(Event *event) { - callstack_push_syscall(event->proc, event->e_un.sysnum); - if (options.syscalls) { - output_left(LT_TOF_SYSCALL, event->proc, -- sysname(event->proc, event->e_un.sysnum)); -+ sysname(event->proc, event->e_un.sysnum)); - } - if (event->proc->breakpoints_enabled == 0) { - enable_all_breakpoints(event->proc); -@@ -406,7 +391,7 @@ handle_exec(Event * event) { - debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); - if (proc->state == STATE_IGNORED) { - untrace_pid(proc->pid); -- remove_proc(proc); -+ remove_process(proc); - return; - } - output_line(proc, "--- Called exec() ---"); -@@ -417,7 +402,7 @@ handle_exec(Event * event) { - proc->filename = pid2name(proc->pid); - saved_pid = proc->pid; - proc->pid = 0; -- breakpoints_init(proc); -+ breakpoints_init(proc, 0); - proc->pid = saved_pid; - proc->callstack_depth = 0; - continue_process(proc->pid); -@@ -503,6 +488,13 @@ static void - handle_breakpoint(Event *event) { - int i, j; - Breakpoint *sbp; -+ Process *leader = event->proc->leader; -+ -+ /* The leader has terminated. */ -+ if (leader == NULL) { -+ continue_process(event->proc->pid); -+ 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); -@@ -513,7 +505,7 @@ handle_breakpoint(Event *event) { - Breakpoint *stub_bp = NULL; - char nop_instruction[] = PPC_NOP; - -- stub_bp = address2bpstruct (event->proc, event->e_un.brk_addr); -+ stub_bp = address2bpstruct(leader, event->e_un.brk_addr); - - if (stub_bp) { - unsigned char *bp_instruction = stub_bp->orig_value; -@@ -528,14 +520,6 @@ handle_breakpoint(Event *event) { - } - } - #endif -- if ((sbp = event->proc->breakpoint_being_enabled) != 0) { -- /* Reinsert breakpoint */ -- continue_enabling_breakpoint(event->proc->pid, -- event->proc-> -- breakpoint_being_enabled); -- event->proc->breakpoint_being_enabled = NULL; -- return; -- } - - for (i = event->proc->callstack_depth - 1; i >= 0; i--) { - if (event->e_un.brk_addr == -@@ -554,7 +538,7 @@ handle_breakpoint(Event *event) { - if (libsym->plt_type != LS_TOPLT_POINT) { - unsigned char break_insn[] = BREAKPOINT_VALUE; - -- sbp = address2bpstruct(event->proc, addr); -+ sbp = address2bpstruct(leader, addr); - assert(sbp); - a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, - addr); -@@ -562,10 +546,10 @@ handle_breakpoint(Event *event) { - if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { - sbp->enabled--; - insert_breakpoint(event->proc, addr, -- libsym); -+ libsym, 1); - } - } else { -- sbp = dict_find_entry(event->proc->breakpoints, addr); -+ sbp = dict_find_entry(leader->breakpoints, addr); - /* On powerpc, the breakpoint address - may end up being actual entry point - of the library symbol, not the PLT -@@ -573,7 +557,7 @@ handle_breakpoint(Event *event) { - sbp is NULL. */ - if (sbp == NULL || addr != sbp->addr) { - insert_breakpoint(event->proc, addr, -- libsym); -+ libsym, 1); - } - } - #elif defined(__mips__) -@@ -581,18 +565,18 @@ handle_breakpoint(Event *event) { - struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; - struct library_symbol *new_sym; - assert(sym); -- addr=sym2addr(event->proc,sym); -- sbp = dict_find_entry(event->proc->breakpoints, addr); -+ addr = sym2addr(leader, sym); -+ sbp = dict_find_entry(leader->breakpoints, addr); - if (sbp) { - if (addr != sbp->addr) { -- insert_breakpoint(event->proc, addr, sym); -+ insert_breakpoint(event->proc, addr, sym, 1); - } - } else { - new_sym=malloc(sizeof(*new_sym) + strlen(sym->name) + 1); - memcpy(new_sym,sym,sizeof(*new_sym) + strlen(sym->name) + 1); -- new_sym->next=event->proc->list_of_symbols; -- event->proc->list_of_symbols=new_sym; -- insert_breakpoint(event->proc, addr, new_sym); -+ new_sym->next = leader->list_of_symbols; -+ leader->list_of_symbols = new_sym; -+ insert_breakpoint(event->proc, addr, new_sym, 1); - } - #endif - for (j = event->proc->callstack_depth - 1; j > i; j--) { -@@ -609,18 +593,23 @@ handle_breakpoint(Event *event) { - event->proc->callstack[i].c_un.libfunc->name); - } - callstack_pop(event->proc); -- continue_after_breakpoint(event->proc, -- address2bpstruct(event->proc, -- event->e_un.brk_addr)); -+ sbp = address2bpstruct(leader, event->e_un.brk_addr); -+ continue_after_breakpoint(event->proc, sbp); - return; - } - } - -- if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) { -+ if ((sbp = address2bpstruct(leader, event->e_un.brk_addr))) { -+ if (sbp->libsym == NULL) { -+ continue_after_breakpoint(event->proc, sbp); -+ return; -+ } -+ - if (strcmp(sbp->libsym->name, "") == 0) { -- debug(2, "Hit _dl_debug_state breakpoint!\n"); -- arch_check_dbg(event->proc); -+ debug(DEBUG_PROCESS, "Hit _dl_debug_state breakpoint!\n"); -+ arch_check_dbg(leader); - } -+ - if (event->proc->state != STATE_IGNORED) { - event->proc->stack_pointer = get_stack_pointer(event->proc); - event->proc->return_addr = -@@ -632,7 +621,7 @@ handle_breakpoint(Event *event) { - if (event->proc->need_to_reinitialize_breakpoints - && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == - 0)) -- reinitialize_breakpoints(event->proc); -+ reinitialize_breakpoints(leader); - #endif - - continue_after_breakpoint(event->proc, sbp); -@@ -689,7 +678,7 @@ callstack_push_symfunc(Process *proc, struct library_symbol *sym) { - - elem->return_addr = proc->return_addr; - if (elem->return_addr) { -- insert_breakpoint(proc, elem->return_addr, 0); -+ insert_breakpoint(proc, elem->return_addr, NULL, 1); - } - - /* handle functions like atexit() on mips which have no return */ -@@ -709,6 +698,7 @@ callstack_pop(Process *proc) { - debug(DEBUG_FUNCTION, "callstack_pop(pid=%d)", proc->pid); - elem = &proc->callstack[proc->callstack_depth - 1]; - if (!elem->is_syscall && elem->return_addr) { -+ assert(proc->leader != NULL); - delete_breakpoint(proc, elem->return_addr); - } - if (elem->arch_ptr != NULL) { -diff --git a/libltrace.c b/libltrace.c -index 0f48d11..e731fe1 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -12,32 +12,39 @@ - #include "common.h" - - char *command = NULL; --Process *list_of_processes = NULL; - - int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */ - --static void --signal_alarm(int sig) { -- Process *tmp = list_of_processes; -+static enum pcb_status -+stop_non_p_processes (Process * proc, void * data) -+{ -+ int stop = 1; - -- signal(SIGALRM, SIG_DFL); -- while (tmp) { -- struct opt_p_t *tmp2 = opt_p; -- while (tmp2) { -- if (tmp->pid == tmp2->pid) { -- tmp = tmp->next; -- if (!tmp) { -- return; -- } -- tmp2 = opt_p; -- continue; -- } -- tmp2 = tmp2->next; -+ struct opt_p_t *it; -+ for (it = opt_p; it != NULL; it = it->next) { -+ Process * p_proc = pid2proc(it->pid); -+ if (p_proc == NULL) { -+ printf("stop_non_p_processes: %d terminated?\n", it->pid); -+ continue; -+ } -+ if (p_proc == proc || p_proc->leader == proc->leader) { -+ stop = 0; -+ break; - } -- debug(2, "Sending SIGSTOP to process %u\n", tmp->pid); -- kill(tmp->pid, SIGSTOP); -- tmp = tmp->next; - } -+ -+ if (stop) { -+ debug(2, "Sending SIGSTOP to process %u", proc->pid); -+ kill(proc->pid, SIGSTOP); -+ } -+ -+ return pcb_cont; -+} -+ -+static void -+signal_alarm(int sig) { -+ signal(SIGALRM, SIG_DFL); -+ each_process(NULL, &stop_non_p_processes, NULL); - } - - static void -@@ -47,15 +54,7 @@ signal_exit(int sig) { - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); - signal(SIGALRM, signal_alarm); -- if (opt_p) { -- struct opt_p_t *tmp = opt_p; -- while (tmp) { -- debug(2, "Sending SIGSTOP to process %u\n", tmp->pid); -- kill(tmp->pid, SIGSTOP); -- tmp = tmp->next; -- } -- } -- alarm(1); -+ //alarm(1); - } - - static void -@@ -108,7 +107,7 @@ ltrace_init(int argc, char **argv) { - } - } - if (command) { -- execute_program(open_program(command, 0), argv); -+ open_program(command, execute_program(command, argv), 0); - } - opt_p_tmp = opt_p; - while (opt_p_tmp) { -diff --git a/ltrace-elf.c b/ltrace-elf.c -index 1a33ec3..d88d5a6 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -14,7 +14,6 @@ - - #include "common.h" - --void do_init_elf(struct ltelf *lte, const char *filename); - void do_close_elf(struct ltelf *lte); - void add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, -@@ -136,7 +135,7 @@ static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, - return 0; - } - --void -+int - do_init_elf(struct ltelf *lte, const char *filename) { - int i; - GElf_Addr relplt_addr = 0; -@@ -147,7 +146,7 @@ do_init_elf(struct ltelf *lte, const char *filename) { - - lte->fd = open(filename, O_RDONLY); - if (lte->fd == -1) -- error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename); -+ return 1; - - #ifdef HAVE_ELF_C_READ_MMAP - lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL); -@@ -454,6 +453,7 @@ do_init_elf(struct ltelf *lte, const char *filename) { - - debug(1, "%s %zd PLT relocations", filename, lte->relplt_count); - } -+ return 0; - } - - void -@@ -622,7 +622,8 @@ read_elf(Process *proc) { - - elf_version(EV_CURRENT); - -- do_init_elf(lte, proc->filename); -+ if (do_init_elf(lte, proc->filename)) -+ return NULL; - - memcpy(&main_lte, lte, sizeof(struct ltelf)); - -@@ -634,7 +635,9 @@ read_elf(Process *proc) { - proc->e_machine = lte->ehdr.e_machine; - - for (i = 0; i < library_num; ++i) { -- do_init_elf(<e[i + 1], library[i]); -+ if (do_init_elf(<e[i + 1], library[i])) -+ error(EXIT_FAILURE, errno, "Can't open \"%s\"", -+ proc->filename); - } - - if (!options.no_plt) { -diff --git a/ltrace.h b/ltrace.h -index 5e43ba5..0ff4572 100644 ---- a/ltrace.h -+++ b/ltrace.h -@@ -20,6 +20,7 @@ enum Event_type { - typedef struct Process Process; - typedef struct Event Event; - struct Event { -+ struct Event * next; - Process * proc; - Event_type type; - union { -diff --git a/output.c b/output.c -index de2a836..945dd52 100644 ---- a/output.c -+++ b/output.c -@@ -96,7 +96,7 @@ begin_of_line(enum tof type, Process *proc) { - } - - static Function * --name2func(char *name) { -+name2func(char const *name) { - Function *tmp; - const char *str1, *str2; - -@@ -153,7 +153,7 @@ tabto(int col) { - } - - void --output_left(enum tof type, Process *proc, char *function_name) { -+output_left(enum tof type, Process *proc, char const *function_name) { - Function *func; - static arg_type_info *arg_unknown = NULL; - if (arg_unknown == NULL) -@@ -168,7 +168,6 @@ output_left(enum tof type, Process *proc, char *function_name) { - } - current_proc = proc; - current_depth = proc->callstack_depth; -- proc->type_being_displayed = type; - begin_of_line(type, proc); - #ifdef USE_DEMANGLE - current_column += -diff --git a/output.h b/output.h -index c58577a..fa840c7 100644 ---- a/output.h -+++ b/output.h -@@ -1,3 +1,3 @@ - void output_line(Process *proc, char *fmt, ...); --void output_left(enum tof type, Process *proc, char *function_name); -+void output_left(enum tof type, Process *proc, char const *function_name); - void output_right(enum tof type, Process *proc, char *function_name); -diff --git a/proc.c b/proc.c -index 1c57532..0425e09 100644 ---- a/proc.c -+++ b/proc.c -@@ -10,73 +10,280 @@ - #include - #include - #include -+#include -+#include - - #include "common.h" - - Process * --open_program(char *filename, pid_t pid) { -+open_program(char *filename, pid_t pid, int enable) { - Process *proc; -+ assert(pid != 0); - proc = calloc(sizeof(Process), 1); - if (!proc) { - perror("malloc"); - exit(1); - } -+ - proc->filename = strdup(filename); - proc->breakpoints_enabled = -1; -- if (pid) { -- proc->pid = pid; -+ proc->pid = pid; - #if defined(HAVE_LIBUNWIND) -- proc->unwind_priv = _UPT_create(pid); -- } else { -- proc->unwind_priv = NULL; -+ proc->unwind_priv = _UPT_create(pid); -+ proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0); - #endif /* defined(HAVE_LIBUNWIND) */ -- } - -- breakpoints_init(proc); -+ add_process(proc); -+ if (proc->leader == NULL) { -+ free(proc); -+ return NULL; -+ } - -- proc->next = list_of_processes; -- list_of_processes = proc; -+ if (proc->leader == proc) -+ if (breakpoints_init(proc, enable)) { -+ fprintf(stderr, "failed to init breakpoints %d\n", -+ proc->pid); -+ remove_process(proc); -+ return NULL; -+ } - --#if defined(HAVE_LIBUNWIND) -- proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0); --#endif /* defined(HAVE_LIBUNWIND) */ - return proc; - } - --void --open_pid(pid_t pid) { -+static int -+open_one_pid(pid_t pid) -+{ - Process *proc; - char *filename; -+ debug(DEBUG_PROCESS, "open_one_pid(pid=%d)", pid); - -- if (trace_pid(pid) < 0) { -- fprintf(stderr, "Cannot attach to pid %u: %s\n", pid, -- strerror(errno)); -- return; -+ /* Get the filename first. Should the trace_pid fail, we can -+ * easily free it, untracing is more work. */ -+ if ((filename = pid2name(pid)) == NULL -+ || trace_pid(pid) < 0) { -+ free(filename); -+ return -1; - } - -- filename = pid2name(pid); -+ proc = open_program(filename, pid, 0); -+ if (proc == NULL) -+ return -1; -+ trace_set_options(proc, pid); -+ -+ return 0; -+} -+ -+enum pcb_status -+start_one_pid(Process * proc, void * data) -+{ -+ continue_process(proc->pid); -+ proc->breakpoints_enabled = 1; -+ return pcb_cont; -+} -+ -+void -+open_pid(pid_t pid) -+{ -+ debug(DEBUG_PROCESS, "open_pid(pid=%d)", pid); -+ /* If we are already tracing this guy, we should be seeing all -+ * his children via normal tracing route. */ -+ if (pid2proc(pid) != NULL) -+ return; - -- if (!filename) { -- fprintf(stderr, "Cannot trace pid %u: %s\n", pid, -- strerror(errno)); -+ /* First, see if we can attach the requested PID itself. */ -+ if (open_one_pid(pid)) { -+ fprintf(stderr, "Cannot attach to pid %u: %s\n", -+ pid, strerror(errno)); - return; - } - -- proc = open_program(filename, pid); -- continue_process(pid); -- proc->breakpoints_enabled = 1; -+ /* Now attach to all tasks that belong to that PID. There's a -+ * race between process_tasks and open_one_pid. So when we -+ * fail in open_one_pid below, we just do another round. -+ * Chances are that by then that PID will have gone away, and -+ * that's why we have seen the failure. The processes that we -+ * manage to open_one_pid are stopped, so we should eventually -+ * reach a point where process_tasks doesn't give any new -+ * processes (because there's nobody left to produce -+ * them). */ -+ size_t old_ntasks = 0; -+ int have_all; -+ while (1) { -+ pid_t *tasks; -+ size_t ntasks; -+ size_t i; -+ -+ if (process_tasks(pid, &tasks, &ntasks) < 0) { -+ fprintf(stderr, "Cannot obtain tasks of pid %u: %s\n", -+ pid, strerror(errno)); -+ goto start; -+ } -+ -+ have_all = 1; -+ for (i = 0; i < ntasks; ++i) -+ if (pid2proc(tasks[i]) == NULL -+ && open_one_pid(tasks[i])) -+ have_all = 0; -+ -+ free(tasks); -+ -+ if (have_all && old_ntasks == ntasks) -+ break; -+ old_ntasks = ntasks; -+ } -+ -+ /* Done. Now initialize breakpoints and then continue -+ * everyone. */ -+ Process * leader; -+start: -+ leader = pid2proc(pid)->leader; -+ enable_all_breakpoints(leader); -+ -+ each_task(pid2proc(pid)->leader, start_one_pid, NULL); -+} -+ -+static enum pcb_status -+find_proc(Process * proc, void * data) -+{ -+ pid_t pid = (pid_t)(uintptr_t)data; -+ return proc->pid == pid ? pcb_stop : pcb_cont; - } - - Process * - pid2proc(pid_t pid) { -- Process *tmp; -+ return each_process(NULL, &find_proc, (void *)(uintptr_t)pid); -+} - -+ -+static Process * list_of_processes = NULL; -+ -+Process * -+each_process(Process * proc, -+ enum pcb_status (* cb)(Process * proc, void * data), -+ void * data) -+{ -+ Process * it = proc ?: list_of_processes; -+ for (; it != NULL; ) { -+ /* Callback might call remove_process. */ -+ Process * next = it->next; -+ if ((*cb) (it, data) == pcb_stop) -+ return it; -+ it = next; -+ } -+ return NULL; -+} -+ -+Process * -+each_task(Process * it, enum pcb_status (* cb)(Process * proc, void * data), -+ void * data) -+{ -+ if (it != NULL) { -+ Process * leader = it->leader; -+ for (; it != NULL && it->leader == leader; ) { -+ /* Callback might call remove_process. */ -+ Process * next = it->next; -+ if ((*cb) (it, data) == pcb_stop) -+ return it; -+ it = next; -+ } -+ } -+ return NULL; -+} -+ -+void -+add_process(Process * proc) -+{ -+ Process ** leaderp = &list_of_processes; -+ if (proc->pid) { -+ pid_t tgid = process_leader(proc->pid); -+ if (tgid == 0) -+ /* Must have been terminated before we managed -+ * to fully attach. */ -+ return; -+ if (tgid == proc->pid) -+ proc->leader = proc; -+ else { -+ Process * leader = pid2proc(tgid); -+ proc->leader = leader; -+ if (leader != NULL) -+ leaderp = &leader->next; -+ } -+ } -+ proc->next = *leaderp; -+ *leaderp = proc; -+} -+ -+static enum pcb_status -+clear_leader(Process * proc, void * data) -+{ -+ debug(DEBUG_FUNCTION, "detach_task %d from leader %d", -+ proc->pid, proc->leader->pid); -+ proc->leader = NULL; -+ return pcb_cont; -+} -+ -+static enum ecb_status -+event_for_proc(Event * event, void * data) -+{ -+ if (event->proc == data) -+ return ecb_deque; -+ else -+ return ecb_cont; -+} -+ -+static void -+delete_events_for(Process * proc) -+{ -+ Event * event; -+ while ((event = each_qd_event(&event_for_proc, proc)) != NULL) -+ free(event); -+} -+ -+void -+remove_process(Process *proc) -+{ -+ Process *tmp, *tmp2; -+ -+ debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); -+ -+ if (proc->leader == proc) -+ each_task(proc, &clear_leader, NULL); -+ -+ if (list_of_processes == proc) { -+ tmp = list_of_processes; -+ list_of_processes = list_of_processes->next; -+ delete_events_for(tmp); -+ free(tmp); -+ return; -+ } - tmp = list_of_processes; -- while (tmp) { -- if (pid == tmp->pid) { -- return tmp; -+ while (tmp->next) { -+ if (tmp->next == proc) { -+ tmp2 = tmp->next; -+ tmp->next = tmp->next->next; -+ delete_events_for(tmp2); -+ free(tmp2); -+ return; - } - tmp = tmp->next; - } -- return NULL; -+} -+ -+void -+install_event_handler(Process * proc, Event_Handler * handler) -+{ -+ debug(DEBUG_FUNCTION, "install_event_handler(pid=%d, %p)", proc->pid, handler); -+ assert(proc->event_handler == NULL); -+ proc->event_handler = handler; -+} -+ -+void -+destroy_event_handler(Process * proc) -+{ -+ Event_Handler * handler = proc->event_handler; -+ debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); -+ assert(handler != NULL); -+ handler->destroy(handler); -+ free(handler); -+ proc->event_handler = NULL; - } -diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c -index 9104189..5a49e9d 100644 ---- a/sysdeps/linux-gnu/breakpoint.c -+++ b/sysdeps/linux-gnu/breakpoint.c -@@ -8,21 +8,11 @@ - - #ifdef ARCH_HAVE_ENABLE_BREAKPOINT - extern void arch_enable_breakpoint(pid_t, Breakpoint *); -+#else /* ARCH_HAVE_ENABLE_BREAKPOINT */ - void --enable_breakpoint(pid_t pid, Breakpoint *sbp) { -- if (sbp->libsym) { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); -- } else { -- debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); -- } -- arch_enable_breakpoint(pid, sbp); --} --#else -- --static unsigned char break_insn[] = BREAKPOINT_VALUE; -- --void --enable_breakpoint(pid_t pid, Breakpoint *sbp) { -+arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) -+{ -+ static unsigned char break_insn[] = BREAKPOINT_VALUE; - unsigned int i, j; - - if (sbp->libsym) { -@@ -32,9 +22,8 @@ enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - - for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { -- long a = -- ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), -- 0); -+ long a = ptrace(PTRACE_PEEKTEXT, pid, -+ sbp->addr + i * sizeof(long), 0); - for (j = 0; - j < sizeof(long) - && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { -@@ -48,20 +37,22 @@ enable_breakpoint(pid_t pid, Breakpoint *sbp) { - } - #endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ - --#ifdef ARCH_HAVE_DISABLE_BREAKPOINT --extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); - void --disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+enable_breakpoint(Process * proc, Breakpoint *sbp) { - if (sbp->libsym) { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); -+ debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); - } else { -- debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); -+ debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); - } -- arch_disable_breakpoint(pid, sbp); -+ arch_enable_breakpoint(proc->pid, sbp); - } --#else -+ -+#ifdef ARCH_HAVE_DISABLE_BREAKPOINT -+extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); -+#else /* ARCH_HAVE_DISABLE_BREAKPOINT */ - void --disable_breakpoint(pid_t pid, const Breakpoint *sbp) { -+arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) -+{ - unsigned int i, j; - - if (sbp->libsym) { -@@ -85,3 +76,13 @@ disable_breakpoint(pid_t pid, const Breakpoint *sbp) { - } - } - #endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ -+ -+void -+disable_breakpoint(Process * proc, Breakpoint *sbp) { -+ if (sbp->libsym) { -+ debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", proc->pid, sbp->addr, sbp->libsym->name); -+ } else { -+ debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); -+ } -+ arch_disable_breakpoint(proc->pid, sbp); -+} -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index fd19e71..8a79583 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -8,20 +8,118 @@ - #include - #include - #include -+#include - - #include "common.h" - - static Event event; - -+/* A queue of events that we missed while enabling the -+ * breakpoint in one of tasks. */ -+static Event * delayed_events = NULL; -+static Event * end_delayed_events = NULL; -+ -+static enum pcb_status -+first (Process * proc, void * data) -+{ -+ return pcb_stop; -+} -+ -+void -+enque_event(Event * event) -+{ -+ debug(DEBUG_FUNCTION, "%d: queuing event %d for later", -+ event->proc->pid, event->type); -+ Event * ne = malloc(sizeof(*ne)); -+ if (ne == NULL) { -+ perror("event will be missed: malloc"); -+ return; -+ } -+ -+ *ne = *event; -+ ne->next = NULL; -+ if (end_delayed_events == NULL) { -+ assert(delayed_events == NULL); -+ end_delayed_events = delayed_events = ne; -+ } -+ else { -+ assert(delayed_events != NULL); -+ end_delayed_events = end_delayed_events->next = ne; -+ } -+} -+ -+Event * -+each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data) -+{ -+ Event * prev = delayed_events; -+ Event * event; -+ for (event = prev; event != NULL; ) { -+ switch ((*pred)(event, data)) { -+ case ecb_cont: -+ prev = event; -+ event = event->next; -+ continue; -+ -+ case ecb_deque: -+ debug(DEBUG_FUNCTION, "dequeuing event %d for %d", -+ event->type, -+ event->proc != NULL ? event->proc->pid : -1); -+ /* -+ printf("dequeuing event %d for %d\n", event->type, -+ event->proc != NULL ? event->proc->pid : -1) ; -+ */ -+ if (end_delayed_events == event) -+ end_delayed_events = prev; -+ if (delayed_events == event) -+ delayed_events = event->next; -+ else -+ prev->next = event->next; -+ if (delayed_events == NULL) -+ end_delayed_events = NULL; -+ /* fall-through */ -+ -+ case ecb_yield: -+ return event; -+ } -+ } -+ -+ return NULL; -+} -+ -+static enum ecb_status -+event_process_not_reenabling(Event * event, void * data) -+{ -+ if (event->proc == NULL -+ || event->proc->leader == NULL -+ || event->proc->leader->event_handler == NULL) -+ return ecb_deque; -+ else -+ return ecb_cont; -+} -+ -+static Event * -+next_qd_event(void) -+{ -+ return each_qd_event(&event_process_not_reenabling, NULL); -+} -+ - Event * --next_event(void) { -+next_event(void) -+{ - pid_t pid; - int status; - int tmp; - int stop_signal; - - debug(DEBUG_FUNCTION, "next_event()"); -- if (!list_of_processes) { -+ Event * ev; -+ if ((ev = next_qd_event()) != NULL) { -+ event = *ev; -+ free(ev); -+ return &event; -+ } -+ -+ if (!each_process(NULL, &first, NULL)) { - debug(DEBUG_EVENT, "event: No more traced programs: exiting"); - exit(0); - } -@@ -46,26 +144,76 @@ next_event(void) { - return &event; - } - get_arch_dep(event.proc); -- event.proc->instruction_pointer = NULL; - debug(3, "event from pid %u", pid); -- if (event.proc->breakpoints_enabled == -1) { -- event.type = EVENT_NONE; -+ if (event.proc->breakpoints_enabled == -1) - trace_set_options(event.proc, event.proc->pid); -- enable_all_breakpoints(event.proc); -- continue_process(event.proc->pid); -- debug(DEBUG_EVENT, "event: NONE: pid=%d (enabling breakpoints)", pid); -- return &event; -- } else if (!event.proc->libdl_hooked) { -- /* debug struct may not have been written yet.. */ -- if (linkmap_init(event.proc, &main_lte) == 0) { -- event.proc->libdl_hooked = 1; -+ Process *leader = event.proc->leader; -+ if (leader == event.proc) { -+ if (event.proc->breakpoints_enabled == -1) { -+ event.type = EVENT_NONE; -+ enable_all_breakpoints(event.proc); -+ continue_process(event.proc->pid); -+ debug(DEBUG_EVENT, -+ "event: NONE: pid=%d (enabling breakpoints)", -+ pid); -+ return &event; -+ } else if (!event.proc->libdl_hooked) { -+ /* debug struct may not have been written yet.. */ -+ if (linkmap_init(event.proc, &main_lte) == 0) { -+ event.proc->libdl_hooked = 1; -+ } - } - } - -- if (opt_i) { -- event.proc->instruction_pointer = -- get_instruction_pointer(event.proc); -+ /* The process should be stopped after the waitpid call. But -+ * when the whole thread group is terminated, we see -+ * individual tasks spontaneously transitioning from 't' to -+ * 'R' and 'Z'. Calls to ptrace fail and /proc/pid/status may -+ * not even be available anymore, so we can't check in -+ * advance. So we just drop the error checking around ptrace -+ * calls. We check for termination ex post when it fails, -+ * suppress the event, and let the event loop collect the -+ * termination in the next iteration. */ -+#define CHECK_PROCESS_TERMINATED \ -+ do { \ -+ int errno_save = errno; \ -+ switch (process_stopped(pid)) \ -+ case 0: \ -+ case -1: { \ -+ debug(DEBUG_EVENT, \ -+ "process not stopped, is it terminating?"); \ -+ event.type = EVENT_NONE; \ -+ continue_process(event.proc->pid); \ -+ return &event; \ -+ } \ -+ errno = errno_save; \ -+ } while (0) -+ -+ event.proc->instruction_pointer = (void *)(uintptr_t)-1; -+ -+ /* Check for task termination now, before we have a need to -+ * call CHECK_PROCESS_TERMINATED later. That would suppress -+ * the event that we are processing. */ -+ if (WIFSIGNALED(status)) { -+ event.type = EVENT_EXIT_SIGNAL; -+ event.e_un.signum = WTERMSIG(status); -+ debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum); -+ return &event; -+ } -+ if (WIFEXITED(status)) { -+ event.type = EVENT_EXIT; -+ event.e_un.ret_val = WEXITSTATUS(status); -+ debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val); -+ return &event; -+ } -+ -+ event.proc->instruction_pointer = get_instruction_pointer(event.proc); -+ if (event.proc->instruction_pointer == (void *)(uintptr_t)-1) { -+ CHECK_PROCESS_TERMINATED; -+ if (errno != 0) -+ perror("get_instruction_pointer"); - } -+ - switch (syscall_p(event.proc, status, &tmp)) { - case 1: - event.type = EVENT_SYSCALL; -@@ -88,10 +236,9 @@ next_event(void) { - debug(DEBUG_EVENT, "event: ARCH_SYSRET: pid=%d, sysnum=%d", pid, tmp); - return &event; - case -1: -- event.type = EVENT_NONE; -- continue_process(event.proc->pid); -- debug(DEBUG_EVENT, "event: NONE: pid=%d (syscall_p returned -1)", pid); -- return &event; -+ CHECK_PROCESS_TERMINATED; -+ if (errno != 0) -+ perror("syscall_p"); - } - if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) { - unsigned long data; -@@ -106,18 +253,6 @@ next_event(void) { - debug(DEBUG_EVENT, "event: EXEC: pid=%d", pid); - return &event; - } -- if (WIFEXITED(status)) { -- event.type = EVENT_EXIT; -- event.e_un.ret_val = WEXITSTATUS(status); -- debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val); -- return &event; -- } -- if (WIFSIGNALED(status)) { -- event.type = EVENT_EXIT_SIGNAL; -- event.e_un.signum = WTERMSIG(status); -- debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum); -- return &event; -- } - if (!WIFSTOPPED(status)) { - /* should never happen */ - event.type = EVENT_NONE; -@@ -128,22 +263,19 @@ next_event(void) { - stop_signal = WSTOPSIG(status); - - /* On some targets, breakpoints are signalled not using -- SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT. Check -- for these. (TODO: is this true?) */ -- if (stop_signal == SIGSEGV -- || stop_signal == SIGILL --#ifdef SIGEMT -- || stop_signal == SIGEMT --#endif -- ) { -- if (!event.proc->instruction_pointer) { -- event.proc->instruction_pointer = -- get_instruction_pointer(event.proc); -- } -+ SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT. SIGEMT -+ is not defined on Linux, but check for the others. - -- if (address2bpstruct(event.proc, event.proc->instruction_pointer)) -+ N.B. see comments in GDB's infrun.c for details. I've -+ actually seen this on an Itanium machine on RHEL 5, I don't -+ remember the exact kernel version anymore. ia64-sigill.s -+ in the test suite tests this. Petr Machata 2011-06-08. */ -+ void * break_address -+ = event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; -+ if ((stop_signal == SIGSEGV || stop_signal == SIGILL) -+ && leader != NULL -+ && address2bpstruct(leader, break_address)) - stop_signal = SIGTRAP; -- } - - if (stop_signal != (SIGTRAP | event.proc->tracesysgood) - && stop_signal != SIGTRAP) { -@@ -156,12 +288,8 @@ next_event(void) { - /* last case [by exhaustion] */ - event.type = EVENT_BREAKPOINT; - -- if (!event.proc->instruction_pointer) { -- event.proc->instruction_pointer = -- get_instruction_pointer(event.proc); -- } -- event.e_un.brk_addr = -- event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; -+ event.e_un.brk_addr = break_address; - debug(DEBUG_EVENT, "event: BREAKPOINT: pid=%d, addr=%p", pid, event.e_un.brk_addr); -+ - return &event; - } -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index e1cadf7..e3b71e5 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -1,13 +1,22 @@ -+#define _GNU_SOURCE /* For getline. */ - #include "config.h" - #include "common.h" - - #include -+#include -+#include - #include - #include - #include - #include - #include - #include -+#include -+#include -+#include -+#include -+#include -+ - - /* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' - * couldn't open it to find the executable. So it may be necessary to -@@ -16,17 +25,19 @@ - - #define MAX_DELAY 100000 /* 100000 microseconds = 0.1 seconds */ - -+#define PROC_PID_FILE(VAR, FORMAT, PID) \ -+ char VAR[strlen(FORMAT) + 6]; \ -+ sprintf(VAR, FORMAT, PID) -+ - /* - * Returns a (malloc'd) file name corresponding to a running pid - */ - char * - pid2name(pid_t pid) { -- char proc_exe[1024]; -- - if (!kill(pid, 0)) { - int delay = 0; - -- sprintf(proc_exe, "/proc/%d/exe", pid); -+ PROC_PID_FILE(proc_exe, "/proc/%d/exe", pid); - - while (delay < MAX_DELAY) { - if (!access(proc_exe, F_OK)) { -@@ -38,6 +49,197 @@ pid2name(pid_t pid) { - return NULL; - } - -+static FILE * -+open_status_file(pid_t pid) -+{ -+ PROC_PID_FILE(fn, "/proc/%d/status", pid); -+ /* Don't complain if we fail. This would typically happen -+ when the process is about to terminate, and these files are -+ not available anymore. This function is called from the -+ event loop, and we don't want to clutter the output just -+ because the process terminates. */ -+ return fopen(fn, "r"); -+} -+ -+static char * -+find_line_starting(FILE * file, const char * prefix, size_t len) -+{ -+ char * line = NULL; -+ size_t line_len = 0; -+ while (!feof(file)) { -+ if (getline(&line, &line_len, file) < 0) -+ return NULL; -+ if (strncmp(line, prefix, len) == 0) -+ return line; -+ } -+ return NULL; -+} -+ -+static void -+each_line_starting(FILE * file, const char *prefix, -+ enum pcb_status (*cb)(const char * line, const char * prefix, -+ void * data), -+ void * data) -+{ -+ size_t len = strlen(prefix); -+ char * line; -+ while ((line = find_line_starting(file, prefix, len)) != NULL) { -+ enum pcb_status st = (*cb)(line, prefix, data); -+ free (line); -+ if (st == pcb_stop) -+ return; -+ } -+} -+ -+static enum pcb_status -+process_leader_cb(const char * line, const char * prefix, void * data) -+{ -+ pid_t * pidp = data; -+ *pidp = atoi(line + strlen(prefix)); -+ return pcb_stop; -+} -+ -+pid_t -+process_leader(pid_t pid) -+{ -+ pid_t tgid = 0; -+ FILE * file = open_status_file(pid); -+ if (file != NULL) { -+ each_line_starting(file, "Tgid:\t", &process_leader_cb, &tgid); -+ fclose(file); -+ } -+ -+ return tgid; -+} -+ -+static enum pcb_status -+process_stopped_cb(const char * line, const char * prefix, void * data) -+{ -+ char c = line[strlen(prefix)]; -+ // t:tracing stop, T:job control stop -+ *(int *)data = (c == 't' || c == 'T'); -+ return pcb_stop; -+} -+ -+int -+process_stopped(pid_t pid) -+{ -+ int is_stopped = -1; -+ FILE * file = open_status_file(pid); -+ if (file != NULL) { -+ each_line_starting(file, "State:\t", &process_stopped_cb, -+ &is_stopped); -+ fclose(file); -+ } -+ return is_stopped; -+} -+ -+static enum pcb_status -+process_status_cb(const char * line, const char * prefix, void * data) -+{ -+ const char * status = line + strlen(prefix); -+ const char c = *status; -+ -+#define RETURN(C) do { \ -+ *(enum process_status *)data = C; \ -+ return pcb_stop; \ -+ } while (0) -+ -+ switch (c) { -+ case 'Z': RETURN(ps_zombie); -+ case 't': RETURN(ps_tracing_stop); -+ case 'T': { -+ /* This can be either "T (stopped)" or, for older -+ * kernels, "T (tracing stop)". */ -+ if (!strcmp(status, "T (stopped)\n")) -+ RETURN(ps_stop); -+ else if (!strcmp(status, "T (tracing stop)\n")) -+ RETURN(ps_tracing_stop); -+ else { -+ fprintf(stderr, "Unknown process status: %s", -+ status); -+ RETURN(ps_stop); /* Some sort of stop -+ * anyway. */ -+ } -+ } -+ } -+ -+ RETURN(ps_other); -+#undef RETURN -+} -+ -+enum process_status -+process_status(pid_t pid) -+{ -+ enum process_status ret = ps_invalid; -+ FILE * file = open_status_file(pid); -+ if (file != NULL) { -+ each_line_starting(file, "State:\t", &process_status_cb, &ret); -+ fclose(file); -+ if (ret == ps_invalid) -+ error(0, errno, "process_status %d", pid); -+ } else -+ /* If the file is not present, the process presumably -+ * exited already. */ -+ ret = ps_zombie; -+ -+ return ret; -+} -+ -+static int -+all_digits(const char *str) -+{ -+ while (isdigit(*str)) -+ str++; -+ return !*str; -+} -+ -+int -+process_tasks(pid_t pid, pid_t **ret_tasks, size_t *ret_n) -+{ -+ PROC_PID_FILE(fn, "/proc/%d/task", pid); -+ DIR * d = opendir(fn); -+ if (d == NULL) -+ return -1; -+ -+ pid_t *tasks = NULL; -+ size_t n = 0; -+ size_t alloc = 0; -+ -+ while (1) { -+ struct dirent entry; -+ struct dirent *result; -+ if (readdir_r(d, &entry, &result) != 0) { -+ free(tasks); -+ return -1; -+ } -+ if (result == NULL) -+ break; -+ if (result->d_type == DT_DIR && all_digits(result->d_name)) { -+ pid_t npid = atoi(result->d_name); -+ if (n >= alloc) { -+ alloc = alloc > 0 ? (2 * alloc) : 8; -+ pid_t *ntasks = realloc(tasks, -+ sizeof(*tasks) * alloc); -+ if (ntasks == NULL) { -+ free(tasks); -+ return -1; -+ } -+ tasks = ntasks; -+ } -+ if (n >= alloc) -+ abort(); -+ tasks[n++] = npid; -+ } -+ } -+ -+ closedir(d); -+ -+ *ret_tasks = tasks; -+ *ret_n = n; -+ return 0; -+} -+ - static int - find_dynamic_entry_addr(Process *proc, void *pvAddr, int d_tag, void **addr) { - int i = 0, done = 0; -@@ -187,7 +389,10 @@ linkmap_add_cb(void *data) { //const char *lib_name, ElfW(Addr) addr) { - addr = sym.st_value; - add_library_symbol(addr, xptr->name, &library_symbols, LS_TOPLT_NONE, 0); - xptr->found = 1; -- insert_breakpoint(lm_add->proc, sym2addr(lm_add->proc, library_symbols), library_symbols); -+ insert_breakpoint(lm_add->proc, -+ sym2addr(lm_add->proc, -+ library_symbols), -+ library_symbols, 1); - } - } - do_close_elf(<e); -@@ -275,10 +480,22 @@ linkmap_init(Process *proc, struct ltelf *lte) { - data.lte = lte; - - add_library_symbol(rdbg->r_brk, "", &library_symbols, LS_TOPLT_NONE, 0); -- insert_breakpoint(proc, sym2addr(proc, library_symbols), library_symbols); -+ insert_breakpoint(proc, sym2addr(proc, library_symbols), -+ library_symbols, 1); - - crawl_linkmap(proc, rdbg, hook_libdl_cb, &data); - - free(rdbg); - return 0; - } -+ -+int -+task_kill (pid_t pid, int sig) -+{ -+ // Taken from GDB -+ int ret; -+ -+ errno = 0; -+ ret = syscall (__NR_tkill, pid, sig); -+ return ret; -+} -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index e4be465..3800fad 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -7,6 +7,7 @@ - #include - #include "ptrace.h" - #include -+#include - - #include "common.h" - -@@ -69,7 +70,7 @@ umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { - - void - trace_me(void) { -- debug(DEBUG_PROCESS, "trace_me: pid=%d\n", getpid()); -+ debug(DEBUG_PROCESS, "trace_me: pid=%d", getpid()); - if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { - perror("PTRACE_TRACEME"); - exit(1); -@@ -78,7 +79,7 @@ trace_me(void) { - - int - trace_pid(pid_t pid) { -- debug(DEBUG_PROCESS, "trace_pid: pid=%d\n", pid); -+ debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid); - if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) { - return -1; - } -@@ -87,9 +88,9 @@ trace_pid(pid_t pid) { - in pid. The child is sent a SIGSTOP, but will not - necessarily have stopped by the completion of this call; - use wait() to wait for the child to stop. */ -- if (waitpid (pid, NULL, 0) != pid) { -+ if (waitpid (pid, NULL, __WALL) != pid) { - perror ("trace_pid: waitpid"); -- exit (1); -+ return -1; - } - - return 0; -@@ -100,7 +101,7 @@ trace_set_options(Process *proc, pid_t pid) { - if (proc->tracesysgood & 0x80) - return; - -- debug(DEBUG_PROCESS, "trace_set_options: pid=%d\n", pid); -+ debug(DEBUG_PROCESS, "trace_set_options: pid=%d", pid); - - long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | - PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | -@@ -115,7 +116,7 @@ trace_set_options(Process *proc, pid_t pid) { - - void - untrace_pid(pid_t pid) { -- debug(DEBUG_PROCESS, "untrace_pid: pid=%d\n", pid); -+ debug(DEBUG_PROCESS, "untrace_pid: pid=%d", pid); - ptrace(PTRACE_DETACH, pid, 1, 0); - } - -@@ -126,56 +127,652 @@ continue_after_signal(pid_t pid, int signum) { - - void - continue_after_signal(pid_t pid, int signum) { -- Process *proc; -- - debug(DEBUG_PROCESS, "continue_after_signal: pid=%d, signum=%d", pid, signum); -- -- proc = pid2proc(pid); -- if (proc && proc->breakpoint_being_enabled) { --#if defined __sparc__ || defined __ia64___ || defined __mips__ -- ptrace(PTRACE_SYSCALL, pid, 0, signum); --#else -- ptrace(PTRACE_SINGLESTEP, pid, 0, signum); --#endif -+ ptrace(PTRACE_SYSCALL, pid, 0, signum); -+} -+ -+static enum ecb_status -+event_for_pid(Event * event, void * data) -+{ -+ if (event->proc != NULL && event->proc->pid == (pid_t)(uintptr_t)data) -+ return ecb_yield; -+ return ecb_cont; -+} -+ -+static int -+have_events_for(pid_t pid) -+{ -+ return each_qd_event(event_for_pid, (void *)(uintptr_t)pid) != NULL; -+} -+ -+void -+continue_process(pid_t pid) -+{ -+ debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); -+ -+ /* Only really continue the process if there are no events in -+ the queue for this process. Otherwise just for the other -+ events to arrive. */ -+ if (!have_events_for(pid)) -+ /* We always trace syscalls to control fork(), -+ * clone(), execve()... */ -+ ptrace(PTRACE_SYSCALL, pid, 0, 0); -+ else -+ debug(DEBUG_PROCESS, -+ "putting off the continue, events in que."); -+} -+ -+/** -+ * This is used for bookkeeping related to PIDs that the event -+ * handlers work with. -+ */ -+struct pid_task { -+ pid_t pid; /* This may be 0 for tasks that exited -+ * mid-handling. */ -+ int sigstopped; -+ int got_event; -+ int delivered; -+} * pids; -+ -+struct pid_set { -+ struct pid_task * tasks; -+ size_t count; -+ size_t alloc; -+}; -+ -+/** -+ * Breakpoint re-enablement. When we hit a breakpoint, we must -+ * disable it, single-step, and re-enable it. That single-step can be -+ * done only by one task in a task group, while others are stopped, -+ * otherwise the processes would race for who sees the breakpoint -+ * disabled and who doesn't. The following is to keep track of it -+ * all. -+ */ -+struct process_stopping_handler -+{ -+ Event_Handler super; -+ -+ /* The task that is doing the re-enablement. */ -+ Process * task_enabling_breakpoint; -+ -+ /* The pointer being re-enabled. */ -+ Breakpoint * breakpoint_being_enabled; -+ -+ enum { -+ /* We are waiting for everyone to land in t/T. */ -+ psh_stopping = 0, -+ -+ /* We are doing the PTRACE_SINGLESTEP. */ -+ psh_singlestep, -+ -+ /* We are waiting for all the SIGSTOPs to arrive so -+ * that we can sink them. */ -+ psh_sinking, -+ -+ /* This is for tracking the ugly workaround. */ -+ psh_ugly_workaround, -+ } state; -+ -+ int exiting; -+ -+ struct pid_set pids; -+}; -+ -+static enum pcb_status -+task_stopped(Process * task, void * data) -+{ -+ /* If the task is already stopped, don't worry about it. -+ * Likewise if it managed to become a zombie or terminate in -+ * the meantime. This can happen when the whole thread group -+ * is terminating. */ -+ switch (process_status(task->pid)) { -+ case ps_invalid: -+ case ps_tracing_stop: -+ case ps_zombie: -+ return pcb_cont; -+ default: -+ return pcb_stop; -+ } -+} -+ -+static struct pid_task * -+get_task_info(struct pid_set * pids, pid_t pid) -+{ -+ assert(pid != 0); -+ size_t i; -+ for (i = 0; i < pids->count; ++i) -+ if (pids->tasks[i].pid == pid) -+ return &pids->tasks[i]; -+ -+ return NULL; -+} -+ -+static struct pid_task * -+add_task_info(struct pid_set * pids, pid_t pid) -+{ -+ if (pids->count == pids->alloc) { -+ size_t ns = (2 * pids->alloc) ?: 4; -+ struct pid_task * n = realloc(pids->tasks, -+ sizeof(*pids->tasks) * ns); -+ if (n == NULL) -+ return NULL; -+ pids->tasks = n; -+ pids->alloc = ns; -+ } -+ struct pid_task * task_info = &pids->tasks[pids->count++]; -+ memset(task_info, 0, sizeof(*task_info)); -+ task_info->pid = pid; -+ return task_info; -+} -+ -+static enum pcb_status -+send_sigstop(Process * task, void * data) -+{ -+ Process * leader = task->leader; -+ struct pid_set * pids = data; -+ -+ /* Look for pre-existing task record, or add new. */ -+ struct pid_task * task_info = get_task_info(pids, task->pid); -+ if (task_info == NULL) -+ task_info = add_task_info(pids, task->pid); -+ if (task_info == NULL) { -+ perror("send_sigstop: add_task_info"); -+ destroy_event_handler(leader); -+ /* Signal failure upwards. */ -+ return pcb_stop; -+ } -+ -+ /* This task still has not been attached to. It should be -+ stopped by the kernel. */ -+ if (task->state == STATE_BEING_CREATED) -+ return pcb_cont; -+ -+ /* Don't bother sending SIGSTOP if we are already stopped, or -+ * if we sent the SIGSTOP already, which happens when we -+ * inherit the handler from breakpoint re-enablement. */ -+ if (task_stopped(task, NULL) == pcb_cont) -+ return pcb_cont; -+ if (task_info->sigstopped) { -+ if (!task_info->delivered) -+ return pcb_cont; -+ task_info->delivered = 0; -+ } -+ -+ if (task_kill(task->pid, SIGSTOP) >= 0) { -+ debug(DEBUG_PROCESS, "send SIGSTOP to %d", task->pid); -+ task_info->sigstopped = 1; -+ } else -+ fprintf(stderr, -+ "Warning: couldn't send SIGSTOP to %d\n", task->pid); -+ -+ return pcb_cont; -+} -+ -+/* On certain kernels, detaching right after a singlestep causes the -+ tracee to be killed with a SIGTRAP (that even though the singlestep -+ was properly caught by waitpid. The ugly workaround is to put a -+ breakpoint where IP points and let the process continue. After -+ this the breakpoint can be retracted and the process detached. */ -+static void -+ugly_workaround(Process * proc) -+{ -+ void * ip = get_instruction_pointer(proc); -+ Breakpoint * sbp = dict_find_entry(proc->leader->breakpoints, ip); -+ if (sbp != NULL) -+ enable_breakpoint(proc, sbp); -+ else -+ insert_breakpoint(proc, ip, NULL, 1); -+ ptrace(PTRACE_CONT, proc->pid, 0, 0); -+} -+ -+static void -+process_stopping_done(struct process_stopping_handler * self, Process * leader) -+{ -+ debug(DEBUG_PROCESS, "process stopping done %d", -+ self->task_enabling_breakpoint->pid); -+ size_t i; -+ if (!self->exiting) { -+ for (i = 0; i < self->pids.count; ++i) -+ if (self->pids.tasks[i].pid != 0 -+ && self->pids.tasks[i].delivered) -+ continue_process(self->pids.tasks[i].pid); -+ continue_process(self->task_enabling_breakpoint->pid); -+ destroy_event_handler(leader); - } else { -- ptrace(PTRACE_SYSCALL, pid, 0, signum); -+ self->state = psh_ugly_workaround; -+ ugly_workaround(self->task_enabling_breakpoint); - } - } - --void --continue_process(pid_t pid) { -- /* We always trace syscalls to control fork(), clone(), execve()... */ -+/* Before we detach, we need to make sure that task's IP is on the -+ * edge of an instruction. So for tasks that have a breakpoint event -+ * in the queue, we adjust the instruction pointer, just like -+ * continue_after_breakpoint does. */ -+static enum ecb_status -+undo_breakpoint(Event * event, void * data) -+{ -+ if (event != NULL -+ && event->proc->leader == data -+ && event->type == EVENT_BREAKPOINT) -+ set_instruction_pointer(event->proc, event->e_un.brk_addr); -+ return ecb_cont; -+} - -- debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); -+static enum pcb_status -+untrace_task(Process * task, void * data) -+{ -+ if (task != data) -+ untrace_pid(task->pid); -+ return pcb_cont; -+} - -- ptrace(PTRACE_SYSCALL, pid, 0, 0); -+static enum pcb_status -+remove_task(Process * task, void * data) -+{ -+ /* Don't untrace leader just yet. */ -+ if (task != data) -+ remove_process(task); -+ return pcb_cont; - } - --void --continue_enabling_breakpoint(pid_t pid, Breakpoint *sbp) { -- enable_breakpoint(pid, sbp); -- continue_process(pid); -+static void -+detach_process(Process * leader) -+{ -+ each_qd_event(&undo_breakpoint, leader); -+ disable_all_breakpoints(leader); -+ -+ /* Now untrace the process, if it was attached to by -p. */ -+ struct opt_p_t * it; -+ for (it = opt_p; it != NULL; it = it->next) { -+ Process * proc = pid2proc(it->pid); -+ if (proc == NULL) -+ continue; -+ if (proc->leader == leader) { -+ each_task(leader, &untrace_task, NULL); -+ break; -+ } -+ } -+ each_task(leader, &remove_task, leader); -+ destroy_event_handler(leader); -+ remove_task(leader, NULL); -+} -+ -+static void -+handle_stopping_event(struct pid_task * task_info, Event ** eventp) -+{ -+ /* Mark all events, so that we know whom to SIGCONT later. */ -+ if (task_info != NULL) -+ task_info->got_event = 1; -+ -+ Event * event = *eventp; -+ -+ /* In every state, sink SIGSTOP events for tasks that it was -+ * sent to. */ -+ if (task_info != NULL -+ && event->type == EVENT_SIGNAL -+ && event->e_un.signum == SIGSTOP) { -+ debug(DEBUG_PROCESS, "SIGSTOP delivered to %d", task_info->pid); -+ if (task_info->sigstopped -+ && !task_info->delivered) { -+ task_info->delivered = 1; -+ *eventp = NULL; // sink the event -+ } else -+ fprintf(stderr, "suspicious: %d got SIGSTOP, but " -+ "sigstopped=%d and delivered=%d\n", -+ task_info->pid, task_info->sigstopped, -+ task_info->delivered); -+ } -+} -+ -+/* Some SIGSTOPs may have not been delivered to their respective tasks -+ * yet. They are still in the queue. If we have seen an event for -+ * that process, continue it, so that the SIGSTOP can be delivered and -+ * caught by ltrace. */ -+static void -+continue_for_sigstop_delivery(struct pid_set * pids) -+{ -+ size_t i; -+ for (i = 0; i < pids->count; ++i) { -+ if (pids->tasks[i].pid != 0 -+ && pids->tasks[i].sigstopped -+ && !pids->tasks[i].delivered -+ && pids->tasks[i].got_event) { -+ debug(DEBUG_PROCESS, "continue %d for SIGSTOP delivery", -+ pids->tasks[i].pid); -+ ptrace(PTRACE_SYSCALL, pids->tasks[i].pid, 0, 0); -+ } -+ } -+} -+ -+static int -+event_exit_p(Event * event) -+{ -+ return event != NULL && (event->type == EVENT_EXIT -+ || event->type == EVENT_EXIT_SIGNAL); -+} -+ -+static int -+event_exit_or_none_p(Event * event) -+{ -+ return event == NULL || event_exit_p(event) -+ || event->type == EVENT_NONE; -+} -+ -+static int -+await_sigstop_delivery(struct pid_set * pids, struct pid_task * task_info, -+ Event * event) -+{ -+ /* If we still didn't get our SIGSTOP, continue the process -+ * and carry on. */ -+ if (event != NULL && !event_exit_or_none_p(event) -+ && task_info != NULL && task_info->sigstopped) { -+ debug(DEBUG_PROCESS, "continue %d for SIGSTOP delivery", -+ task_info->pid); -+ /* We should get the signal the first thing -+ * after this, so it should be OK to continue -+ * even if we are over a breakpoint. */ -+ ptrace(PTRACE_SYSCALL, task_info->pid, 0, 0); -+ -+ } else { -+ /* If all SIGSTOPs were delivered, uninstall the -+ * handler and continue everyone. */ -+ /* XXX I suspect that we should check tasks that are -+ * still around. Is things are now, there should be a -+ * race between waiting for everyone to stop and one -+ * of the tasks exiting. */ -+ int all_clear = 1; -+ size_t i; -+ for (i = 0; i < pids->count; ++i) -+ if (pids->tasks[i].pid != 0 -+ && pids->tasks[i].sigstopped -+ && !pids->tasks[i].delivered) { -+ all_clear = 0; -+ break; -+ } -+ return all_clear; -+ } -+ -+ return 0; -+} -+ -+static int -+all_stops_accountable(struct pid_set * pids) -+{ -+ size_t i; -+ for (i = 0; i < pids->count; ++i) -+ if (pids->tasks[i].pid != 0 -+ && !pids->tasks[i].got_event -+ && !have_events_for(pids->tasks[i].pid)) -+ return 0; -+ return 1; -+} -+ -+/* This event handler is installed when we are in the process of -+ * stopping the whole thread group to do the pointer re-enablement for -+ * one of the threads. We pump all events to the queue for later -+ * processing while we wait for all the threads to stop. When this -+ * happens, we let the re-enablement thread to PTRACE_SINGLESTEP, -+ * re-enable, and continue everyone. */ -+static Event * -+process_stopping_on_event(Event_Handler * super, Event * event) -+{ -+ struct process_stopping_handler * self = (void *)super; -+ Process * task = event->proc; -+ Process * leader = task->leader; -+ Breakpoint * sbp = self->breakpoint_being_enabled; -+ Process * teb = self->task_enabling_breakpoint; -+ -+ debug(DEBUG_PROCESS, -+ "pid %d; event type %d; state %d", -+ task->pid, event->type, self->state); -+ -+ struct pid_task * task_info = get_task_info(&self->pids, task->pid); -+ if (task_info == NULL) -+ fprintf(stderr, "new task??? %d\n", task->pid); -+ handle_stopping_event(task_info, &event); -+ -+ int state = self->state; -+ int event_to_queue = !event_exit_or_none_p(event); -+ -+ /* Deactivate the entry if the task exits. */ -+ if (event_exit_p(event) && task_info != NULL) -+ task_info->pid = 0; -+ -+ switch (state) { -+ case psh_stopping: -+ /* If everyone is stopped, singlestep. */ -+ if (each_task(leader, &task_stopped, NULL) == NULL) { -+ debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d", -+ teb->pid); -+ if (sbp->enabled) -+ disable_breakpoint(teb, sbp); -+ if (ptrace(PTRACE_SINGLESTEP, teb->pid, 0, 0)) -+ perror("PTRACE_SINGLESTEP"); -+ self->state = state = psh_singlestep; -+ } -+ break; -+ -+ case psh_singlestep: { -+ /* In singlestep state, breakpoint signifies that we -+ * have now stepped, and can re-enable the breakpoint. */ -+ if (event != NULL && task == teb) { -+ /* Essentially we don't care what event caused -+ * the thread to stop. We can do the -+ * re-enablement now. */ -+ if (sbp->enabled) -+ enable_breakpoint(teb, sbp); -+ -+ continue_for_sigstop_delivery(&self->pids); -+ -+ self->breakpoint_being_enabled = NULL; -+ self->state = state = psh_sinking; -+ -+ if (event->type == EVENT_BREAKPOINT) -+ event = NULL; // handled -+ } else -+ break; -+ } -+ -+ /* fall-through */ -+ -+ case psh_sinking: -+ if (await_sigstop_delivery(&self->pids, task_info, event)) -+ process_stopping_done(self, leader); -+ break; -+ -+ case psh_ugly_workaround: -+ if (event == NULL) -+ break; -+ if (event->type == EVENT_BREAKPOINT) { -+ undo_breakpoint(event, leader); -+ if (task == teb) -+ self->task_enabling_breakpoint = NULL; -+ } -+ if (self->task_enabling_breakpoint == NULL -+ && all_stops_accountable(&self->pids)) { -+ undo_breakpoint(event, leader); -+ detach_process(leader); -+ event = NULL; // handled -+ } -+ } -+ -+ if (event != NULL && event_to_queue) { -+ enque_event(event); -+ event = NULL; // sink the event -+ } -+ -+ return event; -+} -+ -+static void -+process_stopping_destroy(Event_Handler * super) -+{ -+ struct process_stopping_handler * self = (void *)super; -+ free(self->pids.tasks); - } - - void --continue_after_breakpoint(Process *proc, Breakpoint *sbp) { -- if (sbp->enabled) -- disable_breakpoint(proc->pid, sbp); -+continue_after_breakpoint(Process *proc, Breakpoint *sbp) -+{ - set_instruction_pointer(proc, sbp->addr); - if (sbp->enabled == 0) { - continue_process(proc->pid); - } else { -- debug(DEBUG_PROCESS, "continue_after_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); -- proc->breakpoint_being_enabled = sbp; -+ debug(DEBUG_PROCESS, -+ "continue_after_breakpoint: pid=%d, addr=%p", -+ proc->pid, sbp->addr); - #if defined __sparc__ || defined __ia64___ || defined __mips__ - /* we don't want to singlestep here */ - continue_process(proc->pid); - #else -- ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0); -+ struct process_stopping_handler * handler -+ = calloc(sizeof(*handler), 1); -+ if (handler == NULL) { -+ perror("malloc breakpoint disable handler"); -+ fatal: -+ /* Carry on not bothering to re-enable. */ -+ continue_process(proc->pid); -+ return; -+ } -+ -+ handler->super.on_event = process_stopping_on_event; -+ handler->super.destroy = process_stopping_destroy; -+ handler->task_enabling_breakpoint = proc; -+ handler->breakpoint_being_enabled = sbp; -+ install_event_handler(proc->leader, &handler->super); -+ -+ if (each_task(proc->leader, &send_sigstop, -+ &handler->pids) != NULL) -+ goto fatal; -+ -+ /* And deliver the first fake event, in case all the -+ * conditions are already fulfilled. */ -+ Event ev; -+ ev.type = EVENT_NONE; -+ ev.proc = proc; -+ process_stopping_on_event(&handler->super, &ev); - #endif - } - } - -+/** -+ * Ltrace exit. When we are about to exit, we have to go through all -+ * the processes, stop them all, remove all the breakpoints, and then -+ * detach the processes that we attached to using -p. If we left the -+ * other tasks running, they might hit stray return breakpoints and -+ * produce artifacts, so we better stop everyone, even if it's a bit -+ * of extra work. -+ */ -+struct ltrace_exiting_handler -+{ -+ Event_Handler super; -+ struct pid_set pids; -+}; -+ -+static Event * -+ltrace_exiting_on_event(Event_Handler * super, Event * event) -+{ -+ struct ltrace_exiting_handler * self = (void *)super; -+ Process * task = event->proc; -+ Process * leader = task->leader; -+ -+ debug(DEBUG_PROCESS, "pid %d; event type %d", task->pid, event->type); -+ -+ struct pid_task * task_info = get_task_info(&self->pids, task->pid); -+ handle_stopping_event(task_info, &event); -+ -+ if (event != NULL && event->type == EVENT_BREAKPOINT) -+ undo_breakpoint(event, leader); -+ -+ if (await_sigstop_delivery(&self->pids, task_info, event) -+ && all_stops_accountable(&self->pids)) -+ detach_process(leader); -+ -+ /* Sink all non-exit events. We are about to exit, so we -+ * don't bother with queuing them. */ -+ if (event_exit_or_none_p(event)) -+ return event; -+ -+ return NULL; -+} -+ -+static void -+ltrace_exiting_destroy(Event_Handler * super) -+{ -+ struct ltrace_exiting_handler * self = (void *)super; -+ free(self->pids.tasks); -+} -+ -+static int -+ltrace_exiting_install_handler(Process * proc) -+{ -+ /* Only install to leader. */ -+ if (proc->leader != proc) -+ return 0; -+ -+ /* Perhaps we are already installed, if the user passed -+ * several -p options that are tasks of one process. */ -+ if (proc->event_handler != NULL -+ && proc->event_handler->on_event == <race_exiting_on_event) -+ return 0; -+ -+ /* If stopping handler is already present, let it do the -+ * work. */ -+ if (proc->event_handler != NULL) { -+ assert(proc->event_handler->on_event -+ == &process_stopping_on_event); -+ struct process_stopping_handler * other -+ = (void *)proc->event_handler; -+ other->exiting = 1; -+ return 0; -+ } -+ -+ struct ltrace_exiting_handler * handler -+ = calloc(sizeof(*handler), 1); -+ if (handler == NULL) { -+ perror("malloc exiting handler"); -+ fatal: -+ /* XXXXXXXXXXXXXXXXXXX fixme */ -+ return -1; -+ } -+ -+ handler->super.on_event = ltrace_exiting_on_event; -+ handler->super.destroy = ltrace_exiting_destroy; -+ install_event_handler(proc->leader, &handler->super); -+ -+ if (each_task(proc->leader, &send_sigstop, -+ &handler->pids) != NULL) -+ goto fatal; -+ -+ return 0; -+} -+ -+/* If ltrace gets SIGINT, the processes directly or indirectly run by -+ * ltrace get it too. We just have to wait long enough for the signal -+ * to be delivered and the process terminated, which we notice and -+ * exit ltrace, too. So there's not much we need to do there. We -+ * want to keep tracing those processes as usual, in case they just -+ * SIG_IGN the SIGINT to do their shutdown etc. -+ * -+ * For processes ran on the background, we want to install an exit -+ * handler that stops all the threads, removes all breakpoints, and -+ * detaches. -+ */ -+void -+ltrace_exiting(void) -+{ -+ struct opt_p_t * it; -+ for (it = opt_p; it != NULL; it = it->next) { -+ Process * proc = pid2proc(it->pid); -+ if (proc == NULL || proc->leader == NULL) -+ continue; -+ if (ltrace_exiting_install_handler(proc->leader) < 0) -+ fprintf(stderr, -+ "Couldn't install exiting handler for %d.\n", -+ proc->pid); -+ } -+} -+ - size_t - umovebytes(Process *proc, void *addr, void *laddr, size_t len) { - -diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c -index e8581af..d0299d9 100644 ---- a/sysdeps/linux-gnu/x86_64/trace.c -+++ b/sysdeps/linux-gnu/x86_64/trace.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - - #include "common.h" - #include "ptrace.h" -@@ -44,8 +45,11 @@ int - syscall_p(Process *proc, int status, int *sysnum) { - if (WIFSTOPPED(status) - && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { -- *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); -+ long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); -+ if (ret == -1 && errno) -+ return -1; - -+ *sysnum = ret; - if (proc->callstack_depth > 0 && - proc->callstack[proc->callstack_depth - 1].is_syscall && - proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { -diff --git a/testsuite/ltrace.main/main-threaded.c b/testsuite/ltrace.main/main-threaded.c -new file mode 100644 -index 0000000..a183966 ---- /dev/null -+++ b/testsuite/ltrace.main/main-threaded.c -@@ -0,0 +1,29 @@ -+#include -+ -+extern void print (char *); -+ -+#define PRINT_LOOP 10 -+ -+void * -+th_main (void *arg) -+{ -+ int i; -+ for (i=0; i. -+ -+set testfile "main-threaded" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+set libfile "main-lib" -+set libsrc $srcdir/$subdir/$libfile.c -+set lib_sl $objdir/$subdir/lib$testfile.so -+ -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" -+ || [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable [list debug shlib=$lib_sl ldflags=-pthread] ] != ""} { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" -+} -+ -+# set options for ltrace. -+ltrace_options "-l" "$objdir/$subdir/libmain.so" "-f" -+ -+# 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 -+} -+ -+# Verify the output by checking numbers of print in main-threaded.ltrace. -+set pattern "print(" -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 30 diff --git a/ltrace-0.6.0-vfork.patch b/ltrace-0.6.0-vfork.patch deleted file mode 100644 index 8f56414..0000000 --- a/ltrace-0.6.0-vfork.patch +++ /dev/null @@ -1,1130 +0,0 @@ -diff --git a/breakpoints.c b/breakpoints.c -index 1eff8b0..387b2a5 100644 ---- a/breakpoints.c -+++ b/breakpoints.c -@@ -203,14 +203,7 @@ breakpoints_init(Process *proc, int enable) - proc->breakpoints = dict_init(dict_key2hash_int, - dict_key_cmp_int); - -- if (proc->list_of_symbols != NULL) { -- struct library_symbol * sym = proc->list_of_symbols; -- while (sym != NULL) { -- struct library_symbol * next = sym->next; -- free(sym); -- sym = next; -- } -- } -+ destroy_library_symbol_chain(proc->list_of_symbols); - proc->list_of_symbols = NULL; - - if (options.libcalls && proc->filename) { -diff --git a/common.h b/common.h -index 49861cf..c0b24e5 100644 ---- a/common.h -+++ b/common.h -@@ -252,6 +252,7 @@ enum process_status { - ps_invalid, /* Failure. */ - ps_stop, /* Job-control stop. */ - ps_tracing_stop, -+ ps_sleeping, - ps_zombie, - ps_other, /* Necessary other states can be added as needed. */ - }; -@@ -265,6 +266,7 @@ enum pcb_status { - extern Process * pid2proc(pid_t pid); - extern void add_process(Process * proc); - extern void remove_process(Process * proc); -+extern void change_process_leader(Process * proc, Process * leader); - extern Process *each_process(Process * start, - enum pcb_status (* cb)(Process * proc, void * data), - void * data); -@@ -313,6 +315,10 @@ extern void add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, - enum toplt type_of_plt, int is_weak); - -+extern struct library_symbol * clone_library_symbol(struct library_symbol * s); -+extern void destroy_library_symbol(struct library_symbol * s); -+extern void destroy_library_symbol_chain(struct library_symbol * chain); -+ - /* Arch-dependent stuff: */ - extern char * pid2name(pid_t pid); - extern pid_t process_leader(pid_t pid); -@@ -335,6 +341,7 @@ extern int syscall_p(Process * proc, int status, int * sysnum); - extern void continue_process(pid_t pid); - extern void continue_after_signal(pid_t pid, int signum); - extern void continue_after_breakpoint(Process * proc, 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); -diff --git a/dict.c b/dict.c -index 486a461..ba318cd 100644 ---- a/dict.c -+++ b/dict.c -@@ -180,7 +180,9 @@ dict_key_cmp_int(void *key1, void *key2) { - } - - Dict * --dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)) { -+dict_clone2(Dict * old, void * (*key_clone)(void *, void *), -+ void * (*value_clone)(void *, void *), void * data) -+{ - Dict *d; - int i; - -@@ -199,17 +201,64 @@ dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)) - de_old = old->buckets[i]; - de_new = &d->buckets[i]; - while (de_old) { -+ void * nkey, * nval; - *de_new = malloc(sizeof(struct dict_entry)); - if (!*de_new) { - perror("malloc()"); - exit(1); - } - memcpy(*de_new, de_old, sizeof(struct dict_entry)); -- (*de_new)->key = key_clone(de_old->key); -- (*de_new)->value = value_clone(de_old->value); -+ -+ /* The error detection is rather weak :-/ */ -+ nkey = key_clone(de_old->key, data); -+ if (nkey == NULL && de_old->key != NULL) { -+ perror("key_clone"); -+ err: -+ /* XXX Will this actually work? We -+ * simply memcpy the old dictionary -+ * over up there. */ -+ dict_clear(d); -+ free(de_new); -+ return NULL; -+ } -+ -+ nval = value_clone(de_old->value, data); -+ if (nval == NULL && de_old->value != NULL) { -+ perror("value_clone"); -+ goto err; -+ } -+ -+ (*de_new)->key = nkey; -+ (*de_new)->value = nval; - de_new = &(*de_new)->next; - de_old = de_old->next; - } - } - return d; - } -+ -+struct wrap_clone_cb -+{ -+ void * (*key_clone)(void *); -+ void * (*value_clone)(void *); -+}; -+ -+static void * -+value_clone_1(void * arg, void * data) -+{ -+ return ((struct wrap_clone_cb *)data)->value_clone(arg); -+} -+ -+static void * -+key_clone_1(void * arg, void * data) -+{ -+ return ((struct wrap_clone_cb *)data)->key_clone(arg); -+} -+ -+Dict * -+dict_clone(Dict * old, void * (*key_clone)(void *), -+ void * (*value_clone)(void *)) -+{ -+ struct wrap_clone_cb cb = { key_clone, value_clone }; -+ return dict_clone2(old, &key_clone_1, &value_clone_1, &cb); -+} -diff --git a/dict.h b/dict.h -index a70c3d5..27dc7bf 100644 ---- a/dict.h -+++ b/dict.h -@@ -18,3 +18,7 @@ extern int dict_key_cmp_string(void *key1, void *key2); - extern unsigned int dict_key2hash_int(void *key); - extern int dict_key_cmp_int(void *key1, void *key2); - extern Dict * dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)); -+extern Dict * dict_clone2(Dict * old, -+ void * (* key_clone)(void * key, void * data), -+ void * (* value_clone)(void * value, void * data), -+ void * data); -diff --git a/handle_event.c b/handle_event.c -index 0aa40f7..f56c537 100644 ---- a/handle_event.c -+++ b/handle_event.c -@@ -7,6 +7,7 @@ - #include - #include - #include -+#include - - #include "common.h" - -@@ -35,6 +36,18 @@ static char * shortsignal(Process *proc, int signum); - static char * sysname(Process *proc, int sysnum); - static char * arch_sysname(Process *proc, int sysnum); - -+static Event * -+call_handler(Process * proc, Event * event) -+{ -+ assert(proc != NULL); -+ -+ Event_Handler * handler = proc->event_handler; -+ if (handler == NULL) -+ return event; -+ -+ return (*handler->on_event) (handler, event); -+} -+ - void - handle_event(Event *event) { - if (exiting == 1) { -@@ -44,15 +57,22 @@ handle_event(Event *event) { - } - debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", - event->proc ? event->proc->pid : -1, event->type); -- /* If the thread group defines an overriding event handler, -- give it a chance to kick in. */ -- if (event->proc != NULL -- && event->proc->leader != NULL) { -- Event_Handler * handler = event->proc->leader->event_handler; -- if (handler != NULL) { -- event = (*handler->on_event) (handler, event); -+ -+ /* If the thread group or an individual task define an -+ overriding event handler, give them a chance to kick in. -+ We will end up calling both handlers, if the first one -+ doesn't sink the event. */ -+ if (event->proc != NULL) { -+ event = call_handler(event->proc, event); -+ if (event == NULL) -+ /* It was handled. */ -+ return; -+ -+ /* Note: the previous handler has a chance to alter -+ * the event. */ -+ if (event->proc->leader != NULL) { -+ event = call_handler(event->proc->leader, event); - if (event == NULL) -- /* It was handled. */ - return; - } - } -@@ -102,6 +122,7 @@ handle_event(Event *event) { - handle_arch_sysret(event); - return; - case EVENT_CLONE: -+ case EVENT_VFORK: - debug(1, "event: clone (%u)", event->e_un.newpid); - handle_clone(event); - return; -@@ -125,14 +146,17 @@ handle_event(Event *event) { - - /* TODO */ - static void * --address_clone(void * addr) { -+address_clone(void * addr, void * data) -+{ - debug(DEBUG_FUNCTION, "address_clone(%p)", addr); - return addr; - } - - static void * --breakpoint_clone(void * bp) { -+breakpoint_clone(void * bp, void * data) -+{ - Breakpoint * b; -+ Dict * map = data; - debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); - b = malloc(sizeof(Breakpoint)); - if (!b) { -@@ -140,6 +164,15 @@ breakpoint_clone(void * bp) { - exit(1); - } - memcpy(b, bp, sizeof(Breakpoint)); -+ if (b->libsym != NULL) { -+ struct library_symbol * sym = dict_find_entry(map, b->libsym); -+ if (b->libsym == NULL) { -+ fprintf(stderr, "Can't find cloned symbol %s.\n", -+ b->libsym->name); -+ return NULL; -+ } -+ b->libsym = sym; -+ } - return b; - } - -@@ -204,6 +237,40 @@ pending_new_remove(pid_t pid) { - } - } - -+static int -+clone_breakpoints(Process * proc, Process * orig_proc) -+{ -+ /* When copying breakpoints, we also have to copy the -+ * referenced symbols, and link them properly. */ -+ Dict * map = dict_init(&dict_key2hash_int, &dict_key_cmp_int); -+ struct library_symbol * it = proc->list_of_symbols; -+ proc->list_of_symbols = NULL; -+ for (; it != NULL; it = it->next) { -+ struct library_symbol * libsym = clone_library_symbol(it); -+ if (libsym == NULL) { -+ int save_errno; -+ err: -+ save_errno = errno; -+ destroy_library_symbol_chain(proc->list_of_symbols); -+ dict_clear(map); -+ errno = save_errno; -+ return -1; -+ } -+ libsym->next = proc->list_of_symbols; -+ proc->list_of_symbols = libsym; -+ if (dict_enter(map, it, libsym) != 0) -+ goto err; -+ } -+ -+ proc->breakpoints = dict_clone2(orig_proc->breakpoints, -+ address_clone, breakpoint_clone, map); -+ if (proc->breakpoints == NULL) -+ goto err; -+ -+ dict_clear(map); -+ return 0; -+} -+ - static void - handle_clone(Event * event) { - Process *p; -@@ -216,7 +283,6 @@ handle_clone(Event * event) { - exit(1); - } - memcpy(p, event->proc, sizeof(Process)); -- p->breakpoints = dict_clone(event->proc->breakpoints, address_clone, breakpoint_clone); - p->pid = event->e_un.newpid; - p->parent = event->proc; - -@@ -239,7 +305,17 @@ handle_clone(Event * event) { - p->state = STATE_BEING_CREATED; - add_process(p); - } -- continue_process(event->proc->pid); -+ -+ if (p->leader == p) -+ clone_breakpoints(p, event->proc->leader); -+ else -+ /* Thread groups share breakpoints. */ -+ p->breakpoints = NULL; -+ -+ if (event->type == EVENT_VFORK) -+ continue_after_vfork(p); -+ else -+ continue_process(event->proc->pid); - } - - static void -@@ -253,8 +329,6 @@ handle_new(Event * event) { - pending_new_insert(event->e_un.newpid); - } else { - assert(proc->state == STATE_BEING_CREATED); -- if (proc->event_handler != NULL) -- destroy_event_handler(proc); - if (options.follow) { - proc->state = STATE_ATTACHED; - } else { -diff --git a/libltrace.c b/libltrace.c -index e731fe1..19bfafd 100644 ---- a/libltrace.c -+++ b/libltrace.c -@@ -107,6 +107,11 @@ ltrace_init(int argc, char **argv) { - } - } - if (command) { -+ /* Check that the binary ABI is supported before -+ * calling execute_program. */ -+ struct ltelf lte = {}; -+ open_elf(<e, command); -+ - open_program(command, execute_program(command, argv), 0); - } - opt_p_tmp = opt_p; -diff --git a/ltrace-elf.c b/ltrace-elf.c -index d88d5a6..9aea4a9 100644 ---- a/ltrace-elf.c -+++ b/ltrace-elf.c -@@ -136,18 +136,14 @@ static GElf_Addr get_glink_vma(struct ltelf *lte, GElf_Addr ppcgot, - } - - int --do_init_elf(struct ltelf *lte, const char *filename) { -- int i; -- GElf_Addr relplt_addr = 0; -- size_t relplt_size = 0; -- -- debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); -- debug(1, "Reading ELF from %s...", filename); -- -+open_elf(struct ltelf *lte, const char *filename) -+{ - lte->fd = open(filename, O_RDONLY); - if (lte->fd == -1) - return 1; - -+ elf_version(EV_CURRENT); -+ - #ifdef HAVE_ELF_C_READ_MMAP - lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL); - #else -@@ -180,6 +176,21 @@ do_init_elf(struct ltelf *lte, const char *filename) { - error(EXIT_FAILURE, 0, - "\"%s\" is ELF from incompatible architecture", filename); - -+ return 0; -+} -+ -+int -+do_init_elf(struct ltelf *lte, const char *filename) { -+ int i; -+ GElf_Addr relplt_addr = 0; -+ size_t relplt_size = 0; -+ -+ debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); -+ debug(1, "Reading ELF from %s...", filename); -+ -+ if (open_elf(lte, filename) < 0) -+ return -1; -+ - Elf_Data *plt_data = NULL; - GElf_Addr ppcgot = 0; - -@@ -465,30 +476,76 @@ do_close_elf(struct ltelf *lte) { - close(lte->fd); - } - -+static struct library_symbol * -+create_library_symbol(const char * name, GElf_Addr addr) -+{ -+ size_t namel = strlen(name) + 1; -+ struct library_symbol * sym = calloc(sizeof(*sym) + namel, 1); -+ if (sym == NULL) { -+ perror("create_library_symbol"); -+ return NULL; -+ } -+ sym->name = (char *)(sym + 1); -+ memcpy(sym->name, name, namel); -+ sym->enter_addr = (void *)(uintptr_t) addr; -+ return sym; -+} -+ - void - add_library_symbol(GElf_Addr addr, const char *name, - struct library_symbol **library_symbolspp, -- enum toplt type_of_plt, int is_weak) { -+ enum toplt type_of_plt, int is_weak) -+{ - struct library_symbol *s; - - debug(DEBUG_FUNCTION, "add_library_symbol()"); - -- s = malloc(sizeof(struct library_symbol) + strlen(name) + 1); -+ s = create_library_symbol(name, addr); - if (s == NULL) - error(EXIT_FAILURE, errno, "add_library_symbol failed"); - - s->needs_init = 1; - s->is_weak = is_weak; - s->plt_type = type_of_plt; -+ - s->next = *library_symbolspp; -- s->enter_addr = (void *)(uintptr_t) addr; -- s->name = (char *)(s + 1); -- strcpy(s->name, name); - *library_symbolspp = s; - - debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name); - } - -+struct library_symbol * -+clone_library_symbol(struct library_symbol * sym) -+{ -+ struct library_symbol * copy -+ = create_library_symbol(sym->name, -+ (GElf_Addr)(uintptr_t)sym->enter_addr); -+ if (copy == NULL) -+ return NULL; -+ -+ copy->needs_init = sym->needs_init; -+ copy->is_weak = sym->is_weak; -+ copy->plt_type = sym->plt_type; -+ -+ return copy; -+} -+ -+void -+destroy_library_symbol(struct library_symbol * sym) -+{ -+ free(sym); -+} -+ -+void -+destroy_library_symbol_chain(struct library_symbol * sym) -+{ -+ while (sym != NULL) { -+ struct library_symbol * next = sym->next; -+ destroy_library_symbol(sym); -+ sym = next; -+ } -+} -+ - /* stolen from elfutils-0.123 */ - static unsigned long - private_elf_gnu_hash(const char *name) { -@@ -620,8 +677,6 @@ read_elf(Process *proc) { - library_num = 0; - proc->libdl_hooked = 0; - -- elf_version(EV_CURRENT); -- - if (do_init_elf(lte, proc->filename)) - return NULL; - -@@ -637,7 +692,7 @@ read_elf(Process *proc) { - for (i = 0; i < library_num; ++i) { - if (do_init_elf(<e[i + 1], library[i])) - error(EXIT_FAILURE, errno, "Can't open \"%s\"", -- proc->filename); -+ library[i]); - } - - if (!options.no_plt) { -diff --git a/ltrace-elf.h b/ltrace-elf.h -index a29fe2c..3b675c5 100644 ---- a/ltrace-elf.h -+++ b/ltrace-elf.h -@@ -44,6 +44,7 @@ struct ltelf { - extern size_t library_num; - extern char *library[MAX_LIBRARIES]; - -+extern int open_elf(struct ltelf *lte, const char *filename); - extern struct library_symbol *read_elf(Process *); - - extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); -diff --git a/ltrace.h b/ltrace.h -index 0ff4572..194704d 100644 ---- a/ltrace.h -+++ b/ltrace.h -@@ -9,6 +9,7 @@ enum Event_type { - EVENT_ARCH_SYSCALL, - EVENT_ARCH_SYSRET, - EVENT_CLONE, -+ EVENT_VFORK, - EVENT_EXEC, - EVENT_BREAKPOINT, - EVENT_LIBCALL, -diff --git a/proc.c b/proc.c -index 0425e09..f4d3396 100644 ---- a/proc.c -+++ b/proc.c -@@ -154,9 +154,30 @@ pid2proc(pid_t pid) { - return each_process(NULL, &find_proc, (void *)(uintptr_t)pid); - } - -- - static Process * list_of_processes = NULL; - -+static void -+unlist_process(Process * proc) -+{ -+ Process *tmp; -+ -+ if (list_of_processes == proc) { -+ list_of_processes = list_of_processes->next; -+ return; -+ } -+ -+ for (tmp = list_of_processes; ; tmp = tmp->next) { -+ /* If the following assert fails, the process wasn't -+ * in the list. */ -+ assert(tmp->next != NULL); -+ -+ if (tmp->next == proc) { -+ tmp->next = tmp->next->next; -+ return; -+ } -+ } -+} -+ - Process * - each_process(Process * proc, - enum pcb_status (* cb)(Process * proc, void * data), -@@ -213,6 +234,23 @@ add_process(Process * proc) - *leaderp = proc; - } - -+void -+change_process_leader(Process * proc, Process * leader) -+{ -+ Process ** leaderp = &list_of_processes; -+ if (proc->leader == leader) -+ return; -+ -+ assert(leader != NULL); -+ unlist_process(proc); -+ if (proc != leader) -+ leaderp = &leader->next; -+ -+ proc->leader = leader; -+ proc->next = *leaderp; -+ *leaderp = proc; -+} -+ - static enum pcb_status - clear_leader(Process * proc, void * data) - { -@@ -242,31 +280,14 @@ delete_events_for(Process * proc) - void - remove_process(Process *proc) - { -- Process *tmp, *tmp2; -- - debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); - - if (proc->leader == proc) - each_task(proc, &clear_leader, NULL); - -- if (list_of_processes == proc) { -- tmp = list_of_processes; -- list_of_processes = list_of_processes->next; -- delete_events_for(tmp); -- free(tmp); -- return; -- } -- tmp = list_of_processes; -- while (tmp->next) { -- if (tmp->next == proc) { -- tmp2 = tmp->next; -- tmp->next = tmp->next->next; -- delete_events_for(tmp2); -- free(tmp2); -- return; -- } -- tmp = tmp->next; -- } -+ unlist_process(proc); -+ delete_events_for(proc); -+ free(proc); - } - - void -@@ -283,7 +304,8 @@ destroy_event_handler(Process * proc) - Event_Handler * handler = proc->event_handler; - debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler); - assert(handler != NULL); -- handler->destroy(handler); -+ if (handler->destroy != NULL) -+ handler->destroy(handler); - free(handler); - proc->event_handler = NULL; - } -diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c -index 8a79583..0685342 100644 ---- a/sysdeps/linux-gnu/events.c -+++ b/sysdeps/linux-gnu/events.c -@@ -240,13 +240,20 @@ next_event(void) - if (errno != 0) - perror("syscall_p"); - } -- if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) { -- unsigned long data; -- ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data); -- event.type = EVENT_CLONE; -- event.e_un.newpid = data; -- debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", pid, (int)data); -- return &event; -+ if (WIFSTOPPED(status)) { -+ int what = status >> 16; -+ if (what == PTRACE_EVENT_VFORK -+ || what == PTRACE_EVENT_FORK -+ || what == PTRACE_EVENT_CLONE) { -+ unsigned long data; -+ event.type = what == PTRACE_EVENT_VFORK -+ ? EVENT_VFORK : EVENT_CLONE; -+ ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data); -+ event.e_un.newpid = data; -+ debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", -+ pid, (int)data); -+ return &event; -+ } - } - if (WIFSTOPPED(status) && (status>>16 == PTRACE_EVENT_EXEC)) { - event.type = EVENT_EXEC; -diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c -index e3b71e5..a99593c 100644 ---- a/sysdeps/linux-gnu/proc.c -+++ b/sysdeps/linux-gnu/proc.c -@@ -148,7 +148,7 @@ process_status_cb(const char * line, const char * prefix, void * data) - switch (c) { - case 'Z': RETURN(ps_zombie); - case 't': RETURN(ps_tracing_stop); -- case 'T': { -+ case 'T': - /* This can be either "T (stopped)" or, for older - * kernels, "T (tracing stop)". */ - if (!strcmp(status, "T (stopped)\n")) -@@ -161,7 +161,8 @@ process_status_cb(const char * line, const char * prefix, void * data) - RETURN(ps_stop); /* Some sort of stop - * anyway. */ - } -- } -+ case 'D': -+ case 'S': RETURN(ps_sleeping); - } - - RETURN(ps_other); -diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c -index f8a1779..ba3806d 100644 ---- a/sysdeps/linux-gnu/trace.c -+++ b/sysdeps/linux-gnu/trace.c -@@ -164,9 +164,10 @@ continue_process(pid_t pid) - struct pid_task { - pid_t pid; /* This may be 0 for tasks that exited - * mid-handling. */ -- int sigstopped; -- int got_event; -- int delivered; -+ int sigstopped : 1; -+ int got_event : 1; -+ int delivered : 1; -+ int vforked : 1; - } * pids; - - struct pid_set { -@@ -213,23 +214,6 @@ struct process_stopping_handler - struct pid_set pids; - }; - --static enum pcb_status --task_stopped(Process * task, void * data) --{ -- /* If the task is already stopped, don't worry about it. -- * Likewise if it managed to become a zombie or terminate in -- * the meantime. This can happen when the whole thread group -- * is terminating. */ -- switch (process_status(task->pid)) { -- case ps_invalid: -- case ps_tracing_stop: -- case ps_zombie: -- return pcb_cont; -- default: -- return pcb_stop; -- } --} -- - static struct pid_task * - get_task_info(struct pid_set * pids, pid_t pid) - { -@@ -261,6 +245,57 @@ add_task_info(struct pid_set * pids, pid_t pid) - } - - static enum pcb_status -+task_stopped(Process * task, void * data) -+{ -+ enum process_status st = process_status(task->pid); -+ if (data != NULL) -+ *(enum process_status *)data = st; -+ -+ /* If the task is already stopped, don't worry about it. -+ * Likewise if it managed to become a zombie or terminate in -+ * the meantime. This can happen when the whole thread group -+ * is terminating. */ -+ switch (st) { -+ case ps_invalid: -+ case ps_tracing_stop: -+ case ps_zombie: -+ return pcb_cont; -+ default: -+ return pcb_stop; -+ } -+} -+ -+/* Task is blocked if it's stopped, or if it's a vfork parent. */ -+static enum pcb_status -+task_blocked(Process * task, void * data) -+{ -+ struct pid_set * pids = data; -+ struct pid_task * task_info = get_task_info(pids, task->pid); -+ if (task_info != NULL -+ && task_info->vforked) -+ return pcb_cont; -+ -+ return task_stopped(task, NULL); -+} -+ -+static Event * process_vfork_on_event(Event_Handler * super, Event * event); -+ -+static enum pcb_status -+task_vforked(Process * task, void * data) -+{ -+ if (task->event_handler != NULL -+ && task->event_handler->on_event == &process_vfork_on_event) -+ return pcb_stop; -+ return pcb_cont; -+} -+ -+static int -+is_vfork_parent(Process * task) -+{ -+ return each_task(task->leader, &task_vforked, NULL) != NULL; -+} -+ -+static enum pcb_status - send_sigstop(Process * task, void * data) - { - Process * leader = task->leader; -@@ -283,9 +318,11 @@ send_sigstop(Process * task, void * data) - return pcb_cont; - - /* Don't bother sending SIGSTOP if we are already stopped, or -- * if we sent the SIGSTOP already, which happens when we -- * inherit the handler from breakpoint re-enablement. */ -- if (task_stopped(task, NULL) == pcb_cont) -+ * if we sent the SIGSTOP already, which happens when we are -+ * handling "onexit" and inherited the handler from breakpoint -+ * re-enablement. */ -+ enum process_status st; -+ if (task_stopped(task, &st) == pcb_cont) - return pcb_cont; - if (task_info->sigstopped) { - if (!task_info->delivered) -@@ -293,6 +330,16 @@ send_sigstop(Process * task, void * data) - task_info->delivered = 0; - } - -+ /* Also don't attempt to stop the process if it's a parent of -+ * vforked process. We set up event handler specially to hint -+ * us. In that case parent is in D state, which we use to -+ * weed out unnecessary looping. */ -+ if (st == ps_sleeping -+ && is_vfork_parent (task)) { -+ task_info->vforked = 1; -+ return pcb_cont; -+ } -+ - if (task_kill(task->pid, SIGSTOP) >= 0) { - debug(DEBUG_PROCESS, "send SIGSTOP to %d", task->pid); - task_info->sigstopped = 1; -@@ -536,7 +583,7 @@ process_stopping_on_event(Event_Handler * super, Event * event) - switch (state) { - case psh_stopping: - /* If everyone is stopped, singlestep. */ -- if (each_task(leader, &task_stopped, NULL) == NULL) { -+ if (each_task(leader, &task_blocked, &self->pids) == NULL) { - debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d", - teb->pid); - if (sbp->enabled) -@@ -742,6 +789,110 @@ ltrace_exiting_install_handler(Process * proc) - return 0; - } - -+/* -+ * When the traced process vforks, it's suspended until the child -+ * process calls _exit or exec*. In the meantime, the two share the -+ * address space. -+ * -+ * The child process should only ever call _exit or exec*, but we -+ * can't count on that (it's not the role of ltrace to policy, but to -+ * observe). In any case, we will _at least_ have to deal with -+ * removal of vfork return breakpoint (which we have to smuggle back -+ * in, so that the parent can see it, too), and introduction of exec* -+ * return breakpoint. Since we already have both breakpoint actions -+ * to deal with, we might as well support it all. -+ * -+ * The gist is that we pretend that the child is in a thread group -+ * with its parent, and handle it as a multi-threaded case, with the -+ * exception that we know that the parent is blocked, and don't -+ * attempt to stop it. When the child execs, we undo the setup. -+ * -+ * XXX The parent process could be un-suspended before ltrace gets -+ * child exec/exit event. Make sure this is taken care of. -+ */ -+ -+struct process_vfork_handler -+{ -+ Event_Handler super; -+ void * bp_addr; -+}; -+ -+static Event * -+process_vfork_on_event(Event_Handler * super, Event * event) -+{ -+ struct process_vfork_handler * self = (void *)super; -+ Breakpoint * sbp; -+ assert(self != NULL); -+ -+ switch (event->type) { -+ case EVENT_BREAKPOINT: -+ /* Remember the vfork return breakpoint. */ -+ if (self->bp_addr == NULL) -+ self->bp_addr = event->e_un.brk_addr; -+ break; -+ -+ case EVENT_EXIT: -+ case EVENT_EXIT_SIGNAL: -+ case EVENT_EXEC: -+ /* Smuggle back in the vfork return breakpoint, so -+ * that our parent can trip over it once again. */ -+ if (self->bp_addr != NULL) { -+ sbp = dict_find_entry(event->proc->leader->breakpoints, -+ self->bp_addr); -+ if (sbp != NULL) -+ insert_breakpoint(event->proc->leader, -+ self->bp_addr, sbp->libsym, -+ 1); -+ } -+ -+ continue_process(event->proc->parent->pid); -+ -+ /* Remove the leader that we artificially set up -+ * earlier. */ -+ change_process_leader(event->proc, event->proc); -+ destroy_event_handler(event->proc); -+ -+ /* XXXXX this could happen in the middle of handling -+ * multi-threaded breakpoint. We must be careful to -+ * undo the effects that we introduced above (vforked -+ * = 1 et.al.). */ -+ -+ default: -+ ; -+ } -+ -+ return event; -+} -+ -+void -+continue_after_vfork(Process * proc) -+{ -+ debug(DEBUG_PROCESS, "continue_after_vfork: pid=%d", proc->pid); -+ struct process_vfork_handler * handler = calloc(sizeof(*handler), 1); -+ if (handler == NULL) { -+ perror("malloc vfork handler"); -+ /* Carry on not bothering to treat the process as -+ * necessary. */ -+ continue_process(proc->parent->pid); -+ return; -+ } -+ -+ /* We must set up custom event handler, so that we see -+ * exec/exit events for the task itself. */ -+ handler->super.on_event = process_vfork_on_event; -+ install_event_handler(proc, &handler->super); -+ -+ /* Make sure that the child is sole thread. */ -+ assert(proc->leader == proc); -+ assert(proc->next == NULL || proc->next->leader != proc); -+ -+ /* Make sure that the child's parent is properly set up. */ -+ assert(proc->parent != NULL); -+ assert(proc->parent->leader != NULL); -+ -+ change_process_leader(proc, proc->parent->leader); -+} -+ - /* If ltrace gets SIGINT, the processes directly or indirectly run by - * ltrace get it too. We just have to wait long enough for the signal - * to be delivered and the process terminated, which we notice and -diff --git a/testsuite/ltrace.main/main-threaded.exp b/testsuite/ltrace.main/main-threaded.exp -index 0157797..5539805 100644 ---- a/testsuite/ltrace.main/main-threaded.exp -+++ b/testsuite/ltrace.main/main-threaded.exp -@@ -19,7 +19,7 @@ if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" - } - - # set options for ltrace. --ltrace_options "-l" "$objdir/$subdir/libmain.so" "-f" -+ltrace_options "-l" "$lib_sl" "-f" - - # Run PUT for ltarce. - set exec_output [ltrace_runtest $objdir/$subdir $objdir/$subdir/$binfile] -@@ -35,5 +35,5 @@ if [regexp {ELF from incompatible architecture} $exec_output] { - } - - # Verify the output by checking numbers of print in main-threaded.ltrace. --set pattern "print(" -+set pattern "print" - ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace $pattern 30 -diff --git a/testsuite/ltrace.main/main-vfork.c b/testsuite/ltrace.main/main-vfork.c -new file mode 100644 -index 0000000..a5f6c40 ---- /dev/null -+++ b/testsuite/ltrace.main/main-vfork.c -@@ -0,0 +1,28 @@ -+#include -+ -+extern void print (char *); -+ -+#define PRINT_LOOP 10 -+ -+void -+th_main (char * arg) -+{ -+ int i; -+ for (i=0; i. -+ -+set testfile "main-vfork" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+set libfile "main-lib" -+set libsrc $srcdir/$subdir/$libfile.c -+set lib_sl $objdir/$subdir/lib$testfile.so -+ -+ -+if [get_compiler_info $binfile] { -+ return -1 -+} -+ -+verbose "compiling source file now....." -+if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" -+ || [ltrace_compile $srcdir/$subdir/$srcfile $objdir/$subdir/$binfile executable [list debug shlib=$lib_sl] ] != ""} { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" -+} -+ -+# set options for ltrace. -+ltrace_options "-l" "$lib_sl" "-f" -+ -+# 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 -+} -+ -+# Verify the output by checking numbers of print in main-vfork.ltrace. -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "print" 20 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "vfork resumed" 2 -diff --git a/testsuite/ltrace.torture/vfork-thread.c b/testsuite/ltrace.torture/vfork-thread.c -new file mode 100644 -index 0000000..f909bd3 ---- /dev/null -+++ b/testsuite/ltrace.torture/vfork-thread.c -@@ -0,0 +1,50 @@ -+#include -+#include -+#include -+#include -+ -+ -+void * -+routine (void *data) -+{ -+ int i; -+ for (i = 0; i < 6; ++i) -+ { -+ puts ("bleble"); -+ sleep (1); -+ } -+} -+ -+ -+void * -+routine2 (void *data) -+{ -+ pid_t child = vfork (); -+ if (child == 0) -+ { -+ int i, j; -+ puts ("vforked"); -+ for (i = 0; i < 100000; ++i) -+ for (j = 0; j < 10000; ++j) -+ ; -+ puts ("vforked child exiting"); -+ _exit (0); -+ } -+ puts ("parent continuing"); -+ return NULL; -+} -+ -+int -+main(int argc, char *argv[]) -+{ -+ pthread_t thread; -+ pthread_create (&thread, NULL, &routine, NULL); -+ -+ sleep (1); -+ -+ pthread_t thread2; -+ pthread_create (&thread2, NULL, &routine2, NULL); -+ pthread_join (thread2, NULL); -+ pthread_join (thread, NULL); -+ return 0; -+} -diff --git a/testsuite/ltrace.torture/vfork-thread.exp b/testsuite/ltrace.torture/vfork-thread.exp -new file mode 100644 -index 0000000..bd01319 ---- /dev/null -+++ b/testsuite/ltrace.torture/vfork-thread.exp -@@ -0,0 +1,32 @@ -+# This file was written by Yao Qi . -+ -+set testfile "vfork-thread" -+set srcfile ${testfile}.c -+set binfile ${testfile} -+ -+ -+verbose "compiling source file now....." -+# Build the shared libraries this test case needs. -+if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${objdir}/${subdir}/${binfile}" executable [list debug ldflags=-pthread] ] != "" } { -+ send_user "Testcase compile failed, so all tests in this file will automatically fail\n." -+} -+ -+ltrace_options "-f" -+ -+# 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 -+} -+ -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "puts" 9 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "sleep" 7 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "vfork resumed" 2 -+ltrace_verify_output ${objdir}/${subdir}/${testfile}.ltrace "_exit" 1 diff --git a/ltrace.spec b/ltrace.spec index e69a834..0996184 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,36 +1,18 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace -Version: 0.6.0 -Release: 12%{?dist} +Version: 0.7.2 +Release: 1%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers BuildRequires: elfutils-libelf-devel dejagnu -BuildRequires: autoconf automake BuildRequires: libselinux-devel -# Tarball generated from git checkout. To regenerate: -# git clone http://github.com/ice799/ltrace.git -# cd ltrace && ./autogen.sh && ./configure && make dist -Source: %{name}-%{version}.tar.bz2 - -Patch1: ltrace-0.5-ia64-sigill.patch -Patch2: ltrace-0.6.0-exec-stripped.patch -Patch3: ltrace-0.6.0-demangle.patch -Patch4: ltrace-0.5-etc-memmove.patch -Patch5: ltrace-0.6.0-return-string-n.patch -Patch6: ltrace-0.6.0-threads.patch -Patch7: ltrace-0.6.0-endian.patch -Patch8: ltrace-0.6.0-clone-test.patch -Patch9: ltrace-0.6.0-ppc-args.patch -Patch10: ltrace-0.6.0-ppc-shift.patch -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-dash-n.patch -Patch16: ltrace-0.6.0-library_num.patch +# Note: this URL needs to be updated for each release, as the file +# number changes for each file. Full list of released files is at: +# https://alioth.debian.org/frs/?group_id=30892 +Source: http://alioth.debian.org/frs/download.php/3848/ltrace-0.7.2.tar.bz2 %description Ltrace is a debugging program which runs a specified command until the @@ -44,29 +26,9 @@ execution of processes. %prep %setup -q -%patch1 -p1 -%patch2 -p1 -%patch3 -p1 -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 -%patch8 -p1 -%patch9 -p1 -%patch10 -p1 -%patch11 -p1 -%patch12 -p1 -%patch13 -p1 -%patch14 -p1 -%patch15 -p1 -%patch16 -p1 %build -# This ugly hack is necessary to build and link files for correct -# architecture. It makes a difference on ppc. -export CC="gcc`echo $RPM_OPT_FLAGS | sed -n 's/^.*\(-m[36][124]\).*$/ \1/p'` -D_LARGEFILE64_SOURCE" -# We touch configure.ac up there -%configure CC="$CC" +%configure make %{?_smp_mflags} %install @@ -81,24 +43,78 @@ echo ====================TESTING END===================== %files %defattr(-,root,root) -%doc COPYING README TODO BUGS ChangeLog +%doc COPYING README NEWS %{_bindir}/ltrace %{_mandir}/man1/ltrace.1* +%{_mandir}/man5/ltrace.conf.5* %config(noreplace) %{_sysconfdir}/ltrace.conf %changelog -* Fri Jun 1 2012 Petr Machata - 0.6.0-12 +* Mon Dec 10 2012 Petr Machata - 0.7.2-1 +- Upstream 0.7.2 + - Drop all the patches + +* Sat Nov 10 2012 Petr Machata - 0.7.0-1 +- Upstream 0.7.0 + - Drop all the patches + - Upstream patch for missing sysdeps/linux-gnu/ppc/insn.h + (ltrace-0.7.0-ppc-insn.h.patch) + - Upstream patch for installing ltrace.conf.5 to man5 + (ltrace-0.7.0-man5.patch) + +* Mon Oct 1 2012 Petr Machata - 0.6.0-19 +- Upstream patch for ia64 parameter passing + (ltrace-0.6.0-abi-ia64.patch) +- Upstream fix for a bug in computation of time spent in a syscall + (ltrace-0.6.0-syscall-time.patch) +- Upstream fix for a bug in passing struct(float,struct(float,float)) + on x86_64 (ltrace-0.6.0-x86_64-flatten.patch) +- Upstream patch for support of -l option (ltrace-0.6.0-dash-l.patch) +- Several more upstream patches with random cleanups. Those were + brought to Fedora to make porting of other patches easier. + (ltrace-0.6.0-cleanups.patch) + +* Thu Aug 30 2012 Petr Machata - 0.6.0-18 +- PPC64 passes floating point equivalent structures in registers + +* Thu Jul 19 2012 Fedora Release Engineering - 0.6.0-17 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Fri Jun 1 2012 Petr Machata - 0.6.0-16 - Look for __cxa_demangle in libstdc++ as well - Demangle test case should report it's unsupported if demangling - support isn't compiled in + support isn't compiled in (ltrace-0.6.0-demangle.patch) - Resolves: #827422 -* Thu May 3 2012 Petr Machata - 0.6.0-11 +* Thu May 31 2012 Petr Machata - 0.6.0-15 +- Add upstream patches for parameter passing. Apart from a couple of + fixes, this brings in s390 support (ltrace-0.6.0-abi-s390.patch) + +* Fri May 18 2012 Petr Machata - 0.6.0-14 +- Add upstream patch that improves parameter passing support (the + upstream "revamp" branch) (ltrace-0.6.0-abi.patch) + +* Thu May 3 2012 Petr Machata - 0.6.0-13 - Check -n argument for validity (ltrace-0.6.0-dash-n.patch) - Resolves: #818529 -- Don't suddenly forget all about libs passed in -l on startup - (ltrace-0.6.0-library_num.patch) -- Resolves: #818511 +- ltrace-0.6.0-libs-fixes-1.patch + - Fix double free when process initialization fails for some reason + - Don't indent first level of calls + +* Mon Apr 30 2012 Petr Machata - 0.6.0-12 +- Fix 32-bit builds + +* Mon Apr 30 2012 Petr Machata - 0.6.0-11 +- Fix detach from sleeping process +- Add limited support for return from tail call +- Fix singlestep over atomic instruction sequence on PPC +- Add extensive upstream patch that implements + - tracing calls done from DSOs + - better tools for filtering symbol tables + - support for tracing PLT calls on PPC64 (not entry points read from .plt) + - support for PPC32 old-style (BSS) PLT table +- Drop ppc-shift patch that was superseded by the above +- Drop demangle patch that hasn't been applied for some time now * Wed Apr 11 2012 Peter Robinson - 0.6.0-10 - Drop ExclusiveArch as all current Primary/Secondary Arches are supported diff --git a/sources b/sources index 2914d2c..852c53d 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -eca4ffd76293b2cae871b890b1cd4ddf ltrace-0.6.0.tar.bz2 +f5d9282b471cdf9fbafd916ec5be0717 ltrace-0.7.2.tar.bz2