v8 debug writeup

v8 hidden class test

map

test1

1
2
3
4
5
6
7
8
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(10, 11);
%DebugPrint(p1)
var p2 = new Point(12, 13);
%DebugPrint(p2)

./d8 --allow_natives_syntax test_hidden_class.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
sakura@ubuntu:~/v8/v8/out.gn/x64.debug$ ./d8 --allow_natives_syntax test.js 
DebugPrint: 0x27984250c879: [JS_OBJECT_TYPE]
- map = 0x1e016e18f8d9 [FastProperties]
- prototype = 0x27984250c749
- elements = 0x23ac56e02241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- properties = 0x23ac56e02241 <FixedArray[0]> {
#x: 10 (data field 0)
#y: 11 (data field 1)
}
0x1e016e18f8d9: [Map]
- type: JS_OBJECT_TYPE
- instance size: 104
- inobject properties: 10
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 8
- enum length: invalid
- stable_map
- back pointer: 0x1e016e18f881 <Map(FAST_HOLEY_ELEMENTS)>
- instance descriptors (own) #2: 0x27984250c919 <FixedArray[8]>
- layout descriptor: 0
- prototype: 0x27984250c749 <Object map = 0x1e016e18f829>
- constructor: 0x2672a3c30041 <JSFunction Point (sfi = 0x2672a3c2fc91)>
- code cache: 0x23ac56e02241 <FixedArray[0]>
- dependent code: 0x23ac56e02241 <FixedArray[0]>
- construction counter: 6

DebugPrint: 0x27984250c969: [JS_OBJECT_TYPE]
- map = 0x1e016e18f8d9 [FastProperties]
- prototype = 0x27984250c749
- elements = 0x23ac56e02241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- properties = 0x23ac56e02241 <FixedArray[0]> {
#x: 12 (data field 0)
#y: 13 (data field 1)
}
0x1e016e18f8d9: [Map]
- type: JS_OBJECT_TYPE
- instance size: 104
- inobject properties: 10
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 8
- enum length: invalid
- stable_map
- back pointer: 0x1e016e18f881 <Map(FAST_HOLEY_ELEMENTS)>
- instance descriptors (own) #2: 0x27984250c919 <FixedArray[8]>
- layout descriptor: 0
- prototype: 0x27984250c749 <Object map = 0x1e016e18f931>
- constructor: 0x2672a3c30041 <JSFunction Point (sfi = 0x2672a3c2fc91)>
- code cache: 0x23ac56e02241 <FixedArray[0]>
- dependent code: 0x23ac56e02241 <FixedArray[0]>
- construction counter: 6

结论:相同的map值

test2

add p2.z = 14;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
sakura@ubuntu:~/v8/v8/out.gn/x64.debug$ ./d8 --allow_natives_syntax test.js 
DebugPrint: 0x252f4f50c891: [JS_OBJECT_TYPE]
- map = 0x2d1249b8f8d9 [FastProperties]
- prototype = 0x252f4f50c761
- elements = 0x538c1f82241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- properties = 0x538c1f82241 <FixedArray[0]> {
#x: 10 (data field 0)
#y: 11 (data field 1)
}
0x2d1249b8f8d9: [Map]
- type: JS_OBJECT_TYPE
- instance size: 104
- inobject properties: 10
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 8
- enum length: invalid
- stable_map
- back pointer: 0x2d1249b8f881 <Map(FAST_HOLEY_ELEMENTS)>
- instance descriptors (own) #2: 0x252f4f50c931 <FixedArray[8]>
- layout descriptor: 0
- prototype: 0x252f4f50c761 <Object map = 0x2d1249b8f829>
- constructor: 0x3be1e3530089 <JSFunction Point (sfi = 0x3be1e352fc91)>
- code cache: 0x538c1f82241 <FixedArray[0]>
- dependent code: 0x538c1f82241 <FixedArray[0]>
- construction counter: 6

