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

nickg / nvc / 6434745784

06 Oct 2023 05:34PM UTC coverage: 91.167% (-0.06%) from 91.225%
6434745784

push

github

nickg
Add --vhpi-debug option for verbose VHPI error reporting

6 of 6 new or added lines in 3 files covered. (100.0%)

48770 of 53495 relevant lines covered (91.17%)

624720.27 hits per line

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

63.18
/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

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

71
////////////////////////////////////////////////////////////////////////////////
72
// Utilities
73

74
typedef struct _di_lru_cache di_lru_cache_t;
75

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

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

85
typedef struct _debug_unwinder debug_unwinder_t;
86

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

95
static debug_unwinder_t *unwinders = NULL;
96

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

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

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

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

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

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

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

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

173
   return false;
174
}
175

176
////////////////////////////////////////////////////////////////////////////////
177
// Libdw backend
178

179
#if defined HAVE_LIBDW
180

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

386
////////////////////////////////////////////////////////////////////////////////
387
// Libdwarf backend
388

389
#elif defined HAVE_LIBDWARF
390

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

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

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

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

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

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

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

439
   return result;
440
}
441

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

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

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

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

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

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

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

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

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

486
   return handle;
487
}
488

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

626
   return found;
627
}
628

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

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

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

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

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

663
   return found;
664
}
665

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

747
////////////////////////////////////////////////////////////////////////////////
748
// Windows backend
749

750
#elif defined __MINGW32__ || defined __CYGWIN__
751

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

756
   frame->kind = FRAME_PROG;
757

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

764
   DWORD64 disp;
765
   if (SymFromAddr(hProcess, ip, &disp, psym)) {
766
      text_buf_t *tb = unsafe_symbol(psym->Name);
767
      frame->symbol = tb_claim(tb);
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
   for (ULONG n = 0; n < MAX_TRACE_DEPTH; n++) {
795
      ULONG64 ImageBase;
796
      PRUNTIME_FUNCTION RuntimeFunction =
797
         RtlLookupFunctionEntry(Context.Rip, &ImageBase, &UnwindHistoryTable);
798

799
      if (RuntimeFunction == NULL)
800
         break;
801

802
      PVOID   HandlerData;
803
      ULONG64 EstablisherFrame;
804
      RtlVirtualUnwind(UNW_FLAG_NHANDLER,
805
                       ImageBase,
806
                       Context.Rip,
807
                       RuntimeFunction,
808
                       &Context,
809
                       &HandlerData,
810
                       &EstablisherFrame,
811
                       NULL);
812

813
      if (!Context.Rip)
814
         break;
815

816
      debug_frame_t *frame;
817
      if (!di_lru_get(Context.Rip, &frame)
818
          && !custom_fill_frame(Context.Rip, frame))
819
         platform_fill_frame(Context.Rip, frame);
820

821
      APUSH(di->frames, frame);
822
   }
823
#endif  // __WIN64
824
}
825

826
////////////////////////////////////////////////////////////////////////////////
827
// Unwind backend
828

829
#else
830

831
static void platform_fill_frame(uintptr_t ip, debug_frame_t *frame)
832
{
833
   Dl_info dli;
834
   if (!dladdr((void *)ip, &dli))
835
      return;
836

837
   static void *home_fbase = NULL;
838
   if (home_fbase == NULL) {
839
      Dl_info dli_home;
840
      extern int main(int, char **);
841
      if (dladdr(main, &dli_home))
842
         home_fbase = dli_home.dli_fbase;
843
   }
844

845
   frame->kind   = dli.dli_fbase == home_fbase ? FRAME_PROG : FRAME_LIB;
846
   frame->module = xstrdup(dli.dli_fname);
847
   frame->disp   = ip - (uintptr_t)dli.dli_saddr;
848

849
   if (dli.dli_sname)
850
      frame->symbol = xstrdup(dli.dli_sname);
851
}
852

853
static _Unwind_Reason_Code unwind_frame_iter(struct _Unwind_Context* ctx,
854
                                             void *param)
855
{
856
   debug_info_t *di = param;
857

858
   if (di->skip > 0) {
859
      di->skip--;
860
      return _URC_NO_REASON;
861
   }
862

863
   int ip_before_instruction = 0;
864
   uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
865

866
   if (ip == 0)
867
      return _URC_NO_REASON;
868
   else if (!ip_before_instruction)
869
      ip -= 1;
870

871
   debug_frame_t *frame;
872
   if (!di_lru_get(ip, &frame) && !custom_fill_frame(ip, frame))
873
      platform_fill_frame(ip, frame);
874

875
   APUSH(di->frames, frame);
876

877
   if (frame->symbol != NULL && strcmp(frame->symbol, "main") == 0)
878
      return _URC_NORMAL_STOP;
879
   else
880
      return _URC_NO_REASON;
881
}
882

883
__attribute__((always_inline))
884
static inline void debug_walk_frames(debug_info_t *di)
885
{
886
   di->skip = 1;
887
   _Unwind_Backtrace(unwind_frame_iter, di);
888
}
889

890
#endif
891

892
////////////////////////////////////////////////////////////////////////////////
893
// Public interface
894

895
__attribute__((noinline))
896
debug_info_t *debug_capture(void)
1✔
897
{
898
   // The various DWARF libraries do not seem to be thread-safe
899
   static nvc_lock_t lock;
1✔
900
   SCOPED_LOCK(lock);
1✔
901

902
   debug_info_t *di = xcalloc(sizeof(debug_info_t));
1✔
903
   debug_walk_frames(di);
1✔
904
   return di;
1✔
905
}
906

907
void debug_free(debug_info_t *di)
1✔
908
{
909
   ACLEAR(di->frames);
1✔
910
   free(di);
1✔
911
}
1✔
912

913
unsigned debug_count_frames(debug_info_t *di)
1✔
914
{
915
   return di->frames.count;
1✔
916
}
917

918
const debug_frame_t *debug_get_frame(debug_info_t *di, unsigned n)
2✔
919
{
920
   return AGET(di->frames, n);
2✔
921
}
922

923
void debug_add_unwinder(void *start, size_t len, debug_unwind_fn_t fn,
3,590✔
924
                        void *context)
925
{
926
   debug_unwinder_t *uw = xmalloc(sizeof(debug_unwinder_t));
3,590✔
927
   uw->next    = unwinders;
3,590✔
928
   uw->fn      = fn;
3,590✔
929
   uw->context = context;
3,590✔
930
   uw->start   = (uintptr_t)start;
3,590✔
931
   uw->end     = (uintptr_t)start + len;
3,590✔
932

933
   unwinders = uw;
3,590✔
934
}
3,590✔
935

936
void debug_remove_unwinder(void *start)
3,588✔
937
{
938
   for (debug_unwinder_t **p = &unwinders; *p; p = &((*p)->next)) {
3,588✔
939
      if ((*p)->start == (uintptr_t)start) {
3,588✔
940
         debug_unwinder_t *tmp = *p;
3,588✔
941
         *p = (*p)->next;
3,588✔
942
         free(tmp);
3,588✔
943
         return;
3,588✔
944
      }
945
   }
946

947
   fatal_trace("no unwinder registered for %p", start);
×
948
}
949

950
const char *debug_symbol_name(void *addr)
1✔
951
{
952
   debug_frame_t *frame;
1✔
953
   if (!di_lru_get((uintptr_t)addr, &frame))
1✔
954
      platform_fill_frame((uintptr_t)addr, frame);
1✔
955

956
   return frame->symbol;
1✔
957
}
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

© 2025 Coveralls, Inc