Инструкции счетной машины с использованием gdb

Мне нужно оценить точное начальное местоположение некоторой точки доступа в программе в терминах количества машинных команд x86 (чтобы впоследствии ее можно было запустить в каком-то эмуляторе/симуляторе). Есть ли способ использовать gdb для подсчета количества исполняемых машинных команд до точки останова?

Конечно, есть и другие альтернативы, я мог бы использовать инструмент эмуляции/двоичного инструментария (например, Pin) и отслеживать прогон при подсчете инструкций, но для этого потребуется установить этот инструмент на всех платформах, на которых я работаю, - это не всегда возможно. Мне нужен какой-то инструмент, доступный практически для любой Linux-машины.

С gdb, я думаю, также можно запускать stepi X по большим шагам как некоторый грубозернистый поиск до тех пор, пока мы не нажмем точку останова, а затем повторим с уменьшением разрешения, но это будет мучительно медленным. Есть ли другой способ сделать это?

Ответы

Ответ 1

Попробуй это:

set pagination off
set $count=0
while ($pc != 0xyourstoppingaddress)
stepi
set $count++
end
print $count

Тогда иди принеси чашку кофе. Или длинный обед.

Ответ 2

На самом деле это лишь небольшое улучшение удобства использования Mark.

Мы можем определить функцию do_count:

define do_count
set $count=0
while ($pc != $arg0)
stepi
set $count=$count+1
end
print $count
end

а затем эту функцию можно повторно использовать для подсчета количества шагов снова и снова:

set pagination off
do_count 0xaddress1
do_count 0xaddress2

Можно даже помещать это определение в .gdbinit (в Linux, в Windows его следует называть gdb.ini) в домашней папке, поэтому оно становится доступным автоматически после начала gdb (используйте show user to см., была ли загружена функция).

Ответ 3

Если вы действительно хотите счетчик циклов (может быть, как приближение счетчика команд с известным IPC), и вы работаете на голой железной ARM, вы можете прочитать счетчик циклов, см., Например, Счетчик циклов на ARM Cortex M4 ( или м3)?


В вашем сценарии я бы попробовал Process Record и Replay, чтобы получить истекшее количество команд (доступно с GDB 7.0 и улучшено впоследствии):

  1. Начните измерение: record btrace (или record full если первое не доступно).
  2. continue выполнение (до точки останова или использовать next или другие команды для перехода).
  3. Получить измерение: info record
  4. Очистить записанные результаты: record stop (рекомендуется, поскольку размер буфера ограничен).

Пример:

(gdb) record btrace
(gdb) frame
#0  __sanitizer::InitTlsSize () at .../lib/sanitizer_common/sanitizer_linux_libcdep.cc:220
220       void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
(gdb) info record
Active record target: record-btrace
Recording format: Branch Trace Store.
Buffer size: 64kB.
Recorded 0 instructions in 0 functions (0 gaps) for thread 1 (Thread 0xf7c92300 (LWP 20579)).
(gdb) next
226       ...
(gdb) info record
Active record target: record-btrace
Recording format: Branch Trace Store.
Buffer size: 64kB.
Recorded 2859 instructions in 145 functions (0 gaps) for thread 1 (Thread 0xf7c92300 (LWP 20579)).

Ограничения:

  • Размер буфера записи имеет ограниченный размер (его можно увеличить с помощью set record btrace pt buffer-size <size> для формата BTS выше, см. Документацию для других типов).
  • При record full не все инструкции могут быть record full. Примечательно, что инструкции SSE и AVX не поддерживаются и заставят GDB приостановить выполнение.
  • При записи каждой инструкции есть некоторые накладные расходы (особенно в полном формате). Хотя он не должен быть таким плохим, как подход gdb step, описанный в других ответах (который должен каждый раз проходить через ptrace).