热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何调试一个不太熟悉的代码库?-Howtoapproachdebuggingahugenotsofamiliarcodebase?

Seldomduringworkingonlargescaleprojects,suddenlyyouaremovedontoaprojectwhichisalrea

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上,所以对两者的建议都是有用的。

15 个解决方案

#1


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)来计算模块之间的关系,并能够以图形化的方式查看它们,这也可能是有帮助的。

#2


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可能也很有用。这样,你只需要列出所有的东西(在一个文本文件中),然后工具就会绘制(通常是难看的)图片。(这就是我在上面所做的,包括了上面系的依赖项)

#3


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:

正如其他人已经提出的,编写单元测试是进入代码库的一个很好的方法。这种方法有许多优点:

  1. 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.

    它允许您测试关于代码如何工作的假设。添加一个通过测试可以证明您对正在测试的那一小段代码的假设是正确的。您编写的通过测试越多,您就越能理解代码。

  2. 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.

    当您修复了这个错误并且您知道您已经成功时,您想要修复的错误的失败的单元测试将通过。

  3. The unit tests that you write act as documentation for the future.

    您编写的单元测试作为将来的文档。

  4. 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羽毛的绅士写了一本关于这个主题的优秀著作,其中包括一些伟大的“食谱”,在没有单元测试的情况下,将测试添加到代码库中。

WELC

#4


5  

Some pointers:

一些指针:

  1. Debug from the part which seems more relevant to the workflow.
  2. 从与工作流更相关的部分进行调试。
  3. Use debug strings
  4. 使用调试字符串
  5. Get appropriate .pdb and attach the core dump in debuggers like Windbg or debugdiag to analyze it.
  6. 获取适当的。pdb并在调试器中附加核心转储,如Windbg或debugdiag来分析它。
  7. Get a person's help in your organization who is good at debugging. Even if he is new to your codebase, he could be very helpful. I had prior experience. They would give you valuable pointers.
  8. 在您的组织中得到一个善于调试的人的帮助。即使他是你的代码库新手,他也会很有帮助。我有经验。他们会给你有价值的建议。
  9. Per Assaf Lavie's advice, you could use static code analyzers.
  10. 根据Assaf Lavie的建议,您可以使用静态代码分析器。
  11. The most important thing: as you explore and debug, document everything as you progress. At least the person succeeding you would suffer less.
  12. 最重要的事情:当您探索和调试时,在您进行过程中记录所有内容。至少接替你的人不会那么痛苦。

#5


3  

Three things i don't see yet:

有三件事我还没看到:

  1. write some unit tests which use the libraries/interfaces. demonstrate/verify your understanding of them and promote their maintainability.

    编写一些使用库/接口的单元测试。演示/验证您对它们的理解并提高它们的可维护性。

  2. 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:

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

    1. not commit their uses
    2. 不提交他们的使用
    3. commit their uses, converting them to 'real' assertions after a given period
    4. 提交它们的使用,在给定的时间段后将它们转换为“真正的”断言
    5. commit their uses, allowing another engineer (more familiar with the project) to dispose or promote them to real assertions
    6. 提交它们的使用,允许另一个工程师(更熟悉这个项目)处理或提升它们到真正的断言
  3. refactoring can also help. code that is difficult to read is an indication.

    重构可以帮助。难以阅读的代码是一种指示。

#6


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.

第一步应该尝试读取代码。尝试查看错误所在的代码。遵循代码从主到那个点,然后试着看看哪里出了问题。阅读代码中的注释(如果有的话)。通常,函数名是有用的。理解每个函数的作用。一旦您对代码有了一些了解,就可以开始调试代码了。把断点放在你不理解代码的地方或者你认为错误的地方。开始逐行跟踪代码。调试就像性。最初是痛苦的,但慢慢地你开始享受它。

#7


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 :)

当你的大脑过度“狩猎”时,一开始会是一场斗争。但不久,设计/架构中的主要主题将出现,所有这些都将开始变得有意义。想想,不要去想,蚱蜢:

#8


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:祝你好运!

#9


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!!!

对我来说,了解过程只有一种方法——交互。确定流程/系统的接口。然后确定输入/输出关系(这些步骤可能不是线性的)。一旦您这样做了,您就可以开始对代码进行大量的修改了,因为您知道它“应该做什么”,那么问题就在于找到“它实际上是如何做的”。对我来说,了解系统的界面(不一定是用户界面)是关键。坦率地说——永远不要接触代码!!!

#10


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.

一旦您的大部分代码(手工调试和修复太复杂的重要部分)都在自动的单元测试之下,您将获得回归测试的安全控制,这将使您在更改现有代码库时更加自信。

#11


1  

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

#12


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.

如果管理层建议我对一些我不熟悉的东西做一个大的改变,我要确保他们理解时间尺度,如果事情真的很混乱,就建议重写。

#13


1  

