initial prototypes-from-dwarf implementation
[platform/upstream/ltrace.git] / proc.c
diff --git a/proc.c b/proc.c
index 3b4bfdb..c9625f2 100644 (file)
--- a/proc.c
+++ b/proc.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) 1998,2009 Juan Cespedes
  *
 #include <stdlib.h>
 #include <string.h>
 
-#if defined(HAVE_LIBUNWIND)
-#include <libunwind.h>
-#include <libunwind-ptrace.h>
-#endif /* defined(HAVE_LIBUNWIND) */
-
 #include "backend.h"
 #include "breakpoint.h"
 #include "debug.h"
 #include "fetch.h"
+#include "options.h"
 #include "proc.h"
 #include "value_dict.h"
+#include "dwarf_prototypes.h"
+#include "filter.h"
 
-#ifndef ARCH_HAVE_PROCESS_DATA
+#ifndef OS_HAVE_PROCESS_DATA
 int
-arch_process_init(struct Process *proc)
+os_process_init(struct process *proc)
 {
        return 0;
 }
 
 void
-arch_process_destroy(struct Process *proc)
+os_process_destroy(struct process *proc)
 {
 }
 
 int
-arch_process_clone(struct Process *retp, struct Process *proc)
+os_process_clone(struct process *retp, struct process *proc)
 {
        return 0;
 }
 
 int
-arch_process_exec(struct Process *proc)
+os_process_exec(struct process *proc)
 {
        return 0;
 }
 #endif
 
-#ifndef OS_HAVE_PROCESS_DATA
+#ifndef ARCH_HAVE_PROCESS_DATA
 int
-os_process_init(struct Process *proc)
+arch_process_init(struct process *proc)
 {
        return 0;
 }
 
 void
-os_process_destroy(struct Process *proc)
+arch_process_destroy(struct process *proc)
 {
 }
 
 int
-os_process_clone(struct Process *retp, struct Process *proc)
+arch_process_clone(struct process *retp, struct process *proc)
 {
        return 0;
 }
 
 int
-os_process_exec(struct Process *proc)
+arch_process_exec(struct process *proc)
 {
        return 0;
 }
@@ -93,25 +91,32 @@ os_process_exec(struct Process *proc)
 
 #ifndef ARCH_HAVE_DYNLINK_DONE
 void
-arch_dynlink_done(struct Process *proc)
+arch_dynlink_done(struct process *proc)
 {
 }
 #endif
 
-static void add_process(struct Process *proc, int was_exec);
-static void unlist_process(struct Process *proc);
+static int add_process(struct process *proc, int was_exec);
+static void unlist_process(struct process *proc);
 
 static void
-destroy_unwind(struct Process *proc)
+destroy_unwind(struct process *proc)
 {
 #if defined(HAVE_LIBUNWIND)
-       _UPT_destroy(proc->unwind_priv);
-       unw_destroy_addr_space(proc->unwind_as);
+       if (proc->unwind_priv != NULL)
+               _UPT_destroy(proc->unwind_priv);
+       if (proc->unwind_as != NULL)
+               unw_destroy_addr_space(proc->unwind_as);
 #endif /* defined(HAVE_LIBUNWIND) */
+
+#if defined(HAVE_LIBDW)
+       if (proc->dwfl != NULL)
+               dwfl_end(proc->dwfl);
+#endif /* defined(HAVE_LIBDW) */
 }
 
 static int
