如何调试一个巨大的不太熟悉的代码库?

时间:2010-10-08 09:55:24

标签: c++ c debugging

在大型项目工作期间,很快就会转移到一个已经处于维护阶段的项目。你最终会拥有一个巨大的代码C / C ++代码库,并没有太多关于设计的文档最后一个可以给你一些关于代码的知识转移的人已经离开了公司并且增加了你的恐惧,没有足够的时间来熟悉代码并开发对整个模块的理解。在这种情况下当您需要修复模块上的错误(核心转储,功能,性能问题等)时,您将采取什么方法?

所以问题是: 在尝试修复错误时,调试不太熟悉的C / C ++代码库的常用步骤是什么?

编辑:Enviornment是Linux,但代码也在Windows上移植,因此对这两者的建议都会有所帮助。

15 个答案:

答案 0 :(得分:20)

如果可能,从main()到有问题的区域逐步执行,并按照执行路径执行。在此过程中,您将很好地了解不同部分如何共同发挥作用。

使用静态代码分析工具(如CppDepends甚至Doxygen)来确定模块之间的关系并能够以图形方式查看它们也会很有帮助。

答案 1 :(得分:8)

使用笔和纸,或一般的图像/图表/图表来确定哪些部件属于哪里并绘制一些箭头等等。

这有助于您构建和查看图像,随着您对图像的熟悉程度,图像会在您的脑海中得到改善。

我使用类似的方法攻击一个地狱般的系统,这个系统有10个单身人士,彼此相互包容。为了适应一切,我不得不重新绘制几次,但在你面前看它有帮助。

在构造依赖关系图时使用Graphviz可能也很有用。这样你只需列出所有内容(在文本文件中)然后该工具将绘制(通常难看的)图片。 (这就是我为上述系统中的#include依赖所做的事情)

答案 2 :(得分:6)

正如其他人已经建议的那样,编写单元测试是进入代码库的好方法。这种方法有许多优点:

  1. 它可以让你测试你的 关于代码如何的假设 作品。添加通过测试证明 那是你的假设 你是一小段代码 测试是正确的。更多 通过你写的测试,越好 你了解代码。

  2. 重现的失败单元测试 你要修复的bug会通过 当你修复bug时你就知道了 你成功了。

  3. 您编写的单元测试充当 未来的文件。

  4. 你写的单元测试行为 回归测试,因为更多的错误 固定的。

  5. 当然,将单元测试添加到遗留代码并不总是一件容易的事。令人高兴的是,一位名叫Michael Feathers的绅士写了an excellent book on the subject,其中包括一些关于在没有单元测试的情况下向代码库添加测试的“食谱”。

    WELC

答案 3 :(得分:5)

一些指示:

  1. 从看起来更多的部分进行调试 与工作流程相关。
  2. 使用调试 字符串
  3. 获取适当的.pdb并附加 Windbg等调试器中的核心转储 或者debugdiag来分析它。
  4. 在你的帮助中获得一个人的帮助 擅长的组织 调试。即使他是你的新手 代码库,他可能会非常有帮助。 我有过经验。他们将 给你宝贵的指示。
  5. Per Assaf Lavie建议,您可以使用静态代码分析器。
  6. 最重要的是:和你一样 探索和调试,文档 你进步的一切。至少 接替你的人会 受苦少。

答案 4 :(得分:3)

我还没看到三件事:

  1. 编写一些使用库/接口的单元测试。证明/验证您对它们的理解并促进其可维护性。

  2. 有时候创建一个特殊的断言宏来检查其他工程师的假设是否与你的一致是很好的。你可以:

    1. 未提交用途
    2. 提交其用途,在给定时间段后将其转换为“真实”断言
    3. 提交其用途,允许其他工程师(更熟悉项目)处理或促使他们进行真正的断言
  3. 重构也可以提供帮助。难以阅读的代码是一种迹象。

答案 5 :(得分:2)

第一步应该是尝试阅读代码。尝试查看bug所在的代码。遵循从主要到该点的代码,并尝试看看可能出错的地方。阅读代码中的注释(如果有的话)。通常,函数名称很有用。了解每个功能的作用 一旦了解了代码,就可以开始调试代码了。将断点放在您不理解代码或您认为错误的位置。逐行开始遵循代码。调试就像性。最初很痛苦,但慢慢开始享受它。

答案 6 :(得分:2)

Linux和Windows(通过Cygwin)都可以使用

cscope + ctags。如果你给他们机会,这些工具将成为你不可或缺的工具。虽然,像Visual Studio这样的IDE也可以很好地使用代码浏览工具。

在像你这样的情况下,由于时间的限制,你会受到症状的驱使。我的意思是你没有时间重建大图/设计/架构。因此,您要专注于症状并向外工作,每次重建尽可能多的大局,以满足您对特定问题的需求。但是不要急于做出“本地”决定。有耐心看到做出高质量决定所需的大局。并且不要陷入创可贴综合症,即任何旧的修复都会起作用。您的工作是保留底层架构/设计(如果有的话,以及您可以发现它的任何程度)。

一开始这将是一场斗争,因为你的思想过度“狩猎”。但很快就会出现设计/架构中的主题,所有这些都将开始变得有意义。想想,不思考,草蜢:)