DebugPrint: 0x252f4f50c981: [JS_OBJECT_TYPE]
- map = 0x2d1249b8f8d9 [FastProperties]
- prototype = 0x252f4f50c761
- elements = 0x538c1f82241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- properties = 0x538c1f82241 <FixedArray[0]> {
#x: 12 (data field 0)
#y: 13 (data field 1)
}
0x2d1249b8f8d9: [Map]
- type: JS_OBJECT_TYPE
- instance size: 104
- inobject properties: 10
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 8
- enum length: invalid
- stable_map
- back pointer: 0x2d1249b8f881 <Map(FAST_HOLEY_ELEMENTS)>
- instance descriptors (own) #2: 0x252f4f50c931 <FixedArray[8]>
- layout descriptor: 0
- prototype: 0x252f4f50c761 <Object map = 0x2d1249b8f931>
- constructor: 0x3be1e3530089 <JSFunction Point (sfi = 0x3be1e352fc91)>
- code cache: 0x538c1f82241 <FixedArray[0]>
- dependent code: 0x538c1f82241 <FixedArray[0]>
- construction counter: 6

DebugPrint: 0x252f4f50c981: [JS_OBJECT_TYPE]
- map = 0x2d1249b8f989 [FastProperties]
- prototype = 0x252f4f50c761
- elements = 0x538c1f82241 <FixedArray[0]> [FAST_HOLEY_ELEMENTS]
- properties = 0x538c1f82241 <FixedArray[0]> {
#x: 12 (data field 0)
#y: 13 (data field 1)
#z: 14 (data field 2)
}
0x2d1249b8f989: [Map]
- type: JS_OBJECT_TYPE
- instance size: 104
- inobject properties: 10
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 7
- enum length: invalid
- stable_map
- back pointer: 0x2d1249b8f8d9 <Map(FAST_HOLEY_ELEMENTS)>
- instance descriptors (own) #3: 0x252f4f50ca51 <FixedArray[11]>
- layout descriptor: 0
- prototype: 0x252f4f50c761 <Object map = 0x2d1249b8f931>
- constructor: 0x3be1e3530089 <JSFunction Point (sfi = 0x3be1e352fc91)>
- code cache: 0x538c1f82241 <FixedArray[0]>
- dependent code: 0x538c1f82241 <FixedArray[0]>
- construction counter: 6

结论:指向一个新的map

property的管理方法

In-Object Properties

1
2
3
4
5
6
7
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(10, 11);
%DebugPrint(p1)
while(1);

V8支持所谓的对象内属性,它们被直接保存在对象上,和对象在同一块内存区域。这种属性是V8中速度最快的属性,能够被直接访问。对象内属性的个数由对象初始化大小决定。如果要添加的属性超出对象的大小,这些属性就会被放入properties store中。properties store会增加一层属性访问的消耗,但是不受大小的限制。

Fast Properties

一般来说,我们会把属性保存在线性的properties store中,作为快速属性。快速属性可以通过简单的索引在properties store中访问。不过为了从属性名从properties store获取实际位置,我们要去HiddenClass上的 descriptor array中查询 。具体来讲,这一步骤是先根据属性名搜寻隐藏类的descriptor array,然后得到到数组的偏移地址,然后根据偏移地址到properties store上读取属性信息。实际上,第一次查找属性的时候难免会经历一次隐藏类的哈希查找,但下次一般会结合内联缓存,直接采用缓存的位移来存取属性。

Slow Properties

  然而,若是有很多属性从对象上添加或者删除,就需要花费很多时间和内存来维护descriptor array以及隐藏类。因此,V8也支持所谓的慢属性。拥有慢属性的对象会拥有一个自给的字典作为它的properties store(如上图所示)。这种情况下,所有属性的元信息不再储存在隐藏类的descriptor array中,而是直接保存在属性字典上。因此,此时属性的添加和移除不用再去更新指向的隐藏类。因为内联缓存不适用保存在字典中的属性,所以慢属性一般要比快属性慢。

总结

列表内容有三种不同类型的命名属性:对象内属性,快属性,以及慢/字典属性。
  1. 对象内属性直接储存在对象自身上,提供最快的属性访问。
  2. 快属性活跃在properties store上,所有相关的元信息都在隐藏类的descriptor array中。
  3. 慢属性保存在一个自给的属性字典上,属性的元信息不再与隐藏类有关。
慢属性提供高效的属性添加删除,但是访问速度要慢于快速属性和对象内对象。

