专栏名称: 嵌入式微处理器
关注这个时代最火的嵌入式微处理器,你想知道的都在这里。
目录
相关文章推荐
北京经信局  ·  2025全球数字经济大会首设欧洲分会场!3月 ... ·  19 小时前  
北京经信局  ·  2025全球数字经济大会首设欧洲分会场!3月 ... ·  19 小时前  
易简财经  ·  DeepSeek带飞阿里股价,阿里人连夜看豪宅 ·  昨天  
构力科技PKPM BIMBase  ·  央企担当 | ... ·  3 天前  
构力科技PKPM BIMBase  ·  央企担当 | ... ·  3 天前  
51好读  ›  专栏  ›  嵌入式微处理器

嵌入式开发调试利器——Sanitizer检测器

嵌入式微处理器  · 公众号  ·  · 2024-04-09 16:57

正文

今天给大家分享一个开发调试利器——Sanitizer。

1、Sanitizer简介

Sanitizer是由Google发起的开源工具集,用于检测内存泄露等问题。

链接: https://github.com/google/sanitizers/wiki/

Sanitizer 包括了AddressSanitizer、MemorySanitizer、ThreadSanitizer,以及LeakSanitizer等多种工具。这些工具最初是LLVM项目的一部分,后来也被GNU的GCC编译器支持。从GCC的4.8版本开始,就已经支持AddressSanitizer和ThreadSanitizer,而4.9版本则开始支持LeakSanitizer。

2、Sanitizer使用

1、AddressSanitizer的使用例子

AddressSanitizer(ASan) 是一个快速内存检测器,可以检测出缓冲区溢出、使用已释放内存等问题。编译时带上参数 -fsanitize=address及-g。

(1)捕捉栈缓冲区溢出问题:

AddressSanitizer.c:

// 微信公众号:嵌入式大杂烩
#include 

void test_func(void)
{
    int a[6] = {0};
    int b = a[6];    // 栈缓冲区溢出
}                     

int main(int argc, char **argv)
{
 test_func();

 return 0;
}

编译、运行:

gcc AddressSanitizer.c -fsanitize=address -g -o AddressSanitizer

执行结果分析:

触发了检测错误级别,终止程序并给出了程序运行异常的原因及异常的代码位置。

(2)捕捉使用已释放内存问题:

ThreadSanitizer.c:

// 微信公众号:嵌入式大杂烩
#include 

void test_func(void)
{
    char *p = malloc(10);
    p[0] = 1;
    free(p);
    p[0] = 1;  // 使用已释放内存
}                     

int main(int argc, char **argv)
{
 test_func();

 return 0;
}

2、ThreadSanitizer的使用例子

ThreadSanitizer(TSan) 是一个数据竞争检测器,可以用来分析线程竞态、死锁等线程相关问题。编译时带上参数 -fsanitize=thread及-g。

捕捉 线程间数据竞争 问题:

// 微信公众号:嵌入式大杂烩
#include 
#include 

int g_counter = 0;  // thread1、thread2竞争的数据

void *increment(void *arg)
{
    g_counter++;
}

void *decrement(void *arg)
{
    g_counter--;
}

void test_func(void)
{
    pthread_t thread1, thread2;

    pthread_create(&thread1, NULL, increment, NULL);
    pthread_create(&thread2, NULL, decrement, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("Counter value: %d\n", g_counter);
}

int main(int argc, char **argv)
{
    test_func();

    return 0;
}

编译、运行:

gcc ThreadSanitizer.c -fsanitize=thread -g -pthread -o ThreadSanitizer

执行结果分析:

触发了检测警告级别,程序仍能运行,并给出了程序运行有风险的原因及有风险的代码位置。

3、程序中同时存在多处风险?

上面的例子分别使用AddressSanitizer检测器与ThreadSanitizer检测器来检测对应的异常,可以较为精准地检测到对应的异常。

如果程序中同时存在多处风险呢?

这也是比较贴近我们的实际应用的,毕竟我们并不知道我们的代码里有哪些可能存在的风险。这种情况我们要怎么检测?

编译时能同时带上多个-fsanitize参数调用多个检测器吗?

可以同时带,但有些检测器不能同时使用。

AddressSanitizer与ThreadSanitizer检测器不能同时使用。

但是,假如我们的程序中恰好存在address异常与thread异常呢,单独使用AddressSanitizer检测器、ThreadSanitizer检测器的表现是怎样的?

比如,我们把上面3个例子的代码放在一起:

test.c:

// 微信公众号:嵌入式大杂烩
#include 
#include 
#include 

int g_counter = 0;  // thread1、thread2竞争的数据

void *increment(void *arg)
{
    g_counter++;
}

void *decrement(void *arg)
{
    g_counter--;
}

// 测试:资源竞争
void test_func(void)
{
    pthread_t thread1, thread2;

    pthread_create(&thread1, NULL, increment, NULL);
    pthread_create(&thread2, NULL, decrement, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    printf("Counter value: %d\n", g_counter);
}

// 测试:使用已释放内存
void test_func1(void)
{
    char *p = malloc(10);

    printf("This is test_func1\n");

    p[0] = 1;
    free(p);
    p[0] = 1;  // 使用已释放内存
}     

// 测试:栈缓冲区溢出
void test_func2(void)
{
    int a[6] = {0};
    int b = a[6];    // 栈缓冲区溢出
}     

int main(int argc, char **argv)
{
    test_func();
    test_func1();
 test_func2();

    return 0;
}

带-fsanitize=thread参数编译、运行:

执行结果分析:

ThreadSanitizer检测器能正常检测出资源竞争的问题,也检测出了test_func1中的 使用已释放的堆内存 的问题并以警告级别报告,但没有检测出test_func2的 栈缓冲区溢出 问题。

是不是因为test_func2运行在test_func1后面了,所以test_func2的异常没有被ThreadSanitizer检测器检测出来?

我们调换个位置看看:

int main(int argc, char **argv)
{
    test_func();
 test_func2();
    test_func1();

    return 0;
}

显然,执行结果还是一样的,test_func2的 栈缓冲区溢出 问题还是没有被ThreadSanitizer检测器检测出来。

所以,大致得出结论:当程序里存在thread异常与address异常时,使用ThreadSanitizer检测器能准确检测到thread异常,能检测到部分address异常。

带-fsanitize=address参数编译、运行:







请到「今天看啥」查看全文


推荐文章