blob: ae1fb3242baf0707f70a36a05cb4aa10df05a26b [file] [log] [blame]
// Copyright (c) 2012, 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/console.h"
#include "bin/crashpad.h"
#include "bin/dartutils.h"
#include "bin/dfe.h"
#include "bin/eventhandler.h"
#include "bin/exe_utils.h"
#include "bin/file.h"
#include "bin/loader.h"
#include "bin/platform.h"
#include "bin/process.h"
#include "bin/snapshot_utils.h"
#include "bin/thread.h"
#include "bin/utils.h"
#include "bin/vmservice_impl.h"
#include "platform/assert.h"
#include "vm/benchmark_test.h"
#include "vm/dart.h"
#include "vm/unit_test.h"
extern "C" {
extern const uint8_t kDartVmSnapshotData[];
extern const uint8_t kDartVmSnapshotInstructions[];
extern const uint8_t kDartCoreIsolateSnapshotData[];
extern const uint8_t kDartCoreIsolateSnapshotInstructions[];
}
// TODO(iposva, asiva): This is a placeholder for the real unittest framework.
namespace dart {
// Snapshot pieces when we link in a snapshot.
const uint8_t* bin::vm_snapshot_data = kDartVmSnapshotData;
const uint8_t* bin::vm_snapshot_instructions = kDartVmSnapshotInstructions;
const uint8_t* bin::core_isolate_snapshot_data = kDartCoreIsolateSnapshotData;
const uint8_t* bin::core_isolate_snapshot_instructions =
kDartCoreIsolateSnapshotInstructions;
// Only run tests that match the filter string. The default does not match any
// tests.
static constexpr const char* kNone = "No Test or Benchmarks";
static constexpr const char* kList = "List all Tests and Benchmarks";
static constexpr const char* kAllBenchmarks = "All Benchmarks";
static const char* run_filter = kNone;
static const char* kernel_snapshot = nullptr;
static int run_matches = 0;
void TestCase::Run() {
Syslog::Print("Running test: %s\n", name());
(*run_)();
Syslog::Print("Done: %s\n", name());
}
void RawTestCase::Run() {
Syslog::Print("Running raw test: %s\n", name());
(*run_)();
Syslog::Print("Done: %s\n", name());
}
void TestCaseBase::RunTest() {
if (strcmp(run_filter, this->name()) == 0) {
this->Run();
run_matches++;
} else if (run_filter == kList) {
Syslog::Print("%s %s\n", this->name(), this->expectation());
run_matches++;
}
}
void Benchmark::RunBenchmark() {
if ((run_filter == kAllBenchmarks) ||
(strcmp(run_filter, this->name()) == 0)) {
this->Run();
Syslog::Print("%s(%s): %" Pd64 "\n", this->name(), this->score_kind(),
this->score());
run_matches++;
} else if (run_filter == kList) {
Syslog::Print("%s Pass\n", this->name());
run_matches++;
}
}
static void PrintUsage() {
Syslog::PrintErr(
"Usage: one of the following\n"
" run_vm_tests --list\n"
" run_vm_tests [--dfe=<snapshot file name>] --benchmarks\n"
" run_vm_tests [--dfe=<snapshot file name>] [vm-flags ...] <test name>\n"
" run_vm_tests [--dfe=<snapshot file name>] [vm-flags ...] <benchmark "
"name>\n");
}
#define CHECK_RESULT(result) \
if (Dart_IsError(result)) { \
*error = Utils::StrDup(Dart_GetError(result)); \
Dart_ExitScope(); \
Dart_ShutdownIsolate(); \
return nullptr; \
}
static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri,
const char* packages_config,
Dart_IsolateFlags* flags,
char** error) {
// We only enable the vm-service for this particular test.
// The vm-service seems to have some shutdown race which would cause other
// vm/cc tests to randomly time out due to inability to shut service-isolate
// down.
// Issue(https://dartbug.com/37741):
if ((strcmp(run_filter, "DartAPI_InvokeVMServiceMethod") != 0) &&
(strcmp(run_filter, "DartAPI_InvokeVMServiceMethod_Loop") != 0)) {
return nullptr;
}
ASSERT(script_uri != nullptr);
Dart_Isolate isolate = nullptr;
auto isolate_group_data = new bin::IsolateGroupData(
script_uri, packages_config, /*app_snapshot=*/nullptr,
/*isolate_run_app_snapshot=*/false);
const uint8_t* kernel_buffer = nullptr;
intptr_t kernel_buffer_size = 0;
bin::dfe.Init();
bin::dfe.LoadPlatform(&kernel_buffer, &kernel_buffer_size);
RELEASE_ASSERT(kernel_buffer != nullptr);
flags->load_vmservice_library = true;
flags->is_service_isolate = true;
isolate_group_data->SetKernelBufferUnowned(
const_cast<uint8_t*>(kernel_buffer), kernel_buffer_size);
isolate = Dart_CreateIsolateGroupFromKernel(
script_uri, DART_VM_SERVICE_ISOLATE_NAME, kernel_buffer,
kernel_buffer_size, flags, isolate_group_data, /*isolate_data=*/nullptr,
error);
if (isolate == nullptr) {
delete isolate_group_data;
return nullptr;
}
Dart_EnterScope();
Dart_Handle result =
Dart_SetLibraryTagHandler(bin::Loader::LibraryTagHandler);
CHECK_RESULT(result);
// Load embedder specific bits and return.
if (!bin::VmService::Setup("127.0.0.1", 0,
/*dev_mode=*/false, /*auth_disabled=*/true,
/*write_service_info_filename=*/"",
/*trace_loading=*/false, /*deterministic=*/true,
/*enable_service_port_fallback=*/false,
/*wait_for_dds_to_advertise_service=*/false,
/*serve_devtools=*/false,
/*serve_observatory=*/true,
/*print_dtd=*/false)) {
*error = Utils::StrDup(bin::VmService::GetErrorMessage());
return nullptr;
}
result = Dart_SetEnvironmentCallback(bin::DartUtils::EnvironmentCallback);
CHECK_RESULT(result);
Dart_ExitScope();
Dart_ExitIsolate();
return isolate;
}
static Dart_Isolate CreateIsolateAndSetup(const char* script_uri,
const char* main,
const char* package_root,
const char* packages_config,
Dart_IsolateFlags* flags,
void* data,
char** error) {
ASSERT(script_uri != nullptr);
ASSERT(package_root == nullptr);
if (strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) {
return CreateAndSetupServiceIsolate(script_uri, packages_config, flags,
error);
}
const bool is_kernel_isolate =
strcmp(script_uri, DART_KERNEL_ISOLATE_NAME) == 0;
if (!is_kernel_isolate) {
*error = Utils::StrDup(
"Spawning of only Kernel isolate is supported in run_vm_tests.");
return nullptr;
}
Dart_Isolate isolate = nullptr;
bin::IsolateGroupData* isolate_group_data = nullptr;
const uint8_t* kernel_service_buffer = nullptr;
intptr_t kernel_service_buffer_size = 0;
// Kernel isolate uses an app snapshot or the kernel service dill file.
if (kernel_snapshot != nullptr &&
(bin::DartUtils::SniffForMagicNumber(kernel_snapshot) ==
bin::DartUtils::kAppJITMagicNumber)) {
script_uri = kernel_snapshot;
bin::AppSnapshot* app_snapshot =
bin::Snapshot::TryReadAppSnapshot(script_uri);
ASSERT(app_snapshot != nullptr);
const uint8_t* ignore_vm_snapshot_data;
const uint8_t* ignore_vm_snapshot_instructions;
const uint8_t* isolate_snapshot_data;
const uint8_t* isolate_snapshot_instructions;
app_snapshot->SetBuffers(
&ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions,
&isolate_snapshot_data, &isolate_snapshot_instructions);
isolate_group_data = new bin::IsolateGroupData(
script_uri, packages_config, app_snapshot, app_snapshot != nullptr);
isolate = Dart_CreateIsolateGroup(
DART_KERNEL_ISOLATE_NAME, DART_KERNEL_ISOLATE_NAME,
isolate_snapshot_data, isolate_snapshot_instructions, flags,
isolate_group_data, /*isolate_data=*/nullptr, error);
if (*error != nullptr) {
OS::PrintErr("Error creating isolate group: %s\n", *error);
free(*error);
*error = nullptr;
}
// If a test does not actually require the kernel isolate the main thead can
// start calling Dart::Cleanup() while the kernel isolate is booting up.
// This can cause the isolate to be killed early which will return `nullptr`
// here.
if (isolate == nullptr) {
delete isolate_group_data;
return nullptr;
}
}
if (isolate == nullptr) {
delete isolate_group_data;
isolate_group_data = nullptr;
bin::dfe.Init();
bin::dfe.LoadKernelService(&kernel_service_buffer,
&kernel_service_buffer_size);
ASSERT(kernel_service_buffer != nullptr);
isolate_group_data =
new bin::IsolateGroupData(script_uri, packages_config, nullptr, false);
isolate_group_data->SetKernelBufferUnowned(
const_cast<uint8_t*>(kernel_service_buffer),
kernel_service_buffer_size);
isolate = Dart_CreateIsolateGroupFromKernel(
script_uri, main, kernel_service_buffer, kernel_service_buffer_size,
flags, isolate_group_data, /*isolate_data=*/nullptr, error);
}
if (isolate == nullptr) {
delete isolate_group_data;
return nullptr;
}
Dart_EnterScope();
bin::DartUtils::SetOriginalWorkingDirectory();
Dart_Handle result = bin::DartUtils::PrepareForScriptLoading(
/*is_service_isolate=*/false, /*trace_loading=*/false);
CHECK_RESULT(result);
// Setup kernel service as the main script for this isolate.
if (kernel_service_buffer != nullptr) {
result = Dart_LoadScriptFromKernel(kernel_service_buffer,
kernel_service_buffer_size);
CHECK_RESULT(result);
}
Dart_ExitScope();
Dart_ExitIsolate();
*error = Dart_IsolateMakeRunnable(isolate);
if (*error != nullptr) {
Dart_EnterIsolate(isolate);
Dart_ShutdownIsolate();
return nullptr;
}
return isolate;
}
static void CleanupIsolateGroup(void* callback_data) {
bin::IsolateGroupData* isolate_data =
reinterpret_cast<bin::IsolateGroupData*>(callback_data);
delete isolate_data;
}
static void EmbedderInformationCallback(Dart_EmbedderInformation* info) {
info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION;
info->name = "Run VM Tests";
bin::Process::GetRSSInformation(&(info->max_rss), &(info->current_rss));
}
void ShiftArgs(int* argc, const char** argv) {
// Remove the first flag from the list by shifting all arguments down.
for (intptr_t i = 1; i < *argc - 1; i++) {
argv[i] = argv[i + 1];
}
argv[*argc - 1] = nullptr;
(*argc)--;
}
static int Main(int argc, const char** argv) {
#if !defined(DART_HOST_OS_WINDOWS)
// Very early so any crashes during startup can also be symbolized.
bin::EXEUtils::LoadDartProfilerSymbols(argv[0]);
#endif
// Flags being passed to the Dart VM.
int dart_argc = 0;
const char** dart_argv = nullptr;
// Perform platform specific initialization.
if (!dart::bin::Platform::Initialize()) {
Syslog::PrintErr("Initialization failed\n");
return 1;
}
// Save the console state so we can restore it later.
dart::bin::Console::SaveConfig();
// Store the executable name.
dart::bin::Platform::SetExecutableName(argv[0]);
if (argc < 2) {
// Bad parameter count.
PrintUsage();
return 1;
}
if (argc == 2 && strcmp(argv[1], "--list") == 0) {
run_filter = kList;
// List all tests and benchmarks and exit without initializing the VM.
TestCaseBase::RunAll();
Benchmark::RunAll(argv[0]);
TestCaseBase::RunAllRaw();
fflush(stdout);
return 0;
}
int arg_pos = 1;
bool start_kernel_isolate = false;
bool suppress_core_dump = false;
if (strcmp(argv[arg_pos], "--suppress-core-dump") == 0) {
suppress_core_dump = true;
ShiftArgs(&argc, argv);
}
if (suppress_core_dump) {
bin::Platform::SetCoreDumpResourceLimit(0);
} else {
bin::InitializeCrashpadClient();
}
if (strncmp(argv[arg_pos], "--dfe", strlen("--dfe")) == 0) {
const char* delim = strstr(argv[arg_pos], "=");
if (delim == nullptr || strlen(delim + 1) == 0) {
Syslog::PrintErr("Invalid value for the option: %s\n", argv[arg_pos]);
PrintUsage();
return 1;
}
kernel_snapshot = Utils::StrDup(delim + 1);
start_kernel_isolate = true;
ShiftArgs(&argc, argv);
}
if (arg_pos == argc - 1 && strcmp(argv[arg_pos], "--benchmarks") == 0) {
// "--benchmarks" is the last argument.
run_filter = kAllBenchmarks;
} else {
// Last argument is the test name, the rest are vm flags.
run_filter = argv[argc - 1];
// Remove the first value (executable) from the arguments and
// exclude the last argument which is the test name.
dart_argc = argc - 2;
dart_argv = &argv[1];
}
bin::TimerUtils::InitOnce();
bin::Process::Init();
bin::EventHandler::Start();
char* error = Flags::ProcessCommandLineFlags(dart_argc, dart_argv);
if (error != nullptr) {
Syslog::PrintErr("Failed to parse flags: %s\n", error);
free(error);
return 1;
}
TesterState::vm_snapshot_data = dart::bin::vm_snapshot_data;
TesterState::create_callback = CreateIsolateAndSetup;
TesterState::group_cleanup_callback = CleanupIsolateGroup;
TesterState::argv = dart_argv;
TesterState::argc = dart_argc;
Dart_InitializeParams init_params;
memset(&init_params, 0, sizeof(init_params));
init_params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
init_params.vm_snapshot_data = dart::bin::vm_snapshot_data;
init_params.vm_snapshot_instructions = dart::bin::vm_snapshot_instructions;
init_params.create_group = CreateIsolateAndSetup;
init_params.cleanup_group = CleanupIsolateGroup;
init_params.file_open = dart::bin::DartUtils::OpenFile;
init_params.file_read = dart::bin::DartUtils::ReadFile;
init_params.file_write = dart::bin::DartUtils::WriteFile;
init_params.file_close = dart::bin::DartUtils::CloseFile;
init_params.start_kernel_isolate = start_kernel_isolate;
#if defined(DART_HOST_OS_FUCHSIA)
init_params.vmex_resource = dart::bin::Platform::GetVMEXResource();
#endif
error = Dart::Init(&init_params);
if (error != nullptr) {
Syslog::PrintErr("Failed to initialize VM: %s\n", error);
free(error);
return 1;
}
Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback);
// Apply the filter to all registered tests.
TestCaseBase::RunAll();
// Apply the filter to all registered benchmarks.
Benchmark::RunAll(argv[0]);
bin::Process::TerminateExitCodeHandler();
error = Dart::Cleanup();
if (error != nullptr) {
Syslog::PrintErr("Failed shutdown VM: %s\n", error);
free(error);
return 1;
}
TestCaseBase::RunAllRaw();
bin::EventHandler::Stop();
bin::Process::Cleanup();
// Print a warning message if no tests or benchmarks were matched.
if (run_matches == 0) {
Syslog::PrintErr("No tests matched: %s\n", run_filter);
return 1;
}
if (Expect::failed()) {
return 255;
}
return 0;
}
} // namespace dart
int main(int argc, const char** argv) {
dart::bin::Platform::Exit(dart::Main(argc, argv));
}