如何学习C调试和最佳实践

时间:2008-12-13 16:53:18

标签: c apache debugging gdb valgrind

我在C中编写了一个Apache模块。在某些条件下,我可以将它转换为段错误,但我不知道为什么。此时,它可能是我的代码,它可能是我编译程序的方式,或者它可能是OS库中的错误(在调用dlopen()期间发生了段错误。)

我尝试过运行GDB和Valgrind但没有成功。 GDB让我对dlopen()系统调用进行了回溯,看起来毫无意义。在Valgrind中,这个bug实际上似乎消失了,或者至少变得不可重复。另一方面,对于这些工具,我完全是新手。

我对生产质量C编程有点新兴(我多年前从C开始,但从未专业地使用它。)对于我学习调试程序的最佳方法是什么?我应该调查哪些其他工具?总之,您如何弄清楚如何应对新的bug挑战?

编辑: 为了澄清,我想感谢Sydius和dmckee的意见。我看了一下Apache的指南,并且对dlopen(以及dlsym和dlclose)非常熟悉。我的模块大部分工作(大约有3k行代码,只要我没有激活这一部分,事情似乎工作得很好。)

我想这是我原来的问题所在 - 我不知道接下来该做什么。我知道我没有充分发挥GDB和Valgrind的潜力。我知道我可能没有用正确的标志进行编译。但是我很难搞清楚。我可以找到初学者的指南,告诉我我已经知道的内容,以及那些告诉我超过我需要知道但没有指导的人工页。

7 个答案:

答案 0 :(得分:8)

此链接可能有助于:Apache Debugging Guide解决您的具体问题。具体问题的经验是在一般情况下变得更好的最好方法之一。

答案 1 :(得分:5)

不幸的是,GNU工具不是最好的,我的经验是动态链接器极大地混淆了水域。如果您可以让Apache静态链接到您的模块,这将使gdb尤其能够更可靠地执行。我不知道这有多容易;很大程度上取决于Apache构建系统。

令人担忧但令人震惊的是,你不能轻易地用valgrind重现这个错误。

关于使用正确的标志进行编译,如果使用-g -O0编译所有内容,valgrind和gdb都会为您提供更好的信息。不要相信gcc -g -O足够好的gcc手册页上的声明;它不是---偶数-O将导致优化器消除源代码中的变量。

答案 2 :(得分:3)

我确信调试技术通常与语言无关,并且没有这样的思考“C调试” 有很多不同的工具可以帮助您找到内存泄漏等简单问题,或者只是代码中的愚蠢错误,有时它甚至可以捕获简单的内存溢出。 但是对于真正难以发现的问题,例如源于多任务/中断的问题,dma内存损坏,唯一的工具就是你的大脑和编写良好的代码(事先考虑将调试此代码)。您可以找到有关准备代码以调试here的更多信息。从Sydius post开始,Apache已经有了良好的跟踪机制,所以只需使用它并在代码库中添加simalar。
另外我会说调试中的另一个重要步骤是“不要假设/思考”。将所有步骤基于事实,在基于该假设做出另一步之前,以100%准确度证明您的所有假设。根据假设进行调试通常会带来错误的方向。

在戴夫的澄清之后编辑:

下一步应该是找到导致问题的代码的最小部分。你很难过,如果你禁用某个部分,模块就会被加载。只需将此部分缩小即可,删除/移动部分中的所有内容,直到理想情况下找到一行会导致模块无法加载。在找到这条线之后。这将是开始使用你的大脑的确切时间:)只是不要忘记100%验证这是行

答案 3 :(得分:2)

非常一般的建议:

  • 再看看那个回溯。您控制的代码中是否有任何堆栈帧?如果是这样,什么线,以及那里发生了什么?

  • 您知道dlopen()的作用吗?如果没有阅读手册。如果回溯不包含任何代码,那么Apache尝试加载代码时可能会失败。您确定已使用正确的编译器选项构建模块吗?

  • 有效的调试需要了解您的环境和工具。 Sydius的建议很好。

  • 如果您遇到其他路径,请检查您是否可以编写,加载和运行一个简单的模块。您可能会在几乎所有关于该主题的文档中找到这样的示例。


对dave的澄清:在初学者和专家之间可能是个难点。

您是否在使用其他地方没有使用的违规代码中调用库?也许加载器路径只是为了那个资源搞砸了。

除此之外,我只是出于建议。遗憾。


注意:去年我有机会阅读David J. Agans的书Debugging。它不是特定于软件的,但如果您已经是一个非常好的调试器,那么它是一个很好的读取,并且有用甚至

答案 4 :(得分:2)

dlopen()调用失败的事实对我来说似乎有点怀疑。尝试打开共享对象时可能会出现许多问题;但是它们都不应该导致seg错误。

我能想到的一个例外是SO库初始化的问题。在此基础上,我会建议您尝试获取更多信息。

  • 检查您的库路径,并确保您尝试加载的库位于此路径中。 (注意:由于您使用的是Apache,我认为您还需要检查运行Apache的用户的库路径。(我认为用户是“nobody”。)我相信您正在寻找LD_LIBRARY_PATH环境变量。)另请注意,如果您有多个版本的库,这可能非常重要。确保您正在加载正确的库版本。
  • 作为一般调试原则,尝试简化问题。鉴于我对Apache模块知之甚少,我会尝试从等式中删除Apache:尝试编写一个简单的C程序,它只能执行dlopen()以及后续的dlsym(),然后退出。该程序提供了一个更简单的故障排除和/或调试环境。如果这个程序运行得很干净,那么当程序出错时,您可能需要更仔细地查看不同的程序。 (Apache的做法有何不同?)另一方面,如果您的程序也出现故障,您可能会考虑库的潜在问题,编译器切换程序以及程序中的代码。 (或以上所有。)

虽然我可能没有提供很多通用的调试技巧,但我希望这里的内容可能会有所帮助。

答案 5 :(得分:1)

我查看了valgrind文档,默认情况下它不会检查子进程。如果 Apache可以在子线程中运行您的模块,那就不会让我感到惊讶。请尝试

valgrind --trace-children=yes ....

答案 6 :(得分:1)

我的非CS学生(即电气工程,数学,物理学生)我在Kernighan的编程讲座“The Programming of Programming”中推荐。它很好地提供了一些有助于开发的基本概念(比如测试,现在它来了:调试)。

如果您已经是经验丰富的程序员,那对您来说可能太基础了。然后我再为你提供一句禅谚语:“没有通过经验过滤的智慧毫无价值”。

我只能回复一个答案:再看一下堆栈跟踪,这是调试最相关的帮助(特别是在边界,执行跨越不同的模块(特别是你的和lib / OS边界),并查看函数的参数并检查它们是否合理。)