• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 12288460532

12 Dec 2024 02:06AM UTC coverage: 91.96% (-0.02%) from 91.975%
12288460532

Pull #1099

github

web-flow
Merge 989e24445 into 72b5e72f5
Pull Request #1099: [Fuzz] Show fuzzy-stack-hash for crashes in debug build.

7 of 18 new or added lines in 2 files covered. (38.89%)

1 existing line in 1 file now uncovered.

63218 of 68745 relevant lines covered (91.96%)

624553.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.44
/src/debug.c
1
//
2
//  Copyright (C) 2021-2022  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#if defined __MINGW32__ || defined __CYGWIN__
19
#define WINVER 0x0A00
20
#define _WIN32_WINNT 0x0A00
21
#include <windows.h>
22
#include <dbghelp.h>
23
#endif
24

25
#include "debug.h"
26
#include "array.h"
27
#include "hash.h"
28
#include "ident.h"
29
#include "lib.h"
30
#include "thread.h"
31

32
#include <stdlib.h>
33
#include <string.h>
34
#include <sys/types.h>
35

36
#if !defined __MINGW32__ && !defined __CYGWIN__
37
#include <unistd.h>
38
#include <fcntl.h>
39
#include <assert.h>
40
#include <dlfcn.h>
41
#include <errno.h>
42
#include <unwind.h>
43
#endif  // !__MINGW32__ && !__CYGWIN__
44

45
#if defined HAVE_LIBDW
46
#include <elfutils/libdw.h>
47
#include <elfutils/libdwfl.h>
48
#include <dwarf.h>
49
#elif defined HAVE_LIBDWARF
50
#if defined HAVE_LIBDWARF_LIBDWARF_H
51
#include <libdwarf/libdwarf.h>
52
#include <libdwarf/dwarf.h>
53
#else  // HAVE_LIBDWARF_LIBDWARF_H
54
#include <libdwarf.h>
55
#include <dwarf.h>
56
#endif  // HAVE_LIBDWARF_LIBDWARF_H
57
#include <libelf.h>
58
#endif
59

60
#ifdef __ARM_EABI_UNWINDER__
61
#define _URC_NORMAL_STOP _URC_END_OF_STACK
62
#endif
63

64
#define MAX_TRACE_DEPTH   25
65
#define MAX_HASH_DEPTH    10
66

67
struct debug_info {
68
   A(debug_frame_t*) frames;
69
   unsigned          skip;
70
};
71

72
////////////////////////////////////////////////////////////////////////////////
73
// Utilities
74

75
typedef struct _di_lru_cache di_lru_cache_t;
76

77
struct _di_lru_cache {
78
   di_lru_cache_t *next;
79
   di_lru_cache_t *prev;
80
   debug_frame_t   frame;
81
};
82

83
#define DI_LRU_SIZE 256
84
STATIC_ASSERT(DI_LRU_SIZE > MAX_TRACE_DEPTH);
85

86
typedef struct _debug_unwinder debug_unwinder_t;
87

88
struct _debug_unwinder {
89
   debug_unwinder_t  *next;
90
   debug_unwind_fn_t  fn;
91
   void              *context;
92
   uintptr_t          start;
93
   uintptr_t          end;
94
};
95

96
static debug_unwinder_t *unwinders = NULL;
97

98
static void di_lru_reuse_frame(debug_frame_t *frame, uintptr_t pc)
7✔
99
{
100
   free((char *)frame->module);
7✔
101
   free((char *)frame->symbol);
7✔
102
   free((char *)frame->srcfile);
7✔
103

104
   for (debug_inline_t *it = frame->inlined; it != NULL; ) {
7✔
105
      debug_inline_t *next = it->next;
×
106
      free((char *)it->symbol);
×
107
      free((char *)it->srcfile);
×
108
      free(it);
×
109
      it = next;
×
110
   }
111

112
   memset(frame, '\0', sizeof(debug_frame_t));
7✔
113

114
   frame->pc = pc;
7✔
115
}
7✔
116

