blob: 45a6d47bb49011448a2c01952e97059310580e24 [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'Briend67821e2022-03-08 17:00:57 -080034#include <cutils/android_filesystem_config.h>
Connor O'Briena178a732019-06-05 18:27:47 -070035#include <libbpf.h>
Connor O'Brien57337192018-11-20 12:49:16 -080036
37namespace android {
38namespace bpf {
39
Connor O'Briena178a732019-06-05 18:27:47 -070040static constexpr uint64_t NSEC_PER_SEC = 1000000000;
41static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
42
Connor O'Brien57337192018-11-20 12:49:16 -080043using std::vector;
44
Connor O'Brien6f179e62020-02-18 15:54:27 -080045class TimeInStateTest : public testing::Test {
46 protected:
47 TimeInStateTest() {};
Rafal Slawik45caa842021-01-29 12:25:56 +000048
Connor O'Brien6f179e62020-02-18 15:54:27 -080049 void SetUp() {
50 if (!isTrackingUidTimesSupported() ||
51 !android::base::GetBoolProperty("sys.init.perf_lsm_hooks", false)) {
52 GTEST_SKIP();
53 }
54 }
55};
56
57TEST_F(TimeInStateTest, TotalTimeInState) {
Rafal Slawik27c48db2021-01-05 19:10:07 +000058 auto times = getTotalCpuFreqTimes();
59 ASSERT_TRUE(times.has_value());
60 EXPECT_FALSE(times->empty());
61}
62
Connor O'Brien6f179e62020-02-18 15:54:27 -080063TEST_F(TimeInStateTest, SingleUidTimeInState) {
Connor O'Brienf03b6ae2019-06-05 18:03:12 -070064 auto times = getUidCpuFreqTimes(0);
65 ASSERT_TRUE(times.has_value());
66 EXPECT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -080067}
68
Connor O'Brien6f179e62020-02-18 15:54:27 -080069TEST_F(TimeInStateTest, SingleUidConcurrentTimes) {
Connor O'Brien26de80f2019-06-11 13:49:19 -070070 auto concurrentTimes = getUidConcurrentTimes(0);
71 ASSERT_TRUE(concurrentTimes.has_value());
72 ASSERT_FALSE(concurrentTimes->active.empty());
73 ASSERT_FALSE(concurrentTimes->policy.empty());
74
75 uint64_t policyEntries = 0;
76 for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size();
77 ASSERT_EQ(concurrentTimes->active.size(), policyEntries);
78}
79
80static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) {
81 size_t maxPolicyCpus = 0;
82 for (const auto &vec : concurrentTime.policy) {
83 maxPolicyCpus = std::max(maxPolicyCpus, vec.size());
84 }
85 uint64_t policySum = 0;
86 for (size_t i = 0; i < maxPolicyCpus; ++i) {
87 for (const auto &vec : concurrentTime.policy) {
88 if (i < vec.size()) policySum += vec[i];
89 }
90 ASSERT_LE(concurrentTime.active[i], policySum);
91 policySum -= concurrentTime.active[i];
92 }
93 policySum = 0;
94 for (size_t i = 0; i < concurrentTime.active.size(); ++i) {
95 for (const auto &vec : concurrentTime.policy) {
96 if (i < vec.size()) policySum += vec[vec.size() - 1 - i];
97 }
98 auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i];
99 // This check is slightly flaky because we may read a map entry in the middle of an update
100 // when active times have been updated but policy times have not. This happens infrequently
101 // and can be distinguished from more serious bugs by re-running the test: if the underlying
102 // data itself is inconsistent, the test will fail every time.
103 ASSERT_LE(activeSum, policySum);
104 policySum -= activeSum;
105 }
106}
107
108static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState,
109 const struct concurrent_time_t &concurrentTime) {
110 ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime));
111 ASSERT_EQ(timeInState.size(), concurrentTime.policy.size());
112 uint64_t policySum = 0;
113 for (uint32_t i = 0; i < timeInState.size(); ++i) {
114 uint64_t tisSum =
115 std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0);
116 uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(),
117 concurrentTime.policy[i].end(), (uint64_t)0);
118 if (tisSum < concurrentSum)
119 ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC);
120 else
121 ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC);
122 policySum += concurrentSum;
123 }
124 uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(),
125 (uint64_t)0);
126 EXPECT_EQ(activeSum, policySum);
127}
128
Connor O'Brien6f179e62020-02-18 15:54:27 -0800129TEST_F(TimeInStateTest, SingleUidTimesConsistent) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700130 auto times = getUidCpuFreqTimes(0);
131 ASSERT_TRUE(times.has_value());
132
133 auto concurrentTimes = getUidConcurrentTimes(0);
134 ASSERT_TRUE(concurrentTimes.has_value());
135
136 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
137}
138
Connor O'Brien6f179e62020-02-18 15:54:27 -0800139TEST_F(TimeInStateTest, AllUidTimeInState) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800140 uint64_t zero = 0;
141 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
142 for (const auto &map : maps) {
143 ASSERT_TRUE(map.has_value());
Connor O'Brien57337192018-11-20 12:49:16 -0800144
Connor O'Brien2a716a42020-01-31 18:51:56 -0800145 ASSERT_FALSE(map->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800146
Connor O'Brien2a716a42020-01-31 18:51:56 -0800147 vector<size_t> sizes;
148 auto firstEntry = map->begin()->second;
149 for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
Connor O'Brien57337192018-11-20 12:49:16 -0800150
Connor O'Brien2a716a42020-01-31 18:51:56 -0800151 for (const auto &vec : *map) {
152 ASSERT_EQ(vec.second.size(), sizes.size());
153 for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
154 }
155 }
156}
157
158void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before,
159 const std::vector<std::vector<uint64_t>> &after) {
160 ASSERT_EQ(before.size(), after.size());
161 uint64_t sumBefore = 0, sumAfter = 0;
162 for (size_t i = 0; i < before.size(); ++i) {
163 ASSERT_EQ(before[i].size(), after[i].size());
164 for (size_t j = 0; j < before[i].size(); ++j) {
165 // Times should never decrease
166 ASSERT_LE(before[i][j], after[i][j]);
167 }
168 sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0);
169 sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0);
170 }
171 ASSERT_LE(sumBefore, sumAfter);
172 ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC);
173}
174
Connor O'Brien6f179e62020-02-18 15:54:27 -0800175TEST_F(TimeInStateTest, AllUidUpdatedTimeInState) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800176 uint64_t lastUpdate = 0;
177 auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
178 ASSERT_TRUE(map1.has_value());
179 ASSERT_FALSE(map1->empty());
180 ASSERT_NE(lastUpdate, (uint64_t)0);
181 uint64_t oldLastUpdate = lastUpdate;
182
183 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
184 struct timespec ts;
185 ts.tv_sec = 0;
186 ts.tv_nsec = 1000000;
187 nanosleep (&ts, NULL);
188
189 auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
190 ASSERT_TRUE(map2.has_value());
191 ASSERT_FALSE(map2->empty());
192 ASSERT_NE(lastUpdate, oldLastUpdate);
193
194 bool someUidsExcluded = false;
195 for (const auto &[uid, v] : *map1) {
196 if (map2->find(uid) == map2->end()) {
197 someUidsExcluded = true;
198 break;
199 }
200 }
201 ASSERT_TRUE(someUidsExcluded);
202
203 for (const auto &[uid, newTimes] : *map2) {
204 ASSERT_NE(map1->find(uid), map1->end());
205 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes));
Connor O'Brien57337192018-11-20 12:49:16 -0800206 }
207}
208
Connor O'Brien6f179e62020-02-18 15:54:27 -0800209TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) {
Rafal Slawik27c48db2021-01-05 19:10:07 +0000210 auto allUid = getUidsCpuFreqTimes();
211 auto total = getTotalCpuFreqTimes();
212
213 ASSERT_TRUE(allUid.has_value() && total.has_value());
214
215 // Check the number of policies.
216 ASSERT_EQ(allUid->at(0).size(), total->size());
217
218 for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) {
219 std::vector<uint64_t> totalTimes = total->at(policyIdx);
220 uint32_t totalFreqsCount = totalTimes.size();
221 std::vector<uint64_t> allUidTimes(totalFreqsCount, 0);
222 for (auto const &[uid, uidTimes]: *allUid) {
Connor O'Briend67821e2022-03-08 17:00:57 -0800223 if (uid == AID_SDK_SANDBOX) continue;
Rafal Slawik27c48db2021-01-05 19:10:07 +0000224 for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) {
225 allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx];
226 }
227 }
228
229 for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) {
230 ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]);
231 }
232 }
233}
234
Connor O'Brien6f179e62020-02-18 15:54:27 -0800235TEST_F(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800236 uint64_t zero = 0;
237 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
238 for (const auto &map : maps) {
239 ASSERT_TRUE(map.has_value());
240 ASSERT_FALSE(map->empty());
Connor O'Briena178a732019-06-05 18:27:47 -0700241
Connor O'Brien2a716a42020-01-31 18:51:56 -0800242 for (const auto &kv : *map) {
243 uint32_t uid = kv.first;
244 auto times1 = kv.second;
245 auto times2 = getUidCpuFreqTimes(uid);
246 ASSERT_TRUE(times2.has_value());
Connor O'Briena178a732019-06-05 18:27:47 -0700247
Connor O'Brien2a716a42020-01-31 18:51:56 -0800248 ASSERT_EQ(times1.size(), times2->size());
249 for (uint32_t i = 0; i < times1.size(); ++i) {
250 ASSERT_EQ(times1[i].size(), (*times2)[i].size());
251 for (uint32_t j = 0; j < times1[i].size(); ++j) {
252 ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
253 }
Connor O'Briena178a732019-06-05 18:27:47 -0700254 }
255 }
256 }
257}
258
Connor O'Brien6f179e62020-02-18 15:54:27 -0800259TEST_F(TimeInStateTest, AllUidConcurrentTimes) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800260 uint64_t zero = 0;
261 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
262 for (const auto &map : maps) {
263 ASSERT_TRUE(map.has_value());
264 ASSERT_FALSE(map->empty());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700265
Connor O'Brien2a716a42020-01-31 18:51:56 -0800266 auto firstEntry = map->begin()->second;
267 for (const auto &kv : *map) {
268 ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
269 ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
270 for (size_t i = 0; i < kv.second.policy.size(); ++i) {
271 ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
272 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700273 }
274 }
275}
276
Connor O'Brien6f179e62020-02-18 15:54:27 -0800277TEST_F(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800278 uint64_t lastUpdate = 0;
279 auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
280 ASSERT_TRUE(map1.has_value());
281 ASSERT_FALSE(map1->empty());
282 ASSERT_NE(lastUpdate, (uint64_t)0);
283
284 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
285 struct timespec ts;
286 ts.tv_sec = 0;
287 ts.tv_nsec = 1000000;
288 nanosleep (&ts, NULL);
289
290 uint64_t oldLastUpdate = lastUpdate;
291 auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate);
292 ASSERT_TRUE(map2.has_value());
293 ASSERT_FALSE(map2->empty());
294 ASSERT_NE(lastUpdate, oldLastUpdate);
295
296 bool someUidsExcluded = false;
297 for (const auto &[uid, v] : *map1) {
298 if (map2->find(uid) == map2->end()) {
299 someUidsExcluded = true;
300 break;
Connor O'Brien26de80f2019-06-11 13:49:19 -0700301 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800302 }
303 ASSERT_TRUE(someUidsExcluded);
304
305 for (const auto &[uid, newTimes] : *map2) {
306 ASSERT_NE(map1->find(uid), map1->end());
307 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active}));
308 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy));
309 }
310}
311
Connor O'Brien6f179e62020-02-18 15:54:27 -0800312TEST_F(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800313 uint64_t zero = 0;
314 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
315 for (const auto &map : maps) {
316 ASSERT_TRUE(map.has_value());
317 for (const auto &kv : *map) {
318 uint32_t uid = kv.first;
319 auto times1 = kv.second;
320 auto times2 = getUidConcurrentTimes(uid);
321 ASSERT_TRUE(times2.has_value());
322 for (uint32_t i = 0; i < times1.active.size(); ++i) {
323 ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
324 }
325 for (uint32_t i = 0; i < times1.policy.size(); ++i) {
326 for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
327 ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
328 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700329 }
330 }
331 }
332}
333
Connor O'Briena178a732019-06-05 18:27:47 -0700334void TestCheckDelta(uint64_t before, uint64_t after) {
335 // Times should never decrease
336 ASSERT_LE(before, after);
337 // UID can't have run for more than ~1s on each CPU
338 ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
339}
340
Connor O'Brien6f179e62020-02-18 15:54:27 -0800341TEST_F(TimeInStateTest, TotalTimeInStateMonotonic) {
Rafal Slawik27c48db2021-01-05 19:10:07 +0000342 auto before = getTotalCpuFreqTimes();
343 ASSERT_TRUE(before.has_value());
344 sleep(1);
345 auto after = getTotalCpuFreqTimes();
346 ASSERT_TRUE(after.has_value());
347
348 for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) {
349 auto timesBefore = before->at(policyIdx);
350 auto timesAfter = after->at(policyIdx);
351 for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) {
352 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx]));
353 }
354 }
355}
356
Connor O'Brien6f179e62020-02-18 15:54:27 -0800357TEST_F(TimeInStateTest, AllUidTimeInStateMonotonic) {
Connor O'Briena178a732019-06-05 18:27:47 -0700358 auto map1 = getUidsCpuFreqTimes();
359 ASSERT_TRUE(map1.has_value());
360 sleep(1);
361 auto map2 = getUidsCpuFreqTimes();
362 ASSERT_TRUE(map2.has_value());
363
364 for (const auto &kv : *map1) {
365 uint32_t uid = kv.first;
366 auto times = kv.second;
367 ASSERT_NE(map2->find(uid), map2->end());
368 for (uint32_t policy = 0; policy < times.size(); ++policy) {
369 for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) {
370 auto before = times[policy][freqIdx];
371 auto after = (*map2)[uid][policy][freqIdx];
372 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
373 }
374 }
375 }
376}
377
Connor O'Brien6f179e62020-02-18 15:54:27 -0800378TEST_F(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700379 auto map1 = getUidsConcurrentTimes();
380 ASSERT_TRUE(map1.has_value());
381 ASSERT_FALSE(map1->empty());
382 sleep(1);
383 auto map2 = getUidsConcurrentTimes();
384 ASSERT_TRUE(map2.has_value());
385 ASSERT_FALSE(map2->empty());
386
387 for (const auto &kv : *map1) {
388 uint32_t uid = kv.first;
389 auto times = kv.second;
390 ASSERT_NE(map2->find(uid), map2->end());
391 for (uint32_t i = 0; i < times.active.size(); ++i) {
392 auto before = times.active[i];
393 auto after = (*map2)[uid].active[i];
394 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
395 }
396 for (uint32_t policy = 0; policy < times.policy.size(); ++policy) {
397 for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) {
398 auto before = times.policy[policy][idx];
399 auto after = (*map2)[uid].policy[policy][idx];
400 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
401 }
402 }
403 }
404}
405
Connor O'Brien6f179e62020-02-18 15:54:27 -0800406TEST_F(TimeInStateTest, AllUidTimeInStateSanityCheck) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800407 uint64_t zero = 0;
408 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
409 for (const auto &map : maps) {
410 ASSERT_TRUE(map.has_value());
Connor O'Briena178a732019-06-05 18:27:47 -0700411
Connor O'Brien2a716a42020-01-31 18:51:56 -0800412 bool foundLargeValue = false;
413 for (const auto &kv : *map) {
414 for (const auto &timeVec : kv.second) {
415 for (const auto &time : timeVec) {
416 ASSERT_LE(time, NSEC_PER_YEAR);
417 if (time > UINT32_MAX) foundLargeValue = true;
418 }
Connor O'Briena178a732019-06-05 18:27:47 -0700419 }
420 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800421 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
422 // uint64_t as expected, we should have some times higher than that.
423 ASSERT_TRUE(foundLargeValue);
Connor O'Briena178a732019-06-05 18:27:47 -0700424 }
Connor O'Briena178a732019-06-05 18:27:47 -0700425}
426
Connor O'Brien6f179e62020-02-18 15:54:27 -0800427TEST_F(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800428 uint64_t zero = 0;
429 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
430 for (const auto &concurrentMap : maps) {
431 ASSERT_TRUE(concurrentMap);
Connor O'Brien26de80f2019-06-11 13:49:19 -0700432
Connor O'Brien2a716a42020-01-31 18:51:56 -0800433 bool activeFoundLargeValue = false;
434 bool policyFoundLargeValue = false;
435 for (const auto &kv : *concurrentMap) {
436 for (const auto &time : kv.second.active) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700437 ASSERT_LE(time, NSEC_PER_YEAR);
Connor O'Brien2a716a42020-01-31 18:51:56 -0800438 if (time > UINT32_MAX) activeFoundLargeValue = true;
439 }
440 for (const auto &policyTimeVec : kv.second.policy) {
441 for (const auto &time : policyTimeVec) {
442 ASSERT_LE(time, NSEC_PER_YEAR);
443 if (time > UINT32_MAX) policyFoundLargeValue = true;
444 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700445 }
446 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800447 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
448 // uint64_t as expected, we should have some times higher than that.
449 ASSERT_TRUE(activeFoundLargeValue);
450 ASSERT_TRUE(policyFoundLargeValue);
Connor O'Brien26de80f2019-06-11 13:49:19 -0700451 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700452}
453
Connor O'Brien6f179e62020-02-18 15:54:27 -0800454TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
Connor O'Brien597f6372020-10-20 14:48:40 -0700455 uint32_t uid = 0;
456 {
457 // Find an unused UID
458 auto map = getUidsConcurrentTimes();
459 ASSERT_TRUE(map.has_value());
460 ASSERT_FALSE(map->empty());
461 for (const auto &kv : *map) uid = std::max(uid, kv.first);
462 ++uid;
463 }
464 android::base::unique_fd fd{
465 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
466 ASSERT_GE(fd, 0);
467 uint32_t nCpus = get_nprocs_conf();
468 uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
469 time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
470 std::vector<concurrent_val_t> vals(nCpus);
471 ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
472 EXPECT_FALSE(getUidsConcurrentTimes().has_value());
473 ASSERT_FALSE(deleteMapEntry(fd, &key));
474}
475
Connor O'Brien6f179e62020-02-18 15:54:27 -0800476TEST_F(TimeInStateTest, AllUidTimesConsistent) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700477 auto tisMap = getUidsCpuFreqTimes();
478 ASSERT_TRUE(tisMap.has_value());
479
480 auto concurrentMap = getUidsConcurrentTimes();
481 ASSERT_TRUE(concurrentMap.has_value());
482
483 ASSERT_EQ(tisMap->size(), concurrentMap->size());
484 for (const auto &kv : *tisMap) {
485 uint32_t uid = kv.first;
486 auto times = kv.second;
487 ASSERT_NE(concurrentMap->find(uid), concurrentMap->end());
488
489 auto concurrentTimes = (*concurrentMap)[uid];
490 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes));
491 }
492}
493
Connor O'Brien6f179e62020-02-18 15:54:27 -0800494TEST_F(TimeInStateTest, RemoveUid) {
Connor O'Briena178a732019-06-05 18:27:47 -0700495 uint32_t uid = 0;
496 {
497 // Find an unused UID
498 auto times = getUidsCpuFreqTimes();
499 ASSERT_TRUE(times.has_value());
500 ASSERT_FALSE(times->empty());
501 for (const auto &kv : *times) uid = std::max(uid, kv.first);
502 ++uid;
503 }
504 {
505 // Add a map entry for our fake UID by copying a real map entry
Connor O'Brien26de80f2019-06-11 13:49:19 -0700506 android::base::unique_fd fd{
507 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
Connor O'Briena178a732019-06-05 18:27:47 -0700508 ASSERT_GE(fd, 0);
509 time_key_t k;
510 ASSERT_FALSE(getFirstMapKey(fd, &k));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700511 std::vector<tis_val_t> vals(get_nprocs_conf());
Connor O'Brien1a180402019-06-07 16:39:49 -0700512 ASSERT_FALSE(findMapEntry(fd, &k, vals.data()));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700513 uint32_t copiedUid = k.uid;
Connor O'Briena178a732019-06-05 18:27:47 -0700514 k.uid = uid;
Connor O'Brien1a180402019-06-07 16:39:49 -0700515 ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700516
517 android::base::unique_fd fd2{
518 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
519 k.uid = copiedUid;
520 k.bucket = 0;
521 std::vector<concurrent_val_t> cvals(get_nprocs_conf());
522 ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data()));
523 k.uid = uid;
524 ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST));
Connor O'Briena178a732019-06-05 18:27:47 -0700525 }
526 auto times = getUidCpuFreqTimes(uid);
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700527 ASSERT_TRUE(times.has_value());
528 ASSERT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800529
Connor O'Brien26de80f2019-06-11 13:49:19 -0700530 auto concurrentTimes = getUidConcurrentTimes(0);
531 ASSERT_TRUE(concurrentTimes.has_value());
532 ASSERT_FALSE(concurrentTimes->active.empty());
533 ASSERT_FALSE(concurrentTimes->policy.empty());
534
Connor O'Brien57337192018-11-20 12:49:16 -0800535 uint64_t sum = 0;
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700536 for (size_t i = 0; i < times->size(); ++i) {
537 for (auto x : (*times)[i]) sum += x;
Connor O'Brien57337192018-11-20 12:49:16 -0800538 }
539 ASSERT_GT(sum, (uint64_t)0);
540
Connor O'Brien26de80f2019-06-11 13:49:19 -0700541 uint64_t activeSum = 0;
542 for (size_t i = 0; i < concurrentTimes->active.size(); ++i) {
543 activeSum += concurrentTimes->active[i];
544 }
545 ASSERT_GT(activeSum, (uint64_t)0);
546
547 ASSERT_TRUE(clearUidTimes(uid));
Connor O'Brien57337192018-11-20 12:49:16 -0800548
Connor O'Briena178a732019-06-05 18:27:47 -0700549 auto allTimes = getUidsCpuFreqTimes();
550 ASSERT_TRUE(allTimes.has_value());
551 ASSERT_FALSE(allTimes->empty());
552 ASSERT_EQ(allTimes->find(uid), allTimes->end());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700553
554 auto allConcurrentTimes = getUidsConcurrentTimes();
555 ASSERT_TRUE(allConcurrentTimes.has_value());
556 ASSERT_FALSE(allConcurrentTimes->empty());
557 ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
Connor O'Brien57337192018-11-20 12:49:16 -0800558}
559
Connor O'Brien6f179e62020-02-18 15:54:27 -0800560TEST_F(TimeInStateTest, GetCpuFreqs) {
Connor O'Brien8f296eb2019-10-01 17:58:38 -0700561 auto freqs = getCpuFreqs();
562 ASSERT_TRUE(freqs.has_value());
563
564 auto times = getUidCpuFreqTimes(0);
565 ASSERT_TRUE(times.has_value());
566
567 ASSERT_EQ(freqs->size(), times->size());
568 for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size());
569}
570
Dmitri Plotnikov2677dba2020-10-17 21:06:55 -0700571uint64_t timeNanos() {
572 struct timespec spec;
573 clock_gettime(CLOCK_MONOTONIC, &spec);
574 return spec.tv_sec * 1000000000 + spec.tv_nsec;
575}
576
577// Keeps CPU busy with some number crunching
578void useCpu() {
579 long sum = 0;
580 for (int i = 0; i < 100000; i++) {
581 sum *= i;
582 }
583}
584
585sem_t pingsem, pongsem;
586
587void *testThread(void *) {
588 for (int i = 0; i < 10; i++) {
589 sem_wait(&pingsem);
590 useCpu();
591 sem_post(&pongsem);
592 }
593 return nullptr;
594}
595
Connor O'Brien6f179e62020-02-18 15:54:27 -0800596TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) {
Dmitri Plotnikov2677dba2020-10-17 21:06:55 -0700597 uint64_t startTimeNs = timeNanos();
598
599 sem_init(&pingsem, 0, 1);
600 sem_init(&pongsem, 0, 0);
601
602 pthread_t thread;
603 ASSERT_EQ(pthread_create(&thread, NULL, &testThread, NULL), 0);
604
605 // This process may have been running for some time, so when we start tracking
606 // CPU time, the very first switch may include the accumulated time.
607 // Yield the remainder of this timeslice to the newly created thread.
608 sem_wait(&pongsem);
609 sem_post(&pingsem);
610
611 pid_t tgid = getpid();
612 startTrackingProcessCpuTimes(tgid);
613
614 pid_t tid = pthread_gettid_np(thread);
615 startAggregatingTaskCpuTimes(tid, 42);
616
617 // Play ping-pong with the other thread to ensure that both threads get
618 // some CPU time.
619 for (int i = 0; i < 9; i++) {
620 sem_wait(&pongsem);
621 useCpu();
622 sem_post(&pingsem);
623 }
624
625 pthread_join(thread, NULL);
626
627 std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> optionalMap =
628 getAggregatedTaskCpuFreqTimes(tgid, {0, 42});
629 ASSERT_TRUE(optionalMap);
630
631 std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map = *optionalMap;
632 ASSERT_EQ(map.size(), 2u);
633
634 uint64_t testDurationNs = timeNanos() - startTimeNs;
635 for (auto pair : map) {
636 uint16_t aggregationKey = pair.first;
637 ASSERT_TRUE(aggregationKey == 0 || aggregationKey == 42);
638
639 std::vector<std::vector<uint64_t>> timesInState = pair.second;
640 uint64_t totalCpuTime = 0;
641 for (size_t i = 0; i < timesInState.size(); i++) {
642 for (size_t j = 0; j < timesInState[i].size(); j++) {
643 totalCpuTime += timesInState[i][j];
644 }
645 }
646 ASSERT_GT(totalCpuTime, 0ul);
647 ASSERT_LE(totalCpuTime, testDurationNs);
648 }
649}
650
Connor O'Briend67821e2022-03-08 17:00:57 -0800651void *forceSwitchWithUid(void *uidPtr) {
652 if (!uidPtr) return nullptr;
653 setuid(*(uint32_t *)uidPtr);
654
655 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
656 struct timespec ts;
657 ts.tv_sec = 0;
658 ts.tv_nsec = 1000000;
659 nanosleep(&ts, NULL);
660 return nullptr;
661}
662
663TEST_F(TimeInStateTest, SdkSandboxUid) {
664 // Find an unused app UID and its corresponding SDK sandbox uid.
665 uint32_t appUid = AID_APP_START, sandboxUid;
666 {
667 auto times = getUidsCpuFreqTimes();
668 ASSERT_TRUE(times.has_value());
669 ASSERT_FALSE(times->empty());
670 for (const auto &kv : *times) {
671 if (kv.first > AID_APP_END) break;
672 appUid = std::max(appUid, kv.first);
673 }
674 appUid++;
675 sandboxUid = appUid + (AID_SDK_SANDBOX_PROCESS_START - AID_APP_START);
676 }
677
678 // Create a thread to run with the fake sandbox uid.
679 pthread_t thread;
680 ASSERT_EQ(pthread_create(&thread, NULL, &forceSwitchWithUid, &sandboxUid), 0);
681 pthread_join(thread, NULL);
682
683 // Confirm we recorded stats for appUid and AID_SDK_SANDBOX but not sandboxUid
684 auto allTimes = getUidsCpuFreqTimes();
685 ASSERT_TRUE(allTimes.has_value());
686 ASSERT_FALSE(allTimes->empty());
687 ASSERT_NE(allTimes->find(appUid), allTimes->end());
688 ASSERT_NE(allTimes->find(AID_SDK_SANDBOX), allTimes->end());
689 ASSERT_EQ(allTimes->find(sandboxUid), allTimes->end());
690
691 auto allConcurrentTimes = getUidsConcurrentTimes();
692 ASSERT_TRUE(allConcurrentTimes.has_value());
693 ASSERT_FALSE(allConcurrentTimes->empty());
694 ASSERT_NE(allConcurrentTimes->find(appUid), allConcurrentTimes->end());
695 ASSERT_NE(allConcurrentTimes->find(AID_SDK_SANDBOX), allConcurrentTimes->end());
696 ASSERT_EQ(allConcurrentTimes->find(sandboxUid), allConcurrentTimes->end());
697
698 ASSERT_TRUE(clearUidTimes(appUid));
699}
700
Connor O'Brien57337192018-11-20 12:49:16 -0800701} // namespace bpf
702} // namespace android