内存越界|内存泄漏检测方法

内存泄漏检测可在CMakeLists.txt 文件中,添加 -fsanitize=leak

set(CMAKE_CXX_FLAGS "-fsanitize=leak -std=c++11 -g -Wall -Wno-unused-result -DROS ${CMAKE_CXX_FLAGS}")

g++ 加上 -fsanitize=leak 选项后,能打印内存泄漏时的堆栈。【进程运行结束或用 SIGINT 杀掉后,会打印内存泄漏的堆栈信息到标准出错】

内存错误操作可在CMakeLists.txt里面加上如下编译选项

set(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer ${CMAKE_CXX_FLAGS}" )
当然,上述编译选项可以输出leak信息

内存错误操作:-fsanitize=address
多线程竞争:-fsanitize=thread
内存泄漏:-fsanitize=leak
未定义操作:-fsanitize=undefined(如:除0、空指针解引用、枚举值超范围、使用未初始化的变量值等)

#告诉address检测到异常不退出进程
set(ENV{ASAN_OPTIONS} "halt_on_error=0")

运行时可能会出现以下内存问题

对输出的ERROR重定向报错日志: roslaunch perception_lidar.launch 2> /home/mogo/data/yangdaiyu/address.log

1、AddressSanitizer: stack-use-after-scope on address

2、AddressSanitizer: attempting free on address which was not malloc()-ed

  • /data/autocar/yangdaiyu/perception_ws/src/perception/lidar/robosense/modules/perception/lidar/ai_detection/src/pointpillars/pointpillars_ai_detection.cpp
    msg_ptr->scan_ptr.reset(new PointCloud)
    改:msg_ptr->resetMsg();

3、AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete)

  • /data/autocar/yangdaiyu/perception_ws/src/perception/lidar/robosense/modules/perception/lidar/ai_detection/src/pointpillars/postprocess_cuda.cu
    auto output = std::shared_ptr(new float[num_obj * 9]);
    改:
    auto output = std::shared_ptr(new float[num_obj * 9], [](float* p) { delete[] p; });

  • /data/autocar/yangdaiyu/perception_ws/src/perception/lidar/robosense/modules/perception/lidar/util/segmentor/src/rs_segmentor.cpp
    seg_scan_x_mat.reset(new int[grid_size]);
    seg_scan_y_mat.reset(new int[grid_size]);
    智能指针对new的空间,若不指定释放方式,默认采用delete去释放,对于new数组空间无法全部释放,因此需要指定释放方式,如下:
    seg_scan_x_mat.reset(new int[grid_size], [](int* p) { delete[] p; });
    seg_scan_y_mat.reset(new int[grid_size], [](int* p) { delete[] p; });
    seed_mat.reset(new int[grid_size], [](int* p) { delete[] p; });
    bin_mat.reset(new int[grid_size], [](int* p) { delete[] p; });
    label_mat.reset(new int[grid_size], [](int* p) { delete[] p; });

  • perception/lidar/robosense/modules/common/src/basic_type/rotate_box.cpp:198 std::shared_ptr distPt;
    distPt.reset(new double[N * N], [](int* p) { delete[] p;});
    std::shared_ptr ptDistRemap;
    ptDistRemap.reset(new int[N], [](int* p) { delete[] p;});

4、AddressSanitizer: heap-buffer-overflow on address 堆缓冲区溢出

  • size越界保护
    if(track_object->track_type != ObjectType::UNKNOW) //加上类型判断,AI的polygon是4个点,但RB目标可能只有3个点,会超出界限
    {
    for(int i = 1; i < 4; ++i){
    if(track_object->object->polygons[i].norm() < track_object->object->polygons[index_corner_cur].norm()){
    index_corner_cur = i;
    index_corner_last = i;
    }
    }
    }

  • Eigen::internal::handmade_aligned_free(void*)
    在CMakeLists.txt里面加上add_definitions(-DPCL_NO_PRECOMPILE=ON)可以通过
    此问题时PCL里的bug,升级PCL版本也可以解

