当前位置: 首页 > article >正文

在Windows/Linux/MacOS C++程序中打印崩溃调用栈和局部变量信息

打印崩溃调用栈和局部变量信息的方法有所不同。以下是针对 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;
}

备注

  1. 局部变量打印:在崩溃时打印局部变量非常困难,因为局部变量可能已经破坏或不可访问。在上述示例中,主要展示了如何打印调用栈信息。要获取局部变量的值,通常需要启用调试信息并使用调试器(如 GDB)来解析堆栈帧。

  2. 调试符号:为了更详细地解析调用栈和局部变量,需要在编译时启用调试信息,例如在 GCC 中使用 -g 选项。

  3. 平台差异:不同的操作系统有不同的 API 和方法来处理崩溃和调用栈跟踪,因此需要根据具体平台选择相应的方法。


http://www.kler.cn/a/512758.html

相关文章:

  • 数据结构之堆排序
  • 京华春梦,守岁这方烟火人间
  • 深度学习-89-大语言模型LLM之AI应用开发的基本概念
  • 《目标检测数据集下载地址》
  • 如何使用 Python 进行文件读写操作?
  • gitlab使用多数据库
  • C/C++ 时间复杂度(On)
  • 读西瓜书的数学准备
  • 【Java】探秘二叉树经典题,码农进阶“必刷清单”在此!(上)
  • 【Spring MVC】如何运用应用分层思想实现简单图书管理系统前后端交互工作
  • Leetcode 3428. Maximum and Minimum Sums of at Most Size K Subsequences
  • 【数据分享】1929-2024年全球站点的逐日最高气温数据(Shp\Excel\免费获取)
  • AI音乐生成模型Suno的技术原理,以及Suno的使用指南与应用场景
  • B3DM转换成STEP
  • 解决leetcode第3426题所有安放棋子方案的曼哈顿距离
  • Elasticsearch(ES)基础查询语法的使用
  • spring Ioc 容器的简介和Bean之间的关系
  • 一文大白话讲清楚webpack基本使用——4——vue-loader的配置和使用
  • AI编程工具横向评测--Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发
  • Java基于SSM框架的社区团购系统小程序设计与实现(附源码,文档,部署)
  • xiaozhi-esp32 - 基于 ESP32 的 AI 聊天机器人
  • 2024年博客之星主题创作|Android 开发:前沿技术、跨领域融合与就业技能展望
  • 深入探索 Vue.js 的局部状态管理技术:基于 Pinia 的组合式 API 实现
  • Java程序运行剖析(JVM+JDK+JRE)(总结+超详解)
  • Python中字符串的基本操作
  • C#/.NET/.NET Core技术前沿周刊 | 第 22 期(2025年1.13-1.19)