編譯後的c/c++ 程序, 如何判斷一個函數 是否 真的inline了(如果不看反彙編話)?
如果手上只有編譯後的二進位文件,那麼閱讀代碼反彙編是最靠譜的辦法。除此之外就沒啥正統的好辦法,要麼適用的平台受限(例如說要使用依賴平台相關的API),要麼適用的條件受限(例如說依賴於編譯器的優化程度、檢測手段是否會干擾優化、編譯出來的二進位文件是否包含調試符號信息之類)。
不依賴分析編譯出來的代碼的話,剩下的都只是花樣技巧了。
花樣技巧之一是運行時用backtrace() / backtrace_symbols()來檢測是否有內聯。思路是通過backtrace()運行時查看當前棧頂的棧幀屬於哪個函數;如果該函數與發起探測的函數的名字完全相同,那麼就沒有內聯;否則就是被內聯了。例如說下面的代碼在bar()里想檢測自己是否被inline了:
#include &
#include &
#include &
#include &
#define BUF_SIZE 100
char* enclose_str_with(char* buf, size_t buflen, char left, const char* str, char right) {
size_t len = strlen(str);
if (buflen &< len + 3) return NULL; /* for left, right and terminal */
memset(buf, 0, buflen);
buf[0] = left;
strncpy(buf[1], str, len);
buf[len + 1] = right;
return buf;
}
void print_inlined(const char* funcname, char* top_frame_symbol) {
char buf[BUF_SIZE];
char* name = enclose_str_with(buf, BUF_SIZE, " ", funcname, " ");
if (name) {
if (strstr(top_frame_symbol, name)) {
printf("%s() is not inlined
", funcname);
} else {
printf("%s() is inlined
", funcname);
}
} else {
printf("ERROR: print_inlined(): buf isn"t big enough, need %lu
", strlen(funcname) + 3);
}
}
void bar() {
void* buffer[1];
int size = backtrace(buffer, 1);
char** symbols = backtrace_symbols(buffer, size);
if (size &>= 1) {
printf("%s
", symbols[0]);
print_inlined(__func__, symbols[0]);
} else {
printf("bar() uknown to inlined or not
");
}
free(symbols);
}
void foo() {
bar();
}
int main() {
foo();
return 0;
}
$ gcc -std=c99 xx.c
$ ./a.out
0 a.out 0x000000010c1a4c52 bar + 34
bar() is not inlined
$ gcc -std=c99 -O3 xx.c
$ ./a.out
0 a.out 0x000000010656dccc main + 28
bar() is inlined
$
可以看到-O3編譯時bar()有被內聯到main(),而默認級別(-O0)編譯時則沒有被內聯。
多說一句,這裡在-O3時內聯樹是:
main()
foo()
bar()
從bar()發起backtrace()檢測,發現名字是main()而不是自己,這就知道被內聯了。
這個花招挺渣的其實。能影響它的正確運行的因素相當多,例如生成的二進位文件里是否有調試符號信息之類——strip了符號信息的話就沒轍了。發出來只是演示給題主看「辦法不是沒有」而已。VC++可以輸出inlinelog, 整個inline tree都看的一清二楚。
這是個link option, 用法如:
cl.exe /GL test.cpp /link /d2:-inlinelog
推薦閱讀:
※Swift,Haskell這種可以自定義運算符的語言(不僅僅是重載),在實現編譯器時跟其他語言又什麼區別嗎?
※新版本編譯器是用自己(新版本編譯器)重新編譯後發布還是用舊版本編譯器編譯發布?
※用new申請的內存如果使用free函數來釋放會有怎樣的結果?
※如何看待 NOIP 競賽選手用機與評測機編譯器行為不一致而導致評測出現的問題?
※為什麼微軟不單獨發行編譯器和鏈接器?