14d17a68eSDave Rigby/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
24d17a68eSDave Rigby/*
34d17a68eSDave Rigby *     Copyright 2015 Couchbase, Inc
44d17a68eSDave Rigby *
54d17a68eSDave Rigby *   Licensed under the Apache License, Version 2.0 (the "License");
64d17a68eSDave Rigby *   you may not use this file except in compliance with the License.
74d17a68eSDave Rigby *   You may obtain a copy of the License at
84d17a68eSDave Rigby *
94d17a68eSDave Rigby *       http://www.apache.org/licenses/LICENSE-2.0
104d17a68eSDave Rigby *
114d17a68eSDave Rigby *   Unless required by applicable law or agreed to in writing, software
124d17a68eSDave Rigby *   distributed under the License is distributed on an "AS IS" BASIS,
134d17a68eSDave Rigby *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144d17a68eSDave Rigby *   See the License for the specific language governing permissions and
154d17a68eSDave Rigby *   limitations under the License.
164d17a68eSDave Rigby */
174d17a68eSDave Rigby
184d17a68eSDave Rigby#include "config.h"
194d17a68eSDave Rigby
204d17a68eSDave Rigby#include "platform/internal_config.h"
214d17a68eSDave Rigby
224d17a68eSDave Rigby#include <platform/backtrace.h>
234d17a68eSDave Rigby#include <strings.h>
244d17a68eSDave Rigby
254d17a68eSDave Rigby#if defined(WIN32)
264d17a68eSDave Rigby#  define HAVE_BACKTRACE_SUPPORT 1
274d17a68eSDave Rigby#  include <Dbghelp.h>
284d17a68eSDave Rigby#endif
294d17a68eSDave Rigby
304d17a68eSDave Rigby#if defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR)
314d17a68eSDave Rigby#  define HAVE_BACKTRACE_SUPPORT 1
324d17a68eSDave Rigby#  include <execinfo.h> // for backtrace()
334d17a68eSDave Rigby#  include <dlfcn.h> // for dladdr()
344d17a68eSDave Rigby#  include <stddef.h> // for ptrdiff_t
354d17a68eSDave Rigby#endif
364d17a68eSDave Rigby
374d17a68eSDave Rigby// Maximum number of frames that will be printed.
384d17a68eSDave Rigby#define MAX_FRAMES 50
394d17a68eSDave Rigby
404d17a68eSDave Rigby#if defined(HAVE_BACKTRACE_SUPPORT)
414d17a68eSDave Rigby/**
424d17a68eSDave Rigby * Populates buf with a description of the given address in the program.
434d17a68eSDave Rigby **/
444d17a68eSDave Rigbystatic void describe_address(char* msg, size_t len, void* addr) {
454d17a68eSDave Rigby#if defined(WIN32)
464d17a68eSDave Rigby
474d17a68eSDave Rigby    // Get module information
484d17a68eSDave Rigby    IMAGEHLP_MODULE64 module_info;
494d17a68eSDave Rigby    module_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
504d17a68eSDave Rigby    SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)addr, &module_info);
514d17a68eSDave Rigby
524d17a68eSDave Rigby    // Get symbol information.
534d17a68eSDave Rigby    DWORD64 displacement = 0;
544d17a68eSDave Rigby    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
554d17a68eSDave Rigby    PSYMBOL_INFO sym_info = (PSYMBOL_INFO)buffer;
564d17a68eSDave Rigby    sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
574d17a68eSDave Rigby    sym_info->MaxNameLen = MAX_SYM_NAME;
584d17a68eSDave Rigby
594d17a68eSDave Rigby    if (SymFromAddr(GetCurrentProcess(), (DWORD64)addr, &displacement,
604d17a68eSDave Rigby                    sym_info)) {
614d17a68eSDave Rigby        snprintf(msg, len, "%s(%s+%lld) [0x%p]",
624d17a68eSDave Rigby                 module_info.ImageName ? module_info.ImageName : "",
634d17a68eSDave Rigby                 sym_info->Name, displacement, addr);
644d17a68eSDave Rigby    } else {
654d17a68eSDave Rigby        // No symbol found.
664d17a68eSDave Rigby        snprintf(msg, len, "[0x%p]", addr);
674d17a68eSDave Rigby    }
684d17a68eSDave Rigby#else // !WIN32
694d17a68eSDave Rigby    Dl_info info;
704d17a68eSDave Rigby    int status = dladdr(addr, &info);
714d17a68eSDave Rigby
724d17a68eSDave Rigby    if (status != 0 &&
734d17a68eSDave Rigby        info.dli_fname != NULL &&
744d17a68eSDave Rigby        info.dli_fname[0] != '\0') {
754d17a68eSDave Rigby
764d17a68eSDave Rigby        if (info.dli_saddr == 0) {
774d17a68eSDave Rigby            // No offset calculation possible.
784d17a68eSDave Rigby            snprintf(msg, len, "%s(%s) [%p]",
794d17a68eSDave Rigby                    info.dli_fname,
804d17a68eSDave Rigby                    info.dli_sname ? info.dli_sname : "",
814d17a68eSDave Rigby                    addr);
824d17a68eSDave Rigby        } else {
834d17a68eSDave Rigby            char sign;
844d17a68eSDave Rigby            ptrdiff_t offset;
854d17a68eSDave Rigby            if (addr >= info.dli_saddr) {
864d17a68eSDave Rigby                sign = '+';
874d17a68eSDave Rigby                offset = (char*)addr - (char*)info.dli_saddr;
884d17a68eSDave Rigby            } else {
894d17a68eSDave Rigby                sign = '-';
904d17a68eSDave Rigby                offset = (char*)info.dli_saddr - (char*)addr;
914d17a68eSDave Rigby            }
924d17a68eSDave Rigby            snprintf(msg, len, "%s(%s%c%#tx) [%p]",
934d17a68eSDave Rigby                    info.dli_fname,
944d17a68eSDave Rigby                    info.dli_sname ? info.dli_sname : "",
954d17a68eSDave Rigby                    sign, offset, addr);
964d17a68eSDave Rigby        }
974d17a68eSDave Rigby    } else {
984d17a68eSDave Rigby        // No symbol found.
994d17a68eSDave Rigby        snprintf(msg, len, "[%p]", addr);
1004d17a68eSDave Rigby    }
1014d17a68eSDave Rigby#endif // WIN32
1024d17a68eSDave Rigby}
1034d17a68eSDave Rigby
1044d17a68eSDave RigbyPLATFORM_PUBLIC_API
1054d17a68eSDave Rigbyvoid print_backtrace(write_cb_t write_cb, void* context) {
1064d17a68eSDave Rigby    void* frames[MAX_FRAMES];
1074d17a68eSDave Rigby#if defined(WIN32)
1084d17a68eSDave Rigby    int active_frames = CaptureStackBackTrace(0, MAX_FRAMES, frames, NULL);
1094d17a68eSDave Rigby    SymInitialize(GetCurrentProcess(), NULL, TRUE);
1104d17a68eSDave Rigby#else
1114d17a68eSDave Rigby    int active_frames = backtrace(frames, MAX_FRAMES);
1124d17a68eSDave Rigby#endif
1134d17a68eSDave Rigby
1144d17a68eSDave Rigby    // Note we start from 1 to skip our own frame.
1154d17a68eSDave Rigby    for (int ii = 1; ii < active_frames; ii++) {
1164d17a68eSDave Rigby        // Fixed-sized buffer; possible that description will be cropped.
1174d17a68eSDave Rigby        char msg[200];
1184d17a68eSDave Rigby        describe_address(msg, sizeof(msg), frames[ii]);
1194d17a68eSDave Rigby        write_cb(context, msg);
1204d17a68eSDave Rigby    }
1214d17a68eSDave Rigby    if (active_frames == MAX_FRAMES) {
1224d17a68eSDave Rigby        write_cb(context, "<frame limit reached, possible truncation>");
1234d17a68eSDave Rigby    }
1244d17a68eSDave Rigby}
1254d17a68eSDave Rigby
1264d17a68eSDave Rigby#else // if defined(HAVE_BACKTRACE_SUPPORT)
1274d17a68eSDave Rigby
1284d17a68eSDave RigbyPLATFORM_PUBLIC_API
1294d17a68eSDave Rigbyvoid print_backtrace(write_cb_t write_cb, void* context) {
1304d17a68eSDave Rigby    write_cb(context, "<backtrace not supported on this platform>");
1314d17a68eSDave Rigby}
1324d17a68eSDave Rigby
1334d17a68eSDave Rigby#endif // defined(HAVE_BACKTRACE_SUPPORT)