[memory] Don't fail entirely when a process goes away.
Right now we exit entirely if a process disappears in the middle
of interrogation for vmos. This change instead ignores the process
for that capture.
Test: Added Capture unittests, including ones for these cases.
Change-Id: I8e5a5703a786549ca0a43a2173eb4f4c4d5e69b3
diff --git a/src/developer/memory/metrics/capture.cc b/src/developer/memory/metrics/capture.cc
index d199145..cbe5749 100644
--- a/src/developer/memory/metrics/capture.cc
+++ b/src/developer/memory/metrics/capture.cc
@@ -15,114 +15,87 @@
#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;
- }
-
+class OSImpl : public OS, public TaskEnumerator {
private:
- CaptureLevel level_;
- zx_koid_t self_koid_;
- Capture& capture_;
+ 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) {
- zx_status_t err = get_root_resource(&state.root);
+ 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 = zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC,
- &info, sizeof(info), nullptr, nullptr);
+ err = os.GetInfo(os.ProcessSelf(), ZX_INFO_HANDLE_BASIC,
+ &info, sizeof(info), nullptr, nullptr);
if (err != ZX_OK) {
return err;
}
@@ -132,13 +105,21 @@
}
// 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) {
- 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);
+ 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;
}
@@ -147,12 +128,58 @@
return ZX_OK;
}
- ProcessGetter getter(level, state.self_koid, capture);
- err = getter.WalkRootJobTree();
- if (err != ZX_OK) {
- return err;
- }
- 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
diff --git a/src/developer/memory/metrics/capture.h b/src/developer/memory/metrics/capture.h
index 24b890b..4bf8fa4 100644
--- a/src/developer/memory/metrics/capture.h
+++ b/src/developer/memory/metrics/capture.h
@@ -5,8 +5,9 @@
#ifndef SRC_DEVELOPER_MEMORY_METRICS_CAPTURE_H_
#define SRC_DEVELOPER_MEMORY_METRICS_CAPTURE_H_
-#include <src/lib/fxl/macros.h>
+#include <lib/fit/function.h>
#include <lib/zx/time.h>
+#include <src/lib/fxl/macros.h>
#include <unordered_map>
#include <vector>
#include <zircon/syscalls.h>
@@ -28,6 +29,28 @@
zx_koid_t self_koid;
};
+class OS {
+ public:
+ virtual zx_status_t GetRootResource(zx_handle_t* root_resource) = 0;
+ virtual zx_handle_t ProcessSelf() = 0;
+ virtual zx_time_t GetMonotonic() = 0;
+ virtual zx_status_t GetProcesses(
+ fit::function<zx_status_t(
+ int /* depth */,
+ zx_handle_t /* handle */,
+ zx_koid_t /* koid */,
+ zx_koid_t /* parent_koid */)> cb) = 0;
+ virtual zx_status_t GetProperty(
+ zx_handle_t handle, uint32_t property, void* value, size_t name_len) = 0;
+ virtual zx_status_t GetInfo(
+ zx_handle_t handle,
+ uint32_t topic,
+ void* buffer,
+ size_t buffer_size,
+ size_t* actual,
+ size_t* avail) = 0;
+};
+
class Capture {
public:
static zx_status_t GetCaptureState(CaptureState& state);
@@ -55,6 +78,9 @@
}
private:
+ static zx_status_t GetCaptureState(CaptureState& state, OS& os);
+ static zx_status_t GetCapture(
+ Capture& capture, const CaptureState& state, CaptureLevel level, OS& os);
zx_time_t time_;
zx_info_kmem_stats_t kmem_;
std::unordered_map<zx_koid_t, const Process> koid_to_process_;
diff --git a/src/developer/memory/metrics/tests/BUILD.gn b/src/developer/memory/metrics/tests/BUILD.gn
index 55106f2..a0a29bf3 100644
--- a/src/developer/memory/metrics/tests/BUILD.gn
+++ b/src/developer/memory/metrics/tests/BUILD.gn
@@ -14,6 +14,7 @@
testonly = true
output_name = "memory_metrics_unittests"
sources = [
+ "capture_unittest.cc",
"printer_unittest.cc",
"summary_unittest.cc",
"test_utils.cc",
diff --git a/src/developer/memory/metrics/tests/capture_unittest.cc b/src/developer/memory/metrics/tests/capture_unittest.cc
new file mode 100644
index 0000000..067f064
--- /dev/null
+++ b/src/developer/memory/metrics/tests/capture_unittest.cc
@@ -0,0 +1,270 @@
+// 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 <gtest/gtest.h>
+#include <lib/gtest/test_loop_fixture.h>
+#include <zircon/types.h>
+
+#include "src/developer/memory/metrics/capture.h"
+#include "src/developer/memory/metrics/tests/test_utils.h"
+
+namespace memory {
+namespace test {
+
+using CaptureUnitTest = gtest::TestLoopFixture;
+
+const static zx_info_kmem_stats _kmem = {
+ .total_bytes = 300,
+ .free_bytes = 100,
+ .wired_bytes = 10,
+ .total_heap_bytes = 20,
+ .free_heap_bytes = 30,
+ .vmo_bytes = 40,
+ .mmu_overhead_bytes = 50,
+ .ipc_bytes = 60,
+ .other_bytes = 70
+};
+const static GetInfoResponse kmem_info = {
+ TestUtils::kRootHandle, ZX_INFO_KMEM_STATS, &_kmem, sizeof(_kmem), 1, ZX_OK
+};
+
+const static zx_info_handle_basic_t _self = {.koid = TestUtils::kSelfKoid };
+const static GetInfoResponse self_info = {
+ TestUtils::kSelfHandle, ZX_INFO_HANDLE_BASIC, &_self, sizeof(_self), 1, ZX_OK
+};
+
+const zx_koid_t proc_koid = 10;
+const zx_handle_t proc_handle = 100;
+const char proc_name[] = "P1";
+const static zx_info_task_stats _proc = {};
+const static GetInfoResponse proc_info =
+ {proc_handle, ZX_INFO_TASK_STATS, &_proc, sizeof(_proc), 1, ZX_OK};
+const static GetPropertyResponse proc_prop =
+ {proc_handle, ZX_PROP_NAME, proc_name, sizeof(proc_name), ZX_OK};
+const static GetProcessesCallback proc_cb =
+ {1, proc_handle, proc_koid, 0};
+
+const zx_koid_t proc2_koid = 20;
+const zx_handle_t proc2_handle = 200;
+const char proc2_name[] = "P2";
+const static zx_info_task_stats _proc2 = {};
+const static GetInfoResponse proc2_info =
+ {proc2_handle, ZX_INFO_TASK_STATS, &_proc2, sizeof(_proc2), 1, ZX_OK};
+const static GetPropertyResponse proc2_prop =
+ {proc2_handle, ZX_PROP_NAME, proc2_name, sizeof(proc2_name), ZX_OK};
+const static GetProcessesCallback proc2_cb =
+ {1, proc2_handle, proc2_koid, 0};
+
+const zx_koid_t vmo_koid = 1000;
+const uint64_t vmo_size = 10000;
+const char vmo_name[] = "V1";
+const static zx_info_vmo_t _vmo = {
+ .koid = vmo_koid,
+ .name = "V1",
+ .size_bytes = vmo_size,
+};
+const static GetInfoResponse vmos_info =
+ {proc_handle, ZX_INFO_PROCESS_VMOS, &_vmo, sizeof(_vmo), 1, ZX_OK};
+
+const zx_koid_t vmo2_koid = 2000;
+const uint64_t vmo2_size = 20000;
+const char vmo2_name[] = "V2";
+const static zx_info_vmo_t _vmo2 = {
+ .koid = vmo2_koid,
+ .name = "V2",
+ .size_bytes = vmo2_size,
+};
+const static GetInfoResponse vmos2_info =
+ {proc2_handle, ZX_INFO_PROCESS_VMOS, &_vmo2, sizeof(_vmo2), 1, ZX_OK};
+
+TEST_F(CaptureUnitTest, KMEM) {
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, KMEM, {
+ .get_info = {self_info, kmem_info},
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ auto const& got_kmem = c.kmem();
+ EXPECT_EQ(_kmem.total_bytes, got_kmem.total_bytes);
+}
+
+TEST_F(CaptureUnitTest, PROCESS) {
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, PROCESS, {
+ .get_info = {self_info, kmem_info, proc_info},
+ .get_processes = {{ZX_OK, {proc_cb}}},
+ .get_property = {proc_prop}
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(1U, c.koid_to_process().size());
+ auto const& process = c.process_for_koid(proc_koid);
+ EXPECT_EQ(proc_koid, process.koid);
+ EXPECT_STREQ(proc_name, process.name);
+}
+
+TEST_F(CaptureUnitTest, VMO) {
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, VMO, {
+ .get_info = {self_info, kmem_info, proc_info, vmos_info, vmos_info},
+ .get_processes = {{ZX_OK, {proc_cb}}},
+ .get_property = {proc_prop}
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(1U, c.koid_to_process().size());
+ auto const& process = c.process_for_koid(proc_koid);
+ EXPECT_EQ(proc_koid, process.koid);
+ EXPECT_STREQ(proc_name, process.name);
+ EXPECT_EQ(1U, process.vmos.size());
+ EXPECT_EQ(1U, c.koid_to_vmo().size());
+ EXPECT_EQ(vmo_koid, process.vmos[0]);
+ auto const& vmo = c.vmo_for_koid(vmo_koid);
+ EXPECT_EQ(vmo_koid, vmo.koid);
+ EXPECT_EQ(vmo_size, vmo.size_bytes);
+ EXPECT_STREQ(vmo_name, vmo.name);
+}
+
+TEST_F(CaptureUnitTest, VMODouble) {
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, VMO, {
+ .get_info = {
+ self_info,
+ kmem_info,
+ proc_info,
+ vmos_info,
+ vmos_info,
+ proc2_info,
+ vmos2_info,
+ vmos2_info,
+ },
+ .get_processes = {{ZX_OK, {proc_cb, proc2_cb}}},
+ .get_property = {proc_prop, proc2_prop}
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(2U, c.koid_to_process().size());
+ EXPECT_EQ(2U, c.koid_to_vmo().size());
+
+ auto const& process = c.process_for_koid(proc_koid);
+ EXPECT_EQ(proc_koid, process.koid);
+ EXPECT_STREQ(proc_name, process.name);
+ EXPECT_EQ(1U, process.vmos.size());
+ EXPECT_EQ(vmo_koid, process.vmos[0]);
+ auto const& vmo = c.vmo_for_koid(vmo_koid);
+ EXPECT_EQ(vmo_koid, vmo.koid);
+ EXPECT_EQ(vmo_size, vmo.size_bytes);
+ EXPECT_STREQ(vmo_name, vmo.name);
+
+ auto const& process2 = c.process_for_koid(proc2_koid);
+ EXPECT_EQ(proc2_koid, process2.koid);
+ EXPECT_STREQ(proc2_name, process2.name);
+ EXPECT_EQ(1U, process2.vmos.size());
+ EXPECT_EQ(vmo2_koid, process2.vmos[0]);
+ auto const& vmo2 = c.vmo_for_koid(vmo2_koid);
+ EXPECT_EQ(vmo2_koid, vmo2.koid);
+ EXPECT_EQ(vmo2_size, vmo2.size_bytes);
+ EXPECT_STREQ(vmo2_name, vmo2.name);
+}
+
+TEST_F(CaptureUnitTest, ProcessPropBadState) {
+ // If the process disappears we should ignore it and continue.
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, PROCESS, {
+ .get_info = {self_info, kmem_info, proc2_info},
+ .get_processes = {{ZX_OK, {proc_cb, proc2_cb}}},
+ .get_property = {
+ {proc_handle, ZX_PROP_NAME, nullptr, 0, ZX_ERR_BAD_STATE},
+ proc2_prop
+ }
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(1U, c.koid_to_process().size());
+ auto const& process = c.process_for_koid(proc2_koid);
+ EXPECT_EQ(proc2_koid, process.koid);
+ EXPECT_STREQ(proc2_name, process.name);
+}
+
+TEST_F(CaptureUnitTest, ProcessInfoBadState) {
+ // If the process disappears we should ignore it and continue.
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, PROCESS, {
+ .get_info = {
+ self_info,
+ kmem_info,
+ {proc_handle, ZX_INFO_TASK_STATS,
+ &_proc, sizeof(_proc), 1, ZX_ERR_BAD_STATE},
+ proc2_info
+ },
+ .get_processes = {{ZX_OK, {proc_cb, proc2_cb}}},
+ .get_property = {proc_prop, proc2_prop},
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(1U, c.koid_to_process().size());
+ auto const& process = c.process_for_koid(proc2_koid);
+ EXPECT_EQ(proc2_koid, process.koid);
+ EXPECT_STREQ(proc2_name, process.name);
+}
+
+TEST_F(CaptureUnitTest, VMOCountBadState) {
+ // If the process disappears we should ignore it and continue.
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, VMO, {
+ .get_info = {
+ self_info,
+ kmem_info,
+ proc_info,
+ {proc_handle, ZX_INFO_PROCESS_VMOS,
+ &_vmo, sizeof(_vmo), 1, ZX_ERR_BAD_STATE},
+ proc2_info,
+ vmos2_info,
+ vmos2_info
+ },
+ .get_processes = {{ZX_OK, {proc_cb, proc2_cb}}},
+ .get_property = {proc_prop, proc2_prop}
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(1U, c.koid_to_process().size());
+ auto const& process = c.process_for_koid(proc2_koid);
+ EXPECT_EQ(proc2_koid, process.koid);
+ EXPECT_STREQ(proc2_name, process.name);
+ EXPECT_EQ(1U, process.vmos.size());
+ EXPECT_EQ(1U, c.koid_to_vmo().size());
+ EXPECT_EQ(vmo2_koid, process.vmos[0]);
+ auto const& vmo = c.vmo_for_koid(vmo2_koid);
+ EXPECT_EQ(vmo2_koid, vmo.koid);
+ EXPECT_EQ(vmo2_size, vmo.size_bytes);
+ EXPECT_STREQ(vmo2_name, vmo.name);
+}
+
+TEST_F(CaptureUnitTest, VMOGetBadState) {
+ // If the process disappears we should ignore it and continue.
+ Capture c;
+ auto ret = TestUtils::GetCapture(c, VMO, {
+ .get_info = {
+ self_info,
+ kmem_info,
+ proc_info,
+ vmos_info,
+ {proc_handle, ZX_INFO_PROCESS_VMOS,
+ &_vmo, sizeof(_vmo), 1, ZX_ERR_BAD_STATE},
+ proc2_info,
+ vmos2_info,
+ vmos2_info
+ },
+ .get_processes = {{ZX_OK, {proc_cb, proc2_cb}}},
+ .get_property = {proc_prop, proc2_prop}
+ });
+ EXPECT_EQ(ZX_OK, ret);
+ EXPECT_EQ(1U, c.koid_to_process().size());
+ auto const& process = c.process_for_koid(proc2_koid);
+ EXPECT_EQ(proc2_koid, process.koid);
+ EXPECT_STREQ(proc2_name, process.name);
+ EXPECT_EQ(1U, process.vmos.size());
+ EXPECT_EQ(1U, c.koid_to_vmo().size());
+ EXPECT_EQ(vmo2_koid, process.vmos[0]);
+ auto const& vmo = c.vmo_for_koid(vmo2_koid);
+ EXPECT_EQ(vmo2_koid, vmo.koid);
+ EXPECT_EQ(vmo2_size, vmo.size_bytes);
+ EXPECT_STREQ(vmo2_name, vmo.name);
+}
+
+} // namespace test
+} // namespace memory
diff --git a/src/developer/memory/metrics/tests/test_utils.cc b/src/developer/memory/metrics/tests/test_utils.cc
index d614c07..dc8ba1a 100644
--- a/src/developer/memory/metrics/tests/test_utils.cc
+++ b/src/developer/memory/metrics/tests/test_utils.cc
@@ -4,8 +4,94 @@
#include "src/developer/memory/metrics/tests/test_utils.h"
+#include <gtest/gtest.h>
+
namespace memory {
+const zx_handle_t TestUtils::kRootHandle = 1;
+const zx_handle_t TestUtils::kSelfHandle = 2;
+const zx_koid_t TestUtils::kSelfKoid = 3;
+
+class MockOS : public OS {
+ public:
+ MockOS(OsResponses responses)
+ : responses_(responses),
+ i_get_processes_(0),
+ i_get_property_(0),
+ i_get_info_(0),
+ clock_(0) {}
+
+ private:
+ zx_status_t GetRootResource(zx_handle_t* root_resource) override {
+ *root_resource = TestUtils::kRootHandle;
+ return ZX_OK;
+ }
+
+ zx_handle_t ProcessSelf() override {
+ return TestUtils::kSelfHandle;
+ }
+
+ zx_time_t GetMonotonic() override {
+ return clock_++;
+ }
+
+ zx_status_t GetProcesses(
+ fit::function<zx_status_t(int, zx_handle_t, zx_koid_t, zx_koid_t)> cb)
+ override {
+ auto const& r = responses_.get_processes.at(i_get_processes_++);
+ for (auto const& c : r.callbacks) {
+ auto ret = cb(c.depth, c.handle, c.koid, c.parent_koid);
+ if (ret != ZX_OK) {
+ return ret;
+ }
+ }
+ return r.ret;
+ }
+
+ zx_status_t GetProperty(
+ zx_handle_t handle, uint32_t property, void* value, size_t name_len)
+ override {
+ auto const& r = responses_.get_property.at(i_get_property_++);
+ EXPECT_EQ(r.handle, handle);
+ EXPECT_EQ(r.property, property);
+ auto len = std::min(name_len, r.value_len);
+ memcpy(value, r.value, len);
+ return r.ret;
+ }
+
+ zx_status_t GetInfo(
+ zx_handle_t handle,
+ uint32_t topic,
+ void* buffer,
+ size_t buffer_size,
+ size_t* actual,
+ size_t* avail) override {
+ auto const& r = responses_.get_info.at(i_get_info_++);
+ EXPECT_EQ(r.handle, handle);
+ EXPECT_EQ(r.topic, topic);
+ size_t num_copied = 0;
+ if (buffer != nullptr) {
+ num_copied = std::min(r.value_count, buffer_size / r.value_size);
+ memcpy(buffer, r.values, num_copied * r.value_size);
+ }
+ if (actual != nullptr) {
+ *actual = num_copied;
+ }
+ if (avail != nullptr) {
+ if (num_copied < r.value_count) {
+ *avail = r.value_count - num_copied;
+ } else {
+ *avail = 0;
+ }
+ }
+ return r.ret;
+ }
+
+ OsResponses responses_;
+ uint32_t i_get_processes_, i_get_property_, i_get_info_;
+ zx_time_t clock_;
+};
+
// static.
void TestUtils::CreateCapture(memory::Capture& capture,
const CaptureTemplate& t) {
@@ -27,4 +113,14 @@
[](ProcessSummary a, ProcessSummary b) { return a.koid() < b.koid(); } );
return summaries;
}
+
+zx_status_t TestUtils::GetCapture(
+ Capture& capture, CaptureLevel level, const OsResponses& r) {
+ MockOS os(r);
+ CaptureState state;
+ zx_status_t ret = Capture::GetCaptureState(state, os);
+ EXPECT_EQ(ZX_OK, ret);
+ return Capture::GetCapture(capture, state, level, os);
+}
+
} // namespace memory
diff --git a/src/developer/memory/metrics/tests/test_utils.h b/src/developer/memory/metrics/tests/test_utils.h
index f73401f..c573791 100644
--- a/src/developer/memory/metrics/tests/test_utils.h
+++ b/src/developer/memory/metrics/tests/test_utils.h
@@ -19,9 +19,50 @@
std::vector<Process> processes;
};
+struct GetProcessesCallback {
+ int depth;
+ zx_handle_t handle;
+ zx_koid_t koid;
+ zx_koid_t parent_koid;
+};
+
+struct GetProcessesResponse {
+ zx_status_t ret;
+ std::vector<GetProcessesCallback> callbacks;
+};
+
+struct GetPropertyResponse {
+ zx_handle_t handle;
+ uint32_t property;
+ const void* value;
+ size_t value_len;
+ zx_status_t ret;
+};
+
+struct GetInfoResponse {
+ zx_handle_t handle;
+ uint32_t topic;
+ const void* values;
+ size_t value_size;
+ size_t value_count;
+ zx_status_t ret;
+};
+
+struct OsResponses {
+ const std::vector<GetProcessesResponse> get_processes;
+ const std::vector<GetPropertyResponse> get_property;
+ const std::vector<GetInfoResponse> get_info;
+};
+
class TestUtils {
public:
+ const static zx_handle_t kRootHandle;
+ const static zx_handle_t kSelfHandle;
+ const static zx_koid_t kSelfKoid;
+
static void CreateCapture(memory::Capture& capture, const CaptureTemplate& t);
+ static zx_status_t GetCapture(
+ Capture& capture, CaptureLevel level, const OsResponses& r);
// Sorted by koid.
static std::vector<ProcessSummary> GetProcessSummaries(