精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

C++的全鏈路追蹤方案

汽車電子技術 ? 來源:程序喵大人 ? 作者:程序喵大人 ? 2023-02-21 14:15 ? 次閱讀

**背景:**本人主要在做C++ SDK的開發,需要給到業務端去集成,在集成的過程中可能會出現某些功能性bug,即沒有得到想要的結果。那怎么調試?

**分析:**這種問題其實調試起來稍微有點困難,它不像crash,當發生crash時還能拿到堆棧信息去分析,然而功能性bug沒有crash,也就沒法捕捉對應到當時的堆棧信息。因為不是在本地,也沒法用編譯器debug。那思路就剩log了,一種方式是考慮在SDK內部的關鍵路徑下打印詳細的log,當出現問題時拿到log去分析。然而總有漏的時候,誰能保證log一定打的很全面,很有可能問題就出現在沒有log的函數中。

**解決:**基于上面的背景和問題分析,考慮是否能做一個全鏈路追蹤的方案,把打印出整個SDK的調用路徑,從哪個函數進入,從哪個函數退出等。

**想法1:**可以考慮在SDK的每個接口都加一個context結構體參數,記錄下來函數的調用路徑,這可能是比較通用有效的方案,但是SDK接口已經固定了,更改接口要面臨的困難很大,業務端基本不會同意,所以這種方案不適合我們現有情況,當然一個從0開始建設的中間件和SDK可以考慮考慮。

**想法2:**有沒有一種不用改接口,還能追蹤到函數調用路徑的方案?

繼續沿著這個思路繼續調研,我找到了gcc和clang編譯器的一個編譯參數:-finstrument-functions,編譯時添加此參數會在函數的入口和出口處觸發一個固定的回調函數,即:

__cyg_profile_func_enter(void *callee, void *caller);
__cyg_profile_func_exit(void *callee, void *caller);

參數就是callee和caller的地址,那怎么將地址解析成對應函數名?可以使用dladdr函數:

int dladdr(const void *addr, Dl_info *info);

看下下面的代碼:

// tracing.cc


#include 
#include   // for dladdr
#include 
#include 
#include 


#ifndef NO_INSTRUMENT
#define NO_INSTRUMENT __attribute__((no_instrument_function))
#endif


extern "C" __attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *callee, void *caller) {
    Dl_info info;
    if (dladdr(callee, &info)) {
        int status;
        const char *name;
        char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
        if (status == 0) {
            name = demangled ? demangled : "[not demangled]";
        } else {
            name = info.dli_sname ? info.dli_sname : "[no dli_sname nd std]";
        }


        printf("enter %s (%s)\\n", name, info.dli_fname);


        if (demangled) {
            free(demangled);
            demangled = NULL;
        }
    }
}


extern "C" __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *callee, void *caller) {
    Dl_info info;
    if (dladdr(callee, &info)) {
        int status;
        const char *name;
        char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
        if (status == 0) {
            name = demangled ? demangled : "[not demangled]";
        } else {
            name = info.dli_sname ? info.dli_sname : "[no dli_sname and std]";
        }
        printf("exit %s (%s)\\n", name, info.dli_fname);


        if (demangled) {
            free((void *)demangled);
            demangled = NULL;
        }
    }
}

這是測試文件:

// test_trace.cc
void func1() {}


void func() { func1(); }


int main() { func(); }
將test_trace.cc和tracing.cc文件同時編譯鏈接,即可達到鏈路追蹤的目的:
g++ test_trace.cc tracing.cc -std=c++14 -finstrument-functions -rdynamic -ldl;./a.out
輸出:enter main (./a.out)
enter func() (./a.out)
enter func1() (./a.out)
exit func1() (./a.out)
exit func() (./a.out)
exit main (./a.out)

如果在func()中調用了一些其他的函數呢?

#include 
#include 


void func1() {}


void func() {
    std::vector<int> v{1, 2, 3};
    std::cout << v.size();
    func1();
}


int main() { func(); }

再重新編譯后輸出會是這樣:

