Как написать скрипт gdb (с python)? Пример добавить точки останова, запустить, какую точку останова мы наткнулись?

Я пытаюсь создать небольшой модульный тест с помощью gdb для встроенного микроконтроллера, который управляется OpenOCD. (это дает мне контроль над моей целью через сервер gdb).

Поэтому я хотел бы автоматизировать это с помощью некоторых сценариев gdb.

Я хотел бы написать какой-то скрипт для gdb, который более или менее делает это:

  1. Добавьте пару точек останова
  2. Запустить программу
  3. Когда мы остановимся, где это остановилось (получите информацию о кадре)
  4. Покидать.

Любые идеи?

Пример того, как это сделать в скриптах python gdb, был бы хорош.

Спасибо, Йохан.


Примечание.

Допустим, у нас есть эта базовая структура, которая более или менее входит в test_failed() или test_success() в зависимости от того, что возвращает функция start_test().

void test_failed() {    
    while(1);    
}

void test_success() {    
    while(1);    
}

int main(void) {    
    int status = start_test();    

    if( status > 0 ) {    
        test_failed();    
    }    
    test_success();

    while(1);    
}

Сделать это вручную в gdb очень просто,

(gdb) break test_success
Breakpoint 1 at 0x20: file src/main.c, line 9.
(gdb) break test_failed
Breakpoint 2 at 0x18: file src/main.c, line 5.
(gdb) cont
Continuing.

Breakpoint 1, test_success () at src/main.c:9
9       while(1);
(gdb) frame
#0  test_success () at src/main.c:9
9       while(1);
(gdb) 

Итак, следующим шагом, который я попытался, было добавление этих команд gdb в сценарий запуска gdb, который выглядел примерно так.

break test_success
break test_failed
target remote localhost:3333
cont 
frame

и начните с

arm-none-eabi-gdb --batch --command=commands.gdb main.elf

И этот вид работает, но это не очень приятно. Как мне это сделать с помощью «новых и крутых» сценариев Python, которые, похоже, поддерживает gdb.


person Johan    schedule 30.10.2010    source источник
comment
Также проверьте руководство на вики: sourceware.org/gdb/wiki/PythonGdbTutorial   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 02.07.2015


Ответы (4)


К вашему сведению, последние версии gdb можно использовать в сценариях на Python. Вы можете вызвать код Python из командной строки gdb. Это открывает совершенно новый мир, проверьте соответствующую документацию. Из командной строки запустите:

 dnf/yum/apt-get install gdb-doc
 info gdb extending python

Если вам не нравится текстовый информационный браузер, вот один (среди многих?) Альтернативный графический браузер:

yelp 'info:gdb' # , go to "Extending"

Вот пример скрипта gdb-python. Он прикрепляет gdb к первой найденной запущенной "вашей_программе".

#!/usr/bin/python

import subprocess
import string

def backquotes(cmdwords):
        output = subprocess.Popen(cmdwords, stdout=subprocess.PIPE).communicate()[0]
        return output.strip()

pid = backquotes(['pgrep', 'your_program'])

gdb.execute("attach " + str(pid))
person MarcH    schedule 10.11.2010
comment
У вас случайно нет хороших ссылок? или как, или что-то, что может подтолкнуть меня в правильном направлении (поскольку старый стиль сценариев gdb не очень оптимален...) - person Johan; 10.11.2010

Уменьшенный пример, который я сейчас использую:

class DebugPrintingBreakpoint(gdb.Breakpoint):
    debugging_IDs = frozenset({37, 153, 420})
    def stop(self):
        top = gdb.newest_frame()
        someVector = top.read_var('aVectorVar')
        # Access the begin() & end() pointer of std::vector in GNU Standard C++ lib
        first = someVector['_M_impl']['_M_start']
        last = someVector['_M_impl']['_M_finish']
        values = []
        while first != last:
            values.append(int(first.dereference()['intID']))
            first = first + 1
        if not set(values) & debugging_IDs:
            return False # skip: none of the items we're looking for can be found by ID in the vector on the stack
        print("Found other accompanying IDs: {}".format(values))
        return True # drop to gdb's prompt
