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