mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 21:40:07 +00:00
add tool to track memory watermark
R=sra@google.com Review URL: https://codereview.chromium.org/1384833002 .
This commit is contained in:
parent
8697392367
commit
66db8a7f0e
193
pkg/compiler/tool/track_memory.dart
Normal file
193
pkg/compiler/tool/track_memory.dart
Normal file
|
@ -0,0 +1,193 @@
|
|||
// 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(args) async {
|
||||
_printHeader();
|
||||
_showProgress(0, 0, 0, 0);
|
||||
try {
|
||||
var port = args.length > 0 ? int.parse(args[0]) : 8181;
|
||||
socket = await WebSocket.connect('ws://localhost:$port/ws');
|
||||
socket.listen(_handleResponse);
|
||||
await _resumeMainIsolateIfPaused();
|
||||
_streamListen('GC');
|
||||
_streamListen('Isolate');
|
||||
_streamListen('Debug');
|
||||
} catch (e) {
|
||||
// TODO(sigmund): add better error messages, maybe option to retry.
|
||||
print('\n${_RED}error${_NONE}: $e');
|
||||
print('usage:\n'
|
||||
' Start a Dart process with the --observe flag (and optionally '
|
||||
'the --pause_isolates_on_start flag), then invoke:\n'
|
||||
' dart tool/track_memory.dart [<port>]\n'
|
||||
' by default port is 8181');
|
||||
}
|
||||
}
|
||||
|
||||
/// 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});
|
||||
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';
|
Loading…
Reference in a new issue