blob: 3e292211df922baab3df083986508a222c9d9367 [file] [log] [blame]
// 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.
part of dart.io;
abstract class _IOResourceInfo {
final String type;
final int id;
String get name;
static int _count = 0;
static final Stopwatch _sw = new Stopwatch()..start();
static final _startTime = new DateTime.now().millisecondsSinceEpoch;
static double get timestamp => _startTime + _sw.elapsedMicroseconds / 1000;
_IOResourceInfo(this.type) : id = _IOResourceInfo.getNextID();
/// Get the full set of values for a specific implementation. This is normally
/// looked up based on an id from a referenceValueMap.
Map<String, dynamic> get fullValueMap;
/// The reference map, used to return a list of values, e.g., getting
/// all open sockets. The structure of this is shared among all subclasses.
Map<String, dynamic> get referenceValueMap => {
// The type for a reference object is prefixed with @ in observatory.
'type': '@$type',
'id': id,
'name': name,
};
static int getNextID() => _count++;
}
abstract class _ReadWriteResourceInfo extends _IOResourceInfo {
int totalRead;
int totalWritten;
int readCount;
int writeCount;
double lastRead;
double lastWrite;
// Not all call sites use this. In some cases, e.g., a socket, a read does
// not always mean that we actually read some bytes (we may do a read to see
// if there are some bytes available).
void addRead(int bytes) {
totalRead += bytes;
readCount++;
lastRead = _IOResourceInfo.timestamp;
}
// In cases where we read but did not necessarily get any bytes, use this to
// update the readCount and timestamp. Manually update totalRead if any bytes
// where actually read.
void didRead() {
addRead(0);
}
void addWrite(int bytes) {
totalWritten += bytes;
writeCount++;
lastWrite = _IOResourceInfo.timestamp;
}
_ReadWriteResourceInfo(String type)
: totalRead = 0,
totalWritten = 0,
readCount = 0,
writeCount = 0,
lastRead = 0.0,
lastWrite = 0.0,
super(type);
Map<String, dynamic> get fullValueMap => {
'type': type,
'id': id,
'name': name,
'totalRead': totalRead,
'totalWritten': totalWritten,
'readCount': readCount,
'writeCount': writeCount,
'lastRead': lastRead,
'lastWrite': lastWrite
};
}
class _FileResourceInfo extends _ReadWriteResourceInfo {
static const String _type = '_file';
final file;
static Map<int, _FileResourceInfo> openFiles =
new Map<int, _FileResourceInfo>();
_FileResourceInfo(this.file) : super(_type) {
FileOpened(this);
}
static FileOpened(_FileResourceInfo info) {
assert(!openFiles.containsKey(info.id));
openFiles[info.id] = info;
}
static FileClosed(_FileResourceInfo info) {
assert(openFiles.containsKey(info.id));
openFiles.remove(info.id);
}
static Iterable<Map<String, dynamic>> getOpenFilesList() {
return new List.from(openFiles.values.map((e) => e.referenceValueMap));
}
static Future<ServiceExtensionResponse> getOpenFiles(function, params) {
assert(function == 'ext.dart.io.getOpenFiles');
var data = {'type': '_openfiles', 'data': getOpenFilesList()};
var jsonValue = json.encode(data);
return new Future.value(new ServiceExtensionResponse.result(jsonValue));
}
Map<String, dynamic> getFileInfoMap() {
return fullValueMap;
}
static Future<ServiceExtensionResponse> getFileInfoMapByID(function, params) {
assert(params.containsKey('id'));
var id = int.parse(params['id']);
var result =
openFiles.containsKey(id) ? openFiles[id].getFileInfoMap() : {};
var jsonValue = json.encode(result);
return new Future.value(new ServiceExtensionResponse.result(jsonValue));
}
String get name {
return '${file.path}';
}
}
class _ProcessResourceInfo extends _IOResourceInfo {
static const String _type = '_process';
final process;
final double startedAt;
static Map<int, _ProcessResourceInfo> startedProcesses =
new Map<int, _ProcessResourceInfo>();
_ProcessResourceInfo(this.process)
: startedAt = _IOResourceInfo.timestamp,
super(_type) {
ProcessStarted(this);
}
String get name => process._path;
void stopped() {
ProcessStopped(this);
}
Map<String, dynamic> get fullValueMap => {
'type': type,
'id': id,
'name': name,
'pid': process.pid,
'startedAt': startedAt,
'arguments': process._arguments,
'workingDirectory':
process._workingDirectory == null ? '.' : process._workingDirectory,
};
static ProcessStarted(_ProcessResourceInfo info) {
assert(!startedProcesses.containsKey(info.id));
startedProcesses[info.id] = info;
}
static ProcessStopped(_ProcessResourceInfo info) {
assert(startedProcesses.containsKey(info.id));
startedProcesses.remove(info.id);
}
static Iterable<Map<String, dynamic>> getStartedProcessesList() =>
new List.from(startedProcesses.values.map((e) => e.referenceValueMap));
static Future<ServiceExtensionResponse> getStartedProcesses(
String function, Map<String, String> params) {
assert(function == 'ext.dart.io.getProcesses');
var data = {'type': '_startedprocesses', 'data': getStartedProcessesList()};
var jsonValue = json.encode(data);
return new Future.value(new ServiceExtensionResponse.result(jsonValue));
}
static Future<ServiceExtensionResponse> getProcessInfoMapById(
String function, Map<String, String> params) {
var id = int.parse(params['id']);
var result = startedProcesses.containsKey(id)
? startedProcesses[id].fullValueMap
: {};
var jsonValue = json.encode(result);
return new Future.value(new ServiceExtensionResponse.result(jsonValue));
}
}
class _SocketResourceInfo extends _ReadWriteResourceInfo {
static const String _tcpString = 'TCP';
static const String _udpString = 'UDP';
static const String _type = '_socket';
final /*_NativeSocket|*/ socket;
static Map<int, _SocketResourceInfo> openSockets =
new Map<int, _SocketResourceInfo>();
_SocketResourceInfo(this.socket) : super(_type) {
SocketOpened(this);
}
String get name {
if (socket.isListening) {
return 'listening:${socket.address.host}:${socket.port}';
}
var remote = '';
try {
var remoteHost = socket.remoteAddress.host;
var remotePort = socket.remotePort;
remote = ' -> $remoteHost:$remotePort';
} catch (e) {} // ignored if we can't get the information
return '${socket.address.host}:${socket.port}$remote';
}
static Iterable<Map<String, dynamic>> getOpenSocketsList() {
return new List.from(openSockets.values.map((e) => e.referenceValueMap));
}
Map<String, dynamic> getSocketInfoMap() {
var result = fullValueMap;
result['socketType'] = socket.isTcp ? _tcpString : _udpString;
result['listening'] = socket.isListening;
result['host'] = socket.address.host;
result['port'] = socket.port;
if (!socket.isListening) {
try {
result['remoteHost'] = socket.remoteAddress.host;
result['remotePort'] = socket.remotePort;
} catch (e) {
// UDP.
result['remotePort'] = 'NA';
result['remoteHost'] = 'NA';
}
} else {
result['remotePort'] = 'NA';
result['remoteHost'] = 'NA';
}
result['addressType'] = socket.address.type.name;
return result;
}
static Future<ServiceExtensionResponse> getSocketInfoMapByID(
String function, Map<String, String> params) {
assert(params.containsKey('id'));
var id = int.parse(params['id']);
var result =
openSockets.containsKey(id) ? openSockets[id].getSocketInfoMap() : {};
var jsonValue = json.encode(result);
return new Future.value(new ServiceExtensionResponse.result(jsonValue));
}
static Future<ServiceExtensionResponse> getOpenSockets(function, params) {
assert(function == 'ext.dart.io.getOpenSockets');
var data = {'type': '_opensockets', 'data': getOpenSocketsList()};
var jsonValue = json.encode(data);
return new Future.value(new ServiceExtensionResponse.result(jsonValue));
}
static SocketOpened(_SocketResourceInfo info) {
assert(!openSockets.containsKey(info.id));
openSockets[info.id] = info;
}
static SocketClosed(_SocketResourceInfo info) {
assert(openSockets.containsKey(info.id));
openSockets.remove(info.id);
}
}