Files
sdk/runtime/lib/developer.cc
T
Slava Egorov 0fee95455c [vm] Cleanup Profiler lifecycle
Move away from setting command line flags and calling Cleanup or Init
to reinitialize the profiler. Split starting and stopping the profiler
into separate methods and keep only one-time initialization/cleanup
in Init and Cleanup methods. Create a separate method for reconfiguring
the profiler Profiler::SetConfig.

This removes data races which were plaguing older code and makes it
simpler to reason about.

Fixes https://github.com/dart-lang/sdk/issues/62038
Fixes https://github.com/dart-lang/sdk/issues/61828

TEST=ci

Cq-Include-Trybots: luci.dart.try:vm-tsan-mac-release-arm64-try,vm-tsan-linux-release-x64-try
Change-Id: I679aa0a8724e39624e76abfad6814fe06a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/472862
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2026-01-20 05:58:38 -08:00

284 lines
8.9 KiB
C++

// Copyright (c) 2015, 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.
#include "vm/bootstrap_natives.h"
#include "include/dart_api.h"
#include "vm/debugger.h"
#include "vm/exceptions.h"
#include "vm/flags.h"
#include "vm/heap/heap.h"
#include "vm/isolate.h"
#include "vm/message.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_graph.h"
#include "vm/object_store.h"
#include "vm/service.h"
#include "vm/service_isolate.h"
#include "vm/zone_text_buffer.h"
namespace dart {
DECLARE_FLAG(int, profile_period);
// Native implementations for the dart:developer library.
DEFINE_NATIVE_ENTRY(Developer_debugger, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Bool, when, arguments->NativeArgAt(0));
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
GET_NATIVE_ARGUMENT(String, msg, arguments->NativeArgAt(1));
Debugger* debugger = isolate->debugger();
if (debugger == nullptr) {
return when.ptr();
}
if (when.value()) {
debugger->PauseDeveloper(msg);
}
#endif
return when.ptr();
}
DEFINE_NATIVE_ENTRY(Developer_inspect, 0, 1) {
GET_NATIVE_ARGUMENT(Instance, inspectee, arguments->NativeArgAt(0));
#ifndef PRODUCT
Service::SendInspectEvent(isolate, inspectee);
#endif // !PRODUCT
return inspectee.ptr();
}
DEFINE_NATIVE_ENTRY(Developer_log, 0, 8) {
#if defined(PRODUCT)
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(String, message, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Integer, timestamp, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(Integer, sequence, arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, level, arguments->NativeArgAt(3));
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(4));
GET_NATIVE_ARGUMENT(Instance, dart_zone, arguments->NativeArgAt(5));
GET_NATIVE_ARGUMENT(Instance, error, arguments->NativeArgAt(6));
GET_NATIVE_ARGUMENT(Instance, stack_trace, arguments->NativeArgAt(7));
Service::SendLogEvent(isolate, sequence.Value(), timestamp.Value(),
level.Value(), name, message, dart_zone, error,
stack_trace);
return Object::null();
#endif // PRODUCT
}
DEFINE_NATIVE_ENTRY(Developer_postEvent, 0, 2) {
#if defined(PRODUCT)
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(String, event_kind, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(String, event_data, arguments->NativeArgAt(1));
Service::SendExtensionEvent(isolate, event_kind, event_data);
return Object::null();
#endif // PRODUCT
}
DEFINE_NATIVE_ENTRY(Developer_lookupExtension, 0, 1) {
#if defined(PRODUCT)
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(0));
return isolate->LookupServiceExtensionHandler(name);
#endif // PRODUCT
}
DEFINE_NATIVE_ENTRY(Developer_registerExtension, 0, 2) {
#if defined(PRODUCT)
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Instance, handler, arguments->NativeArgAt(1));
// We don't allow service extensions to be registered for the
// service isolate. This can happen, for example, because the
// service isolate uses dart:io. If we decide that we want to start
// supporting this in the future, it will take some work.
if (!isolate->is_service_isolate()) {
isolate->RegisterServiceExtensionHandler(name, handler);
}
return Object::null();
#endif // PRODUCT
}
DEFINE_NATIVE_ENTRY(Developer_getServiceMajorVersion, 0, 0) {
#if defined(PRODUCT)
return Smi::New(0);
#else
return Smi::New(SERVICE_PROTOCOL_MAJOR_VERSION);
#endif
}
DEFINE_NATIVE_ENTRY(Developer_getServiceMinorVersion, 0, 0) {
#if defined(PRODUCT)
return Smi::New(0);
#else
return Smi::New(SERVICE_PROTOCOL_MINOR_VERSION);
#endif
}
static void SendNull(const SendPort& port) {
const Dart_Port destination_port_id = port.Id();
PortMap::PostMessage(Message::New(destination_port_id, Object::null(),
Message::kNormalPriority));
}
DEFINE_NATIVE_ENTRY(Developer_getServerInfo, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
#if defined(PRODUCT)
SendNull(port);
return Object::null();
#else
ServiceIsolate::WaitForServiceIsolateStartup();
if (!ServiceIsolate::IsRunning()) {
SendNull(port);
} else {
ServiceIsolate::RequestServerInfo(port);
}
return Object::null();
#endif
}
DEFINE_NATIVE_ENTRY(Developer_webServerControl, 0, 3) {
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
#if defined(PRODUCT)
SendNull(port);
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(Bool, enabled, arguments->NativeArgAt(1));
GET_NATIVE_ARGUMENT(Bool, silence_output, arguments->NativeArgAt(2));
ServiceIsolate::WaitForServiceIsolateStartup();
if (!ServiceIsolate::IsRunning()) {
SendNull(port);
} else {
ServiceIsolate::ControlWebServer(port, enabled.value(), silence_output);
}
return Object::null();
#endif
}
DEFINE_NATIVE_ENTRY(Developer_getIsolateIdFromSendPort, 0, 1) {
#if defined(PRODUCT)
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
int64_t port_id = port.Id();
return String::NewFormatted(ISOLATE_SERVICE_ID_FORMAT_STRING, port_id);
#endif
}
// TODO(derekxu16): Make this function accept an idZoneId.
DEFINE_NATIVE_ENTRY(Developer_getObjectId, 0, 1) {
#if defined(PRODUCT)
return Object::null();
#else
GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0));
return String::New(
isolate->EnsureDefaultServiceIdZone().GetServiceId(instance));
#endif
}
DEFINE_NATIVE_ENTRY(Developer_reachability_barrier, 0, 0) {
IsolateGroup* isolate_group = thread->isolate_group();
ASSERT(isolate_group != nullptr);
Heap* heap = isolate_group->heap();
ASSERT(heap != nullptr);
return Integer::New(heap->ReachabilityBarrier());
}
DEFINE_NATIVE_ENTRY(Developer_NativeRuntime_buildId, 0, 0) {
#if defined(DART_PRECOMPILED_RUNTIME)
IsolateGroup* isolate_group = thread->isolate_group();
ASSERT(isolate_group != nullptr);
if (const uint8_t* instructions =
isolate_group->source()->snapshot_instructions) {
const auto& build_id = OS::GetAppBuildId(instructions);
if (build_id.data != nullptr) {
ZoneTextBuffer buffer(zone);
for (intptr_t i = 0; i < build_id.len; i++) {
buffer.Printf("%2.2x", build_id.data[i]);
}
return String::New(buffer.buffer());
}
}
#endif
return String::null();
}
DEFINE_NATIVE_ENTRY(Developer_NativeRuntime_writeHeapSnapshotToFile, 0, 1) {
#if defined(DART_ENABLE_HEAP_SNAPSHOT_WRITER)
const String& filename =
String::CheckedHandle(zone, arguments->NativeArgAt(0));
bool successful = false;
{
NoActiveIsolateScope no_active_isolate_scope(thread);
FileHeapSnapshotWriter file_writer(thread, filename.ToCString(),
&successful);
HeapSnapshotWriter writer(thread, &file_writer);
writer.Write();
}
if (!successful) {
Exceptions::ThrowUnsupportedError(
"Could not create & write heapsnapshot to disc. Possibly due to "
"missing embedder functionality.");
}
#else
Exceptions::ThrowUnsupportedError(
"Heap snapshots are only supported in non-product mode.");
#endif // !defined(DART_ENABLE_HEAP_SNAPSHOT_WRITER)
return Object::null();
}
DEFINE_NATIVE_ENTRY(Developer_NativeRuntime_streamTimelineTo, 0, 5) {
#if defined(SUPPORT_TIMELINE)
const auto& recorder = String::CheckedHandle(zone, arguments->NativeArgAt(0));
const auto& path = String::CheckedHandle(zone, arguments->NativeArgAt(1));
const auto& streams = String::CheckedHandle(zone, arguments->NativeArgAt(2));
#if defined(DART_INCLUDE_PROFILER)
const auto& enable_profiler =
Bool::CheckedHandle(zone, arguments->NativeArgAt(3));
const auto& sampling_interval =
Integer::CheckedHandle(zone, arguments->NativeArgAt(4));
if (enable_profiler.value()) {
Profiler::SetConfig({
.enabled = true,
.period_us = sampling_interval.Value(),
});
}
#endif
const char* error = nullptr;
const bool ok = Timeline::StreamTo(recorder.ToCString(), path.ToCString(),
streams.ToCString(), &error);
if (!ok) {
Exceptions::ThrowUnsupportedError(error);
}
#else
Exceptions::ThrowUnsupportedError(
"Timeline support was excluded during build.");
#endif // !defined(SUPPORT_TIMELINE)
return Object::null();
}
DEFINE_NATIVE_ENTRY(Developer_NativeRuntime_stopStreamingTimeline, 0, 0) {
#if defined(SUPPORT_TIMELINE)
#if defined(DART_INCLUDE_PROFILER)
Profiler::SetConfig({
.enabled = false,
});
#endif
Timeline::StopStreaming();
#else
Exceptions::ThrowUnsupportedError(
"Timeline support was excluded during build.");
#endif // !defined(SUPPORT_TIMELINE)
return Object::null();
}
} // namespace dart