blob: 585aa52f2d18e6b66f94f757bc7a15df8ba6a28e [file] [log] [blame]
// 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/debug_agent/object_provider.h"
#include <fcntl.h>
#include <fuchsia/boot/c/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <zircon/syscalls/object.h>
#include "src/developer/debug/shared/zx_status.h"
#include "src/lib/files/file.h"
#include "src/lib/fxl/logging.h"
namespace debug_agent {
namespace {
template <typename ResultObject>
std::vector<ResultObject> GetChildObjects(const ObjectProvider& provider, zx_handle_t parent,
uint32_t child_kind) {
auto koids = provider.GetChildKoids(parent, child_kind);
std::vector<ResultObject> result;
result.reserve(koids.size());
for (zx_koid_t koid : koids) {
zx_handle_t handle;
if (zx_object_get_child(parent, koid, ZX_RIGHT_SAME_RIGHTS, &handle) == ZX_OK)
result.push_back(ResultObject(handle));
}
return result;
}
// Searches the process tree rooted at "job" for a process with the given
// koid. If found, puts it in *out* and returns true.
bool FindProcess(const ObjectProvider& provider, const zx::job& job, zx_koid_t search_for,
zx::process* out) {
for (auto& proc : provider.GetChildProcesses(job.get())) {
if (provider.KoidForObject(proc) == search_for) {
*out = std::move(proc);
return true;
}
}
for (const auto& job : provider.GetChildJobs(job.get())) {
if (FindProcess(provider, job, search_for, out))
return true;
}
return false;
}
// Searches root job for a job with the given
// koid. If found, puts it in *out* and returns true.
bool FindJob(const ObjectProvider& provider, zx::job root_job, zx_koid_t search_for, zx::job* out) {
if (provider.KoidForObject(root_job) == search_for) {
out->reset(root_job.release());
return true;
}
auto child_jobs = provider.GetChildJobs(root_job.get());
for (auto& child_job : child_jobs) {
if (FindJob(provider, zx::job(child_job.release()), search_for, out))
return true;
}
return false;
}
} // namespace
ObjectProvider::ObjectProvider() = default;
ObjectProvider::~ObjectProvider() = default;
zx::thread ObjectProvider::ThreadForKoid(zx_handle_t process, zx_koid_t thread_koid) const {
zx_handle_t thread_handle = ZX_HANDLE_INVALID;
if (zx_object_get_child(process, thread_koid, ZX_RIGHT_SAME_RIGHTS, &thread_handle) != ZX_OK)
return zx::thread();
return zx::thread(thread_handle);
}
zx_koid_t ObjectProvider::KoidForObject(zx_handle_t object) const {
zx_info_handle_basic_t info;
if (zx_object_get_info(object, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr) !=
ZX_OK)
return 0;
return info.koid;
}
std::string ObjectProvider::NameForObject(zx_handle_t object) const {
char name[ZX_MAX_NAME_LEN];
if (zx_object_get_property(object, ZX_PROP_NAME, name, sizeof(name)) == ZX_OK)
return std::string(name);
return std::string();
}
std::vector<zx_koid_t> ObjectProvider::GetChildKoids(zx_handle_t parent,
uint32_t child_kind) const {
constexpr size_t kNumExtraKoids = 10u;
size_t actual = 0;
size_t available = 0;
std::vector<zx_koid_t> result;
// This is inherently racy, but we retry once with a bit of slop to try to
// get a complete list.
for (int pass = 0; pass < 2; pass++) {
if (actual < available)
result.resize(available + kNumExtraKoids);
zx_status_t status = zx_object_get_info(parent, child_kind, result.data(),
result.size() * sizeof(zx_koid_t), &actual, &available);
if (status != ZX_OK || actual == available)
break;
}
result.resize(actual);
return result;
}
zx_status_t ObjectProvider::GetChild(zx_handle_t parent, zx_koid_t koid, uint32_t rights,
zx_handle_t* child) const {
return zx_object_get_child(parent, koid, ZX_RIGHT_SAME_RIGHTS, child);
}
std::vector<zx::job> ObjectProvider::GetChildJobs(zx_handle_t job) const {
return GetChildObjects<zx::job>(*this, job, ZX_INFO_JOB_CHILDREN);
}
std::vector<zx::process> ObjectProvider::GetChildProcesses(zx_handle_t job) const {
return GetChildObjects<zx::process>(*this, job, ZX_INFO_JOB_PROCESSES);
}
std::vector<zx::thread> ObjectProvider::GetChildThreads(zx_handle_t process) const {
return GetChildObjects<zx::thread>(*this, process, ZX_INFO_PROCESS_THREADS);
}
zx::process ObjectProvider::GetProcessFromException(zx_handle_t exception) const {
zx_handle_t process_handle = ZX_HANDLE_INVALID;
zx_status_t status = zx_exception_get_process(exception, &process_handle);
FXL_DCHECK(status == ZX_OK) << "Got: " << debug_ipc::ZxStatusToString(status);
return zx::process(process_handle);
}
zx::thread ObjectProvider::GetThreadFromException(zx_handle_t exception) const {
zx_handle_t thread_handle = ZX_HANDLE_INVALID;
zx_status_t status = zx_exception_get_thread(exception, &thread_handle);
FXL_DCHECK(status == ZX_OK) << "Got: " << debug_ipc::ZxStatusToString(status);
return zx::thread(thread_handle);
}
zx::process ObjectProvider::GetProcessFromKoid(zx_koid_t koid) const {
zx::process result;
FindProcess(*this, GetRootJob(), koid, &result);
return result;
}
zx::job ObjectProvider::GetJobFromKoid(zx_koid_t koid) const {
zx::job result;
FindJob(*this, GetRootJob(), koid, &result);
return result;
}
zx_koid_t ObjectProvider::GetRootJobKoid() const { return KoidForObject(GetRootJob()); }
// The hub writes the job it uses to create components in a special file.
//
// This is note quite correct. This code actually returns the job that contains
// the debug agent itself, which is usually the right thing because the debug
// agent normally runs in the component root.
//
// TODO: Find the correct job even when the debug agent is run from elsewhere.
zx_koid_t ObjectProvider::GetComponentJobKoid() const {
std::string koid_str;
bool file_read = files::ReadFileToString("/hub/job-id", &koid_str);
if (!file_read) {
FXL_LOG(ERROR) << "Not able to read job-id: " << strerror(errno);
return 0;
}
char* end = NULL;
uint64_t koid = strtoul(koid_str.c_str(), &end, 10);
if (*end) {
FXL_LOG(ERROR) << "Invalid job-id: " << koid_str.c_str();
return 0;
}
return koid;
}
// TODO(brettw) this is based on the code in Zircon's task-utils which uses
// this hack to get the root job handle. It will likely need to be updated
// when a better way to get the root job is found.
zx::job ObjectProvider::GetRootJob() const {
int fd = open("/svc/fuchsia.boot.RootJob", O_RDWR);
if (fd < 0) {
FXL_NOTREACHED();
return zx::job();
}
zx::channel channel;
zx_status_t status = fdio_get_service_handle(fd, channel.reset_and_get_address());
if (status != ZX_OK) {
FXL_NOTREACHED();
return zx::job();
}
zx_handle_t root_job;
zx_status_t fidl_status = fuchsia_boot_RootJobGet(channel.get(), &root_job);
if (fidl_status != ZX_OK) {
FXL_NOTREACHED();
return zx::job();
}
return zx::job(root_job);
}
zx_status_t ObjectProvider::ListHandleRights(zx_handle_t handle, std::string* out) const {
zx_info_handle_basic info;
zx_status_t status =
zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK)
return status;
// clang-format off
std::stringstream ss;
if (info.rights & ZX_RIGHT_DUPLICATE) { ss << "ZX_RIGHT_DUPLICATE" << std::endl; }
if (info.rights & ZX_RIGHT_TRANSFER) { ss << "ZX_RIGHT_TRANSFER" << std::endl; }
if (info.rights & ZX_RIGHT_READ) { ss << "ZX_RIGHT_READ" << std::endl; }
if (info.rights & ZX_RIGHT_WRITE) { ss << "ZX_RIGHT_WRITE" << std::endl; }
if (info.rights & ZX_RIGHT_EXECUTE) { ss << "ZX_RIGHT_EXECUTE" << std::endl; }
if (info.rights & ZX_RIGHT_MAP) { ss << "ZX_RIGHT_MAP" << std::endl; }
if (info.rights & ZX_RIGHT_GET_PROPERTY) { ss << "ZX_RIGHT_GET_PROPERTY" << std::endl; }
if (info.rights & ZX_RIGHT_SET_PROPERTY) { ss << "ZX_RIGHT_SET_PROPERTY" << std::endl; }
if (info.rights & ZX_RIGHT_ENUMERATE) { ss << "ZX_RIGHT_ENUMERATE" << std::endl; }
if (info.rights & ZX_RIGHT_DESTROY) { ss << "ZX_RIGHT_DESTROY" << std::endl; }
if (info.rights & ZX_RIGHT_SET_POLICY) { ss << "ZX_RIGHT_SET_POLICY" << std::endl; }
if (info.rights & ZX_RIGHT_GET_POLICY) { ss << "ZX_RIGHT_GET_POLICY" << std::endl; }
if (info.rights & ZX_RIGHT_SIGNAL) { ss << "ZX_RIGHT_SIGNAL" << std::endl; }
if (info.rights & ZX_RIGHT_SIGNAL_PEER) { ss << "ZX_RIGHT_SIGNAL_PEER" << std::endl; }
if (info.rights & ZX_RIGHT_WAIT) { ss << "ZX_RIGHT_WAIT" << std::endl; }
if (info.rights & ZX_RIGHT_INSPECT) { ss << "ZX_RIGHT_INSPECT" << std::endl; }
if (info.rights & ZX_RIGHT_MANAGE_JOB) { ss << "ZX_RIGHT_MANAGE_JOB" << std::endl; }
if (info.rights & ZX_RIGHT_MANAGE_PROCESS) { ss << "ZX_RIGHT_MANAGE_PROCESS" << std::endl; }
if (info.rights & ZX_RIGHT_MANAGE_THREAD) { ss << "ZX_RIGHT_MANAGE_THREAD" << std::endl; }
if (info.rights & ZX_RIGHT_APPLY_PROFILE) { ss << "ZX_RIGHT_APPLY_PROFILE" << std::endl; }
if (info.rights & ZX_RIGHT_SAME_RIGHTS) { ss << "ZX_RIGHT_SAME_RIGHTS" << std::endl; }
// clang-format on
*out = ss.str();
return ZX_OK;
}
zx_status_t ObjectProvider::Kill(zx_handle_t handle) { return zx_task_kill(handle); }
} // namespace debug_agent