Coverage Report

Created: 2024-02-29 06:05

/src/strongswan/src/libstrongswan/utils/backtrace.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2006-2013 Martin Willi
3
 *
4
 * Copyright (C) secunet Security Networks AG
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License as published by the
8
 * Free Software Foundation; either version 2 of the License, or (at your
9
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10
 *
11
 * This program is distributed in the hope that it will be useful, but
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14
 * for more details.
15
 */
16
17
#define _GNU_SOURCE
18
#include <string.h>
19
20
#ifdef HAVE_BACKTRACE
21
#include <execinfo.h>
22
#endif /* HAVE_BACKTRACE */
23
24
#ifdef WIN32
25
#include <winsock2.h>
26
#include <windows.h>
27
#ifdef HAVE_DBGHELP
28
#include <dbghelp.h>
29
#endif /* HAVE_DBGHELP */
30
31
#include <psapi.h>
32
/* missing in MinGW */
33
#ifdef WIN64
34
#ifndef GetModuleInformation
35
WINBOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule,
36
                LPMODULEINFO lpmodinfo, DWORD cb);
37
#define GetModuleInformation K32GetModuleInformation
38
#endif /* !GetModuleInformation */
39
#ifndef GetModuleFileNameEx
40
DWORD K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule,
41
                LPTSTR lpFilename, DWORD nSize);
42
#define GetModuleFileNameEx K32GetModuleFileNameExA
43
#endif /* !GetModuleFileNameEx */
44
#endif /* WIN64 */
45
#endif
46
47
#include "backtrace.h"
48
49
#include <utils/debug.h>
50
51
typedef struct private_backtrace_t private_backtrace_t;
52
53
/**
54
 * Private data of an backtrace_t object.
55
 */
56
struct private_backtrace_t {
57
58
  /**
59
   * Public backtrace_t interface.
60
   */
61
  backtrace_t public;
62
63
  /**
64
   * Number of stacks frames obtained in stack_frames
65
   */
66
  int frame_count;
67
68
  /**
69
   * Recorded stack frames.
70
   */
71
  void *frames[];
72
};
73
74
/**
75
 * Forward declaration of method getter
76
 */
77
static backtrace_t get_methods();
78
79
/**
80
 * Write a format string with arguments to a FILE line, if it is NULL to DBG
81
 */
82
static void println(FILE *file, char *format, ...)
83
0
{
84
0
  char buf[512];
85
0
  va_list args;
86
87
0
  va_start(args, format);
88
0
  if (file)
89
0
  {
90
0
    vfprintf(file, format, args);
91
0
    fputs("\n", file);
92
0
  }
93
0
  else
94
0
  {
95
0
    vsnprintf(buf, sizeof(buf), format, args);
96
0
    DBG1(DBG_LIB, "%s", buf);
97
0
  }
98
0
  va_end(args);
99
0
}
100
101
#if ((defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H)) && \
102
    defined(HAVE_DLADDR)) || defined(WIN32)
103
104
/**
105
 * Same as tty_escape_get(), but for a potentially NULL FILE*
106
 */
107
static inline char* esc(FILE *file, tty_escape_t escape)
108
0
{
109
0
  if (file)
110
0
  {
111
0
    return tty_escape_get(fileno(file), escape);
112
0
  }
113
0
  return "";
114
0
}
115
116
#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H/WIN32 */
117
118
#ifdef HAVE_DBGHELP
119
120
#include <dbghelp.h>
121
#include <threading/mutex.h>
122
123
/**
124
 * Mutex to access non-thread-safe dbghelp functions
125
 */
