Files
sdk/runtime/bin/dfe.cc
T
Zichang Guo aa1b00d668 Reland "[vm] resolve symbolic link to find kernel_service snapshot"
This is a reland of 8a8c19dd4b

Fix the memory leak of namespace.

Original change's description:
> [vm] resolve symbolic link to find kernel_service snapshot
>
> When VM starts, it will try to find kernel service snapshot which is located together with exe file. However, if symbolic link is provided, the directory to search will become the parent directory of link but not the actual target.
> This cl will resolve the symbolic link to find the kernel service snapshot.
>
> Bug: https://github.com/dart-lang/sdk/issues/35188
> Change-Id: I842973a9b73439003a748122d9416158d155892e
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129380
> Commit-Queue: Zichang Guo <zichangguo@google.com>
> Reviewed-by: Siva Annamalai <asiva@google.com>

Bug: https://github.com/dart-lang/sdk/issues/35188
Change-Id: I45f09d3ec813926b72d79880aeee2cc8a0453f87
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/133246
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Zichang Guo <zichangguo@google.com>
2020-01-24 19:28:57 +00:00

537 lines
17 KiB
C++

// Copyright (c) 2017, 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/dfe.h"
#include <memory>
#include "bin/abi_version.h"
#include "bin/dartutils.h"
#include "bin/directory.h"
#include "bin/error_exit.h"
#include "bin/file.h"
#include "bin/main_options.h"
#include "bin/platform.h"
#include "bin/utils.h"
#include "include/dart_tools_api.h"
#include "platform/utils.h"
extern "C" {
#if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM)
extern const uint8_t kKernelServiceDill[];
extern intptr_t kKernelServiceDillSize;
extern const uint8_t kPlatformStrongDill[];
extern intptr_t kPlatformStrongDillSize;
#else
const uint8_t* kKernelServiceDill = nullptr;
intptr_t kKernelServiceDillSize = 0;
const uint8_t* kPlatformStrongDill = nullptr;
intptr_t kPlatformStrongDillSize = 0;
#endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM)
}
namespace dart {
namespace bin {
#if !defined(DART_PRECOMPILED_RUNTIME)
DFE dfe;
#endif
const char kKernelServiceSnapshot[] = "kernel-service.dart.snapshot";
const char kSnapshotsDirectory[] = "snapshots";
static char* GetDirectoryPrefixFromExeName() {
const char* name = nullptr;
const int kTargetSize = 4096;
char target[kTargetSize];
intptr_t target_size =
Platform::ResolveExecutablePathInto(target, kTargetSize);
if (target_size > 0 && target_size < kTargetSize - 1) {
target[target_size] = 0;
name = target;
}
if (name == nullptr) {
name = Platform::GetExecutableName();
target_size = strlen(name);
}
Namespace* namespc = Namespace::Create(Namespace::Default());
if (File::GetType(namespc, name, false) == File::kIsLink) {
// Resolve the link without creating Dart scope String.
name = File::LinkTarget(namespc, name, target, kTargetSize);
if (name == NULL) {
return strdup("");
}
target_size = strlen(name);
}
namespc->Release();
const char* sep = File::PathSeparator();
const intptr_t sep_length = strlen(sep);
for (intptr_t i = target_size - 1; i >= 0; --i) {
const char* str = name + i;
if (strncmp(str, sep, sep_length) == 0
#if defined(HOST_OS_WINDOWS)
// TODO(aam): GetExecutableName doesn't work reliably on Windows,
// the code below is a workaround for that (we would be using
// just single Platform::Separator instead of both slashes if it did).
|| *str == '/'
#endif
) {
return Utils::StrNDup(name, i + 1);
}
}
return strdup("");
}
DFE::DFE()
: use_dfe_(false),
use_incremental_compiler_(false),
frontend_filename_(nullptr),
application_kernel_buffer_(nullptr),
application_kernel_buffer_size_(0) {
// The run_vm_tests binary has the DART_PRECOMPILER set in order to allow unit
// tests to exercise JIT and AOT pipeline.
//
// Only on X64 do we have kernel-service.dart.snapshot available otherwise we
// need to fall back to the built-in one (if we have it).
#if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) || \
(defined(DART_PRECOMPILER) && defined(TARGET_ARCH_X64))
kernel_service_dill_ = nullptr;
kernel_service_dill_size_ = 0;
#else
kernel_service_dill_ = kKernelServiceDill;
kernel_service_dill_size_ = kKernelServiceDillSize;
#endif
#if defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM)
platform_strong_dill_for_compilation_ = nullptr;
platform_strong_dill_for_compilation_size_ = 0;
platform_strong_dill_for_execution_ = nullptr;
platform_strong_dill_for_execution_size_ = 0;
#else
platform_strong_dill_for_compilation_ = kPlatformStrongDill;
platform_strong_dill_for_compilation_size_ = kPlatformStrongDillSize;
platform_strong_dill_for_execution_ = kPlatformStrongDill;
platform_strong_dill_for_execution_size_ = kPlatformStrongDillSize;
#endif
}
DFE::~DFE() {
if (frontend_filename_ != nullptr) {
free(frontend_filename_);
}
frontend_filename_ = nullptr;
free(application_kernel_buffer_);
application_kernel_buffer_ = nullptr;
application_kernel_buffer_size_ = 0;
}
void DFE::Init() {
Init(Options::kAbiVersionUnset);
}
void DFE::Init(int target_abi_version) {
if (platform_strong_dill_for_compilation_ == nullptr) {
return;
}
if (!InitKernelServiceAndPlatformDills(target_abi_version)) {
return;
}
Dart_SetDartLibrarySourcesKernel(platform_strong_dill_for_compilation_,
platform_strong_dill_for_compilation_size_);
}
bool DFE::InitKernelServiceAndPlatformDills(int target_abi_version) {
const char kAbiVersionsDir[] = "dart-sdk/lib/_internal/abiversions";
const char kKernelServiceDillFile[] = "kernel_service.dill";
const char kPlatformStrongDillFile[] = "vm_platform_strong.dill";
if (frontend_filename_ != nullptr) {
return true;
}
// |dir_prefix| includes the last path seperator.
auto dir_prefix = std::unique_ptr<char, void (*)(void*)>(
GetDirectoryPrefixFromExeName(), free);
if (target_abi_version != Options::kAbiVersionUnset) {
kernel_service_dill_ = nullptr;
kernel_service_dill_size_ = 0;
platform_strong_dill_for_compilation_ = nullptr;
platform_strong_dill_for_compilation_size_ = 0;
// Look in the old abi version directory.
char* script_uri =
Utils::SCreate("%s%s/%d/%s", dir_prefix.get(), kAbiVersionsDir,
target_abi_version, kPlatformStrongDillFile);
if (!TryReadKernelFile(
script_uri,
const_cast<uint8_t**>(&platform_strong_dill_for_compilation_),
&platform_strong_dill_for_compilation_size_)) {
Syslog::PrintErr("Can't find old ABI dill file: %s\n", script_uri);
free(script_uri);
return false;
}
free(script_uri);
script_uri = Utils::SCreate("%s%s/%d/%s", dir_prefix.get(), kAbiVersionsDir,
target_abi_version, kKernelServiceDillFile);
if (!TryReadKernelFile(script_uri,
const_cast<uint8_t**>(&kernel_service_dill_),
&kernel_service_dill_size_)) {
Syslog::PrintErr("Can't find old ABI dill file: %s\n", script_uri);
free(script_uri);
return false;
} else {
frontend_filename_ = script_uri;
return true;
}
}
// Look for the frontend snapshot next to the executable.
frontend_filename_ =
Utils::SCreate("%s%s", dir_prefix.get(), kKernelServiceSnapshot);
if (File::Exists(nullptr, frontend_filename_)) {
return true;
}
free(frontend_filename_);
frontend_filename_ = nullptr;
// If the frontend snapshot is not found next to the executable, then look for
// it in the "snapshots" directory.
frontend_filename_ =
Utils::SCreate("%s%s%s%s", dir_prefix.get(), kSnapshotsDirectory,
File::PathSeparator(), kKernelServiceSnapshot);
if (File::Exists(nullptr, frontend_filename_)) {
return true;
}
free(frontend_filename_);
frontend_filename_ = nullptr;
return true;
}
bool DFE::KernelServiceDillAvailable() const {
return kernel_service_dill_ != nullptr;
}
void DFE::LoadKernelService(const uint8_t** kernel_service_buffer,
intptr_t* kernel_service_buffer_size) {
*kernel_service_buffer = kernel_service_dill_;
*kernel_service_buffer_size = kernel_service_dill_size_;
}
void DFE::LoadPlatform(const uint8_t** kernel_buffer,
intptr_t* kernel_buffer_size) {
*kernel_buffer = platform_strong_dill_for_execution_;
*kernel_buffer_size = platform_strong_dill_for_execution_size_;
}
bool DFE::CanUseDartFrontend() const {
return (platform_strong_dill_for_compilation_ != nullptr) &&
(KernelServiceDillAvailable() || (frontend_filename() != nullptr));
}
class WindowsPathSanitizer {
public:
explicit WindowsPathSanitizer(const char* path) {
// For Windows we need to massage the paths a bit according to
// http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx
//
// Convert
// C:\one\two\three
// to
// /C:/one/two/three
//
// (see builtin.dart#_sanitizeWindowsPath)
intptr_t len = strlen(path);
sanitized_uri_ = reinterpret_cast<char*>(malloc(len + 1 + 1));
if (sanitized_uri_ == nullptr) {
OUT_OF_MEMORY();
}
char* s = sanitized_uri_;
if (len > 2 && path[1] == ':') {
*s++ = '/';
}
for (const char* p = path; *p != '\0'; ++p, ++s) {
*s = *p == '\\' ? '/' : *p;
}
*s = '\0';
}
~WindowsPathSanitizer() { free(sanitized_uri_); }
const char* sanitized_uri() { return sanitized_uri_; }
private:
char* sanitized_uri_;
DISALLOW_COPY_AND_ASSIGN(WindowsPathSanitizer);
};
Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri,
bool incremental,
const char* package_config) {
// TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill
// instead of vm_platform.dill to Frontend for compilation.
#if defined(HOST_OS_WINDOWS)
WindowsPathSanitizer path_sanitizer(script_uri);
const char* sanitized_uri = path_sanitizer.sanitized_uri();
#else
const char* sanitized_uri = script_uri;
#endif
return Dart_CompileToKernel(
sanitized_uri, platform_strong_dill_for_compilation_,
platform_strong_dill_for_compilation_size_, incremental, package_config);
}
void DFE::CompileAndReadScript(const char* script_uri,
uint8_t** kernel_buffer,
intptr_t* kernel_buffer_size,
char** error,
int* exit_code,
const char* package_config) {
Dart_KernelCompilationResult result =
CompileScript(script_uri, use_incremental_compiler(), package_config);
switch (result.status) {
case Dart_KernelCompilationStatus_Ok:
*kernel_buffer = result.kernel;
*kernel_buffer_size = result.kernel_size;
*error = nullptr;
*exit_code = 0;
break;
case Dart_KernelCompilationStatus_Error:
free(result.kernel);
*error = result.error; // Copy error message.
*exit_code = kCompilationErrorExitCode;
break;
case Dart_KernelCompilationStatus_Crash:
free(result.kernel);
*error = result.error; // Copy error message.
*exit_code = kDartFrontendErrorExitCode;
break;
case Dart_KernelCompilationStatus_Unknown:
free(result.kernel);
*error = result.error; // Copy error message.
*exit_code = kErrorExitCode;
break;
}
}
void DFE::ReadScript(const char* script_uri,
uint8_t** kernel_buffer,
intptr_t* kernel_buffer_size) const {
int64_t start = Dart_TimelineGetMicros();
if (!TryReadKernelFile(script_uri, kernel_buffer, kernel_buffer_size)) {
return;
}
if (!Dart_IsKernel(*kernel_buffer, *kernel_buffer_size)) {
free(*kernel_buffer);
*kernel_buffer = nullptr;
*kernel_buffer_size = -1;
}
int64_t end = Dart_TimelineGetMicros();
Dart_TimelineEvent("DFE::ReadScript", start, end,
Dart_Timeline_Event_Duration, 0, nullptr, nullptr);
}
// Attempts to treat [buffer] as a in-memory kernel byte representation.
// If successful, returns [true] and places [buffer] into [kernel_ir], byte size
// into [kernel_ir_size].
// If unsuccessful, returns [false], puts [nullptr] into [kernel_ir], -1 into
// [kernel_ir_size].
static bool TryReadSimpleKernelBuffer(uint8_t* buffer,
uint8_t** p_kernel_ir,
intptr_t* p_kernel_ir_size) {
DartUtils::MagicNumber magic_number =
DartUtils::SniffForMagicNumber(buffer, *p_kernel_ir_size);
if (magic_number == DartUtils::kKernelMagicNumber) {
// Do not free buffer if this is a kernel file - kernel_file will be
// backed by the same memory as the buffer and caller will own it.
// Caller is responsible for freeing the buffer when this function
// returns true.
*p_kernel_ir = buffer;
return true;
}
free(buffer);
*p_kernel_ir = nullptr;
*p_kernel_ir_size = -1;
return false;
}
/// Reads [script_uri] file, returns [true] if successful, [false] otherwise.
///
/// If successful, newly allocated buffer with file contents is returned in
/// [buffer], file contents byte count - in [size].
static bool TryReadFile(const char* script_uri, uint8_t** buffer,
intptr_t* size) {
void* script_file = DartUtils::OpenFileUri(script_uri, false);
if (script_file == nullptr) {
return false;
}
DartUtils::ReadFile(buffer, size, script_file);
DartUtils::CloseFile(script_file);
if (*size <= 0 || buffer == nullptr) {
return false;
}
return true;
}
class KernelIRNode {
public:
KernelIRNode(uint8_t* kernel_ir, intptr_t kernel_size)
: kernel_ir_(kernel_ir), kernel_size_(kernel_size) {}
~KernelIRNode() {
free(kernel_ir_);
}
static void Add(KernelIRNode** p_head, KernelIRNode** p_tail,
KernelIRNode* node) {
if (*p_head == nullptr) {
*p_head = node;
} else {
(*p_tail)->next_ = node;
}
*p_tail = node;
}
static void Merge(KernelIRNode* head, uint8_t** p_bytes,
intptr_t* p_size) {
intptr_t size = 0;
for (KernelIRNode* node = head; node != nullptr; node = node->next_) {
size = size + node->kernel_size_;
}
*p_bytes = reinterpret_cast<uint8_t*>(malloc(size));
if (*p_bytes == nullptr) {
OUT_OF_MEMORY();
}
uint8_t* p = *p_bytes;
KernelIRNode* node = head;
while (node != nullptr) {
memmove(p, node->kernel_ir_, node->kernel_size_);
p += node->kernel_size_;
KernelIRNode* next = node->next_;
node = next;
}
*p_size = size;
}
static void Delete(KernelIRNode* head) {
KernelIRNode* node = head;
while (node != nullptr) {
KernelIRNode* next = node->next_;
delete (node);
node = next;
}
}
private:
uint8_t* kernel_ir_;
intptr_t kernel_size_;
KernelIRNode* next_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(KernelIRNode);
};
class StringPointer {
public:
explicit StringPointer(char* c_str) : c_str_(c_str) {}
~StringPointer() { free(c_str_); }
const char* c_str() { return c_str_; }
private:
char* c_str_;
DISALLOW_COPY_AND_ASSIGN(StringPointer);
};
// Supports "kernel list" files as input.
// Those are text files that start with '#@dill' on new line, followed
// by absolute paths to kernel files or relative paths, that are relative
// to [script_uri] "kernel list" file.
// Below is an example of valid kernel list file:
// ```
// #@dill
// /projects/mytest/build/bin/main.vm.dill
// /projects/mytest/build/packages/mytest/lib.vm.dill
// ```
static bool TryReadKernelListBuffer(const char* script_uri,
uint8_t* buffer,
intptr_t buffer_size,
uint8_t** kernel_ir,
intptr_t* kernel_ir_size) {
const char* kernel_list_dirname = DartUtils::DirName(script_uri);
if (strcmp(kernel_list_dirname, script_uri) == 0) {
kernel_list_dirname = "";
}
KernelIRNode* kernel_ir_head = nullptr;
KernelIRNode* kernel_ir_tail = nullptr;
// Add all kernels to the linked list
char* filename =
reinterpret_cast<char*>(buffer + kernel_list_magic_number.length);
intptr_t filename_size = buffer_size - kernel_list_magic_number.length;
char* tail = reinterpret_cast<char*>(memchr(filename, '\n', filename_size));
while (tail != nullptr) {
*tail = '\0';
intptr_t this_kernel_size;
uint8_t* this_buffer;
StringPointer resolved_filename(
File::IsAbsolutePath(filename)
? strdup(filename)
: Utils::SCreate("%s%s", kernel_list_dirname, filename));
if (!TryReadFile(resolved_filename.c_str(), &this_buffer,
&this_kernel_size)) {
return false;
}
uint8_t* this_kernel_ir;
if (!TryReadSimpleKernelBuffer(this_buffer, &this_kernel_ir,
&this_kernel_size)) {
// Abandon read if any of the files in the list are invalid.
KernelIRNode::Delete(kernel_ir_head);
*kernel_ir = nullptr;
*kernel_ir_size = -1;
return false;
}
KernelIRNode::Add(&kernel_ir_head, &kernel_ir_tail,
new KernelIRNode(this_kernel_ir, this_kernel_size));
filename_size -= tail + 1 - filename;
filename = tail + 1;
tail = reinterpret_cast<char*>(memchr(filename, '\n', filename_size));
}
free(buffer);
KernelIRNode::Merge(kernel_ir_head, kernel_ir, kernel_ir_size);
KernelIRNode::Delete(kernel_ir_head);
return true;
}
bool DFE::TryReadKernelFile(const char* script_uri,
uint8_t** kernel_ir,
intptr_t* kernel_ir_size) {
*kernel_ir = nullptr;
*kernel_ir_size = -1;
uint8_t* buffer;
if (!TryReadFile(script_uri, &buffer, kernel_ir_size)) {
return false;
}
DartUtils::MagicNumber magic_number =
DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size);
if (magic_number == DartUtils::kKernelListMagicNumber) {
return TryReadKernelListBuffer(script_uri, buffer, *kernel_ir_size,
kernel_ir, kernel_ir_size);
}
return TryReadSimpleKernelBuffer(buffer, kernel_ir, kernel_ir_size);
}
} // namespace bin
} // namespace dart