117
static bool di_lru_get(uintptr_t pc, debug_frame_t **pframe)
7✔
118
{
119
   static __thread di_lru_cache_t *lru_cache = NULL;
7✔
120

121
   unsigned size;
7✔
122
   di_lru_cache_t **it;
7✔
123
   for (it = &lru_cache, size = 0;
7✔
124
        *it != NULL && (*it)->frame.pc != pc;
22✔
125
        it = &((*it)->next), size++)
15✔
126
      ;
127

128
   if (*it != NULL && *it == lru_cache) {
7✔
129
      *pframe = &((*it)->frame);
×
130
      return true;
×
131
   }
132
   else if (*it != NULL) {
7✔
133
      di_lru_cache_t *tmp = *it;
×
134
      if ((*it)->next) (*it)->next->prev = (*it)->prev;
×
135
      *it = (*it)->next;
×
136
      tmp->next = lru_cache;
×
137
      tmp->prev = NULL;
×
138
      lru_cache->prev = tmp;
×
139
      lru_cache = tmp;
×
140
      *pframe = &(tmp->frame);
×
141
      return true;
×
142
   }
143
   else if (size < DI_LRU_SIZE) {
7✔
144
      di_lru_cache_t *new = xcalloc(sizeof(di_lru_cache_t));
7✔
145
      new->prev = NULL;
7✔
146
      new->next = lru_cache;
7✔
147
      if (lru_cache) lru_cache->prev = new;
7✔
148
      lru_cache = new;
7✔
149
      di_lru_reuse_frame(&(new->frame), pc);
7✔
150
      *pframe = &(new->frame);
7✔
151
      return false;
7✔
152
   }
153
   else {
154
      di_lru_cache_t *lru = container_of(it, di_lru_cache_t, next);
×
155
      lru->prev->next = NULL;
×
156
      lru->next = lru_cache;
×
157
      lru_cache->prev = lru;
×
158
      lru_cache = lru;
×
159
      di_lru_reuse_frame(&(lru->frame), pc);
×
160
      *pframe = &(lru->frame);
×
161
      return false;
×
162
   }
163
}
164

165
static bool custom_fill_frame(uintptr_t ip, debug_frame_t *frame)
6✔
166
{
167
   for (debug_unwinder_t *uw = unwinders; uw; uw = uw->next) {
6✔
168
      if (ip >= uw->start && ip < uw->end) {
×
169
         (*uw->fn)(ip, frame, uw->context);
×
170
         return true;
×
171
      }
172
   }
173

174
   return false;
175
}
176

177
////////////////////////////////////////////////////////////////////////////////
178
// Libdw backend
179

180
#if defined HAVE_LIBDW
181

182
static void libdw_fill_inlining(uintptr_t biased_ip, Dwarf_Die *fundie,
6✔
183
                                debug_frame_t *frame)
184
{
185
   Dwarf_Die child;
6✔
186
   if (dwarf_child(fundie, &child) == 0) {
6✔
187
      Dwarf_Die *iter = &child;
188
      do {
177✔
189
         if (dwarf_tag(iter) != DW_TAG_inlined_subroutine)
177✔
190
            continue;
177✔
191
         else if (!dwarf_haspc(iter, biased_ip))
×
192
            continue;
×
193

194
         debug_inline_t *inl = xcalloc(sizeof(debug_inline_t));
×
195
         inl->symbol = xstrdup(dwarf_diename(iter));
×
196

197
         Dwarf_Attribute attr;
×
198
         if (dwarf_attr(iter, DW_AT_abstract_origin, &attr) == NULL)
×
199
            continue;
×
200

201
         Dwarf_Die origin;
×
202
         if (dwarf_formref_die(&attr, &origin) == NULL)
×
203
            continue;
×
204

205
         const char *srcfile = dwarf_decl_file(iter);
×
206
         if (srcfile != NULL)
×
207
            inl->srcfile = xstrdup(srcfile);
×
208

209
         if (frame->kind == FRAME_VHDL) {
×
210
            Dwarf_Die *scopes;
×
211
            int n = dwarf_getscopes_die(&origin, &scopes);
×
212
            for (int i = 0; i < n; i++) {
×
213
               if (dwarf_tag(&(scopes[i])) == DW_TAG_module)
×
214
                  inl->vhdl_unit = ident_new(dwarf_diename(&(scopes[i])));
×
215
            }
216
         }
217

218
         Dwarf_Word call_lineno = 0;
×
219
         if (dwarf_attr(iter, DW_AT_call_line, &attr))
×
220
            dwarf_formudata(&attr, &call_lineno);
×
221

222
         Dwarf_Word call_colno = 0;
×
223
         if (dwarf_attr(iter, DW_AT_call_column, &attr))
×
224
            dwarf_formudata(&attr, &call_colno);
×
225

226
         if (frame->inlined) {
×
227
            inl->lineno = frame->inlined->lineno;
×
228
            inl->colno  = frame->inlined->colno;
×
229

230
            frame->inlined->lineno = call_lineno;
×
231
            frame->inlined->colno  = call_colno;
×
232
         }
233
         else {
234
            inl->lineno = frame->lineno;
×
235
            inl->colno  = frame->colno;
×
236

237
            frame->lineno = call_lineno;
×
238
            frame->colno  = call_colno;
×
239
         }
240

241
         inl->next = frame->inlined;
×
242
         frame->inlined = inl;
×
243

244
         libdw_fill_inlining(biased_ip, iter, frame);
×
245
      } while (dwarf_siblingof(iter, iter) == 0);
177✔
246
   }
247
}
6✔
248

