diff --git a/attachment/repos/dart.patch b/attachment/repos/dart.patch index 8b9df115f3301af5c304af6d97f53207bf4774ba..cc33b2e571d3572b11b6cd6f6383a081c57fa7f6 100644 --- a/attachment/repos/dart.patch +++ b/attachment/repos/dart.patch @@ -1,25 +1,3 @@ -diff --git a/.gitignore b/.gitignore -index 3c7338f1ac8..dc54d947c56 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -76,7 +76,7 @@ packages - pubspec.lock - - # The top level package file (this is auto-generated per checkout). --/.dart_tool/package_config.json -+#/.dart_tool/package_config.json - - # Local pub storage - .pub -@@ -100,7 +100,7 @@ tools/xcodebuild - /outline.dill - /generated/ - /crash_logs/ --/build/config/gclient_args.gni -+#/build/config/gclient_args.gni - /pkg/front_end/testcases/old_dills/ - logs/logs.json - logs/results.json diff --git a/pkg/dds/lib/dds.dart b/pkg/dds/lib/dds.dart index 177a9265649..c7038c56ad7 100644 --- a/pkg/dds/lib/dds.dart @@ -112,18 +90,22 @@ index 33be98fc0aa..eda146a0580 100644 String? _authCode; diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn -index 13bfd0a724b..cd34d9a67ed 100644 +index 13bfd0a724b..042b6a45f7e 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn -@@ -88,6 +88,7 @@ config("dart_precompiler_config") { +@@ -87,7 +87,10 @@ config("dart_precompiler_config") { + config("dart_os_config") { defines = [] - -+ print("DART TARGET OS: "+target_os ) +- ++ if (target_os == "linux") { ++ target_os = "ohos" ++ } ++ print("DART TARGET OS: "+target_os ) if (target_os == "android") { defines += [ "DART_TARGET_OS_ANDROID" ] } else if (target_os == "fuchsia") { -@@ -97,6 +98,9 @@ config("dart_os_config") { +@@ -97,6 +100,9 @@ config("dart_os_config") { defines += [ "DART_TARGET_OS_MACOS_IOS" ] } else if (target_os == "linux") { defines += [ "DART_TARGET_OS_LINUX" ] @@ -133,7 +115,7 @@ index 13bfd0a724b..cd34d9a67ed 100644 } else if (target_os == "mac") { defines += [ "DART_TARGET_OS_MACOS" ] } else if (target_os == "win") { -@@ -221,6 +225,7 @@ config("dart_config") { +@@ -221,6 +227,7 @@ config("dart_config") { "-ggdb3", "-fno-rtti", "-fno-exceptions", @@ -200,8 +182,20 @@ index 1e6f9a3bae2..557b9db7bf4 100644 #define SUPPORT_ANALYZE_SNAPSHOT #endif +diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni +index feb1ee80a3b..d93fa28e4ee 100644 +--- a/runtime/bin/builtin_impl_sources.gni ++++ b/runtime/bin/builtin_impl_sources.gni +@@ -21,6 +21,7 @@ builtin_impl_sources = [ + "directory_android.cc", + "directory_fuchsia.cc", + "directory_linux.cc", ++ "directory_ohos.cc", + "directory_macos.cc", + "directory_win.cc", + "exe_utils.cc", diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc -index 14762ed7e63..8b3019c8f03 100644 +index 14762ed7e63..5e70d68bfc6 100644 --- a/runtime/bin/builtin_natives.cc +++ b/runtime/bin/builtin_natives.cc @@ -18,6 +18,13 @@ @@ -210,8 +204,8 @@ index 14762ed7e63..8b3019c8f03 100644 +#ifdef DART_TARGET_OS_OHOS +#ifndef DART_HOST_OS_WINDOWS -+#include -+#include ++//#include ++//#include +#endif +#endif + @@ -225,262 +219,5661 @@ index 14762ed7e63..8b3019c8f03 100644 + +#ifdef DART_TARGET_OS_OHOS +#ifndef DART_HOST_OS_WINDOWS -+ { -+ pthread_t thread = pthread_self(); -+ OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars) ; -+ } ++ // { ++ // pthread_t thread = pthread_self(); ++ // OH_LOG_Print(LOG_APP,LOG_INFO,LOG_DOMAIN,"XComDartVm","Thread:%{public}lu %{public}s",thread,chars) ; ++ // } +#endif +#endif } } // namespace bin -diff --git a/runtime/bin/ffi_unit_test/BUILD.gn b/runtime/bin/ffi_unit_test/BUILD.gn -index c06d9e57e9f..ae681704cc6 100644 ---- a/runtime/bin/ffi_unit_test/BUILD.gn -+++ b/runtime/bin/ffi_unit_test/BUILD.gn -@@ -73,6 +73,10 @@ config("define_target_os_linux") { - defines = [ "DART_TARGET_OS_LINUX" ] - } - -+config("define_target_os_ohos") { -+ defines = [ "DART_TARGET_OS_OHOS" ] +diff --git a/runtime/bin/crypto_ohos.cc b/runtime/bin/crypto_ohos.cc +new file mode 100644 +index 00000000000..b7aca695fb7 +--- /dev/null ++++ b/runtime/bin/crypto_ohos.cc +@@ -0,0 +1,44 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/crypto.h" ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool Crypto::GetRandomBytes(intptr_t count, uint8_t* buffer) { ++ ThreadSignalBlocker signal_blocker(SIGPROF); ++ intptr_t fd = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ open("/dev/urandom", O_RDONLY | O_CLOEXEC)); ++ if (fd < 0) { ++ return false; ++ } ++ intptr_t bytes_read = 0; ++ do { ++ int res = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(fd, buffer + bytes_read, count - bytes_read)); ++ if (res < 0) { ++ int err = errno; ++ close(fd); ++ errno = err; ++ return false; ++ } ++ bytes_read += res; ++ } while (bytes_read < count); ++ close(fd); ++ return true; +} + - config("define_target_os_macos") { - defines = [ "DART_TARGET_OS_MACOS" ] - } -diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h -index 2c9100f2af0..49293cd6306 100644 ---- a/runtime/bin/main_options.h -+++ b/runtime/bin/main_options.h -@@ -90,9 +90,12 @@ enum VerbosityLevel { - kAll, - }; - --static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; -+//static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; -+//TODO 临时修改非localhost -+static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "0.0.0.0"; - static constexpr int DEFAULT_VM_SERVICE_SERVER_PORT = 8181; --static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1; -+//static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1; -+static constexpr int INVALID_VM_SERVICE_SERVER_PORT = DEFAULT_VM_SERVICE_SERVER_PORT; - - class Options { - public: -diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc -index d7296c98e0f..d1abac994ff 100644 ---- a/runtime/bin/vmservice_impl.cc -+++ b/runtime/bin/vmservice_impl.cc -@@ -127,6 +127,9 @@ bool VmService::Setup(const char* server_ip, - - Dart_Handle result; - -+ //TODO 临时关闭验证 -+ auth_codes_disabled=true; ++} // namespace bin ++} // namespace dart + - // Prepare builtin and its dependent libraries for use to resolve URIs. - // Set up various closures, e.g: printing, timers etc. - // Set up 'package root' for URI resolution. -diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h -index 9c8d00fc657..7122f9f7d08 100644 ---- a/runtime/platform/globals.h -+++ b/runtime/platform/globals.h -@@ -383,7 +383,8 @@ struct simd128_value_t { - - #if !defined(DART_TARGET_OS_ANDROID) && !defined(DART_TARGET_OS_FUCHSIA) && \ - !defined(DART_TARGET_OS_MACOS_IOS) && !defined(DART_TARGET_OS_LINUX) && \ -- !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) -+ !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) && \ -+ !defined(DART_TARGET_OS_OHOS) - // No target OS specified; pick the one matching the host OS. - #if defined(DART_HOST_OS_ANDROID) - #define DART_TARGET_OS_ANDROID 1 -@@ -394,6 +395,8 @@ struct simd128_value_t { - #define DART_TARGET_OS_MACOS_IOS 1 - #elif defined(DART_HOST_OS_LINUX) - #define DART_TARGET_OS_LINUX 1 -+#elif defined(DART_HOST_OS_OHOS) -+#define DART_TARGET_OS_OHOS 1 - #elif defined(DART_HOST_OS_MACOS) - #define DART_TARGET_OS_MACOS 1 - #elif defined(DART_HOST_OS_WINDOWS) -@@ -789,6 +792,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) { - #define kTargetOperatingSystemName "fuchsia" - #elif defined(DART_TARGET_OS_LINUX) - #define kTargetOperatingSystemName "linux" -+#elif defined(DART_TARGET_OS_OHOS) -+#define kTargetOperatingSystemName "ohos" - #elif defined(DART_TARGET_OS_MACOS_IOS) - #define kTargetOperatingSystemName "ios" - #elif defined(DART_TARGET_OS_MACOS) -diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc -index 66fa5a8d564..cc5eb834b3d 100644 ---- a/runtime/vm/compiler/ffi/abi.cc -+++ b/runtime/vm/compiler/ffi/abi.cc -@@ -53,6 +53,8 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8, - #define DART_TARGET_OS_NAME Fuchsia - #elif defined(DART_TARGET_OS_LINUX) - #define DART_TARGET_OS_NAME Linux -+#elif defined(DART_TARGET_OS_OHOS) -+#define DART_TARGET_OS_NAME Ohos - #elif defined(DART_TARGET_OS_MACOS) - #if DART_TARGET_OS_MACOS_IOS - #define DART_TARGET_OS_NAME IOS -diff --git a/runtime/vm/compiler/ffi/abi.h b/runtime/vm/compiler/ffi/abi.h -index b693ef85255..54b15f48890 100644 ---- a/runtime/vm/compiler/ffi/abi.h -+++ b/runtime/vm/compiler/ffi/abi.h -@@ -24,6 +24,8 @@ enum class Abi { - kAndroidArm64, - kAndroidIA32, - kAndroidX64, -+ kOhosArm, -+ kOhosArm64, - kFuchsiaArm64, - kFuchsiaX64, - kIOSArm, -@@ -49,9 +51,9 @@ const int64_t num_abis = static_cast(Abi::kWindowsX64) + 1; - // - runtime/vm/compiler/frontend/kernel_to_il.cc - static_assert(static_cast(Abi::kAndroidArm) == 0, - "Enum value unexpected."); --static_assert(static_cast(Abi::kWindowsX64) == 19, -+static_assert(static_cast(Abi::kWindowsX64) == 21, - "Enum value unexpected."); --static_assert(num_abis == 20, "Enum value unexpected."); -+static_assert(num_abis == 22, "Enum value unexpected."); - - // The target ABI. Defines sizes and alignment of native types. - Abi TargetAbi(); -diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc -index 001605f6163..11529096e4e 100644 ---- a/runtime/vm/compiler/ffi/native_calling_convention.cc -+++ b/runtime/vm/compiler/ffi/native_calling_convention.cc -@@ -31,7 +31,7 @@ static bool SoftFpAbi() { ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc +index d2af2c50dad..d4e9082b9f0 100644 +--- a/runtime/bin/directory_linux.cc ++++ b/runtime/bin/directory_linux.cc +@@ -413,6 +413,10 @@ bool Directory::Create(Namespace* namespc, const char* dir_name) { } - #else // !defined(FFI_UNIT_TESTS) - static bool SoftFpAbi() { --#if defined(TARGET_ARCH_ARM) && defined(DART_TARGET_OS_ANDROID) -+#if defined(TARGET_ARCH_ARM) && (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS)) - return true; - #else - return false; -diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc -index 707d9e845db..08a0930de5d 100644 ---- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc -+++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc -@@ -658,7 +658,7 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress46127) { - RunSignatureTest(Z, "regress46127", arguments, struct_type); - - #if defined(TARGET_ARCH_IA32) && \ -- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) -+ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) || defined(DART_TARGET_OS_OHOS) - // We must count the result pointer passed on the stack as well. - EXPECT_EQ(4, native_calling_convention.StackTopInBytes()); - #else -diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc -index ce9a8ffbca3..04c3aefb682 100644 ---- a/runtime/vm/cpu_arm.cc -+++ b/runtime/vm/cpu_arm.cc -@@ -59,7 +59,7 @@ DEFINE_FLAG(bool, - "Use integer division instruction if supported"); - - #if defined(TARGET_HOST_MISMATCH) --#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) -+#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_OHOS) - DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI."); - #else - DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI."); -diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc -index a0e900a85a3..9e022d1a470 100644 ---- a/runtime/vm/dart.cc -+++ b/runtime/vm/dart.cc -@@ -1205,6 +1205,8 @@ char* Dart::FeaturesString(IsolateGroup* isolate_group, - #endif - #elif defined(DART_TARGET_OS_LINUX) - buffer.AddString(" linux"); -+#elif defined(DART_TARGET_OS_OHOS) -+ buffer.AddString(" ohos"); - #elif defined(DART_TARGET_OS_WINDOWS) - buffer.AddString(" windows"); - #else -diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h -index 80b830e821e..13db280340a 100644 ---- a/runtime/vm/globals.h -+++ b/runtime/vm/globals.h -@@ -115,7 +115,7 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; - - #if defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \ - defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \ -- defined(DART_TARGET_OS_ANDROID) -+ defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS) - #define SUPPORT_TIMELINE 1 - #endif -diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc -index c11a5faf6fe..cc001b8f826 100644 ---- a/runtime/vm/image_snapshot.cc -+++ b/runtime/vm/image_snapshot.cc -@@ -1051,7 +1051,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { - #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) - stream_->WriteString(".section __DWARF,__debug_abbrev,regular,debug\n"); - #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) - stream_->WriteString(".section .debug_abbrev,\"\"\n"); - #else - UNIMPLEMENTED(); -@@ -1061,7 +1061,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { - #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) - stream_->WriteString(".section __DWARF,__debug_info,regular,debug\n"); - #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) - stream_->WriteString(".section .debug_info,\"\"\n"); - #else - UNIMPLEMENTED(); -@@ -1073,7 +1073,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { - #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) - stream_->WriteString(".section __DWARF,__debug_line,regular,debug\n"); - #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) - stream_->WriteString(".section .debug_line,\"\"\n"); - #else - UNIMPLEMENTED(); -@@ -1154,7 +1154,7 @@ void AssemblyImageWriter::Finalize() { - } - - #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) - // Non-executable stack. - assembly_stream_->WriteString(".section .note.GNU-stack,\"\"\n"); - #endif -@@ -1312,7 +1312,7 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream, - WriteBytes(bytes + last_position, symbol.offset - last_position); - assembly_stream_->Printf("\"%s\":\n", symbol.name); - #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) - // Output size and type of the read-only data symbol to the assembly stream. - assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size); - assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name); -@@ -1354,7 +1354,7 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section, - current_symbols_ = - new (zone_) ZoneGrowableArray(zone_, 0); - #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) - assembly_stream_->WriteString(".section .rodata\n"); - #elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) - assembly_stream_->WriteString(".const\n"); -@@ -1411,7 +1411,7 @@ void AssemblyImageWriter::ExitSection(ProgramSection name, - // We should still be in the same section as the last EnterSection. - ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm)); - #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ -- defined(DART_TARGET_OS_FUCHSIA) -+ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + const char* Directory::SystemTemp(Namespace* namespc) { ++ if (Directory::system_temp_path_override_ != NULL) { ++ return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); ++ } ++ + PathBuffer path; + const char* temp_dir = getenv("TMPDIR"); + if (temp_dir == NULL) { +diff --git a/runtime/bin/directory_ohos.cc b/runtime/bin/directory_ohos.cc +new file mode 100644 +index 00000000000..b5421969b61 +--- /dev/null ++++ b/runtime/bin/directory_ohos.cc +@@ -0,0 +1,513 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/directory.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/crypto.h" ++#include "bin/dartutils.h" ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/namespace.h" ++#include "bin/platform.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++PathBuffer::PathBuffer() : length_(0) { ++ data_ = calloc(PATH_MAX + 1, sizeof(char)); // NOLINT ++} ++ ++PathBuffer::~PathBuffer() { ++ free(data_); ++} ++ ++bool PathBuffer::AddW(const wchar_t* name) { ++ UNREACHABLE(); ++ return false; ++} ++ ++char* PathBuffer::AsString() const { ++ return reinterpret_cast(data_); ++} ++ ++wchar_t* PathBuffer::AsStringW() const { ++ UNREACHABLE(); ++ return NULL; ++} ++ ++const char* PathBuffer::AsScopedString() const { ++ return DartUtils::ScopedCopyCString(AsString()); ++} ++ ++bool PathBuffer::Add(const char* name) { ++ char* data = AsString(); ++ int written = snprintf(data + length_, PATH_MAX - length_, "%s", name); ++ data[PATH_MAX] = '\0'; ++ if ((written <= PATH_MAX - length_) && (written >= 0) && ++ (static_cast(written) == strnlen(name, PATH_MAX + 1))) { ++ length_ += written; ++ return true; ++ } else { ++ errno = ENAMETOOLONG; ++ return false; ++ } ++} ++ ++void PathBuffer::Reset(intptr_t new_length) { ++ length_ = new_length; ++ AsString()[length_] = '\0'; ++} ++ ++// A linked list of symbolic links, with their unique file system identifiers. ++// These are scanned to detect loops while doing a recursive directory listing. ++struct LinkList { ++ dev_t dev; ++ ino64_t ino; ++ LinkList* next; ++}; ++ ++ListType DirectoryListingEntry::Next(DirectoryListing* listing) { ++ if (done_) { ++ return kListDone; ++ } ++ ++ if (fd_ == -1) { ++ ASSERT(lister_ == 0); ++ NamespaceScope ns(listing->namespc(), listing->path_buffer().AsString()); ++ const int listingfd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); ++ if (listingfd < 0) { ++ done_ = true; ++ return kListError; ++ } ++ fd_ = listingfd; ++ } ++ ++ if (lister_ == 0) { ++ do { ++ lister_ = reinterpret_cast(fdopendir(fd_)); ++ } while ((lister_ == 0) && (errno == EINTR)); ++ if (lister_ == 0) { ++ done_ = true; ++ return kListError; ++ } ++ if (parent_ != NULL) { ++ if (!listing->path_buffer().Add(File::PathSeparator())) { ++ return kListError; ++ } ++ } ++ path_length_ = listing->path_buffer().length(); ++ } ++ // Reset. ++ listing->path_buffer().Reset(path_length_); ++ ResetLink(); ++ ++ // Iterate the directory and post the directories and files to the ++ // ports. ++ errno = 0; ++ dirent* entry = readdir(reinterpret_cast(lister_)); ++ if (entry != NULL) { ++ if (!listing->path_buffer().Add(entry->d_name)) { ++ done_ = true; ++ return kListError; ++ } ++ switch (entry->d_type) { ++ case DT_DIR: ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ case DT_BLK: ++ case DT_CHR: ++ case DT_FIFO: ++ case DT_SOCK: ++ case DT_REG: ++ return kListFile; ++ case DT_LNK: ++ if (!listing->follow_links()) { ++ return kListLink; ++ } ++ // Else fall through to next case. ++ FALL_THROUGH; ++ case DT_UNKNOWN: { ++ // On some file systems the entry type is not determined by ++ // readdir. For those and for links we use stat to determine ++ // the actual entry type. Notice that stat returns the type of ++ // the file pointed to. ++ NamespaceScope ns(listing->namespc(), ++ listing->path_buffer().AsString()); ++ struct stat64 entry_info; ++ int stat_success; ++ stat_success = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); ++ if (stat_success == -1) { ++ return kListError; ++ } ++ if (listing->follow_links() && S_ISLNK(entry_info.st_mode)) { ++ // Check to see if we are in a loop created by a symbolic link. ++ LinkList current_link = {entry_info.st_dev, entry_info.st_ino, link_}; ++ LinkList* previous = link_; ++ while (previous != NULL) { ++ if ((previous->dev == current_link.dev) && ++ (previous->ino == current_link.ino)) { ++ // Report the looping link as a link, rather than following it. ++ return kListLink; ++ } ++ previous = previous->next; ++ } ++ stat_success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ if (stat_success == -1 || (S_IFMT & entry_info.st_mode) == 0) { ++ // Report a broken link as a link, even if follow_links is true. ++ // A symbolic link can potentially point to an anon_inode. For ++ // example, an epoll file descriptor will have a symbolic link whose ++ // content is the string anon_inode:[eventpoll]. In this case, the ++ // target doesn't belong to any regular file catogory. ++ return kListLink; ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ // Recurse into the subdirectory with current_link added to the ++ // linked list of seen file system links. ++ link_ = new LinkList(current_link); ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ } ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ if ((strcmp(entry->d_name, ".") == 0) || ++ (strcmp(entry->d_name, "..") == 0)) { ++ return Next(listing); ++ } ++ return kListDirectory; ++ } else if (S_ISLNK(entry_info.st_mode)) { ++ return kListLink; ++ } else { ++ // Regular files, character devices, block devices, fifos, sockets and ++ // unknown types are all considered as files. ++ return kListFile; ++ } ++ } ++ ++ default: ++ // We should have covered all the bases. If not, let's get an error. ++ FATAL1("Unexpected d_type: %d\n", entry->d_type); ++ return kListError; ++ } ++ } ++ done_ = true; ++ ++ if (errno != 0) { ++ return kListError; ++ } ++ ++ return kListDone; ++} ++ ++DirectoryListingEntry::~DirectoryListingEntry() { ++ ResetLink(); ++ if (lister_ != 0) { ++ // This also closes fd_. ++ VOID_NO_RETRY_EXPECTED(closedir(reinterpret_cast(lister_))); ++ } ++} ++ ++void DirectoryListingEntry::ResetLink() { ++ if ((link_ != NULL) && ((parent_ == NULL) || (parent_->link_ != link_))) { ++ delete link_; ++ link_ = NULL; ++ } ++ if (parent_ != NULL) { ++ link_ = parent_->link_; ++ } ++} ++ ++static bool DeleteRecursively(int dirfd, PathBuffer* path); ++ ++static bool DeleteFile(int dirfd, char* file_name, PathBuffer* path) { ++ return path->Add(file_name) && ++ (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); ++} ++ ++static bool DeleteDir(int dirfd, char* dir_name, PathBuffer* path) { ++ if ((strcmp(dir_name, ".") == 0) || (strcmp(dir_name, "..") == 0)) { ++ return true; ++ } ++ return path->Add(dir_name) && DeleteRecursively(dirfd, path); ++} ++ ++static bool DeleteRecursively(int dirfd, PathBuffer* path) { ++ // Do not recurse into links for deletion. Instead delete the link. ++ // If it's a file, delete it. ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY( ++ fstatat64(dirfd, path->AsString(), &st, AT_SYMLINK_NOFOLLOW)) == -1) { ++ return false; ++ } else if (!S_ISDIR(st.st_mode)) { ++ return (NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), 0)) == 0); ++ } ++ ++ if (!path->Add(File::PathSeparator())) { ++ return false; ++ } ++ ++ // Not a link. Attempt to open as a directory and recurse into the ++ // directory. ++ const int fd = ++ TEMP_FAILURE_RETRY(openat64(dirfd, path->AsString(), O_DIRECTORY)); ++ if (fd < 0) { ++ return false; ++ } ++ DIR* dir_pointer; ++ do { ++ dir_pointer = fdopendir(fd); ++ } while ((dir_pointer == NULL) && (errno == EINTR)); ++ if (dir_pointer == NULL) { ++ FDUtils::SaveErrorAndClose(fd); ++ return false; ++ } ++ ++ // Iterate the directory and delete all files and directories. ++ int path_length = path->length(); ++ while (true) { ++ // In case `readdir()` returns `NULL` we distinguish between end-of-stream ++ // and error by looking if `errno` was updated. ++ errno = 0; ++ // In glibc 2.24+, readdir_r is deprecated. ++ // According to the man page for readdir: ++ // "readdir(3) is not required to be thread-safe. However, in modern ++ // implementations (including the glibc implementation), concurrent calls to ++ // readdir(3) that specify different directory streams are thread-safe." ++ dirent* entry = readdir(dir_pointer); ++ if (entry == NULL) { ++ // Failed to read next directory entry. ++ if (errno != 0) { ++ break; ++ } ++ // End of directory. ++ int status = NO_RETRY_EXPECTED(closedir(dir_pointer)); ++ if (status != 0) { ++ return false; ++ } ++ status = ++ NO_RETRY_EXPECTED(unlinkat(dirfd, path->AsString(), AT_REMOVEDIR)); ++ return status == 0; ++ } ++ bool ok = false; ++ switch (entry->d_type) { ++ case DT_DIR: ++ ok = DeleteDir(dirfd, entry->d_name, path); ++ break; ++ case DT_BLK: ++ case DT_CHR: ++ case DT_FIFO: ++ case DT_SOCK: ++ case DT_REG: ++ case DT_LNK: ++ // Treat all links as files. This will delete the link which ++ // is what we want no matter if the link target is a file or a ++ // directory. ++ ok = DeleteFile(dirfd, entry->d_name, path); ++ break; ++ case DT_UNKNOWN: { ++ if (!path->Add(entry->d_name)) { ++ break; ++ } ++ // On some file systems the entry type is not determined by ++ // readdir. For those we use lstat to determine the entry ++ // type. ++ struct stat64 entry_info; ++ if (TEMP_FAILURE_RETRY(fstatat64(dirfd, path->AsString(), &entry_info, ++ AT_SYMLINK_NOFOLLOW)) == -1) { ++ break; ++ } ++ path->Reset(path_length); ++ if (S_ISDIR(entry_info.st_mode)) { ++ ok = DeleteDir(dirfd, entry->d_name, path); ++ } else { ++ // Treat links as files. This will delete the link which is ++ // what we want no matter if the link target is a file or a ++ // directory. ++ ok = DeleteFile(dirfd, entry->d_name, path); ++ } ++ break; ++ } ++ default: ++ // We should have covered all the bases. If not, let's get an error. ++ FATAL1("Unexpected d_type: %d\n", entry->d_type); ++ break; ++ } ++ if (!ok) { ++ break; ++ } ++ path->Reset(path_length); ++ } ++ // Only happens if an error. ++ ASSERT(errno != 0); ++ int err = errno; ++ VOID_NO_RETRY_EXPECTED(closedir(dir_pointer)); ++ errno = err; ++ return false; ++} ++ ++Directory::ExistsResult Directory::Exists(Namespace* namespc, ++ const char* dir_name) { ++ NamespaceScope ns(namespc, dir_name); ++ struct stat64 entry_info; ++ int success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ if (success == 0) { ++ if (S_ISDIR(entry_info.st_mode)) { ++ return EXISTS; ++ } else { ++ // An OSError may be constructed based on the return value of this ++ // function, so set errno to something that makes sense. ++ errno = ENOTDIR; ++ return DOES_NOT_EXIST; ++ } ++ } else { ++ if ((errno == EACCES) || (errno == EBADF) || (errno == EFAULT) || ++ (errno == ENOMEM) || (errno == EOVERFLOW)) { ++ // Search permissions denied for one of the directories in the ++ // path or a low level error occurred. We do not know if the ++ // directory exists. ++ return UNKNOWN; ++ } ++ ASSERT((errno == ELOOP) || (errno == ENAMETOOLONG) || (errno == ENOENT) || ++ (errno == ENOTDIR)); ++ return DOES_NOT_EXIST; ++ } ++} ++ ++char* Directory::CurrentNoScope() { ++ return getcwd(NULL, 0); ++} ++ ++bool Directory::Create(Namespace* namespc, const char* dir_name) { ++ NamespaceScope ns(namespc, dir_name); ++ // Create the directory with the permissions specified by the ++ // process umask. ++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); ++ // If the directory already exists, treat it as a success. ++ if ((result == -1) && (errno == EEXIST)) { ++ return (Exists(namespc, dir_name) == EXISTS); ++ } ++ return (result == 0); ++} ++ ++const char* Directory::SystemTemp(Namespace* namespc) { ++ if (Directory::system_temp_path_override_ != NULL) { ++ return DartUtils::ScopedCopyCString(Directory::system_temp_path_override_); ++ } ++ ++ PathBuffer path; ++ const char* temp_dir = getenv("TMPDIR"); ++ if (temp_dir == NULL) { ++ temp_dir = getenv("TMP"); ++ } ++ if (temp_dir == NULL) { ++ temp_dir = "/tmp"; ++ } ++ NamespaceScope ns(namespc, temp_dir); ++ if (!path.Add(ns.path())) { ++ return NULL; ++ } ++ ++ // Remove any trailing slash. ++ char* result = path.AsString(); ++ int length = strlen(result); ++ if ((length > 1) && (result[length - 1] == '/')) { ++ result[length - 1] = '\0'; ++ } ++ return path.AsScopedString(); ++} ++ ++// Returns a new, unused directory name, adding characters to the end ++// of prefix. Creates the directory with the permissions specified ++// by the process umask. ++// The return value is Dart_ScopeAllocated. ++const char* Directory::CreateTemp(Namespace* namespc, const char* prefix) { ++ PathBuffer path; ++ const int firstchar = 'A'; ++ const int numchars = 'Z' - 'A' + 1; ++ uint8_t random_bytes[7]; ++ ++ // mkdtemp doesn't have an "at" variant, so we have to simulate it. ++ if (!path.Add(prefix)) { ++ return NULL; ++ } ++ intptr_t prefix_length = path.length(); ++ while (true) { ++ Crypto::GetRandomBytes(6, random_bytes); ++ for (intptr_t i = 0; i < 6; i++) { ++ random_bytes[i] = (random_bytes[i] % numchars) + firstchar; ++ } ++ random_bytes[6] = '\0'; ++ if (!path.Add(reinterpret_cast(random_bytes))) { ++ return NULL; ++ } ++ NamespaceScope ns(namespc, path.AsString()); ++ const int result = NO_RETRY_EXPECTED(mkdirat(ns.fd(), ns.path(), 0777)); ++ if (result == 0) { ++ return path.AsScopedString(); ++ } else if (errno == EEXIST) { ++ path.Reset(prefix_length); ++ } else { ++ return NULL; ++ } ++ } ++} ++ ++bool Directory::Delete(Namespace* namespc, ++ const char* dir_name, ++ bool recursive) { ++ NamespaceScope ns(namespc, dir_name); ++ if (!recursive) { ++ if ((File::GetType(namespc, dir_name, false) == File::kIsLink) && ++ (File::GetType(namespc, dir_name, true) == File::kIsDirectory)) { ++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0; ++ } ++ return NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), AT_REMOVEDIR)) == 0; ++ } else { ++ PathBuffer path; ++ if (!path.Add(ns.path())) { ++ return false; ++ } ++ return DeleteRecursively(ns.fd(), &path); ++ } ++} ++ ++bool Directory::Rename(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ ExistsResult exists = Exists(namespc, old_path); ++ if (exists != EXISTS) { ++ return false; ++ } ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler_ohos.cc b/runtime/bin/eventhandler_ohos.cc +new file mode 100644 +index 00000000000..20204a1df3b +--- /dev/null ++++ b/runtime/bin/eventhandler_ohos.cc +@@ -0,0 +1,443 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/eventhandler.h" ++#include "bin/eventhandler_ohos.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/dartutils.h" ++#include "bin/fdutils.h" ++#include "bin/lockers.h" ++#include "bin/process.h" ++#include "bin/socket.h" ++#include "bin/thread.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++intptr_t DescriptorInfo::GetPollEvents() { ++ // Do not ask for EPOLLERR and EPOLLHUP explicitly as they are ++ // triggered anyway. ++ intptr_t events = 0; ++ if ((Mask() & (1 << kInEvent)) != 0) { ++ events |= EPOLLIN; ++ } ++ if ((Mask() & (1 << kOutEvent)) != 0) { ++ events |= EPOLLOUT; ++ } ++ return events; ++} ++ ++// Unregister the file descriptor for a DescriptorInfo structure with ++// epoll. ++static void RemoveFromEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) { ++ VOID_NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, di->fd(), NULL)); ++} ++ ++static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) { ++ struct epoll_event event; ++ event.events = EPOLLRDHUP | di->GetPollEvents(); ++ if (!di->IsListeningSocket()) { ++ event.events |= EPOLLET; ++ } ++ event.data.ptr = di; ++ int status = ++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, di->fd(), &event)); ++ if (status == -1) { ++ // TODO(dart:io): Verify that the dart end is handling this correctly. ++ ++ // Epoll does not accept the file descriptor. It could be due to ++ // already closed file descriptor, or unuspported devices, such ++ // as /dev/null. In such case, mark the file descriptor as closed, ++ // so dart will handle it accordingly. ++ di->NotifyAllDartPorts(1 << kCloseEvent); ++ } ++} ++ ++EventHandlerImplementation::EventHandlerImplementation() ++ : socket_map_(&SimpleHashMap::SamePointerValue, 16) { ++ intptr_t result; ++ result = NO_RETRY_EXPECTED(pipe(interrupt_fds_)); ++ if (result != 0) { ++ FATAL("Pipe creation failed"); ++ } ++ if (!FDUtils::SetNonBlocking(interrupt_fds_[0])) { ++ FATAL("Failed to set pipe fd non blocking\n"); ++ } ++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[0])) { ++ FATAL("Failed to set pipe fd close on exec\n"); ++ } ++ if (!FDUtils::SetCloseOnExec(interrupt_fds_[1])) { ++ FATAL("Failed to set pipe fd close on exec\n"); ++ } ++ shutdown_ = false; ++ // The initial size passed to epoll_create is ignore on newer (>= ++ // 2.6.8) Linux versions ++ static const int kEpollInitialSize = 64; ++ epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize)); ++ if (epoll_fd_ == -1) { ++ FATAL1("Failed creating epoll file descriptor: %i", errno); ++ } ++ if (!FDUtils::SetCloseOnExec(epoll_fd_)) { ++ FATAL("Failed to set epoll fd close on exec\n"); ++ } ++ // Register the interrupt_fd with the epoll instance. ++ struct epoll_event event; ++ event.events = EPOLLIN; ++ event.data.ptr = NULL; ++ int status = NO_RETRY_EXPECTED( ++ epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupt_fds_[0], &event)); ++ if (status == -1) { ++ FATAL("Failed adding interrupt fd to epoll instance"); ++ } ++ timer_fd_ = NO_RETRY_EXPECTED(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)); ++ if (timer_fd_ == -1) { ++ FATAL1("Failed creating timerfd file descriptor: %i", errno); ++ } ++ // Register the timer_fd_ with the epoll instance. ++ event.events = EPOLLIN; ++ event.data.fd = timer_fd_; ++ status = ++ NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &event)); ++ if (status == -1) { ++ FATAL2("Failed adding timerfd fd(%i) to epoll instance: %i", timer_fd_, ++ errno); ++ } ++} ++ ++static void DeleteDescriptorInfo(void* info) { ++ DescriptorInfo* di = reinterpret_cast(info); ++ di->Close(); ++ delete di; ++} ++ ++EventHandlerImplementation::~EventHandlerImplementation() { ++ socket_map_.Clear(DeleteDescriptorInfo); ++ close(epoll_fd_); ++ close(timer_fd_); ++ close(interrupt_fds_[0]); ++ close(interrupt_fds_[1]); ++} ++ ++void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask, ++ DescriptorInfo* di) { ++ intptr_t new_mask = di->Mask(); ++ if ((old_mask != 0) && (new_mask == 0)) { ++ RemoveFromEpollInstance(epoll_fd_, di); ++ } else if ((old_mask == 0) && (new_mask != 0)) { ++ AddToEpollInstance(epoll_fd_, di); ++ } else if ((old_mask != 0) && (new_mask != 0) && (old_mask != new_mask)) { ++ ASSERT(!di->IsListeningSocket()); ++ RemoveFromEpollInstance(epoll_fd_, di); ++ AddToEpollInstance(epoll_fd_, di); ++ } ++} ++ ++DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo( ++ intptr_t fd, ++ bool is_listening) { ++ ASSERT(fd >= 0); ++ SimpleHashMap::Entry* entry = socket_map_.Lookup( ++ GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true); ++ ASSERT(entry != NULL); ++ DescriptorInfo* di = reinterpret_cast(entry->value); ++ if (di == NULL) { ++ // If there is no data in the hash map for this file descriptor a ++ // new DescriptorInfo for the file descriptor is inserted. ++ if (is_listening) { ++ di = new DescriptorInfoMultiple(fd); ++ } else { ++ di = new DescriptorInfoSingle(fd); ++ } ++ entry->value = di; ++ } ++ ASSERT(fd == di->fd()); ++ return di; ++} ++ ++void EventHandlerImplementation::WakeupHandler(intptr_t id, ++ Dart_Port dart_port, ++ int64_t data) { ++ InterruptMessage msg; ++ msg.id = id; ++ msg.dart_port = dart_port; ++ msg.data = data; ++ // WriteToBlocking will write up to 512 bytes atomically, and since our msg ++ // is smaller than 512, we don't need a thread lock. ++ // See: http://linux.die.net/man/7/pipe, section 'Pipe_buf'. ++ ASSERT(kInterruptMessageSize < PIPE_BUF); ++ intptr_t result = ++ FDUtils::WriteToBlocking(interrupt_fds_[1], &msg, kInterruptMessageSize); ++ if (result != kInterruptMessageSize) { ++ if (result == -1) { ++ perror("Interrupt message failure:"); ++ } ++ FATAL1("Interrupt message failure. Wrote %" Pd " bytes.", result); ++ } ++} ++ ++void EventHandlerImplementation::HandleInterruptFd() { ++ const intptr_t MAX_MESSAGES = kInterruptMessageSize; ++ InterruptMessage msg[MAX_MESSAGES]; ++ ssize_t bytes = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(interrupt_fds_[0], msg, MAX_MESSAGES * kInterruptMessageSize)); ++ for (ssize_t i = 0; i < bytes / kInterruptMessageSize; i++) { ++ if (msg[i].id == kTimerId) { ++ timeout_queue_.UpdateTimeout(msg[i].dart_port, msg[i].data); ++ UpdateTimerFd(); ++ } else if (msg[i].id == kShutdownId) { ++ shutdown_ = true; ++ } else { ++ ASSERT((msg[i].data & COMMAND_MASK) != 0); ++ Socket* socket = reinterpret_cast(msg[i].id); ++ RefCntReleaseScope rs(socket); ++ if (socket->fd() == -1) { ++ continue; ++ } ++ DescriptorInfo* di = ++ GetDescriptorInfo(socket->fd(), IS_LISTENING_SOCKET(msg[i].data)); ++ if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) { ++ ASSERT(!di->IsListeningSocket()); ++ // Close the socket for reading. ++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD)); ++ } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) { ++ ASSERT(!di->IsListeningSocket()); ++ // Close the socket for writing. ++ VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR)); ++ } else if (IS_COMMAND(msg[i].data, kCloseCommand)) { ++ // Close the socket and free system resources and move on to next ++ // message. ++ if (IS_SIGNAL_SOCKET(msg[i].data)) { ++ Process::ClearSignalHandlerByFd(di->fd(), socket->isolate_port()); ++ } ++ intptr_t old_mask = di->Mask(); ++ Dart_Port port = msg[i].dart_port; ++ if (port != ILLEGAL_PORT) { ++ di->RemovePort(port); ++ } ++ intptr_t new_mask = di->Mask(); ++ UpdateEpollInstance(old_mask, di); ++ ++ intptr_t fd = di->fd(); ++ ASSERT(fd == socket->fd()); ++ if (di->IsListeningSocket()) { ++ // We only close the socket file descriptor from the operating ++ // system if there are no other dart socket objects which ++ // are listening on the same (address, port) combination. ++ ListeningSocketRegistry* registry = ++ ListeningSocketRegistry::Instance(); ++ ++ MutexLocker locker(registry->mutex()); ++ ++ if (registry->CloseSafe(socket)) { ++ ASSERT(new_mask == 0); ++ socket_map_.Remove(GetHashmapKeyFromFd(fd), ++ GetHashmapHashFromFd(fd)); ++ di->Close(); ++ delete di; ++ } ++ socket->CloseFd(); ++ } else { ++ ASSERT(new_mask == 0); ++ socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd)); ++ di->Close(); ++ delete di; ++ socket->CloseFd(); ++ } ++ DartUtils::PostInt32(port, 1 << kDestroyedEvent); ++ } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) { ++ int count = TOKEN_COUNT(msg[i].data); ++ intptr_t old_mask = di->Mask(); ++ di->ReturnTokens(msg[i].dart_port, count); ++ UpdateEpollInstance(old_mask, di); ++ } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) { ++ // `events` can only have kInEvent/kOutEvent flags set. ++ intptr_t events = msg[i].data & EVENT_MASK; ++ ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent))); ++ ++ intptr_t old_mask = di->Mask(); ++ di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK); ++ UpdateEpollInstance(old_mask, di); ++ } else { ++ UNREACHABLE(); ++ } ++ } ++ } ++} ++ ++void EventHandlerImplementation::UpdateTimerFd() { ++ struct itimerspec it; ++ memset(&it, 0, sizeof(it)); ++ if (timeout_queue_.HasTimeout()) { ++ int64_t millis = timeout_queue_.CurrentTimeout(); ++ it.it_value.tv_sec = millis / 1000; ++ it.it_value.tv_nsec = (millis % 1000) * 1000000; ++ } ++ VOID_NO_RETRY_EXPECTED( ++ timerfd_settime(timer_fd_, TFD_TIMER_ABSTIME, &it, NULL)); ++} ++ ++#ifdef DEBUG_POLL ++static void PrintEventMask(intptr_t fd, intptr_t events) { ++ Syslog::Print("%d ", fd); ++ if ((events & EPOLLIN) != 0) { ++ Syslog::Print("EPOLLIN "); ++ } ++ if ((events & EPOLLPRI) != 0) { ++ Syslog::Print("EPOLLPRI "); ++ } ++ if ((events & EPOLLOUT) != 0) { ++ Syslog::Print("EPOLLOUT "); ++ } ++ if ((events & EPOLLERR) != 0) { ++ Syslog::Print("EPOLLERR "); ++ } ++ if ((events & EPOLLHUP) != 0) { ++ Syslog::Print("EPOLLHUP "); ++ } ++ if ((events & EPOLLRDHUP) != 0) { ++ Syslog::Print("EPOLLRDHUP "); ++ } ++ int all_events = ++ EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP; ++ if ((events & ~all_events) != 0) { ++ Syslog::Print("(and %08x) ", events & ~all_events); ++ } ++ Syslog::Print("(available %d) ", FDUtils::AvailableBytes(fd)); ++ ++ Syslog::Print("\n"); ++} ++#endif ++ ++intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events, ++ DescriptorInfo* di) { ++#ifdef DEBUG_POLL ++ PrintEventMask(di->fd(), events); ++#endif ++ if ((events & EPOLLERR) != 0) { ++ // Return error only if EPOLLIN is present. ++ return ((events & EPOLLIN) != 0) ? (1 << kErrorEvent) : 0; ++ } ++ intptr_t event_mask = 0; ++ if ((events & EPOLLIN) != 0) { ++ event_mask |= (1 << kInEvent); ++ } ++ if ((events & EPOLLOUT) != 0) { ++ event_mask |= (1 << kOutEvent); ++ } ++ if ((events & (EPOLLHUP | EPOLLRDHUP)) != 0) { ++ event_mask |= (1 << kCloseEvent); ++ } ++ return event_mask; ++} ++ ++void EventHandlerImplementation::HandleEvents(struct epoll_event* events, ++ int size) { ++ bool interrupt_seen = false; ++ for (int i = 0; i < size; i++) { ++ if (events[i].data.ptr == NULL) { ++ interrupt_seen = true; ++ } else if (events[i].data.fd == timer_fd_) { ++ int64_t val; ++ VOID_TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ read(timer_fd_, &val, sizeof(val))); ++ if (timeout_queue_.HasTimeout()) { ++ DartUtils::PostNull(timeout_queue_.CurrentPort()); ++ timeout_queue_.RemoveCurrent(); ++ } ++ UpdateTimerFd(); ++ } else { ++ DescriptorInfo* di = ++ reinterpret_cast(events[i].data.ptr); ++ const intptr_t old_mask = di->Mask(); ++ const intptr_t event_mask = GetPollEvents(events[i].events, di); ++ if ((event_mask & (1 << kErrorEvent)) != 0) { ++ di->NotifyAllDartPorts(event_mask); ++ UpdateEpollInstance(old_mask, di); ++ } else if (event_mask != 0) { ++ Dart_Port port = di->NextNotifyDartPort(event_mask); ++ ASSERT(port != 0); ++ UpdateEpollInstance(old_mask, di); ++ DartUtils::PostInt32(port, event_mask); ++ } ++ } ++ } ++ if (interrupt_seen) { ++ // Handle after socket events, so we avoid closing a socket before we handle ++ // the current events. ++ HandleInterruptFd(); ++ } ++} ++ ++void EventHandlerImplementation::Poll(uword args) { ++ ThreadSignalBlocker signal_blocker(SIGPROF); ++ static const intptr_t kMaxEvents = 16; ++ struct epoll_event events[kMaxEvents]; ++ EventHandler* handler = reinterpret_cast(args); ++ EventHandlerImplementation* handler_impl = &handler->delegate_; ++ ASSERT(handler_impl != NULL); ++ ++ while (!handler_impl->shutdown_) { ++ intptr_t result = TEMP_FAILURE_RETRY_NO_SIGNAL_BLOCKER( ++ epoll_wait(handler_impl->epoll_fd_, events, kMaxEvents, -1)); ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ if (result <= 0) { ++ if (errno != EWOULDBLOCK) { ++ perror("Poll failed"); ++ } ++ } else { ++ handler_impl->HandleEvents(events, result); ++ } ++ } ++ DEBUG_ASSERT(ReferenceCounted::instances() == 0); ++ handler->NotifyShutdownDone(); ++} ++ ++void EventHandlerImplementation::Start(EventHandler* handler) { ++ int result = ++ Thread::Start("dart:io EventHandler", &EventHandlerImplementation::Poll, ++ reinterpret_cast(handler)); ++ if (result != 0) { ++ FATAL1("Failed to start event handler thread %d", result); ++ } ++} ++ ++void EventHandlerImplementation::Shutdown() { ++ SendData(kShutdownId, 0, 0); ++} ++ ++void EventHandlerImplementation::SendData(intptr_t id, ++ Dart_Port dart_port, ++ int64_t data) { ++ WakeupHandler(id, dart_port, data); ++} ++ ++void* EventHandlerImplementation::GetHashmapKeyFromFd(intptr_t fd) { ++ // The hashmap does not support keys with value 0. ++ return reinterpret_cast(fd + 1); ++} ++ ++uint32_t EventHandlerImplementation::GetHashmapHashFromFd(intptr_t fd) { ++ // The hashmap does not support keys with value 0. ++ return dart::Utils::WordHash(fd + 1); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/eventhandler_ohos.h b/runtime/bin/eventhandler_ohos.h +new file mode 100644 +index 00000000000..0e4d84c8c12 +--- /dev/null ++++ b/runtime/bin/eventhandler_ohos.h +@@ -0,0 +1,99 @@ ++// 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. ++ ++#ifndef RUNTIME_BIN_EVENTHANDLER_OHOS_H_ ++#define RUNTIME_BIN_EVENTHANDLER_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_EVENTHANDLER_H_) ++#error Do not include eventhandler_linux.h directly; use eventhandler.h instead. ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#include "platform/hashmap.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++class DescriptorInfo : public DescriptorInfoBase { ++ public: ++ explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) {} ++ ++ virtual ~DescriptorInfo() {} ++ ++ intptr_t GetPollEvents(); ++ ++ virtual void Close() { ++ close(fd_); ++ fd_ = -1; ++ } ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfo); ++}; ++ ++class DescriptorInfoSingle : public DescriptorInfoSingleMixin { ++ public: ++ explicit DescriptorInfoSingle(intptr_t fd) ++ : DescriptorInfoSingleMixin(fd, false) {} ++ virtual ~DescriptorInfoSingle() {} ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoSingle); ++}; ++ ++class DescriptorInfoMultiple ++ : public DescriptorInfoMultipleMixin { ++ public: ++ explicit DescriptorInfoMultiple(intptr_t fd) ++ : DescriptorInfoMultipleMixin(fd, false) {} ++ virtual ~DescriptorInfoMultiple() {} ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(DescriptorInfoMultiple); ++}; ++ ++class EventHandlerImplementation { ++ public: ++ EventHandlerImplementation(); ++ ~EventHandlerImplementation(); ++ ++ void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo* di); ++ ++ // Gets the socket data structure for a given file ++ // descriptor. Creates a new one if one is not found. ++ DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening); ++ void SendData(intptr_t id, Dart_Port dart_port, int64_t data); ++ void Start(EventHandler* handler); ++ void Shutdown(); ++ ++ private: ++ void HandleEvents(struct epoll_event* events, int size); ++ static void Poll(uword args); ++ void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data); ++ void HandleInterruptFd(); ++ void UpdateTimerFd(); ++ void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask); ++ intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di); ++ static void* GetHashmapKeyFromFd(intptr_t fd); ++ static uint32_t GetHashmapHashFromFd(intptr_t fd); ++ ++ SimpleHashMap socket_map_; ++ TimeoutQueue timeout_queue_; ++ bool shutdown_; ++ int interrupt_fds_[2]; ++ int epoll_fd_; ++ int timer_fd_; ++ ++ DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation); ++}; ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // RUNTIME_BIN_EVENTHANDLER_OHOS_H_ +diff --git a/runtime/bin/fdutils_ohos.cc b/runtime/bin/fdutils_ohos.cc +new file mode 100644 +index 00000000000..f7531006cbe +--- /dev/null ++++ b/runtime/bin/fdutils_ohos.cc +@@ -0,0 +1,142 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/fdutils.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool FDUtils::SetCloseOnExec(intptr_t fd) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFD)); ++ if (status < 0) { ++ perror("fcntl(F_GETFD) failed"); ++ return false; ++ } ++ status |= FD_CLOEXEC; ++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFD, status)) < 0) { ++ perror("fcntl(F_SETFD, FD_CLOEXEC) failed"); ++ return false; ++ } ++ return true; ++} ++ ++static bool SetBlockingHelper(intptr_t fd, bool blocking) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL)); ++ if (status < 0) { ++ perror("fcntl(F_GETFL) failed"); ++ return false; ++ } ++ status = blocking ? (status & ~O_NONBLOCK) : (status | O_NONBLOCK); ++ if (NO_RETRY_EXPECTED(fcntl(fd, F_SETFL, status)) < 0) { ++ perror("fcntl(F_SETFL, O_NONBLOCK) failed"); ++ return false; ++ } ++ return true; ++} ++ ++bool FDUtils::SetNonBlocking(intptr_t fd) { ++ return SetBlockingHelper(fd, false); ++} ++ ++bool FDUtils::SetBlocking(intptr_t fd) { ++ return SetBlockingHelper(fd, true); ++} ++ ++bool FDUtils::IsBlocking(intptr_t fd, bool* is_blocking) { ++ intptr_t status; ++ status = NO_RETRY_EXPECTED(fcntl(fd, F_GETFL)); ++ if (status < 0) { ++ return false; ++ } ++ *is_blocking = (status & O_NONBLOCK) == 0; ++ return true; ++} ++ ++intptr_t FDUtils::AvailableBytes(intptr_t fd) { ++ int available; // ioctl for FIONREAD expects an 'int*' argument. ++ int result = NO_RETRY_EXPECTED(ioctl(fd, FIONREAD, &available)); ++ if (result < 0) { ++ return result; ++ } ++ ASSERT(available >= 0); ++ return static_cast(available); ++} ++ ++ssize_t FDUtils::ReadFromBlocking(int fd, void* buffer, size_t count) { ++#ifdef DEBUG ++ bool is_blocking = false; ++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); ++ ASSERT(is_blocking); ++#endif ++ size_t remaining = count; ++ char* buffer_pos = reinterpret_cast(buffer); ++ while (remaining > 0) { ++ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer_pos, remaining)); ++ if (bytes_read == 0) { ++ return count - remaining; ++ } else if (bytes_read == -1) { ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ // Error code EWOULDBLOCK should only happen for non blocking ++ // file descriptors. ++ ASSERT(errno != EWOULDBLOCK); ++ return -1; ++ } else { ++ ASSERT(bytes_read > 0); ++ remaining -= bytes_read; ++ buffer_pos += bytes_read; ++ } ++ } ++ return count; ++} ++ ++ssize_t FDUtils::WriteToBlocking(int fd, const void* buffer, size_t count) { ++#ifdef DEBUG ++ bool is_blocking = false; ++ ASSERT(FDUtils::IsBlocking(fd, &is_blocking)); ++ ASSERT(is_blocking); ++#endif ++ size_t remaining = count; ++ char* buffer_pos = const_cast(reinterpret_cast(buffer)); ++ while (remaining > 0) { ++ ssize_t bytes_written = ++ TEMP_FAILURE_RETRY(write(fd, buffer_pos, remaining)); ++ if (bytes_written == 0) { ++ return count - remaining; ++ } else if (bytes_written == -1) { ++ ASSERT(EAGAIN == EWOULDBLOCK); ++ // Error code EWOULDBLOCK should only happen for non blocking ++ // file descriptors. ++ ASSERT(errno != EWOULDBLOCK); ++ return -1; ++ } else { ++ ASSERT(bytes_written > 0); ++ remaining -= bytes_written; ++ buffer_pos += bytes_written; ++ } ++ } ++ return count; ++} ++ ++void FDUtils::SaveErrorAndClose(intptr_t fd) { ++ int err = errno; ++ close(fd); ++ errno = err; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/ffi_unit_test/BUILD.gn b/runtime/bin/ffi_unit_test/BUILD.gn +index c06d9e57e9f..ae681704cc6 100644 +--- a/runtime/bin/ffi_unit_test/BUILD.gn ++++ b/runtime/bin/ffi_unit_test/BUILD.gn +@@ -73,6 +73,10 @@ config("define_target_os_linux") { + defines = [ "DART_TARGET_OS_LINUX" ] + } + ++config("define_target_os_ohos") { ++ defines = [ "DART_TARGET_OS_OHOS" ] ++} ++ + config("define_target_os_macos") { + defines = [ "DART_TARGET_OS_MACOS" ] + } +diff --git a/runtime/bin/file_ohos.cc b/runtime/bin/file_ohos.cc +new file mode 100644 +index 00000000000..225b1d62c67 +--- /dev/null ++++ b/runtime/bin/file_ohos.cc +@@ -0,0 +1,786 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/file.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/builtin.h" ++#include "bin/fdutils.h" ++#include "bin/namespace.h" ++#include "platform/signal_blocker.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++class FileHandle { ++ public: ++ explicit FileHandle(int fd) : fd_(fd) {} ++ ~FileHandle() {} ++ int fd() const { return fd_; } ++ void set_fd(int fd) { fd_ = fd; } ++ ++ private: ++ int fd_; ++ ++ DISALLOW_COPY_AND_ASSIGN(FileHandle); ++}; ++ ++File::~File() { ++ if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) && ++ (handle_->fd() != STDERR_FILENO)) { ++ Close(); ++ } ++ delete handle_; ++} ++ ++void File::Close() { ++ ASSERT(handle_->fd() >= 0); ++ if (handle_->fd() == STDOUT_FILENO) { ++ // If stdout, redirect fd to /dev/null. ++ int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY)); ++ ASSERT(null_fd >= 0); ++ VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd())); ++ close(null_fd); ++ } else { ++ int err = close(handle_->fd()); ++ if (err != 0) { ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ Syslog::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize)); ++ } ++ } ++ handle_->set_fd(kClosedFd); ++} ++ ++intptr_t File::GetFD() { ++ return handle_->fd(); ++} ++ ++bool File::IsClosed() { ++ return handle_->fd() == kClosedFd; ++} ++ ++MappedMemory* File::Map(MapType type, ++ int64_t position, ++ int64_t length, ++ void* start) { ++ ASSERT(handle_->fd() >= 0); ++ ASSERT(length > 0); ++ void* hint = nullptr; ++ int prot = PROT_NONE; ++ int flags = MAP_PRIVATE; ++ switch (type) { ++ case kReadOnly: ++ prot = PROT_READ; ++ break; ++ case kReadExecute: ++ // Try to allocate near the VM's binary. ++ hint = reinterpret_cast(&Dart_Initialize); ++ prot = PROT_READ | PROT_EXEC; ++ break; ++ case kReadWrite: ++ prot = PROT_READ | PROT_WRITE; ++ break; ++ } ++ if (start != nullptr) { ++ hint = start; ++ flags |= MAP_FIXED; ++ } ++ void* addr = mmap(hint, length, prot, flags, handle_->fd(), position); ++ if (addr == MAP_FAILED) { ++ return NULL; ++ } ++ return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr); ++} ++ ++void MappedMemory::Unmap() { ++ int result = munmap(address_, size_); ++ ASSERT(result == 0); ++ address_ = 0; ++ size_ = 0; ++} ++ ++int64_t File::Read(void* buffer, int64_t num_bytes) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(read(handle_->fd(), buffer, num_bytes)); ++} ++ ++int64_t File::Write(const void* buffer, int64_t num_bytes) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes)); ++} ++ ++bool File::VPrint(const char* format, va_list args) { ++ // Measure. ++ va_list measure_args; ++ va_copy(measure_args, args); ++ intptr_t len = vsnprintf(NULL, 0, format, measure_args); ++ va_end(measure_args); ++ ++ char* buffer = reinterpret_cast(malloc(len + 1)); ++ ++ // Print. ++ va_list print_args; ++ va_copy(print_args, args); ++ vsnprintf(buffer, len + 1, format, print_args); ++ va_end(print_args); ++ ++ bool result = WriteFully(buffer, len); ++ free(buffer); ++ return result; ++} ++ ++int64_t File::Position() { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), 0, SEEK_CUR)); ++} ++ ++bool File::SetPosition(int64_t position) { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(lseek64(handle_->fd(), position, SEEK_SET)) >= 0; ++} ++ ++bool File::Truncate(int64_t length) { ++ ASSERT(handle_->fd() >= 0); ++ return TEMP_FAILURE_RETRY(ftruncate64(handle_->fd(), length) != -1); ++} ++ ++bool File::Flush() { ++ ASSERT(handle_->fd() >= 0); ++ return NO_RETRY_EXPECTED(fsync(handle_->fd())) != -1; ++} ++ ++bool File::Lock(File::LockType lock, int64_t start, int64_t end) { ++ ASSERT(handle_->fd() >= 0); ++ ASSERT((end == -1) || (end > start)); ++ struct flock fl; ++ switch (lock) { ++ case File::kLockUnlock: ++ fl.l_type = F_UNLCK; ++ break; ++ case File::kLockShared: ++ case File::kLockBlockingShared: ++ fl.l_type = F_RDLCK; ++ break; ++ case File::kLockExclusive: ++ case File::kLockBlockingExclusive: ++ fl.l_type = F_WRLCK; ++ break; ++ default: ++ return false; ++ } ++ fl.l_whence = SEEK_SET; ++ fl.l_start = start; ++ fl.l_len = end == -1 ? 0 : end - start; ++ int cmd = F_SETLK; ++ if ((lock == File::kLockBlockingShared) || ++ (lock == File::kLockBlockingExclusive)) { ++ cmd = F_SETLKW; ++ } ++ return TEMP_FAILURE_RETRY(fcntl(handle_->fd(), cmd, &fl)) != -1; ++} ++ ++int64_t File::Length() { ++ ASSERT(handle_->fd() >= 0); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstat64(handle_->fd(), &st)) == 0) { ++ return st.st_size; ++ } ++ return -1; ++} ++ ++File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) { ++ UNREACHABLE(); ++ return NULL; ++} ++ ++File* File::OpenFD(int fd) { ++ return new File(new FileHandle(fd)); ++} ++ ++File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) { ++ NamespaceScope ns(namespc, name); ++ // Report errors for non-regular files. ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ // Only accept regular files, character devices, and pipes. ++ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) { ++ errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT; ++ return NULL; ++ } ++ } ++ int flags = O_RDONLY; ++ if ((mode & kWrite) != 0) { ++ ASSERT((mode & kWriteOnly) == 0); ++ flags = (O_RDWR | O_CREAT); ++ } ++ if ((mode & kWriteOnly) != 0) { ++ ASSERT((mode & kWrite) == 0); ++ flags = (O_WRONLY | O_CREAT); ++ } ++ if ((mode & kTruncate) != 0) { ++ flags = flags | O_TRUNC; ++ } ++ flags |= O_CLOEXEC; ++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666)); ++ if (fd < 0) { ++ return NULL; ++ } ++ if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) || ++ (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) { ++ int64_t position = NO_RETRY_EXPECTED(lseek64(fd, 0, SEEK_END)); ++ if (position < 0) { ++ return NULL; ++ } ++ } ++ return OpenFD(fd); ++} ++ ++Utils::CStringUniquePtr File::UriToPath(const char* uri) { ++ const char* path = (strlen(uri) >= 8 && strncmp(uri, "file:///", 8) == 0) ++ ? uri + 7 : uri; ++ UriDecoder uri_decoder(path); ++ if (uri_decoder.decoded() == nullptr) { ++ errno = EINVAL; ++ return Utils::CreateCStringUniquePtr(nullptr); ++ } ++ return Utils::CreateCStringUniquePtr(strdup(uri_decoder.decoded())); ++} ++ ++File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) { ++ auto path = UriToPath(uri); ++ if (path == nullptr) { ++ return nullptr; ++ } ++ return File::Open(namespc, path.get(), mode); ++} ++ ++File* File::OpenStdio(int fd) { ++ return new File(new FileHandle(fd)); ++} ++ ++bool File::Exists(Namespace* namespc, const char* name) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ // Everything but a directory and a link is a file to Dart. ++ return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode); ++ } else { ++ return false; ++ } ++} ++ ++bool File::ExistsUri(Namespace* namespc, const char* uri) { ++ auto path = UriToPath(uri); ++ if (path == nullptr) { ++ return false; ++ } ++ return File::Exists(namespc, path.get()); ++} ++ ++bool File::Create(Namespace* namespc, const char* name, bool exclusive) { ++ NamespaceScope ns(namespc, name); ++ int flags = O_RDONLY | O_CREAT | O_CLOEXEC; ++ if (exclusive) { ++ flags |= O_EXCL; ++ } ++ const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666)); ++ if (fd < 0) { ++ return false; ++ } ++ // File.create returns a File, so we shouldn't be giving the illusion that the ++ // call has created a file or that a file already exists if there is already ++ // an entity at the same path that is a directory or a link. ++ bool is_file = true; ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstat64(fd, &st)) == 0) { ++ if (S_ISDIR(st.st_mode)) { ++ errno = EISDIR; ++ is_file = false; ++ } else if (S_ISLNK(st.st_mode)) { ++ errno = ENOENT; ++ is_file = false; ++ } ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return is_file; ++} ++ ++bool File::CreateLink(Namespace* namespc, ++ const char* name, ++ const char* target) { ++ NamespaceScope ns(namespc, name); ++ return NO_RETRY_EXPECTED(symlinkat(target, ns.fd(), ns.path())) == 0; ++} ++ ++bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) { ++ int pipe_fds[2]; ++ int status = NO_RETRY_EXPECTED(pipe(pipe_fds)); ++ if (status != 0) { ++ return false; ++ } ++ *readPipe = OpenFD(pipe_fds[0]); ++ *writePipe = OpenFD(pipe_fds[1]); ++ return true; ++} ++ ++File::Type File::GetType(Namespace* namespc, ++ const char* name, ++ bool follow_links) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 entry_info; ++ int stat_success; ++ if (follow_links) { ++ stat_success = ++ TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0)); ++ } else { ++ stat_success = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW)); ++ } ++ if (stat_success == -1) { ++ return File::kDoesNotExist; ++ } ++ if (S_ISDIR(entry_info.st_mode)) { ++ return File::kIsDirectory; ++ } ++ if (S_ISREG(entry_info.st_mode)) { ++ return File::kIsFile; ++ } ++ if (S_ISLNK(entry_info.st_mode)) { ++ return File::kIsLink; ++ } ++ if (S_ISSOCK(entry_info.st_mode)) { ++ return File::kIsSock; ++ } ++ if (S_ISFIFO(entry_info.st_mode)) { ++ return File::kIsPipe; ++ } ++ return File::kDoesNotExist; ++} ++ ++static void SetErrno(File::Type type) { ++ switch (type) { ++ case File::kIsDirectory: ++ errno = EISDIR; ++ break; ++ case File::kDoesNotExist: ++ errno = ENOENT; ++ break; ++ default: ++ errno = EINVAL; ++ break; ++ } ++} ++ ++static bool CheckTypeAndSetErrno(Namespace* namespc, ++ const char* name, ++ File::Type expected, ++ bool follow_links) { ++ File::Type actual = File::GetType(namespc, name, follow_links); ++ if (actual == expected) { ++ return true; ++ } ++ SetErrno(actual); ++ return false; ++} ++ ++bool File::Delete(Namespace* namespc, const char* name) { ++ File::Type type = File::GetType(namespc, name, true); ++ if (type == kIsFile || type == kIsSock || type == kIsPipe) { ++ NamespaceScope ns(namespc, name); ++ return (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0); ++ } ++ SetErrno(type); ++ return false; ++} ++ ++bool File::DeleteLink(Namespace* namespc, const char* name) { ++ NamespaceScope ns(namespc, name); ++ return CheckTypeAndSetErrno(namespc, name, kIsLink, false) && ++ (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0); ++} ++ ++bool File::Rename(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ File::Type type = File::GetType(namespc, old_path, true); ++ if (type == kIsFile || type == kIsSock || type == kIsPipe) { ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++ } ++ SetErrno(type); ++ return false; ++} ++ ++bool File::RenameLink(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ NamespaceScope oldns(namespc, old_path); ++ NamespaceScope newns(namespc, new_path); ++ return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) && ++ (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(), ++ newns.path())) == 0); ++} ++ ++bool File::Copy(Namespace* namespc, ++ const char* old_path, ++ const char* new_path) { ++ File::Type type = File::GetType(namespc, old_path, true); ++ if (type != kIsFile && type != kIsSock && type != kIsPipe) { ++ SetErrno(type); ++ return false; ++ } ++ NamespaceScope oldns(namespc, old_path); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(oldns.fd(), oldns.path(), &st, 0)) != 0) { ++ return false; ++ } ++ const int old_fd = TEMP_FAILURE_RETRY( ++ openat64(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC)); ++ if (old_fd < 0) { ++ return false; ++ } ++ NamespaceScope newns(namespc, new_path); ++ const int new_fd = TEMP_FAILURE_RETRY( ++ openat64(newns.fd(), newns.path(), ++ O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode)); ++ if (new_fd < 0) { ++ close(old_fd); ++ return false; ++ } ++ int64_t offset = 0; ++ intptr_t result = 1; ++ while (result > 0) { ++ // Loop to ensure we copy everything, and not only up to 2GB. ++ result = NO_RETRY_EXPECTED(sendfile64(new_fd, old_fd, &offset, kMaxUint32)); ++ } ++ // From sendfile man pages: ++ // Applications may wish to fall back to read(2)/write(2) in the case ++ // where sendfile() fails with EINVAL or ENOSYS. ++ if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) { ++ const intptr_t kBufferSize = 8 * KB; ++ uint8_t* buffer = reinterpret_cast(malloc(kBufferSize)); ++ while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) > ++ 0) { ++ int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result)); ++ if (wrote != result) { ++ result = -1; ++ break; ++ } ++ } ++ free(buffer); ++ } ++ int e = errno; ++ close(old_fd); ++ close(new_fd); ++ if (result < 0) { ++ VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0)); ++ errno = e; ++ return false; ++ } ++ return true; ++} ++ ++static bool StatHelper(Namespace* namespc, ++ const char* name, ++ struct stat64* st) { ++ NamespaceScope ns(namespc, name); ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), st, 0)) != 0) { ++ return false; ++ } ++ // Signal an error if it's a directory. ++ if (S_ISDIR(st->st_mode)) { ++ errno = EISDIR; ++ return false; ++ } ++ // Otherwise assume the caller knows what it's doing. ++ return true; ++} ++ ++int64_t File::LengthFromPath(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_size; ++} ++ ++static int64_t TimespecToMilliseconds(const struct timespec& t) { ++ return static_cast(t.tv_sec) * 1000L + ++ static_cast(t.tv_nsec) / 1000000L; ++} ++ ++static void MillisecondsToTimespec(int64_t millis, struct timespec* t) { ++ ASSERT(t != NULL); ++ t->tv_sec = millis / kMillisecondsPerSecond; ++ t->tv_nsec = (millis % kMillisecondsPerSecond) * 1000L; ++} ++ ++void File::Stat(Namespace* namespc, const char* name, int64_t* data) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 st; ++ if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) { ++ if (S_ISREG(st.st_mode)) { ++ data[kType] = kIsFile; ++ } else if (S_ISDIR(st.st_mode)) { ++ data[kType] = kIsDirectory; ++ } else if (S_ISLNK(st.st_mode)) { ++ data[kType] = kIsLink; ++ } else if (S_ISSOCK(st.st_mode)) { ++ data[kType] = kIsSock; ++ } else if (S_ISFIFO(st.st_mode)) { ++ data[kType] = kIsPipe; ++ } else { ++ data[kType] = kDoesNotExist; ++ } ++ data[kCreatedTime] = TimespecToMilliseconds(st.st_ctim); ++ data[kModifiedTime] = TimespecToMilliseconds(st.st_mtim); ++ data[kAccessedTime] = TimespecToMilliseconds(st.st_atim); ++ data[kMode] = st.st_mode; ++ data[kSize] = st.st_size; ++ } else { ++ data[kType] = kDoesNotExist; ++ } ++} ++ ++time_t File::LastModified(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_mtime; ++} ++ ++time_t File::LastAccessed(Namespace* namespc, const char* name) { ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return -1; ++ } ++ return st.st_atime; ++} ++ ++bool File::SetLastAccessed(Namespace* namespc, ++ const char* name, ++ int64_t millis) { ++ // First get the current times. ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return false; ++ } ++ ++ // Set the new time: ++ NamespaceScope ns(namespc, name); ++ struct timespec times[2]; ++ MillisecondsToTimespec(millis, ×[0]); ++ times[1] = st.st_mtim; ++ return utimensat(ns.fd(), ns.path(), times, 0) == 0; ++} ++ ++bool File::SetLastModified(Namespace* namespc, ++ const char* name, ++ int64_t millis) { ++ // First get the current times. ++ struct stat64 st; ++ if (!StatHelper(namespc, name, &st)) { ++ return false; ++ } ++ ++ // Set the new time: ++ NamespaceScope ns(namespc, name); ++ struct timespec times[2]; ++ times[0] = st.st_atim; ++ MillisecondsToTimespec(millis, ×[1]); ++ return utimensat(ns.fd(), ns.path(), times, 0) == 0; ++} ++ ++const char* File::LinkTarget(Namespace* namespc, ++ const char* name, ++ char* dest, ++ int dest_size) { ++ NamespaceScope ns(namespc, name); ++ struct stat64 link_stats; ++ const int status = TEMP_FAILURE_RETRY( ++ fstatat64(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW)); ++ if (status != 0) { ++ return NULL; ++ } ++ if (!S_ISLNK(link_stats.st_mode)) { ++ errno = ENOENT; ++ return NULL; ++ } ++ // Don't rely on the link_stats.st_size for the size of the link ++ // target. For some filesystems, e.g. procfs, this value is always ++ // 0. Also the link might have changed before the readlink call. ++ const int kBufferSize = PATH_MAX + 1; ++ char target[kBufferSize]; ++ const int target_size = ++ TEMP_FAILURE_RETRY(readlinkat(ns.fd(), ns.path(), target, kBufferSize)); ++ if (target_size <= 0) { ++ return NULL; ++ } ++ if (dest == NULL) { ++ dest = DartUtils::ScopedCString(target_size + 1); ++ } else { ++ ASSERT(dest_size > 0); ++ if (dest_size <= target_size) { ++ return NULL; ++ } ++ } ++ memmove(dest, target, target_size); ++ dest[target_size] = '\0'; ++ return dest; ++} ++ ++bool File::IsAbsolutePath(const char* pathname) { ++ return (pathname != NULL) && (pathname[0] == '/'); ++} ++ ++intptr_t File::ReadLinkInto(const char* pathname, ++ char* result, ++ size_t result_size) { ++ ASSERT(pathname != NULL); ++ ASSERT(IsAbsolutePath(pathname)); ++ struct stat64 link_stats; ++ if (TEMP_FAILURE_RETRY(lstat64(pathname, &link_stats)) != 0) { ++ return -1; ++ } ++ if (!S_ISLNK(link_stats.st_mode)) { ++ errno = ENOENT; ++ return -1; ++ } ++ size_t target_size = ++ TEMP_FAILURE_RETRY(readlink(pathname, result, result_size)); ++ if (target_size <= 0) { ++ return -1; ++ } ++ // readlink returns non-zero terminated strings. Append. ++ if (target_size < result_size) { ++ result[target_size] = '\0'; ++ target_size++; ++ } ++ return target_size; ++} ++ ++const char* File::ReadLink(const char* pathname) { ++ // Don't rely on the link_stats.st_size for the size of the link ++ // target. For some filesystems, e.g. procfs, this value is always ++ // 0. Also the link might have changed before the readlink call. ++ const int kBufferSize = PATH_MAX + 1; ++ char target[kBufferSize]; ++ size_t target_size = ReadLinkInto(pathname, target, kBufferSize); ++ if (target_size <= 0) { ++ return NULL; ++ } ++ char* target_name = DartUtils::ScopedCString(target_size); ++ ASSERT(target_name != NULL); ++ memmove(target_name, target, target_size); ++ return target_name; ++} ++ ++const char* File::GetCanonicalPath(Namespace* namespc, ++ const char* name, ++ char* dest, ++ int dest_size) { ++ if (name == NULL) { ++ return NULL; ++ } ++ if (!Namespace::IsDefault(namespc)) { ++ // TODO(zra): There is no realpathat(). Also chasing a symlink might result ++ // in a path to something outside of the namespace, so canonicalizing paths ++ // would have to be done carefully. For now, don't do anything. ++ return name; ++ } ++ char* abs_path; ++ if (dest == NULL) { ++ dest = DartUtils::ScopedCString(PATH_MAX + 1); ++ } else { ++ ASSERT(dest_size >= PATH_MAX); ++ } ++ ASSERT(dest != NULL); ++ do { ++ abs_path = realpath(name, dest); ++ } while ((abs_path == NULL) && (errno == EINTR)); ++ ASSERT(abs_path == NULL || IsAbsolutePath(abs_path)); ++ ASSERT(abs_path == NULL || (abs_path == dest)); ++ return abs_path; ++} ++ ++const char* File::PathSeparator() { ++ return "/"; ++} ++ ++const char* File::StringEscapedPathSeparator() { ++ return "/"; ++} ++ ++File::StdioHandleType File::GetStdioHandleType(int fd) { ++ struct stat64 buf; ++ int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf)); ++ if (result == -1) { ++ return kTypeError; ++ } ++ if (S_ISCHR(buf.st_mode)) { ++ return kTerminal; ++ } ++ if (S_ISFIFO(buf.st_mode)) { ++ return kPipe; ++ } ++ if (S_ISSOCK(buf.st_mode)) { ++ return kSocket; ++ } ++ if (S_ISREG(buf.st_mode)) { ++ return kFile; ++ } ++ return kOther; ++} ++ ++File::Identical File::AreIdentical(Namespace* namespc_1, ++ const char* file_1, ++ Namespace* namespc_2, ++ const char* file_2) { ++ struct stat64 file_1_info; ++ struct stat64 file_2_info; ++ int status; ++ { ++ NamespaceScope ns1(namespc_1, file_1); ++ status = TEMP_FAILURE_RETRY( ++ fstatat64(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW)); ++ if (status == -1) { ++ return File::kError; ++ } ++ } ++ { ++ NamespaceScope ns2(namespc_2, file_2); ++ status = TEMP_FAILURE_RETRY( ++ fstatat64(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW)); ++ if (status == -1) { ++ return File::kError; ++ } ++ } ++ return ((file_1_info.st_ino == file_2_info.st_ino) && ++ (file_1_info.st_dev == file_2_info.st_dev)) ++ ? File::kIdentical ++ : File::kDifferent; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/file_system_watcher_ohos.cc b/runtime/bin/file_system_watcher_ohos.cc +new file mode 100644 +index 00000000000..fa030a251e8 +--- /dev/null ++++ b/runtime/bin/file_system_watcher_ohos.cc +@@ -0,0 +1,149 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/file_system_watcher.h" ++ ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/socket.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool FileSystemWatcher::IsSupported() { ++ return true; ++} ++ ++intptr_t FileSystemWatcher::Init() { ++ int id = NO_RETRY_EXPECTED(inotify_init1(IN_CLOEXEC)); ++ if (id < 0) { ++ return -1; ++ } ++ // Some systems dosn't support setting this as non-blocking. Since watching ++ // internals are kept away from the user, we know it's possible to continue, ++ // even if setting non-blocking fails. ++ FDUtils::SetNonBlocking(id); ++ return id; ++} ++ ++void FileSystemWatcher::Close(intptr_t id) { ++ USE(id); ++} ++ ++intptr_t FileSystemWatcher::WatchPath(intptr_t id, ++ Namespace* namespc, ++ const char* path, ++ int events, ++ bool recursive) { ++ int list_events = IN_DELETE_SELF | IN_MOVE_SELF; ++ if ((events & kCreate) != 0) { ++ list_events |= IN_CREATE; ++ } ++ if ((events & kModifyContent) != 0) { ++ list_events |= IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY; ++ } ++ if ((events & kDelete) != 0) { ++ list_events |= IN_DELETE; ++ } ++ if ((events & kMove) != 0) { ++ list_events |= IN_MOVE; ++ } ++ const char* resolved_path = File::GetCanonicalPath(namespc, path); ++ path = resolved_path != NULL ? resolved_path : path; ++ int path_id = NO_RETRY_EXPECTED(inotify_add_watch(id, path, list_events)); ++ if (path_id < 0) { ++ return -1; ++ } ++ return path_id; ++} ++ ++void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) { ++ VOID_NO_RETRY_EXPECTED(inotify_rm_watch(id, path_id)); ++} ++ ++intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) { ++ USE(path_id); ++ return id; ++} ++ ++static int InotifyEventToMask(struct inotify_event* e) { ++ int mask = 0; ++ if ((e->mask & IN_CLOSE_WRITE) != 0 || (e->mask & IN_MODIFY) != 0) { ++ mask |= FileSystemWatcher::kModifyContent; ++ } ++ if ((e->mask & IN_ATTRIB) != 0) { ++ mask |= FileSystemWatcher::kModifyAttribute; ++ } ++ if ((e->mask & IN_CREATE) != 0) { ++ mask |= FileSystemWatcher::kCreate; ++ } ++ if ((e->mask & IN_MOVE) != 0) { ++ mask |= FileSystemWatcher::kMove; ++ } ++ if ((e->mask & IN_DELETE) != 0) { ++ mask |= FileSystemWatcher::kDelete; ++ } ++ if ((e->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) != 0) { ++ mask |= FileSystemWatcher::kDeleteSelf; ++ } ++ if ((e->mask & IN_ISDIR) != 0) { ++ mask |= FileSystemWatcher::kIsDir; ++ } ++ return mask; ++} ++ ++Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) { ++ USE(path_id); ++ const intptr_t kEventSize = sizeof(struct inotify_event); ++ const intptr_t kBufferSize = kEventSize + NAME_MAX + 1; ++ uint8_t buffer[kBufferSize]; ++ intptr_t bytes = ++ SocketBase::Read(id, buffer, kBufferSize, SocketBase::kAsync); ++ if (bytes < 0) { ++ return DartUtils::NewDartOSError(); ++ } ++ const intptr_t kMaxCount = bytes / kEventSize; ++ Dart_Handle events = Dart_NewList(kMaxCount); ++ intptr_t offset = 0; ++ intptr_t i = 0; ++ while (offset < bytes) { ++ struct inotify_event* e = ++ reinterpret_cast(buffer + offset); ++ if ((e->mask & IN_IGNORED) == 0) { ++ Dart_Handle event = Dart_NewList(5); ++ int mask = InotifyEventToMask(e); ++ Dart_ListSetAt(event, 0, Dart_NewInteger(mask)); ++ Dart_ListSetAt(event, 1, Dart_NewInteger(e->cookie)); ++ if (e->len > 0) { ++ Dart_Handle name = Dart_NewStringFromUTF8( ++ reinterpret_cast(e->name), strlen(e->name)); ++ if (Dart_IsError(name)) { ++ return name; ++ } ++ Dart_ListSetAt(event, 2, name); ++ } else { ++ Dart_ListSetAt(event, 2, Dart_Null()); ++ } ++ Dart_ListSetAt(event, 3, Dart_NewBoolean((e->mask & IN_MOVED_TO) != 0u)); ++ Dart_ListSetAt(event, 4, Dart_NewInteger(e->wd)); ++ Dart_ListSetAt(events, i, event); ++ i++; ++ } ++ offset += kEventSize + e->len; ++ } ++ ASSERT(offset == bytes); ++ return events; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/io_impl_sources.gni b/runtime/bin/io_impl_sources.gni +index f2c1be724ef..0a5d97d1bbe 100644 +--- a/runtime/bin/io_impl_sources.gni ++++ b/runtime/bin/io_impl_sources.gni +@@ -16,6 +16,8 @@ io_impl_sources = [ + "eventhandler_fuchsia.h", + "eventhandler_linux.cc", + "eventhandler_linux.h", ++ "eventhandler_ohos.cc", ++ "eventhandler_ohos.h", + "eventhandler_macos.cc", + "eventhandler_macos.h", + "eventhandler_win.cc", +@@ -25,6 +27,7 @@ io_impl_sources = [ + "file_system_watcher_android.cc", + "file_system_watcher_fuchsia.cc", + "file_system_watcher_linux.cc", ++ "file_system_watcher_ohos.cc", + "file_system_watcher_macos.cc", + "file_system_watcher_win.cc", + "filter.cc", +@@ -41,6 +44,7 @@ io_impl_sources = [ + "namespace_fuchsia.cc", + "namespace_fuchsia.h", + "namespace_linux.cc", ++ "namespace_ohos.cc", + "namespace_macos.cc", + "namespace_win.cc", + "platform.cc", +@@ -48,6 +52,7 @@ io_impl_sources = [ + "platform_android.cc", + "platform_fuchsia.cc", + "platform_linux.cc", ++ "platform_ohos.cc", + "platform_macos.cc", + "platform_macos.h", + "platform_win.cc", +@@ -56,6 +61,7 @@ io_impl_sources = [ + "process_android.cc", + "process_fuchsia.cc", + "process_linux.cc", ++ "process_ohos.cc", + "process_macos.cc", + "process_win.cc", + "reference_counting.h", +@@ -70,6 +76,7 @@ io_impl_sources = [ + "security_context_android.cc", + "security_context_fuchsia.cc", + "security_context_linux.cc", ++ "security_context_ohos.cc", + "security_context_macos.cc", + "security_context_win.cc", + "socket.cc", +@@ -83,6 +90,8 @@ io_impl_sources = [ + "socket_base_fuchsia.h", + "socket_base_linux.cc", + "socket_base_linux.h", ++ "socket_base_ohos.cc", ++ "socket_base_ohos.h", + "socket_base_macos.cc", + "socket_base_macos.h", + "socket_base_posix.cc", +@@ -90,6 +99,7 @@ io_impl_sources = [ + "socket_base_win.h", + "socket_fuchsia.cc", + "socket_linux.cc", ++ "socket_ohos.cc", + "socket_macos.cc", + "socket_win.cc", + "stdio.cc", +@@ -97,6 +107,7 @@ io_impl_sources = [ + "stdio_android.cc", + "stdio_fuchsia.cc", + "stdio_linux.cc", ++ "stdio_ohos.cc", + "stdio_macos.cc", + "stdio_win.cc", + "sync_socket.cc", +@@ -104,6 +115,7 @@ io_impl_sources = [ + "sync_socket_android.cc", + "sync_socket_fuchsia.cc", + "sync_socket_linux.cc", ++ "sync_socket_ohos.cc", + "sync_socket_macos.cc", + "sync_socket_win.cc", + "typed_data_utils.cc", +diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h +index 2c9100f2af0..49293cd6306 100644 +--- a/runtime/bin/main_options.h ++++ b/runtime/bin/main_options.h +@@ -90,9 +90,12 @@ enum VerbosityLevel { + kAll, + }; + +-static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; ++//static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; ++//TODO 临时修改非localhost ++static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "0.0.0.0"; + static constexpr int DEFAULT_VM_SERVICE_SERVER_PORT = 8181; +-static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1; ++//static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1; ++static constexpr int INVALID_VM_SERVICE_SERVER_PORT = DEFAULT_VM_SERVICE_SERVER_PORT; + + class Options { + public: +diff --git a/runtime/bin/namespace_ohos.cc b/runtime/bin/namespace_ohos.cc +new file mode 100644 +index 00000000000..71b68ca618d +--- /dev/null ++++ b/runtime/bin/namespace_ohos.cc +@@ -0,0 +1,159 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/namespace.h" ++ ++#include ++#include ++ ++#include "bin/file.h" ++#include "platform/signal_blocker.h" ++#include "platform/text_buffer.h" ++ ++namespace dart { ++namespace bin { ++ ++class NamespaceImpl { ++ public: ++ explicit NamespaceImpl(intptr_t rootfd) : rootfd_(rootfd), cwd_(strdup("/")) { ++ ASSERT(rootfd_ > 0); ++ cwdfd_ = dup(rootfd_); ++ ASSERT(cwdfd_ > 0); ++ } ++ ++ explicit NamespaceImpl(const char* path) ++ : rootfd_(TEMP_FAILURE_RETRY(open64(path, O_DIRECTORY))), ++ cwd_(strdup("/")) { ++ ASSERT(rootfd_ > 0); ++ cwdfd_ = dup(rootfd_); ++ ASSERT(cwdfd_ > 0); ++ } ++ ++ ~NamespaceImpl() { ++ NO_RETRY_EXPECTED(close(rootfd_)); ++ free(cwd_); ++ NO_RETRY_EXPECTED(close(cwdfd_)); ++ } ++ ++ intptr_t rootfd() const { return rootfd_; } ++ char* cwd() const { return cwd_; } ++ intptr_t cwdfd() const { return cwdfd_; } ++ ++ bool SetCwd(Namespace* namespc, const char* new_path) { ++ NamespaceScope ns(namespc, new_path); ++ const intptr_t new_cwdfd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_DIRECTORY)); ++ if (new_cwdfd < 0) { ++ return false; ++ } ++ ++ TextBuffer tbuf(PATH_MAX); ++ if (!File::IsAbsolutePath(new_path)) { ++ tbuf.AddString(cwd_); ++ } ++ tbuf.AddString(File::PathSeparator()); ++ tbuf.AddString(ns.path()); ++ ++ // Normalize it. ++ char result[PATH_MAX]; ++ const intptr_t result_len = ++ File::CleanUnixPath(tbuf.buffer(), result, PATH_MAX); ++ if (result_len < 0) { ++ errno = ENAMETOOLONG; ++ return false; ++ } ++ ++ free(cwd_); ++ cwd_ = strdup(result); ++ close(cwdfd_); ++ cwdfd_ = new_cwdfd; ++ return true; ++ } ++ ++ private: ++ intptr_t rootfd_; // dirfd for the namespace root. ++ char* cwd_; // cwd relative to the namespace. ++ intptr_t cwdfd_; // dirfd for the cwd. ++ ++ DISALLOW_COPY_AND_ASSIGN(NamespaceImpl); ++}; ++ ++Namespace* Namespace::Create(intptr_t namespc) { ++ NamespaceImpl* namespc_impl = NULL; ++ if (namespc != kNone) { ++ namespc_impl = new NamespaceImpl(namespc); ++ } ++ return new Namespace(namespc_impl); ++} ++ ++Namespace* Namespace::Create(const char* path) { ++ return new Namespace(new NamespaceImpl(path)); ++} ++ ++Namespace::~Namespace() { ++ delete namespc_; ++} ++ ++intptr_t Namespace::Default() { ++ return kNone; ++} ++ ++const char* Namespace::GetCurrent(Namespace* namespc) { ++ if (Namespace::IsDefault(namespc)) { ++ char buffer[PATH_MAX]; ++ if (getcwd(buffer, PATH_MAX) == NULL) { ++ return NULL; ++ } ++ return DartUtils::ScopedCopyCString(buffer); ++ } ++ const char* cwd = namespc->namespc()->cwd(); ++ return cwd; ++} ++ ++bool Namespace::SetCurrent(Namespace* namespc, const char* path) { ++ if (Namespace::IsDefault(namespc)) { ++ return (NO_RETRY_EXPECTED(chdir(path)) == 0); ++ } ++ return namespc->namespc()->SetCwd(namespc, path); ++} ++ ++void Namespace::ResolvePath(Namespace* namespc, ++ const char* path, ++ intptr_t* dirfd, ++ const char** resolved_path) { ++ ASSERT(dirfd != NULL); ++ ASSERT(resolved_path != NULL); ++ if (Namespace::IsDefault(namespc)) { ++ *dirfd = AT_FDCWD; ++ *resolved_path = path; ++ return; ++ } ++ if (File::IsAbsolutePath(path)) { ++ *dirfd = namespc->namespc()->rootfd(); ++ if (strcmp(path, File::PathSeparator()) == 0) { ++ // Change "/" to ".". ++ *resolved_path = "."; ++ } else { ++ // Otherwise strip off the leading "/". ++ *resolved_path = &path[1]; ++ } ++ } else { ++ *dirfd = namespc->namespc()->cwdfd(); ++ *resolved_path = path; ++ } ++} ++ ++NamespaceScope::NamespaceScope(Namespace* namespc, const char* path) { ++ Namespace::ResolvePath(namespc, path, &fd_, &path_); ++} ++ ++NamespaceScope::~NamespaceScope() {} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/platform_ohos.cc b/runtime/bin/platform_ohos.cc +new file mode 100644 +index 00000000000..2ff45e4236d +--- /dev/null ++++ b/runtime/bin/platform_ohos.cc +@@ -0,0 +1,183 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/platform.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bin/console.h" ++#include "bin/file.h" ++ ++namespace dart { ++namespace bin { ++ ++const char* Platform::executable_name_ = NULL; ++int Platform::script_index_ = 1; ++char** Platform::argv_ = NULL; ++ ++static void segv_handler(int signal, siginfo_t* siginfo, void* context) { ++ Syslog::PrintErr( ++ "\n===== CRASH =====\n" ++ "si_signo=%s(%d), si_code=%d, si_addr=%p\n", ++ strsignal(siginfo->si_signo), siginfo->si_signo, siginfo->si_code, ++ siginfo->si_addr); ++ Dart_DumpNativeStackTrace(context); ++ Dart_PrepareToAbort(); ++ abort(); ++} ++ ++bool Platform::Initialize() { ++ // Turn off the signal handler for SIGPIPE as it causes the process ++ // to terminate on writing to a closed pipe. Without the signal ++ // handler error EPIPE is set instead. ++ struct sigaction act = {}; ++ act.sa_handler = SIG_IGN; ++ if (sigaction(SIGPIPE, &act, 0) != 0) { ++ perror("Setting signal handler failed"); ++ return false; ++ } ++ ++ // tcsetattr raises SIGTTOU if we try to set console attributes when ++ // backgrounded, which suspends the process. Ignoring the signal prevents ++ // us from being suspended and lets us fail gracefully instead. ++ sigset_t signal_mask; ++ sigemptyset(&signal_mask); ++ sigaddset(&signal_mask, SIGTTOU); ++ if (sigprocmask(SIG_BLOCK, &signal_mask, NULL) < 0) { ++ perror("Setting signal handler failed"); ++ return false; ++ } ++ ++ act.sa_flags = SA_SIGINFO; ++ act.sa_sigaction = &segv_handler; ++ if (sigemptyset(&act.sa_mask) != 0) { ++ perror("sigemptyset() failed."); ++ return false; ++ } ++ if (sigaddset(&act.sa_mask, SIGPROF) != 0) { ++ perror("sigaddset() failed"); ++ return false; ++ } ++ if (sigaction(SIGSEGV, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ if (sigaction(SIGBUS, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ if (sigaction(SIGTRAP, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ if (sigaction(SIGILL, &act, NULL) != 0) { ++ perror("sigaction() failed."); ++ return false; ++ } ++ return true; ++} ++ ++int Platform::NumberOfProcessors() { ++ return sysconf(_SC_NPROCESSORS_ONLN); ++} ++ ++const char* Platform::OperatingSystemVersion() { ++ struct utsname info; ++ int ret = uname(&info); ++ if (ret != 0) { ++ return NULL; ++ } ++ const char* kFormat = "%s %s %s"; ++ int len = ++ snprintf(NULL, 0, kFormat, info.sysname, info.release, info.version); ++ if (len <= 0) { ++ return NULL; ++ } ++ char* result = DartUtils::ScopedCString(len + 1); ++ ASSERT(result != NULL); ++ len = snprintf(result, len + 1, kFormat, info.sysname, info.release, ++ info.version); ++ if (len <= 0) { ++ return NULL; ++ } ++ return result; ++} ++ ++const char* Platform::LibraryPrefix() { ++ return "lib"; ++} ++ ++const char* Platform::LibraryExtension() { ++ return "so"; ++} ++ ++const char* Platform::LocaleName() { ++ char* lang = getenv("LANG"); ++ if (lang == NULL) { ++ return "en_US"; ++ } ++ return lang; ++} ++ ++bool Platform::LocalHostname(char* buffer, intptr_t buffer_length) { ++ return gethostname(buffer, buffer_length) == 0; ++} ++ ++char** Platform::Environment(intptr_t* count) { ++ // Using environ directly is only safe as long as we do not ++ // provide access to modifying environment variables. ++ intptr_t i = 0; ++ char** tmp = environ; ++ while (*(tmp++) != NULL) { ++ i++; ++ } ++ *count = i; ++ char** result; ++ result = reinterpret_cast(Dart_ScopeAllocate(i * sizeof(*result))); ++ for (intptr_t current = 0; current < i; current++) { ++ result[current] = environ[current]; ++ } ++ return result; ++} ++ ++const char* Platform::GetExecutableName() { ++ return executable_name_; ++} ++ ++const char* Platform::ResolveExecutablePath() { ++ return File::ReadLink("/proc/self/exe"); ++} ++ ++intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) { ++ return File::ReadLinkInto("/proc/self/exe", result, result_size); ++} ++ ++void Platform::SetProcessName(const char* name) { ++ prctl(PR_SET_NAME, reinterpret_cast(name), 0, 0, 0); // NOLINT ++} ++ ++void Platform::Exit(int exit_code) { ++ Console::RestoreConfig(); ++ Dart_PrepareToAbort(); ++ exit(exit_code); ++} ++ ++void Platform::SetCoreDumpResourceLimit(int value) { ++ rlimit limit = {static_cast(value), static_cast(value)}; ++ setrlimit(RLIMIT_CORE, &limit); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/process_ohos.cc b/runtime/bin/process_ohos.cc +new file mode 100644 +index 00000000000..5cad067780f +--- /dev/null ++++ b/runtime/bin/process_ohos.cc +@@ -0,0 +1,1165 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/process.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/dartutils.h" ++#include "bin/directory.h" ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/lockers.h" ++#include "bin/reference_counting.h" ++#include "bin/thread.h" ++#include "platform/syslog.h" ++ ++#include "platform/signal_blocker.h" ++#include "platform/utils.h" ++ ++extern char** environ; ++ ++namespace dart { ++namespace bin { ++ ++int Process::global_exit_code_ = 0; ++Mutex* Process::global_exit_code_mutex_ = nullptr; ++Process::ExitHook Process::exit_hook_ = NULL; ++ ++// ProcessInfo is used to map a process id to the file descriptor for ++// the pipe used to communicate the exit code of the process to Dart. ++// ProcessInfo objects are kept in the static singly-linked ++// ProcessInfoList. ++class ProcessInfo { ++ public: ++ ProcessInfo(pid_t pid, intptr_t fd) : pid_(pid), fd_(fd) {} ++ ~ProcessInfo() { ++ int closed = close(fd_); ++ if (closed != 0) { ++ FATAL("Failed to close process exit code pipe"); ++ } ++ } ++ pid_t pid() { return pid_; } ++ intptr_t fd() { return fd_; } ++ ProcessInfo* next() { return next_; } ++ void set_next(ProcessInfo* info) { next_ = info; } ++ ++ private: ++ pid_t pid_; ++ intptr_t fd_; ++ ProcessInfo* next_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ProcessInfo); ++}; ++ ++// Singly-linked list of ProcessInfo objects for all active processes ++// started from Dart. ++class ProcessInfoList { ++ public: ++ static void Init(); ++ static void Cleanup(); ++ ++ static void AddProcess(pid_t pid, intptr_t fd) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* info = new ProcessInfo(pid, fd); ++ info->set_next(active_processes_); ++ active_processes_ = info; ++ } ++ ++ static intptr_t LookupProcessExitFd(pid_t pid) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* current = active_processes_; ++ while (current != NULL) { ++ if (current->pid() == pid) { ++ return current->fd(); ++ } ++ current = current->next(); ++ } ++ return 0; ++ } ++ ++ static void RemoveProcess(pid_t pid) { ++ MutexLocker locker(mutex_); ++ ProcessInfo* prev = NULL; ++ ProcessInfo* current = active_processes_; ++ while (current != NULL) { ++ if (current->pid() == pid) { ++ if (prev == NULL) { ++ active_processes_ = current->next(); ++ } else { ++ prev->set_next(current->next()); ++ } ++ delete current; ++ return; ++ } ++ prev = current; ++ current = current->next(); ++ } ++ } ++ ++ private: ++ // Linked list of ProcessInfo objects for all active processes ++ // started from Dart code. ++ static ProcessInfo* active_processes_; ++ // Mutex protecting all accesses to the linked list of active ++ // processes. ++ static Mutex* mutex_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessInfoList); ++}; ++ ++ProcessInfo* ProcessInfoList::active_processes_ = NULL; ++Mutex* ProcessInfoList::mutex_ = nullptr; ++ ++// The exit code handler sets up a separate thread which waits for child ++// processes to terminate. That separate thread can then get the exit code from ++// processes that have exited and communicate it to Dart through the ++// event loop. ++class ExitCodeHandler { ++ public: ++ static void Init(); ++ static void Cleanup(); ++ ++ // Notify the ExitCodeHandler that another process exists. ++ static void ProcessStarted() { ++ // Multiple isolates could be starting processes at the same ++ // time. Make sure that only one ExitCodeHandler thread exists. ++ MonitorLocker locker(monitor_); ++ process_count_++; ++ ++ monitor_->Notify(); ++ ++ if (running_) { ++ return; ++ } ++ ++ // Start thread that handles process exits when wait returns. ++ int result = ++ Thread::Start("dart:io Process.start", ExitCodeHandlerEntry, 0); ++ if (result != 0) { ++ FATAL1("Failed to start exit code handler worker thread %d", result); ++ } ++ ++ running_ = true; ++ } ++ ++ static void TerminateExitCodeThread() { ++ MonitorLocker locker(monitor_); ++ ++ if (!running_) { ++ return; ++ } ++ ++ // Set terminate_done_ to false, so we can use it as a guard for our ++ // monitor. ++ running_ = false; ++ ++ // Wake up the [ExitCodeHandler] thread which is blocked on `wait()` (see ++ // [ExitCodeHandlerEntry]). ++ if (TEMP_FAILURE_RETRY(fork()) == 0) { ++ // Avoid calling any atexit callbacks to prevent deadlocks. ++ _exit(0); ++ } ++ ++ monitor_->Notify(); ++ ++ while (!terminate_done_) { ++ monitor_->Wait(Monitor::kNoTimeout); ++ } ++ } ++ ++ private: ++ // Entry point for the separate exit code handler thread started by ++ // the ExitCodeHandler. ++ static void ExitCodeHandlerEntry(uword param) { ++ pid_t pid = 0; ++ int status = 0; ++ while (true) { ++ { ++ MonitorLocker locker(monitor_); ++ while (running_ && process_count_ == 0) { ++ monitor_->Wait(Monitor::kNoTimeout); ++ } ++ if (!running_) { ++ terminate_done_ = true; ++ monitor_->Notify(); ++ return; ++ } ++ } ++ ++ if ((pid = TEMP_FAILURE_RETRY(wait(&status))) > 0) { ++ int exit_code = 0; ++ int negative = 0; ++ if (WIFEXITED(status)) { ++ exit_code = WEXITSTATUS(status); ++ } ++ if (WIFSIGNALED(status)) { ++ exit_code = WTERMSIG(status); ++ negative = 1; ++ } ++ intptr_t exit_code_fd = ProcessInfoList::LookupProcessExitFd(pid); ++ if (exit_code_fd != 0) { ++ int message[2] = {exit_code, negative}; ++ ssize_t result = ++ FDUtils::WriteToBlocking(exit_code_fd, &message, sizeof(message)); ++ // If the process has been closed, the read end of the exit ++ // pipe has been closed. It is therefore not a problem that ++ // write fails with a broken pipe error. Other errors should ++ // not happen. ++ if ((result != -1) && (result != sizeof(message))) { ++ FATAL("Failed to write entire process exit message"); ++ } else if ((result == -1) && (errno != EPIPE)) { ++ FATAL1("Failed to write exit code: %d", errno); ++ } ++ ProcessInfoList::RemoveProcess(pid); ++ { ++ MonitorLocker locker(monitor_); ++ process_count_--; ++ } ++ } ++ } else if (pid < 0) { ++ FATAL1("Wait for process exit failed: %d", errno); ++ } ++ } ++ } ++ ++ static bool terminate_done_; ++ static int process_count_; ++ static bool running_; ++ static Monitor* monitor_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ExitCodeHandler); ++}; ++ ++bool ExitCodeHandler::running_ = false; ++int ExitCodeHandler::process_count_ = 0; ++bool ExitCodeHandler::terminate_done_ = false; ++Monitor* ExitCodeHandler::monitor_ = nullptr; ++ ++class ProcessStarter { ++ public: ++ ProcessStarter(Namespace* namespc, ++ const char* path, ++ char* arguments[], ++ intptr_t arguments_length, ++ const char* working_directory, ++ char* environment[], ++ intptr_t environment_length, ++ ProcessStartMode mode, ++ intptr_t* in, ++ intptr_t* out, ++ intptr_t* err, ++ intptr_t* id, ++ intptr_t* exit_event, ++ char** os_error_message) ++ : namespc_(namespc), ++ path_(path), ++ working_directory_(working_directory), ++ mode_(mode), ++ in_(in), ++ out_(out), ++ err_(err), ++ id_(id), ++ exit_event_(exit_event), ++ os_error_message_(os_error_message) { ++ read_in_[0] = -1; ++ read_in_[1] = -1; ++ read_err_[0] = -1; ++ read_err_[1] = -1; ++ write_out_[0] = -1; ++ write_out_[1] = -1; ++ exec_control_[0] = -1; ++ exec_control_[1] = -1; ++ ++ program_arguments_ = reinterpret_cast(Dart_ScopeAllocate( ++ (arguments_length + 2) * sizeof(*program_arguments_))); ++ program_arguments_[0] = const_cast(path_); ++ for (int i = 0; i < arguments_length; i++) { ++ program_arguments_[i + 1] = arguments[i]; ++ } ++ program_arguments_[arguments_length + 1] = NULL; ++ ++ program_environment_ = NULL; ++ if (environment != NULL) { ++ program_environment_ = reinterpret_cast(Dart_ScopeAllocate( ++ (environment_length + 1) * sizeof(*program_environment_))); ++ for (int i = 0; i < environment_length; i++) { ++ program_environment_[i] = environment[i]; ++ } ++ program_environment_[environment_length] = NULL; ++ } ++ } ++ ++ int Start() { ++ // Create pipes required. ++ int err = CreatePipes(); ++ if (err != 0) { ++ return err; ++ } ++ ++ // Fork to create the new process. ++ pid_t pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ // Failed to fork. ++ return CleanupAndReturnError(); ++ } else if (pid == 0) { ++ // This runs in the new process. ++ NewProcess(); ++ } ++ ++ // This runs in the original process. ++ ++ // If the child process is not started in detached mode, be sure to ++ // listen for exit-codes, now that we have a non detached child process ++ // and also Register this child process. ++ if (Process::ModeIsAttached(mode_)) { ++ ExitCodeHandler::ProcessStarted(); ++ err = RegisterProcess(pid); ++ if (err != 0) { ++ return err; ++ } ++ } ++ ++ // Notify child process to start. This is done to delay the call to exec ++ // until the process is registered above, and we are ready to receive the ++ // exit code. ++ char msg = '1'; ++ int bytes_written = ++ FDUtils::WriteToBlocking(read_in_[1], &msg, sizeof(msg)); ++ if (bytes_written != sizeof(msg)) { ++ return CleanupAndReturnError(); ++ } ++ ++ // Read the result of executing the child process. ++ close(exec_control_[1]); ++ exec_control_[1] = -1; ++ if (Process::ModeIsAttached(mode_)) { ++ err = ReadExecResult(); ++ } else { ++ err = ReadDetachedExecResult(&pid); ++ } ++ close(exec_control_[0]); ++ exec_control_[0] = -1; ++ ++ // Return error code if any failures. ++ if (err != 0) { ++ if (Process::ModeIsAttached(mode_)) { ++ // Since exec() failed, we're not interested in the exit code. ++ // We close the reading side of the exit code pipe here. ++ // GetProcessExitCodes will get a broken pipe error when it ++ // tries to write to the writing side of the pipe and it will ++ // ignore the error. ++ close(*exit_event_); ++ *exit_event_ = -1; ++ } ++ CloseAllPipes(); ++ return err; ++ } ++ ++ if (Process::ModeHasStdio(mode_)) { ++ // Connect stdio, stdout and stderr. ++ FDUtils::SetNonBlocking(read_in_[0]); ++ *in_ = read_in_[0]; ++ close(read_in_[1]); ++ FDUtils::SetNonBlocking(write_out_[1]); ++ *out_ = write_out_[1]; ++ close(write_out_[0]); ++ FDUtils::SetNonBlocking(read_err_[0]); ++ *err_ = read_err_[0]; ++ close(read_err_[1]); ++ } else { ++ // Close all fds. ++ close(read_in_[0]); ++ close(read_in_[1]); ++ ASSERT(write_out_[0] == -1); ++ ASSERT(write_out_[1] == -1); ++ ASSERT(read_err_[0] == -1); ++ ASSERT(read_err_[1] == -1); ++ } ++ ASSERT(exec_control_[0] == -1); ++ ASSERT(exec_control_[1] == -1); ++ ++ *id_ = pid; ++ return 0; ++ } ++ ++ private: ++ static constexpr int kErrorBufferSize = 1024; ++ ++ int CreatePipes() { ++ int result; ++ result = TEMP_FAILURE_RETRY(pipe2(exec_control_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ // For a detached process the pipe to connect stdout is still used for ++ // signaling when to do the first fork. ++ result = TEMP_FAILURE_RETRY(pipe2(read_in_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ // For detached processes the pipe to connect stderr and stdin are not used. ++ if (Process::ModeHasStdio(mode_)) { ++ result = TEMP_FAILURE_RETRY(pipe2(read_err_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ result = TEMP_FAILURE_RETRY(pipe2(write_out_, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ } ++ ++ return 0; ++ } ++ ++ void NewProcess() { ++ // Wait for parent process before setting up the child process. ++ char msg; ++ int bytes_read = FDUtils::ReadFromBlocking(read_in_[0], &msg, sizeof(msg)); ++ if (bytes_read != sizeof(msg)) { ++ perror("Failed receiving notification message"); ++ _exit(1); ++ } ++ if (Process::ModeIsAttached(mode_)) { ++ ExecProcess(); ++ } else { ++ ExecDetachedProcess(); ++ } ++ } ++ ++ // Tries to find path_ relative to the current namespace unless it should be ++ // searched in the PATH. ++ // The path that should be passed to exec is returned in realpath. ++ // Returns true on success, and false if there was an error that should ++ // be reported to the parent. ++ bool FindPathInNamespace(char* realpath, intptr_t realpath_size) { ++ // Perform a PATH search if there's no slash in the path. ++ if (strchr(path_, '/') == NULL) { ++ // TODO(zra): If there is a non-default namespace, the entries in PATH ++ // should be treated as relative to the namespace. ++ strncpy(realpath, path_, realpath_size); ++ realpath[realpath_size - 1] = '\0'; ++ return true; ++ } ++ NamespaceScope ns(namespc_, path_); ++ const int fd = ++ TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC)); ++ if (fd == -1) { ++ return false; ++ } ++ char procpath[PATH_MAX]; ++ snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd); ++ const intptr_t length = ++ TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size)); ++ if (length < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return false; ++ } ++ realpath[length] = '\0'; ++ FDUtils::SaveErrorAndClose(fd); ++ return true; ++ } ++ ++ void ExecProcess() { ++ if (mode_ == kNormal) { ++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ } else { ++ ASSERT(mode_ == kInheritStdio); ++ } ++ ++ if (working_directory_ != NULL && ++ !Directory::SetCurrent(namespc_, working_directory_)) { ++ ReportChildError(); ++ } ++ ++ if (program_environment_ != NULL) { ++ environ = program_environment_; ++ } ++ ++ char realpath[PATH_MAX]; ++ if (!FindPathInNamespace(realpath, PATH_MAX)) { ++ ReportChildError(); ++ } ++ // TODO(dart:io) Test for the existence of execveat, and use it instead. ++ execvp(realpath, const_cast(program_arguments_)); ++ ReportChildError(); ++ } ++ ++ void ExecDetachedProcess() { ++ if (mode_ == kDetached) { ++ ASSERT(write_out_[0] == -1); ++ ASSERT(write_out_[1] == -1); ++ ASSERT(read_err_[0] == -1); ++ ASSERT(read_err_[1] == -1); ++ // For a detached process the pipe to connect stdout is only used for ++ // signaling when to do the first fork. ++ close(read_in_[0]); ++ read_in_[0] = -1; ++ close(read_in_[1]); ++ read_in_[1] = -1; ++ } else { ++ // Don't close any fds if keeping stdio open to the detached process. ++ ASSERT(mode_ == kDetachedWithStdio); ++ } ++ // Fork once more to start a new session. ++ pid_t pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ ReportChildError(); ++ } else if (pid == 0) { ++ // Start a new session. ++ if (TEMP_FAILURE_RETRY(setsid()) == -1) { ++ ReportChildError(); ++ } else { ++ // Do a final fork to not be the session leader. ++ pid = TEMP_FAILURE_RETRY(fork()); ++ if (pid < 0) { ++ ReportChildError(); ++ } else if (pid == 0) { ++ if (mode_ == kDetached) { ++ SetupDetached(); ++ } else { ++ SetupDetachedWithStdio(); ++ } ++ ++ if ((working_directory_ != NULL) && ++ !Directory::SetCurrent(namespc_, working_directory_)) { ++ ReportChildError(); ++ } ++ if (program_environment_ != NULL) { ++ environ = program_environment_; ++ } ++ ++ // Report the final PID and do the exec. ++ ReportPid(getpid()); // getpid cannot fail. ++ char realpath[PATH_MAX]; ++ if (!FindPathInNamespace(realpath, PATH_MAX)) { ++ ReportChildError(); ++ } ++ // TODO(dart:io) Test for the existence of execveat, and use it ++ // instead. ++ execvp(realpath, const_cast(program_arguments_)); ++ ReportChildError(); ++ } else { ++ // Exit the intermediate process. Avoid calling any atexit callbacks ++ // to avoid potential issues (e.g. deadlocks). ++ _exit(0); ++ } ++ } ++ } else { ++ // Exit the intermediate process. Avoid calling any atexit callbacks ++ // to avoid potential issues (e.g. deadlocks). ++ _exit(0); ++ } ++ } ++ ++ int RegisterProcess(pid_t pid) { ++ int result; ++ int event_fds[2]; ++ result = TEMP_FAILURE_RETRY(pipe2(event_fds, O_CLOEXEC)); ++ if (result < 0) { ++ return CleanupAndReturnError(); ++ } ++ ++ ProcessInfoList::AddProcess(pid, event_fds[1]); ++ *exit_event_ = event_fds[0]; ++ FDUtils::SetNonBlocking(event_fds[0]); ++ return 0; ++ } ++ ++ int ReadExecResult() { ++ int child_errno; ++ int bytes_read = -1; ++ // Read exec result from child. If no data is returned the exec was ++ // successful and the exec call closed the pipe. Otherwise the errno ++ // is written to the pipe. ++ bytes_read = FDUtils::ReadFromBlocking(exec_control_[0], &child_errno, ++ sizeof(child_errno)); ++ if (bytes_read == sizeof(child_errno)) { ++ ReadChildError(); ++ return child_errno; ++ } else if (bytes_read == -1) { ++ return errno; ++ } ++ return 0; ++ } ++ ++ int ReadDetachedExecResult(pid_t* pid) { ++ int child_errno; ++ int bytes_read = -1; ++ // Read exec result from child. If only pid data is returned the exec was ++ // successful and the exec call closed the pipe. Otherwise the errno ++ // is written to the pipe as well. ++ int result[2]; ++ bytes_read = ++ FDUtils::ReadFromBlocking(exec_control_[0], result, sizeof(result)); ++ if (bytes_read == sizeof(int)) { ++ *pid = result[0]; ++ } else if (bytes_read == 2 * sizeof(int)) { ++ *pid = result[0]; ++ child_errno = result[1]; ++ ReadChildError(); ++ return child_errno; ++ } else if (bytes_read == -1) { ++ return errno; ++ } ++ return 0; ++ } ++ ++ void SetupDetached() { ++ ASSERT(mode_ == kDetached); ++ ++ // Close all open file descriptors except for exec_control_[1]. ++ int max_fds = sysconf(_SC_OPEN_MAX); ++ if (max_fds == -1) { ++ max_fds = _POSIX_OPEN_MAX; ++ } ++ for (int fd = 0; fd < max_fds; fd++) { ++ if (fd != exec_control_[1]) { ++ close(fd); ++ } ++ } ++ ++ // Re-open stdin, stdout and stderr and connect them to /dev/null. ++ // The loop above should already have closed all of them, so ++ // creating new file descriptors should start at STDIN_FILENO. ++ int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); ++ if (fd != STDIN_FILENO) { ++ ReportChildError(); ++ } ++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDOUT_FILENO)) != ++ STDOUT_FILENO) { ++ ReportChildError(); ++ } ++ if (TEMP_FAILURE_RETRY(dup2(STDIN_FILENO, STDERR_FILENO)) != ++ STDERR_FILENO) { ++ ReportChildError(); ++ } ++ } ++ ++ void SetupDetachedWithStdio() { ++ // Close all open file descriptors except for ++ // exec_control_[1], write_out_[0], read_in_[1] and ++ // read_err_[1]. ++ int max_fds = sysconf(_SC_OPEN_MAX); ++ if (max_fds == -1) { ++ max_fds = _POSIX_OPEN_MAX; ++ } ++ for (int fd = 0; fd < max_fds; fd++) { ++ if ((fd != exec_control_[1]) && (fd != write_out_[0]) && ++ (fd != read_in_[1]) && (fd != read_err_[1])) { ++ close(fd); ++ } ++ } ++ ++ if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(write_out_[0]); ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(read_in_[1]); ++ ++ if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ++ ReportChildError(); ++ } ++ close(read_err_[1]); ++ } ++ ++ int CleanupAndReturnError() { ++ int actual_errno = errno; ++ // If CleanupAndReturnError is called without an actual errno make ++ // sure to return an error anyway. ++ if (actual_errno == 0) { ++ actual_errno = EPERM; ++ } ++ SetChildOsErrorMessage(); ++ CloseAllPipes(); ++ return actual_errno; ++ } ++ ++ void SetChildOsErrorMessage() { ++ char* error_message = DartUtils::ScopedCString(kErrorBufferSize); ++ Utils::StrError(errno, error_message, kErrorBufferSize); ++ *os_error_message_ = error_message; ++ } ++ ++ void ReportChildError() { ++ // In the case of failure in the child process write the errno and ++ // the OS error message to the exec control pipe and exit. ++ int child_errno = errno; ++ char error_buf[kErrorBufferSize]; ++ char* os_error_message = ++ Utils::StrError(errno, error_buf, kErrorBufferSize); ++ int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno, ++ sizeof(child_errno)); ++ if (bytes_written == sizeof(child_errno)) { ++ FDUtils::WriteToBlocking(exec_control_[1], os_error_message, ++ strlen(os_error_message) + 1); ++ } ++ close(exec_control_[1]); ++ ++ // We avoid running through registered atexit() handlers because that is ++ // unnecessary work. It can also cause deadlocks on exit in the forked ++ // process. ++ _exit(1); ++ } ++ ++ void ReportPid(int pid) { ++ // In the case of starting a detached process the actual pid of that process ++ // is communicated using the exec control pipe. ++ int bytes_written = ++ FDUtils::WriteToBlocking(exec_control_[1], &pid, sizeof(pid)); ++ ASSERT(bytes_written == sizeof(int)); ++ USE(bytes_written); ++ } ++ ++ void ReadChildError() { ++ char* message = DartUtils::ScopedCString(kErrorBufferSize); ++ if (message != NULL) { ++ FDUtils::ReadFromBlocking(exec_control_[0], message, kErrorBufferSize); ++ message[kErrorBufferSize - 1] = '\0'; ++ *os_error_message_ = message; ++ } else { ++ // Could not get error message. It will be NULL. ++ ASSERT(*os_error_message_ == NULL); ++ } ++ } ++ ++ void ClosePipe(int* fds) { ++ for (int i = 0; i < 2; i++) { ++ if (fds[i] != -1) { ++ close(fds[i]); ++ fds[i] = -1; ++ } ++ } ++ } ++ ++ void CloseAllPipes() { ++ ClosePipe(exec_control_); ++ ClosePipe(read_in_); ++ ClosePipe(read_err_); ++ ClosePipe(write_out_); ++ } ++ ++ int read_in_[2]; // Pipe for stdout to child process. ++ int read_err_[2]; // Pipe for stderr to child process. ++ int write_out_[2]; // Pipe for stdin to child process. ++ int exec_control_[2]; // Pipe to get the result from exec. ++ ++ char** program_arguments_; ++ char** program_environment_; ++ ++ Namespace* namespc_; ++ const char* path_; ++ const char* working_directory_; ++ ProcessStartMode mode_; ++ intptr_t* in_; ++ intptr_t* out_; ++ intptr_t* err_; ++ intptr_t* id_; ++ intptr_t* exit_event_; ++ char** os_error_message_; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcessStarter); ++}; ++ ++int Process::Start(Namespace* namespc, ++ const char* path, ++ char* arguments[], ++ intptr_t arguments_length, ++ const char* working_directory, ++ char* environment[], ++ intptr_t environment_length, ++ ProcessStartMode mode, ++ intptr_t* in, ++ intptr_t* out, ++ intptr_t* err, ++ intptr_t* id, ++ intptr_t* exit_event, ++ char** os_error_message) { ++ ProcessStarter starter(namespc, path, arguments, arguments_length, ++ working_directory, environment, environment_length, ++ mode, in, out, err, id, exit_event, os_error_message); ++ return starter.Start(); ++} ++ ++static bool CloseProcessBuffers(struct pollfd* fds, int alive) { ++ int e = errno; ++ for (int i = 0; i < alive; i++) { ++ close(fds[i].fd); ++ } ++ errno = e; ++ return false; ++} ++ ++bool Process::Wait(intptr_t pid, ++ intptr_t in, ++ intptr_t out, ++ intptr_t err, ++ intptr_t exit_event, ++ ProcessResult* result) { ++ // Close input to the process right away. ++ close(in); ++ ++ // There is no return from this function using Dart_PropagateError ++ // as memory used by the buffer lists is freed through their ++ // destructors. ++ BufferList out_data; ++ BufferList err_data; ++ union { ++ uint8_t bytes[8]; ++ int32_t ints[2]; ++ } exit_code_data; ++ ++ struct pollfd fds[3]; ++ fds[0].fd = out; ++ fds[1].fd = err; ++ fds[2].fd = exit_event; ++ ++ for (int i = 0; i < 3; i++) { ++ fds[i].events = POLLIN; ++ } ++ ++ int alive = 3; ++ while (alive > 0) { ++ // Blocking call waiting for events from the child process. ++ if (TEMP_FAILURE_RETRY(poll(fds, alive, -1)) <= 0) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ ++ // Process incoming data. ++ for (int i = 0; i < alive; i++) { ++ if ((fds[i].revents & (POLLNVAL | POLLERR)) != 0) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ if ((fds[i].revents & POLLIN) != 0) { ++ intptr_t avail = FDUtils::AvailableBytes(fds[i].fd); ++ if (fds[i].fd == out) { ++ if (!out_data.Read(out, avail)) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } else if (fds[i].fd == err) { ++ if (!err_data.Read(err, avail)) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } else if (fds[i].fd == exit_event) { ++ if (avail == 8) { ++ intptr_t b = ++ TEMP_FAILURE_RETRY(read(exit_event, exit_code_data.bytes, 8)); ++ if (b != 8) { ++ return CloseProcessBuffers(fds, alive); ++ } ++ } ++ } else { ++ UNREACHABLE(); ++ } ++ } ++ if ((fds[i].revents & POLLHUP) != 0) { ++ // Remove the pollfd from the list of pollfds. ++ close(fds[i].fd); ++ alive--; ++ if (i < alive) { ++ fds[i] = fds[alive]; ++ } ++ // Process the same index again. ++ i--; ++ continue; ++ } ++ } ++ } ++ ++ // All handles closed and all data read. ++ result->set_stdout_data(out_data.GetData()); ++ result->set_stderr_data(err_data.GetData()); ++ DEBUG_ASSERT(out_data.IsEmpty()); ++ DEBUG_ASSERT(err_data.IsEmpty()); ++ ++ // Calculate the exit code. ++ intptr_t exit_code = exit_code_data.ints[0]; ++ intptr_t negative = exit_code_data.ints[1]; ++ if (negative != 0) { ++ exit_code = -exit_code; ++ } ++ result->set_exit_code(exit_code); ++ ++ return true; ++} ++ ++bool Process::Kill(intptr_t id, int signal) { ++ return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1); ++} ++ ++void Process::TerminateExitCodeHandler() { ++ ExitCodeHandler::TerminateExitCodeThread(); ++} ++ ++intptr_t Process::CurrentProcessId() { ++ return static_cast(getpid()); ++} ++ ++static void SaveErrorAndClose(FILE* file) { ++ int actual_errno = errno; ++ fclose(file); ++ errno = actual_errno; ++} ++ ++int64_t Process::CurrentRSS() { ++ // The second value in /proc/self/statm is the current RSS in pages. ++ // It is not possible to use getrusage() because the interested fields are not ++ // implemented by the linux kernel. ++ FILE* statm = fopen("/proc/self/statm", "r"); ++ if (statm == NULL) { ++ return -1; ++ } ++ int64_t current_rss_pages = 0; ++ int matches = fscanf(statm, "%*s%" Pd64 "", ¤t_rss_pages); ++ if (matches != 1) { ++ SaveErrorAndClose(statm); ++ return -1; ++ } ++ fclose(statm); ++ return current_rss_pages * getpagesize(); ++} ++ ++int64_t Process::MaxRSS() { ++ struct rusage usage; ++ usage.ru_maxrss = 0; ++ int r = getrusage(RUSAGE_SELF, &usage); ++ if (r < 0) { ++ return -1; ++ } ++ return usage.ru_maxrss * KB; ++} ++ ++static Mutex* signal_mutex = nullptr; ++static SignalInfo* signal_handlers = NULL; ++static const int kSignalsCount = 7; ++static const int kSignals[kSignalsCount] = { ++ SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGWINCH, ++ SIGQUIT // Allow VMService to listen on SIGQUIT. ++}; ++ ++SignalInfo::~SignalInfo() { ++ close(fd_); ++} ++ ++static void SignalHandler(int signal) { ++ MutexLocker lock(signal_mutex); ++ const SignalInfo* handler = signal_handlers; ++ while (handler != NULL) { ++ if (handler->signal() == signal) { ++ int value = 0; ++ VOID_TEMP_FAILURE_RETRY(write(handler->fd(), &value, 1)); ++ } ++ handler = handler->next(); ++ } ++} ++ ++intptr_t Process::SetSignalHandler(intptr_t signal) { ++ bool found = false; ++ for (int i = 0; i < kSignalsCount; i++) { ++ if (kSignals[i] == signal) { ++ found = true; ++ break; ++ } ++ } ++ if (!found) { ++ return -1; ++ } ++ int fds[2]; ++ if (NO_RETRY_EXPECTED(pipe2(fds, O_CLOEXEC)) != 0) { ++ return -1; ++ } ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ bool listen = true; ++ sa_handler_t oldact_handler = nullptr; ++ while (handler != NULL) { ++ if (handler->signal() == signal) { ++ oldact_handler = handler->oldact(); ++ listen = false; ++ break; ++ } ++ handler = handler->next(); ++ } ++ if (listen) { ++ struct sigaction act = {}; ++ act.sa_handler = SignalHandler; ++ sigemptyset(&act.sa_mask); ++ for (int i = 0; i < kSignalsCount; i++) { ++ sigaddset(&act.sa_mask, kSignals[i]); ++ } ++ struct sigaction oldact = {}; ++ int status = NO_RETRY_EXPECTED(sigaction(signal, &act, &oldact)); ++ if (status < 0) { ++ int err = errno; ++ close(fds[0]); ++ close(fds[1]); ++ errno = err; ++ return -1; ++ } ++ oldact_handler = oldact.sa_handler; ++ } ++ signal_handlers = ++ new SignalInfo(fds[1], signal, oldact_handler, signal_handlers); ++ return fds[0]; ++} ++ ++void Process::ClearSignalHandler(intptr_t signal, Dart_Port port) { ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ sa_handler_t oldact_handler = SIG_DFL; ++ bool any_removed = false; ++ bool any_remaining = false; ++ while (handler != NULL) { ++ bool remove = false; ++ if (handler->signal() == signal) { ++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) { ++ if (signal_handlers == handler) { ++ signal_handlers = handler->next(); ++ } ++ handler->Unlink(); ++ remove = true; ++ oldact_handler = handler->oldact(); ++ any_removed = true; ++ } else { ++ any_remaining = true; ++ } ++ } ++ SignalInfo* next = handler->next(); ++ if (remove) { ++ delete handler; ++ } ++ handler = next; ++ } ++ if (any_removed && !any_remaining) { ++ struct sigaction act = {}; ++ act.sa_handler = oldact_handler; ++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); ++ } ++} ++ ++void Process::ClearSignalHandlerByFd(intptr_t fd, Dart_Port port) { ++ ThreadSignalBlocker blocker(kSignalsCount, kSignals); ++ MutexLocker lock(signal_mutex); ++ SignalInfo* handler = signal_handlers; ++ sa_handler_t oldact_handler = SIG_DFL; ++ bool any_remaining = false; ++ intptr_t signal = -1; ++ while (handler != NULL) { ++ bool remove = false; ++ if (handler->fd() == fd) { ++ if ((port == ILLEGAL_PORT) || (handler->port() == port)) { ++ if (signal_handlers == handler) { ++ signal_handlers = handler->next(); ++ } ++ handler->Unlink(); ++ remove = true; ++ signal = handler->signal(); ++ } else { ++ any_remaining = true; ++ } ++ } ++ SignalInfo* next = handler->next(); ++ if (remove) { ++ delete handler; ++ } ++ handler = next; ++ } ++ if ((signal != -1) && !any_remaining) { ++ struct sigaction act = {}; ++ act.sa_handler = oldact_handler; ++ VOID_NO_RETRY_EXPECTED(sigaction(signal, &act, NULL)); ++ } ++} ++ ++void ProcessInfoList::Init() { ++ active_processes_ = NULL; ++ ASSERT(ProcessInfoList::mutex_ == nullptr); ++ ProcessInfoList::mutex_ = new Mutex(); ++} ++ ++void ProcessInfoList::Cleanup() { ++ ASSERT(ProcessInfoList::mutex_ != nullptr); ++ delete ProcessInfoList::mutex_; ++ ProcessInfoList::mutex_ = nullptr; ++} ++ ++void ExitCodeHandler::Init() { ++ running_ = false; ++ process_count_ = 0; ++ terminate_done_ = false; ++ ASSERT(ExitCodeHandler::monitor_ == nullptr); ++ ExitCodeHandler::monitor_ = new Monitor(); ++} ++ ++void ExitCodeHandler::Cleanup() { ++ ASSERT(ExitCodeHandler::monitor_ != nullptr); ++ delete ExitCodeHandler::monitor_; ++ ExitCodeHandler::monitor_ = nullptr; ++} ++ ++void Process::Init() { ++ ExitCodeHandler::Init(); ++ ProcessInfoList::Init(); ++ ++ ASSERT(signal_mutex == nullptr); ++ signal_mutex = new Mutex(); ++ signal_handlers = NULL; ++ ++ ASSERT(Process::global_exit_code_mutex_ == nullptr); ++ Process::global_exit_code_mutex_ = new Mutex(); ++} ++ ++void Process::Cleanup() { ++ ClearAllSignalHandlers(); ++ ++ ASSERT(signal_mutex != nullptr); ++ delete signal_mutex; ++ signal_mutex = nullptr; ++ ++ ASSERT(Process::global_exit_code_mutex_ != nullptr); ++ delete Process::global_exit_code_mutex_; ++ Process::global_exit_code_mutex_ = nullptr; ++ ++ ProcessInfoList::Cleanup(); ++ ExitCodeHandler::Cleanup(); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/security_context_ohos.cc b/runtime/bin/security_context_ohos.cc +new file mode 100644 +index 00000000000..2ae284f0972 +--- /dev/null ++++ b/runtime/bin/security_context_ohos.cc +@@ -0,0 +1,88 @@ ++// 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. ++ ++#if !defined(DART_IO_SECURE_SOCKET_DISABLED) ++ ++#include "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/security_context.h" ++ ++#include ++#include ++#include ++ ++#include "bin/directory.h" ++#include "bin/file.h" ++#include "bin/secure_socket_filter.h" ++#include "bin/secure_socket_utils.h" ++#include "platform/syslog.h" ++ ++namespace dart { ++namespace bin { ++ ++// The security context won't necessarily use the compiled-in root certificates, ++// but since there is no way to update the size of the allocation after creating ++// the weak persistent handle, we assume that it will. Note that when the ++// root certs aren't compiled in, |root_certificates_pem_length| is 0. ++const intptr_t SSLCertContext::kApproximateSize = ++ sizeof(SSLCertContext) + root_certificates_pem_length; ++ ++void SSLCertContext::TrustBuiltinRoots() { ++ // First, try to use locations specified on the command line. ++ if (root_certs_file() != NULL) { ++ LoadRootCertFile(root_certs_file()); ++ return; ++ } ++ if (root_certs_cache() != NULL) { ++ LoadRootCertCache(root_certs_cache()); ++ return; ++ } ++ ++ if (bypass_trusting_system_roots()) { ++ if (SSL_LOG_STATUS) { ++ Syslog::Print("Bypass trusting Linux built-in roots\n"); ++ } ++ } else { ++ // On Linux, we use the compiled-in trusted certs as a last resort. First, ++ // we try to find the trusted certs in various standard locations. A good ++ // discussion of the complexities of this endeavor can be found here: ++ // ++ // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/ ++ const char* bundle = "/etc/pki/tls/certs/ca-bundle.crt"; ++ const char* cachedir = "/etc/ssl/certs"; ++ if (File::Exists(NULL, bundle)) { ++ LoadRootCertFile(bundle); ++ return; ++ } ++ ++ if (Directory::Exists(NULL, cachedir) == Directory::EXISTS) { ++ LoadRootCertCache(cachedir); ++ return; ++ } ++ } ++ ++ // Fall back on the compiled-in certs if the standard locations don't exist, ++ // or we aren't on Linux. ++ if (SSL_LOG_STATUS) { ++ Syslog::Print("Trusting compiled-in roots\n"); ++ } ++ AddCompiledInCerts(); ++} ++ ++void SSLCertContext::RegisterCallbacks(SSL* ssl) { ++ // No callbacks to register for implementations using BoringSSL's built-in ++ // verification mechanism. ++} ++ ++TrustEvaluateHandlerFunc SSLCertContext::GetTrustEvaluateHandler() const { ++ return nullptr; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) ++ ++#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) +diff --git a/runtime/bin/socket_base_ohos.cc b/runtime/bin/socket_base_ohos.cc +new file mode 100644 +index 00000000000..eb4828d5972 +--- /dev/null ++++ b/runtime/bin/socket_base_ohos.cc +@@ -0,0 +1,148 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/socket_base.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/file.h" ++#include "bin/socket_base_ohos.h" ++#include "bin/thread.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++void SocketBase::GetError(intptr_t fd, OSError* os_error) { ++ int len = sizeof(errno); ++ int err = 0; ++ VOID_NO_RETRY_EXPECTED(getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, ++ reinterpret_cast(&len))); ++ errno = err; ++ os_error->SetCodeAndMessage(OSError::kSystem, errno); ++} ++ ++int SocketBase::GetType(intptr_t fd) { ++ struct stat64 buf; ++ int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf)); ++ if (result == -1) { ++ return -1; ++ } ++ if (S_ISCHR(buf.st_mode)) { ++ return File::kTerminal; ++ } ++ if (S_ISFIFO(buf.st_mode)) { ++ return File::kPipe; ++ } ++ if (S_ISREG(buf.st_mode)) { ++ return File::kFile; ++ } ++ return File::kOther; ++} ++ ++AddressList* SocketBase::LookupAddress(const char* host, ++ int type, ++ OSError** os_error) { ++ // Perform a name lookup for a host name. ++ struct addrinfo hints; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = SocketAddress::FromType(type); ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags = AI_ADDRCONFIG; ++ hints.ai_protocol = IPPROTO_TCP; ++ struct addrinfo* info = NULL; ++ int status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info)); ++ if (status != 0) { ++ // We failed, try without AI_ADDRCONFIG. This can happen when looking up ++ // e.g. '::1', when there are no global IPv6 addresses. ++ hints.ai_flags = 0; ++ status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info)); ++ if (status != 0) { ++ ASSERT(*os_error == NULL); ++ *os_error = ++ new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); ++ return NULL; ++ } ++ } ++ intptr_t count = 0; ++ for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { ++ if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { ++ count++; ++ } ++ } ++ intptr_t i = 0; ++ AddressList* addresses = new AddressList(count); ++ for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { ++ if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { ++ addresses->SetAt(i, new SocketAddress(c->ai_addr)); ++ i++; ++ } ++ } ++ freeaddrinfo(info); ++ return addresses; ++} ++ ++bool SocketBase::SetMulticastLoop(intptr_t fd, ++ intptr_t protocol, ++ bool enabled) { ++ int on = enabled ? 1 : 0; ++ int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; ++ int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP ++ : IPV6_MULTICAST_LOOP; ++ return NO_RETRY_EXPECTED(setsockopt( ++ fd, level, optname, reinterpret_cast(&on), sizeof(on))) == ++ 0; ++} ++ ++bool SocketBase::GetOption(intptr_t fd, ++ int level, ++ int option, ++ char* data, ++ unsigned int* length) { ++ socklen_t optlen = static_cast(*length); ++ auto result = NO_RETRY_EXPECTED(getsockopt(fd, level, option, data, &optlen)); ++ *length = static_cast(optlen); ++ return result == 0; ++} ++ ++bool SocketBase::JoinMulticast(intptr_t fd, ++ const RawAddr& addr, ++ const RawAddr&, ++ int interfaceIndex) { ++ int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; ++ struct group_req mreq; ++ mreq.gr_interface = interfaceIndex; ++ memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr)); ++ return NO_RETRY_EXPECTED( ++ setsockopt(fd, proto, MCAST_JOIN_GROUP, &mreq, sizeof(mreq))) == 0; ++} ++ ++bool SocketBase::LeaveMulticast(intptr_t fd, ++ const RawAddr& addr, ++ const RawAddr&, ++ int interfaceIndex) { ++ int proto = addr.addr.sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; ++ struct group_req mreq; ++ mreq.gr_interface = interfaceIndex; ++ memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr)); ++ return NO_RETRY_EXPECTED(setsockopt(fd, proto, MCAST_LEAVE_GROUP, &mreq, ++ sizeof(mreq))) == 0; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/socket_base_ohos.h b/runtime/bin/socket_base_ohos.h +new file mode 100644 +index 00000000000..a961edfeee5 +--- /dev/null ++++ b/runtime/bin/socket_base_ohos.h +@@ -0,0 +1,17 @@ ++// 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. ++ ++#ifndef RUNTIME_BIN_SOCKET_BASE_OHOS_H_ ++#define RUNTIME_BIN_SOCKET_BASE_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_SOCKET_BASE_H_) ++#error Do not include socket_base_linux.h directly. Use socket_base.h. ++#endif ++ ++#include ++#include ++#include ++#include ++ ++#endif // RUNTIME_BIN_SOCKET_BASE_OHOS_H_ +diff --git a/runtime/bin/socket_ohos.cc b/runtime/bin/socket_ohos.cc +new file mode 100644 +index 00000000000..0d4386b14c6 +--- /dev/null ++++ b/runtime/bin/socket_ohos.cc +@@ -0,0 +1,280 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/socket.h" ++ ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++#include "platform/syslog.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++Socket::Socket(intptr_t fd) ++ : ReferenceCounted(), ++ fd_(fd), ++ isolate_port_(Dart_GetMainPortId()), ++ port_(ILLEGAL_PORT), ++ udp_receive_buffer_(NULL) {} ++ ++void Socket::CloseFd() { ++ SetClosedFd(); ++} ++ ++void Socket::SetClosedFd() { ++ fd_ = kClosedFd; ++} ++ ++static intptr_t Create(const RawAddr& addr) { ++ intptr_t fd; ++ intptr_t type = SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC; ++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ return fd; ++} ++ ++static intptr_t Connect(intptr_t fd, const RawAddr& addr) { ++ intptr_t result = TEMP_FAILURE_RETRY( ++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr))); ++ if ((result == 0) || (errno == EINPROGRESS)) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ return Connect(fd, addr); ++} ++ ++intptr_t Socket::CreateUnixDomainConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ intptr_t result = TEMP_FAILURE_RETRY(connect( ++ fd, (struct sockaddr*)&addr.un, SocketAddress::GetAddrLength(addr))); ++ if (result == 0 || errno == EAGAIN) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateBindConnect(const RawAddr& addr, ++ const RawAddr& source_addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ intptr_t result = TEMP_FAILURE_RETRY( ++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); ++ if (result != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ return Connect(fd, addr); ++} ++ ++intptr_t Socket::CreateUnixDomainBindConnect(const RawAddr& addr, ++ const RawAddr& source_addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ intptr_t result = TEMP_FAILURE_RETRY( ++ bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); ++ if (result != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ result = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&addr.un, ++ SocketAddress::GetAddrLength(addr))); ++ if (result == 0 || errno == EAGAIN) { ++ return fd; ++ } ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t Socket::CreateBindDatagram(const RawAddr& addr, ++ bool reuseAddress, ++ bool reusePort, ++ int ttl) { ++ intptr_t fd; ++ ++ fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, ++ SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, ++ IPPROTO_UDP)); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ if (reuseAddress) { ++ int optval = 1; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))); ++ } ++ ++ if (reusePort) { ++#ifdef SO_REUSEPORT // Not all Linux versions support this. ++ int optval = 1; ++ int reuse_port_success = ++ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); ++ // Even if it's defined, we might be running on a kernel ++ // that doesn't support it at runtime. ++ if (reuse_port_success != 0) { ++ if (errno == EINTR) { ++ FATAL("Unexpected EINTR errno"); ++ } ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ Syslog::PrintErr("Dart Socket ERROR: %s:%d: %s.", __FILE__, __LINE__, ++ Utils::StrError(errno, error_buf, kBufferSize)); ++ } ++#else // !defined SO_REUSEPORT ++ Syslog::PrintErr( ++ "Dart Socket ERROR: %s:%d: `reusePort` not available on this Linux " ++ "version.", ++ __FILE__, __LINE__); ++#endif // SO_REUSEPORT ++ } ++ ++ if (!SocketBase::SetMulticastHops(fd, ++ addr.addr.sa_family == AF_INET ++ ? SocketAddress::TYPE_IPV4 ++ : SocketAddress::TYPE_IPV6, ++ ttl)) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ if (NO_RETRY_EXPECTED( ++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++intptr_t ServerSocket::CreateBindListen(const RawAddr& addr, ++ intptr_t backlog, ++ bool v6_only) { ++ intptr_t fd; ++ ++ fd = NO_RETRY_EXPECTED( ++ socket(addr.ss.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ int optval = 1; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))); ++ ++ if (addr.ss.ss_family == AF_INET6) { ++ optval = v6_only ? 1 : 0; ++ VOID_NO_RETRY_EXPECTED( ++ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))); ++ } ++ ++ if (NO_RETRY_EXPECTED( ++ bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ // Test for invalid socket port 65535 (some browsers disallow it). ++ if ((SocketAddress::GetAddrPort(addr) == 0) && ++ (SocketBase::GetPort(fd) == 65535)) { ++ // Don't close the socket until we have created a new socket, ensuring ++ // that we do not get the bad port number again. ++ intptr_t new_fd = CreateBindListen(addr, backlog, v6_only); ++ FDUtils::SaveErrorAndClose(fd); ++ return new_fd; ++ } ++ ++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ ++ return fd; ++} ++ ++intptr_t ServerSocket::CreateUnixDomainBindListen(const RawAddr& addr, ++ intptr_t backlog) { ++ intptr_t fd = Create(addr); ++ if (NO_RETRY_EXPECTED(bind(fd, (struct sockaddr*)&addr.un, ++ SocketAddress::GetAddrLength(addr))) < 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++bool ServerSocket::StartAccept(intptr_t fd) { ++ USE(fd); ++ return true; ++} ++ ++static bool IsTemporaryAcceptError(int error) { ++ // On Linux a number of protocol errors should be treated as EAGAIN. ++ // These are the ones for TCP/IP. ++ return (error == EAGAIN) || (error == ENETDOWN) || (error == EPROTO) || ++ (error == ENOPROTOOPT) || (error == EHOSTDOWN) || (error == ENONET) || ++ (error == EHOSTUNREACH) || (error == EOPNOTSUPP) || ++ (error == ENETUNREACH); ++} ++ ++intptr_t ServerSocket::Accept(intptr_t fd) { ++ intptr_t socket; ++ struct sockaddr clientaddr; ++ socklen_t addrlen = sizeof(clientaddr); ++ socket = TEMP_FAILURE_RETRY(accept(fd, &clientaddr, &addrlen)); ++ if (socket == -1) { ++ if (IsTemporaryAcceptError(errno)) { ++ // We need to signal to the caller that this is actually not an ++ // error. We got woken up from the poll on the listening socket, ++ // but there is no connection ready to be accepted. ++ ASSERT(kTemporaryFailure != -1); ++ socket = kTemporaryFailure; ++ } ++ } else { ++ if (!FDUtils::SetCloseOnExec(socket)) { ++ FDUtils::SaveErrorAndClose(socket); ++ return -1; ++ } ++ if (!FDUtils::SetNonBlocking(socket)) { ++ FDUtils::SaveErrorAndClose(socket); ++ return -1; ++ } ++ } ++ return socket; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/stdio_ohos.cc b/runtime/bin/stdio_ohos.cc +new file mode 100644 +index 00000000000..8dd67b67b45 +--- /dev/null ++++ b/runtime/bin/stdio_ohos.cc +@@ -0,0 +1,139 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/stdio.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool Stdin::ReadByte(intptr_t fd, int* byte) { ++ unsigned char b; ++ ssize_t s = TEMP_FAILURE_RETRY(read(fd, &b, 1)); ++ if (s < 0) { ++ return false; ++ } ++ *byte = (s == 0) ? -1 : b; ++ return true; ++} ++ ++bool Stdin::GetEchoMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ECHO) != 0); ++ return true; ++} ++ ++bool Stdin::SetEchoMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ECHO; ++ } else { ++ term.c_lflag &= ~(ECHO); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++bool Stdin::GetEchoNewlineMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ECHONL) != 0); ++ return true; ++} ++ ++bool Stdin::SetEchoNewlineMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ECHONL; ++ } else { ++ term.c_lflag &= ~(ECHONL); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++bool Stdin::GetLineMode(intptr_t fd, bool* enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ *enabled = ((term.c_lflag & ICANON) != 0); ++ return true; ++} ++ ++bool Stdin::SetLineMode(intptr_t fd, bool enabled) { ++ struct termios term; ++ int status = NO_RETRY_EXPECTED(tcgetattr(fd, &term)); ++ if (status != 0) { ++ return false; ++ } ++ if (enabled) { ++ term.c_lflag |= ICANON; ++ } else { ++ term.c_lflag &= ~(ICANON); ++ } ++ status = NO_RETRY_EXPECTED(tcsetattr(fd, TCSANOW, &term)); ++ return (status == 0); ++} ++ ++static bool TermIsKnownToSupportAnsi() { ++ const char* term = getenv("TERM"); ++ if (term == NULL) { ++ return false; ++ } ++ ++ return strstr(term, "xterm") != NULL || strstr(term, "screen") != NULL || ++ strstr(term, "rxvt") != NULL; ++} ++ ++bool Stdin::AnsiSupported(intptr_t fd, bool* supported) { ++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi(); ++ return true; ++} ++ ++bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { ++ struct winsize w; ++ int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)); ++ if ((status == 0) && ((w.ws_col != 0) || (w.ws_row != 0))) { ++ size[0] = w.ws_col; ++ size[1] = w.ws_row; ++ return true; ++ } ++ return false; ++} ++ ++bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { ++ *supported = (isatty(fd) != 0) && TermIsKnownToSupportAnsi(); ++ return true; ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/sync_socket_ohos.cc b/runtime/bin/sync_socket_ohos.cc +new file mode 100644 +index 00000000000..be15707ba28 +--- /dev/null ++++ b/runtime/bin/sync_socket_ohos.cc +@@ -0,0 +1,92 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "bin/sync_socket.h" ++ ++#include // NOLINT ++ ++#include "bin/fdutils.h" ++#include "bin/socket_base.h" ++#include "platform/signal_blocker.h" ++ ++namespace dart { ++namespace bin { ++ ++bool SynchronousSocket::Initialize() { ++ // Nothing to do on Ohos. ++ return true; ++} ++ ++static intptr_t Create(const RawAddr& addr) { ++ intptr_t fd; ++ intptr_t type = SOCK_STREAM | SOCK_CLOEXEC; ++ fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, type, 0)); ++ if (fd < 0) { ++ return -1; ++ } ++ return fd; ++} ++ ++static intptr_t Connect(intptr_t fd, const RawAddr& addr) { ++ intptr_t result = TEMP_FAILURE_RETRY( ++ connect(fd, &addr.addr, SocketAddress::GetAddrLength(addr))); ++ if (result == 0) { ++ return fd; ++ } ++ ASSERT(errno != EINPROGRESS); ++ FDUtils::SaveErrorAndClose(fd); ++ return -1; ++} ++ ++intptr_t SynchronousSocket::CreateConnect(const RawAddr& addr) { ++ intptr_t fd = Create(addr); ++ if (fd < 0) { ++ return fd; ++ } ++ return Connect(fd, addr); ++} ++ ++intptr_t SynchronousSocket::Available(intptr_t fd) { ++ return SocketBase::Available(fd); ++} ++ ++intptr_t SynchronousSocket::GetPort(intptr_t fd) { ++ return SocketBase::GetPort(fd); ++} ++ ++SocketAddress* SynchronousSocket::GetRemotePeer(intptr_t fd, intptr_t* port) { ++ return SocketBase::GetRemotePeer(fd, port); ++} ++ ++intptr_t SynchronousSocket::Read(intptr_t fd, ++ void* buffer, ++ intptr_t num_bytes) { ++ return SocketBase::Read(fd, buffer, num_bytes, SocketBase::kSync); ++} ++ ++intptr_t SynchronousSocket::Write(intptr_t fd, ++ const void* buffer, ++ intptr_t num_bytes) { ++ return SocketBase::Write(fd, buffer, num_bytes, SocketBase::kSync); ++} ++ ++void SynchronousSocket::ShutdownRead(intptr_t fd) { ++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_RD)); ++} ++ ++void SynchronousSocket::ShutdownWrite(intptr_t fd) { ++ VOID_NO_RETRY_EXPECTED(shutdown(fd, SHUT_WR)); ++} ++ ++void SynchronousSocket::Close(intptr_t fd) { ++ return SocketBase::Close(fd); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/thread_ohos.cc b/runtime/bin/thread_ohos.cc +new file mode 100644 +index 00000000000..eaaf6684303 +--- /dev/null ++++ b/runtime/bin/thread_ohos.cc +@@ -0,0 +1,280 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) ++ ++#include "bin/thread.h" ++#include "bin/thread_ohos.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/assert.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++#define VALIDATE_PTHREAD_RESULT(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL2("pthread error: %d (%s)", result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++ ++#ifdef DEBUG ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ ++ result, Utils::StrError(result, error_buf, kBufferSize)); \ ++ return result; \ ++ } ++#else ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ return result; \ ++ } ++#endif ++ ++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) { ++ int64_t secs = micros / kMicrosecondsPerSecond; ++ int64_t nanos = ++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond; ++ int result = clock_gettime(CLOCK_MONOTONIC, ts); ++ ASSERT(result == 0); ++ ts->tv_sec += secs; ++ ts->tv_nsec += nanos; ++ if (ts->tv_nsec >= kNanosecondsPerSecond) { ++ ts->tv_sec += 1; ++ ts->tv_nsec -= kNanosecondsPerSecond; ++ } ++} ++ ++class ThreadStartData { ++ public: ++ ThreadStartData(const char* name, ++ Thread::ThreadStartFunction function, ++ uword parameter) ++ : name_(name), function_(function), parameter_(parameter) {} ++ ++ const char* name() const { return name_; } ++ Thread::ThreadStartFunction function() const { return function_; } ++ uword parameter() const { return parameter_; } ++ ++ private: ++ const char* name_; ++ Thread::ThreadStartFunction function_; ++ uword parameter_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData); ++}; ++ ++// Dispatch to the thread start function provided by the caller. This trampoline ++// is used to ensure that the thread is properly destroyed if the thread just ++// exits. ++static void* ThreadStart(void* data_ptr) { ++ ThreadStartData* data = reinterpret_cast(data_ptr); ++ ++ const char* name = data->name(); ++ Thread::ThreadStartFunction function = data->function(); ++ uword parameter = data->parameter(); ++ delete data; ++ ++ // Set the thread name. There is 16 bytes limit on the name (including \0). ++ // pthread_setname_np ignores names that are too long rather than truncating. ++ char truncated_name[16]; ++ snprintf(truncated_name, sizeof(truncated_name), "%s", name); ++ pthread_setname_np(pthread_self(), truncated_name); ++ ++ // Call the supplied thread start function handing it its parameters. ++ function(parameter); ++ ++ return NULL; ++} ++ ++int Thread::Start(const char* name, ++ ThreadStartFunction function, ++ uword parameter) { ++ pthread_attr_t attr; ++ int result = pthread_attr_init(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setstacksize(&attr, Thread::GetMaxStackSize()); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ ThreadStartData* data = new ThreadStartData(name, function, parameter); ++ ++ pthread_t tid; ++ result = pthread_create(&tid, &attr, ThreadStart, data); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_destroy(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ return 0; ++} ++ ++const ThreadId Thread::kInvalidThreadId = static_cast(0); ++ ++intptr_t Thread::GetMaxStackSize() { ++ const int kStackSize = (128 * kWordSize * KB); ++ return kStackSize; ++} ++ ++ThreadId Thread::GetCurrentThreadId() { ++ return pthread_self(); ++} ++ ++bool Thread::Compare(ThreadId a, ThreadId b) { ++ return (pthread_equal(a, b) != 0); ++} ++ ++Mutex::Mutex() { ++ pthread_mutexattr_t attr; ++ int result = pthread_mutexattr_init(&attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &attr); ++ // Verify that creating a pthread_mutex succeeded. ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&attr); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Mutex::~Mutex() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ // Verify that the pthread_mutex was destroyed. ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Mutex::Lock() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ // Specifically check for dead lock to help debugging. ++ ASSERT(result != EDEADLK); ++ ASSERT(result == 0); // Verify no other errors. ++ // TODO(iposva): Do we need to track lock owners? ++} ++ ++bool Mutex::TryLock() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT(result == 0); // Verify no other errors. ++ // TODO(iposva): Do we need to track lock owners? ++ return true; ++} ++ ++void Mutex::Unlock() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_mutex_unlock(data_.mutex()); ++ // Specifically check for wrong thread unlocking to aid debugging. ++ ASSERT(result != EPERM); ++ ASSERT(result == 0); // Verify no other errors. ++} ++ ++Monitor::Monitor() { ++ pthread_mutexattr_t mutex_attr; ++ int result = pthread_mutexattr_init(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ pthread_condattr_t cond_attr; ++ result = pthread_condattr_init(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_init(data_.cond(), &cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_destroy(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::~Monitor() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_destroy(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::Enter() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ // TODO(iposva): Do we need to track lock owners? ++} ++ ++void Monitor::Exit() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_mutex_unlock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::WaitResult Monitor::Wait(int64_t millis) { ++ return WaitMicros(millis * kMicrosecondsPerMillisecond); ++} ++ ++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { ++ // TODO(iposva): Do we need to track lock owners? ++ Monitor::WaitResult retval = kNotified; ++ if (micros == kNoTimeout) { ++ // Wait forever. ++ int result = pthread_cond_wait(data_.cond(), data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ } else { ++ struct timespec ts; ++ ComputeTimeSpecMicros(&ts, micros); ++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts); ++ ASSERT((result == 0) || (result == ETIMEDOUT)); ++ if (result == ETIMEDOUT) { ++ retval = kTimedOut; ++ } ++ } ++ return retval; ++} ++ ++void Monitor::Notify() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_cond_signal(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::NotifyAll() { ++ // TODO(iposva): Do we need to track lock owners? ++ int result = pthread_cond_broadcast(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) +diff --git a/runtime/bin/thread_ohos.h b/runtime/bin/thread_ohos.h +new file mode 100644 +index 00000000000..2d2b5fbb12c +--- /dev/null ++++ b/runtime/bin/thread_ohos.h +@@ -0,0 +1,57 @@ ++// 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. ++ ++#ifndef RUNTIME_BIN_THREAD_OHOS_H_ ++#define RUNTIME_BIN_THREAD_OHOS_H_ ++ ++#if !defined(RUNTIME_BIN_THREAD_H_) ++#error Do not include thread_linux.h directly; use thread.h instead. ++#endif ++ ++#include ++ ++#include "platform/assert.h" ++#include "platform/globals.h" ++ ++namespace dart { ++namespace bin { ++ ++typedef pthread_t ThreadId; ++ ++class MutexData { ++ private: ++ MutexData() {} ++ ~MutexData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ ++ pthread_mutex_t mutex_; ++ ++ friend class Mutex; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MutexData); ++}; ++ ++class MonitorData { ++ private: ++ MonitorData() {} ++ ~MonitorData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ pthread_cond_t* cond() { return &cond_; } ++ ++ pthread_mutex_t mutex_; ++ pthread_cond_t cond_; ++ ++ friend class Monitor; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MonitorData); ++}; ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // RUNTIME_BIN_THREAD_OHOS_H_ +diff --git a/runtime/bin/utils_ohos.cc b/runtime/bin/utils_ohos.cc +new file mode 100644 +index 00000000000..487e4f30199 +--- /dev/null ++++ b/runtime/bin/utils_ohos.cc +@@ -0,0 +1,113 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "bin/utils.h" ++#include "platform/assert.h" ++#include "platform/utils.h" ++ ++namespace dart { ++namespace bin { ++ ++OSError::OSError() : sub_system_(kSystem), code_(0), message_(NULL) { ++ Reload(); ++} ++ ++void OSError::Reload() { ++ SetCodeAndMessage(kSystem, errno); ++} ++ ++void OSError::SetCodeAndMessage(SubSystem sub_system, int code) { ++ set_sub_system(sub_system); ++ set_code(code); ++ if (sub_system == kSystem) { ++ const int kBufferSize = 1024; ++ char error_buf[kBufferSize]; ++ SetMessage(Utils::StrError(code, error_buf, kBufferSize)); ++ } else if (sub_system == kGetAddressInfo) { ++ SetMessage(gai_strerror(code)); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++const char* StringUtils::ConsoleStringToUtf8(const char* str, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++const char* StringUtils::Utf8ToConsoleString(const char* utf8, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++char* StringUtils::ConsoleStringToUtf8(char* str, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++char* StringUtils::Utf8ToConsoleString(char* utf8, ++ intptr_t len, ++ intptr_t* result_len) { ++ return NULL; ++} ++ ++bool ShellUtils::GetUtf8Argv(int argc, char** argv) { ++ return false; ++} ++ ++void TimerUtils::InitOnce() {} ++ ++int64_t TimerUtils::GetCurrentMonotonicMillis() { ++ return GetCurrentMonotonicMicros() / 1000; ++} ++ ++int64_t TimerUtils::GetCurrentMonotonicMicros() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ // Convert to microseconds. ++ int64_t result = ts.tv_sec; ++ result *= kMicrosecondsPerSecond; ++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond); ++ return result; ++} ++ ++void TimerUtils::Sleep(int64_t millis) { ++ struct timespec req; // requested. ++ struct timespec rem; // remainder. ++ int64_t micros = millis * kMicrosecondsPerMillisecond; ++ int64_t seconds = micros / kMicrosecondsPerSecond; ++ micros = micros - seconds * kMicrosecondsPerSecond; ++ int64_t nanos = micros * kNanosecondsPerMicrosecond; ++ req.tv_sec = seconds; ++ req.tv_nsec = nanos; ++ while (true) { ++ int r = nanosleep(&req, &rem); ++ if (r == 0) { ++ break; ++ } ++ // We should only ever see an interrupt error. ++ ASSERT(errno == EINTR); ++ // Copy remainder into requested and repeat. ++ req = rem; ++ } ++} ++ ++} // namespace bin ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc +index d7296c98e0f..d1abac994ff 100644 +--- a/runtime/bin/vmservice_impl.cc ++++ b/runtime/bin/vmservice_impl.cc +@@ -127,6 +127,9 @@ bool VmService::Setup(const char* server_ip, + + Dart_Handle result; + ++ //TODO 临时关闭验证 ++ auth_codes_disabled=true; ++ + // Prepare builtin and its dependent libraries for use to resolve URIs. + // Set up various closures, e.g: printing, timers etc. + // Set up 'package root' for URI resolution. +diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h +index 9c8d00fc657..7122f9f7d08 100644 +--- a/runtime/platform/globals.h ++++ b/runtime/platform/globals.h +@@ -383,7 +383,8 @@ struct simd128_value_t { + + #if !defined(DART_TARGET_OS_ANDROID) && !defined(DART_TARGET_OS_FUCHSIA) && \ + !defined(DART_TARGET_OS_MACOS_IOS) && !defined(DART_TARGET_OS_LINUX) && \ +- !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) ++ !defined(DART_TARGET_OS_MACOS) && !defined(DART_TARGET_OS_WINDOWS) && \ ++ !defined(DART_TARGET_OS_OHOS) + // No target OS specified; pick the one matching the host OS. + #if defined(DART_HOST_OS_ANDROID) + #define DART_TARGET_OS_ANDROID 1 +@@ -394,6 +395,8 @@ struct simd128_value_t { + #define DART_TARGET_OS_MACOS_IOS 1 + #elif defined(DART_HOST_OS_LINUX) + #define DART_TARGET_OS_LINUX 1 ++#elif defined(DART_HOST_OS_OHOS) ++#define DART_TARGET_OS_OHOS 1 + #elif defined(DART_HOST_OS_MACOS) + #define DART_TARGET_OS_MACOS 1 + #elif defined(DART_HOST_OS_WINDOWS) +@@ -789,6 +792,8 @@ DART_FORCE_INLINE D bit_copy(const S& source) { + #define kTargetOperatingSystemName "fuchsia" + #elif defined(DART_TARGET_OS_LINUX) + #define kTargetOperatingSystemName "linux" ++#elif defined(DART_TARGET_OS_OHOS) ++#define kTargetOperatingSystemName "ohos" + #elif defined(DART_TARGET_OS_MACOS_IOS) + #define kTargetOperatingSystemName "ios" + #elif defined(DART_TARGET_OS_MACOS) +diff --git a/runtime/platform/platform_sources.gni b/runtime/platform/platform_sources.gni +index bef56feb093..50f90cc76dc 100644 +--- a/runtime/platform/platform_sources.gni ++++ b/runtime/platform/platform_sources.gni +@@ -25,6 +25,7 @@ platform_sources = [ + "syslog_android.cc", + "syslog_fuchsia.cc", + "syslog_linux.cc", ++ "syslog_ohos.cc", + "syslog_macos.cc", + "syslog_win.cc", + "text_buffer.cc", +@@ -37,6 +38,7 @@ platform_sources = [ + "utils_android.cc", + "utils_fuchsia.cc", + "utils_linux.cc", ++ "utils_ohos.cc", + "utils_macos.cc", + "utils_win.cc", + ] +diff --git a/runtime/platform/syslog_ohos.cc b/runtime/platform/syslog_ohos.cc +new file mode 100644 +index 00000000000..3dcbc463dc6 +--- /dev/null ++++ b/runtime/platform/syslog_ohos.cc +@@ -0,0 +1,26 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/syslog.h" ++ ++#include // NOLINT ++ ++namespace dart { ++ ++void Syslog::VPrint(const char* format, va_list args) { ++ vfprintf(stdout, format, args); ++ fflush(stdout); ++} ++ ++void Syslog::VPrintErr(const char* format, va_list args) { ++ vfprintf(stderr, format, args); ++ fflush(stderr); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/platform/utils_ohos.cc b/runtime/platform/utils_ohos.cc +new file mode 100644 +index 00000000000..806796a5935 +--- /dev/null ++++ b/runtime/platform/utils_ohos.cc +@@ -0,0 +1,54 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/utils.h" ++#include "platform/utils_ohos.h" ++ ++namespace dart { ++ ++char* Utils::StrNDup(const char* s, intptr_t n) { ++ return strndup(s, n); ++} ++ ++char* Utils::StrDup(const char* s) { ++ return strdup(s); ++} ++ ++intptr_t Utils::StrNLen(const char* s, intptr_t n) { ++ return strnlen(s, n); ++} ++ ++int Utils::SNPrint(char* str, size_t size, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ int retval = VSNPrint(str, size, format, args); ++ va_end(args); ++ return retval; ++} ++ ++int Utils::VSNPrint(char* str, size_t size, const char* format, va_list args) { ++ MSAN_UNPOISON(str, size); ++ int retval = vsnprintf(str, size, format, args); ++ if (retval < 0) { ++ FATAL1("Fatal error in Utils::VSNPrint with format '%s'", format); ++ } ++ return retval; ++} ++ ++int Utils::Close(int fildes) { ++ return close(fildes); ++} ++size_t Utils::Read(int filedes, void* buf, size_t nbyte) { ++ return read(filedes, buf, nbyte); ++} ++int Utils::Unlink(const char* path) { ++ return unlink(path); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/platform/utils_ohos.h b/runtime/platform/utils_ohos.h +new file mode 100644 +index 00000000000..b33c3291826 +--- /dev/null ++++ b/runtime/platform/utils_ohos.h +@@ -0,0 +1,56 @@ ++// 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. ++ ++#ifndef RUNTIME_PLATFORM_UTILS_OHOS_H_ ++#define RUNTIME_PLATFORM_UTILS_OHOS_H_ ++ ++#if !defined(RUNTIME_PLATFORM_UTILS_H_) ++#error Do not include utils_linux.h directly; use utils.h instead. ++#endif ++ ++#include // NOLINT ++ ++namespace dart { ++ ++inline uint16_t Utils::HostToBigEndian16(uint16_t value) { ++ return htobe16(value); ++} ++ ++inline uint32_t Utils::HostToBigEndian32(uint32_t value) { ++ return htobe32(value); ++} ++ ++inline uint64_t Utils::HostToBigEndian64(uint64_t value) { ++ return htobe64(value); ++} ++ ++inline uint16_t Utils::HostToLittleEndian16(uint16_t value) { ++ return htole16(value); ++} ++ ++inline uint32_t Utils::HostToLittleEndian32(uint32_t value) { ++ return htole32(value); ++} ++ ++inline uint64_t Utils::HostToLittleEndian64(uint64_t value) { ++ return htole64(value); ++} ++ ++inline char* Utils::StrError(int err, char* buffer, size_t bufsize) { ++#if !defined(__GLIBC__) || \ ++ ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) ++ // Use the XSI version. ++ if (strerror_r(err, buffer, bufsize) != 0) { ++ snprintf(buffer, bufsize, "%s", "strerror_r failed"); ++ } ++ return buffer; ++#else ++ // Use the GNU specific version. ++ return strerror_r(err, buffer, bufsize); ++#endif ++} ++ ++} // namespace dart ++ ++#endif // RUNTIME_PLATFORM_UTILS_OHOS_H_ +diff --git a/runtime/vm/compiler/ffi/abi.cc b/runtime/vm/compiler/ffi/abi.cc +index 66fa5a8d564..cc5eb834b3d 100644 +--- a/runtime/vm/compiler/ffi/abi.cc ++++ b/runtime/vm/compiler/ffi/abi.cc +@@ -53,6 +53,8 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8, + #define DART_TARGET_OS_NAME Fuchsia + #elif defined(DART_TARGET_OS_LINUX) + #define DART_TARGET_OS_NAME Linux ++#elif defined(DART_TARGET_OS_OHOS) ++#define DART_TARGET_OS_NAME Ohos + #elif defined(DART_TARGET_OS_MACOS) + #if DART_TARGET_OS_MACOS_IOS + #define DART_TARGET_OS_NAME IOS +diff --git a/runtime/vm/compiler/ffi/abi.h b/runtime/vm/compiler/ffi/abi.h +index b693ef85255..54b15f48890 100644 +--- a/runtime/vm/compiler/ffi/abi.h ++++ b/runtime/vm/compiler/ffi/abi.h +@@ -24,6 +24,8 @@ enum class Abi { + kAndroidArm64, + kAndroidIA32, + kAndroidX64, ++ kOhosArm, ++ kOhosArm64, + kFuchsiaArm64, + kFuchsiaX64, + kIOSArm, +@@ -49,9 +51,9 @@ const int64_t num_abis = static_cast(Abi::kWindowsX64) + 1; + // - runtime/vm/compiler/frontend/kernel_to_il.cc + static_assert(static_cast(Abi::kAndroidArm) == 0, + "Enum value unexpected."); +-static_assert(static_cast(Abi::kWindowsX64) == 19, ++static_assert(static_cast(Abi::kWindowsX64) == 21, + "Enum value unexpected."); +-static_assert(num_abis == 20, "Enum value unexpected."); ++static_assert(num_abis == 22, "Enum value unexpected."); + + // The target ABI. Defines sizes and alignment of native types. + Abi TargetAbi(); +diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc +index 001605f6163..11529096e4e 100644 +--- a/runtime/vm/compiler/ffi/native_calling_convention.cc ++++ b/runtime/vm/compiler/ffi/native_calling_convention.cc +@@ -31,7 +31,7 @@ static bool SoftFpAbi() { + } + #else // !defined(FFI_UNIT_TESTS) + static bool SoftFpAbi() { +-#if defined(TARGET_ARCH_ARM) && defined(DART_TARGET_OS_ANDROID) ++#if defined(TARGET_ARCH_ARM) && (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS)) + return true; + #else + return false; +diff --git a/runtime/vm/compiler/ffi/native_calling_convention_test.cc b/runtime/vm/compiler/ffi/native_calling_convention_test.cc +index 707d9e845db..08a0930de5d 100644 +--- a/runtime/vm/compiler/ffi/native_calling_convention_test.cc ++++ b/runtime/vm/compiler/ffi/native_calling_convention_test.cc +@@ -658,7 +658,7 @@ UNIT_TEST_CASE_WITH_ZONE(NativeCallingConvention_regress46127) { + RunSignatureTest(Z, "regress46127", arguments, struct_type); + + #if defined(TARGET_ARCH_IA32) && \ +- (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) ++ (defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX)) || defined(DART_TARGET_OS_OHOS) + // We must count the result pointer passed on the stack as well. + EXPECT_EQ(4, native_calling_convention.StackTopInBytes()); + #else +diff --git a/runtime/vm/cpu_arm.cc b/runtime/vm/cpu_arm.cc +index ce9a8ffbca3..04c3aefb682 100644 +--- a/runtime/vm/cpu_arm.cc ++++ b/runtime/vm/cpu_arm.cc +@@ -59,7 +59,7 @@ DEFINE_FLAG(bool, + "Use integer division instruction if supported"); + + #if defined(TARGET_HOST_MISMATCH) +-#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) ++#if defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_OHOS) + DEFINE_FLAG(bool, sim_use_hardfp, false, "Use the hardfp ABI."); + #else + DEFINE_FLAG(bool, sim_use_hardfp, true, "Use the hardfp ABI."); +diff --git a/runtime/vm/cpuinfo_ohos.cc b/runtime/vm/cpuinfo_ohos.cc +new file mode 100644 +index 00000000000..e5d90ac5dfb +--- /dev/null ++++ b/runtime/vm/cpuinfo_ohos.cc +@@ -0,0 +1,107 @@ ++// 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 "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "vm/cpuid.h" ++#include "vm/cpuinfo.h" ++#include "vm/proccpuinfo.h" ++ ++#include "platform/assert.h" ++ ++// As with Windows, on IA32 and X64, we use the cpuid instruction. ++// The analogous instruction is privileged on ARM, so we resort to ++// reading from /proc/cpuinfo. ++ ++namespace dart { ++ ++CpuInfoMethod CpuInfo::method_ = kCpuInfoDefault; ++const char* CpuInfo::fields_[kCpuInfoMax] = {0}; ++ ++void CpuInfo::Init() { ++#if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) ++ fields_[kCpuInfoProcessor] = "vendor_id"; ++ fields_[kCpuInfoModel] = "model name"; ++ fields_[kCpuInfoHardware] = "model name"; ++ fields_[kCpuInfoFeatures] = "flags"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoCpuId; ++ CpuId::Init(); ++#elif defined(HOST_ARCH_ARM) ++ fields_[kCpuInfoProcessor] = "Processor"; ++ fields_[kCpuInfoModel] = "model name"; ++ fields_[kCpuInfoHardware] = "Hardware"; ++ fields_[kCpuInfoFeatures] = "Features"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoSystem; ++ ProcCpuInfo::Init(); ++#elif defined(HOST_ARCH_ARM64) ++ fields_[kCpuInfoProcessor] = "Processor"; ++ fields_[kCpuInfoModel] = "CPU implementer"; ++ fields_[kCpuInfoHardware] = "CPU implementer"; ++ fields_[kCpuInfoFeatures] = "Features"; ++ fields_[kCpuInfoArchitecture] = "CPU architecture"; ++ method_ = kCpuInfoSystem; ++ ProcCpuInfo::Init(); ++#elif defined(HOST_ARCH_RISCV32) || defined(HOST_ARCH_RISCV64) ++ // We only rely on the base Linux configuration of IMAFDC, so don't need ++ // dynamic feature detection. ++ method_ = kCpuInfoNone; ++#else ++#error Unrecognized target architecture ++#endif ++} ++ ++void CpuInfo::Cleanup() { ++ if (method_ == kCpuInfoCpuId) { ++ CpuId::Cleanup(); ++ } else if (method_ == kCpuInfoSystem) { ++ ProcCpuInfo::Cleanup(); ++ } else { ++ ASSERT(method_ == kCpuInfoNone); ++ } ++} ++ ++bool CpuInfo::FieldContains(CpuInfoIndices idx, const char* search_string) { ++ if (method_ == kCpuInfoCpuId) { ++ const char* field = CpuId::field(idx); ++ bool contains = (strstr(field, search_string) != NULL); ++ free(const_cast(field)); ++ return contains; ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::FieldContains(FieldName(idx), search_string); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++const char* CpuInfo::ExtractField(CpuInfoIndices idx) { ++ if (method_ == kCpuInfoCpuId) { ++ return CpuId::field(idx); ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::ExtractField(FieldName(idx)); ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++bool CpuInfo::HasField(const char* field) { ++ if (method_ == kCpuInfoCpuId) { ++ return (strcmp(field, fields_[kCpuInfoProcessor]) == 0) || ++ (strcmp(field, fields_[kCpuInfoModel]) == 0) || ++ (strcmp(field, fields_[kCpuInfoHardware]) == 0) || ++ (strcmp(field, fields_[kCpuInfoFeatures]) == 0); ++ } else if (method_ == kCpuInfoSystem) { ++ return ProcCpuInfo::HasField(field); ++ } else if (method_ == kCpuInfoNone) { ++ return false; ++ } else { ++ UNREACHABLE(); ++ } ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc +index a0e900a85a3..9e022d1a470 100644 +--- a/runtime/vm/dart.cc ++++ b/runtime/vm/dart.cc +@@ -1205,6 +1205,8 @@ char* Dart::FeaturesString(IsolateGroup* isolate_group, + #endif + #elif defined(DART_TARGET_OS_LINUX) + buffer.AddString(" linux"); ++#elif defined(DART_TARGET_OS_OHOS) ++ buffer.AddString(" ohos"); + #elif defined(DART_TARGET_OS_WINDOWS) + buffer.AddString(" windows"); + #else +diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h +index 80b830e821e..13db280340a 100644 +--- a/runtime/vm/globals.h ++++ b/runtime/vm/globals.h +@@ -115,7 +115,7 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; + + #if defined(DART_ENABLE_TIMELINE) || !defined(PRODUCT) || \ + defined(DART_HOST_OS_FUCHSIA) || defined(DART_TARGET_OS_FUCHSIA) || \ +- defined(DART_TARGET_OS_ANDROID) ++ defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_OHOS) + #define SUPPORT_TIMELINE 1 + #endif + +diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc +index c11a5faf6fe..cc001b8f826 100644 +--- a/runtime/vm/image_snapshot.cc ++++ b/runtime/vm/image_snapshot.cc +@@ -1051,7 +1051,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_abbrev,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_abbrev,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1061,7 +1061,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_info,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_info,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1073,7 +1073,7 @@ class DwarfAssemblyStream : public DwarfWriteStream { + #if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + stream_->WriteString(".section __DWARF,__debug_line,regular,debug\n"); + #elif defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + stream_->WriteString(".section .debug_line,\"\"\n"); + #else + UNIMPLEMENTED(); +@@ -1154,7 +1154,7 @@ void AssemblyImageWriter::Finalize() { + } + + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Non-executable stack. + assembly_stream_->WriteString(".section .note.GNU-stack,\"\"\n"); + #endif +@@ -1312,7 +1312,7 @@ void AssemblyImageWriter::WriteROData(NonStreamingWriteStream* clustered_stream, + WriteBytes(bytes + last_position, symbol.offset - last_position); + assembly_stream_->Printf("\"%s\":\n", symbol.name); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + // Output size and type of the read-only data symbol to the assembly stream. + assembly_stream_->Printf(".size \"%s\", %zu\n", symbol.name, symbol.size); + assembly_stream_->Printf(".type \"%s\", %%object\n", symbol.name); +@@ -1354,7 +1354,7 @@ bool AssemblyImageWriter::EnterSection(ProgramSection section, + current_symbols_ = + new (zone_) ZoneGrowableArray(zone_, 0); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) + assembly_stream_->WriteString(".section .rodata\n"); + #elif defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS) + assembly_stream_->WriteString(".const\n"); +@@ -1411,7 +1411,7 @@ void AssemblyImageWriter::ExitSection(ProgramSection name, + // We should still be in the same section as the last EnterSection. + ASSERT_EQUAL(current_section_label_, SectionLabel(name, vm)); + #if defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID) || \ +- defined(DART_TARGET_OS_FUCHSIA) ++ defined(DART_TARGET_OS_FUCHSIA) || defined(DART_TARGET_OS_OHOS) // Output the size of the section symbol to the assembly stream. assembly_stream_->Printf(".size %s, %zu\n", SectionSymbol(name, vm), size); assembly_stream_->Printf(".type %s, %%object\n", SectionSymbol(name, vm)); @@ -493,6 +5886,1754 @@ index c11a5faf6fe..cc001b8f826 100644 // Output the size of the code symbol to the assembly stream. assembly_stream_->Printf(".size \"%s\", %zu\n", symbol, code.Size()); assembly_stream_->Printf(".type \"%s\", %%function\n", symbol); +diff --git a/runtime/vm/native_symbol_ohos.cc b/runtime/vm/native_symbol_ohos.cc +new file mode 100644 +index 00000000000..37c9e47402d +--- /dev/null ++++ b/runtime/vm/native_symbol_ohos.cc +@@ -0,0 +1,72 @@ ++// 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 "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "platform/memory_sanitizer.h" ++#include "vm/native_symbol.h" ++#include "vm/os.h" ++ ++#include // NOLINT ++#include // NOLINT ++ ++namespace dart { ++ ++void NativeSymbolResolver::Init() {} ++ ++void NativeSymbolResolver::Cleanup() {} ++ ++char* NativeSymbolResolver::LookupSymbolName(uword pc, uword* start) { ++ Dl_info info; ++ int r = dladdr(reinterpret_cast(pc), &info); ++ if (r == 0) { ++ return NULL; ++ } ++ if (info.dli_sname == NULL) { ++ return NULL; ++ } ++ if (start != NULL) { ++ *start = reinterpret_cast(info.dli_saddr); ++ } ++ int status = 0; ++ size_t len = 0; ++ char* demangled = abi::__cxa_demangle(info.dli_sname, NULL, &len, &status); ++ MSAN_UNPOISON(demangled, len); ++ if (status == 0) { ++ return demangled; ++ } ++ return strdup(info.dli_sname); ++} ++ ++void NativeSymbolResolver::FreeSymbolName(char* name) { ++ free(name); ++} ++ ++bool NativeSymbolResolver::LookupSharedObject(uword pc, ++ uword* dso_base, ++ char** dso_name) { ++ Dl_info info; ++ int r = dladdr(reinterpret_cast(pc), &info); ++ if (r == 0) { ++ return false; ++ } ++ if (dso_base != nullptr) { ++ *dso_base = reinterpret_cast(info.dli_fbase); ++ } ++ if (dso_name != nullptr) { ++ *dso_name = strdup(info.dli_fname); ++ } ++ return true; ++} ++ ++void NativeSymbolResolver::AddSymbols(const char* dso_name, ++ void* buffer, ++ size_t size) { ++ OS::PrintErr("warning: Dart_AddSymbols has no effect on Ohos\n"); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/os_ohos.cc b/runtime/vm/os_ohos.cc +new file mode 100644 +index 00000000000..7ac0322c01c +--- /dev/null ++++ b/runtime/vm/os_ohos.cc +@@ -0,0 +1,669 @@ ++// 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 "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include "vm/os.h" ++ ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/memory_sanitizer.h" ++#include "platform/utils.h" ++#include "vm/code_comments.h" ++#include "vm/code_observers.h" ++#include "vm/dart.h" ++#include "vm/flags.h" ++#include "vm/isolate.h" ++#include "vm/lockers.h" ++#include "vm/os_thread.h" ++#include "vm/timeline.h" ++#include "vm/zone.h" ++ ++namespace dart { ++ ++#ifndef PRODUCT ++ ++DEFINE_FLAG(bool, ++ generate_perf_events_symbols, ++ false, ++ "Generate events symbols for profiling with perf (disables dual " ++ "code mapping)"); ++ ++DEFINE_FLAG(bool, ++ generate_perf_jitdump, ++ false, ++ "Generate jitdump file to use with perf-inject (disables dual code " ++ "mapping)"); ++ ++DECLARE_FLAG(bool, write_protect_code); ++DECLARE_FLAG(bool, write_protect_vm_isolate); ++#if !defined(DART_PRECOMPILED_RUNTIME) ++DECLARE_FLAG(bool, code_comments); ++#endif ++ ++// Linux CodeObservers. ++ ++// Simple perf support: generate /tmp/perf-.map file that maps ++// memory ranges to symbol names for JIT generated code. This allows ++// perf-report to resolve addresses falling into JIT generated code. ++// However perf-annotate does not work in this mode because JIT code ++// is transient and does not exist anymore at the moment when you ++// invoke perf-report. ++class PerfCodeObserver : public CodeObserver { ++ public: ++ PerfCodeObserver() : out_file_(NULL) { ++ Dart_FileOpenCallback file_open = Dart::file_open_callback(); ++ if (file_open == NULL) { ++ return; ++ } ++ intptr_t pid = getpid(); ++ char* filename = OS::SCreate(NULL, "/tmp/perf-%" Pd ".map", pid); ++ out_file_ = (*file_open)(filename, true); ++ free(filename); ++ } ++ ++ ~PerfCodeObserver() { ++ Dart_FileCloseCallback file_close = Dart::file_close_callback(); ++ if ((file_close == NULL) || (out_file_ == NULL)) { ++ return; ++ } ++ (*file_close)(out_file_); ++ } ++ ++ virtual bool IsActive() const { ++ return FLAG_generate_perf_events_symbols && (out_file_ != NULL); ++ } ++ ++ virtual void Notify(const char* name, ++ uword base, ++ uword prologue_offset, ++ uword size, ++ bool optimized, ++ const CodeComments* comments) { ++ Dart_FileWriteCallback file_write = Dart::file_write_callback(); ++ if ((file_write == NULL) || (out_file_ == NULL)) { ++ return; ++ } ++ const char* marker = optimized ? "*" : ""; ++ char* buffer = ++ OS::SCreate(Thread::Current()->zone(), "%" Px " %" Px " %s%s\n", base, ++ size, marker, name); ++ { ++ MutexLocker ml(CodeObservers::mutex()); ++ (*file_write)(buffer, strlen(buffer), out_file_); ++ } ++ } ++ ++ private: ++ void* out_file_; ++ ++ DISALLOW_COPY_AND_ASSIGN(PerfCodeObserver); ++}; ++ ++// Code observer that generates a JITDUMP[1] file that can be interpreted by ++// perf-inject to generate ELF images for JIT generated code objects, which ++// allows both perf-report and perf-annotate to recognize them. ++// ++// Usage: ++// ++// $ perf record -k mono dart --generate-perf-jitdump benchmark.dart ++// $ perf inject -j -i perf.data -o perf.data.jitted ++// $ perf report -i perf.data.jitted ++// ++// [1] see linux/tools/perf/Documentation/jitdump-specification.txt for ++// JITDUMP binary format. ++class JitDumpCodeObserver : public CodeObserver { ++ public: ++ JitDumpCodeObserver() : pid_(getpid()) { ++ char* const filename = OS::SCreate(nullptr, "/tmp/jit-%" Pd ".dump", pid_); ++ const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666); ++ free(filename); ++ ++ if (fd == -1) { ++ return; ++ } ++ ++ // Map JITDUMP file, this mapping will be recorded by perf. This allows ++ // perf-inject to find this file later. ++ const long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) ++ if (page_size == -1) { ++ close(fd); ++ return; ++ } ++ ++ mapped_ = ++ mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); ++ if (mapped_ == nullptr) { ++ close(fd); ++ return; ++ } ++ mapped_size_ = page_size; ++ ++ out_file_ = fdopen(fd, "w+"); ++ if (out_file_ == nullptr) { ++ close(fd); ++ return; ++ } ++ ++ // Buffer the output to avoid high IO overheads - we are going to be ++ // writing all JIT generated code out. ++ setvbuf(out_file_, nullptr, _IOFBF, 2 * MB); ++ ++ // Disable code write protection and vm isolate write protection, because ++ // calling mprotect on the pages filled with JIT generated code objects ++ // confuses perf. ++ FLAG_write_protect_code = false; ++ FLAG_write_protect_vm_isolate = false; ++ ++#if !defined(DART_PRECOMPILED_RUNTIME) ++ // Enable code comments. ++ FLAG_code_comments = true; ++#endif ++ ++ // Write JITDUMP header. ++ WriteHeader(); ++ } ++ ++ ~JitDumpCodeObserver() { ++ if (mapped_ != nullptr) { ++ munmap(mapped_, mapped_size_); ++ mapped_ = nullptr; ++ } ++ ++ if (out_file_ != nullptr) { ++ fclose(out_file_); ++ out_file_ = nullptr; ++ } ++ } ++ ++ virtual bool IsActive() const { ++ return FLAG_generate_perf_jitdump && (out_file_ != nullptr); ++ } ++ ++ virtual void Notify(const char* name, ++ uword base, ++ uword prologue_offset, ++ uword size, ++ bool optimized, ++ const CodeComments* comments) { ++ MutexLocker ml(CodeObservers::mutex()); ++ ++ const char* marker = optimized ? "*" : ""; ++ char* buffer = OS::SCreate(Thread::Current()->zone(), "%s%s", marker, name); ++ const size_t name_length = strlen(buffer); ++ ++ WriteDebugInfo(base, comments); ++ ++ CodeLoadEvent ev; ++ ev.event = BaseEvent::kLoad; ++ ev.size = sizeof(ev) + (name_length + 1) + size; ++ ev.time_stamp = OS::GetCurrentMonotonicTicks(); ++ ev.process_id = getpid(); ++ ev.thread_id = syscall(SYS_gettid); ++ ev.vma = base; ++ ev.code_address = base; ++ ev.code_size = size; ++ ev.code_id = code_id_++; ++ ++ WriteFully(&ev, sizeof(ev)); ++ WriteFully(buffer, name_length + 1); ++ WriteFully(reinterpret_cast(base), size); ++ } ++ ++ private: ++ struct Header { ++ const uint32_t magic = 0x4A695444; ++ const uint32_t version = 1; ++ const uint32_t size = sizeof(Header); ++ uint32_t elf_mach_target; ++ const uint32_t reserved = 0xDEADBEEF; ++ uint32_t process_id; ++ uint64_t time_stamp; ++ const uint64_t flags = 0; ++ }; ++ ++ struct BaseEvent { ++ enum Event { ++ kLoad = 0, ++ kMove = 1, ++ kDebugInfo = 2, ++ kClose = 3, ++ kUnwindingInfo = 4 ++ }; ++ ++ uint32_t event; ++ uint32_t size; ++ uint64_t time_stamp; ++ }; ++ ++ struct CodeLoadEvent : BaseEvent { ++ uint32_t process_id; ++ uint32_t thread_id; ++ uint64_t vma; ++ uint64_t code_address; ++ uint64_t code_size; ++ uint64_t code_id; ++ }; ++ ++ struct DebugInfoEvent : BaseEvent { ++ uint64_t address; ++ uint64_t entry_count; ++ // DebugInfoEntry entries[entry_count_]; ++ }; ++ ++ struct DebugInfoEntry { ++ uint64_t address; ++ int32_t line_number; ++ int32_t column; ++ // Followed by nul-terminated name. ++ }; ++ ++ // ELF machine architectures ++ // From linux/include/uapi/linux/elf-em.h ++ static const uint32_t EM_386 = 3; ++ static const uint32_t EM_X86_64 = 62; ++ static const uint32_t EM_ARM = 40; ++ static const uint32_t EM_AARCH64 = 183; ++ static const uint32_t EM_RISCV = 243; ++ ++ static uint32_t GetElfMachineArchitecture() { ++#if TARGET_ARCH_IA32 ++ return EM_386; ++#elif TARGET_ARCH_X64 ++ return EM_X86_64; ++#elif TARGET_ARCH_ARM ++ return EM_ARM; ++#elif TARGET_ARCH_ARM64 ++ return EM_AARCH64; ++#elif TARGET_ARCH_RISCV32 || TARGET_ARCH_RISCV64 ++ return EM_RISCV; ++#else ++ UNREACHABLE(); ++ return 0; ++#endif ++ } ++ ++#if ARCH_IS_64_BIT ++ static const int kElfHeaderSize = 0x40; ++#else ++ static const int kElfHeaderSize = 0x34; ++#endif ++ ++ void WriteDebugInfo(uword base, const CodeComments* comments) { ++ if (comments == nullptr || comments->Length() == 0) { ++ return; ++ } ++ ++ // Open the comments file for the given code object. ++ // Note: for some reason we can't emit all comments into a single file ++ // the mapping between PCs and lines goes out of sync (might be ++ // perf-annotate bug). ++ char* comments_file_name = ++ OS::SCreate(nullptr, "/tmp/jit-%" Pd "-%" Pd ".cmts", pid_, code_id_); ++ const intptr_t filename_length = strlen(comments_file_name); ++ FILE* comments_file = fopen(comments_file_name, "w"); ++ setvbuf(comments_file, nullptr, _IOFBF, 2 * MB); ++ ++ // Count the number of DebugInfoEntry we are going to emit: one ++ // per PC. ++ intptr_t entry_count = 0; ++ for (uint64_t i = 0, len = comments->Length(); i < len;) { ++ const intptr_t pc_offset = comments->PCOffsetAt(i); ++ while (i < len && comments->PCOffsetAt(i) == pc_offset) { ++ i++; ++ } ++ entry_count++; ++ } ++ ++ DebugInfoEvent info; ++ info.event = BaseEvent::kDebugInfo; ++ info.time_stamp = OS::GetCurrentMonotonicTicks(); ++ info.address = base; ++ info.entry_count = entry_count; ++ info.size = sizeof(info) + ++ entry_count * (sizeof(DebugInfoEntry) + filename_length + 1); ++ const int32_t padding = Utils::RoundUp(info.size, 8) - info.size; ++ info.size += padding; ++ ++ // Write out DebugInfoEvent record followed by entry_count DebugInfoEntry ++ // records. ++ WriteFully(&info, sizeof(info)); ++ intptr_t line_number = 0; // Line number within comments_file. ++ for (intptr_t i = 0, len = comments->Length(); i < len;) { ++ const intptr_t pc_offset = comments->PCOffsetAt(i); ++ while (i < len && comments->PCOffsetAt(i) == pc_offset) { ++ line_number += WriteLn(comments_file, comments->CommentAt(i)); ++ i++; ++ } ++ DebugInfoEntry entry; ++ entry.address = base + pc_offset + kElfHeaderSize; ++ entry.line_number = line_number; ++ entry.column = 0; ++ WriteFully(&entry, sizeof(entry)); ++ WriteFully(comments_file_name, filename_length + 1); ++ } ++ ++ // Write out the padding. ++ const char padding_bytes[8] = {0}; ++ WriteFully(padding_bytes, padding); ++ ++ fclose(comments_file); ++ free(comments_file_name); ++ } ++ ++ void WriteHeader() { ++ Header header; ++ header.elf_mach_target = GetElfMachineArchitecture(); ++ header.process_id = getpid(); ++ header.time_stamp = OS::GetCurrentTimeMicros(); ++ WriteFully(&header, sizeof(header)); ++ } ++ ++ // Returns number of new-lines written. ++ intptr_t WriteLn(FILE* f, const char* comment) { ++ fputs(comment, f); ++ fputc('\n', f); ++ ++ intptr_t line_count = 1; ++ while ((comment = strstr(comment, "\n")) != nullptr) { ++ line_count++; ++ } ++ return line_count; ++ } ++ ++ void WriteFully(const void* buffer, size_t size) { ++ const char* ptr = static_cast(buffer); ++ while (size > 0) { ++ const size_t written = fwrite(ptr, 1, size, out_file_); ++ if (written == 0) { ++ UNREACHABLE(); ++ break; ++ } ++ size -= written; ++ ptr += written; ++ } ++ } ++ ++ const intptr_t pid_; ++ ++ FILE* out_file_ = nullptr; ++ void* mapped_ = nullptr; ++ long mapped_size_ = 0; // NOLINT(runtime/int) ++ ++ intptr_t code_id_ = 0; ++ ++ DISALLOW_COPY_AND_ASSIGN(JitDumpCodeObserver); ++}; ++ ++#endif // !PRODUCT ++ ++intptr_t OS::ProcessId() { ++ return static_cast(getpid()); ++} ++ ++static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { ++ time_t seconds = static_cast(seconds_since_epoch); ++ if (seconds != seconds_since_epoch) return false; ++ struct tm* error_code = localtime_r(&seconds, tm_result); ++ return error_code != NULL; ++} ++ ++const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { ++ tm decomposed; ++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed); ++ // If unsuccessful, return an empty string like V8 does. ++ return (succeeded && (decomposed.tm_zone != NULL)) ? decomposed.tm_zone : ""; ++} ++ ++int OS::GetTimeZoneOffsetInSeconds(int64_t seconds_since_epoch) { ++ tm decomposed; ++ bool succeeded = LocalTime(seconds_since_epoch, &decomposed); ++ // Even if the offset was 24 hours it would still easily fit into 32 bits. ++ // If unsuccessful, return zero like V8 does. ++ return succeeded ? static_cast(decomposed.tm_gmtoff) : 0; ++} ++ ++int64_t OS::GetCurrentTimeMillis() { ++ return GetCurrentTimeMicros() / 1000; ++} ++ ++int64_t OS::GetCurrentTimeMicros() { ++ // gettimeofday has microsecond resolution. ++ struct timeval tv; ++ if (gettimeofday(&tv, NULL) < 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ return (static_cast(tv.tv_sec) * 1000000) + tv.tv_usec; ++} ++ ++int64_t OS::GetCurrentMonotonicTicks() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { ++ UNREACHABLE(); ++ return 0; ++ } ++ // Convert to nanoseconds. ++ int64_t result = ts.tv_sec; ++ result *= kNanosecondsPerSecond; ++ result += ts.tv_nsec; ++ return result; ++} ++ ++int64_t OS::GetCurrentMonotonicFrequency() { ++ return kNanosecondsPerSecond; ++} ++ ++int64_t OS::GetCurrentMonotonicMicros() { ++ int64_t ticks = GetCurrentMonotonicTicks(); ++ ASSERT(GetCurrentMonotonicFrequency() == kNanosecondsPerSecond); ++ return ticks / kNanosecondsPerMicrosecond; ++} ++ ++int64_t OS::GetCurrentThreadCPUMicros() { ++ struct timespec ts; ++ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) { ++ UNREACHABLE(); ++ return -1; ++ } ++ int64_t result = ts.tv_sec; ++ result *= kMicrosecondsPerSecond; ++ result += (ts.tv_nsec / kNanosecondsPerMicrosecond); ++ return result; ++} ++ ++int64_t OS::GetCurrentMonotonicMicrosForTimeline() { ++#if defined(SUPPORT_TIMELINE) ++ if (Timeline::recorder_discards_clock_values()) return -1; ++ return GetCurrentMonotonicMicros(); ++#else ++ return -1; ++#endif ++} ++ ++int64_t OS::GetCurrentThreadCPUMicrosForTimeline() { ++#if defined(SUPPORT_TIMELINE) ++ if (Timeline::recorder_discards_clock_values()) return -1; ++ return OS::GetCurrentThreadCPUMicros(); ++#else ++ return -1; ++#endif ++} ++// TODO(5411554): May need to hoist these architecture dependent code ++// into a architecture specific file e.g: os_ia32_linux.cc ++intptr_t OS::ActivationFrameAlignment() { ++#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \ ++ defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_RISCV32) || \ ++ defined(TARGET_ARCH_RISCV64) ++ const int kMinimumAlignment = 16; ++#elif defined(TARGET_ARCH_ARM) ++ const int kMinimumAlignment = 8; ++#else ++#error Unsupported architecture. ++#endif ++ intptr_t alignment = kMinimumAlignment; ++ // TODO(5411554): Allow overriding default stack alignment for ++ // testing purposes. ++ // Flags::DebugIsInt("stackalign", &alignment); ++ ASSERT(Utils::IsPowerOfTwo(alignment)); ++ ASSERT(alignment >= kMinimumAlignment); ++ return alignment; ++} ++ ++int OS::NumberOfAvailableProcessors() { ++ return sysconf(_SC_NPROCESSORS_ONLN); ++} ++ ++void OS::Sleep(int64_t millis) { ++ int64_t micros = millis * kMicrosecondsPerMillisecond; ++ SleepMicros(micros); ++} ++ ++void OS::SleepMicros(int64_t micros) { ++ struct timespec req; // requested. ++ struct timespec rem; // remainder. ++ int64_t seconds = micros / kMicrosecondsPerSecond; ++ micros = micros - seconds * kMicrosecondsPerSecond; ++ int64_t nanos = micros * kNanosecondsPerMicrosecond; ++ req.tv_sec = seconds; ++ req.tv_nsec = nanos; ++ while (true) { ++ int r = nanosleep(&req, &rem); ++ if (r == 0) { ++ break; ++ } ++ // We should only ever see an interrupt error. ++ ASSERT(errno == EINTR); ++ // Copy remainder into requested and repeat. ++ req = rem; ++ } ++} ++ ++// TODO(regis): Function called only from the simulator. ++void OS::DebugBreak() { ++ __builtin_trap(); ++} ++ ++DART_NOINLINE uintptr_t OS::GetProgramCounter() { ++ return reinterpret_cast( ++ __builtin_extract_return_addr(__builtin_return_address(0))); ++} ++ ++void OS::Print(const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ VFPrint(stdout, format, args); ++ va_end(args); ++} ++ ++void OS::VFPrint(FILE* stream, const char* format, va_list args) { ++ vfprintf(stream, format, args); ++ fflush(stream); ++} ++ ++char* OS::SCreate(Zone* zone, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ char* buffer = VSCreate(zone, format, args); ++ va_end(args); ++ return buffer; ++} ++ ++char* OS::VSCreate(Zone* zone, const char* format, va_list args) { ++ // Measure. ++ va_list measure_args; ++ va_copy(measure_args, args); ++ intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args); ++ va_end(measure_args); ++ ++ char* buffer; ++ if (zone != nullptr) { ++ buffer = zone->Alloc(len + 1); ++ } else { ++ buffer = reinterpret_cast(malloc(len + 1)); ++ } ++ ASSERT(buffer != NULL); ++ ++ // Print. ++ va_list print_args; ++ va_copy(print_args, args); ++ Utils::VSNPrint(buffer, len + 1, format, print_args); ++ va_end(print_args); ++ return buffer; ++} ++ ++bool OS::StringToInt64(const char* str, int64_t* value) { ++ ASSERT(str != NULL && strlen(str) > 0 && value != NULL); ++ int32_t base = 10; ++ char* endptr; ++ int i = 0; ++ if (str[0] == '-') { ++ i = 1; ++ } else if (str[0] == '+') { ++ i = 1; ++ } ++ if ((str[i] == '0') && (str[i + 1] == 'x' || str[i + 1] == 'X') && ++ (str[i + 2] != '\0')) { ++ base = 16; ++ } ++ errno = 0; ++ if (base == 16) { ++ // Unsigned 64-bit hexadecimal integer literals are allowed but ++ // immediately interpreted as signed 64-bit integers. ++ *value = static_cast(strtoull(str, &endptr, base)); ++ } else { ++ *value = strtoll(str, &endptr, base); ++ } ++ return ((errno == 0) && (endptr != str) && (*endptr == 0)); ++} ++ ++void OS::RegisterCodeObservers() { ++#ifndef PRODUCT ++ if (FLAG_generate_perf_events_symbols) { ++ CodeObservers::Register(new PerfCodeObserver); ++ } ++ ++ if (FLAG_generate_perf_jitdump) { ++ CodeObservers::Register(new JitDumpCodeObserver); ++ } ++#endif // !PRODUCT ++} ++ ++void OS::PrintErr(const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ VFPrint(stderr, format, args); ++ va_end(args); ++} ++ ++void OS::Init() {} ++ ++void OS::Cleanup() {} ++ ++void OS::PrepareToAbort() {} ++ ++void OS::Abort() { ++ PrepareToAbort(); ++ abort(); ++} ++ ++void OS::Exit(int code) { ++ exit(code); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/os_thread_ohos.cc b/runtime/vm/os_thread_ohos.cc +new file mode 100644 +index 00000000000..e92a8c26794 +--- /dev/null ++++ b/runtime/vm/os_thread_ohos.cc +@@ -0,0 +1,500 @@ ++// 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 "platform/globals.h" // NOLINT ++ ++#if defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) ++ ++#include "vm/os_thread.h" ++ ++#include // NOLINT ++#include ++#include // NOLINT ++#include // NOLINT ++#include // NOLINT ++ ++#include "platform/address_sanitizer.h" ++#include "platform/assert.h" ++#include "platform/safe_stack.h" ++#include "platform/signal_blocker.h" ++#include "platform/utils.h" ++ ++#include "vm/flags.h" ++ ++namespace dart { ++ ++DEFINE_FLAG(int, ++ worker_thread_priority, ++ kMinInt, ++ "The thread priority the VM should use for new worker threads."); ++ ++#define VALIDATE_PTHREAD_RESULT(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL2("pthread error: %d (%s)", result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++ ++// Variation of VALIDATE_PTHREAD_RESULT for named objects. ++#if defined(PRODUCT) ++#define VALIDATE_PTHREAD_RESULT_NAMED(result) VALIDATE_PTHREAD_RESULT(result) ++#else ++#define VALIDATE_PTHREAD_RESULT_NAMED(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ FATAL3("[%s] pthread error: %d (%s)", name_, result, \ ++ Utils::StrError(result, error_buf, kBufferSize)); \ ++ } ++#endif ++ ++#if defined(DEBUG) ++#define ASSERT_PTHREAD_SUCCESS(result) VALIDATE_PTHREAD_RESULT(result) ++#else ++// NOTE: This (currently) expands to a no-op. ++#define ASSERT_PTHREAD_SUCCESS(result) ASSERT(result == 0) ++#endif ++ ++#ifdef DEBUG ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) { \ ++ const int kBufferSize = 1024; \ ++ char error_buf[kBufferSize]; \ ++ fprintf(stderr, "%s:%d: pthread error: %d (%s)\n", __FILE__, __LINE__, \ ++ result, Utils::StrError(result, error_buf, kBufferSize)); \ ++ return result; \ ++ } ++#else ++#define RETURN_ON_PTHREAD_FAILURE(result) \ ++ if (result != 0) return result; ++#endif ++ ++static void ComputeTimeSpecMicros(struct timespec* ts, int64_t micros) { ++ int64_t secs = micros / kMicrosecondsPerSecond; ++ int64_t nanos = ++ (micros - (secs * kMicrosecondsPerSecond)) * kNanosecondsPerMicrosecond; ++ int result = clock_gettime(CLOCK_MONOTONIC, ts); ++ ASSERT(result == 0); ++ ts->tv_sec += secs; ++ ts->tv_nsec += nanos; ++ if (ts->tv_nsec >= kNanosecondsPerSecond) { ++ ts->tv_sec += 1; ++ ts->tv_nsec -= kNanosecondsPerSecond; ++ } ++} ++ ++class ThreadStartData { ++ public: ++ ThreadStartData(const char* name, ++ OSThread::ThreadStartFunction function, ++ uword parameter) ++ : name_(name), function_(function), parameter_(parameter) {} ++ ++ const char* name() const { return name_; } ++ OSThread::ThreadStartFunction function() const { return function_; } ++ uword parameter() const { return parameter_; } ++ ++ private: ++ const char* name_; ++ OSThread::ThreadStartFunction function_; ++ uword parameter_; ++ ++ DISALLOW_COPY_AND_ASSIGN(ThreadStartData); ++}; ++ ++// TODO(bkonyi): remove this call once the prebuilt SDK is updated. ++// Spawned threads inherit their spawner's signal mask. We sometimes spawn ++// threads for running Dart code from a thread that is blocking SIGPROF. ++// This function explicitly unblocks SIGPROF so the profiler continues to ++// sample this thread. ++static void UnblockSIGPROF() { ++ sigset_t set; ++ sigemptyset(&set); ++ sigaddset(&set, SIGPROF); ++ int r = pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++ USE(r); ++ ASSERT(r == 0); ++ ASSERT(!CHECK_IS_BLOCKING(SIGPROF)); ++} ++ ++// Dispatch to the thread start function provided by the caller. This trampoline ++// is used to ensure that the thread is properly destroyed if the thread just ++// exits. ++static void* ThreadStart(void* data_ptr) { ++ if (FLAG_worker_thread_priority != kMinInt) { ++ if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), ++ FLAG_worker_thread_priority) == -1) { ++ FATAL2("Setting thread priority to %d failed: errno = %d\n", ++ FLAG_worker_thread_priority, errno); ++ } ++ } ++ ++ ThreadStartData* data = reinterpret_cast(data_ptr); ++ ++ const char* name = data->name(); ++ OSThread::ThreadStartFunction function = data->function(); ++ uword parameter = data->parameter(); ++ delete data; ++ ++ // Set the thread name. There is 16 bytes limit on the name (including \0). ++ // pthread_setname_np ignores names that are too long rather than truncating. ++ char truncated_name[16]; ++ snprintf(truncated_name, ARRAY_SIZE(truncated_name), "%s", name); ++ pthread_setname_np(pthread_self(), truncated_name); ++ ++ // Create new OSThread object and set as TLS for new thread. ++ OSThread* thread = OSThread::CreateOSThread(); ++ if (thread != NULL) { ++ OSThread::SetCurrent(thread); ++ thread->set_name(name); ++ UnblockSIGPROF(); ++ // Call the supplied thread start function handing it its parameters. ++ function(parameter); ++ } ++ ++ return NULL; ++} ++ ++int OSThread::Start(const char* name, ++ ThreadStartFunction function, ++ uword parameter) { ++ pthread_attr_t attr; ++ int result = pthread_attr_init(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_setstacksize(&attr, OSThread::GetMaxStackSize()); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ ThreadStartData* data = new ThreadStartData(name, function, parameter); ++ ++ pthread_t tid; ++ result = pthread_create(&tid, &attr, ThreadStart, data); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ result = pthread_attr_destroy(&attr); ++ RETURN_ON_PTHREAD_FAILURE(result); ++ ++ return 0; ++} ++ ++const ThreadId OSThread::kInvalidThreadId = static_cast(0); ++const ThreadJoinId OSThread::kInvalidThreadJoinId = ++ static_cast(0); ++ ++ThreadLocalKey OSThread::CreateThreadLocal(ThreadDestructor destructor) { ++ pthread_key_t key = kUnsetThreadLocalKey; ++ int result = pthread_key_create(&key, destructor); ++ VALIDATE_PTHREAD_RESULT(result); ++ ASSERT(key != kUnsetThreadLocalKey); ++ return key; ++} ++ ++void OSThread::DeleteThreadLocal(ThreadLocalKey key) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ int result = pthread_key_delete(key); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void OSThread::SetThreadLocal(ThreadLocalKey key, uword value) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ int result = pthread_setspecific(key, reinterpret_cast(value)); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++intptr_t OSThread::GetMaxStackSize() { ++ const int kStackSize = (128 * kWordSize * KB); ++ return kStackSize; ++} ++ ++ThreadId OSThread::GetCurrentThreadId() { ++ return pthread_self(); ++} ++ ++#ifdef SUPPORT_TIMELINE ++ThreadId OSThread::GetCurrentThreadTraceId() { ++ return syscall(__NR_gettid); ++} ++#endif // SUPPORT_TIMELINE ++ ++ThreadJoinId OSThread::GetCurrentThreadJoinId(OSThread* thread) { ++ ASSERT(thread != NULL); ++ // Make sure we're filling in the join id for the current thread. ++ ASSERT(thread->id() == GetCurrentThreadId()); ++ // Make sure the join_id_ hasn't been set, yet. ++ DEBUG_ASSERT(thread->join_id_ == kInvalidThreadJoinId); ++ pthread_t id = pthread_self(); ++#if defined(DEBUG) ++ thread->join_id_ = id; ++#endif ++ return id; ++} ++ ++void OSThread::Join(ThreadJoinId id) { ++ int result = pthread_join(id, NULL); ++ ASSERT(result == 0); ++} ++ ++intptr_t OSThread::ThreadIdToIntPtr(ThreadId id) { ++ ASSERT(sizeof(id) == sizeof(intptr_t)); ++ return static_cast(id); ++} ++ ++ThreadId OSThread::ThreadIdFromIntPtr(intptr_t id) { ++ return static_cast(id); ++} ++ ++bool OSThread::Compare(ThreadId a, ThreadId b) { ++ return pthread_equal(a, b) != 0; ++} ++ ++bool OSThread::GetCurrentStackBounds(uword* lower, uword* upper) { ++ pthread_attr_t attr; ++ // May fail on the main thread. ++ if (pthread_getattr_np(pthread_self(), &attr) != 0) { ++ return false; ++ } ++ ++ void* base; ++ size_t size; ++ int error = pthread_attr_getstack(&attr, &base, &size); ++ pthread_attr_destroy(&attr); ++ if (error != 0) { ++ return false; ++ } ++ ++ *lower = reinterpret_cast(base); ++ *upper = *lower + size; ++ return true; ++} ++ ++#if defined(USING_SAFE_STACK) ++NO_SANITIZE_ADDRESS ++NO_SANITIZE_SAFE_STACK ++uword OSThread::GetCurrentSafestackPointer() { ++#error "SAFE_STACK is unsupported on this platform" ++ return 0; ++} ++ ++NO_SANITIZE_ADDRESS ++NO_SANITIZE_SAFE_STACK ++void OSThread::SetCurrentSafestackPointer(uword ssp) { ++#error "SAFE_STACK is unsupported on this platform" ++} ++#endif ++ ++Mutex::Mutex(NOT_IN_PRODUCT(const char* name)) ++#if !defined(PRODUCT) ++ : name_(name) ++#endif ++{ ++ pthread_mutexattr_t attr; ++ int result = pthread_mutexattr_init(&attr); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &attr); ++ // Verify that creating a pthread_mutex succeeded. ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++ result = pthread_mutexattr_destroy(&attr); ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++} ++ ++Mutex::~Mutex() { ++ int result = pthread_mutex_destroy(data_.mutex()); ++ // Verify that the pthread_mutex was destroyed. ++ VALIDATE_PTHREAD_RESULT_NAMED(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++#endif // defined(DEBUG) ++} ++ ++void Mutex::Lock() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ // Specifically check for dead lock to help debugging. ++ ASSERT(result != EDEADLK); ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++} ++ ++bool Mutex::TryLock() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++ return true; ++} ++ ++void Mutex::Unlock() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ int result = pthread_mutex_unlock(data_.mutex()); ++ // Specifically check for wrong thread unlocking to aid debugging. ++ ASSERT(result != EPERM); ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++} ++ ++Monitor::Monitor() { ++ pthread_mutexattr_t mutex_attr; ++ int result = pthread_mutexattr_init(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ result = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); ++ VALIDATE_PTHREAD_RESULT(result); ++#endif // defined(DEBUG) ++ ++ result = pthread_mutex_init(data_.mutex(), &mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_mutexattr_destroy(&mutex_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ pthread_condattr_t cond_attr; ++ result = pthread_condattr_init(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_init(data_.cond(), &cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_condattr_destroy(&cond_attr); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++} ++ ++Monitor::~Monitor() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++#endif // defined(DEBUG) ++ ++ int result = pthread_mutex_destroy(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++ result = pthread_cond_destroy(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++bool Monitor::TryEnter() { ++ int result = pthread_mutex_trylock(data_.mutex()); ++ // Return false if the lock is busy and locking failed. ++ if (result == EBUSY) { ++ return false; ++ } ++ ASSERT_PTHREAD_SUCCESS(result); // Verify no other errors. ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++ return true; ++} ++ ++void Monitor::Enter() { ++ int result = pthread_mutex_lock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++#endif // defined(DEBUG) ++} ++ ++void Monitor::Exit() { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ ++ int result = pthread_mutex_unlock(data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++Monitor::WaitResult Monitor::Wait(int64_t millis) { ++ Monitor::WaitResult retval = WaitMicros(millis * kMicrosecondsPerMillisecond); ++ return retval; ++} ++ ++Monitor::WaitResult Monitor::WaitMicros(int64_t micros) { ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ ThreadId saved_owner = owner_; ++ owner_ = OSThread::kInvalidThreadId; ++#endif // defined(DEBUG) ++ ++ Monitor::WaitResult retval = kNotified; ++ if (micros == kNoTimeout) { ++ // Wait forever. ++ int result = pthread_cond_wait(data_.cond(), data_.mutex()); ++ VALIDATE_PTHREAD_RESULT(result); ++ } else { ++ struct timespec ts; ++ ComputeTimeSpecMicros(&ts, micros); ++ int result = pthread_cond_timedwait(data_.cond(), data_.mutex(), &ts); ++ ASSERT((result == 0) || (result == ETIMEDOUT)); ++ if (result == ETIMEDOUT) { ++ retval = kTimedOut; ++ } ++ } ++ ++#if defined(DEBUG) ++ // When running with assertions enabled we track the owner. ++ ASSERT(owner_ == OSThread::kInvalidThreadId); ++ owner_ = OSThread::GetCurrentThreadId(); ++ ASSERT(owner_ == saved_owner); ++#endif // defined(DEBUG) ++ return retval; ++} ++ ++void Monitor::Notify() { ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ int result = pthread_cond_signal(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++void Monitor::NotifyAll() { ++ // When running with assertions enabled we track the owner. ++ ASSERT(IsOwnedByCurrentThread()); ++ int result = pthread_cond_broadcast(data_.cond()); ++ VALIDATE_PTHREAD_RESULT(result); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(DART_USE_ABSL) +diff --git a/runtime/vm/os_thread_ohos.h b/runtime/vm/os_thread_ohos.h +new file mode 100644 +index 00000000000..aaf490f9f77 +--- /dev/null ++++ b/runtime/vm/os_thread_ohos.h +@@ -0,0 +1,76 @@ ++// 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. ++ ++#ifndef RUNTIME_VM_OS_THREAD_OHOS_H_ ++#define RUNTIME_VM_OS_THREAD_OHOS_H_ ++ ++#if !defined(RUNTIME_VM_OS_THREAD_H_) ++#error Do not include os_thread_linux.h directly; use os_thread.h instead. ++#endif ++ ++#include ++ ++#include "platform/assert.h" ++#include "platform/globals.h" ++ ++namespace dart { ++ ++typedef pthread_key_t ThreadLocalKey; ++typedef pthread_t ThreadId; ++typedef pthread_t ThreadJoinId; ++ ++static const ThreadLocalKey kUnsetThreadLocalKey = ++ static_cast(-1); ++ ++class ThreadInlineImpl { ++ private: ++ ThreadInlineImpl() {} ++ ~ThreadInlineImpl() {} ++ ++ static uword GetThreadLocal(ThreadLocalKey key) { ++ ASSERT(key != kUnsetThreadLocalKey); ++ return reinterpret_cast(pthread_getspecific(key)); ++ } ++ ++ friend class OSThread; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(ThreadInlineImpl); ++}; ++ ++class MutexData { ++ private: ++ MutexData() {} ++ ~MutexData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ ++ pthread_mutex_t mutex_; ++ ++ friend class Mutex; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MutexData); ++}; ++ ++class MonitorData { ++ private: ++ MonitorData() {} ++ ~MonitorData() {} ++ ++ pthread_mutex_t* mutex() { return &mutex_; } ++ pthread_cond_t* cond() { return &cond_; } ++ ++ pthread_mutex_t mutex_; ++ pthread_cond_t cond_; ++ ++ friend class Monitor; ++ ++ DISALLOW_ALLOCATION(); ++ DISALLOW_COPY_AND_ASSIGN(MonitorData); ++}; ++ ++} // namespace dart ++ ++#endif // RUNTIME_VM_OS_THREAD_OHOS_H_ +diff --git a/runtime/vm/signal_handler_ohos.cc b/runtime/vm/signal_handler_ohos.cc +new file mode 100644 +index 00000000000..c64c9984606 +--- /dev/null ++++ b/runtime/vm/signal_handler_ohos.cc +@@ -0,0 +1,136 @@ ++// 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 "vm/globals.h" ++#include "vm/instructions.h" ++#include "vm/signal_handler.h" ++#include "vm/simulator.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++namespace dart { ++ ++uintptr_t SignalHandler::GetProgramCounter(const mcontext_t& mcontext) { ++ uintptr_t pc = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ pc = static_cast(mcontext.gregs[REG_EIP]); ++#elif defined(HOST_ARCH_X64) ++ pc = static_cast(mcontext.gregs[REG_RIP]); ++#elif defined(HOST_ARCH_ARM) ++ pc = static_cast(mcontext.arm_pc); ++#elif defined(HOST_ARCH_ARM64) ++ pc = static_cast(mcontext.pc); ++#elif defined(HOST_ARCH_RISCV32) ++ pc = static_cast(mcontext.__gregs[REG_PC]); ++#elif defined(HOST_ARCH_RISCV64) ++ pc = static_cast(mcontext.__gregs[REG_PC]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return pc; ++} ++ ++uintptr_t SignalHandler::GetFramePointer(const mcontext_t& mcontext) { ++ uintptr_t fp = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ fp = static_cast(mcontext.gregs[REG_EBP]); ++#elif defined(HOST_ARCH_X64) ++ fp = static_cast(mcontext.gregs[REG_RBP]); ++#elif defined(HOST_ARCH_ARM) ++ // B1.3.3 Program Status Registers (PSRs) ++ if ((mcontext.arm_cpsr & (1 << 5)) != 0) { ++ // Thumb mode. ++ fp = static_cast(mcontext.arm_r7); ++ } else { ++ // ARM mode. ++ fp = static_cast(mcontext.arm_fp); ++ } ++#elif defined(HOST_ARCH_ARM64) ++ fp = static_cast(mcontext.regs[29]); ++#elif defined(HOST_ARCH_RISCV32) ++ fp = static_cast(mcontext.__gregs[REG_S0]); ++#elif defined(HOST_ARCH_RISCV64) ++ fp = static_cast(mcontext.__gregs[REG_S0]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ ++ return fp; ++} ++ ++uintptr_t SignalHandler::GetCStackPointer(const mcontext_t& mcontext) { ++ uintptr_t sp = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ sp = static_cast(mcontext.gregs[REG_ESP]); ++#elif defined(HOST_ARCH_X64) ++ sp = static_cast(mcontext.gregs[REG_RSP]); ++#elif defined(HOST_ARCH_ARM) ++ sp = static_cast(mcontext.arm_sp); ++#elif defined(HOST_ARCH_ARM64) ++ sp = static_cast(mcontext.sp); ++#elif defined(HOST_ARCH_RISCV32) ++ sp = static_cast(mcontext.__gregs[REG_SP]); ++#elif defined(HOST_ARCH_RISCV64) ++ sp = static_cast(mcontext.__gregs[REG_SP]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return sp; ++} ++ ++uintptr_t SignalHandler::GetDartStackPointer(const mcontext_t& mcontext) { ++#if defined(TARGET_ARCH_ARM64) && !defined(USING_SIMULATOR) ++ return static_cast(mcontext.regs[SPREG]); ++#else ++ return GetCStackPointer(mcontext); ++#endif ++} ++ ++uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { ++ uintptr_t lr = 0; ++ ++#if defined(HOST_ARCH_IA32) ++ lr = 0; ++#elif defined(HOST_ARCH_X64) ++ lr = 0; ++#elif defined(HOST_ARCH_ARM) ++ lr = static_cast(mcontext.arm_lr); ++#elif defined(HOST_ARCH_ARM64) ++ lr = static_cast(mcontext.regs[30]); ++#elif defined(HOST_ARCH_RISCV32) ++ lr = static_cast(mcontext.__gregs[REG_RA]); ++#elif defined(HOST_ARCH_RISCV64) ++ lr = static_cast(mcontext.__gregs[REG_RA]); ++#else ++#error Unsupported architecture. ++#endif // HOST_ARCH_... ++ return lr; ++} ++ ++void SignalHandler::Install(SignalAction action) { ++ struct sigaction act = {}; ++ act.sa_handler = NULL; ++ act.sa_sigaction = action; ++ sigemptyset(&act.sa_mask); ++ act.sa_flags = SA_RESTART | SA_SIGINFO; ++ int r = sigaction(SIGPROF, &act, NULL); ++ ASSERT(r == 0); ++} ++ ++void SignalHandler::Remove() { ++ // Ignore future SIGPROF signals because by default SIGPROF will terminate ++ // the process and we may have some signals in flight. ++ struct sigaction act = {}; ++ act.sa_handler = SIG_IGN; ++ sigemptyset(&act.sa_mask); ++ act.sa_flags = 0; ++ int r = sigaction(SIGPROF, &act, NULL); ++ ASSERT(r == 0); ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/thread_interrupter_ohos.cc b/runtime/vm/thread_interrupter_ohos.cc +new file mode 100644 +index 00000000000..94e72d58209 +--- /dev/null ++++ b/runtime/vm/thread_interrupter_ohos.cc +@@ -0,0 +1,72 @@ ++// 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 "platform/globals.h" ++#if defined(DART_HOST_OS_OHOS) ++ ++#include // NOLINT ++ ++#include "vm/flags.h" ++#include "vm/os.h" ++#include "vm/profiler.h" ++#include "vm/signal_handler.h" ++#include "vm/thread_interrupter.h" ++ ++namespace dart { ++ ++#ifndef PRODUCT ++ ++DECLARE_FLAG(bool, trace_thread_interrupter); ++ ++class ThreadInterrupterOhos : public AllStatic { ++ public: ++ static void ThreadInterruptSignalHandler(int signal, ++ siginfo_t* info, ++ void* context_) { ++ if (signal != SIGPROF) { ++ return; ++ } ++ Thread* thread = Thread::Current(); ++ if (thread == NULL) { ++ return; ++ } ++ ThreadInterrupter::SampleBufferWriterScope scope; ++ if (!scope.CanSample()) { ++ return; ++ } ++ // Extract thread state. ++ ucontext_t* context = reinterpret_cast(context_); ++ mcontext_t mcontext = context->uc_mcontext; ++ InterruptedThreadState its; ++ its.pc = SignalHandler::GetProgramCounter(mcontext); ++ its.fp = SignalHandler::GetFramePointer(mcontext); ++ its.csp = SignalHandler::GetCStackPointer(mcontext); ++ its.dsp = SignalHandler::GetDartStackPointer(mcontext); ++ its.lr = SignalHandler::GetLinkRegister(mcontext); ++ Profiler::SampleThread(thread, its); ++ } ++}; ++ ++void ThreadInterrupter::InterruptThread(OSThread* thread) { ++ if (FLAG_trace_thread_interrupter) { ++ OS::PrintErr("ThreadInterrupter interrupting %p\n", ++ reinterpret_cast(thread->id())); ++ } ++ int result = pthread_kill(thread->id(), SIGPROF); ++ ASSERT((result == 0) || (result == ESRCH)); ++} ++ ++void ThreadInterrupter::InstallSignalHandler() { ++ SignalHandler::Install(&ThreadInterrupterOhos::ThreadInterruptSignalHandler); ++} ++ ++void ThreadInterrupter::RemoveSignalHandler() { ++ SignalHandler::Remove(); ++} ++ ++#endif // !PRODUCT ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) +diff --git a/runtime/vm/timeline_ohos.cc b/runtime/vm/timeline_ohos.cc +new file mode 100644 +index 00000000000..27f2e225e34 +--- /dev/null ++++ b/runtime/vm/timeline_ohos.cc +@@ -0,0 +1,120 @@ ++// 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 "vm/globals.h" ++#if defined(DART_HOST_OS_OHOS) && defined(SUPPORT_TIMELINE) ++ ++#include ++#include ++#include ++ ++#include "platform/atomic.h" ++#include "platform/signal_blocker.h" ++#include "vm/isolate.h" ++#include "vm/json_stream.h" ++#include "vm/lockers.h" ++#include "vm/log.h" ++#include "vm/object.h" ++#include "vm/service_event.h" ++#include "vm/thread.h" ++#include "vm/timeline.h" ++ ++namespace dart { ++ ++DECLARE_FLAG(bool, trace_timeline); ++ ++static int OpenTraceFD() { ++ const char* kSystraceDebugPath = "/sys/kernel/debug/tracing/trace_marker"; ++ const char* kSystracePath = "/sys/kernel/tracing/trace_marker"; ++ ++ int fd = TEMP_FAILURE_RETRY(::open(kSystracePath, O_WRONLY)); ++ if (fd < 0) { ++ fd = TEMP_FAILURE_RETRY(::open(kSystraceDebugPath, O_WRONLY)); ++ } ++ ++ if (fd < 0 && FLAG_trace_timeline) { ++ OS::PrintErr("TimelineEventSystraceRecorder: Could not open `%s` or `%s`\n", ++ kSystraceDebugPath, kSystracePath); ++ } ++ return fd; ++} ++ ++TimelineEventSystraceRecorder::TimelineEventSystraceRecorder() ++ : TimelineEventPlatformRecorder(), systrace_fd_(OpenTraceFD()) { ++ Timeline::set_recorder_discards_clock_values(true); ++} ++ ++TimelineEventSystraceRecorder::~TimelineEventSystraceRecorder() { ++ if (systrace_fd_ >= 0) { ++ close(systrace_fd_); ++ } ++} ++ ++intptr_t TimelineEventSystraceRecorder::PrintSystrace(TimelineEvent* event, ++ char* buffer, ++ intptr_t buffer_size) { ++ ASSERT(buffer != NULL); ++ ASSERT(buffer_size > 0); ++ buffer[0] = '\0'; ++ intptr_t length = 0; ++ int64_t pid = OS::ProcessId(); ++ switch (event->event_type()) { ++ case TimelineEvent::kBegin: { ++ length = Utils::SNPrint(buffer, buffer_size, "B|%" Pd64 "|%s", pid, ++ event->label()); ++ break; ++ } ++ case TimelineEvent::kEnd: { ++ length = Utils::SNPrint(buffer, buffer_size, "E"); ++ break; ++ } ++ case TimelineEvent::kCounter: { ++ if (event->arguments_length() > 0) { ++ // We only report the first counter value. ++ length = Utils::SNPrint(buffer, buffer_size, "C|%" Pd64 "|%s|%s", pid, ++ event->label(), event->arguments()[0].value); ++ } ++ break; ++ } ++ case TimelineEvent::kAsyncBegin: { ++ length = Utils::SNPrint(buffer, buffer_size, "S|%" Pd64 "|%s|%" Pd64 "", ++ pid, event->label(), event->Id()); ++ break; ++ } ++ case TimelineEvent::kAsyncEnd: { ++ length = Utils::SNPrint(buffer, buffer_size, "F|%" Pd64 "|%s|%" Pd64 "", ++ pid, event->label(), event->Id()); ++ break; ++ } ++ default: ++ // Ignore event types that we cannot serialize to the Systrace format. ++ break; ++ } ++ return length; ++} ++ ++void TimelineEventSystraceRecorder::OnEvent(TimelineEvent* event) { ++ if (event == NULL) { ++ return; ++ } ++ if (systrace_fd_ < 0) { ++ return; ++ } ++ ++ // Serialize to the systrace format. ++ const intptr_t kBufferLength = 1024; ++ char buffer[kBufferLength]; ++ const intptr_t event_length = PrintSystrace(event, &buffer[0], kBufferLength); ++ if (event_length > 0) { ++ ssize_t result; ++ // Repeatedly attempt the write while we are being interrupted. ++ do { ++ result = write(systrace_fd_, buffer, event_length); ++ } while ((result == -1L) && (errno == EINTR)); ++ } ++} ++ ++} // namespace dart ++ ++#endif // defined(DART_HOST_OS_OHOS) && !defined(PRODUCT) +diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni +index 4413fdeecb2..41168412aec 100644 +--- a/runtime/vm/vm_sources.gni ++++ b/runtime/vm/vm_sources.gni +@@ -69,6 +69,7 @@ vm_sources = [ + "cpuinfo_android.cc", + "cpuinfo_fuchsia.cc", + "cpuinfo_linux.cc", ++ "cpuinfo_ohos.cc", + "cpuinfo_macos.cc", + "cpuinfo_win.cc", + "dart.cc", +@@ -190,6 +191,7 @@ vm_sources = [ + "native_symbol_android.cc", + "native_symbol_fuchsia.cc", + "native_symbol_linux.cc", ++ "native_symbol_ohos.cc", + "native_symbol_macos.cc", + "native_symbol_win.cc", + "object.cc", +@@ -209,6 +211,7 @@ vm_sources = [ + "os_android.cc", + "os_fuchsia.cc", + "os_linux.cc", ++ "os_ohos.cc", + "os_macos.cc", + "os_thread.cc", + "os_thread.h", +@@ -224,6 +227,8 @@ vm_sources = [ + "os_thread_macos.h", + "os_thread_win.cc", + "os_thread_win.h", ++ "os_thread_ohos.cc", ++ "os_thread_ohos.h", + "os_win.cc", + "parser.cc", + "parser.h", +@@ -292,6 +297,7 @@ vm_sources = [ + "signal_handler_android.cc", + "signal_handler_fuchsia.cc", + "signal_handler_linux.cc", ++ "signal_handler_ohos.cc", + "signal_handler_macos.cc", + "signal_handler_win.cc", + "simulator.h", +@@ -331,6 +337,7 @@ vm_sources = [ + "thread_interrupter_android.cc", + "thread_interrupter_fuchsia.cc", + "thread_interrupter_linux.cc", ++ "thread_interrupter_ohos.cc", + "thread_interrupter_macos.cc", + "thread_interrupter_win.cc", + "thread_pool.cc", +@@ -346,6 +353,7 @@ vm_sources = [ + "timeline_android.cc", + "timeline_fuchsia.cc", + "timeline_linux.cc", ++ "timeline_ohos.cc", + "timeline_macos.cc", + "timer.cc", + "timer.h", diff --git a/sdk/lib/_internal/vm/bin/vmservice_server.dart b/sdk/lib/_internal/vm/bin/vmservice_server.dart index b7e5be3eab5..7f318bb176d 100644 --- a/sdk/lib/_internal/vm/bin/vmservice_server.dart