1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "bin/console.h"
6#include "bin/crashpad.h"
7#include "bin/dartutils.h"
8#include "bin/dfe.h"
9#include "bin/eventhandler.h"
10#include "bin/file.h"
11#include "bin/loader.h"
12#include "bin/platform.h"
13#include "bin/process.h"
14#include "bin/snapshot_utils.h"
15#include "bin/thread.h"
16#include "bin/utils.h"
17#include "bin/vmservice_impl.h"
18#include "platform/assert.h"
19#include "vm/benchmark_test.h"
20#include "vm/dart.h"
21#include "vm/unit_test.h"
22
23extern "C" {
24extern const uint8_t kDartVmSnapshotData[];
25extern const uint8_t kDartVmSnapshotInstructions[];
26extern const uint8_t kDartCoreIsolateSnapshotData[];
27extern const uint8_t kDartCoreIsolateSnapshotInstructions[];
28}
29
30// TODO(iposva, asiva): This is a placeholder for the real unittest framework.
31namespace dart {
32
33// Snapshot pieces when we link in a snapshot.
34const uint8_t* bin::vm_snapshot_data = kDartVmSnapshotData;
35const uint8_t* bin::vm_snapshot_instructions = kDartVmSnapshotInstructions;
36const uint8_t* bin::core_isolate_snapshot_data = kDartCoreIsolateSnapshotData;
37const uint8_t* bin::core_isolate_snapshot_instructions =
38 kDartCoreIsolateSnapshotInstructions;
39
40// Only run tests that match the filter string. The default does not match any
41// tests.
42static const char* const kNone = "No Test or Benchmarks";
43static const char* const kList = "List all Tests and Benchmarks";
44static const char* const kAllBenchmarks = "All Benchmarks";
45static const char* run_filter = kNone;
46static const char* kernel_snapshot = nullptr;
47
48static int run_matches = 0;
49
50void TestCase::Run() {
51 Syslog::Print("Running test: %s\n", name());
52 (*run_)();
53 Syslog::Print("Done: %s\n", name());
54}
55
56void RawTestCase::Run() {
57 Syslog::Print("Running raw test: %s\n", name());
58 (*run_)();
59 Syslog::Print("Done: %s\n", name());
60}
61
62void TestCaseBase::RunTest() {
63 if (strcmp(run_filter, this->name()) == 0) {
64 this->Run();
65 run_matches++;
66 } else if (run_filter == kList) {
67 Syslog::Print("%s %s\n", this->name(), this->expectation());
68 run_matches++;
69 }
70}
71
72void Benchmark::RunBenchmark() {
73 if ((run_filter == kAllBenchmarks) ||
74 (strcmp(run_filter, this->name()) == 0)) {
75 this->Run();
76 Syslog::Print("%s(%s): %" Pd64 "\n", this->name(), this->score_kind(),
77 this->score());
78 run_matches++;
79 } else if (run_filter == kList) {
80 Syslog::Print("%s Pass\n", this->name());
81 run_matches++;
82 }
83}
84
85static void PrintUsage() {
86 Syslog::PrintErr(
87 "Usage: one of the following\n"
88 " run_vm_tests --list\n"
89 " run_vm_tests [--dfe=<snapshot file name>] --benchmarks\n"
90 " run_vm_tests [--dfe=<snapshot file name>] [vm-flags ...] <test name>\n"
91 " run_vm_tests [--dfe=<snapshot file name>] [vm-flags ...] <benchmark "
92 "name>\n");
93}
94
95#define CHECK_RESULT(result) \
96 if (Dart_IsError(result)) { \
97 *error = Utils::StrDup(Dart_GetError(result)); \
98 Dart_ExitScope(); \
99 Dart_ShutdownIsolate(); \
100 return nullptr; \
101 }
102
103static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri,
104 const char* packages_config,
105 Dart_IsolateFlags* flags,
106 char** error) {
107 // We only enable the vm-service for this particular test.
108 // The vm-service seems to have some shutdown race which would cause other
109 // vm/cc tests to randomly time out due to inability to shut service-isolate
110 // down.
111 // Issue(https://dartbug.com/37741):
112 if (strcmp(run_filter, "DartAPI_InvokeVMServiceMethod") != 0) {
113 return nullptr;
114 }
115
116 ASSERT(script_uri != nullptr);
117 Dart_Isolate isolate = nullptr;
118 auto isolate_group_data = new bin::IsolateGroupData(
119 script_uri, packages_config, /*app_snapshot=*/nullptr,
120 /*isolate_run_app_snapshot=*/false);
121
122 const uint8_t* kernel_buffer = nullptr;
123 intptr_t kernel_buffer_size = 0;
124
125 bin::dfe.Init();
126 bin::dfe.LoadPlatform(&kernel_buffer, &kernel_buffer_size);
127 RELEASE_ASSERT(kernel_buffer != nullptr);
128
129 flags->load_vmservice_library = true;
130 isolate_group_data->SetKernelBufferUnowned(
131 const_cast<uint8_t*>(kernel_buffer), kernel_buffer_size);
132 isolate = Dart_CreateIsolateGroupFromKernel(
133 script_uri, DART_VM_SERVICE_ISOLATE_NAME, kernel_buffer,
134 kernel_buffer_size, flags, isolate_group_data, /*isolate_data=*/nullptr,
135 error);
136 if (isolate == nullptr) {
137 delete isolate_group_data;
138 return nullptr;
139 }
140
141 Dart_EnterScope();
142
143 Dart_Handle result =
144 Dart_SetLibraryTagHandler(bin::Loader::LibraryTagHandler);
145 CHECK_RESULT(result);
146
147 // Load embedder specific bits and return.
148 if (!bin::VmService::Setup("127.0.0.1", 0,
149 /*dev_mode=*/false, /*auth_disabled=*/true,
150 /*write_service_info_filename*/ "",
151 /*trace_loading=*/false, /*deterministic=*/true,
152 /*enable_service_port_fallback=*/false)) {
153 *error = Utils::StrDup(bin::VmService::GetErrorMessage());
154 return nullptr;
155 }
156 result = Dart_SetEnvironmentCallback(bin::DartUtils::EnvironmentCallback);
157 CHECK_RESULT(result);
158 Dart_ExitScope();
159 Dart_ExitIsolate();
160 return isolate;
161}
162
163static Dart_Isolate CreateIsolateAndSetup(const char* script_uri,
164 const char* main,
165 const char* package_root,
166 const char* packages_config,
167 Dart_IsolateFlags* flags,
168 void* data,
169 char** error) {
170 ASSERT(script_uri != nullptr);
171 ASSERT(package_root == nullptr);
172 if (strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) {
173 return CreateAndSetupServiceIsolate(script_uri, packages_config, flags,
174 error);
175 }
176 const bool is_kernel_isolate =
177 strcmp(script_uri, DART_KERNEL_ISOLATE_NAME) == 0;
178 if (!is_kernel_isolate) {
179 *error = Utils::StrDup(
180 "Spawning of only Kernel isolate is supported in run_vm_tests.");
181 return nullptr;
182 }
183 Dart_Isolate isolate = nullptr;
184 bin::IsolateGroupData* isolate_group_data = nullptr;
185 const uint8_t* kernel_service_buffer = nullptr;
186 intptr_t kernel_service_buffer_size = 0;
187
188 // Kernel isolate uses an app snapshot or the kernel service dill file.
189 if (kernel_snapshot != nullptr &&
190 (bin::DartUtils::SniffForMagicNumber(kernel_snapshot) ==
191 bin::DartUtils::kAppJITMagicNumber)) {
192 script_uri = kernel_snapshot;
193 bin::AppSnapshot* app_snapshot =
194 bin::Snapshot::TryReadAppSnapshot(script_uri);
195 ASSERT(app_snapshot != nullptr);
196 const uint8_t* ignore_vm_snapshot_data;
197 const uint8_t* ignore_vm_snapshot_instructions;
198 const uint8_t* isolate_snapshot_data;
199 const uint8_t* isolate_snapshot_instructions;
200 app_snapshot->SetBuffers(
201 &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions,
202 &isolate_snapshot_data, &isolate_snapshot_instructions);
203 isolate_group_data = new bin::IsolateGroupData(
204 script_uri, packages_config, app_snapshot, app_snapshot != nullptr);
205 isolate = Dart_CreateIsolateGroup(
206 DART_KERNEL_ISOLATE_NAME, DART_KERNEL_ISOLATE_NAME,
207 isolate_snapshot_data, isolate_snapshot_instructions, flags,
208 isolate_group_data, /*isolate_data=*/nullptr, error);
209 if (*error != nullptr) {
210 OS::PrintErr("Error creating isolate group: %s\n", *error);
211 free(*error);
212 *error = nullptr;
213 }
214 // If a test does not actually require the kernel isolate the main thead can
215 // start calling Dart::Cleanup() while the kernel isolate is booting up.
216 // This can cause the isolate to be killed early which will return `nullptr`
217 // here.
218 if (isolate == nullptr) {
219 delete isolate_group_data;
220 return nullptr;
221 }
222 }
223 if (isolate == nullptr) {
224 delete isolate_group_data;
225 isolate_group_data = nullptr;
226
227 bin::dfe.Init();
228 bin::dfe.LoadKernelService(&kernel_service_buffer,
229 &kernel_service_buffer_size);
230 ASSERT(kernel_service_buffer != nullptr);
231 isolate_group_data =
232 new bin::IsolateGroupData(script_uri, packages_config, nullptr, false);
233 isolate_group_data->SetKernelBufferUnowned(
234 const_cast<uint8_t*>(kernel_service_buffer),
235 kernel_service_buffer_size);
236 isolate = Dart_CreateIsolateGroupFromKernel(
237 script_uri, main, kernel_service_buffer, kernel_service_buffer_size,
238 flags, isolate_group_data, /*isolate_data=*/nullptr, error);
239 }
240 if (isolate == nullptr) {
241 delete isolate_group_data;
242 return nullptr;
243 }
244
245 Dart_EnterScope();
246
247 bin::DartUtils::SetOriginalWorkingDirectory();
248 Dart_Handle result = bin::DartUtils::PrepareForScriptLoading(
249 /*is_service_isolate=*/false, /*trace_loading=*/false);
250 CHECK_RESULT(result);
251
252 // Setup kernel service as the main script for this isolate.
253 if (kernel_service_buffer != nullptr) {
254 result = Dart_LoadScriptFromKernel(kernel_service_buffer,
255 kernel_service_buffer_size);
256 CHECK_RESULT(result);
257 }
258
259 Dart_ExitScope();
260 Dart_ExitIsolate();
261 *error = Dart_IsolateMakeRunnable(isolate);
262 if (*error != nullptr) {
263 Dart_EnterIsolate(isolate);
264 Dart_ShutdownIsolate();
265 return nullptr;
266 }
267
268 return isolate;
269}
270
271static void CleanupIsolateGroup(void* callback_data) {
272 bin::IsolateGroupData* isolate_data =
273 reinterpret_cast<bin::IsolateGroupData*>(callback_data);
274 delete isolate_data;
275}
276
277void ShiftArgs(int* argc, const char** argv) {
278 // Remove the first flag from the list by shifting all arguments down.
279 for (intptr_t i = 1; i < *argc - 1; i++) {
280 argv[i] = argv[i + 1];
281 }
282 argv[*argc - 1] = nullptr;
283 (*argc)--;
284}
285
286static int Main(int argc, const char** argv) {
287 // Flags being passed to the Dart VM.
288 int dart_argc = 0;
289 const char** dart_argv = nullptr;
290
291 // Perform platform specific initialization.
292 if (!dart::bin::Platform::Initialize()) {
293 Syslog::PrintErr("Initialization failed\n");
294 return 1;
295 }
296
297 // Save the console state so we can restore it later.
298 dart::bin::Console::SaveConfig();
299
300 // Store the executable name.
301 dart::bin::Platform::SetExecutableName(argv[0]);
302
303 if (argc < 2) {
304 // Bad parameter count.
305 PrintUsage();
306 return 1;
307 }
308
309 if (argc == 2 && strcmp(argv[1], "--list") == 0) {
310 run_filter = kList;
311 // List all tests and benchmarks and exit without initializing the VM.
312 TestCaseBase::RunAll();
313 Benchmark::RunAll(argv[0]);
314 TestCaseBase::RunAllRaw();
315 fflush(stdout);
316 return 0;
317 }
318
319 int arg_pos = 1;
320 bool start_kernel_isolate = false;
321 bool suppress_core_dump = false;
322 if (strcmp(argv[arg_pos], "--suppress-core-dump") == 0) {
323 suppress_core_dump = true;
324 ShiftArgs(&argc, argv);
325 }
326
327 if (suppress_core_dump) {
328 bin::Platform::SetCoreDumpResourceLimit(0);
329 } else {
330 bin::InitializeCrashpadClient();
331 }
332
333 if (strncmp(argv[arg_pos], "--dfe", strlen("--dfe")) == 0) {
334 const char* delim = strstr(argv[arg_pos], "=");
335 if (delim == nullptr || strlen(delim + 1) == 0) {
336 Syslog::PrintErr("Invalid value for the option: %s\n", argv[arg_pos]);
337 PrintUsage();
338 return 1;
339 }
340 kernel_snapshot = Utils::StrDup(delim + 1);
341 start_kernel_isolate = true;
342 ShiftArgs(&argc, argv);
343 }
344
345 if (arg_pos == argc - 1 && strcmp(argv[arg_pos], "--benchmarks") == 0) {
346 // "--benchmarks" is the last argument.
347 run_filter = kAllBenchmarks;
348 } else {
349 // Last argument is the test name, the rest are vm flags.
350 run_filter = argv[argc - 1];
351 // Remove the first value (executable) from the arguments and
352 // exclude the last argument which is the test name.
353 dart_argc = argc - 2;
354 dart_argv = &argv[1];
355 }
356
357 bin::TimerUtils::InitOnce();
358 bin::Process::Init();
359 bin::EventHandler::Start();
360
361 char* error = Flags::ProcessCommandLineFlags(dart_argc, dart_argv);
362 if (error != nullptr) {
363 Syslog::PrintErr("Failed to parse flags: %s\n", error);
364 free(error);
365 return 1;
366 }
367
368 TesterState::vm_snapshot_data = dart::bin::vm_snapshot_data;
369 TesterState::create_callback = CreateIsolateAndSetup;
370 TesterState::group_cleanup_callback = CleanupIsolateGroup;
371 TesterState::argv = dart_argv;
372 TesterState::argc = dart_argc;
373
374 error = Dart::Init(
375 dart::bin::vm_snapshot_data, dart::bin::vm_snapshot_instructions,
376 /*create_group=*/CreateIsolateAndSetup,
377 /*initialize_isolate=*/nullptr,
378 /*shutdown_isolate=*/nullptr,
379 /*cleanup_isolate=*/nullptr,
380 /*cleanup_group=*/CleanupIsolateGroup,
381 /*thread_exit=*/nullptr, dart::bin::DartUtils::OpenFile,
382 dart::bin::DartUtils::ReadFile, dart::bin::DartUtils::WriteFile,
383 dart::bin::DartUtils::CloseFile, /*entropy_source=*/nullptr,
384 /*get_service_assets=*/nullptr, start_kernel_isolate,
385 /*code_observer=*/nullptr);
386 if (error != nullptr) {
387 Syslog::PrintErr("Failed to initialize VM: %s\n", error);
388 free(error);
389 return 1;
390 }
391
392 // Apply the filter to all registered tests.
393 TestCaseBase::RunAll();
394 // Apply the filter to all registered benchmarks.
395 Benchmark::RunAll(argv[0]);
396
397 bin::Process::TerminateExitCodeHandler();
398 error = Dart::Cleanup();
399 if (error != nullptr) {
400 Syslog::PrintErr("Failed shutdown VM: %s\n", error);
401 free(error);
402 return 1;
403 }
404
405 TestCaseBase::RunAllRaw();
406
407 bin::EventHandler::Stop();
408 bin::Process::Cleanup();
409
410 // Print a warning message if no tests or benchmarks were matched.
411 if (run_matches == 0) {
412 Syslog::PrintErr("No tests matched: %s\n", run_filter);
413 return 1;
414 }
415 if (Expect::failed()) {
416 return 255;
417 }
418 return 0;
419}
420
421} // namespace dart
422
423int main(int argc, const char** argv) {
424 dart::bin::Platform::Exit(dart::Main(argc, argv));
425}
426