249
static void platform_fill_frame(uintptr_t ip, debug_frame_t *frame)
7✔
250
{
251
   static Dwfl *handle = NULL;
7✔
252
   static Dwfl_Module *home = NULL;
7✔
253

254
   if (handle == NULL) {
7✔
255
      static Dwfl_Callbacks callbacks = {
2✔
256
         .find_elf = dwfl_linux_proc_find_elf,
257
         .find_debuginfo = dwfl_standard_find_debuginfo,
258
         .debuginfo_path = NULL
259
      };
260

261
      if ((handle = dwfl_begin(&callbacks)) == NULL)
2✔
262
         fatal("failed to initialise dwfl");
×
263

264
      dwfl_report_begin(handle);
2✔
265
      if (dwfl_linux_proc_report(handle, getpid()) < 0)
2✔
266
         fatal("dwfl_linux_proc_report failed");
×
267
      dwfl_report_end(handle, NULL, NULL);
2✔
268

269
      home = dwfl_addrmodule(handle, (uintptr_t)platform_fill_frame);
2✔
270
   }
271

272
   Dwfl_Module *mod = dwfl_addrmodule(handle, ip);
7✔
273
   frame->kind = (mod == home) ? FRAME_PROG : FRAME_LIB;
7✔
274
   frame->pc   = ip;
7✔
275

276
   const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0);
7✔
277
   const char *sym_name = dwfl_module_addrname(mod, ip);
7✔
278

279
   Dwarf_Addr mod_bias = 0;
7✔
280
   Dwarf_Die *cudie = dwfl_module_addrdie(mod, ip, &mod_bias);
7✔
281

282
   if (cudie == NULL) {
7✔
283
      // Clang does not emit aranges so search each CU
284
      cudie = dwfl_module_nextcu(mod, NULL, &mod_bias);
1✔
285
   }
286

287
   Dwarf_Die *fundie = NULL, *module = NULL, child, child1;
7✔
288
   do {
112✔
289
      if (dwarf_child(cudie, &child) != 0)
112✔
290
         continue;
×
291

292
      Dwarf_Die *iter = &child;
293
      do {
31,597✔
294
         switch (dwarf_tag(iter)) {
31,597✔
295
         case DW_TAG_inlined_subroutine:
13,427✔
296
         case DW_TAG_subprogram:
297
            if (dwarf_haspc(iter, ip - mod_bias)) {
13,427✔
298
               fundie = iter;
6✔
299
               goto found_die_with_ip;
6✔
300
            }
301
            break;
302
         case DW_TAG_module:
×
303
            if (dwarf_child(&child, &child1) == 0) {
×
304
               Dwarf_Die *iter1 = &child1;
305
               do {
×
306
                  switch (dwarf_tag(iter1)) {
×
307
                  case DW_TAG_inlined_subroutine:
×
308
                  case DW_TAG_subprogram:
309
                     if (dwarf_haspc(iter1, ip - mod_bias)) {
×
310
                        module = iter;
×
311
                        fundie = iter1;
×
312
                        goto found_die_with_ip;
×
313
                     }
314
                  }
315
               } while (dwarf_siblingof(iter1, iter1) == 0);
×
316
            }
317
            break;
318
         }
319
      } while (dwarf_siblingof(iter, iter) == 0);
31,591✔
320
   } while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias)));
106✔
321

322
 found_die_with_ip:
7✔
323
   if (cudie != NULL) {
7✔
324
      Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, ip - mod_bias);
6✔
325
      const char *srcfile = dwarf_linesrc(srcloc, 0, 0);
6✔
326

327
      dwarf_lineno(srcloc, (int *)&(frame->lineno));
6✔
328
      dwarf_linecol(srcloc, (int *)&(frame->colno));
6✔
329

330
      if (module != NULL) {
6✔
331
         // VHDL compilation units are wrapped in a DWARF module which
332
         // gives the unit name
333
         frame->kind = FRAME_VHDL;
×
334
         frame->vhdl_unit = ident_new(dwarf_diename(module));
×
335
      }
336

337
      if (srcfile != NULL)
6✔
338
         frame->srcfile = xstrdup(srcfile);
6✔
339

340
      if (fundie != NULL)
6✔
341
         libdw_fill_inlining(ip - mod_bias, fundie, frame);
6✔
342
   }
343

344
   if (sym_name != NULL)
7✔
345
      frame->symbol = xstrdup(sym_name);
7✔
346
   if (module_name != NULL)
7✔
347
      frame->module = xstrdup(module_name);
7✔
348
}
7✔
349

350
static _Unwind_Reason_Code libdw_frame_iter(struct _Unwind_Context* ctx,
7✔
351
                                            void *param)
