3dc9d8dcf9
TEST=pkg/vm_service/test/get_queued_microtasks_rpc_test CoreLibraryReviewExempt: This CL does not include any core library API changes, only VM Service implementation changes within sdk/lib/vmservice/. Change-Id: Ie5488f498e4d0f3d201e3f31423fd5029b74a726 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425160 Commit-Queue: Derek Xu <derekx@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
116 lines
3.6 KiB
C++
116 lines
3.6 KiB
C++
// 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();
|
|
}
|
|
}
|
|
|
|
void MicrotaskMirrorQueue::PrintJSON(JSONStream& js) const {
|
|
ASSERT(!is_disabled_);
|
|
|
|
JSONObject jsobj_topLevel(&js);
|
|
jsobj_topLevel.AddProperty("type", "QueuedMicrotasks");
|
|
jsobj_topLevel.AddProperty64("timestamp", OS::GetCurrentTimeMicros());
|
|
|
|
JSONArray jsarr(&jsobj_topLevel, "microtasks");
|
|
queue_.ForEach([&jsarr](const MicrotaskMirrorQueueEntry& entry) {
|
|
JSONObject jsobj_entry(&jsarr);
|
|
jsobj_entry.AddProperty("type", "Microtask");
|
|
jsobj_entry.AddProperty("id", entry.id());
|
|
jsobj_entry.AddProperty("stackTrace", entry.stack_trace().get());
|
|
});
|
|
}
|
|
|
|
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)
|