1、入门案例
首先有1个a.cpp
,代码如下:
#include <map>
#include <set>
#include <iostream>
#include <string>using namespace std;struct MyStruct {std::string mName;std::map<int, std::string> mField1;std::set<std::string> mField2;int mI;int mJ;
};int main() {std::map<int,string> lm;std::set<std::string> ls;MyStruct s = {std::string("student"), lm, ls, 3, 4};return 0;
}
上面的C代码中有一个结构体MyStruct,如果想要打印结构体的内容。在GDB 9.X版本中打印出来是这样
$1 = {mName = "student", mField1 = std::map with 0 elements, mField2 = std::set with 0 elements, mI = 3, mJ = 4}
如果使用的是GDB 7.X,打印出来就是这样
$1 = {mName = {static npos = 18446744073709551615, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x7fffffffe380 "student"},_M_string_length = 7, {_M_local_buf = "student\000\000\000\000\000\000\000\000", _M_allocated_capacity = 32772479054607475}}, mField1 = {_M_t = {_M_impl = {<std::allocator<std::_Rb_tree_node<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<int const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<int> >> = {_M_key_compare = {<std::binary_function<int, int, bool>> = {<No data fields>}, <No data fields>}}, <std::_Rb_tree_header> = {_M_header = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffe398,_M_right = 0x7fffffffe398}, _M_node_count = 0}, <No data fields>}}}, mField2 = {_M_t = {_M_impl = {<std::allocator<std::_Rb_tree_node<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {_M_key_compare = {<std::binary_function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool>> = {<No data fields>}, <No data fields>}}, <std::_Rb_tree_header> = {_M_header = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffe3c8, _M_right = 0x7fffffffe3c8}, _M_node_count = 0}, <No data fields>}}},mI = 3, mJ = 4}
可以看到在GDB 9.X中打印出来的要美观很多,主要是由于GDB 9.x自带了一系列标准库的Python Pretty Printer(美观打印器)。如果想要查看原始的数据,可以使用 p /r s
命令。
当然我们也可以实现字节的美观打印器。主要需要完成下面几个功能:
(1)自定义打印类,提供to_string()方法,该方法返回希望打印出来的字符串。
(2)创建判断函数,判断一个值是否需要使用自定义的打印类来打印。
(3)将判断函数注册到GDB美观打印函数中。
下面直接看完整的代码,t.py
class MyPrinter:def __init__(self, val):self.val = valdef to_string(self):return "name: {} integer: {}".format(self.val['mName'], self.val['mI'])def lookup_pretty_printer(val):if val.type.code == gdb.TYPE_CODE_PTR:return Noneif 'MyStruct' == val.type.tag:return MyPrinter(val)return None
gdb.printing.register_pretty_printer(gdb.current_objfile(),lookup_pretty_printer,replace=True)
简单解释下上面的python代码。
1、定义了一个名为 MyPrinter
的类,它有一个构造函数,接收一个 val
参数。参数就是p s
中的s
, to_string
方法定义了如何将 MyStruct
对象转换为字符串,例子中包含 mName
和 mI
字段的值。
2、lookup_pretty_printer
这个函数用来决定是否使用 MyPrinter
来打印某个值。如果传入的 val
是指针类型,则直接返回 None
,表明不需要特别处理。如果 val
的类型标签是 MyStruct
,则返回一个新的 MyPrinter
实例,否则也返回 None
3、最后一行,gdb.printing
是 GDB 的一个 Python 接口模块,它提供了一系列与 pretty printing 相关的功能。Pretty printing 是一种用于美化或格式化输出的技术,尤其是在处理复杂数据结构时,能够使输出更加清晰易读。gdb.printing
模块提供了以下功能:
-
注册 pretty printers
-
管理 pretty printer 集合
-
提供查找 pretty printer 的方法
gdb.printing.register_pretty_printer
是gdb.printing
模块中的一个函数,用于注册一个 pretty printer。当 GDB 在打印某个变量或表达式时,它会调用已注册的 pretty printer 来美化输出。这个函数允许用户为特定类型的数据结构定制输出格式,使得输出更加直观和有用。三个参数分别表示:
- objfile:
gdb.Objfile
或None
,表示要为其注册 pretty printer 的目标文件。如果是None
,则表示注册的 pretty printer 对所有目标文件有效。 - pretty_printer: 函数,接受一个
gdb.Value
对象并返回一个 pretty printer 对象或None
。这个函数负责决定是否使用自定义的 pretty printer。 - replace: 布尔值,默认为
False
,表示是否替换现有的 pretty printer。如果为True
,则新注册的 pretty printer 会覆盖先前为相同类型注册的所有 pretty printer。
- objfile:
使用步骤:
-
使用
g++ -g a.cpp -o a
将代码编译成可调试的可执行程序。 -
将上面的
t.py
和上面编译出来的a
放到同一目录。 -
使用
gdb a
使用调试状态执行a
。 -
在gdb命令行加载
t.py
,使用source t.py
。 -
添加断点
b 22
,在a.cpp
第22行添加断点。 -
使用
r
命令运行程序,会在代码22行的位置停下来;使用p s
打印变量s的值,就能看到打印格式就是我们t.py
中MyPrinter.to_string
指定的输出格式。
2、在GDB窗口定义python
脚本
除了可以上面的在外部定义python脚本,也可以直接在gdb调试窗口中定义函数。
步骤如下:
-
在gdb 窗口输入
python
进入python脚本编辑模式 -
输入python脚本内容,完成后输入
end
退回到gdb
模式下 -
使用
python
前缀调用python脚本中的内容(gdb) python >def my_function(arg): > print(f"Argument received: {arg}") >end (gdb) python my_function(100) Argument received: 100
3、使用python控制gdb流程。
比如我们需要监控某个函数中某个变量的值,比如循环中执行多次,如果我们每次在gdb中手动输入命令,那就太繁琐了,我们可以使用python来做到这些。
比如有个c程序b.c
#include <stdio.h>int sum=0;
void print_message(const int i) {sum+=i;
}int main() {for(int i=0; i<100; i++){print_message(i);}printf("sum = %d\n", sum);return 0;
}
想要打印每次print_message
函数执行时,变量i
的值,也可以通过python
脚本来实现。
下面是python
脚本的内容c1.py
,
import gdbclass PrintVariable(gdb.Breakpoint):def __init__(self, function, variable):super(PrintVariable, self).__init__(function, gdb.BP_BREAKPOINT)self.variable = variabledef stop(self):frame = gdb.selected_frame()if frame:try:value = frame.read_var(self.variable)print(f"Variable '{self.variable}' has value: {value}")except gdb.error as e:print(f"Error reading variable '{self.variable}': {e}")# 禁用分页
gdb.execute("set pagination off")# 要执行的程序 file 是关键字,后文件路径
gdb.execute("file /root/c_debug/b")# 替换这里的'function_name'和'variable_name'为你的函数名(或者代码行号)和变量名
function_name = "5"
variable_name = "i"# 创建一个断点在函数f的开始处
bp = PrintVariable(function_name, variable_name)# 设置断点命令
bp.commands = "print " + variable_name# 运行程序
gdb.execute("run")
脚本的内容都有注释,也就不再说什么了 。
下面看看怎么执行:
- 将上面的b.c编译成可调试的可执行程序。
gcc -g b.c -o b
,我本地是放在/root/c_debug/b
,也是上面脚本中的路径
- 使用
gdb
命令进入gdb
窗口,执行source c1.py
,由于我们是在代码第5行设置断点,就可以看到就会自动执行上面我们编译的b
,同时每次打印变量i
的值。
root@wbo112:~/c_debug# gdb
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.2) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) source c1.py
Breakpoint 1 at 0x1154: file b.c, line 5.
Variable 'i' has value: 0
Variable 'i' has value: 1
Variable 'i' has value: 2
Variable 'i' has value: 3
Variable 'i' has value: 4
Variable 'i' has value: 5
Variable 'i' has value: 6
Variable 'i' has value: 7
Variable 'i' has value: 8
Variable 'i' has value: 9
Variable 'i' has value: 10
Variable 'i' has value: 11
Variable 'i' has value: 12
Variable 'i' has value: 13
Variable 'i' has value: 14
Variable 'i' has value: 15
Variable 'i' has value: 16
Variable 'i' has value: 17
Variable 'i' has value: 18
Variable 'i' has value: 19
Variable 'i' has value: 20
Variable 'i' has value: 21
Variable 'i' has value: 22
Variable 'i' has value: 23
Variable 'i' has value: 24
Variable 'i' has value: 25
Variable 'i' has value: 26
Variable 'i' has value: 27
Variable 'i' has value: 28
Variable 'i' has value: 29
Variable 'i' has value: 30
Variable 'i' has value: 31
Variable 'i' has value: 32
Variable 'i' has value: 33
Variable 'i' has value: 34
Variable 'i' has value: 35
Variable 'i' has value: 36
Variable 'i' has value: 37
Variable 'i' has value: 38
Variable 'i' has value: 39
Variable 'i' has value: 40
Variable 'i' has value: 41
Variable 'i' has value: 42
Variable 'i' has value: 43
Variable 'i' has value: 44
Variable 'i' has value: 45
Variable 'i' has value: 46
Variable 'i' has value: 47
Variable 'i' has value: 48
Variable 'i' has value: 49
Variable 'i' has value: 50
Variable 'i' has value: 51
Variable 'i' has value: 52
Variable 'i' has value: 53
Variable 'i' has value: 54
Variable 'i' has value: 55
Variable 'i' has value: 56
Variable 'i' has value: 57
Variable 'i' has value: 58
Variable 'i' has value: 59
Variable 'i' has value: 60
Variable 'i' has value: 61
Variable 'i' has value: 62
Variable 'i' has value: 63
Variable 'i' has value: 64
Variable 'i' has value: 65
Variable 'i' has value: 66
Variable 'i' has value: 67
Variable 'i' has value: 68
Variable 'i' has value: 69
Variable 'i' has value: 70
Variable 'i' has value: 71
Variable 'i' has value: 72
Variable 'i' has value: 73
Variable 'i' has value: 74
Variable 'i' has value: 75
Variable 'i' has value: 76
Variable 'i' has value: 77
Variable 'i' has value: 78
Variable 'i' has value: 79
Variable 'i' has value: 80
Variable 'i' has value: 81
Variable 'i' has value: 82
Variable 'i' has value: 83
Variable 'i' has value: 84
Variable 'i' has value: 85
Variable 'i' has value: 86
Variable 'i' has value: 87
Variable 'i' has value: 88
Variable 'i' has value: 89
Variable 'i' has value: 90
Variable 'i' has value: 91
Variable 'i' has value: 92
Variable 'i' has value: 93
Variable 'i' has value: 94
Variable 'i' has value: 95
Variable 'i' has value: 96
Variable 'i' has value: 97
Variable 'i' has value: 98
Variable 'i' has value: 99
sum = 4950
[Inferior 1 (process 78220) exited normally]
(gdb) q