摘要:前言之前受知乎用戶(hù)啟發(fā),寫(xiě)了個(gè)源碼的調(diào)用圖生成器,可以以圖示法顯示函數(shù)的調(diào)用關(guān)系,代碼放在了倉(cāng)庫(kù)里,僅供參考主要思路利用的的注入選項(xiàng),得到每個(gè)函數(shù)的調(diào)用地址信息,生成一個(gè)文件,然后利用和將函數(shù)名及其所在源碼位置從地址中解析出來(lái),從而得到
前言
之前受知乎用戶(hù)mailto1587啟發(fā),寫(xiě)了個(gè)C++源碼的調(diào)用圖生成器,可以以圖示法顯示C++函數(shù)的調(diào)用關(guān)系,
代碼放在了github倉(cāng)庫(kù)里,僅供參考:
CodeSnippet/python/SRCGraphviz/c++ at master · Cheukyin/CodeSnippet · GitHub
利用gcc/g++的-finstrument-functions的注入選項(xiàng),
得到每個(gè)函數(shù)的調(diào)用地址信息,生成一個(gè)trace文件,
然后利用addr2line和c++filt將函數(shù)名及其所在源碼位置從地址中解析出來(lái),
從而得到程序的Call Stack,
然后用pygraphviz畫(huà)出來(lái)
比如我現(xiàn)在有A.hpp、B.hpp、C.hpp、ABCTest.cpp這幾個(gè)文件,
我想看他們的Call Graph
源碼如下:
然后按下面編譯(instrument.c在上面github地址中可以下載,用于注入地址信息):
g++ -g -finstrument-functions -O0 instrument.c ABCTest.cpp -o test
然后運(yùn)行程序,得到trace.txt
輸入shell命令./test
最后
輸入shell命令python CallGraph.py trace.txt test
彈出一張Call Graph
圖上標(biāo)注含義:
綠線(xiàn)表示程序啟動(dòng)后的第一次調(diào)用
紅線(xiàn)表示進(jìn)入當(dāng)前上下文的最后一次調(diào)用
每一條線(xiàn)表示一次調(diào)用,#符號(hào)后面的數(shù)字是序號(hào),at XXX表示該次調(diào)用發(fā)生在這個(gè)文件(文件路徑在框上方)的第幾行
在圓圈里,XXX:YYY,YYY是調(diào)用的函數(shù)名,XXX表示這個(gè)函數(shù)是在該文件的第幾行被定義的
獲取C/C++調(diào)用關(guān)系利用-finstrument-functions編譯選項(xiàng),
可以讓編譯器在每個(gè)函數(shù)的開(kāi)頭和結(jié)尾注入__cyg_profile_func_enter和 __cyg_profile_func_exit
這兩個(gè)函數(shù)的實(shí)現(xiàn)由用戶(hù)定義
在本例中,只用到__cyg_profile_func_enter,定義在instrument.c中,
其函數(shù)原型如下:
void __cyg_profile_func_enter (void *this_fn, void *call_site);
其中this_fn為 被調(diào)用的地址,call_site為 調(diào)用方的地址
顯然,假如我們把所有的 調(diào)用方和被調(diào)用方的地址 都打印出來(lái),
就可以得到一張完整的運(yùn)行時(shí)Call Graph
因此,我們的instrument.c實(shí)現(xiàn)如下:
/* Function prototypes with attributes */ void main_constructor( void ) __attribute__ ((no_instrument_function, constructor)); void main_destructor( void ) __attribute__ ((no_instrument_function, destructor)); void __cyg_profile_func_enter( void *, void * ) __attribute__ ((no_instrument_function)); void __cyg_profile_func_exit( void *, void * ) __attribute__ ((no_instrument_function)); static FILE *fp; void main_constructor( void ) { fp = fopen( "trace.txt", "w" ); if (fp == NULL) exit(-1); } void main_deconstructor( void ) { fclose( fp ); } void __cyg_profile_func_enter( void *this_fn, void *call_site ) { /* fprintf(fp, "E %p %p ", (int *)this_fn, (int *)call_site); */ fprintf(fp, "%p %p ", (int *)this_fn, (int *)call_site); }
其中main_constructor在 調(diào)用main 前執(zhí)行,main_deconstructor在調(diào)用main后執(zhí)行,
以上幾個(gè)函數(shù)的作用就是 將所有的 調(diào)用方和被調(diào)用方的地址 寫(xiě)入trace.txt中
然而,現(xiàn)在有一個(gè)問(wèn)題,就是trace.txt中保存的是地址,我們?nèi)绾螌⒌刂贩g成源碼中的符號(hào)?
答案就是用addr2line
以上面ABCTest.cpp工程為例,比如我們現(xiàn)在有地址0x400974,輸入以下命令
addr2line 0x400aa4 -e a.out -f
結(jié)果為
_ZN1A4AOneEv /home/cheukyin/PersonalProjects/CodeSnippet/python/SRCGraphviz/c++/A.hpp:11
第一行該地址所在的函數(shù)名,第二行為函數(shù)所在的源碼位置
然而,你一定會(huì)問(wèn),_ZN1A4AOneEv是什么鬼?
為實(shí)現(xiàn)重載、命名空間等功能,因此C++有name mangling,因此函數(shù)名是不可讀的
我們需要利用c++filt作進(jìn)一步解析:
輸入shell命令 addr2line 0x400aa4 -e a.out -f | c++filt
結(jié)果是不是就清晰很多:
A::AOne() /home/cheukyin/PersonalProjects/CodeSnippet/python/SRCGraphviz/c++/A.hpp:11
注意這個(gè)結(jié)果中包含了函數(shù)名、函數(shù)所在文件和行號(hào)
調(diào)用圖渲染經(jīng)過(guò)上面的步驟,我們已經(jīng)可以把所有的(調(diào)用方, 被調(diào)用方)對(duì)分析出來(lái)了,相當(dāng)于獲取到調(diào)用圖所有的節(jié)點(diǎn)和邊,
最后可以用pygraphviz將 每一條調(diào)用關(guān)系 畫(huà)出來(lái)即可,代碼用python實(shí)現(xiàn)在 CallGraph.py 中
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/38409.html
摘要:目前在和平臺(tái)下使用最廣泛的免費(fèi)服務(wù)器有和。涉及到普通用戶(hù)執(zhí)行命令時(shí)權(quán)限不足的問(wèn)題,可在命令前增加指令解決。是基于二進(jìn)制的線(xiàn)路協(xié)議,與協(xié)議作用相同,但屬于服務(wù)器自有協(xié)議是服務(wù)器,它實(shí)現(xiàn)了協(xié)議等協(xié)議安裝。 前言 瀏覽器上網(wǎng)的過(guò)程簡(jiǎn)單來(lái)說(shuō)即是瀏覽器從服務(wù)器中獲取網(wǎng)站信息,經(jīng)過(guò)渲染后將效果呈現(xiàn)給用戶(hù)。這里側(cè)重介紹下在幕后默默工作著的服務(wù)器。Web服務(wù)器是運(yùn)行在物理服務(wù)器上的一個(gè)程序,永久地等待...
摘要:目前在和平臺(tái)下使用最廣泛的免費(fèi)服務(wù)器有和。涉及到普通用戶(hù)執(zhí)行命令時(shí)權(quán)限不足的問(wèn)題,可在命令前增加指令解決。是基于二進(jìn)制的線(xiàn)路協(xié)議,與協(xié)議作用相同,但屬于服務(wù)器自有協(xié)議是服務(wù)器,它實(shí)現(xiàn)了協(xié)議等協(xié)議安裝。 前言 瀏覽器上網(wǎng)的過(guò)程簡(jiǎn)單來(lái)說(shuō)即是瀏覽器從服務(wù)器中獲取網(wǎng)站信息,經(jīng)過(guò)渲染后將效果呈現(xiàn)給用戶(hù)。這里側(cè)重介紹下在幕后默默工作著的服務(wù)器。Web服務(wù)器是運(yùn)行在物理服務(wù)器上的一個(gè)程序,永久地等待...
摘要:類(lèi)將源代碼解釋并構(gòu)建成抽象語(yǔ)法樹(shù),使用類(lèi)來(lái)創(chuàng)建它們,并使用類(lèi)來(lái)分配內(nèi)存。類(lèi)抽象語(yǔ)法樹(shù)的訪(fǎng)問(wèn)者類(lèi),主要用來(lái)遍歷抽象語(yǔ)法樹(shù)。在該函數(shù)中,先使用類(lèi)來(lái)生成抽象語(yǔ)法樹(shù)再使用類(lèi)來(lái)生成本地代碼。 通過(guò)上一篇文章,我們知道了JavaScript引擎是執(zhí)行JavaScript代碼的程序或解釋器,了解了JavaScript引擎的基本工作原理。我們經(jīng)常聽(tīng)說(shuō)的JavaScript引擎就是V8引擎,這篇文章我們...
摘要:現(xiàn)在,我們將會(huì)剖析的工作原理,而最重要的是它和在性能方面的比對(duì)加載時(shí)間,執(zhí)行速度,垃圾回收,內(nèi)存使用,平臺(tái)訪(fǎng)問(wèn),調(diào)試,多線(xiàn)程以及可移植性。目前,是專(zhuān)門(mén)圍繞和的使用場(chǎng)景設(shè)計(jì)的。目前不支持多線(xiàn)程。 原文請(qǐng)查閱這里,略有改動(dòng),本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第六章...
閱讀 819·2023-04-25 19:28
閱讀 1460·2021-09-10 10:51
閱讀 2469·2019-08-30 15:55
閱讀 3462·2019-08-26 13:55
閱讀 3067·2019-08-26 13:24
閱讀 3382·2019-08-26 11:46
閱讀 2807·2019-08-23 17:10
閱讀 1483·2019-08-23 16:57