Files
sdk/runtime/bin/dartdev_isolate.cc
T
asiva 7b25ce88d3 [dartdev] Use VmInteropHandler for invoking sub commands
Use VmInteropHandler for invoking sub commands instead of running them
in an isolate. Running sub commands in an isolate causes an increased footprint.
Changing this to use VmInteropHandler avoids the additional memory footprint.

Commands that need to use an AOT runtime for execution now exec the AOT
runtime and run the command.

TEST=ci

Change-Id: Ic96845b19951170effea3dd3619f798e2c72968a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402781
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Siva Annamalai <asiva@google.com>
2025-01-16 09:15:01 -08:00

409 lines
15 KiB
C++

// Copyright (c) 2020, 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 "bin/dartdev_isolate.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#include <functional>
#include <memory>
#include "bin/directory.h"
#include "bin/error_exit.h"
#include "bin/exe_utils.h"
#include "bin/file.h"
#include "bin/lockers.h"
#include "bin/main_options.h"
#include "bin/platform.h"
#include "bin/process.h"
#include "include/dart_embedder_api.h"
#include "platform/utils.h"
#define CHECK_RESULT(result) \
if (Dart_IsError(result)) { \
ProcessError(Dart_GetError(result), kErrorExitCode); \
if (send_port_id != ILLEGAL_PORT) { \
Dart_CloseNativePort(send_port_id); \
} \
Dart_ExitScope(); \
Dart_ShutdownIsolate(); \
return; \
}
namespace dart {
namespace bin {
DartDevIsolate::DartDevRunner DartDevIsolate::runner_ =
DartDevIsolate::DartDevRunner();
bool DartDevIsolate::should_run_dart_dev_ = false;
bool DartDevIsolate::print_usage_error_ = false;
CommandLineOptions* DartDevIsolate::vm_options_ = nullptr;
Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor();
DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ =
DartDevIsolate::DartDev_Result_Unknown;
char** DartDevIsolate::DartDevRunner::script_ = nullptr;
char** DartDevIsolate::DartDevRunner::package_config_override_ = nullptr;
std::unique_ptr<char*[], void (*)(char*[])>
DartDevIsolate::DartDevRunner::argv_ =
std::unique_ptr<char*[], void (*)(char**)>(nullptr, [](char**) {});
intptr_t DartDevIsolate::DartDevRunner::argc_ = 0;
bool DartDevIsolate::ShouldParseCommand(const char* script_uri) {
// If script_uri is a known DartDev command, we should not try to run it.
//
// Otherwise if script_uri is not a file path or of a known URI scheme, we
// assume this is a mistyped DartDev command.
//
// This should be kept in sync with the commands in
// `pkg/dartdev/lib/dartdev.dart`.
return (
(strcmp(script_uri, "analyze") == 0) ||
(strcmp(script_uri, "compilation-server") == 0) ||
(strcmp(script_uri, "build") == 0) ||
(strcmp(script_uri, "compile") == 0) ||
(strcmp(script_uri, "create") == 0) ||
(strcmp(script_uri, "development-service") == 0) ||
(strcmp(script_uri, "devtools") == 0) ||
(strcmp(script_uri, "doc") == 0) || (strcmp(script_uri, "fix") == 0) ||
(strcmp(script_uri, "format") == 0) ||
(strcmp(script_uri, "info") == 0) || (strcmp(script_uri, "pub") == 0) ||
(strcmp(script_uri, "run") == 0) || (strcmp(script_uri, "test") == 0) ||
(strcmp(script_uri, "info") == 0) ||
(strcmp(script_uri, "language-server") == 0) ||
(strcmp(script_uri, "tooling-daemon") == 0) ||
(!File::ExistsUri(nullptr, script_uri) &&
(strncmp(script_uri, "http://", 7) != 0) &&
(strncmp(script_uri, "https://", 8) != 0) &&
(strncmp(script_uri, "file://", 7) != 0) &&
(strncmp(script_uri, "package:", 8) != 0) &&
(strncmp(script_uri, "google3://", 10) != 0)));
}
CStringUniquePtr DartDevIsolate::TryResolveArtifactPath(const char* filename) {
auto try_resolve_path = [&](CStringUniquePtr dir_prefix) {
// First assume we're in dart-sdk/bin.
char* snapshot_path =
Utils::SCreate("%ssnapshots/%s", dir_prefix.get(), filename);
if (File::Exists(nullptr, snapshot_path)) {
return CStringUniquePtr(snapshot_path);
}
free(snapshot_path);
// If we're not in dart-sdk/bin, we might be in one of the $SDK/out/*
// directories. Try to use a snapshot from a previously built SDK.
snapshot_path = Utils::SCreate("%s%s", dir_prefix.get(), filename);
if (File::Exists(nullptr, snapshot_path)) {
return CStringUniquePtr(snapshot_path);
}
free(snapshot_path);
return CStringUniquePtr(nullptr);
};
// Try to find the artifact using the resolved EXE path first. This can fail
// if the Dart SDK file structure is faked using symlinks and the actual
// artifacts are spread across directories on the file system (e.g., some
// google3 execution environments).
auto result =
try_resolve_path(EXEUtils::GetDirectoryPrefixFromResolvedExeName());
if (result == nullptr) {
result =
try_resolve_path(EXEUtils::GetDirectoryPrefixFromUnresolvedExeName());
}
return result;
}
CStringUniquePtr DartDevIsolate::TryResolveDartDevSnapshotPath() {
return TryResolveArtifactPath("dartdev.dart.snapshot");
}
void DartDevIsolate::DartDevRunner::Run(
Dart_IsolateGroupCreateCallback create_isolate,
char** packages_file,
char** script,
CommandLineOptions* dart_options) {
create_isolate_ = create_isolate;
dart_options_ = dart_options;
package_config_override_ = packages_file;
script_ = script;
// We've encountered an error during preliminary argument parsing so we'll
// output the standard help message and exit with an error code.
if (print_usage_error_) {
dart_options_->Reset();
dart_options_->AddArgument("--help");
}
MonitorLocker locker(monitor_);
Thread::Start("DartDev Runner", RunCallback, reinterpret_cast<uword>(this));
monitor_->WaitMicros(Monitor::kNoTimeout);
if (result_ == DartDevIsolate::DartDev_Result_Run) {
// Clear the DartDev dart_options and replace them with the processed
// options provided by DartDev.
dart_options_->Reset();
dart_options_->AddArguments(const_cast<const char**>(argv_.get()), argc_);
}
}
static Dart_CObject* GetArrayItem(Dart_CObject* message, intptr_t index) {
return message->value.as_array.values[index];
}
void DartDevIsolate::DartDevRunner::DartDevResultCallback(
Dart_Port dest_port_id,
Dart_CObject* message) {
// These messages are produced in pkg/dartdev/lib/src/vm_interop_handler.dart.
ASSERT(message->type == Dart_CObject_kArray);
int32_t type = GetArrayItem(message, 0)->value.as_int32;
switch (type) {
case DartDevIsolate::DartDev_Result_Run: {
result_ = DartDevIsolate::DartDev_Result_Run;
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
auto item2 = GetArrayItem(message, 2);
ASSERT(item2->type == Dart_CObject_kString ||
item2->type == Dart_CObject_kNull);
auto item3 = GetArrayItem(message, 3);
ASSERT(item3->type == Dart_CObject_kBool);
const bool mark_main_isolate_as_system_isolate = item3->value.as_bool;
if (mark_main_isolate_as_system_isolate) {
Options::set_mark_main_isolate_as_system_isolate(true);
}
if (*script_ != nullptr) {
free(*script_);
}
if (*package_config_override_ != nullptr) {
free(*package_config_override_);
*package_config_override_ = nullptr;
}
*script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
if (item2->type == Dart_CObject_kString) {
*package_config_override_ = Utils::StrDup(item2->value.as_string);
}
ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray);
Dart_CObject* args = GetArrayItem(message, 4);
argc_ = args->value.as_array.length;
Dart_CObject** dart_args = args->value.as_array.values;
auto deleter = [](char** args) {
for (intptr_t i = 0; i < argc_; ++i) {
free(args[i]);
}
delete[] args;
};
argv_ =
std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_], deleter);
for (intptr_t i = 0; i < argc_; ++i) {
argv_[i] = Utils::StrDup(dart_args[i]->value.as_string);
}
break;
}
case DartDevIsolate::DartDev_Result_RunExec: {
result_ = DartDevIsolate::DartDev_Result_RunExec;
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString);
auto item2 = GetArrayItem(message, 2);
ASSERT(item2->type == Dart_CObject_kString ||
item2->type == Dart_CObject_kNull);
auto item3 = GetArrayItem(message, 3);
ASSERT(item3->type == Dart_CObject_kBool);
const bool mark_main_isolate_as_system_isolate = item3->value.as_bool;
if (mark_main_isolate_as_system_isolate) {
Options::set_mark_main_isolate_as_system_isolate(true);
}
if (*script_ != nullptr) {
free(*script_);
}
if (*package_config_override_ != nullptr) {
free(*package_config_override_);
*package_config_override_ = nullptr;
}
*script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
if (item2->type == Dart_CObject_kString) {
*package_config_override_ = Utils::StrDup(item2->value.as_string);
}
intptr_t num_vm_options = 0;
const char** vm_options = nullptr;
ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray);
Dart_CObject* args = GetArrayItem(message, 4);
intptr_t argc = args->value.as_array.length;
Dart_CObject** dart_args = args->value.as_array.values;
if (vm_options_ != nullptr) {
num_vm_options = vm_options_->count();
vm_options = vm_options_->arguments();
}
auto deleter = [](char** args) {
for (intptr_t i = 0; i < argc_; ++i) {
free(args[i]);
}
delete[] args;
};
// Total count of arguments to be passed to the script being execed.
argc_ = argc + num_vm_options + 1;
// Array of arguments to be passed to the script being execed.
argv_ = std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_ + 1],
deleter);
intptr_t idx = 0;
// Copy in name of the script to run (dartaotruntime).
argv_[0] = Utils::StrDup(GetArrayItem(message, 1)->value.as_string);
idx += 1;
// Copy in any vm options that need to be passed to the execed process.
for (intptr_t i = 0; i < num_vm_options; ++i) {
argv_[i + idx] = Utils::StrDup(vm_options[i]);
}
idx += num_vm_options;
// Copy in the dart options that need to be passed to the command.
for (intptr_t i = 0; i < argc; ++i) {
argv_[i + idx] = Utils::StrDup(dart_args[i]->value.as_string);
}
// Null terminate the argv array.
argv_[argc + idx] = nullptr;
// Exec the script to be run and pass the arguments.
char err_msg[256];
err_msg[0] = '\0';
int ret = Process::Exec(nullptr, *script_,
const_cast<const char**>(argv_.get()), argc_,
nullptr, err_msg, sizeof(err_msg));
if (ret != 0) {
ProcessError(err_msg, kErrorExitCode);
}
break;
}
case DartDevIsolate::DartDev_Result_Exit: {
ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32);
int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32;
// If we're given a non-zero exit code, DartDev is signaling for us to
// shutdown.
int32_t exit_code =
print_usage_error_ ? kErrorExitCode : dartdev_exit_code;
Process::SetGlobalExitCode(exit_code);
// If DartDev hasn't signaled for us to do anything else, we can assume
// there's nothing else for the VM to run and that we can exit.
if (result_ == DartDevIsolate::DartDev_Result_Unknown) {
result_ = DartDevIsolate::DartDev_Result_Exit;
}
// DartDev is done processing the command. Unblock the main thread and
// continue the launch procedure.
DartDevRunner::monitor_->Notify();
break;
}
default:
UNREACHABLE();
}
}
void DartDevIsolate::DartDevRunner::RunCallback(uword args) {
MonitorLocker locker_(DartDevRunner::monitor_);
DartDevRunner* runner = reinterpret_cast<DartDevRunner*>(args);
// Hardcode flags to match those used to generate the DartDev snapshot.
Dart_IsolateFlags flags;
Dart_IsolateFlagsInitialize(&flags);
flags.enable_asserts = false;
flags.use_field_guards = true;
flags.use_osr = true;
flags.is_system_isolate = true;
flags.branch_coverage = false;
flags.coverage = false;
char* error = nullptr;
Dart_Isolate dartdev_isolate = runner->create_isolate_(
DART_DEV_ISOLATE_NAME, DART_DEV_ISOLATE_NAME, nullptr,
runner->packages_file_, &flags, /* callback_data */ nullptr,
const_cast<char**>(&error));
if (dartdev_isolate == nullptr) {
ProcessError(error, kErrorExitCode);
free(error);
return;
}
Dart_EnterIsolate(dartdev_isolate);
Dart_EnterScope();
// Retrieve the DartDev entrypoint.
Dart_Port send_port_id = ILLEGAL_PORT;
Dart_Handle root_lib = Dart_RootLibrary();
Dart_Handle main_closure =
Dart_GetField(root_lib, Dart_NewStringFromCString("main"));
CHECK_RESULT(main_closure);
if (!Dart_IsClosure(main_closure)) {
ProcessError("Unable to find 'main' in root library 'dartdev'",
kErrorExitCode);
Dart_ExitScope();
Dart_ShutdownIsolate();
return;
}
// Create a SendPort that DartDev can use to communicate its results over.
send_port_id =
Dart_NewNativePort(DART_DEV_ISOLATE_NAME, DartDevResultCallback, false);
ASSERT(send_port_id != ILLEGAL_PORT);
Dart_Handle send_port = Dart_NewSendPort(send_port_id);
CHECK_RESULT(send_port);
const intptr_t kNumIsolateArgs = 4;
Dart_Handle isolate_args[kNumIsolateArgs];
isolate_args[0] = main_closure; // entryPoint
isolate_args[1] = runner->dart_options_->CreateRuntimeOptions(); // args
isolate_args[2] = send_port; // message
isolate_args[3] = Dart_True(); // isSpawnUri
Dart_Handle isolate_lib =
Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate"));
Dart_Handle result =
Dart_Invoke(isolate_lib, Dart_NewStringFromCString("_startIsolate"),
kNumIsolateArgs, isolate_args);
CHECK_RESULT(result);
CHECK_RESULT(Dart_RunLoop());
Dart_CloseNativePort(send_port_id);
Dart_ExitScope();
Dart_ShutdownIsolate();
}
void DartDevIsolate::DartDevRunner::ProcessError(const char* msg,
int32_t exit_code) {
Syslog::PrintErr("%s.\n", msg);
Process::SetGlobalExitCode(exit_code);
result_ = DartDevIsolate::DartDev_Result_Exit;
DartDevRunner::monitor_->Notify();
}
DartDevIsolate::DartDev_Result DartDevIsolate::RunDartDev(
Dart_IsolateGroupCreateCallback create_isolate,
char** packages_file,
char** script,
CommandLineOptions* vm_options,
CommandLineOptions* dart_options) {
vm_options_ = vm_options;
runner_.Run(create_isolate, packages_file, script, dart_options);
return runner_.result();
}
} // namespace bin
} // namespace dart
#endif // if !defined(DART_PRECOMPILED_RUNTIME)