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