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  **/
describe_address(char* msg, size_t len, void* addr)44 static 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 
104 PLATFORM_PUBLIC_API
print_backtrace(write_cb_t write_cb, void* context)105 void 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 
128 PLATFORM_PUBLIC_API
print_backtrace(write_cb_t write_cb, void* context)129 void 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