Seldom during working on large scale projects, suddenly you are moved on to a project which is already in maintainance phase.You end up with having a huge code C/C++ code base on your hands, with not much doccumentation about the design.The last person who could give you some knowledge transfer about the code has left the company already and to add to your horrors there is not enough time to get acquainted with the code and develop an understanding of the overall module/s.In this scenario when you are expected to fix bugs(core dumps,functionality,performance problems etc) on the module/s what is the approach that you will take?
在处理大型项目时,您很少会突然转向已经处于维护阶段的项目。最终,您将拥有一个巨大的代码C/ c++代码库,并没有太多的关于设计的文档。最后一个能够向您提供关于代码的知识转移的人已经离开了公司,为了增加您的恐惧,没有足够的时间来熟悉代码并对整个模块/s进行理解。在这个场景中,当您希望修复模块/s上的bug(核心转储、功能、性能问题等)时,您将采用什么方法?
So the question is: What are your usual steps for debugging a not so familiar C/C++ code base when trying to fix a bug?
所以问题是:当您试图修复一个bug时,调试一个不太熟悉的C/ c++代码库的通常步骤是什么?
EDIT: Enviornment is Linux, but code is ported on Windows too so suggestions for both will be helpful.
编辑:环境是Linux,但是代码也被移植到Windows上,所以对两者的建议都是有用的。
20
If possible, step through it from main() to the problematic area, and follow the execution path. Along the way you'll get a good idea of how the different parts play together.
如果可能的话,从main()逐步到有问题的区域,并遵循执行路径。在这个过程中,你会对不同的部分如何相互作用有一个很好的了解。
It could also be helpful to use a static code analysis tool, like CppDepends or even Doxygen, to figure out the relations between modules and be able to view them graphically.
使用一个静态代码分析工具(比如cppdepend,甚至是Doxygen)来计算模块之间的关系,并能够以图形化的方式查看它们,这也可能是有帮助的。
8
Use a pen and paper, or images/graphs/charts in general, to figure out which parts belong where and draw some arrows and so on.
使用钢笔和纸,或者图像/图形/图表,来找出哪些部分属于哪里,并画一些箭头等等。
This helps you build and see the image that will then be refined in your mind as you become more comfortable with it.
这将帮助你建立并看到一个形象,当你对它感到更舒适的时候,这个形象将在你的脑海中得到完善。
I used a similar approach attacking a hellish system that had 10 singletons all #including each other. I had to redraw it a few times in order to fit everything, but seeing it in front of you helps.
我使用了一种类似的方法来攻击一个有10个单例的地狱式系统。我不得不重新画了几次,以适应一切,但看到它在你面前是有帮助的。
It might also be useful to use Graphviz when constructing dependency graphs. That way you only have to list everything (in a text file) and then the tool will draw the (often unsightly) picture. (This is what I did for the #include dependencies in above syste,)
在构造依赖图时使用Graphviz可能也很有用。这样,你只需要列出所有的东西(在一个文本文件中),然后工具就会绘制(通常是难看的)图片。(这就是我在上面所做的,包括了上面系的依赖项)
6
As others have already suggested, writing unit-tests is a great way to get into the codebase. There are a number of advantages to this approach:
正如其他人已经提出的,编写单元测试是进入代码库的一个很好的方法。这种方法有许多优点:
It allows you to test your assumptions about how the code works. Adding a passing test proves that your assumptions about that small piece of code that you are testing are correct. The more passing tests you write, the better you understand the code.
它允许您测试关于代码如何工作的假设。添加一个通过测试可以证明您对正在测试的那一小段代码的假设是正确的。您编写的通过测试越多,您就越能理解代码。
A failing unit test that reproduces the bug you want to fix will pass when you fix the bug and you know that you have succeeded.
当您修复了这个错误并且您知道您已经成功时,您想要修复的错误的失败的单元测试将通过。
The unit tests that you write act as documentation for the future.
您编写的单元测试作为将来的文档。
The unit tests you write act as regression tests as more bugs are fixed.
您编写的单元测试作为回归测试,因为有更多的bug被修复。
Of course adding unit tests to legacy code is not always an easy task. Happily, a gentleman by the name of Michael Feathers has written an excellent book on the subject, which includes some great 'recipes' on adding tests to code bases without unit tests.
当然,向遗留代码添加单元测试并不总是一件容易的任务。令人高兴的是,一位名叫Michael羽毛的绅士写了一本关于这个主题的优秀著作,其中包括一些伟大的“食谱”,在没有单元测试的情况下,将测试添加到代码库中。
5
Some pointers:
一些指针:
3
Three things i don't see yet:
有三件事我还没看到:
write some unit tests which use the libraries/interfaces. demonstrate/verify your understanding of them and promote their maintainability.
编写一些使用库/接口的单元测试。演示/验证您对它们的理解并提高它们的可维护性。
sometimes it is nice to create an special assertion macro to check that the other engineer's assumptions are in line with yours. you could:
有时创建一个特殊的断言宏来检查其他工程师的假设是否与您的一致是很好的。你可以:
refactoring can also help. code that is difficult to read is an indication.
重构可以帮助。难以阅读的代码是一种指示。
2
The first step should be try to read the code. Try to see the code where the bug is. Follow the code from main to that point ans try to see what could be wrong. Read the comments from the code(if any). Normally the function names are useful. Understand what each function does.
Once you get some idea of the code then you can start debugging the code. Put breakpoints where you don't understand the code or where you think the error can be. Start following the code line by line. Debugging is like sex. Initially painful, but slowly you start to enjoy it.
第一步应该尝试读取代码。尝试查看错误所在的代码。遵循代码从主到那个点,然后试着看看哪里出了问题。阅读代码中的注释(如果有的话)。通常,函数名是有用的。理解每个函数的作用。一旦您对代码有了一些了解,就可以开始调试代码了。把断点放在你不理解代码的地方或者你认为错误的地方。开始逐行跟踪代码。调试就像性。最初是痛苦的,但慢慢地你开始享受它。
2
cscope + ctags are available on both Linux and Windows (via Cygwin). If you give them a chance, these tools will become indispensable to you. Although, IDEs like Visual Studio also do an excellent job with code browsing facilities as well.
cscope + ctags可以在Linux和Windows上(通过Cygwin)使用。如果你给他们一个机会,这些工具将成为你不可或缺的。尽管如此,ide如Visual Studio在代码浏览工具方面也做得很好。
In a situation like yours, because of time constraints, you are driven by symptoms. I mean that you don't have time to reconstruct the big picture / design / architecture. So you focus on the symptoms and work outwards, and each time reconstruct as much of the big picture as you need for that particular problem. But do not make "local" decisions in a hurry. Have the patience to see as much of the big picture as needed to make a good quality decision. And don't get caught in the band-aid syndrome i.e. put any old fix in that will work. It is your job to preserve the underlying architecture / design (if there is one, and to whatever extent that you can discover it).
在你这样的情况下,由于时间的限制,你会被症状所驱动。我的意思是你没有时间去重建大的图景/设计/架构。所以你关注症状并向外努力,每次重建你所需要的那个特定问题的大图景。但不要匆忙地做出“本地”决定。要有耐心,尽可能多地看到大局,以便做出高质量的决定。不要陷入创可贴综合症,也就是说,用旧的疗法治疗就可以了。您的工作是维护底层架构/设计(如果有的话,并且您可以在任何程度上发现它)。
It will be a struggle at first, as your mind "hunts" excessively. But soon the main themes in the design / architecture will emerge, and all of it will start to make sense. Think, by not thinking, grasshoppa :)
当你的大脑过度“狩猎”时,一开始会是一场斗争。但不久,设计/架构中的主要主题将出现,所有这些都将开始变得有意义。想想,不要去想,蚱蜢:
1
You have to have a fully reliable IDE which has a lot of debbugging tools (breakpoints, watches, and the like). The best way to familiarize yourself with a huge code is to play around with it and see how data is passed from one method to another. Also, you can reverse engineer the code so could see the relationship of the classes. :D Good Luck!
您必须有一个完全可靠的IDE,其中包含大量的漏洞工具(断点、手表等)。熟悉大型代码的最好方法是使用它,查看数据如何从一个方法传递到另一个方法。此外,您还可以反向设计代码,以便查看类的关系。D:祝你好运!
1
For me, there is only one way to get to know a process - Interaction. Identify the interfaces of the process/system. Then identify the input/output relationship (these steps maybe not linear). Once you do that, you can start tinkering at the code with a fair amount of confidence because you know what it is "supposed to do" then it's just a matter of finding out "how it is actually being done". For me though, getting to know the interface (Not necessarily the user interface) of the system is the key. To put it bluntly - Never touch the code first!!!
对我来说,了解过程只有一种方法——交互。确定流程/系统的接口。然后确定输入/输出关系(这些步骤可能不是线性的)。一旦您这样做了,您就可以开始对代码进行大量的修改了,因为您知道它“应该做什么”,那么问题就在于找到“它实际上是如何做的”。对我来说,了解系统的界面(不一定是用户界面)是关键。坦率地说——永远不要接触代码!!!
1
Not sure about C/C++, but coming from Java and C#, unit testing will help. In Java there's JUnit and TestNG libraries for unit testing, in C# there's NUnit and mstest. Not sure about C/C++.
不确定C/ c++,但是来自Java和c#,单元测试将会有所帮助。在Java中有用于单元测试的JUnit和TestNG库,在c#中有NUnit和mstest。对C / c++不确定。
Read the book 'Refactoring: Improving the Design of Existing Code' by Martin Fowler, Kent Beck, et al. Will be quite a few tips in there I'm sure that will help, and give you some guidance to improving the code.
阅读Martin Fowler、Kent Beck等人的《重构:改进现有代码的设计》一书,我相信这将会对您有所帮助,并为您提供一些改进代码的指导。
One tip: if it aint broke, don't fix it. Don't bother trying to fix some library or really complicated function if it works. Focus on parts where there's bugs.
一个提示:如果没有坏,不要去修理。不要费事去修复某个库,如果它可以工作的话,也不要去修复那些非常复杂的函数。关注有缺陷的部分。
Write a unit test to reproduce the scenario where the code should work. The test will fail at first. Fix the code until the unit test passes successfully. Repeat :)
编写一个单元测试来重现代码应该工作的场景。考试一开始就会不及格。修改代码直到单元测试成功通过。重复一遍:)
Once a majority of your code, the important bits that are too complex to manually debug and fix, is under automated unit tests, you'll have a safety harness of regression tests that'll make you feel more confident at changing the existing code base.
一旦您的大部分代码(手工调试和修复太复杂的重要部分)都在自动的单元测试之下,您将获得回归测试的安全控制,这将使您在更改现有代码库时更加自信。
1
while (!codeUnderstood)
{
Breakpoints();
Run();
StepInto();
if(needed)
{
StepOver();
}
}
1
I don't try to get an overview of the whole system as suggested by many here. If there is something which needs fixing I learn the smallest part of the code I can to fix the bug. The next time there is an issue I'm a little more familiar and a little less daunted and I learn a little more. Eventually I'm able to support the whole shebang.
我不像这里的许多人所建议的那样试图获得整个系统的概述。如果有什么需要修改的地方,我可以学习代码中最小的部分来修复bug。下次有问题的时候,我会更熟悉一些,也不会那么害怕,我也会学到更多。最终我能支撑起整个过程。
If management suggests I do a major change to something I'm not familiar with I make sure they understand the time scales and if things a really messy suggest a rewrite.
如果管理层建议我对一些我不熟悉的东西做一个大的改变,我要确保他们理解时间尺度,如果事情真的很混乱,就建议重写。
1
Usually the program in question will produce some kind of output ( log, console printout, dialog box ).
通常问题中的程序会产生某种输出(日志、控制台打印输出、对话框)。
printf( "Calling xxx\n" );
, so you can pinpoint exactly to the point where the problem starts.Now you can see what players you have and start the analysis of how you've got to the wrong place.
现在你可以看到你有哪些球员,并开始分析你是如何走错地方的。
Hopefully the names of the methods on the call stack are more meaningful than a, b and c ( seen this ), and there is some sort of comments, method documentation more meaningful than calling a
( seen this many times ).
希望调用堆栈上方法的名称比a、b和c更有意义(见过这个),并且有一些注释,方法文档比调用a更有意义(见过很多次)。
If the source is poorly documented, don't be afraid to leave your comments once you have figured out what's going on. If program design permits it create a unit test for the problem you've fixed.
如果消息来源的文档记录很糟糕,一旦你弄明白是怎么回事,不要害怕留下你的评论。如果程序设计允许它为您已经修复的问题创建一个单元测试。
1
Thanks for the nice answers, quite a number of points to take up. I have worked on such situation a number of times and here is the usual procedure i follow:
谢谢你这么漂亮的回答,有很多问题需要考虑。我曾多次处理过这种情况,以下是我通常遵循的程序:
0
You can try GNU cFlow tool (http://www.gnu.org/software/cflow/). It will give you graph, charting control flow within program.
您可以尝试GNU cFlow工具(http://www.gnu.org/software/cflow/)。它会给你图形,在程序中绘制控制流。