答案 7 :(得分:1)

你必须有一个完全可靠的IDE,它有很多debbugging工具(断点,手表等)。熟悉大量代码的最佳方法是使用它来查看数据如何从一种方法传递到另一种方法。此外,您可以对代码进行反向工程,以便可以看到类的关系。 :D祝你好运!

答案 8 :(得分:1)

while (!codeUnderstood)
{
  Breakpoints();
  Run();
  StepInto();
  if(needed)
  { 
   StepOver();
  }
}

答案 9 :(得分:1)

对我而言,只有一种方法可以了解流程 - 互动。确定流程/系统的接口。然后确定输入/输出关系(这些步骤可能不是线性的)。一旦你这样做,你就可以开始修改代码,因为你知道它应该做什么,那么它只是找到“它是如何实际完成的”。但对我来说,了解系统的界面(不一定是用户界面)是关键。说穿了 - 不要先触摸代码!!!

答案 10 :(得分:1)

不确定C / C ++,但来自Java和C#,单元测试会有所帮助。在Java中有用于单元测试的JUnit和TestNG库,在C#中有NUnit和mstest。不确定C / C ++。

阅读Martin Fowler,Kent Beck等人的着作“重构:改进现有代码的设计”。在那里会有很多提示,我相信这会有所帮助,并为您提供一些改进代码的指导。

一个提示:如果它没有破坏,请不要修理它。如果它工作,不要试图修复一些库或真正复杂的功能。专注于存在错误的部分。

编写单元测试以重现代码应该工作的场景。测试最初会失败。修复代码,直到单元测试成功通过。重复:)

一旦大部分代码,过于复杂而无法手动调试和修复的重要部分都在自动化单元测试中,您将拥有回归测试的安全工具,这将使您在更改时更有信心现有的代码库。

答案 11 :(得分:1)

我并不试图按照许多人的建议来概述整个系统。如果有需要修复的东西,我会学习代码的最小部分,我可以修复bug。下一次出现问题时,我会更熟悉一点,而且我会学到一些东西。最终我能够支持整个社交。

如果管理层建议我对我不熟悉的事情进行重大更改,请确保他们了解时间尺度,如果事情非常混乱则表示重写。

答案 12 :(得分:1)

通常,相关程序会产生某种输出(日志,控制台打印输出,对话框)。

  1. 找到离您最近的地方 程序输出中的问题
  2. 搜索代码库并查找该输出中的文本
  3. 开始自己打印,没有任何花哨,只有printf( "Calling xxx\n" );,所以你可以准确地指出问题开始的位置。
  4. 一旦确定问题点,请设置断点
  5. 当你点击断点时,打印一个stacktrace
  6. 现在你可以看到你拥有的玩家并开始分析你是如何到达错误的地方的。

    希望调用堆栈上方法的名称比 a b c (看到这个)更有意义,并且那里是某种评​​论,方法文档比calling a更有意义(多次见过)。

    如果来源记录不完整,一旦弄清楚发生了什么,不要害怕留下你的评论。如果程序设计允许它为您修复的问题创建一个单元测试。

答案 13 :(得分:1)

感谢您提供了很好的答案,还有很多要点。我已经多次研究过这种情况,这是我遵循的常规程序:

  1. 检查崩溃日志或跟踪日志。如果只是一个简单的开发人员错误,如果无法一次评估,则检查相关的跟踪,然后转到2。
  2. 重现错误!这是最重要的事情。一些错误很少发生,如果你能够重现这个错误,就像它一样。这意味着你有更好的破解率。
  3. 如果您无法重现错误,请找到另一种用例,即可以实际重现错误的情况。能够实际调试场景比崩溃日志更有用。
  4. 前往版本控制!检查以前的几个软件版本是否存在相同的错误行为。如果不是......沃拉!你可以找到bug引入的两个版本,你可以轻松获得两个版本的代码差异并定位相关区域。(有时它不是新添加的代码有bug但它暴露了一些旧的剩余.Well我们至少有一个开始我会说!)
  5. 启用调试跟踪。运行错误的用例,检查是否可以找到一些对调查有用的其他信息。
  6. 通过跟踪日志获取相关代码区域。查看一些介绍该bug的代码。
  7. 在相关代码中加入一些断点。研究流程。检查数据流。寻找指针(通常的罪魁祸首)。重复,直到你掌握了流量。
  8. 如果您的SW版本不能重现该错误,请比较流程中的不同之处。问问自己,差异是什么?
  9. 仍然没有运气! - Arghh ......我的技巧已经筋疲力竭......需要领先于旧路。理解代码......并理解代码并理解它,直到您知道在执行特定用例时代码中发生了什么。
  10. 通过新开发的理解尝试调试代码并确保解决方案即将来临。
  11. 最重要 - 记录您对模块的理解。即使是小编的坚韧不拔的东西。有一天,它肯定会帮助你或者像你一样的人......有时候!

答案 14 :(得分:0)

您可以尝试使用GNU cFlow工具(http://www.gnu.org/software/cflow/)。 它将为您提供图表,绘制程序内的控制流程。