diff --git a/src/developer/debug/debug_agent/debug_agent.cc b/src/developer/debug/debug_agent/debug_agent.cc index 401068519731f34e0e335fa6950e2b19a68e9b9d..66c5ee5027e79c269ec6efbac9f04a9cfa8bd66f 100644 --- a/src/developer/debug/debug_agent/debug_agent.cc +++ b/src/developer/debug/debug_agent/debug_agent.cc @@ -536,11 +536,6 @@ void DebugAgent::LaunchComponent(const debug_ipc::LaunchRequest& request, *reply = {}; reply->inferior_type = debug_ipc::InferiorType::kComponent; - if (!debug_ipc::MessageLoopTarget::Current()->SupportsFidl()) { - reply->status = ZX_ERR_NOT_SUPPORTED; - return; - } - ComponentLauncher component_launcher(services_); ComponentDescription description; diff --git a/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc b/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc index a171d98d4311ee78f6df88f7f9d7c1eb55c26f5f..aedfd114eea158dabb166bc43af7ae7336de5cf6 100644 --- a/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc +++ b/src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.cc @@ -4,12 +4,12 @@ #include "src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.h" -#include "src/developer/debug/shared/message_loop_async.h" +#include "src/developer/debug/shared/message_loop_target.h" namespace debug_agent { MessageLoopWrapper::MessageLoopWrapper() { - loop_ = std::make_unique<debug_ipc::MessageLoopAsync>(); + loop_ = std::make_unique<debug_ipc::MessageLoopTarget>(); loop_->Init(); } diff --git a/src/developer/debug/debug_agent/main.cc b/src/developer/debug/debug_agent/main.cc index 98e1d567dba572786d1a4496b44d0a1dc75b6959..63115b1bbfef97d014b613c272475d69882a7637 100644 --- a/src/developer/debug/debug_agent/main.cc +++ b/src/developer/debug/debug_agent/main.cc @@ -20,9 +20,7 @@ #include "src/developer/debug/debug_agent/unwind.h" #include "src/developer/debug/shared/buffered_fd.h" #include "src/developer/debug/shared/logging/logging.h" -#include "src/developer/debug/shared/message_loop_async.h" #include "src/developer/debug/shared/message_loop_target.h" -#include "src/developer/debug/shared/message_loop_zircon.h" #include "src/developer/debug/shared/zx_status.h" #include "src/lib/files/unique_fd.h" @@ -31,21 +29,6 @@ using namespace debug_ipc; namespace debug_agent { namespace { -std::unique_ptr<debug_ipc::MessageLoopTarget> GetMessageLoop( - MessageLoopTarget::Type type) { - switch (type) { - case MessageLoopTarget::Type::kAsync: - return std::make_unique<debug_ipc::MessageLoopAsync>(); - case MessageLoopTarget::Type::kZircon: - return std::make_unique<debug_ipc::MessageLoopZircon>(); - case MessageLoopTarget::Type::kLast: - break; - } - - FXL_NOTREACHED(); - return nullptr; -} - // SocketConnection ------------------------------------------------------------ // Represents one connection to a client. @@ -173,9 +156,6 @@ const std::map<std::string, std::string>& GetOptions() { static std::map<std::string, std::string> options = { {"auwind", R"( [Experimental] Use the unwinder from AOSP.)"}, - {"legacy-message-loop", R"( - [DEPRECATED] Use the originalbackend message loop. - Will be removed eventually.)"}, {"debug-mode", R"( Run the agent on debug mode. This will enable conditional logging messages and timing profiling. Mainly useful for people developing zxdb.)"}, @@ -241,13 +221,6 @@ int main(int argc, char* argv[]) { debug_agent::SetUnwinderType(debug_agent::UnwinderType::kAndroid); } - // By default use the async agent message loop. - auto message_loop_type = MessageLoopTarget::Type::kAsync; - if (cmdline.HasOption("legacy-message-loop")) { - // Use new async loop. - message_loop_type = MessageLoopTarget::Type::kZircon; - } - // TODO(donosoc): Do correct category setup. debug_ipc::SetLogCategories({LogCategory::kAll}); if (cmdline.HasOption("debug-mode")) { @@ -267,14 +240,11 @@ int main(int argc, char* argv[]) { auto services = sys::ServiceDirectory::CreateFromNamespace(); - printf("Using %s message loop.\n", - MessageLoopTarget::TypeToString(message_loop_type)); - auto message_loop = debug_agent::GetMessageLoop(message_loop_type); + auto message_loop = std::make_unique<MessageLoopTarget>(); zx_status_t status = message_loop->InitTarget(); if (status != ZX_OK) { - const char* type = MessageLoopTarget::TypeToString(message_loop_type); - FXL_LOG(ERROR) << "Could not initialize message loop (type: " << type - << "): " << debug_ipc::ZxStatusToString(status); + FXL_LOG(ERROR) << "Could not initialize message loop: " + << debug_ipc::ZxStatusToString(status); } // The scope ensures the objects are destroyed before calling Cleanup on the diff --git a/src/developer/debug/shared/BUILD.gn b/src/developer/debug/shared/BUILD.gn index f65d5b2b3df56367f37e418d42486889cc79545e..28a077c3073ae27174cd880bbd211707bcc41ee3 100644 --- a/src/developer/debug/shared/BUILD.gn +++ b/src/developer/debug/shared/BUILD.gn @@ -49,18 +49,8 @@ static_library("shared") { "buffered_zx_socket.h", "event_handlers.cc", "event_handlers.h", - - # Implementation of message loop using async-loop. - "message_loop_async.cc", - "message_loop_async.h", - - # New interface to abstract message loop from the debug agent. "message_loop_target.cc", "message_loop_target.h", - - # Original message loop (previously MessageLoopZircon). - "message_loop_zircon.cc", - "message_loop_zircon.h", "zircon_utils.cc", "zircon_utils.h", ] diff --git a/src/developer/debug/shared/event_handlers.cc b/src/developer/debug/shared/event_handlers.cc index ed27c47ee0ccac05774dbe0451cd013ffe7f6c00..7088e4d77734d7b58a94948df19a1533e1eaea54 100644 --- a/src/developer/debug/shared/event_handlers.cc +++ b/src/developer/debug/shared/event_handlers.cc @@ -7,7 +7,7 @@ #include <lib/async-loop/loop.h> #include <lib/async/default.h> -#include "src/developer/debug/shared/message_loop_async.h" +#include "src/developer/debug/shared/message_loop_target.h" #include "src/developer/debug/shared/logging/logging.h" #include "src/developer/debug/shared/zircon_utils.h" #include "src/developer/debug/shared/zx_status.h" @@ -53,7 +53,7 @@ void SignalHandler::Handler(async_dispatcher_t*, async_wait_t* wait, const zx_packet_signal_t* signal) { FXL_DCHECK(status == ZX_OK); - auto* loop = MessageLoopAsync::Current(); + auto* loop = MessageLoopTarget::Current(); FXL_DCHECK(loop); // Search for the AsyncHandle that triggered this signal. @@ -140,7 +140,7 @@ void ExceptionHandler::Handler(async_dispatcher_t*, << ExceptionTypeToString(packet->type); } - auto* loop = MessageLoopAsync::Current(); + auto* loop = MessageLoopTarget::Current(); FXL_DCHECK(loop); auto handler_it = loop->exception_handlers().find(exception); diff --git a/src/developer/debug/shared/message_loop_async.cc b/src/developer/debug/shared/message_loop_async.cc deleted file mode 100644 index 8a99a64dd891c4d24d018b652cc41ca68a364d5f..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/message_loop_async.cc +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/developer/debug/shared/message_loop_async.h" - -#include <lib/fdio/io.h> -#include <lib/zx/handle.h> -#include <lib/zx/job.h> -#include <lib/zx/process.h> -#include <stdio.h> -#include <zircon/syscalls/exception.h> - -#include "src/developer/debug/shared/event_handlers.h" -#include "src/developer/debug/shared/fd_watcher.h" -#include "src/developer/debug/shared/logging/logging.h" -#include "src/developer/debug/shared/socket_watcher.h" -#include "src/developer/debug/shared/zircon_exception_watcher.h" -#include "src/developer/debug/shared/zx_status.h" -#include "src/lib/fxl/compiler_specific.h" -#include "src/lib/fxl/logging.h" - -namespace debug_ipc { - -namespace { - -thread_local MessageLoopAsync* current_message_loop_async = nullptr; - -} // namespace - -// Exception ------------------------------------------------------------ - -struct MessageLoopAsync::Exception { - zx_koid_t thread_koid = 0; - // Not-owning. Must outlive. - async_exception_t* exception_token = nullptr; -}; - -// MessageLoopAsync ----------------------------------------------------------- - -MessageLoopAsync::MessageLoopAsync() : loop_(&kAsyncLoopConfigAttachToThread) {} - -MessageLoopAsync::~MessageLoopAsync() { - FXL_DCHECK(Current() != this); // Cleanup should have been called. -} - -void MessageLoopAsync::Init() { InitTarget(); } - -zx_status_t MessageLoopAsync::InitTarget() { - MessageLoop::Init(); - - FXL_DCHECK(!current_message_loop_async); - current_message_loop_async = this; - MessageLoopTarget::current_message_loop_type = - MessageLoopTarget::Type::kAsync; - - zx::event::create(0, &task_event_); - - WatchInfo info; - info.type = WatchType::kTask; - zx_status_t status = - AddSignalHandler(kTaskSignalKey, task_event_.get(), kTaskSignal, &info); - - if (status != ZX_OK) - return status; - - watches_[kTaskSignalKey] = std::move(info); - return ZX_OK; -} - -void MessageLoopAsync::Cleanup() { - // We need to remove the signal/exception handlers before the message loop - // goes away. - signal_handlers_.clear(); - exception_handlers_.clear(); - - FXL_DCHECK(current_message_loop_async == this); - current_message_loop_async = nullptr; - MessageLoopTarget::current_message_loop_type = MessageLoopTarget::Type::kLast; - - MessageLoop::Cleanup(); -} - -// static -MessageLoopAsync* MessageLoopAsync::Current() { - return current_message_loop_async; -} - -const MessageLoopAsync::WatchInfo* MessageLoopAsync::FindWatchInfo( - int id) const { - auto it = watches_.find(id); - if (it == watches_.end()) - return nullptr; - return &it->second; -} - -zx_status_t MessageLoopAsync::AddSignalHandler(int id, zx_handle_t object, - zx_signals_t signals, - WatchInfo* associated_info) { - SignalHandler handler; - zx_status_t status = handler.Init(id, object, signals); - if (status != ZX_OK) - return status; - - // The handler should not be there already. - FXL_DCHECK(signal_handlers_.find(handler.handle()) == signal_handlers_.end()); - - associated_info->signal_handler_key = handler.handle(); - signal_handlers_[handler.handle()] = std::move(handler); - - return ZX_OK; -} - -zx_status_t MessageLoopAsync::AddExceptionHandler(int id, zx_handle_t object, - uint32_t options, - WatchInfo* associated_info) { - ExceptionHandler handler; - zx_status_t status = handler.Init(id, object, options); - if (status != ZX_OK) - return status; - - // The handler should not be there already. - FXL_DCHECK(exception_handlers_.find(handler.handle()) == - exception_handlers_.end()); - - associated_info->exception_handler_key = handler.handle(); - exception_handlers_[handler.handle()] = std::move(handler); - - return ZX_OK; -} - -MessageLoop::WatchHandle MessageLoopAsync::WatchFD(WatchMode mode, int fd, - FDWatcher* watcher) { - WatchInfo info; - info.type = WatchType::kFdio; - info.fd_watcher = watcher; - info.fd = fd; - info.fdio = fdio_unsafe_fd_to_io(fd); - if (!info.fdio) - return WatchHandle(); - - uint32_t events = 0; - switch (mode) { - case WatchMode::kRead: - events = POLLIN; - break; - case WatchMode::kWrite: - events = POLLOUT; - break; - case WatchMode::kReadWrite: - events = POLLIN | POLLOUT; - break; - } - - zx_signals_t signals = ZX_SIGNAL_NONE; - fdio_unsafe_wait_begin(info.fdio, events, &info.fd_handle, &signals); - if (info.fd_handle == ZX_HANDLE_INVALID) - return WatchHandle(); - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - } - - zx_status_t status = - AddSignalHandler(watch_id, info.fd_handle, signals, &info); - if (status != ZX_OK) - return WatchHandle(); - - watches_[watch_id] = info; - return WatchHandle(this, watch_id); -} - -zx_status_t MessageLoopAsync::WatchSocket(WatchMode mode, - zx_handle_t socket_handle, - SocketWatcher* watcher, - MessageLoop::WatchHandle* out) { - WatchInfo info; - info.type = WatchType::kSocket; - info.socket_watcher = watcher; - info.socket_handle = socket_handle; - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - } - - zx_signals_t signals = 0; - if (mode == WatchMode::kRead || mode == WatchMode::kReadWrite) - signals |= ZX_SOCKET_READABLE; - - if (mode == WatchMode::kWrite || mode == WatchMode::kReadWrite) - signals |= ZX_SOCKET_WRITABLE; - - zx_status_t status = - AddSignalHandler(watch_id, socket_handle, ZX_SOCKET_WRITABLE, &info); - if (status != ZX_OK) - return status; - - watches_[watch_id] = info; - *out = WatchHandle(this, watch_id); - return ZX_OK; -} - -zx_status_t MessageLoopAsync::WatchProcessExceptions( - WatchProcessConfig config, MessageLoop::WatchHandle* out) { - WatchInfo info; - info.resource_name = config.process_name; - info.type = WatchType::kProcessExceptions; - info.exception_watcher = config.watcher; - info.task_koid = config.process_koid; - info.task_handle = config.process_handle; - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - } - - // Watch all exceptions for the process. - zx_status_t status; - status = AddExceptionHandler(watch_id, config.process_handle, - ZX_EXCEPTION_PORT_DEBUGGER, &info); - if (status != ZX_OK) - return status; - - // Watch for the process terminated signal. - status = AddSignalHandler(watch_id, config.process_handle, - ZX_PROCESS_TERMINATED, &info); - if (status != ZX_OK) - return status; - - DEBUG_LOG(MessageLoop) << "Watching process " << info.resource_name; - - watches_[watch_id] = info; - *out = WatchHandle(this, watch_id); - return ZX_OK; -} - -zx_status_t MessageLoopAsync::WatchJobExceptions( - WatchJobConfig config, MessageLoop::WatchHandle* out) { - WatchInfo info; - info.resource_name = config.job_name; - info.type = WatchType::kJobExceptions; - info.exception_watcher = config.watcher; - info.task_koid = config.job_koid; - info.task_handle = config.job_handle; - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - } - - // Create and track the exception handle. - zx_status_t status = AddExceptionHandler(watch_id, config.job_handle, - ZX_EXCEPTION_PORT_DEBUGGER, &info); - if (status != ZX_OK) - return status; - - DEBUG_LOG(MessageLoop) << "Watching job " << info.resource_name; - - watches_[watch_id] = info; - *out = WatchHandle(this, watch_id); - return ZX_OK; -} - -zx_status_t MessageLoopAsync::ResumeFromException(zx_koid_t thread_koid, - zx::thread& thread, - uint32_t options) { - auto it = thread_exception_map_.find(thread_koid); - FXL_DCHECK(it != thread_exception_map_.end()); - zx_status_t res = async_resume_from_exception(async_get_default_dispatcher(), - it->second.exception_token, - thread.get(), options); - thread_exception_map_.erase(thread_koid); - return res; -} - -bool MessageLoopAsync::CheckAndProcessPendingTasks() { - std::lock_guard<std::mutex> guard(mutex_); - // Do a C++ task. - if (ProcessPendingTask()) { - SetHasTasks(); // Enqueue another task signal. - return true; - } - return false; -} - -void MessageLoopAsync::HandleException(const ExceptionHandler& handler, - zx_port_packet_t packet) { - WatchInfo* watch_info = nullptr; - { - // Some event being watched. - std::lock_guard<std::mutex> guard(mutex_); - auto found = watches_.find(packet.key); - if (found == watches_.end()) { - // It is possible to get an exception that doesn't have a watch handle. - // A case is a race between detaching from a process and getting an - // exception on that process. - // - // The normal process looks like this: - // - // 1. In order to correctly detach, the debug agent has to resume threads - // from their exceptions. Otherwise that exception will be treated as - // unhandled when the agent detaches and will bubble up. - // 2. The agent detaches from the exception port. This means that the - // watch handle is no longer listening. - // - // It is possible between (1) and (2) to get an exception, which will be - // queued in the exception port of the thread. Now, the agent won't read - // from the port until *after* it has detached from the exception port. - // This means that this new exception is not handled and will be bubbled - // up, which is correct as the debug agent stated that it has nothing more - // to do with the process. - // - // Now the problem is that zircon does not clean stale packets from a - // queue, meaning that the next time the message loop waits on the port, - // it will find a stale packet. In this context a stale packet means one - // that does not have a watch handle, as it was deleted in (1). Hence we - // get into this case and we simply log it for posperity. - // - // TODO(ZX-2623): zircon is going to clean up stale packets from ports - // in the future. When that is done, this case should not - // happen and we should go back into asserting it. - FXL_LOG(WARNING) << "Got stale port packet. This is most probably due to " - "a race between detaching from a process and an " - "exception ocurring."; - return; - } - watch_info = &found->second; - } - - // Dispatch the watch callback outside of the lock. This depends on the - // stability of the WatchInfo pointer in the map (std::map is stable across - // updates) and the watch not getting unregistered from another thread - // asynchronously (which the API requires and is enforced by a DCHECK in - // the StopWatching impl). - switch (watch_info->type) { - case WatchType::kProcessExceptions: - OnProcessException(handler, *watch_info, packet); - break; - case WatchType::kJobExceptions: - OnJobException(handler, *watch_info, packet); - break; - case WatchType::kTask: - case WatchType::kFdio: - case WatchType::kSocket: - FXL_NOTREACHED(); - } -} - -uint64_t MessageLoopAsync::GetMonotonicNowNS() const { - zx::time ret; - zx::clock::get(&ret); - - return ret.get(); -} - -// Previously, the approach was to first look for C++ tasks and when handled -// look for WatchHandle work and finally wait for an event. This worked because -// handle events didn't post C++ tasks. -// -// But some tests do post tasks on handle events. Because C++ tasks are signaled -// by explicitly signaling an zx::event, without manually checking, the C++ -// tasks will never be checked and we would get blocked until a watch handled -// is triggered. -// -// In order to handle the events properly, we need to check for C++ tasks before -// and *after* handling watch handle events. This way we always process C++ -// tasks before handle events and will get signaled if one of them posted a new -// task. -void MessageLoopAsync::RunImpl() { - // Init should have been called. - FXL_DCHECK(Current() == this); - zx_status_t status; - - zx::time time; - uint64_t delay = DelayNS(); - if (delay == MessageLoop::kMaxDelay) { - time = zx::time::infinite(); - } else { - time = zx::deadline_after(zx::nsec(delay)); - } - - for (;;) { - status = loop_.ResetQuit(); - FXL_DCHECK(status != ZX_ERR_BAD_STATE); - status = loop_.Run(time); - FXL_DCHECK(status == ZX_OK || status == ZX_ERR_CANCELED || - status == ZX_ERR_TIMED_OUT) - << "Expected ZX_OK || ZX_ERR_CANCELED || ZX_ERR_TIMED_OUT, got " - << ZxStatusToString(status); - - if (status != ZX_ERR_TIMED_OUT) { - return; - } - - std::lock_guard<std::mutex> guard(mutex_); - if (ProcessPendingTask()) - SetHasTasks(); - } -} - -void MessageLoopAsync::QuitNow() { - MessageLoop::QuitNow(); - loop_.Quit(); -} - -void MessageLoopAsync::StopWatching(int id) { - // The dispatch code for watch callbacks requires this be called on the - // same thread as the message loop is. - FXL_DCHECK(Current() == this); - - std::lock_guard<std::mutex> guard(mutex_); - - auto found = watches_.find(id); - FXL_DCHECK(found != watches_.end()); - - WatchInfo& info = found->second; - // BufferedFD constantly creates and destroys FD handles, flooding the log - // with non-helpful logging statements. - if (info.type != WatchType::kFdio) { - DEBUG_LOG(MessageLoop) << "Stop watching " << WatchTypeToString(info.type) - << " " << info.resource_name; - } - - switch (info.type) { - case WatchType::kProcessExceptions: { - RemoveExceptionHandler(info.exception_handler_key); - RemoveSignalHandler(info.signal_handler_key); - break; - } - case WatchType::kJobExceptions: { - RemoveExceptionHandler(info.exception_handler_key); - break; - } - case WatchType::kTask: - case WatchType::kFdio: - case WatchType::kSocket: - RemoveSignalHandler(info.signal_handler_key); - break; - } - watches_.erase(found); -} - -void MessageLoopAsync::SetHasTasks() { task_event_.signal(0, kTaskSignal); } - -void MessageLoopAsync::OnFdioSignal(int watch_id, const WatchInfo& info, - zx_signals_t observed) { - uint32_t events = 0; - fdio_unsafe_wait_end(info.fdio, observed, &events); - - if ((events & POLLERR) || (events & POLLHUP) || (events & POLLNVAL) || - (events & POLLRDHUP)) { - info.fd_watcher->OnFDReady(info.fd, false, false, true); - - // Don't dispatch any other notifications when there's an error. Zircon - // seems to set readable and writable on error even if there's nothing - // there. - return; - } - - bool readable = !!(events & POLLIN); - bool writable = !!(events & POLLOUT); - info.fd_watcher->OnFDReady(info.fd, readable, writable, false); -} - -void MessageLoopAsync::RemoveSignalHandler(const async_wait_t* key) { - FXL_DCHECK(key); - size_t erase_count = signal_handlers_.erase(key); - FXL_DCHECK(erase_count == 1u); -} - -void MessageLoopAsync::RemoveExceptionHandler(const async_exception_t* key) { - FXL_DCHECK(key); - size_t erase_count = exception_handlers_.erase(key); - FXL_DCHECK(erase_count == 1u); -} - -void MessageLoopAsync::AddException(const ExceptionHandler& handler, - zx_koid_t thread_koid) { - FXL_DCHECK(thread_exception_map_.find(thread_koid) == - thread_exception_map_.end()); - - Exception exception; - exception.thread_koid = thread_koid; - exception.exception_token = const_cast<async_exception_t*>(handler.handle()); - thread_exception_map_[thread_koid] = std::move(exception); -} - -void MessageLoopAsync::OnProcessException(const ExceptionHandler& handler, - const WatchInfo& info, - const zx_port_packet_t& packet) { - if (ZX_PKT_IS_EXCEPTION(packet.type)) { - // All debug exceptions. - switch (packet.type) { - case ZX_EXCP_THREAD_STARTING: - AddException(handler, packet.exception.tid); - info.exception_watcher->OnThreadStarting(info.task_koid, - packet.exception.tid); - break; - case ZX_EXCP_THREAD_EXITING: - AddException(handler, packet.exception.tid); - info.exception_watcher->OnThreadExiting(info.task_koid, - packet.exception.tid); - break; - case ZX_EXCP_GENERAL: - case ZX_EXCP_FATAL_PAGE_FAULT: - case ZX_EXCP_UNDEFINED_INSTRUCTION: - case ZX_EXCP_SW_BREAKPOINT: - case ZX_EXCP_HW_BREAKPOINT: - case ZX_EXCP_UNALIGNED_ACCESS: - case ZX_EXCP_POLICY_ERROR: - AddException(handler, packet.exception.tid); - info.exception_watcher->OnException(info.task_koid, - packet.exception.tid, packet.type); - break; - default: - FXL_NOTREACHED(); - } - } else { - FXL_NOTREACHED(); - } -} - -void MessageLoopAsync::OnProcessTerminated(const WatchInfo& info, - zx_signals_t observed) { - FXL_DCHECK(observed & ZX_PROCESS_TERMINATED); - info.exception_watcher->OnProcessTerminated(info.task_koid); -} - -void MessageLoopAsync::OnJobException(const ExceptionHandler& handler, - const WatchInfo& info, - const zx_port_packet_t& packet) { - if (ZX_PKT_IS_EXCEPTION(packet.type)) { - // All debug exceptions. - switch (packet.type) { - case ZX_EXCP_PROCESS_STARTING: - AddException(handler, packet.exception.tid); - info.exception_watcher->OnProcessStarting( - info.task_koid, packet.exception.pid, packet.exception.tid); - return; - default: - break; - } - } - FXL_NOTREACHED(); -} - -void MessageLoopAsync::OnSocketSignal(int watch_id, const WatchInfo& info, - zx_signals_t observed) { - // Dispatch readable signal. - if (observed & ZX_SOCKET_READABLE) - info.socket_watcher->OnSocketReadable(info.socket_handle); - - // When signaling both readable and writable, make sure the readable handler - // didn't remove the watch. - if ((observed & ZX_SOCKET_READABLE) && (observed & ZX_SOCKET_WRITABLE)) { - std::lock_guard<std::mutex> guard(mutex_); - if (watches_.find(watch_id) == watches_.end()) - return; - } - - // Dispatch writable signal. - if (observed & ZX_SOCKET_WRITABLE) - info.socket_watcher->OnSocketWritable(info.socket_handle); -} - -} // namespace debug_ipc diff --git a/src/developer/debug/shared/message_loop_async.h b/src/developer/debug/shared/message_loop_async.h deleted file mode 100644 index f25d4909d2c4279c6df7a20c947df8c0689103d9..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/message_loop_async.h +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include <lib/async-loop/cpp/loop.h> -#include <lib/fdio/unsafe.h> -#include <lib/zx/event.h> -#include <lib/zx/port.h> -#include <lib/zx/thread.h> - -#include <vector> - -#include "src/developer/debug/shared/event_handlers.h" -#include "src/developer/debug/shared/message_loop_target.h" - -namespace debug_ipc { - -class ExceptionHandler; -class SignalHandler; -class SocketWatcher; -class ZirconExceptionWatcher; - -class MessageLoopAsync : public MessageLoopTarget { - public: - // Associated struct to track information about what type of resource a watch - // handle is following. - // EventHandlers need access to the WatchInfo implementation, hence the reason - // for it to be public. - // Definition at the end of the header. - struct WatchInfo; - - using SignalHandlerMap = std::map<const async_wait_t*, SignalHandler>; - using ExceptionHandlerMap = - std::map<const async_exception_t*, ExceptionHandler>; - - MessageLoopAsync(); - ~MessageLoopAsync(); - - void Init() override; - zx_status_t InitTarget() override; - - Type GetType() const override { return Type::kAsync; } - bool SupportsFidl() const override { return true; } - - void Cleanup() override; - - // Returns the current message loop or null if there isn't one. - static MessageLoopAsync* Current(); - - // MessageLoop implementation. - WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override; - - zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle, - SocketWatcher* watcher, WatchHandle* out) override; - - // Attaches to the exception port of the given process and issues callbacks - // on the given watcher. The watcher must outlive the returned WatchHandle. - // Must only be called on the message loop thread. - zx_status_t WatchProcessExceptions(WatchProcessConfig config, - WatchHandle* out) override; - - // Attaches to the exception port of the given job and issues callbacks - // on the given watcher. The watcher must outlive the returned WatchHandle. - // Must only be called on the message loop thread. - zx_status_t WatchJobExceptions(WatchJobConfig config, - WatchHandle* out) override; - - // When this class issues an exception notification, the code should call - // this function to resume the thread from the exception. This is a wrapper - // for zx_task_resume_from_exception. - /* zx_status_t ResumeFromException(zx::thread& thread, uint32_t options); */ - zx_status_t ResumeFromException(zx_koid_t thread_koid, zx::thread&, - uint32_t options) override; - - void QuitNow() override; - - const SignalHandlerMap& signal_handlers() const { return signal_handlers_; } - - const ExceptionHandlerMap& exception_handlers() const { - return exception_handlers_; - } - - private: - const WatchInfo* FindWatchInfo(int id) const; - - // MessageLoop protected implementation. - uint64_t GetMonotonicNowNS() const override; - void RunImpl() override; - void StopWatching(int id) override; - // Triggers an event signaling that there is a pending event. - void SetHasTasks() override; - - // Check for any pending C++ tasks and process them. - // Returns true if there was an event pending to be processed. - bool CheckAndProcessPendingTasks(); - - // Handles WatchHandles event. These are all the events that are not C++ tasks - // posted to the message loop. - void HandleException(const ExceptionHandler&, zx_port_packet_t packet); - - // Handle an event of the given type. - void OnFdioSignal(int watch_id, const WatchInfo& info, zx_signals_t observed); - void OnProcessException(const ExceptionHandler&, const WatchInfo& info, - const zx_port_packet_t& packet); - void OnProcessTerminated(const WatchInfo&, zx_signals_t observed); - - void OnJobException(const ExceptionHandler&, const WatchInfo& info, - const zx_port_packet_t& packet); - void OnSocketSignal(int watch_id, const WatchInfo& info, - zx_signals_t observed); - - using WatchMap = std::map<int, WatchInfo>; - WatchMap watches_; - - // ID used as an index into watches_. - int next_watch_id_ = 1; - - async::Loop loop_; - zx::event task_event_; - - SignalHandlerMap signal_handlers_; - // See SignalHandler constructor. - // |associated_info| needs to be updated with the fact that it has an - // associated SignalHandler. - zx_status_t AddSignalHandler(int, zx_handle_t, zx_signals_t, WatchInfo* info); - void RemoveSignalHandler(const async_wait_t* id); - - ExceptionHandlerMap exception_handlers_; - // See ExceptionHandler constructor. - // |associated_info| needs to be updated with the fact that it has an - // associated ExceptionHandler. - zx_status_t AddExceptionHandler(int, zx_handle_t, uint32_t, WatchInfo* info); - void RemoveExceptionHandler(const async_exception_t*); - - // Every exception source (ExceptionHandler) will get an async_exception_t* - // that works as a "key" for the async_loop. This async_exception_t* is what - // you give back to the loop to return from an exception. - // - // So, everytime there is an exception, there needs to be a tracking from - // thread_koid to this async_exception_t*, so that when a thread is resumed, - // we can pass to the loop the correct key by just using the thread koid. - struct Exception; - std::map<zx_koid_t, Exception> thread_exception_map_; - void AddException(const ExceptionHandler&, zx_koid_t thread_koid); - - FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopAsync); - - friend class SignalHandler; - friend class ExceptionHandler; -}; - -// EventHandlers need access to the WatchInfo implementation. -struct MessageLoopAsync::WatchInfo { - // Name of the resource being watched. - // Mostly tracked for debugging purposes. - std::string resource_name; - - WatchType type = WatchType::kFdio; - - // FDIO-specific watcher parameters. - int fd = -1; - fdio_t* fdio = nullptr; - FDWatcher* fd_watcher = nullptr; - zx_handle_t fd_handle = ZX_HANDLE_INVALID; - - // Socket-specific parameters. - SocketWatcher* socket_watcher = nullptr; - zx_handle_t socket_handle = ZX_HANDLE_INVALID; - - // Task-exception-specific parameters, can be of job or process type. - ZirconExceptionWatcher* exception_watcher = nullptr; - zx_koid_t task_koid = 0; - zx_handle_t task_handle = ZX_HANDLE_INVALID; - - // This makes easier the lookup of the associated ExceptionHandler with this - // watch id. - const async_wait_t* signal_handler_key = nullptr; - const async_exception_t* exception_handler_key = nullptr; -}; - -} // namespace debug_ipc diff --git a/src/developer/debug/shared/message_loop_target.cc b/src/developer/debug/shared/message_loop_target.cc index 7295eb06d2740cb93d57db083169727c060ec7e7..6dafa464a8c0391afe85f77e80d847edb6feff2e 100644 --- a/src/developer/debug/shared/message_loop_target.cc +++ b/src/developer/debug/shared/message_loop_target.cc @@ -1,48 +1,577 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/developer/debug/shared/message_loop_target.h" -#include "src/developer/debug/shared/message_loop_async.h" -#include "src/developer/debug/shared/message_loop_zircon.h" +#include <lib/fdio/io.h> +#include <lib/zx/handle.h> +#include <lib/zx/job.h> +#include <lib/zx/process.h> +#include <stdio.h> +#include <zircon/syscalls/exception.h> +#include "src/developer/debug/shared/event_handlers.h" +#include "src/developer/debug/shared/fd_watcher.h" +#include "src/developer/debug/shared/logging/logging.h" +#include "src/developer/debug/shared/socket_watcher.h" +#include "src/developer/debug/shared/zircon_exception_watcher.h" +#include "src/developer/debug/shared/zx_status.h" +#include "src/lib/fxl/compiler_specific.h" #include "src/lib/fxl/logging.h" namespace debug_ipc { -MessageLoopTarget::~MessageLoopTarget() = default; +namespace { -MessageLoopTarget::Type MessageLoopTarget::current_message_loop_type = - MessageLoopTarget::Type::kLast; +thread_local MessageLoopTarget* current_message_loop = nullptr; +} // namespace + +// Exception ------------------------------------------------------------ + +struct MessageLoopTarget::Exception { + zx_koid_t thread_koid = 0; + // Not-owning. Must outlive. + async_exception_t* exception_token = nullptr; +}; + +// MessageLoopTarget ----------------------------------------------------------- + +MessageLoopTarget::MessageLoopTarget() : loop_(&kAsyncLoopConfigAttachToThread) {} + +MessageLoopTarget::~MessageLoopTarget() { + FXL_DCHECK(Current() != this); // Cleanup should have been called. +} + +void MessageLoopTarget::Init() { InitTarget(); } + +zx_status_t MessageLoopTarget::InitTarget() { + MessageLoop::Init(); + + FXL_DCHECK(!current_message_loop); + current_message_loop = this; + + zx::event::create(0, &task_event_); + + WatchInfo info; + info.type = WatchType::kTask; + zx_status_t status = + AddSignalHandler(kTaskSignalKey, task_event_.get(), kTaskSignal, &info); + + if (status != ZX_OK) + return status; + + watches_[kTaskSignalKey] = std::move(info); + return ZX_OK; +} + +void MessageLoopTarget::Cleanup() { + // We need to remove the signal/exception handlers before the message loop + // goes away. + signal_handlers_.clear(); + exception_handlers_.clear(); + + FXL_DCHECK(current_message_loop == this); + current_message_loop = nullptr; + + MessageLoop::Cleanup(); +} + +// static MessageLoopTarget* MessageLoopTarget::Current() { - FXL_DCHECK(current_message_loop_type != MessageLoopTarget::Type::kLast); - switch (current_message_loop_type) { - case MessageLoopTarget::Type::kAsync: - return MessageLoopAsync::Current(); - case MessageLoopTarget::Type::kZircon: - return MessageLoopZircon::Current(); - case MessageLoopTarget::Type::kLast: + return current_message_loop; +} + +const MessageLoopTarget::WatchInfo* MessageLoopTarget::FindWatchInfo( + int id) const { + auto it = watches_.find(id); + if (it == watches_.end()) + return nullptr; + return &it->second; +} + +zx_status_t MessageLoopTarget::AddSignalHandler(int id, zx_handle_t object, + zx_signals_t signals, + WatchInfo* associated_info) { + SignalHandler handler; + zx_status_t status = handler.Init(id, object, signals); + if (status != ZX_OK) + return status; + + // The handler should not be there already. + FXL_DCHECK(signal_handlers_.find(handler.handle()) == signal_handlers_.end()); + + associated_info->signal_handler_key = handler.handle(); + signal_handlers_[handler.handle()] = std::move(handler); + + return ZX_OK; +} + +zx_status_t MessageLoopTarget::AddExceptionHandler(int id, zx_handle_t object, + uint32_t options, + WatchInfo* associated_info) { + ExceptionHandler handler; + zx_status_t status = handler.Init(id, object, options); + if (status != ZX_OK) + return status; + + // The handler should not be there already. + FXL_DCHECK(exception_handlers_.find(handler.handle()) == + exception_handlers_.end()); + + associated_info->exception_handler_key = handler.handle(); + exception_handlers_[handler.handle()] = std::move(handler); + + return ZX_OK; +} + +MessageLoop::WatchHandle MessageLoopTarget::WatchFD(WatchMode mode, int fd, + FDWatcher* watcher) { + WatchInfo info; + info.type = WatchType::kFdio; + info.fd_watcher = watcher; + info.fd = fd; + info.fdio = fdio_unsafe_fd_to_io(fd); + if (!info.fdio) + return WatchHandle(); + + uint32_t events = 0; + switch (mode) { + case WatchMode::kRead: + events = POLLIN; + break; + case WatchMode::kWrite: + events = POLLOUT; + break; + case WatchMode::kReadWrite: + events = POLLIN | POLLOUT; break; } - FXL_NOTREACHED(); - return nullptr; + zx_signals_t signals = ZX_SIGNAL_NONE; + fdio_unsafe_wait_begin(info.fdio, events, &info.fd_handle, &signals); + if (info.fd_handle == ZX_HANDLE_INVALID) + return WatchHandle(); + + int watch_id; + { + std::lock_guard<std::mutex> guard(mutex_); + + watch_id = next_watch_id_; + next_watch_id_++; + } + + zx_status_t status = + AddSignalHandler(watch_id, info.fd_handle, signals, &info); + if (status != ZX_OK) + return WatchHandle(); + + watches_[watch_id] = info; + return WatchHandle(this, watch_id); } -const char* MessageLoopTarget::TypeToString(Type type) { - switch (type) { - case MessageLoopTarget::Type::kAsync: - return "Async"; - case MessageLoopTarget::Type::kZircon: - return "Zircon"; - case MessageLoopTarget::Type::kLast: - return "Last"; +zx_status_t MessageLoopTarget::WatchSocket(WatchMode mode, + zx_handle_t socket_handle, + SocketWatcher* watcher, + MessageLoop::WatchHandle* out) { + WatchInfo info; + info.type = WatchType::kSocket; + info.socket_watcher = watcher; + info.socket_handle = socket_handle; + + int watch_id; + { + std::lock_guard<std::mutex> guard(mutex_); + + watch_id = next_watch_id_; + next_watch_id_++; + } + + zx_signals_t signals = 0; + if (mode == WatchMode::kRead || mode == WatchMode::kReadWrite) + signals |= ZX_SOCKET_READABLE; + + if (mode == WatchMode::kWrite || mode == WatchMode::kReadWrite) + signals |= ZX_SOCKET_WRITABLE; + + zx_status_t status = + AddSignalHandler(watch_id, socket_handle, ZX_SOCKET_WRITABLE, &info); + if (status != ZX_OK) + return status; + + watches_[watch_id] = info; + *out = WatchHandle(this, watch_id); + return ZX_OK; +} + +zx_status_t MessageLoopTarget::WatchProcessExceptions( + WatchProcessConfig config, MessageLoop::WatchHandle* out) { + WatchInfo info; + info.resource_name = config.process_name; + info.type = WatchType::kProcessExceptions; + info.exception_watcher = config.watcher; + info.task_koid = config.process_koid; + info.task_handle = config.process_handle; + + int watch_id; + { + std::lock_guard<std::mutex> guard(mutex_); + + watch_id = next_watch_id_; + next_watch_id_++; + } + + // Watch all exceptions for the process. + zx_status_t status; + status = AddExceptionHandler(watch_id, config.process_handle, + ZX_EXCEPTION_PORT_DEBUGGER, &info); + if (status != ZX_OK) + return status; + + // Watch for the process terminated signal. + status = AddSignalHandler(watch_id, config.process_handle, + ZX_PROCESS_TERMINATED, &info); + if (status != ZX_OK) + return status; + + DEBUG_LOG(MessageLoop) << "Watching process " << info.resource_name; + + watches_[watch_id] = info; + *out = WatchHandle(this, watch_id); + return ZX_OK; +} + +zx_status_t MessageLoopTarget::WatchJobExceptions( + WatchJobConfig config, MessageLoop::WatchHandle* out) { + WatchInfo info; + info.resource_name = config.job_name; + info.type = WatchType::kJobExceptions; + info.exception_watcher = config.watcher; + info.task_koid = config.job_koid; + info.task_handle = config.job_handle; + + int watch_id; + { + std::lock_guard<std::mutex> guard(mutex_); + + watch_id = next_watch_id_; + next_watch_id_++; + } + + // Create and track the exception handle. + zx_status_t status = AddExceptionHandler(watch_id, config.job_handle, + ZX_EXCEPTION_PORT_DEBUGGER, &info); + if (status != ZX_OK) + return status; + + DEBUG_LOG(MessageLoop) << "Watching job " << info.resource_name; + + watches_[watch_id] = info; + *out = WatchHandle(this, watch_id); + return ZX_OK; +} + +zx_status_t MessageLoopTarget::ResumeFromException(zx_koid_t thread_koid, + zx::thread& thread, + uint32_t options) { + auto it = thread_exception_map_.find(thread_koid); + FXL_DCHECK(it != thread_exception_map_.end()); + zx_status_t res = async_resume_from_exception(async_get_default_dispatcher(), + it->second.exception_token, + thread.get(), options); + thread_exception_map_.erase(thread_koid); + return res; +} + +bool MessageLoopTarget::CheckAndProcessPendingTasks() { + std::lock_guard<std::mutex> guard(mutex_); + // Do a C++ task. + if (ProcessPendingTask()) { + SetHasTasks(); // Enqueue another task signal. + return true; + } + return false; +} + +void MessageLoopTarget::HandleException(const ExceptionHandler& handler, + zx_port_packet_t packet) { + WatchInfo* watch_info = nullptr; + { + // Some event being watched. + std::lock_guard<std::mutex> guard(mutex_); + auto found = watches_.find(packet.key); + if (found == watches_.end()) { + // It is possible to get an exception that doesn't have a watch handle. + // A case is a race between detaching from a process and getting an + // exception on that process. + // + // The normal process looks like this: + // + // 1. In order to correctly detach, the debug agent has to resume threads + // from their exceptions. Otherwise that exception will be treated as + // unhandled when the agent detaches and will bubble up. + // 2. The agent detaches from the exception port. This means that the + // watch handle is no longer listening. + // + // It is possible between (1) and (2) to get an exception, which will be + // queued in the exception port of the thread. Now, the agent won't read + // from the port until *after* it has detached from the exception port. + // This means that this new exception is not handled and will be bubbled + // up, which is correct as the debug agent stated that it has nothing more + // to do with the process. + // + // Now the problem is that zircon does not clean stale packets from a + // queue, meaning that the next time the message loop waits on the port, + // it will find a stale packet. In this context a stale packet means one + // that does not have a watch handle, as it was deleted in (1). Hence we + // get into this case and we simply log it for posperity. + // + // TODO(ZX-2623): zircon is going to clean up stale packets from ports + // in the future. When that is done, this case should not + // happen and we should go back into asserting it. + FXL_LOG(WARNING) << "Got stale port packet. This is most probably due to " + "a race between detaching from a process and an " + "exception ocurring."; + return; + } + watch_info = &found->second; + } + + // Dispatch the watch callback outside of the lock. This depends on the + // stability of the WatchInfo pointer in the map (std::map is stable across + // updates) and the watch not getting unregistered from another thread + // asynchronously (which the API requires and is enforced by a DCHECK in + // the StopWatching impl). + switch (watch_info->type) { + case WatchType::kProcessExceptions: + OnProcessException(handler, *watch_info, packet); + break; + case WatchType::kJobExceptions: + OnJobException(handler, *watch_info, packet); + break; + case WatchType::kTask: + case WatchType::kFdio: + case WatchType::kSocket: + FXL_NOTREACHED(); + } +} + +uint64_t MessageLoopTarget::GetMonotonicNowNS() const { + zx::time ret; + zx::clock::get(&ret); + + return ret.get(); +} + +// Previously, the approach was to first look for C++ tasks and when handled +// look for WatchHandle work and finally wait for an event. This worked because +// handle events didn't post C++ tasks. +// +// But some tests do post tasks on handle events. Because C++ tasks are signaled +// by explicitly signaling an zx::event, without manually checking, the C++ +// tasks will never be checked and we would get blocked until a watch handled +// is triggered. +// +// In order to handle the events properly, we need to check for C++ tasks before +// and *after* handling watch handle events. This way we always process C++ +// tasks before handle events and will get signaled if one of them posted a new +// task. +void MessageLoopTarget::RunImpl() { + // Init should have been called. + FXL_DCHECK(Current() == this); + zx_status_t status; + + zx::time time; + uint64_t delay = DelayNS(); + if (delay == MessageLoop::kMaxDelay) { + time = zx::time::infinite(); + } else { + time = zx::deadline_after(zx::nsec(delay)); + } + + for (;;) { + status = loop_.ResetQuit(); + FXL_DCHECK(status != ZX_ERR_BAD_STATE); + status = loop_.Run(time); + FXL_DCHECK(status == ZX_OK || status == ZX_ERR_CANCELED || + status == ZX_ERR_TIMED_OUT) + << "Expected ZX_OK || ZX_ERR_CANCELED || ZX_ERR_TIMED_OUT, got " + << ZxStatusToString(status); + + if (status != ZX_ERR_TIMED_OUT) { + return; + } + + std::lock_guard<std::mutex> guard(mutex_); + if (ProcessPendingTask()) + SetHasTasks(); + } +} + +void MessageLoopTarget::QuitNow() { + MessageLoop::QuitNow(); + loop_.Quit(); +} + +void MessageLoopTarget::StopWatching(int id) { + // The dispatch code for watch callbacks requires this be called on the + // same thread as the message loop is. + FXL_DCHECK(Current() == this); + + std::lock_guard<std::mutex> guard(mutex_); + + auto found = watches_.find(id); + FXL_DCHECK(found != watches_.end()); + + WatchInfo& info = found->second; + // BufferedFD constantly creates and destroys FD handles, flooding the log + // with non-helpful logging statements. + if (info.type != WatchType::kFdio) { + DEBUG_LOG(MessageLoop) << "Stop watching " << WatchTypeToString(info.type) + << " " << info.resource_name; } + switch (info.type) { + case WatchType::kProcessExceptions: { + RemoveExceptionHandler(info.exception_handler_key); + RemoveSignalHandler(info.signal_handler_key); + break; + } + case WatchType::kJobExceptions: { + RemoveExceptionHandler(info.exception_handler_key); + break; + } + case WatchType::kTask: + case WatchType::kFdio: + case WatchType::kSocket: + RemoveSignalHandler(info.signal_handler_key); + break; + } + watches_.erase(found); +} + +void MessageLoopTarget::SetHasTasks() { task_event_.signal(0, kTaskSignal); } + +void MessageLoopTarget::OnFdioSignal(int watch_id, const WatchInfo& info, + zx_signals_t observed) { + uint32_t events = 0; + fdio_unsafe_wait_end(info.fdio, observed, &events); + + if ((events & POLLERR) || (events & POLLHUP) || (events & POLLNVAL) || + (events & POLLRDHUP)) { + info.fd_watcher->OnFDReady(info.fd, false, false, true); + + // Don't dispatch any other notifications when there's an error. Zircon + // seems to set readable and writable on error even if there's nothing + // there. + return; + } + + bool readable = !!(events & POLLIN); + bool writable = !!(events & POLLOUT); + info.fd_watcher->OnFDReady(info.fd, readable, writable, false); +} + +void MessageLoopTarget::RemoveSignalHandler(const async_wait_t* key) { + FXL_DCHECK(key); + size_t erase_count = signal_handlers_.erase(key); + FXL_DCHECK(erase_count == 1u); +} + +void MessageLoopTarget::RemoveExceptionHandler(const async_exception_t* key) { + FXL_DCHECK(key); + size_t erase_count = exception_handlers_.erase(key); + FXL_DCHECK(erase_count == 1u); +} + +void MessageLoopTarget::AddException(const ExceptionHandler& handler, + zx_koid_t thread_koid) { + FXL_DCHECK(thread_exception_map_.find(thread_koid) == + thread_exception_map_.end()); + + Exception exception; + exception.thread_koid = thread_koid; + exception.exception_token = const_cast<async_exception_t*>(handler.handle()); + thread_exception_map_[thread_koid] = std::move(exception); +} + +void MessageLoopTarget::OnProcessException(const ExceptionHandler& handler, + const WatchInfo& info, + const zx_port_packet_t& packet) { + if (ZX_PKT_IS_EXCEPTION(packet.type)) { + // All debug exceptions. + switch (packet.type) { + case ZX_EXCP_THREAD_STARTING: + AddException(handler, packet.exception.tid); + info.exception_watcher->OnThreadStarting(info.task_koid, + packet.exception.tid); + break; + case ZX_EXCP_THREAD_EXITING: + AddException(handler, packet.exception.tid); + info.exception_watcher->OnThreadExiting(info.task_koid, + packet.exception.tid); + break; + case ZX_EXCP_GENERAL: + case ZX_EXCP_FATAL_PAGE_FAULT: + case ZX_EXCP_UNDEFINED_INSTRUCTION: + case ZX_EXCP_SW_BREAKPOINT: + case ZX_EXCP_HW_BREAKPOINT: + case ZX_EXCP_UNALIGNED_ACCESS: + case ZX_EXCP_POLICY_ERROR: + AddException(handler, packet.exception.tid); + info.exception_watcher->OnException(info.task_koid, + packet.exception.tid, packet.type); + break; + default: + FXL_NOTREACHED(); + } + } else { + FXL_NOTREACHED(); + } +} + +void MessageLoopTarget::OnProcessTerminated(const WatchInfo& info, + zx_signals_t observed) { + FXL_DCHECK(observed & ZX_PROCESS_TERMINATED); + info.exception_watcher->OnProcessTerminated(info.task_koid); +} + +void MessageLoopTarget::OnJobException(const ExceptionHandler& handler, + const WatchInfo& info, + const zx_port_packet_t& packet) { + if (ZX_PKT_IS_EXCEPTION(packet.type)) { + // All debug exceptions. + switch (packet.type) { + case ZX_EXCP_PROCESS_STARTING: + AddException(handler, packet.exception.tid); + info.exception_watcher->OnProcessStarting( + info.task_koid, packet.exception.pid, packet.exception.tid); + return; + default: + break; + } + } FXL_NOTREACHED(); - return nullptr; +} + +void MessageLoopTarget::OnSocketSignal(int watch_id, const WatchInfo& info, + zx_signals_t observed) { + // Dispatch readable signal. + if (observed & ZX_SOCKET_READABLE) + info.socket_watcher->OnSocketReadable(info.socket_handle); + + // When signaling both readable and writable, make sure the readable handler + // didn't remove the watch. + if ((observed & ZX_SOCKET_READABLE) && (observed & ZX_SOCKET_WRITABLE)) { + std::lock_guard<std::mutex> guard(mutex_); + if (watches_.find(watch_id) == watches_.end()) + return; + } + + // Dispatch writable signal. + if (observed & ZX_SOCKET_WRITABLE) + info.socket_watcher->OnSocketWritable(info.socket_handle); } const char* WatchTypeToString(WatchType type) { diff --git a/src/developer/debug/shared/message_loop_target.h b/src/developer/debug/shared/message_loop_target.h index 2ca33804bc67d6407be22911b1ed987a9f27ccaa..0d738971d4313a26c97f8a963f1f6c88476c9e39 100644 --- a/src/developer/debug/shared/message_loop_target.h +++ b/src/developer/debug/shared/message_loop_target.h @@ -1,17 +1,26 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #pragma once +#include <lib/async-loop/cpp/loop.h> +#include <lib/fdio/unsafe.h> +#include <lib/zx/event.h> +#include <lib/zx/port.h> #include <lib/zx/thread.h> +#include <vector> + +#include "src/developer/debug/shared/event_handlers.h" #include "src/developer/debug/shared/message_loop.h" namespace debug_ipc { -class ZirconExceptionWatcher; +class ExceptionHandler; +class SignalHandler; class SocketWatcher; +class ZirconExceptionWatcher; enum class WatchType : uint32_t { kTask, @@ -22,39 +31,32 @@ enum class WatchType : uint32_t { }; const char* WatchTypeToString(WatchType); -// MessageLoopTarget is an abstract interface for all message loops that can -// be used in the debug agent. -class MessageLoopTarget : public MessageLoop { +class MessageLoopTarget final : public MessageLoop { public: - // New message loops must be suscribed here. - enum class Type { - kAsync, - kZircon, - kLast, - }; - static const char* TypeToString(Type); - - // Set by a message loop at InitTarget(); - static Type current_message_loop_type; + // Associated struct to track information about what type of resource a watch + // handle is following. + // EventHandlers need access to the WatchInfo implementation, hence the reason + // for it to be public. + // Definition at the end of the header. + struct WatchInfo; - // Target message loops can call wither Init or InitTarget. The difference is - // that InitTarget will return a status about what happened, whether Init - // will silently ignore it. - virtual zx_status_t InitTarget() = 0; + using SignalHandlerMap = std::map<const async_wait_t*, SignalHandler>; + using ExceptionHandlerMap = + std::map<const async_exception_t*, ExceptionHandler>; - virtual ~MessageLoopTarget(); + MessageLoopTarget(); + ~MessageLoopTarget(); - virtual Type GetType() const = 0; + void Init() override; + zx_status_t InitTarget(); - // Fidl requires a special dispatcher to be setup. Not all message loops - // support it. - virtual bool SupportsFidl() const = 0; + void Cleanup() override; // Returns the current message loop or null if there isn't one. static MessageLoopTarget* Current(); // MessageLoop implementation. - WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override = 0; + WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override; // Watches the given socket for read/write status. The watcher must outlive // the returned WatchHandle. Must only be called on the message loop thread. @@ -63,8 +65,8 @@ class MessageLoopTarget : public MessageLoop { // become both readable and writable at the same time which will necessitate // calling both callbacks. The code does not expect the FDWatcher to // disappear in between these callbacks. - virtual zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle, - SocketWatcher* watcher, WatchHandle* out) = 0; + zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle, + SocketWatcher* watcher, WatchHandle* out); // Attaches to the exception port of the given process and issues callbacks // on the given watcher. The watcher must outlive the returned WatchHandle. @@ -75,8 +77,8 @@ class MessageLoopTarget : public MessageLoop { zx_koid_t process_koid; ZirconExceptionWatcher* watcher = nullptr; }; - virtual zx_status_t WatchProcessExceptions(WatchProcessConfig config, - WatchHandle* out) = 0; + zx_status_t WatchProcessExceptions(WatchProcessConfig config, + WatchHandle* out); // Attaches to the exception port of the given job and issues callbacks // on the given watcher. The watcher must outlive the returned WatchHandle. @@ -87,17 +89,120 @@ class MessageLoopTarget : public MessageLoop { zx_koid_t job_koid; ZirconExceptionWatcher* watcher; }; - virtual zx_status_t WatchJobExceptions(WatchJobConfig config, - WatchHandle* out) = 0; + zx_status_t WatchJobExceptions(WatchJobConfig config, WatchHandle* out); // When this class issues an exception notification, the code should call // this function to resume the thread from the exception. This is a wrapper // for zx_task_resume_from_exception or it's async-loop equivalent. // |thread_koid| is needed to identify the exception in some message loop // implementations. - virtual zx_status_t ResumeFromException(zx_koid_t thread_koid, - zx::thread& thread, - uint32_t options) = 0; + zx_status_t ResumeFromException(zx_koid_t thread_koid, zx::thread& thread, + uint32_t options); + + void QuitNow() override; + + const SignalHandlerMap& signal_handlers() const { return signal_handlers_; } + + const ExceptionHandlerMap& exception_handlers() const { + return exception_handlers_; + } + + private: + const WatchInfo* FindWatchInfo(int id) const; + + // MessageLoop protected implementation. + uint64_t GetMonotonicNowNS() const override; + void RunImpl() override; + void StopWatching(int id) override; + // Triggers an event signaling that there is a pending event. + void SetHasTasks() override; + + // Check for any pending C++ tasks and process them. + // Returns true if there was an event pending to be processed. + bool CheckAndProcessPendingTasks(); + + // Handles WatchHandles event. These are all the events that are not C++ tasks + // posted to the message loop. + void HandleException(const ExceptionHandler&, zx_port_packet_t packet); + + // Handle an event of the given type. + void OnFdioSignal(int watch_id, const WatchInfo& info, zx_signals_t observed); + void OnProcessException(const ExceptionHandler&, const WatchInfo& info, + const zx_port_packet_t& packet); + void OnProcessTerminated(const WatchInfo&, zx_signals_t observed); + + void OnJobException(const ExceptionHandler&, const WatchInfo& info, + const zx_port_packet_t& packet); + void OnSocketSignal(int watch_id, const WatchInfo& info, + zx_signals_t observed); + + using WatchMap = std::map<int, WatchInfo>; + WatchMap watches_; + + // ID used as an index into watches_. + int next_watch_id_ = 1; + + async::Loop loop_; + zx::event task_event_; + + SignalHandlerMap signal_handlers_; + // See SignalHandler constructor. + // |associated_info| needs to be updated with the fact that it has an + // associated SignalHandler. + zx_status_t AddSignalHandler(int, zx_handle_t, zx_signals_t, WatchInfo* info); + void RemoveSignalHandler(const async_wait_t* id); + + ExceptionHandlerMap exception_handlers_; + // See ExceptionHandler constructor. + // |associated_info| needs to be updated with the fact that it has an + // associated ExceptionHandler. + zx_status_t AddExceptionHandler(int, zx_handle_t, uint32_t, WatchInfo* info); + void RemoveExceptionHandler(const async_exception_t*); + + // Every exception source (ExceptionHandler) will get an async_exception_t* + // that works as a "key" for the async_loop. This async_exception_t* is what + // you give back to the loop to return from an exception. + // + // So, everytime there is an exception, there needs to be a tracking from + // thread_koid to this async_exception_t*, so that when a thread is resumed, + // we can pass to the loop the correct key by just using the thread koid. + struct Exception; + std::map<zx_koid_t, Exception> thread_exception_map_; + void AddException(const ExceptionHandler&, zx_koid_t thread_koid); + + FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopTarget); + + friend class SignalHandler; + friend class ExceptionHandler; +}; + +// EventHandlers need access to the WatchInfo implementation. +struct MessageLoopTarget::WatchInfo { + // Name of the resource being watched. + // Mostly tracked for debugging purposes. + std::string resource_name; + + WatchType type = WatchType::kFdio; + + // FDIO-specific watcher parameters. + int fd = -1; + fdio_t* fdio = nullptr; + FDWatcher* fd_watcher = nullptr; + zx_handle_t fd_handle = ZX_HANDLE_INVALID; + + // Socket-specific parameters. + SocketWatcher* socket_watcher = nullptr; + zx_handle_t socket_handle = ZX_HANDLE_INVALID; + + // Task-exception-specific parameters, can be of job or process type. + ZirconExceptionWatcher* exception_watcher = nullptr; + zx_koid_t task_koid = 0; + zx_handle_t task_handle = ZX_HANDLE_INVALID; + + // This makes easier the lookup of the associated ExceptionHandler with this + // watch id. + const async_wait_t* signal_handler_key = nullptr; + const async_exception_t* exception_handler_key = nullptr; }; } // namespace debug_ipc diff --git a/src/developer/debug/shared/message_loop_zircon.cc b/src/developer/debug/shared/message_loop_zircon.cc deleted file mode 100644 index c78ecd3e41266d473e60a1c281998fe83b7f422d..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/message_loop_zircon.cc +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/developer/debug/shared/message_loop_zircon.h" - -#include <lib/fdio/io.h> -#include <lib/fdio/unsafe.h> -#include <lib/zx/handle.h> -#include <lib/zx/job.h> -#include <lib/zx/process.h> -#include <zircon/syscalls/exception.h> - -#include "src/developer/debug/shared/fd_watcher.h" -#include "src/developer/debug/shared/socket_watcher.h" -#include "src/developer/debug/shared/zircon_exception_watcher.h" -#include "src/lib/fxl/logging.h" - -namespace debug_ipc { - -namespace { - -// This signal on the task_event_ indicates there is work to do. -constexpr uint32_t kTaskSignal = ZX_USER_SIGNAL_0; - -// 0 is an invalid ID for watchers, so is safe to use here. -constexpr uint64_t kTaskSignalKey = 0; - -thread_local MessageLoopZircon* current_message_loop_zircon = nullptr; - -} // namespace - -// Everything in this class must be simple and copyable since we copy this -// structure for every call (to avoid locking problems). -struct MessageLoopZircon::WatchInfo { - // Mostly for debugging purposes. - std::string resource_name; - - WatchType type = WatchType::kFdio; - - // FDIO-specific watcher parameters. - int fd = -1; - fdio_t* fdio = nullptr; - FDWatcher* fd_watcher = nullptr; - zx_handle_t fd_handle = ZX_HANDLE_INVALID; - - // Socket-specific parameters. - SocketWatcher* socket_watcher = nullptr; - zx_handle_t socket_handle = ZX_HANDLE_INVALID; - - // Task-exception-specific parameters, can be of job or process type. - ZirconExceptionWatcher* exception_watcher = nullptr; - zx_koid_t task_koid = 0; - zx_handle_t task_handle = ZX_HANDLE_INVALID; -}; - -MessageLoopZircon::MessageLoopZircon() { - zx::port::create(0, &port_); - - zx::event::create(0, &task_event_); - task_event_.wait_async(port_, kTaskSignalKey, kTaskSignal, - ZX_WAIT_ASYNC_REPEATING); -} - -MessageLoopZircon::~MessageLoopZircon() { - FXL_DCHECK(Current() != this); // Cleanup should have been called. -} - -void MessageLoopZircon::Init() { InitTarget(); } - -zx_status_t MessageLoopZircon::InitTarget() { - MessageLoop::Init(); - - FXL_DCHECK(!current_message_loop_zircon); - current_message_loop_zircon = this; - MessageLoopTarget::current_message_loop_type = - MessageLoopTarget::Type::kZircon; - - return ZX_OK; -} - -void MessageLoopZircon::Cleanup() { - FXL_DCHECK(current_message_loop_zircon == this); - current_message_loop_zircon = nullptr; - MessageLoopTarget::current_message_loop_type = MessageLoopTarget::Type::kLast; - - MessageLoop::Cleanup(); -} - -void MessageLoopZircon::QuitNow() { MessageLoop::QuitNow(); } - -// static -MessageLoopZircon* MessageLoopZircon::Current() { - return current_message_loop_zircon; -} - -MessageLoop::WatchHandle MessageLoopZircon::WatchFD(WatchMode mode, int fd, - FDWatcher* watcher) { - WatchInfo info; - info.type = WatchType::kFdio; - info.fd_watcher = watcher; - info.fd = fd; - info.fdio = fdio_unsafe_fd_to_io(fd); - if (!info.fdio) - return WatchHandle(); - - uint32_t events = 0; - switch (mode) { - case WatchMode::kRead: - events = POLLIN; - break; - case WatchMode::kWrite: - events = POLLOUT; - break; - case WatchMode::kReadWrite: - events = POLLIN | POLLOUT; - break; - } - - zx_signals_t signals = ZX_SIGNAL_NONE; - fdio_unsafe_wait_begin(info.fdio, events, &info.fd_handle, &signals); - if (info.fd_handle == ZX_HANDLE_INVALID) - return WatchHandle(); - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - if (zx_object_wait_async(info.fd_handle, port_.get(), - static_cast<uint64_t>(watch_id), signals, - ZX_WAIT_ASYNC_REPEATING) != ZX_OK) - return WatchHandle(); - - watches_[watch_id] = info; - } - - return WatchHandle(this, watch_id); -} - -zx_status_t MessageLoopZircon::WatchSocket(WatchMode mode, - zx_handle_t socket_handle, - SocketWatcher* watcher, - MessageLoop::WatchHandle* out) { - WatchInfo info; - info.type = WatchType::kSocket; - info.socket_watcher = watcher; - info.socket_handle = socket_handle; - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - - if (mode == WatchMode::kRead || mode == WatchMode::kReadWrite) { - zx_status_t status = - zx_object_wait_async(socket_handle, port_.get(), watch_id, - ZX_SOCKET_READABLE, ZX_WAIT_ASYNC_REPEATING); - if (status != ZX_OK) - return status; - } - - if (mode == WatchMode::kWrite || mode == WatchMode::kReadWrite) { - zx_status_t status = - zx_object_wait_async(socket_handle, port_.get(), watch_id, - ZX_SOCKET_WRITABLE, ZX_WAIT_ASYNC_REPEATING); - if (status != ZX_OK) - return status; - } - - watches_[watch_id] = info; - } - - *out = WatchHandle(this, watch_id); - return ZX_OK; -} - -zx_status_t MessageLoopZircon::WatchProcessExceptions( - WatchProcessConfig config, MessageLoop::WatchHandle* out) { - WatchInfo info; - info.type = WatchType::kProcessExceptions; - info.resource_name = config.process_name; - info.exception_watcher = config.watcher; - info.task_koid = config.process_koid; - info.task_handle = config.process_handle; - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - - // Bind to the exception port. - zx_status_t status = - zx_task_bind_exception_port(config.process_handle, port_.get(), - watch_id, ZX_EXCEPTION_PORT_DEBUGGER); - if (status != ZX_OK) - return status; - - // Also watch for process termination. - status = - zx_object_wait_async(config.process_handle, port_.get(), watch_id, - ZX_PROCESS_TERMINATED, ZX_WAIT_ASYNC_REPEATING); - if (status != ZX_OK) - return status; - - watches_[watch_id] = info; - } - - *out = WatchHandle(this, watch_id); - return ZX_OK; -} - -zx_status_t MessageLoopZircon::WatchJobExceptions( - WatchJobConfig config, MessageLoop::WatchHandle* out) { - WatchInfo info; - info.type = WatchType::kJobExceptions; - info.resource_name = config.job_name; - info.exception_watcher = config.watcher; - info.task_koid = config.job_koid; - info.task_handle = config.job_handle; - - int watch_id; - { - std::lock_guard<std::mutex> guard(mutex_); - - watch_id = next_watch_id_; - next_watch_id_++; - - // Bind to the exception port. - zx_status_t status = zx_task_bind_exception_port( - config.job_handle, port_.get(), watch_id, ZX_EXCEPTION_PORT_DEBUGGER); - if (status != ZX_OK) - return status; - - watches_[watch_id] = info; - } - - *out = WatchHandle(this, watch_id); - return ZX_OK; -} - -// |thread_koid| is unused in this message loop. -zx_status_t MessageLoopZircon::ResumeFromException(zx_koid_t, - zx::thread& thread, - uint32_t options) { - return thread.resume_from_exception(port_, options); -} - -bool MessageLoopZircon::CheckAndProcessPendingTasks() { - std::lock_guard<std::mutex> guard(mutex_); - // Do a C++ task. - if (ProcessPendingTask()) { - SetHasTasks(); // Enqueue another task signal. - return true; - } - return false; -} - -void MessageLoopZircon::HandleException(zx_port_packet_t packet) { - WatchInfo* watch_info = nullptr; - { - // Some event being watched. - std::lock_guard<std::mutex> guard(mutex_); - auto found = watches_.find(packet.key); - if (found == watches_.end()) { - // It is possible to get an exception that doesn't have a watch handle. - // A case is a race between detaching from a process and getting an - // exception on that process. - // - // The normal process looks like this: - // - // 1. In order to correctly detach, the debug agent has to resume threads - // from their exceptions. Otherwise that exception will be treated as - // unhandled when the agent detaches and will bubble up. - // 2. The agent detaches from the exception port. This means that the - // watch handle is no longer listening. - // - // It is possible between (1) and (2) to get an exception, which will be - // queued in the exception port of the thread. Now, the agent won't read - // from the port until *after* it has detached from the exception port. - // This means that this new exception is not handled and will be bubbled - // up, which is correct as the debug agent stated that it has nothing more - // to do with the process. - // - // Now the problem is that zircon does not clean stale packets from a - // queue, meaning that the next time the message loop waits on the port, - // it will find a stale packet. In this context a stale packet means one - // that does not have a watch handle, as it was deleted in (1). Hence we - // get into this case and we simply log it for posperity. - // - // TODO(zX-2623): zircon is going to clean up stale packets from ports - // in the future. When that is done, this case should not - // happen and we should go back into asserting it. - FXL_LOG(WARNING) << "Got stale port packet. This is most probably due to " - "a race between detaching from a process and an " - "exception ocurring."; - return; - } - watch_info = &found->second; - } - - // Dispatch the watch callback outside of the lock. This depends on the - // stability of the WatchInfo pointer in the map (std::map is stable across - // updates) and the watch not getting unregistered from another thread - // asynchronously (which the API requires and is enforced by a DCHECK in - // the StopWatching impl). - switch (watch_info->type) { - case WatchType::kFdio: - OnFdioSignal(packet.key, *watch_info, packet); - break; - case WatchType::kProcessExceptions: - OnProcessException(*watch_info, packet); - break; - case WatchType::kJobExceptions: - OnJobException(*watch_info, packet); - break; - case WatchType::kSocket: - OnSocketSignal(packet.key, *watch_info, packet); - break; - default: - FXL_NOTREACHED(); - } -} - -uint64_t MessageLoopZircon::GetMonotonicNowNS() const { - zx::time ret; - zx::clock::get(&ret); - - return ret.get(); -} - -// Previously, the approach was to first look for C++ tasks and when handled -// look for WatchHandle work and finally wait for an event. This worked because -// handle events didn't post C++ tasks. -// -// But some tests do post tasks on handle events. Because C++ tasks are signaled -// by explicitly signaling an zx::event, without manually checking, the C++ -// tasks will never be checked and we would get blocked until a watch handled -// is triggered. -// -// In order to handle the events properly, we need to check for C++ tasks before -// and *after* handling watch handle events. This way we always process C++ -// tasks before handle events and will get signaled if one of them posted a new -// task. -void MessageLoopZircon::RunImpl() { - // Init should have been called. - FXL_DCHECK(Current() == this); - - zx::time time; - uint64_t delay = DelayNS(); - if (delay == MessageLoop::kMaxDelay) { - time = zx::time::infinite(); - } else { - time = zx::deadline_after(zx::nsec(delay)); - } - - zx_port_packet_t packet; - while (!should_quit() && port_.wait(time, &packet) == ZX_OK) { - // We check first for pending C++ tasks. If an event was handled, it will - // signal the associated zx::event in order to trigger the port once more - // (this is the way we process an enqueued event). If there is no enqueued - // event, we won't trigger the event and go back to wait on the port. - if (packet.key == kTaskSignalKey) { - CheckAndProcessPendingTasks(); - continue; - } - - // If it wasn't a task, we check for what kind of exception it was and - // handle it. - HandleException(packet); - - // The exception handling could have added more pending work, so we have to - // re-check in order to correctly signal for new work. - CheckAndProcessPendingTasks(); - } -} - -void MessageLoopZircon::StopWatching(int id) { - // The dispatch code for watch callbacks requires this be called on the - // same thread as the message loop is. - FXL_DCHECK(Current() == this); - - std::lock_guard<std::mutex> guard(mutex_); - - auto found = watches_.find(id); - if (found == watches_.end()) { - FXL_NOTREACHED(); - return; - } - - WatchInfo& info = found->second; - switch (info.type) { - case WatchType::kFdio: - port_.cancel(*zx::unowned_handle(info.fd_handle), - static_cast<uint64_t>(id)); - break; - case WatchType::kProcessExceptions: { - zx::unowned_process process(info.task_handle); - - // Binding an invalid port will detach from the exception port. - process->bind_exception_port(zx::port(), 0, ZX_EXCEPTION_PORT_DEBUGGER); - // Stop watching for process events. - port_.cancel(*process, id); - break; - } - case WatchType::kJobExceptions: { - zx::unowned_job job(info.task_handle); - // Binding an invalid port will detach from the exception port. - job->bind_exception_port(zx::port(), 0, ZX_EXCEPTION_PORT_DEBUGGER); - // Stop watching for job events. - port_.cancel(*job, id); - break; - } - case WatchType::kSocket: - port_.cancel(*zx::unowned_handle(info.socket_handle), id); - break; - default: - FXL_NOTREACHED(); - break; - } - watches_.erase(found); -} - -void MessageLoopZircon::SetHasTasks() { task_event_.signal(0, kTaskSignal); } - -void MessageLoopZircon::OnFdioSignal(int watch_id, const WatchInfo& info, - const zx_port_packet_t& packet) { - uint32_t events = 0; - fdio_unsafe_wait_end(info.fdio, packet.signal.observed, &events); - - if ((events & POLLERR) || (events & POLLHUP) || (events & POLLNVAL) || - (events & POLLRDHUP)) { - info.fd_watcher->OnFDReady(info.fd, false, false, true); - - // Don't dispatch any other notifications when there's an error. Zircon - // seems to set readable and writable on error even if there's nothing - // there. - return; - } - - info.fd_watcher->OnFDReady(info.fd, !!(events & POLLIN), !!(events & POLLOUT), - false); -} - -void MessageLoopZircon::OnProcessException(const WatchInfo& info, - const zx_port_packet_t& packet) { - if (ZX_PKT_IS_EXCEPTION(packet.type)) { - // All debug exceptions. - switch (packet.type) { - case ZX_EXCP_THREAD_STARTING: - info.exception_watcher->OnThreadStarting(info.task_koid, - packet.exception.tid); - break; - case ZX_EXCP_THREAD_EXITING: - info.exception_watcher->OnThreadExiting(info.task_koid, - packet.exception.tid); - break; - case ZX_EXCP_GENERAL: - case ZX_EXCP_FATAL_PAGE_FAULT: - case ZX_EXCP_UNDEFINED_INSTRUCTION: - case ZX_EXCP_SW_BREAKPOINT: - case ZX_EXCP_HW_BREAKPOINT: - case ZX_EXCP_UNALIGNED_ACCESS: - case ZX_EXCP_POLICY_ERROR: - info.exception_watcher->OnException(info.task_koid, - packet.exception.tid, packet.type); - break; - default: - FXL_NOTREACHED(); - } - } else if (ZX_PKT_IS_SIGNAL_REP(packet.type) && - packet.signal.observed & ZX_PROCESS_TERMINATED) { - // This type of watcher also gets process terminated signals. - info.exception_watcher->OnProcessTerminated(info.task_koid); - } else { - FXL_NOTREACHED(); - } -} - -void MessageLoopZircon::OnJobException(const WatchInfo& info, - const zx_port_packet_t& packet) { - if (ZX_PKT_IS_EXCEPTION(packet.type)) { - // All debug exceptions. - switch (packet.type) { - case ZX_EXCP_PROCESS_STARTING: - info.exception_watcher->OnProcessStarting( - info.task_koid, packet.exception.pid, packet.exception.tid); - break; - default: - FXL_NOTREACHED(); - } - } else { - FXL_NOTREACHED(); - } -} - -void MessageLoopZircon::OnSocketSignal(int watch_id, const WatchInfo& info, - const zx_port_packet_t& packet) { - if (!ZX_PKT_IS_SIGNAL_REP(packet.type)) - return; - - auto observed = packet.signal.observed; - - // See if the socket was closed. - if ((observed & ZX_SOCKET_PEER_CLOSED) || - (observed & ZX_SIGNAL_HANDLE_CLOSED)) { - info.socket_watcher->OnSocketError(info.socket_handle); - // |info| is can be deleted at this point, so don't use it anymore. - return; - } - - // Dispatch readable signal. - if (observed & ZX_SOCKET_READABLE) - info.socket_watcher->OnSocketReadable(info.socket_handle); - - // When signaling both readable and writable, make sure the readable handler - // didn't remove the watch. - if ((observed & ZX_SOCKET_READABLE) && (observed & ZX_SOCKET_WRITABLE)) { - std::lock_guard<std::mutex> guard(mutex_); - if (watches_.find(packet.key) == watches_.end()) - return; - } - - // Dispatch writable signal. - if (observed & ZX_SOCKET_WRITABLE) - info.socket_watcher->OnSocketWritable(info.socket_handle); -} - -} // namespace debug_ipc diff --git a/src/developer/debug/shared/message_loop_zircon.h b/src/developer/debug/shared/message_loop_zircon.h deleted file mode 100644 index e0704060968ccfcd5eadd066c247311b2422b38a..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/message_loop_zircon.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include <lib/fdio/unsafe.h> -#include <lib/zx/event.h> -#include <lib/zx/port.h> -#include <lib/zx/thread.h> - -#include "src/developer/debug/shared/message_loop_target.h" - -namespace debug_ipc { - -class ZirconExceptionWatcher; -class SocketWatcher; - -class MessageLoopZircon : public MessageLoopTarget { - public: - MessageLoopZircon(); - ~MessageLoopZircon(); - - void Init() override; - zx_status_t InitTarget() override; - - void Cleanup() override; - void QuitNow() override; - - Type GetType() const override { return Type::kZircon; } - bool SupportsFidl() const override { return false; } - - // Returns the current message loop or null if there isn't one. - static MessageLoopZircon* Current(); - - // MessageLoop implementation. - WatchHandle WatchFD(WatchMode mode, int fd, FDWatcher* watcher) override; - - zx_status_t WatchSocket(WatchMode mode, zx_handle_t socket_handle, - SocketWatcher* watcher, WatchHandle* out) override; - - zx_status_t WatchProcessExceptions(WatchProcessConfig config, - WatchHandle* out) override; - - zx_status_t WatchJobExceptions(WatchJobConfig config, - WatchHandle* out) override; - - zx_status_t ResumeFromException(zx_koid_t thread_koid, zx::thread& thread, - uint32_t options) override; - - private: - // Associated struct to track information about what type of resource a watch - // handle is following. - struct WatchInfo; - - // MessageLoop protected implementation. - uint64_t GetMonotonicNowNS() const override; - void RunImpl() override; - void StopWatching(int id) override; - // Triggers an event signaling that there is a pending event. - void SetHasTasks() override; - - // Check for any pending C++ tasks and process them. - // Returns true if there was an event pending to be processed. - bool CheckAndProcessPendingTasks(); - - // Handles WatchHandles event. These are all the events that are not C++ - // tasks posted to the message loop. - void HandleException(zx_port_packet_t packet); - - // Handle an event of the given type. - void OnFdioSignal(int watch_id, const WatchInfo& info, - const zx_port_packet_t& packet); - void OnProcessException(const WatchInfo& info, - const zx_port_packet_t& packet); - void OnJobException(const WatchInfo& info, const zx_port_packet_t& packet); - void OnSocketSignal(int watch_id, const WatchInfo& info, - const zx_port_packet_t& packet); - - using WatchMap = std::map<int, WatchInfo>; - WatchMap watches_; - - // ID used as an index into watches_. - int next_watch_id_ = 1; - - zx::port port_; - - // This event is signaled when there are tasks to process. - zx::event task_event_; - - FXL_DISALLOW_COPY_AND_ASSIGN(MessageLoopZircon); -}; - -} // namespace debug_ipc