blob: 7fc13a1de68d83c9039103489328d03115582be8 [file] [log] [blame]
// Copyright (c) 2013, 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 <memory>
#include <utility>
#include "vm/dart.h"
#include "platform/thread_sanitizer.h"
#include "platform/unwinding_records.h"
#include "vm/app_snapshot.h"
#include "vm/code_observers.h"
#include "vm/compiler/runtime_offsets_extracted.h"
#include "vm/compiler/runtime_offsets_list.h"
#include "vm/cpu.h"
#include "vm/dart_api_state.h"
#include "vm/dart_entry.h"
#include "vm/debugger.h"
#if defined(DART_PRECOMPILED_RUNTIME) && defined(DART_TARGET_OS_LINUX)
#include "vm/elf.h"
#endif
#include "vm/ffi_callback_metadata.h"
#include "vm/flags.h"
#include "vm/handles.h"
#include "vm/heap/become.h"
#include "vm/heap/freelist.h"
#include "vm/heap/heap.h"
#include "vm/heap/pointer_block.h"
#include "vm/isolate.h"
#include "vm/isolate_reload.h"
#include "vm/kernel_isolate.h"
#include "vm/message_handler.h"
#include "vm/metrics.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_id_ring.h"
#include "vm/object_store.h"
#include "vm/port.h"
#include "vm/profiler.h"
#include "vm/raw_object_fields.h"
#include "vm/reverse_pc_lookup_cache.h"
#include "vm/service_isolate.h"
#include "vm/simulator.h"
#include "vm/snapshot.h"
#include "vm/stack_frame.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
#include "vm/tags.h"
#include "vm/thread_interrupter.h"
#include "vm/thread_pool.h"
#include "vm/timeline.h"
#include "vm/unwinding_records.h"
#include "vm/virtual_memory.h"
#include "vm/zone.h"
namespace dart {
DECLARE_FLAG(bool, print_class_table);
DEFINE_FLAG(bool, trace_shutdown, false, "Trace VM shutdown on stderr");
Isolate* Dart::vm_isolate_ = nullptr;
int64_t Dart::start_time_micros_ = 0;
ThreadPool* Dart::thread_pool_ = nullptr;
DebugInfo* Dart::pprof_symbol_generator_ = nullptr;
ReadOnlyHandles* Dart::predefined_handles_ = nullptr;
Snapshot::Kind Dart::vm_snapshot_kind_ = Snapshot::kInvalid;
Dart_ThreadStartCallback Dart::thread_start_callback_ = nullptr;
Dart_ThreadExitCallback Dart::thread_exit_callback_ = nullptr;
Dart_FileOpenCallback Dart::file_open_callback_ = nullptr;
Dart_FileReadCallback Dart::file_read_callback_ = nullptr;
Dart_FileWriteCallback Dart::file_write_callback_ = nullptr;
Dart_FileCloseCallback Dart::file_close_callback_ = nullptr;
Dart_EntropySource Dart::entropy_source_callback_ = nullptr;
Dart_DwarfStackTraceFootnoteCallback Dart::dwarf_stacktrace_footnote_callback_ =
nullptr;
// Structure for managing read-only global handles allocation used for
// creating global read-only handles that are pre created and initialized
// for use across all isolates. Having these global pre created handles
// stored in the vm isolate ensures that we don't constantly create and
// destroy handles for read-only objects referred in the VM code
// (e.g: symbols, null object, empty array etc.)
// The ReadOnlyHandles C++ Wrapper around VMHandles which is a ValueObject is
// to ensure that the handles area is not trashed by automatic running of C++
// static destructors when 'exit()" is called by any isolate. There might be
// other isolates running at the same time and trashing the handles area will
// have unintended consequences.
class ReadOnlyHandles {
public:
ReadOnlyHandles() {}
private:
VMHandles handles_;
LocalHandles api_handles_;
friend class Dart;
DISALLOW_COPY_AND_ASSIGN(ReadOnlyHandles);
};
class DartInitializationState : public AllStatic {
public:
static bool SetInitializing() {
ASSERT(in_use_count_.load() == 0);
uint8_t expected = kUnInitialized;
return state_.compare_exchange_strong(expected, kInitializing);
}
static void ResetInitializing() {
ASSERT(in_use_count_.load() == 0);
uint8_t expected = kInitializing;
bool result = state_.compare_exchange_strong(expected, kUnInitialized);
ASSERT(result);
}
static void SetInitialized() {
ASSERT(in_use_count_.load() == 0);
uint8_t expected = kInitializing;
bool result = state_.compare_exchange_strong(expected, kInitialized);
ASSERT(result);
}
static bool IsInitialized() { return state_.load() == kInitialized; }
static bool SetCleaningup() {
uint8_t expected = kInitialized;
return state_.compare_exchange_strong(expected, kCleaningup);
}
static void SetUnInitialized() {
while (in_use_count_.load() > 0) {
OS::Sleep(1); // Sleep for 1 millis waiting for it to not be in use.
}
uint8_t expected = kCleaningup;
bool result = state_.compare_exchange_strong(expected, kUnInitialized);
ASSERT(result);
}
static bool SetInUse() {
if (state_.load() != kInitialized) {
return false;
}
in_use_count_ += 1;
return true;
}
static void ResetInUse() {
uint8_t value = state_.load();
ASSERT((value == kInitialized) || (value == kCleaningup));
in_use_count_ -= 1;
}
private:
static constexpr uint8_t kUnInitialized = 0;
static constexpr uint8_t kInitializing = 1;
static constexpr uint8_t kInitialized = 2;
static constexpr uint8_t kCleaningup = 3;
static std::atomic<uint8_t> state_;
static std::atomic<uint64_t> in_use_count_;
};
std::atomic<uint8_t> DartInitializationState::state_ = {kUnInitialized};
std::atomic<uint64_t> DartInitializationState::in_use_count_ = {0};
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
static void CheckOffsets() {
#if !defined(IS_SIMARM_HOST64)
// These offsets are embedded in precompiled instructions. We need the
// compiler and the runtime to agree.
bool ok = true;
#define CHECK_OFFSET(expr, offset) \
if ((expr) != (offset)) { \
OS::PrintErr("%s got %" Pd ", %s expected %" Pd "\n", #expr, \
static_cast<intptr_t>(expr), #offset, \
static_cast<intptr_t>(offset)); \
ok = false; \
}
// No consistency checks needed for these constructs.
#define CHECK_ARRAY_SIZEOF(Class, Name, ElementOffset)
#define CHECK_PAYLOAD_SIZEOF(Class, Name, HeaderSize)
#if defined(DART_PRECOMPILED_RUNTIME)
#define CHECK_FIELD(Class, Name) \
CHECK_OFFSET(Class::Name(), AOT_##Class##_##Name);
#define CHECK_ARRAY(Class, Name) \
CHECK_OFFSET(Class::ArrayTraits::elements_start_offset(), \
AOT_##Class##_elements_start_offset); \
CHECK_OFFSET(Class::ArrayTraits::kElementSize, AOT_##Class##_element_size)
#define CHECK_SIZEOF(Class, Name, What) \
CHECK_OFFSET(sizeof(What), AOT_##Class##_##Name);
#define CHECK_RANGE(Class, Getter, Type, First, Last, Filter) \
for (intptr_t i = static_cast<intptr_t>(First); \
i <= static_cast<intptr_t>(Last); i++) { \
if (Filter(static_cast<Type>(i))) { \
CHECK_OFFSET(Class::Getter(static_cast<Type>(i)), \
AOT_##Class##_##Getter[i]); \
} \
}
#define CHECK_CONSTANT(Class, Name) \
CHECK_OFFSET(Class::Name, AOT_##Class##_##Name);
#else
#define CHECK_FIELD(Class, Name) CHECK_OFFSET(Class::Name(), Class##_##Name);
#define CHECK_ARRAY(Class, Name) \
CHECK_OFFSET(Class::ArrayTraits::elements_start_offset(), \
Class##_elements_start_offset); \
CHECK_OFFSET(Class::ArrayTraits::kElementSize, Class##_element_size);
#if defined(DART_PRECOMPILER)
// Objects in precompiler may have extra fields only used during
// precompilation (such as Class::target_instance_size_in_words_),
// so size of objects in precompiler doesn't necessarily match
// size of objects at run time.
#define CHECK_SIZEOF(Class, Name, What)
#else
#define CHECK_SIZEOF(Class, Name, What) \
CHECK_OFFSET(sizeof(What), Class##_##Name);
#endif // defined(DART_PRECOMPILER)
#define CHECK_RANGE(Class, Getter, Type, First, Last, Filter) \
for (intptr_t i = static_cast<intptr_t>(First); \
i <= static_cast<intptr_t>(Last); i++) { \
if (Filter(static_cast<Type>(i))) { \
CHECK_OFFSET(Class::Getter(static_cast<Type>(i)), Class##_##Getter[i]); \
} \
}
#define CHECK_CONSTANT(Class, Name) CHECK_OFFSET(Class::Name, Class##_##Name);
#endif // defined(DART_PRECOMPILED_RUNTIME)
COMMON_OFFSETS_LIST(CHECK_FIELD, CHECK_ARRAY, CHECK_SIZEOF,
CHECK_ARRAY_SIZEOF, CHECK_PAYLOAD_SIZEOF, CHECK_RANGE,
CHECK_CONSTANT)
NOT_IN_PRECOMPILED_RUNTIME(JIT_OFFSETS_LIST(
CHECK_FIELD, CHECK_ARRAY, CHECK_SIZEOF, CHECK_ARRAY_SIZEOF,
CHECK_PAYLOAD_SIZEOF, CHECK_RANGE, CHECK_CONSTANT))
ONLY_IN_PRECOMPILED(AOT_OFFSETS_LIST(CHECK_FIELD, CHECK_ARRAY, CHECK_SIZEOF,
CHECK_ARRAY_SIZEOF, CHECK_PAYLOAD_SIZEOF,
CHECK_RANGE, CHECK_CONSTANT))
if (!ok) {
FATAL(
"CheckOffsets failed. Try updating offsets by running "
"./tools/run_offsets_extractor.dart");
}
#undef CHECK_FIELD
#undef CHECK_ARRAY
#undef CHECK_ARRAY_STRUCTFIELD
#undef CHECK_SIZEOF
#undef CHECK_RANGE
#undef CHECK_CONSTANT
#undef CHECK_OFFSET
#undef CHECK_PAYLOAD_SIZEOF
#endif // !defined(IS_SIMARM_HOST64)
}
#endif // defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
char* Dart::DartInit(const Dart_InitializeParams* params) {
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
CheckOffsets();
#elif defined(ARCH_IS_64_BIT) != defined(TARGET_ARCH_IS_64_BIT)
return Utils::StrDup(
"JIT cannot simulate target architecture with different word size than "
"host");
#endif
#if defined(DART_HOST_OS_MACOS) && !defined(DART_HOST_OS_IOS)
char* error = CheckIsAtLeastMinRequiredMacOSVersion();
if (error != nullptr) {
return error;
}
#endif
if (!Flags::Initialized()) {
return Utils::StrDup("VM initialization failed-VM Flags not initialized.");
}
if (vm_isolate_ != nullptr) {
return Utils::StrDup("VM initialization is in an inconsistent state.");
}
const Snapshot* snapshot = nullptr;
if (params->vm_snapshot_data != nullptr) {
snapshot = Snapshot::SetupFromBuffer(params->vm_snapshot_data);
if (snapshot == nullptr) {
return Utils::StrDup("Invalid vm isolate snapshot seen");
}
}
// We are initializing the VM. We will take the VM-global flags used
// during snapshot generation time also at runtime (this avoids the need
// for the embedder to pass the same flags used during snapshot generation
// also to the runtime).
if (snapshot != nullptr) {
char* error =
SnapshotHeaderReader::InitializeGlobalVMFlagsFromSnapshot(snapshot);
if (error != nullptr) {
return error;
}
}
FrameLayout::Init();
set_thread_start_callback(params->thread_start);
set_thread_exit_callback(params->thread_exit);
SetFileCallbacks(params->file_open, params->file_read, params->file_write,
params->file_close);
set_entropy_source_callback(params->entropy_source);
OS::Init();
NOT_IN_PRODUCT(CodeObservers::Init());
if (params->code_observer != nullptr) {
NOT_IN_PRODUCT(CodeObservers::RegisterExternal(*params->code_observer));
}
start_time_micros_ = OS::GetCurrentMonotonicMicros();
#if defined(DART_HOST_OS_FUCHSIA)
VirtualMemory::Init(params->vmex_resource);
#else
VirtualMemory::Init();
#endif
#if defined(DART_PRECOMPILED_RUNTIME) && defined(DART_TARGET_OS_LINUX)
if (VirtualMemory::PageSize() > kElfPageSize) {
return Utils::SCreate(
"Incompatible page size for AOT compiled ELF: expected at most %" Pd
", got %" Pd "",
kElfPageSize, VirtualMemory::PageSize());
}
#endif
OSThread::Init();
Random::Init();
Zone::Init();
#if defined(SUPPORT_TIMELINE)
Timeline::Init();
TimelineBeginEndScope tbes(Timeline::GetVMStream(), "Dart::Init");
#endif
IsolateGroup::Init();
Isolate::InitVM();
UserTags::Init();
PortMap::Init();
Service::Init();
FreeListElement::Init();
ForwardingCorpse::Init();
Api::Init();
NativeSymbolResolver::Init();
Page::Init();
StoreBuffer::Init();
MarkingStack::Init();
TargetCPUFeatures::Init();
FfiCallbackMetadata::Init();
#if defined(USING_SIMULATOR)
Simulator::Init();
#endif
// Create the read-only handles area.
ASSERT(predefined_handles_ == nullptr);
predefined_handles_ = new ReadOnlyHandles();
// Create the VM isolate and finish the VM initialization.
ASSERT(thread_pool_ == nullptr);
thread_pool_ = new ThreadPool();
{
ASSERT(vm_isolate_ == nullptr);
ASSERT(Flags::Initialized());
const bool is_vm_isolate = true;
// Setup default flags for the VM isolate.
Dart_IsolateFlags api_flags;
Isolate::FlagsInitialize(&api_flags);
api_flags.is_system_isolate = true;
// We make a fake [IsolateGroupSource] here, since the "vm-isolate" is not
// really an isolate itself - it acts more as a container for VM-global
// objects.
std::unique_ptr<IsolateGroupSource> source(new IsolateGroupSource(
kVmIsolateName, kVmIsolateName, params->vm_snapshot_data,
params->vm_snapshot_instructions, nullptr, -1, api_flags));
// ObjectStore should be created later, after null objects are initialized.
auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr,
/*object_store=*/nullptr, api_flags,
/*is_vm_isolate*/ true);
group->CreateHeap(/*is_vm_isolate=*/true,
/*is_service_or_kernel_isolate=*/false);
IsolateGroup::RegisterIsolateGroup(group);
vm_isolate_ =
Isolate::InitIsolate(kVmIsolateName, group, api_flags, is_vm_isolate);
group->set_initial_spawn_successful();
// Verify assumptions about executing in the VM isolate.
ASSERT(vm_isolate_ == Isolate::Current());
ASSERT(vm_isolate_ == Thread::Current()->isolate());
Thread* T = Thread::Current();
ASSERT(T != nullptr);
StackZone zone(T);
HandleScope handle_scope(T);
Object::InitNullAndBool(vm_isolate_->group());
vm_isolate_->isolate_group_->set_object_store(new ObjectStore());
vm_isolate_->isolate_object_store()->Init();
vm_isolate_->finalizers_ = GrowableObjectArray::null();
Object::Init(vm_isolate_->group());
OffsetsTable::Init();
ArgumentsDescriptor::Init();
ICData::Init();
if (params->vm_snapshot_data != nullptr) {
#if defined(SUPPORT_TIMELINE)
TimelineBeginEndScope tbes(Timeline::GetVMStream(), "ReadVMSnapshot");
#endif
ASSERT(snapshot != nullptr);
vm_snapshot_kind_ = snapshot->kind();
if (Snapshot::IncludesCode(vm_snapshot_kind_)) {
if (vm_snapshot_kind_ == Snapshot::kFullAOT) {
#if !defined(DART_PRECOMPILED_RUNTIME)
return Utils::StrDup("JIT runtime cannot run a precompiled snapshot");
#endif
}
if (params->vm_snapshot_instructions == nullptr) {
return Utils::StrDup("Missing instructions snapshot");
}
} else if (Snapshot::IsFull(vm_snapshot_kind_)) {
#if defined(DART_PRECOMPILED_RUNTIME)
return Utils::StrDup(
"Precompiled runtime requires a precompiled snapshot");
#else
StubCode::Init();
Object::FinishInit(vm_isolate_->group());
#endif
} else {
return Utils::StrDup("Invalid vm isolate snapshot seen");
}
FullSnapshotReader reader(snapshot, params->vm_snapshot_instructions, T);
const Error& error = Error::Handle(reader.ReadVMSnapshot());
if (!error.IsNull()) {
// Must copy before leaving the zone.
return Utils::StrDup(error.ToErrorCString());
}
Object::FinishInit(vm_isolate_->group());
#if defined(SUPPORT_TIMELINE)
if (tbes.enabled()) {
tbes.SetNumArguments(2);
tbes.FormatArgument(0, "snapshotSize", "%" Pd, snapshot->length());
tbes.FormatArgument(
1, "heapSize", "%" Pd,
vm_isolate_group()->heap()->UsedInWords(Heap::kOld) * kWordSize);
}
#endif // !defined(PRODUCT)
if (FLAG_trace_isolates) {
OS::PrintErr("Size of vm isolate snapshot = %" Pd "\n",
snapshot->length());
vm_isolate_group()->heap()->PrintSizes();
MegamorphicCacheTable::PrintSizes(T);
intptr_t size;
intptr_t capacity;
Symbols::GetStats(vm_isolate_->group(), &size, &capacity);
OS::PrintErr("VM Isolate: Number of symbols : %" Pd "\n", size);
OS::PrintErr("VM Isolate: Symbol table capacity : %" Pd "\n", capacity);
}
} else {
#if defined(DART_PRECOMPILED_RUNTIME)
return Utils::StrDup(
"Precompiled runtime requires a precompiled snapshot");
#else
vm_snapshot_kind_ = Snapshot::kNone;
StubCode::Init();
Object::FinishInit(vm_isolate_->group());
Symbols::Init(vm_isolate_->group());
#endif
}
// We need to initialize the constants here for the vm isolate thread due to
// bootstrapping issues.
T->InitVMConstants();
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
// Dart VM requires at least SSE2.
if (!TargetCPUFeatures::sse2_supported()) {
return Utils::StrDup("SSE2 is required.");
}
#endif
{
#if defined(SUPPORT_TIMELINE)
TimelineBeginEndScope tbes(Timeline::GetVMStream(), "FinalizeVMIsolate");
#endif
Object::FinalizeVMIsolate(vm_isolate_->group());
}
#if defined(DEBUG)
vm_isolate_group()->heap()->Verify("Dart::DartInit", kRequireMarked);
#endif
}
NOT_IN_PRODUCT(Profiler::Init());
// Allocate the "persistent" scoped handles for the predefined API
// values (such as Dart_True, Dart_False and Dart_Null).
Api::InitHandles();
Thread::ExitIsolate(); // Unregister the VM isolate from this thread.
Isolate::SetCreateGroupCallback(params->create_group);
Isolate::SetInitializeCallback_(params->initialize_isolate);
Isolate::SetShutdownCallback(params->shutdown_isolate);
Isolate::SetCleanupCallback(params->cleanup_isolate);
Isolate::SetGroupCleanupCallback(params->cleanup_group);
Isolate::SetRegisterKernelBlobCallback(params->register_kernel_blob);
Isolate::SetUnregisterKernelBlobCallback(params->unregister_kernel_blob);
#ifndef PRODUCT
const bool support_service = true;
Service::SetGetServiceAssetsCallback(params->get_service_assets);
#else
const bool support_service = false;
#endif
const bool is_dart2_aot_precompiler =
FLAG_precompiled_mode && !kDartPrecompiledRuntime;
if (!is_dart2_aot_precompiler &&
(support_service || !kDartPrecompiledRuntime)) {
ServiceIsolate::Run();
}
#ifndef DART_PRECOMPILED_RUNTIME
if (params->start_kernel_isolate) {
KernelIsolate::InitializeState();
}
#endif // DART_PRECOMPILED_RUNTIME
return nullptr;
}
char* Dart::Init(const Dart_InitializeParams* params) {
if (!DartInitializationState::SetInitializing()) {
return Utils::StrDup(
"Bad VM initialization state, "
"already initialized or "
"multiple threads initializing the VM.");
}
char* retval = DartInit(params);
if (retval != nullptr) {
DartInitializationState::ResetInitializing();
return retval;
}
DartInitializationState::SetInitialized();
return nullptr;
}
static void DumpAliveIsolates(intptr_t num_attempts,
bool only_application_isolates) {
IsolateGroup::ForEach([&](IsolateGroup* group) {
group->ForEachIsolate([&](Isolate* isolate) {
if (!only_application_isolates || !Isolate::IsSystemIsolate(isolate)) {
OS::PrintErr("Attempt:%" Pd " waiting for isolate %s to check in\n",
num_attempts, isolate->name());
}
});
});
}
static bool OnlyVmIsolateLeft() {
intptr_t count = 0;
bool found_vm_isolate = false;
IsolateGroup::ForEach([&](IsolateGroup* group) {
group->ForEachIsolate([&](Isolate* isolate) {
count++;
if (isolate == Dart::vm_isolate()) {
found_vm_isolate = true;
}
});
});
return count == 1 && found_vm_isolate;
}
// This waits until only the VM, service and kernel isolates are in the list.
void Dart::WaitForApplicationIsolateShutdown() {
ASSERT(!Isolate::creation_enabled_);
MonitorLocker ml(Isolate::isolate_creation_monitor_);
intptr_t num_attempts = 0;
while (IsolateGroup::HasApplicationIsolateGroups()) {
Monitor::WaitResult retval = ml.Wait(1000);
if (retval == Monitor::kTimedOut) {
num_attempts += 1;
if (num_attempts > 10) {
DumpAliveIsolates(num_attempts, /*only_application_isolates=*/true);
}
}
}
}
// This waits until only the VM isolate remains in the list.
void Dart::WaitForIsolateShutdown() {
int64_t start_time = 0;
if (FLAG_trace_shutdown) {
start_time = UptimeMillis();
OS::PrintErr("[+%" Pd64
"ms] SHUTDOWN: Waiting for service "
"and kernel isolates to shutdown\n",
start_time);
}
ASSERT(!Isolate::creation_enabled_);
MonitorLocker ml(Isolate::isolate_creation_monitor_);
intptr_t num_attempts = 0;
while (!IsolateGroup::HasOnlyVMIsolateGroup()) {
Monitor::WaitResult retval = ml.Wait(1000);
if (retval == Monitor::kTimedOut) {
num_attempts += 1;
if (num_attempts > 10) {
DumpAliveIsolates(num_attempts, /*only_application_isolates=*/false);
}
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: %" Pd
" time out waiting for "
"service and kernel isolates to shutdown\n",
UptimeMillis(), num_attempts);
}
}
}
if (FLAG_trace_shutdown) {
int64_t stop_time = UptimeMillis();
OS::PrintErr("[+%" Pd64
"ms] SHUTDOWN: Done waiting for service "
"and kernel isolates to shutdown\n",
stop_time);
if ((stop_time - start_time) > 500) {
OS::PrintErr("[+%" Pd64
"ms] SHUTDOWN: waited too long for service "
"and kernel isolates to shutdown\n",
(stop_time - start_time));
}
}
ASSERT(OnlyVmIsolateLeft());
}
char* Dart::Cleanup() {
ASSERT(Isolate::Current() == nullptr);
if (!DartInitializationState::SetCleaningup()) {
return Utils::StrDup("VM already terminated.");
}
ASSERT(vm_isolate_ != nullptr);
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Starting shutdown\n",
UptimeMillis());
}
#if !defined(PRODUCT)
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutting down profiling\n",
UptimeMillis());
}
Profiler::Cleanup();
#endif // !defined(PRODUCT)
NativeSymbolResolver::Cleanup();
// Disable the creation of new isolates.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Disabling isolate creation\n",
UptimeMillis());
}
Isolate::DisableIsolateCreation();
// Send the OOB Kill message to all remaining application isolates.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Killing all app isolates\n",
UptimeMillis());
}
Isolate::KillAllIsolates(Isolate::kInternalKillMsg);
// Wait for all isolates, but the service and the vm isolate to shut down.
// Only do that if there is a service isolate running.
if (ServiceIsolate::IsRunning() || KernelIsolate::IsRunning()) {
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutting down app isolates\n",
UptimeMillis());
}
WaitForApplicationIsolateShutdown();
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done shutting down app isolates\n",
UptimeMillis());
}
}
Isolate::KillAllSystemIsolates(Isolate::kInternalKillMsg);
// Shutdown the kernel isolate.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutting down kernel isolate\n",
UptimeMillis());
}
KernelIsolate::Shutdown();
// Shutdown the service isolate.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutting down service isolate\n",
UptimeMillis());
}
ServiceIsolate::Shutdown();
// Wait for the remaining isolate (service/kernel isolate) to shutdown
// before shutting down the thread pool.
WaitForIsolateShutdown();
// Shutdown the thread pool. On return, all thread pool threads have exited.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Deleting thread pool\n",
UptimeMillis());
}
DartInitializationState::SetUnInitialized();
thread_pool_->Shutdown();
delete thread_pool_;
thread_pool_ = nullptr;
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done deleting thread pool\n",
UptimeMillis());
}
Api::Cleanup();
delete predefined_handles_;
predefined_handles_ = nullptr;
// Set the VM isolate as current isolate.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Cleaning up vm isolate\n",
UptimeMillis());
}
// If Dart_Cleanup() is called on a thread which hasn't invoked any Dart API
// functions before, entering the "vm-isolate" will cause lazy creation of a
// OSThread (which is attached to the current thread via TLS).
//
// If we run in PRODUCT mode this lazy creation of OSThread can happen here,
// which is why disabling the OSThread creation has to come after entering the
// "vm-isolate".
Thread::EnterIsolate(vm_isolate_);
// Disable creation of any new OSThread structures which means no more new
// threads can do an EnterIsolate. This must come after isolate shutdown
// because new threads may need to be spawned to shutdown the isolates.
// This must come after deletion of the thread pool to avoid a race in which
// a thread spawned by the thread pool does not exit through the thread
// pool, messing up its bookkeeping.
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Disabling OS Thread creation\n",
UptimeMillis());
}
OSThread::DisableOSThreadCreation();
ShutdownIsolate(Thread::Current());
vm_isolate_ = nullptr;
ASSERT(Isolate::IsolateListLength() == 0);
Service::Cleanup();
PortMap::Cleanup();
UserTags::Cleanup();
IsolateGroup::Cleanup();
ICData::Cleanup();
ArgumentsDescriptor::Cleanup();
OffsetsTable::Cleanup();
FfiCallbackMetadata::Cleanup();
TargetCPUFeatures::Cleanup();
MarkingStack::Cleanup();
StoreBuffer::Cleanup();
Object::Cleanup();
Page::Cleanup();
StubCode::Cleanup();
#if defined(SUPPORT_TIMELINE)
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutting down timeline\n",
UptimeMillis());
}
Timeline::Cleanup();
#endif
Zone::Cleanup();
Random::Cleanup();
// Delete the current thread's TLS and set it's TLS to null.
// If it is the last thread then the destructor would call
// OSThread::Cleanup.
OSThread* os_thread = OSThread::Current();
OSThread::SetCurrent(nullptr);
delete os_thread;
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Deleted os_thread\n",
UptimeMillis());
}
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Deleting code observers\n",
UptimeMillis());
}
NOT_IN_PRODUCT(CodeObservers::Cleanup());
OS::Cleanup();
if (FLAG_trace_shutdown) {
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done\n", UptimeMillis());
}
Flags::Cleanup();
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
IsolateGroupReloadContext::SetFileModifiedCallback(nullptr);
Service::SetEmbedderStreamCallbacks(nullptr, nullptr);
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
VirtualMemory::Cleanup();
return nullptr;
}
bool Dart::IsInitialized() {
return DartInitializationState::IsInitialized();
}
bool Dart::SetActiveApiCall() {
return DartInitializationState::SetInUse();
}
void Dart::ResetActiveApiCall() {
DartInitializationState::ResetInUse();
}
Isolate* Dart::CreateIsolate(const char* name_prefix,
const Dart_IsolateFlags& api_flags,
IsolateGroup* isolate_group) {
// Create a new isolate.
Isolate* isolate =
Isolate::InitIsolate(name_prefix, isolate_group, api_flags);
return isolate;
}
ErrorPtr Dart::InitIsolateGroupFromSnapshot(
Thread* T,
const uint8_t* snapshot_data,
const uint8_t* snapshot_instructions,
const uint8_t* kernel_buffer,
intptr_t kernel_buffer_size) {
auto IG = T->isolate_group();
Error& error = Error::Handle(T->zone());
error = Object::Init(IG, kernel_buffer, kernel_buffer_size);
if (!error.IsNull()) {
return error.ptr();
}
if (snapshot_data != nullptr && kernel_buffer == nullptr) {
// Read the snapshot and setup the initial state.
#if defined(SUPPORT_TIMELINE)
TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
"ReadProgramSnapshot");
#endif // defined(SUPPORT_TIMELINE)
const Snapshot* snapshot = Snapshot::SetupFromBuffer(snapshot_data);
if (snapshot == nullptr) {
const String& message = String::Handle(String::New("Invalid snapshot"));
return ApiError::New(message);
}
if (!IsSnapshotCompatible(vm_snapshot_kind_, snapshot->kind())) {
const String& message = String::Handle(String::NewFormatted(
"Incompatible snapshot kinds: vm '%s', isolate '%s'",
Snapshot::KindToCString(vm_snapshot_kind_),
Snapshot::KindToCString(snapshot->kind())));
return ApiError::New(message);
}
if (FLAG_trace_isolates) {
OS::PrintErr("Size of isolate snapshot = %" Pd "\n", snapshot->length());
}
FullSnapshotReader reader(snapshot, snapshot_instructions, T);
const Error& error = Error::Handle(reader.ReadProgramSnapshot());
if (!error.IsNull()) {
return error.ptr();
}
T->SetupDartMutatorStateDependingOnSnapshot(IG);
#if defined(SUPPORT_TIMELINE)
if (tbes.enabled()) {
tbes.SetNumArguments(2);
tbes.FormatArgument(0, "snapshotSize", "%" Pd, snapshot->length());
tbes.FormatArgument(1, "heapSize", "%" Pd,
IG->heap()->UsedInWords(Heap::kOld) * kWordSize);
}
#endif // defined(SUPPORT_TIMELINE)
if (FLAG_trace_isolates) {
IG->heap()->PrintSizes();
MegamorphicCacheTable::PrintSizes(T);
}
} else {
if ((vm_snapshot_kind_ != Snapshot::kNone) && kernel_buffer == nullptr) {
const String& message =
String::Handle(String::New("Missing isolate snapshot"));
return ApiError::New(message);
}
}
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
IG->class_table()->PopulateUserVisibleNames();
#endif
return Error::null();
}
#if !defined(DART_PRECOMPILED_RUNTIME)
// The runtime assumes it can create certain kinds of objects at-will without
// a check whether their class need to be finalized first.
//
// Some of those objects can end up flowing to user code (i.e. their class is a
// subclass of [Instance]).
//
// We therefore ensure that classes are finalized before objects of them are
// created or at least before such objects can reach user code.
static void FinalizeBuiltinClasses(Thread* thread) {
auto class_table = thread->isolate_group()->class_table();
Class& cls = Class::Handle(thread->zone());
for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) {
if (class_table->HasValidClassAt(cid)) {
cls = class_table->At(cid);
RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Object::null());
}
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
ErrorPtr Dart::InitializeIsolateGroup(Thread* T,
const uint8_t* snapshot_data,
const uint8_t* snapshot_instructions,
const uint8_t* kernel_buffer,
intptr_t kernel_buffer_size) {
auto& error = Error::Handle(
InitIsolateGroupFromSnapshot(T, snapshot_data, snapshot_instructions,
kernel_buffer, kernel_buffer_size));
if (!error.IsNull()) {
return error.ptr();
}
Object::VerifyBuiltinVtables();
auto IG = T->isolate_group();
DEBUG_ONLY(IG->heap()->Verify("InitializeIsolate", kForbidMarked));
#if !defined(DART_PRECOMPILED_RUNTIME)
FinalizeBuiltinClasses(T);
#endif
if (snapshot_data == nullptr || kernel_buffer != nullptr) {
auto object_store = IG->object_store();
error ^= object_store->PreallocateObjects();
if (!error.IsNull()) {
return error.ptr();
}
}
if (FLAG_print_class_table) {
IG->class_table()->Print();
}
return Error::null();
}
ErrorPtr Dart::InitializeIsolate(Thread* T,
bool is_first_isolate_in_group,
void* isolate_data) {
auto I = T->isolate();
auto IG = T->isolate_group();
auto Z = T->zone();
// If a static field gets registered in [IsolateGroup::RegisterStaticField]:
//
// * before this block it will ignore this isolate. The [Clone] of the
// initial field table will pick up the new value.
// * after this block it will add the new static field to this isolate.
{
SafepointReadRwLocker reader(T, IG->program_lock());
I->set_field_table(T, IG->initial_field_table()->Clone(I));
I->field_table()->MarkReadyToUse();
}
const auto& out_of_memory =
Object::Handle(IG->object_store()->out_of_memory());
const auto& error = Error::Handle(
Z, I->isolate_object_store()->PreallocateObjects(out_of_memory));
if (!error.IsNull()) {
return error.ptr();
}
I->set_init_callback_data(isolate_data);
#if !defined(PRODUCT)
if (Isolate::IsSystemIsolate(I)) {
ServiceIsolate::MaybeMakeServiceIsolate(I);
} else {
I->message_handler()->set_should_pause_on_start(
FLAG_pause_isolates_on_start);
I->message_handler()->set_should_pause_on_exit(FLAG_pause_isolates_on_exit);
}
#endif // !defined(PRODUCT)
ServiceIsolate::SendIsolateStartupMessage();
#if !defined(PRODUCT)
I->debugger()->NotifyIsolateCreated();
#endif
// Create tag table.
I->set_tag_table(GrowableObjectArray::Handle(GrowableObjectArray::New()));
// Set up default UserTag.
const UserTag& default_tag = UserTag::Handle(UserTag::DefaultTag());
I->set_current_tag(default_tag);
I->init_loaded_prefixes_set_storage();
return Error::null();
}
char* Dart::FeaturesString(IsolateGroup* isolate_group,
bool is_vm_isolate,
Snapshot::Kind kind) {
TextBuffer buffer(64);
// Different fields are included for DEBUG/RELEASE/PRODUCT.
#if defined(DEBUG)
buffer.AddString("debug");
#elif defined(PRODUCT)
buffer.AddString("product");
#else
buffer.AddString("release");
#endif
#define ADD_FLAG(name, value) \
do { \
buffer.AddString(value ? (" " #name) : (" no-" #name)); \
} while (0);
#define ADD_P(name, T, DV, C) ADD_FLAG(name, FLAG_##name)
#define ADD_R(name, PV, T, DV, C) ADD_FLAG(name, FLAG_##name)
#define ADD_C(name, PCV, PV, T, DV, C) ADD_FLAG(name, FLAG_##name)
#define ADD_D(name, T, DV, C) ADD_FLAG(name, FLAG_##name)
#define ADD_ISOLATE_GROUP_FLAG(name, isolate_flag, flag) \
do { \
const bool value = \
isolate_group != nullptr ? isolate_group->name() : flag; \
ADD_FLAG(name, value); \
} while (0);
if (Snapshot::IncludesCode(kind)) {
VM_GLOBAL_FLAG_LIST(ADD_P, ADD_R, ADD_C, ADD_D);
ADD_FLAG(tsan, kTargetUsesThreadSanitizer)
// Enabling assertions affects deopt ids.
ADD_ISOLATE_GROUP_FLAG(asserts, enable_asserts, FLAG_enable_asserts);
if (kind == Snapshot::kFullJIT) {
ADD_ISOLATE_GROUP_FLAG(use_field_guards, use_field_guards,
FLAG_use_field_guards);
ADD_ISOLATE_GROUP_FLAG(use_osr, use_osr, FLAG_use_osr);
ADD_ISOLATE_GROUP_FLAG(branch_coverage, branch_coverage,
FLAG_branch_coverage);
}
// Generated code must match the host architecture and ABI. We check the
// strong condition of matching on operating system so that
// Platform.isAndroid etc can be compile-time constants.
#if defined(TARGET_ARCH_IA32)
buffer.AddString(" ia32");
#elif defined(TARGET_ARCH_X64)
buffer.AddString(" x64");
#elif defined(TARGET_ARCH_ARM)
buffer.AddString(" arm");
#elif defined(TARGET_ARCH_ARM64)
buffer.AddString(" arm64");
#elif defined(TARGET_ARCH_RISCV32)
buffer.AddString(" riscv32");
#elif defined(TARGET_ARCH_RISCV64)
buffer.AddString(" riscv64");
#else
#error What architecture?
#endif
#if defined(DART_TARGET_OS_ANDROID)
buffer.AddString(" android");
#elif defined(DART_TARGET_OS_FUCHSIA)
buffer.AddString(" fuchsia");
#elif defined(DART_TARGET_OS_MACOS)
#if defined(DART_TARGET_OS_MACOS_IOS)
buffer.AddString(" ios");
#else
buffer.AddString(" macos");
#endif
#elif defined(DART_TARGET_OS_LINUX)
buffer.AddString(" linux");
#elif defined(DART_TARGET_OS_WINDOWS)
buffer.AddString(" windows");
#else
#error What operating system?
#endif
#if defined(DART_COMPRESSED_POINTERS)
buffer.AddString(" compressed-pointers");
#else
buffer.AddString(" no-compressed-pointers");
#endif
}
if (!Snapshot::IsAgnosticToNullSafety(kind)) {
if (isolate_group != nullptr) {
if (isolate_group->null_safety()) {
buffer.AddString(" null-safety");
} else {
buffer.AddString(" no-null-safety");
}
} else {
if (FLAG_sound_null_safety) {
buffer.AddString(" null-safety");
} else {
buffer.AddString(" no-null-safety");
}
}
}
#undef ADD_ISOLATE_FLAG
#undef ADD_D
#undef ADD_C
#undef ADD_R
#undef ADD_P
#undef ADD_FLAG
return buffer.Steal();
}
void Dart::RunShutdownCallback() {
Thread* thread = Thread::Current();
ASSERT(thread->execution_state() == Thread::kThreadInVM);
Isolate* isolate = thread->isolate();
void* isolate_group_data = isolate->group()->embedder_data();
void* isolate_data = isolate->init_callback_data();
Dart_IsolateShutdownCallback callback = isolate->on_shutdown_callback();
if (callback != nullptr) {
TransitionVMToNative transition(thread);
(callback)(isolate_group_data, isolate_data);
}
}
void Dart::ShutdownIsolate(Thread* T) {
T->isolate()->Shutdown();
}
int64_t Dart::UptimeMicros() {
return OS::GetCurrentMonotonicMicros() - Dart::start_time_micros_;
}
uword Dart::AllocateReadOnlyHandle() {
ASSERT(Isolate::Current() == Dart::vm_isolate());
ASSERT(predefined_handles_ != nullptr);
uword handle = predefined_handles_->handles_.AllocateScopedHandle();
#if defined(DEBUG)
*reinterpret_cast<uword*>(handle + kOffsetOfIsZoneHandle * kWordSize) = 0;
#endif
return handle;
}
LocalHandle* Dart::AllocateReadOnlyApiHandle() {
ASSERT(Isolate::Current() == Dart::vm_isolate());
ASSERT(predefined_handles_ != nullptr);
return predefined_handles_->api_handles_.AllocateHandle();
}
bool Dart::IsReadOnlyHandle(uword address) {
ASSERT(predefined_handles_ != nullptr);
return predefined_handles_->handles_.IsValidScopedHandle(address);
}
bool Dart::IsReadOnlyApiHandle(Dart_Handle handle) {
ASSERT(predefined_handles_ != nullptr);
return predefined_handles_->api_handles_.IsValidHandle(handle);
}
} // namespace dart