commit 079c27652d14a47f106e84a5d50191e773634ec3 Author: taynpg Date: Mon Mar 23 16:42:28 2026 +0800 mingw崩溃日志分析。 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8d63d3f --- /dev/null +++ b/.clang-format @@ -0,0 +1,20 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +PointerAlignment: Left +AccessModifierOffset: -4 +ReflowComments: true +SpacesBeforeTrailingComments: 3 +AllowShortFunctionsOnASingleLine: None +AllowShortEnumsOnASingleLine: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true + AfterClass: true +TabWidth: 4 +ColumnLimit: 130 +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^<.*>' + Priority: 1 + - Regex: '^".*"' + Priority: 2 diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..40557fb --- /dev/null +++ b/.clangd @@ -0,0 +1,12 @@ +Hover: + ShowAKA: Yes +Diagnostics: + UnusedIncludes: None # 禁用未使用头文件提示 + Suppress: [ + anon_type_definition, # 禁用匿名的typedef提示 + unused-variable, # 禁用未使用变量提示 + unused-function, # 禁用未使用函数提示 + unused-includes, + ] + ClangTidy: + Remove: misc-unused-alias-decls diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe107f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +.cache/ +.qtcreator/ +.vs +out/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c69d75a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cxxLibrary"] + path = cxxLibrary + url = https://www.sinxmiao.cn/taynpg/cxxLibrary diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9522dd0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,150 @@ +{ + "files.autoSave": "onFocusChange", + "editor.fontSize": 15, + "editor.fontFamily": "'Mononoki Nerd Font Mono Regular', 'Mononoki Nerd Font Mono Regular', 'Mononoki Nerd Font Mono Regular'", + "editor.wordWrap": "on", + "terminal.integrated.fontFamily": "Mononoki Nerd Font Mono Regular", + "cmake.configureOnOpen": true, + //"C_Cpp.intelliSenseEngine": "disabled", + "cmake.debugConfig": { + "console": "externalTerminal", + "setupCommands": [ + { + "description": "-gdb-set charset utf-8", + "text": "-gdb-set charset UTF-8" + }, + { + "description": "Enable gdb pretty-printing", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + //"visualizerFile": "${workspaceRoot}/.vscode/qt6.natvis", + "args": [ + "-n", + "COT1", + "-show" + ] + }, + "cmake.configureArgs": [ + "-Wno-dev" + ], + // "cmake.configureSettings": { + // "CMAKE_TOOLCHAIN_FILE": "${env:VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + // }, + + "cmake.options.statusBarVisibility": "visible", + "cmake.generator": "Ninja", + "C_Cpp.default.compileCommands": "${workspaceRoot}/build/compile_commands.json", + "C_Cpp.default.cppStandard": "c++17", + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "editor.inlayHints.enabled": "off", + "editor.unicodeHighlight.allowedLocales": { + "ja": true, + "zh-hant": true, + "zh-hans": true + }, + "files.associations": { + "*.cfg": "json", + "xstring": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "condition_variable": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "filesystem": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xmemory0": "cpp", + "xstddef": "cpp", + "xtr1common": "cpp", + "xtree": "cpp", + "xutility": "cpp", + "bit": "cpp", + "compare": "cpp", + "concepts": "cpp", + "coroutine": "cpp", + "format": "cpp", + "stop_token": "cpp", + "bitset": "cpp", + "complex": "cpp", + "cstdarg": "cpp", + "set": "cpp", + "variant": "cpp", + "expected": "cpp", + "source_location": "cpp", + "regex": "cpp", + "*.in": "cpp", + "deque": "cpp", + "future": "cpp", + "queue": "cpp", + "resumable": "cpp", + "any": "cpp", + "codecvt": "cpp", + "csignal": "cpp", + "cwctype": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "shared_mutex": "cpp", + "cinttypes": "cpp", + "cfenv": "cpp", + "unordered_set": "cpp", + "ranges": "cpp", + "typeindex": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2cfb992 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +project(mingwSpecial LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 17) + +if (WIN32) + add_definitions(-D_WIN32_WINNT=0x0602) +endif() + +add_subdirectory(cxxLibrary) +add_subdirectory(dumpDemo) diff --git a/cxxLibrary b/cxxLibrary new file mode 160000 index 0000000..6daac72 --- /dev/null +++ b/cxxLibrary @@ -0,0 +1 @@ +Subproject commit 6daac723a939dd1d4eb69d58c6b45ff4eb75ac4d diff --git a/dumpDemo/CMakeLists.txt b/dumpDemo/CMakeLists.txt new file mode 100644 index 0000000..a7aa7a6 --- /dev/null +++ b/dumpDemo/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.16) + +project(dumpDemo LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 17) + +add_executable(dumpDemo crashdump.h crashdump.cpp main.cpp) +add_executable(dumpDemoParse dump_parse.cpp) +target_link_libraries(dumpDemoParse PRIVATE cxxLibrary) \ No newline at end of file diff --git a/dumpDemo/crashdump.cpp b/dumpDemo/crashdump.cpp new file mode 100644 index 0000000..10eb09f --- /dev/null +++ b/dumpDemo/crashdump.cpp @@ -0,0 +1,176 @@ +#include "crashdump.h" + +#include +#include + +CrashDump::CrashDump(EXCEPTION_POINTERS* exp) : exp_(exp) +{ +} + +std::string CrashDump::GetModuleByReAddr(PBYTE retAddr, PBYTE& moduleAddr) +{ + MODULEENTRY32 M = {sizeof(M)}; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); + char moduleName[MAX_PATH]{}; + if (hSnapshot != INVALID_HANDLE_VALUE && Module32First(hSnapshot, &M)) { + do { + if (DWORD(retAddr - M.modBaseAddr) < M.modBaseSize) { + std::snprintf(moduleName, MAX_PATH, "%s", M.szExePath); + moduleAddr = M.modBaseAddr; + break; + } + } while (Module32Next(hSnapshot, &M)); + } + std::string name(moduleName); + return name; +} + +std::string CrashDump::GetVersion() +{ + OSVERSIONINFO V = {sizeof(OSVERSIONINFO)}; + if (!GetVersionEx((POSVERSIONINFO)&V)) { + ZeroMemory(&V, sizeof(V)); + V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx((POSVERSIONINFO)&V); + } + if (V.dwPlatformId != VER_PLATFORM_WIN32_NT) { + V.dwBuildNumber = LOWORD(V.dwBuildNumber); + } + char buffer[512]{}; + std::snprintf(buffer, sizeof(buffer), "Windows: %ld.%ld.%ld, %s.", V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, + V.szCSDVersion); + std::string info(buffer); + return info; +} + +std::string CrashDump::GetExceptionInfo() +{ + char moduleName[MAX_PATH]{}; + PBYTE moduleAddr{}; + GetModuleFileName(NULL, moduleName, MAX_PATH); + if (exp_ == nullptr) { + return "Not Set Exception Pointer."; + } + std::ostringstream oss; + auto& E = exp_->ExceptionRecord; + auto& C = exp_->ContextRecord; + oss << "ExceptionAddr: " << E->ExceptionAddress << "\n"; + oss << "ExceptionCode: " << E->ExceptionCode << "\n"; + if (E->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + oss << (E->ExceptionInformation[0] ? "Write " : "Read "); + oss << E->ExceptionInformation[1] << "\n"; + } + oss << "Instruction:"; + for (int i = 0; i < 16; i++) { + oss << " " << std::hex << std::setw(2) << std::setfill('0') << static_cast(PBYTE(E->ExceptionAddress)[i]); + } + oss << std::hex << std::setfill('0'); + + // 1. 基本寄存器组 + oss << "\nGeneral Purpose Registers:" + << "\nRAX: " << std::setw(16) << C->Rax << " RBX: " << std::setw(16) << C->Rbx << " RCX: " << std::setw(16) << C->Rcx + << " RDX: " << std::setw(16) << C->Rdx + + << "\nRSI: " << std::setw(16) << C->Rsi << " RDI: " << std::setw(16) << C->Rdi << " RBP: " << std::setw(16) << C->Rbp + << " RSP: " << std::setw(16) << C->Rsp + + << "\nR8: " << std::setw(16) << C->R8 << " R9: " << std::setw(16) << C->R9 << " R10: " << std::setw(16) << C->R10 + << " R11: " << std::setw(16) << C->R11 + + << "\nR12: " << std::setw(16) << C->R12 << " R13: " << std::setw(16) << C->R13 << " R14: " << std::setw(16) << C->R14 + << " R15: " << std::setw(16) << C->R15; + + // 2. 关键控制寄存器 + oss << "\n\nControl Registers:" + << "\nRIP: " << std::setw(16) << C->Rip << " EFLAGS: " << std::setw(8) << C->EFlags << " CS: " << std::setw(4) + << C->SegCs << " SS: " << std::setw(4) << C->SegSs; + + // 3. 调试寄存器(如果有用) + if (C->ContextFlags & CONTEXT_DEBUG_REGISTERS) { + oss << "\n\nDebug Registers:" + << "\nDR0: " << std::setw(16) << C->Dr0 << " DR1: " << std::setw(16) << C->Dr1 << " DR2: " << std::setw(16) + << C->Dr2 << " DR3: " << std::setw(16) << C->Dr3 << "\nDR6: " << std::setw(16) << C->Dr6 + << " DR7: " << std::setw(16) << C->Dr7; + } + + // 4. MXCSR状态(浮点/SSE) + if (C->ContextFlags & CONTEXT_FLOATING_POINT) { + oss << "\n\nFPU/SSE State:" + << "\nMXCSR: " << std::setw(8) << C->MxCsr; + } + + // 5. 最后分支记录(如果可用) + if (C->LastBranchToRip || C->LastBranchFromRip) { + oss << "\n\nLast Branch:" + << "\nFrom: " << std::setw(16) << C->LastBranchFromRip << " To: " << std::setw(16) << C->LastBranchToRip; + } + + oss << GetCallStack(exp_) << "\n"; + + return oss.str(); +} + +std::string CrashDump::GetCallStack(EXCEPTION_POINTERS* exp) +{ + struct StackFrame { + StackFrame* prev_frame; + void* return_address; + }; + + std::ostringstream oss; + StackFrame* current_frame = nullptr; + + // 1. 初始化栈帧指针 + if (exp) { + // 异常情况:从异常上下文中获取栈帧 +#if defined(_M_X64) || defined(__x86_64__) + current_frame = reinterpret_cast(exp->ContextRecord->Rbp); +#else + current_frame = reinterpret_cast(exp->ContextRecord->Ebp); +#endif + } else { + // 非异常情况:手动获取当前栈帧(编译器相关) +#if defined(__GNUC__) || defined(__clang__) + // GCC/Clang 内联汇编获取 EBP/RBP +#if defined(__x86_64__) + register void* frame_ptr asm("rbp"); +#else + register void* frame_ptr asm("ebp"); +#endif + current_frame = reinterpret_cast(frame_ptr); +#elif defined(_MSC_VER) + // MSVC 内置函数(如果可用) + current_frame = reinterpret_cast(_AddressOfReturnAddress() - sizeof(void*)); +#else + return "Stack walking not supported on this compiler"; +#endif + } + + // 2. 遍历调用栈 + constexpr size_t max_frames = 64; + PBYTE module_addr = nullptr; + for (size_t i = 0; i < max_frames && current_frame; ++i) { + // 安全检查 + if (IsBadReadPtr(current_frame, sizeof(StackFrame)) || + IsBadCodePtr(reinterpret_cast(current_frame->return_address))) { + break; + } + + // 格式化输出 + oss << "\n0x" << std::hex << std::setw(8) << std::setfill('0') + << reinterpret_cast(current_frame->return_address) << " "; + + // 获取模块信息 + std::string module_name = GetModuleByReAddr(reinterpret_cast(current_frame->return_address), module_addr); + if (!module_name.empty()) { + DWORD rva = static_cast(reinterpret_cast(current_frame->return_address) - module_addr); + // 输出模块信息和RVA + oss << " (RVA: 0x" << std::hex << std::setw(8) << std::setfill('0') << rva << ") " << module_name; + } + + // 移动到上一栈帧 + current_frame = current_frame->prev_frame; + } + + return oss.str(); +} diff --git a/dumpDemo/crashdump.h b/dumpDemo/crashdump.h new file mode 100644 index 0000000..0a118a7 --- /dev/null +++ b/dumpDemo/crashdump.h @@ -0,0 +1,25 @@ +#ifndef CRASHDUMP_H +#define CRASHDUMP_H + +#include +#include +#include +#include +#include + +class CrashDump +{ +public: + CrashDump(EXCEPTION_POINTERS* exp); + +public: + std::string GetModuleByReAddr(PBYTE retAddr, PBYTE& moduleAddr); + std::string GetVersion(); + std::string GetExceptionInfo(); + std::string GetCallStack(EXCEPTION_POINTERS* exp); + +private: + EXCEPTION_POINTERS* exp_{}; +}; + +#endif // CRASHDUMP_H diff --git a/dumpDemo/dump_parse.cpp b/dumpDemo/dump_parse.cpp new file mode 100644 index 0000000..8271775 --- /dev/null +++ b/dumpDemo/dump_parse.cpp @@ -0,0 +1,338 @@ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COLOR_RESET "\033[0m" +#define COLOR_RED "\033[31m" +#define COLOR_GREEN "\033[32m" +#define COLOR_YELLOW "\033[33m" +#define COLOR_BLUE "\033[34m" +#define COLOR_MAGENTA "\033[35m" +#define COLOR_CYAN "\033[36m" + +bool is_utf8(const std::string& data) +{ + for (size_t i = 0; i < data.size();) { + uint8_t c = static_cast(data[i]); + if (c <= 0x7F) { // ASCII + i++; + } else if ((c & 0xE0) == 0xC0) { // 2字节 + if (i + 1 >= data.size() || (data[i + 1] & 0xC0) != 0x80) + return false; + i += 2; + } else if ((c & 0xF0) == 0xE0) { // 3字节 + if (i + 2 >= data.size() || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80) + return false; + i += 3; + } else if ((c & 0xF8) == 0xF0) { // 4字节 + if (i + 3 >= data.size() || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || + (data[i + 3] & 0xC0) != 0x80) + return false; + i += 4; + } else { + return false; // 非法UTF-8序列 + } + } + return true; +} + +std::string to_u8(const std::string& str) +{ + if (str.empty()) { + return ""; + } + + // 1. ANSI (当前代码页) → UTF-16 (WideChar) + int wide_len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0); + if (wide_len == 0) { + return ""; + } + + std::wstring wide_str(wide_len, L'\0'); + MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wide_str[0], wide_len); + + // 2. UTF-16 → UTF-8 + int utf8_len = WideCharToMultiByte(CP_UTF8, 0, wide_str.c_str(), -1, nullptr, 0, nullptr, nullptr); + if (utf8_len == 0) { + return ""; + } + + std::string utf8_str(utf8_len, '\0'); + WideCharToMultiByte(CP_UTF8, 0, wide_str.c_str(), -1, &utf8_str[0], utf8_len, nullptr, nullptr); + + // 移除末尾的\0 + if (!utf8_str.empty() && utf8_str.back() == '\0') { + utf8_str.pop_back(); + } + + return utf8_str; +} + +void enable_ansi_color() +{ +#if defined(_WIN32) + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD mode = 0; + GetConsoleMode(hConsole, &mode); + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hConsole, mode); +#endif +} + +void PrintSource(const std::string& str) +{ + std::string location = str; + size_t colon_pos = location.find_last_of(':'); + if (colon_pos == std::string::npos) { + return; // 格式不符 + } + + std::string key("at "); + auto sp = location.find(key); + std::string file_path = location.substr(sp + key.size(), colon_pos - sp - key.size()); + int line_number = std::stoi(location.substr(colon_pos + 1)); + + // 读取源文件并提取对应行 + + int upper = line_number + 5; + int lower = line_number - 5; + lower = lower < 1 ? 1 : lower; + + boost::nowide::ifstream source_file(file_path); + if (!source_file) { + boost::nowide::cerr << "无法打开源文件: " << file_path << std::endl; + return; + } + + std::string line_content; + int current_line = 0; + while (std::getline(source_file, line_content)) { + current_line++; + if (current_line >= lower && current_line <= upper) { + if (current_line == line_number) { + boost::nowide::cout << COLOR_YELLOW << current_line << ":" << line_content << COLOR_RESET << std::endl; + } else { + boost::nowide::cout << current_line << ":" << line_content << std::endl; + } + } + if (current_line > upper) { + break; + } + } +} + +std::pair exe_cmd(const std::string& exe, const std::vector& args) +{ + boost::asio::io_context ctx; + auto bin_path = boost::process::environment::find_executable(exe); + if (bin_path.empty()) { + return {-1, "Error: Command not found: " + exe}; + } + + boost::asio::readable_pipe rp_out(ctx), rp_err(ctx); + std::string out, err; + + try { + // 启动进程 + boost::process::process proc = + boost::process::process(ctx, bin_path, args, boost::process::process_stdio{{}, rp_out, rp_err}); + + // 同步读取(可能阻塞) + boost::system::error_code ec_out, ec_err; + boost::asio::read(rp_out, boost::asio::dynamic_buffer(out), ec_out); + boost::asio::read(rp_err, boost::asio::dynamic_buffer(err), ec_err); + + proc.wait(); + return {proc.exit_code(), out + (err.empty() ? "" : "\n" + err)}; + } catch (const std::exception& e) { + return {-1, std::string("Exception: ") + e.what()}; + } catch (...) { + return {-1, "Unknown exception"}; + } +} + +class CrashAnalyzer +{ +public: + CrashAnalyzer(const std::string& logPath, const std::string& exePath) : m_logPath(logPath), m_exePath(exePath) + { + } + + bool Analyze() + { + if (!CheckTools()) + return false; + if (!ParseLog()) + return false; + return ResolveSymbols(); + } + +private: + struct StackFrame { + uint64_t address; + uint64_t rva; + std::string module; + }; + + bool CheckTools() + { + auto check = [](const std::string& cmd) { + auto r = exe_cmd(cmd, {"--version"}); + return r.first == 0; + }; + + if (!check("addr2line")) { + boost::nowide::cerr << "错误:未找到 addr2line 工具\n"; + return false; + } + if (!check("objdump")) { + boost::nowide::cerr << "错误:未找到 objdump 工具\n"; + return false; + } + return true; + } + + bool ParseLog() + { + boost::nowide::ifstream file(m_logPath); + if (!file) { + boost::nowide::cerr << "无法打开日志文件: " << m_logPath << "\n"; + return false; + } + + std::regex addrRegex(R"(0x([0-9a-fA-F]+)\s+\(RVA:\s+(0x[0-9a-fA-F]+)\)\s+(.+))"); + std::string line; + + while (getline(file, line)) { + if (line.find("ExceptionAddr:") != std::string::npos) { + sscanf(line.c_str(), "ExceptionAddr: %llx", &m_exceptionAddr); + } + + std::smatch match; + if (regex_match(line, match, addrRegex)) { + StackFrame frame; + frame.address = std::stoull(match[1].str(), nullptr, 16); + frame.rva = std::stoull(match[2].str(), nullptr, 16); + frame.module = match[3].str(); + if (!is_utf8(frame.module)) { + frame.module = to_u8(frame.module); + } + m_stackFrames.push_back(frame); + } + } + + if (m_stackFrames.empty()) { + boost::nowide::cerr << "日志中未找到有效的调用栈信息\n"; + return false; + } + // else { + // int i = 0; + // for (const auto& f : m_stackFrames) { + // ++i; + // boost::nowide::cout << i << "PARSEADDR:" << f.address << "\n"; + // } + // } + + // 计算运行时基址(取第一个栈帧作为基准) + m_runtimeBase = m_stackFrames[0].address - m_stackFrames[0].rva; + return true; + } + + bool ResolveSymbols() + { + // 获取编译时基址 + auto result = exe_cmd("objdump", {"-p", m_exePath}); + if (result.first != 0) { + boost::nowide::cerr << "objdump 执行失败: " << result.second << "\n"; + return false; + } + + uint64_t imageBase = 0; + std::istringstream iss(result.second); + std::string line; + while (getline(iss, line)) { + if (line.find("ImageBase") != std::string::npos) { + sscanf(line.c_str(), " ImageBase %llx", &imageBase); + break; + } + } + + if (imageBase == 0) { + boost::nowide::cerr << "无法获取可执行文件的ImageBase\n"; + return false; + } + + // 解析异常地址 + uint64_t exceptionRva = m_exceptionAddr - m_runtimeBase; + uint64_t compileAddr = imageBase + exceptionRva; + PrintSourceInfo("\n============================= 异常位置 ===============================\n", m_exePath, compileAddr); + + // 解析调用栈 + int l = 0; + for (const auto& frame : m_stackFrames) { + ++l; + uint64_t compileAddr = imageBase + frame.rva; + PrintSourceInfo("调用栈" + std::to_string(l) + " ", frame.module, compileAddr); + } + + return true; + } + + void PrintSourceInfo(const std::string& type, const std::string& module, uint64_t addr) + { + // 将 addr 转为全小写 16 进制字符串 + std::ostringstream oss; + oss << std::hex << std::nouppercase << addr; // 无前缀、全小写 + std::string addr_hex = oss.str(); + + // 调用 addr2line + // boost::nowide::cout << "READY:" << module << "|" << addr_hex << std::endl; + auto result = exe_cmd("addr2line", {"-e", module, "-f", "-C", "-p", addr_hex}); + if (result.first == 0) { + boost::nowide::cout << COLOR_CYAN << type << COLOR_RESET << "[" << addr_hex << "]: " << result.second; + boost::nowide::cout << COLOR_YELLOW << "--> " << module << COLOR_RESET << std::endl; + } + + if (type.find("=") != std::string::npos) { + PrintSource(result.second); + boost::nowide::cout << COLOR_CYAN << "======================================================================\n" + << COLOR_RESET << std::endl; + } + } + + std::string m_logPath; + std::string m_exePath; + uint64_t m_exceptionAddr = 0; + uint64_t m_runtimeBase = 0; + std::vector m_stackFrames; +}; + +int main(int argc, char* argv[]) +{ + boost::nowide::args args(argc, argv); + boost::nowide::nowide_filesystem(); + enable_ansi_color(); + + if (argc != 3) { + boost::nowide::cerr << "用法: " << argv[0] << " <崩溃日志文件> <可执行文件路径>\n"; + return 1; + } + + CrashAnalyzer analyzer(argv[1], argv[2]); + if (!analyzer.Analyze()) { + return 1; + } + + return 0; +} diff --git a/dumpDemo/main.cpp b/dumpDemo/main.cpp new file mode 100644 index 0000000..ac6325b --- /dev/null +++ b/dumpDemo/main.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "crashdump.h" + +long __stdcall DumpCall(EXCEPTION_POINTERS* excp) +{ + // 1. 生成带时间戳的文件名 + auto now = std::chrono::system_clock::now(); + auto now_time_t = std::chrono::system_clock::to_time_t(now); + auto now_ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + + std::ostringstream oss; + oss << std::put_time(std::localtime(&now_time_t), "%Y%m%d_%H%M%S") << "_" << std::setfill('0') << std::setw(3) + << now_ms.count() << "_crash.log"; + + std::string filename = oss.str(); + + // 2. 获取崩溃信息 + CrashDump dump(excp); + std::string crashInfo = dump.GetExceptionInfo(); + + // 3. 写入文件 (C++11 风格) + try { + std::ofstream outfile(filename, std::ios::out | std::ios::trunc); + if (outfile) { + outfile << "=== Crash Dump ===\n\n"; + outfile << "Timestamp: " << std::put_time(std::localtime(&now_time_t), "%Y-%m-%d %H:%M:%S") << "." + << std::setfill('0') << std::setw(3) << now_ms.count() << "\n"; + outfile << crashInfo; + outfile << "\n=== End of Dump ==="; + + // 确保数据写入磁盘 + outfile.flush(); + if (outfile.good()) { + return EXCEPTION_EXECUTE_HANDLER; + } + } + } catch (...) { + // 文件写入失败的备用方案 + OutputDebugStringA(("Failed to write crash dump to " + filename).c_str()); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +void tasks() +{ + int c = 5; + while (--c > 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + int* p = nullptr; + *p = 11; + std::cout << "over..." << std::endl; +} + +int main() +{ + SetUnhandledExceptionFilter(DumpCall); + + std::cout << "Hello World!" << std::endl; + + std::thread thread(tasks); + thread.join(); + + std::cout << "End." << std::endl; + + return 0; +}