blob: c0cd3e07ff41127e46897fe71ab475af84f863bf [file] [log] [blame]
Connor O'Brien57337192018-11-20 12:49:16 -08001
Connor O'Briend65f2a02019-08-28 16:15:38 -07002#include <bpf_timeinstate.h>
Connor O'Briena178a732019-06-05 18:27:47 -07003
4#include <sys/sysinfo.h>
5
Connor O'Brien26de80f2019-06-11 13:49:19 -07006#include <numeric>
Connor O'Brien57337192018-11-20 12:49:16 -08007#include <unordered_map>
8#include <vector>
9
10#include <gtest/gtest.h>
11
Connor O'Briena178a732019-06-05 18:27:47 -070012#include <android-base/unique_fd.h>
13#include <bpf/BpfMap.h>
Connor O'Brien57337192018-11-20 12:49:16 -080014#include <cputimeinstate.h>
Connor O'Briena178a732019-06-05 18:27:47 -070015#include <libbpf.h>
Connor O'Brien57337192018-11-20 12:49:16 -080016
17namespace android {
18namespace bpf {
19
Connor O'Briena178a732019-06-05 18:27:47 -070020static constexpr uint64_t NSEC_PER_SEC = 1000000000;
21static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
22
Connor O'Brien57337192018-11-20 12:49:16 -080023using std::vector;
24
Connor O'Brien26de80f2019-06-11 13:49:19 -070025TEST(TimeInStateTest, SingleUidTimeInState) {
Connor O'Brienf03b6ae2019-06-05 18:03:12 -070026 auto times = getUidCpuFreqTimes(0);
27 ASSERT_TRUE(times.has_value());
28 EXPECT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -080029}
30
Connor O'Brien26de80f2019-06-11 13:49:19 -070031TEST(TimeInStateTest, SingleUidConcurrentTimes) {
32 auto concurrentTimes = getUidConcurrentTimes(0);
33 ASSERT_TRUE(concurrentTimes.has_value());
34 ASSERT_FALSE(concurrentTimes->active.empty());
35 ASSERT_FALSE(concurrentTimes->policy.empty());
36
37 uint64_t policyEntries = 0;
38 for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size();
39 ASSERT_EQ(concurrentTimes->active.size(), policyEntries);
40}
41
42static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) {
43 size_t maxPolicyCpus = 0;
44 for (const auto &vec : concurrentTime.policy) {
45 maxPolicyCpus = std::max(maxPolicyCpus, vec.size());
46 }
47 uint64_t policySum = 0;
48 for (size_t i = 0; i < maxPolicyCpus; ++i) {
49 for (const auto &vec : concurrentTime.policy) {
50 if (i < vec.size()) policySum += vec[i];
51 }
52 ASSERT_LE(concurrentTime.active[i], policySum);
53 policySum -= concurrentTime.active[i];
54 }
55 policySum = 0;
56 for (size_t i = 0; i < concurrentTime.active.size(); ++i) {
57 for (const auto &vec : concurrentTime.policy) {
58 if (i < vec.size()) policySum += vec[vec.size() - 1 - i];
59 }
60 auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i];
61 // This check is slightly flaky because we may read a map entry in the middle of an update
62 // when active times have been updated but policy times have not. This happens infrequently
63 // and can be distinguished from more serious bugs by re-running the test: if the underlying
64 // data itself is inconsistent, the test will fail every time.
65 ASSERT_LE(activeSum, policySum);
66 policySum -= activeSum;
67 }
68}
69
70static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState,
71 const struct concurrent_time_t &concurrentTime) {
72 ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime));
73 ASSERT_EQ(timeInState.size(), concurrentTime.policy.size());
74 uint64_t policySum = 0;
75 for (uint32_t i = 0; i < timeInState.size(); ++i) {
76 uint64_t tisSum =
77 std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0);
78 uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(),
79 concurrentTime.policy[i].end(), (uint64_t)0);
80 if (tisSum < concurrentSum)
81 ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC);
82 else
83 ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC);
84 policySum += concurrentSum;
85 }
86 uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(),
87 (uint64_t)0);
88 EXPECT_EQ(activeSum, policySum);
89}
90
91TEST(TimeInStateTest, SingleUidTimesConsistent) {
92 auto times = getUidCpuFreqTimes(0);
93 ASSERT_TRUE(times.has_value());
94
95 auto concurrentTimes = getUidConcurrentTimes(0);
96 ASSERT_TRUE(concurrentTimes.has_value());
97
98 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
99}
100
101TEST(TimeInStateTest, AllUidTimeInState) {
Connor O'Brien57337192018-11-20 12:49:16 -0800102 vector<size_t> sizes;
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700103 auto map = getUidsCpuFreqTimes();
104 ASSERT_TRUE(map.has_value());
Connor O'Brien57337192018-11-20 12:49:16 -0800105
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700106 ASSERT_FALSE(map->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800107
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700108 auto firstEntry = map->begin()->second;
Connor O'Brien57337192018-11-20 12:49:16 -0800109 for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
110
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700111 for (const auto &vec : *map) {
Connor O'Brien57337192018-11-20 12:49:16 -0800112 ASSERT_EQ(vec.second.size(), sizes.size());
113 for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
114 }
115}
116
Connor O'Brien26de80f2019-06-11 13:49:19 -0700117TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
Connor O'Briena178a732019-06-05 18:27:47 -0700118 auto map = getUidsCpuFreqTimes();
119 ASSERT_TRUE(map.has_value());
120 ASSERT_FALSE(map->empty());
121
122 for (const auto &kv : *map) {
123 uint32_t uid = kv.first;
124 auto times1 = kv.second;
125 auto times2 = getUidCpuFreqTimes(uid);
126 ASSERT_TRUE(times2.has_value());
127
128 ASSERT_EQ(times1.size(), times2->size());
129 for (uint32_t i = 0; i < times1.size(); ++i) {
130 ASSERT_EQ(times1[i].size(), (*times2)[i].size());
131 for (uint32_t j = 0; j < times1[i].size(); ++j) {
132 ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
133 }
134 }
135 }
136}
137
Connor O'Brien26de80f2019-06-11 13:49:19 -0700138TEST(TimeInStateTest, AllUidConcurrentTimes) {
139 auto map = getUidsConcurrentTimes();
140 ASSERT_TRUE(map.has_value());
141 ASSERT_FALSE(map->empty());
142
143 auto firstEntry = map->begin()->second;
144 for (const auto &kv : *map) {
145 ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
146 ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
147 for (size_t i = 0; i < kv.second.policy.size(); ++i) {
148 ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
149 }
150 }
151}
152
153TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
154 auto map = getUidsConcurrentTimes();
155 ASSERT_TRUE(map.has_value());
156 for (const auto &kv : *map) {
157 uint32_t uid = kv.first;
158 auto times1 = kv.second;
159 auto times2 = getUidConcurrentTimes(uid);
160 ASSERT_TRUE(times2.has_value());
161 for (uint32_t i = 0; i < times1.active.size(); ++i) {
162 ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
163 }
164 for (uint32_t i = 0; i < times1.policy.size(); ++i) {
165 for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
166 ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
167 }
168 }
169 }
170}
171
Connor O'Briena178a732019-06-05 18:27:47 -0700172void TestCheckDelta(uint64_t before, uint64_t after) {
173 // Times should never decrease
174 ASSERT_LE(before, after);
175 // UID can't have run for more than ~1s on each CPU
176 ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
177}
178
Connor O'Brien26de80f2019-06-11 13:49:19 -0700179TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
Connor O'Briena178a732019-06-05 18:27:47 -0700180 auto map1 = getUidsCpuFreqTimes();
181 ASSERT_TRUE(map1.has_value());
182 sleep(1);
183 auto map2 = getUidsCpuFreqTimes();
184 ASSERT_TRUE(map2.has_value());
185
186 for (const auto &kv : *map1) {
187 uint32_t uid = kv.first;
188 auto times = kv.second;
189 ASSERT_NE(map2->find(uid), map2->end());
190 for (uint32_t policy = 0; policy < times.size(); ++policy) {
191 for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) {
192 auto before = times[policy][freqIdx];
193 auto after = (*map2)[uid][policy][freqIdx];
194 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
195 }
196 }
197 }
198}
199
Connor O'Brien26de80f2019-06-11 13:49:19 -0700200TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
201 auto map1 = getUidsConcurrentTimes();
202 ASSERT_TRUE(map1.has_value());
203 ASSERT_FALSE(map1->empty());
204 sleep(1);
205 auto map2 = getUidsConcurrentTimes();
206 ASSERT_TRUE(map2.has_value());
207 ASSERT_FALSE(map2->empty());
208
209 for (const auto &kv : *map1) {
210 uint32_t uid = kv.first;
211 auto times = kv.second;
212 ASSERT_NE(map2->find(uid), map2->end());
213 for (uint32_t i = 0; i < times.active.size(); ++i) {
214 auto before = times.active[i];
215 auto after = (*map2)[uid].active[i];
216 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
217 }
218 for (uint32_t policy = 0; policy < times.policy.size(); ++policy) {
219 for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) {
220 auto before = times.policy[policy][idx];
221 auto after = (*map2)[uid].policy[policy][idx];
222 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
223 }
224 }
225 }
226}
227
228TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
Connor O'Briena178a732019-06-05 18:27:47 -0700229 auto map = getUidsCpuFreqTimes();
230 ASSERT_TRUE(map.has_value());
231
232 bool foundLargeValue = false;
233 for (const auto &kv : *map) {
234 for (const auto &timeVec : kv.second) {
235 for (const auto &time : timeVec) {
236 ASSERT_LE(time, NSEC_PER_YEAR);
237 if (time > UINT32_MAX) foundLargeValue = true;
238 }
239 }
240 }
241 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
242 // uint64_t as expected, we should have some times higher than that.
243 ASSERT_TRUE(foundLargeValue);
244}
245
Connor O'Brien26de80f2019-06-11 13:49:19 -0700246TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
247 auto concurrentMap = getUidsConcurrentTimes();
248 ASSERT_TRUE(concurrentMap);
249
250 bool activeFoundLargeValue = false;
251 bool policyFoundLargeValue = false;
252 for (const auto &kv : *concurrentMap) {
253 for (const auto &time : kv.second.active) {
254 ASSERT_LE(time, NSEC_PER_YEAR);
255 if (time > UINT32_MAX) activeFoundLargeValue = true;
256 }
257 for (const auto &policyTimeVec : kv.second.policy) {
258 for (const auto &time : policyTimeVec) {
259 ASSERT_LE(time, NSEC_PER_YEAR);
260 if (time > UINT32_MAX) policyFoundLargeValue = true;
261 }
262 }
263 }
264 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
265 // uint64_t as expected, we should have some times higher than that.
266 ASSERT_TRUE(activeFoundLargeValue);
267 ASSERT_TRUE(policyFoundLargeValue);
268}
269
270TEST(TimeInStateTest, AllUidTimesConsistent) {
271 auto tisMap = getUidsCpuFreqTimes();
272 ASSERT_TRUE(tisMap.has_value());
273
274 auto concurrentMap = getUidsConcurrentTimes();
275 ASSERT_TRUE(concurrentMap.has_value());
276
277 ASSERT_EQ(tisMap->size(), concurrentMap->size());
278 for (const auto &kv : *tisMap) {
279 uint32_t uid = kv.first;
280 auto times = kv.second;
281 ASSERT_NE(concurrentMap->find(uid), concurrentMap->end());
282
283 auto concurrentTimes = (*concurrentMap)[uid];
284 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes));
285 }
286}
287
Connor O'Brien57337192018-11-20 12:49:16 -0800288TEST(TimeInStateTest, RemoveUid) {
Connor O'Briena178a732019-06-05 18:27:47 -0700289 uint32_t uid = 0;
290 {
291 // Find an unused UID
292 auto times = getUidsCpuFreqTimes();
293 ASSERT_TRUE(times.has_value());
294 ASSERT_FALSE(times->empty());
295 for (const auto &kv : *times) uid = std::max(uid, kv.first);
296 ++uid;
297 }
298 {
299 // Add a map entry for our fake UID by copying a real map entry
Connor O'Brien26de80f2019-06-11 13:49:19 -0700300 android::base::unique_fd fd{
301 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
Connor O'Briena178a732019-06-05 18:27:47 -0700302 ASSERT_GE(fd, 0);
303 time_key_t k;
304 ASSERT_FALSE(getFirstMapKey(fd, &k));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700305 std::vector<tis_val_t> vals(get_nprocs_conf());
Connor O'Brien1a180402019-06-07 16:39:49 -0700306 ASSERT_FALSE(findMapEntry(fd, &k, vals.data()));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700307 uint32_t copiedUid = k.uid;
Connor O'Briena178a732019-06-05 18:27:47 -0700308 k.uid = uid;
Connor O'Brien1a180402019-06-07 16:39:49 -0700309 ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700310
311 android::base::unique_fd fd2{
312 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
313 k.uid = copiedUid;
314 k.bucket = 0;
315 std::vector<concurrent_val_t> cvals(get_nprocs_conf());
316 ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data()));
317 k.uid = uid;
318 ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST));
Connor O'Briena178a732019-06-05 18:27:47 -0700319 }
320 auto times = getUidCpuFreqTimes(uid);
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700321 ASSERT_TRUE(times.has_value());
322 ASSERT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800323
Connor O'Brien26de80f2019-06-11 13:49:19 -0700324 auto concurrentTimes = getUidConcurrentTimes(0);
325 ASSERT_TRUE(concurrentTimes.has_value());
326 ASSERT_FALSE(concurrentTimes->active.empty());
327 ASSERT_FALSE(concurrentTimes->policy.empty());
328
Connor O'Brien57337192018-11-20 12:49:16 -0800329 uint64_t sum = 0;
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700330 for (size_t i = 0; i < times->size(); ++i) {
331 for (auto x : (*times)[i]) sum += x;
Connor O'Brien57337192018-11-20 12:49:16 -0800332 }
333 ASSERT_GT(sum, (uint64_t)0);
334
Connor O'Brien26de80f2019-06-11 13:49:19 -0700335 uint64_t activeSum = 0;
336 for (size_t i = 0; i < concurrentTimes->active.size(); ++i) {
337 activeSum += concurrentTimes->active[i];
338 }
339 ASSERT_GT(activeSum, (uint64_t)0);
340
341 ASSERT_TRUE(clearUidTimes(uid));
Connor O'Brien57337192018-11-20 12:49:16 -0800342
Connor O'Briena178a732019-06-05 18:27:47 -0700343 auto allTimes = getUidsCpuFreqTimes();
344 ASSERT_TRUE(allTimes.has_value());
345 ASSERT_FALSE(allTimes->empty());
346 ASSERT_EQ(allTimes->find(uid), allTimes->end());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700347
348 auto allConcurrentTimes = getUidsConcurrentTimes();
349 ASSERT_TRUE(allConcurrentTimes.has_value());
350 ASSERT_FALSE(allConcurrentTimes->empty());
351 ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
Connor O'Brien57337192018-11-20 12:49:16 -0800352}
353
354} // namespace bpf
355} // namespace android