mingw崩溃日志分析。
This commit is contained in:
20
.clang-format
Normal file
20
.clang-format
Normal file
@@ -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
|
||||||
12
.clangd
Normal file
12
.clangd
Normal file
@@ -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
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
.qtcreator/
|
||||||
|
.vs
|
||||||
|
out/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "cxxLibrary"]
|
||||||
|
path = cxxLibrary
|
||||||
|
url = https://www.sinxmiao.cn/taynpg/cxxLibrary
|
||||||
150
.vscode/settings.json
vendored
Normal file
150
.vscode/settings.json
vendored
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@@ -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)
|
||||||
1
cxxLibrary
Submodule
1
cxxLibrary
Submodule
Submodule cxxLibrary added at 6daac723a9
8
dumpDemo/CMakeLists.txt
Normal file
8
dumpDemo/CMakeLists.txt
Normal file
@@ -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)
|
||||||
176
dumpDemo/crashdump.cpp
Normal file
176
dumpDemo/crashdump.cpp
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#include "crashdump.h"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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<int>(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<StackFrame*>(exp->ContextRecord->Rbp);
|
||||||
|
#else
|
||||||
|
current_frame = reinterpret_cast<StackFrame*>(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<StackFrame*>(frame_ptr);
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
// MSVC 内置函数(如果可用)
|
||||||
|
current_frame = reinterpret_cast<StackFrame*>(_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<FARPROC>(current_frame->return_address))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化输出
|
||||||
|
oss << "\n0x" << std::hex << std::setw(8) << std::setfill('0')
|
||||||
|
<< reinterpret_cast<uintptr_t>(current_frame->return_address) << " ";
|
||||||
|
|
||||||
|
// 获取模块信息
|
||||||
|
std::string module_name = GetModuleByReAddr(reinterpret_cast<PBYTE>(current_frame->return_address), module_addr);
|
||||||
|
if (!module_name.empty()) {
|
||||||
|
DWORD rva = static_cast<DWORD>(reinterpret_cast<PBYTE>(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();
|
||||||
|
}
|
||||||
25
dumpDemo/crashdump.h
Normal file
25
dumpDemo/crashdump.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef CRASHDUMP_H
|
||||||
|
#define CRASHDUMP_H
|
||||||
|
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <string>
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
#include <wbemidl.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
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
|
||||||
338
dumpDemo/dump_parse.cpp
Normal file
338
dumpDemo/dump_parse.cpp
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/nowide/args.hpp>
|
||||||
|
#include <boost/nowide/filesystem.hpp>
|
||||||
|
#include <boost/nowide/fstream.hpp>
|
||||||
|
#include <boost/nowide/iostream.hpp>
|
||||||
|
#include <boost/process.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<uint8_t>(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<int, std::string> exe_cmd(const std::string& exe, const std::vector<std::string>& 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<StackFrame> 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;
|
||||||
|
}
|
||||||
75
dumpDemo/main.cpp
Normal file
75
dumpDemo/main.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#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<std::chrono::milliseconds>(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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user