7

我用 C 编写了一个 Apache 模块。在某些条件下,我可以让它出现段错误,但我不知道为什么。此时,可能是我的代码,可能是我编译程序的方式,也可能是操作系统库中的错误(段错误发生在调用 dlopen() 期间)。

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

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

编辑:澄清一下,我要感谢 Sydius 和 dmckee 的意见。我看过 Apache 的指南,并且对 dlopen(以及 dlsym 和 dlclose)相当熟悉。我的模块大部分都可以工作(它大约有 3k 行代码,只要我不激活这一部分,一切似乎就可以正常工作。)

我想这就是我最初的问题的来源——我不知道下一步该做什么。我知道我没有充分利用 GDB 和 Valgrind。我知道我可能没有使用完全正确的标志进行编译。但我很难弄清楚更多。我可以找到告诉我已经知道的内容的初学者指南,以及告诉我比我需要知道的更多但没有指导的手册页。

4

6 回答 6

5

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

使用 valgrind 无法轻松重现该错误令人担忧,但并不令人震惊。

关于使用正确的标志进行编译,如果您使用-g -O0. 不要相信 gcc 手册页上的声明gcc -g -O已经足够好了;它不是——甚至-O会导致源代码中的变量被优化器消除。

于 2008-12-13T19:30:24.507 回答
3

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

戴夫澄清后编辑:

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

于 2008-12-13T19:02:35.453 回答
2

非常一般的建议:

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

  • 你知道做什么dlopen()吗?如果不看说明书。如果回溯不包括您的任何代码,那么在Apache尝试加载您的代码时这很可能会失败。您确定您已经使用正确的编译器选项构建了模块吗?

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

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


戴夫澄清说:初学者和专家之间可能是一个棘手的问题。

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

除此之外,我几乎没有建议。对不起。


注意:去年我有机会阅读 David J. Agans 的书Debugging。它不是特定于软件的,但它是一本很好的读物,并且即使您已经是一个非常好的调试器也很有帮助。

于 2008-12-13T18:25:18.600 回答
2

它在 dlopen() 调用上失败的事实对我来说似乎有点可疑。尝试打开共享对象时,有很多事情可能会出错。但它们都不应该导致段错误。

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

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

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

于 2008-12-13T21:57:44.770 回答
1

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

valgrind --trace-children=yes ....
于 2008-12-13T19:47:45.783 回答
1

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

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

我只能备份一个答案:再次查看堆栈跟踪,这是调试最相关的帮助(尤其是在边界处,执行跨越不同模块(尤其是您的和 lib/OS 边界),并查看函数的参数并检查它们是否正常)。

于 2008-12-13T20:18:50.587 回答