1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | // |
8 | // Implements unw_* functions from <libunwind.h> |
9 | // |
10 | //===----------------------------------------------------------------------===// |
11 | |
12 | #include <libunwind.h> |
13 | |
14 | #include "config.h" |
15 | #include "libunwind_ext.h" |
16 | |
17 | #include <stdlib.h> |
18 | |
19 | // Define the __has_feature extension for compilers that do not support it so |
20 | // that we can later check for the presence of ASan in a compiler-neutral way. |
21 | #if !defined(__has_feature) |
22 | #define __has_feature(feature) 0 |
23 | #endif |
24 | |
25 | #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) |
26 | #include <sanitizer/asan_interface.h> |
27 | #endif |
28 | |
29 | #if !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__) |
30 | #include "AddressSpace.hpp" |
31 | #include "UnwindCursor.hpp" |
32 | |
33 | using namespace libunwind; |
34 | |
35 | /// internal object to represent this processes address space |
36 | LocalAddressSpace LocalAddressSpace::sThisAddressSpace; |
37 | |
38 | _LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space = |
39 | (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace; |
40 | |
41 | /// Create a cursor of a thread in this process given 'context' recorded by |
42 | /// __unw_getcontext(). |
43 | _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, |
44 | unw_context_t *context) { |
45 | _LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)" , |
46 | static_cast<void *>(cursor), |
47 | static_cast<void *>(context)); |
48 | #if defined(__i386__) |
49 | # define REGISTER_KIND Registers_x86 |
50 | #elif defined(__x86_64__) |
51 | # define REGISTER_KIND Registers_x86_64 |
52 | #elif defined(__powerpc64__) |
53 | # define REGISTER_KIND Registers_ppc64 |
54 | #elif defined(__powerpc__) |
55 | # define REGISTER_KIND Registers_ppc |
56 | #elif defined(__aarch64__) |
57 | # define REGISTER_KIND Registers_arm64 |
58 | #elif defined(__arm__) |
59 | # define REGISTER_KIND Registers_arm |
60 | #elif defined(__or1k__) |
61 | # define REGISTER_KIND Registers_or1k |
62 | #elif defined(__hexagon__) |
63 | # define REGISTER_KIND Registers_hexagon |
64 | #elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 |
65 | # define REGISTER_KIND Registers_mips_o32 |
66 | #elif defined(__mips64) |
67 | # define REGISTER_KIND Registers_mips_newabi |
68 | #elif defined(__mips__) |
69 | # warning The MIPS architecture is not supported with this ABI and environment! |
70 | #elif defined(__sparc__) && defined(__arch64__) |
71 | #define REGISTER_KIND Registers_sparc64 |
72 | #elif defined(__sparc__) |
73 | # define REGISTER_KIND Registers_sparc |
74 | #elif defined(__riscv) |
75 | # define REGISTER_KIND Registers_riscv |
76 | #elif defined(__ve__) |
77 | # define REGISTER_KIND Registers_ve |
78 | #elif defined(__s390x__) |
79 | # define REGISTER_KIND Registers_s390x |
80 | #elif defined(__loongarch__) && __loongarch_grlen == 64 |
81 | #define REGISTER_KIND Registers_loongarch |
82 | #else |
83 | # error Architecture not supported |
84 | #endif |
85 | // Use "placement new" to allocate UnwindCursor in the cursor buffer. |
86 | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, REGISTER_KIND> *>(cursor)) |
87 | UnwindCursor<LocalAddressSpace, REGISTER_KIND>( |
88 | context, LocalAddressSpace::sThisAddressSpace); |
89 | #undef REGISTER_KIND |
90 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
91 | co->setInfoBasedOnIPRegister(); |
92 | |
93 | return UNW_ESUCCESS; |
94 | } |
95 | _LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local) |
96 | |
97 | /// Get value of specified register at cursor position in stack frame. |
98 | _LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, |
99 | unw_word_t *value) { |
100 | _LIBUNWIND_TRACE_API("__unw_get_reg(cursor=%p, regNum=%d, &value=%p)" , |
101 | static_cast<void *>(cursor), regNum, |
102 | static_cast<void *>(value)); |
103 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
104 | if (co->validReg(regNum)) { |
105 | *value = co->getReg(regNum); |
106 | return UNW_ESUCCESS; |
107 | } |
108 | return UNW_EBADREG; |
109 | } |
110 | _LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg) |
111 | |
112 | /// Set value of specified register at cursor position in stack frame. |
113 | _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, |
114 | unw_word_t value) { |
115 | _LIBUNWIND_TRACE_API("__unw_set_reg(cursor=%p, regNum=%d, value=0x%" PRIxPTR |
116 | ")" , |
117 | static_cast<void *>(cursor), regNum, value); |
118 | typedef LocalAddressSpace::pint_t pint_t; |
119 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
120 | if (co->validReg(regNum)) { |
121 | co->setReg(regNum, (pint_t)value); |
122 | // special case altering IP to re-find info (being called by personality |
123 | // function) |
124 | if (regNum == UNW_REG_IP) { |
125 | unw_proc_info_t info; |
126 | // First, get the FDE for the old location and then update it. |
127 | co->getInfo(&info); |
128 | co->setInfoBasedOnIPRegister(false); |
129 | // If the original call expects stack adjustment, perform this now. |
130 | // Normal frame unwinding would have included the offset already in the |
131 | // CFA computation. |
132 | // Note: for PA-RISC and other platforms where the stack grows up, |
133 | // this should actually be - info.gp. LLVM doesn't currently support |
134 | // any such platforms and Clang doesn't export a macro for them. |
135 | if (info.gp) |
136 | co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp); |
137 | } |
138 | return UNW_ESUCCESS; |
139 | } |
140 | return UNW_EBADREG; |
141 | } |
142 | _LIBUNWIND_WEAK_ALIAS(__unw_set_reg, unw_set_reg) |
143 | |
144 | /// Get value of specified float register at cursor position in stack frame. |
145 | _LIBUNWIND_HIDDEN int __unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, |
146 | unw_fpreg_t *value) { |
147 | _LIBUNWIND_TRACE_API("__unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)" , |
148 | static_cast<void *>(cursor), regNum, |
149 | static_cast<void *>(value)); |
150 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
151 | if (co->validFloatReg(regNum)) { |
152 | *value = co->getFloatReg(regNum); |
153 | return UNW_ESUCCESS; |
154 | } |
155 | return UNW_EBADREG; |
156 | } |
157 | _LIBUNWIND_WEAK_ALIAS(__unw_get_fpreg, unw_get_fpreg) |
158 | |
159 | /// Set value of specified float register at cursor position in stack frame. |
160 | _LIBUNWIND_HIDDEN int __unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, |
161 | unw_fpreg_t value) { |
162 | #if defined(_LIBUNWIND_ARM_EHABI) |
163 | _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)" , |
164 | static_cast<void *>(cursor), regNum, value); |
165 | #else |
166 | _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)" , |
167 | static_cast<void *>(cursor), regNum, value); |
168 | #endif |
169 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
170 | if (co->validFloatReg(regNum)) { |
171 | co->setFloatReg(regNum, value); |
172 | return UNW_ESUCCESS; |
173 | } |
174 | return UNW_EBADREG; |
175 | } |
176 | _LIBUNWIND_WEAK_ALIAS(__unw_set_fpreg, unw_set_fpreg) |
177 | |
178 | /// Move cursor to next frame. |
179 | _LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) { |
180 | _LIBUNWIND_TRACE_API("__unw_step(cursor=%p)" , static_cast<void *>(cursor)); |
181 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
182 | return co->step(); |
183 | } |
184 | _LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step) |
185 | |
186 | // Move cursor to next frame and for stage2 of unwinding. |
187 | // This resets MTE tags of tagged frames to zero. |
188 | extern "C" _LIBUNWIND_HIDDEN int __unw_step_stage2(unw_cursor_t *cursor) { |
189 | _LIBUNWIND_TRACE_API("__unw_step_stage2(cursor=%p)" , |
190 | static_cast<void *>(cursor)); |
191 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
192 | return co->step(true); |
193 | } |
194 | |
195 | /// Get unwind info at cursor position in stack frame. |
196 | _LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor, |
197 | unw_proc_info_t *info) { |
198 | _LIBUNWIND_TRACE_API("__unw_get_proc_info(cursor=%p, &info=%p)" , |
199 | static_cast<void *>(cursor), static_cast<void *>(info)); |
200 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
201 | co->getInfo(info); |
202 | if (info->end_ip == 0) |
203 | return UNW_ENOINFO; |
204 | return UNW_ESUCCESS; |
205 | } |
206 | _LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info) |
207 | |
208 | /// Resume execution at cursor position (aka longjump). |
209 | _LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) { |
210 | _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)" , static_cast<void *>(cursor)); |
211 | #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) |
212 | // Inform the ASan runtime that now might be a good time to clean stuff up. |
213 | __asan_handle_no_return(); |
214 | #endif |
215 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
216 | co->jumpto(); |
217 | return UNW_EUNSPEC; |
218 | } |
219 | _LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume) |
220 | |
221 | /// Get name of function at cursor position in stack frame. |
222 | _LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf, |
223 | size_t bufLen, unw_word_t *offset) { |
224 | _LIBUNWIND_TRACE_API("__unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)" , |
225 | static_cast<void *>(cursor), static_cast<void *>(buf), |
226 | static_cast<unsigned long>(bufLen)); |
227 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
228 | if (co->getFunctionName(buf, bufLen, offset)) |
229 | return UNW_ESUCCESS; |
230 | return UNW_EUNSPEC; |
231 | } |
232 | _LIBUNWIND_WEAK_ALIAS(__unw_get_proc_name, unw_get_proc_name) |
233 | |
234 | /// Checks if a register is a floating-point register. |
235 | _LIBUNWIND_HIDDEN int __unw_is_fpreg(unw_cursor_t *cursor, |
236 | unw_regnum_t regNum) { |
237 | _LIBUNWIND_TRACE_API("__unw_is_fpreg(cursor=%p, regNum=%d)" , |
238 | static_cast<void *>(cursor), regNum); |
239 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
240 | return co->validFloatReg(regNum); |
241 | } |
242 | _LIBUNWIND_WEAK_ALIAS(__unw_is_fpreg, unw_is_fpreg) |
243 | |
244 | /// Checks if a register is a floating-point register. |
245 | _LIBUNWIND_HIDDEN const char *__unw_regname(unw_cursor_t *cursor, |
246 | unw_regnum_t regNum) { |
247 | _LIBUNWIND_TRACE_API("__unw_regname(cursor=%p, regNum=%d)" , |
248 | static_cast<void *>(cursor), regNum); |
249 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
250 | return co->getRegisterName(regNum); |
251 | } |
252 | _LIBUNWIND_WEAK_ALIAS(__unw_regname, unw_regname) |
253 | |
254 | /// Checks if current frame is signal trampoline. |
255 | _LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) { |
256 | _LIBUNWIND_TRACE_API("__unw_is_signal_frame(cursor=%p)" , |
257 | static_cast<void *>(cursor)); |
258 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
259 | return co->isSignalFrame(); |
260 | } |
261 | _LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame) |
262 | |
263 | #ifdef _AIX |
264 | _LIBUNWIND_EXPORT uintptr_t __unw_get_data_rel_base(unw_cursor_t *cursor) { |
265 | _LIBUNWIND_TRACE_API("unw_get_data_rel_base(cursor=%p)" , |
266 | static_cast<void *>(cursor)); |
267 | AbstractUnwindCursor *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
268 | return co->getDataRelBase(); |
269 | } |
270 | _LIBUNWIND_WEAK_ALIAS(__unw_get_data_rel_base, unw_get_data_rel_base) |
271 | #endif |
272 | |
273 | #ifdef __arm__ |
274 | // Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD |
275 | _LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) { |
276 | _LIBUNWIND_TRACE_API("__unw_get_fpreg_save_vfp_as_X(cursor=%p)" , |
277 | static_cast<void *>(cursor)); |
278 | AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; |
279 | return co->saveVFPAsX(); |
280 | } |
281 | _LIBUNWIND_WEAK_ALIAS(__unw_save_vfp_as_X, unw_save_vfp_as_X) |
282 | #endif |
283 | |
284 | |
285 | #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) |
286 | /// SPI: walks cached DWARF entries |
287 | _LIBUNWIND_HIDDEN void __unw_iterate_dwarf_unwind_cache(void (*func)( |
288 | unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { |
289 | _LIBUNWIND_TRACE_API("__unw_iterate_dwarf_unwind_cache(func=%p)" , |
290 | reinterpret_cast<void *>(func)); |
291 | DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func); |
292 | } |
293 | _LIBUNWIND_WEAK_ALIAS(__unw_iterate_dwarf_unwind_cache, |
294 | unw_iterate_dwarf_unwind_cache) |
295 | |
296 | /// IPI: for __register_frame() |
297 | void __unw_add_dynamic_fde(unw_word_t fde) { |
298 | CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; |
299 | CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; |
300 | const char *message = CFI_Parser<LocalAddressSpace>::decodeFDE( |
301 | addressSpace&: LocalAddressSpace::sThisAddressSpace, |
302 | fdeStart: (LocalAddressSpace::pint_t) fde, fdeInfo: &fdeInfo, cieInfo: &cieInfo); |
303 | if (message == NULL) { |
304 | // dynamically registered FDEs don't have a mach_header group they are in. |
305 | // Use fde as mh_group |
306 | unw_word_t mh_group = fdeInfo.fdeStart; |
307 | DwarfFDECache<LocalAddressSpace>::add(mh: (LocalAddressSpace::pint_t)mh_group, |
308 | ip_start: fdeInfo.pcStart, ip_end: fdeInfo.pcEnd, |
309 | fde: fdeInfo.fdeStart); |
310 | } else { |
311 | _LIBUNWIND_DEBUG_LOG("__unw_add_dynamic_fde: bad fde: %s" , message); |
312 | } |
313 | } |
314 | |
315 | /// IPI: for __deregister_frame() |
316 | void __unw_remove_dynamic_fde(unw_word_t fde) { |
317 | // fde is own mh_group |
318 | DwarfFDECache<LocalAddressSpace>::removeAllIn(mh: (LocalAddressSpace::pint_t)fde); |
319 | } |
320 | |
321 | void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) { |
322 | // The eh_frame section start serves as the mh_group |
323 | unw_word_t mh_group = eh_frame_start; |
324 | CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo; |
325 | CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo; |
326 | auto p = (LocalAddressSpace::pint_t)eh_frame_start; |
327 | while (LocalAddressSpace::sThisAddressSpace.get32(addr: p)) { |
328 | if (CFI_Parser<LocalAddressSpace>::decodeFDE( |
329 | addressSpace&: LocalAddressSpace::sThisAddressSpace, fdeStart: p, fdeInfo: &fdeInfo, cieInfo: &cieInfo, |
330 | useCIEInfo: true) == NULL) { |
331 | DwarfFDECache<LocalAddressSpace>::add(mh: (LocalAddressSpace::pint_t)mh_group, |
332 | ip_start: fdeInfo.pcStart, ip_end: fdeInfo.pcEnd, |
333 | fde: fdeInfo.fdeStart); |
334 | p += fdeInfo.fdeLength; |
335 | } else if (CFI_Parser<LocalAddressSpace>::parseCIE( |
336 | addressSpace&: LocalAddressSpace::sThisAddressSpace, cie: p, cieInfo: &cieInfo) == NULL) { |
337 | p += cieInfo.cieLength; |
338 | } else |
339 | return; |
340 | } |
341 | } |
342 | |
343 | void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) { |
344 | // The eh_frame section start serves as the mh_group |
345 | DwarfFDECache<LocalAddressSpace>::removeAllIn( |
346 | mh: (LocalAddressSpace::pint_t)eh_frame_start); |
347 | } |
348 | |
349 | #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) |
350 | #endif // !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__) |
351 | |
352 | #ifdef __APPLE__ |
353 | |
354 | namespace libunwind { |
355 | |
356 | static constexpr size_t MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS = 8; |
357 | |
358 | static RWMutex findDynamicUnwindSectionsLock; |
359 | static size_t numDynamicUnwindSectionsFinders = 0; |
360 | static unw_find_dynamic_unwind_sections |
361 | dynamicUnwindSectionsFinders[MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS] = {0}; |
362 | |
363 | bool findDynamicUnwindSections(void *addr, unw_dynamic_unwind_sections *info) { |
364 | bool found = false; |
365 | findDynamicUnwindSectionsLock.lock_shared(); |
366 | for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) { |
367 | if (dynamicUnwindSectionsFinders[i]((unw_word_t)addr, info)) { |
368 | found = true; |
369 | break; |
370 | } |
371 | } |
372 | findDynamicUnwindSectionsLock.unlock_shared(); |
373 | return found; |
374 | } |
375 | |
376 | } // namespace libunwind |
377 | |
378 | int __unw_add_find_dynamic_unwind_sections( |
379 | unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) { |
380 | findDynamicUnwindSectionsLock.lock(); |
381 | |
382 | // Check that we have enough space... |
383 | if (numDynamicUnwindSectionsFinders == MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS) { |
384 | findDynamicUnwindSectionsLock.unlock(); |
385 | return UNW_ENOMEM; |
386 | } |
387 | |
388 | // Check for value already present... |
389 | for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) { |
390 | if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) { |
391 | findDynamicUnwindSectionsLock.unlock(); |
392 | return UNW_EINVAL; |
393 | } |
394 | } |
395 | |
396 | // Success -- add callback entry. |
397 | dynamicUnwindSectionsFinders[numDynamicUnwindSectionsFinders++] = |
398 | find_dynamic_unwind_sections; |
399 | findDynamicUnwindSectionsLock.unlock(); |
400 | |
401 | return UNW_ESUCCESS; |
402 | } |
403 | |
404 | int __unw_remove_find_dynamic_unwind_sections( |
405 | unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) { |
406 | findDynamicUnwindSectionsLock.lock(); |
407 | |
408 | // Find index to remove. |
409 | size_t finderIdx = numDynamicUnwindSectionsFinders; |
410 | for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) { |
411 | if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) { |
412 | finderIdx = i; |
413 | break; |
414 | } |
415 | } |
416 | |
417 | // If no such registration is present then error out. |
418 | if (finderIdx == numDynamicUnwindSectionsFinders) { |
419 | findDynamicUnwindSectionsLock.unlock(); |
420 | return UNW_EINVAL; |
421 | } |
422 | |
423 | // Remove entry. |
424 | for (size_t i = finderIdx; i != numDynamicUnwindSectionsFinders - 1; ++i) |
425 | dynamicUnwindSectionsFinders[i] = dynamicUnwindSectionsFinders[i + 1]; |
426 | dynamicUnwindSectionsFinders[--numDynamicUnwindSectionsFinders] = nullptr; |
427 | |
428 | findDynamicUnwindSectionsLock.unlock(); |
429 | return UNW_ESUCCESS; |
430 | } |
431 | |
432 | #endif // __APPLE__ |
433 | |
434 | // Add logging hooks in Debug builds only |
435 | #ifndef NDEBUG |
436 | #include <stdlib.h> |
437 | |
438 | _LIBUNWIND_HIDDEN |
439 | bool logAPIs() { |
440 | // do manual lock to avoid use of _cxa_guard_acquire or initializers |
441 | static bool checked = false; |
442 | static bool log = false; |
443 | if (!checked) { |
444 | log = (getenv(name: "LIBUNWIND_PRINT_APIS" ) != NULL); |
445 | checked = true; |
446 | } |
447 | return log; |
448 | } |
449 | |
450 | _LIBUNWIND_HIDDEN |
451 | bool logUnwinding() { |
452 | // do manual lock to avoid use of _cxa_guard_acquire or initializers |
453 | static bool checked = false; |
454 | static bool log = false; |
455 | if (!checked) { |
456 | log = (getenv(name: "LIBUNWIND_PRINT_UNWINDING" ) != NULL); |
457 | checked = true; |
458 | } |
459 | return log; |
460 | } |
461 | |
462 | _LIBUNWIND_HIDDEN |
463 | bool logDWARF() { |
464 | // do manual lock to avoid use of _cxa_guard_acquire or initializers |
465 | static bool checked = false; |
466 | static bool log = false; |
467 | if (!checked) { |
468 | log = (getenv(name: "LIBUNWIND_PRINT_DWARF" ) != NULL); |
469 | checked = true; |
470 | } |
471 | return log; |
472 | } |
473 | |
474 | #endif // NDEBUG |
475 | |
476 | |