352
{
353
   debug_info_t *di = param;
7✔
354

355
   if (di->skip > 0) {
7✔
356
      di->skip--;
1✔
357
      return _URC_NO_REASON;
1✔
358
   }
359

360
   int ip_before_instruction = 0;
6✔
361
   uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
6✔
362

363
   if (ip == 0)
6✔
364
      return _URC_NO_REASON;
365
   else if (!ip_before_instruction)
6✔
366
      ip -= 1;
6✔
367

368
   debug_frame_t *frame;
6✔
369
   if (!di_lru_get(ip, &frame) && !custom_fill_frame(ip, frame))
6✔
370
      platform_fill_frame(ip, frame);
6✔
371

372
   APUSH(di->frames, frame);
6✔
373

374
   if (frame->symbol != NULL && strcmp(frame->symbol, "main") == 0)
6✔
375
      return _URC_NORMAL_STOP;
376
   else
377
      return _URC_NO_REASON;
5✔
378
}
379

380
__attribute__((always_inline))
381
static inline void debug_walk_frames(debug_info_t *di)
1✔
382
{
383
   di->skip = 1;
1✔
384
   _Unwind_Backtrace(libdw_frame_iter, di);
1✔
385
}
386

387
////////////////////////////////////////////////////////////////////////////////
388
// Libdwarf backend
389

390
#elif defined HAVE_LIBDWARF
391

392
typedef struct {
393
   Dwarf_Debug   debug;
394
   Dwarf_Arange *aranges;
395
   Dwarf_Signed  arange_count;
396
   int           fd;
397
} libdwarf_handle_t;
398

399
static bool libdwarf_die_has_pc(libdwarf_handle_t *handle, Dwarf_Die die,
400
                                Dwarf_Addr pc)
401
{
402
   Dwarf_Addr low_pc = 0, high_pc = 0;
403

404
   if (dwarf_lowpc(die, &low_pc, NULL) == DW_DLV_OK) {
405
      Dwarf_Half form;
406
      enum Dwarf_Form_Class class;
407
      if (dwarf_highpc_b(die, &high_pc, &form, &class, NULL) == DW_DLV_OK) {
408
         if (class == DW_FORM_CLASS_CONSTANT)
409
            high_pc += low_pc;   // DWARF4
410
         return pc >= low_pc && pc < high_pc;
411
      }
412
   }
413

414
   Dwarf_Attribute attr;
415
   if (dwarf_attr(die, DW_AT_ranges, &attr, NULL) != DW_DLV_OK)
416
      return false;
417

418
   Dwarf_Off offset;
419
   if (dwarf_global_formref(attr, &offset, NULL) != DW_DLV_OK)
420
      return false;
421

422
   Dwarf_Ranges *ranges;
423
   Dwarf_Signed ranges_count = 0;
424
   Dwarf_Unsigned byte_count = 0;
425
   bool result = false;
426

427
   if (dwarf_get_ranges_a(handle->debug, offset, die, &ranges, &ranges_count,
428
                          &byte_count, NULL) == DW_DLV_OK) {
429
      for (int i = 0; i < ranges_count; i++) {
430
         if (ranges[i].dwr_addr1 != 0 &&
431
             pc >= ranges[i].dwr_addr1 + low_pc &&
432
             pc < ranges[i].dwr_addr2 + low_pc) {
433
            result = true;
434
            break;
435
         }
436
      }
437
      dwarf_ranges_dealloc(handle->debug, ranges, ranges_count);
438
   }
439

440
   return result;
441
}
442

443
static libdwarf_handle_t *libdwarf_handle_for_file(const char *fname)
444
{
445
   static shash_t *hash = NULL;
446

447
   if (hash == NULL)
448
      hash = shash_new(64);
449

450
   libdwarf_handle_t *handle = shash_get(hash, fname);
451

452
   if (handle == (void *)-1)
453
      return NULL;
454
   else if (handle == NULL) {
455
      if (elf_version(EV_CURRENT) == EV_NONE)
456
         fatal("ELF library too old");
457

458
      int fd;
459
      if (strchr(fname, DIR_SEP[0]))
460
         fd = open(fname, O_RDONLY);
461
      else {
462
         char LOCAL *full = search_path(fname);
463
         fd = open(full, O_RDONLY);
464
      }
465

466
      if (fd == -1) {
467
         warnf("open: %s: %s", fname, strerror(errno));
468
         shash_put(hash, fname, (void *)-1);
469
         return NULL;
470
      }
471

472
      Dwarf_Debug debug;
473
      Dwarf_Error err;
474
      if (dwarf_init(fd, DW_DLC_READ, NULL, NULL, &debug, &err) != DW_DLV_OK) {
475
         warnf("dwarf_init: %s: %s", fname, dwarf_errmsg(err));
476
         shash_put(hash, fname, (void *)-1);
477
         return NULL;
478
      }
479

480
      handle = xcalloc(sizeof(libdwarf_handle_t));
481
      handle->fd    = fd;
482
      handle->debug = debug;
483

484
      shash_put(hash, fname, handle);
485
   }
486

487
   return handle;
488
}
489

