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

时间:2010-10-30 20:34:01

标签: unit-testing scripting gdb openocd gdb-python

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

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

我想为gdb写一些或多或少的脚本:

  1. 添加几个断点
  2. 启动程序
  3. 当我们停止时,它停在哪里(获取框架信息)
  4. 退出。
  5. 有什么想法吗?

    关于如何在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似乎支持。

4 个答案:

答案 0 :(得分:10)

FYI最近的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))

答案 1 :(得分:7)

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

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 docs

答案 2 :(得分:2)

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

如果您希望它们按特定顺序执行,则不应同时使用“--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代替它可能会更好。

答案 3 :(得分:1)

我想回顾一下每当我回到这个主题时都会感到困惑的事情(注意,我目前在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的分支?!

因此,为了在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:如果OP中的C代码在/tmp/myprog.c中 - 顶部添加了int start_test() { return rand() % 50; },否则它不会编译 - 并且使用{编译{1}} 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 Python中继承断点类的示例,请参阅How to print the current line of source at breakpoint in GDB and nothing else?