126
static mutex_t *dbghelp_mutex;
127
128
void backtrace_init()
129
{
130
  SymSetOptions(SYMOPT_LOAD_LINES);
131
  SymInitialize(GetCurrentProcess(), NULL, TRUE);
132
  dbghelp_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
133
}
134
135
void backtrace_deinit()
136
{
137
  dbghelp_mutex->destroy(dbghelp_mutex);
138
  SymCleanup(GetCurrentProcess());
139
}
140
141
#elif defined(HAVE_DLADDR) || defined(HAVE_BFD_H)
142
143
#ifdef HAVE_DLADDR
144
#include <dlfcn.h>
145
#endif
146
147
#ifdef HAVE_BFD_H
148
149
#include <bfd.h>
150
#include <collections/hashtable.h>
151
#include <threading/mutex.h>
152
153
/* interface changes for newer BFD versions, note that older versions declared
154
 * some of the new functions as macros but with different arguments */
155
#if HAVE_DECL_BFD_GET_SECTION_FLAGS
156
#define get_section_flags(a, s) bfd_get_section_flags(a, s)
157
#elif HAVE_DECL_BFD_SECTION_FLAGS
158
#define get_section_flags(a, s) bfd_section_flags(s)
159
#else
160
#error Unknown BFD API
161
#endif
162
163
#if HAVE_DECL_BFD_GET_SECTION_VMA
164
#define get_section_vma(a, s) bfd_get_section_vma(a, s)
165
#elif HAVE_DECL_BFD_SECTION_VMA
166
#define get_section_vma(a, s) bfd_section_vma(s)
167
#else
168
#error Unknown BFD API
169
#endif
170
171
#if HAVE_DECL_BFD_GET_SECTION_SIZE
172
#define get_section_size bfd_get_section_size
173
#elif HAVE_DECL_BFD_SECTION_SIZE
174
#define get_section_size bfd_section_size
175
#else
176
#error Unknown BFD API
177
#endif
178
179
/**
180
 * Hashtable-cached bfd handle
181
 */
182
typedef struct {
183
  /** binary file name on disk */
184
  char *filename;
185
  /** bfd handle */
186
  bfd *abfd;
187
  /** loaded symbols */
188
  asymbol **syms;
189
} bfd_entry_t;
190
191
/**
192
 * Destroy a bfd_entry
193
 */
194
static void bfd_entry_destroy(bfd_entry_t *this)
195
{
196
  free(this->filename);
197
  free(this->syms);
198
  bfd_close(this->abfd);
199
  free(this);
200
}
201
202
/**
203
 * Data to pass to find_addr()
204
 */
205
typedef struct {
206
  /** used bfd entry */
207
  bfd_entry_t *entry;
208
  /** backtrace address */
209
  bfd_vma vma;
210
  /** TRUE if address found */
211
  bool found;
212
  /** optional stream to log to */
213
  FILE *file;
214
  /** optional list of function names to match */
215
  char **list;
216
  /** optional number of names in list */
217
  int count;
218
  /** TRUE if found function name is in list */
219
  bool in_list;
220
} bfd_find_data_t;
221
222
/**
223
 * bfd entry cache
224
 */
225
static hashtable_t *bfds;
226
227
static mutex_t *bfd_mutex;
228
229
/**
230
 * Hashtable hash function
231
 */
232
static u_int bfd_hash(char *key)
233
{
234
  return chunk_hash(chunk_create(key, strlen(key)));
235
}
236
237
/**
238
 * Hashtable equals function
239
 */
240
static bool bfd_equals(char *a, char *b)
241
{
242
  return streq(a, b);
243
}
244
245
/**
246
 * Do not print internal errors by libbfd as we get quite a lot of
247
 * "DWARF error: could not find variable specification" messages when running
248
 * against Ubuntu 20.04's libcrypto (same with addr2line and still in 22.10).
249
 */
250
void suppress_bfd_errors (const char *fmt, va_list args)
251
{
252
}
253
254
/**
255
 * See header.
256
 */
