]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/stacktrace.c
coredump: include stacktrace of coredumps in the log message
[thirdparty/systemd.git] / src / journal / stacktrace.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dwarf.h>
23 #include <elfutils/libdwfl.h>
24
25 #include "util.h"
26 #include "macro.h"
27 #include "stacktrace.h"
28
29 #define FRAMES_MAX 64
30 #define THREADS_MAX 64
31
32 struct stack_context {
33 FILE *f;
34 Dwfl *dwfl;
35 Elf *elf;
36 unsigned n_thread;
37 unsigned n_frame;
38 };
39
40 static int frame_callback(Dwfl_Frame *frame, void *userdata) {
41 struct stack_context *c = userdata;
42 Dwarf_Addr pc, pc_adjusted, bias = 0;
43 _cleanup_free_ Dwarf_Die *scopes = NULL;
44 const char *fname = NULL, *symbol = NULL;
45 Dwfl_Module *module;
46 bool is_activation;
47
48 assert(frame);
49 assert(c);
50
51 if (c->n_frame >= FRAMES_MAX)
52 return DWARF_CB_ABORT;
53
54 if (!dwfl_frame_pc(frame, &pc, &is_activation))
55 return DWARF_CB_ABORT;
56
57 pc_adjusted = pc - (is_activation ? 0 : 1);
58
59 module = dwfl_addrmodule(c->dwfl, pc_adjusted);
60 if (module) {
61 Dwarf_Die *s, *cudie;
62 int n;
63
64 cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
65 if (cudie) {
66 n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
67 for (s = scopes; s < scopes + n; s++) {
68 if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
69 Dwarf_Attribute *a, space;
70
71 a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
72 if (!a)
73 a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
74 if (a)
75 symbol = dwarf_formstring(a);
76 if (!symbol)
77 symbol = dwarf_diename(s);
78
79 if (symbol)
80 break;
81 }
82 }
83 }
84
85 if (!symbol)
86 symbol = dwfl_module_addrname(module, pc_adjusted);
87
88 fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
89 }
90
91 fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname));
92 c->n_frame ++;
93
94 return DWARF_CB_OK;
95 }
96
97 static int thread_callback(Dwfl_Thread *thread, void *userdata) {
98 struct stack_context *c = userdata;
99 pid_t tid;
100
101 assert(thread);
102 assert(c);
103
104 if (c->n_thread >= THREADS_MAX)
105 return DWARF_CB_ABORT;
106
107 if (c->n_thread != 0)
108 fputc('\n', c->f);
109
110 c->n_frame = 0;
111
112 tid = dwfl_thread_tid(thread);
113 fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
114
115 if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
116 return DWARF_CB_ABORT;
117
118 c->n_thread ++;
119
120 return DWARF_CB_OK;
121 }
122
123 int coredump_make_stack_trace(int fd, const char *executable, char **ret) {
124
125 static const Dwfl_Callbacks callbacks = {
126 .find_elf = dwfl_build_id_find_elf,
127 .find_debuginfo = dwfl_standard_find_debuginfo,
128 };
129
130 struct stack_context c = {};
131 char *buf = NULL;
132 size_t sz = 0;
133 int r;
134
135 assert(fd >= 0);
136 assert(ret);
137
138 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
139 return -errno;
140
141 c.f = open_memstream(&buf, &sz);
142 if (!c.f)
143 return -ENOMEM;
144
145 elf_version(EV_CURRENT);
146
147 c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
148 if (!c.elf) {
149 r = -EINVAL;
150 goto finish;
151 }
152
153 c.dwfl = dwfl_begin(&callbacks);
154 if (!c.dwfl) {
155 r = -EINVAL;
156 goto finish;
157 }
158
159 if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
160 r = -EINVAL;
161 goto finish;
162 }
163
164 if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
165 r = -EINVAL;
166 goto finish;
167 }
168
169 if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
170 r = -EINVAL;
171 goto finish;
172 }
173
174 if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
175 r = -EINVAL;
176 goto finish;
177 }
178
179 fclose(c.f);
180 c.f = NULL;
181
182 *ret = buf;
183 buf = NULL;
184
185 r = 0;
186
187 finish:
188 if (c.dwfl)
189 dwfl_end(c.dwfl);
190
191 if (c.elf)
192 elf_end(c.elf);
193
194 if (c.f)
195 fclose(c.f);
196
197 free(buf);
198
199 return r;
200 }