5、LeakSanitizer: detected memory leaks

  • perception/lidar/robosense/modules/perception/infer/src/rs_trt_infer.cpp:156
    #0 0x7fb54b343b in operator new(unsigned long) (/usr/lib/aarch64-linux-gnu/libasan.so.4+0xd243b)
    #1 0x7f9ef34143 (/usr/lib/aarch64-linux-gnu/libnvinfer.so.7+0x4e7143)
    #2 0x7f9f01dc77 in nvinfer1::rt::SafeEngine::deserializeCoreEngine(nvinfer1::rt::CoreReadArchive&, std::vector<nvinfer1::rt::EngineLayerAttribute, std::allocatornvinfer1::rt::EngineLayerAttribute >&) (/usr/lib/aarch64-linux-gnu/libnvinfer.so.7+0x5d0c77)
    #3 0x7f9ed9f3b7 in nvinfer1::rt::Engine::deserialize(void const*, unsigned long, nvinfer1::IGpuAllocator&, nvinfer1::IPluginFactory*) (/usr/lib/aarch64-linux-gnu/libnvinfer.so.7+0x3523b7)
    #4 0x7f9eda7f53 in nvinfer1::Runtime::deserializeCudaEngine(void const*, unsigned long, nvinfer1::IPluginFactory*) (/usr/lib/aarch64-linux-gnu/libnvinfer.so.7+0x35af53)

    infer->deserializeCudaEngine(cahce_engine.c_str(), cahce_engine.size(), nullptr);

以上列出了几个在使用C++和TensorRT等库进行自动驾驶感知系统开发时常见的内存管理问题,以及相应的解决方案。这些问题通常与内存泄漏、堆缓冲区溢出、不恰当的内存释放等相关,并且使用AddressSanitizer(ASan)和LeakSanitizer等工具可以帮助检测这些问题。下面是对每个问题的详细解释和解决方案:

1. AddressSanitizer: stack-use-after-scope

问题:在栈上分配的变量在作用域结束后仍然被使用。

解决方案:确保不在变量作用域外访问该变量。可以通过缩小变量的作用域,或者使用智能指针等机制来管理对象的生命周期。

2. AddressSanitizer: attempting free on address which was not malloc()-ed

问题:尝试释放一个不是通过malloc(或其变体如new)分配的内存。

解决方案:确保只释放通过mallocnew等函数分配的内存。同时,检查是否有双重释放或错误释放的情况。

3. AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete)

问题:使用new[]分配的内存被delete释放,或者相反。

解决方案:对于数组分配,应使用new[]delete[]配对,对于单个对象则使用newdelete。在使用std::shared_ptr时,如果指向的是动态分配的数组,需要提供一个自定义的删除器(如示例中的lambda表达式)来确保使用delete[]

4. AddressSanitizer: heap-buffer-overflow on address

问题:访问堆内存时超出了分配的范围。

解决方案:添加边界检查,确保不会访问超出分配范围的内存。例如,在循环中检查索引是否超出数组边界。此外,升级或修复库(如PCL)中的已知问题也是解决此类问题的一种方法。

5. LeakSanitizer: detected memory leaks

问题:检测到内存泄漏。

解决方案

  • 使用智能指针(如std::shared_ptrstd::unique_ptr)来自动管理内存。
  • 定期检查并关闭不再需要的文件描述符、网络连接等资源。
  • 在使用外部库(如TensorRT)时,确保正确管理库分配的资源。例如,TensorRT的deserializeCudaEngine函数可能分配了内存,但如果在某个点发生异常或错误,这些内存可能不会被释放。确保在出错时也能正确释放资源。

总结

内存管理问题是C++编程中常见的难题,特别是在处理复杂系统如自动驾驶感知系统时。使用现代C++特性(如智能指针)和工具(如AddressSanitizer和LeakSanitizer)可以帮助检测和解决这些问题。此外,仔细的代码审查和测试也是减少内存问题的重要步骤。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值