mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:43:57 +00:00
3c298dbca5
TEST=build Change-Id: I18fc7cfe725dc978d4b23de6191e455ac7cd75e5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/293800 Reviewed-by: Siva Annamalai <asiva@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
396 lines
10 KiB
C++
396 lines
10 KiB
C++
// 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_PROCESS_H_
|
|
#define RUNTIME_BIN_PROCESS_H_
|
|
|
|
#include <errno.h>
|
|
|
|
#include "bin/builtin.h"
|
|
#include "bin/io_buffer.h"
|
|
#include "bin/lockers.h"
|
|
#include "bin/namespace.h"
|
|
#include "bin/thread.h"
|
|
#include "platform/globals.h"
|
|
#if !defined(DART_HOST_OS_WINDOWS)
|
|
#include "platform/signal_blocker.h"
|
|
#endif
|
|
#include "platform/utils.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
class ProcessResult {
|
|
public:
|
|
ProcessResult() : exit_code_(0) {}
|
|
|
|
void set_stdout_data(Dart_Handle stdout_data) { stdout_data_ = stdout_data; }
|
|
void set_stderr_data(Dart_Handle stderr_data) { stderr_data_ = stderr_data; }
|
|
|
|
void set_exit_code(intptr_t exit_code) { exit_code_ = exit_code; }
|
|
|
|
Dart_Handle stdout_data() { return stdout_data_; }
|
|
Dart_Handle stderr_data() { return stderr_data_; }
|
|
intptr_t exit_code() { return exit_code_; }
|
|
|
|
private:
|
|
Dart_Handle stdout_data_;
|
|
Dart_Handle stderr_data_;
|
|
intptr_t exit_code_;
|
|
|
|
DISALLOW_ALLOCATION();
|
|
};
|
|
|
|
// To be kept in sync with ProcessSignal consts in sdk/lib/io/process.dart
|
|
// Note that this map is as on Linux.
|
|
enum ProcessSignals {
|
|
kSighup = 1,
|
|
kSigint = 2,
|
|
kSigquit = 3,
|
|
kSigill = 4,
|
|
kSigtrap = 5,
|
|
kSigabrt = 6,
|
|
kSigbus = 7,
|
|
kSigfpe = 8,
|
|
kSigkill = 9,
|
|
kSigusr1 = 10,
|
|
kSigsegv = 11,
|
|
kSigusr2 = 12,
|
|
kSigpipe = 13,
|
|
kSigalrm = 14,
|
|
kSigterm = 15,
|
|
kSigchld = 17,
|
|
kSigcont = 18,
|
|
kSigstop = 19,
|
|
kSigtstp = 20,
|
|
kSigttin = 21,
|
|
kSigttou = 22,
|
|
kSigurg = 23,
|
|
kSigxcpu = 24,
|
|
kSigxfsz = 25,
|
|
kSigvtalrm = 26,
|
|
kSigprof = 27,
|
|
kSigwinch = 28,
|
|
kSigpoll = 29,
|
|
kSigsys = 31,
|
|
kLastSignal = kSigsys,
|
|
};
|
|
|
|
// To be kept in sync with ProcessStartMode consts in sdk/lib/io/process.dart.
|
|
enum ProcessStartMode {
|
|
kNormal = 0,
|
|
kInheritStdio = 1,
|
|
kDetached = 2,
|
|
kDetachedWithStdio = 3,
|
|
};
|
|
|
|
class Process {
|
|
public:
|
|
static void Init();
|
|
static void Cleanup();
|
|
|
|
// Start a new process providing access to stdin, stdout, stderr and
|
|
// process exit streams.
|
|
static int 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_handler,
|
|
char** os_error_message);
|
|
|
|
static bool Wait(intptr_t id,
|
|
intptr_t in,
|
|
intptr_t out,
|
|
intptr_t err,
|
|
intptr_t exit_handler,
|
|
ProcessResult* result);
|
|
|
|
// Kill a process with a given pid.
|
|
static bool Kill(intptr_t id, int signal);
|
|
|
|
// Terminate the exit code handler thread. Does not return before
|
|
// the thread has terminated.
|
|
static void TerminateExitCodeHandler();
|
|
|
|
static int GlobalExitCode() {
|
|
MutexLocker ml(global_exit_code_mutex_);
|
|
return global_exit_code_;
|
|
}
|
|
|
|
static void SetGlobalExitCode(int exit_code) {
|
|
MutexLocker ml(global_exit_code_mutex_);
|
|
global_exit_code_ = exit_code;
|
|
}
|
|
|
|
typedef void (*ExitHook)(int64_t exit_code);
|
|
static void SetExitHook(ExitHook hook) { exit_hook_ = hook; }
|
|
static void RunExitHook(int64_t exit_code) {
|
|
if (exit_hook_ != nullptr) {
|
|
exit_hook_(exit_code);
|
|
}
|
|
}
|
|
|
|
static intptr_t CurrentProcessId();
|
|
|
|
static intptr_t SetSignalHandler(intptr_t signal);
|
|
// When there is a current Isolate and the 'port' argument is
|
|
// Dart_GetMainPortId(), this clears the signal handler for the current
|
|
// isolate. When 'port' is ILLEGAL_PORT, this clears all signal handlers for
|
|
// 'signal' for all Isolates.
|
|
static void ClearSignalHandler(intptr_t signal, Dart_Port port);
|
|
static void ClearSignalHandlerByFd(intptr_t fd, Dart_Port port);
|
|
static void ClearAllSignalHandlers();
|
|
|
|
static Dart_Handle GetProcessIdNativeField(Dart_Handle process,
|
|
intptr_t* pid);
|
|
static Dart_Handle SetProcessIdNativeField(Dart_Handle process, intptr_t pid);
|
|
|
|
static int64_t CurrentRSS();
|
|
static int64_t MaxRSS();
|
|
static void GetRSSInformation(int64_t* max_rss, int64_t* current_rss);
|
|
|
|
static bool ModeIsAttached(ProcessStartMode mode);
|
|
static bool ModeHasStdio(ProcessStartMode mode);
|
|
|
|
private:
|
|
static int global_exit_code_;
|
|
static Mutex* global_exit_code_mutex_;
|
|
static ExitHook exit_hook_;
|
|
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Process);
|
|
};
|
|
|
|
typedef void (*sa_handler_t)(int);
|
|
|
|
class SignalInfo {
|
|
public:
|
|
SignalInfo(intptr_t fd,
|
|
intptr_t signal,
|
|
sa_handler_t oldact,
|
|
SignalInfo* next)
|
|
: fd_(fd),
|
|
signal_(signal),
|
|
oldact_(oldact),
|
|
// SignalInfo is expected to be created when in a isolate.
|
|
port_(Dart_GetMainPortId()),
|
|
next_(next),
|
|
prev_(nullptr) {
|
|
if (next_ != nullptr) {
|
|
next_->prev_ = this;
|
|
}
|
|
}
|
|
|
|
~SignalInfo();
|
|
|
|
void Unlink() {
|
|
if (prev_ != nullptr) {
|
|
prev_->next_ = next_;
|
|
}
|
|
if (next_ != nullptr) {
|
|
next_->prev_ = prev_;
|
|
}
|
|
}
|
|
|
|
intptr_t fd() const { return fd_; }
|
|
intptr_t signal() const { return signal_; }
|
|
sa_handler_t oldact() const { return oldact_; }
|
|
Dart_Port port() const { return port_; }
|
|
SignalInfo* next() const { return next_; }
|
|
|
|
private:
|
|
intptr_t fd_;
|
|
intptr_t signal_;
|
|
sa_handler_t oldact_;
|
|
// The port_ is used to identify what isolate the signal-info belongs to.
|
|
Dart_Port port_;
|
|
SignalInfo* next_;
|
|
SignalInfo* prev_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(SignalInfo);
|
|
};
|
|
|
|
// Utility class for collecting the output when running a process
|
|
// synchronously by using Process::Wait. This class is sub-classed in
|
|
// the platform specific files to implement reading into the buffers
|
|
// allocated.
|
|
class BufferListBase {
|
|
protected:
|
|
static constexpr intptr_t kBufferSize = 16 * 1024;
|
|
|
|
class BufferListNode {
|
|
public:
|
|
explicit BufferListNode(intptr_t size) {
|
|
data_ = new uint8_t[size];
|
|
// We check for a failed allocation below in Allocate()
|
|
next_ = nullptr;
|
|
}
|
|
|
|
~BufferListNode() { delete[] data_; }
|
|
|
|
bool Valid() const { return data_ != nullptr; }
|
|
|
|
uint8_t* data() const { return data_; }
|
|
BufferListNode* next() const { return next_; }
|
|
void set_next(BufferListNode* n) { next_ = n; }
|
|
|
|
private:
|
|
uint8_t* data_;
|
|
BufferListNode* next_;
|
|
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(BufferListNode);
|
|
};
|
|
|
|
public:
|
|
BufferListBase()
|
|
: head_(nullptr), tail_(nullptr), data_size_(0), free_size_(0) {}
|
|
~BufferListBase() {
|
|
Free();
|
|
DEBUG_ASSERT(IsEmpty());
|
|
}
|
|
|
|
// Returns the collected data as a Uint8List. If an error occurs an
|
|
// error handle is returned.
|
|
Dart_Handle GetData() {
|
|
uint8_t* buffer;
|
|
intptr_t buffer_position = 0;
|
|
Dart_Handle result = IOBuffer::Allocate(data_size_, &buffer);
|
|
if (Dart_IsNull(result)) {
|
|
return DartUtils::NewDartOSError();
|
|
}
|
|
if (Dart_IsError(result)) {
|
|
Free();
|
|
return result;
|
|
}
|
|
for (BufferListNode* current = head_; current != nullptr;
|
|
current = current->next()) {
|
|
intptr_t to_copy = dart::Utils::Minimum(data_size_, kBufferSize);
|
|
memmove(buffer + buffer_position, current->data(), to_copy);
|
|
buffer_position += to_copy;
|
|
data_size_ -= to_copy;
|
|
}
|
|
ASSERT(data_size_ == 0);
|
|
Free();
|
|
return result;
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
bool IsEmpty() const { return (head_ == nullptr) && (tail_ == nullptr); }
|
|
#endif
|
|
|
|
protected:
|
|
bool Allocate() {
|
|
ASSERT(free_size_ == 0);
|
|
BufferListNode* node = new BufferListNode(kBufferSize);
|
|
if ((node == nullptr) || !node->Valid()) {
|
|
// Failed to allocate a buffer for the node.
|
|
delete node;
|
|
return false;
|
|
}
|
|
if (head_ == nullptr) {
|
|
head_ = node;
|
|
tail_ = node;
|
|
} else {
|
|
ASSERT(tail_->next() == nullptr);
|
|
tail_->set_next(node);
|
|
tail_ = node;
|
|
}
|
|
free_size_ = kBufferSize;
|
|
return true;
|
|
}
|
|
|
|
void Free() {
|
|
BufferListNode* current = head_;
|
|
while (current != nullptr) {
|
|
BufferListNode* tmp = current;
|
|
current = current->next();
|
|
delete tmp;
|
|
}
|
|
head_ = nullptr;
|
|
tail_ = nullptr;
|
|
data_size_ = 0;
|
|
free_size_ = 0;
|
|
}
|
|
|
|
// Returns the address of the first byte in the free space.
|
|
uint8_t* FreeSpaceAddress() {
|
|
return tail_->data() + (kBufferSize - free_size_);
|
|
}
|
|
|
|
intptr_t data_size() const { return data_size_; }
|
|
void set_data_size(intptr_t size) { data_size_ = size; }
|
|
|
|
intptr_t free_size() const { return free_size_; }
|
|
void set_free_size(intptr_t size) { free_size_ = size; }
|
|
|
|
BufferListNode* head() const { return head_; }
|
|
BufferListNode* tail() const { return tail_; }
|
|
|
|
private:
|
|
// Linked list for data collected.
|
|
BufferListNode* head_;
|
|
BufferListNode* tail_;
|
|
|
|
// Number of bytes of data collected in the linked list.
|
|
intptr_t data_size_;
|
|
|
|
// Number of free bytes in the last node in the list.
|
|
intptr_t free_size_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(BufferListBase);
|
|
};
|
|
|
|
#if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) || \
|
|
defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS)
|
|
class BufferList : public BufferListBase {
|
|
public:
|
|
BufferList() {}
|
|
|
|
bool Read(int fd, intptr_t available) {
|
|
// Read all available bytes.
|
|
while (available > 0) {
|
|
if (free_size() == 0) {
|
|
if (!Allocate()) {
|
|
errno = ENOMEM;
|
|
return false;
|
|
}
|
|
}
|
|
ASSERT(free_size() > 0);
|
|
ASSERT(free_size() <= kBufferSize);
|
|
intptr_t block_size = dart::Utils::Minimum(free_size(), available);
|
|
#if defined(DART_HOST_OS_FUCHSIA)
|
|
intptr_t bytes = NO_RETRY_EXPECTED(
|
|
read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size));
|
|
#else
|
|
intptr_t bytes = TEMP_FAILURE_RETRY(
|
|
read(fd, reinterpret_cast<void*>(FreeSpaceAddress()), block_size));
|
|
#endif // defined(DART_HOST_OS_FUCHSIA)
|
|
if (bytes < 0) {
|
|
return false;
|
|
}
|
|
set_data_size(data_size() + bytes);
|
|
set_free_size(free_size() - bytes);
|
|
available -= bytes;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(BufferList);
|
|
};
|
|
#endif // defined(DART_HOST_OS_ANDROID) ...
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_BIN_PROCESS_H_
|