编码以便于调试

时间:2010-01-14 12:56:22

标签: c++ debugging

我正在寻找有关如何通过向我的应用程序添加代码来帮助调试的提示。一个例子,以便我更清楚我所追求的是:为了检测shared_ptrs持有的悬空对象,我创建了一个跟踪器类,它允许我跟踪有多少对象存活以及它们最初创建的位置,这是然后像这样使用:

class MyClass {
    TRACK_THIS_TYPE(MyClass);
};

boost::shared_ptr<MyClass> myObj(new MyClass);
TRACK_THIS_OBJECT(myObj);

其中TRACK_THIS_TYPE(t)是一个宏,用于确保获取类的实例计数(以及已创建的对象数),TRACK_THIS_OBJECT是一个宏,用于存储创建对象的文件和行与weak_ptr一起到对象。

这允许我检测悬空物体以及它们在何处创建。它不允许我找出哪些对象将shared_ptr保存到我的对象,这可能是对上述内容的改进。我想可以创建类似TRACK_THIS_PTR(T)宏的东西,它将存储创建shared_ptr实例的文件和行。

另一个例子是旧的

assert(condition && "My descriptive text");

允许您将有意义的注释直接放入断言中。

有没有人有任何巧妙的c ++技巧来收集统计数据,自动堆栈跟踪,跟踪对象/指针/资源,死锁/饥饿或其他线程问题检测,确保在某处处理异常,文档帮助或类似?任何事情都是真的,无论是有助于防止错误的东西,还是有助于防止错误的东西。

修改:除了对此问题的回复之外,我还收到了有关google-glog作为日志记录实用程序的提示。

6 个答案:

答案 0 :(得分:4)

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

它会强迫您考虑哪些值可以带走您的数据,并且可以更容易地快速找到导致某些愚蠢错误的重构。

如果你真的关心表演,你可以禁用它们进行发布。

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

答案 1 :(得分:3)

有趣的是,为了跟踪记忆问题,我们有相似的项目正在进行中。

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

我为之工作的公司(Amadeus)目前正在开发AMPolice重新组合valgrind。它仍在进行中,特别是在文档部门。

正如您所看到的,这与您自己的方法完全不同:二进制保持不变,并在必要时通过切换到“调试”内存管理(在运行时)来跟踪内存分配(使用命令行) API)。

因此,这更容易使用,但是从我们的测试中它确实会影响时间(4x或5x)。

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

答案 2 :(得分:2)

以下内容主要是为了在发布后轻松调试

Stackwalker和类似工具提供了一种在最终用户计算机上获取可用调用堆栈的简便方法,没有调试器处于活动状态。非常相似,Google Breakpad可用于从崩溃的进程中轻松提取迷你转储。

答案 3 :(得分:1)

您可以查看使用“log4cxx”

之类的内容记录您正在生成的统计数据

http://logging.apache.org/log4cxx/index.html

这可以让您控制在运行时进行的跟踪级别(或者至少通过在运行时读入的配置文件)。

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

答案 4 :(得分:1)

我个人更愿意瞄准不首先编写错误。将大量调试代码添加到应用程序可能会产生不幸的副作用。它显然影响性能,在多线程应用程序的情况下,可以改变应用程序的时间,导致MT错误被隐藏。

我发现我通常花很少的时间来调试我的代码 - 而且每次我在调试器中花费的时间都算浪费时间。我发现的确有帮助的是我可以在每次更改后运行测试 - 您不必使用TDD来使用此方法。

答案 5 :(得分:0)

我的c ++现在有点生疏,但我记得每当我必须分配一个对象时,我就有一个执行它的宏:

新(课)

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

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