| /* |
| * This file is based on a patch submitted by Mark Wielaard <[email protected]> |
| * to ltrace project: |
| * https://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e |
| * |
| * It was re-licensed for strace by the original author: |
| * https://lists.strace.io/pipermail/strace-devel/2018-March/008063.html |
| * |
| * Copyright (c) 2014-2018 Mark Wielaard <[email protected]> |
| * Copyright (c) 2018 Masatake YAMATO <[email protected]> |
| * Copyright (c) 2018 The strace developers. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "defs.h" |
| #include "unwind.h" |
| #include "mmap_notify.h" |
| #include <elfutils/libdwfl.h> |
| |
| struct ctx { |
| Dwfl *dwfl; |
| unsigned int last_proc_updating; |
| }; |
| |
| static unsigned int mapping_generation; |
| |
| static void |
| update_mapping_generation(struct tcb *tcp, void *unused) |
| { |
| mapping_generation++; |
| } |
| |
| static void |
| init(void) |
| { |
| mmap_notify_register_client(update_mapping_generation, NULL); |
| } |
| |
| static void * |
| tcb_init(struct tcb *tcp) |
| { |
| static const Dwfl_Callbacks proc_callbacks = { |
| .find_elf = dwfl_linux_proc_find_elf, |
| .find_debuginfo = dwfl_standard_find_debuginfo |
| }; |
| |
| Dwfl *dwfl = dwfl_begin(&proc_callbacks); |
| if (dwfl == NULL) { |
| error_msg("dwfl_begin: %s", dwfl_errmsg(-1)); |
| return NULL; |
| } |
| |
| int r = dwfl_linux_proc_attach(dwfl, tcp->pid, true); |
| if (r) { |
| const char *msg = NULL; |
| |
| if (r < 0) |
| msg = dwfl_errmsg(-1); |
| else if (r > 0) |
| msg = strerror(r); |
| |
| error_msg("dwfl_linux_proc_attach returned an error" |
| " for process %d: %s", tcp->pid, msg); |
| dwfl_end(dwfl); |
| return NULL; |
| } |
| |
| struct ctx *ctx = xmalloc(sizeof(*ctx)); |
| ctx->dwfl = dwfl; |
| ctx->last_proc_updating = 0; |
| return ctx; |
| } |
| |
| static void |
| tcb_fin(struct tcb *tcp) |
| { |
| struct ctx *ctx = tcp->unwind_ctx; |
| if (ctx) { |
| dwfl_end(ctx->dwfl); |
| free(ctx); |
| } |
| } |
| |
| static void |
| flush_cache_maybe(struct tcb *tcp) |
| { |
| struct ctx *ctx = tcp->unwind_ctx; |
| if (!ctx) |
| return; |
| |
| if (ctx->last_proc_updating == mapping_generation) |
| return; |
| |
| int r = dwfl_linux_proc_report(ctx->dwfl, tcp->pid); |
| |
| if (r < 0) |
| error_msg("dwfl_linux_proc_report returned an error" |
| " for pid %d: %s", tcp->pid, dwfl_errmsg(-1)); |
| else if (r > 0) |
| error_msg("dwfl_linux_proc_report returned an error" |
| " for pid %d", tcp->pid); |
| else if (dwfl_report_end(ctx->dwfl, NULL, NULL) != 0) |
| error_msg("dwfl_report_end returned an error" |
| " for pid %d: %s", tcp->pid, dwfl_errmsg(-1)); |
| |
| ctx->last_proc_updating = mapping_generation; |
| } |
| |
| struct frame_user_data { |
| unwind_call_action_fn call_action; |
| unwind_error_action_fn error_action; |
| void *data; |
| int stack_depth; |
| }; |
| |
| static int |
| frame_callback(Dwfl_Frame *state, void *arg) |
| { |
| struct frame_user_data *user_data = arg; |
| Dwarf_Addr pc; |
| bool isactivation; |
| |
| if (!dwfl_frame_pc(state, &pc, &isactivation)) { |
| /* Propagate the error to the caller. */ |
| return -1; |
| } |
| |
| if (!isactivation) |
| pc--; |
| |
| Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state)); |
| Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc); |
| GElf_Off off = 0; |
| |
| if (mod != NULL) { |
| const char *modname = NULL; |
| const char *symname = NULL; |
| GElf_Sym sym; |
| Dwarf_Addr true_offset = pc; |
| |
| modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL); |
| symname = dwfl_module_addrinfo(mod, pc, &off, &sym, |
| NULL, NULL, NULL); |
| dwfl_module_relocate_address(mod, &true_offset); |
| user_data->call_action(user_data->data, modname, symname, |
| off, true_offset); |
| } |
| /* Max number of frames to print reached? */ |
| if (user_data->stack_depth-- == 0) |
| return DWARF_CB_ABORT; |
| |
| return DWARF_CB_OK; |
| } |
| |
| static void |
| tcb_walk(struct tcb *tcp, |
| unwind_call_action_fn call_action, |
| unwind_error_action_fn error_action, |
| void *data) |
| { |
| struct ctx *ctx = tcp->unwind_ctx; |
| if (!ctx) |
| return; |
| |
| struct frame_user_data user_data = { |
| .call_action = call_action, |
| .error_action = error_action, |
| .data = data, |
| .stack_depth = 256, |
| }; |
| |
| flush_cache_maybe(tcp); |
| |
| int r = dwfl_getthread_frames(ctx->dwfl, tcp->pid, frame_callback, |
| &user_data); |
| if (r) |
| error_action(data, |
| r < 0 ? dwfl_errmsg(-1) : "too many stack frames", |
| 0); |
| } |
| |
| const struct unwind_unwinder_t unwinder = { |
| .name = "libdw", |
| .init = init, |
| .tcb_init = tcb_init, |
| .tcb_fin = tcb_fin, |
| .tcb_walk = tcb_walk, |
| }; |