enter [no dli_sname nd std] (./a.out)
enter [no dli_sname nd std] (./a.out)
exit [no dli_sname and std] (./a.out)
exit [no dli_sname and std] (./a.out)
enter main (./a.out)
enter func() (./a.out)
enter std::allocator<int>::allocator() (./a.out)
enter __gnu_cxx::new_allocator<int>::new_allocator() (./a.out)
exit __gnu_cxx::new_allocator<int>::new_allocator() (./a.out)
exit std::allocator<int>::allocator() (./a.out)
enter std::vector<int, std::allocator<int> >::vector(std::initializer_list<int>, std::allocator<int> const&) (./a.out)
enter std::_Vector_base<int, std::allocator<int> >::_Vector_base(std::allocator<int> const&) (./a.out)
enter std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Vector_impl(std::allocator<int> const&) (./a.out)
enter std::allocator<int>::allocator(std::allocator<int> const&) (./a.out)
enter __gnu_cxx::new_allocator<int>::new_allocator(__gnu_cxx::new_allocator<int> const&) (./a.out)
exit __gnu_cxx::new_allocator<int>::new_allocator(__gnu_cxx::new_allocator<int> const&) (./a.out)
exit std::allocator<int>::allocator(std::allocator<int> const&) (./a.out)
exit std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Vector_impl(std::allocator<int> const&) (./a.out)
exit std::_Vector_base<int, std::allocator<int> >::_Vector_base(std::allocator<int> const&) (./a.out)

上面我只貼出了部分信息,這顯然不是我們想要的,我們只想要顯示自定義的函數調用路徑,其他的都想要過濾掉,怎么辦?

這里可以將自定義的函數都加一個統一的前綴,在打印時只打印含有前綴的符號,這種個人認為是比較通用的方案。

下面是我過濾掉std和gnu子串的代碼:

if (!strcasestr(name, "std") && !strcasestr(name, "gnu")) {
    printf("enter %s (%s)\\n", name, info.dli_fname);
}


if (!strcasestr(name, "std") && !strcasestr(name, "gnu")) {
    printf("exit %s (%s)\\n", name, info.dli_fname);
}

重新編譯后就會輸出我想要的結果:

g++ test_trace.cc tracing.cc -std=c++14 -finstrument-functions -rdynamic -ldl;./a.out
輸出:enter main (./a.out)
enter func() (./a.out)
enter func1() (./a.out)
exit func1() (./a.out)
exit func() (./a.out)
exit main (./a.out)

還有一種方式是在編譯時使用下面的參數:

-finstrument-functions-exclude-file-list

它可以排除不想要做trace的文件,但是這個參數只在gcc中可用,在clang中卻不支持 ,所以上面的字符串過濾方式更通用一些。

上面只能拿到函數的名字,不能定位到具體的文件和行號,如果想要獲得更多信息,需要結合bfd系列參數(bfd_find_nearest_line)和libunwind一起使用,大家可以繼續研究。。。

tips1: 這是一篇拋磚引玉的文章,本人不是后端開發,據我所知后端C++中有很多成熟的trace方案,大家有更好的方案可以留言,分享一波。

tips2: 上面的方案可以達到鏈路追蹤的目的,但本人最后沒有應用到項目中,因為本人在做的項目對性能要求較高,使用此種方案會使整個SDK性能下降嚴重,無法滿足需求正常運行。于是暫時放棄了鏈路追蹤的這個想法。

本文的知識點還是值得了解一下的,大家或許會用得到。在研究的過程中我也發現了一個基于此種方案的開源項目(call-stack-logger),感興趣的也可以去了解了解。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 堆棧
    +關注

    關注

    0

    文章

    182

    瀏覽量

    19732
  • 編譯器
    +關注

    關注

    1

    文章

    1618

    瀏覽量

    49052
  • BUG
    BUG
    +關注

    關注

    0

    文章

    155

    瀏覽量

    15653
