LLVM 学习记录:调试技巧

Posted by 叉叉敌 on May 28, 2024

调试的时候有几个技巧,分享和记录下,方便自己后面可以熟练的用到。

看所有的编译命令,但是不执行

-###,这个就是 clang 的第一个参数。

https://clang.llvm.org/docs/CommandGuide/clang.html

用 clang 编译的时候,会从打印(但不运行)为此编译运行的命令。

然后打印出出错的命令,然后一步一步的执行,就知道是哪里的问题了。

比如:优化或者是定位哪一步的编译时间过长,是词法解析,还是编译器后端,还是 lld 导致的,用这个方法分别执行每一步的命令,然后统计下,就知道是哪里耗费的时间长。

llvm_debug 用法

将对应的实例以字符串的形式输入到 errs() 流中,但是类似这种调试信息,我们通常并不想在正式发布的时候打印,而且有时候 Pass 的代码一多,就会造成打印的信息过多,干扰我们调试。

https://zhqli.com/post/1667466024

借助 LLVM 的 LLVM_DEBUG 对调试信息进行分类,使用之前需要导入对应的头文件并定义 DEBUG_TYPE, 如下所示

在 llvm 代码里面可以看到 LLVM_DEBUG()的用法,这个用法是什么喃?

用的时候需要在 cpp 顶部定义各一个#incldue DEBUG_TYPE "pass_name"

举个例子

https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp

AMDGPU 的 calllowering 里面。

#define DEBUG_TYPE "amdgpu-call-lowering"

定义一个 debug_type 的,后面是 pass 的名字。如何使用, LLVM_DEBUG(dbgs())

// Make sure that they can fit on the caller's stack.
  const SIMachineFunctionInfo *FuncInfo = MF.getInfo<SIMachineFunctionInfo>();
  if (OutInfo.getStackSize() > FuncInfo->getBytesInStackArgArea()) {
    LLVM_DEBUG(dbgs() << "... Cannot fit call operands on caller's stack.\n");
    return false;
  }

这样 debug 的时候,只需要跟上这个 pass 的名字,就只打印出这个 pass 的相关的 log 信息了。定位就方便多了。

opt xxx --debug-only=amdgpu-call-lowering

统计运行时间

上面提到如何用###来查看命令,那么还有如何通过执行直接获取每一个的执行时间?

可以用这个命令通过 opt -time-trace 选项告诉 opt 导出所有由 TimeTraceaScope 收集的信息,并将 trace 文件保存到 -time-trace-file 指定的 json 文件中,这个 json 文件怎么查看呢?可以在 chrome 地址栏输入 chrome://tracing, 然后打开这个 json 文件即可可视化查看,是不是有点惊喜?

alt text

点击图片中第三个图标,就可以放大和缩小这个图片了,能看到具体是哪一个出了问题。

chrome 的 trace 查看功能实际上是集成了项目 perfetto, 这个项目可以在线使用,地址是 https://ui.perfetto.dev/.

那么 clang 里面用 -ftime-trace 也是可以用的`。

方便么?do you get it?

顺便推荐一本书,就是短时间内学习有用的 LLVM 技能,并使您能够快速构建原型和项目。编程语言爱好者还会发现本书对于在 LLVM 的帮助下构建新的编程语言很有用。 https://github.com/PacktPublishing/LLVM-Techniques-Tips-and-Best-Practices-Clang-and-Middle-End-Libraries

还有大佬翻译为中文了,https://github.com/xiaoweiChen/LLVM-Techniques-Tips-and-Best-Practies