257
void backtrace_init()
258
{
259
  bfd_init();
260
  bfds = hashtable_create((hashtable_hash_t)bfd_hash,
261
              (hashtable_equals_t)bfd_equals, 8);
262
  bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
263
  bfd_set_error_handler(suppress_bfd_errors);
264
}
265
266
/**
267
 * See header.
268
 */
269
void backtrace_deinit()
270
{
271
  enumerator_t *enumerator;
272
  bfd_entry_t *entry;
273
  char *key;
274
275
  enumerator = bfds->create_enumerator(bfds);
276
  while (enumerator->enumerate(enumerator, &key, &entry))
277
  {
278
    bfds->remove_at(bfds, enumerator);
279
    bfd_entry_destroy(entry);
280
  }
281
  enumerator->destroy(enumerator);
282
283
  bfds->destroy(bfds);
284
  bfd_mutex->destroy(bfd_mutex);
285
}
286
287
/**
288
 * Find and print information to an address
289
 */
290
static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data)
291
{
292
  bfd_size_type size;
293
  bfd_vma vma;
294
  const char *source;
295
  const char *function;
296
  char fbuf[512] = "", sbuf[512] = "";
297
  u_int line;
298
  int i;
299
300
  if (data->found || (get_section_flags(abfd, section) & SEC_ALLOC) == 0)
301
  {
302
    return;
303
  }
304
  vma = get_section_vma(abfd, section);
305
  if (data->vma < vma)
306
  {
307
    return;
308
  }
309
  size = get_section_size(section);
310
  if (data->vma >= vma + size)
311
  {
312
    return;
313
  }
314
315
  data->found = bfd_find_nearest_line(abfd, section, data->entry->syms,
316
                    data->vma - vma, &source, &function,
317
                    &line);
318
  if (!data->found)
319
  {
320
    return;
321
  }
322
  if (data->count && function)
323
  {
324
    for (i = 0; i < data->count; i++)
325
    {
326
      if (streq(function, data->list[i]))
327
      {
328
        data->in_list = TRUE;
329
        break;
330
      }
331
    }
332
  }
333
  else if (data->file && (source || function))
334
  {
335
    if (function)
336
    {
337
      snprintf(fbuf, sizeof(fbuf), "%s%s() ",
338
        esc(data->file, TTY_FG_BLUE), function);
339
    }
340
    if (source)
341
    {
342
      snprintf(sbuf, sizeof(sbuf), "%s@ %s:%d",
343
        esc(data->file, TTY_FG_GREEN), source, line);
344
    }
345
    println(data->file, "    -> %s%s%s", fbuf, sbuf,
346
        esc(data->file, TTY_FG_DEF));
347
  }
348
}
349
350
/**
351
 * Find a cached bfd entry, create'n'cache if not found
352
 */
353
static bfd_entry_t *get_bfd_entry(char *filename)
354
{
355
  bool dynamic = FALSE, ok = FALSE;
356
  bfd_entry_t *entry;
357
  long size;
358
359
  /* check cache */
360
  entry = bfds->get(bfds, filename);
361
  if (entry)
362
  {
363
    return entry;
364
  }
365
366
  INIT(entry,
367
    .abfd = bfd_openr(filename, NULL),
368
  );
369
370
  if (!entry->abfd)
371
  {
372
    free(entry);
373
    return NULL;
374
  }
375
#ifdef BFD_DECOMPRESS
376
  entry->abfd->flags |= BFD_DECOMPRESS;
377
#endif
378
  if (bfd_check_format(entry->abfd, bfd_archive) == 0 &&
379
    bfd_check_format_matches(entry->abfd, bfd_object, NULL))
380
  {
381
    if (bfd_get_file_flags(entry->abfd) & HAS_SYMS)
382
    {
383
      size = bfd_get_symtab_upper_bound(entry->abfd);
384
      if (size == 0)
385
      {
386
        size = bfd_get_dynamic_symtab_upper_bound(entry->abfd);
387
        dynamic = TRUE;
388
      }
389
      if (size >= 0)
390
      {
391
        entry->syms = malloc(size);
392
        if (dynamic)
393
        {
394
          ok = bfd_canonicalize_dynamic_symtab(entry->abfd,
395
                             entry->syms) >= 0;
396
        }
397
        else
398
        {
399
          ok = bfd_canonicalize_symtab(entry->abfd,
400
                         entry->syms) >= 0;
401
        }
402
      }
403
    }
404
  }
405
  if (ok)
406
  {
407
    entry->filename = strdup(filename);
408
    bfds->put(bfds, entry->filename, entry);
409
    return entry;
410
  }
411
  bfd_entry_destroy(entry);
412
  return NULL;
413
}
414
415
/**
416
 * Lookup the given address
417
 */
