Skip to content

Commit 2877d78

Browse files
derekxu16Commit Queue
authored andcommitted
Reland "[VM/Service] Record timeline events representing completed microtasks"
This is a reland of commit 22021cc I showed some excerpts of Golem results to @mraleph for varying targets and constant CPU, patch A, and patch B. Those constants being: CPU: Intel Xeon patch A: revision=115869&patch=20143 patch B: revision=115876 (example results: https://golem.corp.goog/Comparison?repository=dart#targetA%3Ddart%3BmachineTypeA%3Dlinux-x64%3BrevisionA%3D115869%3BpatchA%3Dderekx-Reland---VM%2FService--Record-timeline-events-representing-completed-microtasks--4%3BtargetB%3Ddart%3BmachineTypeB%3Dlinux-x64%3BrevisionB%3D115876%3BpatchB%3DNone) He said that this change could be relanded as long as the following "RunTime as Score" regressions of the "dart" target were noted in the commit message: - StreamSingleSubTest: -3.907% (-0.8 noise) - ScheduleMicrotaskTest: -12.67% (-0.6 noise) TEST=pkg/vm_service/test/timeline_events_for_completed_microtasks_test Original change's description: > [VM/Service] Record timeline events representing completed microtasks > > TEST=pkg/vm_service/test/timeline_events_for_completed_microtasks_test > > CoreLibraryReviewExempt: This CL does not include any core library API > changes, it only modifies the implementation of microtasks (by > instrumenting them). > Change-Id: I54d886db9519c73f9e3218a9cc1c46bc9fe9acc3 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420221 > Commit-Queue: Derek Xu <[email protected]> > Reviewed-by: Ben Konyi <[email protected]> CoreLibraryReviewExempt: This CL does not include any core library API changes, it only modifies the implementation of microtasks (by instrumenting them). Change-Id: Ia7741a9bb9ab948e8d5fff1cf9abe9915e247d81 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423921 Reviewed-by: Ben Konyi <[email protected]> Commit-Queue: Derek Xu <[email protected]>
1 parent 9a31095 commit 2877d78

22 files changed

+421
-32
lines changed

pkg/vm_service/test/common/service_test_common.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
library service_test_common;
66

77
import 'dart:async';
8+
import 'dart:collection' show HashMap;
89
import 'dart:typed_data';
910

1011
import 'package:path/path.dart' as p;
1112
import 'package:test/test.dart';
1213
import 'package:vm_service/vm_service.dart';
14+
import 'package:vm_service_protos/vm_service_protos.dart' show DebugAnnotation;
1315

1416
typedef IsolateTest = Future<void> Function(
1517
VmService service,
@@ -914,3 +916,19 @@ IsolateTest testExpressionEvaluationAndAvailableVariables(
914916
}
915917
};
916918
}
919+
920+
Map<String, String> mapFromListOfDebugAnnotations(
921+
List<DebugAnnotation> debugAnnotations,
922+
) {
923+
return HashMap.fromEntries(
924+
debugAnnotations.map((a) {
925+
if (a.hasStringValue()) {
926+
return MapEntry(a.name, a.stringValue);
927+
} else if (a.hasLegacyJsonValue()) {
928+
return MapEntry(a.name, a.legacyJsonValue);
929+
} else {
930+
throw 'We should not be writing annotations without values';
931+
}
932+
}),
933+
);
934+
}

pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// VMOptions=
66
// VMOptions=--intern_strings_when_writing_perfetto_timeline
77

8-
import 'dart:collection';
98
import 'dart:convert';
109
import 'dart:developer';
1110
import 'dart:io' show Platform;
@@ -14,6 +13,7 @@ import 'package:test/test.dart';
1413
import 'package:vm_service/vm_service.dart' hide Timeline;
1514
import 'package:vm_service_protos/vm_service_protos.dart';
1615

16+
import 'common/service_test_common.dart' show mapFromListOfDebugAnnotations;
1717
import 'common/test_helper.dart';
1818

