blob: 1513ecafc8769057ac47872fec7f6bd26e8d2e90 [file] [log] [blame]
Steven Moreland72530412020-01-13 16:13:58 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Connor O'Brien57337192018-11-20 12:49:16 -080017
Connor O'Briend65f2a02019-08-28 16:15:38 -070018#include <bpf_timeinstate.h>
Connor O'Briena178a732019-06-05 18:27:47 -070019
20#include <sys/sysinfo.h>
21
Dmitri Plotnikov2677dba2020-10-17 21:06:55 -070022#include <pthread.h>
23#include <semaphore.h>
Connor O'Brien26de80f2019-06-11 13:49:19 -070024#include <numeric>
Connor O'Brien57337192018-11-20 12:49:16 -080025#include <unordered_map>
26#include <vector>
27
28#include <gtest/gtest.h>
29
Connor O'Brien6f179e62020-02-18 15:54:27 -080030#include <android-base/properties.h>
Connor O'Briena178a732019-06-05 18:27:47 -070031#include <android-base/unique_fd.h>
32#include <bpf/BpfMap.h>
Connor O'Brien57337192018-11-20 12:49:16 -080033#include <cputimeinstate.h>
Connor O'Briena178a732019-06-05 18:27:47 -070034#include <libbpf.h>
Connor O'Brien57337192018-11-20 12:49:16 -080035
36namespace android {
37namespace bpf {
38
Connor O'Briena178a732019-06-05 18:27:47 -070039static constexpr uint64_t NSEC_PER_SEC = 1000000000;
40static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
41
Connor O'Brien57337192018-11-20 12:49:16 -080042using std::vector;
43
Connor O'Brien6f179e62020-02-18 15:54:27 -080044class TimeInStateTest : public testing::Test {
45 protected:
46 TimeInStateTest() {};
Rafal Slawik45caa842021-01-29 12:25:56 +000047
Connor O'Brien6f179e62020-02-18 15:54:27 -080048 void SetUp() {
49 if (!isTrackingUidTimesSupported() ||
50 !android::base::GetBoolProperty("sys.init.perf_lsm_hooks", false)) {
51 GTEST_SKIP();
52 }
53 }
54};
55
56TEST_F(TimeInStateTest, TotalTimeInState) {
Rafal Slawik27c48db2021-01-05 19:10:07 +000057 auto times = getTotalCpuFreqTimes();
58 ASSERT_TRUE(times.has_value());
59 EXPECT_FALSE(times->empty());
60}
61
Connor O'Brien6f179e62020-02-18 15:54:27 -080062TEST_F(TimeInStateTest, SingleUidTimeInState) {
Connor O'Brienf03b6ae2019-06-05 18:03:12 -070063 auto times = getUidCpuFreqTimes(0);
64 ASSERT_TRUE(times.has_value());
65 EXPECT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -080066}
67
Connor O'Brien6f179e62020-02-18 15:54:27 -080068TEST_F(TimeInStateTest, SingleUidConcurrentTimes) {
Connor O'Brien26de80f2019-06-11 13:49:19 -070069 auto concurrentTimes = getUidConcurrentTimes(0);
70 ASSERT_TRUE(concurrentTimes.has_value());
71 ASSERT_FALSE(concurrentTimes->active.empty());
72 ASSERT_FALSE(concurrentTimes->policy.empty());
73
74 uint64_t policyEntries = 0;
75 for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size();
76 ASSERT_EQ(concurrentTimes->active.size(), policyEntries);
77}
78
79static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) {
80 size_t maxPolicyCpus = 0;
81 for (const auto &vec : concurrentTime.policy) {
82 maxPolicyCpus = std::max(maxPolicyCpus, vec.size());
83 }
84 uint64_t policySum = 0;
85 for (size_t i = 0; i < maxPolicyCpus; ++i) {
86 for (const auto &vec : concurrentTime.policy) {
87 if (i < vec.size()) policySum += vec[i];
88 }
89 ASSERT_LE(concurrentTime.active[i], policySum);
90 policySum -= concurrentTime.active[i];
91 }
92 policySum = 0;
93 for (size_t i = 0; i < concurrentTime.active.size(); ++i) {
94 for (const auto &vec : concurrentTime.policy) {
95 if (i < vec.size()) policySum += vec[vec.size() - 1 - i];
96 }
97 auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i];
98 // This check is slightly flaky because we may read a map entry in the middle of an update
99 // when active times have been updated but policy times have not. This happens infrequently
100 // and can be distinguished from more serious bugs by re-running the test: if the underlying
101 // data itself is inconsistent, the test will fail every time.
102 ASSERT_LE(activeSum, policySum);
103 policySum -= activeSum;
104 }
105}
106
107static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState,
108 const struct concurrent_time_t &concurrentTime) {
109 ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime));
110 ASSERT_EQ(timeInState.size(), concurrentTime.policy.size());
111 uint64_t policySum = 0;
112 for (uint32_t i = 0; i < timeInState.size(); ++i) {
113 uint64_t tisSum =
114 std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0);
115 uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(),
116 concurrentTime.policy[i].end(), (uint64_t)0);
117 if (tisSum < concurrentSum)
118 ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC);
119 else
120 ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC);
121 policySum += concurrentSum;
122 }
123 uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(),
124 (uint64_t)0);
125 EXPECT_EQ(activeSum, policySum);
126}
127
Connor O'Brien6f179e62020-02-18 15:54:27 -0800128TEST_F(TimeInStateTest, SingleUidTimesConsistent) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700129 auto times = getUidCpuFreqTimes(0);
130 ASSERT_TRUE(times.has_value());
131
132 auto concurrentTimes = getUidConcurrentTimes(0);
133 ASSERT_TRUE(concurrentTimes.has_value());
134
135 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
136}
137
Connor O'Brien6f179e62020-02-18 15:54:27 -0800138TEST_F(TimeInStateTest, AllUidTimeInState) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800139 uint64_t zero = 0;
140 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
141 for (const auto &map : maps) {
142 ASSERT_TRUE(map.has_value());
Connor O'Brien57337192018-11-20 12:49:16 -0800143
Connor O'Brien2a716a42020-01-31 18:51:56 -0800144 ASSERT_FALSE(map->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800145
Connor O'Brien2a716a42020-01-31 18:51:56 -0800146 vector<size_t> sizes;
147 auto firstEntry = map->begin()->second;
148 for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
Connor O'Brien57337192018-11-20 12:49:16 -0800149
Connor O'Brien2a716a42020-01-31 18:51:56 -0800150 for (const auto &vec : *map) {
151 ASSERT_EQ(vec.second.size(), sizes.size());
152 for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
153 }
154 }
155}
156
157void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before,
158 const std::vector<std::vector<uint64_t>> &after) {
159 ASSERT_EQ(before.size(), after.size());
160 uint64_t sumBefore = 0, sumAfter = 0;
161 for (size_t i = 0; i < before.size(); ++i) {
162 ASSERT_EQ(before[i].size(), after[i].size());
163 for (size_t j = 0; j < before[i].size(); ++j) {
164 // Times should never decrease
165 ASSERT_LE(before[i][j], after[i][j]);
166 }
167 sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0);
168 sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0);
169 }
170 ASSERT_LE(sumBefore, sumAfter);
171 ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC);
172}
173
Connor O'Brien6f179e62020-02-18 15:54:27 -0800174TEST_F(TimeInStateTest, AllUidUpdatedTimeInState) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800175 uint64_t lastUpdate = 0;
176 auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
177 ASSERT_TRUE(map1.has_value());
178 ASSERT_FALSE(map1->empty());
179 ASSERT_NE(lastUpdate, (uint64_t)0);
180 uint64_t oldLastUpdate = lastUpdate;
181
182 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
183 struct timespec ts;
184 ts.tv_sec = 0;
185 ts.tv_nsec = 1000000;
186 nanosleep (&ts, NULL);
187
188 auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
189 ASSERT_TRUE(map2.has_value());
190 ASSERT_FALSE(map2->empty());
191 ASSERT_NE(lastUpdate, oldLastUpdate);
192
193 bool someUidsExcluded = false;
194 for (const auto &[uid, v] : *map1) {
195 if (map2->find(uid) == map2->end()) {
196 someUidsExcluded = true;
197 break;
198 }
199 }
200 ASSERT_TRUE(someUidsExcluded);
201
202 for (const auto &[uid, newTimes] : *map2) {
203 ASSERT_NE(map1->find(uid), map1->end());
204 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes));
Connor O'Brien57337192018-11-20 12:49:16 -0800205 }
206}
207
Connor O'Brien6f179e62020-02-18 15:54:27 -0800208TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
Rafal Slawik27c48db2021-01-05 19:10:07 +0000209 auto allUid = getUidsCpuFreqTimes();
210 auto total = getTotalCpuFreqTimes();
211
212 ASSERT_TRUE(allUid.has_value() && total.has_value());
213
214 // Check the number of policies.
215 ASSERT_EQ(allUid->at(0).size(), total->size());
216
217 for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) {
218 std::vector<uint64_t> totalTimes = total->at(policyIdx);
219 uint32_t totalFreqsCount = totalTimes.size();
220 std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
221 for (auto const &[uid, uidTimes]: *allUid) {
222 for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
223 allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
224 }
225 }
226
227 for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) {
228 ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]);
229 }
230 }
231}
232
Connor O'Brien6f179e62020-02-18 15:54:27 -0800233TEST_F(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800234 uint64_t zero = 0;
235 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
236 for (const auto &map : maps) {
237 ASSERT_TRUE(map.has_value());
238 ASSERT_FALSE(map->empty());
Connor O'Briena178a732019-06-05 18:27:47 -0700239
Connor O'Brien2a716a42020-01-31 18:51:56 -0800240 for (const auto &kv : *map) {
241 uint32_t uid = kv.first;
242 auto times1 = kv.second;
243 auto times2 = getUidCpuFreqTimes(uid);
244 ASSERT_TRUE(times2.has_value());
Connor O'Briena178a732019-06-05 18:27:47 -0700245
Connor O'Brien2a716a42020-01-31 18:51:56 -0800246 ASSERT_EQ(times1.size(), times2->size());
247 for (uint32_t i = 0; i < times1.size(); ++i) {
248 ASSERT_EQ(times1[i].size(), (*times2)[i].size());
249 for (uint32_t j = 0; j < times1[i].size(); ++j) {
250 ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
251 }
Connor O'Briena178a732019-06-05 18:27:47 -0700252 }
253 }
254 }
255}
256
Connor O'Brien6f179e62020-02-18 15:54:27 -0800257TEST_F(TimeInStateTest, AllUidConcurrentTimes) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800258 uint64_t zero = 0;
259 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
260 for (const auto &map : maps) {
261 ASSERT_TRUE(map.has_value());
262 ASSERT_FALSE(map->empty());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700263
Connor O'Brien2a716a42020-01-31 18:51:56 -0800264 auto firstEntry = map->begin()->second;
265 for (const auto &kv : *map) {
266 ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
267 ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
268 for (size_t i = 0; i < kv.second.policy.size(); ++i) {
269 ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
270 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700271 }
272 }
273}
274
Connor O'Brien6f179e62020-02-18 15:54:27 -0800275TEST_F(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800276 uint64_t lastUpdate = 0;
277 auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
278 ASSERT_TRUE(map1.has_value());
279 ASSERT_FALSE(map1->empty());
280 ASSERT_NE(lastUpdate, (uint64_t)0);
281
282 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
283 struct timespec ts;
284 ts.tv_sec = 0;
285 ts.tv_nsec = 1000000;
286 nanosleep (&ts, NULL);
287
288 uint64_t oldLastUpdate = lastUpdate;
289 auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate);
290 ASSERT_TRUE(map2.has_value());
291 ASSERT_FALSE(map2->empty());
292 ASSERT_NE(lastUpdate, oldLastUpdate);
293
294 bool someUidsExcluded = false;
295 for (const auto &[uid, v] : *map1) {
296 if (map2->find(uid) == map2->end()) {
297 someUidsExcluded = true;
298 break;
Connor O'Brien26de80f2019-06-11 13:49:19 -0700299 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800300 }
301 ASSERT_TRUE(someUidsExcluded);
302
303 for (const auto &[uid, newTimes] : *map2) {
304 ASSERT_NE(map1->find(uid), map1->end());
305 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active}));
306 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy));
307 }
308}
309
Connor O'Brien6f179e62020-02-18 15:54:27 -0800310TEST_F(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800311 uint64_t zero = 0;
312 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
313 for (const auto &map : maps) {
314 ASSERT_TRUE(map.has_value());
315 for (const auto &kv : *map) {
316 uint32_t uid = kv.first;
317 auto times1 = kv.second;
318 auto times2 = getUidConcurrentTimes(uid);
319 ASSERT_TRUE(times2.has_value());
320 for (uint32_t i = 0; i < times1.active.size(); ++i) {
321 ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
322 }
323 for (uint32_t i = 0; i < times1.policy.size(); ++i) {
324 for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
325 ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
326 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700327 }
328 }
329 }
330}
331
Connor O'Briena178a732019-06-05 18:27:47 -0700332void TestCheckDelta(uint64_t before, uint64_t after) {
333 // Times should never decrease
334 ASSERT_LE(before, after);
335 // UID can't have run for more than ~1s on each CPU
336 ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
337}
338
Connor O'Brien6f179e62020-02-18 15:54:27 -0800339TEST_F(TimeInStateTest, TotalTimeInStateMonotonic) {
Rafal Slawik27c48db2021-01-05 19:10:07 +0000340 auto before = getTotalCpuFreqTimes();
341 ASSERT_TRUE(before.has_value());
342 sleep(1);
343 auto after = getTotalCpuFreqTimes();
344 ASSERT_TRUE(after.has_value());
345
346 for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) {
347 auto timesBefore = before->at(policyIdx);
348 auto timesAfter = after->at(policyIdx);
349 for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) {
350 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx]));
351 }
352 }
353}
354
Connor O'Brien6f179e62020-02-18 15:54:27 -0800355TEST_F(TimeInStateTest, AllUidTimeInStateMonotonic) {
Connor O'Briena178a732019-06-05 18:27:47 -0700356 auto map1 = getUidsCpuFreqTimes();
357 ASSERT_TRUE(map1.has_value());
358 sleep(1);
359 auto map2 = getUidsCpuFreqTimes();
360 ASSERT_TRUE(map2.has_value());
361
362 for (const auto &kv : *map1) {
363 uint32_t uid = kv.first;
364 auto times = kv.second;
365 ASSERT_NE(map2->find(uid), map2->end());
366 for (uint32_t policy = 0; policy < times.size(); ++policy) {
367 for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) {
368 auto before = times[policy][freqIdx];
369 auto after = (*map2)[uid][policy][freqIdx];
370 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
371 }
372 }
373 }
374}
375
Connor O'Brien6f179e62020-02-18 15:54:27 -0800376TEST_F(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700377 auto map1 = getUidsConcurrentTimes();
378 ASSERT_TRUE(map1.has_value());
379 ASSERT_FALSE(map1->empty());
380 sleep(1);
381 auto map2 = getUidsConcurrentTimes();
382 ASSERT_TRUE(map2.has_value());
383 ASSERT_FALSE(map2->empty());
384
385 for (const auto &kv : *map1) {
386 uint32_t uid = kv.first;
387 auto times = kv.second;
388 ASSERT_NE(map2->find(uid), map2->end());
389 for (uint32_t i = 0; i < times.active.size(); ++i) {
390 auto before = times.active[i];
391 auto after = (*map2)[uid].active[i];
392 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
393 }
394 for (uint32_t policy = 0; policy < times.policy.size(); ++policy) {
395 for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) {
396 auto before = times.policy[policy][idx];
397 auto after = (*map2)[uid].policy[policy][idx];
398 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
399 }
400 }
401 }
402}
403
Connor O'Brien6f179e62020-02-18 15:54:27 -0800404TEST_F(TimeInStateTest, AllUidTimeInStateSanityCheck) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800405 uint64_t zero = 0;
406 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
407 for (const auto &map : maps) {
408 ASSERT_TRUE(map.has_value());
Connor O'Briena178a732019-06-05 18:27:47 -0700409
Connor O'Brien2a716a42020-01-31 18:51:56 -0800410 bool foundLargeValue = false;
411 for (const auto &kv : *map) {
412 for (const auto &timeVec : kv.second) {
413 for (const auto &time : timeVec) {
414 ASSERT_LE(time, NSEC_PER_YEAR);
415 if (time > UINT32_MAX) foundLargeValue = true;
416 }
Connor O'Briena178a732019-06-05 18:27:47 -0700417 }
418 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800419 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
420 // uint64_t as expected, we should have some times higher than that.
421 ASSERT_TRUE(foundLargeValue);
Connor O'Briena178a732019-06-05 18:27:47 -0700422 }
Connor O'Briena178a732019-06-05 18:27:47 -0700423}
424
Connor O'Brien6f179e62020-02-18 15:54:27 -0800425TEST_F(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800426 uint64_t zero = 0;
427 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
428 for (const auto &concurrentMap : maps) {
429 ASSERT_TRUE(concurrentMap);
Connor O'Brien26de80f2019-06-11 13:49:19 -0700430
Connor O'Brien2a716a42020-01-31 18:51:56 -0800431 bool activeFoundLargeValue = false;
432 bool policyFoundLargeValue = false;
433 for (const auto &kv : *concurrentMap) {
434 for (const auto &time : kv.second.active) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700435 ASSERT_LE(time, NSEC_PER_YEAR);
Connor O'Brien2a716a42020-01-31 18:51:56 -0800436 if (time > UINT32_MAX) activeFoundLargeValue = true;
437 }
438 for (const auto &policyTimeVec : kv.second.policy) {
439 for (const auto &time : policyTimeVec) {
440 ASSERT_LE(time, NSEC_PER_YEAR);
441 if (time > UINT32_MAX) policyFoundLargeValue = true;
442 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700443 }
444 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800445 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
446 // uint64_t as expected, we should have some times higher than that.
447 ASSERT_TRUE(activeFoundLargeValue);
448 ASSERT_TRUE(policyFoundLargeValue);
Connor O'Brien26de80f2019-06-11 13:49:19 -0700449 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700450}
451
Connor O'Brien6f179e62020-02-18 15:54:27 -0800452TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
Connor O'Brien597f6372020-10-20 14:48:40 -0700453 uint32_t uid = 0;
454 {
455 // Find an unused UID
456 auto map = getUidsConcurrentTimes();
457 ASSERT_TRUE(map.has_value());
458 ASSERT_FALSE(map->empty());
459 for (const auto &kv : *map) uid = std::max(uid, kv.first);
460 ++uid;
461 }
462 android::base::unique_fd fd{
463 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
464 ASSERT_GE(fd, 0);
465 uint32_t nCpus = get_nprocs_conf();
466 uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
467 time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
468 std::vector<concurrent_val_t> vals(nCpus);
469 ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
470 EXPECT_FALSE(getUidsConcurrentTimes().has_value());
471 ASSERT_FALSE(deleteMapEntry(fd, &key));
472}
473
Connor O'Brien6f179e62020-02-18 15:54:27 -0800474TEST_F(TimeInStateTest, AllUidTimesConsistent) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700475 auto tisMap = getUidsCpuFreqTimes();
476 ASSERT_TRUE(tisMap.has_value());
477
478 auto concurrentMap = getUidsConcurrentTimes();
479 ASSERT_TRUE(concurrentMap.has_value());
480
481 ASSERT_EQ(tisMap->size(), concurrentMap->size());
482 for (const auto &kv : *tisMap) {
483 uint32_t uid = kv.first;
484 auto times = kv.second;
485 ASSERT_NE(concurrentMap->find(uid), concurrentMap->end());
486
487 auto concurrentTimes = (*concurrentMap)[uid];
488 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes));
489 }
490}
491
Connor O'Brien6f179e62020-02-18 15:54:27 -0800492TEST_F(TimeInStateTest, RemoveUid) {
Connor O'Briena178a732019-06-05 18:27:47 -0700493 uint32_t uid = 0;
494 {
495 // Find an unused UID
496 auto times = getUidsCpuFreqTimes();
497 ASSERT_TRUE(times.has_value());
498 ASSERT_FALSE(times->empty());
499 for (const auto &kv : *times) uid = std::max(uid, kv.first);
500 ++uid;
501 }
502 {
503 // Add a map entry for our fake UID by copying a real map entry
Connor O'Brien26de80f2019-06-11 13:49:19 -0700504 android::base::unique_fd fd{
505 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
Connor O'Briena178a732019-06-05 18:27:47 -0700506 ASSERT_GE(fd, 0);
507 time_key_t k;
508 ASSERT_FALSE(getFirstMapKey(fd, &k));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700509 std::vector<tis_val_t> vals(get_nprocs_conf());
Connor O'Brien1a180402019-06-07 16:39:49 -0700510 ASSERT_FALSE(findMapEntry(fd, &k, vals.data()));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700511 uint32_t copiedUid = k.uid;
Connor O'Briena178a732019-06-05 18:27:47 -0700512 k.uid = uid;
Connor O'Brien1a180402019-06-07 16:39:49 -0700513 ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700514
515 android::base::unique_fd fd2{
516 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
517 k.uid = copiedUid;
518 k.bucket = 0;
519 std::vector<concurrent_val_t> cvals(get_nprocs_conf());
520 ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data()));
521 k.uid = uid;
522 ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST));
Connor O'Briena178a732019-06-05 18:27:47 -0700523 }
524 auto times = getUidCpuFreqTimes(uid);
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700525 ASSERT_TRUE(times.has_value());
526 ASSERT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800527
Connor O'Brien26de80f2019-06-11 13:49:19 -0700528 auto concurrentTimes = getUidConcurrentTimes(0);
529 ASSERT_TRUE(concurrentTimes.has_value());
530 ASSERT_FALSE(concurrentTimes->active.empty());
531 ASSERT_FALSE(concurrentTimes->policy.empty());
532
Connor O'Brien57337192018-11-20 12:49:16 -0800533 uint64_t sum = 0;
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700534 for (size_t i = 0; i < times->size(); ++i) {
535 for (auto x : (*times)[i]) sum += x;
Connor O'Brien57337192018-11-20 12:49:16 -0800536 }
537 ASSERT_GT(sum, (uint64_t)0);
538
Connor O'Brien26de80f2019-06-11 13:49:19 -0700539 uint64_t activeSum = 0;
540 for (size_t i = 0; i < concurrentTimes->active.size(); ++i) {
541 activeSum += concurrentTimes->active[i];
542 }
543 ASSERT_GT(activeSum, (uint64_t)0);
544
545 ASSERT_TRUE(clearUidTimes(uid));
Connor O'Brien57337192018-11-20 12:49:16 -0800546
Connor O'Briena178a732019-06-05 18:27:47 -0700547 auto allTimes = getUidsCpuFreqTimes();
548 ASSERT_TRUE(allTimes.has_value());
549 ASSERT_FALSE(allTimes->empty());
550 ASSERT_EQ(allTimes->find(uid), allTimes->end());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700551
552 auto allConcurrentTimes = getUidsConcurrentTimes();
553 ASSERT_TRUE(allConcurrentTimes.has_value());
554 ASSERT_FALSE(allConcurrentTimes->empty());
555 ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
Connor O'Brien57337192018-11-20 12:49:16 -0800556}
557
Connor O'Brien6f179e62020-02-18 15:54:27 -0800558TEST_F(TimeInStateTest, GetCpuFreqs) {
Connor O'Brien8f296eb2019-10-01 17:58:38 -0700559 auto freqs = getCpuFreqs();
560 ASSERT_TRUE(freqs.has_value());
561
562 auto times = getUidCpuFreqTimes(0);
563 ASSERT_TRUE(times.has_value());
564
565 ASSERT_EQ(freqs->size(), times->size());
566 for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size());
567}
568
Dmitri Plotnikov2677dba2020-10-17 21:06:55 -0700569uint64_t timeNanos() {
570 struct timespec spec;
571 clock_gettime(CLOCK_MONOTONIC, &spec);
572 return spec.tv_sec * 1000000000 + spec.tv_nsec;
573}
574
575// Keeps CPU busy with some number crunching
576void useCpu() {
577 long sum = 0;
578 for (int i = 0; i < 100000; i++) {
579 sum *= i;
580 }
581}
582
583sem_t pingsem, pongsem;
584
585void *testThread(void *) {
586 for (int i = 0; i < 10; i++) {
587 sem_wait(&pingsem);
588 useCpu();
589 sem_post(&pongsem);
590 }
591 return nullptr;
592}
593
Connor O'Brien6f179e62020-02-18 15:54:27 -0800594TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) {
Dmitri Plotnikov2677dba2020-10-17 21:06:55 -0700595 uint64_t startTimeNs = timeNanos();
596
597 sem_init(&pingsem, 0, 1);
598 sem_init(&pongsem, 0, 0);
599
600 pthread_t thread;
601 ASSERT_EQ(pthread_create(&thread, NULL, &testThread, NULL), 0);
602
603 // This process may have been running for some time, so when we start tracking
604 // CPU time, the very first switch may include the accumulated time.
605 // Yield the remainder of this timeslice to the newly created thread.
606 sem_wait(&pongsem);
607 sem_post(&pingsem);
608
609 pid_t tgid = getpid();
610 startTrackingProcessCpuTimes(tgid);
611
612 pid_t tid = pthread_gettid_np(thread);
613 startAggregatingTaskCpuTimes(tid, 42);
614
615 // Play ping-pong with the other thread to ensure that both threads get
616 // some CPU time.
617 for (int i = 0; i < 9; i++) {
618 sem_wait(&pongsem);
619 useCpu();
620 sem_post(&pingsem);
621 }
622
623 pthread_join(thread, NULL);
624
625 std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> optionalMap =
626 getAggregatedTaskCpuFreqTimes(tgid, {0, 42});
627 ASSERT_TRUE(optionalMap);
628
629 std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map = *optionalMap;
630 ASSERT_EQ(map.size(), 2u);
631
632 uint64_t testDurationNs = timeNanos() - startTimeNs;
633 for (auto pair : map) {
634 uint16_t aggregationKey = pair.first;
635 ASSERT_TRUE(aggregationKey == 0 || aggregationKey == 42);
636
637 std::vector<std::vector<uint64_t>> timesInState = pair.second;
638 uint64_t totalCpuTime = 0;
639 for (size_t i = 0; i < timesInState.size(); i++) {
640 for (size_t j = 0; j < timesInState[i].size(); j++) {
641 totalCpuTime += timesInState[i][j];
642 }
643 }
644 ASSERT_GT(totalCpuTime, 0ul);
645 ASSERT_LE(totalCpuTime, testDurationNs);
646 }
647}
648
Connor O'Brien57337192018-11-20 12:49:16 -0800649} // namespace bpf
650} // namespace android