Elements or array-indexed Properties

  目前为止我们介绍了命名属性并且忽略了通常出现在数组中的整数索引属性。处理这种整数型属性要比命名属性简单很多。
所有的整数型属性都被单独保存在一个elements store中,并且有多达20种元素类型。

Packed or Holey Elements

1
2
3
4
5
6
7
8
9
10
11
12
const o = ["a", "b", "c"];
print(o[1]); // Prints "b".
%DebugPrint(o)
delete o[1]; // Introduces a hole in the elements store.
%DebugPrint(o)
print(o[1]); // Prints "undefined"; property 1 does not exist.
o.__proto__ = {1: "B"}; // Define property 1 on the prototype.
%DebugPrint(o)
print(o[0]); // Prints "a".
print(o[1]); // Prints "B".
print(o[2]); // Prints "c".
print(o[3]); // Prints undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
parallels@ubuntu:~/v8/v8/out.gn/x64.debug$ ./d8 --allow_natives_syntax test.js 
b
DebugPrint: 0xf279b90c8e9: [JSArray]
- map = 0x236e9503b71 [FastProperties]
- prototype = 0x92dae107f51
- elements = 0xf279b90c861 <FixedArray[3]> [FAST_ELEMENTS (COW)]
- length = 3
- properties = 0x31013b402241 <FixedArray[0]> {
#length: 0x37e7c51b6b51 <AccessorInfo> (const accessor descriptor)
}
- elements = 0xf279b90c861 <FixedArray[3]> {
0: 0x31013b408171 <String[1]: a>
1: 0x31013b408191 <String[1]: b>
2: 0x31013b4081b1 <String[1]: c>
}
0x236e9503b71: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: FAST_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x236e9503d29 <Map(FAST_HOLEY_DOUBLE_ELEMENTS)>
- instance descriptors #1: 0x92dae108a11 <FixedArray[5]>
- layout descriptor: 0
- transitions #1: 0x92dae108a81 <TransitionArray[5]>
0x31013b404069 <Symbol: elements_transition_symbol>: (transition to FAST_HOLEY_ELEMENTS) -> 0x236e9503e31 <Map(FAST_HOLEY_ELEMENTS)>
- prototype: 0x92dae107f51 <JSArray[0]>
- constructor: 0x92dae107f71 <JSFunction Array (sfi = 0x31013b43d6c1)>
- code cache: 0x31013b402241 <FixedArray[0]>
- dependent code: 0x31013b402241 <FixedArray[0]>
- construction counter: 0

DebugPrint: 0xf279b90c8e9: [JSArray]
- map = 0x236e9503e31 [FastProperties]
- prototype = 0x92dae107f51
- elements = 0xf279b90c919 <FixedArray[3]> [FAST_HOLEY_ELEMENTS]
- length = 3
- properties = 0x31013b402241 <FixedArray[0]> {
#length: 0x37e7c51b6b51 <AccessorInfo> (const accessor descriptor)
}
- elements = 0xf279b90c919 <FixedArray[3]> {
0: 0x31013b408171 <String[1]: a>
1: 0x31013b402351 <the_hole>
2: 0x31013b4081b1 <String[1]: c>
}
0x236e9503e31: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x236e9503b71 <Map(FAST_ELEMENTS)>
- instance descriptors (own) #1: 0x92dae108a11 <FixedArray[5]>
- layout descriptor: 0
- prototype: 0x92dae107f51 <JSArray[0]>
- constructor: 0x92dae107f71 <JSFunction Array (sfi = 0x31013b43d6c1)>
- code cache: 0x31013b402241 <FixedArray[0]>
- dependent code: 0x31013b402241 <FixedArray[0]>
- construction counter: 0

