X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=output.c;h=8d378ea42c5ab250449b757ef80c3b9f0f76305c;hb=deb4d2cecbe758a88cf7ef2b0dfedb5969531d78;hp=1d01d315a872456c612b483d207179515b10d8a6;hpb=689b9dbb8d7f88ab91e7741932ed000b6e49be9a;p=platform%2Fupstream%2Fltrace.git diff --git a/output.c b/output.c index 1d01d31..8d378ea 100644 --- a/output.c +++ b/output.c @@ -1,6 +1,6 @@ /* * This file is part of ltrace. - * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc. + * Copyright (C) 2011,2012,2013,2014 Petr Machata, Red Hat Inc. * Copyright (C) 2010 Joe Damato * Copyright (C) 1997,1998,1999,2001,2002,2003,2004,2007,2008,2009 Juan Cespedes * Copyright (C) 2006 Paul Gilliam, IBM Corporation @@ -22,10 +22,6 @@ * 02110-1301 USA */ -/* glibc before 2.10, eglibc and uClibc all need _GNU_SOURCE defined - * for open_memstream to become visible. */ -#define _GNU_SOURCE - #include "config.h" #include @@ -37,35 +33,42 @@ #include #include #include +#include -#include "common.h" -#include "proc.h" +#include "output.h" +#include "demangle.h" +#include "fetch.h" +#include "lens_default.h" #include "library.h" +#include "memstream.h" +#include "options.h" +#include "param.h" +#include "proc.h" +#include "prototype.h" +#include "summary.h" #include "type.h" #include "value.h" #include "value_dict.h" -#include "param.h" -#include "fetch.h" -#include "lens_default.h" +#include "filter.h" +#include "debug.h" -/* TODO FIXME XXX: include in common.h: */ -extern struct timeval current_time_spent; - -Dict *dict_opt_c = NULL; +#if defined(HAVE_LIBDW) +#include "dwarf_prototypes.h" +#endif -static Process *current_proc = 0; +static struct process *current_proc = NULL; static size_t current_depth = 0; static int current_column = 0; static void -output_indent(struct Process *proc) +output_indent(struct process *proc) { int d = options.indent * (proc->callstack_depth - 1); current_column += fprintf(options.output, "%*s", d, ""); } static void -begin_of_line(Process *proc, int is_func, int indent) +begin_of_line(struct process *proc, int is_func, int indent) { current_column = 0; if (!proc) { @@ -78,11 +81,10 @@ begin_of_line(Process *proc, int is_func, int indent) } if (opt_r) { struct timeval tv; - struct timezone tz; static struct timeval old_tv = { 0, 0 }; struct timeval diff; - gettimeofday(&tv, &tz); + gettimeofday(&tv, NULL); if (old_tv.tv_sec == 0 && old_tv.tv_usec == 0) { old_tv.tv_sec = tv.tv_sec; @@ -98,16 +100,16 @@ begin_of_line(Process *proc, int is_func, int indent) old_tv.tv_sec = tv.tv_sec; old_tv.tv_usec = tv.tv_usec; current_column += fprintf(options.output, "%3lu.%06d ", - diff.tv_sec, (int)diff.tv_usec); + (unsigned long)diff.tv_sec, + (int)diff.tv_usec); } if (opt_t) { struct timeval tv; - struct timezone tz; - - gettimeofday(&tv, &tz); + gettimeofday(&tv, NULL); if (opt_t > 2) { current_column += fprintf(options.output, "%lu.%06d ", - tv.tv_sec, (int)tv.tv_usec); + (unsigned long)tv.tv_sec, + (int)tv.tv_usec); } else if (opt_t > 1) { struct tm *tmp = localtime(&tv.tv_sec); current_column += @@ -122,12 +124,15 @@ begin_of_line(Process *proc, int is_func, int indent) } } if (opt_i) { - if (is_func) + if (is_func) { + struct callstack_element *stel + = &proc->callstack[proc->callstack_depth - 1]; current_column += fprintf(options.output, "[%p] ", - proc->return_addr); - else + stel->return_addr); + } else { current_column += fprintf(options.output, "[%p] ", proc->instruction_pointer); + } } if (options.indent > 0 && indent) { output_indent(proc); @@ -137,78 +142,138 @@ begin_of_line(Process *proc, int is_func, int indent) static struct arg_type_info * get_unknown_type(void) { - static struct arg_type_info *info = NULL; - if (info == NULL) { - info = malloc(sizeof(*info)); - if (info == NULL) { - report_global_error("malloc: %s", strerror(errno)); - abort(); - } - *info = *type_get_simple(ARGTYPE_LONG); - info->lens = &guess_lens; - } - return info; + static struct arg_type_info *ret = NULL; + if (ret != NULL) + return ret; + + static struct arg_type_info info; + info = *type_get_simple(ARGTYPE_LONG); + info.lens = &guess_lens; + ret = &info; + return ret; } /* The default prototype is: long X(long, long, long, long). */ -static Function * +static struct prototype * build_default_prototype(void) { - Function *ret = malloc(sizeof(*ret)); - size_t i = 0; - if (ret == NULL) - goto err; - memset(ret, 0, sizeof(*ret)); + static struct prototype *ret = NULL; + if (ret != NULL) + return ret; - struct arg_type_info *unknown_type = get_unknown_type(); + static struct prototype proto; + prototype_init(&proto); - ret->return_info = unknown_type; - ret->own_return_info = 0; + struct arg_type_info *unknown_type = get_unknown_type(); + assert(unknown_type != NULL); + proto.return_info = unknown_type; + proto.own_return_info = 0; - ret->num_params = 4; - ret->params = malloc(sizeof(*ret->params) * ret->num_params); - if (ret->params == NULL) - goto err; + struct param unknown_param; + param_init_type(&unknown_param, unknown_type, 0); - for (i = 0; i < ret->num_params; ++i) - param_init_type(&ret->params[i], unknown_type, 0); + size_t i; + for (i = 0; i < 4; ++i) + if (prototype_push_param(&proto, &unknown_param) < 0) { + report_global_error("build_default_prototype: %s", + strerror(errno)); + prototype_destroy(&proto); + return NULL; + } + ret = &proto; return ret; +} -err: - report_global_error("malloc: %s", strerror(errno)); - if (ret->params != NULL) { - while (i-- > 0) - param_destroy(&ret->params[i]); - free(ret->params); +static bool +snip_period(char *buf) +{ + char *period = strrchr(buf, '.'); + if (period != NULL && strcmp(period, ".so") != 0) { + *period = 0; + return true; + } else { + return false; } +} - free(ret); +static struct prototype * +library_get_prototype(struct library *lib, const char *name) +{ + if (lib->protolib == NULL) { + size_t sz = strlen(lib->soname); + char buf[sz + 1]; + memcpy(buf, lib->soname, sz + 1); + + do { + if (protolib_cache_maybe_load(&g_protocache, buf, 0, + true, &lib->protolib) < 0) + return NULL; + } while (lib->protolib == NULL + && lib->type == LT_LIBTYPE_DSO + && snip_period(buf)); + +#if defined(HAVE_LIBDW) + // DWARF data fills in the gaps in the .conf files, so I don't + // check for lib->protolib==NULL here + if (lib->dwfl != NULL && + (filter_matches_library(options.plt_filter, lib ) || + filter_matches_library(options.static_filter, lib ) || + filter_matches_library(options.export_filter, lib ))) + import_DWARF_prototypes(lib); + else + debug(DEBUG_FUNCTION, + "Filter didn't match prototype '%s' in lib '%s'. " + "Not importing", + name, lib->soname); +#endif - return NULL; + if (lib->protolib == NULL) + lib->protolib = protolib_cache_default(&g_protocache, + buf, 0); + } + if (lib->protolib == NULL) + return NULL; + + return protolib_lookup_prototype(lib->protolib, name, + lib->type != LT_LIBTYPE_SYSCALL); } -static Function * -name2func(char const *name) { - Function *tmp; - const char *str1, *str2; +struct find_proto_data { + const char *name; + struct prototype *ret; +}; - for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) { - str1 = tmp->name; - str2 = name; - if (!strcmp(str1, str2)) - return tmp; - } +static enum callback_status +find_proto_cb(struct process *proc, struct library *lib, void *d) +{ + struct find_proto_data *data = d; + data->ret = library_get_prototype(lib, data->name); + return CBS_STOP_IF(data->ret != NULL); +} - static Function *def = NULL; - if (def == NULL) - def = build_default_prototype(); +static struct prototype * +lookup_symbol_prototype(struct process *proc, struct library_symbol *libsym) +{ + if (libsym->proto != NULL) + return libsym->proto; + + struct library *lib = libsym->lib; + if (lib != NULL) { + struct find_proto_data data = { libsym->name }; + data.ret = library_get_prototype(lib, libsym->name); + if (data.ret == NULL + && libsym->plt_type == LS_TOPLT_EXEC) + proc_each_library(proc, NULL, find_proto_cb, &data); + if (data.ret != NULL) + return data.ret; + } - return def; + return build_default_prototype(); } void -output_line(struct Process *proc, const char *fmt, ...) +output_line(struct process *proc, const char *fmt, ...) { if (options.summary) return; @@ -248,7 +313,8 @@ output_error(FILE *stream) } static int -fetch_simple_param(enum tof type, Process *proc, struct fetch_context *context, +fetch_simple_param(enum tof type, struct process *proc, + struct fetch_context *context, struct value_dict *arguments, struct arg_type_info *info, int own, struct value *valuep) @@ -290,7 +356,8 @@ fetch_param_stop(struct value_dict *arguments, ssize_t *params_leftp) } static int -fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context, +fetch_param_pack(enum tof type, struct process *proc, + struct fetch_context *context, struct value_dict *arguments, struct param *param, ssize_t *params_leftp) { @@ -343,7 +410,8 @@ fetch_param_pack(enum tof type, Process *proc, struct fetch_context *context, } static int -fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, +fetch_one_param(enum tof type, struct process *proc, + struct fetch_context *context, struct value_dict *arguments, struct param *param, ssize_t *params_leftp) { @@ -371,15 +439,36 @@ fetch_one_param(enum tof type, Process *proc, struct fetch_context *context, abort(); } +struct fetch_one_param_data +{ + struct process *proc; + struct fetch_context *context; + struct value_dict *arguments; + ssize_t *params_leftp; + enum tof tof; +}; + +static enum callback_status +fetch_one_param_cb(struct prototype *proto, struct param *param, void *data) +{ + struct fetch_one_param_data *cb_data = data; + return CBS_STOP_IF(fetch_one_param(cb_data->tof, cb_data->proc, + cb_data->context, + cb_data->arguments, param, + cb_data->params_leftp) < 0); +} + static int -fetch_params(enum tof type, Process *proc, struct fetch_context *context, - struct value_dict *arguments, Function *func, ssize_t *params_leftp) +fetch_params(enum tof type, struct process *proc, + struct fetch_context *context, + struct value_dict *arguments, struct prototype *func, + ssize_t *params_leftp) { - size_t i; - for (i = 0; i < func->num_params; ++i) - if (fetch_one_param(type, proc, context, arguments, - &func->params[i], params_leftp) < 0) - return -1; + struct fetch_one_param_data cb_data + = { proc, context, arguments, params_leftp, type }; + if (prototype_each_param(func, NULL, + &fetch_one_param_cb, &cb_data) != NULL) + return -1; /* Implicit stop at the end of parameter list. */ fetch_param_stop(arguments, params_leftp); @@ -424,15 +513,11 @@ output_params(struct value_dict *arguments, size_t start, size_t end, } void -output_left(enum tof type, struct Process *proc, +output_left(enum tof type, struct process *proc, struct library_symbol *libsym) { - const char *function_name = libsym->name; - Function *func; + assert(! options.summary); - if (options.summary) { - return; - } if (current_proc) { fprintf(options.output, " \n"); current_column = 0; @@ -447,10 +532,10 @@ output_left(enum tof type, struct Process *proc, fprintf(options.output, "%s->", libsym->lib->soname)); - const char *name = function_name; + const char *name = libsym->name; #ifdef USE_DEMANGLE if (options.demangle) - name = my_demangle(function_name); + name = my_demangle(libsym->name); #endif if (account_output(¤t_column, fprintf(options.output, "%s", name)) < 0) @@ -467,17 +552,23 @@ output_left(enum tof type, struct Process *proc, account_output(¤t_column, fprintf(options.output, "(")); - func = name2func(function_name); + struct prototype *func = lookup_symbol_prototype(proc->leader, libsym); if (func == NULL) { + fail: account_output(¤t_column, fprintf(options.output, "???")); return; } struct fetch_context *context = fetch_arg_init(type, proc, func->return_info); + if (context == NULL) + goto fail; + struct value_dict *arguments = malloc(sizeof(*arguments)); - if (arguments == NULL) - return; + if (arguments == NULL) { + fetch_arg_done(context); + goto fail; + } val_dict_init(arguments); ssize_t params_left = -1; @@ -498,61 +589,99 @@ output_left(enum tof type, struct Process *proc, stel->out.need_delim = need_delim; } -void -output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) +#if defined(HAVE_LIBDW) +/* Prints information about one frame of a thread. Called by + dwfl_getthread_frames in output_right. Returns 1 when done (max + number of frames reached). Returns -1 on error. Returns 0 on + success (if there are more frames in the thread, call us again). */ +static int +frame_callback (Dwfl_Frame *state, void *arg) { - const char *function_name = libsym->name; - Function *func = name2func(function_name); - if (func == NULL) - return; + Dwarf_Addr pc; + bool isactivation; - if (options.summary) { - struct opt_c_struct *st; - if (!dict_opt_c) { - dict_opt_c = - dict_init(dict_key2hash_string, - dict_key_cmp_string); - } - st = dict_find_entry(dict_opt_c, function_name); - if (!st) { - char *na; - st = malloc(sizeof(struct opt_c_struct)); - na = strdup(function_name); - if (!st || !na) { - perror("malloc()"); - exit(1); + int *frames = (int *) arg; + + if (!dwfl_frame_pc(state, &pc, &isactivation)) + return -1; + + if (!isactivation) + pc--; + + Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state)); + Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc); + const char *modname = NULL; + const char *symname = NULL; + GElf_Off off = 0; + if (mod != NULL) { + GElf_Sym sym; + modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, + NULL, NULL, NULL); + symname = dwfl_module_addrinfo(mod, pc, &off, &sym, + NULL, NULL, NULL); + } + + /* This mimics the output produced by libunwind below. */ + fprintf(options.output, " > %s(%s+0x%" PRIx64 ") [%" PRIx64 "]\n", + modname, symname, off, pc); + + /* See if we can extract the source line too and print it on + the next line if we can find it. */ + if (mod != NULL) { + Dwfl_Line *l = dwfl_module_getsrc(mod, pc); + if (l != NULL) { + int line, col; + line = col = -1; + const char *src = dwfl_lineinfo(l, NULL, &line, &col, + NULL, NULL); + if (src != NULL) { + fprintf(options.output, "\t%s", src); + if (line > 0) { + fprintf(options.output, ":%d", line); + if (col > 0) + fprintf(options.output, + ":%d", col); + } + fprintf(options.output, "\n"); } - st->count = 0; - st->tv.tv_sec = st->tv.tv_usec = 0; - dict_enter(dict_opt_c, na, st); - } - 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; + } + + /* Max number of frames to print reached? */ + if ((*frames)-- == 0) + return 1; -// fprintf(options.output, "%s <%lu.%06d>\n", function_name, -// current_time_spent.tv_sec, (int)current_time_spent.tv_usec); + return 0; +} +#endif /* defined(HAVE_LIBDW) */ + +void +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; - } - 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, " \n"); - current_proc = 0; + current_proc = NULL; } if (current_proc != proc) { begin_of_line(proc, type == LT_TOF_FUNCTIONR, 1); #ifdef USE_DEMANGLE current_column += fprintf(options.output, "<... %s resumed> ", - options.demangle ? my_demangle(function_name) : function_name); + options.demangle ? my_demangle(libsym->name) + : libsym->name); #else current_column += - fprintf(options.output, "<... %s resumed> ", function_name); + fprintf(options.output, "<... %s resumed> ", libsym->name); #endif } @@ -564,17 +693,17 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) /* Fetch & enter into dictionary the retval first, so that * other values can use it in expressions. */ struct value retval; - int own_retval = 0; + bool own_retval = false; if (context != NULL) { value_init(&retval, proc, NULL, func->return_info, 0); - own_retval = 1; + own_retval = true; if (fetch_retval(context, type, proc, func->return_info, &retval) < 0) value_set_type(&retval, NULL, 0); else if (stel->arguments != NULL - && val_dict_push_named(stel->arguments, &retval, - "retval", 0) == 0) - own_retval = 0; + && val_dict_push_named(stel->arguments, &retval, + "retval", 0) == 0) + own_retval = false; } if (stel->arguments != NULL) @@ -595,25 +724,70 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) value_destroy(&retval); if (opt_T) { + assert(spent != NULL); fprintf(options.output, " <%lu.%06d>", - 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) - if (options.bt_depth > 0) { + if (options.bt_depth > 0 + && proc->unwind_priv != NULL + && proc->unwind_as != NULL) { unw_cursor_t cursor; - unw_word_t ip, sp; + arch_addr_t ip, function_offset; + struct library *lib = NULL; int unwind_depth = options.bt_depth; char fn_name[100]; + const char *lib_name; + size_t distance; + /* Verify that we can safely cast arch_addr_t* to + * unw_word_t*. */ + (void)sizeof(char[1 - 2*(sizeof(unw_word_t) + != sizeof(arch_addr_t))]); unw_init_remote(&cursor, proc->unwind_as, proc->unwind_priv); while (unwind_depth) { - unw_get_reg(&cursor, UNW_REG_IP, &ip); - unw_get_reg(&cursor, UNW_REG_SP, &sp); - unw_get_proc_name(&cursor, fn_name, 100, NULL); - fprintf(options.output, "\t\t\t%s (ip = 0x%lx)\n", fn_name, (long) ip); + + int rc = unw_get_reg(&cursor, UNW_REG_IP, + (unw_word_t *) &ip); + if (rc < 0) { + fprintf(options.output, " > Error: %s\n", + unw_strerror(rc)); + goto cont; + } + + /* We are looking for the library with the base address + * closest to the current ip. */ + lib_name = "unmapped_area"; + distance = (size_t) -1; + lib = proc->libraries; + while (lib != NULL) { + /* N.B.: Assumes sizeof(size_t) == + * sizeof(arch_addr_t). + * Keyword: double cast. */ + if ((ip >= lib->base) && + ((size_t)(ip - lib->base) + < distance)) { + distance = ip - lib->base; + lib_name = lib->pathname; + } + lib = lib->next; + } + + rc = unw_get_proc_name(&cursor, fn_name, + sizeof(fn_name), + (unw_word_t *) &function_offset); + if (rc == 0 || rc == -UNW_ENOMEM) + fprintf(options.output, " > %s(%s+%p) [%p]\n", + lib_name, fn_name, function_offset, ip); + else + fprintf(options.output, " > %s(??\?) [%p]\n", + lib_name, ip); + + cont: if (unw_step(&cursor) <= 0) break; unwind_depth--; @@ -622,7 +796,25 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym) } #endif /* defined(HAVE_LIBUNWIND) */ - current_proc = 0; +#if defined(HAVE_LIBDW) + if (options.bt_depth > 0 && proc->leader->dwfl != NULL) { + int frames = options.bt_depth; + if (dwfl_getthread_frames(proc->leader->dwfl, proc->pid, + frame_callback, &frames) < 0) { + // Only print an error if we couldn't show anything. + // Otherwise just show there might be more... + if (frames == options.bt_depth) + fprintf(stderr, + "dwfl_getthread_frames tid %d: %s\n", + proc->pid, dwfl_errmsg(-1)); + else + fprintf(options.output, " > [...]\n"); + } + fprintf(options.output, "\n"); + } +#endif /* defined(HAVE_LIBDW) */ + + current_proc = NULL; current_column = 0; } @@ -640,18 +832,18 @@ delim_output(FILE *stream, int *need_delimp, o = writer(stream, data); } else { - char *buf; - size_t bufsz; - FILE *tmp = open_memstream(&buf, &bufsz); - o = writer(tmp, data); - fclose(tmp); - + struct memstream ms; + if (memstream_init(&ms) < 0) + return -1; + o = writer(ms.stream, data); + if (memstream_close(&ms) < 0) + o = -1; if (o > 0 && ((*need_delimp && account_output(&o, fprintf(stream, ", ")) < 0) - || fwrite(buf, 1, bufsz, stream) != bufsz)) + || fwrite(ms.buf, 1, ms.size, stream) != ms.size)) o = -1; - free(buf); + memstream_destroy(&ms); } if (o < 0)