-process_bare_init(struct Process *proc, const char *filename,
+process_bare_init(struct process *proc, const char *filename,
                  pid_t pid, int was_exec)
 {
        if (!was_exec) {
@@ -121,39 +126,66 @@ process_bare_init(struct Process *proc, const char *filename,
                if (proc->filename == NULL) {
                fail:
                        free(proc->filename);
-                       if (proc->breakpoints != NULL)
-                               dict_clear(proc->breakpoints);
+                       if (proc->breakpoints != NULL) {
+                               dict_destroy(proc->breakpoints,
+                                            NULL, NULL, NULL);
+                               free(proc->breakpoints);
+                               proc->breakpoints = NULL;
+                       }
                        return -1;
                }
        }
 
        /* Add process so that we know who the leader is.  */
        proc->pid = pid;
-       add_process(proc, was_exec);
-       if (proc->leader == NULL)
+       if (add_process(proc, was_exec) < 0)
                goto fail;
+       if (proc->leader == NULL) {
+       unlist_and_fail:
+               if (!was_exec)
+                       unlist_process(proc);
+               goto fail;
+       }
 
        if (proc->leader == proc) {
-               proc->breakpoints = dict_init(target_address_hash,
-                                             target_address_cmp);
+               proc->breakpoints = malloc(sizeof(*proc->breakpoints));
                if (proc->breakpoints == NULL)
-                       goto fail;
+                       goto unlist_and_fail;
+               DICT_INIT(proc->breakpoints,
+                         arch_addr_t, struct breakpoint *,
+                         arch_addr_hash, arch_addr_eq, NULL);
        } else {
                proc->breakpoints = NULL;
        }
 
 #if defined(HAVE_LIBUNWIND)
-       proc->unwind_priv = _UPT_create(pid);
-       proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0);
+       if (options.bt_depth > 0) {
+               proc->unwind_priv = _UPT_create(pid);
+               proc->unwind_as = unw_create_addr_space(&_UPT_accessors, 0);
+
+               if (proc->unwind_priv == NULL || proc->unwind_as == NULL) {
+                       fprintf(stderr,
+                               "Couldn't initialize unwinding "
+                               "for process %d\n", proc->pid);
+                       destroy_unwind(proc);
+                       proc->unwind_priv = NULL;
+                       proc->unwind_as = NULL;
+               }
+       }
 #endif /* defined(HAVE_LIBUNWIND) */
 
+#if defined(HAVE_LIBDW)
+       proc->dwfl = NULL; /* Initialize for leader only on first library.  */
+#endif /* defined(HAVE_LIBDW) */
+
        return 0;
 }
 
 static void
-process_bare_destroy(struct Process *proc, int was_exec)
+process_bare_destroy(struct process *proc, int was_exec)
 {
-       dict_clear(proc->breakpoints);
+       dict_destroy(proc->breakpoints, NULL, NULL, NULL);
+       free(proc->breakpoints);
        if (!was_exec) {
                free(proc->filename);
                unlist_process(proc);
@@ -162,7 +194,7 @@ process_bare_destroy(struct Process *proc, int was_exec)
 }
 
 static int
-process_init_main(struct Process *proc)
+process_init_main(struct process *proc)
 {
        if (breakpoints_init(proc) < 0) {
                fprintf(stderr, "failed to init breakpoints %d\n",
@@ -174,7 +206,7 @@ process_init_main(struct Process *proc)
 }
 
 int
-process_init(struct Process *proc, const char *filename, pid_t pid)
+process_init(struct process *proc, const char *filename, pid_t pid)
 {
        if (process_bare_init(proc, filename, pid, 0) < 0) {
        fail:
@@ -194,9 +226,11 @@ process_init(struct Process *proc, const char *filename, pid_t pid)
                goto fail;
        }
 
-       if (proc->leader != proc)
-               return 0;
-       if (process_init_main(proc) < 0) {
+       if (proc->leader != proc) {
+               proc->e_machine = proc->leader->e_machine;
+               proc->e_class = proc->leader->e_class;
+               get_arch_dep(proc);
+       } else if (process_init_main(proc) < 0) {
                process_bare_destroy(proc, 0);
                goto fail;
        }
@@ -204,7 +238,7 @@ process_init(struct Process *proc, const char *filename, pid_t pid)
 }
 
 static enum callback_status
-destroy_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data)
+destroy_breakpoint_cb(struct process *proc, struct breakpoint *bp, void *data)
 {
        breakpoint_destroy(bp);
        free(bp);
@@ -212,10 +246,10 @@ destroy_breakpoint_cb(struct Process *proc, struct breakpoint *bp, void *data)
 }
 
 // XXX see comment in handle_event.c
-void callstack_pop(struct Process *proc);
+void callstack_pop(struct process *proc);
 
 static void
-private_process_destroy(struct Process *proc, int was_exec)
+private_process_destroy(struct process *proc, int was_exec)
 {
        /* Pop remaining stack elements.  */
        while (proc->callstack_depth > 0) {
@@ -249,7 +283,8 @@ private_process_destroy(struct Process *proc, int was_exec)
        /* Breakpoints.  */
        if (proc->breakpoints != NULL) {
                proc_each_breakpoint(proc, NULL, destroy_breakpoint_cb, NULL);
-               dict_clear(proc->breakpoints);
+               dict_destroy(proc->breakpoints, NULL, NULL, NULL);
+               free(proc->breakpoints);
                proc->breakpoints = NULL;
        }
 
@@ -257,7 +292,7 @@ private_process_destroy(struct Process *proc, int was_exec)
 }
 
 void
-process_destroy(struct Process *proc)
+process_destroy(struct process *proc)
 {
        arch_process_destroy(proc);
        os_process_destroy(proc);
@@ -265,7 +300,7 @@ process_destroy(struct Process *proc)
 }
 
 int
-process_exec(struct Process *proc)
+process_exec(struct process *proc)
 {
        /* Call exec handlers first, before we destroy the main
         * state.  */
@@ -284,11 +319,11 @@ process_exec(struct Process *proc)
        return 0;
 }
 
-struct Process *
+struct process *
 open_program(const char *filename, pid_t pid)
 {
        assert(pid != 0);
-       struct Process *proc = malloc(sizeof(*proc));
+       struct process *proc = malloc(sizeof(*proc));
        if (proc == NULL || process_init(proc, filename, pid) < 0) {
                free(proc);
                return NULL;
@@ -297,41 +332,36 @@ open_program(const char *filename, pid_t pid)
 }
 
 struct clone_single_bp_data {
-       struct Process *old_proc;
-       struct Process *new_proc;
-       int error;
+       struct process *old_proc;
+       struct process *new_proc;
 };
 
-static void
-clone_single_bp(void *key, void *value, void *u)
+static enum callback_status
+clone_single_bp(arch_addr_t *key, struct breakpoint **bpp, void *u)
 {
-       struct breakpoint *bp = value;
+       struct breakpoint *bp = *bpp;
        struct clone_single_bp_data *data = u;
 
-       /* Don't bother if there were errors anyway.  */
-       if (data->error != 0)
-               return;
-
        struct breakpoint *clone = malloc(sizeof(*clone));
        if (clone == NULL
-           || breakpoint_clone(clone, data->new_proc,
-                               bp, data->old_proc) < 0) {
+           || breakpoint_clone(clone, data->new_proc, bp) < 0) {
        fail:
                free(clone);
-               data->error = -1;
+               return CBS_STOP;
        }
        if (proc_add_breakpoint(data->new_proc->leader, clone) < 0) {
                breakpoint_destroy(clone);
                goto fail;
        }
+       return CBS_CONT;
 }
 
 int
-process_clone(struct Process *retp, struct Process *proc, pid_t pid)
+process_clone(struct process *retp, struct process *proc, pid_t pid)
 {
        if (process_bare_init(retp, proc->filename, pid, 0) < 0) {
        fail1:
-               fprintf(stderr, "failed to clone process %d->%d : %s\n",
+               fprintf(stderr, "Failed to clone process %d to %d: %s\n",
                        proc->pid, pid, strerror(errno));
                return -1;
        }
@@ -350,8 +380,12 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
        struct library **nlibp = &retp->libraries;
        for (lib = proc->leader->libraries; lib != NULL; lib = lib->next) {
                *nlibp = malloc(sizeof(**nlibp));
+
                if (*nlibp == NULL
                    || library_clone(*nlibp, lib) < 0) {
+                       free(*nlibp);
+                       *nlibp = NULL;
+
                fail2:
                        process_bare_destroy(retp, 0);
 
@@ -373,10 +407,10 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
        struct clone_single_bp_data data = {
                .old_proc = proc,
                .new_proc = retp,
-               .error = 0,
        };
-       dict_apply_to_all(proc->leader->breakpoints, &clone_single_bp, &data);
-       if (data.error < 0)
+       if (DICT_EACH(proc->leader->breakpoints,
+                     arch_addr_t, struct breakpoint *, NULL,
+                     clone_single_bp, &data) != NULL)
                goto fail2;
 
        /* And finally the call stack.  */
@@ -396,7 +430,7 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
                                size_t j;
                        fail3:
                                for (j = 0; j < i; ++j) {
-                                       nctx = elem->fetch_context;
+                                       nctx = retp->callstack[j].fetch_context;
                                        fetch_arg_done(nctx);
                                        elem->fetch_context = NULL;
                                }
@@ -405,14 +439,13 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
                        elem->fetch_context = nctx;
                }
 
-               struct value_dict *args = elem->arguments;
-               if (args != NULL) {
+               if (elem->arguments != NULL) {
                        struct value_dict *nargs = malloc(sizeof(*nargs));
                        if (nargs == NULL
-                           || val_dict_clone(nargs, args) < 0) {
+                           || val_dict_clone(nargs, elem->arguments) < 0) {
                                size_t j;
                                for (j = 0; j < i; ++j) {
-                                       nargs = elem->arguments;
+                                       nargs = retp->callstack[j].arguments;
                                        val_dict_destroy(nargs);
                                        free(nargs);
                                        elem->arguments = NULL;
@@ -456,20 +489,18 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
 static int
 open_one_pid(pid_t pid)
 {
-       Process *proc;
-       char *filename;
        debug(DEBUG_PROCESS, "open_one_pid(pid=%d)", pid);
 
        /* Get the filename first.  Should the trace_pid fail, we can
         * easily free it, untracing is more work.  */
-       if ((filename = pid2name(pid)) == NULL
-           || trace_pid(pid) < 0) {
+       char *filename = pid2name(pid);
+       if (filename == NULL || trace_pid(pid) < 0) {
        fail:
                free(filename);
                return -1;
        }
 
-       proc = open_program(filename, pid);
+       struct process *proc = open_program(filename, pid);
        if (proc == NULL)
                goto fail;
        free(filename);
@@ -479,12 +510,31 @@ open_one_pid(pid_t pid)
 }
 
 static enum callback_status
-start_one_pid(Process * proc, void * data)
+start_one_pid(struct process *proc, void *data)
 {
        continue_process(proc->pid);
        return CBS_CONT;
 }
 
+static enum callback_status
+is_main(struct process *proc, struct library *lib, void *data)
+{
+       return CBS_STOP_IF(lib->type == LT_LIBTYPE_MAIN);
+}
+
+void
+process_hit_start(struct process *proc)
+{
+       struct process *leader = proc->leader;
+       assert(leader != NULL);
+
+       struct library *mainlib
+               = proc_each_library(leader, NULL, is_main, NULL);
+       assert(mainlib != NULL);
+       linkmap_init(leader, mainlib->dyn_addr);
+       arch_dynlink_done(leader);
+}
+
 void
 open_pid(pid_t pid)
 {
@@ -495,7 +545,7 @@ open_pid(pid_t pid)
                return;
 
        /* First, see if we can attach the requested PID itself.  */
-       if (open_one_pid(pid)) {
+       if (open_one_pid(pid) < 0) {
                fprintf(stderr, "Cannot attach to pid %u: %s\n",
                        pid, strerror(errno));
                trace_fail_warning(pid);
@@ -527,7 +577,7 @@ open_pid(pid_t pid)
                have_all = 1;
                for (i = 0; i < ntasks; ++i)
                        if (pid2proc(tasks[i]) == NULL
-                           && open_one_pid(tasks[i]))
+                           && open_one_pid(tasks[i]) < 0)
                                have_all = 0;
 
                free(tasks);
@@ -537,40 +587,39 @@ open_pid(pid_t pid)
                old_ntasks = ntasks;
        }
 
-       struct Process *leader = pid2proc(pid)->leader;
+       struct process *leader = pid2proc(pid)->leader;
 
        /* XXX Is there a way to figure out whether _start has
         * actually already been hit?  */
-       arch_dynlink_done(leader);
+       process_hit_start(leader);
 
        /* Done.  Continue everyone.  */
        each_task(leader, NULL, start_one_pid, NULL);
 }
 
 static enum callback_status
-find_proc(Process * proc, void * data)
+find_proc(struct process *proc, void *data)
 {
-       pid_t pid = (pid_t)(uintptr_t)data;
-       return proc->pid == pid ? CBS_STOP : CBS_CONT;
+       return CBS_STOP_IF(proc->pid == (pid_t)(uintptr_t)data);
 }
 
-Process *
-pid2proc(pid_t pid) {
+struct process *
+pid2proc(pid_t pid)
+{
        return each_process(NULL, &find_proc, (void *)(uintptr_t)pid);
 }
 
-static Process * list_of_processes = NULL;
+static struct process *list_of_processes = NULL;
 
 static void
-unlist_process(Process * proc)
+unlist_process(struct process *proc)
 {
-       Process *tmp;
-
        if (list_of_processes == proc) {
                list_of_processes = list_of_processes->next;
                return;
        }
 
+       struct process *tmp;
        for (tmp = list_of_processes; ; tmp = tmp->next) {
                /* If the following assert fails, the process wasn't
                 * in the list.  */
@@ -583,17 +632,17 @@ unlist_process(Process * proc)
        }
 }
 
-struct Process *
-each_process(struct Process *start_after,
-            enum callback_status(*cb)(struct Process *proc, void *data),
+struct process *
+each_process(struct process *start_after,
+            enum callback_status(*cb)(struct process *proc, void *data),
             void *data)
 {
-       struct Process *it = start_after == NULL ? list_of_processes
+       struct process *it = start_after == NULL ? list_of_processes
                : start_after->next;
 
        while (it != NULL) {
                /* Callback might call remove_process.  */
-               struct Process *next = it->next;
+               struct process *next = it->next;
                switch ((*cb)(it, data)) {
                case CBS_FAIL:
                        /* XXX handle me */
@@ -607,20 +656,20 @@ each_process(struct Process *start_after,
        return NULL;
 }
 
-Process *
-each_task(struct Process *proc, struct Process *start_after,
-         enum callback_status(*cb)(struct Process *proc, void *data),
+struct process *
+each_task(struct process *proc, struct process *start_after,
+         enum callback_status(*cb)(struct process *proc, void *data),
          void *data)
 {
        assert(proc != NULL);
-       struct Process *it = start_after == NULL ? proc->leader
+       struct process *it = start_after == NULL ? proc->leader
                : start_after->next;
 
        if (it != NULL) {
-               struct Process *leader = it->leader;
+               struct process *leader = it->leader;
                while (it != NULL && it->leader == leader) {
                        /* Callback might call remove_process.  */
-                       struct Process *next = it->next;
+                       struct process *next = it->next;
                        switch ((*cb)(it, data)) {
                        case CBS_FAIL:
                                /* XXX handle me */
@@ -635,20 +684,20 @@ each_task(struct Process *proc, struct Process *start_after,
        return NULL;
 }
 
-static void
-add_process(struct Process *proc, int was_exec)
+static int
+add_process(struct process *proc, int was_exec)
 {
-       Process ** leaderp = &list_of_processes;
+       struct process **leaderp = &list_of_processes;
        if (proc->pid) {
                pid_t tgid = process_leader(proc->pid);
                if (tgid == 0)
                        /* Must have been terminated before we managed
                         * to fully attach.  */
-                       return;
-               if (tgid == proc->pid)
+                       return -1;
+               if (tgid == proc->pid) {
                        proc->leader = proc;
-               else {
-                       Process * leader = pid2proc(tgid);
+               else {
+                       struct process *leader = pid2proc(tgid);
                        proc->leader = leader;
                        if (leader != NULL)
                                leaderp = &leader->next;
@@ -659,12 +708,13 @@ add_process(struct Process *proc, int was_exec)
                proc->next = *leaderp;
                *leaderp = proc;
        }
+       return 0;
 }
 
 void
-change_process_leader(Process * proc, Process * leader)
+change_process_leader(struct process *proc, struct process *leader)
 {
-       Process ** leaderp = &list_of_processes;
+       struct process **leaderp = &list_of_processes;
        if (proc->leader == leader)
                return;
 
@@ -679,7 +729,7 @@ change_process_leader(Process * proc, Process * leader)
 }
 
 static enum callback_status
-clear_leader(struct Process *proc, void *data)
+clear_leader(struct process *proc, void *data)
 {
        debug(DEBUG_FUNCTION, "detach_task %d from leader %d",
              proc->pid, proc->leader->pid);
@@ -688,7 +738,7 @@ clear_leader(struct Process *proc, void *data)
 }
 
 void
-remove_process(Process *proc)
+remove_process(struct process *proc)
 {
        debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid);
 
@@ -702,7 +752,7 @@ remove_process(Process *proc)
 }
 
 void
-install_event_handler(Process *proc, struct event_handler *handler)
+install_event_handler(struct process *proc, struct event_handler *handler)
 {
        debug(DEBUG_FUNCTION, "install_event_handler(pid=%d, %p)", proc->pid, handler);
        assert(proc->event_handler == NULL);
@@ -710,7 +760,7 @@ install_event_handler(Process *proc, struct event_handler *handler)
 }
 
 void
-destroy_event_handler(Process * proc)
+destroy_event_handler(struct process *proc)
 {
        struct event_handler *handler = proc->event_handler;
        debug(DEBUG_FUNCTION, "destroy_event_handler(pid=%d, %p)", proc->pid, handler);
@@ -722,7 +772,7 @@ destroy_event_handler(Process * proc)
 }
 
 static int
-breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc)
+breakpoint_for_symbol(struct library_symbol *libsym, struct process *proc)
 {
        arch_addr_t bp_addr;
        assert(proc->leader == proc);
@@ -749,9 +799,8 @@ breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc)
         * be also custom-allocated, and we would really need to swap
         * the two: delete the one now in the dictionary, swap values
         * around, and put the new breakpoint back in.  */
-       struct breakpoint *bp = dict_find_entry(proc->breakpoints,
-                                               bp_addr);
-       if (bp != NULL) {
+       struct breakpoint *bp;
+       if (DICT_FIND_VAL(proc->breakpoints, &bp_addr, &bp) == 0) {
                /* MIPS backend makes duplicate requests.  This is
                 * likely a bug in the backend.  Currently there's no
                 * point assigning more than one symbol to a
@@ -795,11 +844,11 @@ breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc)
 static enum callback_status
 cb_breakpoint_for_symbol(struct library_symbol *libsym, void *data)
 {
-       return breakpoint_for_symbol(libsym, data) < 0 ? CBS_FAIL : CBS_CONT;
+       return CBS_STOP_IF(breakpoint_for_symbol(libsym, data) < 0);
 }
 
 static int
-proc_activate_latent_symbol(struct Process *proc,
+proc_activate_latent_symbol(struct process *proc,
                            struct library_symbol *libsym)
 {
        assert(libsym->latent);
@@ -809,7 +858,7 @@ proc_activate_latent_symbol(struct Process *proc,
 }
 
 int
-proc_activate_delayed_symbol(struct Process *proc,
+proc_activate_delayed_symbol(struct process *proc,
                             struct library_symbol *libsym)
 {
        assert(libsym->delayed);
@@ -819,7 +868,7 @@ proc_activate_delayed_symbol(struct Process *proc,
 }
 
 static enum callback_status
-activate_latent_in(struct Process *proc, struct library *lib, void *data)
+activate_latent_in(struct process *proc, struct library *lib, void *data)
 {
        struct library_exported_name *exported;
        for (exported = data; exported != NULL; exported = exported->next) {
@@ -836,7 +885,7 @@ activate_latent_in(struct Process *proc, struct library *lib, void *data)
 }
 
 void
-proc_add_library(struct Process *proc, struct library *lib)
+proc_add_library(struct process *proc, struct library *lib)
 {
        assert(lib->next == NULL);
        lib->next = proc->libraries;
@@ -844,12 +893,77 @@ proc_add_library(struct Process *proc, struct library *lib)
        debug(DEBUG_PROCESS, "added library %s@%p (%s) to %d",
              lib->soname, lib->base, lib->pathname, proc->pid);
 
+#if defined(HAVE_LIBDW)
+       Dwfl *dwfl = NULL;
+
+       /* Setup module tracking for libdwfl unwinding.  */
+       struct process *leader = proc->leader;
+       dwfl = leader->dwfl;
+       if (dwfl == NULL) {
+               static const Dwfl_Callbacks proc_callbacks = {
+                       .find_elf = dwfl_linux_proc_find_elf,
+                       .find_debuginfo = dwfl_standard_find_debuginfo
+               };
+               dwfl = dwfl_begin(&proc_callbacks);
+               if (dwfl == NULL)
+                       fprintf(stderr,
+                                       "Couldn't initialize libdwfl unwinding "
+                                       "for process %d: %s\n", leader->pid,
+                                       dwfl_errmsg (-1));
+       }
+
+       if (dwfl != NULL) {
+               dwfl_report_begin_add(dwfl);
+               if (dwfl_report_elf(dwfl, lib->soname,
+                                                       lib->pathname, -1,
+                                                       (GElf_Addr) lib->base,
+                                                       false) == NULL)
+                       fprintf(stderr,
+                                       "dwfl_report_elf %s@%p (%s) %d: %s\n",
+                                       lib->soname, lib->base, lib->pathname,
+                                       proc->pid, dwfl_errmsg (-1));
+               dwfl_report_end(dwfl, NULL, NULL);
+
+               if (options.bt_depth > 0) {
+                       if (leader->dwfl == NULL) {
+                               int r = dwfl_linux_proc_attach(dwfl,
+                                                                                          leader->pid,
+                                                                                          true);
+                               if (r == 0)
+                                       leader->dwfl = dwfl;
+                               else {
+                                       const char *msg;
+                                       dwfl_end(dwfl);
+                                       if (r < 0)
+                                               msg = dwfl_errmsg(-1);
+                                       else
+                                               msg = strerror(r);
+                                       fprintf(stderr, "Couldn't initialize "
+                                               "libdwfl unwinding for "
+                                               "process %d: %s\n",
+                                               leader->pid, msg);
+                               }
+                       }
+               }
+       }
+
+       if( 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->protolib, lib, dwfl );
+       }
+
+#endif /* defined(HAVE_LIBDW) */
+
        /* Insert breakpoints for all active (non-latent) symbols.  */
        struct library_symbol *libsym = NULL;
        while ((libsym = library_each_symbol(lib, libsym,
                                             cb_breakpoint_for_symbol,
                                             proc)) != NULL)
-               fprintf(stderr, "Couldn't insert breakpoint for %s to %d: %s.",
+               fprintf(stderr,
+                       "Couldn't insert breakpoint for %s to %d: %s.\n",
                        libsym->name, proc->pid, strerror(errno));
 
        /* Look through export list of the new library and compare it
@@ -859,12 +973,12 @@ proc_add_library(struct Process *proc, struct library *lib)
        while ((lib2 = proc_each_library(proc, lib2, activate_latent_in,
                                         lib->exported_names)) != NULL)
                fprintf(stderr,
-                       "Couldn't activate latent symbols for %s in %d: %s.",
-                       libsym->name, proc->pid, strerror(errno));
+                       "Couldn't activate latent symbols for %s in %d: %s.\n",
+                       lib2->soname, proc->pid, strerror(errno));
 }
 
 int
-proc_remove_library(struct Process *proc, struct library *lib)
+proc_remove_library(struct process *proc, struct library *lib)
 {
        struct library **libp;
        for (libp = &proc->libraries; *libp != NULL; libp = &(*libp)->next)
@@ -876,13 +990,15 @@ proc_remove_library(struct Process *proc, struct library *lib)
 }
 
 struct library *
-proc_each_library(struct Process *proc, struct library *it,
-                 enum callback_status (*cb)(struct Process *proc,
+proc_each_library(struct process *proc, struct library *it,
+                 enum callback_status (*cb)(struct process *proc,
                                             struct library *lib, void *data),
                  void *data)
 {
        if (it == NULL)
                it = proc->libraries;
+       else
+               it = it->next;
 
        while (it != NULL) {
                struct library *next = it->next;
@@ -903,7 +1019,7 @@ proc_each_library(struct Process *proc, struct library *it,
 }
 
 static void
-check_leader(struct Process *proc)
+check_leader(struct process *proc)
 {
        /* Only the group leader should be getting the breakpoints and
         * thus have ->breakpoint initialized.  */
@@ -913,7 +1029,7 @@ check_leader(struct Process *proc)
 }
 
 int
-proc_add_breakpoint(struct Process *proc, struct breakpoint *bp)
+proc_add_breakpoint(struct process *proc, struct breakpoint *bp)
 {
        debug(DEBUG_FUNCTION, "proc_add_breakpoint(pid=%d, %s@%p)",
              proc->pid, breakpoint_name(bp), bp->addr);
@@ -922,9 +1038,9 @@ proc_add_breakpoint(struct Process *proc, struct breakpoint *bp)
        /* XXX We might merge bp->libsym instead of the following
         * assert, but that's not necessary right now.  Read the
         * comment in breakpoint_for_symbol.  */
-       assert(dict_find_entry(proc->breakpoints, bp->addr) == NULL);
+       assert(dict_find(proc->breakpoints, &bp->addr) == NULL);
 
-       if (dict_enter(proc->breakpoints, bp->addr, bp) < 0) {
+       if (DICT_INSERT(proc->breakpoints, &bp->addr, &bp) < 0) {
                fprintf(stderr,
                        "couldn't enter breakpoint %s@%p to dictionary: %s\n",
                        breakpoint_name(bp), bp->addr, strerror(errno));
@@ -935,67 +1051,50 @@ proc_add_breakpoint(struct Process *proc, struct breakpoint *bp)
 }
 
 void
-proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp)
+proc_remove_breakpoint(struct process *proc, struct breakpoint *bp)
 {
        debug(DEBUG_FUNCTION, "proc_remove_breakpoint(pid=%d, %s@%p)",
              proc->pid, breakpoint_name(bp), bp->addr);
        check_leader(proc);
-       struct breakpoint *removed = dict_remove(proc->breakpoints, bp->addr);
-       assert(removed == bp);
+       int rc = DICT_ERASE(proc->breakpoints, &bp->addr, struct breakpoint *,
+                           NULL, NULL, NULL);
+       assert(rc == 0);
 }
 
-/* Dict doesn't support iteration restarts, so here's this contraption
- * for now.  XXX add restarts to dict.  */
 struct each_breakpoint_data
 {
-       void *start;
-       void *end;
-       struct Process *proc;
-       enum callback_status (*cb)(struct Process *proc,
+       struct process *proc;
+       enum callback_status (*cb)(struct process *proc,
                                   struct breakpoint *bp,
                                   void *data);
        void *cb_data;
 };
 
-static void
-each_breakpoint_cb(void *key, void *value, void *d)
+static enum callback_status
+each_breakpoint_cb(arch_addr_t *key, struct breakpoint **bpp, void *d)
 {
        struct each_breakpoint_data *data = d;
-       if (data->end != NULL)
-               return;
-       if (data->start == key)
-               data->start = NULL;
-
-       if (data->start == NULL) {
-               switch (data->cb(data->proc, value, data->cb_data)) {
-               case CBS_FAIL:
-                       /* XXX handle me */
-               case CBS_STOP:
-                       data->end = key;
-               case CBS_CONT:
-                       return;
-               }
-       }
+       return data->cb(data->proc, *bpp, data->cb_data);
 }
 
-void *
-proc_each_breakpoint(struct Process *proc, void *start,
-                    enum callback_status (*cb)(struct Process *proc,
+arch_addr_t *
+proc_each_breakpoint(struct process *proc, arch_addr_t *start,
+                    enum callback_status (*cb)(struct process *proc,
                                                struct breakpoint *bp,
                                                void *data), void *data)
 {
        struct each_breakpoint_data dd = {
-               .start = start,
                .proc = proc,
                .cb = cb,
                .cb_data = data,
        };
-       dict_apply_to_all(proc->breakpoints, &each_breakpoint_cb, &dd);
-       return dd.end;
+       return DICT_EACH(proc->breakpoints,
+                        arch_addr_t, struct breakpoint *, start,
+                        &each_breakpoint_cb, &dd);
 }
 
 int
-proc_find_symbol(struct Process *proc, struct library_symbol *sym,
+proc_find_symbol(struct process *proc, struct library_symbol *sym,
                 struct library **retlib, struct library_symbol **retsym)
 {
        struct library *lib = sym->lib;
@@ -1021,7 +1120,7 @@ proc_find_symbol(struct Process *proc, struct library_symbol *sym,
 }
 
 struct library_symbol *
-proc_each_symbol(struct Process *proc, struct library_symbol *start_after,
+proc_each_symbol(struct process *proc, struct library_symbol *start_after,
                 enum callback_status (*cb)(struct library_symbol *, void *),
                 void *data)
 {
@@ -1035,3 +1134,26 @@ proc_each_symbol(struct Process *proc, struct library_symbol *start_after,
 
        return NULL;
 }
+
+#define DEF_READER(NAME, SIZE)                                         \
+       int                                                             \
+       NAME(struct process *proc, arch_addr_t addr,                    \
+            uint##SIZE##_t *lp)                                        \
+       {                                                               \
+               union {                                                 \
+                       uint##SIZE##_t dst;                             \
+                       char buf[0];                                    \
+               } u;                                                    \
+               if (umovebytes(proc, addr, &u.buf, sizeof(u.dst))       \
+                   != sizeof(u.dst))                                   \
+                       return -1;                                      \
+               *lp = u.dst;                                            \
+               return 0;                                               \
+       }
+
+DEF_READER(proc_read_8, 8)
+DEF_READER(proc_read_16, 16)
+DEF_READER(proc_read_32, 32)
+DEF_READER(proc_read_64, 64)
+
+#undef DEF_READER