收藏 人收藏

    評論

    相關推薦

    實時監測倒換系統

    系統移動通信解決方案旨在對移動運營商光纜重要或應用場合進行實時監控和故障恢復快速的保護倒換,保證光網絡鏈路層的穩定,提高光網絡的可靠性;同時該系統可輔助網管中心實時監視全網光功率動態、為值勤維護人員快速
    發表于 12-02 09:50

    壓測一招搞定,阿里云性能測試鉑金版發布

    版本的發布,就是把全局的復雜流量形態編排、串聯這塊的工作變成一個基于直白式交互的功能,可以讓用戶在壓測場景構造和復現上無需編碼,網頁上就能編排復雜的壓測業務,希望以更智能而簡單的方案
    發表于 01-30 14:13

    基于分布式調用監控技術的全息排查功能

    的分布式追蹤功能結合周邊的本地堆棧排查,性能Metrics統計,業務全息排查三大附加功能,形成了完整的
    發表于 08-07 17:02

    監控工具Skywalking使用指南

    國產監控工具Skywalking
    發表于 09-03 14:26

    如何為Arm編譯Cc++代碼

    編寫CC++應用程序時,需要使用編譯器工具將其編譯為機器代碼。然后,您可以在基于Arm的處理器上運行此編譯的可執行代碼,或者使用模型對其進行模擬。 裸機編譯編譯器工具包括以下組件
    發表于 08-02 17:28

    C++設計校園最短路徑的設計方案

    C++設計校園最短路徑的設計方案
    發表于 12-30 15:04 ?1次下載

    淺談OPPO設計的首個Android色彩管理系統

    2020年11月18日,中國,深圳--今日,OPPO在以“躍遷·致善”為主題的未來科技大會2020(OPPO INNO DAY 2020)上,正式推出OPPO色彩管理系統,同時也是面向未來廣色域、高色深時代設計的首個And
    的頭像 發表于 11-18 16:25 ?1868次閱讀

    2021 OPPO開發者大會:運營

    2021 OPPO開發者大會:運營 2021 OPPO開發者大會上介紹了運營,技術能
    的頭像 發表于 10-27 15:07 ?2274次閱讀
    2021 OPPO開發者大會:<b class='flag-5'>全</b><b class='flag-5'>鏈</b><b class='flag-5'>路</b>運營

    安歌科技Enotek如何實現智能物流解決方案?

    除了核心軟硬件自研自產外,作為一家深耕物流領域十四余載的工業智能物流解決方案提供商,安歌科技始終秉承“以創新,致未來”的初心,致力于為各行業提供安全可靠、智能高效的整體集成
    的頭像 發表于 11-11 15:14 ?652次閱讀

    追蹤系統SkyWalking的原理

    什么是追蹤? 追蹤的原理
    的頭像 發表于 01-17 11:00 ?4079次閱讀

    介紹Node.js應用信息獲取的方法

    這種技術,就是Node.js應用追蹤。它是 Node.js 在涉及到復雜服務端業務場景中,必不可少的技術保障。
    的頭像 發表于 02-10 11:21 ?608次閱讀

    淺談C語言與C++的前世今生

    C++開發人員將有這些問題歸咎于C,而C開發人員則認為C++過于瘋狂。我覺得站在C的角度看C++
    發表于 05-26 09:27 ?438次閱讀
    淺談<b class='flag-5'>C</b>語言與<b class='flag-5'>C++</b>的前世今生

    c++實現的多畫面視頻分割示例

    c++實現的多畫面視頻分割示例,有4、8、16、64顯示等顯示分割界面
    發表于 08-30 17:40 ?3次下載

    C++之父新作帶你勾勒現代C++地圖

    為了幫助大家解決這些痛點問題,讓大家領略現代C++之美,掌握其中的精髓,更好地使用C++,C++之父Bjarne Stroustrup坐不住了,他親自操刀寫就了這本《C++之旅》!
    的頭像 發表于 10-30 16:35 ?798次閱讀
    <b class='flag-5'>C++</b>之父新作帶你勾勒現代<b class='flag-5'>C++</b>地圖

    C++簡史:C++是如何開始的

    MISRA C++:2023,MISRA? C++ 標準的下一個版本,來了!為了幫助您做好準備,我們介紹了 Perforce 首席技術支持工程師 Frank van den Beuken 博士撰寫
    的頭像 發表于 01-11 09:00 ?539次閱讀
    <b class='flag-5'>C++</b>簡史:<b class='flag-5'>C++</b>是如何開始的