加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
  • 推荐器件
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

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

04/07 15:38
2836
阅读需 14 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是杂烩君。本次我们来分享一个开发调试利器——Sanitizer。

Sanitizer简介

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

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

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

Sanitizer使用

1、AddressSanitizer的使用例子

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

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

AddressSanitizer.c:

// 微信公众号:嵌入式大杂烩
#include <stdlib.h>

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 <stdlib.h>

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 <stdio.h>
#include <pthread.h>

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: %dn", 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 <stdio.h>
#include <stdlib.h>
#include <pthread.h>

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: %dn", g_counter);
}

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

    printf("This is test_func1n");

    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参数编译、运行:

执行顺序:

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

    return 0;
}

执行结果分析:

AddressSanitizer检测器检测到了test_func1中的已使用释放的堆内存的异常并以错误级别报告,并终止了程序;没有检测到test_func的资源竞争的风险;也没有检测到test_func2的栈缓冲区溢出的问题,因为执行到test_func1的时候程序已经被终止了,如果把test_func2放在test_func1之前运行,就能检测到test_func2的异常。

结论:当程序里存在thread异常与address异常时,使用AddressSanitizer检测器能准确检测到第一个触发的address异常,不能检测到thread异常。

如果程序中存在多种可能存在的风险时,需要使用多个检测器单独挨个检测。每个检测器都有其擅长检测的方面,可以经过初步分析之后确定大致地方向,选择适合地检测器来做检测。

以上就是关于Sanitizer的一些简单介绍及使用的分享,更多的关于Sanitizer的资料可查阅:https://github.com/google/sanitizers/wiki/

码字不易,如果文章对你有帮助,麻烦帮忙点赞、关注,谢谢大家!

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
M25P05-AVMN6P 1 Rochester Electronics LLC 64KX8 FLASH 2.7V PROM, PDSO8, 0.150 INCH, ROHS COMPLIANT, PLASTIC, SOP-8
$0.79 查看
LAN8742A-CZ 1 SMSC Ethernet Transceiver, 4 X 4 MM, 0.90 MM HEIGHT, HALOGEN FREE AND ROHS COMPLIANT, SQFN-24
$1.51 查看
HFBR-1531Z 1 Broadcom Limited Transmitter, Through Hole Mount, ROHS COMPLIANT PACKAGE

ECAD模型

下载ECAD模型
$17.74 查看

相关推荐

电子产业图谱

本公众号专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!