490
static bool libdwarf_get_sibling(libdwarf_handle_t *handle, Dwarf_Die *die)
491
{
492
#ifdef __FreeBSD__
493
   // FreeBSD has the arguments in the wrong order for some reason
494
   return dwarf_siblingof_b(handle->debug, *die, die, true, NULL) == DW_DLV_OK;
495
#else
496
   return dwarf_siblingof_b(handle->debug, *die, true, die, NULL) == DW_DLV_OK;
497
#endif
498
}
499

500
static void libdwarf_get_symbol(libdwarf_handle_t *handle, Dwarf_Die die,
501
                                Dwarf_Unsigned rel_addr, debug_frame_t *frame)
502
{
503
   Dwarf_Die child, prev = NULL;
504
   if (dwarf_child(die, &child, NULL) != DW_DLV_OK)
505
      return;
506

507
   do {
508
      if (prev != NULL)
509
         dwarf_dealloc(handle->debug, prev, DW_DLA_DIE);
510
      prev = child;
511

512
      Dwarf_Half tag;
513
      if (dwarf_tag(child, &tag, NULL) != DW_DLV_OK)
514
         continue;
515
      else if (tag == DW_TAG_module) {
516
         libdwarf_get_symbol(handle, child, rel_addr, frame);
517
         if (frame->symbol == NULL)
518
            continue;
519

520
         char *name;
521
         if (dwarf_diename(child, &name, NULL) == DW_DLV_OK) {
522
            frame->vhdl_unit = ident_new(name);
523
            frame->kind = FRAME_VHDL;
524
            dwarf_dealloc(handle->debug, name, DW_DLA_STRING);
525
            break;
526
         }
527

528
         continue;
529
      }
530
      else if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
531
         continue;
532
      else if (!libdwarf_die_has_pc(handle, child, rel_addr))
533
         continue;
534

535
      char *name;
536
      if (dwarf_diename(child, &name, NULL) == DW_DLV_OK) {
537
         frame->symbol = xstrdup(name);
538
         dwarf_dealloc(handle->debug, name, DW_DLA_STRING);
539
      }
540

541
      break;
542
   } while (libdwarf_get_sibling(handle, &child));
543

544
   if (prev != NULL)
545
      dwarf_dealloc(handle->debug, prev, DW_DLA_DIE);
546
}
547

548
static void libdwarf_get_srcline(libdwarf_handle_t *handle, Dwarf_Die die,
549
                                 Dwarf_Unsigned rel_addr, debug_frame_t *frame)
550
{
551
   Dwarf_Line*  linebuf = NULL;
552
   Dwarf_Signed linecount = 0;
553
   Dwarf_Signed idx = 0;
554
   if (dwarf_srclines(die, &linebuf, &linecount, NULL) == DW_DLV_OK) {
555
      Dwarf_Unsigned pladdr = 0;
556
      for (Dwarf_Signed i = 0; i < linecount; i++) {
557
         Dwarf_Unsigned laddr;
558
         if (dwarf_lineaddr(linebuf[i], &laddr, NULL) != DW_DLV_OK)
559
            break;
560
         else if (rel_addr == laddr) {
561
            idx = i;
562
            break;
563
         }
564
         else if (rel_addr < laddr && rel_addr > pladdr) {
565
            idx = i - 1;
566
            break;
567
         }
568
         pladdr = laddr;
569
      }
570

571
      if (idx >= 0) {
572
         Dwarf_Unsigned lineno;
573
         if (dwarf_lineno(linebuf[idx], &lineno, NULL) == DW_DLV_OK)
574
            frame->lineno = lineno;
575

576
         char *srcfile;
577
         if (dwarf_linesrc(linebuf[idx], &srcfile, NULL) == DW_DLV_OK) {
578
            frame->srcfile = xstrdup(srcfile);
579
            dwarf_dealloc(handle->debug, srcfile, DW_DLA_STRING);
580
         }
581
      }
582

583
      dwarf_srclines_dealloc(handle->debug, linebuf, linecount);
584
   }
585
}
586

587
static bool libdwarf_scan_aranges(libdwarf_handle_t *handle,
588
                                  Dwarf_Unsigned rel_addr,
589
                                  debug_frame_t *frame)
