added forgotten error checking to some ltrace calls
[platform/upstream/ltrace.git] / output.c
index edf4522..8d378ea 100644 (file)
--- a/output.c
+++ b/output.c
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -33,6 +33,7 @@
 #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;
 
@@ -210,6 +213,21 @@ library_get_prototype(struct library *lib, const char *name)
                         && 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);
@@ -498,9 +516,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;
@@ -535,7 +552,7 @@ output_left(enum tof type, struct process *proc,
 
        account_output(&current_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(&current_column, fprintf(options.output, "???"));
@@ -572,70 +589,88 @@ output_left(enum tof type, struct process *proc,
        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);
@@ -658,17 +693,17 @@ again:
        /* 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)
@@ -689,10 +724,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)
@@ -713,9 +750,14 @@ again:
                                        != 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.  */
@@ -735,9 +777,17 @@ again:
                                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--;
@@ -746,7 +796,25 @@ again:
        }
 #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;
 }