【问题标题】:Debugging LLVM IR调试 LLVM IR
【发布时间】:2013-05-28 18:19:25
【问题描述】:

我已经构建了一个以 LLVM 为目标的前端,它可以产生一些 IR。随后,完全可以预料,IR 输出在某些情况下是不正确的(例如,它看起来是正确的,但结果程序在执行时会崩溃)。但是,我还没有找到很多有用的工具来处理这个问题。

我尝试过使用 lli,但错误消息输出非常无用(当您假设解释器可以提供非常精确的错误详细信息时)。

我考虑将 IR 转换为 C 代码,然后使用 Visual Studio 进行调试,但似乎此功能已从 LLVM 中删除。

我还研究了如何处理 GDB。但是,DWARF 调试信息格式似乎对一些现有的语言非常特定,此外,我用我的前端翻译的源是正确的,它是生成的 IR 是错误的,所以原始源的调试符号不会'不要太有帮助-例如,我需要查看一堆中间寄存器值的值,这些值不对应于任何源变量,或者编译器生成的函数中的断点。

有哪些工具和技术可用于调试 LLVM IR 输出?

【问题讨论】:

    标签: llvm


    【解决方案1】:

    我不确定我是否完全理解您的问题。您是说您的编译器(从语言 X 到 LLVM IR)产生了不正确的输出(不正确的 LLVM IR)并且您不确定如何调试它?也就是说,有两种可能:

    1. 您的编译器生成的 IR 不正确 - 您可以指着一些指令说 - 这不是我想要生成的。
    2. IR 似乎正确,但没有产生我预期的结果。

    我假设它是 (1) 你在谈论(因为这是问题的意思,在你更新它之前)

    那么,这不是 LLVM 特有的问题。假设您正在编写从语言 X 到本机代码的编译器。生成的本机代码不正确 - 您如何调试问题?好吧,你调试你的编译器,很明显。您尝试找到编译器对输入的理解正确的最后一个位置,或者它变得不正确的第一个位置。你如何做到这一点在很大程度上取决于你的编译器的体系结构。但是,有很大帮助的是在编译器中拥有其他中间层的可打印表示。

    例如,Clang(从 C、C++ 和 Objective C 生成 LLVM IR)可以转储其完整的 AST。因此,查看 AST 中的错误代码可以将编译器减半,有助于确定问题出在前端(C 源 -> AST)还是代码生成(AST -> LLVM IR)。 LLVM 后端(将 LLVM IR 编译为本机代码)也有一些中间层(最显着的是 SelectionDAG 和 MI),可以为了调试而对其进行检查。这些只是其他现有编译器的示例,YMMV 和你的。

    【讨论】:

    • 呃,其实是#2。我现在可以毫无问题地赶上#1。该程序非常小,我目视检查了 IR 的大部分部分,它们似乎完全正确,但程序随后在运行时崩溃。我也验证了源程序是正确的。
    • @DeadMG:那么你必须问一个不同的问题。 “这是 LLVM IR 的一小部分,它不起作用”,看看是否有人可以提供帮助。除此之外,我认为它与lli 一起崩溃?你试过用llc 编译它,看看结果如何?使用模块验证通过opt 运行它?
    • lli 确实失败了(当然,还有一个非常无用的错误消息)。我已经使用llc 进行了编译,并验证了函数和整个模块。
    • @DeadMG:尽可能减少失败的 LLVM IR,并将其与您遇到的 lli 错误一起发布(以及确切的 lli 调用、版本等)
    • @DeadMG:顺便说一句,最近 LLVM 列表中有一些关于在 GDB 中调试 LLVM IR 的讨论 - 已经做了一些工作来启用它。不知道那是什么状态,你可能需要在列表档案中挖掘。
    【解决方案2】:

    Will Diez 描述了他是如何实现的:
    https://groups.google.com/d/msg/llvm-dev/O4Dj9FW1gtM/ovnm6dqoJJsJ

    大家好,

    出于我自己的目的,我写了一个通行证,完全符合你们的要求 描述:将调试元数据添加到 LLVM IR。

    作为pass,它必须解决“这个文件需要存在”的问题 在磁盘上某个地方,以便 gdb 可以找到它”,我解决了我的转储问题 到 /tmp/ 某处。不是一个很好的解决方案(谁删除了这些?)但是 工作得很好。

    另一个有趣的问题是如何与任何现有的调试共存 元数据,可用于同时调试 IR 使用 C 源代码进行内联转换以实现插桩式通行证 如 SAFECode、ASan/TSan。

    快速示例:

    (gdb) break main
    Breakpoint 1 at 0x4010b1: file
    /home/wdietz2/magic/test/unit/test_loop.c, line 9.
    (gdb) r
    Starting program:
    /home/wdietz2/llvm/32-obj-make/projects/magic/test/Output/test_loop
    
    Breakpoint 1, main (argc=<value optimized out>, argv=<value optimized
    out>) at /home/wdietz2/magic/test/unit/test_loop.c:9
    9         unsigned k = 0;
    Missing separate debuginfos, use: debuginfo-install
    glibc-2.12-1.80.el6_3.5.x86_64 libgcc-4.4.6-4.el6.x86_64
    libstdc++-4.4.6-4.el6.x86_64
    (gdb) n
    10        source(argc != 0, &k);
    (gdb) n
    14        %and.i.i.i.i104 = and i64 %4, 70368744177660
    (gdb) n
    15        %5 = load i8** @global, align 8
    (gdb) n
    18        store i32 16843009, i32* %6, align 1
    (gdb) n
    19        store i8 1, i8* getelementptr inbounds ([1 x i8]* @array,
    i64 0, i64 0), align 1
    (gdb) n
    20        call coldcc void @runtime_func() nounwind
    (gdb) n
    11        while(i-- > argc)
    (gdb) n
    23        %and.i.i.i.i85 = and i64 %7, 70368744177660
    (gdb) n
    14          while(j++ < i) k += j;
    (gdb) n
    11        while(i-- > argc)
    (gdb) n
    14          while(j++ < i) k += j;
    (gdb) n
    102       %77 = load i8** @global, align 8
    (gdb) n
    105       %79 = load i32* %78, align 4
    (gdb) n
    106       %cmp7.i.i.i = icmp ne i32 %79, 0
    (gdb) n
    108       call void @llvm.memset.p0i8.i64(i8* %add.ptr.i.i.i.i86, i8
    %conv8.i.i.i, i64 4, i32 1, i1 false) nounwind
    (gdb) n
    14          while(j++ < i) k += j;
    (gdb) n
    15          while(j-- > 0) k *= k + j;
    (gdb) n
    95        %69 = load i8** @global, align 8
    (gdb) n
    98        %71 = load i32* %70, align 4
    (gdb)
    

    pass 本身相当简单——它解决的难题是 将 IR 发送到磁盘并推断指令 * 上的内容 什么线,如果做得好,这真的不应该是一个问题 LLVM。如果需要,我当然可以根据要求提供代码。

    简而言之,它似乎对我很有效,并且在 LLVM 本身会很棒!

    不幸的是,代码似乎不可用。

    【讨论】:

      猜你喜欢
      • 2014-10-11
      • 1970-01-01
      • 2013-07-28
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 1970-01-01
      • 2017-04-09
      相关资源
      最近更新 更多