1919
void primeTimeline() {
@@ -143,22 +143,6 @@ List<TrackEvent> extractTrackEventsFromTracePackets(
143143
return result;
144144
}
145145

146-
Map<String, String> mapFromListOfDebugAnnotations(
147-
List<DebugAnnotation> debugAnnotations,
148-
) {
149-
return HashMap.fromEntries(
150-
debugAnnotations.map((a) {
151-
if (a.hasStringValue()) {
152-
return MapEntry(a.name, a.stringValue);
153-
} else if (a.hasLegacyJsonValue()) {
154-
return MapEntry(a.name, a.legacyJsonValue);
155-
} else {
156-
throw 'We should not be writing annotations without values';
157-
}
158-
}),
159-
);
160-
}
161-
162146
void checkThatAllEventsHaveIsolateNumbers(Iterable<TrackEvent> events) {
163147
for (final event in events) {
164148
final debugAnnotations =
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2025, 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+
import 'dart:async';
6+
import 'dart:convert';
7+
8+
import 'package:test/test.dart';
9+
import 'package:vm_service/vm_service.dart' hide Timeline;
10+
import 'package:vm_service_protos/vm_service_protos.dart';
11+
12+
import 'common/service_test_common.dart' show mapFromListOfDebugAnnotations;
13+
import 'common/test_helper.dart';
14+
15+
const String shortFile = 'timeline_events_for_completed_microtasks_test.dart';
16+
17+
void primeTimeline() {
18+
for (int i = 0; i < 5; i++) {
19+
scheduleMicrotask(() {});
20+
}
21+
}
22+
23+
Iterable<TrackEvent> extractTrackEventsFromTracePackets(
24+
List<TracePacket> packets,
25+
) =>
26+
packets
27+
.where((packet) => packet.hasTrackEvent())
28+
.map((packet) => packet.trackEvent);
29+
30+
final tests = <IsolateTest>[
31+
(VmService service, IsolateRef isolateRef) async {
32+
final result = await service.getPerfettoVMTimeline();
33+
34+
final trace = Trace.fromBuffer(base64Decode(result.trace!));
35+
final packets = trace.packet;
36+
final mainIsolateMicrotaskEvents =
37+
extractTrackEventsFromTracePackets(packets)
38+
.where((event) => event.name == 'Microtask')
39+
.where((event) {
40+
final debugAnnotations =
41+
mapFromListOfDebugAnnotations(event.debugAnnotations);
42+
return debugAnnotations['isolateId'] == isolateRef.id;
43+
});
44+
expect(mainIsolateMicrotaskEvents.length, greaterThanOrEqualTo(5));
45+
46+
for (final event in mainIsolateMicrotaskEvents) {
47+
final debugAnnotations =
48+
mapFromListOfDebugAnnotations(event.debugAnnotations);
49+
expect(debugAnnotations['microtaskId'], isNotNull);
50+
expect(
51+
debugAnnotations['stack trace captured when microtask was enqueued'],
52+
contains(shortFile),
53+
);
54+
}
55+
},
56+
];
57+
58+
void main([args = const <String>[]]) => runIsolateTests(
59+
args,
60+
tests,
61+
shortFile,
62+
testeeBefore: primeTimeline,
63+
extraArgs: ['--profile-microtasks', '--timeline-streams=Microtask'],
64+
);

runtime/bin/dart_embedder_api_impl.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ Dart_Isolate CreateKernelServiceIsolate(const IsolateCreationData& data,
7575
Dart_ShutdownIsolate();
7676
return nullptr;
7777
}
78-
result = bin::DartUtils::PrepareForScriptLoading(/*is_service_isolate=*/false,
79-
/*trace_loading=*/false);
78+
result = bin::DartUtils::PrepareForScriptLoading(
79+
/*is_service_isolate=*/false,
80+
/*trace_loading=*/false, /*flag_profile_microtasks=*/false);
8081
Dart_ExitScope();
8182
Dart_ExitIsolate();
8283
return kernel_isolate;

runtime/bin/dartutils.cc

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,31 @@ Dart_Handle DartUtils::PrepareCoreLibrary(Dart_Handle core_lib,
529529
}
530530

531531
Dart_Handle DartUtils::PrepareAsyncLibrary(Dart_Handle async_lib,
532-
Dart_Handle isolate_lib) {
532+
Dart_Handle isolate_lib,
533+
bool flag_profile_microtasks) {
534+
#if !defined(PRODUCT)
535+
if (flag_profile_microtasks) {
536+
Dart_Handle microtask_mirror_queue_type_name =
537+
Dart_NewStringFromCString("_MicrotaskMirrorQueue");
538+
RETURN_IF_ERROR(microtask_mirror_queue_type_name);
539+
540+
Dart_Handle microtask_mirror_queue_type =
541+
Dart_GetNonNullableType(async_lib, microtask_mirror_queue_type_name,
542+
/*number_of_type_arguments=*/0,
543+
/*type_arguments=*/nullptr);
544+
RETURN_IF_ERROR(microtask_mirror_queue_type);
545+
546+
Dart_Handle should_profile_microtasks_field_name =
547+
Dart_NewStringFromCString("_shouldProfileMicrotasks");
548+
RETURN_IF_ERROR(should_profile_microtasks_field_name);
549+
550+
Dart_Handle set_field_result =
551+
Dart_SetField(microtask_mirror_queue_type,
552+
should_profile_microtasks_field_name, Dart_True());
553+
RETURN_IF_ERROR(set_field_result);
554+
}
555+
#endif // !defined(PRODUCT)
556+
533557
Dart_Handle schedule_immediate_closure =
534558
Dart_Invoke(isolate_lib, NewString("_getIsolateScheduleImmediateClosure"),
535559
0, nullptr);
@@ -568,7 +592,8 @@ Dart_Handle DartUtils::SetupPackageConfig(const char* packages_config) {
568592
}
569593

570594
Dart_Handle DartUtils::PrepareForScriptLoading(bool is_service_isolate,
571-
bool trace_loading) {
595+
bool trace_loading,
596+
bool flag_profile_microtasks) {
572597
// First ensure all required libraries are available.
573598
Dart_Handle url = NewString(kCoreLibURL);
574599
RETURN_IF_ERROR(url);
@@ -610,7 +635,8 @@ Dart_Handle DartUtils::PrepareForScriptLoading(bool is_service_isolate,
610635
trace_loading);
611636
RETURN_IF_ERROR(result);
612637

613-
RETURN_IF_ERROR(PrepareAsyncLibrary(async_lib, isolate_lib));
638+
RETURN_IF_ERROR(
639+
PrepareAsyncLibrary(async_lib, isolate_lib, flag_profile_microtasks));
614640
RETURN_IF_ERROR(PrepareCoreLibrary(core_lib, io_lib, is_service_isolate));
615641
RETURN_IF_ERROR(PrepareIsolateLibrary(isolate_lib));
616642
RETURN_IF_ERROR(PrepareIOLibrary(io_lib));

runtime/bin/dartutils.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ class DartUtils {
178178
static Dart_Handle ReadStringFromFile(const char* filename);
179179
static Dart_Handle MakeUint8Array(const void* buffer, intptr_t length);
180180
static Dart_Handle PrepareForScriptLoading(bool is_service_isolate,
181-
bool trace_loading);
181+
bool trace_loading,
182+
bool flag_profile_microtasks);
182183
static Dart_Handle SetupPackageConfig(const char* packages_file);
183184

184185
static Dart_Handle SetupIOLibrary(const char* namespc_path,
@@ -316,7 +317,8 @@ class DartUtils {
316317
Dart_Handle io_lib,
317318
bool is_service_isolate);
318319
static Dart_Handle PrepareAsyncLibrary(Dart_Handle async_lib,
319-
Dart_Handle isolate_lib);
320+
Dart_Handle isolate_lib,
321+
bool flag_profile_microtasks);
320322
static Dart_Handle PrepareIOLibrary(Dart_Handle io_lib);
321323
static Dart_Handle PrepareIsolateLibrary(Dart_Handle isolate_lib);
322324
static Dart_Handle PrepareCLILibrary(Dart_Handle cli_lib);

runtime/bin/main_impl.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,13 @@ static Dart_Handle SetupCoreLibraries(Dart_Isolate isolate,
161161
// Prepare builtin and other core libraries for use to resolve URIs.
162162
// Set up various closures, e.g: printing, timers etc.
163163
// Set up package configuration for URI resolution.
164-
result = DartUtils::PrepareForScriptLoading(false, Options::trace_loading());
164+
#if defined(PRODUCT)
165+
bool flag_profile_microtasks = false;
166+
#else
167+
bool flag_profile_microtasks = Options::profile_microtasks();
168+
#endif // defined(PRODUCT)
169+
result = DartUtils::PrepareForScriptLoading(false, Options::trace_loading(),
170+
flag_profile_microtasks);
165171
if (Dart_IsError(result)) return result;
166172

167173
// Setup packages config if specified.

runtime/bin/main_options.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ void Options::PrintUsage() {
213213
" --timeline-streams=\"Compiler, Dart, GC, Microtask\"\n"
214214
" This set is subject to change.\n"
215215
" Please see these options for further documentation.\n"
216+
"--profile-microtasks\n"
217+
" Record information about each microtask. Information about completed\n"
218+
" microtasks will be written to the \"Microtask\" timeline stream.\n"
216219
#endif // !defined(PRODUCT)
217220
"--version\n"
218221
" Print the VM version.\n"

runtime/bin/main_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ namespace bin {
5858
V(no_serve_observatory, disable_observatory) \
5959
V(serve_observatory, enable_observatory) \
6060
V(print_dtd, print_dtd) \
61+
V(profile_microtasks, profile_microtasks) \
6162
/* The purpose of this flag is documented in */ \
6263
/* pkg/dartdev/lib/src/commands/run.dart. */ \
6364
V(resident, resident)

runtime/bin/run_vm_tests.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ static Dart_Isolate CreateIsolateAndSetup(const char* script_uri,
255255

256256
bin::DartUtils::SetOriginalWorkingDirectory();
257257
Dart_Handle result = bin::DartUtils::PrepareForScriptLoading(
258-
/*is_service_isolate=*/false, /*trace_loading=*/false);
258+
/*is_service_isolate=*/false, /*trace_loading=*/false,
259+
/*flag_profile_microtasks=*/false);
259260
CHECK_RESULT(result);
260261

261262
// Setup kernel service as the main script for this isolate.

0 commit comments

Comments
 (0)