Skip to content

Commit 6ea3032

Browse files
committed
[Breakpad/Crashpad模块增强]: 改进崩溃报告系统,添加单元测试和示例代码
- **Breakpad模块重构**: 使用PIMPL模式改进封装,添加崩溃回调功能,支持手动生成minidump,增加单元测试和示例程序 - **Crashpad模块改进**: 同样使用PIMPL模式,增加配置获取接口,改进初始化流程,添加示例程序 - **工具函数添加**: 在utils中添加字符串转换工具函数,支持Windows平台的宽字符串转换 - **文档更新**: 更新README.md,详细描述各模块的文件结构和功能,修复格式问题 - **新增崩溃报告服务器**: 为Crashpad添加Python实现的崩溃报告服务器脚本,支持文件上传、列表查看和下载 - **构建系统更新**: 更新CMakeLists.txt,将测试程序重命名并链接必要的依赖库 - **代码清理**: 删除旧的main.cc文件,使用新的示例程序,改进代码结构和错误处理
1 parent 388512d commit 6ea3032

File tree

16 files changed

+991
-191
lines changed

16 files changed

+991
-191
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,4 @@ output
110110
packet
111111
releases
112112
install
113+
crash_reports

README.md

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,54 @@
99

1010
这个项目收集了各种 C++ 编程中的实用示例,涵盖了系统编程、加密算法、多线程、崩溃处理、设计模式等多个领域。每个示例都力求简洁明了,便于学习和在实际项目中使用。
1111

12-
## 代码结构
12+
## 模块详情
1313

1414
### 1. [Breakpad](src/Breakpad/)
1515

1616
Google Breakpad 的简单封装,用于应用程序崩溃报告和堆栈跟踪。
1717

18-
- `breakpad.hpp` / `breakpad.cc` - Breakpad 封装类
19-
- `main.cc` - 使用示例
18+
- **核心文件**:
19+
- `breakpad.hpp` / `breakpad.cc` - Breakpad 封装类
20+
- `example.cc` - 使用示例
21+
- `breakpad_unittest.cc` - 单元测试
2022

2123
### 2. [ByteOrder](src/ByteOrder/)
2224

2325
系统字节序检测工具。
2426

25-
- `byteorder.cc` - 判断系统的字节序(大端序/小端序)
27+
- **核心文件**:
28+
- `byteorder.cc` - 判断系统的字节序(大端序/小端序)
2629

2730
### 3. [Crashpad](src/Crashpad/)
2831

2932
Google Crashpad 的简单封装,现代化的崩溃报告系统。
3033

31-
- `crashpad.hpp` / `crashpad.cc` - Crashpad 封装类
32-
- `main.cc` - 使用示例
34+
- **核心文件**:
35+
- `crashpad.hpp` / `crashpad.cc` - Crashpad 封装类
36+
- `example.cc` - 使用示例
37+
- `crashpad_server.py` - 崩溃报告服务器脚本
3338

3439
### 4. [Memcpy](src/Memcpy/)
3540

3641
内存拷贝函数实现及相关测试。
3742

38-
- `mymemcpy.hpp` / `mymemcpy.cc` - 自定义 memcpy 函数实现
39-
- `mymemcpy_unittest.cc` - 单元测试
43+
- **核心文件**:
44+
- `mymemcpy.hpp` / `mymemcpy.cc` - 自定义 memcpy 函数实现
45+
- `mymemcpy_unittest.cc` - 单元测试
4046

4147
### 5. [MonitorDir](src/MonitorDir/)
4248

4349
跨平台目录监控实现,支持 Windows、macOS 和 Linux。
4450

