ATLAS Offline Software
Loading...
Searching...
No Matches
UnwindBacktrace.cxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
3 */
10
11
13
14
15#ifdef HAVE_LINUX_UNWIND_BACKTRACE
16
17
19#include <atomic>
20#include <unwind.h>
21#include <stdio.h>
22#include <malloc.h>
23
24
25// A couple declarations from libgcc that are not in unwind.h.
26extern "C" {
27 const void* _Unwind_Find_FDE (void*, void*);
28 void __register_frame_info (const void*, void*);
29 void __deregister_frame_info (const void*);
30}
31
32
33namespace CxxUtils {
34
35
36typedef int sword __attribute__ ((mode (SI)));
37typedef unsigned int uword __attribute__ ((mode (SI)));
38using ubyte = unsigned char;
39
40
41// Some DWARF definitions that we need.
42enum
43 {
44 DW_EH_PE_absptr = 0x00
45 };
46
47// Stack unwinder instruction opcodes (see DWARF docs).
48enum
49 {
50 DW_CFA_advance_loc = 0x40,
51 DW_CFA_offset = 0x80,
52 DW_CFA_restore = 0xc0,
53 DW_CFA_extended = 0,
54
55 DW_CFA_nop = 0x00,
56 DW_CFA_set_loc = 0x01,
57 DW_CFA_advance_loc1 = 0x02,
58 DW_CFA_advance_loc2 = 0x03,
59 DW_CFA_advance_loc4 = 0x04,
60 DW_CFA_offset_extended = 0x05,
61 DW_CFA_restore_extended = 0x06,
62 DW_CFA_undefined = 0x07,
63 DW_CFA_same_value = 0x08,
64 DW_CFA_register = 0x09,
65 DW_CFA_remember_state = 0x0a,
66 DW_CFA_restore_state = 0x0b,
67 DW_CFA_def_cfa = 0x0c,
68 DW_CFA_def_cfa_register = 0x0d,
69
70 DW_CFA_def_cfa_offset = 0x0e,
71 DW_CFA_def_cfa_expression = 0x0f,
72 DW_CFA_expression = 0x10,
73 DW_CFA_offset_extended_sf = 0x11,
74 DW_CFA_def_cfa_sf = 0x12,
75 DW_CFA_def_cfa_offset_sf = 0x13,
76 DW_CFA_val_offset = 0x14,
77 DW_CFA_val_offset_sf = 0x15,
78 DW_CFA_val_expression = 0x16,
79
80 DW_CFA_low_user = 0x1c,
81 DW_CFA_MIPS_advance_loc8 = 0x1d,
82 DW_CFA_GNU_window_save = 0x2d,
83 DW_CFA_GNU_args_size = 0x2e,
84 DW_CFA_GNU_negative_offset_extended = 0x2f,
85 DW_CFA_high_user = 0x3f
86 };
87
88
89// DWARF codes for x86_64 registers (see the ABI doc).
90enum Registers
91{
92 REG_RAX = 0,
93 REG_RDX = 1,
94 REG_RCX = 2,
95 REG_RBX = 3,
96 REG_RSI = 4,
97 REG_RDI = 5,
98 REG_RBP = 6,
99 REG_RSP = 7,
100 REG_R8 = 8,
101 REG_R9 = 9,
102 REG_R10 = 10,
103 REG_R11 = 11,
104 REG_R12 = 12,
105 REG_R13 = 13,
106 REG_R14 = 14,
107 REG_R15 = 15,
108 REG_RA = 16 // return address pseudo-register
109};
110
111
114struct SynthCIE
115{
116 uword length;
117 sword CIE_id = 0;
118 ubyte version = 4;
119 unsigned char augmentation[1] = {0};
120 ubyte address_size = sizeof(void*);
121 ubyte segment_size = 0;
122 ubyte code_alignment_factor = 1;
123 ubyte data_alignment_factor = 0x78; // -8 in LEB128 encoding
124 ubyte return_address_register = REG_RA;
125 // Stack unwinding instructions.
126 // This is the default unwinding prolog generated by gas on x86_64,
127 // describing the state of things at the start of a function.
128 // We use this since we're assuming that we've called to bad code
129 // and crashed promptly.
130 // The CFA is RSP+8, and the RA is then -8(CFA) (eg, (SP)).
131 // We get -8 because the 1 below is multiplied by data_alignment_factor.
132 ubyte insns[9] = {DW_CFA_def_cfa, REG_RSP, 8,
133 DW_CFA_offset + REG_RA, 1,
134 DW_CFA_nop,DW_CFA_nop,DW_CFA_nop,DW_CFA_nop};
135} __attribute__ ((packed, aligned (__alignof__ (void *))));
136
138struct SynthFDE
139{
140 uword length;
141 sword CIE_delta;
142 void* pc_begin;
143 uintptr_t pc_range;
144 //ubyte insns[8] = {DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop, DW_CFA_nop};
145} __attribute__ ((packed, aligned (__alignof__ (void *))));
146
148struct SynthFDEVector
149{
150 const void* orig_data = nullptr;
151 size_t count = 0;
152 const struct SynthFDE* array[1] = {nullptr};
153};
154
156struct SynthObject
157{
158 void* pc_begin;
159 void* tbase;
160 void* dbase;
161 union {
162 const SynthFDE* single;
163 SynthFDEVector* sort;
164 } u;
165 struct {
166 unsigned long sorted : 1;
167 unsigned long from_aray : 1;
168 unsigned long mixed_encoding : 1;
169 unsigned long encoding : 8;
170 unsigned long count : 21;
171 } b;
172 struct object* next;
173 void* pad;
174} __attribute__ ((packed, aligned (__alignof__ (void *))));
175
180struct SynthData
181{
182 SynthCIE cie;
183 SynthFDE fde;
184 uword sentinel;
185 uword padding;
186 SynthObject ob;
187 SynthFDEVector* vec;
188 SynthData() : vec (reinterpret_cast<SynthFDEVector*>(malloc (sizeof (SynthFDEVector)))) {}
189 ~SynthData() { if (vec) free (vec); }
190 SynthData (const SynthData&) = delete;
191 SynthData& operator= (const SynthData&) = delete;
192} __attribute__ ((packed, aligned (__alignof__ (void *))));
193
194
195// Statically-allocated FDE data to inject.
196static SynthData synthData ATLAS_THREAD_SAFE;
197
198
209bool tryUnwindFixup (_Unwind_Ptr addr)
210{
211 // Only allow going past this point once.
212 static std::atomic<int> fixed = 0;
213 int exp = 0;
214 if (!fixed.compare_exchange_strong (exp, 1)) {
215 return false;
216 }
217
218 // Initialize synthetic CIE/FDE data.
219 SynthData& data = synthData;
220 data.cie.length = sizeof (data.cie) - sizeof (data.cie.length);
221 data.fde.length = sizeof (data.fde) - sizeof (data.fde.length);
222 // cppcheck-suppress comparePointers
223 data.fde.CIE_delta = reinterpret_cast<char*>(&data.fde.CIE_delta) - reinterpret_cast<char*>(&data.cie.length);
224 data.fde.pc_begin = (void*)addr;
225 data.fde.pc_range = 8;
226 data.sentinel = 0;
227
228 // Register it with libgcc.
229 __register_frame_info (&data.fde, &data.ob);
230
231 // The first time we try to use the new FDE, libgcc will try to allocate
232 // memory for the sorted FDE vector. But we want to avoid allocating memory
233 // here. We can do this by diddling the internal libgcc structure to point
234 // to a pre-allocated vector object. But this is inherently racy: we
235 // should be holding the libgcc lock when doing this, but that's not exported
236 // from the library. We make do by initializing the fields in an order
237 // that should be safe (enforced by memory fences) and hope for the best.
238 std::atomic_thread_fence (std::memory_order_seq_cst);
239 data.vec->orig_data = &data.fde;
240 data.vec->count = 1;
241 data.vec->array[0] = &data.fde;
242 std::atomic_thread_fence (std::memory_order_seq_cst);
243 data.ob.u.sort = data.vec;
244 data.vec = nullptr;
245 data.ob.b.encoding = DW_EH_PE_absptr;
246 data.ob.pc_begin = data.fde.pc_begin;
247 data.ob.b.count = 1;
248 std::atomic_thread_fence (std::memory_order_seq_cst);
249 data.ob.b.sorted = 1;
250 data.ob.pad = 0;
251
252 return true;
253}
254
255
256//************************************************************************
257
258
260struct dwarf_eh_bases
261{
262 void *tbase;
263 void *dbase;
264 void *func;
265};
266
267
269struct UnwindBacktraceData
270{
271 static constexpr unsigned MAX_DEPTH = 128;
272 unsigned naddr = 0;
273 _Unwind_Ptr addrs[MAX_DEPTH] = {0};
274 bool sawBad = false;
275};
276
277
284_Unwind_Reason_Code
285unwindBacktraceCallback (_Unwind_Context *ctx, void *data_in)
286{
287 // Record this IP.
288 auto& data = *reinterpret_cast<UnwindBacktraceData*> (data_in);
289 _Unwind_Ptr ip = _Unwind_GetIP (ctx);
290 data.addrs[data.naddr++] = ip;
291
292 if (ip == 0 && !data.sawBad) {
293 // Is this the end of the stack, or a bad frame? Try to guess.
294 _Unwind_Word cfa = _Unwind_GetCFA (ctx);
295 if (cfa) {
296 void* x = *reinterpret_cast<void**>(cfa);
297 if (x) {
298 dwarf_eh_bases bases;
299 if (_Unwind_Find_FDE (x, &bases) != nullptr) {
300 data.sawBad = false;
301 return _URC_END_OF_STACK;
302 }
303 }
304 }
305 }
306 else {
307 // If we can't find the FDE for this address, set a flag and abort.
308 dwarf_eh_bases bases;
309 if (!data.sawBad && _Unwind_Find_FDE (reinterpret_cast<void*>(ip), &bases) == nullptr)
310 {
311 data.sawBad = true;
312 return _URC_END_OF_STACK;
313 }
314 }
315
316 // Stop if we've hit the end of our buffer.
317 if (data.naddr == UnwindBacktraceData::MAX_DEPTH)
318 return _URC_END_OF_STACK;
319
320 return _URC_NO_REASON;
321}
322
323
330void backtraceByUnwind (backtraceLineFn* lineFn, IOFD fd)
331{
332 // Do the backtrace once.
333 UnwindBacktraceData data;
334 _Unwind_Backtrace (unwindBacktraceCallback, &data);
335
336 // If we saw a bad entry, then try to fix it up.
337 // If that worked, retry the backtrace.
338 if (data.sawBad && tryUnwindFixup (data.addrs[data.naddr-1])) {
339 data.naddr = 0;
340 _Unwind_Backtrace (unwindBacktraceCallback, &data);
341 }
342
343 // Clean a null entry from the end of the list.
344 if (data.naddr > 0 && data.addrs[data.naddr-1] == 0) {
345 --data.naddr;
346 }
347
348 for (unsigned i = 0; i < data.naddr; i++) {
349 lineFn (fd, data.addrs[i]);
350 }
351}
352
353
354} // namespace CxxUtils
355
356
357namespace {
358
359
360// Make sure there's a block on the free list of the internal btree
361// libgcc uses to keep track of registrations, so we won't have
362// to call malloc later.
363bool initUnwind()
364{
365 CxxUtils::SynthData data;
366 data.cie.length = sizeof (data.cie) - sizeof (data.cie.length);
367 data.fde.length = sizeof (data.fde) - sizeof (data.fde.length);
368 // cppcheck-suppress comparePointers
369 data.fde.CIE_delta = reinterpret_cast<char*>(&data.fde.CIE_delta) - reinterpret_cast<char*>(&data.cie.length);
370 data.fde.pc_begin = (void*)&data;
371 data.fde.pc_range = 8;
372 data.sentinel = 0;
373
374 __register_frame_info (&data.fde, &data.ob);
375 __deregister_frame_info (&data.fde);
376 return true;
377}
378const static bool initUnwindFlag = initUnwind();
379
380
381} // anonymous namespace
382
383
384#endif
std::vector< size_t > vec
Hacked backtrace that can go past a bad stack frame.
double length(const pvec &v)
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
int IOFD
Type the system uses for channel descriptors.
Definition SealCommon.h:27
__attribute__((always_inline)) inline uint16_t TileCalibDrawerBase
#define x
Define macros for attributes used to control the static checker.
#define ATLAS_THREAD_SAFE
int count(std::string s, const std::string &regx)
count how many occurances of a regx are in a string
Definition hcg.cxx:146
std::vector< typename R::value_type > sorted(const R &r, PROJ proj={})
Helper function to create a sorted vector from an unsorted range.
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77