使用 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 调试技巧后续更新