blob: d120f94e73a40566e0382baa099d8a5b7bd6cd1a [file] [log] [blame]
// Copyright (c) 2021, 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.
import 'dart:async';
import 'package:vm_service/vm_service.dart';
class DwdsStats {
/// The time when the user starts the debugger.
DateTime? _debuggerStart;
DateTime? get debuggerStart => _debuggerStart;
/// The time when dwds launches DevTools.
DateTime? _devToolsStart;
DateTime? get devToolsStart => _devToolsStart;
/// Records and returns whether the debugger is ready.
bool _isFirstDebuggerReady = true;
bool get isFirstDebuggerReady {
final wasReady = _isFirstDebuggerReady;
_isFirstDebuggerReady = false;
return wasReady;
}
void updateLoadTime({
required DateTime debuggerStart,
required DateTime devToolsStart,
}) {
_debuggerStart = debuggerStart;
_devToolsStart = devToolsStart;
}
DwdsStats();
}
class DwdsEventKind {
static const String compilerUpdateDependencies =
'COMPILER_UPDATE_DEPENDENCIES';
static const String devtoolsLaunch = 'DEVTOOLS_LAUNCH';
static const String devToolsLoad = 'DEVTOOLS_LOAD';
static const String debuggerReady = 'DEBUGGER_READY';
static const String dwdsAttach = 'DWDS_ATTACH';
static const String dwdsLaunch = 'DWDS_LAUNCH';
static const String evaluate = 'EVALUATE';
static const String evaluateInFrame = 'EVALUATE_IN_FRAME';
static const String fullReload = 'FULL_RELOAD';
static const String getIsolate = 'GET_ISOLATE';
static const String getScripts = 'GET_SCRIPTS';
static const String getSourceReport = 'GET_SOURCE_REPORT';
static const String getVM = 'GET_VM';
static const String hotRestart = 'HOT_RESTART';
static const String httpRequestException = 'HTTP_REQUEST_EXCEPTION';
static const String resume = 'RESUME';
DwdsEventKind._();
}
class DwdsEvent {
final String type;
final Map<String, dynamic> payload;
DwdsEvent(this.type, this.payload);
DwdsEvent.dwdsLaunch({required String codeRunner, bool? isFlutterApp})
: this(DwdsEventKind.dwdsLaunch, {
'codeRunner': codeRunner,
'isFlutterApp': isFlutterApp ?? false,
});
DwdsEvent.dwdsAttach({required String client, bool? isFlutterApp})
: this(DwdsEventKind.dwdsAttach, {
'client': client,
'isFlutterApp': isFlutterApp ?? false,
});
DwdsEvent.compilerUpdateDependencies(String entrypoint)
: this(DwdsEventKind.compilerUpdateDependencies, {
'entrypoint': entrypoint,
});
DwdsEvent.devtoolsLaunch() : this(DwdsEventKind.devtoolsLaunch, {});
DwdsEvent.evaluate(String expression, Response? result)
: this(DwdsEventKind.evaluate, {
'expression': expression,
'success': result != null && result is InstanceRef,
if (result != null && result is ErrorRef) 'error': result,
});
DwdsEvent.evaluateInFrame(String expression, Response? result)
: this(DwdsEventKind.evaluateInFrame, {
'expression': expression,
'success': result != null && result is InstanceRef,
if (result != null && result is ErrorRef) 'error': result,
});
DwdsEvent.getIsolate() : this(DwdsEventKind.getIsolate, {});
DwdsEvent.getScripts() : this(DwdsEventKind.getScripts, {});
DwdsEvent.getVM() : this(DwdsEventKind.getVM, {});
DwdsEvent.resume(String? step)
: this(DwdsEventKind.resume, {if (step != null) 'step': step});
DwdsEvent.getSourceReport() : this(DwdsEventKind.getSourceReport, {});
DwdsEvent.hotRestart() : this(DwdsEventKind.hotRestart, {});
DwdsEvent.fullReload() : this(DwdsEventKind.fullReload, {});
DwdsEvent.debuggerReady(int elapsedMilliseconds, String screen)
: this(DwdsEventKind.debuggerReady, {
'elapsedMilliseconds': elapsedMilliseconds,
'screen': screen,
});
DwdsEvent.devToolsLoad(int elapsedMilliseconds, String screen)
: this(DwdsEventKind.devToolsLoad, {
'elapsedMilliseconds': elapsedMilliseconds,
'screen': screen,
});
DwdsEvent.httpRequestException(String server, String exception)
: this(DwdsEventKind.httpRequestException, {
'server': server,
'exception': exception,
});
void addException(dynamic exception) {
payload['exception'] = exception;
}
void addElapsedTime(int elapsedMilliseconds) {
payload['elapsedMilliseconds'] = elapsedMilliseconds;
}
@override
String toString() {
return 'TYPE: $type Payload: $payload';
}
}
final _eventController = StreamController<DwdsEvent>.broadcast();
/// Adds an event to the global [eventStream];
void emitEvent(DwdsEvent event) => _eventController.sink.add(event);
/// A global stream of [DwdsEvent]s.
Stream<DwdsEvent> get eventStream => _eventController.stream;
/// Call [function] and record its execution time.
///
/// Calls [event] to create the event to be recorded,
/// and appends time and exception details to it if
/// available.
Future<T> captureElapsedTime<T>(
Future<T> Function() function,
DwdsEvent Function(T? result) event,
) async {
final stopwatch = Stopwatch()..start();
T? result;
try {
return result = await function();
} catch (e) {
emitEvent(
event(null)
..addException(e)
..addElapsedTime(stopwatch.elapsedMilliseconds),
);
rethrow;
} finally {
emitEvent(event(result)..addElapsedTime(stopwatch.elapsedMilliseconds));
}
}