590
{
591
   Dwarf_Off cu_header_offset = 0;
592
   bool      found = false;
593

594
   if (handle->aranges == NULL) {
595
      if (dwarf_get_aranges(handle->debug, &(handle->aranges),
596
                            &(handle->arange_count), NULL) != DW_DLV_OK)
597
         return false;
598
   }
599

600
   Dwarf_Arange arange;
601
   if (dwarf_get_arange(handle->aranges, handle->arange_count, rel_addr,
602
                        &arange, NULL) != DW_DLV_OK)
603
      return false;
604

605
   if (dwarf_get_cu_die_offset(arange, &cu_header_offset, NULL) != DW_DLV_OK)
606
      return false;
607

608
   Dwarf_Die die = NULL;
609
   if (dwarf_offdie(handle->debug, cu_header_offset, &die, NULL) != DW_DLV_OK)
610
      return false;
611

612
   Dwarf_Half tag = 0;
613
   if (dwarf_tag(die, &tag, NULL) != DW_DLV_OK)
614
      goto free_die;
615
   else if (tag != DW_TAG_compile_unit)
616
      goto free_die;
617

618
   if (libdwarf_die_has_pc(handle, die, rel_addr)) {
619
      libdwarf_get_srcline(handle, die, rel_addr, frame);
620
      libdwarf_get_symbol(handle, die, rel_addr, frame);
621
      found = true;
622
   }
623

624
 free_die:
625
   dwarf_dealloc(handle->debug, die, DW_DLA_DIE);
626

627
   return found;
628
}
629

630
static bool libdwarf_scan_cus(libdwarf_handle_t *handle,
631
                              Dwarf_Unsigned rel_addr,
632
                              debug_frame_t *frame)
633
{
634
   Dwarf_Unsigned next_cu_offset;
635
   Dwarf_Error error;
636
   bool found = false;
637
   while (dwarf_next_cu_header_c(handle->debug, true, NULL, NULL, NULL,
638
                                 NULL, NULL, NULL, NULL, NULL,
639
                                 &next_cu_offset, &error) == DW_DLV_OK) {
640
      if (found)
641
         continue;   // Read all the way to the end to reset the iterator
642

643
      Dwarf_Die die = NULL;
644
      if (!libdwarf_get_sibling(handle, &die))
645
         continue;
646

647
      Dwarf_Half tag = 0;
648
      if (dwarf_tag(die, &tag, NULL) != DW_DLV_OK)
649
         goto free_die;
650
      else if (tag != DW_TAG_compile_unit)
651
         goto free_die;
652

653
      if (libdwarf_die_has_pc(handle, die, rel_addr)) {
654
         libdwarf_get_srcline(handle, die, rel_addr, frame);
655
         libdwarf_get_symbol(handle, die, rel_addr, frame);
656
         found = true;
657
      }
658

659
   free_die:
660
      dwarf_dealloc(handle->debug, die, DW_DLA_DIE);
661
      die = NULL;
662
   }
663

664
   return found;
665
}
666

667
static void platform_fill_frame(uintptr_t ip, debug_frame_t *frame)
668
{
669
   Dl_info dli;
670
   if (!dladdr((void *)ip, &dli))
671
      return;
672

673
   static void *home_fbase = NULL;
674
   if (home_fbase == NULL) {
675
      Dl_info dli_home;
676
      extern int main(int, char **);
677
      if (dladdr(main, &dli_home))
678
         home_fbase = dli_home.dli_fbase;
679
   }
680

681
   frame->kind   = dli.dli_fbase == home_fbase ? FRAME_PROG : FRAME_LIB;
682
   frame->module = xstrdup(dli.dli_fname);
683
   frame->disp   = ip - (uintptr_t)dli.dli_saddr;
684

685
#ifdef __FreeBSD__
686
   // FreeBSD has non-standard libdwarf
687
   Dwarf_Addr rel_addr;
688
   if (frame->kind == FRAME_PROG)
689
      rel_addr = ip;
690
   else
691
      rel_addr = ip - (uintptr_t)dli.dli_fbase;
692
#else
693
   Dwarf_Addr rel_addr = ip - (uintptr_t)dli.dli_fbase;
694
#endif
695

696
   libdwarf_handle_t *handle = libdwarf_handle_for_file(dli.dli_fname);
697
   if (handle == NULL)
698
      return;
699

700
   if (!libdwarf_scan_aranges(handle, rel_addr, frame)) {
701
      // Clang does emit aranges so we have to search each compilation unit
702
      libdwarf_scan_cus(handle, rel_addr, frame);
703
   }
704

705
   if (frame->symbol == NULL && dli.dli_sname != NULL) {
706
      // Fallback: just use the nearest global symbol
707
      frame->symbol = xstrdup(dli.dli_sname);
708
   }
709
}
710

711
static _Unwind_Reason_Code libdwarf_frame_iter(struct _Unwind_Context* ctx,
712
                                               void *param)
