blob: 519689bb0c67a5a196632723e8c8023798845d0d [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'Briena178a732019-06-05 18:27:47 -070030#include <android-base/unique_fd.h>
31#include <bpf/BpfMap.h>
Connor O'Brien57337192018-11-20 12:49:16 -080032#include <cputimeinstate.h>
Connor O'Briena178a732019-06-05 18:27:47 -070033#include <libbpf.h>
Connor O'Brien57337192018-11-20 12:49:16 -080034
35namespace android {
36namespace bpf {
37
Connor O'Briena178a732019-06-05 18:27:47 -070038static constexpr uint64_t NSEC_PER_SEC = 1000000000;
39static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
40
Connor O'Brien57337192018-11-20 12:49:16 -080041using std::vector;
42
Connor O'Brien26de80f2019-06-11 13:49:19 -070043TEST(TimeInStateTest, SingleUidTimeInState) {
Connor O'Brienf03b6ae2019-06-05 18:03:12 -070044 auto times = getUidCpuFreqTimes(0);
45 ASSERT_TRUE(times.has_value());
46 EXPECT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -080047}
48
Connor O'Brien26de80f2019-06-11 13:49:19 -070049TEST(TimeInStateTest, SingleUidConcurrentTimes) {
50 auto concurrentTimes = getUidConcurrentTimes(0);
51 ASSERT_TRUE(concurrentTimes.has_value());
52 ASSERT_FALSE(concurrentTimes->active.empty());
53 ASSERT_FALSE(concurrentTimes->policy.empty());
54
55 uint64_t policyEntries = 0;
56 for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size();
57 ASSERT_EQ(concurrentTimes->active.size(), policyEntries);
58}
59
60static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) {
61 size_t maxPolicyCpus = 0;
62 for (const auto &vec : concurrentTime.policy) {
63 maxPolicyCpus = std::max(maxPolicyCpus, vec.size());
64 }
65 uint64_t policySum = 0;
66 for (size_t i = 0; i < maxPolicyCpus; ++i) {
67 for (const auto &vec : concurrentTime.policy) {
68 if (i < vec.size()) policySum += vec[i];
69 }
70 ASSERT_LE(concurrentTime.active[i], policySum);
71 policySum -= concurrentTime.active[i];
72 }
73 policySum = 0;
74 for (size_t i = 0; i < concurrentTime.active.size(); ++i) {
75 for (const auto &vec : concurrentTime.policy) {
76 if (i < vec.size()) policySum += vec[vec.size() - 1 - i];
77 }
78 auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i];
79 // This check is slightly flaky because we may read a map entry in the middle of an update
80 // when active times have been updated but policy times have not. This happens infrequently
81 // and can be distinguished from more serious bugs by re-running the test: if the underlying
82 // data itself is inconsistent, the test will fail every time.
83 ASSERT_LE(activeSum, policySum);
84 policySum -= activeSum;
85 }
86}
87
88static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState,
89 const struct concurrent_time_t &concurrentTime) {
90 ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime));
91 ASSERT_EQ(timeInState.size(), concurrentTime.policy.size());
92 uint64_t policySum = 0;
93 for (uint32_t i = 0; i < timeInState.size(); ++i) {
94 uint64_t tisSum =
95 std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0);
96 uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(),
97 concurrentTime.policy[i].end(), (uint64_t)0);
98 if (tisSum < concurrentSum)
99 ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC);
100 else
101 ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC);
102 policySum += concurrentSum;
103 }
104 uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(),
105 (uint64_t)0);
106 EXPECT_EQ(activeSum, policySum);
107}
108
109TEST(TimeInStateTest, SingleUidTimesConsistent) {
110 auto times = getUidCpuFreqTimes(0);
111 ASSERT_TRUE(times.has_value());
112
113 auto concurrentTimes = getUidConcurrentTimes(0);
114 ASSERT_TRUE(concurrentTimes.has_value());
115
116 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
117}
118
119TEST(TimeInStateTest, AllUidTimeInState) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800120 uint64_t zero = 0;
121 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
122 for (const auto &map : maps) {
123 ASSERT_TRUE(map.has_value());
Connor O'Brien57337192018-11-20 12:49:16 -0800124
Connor O'Brien2a716a42020-01-31 18:51:56 -0800125 ASSERT_FALSE(map->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800126
Connor O'Brien2a716a42020-01-31 18:51:56 -0800127 vector<size_t> sizes;
128 auto firstEntry = map->begin()->second;
129 for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
Connor O'Brien57337192018-11-20 12:49:16 -0800130
Connor O'Brien2a716a42020-01-31 18:51:56 -0800131 for (const auto &vec : *map) {
132 ASSERT_EQ(vec.second.size(), sizes.size());
133 for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
134 }
135 }
136}
137
138void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before,
139 const std::vector<std::vector<uint64_t>> &after) {
140 ASSERT_EQ(before.size(), after.size());
141 uint64_t sumBefore = 0, sumAfter = 0;
142 for (size_t i = 0; i < before.size(); ++i) {
143 ASSERT_EQ(before[i].size(), after[i].size());
144 for (size_t j = 0; j < before[i].size(); ++j) {
145 // Times should never decrease
146 ASSERT_LE(before[i][j], after[i][j]);
147 }
148 sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0);
149 sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0);
150 }
151 ASSERT_LE(sumBefore, sumAfter);
152 ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC);
153}
154
155TEST(TimeInStateTest, AllUidUpdatedTimeInState) {
156 uint64_t lastUpdate = 0;
157 auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
158 ASSERT_TRUE(map1.has_value());
159 ASSERT_FALSE(map1->empty());
160 ASSERT_NE(lastUpdate, (uint64_t)0);
161 uint64_t oldLastUpdate = lastUpdate;
162
163 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
164 struct timespec ts;
165 ts.tv_sec = 0;
166 ts.tv_nsec = 1000000;
167 nanosleep (&ts, NULL);
168
169 auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
170 ASSERT_TRUE(map2.has_value());
171 ASSERT_FALSE(map2->empty());
172 ASSERT_NE(lastUpdate, oldLastUpdate);
173
174 bool someUidsExcluded = false;
175 for (const auto &[uid, v] : *map1) {
176 if (map2->find(uid) == map2->end()) {
177 someUidsExcluded = true;
178 break;
179 }
180 }
181 ASSERT_TRUE(someUidsExcluded);
182
183 for (const auto &[uid, newTimes] : *map2) {
184 ASSERT_NE(map1->find(uid), map1->end());
185 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes));
Connor O'Brien57337192018-11-20 12:49:16 -0800186 }
187}
188
Connor O'Brien26de80f2019-06-11 13:49:19 -0700189TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800190 uint64_t zero = 0;
191 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
192 for (const auto &map : maps) {
193 ASSERT_TRUE(map.has_value());
194 ASSERT_FALSE(map->empty());
Connor O'Briena178a732019-06-05 18:27:47 -0700195
Connor O'Brien2a716a42020-01-31 18:51:56 -0800196 for (const auto &kv : *map) {
197 uint32_t uid = kv.first;
198 auto times1 = kv.second;
199 auto times2 = getUidCpuFreqTimes(uid);
200 ASSERT_TRUE(times2.has_value());
Connor O'Briena178a732019-06-05 18:27:47 -0700201
Connor O'Brien2a716a42020-01-31 18:51:56 -0800202 ASSERT_EQ(times1.size(), times2->size());
203 for (uint32_t i = 0; i < times1.size(); ++i) {
204 ASSERT_EQ(times1[i].size(), (*times2)[i].size());
205 for (uint32_t j = 0; j < times1[i].size(); ++j) {
206 ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
207 }
Connor O'Briena178a732019-06-05 18:27:47 -0700208 }
209 }
210 }
211}
212
Connor O'Brien26de80f2019-06-11 13:49:19 -0700213TEST(TimeInStateTest, AllUidConcurrentTimes) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800214 uint64_t zero = 0;
215 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
216 for (const auto &map : maps) {
217 ASSERT_TRUE(map.has_value());
218 ASSERT_FALSE(map->empty());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700219
Connor O'Brien2a716a42020-01-31 18:51:56 -0800220 auto firstEntry = map->begin()->second;
221 for (const auto &kv : *map) {
222 ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
223 ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
224 for (size_t i = 0; i < kv.second.policy.size(); ++i) {
225 ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
226 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700227 }
228 }
229}
230
Connor O'Brien2a716a42020-01-31 18:51:56 -0800231TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
232 uint64_t lastUpdate = 0;
233 auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
234 ASSERT_TRUE(map1.has_value());
235 ASSERT_FALSE(map1->empty());
236 ASSERT_NE(lastUpdate, (uint64_t)0);
237
238 // Sleep briefly to trigger a context switch, ensuring we see at least one update.
239 struct timespec ts;
240 ts.tv_sec = 0;
241 ts.tv_nsec = 1000000;
242 nanosleep (&ts, NULL);
243
244 uint64_t oldLastUpdate = lastUpdate;
245 auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate);
246 ASSERT_TRUE(map2.has_value());
247 ASSERT_FALSE(map2->empty());
248 ASSERT_NE(lastUpdate, oldLastUpdate);
249
250 bool someUidsExcluded = false;
251 for (const auto &[uid, v] : *map1) {
252 if (map2->find(uid) == map2->end()) {
253 someUidsExcluded = true;
254 break;
Connor O'Brien26de80f2019-06-11 13:49:19 -0700255 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800256 }
257 ASSERT_TRUE(someUidsExcluded);
258
259 for (const auto &[uid, newTimes] : *map2) {
260 ASSERT_NE(map1->find(uid), map1->end());
261 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active}));
262 ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy));
263 }
264}
265
266TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
267 uint64_t zero = 0;
268 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
269 for (const auto &map : maps) {
270 ASSERT_TRUE(map.has_value());
271 for (const auto &kv : *map) {
272 uint32_t uid = kv.first;
273 auto times1 = kv.second;
274 auto times2 = getUidConcurrentTimes(uid);
275 ASSERT_TRUE(times2.has_value());
276 for (uint32_t i = 0; i < times1.active.size(); ++i) {
277 ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
278 }
279 for (uint32_t i = 0; i < times1.policy.size(); ++i) {
280 for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
281 ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
282 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700283 }
284 }
285 }
286}
287
Connor O'Briena178a732019-06-05 18:27:47 -0700288void TestCheckDelta(uint64_t before, uint64_t after) {
289 // Times should never decrease
290 ASSERT_LE(before, after);
291 // UID can't have run for more than ~1s on each CPU
292 ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
293}
294
Connor O'Brien26de80f2019-06-11 13:49:19 -0700295TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
Connor O'Briena178a732019-06-05 18:27:47 -0700296 auto map1 = getUidsCpuFreqTimes();
297 ASSERT_TRUE(map1.has_value());
298 sleep(1);
299 auto map2 = getUidsCpuFreqTimes();
300 ASSERT_TRUE(map2.has_value());
301
302 for (const auto &kv : *map1) {
303 uint32_t uid = kv.first;
304 auto times = kv.second;
305 ASSERT_NE(map2->find(uid), map2->end());
306 for (uint32_t policy = 0; policy < times.size(); ++policy) {
307 for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) {
308 auto before = times[policy][freqIdx];
309 auto after = (*map2)[uid][policy][freqIdx];
310 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
311 }
312 }
313 }
314}
315
Connor O'Brien26de80f2019-06-11 13:49:19 -0700316TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
317 auto map1 = getUidsConcurrentTimes();
318 ASSERT_TRUE(map1.has_value());
319 ASSERT_FALSE(map1->empty());
320 sleep(1);
321 auto map2 = getUidsConcurrentTimes();
322 ASSERT_TRUE(map2.has_value());
323 ASSERT_FALSE(map2->empty());
324
325 for (const auto &kv : *map1) {
326 uint32_t uid = kv.first;
327 auto times = kv.second;
328 ASSERT_NE(map2->find(uid), map2->end());
329 for (uint32_t i = 0; i < times.active.size(); ++i) {
330 auto before = times.active[i];
331 auto after = (*map2)[uid].active[i];
332 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
333 }
334 for (uint32_t policy = 0; policy < times.policy.size(); ++policy) {
335 for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) {
336 auto before = times.policy[policy][idx];
337 auto after = (*map2)[uid].policy[policy][idx];
338 ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
339 }
340 }
341 }
342}
343
344TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800345 uint64_t zero = 0;
346 auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
347 for (const auto &map : maps) {
348 ASSERT_TRUE(map.has_value());
Connor O'Briena178a732019-06-05 18:27:47 -0700349
Connor O'Brien2a716a42020-01-31 18:51:56 -0800350 bool foundLargeValue = false;
351 for (const auto &kv : *map) {
352 for (const auto &timeVec : kv.second) {
353 for (const auto &time : timeVec) {
354 ASSERT_LE(time, NSEC_PER_YEAR);
355 if (time > UINT32_MAX) foundLargeValue = true;
356 }
Connor O'Briena178a732019-06-05 18:27:47 -0700357 }
358 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800359 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
360 // uint64_t as expected, we should have some times higher than that.
361 ASSERT_TRUE(foundLargeValue);
Connor O'Briena178a732019-06-05 18:27:47 -0700362 }
Connor O'Briena178a732019-06-05 18:27:47 -0700363}
364
Connor O'Brien26de80f2019-06-11 13:49:19 -0700365TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
Connor O'Brien2a716a42020-01-31 18:51:56 -0800366 uint64_t zero = 0;
367 auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
368 for (const auto &concurrentMap : maps) {
369 ASSERT_TRUE(concurrentMap);
Connor O'Brien26de80f2019-06-11 13:49:19 -0700370
Connor O'Brien2a716a42020-01-31 18:51:56 -0800371 bool activeFoundLargeValue = false;
372 bool policyFoundLargeValue = false;
373 for (const auto &kv : *concurrentMap) {
374 for (const auto &time : kv.second.active) {
Connor O'Brien26de80f2019-06-11 13:49:19 -0700375 ASSERT_LE(time, NSEC_PER_YEAR);
Connor O'Brien2a716a42020-01-31 18:51:56 -0800376 if (time > UINT32_MAX) activeFoundLargeValue = true;
377 }
378 for (const auto &policyTimeVec : kv.second.policy) {
379 for (const auto &time : policyTimeVec) {
380 ASSERT_LE(time, NSEC_PER_YEAR);
381 if (time > UINT32_MAX) policyFoundLargeValue = true;
382 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700383 }
384 }
Connor O'Brien2a716a42020-01-31 18:51:56 -0800385 // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
386 // uint64_t as expected, we should have some times higher than that.
387 ASSERT_TRUE(activeFoundLargeValue);
388 ASSERT_TRUE(policyFoundLargeValue);
Connor O'Brien26de80f2019-06-11 13:49:19 -0700389 }
Connor O'Brien26de80f2019-06-11 13:49:19 -0700390}
391
Connor O'Brien597f6372020-10-20 14:48:40 -0700392TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
393 uint32_t uid = 0;
394 {
395 // Find an unused UID
396 auto map = getUidsConcurrentTimes();
397 ASSERT_TRUE(map.has_value());
398 ASSERT_FALSE(map->empty());
399 for (const auto &kv : *map) uid = std::max(uid, kv.first);
400 ++uid;
401 }
402 android::base::unique_fd fd{
403 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
404 ASSERT_GE(fd, 0);
405 uint32_t nCpus = get_nprocs_conf();
406 uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
407 time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
408 std::vector<concurrent_val_t> vals(nCpus);
409 ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
410 EXPECT_FALSE(getUidsConcurrentTimes().has_value());
411 ASSERT_FALSE(deleteMapEntry(fd, &key));
412}
413
Connor O'Brien26de80f2019-06-11 13:49:19 -0700414TEST(TimeInStateTest, AllUidTimesConsistent) {
415 auto tisMap = getUidsCpuFreqTimes();
416 ASSERT_TRUE(tisMap.has_value());
417
418 auto concurrentMap = getUidsConcurrentTimes();
419 ASSERT_TRUE(concurrentMap.has_value());
420
421 ASSERT_EQ(tisMap->size(), concurrentMap->size());
422 for (const auto &kv : *tisMap) {
423 uint32_t uid = kv.first;
424 auto times = kv.second;
425 ASSERT_NE(concurrentMap->find(uid), concurrentMap->end());
426
427 auto concurrentTimes = (*concurrentMap)[uid];
428 ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes));
429 }
430}
431
Connor O'Brien57337192018-11-20 12:49:16 -0800432TEST(TimeInStateTest, RemoveUid) {
Connor O'Briena178a732019-06-05 18:27:47 -0700433 uint32_t uid = 0;
434 {
435 // Find an unused UID
436 auto times = getUidsCpuFreqTimes();
437 ASSERT_TRUE(times.has_value());
438 ASSERT_FALSE(times->empty());
439 for (const auto &kv : *times) uid = std::max(uid, kv.first);
440 ++uid;
441 }
442 {
443 // Add a map entry for our fake UID by copying a real map entry
Connor O'Brien26de80f2019-06-11 13:49:19 -0700444 android::base::unique_fd fd{
445 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
Connor O'Briena178a732019-06-05 18:27:47 -0700446 ASSERT_GE(fd, 0);
447 time_key_t k;
448 ASSERT_FALSE(getFirstMapKey(fd, &k));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700449 std::vector<tis_val_t> vals(get_nprocs_conf());
Connor O'Brien1a180402019-06-07 16:39:49 -0700450 ASSERT_FALSE(findMapEntry(fd, &k, vals.data()));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700451 uint32_t copiedUid = k.uid;
Connor O'Briena178a732019-06-05 18:27:47 -0700452 k.uid = uid;
Connor O'Brien1a180402019-06-07 16:39:49 -0700453 ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST));
Connor O'Brien26de80f2019-06-11 13:49:19 -0700454
455 android::base::unique_fd fd2{
456 bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
457 k.uid = copiedUid;
458 k.bucket = 0;
459 std::vector<concurrent_val_t> cvals(get_nprocs_conf());
460 ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data()));
461 k.uid = uid;
462 ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST));
Connor O'Briena178a732019-06-05 18:27:47 -0700463 }
464 auto times = getUidCpuFreqTimes(uid);
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700465 ASSERT_TRUE(times.has_value());
466 ASSERT_FALSE(times->empty());
Connor O'Brien57337192018-11-20 12:49:16 -0800467
Connor O'Brien26de80f2019-06-11 13:49:19 -0700468 auto concurrentTimes = getUidConcurrentTimes(0);
469 ASSERT_TRUE(concurrentTimes.has_value());
470 ASSERT_FALSE(concurrentTimes->active.empty());
471 ASSERT_FALSE(concurrentTimes->policy.empty());
472
Connor O'Brien57337192018-11-20 12:49:16 -0800473 uint64_t sum = 0;
Connor O'Brienf03b6ae2019-06-05 18:03:12 -0700474 for (size_t i = 0; i < times->size(); ++i) {
475 for (auto x : (*times)[i]) sum += x;
Connor O'Brien57337192018-11-20 12:49:16 -0800476 }
477 ASSERT_GT(sum, (uint64_t)0);
478
Connor O'Brien26de80f2019-06-11 13:49:19 -0700479 uint64_t activeSum = 0;
480 for (size_t i = 0; i < concurrentTimes->active.size(); ++i) {
481 activeSum += concurrentTimes->active[i];
482 }
483 ASSERT_GT(activeSum, (uint64_t)0);
484
485 ASSERT_TRUE(clearUidTimes(uid));
Connor O'Brien57337192018-11-20 12:49:16 -0800486
Connor O'Briena178a732019-06-05 18:27:47 -0700487 auto allTimes = getUidsCpuFreqTimes();
488 ASSERT_TRUE(allTimes.has_value());
489 ASSERT_FALSE(allTimes->empty());
490 ASSERT_EQ(allTimes->find(uid), allTimes->end());
Connor O'Brien26de80f2019-06-11 13:49:19 -0700491
492 auto allConcurrentTimes = getUidsConcurrentTimes();
493 ASSERT_TRUE(allConcurrentTimes.has_value());
494 ASSERT_FALSE(allConcurrentTimes->empty());
495 ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
Connor O'Brien57337192018-11-20 12:49:16 -0800496}
497
Connor O'Brien8f296eb2019-10-01 17:58:38 -0700498TEST(TimeInStateTest, GetCpuFreqs) {
499 auto freqs = getCpuFreqs();
500 ASSERT_TRUE(freqs.has_value());
501
502 auto times = getUidCpuFreqTimes(0);
503 ASSERT_TRUE(times.has_value());
504
505 ASSERT_EQ(freqs->size(), times->size());
506 for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size());
507}
508
Dmitri Plotnikov2677dba2020-10-17 21:06:55 -0700509uint64_t timeNanos() {
510 struct timespec spec;
511 clock_gettime(CLOCK_MONOTONIC, &spec);
512 return spec.tv_sec * 1000000000 + spec.tv_nsec;
513}
514
515// Keeps CPU busy with some number crunching
516void useCpu() {
517 long sum = 0;
518 for (int i = 0; i < 100000; i++) {
519 sum *= i;
520 }
521}
522
523sem_t pingsem, pongsem;
524
525void *testThread(void *) {
526 for (int i = 0; i < 10; i++) {
527 sem_wait(&pingsem);
528 useCpu();
529 sem_post(&pongsem);
530 }
531 return nullptr;
532}
533
534TEST(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) {
535 uint64_t startTimeNs = timeNanos();
536
537 sem_init(&pingsem, 0, 1);
538 sem_init(&pongsem, 0, 0);
539
540 pthread_t thread;
541 ASSERT_EQ(pthread_create(&thread, NULL, &testThread, NULL), 0);
542
543 // This process may have been running for some time, so when we start tracking
544 // CPU time, the very first switch may include the accumulated time.
545 // Yield the remainder of this timeslice to the newly created thread.
546 sem_wait(&pongsem);
547 sem_post(&pingsem);
548
549 pid_t tgid = getpid();
550 startTrackingProcessCpuTimes(tgid);
551
552 pid_t tid = pthread_gettid_np(thread);
553 startAggregatingTaskCpuTimes(tid, 42);
554
555 // Play ping-pong with the other thread to ensure that both threads get
556 // some CPU time.
557 for (int i = 0; i < 9; i++) {
558 sem_wait(&pongsem);
559 useCpu();
560 sem_post(&pingsem);
561 }
562
563 pthread_join(thread, NULL);
564
565 std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> optionalMap =
566 getAggregatedTaskCpuFreqTimes(tgid, {0, 42});
567 ASSERT_TRUE(optionalMap);
568
569 std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map = *optionalMap;
570 ASSERT_EQ(map.size(), 2u);
571
572 uint64_t testDurationNs = timeNanos() - startTimeNs;
573 for (auto pair : map) {
574 uint16_t aggregationKey = pair.first;
575 ASSERT_TRUE(aggregationKey == 0 || aggregationKey == 42);
576
577 std::vector<std::vector<uint64_t>> timesInState = pair.second;
578 uint64_t totalCpuTime = 0;
579 for (size_t i = 0; i < timesInState.size(); i++) {
580 for (size_t j = 0; j < timesInState[i].size(); j++) {
581 totalCpuTime += timesInState[i][j];
582 }
583 }
584 ASSERT_GT(totalCpuTime, 0ul);
585 ASSERT_LE(totalCpuTime, testDurationNs);
586 }
587}
588
Connor O'Brien57337192018-11-20 12:49:16 -0800589} // namespace bpf
590} // namespace android