source: XIOS3/trunk/extern/cpptrace/src/object.hpp

Last change on this file was 2573, checked in by ymipsl, 10 months ago

create new external source lib : cpptrace, for statck trace output
YM

File size: 5.5 KB
Line 
1#ifndef OBJECT_HPP
2#define OBJECT_HPP
3
4#include "common.hpp"
5
6#include <string>
7#include <vector>
8#include <mutex>
9#include <unordered_map>
10
11#if IS_LINUX || IS_APPLE
12 #include <unistd.h>
13 #include <dlfcn.h>
14 #if IS_APPLE
15  #include "mach-o.hpp"
16 #else
17  #include "elf.hpp"
18 #endif
19#elif IS_WINDOWS
20 #include <windows.h>
21 #include "pe.hpp"
22#endif
23
24struct dlframe {
25    std::string obj_path;
26    std::string symbol;
27    uintptr_t raw_address = 0;
28    uintptr_t obj_address = 0;
29};
30
31#if IS_LINUX || IS_APPLE
32#if !IS_APPLE
33static uintptr_t get_module_image_base(const std::string& obj_path) {
34    static std::mutex mutex;
35    std::lock_guard<std::mutex> lock(mutex);
36    static std::unordered_map<std::string, uintptr_t> cache;
37    auto it = cache.find(obj_path);
38    if(it == cache.end()) {
39        // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
40        // have two threads try to do the same computation
41        auto base = elf_get_module_image_base(obj_path);
42        cache.insert(it, {obj_path, base});
43        return base;
44    } else {
45        return it->second;
46    }
47}
48#else
49static uintptr_t get_module_image_base(const std::string& obj_path) {
50    // We have to parse the Mach-O to find the offset of the text section.....
51    // I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
52    // now that there is only one, and I'm using only the first section entry within that load command.
53    static std::mutex mutex;
54    std::lock_guard<std::mutex> lock(mutex);
55    static std::unordered_map<std::string, uintptr_t> cache;
56    auto it = cache.find(obj_path);
57    if(it == cache.end()) {
58        // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
59        // have two threads try to do the same computation
60        auto base = macho_get_text_vmaddr(obj_path.c_str());
61        cache.insert(it, {obj_path, base});
62        return base;
63    } else {
64        return it->second;
65    }
66}
67#endif
68// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
69static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) {
70    // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
71    std::vector<dlframe> frames;
72    frames.reserve(addrs.size());
73    for(const void* addr : addrs) {
74        Dl_info info;
75        dlframe frame;
76        frame.raw_address = reinterpret_cast<uintptr_t>(addr);
77        if(dladdr(addr, &info)) { // thread safe
78            // dli_sname and dli_saddr are only present with -rdynamic, sname will be included
79            // but we don't really need dli_saddr
80            frame.obj_path = info.dli_fname;
81            frame.obj_address = reinterpret_cast<uintptr_t>(addr)
82                                - reinterpret_cast<uintptr_t>(info.dli_fbase)
83                                + get_module_image_base(info.dli_fname);
84            frame.symbol = info.dli_sname ?: "";
85        }
86        frames.push_back(frame);
87    }
88    return frames;
89}
90#else
91static std::string get_module_name(HMODULE handle) {
92    static std::mutex mutex;
93    std::lock_guard<std::mutex> lock(mutex);
94    static std::unordered_map<HMODULE, std::string> cache;
95    auto it = cache.find(handle);
96    if(it == cache.end()) {
97        char path[MAX_PATH];
98        if(GetModuleFileNameA(handle, path, sizeof(path))) {
99            ///fprintf(stderr, "path: %s base: %p\n", path, handle);
100            cache.insert(it, {handle, path});
101            return path;
102        } else {
103            fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
104            cache.insert(it, {handle, ""});
105            return "";
106        }
107    } else {
108        return it->second;
109    }
110}
111static uintptr_t get_module_image_base(const std::string& obj_path) {
112    static std::mutex mutex;
113    std::lock_guard<std::mutex> lock(mutex);
114    static std::unordered_map<std::string, uintptr_t> cache;
115    auto it = cache.find(obj_path);
116    if(it == cache.end()) {
117        // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
118        // have two threads try to do the same computation
119        auto base = pe_get_module_image_base(obj_path);
120        cache.insert(it, {obj_path, base});
121        return base;
122    } else {
123        return it->second;
124    }
125}
126// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
127static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) {
128    // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
129    std::vector<dlframe> frames;
130    frames.reserve(addrs.size());
131    for(const void* addr : addrs) {
132        dlframe frame;
133        frame.raw_address = reinterpret_cast<uintptr_t>(addr);
134        HMODULE handle;
135        // Multithread safe as long as another thread doesn't come along and free the module
136        if(GetModuleHandleExA(
137            GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
138            static_cast<const char*>(addr),
139            &handle
140        )) {
141            frame.obj_path = get_module_name(handle);
142            frame.obj_address = reinterpret_cast<uintptr_t>(addr)
143                                - reinterpret_cast<uintptr_t>(handle)
144                                + get_module_image_base(frame.obj_path);
145        } else {
146            fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
147        }
148        frames.push_back(frame);
149    }
150    return frames;
151}
152#endif
153
154#endif
Note: See TracBrowser for help on using the repository browser.