diff options
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | common.h | 9 | ||||
-rw-r--r-- | display_args.c | 43 | ||||
-rw-r--r-- | fetch.c | 130 | ||||
-rw-r--r-- | fetch.h | 64 | ||||
-rw-r--r-- | handle_event.c | 2 | ||||
-rw-r--r-- | output.c | 306 | ||||
-rw-r--r-- | output.h | 1 | ||||
-rw-r--r-- | proc.c | 24 | ||||
-rw-r--r-- | proc.h | 7 |
11 files changed, 452 insertions, 149 deletions
@@ -1,5 +1,18 @@ 2012-01-06 Petr Machata <pmachata@redhat.com> + * fetch.c, fetch.h: New module. + * common.h (enum tof): Move to fetch.h + (struct output_state): New struct + (struct callstack_element.fetch_context, .out): New fields + * handle_event.c (handle_clone): Clone fetch_context's + * output.c (build_default_prototype): New function + (fetch_simple_param, fetch_param_stop, fetch_one_param) + (fetch_params, output_one, output_params): New functions. + (output_left, output_right): Do the parameter output using the + above functions. + +2012-01-06 Petr Machata <pmachata@redhat.com> + * common.h (enum tof.LT_TOF_NONE, .LT_TOF_STRUCT): Drop. * output.c: Adjust. diff --git a/Makefile.am b/Makefile.am index 3c8cb25..90165d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,6 +34,7 @@ libltrace_la_SOURCES = \ value.c \ value_dict.c \ expr.c \ + fetch.c \ vect.c \ libltrace_la_LIBADD = \ @@ -76,6 +77,7 @@ noinst_HEADERS = \ value.h \ value_dict.h \ expr.h \ + fetch.h \ vect.h \ dist_man1_MANS = \ @@ -50,13 +50,6 @@ extern char * command; extern int exiting; /* =1 if we have to exit ASAP */ -enum tof { - LT_TOF_FUNCTION, /* A real library function */ - LT_TOF_FUNCTIONR, /* Return from a real library function */ - LT_TOF_SYSCALL, /* A syscall */ - LT_TOF_SYSCALLR, /* Return from a syscall */ -}; - typedef struct Function Function; struct Function { const char * name; @@ -140,8 +133,6 @@ extern void continue_after_signal(pid_t pid, int signum); extern void continue_after_syscall(Process *proc, int sysnum, int ret_p); extern void continue_after_breakpoint(struct Process *proc, struct breakpoint *sbp); extern void continue_after_vfork(Process * proc); -extern long gimme_arg(enum tof type, Process *proc, int arg_num, - struct arg_type_info *info); extern size_t umovebytes (Process *proc, void * addr, void * laddr, size_t count); extern int ffcheck(void * maddr); extern void * sym2addr(Process *, struct library_symbol *); diff --git a/display_args.c b/display_args.c index 8848b35..4a1747d 100644 --- a/display_args.c +++ b/display_args.c @@ -35,9 +35,6 @@ #include "type.h" #include "common.h" -static int format_argument_2(FILE *stream, struct value *value, - struct value_dict *arguments); - #define READER(NAME, TYPE) \ static int \ NAME(struct value *value, TYPE *ret, struct value_dict *arguments) \ @@ -235,7 +232,7 @@ format_struct(FILE *stream, struct value *value, struct value_dict *arguments) struct value element; if (value_init_element(&element, value, i) < 0) return -1; - int o = format_argument_2(stream, &element, arguments); + int o = format_argument(stream, &element, arguments); if (o < 0) return -1; written += o; @@ -251,7 +248,7 @@ format_pointer(FILE *stream, struct value *value, struct value_dict *arguments) struct value element; if (value_init_deref(&element, value) < 0) return -1; - return format_argument_2(stream, &element, arguments); + return format_argument(stream, &element, arguments); } /* @@ -300,7 +297,7 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments, return -1; if (value_is_zero(&element, arguments)) /* XXX emulate ZERO */ break; - int o = format_argument_2(stream, &element, arguments); + int o = format_argument(stream, &element, arguments); if (o < 0) return -1; written += o; @@ -313,9 +310,8 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments, return written; } -static int -format_argument_2(FILE *stream, struct value *value, - struct value_dict *arguments) +int +format_argument(FILE *stream, struct value *value, struct value_dict *arguments) { struct expr_node *length = NULL; switch (value->type->type) { @@ -384,7 +380,7 @@ format_argument_2(FILE *stream, struct value *value, value_clone(&tmp, value); value_set_type(&tmp, info, 0); - int ret = format_argument_2(stream, &tmp, arguments); + int ret = format_argument(stream, &tmp, arguments); value_destroy(&tmp); type_destroy(&info[0]); @@ -400,30 +396,3 @@ format_argument_2(FILE *stream, struct value *value, } abort(); } - -int -format_argument(FILE *stream, struct value *value, struct value_dict *arguments) -{ - /* Arrays decay into pointers for purposes of argument - * passing. Before the proper support for this lands, wrap - * top-level arrays in pointers here. */ - if (value->type->type == ARGTYPE_ARRAY) { - - struct arg_type_info info; - type_init_pointer(&info, value->type, 0); - - struct value tmp; - value_clone(&tmp, value); - value_set_type(&tmp, &info, 0); - - int ret = format_argument_2(stream, &tmp, arguments); - - value_destroy(&tmp); - type_destroy(&info); - - return ret; - - } else { - return format_argument_2(stream, value, arguments); - } -} @@ -0,0 +1,130 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include "fetch.h" +#include "value.h" +#include "arch.h" + +#ifdef ARCH_HAVE_FETCH_ARG +struct fetch_context *arch_fetch_arg_init(enum tof type, struct Process *proc, + struct arg_type_info *ret_info); + +struct fetch_context *arch_fetch_arg_clone(struct Process *proc, + struct fetch_context *context); + +int arch_fetch_arg_next(struct fetch_context *ctx, enum tof type, + struct Process *proc, struct arg_type_info *info, + struct value *valuep); + +int arch_fetch_retval(struct fetch_context *ctx, enum tof type, + struct Process *proc, struct arg_type_info *info, + struct value *valuep); + +void arch_fetch_arg_done(struct fetch_context *context); + +#else +/* Fall back to gimme_arg. */ + +long gimme_arg(enum tof type, struct Process *proc, int arg_num, + struct arg_type_info *info); + +struct fetch_context { + int argnum; +}; + +struct fetch_context * +arch_fetch_arg_init(enum tof type, struct Process *proc, + struct arg_type_info *ret_info) +{ + return calloc(sizeof(struct fetch_context), 1); +} + +struct fetch_context * +arch_fetch_arg_clone(struct Process *proc, struct fetch_context *context) +{ + struct fetch_context *ret = malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + return memcpy(ret, context, sizeof(*ret)); +} + +int +arch_fetch_arg_next(struct fetch_context *context, enum tof type, + struct Process *proc, + struct arg_type_info *info, struct value *valuep) +{ + long l = gimme_arg(type, proc, context->argnum++, info); + value_set_long(valuep, l); + return 0; +} + +int +arch_fetch_retval(struct fetch_context *context, enum tof type, + struct Process *proc, + struct arg_type_info *info, struct value *valuep) +{ + long l = gimme_arg(type, proc, -1, info); + value_set_long(valuep, l); + return 0; +} + +void +arch_fetch_arg_done(struct fetch_context *context) +{ + free(context); +} +#endif + +struct fetch_context * +fetch_arg_init(enum tof type, struct Process *proc, + struct arg_type_info *ret_info) +{ + return arch_fetch_arg_init(type, proc, ret_info); +} + +struct fetch_context * +fetch_arg_clone(struct Process *proc, struct fetch_context *context) +{ + return arch_fetch_arg_clone(proc, context); +} + +int +fetch_arg_next(struct fetch_context *context, enum tof type, + struct Process *proc, + struct arg_type_info *info, struct value *valuep) +{ + return arch_fetch_arg_next(context, type, proc, info, valuep); +} + +int +fetch_retval(struct fetch_context *context, enum tof type, + struct Process *proc, + struct arg_type_info *info, struct value *valuep) +{ + return arch_fetch_retval(context, type, proc, info, valuep); +} + +void +fetch_arg_done(struct fetch_context *context) +{ + return arch_fetch_arg_done(context); +} @@ -0,0 +1,64 @@ +/* + * This file is part of ltrace. + * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef FETCH_H +#define FETCH_H + +#include "forward.h" + +/* XXX isn't SYSCALL TOF just a different ABI? Maybe we needed to + * support variant ABIs all along. */ +enum tof { + LT_TOF_FUNCTION, /* A real library function */ + LT_TOF_FUNCTIONR, /* Return from a real library function */ + LT_TOF_SYSCALL, /* A syscall */ + LT_TOF_SYSCALLR, /* Return from a syscall */ +}; + +/* The contents of the structure is defined by the back end. */ +struct fetch_context; + +/* Initialize argument fetching. Returns NULL on failure. RET_INFO + * is the return type of the function. */ +struct fetch_context *fetch_arg_init(enum tof type, struct Process *proc, + struct arg_type_info *ret_info); + +/* Make a clone of context. */ +struct fetch_context *fetch_arg_clone(struct Process *proc, + struct fetch_context *context); + +/* Load next argument. The function returns 0 on success or a + * negative value on failure. The extracted value is stored in + * *VALUEP. */ +int fetch_arg_next(struct fetch_context *context, enum tof type, + struct Process *proc, + struct arg_type_info *info, struct value *valuep); + +/* Load return value. The function returns 0 on success or a negative + * value on failure. The extracted value is stored in *VALUEP. */ +int fetch_retval(struct fetch_context *context, enum tof type, + struct Process *proc, + struct arg_type_info *info, struct value *valuep); + +/* Destroy fetch context. CONTEXT shall be the same memory location + * that was passed to fetch_arg_next. */ +void fetch_arg_done(struct fetch_context *context); + +#endif /* FETCH_H */ diff --git a/handle_event.c b/handle_event.c index 7d1eb61..ffce2ff 100644 --- a/handle_event.c +++ b/handle_event.c @@ -37,9 +37,9 @@ #include "common.h" #include "value_dict.h" #include "breakpoint.h" -#include "common.h" #include "library.h" #include "proc.h" +#include "fetch.h" static void handle_signal(Event *event); static void handle_exit(Event *event); @@ -31,6 +31,7 @@ #include <time.h> #include <sys/time.h> #include <unistd.h> +#include <errno.h> #include "common.h" #include "proc.h" @@ -38,6 +39,7 @@ #include "type.h" #include "value.h" #include "value_dict.h" +#include "fetch.h" /* TODO FIXME XXX: include in common.h: */ extern struct timeval current_time_spent; @@ -125,27 +127,50 @@ begin_of_line(Process *proc, int is_func, int indent) } } +/* The default prototype is: long X(long, long, long, long). */ +static Function * +build_default_prototype(void) +{ + Function *ret = malloc(sizeof(*ret)); + size_t i = 0; + if (ret == NULL) + goto err; + memset(ret, 0, sizeof(*ret)); + + struct arg_type_info *unknown_type = type_get_simple(ARGTYPE_UNKNOWN); + + ret->return_info = unknown_type; + + ret->num_params = 4; + for (i = 0; i < (size_t)ret->num_params; ++i) + ret->arg_info[i] = unknown_type; + + return ret; + +err: + report_global_error("malloc: %s", strerror(errno)); + free(ret); + + return NULL; +} + static Function * name2func(char const *name) { Function *tmp; const char *str1, *str2; - tmp = list_of_functions; - while (tmp) { -#ifdef USE_DEMANGLE - str1 = options.demangle ? my_demangle(tmp->name) : tmp->name; - str2 = options.demangle ? my_demangle(name) : name; -#else + for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) { str1 = tmp->name; str2 = name; -#endif - if (!strcmp(str1, str2)) { - + if (!strcmp(str1, str2)) return tmp; - } - tmp = tmp->next; } - return NULL; + + static Function *def = NULL; + if (def == NULL) + def = build_default_prototype(); + + return def; } void @@ -182,15 +207,128 @@ tabto(int col) { } } +static int +account_output(int o) +{ + if (o < 0) + return -1; + current_column += o; + return 0; +} + +static int +output_error(void) +{ + return account_output(fprintf(options.output, "?")); +} + +static int +fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context, + struct value_dict *arguments, struct arg_type_info *info, + struct value *valuep) +{ + /* Arrays decay into pointers per C standard. We check for + * this here, because here we also capture arrays that come + * from parameter packs. */ + int own = 0; + if (info->type == ARGTYPE_ARRAY) { + struct arg_type_info *tmp = malloc(sizeof(*tmp)); + if (tmp != NULL) { + type_init_pointer(tmp, info, 0); + info = tmp; + own = 1; + } + } + + struct value value; + value_init(&value, proc, NULL, info, own); + if (fetch_arg_next(context, type, proc, info, &value) < 0) + return -1; + + if (val_dict_push_next(arguments, &value) < 0) { + value_destroy(&value); + return -1; + } + + if (valuep != NULL) + *valuep = value; + + return 0; +} + +static void +fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp) +{ + if (*params_leftp == -1) + *params_leftp = val_dict_count(arguments); +} + +static int +fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, + struct value_dict *arguments, struct arg_type_info *info, + ssize_t *params_leftp) +{ + return fetch_simple_param(type, proc, context, arguments, + info, NULL); +} + +static int +fetch_params(enum tof type, Process *proc, struct fetch_context *context, + struct value_dict *arguments, Function *func, ssize_t *params_leftp) +{ + size_t i; + for (i = 0; i < (size_t)func->num_params; ++i) { + if (i == (size_t)(func->num_params - func->params_right)) + fetch_param_stop(arguments, params_leftp); + if (fetch_one_param(type, proc, context, arguments, + func->arg_info[i], params_leftp) < 0) + return -1; + } + + /* Implicit stop at the end of parameter list. */ + fetch_param_stop(arguments, params_leftp); + return 0; +} + +static int +output_one(struct value *val, struct value_dict *arguments) +{ + int o = format_argument(options.output, val, arguments); + if (account_output(o) < 0) { + if (output_error() < 0) + return -1; + o = 1; + } + return o; +} + +static int +output_params(struct value_dict *arguments, size_t start, size_t end, + int *need_delimp) +{ + size_t i; + int need_delim = *need_delimp; + for (i = start; i < end; ++i) { + if (need_delim + && account_output(fprintf(options.output, ", ")) < 0) + return -1; + struct value *value = val_dict_get_num(arguments, i); + if (value == NULL) + return -1; + need_delim = output_one(value, arguments); + if (need_delim < 0) + return -1; + } + *need_delimp = need_delim; + return 0; +} + void output_left(enum tof type, struct Process *proc, struct library_symbol *libsym) { const char *function_name = libsym->name; Function *func; - static struct arg_type_info *arg_unknown = NULL; - if (arg_unknown == NULL) - arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); if (options.summary) { return; @@ -201,74 +339,46 @@ output_left(enum tof type, struct Process *proc, } current_proc = proc; current_depth = proc->callstack_depth; - begin_of_line(type, type == LT_TOF_FUNCTION, 1); + begin_of_line(proc, type == LT_TOF_FUNCTION, 1); if (!options.hide_caller && libsym->lib != NULL && libsym->plt_type != LS_TOPLT_NONE) current_column += fprintf(options.output, "%s->", libsym->lib->soname); + + const char *name = function_name; #ifdef USE_DEMANGLE - current_column += - fprintf(options.output, "%s(", - (options.demangle - ? my_demangle(function_name) : function_name)); -#else - current_column += fprintf(options.output, "%s(", function_name); + if (options.demangle) + name = my_demangle(function_name); #endif + if (account_output(fprintf(options.output, "%s(", name)) < 0) + return; func = name2func(function_name); + if (func == NULL) + return; + struct fetch_context *context = fetch_arg_init(type, proc, + func->return_info); struct value_dict *arguments = malloc(sizeof(*arguments)); if (arguments == NULL) return; val_dict_init(arguments); - int num, right; - if (!func) { - int i; - for (i = 0; i < 4; i++) { - long l = gimme_arg(type, proc, i, arg_unknown); - struct value val; - value_init(&val, proc, NULL, arg_unknown, 0); - value_set_long(&val, l); - val_dict_push_next(arguments, &val); - } - right = 0; - num = 4; - } else { - int i; - for (i = 0; i < func->num_params; i++) { - long l = gimme_arg(type, proc, i, func->arg_info[i]); - struct value val; - value_init(&val, proc, NULL, func->arg_info[i], 0); - value_set_long(&val, l); - val_dict_push_next(arguments, &val); - } - right = func->params_right; - num = func->num_params; - } - - int i; - for (i = 0; i < num - right - 1; i++) { - current_column += - format_argument(options.output, - val_dict_get_num(arguments, i), - arguments); - current_column += fprintf(options.output, ", "); - } - - if (num > right) { - current_column += - format_argument(options.output, - val_dict_get_num(arguments, i), - arguments); - if (right) { - current_column += fprintf(options.output, ", "); - } + ssize_t params_left = -1; + int need_delim = 0; + if (fetch_params(type, proc, context, arguments, func, ¶ms_left) < 0 + || output_params(arguments, 0, params_left, &need_delim) < 0) { + val_dict_destroy(arguments); + fetch_arg_done(context); + return; } struct callstack_element *stel = &proc->callstack[proc->callstack_depth - 1]; + stel->fetch_context = context; stel->arguments = arguments; + stel->out.params_left = params_left; + stel->out.need_delim = need_delim; } void @@ -276,9 +386,8 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) { const char *function_name = libsym->name; Function *func = name2func(function_name); - static struct arg_type_info *arg_unknown = NULL; - if (arg_unknown == NULL) - arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); + if (func == NULL) + return; if (options.summary) { struct opt_c_struct *st; @@ -333,44 +442,41 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) struct callstack_element *stel = &proc->callstack[proc->callstack_depth - 1]; + struct fetch_context *context = stel->fetch_context; + + /* Fetch & enter into dictionary the retval first, so that + * other values can use it in expressions. */ struct value retval; - struct arg_type_info *return_info = arg_unknown; - if (func != NULL) - return_info = func->return_info; - long l = gimme_arg(type, proc, -1, return_info); - value_init(&retval, proc, NULL, return_info, 0); - value_set_long(&retval, l); - val_dict_push_named(stel->arguments, &retval, "retval", 0); - - if (!func) { - current_column += fprintf(options.output, ") "); - tabto(options.align - 1); - fprintf(options.output, "= "); - } else { - int i; - for (i = func->num_params - func->params_right; - i < func->num_params - 1; i++) { - current_column += - format_argument(options.output, - val_dict_get_num - (stel->arguments, i), - stel->arguments); - current_column += fprintf(options.output, ", "); - } - if (func->params_right) { - current_column += - format_argument(options.output, - val_dict_get_num - (stel->arguments, i), - stel->arguments); + int own_retval = 0; + if (context != NULL) { + value_init(&retval, proc, NULL, func->return_info, 0); + own_retval = 1; + if (fetch_retval(context, type, proc, func->return_info, + &retval) == 0) { + if (stel->arguments != NULL + && val_dict_push_named(stel->arguments, &retval, + "retval", 0) == 0) + own_retval = 0; } - current_column += fprintf(options.output, ") "); - tabto(options.align - 1); - fprintf(options.output, "= "); } - format_argument(options.output, &retval, stel->arguments); + if (stel->arguments != NULL) + output_params(stel->arguments, stel->out.params_left, + val_dict_count(stel->arguments), + &stel->out.need_delim); + + current_column += fprintf(options.output, ") "); + tabto(options.align - 1); + fprintf(options.output, "= "); + + output_one(&retval, stel->arguments); + + if (own_retval) + value_destroy(&retval); + val_dict_destroy(stel->arguments); + free(stel->arguments); + fetch_arg_done(context); if (opt_T) { fprintf(options.output, " <%lu.%06d>", @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ +#include "fetch.h" #include "forward.h" @@ -291,11 +291,26 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) size_t i; for (i = 0; i < retp->callstack_depth; ++i) { + struct fetch_context *ctx = retp->callstack[i].fetch_context; + if (ctx != NULL) { + struct fetch_context *nctx = fetch_arg_clone(p, ctx); + if (nctx == NULL) { + int j; + release1: + for (j = 0; j < i; ++j) { + nctx = retp->callstack[i].fetch_context; + fetch_arg_done(nctx); + retp->callstack[i].fetch_context = NULL; + } + goto fail2; + } + retp->callstack[i].fetch_context = nctx; + } + struct value_dict *args = retp->callstack[i].arguments; if (args != NULL) { fail3: struct value_dict *nargs = malloc(sizeof(*nargs)); - fprintf(stderr, "{A:%p->%p}", args, nargs); if (nargs == NULL || val_dict_clone(nargs, args) < 0) { @@ -306,7 +321,12 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid) free(nargs); p->callstack[i].arguments = NULL; } - goto fail2; + + /* Pretend that this round went well, + * so that release1 frees I-th + * fetch_context. */ + ++i; + goto release1; } retp->callstack[i].arguments = nargs; } @@ -64,6 +64,11 @@ enum process_state { STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ }; +struct output_state { + size_t params_left; + int need_delim; +}; + struct callstack_element { union { int syscall; @@ -72,7 +77,9 @@ struct callstack_element { int is_syscall; void * return_addr; struct timeval time_spent; + struct fetch_context *fetch_context; struct value_dict *arguments; + struct output_state out; }; /* XXX We should get rid of this. */ |