1 /*
2 * This file is based on a patch submitted by Mark Wielaard <mjw@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@redhat.com>
10 * Copyright (c) 2018 Masatake YAMATO <yamato@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
update_mapping_generation(struct tcb * tcp,void * unused)50 update_mapping_generation(struct tcb *tcp, void *unused)
51 {
52 mapping_generation++;
53 }
54
55 static void
init(void)56 init(void)
57 {
58 mmap_notify_register_client(update_mapping_generation, NULL);
59 }
60
61 static void *
tcb_init(struct tcb * tcp)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
tcb_fin(struct tcb * tcp)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
flush_cache_maybe(struct tcb * tcp)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
frame_callback(Dwfl_Frame * state,void * arg)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
tcb_walk(struct tcb * tcp,unwind_call_action_fn call_action,unwind_error_action_fn error_action,void * data)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