Skip to content

Commit 37ecb56

Browse files
author
Anna Gringauze
authored
Add metrics for devtools IPL (#1404)
* Add metrics for devtools IPL * Addressed CR comments * Addressed CR comments * Removed gathering stats on getObject * Updated old metrics to call _withEvent, added tests * Moved creation of events to DwdsEvents class * Add ext.dwds.sendEvent service extension to implement DEBUGGER_READY event * Generalize devtools events to support multiple screens * Support 'action' field in Devtools event payload * Make tests to fail on timeout with a descriptive message * Addressed CR comments about test structure
1 parent 36583bc commit 37ecb56

File tree

8 files changed

+454
-192
lines changed

8 files changed

+454
-192
lines changed

dwds/CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
1+
## 11.3.1-dev
2+
3+
- Add metrics measuring DevTools Initial Page Load time.
4+
- Add `ext.dwds.sendEvent` service extension to dwds so other tools
5+
can send events to the debugger.
6+
Event format:
7+
```
8+
{
9+
'type': '<event type>',
10+
'payload': {
11+
'screen: '<screen name>',
12+
'action: '<action name>',
13+
}
14+
}
15+
```
16+
Currently supported event values:
17+
```
18+
{
19+
'type: 'DevtoolsEvent',
20+
'payload': {
21+
'screen': 'debugger'
22+
'action': 'screenReady'
23+
}
24+
}
25+
```
26+
127
## 11.3.0
228

329
- Update SDK constraint to `>=2.14.0 <3.0.0`

dwds/lib/src/dwds_vm_client.dart

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class DwdsVmClient {
4040
await client.dispose();
4141
}();
4242

43-
static Future<DwdsVmClient> create(DebugService debugService) async {
43+
static Future<DwdsVmClient> create(
44+
DebugService debugService, DwdsStats dwdsStats) async {
4445
// Set up hot restart as an extension.
4546
var requestController = StreamController<Map<String, Object>>();
4647
var responseController = StreamController<Map<String, Object>>();
@@ -154,6 +155,12 @@ class DwdsVmClient {
154155
});
155156
await client.registerService('ext.dwds.screenshot', 'DWDS');
156157

158+
client.registerServiceCallback('ext.dwds.sendEvent', (event) async {
159+
_processSendEvent(event, chromeProxyService, dwdsStats);
160+
return {'result': Success().toJson()};
161+
});
162+
await client.registerService('ext.dwds.sendEvent', 'DWDS');
163+
157164
client.registerServiceCallback('ext.dwds.emitEvent', (event) async {
158165
emitEvent(DwdsEvent(
159166
event['type'] as String, event['payload'] as Map<String, dynamic>));
@@ -187,6 +194,28 @@ class DwdsVmClient {
187194
}
188195
}
189196

197+
void _processSendEvent(Map<String, dynamic> event,
198+
ChromeProxyService chromeProxyService, DwdsStats dwdsStats) {
199+
var type = event['type'] as String;
200+
var payload = event['payload'] as Map<String, dynamic>;
201+
switch (type) {
202+
case 'DevtoolsEvent':
203+
{
204+
var screen = payload == null ? null : payload['screen'];
205+
var action = payload == null ? null : payload['action'];
206+
if (screen == 'debugger' &&
207+
action == 'screenReady' &&
208+
dwdsStats.isFirstDebuggerReady()) {
209+
emitEvent(DwdsEvent.debuggerReady(DateTime.now()
210+
.difference(dwdsStats.debuggerStart)
211+
.inMilliseconds));
212+
} else {
213+
_logger.warning('Ignoring unknown event: $event');
214+
}
215+
}
216+
}
217+
}
218+
190219
Future<void> _disableBreakpointsAndResume(
191220
VmService client, ChromeProxyService chromeProxyService) async {
192221
_logger.info('Attempting to disable breakpoints and resume the isolate');

dwds/lib/src/events.dart

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,90 @@
66

77
import 'dart:async';
88

9+
import 'package:vm_service/vm_service.dart';
10+
11+
class DwdsStats {
12+
/// The time when the user starts the debugger.
13+
final DateTime debuggerStart;
14+
15+
var _isDebuggerReady = false;
16+
17+
/// Records and returns whether the debugger became ready.
18+
bool isFirstDebuggerReady() {
19+
final wasReady = _isDebuggerReady;
20+
_isDebuggerReady = true;
21+
return !wasReady;
22+
}
23+
24+
DwdsStats(this.debuggerStart);
25+
}
26+
27+
class DwdsEventKind {
28+
static const String compilerUpdateDependencies =
29+
'COMPILER_UPDATE_DEPENDENCIES';
30+
static const String devtoolsLaunch = 'DEVTOOLS_LAUNCH';
31+
static const String evaluate = 'EVALUATE';
32+
static const String evaluateInFrame = 'EVALUATE_IN_FRAME';
33+
static const String getIsolate = 'GET_ISOLATE';
34+
static const String getScripts = 'GET_SCRIPTS';
35+
static const String getSourceReport = 'GET_SOURCE_REPORT';
36+
static const String debuggerReady = 'DEBUGGER_READY';
37+
static const String getVM = 'GET_VM';
38+
static const String resume = 'RESUME';
39+
40+
DwdsEventKind._();
41+
}
42+
943
class DwdsEvent {
1044
final String type;
1145
final Map<String, dynamic> payload;
1246

1347
DwdsEvent(this.type, this.payload);
1448

49+
DwdsEvent.compilerUpdateDependencies(String entrypoint)
50+
: this(DwdsEventKind.compilerUpdateDependencies, {
51+
'entrypoint': entrypoint,
52+
});
53+
54+
DwdsEvent.devtoolsLaunch() : this(DwdsEventKind.devtoolsLaunch, {});
55+
56+
DwdsEvent.evaluate(String expression, Response result)
57+
: this(DwdsEventKind.evaluate, {
58+
'expression': expression,
59+
'success': result != null && result is InstanceRef,
60+
if (result != null && result is ErrorRef) 'error': result,
61+
});
62+
63+
DwdsEvent.evaluateInFrame(String expression, Response result)
64+
: this(DwdsEventKind.evaluateInFrame, {
65+
'expression': expression,
66+
'success': result != null && result is InstanceRef,
67+
if (result != null && result is ErrorRef) 'error': result,
68+
});
69+
70+
DwdsEvent.getIsolate() : this(DwdsEventKind.getIsolate, {});
71+
72+
DwdsEvent.getScripts() : this(DwdsEventKind.getScripts, {});
73+
74+
DwdsEvent.getVM() : this(DwdsEventKind.getVM, {});
75+
76+
DwdsEvent.resume(String step) : this(DwdsEventKind.resume, {'step': step});
77+
78+
DwdsEvent.getSourceReport() : this(DwdsEventKind.getSourceReport, {});
79+
80+
DwdsEvent.debuggerReady(int elapsedMilliseconds)
81+
: this(DwdsEventKind.debuggerReady, {
82+
'elapsedMilliseconds': elapsedMilliseconds,
83+
});
84+
85+
void addException(dynamic exception) {
86+
payload['exception'] = exception;
87+
}
88+
89+
void addElapsedTime(int elapsedMilliseconds) {
90+
payload['elapsedMilliseconds'] = elapsedMilliseconds;
91+
}
92+
1593
@override
1694
String toString() {
1795
return 'TYPE: $type Payload: $payload';

dwds/lib/src/handlers/dev_handler.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,13 @@ class DevHandler {
222222
}
223223

224224
Future<AppDebugServices> loadAppServices(AppConnection appConnection) async {
225+
var dwdsStats = DwdsStats(DateTime.now());
225226
var appId = appConnection.request.appId;
226227
if (_servicesByAppId[appId] == null) {
227228
var debugService = await _startLocalDebugService(
228229
await _chromeConnection(), appConnection);
229230
var appServices = await _createAppDebugServices(
230-
appConnection.request.appId, debugService);
231+
appConnection.request.appId, debugService, dwdsStats);
231232
unawaited(appServices.chromeProxyService.remoteDebugger.onClose.first
232233
.whenComplete(() async {
233234
await appServices.close();
@@ -434,8 +435,8 @@ class DevHandler {
434435
}
435436

436437
Future<AppDebugServices> _createAppDebugServices(
437-
String appId, DebugService debugService) async {
438-
var webdevClient = await DwdsVmClient.create(debugService);
438+
String appId, DebugService debugService, DwdsStats dwdsStats) async {
439+
var webdevClient = await DwdsVmClient.create(debugService, dwdsStats);
439440
if (_spawnDds) {
440441
await debugService.startDartDevelopmentService();
441442
}
@@ -463,6 +464,7 @@ class DevHandler {
463464
// Waits for a `DevToolsRequest` to be sent from the extension background
464465
// when the extension is clicked.
465466
extensionDebugger.devToolsRequestStream.listen((devToolsRequest) async {
467+
var dwdsStats = DwdsStats(DateTime.now());
466468
var connection = _appConnectionByAppId[devToolsRequest.appId];
467469
if (connection == null) {
468470
// TODO(grouma) - Ideally we surface this warning to the extension so
@@ -491,8 +493,11 @@ class DevHandler {
491493
expressionCompiler: _expressionCompiler,
492494
spawnDds: _spawnDds,
493495
);
494-
var appServices =
495-
await _createAppDebugServices(devToolsRequest.appId, debugService);
496+
var appServices = await _createAppDebugServices(
497+
devToolsRequest.appId,
498+
debugService,
499+
dwdsStats,
500+
);
496501
var encodedUri = await debugService.encodedUri;
497502
extensionDebugger.sendEvent('dwds.encodedUri', encodedUri);
498503
unawaited(appServices.chromeProxyService.remoteDebugger.onClose.first
@@ -516,7 +521,7 @@ class DevHandler {
516521
// TODO(grouma) - We may want to log the debugServiceUri if we don't launch
517522
// DevTools so that users can manually connect.
518523
if (!_serveDevTools) return;
519-
emitEvent(DwdsEvent('DEVTOOLS_LAUNCH', {}));
524+
emitEvent(DwdsEvent.devtoolsLaunch());
520525
await remoteDebugger.sendCommand('Target.createTarget', params: {
521526
'newWindow': true,
522527
'url': Uri(

0 commit comments

Comments
 (0)