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 |