博客
关于我
C++封装一个易用的打印backtrace信息的函数
阅读量:733 次
发布时间:2019-03-21

本文共 2567 字,大约阅读时间需要 8 分钟。

如何通过C++编写一个易于使用的打印堆栈信息函数

  • 堆栈信息追踪的重要性

    在调试程序过程中,当遇到错误时,能够快速获取当前函数调用堆栈信息至关重要。通常,这会在调用assert函数之前进行,以便定位问题所在。

  • 查看所需的核心函数

  • 2.1 backtrace函数

    backtrace函数的原型为:

    int backtrace(void **buffer, int size);

    使用说明:

    该函数会将当前程序的堆栈信息写入buffer数组。每个数组元素是返回地址,信息是倒序存储的(最近调用在前,遥远调用在后)。通过确保buffer大小和容量足够,可以获取完整的堆栈日志。返回值表示实际获取到的堆栈信息数量,数值不会超过size

    2.2 backtrace_symbols函数

    backtrace_symbols函数的原型为:

    char **backtrace_symbols(void *const *buffer, int size);

    使用说明:

    该函数接受backtrace返回的地址数组,将其转换为字符串数组。每个字符串包含函数名(如果确定可知)、十六进制偏移量和实际返回地址。返回值是一个指针,指向新的字符串数组。注意,这个数组的内存必须由调用者释放,但字符串内容不需要处理。

    2.3 __cxa_demangle函数

    __cxa_demangle函数的原型为:

    char* __cxa_demangle(const char *mangled_name, char *output_buffer, size_t *length, int *status);

    使用说明:

    这个C++内联函数反转编译器添加的符号信息,将其转换为原始函数名。主要作用是解析 και符号sequences。调用者应提供被编译后的符号字符串,并处理返回结果,注意释放动态分配的内存。

    1. 测试各函数的实际使用情况
    2. 3.1 调用backtrace获取日志

      举例:

      void fun(){      static int count = 0;      if (++count == 5){          Backtrace(50, 0);          return;      }      fun();  }  int main(int argc, char* argv[]){      fun();      return 0;  }

      运行后,期望输出包含5次fun调用的堆栈信息。

      3.2 转换为符号名称

      调用backtrace_symbols后,输出应类似于:

      ./all(_Z9BacktraceiiRKSs+0x58) [0x401c43]./all(_Z3funv+0x48)...

      需要提取括号内的名称并解析。

      3.3 使用__cxa_demangle获取原始函数名

      最终结果应类似于:

      Backtrace(int, int, std::string const&).fun()fun()fun()fun()fun()./all(main+0x9)...

      结果表明,其中Backtrace被调用5次,遵循预期调用次数。

      1. 完整代码示例
      2. #include 
        #include
        #include
        #include
        #include
        #include
        #include
        #include
        namespace detail { std::string demangle(const char* str) { size_t size = 0; int status = 0; std::string sstr; sstr.resize(256); if (1 == sscanf(str, "%*[^(]%*[^_]%255[^)+]", &sstr[0])) { char* tmp = abi::__cxa_demangle(&sstr[0], nullptr, &size, &status); if (tmp) { std::string result(tmp); free(tmp); return result; } } if (1 == sscanf(str, "%255s", &sstr[0])) return sstr; return str; } std::string Backtrace(int size = 64, int skip = 2, const std::string& prefix = "") { void* array[size]; size_t s = backtrace(array, size); char** str = backtrace_symbols(array, s); std::vector
        bt; std::stringstream ss; if (str) { for (size_t i = skip; i < s; ++i) { bt.push_back(demangle(str[i])); } free(str); } else { ss << "backtrace_symbols failed"; goto good; } for (const auto& b : bt) { ss << prefix << b << "\n"; }good: return ss.str(); } void fun() { static int count = 0; if (++count == 5) { Backtrace(50, 0); return; } fun(); } int main(int argc, char* argv[]) { fun(); return 0; }}

        以上代码展示了一个完整的解决方案,包括函数定义、调试工具的使用和示例测试程序。通过这种方法,可以方便地获取和解析堆栈信息,使调试过程更加高效。

    转载地址:http://wxsgz.baihongyu.com/

    你可能感兴趣的文章
    mysql 字段合并问题(group_concat)
    查看>>
    mysql 字段类型类型
    查看>>
    MySQL 字符串截取函数,字段截取,字符串截取
    查看>>
    MySQL 存储引擎
    查看>>
    mysql 存储过程 注入_mysql 视图 事务 存储过程 SQL注入
    查看>>
    MySQL 存储过程参数:in、out、inout
    查看>>
    mysql 存储过程每隔一段时间执行一次
    查看>>
    mysql 存在update不存在insert
    查看>>
    Mysql 学习总结(86)—— Mysql 的 JSON 数据类型正确使用姿势
    查看>>
    Mysql 学习总结(87)—— Mysql 执行计划(Explain)再总结
    查看>>
    Mysql 学习总结(88)—— Mysql 官方为什么不推荐用雪花 id 和 uuid 做 MySQL 主键
    查看>>
    Mysql 学习总结(89)—— Mysql 库表容量统计
    查看>>
    mysql 实现主从复制/主从同步
    查看>>
    mysql 审核_审核MySQL数据库上的登录
    查看>>
    mysql 导入 sql 文件时 ERROR 1046 (3D000) no database selected 错误的解决
    查看>>
    mysql 导入导出大文件
    查看>>
    mysql 将null转代为0
    查看>>
    mysql 常用
    查看>>
    MySQL 常用列类型
    查看>>
    mysql 常用命令
    查看>>