Инструкции по трассировке / профилированию - PullRequest
13 голосов
/ 04 июня 2010

Я хотел бы статистически профилировать мой C-код на уровне инструкций. Мне нужно знать, сколько сложений, умножений, делений и т. Д. Я выполняю.

Это не обычный запуск требования профилирования кода фрезерного кода. Я разработчик алгоритмов и хочу оценить стоимость преобразования моего кода в аппаратные реализации. Для этого меня просят разбить вызов инструкции во время выполнения (синтаксический анализ скомпилированной сборки недостаточен, поскольку он не учитывает циклы в коде).

После осмотра кажется, что VMware может предложить возможное решение, но я все еще не смог найти особую функцию, которая позволила бы мне отслеживать поток вызовов инструкций моего процесса.

Вам известны какие-либо инструменты профилирования, которые позволяют это?

Ответы [ 5 ]

10 голосов
/ 08 июня 2010

Я в конечном итоге использовал тривиальное, но эффективное решение.

  1. Настроил GDB для отображения разборки следующей инструкции (каждый раз, когда она останавливается), вызвав:

display/i $pc

  1. Сконфигурировал простой скрипт gdb, который разбивает функцию, которую мне нужно проанализировать, и переходит к пошаговой инструкции по инструкции: set $i=0 break main run while ($i<100000) si set $i = $i + 1 end quit

  2. Выполнен GDB с выводом моего скрипта в файл журнала:

    gdb -x script a.out > log.txt

  3. Анализ журнала для подсчета вызовов определенных команд.

грубо, но работает ...

6 голосов
/ 19 мая 2014

Вы можете использовать pin-instat , который является инструментом PIN. Это немного перебивает, так как записывает больше информации, чем счетчик команд. Это все еще должно быть более эффективным, чем ваш подход к GDB.

Отказ от ответственности: я автор pin-instat.

5 голосов
/ 19 мая 2014

Инструмент Linux perf даст вам много информации для профилирования; в частности, perf annotate даст вам относительное количество за инструкцию.

Можно перейти к уровню инструкций с помощью perf annotate. Для этого вам нужно вызвать perf annotate с именем команды для аннотирования. Все функции с образцами будут разобраны, и в каждой инструкции будет указан относительный процент образцов:
perf record ./noploop 5
perf annotate -d ./noploop

------------------------------------------------
 Percent |   Source code & Disassembly of noploop.noggdb
------------------------------------------------
         :
         :
         :
         :   Disassembly of section .text:
         :
         :   08048484 <main>:
    0.00 :    8048484:       55                      push   %ebp
    0.00 :    8048485:       89 e5                   mov    %esp,%ebp [...]
    0.00 :    8048530:       eb 0b                   jmp    804853d <main+0xb9>
   15.08 :    8048532:       8b 44 24 2c             mov    0x2c(%esp),%eax
    0.00 :    8048536:       83 c0 01                add    $0x1,%eax
   14.52 :    8048539:       89 44 24 2c             mov    %eax,0x2c(%esp)
   14.27 :    804853d:       8b 44 24 2c             mov    0x2c(%esp),%eax
   56.13 :    8048541:       3d ff e0 f5 05          cmp    $0x5f5e0ff,%eax
    0.00 :    8048546:       76 ea                   jbe    8048532 <main+0xae> [...]
4 голосов
/ 04 июня 2010

Инструмент valgrind cachegrind может использоваться для получения количества выполнений каждой строки в скомпилированной сборке (значение Ir в первом столбце).

0 голосов

Режим пользователя QEMU -d in_asm

Это еще одна простая вещь, которую вы можете сделать, чтобы получить трассировку инструкции:

sudo apt-get install qemu-user
qemu-x86_64 -d in_asm main.out

Давайте проверим это с тройным привет миром x86_64:

main.S

.text
.global _start
_start:
asm_main_after_prologue:
    mov $3, %rbx
write:
    mov $1, %rax    /* syscall number */
    mov $1, %rdi    /* stdout */
    mov $msg, %rsi  /* buffer */
    mov $len, %rdx  /* len */
    syscall
    dec %rbx
    jne write
exit:
    mov $60, %rax   /* syscall number */
    mov $0, %rdi    /* exit status */
    syscall
msg:
    .ascii "hello\n"
len = . - msg