Usually the program in question will produce some kind of output ( log, console printout, dialog box ).

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

  1. Find the closest place to your problem in the program output
  2. 在程序输出中找到与您的问题最近的位置
  3. Search through the code base and look for the text in that output
  4. 搜索代码库并在输出中查找文本
  5. Start putting your own printouts, nothing fancy, just printf( "Calling xxx\n" );, so you can pinpoint exactly to the point where the problem starts.
  6. 开始放置您自己的打印输出,没有什么特别的,只是printf(“调用xxx\n”);这样您就可以精确地找到问题开始的地方。
  7. Once you pinpointed the problem spot, put a breakpoint
  8. 一旦确定了问题点,就设置一个断点
  9. When you hit the breakpoint, print a stacktrace
  10. 当您到达断点时,打印一个堆栈跟踪。

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.

如果消息来源的文档记录很糟糕,一旦你弄明白是怎么回事,不要害怕留下你的评论。如果程序设计允许它为您已经修复的问题创建一个单元测试。

#14


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:

谢谢你这么漂亮的回答,有很多问题需要考虑。我曾多次处理过这种情况,以下是我通常遵循的程序:

  1. Check the crash log or trace log. Check relevant trace if just a simple developer mistake if cannot evaluate in one go, then move on to 2.
  2. 检查崩溃日志或跟踪日志。检查相关跟踪如果只是一个简单的开发人员错误,如果不能一次性评估,那么继续到2。
  3. Reproduce the bug! This is the most important thing to do. Some bugs are rare to occur and if you get to reproduce the bug nothing like it. It means you have a better % of cracking it.
  4. 复制错误!这是最重要的事情。有些错误很少发生,如果你复制这个错误就不会发生。这意味着你有更好的百分比来破解它。
  5. If you cant reproduce a bug, find a alternative use case, situation where in you can actually reproduce the bug. Being able to actually debug a scenario is much more useful than just the crash log.
  6. 如果您不能复制错误,请找到一个替代的用例,在这种情况下,您实际上可以复制错误。能够实际调试场景要比只调试崩溃日志有用得多。
  7. Head to version control! Check if the same buggy behavior exists on previous few SW versions. If NOT..Voila! You can find between what two versions the bug got introduced and You can easily get the code difference of the two versions and target the relevant area.(Sometimes it is not the newly added code which has the bug but it exposes some old leftovers.Well, We atleast have a start I would say!)
  8. 头版本控制!检查在以前的几个版本中是否存在相同的错误行为。如果不是,瞧!您可以在两个版本之间找到bug,您可以轻松获得两个版本的代码差异,并针对相关区域。(有时并不是新添加的代码有bug,而是暴露了一些旧的遗留问题。好吧,我想我们至少有一个开始!
  9. Enable the debug traces. Run the use case of the bug, check if you can find some additional information useful for investigation.
  10. 启用调试痕迹。运行这个bug的用例,检查是否可以找到一些对调查有用的附加信息。
  11. Get hold of the relevant code area through the trace log. Check out there for some code introducing the bug.
  12. 通过跟踪日志获取相关代码区域。查看一些引入bug的代码。
  13. Put some breakpoints in the relevant code. Study the flow. Check the data flows.Lookout for pointers(usual culprits). Repeat till you get a hold of the flow.
  14. 在相关代码中添加一些断点。研究流。检查数据流。寻找指针(罪犯)。重复,直到你掌握了气流。
  15. If you have a SW version which does not reproduce the bug, compare what is different in the flows. Ask yourself, Whats the difference?
  16. 如果您有一个不再现错误的SW版本,请比较流中的不同之处。问问你自己,有什么不同?
  17. Still no Luck!- Arghh...My tricks have exhausted..Need to head the old way. Understand the code..and understand the code and understand it till you know what is happening in the code when that particular use case is being executed.
  18. 仍然没有运气!-是的…我的技巧已经用尽了. .需要走老路。理解代码. .理解代码并理解它,直到您知道在执行特定用例时代码中发生了什么。
  19. With newly developed understanding try debugging the code and sure the solution is around the corner.
  20. 有了新开发的理解,试着调试代码,确保解决方案就在眼前。
  21. Most important - Document the understanding you have developed about the module/s. Even small knitty gritty things. It is sure going to help you or someone just like you, someday..sometime!
  22. 最重要的是——记录你对模块/s的理解。甚至是小的针织品。总有一天,它一定会帮助你或像你一样的人。

#15


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/)。它会给你图形,在程序中绘制控制流。


推荐阅读
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 反向投影技术主要用于在大型输入图像中定位特定的小型模板图像。通过直方图对比,它能够识别出最匹配的区域或点,从而确定模板图像在输入图像中的位置。 ... [详细]
  • 本文探讨了如何通过预处理器开关选择不同的类实现,并解决在特定情况下遇到的链接器错误。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • 本文将介绍如何使用 Go 语言编写和运行一个简单的“Hello, World!”程序。内容涵盖开发环境配置、代码结构解析及执行步骤。 ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
author-avatar
mobiledu2502855913
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有