调试LLVM IR

9
我制作了一个以LLVM为目标的前端,并生成了一些IR。随后,出现了一些IR输出不正确的情况(例如,看起来是正确的,但执行时导致程序崩溃)。然而,我没有找到很多有用的工具来解决这个问题。
我尝试使用lli,但输出的错误消息非常不明确(当你认为解释器可以提供非常精确的错误详情时)。
我尝试将IR转换为C代码,然后使用Visual Studio进行调试,但似乎LLVM已经删除了此功能。
我还研究了如何处理GDB。然而,DWARF调试信息格式似乎相当特定于一些现有语言,并且此外,我用我的前端正在翻译的源代码是正确的,错误在于生成的IR,因此原始源代码的调试符号并不太有用-例如,我需要查看许多中间寄存器值的值,这些值与任何源变量都不对应,或者在编译器生成的函数中断点。
有哪些工具和技术可用于调试LLVM IR输出?
2个回答

5
我不确定我完全理解你的问题。你是说你的编译器(从语言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和MIs),可以用于调试。这些只是其他现有编译器的示例,你的情况可能会有所不同。

1
额,实际上是第二个问题。我现在可以很容易地解决第一个问题。这个程序非常小,我已经目测了红外线的大部分部件,它们似乎都是完全正确的,但是程序在运行时崩溃了。我还验证了源程序的正确性。 - Puppy
@DeadMG:那么你需要提出一个不同的问题。"这里有一小段LLVM IR代码,但它不能工作",看看是否有人可以帮忙。除此之外,我假设它会在lli中崩溃?你尝试使用llc编译它并查看输出了吗?通过模块验证运行它通过opt - Eli Bendersky
这实际上解决了我的问题。问题在于我链接到的一些其他代码,我认为它是有效的,但事实并非如此。我恰好在删除它时注意到了,因为我要发布的示例中没有它。这只是表明,那样的假设是不好的。 - Puppy
@DeadMG:是的,又一个橡皮鸭调试的好例子 :-) - Eli Bendersky
3
顺便提一句,最近LLVM列表中有关于在GDB中调试LLVM IR的讨论 - 已经进行了一些工作以实现这个功能。不确定现在的进展如何,您可能需要查看列表存档。 - Eli Bendersky
显示剩余2条评论

2

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

Hi all,

For my own purposes, I wrote a pass that does exactly what you all are describing: add debug metadata to LLVM IR.

As a pass, it had to tackle the problem of "This file needs to exist on disk somewhere so gdb can find it", which I solved my dumping it onto /tmp/ somewhere. Not a great solution (who deletes these?) but worked well enough.

Another interesting issue is how to coexist with any existing debug metadata, which can be useful for simultaneously debugging an IR transform inline with the C source for instrumentation-style passes like SAFECode, ASan/TSan.

Quick Example:

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

The pass itself is rather simple--the hard problem it solves is emitting the IR to disk and reasoning about what Instruction* is on what line, which really shouldn't be a problem if done properly in LLVM. If desired I can certainly make the code available on request.

In short, it seemed to work well for me and having it done properly in LLVM itself would be great!

很遗憾,代码似乎不可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接