1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <elfutils/libdwfl.h>
23 #include <stdio_ext.h>
25 #include "alloc-util.h"
27 #include "format-util.h"
29 #include "stacktrace.h"
30 #include "string-util.h"
34 #define THREADS_MAX 64
36 struct stack_context
{
44 static int frame_callback(Dwfl_Frame
*frame
, void *userdata
) {
45 struct stack_context
*c
= userdata
;
46 Dwarf_Addr pc
, pc_adjusted
, bias
= 0;
47 _cleanup_free_ Dwarf_Die
*scopes
= NULL
;
48 const char *fname
= NULL
, *symbol
= NULL
;
55 if (c
->n_frame
>= FRAMES_MAX
)
56 return DWARF_CB_ABORT
;
58 if (!dwfl_frame_pc(frame
, &pc
, &is_activation
))
59 return DWARF_CB_ABORT
;
61 pc_adjusted
= pc
- (is_activation
? 0 : 1);
63 module
= dwfl_addrmodule(c
->dwfl
, pc_adjusted
);
68 cudie
= dwfl_module_addrdie(module
, pc_adjusted
, &bias
);
70 n
= dwarf_getscopes(cudie
, pc_adjusted
- bias
, &scopes
);
71 for (s
= scopes
; s
< scopes
+ n
; s
++) {
72 if (IN_SET(dwarf_tag(s
), DW_TAG_subprogram
, DW_TAG_inlined_subroutine
, DW_TAG_entry_point
)) {
73 Dwarf_Attribute
*a
, space
;
75 a
= dwarf_attr_integrate(s
, DW_AT_MIPS_linkage_name
, &space
);
77 a
= dwarf_attr_integrate(s
, DW_AT_linkage_name
, &space
);
79 symbol
= dwarf_formstring(a
);
81 symbol
= dwarf_diename(s
);
90 symbol
= dwfl_module_addrname(module
, pc_adjusted
);
92 fname
= dwfl_module_info(module
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
95 fprintf(c
->f
, "#%-2u 0x%016" PRIx64
" %s (%s)\n", c
->n_frame
, (uint64_t) pc
, strna(symbol
), strna(fname
));
101 static int thread_callback(Dwfl_Thread
*thread
, void *userdata
) {
102 struct stack_context
*c
= userdata
;
108 if (c
->n_thread
>= THREADS_MAX
)
109 return DWARF_CB_ABORT
;
111 if (c
->n_thread
!= 0)
116 tid
= dwfl_thread_tid(thread
);
117 fprintf(c
->f
, "Stack trace of thread " PID_FMT
":\n", tid
);
119 if (dwfl_thread_getframes(thread
, frame_callback
, c
) < 0)
120 return DWARF_CB_ABORT
;
127 int coredump_make_stack_trace(int fd
, const char *executable
, char **ret
) {
129 static const Dwfl_Callbacks callbacks
= {
130 .find_elf
= dwfl_build_id_find_elf
,
131 .find_debuginfo
= dwfl_standard_find_debuginfo
,
134 struct stack_context c
= {};
142 if (lseek(fd
, 0, SEEK_SET
) == (off_t
) -1)
145 c
.f
= open_memstream(&buf
, &sz
);
149 (void) __fsetlocking(c
.f
, FSETLOCKING_BYCALLER
);
151 elf_version(EV_CURRENT
);
153 c
.elf
= elf_begin(fd
, ELF_C_READ_MMAP
, NULL
);
159 c
.dwfl
= dwfl_begin(&callbacks
);
165 if (dwfl_core_file_report(c
.dwfl
, c
.elf
, executable
) < 0) {
170 if (dwfl_report_end(c
.dwfl
, NULL
, NULL
) != 0) {
175 if (dwfl_core_file_attach(c
.dwfl
, c
.elf
) < 0) {
180 if (dwfl_getthreads(c
.dwfl
, thread_callback
, &c
) < 0) {
185 c
.f
= safe_fclose(c
.f
);