<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          工程師必備:C/C++單元測試萬能插樁工具

          共 3217字,需瀏覽 7分鐘

           ·

          2021-11-04 14:43

          關(guān)注、星標(biāo)公眾號,直達(dá)精彩內(nèi)容

          來源:騰訊技術(shù)工程

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

          問題&思路

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

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

          原理

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

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

          這樣,當(dāng)控制流從函數(shù) A 進(jìn)入函數(shù) B 的開始位置的時(shí)候,即會(huì)執(zhí)行上述代碼,從而直接跳轉(zhuǎn)到 C 的開頭處。其最終效果,是所有對函數(shù) B 的調(diào)用,都如同直接調(diào)用了函數(shù) C。

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

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

          實(shí)際實(shí)現(xiàn)中,考慮到不同測試用例間的互不干擾,除了能執(zhí)行函數(shù)替換,還需要在執(zhí)行完一個(gè)測試時(shí)還原現(xiàn)場。這些具體細(xì)節(jié)可以直接參考代碼。

          使用

          對全局函數(shù)插樁

          原始函數(shù):

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

          對應(yīng)的樁函數(shù):

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

          插樁示例:


          assert(global(3,?2)?==?5);

          //通過mock調(diào)用,完成函數(shù)動(dòng)態(tài)替換
          assert(0?==?mock(&global,?&fake_global));

          //調(diào)用mock后的函數(shù),可以看到返回值變了
          assert(global(3,?2)?==?1);

          //結(jié)束mock
          reset();

          //函數(shù)行為恢復(fù)
          assert(global(3,?2)?==?5);

          對普通成員函數(shù)插樁

          被測代碼:

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

          樁函數(shù):

          int?fake_member(A?*pTihs,?int?a)?{
          ??//由于是對成員函數(shù)插樁,這里需要這個(gè)this指針參數(shù)
          ????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);

          對靜態(tài)成員函數(shù)插樁

          樁函數(shù):

          int?fake_static_member()?{
          ??//靜態(tài)函數(shù)不需要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);

          對虛函數(shù)插樁

          樁函數(shù):

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

          插樁示例:

          A?a;
          assert(a.virtual_member()?==?400);

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

          reset();
          assert(a.virtual_member()?==?400);

          對系統(tǒng)及第三方庫函數(shù)插樁

          樁函數(shù):

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

          插樁示例:

          //直接寫入一個(gè)無效的文件描述符,會(huì)失敗
          assert(write(5,?"hello",?5)?==?-1);

          //來一個(gè)假的wirte
          mock(write,?fake_write);
          //模擬調(diào)用成功
          assert(write(5,?"hello",?5)?==?100);

          reset();

          assert(write(5,?"hello",?5)?==?-1);

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

          使用限制&注意事項(xiàng)

          • 目前支持 X86_64 平臺(tái)上的 Linux、MacOS 系統(tǒng),如有需求,Windows 和其它硬件平臺(tái),如 X86_32、ARM,也可在短期內(nèi)支持。
          • MacOS 下,需要在執(zhí)行前對單測可執(zhí)行文件做以下修改:
          printf?'\x07'?|?dd?of=?bs=1?seek=160?count=1?conv=notrunc
          • 顯然,這種方法對內(nèi)聯(lián)函數(shù)無效,不過對于單元測試來說,可以關(guān)閉內(nèi)聯(lián),同時(shí)也建議關(guān)閉其它編譯器優(yōu)化。
          • 可以使用-fno-access-control 編譯你的測試代碼,可以使 g++關(guān)閉 c++成員的訪問控制(即 protected 及 private 不再生效)。

          項(xiàng)目地址

          https://github.com/wangyongfeng5/lmock

          結(jié)語

          持續(xù)改進(jìn)是研效工具平臺(tái)發(fā)展的必經(jīng)之路,歡迎感興趣的同學(xué)與我們交流探討,共同助力測試效能的優(yōu)化。

          ???????????????? ?END ?????????????????

          關(guān)注我的微信公眾號,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。

          歡迎關(guān)注我的視頻號:

          點(diǎn)擊“閱讀原文”查看更多分享,歡迎點(diǎn)分享、收藏、點(diǎn)贊、在看。

          瀏覽 69
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人大香蕉综合 | 18禁国产精品久久久久 | 黄色成人网站在线免费看 | 插逼视频网站大全 | 国产亚洲色情 |