713
{
714
   debug_info_t *di = param;
715

716
   if (di->skip > 0) {
717
      di->skip--;
718
      return _URC_NO_REASON;
719
   }
720

721
   int ip_before_instruction = 0;
722
   uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
723

724
   if (ip == 0)
725
      return _URC_NO_REASON;
726
   else if (!ip_before_instruction)
727
      ip -= 1;
728

729
   debug_frame_t *frame;
730
   if (!di_lru_get(ip, &frame) && !custom_fill_frame(ip, frame))
731
      platform_fill_frame(ip, frame);
732

733
   APUSH(di->frames, frame);
734

735
   if (frame->symbol != NULL && strcmp(frame->symbol, "main") == 0)
736
      return _URC_NORMAL_STOP;
737
   else
738
      return _URC_NO_REASON;
739
}
740

741
__attribute__((always_inline))
742
static inline void debug_walk_frames(debug_info_t *di)
743
{
744
   di->skip = 1;
745
   _Unwind_Backtrace(libdwarf_frame_iter, di);
746
}
747

748
////////////////////////////////////////////////////////////////////////////////
749
// Windows backend
750

751
#elif defined __MINGW32__ || defined __CYGWIN__
752

753
static void platform_fill_frame(uintptr_t ip, debug_frame_t *frame)
754
{
755
   HANDLE hProcess = GetCurrentProcess();
756

757
   frame->kind = FRAME_PROG;
758

759
   char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
760
   PSYMBOL_INFO psym = (PSYMBOL_INFO)buffer;
761
   memset(buffer, '\0', sizeof(SYMBOL_INFO));
762
   psym->SizeOfStruct = sizeof(SYMBOL_INFO);
763
   psym->MaxNameLen = MAX_SYM_NAME;
764

765
   DWORD64 disp;
766
   if (SymFromAddr(hProcess, ip, &disp, psym)) {
767
      frame->symbol = xstrdup(psym->Name);
768
      frame->disp   = disp;
769
   }
770

771
   IMAGEHLP_MODULE module;
772
   memset(&module, '\0', sizeof(module));
773
   module.SizeOfStruct = sizeof(module);
774
   if (SymGetModuleInfo(hProcess, ip, &module))
775
      frame->module = xstrdup(module.ModuleName);
776
}
777

778
__attribute__((noinline))
779
static void debug_walk_frames(debug_info_t *di)
780
{
781
#ifdef __WIN64
782
   static bool done_sym_init = false;
783
   if (!done_sym_init) {
784
      SymInitialize(GetCurrentProcess(), NULL, TRUE);
785
      done_sym_init = true;
786
   }
787

788
   CONTEXT Context;
789
   RtlCaptureContext(&Context);
790

791
   UNWIND_HISTORY_TABLE UnwindHistoryTable;
792
   RtlZeroMemory(&UnwindHistoryTable, sizeof(UNWIND_HISTORY_TABLE));
793

794
#ifdef ARCH_ARM64
795
#define PC_REG Pc
796
#else
797
#define PC_REG Rip
798
#endif
799

800
   for (ULONG n = 0; n < MAX_TRACE_DEPTH; n++) {
801
      ULONG64 ImageBase;
802
      PRUNTIME_FUNCTION RuntimeFunction =
803
         RtlLookupFunctionEntry(Context.PC_REG, &ImageBase,
804
                                &UnwindHistoryTable);
805

806
      if (RuntimeFunction == NULL)
807
         break;
808

809
      PVOID   HandlerData;
810
      ULONG64 EstablisherFrame;
811
      RtlVirtualUnwind(UNW_FLAG_NHANDLER,
812
                       ImageBase,
813
                       Context.PC_REG,
814
                       RuntimeFunction,
815
                       &Context,
816
                       &HandlerData,
817
                       &EstablisherFrame,
818
                       NULL);
819

820
      if (!Context.PC_REG)
821
         break;
822

823
      debug_frame_t *frame;
824
      if (!di_lru_get(Context.PC_REG, &frame)
825
           && !custom_fill_frame(Context.PC_REG, frame))
826
         platform_fill_frame(Context.PC_REG, frame);
827

828
      APUSH(di->frames, frame);
829
   }
830
#endif  // __WIN64
831
}
832

833
////////////////////////////////////////////////////////////////////////////////
834
// Unwind backend
835

836
#else
837

838
static void platform_fill_frame(uintptr_t ip, debug_frame_t *frame)
839
{
840
   Dl_info dli;
841
   if (!dladdr((void *)ip, &dli))
842
      return;
843

844
   static void *home_fbase = NULL;
845
   if (home_fbase == NULL) {
846
      Dl_info dli_home;
847
      extern int main(int, char **);
848
      if (dladdr(main, &dli_home))
849
         home_fbase = dli_home.dli_fbase;
850
   }
851

852
   frame->kind   = dli.dli_fbase == home_fbase ? FRAME_PROG : FRAME_LIB;
853
   frame->module = xstrdup(dli.dli_fname);
854
   frame->disp   = ip - (uintptr_t)dli.dli_saddr;
855

856
   if (dli.dli_sname)
857
      frame->symbol = xstrdup(dli.dli_sname);
858
}
859

