blob: 9266c4daac4b0dfc92f4187d7d1cbd6e8a74ad10 [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 "garnet/bin/memory_monitor/capture.h"
#include <fcntl.h>
#include <fuchsia/sysinfo/c/fidl.h>
#include <lib/zx/channel.h>
#include <lib/fdio/fdio.h>
#include <memory>
#include <src/lib/fxl/logging.h>
#include <task-utils/walker.h>
#include <zircon/process.h>
#include <zircon/status.h>
namespace memory {
namespace {
zx_status_t get_root_resource(zx_handle_t* root_resource) {
const char* sysinfo = "/dev/misc/sysinfo";
int fd = open(sysinfo, O_RDWR);
if (fd < 0) {
FXL_LOG(ERROR) << "Cannot open sysinfo: " << strerror(errno);
return ZX_ERR_NOT_FOUND;
}
zx::channel channel;
zx_status_t status =
fdio_get_service_handle(fd, channel.reset_and_get_address());
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Cannot obtain sysinfo channel: "
<< zx_status_get_string(status);
return status;
}
zx_status_t fidl_status = fuchsia_sysinfo_DeviceGetRootResource(
channel.get(), &status, root_resource);
if (fidl_status != ZX_OK) {
FXL_LOG(ERROR) << "Cannot obtain root resource: "
<< zx_status_get_string(fidl_status);
return fidl_status;
} else if (status != ZX_OK) {
FXL_LOG(ERROR) << "Cannot obtain root resource: "
<< zx_status_get_string(status);
return status;
}
return ZX_OK;
}
} // namespace
class Capture::ProcessGetter final : public TaskEnumerator {
public:
ProcessGetter(CaptureLevel level, zx_koid_t self_koid, Capture& capture)
: level_(level), self_koid_(self_koid), capture_(capture) {}
zx_status_t OnProcess(
int depth,
zx_handle_t handle,
zx_koid_t koid,
zx_koid_t parent_koid) override {
Process process = { .koid = koid };
zx_status_t s = zx_object_get_property(
handle, ZX_PROP_NAME, process.name, ZX_MAX_NAME_LEN);
if (s != ZX_OK) {
return s;
}
s = zx_object_get_info(handle, ZX_INFO_TASK_STATS,
&process.stats, sizeof(process.stats),
nullptr, nullptr);
if (s != ZX_OK) {
return s;
}
if (level_ == PROCESS) {
return ZX_OK;
}
if (koid == self_koid_) {
return ZX_OK;
}
size_t num_vmos;
s = zx_object_get_info(handle, ZX_INFO_PROCESS_VMOS,
nullptr, 0, nullptr, &num_vmos);
if (s != ZX_OK) {
return s;
}
zx_info_vmo_t* vmos = new zx_info_vmo_t[num_vmos];
s = zx_object_get_info(handle, ZX_INFO_PROCESS_VMOS,
vmos, num_vmos * sizeof(zx_info_vmo_t),
&num_vmos, nullptr);
if (s != ZX_OK) {
delete[] vmos;
return s;
}
process.vmos.reserve(num_vmos);
for (size_t i = 0; i < num_vmos; i++) {
zx_koid_t vmo_koid = vmos[i].koid;
capture_.koid_to_vmo_.emplace(vmo_koid, vmos[i]);
process.vmos.push_back(vmo_koid);
}
delete[] vmos;
capture_.koid_to_process_.emplace(koid, process);
return ZX_OK;
}
private:
CaptureLevel level_;
zx_koid_t self_koid_;
Capture& capture_;
bool has_on_process() const final { return true; }
};
// static.
zx_status_t Capture::GetCaptureState(CaptureState& state) {
zx_status_t err = get_root_resource(&state.root);
if (err != ZX_OK) {
return err;
}
zx_info_handle_basic_t info;
err = zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC,
&info, sizeof(info), nullptr, nullptr);
if (err != ZX_OK) {
return err;
}
state.self_koid = info.koid;
return ZX_OK;
}
// static.
zx_status_t Capture::GetCapture(
Capture& capture, const CaptureState& state, CaptureLevel level) {
capture.time_ = zx_clock_get_monotonic();
zx_status_t err = zx_object_get_info(
state.root, ZX_INFO_KMEM_STATS,
&capture.kmem_, sizeof(capture.kmem_),
NULL, NULL);
if (err != ZX_OK) {
return err;
}
if (level == KMEM) {
return ZX_OK;
}
ProcessGetter getter(level, state.self_koid, capture);
err = getter.WalkRootJobTree();
if (err != ZX_OK) {
return err;
}
return ZX_OK;
}
} // namespace memory