UNPKG

3.92 kBJavaScriptView Raw
1'use strict'
2
3const stream = require('../lib/destroyable-stream')
4const endpoint = require('endpoint')
5const parser = require('@nearform/trace-events-parser')
6
7// This list is from: https://github.com/catapult-project/catapult/blob/master
8// /tracing/tracing/metrics/v8/gc_metric_test.html#L50L57
9const gcEventNames = new Set([
10 'V8.GCCompactor',
11 'V8.GCFinalizeMC',
12 'V8.GCFinalizeMCReduceMemory',
13 'V8.GCIncrementalMarking',
14 'V8.GCIncrementalMarkingFinalize',
15 'V8.GCIncrementalMarkingStart',
16 'V8.GCPhantomHandleProcessingCallback',
17 'V8.GCScavenger'
18])
19
20class TraceEventDecoder extends stream.Transform {
21 constructor (systemInfoReader) {
22 super({
23 readableObjectMode: true,
24 writableObjectMode: false
25 })
26
27 this.incremetalMarkingStart = 0
28 this.incremetalMarkingStartFound = false
29
30 // Get system clock synchronization info
31 this.systemInfoReader = systemInfoReader
32 this.clockOffset = null
33
34 // trace-events-parser is synchronous so there is no need to think about
35 // backpresure
36 this.parser = parser()
37 this.parser.on('data', (data) => this._process(data))
38 this.systemInfoReader.on('error', (err) => this.destroy(err))
39 }
40
41 _process (traceEvent) {
42 if (traceEvent.cat !== 'v8' || !gcEventNames.has(traceEvent.name)) {
43 return
44 }
45
46 // Combine sequences of blocking GCIncrementalMarking and the following
47 // GCFinalizeMC into just one event called V8.GCMarkSweepCompact
48
49 // After this, the following events exists:
50 // V8.GCCompactor (purpose unknown)
51 // V8.GCFinalizeMC
52 // V8.GCFinalizeMCReduceMemory (purpose unknown)
53 // V8.GCIncrementalMarking
54 // V8.GCPhantomHandleProcessingCallback (purpose unknown)
55 // V8.GCScavenger
56 // V8.GCMarkSweepCompact (aggregated event)
57
58 if (traceEvent.name === 'V8.GCIncrementalMarkingStart') {
59 this.incremetalMarkingStart = traceEvent.ts
60 this.incremetalMarkingStartFound = true
61 } else if (this.incremetalMarkingStartFound &&
62 traceEvent.name === 'V8.GCIncrementalMarkingFinalize') {
63 // skip
64 } else if (this.incremetalMarkingStartFound &&
65 traceEvent.name === 'V8.GCIncrementalMarking') {
66 // skip
67 } else if (this.incremetalMarkingStartFound &&
68 traceEvent.name === 'V8.GCFinalizeMC') {
69 this.incremetalMarkingStartFound = false
70
71 const starttime = this.incremetalMarkingStart
72 const endtime = traceEvent.ts + traceEvent.dur
73 this.push({
74 pid: traceEvent.pid,
75 tid: traceEvent.tid,
76 ts: starttime,
77 ph: 'X',
78 cat: 'v8',
79 name: 'V8.GCMarkSweepCompact',
80 dur: endtime - starttime,
81 args: {
82 startTimestamp: (starttime * 1e-3) + this.clockOffset,
83 endTimestamp: (endtime * 1e-3) + this.clockOffset
84 }
85 })
86 } else {
87 // Add unixtime to the traceEvent
88 const endtime = traceEvent.ts + traceEvent.dur
89 traceEvent.args = {
90 startTimestamp: (traceEvent.ts * 1e-3) + this.clockOffset,
91 endTimestamp: (endtime * 1e-3) + this.clockOffset
92 }
93 this.push(traceEvent)
94 }
95 }
96
97 _transform (chunk, encoding, callback) {
98 const self = this
99 if (this.clockOffset === null) {
100 this.systemInfoReader
101 .pipe(endpoint({ objectMode: true }, function (err, data) {
102 if (err) return // emitted in the constructor
103
104 // Save clock offset
105 const systemInfo = data[0]
106 self.clockOffset = systemInfo.clockOffset
107
108 // Now that the clock offset is known, the data can be processed
109 self.parser.write(chunk, encoding)
110 callback(null)
111 }))
112 } else {
113 // Clock offset is already known, process directly
114 this.parser.write(chunk, encoding)
115 callback(null)
116 }
117 }
118
119 _flush (callback) {
120 this.parser.end()
121 callback(null)
122 }
123}
124module.exports = TraceEventDecoder