gdb调试core并保存std::string到文件

线上有个服务core dump,首先使用gdb打开core文件

gdb bin/xx xx.12769

然后使用backtrace(bt)查看core对应的栈,并使用frame(f)切换到我们业务的层级。

由于core的位置是opencv,调用cv::mean时候core了,我们需要把原始的图片dump出来分析。

业务中有个image_data局部变量保存了入参的图片,下面我们尝试dump出来。

(gdb) p image_data

$3 = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 

    _M_p = 0x7f06d5428c58 "\377\330\377\340"}}

可以看到image_data是std::string,里面有层_M_dataplus._M_p只向了原始的字符串指针。我们需要做的是指定这个字符串长度。 使用网上的一些方法都不太管用,我们来用自己的方法分析。我使用的是gcc8编译,关于std::string可以看看腾讯的这个文章写的比较清楚,gcc的string对象内存布局如下:

preview

所以,我们只需要获取_M_dataplus._M_p的前一个_Rep地址就ok了。

(gdb) p ((std::string::_Rep*)image_data)[-1]

$4 = {<std::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Rep_base> = {_M_length = 30752, _M_capacity = 32711, _M_refcount = -1}, 

  static _S_max_size = 4611686018427387897, static _S_terminal = 0 '\000', static _S_empty_rep_storage = <optimized out>}

可以看到,字符串长度30752,分配大小32711,没有被引用。

接着,我们知道_M_p的地址是0x7f06d5428c58,因此可以用gdb的dump命令把内存数据保存到文件。但是这个命令需要知道内存的起止,我们现在知道了起始位置和长度,需要计算止地址,这个好办。

打开苹果的编程型计算器,输入起始地址

然后转换成十进制

然后加上长度

 

 然后再转换成十六进制就是我们要的地址了。

 然后执行命令

(gdb) dump binary memory /tmp/test.jpg 0x7f06d5428c58 0x7F06D5430478

然后看看/tmp/test.jpg吧~ 

### ROS中通过`rosnode kill`命令同时终止`std::thread`线程 在ROS环境中,当使用`rosnode kill`命令终止某个节点时,实际上会触发该节点的关闭流程。如果节点内部存在多个`std::thread`线程,则需要确保这些线程能够被安全地中止释放资源。以下是关于如何实现这一目标的具体方法: #### 1. 使用信号处理机制 为了确保主线程和子线程都能响应外部中断请求(如`rosnode kill`),可以在初始化阶段设置自定义信号处理器[^1]。例如,在`main()`函数中注册SIGINT和SIGTERM信号处理器来捕获Ctrl+C或kill指令,调用`rclcpp::shutdown()`以通知所有线程停止工作。 ```cpp void customSignalHandler(int sig) { RCLCPP_INFO(rclcpp::get_logger("custom_signal_handler"), "Received signal %d", sig); rclcpp::shutdown(); } int main(int argc, char **argv) { rclcpp::init(argc, argv); // 注册信号处理器 signal(SIGINT, customSignalHandler); // Ctrl+C signal(SIGTERM, customSignalHandler); // Kill command auto node = std::make_shared<MyNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; } ``` #### 2. 子线程的安全退出逻辑 对于每个由`std::thread`创建的工作线程,应设计一种机制使其能够在接收到退出信号后优雅地终止。通常可以通过以下方式实现: - 设置一个共享标志变量(如`std::atomic<bool>`类型的成员变量)用于指示线程是否应该继续运行。 - 定期检查这个标志变量的状态;一旦发现其变为false,则立即返回从而结束当前线程的任务循环。 下面是一个简单的例子展示如何管理多线程环境下的生命周期: ```cpp class MyNode : public rclcpp::Node { public: explicit MyNode(const std::string& name): Node(name), running(true){ thread_ = std::thread(&MyNode::workerThreadFunction, this); } ~MyNode(){ running.store(false); if(thread_.joinable()){ thread_.join(); } } private: bool running; void workerThreadFunction(){ while(running.load()){ // 执行某些操作... std::this_thread::sleep_for(std::chrono::milliseconds(100)); } RCLCPP_INFO(this->get_logger(), "Worker thread exiting..."); } std::thread thread_; }; ``` #### 3. 节点清理与资源释放 除了上述措施之外,还需要考虑整个应用程序正常退出前完成必要的清理动作。这包括但不限于销毁所有的Publisher/Subscriber实例以及解除内存分配等。为此可利用C++标准库中的`atexit()`函数指定一组回调函数,在进程即将终结之前自动执行它们[^2]。 ```cpp static void cleanupResources() { RCLCPP_INFO(rclcpp::get_logger("global_cleanup"), "Performing global resource cleanup..."); // Add your specific clean-up code here. } // Register the callback somewhere during initialization phase. atexit(cleanupResources); ``` #### 4. Core Dump调试支持 尽管采取了以上预防手段,但在实际开发过程中仍可能出现意外崩溃的情况。此时启用Core Dump功能可以帮助定位潜在问题所在位置[^3]。具体做法如下所示: ```bash ulimit -c unlimited # 允许生成任意大小的核心转储文件 ./your_ros_application # 启动应用直到发生异常导致核心转储 gdb ./your_ros_application corefile_name # 加载GDB分析工具加载刚才产生的core file bt # 查看回溯堆栈信息找出根本原因 ``` 另外值得注意的是,有时候即使启用了core dump也可能因为程序陷入死锁等原因而无法自然产生相应记录。这种情况下可以尝试强制发送SEGV信号给目标PID促使它立刻崩塌以便获取更详细的诊断数据: ```bash kill -11 <target_pid> ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值