# Ensure shared libraries are loaded already
gdb.execute("start")
# Set our breakpoint, which happens to reside in some shared lib, hence the "start" previously
DebugPrintingBreakpoint("source.cpp:42")
gdb.execute("continue")

Вы можете выполнить этот скрипт из командной строки gdb следующим образом:

(gdb) source script.py

Или из командной строки:

$ gdb --command script.py ./executable.elf

Дополнительную информацию см. в полной документации GDB Python API.

person Giel    schedule 15.08.2014
comment
У меня нет gdb.continue на 7.7.1, у вас какая версия? - person Ciro Santilli 新疆再教育营六四事件ۍ 03.07.2015
comment
@CiroSantilli六四事件法轮功纳米比亚威视 Думаю, это опечатка. Вероятно, это должно быть: gdb.execute(continue)? Можете ли вы подтвердить, что это работает (у меня нет прямого доступа к тестовой настройке, на которой я использовал этот скрипт ^^)? - person Giel; 05.07.2015
comment
Я могу указать gdb на мой файл python с помощью --command ‹path.py›, но как мне указать аргументы для моей программы python (т. е. скрипт использует sys.argv)? - person nmz787; 27.05.2016
comment
@nmz787 Я не думаю, что поддержка GDB Python вообще передает аргументы командной строки. Вероятно, вместо этого вы можете использовать переменные среды для передачи аргументов. Затем сделайте env OPT_X=y gdb --command script.py ./executable.elf - person Giel; 27.05.2016
comment
Хм, ваш комментарий не имеет для меня смысла... и я думаю, что это потому, что я пытался отладить свой скрипт Python с помощью GDB (пошагово выполнить код Python, а не сборку интерпретатора), а не управлять GDB через Python. Когда я использую --command ‹путь к моему файлу py›, я вижу инструкции по использованию, которые мой код Python создает без аргументов строки cmd... поэтому я подумал, что я близок к тому, чтобы пройтись по строкам кода Python. Возможно, вы знаете учебник по отладке кода Python с помощью GDB (я хочу использовать обратную отладку, которую отладчики Python IDE не поддерживают). Я не нашел хорошего учебника... - person nmz787; 02.06.2016
comment
Нет, для самого Python я обычно использую только посмертную отладку (т. е. анализ трассировок) или отладочные отпечатки. Что, я согласен, далеко не идеально - person Giel; 08.06.2016

Хорошо, я нашел ответ, задавая вопрос... и это было очень просто.

Вы не должны использовать «--command» и «--eval» одновременно, если вы ожидаете, что они будут выполняться в определенном порядке!

Более предсказуемый способ — поместить все в файл commands.gdb и игнорировать --eval.

Таким образом, это становится примерно таким:

arm-none-eabi-gdb --batch --command=commands.gdb main.elf

Где commands.gdb выглядит так:

break test_success
break test_failed
target remote localhost:3333
cont 
frame

Но, вероятно, было бы намного лучше сделать это с помощью чего-то вроде python.

person Johan    schedule 31.10.2010

Просто хотел отметить кое-что, что меня сбивает с толку всякий раз, когда я возвращаюсь к этой теме (обратите внимание, я сейчас на Ubuntu 14.04, GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1):

Во-первых, есть упоминания о том, что "можно вызвать gdb в качестве интерпретатора":

... это означает, что можно было бы написать текстовый файл сценария со строкой shebang #!/usr/bin/gbd -P или #!/usr/bin/gbd --python, затем написать в нем код Python, затем сделать его исполняемым chmod +x pygdbscript, затем запустить ./pygdbscript; ... но как в этом посте:

..., я получаю gdb: unrecognized option '--python', если попробую что-нибудь подобное. По-видимому, эта опция есть/была функцией в какой-то ветке "лучников" gdb?!


Итак, чтобы запустить Python-скрипт в gdb, на самом деле есть два пути:

  • Назовите файл сценария с расширением .py; скажи test.py здесь:
def Something():
  print("hello from python")

Something()
gdb.execute("quit");

Обратите внимание, что в этом случае вы просто пишете простой код Python; и вам не нужно import gdb для доступа к объекту gdb. Это вы можете запустить с помощью любого из:

gdb -x test.py
gdb -x=test.py
gdb --command test.py
gdb --command=test.py
gdb -command test.py
gdb -command=test.py

