OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// This Element is part of MemoryDashboardElement. | 5 /// This Element is part of MemoryDashboardElement. |
6 /// | 6 /// |
7 /// The Element periodically interrogates the VM to log the memory usage of each | 7 /// The Element periodically interrogates the VM to log the memory usage of each |
8 /// Isolate and of the Native Memory. | 8 /// Isolate and of the Native Memory. |
9 /// | 9 /// |
10 /// For each isolate it is shown the Used and Free heap (new and old are merged | 10 /// For each isolate it is shown the Used and Free heap (new and old are merged |
(...skipping 23 matching lines...) Expand all Loading... |
34 | 34 |
35 RenderingScheduler<MemoryGraphElement> _r; | 35 RenderingScheduler<MemoryGraphElement> _r; |
36 | 36 |
37 final StreamController<IsolateSelectedEvent> _onIsolateSelected = | 37 final StreamController<IsolateSelectedEvent> _onIsolateSelected = |
38 new StreamController<IsolateSelectedEvent>(); | 38 new StreamController<IsolateSelectedEvent>(); |
39 | 39 |
40 Stream<RenderedEvent<MemoryGraphElement>> get onRendered => _r.onRendered; | 40 Stream<RenderedEvent<MemoryGraphElement>> get onRendered => _r.onRendered; |
41 Stream<IsolateSelectedEvent> get onIsolateSelected => | 41 Stream<IsolateSelectedEvent> get onIsolateSelected => |
42 _onIsolateSelected.stream; | 42 _onIsolateSelected.stream; |
43 | 43 |
44 M.VMRef _vm; | 44 M.VM _vm; |
45 M.VMRepository _vms; | |
46 M.IsolateRepository _isolates; | 45 M.IsolateRepository _isolates; |
47 M.EventRepository _events; | 46 M.EventRepository _events; |
48 StreamSubscription _onGCSubscription; | 47 StreamSubscription _onGCSubscription; |
49 StreamSubscription _onResizeSubscription; | 48 StreamSubscription _onResizeSubscription; |
50 StreamSubscription _onConnectionClosedSubscription; | 49 StreamSubscription _onConnectionClosedSubscription; |
51 Timer _onTimer; | 50 Timer _onTimer; |
52 | 51 |
53 M.VMRef get vm => _vm; | 52 M.VM get vm => _vm; |
54 | 53 |
55 factory MemoryGraphElement(M.VMRef vm, M.VMRepository vms, | 54 factory MemoryGraphElement( |
56 M.IsolateRepository isolates, M.EventRepository events, | 55 M.VM vm, M.IsolateRepository isolates, M.EventRepository events, |
57 {RenderingQueue queue}) { | 56 {RenderingQueue queue}) { |
58 assert(vm != null); | 57 assert(vm != null); |
59 assert(vms != null); | |
60 assert(isolates != null); | 58 assert(isolates != null); |
61 assert(events != null); | 59 assert(events != null); |
62 MemoryGraphElement e = document.createElement(tag.name); | 60 MemoryGraphElement e = document.createElement(tag.name); |
63 e._r = new RenderingScheduler(e, queue: queue); | 61 e._r = new RenderingScheduler(e, queue: queue); |
64 e._vm = vm; | 62 e._vm = vm; |
65 e._vms = vms; | |
66 e._isolates = isolates; | 63 e._isolates = isolates; |
67 e._events = events; | 64 e._events = events; |
68 return e; | 65 return e; |
69 } | 66 } |
70 | 67 |
71 MemoryGraphElement.created() : super.created() { | 68 MemoryGraphElement.created() : super.created() { |
72 final now = new DateTime.now(); | 69 final now = new DateTime.now(); |
73 var sample = now.subtract(_window); | 70 var sample = now.subtract(_window); |
74 while (sample.isBefore(now)) { | 71 while (sample.isBefore(now)) { |
75 _ts.add(sample); | 72 _ts.add(sample); |
76 _vmSamples.add(<int>[0, 0]); | 73 _vmSamples.add(0); |
77 _isolateUsedSamples.add([]); | 74 _isolateUsedSamples.add([]); |
78 _isolateFreeSamples.add([]); | 75 _isolateFreeSamples.add([]); |
79 sample = sample.add(_period); | 76 sample = sample.add(_period); |
80 } | 77 } |
81 _ts.add(now); | 78 _ts.add(now); |
82 _vmSamples.add(<int>[0, 0]); | 79 _vmSamples.add(0); |
83 _isolateUsedSamples.add([]); | 80 _isolateUsedSamples.add([]); |
84 _isolateFreeSamples.add([]); | 81 _isolateFreeSamples.add([]); |
85 } | 82 } |
86 | 83 |
87 static const Duration _period = const Duration(seconds: 2); | 84 static const Duration _period = const Duration(seconds: 2); |
88 static const Duration _window = const Duration(minutes: 2); | 85 static const Duration _window = const Duration(minutes: 2); |
89 | 86 |
90 @override | 87 @override |
91 attached() { | 88 attached() { |
92 super.attached(); | 89 super.attached(); |
(...skipping 12 matching lines...) Expand all Loading... |
105 super.detached(); | 102 super.detached(); |
106 _r.disable(notify: true); | 103 _r.disable(notify: true); |
107 children = []; | 104 children = []; |
108 _onGCSubscription.cancel(); | 105 _onGCSubscription.cancel(); |
109 _onConnectionClosedSubscription.cancel(); | 106 _onConnectionClosedSubscription.cancel(); |
110 _onResizeSubscription.cancel(); | 107 _onResizeSubscription.cancel(); |
111 _onTimer.cancel(); | 108 _onTimer.cancel(); |
112 } | 109 } |
113 | 110 |
114 final List<DateTime> _ts = <DateTime>[]; | 111 final List<DateTime> _ts = <DateTime>[]; |
115 final List<List<int>> _vmSamples = <List<int>>[]; | 112 final List<int> _vmSamples = <int>[]; |
116 final List<M.IsolateRef> _seenIsolates = <M.IsolateRef>[]; | 113 final List<M.IsolateRef> _seenIsolates = <M.IsolateRef>[]; |
117 final List<List<int>> _isolateUsedSamples = <List<int>>[]; | 114 final List<List<int>> _isolateUsedSamples = <List<int>>[]; |
118 final List<List<int>> _isolateFreeSamples = <List<int>>[]; | 115 final List<List<int>> _isolateFreeSamples = <List<int>>[]; |
119 final Map<String, int> _isolateIndex = <String, int>{}; | 116 final Map<String, int> _isolateIndex = <String, int>{}; |
120 final Map<String, String> _isolateName = <String, String>{}; | 117 final Map<String, String> _isolateName = <String, String>{}; |
121 | 118 |
122 var _selected; | 119 var _selected; |
123 var _previewed; | 120 var _previewed; |
124 var _hovered; | 121 var _hovered; |
125 | 122 |
126 void render() { | 123 void render() { |
127 if (_previewed != null || _hovered != null) return; | 124 if (_previewed != null || _hovered != null) return; |
128 | 125 |
129 // cache data of hoverboards | |
130 final ts = new List<DateTime>.from(_ts); | |
131 final vmSamples = new List<List<int>>.from(_vmSamples); | |
132 final isolateFreeSamples = new List<List<int>>.from(_isolateFreeSamples); | |
133 final isolateUsedSamples = new List<List<int>>.from(_isolateUsedSamples); | |
134 | |
135 final now = _ts.last; | 126 final now = _ts.last; |
136 final nativeComponents = 1; | 127 final nativeComponents = 1; |
137 final legend = new DivElement(); | 128 final legend = new DivElement(); |
138 final host = new DivElement(); | 129 final host = new DivElement(); |
139 final theme = new MemoryChartTheme(1); | 130 final theme = new MemoryChartTheme(1); |
140 children = [theme.style, legend, host]; | 131 children = [theme.style, legend, host]; |
141 final rect = host.getBoundingClientRect(); | 132 final rect = host.getBoundingClientRect(); |
142 | 133 |
143 final series = | 134 final series = |
144 new List<int>.generate(_isolateIndex.length * 2 + 1, (i) => i + 1); | 135 new List<int>.generate(_isolateIndex.length * 2 + 1, (i) => i + 1); |
145 // The stacked line chart sorts from top to bottom | 136 // The stacked line chart sorts from top to bottom |
146 final columns = [ | 137 final columns = [ |
147 new ChartColumnSpec( | 138 new ChartColumnSpec( |
148 formatter: _formatTimeAxis, type: ChartColumnSpec.TYPE_NUMBER), | 139 formatter: _formatTimeAxis, type: ChartColumnSpec.TYPE_NUMBER), |
149 new ChartColumnSpec(label: 'Native', formatter: Utils.formatSize) | 140 new ChartColumnSpec(label: 'Native', formatter: Utils.formatSize) |
150 ]..addAll(_isolateName.keys.expand((id) => [ | 141 ]..addAll(_isolateName.keys.expand((id) => [ |
151 new ChartColumnSpec(formatter: Utils.formatSize), | 142 new ChartColumnSpec(formatter: Utils.formatSize), |
152 new ChartColumnSpec(label: _label(id), formatter: Utils.formatSize) | 143 new ChartColumnSpec(label: _label(id), formatter: Utils.formatSize) |
153 ])); | 144 ])); |
154 // The stacked line chart sorts from top to bottom | 145 // The stacked line chart sorts from top to bottom |
155 final rows = new List.generate(_ts.length, (sampleIndex) { | 146 final rows = new List.generate(_ts.length, (sampleIndex) { |
156 final free = isolateFreeSamples[sampleIndex]; | 147 final free = _isolateFreeSamples[sampleIndex]; |
157 final used = isolateUsedSamples[sampleIndex]; | 148 final used = _isolateUsedSamples[sampleIndex]; |
158 final isolates = _isolateIndex.keys.expand((key) { | |
159 final isolateIndex = _isolateIndex[key]; | |
160 return <int>[free[isolateIndex], used[isolateIndex]]; | |
161 }); | |
162 return [ | 149 return [ |
163 ts[sampleIndex].difference(now).inMicroseconds, | 150 _ts[sampleIndex].difference(now).inMicroseconds, |
164 vmSamples[sampleIndex][1] ?? 1000000 | 151 _vmSamples[sampleIndex] |
165 ]..addAll(isolates); | 152 ]..addAll(_isolateIndex.keys.expand((key) { |
| 153 final isolateIndex = _isolateIndex[key]; |
| 154 return [free[isolateIndex], used[isolateIndex]]; |
| 155 })); |
166 }); | 156 }); |
167 | 157 |
168 final scale = new LinearScale()..domain = [(-_window).inMicroseconds, 0]; | 158 final scale = new LinearScale()..domain = [(-_window).inMicroseconds, 0]; |
169 final axisConfig = new ChartAxisConfig()..scale = scale; | 159 final axisConfig = new ChartAxisConfig()..scale = scale; |
170 final sMemory = | 160 final sMemory = |
171 new ChartSeries('Memory', series, new StackedLineChartRenderer()); | 161 new ChartSeries('Memory', series, new StackedLineChartRenderer()); |
172 final config = new ChartConfig([sMemory], [0]) | 162 final config = new ChartConfig([sMemory], [0]) |
173 ..legend = new ChartLegend(legend) | 163 ..legend = new ChartLegend(legend) |
174 ..registerDimensionAxis(0, axisConfig); | 164 ..registerDimensionAxis(0, axisConfig); |
175 config.minimumSize = new Rect(rect.width, rect.height); | 165 config.minimumSize = new Rect(rect.width, rect.height); |
176 final data = new ChartData(columns, rows); | 166 final data = new ChartData(columns, rows); |
177 final state = new ChartState(isMultiSelect: true) | 167 final state = new ChartState(isMultiSelect: true) |
178 ..changes.listen(_handleEvent); | 168 ..changes.listen(_handleEvent); |
179 final area = new CartesianArea(host, data, config, state: state) | 169 final area = new CartesianArea(host, data, config, state: state) |
180 ..theme = theme; | 170 ..theme = theme; |
181 area.addChartBehavior(new Hovercard(builder: (int column, int row) { | 171 area.addChartBehavior(new Hovercard(builder: (int column, int row) { |
| 172 final data = rows[row]; |
182 if (column == 1) { | 173 if (column == 1) { |
183 final data = vmSamples[row]; | 174 return _formatNativeOvercard(data[1]); |
184 return _formatNativeOvercard(data[0], data[1]); | |
185 } | 175 } |
186 final isolate = _seenIsolates[column - 2]; | 176 final isolate = _seenIsolates[column - 2]; |
187 final index = _isolateIndex[isolate.id]; | 177 final index = _isolateIndex[isolate.id] * 2 + 2; |
188 final free = isolateFreeSamples[row][index]; | 178 return _formatIsolateOvercard(isolate.name, data[index], data[index + 1]); |
189 final used = isolateUsedSamples[row][index]; | |
190 return _formatIsolateOvercard(isolate.name, free, used); | |
191 })); | 179 })); |
192 area.draw(); | 180 area.draw(); |
193 | 181 |
194 if (_selected != null) { | 182 if (_selected != null) { |
195 state.select(_selected); | 183 state.select(_selected); |
196 if (_selected > 1) { | 184 if (_selected > 1) { |
197 state.select(_selected + 1); | 185 state.select(_selected + 1); |
198 } | 186 } |
199 } | 187 } |
200 } | 188 } |
201 | 189 |
202 String _formatTimeAxis(num ms) => | 190 String _formatTimeAxis(num ms) => |
203 Utils.formatDuration(new Duration(microseconds: ms.toInt()), | 191 Utils.formatDuration(new Duration(microseconds: ms.toInt()), |
204 precision: DurationComponent.Seconds); | 192 precision: DurationComponent.Seconds); |
205 | 193 |
206 bool _running = false; | 194 bool _running = false; |
207 | 195 |
208 Future _refresh({M.IsolateRef gcIsolate}) async { | 196 Future _refresh({M.IsolateRef gcIsolate}) async { |
209 if (_running) return; | 197 if (_running) return; |
210 _running = true; | 198 _running = true; |
211 final now = new DateTime.now(); | 199 final now = new DateTime.now(); |
212 final start = now.subtract(_window); | 200 final start = now.subtract(_window); |
213 final vm = await _vms.get(_vm); | |
214 // The Service classes order isolates from the older to the newer | 201 // The Service classes order isolates from the older to the newer |
215 final isolates = | 202 final isolates = |
216 (await Future.wait(vm.isolates.map(_isolates.get))).reversed.toList(); | 203 (await Future.wait(_vm.isolates.map(_isolates.get))).reversed.toList(); |
217 while (_ts.first.isBefore(start)) { | 204 while (_ts.first.isBefore(start)) { |
218 _ts.removeAt(0); | 205 _ts.removeAt(0); |
219 _vmSamples.removeAt(0); | 206 _vmSamples.removeAt(0); |
220 _isolateUsedSamples.removeAt(0); | 207 _isolateUsedSamples.removeAt(0); |
221 _isolateFreeSamples.removeAt(0); | 208 _isolateFreeSamples.removeAt(0); |
222 } | 209 } |
223 | 210 |
224 if (_ts.first.isAfter(start)) { | 211 if (_ts.first.isAfter(start)) { |
225 _ts.insert(0, start); | 212 _ts.insert(0, start); |
226 _vmSamples.insert(0, _vmSamples.first); | 213 _vmSamples.insert(0, _vmSamples.first); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 _isolateUsedSamples.last[index] + _isolateFreeSamples.last[index]; | 248 _isolateUsedSamples.last[index] + _isolateFreeSamples.last[index]; |
262 isolateFreeSample[index] = 0; | 249 isolateFreeSample[index] = 0; |
263 } else { | 250 } else { |
264 isolateUsedSample[index] = _used(isolate); | 251 isolateUsedSample[index] = _used(isolate); |
265 isolateFreeSample[index] = _free(isolate); | 252 isolateFreeSample[index] = _free(isolate); |
266 } | 253 } |
267 }); | 254 }); |
268 _isolateUsedSamples.add(isolateUsedSample); | 255 _isolateUsedSamples.add(isolateUsedSample); |
269 _isolateFreeSamples.add(isolateFreeSample); | 256 _isolateFreeSamples.add(isolateFreeSample); |
270 | 257 |
271 _vmSamples.add(<int>[vm.currentRSS, vm.heapAllocatedMemoryUsage]); | 258 _vmSamples.add(vm.heapAllocatedMemoryUsage); |
272 | 259 |
273 _ts.add(now); | 260 _ts.add(now); |
274 } | 261 } |
275 final List<int> isolateUsedSample = new List<int>.filled(length, 0); | 262 final List<int> isolateUsedSample = new List<int>.filled(length, 0); |
276 final List<int> isolateFreeSample = new List<int>.filled(length, 0); | 263 final List<int> isolateFreeSample = new List<int>.filled(length, 0); |
277 isolates.forEach((M.Isolate isolate) { | 264 isolates.forEach((M.Isolate isolate) { |
278 _isolateName[isolate.id] = isolate.name; | 265 _isolateName[isolate.id] = isolate.name; |
279 final index = _isolateIndex[isolate.id]; | 266 final index = _isolateIndex[isolate.id]; |
280 isolateUsedSample[index] = _used(isolate); | 267 isolateUsedSample[index] = _used(isolate); |
281 isolateFreeSample[index] = _free(isolate); | 268 isolateFreeSample[index] = _free(isolate); |
282 }); | 269 }); |
283 _isolateUsedSamples.add(isolateUsedSample); | 270 _isolateUsedSamples.add(isolateUsedSample); |
284 _isolateFreeSamples.add(isolateFreeSample); | 271 _isolateFreeSamples.add(isolateFreeSample); |
285 | 272 |
286 _vmSamples.add(<int>[vm.currentRSS, vm.heapAllocatedMemoryUsage]); | 273 _vmSamples.add(vm.heapAllocatedMemoryUsage); |
287 | 274 |
288 _ts.add(now); | 275 _ts.add(now); |
289 _r.dirty(); | 276 _r.dirty(); |
290 _running = false; | 277 _running = false; |
291 } | 278 } |
292 | 279 |
293 void _handleEvent(records) => records.forEach((record) { | 280 void _handleEvent(records) => records.forEach((record) { |
294 if (record is ChartSelectionChangeRecord) { | 281 if (record is ChartSelectionChangeRecord) { |
295 var selected = record.add; | 282 var selected = record.add; |
296 if (selected == null) { | 283 if (selected == null) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 final index = _isolateIndex[isolateId]; | 318 final index = _isolateIndex[isolateId]; |
332 final name = _isolateName[isolateId]; | 319 final name = _isolateName[isolateId]; |
333 final free = _isolateFreeSamples.last[index]; | 320 final free = _isolateFreeSamples.last[index]; |
334 final used = _isolateUsedSamples.last[index]; | 321 final used = _isolateUsedSamples.last[index]; |
335 final usedStr = Utils.formatSize(used); | 322 final usedStr = Utils.formatSize(used); |
336 final capacity = free + used; | 323 final capacity = free + used; |
337 final capacityStr = Utils.formatSize(capacity); | 324 final capacityStr = Utils.formatSize(capacity); |
338 return '${name} ($usedStr / $capacityStr)'; | 325 return '${name} ($usedStr / $capacityStr)'; |
339 } | 326 } |
340 | 327 |
341 static HtmlElement _formatNativeOvercard(int currentRSS, int heap) => | 328 static HtmlElement _formatNativeOvercard(int heap) => new DivElement() |
| 329 ..children = [ |
342 new DivElement() | 330 new DivElement() |
| 331 ..classes = ['hovercard-title'] |
| 332 ..text = 'Native', |
| 333 new DivElement() |
| 334 ..classes = ['hovercard-measure', 'hovercard-multi'] |
343 ..children = [ | 335 ..children = [ |
344 new DivElement() | 336 new DivElement() |
345 ..classes = ['hovercard-title'] | 337 ..classes = ['hovercard-measure-label'] |
346 ..text = 'Native', | 338 ..text = 'Heap', |
347 new DivElement() | 339 new DivElement() |
348 ..classes = ['hovercard-measure', 'hovercard-multi'] | 340 ..classes = ['hovercard-measure-value'] |
349 ..children = [ | 341 ..text = Utils.formatSize(heap), |
350 new DivElement() | 342 ] |
351 ..classes = ['hovercard-measure-label'] | 343 ]; |
352 ..text = 'Total Memory Usage', | |
353 new DivElement() | |
354 ..classes = ['hovercard-measure-value'] | |
355 ..text = currentRSS != null | |
356 ? Utils.formatSize(currentRSS) | |
357 : "unavailable", | |
358 ], | |
359 new DivElement() | |
360 ..classes = ['hovercard-measure', 'hovercard-multi'] | |
361 ..children = [ | |
362 new DivElement() | |
363 ..classes = ['hovercard-measure-label'] | |
364 ..text = 'Native Heap', | |
365 new DivElement() | |
366 ..classes = ['hovercard-measure-value'] | |
367 ..text = heap != null ? Utils.formatSize(heap) : "unavailable", | |
368 ] | |
369 ]; | |
370 | 344 |
371 static HtmlElement _formatIsolateOvercard(String name, int free, int used) { | 345 static HtmlElement _formatIsolateOvercard(String name, int free, int used) { |
372 final capacity = free + used; | 346 final capacity = free + used; |
373 return new DivElement() | 347 return new DivElement() |
374 ..children = [ | 348 ..children = [ |
375 new DivElement() | 349 new DivElement() |
376 ..classes = ['hovercard-title'] | 350 ..classes = ['hovercard-title'] |
377 ..text = name, | 351 ..text = name, |
378 new DivElement() | 352 new DivElement() |
379 ..classes = ['hovercard-measure', 'hovercard-multi'] | 353 ..classes = ['hovercard-measure', 'hovercard-multi'] |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 </defs> | 427 </defs> |
454 '''; | 428 '''; |
455 | 429 |
456 StyleElement get style => new StyleElement() | 430 StyleElement get style => new StyleElement() |
457 ..text = ''' | 431 ..text = ''' |
458 memory-graph svg .stacked-line-rdr-line:nth-child(2n+${_offset+1}) | 432 memory-graph svg .stacked-line-rdr-line:nth-child(2n+${_offset+1}) |
459 path:nth-child(1) { | 433 path:nth-child(1) { |
460 filter: url(#stroke-grid); | 434 filter: url(#stroke-grid); |
461 }'''; | 435 }'''; |
462 } | 436 } |
OLD | NEW |