Index: pkg/compiler/tool/track_memory.dart |
diff --git a/pkg/compiler/tool/track_memory.dart b/pkg/compiler/tool/track_memory.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1442b4dce8429aad8afa036d29d8fbde1b1b563f |
--- /dev/null |
+++ b/pkg/compiler/tool/track_memory.dart |
@@ -0,0 +1,187 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+/// A script to track the high water-mark of memory usage of an application. |
+/// To monitor how much memory dart2js is using, run dart2js as follows: |
+/// |
+/// DART_VM_OPTIONS=--observe dart2js ... |
+/// |
+/// and run this script immediately after. |
+library compiler.tool.track_memory; |
+ |
+import 'dart:math' show max; |
+import 'dart:io'; |
+import 'dart:async'; |
+ |
+import 'dart:convert'; |
+ |
+/// Socket to connect to the vm observatory service. |
+WebSocket socket; |
+ |
+main() async { |
+ _printHeader(); |
+ _showProgress(0, 0, 0, 0); |
+ try { |
+ 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
|
+ socket.listen(_handleResponse); |
+ await _resumeMainIsolateIfPaused(); |
+ _streamListen('GC'); |
+ _streamListen('Isolate'); |
+ _streamListen('Debug'); |
+ } on SocketException catch (e) { |
+ // TODO(sigmund): add better error messages, maybe option to retry. |
+ print('\n${_RED}error${_NONE}: $e'); |
+ } |
+} |
+ |
+/// Internal counter for request ids. |
+int _requestId = 0; |
+Map _pendingResponses = {}; |
+ |
+/// Subscribe to listen to a vm service data stream. |
+_streamListen(String streamId) => |
+ _sendMessage("streamListen", {"streamId": "$streamId"}); |
+ |
+/// Tell the vm service to resume a specific isolate. |
+_resumeIsolate(String isolateId) => |
+ _sendMessage("resume", {"isolateId": "$isolateId"}); |
+ |
+/// Resumes the main isolate if it was paused on start. |
+_resumeMainIsolateIfPaused() async { |
+ var vm = await _sendMessage('getVM'); |
+ var isolateId = vm["isolates"][0]["id"]; |
+ 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
|
+ bool isPaused = isolate["pauseEvent"]["kind"] == "PauseStart"; |
+ if (isPaused) _resumeIsolate(isolateId); |
+} |
+ |
+/// Send a message to the vm service. |
+Future _sendMessage(String method, [Map args= const {}]) { |
+ var id = _requestId++; |
+ _pendingResponses[id] = new Completer(); |
+ socket.add(JSON.encode({ |
+ "jsonrpc": "2.0", |
+ "id": "$id", |
+ "method": "$method", |
+ "params": args, |
+ })); |
+ return _pendingResponses[id].future; |
+} |
+ |
+/// Handle all responses |
+_handleResponse(String s) { |
+ var json = JSON.decode(s); |
+ if (json['method'] != "streamNotify") { |
+ var id = json['id']; |
+ if (id is String) id = int.parse(id); |
+ if (id == null || !_pendingResponses.containsKey(id)) return; |
+ _pendingResponses.remove(id).complete(json["result"]); |
+ return; |
+ } |
+ |
+ // isolate pauses on exit automatically. We detect this to stop and exit. |
+ if (json["params"]["streamId"] == "Debug") { |
+ _handleDebug(json); |
+ } else if (json["params"]["streamId"] == "Isolate") { |
+ _handleIsolate(json); |
+ } else if (json["params"]["streamId"] == "GC") { |
+ _handleGC(json); |
+ } |
+} |
+ |
+/// Handle a `Debug` notification. |
+_handleDebug(Map json) { |
+ var isolateId = json["params"]["event"]["isolate"]["id"]; |
+ if (json["params"]["event"]["kind"] == "PauseStart") { |
+ _resumeIsolate(isolateId); |
+ } if (json["params"]["event"]["kind"] == "PauseExit") { |
+ _resumeIsolate(isolateId); |
+ } |
+} |
+ |
+/// Handle a `Isolate` notification. |
+_handleIsolate(Map json) { |
+ if (json["params"]["event"]["kind"] == "IsolateExit") { |
+ print(''); |
+ socket.close(); |
+ } |
+} |
+ |
+/// Handle a `GC` notification. |
+_handleGC(Map json) { |
+ // print(new JsonEncoder.withIndent(" ").convert(json)); |
+ var event = json["params"]["event"]; |
+ var newUsed = event["new"]["used"]; |
+ var newCapacity = event["new"]["capacity"]; |
+ var oldUsed = event["old"]["used"]; |
+ var oldCapacity = event["old"]["capacity"]; |
+ _showProgress(newUsed, newCapacity, oldUsed, oldCapacity); |
+} |
+ |
+int lastNewUsed = 0; |
+int lastOldUsed = 0; |
+int lastMaxUsed = 0; |
+int lastNewCapacity = 0; |
+int lastOldCapacity = 0; |
+int lastMaxCapacity = 0; |
+ |
+/// Shows a status line with use/capacity numbers for new/old/total/max, |
+/// highlighting in red when capacity increases, and in green when it decreases. |
+_showProgress(newUsed, newCapacity, oldUsed, oldCapacity) { |
+ var sb = new StringBuffer(); |
+ sb.write('\r '); // replace the status-line in place |
+ _writeNumber(sb, lastNewUsed, newUsed); |
+ _writeNumber(sb, lastNewCapacity, newCapacity, color: true); |
+ |
+ sb.write(' | '); |
+ _writeNumber(sb, lastOldUsed, oldUsed); |
+ _writeNumber(sb, lastOldCapacity, oldCapacity, color: true); |
+ |
+ sb.write(' | '); |
+ _writeNumber(sb, lastNewUsed + lastOldUsed, newUsed + oldUsed); |
+ _writeNumber(sb, lastNewCapacity + lastOldCapacity, newCapacity + oldCapacity, |
+ color: true); |
+ |
+ sb.write(' | '); |
+ var maxUsed = max(lastMaxUsed, newUsed + oldUsed); |
+ var maxCapacity = max(lastMaxCapacity, newCapacity + oldCapacity); |
+ _writeNumber(sb, lastMaxUsed, maxUsed); |
+ _writeNumber(sb, lastMaxCapacity, maxCapacity, color: true); |
+ stdout.write('$sb'); |
+ |
+ lastNewUsed = newUsed; |
+ lastOldUsed = oldUsed; |
+ lastMaxUsed = maxUsed; |
+ lastNewCapacity = newCapacity; |
+ lastOldCapacity = oldCapacity; |
+ lastMaxCapacity = maxCapacity; |
+} |
+ |
+const mega = 1024 * 1024; |
+_writeNumber(sb, before, now, {color: false}) { |
+ if (color) sb.write(before < now ? _RED : before > now ? _GREEN : ''); |
+ var string; |
+ if (now < 1024) { |
+ string = ' ${now}b'; |
+ } else if (now < mega) { |
+ string = ' ${(now/1024).toStringAsFixed(0)}K'; |
+ } else { |
+ string = ' ${(now/mega).toStringAsFixed(1)}M'; |
+ } |
+ if (string.length < 10) string = '${" " * (8 - string.length)}$string'; |
+ sb.write(string); |
+ if (color) sb.write(before != now ? _NONE : ''); |
+ return before > now; |
+} |
+ |
+_printHeader() { |
+ print(''' |
+Memory usage: |
+ new generation | old generation | total | max |
+ in-use/capacity | in-use/capacity | in-use/capacity | in-use/capacity '''); |
+} |
+ |
+const _RED = "\x1b[31m"; |
+const _GREEN = "\x1b[32m"; |
+const _NONE = "\x1b[0m"; |