1 /* 2 * This file is based on a patch submitted by Mark Wielaard <mjw (at) redhat.com> 3 * to ltrace project: 4 * https://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e 5 * 6 * It was re-licensed for strace by the original author: 7 * https://lists.strace.io/pipermail/strace-devel/2018-March/008063.html 8 * 9 * Copyright (c) 2014-2018 Mark Wielaard <mjw (at) redhat.com> 10 * Copyright (c) 2018 Masatake YAMATO <yamato (at) redhat.com> 11 * Copyright (c) 2018 The strace developers. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. The name of the author may not be used to endorse or promote products 23 * derived from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include "defs.h" 38 #include "unwind.h" 39 #include "mmap_notify.h" 40 #include <elfutils/libdwfl.h> 41 42 struct ctx { 43 Dwfl *dwfl; 44 unsigned int last_proc_updating; 45 }; 46 47 static unsigned int mapping_generation; 48 49 static void 50 update_mapping_generation(struct tcb *tcp, void *unused) 51 { 52 mapping_generation++; 53 } 54 55 static void 56 init(void) 57 { 58 mmap_notify_register_client(update_mapping_generation, NULL); 59 } 60 61 static void * 62 tcb_init(struct tcb *tcp) 63 { 64 static const Dwfl_Callbacks proc_callbacks = { 65 .find_elf = dwfl_linux_proc_find_elf, 66 .find_debuginfo = dwfl_standard_find_debuginfo 67 }; 68 69 Dwfl *dwfl = dwfl_begin(&proc_callbacks); 70 if (dwfl == NULL) { 71 error_msg("dwfl_begin: %s", dwfl_errmsg(-1)); 72 return NULL; 73 } 74 75 int r = dwfl_linux_proc_attach(dwfl, tcp->pid, true); 76 if (r) { 77 const char *msg = NULL; 78 79 if (r < 0) 80 msg = dwfl_errmsg(-1); 81 else if (r > 0) 82 msg = strerror(r); 83 84 error_msg("dwfl_linux_proc_attach returned an error" 85 " for process %d: %s", tcp->pid, msg); 86 dwfl_end(dwfl); 87 return NULL; 88 } 89 90 struct ctx *ctx = xmalloc(sizeof(*ctx)); 91 ctx->dwfl = dwfl; 92 ctx->last_proc_updating = 0; 93 return ctx; 94 } 95 96 static void 97 tcb_fin(struct tcb *tcp) 98 { 99 struct ctx *ctx = tcp->unwind_ctx; 100 if (ctx) { 101 dwfl_end(ctx->dwfl); 102 free(ctx); 103 } 104 } 105 106 static void 107 flush_cache_maybe(struct tcb *tcp) 108 { 109 struct ctx *ctx = tcp->unwind_ctx; 110 if (!ctx) 111 return; 112 113 if (ctx->last_proc_updating == mapping_generation) 114 return; 115 116 int r = dwfl_linux_proc_report(ctx->dwfl, tcp->pid); 117 118 if (r < 0) 119 error_msg("dwfl_linux_proc_report returned an error" 120 " for pid %d: %s", tcp->pid, dwfl_errmsg(-1)); 121 else if (r > 0) 122 error_msg("dwfl_linux_proc_report returned an error" 123 " for pid %d", tcp->pid); 124 else if (dwfl_report_end(ctx->dwfl, NULL, NULL) != 0) 125 error_msg("dwfl_report_end returned an error" 126 " for pid %d: %s", tcp->pid, dwfl_errmsg(-1)); 127 128 ctx->last_proc_updating = mapping_generation; 129 } 130 131 struct frame_user_data { 132 unwind_call_action_fn call_action; 133 unwind_error_action_fn error_action; 134 void *data; 135 int stack_depth; 136 }; 137 138 static int 139 frame_callback(Dwfl_Frame *state, void *arg) 140 { 141 struct frame_user_data *user_data = arg; 142 Dwarf_Addr pc; 143 bool isactivation; 144 145 if (!dwfl_frame_pc(state, &pc, &isactivation)) { 146 /* Propagate the error to the caller. */ 147 return -1; 148 } 149 150 if (!isactivation) 151 pc--; 152 153 Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state)); 154 Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc); 155 GElf_Off off = 0; 156 157 if (mod != NULL) { 158 const char *modname = NULL; 159 const char *symname = NULL; 160 GElf_Sym sym; 161 Dwarf_Addr true_offset = pc; 162 163 modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, 164 NULL, NULL, NULL); 165 symname = dwfl_module_addrinfo(mod, pc, &off, &sym, 166 NULL, NULL, NULL); 167 dwfl_module_relocate_address(mod, &true_offset); 168 user_data->call_action(user_data->data, modname, symname, 169 off, true_offset); 170 } 171 /* Max number of frames to print reached? */ 172 if (user_data->stack_depth-- == 0) 173 return DWARF_CB_ABORT; 174 175 return DWARF_CB_OK; 176 } 177 178 static void 179 tcb_walk(struct tcb *tcp, 180 unwind_call_action_fn call_action, 181 unwind_error_action_fn error_action, 182 void *data) 183 { 184 struct ctx *ctx = tcp->unwind_ctx; 185 if (!ctx) 186 return; 187 188 struct frame_user_data user_data = { 189 .call_action = call_action, 190 .error_action = error_action, 191 .data = data, 192 .stack_depth = 256, 193 }; 194 195 flush_cache_maybe(tcp); 196 197 int r = dwfl_getthread_frames(ctx->dwfl, tcp->pid, frame_callback, 198 &user_data); 199 if (r) 200 error_action(data, 201 r < 0 ? dwfl_errmsg(-1) : "too many stack frames", 202 0); 203 } 204 205 const struct unwind_unwinder_t unwinder = { 206 .name = "libdw", 207 .init = init, 208 .tcb_init = tcb_init, 209 .tcb_fin = tcb_fin, 210 .tcb_walk = tcb_walk, 211 }; 212