undefined
DebugPrint: 0xf279b90c8e9: [JSArray]
- map = 0x236e950f779 [FastProperties]
- prototype = 0xf279b90c9e9
- elements = 0xf279b90c919 <FixedArray[3]> [FAST_HOLEY_ELEMENTS]
- length = 3
- properties = 0x31013b402241 <FixedArray[0]> {
#length: 0x37e7c51b6b51 <AccessorInfo> (const accessor descriptor)
}
- elements = 0xf279b90c919 <FixedArray[3]> {
0: 0x31013b408171 <String[1]: a>
1: 0x31013b402351 <the_hole>
2: 0x31013b4081b1 <String[1]: c>
}
0x236e950f779: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x31013b402311 <undefined>
- instance descriptors (own) #1: 0xf279b90cad9 <FixedArray[5]>
- layout descriptor: 0
- prototype: 0xf279b90c9e9 <Object map = 0x236e950f7d1>
- constructor: 0x92dae107f71 <JSFunction Array (sfi = 0x31013b43d6c1)>
- code cache: 0x31013b402241 <FixedArray[0]>
- dependent code: 0x31013b402241 <FixedArray[0]>
- construction counter: 0

a
B
c
undefined


  简言之,如果属性在接收者上找不到(接受者可以理解为要访问的属性或者方法指向的那个对象),那么就会继续到原型链上找。
这些元素是自给自足的,换言之,我们不需要在隐藏类上储存这些索引属性。另外,我们需要一个特殊的值,称之为空洞,来标记那些不存在属性。这点对于数组方法的性能很关键。
若是我们知道elements store没有空洞,是被填满的,我们可以提高本地操作(指无需原型链参与)的性能,不需要再去花费昂贵的代价查找原型链。

Fast or Dictionary Elements

  第二个关于元素的主要的区分为是否是快速或是字典模式。快速元素就是VM内部简单的将数组索引与elements store中的索引映射。
然而,这种简单表示,在那种有很大的空洞以及很少位置被占用的数组上是相当浪费的。在这种情况下,我们将转换成字典模式,这会减小内存但轻微的牺牲性能。

1
2
const sparseArray = [];
sparseArray[9999] = "foo"; // Creates an array with dictionary elements.
1
2
3
4
5
6
7
8
9
10
11
12
13
gdb-peda$ r --allow_natives_syntax test.js 
Starting program: /home/parallels/v8/v8/out.gn/x64.debug/d8 --allow_natives_syntax test.js
DebugPrint: 0x6ed6db8c769: [JSArray]
- map = 0x16a58d0f779 [FastProperties]
- prototype = 0x37db83507f51
- elements = 0x6ed6db8c799 <FixedArray[17]> [DICTIONARY_ELEMENTS]
- length = 10000
- properties = 0x38a1eb682241 <FixedArray[0]> {
#length: 0x3529df1b6b51 <AccessorInfo> (const accessor descriptor)
}
- elements = 0x6ed6db8c799 <FixedArray[17]> {
9999: 0x37db8352faa9 <String[3]: foo> (data, dict_index: 0, attrs: [WEC])
}


0x270F=9999

  在这个例子中,如果给这个数组分配完整的10k个空间将会造成极大浪费。而实际上V8是创建了一个key-value-descriptor的triplets。这个例子中的key是“9999”,value是“foo”,而descriptor是使用的默认值。

1
2
3
4
const array = [];
Object.defineProperty(array, 0, {value: "fixed", configurable: false});
%DebugPrint(array)
while(1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
gdb-peda$ r --allow_natives_syntax test.js 
Starting program: /home/parallels/v8/v8/out.gn/x64.debug/d8 --allow_natives_syntax test.js
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff43f1700 (LWP 18068)]
DebugPrint: 0x28f5f190c759: [JSArray]
- map = 0x1e39b868f881 [FastProperties]
- prototype = 0x61172b07f51
- elements = 0x28f5f190c849 <FixedArray[17]> [DICTIONARY_ELEMENTS]
- length = 1
- properties = 0x3c90a5102241 <FixedArray[0]> {
#length: 0x2a07bddb6b51 <AccessorInfo> (const accessor descriptor)
}
- elements = 0x28f5f190c849 <FixedArray[17]> {
0: 0x3c90a5111569 <String[5]: fixed> (data, dict_index: 0, attrs: [___])
}
0x1e39b868f881: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: DICTIONARY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x1e39b8683e31 <Map(FAST_HOLEY_ELEMENTS)>
- instance descriptors (own) #1: 0x61172b08a11 <FixedArray[5]>
- layout descriptor: 0
- prototype: 0x61172b07f51 <JSArray[0]>
- constructor: 0x61172b07f71 <JSFunction Array (sfi = 0x3c90a513d6c1)>
- code cache: 0x3c90a5102241 <FixedArray[0]>
- dependent code: 0x3c90a5102241 <FixedArray[0]>
- construction counter: 0


  在上面例子中,我们给数组添加了一个non-configurable属性。这个信息就会被保存慢元素字典的triplet中的descriptor 部分。需要切记一点,数组方法在那些拥有慢元素的对象上的性能会相当慢。