45-
- `monitordir.hpp` - 目录监控接口
46-
- `monitordir_win.cc` - Windows 实现(使用 `ReadDirectoryChangesW`
47-
- `monitordir_mac.cc` - macOS 实现(使用 `FSEvents`
48-
- `monitordir_linux_inotify.cc` - Linux inotify 实现
49-
- `monitordir_linux_fanotify.cc` - Linux fanotify 实现
50-
- `main.cc` - 使用示例
51+
- **核心文件**:
52+
- `monitordir.hpp` - 目录监控接口
53+
- `monitordir_win.cc` - Windows 实现(使用 `ReadDirectoryChangesW`
54+
- `monitordir_mac.cc` - macOS 实现(使用 `FSEvents`
55+
- `monitordir_linux_inotify.cc` - Linux inotify 实现
56+
- `monitordir_linux_fanotify.cc` - Linux fanotify 实现
57+
- `main.cc` - 使用示例
5158

52-
**Linux 平台说明**
59+
**Linux 平台说明**:
5360

5461
- `fanotify` 使用全局模式(`FAN_MARK_FILESYSTEM`),可以监控整个文件系统的事件
5562
- 需要 `CAP_SYS_ADMIN` 能力(root 权限)
@@ -59,38 +66,48 @@ Google Crashpad 的简单封装,现代化的崩溃报告系统。
5966

6067
基于 efsw 库的目录监控示例。
6168

62-
- `main.cc` - 使用 efsw 实现的目录监控示例
69+
- **核心文件**:
70+
- `main.cc` - 使用 efsw 实现的目录监控示例
6371

6472
### 7. [OpenSSL](src/OpenSSL/)
6573

6674
OpenSSL 加密算法使用示例。
6775

68-
- `openssl_common.hpp` / `openssl_common.cc` - 公共工具函数
69-
- `openssl_aes.cc` - AES 加解密示例
70-
- `openssl_base64.cc` - Base64 编解码示例
71-
- `openssl_hash.cc` - SHA256 哈希计算示例
72-
- `openssl_rsa.cc` - RSA 加解密示例
76+
- **核心文件**:
77+
- `openssl_common.hpp` / `openssl_common.cc` - 公共工具函数
78+
- `openssl_aes.cc` - AES 加解密示例
79+
- `openssl_base64.cc` - Base64 编解码示例
80+
- `openssl_hash.cc` - SHA256 哈希计算示例
81+
- `openssl_rsa.cc` - RSA 加解密示例
7382

7483
### 8. [Singleton](src/Singleton/)
7584

7685
现代化线程安全的单例模式实现,使用 C++11 特性确保跨平台兼容性。
7786

78-
- `singleton.hpp` - 模板化的单例基类,提供线程安全的实例访问
79-
- `singleton_unitest.cc` - 完整的单元测试,验证单例的唯一性、线程安全性等特性
87+
- **核心文件**:
88+
- `singleton.hpp` - 模板化的单例基类,提供线程安全的实例访问
89+
- `singleton_unitest.cc` - 完整的单元测试,验证单例的唯一性、线程安全性等特性
8090

8191
### 9. [Thread](src/Thread/)
8292

8393
基于 std::jthread 实现的线程和线程池(注意:Apple Clang 不支持)。
8494

85-
- `thread.hpp` - 线程类封装
86-
- `threadpool.hpp` - 线程池实现
87-
- `queue.hpp` - 线程安全队列
88-
- `*_unittest.cc` - 各组件单元测试
95+
- **核心文件**:
96+
- `thread.hpp` - 线程类封装
97+
- `threadpool.hpp` - 线程池实现
98+
- `queue.hpp` - 线程安全队列
99+
- `thread_unittest.cc` - 线程单元测试
100+
- `threadpool_unittest.cc` - 线程池单元测试
101+
- `queue_unittest.cc` - 队列单元测试
89102

90103
### 10. [utils](src/utils/)
91104

92105
通用工具类。
93106

94-
- `scopeguard.hpp` - RAII 范围守卫
95-
- `object.hpp` - 对象工具类
96-
- `utils.hpp` - 通用工具函数
107+
- **核心文件**:
108+
- `scopeguard.hpp` - RAII 范围守卫
109+
- `object.hpp` - 对象工具类
110+
- `utils.hpp` / `utils.cc` - 通用工具函数
111+
-
112+
113+
s

src/Breakpad/CMakeLists.txt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@ if(unofficial-breakpad_FOUND)
33
message(STATUS "found unofficial-breakpad")
44
endif()
55

6-
add_executable(breakpad_test main.cc breakpad.cc breakpad.hpp)
6+
add_executable(breakpad_unittest breakpad_unittest.cc breakpad.cc breakpad.hpp
7+
${CMAKE_SOURCE_DIR}/src/utils/utils.cc)
78
target_link_libraries(
8-
breakpad_test PRIVATE unofficial::breakpad::libbreakpad
9-
unofficial::breakpad::libbreakpad_client)
9+
breakpad_unittest
10+
PRIVATE unofficial::breakpad::libbreakpad
11+
unofficial::breakpad::libbreakpad_client GTest::gtest
12+
GTest::gtest_main GTest::gmock GTest::gmock_main)
13+
add_test(NAME breakpad_unittest COMMAND breakpad_unittest)
14+
15+
add_executable(breakpad_example example.cc breakpad.cc breakpad.hpp
16+
${CMAKE_SOURCE_DIR}/src/utils/utils.cc)
17+
target_link_libraries(
18+
breakpad_example PRIVATE unofficial::breakpad::libbreakpad
19+
unofficial::breakpad::libbreakpad_client)

src/Breakpad/breakpad.cc

Lines changed: 147 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "breakpad.hpp"
22

3+
#include <utils/utils.hpp>
4+
35
#ifdef _WIN32
46
#include <client/windows/handler/exception_handler.h>
57
#elif __APPLE__
@@ -8,83 +10,177 @@
810
#include <client/linux/handler/exception_handler.h>
911
#endif
1012

11-
#include <codecvt>
13+
#include <filesystem>
1214
#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 {
1436

1537
#ifdef _WIN32
1638

17-
auto convertWideStringToUTF8(const wchar_t *wstr) -> std::string
39+
std::string toUtf8(const std::wstring &wstr)
1840
{
19-
if (wstr == nullptr) {
41+
if (wstr.empty())
2042
return {};
21-
}
2243

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)
2853
return {};
29-
}
3054

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;
4065
}
4166

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)
4873
{
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);
5377
}
78+
5479
#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)
5682
{
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);
6086
}
87+
6188
#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)
6393
{
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);
6896
}
97+
6998
#endif
7099

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()
72113
{
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+
73121
#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);
77128
#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+
84136
#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);
87139
#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;
88160
}
89161

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+
90166
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

Comments
 (0)