Revert "[VM/Service] Record timeline events representing completed microtasks" This reverts commit 22021cc12014feffd33f9a3d50e37447a980b99b. Reason for revert: I didn't gate stacktrace collection behind `--profile-microtasks` correctly 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 <derekx@google.com> > Reviewed-by: Ben Konyi <bkonyi@google.com> No-Presubmit: true No-Tree-Checks: true No-Try: true Change-Id: I82b4a49764956254c51bd3abfee0cb3cd1b329d2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423920 Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Reviewed-by: Jessy Yameogo <yjessy@google.com> Commit-Queue: Jessy Yameogo <yjessy@google.com> Auto-Submit: Derek Xu <derekx@google.com>
diff --git a/pkg/vm_service/test/common/service_test_common.dart b/pkg/vm_service/test/common/service_test_common.dart index fc208ac..ae04eb2 100644 --- a/pkg/vm_service/test/common/service_test_common.dart +++ b/pkg/vm_service/test/common/service_test_common.dart
@@ -5,13 +5,11 @@ library service_test_common; import 'dart:async'; -import 'dart:collection' show HashMap; import 'dart:typed_data'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; -import 'package:vm_service_protos/vm_service_protos.dart' show DebugAnnotation; typedef IsolateTest = Future<void> Function( VmService service, @@ -916,19 +914,3 @@ } }; } - -Map<String, String> mapFromListOfDebugAnnotations( - List<DebugAnnotation> debugAnnotations, -) { - return HashMap.fromEntries( - debugAnnotations.map((a) { - if (a.hasStringValue()) { - return MapEntry(a.name, a.stringValue); - } else if (a.hasLegacyJsonValue()) { - return MapEntry(a.name, a.legacyJsonValue); - } else { - throw 'We should not be writing annotations without values'; - } - }), - ); -}
diff --git a/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart b/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart index 850b649..dcd2c7b 100644 --- a/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart +++ b/pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart
@@ -5,6 +5,7 @@ // VMOptions= // VMOptions=--intern_strings_when_writing_perfetto_timeline +import 'dart:collection'; import 'dart:convert'; import 'dart:developer'; import 'dart:io' show Platform; @@ -13,7 +14,6 @@ import 'package:vm_service/vm_service.dart' hide Timeline; import 'package:vm_service_protos/vm_service_protos.dart'; -import 'common/service_test_common.dart' show mapFromListOfDebugAnnotations; import 'common/test_helper.dart'; void primeTimeline() { @@ -143,6 +143,22 @@ return result; } +Map<String, String> mapFromListOfDebugAnnotations( + List<DebugAnnotation> debugAnnotations, +) { + return HashMap.fromEntries( + debugAnnotations.map((a) { + if (a.hasStringValue()) { + return MapEntry(a.name, a.stringValue); + } else if (a.hasLegacyJsonValue()) { + return MapEntry(a.name, a.legacyJsonValue); + } else { + throw 'We should not be writing annotations without values'; + } + }), + ); +} + void checkThatAllEventsHaveIsolateNumbers(Iterable<TrackEvent> events) { for (final event in events) { final debugAnnotations =
diff --git a/pkg/vm_service/test/timeline_events_for_completed_microtasks_test.dart b/pkg/vm_service/test/timeline_events_for_completed_microtasks_test.dart deleted file mode 100644 index 8dfe37e..0000000 --- a/pkg/vm_service/test/timeline_events_for_completed_microtasks_test.dart +++ /dev/null
@@ -1,64 +0,0 @@ -// Copyright (c) 2025, 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 'dart:convert'; - -import 'package:test/test.dart'; -import 'package:vm_service/vm_service.dart' hide Timeline; -import 'package:vm_service_protos/vm_service_protos.dart'; - -import 'common/service_test_common.dart' show mapFromListOfDebugAnnotations; -import 'common/test_helper.dart'; - -const String shortFile = 'timeline_events_for_completed_microtasks_test.dart'; - -void primeTimeline() { - for (int i = 0; i < 5; i++) { - scheduleMicrotask(() {}); - } -} - -Iterable<TrackEvent> extractTrackEventsFromTracePackets( - List<TracePacket> packets, -) => - packets - .where((packet) => packet.hasTrackEvent()) - .map((packet) => packet.trackEvent); - -final tests = <IsolateTest>[ - (VmService service, IsolateRef isolateRef) async { - final result = await service.getPerfettoVMTimeline(); - - final trace = Trace.fromBuffer(base64Decode(result.trace!)); - final packets = trace.packet; - final mainIsolateMicrotaskEvents = - extractTrackEventsFromTracePackets(packets) - .where((event) => event.name == 'Microtask') - .where((event) { - final debugAnnotations = - mapFromListOfDebugAnnotations(event.debugAnnotations); - return debugAnnotations['isolateId'] == isolateRef.id; - }); - expect(mainIsolateMicrotaskEvents.length, greaterThanOrEqualTo(5)); - - for (final event in mainIsolateMicrotaskEvents) { - final debugAnnotations = - mapFromListOfDebugAnnotations(event.debugAnnotations); - expect(debugAnnotations['microtaskId'], isNotNull); - expect( - debugAnnotations['stack trace captured when microtask was enqueued'], - contains(shortFile), - ); - } - }, -]; - -void main([args = const <String>[]]) => runIsolateTests( - args, - tests, - shortFile, - testeeBefore: primeTimeline, - extraArgs: ['--profile-microtasks', '--timeline-streams=Microtask'], - );
diff --git a/runtime/lib/async.cc b/runtime/lib/async.cc index 883ec1d..538c20e 100644 --- a/runtime/lib/async.cc +++ b/runtime/lib/async.cc
@@ -5,17 +5,12 @@ #include "vm/bootstrap_natives.h" #include "vm/debugger.h" #include "vm/exceptions.h" -#include "vm/flags.h" -#include "vm/microtask_mirror_queues.h" #include "vm/native_entry.h" #include "vm/object_store.h" #include "vm/runtime_entry.h" namespace dart { -// This flag is defined in "vm/microtask_mirror_queues.cc". -DECLARE_FLAG(bool, profile_microtasks); - DEFINE_NATIVE_ENTRY(AsyncStarMoveNext_debuggerStepCheck, 0, 1) { #if !defined(PRODUCT) GET_NON_NULL_NATIVE_ARGUMENT(Closure, generator, arguments->NativeArgAt(0)); @@ -61,41 +56,4 @@ return closure.ptr(); } -DEFINE_NATIVE_ENTRY(MicrotaskMirrorQueue_onScheduleAsyncCallback, 0, 1) { -#if !defined(PRODUCT) - if (FLAG_profile_microtasks) { - GET_NON_NULL_NATIVE_ARGUMENT(StackTrace, stack_trace, - arguments->NativeArgAt(0)); - MicrotaskMirrorQueues::GetQueue(static_cast<int64_t>(isolate->main_port())) - ->OnScheduleAsyncCallback(stack_trace); - } -#endif // !defined(PRODUCT) - return Object::null(); -} - -DEFINE_NATIVE_ENTRY(MicrotaskMirrorQueue_onSchedulePriorityAsyncCallback, - 0, - 0) { -#if !defined(PRODUCT) - if (FLAG_profile_microtasks) { - MicrotaskMirrorQueues::GetQueue(static_cast<int64_t>(isolate->main_port())) - ->OnSchedulePriorityAsyncCallback(); - } -#endif // !defined(PRODUCT) - return Object::null(); -} - -DEFINE_NATIVE_ENTRY(MicrotaskMirrorQueue_onAsyncCallbackComplete, 0, 2) { -#if !defined(PRODUCT) - if (FLAG_profile_microtasks) { - GET_NON_NULL_NATIVE_ARGUMENT(Integer, start_time, - arguments->NativeArgAt(0)); - GET_NON_NULL_NATIVE_ARGUMENT(Integer, end_time, arguments->NativeArgAt(1)); - MicrotaskMirrorQueues::GetQueue(static_cast<int64_t>(isolate->main_port())) - ->OnAsyncCallbackComplete(start_time.Value(), end_time.Value()); - } -#endif // !defined(PRODUCT) - return Object::null(); -} - } // namespace dart
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 64c2096..1562fcc 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h
@@ -149,9 +149,6 @@ V(Timeline_getTraceClock, 0) \ V(Timeline_isDartStreamEnabled, 0) \ V(Timeline_reportTaskEvent, 5) \ - V(MicrotaskMirrorQueue_onScheduleAsyncCallback, 1) \ - V(MicrotaskMirrorQueue_onSchedulePriorityAsyncCallback, 0) \ - V(MicrotaskMirrorQueue_onAsyncCallbackComplete, 2) \ V(TypedDataBase_length, 1) \ V(TypedDataBase_setClampedRange, 5) \ V(TypedData_GetFloat32, 2) \
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index d17a7a8..093cbef 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc
@@ -33,7 +33,6 @@ #include "vm/kernel_isolate.h" #include "vm/message_handler.h" #include "vm/metrics.h" -#include "vm/microtask_mirror_queues.h" #include "vm/native_entry.h" #include "vm/native_message_handler.h" #include "vm/object.h" @@ -765,7 +764,6 @@ } Timeline::Cleanup(); #endif - NOT_IN_PRODUCT(MicrotaskMirrorQueues::CleanUp()); Zone::Cleanup(); Random::Cleanup(); // Delete the current thread's TLS and set it's TLS to null.
diff --git a/runtime/vm/microtask_mirror_queues.cc b/runtime/vm/microtask_mirror_queues.cc deleted file mode 100644 index 74a0156..0000000 --- a/runtime/vm/microtask_mirror_queues.cc +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright (c) 2025, 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. - -#if !defined(PRODUCT) - -#include "vm/microtask_mirror_queues.h" - -#include <utility> - -#include "platform/hashmap.h" -#include "vm/flags.h" -#include "vm/object.h" -#include "vm/timeline.h" - -namespace dart { - -DEFINE_FLAG( - bool, - profile_microtasks, - false, - "Record information about each microtask. Information about completed " - "microtasks will be written to the \"Microtask\" timeline stream."); - -MicrotaskMirrorQueue* MicrotaskMirrorQueues::GetQueue(int64_t isolate_id) { - void* key = reinterpret_cast<void*>(isolate_id); - const intptr_t hash = Utils::WordHash(isolate_id); - - MutexLocker ml(&isolate_id_to_queue_lock_); - - SimpleHashMap::Entry* entry = isolate_id_to_queue_.Lookup(key, hash, true); - if (entry->value == nullptr) { - entry->value = new MicrotaskMirrorQueue(); - } - return static_cast<MicrotaskMirrorQueue*>(entry->value); -} - -void MicrotaskMirrorQueue::OnScheduleAsyncCallback(const StackTrace& st) { - if (is_disabled_) { - return; - } - - queue_.PushBack(MicrotaskMirrorQueueEntry( - next_available_id_++, CStringUniquePtr(Utils::StrDup(st.ToCString())))); -} - -void MicrotaskMirrorQueue::OnSchedulePriorityAsyncCallback() { - // If this function is called, it means that the microtask queue can no longer - // be accurately modeled by a |ListQueue|. This only ever gets called when an - // exception goes unhandled, so we just handle the situation by disabling all - // further reads from / writes to this queue. - is_disabled_ = true; -} - -void MicrotaskMirrorQueue::OnAsyncCallbackComplete(int64_t start_time, - int64_t end_time) { - if (is_disabled_) { - return; - } - - ASSERT(queue_.Length() >= 1); - MicrotaskMirrorQueueEntry&& front = std::move(queue_.PopFront()); - - TimelineStream* stream = Timeline::GetMicrotaskStream(); - ASSERT(stream != nullptr); - TimelineEvent* event = stream->StartEvent(); - if (event != nullptr) { - if (start_time < end_time) { - event->Duration("Microtask", start_time, end_time); - } else { - event->Instant("Microtask", start_time); - } - - event->SetNumArguments(2); - event->FormatArgument(0, "microtaskId", "%" Pd, front.id()); - event->SetArgument(1, "stack trace captured when microtask was enqueued", - front.ReleaseStackTrace()); - event->Complete(); - } -} - -Mutex MicrotaskMirrorQueues::isolate_id_to_queue_lock_; - -SimpleHashMap MicrotaskMirrorQueues::isolate_id_to_queue_( - &SimpleHashMap::SamePointerValue, - MicrotaskMirrorQueues::kIsolateIdToQueueInitialCapacity); - -void MicrotaskMirrorQueues::CleanUp() { - for (SimpleHashMap::Entry* entry = isolate_id_to_queue_.Start(); - entry != nullptr; entry = isolate_id_to_queue_.Next(entry)) { - MicrotaskMirrorQueue* value = - static_cast<MicrotaskMirrorQueue*>(entry->value); - delete value; - } -} - -} // namespace dart - -#endif // !defined(PRODUCT)
diff --git a/runtime/vm/microtask_mirror_queues.h b/runtime/vm/microtask_mirror_queues.h deleted file mode 100644 index f47192f..0000000 --- a/runtime/vm/microtask_mirror_queues.h +++ /dev/null
@@ -1,78 +0,0 @@ -// Copyright (c) 2025, 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. - -#ifndef RUNTIME_VM_MICROTASK_MIRROR_QUEUES_H_ -#define RUNTIME_VM_MICROTASK_MIRROR_QUEUES_H_ - -#include <utility> - -#include "platform/hashmap.h" -#include "platform/list_queue.h" -#include "platform/synchronization.h" -#include "platform/utils.h" -#include "vm/stack_frame.h" - -namespace dart { - -class MicrotaskMirrorQueueEntry : public ValueObject { - public: - static constexpr intptr_t kInvalidId = -1; - - MicrotaskMirrorQueueEntry() : id_(kInvalidId), stack_trace_(nullptr) {} - MicrotaskMirrorQueueEntry(intptr_t id, CStringUniquePtr&& st) - : id_(id), stack_trace_(std::move(st)) {} - - void operator=(MicrotaskMirrorQueueEntry&& other) { - id_ = other.id_; - stack_trace_.swap(other.stack_trace_); - } - - intptr_t id() const { return id_; } - CStringUniquePtr const& stack_trace() const { return stack_trace_; } - /// Releases ownership of the stack trace string that this entry is holding. - char* ReleaseStackTrace() { return stack_trace_.release(); } - - private: - intptr_t id_; - CStringUniquePtr stack_trace_; -}; - -/// A queue that mirrors the microtask queue of an isolate. This allows the VM -/// Service to return information about microtasks. -class MicrotaskMirrorQueue { - public: - MicrotaskMirrorQueue() - : is_disabled_(false), queue_(), next_available_id_(0) {} - - void OnScheduleAsyncCallback(const StackTrace& st); - void OnSchedulePriorityAsyncCallback(); - void OnAsyncCallbackComplete(int64_t start_time, int64_t end_time); - - private: - bool is_disabled_; - ListQueue<MicrotaskMirrorQueueEntry> queue_; - // The unique ID that will be assigned to the next |MicrotaskMirrorQueueEntry| - // added to |queue_|. - intptr_t next_available_id_; - - DISALLOW_COPY_AND_ASSIGN(MicrotaskMirrorQueue); -}; - -/// A wrapper around a map from isolate IDs to |MicrotaskMirrorQueue|s. -class MicrotaskMirrorQueues : public AllStatic { - public: - static MicrotaskMirrorQueue* GetQueue(int64_t isolate_id); - static void CleanUp(); - - private: - static constexpr intptr_t kIsolateIdToQueueInitialCapacity = 1 << 4; // 16 - static Mutex isolate_id_to_queue_lock_; - static SimpleHashMap isolate_id_to_queue_; - - DISALLOW_COPY_AND_ASSIGN(MicrotaskMirrorQueues); -}; - -} // namespace dart - -#endif // RUNTIME_VM_MICROTASK_MIRROR_QUEUES_H_
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index 70079cb..a983cbc 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni
@@ -177,8 +177,6 @@ "message_snapshot.h", "metrics.cc", "metrics.h", - "microtask_mirror_queues.cc", - "microtask_mirror_queues.h", "native_arguments.h", "native_entry.cc", "native_entry.h",
diff --git a/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart b/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart index 3fc50aa..dd5bb1a 100644 --- a/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart +++ b/sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart
@@ -5,24 +5,6 @@ part of "async_patch.dart"; @patch -abstract final class _MicrotaskMirrorQueue { - @patch - @pragma("vm:external-name", "MicrotaskMirrorQueue_onScheduleAsyncCallback") - external static void _onScheduleAsyncCallback(StackTrace st); - - @patch - @pragma( - "vm:external-name", - "MicrotaskMirrorQueue_onSchedulePriorityAsyncCallback", - ) - external static void _onSchedulePriorityAsyncCallback(); - - @patch - @pragma("vm:external-name", "MicrotaskMirrorQueue_onAsyncCallbackComplete") - external static void _onAsyncCallbackComplete(int startTime, int endTime); -} - -@patch class _AsyncRun { @patch static void _scheduleImmediate(void callback()) {
diff --git a/sdk/lib/async/async.dart b/sdk/lib/async/async.dart index 7808477..af2f4c3 100644 --- a/sdk/lib/async/async.dart +++ b/sdk/lib/async/async.dart
@@ -104,7 +104,6 @@ library dart.async; import "dart:collection" show HashMap; -import 'dart:developer' show Timeline; import "dart:_internal" show CastStream,
diff --git a/sdk/lib/async/schedule_microtask.dart b/sdk/lib/async/schedule_microtask.dart index 66db6a0..25e7c03 100644 --- a/sdk/lib/async/schedule_microtask.dart +++ b/sdk/lib/async/schedule_microtask.dart
@@ -37,17 +37,7 @@ var next = entry.next; _nextCallback = next; if (next == null) _lastCallback = null; - if (const bool.fromEnvironment("dart.vm.product")) { - (entry.callback)(); - } else { - final callbackStartTime = Timeline.now; - (entry.callback)(); - final callbackEndTime = Timeline.now; - _MicrotaskMirrorQueue._onAsyncCallbackComplete( - callbackStartTime, - callbackEndTime, - ); - } + (entry.callback)(); } } @@ -72,9 +62,6 @@ /// microtasks, but as part of the current system event. void _scheduleAsyncCallback(_AsyncCallback callback) { _AsyncCallbackEntry newEntry = _AsyncCallbackEntry(callback); - if (!const bool.fromEnvironment("dart.vm.product")) { - _MicrotaskMirrorQueue._onScheduleAsyncCallback(StackTrace.current); - } _AsyncCallbackEntry? lastCallback = _lastCallback; if (lastCallback == null) { _nextCallback = _lastCallback = newEntry; @@ -100,9 +87,6 @@ return; } _AsyncCallbackEntry entry = _AsyncCallbackEntry(callback); - if (!const bool.fromEnvironment("dart.vm.product")) { - _MicrotaskMirrorQueue._onSchedulePriorityAsyncCallback(); - } _AsyncCallbackEntry? lastPriorityCallback = _lastPriorityCallback; if (lastPriorityCallback == null) { entry.next = _nextCallback; @@ -164,15 +148,6 @@ Zone.current.scheduleMicrotask(Zone.current.bindCallbackGuarded(callback)); } -abstract final class _MicrotaskMirrorQueue { - // The VM implementations of these methods are in - // sdk/lib/_internal/vm/lib/schedule_microtask_patch.dart. - - static void _onScheduleAsyncCallback(StackTrace st) {} - static void _onSchedulePriorityAsyncCallback() {} - static void _onAsyncCallbackComplete(int startTime, int endTime) {} -} - class _AsyncRun { /// Schedule the given callback before any other event in the event-loop. external static void _scheduleImmediate(void Function() callback);