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