在gdb中获取打印表示的输出,并将其作为带有gdb>的printf的字符串使用?

时间:2015-11-16 21:06:33

标签: string gdb

在GDB中使用printprintf命令有所不同: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.'

这样的事情可能吗?

2 个答案:

答案 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