The ElementsAccessor

你可以想到开发人员根本不愿意为这20种元素在C++中对应重复写20遍数组方法。这里就是体现C++神奇的地方了。为了不实现数组方法一遍又一遍,我们建立了一个属性存取器(ElementsAccessor),在它里面大部分都仅仅是简单的属性访问相关的方法。这个属性访问其依赖C++中的CRTP来实现不同种类需求的数组方法。所以,有时如果我们调用例如数组的slice,V8会调用C++中的代码,然后通过属性存取器来选择所需函数,如slice的专门版本。

学习v8字节码

阅读和打印(只是个例子)

./d8 --print-bytecode test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function incrementX(obj) {
return 1 + obj.x;
}

incrementX({x: 42});
....
....
$ node --print-bytecode incrementX.js
...
[generating bytecode for function: incrementX]
Parameter count 2
Frame size 8
12 E> 0x2ddf8802cf6e @ StackCheck
19 S> 0x2ddf8802cf6f @ LdaSmi [1]
0x2ddf8802cf71 @ Star r0
34 E> 0x2ddf8802cf73 @ LdaNamedProperty a0, [0], [4]
28 E> 0x2ddf8802cf77 @ Add r0, [6]
36 S> 0x2ddf8802cf7a @ Return
Constant pool (size = 1)
0x2ddf8802cf21: [FixedArray] in OldSpace
- map = 0x2ddfb2d02309 <Map(HOLEY_ELEMENTS)>
- length: 1
0: 0x2ddf8db91611 <String[1]: x>
Handler Table (size = 16)

  • LdaSmi [1]
    LdaSmi [1]将常量 1 加载到累加器中。
  • Star r0
    接下来,Star r0 将当前在累加器中的值 1 存储在寄存器 r0 中。
  • LdaNamedProperty a0, [0], [4]
    LdaNamedProperty 将 a0 的命名属性加载到累加器中。ai 指向 incrementX() 的第 i 个参数。在这个例子中,我们在 a0 上查找一个命名属性,这是 incrementX() 的第一个参数。该属性名由常量 0 确定。LdaNamedProperty 使用 0 在单独的表中查找名称:
    1
    2
    - length: 1
    0: 0x2ddf8db91611 <String[1]: x>

可以看到,0 映射到了 x。因此这行字节码的意思是加载 obj.x。
那么值为 4 的操作数是干什么的呢? 它是函数 incrementX() 的反馈向量的索引。反馈向量包含用于性能优化的 runtime 信息。
现在寄存器看起来是这样的:

  • Add r0, [6]
    最后一条指令将 r0 加到累加器,结果是 43。 6 是反馈向量的另一个索引。

  • Return
    Return 返回累加器中的值。返回语句是函数 incrementX() 的结束。此时 incrementX() 的调用者可以在累加器中获得值 43,并可以进一步处理此值。

乍一看,V8 的字节码看起来非常奇怪,特别是当我们打印出所有的额外信息。但是一旦你知道 Ignition 是一个带有累加器寄存器的寄存器,你就可以分析出大多数字节码都干了什么。

bytecode如何产生

  • 加法
  • 访问对象

    编译bytecode

v8自带的gdb命令

在/tools中可以找到gdbinit和gdb-v8-support.py
我将其都复制到调试路径下

1
2
3
parallels@ubuntu:~/v8/v8/out.gn/x64.debug$ ls | grep gdb
gdbinit
gdb-v8-support.py

对如下代码进行测试

1
2
var a=[0xdeadbee,0xdeadbeef,"hoge"];
while(1);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
gdb-peda$ source gdbinit
gdb-peda$ source gdb-v8-support.py
gdb-peda$ find 0xdeadbee
Searching for '0xdeadbee' in: None ranges
Found 1 results, display max 1 items:
mapped : 0x5e88e28c6c4 --> 0x7daafaa10deadbee

