/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012,2013 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
#include <unistd.h>
#include <errno.h>
#include <assert.h>
+#include <inttypes.h>
#include "output.h"
#include "demangle.h"
#include "param.h"
#include "proc.h"
#include "prototype.h"
+#include "summary.h"
#include "type.h"
#include "value.h"
#include "value_dict.h"
+#include "filter.h"
+#include "debug.h"
-/* TODO FIXME XXX: include in common.h: */
-extern struct timeval current_time_spent;
-
-struct dict *dict_opt_c = NULL;
+#if defined(HAVE_LIBDW)
+#include "dwarf_prototypes.h"
+#endif
-static struct process *current_proc = 0;
+static struct process *current_proc = NULL;
static size_t current_depth = 0;
static int current_column = 0;
&& 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
+
if (lib->protolib == NULL)
lib->protolib = protolib_cache_default(&g_protocache,
buf, 0);
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;
account_output(¤t_column, fprintf(options.output, "("));
- struct prototype *func = lookup_symbol_prototype(proc, libsym);
+ struct prototype *func = lookup_symbol_prototype(proc->leader, libsym);
if (func == NULL) {
fail:
account_output(¤t_column, fprintf(options.output, "???"));
stel->out.need_delim = need_delim;
}
-static void
-free_stringp_cb(const char **stringp, void *data)
+#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)
{
- free((char *)*stringp);
-}
+ Dwarf_Addr pc;
+ bool isactivation;
-void
-output_right(enum tof type, struct process *proc, struct library_symbol *libsym)
-{
- struct prototype *func = lookup_symbol_prototype(proc, libsym);
- if (func == NULL)
- return;
+ int *frames = (int *) arg;
-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);
- }
+ 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);
+ }
- 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;
+ /* 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 = 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)) {
+ /* Max number of frames to print reached? */
+ if ((*frames)-- == 0)
+ return 1;
+
+ 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 != 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);
/* 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)
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)
!= sizeof(arch_addr_t))]);
unw_init_remote(&cursor, proc->unwind_as, proc->unwind_priv);
while (unwind_depth) {
- unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *) &ip);
- unw_get_proc_name(&cursor, fn_name, sizeof(fn_name),
- (unw_word_t *) &function_offset);
+
+ 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 = lib->next;
}
- fprintf(options.output, " > %s(%s+0x%p) [%p]\n",
+ 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--;
}
#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;
}