418
static void lookup_addr(char *filename, bfd_find_data_t *data)
419
{
420
  bfd_entry_t *entry;
421
  bool old = FALSE;
422
423
  bfd_mutex->lock(bfd_mutex);
424
  if (lib && lib->leak_detective)
425
  {
426
    old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
427
  }
428
  entry = get_bfd_entry(filename);
429
  if (entry)
430
  {
431
    data->entry = entry;
432
    bfd_map_over_sections(entry->abfd, (void*)find_addr, data);
433
  }
434
  if (lib && lib->leak_detective)
435
  {
436
    lib->leak_detective->set_state(lib->leak_detective, old);
437
  }
438
  bfd_mutex->unlock(bfd_mutex);
439
}
440
441
/**
442
 * Print the source file with line number to file, libbfd variant
443
 */
444
static void print_sourceline(FILE *file, char *filename, void *ptr, void *base)
445
{
446
  bfd_find_data_t data = {
447
    .file = file,
448
    .vma = (uintptr_t)ptr,
449
  };
450
451
  lookup_addr(filename, &data);
452
}
453
454
/**
455
 * Check if the function name of the source line is in the given list
456
 */
457
static bool contains_function_bfd(char *filename, void *ptr, char *list[],
458
                  int count)
459
{
460
  bfd_find_data_t data = {
461
    .vma = (uintptr_t)ptr,
462
    .list = list,
463
    .count = count,
464
  };
465
466
  lookup_addr(filename, &data);
467
  return data.in_list;
468
}
469
470
#else /* !HAVE_BFD_H */
471
472
3.92k
void backtrace_init() {}
473
3.92k
void backtrace_deinit() {}
474
475
#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32)
476
477
/**
478
 * Print the source file with line number to file, slow addr2line variant
479
 */
480
static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
481
0
{
482
0
  char buf[1024];
483
0
  FILE *output;
484
0
  int c, i = 0;
485
486
#ifdef __APPLE__
487
  snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1",
488
       filename, base, ptr);
489
#else /* !__APPLE__ */
490
0
  snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr);
491
0
#endif /* __APPLE__ */
492
493
0
  output = popen(buf, "r");
494
0
  if (output)
495
0
  {
496
0
    while (i < sizeof(buf))
497
0
    {
498
0
      c = getc(output);
499
0
      if (c == '\n' || c == EOF)
500
0
      {
501
0
        buf[i++] = 0;
502
0
        break;
503
0
      }
504
0
      buf[i++] = c;
505
0
    }
506
0
    pclose(output);
507
508
0
    println(file, "    -> %s%s%s", esc(file, TTY_FG_GREEN), buf,
509
0
        esc(file, TTY_FG_DEF));
510
0
  }
511
0
}
512
513
#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H/WIN32 */
514
515
#endif /* HAVE_BFD_H */
516
517
#else /* !HAVE_DLADDR && !HAVE_DBGHELP */
518
519
void backtrace_init() {}
520
void backtrace_deinit() {}
521
522
#endif /* HAVE_DLADDR */
523
524
METHOD(backtrace_t, log_, void,
525
  private_backtrace_t *this, FILE *file, bool detailed)
