打印崩溃调用栈和局部变量信息的方法有所不同。以下是针对 Windows、Linux 和 MacOS 的示例代码。
Windows
在 Windows 上,可以使用 Windows API 来捕获异常并打印调用栈。
#include <windows.h>
#include <DbgHelp.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "dbghelp.lib")
void print_stack_trace() {
const int FRAMES_TO_CAPTURE = 64;
IMAGEHLP_STACK_FRAME frames[FRAMES_TO_CAPTURE];
STACKFRAME64 stack;
DWORD machine_type;
HANDLE process = GetCurrentProcess();
DWORD i;
SYMBOL_INFOW* symbol_info;
DWORD64 displacement;
SymInitialize(process, NULL, TRUE);
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
ZeroMemory(&stack, sizeof(stack));
stack.AddrPC.Offset = (DWORD_PTR)__debugbreak;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrFrame.Offset = (DWORD_PTR)__security_check_cookie;
stack.AddrFrame.Mode = AddrModeFlat;
stack.AddrStack.Offset = (DWORD_PTR)&stack;
stack.AddrStack.Mode = AddrModeFlat;
machine_type = IMAGE_FILE_MACHINE_UNKNOWN;
if (!StackWalk64(machine_type, process, GetCurrentThread(), &stack)) {
machine_type = IMAGE_FILE_MACHINE_I386;
if (!StackWalk64(machine_type, process, GetCurrentThread(), &stack)) {
machine_type = IMAGE_FILE_MACHINE_AMD64;
if (!StackWalk64(machine_type, process, GetCurrentThread(), &stack)) {
fprintf(stderr, "Failed to determine machine type\n");
return;
}
}
}
symbol_info = (SYMBOL_INFOW*)calloc(sizeof(SYMBOL_INFOW) + 256 * sizeof(wchar_t), 1);
if (symbol_info == NULL) {
fprintf(stderr, "Failed to allocate memory for symbol_info\n");
return;
}
symbol_info->MaxNameLen = 255;
symbol_info->SizeOfStruct = sizeof(SYMBOL_INFOW);
for (i = 0; i < FRAMES_TO_CAPTURE; i++) {
if (!StackWalk64(machine_type, process, GetCurrentThread(), &stack)) {
break;
}
if (!SymFromAddr(process, stack.AddrPC.Offset, &displacement, symbol_info)) {
fprintf(stderr, "Frame %d: 0x%llx\n", i, stack.AddrPC.Offset);
} else {
fwprintf(stderr, L"Frame %d: %ls + 0x%llx\n", i, symbol_info->Name, displacement);
}
// Print local variables here, but it's tricky without proper debugging symbols
}
free(symbol_info);
SymCleanup(process);
}
void cause_crash() {
int *p = NULL;
*p = 1;
}
int main() {
__try {
cause_crash();
}
__except (print_stack_trace(), EXCEPTION_EXECUTE_HANDLER) {
exit(EXIT_FAILURE);
}
return 0;
}
Linux
在 Linux 上,可以使用信号处理和 backtrace
函数来捕获崩溃并打印调用栈。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>
void print_stack_trace() {
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace(array, 10);
strings = backtrace_symbols(array, size);
printf("Stack backtrace:\n");
for (i = 0; i < size; i++) {
printf("[bt] %s\n", strings[i]);
}
free(strings);
}
void signal_handler(int sig) {
print_stack_trace();
exit(EXIT_FAILURE);
}
void cause_crash() {
int *p = NULL;
*p = 1;
}
int main() {
signal(SIGSEGV, signal_handler);
cause_crash();
return 0;
}
MacOS
在 MacOS 上,可以使用类似 Linux 的方法,使用 backtrace
和 backtrace_symbols
函数。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>
void print_stack_trace() {
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace(array, 10);
strings = backtrace_symbols(array, size);
printf("Stack backtrace:\n");
for (i = 0; i < size; i++) {
printf("[bt] %s\n", strings[i]);
}
free(strings);
}
void signal_handler(int sig) {
print_stack_trace();
exit(EXIT_FAILURE);
}
void cause_crash() {
int *p = NULL;
*p = 1;
}
int main() {
signal(SIGSEGV, signal_handler);
cause_crash();
return 0;
}
备注
-
局部变量打印:在崩溃时打印局部变量非常困难,因为局部变量可能已经破坏或不可访问。在上述示例中,主要展示了如何打印调用栈信息。要获取局部变量的值,通常需要启用调试信息并使用调试器(如 GDB)来解析堆栈帧。
-
调试符号:为了更详细地解析调用栈和局部变量,需要在编译时启用调试信息,例如在 GCC 中使用
-g
选项。 -
平台差异:不同的操作系统有不同的 API 和方法来处理崩溃和调用栈跟踪,因此需要根据具体平台选择相应的方法。