如何编写 gdb 脚本(使用 python)?示例添加断点,运行,我们碰到了什么断点?

发布于 2024-09-30 06:46:21 字数 1441 浏览 8 评论 0 原文

我正在尝试使用 gdb 创建一个小单元测试, 对于由 OpenOCD 控制的嵌入式 MCU(这使我可以通过 gdb 服务器控制我的目标) 。

所以我想通过 gdb 的一些脚本来自动执行此操作。

我想为 gdb 编写某种脚本,或多或少可以执行以下操作:

  1. 添加几个断点
  2. 启动程序
  3. 当我们停止时,它在哪里停止(获取帧信息)
  4. 退出。

有什么想法吗?

关于如何在 python gdb 脚本中执行此操作的示例会很好。

谢谢 Johan


注意

假设我们有这个基本结构, 或多或少进入 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好像支持。

I'm trying to create a little unit test with gdb,
for a embedded mcu that is controlled by OpenOCD (that gives me control over my target via a gdb server).

So I would like to automate this with some scripting of gdb.

I would like to write some kind of script for gdb that more or less does this:

  1. Add a couple of breakpoints
  2. Start the program
  3. When we stop, where did it stop (get the frame info)
  4. Quit.

Any ideas?

A example on how to do this in python gdb scripting would be nice.

Thanks
Johan


Note:

Let's say that we have this basic structure,
that more or less goes into test_failed() or test_success()
depending on what the function start_test() returns.

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);    
}

To do this manually in gdb is very strait forward,

(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) 

So the next step I tried was to add those gdb commands into a gdb startup script that more or less just looked like this.

break test_success
break test_failed
target remote localhost:3333
cont 
frame

and start it with

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

And this kind of works, but it is not very nice.
How do I do this with the "new and cool" python scripts,
that gdb seem to support.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

尝蛊 2024-10-07 06:46:21

仅供参考,最近的 gdb 版本可以用 Python 编写脚本。您可以从 gdb 命令行调用 python 代码。这打开了一个全新的世界,查看相关文档。从命令行运行:

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

如果您不喜欢基于文本的信息浏览器,这里有一个(众多?)替代图形浏览器:

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

这是一个示例 gdb-python 脚本。它将 gdb 附加到第一个发现正在运行的“your_program”。

#!/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))

FYI recent gdb versions are scriptable in Python. You can call python code from the gdb command line. This opens a whole new world, check the relevant documentation. From the command line run:

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

If you do not like the text-based info browser, here is one (among many?) alternative, graphical browser:

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

Here is a sample gdb-python script. It attaches gdb to the first "your_program" found running.

#!/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))
还如梦归 2024-10-07 06:46:21

我当前正在使用的简化示例:

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 文档 了解更多信息。

A reduced example that I'm currently using:

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")

You can execute this script from gdb's prompt like this:

(gdb) source script.py

Or from the command-line:

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

See the complete GDB Python API docs for further info.

攀登最高峰 2024-10-07 06:46:21

好吧,我在问问题的时候找到了答案……而且这是一件非常简单的事情。

如果您希望以特定顺序执行“--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这样的东西来代替可能会更好。

OK, I found the answer while asking the question... and it and it was a really simple thing.

You should not use both the "--command" and the "--eval" at the same time if you expect them to be executed in a specific order!

A more predicable way is to put everything in the commands.gdb file and ignore --eval.

So it becomes something like this:

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

Where commands.gdb looks like this:

break test_success
break test_failed
target remote localhost:3333
cont 
frame

But it would probably be so much nicer to do this with something like python instead.

冷弦 2024-10-07 06:46:21

只是想指出一些我每次回到这个主题时都感到困惑的事情(注意,我目前使用的是 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的某个“archer”分支的一个功能?!


因此,要在 gdb 中运行 Python 脚本,实际上有两种方法:

  • 将脚本文件命名为扩展名 .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 中的断点处打印源代码的当前行,而不打印其他内容?

Just wanted to note something that I find confusing whenever I come back to this topic (Note, I'm currently on Ubuntu 14.04, GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1):

First, there are references about it being "possible to invoke gdb as an interpreter":

... meaning, one would write a script text file with the shebang line #!/usr/bin/gbd -P or #!/usr/bin/gbd --python, then write Python code in it, then make it executable chmod +x pygdbscript, then run ./pygdbscript; ... but as in this post:

..., I get gdb: unrecognized option '--python' if I try anything like that. Apparently this option is/was a feature in some "archer" branch of gdb?!


So, in order to run a Python script in gdb, there are actually two ways:

  • Name your script file with extension .py; say test.py here:
def Something():
  print("hello from python")

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

Note, in this case, you just write plain Python code; and you do not need to import gdb in order to access the gdb object. This you can run with either of:

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

... which seem to be equivalent, as the result for any of these is the same printout, before gdb is instructed to exit by the script:

$ 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

NOTE that for this case, also names like test.gdb.py will be interpreted as pure Python scripts, since then end in .py.

  • Name your script anything else - as long as it does not end with .py extension; say test.pygdb here:
python
def Something():
  print("hello from python")

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

In this case, gdb interprets the script as being a gdb script, i.e. with gdb commands - and that means, that whatever Python code you may want to write in here, must be wrapped in "python" as a starting line and "end" at end of the Python code. Again, it would be called with any of these equivalent calls:

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

... and then the output is the same as in the previous case (since it is the same Python script running):

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

And in response to OP: if the C code in OP is in /tmp/myprog.c - with an added int start_test() { return rand() % 50; } on top, otherwise it won't compile - , and is compiled with with gcc -g myprog.c -o myprog.exe into /tmp/myprog.exe; then you can use a myprog.gdb.py script like this:

# 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)

... then run this script with:

$ 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) 

Note that at the end of this script, the (gdb) interactive prompt remains, and you can use it normally here; if you don't need the interactive prompt, you can do gdb.execute("quit"); as in the above scripts to force gdb to exit instead at end of script execution.

Also, for an example of subclassing breakpoint class in gdb Python, see How to print the current line of source at breakpoint in GDB and nothing else?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文