Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <dwarf.h>
4 : : #include <elfutils/libdwfl.h>
5 : : #include <sys/types.h>
6 : : #include <unistd.h>
7 : :
8 : : #include "alloc-util.h"
9 : : #include "fileio.h"
10 : : #include "fd-util.h"
11 : : #include "format-util.h"
12 : : #include "macro.h"
13 : : #include "stacktrace.h"
14 : : #include "string-util.h"
15 : : #include "util.h"
16 : :
17 : : #define FRAMES_MAX 64
18 : : #define THREADS_MAX 64
19 : :
20 : : struct stack_context {
21 : : FILE *f;
22 : : Dwfl *dwfl;
23 : : Elf *elf;
24 : : unsigned n_thread;
25 : : unsigned n_frame;
26 : : };
27 : :
28 : 0 : static int frame_callback(Dwfl_Frame *frame, void *userdata) {
29 : 0 : struct stack_context *c = userdata;
30 : 0 : Dwarf_Addr pc, pc_adjusted, bias = 0;
31 : 0 : _cleanup_free_ Dwarf_Die *scopes = NULL;
32 : 0 : const char *fname = NULL, *symbol = NULL;
33 : : Dwfl_Module *module;
34 : : bool is_activation;
35 : :
36 [ # # ]: 0 : assert(frame);
37 [ # # ]: 0 : assert(c);
38 : :
39 [ # # ]: 0 : if (c->n_frame >= FRAMES_MAX)
40 : 0 : return DWARF_CB_ABORT;
41 : :
42 [ # # ]: 0 : if (!dwfl_frame_pc(frame, &pc, &is_activation))
43 : 0 : return DWARF_CB_ABORT;
44 : :
45 : 0 : pc_adjusted = pc - (is_activation ? 0 : 1);
46 : :
47 : 0 : module = dwfl_addrmodule(c->dwfl, pc_adjusted);
48 [ # # ]: 0 : if (module) {
49 : : Dwarf_Die *s, *cudie;
50 : : int n;
51 : :
52 : 0 : cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
53 [ # # ]: 0 : if (cudie) {
54 : 0 : n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
55 [ # # ]: 0 : for (s = scopes; s < scopes + n; s++) {
56 [ # # # # ]: 0 : if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
57 : : Dwarf_Attribute *a, space;
58 : :
59 : 0 : a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
60 [ # # ]: 0 : if (!a)
61 : 0 : a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
62 [ # # ]: 0 : if (a)
63 : 0 : symbol = dwarf_formstring(a);
64 [ # # ]: 0 : if (!symbol)
65 : 0 : symbol = dwarf_diename(s);
66 : :
67 [ # # ]: 0 : if (symbol)
68 : 0 : break;
69 : : }
70 : : }
71 : : }
72 : :
73 [ # # ]: 0 : if (!symbol)
74 : 0 : symbol = dwfl_module_addrname(module, pc_adjusted);
75 : :
76 : 0 : fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
77 : : }
78 : :
79 : 0 : fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname));
80 : 0 : c->n_frame++;
81 : :
82 : 0 : return DWARF_CB_OK;
83 : : }
84 : :
85 : 0 : static int thread_callback(Dwfl_Thread *thread, void *userdata) {
86 : 0 : struct stack_context *c = userdata;
87 : : pid_t tid;
88 : :
89 [ # # ]: 0 : assert(thread);
90 [ # # ]: 0 : assert(c);
91 : :
92 [ # # ]: 0 : if (c->n_thread >= THREADS_MAX)
93 : 0 : return DWARF_CB_ABORT;
94 : :
95 [ # # ]: 0 : if (c->n_thread != 0)
96 : 0 : fputc('\n', c->f);
97 : :
98 : 0 : c->n_frame = 0;
99 : :
100 : 0 : tid = dwfl_thread_tid(thread);
101 : 0 : fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
102 : :
103 [ # # ]: 0 : if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
104 : 0 : return DWARF_CB_ABORT;
105 : :
106 : 0 : c->n_thread++;
107 : :
108 : 0 : return DWARF_CB_OK;
109 : : }
110 : :
111 : 0 : static int make_stack_trace(int fd, const char *executable, char **ret) {
112 : :
113 : : static const Dwfl_Callbacks callbacks = {
114 : : .find_elf = dwfl_build_id_find_elf,
115 : : .find_debuginfo = dwfl_standard_find_debuginfo,
116 : : };
117 : :
118 : 0 : struct stack_context c = {};
119 : 0 : char *buf = NULL;
120 : 0 : size_t sz = 0;
121 : : int r;
122 : :
123 [ # # ]: 0 : assert(fd >= 0);
124 [ # # ]: 0 : assert(ret);
125 : :
126 [ # # ]: 0 : if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
127 : 0 : return -errno;
128 : :
129 : 0 : c.f = open_memstream_unlocked(&buf, &sz);
130 [ # # ]: 0 : if (!c.f)
131 : 0 : return -ENOMEM;
132 : :
133 : 0 : elf_version(EV_CURRENT);
134 : :
135 : 0 : c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
136 [ # # ]: 0 : if (!c.elf) {
137 : 0 : r = -EINVAL;
138 : 0 : goto finish;
139 : : }
140 : :
141 : 0 : c.dwfl = dwfl_begin(&callbacks);
142 [ # # ]: 0 : if (!c.dwfl) {
143 : 0 : r = -EINVAL;
144 : 0 : goto finish;
145 : : }
146 : :
147 [ # # ]: 0 : if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
148 : 0 : r = -EINVAL;
149 : 0 : goto finish;
150 : : }
151 : :
152 [ # # ]: 0 : if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
153 : 0 : r = -EINVAL;
154 : 0 : goto finish;
155 : : }
156 : :
157 [ # # ]: 0 : if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
158 : 0 : r = -EINVAL;
159 : 0 : goto finish;
160 : : }
161 : :
162 [ # # ]: 0 : if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
163 : 0 : r = -EINVAL;
164 : 0 : goto finish;
165 : : }
166 : :
167 : 0 : c.f = safe_fclose(c.f);
168 : :
169 : 0 : *ret = TAKE_PTR(buf);
170 : :
171 : 0 : r = 0;
172 : :
173 : 0 : finish:
174 [ # # ]: 0 : if (c.dwfl)
175 : 0 : dwfl_end(c.dwfl);
176 : :
177 [ # # ]: 0 : if (c.elf)
178 : 0 : elf_end(c.elf);
179 : :
180 : 0 : safe_fclose(c.f);
181 : :
182 : 0 : free(buf);
183 : :
184 : 0 : return r;
185 : : }
186 : :
187 : 0 : void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
188 : : int r;
189 : :
190 : 0 : r = make_stack_trace(fd, executable, ret);
191 [ # # ]: 0 : if (r == -EINVAL)
192 [ # # ]: 0 : log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
193 [ # # ]: 0 : else if (r < 0)
194 [ # # ]: 0 : log_warning_errno(r, "Failed to generate stack trace: %m");
195 : 0 : }
|