UNPKG

3.63 kBJavaScriptView Raw
1'use strict'
2
3const summary = require('summary')
4
5function performanceIssue (issue) {
6 return issue ? 'performance' : 'none'
7}
8
9function analyseMemory (systemInfo, processStatSubset, traceEventSubset) {
10 // Create a set of all GC events that blocks the eventloop
11 const blockingEventsNewSpace = new Set(['V8.GCScavenger'])
12 const blockingEventsOldSpace = new Set([
13 'V8.GCFinalizeMC', 'V8.GCIncrementalMarkingFinalize'
14 ])
15 // In node.js 8 and higher, V8.GCIncrementalMarking runs cocurrently
16 if (systemInfo.nodeVersion.major < 8) {
17 blockingEventsOldSpace.add('V8.GCIncrementalMarking')
18 }
19
20 // Create an data structure with all blocking gc events
21 // * compute a time-window-index, this is used to aggregate many small gc-events.
22 // each window is 1 second long. Note that rooling time windows are not used,
23 // as it is likely unnecessary and computationally costly.
24 // * pre-compute the duration
25 const gcevents = traceEventSubset
26 .filter((d) => blockingEventsNewSpace.has(d.name) || blockingEventsOldSpace.has(d.name))
27 .map((d) => ({
28 name: d.name,
29 timeWindow: Math.round((0.5 / 1000) * (d.args.endTimestamp + d.args.startTimestamp)),
30 duration: d.args.endTimestamp - d.args.startTimestamp
31 }))
32
33 // Check that there is at least one GC event.
34 if (gcevents.length === 0) {
35 return {
36 external: 'none',
37 rss: 'none',
38 heapTotal: 'data',
39 heapUsed: 'data'
40 }
41 }
42
43 // Setup a 2d array structure, to hold data for each time window
44 const timeWindows = gcevents.map((d) => d.timeWindow)
45 const timeWindowMax = Math.max(...timeWindows)
46 const timeWindowMin = Math.min(...timeWindows)
47 const timeWindowData = []
48 for (let i = 0; i <= (timeWindowMax - timeWindowMin); i++) {
49 timeWindowData.push([])
50 }
51 // Move gcevents to timeWindowData
52 for (const gcevent of gcevents) {
53 timeWindowData[gcevent.timeWindow - timeWindowMin].push(gcevent)
54 }
55
56 // Compute the max blocked time
57 // The maxBlockedTimeOver1SecNewSpace and maxBlockedTimeOver1SecOldSpace are
58 // just for indicating in the graph coloring what the time was mostly spent on
59 const maxBlockedTimeOver1Sec = maxBlockedTime(timeWindowData)
60 const maxBlockedTimeOver1SecNewSpace = maxBlockedTime(
61 timeWindowData.map(
62 (data) => data.filter((d) => blockingEventsNewSpace.has(d.name))
63 )
64 )
65 const maxBlockedTimeOver1SecOldSpace = maxBlockedTime(
66 timeWindowData.map(
67 (data) => data.filter((d) => blockingEventsOldSpace.has(d.name))
68 )
69 )
70
71 return {
72 // We are currently not checking anything related to the external memory
73 external: 'none',
74 // If the user has a lot of code or a huge stack, the RSS could be huge.
75 // This does not necessary indicate an issue, thus RSS is never used
76 // as a measurement.
77 rss: 'none',
78 // Detect an issue if more than 100ms per 1sec, was spent doing
79 // blocking garbage collection.
80 // Mark the issue in heapTotal, if time was primarily spent cleaning
81 // up the old space.
82 // Mark the issue in heapUsed: if time was primarily spent cleaning
83 // up the new space.
84 heapTotal: performanceIssue(
85 (maxBlockedTimeOver1Sec >= 100) &&
86 (maxBlockedTimeOver1SecOldSpace >= maxBlockedTimeOver1SecNewSpace)
87 ),
88 heapUsed: performanceIssue(
89 (maxBlockedTimeOver1Sec >= 100) &&
90 (maxBlockedTimeOver1SecOldSpace < maxBlockedTimeOver1SecNewSpace)
91 )
92 }
93}
94
95module.exports = analyseMemory
96
97function maxBlockedTime (timeWindowData) {
98 return summary(
99 timeWindowData.map((data) => summary(
100 data.map((d) => d.duration)
101 ).sum())
102 ).max()
103}