526
0
{
527
0
#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32)
528
0
  size_t i;
529
0
  char **strings = NULL;
530
531
0
  println(file, " dumping %d stack frame addresses:", this->frame_count);
532
0
  for (i = 0; i < this->frame_count; i++)
533
0
  {
534
0
#ifdef HAVE_DLADDR
535
0
    Dl_info info;
536
537
0
    if (dladdr(this->frames[i], &info))
538
0
    {
539
0
      void *ptr = this->frames[i];
540
541
0
      if (strstr(info.dli_fname, ".so"))
542
0
      {
543
0
        ptr = (void*)(this->frames[i] - info.dli_fbase);
544
0
      }
545
0
      if (info.dli_sname)
546
0
      {
547
0
        println(file, "  %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
548
0
            esc(file, TTY_FG_YELLOW), info.dli_fname,
549
0
            esc(file, TTY_FG_DEF), info.dli_fbase,
550
0
            esc(file, TTY_FG_RED), info.dli_sname,
551
0
            esc(file, TTY_FG_DEF), this->frames[i] - info.dli_saddr,
552
0
            this->frames[i]);
553
0
      }
554
0
      else
555
0
      {
556
0
        println(file, "  %s%s%s @ %p [%p]",
557
0
            esc(file, TTY_FG_YELLOW), info.dli_fname,
558
0
            esc(file, TTY_FG_DEF), info.dli_fbase, this->frames[i]);
559
0
      }
560
0
      if (detailed && info.dli_fname[0])
561
0
      {
562
0
        print_sourceline(file, (char*)info.dli_fname,
563
0
                 ptr, info.dli_fbase);
564
0
      }
565
0
    }
566
0
    else
567
#elif defined(HAVE_DBGHELP)
568
    struct {
569
      SYMBOL_INFO hdr;
570
      char buf[128];
571
    } symbol;
572
    char filename[MAX_PATH];
573
    HINSTANCE module;
574
    HANDLE process;
575
    DWORD64 displace, frame;
576
577
    process = GetCurrentProcess();
578
    frame = (uintptr_t)this->frames[i];
579
580
    memset(&symbol, 0, sizeof(symbol));
581
    symbol.hdr.SizeOfStruct = sizeof(symbol.hdr);
582
    symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1;
583
584
    dbghelp_mutex->lock(dbghelp_mutex);
585
586
    module = (HINSTANCE)SymGetModuleBase64(process, frame);
587
588
    if (module && GetModuleFileName(module, filename, sizeof(filename)))
589
    {
590
      if (SymFromAddr(process, frame, &displace, &symbol.hdr))
591
      {
592
        println(file, "  %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
593
            esc(file, TTY_FG_YELLOW), filename,
594
            esc(file, TTY_FG_DEF), (void*)module,
595
            esc(file, TTY_FG_RED), symbol.hdr.Name,
596
            esc(file, TTY_FG_DEF), displace,
597
            this->frames[i]);
598
      }
599
      else
600
      {
601
        println(file, "  %s%s%s @ %p [%p]",
602
            esc(file, TTY_FG_YELLOW), filename,
603
            esc(file, TTY_FG_DEF), (void*)module, this->frames[i]);
604
      }
605
      if (detailed)
606
      {
607
        IMAGEHLP_LINE64 line;
608
        DWORD off;
609
610
        memset(&line, 0, sizeof(line));
611
        line.SizeOfStruct = sizeof(line);
612
613
        if (SymGetLineFromAddr64(process, frame, &off, &line))
614
        {
615
616
          println(file, "    -> %s%s:%u%s", esc(file, TTY_FG_GREEN),
617
              line.FileName, line.LineNumber,
618
              esc(file, TTY_FG_DEF));
619
        }
620
      }
621
    }
622
    else
623
#elif defined(WIN32)
624
    HMODULE module;
625
    MODULEINFO info;
626
    char filename[MAX_PATH];
627
628
    if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
629
            this->frames[i], &module) &&
630
      GetModuleInformation(GetCurrentProcess(), module,
631
            &info, sizeof(info)) &&
632
      GetModuleFileNameEx(GetCurrentProcess(), module,
633
            filename, sizeof(filename)))
