Replace readdir_r with readdir
[platform/upstream/ltrace.git] / output.c
index 1d01d31..b63befe 100644 (file)
--- 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
  * 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 <stdio.h>
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
+#include <inttypes.h>
 
-#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,173 @@ 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;
        }
+}
+
+struct lookup_prototype_alias_context
+{
+       struct library *lib;
+       struct prototype *result; // output
+};
+static enum callback_status
+lookup_prototype_alias_cb(const char *name, void *data)
+{
+       struct lookup_prototype_alias_context *context =
+               (struct lookup_prototype_alias_context*)data;
 
-       free(ret);
+       struct library *lib = context->lib;
 
-       return NULL;
+       context->result =
+               protolib_lookup_prototype(lib->protolib, name,
+                                         lib->type != LT_LIBTYPE_SYSCALL);
+       if (context->result != NULL)
+               return CBS_STOP;
+
+       return CBS_CONT;
 }
 
-static Function *
-name2func(char const *name) {
-       Function *tmp;
-       const char *str1, *str2;
+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_module != 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
 
-       for (tmp = list_of_functions; tmp != NULL; tmp = tmp->next) {
-               str1 = tmp->name;
-               str2 = name;
-               if (!strcmp(str1, str2))
-                       return tmp;
+               if (lib->protolib == NULL)
+                       lib->protolib = protolib_cache_default(&g_protocache,
+                                                              buf, 0);
        }
+       if (lib->protolib == NULL)
+               return NULL;
+
+       struct prototype *result =
+               protolib_lookup_prototype(lib->protolib, name,
+                                         lib->type != LT_LIBTYPE_SYSCALL);
+       if (result != NULL)
+               return result;
+
+       // prototype not found. Is it aliased?
+       struct lookup_prototype_alias_context context = {.lib = lib,
+                                                        .result = NULL};
+       library_exported_names_each_alias(&lib->exported_names, name,
+                                         NULL, lookup_prototype_alias_cb,
+                                         &context);
+
+       // if found, the prototype is stored here, otherwise it's NULL
+       return context.result;
+}
+
+struct find_proto_data {
+       const char *name;
+       struct prototype *ret;
+};
+
+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 +348,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 +391,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 +445,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 +474,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 +548,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, " <unfinished ...>\n");
                current_column = 0;
@@ -447,10 +567,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(&current_column,
                           fprintf(options.output, "%s", name)) < 0)
@@ -467,17 +587,23 @@ output_left(enum tof type, struct Process *proc,
 
        account_output(&current_column, fprintf(options.output, "("));
 
-       func = name2func(function_name);
+       struct prototype *func = lookup_symbol_prototype(proc->leader, libsym);
        if (func == NULL) {
+       fail:
                account_output(&current_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 +624,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;
+       }
 
-//              fprintf(options.output, "%s <%lu.%06d>\n", function_name,
-//                              current_time_spent.tv_sec, (int)current_time_spent.tv_usec);
+       /* 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 && (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);
 #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 +728,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 +759,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 +831,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 +867,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)