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

问题描述 投票:18回答:15

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

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

编辑:环境是Linux,但代码也在Windows上报告,因此对两者的建议都会有所帮助。

c++ c debugging
15个回答
20
投票

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

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


1
投票

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

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

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

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

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


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

1
投票

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

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


1
投票

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

  1. 在程序输出中找到最接近问题的位置
  2. 搜索代码库并查找该输出中的文本
  3. 开始自己打印输出,没有任何花哨,只需printf( "Calling xxx\n" );,所以你可以准确地指出问题开始的点。
  4. 一旦你确定了问题点,就设置一个断点
  5. 当您点击断点时,打印堆栈跟踪

现在你可以看到你拥有的球员并开始分析你如何到达错误的位置。

希望调用堆栈上方法的名称比a,b和c(见过这个)更有意义,并且有一些注释,方法文档比calling a更有意义(见过很多次)。

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


1
投票

谢谢你的答案很好,有很多要点。我已经多次研究过这种情况,这是我遵循的通常程序:

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

0
投票

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


8
投票

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

这可以帮助您构建和查看图像,随着您对它的熟悉程度,这些图像将在您的脑海中得到完善。

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

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


6
投票

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

  1. 它允许您测试您对代码如何工作的假设。添加通过测试证明您对正在测试的那小段代码的假设是正确的。您编写的测试越多,您对代码的理解就越好。
  2. 当您修复错误并且知道您已成功完成时,将通过失败的单元测试来重现您要修复的错误。
  3. 您编写的单元测试将作为未来的文档。
  4. 由于修复了更多错误,因此单元测试您编写的回归测试。

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


5
投票

一些指示:

  1. 从与工作流程更相关的部分进行调试。
  2. 使用调试字符串
  3. 获取适当的.pdb并将核心转储附加到Windbg或debugdiag等调试器中进行分析。
  4. 在您的组织中获得擅长调试的人员帮助。即使他不熟悉你的代码库,他也会非常乐于助人。我有过经验。他们会给你有价值的指示。
  5. 根据Per Assaf Lavie的建议,您可以使用静态代码分析器。
  6. 最重要的是:在您进行探索和调试时,随着进度记录所有内容。至少接替你的人会遭受更少的痛苦。

3
投票

我还没看到三件事:

  1. 编写一些使用库/接口的单元测试。证明/验证您对它们的理解并促进其可维护性。
  2. 有时候创建一个特殊的断言宏来检查其他工程师的假设是否与你的一致是很好的。你可以: 不承诺使用它们 提交它们的用途,在给定的时间段后将它们转换为“真正的”断言 提交他们的用途,允许另一位工程师(更熟悉项目)处理或促进他们真正的断言
  3. 重构也可以帮助。难以阅读的代码是一种指示。

2
投票

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


2
投票

cscope + ctags可在Linux和Windows上使用(通过Cygwin)。如果你给他们机会,这些工具将成为你不可或缺的工具。虽然像Visual Studio这样的IDE在代码浏览设备方面也做得很好。

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

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


1
投票

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


1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.