blob: 4fd2d1d9a8c191740eb749eec9e997e32f9d3cec [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/globals.h"
#if defined(TARGET_OS_MACOS)
#include <errno.h> // NOLINT
#include <stdio.h> // NOLINT
#include <stdlib.h> // NOLINT
#include <string.h> // NOLINT
#include <sys/event.h> // NOLINT
#include <unistd.h> // NOLINT
#include "bin/dartutils.h"
#include "bin/dbg_connection.h"
#include "bin/fdutils.h"
#include "bin/log.h"
#include "bin/socket.h"
#include "platform/thread.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
#define INVALID_FD -1
int DebuggerConnectionImpl::kqueue_fd_ = INVALID_FD;
int DebuggerConnectionImpl::wakeup_fds_[2] = {INVALID_FD, INVALID_FD};
// Used by VM threads to send a message to the debugger connetion
// handler thread.
void DebuggerConnectionImpl::SendMessage(MessageType id) {
ASSERT(wakeup_fds_[1] != INVALID_FD);
struct Message msg;
msg.msg_id = id;
int result = FDUtils::WriteToBlocking(wakeup_fds_[1], &msg, sizeof(msg));
if (result != sizeof(msg)) {
if (result == -1) {
perror("Wakeup message failure: ");
}
FATAL1("Wakeup message failure. Wrote %d bytes.", result);
}
}
// Used by the debugger connection handler to read the messages sent
// by the VM.
bool DebuggerConnectionImpl::ReceiveMessage(Message* msg) {
int total_read = 0;
int bytes_read = 0;
int remaining = sizeof(Message);
uint8_t* buf = reinterpret_cast<uint8_t*>(msg);
while (remaining > 0) {
bytes_read =
TEMP_FAILURE_RETRY(read(wakeup_fds_[0], buf + total_read, remaining));
if ((bytes_read < 0) && (total_read == 0)) {
return false;
}
if (bytes_read > 0) {
total_read += bytes_read;
remaining -= bytes_read;
}
}
ASSERT(remaining >= 0);
return remaining == 0;
}
void DebuggerConnectionImpl::HandleEvent(struct kevent* event) {
int ident = event->ident;
if (ident == DebuggerConnectionHandler::listener_fd_) {
if (DebuggerConnectionHandler::IsConnected()) {
FATAL("Cannot connect to more than one debugger.\n");
}
int fd = ServerSocket::Accept(ident);
if (fd < 0) {
FATAL("Accepting new debugger connection failed.\n");
}
FDUtils::SetBlocking(fd);
DebuggerConnectionHandler::AcceptDbgConnection(fd);
/* For now, don't poll the debugger connection.
struct kevent ev_add;
EV_SET(&ev_add, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
int status =
TEMP_FAILURE_RETRY(kevent(kqueue_fd_, &ev_add, 1, NULL, 0, NULL));
if (status == -1) {
FATAL1("Failed adding debugger socket to kqueue: %s\n", strerror(errno));
}
*/
} else if (ident == wakeup_fds_[0]) {
Message msg;
if (ReceiveMessage(&msg)) {
Log::Print("Received sync message id %d.\n", msg.msg_id);
}
} else {
Log::Print("unexpected: receiving debugger connection event.\n");
UNIMPLEMENTED();
}
}
void DebuggerConnectionImpl::Handler(uword args) {
static const intptr_t kMaxEvents = 4;
struct kevent events[kMaxEvents];
while (1) {
// Wait indefinitely for an event.
int result = TEMP_FAILURE_RETRY(
kevent(kqueue_fd_, NULL, 0, events, kMaxEvents, NULL));
if (result == -1) {
FATAL1("kevent failed %s\n", strerror(errno));
} else {
ASSERT(result <= kMaxEvents);
for (int i = 0; i < result; i++) {
HandleEvent(&events[i]);
}
}
}
Log::Print("shutting down debugger thread\n");
}
void DebuggerConnectionImpl::SetupPollQueue() {
int result;
result = TEMP_FAILURE_RETRY(pipe(wakeup_fds_));
if (result != 0) {
FATAL1("Pipe creation failed with error %d\n", result);
}
FDUtils::SetNonBlocking(wakeup_fds_[0]);
kqueue_fd_ = TEMP_FAILURE_RETRY(kqueue());
if (kqueue_fd_ == -1) {
FATAL("Failed creating kqueue\n");
}
// Register the wakeup_fd_ with the kqueue.
struct kevent event;
EV_SET(&event, wakeup_fds_[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
int status = TEMP_FAILURE_RETRY(kevent(kqueue_fd_, &event, 1, NULL, 0, NULL));
if (status == -1) {
FATAL1("Failed adding wakeup pipe fd to kqueue: %s\n", strerror(errno));
}
// Register the listening socket.
EV_SET(&event, DebuggerConnectionHandler::listener_fd_,
EVFILT_READ, EV_ADD, 0, 0, NULL);
status = TEMP_FAILURE_RETRY(kevent(kqueue_fd_, &event, 1, NULL, 0, NULL));
if (status == -1) {
FATAL1("Failed adding listener socket to kqueue: %s\n", strerror(errno));
}
}
void DebuggerConnectionImpl::StartHandler(int port_number) {
ASSERT(DebuggerConnectionHandler::listener_fd_ != -1);
SetupPollQueue();
int result =
dart::Thread::Start(&DebuggerConnectionImpl::Handler, 0);
if (result != 0) {
FATAL1("Failed to start debugger connection handler thread: %d\n", result);
}
}
intptr_t DebuggerConnectionImpl::Send(intptr_t socket,
const char* buf,
int len) {
return TEMP_FAILURE_RETRY(write(socket, buf, len));
}
intptr_t DebuggerConnectionImpl::Receive(intptr_t socket, char* buf, int len) {
return TEMP_FAILURE_RETRY(read(socket, buf, len));
}
} // namespace bin
} // namespace dart
#endif // defined(TARGET_OS_MACOS)