|
1 | 1 | #include "breakpad.hpp" |
2 | 2 |
|
| 3 | +#include <utils/utils.hpp> |
| 4 | + |
3 | 5 | #ifdef _WIN32 |
4 | 6 | #include <client/windows/handler/exception_handler.h> |
5 | 7 | #elif __APPLE__ |
|
8 | 10 | #include <client/linux/handler/exception_handler.h> |
9 | 11 | #endif |
10 | 12 |
|
11 | | -#include <codecvt> |
| 13 | +#include <filesystem> |
12 | 14 | #include <iostream> |
13 | | -#include <locale> |
| 15 | +#include <stdexcept> |
| 16 | + |
| 17 | +class BreakpadPrivate |
| 18 | +{ |
| 19 | +public: |
| 20 | + explicit BreakpadPrivate(const std::string &dump_path, int timeout_ms, Breakpad *q); |
| 21 | + ~BreakpadPrivate() = default; |
| 22 | + |
| 23 | + bool initialize(); |
| 24 | + |
| 25 | + bool handleCrash(const std::string &dump_path, bool succeeded); |
| 26 | + |
| 27 | + Breakpad *q_ptr; |
| 28 | + |
| 29 | + std::string dumpPath; |
| 30 | + int timeoutMS; |
| 31 | + Breakpad::CrashCallback crashCallback; |
| 32 | + std::unique_ptr<google_breakpad::ExceptionHandler> handlerPtr; |
| 33 | +}; |
| 34 | + |
| 35 | +namespace { |
14 | 36 |
|
15 | 37 | #ifdef _WIN32 |
16 | 38 |
|
17 | | -auto convertWideStringToUTF8(const wchar_t *wstr) -> std::string |
| 39 | +std::string toUtf8(const std::wstring &wstr) |
18 | 40 | { |
19 | | - if (wstr == nullptr) { |
| 41 | + if (wstr.empty()) |
20 | 42 | return {}; |
21 | | - } |
22 | 43 |
|
23 | | - // 首先,获取转换后的字符串长度(不包括空终止符) |
24 | | - int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr); |
25 | | - |
26 | | - // 如果转换失败,返回空字符串 |
27 | | - if (len == 0) { |
| 44 | + int size_needed = WideCharToMultiByte(CP_UTF8, |
| 45 | + 0, |
| 46 | + wstr.c_str(), |
| 47 | + (int) wstr.size(), |
| 48 | + nullptr, |
| 49 | + 0, |
| 50 | + nullptr, |
| 51 | + nullptr); |
| 52 | + if (size_needed == 0) |
28 | 53 | return {}; |
29 | | - } |
30 | 54 |
|
31 | | - // 分配足够的空间来存储转换后的字符串 |
32 | | - std::string utf8String(len, 0); |
33 | | - |
34 | | - // 执行转换 |
35 | | - WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &utf8String[0], len, nullptr, nullptr); |
36 | | - |
37 | | - // 去除末尾的空字符 |
38 | | - utf8String.resize(len - 1); |
39 | | - return utf8String; |
| 55 | + std::string str(size_needed, 0); |
| 56 | + WideCharToMultiByte(CP_UTF8, |
| 57 | + 0, |
| 58 | + wstr.c_str(), |
| 59 | + (int) wstr.size(), |
| 60 | + &str[0], |
| 61 | + size_needed, |
| 62 | + nullptr, |
| 63 | + nullptr); |
| 64 | + return str; |
40 | 65 | } |
41 | 66 |
|
42 | | -auto callback(const wchar_t *dump_path, |
43 | | - const wchar_t *id, |
44 | | - void *context, |
45 | | - EXCEPTION_POINTERS *exinfo, |
46 | | - MDRawAssertionInfo *assertion, |
47 | | - bool succeeded) -> bool |
| 67 | +bool windowsCallback(const wchar_t *dump_path, |
| 68 | + const wchar_t *id, |
| 69 | + void *context, |
| 70 | + EXCEPTION_POINTERS *exinfo, |
| 71 | + MDRawAssertionInfo *assertion, |
| 72 | + bool succeeded) |
48 | 73 | { |
49 | | - const auto *succeeded_str = succeeded ? "succeeded" : "fialed"; |
50 | | - auto dump_path_str = convertWideStringToUTF8(dump_path) + convertWideStringToUTF8(id); |
51 | | - std::cout << "Create dump file " << succeeded_str << " Dump path: " << dump_path_str << '\n'; |
52 | | - return succeeded; |
| 74 | + auto *private_impl = static_cast<BreakpadPrivate *>(context); |
| 75 | + std::string full_path = toUtf8(dump_path) + toUtf8(id) + ".dmp"; |
| 76 | + return private_impl->handleCrash(full_path, succeeded); |
53 | 77 | } |
| 78 | + |
54 | 79 | #elif __APPLE__ |
55 | | -bool callback(const char *dump_path, const char *id, void *context, bool succeeded) |
| 80 | + |
| 81 | +bool macCallback(const char *dump_path, const char *id, void *context, bool succeeded) |
56 | 82 | { |
57 | | - auto succeeded_str = succeeded ? "succeeded" : "fialed"; |
58 | | - std::cout << "Create dump file " << succeeded_str << " Dump path: " << dump_path << std::endl; |
59 | | - return succeeded; |
| 83 | + auto *private_impl = static_cast<BreakpadPrivate *>(context); |
| 84 | + std::string full_path = std::string(dump_path) + std::string(id) + ".dmp"; |
| 85 | + return private_impl->handleCrash(full_path, succeeded); |
60 | 86 | } |
| 87 | + |
61 | 88 | #elif __linux__ |
62 | | -bool callback(const google_breakpad::MinidumpDescriptor &descriptor, void *context, bool succeeded) |
| 89 | + |
| 90 | +bool linuxCallback(const google_breakpad::MinidumpDescriptor &descriptor, |
| 91 | + void *context, |
| 92 | + bool succeeded) |
63 | 93 | { |
64 | | - auto succeeded_str = succeeded ? "succeeded" : "fialed"; |
65 | | - std::cout << "Create dump file " << succeeded_str << " Dump path: " << descriptor.path() |
66 | | - << std::endl; |
67 | | - return succeeded; |
| 94 | + auto *private_impl = static_cast<BreakpadPrivate *>(context); |
| 95 | + return private_impl->handleCrash(descriptor.path(), succeeded); |
68 | 96 | } |
| 97 | + |
69 | 98 | #endif |
70 | 99 |
|
71 | | -Breakpad::Breakpad(const std::string &dump_path) |
| 100 | +} // namespace |
| 101 | + |
| 102 | +BreakpadPrivate::BreakpadPrivate(const std::string &dump_path, int timeout_ms, Breakpad *q) |
| 103 | + : q_ptr(q) |
| 104 | + , dumpPath(dump_path) |
| 105 | + , timeoutMS(timeout_ms) |
| 106 | +{ |
| 107 | + if (!initialize()) { |
| 108 | + throw std::runtime_error("Failed to initialize Breakpad"); |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +bool BreakpadPrivate::initialize() |
72 | 113 | { |
| 114 | + try { |
| 115 | + std::filesystem::create_directories(dumpPath); |
| 116 | + } catch (const std::filesystem::filesystem_error &e) { |
| 117 | + std::cerr << "Failed to create dump directory: " << e.what() << std::endl; |
| 118 | + return false; |
| 119 | + } |
| 120 | + |
73 | 121 | #ifdef _WIN32 |
74 | | - auto dump_path_w = std::wstring(dump_path.begin(), dump_path.end()); |
75 | | - handler_ptr = std::make_unique<google_breakpad::ExceptionHandler>( |
76 | | - dump_path_w, nullptr, callback, nullptr, google_breakpad::ExceptionHandler::HANDLER_ALL); |
| 122 | + handlerPtr = std::make_unique<google_breakpad::ExceptionHandler>( |
| 123 | + Utils::toWide(dumpPath), |
| 124 | + nullptr, |
| 125 | + &windowsCallback, |
| 126 | + this, |
| 127 | + google_breakpad::ExceptionHandler::HANDLER_ALL); |
77 | 128 | #elif __APPLE__ |
78 | | - handler_ptr = std::make_unique<google_breakpad::ExceptionHandler>(dump_path, |
79 | | - nullptr, |
80 | | - callback, |
81 | | - nullptr, |
82 | | - true, |
83 | | - nullptr); |
| 129 | + handlerPtr = std::make_unique<google_breakpad::ExceptionHandler>(dumpPath, |
| 130 | + nullptr, |
| 131 | + &macCallback, |
| 132 | + this, |
| 133 | + true, |
| 134 | + nullptr); |
| 135 | + |
84 | 136 | #elif __linux__ |
85 | | - handler_ptr = std::make_unique<google_breakpad::ExceptionHandler>( |
86 | | - google_breakpad::MinidumpDescriptor(dump_path), nullptr, callback, nullptr, true, -1); |
| 137 | + handlerPtr = std::make_unique<google_breakpad::ExceptionHandler>( |
| 138 | + google_breakpad::MinidumpDescriptor(dumpPath), nullptr, &linuxCallback, this, true, -1); |
87 | 139 | #endif |
| 140 | + |
| 141 | + if (!handlerPtr) { |
| 142 | + std::cerr << "Failed to create ExceptionHandler" << std::endl; |
| 143 | + return false; |
| 144 | + } |
| 145 | + |
| 146 | + return true; |
| 147 | +} |
| 148 | + |
| 149 | +bool BreakpadPrivate::handleCrash(const std::string &dump_path, bool succeeded) |
| 150 | +{ |
| 151 | + // 调用用户设置的回调函数 |
| 152 | + if (crashCallback) { |
| 153 | + return crashCallback(dump_path, succeeded); |
| 154 | + } |
| 155 | + |
| 156 | + // 默认处理:输出日志信息 |
| 157 | + std::cout << "Crash dump " << (succeeded ? "succeeded" : "failed") << ": " << dump_path |
| 158 | + << std::endl; |
| 159 | + return succeeded; |
88 | 160 | } |
89 | 161 |
|
| 162 | +Breakpad::Breakpad(const std::string &dump_path, int timeout_ms) |
| 163 | + : d_ptr(std::make_unique<BreakpadPrivate>(dump_path, timeout_ms, this)) |
| 164 | +{} |
| 165 | + |
90 | 166 | Breakpad::~Breakpad() = default; |
| 167 | + |
| 168 | +void Breakpad::setCrashCallback(CrashCallback callback) |
| 169 | +{ |
| 170 | + d_ptr->crashCallback = std::move(callback); |
| 171 | +} |
| 172 | + |
| 173 | +bool Breakpad::writeMinidump() |
| 174 | +{ |
| 175 | + return d_ptr->handlerPtr && d_ptr->handlerPtr->WriteMinidump(); |
| 176 | +} |
| 177 | + |
| 178 | +std::string Breakpad::getDumpPath() const |
| 179 | +{ |
| 180 | + return d_ptr->dumpPath; |
| 181 | +} |
| 182 | + |
| 183 | +int Breakpad::getTimeoutMs() const |
| 184 | +{ |
| 185 | + return d_ptr->timeoutMS; |
| 186 | +} |
0 commit comments