634
    {
635
      println(file, "  %s%s%s @ %p [%p]",
636
          esc(file, TTY_FG_YELLOW), filename,
637
          esc(file, TTY_FG_DEF), info.lpBaseOfDll, this->frames[i]);
638
#ifdef HAVE_BFD_H
639
      print_sourceline(file, filename, this->frames[i], info.lpBaseOfDll);
640
#endif /* HAVE_BFD_H */
641
    }
642
    else
643
#endif /* HAVE_DLADDR/HAVE_DBGHELP/WIN32 */
644
0
    {
645
0
#ifdef HAVE_BACKTRACE
646
0
      if (!strings)
647
0
      {
648
0
        strings = backtrace_symbols(this->frames, this->frame_count);
649
0
      }
650
0
      if (strings)
651
0
      {
652
0
        println(file, "    %s", strings[i]);
653
0
      }
654
0
      else
655
0
#endif /* HAVE_BACKTRACE */
656
0
      {
657
0
        println(file, "    %p", this->frames[i]);
658
0
      }
659
0
    }
660
#ifdef HAVE_DBGHELP
661
    dbghelp_mutex->unlock(dbghelp_mutex);
662
#endif
663
0
  }
664
0
  free(strings);
665
#else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */
666
  println(file, "no support for capturing backtraces");
667
#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
668
0
}
669
670
METHOD(backtrace_t, contains_function, bool,
671
  private_backtrace_t *this, char *function[], int count)
672
0
{
673
0
#ifdef HAVE_DLADDR
674
0
  int i, j;
675
676
0
  for (i = 0; i< this->frame_count; i++)
677
0
  {
678
0
    Dl_info info;
679
680
0
    if (dladdr(this->frames[i], &info))
681
0
    {
682
0
      if (info.dli_sname)
683
0
      {
684
0
        for (j = 0; j < count; j++)
685
0
        {
686
0
          if (streq(info.dli_sname, function[j]))
687
0
          {
688
0
            return TRUE;
689
0
          }
690
0
        }
691
0
      }
692
#ifdef HAVE_BFD_H
693
      else if (info.dli_fname[0])
694
      {
695
        void *ptr = this->frames[i];
696
697
        if (strstr(info.dli_fname, ".so"))
698
        {
699
          ptr = (void*)(this->frames[i] - info.dli_fbase);
700
        }
701
        if (contains_function_bfd((char*)info.dli_fname, ptr,
702
                      function, count))
703
        {
704
          return TRUE;
705
        }
706
      }
707
#endif /* HAVE_BFD_H */
708
0
    }
709
0
  }
710
#elif defined(HAVE_DBGHELP)
711
  int i, j;
712
  HANDLE process;
713
714
  process = GetCurrentProcess();
715
716
  dbghelp_mutex->lock(dbghelp_mutex);
717
718
  for (i = 0; i < this->frame_count; i++)
719
  {
720
    struct {
721
      SYMBOL_INFO hdr;
722
      char buf[128];
723
    } symbol;
724
725
    memset(&symbol, 0, sizeof(symbol));
726
    symbol.hdr.SizeOfStruct = sizeof(symbol.hdr);
727
    symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1;
728
729
    if (SymFromAddr(process, (DWORD64)this->frames[i], NULL, &symbol.hdr))
730
    {
731
      for (j = 0; j < count; j++)
732
      {
733
        if (streq(symbol.hdr.Name, function[j]))
734
        {
735
          dbghelp_mutex->unlock(dbghelp_mutex);
736
          return TRUE;
737
        }
738
      }
739
    }
740
  }
741
742
  dbghelp_mutex->unlock(dbghelp_mutex);
743
#endif /* HAVE_DLADDR/HAVE_DBGHELP */
744
0
  return FALSE;
745
0
}
746
747
METHOD(backtrace_t, equals, bool,
748
  private_backtrace_t *this, backtrace_t *other_public)
