在大型项目工作期间,很快就会转移到一个已处于维护阶段的项目。最终你手上有一个巨大的代码C / C ++代码库,并没有太多关于设计的文档。最后能够给你一些关于代码的知识转移的人已经离开了公司,并且为你的恐怖添加了没有足够的时间来熟悉代码并开发对整体模块的理解。在这种情况下,当你是期望修复模块上的错误(核心转储,功能,性能问题等)你将采取什么方法?
所以问题是:在尝试修复错误时,调试不太熟悉的C / C ++代码库的常用步骤是什么?
编辑:环境是Linux,但代码也在Windows上报告,因此对两者的建议都会有所帮助。
如果可能,从main()到有问题的区域逐步执行,并按照执行路径执行。在此过程中,您将了解不同部分如何共同发挥作用。
使用静态代码分析工具(如CppDepends甚至Doxygen)来计算模块之间的关系并能够以图形方式查看它们也会很有帮助。
不确定C / C ++,但来自Java和C#,单元测试将有所帮助。在Java中有用于单元测试的JUnit和TestNG库,在C#中有NUnit和mstest。不确定C / C ++。
阅读Martin Fowler,Kent Beck等人的着作“重构:改进现有代码的设计”一书。将在那里提供相当多的提示,我相信这将有所帮助,并为您提供改进代码的一些指导。
一个提示:如果它没有破坏,请不要修理它。如果它工作,不要试图修复一些库或真正复杂的功能。专注于存在错误的部分。
编写单元测试以重现代码应该工作的场景。测试最初会失败。修复代码,直到单元测试成功通过。重复:)
一旦大部分代码,过于复杂而无法手动调试和修复的重要位,都在自动化单元测试中,您将拥有回归测试的安全工具,这将使您对更改现有代码库更有信心。
while (!codeUnderstood)
{
Breakpoints();
Run();
StepInto();
if(needed)
{
StepOver();
}
}
我不打算按照许多人的建议来概述整个系统。如果有需要修复的东西,我会学习代码的最小部分,我可以修复bug。下一次出现问题时,我会更熟悉一点,而且我会学到一些东西。最终我能够支持整个shebang。
如果管理层建议我对我不熟悉的事情进行重大改变,我会确保他们理解时间尺度,如果事情非常混乱则表示重写。
通常,有问题的程序会产生某种输出(日志,控制台打印输出,对话框)。
printf( "Calling xxx\n" );
,所以你可以准确地指出问题开始的点。现在你可以看到你拥有的球员并开始分析你如何到达错误的位置。
希望调用堆栈上方法的名称比a,b和c(见过这个)更有意义,并且有一些注释,方法文档比calling a
更有意义(见过很多次)。
如果来源记录不完整,那么一旦弄清楚发生了什么,不要害怕留下你的评论。如果程序设计允许它为您修复的问题创建单元测试。
谢谢你的答案很好,有很多要点。我已经多次研究过这种情况,这是我遵循的通常程序:
你可以试试GNU cFlow工具(http://www.gnu.org/software/cflow/)。它将为您提供图表,绘制程序内的控制流程。
通常使用笔和纸,或图像/图形/图表来确定哪些部件属于哪里并绘制一些箭头等等。
这可以帮助您构建和查看图像,随着您对它的熟悉程度,这些图像将在您的脑海中得到完善。
我使用类似的方法来攻击一个地狱般的系统,这个系统有10个单身人士,彼此都是#include。为了适应一切,我不得不重新绘制几次,但在你面前看它有帮助。
在构造依赖图时使用Graphviz可能也很有用。这样你只需列出所有内容(在文本文件中)然后该工具将绘制(通常难看的)图片。 (这就是我为上述系统中的#include依赖所做的事情)
正如其他人已经建议的那样,编写单元测试是进入代码库的好方法。这种方法有许多优点:
当然,将单元测试添加到遗留代码并不总是一件容易的事。令人高兴的是,一位名叫Michael Feathers的绅士写了an excellent book on the subject,其中包括一些关于在没有单元测试的情况下向代码库添加测试的伟大“食谱”。
一些指示:
我还没看到三件事:
第一步应该是尝试阅读代码。尝试查看bug所在的代码。遵循从主要到该点的代码,并尝试看看可能出错的地方。阅读代码中的注释(如果有的话)。通常,函数名称很有用。了解每个功能的作用。 一旦了解了代码,就可以开始调试代码了。将断点放在您不理解代码或您认为错误的位置。逐行开始遵循代码。调试就像性。最初很痛苦,但慢慢开始享受它。
cscope + ctags可在Linux和Windows上使用(通过Cygwin)。如果你给他们机会,这些工具将成为你不可或缺的工具。虽然像Visual Studio这样的IDE在代码浏览设备方面也做得很好。
在像你这样的情况下,由于时间的限制,你会受到症状的驱使。我的意思是你没有时间重建大图/设计/架构。因此,您要专注于症状并向外工作,每次重建尽可能多的大局,以满足您对特定问题的需求。但是不要急于做出“本地”决定。有耐心看到做出高质量决定所需的大局。并且不要陷入创可贴综合症,即任何旧的修复都会起作用。您的工作是保留底层架构/设计(如果有的话,可以在任何程度上发现它)。
一开始这将是一场斗争,因为你的思想过度“狩猎”。但很快就会出现设计/架构中的主题,所有这些都将开始变得有意义。想想,不思考,grasshoppa :)
你必须有一个完全可靠的IDE,它有很多debbugging工具(断点,手表等)。熟悉大量代码的最佳方法是使用它来查看数据如何从一种方法传递到另一种方法。此外,您可以对代码进行反向工程,以便可以看到类的关系。 :D祝你好运!
对我来说,只有一种方法可以了解一个过程 - 互动。确定流程/系统的接口。然后确定输入/输出关系(这些步骤可能不是线性的)。一旦你这样做,你就可以开始修改代码,因为你知道它应该做什么,那么它只是找到“它是如何实际完成的”。但对我来说,了解系统的界面(不一定是用户界面)是关键。直言不讳 - 永远不要先触摸代码!