一.前言
嵌入式开发中GDB进行调试是必备手段,GDB拥有强大的脚本集成能力,尤其是支持Python脚本,可以使用Python 扩展GDB。注意:GDB构建时需要配置--with-python选项才会支持Python脚本,一般使用现成的二进制GDB工具, 这个选项是会配置的。
利用脚本我们能实现自动化测试,提高调试效率等。这里就来分享在GDB调试中Python脚本的使用,分享一些实例,更多信息可以直接参考GDB的文档。
二.GDB中的Python简介
以下介绍GDB中Python的一些基础内容。
2.1获取帮助
GDB环境中,输入help python显示如下
(gdb) help pythonpython, pyEvaluate a Python command.The command can be given as an argument, for instance:python print (23)If no argument is given, the following lines are read and usedas the Python commands. Type a line containing "end" to indicatethe end of the command.(gdb)
输入python help (gdb)可以查看Python API帮助
(gdb) python help (gdb)Help on package gdb:NAMEgdb - # Copyright (C) 2010-2020 Free Software Foundation, Inc.PACKAGE CONTENTSFrameDecoratorFrameIteratorcommand (package)framesfunction (package)printer (package)printingprompttypesunwinderxmethodSUBMODULESevents
2.2Data文件
GDB有时会读取辅助数据文件。这些文件存储在称为数据目录data directory的目录中。
这个目录可以通过GDB环境的show data-directory命令查看,通过set data-directory directory命令设置
例如
(gdb) show data-directoryGDB's data directory is ".../directory /opt/riscv-toolchain/10.2.0/share/gdb".(gdb) set data-directory /opt/riscv-toolchain/10.2.0/share/gdb(gdb)
也可以在执行GDB时指定--data-directory选项, 例如
riscv64-unknown-elf-gdb --data-directory=/opt/riscv-toolchain/10Quit/share/gdb
而该目录的默认配置是在构建配置时的--with-gdb-datadir选项指定的, 如果指定为‘--prefix’ 或‘--exec-prefix’对应的二进制文件目录, 则该目录会随着GDB的安装路径自动变化。
Python脚本的牧人搜索路径就是上述
Datadirectory下的python
比如我这里的
root@qinyunti:~/# ls /opt/riscv-toolchain/10.2.0/share/gdbpython syscalls system-gdbinitroot@qinyunti:~/# ls /opt/riscv-toolchain/10.2.0/share/gdb/python/gdb/FrameDecorator.py __init__.py command function printing.py types.py xmethod.pyFrameIterator.py __pycache__ frames.py printer prompt.py unwinder.pyroot@qinyunti:~/#
注意上述的两个子目录command function
data-directory/python/gdb/command 或 data-directory/python/gdb/function中的命令或者函数会自动在GDB启动时导入。
root@qinyunti:~/# ls /opt/riscv-toolchain/10.2.0/share/gdb/python/gdb/command/__init__.py __pycache__ explore.py frame_filters.py pretty_printers.py prompt.py type_printers.py unwinders.py xmethods.pyroot@qinyunti:~/# ls /opt/riscv-toolchain/10.2.0/share/gdb/python/gdb/function/__init__.py __pycache__ as_string.py caller_is.py strfns.pyroot@qinyunti:~/#
2.3执行Python脚本的方式
2.3.1直接命令行输入
直接GDB命令行中输入脚本
GDB中输入python回车进入python命令行交互环境
输入脚本,最后以end结束, 例如
定义函数add_function(a,b), 然后python add_function(1,2)调用执行
(gdb) python>def add_function(a,b):> c = a + b> print(f"{a}+{b}={c}")>end(gdb) python add_function(1,2)1+2=3(gdb)
2.3.2脚本文件
新建add.py输入以下内容
def add_function(a,b): c = a + b print(f"{a}+{b}={c}")
导入脚本source add.py
执行脚本python add_function(2,3)
data-directory/python/gdb/command 或 data-directory/python/gdb/function中的会自动导入无需source手动导入。
2.4 Python支持的模块
GDB自带多个模块来辅助编写Python代码。
• gdb.printing:构建和注册美观打印。
• gdb.types:用于处理类型的工具。
• gdb.prompt:用于快速值替代的实用工具。
• gdb.ptwrite:PTWRITE滤波器注册工具。
详见
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-modules.html#Python-modules
2.5 Python API
详见
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html#Python-API
2.6参考
更多内容可以参考GDB的在线文档
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html#Python-API
三. 实例在函数返回指定值后停止
在调试时,有一种比较常见的需求,希望能在某个函数返回某个值时暂停,以查看当时状态,手动的方式是在函数返回处打断点,然后不断c, 并打印返回值,这样比较繁琐,尤其是在函数频繁调用且很小概率返回需要的值时。 此时就可以使用Python脚本来实现,返回特定值后自动暂停。
创建test.py
import gdbclass FunctionFinishBreakpoint (gdb.FinishBreakpoint): def __init__ (self): gdb.FinishBreakpoint.__init__(self, gdb.newest_frame(), internal=True) self.silent = True def stop(self): print("after: {}".format(self.return_value)) return self.return_value == 5class FunctionBreakpoint(gdb.Breakpoint): def __init__ (self, spec): gdb.Breakpoint.__init__(self, spec) self.silent = True def stop (self): print("before") FunctionFinishBreakpoint() # set breakpoint on function return return False # do not stop at function entryFunctionBreakpoint("test")
source test.py时自动执行
FunctionBreakpoint("test")test为需要监控的函数名字
FunctionBreakpoint("test")的作用是每次在test执行时回调FunctionBreakpoint.stop且回调是silent的,此时调用FunctionFinishBreakpoint()
而FunctionFinishBreakpoint.stop在其stop函数返回False时不暂停,返回True时暂停。
所以return not self.return_value表示返回0时暂停。
Breakpoint参考https://sourceware.org/gdb/current/onlinedocs/gdb.html/Breakpoints-In-Python.html#Breakpoints-In-PythonFinishBreakpoint参考https://sourceware.org/gdb/current/onlinedocs/gdb.html/Finish-Breakpoints-in-Python.html#Finish-Breakpoints-in-Python
测试代码
static __attribute__((optimize("O0"))) int test(int i) { return i;} for(int i=0;i<10;i++){ int res = test(i); char buf[16]; itoa(buf, res); uart_put_string(buf); }
看到source test.py后打印如下
(gdb) source test.pyBreakpoint 1 at 0x1000ba: file main.c, line 89.(gdb) cContinuing.beforeafter: 0beforeafter: 1beforeafter: 2beforeafter: 3beforeafter: 4beforeafter: 5(gdb) p i$1 = 5(gdb)
可以看到返回5时就暂停了,此时查看i也是5
三. 总结
使用Python 扩展 GDB可以方便自动化测试,提高调试效率,以上仅做简单介绍, 分享了一个简单的实例。平时可以多积累一些实例脚本,以便不时之需。 更详细的Python脚本语法等可参考GDB的文档。