【问题标题】:Can gdb break on implicit class methods?gdb 可以中断隐式类方法吗?
【发布时间】:2014-05-15 20:27:33
【问题描述】:

编译器会生成一些类方法,如复制构造函数、析构函数等。是否可以在这些方法上使用 gdb 中断,例如,观察对象被复制或销毁的位置?

【问题讨论】:

  • 你可以打破成员类型的构造函数/析构函数
  • 通常,在我现在看到的情况下,所有成员也隐式定义了 {copy,move} 构造函数/析构函数。

标签: c++ debugging gdb destructor copy-constructor


【解决方案1】:

gdb 可以中断 debuginfo 中提及的任何内容或作为 ELF sybmol 提及的任何内容。顺便说一句,我不知道您的编译器是否会为这些人工方法发出调试信息。

【讨论】:

    【解决方案2】:

    gdb 可以中断隐式类方法吗?

    是的,当然可以。

    (gdb) break MyClass::MyClass(const MyClass &)     // break when copied
    (gdb) break MyClass::~MyClass()                   // break when object destroyed
    

    就这么简单。这些是基于断点的,不是基于文件:行,而是基于函数名。如果你有一个包装类的命名空间,那么请确保为它提供完全限定的名称,例如

    (gdb) break NyNamespace::MyClass::MyClass(const MyClass &)
    

    查看here 获取在 GDB 中指定断点的方法列表。

    【讨论】:

    • 使用 gdb 7.5.1 和 gcc 4.7.2 并使用 -ggdb 编译,我发现在隐式方法上设置断点会在定义类的行上设置断点。有趣的是,如果我在该文件:lieno 处设置断点,gdb 会询问我要设置哪个断点,让我选择所有隐式定义的方法!
    • 奇怪它对我不起作用。当我要求 gdb 在 boost::asio::detail::task_io_service::task_io_service() 处中断时,gdb 确实设置了一个断点(在 3 个位置?)。然后,当我在 gdb 上运行程序时,出现类似“无法插入断点 1。访问内存地址 0x11 时出错:输入/输出错误”之类的错误。
    • 嗯,这可以在模板专家类上使用吗?
    • @cli__ 我在本地使用 GCC 8.2.1 和 GDB 8.2 尝试了 this 程序。有用。我给了b area<int>r。它闯入area,执行bt 显示main 中的第一个float 变体调用被跳过,它只闯入int 变体。
    • 对我来说,Function "MyClass::MyClass(const MyClass&)" not defined. 等总是在这里失败。我创建了一个最小的主文件,并向MyClass 添加了一个简单的void ok() {} 方法。为ok 添加断点可以正常工作(通过break MyClass::ok())。即使有我能想到的所有标志也是如此:-g -ggdb -O0
    【解决方案3】:

    我有类似的需求,我用

    构建了我的代码
    g++ main.cpp -std=c++1y -o app_name -g -ggdb
    

    但是 gdb 看不到我的隐含构造函数和赋值运算符符号。出于这个原因,它不会对他们造成影响。 正因为如此,我使用这个 hack 来破解被检查类的隐式定义方法:只需使用你感兴趣的显式定义的方法来实现你自己的类。然后将这个新的类对象添加为你想要的类的数据成员调试。在新类的方法上设置断点。享受!

    例子:

    class LifeCycleInspector
    {
    public:
        LifeCycleInspector()
        {
            printf( "Default C-tor\n" );
        }
    
        LifeCycleInspector( const LifeCycleInspector& )
        {
            printf( "Copy C-tor\n" );
        }
    
        LifeCycleInspector( LifeCycleInspector&& )
        {
            printf( "Move C-tor\n" );
        }
    
        LifeCycleInspector& operator=( const LifeCycleInspector& )
        {
            printf( "Copy A-ment\n" );
            return *this;
        }
    
        LifeCycleInspector& operator=( LifeCycleInspector&& )
        {
            printf( "Move A-ment\n" );
            return *this;
        }
    
        virtual ~LifeCycleInspector()
        {
            printf( "D-tor\n" );
        }
    };
    
    class Test
    {
    private:
        LifeCycleInspector lci_;
    };
    
    void foo()
    {
        Test t1;
        Test t2( t1 );
        Test t3;
        t3 = t1;
        Test t4( std::move( t3 ) );
        Test t5;
        t5 = std::move( t4 );
    }
    
    int main()
    {
        foo();
    
        return 0;
    }
    

    输出样本:

    Default C-tor
    Copy C-tor
    Default C-tor
    Copy A-ment
    Move C-tor
    Default C-tor
    Move A-ment
    D-tor
    D-tor
    D-tor
    D-tor
    D-tor
    

    重要!如果您将方法设为空,例如:

    LifeCycleInspector( const LifeCycleInspector& )
    {   
    }
    

    - 你的编译器优化可能会做一些花哨的事情,而你的调试输出可能很奇怪。在这种情况下,您可以在方法中添加一些例程以防止优化。此外,您应该将方法实现从类移动到其标头或源,因为在类的主体中定义方法隐含地使该方法成为内联的候选者。 gdb 不会中断内联方法,因为它们通常没有可以解决的定义。

    【讨论】:

    • "最好将方法实现从标头移动到源。"这是否属实是高度上下文相关的。此外,即使对象为空,添加成员也会为其添加的类增加内存开销,因此虽然它对于调试目的很有用,但如果该类型的对象数量非常多,它可能会非常昂贵。相反,从它继承将允许空的低音类优化。
    • 谢谢,我的意思是你应该避免在类的主体中定义方法,因为这隐含地使它们成为内联的候选者(当你使用 MyClass::MyClass() = default; 定义在您的班级之外-即使您已将其移至源文件)。并且,除非您获取它的地址,否则内联方法没有实际的头/主体,gdb 不能对其进行中断。值得一提的是,EBO 不适用于多重继承,并且没有数据内存占用空间的类通常像 1 个字符。
    • 你是什么意思EBO对MI“不起作用”? godbolt.org/g/JHe1Gn 看起来不错。
    • 它必须是'EBO 通常不适用于 MI'。来源“Effective C++ 3rd edition”:Item39 关于 EBO 通常在多重继承下不可用的观察来自 David Vandevoorde 和 Nicolai M. Josuttis 的 C++ 模板(Addison-Wesley,2003)。
    • 嗯,很高兴知道我们在 15 年内取得了一些进展。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多