检查内存的利器AddressSanitizer

1.AddressSanitizer简介

AddressSanitizer(简称ASan)是一个快速的内存错误检测工具。根据谷歌工程师介绍ASan已经在 chromium 项目上检测出了300多个潜在的未知bug,而且在使用 ASan作为内存错误检测工具对程序性能损耗也是及其可观的。

ASan能够检查内存泄露、越界、未初始化、重复释放、缓冲区溢出、堆栈溢出、野指针、线程死锁等问题。

ASan执行速度非常快,根据检测结果显示可能导致性能降低2倍左右,比Valgrind(官方给的数据大概是降低10-20倍)快了一个数量级。

Asan适用于linux系统,嵌入式linux系统和windows系统

2.在TI TDA2平台上检查内存问题

准备一个测试文件memtest.c,添加如下代码:

#include <stdlib.h>

#include <malloc.h>

#include <string.h>

void test2(){

       int a;

       int *p1 = &a ;

       *p1 = 1;

}

void test(){

       printf("test.\n");

       int *ptr = malloc(sizeof(int)*10);

       ptr[10] = 7;

       free(ptr);

       char *ps;

       ps = (char *)malloc(sizeof(5));

       strcpy(ps,"ABC");

       test2();

}

int main(){

       printf("test memory\n");

       test();

       return 0;

}

该程序有两处错误,ptr[10] = 7动态数组越界,ps没有释放内存。我们来试试ASAN能否找出这些错误。

交叉编译:arm-linux-gnueabihf-gcc memtest.c -fsanitize=address -fno-omit-frame-pointer-fsanitize-recover=address-o memtest

选项-fsanitize=address表示使用ASAN库,为了在错误信息中让栈追溯信息更友好,可以加上-fno-omit-frame-pointer选项。

在C62板子上运行:./memtest

 

Asan找到了内存越界的错误heap-buffer-overflow on address 0xb5300ff8 at pc…,位置在第15行。Asan找到一个错误就停止了,修复了该错误需要重新编译运行,找下一个错误。

在C62开发板上运行:

./memtest_g

test memory

test free .

在TI TDA2平台上ASAN没能检查出内存泄露。

3.在Linux平台上检查内存问题

重新编译上节程序在linux PC上运行

 

内存泄露的问题终于查到了。

非常奇怪asan为什么不会在TI开发板上查内存泄漏,我尝试更换交叉编译器gcc-linaro-6.4.1和gcc-linaro-7.5.0,但是结果是一样的。

结论asan在TI TDA2平台上不能查出内存泄露问题,但能查内存越界。在linux PC机上都可以。

4.检查堆栈溢出

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

void *thread_function(void *arg);

char message[] = "Hello World";

int main() {

    int res;

    pthread_t a_thread;

    void *thread_result;

    res = pthread_create(&a_thread, NULL, thread_function, (void *)message);

    if (res != 0) {

        perror("Thread creation failed");

        exit(EXIT_FAILURE);

    }

    printf("Waiting for thread to finish...\n");

    res = pthread_join(a_thread, &thread_result);

    if (res != 0) {

        perror("Thread join failed");

        exit(EXIT_FAILURE);

    }

    printf("Thread joined, res %d, it returned %s\n", res, (char *)thread_result);

    printf("Message is now %s\n", message);

    exit(EXIT_SUCCESS);

}

void *thread_function(void *arg) {

    thread_function(arg);

}

这个程序的线程入口函数反复调用自己,直到堆栈溢出。

交叉编译:arm-linux-gnueabihf-gcc stack_test.c -fsanitize=address -fno-omit-frame-pointer-o stack_test -lpthread

运行:./stack_test

 

ASAN报告stack-overflow on address 0xb4cf8ff8, stack_test.c第28行,报告的错误非常准确。

5.检测结果存入文件

为了将检测结果存入文件,可采用如下命令:

gcc memtest.c -fsanitize=address -fno-omit-frame-pointer  -o memtest

export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=0:malloc_context_size=15:log_path=./asan.log:suppressions=$SUPP_FILE

./memtest

程序运行后asan的检测结果存放在asan.log.[dddd]中。

halt_on_error原本是控制检测到一处错误时是否停止,但是发现它并不起作用。

6.Asan在C62项目上的实战应用

测试团队报告在道路标定过程中切换dow开关时意外出现进程崩溃现象。我查看了coredump文件,发现竟是dow调用ncnn库时进程终止,然而随后两天反复测试却没有重现。想修复问题却无从下手,ncnn是腾迅的库,究竟是哪里出了问题?猜想可能有内存越界问题。内存越界可能修改了无用的内存,不引发任何问题;但也可能改写了某个模块的数据,造成致命问题,具有一定的偶发性和随机性。可以尝试用asan检查一下。

6.1在OC模块的CMakeList.txt添加下面一行:

set(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer  ${CMAKE_CXX_FLAGS}")

  1. 模块编译OC,将生成的库上传到/opt/vision_sdk/project/app_libs/目录下
  2. 将项目中的ti_components/os_tools/linux/linaro/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib/libasan.so.2.0.0
  3. 拷贝到C62开发板的/opt/vision_sdk/project/app_libs/目录下
  4. app.cfg不必要的模块注掉,例如PasTask ,ApaTask
  5. 手动运行apps.out

LD_PRELOAD=/opt/vision_sdk/project/app_libs/libasan.so.2.0.0 ./apps.out

 6.2道路标定测试

标定中发生栈缓冲区溢出

 

对照代码,图中位置发生越界(代码添加了log,行号略有变化)

 

在TI平台上使用asan特别容易卡死,因此要注意以下几点:

  • 不要使用log_path写文件
  • 不要多个模块同时检查
  • 不要与-g一起使用

7.Windows平台VS2019中使用 asan

Asan以优异的性能被集成到VS2019,windows也可以使用它。

7.1安装VS2019注意勾选Asan

 

7.2创建一个工程,编写测试代码

属性-C/C++常规-启用地址擦除系统,选择“是”  

7.3Release模块下运行

Asan捕捉到堆缓冲区溢出错误

终端有更详细地输出

 

Asan在windows下查内存越界也同样得心应手,但是Asan检测不到内存泄漏。

8.总结

内存问题一直困扰着当前的项目,Asan是一个极为优秀的内存检测工具。算法程序在移植之前可以用vld查内存泄漏,用Asan查内存越界。在TI平台上推广使用Asan也是消灭错误、提高代码质量的有力工具。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值