C++ 为便于调试而编码

C++ 为便于调试而编码,c++,debugging,C++,Debugging,我正在寻找如何通过向应用程序添加代码来帮助调试的技巧。举一个例子,以便更清楚地了解我的目的:为了检测共享PTR持有的悬空对象,我创建了一个跟踪器类,该类允许我跟踪有多少对象处于活动状态以及它们最初创建的位置,然后像这样使用: class MyClass { TRACK_THIS_TYPE(MyClass); }; boost::shared_ptr<MyClass> myObj(new MyClass); TRACK_THIS_OBJECT(myObj); 这允许您将有意

我正在寻找如何通过向应用程序添加代码来帮助调试的技巧。举一个例子,以便更清楚地了解我的目的:为了检测共享PTR持有的悬空对象,我创建了一个跟踪器类,该类允许我跟踪有多少对象处于活动状态以及它们最初创建的位置,然后像这样使用:

class MyClass {
    TRACK_THIS_TYPE(MyClass);
};

boost::shared_ptr<MyClass> myObj(new MyClass);
TRACK_THIS_OBJECT(myObj);
这允许您将有意义的注释直接放入断言中

<> P>有人有没有任何干净的C++技巧来收集统计数据、自动堆栈跟踪、跟踪对象/指针/资源、死锁/饥饿或其他线程问题,确保异常在某个地方得到处理,文档帮助或类似的操作?无论是有助于防止错误还是事后帮助,一切都会顺利进行


编辑:除了回答这个问题外,我还收到了一些关于日志记录实用程序的提示。

您可以使用log4cxx之类的工具来记录您正在生成的统计数据

这应该允许您控制运行时正在执行的跟踪级别,或者至少通过在运行时读入的配置文件进行控制


它将自动为日志记录添加时间戳,并允许您设置输出格式以适应其他工具,如Excel或数据库,从而允许您对日志记录数据进行统计分析。

您可以使用类似log4cxx的工具来记录您正在生成的统计数据

这应该允许您控制运行时正在执行的跟踪级别,或者至少通过在运行时读入的配置文件进行控制


它将自动为日志记录添加时间戳,并允许您设置输出格式以适合其他工具,例如Excel或数据库,以便您对日志记录数据进行统计分析。

我经常使用BOOST_ASSERT来检查输入、中间计算和返回之前,即使它看起来很明显

它迫使您思考哪些值可以获取数据,并使您更容易快速找到留下一些愚蠢错误的重构

如果您真的关心性能,可以在发布版本中禁用它们


关于内存管理,我大量使用RAII,并尽力使用尽可能少的指针和手动内存分配。当代码中只有2或3个指针时,更容易避免出错。

我经常使用BOOST\u ASSERT来检查输入、中间计算和返回之前,即使它看起来很明显

它迫使您思考哪些值可以获取数据,并使您更容易快速找到留下一些愚蠢错误的重构

如果您真的关心性能,可以在发布版本中禁用它们


关于内存管理,我大量使用RAII,并尽力使用尽可能少的指针和手动内存分配。当代码中只有2或3个指针时,更容易避免出错。

我个人更倾向于一开始就不写bug。向应用程序添加大量调试代码可能会产生不幸的副作用。它显然会影响性能,并且在多线程应用程序的情况下,可能会更改应用程序的计时,从而导致隐藏MT错误


我发现我通常只花很少的时间调试我的代码,而我花在调试器上的任何时间都被认为是浪费了时间。我发现有帮助的是,我可以在每次更改后运行测试—使用这种方法不必使用TDD。

我个人更倾向于一开始就不编写bug。向应用程序添加大量调试代码可能会产生不幸的副作用。它显然会影响性能,并且在多线程应用程序的情况下,可能会更改应用程序的计时,从而导致隐藏MT错误


我发现我通常只花很少的时间调试我的代码,而我花在调试器上的任何时间都被认为是浪费了时间。我发现有帮助的是,我可以在每次更改后运行测试—您不必使用TDD来使用这种方法。

以下内容主要是为了方便发布后的调试


类似的工具提供了一种在最终用户机器上获取可用调用堆栈的简单方法,而无需激活调试器。非常类似,可以用于从崩溃的进程中轻松提取迷你转储。

以下内容主要是为了在发布后易于调试


类似的工具提供了一种在最终用户机器上获取可用调用堆栈的简单方法,而无需激活调试器。非常类似,可用于从崩溃进程中轻松提取微型转储。

有趣的是,我们有一个非常类似的项目正在工作,以便跟踪内存问题

许多人对RAII信誓旦旦,但即使使用shared_ptr,也可能造成泄漏,问题主要是由于引用周期,这就是为什么基于引用计数的垃圾收集器有特殊的算法来检测 t周期:x

我为Amadeus工作的公司目前正在开发Resemble valgrind。这项工作仍在进行中,特别是在文件部

如您所见,这与您自己的方法完全不同:二进制文件保持不变,并且在运行时使用命令行API切换到调试内存管理来跟踪内存分配

因此,这更易于使用,尽管从我们的测试来看,它确实会影响4倍或5倍的计时


该工具非常通用,因此通常可以被许多人使用,但当然主要问题仍然是日志的剪切大小,因为跟踪每一个新的日志都非常昂贵:x

