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