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