blob: 18db6f0fa1fac5e621fcdb8890549aba6116fabb [file] [log] [blame]
// Copyright 2022 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/process_explorer/process_explorer.h"
#include <fuchsia/kernel/cpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <inspector/inspector.h>
#include <task-utils/walker.h>
#include "src/developer/process_explorer/utils.h"
#include "src/lib/fsl/socket/strings.h"
namespace process_explorer {
namespace {
class ProcessWalker : public TaskEnumerator {
public:
ProcessWalker() = default;
~ProcessWalker() = default;
zx_status_t WalkProcessTree(std::vector<Process>* processes) {
FX_CHECK(processes_.empty());
if (auto status = WalkRootJobTree(); status != ZX_OK) {
FX_LOGS(ERROR) << "Unable to walk job tree: " << zx_status_get_string(status);
return status;
}
*processes = std::move(processes_);
processes_.clear();
return ZX_OK;
}
zx_status_t OnProcess(int depth, zx_handle_t process_handle, zx_koid_t koid,
zx_koid_t parent_koid) override {
zx::unowned_process process(process_handle);
char name[ZX_MAX_NAME_LEN];
if (auto status = process->get_property(ZX_PROP_NAME, &name, sizeof(name)); status != ZX_OK) {
FX_LOGS(ERROR) << "Unable to get process name: " << zx_status_get_string(status);
return status;
}
std::vector<zx_info_handle_extended_t> handles;
if (auto status = GetHandles(std::move(process), &handles); status != ZX_OK) {
FX_LOGS(ERROR) << "Unable to get associated handles for process: "
<< zx_status_get_string(status);
return status;
}
std::vector<KernelObject> process_objects;
for (auto const& handle : handles) {
process_objects.push_back(
{handle.type, handle.koid, handle.related_koid, handle.peer_owner_koid});
}
std::string process_name(name);
processes_.push_back({koid, process_name, process_objects});
return ZX_OK;
}
protected:
bool has_on_process() const override { return true; }
private:
std::vector<Process> processes_;
};
zx_status_t GetProcessesData(std::vector<Process>* processes_data) {
ProcessWalker process_walker;
if (auto status = process_walker.WalkProcessTree(processes_data); status != ZX_OK) {
FX_LOGS(ERROR) << "Unable to get processes data: " << zx_status_get_string(status);
return status;
}
// TODO(https://fxbug.dev/42138321): Remove call to FillPeerOwnerKoid (and remove
// FillPeerOwnerKoid itself) after peer owner koids become populated by
// the kernel.
FillPeerOwnerKoid(*processes_data);
return ZX_OK;
}
} // namespace
Explorer::Explorer(async_dispatcher_t* dispatcher, component::OutgoingDirectory& outgoing) {
ZX_ASSERT(outgoing
.AddUnmanagedProtocol<fuchsia_process_explorer::Query>(
query_bindings_.CreateHandler(this, dispatcher, fidl::kIgnoreBindingClosure))
.is_ok());
ZX_ASSERT(outgoing
.AddUnmanagedProtocol<fuchsia_process_explorer::ProcessExplorer>(
explorer_bindings_.CreateHandler(this, dispatcher, fidl::kIgnoreBindingClosure))
.is_ok());
}
Explorer::~Explorer() = default;
void Explorer::WriteJsonProcessesData(WriteJsonProcessesDataRequest& request,
WriteJsonProcessesDataCompleter::Sync& completer) {
std::vector<Process> processes_data;
if (auto status = GetProcessesData(&processes_data); status != ZX_OK) {
// Returning immediately. Nothing will have been written on the socket which will let the client
// know an error has occurred.
return;
}
const std::string json_string = WriteProcessesDataAsJson(std::move(processes_data));
// TODO(https://fxbug.dev/42059896): change to asynchronous
fsl::BlockingCopyFromString(json_string, request.socket());
}
void Explorer::GetTaskInfo(GetTaskInfoRequest& request, GetTaskInfoCompleter::Sync& completer) {
// TODO(surajmalhtora): Implement.
}
void Explorer::GetHandleInfo(GetHandleInfoRequest& request,
GetHandleInfoCompleter::Sync& completer) {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void Explorer::GetVmaps(GetVmapsRequest& request, GetVmapsCompleter::Sync& completer) {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
class StackTraceWalker final : public TaskEnumerator {
public:
StackTraceWalker() = default;
~StackTraceWalker() = default;
zx::result<std::string> GetStackTrace(std::variant<zx_koid_t, std::string> sought_task) {
sought_task_ = std::move(sought_task);
if (auto status = WalkRootJobTree(); status != ZX_ERR_STOP) {
if (status == ZX_OK) {
return zx::error(ZX_ERR_NOT_FOUND);
}
return zx::error(status);
}
return zx::ok(std::move(stack_trace_));
}
zx_status_t OnProcess(int depth, zx_handle_t process_handle, zx_koid_t koid,
zx_koid_t parent_koid) override {
zx::unowned_process process(process_handle);
if (zx_koid_t* sought_task_koid = std::get_if<zx_koid_t>(&sought_task_); sought_task_koid) {
if (*sought_task_koid != koid) {
return ZX_OK;
}
} else {
auto& sought_process_name = std::get<std::string>(sought_task_);
char name[ZX_MAX_NAME_LEN];
if (auto status = process->get_property(ZX_PROP_NAME, &name, std::size(name));
status != ZX_OK) {
FX_LOGS(ERROR) << "Unable to get process name: " << zx_status_get_string(status);
return status;
}
if (sought_process_name != name) {
return ZX_OK;
}
}
char* stack_trace_buffer;
size_t stack_trace_buffer_size;
FILE* file = open_memstream(&stack_trace_buffer, &stack_trace_buffer_size);
inspector_print_debug_info_for_all_threads(file, process->get());
fclose(file);
stack_trace_ = std::string(stack_trace_buffer, stack_trace_buffer_size);
free(stack_trace_buffer);
return ZX_ERR_STOP;
}
protected:
bool has_on_process() const override { return true; }
private:
std::variant<zx_koid_t, std::string> sought_task_;
std::string stack_trace_;
};
void Explorer::GetStackTrace(GetStackTraceRequest& request,
GetStackTraceCompleter::Sync& completer) {
StackTraceWalker walker;
std::variant<zx_koid_t, std::string> task;
if (request.koid().has_value()) {
task = request.koid().value();
} else if (request.process_name().has_value()) {
task = request.process_name().value();
} else {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
return;
}
completer.Reply(walker.GetStackTrace(task));
}
class KillWalker final : public TaskEnumerator {
public:
KillWalker() = default;
~KillWalker() = default;
zx::result<zx_koid_t> KillTask(std::variant<zx_koid_t, std::string> task) {
task_to_kill_ = std::move(task);
if (auto status = WalkRootJobTree(); status != ZX_ERR_STOP) {
if (status == ZX_OK) {
return zx::error(ZX_ERR_NOT_FOUND);
}
return zx::error(status);
}
return zx::ok(task_killed_.value());
}
zx_status_t OnProcess(int depth, zx_handle_t process_handle, zx_koid_t koid,
zx_koid_t parent_koid) override {
zx::unowned_process process(process_handle);
if (auto task_koid = std::get_if<zx_koid_t>(&task_to_kill_); task_koid) {
if (*task_koid != koid) {
return ZX_OK;
}
} else {
auto& process_name = std::get<std::string>(task_to_kill_);
char name[ZX_MAX_NAME_LEN];
if (auto status = process->get_property(ZX_PROP_NAME, &name, sizeof(name)); status != ZX_OK) {
FX_LOGS(ERROR) << "Unable to get process name: " << zx_status_get_string(status);
return status;
}
if (process_name != name) {
return ZX_OK;
}
}
zx_status_t status = process->kill();
if (status != ZX_OK) {
return status;
}
task_killed_ = koid;
return ZX_ERR_STOP;
}
zx_status_t OnJob(int depth, zx_handle_t job_handle, zx_koid_t koid,
zx_koid_t parent_koid) override {
zx::unowned_job job(job_handle);
if (auto* task_koid = std::get_if<zx_koid_t>(&task_to_kill_);
!task_koid || *task_koid != koid) {
return ZX_OK;
}
zx_status_t status = job->kill();
if (status != ZX_OK) {
return status;
}
task_killed_ = koid;
return ZX_ERR_STOP;
}
protected:
bool has_on_job() const override { return true; }
bool has_on_process() const override { return true; }
private:
std::variant<zx_koid_t, std::string> task_to_kill_;
std::optional<zx_koid_t> task_killed_;
};
void Explorer::KillTask(KillTaskRequest& request, KillTaskCompleter::Sync& completer) {
KillWalker walker;
std::variant<zx_koid_t, std::string> task;
if (request.koid().has_value()) {
task = request.koid().value();
} else if (request.process_name().has_value()) {
task = request.process_name().value();
} else {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
return;
}
completer.Reply(walker.KillTask(task));
}
} // namespace process_explorer