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 **/
describe_address(char* msg, size_t len, void* addr)41 static 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
118 PLATFORM_PUBLIC_API
print_backtrace(write_cb_t write_cb, void* context)119 void 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
142 PLATFORM_PUBLIC_API
print_backtrace(write_cb_t write_cb, void* context)143 void 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
print_to_file_cb(void* ctx, const char* frame)149 static void print_to_file_cb(void* ctx, const char* frame) {
150 fprintf(ctx, "\t%s\n", frame);
151 }
152
153 PLATFORM_PUBLIC_API
print_backtrace_to_file(FILE* stream)154 void print_backtrace_to_file(FILE* stream) {
155 print_backtrace(print_to_file_cb, stream);
156 }
157
158 struct context {
159 const char *indent;
160 char *buffer;
161 size_t size;
162 size_t offset;
163 bool error;
164 };
165
memory_cb(void* ctx, const char* frame)166 static 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
184 PLATFORM_PUBLIC_API
print_backtrace_to_buffer(const char *indent, char *buffer, size_t size)185 bool 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