使用 LLDB 调试 V8

编译 V8

这里编译 debug 版本,我这里用的 V8 版本是 8.3.110.9 (8.3-lkgr)

$ v8gen.py x64.debug
$ ninja -C out.gn/x64.debug

如果对 V8 编译不熟悉可以参考我之前的文章《V8 编译踩坑》

d8 常用的运行参数

使用运行时函数进行调试

//test.js
var a = [1,2,3];
%DebugPrint(a); // 打印调试信息
%SystemBreak(); // 下断点
$ ./d8 test.js --allow-natives-syntax
DebugPrint: 0x466080c5879: [JSArray]
 - map: 0x046608281409 <Map(PACKED_SMI_ELEMENTS)> [FastProperties]
 - prototype: 0x046608248251 <JSArray[0]>
 - elements: 0x04660824ef2d <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
 - length: 3
 - properties: 0x0466080406e9 <FixedArray[0]> {
    #length: 0x0466081c0165 <AccessorInfo> (const accessor descriptor)
 }
 - elements: 0x04660824ef2d <FixedArray[3]> {
           0: 1
           1: 2
           2: 3
 }
0x46608281409: [Map]
 - type: JS_ARRAY_TYPE
 - instance size: 16
 - inobject properties: 0
 - elements kind: PACKED_SMI_ELEMENTS
 - unused property fields: 0
 - enum length: invalid
 - back pointer: 0x04660804030d <undefined>
 - prototype_validity cell: 0x0466081c0451 <Cell value= 1>
 - instance descriptors #1: 0x0466082488d9 <DescriptorArray[1]>
 - transitions #1: 0x0466082488f5 <TransitionArray[4]>Transition array #1:
     0x0466080425c9 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_SMI_ELEMENTS) -> 0x046608281481 <Map(HOLEY_SMI_ELEMENTS)>

 - prototype: 0x046608248251 <JSArray[0]>
 - constructor: 0x046608248125 <JSFunction Array (sfi = 0x466081cb321)>
 - dependent code: 0x0466080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
 - construction counter: 0

[1]    75260 trace trap  ./d8 test.js --allow-natives-syntax

查看 Ignition 生成的字节码

// test2.js
const a = [1,2,3]
const b = "123"
$ ./d8 test2.js --print-bytecode
[generated bytecode for function:  (0x0f0c0824eee9 <SharedFunctionInfo>)]
Parameter count 1
Register count 1
Frame size 8
         0xf0c0824ef6a @    0 : 7a 00 00 25       CreateArrayLiteral [0], [0], #37
         0xf0c0824ef6e @    4 : 1d 02             StaCurrentContextSlot [2]
         0xf0c0824ef70 @    6 : 12 01             LdaConstant [1]
         0xf0c0824ef72 @    8 : 1d 03             StaCurrentContextSlot [3]
         0xf0c0824ef74 @   10 : 0d                LdaUndefined
         0xf0c0824ef75 @   11 : aa                Return
Constant pool (size = 2)
0xf0c0824ef39: [FixedArray] in OldSpace
 - map: 0x0f0c080404b1 <Map>
 - length: 2
           0: 0x0f0c0824ef25 <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x0f0c0824ef11 <FixedArray[3]>>
           1: 0x0f0c0824eea5 <String[#3]: 123>
Handler Table (size = 0)
Source Position Table (size = 0)

可以看出变量 a 数组 [1,2,3] 存放在地址为 0xf0c0824ef39 的老生带里

跟踪 TurboFan 的优化操作

// test3.js
function add(a, b) {
  return a + b;
}

for (let i = 0; i < 10000; i++) {
  add(1,2)
}
$ ./d8 test3.js --trace-opt --trace-deopt
[marking 0x0e660824f011 <JSFunction (sfi = 0xe660824eef9)> for optimized recompilation, reason: small function]
[compiling method 0x0e660824f011 <JSFunction (sfi = 0xe660824eef9)> using TurboFan OSR]
[optimizing 0x0e660824f011 <JSFunction (sfi = 0xe660824eef9)> - took 10.606, 9.730, 0.427 ms]

我修改循环的最大值,发现直到 i > 10000 的时候 TurboFan 开始编译函数进行优化

配置 LLDB

在 V8 的源码里找到这个 plugin

$ ll | grep lldb
-rw-r--r--   1 Vincent  staff   3.9K May 14 14:42 lldb_commands.py

添加到 HOME 目录下 gdb 的启动脚本中

$ echo 'source /path/to/lldb_commands.py' >> ~/.gdbinit

使用 LLDB 进行调试

$ lldb ./d8 -- --allow-natives-syntax ./test.js
(lldb) target create "./d8"
Current executable set to './d8' (x86_64).
(lldb) settings set -- target.run-args  "--allow-natives-syntax" "./test.js"
(lldb) r
Process 99948 launched: '/Users/Vincent/Common/v8/out.gn/x64.debug/d8' (x86_64)
DebugPrint: 0x2e34080c5815: [JSArray]
...

找到断点处变量 a 的地址是 0x2e34080c5815,我们可以用刚才添加的 lldb_commands.py 插件中的 job 指令查看 JSArray 在内存中的数据结构

(lldb) job 0x2e34080c5815
0x2e34080c5815: [JSArray]
 - map: 0x2e3408281409 <Map(PACKED_SMI_ELEMENTS)> [FastProperties]
 - prototype: 0x2e3408248251 <JSArray[0]>
 - elements: 0x2e340824ef2d <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
 - length: 3
 - properties: 0x2e34080406e9 <FixedArray[0]> {
    #length: 0x2e34081c0165 <AccessorInfo> (const accessor descriptor)
 }
 - elements: 0x2e340824ef2d <FixedArray[3]> {
           0: 1
           1: 2
           2: 3
 }

我们发现数组的数据是存储在 FixedArray 对象中,继续查看一下

(lldb) job 0x2e340824ef2d
0x2e340824ef2d: [FixedArray] in OldSpace
 - map: 0x2e34080404d9 <Map>
 - length: 3
           0: 1
           1: 2
           2: 3

更多 V8 调试技巧后续更新