代码之家  ›  专栏  ›  技术社区  ›  dave mankoff

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

  •  7
  • dave mankoff  · 技术社区  · 17 年前

    我已经用C语言编写了一个Apache模块。在某些情况下,我可以将其发送到segfault,但我不知道为什么。此时,可能是我的代码,可能是我编译程序的方式,也可能是操作系统库中的错误(segfault发生在调用dlopen()期间)。

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

    我对生产质量的C编程有点陌生(我多年前开始使用C,但从未在专业领域使用过它)对于我来说,学习调试程序的诀窍最好的方法是什么?我还应该调查哪些其他工具?总之,您如何解决新的bug挑战?

    编辑: 我想澄清一下,我要感谢西迪厄斯和德姆奇的意见。我看过Apache指南,对dlopen(以及dlsym和dlclose)相当熟悉。我的模块大部分工作正常(大约有3k行代码,只要我不激活这一部分,一切似乎都很正常。)

    我想这就是我最初的问题的来源——我不知道下一步该怎么办。我知道我还没有充分利用GDB和Valgrind的潜力。我知道我可能没有使用正确的标志进行编译。但我想不出更多。我可以找到初学者指南,告诉我我已经知道的,和手册页,告诉我比我需要知道的更多,但没有指导。

    7 回复  |  直到 17 年前
        1
  •  5
  •   user JaredPar    12 年前

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

    令人担忧但并不令人震惊的是,你不能轻易地用valgrind复制这个bug。

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

        2
  •  3
  •   Community Mohan Dere    9 年前

    我敢肯定,调试技术在通用语言中是独立的,不存在“C调试”这样的想法。
    有很多不同的工具可以帮助你发现简单的问题,比如内存泄漏,或者只是代码中的愚蠢错误,有时甚至可以捕获简单的内存溢出。 但是,对于真正难以发现的问题,比如源于多任务/中断的问题,dma内存损坏唯一的工具是你的大脑和编写良好的代码(提前考虑这些代码将被调试)。您可以找到有关准备代码进行调试的更多信息 here .好像是从 Sydius post Apache已经有了一个很好的跟踪机制,所以只需使用它并将simalar添加到代码库中即可。
    另外,我想说调试中的另一个重要步骤是“不要假设/思考”。你的所有步骤都要以赤裸裸的事实为基础,在你根据假设采取下一步行动之前,要100%准确地证明你的所有假设。基于假设进行调试通常会导致错误的方向。

    在Dave澄清后编辑:

    下一步应该是找到导致问题的代码的最小部分。如果你禁用了某个部分,模块就会被加载。只需将该部分尽可能小,移除/熏制该部分中的所有内容,直到找到理想的一条线,使模块无法加载。找到这条线之后。这将是一个准确的时间开始使用你的大脑:)只是别忘了100%验证 这是电话线 .

        3
  •  2
  •   dmckee --- ex-moderator kitten    17 年前

    非常笼统的建议:

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

    • 你知道吗 dlopen() 做如果没有,请阅读手册。如果回溯不包含任何代码,那么这很可能是失败的 Apache 尝试加载你的代码。你确定你用正确的编译器选项构建了这个模块吗?

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

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


    对于dave的澄清: 初学者和专家之间可能是一个棘手的问题。

    你是在用你在其他地方不使用的代码调用库吗?也许加载程序的路径就因为这个资源而混乱了。

    除此之外,我几乎没有什么建议。很抱歉


    注:我有机会读了大卫·J·阿甘的书 Debugging 去年。它不是特定于软件的,但它是一本很好的读物,而且很有帮助 即使 如果你已经是一个很好的调试器。

        4
  •  2
  •   Jedidiah Thomet    17 年前

    它在dlopen()调用中失败的事实对我来说似乎有点可疑。尝试打开共享对象时,可能会出现很多问题;但没有一个 应该 导致seg故障。

    我能想到的一个例外是SO库初始化中的问题。在此基础上,我建议你做几件事来获取更多信息。

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

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

        5
  •  1
  •   Norman Ramsey    17 年前

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

    valgrind --trace-children=yes ....
    
        6
  •  1
  •   flolo    17 年前

    我在Kernighan的编程课程“编程实践”中推荐我们的非计算机专业学生(即电气工程、数学、物理专业的学生)。它很好地提供了一些帮助开发的基本概念(比如测试和调试)。

    如果你已经是有经验的程序员,这对你来说可能太基本了。然后我再给你们一句禅宗格言:“没有经验过滤的智慧是毫无价值的”。

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