在GDB中使用print
和printf
命令有所不同:print
如果参数是对象,则可以打印相关字段,而printf
严格要求格式说明符和C风格的字符串。
我想做的是"得到" gdb
print
表达式的输出,并将其用作具有%s
格式说明符的字符串数据。通常,这不起作用 - 使用此test.cpp
程序:
// g++ --std=c++11 -g test.cpp -o test.exe
#include <iostream>
#include <set>
std::string aa; // just to have reference to std::string
int main()
{
std::set<std::string> my_set;
my_set.insert("AA");
my_set.insert("BB");
std::cout << "Hello World!" << std::endl;
return 0;
}
我可以得到这样的输出:
$ gdb --args ./test.exe
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Breakpoint 1, main () at test.cpp:13
13 std::cout << "Hello World!" << std::endl;
(gdb) p my_set
$1 = std::set with 2 elements = {[0] = "AA", [1] = "BB"}
(gdb) p *my_set
No symbol "operator*" in current context.
(gdb) p my_set->begin()
Cannot resolve method std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::begin to any overloaded instance
...但我不能只使用my_set
作为printf
的参数,因为预期会有一个char数组:
(gdb) printf "it is: '%s'\n", my_set
it is: 'Value can't be converted to integer.
那么,有可能以某种方式获取print
对象的表示形式作为字符串,将其用作printf
的参数吗?假设伪代码函数print_repr()
,我想实现这个目标:
(gdb) printf "it is: '%s'\n", print_repr(my_set)
it is: '= std::set with 2 elements = {[0] = "AA", [1] = "BB"}'
...也希望同样能够解决错误,比如说:
(gdb) printf "it is: '%s'\n", print_repr(*my_set)
it is: 'No symbol "operator*" in current context.'
这样的事情可能吗?
答案 0 :(得分:1)
首先,让我说IMO没有很好的理由,gdb不能通过特殊的printf
替换来做到这一点。为什么不?整个事情都在gdb的控制之下
也就是说,使用gdb CLI是可能的,但这很困难。你必须做一个涉及set logging
的有趣舞蹈,然后使用shell
将输出文件重写为gdb脚本,然后source
。可能,但粗略。
使用Python稍微简单一些。您可以在Python中编写一个便捷函数,将函数作为字符串返回。然后你可以写出类似的东西:
(gdb) printf "%s", $_print_repr(whatever)
gdb手册中有一节介绍编写新的便捷功能。这很容易。
答案 1 :(得分:0)
我只是尝试使用@TomTromey的指针,但它并非完全无关紧要,所以我将这个作为答案发布,在gdb 7.7.1上测试。首先,我将其添加到我的~/.gdbinit
文件中:
python
# https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API
# https://sourceware.org/gdb/onlinedocs/gdb/Functions-In-Python.html
class Greet (gdb.Function):
"""Return string to greet someone.
Takes a name as argument."""
def __init__ (self):
super (Greet, self).__init__ ("greet")
def invoke (self, name):
return "Hello, %s!" % name.string ()
Greet() # instantiate; then call in gdb: p $greet("myname")
#(gdb) python gdb.execute("p 22")
#$2 = 22
class Strp (gdb.Function):
"""Get a representation of gdb's printout as string
Argument: the gdb command as string."""
def __init__ (self):
super (Strp, self).__init__ ("strp")
def invoke (self, incmd):
incmds = str(incmd)
#print("is",incmds)
# strip quotes
if (incmds[0] == incmds[-1]) and incmds.startswith(("'", '"')):
incmds = incmds[1:-1]
rets=""
try:
rets += gdb.execute(incmds, from_tty=True, to_string=True)
except Exception as e:
rets += "Exception: {0}".format(e)
#print("ret",ret)
return rets
Strp() # instantiate; then call in gdb: p $strp("p 22")
# quotes end up in arg string, so by default getting Python Exception <class 'gdb.error'> Undefined command: "". Try "help".: ... but then, if call WITHOUT quotes p $strp(p aa), gdb may interpret "p" as symbol (which doesn't exist) before it is sent as arg to python; therefore, use quotes, then strip them
end
然后,我可以在gdb
:
(gdb) p $strp("x 22")
$1 = "Exception: Cannot access memory at address 0x16"
(gdb) p $strp("p 22")
$3 = "$2 = 22\n"
(gdb) p $strp("p aa")
$4 = "Exception: No symbol \"aa\" in current context."
(gdb) printf "%s\n", $strp("p 22")
You can't do that without a process to debug.
因此,常规gdb
p
rint不需要进程,但是printf
会这样做 - 所以在设置断点后,r
将程序/进程设置为gdb
,最后可以在提示符下使用Python函数和printf
:
Breakpoint 1, main () at /...:17
17 int main( ){
(gdb) printf "%s\n", "aa"
aa
(gdb) printf "%s\n", $strp("p 22")
$12 = 22
(gdb) printf "%s\n", $strp("x 22")
Exception: Cannot access memory at address 0x16
(gdb) printf "%s\n", $strp("p aa")
Exception: No symbol "aa" in current context.
(gdb) printf "%s -- %s\n", $strp("p aa"), $strp("p 22")
Exception: No symbol "aa" in current context. -- $13 = 22