blob: 6888457db20e6982d2858444c83f32a59e37c2b2 [file] [log] [blame]
James Kuszmaul418fd062022-03-22 15:22:27 -07001#include "aos/util/top.h"
2
3#include <unistd.h>
4
5#include <array>
6#include <string>
7#include <thread>
8
Philipp Schrader790cb542023-07-05 21:06:52 -07009#include "gtest/gtest.h"
10
James Kuszmaul418fd062022-03-22 15:22:27 -070011#include "aos/events/shm_event_loop.h"
12#include "aos/json_to_flatbuffer.h"
13#include "aos/testing/path.h"
14#include "aos/testing/tmpdir.h"
James Kuszmaul418fd062022-03-22 15:22:27 -070015
16namespace aos::util::testing {
17
18class TopTest : public ::testing::Test {
19 protected:
20 TopTest()
21 : shm_dir_(aos::testing::TestTmpDir() + "/aos"),
22 cpu_consumer_([this]() {
23 while (!stop_flag_.load()) {
24 }
25 }),
26 config_file_(
27 aos::testing::ArtifactPath("aos/events/pingpong_config.json")),
28 config_(aos::configuration::ReadConfig(config_file_)),
29 event_loop_(&config_.message()) {
30 FLAGS_shm_base = shm_dir_;
31
Austin Schuh60e77942022-05-16 17:48:24 -070032 // Nuke the shm dir, to ensure we aren't being affected by any preexisting
33 // tests.
James Kuszmaul418fd062022-03-22 15:22:27 -070034 aos::util::UnlinkRecursive(shm_dir_);
35 }
36 ~TopTest() {
37 stop_flag_ = true;
38 cpu_consumer_.join();
39 }
40
41 gflags::FlagSaver flag_saver_;
42 std::string shm_dir_;
43
44 std::thread cpu_consumer_;
45 std::atomic<bool> stop_flag_{false};
46 const std::string config_file_;
47 const aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
48 aos::ShmEventLoop event_loop_;
49};
50
51TEST_F(TopTest, TestSelfStat) {
52 const pid_t pid = getpid();
53 std::optional<ProcStat> proc_stat = ReadProcStat(pid);
54 ASSERT_TRUE(proc_stat.has_value());
55 ASSERT_EQ(pid, proc_stat->pid);
56 ASSERT_EQ("top_test", proc_stat->name);
57 ASSERT_EQ('R', proc_stat->state);
58 ASSERT_LT(1, proc_stat->num_threads);
59}
60
61TEST_F(TopTest, QuerySingleProcess) {
62 const pid_t pid = getpid();
63 Top top(&event_loop_);
64 top.set_track_pids({pid});
65 event_loop_.AddTimer([this]() { event_loop_.Exit(); })
Philipp Schradera6712522023-07-05 20:25:11 -070066 ->Schedule(event_loop_.monotonic_now() + std::chrono::seconds(2));
James Kuszmaul418fd062022-03-22 15:22:27 -070067 event_loop_.Run();
68 flatbuffers::FlatBufferBuilder fbb;
69 fbb.ForceDefaults(true);
70 fbb.Finish(top.InfoForProcess(&fbb, pid));
71 aos::FlatbufferDetachedBuffer<ProcessInfo> info = fbb.Release();
72 ASSERT_EQ(pid, info.message().pid());
73 ASSERT_TRUE(info.message().has_name());
74 ASSERT_EQ("top_test", info.message().name()->string_view());
75 // Check that we did indeed consume ~1 CPU core (because we're multi-threaded,
76 // we could've consumed a bit more; and on systems where we are competing with
77 // other processes for CPU time, we may not get a full 100% load).
78 ASSERT_LT(0.5, info.message().cpu_usage());
79 ASSERT_GT(1.1, info.message().cpu_usage());
80 // Sanity check memory usage.
81 ASSERT_LT(1000000, info.message().physical_memory());
82 ASSERT_GT(1000000000, info.message().physical_memory());
83}
84
85TEST_F(TopTest, TopProcesses) {
86 // Make some dummy processes that will just spin and get killed off at the
87 // end, so that we actually have things to query.
88 constexpr int kNProcesses = 2;
89 std::vector<pid_t> children;
90 // This will create kNProcesses children + ourself, which means we have enough
91 // processes to test that we correctly exclude extras when requesting fewer
92 // processes than exist.
93 for (int ii = 0; ii < kNProcesses; ++ii) {
94 const pid_t pid = fork();
95 PCHECK(pid >= 0);
96 if (pid == 0) {
James Kuszmaul860a94c2022-04-06 16:35:07 -070097 LOG(INFO) << "In child process.";
James Kuszmaul418fd062022-03-22 15:22:27 -070098 while (true) {
James Kuszmaul860a94c2022-04-06 16:35:07 -070099 // This is a "please don't optimize me out" thing for the compiler.
100 // Otherwise, the entire if (pid == 0) block can get optimized away...
101 asm("");
102 continue;
James Kuszmaul418fd062022-03-22 15:22:27 -0700103 }
James Kuszmaul860a94c2022-04-06 16:35:07 -0700104 LOG(FATAL) << "This should be unreachable.";
James Kuszmaul418fd062022-03-22 15:22:27 -0700105 } else {
James Kuszmaul860a94c2022-04-06 16:35:07 -0700106 CHECK_NE(0, pid) << "The compiler is messing with you.";
James Kuszmaul418fd062022-03-22 15:22:27 -0700107 children.push_back(pid);
108 }
109 }
110
111 Top top(&event_loop_);
112 top.set_track_top_processes(true);
113 event_loop_.AddTimer([this]() { event_loop_.Exit(); })
Philipp Schradera6712522023-07-05 20:25:11 -0700114 ->Schedule(event_loop_.monotonic_now() + std::chrono::seconds(2));
James Kuszmaul418fd062022-03-22 15:22:27 -0700115 event_loop_.SkipTimingReport();
116 event_loop_.SkipAosLog();
117 event_loop_.Run();
118 flatbuffers::FlatBufferBuilder fbb;
119 fbb.ForceDefaults(true);
120 fbb.Finish(top.TopProcesses(&fbb, kNProcesses));
121 aos::FlatbufferDetachedBuffer<TopProcessesFbs> info = fbb.Release();
122 ASSERT_EQ(kNProcesses, info.message().processes()->size());
123 double last_cpu = std::numeric_limits<double>::infinity();
124 std::set<pid_t> observed_pids;
125 int process_index = 0;
126 for (const ProcessInfo *info : *info.message().processes()) {
127 SCOPED_TRACE(aos::FlatbufferToJson(info));
128 ASSERT_EQ(0, observed_pids.count(info->pid()));
129 observed_pids.insert(info->pid());
130 ASSERT_TRUE(info->has_name());
131 // Confirm that the top process has non-zero CPU usage, but allow the
132 // lower-down processes to have not been scheduled in the last measurement
133 // cycle.
134 if (process_index < 1) {
135 ASSERT_LT(0.0, info->cpu_usage());
136 } else {
137 ASSERT_LE(0.0, info->cpu_usage());
138 }
139 ++process_index;
140 ASSERT_GE(last_cpu, info->cpu_usage());
141 last_cpu = info->cpu_usage();
142 ASSERT_LT(0, info->physical_memory());
143 }
144
145 for (const pid_t child : children) {
146 kill(child, SIGINT);
147 }
148}
149
150// Test thgat if we request arbitrarily many processes that we only get back as
151// many processes as actually exist and that nothing breaks.
152TEST_F(TopTest, AllTopProcesses) {
153 constexpr int kNProcesses = 1000000;
154
155 Top top(&event_loop_);
156 top.set_track_top_processes(true);
157 event_loop_.AddTimer([this]() { event_loop_.Exit(); })
Philipp Schradera6712522023-07-05 20:25:11 -0700158 ->Schedule(event_loop_.monotonic_now() + std::chrono::seconds(2));
James Kuszmaul418fd062022-03-22 15:22:27 -0700159 event_loop_.Run();
160 flatbuffers::FlatBufferBuilder fbb;
161 fbb.ForceDefaults(true);
162 // There should only be at most 2-3 processes visible inside the bazel
163 // sandbox.
164 fbb.Finish(top.TopProcesses(&fbb, kNProcesses));
165 aos::FlatbufferDetachedBuffer<TopProcessesFbs> info = fbb.Release();
166 ASSERT_GT(kNProcesses, info.message().processes()->size());
167 double last_cpu = std::numeric_limits<double>::infinity();
168 std::set<pid_t> observed_pids;
169 for (const ProcessInfo *info : *info.message().processes()) {
170 SCOPED_TRACE(aos::FlatbufferToJson(info));
171 LOG(INFO) << aos::FlatbufferToJson(info);
172 ASSERT_EQ(0, observed_pids.count(info->pid()));
173 observed_pids.insert(info->pid());
174 ASSERT_TRUE(info->has_name());
175 ASSERT_LE(0.0, info->cpu_usage());
176 ASSERT_GE(last_cpu, info->cpu_usage());
177 last_cpu = info->cpu_usage();
178 ASSERT_LE(0, info->physical_memory());
179 }
180}
181
182} // namespace aos::util::testing