... которые кажутся эквивалентными, так как результатом для любого из них является одна и та же распечатка, прежде чем gdb получает указание выйти из сценария:

$ gdb -x=test.py
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...
For help, type "help".
Type "apropos word" to search for commands related to "word".
hello from python

ПРИМЕЧАНИЕ, что в этом случае такие имена, как test.gdb.py, будут интерпретироваться как чистые скрипты Python, поскольку в этом случае они заканчиваются на .py.

  • Назовите свой скрипт как угодно, если он не не заканчивается расширением .py; скажите test.pygdb здесь:
python
def Something():
  print("hello from python")

Something()
gdb.execute("quit");
end

В этом случае gdb интерпретирует сценарий как сценарий gdb, т. е. с gdb командами, и это означает, что любой код Python, который вы хотите написать здесь, должен быть обернут в "python" как начальная строка и «end» в конце кода Python. Опять же, он будет вызываться любым из этих эквивалентных вызовов:

gdb -x test.pygdb
gdb -x=test.pygdb
gdb --command test.pygdb
gdb --command=test.pygdb
gdb -command test.pygdb
gdb -command=test.pygdb

... и тогда вывод будет таким же, как и в предыдущем случае (поскольку это тот же самый скрипт Python):

$ gdb -x test.pygdb 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...
hello from python

И в ответ на OP: если код C в OP находится в /tmp/myprog.c - с добавленным int start_test() { return rand() % 50; } сверху, иначе он не будет компилироваться - и компилируется с gcc -g myprog.c -o myprog.exe в /tmp/myprog.exe; то вы можете использовать сценарий myprog.gdb.py следующим образом:

# need to specify the executable file which we debug (in this case, not from command line)
# here `gdb` command `file` is used - it does not have a Python equivalent (https://sourceware.org/gdb/onlinedocs/gdb/Objfiles-In-Python.html#index-Objfile_002eframe_005ffilters)
# so we must use gdb.execute:

myexefile="/tmp/myprog.exe"
print("""
### myprog.gdb.py is running: """ + myexefile + """ - and adding breakpoints:
""")

gdb.execute("file " + myexefile)
gdb.execute("set pagination off")

ax = gdb.Breakpoint("test_success")
bx = gdb.Breakpoint("test_failed")

gdb.execute("run")

# here the program will break, so we can do:

print("""
### myprog.gdb.py after the break - current stack frame:
""")

current_frame_at_break = gdb.selected_frame()
print(current_frame_at_break) # instead of gdb.execute("frame")

print("""
### myprog.gdb.py - backtrace:
""")

gdb.execute("backtrace 2")

print("""
### myprog.gdb.py - go to frame that called current frame:
""")

parent_frame = current_frame_at_break.older()
print(parent_frame)
status_var = parent_frame.read_var("status")
print("status_var is: ", status_var)

... затем запустите этот скрипт с помощью:

$ gdb -x myprog.gdb.py
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
....
For help, type "help".
Type "apropos word" to search for commands related to "word".

### myprog.gdb.py is running: /tmp/myprog.exe - and adding breakpoints:

Breakpoint 1 at 0x400565: file myprog.c, line 8.
Breakpoint 2 at 0x40055f: file myprog.c, line 4.

Breakpoint 2, test_failed () at myprog.c:4
4       while(1);    

### myprog.gdb.py after the break - current stack frame:

{stack=0x7fffffffdc70,code=0x40055b,!special}

### myprog.gdb.py - backtrace:

#0  test_failed () at myprog.c:4
#1  0x000000000040058c in main () at myprog.c:15

### myprog.gdb.py - go to frame that called current frame:

{stack=0x7fffffffdc90,code=0x400567,!special}
status_var is: 33
(gdb) 

Обратите внимание, что в конце этого сценария интерактивная подсказка (gdb) остается, и вы можете использовать ее здесь как обычно; если вам не нужна интерактивная подсказка, вы можете сделать gdb.execute("quit");, как в приведенных выше сценариях, чтобы заставить gdb выйти вместо этого в конце выполнения сценария.

Кроме того, пример подкласса класса точки останова в gdb Python см. в разделе Как напечатать текущую строку исходного кода в точке останова в GDB и ничего больше?

person sdaau    schedule 17.10.2017