749
0
{
750
0
  private_backtrace_t *other = (private_backtrace_t*)other_public;
751
0
  int i;
752
753
0
  if (this == other)
754
0
  {
755
0
    return TRUE;
756
0
  }
757
0
  if (this->frame_count != other->frame_count)
758
0
  {
759
0
    return FALSE;
760
0
  }
761
0
  for (i = 0; i < this->frame_count; i++)
762
0
  {
763
0
    if (this->frames[i] != other->frames[i])
764
0
    {
765
0
      return FALSE;
766
0
    }
767
0
  }
768
0
  return TRUE;
769
0
}
770
771
/**
772
 * Frame enumerator
773
 */
774
typedef struct {
775
  /** implements enumerator_t */
776
  enumerator_t public;
777
  /** reference to backtrace */
778
  private_backtrace_t *bt;
779
  /** current position */
780
  int i;
781
} frame_enumerator_t;
782
783
METHOD(enumerator_t, frame_enumerate, bool,
784
  frame_enumerator_t *this, va_list args)
785
0
{
786
0
  void **addr;
787
788
0
  VA_ARGS_VGET(args, addr);
789
790
0
  if (this->i < this->bt->frame_count)
791
0
  {
792
0
    *addr = this->bt->frames[this->i++];
793
0
    return TRUE;
794
0
  }
795
0
  return FALSE;
796
0
}
797
798
METHOD(backtrace_t, create_frame_enumerator, enumerator_t*,
799
  private_backtrace_t *this)
800
0
{
801
0
  frame_enumerator_t *enumerator;
802
803
0
  INIT(enumerator,
804
0
    .public = {
805
0
      .enumerate = enumerator_enumerate_default,
806
0
      .venumerate = _frame_enumerate,
807
0
      .destroy = (void*)free,
808
0
    },
809
0
    .bt = this,
810
0
  );
811
0
  return &enumerator->public;
812
0
}
813
814
METHOD(backtrace_t, clone_, backtrace_t*,
815
  private_backtrace_t *this)
816
0
{
817
0
  private_backtrace_t *clone;
818
819
0
  clone = malloc(sizeof(private_backtrace_t) +
820
0
           this->frame_count * sizeof(void*));
821
0
  memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*));
822
0
  clone->frame_count = this->frame_count;
823
824
0
  clone->public = get_methods();
825
826
0
  return &clone->public;
827
0
}
828
829
METHOD(backtrace_t, destroy, void,
830
  private_backtrace_t *this)
831
0
{
832
0
  free(this);
833
0
}
834
835
#ifdef HAVE_LIBUNWIND_H
836
#define UNW_LOCAL_ONLY
837
#include <libunwind.h>
838
839
/**
840
 * libunwind variant for glibc backtrace()
841
 */
842
static inline int backtrace_unwind(void **frames, int count)
843
{
844
  unw_context_t context;
845
  unw_cursor_t cursor;
846
  unw_word_t ip;
847
  int depth = 0;
848
849
  unw_getcontext(&context);
850
  unw_init_local(&cursor, &context);
851
  do
852
  {
853
    unw_get_reg(&cursor, UNW_REG_IP, &ip);
854
    frames[depth++] = (void*)ip;
855
  }
856
  while (depth < count && unw_step(&cursor) > 0);
857
858
  return depth;
859
}
860
#endif /* HAVE_UNWIND */
861
862
#ifdef HAVE_DBGHELP
863
864
/**
865
 * Windows dbghelp variant for glibc backtrace()
866
 */
