blob: ccd9359492bfeb532386c05bc95bb714244b12de [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 "task_tree.h"
#include <fuchsia/kernel/c/fidl.h>
#include <inttypes.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fdio.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/channel.h>
#include <zircon/status.h>
#include "os.h"
namespace harvester {
const size_t kNumInitialKoids = 128;
zx_status_t TaskTree::GetHandleForChildKoid(zx_koid_t child_koid,
zx_handle_t parent,
zx_koid_t parent_koid,
zx_handle_t* child_handle) {
auto it = koids_to_handles_.find(child_koid);
if (it != koids_to_handles_.end()) {
*child_handle = it->second;
stale_koids_to_handles_.erase(child_koid);
return ZX_OK;
}
zx_status_t status = zx_object_get_child(parent, child_koid,
ZX_RIGHT_SAME_RIGHTS, child_handle);
if (status != ZX_OK) {
FX_LOGS(WARNING) << "zx_object_get_child(" << parent_koid << ", (job)"
<< child_koid
<< ", ...) failed: " << zx_status_get_string(status)
<< " (" << status << ")";
} else {
koids_to_handles_.insert(
std::pair<zx_koid_t, zx_handle_t>(child_koid, *child_handle));
stale_koids_to_handles_.erase(child_koid);
}
return status;
}
void TaskTree::GatherThreadsForProcess(zx_handle_t parent_process,
zx_koid_t parent_process_koid) {
zx_status_t status;
// Get the koids for the threads belonging to this process.
std::vector<zx_koid_t> koids(kNumInitialKoids);
status = os_->GetChildren(parent_process, parent_process_koid,
ZX_INFO_PROCESS_THREADS, "ZX_INFO_PROCESS_THREADS",
koids);
if (status != ZX_OK) {
return;
}
for (zx_koid_t koid : koids) {
zx_handle_t next_thread_handle;
status = GetHandleForChildKoid(koid, parent_process, parent_process_koid,
&next_thread_handle);
if (status == ZX_OK) {
// Store the thread / koid / parent process triple.
threads_.emplace_back(next_thread_handle, koid, parent_process_koid);
}
}
}
void TaskTree::GatherProcessesForJob(zx_handle_t parent_job,
zx_koid_t parent_job_koid) {
zx_status_t status;
// Get the koids for the processes under this job.
std::vector<zx_koid_t> koids(kNumInitialKoids);
status = os_->GetChildren(parent_job, parent_job_koid, ZX_INFO_JOB_PROCESSES,
"ZX_INFO_JOB_PROCESSES", koids);
if (status != ZX_OK) {
return;
}
for (zx_koid_t koid : koids) {
zx_handle_t next_process_handle;
status = GetHandleForChildKoid(koid, parent_job, parent_job_koid,
&next_process_handle);
if (status == ZX_OK) {
// Store the process / koid / parent job triple.
processes_.emplace_back(next_process_handle, koid, parent_job_koid);
// Gather the process's threads.
GatherThreadsForProcess(next_process_handle, koid);
}
}
}
void TaskTree::GatherProcessesAndJobsForJob(zx_handle_t parent_job,
zx_koid_t parent_job_koid) {
zx_status_t status;
// Gather the job's processes.
GatherProcessesForJob(parent_job, parent_job_koid);
// Get the koids for the child jobs under this job.
std::vector<zx_koid_t> koids(kNumInitialKoids);
status = os_->GetChildren(parent_job, parent_job_koid, ZX_INFO_JOB_CHILDREN,
"ZX_INFO_JOB_CHILDREN", koids);
if (status != ZX_OK) {
return;
}
for (zx_koid_t koid : koids) {
zx_handle_t child_job_handle;
status = GetHandleForChildKoid(koid, parent_job, parent_job_koid,
&child_job_handle);
if (status == ZX_OK) {
// Store the child job / koid / parent job triple.
jobs_.emplace_back(child_job_handle, koid, parent_job_koid);
// Gather the job's processes and child jobs.
GatherProcessesAndJobsForJob(child_job_handle, koid);
}
}
}
void TaskTree::GatherJobs() {
zx::channel local, remote;
zx_status_t status = zx::channel::create(0, &local, &remote);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Could not create channel";
return;
}
status =
fdio_service_connect("/svc/fuchsia.kernel.RootJob", remote.release());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot open fuchsia.kernel.RootJob: "
<< zx_status_get_string(status);
return;
}
zx_handle_t root_job;
zx_koid_t root_job_koid = 0;
auto it = koids_to_handles_.find(root_job_koid);
if (it != koids_to_handles_.end()) {
root_job = it->second;
} else {
zx_status_t fidl_status = fuchsia_kernel_RootJobGet(local.get(), &root_job);
if (fidl_status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot obtain root job";
return;
}
koids_to_handles_.insert(
std::pair<zx_koid_t, zx_handle_t>(root_job_koid, root_job));
}
// We will rebuild these data structures as we walk the job tree.
jobs_.clear();
processes_.clear();
threads_.clear();
stale_koids_to_handles_.insert(koids_to_handles_.begin(),
koids_to_handles_.end());
stale_koids_to_handles_.erase(root_job_koid);
// Store the root job node.
jobs_.emplace_back(root_job, root_job_koid, root_job_koid);
// Gather the root job's processes and jobs.
GatherProcessesAndJobsForJob(root_job, root_job_koid);
for (auto& [koid, handle] : stale_koids_to_handles_) {
koids_to_handles_.erase(koid);
zx_handle_close(handle);
}
stale_koids_to_handles_.clear();
}
void TaskTree::Gather() { GatherJobs(); }
void TaskTree::Clear() {
// Close all open handles.
for (auto& koid_handle_pair : koids_to_handles_) {
zx_handle_close(koid_handle_pair.second);
}
koids_to_handles_.clear();
jobs_.clear();
processes_.clear();
threads_.clear();
}
} // namespace harvester