使用gdb调试C++模板

10
当我在使用模板的函数中进行调试时,我想知道当前函数使用哪种模板类型。我尝试了“p T”,但它会显示gdb不能打印出该类型。
如何在特定的模板类型上设置断点?比如说函数foo<T>(...)有两种可能的形式:foo<int>(...)foo<long>(...)。如何设置断点,使得gdb只在使用int的第一个函数处暂停,而不是使用long的第二个函数处暂停?
编辑:如果能够按行号设置断点就更好了。这样做有很多好处,例如函数的初始部分可能运行时间长,在if语句中调试的位置可能等等。

1
我今天在工作中做了这件事,我相信 b foo<int> 可以正常工作... 如果你执行 b foo.cpp:123(假设第123行是 foo<T> 的行),它将为所有模板变量设置多个断点。 - Mats Petersson
你所在的模板实例化应该在回溯中可见。 - Baum mit Augen
https://dev59.com/N3I-5IYBdhLWcg3wy76n - Ciro Santilli OurBigBook.com
2个回答

13

要为所有实例设置断点,请使用:

gdb> rbreak Foo<.*>

仅在已知实例上设置断点

gdb> break Foo<int>

你也可以使用 rbreak Foo<int>,但是如果您没有给出任何正则表达式进行评估,则使用调用witch没有意义 :-)

示例代码:

#include <iostream>
#include <string>

template < typename T>
T Foo(T t) { return t; }

int main()
{
    std::cout << Foo<int>(1) << std::endl;
    std::cout << Foo<std::string>("Hallo") << std::endl;
}

只需使用调试信息编译:

g++ main.cpp -g -o go

运行gdb:

gdb go

And test:

gdb> rbreak Foo<int>
gdb> run
gdb> backtrace
gdb> cont

正如您所看到的:只有一个模板实例受到影响。

在回溯中,您可以看到调用了哪个模板实例:

#0  Foo<int> (t=1) at main.cpp:5
#1  0x0000000000400b69 in main () at main.cpp:9

正如您所看到的,这里涉及到的是Foo<int>

回答一个评论:

"有没有一种方法可以在特定已知实例的某一行中设置断点?"

有的!

gdb> break main:692
gdb> info break

这将返回类似于以下内容:
Num     Type           Disp Enb Address            What
5       breakpoint     keep y   <MULTIPLE>         
5.1                         y   0x00000000004026db in Foo<int>(int) at main.cpp:692
5.2                         y   0x00000000004027a6 in Foo<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) at main.cpp:692
(gdb)

现在,您可以通过以下方式禁用特定的实例:
gdb> disable 5.2

您无法删除特定的“子断点”。但禁用是您需要的。如果您喜欢,还可以使用范围实例,例如:

gdb> disable 5.1-2

1
啊,如果b Foo默认与rbreak Foo<.*>的所有实例相同就更好了。 - Ciro Santilli OurBigBook.com
有没有一种方法可以在特定已知实例的某一行中设置断点?例如,类似于“b foo.cpp:5”,但是过滤器为“Foo<int>”? - Dan M.
@DanM。我更新了我的回答。简短来说:是的,你可以 :-) - Klaus
@Klaus 谢谢!除了范围禁用,还有其他的集中禁用方法吗?范围禁用非常有帮助,也是我一直在寻找的,但在我的情况下,仅在一行上中断就会产生数千个子断点,因此任何自动化的方式(比如rbreak)都会很感激。 - Dan M.

5

您可以使用ptype而不是p来打印类型。如果您的g++和gdb版本足够新(几年前的版本),这将起作用。

考虑以下源代码:

#include <iostream>

template<typename T>
struct S
{
  S(T t)
  {
    std::cout << t;
  }
};

int main()
{
  S<const char*> s2("hello");
  S<int> s1(23);
  return 0;
}

在这里,我可以进入s2的构造函数并查看T

(gdb) ptype T
type = const char *

看一下当前帧:

(gdb) frame
#0  S<char const*>::S (this=0x7fffffffe35f, t=0x400940 "hello") at q.cc:8
8       std::cout << t;

我可以使用给定的函数名称设置断点:
(gdb) b S<const char *>::S
Breakpoint 2 at 0x40087a: file q.cc, line 8.

我更新了ptype部分以打印有趣的事情——即ptype T而不是ptype S。在回答时有一点小错误。 - Tom Tromey
1
你可以通过行号设置断点,但问题是基于类型进行过滤。这可以通过一些Python脚本来完成;基本上你需要一个新的便利函数来检查类型。 - Tom Tromey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接