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/backtrace.h>
21#include <strings.h>
22
23#if defined(WIN32) && defined(HAVE_BACKTRACE_SUPPORT)
24#include <Dbghelp.h>
25#endif
26
27#if defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR)
28#  define HAVE_BACKTRACE_SUPPORT 1
29#  include <execinfo.h> // for backtrace()
30#  include <dlfcn.h> // for dladdr()
31#  include <stddef.h> // for ptrdiff_t
32#endif
33
34// Maximum number of frames that will be printed.
35#define MAX_FRAMES 50
36
37#if defined(HAVE_BACKTRACE_SUPPORT)
38/**
39 * Populates buf with a description of the given address in the program.
40 **/
41static void describe_address(char* msg, size_t len, void* addr) {
42#if defined(WIN32)
43
44    // Get module information
45    IMAGEHLP_MODULE64 module_info;
46    module_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
47    SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)addr, &module_info);
48
49    // Get symbol information.
50    DWORD64 displacement = 0;
51    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
52    PSYMBOL_INFO sym_info = (PSYMBOL_INFO)buffer;
53    sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
54    sym_info->MaxNameLen = MAX_SYM_NAME;
55
56    if (SymFromAddr(GetCurrentProcess(), (DWORD64)addr, &displacement,
57                    sym_info)) {
58        snprintf(msg, len, "%s(%s+%lld) [0x%p]",
59                 module_info.ImageName ? module_info.ImageName : "",
60                 sym_info->Name, displacement, addr);
61    } else {
62        // No symbol found.
63        snprintf(msg, len, "[0x%p]", addr);
64    }
65#else // !WIN32
66    Dl_info info;
67    int status = dladdr(addr, &info);
68
69    if (status != 0) {
70        ptrdiff_t image_offset = (char*)addr - (char*)info.dli_fbase;
71        if (info.dli_fname != NULL && info.dli_fname[0] != '\0') {
72            // Found a nearest symbol - print it.
73            if (info.dli_saddr == 0) {
74                // No function offset calculation possible.
75                snprintf(msg,
76                         len,
77                         "%s(%s) [%p+0x%" PRIx64 "]",
78                         info.dli_fname,
79                         info.dli_sname ? info.dli_sname : "",
80                         info.dli_fbase,
81                         (uint64_t)image_offset);
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,
93                         len,
94                         "%s(%s%c%#tx) [%p+0x%" PRIx64 "]",
95                         info.dli_fname,
96                         info.dli_sname ? info.dli_sname : "",
97                         sign,
98                         offset,
99                         info.dli_fbase,
100                         (uint64_t)image_offset);
101            }
102        } else {
103            // No function found; just print library name and offset.
104            snprintf(msg,
105                     len,
106                     "%s [%p+0x%" PRIx64 "]",
107                     info.dli_fname,
108                     info.dli_fbase,
109                     (uint64_t)image_offset);
110        }
111    } else {
112        // dladdr failed.
113        snprintf(msg, len, "[%p]", addr);
114    }
115#endif // WIN32
116}
117
118PLATFORM_PUBLIC_API
119void print_backtrace(write_cb_t write_cb, void* context) {
120    void* frames[MAX_FRAMES];
121#if defined(WIN32)
122    int active_frames = CaptureStackBackTrace(0, MAX_FRAMES, frames, NULL);
123    SymInitialize(GetCurrentProcess(), NULL, TRUE);
124#else
125    int active_frames = backtrace(frames, MAX_FRAMES);
126#endif
127
128    // Note we start from 1 to skip our own frame.
129    for (int ii = 1; ii < active_frames; ii++) {
130        // Fixed-sized buffer; possible that description will be cropped.
131        char msg[300];
132        describe_address(msg, sizeof(msg), frames[ii]);
133        write_cb(context, msg);
134    }
135    if (active_frames == MAX_FRAMES) {
136        write_cb(context, "<frame limit reached, possible truncation>");
137    }
138}
139
140#else // if defined(HAVE_BACKTRACE_SUPPORT)
141
142PLATFORM_PUBLIC_API
143void print_backtrace(write_cb_t write_cb, void* context) {
144    write_cb(context, "<backtrace not supported on this platform>");
145}
146
147#endif // defined(HAVE_BACKTRACE_SUPPORT)
148
149static void print_to_file_cb(void* ctx, const char* frame) {
150    fprintf(ctx, "\t%s\n", frame);
151}
152
153PLATFORM_PUBLIC_API
154void print_backtrace_to_file(FILE* stream) {
155    print_backtrace(print_to_file_cb, stream);
156}
157
158struct context {
159    const char *indent;
160    char *buffer;
161    size_t size;
162    size_t offset;
163    bool error;
164};
165
166static void memory_cb(void* ctx, const char* frame) {
167    struct context *c = ctx;
168
169
170    if (!c->error) {
171
172        int length = snprintf(c->buffer + c->offset, c->size - c->offset,
173                              "%s%s\n", c->indent, frame);
174
175        if ((length < 0) ||
176            (length >= (c->size - c->offset))) {
177            c->error = true;
178        } else {
179            c->offset += length;
180        }
181    }
182}
183
184PLATFORM_PUBLIC_API
185bool print_backtrace_to_buffer(const char *indent, char *buffer, size_t size) {
186    struct context c = {
187        .indent = indent,
188        .buffer = buffer,
189        .size = size,
190        .offset = 0,
191        .error = false};
192    print_backtrace(memory_cb, &c);
193    return !c.error;
194}
195