diff --git a/SOURCES/ltrace-0.7.91-account_execl.patch b/SOURCES/ltrace-0.7.91-account_execl.patch new file mode 100644 index 0000000..2aa4ba0 --- /dev/null +++ b/SOURCES/ltrace-0.7.91-account_execl.patch @@ -0,0 +1,834 @@ +From 0cf4ab66e9927e101a51dd9fa9adc6c8dc56b5e7 Mon Sep 17 00:00:00 2001 +From: Petr Machata <pmachata@redhat.com> +Date: Thu, 21 Nov 2013 20:25:53 +0100 +Subject: [PATCH] Consider exec and exit events an end of outstanding calls + +- This cleans up a lot of stuff. The actual substance is addition of + account_current_callstack in handle_event.c (which however uses + those cleaned-up interfaces). + +- trace-exec.exp was extended to check that the exec syscall can be + seen in -c output. That's one of the symptoms of what this fixes. +--- + Makefile.am | 8 +- + common.h | 2 - + forward.h | 1 + + handle_event.c | 225 ++++++++++++++++++++------------- + libltrace.c | 5 +- + options.h | 8 +- + output.c | 86 +++---------- + output.h | 5 +- + proc.h | 2 +- + summary.c | 89 +++++++++++++- + summary.h | 35 +++++ + testsuite/ltrace.minor/trace-exec.exp | 16 ++- + 12 files changed, 299 insertions(+), 183 deletions(-) + create mode 100644 summary.h + +diff --git a/Makefile.am b/Makefile.am +index d711aec..efcf18a 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -54,10 +54,10 @@ ltrace_LDADD = \ + + noinst_HEADERS = bits.h backend.h breakpoint.h common.h debug.h \ + defs.h demangle.h dict.h forward.h ltrace-elf.h ltrace.h \ +- options.h output.h proc.h read_config_file.h library.h \ +- filter.h glob.h vect.h type.h value.h value_dict.h callback.h \ +- expr.h fetch.h vect.h param.h printf.h zero.h lens.h \ +- lens_default.h lens_enum.h memstream.h prototype.h ++ options.h output.h proc.h read_config_file.h summary.h \ ++ library.h filter.h glob.h vect.h type.h value.h value_dict.h \ ++ callback.h expr.h fetch.h vect.h param.h printf.h zero.h \ ++ lens.h lens_default.h lens_enum.h memstream.h prototype.h + + dist_man1_MANS = ltrace.1 + dist_man5_MANS = ltrace.conf.5 +diff --git a/common.h b/common.h +index a53c5db..7259ba4 100644 +--- a/common.h ++++ b/common.h +@@ -54,8 +54,6 @@ extern void handle_event(Event * event); + + extern pid_t execute_program(const char * command, char ** argv); + +-extern void show_summary(void); +- + struct breakpoint; + struct library_symbol; + +diff --git a/forward.h b/forward.h +index 8641213..58d8f05 100644 +--- a/forward.h ++++ b/forward.h +@@ -34,6 +34,7 @@ struct param_enum; + struct process; + struct protolib; + struct prototype; ++struct timedelta; + struct value; + struct value_dict; + struct vect; +diff --git a/handle_event.c b/handle_event.c +index 9ed62a2..6fa7e98 100644 +--- a/handle_event.c ++++ b/handle_event.c +@@ -32,7 +32,6 @@ + #include <stdio.h> + #include <stdlib.h> + #include <string.h> +-#include <sys/time.h> + #include <stdbool.h> + + #include "backend.h" +@@ -41,8 +40,9 @@ + #include "fetch.h" + #include "library.h" + #include "proc.h" +-#include "value_dict.h" + #include "prototype.h" ++#include "summary.h" ++#include "value_dict.h" + + static void handle_signal(Event *event); + static void handle_exit(Event *event); +@@ -419,32 +419,11 @@ handle_signal(Event *event) { + continue_after_signal(event->proc->pid, event->e_un.signum); + } + +-static void +-handle_exit(Event *event) { +- debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); +- if (event->proc->state != STATE_IGNORED) { +- output_line(event->proc, "+++ exited (status %d) +++", +- event->e_un.ret_val); +- } +- remove_process(event->proc); +-} +- +-static void +-handle_exit_signal(Event *event) { +- debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); +- if (event->proc->state != STATE_IGNORED) { +- output_line(event->proc, "+++ killed by %s +++", +- shortsignal(event->proc, event->e_un.signum)); +- } +- remove_process(event->proc); +-} +- +-static void +-output_syscall(struct process *proc, const char *name, enum tof tof, +- void (*output)(enum tof, struct process *, +- struct library_symbol *)) ++static int ++init_syscall_symbol(struct library_symbol *libsym, const char *name) + { + static struct library syscall_lib; ++ + if (syscall_lib.protolib == NULL) { + struct protolib *protolib + = protolib_cache_load(&g_protocache, "syscalls", 0, 1); +@@ -475,10 +454,91 @@ output_syscall(struct process *proc, const char *name, enum tof tof, + syscall_lib.protolib = protolib; + } + ++ if (library_symbol_init(libsym, 0, name, 0, LS_TOPLT_NONE) < 0) ++ return -1; ++ ++ libsym->lib = &syscall_lib; ++ return 0; ++} ++ ++/* Account the unfinished functions on the call stack. */ ++static void ++account_current_callstack(struct process *proc) ++{ ++ if (! options.summary) ++ return; ++ ++ struct timedelta spent[proc->callstack_depth]; ++ ++ size_t i; ++ for (i = 0; i < proc->callstack_depth; ++i) { ++ struct callstack_element *elem = &proc->callstack[i]; ++ spent[i] = calc_time_spent(elem->enter_time); ++ } ++ ++ for (i = 0; i < proc->callstack_depth; ++i) { ++ struct callstack_element *elem = &proc->callstack[i]; ++ struct library_symbol syscall, *libsym = NULL; ++ if (elem->is_syscall) { ++ const char *name = sysname(proc, elem->c_un.syscall); ++ if (init_syscall_symbol(&syscall, name) >= 0) ++ libsym = &syscall; ++ ++ } else { ++ libsym = elem->c_un.libfunc; ++ } ++ ++ if (libsym != NULL) { ++ summary_account_call(libsym, spent[i]); ++ ++ if (elem->is_syscall) ++ library_symbol_destroy(&syscall); ++ } ++ } ++} ++ ++static void ++handle_exit(Event *event) { ++ debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); ++ if (event->proc->state != STATE_IGNORED) { ++ output_line(event->proc, "+++ exited (status %d) +++", ++ event->e_un.ret_val); ++ } ++ ++ account_current_callstack(event->proc); ++ remove_process(event->proc); ++} ++ ++static void ++handle_exit_signal(Event *event) { ++ debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); ++ if (event->proc->state != STATE_IGNORED) { ++ output_line(event->proc, "+++ killed by %s +++", ++ shortsignal(event->proc, event->e_un.signum)); ++ } ++ ++ account_current_callstack(event->proc); ++ remove_process(event->proc); ++} ++ ++static void ++output_syscall(struct process *proc, const char *name, enum tof tof, ++ bool left, struct timedelta *spent) ++{ ++ if (left) ++ assert(spent == NULL); ++ + struct library_symbol syscall; +- if (library_symbol_init(&syscall, 0, name, 0, LS_TOPLT_NONE) >= 0) { +- syscall.lib = &syscall_lib; +- (*output)(tof, proc, &syscall); ++ if (init_syscall_symbol(&syscall, name) >= 0) { ++ if (left) { ++ if (! options.summary) ++ output_left(tof, proc, &syscall); ++ } else if (options.summary) { ++ summary_account_call(&syscall, *spent); ++ } else { ++ output_right(tof, proc, &syscall, spent); ++ } ++ + library_symbol_destroy(&syscall); + } + } +@@ -486,17 +546,19 @@ output_syscall(struct process *proc, const char *name, enum tof tof, + static void + output_syscall_left(struct process *proc, const char *name) + { +- output_syscall(proc, name, LT_TOF_SYSCALL, &output_left); ++ output_syscall(proc, name, LT_TOF_SYSCALL, true, NULL); + } + + static void +-output_syscall_right(struct process *proc, const char *name) ++output_syscall_right(struct process *proc, const char *name, ++ struct timedelta *spent) + { +- output_syscall(proc, name, LT_TOF_SYSCALLR, &output_right); ++ output_syscall(proc, name, LT_TOF_SYSCALLR, false, spent); + } + + static void +-handle_syscall(Event *event) { ++handle_syscall(Event *event) ++{ + debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + callstack_push_syscall(event->proc, event->e_un.sysnum); +@@ -526,6 +588,8 @@ handle_exec(Event *event) + } + output_line(proc, "--- Called exec() ---"); + ++ account_current_callstack(proc); ++ + if (process_exec(proc) < 0) { + fprintf(stderr, + "couldn't reinitialize process %d after exec\n", pid); +@@ -549,74 +613,58 @@ handle_arch_syscall(Event *event) { + continue_process(event->proc->pid); + } + +-struct timeval current_time_spent; +- + static void +-calc_time_spent(struct process *proc) ++handle_x_sysret(Event *event, char *(*name_cb)(struct process *, int)) + { +- struct timeval tv; +- struct timezone tz; +- struct timeval diff; +- struct callstack_element *elem; +- +- debug(DEBUG_FUNCTION, "calc_time_spent(pid=%d)", proc->pid); +- elem = &proc->callstack[proc->callstack_depth - 1]; +- +- gettimeofday(&tv, &tz); ++ debug(DEBUG_FUNCTION, "handle_x_sysret(pid=%d, sysnum=%d)", ++ event->proc->pid, event->e_un.sysnum); + +- diff.tv_sec = tv.tv_sec - elem->time_spent.tv_sec; +- if (tv.tv_usec >= elem->time_spent.tv_usec) { +- diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec; +- } else { +- diff.tv_sec--; +- diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec; +- } +- current_time_spent = diff; +-} ++ unsigned d = event->proc->callstack_depth; ++ assert(d > 0); ++ struct callstack_element *elem = &event->proc->callstack[d - 1]; ++ assert(elem->is_syscall); + +-static void +-handle_sysret(Event *event) { +- debug(DEBUG_FUNCTION, "handle_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { +- if (opt_T || options.summary) { +- calc_time_spent(event->proc); +- } ++ struct timedelta spent = calc_time_spent(elem->enter_time); + if (options.syscalls) + output_syscall_right(event->proc, +- sysname(event->proc, +- event->e_un.sysnum)); ++ name_cb(event->proc, ++ event->e_un.sysnum), ++ &spent); + +- 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_after_syscall(event->proc, event->e_un.sysnum, 1); + } + + static void +-handle_arch_sysret(Event *event) { +- debug(DEBUG_FUNCTION, "handle_arch_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); +- if (event->proc->state != STATE_IGNORED) { +- if (opt_T || options.summary) { +- calc_time_spent(event->proc); +- } +- if (options.syscalls) +- output_syscall_right(event->proc, +- arch_sysname(event->proc, +- event->e_un.sysnum)); +- callstack_pop(event->proc); +- } +- continue_process(event->proc->pid); ++handle_sysret(Event *event) ++{ ++ handle_x_sysret(event, &sysname); ++} ++ ++static void ++handle_arch_sysret(Event *event) ++{ ++ handle_x_sysret(event, &arch_sysname); + } + + static void + output_right_tos(struct process *proc) + { + size_t d = proc->callstack_depth; ++ assert(d > 0); + struct callstack_element *elem = &proc->callstack[d - 1]; +- if (proc->state != STATE_IGNORED) +- output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc); ++ assert(! elem->is_syscall); ++ ++ if (proc->state != STATE_IGNORED) { ++ struct timedelta spent = calc_time_spent(elem->enter_time); ++ if (options.summary) ++ summary_account_call(elem->c_un.libfunc, spent); ++ else ++ output_right(LT_TOF_FUNCTIONR, proc, elem->c_un.libfunc, ++ &spent); ++ } + } + + #ifndef ARCH_HAVE_SYMBOL_RET +@@ -645,14 +693,8 @@ handle_breakpoint(Event *event) + + for (i = event->proc->callstack_depth - 1; i >= 0; i--) { + if (brk_addr == event->proc->callstack[i].return_addr) { +- for (j = event->proc->callstack_depth - 1; j > i; j--) { ++ for (j = event->proc->callstack_depth - 1; j > i; j--) + callstack_pop(event->proc); +- } +- if (event->proc->state != STATE_IGNORED) { +- if (opt_T || options.summary) { +- calc_time_spent(event->proc); +- } +- } + + struct library_symbol *libsym = + event->proc->callstack[i].c_un.libfunc; +@@ -705,11 +747,14 @@ handle_breakpoint(Event *event) + /* breakpoint_on_hit may delete its own breakpoint, so we have + * to look it up again. */ + if ((sbp = address2bpstruct(leader, brk_addr)) != NULL) { ++ + if (event->proc->state != STATE_IGNORED + && sbp->libsym != NULL) { + event->proc->stack_pointer = get_stack_pointer(event->proc); + callstack_push_symfunc(event->proc, sbp); +- output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym); ++ if (! options.summary) ++ output_left(LT_TOF_FUNCTION, event->proc, ++ sbp->libsym); + } + + breakpoint_on_continue(sbp, event->proc); +@@ -743,7 +788,7 @@ callstack_push_syscall(struct process *proc, int sysnum) + proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; +- gettimeofday(&elem->time_spent, &tz); ++ gettimeofday(&elem->enter_time, &tz); + } + } + +@@ -781,7 +826,7 @@ callstack_push_symfunc(struct process *proc, struct breakpoint *bp) + + if (opt_T || options.summary) { + struct timezone tz; +- gettimeofday(&elem->time_spent, &tz); ++ gettimeofday(&elem->enter_time, &tz); + } + } + +diff --git a/libltrace.c b/libltrace.c +index 2d910a1..0112c9f 100644 +--- a/libltrace.c ++++ b/libltrace.c +@@ -32,11 +32,12 @@ + #include <string.h> + #include <unistd.h> + ++#include "backend.h" + #include "common.h" + #include "proc.h" +-#include "read_config_file.h" +-#include "backend.h" + #include "prototype.h" ++#include "read_config_file.h" ++#include "summary.h" + + char *command = NULL; + +diff --git a/options.h b/options.h +index 6c28ed9..d0df3a7 100644 +--- a/options.h ++++ b/options.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009,2010 Joe Damato + * Copyright (C) 1998,2002,2008 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -103,12 +103,6 @@ int parse_colon_separated_list(const char *paths, struct vect *vec); + /* Vector of struct opt_F_t. */ + extern struct vect opt_F; + +-struct opt_c_struct { +- int count; +- struct timeval tv; +-}; +-extern struct dict *dict_opt_c; +- + extern char **process_options(int argc, char **argv); + + #endif /* _OPTIONS_H_ */ +diff --git a/output.c b/output.c +index edf4522..82b6a5e 100644 +--- a/output.c ++++ b/output.c +@@ -44,16 +44,12 @@ + #include "param.h" + #include "proc.h" + #include "prototype.h" ++#include "summary.h" + #include "type.h" + #include "value.h" + #include "value_dict.h" + +-/* TODO FIXME XXX: include in common.h: */ +-extern struct timeval current_time_spent; +- +-struct dict *dict_opt_c = NULL; +- +-static struct process *current_proc = 0; ++static struct process *current_proc = NULL; + static size_t current_depth = 0; + static int current_column = 0; + +@@ -498,9 +494,8 @@ void + output_left(enum tof type, struct process *proc, + struct library_symbol *libsym) + { +- if (options.summary) { +- return; +- } ++ assert(! options.summary); ++ + if (current_proc) { + fprintf(options.output, " <unfinished ...>\n"); + current_column = 0; +@@ -572,70 +567,21 @@ output_left(enum tof type, struct process *proc, + stel->out.need_delim = need_delim; + } + +-static void +-free_stringp_cb(const char **stringp, void *data) +-{ +- free((char *)*stringp); +-} +- + void +-output_right(enum tof type, struct process *proc, struct library_symbol *libsym) ++output_right(enum tof type, struct process *proc, struct library_symbol *libsym, ++ struct timedelta *spent) + { ++ assert(! options.summary); ++ + struct prototype *func = lookup_symbol_prototype(proc, libsym); + if (func == NULL) + return; + +-again: +- if (options.summary) { +- if (dict_opt_c == NULL) { +- dict_opt_c = malloc(sizeof(*dict_opt_c)); +- if (dict_opt_c == NULL) { +- oom: +- fprintf(stderr, +- "Can't allocate memory for " +- "keeping track of -c.\n"); +- free(dict_opt_c); +- options.summary = 0; +- goto again; +- } +- DICT_INIT(dict_opt_c, char *, struct opt_c_struct, +- dict_hash_string, dict_eq_string, NULL); +- } +- +- struct opt_c_struct *st +- = DICT_FIND_REF(dict_opt_c, &libsym->name, +- struct opt_c_struct); +- if (st == NULL) { +- const char *na = strdup(libsym->name); +- struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}}; +- if (na == NULL +- || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) { +- free((char *)na); +- DICT_DESTROY(dict_opt_c, const char *, +- struct opt_c_struct, +- free_stringp_cb, NULL, NULL); +- goto oom; +- } +- st = DICT_FIND_REF(dict_opt_c, &libsym->name, +- struct opt_c_struct); +- assert(st != NULL); +- } +- +- if (st->tv.tv_usec + current_time_spent.tv_usec > 1000000) { +- st->tv.tv_usec += current_time_spent.tv_usec - 1000000; +- st->tv.tv_sec++; +- } else { +- st->tv.tv_usec += current_time_spent.tv_usec; +- } +- st->count++; +- st->tv.tv_sec += current_time_spent.tv_sec; +- return; +- } +- +- if (current_proc && (current_proc != proc || +- current_depth != proc->callstack_depth)) { ++ if (current_proc != NULL ++ && (current_proc != proc ++ || current_depth != proc->callstack_depth)) { + fprintf(options.output, " <unfinished ...>\n"); +- current_proc = 0; ++ current_proc = NULL; + } + if (current_proc != proc) { + begin_of_line(proc, type == LT_TOF_FUNCTIONR, 1); +@@ -689,10 +635,12 @@ again: + value_destroy(&retval); + + if (opt_T) { ++ assert(spent != NULL); + fprintf(options.output, " <%lu.%06d>", +- (unsigned long)current_time_spent.tv_sec, +- (int)current_time_spent.tv_usec); ++ (unsigned long) spent->tm.tv_sec, ++ (int) spent->tm.tv_usec); + } ++ + fprintf(options.output, "\n"); + + #if defined(HAVE_LIBUNWIND) +@@ -746,7 +694,7 @@ again: + } + #endif /* defined(HAVE_LIBUNWIND) */ + +- current_proc = 0; ++ current_proc = NULL; + current_column = 0; + } + +diff --git a/output.h b/output.h +index b9f0518..2e74d61 100644 +--- a/output.h ++++ b/output.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2011, 2012 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2011, 2012, 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -28,7 +28,8 @@ void output_line(struct process *proc, const char *fmt, ...); + void output_left(enum tof type, struct process *proc, + struct library_symbol *libsym); + void output_right(enum tof type, struct process *proc, +- struct library_symbol *libsym); ++ struct library_symbol *libsym, ++ struct timedelta *spent); + + /* This function is for emitting lists of comma-separated strings. + * +diff --git a/proc.h b/proc.h +index e8032fa..64f8fe2 100644 +--- a/proc.h ++++ b/proc.h +@@ -66,7 +66,7 @@ struct callstack_element { + } c_un; + int is_syscall; + arch_addr_t return_addr; +- struct timeval time_spent; ++ struct timeval enter_time; + struct fetch_context *fetch_context; + struct value_dict *arguments; + struct output_state out; +diff --git a/summary.c b/summary.c +index 9e22086..9103f71 100644 +--- a/summary.c ++++ b/summary.c +@@ -22,11 +22,15 @@ + + #include "config.h" + ++#include <sys/time.h> + #include <stdio.h> + #include <stdlib.h> +-#include <sys/time.h> ++#include <string.h> + +-#include "common.h" ++#include "summary.h" ++#include "dict.h" ++#include "library.h" ++#include "options.h" + + struct entry_st { + const char *name; +@@ -40,6 +44,32 @@ struct fill_struct_data { + unsigned long tot_usecs; + }; + ++struct opt_c_struct { ++ int count; ++ struct timeval tv; ++}; ++ ++static struct dict *dict_opt_c; ++ ++struct timedelta ++calc_time_spent(struct timeval start) ++{ ++ struct timeval tv; ++ gettimeofday(&tv, NULL); ++ ++ struct timeval diff; ++ diff.tv_sec = tv.tv_sec - start.tv_sec; ++ if (tv.tv_usec >= start.tv_usec) { ++ diff.tv_usec = tv.tv_usec - start.tv_usec; ++ } else { ++ diff.tv_sec--; ++ diff.tv_usec = 1000000 + tv.tv_usec - start.tv_usec; ++ } ++ ++ struct timedelta ret = { diff }; ++ return ret; ++} ++ + static enum callback_status + fill_struct(const char **namep, struct opt_c_struct *st, void *u) + { +@@ -114,3 +144,58 @@ show_summary(void) + + vect_destroy(&cdata.entries, NULL, NULL); + } ++ ++static void ++free_stringp_cb(const char **stringp, void *data) ++{ ++ free((char *)*stringp); ++} ++ ++void ++summary_account_call(struct library_symbol *libsym, struct timedelta spent) ++{ ++ assert(options.summary); ++ ++ if (dict_opt_c == NULL) { ++ dict_opt_c = malloc(sizeof(*dict_opt_c)); ++ if (dict_opt_c == NULL) { ++ oom: ++ fprintf(stderr, ++ "Can't allocate memory for " ++ "keeping track of -c.\n"); ++ free(dict_opt_c); ++ options.summary = 0; ++ return; ++ } ++ DICT_INIT(dict_opt_c, char *, struct opt_c_struct, ++ dict_hash_string, dict_eq_string, NULL); ++ } ++ ++ struct opt_c_struct *st = DICT_FIND_REF(dict_opt_c, &libsym->name, ++ struct opt_c_struct); ++ if (st == NULL) { ++ const char *na = strdup(libsym->name); ++ struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}}; ++ if (na == NULL ++ || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) { ++ free((char *) na); ++ DICT_DESTROY(dict_opt_c, const char *, ++ struct opt_c_struct, ++ free_stringp_cb, NULL, NULL); ++ goto oom; ++ } ++ st = DICT_FIND_REF(dict_opt_c, &libsym->name, ++ struct opt_c_struct); ++ assert(st != NULL); ++ } ++ ++ if (st->tv.tv_usec + spent.tm.tv_usec > 1000000) { ++ st->tv.tv_usec += spent.tm.tv_usec - 1000000; ++ st->tv.tv_sec++; ++ } else { ++ st->tv.tv_usec += spent.tm.tv_usec; ++ } ++ st->count++; ++ st->tv.tv_sec += spent.tm.tv_sec; ++ return; ++} +diff --git a/summary.h b/summary.h +new file mode 100644 +index 0000000..f680ef9 +--- /dev/null ++++ b/summary.h +@@ -0,0 +1,35 @@ ++/* ++ * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef _SUMMARY_H_ ++#define _SUMMARY_H_ ++ ++#include "forward.h" ++ ++struct timedelta { ++ struct timeval tm; ++}; ++ ++struct timedelta calc_time_spent(struct timeval start); ++void summary_account_call(struct library_symbol *libsym, ++ struct timedelta spent); ++void show_summary(void); ++ ++#endif /* _SUMMARY_H_ */ +diff --git a/testsuite/ltrace.minor/trace-exec.exp b/testsuite/ltrace.minor/trace-exec.exp +index 7a953de..57260f8 100644 +--- a/testsuite/ltrace.minor/trace-exec.exp ++++ b/testsuite/ltrace.minor/trace-exec.exp +@@ -1,5 +1,5 @@ + # This file is part of ltrace. +-# Copyright (C) 2012 Petr Machata, Red Hat Inc. ++# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License as +@@ -16,22 +16,30 @@ + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + # 02110-1301 USA + +-ltraceMatch [ltraceRun -xmain -- [ltraceCompile {} [ltraceSource c { ++set bin1 [ltraceCompile {} [ltraceSource c { + #include <unistd.h> + #include <stdlib.h> + int main(int argc, char ** argv) { + execl(argv[1], argv[1], NULL); + abort(); + } +-}]] [ltraceCompile {} [ltraceSource c { ++}]] ++ ++set bin2 [ltraceCompile {} [ltraceSource c { + #include <stdio.h> + int main(void) { + return puts("Hello, World."); + } +-}]]] { ++}]] ++ ++ltraceMatch [ltraceRun -xmain -- $bin1 $bin2] { + {{^execl\(} == 1} + {{^puts\(.*\) .*= 14} == 1} + {{^main\(} == 2} + } + ++ltraceMatch [ltraceRun -c -- $bin1 $bin2] { ++ {{exec} > 0} ++} ++ + ltraceDone +-- +1.7.6.5 + diff --git a/SOURCES/ltrace-0.7.91-breakpoint-on_install.patch b/SOURCES/ltrace-0.7.91-breakpoint-on_install.patch new file mode 100644 index 0000000..15959a3 --- /dev/null +++ b/SOURCES/ltrace-0.7.91-breakpoint-on_install.patch @@ -0,0 +1,81 @@ +From 56134ff5442bee4e128b189bb86cfc97dcb6f60a Mon Sep 17 00:00:00 2001 +From: Petr Machata <pmachata@redhat.com> +Date: Fri, 10 Jan 2014 20:05:15 +0100 +Subject: [PATCH 1/2] Add a new per-breakpoint callback on_install + +--- + breakpoint.h | 9 ++++++++- + breakpoints.c | 11 ++++++++++- + 2 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/breakpoint.h b/breakpoint.h +index 95964a8..c36f673 100644 +--- a/breakpoint.h ++++ b/breakpoint.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -46,6 +46,7 @@ + struct bp_callbacks { + void (*on_hit)(struct breakpoint *bp, struct process *proc); + void (*on_continue)(struct breakpoint *bp, struct process *proc); ++ void (*on_install)(struct breakpoint *bp, struct process *proc); + void (*on_retract)(struct breakpoint *bp, struct process *proc); + + /* Create a new breakpoint that should handle return from the +@@ -84,6 +85,12 @@ void breakpoint_on_continue(struct breakpoint *bp, struct process *proc); + * the instruction underneath it). */ + void breakpoint_on_retract(struct breakpoint *bp, struct process *proc); + ++/* Call ON_INSTALL handler of BP, if any is set. This should be ++ * called after the breakpoint is enabled for the first time, not ++ * every time it's enabled (such as after stepping over a site of a ++ * temporarily disabled breakpoint). */ ++void breakpoint_on_install(struct breakpoint *bp, struct process *proc); ++ + /* Call GET_RETURN_BP handler of BP, if any is set. If none is set, + * call CREATE_DEFAULT_RETURN_BP to obtain one. */ + int breakpoint_get_return_bp(struct breakpoint **ret, +diff --git a/breakpoints.c b/breakpoints.c +index 947cb71..c3fa275 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2007,2011,2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2007,2011,2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * Copyright (C) 1998,2001,2002,2003,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -85,6 +85,14 @@ breakpoint_on_retract(struct breakpoint *bp, struct process *proc) + (bp->cbs->on_retract)(bp, proc); + } + ++void ++breakpoint_on_install(struct breakpoint *bp, struct process *proc) ++{ ++ assert(bp != NULL); ++ if (bp->cbs != NULL && bp->cbs->on_install != NULL) ++ (bp->cbs->on_install)(bp, proc); ++} ++ + int + breakpoint_get_return_bp(struct breakpoint **ret, + struct breakpoint *bp, struct process *proc) +@@ -229,6 +237,7 @@ breakpoint_turn_on(struct breakpoint *bp, struct process *proc) + if (bp->enabled == 1) { + assert(proc->pid != 0); + enable_breakpoint(proc, bp); ++ breakpoint_on_install(bp, proc); + } + return 0; + } +-- +1.7.6.5 + diff --git a/SOURCES/ltrace-0.7.91-cant_open.patch b/SOURCES/ltrace-0.7.91-cant_open.patch new file mode 100644 index 0000000..4d3a0d4 --- /dev/null +++ b/SOURCES/ltrace-0.7.91-cant_open.patch @@ -0,0 +1,36 @@ +diff -urp ltrace-0.7.91/libltrace.c master/libltrace.c +--- ltrace-0.7.91/libltrace.c 2014-01-14 16:31:37.696174464 +0100 ++++ master/libltrace.c 2013-11-21 14:06:38.623701688 +0100 +@@ -113,9 +117,13 @@ ltrace_init(int argc, char **argv) { + if (command) { + /* Check that the binary ABI is supported before + * calling execute_program. */ +- struct ltelf lte; +- ltelf_init(<e, command); +- ltelf_destroy(<e); ++ { ++ struct ltelf lte; ++ if (ltelf_init(<e, command) == 0) ++ ltelf_destroy(<e); ++ else ++ exit(EXIT_FAILURE); ++ } + + pid_t pid = execute_program(command, argv); + struct process *proc = open_program(command, pid); +diff -urp ltrace-0.7.91/ltrace-elf.c master/ltrace-elf.c +--- ltrace-0.7.91/ltrace-elf.c 2014-01-14 16:31:37.688174420 +0100 ++++ master/ltrace-elf.c 2013-11-22 18:17:11.767721609 +0100 +@@ -361,8 +361,11 @@ ltelf_init(struct ltelf *lte, const char + { + memset(lte, 0, sizeof *lte); + lte->fd = open(filename, O_RDONLY); +- if (lte->fd == -1) ++ if (lte->fd == -1) { ++ fprintf(stderr, "Can't open %s: %s\n", filename, ++ strerror(errno)); + return 1; ++ } + + elf_version(EV_CURRENT); + diff --git a/SOURCES/ltrace-0.7.91-man.patch b/SOURCES/ltrace-0.7.91-man.patch new file mode 100644 index 0000000..d7a7dbf --- /dev/null +++ b/SOURCES/ltrace-0.7.91-man.patch @@ -0,0 +1,45 @@ +diff -up ltrace-0.7.91/options.c\~ ltrace-0.7.91/options.c +--- ltrace-0.7.91/options.c~ 2013-10-22 11:54:21.000000000 +0200 ++++ ltrace-0.7.91/options.c 2014-01-13 15:38:51.362221740 +0100 +@@ -128,6 +128,8 @@ usage_debug(void) { + "\n" + "Debugging options are mixed using bitwise-or.\n" + "Note that the meanings and values are subject to change.\n" ++ "Also note that these values are used inconsistently in ltrace, and the\n" ++ "only debuglevel that you can rely on is -D77 that will show everything.\n" + ); + } + +diff -up ltrace-0.7.91/ltrace.1\~ ltrace-0.7.91/ltrace.1 +--- ltrace-0.7.91/ltrace.1~ 2013-10-23 17:44:13.000000000 +0200 ++++ ltrace-0.7.91/ltrace.1 2014-01-13 15:51:24.236730677 +0100 +@@ -1,5 +1,5 @@ + .\" -*-nroff-*- +-.\" Copyright (c) 2012, 2013 Petr Machata, Red Hat Inc. ++.\" Copyright (c) 2012,2013,2014 Petr Machata, Red Hat Inc. + .\" Copyright (c) 1997-2005 Juan Cespedes <cespedes@debian.org> + .\" + .\" This program is free software; you can redistribute it and/or +@@ -118,9 +118,9 @@ Besides removing any initial underscore + this makes C++ function names readable. + .IP "\-D, \-\-debug \fRmask\fI" + Show debugging output of \fBltrace\fR itself. \fImask\fR is a number +-with internal meaning that's not really well defined at all. +-\fImask\fR of 77 shows all debug messages, which is what you usually +-need. ++describing which debug messages should be displayed. Use the option ++\-Dh to see what can be used, but note that currently the only ++reliable debugmask is 77, which shows all debug messages. + .IP "\-e \fIfilter" + A qualifying expression which modifies which library calls to trace. + The format of the filter expression is described in the section +@@ -156,7 +156,8 @@ dependency ordering. If you want to mak + library are actually called, use \fB-x @\fIlibrary_pattern\fR instead. + .IP \-L + When no -e option is given, don't assume the default action of +-\fB@MAIN\fR. ++\fB@MAIN\fR. In practice this means that library calls will not be ++traced. + .IP "\-n, \-\-indent \fInr" + Indent trace output by \fInr\fR spaces for each level of call + nesting. Using this option makes the program flow visualization easy diff --git a/SOURCES/ltrace-0.7.91-ppc64-fork.patch b/SOURCES/ltrace-0.7.91-ppc64-fork.patch new file mode 100644 index 0000000..53c66fd --- /dev/null +++ b/SOURCES/ltrace-0.7.91-ppc64-fork.patch @@ -0,0 +1,52 @@ +From 35742523e3daa0e59de0c1c3fdd8e5ff52891967 Mon Sep 17 00:00:00 2001 +From: Petr Machata <pmachata@redhat.com> +Date: Thu, 9 Jan 2014 23:41:50 +0100 +Subject: [PATCH] Fix a problem in tracing across fork on PPC64 + +In order to avoid single-stepping through large portions of the +dynamic linker, ltrace remembers at which address the instruction that +resolved a PLT slot is. It then puts a breakpoint to this address so +that it can fast-forward to that address next time it needs to catch a +PLT slot being resolved. + +When a process is cloned, the pointer to this breakpoint is simply +copied over to the new process, instead of being looked up in the new +process structures. This patches fixes this. +--- + sysdeps/linux-gnu/ppc/plt.c | 14 +++++++++++++- + 1 files changed, 13 insertions(+), 1 deletions(-) + +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 3ec1397..8715da6 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * Copyright (C) 2006 Paul Gilliam + * +@@ -1157,6 +1157,18 @@ int + arch_process_clone(struct process *retp, struct process *proc) + { + retp->arch = proc->arch; ++ ++ if (retp->arch.dl_plt_update_bp != NULL) { ++ /* Point it to the corresponding breakpoint in RETP. ++ * It must be there, this part of PROC has already ++ * been cloned to RETP. */ ++ retp->arch.dl_plt_update_bp ++ = address2bpstruct(retp, ++ retp->arch.dl_plt_update_bp->addr); ++ ++ assert(retp->arch.dl_plt_update_bp != NULL); ++ } ++ + return 0; + } + +-- +1.7.6.5 + diff --git a/SOURCES/ltrace-0.7.91-ppc64-unprelink.patch b/SOURCES/ltrace-0.7.91-ppc64-unprelink.patch new file mode 100644 index 0000000..a5929e3 --- /dev/null +++ b/SOURCES/ltrace-0.7.91-ppc64-unprelink.patch @@ -0,0 +1,221 @@ +From a0093ca43cf40d7e5f6cebeb64156062d2de46d9 Mon Sep 17 00:00:00 2001 +From: Petr Machata <pmachata@redhat.com> +Date: Fri, 10 Jan 2014 20:06:51 +0100 +Subject: [PATCH 2/2] Don't crash untraced calls via PLT in prelinked PPC64 + binaries + +In prelinked binaries, ltrace has to unprelinks PLT slots in order to +catch calls done through PLT. This makes the calls done through these +slots invalid, because the special first PLT slot is not initialized, +and dynamic linker SIGSEGVs because of this. Ltrace relies on +arranging breakpoints such that the dynamic linker is not actually +entered, and moves PC around itself to simulate the effects of a call +through PLT. + +Originally, arch_elf_add_plt_entry was called only for symbols that +were actually traced. Later this was changed and it's now called for +all PLT entries, and the resulting candidate list is filtered +afterwards. This gives backends a chance to rename the symbol, as is +useful with IRELATIVE PLT calls, where symbol name may not be +available at all. But the PPC backend was never updated to reflect +this, and unresolved all symbols for which arch_elf_add_plt_entry was +called, thus rendering _all_ PLT slots invalid, even those that +weren't later procted by breakpoints. Thus calls done through any +untraced slots failed. + +This patch fixes this problem by deferring the unprelinking of PLT +slots into the on_install hook of breakpoints. +--- + sysdeps/linux-gnu/ppc/arch.h | 21 ++++++++- + sysdeps/linux-gnu/ppc/plt.c | 94 +++++++++++++++++++++++++++++++++-------- + 2 files changed, 94 insertions(+), 21 deletions(-) + +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index 2add3b8..bf9b5dc 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012,2013 Petr Machata ++ * Copyright (C) 2012,2013,2014 Petr Machata + * Copyright (C) 2006 Paul Gilliam + * Copyright (C) 2002,2004 Juan Cespedes + * +@@ -87,12 +87,29 @@ enum ppc64_plt_type { + /* Very similar to PPC_PLT_UNRESOLVED, but for JMP_IREL + * slots. */ + PPC_PLT_IRELATIVE, ++ ++ /* Transitional state before the breakpoint is enabled. */ ++ PPC_PLT_NEED_UNRESOLVE, + }; + + #define ARCH_HAVE_LIBRARY_SYMBOL_DATA ++struct ppc_unresolve_data; + struct arch_library_symbol_data { + enum ppc64_plt_type type; +- GElf_Addr resolved_value; ++ ++ /* State Contents ++ * ++ * PPC_DEFAULT N/A ++ * PPC64_PLT_STUB N/A ++ * PPC_PLT_UNRESOLVED PLT entry address. ++ * PPC_PLT_IRELATIVE Likewise. ++ * PPC_PLT_RESOLVED The original value the slot was resolved to. ++ * PPC_PLT_NEED_UNRESOLVE DATA. ++ */ ++ union { ++ GElf_Addr resolved_value; ++ struct ppc_unresolve_data *data; ++ }; + + /* Address of corresponding slot in .plt. */ + GElf_Addr plt_slot_addr; +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 8715da6..332daa8 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -679,6 +679,14 @@ arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, + return PLT_OK; + } + ++struct ppc_unresolve_data { ++ struct ppc_unresolve_data *self; /* A canary. */ ++ GElf_Addr plt_entry_addr; ++ GElf_Addr plt_slot_addr; ++ GElf_Addr plt_slot_value; ++ bool is_irelative; ++}; ++ + enum plt_status + arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, +@@ -778,28 +786,23 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + && (plt_slot_value == plt_entry_addr || plt_slot_value == 0)) { + libsym->arch.type = PPC_PLT_UNRESOLVED; + libsym->arch.resolved_value = plt_entry_addr; +- + } else { +- /* Unresolve the .plt slot. If the binary was +- * prelinked, this makes the code invalid, because in +- * case of prelinked binary, the dynamic linker +- * doesn't update .plt[0] and .plt[1] with addresses +- * of the resover. But we don't care, we will never +- * need to enter the resolver. That just means that +- * we have to un-un-resolve this back before we +- * detach. */ +- +- if (unresolve_plt_slot(proc, plt_slot_addr, plt_entry_addr) < 0) { +- library_symbol_destroy(libsym); ++ /* Mark the symbol for later unresolving. We may not ++ * do this right away, as this is called by ltrace ++ * core for all symbols, and only later filtered. We ++ * only unresolve the symbol before the breakpoint is ++ * enabled. */ ++ ++ libsym->arch.type = PPC_PLT_NEED_UNRESOLVE; ++ libsym->arch.data = malloc(sizeof *libsym->arch.data); ++ if (libsym->arch.data == NULL) + goto fail2; +- } + +- if (! is_irelative) { +- mark_as_resolved(libsym, plt_slot_value); +- } else { +- libsym->arch.type = PPC_PLT_IRELATIVE; +- libsym->arch.resolved_value = plt_entry_addr; +- } ++ libsym->arch.data->self = libsym->arch.data; ++ libsym->arch.data->plt_entry_addr = plt_entry_addr; ++ libsym->arch.data->plt_slot_addr = plt_slot_addr; ++ libsym->arch.data->plt_slot_value = plt_slot_value; ++ libsym->arch.data->is_irelative = is_irelative; + } + + *ret = libsym; +@@ -999,6 +1002,7 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc) + return; + + case PPC64_PLT_STUB: ++ case PPC_PLT_NEED_UNRESOLVE: + /* These should never hit here. */ + break; + } +@@ -1050,6 +1054,52 @@ ppc_plt_bp_retract(struct breakpoint *bp, struct process *proc) + } + } + ++static void ++ppc_plt_bp_install(struct breakpoint *bp, struct process *proc) ++{ ++ /* This should not be an artificial breakpoint. */ ++ struct library_symbol *libsym = bp->libsym; ++ if (libsym == NULL) ++ libsym = bp->arch.irel_libsym; ++ assert(libsym != NULL); ++ ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ /* Unresolve the .plt slot. If the binary was ++ * prelinked, this makes the code invalid, because in ++ * case of prelinked binary, the dynamic linker ++ * doesn't update .plt[0] and .plt[1] with addresses ++ * of the resover. But we don't care, we will never ++ * need to enter the resolver. That just means that ++ * we have to un-un-resolve this back before we ++ * detach. */ ++ ++ struct ppc_unresolve_data *data = libsym->arch.data; ++ libsym->arch.data = NULL; ++ assert(data->self == data); ++ ++ GElf_Addr plt_slot_addr = data->plt_slot_addr; ++ GElf_Addr plt_slot_value = data->plt_slot_value; ++ GElf_Addr plt_entry_addr = data->plt_entry_addr; ++ ++ if (unresolve_plt_slot(proc, plt_slot_addr, ++ plt_entry_addr) == 0) { ++ if (! data->is_irelative) { ++ mark_as_resolved(libsym, plt_slot_value); ++ } else { ++ libsym->arch.type = PPC_PLT_IRELATIVE; ++ libsym->arch.resolved_value = plt_entry_addr; ++ } ++ } else { ++ fprintf(stderr, "Couldn't unresolve %s@%p. Not tracing" ++ " this symbol.\n", ++ breakpoint_name(bp), bp->addr); ++ proc_remove_breakpoint(proc, bp); ++ } ++ ++ free(data); ++ } ++} ++ + int + arch_library_init(struct library *lib) + { +@@ -1080,6 +1130,11 @@ arch_library_symbol_init(struct library_symbol *libsym) + void + arch_library_symbol_destroy(struct library_symbol *libsym) + { ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ assert(libsym->arch.data->self == libsym->arch.data); ++ free(libsym->arch.data); ++ libsym->arch.data = NULL; ++ } + } + + int +@@ -1115,6 +1170,7 @@ arch_breakpoint_init(struct process *proc, struct breakpoint *bp) + static struct bp_callbacks cbs = { + .on_continue = ppc_plt_bp_continue, + .on_retract = ppc_plt_bp_retract, ++ .on_install = ppc_plt_bp_install, + }; + breakpoint_set_callbacks(bp, &cbs); + +-- +1.7.6.5 + diff --git a/SOURCES/ltrace-0.7.91-s390-fetch-syscall.patch b/SOURCES/ltrace-0.7.91-s390-fetch-syscall.patch new file mode 100644 index 0000000..da50b48 --- /dev/null +++ b/SOURCES/ltrace-0.7.91-s390-fetch-syscall.patch @@ -0,0 +1,69 @@ +@@ -, +, @@ + exe->mount("source", "target", "filesystemtype", 0, nil <unfinished ...> + mount@SYS("", "target", "filesystemtype", 0, nil) = -2 + <... mount resumed> = -1 +--- + sysdeps/linux-gnu/s390/fetch.c | 17 ++++++++++++----- + 1 files changed, 12 insertions(+), 5 deletions(-) +--- a/sysdeps/linux-gnu/s390/fetch.c ++++ a/sysdeps/linux-gnu/s390/fetch.c +@@ -23,6 +23,7 @@ + #include <sys/ucontext.h> + #include <assert.h> + #include <errno.h> ++#include <stdbool.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> +@@ -61,7 +62,8 @@ s390x(struct fetch_context *ctx) + } + + static int +-fetch_register_banks(struct process *proc, struct fetch_context *ctx) ++fetch_register_banks(struct process *proc, struct fetch_context *ctx, ++ bool syscall_enter) + { + ptrace_area parea; + parea.len = sizeof(ctx->regs); +@@ -72,15 +74,20 @@ fetch_register_banks(struct process *proc, struct fetch_context *ctx) + strerror(errno)); + return -1; + } ++ ++ if (syscall_enter) ++ ctx->regs.gprs[2] = ctx->regs.orig_gpr2; ++ + return 0; + } + + static int +-fetch_context_init(struct process *proc, struct fetch_context *context) ++fetch_context_init(struct process *proc, struct fetch_context *context, ++ bool syscall_enter) + { + context->greg = 2; + context->freg = 0; +- return fetch_register_banks(proc, context); ++ return fetch_register_banks(proc, context, syscall_enter); + } + + struct fetch_context * +@@ -89,7 +96,7 @@ arch_fetch_arg_init(enum tof type, struct process *proc, + { + struct fetch_context *context = malloc(sizeof(*context)); + if (context == NULL +- || fetch_context_init(proc, context) < 0) { ++ || fetch_context_init(proc, context, type == LT_TOF_SYSCALL) < 0) { + fprintf(stderr, "arch_fetch_arg_init: %s\n", + strerror(errno)); + free(context); +@@ -277,7 +284,7 @@ arch_fetch_retval(struct fetch_context *ctx, enum tof type, + return 0; + } + +- if (fetch_context_init(proc, ctx) < 0) ++ if (fetch_context_init(proc, ctx, false) < 0) + return -1; + return arch_fetch_arg_next(ctx, type, proc, info, valuep); + } +-- diff --git a/SOURCES/ltrace-0.7.91-s390-irelative.patch b/SOURCES/ltrace-0.7.91-s390-irelative.patch new file mode 100644 index 0000000..29222c9 --- /dev/null +++ b/SOURCES/ltrace-0.7.91-s390-irelative.patch @@ -0,0 +1,67 @@ +@@ -, +, @@ +--- + sysdeps/linux-gnu/s390/arch.h | 2 ++ + sysdeps/linux-gnu/s390/plt.c | 22 ++++++++++++++++++++++ + 2 files changed, 24 insertions(+) +--- a/sysdeps/linux-gnu/s390/arch.h ++++ a/sysdeps/linux-gnu/s390/arch.h +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation + * + * This program is free software; you can redistribute it and/or +@@ -25,6 +26,7 @@ + #define ARCH_HAVE_FETCH_ARG + #define ARCH_HAVE_SIZEOF + #define ARCH_HAVE_ALIGNOF ++#define ARCH_HAVE_ADD_PLT_ENTRY + + #define LT_ELFCLASS ELFCLASS32 + #define LT_ELF_MACHINE EM_S390 +--- a/sysdeps/linux-gnu/s390/plt.c ++++ a/sysdeps/linux-gnu/s390/plt.c +@@ -1,5 +1,6 @@ + /* + * This file is part of ltrace. ++ * Copyright (C) 2013 Petr Machata, Red Hat Inc. + * Copyright (C) 2004,2008,2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -19,9 +20,12 @@ + */ + + #include <gelf.h> ++#include <stdbool.h> ++ + #include "proc.h" + #include "common.h" + #include "library.h" ++#include "trace.h" + + GElf_Addr + arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { +@@ -33,3 +37,21 @@ sym2addr(struct process *proc, struct library_symbol *sym) + { + return sym->enter_addr; + } ++ ++enum plt_status ++arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, ++ const char *a_name, GElf_Rela *rela, size_t ndx, ++ struct library_symbol **ret) ++{ ++#ifdef R_390_IRELATIVE ++ bool irelative = GELF_R_TYPE(rela->r_info) == R_390_IRELATIVE; ++#else ++ bool irelative = false; ++#endif ++ ++ if (irelative) ++ return linux_elf_add_plt_entry_irelative(proc, lte, rela, ++ ndx, ret); ++ ++ return PLT_DEFAULT; ++} +-- diff --git a/SOURCES/ltrace-0.7.91-x86_64-irelative.patch b/SOURCES/ltrace-0.7.91-x86_64-irelative.patch new file mode 100644 index 0000000..949b5fb --- /dev/null +++ b/SOURCES/ltrace-0.7.91-x86_64-irelative.patch @@ -0,0 +1,156 @@ +@@ -, +, @@ + relocation +- In general they are. But IRELATIVE relocations are sorted to come + last, and PLT entries are not sorted accordingly. +--- + sysdeps/linux-gnu/x86/arch.h | 11 +++++ + sysdeps/linux-gnu/x86/plt.c | 101 +++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 111 insertions(+), 1 deletions(-) +--- a/sysdeps/linux-gnu/x86/arch.h ++++ a/sysdeps/linux-gnu/x86/arch.h +@@ -19,6 +19,10 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ ++#ifndef LTRACE_X86_ARCH_H ++#define LTRACE_X86_ARCH_H ++ ++#include "vect.h" + + #define BREAKPOINT_VALUE {0xcc} + #define BREAKPOINT_LENGTH 1 +@@ -30,9 +34,16 @@ + + #define ARCH_HAVE_ADD_PLT_ENTRY + ++#define ARCH_HAVE_LTELF_DATA ++struct arch_ltelf_data { ++ struct vect plt_map; ++}; ++ + #ifdef __x86_64__ + #define LT_ELFCLASS ELFCLASS64 + #define LT_ELF_MACHINE EM_X86_64 + #endif + #define LT_ELFCLASS2 ELFCLASS32 + #define LT_ELF_MACHINE2 EM_386 ++ ++#endif /* LTRACE_X86_ARCH_H */ +--- a/sysdeps/linux-gnu/x86/plt.c ++++ a/sysdeps/linux-gnu/x86/plt.c +@@ -27,10 +27,19 @@ + #include "library.h" + #include "trace.h" + ++static GElf_Addr ++x86_plt_offset(uint32_t i) ++{ ++ /* Skip the first PLT entry, which contains a stub to call the ++ * resolver. */ ++ return (i + 1) * 16; ++} ++ + GElf_Addr + arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela *rela) + { +- return lte->plt_addr + (ndx + 1) * 16; ++ uint32_t i = *VECT_ELEMENT(<e->arch.plt_map, uint32_t, ndx); ++ return x86_plt_offset(i) + lte->plt_addr; + } + + void * +@@ -62,3 +71,93 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + + return PLT_DEFAULT; + } ++ ++int ++arch_elf_init(struct ltelf *lte, struct library *lib) ++{ ++ VECT_INIT(<e->arch.plt_map, unsigned int); ++ ++ /* IRELATIVE slots may make the whole situation a fair deal ++ * more complex. On x86{,_64}, the PLT slots are not ++ * presented in the order of the corresponding relocations, ++ * but in the order it which these symbols are in the symbol ++ * table. That's static symbol table, which may be stripped ++ * off, not dynsym--that doesn't contain IFUNC symbols at all. ++ * So we have to decode each PLT entry to figure out what ++ * entry it corresponds to. We need to interpret the PLT ++ * table to figure this out. ++ * ++ * On i386, the PLT entry format is as follows: ++ * ++ * 8048300: ff 25 0c a0 04 08 jmp *0x804a00c ++ * 8048306: 68 20 00 00 00 push $0x20 ++ * 804830b: e9 e0 ff ff ff jmp 80482f0 <_init+0x30> ++ * ++ * For PIE binaries it is the following: ++ * ++ * 410: ff a3 10 00 00 00 jmp *0x10(%ebx) ++ * 416: 68 00 00 00 00 push $0x0 ++ * 41b: e9 d0 ff ff ff jmp 3f0 <_init+0x30> ++ * ++ * On x86_64, it is: ++ * ++ * 400420: ff 25 f2 0b 20 00 jmpq *0x200bf2(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> ++ * 400426: 68 00 00 00 00 pushq $0x0 ++ * 40042b: e9 e0 ff ff ff jmpq 400410 <_init+0x18> ++ * ++ * On i386, the argument to push is an offset of relocation to ++ * use. The first PLT slot has an offset of 0x0, the second ++ * 0x8, etc. On x86_64, it's directly the index that we are ++ * looking for. ++ */ ++ ++ /* Here we scan the PLT table and initialize a map of ++ * relocation->slot number in lte->arch.plt_map. */ ++ ++ size_t i; ++ for (i = 0; i < vect_size(<e->plt_relocs); ++i) { ++ ++ GElf_Addr offset = x86_plt_offset(i); ++ uint32_t reloc_arg = 0; ++ ++ uint8_t byte; ++ if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 ++ || byte != 0xff ++ || elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 ++ || (byte != 0xa3 && byte != 0x25)) ++ goto next; ++ ++ /* Skip immediate argument in the instruction. */ ++ offset += 4; ++ ++ if (elf_read_next_u8(lte->plt_data, &offset, &byte) < 0 ++ || byte != 0x68 ++ || elf_read_next_u32(lte->plt_data, ++ &offset, &reloc_arg) < 0) { ++ reloc_arg = 0; ++ goto next; ++ } ++ ++ if (lte->ehdr.e_machine == EM_386) { ++ if (reloc_arg % 8 != 0) { ++ reloc_arg = 0; ++ goto next; ++ } ++ reloc_arg /= 8; ++ } ++ ++ next: ++ if (VECT_PUSHBACK(<e->arch.plt_map, &reloc_arg) < 0) { ++ arch_elf_destroy(lte); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++void ++arch_elf_destroy(struct ltelf *lte) ++{ ++ VECT_DESTROY(<e->arch.plt_map, uint32_t, NULL, NULL); ++} +-- diff --git a/SPECS/ltrace.spec b/SPECS/ltrace.spec index 78b4d2c..1a04bf8 100644 --- a/SPECS/ltrace.spec +++ b/SPECS/ltrace.spec @@ -1,7 +1,7 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace Version: 0.7.91 -Release: 1%{?dist} +Release: 7%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers @@ -17,6 +17,40 @@ Source: ltrace-%{version}.tar.bz2 # Merge of several upstream commits that fixes compilation on ARM. Patch0: ltrace-0.7.91-arm.patch +# Upstream patch that fixes accounting of exec, __libc_start_main and +# others in -c output. +Patch1: ltrace-0.7.91-account_execl.patch + +# Upstream patch that fixes interpretation of PLT on x86_64 when +# IRELATIVE slots are present. +# https://bugzilla.redhat.com/show_bug.cgi?id=844998 +Patch2: ltrace-0.7.91-x86_64-irelative.patch + +# Upstream patch that fixes fetching of system call arguments on s390. +# https://bugzilla.redhat.com/show_bug.cgi?id=844998 +Patch3: ltrace-0.7.91-s390-fetch-syscall.patch + +# Upstream patch that enables tracing of IRELATIVE PLT slots on s390. +# https://bugzilla.redhat.com/show_bug.cgi?id=844998 +Patch4: ltrace-0.7.91-s390-irelative.patch + +# Fix for a regression in tracing across fork. Upstream patch. +# https://bugzilla.redhat.com/show_bug.cgi?id=1052255 +Patch5: ltrace-0.7.91-ppc64-fork.patch + +# Fix crashing a prelinked PPC64 binary which makes PLT calls through +# slots that ltrace doesn't trace. +# https://bugzilla.redhat.com/show_bug.cgi?id=1051221 +Patch6: ltrace-0.7.91-breakpoint-on_install.patch +Patch7: ltrace-0.7.91-ppc64-unprelink.patch + +# Man page nits. Backport of an upstream patch. +# https://bugzilla.redhat.com/show_bug.cgi?id=866394 +Patch8: ltrace-0.7.91-man.patch + +# https://bugzilla.redhat.com/show_bug.cgi?id=1053765 +Patch9: ltrace-0.7.91-cant_open.patch + %description Ltrace is a debugging program which runs a specified command until the command exits. While the command is executing, ltrace intercepts and @@ -30,6 +64,15 @@ execution of processes. %prep %setup -q -n %{name}-%{version} %patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 %build %configure @@ -54,6 +97,38 @@ echo ====================TESTING END===================== %{_datadir}/ltrace %changelog +* Fri Jan 24 2014 Daniel Mach <dmach@redhat.com> - 0.7.91-7 +- Mass rebuild 2014-01-24 + +* Tue Jan 14 2014 Petr Machata <pmachata@redhat.com> - 0.7.91-6 +- Fix a problem when attempting to trace a command that hasn't been + found (ltrace-0.7.91-cant_open.patch) + +* Tue Jan 14 2014 Petr Machata <pmachata@redhat.com> - 0.7.91-5 +- Fix crashing a prelinked PPC64 binary which makes PLT calls through + slots that ltrace doesn't trace. + (ltrace-0.7.91-breakpoint-on_install.patch, + ltrace-0.7.91-ppc64-unprelink.patch) +- Fix a problem in tracing across fork on PPC64 + (ltrace-0.7.91-ppc64-fork.patch) +- Fix a couple nits in ltrace.1 + (ltrace-0.7.91-man.patch) + +* Fri Dec 27 2013 Daniel Mach <dmach@redhat.com> - 0.7.91-4 +- Mass rebuild 2013-12-27 + +* Wed Dec 4 2013 Petr Machata <pmachata@redhat.com> - 0.7.91-3 +- Fix interpretation of x86_64 PLT with IRELATIVE slots. + (ltrace-0.7.91-x86_64-irelative.patch) +- Fix fetching of system call arguments on s390. + (ltrace-0.7.91-s390-fetch-syscall.patch) +- Enable tracing of IRELATIVE PLT slots on s390. + (ltrace-0.7.91-s390-irelative.patch) + +* Thu Nov 21 2013 Petr Machata <pmachata@redhat.com> - 0.7.91-2 +- Fix a problem in including in summary (-c) function calls that don't + finish before exec or exit (ltrace-0.7.91-account_execl.patch) + * Tue Nov 5 2013 Petr Machata <pmachata@redhat.com> - 0.7.91-1 - Rebase to a pre-release 0.8 - Drop BR on autoconf and friends