xref: /4.0.0/platform/src/backtrace.c (revision 4d17a68e)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2015 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include "config.h"
19
20#include "platform/internal_config.h"
21
22#include <platform/backtrace.h>
23#include <strings.h>
24
25#if defined(WIN32)
26#  define HAVE_BACKTRACE_SUPPORT 1
27#  include <Dbghelp.h>
28#endif
29
30#if defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR)
31#  define HAVE_BACKTRACE_SUPPORT 1
32#  include <execinfo.h> // for backtrace()
33#  include <dlfcn.h> // for dladdr()
34#  include <stddef.h> // for ptrdiff_t
35#endif
36
37// Maximum number of frames that will be printed.
38#define MAX_FRAMES 50
39
40#if defined(HAVE_BACKTRACE_SUPPORT)
41/**
42 * Populates buf with a description of the given address in the program.
43 **/
44static void describe_address(char* msg, size_t len, void* addr) {
45#if defined(WIN32)
46
47    // Get module information
48    IMAGEHLP_MODULE64 module_info;
49    module_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
50    SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)addr, &module_info);
51
52    // Get symbol information.
53    DWORD64 displacement = 0;
54    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
55    PSYMBOL_INFO sym_info = (PSYMBOL_INFO)buffer;
56    sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
57    sym_info->MaxNameLen = MAX_SYM_NAME;
58
59    if (SymFromAddr(GetCurrentProcess(), (DWORD64)addr, &displacement,
60                    sym_info)) {
61        snprintf(msg, len, "%s(%s+%lld) [0x%p]",
62                 module_info.ImageName ? module_info.ImageName : "",
63                 sym_info->Name, displacement, addr);
64    } else {
65        // No symbol found.
66        snprintf(msg, len, "[0x%p]", addr);
67    }
68#else // !WIN32
69    Dl_info info;
70    int status = dladdr(addr, &info);
71
72    if (status != 0 &&
73        info.dli_fname != NULL &&
74        info.dli_fname[0] != '\0') {
75
76        if (info.dli_saddr == 0) {
77            // No offset calculation possible.
78            snprintf(msg, len, "%s(%s) [%p]",
79                    info.dli_fname,
80                    info.dli_sname ? info.dli_sname : "",
81                    addr);
82        } else {
83            char sign;
84            ptrdiff_t offset;
85            if (addr >= info.dli_saddr) {
86                sign = '+';
87                offset = (char*)addr - (char*)info.dli_saddr;
88            } else {
89                sign = '-';
90                offset = (char*)info.dli_saddr - (char*)addr;
91            }
92            snprintf(msg, len, "%s(%s%c%#tx) [%p]",
93                    info.dli_fname,
94                    info.dli_sname ? info.dli_sname : "",
95                    sign, offset, addr);
96        }
97    } else {
98        // No symbol found.
99        snprintf(msg, len, "[%p]", addr);
100    }
101#endif // WIN32
102}
103
104PLATFORM_PUBLIC_API
105void print_backtrace(write_cb_t write_cb, void* context) {
106    void* frames[MAX_FRAMES];
107#if defined(WIN32)
108    int active_frames = CaptureStackBackTrace(0, MAX_FRAMES, frames, NULL);
109    SymInitialize(GetCurrentProcess(), NULL, TRUE);
110#else
111    int active_frames = backtrace(frames, MAX_FRAMES);
112#endif
113
114    // Note we start from 1 to skip our own frame.
115    for (int ii = 1; ii < active_frames; ii++) {
116        // Fixed-sized buffer; possible that description will be cropped.
117        char msg[200];
118        describe_address(msg, sizeof(msg), frames[ii]);
119        write_cb(context, msg);
120    }
121    if (active_frames == MAX_FRAMES) {
122        write_cb(context, "<frame limit reached, possible truncation>");
123    }
124}
125
126#else // if defined(HAVE_BACKTRACE_SUPPORT)
127
128PLATFORM_PUBLIC_API
129void print_backtrace(write_cb_t write_cb, void* context) {
130    write_cb(context, "<backtrace not supported on this platform>");
131}
132
133#endif // defined(HAVE_BACKTRACE_SUPPORT)
134