Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 /// A script to track the high water-mark of memory usage of an application. | |
| 6 /// To monitor how much memory dart2js is using, run dart2js as follows: | |
| 7 /// | |
| 8 /// DART_VM_OPTIONS=--observe dart2js ... | |
| 9 /// | |
| 10 /// and run this script immediately after. | |
| 11 library compiler.tool.track_memory; | |
| 12 | |
| 13 import 'dart:math' show max; | |
| 14 import 'dart:io'; | |
| 15 import 'dart:async'; | |
| 16 | |
| 17 import 'dart:convert'; | |
| 18 | |
| 19 /// Socket to connect to the vm observatory service. | |
| 20 WebSocket socket; | |
| 21 | |
| 22 main() async { | |
| 23 _printHeader(); | |
| 24 _showProgress(0, 0, 0, 0); | |
| 25 try { | |
| 26 socket = await WebSocket.connect('ws://localhost:8181/ws'); | |
|
sra1
2015/10/05 17:04:58
TODO: you might need to change the port if another
Siggi Cherem (dart-lang)
2015/10/05 17:19:44
Done. Just made it an arg which defaults to 8181
| |
| 27 socket.listen(_handleResponse); | |
| 28 await _resumeMainIsolateIfPaused(); | |
| 29 _streamListen('GC'); | |
| 30 _streamListen('Isolate'); | |
| 31 _streamListen('Debug'); | |
| 32 } on SocketException catch (e) { | |
| 33 // TODO(sigmund): add better error messages, maybe option to retry. | |
| 34 print('\n${_RED}error${_NONE}: $e'); | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 /// Internal counter for request ids. | |
| 39 int _requestId = 0; | |
| 40 Map _pendingResponses = {}; | |
| 41 | |
| 42 /// Subscribe to listen to a vm service data stream. | |
| 43 _streamListen(String streamId) => | |
| 44 _sendMessage("streamListen", {"streamId": "$streamId"}); | |
| 45 | |
| 46 /// Tell the vm service to resume a specific isolate. | |
| 47 _resumeIsolate(String isolateId) => | |
| 48 _sendMessage("resume", {"isolateId": "$isolateId"}); | |
| 49 | |
| 50 /// Resumes the main isolate if it was paused on start. | |
| 51 _resumeMainIsolateIfPaused() async { | |
| 52 var vm = await _sendMessage('getVM'); | |
| 53 var isolateId = vm["isolates"][0]["id"]; | |
| 54 var isolate = await _sendMessage('getIsolate', {'isolateId': isolateId}); | |
|
sra1
2015/10/05 17:04:58
Some strings are 'x' others "x".
I assume the inte
Siggi Cherem (dart-lang)
2015/10/05 17:19:44
Good point - yeah, I had started with ', then copi
| |
| 55 bool isPaused = isolate["pauseEvent"]["kind"] == "PauseStart"; | |
| 56 if (isPaused) _resumeIsolate(isolateId); | |
| 57 } | |
| 58 | |
| 59 /// Send a message to the vm service. | |
| 60 Future _sendMessage(String method, [Map args= const {}]) { | |
| 61 var id = _requestId++; | |
| 62 _pendingResponses[id] = new Completer(); | |
| 63 socket.add(JSON.encode({ | |
| 64 "jsonrpc": "2.0", | |
| 65 "id": "$id", | |
| 66 "method": "$method", | |
| 67 "params": args, | |
| 68 })); | |
| 69 return _pendingResponses[id].future; | |
| 70 } | |
| 71 | |
| 72 /// Handle all responses | |
| 73 _handleResponse(String s) { | |
| 74 var json = JSON.decode(s); | |
| 75 if (json['method'] != "streamNotify") { | |
| 76 var id = json['id']; | |
| 77 if (id is String) id = int.parse(id); | |
| 78 if (id == null || !_pendingResponses.containsKey(id)) return; | |
| 79 _pendingResponses.remove(id).complete(json["result"]); | |
| 80 return; | |
| 81 } | |
| 82 | |
| 83 // isolate pauses on exit automatically. We detect this to stop and exit. | |
| 84 if (json["params"]["streamId"] == "Debug") { | |
| 85 _handleDebug(json); | |
| 86 } else if (json["params"]["streamId"] == "Isolate") { | |
| 87 _handleIsolate(json); | |
| 88 } else if (json["params"]["streamId"] == "GC") { | |
| 89 _handleGC(json); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 /// Handle a `Debug` notification. | |
| 94 _handleDebug(Map json) { | |
| 95 var isolateId = json["params"]["event"]["isolate"]["id"]; | |
| 96 if (json["params"]["event"]["kind"] == "PauseStart") { | |
| 97 _resumeIsolate(isolateId); | |
| 98 } if (json["params"]["event"]["kind"] == "PauseExit") { | |
| 99 _resumeIsolate(isolateId); | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 /// Handle a `Isolate` notification. | |
| 104 _handleIsolate(Map json) { | |
| 105 if (json["params"]["event"]["kind"] == "IsolateExit") { | |
| 106 print(''); | |
| 107 socket.close(); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 /// Handle a `GC` notification. | |
| 112 _handleGC(Map json) { | |
| 113 // print(new JsonEncoder.withIndent(" ").convert(json)); | |
| 114 var event = json["params"]["event"]; | |
| 115 var newUsed = event["new"]["used"]; | |
| 116 var newCapacity = event["new"]["capacity"]; | |
| 117 var oldUsed = event["old"]["used"]; | |
| 118 var oldCapacity = event["old"]["capacity"]; | |
| 119 _showProgress(newUsed, newCapacity, oldUsed, oldCapacity); | |
| 120 } | |
| 121 | |
| 122 int lastNewUsed = 0; | |
| 123 int lastOldUsed = 0; | |
| 124 int lastMaxUsed = 0; | |
| 125 int lastNewCapacity = 0; | |
| 126 int lastOldCapacity = 0; | |
| 127 int lastMaxCapacity = 0; | |
| 128 | |
| 129 /// Shows a status line with use/capacity numbers for new/old/total/max, | |
| 130 /// highlighting in red when capacity increases, and in green when it decreases. | |
| 131 _showProgress(newUsed, newCapacity, oldUsed, oldCapacity) { | |
| 132 var sb = new StringBuffer(); | |
| 133 sb.write('\r '); // replace the status-line in place | |
| 134 _writeNumber(sb, lastNewUsed, newUsed); | |
| 135 _writeNumber(sb, lastNewCapacity, newCapacity, color: true); | |
| 136 | |
| 137 sb.write(' | '); | |
| 138 _writeNumber(sb, lastOldUsed, oldUsed); | |
| 139 _writeNumber(sb, lastOldCapacity, oldCapacity, color: true); | |
| 140 | |
| 141 sb.write(' | '); | |
| 142 _writeNumber(sb, lastNewUsed + lastOldUsed, newUsed + oldUsed); | |
| 143 _writeNumber(sb, lastNewCapacity + lastOldCapacity, newCapacity + oldCapacity, | |
| 144 color: true); | |
| 145 | |
| 146 sb.write(' | '); | |
| 147 var maxUsed = max(lastMaxUsed, newUsed + oldUsed); | |
| 148 var maxCapacity = max(lastMaxCapacity, newCapacity + oldCapacity); | |
| 149 _writeNumber(sb, lastMaxUsed, maxUsed); | |
| 150 _writeNumber(sb, lastMaxCapacity, maxCapacity, color: true); | |
| 151 stdout.write('$sb'); | |
| 152 | |
| 153 lastNewUsed = newUsed; | |
| 154 lastOldUsed = oldUsed; | |
| 155 lastMaxUsed = maxUsed; | |
| 156 lastNewCapacity = newCapacity; | |
| 157 lastOldCapacity = oldCapacity; | |
| 158 lastMaxCapacity = maxCapacity; | |
| 159 } | |
| 160 | |
| 161 const mega = 1024 * 1024; | |
| 162 _writeNumber(sb, before, now, {color: false}) { | |
| 163 if (color) sb.write(before < now ? _RED : before > now ? _GREEN : ''); | |
| 164 var string; | |
| 165 if (now < 1024) { | |
| 166 string = ' ${now}b'; | |
| 167 } else if (now < mega) { | |
| 168 string = ' ${(now/1024).toStringAsFixed(0)}K'; | |
| 169 } else { | |
| 170 string = ' ${(now/mega).toStringAsFixed(1)}M'; | |
| 171 } | |
| 172 if (string.length < 10) string = '${" " * (8 - string.length)}$string'; | |
| 173 sb.write(string); | |
| 174 if (color) sb.write(before != now ? _NONE : ''); | |
| 175 return before > now; | |
| 176 } | |
| 177 | |
| 178 _printHeader() { | |
| 179 print(''' | |
| 180 Memory usage: | |
| 181 new generation | old generation | total | max | |
| 182 in-use/capacity | in-use/capacity | in-use/capacity | in-use/capacity '''); | |
| 183 } | |
| 184 | |
| 185 const _RED = "\x1b[31m"; | |
| 186 const _GREEN = "\x1b[32m"; | |
| 187 const _NONE = "\x1b[0m"; | |
| OLD | NEW |