blob: cbe574938dc498b32752c944a4f21fb975f08010 [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/memory/metrics/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 {
class OSImpl : public OS, public TaskEnumerator {
private:
zx_status_t GetRootResource(zx_handle_t* root_resource) override {
const char* sysinfo = "/dev/misc/sysinfo";
int fd = open(sysinfo, O_RDWR);
if (fd < 0) {
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) {
return status;
}
zx_status_t fidl_status = fuchsia_sysinfo_DeviceGetRootResource(
channel.get(), &status, root_resource);
if (fidl_status != ZX_OK) {
return fidl_status;
}
return status;
}
zx_handle_t ProcessSelf() override { return zx_process_self(); }
zx_time_t GetMonotonic() override { return zx_clock_get_monotonic(); }
zx_status_t GetProcesses(
fit::function<zx_status_t(int, zx_handle_t, zx_koid_t, zx_koid_t)> cb)
override {
cb_ = std::move(cb);
return WalkRootJobTree();
}
zx_status_t OnProcess(int depth, zx_handle_t handle, zx_koid_t koid,
zx_koid_t parent_koid) override {
return cb_(depth, handle, koid, parent_koid);
}
zx_status_t GetProperty(
zx_handle_t handle, uint32_t property, void* value, size_t name_len)
override {
return zx_object_get_property(handle, property, value, name_len);
}
zx_status_t GetInfo(
zx_handle_t handle,
uint32_t topic,
void* buffer,
size_t buffer_size,
size_t* actual,
size_t* avail) override {
return zx_object_get_info(
handle, topic, buffer, buffer_size, actual, avail);
}
bool has_on_process() const final { return true; }
fit::function<zx_status_t(
int /* depth */,
zx_handle_t /* handle */,
zx_koid_t /* koid */,
zx_koid_t /* parent_koid */)> cb_;
};
// static.
zx_status_t Capture::GetCaptureState(CaptureState& state) {
OSImpl osImpl;
return GetCaptureState(state, osImpl);
}
zx_status_t Capture::GetCaptureState(CaptureState& state, OS& os) {
zx_status_t err = os.GetRootResource(&state.root);
if (err != ZX_OK) {
return err;
}
zx_info_handle_basic_t info;
err = os.GetInfo(os.ProcessSelf(), 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) {
OSImpl osImpl;
return GetCapture(capture, state, level, osImpl);
}
zx_status_t Capture::GetCapture(
Capture& capture,
const CaptureState& state,
CaptureLevel level,
OS& os) {
capture.time_ = os.GetMonotonic();
zx_status_t err = os.GetInfo(
state.root, ZX_INFO_KMEM_STATS, &capture.kmem_, sizeof(capture.kmem_),
nullptr, nullptr);
if (err != ZX_OK) {
return err;
}
if (level == KMEM) {
return ZX_OK;
}
err = os.GetProcesses([state, &capture, level, &os](
int depth,
zx_handle_t handle,
zx_koid_t koid,
zx_koid_t parent_koid) {
Process process = { .koid = koid };
zx_status_t s = os.GetProperty(
handle, ZX_PROP_NAME, process.name, ZX_MAX_NAME_LEN);
if (s != ZX_OK) {
return s == ZX_ERR_BAD_STATE ? ZX_OK : s;
}
s = os.GetInfo(handle, ZX_INFO_TASK_STATS,
&process.stats, sizeof(process.stats),
nullptr, nullptr);
if (s != ZX_OK) {
return s == ZX_ERR_BAD_STATE ? ZX_OK : s;
}
if (level == PROCESS) {
capture.koid_to_process_.emplace(koid, process);
return ZX_OK;
}
if (koid == state.self_koid) {
return ZX_OK;
}
size_t num_vmos;
s = os.GetInfo(handle, ZX_INFO_PROCESS_VMOS,
nullptr, 0, nullptr, &num_vmos);
if (s != ZX_OK) {
return s == ZX_ERR_BAD_STATE ? ZX_OK : s;
}
auto vmos = new zx_info_vmo_t[num_vmos];
s = os.GetInfo(handle, ZX_INFO_PROCESS_VMOS,
vmos, num_vmos * sizeof(zx_info_vmo_t),
&num_vmos, nullptr);
if (s != ZX_OK) {
delete[] vmos;
return s == ZX_ERR_BAD_STATE ? ZX_OK : 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;
});
return err;
}
} // namespace memory