有趣的是,为了跟踪内存问题,我们有一个非常类似的项目

许多人都相信RAII,但即使使用shared_ptr,也可能造成泄漏,问题主要是由于引用周期,这就是为什么基于引用计数的垃圾收集器有特殊的算法来检测周期:x

我为Amadeus工作的公司目前正在开发Resemble valgrind。这项工作仍在进行中,特别是在文件部

如您所见,这与您自己的方法完全不同:二进制文件保持不变,并且在运行时使用命令行API切换到调试内存管理来跟踪内存分配

因此,这更易于使用,尽管从我们的测试来看,它确实会影响4倍或5倍的计时


工具是相当通用的,所以通常可以被许多人使用,但是当然,主要的问题仍然是日志的剪切大小,因为追踪每一个新的都是相当昂贵的:X

< P>我的C++现在有点生疏了,但我确实记得每当我分配一个对象时,我就有一个宏执行了类似的:

新阶级

然后是一个用于释放对象的宏,称为RELEASE。然后,每个宏将存储创建和销毁对象的类和行号


这将允许通过查看对象没有被释放的对象来容易地检测内存泄漏。

< P>现在我的C++有点生疏了,但是我确实记得每当我分配一个对象时,我就有一个宏,执行它的过程,比如:

新阶级

然后是一个用于释放对象的宏,称为RELEASE。然后,每个宏将存储创建和销毁对象的类和行号


这样可以通过查看对象未被释放的位置来轻松检测内存泄漏。

小心断言和副作用!它可以在部署/调试时更改您的行为,前提是您删除部署时生成的断言。您能否给出一个副作用的示例。这是一篇关于编写断言宏的好文章,一般来说可能是宏。。小心断言和副作用!它可以在部署/调试时更改您的行为,前提是您删除部署时生成的断言。您能否给出一个副作用的示例。这是一篇关于编写断言宏的好文章,一般来说可能是宏。。我认为我们大多数人的目标是不写bug…:影响性能是可以的,通常任何这样的调试代码都只包含在调试构建中。不过,你关于测试的观点非常正确。您还有一个正确的观点,即调试代码可能会影响程序的行为-这并不意味着为调试构建包含调试代码总是一件坏事。我想您不会做太多的MT工作吧?说真的,我见过甚至写一个简单的调试日志都会隐藏MT bug的情况。@Neil Butterworth:我做过很多多线程编码,是的,我见过调试代码会影响计时的情况,例如在写日志或其他东西时。还有很多其他因素也会影响时间安排-调试和发布版本的行为可能会完全不同,即使没有您自己的调试代码。通常情况下,您应该能够包含/排除带有define或类似名称的调试代码,以便在调试和发布版本中使用或不使用define进行测试。@Neil回答和注释绝对正确。为调试而编写代码是浪费时间的两倍。同样,任何测量系统的方法都必然会影响它。运行测试确实是一个更好的解决方案。有些bug是单元测试无法检测到的。例如,如果每个单元本身工作正常,但所有单元一起形成一个循环引用,则断开引用计数智能指针。在正确的位置使用简单的断言可以防止这种情况。至于MT bug:我个人更倾向于首先编写线程安全代码;-无论如何,我不认为机器翻译错误在这里是相关的。当然,调试代码可能会隐藏一个机器翻译错误,但它也可能会暴露一个在其他情况下找不到的机器翻译错误。我认为我们大多数人的目标是不编写错误…:影响性能是可以的,通常只包括任何此类调试代码
在调试版本中。不过,你关于测试的观点非常正确。您还有一个正确的观点,即调试代码可能会影响程序的行为-这并不意味着为调试构建包含调试代码总是一件坏事。我想您不会做太多的MT工作吧?说真的,我见过甚至写一个简单的调试日志都会隐藏MT bug的情况。@Neil Butterworth:我做过很多多线程编码,是的,我见过调试代码会影响计时的情况,例如在写日志或其他东西时。还有很多其他因素也会影响时间安排-调试和发布版本的行为可能会完全不同,即使没有您自己的调试代码。通常情况下,您应该能够包含/排除带有define或类似名称的调试代码,以便在调试和发布版本中使用或不使用define进行测试。@Neil回答和注释绝对正确。为调试而编写代码是浪费时间的两倍。同样,任何测量系统的方法都必然会影响它。运行测试确实是一个更好的解决方案。有些bug是单元测试无法检测到的。例如,如果每个单元本身工作正常,但所有单元一起形成一个循环引用,则断开引用计数智能指针。在正确的位置使用简单的断言可以防止这种情况。至于MT bug:我个人更倾向于首先编写线程安全代码;-无论如何,我不认为机器翻译错误在这里是相关的。当然,调试代码可能会隐藏一个机器翻译错误,但它也可能会暴露一个在其他情况下找不到的机器翻译错误。我认为这看起来非常有趣,可能是从野外潜在的崩溃中获取信息的一个非常好的方法。我接受了这个答案,因为我现在开始使用Breakpad并取得了很好的效果。我以前不知道Google Breakpad。我认为这看起来确实很有趣,而且可能是从野外潜在的崩溃中获取信息的一个很好的方法。我接受了这个答案,因为我现在已经开始使用Breakpad并取得了很好的效果。
assert(condition && "My descriptive text");