2014-12-28 13 views
0

我在Windows上使用MinGW GCC编译器。如果我添加-pg切换到编译器,我可以使用配置文件数据生成EXE和DLL。如何分析一个EXE和它在同一时间加载的DLL?

gmon.out生成。但问题是,当我使用

gprof myprogram.exe gmon.out 

我没有得到任何的个人资料输出(比表格标题和其他文本等)。

gprof mydll.dll gmon.out 

我只得到该特定DLL的输出,但不是主要的exe。

也许exe和dll都希望生成相同的文件并获得dll。

目标是在一个输出中获取EXE和DLL中函数的统计信息。

gprof能做到吗?如果没有,有什么工具可以在Windows上做到这一点?

回答

0

看来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。

+0

您也可以使用'GDB',使用Ctrl-C和'bt' 10次,并且您会在栈上看到该例程大约3次,告诉您它占了大约33%的时间。您还将看到它的参数是什么,例程中的哪些代码行占用了该时间,哪些代码行通常从中调用,等等。这可以告诉你如何减少时间。也许论证有时是相同的。也许它被称为比必要的更频繁。也许它的内部代码可能被展开或者其他东西。少量的详细样本可以为您提供更多信息。 –

+0

@MikeDunlavey有问题的功能导致零星的微冻死。这是一个GUI应用程序,一个游戏。当你处于全屏游戏中的行动中时,你不能简单地在GDB中使用Ctrl-C。 – Calmarius

+0

如果你真的有问题,你会不会介意暂停游戏找到它,是吗?无论如何,如果这种情况只是偶尔发生在逐帧图像中,那么这种方式有一种笨拙而有效的方式。它是安排一个看门狗定时器,它只会在帧的运行时间超过N ms时中断。在那里设置一个断点,当它触发时,堆栈应该告诉你为什么需要额外的时间。 –