867
static inline int backtrace_win(void **frames, int count)
868
{
869
  STACKFRAME frame;
870
  HANDLE process, thread;
871
  DWORD machine;
872
  CONTEXT context;
873
  int got = 0;
874
875
  memset(&frame, 0, sizeof(frame));
876
  memset(&context, 0, sizeof(context));
877
878
  process = GetCurrentProcess();
879
  thread = GetCurrentThread();
880
881
#ifdef __x86_64
882
  machine = IMAGE_FILE_MACHINE_AMD64;
883
884
  frame.AddrPC.Offset = context.Rip;
885
  frame.AddrPC.Mode = AddrModeFlat;
886
  frame.AddrStack.Offset = context.Rsp;
887
  frame.AddrStack.Mode = AddrModeFlat;
888
  frame.AddrFrame.Offset = context.Rbp;
889
  frame.AddrFrame.Mode = AddrModeFlat;
890
#else /* x86 */
891
  machine = IMAGE_FILE_MACHINE_I386;
892
893
  frame.AddrPC.Offset = context.Eip;
894
  frame.AddrPC.Mode = AddrModeFlat;
895
  frame.AddrStack.Offset = context.Esp;
896
  frame.AddrStack.Mode = AddrModeFlat;
897
  frame.AddrFrame.Offset = context.Ebp;
898
  frame.AddrFrame.Mode = AddrModeFlat;
899
#endif /* x86_64/x86 */
900
901
  dbghelp_mutex->lock(dbghelp_mutex);
902
903
  RtlCaptureContext(&context);
904
905
  while (got < count)
906
  {
907
    if (!StackWalk64(machine, process, thread, &frame, &context,
908
             NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
909
    {
910
      break;
911
    }
912
    frames[got++] = (void*)frame.AddrPC.Offset;
913
  }
914
915
  dbghelp_mutex->unlock(dbghelp_mutex);
916
917
  return got;
918
}
919
920
#endif /* HAVE_DBGHELP */
921
922
/**
923
 * Get implementation methods of backtrace_t
924
 */
925
static backtrace_t get_methods()
926
0
{
927
0
  return (backtrace_t) {
928
0
    .log = _log_,
929
0
    .contains_function = _contains_function,
930
0
    .equals = _equals,
931
0
    .clone = _clone_,
932
0
    .create_frame_enumerator = _create_frame_enumerator,
933
0
    .destroy = _destroy,
934
0
  };
935
0
}
936
937
/**
938
 * See header
939
 */
940
backtrace_t *backtrace_create(int skip)
941
0
{
942
0
  private_backtrace_t *this;
943
0
  void *frames[50];
944
0
  int frame_count = 0;
945
946
#ifdef HAVE_LIBUNWIND_H
947
  frame_count = backtrace_unwind(frames, countof(frames));
948
#elif defined(HAVE_BACKTRACE)
949
0
  frame_count = backtrace(frames, countof(frames));
950
#elif defined(HAVE_DBGHELP)
951
  frame_count = backtrace_win(frames, countof(frames));
952
#elif defined(WIN32)
953
  frame_count = CaptureStackBackTrace(skip, countof(frames), frames, NULL);
954
  skip = 0;
955
#endif
956
0
  frame_count = max(frame_count - skip, 0);
957
0
  this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
958
0
  memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
959
0
  this->frame_count = frame_count;
960
961
0
  this->public = get_methods();
962
963
0
  return &this->public;
964
0
}
965
966
/**
967
 * See header
968
 */
969
void backtrace_dump(char *label, FILE *file, bool detailed)
970
0
{
971
0
  backtrace_t *backtrace;
972
973
0
  backtrace = backtrace_create(2);
974
975
0
  if (label)
976
0
  {
977
0
    println(file, "Debug backtrace: %s", label);
978
0
  }
979
0
  backtrace->log(backtrace, file, detailed);
980
0
  backtrace->destroy(backtrace);
981
0
}