返回

使用 Python 辅助脚本调试本地可执行文件

除了 GDB 调试,您还可以使用 Python 辅助脚本 gdb-debughelpers.py 来调试 native-image 进程。GDB Python API 用于为调试原生可执行文件或共享库提供相当好的体验。它需要支持 Python 的 GDB。此调试扩展已针对 GDB 14.2 进行测试,并支持 GraalVM for JDK 17 及更高版本中引入的新调试信息生成。

注意:gdb-debughelpers.py 文件不适用于低于 14.2 版本的 gdb 或低于 GraalVM for JDK 17 的版本。

Python 脚本 gdb-debughelpers.py 可以在 <GRAALVM_HOME>/lib/svm/debug 目录中找到。如果启用了调试信息生成(请参阅构建包含调试信息的原生可执行文件),该脚本将复制到构建目录。native-image 工具会将调试部分 .debug_gdb_scripts 添加到调试信息文件,这使得 GDB 能够从当前工作目录自动加载 gdb-debughelpers.py

出于安全原因,GDB 首次遇到请求加载特定 Python 文件的原生可执行文件或共享库时,将打印一条警告。

warning: File "<CWD>/gdb-debughelpers.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".

To enable execution of this file add
        add-auto-load-safe-path <CWD>/gdb-debughelpers.py
line to your configuration file "<HOME>/.gdbinit".
To completely disable this security protection add
        add-auto-load-safe-path /
line to your configuration file "<HOME>/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"

要解决此问题,请将当前工作目录添加到 ~/.gdbinit,如下所示:

echo "add-auto-load-safe-path <CWD>/gdb-debughelpers.py" >> ~/.gdbinit

或者将路径作为命令行参数传递给 gdb

gdb -iex "set auto-load safe-path <CWD>/gdb-debughelpers.py" <binary-name>

这两种方法都使 GDB 能够从当前工作目录自动加载 gdb-debughelpers.py

自动加载是向 GDB 提供脚本的推荐方式。但是,也可以通过以下方式从 GDB 手动显式加载脚本:

(gdb) source gdb-debughelpers.py

美观打印支持

加载 gdb-debughelpers.py 会向 GDB 注册一个新的美观打印器,这为调试原生可执行文件或共享库增加了额外的便利。此美观打印器处理 Java 对象、数组、字符串和枚举的打印,用于调试原生可执行文件或共享库。如果 Java 应用程序使用 @CStruct@CPointer 注解来访问 C 数据结构,此美观打印器也将尝试像打印 Java 数据结构一样打印它们。如果 C 数据结构无法由美观打印器打印,则由 GDB 执行打印。

此美观打印器还会打印装箱原始类型(而不是 Java 对象)的原始值。

每当通过 print 命令的 p 别名进行打印时,美观打印器都会拦截该调用,以便对 Java 对象的相应运行时类型执行类型转换。这同样适用于使用 p 别名时的自动补全。这意味着,如果静态类型与运行时类型不同,print 命令将使用静态类型,这需要用户自行发现运行时类型并进行类型转换。此外,p 别名还支持 Java 对象的字段和数组访问以及函数调用。

限制

print 命令仍使用其默认实现,因为没有办法在保留默认 print 命令功能的同时覆盖它。覆盖会导致非 Java 对象的打印无法正常工作。因此,只有 print 命令的 p 别名被美观打印器覆盖,以便用户仍然可以使用默认的 GDB print 命令。

控制美观打印器行为的选项

除了增强的 p 别名,gdb-debughelpers.py 还引入了一些 GDB 参数来定制美观打印器的行为。GDB 中的参数可以通过 set <param> <value>show <param> 命令进行控制,从而与 GDB 的定制选项集成。

  • svm-print 开/关

使用此命令启用/禁用美观打印器。这也会将 print 命令的别名 p 重置为其默认行为。另外,可以通过 GDB print 命令的 raw 打印选项来抑制美观打印。

(gdb) show svm-print
The current value of 'svm-print' is "on".

(gdb) print str
$1 = "string"

(gdb) print/r str
$2 = (java.lang.String *) 0x7ffff689d2d0

(gdb) set svm-print off
1 printer disabled
1 of 2 printers enabled

(gdb) print str
$3 = (java.lang.String *) 0x7ffff689d2d0
  • svm-print-string-limit <整数>

自定义美观打印 Java 字符串的最大长度。默认值为 200。设置为 -1unlimited 以无限打印 Java 字符串。这不会更改 C 字符串的限制,C 字符串的限制可以通过 GDB 的 set print characters 命令控制。

  • svm-print-element-limit <整数>

自定义美观打印 Java 数组、ArrayList 和 HashMap 的最大元素数量。默认值为 10。设置为 -1unlimited 以打印无限数量的元素。这不会更改 C 数组的限制,C 数组的限制可以通过 GDB 的 set print elements 命令控制。但是,GDB 的参数 print elementssvm-print-element-limit 的上限。

  • svm-print-field-limit <整数>

自定义美观打印 Java 对象字段的最大元素数量。默认值为 50。设置为 -1unlimited 以打印无限数量的字段。GDB 的参数 print elementssvm-print-field-limit 的上限。

  • svm-print-depth-limit <整数>

自定义递归美观打印的最大深度。默认值为 1。将打印直接子级的子级(一个合理的默认值,使装箱值的内容可见)。设置为 -1unlimited 以打印无限深度。GDB 的参数 print max-depthsvm-print-depth-limit 的上限。

  • svm-use-hlrep 开/关

启用/禁用高级表示形式的美观打印。它提供了一种更数据导向的方式来查看一些具有已知内部结构的 Java 数据结构,例如列表或映射。目前支持 ArrayList 和 HashMap。

  • svm-infer-generics <整数>

自定义在推断高级表示形式的泛型类型时考虑的元素数量。默认值为 10。设置为 0 表示不推断泛型类型,设置为 -1unlimited 表示推断所有元素的泛型类型。

  • svm-print-address 绝对/开/关

除了常规美观打印外,启用/禁用地址的打印。当使用 absolute 模式时,即使是压缩引用也显示为绝对地址。默认情况下禁用地址打印。

  • svm-print-static-fields 开/关

启用/禁用 Java 对象的静态字段打印。默认情况下禁用静态字段打印。

  • svm-complete-static-variables 开/关

启用/禁用增强型 p 别名的静态字段成员自动补全。默认情况下启用静态字段自动补全。

  • svm-selfref-check 开/关

启用/禁用数据结构的自引用检查。美观打印器会检测自引用数据结构,并阻止进一步展开以避免无限递归。默认情况下启用自引用检查。为了测试,可以暂时禁用此功能(通常您不会这样做)。

联系我们