編譯後的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 競賽選手用機與評測機編譯器行為不一致而導致評測出現的問題?
為什麼微軟不單獨發行編譯器和鏈接器?

TAG:C | GCC | 編譯器 | 性能優化 | Clang |