Как распечатать каждую выполненную строку в GDB автоматически, пока не будет достигнута определенная точка останова?
Я хотел бы иметь возможность устанавливать точку останова в GDB и запускать ее до этой точки - и в процессе распечатывать строки, через которые он "прошел".
Вот пример, основанный на этом простом файле с main
и функция, и две точки останова для каждого:
$ cat > test.c <<EOF
#include "stdio.h"
int count=0;
void doFunction(void) {
// two steps forward
count += 2;
// one step back
count--;
}
int main(void) {
// some pointless init commands;
count = 1;
count += 2;
count = 0;
//main loop
while(1) {
doFunction();
printf("%d\n", count);
}
}
EOF
$ gcc -g -Wall test.c -o test.exe
$ chmod +x test.exe
$ gdb -se test.exe
...
Reading symbols from /path/to/test.exe...done.
(gdb) b main
Breakpoint 1 at 0x80483ec: file test.c, line 14.
(gdb) b doFunction
Breakpoint 2 at 0x80483c7: file test.c, line 7.
Для начала сеанса мне нужно запустить (r
) программа, которая затем остановится на первой точке останова (main
):
(gdb) r
Starting program: /path/to/test.exe
Breakpoint 1, main () at test.c:14
14 count = 1;
(gdb)
На данный момент - я могу, например, нажать продолжить (c
); и процесс будет проходить, ничего не выводя, и прерываться на запрошенную строку:
(gdb) c
Continuing.
Breakpoint 2, doFunction () at test.c:7
7 count += 2;
(gdb)
С другой стороны, вместо продолжения - я могу идти строка за строкой, либо используя шаг (s
) или следующий (n
); например:
14 count = 1;
(gdb) n
15 count += 2;
(gdb) s
16 count = 0;
(gdb) s
19 doFunction();
(gdb) s
Breakpoint 2, doFunction () at test.c:7
7 count += 2;
(gdb) s
9 count--;
(gdb) s
10 }
(gdb) s
main () at test.c:20
20 printf("%d\n", count);
(gdb) s
...
(gdb) s
_IO_vfprintf_internal (s=Cannot access memory at address 0xe5853361
) at vfprintf.c:210
210 vfprintf.c: No such file or directory.
in vfprintf.c
(gdb) s
245 in vfprintf.c
(gdb) s
210 in vfprintf.c
(gdb) n
245 in vfprintf.c
...
(gdb) n
2006 in vfprintf.c
(gdb) n
__printf (format=0x80484f0 "%d\n") at printf.c:39
39 printf.c: No such file or directory.
in printf.c
(gdb) n
main () at test.c:21
21 }
(gdb) n
19 doFunction();
(gdb) n
Breakpoint 2, doFunction () at test.c:7
7 count += 2;
(gdb)
В любом случае, я знаю, что могу удерживать нажатой клавишу Enter, и последняя введенная команда (шаг или следующая) будет повторяться (во втором случае оставалась немного более продолжительная сессия, чтобы показать, что "следующий" остается на том же уровне, "шаговые" шаги внутри вызываемых функций). Однако, как видно, в зависимости от того, выполняется ли шаг или следующий шаг, может потребоваться некоторое время, пока не будет достигнут результат - и поэтому я не хочу сидеть 10 минут, держа руку на кнопке "Ввод":)
Итак, мой вопрос - могу ли я как-нибудь проинструктировать gdb
запустить до "точки останова 2" без дальнейшего вмешательства пользователя - при печати строк, через которые он проходит, как если бы был нажат шаг (или следующий)?
6 ответов
Ну, это было нелегко - но я думаю, что я в некоторой степени это понял:) Я прошел через множество неудачных попыток (опубликовано здесь); соответствующий код ниже.
По сути, проблема в "следующем / шаге до точки останова" состоит в том, как определить, находитесь ли вы на "точке останова" или нет, если отладчик остановлен (на шаге). Обратите внимание, что я использую GDB 7.2-1ubuntu11 (актуально для Ubuntu 11.04). Итак, все прошло так:
- Я впервые обнаружил удобные переменные и подумал - учитывая, что существуют счетчики программ и так далее, должна существовать некоторая вспомогательная переменная GDB, которая дает статус "точки останова" и может использоваться непосредственно в сценарии GDB. Однако, просматривая в течение некоторого времени справочный индекс GDB, я просто не могу найти такие переменные (мои попытки в nub.gdb)
- В отсутствие такой внутренней переменной "состояние точки останова" - единственное, что остается сделать, - это захватить вывод команды GDB ('stdout') из командной строки (в ответ на команды) как строку и проанализировать ее (ища) Breakpoint")
- Затем я узнал о Python API для GDB и
gdb.execute("CMDSTR", toString=True)
команда, которая, по-видимому, именно то, что нужно для захвата вывода: " По умолчанию любой вывод, созданный командой, отправляется на стандартный вывод gdb. Если параметр to_string равен True, то вывод будет собран gdb.execute и возвращен как строка [ 1 ] "!- Итак, сначала я попытался создать скрипт ( pygdb-nub.py, gdbwrap), который будет использовать
gdb.execute
в рекомендуемом порядке; не удалось здесь - из-за этого: - Затем я подумал, что буду использовать скрипт на Python для
subprocess.Popen
программа GDB при замене ее стандартных и стандартных выводов; а затем продолжить управление GDB оттуда ( pygdb-sub.py) - это тоже не удалось... (очевидно, потому что я не перенаправил stdin/out правильно) - Затем я подумал, что буду использовать скрипты Python для вызова из GDB (через
source
) который внутренне разветвляется в pty всякий раз, когдаgdb.execute
должен быть вызван так, чтобы захватить его вывод ( pygdb-fork.gdb, pygdb-fork.py)... Это почти сработало - так как есть возвращаемые строки; однако GDB замечает, что что-то не так: " [tcsetpgrp потерпел неудачу в Terminal_inferior: операция не разрешена] ", и последующие строки возврата, похоже, не меняются.
- Итак, сначала я попытался создать скрипт ( pygdb-nub.py, gdbwrap), который будет использовать
И наконец, подход, который работал: временно перенаправить вывод GDB из gdb.execute
в лог-файл в оперативной памяти (Linux: /dev/shm
); а затем читая его обратно, анализируя и печатая его из python - python также обрабатывает простой цикл while, который выполняет шаги до достижения точки останова.
Ирония в том, что большинство этих ошибок, которые вызвали это решение путем перенаправления файла журнала, на самом деле недавно исправлены в SVN; Это означает, что они будут распространяться в дистрибутивах в ближайшем будущем, и каждый сможет использовать gdb.execute("CMDSTR", toString=True)
напрямую: / Тем не менее, так как я не могу рисковать сборкой GDB из источника прямо сейчас (и, возможно, столкнуться с возможными новыми несовместимостями), это тоже достаточно хорошо для меня:)
Вот соответствующие файлы (частично также в pygdb-fork.gdb, pygdb-fork.py):
pygdb-logg.gdb
является:
# gdb script: pygdb-logg.gdb
# easier interface for pygdb-logg.py stuff
# from within gdb: (gdb) source -v pygdb-logg.gdb
# from cdmline: gdb -x pygdb-logg.gdb -se test.exe
# first, "include" the python file:
source -v pygdb-logg.py
# define shorthand for nextUntilBreakpoint():
define nub
python nextUntilBreakpoint()
end
# set up breakpoints for test.exe:
b main
b doFunction
# go to main breakpoint
run
pygdb-logg.py
является:
# gdb will 'recognize' this as python
# upon 'source pygdb-logg.py'
# however, from gdb functions still have
# to be called like:
# (gdb) python print logExecCapture("bt")
import sys
import gdb
import os
def logExecCapture(instr):
# /dev/shm - save file in RAM
ltxname="/dev/shm/c.log"
gdb.execute("set logging file "+ltxname) # lpfname
gdb.execute("set logging redirect on")
gdb.execute("set logging overwrite on")
gdb.execute("set logging on")
gdb.execute(instr)
gdb.execute("set logging off")
replyContents = open(ltxname, 'r').read() # read entire file
return replyContents
# next until breakpoint
def nextUntilBreakpoint():
isInBreakpoint = -1;
# as long as we don't find "Breakpoint" in report:
while isInBreakpoint == -1:
REP=logExecCapture("n")
isInBreakpoint = REP.find("Breakpoint")
print "LOOP:: ", isInBreakpoint, "\n", REP
В принципе, pygdb-logg.gdb
загружает pygdb-logg.py
скрипт Python, устанавливает псевдоним nub
за nextUntilBreakpoint
и инициализирует сеанс - все остальное обрабатывается скриптом Python. А вот пример сеанса - по отношению к источнику теста в OP:
$ gdb -x pygdb-logg.gdb -se test.exe
...
Reading symbols from /path/to/test.exe...done.
Breakpoint 1 at 0x80483ec: file test.c, line 14.
Breakpoint 2 at 0x80483c7: file test.c, line 7.
Breakpoint 1, main () at test.c:14
14 count = 1;
(gdb) nub
LOOP:: -1
15 count += 2;
LOOP:: -1
16 count = 0;
LOOP:: -1
19 doFunction();
LOOP:: 1
Breakpoint 2, doFunction () at test.c:7
7 count += 2;
(gdb) nub
LOOP:: -1
9 count--;
LOOP:: -1
10 }
LOOP:: -1
main () at test.c:20
20 printf("%d\n", count);
1
LOOP:: -1
21 }
LOOP:: -1
19 doFunction();
LOOP:: 1
Breakpoint 2, doFunction () at test.c:7
7 count += 2;
(gdb)
... как я и хотел:P Просто не знаю, насколько он надежен (и можно ли будет его использовать в avr-gdb
, это то, что мне нужно для этого:) РЕДАКТИРОВАТЬ: версия avr-gdb в Ubuntu 11.04 в настоящее время 6.4, которая не распознает команду python:()
Ну, надеюсь, это поможет кому-то,
Ура!
Вот несколько ссылок:
- GDB: ошибка, обнаруженная на stdin
- У GDB есть проблемы с получением команд, переданных в STDIN
- Re: [GDB] Как я могу использовать GDB другой ввод?
- GDB не принимает ввод на стандартный ввод
- Использование gdb в IDE - comp.os.linux.development.apps | Группы Google
- Рматхью: Терминальная болезнь
- [Учебник] Вызов внешней программы на C (Linux) - GIDForums
- Оболочка - как использовать несколько аргументов с помощью шебанга (то есть #!)? - Переполнение стека
- Перенаправление / сохранение вывода оболочки в переменную GDB? - Переполнение стека
- Кори Голдберг: Python - перенаправление или выключение STDOUT и STDERR
- Скалы Безумия › 9. Сценарии GDB
- GDB Python скриптинг: где есть
parse_and_eval
ушел? - Переполнение стека - shell - вызывает gdb для автоматической передачи аргументов отлаживаемой программе - переполнение стека
- Хранение файлов / каталогов в памяти с помощью tmpfs | HowtoForge - Linux Howtos и руководства
- простой способ прикоснуться к файлу, если он не существует | Python | питон
- os.fork() отличается в cgi-скрипте? - питон
- Написание тестов, которые используют GDB - как захватить вывод? - Переполнение стека
- Отладка с помощью GDB: как создавать команды GDB в Python - Wiki
- GDB справочная карточка
Как насчет того, чтобы сделать это в gdb, используя командный файл. Измените аргумент файла и количество циклов, как требуется.
gdb -x run.gdb
run.gdb:
set pagination off
set logging file gdb.log
set logging on
set $i = 0
file main
break main
break WriteData
# sadly, commands not getting executed on reaching breakpoint 2
commands 2
set $i=1000
print "commands 2 : %d",$i
end
run
while ( $i < 1000 )
step
# next
# continue
set $i = $i + 1
end
Основываясь на ссылке в ответе @sdaau ( http://www.mail-archive.com/gdb@gnu.org/msg00031.html), я создал свой собственный скрипт, чтобы просто продолжать посылать 's' и читать вывод gdb Постоянно, при выводе вывода в текстовый файл и на терминал, конечно, мой сценарий может быть изменен в соответствии с чьими-либо потребностями, однако я надеюсь, что сделанные мной изменения должны соответствовать большинству потребностей людей.
http://www.codeground.net/coding/gdb-step-into-all-lines-to-get-full-application-flow/
wget http://www.codeground.net/downloads/gdbwalkthrough.c
gcc gdbwalkthrough.c -o gdbwalkthrough
./gdbwalkthrough <application full path> [application arguments]
В качестве нового ответа, так как предыдущий уже сложен:) В основном, если дело в том, чтобы наблюдать за выполнением строк исходного кода (и / или сборки), когда программа работает, - мотивация часто для меня, когда я смотрю в "автоматический". распечатка"- тогда, в принципе, очень быстрый способ - использовать режим GDB TUI; Я цитирую:
c - поведение gdb: оптимизированное значение - переполнение стека #1354762
Используйте режим GDB TUI. Моя копия GDB включает его, когда я набираю минус и ввод. Затем введите Cx 2 (удерживайте нажатой клавишу "Control" и нажмите X, отпустите обе кнопки и затем нажмите 2). Это поместит его в разделенное отображение источника и разборки. Затем используйте stepi и nexti для перемещения одной машинной инструкции за раз. Используйте Cx o для переключения между окнами TUI.
Хитрость в том, что даже если вы нажмете continue
- этот источник времени будет показан и указан в TUI; и следит за запуском программы:
... и это позволяет мне избежать многих ситуаций, когда мне приходится писать сценарии точек останова в "контексте автоматического перехода" (хотя такие ситуации все еще существуют). Документы по TUI: TUI - отладка с помощью GDB
Ура!
На самом деле, у меня есть с расширением Python-GDB, которое делает то же самое, что вы описали, но с некоторыми дополнительными функциями.
Вы можете просто клонировать репо:
git clone https://github.com/Viaceslavus/gdb-debug-until.git
и передайте скрипт python в GDB с помощью следующей команды внутри GDB:
source gdb-debug-until/debug_until.py
(При необходимости измените путь к скрипту Python)
И теперь вы можете использовать следующую команду для запуска каждой строки вашего кода до точки останова:
debug-until somefile.c:100 --args="" --end="somefile.c:200"
«somefile.c:100» — это начальная точка останова, а «somefile.c:200» — конечная точка останова.
"--args" задает набор аргументов для вашей программы (вы можете опустить его, если аргументов нет).
С этим расширением вы также можете несколько раз просмотреть код (с опцией '-r') и даже указать некоторые события, которые должны обрабатываться во время отладки. Для получения дополнительной информации см.:
репозиторий Githubhttps://github.com/Viaceslavus/gdb-debug-until.
В настоящее время принятый ответ включает в себя много файлов io и останавливается только на точках останова, но точки наблюдения, сигналы и, возможно, даже конец программы игнорируются.
Используя python api, с этим можно легко справиться:
- определить пользовательскую команду (с дополнительным аргументом, чтобы сказать, как быстро перейти на автоматический шаг)
- необязательно: определите параметр по умолчанию (заменен здесь для простоты фиксированным значением)
- выполнить цикл while в Python, обработать «ожидаемое» прерывание клавиатуры CTRL-C
- зарегистрировать
stop
обработчик событий, который проверяет причину остановки и сохраняет в ней тип шага - отрегулируйте цикл while, чтобы он остановился для "непростой" остановки (точка останова / точка наблюдения / сигнал / ...)
Следующий код можно поместить в файл gdb-auto-step.py, который можно сделать активным с помощью
source gdb-auto-step.py
всякий раз, когда вы этого хотите (или включите в файл .gdbinit, чтобы он всегда был доступен):
import gdb
import time
class CmdAutoStep (gdb.Command):
"""Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (1-19, default 5)."""
def __init__(self):
print('Registering command auto-step')
super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
gdb.events.stop.connect(stop_handler_auto_step)
def invoke(self, argument, from_tty):
number = 5 # optional: use a parameter for the default
if argument:
if not argument.isdigit():
raise gdb.GdbError("argument must be a digit, not " + argument)
number = int(argument)
if number == 0 or number > 19:
raise gdb.GdbError("argument must be a digit between 1 and 19")
sleep_time = 3.0 / (number * 1.4)
try:
frame = gdb.newest_frame()
except gdb.error:
raise gdb.GdbError("No stack.")
global last_stop_was_simple
last_stop_was_simple = True
pagination = gdb.execute("show pagination", False, True).find("on")
if pagination:
gdb.execute("set pagination off", False, False)
try:
while (last_stop_was_simple):
gdb.execute ("step")
time.sleep(sleep_time)
except KeyboardInterrupt as ki:
if pagination:
gdb.execute("set pagination on", False, False)
except gdb.GdbError as user_error:
if pagination:
gdb.execute("set pagination on", False, False)
# pass user errors unchanged
raise user_error
except:
if pagination:
gdb.execute("set pagination on", False, False)
traceback.print_exc()
def stop_handler_auto_step(event):
# check the type of stop, the following is the common one after step/next,
# a more complex one would be a subclass (for example breakpoint or signal)
global last_stop_was_simple
last_stop_was_simple = type(event) is gdb.StopEvent
CmdAutoStep()
Примечание. Если у кого-то есть лучший способ проверить значение разбивки на страницы, оставьте комментарий.