sanitizer工具集的介绍
Sanitizers是谷歌发起的开源工具集,包括了Address Sanitizer, undefined behavior Sanitizer, Thread Sanitizer, Leak Sanitizer。GCC从4.8版本开始支持Address sanitizer和Thread Sanitizer,4.9版本开始支持Leak Sanitizer和undefined behavior Sanitizer。
Address Sanitizer(ASAN):
也即地址消毒技术,通过编译插桩(CTI),能够发现此堆/栈/全局变量读写溢出,内存泄露等问题,并将信息直接打印到日志中。Address Sanitizer(ASan)是一个快速的内存错误检测工具。它非常快,只拖慢程序两倍左右(比起Valgrind快多了)。它包括一个编译器instrumentation模块和一个提供malloc()/free()替代项的运行时库。
Thread Sanitizer(TSan):
是一个检查线程Data Race的C/C 工具。
Leak Sanitizer(LSan):
检测内存的LeakSanitizer是集成在Address Sanitizer中的一个相对独立的工具,它工作在检查过程的最后阶段。
Undefiend Behavior Sanitizer(UBSan):
检测未定义行为(使用空指针、有符号整数溢出等)。
环境配置
QMake:
在pro文件中添加:
代码语言:javascript复制QMAKE_CXXFLAGS ="-fsanitize=undefined,address,leak -fno-omit-frame-pointer"
QMAKE_CFLAGS ="-fsanitize=undefined,address,leak -fno-omit-frame-pointer"
QMAKE_LFLAGS ="-fsanitize=undefined,address,leak -fno-omit-frame-pointer"
Jetbrains全家桶1年46,售后保障稳定
用-fsanitize=address选项,编译和链接你的程序。 用undefined,可以使用undefined behavior sanitizer检测未定义行为。 用leak,可以使用leak sanitizer检测内存泄露(很多平台默认关闭,x86架构下默认开启的)。 用-fno-omit-frame-pointer(与相对)编译,以得到更容易理解stack trace。 注:-fomit-frame-pointer是打开优化选项(-O1打开),与-fno-omit-frame-pointer相反,即在函数调用时不保存栈帧指针SFP,代价是不能通过backtrace进行调试根据堆栈信息了。
TSan环境配置: 主要用于检测多线程资源竞争的问题,但是该选项不能与-fsanitize=address、-fsanitize=leak组合。 在pro文件中添加:
代码语言:javascript复制QMAKE_CXXFLAGS ="-fsanitize=thread"<br>
QMAKE_CFLAGS ="-fsanitize=thread"<br>
QMAKE_LFLAGS ="-fsanitize=thread"<br>
CMake
在CMakeLists添加:
代码语言:javascript复制set(CMAKE_CXX_FLAGS "-fsanitize=undefined,address,leak -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "-fsanitize=undefined,address,leak -fno-omit-frame-pointer")
set(CMAKE_L_FLAGS "-fsanitize=undefined,address,leak -fno-omit-frame-pointer")
TSan环境配置:
代码语言:javascript复制set(CMAKE_CXX_FLAGS "-fsanitize=thread")
set(CMAKE_C_FLAGS "-fsanitize=thread")
set(CMAKE_L_FLAGS "-fsanitize=thread")
工作原理和使用方法
原理
Address Sanitizer替换了malloc和free的实现。当调用malloc函数时,它将分配指定大小的内存A,并将内存A周围的区域标记为”off-limits“。当free方法被调用时,内存A也被标记为”off-limits“,同时内存A被添加到隔离队列,这个操作将导致内存A无法再被重新malloc使用。 当访问到被标记为”off-limits“的内存时,Address Sanitizer就会报告异常。 === 错误类型 ===
- Use after free 释放后使用
- Heap buffer overflow 堆缓冲区溢出
- Stack buffer overflow 栈缓冲区溢出
- Global buffer overflow 全局缓冲区溢出
- Use after return 返回后使用
- Use after scope 作用域后使用
- Initialization order bugs 初始化顺序错误
- Memory leaks 内存泄露
- Using misaligned or null pointer 使用未对齐的指针
- Signed integer overflow 有符号整数溢出
- Conversion to, from, or between floating-point types which would overflow the destination 和浮点数相关转换溢出
- data race 数据竞争
错误示例:
==== Address Sanitizer ====
代码语言:javascript复制int main() {
int* array = new int[100];
delete [] array;
return array[1];
}
====16829 ==== ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000044 at pc 0x00000065b3ea bp 0x7fffffffe3a0 sp 0x7fffffffe398 · 第一部分(ERROR)指出错误类型是heap-use-after-free; READ of size 4 at 0x614000000044 thread T0 #0 0x65b3e9 in main …/…/deepin-image-viewer/viewer/main.cpp:33 #1 0x7ffff4b2409a in __libc_start_main …/csu/libc-start.c:308 #2 0x440319 in _start (/data/home/shuwenzhi/workspace/sp2/build-deepin-image-viewer-unknown-Debug/viewer/deepin-image-viewer 0x440319) · 第二部分(READ), 指出线程名thread T0,操作为READ,发生的位置是main.cpp:33。 libc_start_main()函数应执行执行环境的任何必要初始化,使用适当的参数调用main函数,并处理main()的返回。 0x614000000044 is located 4 bytes inside of 400-byte region [0x614000000040,0x6140000001d0) freed by thread T0 here: #0 0x7ffff72f3c40 in operator delete (/usr/lib/x86_64-linux-gnu/libasan.so.5 0xebc40) #1 0x65b3ab in main …/…/deepin-image-viewer/viewer/main.cpp:32 #2 0x7ffff4b2409a in __libc_start_main …/csu/libc-start.c:308 · 第二部分(freed), 该heap块之前已经在main.cpp:32被释放了; previously allocated by thread T0 here: #0 0x7ffff72f2ef0 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.5 0xeaef0) #1 0x65b38b in main …/…/deepin-image-viewer/viewer/main.cpp:31 #2 0x7ffff4b2409a in __libc_start_main …/csu/libc-start.c:308 · 第二部分(allocated), 该heap块是在main.cpp:31分配。 SUMMARY: AddressSanitizer: heap-use-after-free …/…/deepin-image-viewer/viewer/main.cpp:33 in main · 第三部分 (SUMMARY) 前面输出的概要说明。
==== Undefined Behavior Sanitizer ====
代码语言:javascript复制void testsignoverflow() {
int k = 0x7fffffff;
k = 2;
}
…/worktest/testaddress/main.cpp:11:7: runtime error: signed integer overflow: 2147483647 2 cannot be represented in type ‘int’ · 超过int范围,不能用int表达。
==== Leak Sanitizer ====
代码语言:javascript复制void* p;
int main(){
p = malloc(4);
p = 0;
return 0;
}
==7089 == ERROR: LeakSanitizer: detected memory leaks 错误类型:detected memory leaks。 Direct leak of 4 byte(s) in 1 object(s) allocated from: #0 0x7f13b7aab330 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5 0xe9330) #1 0x65b36b in main …/…/deepin-image-viewer/viewer/main.cpp:33 #2 0x7f13b52de09a in __libc_start_main …/csu/libc-start.c:308 内存在main.cpp:33分配。 SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s)。
==== Thread Sanitizer ====
代码语言:javascript复制int Global;
void *Thread1(void *x) {
Global ;
return NULL;
}
void *Thread2(void *x) {
Global--;
return NULL;
}
int main(){
pthread_t t[2];
pthread_create(&t[0], NULL, Thread1, NULL);
pthread_create(&t[1], NULL, Thread2, NULL);
pthread_join(t[0], NULL);
pthread_join(t[1], NULL);
}
WARNING: ThreadSanitizer: data race (pid=26333) · 错误类型:data race Read of size 4 at 0x000000408174 by thread T2: #0 Thread2(void*) …/worktest/testaddress/main.cpp:47 (testaddress 0x403520) #1 (libtsan.so.0 0x29b3d) · 在线程T2读取4个字节,发生在main.cpp:47。 Previous write of size 4 at 0x000000408174 by thread T1: #0 Thread1(void*) …/worktest/testaddress/main.cpp:42 (testaddress 0x4034db) #1 (libtsan.so.0 0x29b3d) · 在线程T1读取4个字节,发生在main.cpp:42。 Location is global ‘Global’ of size 4 at 0x000000408174 (testaddress 0x000000408174) Thread T2 (tid=26336, running) created by main thread at: #0 pthread_create (libtsan.so.0 0x2be1b) #1 main …/worktest/testaddress/main.cpp:74 (testaddress 0x403672) Thread T1 (tid=26335, finished) created by main thread at: #0 pthread_create (libtsan.so.0 0x2be1b) #1 main …/worktest/testaddress/main.cpp:73 (testaddress 0x403651) SUMMARY: ThreadSanitizer: data race …/worktest/testaddress/main.cpp:47 in Thread2(void*) · 线程T1、T2在主线程main.cpp的73和74行创建。
=== 使用建议 === ASAN、LSan、UBSan: 对可能出现内存泄露、访问越界、堆栈溢出,可以使用此三种工具同时检查,建议在每次提交代码之前,开启此三项检查,可以排除大部分常见错误,项目不大的话也可以配置到debug里。 Thread Sanitizer(TSan): 由于此工具会和其他工具组合冲突,建议在新增线程或者线程中可能出现data trace的情况下使用。例如:出现多线程的线程安全问题,可以开启此工具检查。 错误输出: 在正常的项目开发中,会有存有大量的日志信息输出到应用程序输出里,这样会加大查找错误信息的难度,因此建议在将sanitizer错误信息输出到日志里。
代码语言:javascript复制#include <sanitizer/asan_interface.h>
__sanitizer_set_report_path("asan.log")
在指定的目录会生成一个asan.log.pid(进程号)的文件。
总结
环境兼容
- x86:可以正常使用。
- 盘古V(wayland):错误信息不在应用程序输出里,而在编译输出里,有一个问题,编译输出错误信息后将错误代码删除,重新编译仍有错误信息。
- 鲲鹏(arm):可以正常使用,与x86使用相同。
- 龙芯(MIPS):不支持MIPS,缺少对应环境。
使用人员
- 研发人员:由于使用sanitizer工具集需要代码编译,因此建议研发人员使用。
- 测试人员:使用建议使用valgrind,详细使用请参照valgrind工具使用。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/219122.html原文链接:https://javaforall.cn