адаптировано с GitHub upstream .

Собрать и запустить с:

as -o main.o main.S 
ld -o main.out main.o
./main.out

Вывод Stdout:

hello
hello
hello

При запуске через QEMU трассировка инструкций выводится в stderr:

warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
host mmap_min_addr=0x10000
Reserved 0x1000 bytes of guest address space
Relocating guest address space from 0x0000000000400000 to 0x400000
guest_base  0x0
start            end              size             prot
0000000000400000-0000000000401000 0000000000001000 r-x
0000004000000000-0000004000001000 0000000000001000 ---
0000004000001000-0000004000801000 0000000000800000 rw-
start_brk   0x0000000000000000
end_code    0x00000000004000b8
start_code  0x0000000000400000
start_data  0x00000000004000b8
end_data    0x00000000004000b8
start_stack 0x00000040007fed70
brk         0x00000000004000b8
entry       0x0000000000400078
----------------
IN: 
0x0000000000400078:  mov    $0x3,%rbx
0x000000000040007f:  mov    $0x1,%rax
0x0000000000400086:  mov    $0x1,%rdi
0x000000000040008d:  mov    $0x4000b2,%rsi
0x0000000000400094:  mov    $0x6,%rdx
0x000000000040009b:  syscall 

----------------
IN: 
0x000000000040009d:  dec    %rbx
0x00000000004000a0:  jne    0x40007f

----------------
IN: 
0x000000000040007f:  mov    $0x1,%rax
0x0000000000400086:  mov    $0x1,%rdi
0x000000000040008d:  mov    $0x4000b2,%rsi
0x0000000000400094:  mov    $0x6,%rdx
0x000000000040009b:  syscall 

----------------
IN: 
0x00000000004000a2:  mov    $0x3c,%rax
0x00000000004000a9:  mov    $0x0,%rdi
0x00000000004000b0:  syscall 

Я ожидаю, что этот метод будет относительно быстрым. Он работает путем чтения инструкций ввода и выдачи инструкций вывода, которые может запустить хост, так же, как cachegrind, который упоминался в: https://stackoverflow.com/a/2971979/895245

Крутая вещь в этом заключается в том, что вы также можете тривиально отслеживать исполняемые файлы других архитектур, см., Например, aarch64: Как собственный код Android, написанный для ARM, работает на x86?

Этот метод также отображает текущий символ незарезанных исполняемых файлов, например, след:

main.c

#include <stdio.h>

int say_hello() {
    puts("hello");
}

int main(void) {
    say_hello();
}

скомпилируйте и запустите:

gcc -ggdb3 -O0 -o main.out main.c
qemu-x86_64 -d in_asm ./main.out

содержит:

----------------
IN: main
0x0000000000400537:  push   %rbp
0x0000000000400538:  mov    %rsp,%rbp
0x000000000040053b:  mov    $0x0,%eax
0x0000000000400540:  callq  0x400526

----------------
IN: say_hello
0x0000000000400526:  push   %rbp
0x0000000000400527:  mov    %rsp,%rbp
0x000000000040052a:  mov    $0x4005d4,%edi
0x000000000040052f:  callq  0x400400

----------------
IN: 
0x0000000000400400:  jmpq   *0x200c12(%rip)        # 0x601018

Однако он не отображает символы в общих библиотеках, таких как put.

Но вы можете увидеть их, если скомпилируете с -static:

----------------
IN: main
0x00000000004009bf:  push   %rbp
0x00000000004009c0:  mov    %rsp,%rbp
0x00000000004009c3:  mov    $0x0,%eax
0x00000000004009c8:  callq  0x4009ae

----------------
IN: say_hello
0x00000000004009ae:  push   %rbp
0x00000000004009af:  mov    %rsp,%rbp
0x00000000004009b2:  mov    $0x4a1064,%edi
0x00000000004009b7:  callq  0x40faa0

----------------
IN: puts
0x000000000040faa0:  push   %r12
0x000000000040faa2:  push   %rbp
0x000000000040faa3:  mov    %rdi,%r12
0x000000000040faa6:  push   %rbx
0x000000000040faa7:  callq  0x423830

Похожие: https://unix.stackexchange.com/questions/147343/how-to-determine-what-instructions-a-process-is-executing

Протестировано в Ubuntu 16.04, QEMU 2.5.0.

...