gdb-peda$ v8print 0x5e88e28c6c4-0x14+1
0x5e88e28c6b1: [FixedArray]
- map = 0x2702dbb82679 <Map(FAST_HOLEY_ELEMENTS)>
- length: 3
0: 233495534
1: 0x318d7daafaa1 <Number 3.73593e+09>
2: 0x318d7daafa81 <String[4]: hoge>
gdb-peda$ v8print *(void **)(0x5e88e28c6c4-0x14)
0x2702dbb82679: [Map]
- type: FIXED_ARRAY_TYPE
- instance size: 0
- elements kind: FAST_HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x504a7702311 <undefined>
- instance descriptors (own) #0: 0x504a7702231 <FixedArray[0]>
- layout descriptor: 0
- prototype: 0x504a7702201 <null>
- constructor: 0x504a7702201 <null>
- code cache: 0x504a7702241 <FixedArray[0]>
- dependent code: 0x504a7702241 <FixedArray[0]>
- construction counter: 0

JIT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
parallels@ubuntu:~/v8/v8/out.gn/x64.debug$ ./d8 --help |grep print
--print_bytecode (print bytecode generated by ignition interpreter)
--print_bytecode_filter (filter for selecting which functions to print bytecode)
--hydrogen_stats (print statistics for hydrogen)
--print_deopt_stress (print number of possible deopt points)
--turbo_stats (print TurboFan statistics)
--turbo_stats_nvp (print TurboFan statistics in machine-readable format)
--print_wasm_code (Print WebAssembly code)
--trace_gc (print one trace line following each garbage collection)
--trace_gc_nvp (print one detailed trace line in name=value format after each garbage collection)
--trace_gc_ignore_scavenger (do not print trace line after scavenger collection)
--trace_idle_notification (print one trace line following each idle notification)
--trace_idle_notification_verbose (prints the heap state used by the idle notification)
--trace_gc_verbose (print more details following each garbage collection)
--trace_allocation_stack_interval (print stack trace after <n> free-list allocations)
--trace_mutator_utilization (print mutator utilization, allocation speed, gc speed)
--trace_codegen (print name of functions for which code is generated)
--trace_serializer (print code serializer trace)
--external_reference_stats (print statistics on external references used during serialization)
--trace_side_effect_free_debug_evaluate (print debug messages for side-effect-free debug-evaluate for testing)
--max_stack_trace_source_length (maximum length of function source code printed in a stack trace.)
--trace_object_groups (print object groups detected during each garbage collection)
--use_idle_notification (Use idle notification to reduce memory footprint.)
--use_verbose_printer (allows verbose printing)
--log_regs_modified (When logging register values, only print modified registers.)
--stack_trace_on_illegal (print stack trace when an illegal exception is thrown)
--print_all_exceptions (print exception object and stack trace on each thrown exception)
--print_ast (print source AST)
--print_builtin_ast (print source AST for builtins)
--print_builtin_scopes (print scopes for builtins)
--print_scopes (print scopes)
--gc_verbose (print stuff during garbage collection)
--print_handles (report handles after GC)
--print_global_handles (report global handles after GC)
--print_turbo_replay (print C++ code to recreate TurboFan graphs)
--trace_normalization (prints when objects are turned into dictionaries.)
--print_break_location (print source location on debug break)
--print_opt_source (print source code of optimized and inlined functions)
--print_code_stubs (print code stubs)
--print_code (print generated code)
--print_opt_code (print optimized code)
--print_opt_code_filter (filter for printing optimized code)
--print_unopt_code (print unoptimized code before printing optimized code based on it)
--print_code_verbose (print more information for code)
--print_builtin_code (print generated code for builtins)
--sodium (print generated code output suitable for use with the Sodium code viewer)
--print_all_code (enable all flags related to printing code)
...
sakura@sakuradeMacBook-Pro:~/v8/v8/out.gn/x64.debug$ ./d8 --help | grep trace
--trace-pretenuring (trace pretenuring decisions of HAllocate instructions)
--trace-pretenuring-statistics (trace allocation site pretenuring statistics)
--trace-block-coverage (trace collected block coverage information)
--trace-ignition-codegen (trace the codegen of ignition interpreter bytecode handlers)
--trace-ignition-dispatches (traces the dispatches to bytecode handlers by the ignition interpreter)
--trace-ignition-dispatches-output-file (the file to which the bytecode handler dispatch table is written (by default, the table is not written to a file))
--trace-track-allocation-sites (trace the tracking of allocation sites)
--trace-migration (trace object migration)
--trace-generalization (trace map generalization)
--trace-concurrent-recompilation (track concurrent recompilation)
--trace-turbo (trace generated TurboFan IR)
--trace-turbo-path (directory to dump generated TurboFan IR to)
--trace-turbo-filter (filter for tracing turbofan compilation)
--trace-turbo-graph (trace generated TurboFan graphs)
--trace-turbo-scheduled (trace TurboFan IR with schedule)
--trace-turbo-cfg-file (trace turbo cfg graph (for C1 visualizer) to a given file name)
--trace-turbo-types (trace TurboFan's types)
--trace-turbo-scheduler (trace TurboFan's scheduler)
--trace-turbo-reduction (trace TurboFan's various reducers)
--trace-turbo-trimming (trace TurboFan's graph trimmer)
--trace-turbo-jt (trace TurboFan's jump threading)
--trace-turbo-ceq (trace TurboFan's control equivalence)
--trace-turbo-loop (trace TurboFan's loop optimizations)
--trace-alloc (trace register allocator)
--trace-all-uses (trace all use positions)
--trace-representation (trace representation types)
--trace-verify-csa (trace code stubs verification)
--trace-turbo-inlining (trace TurboFan inlining)
--trace-osr (trace on-stack replacement)
--trace-environment-liveness (trace liveness of local variable slots)
--trace-turbo-load-elimination (trace TurboFan load elimination)
--trace-store-elimination (trace store elimination)
--wasm-trace-native-heap (trace wasm native heap events)
--wasm-trace-serialization (trace serialization/deserialization)
--trace-wasm-decoder (trace decoding of wasm code)
--trace-wasm-decode-time (trace decoding time of wasm code)
--trace-wasm-compiler (trace compiling of wasm code)
--trace-wasm-interpreter (trace interpretation of wasm code)
--trace-wasm-streaming (trace streaming compilation of wasm code)
--trace-wasm-ast-start (start function for wasm AST trace (inclusive))
--trace-wasm-ast-end (end function for wasm AST trace (exclusive))
--trace-liftoff (trace Liftoff, the baseline compiler for WebAssembly)
--wasm-trace-memory (print all memory updates performed in wasm code)
--trace-asm-time (log asm.js timing info to the console)
--trace-asm-scanner (log tokens encountered by asm.js scanner)
--trace-asm-parser (verbose logging of asm.js parse failures)
--trace-wasm-lazy-compilation (trace lazy compilation of wasm functions)
--trace-gc (print one trace line following each garbage collection)
--trace-gc-nvp (print one detailed trace line in name=value format after each garbage collection)
--trace-gc-ignore-scavenger (do not print trace line after scavenger collection)
--trace-idle-notification (print one trace line following each idle notification)
--trace-idle-notification-verbose (prints the heap state used by the idle notification)
--trace-gc-verbose (print more details following each garbage collection)
--trace-allocation-stack-interval (print stack trace after <n> free-list allocations)
--trace-duplicate-threshold-kb (print duplicate objects in the heap if their size is more than given threshold)
--trace-fragmentation (report fragmentation for old space)
--trace-fragmentation-verbose (report fragmentation for old space (detailed))
--trace-evacuation (report evacuation statistics)
--trace-mutator-utilization (print mutator utilization, allocation speed, gc speed)
--trace-unmapper (Trace the unmapping)
--trace-parallel-scavenge (trace parallel scavenge)
--trace-concurrent-marking (trace concurrent marking)
--trace-incremental-marking (trace progress of the incremental marking)
--trace-stress-marking (trace stress marking progress)
--trace-stress-scavenge (trace stress scavenge progress)
--trace-gc-object-stats (trace object counts and memory usage)
--trace-zone-stats (trace zone memory usage)
--trace-detached-contexts (trace native contexts that are expected to be garbage collected)
--stack-trace-limit (number of stack frames to capture)
--builtins-in-stack-traces (show built-in functions in stack traces)
--trace (trace function calls)
--trace-opt (trace lazy optimization)
--trace-opt-verbose (extra verbose compilation tracing)
--trace-opt-stats (trace lazy optimization statistics)
--trace-deopt (trace optimize function deoptimization)
--trace-file-names (include file names in trace-opt/trace-deopt output)
--trace-interrupts (trace interrupts when they are handled)
--trace-serializer (print code serializer trace)
--trace-compiler-dispatcher (trace compiler dispatcher activity)
--trace-compiler-dispatcher-jobs (trace progress of individual jobs managed by the compiler dispatcher)
--trace-js-array-abuse (trace out-of-bounds accesses to JS arrays)
--trace-external-array-abuse (trace out-of-bounds-accesses to external arrays)
--trace-array-abuse (trace out-of-bounds accesses to all arrays)
--trace-side-effect-free-debug-evaluate (print debug messages for side-effect-free debug-evaluate for testing)
--max-stack-trace-source-length (maximum length of function source code printed in a stack trace.)
--heap-profiler-trace-objects (Dump heap object allocations/movements/size_updates)
--trace-ic (trace inline cache state transitions for tools/ic-processor)
--trace-prototype-users (Trace updates to prototype user tracking)
--trace-for-in-enumerate (Trace for-in enumerate slow-paths)
--trace-maps (trace map creation)
--trace-maps-details (also log map details)
--trace-sim (Trace simulator execution)
--trace-sim-messages (Trace simulator debug messages. Implied by --trace-sim.)
--stack-trace-on-illegal (print stack trace when an illegal exception is thrown)
--trace-rail (trace RAIL mode)
--print-all-exceptions (print exception object and stack trace on each thrown exception)
--trace-lazy-deserialization (Trace lazy deserialization.)
--trace-minor-mc-parallel-marking (trace parallel marking for the young generation)
--trace-contexts (trace contexts operations)
--trace-turbo-escape (enable tracing in escape analysis)
--trace-module-status (Trace status transitions of ECMAScript modules)
--trace-normalization (prints when objects are turned into dictionaries.)
--trace-lazy (trace lazy compilation)
--trace-isolates (trace isolate state changes)
--trace-regexp-bytecodes (trace regexp bytecode execution)
--trace-regexp-assembler (trace regexp macro assembler calls.)
--trace-regexp-parser (trace regexp parsing)
--trace-wasm-instances (trace creation and collection of wasm instances)
--redirect-code-traces (output deopt information and disassembly into file code-<pid>-<isolate id>.asm)
--redirect-code-traces-to (output deopt information and disassembly into the given file)
--trace-elements-transitions (trace elements transitions)
--trace-creation-allocation-sites (trace the creation of allocation sites)

IR可视化工具:turbolizer

安装

1
2
3
sakura@sakuradeMacBook-Pro:~/Desktop/v8/tools/turbolizer$ npm i
up to date in 2.01s
sakura@sakuradeMacBook-Pro:~/Desktop/v8/tools/turbolizer$ npm run-script build

使用

生成的在build目录下,如图

然后在turbolizer目录下启动服务

1
python -m SimpleHTTPServer 8000

打开Chrome浏览器,注意一件事情,那就是这东西十分不好用……他对浏览器兼容适配很不好,Chrome勉强能用,提示无效的json文件就刷新几下看看……

选择输入文件。
这里的输入文件是来源于–trace-turbo这个flag生成的json文件。
我举个例子

1
2
3
4
5
function add(a,b) { return a + b; }
add(23, 44);
add(2, 88);
%OptimizeFunctionOnNextCall(add);
add(2, 7);

1
./d8 --allow-natives-syntax --trace-turbo test.js --trace-turbo-path /Users/sakura/Desktop/tur

–allow-natives-syntax是用来开启%OptimizeFunctionOnNextCall(add)标志的
–trace-turbo是trace TurboFan优化阶段,生成json文件
–trace-turbo-path 指定输出json的目录

然后上传上去就可以选择阶段和可视化的看IR了~

因为这个工具的一些兼容关系,你可能需要自己改一下html,比如我就是height太小(原本25px,逗我……)