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