Как профилировать EXE и DLL, которые он загружал одновременно?

Я использую компилятор MinGW GCC на Windows. Если я добавлю ключ -pg к компилятору, я могу сгенерировать как EXE, так и DLL с данными профиля.

Gmon.out генерируется. Но проблема в том, что когда я использую

gprof myprogram.exe gmon.out

Я не получаю вывод профиля (кроме заголовков таблицы и другого текста).

gprof mydll.dll gmon.out

Я получаю вывод только для этой конкретной DLL, но не для основного EXE-файла.

Возможно, и exe, и dll захотели сгенерировать один и тот же файл, и dll победила.

Цель состоит в том, чтобы получить статистику для функций в EXE и DLL в одном выводе.

Может ли gprof сделать это? Если нет, есть ли инструмент, который может сделать это в Windows?

1 ответ

Решение

Кажется, gprof не может этого сделать. sprof недоступен в MinGw. Вот и прокатился мой собственный профилировщик.

я использовал -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. И функция в 0x691C83B9 в DLL действительно является неоптимальной функцией со сложностью O(n³) и вызывается много раз, я должен рефакторинг, что... Я полностью забыл, что эта функция даже существует... 0x004390F9 - это WinMain.

Другие вопросы по тегам