860
static _Unwind_Reason_Code unwind_frame_iter(struct _Unwind_Context* ctx,
861
                                             void *param)
862
{
863
   debug_info_t *di = param;
864

865
   if (di->skip > 0) {
866
      di->skip--;
867
      return _URC_NO_REASON;
868
   }
869

870
   int ip_before_instruction = 0;
871
   uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
872

873
   if (ip == 0)
874
      return _URC_NO_REASON;
875
   else if (!ip_before_instruction)
876
      ip -= 1;
877

878
   debug_frame_t *frame;
879
   if (!di_lru_get(ip, &frame) && !custom_fill_frame(ip, frame))
880
      platform_fill_frame(ip, frame);
881

882
   APUSH(di->frames, frame);
883

884
   if (frame->symbol != NULL && strcmp(frame->symbol, "main") == 0)
885
      return _URC_NORMAL_STOP;
886
   else
887
      return _URC_NO_REASON;
888
}
889

890
__attribute__((always_inline))
891
static inline void debug_walk_frames(debug_info_t *di)
892
{
893
   di->skip = 1;
894
   _Unwind_Backtrace(unwind_frame_iter, di);
895
}
896

897
#endif
898

899
////////////////////////////////////////////////////////////////////////////////
900
// Public interface
901

902
__attribute__((noinline))
903
debug_info_t *debug_capture(void)
1✔
904
{
905
   // The various DWARF libraries do not seem to be thread-safe
906
   static nvc_lock_t lock;
1✔
907
   SCOPED_LOCK(lock);
1✔
908

909
   debug_info_t *di = xcalloc(sizeof(debug_info_t));
1✔
910
   debug_walk_frames(di);
1✔
911
   return di;
1✔
912
}
913

914
void debug_free(debug_info_t *di)
1✔
915
{
916
   ACLEAR(di->frames);
1✔
917
   free(di);
1✔
918
}
1✔
919

920
unsigned debug_count_frames(debug_info_t *di)
1✔
921
{
922
   return di->frames.count;
1✔
923
}
924

925
const debug_frame_t *debug_get_frame(debug_info_t *di, unsigned n)
2✔
926
{
927
   return AGET(di->frames, n);
2✔
928
}
929

NEW
930
hash_state_t debug_hash(debug_info_t *di)
×
931
{
NEW
932
   hash_state_t hash = HASH_INIT;
×
NEW
933
   int hash_depth = 0;
×
934

NEW
935
   const int nframes = debug_count_frames(di);
×
NEW
936
   for (int n = 1; n < nframes; n++) {
×
NEW
937
      const debug_frame_t *f = debug_get_frame(di, n);
×
NEW
938
      if (f->kind == FRAME_PROG && f->symbol != NULL
×
NEW
939
          && hash_depth++ < MAX_HASH_DEPTH)
×
NEW
940
         hash_update(&hash, f->symbol, strlen(f->symbol));
×
941
   }
942

NEW
943
   return hash;
×
944
}
945

946
void debug_add_unwinder(void *start, size_t len, debug_unwind_fn_t fn,
3,474✔
947
                        void *context)
948
{
949
   debug_unwinder_t *uw = xmalloc(sizeof(debug_unwinder_t));
3,474✔
950
   uw->next    = unwinders;
3,474✔
951
   uw->fn      = fn;
3,474✔
952
   uw->context = context;
3,474✔
953
   uw->start   = (uintptr_t)start;
3,474✔
954
   uw->end     = (uintptr_t)start + len;
3,474✔
955

956
   unwinders = uw;
3,474✔
957
}
3,474✔
958

959
void debug_remove_unwinder(void *start)
3,472✔
960
{
961
   for (debug_unwinder_t **p = &unwinders; *p; p = &((*p)->next)) {
3,472✔
962
      if ((*p)->start == (uintptr_t)start) {
3,472✔
963
         debug_unwinder_t *tmp = *p;
3,472✔
964
         *p = (*p)->next;
3,472✔
965
         free(tmp);
3,472✔
966
         return;
3,472✔
967
      }
968
   }
969

970
   fatal_trace("no unwinder registered for %p", start);
971
}
972

973
const char *debug_symbol_name(void *addr)
1✔
974
{
975
   debug_frame_t *frame;
1✔
976
   if (!di_lru_get((uintptr_t)addr, &frame))
1✔
977
      platform_fill_frame((uintptr_t)addr, frame);
1✔
978

979
   return frame->symbol;
1✔
980
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc