chiark / gitweb /
Quote unit names in suggested systemctl commandlines
[elogind.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 }