日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

效能优化实践:C/C++单元测试万能插桩工具

發布時間:2024/2/28 c/c++ 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 效能优化实践:C/C++单元测试万能插桩工具 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:mannywang,騰訊安全平臺后臺開發

研發效能是一個涉及面很廣的話題,它涵蓋了軟件交付的整個生命周期,涉及產品、架構、開發、測試、運維,每個環節都可能影響順暢、高質量地持續有效交付。在騰訊安全平臺部實際研發與測試工作中我們發現,代碼插樁隔離是單元測試工作中的一個強需求,然而業界現有 C/C++插樁工具由于使用上的局限性,運行效率和體驗仍有很大改善空間。本文介紹了團隊基于研效優化實踐而自研的動態插樁工具,旨在實現單元測試的輕量化運行,提高代碼覆蓋率,從而助力研發團隊的效能提升。

問題&思路

目前存在的 C/C++插樁工具,基本上都有各種使用上的局限,比如流行的 gmock,只能對 C++的虛函數進行插樁替換,針對非虛函數,則需要先對被測代碼進行改造;同時對于系統接口,C 風格的第三方庫代碼,也無能為力。

如果可以繞開編譯器,直接從底層入手,比如做機器指令修改,則可以不受語法及編譯器的束縛,直接達到目的,這樣在使用中就 幾乎不受限制

原理

C/C++語言編譯后的可執行體,其實就是一個個的函數實現,每個函數的開頭就是它的入口。一個函數 A 調用另一個函數 B,就是代碼在執行過程中,控制流從函數 A 的某處跳到了函數 B 的開頭,所以如果想用一個新的函數 C 取代函數 B,可以在函數 B 的開頭用機器碼的形式寫入如下等價邏輯:

MOVQ?ADDRESS_OF_C?%RAX?//將函數C的地址放到寄存器RAX JMPQ?*RAX???????????//無條件跳轉到RAX所指向的位置

這樣,當控制流從函數 A 進入函數 B 的開始位置的時候,即會執行上述代碼,從而直接跳轉到 C 的開頭處。其最終效果,是所有對函數 B 的調用,都如同直接調用了函數 C。

基于上述原理,被插樁的代碼包括第三方庫,如 MySql、其他同事未完成的模塊、甚至是操作系統的 API 接口,如 read、select 等

同時,樁函數不僅可以模擬原函數的返回值,實際上它作為一個普通的 C 函數,對原函數有完全的操作能力,比如可以訪問傳遞給原函數調用真實的參數、C++成員變量(針對對成員函數的模擬),給定任意的返回值,訪問全局變量、對調用進行計數等

實際實現中,考慮到不同測試用例間的互不干擾,除了能執行函數替換,還需要在執行完一個測試時還原現場。這些具體細節可以直接參考代碼。

使用

對全局函數插樁

原始函數:

int?global(int?a,?int?b)?{return?a?+?b; }

對應的樁函數:

int?fake_global(int?a,?int?b)?{//校驗參數正確性,確定被測代碼傳入了正確的值assert(a?==?3);assert(b?==?2);//給一個返回值,配合被測代碼走特定分支return?a?-?b; }

插樁示例:

assert(global(3,?2)?==?5);//通過mock調用,完成函數動態替換 assert(0?==?mock(&global,?&fake_global));//調用mock后的函數,可以看到返回值變了 assert(global(3,?2)?==?1);//結束mock reset();//函數行為恢復 assert(global(3,?2)?==?5);

對普通成員函數插樁

被測代碼:

class?A?{ public:int?member(int?a)?{return?++a;}static?int?static_member(int?a)?{return?200;}virtual?int?virtual_member()?{return?400;} };

樁函數:

int?fake_member(A?*pTihs,?int?a)?{//由于是對成員函數插樁,這里需要這個this指針參數return?--a; }

插樁示例:

A?a; assert(a.member(100)?==?101);mock(&A::member,?fake_member); assert(a.member(100)?==?99);reset();assert(a.member(100)?==?101);

對靜態成員函數插樁

樁函數:

int?fake_static_member()?{//靜態函數不需要this指針return?300; }

插樁示例:

assert(A::static_member(200)?==?200);mock(&A::static_member,?fake_static_member); assert(A::static_member(100)?==?300);reset();assert(A::static_member(200)?==?200);

對虛函數插樁

樁函數:

int?fake_virtual_member(A?*pThis)?{//虛函數同普通的成員函數由于,同樣需要this指針return?500; }

插樁示例:

A?a; assert(a.virtual_member()?==?400);//虛函數mock需要多傳一個相關類的對象,任意一個對象即可,跟實際代碼中的對象沒有關系 A?a_obj; mock(&A::virtual_member,?fake_virtual_member,?&a_obj); assert(a.virtual_member()?==?500);reset(); assert(a.virtual_member()?==?400);

對系統及第三方庫函數插樁

樁函數:

int?fake_write(int,?char*,?int)?{return?100; }

插樁示例:

//直接寫入一個無效的文件描述符,會失敗 assert(write(5,?"hello",?5)?==?-1);//來一個假的wirte mock(write,?fake_write); //模擬調用成功 assert(write(5,?"hello",?5)?==?100);reset();assert(write(5,?"hello",?5)?==?-1);

可以看到,對系統函數的 mock,其實跟普通的全局函數并無兩樣,第三方庫函數也是同理。

使用限制&注意事項

  • 目前支持 X86_64 平臺上的 Linux、MacOS 系統,如有需求,Windows 和其它硬件平臺,如 X86_32、ARM,也可在短期內支持。

  • MacOS 下,需要在執行前對單測可執行文件做以下修改:

printf?'\x07'?|?dd?of=<ut_executable>?bs=1?seek=160?count=1?conv=notrunc
  • 顯然,這種方法對內聯函數無效,不過對于單元測試來說,可以關閉內聯,同時也建議關閉其它編譯器優化。

  • 可以使用-fno-access-control 編譯你的測試代碼,可以使 g++關閉 c++成員的訪問控制(即 protected 及 private 不再生效)。

項目地址

https://github.com/wangyongfeng5/lmock

結語

持續改進是研效工具平臺發展的必經之路,歡迎感興趣的同學與我們交流探討,共同助力測試效能的優化。

總結

以上是生活随笔為你收集整理的效能优化实践:C/C++单元测试万能插桩工具的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。