大家好,我是雜燴君。本次我們來分享一個開發(fā)調試利器——Sanitizer。
Sanitizer簡介
Sanitizer是由Google發(fā)起的開源工具集,用于檢測內(nèi)存泄露等問題。
鏈接:https://github.com/google/sanitizers/wiki/
它包括了AddressSanitizer、MemorySanitizer、ThreadSanitizer、LeakSanitizer等多種工具。這些工具最初是LLVM項目的一部分,后來也被GNU的GCC編譯器支持。從GCC的4.8版本開始,就已經(jīng)支持AddressSanitizer和ThreadSanitizer,而4.9版本則開始支持LeakSanitizer。
Sanitizer使用
1、AddressSanitizer的使用例子
AddressSanitizer(ASan) 是一個快速內(nèi)存檢測器,可以檢測出緩沖區(qū)溢出、使用已釋放內(nèi)存等問題。編譯時帶上參數(shù) -fsanitize=address及-g。
(1)捕捉棧緩沖區(qū)溢出問題:
AddressSanitizer.c:
//?微信公眾號:嵌入式大雜燴
#include?<stdlib.h>
void?test_func(void)
{
????int?a[6]?=?{0};
????int?b?=?a[6];????//?棧緩沖區(qū)溢出
}?????????????????????
int?main(int?argc,?char?**argv)
{
?test_func();
?return?0;
}
編譯、運行:
gcc?AddressSanitizer.c?-fsanitize=address?-g?-o?AddressSanitizer
執(zhí)行結果分析:
觸發(fā)了檢測錯誤級別,終止程序并給出了程序運行異常的原因及異常的代碼位置。
(2)捕捉使用已釋放內(nèi)存問題:
ThreadSanitizer.c:
//?微信公眾號:嵌入式大雜燴
#include?<stdlib.h>
void?test_func(void)
{
????char?*p?=?malloc(10);
????p[0]?=?1;
????free(p);
????p[0]?=?1;??//?使用已釋放內(nèi)存
}?????????????????????
int?main(int?argc,?char?**argv)
{
?test_func();
?return?0;
}
2、ThreadSanitizer的使用例子
ThreadSanitizer(TSan) 是一個數(shù)據(jù)競爭檢測器,可以用來分析線程競態(tài)、死鎖等線程相關問題。編譯時帶上參數(shù) -fsanitize=thread及-g。
捕捉 線程間數(shù)據(jù)競爭 問題:
//?微信公眾號:嵌入式大雜燴
#include?<stdio.h>
#include?<pthread.h>
int?g_counter?=?0;??//?thread1、thread2競爭的數(shù)據(jù)
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
執(zhí)行結果分析:
觸發(fā)了檢測警告級別,程序仍能運行,并給出了程序運行有風險的原因及有風險的代碼位置。
3、程序中同時存在多處風險?
上面的例子分別使用AddressSanitizer檢測器與ThreadSanitizer檢測器來檢測對應的異常,可以較為精準地檢測到對應的異常。
如果程序中同時存在多處風險呢?
這也是比較貼近我們的實際應用的,畢竟我們并不知道我們的代碼里有哪些可能存在的風險。這種情況我們要怎么檢測?
編譯時能同時帶上多個-fsanitize參數(shù)調用多個檢測器嗎?
可以同時帶,但有些檢測器不能同時使用。
AddressSanitizer與ThreadSanitizer檢測器不能同時使用。
但是,假如我們的程序中恰好存在address異常與thread異常呢,單獨使用AddressSanitizer檢測器、ThreadSanitizer檢測器的表現(xiàn)是怎樣的?
比如,我們把上面3個例子的代碼放在一起:
test.c:
//?微信公眾號:嵌入式大雜燴
#include?<stdio.h>
#include?<stdlib.h>
#include?<pthread.h>
int?g_counter?=?0;??//?thread1、thread2競爭的數(shù)據(jù)
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);
}
//?測試:使用已釋放內(nèi)存
void?test_func1(void)
{
????char?*p?=?malloc(10);
????printf("This?is?test_func1n");
????p[0]?=?1;
????free(p);
????p[0]?=?1;??//?使用已釋放內(nèi)存
}?????
//?測試:棧緩沖區(qū)溢出
void?test_func2(void)
{
????int?a[6]?=?{0};
????int?b?=?a[6];????//?棧緩沖區(qū)溢出
}?????
int?main(int?argc,?char?**argv)
{
????test_func();
????test_func1();
?test_func2();
????return?0;
}
帶-fsanitize=thread參數(shù)編譯、運行:
執(zhí)行結果分析:
ThreadSanitizer檢測器能正常檢測出資源競爭的問題,也檢測出了test_func1中的使用已釋放的堆內(nèi)存的問題并以警告級別報告,但沒有檢測出test_func2的棧緩沖區(qū)溢出問題。
是不是因為test_func2運行在test_func1后面了,所以test_func2的異常沒有被ThreadSanitizer檢測器檢測出來?
我們調換個位置看看:
int?main(int?argc,?char?**argv)
{
????test_func();
?test_func2();
????test_func1();
????return?0;
}
顯然,執(zhí)行結果還是一樣的,test_func2的棧緩沖區(qū)溢出問題還是沒有被ThreadSanitizer檢測器檢測出來。
所以,大致得出結論:當程序里存在thread異常與address異常時,使用ThreadSanitizer檢測器能準確檢測到thread異常,能檢測到部分address異常。
帶-fsanitize=address參數(shù)編譯、運行:
執(zhí)行順序:
int?main(int?argc,?char?**argv)
{
????test_func();
????test_func1();
????test_func2();
????return?0;
}
執(zhí)行結果分析:
AddressSanitizer檢測器檢測到了test_func1中的已使用釋放的堆內(nèi)存的異常并以錯誤級別報告,并終止了程序;沒有檢測到test_func的資源競爭的風險;也沒有檢測到test_func2的棧緩沖區(qū)溢出的問題,因為執(zhí)行到test_func1的時候程序已經(jīng)被終止了,如果把test_func2放在test_func1之前運行,就能檢測到test_func2的異常。
結論:當程序里存在thread異常與address異常時,使用AddressSanitizer檢測器能準確檢測到第一個觸發(fā)的address異常,不能檢測到thread異常。
如果程序中存在多種可能存在的風險時,需要使用多個檢測器單獨挨個檢測。每個檢測器都有其擅長檢測的方面,可以經(jīng)過初步分析之后確定大致地方向,選擇適合地檢測器來做檢測。
以上就是關于Sanitizer的一些簡單介紹及使用的分享,更多的關于Sanitizer的資料可查閱:https://github.com/google/sanitizers/wiki/
碼字不易,如果文章對你有幫助,麻煩幫忙點贊、關注,謝謝大家!