blob: 584f514adad2e858079910473c43b5c23f4577c5 [file] [log] [blame]
// Copyright 2019 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/debug_agent/limbo_provider.h"
#include <zircon/status.h>
#include "src/developer/debug/debug_agent/object_provider.h"
using namespace fuchsia::exception;
namespace debug_agent {
// LimboProvider -----------------------------------------------------------------------------------
LimboProvider::LimboProvider(std::shared_ptr<sys::ServiceDirectory> services)
: services_(std::move(services)) {}
LimboProvider::~LimboProvider() = default;
zx_status_t LimboProvider::Init() {
// We get the initial state of the hanging gets.
ProcessLimboSyncPtr process_limbo;
zx_status_t status = services_->Connect(process_limbo.NewRequest());
if (status != ZX_OK)
return status;
// Check if the limbo is active.
bool is_limbo_active = false;
status = process_limbo->WatchActive(&is_limbo_active);
if (status != ZX_OK)
return status;
is_limbo_active_ = is_limbo_active;
if (is_limbo_active_) {
// Get the current set of process in exceptions.
ProcessLimbo_WatchProcessesWaitingOnException_Result result;
status = process_limbo->WatchProcessesWaitingOnException(&result);
if (status != ZX_OK)
return status;
if (result.is_err())
return result.err();
// Add to the exceptions.
for (auto& exception : result.response().exception_list) {
zx_koid_t process_koid = exception.info().process_koid;
limbo_[process_koid] = std::move(exception);
}
}
// Now that we were able to get the current state of the limbo, we move to an async binding.
connection_.Bind(process_limbo.Unbind().TakeChannel());
// |this| owns the connection, so it's guaranteed to outlive it.
connection_.set_error_handler([this](zx_status_t status) {
FXL_LOG(ERROR) << "Got error on limbo: " << zx_status_get_string(status);
Reset();
});
WatchActive();
WatchLimbo();
valid_ = true;
return ZX_OK;
}
void LimboProvider::Reset() {
valid_ = false;
limbo_.clear();
is_limbo_active_ = false;
connection_ = {};
}
void LimboProvider::WatchActive() {
// |this| owns the connection, so it's guaranteed to outlive it.
connection_->WatchActive([this](bool is_active) {
if (!is_active)
limbo_.clear();
is_limbo_active_ = is_active;
// Re-issue the hanging get.
WatchActive();
});
}
bool LimboProvider::Valid() const { return valid_; }
const std::map<zx_koid_t, fuchsia::exception::ProcessExceptionMetadata>& LimboProvider::Limbo()
const {
return limbo_;
}
// WatchLimbo --------------------------------------------------------------------------------------
namespace {
ProcessExceptionMetadata DuplicateException(const ProcessExceptionMetadata& exception) {
ProcessExceptionMetadata result = {};
result.set_info(exception.info());
if (exception.has_process()) {
zx::process process;
exception.process().duplicate(ZX_RIGHT_SAME_RIGHTS, &process);
result.set_process(std::move(process));
}
if (exception.has_thread()) {
zx::thread thread;
exception.thread().duplicate(ZX_RIGHT_SAME_RIGHTS, &thread);
result.set_thread(std::move(thread));
}
return result;
}
} // namespace
void LimboProvider::WatchLimbo() {
connection_->WatchProcessesWaitingOnException(
// |this| owns the connection, so it's guaranteed to outlive it.
[this](ProcessLimbo_WatchProcessesWaitingOnException_Result result) {
if (result.is_err()) {
FXL_LOG(ERROR) << "Got error waiting on limbo: " << zx_status_get_string(result.err());
} else {
// We need to figure out which exceptions remain.
// Add the exceptions to the limbo.
std::vector<fuchsia::exception::ProcessExceptionMetadata> new_exceptions;
std::map<zx_koid_t, fuchsia::exception::ProcessExceptionMetadata> new_limbo;
for (auto& exception : result.response().exception_list) {
// We check to see if it's currently on the limbo. If it wasn't, it is a new exception.
zx_koid_t process_koid = exception.info().process_koid;
if (auto it = limbo_.find(process_koid); it == limbo_.end())
new_exceptions.push_back(DuplicateException(exception));
new_limbo.insert({process_koid, std::move(exception)});
}
limbo_ = std::move(new_limbo);
if (!new_exceptions.empty() && on_enter_limbo_)
on_enter_limbo_(std::move(new_exceptions));
}
// Re-issue the hanging get.
WatchLimbo();
});
}
zx_status_t LimboProvider::RetrieveException(zx_koid_t process_koid,
fuchsia::exception::ProcessException* out) {
ProcessLimboSyncPtr process_limbo;
if (zx_status_t status = services_->Connect(process_limbo.NewRequest()); status != ZX_OK)
return status;
ProcessLimbo_RetrieveException_Result result = {};
if (zx_status_t status = process_limbo->RetrieveException(process_koid, &result);
status != ZX_OK) {
return status;
}
if (result.is_err())
return result.err();
*out = std::move(result.response().ResultValue_());
return ZX_OK;
}
zx_status_t LimboProvider::ReleaseProcess(zx_koid_t process_koid) {
ProcessLimboSyncPtr process_limbo;
if (zx_status_t status = services_->Connect(process_limbo.NewRequest()); status != ZX_OK)
return status;
ProcessLimbo_ReleaseProcess_Result result;
if (zx_status_t status = process_limbo->ReleaseProcess(process_koid, &result); status != ZX_OK)
return status;
if (result.is_err())
return result.err();
limbo_.erase(process_koid);
return ZX_OK;
}
} // namespace debug_agent