看來gprof不能這樣做。 MinGw中不提供sprof。所以推出了我自己的分析器。
我用-finstrument-functions
所以兩個函數__cyg_profile_func_enter
和__cyg_profile_func_exit
將分別在每個函數之前和之後調用。
實際的分析函數被導出到一個DLL中並在這些函數中調用,並且該DLL被鏈接到所討論的所有EXE和DLL。因此它可以分析他們兩個
庫中的代碼是這樣的(刪除混亂:斷言,錯誤檢查,簡化函數調用爲清晰)。
static void proflib_init()
{
atexit(proflib_finalize);
empty(callstack);
empty(trackingData);
proflibIntialized = 1;
}
static void proflib_finalize()
{
/* Make a log. */
FILE *f = fopen("proflib_log.txt", "wt");
int i;
sortBySelftime(trackingData);
fprintf(f, "%10s%15s%15s%15s\n", "Func name", "Cumulative", "Self time", "Count");
for (i = 0; i < getlength(trackingData); i++)
{
FunctionTimeInfo *fri = trackingData[i];
fprintf(f, "%10p%15"PRIu64"%15"PRIu64"%20d\n", fri->addr, fri->cumulative, fri->selfTime, fri->count);
}
fclose(f);
}
void proflib_func_enter(void *func, void *caller)
{
FunctionTimeInfo elem;
long long pc;
pc = rdtsc(); /* Read timestamp counter from CPU. */
if (!is_prolib_initialized())
{
proflib_init();
}
/* Register self time as control moves to the child function. */
if (!isEmpty(callstack))
{
FunctionTimeInfo *top = gettop(callstack);
top->selfTime += pc - top->selfSample;
}
elem.addr = func; /* Address of function. */
elem.cumulative = pc; /* Time spent in function and functions called by this. (so far store the reference point only.)*/
elem.selfSample = pc; /* Reference point for self time counting. */
elem.count = 1; /* Number of this the function is counted. */
elem.selfTime = 0; /* Time spent in the function not including called functions. */
push(callstack, elem);
}
void proflib_func_exit(void *func, void *caller)
{
FunctionTimeInfo *fti;
FunctionTimeInfo *storedStat;
long long pc;
pc = rdtsc();
fti = gettop(callstack);
fti->cumulative = pc - fti->cumulative; /* Finalize the time. */
fti->selfTime += pc - fti->selfSample;
pop(callstack);
{
FunctionTimeInfo *top = gettop(callstack);
top->selfSample = pc; /* Set new self reference for the parent. */
}
storedStat = find(trackingData, func);
if (storedStat)
{
/* Already have an entry. */
storedStat->cumulative += fti->cumulative;
storedStat->selfTime += fti->selfTime;
storedStat->count++;
}
else
{
/* Add it as new entry. */
add(trackingData, fti);
}
}
而且它產生的日誌是這樣的:
Func name Cumulative Self time Count
691C83B9 1138235861408 1138235861408 1137730
00416396 539018507364 539018507364 16657216
0040A0DC 259288775768 199827541522 1914832
0041067D 876519599063 163253984165 92203200
691C9E0E 785372027387 150744125859 190020
004390F9 3608742795672 149177603708 1
0042E6A4 141485929006 116938396343 37753
00428CB8 456357355541 112610168088 193304
0041C2A4 340078363426 84539535634 114437798
691CB980 402228058455 82958191728 29675
00408A0A 79628811602 77769403982 512220
0040D8CD 93610151071 63396331438 87773597
0040D91A 60276409516 60276409516 175547194
00427C36 72489783460 58130405593 1
691C7C3D 56702394950 56702394950 3455819
691C949F 101350487028 47913486509 2977100
691CBBF3 241451044787 45153581905 29771
0043702E 920148247934 41990658926 25612
...
的函數名稱可以從MAP文件中找到了。而DLL中0x691C83B9的函數實際上是一個次優函數,具有O(n³)的複雜性,並且被稱爲很多次,我必須重構該函數......我完全忘記了該函數甚至存在... 0x004390F9是WinMain。
您也可以使用'GDB',使用Ctrl-C和'bt' 10次,並且您會在棧上看到該例程大約3次,告訴您它佔了大約33%的時間。您還將看到它的參數是什麼,例程中的哪些代碼行佔用了該時間,哪些代碼行通常從中調用,等等。這可以告訴你如何減少時間。也許論證有時是相同的。也許它被稱爲比必要的更頻繁。也許它的內部代碼可能被展開或者其他東西。少量的詳細樣本可以爲您提供更多信息。 –
@MikeDunlavey有問題的功能導致零星的微凍死。這是一個GUI應用程序,一個遊戲。當你處於全屏遊戲中的行動中時,你不能簡單地在GDB中使用Ctrl-C。 – Calmarius
如果你真的有問題,你會不會介意暫停遊戲找到它,是嗎?無論如何,如果這種情況只是偶爾發生在逐幀圖像中,那麼這種方式有一種笨拙而有效的方式。它是安排一個看門狗定時器,它只會在幀的運行時間超過N ms時中斷。在那裏設置一個斷點,當它觸發時,堆棧應該告訴你爲什麼需要額外的時間。 –