1 /*
2 * Copyright 2020 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
17 #include "MockProcStatCollector.h"
18 #include "MockUidStatsCollector.h"
19 #include "MockWatchdogServiceHelper.h"
20 #include "PackageInfoTestUtils.h"
21 #include "PerformanceProfiler.h"
22
23 #include <WatchdogProperties.sysprop.h>
24 #include <android-base/file.h>
25 #include <gmock/gmock.h>
26 #include <utils/RefBase.h>
27
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <string>
32 #include <type_traits>
33 #include <vector>
34
35 namespace android {
36 namespace automotive {
37 namespace watchdog {
38
39 namespace {
40
41 using ::aidl::android::automotive::watchdog::internal::ResourceStats;
42 using ::android::RefBase;
43 using ::android::sp;
44 using ::android::base::ReadFdToString;
45 using ::android::base::Result;
46 using ::testing::_;
47 using ::testing::AllOf;
48 using ::testing::ElementsAreArray;
49 using ::testing::Eq;
50 using ::testing::ExplainMatchResult;
51 using ::testing::Field;
52 using ::testing::IsSubsetOf;
53 using ::testing::Matcher;
54 using ::testing::Return;
55 using ::testing::Test;
56 using ::testing::UnorderedElementsAreArray;
57 using ::testing::VariantWith;
58
59 constexpr int kTestTopNStatsPerCategory = 5;
60 constexpr int kTestTopNStatsPerSubcategory = 5;
61 constexpr int kTestMaxUserSwitchEvents = 3;
62 constexpr std::chrono::seconds kTestSystemEventDataCacheDurationSec = 60s;
63 constexpr time_t kTestNow = static_cast<time_t>(1'683'270'000);
64
65 MATCHER_P(IoStatsViewEq, expected, "") {
66 return ExplainMatchResult(AllOf(Field("bytes", &UserPackageStats::IoStatsView::bytes,
67 ElementsAreArray(expected.bytes)),
68 Field("fsync", &UserPackageStats::IoStatsView::fsync,
69 ElementsAreArray(expected.fsync))),
70 arg, result_listener);
71 }
72
73 MATCHER_P(ProcessValueEq, expected, "") {
74 return ExplainMatchResult(AllOf(Field("comm",
75 &UserPackageStats::ProcSingleStatsView::ProcessValue::
76 comm,
77 Eq(expected.comm)),
78 Field("value",
79 &UserPackageStats::ProcSingleStatsView::ProcessValue::
80 value,
81 Eq(expected.value))),
82 arg, result_listener);
83 }
84
85 MATCHER_P(ProcSingleStatsViewEq, expected, "") {
86 std::vector<Matcher<const UserPackageStats::ProcSingleStatsView::ProcessValue&>>
87 processValueMatchers;
88 processValueMatchers.reserve(expected.topNProcesses.size());
89 for (const auto& processValue : expected.topNProcesses) {
90 processValueMatchers.push_back(ProcessValueEq(processValue));
91 }
92 return ExplainMatchResult(AllOf(Field("value", &UserPackageStats::ProcSingleStatsView::value,
93 Eq(expected.value)),
94 Field("topNProcesses",
95 &UserPackageStats::ProcSingleStatsView::topNProcesses,
96 ElementsAreArray(processValueMatchers))),
97 arg, result_listener);
98 }
99
100 MATCHER_P(ProcessCpuValueEq, expected, "") {
101 return ExplainMatchResult(AllOf(Field("comm",
102 &UserPackageStats::ProcCpuStatsView::ProcessCpuValue::
103 comm,
104 Eq(expected.comm)),
105 Field("cpuTime",
106 &UserPackageStats::ProcCpuStatsView::ProcessCpuValue::
107 cpuTime,
108 Eq(expected.cpuTime)),
109 Field("cpuCycles",
110 &UserPackageStats::ProcCpuStatsView::ProcessCpuValue::
111 cpuCycles,
112 Eq(expected.cpuCycles))),
113 arg, result_listener);
114 }
115
116 MATCHER_P(ProcCpuStatsViewEq, expected, "") {
117 std::vector<Matcher<const UserPackageStats::ProcCpuStatsView::ProcessCpuValue&>>
118 processValueMatchers;
119 processValueMatchers.reserve(expected.topNProcesses.size());
120 for (const auto& processValue : expected.topNProcesses) {
121 processValueMatchers.push_back(ProcessCpuValueEq(processValue));
122 }
123 return ExplainMatchResult(AllOf(Field("cpuTime", &UserPackageStats::ProcCpuStatsView::cpuTime,
124 Eq(expected.cpuTime)),
125 Field("cpuCycles",
126 &UserPackageStats::ProcCpuStatsView::cpuCycles,
127 Eq(expected.cpuCycles)),
128 Field("topNProcesses",
129 &UserPackageStats::ProcCpuStatsView::topNProcesses,
130 ElementsAreArray(processValueMatchers))),
131 arg, result_listener);
132 }
133
134 MATCHER_P(UserPackageStatsEq, expected, "") {
135 const auto uidMatcher = Field("uid", &UserPackageStats::uid, Eq(expected.uid));
136 const auto packageNameMatcher =
137 Field("genericPackageName", &UserPackageStats::genericPackageName,
138 Eq(expected.genericPackageName));
139 return std::visit(
__anon2065062a0202(const auto& statsView) 140 [&](const auto& statsView) -> bool {
141 using T = std::decay_t<decltype(statsView)>;
142 if constexpr (std::is_same_v<T, UserPackageStats::IoStatsView>) {
143 return ExplainMatchResult(AllOf(uidMatcher, packageNameMatcher,
144 Field("statsView:IoStatsView",
145 &UserPackageStats::statsView,
146 VariantWith<
147 UserPackageStats::IoStatsView>(
148 IoStatsViewEq(statsView)))),
149 arg, result_listener);
150 } else if constexpr (std::is_same_v<T, UserPackageStats::ProcSingleStatsView>) {
151 return ExplainMatchResult(AllOf(uidMatcher, packageNameMatcher,
152 Field("statsView:ProcSingleStatsView",
153 &UserPackageStats::statsView,
154 VariantWith<UserPackageStats::
155 ProcSingleStatsView>(
156 ProcSingleStatsViewEq(
157 statsView)))),
158 arg, result_listener);
159 } else if constexpr (std::is_same_v<T, UserPackageStats::ProcCpuStatsView>) {
160 return ExplainMatchResult(AllOf(uidMatcher, packageNameMatcher,
161 Field("statsView:ProcCpuStatsView",
162 &UserPackageStats::statsView,
163 VariantWith<UserPackageStats::
164 ProcCpuStatsView>(
165 ProcCpuStatsViewEq(statsView)))),
166 arg, result_listener);
167 }
168 *result_listener << "Unexpected variant in UserPackageStats::stats";
169 return false;
170 },
171 expected.statsView);
172 }
173
174 MATCHER_P(UserPackageSummaryStatsEq, expected, "") {
__anon2065062a0302(const std::vector<UserPackageStats>& stats) 175 const auto& userPackageStatsMatchers = [&](const std::vector<UserPackageStats>& stats) {
176 std::vector<Matcher<const UserPackageStats&>> matchers;
177 for (const auto& curStats : stats) {
178 matchers.push_back(UserPackageStatsEq(curStats));
179 }
180 return ElementsAreArray(matchers);
181 };
__anon2065062a0402(const int64_t expected[][UID_STATES]) 182 const auto& totalIoStatsArrayMatcher = [&](const int64_t expected[][UID_STATES]) {
183 std::vector<Matcher<const int64_t[UID_STATES]>> matchers;
184 for (int i = 0; i < METRIC_TYPES; ++i) {
185 matchers.push_back(ElementsAreArray(expected[i], UID_STATES));
186 }
187 return ElementsAreArray(matchers);
188 };
189 return ExplainMatchResult(AllOf(Field("topNCpuTimes", &UserPackageSummaryStats::topNCpuTimes,
190 userPackageStatsMatchers(expected.topNCpuTimes)),
191 Field("topNIoReads", &UserPackageSummaryStats::topNIoReads,
192 userPackageStatsMatchers(expected.topNIoReads)),
193 Field("topNIoWrites", &UserPackageSummaryStats::topNIoWrites,
194 userPackageStatsMatchers(expected.topNIoWrites)),
195 Field("topNIoBlocked", &UserPackageSummaryStats::topNIoBlocked,
196 userPackageStatsMatchers(expected.topNIoBlocked)),
197 Field("topNMajorFaults",
198 &UserPackageSummaryStats::topNMajorFaults,
199 userPackageStatsMatchers(expected.topNMajorFaults)),
200 Field("totalIoStats", &UserPackageSummaryStats::totalIoStats,
201 totalIoStatsArrayMatcher(expected.totalIoStats)),
202 Field("taskCountByUid",
203 &UserPackageSummaryStats::taskCountByUid,
204 IsSubsetOf(expected.taskCountByUid)),
205 Field("totalCpuTimeMillis",
206 &UserPackageSummaryStats::totalCpuTimeMillis,
207 Eq(expected.totalCpuTimeMillis)),
208 Field("totalCpuCycles",
209 &UserPackageSummaryStats::totalCpuCycles,
210 Eq(expected.totalCpuCycles)),
211 Field("totalMajorFaults",
212 &UserPackageSummaryStats::totalMajorFaults,
213 Eq(expected.totalMajorFaults)),
214 Field("majorFaultsPercentChange",
215 &UserPackageSummaryStats::majorFaultsPercentChange,
216 Eq(expected.majorFaultsPercentChange))),
217 arg, result_listener);
218 }
219
220 MATCHER_P(SystemSummaryStatsEq, expected, "") {
221 return ExplainMatchResult(AllOf(Field("cpuIoWaitTimeMillis",
222 &SystemSummaryStats::cpuIoWaitTimeMillis,
223 Eq(expected.cpuIoWaitTimeMillis)),
224 Field("cpuIdleTimeMillis",
225 &SystemSummaryStats::cpuIdleTimeMillis,
226 Eq(expected.cpuIdleTimeMillis)),
227 Field("totalCpuTimeMillis",
228 &SystemSummaryStats::totalCpuTimeMillis,
229 Eq(expected.totalCpuTimeMillis)),
230 Field("totalCpuCycles", &SystemSummaryStats::totalCpuCycles,
231 Eq(expected.totalCpuCycles)),
232 Field("contextSwitchesCount",
233 &SystemSummaryStats::contextSwitchesCount,
234 Eq(expected.contextSwitchesCount)),
235 Field("ioBlockedProcessCount",
236 &SystemSummaryStats::ioBlockedProcessCount,
237 Eq(expected.ioBlockedProcessCount)),
238 Field("totalProcessCount",
239 &SystemSummaryStats::totalProcessCount,
240 Eq(expected.totalProcessCount))),
241 arg, result_listener);
242 }
243
244 MATCHER_P(PerfStatsRecordEq, expected, "") {
245 return ExplainMatchResult(AllOf(Field(&PerfStatsRecord::systemSummaryStats,
246 SystemSummaryStatsEq(expected.systemSummaryStats)),
247 Field(&PerfStatsRecord::userPackageSummaryStats,
248 UserPackageSummaryStatsEq(
249 expected.userPackageSummaryStats))),
250 arg, result_listener);
251 }
252
constructPerfStatsRecordMatchers(const std::vector<PerfStatsRecord> & records)253 const std::vector<Matcher<const PerfStatsRecord&>> constructPerfStatsRecordMatchers(
254 const std::vector<PerfStatsRecord>& records) {
255 std::vector<Matcher<const PerfStatsRecord&>> matchers;
256 for (const auto& record : records) {
257 matchers.push_back(PerfStatsRecordEq(record));
258 }
259 return matchers;
260 }
261
262 MATCHER_P(CollectionInfoEq, expected, "") {
263 return ExplainMatchResult(AllOf(Field("maxCacheSize", &CollectionInfo::maxCacheSize,
264 Eq(expected.maxCacheSize)),
265 Field("records", &CollectionInfo::records,
266 ElementsAreArray(constructPerfStatsRecordMatchers(
267 expected.records)))),
268 arg, result_listener);
269 }
270
271 MATCHER_P(UserSwitchCollectionInfoEq, expected, "") {
272 return ExplainMatchResult(AllOf(Field("from", &UserSwitchCollectionInfo::from,
273 Eq(expected.from)),
274 Field("to", &UserSwitchCollectionInfo::to, Eq(expected.to)),
275 Field("maxCacheSize", &UserSwitchCollectionInfo::maxCacheSize,
276 Eq(expected.maxCacheSize)),
277 Field("records", &UserSwitchCollectionInfo::records,
278 ElementsAreArray(constructPerfStatsRecordMatchers(
279 expected.records)))),
280 arg, result_listener);
281 }
282
283 MATCHER_P(UserSwitchCollectionsEq, expected, "") {
284 std::vector<Matcher<const UserSwitchCollectionInfo&>> userSwitchCollectionMatchers;
285 for (const auto& curCollection : expected) {
286 userSwitchCollectionMatchers.push_back(UserSwitchCollectionInfoEq(curCollection));
287 }
288 return ExplainMatchResult(ElementsAreArray(userSwitchCollectionMatchers), arg, result_listener);
289 }
290
countOccurrences(std::string str,std::string subStr)291 int countOccurrences(std::string str, std::string subStr) {
292 size_t pos = 0;
293 int occurrences = 0;
294 while ((pos = str.find(subStr, pos)) != std::string::npos) {
295 ++occurrences;
296 pos += subStr.length();
297 }
298 return occurrences;
299 }
300
sampleUidStats(int multiplier=1)301 std::tuple<std::vector<UidStats>, UserPackageSummaryStats> sampleUidStats(int multiplier = 1) {
302 /* The number of returned sample stats are less that the top N stats per category/sub-category.
303 * The top N stats per category/sub-category is set to % during test setup. Thus, the default
304 * testing behavior is # reported stats < top N stats.
305 */
306 const auto int64Multiplier = [&](int64_t bytes) -> int64_t {
307 return static_cast<int64_t>(bytes * multiplier);
308 };
309 const auto uint64Multiplier = [&](uint64_t count) -> uint64_t {
310 return static_cast<uint64_t>(count * multiplier);
311 };
312 std::vector<UidStats>
313 uidStats{{.packageInfo = constructPackageInfo("mount", 1009),
314 .cpuTimeMillis = int64Multiplier(50),
315 .ioStats = {/*fgRdBytes=*/0,
316 /*bgRdBytes=*/int64Multiplier(14'000),
317 /*fgWrBytes=*/0,
318 /*bgWrBytes=*/int64Multiplier(16'000),
319 /*fgFsync=*/0, /*bgFsync=*/int64Multiplier(100)},
320 .procStats = {.cpuTimeMillis = int64Multiplier(50),
321 .cpuCycles = 4000,
322 .totalMajorFaults = uint64Multiplier(11'000),
323 .totalTasksCount = 1,
324 .ioBlockedTasksCount = 1,
325 .processStatsByPid =
326 {{/*pid=*/100,
327 {/*comm=*/"disk I/O", /*startTime=*/234,
328 /*cpuTimeMillis=*/int64Multiplier(50),
329 /*totalCpuCycles=*/4000,
330 /*totalMajorFaults=*/uint64Multiplier(11'000),
331 /*totalTasksCount=*/1,
332 /*ioBlockedTasksCount=*/1,
333 /*cpuCyclesByTid=*/{{100, 4000}}}}}}},
334 {.packageInfo =
335 constructPackageInfo("com.google.android.car.kitchensink", 1002001),
336 .cpuTimeMillis = int64Multiplier(60),
337 .ioStats = {/*fgRdBytes=*/0,
338 /*bgRdBytes=*/int64Multiplier(3'400),
339 /*fgWrBytes=*/0,
340 /*bgWrBytes=*/int64Multiplier(6'700),
341 /*fgFsync=*/0,
342 /*bgFsync=*/int64Multiplier(200)},
343 .procStats = {.cpuTimeMillis = int64Multiplier(50),
344 .cpuCycles = 10'000,
345 .totalMajorFaults = uint64Multiplier(22'445),
346 .totalTasksCount = 5,
347 .ioBlockedTasksCount = 3,
348 .processStatsByPid =
349 {{/*pid=*/1000,
350 {/*comm=*/"KitchenSinkApp", /*startTime=*/467,
351 /*cpuTimeMillis=*/int64Multiplier(25),
352 /*totalCpuCycles=*/4000,
353 /*totalMajorFaults=*/uint64Multiplier(12'345),
354 /*totalTasksCount=*/2,
355 /*ioBlockedTasksCount=*/1,
356 /*cpuCyclesByTid=*/{{1000, 4000}}}},
357 {/*pid=*/1001,
358 {/*comm=*/"CTS", /*startTime=*/789,
359 /*cpuTimeMillis=*/int64Multiplier(25),
360 /*totalCpuCycles=*/5000,
361 /*totalMajorFaults=*/uint64Multiplier(10'100),
362 /*totalTasksCount=*/3,
363 /*ioBlockedTasksCount=*/2,
364 /*cpuCyclesByTid=*/{{1001, 3000}, {1002, 2000}}}}}}},
365 {.packageInfo = constructPackageInfo("", 1012345),
366 .cpuTimeMillis = int64Multiplier(100),
367 .ioStats = {/*fgRdBytes=*/int64Multiplier(1'000),
368 /*bgRdBytes=*/int64Multiplier(4'200),
369 /*fgWrBytes=*/int64Multiplier(300),
370 /*bgWrBytes=*/int64Multiplier(5'600),
371 /*fgFsync=*/int64Multiplier(600),
372 /*bgFsync=*/int64Multiplier(300)},
373 .procStats = {.cpuTimeMillis = int64Multiplier(100),
374 .cpuCycles = 50'000,
375 .totalMajorFaults = uint64Multiplier(50'900),
376 .totalTasksCount = 4,
377 .ioBlockedTasksCount = 2,
378 .processStatsByPid =
379 {{/*pid=*/2345,
380 {/*comm=*/"MapsApp", /*startTime=*/6789,
381 /*cpuTimeMillis=*/int64Multiplier(100),
382 /*totalCpuCycles=*/50'000,
383 /*totalMajorFaults=*/uint64Multiplier(50'900),
384 /*totalTasksCount=*/4,
385 /*ioBlockedTasksCount=*/2,
386 /*cpuCyclesByTid=*/{{2345, 50'000}}}}}}},
387 {.packageInfo = constructPackageInfo("com.google.radio", 1015678),
388 .cpuTimeMillis = 0,
389 .ioStats = {/*fgRdBytes=*/0,
390 /*bgRdBytes=*/0,
391 /*fgWrBytes=*/0,
392 /*bgWrBytes=*/0,
393 /*fgFsync=*/0, /*bgFsync=*/0},
394 .procStats = {.cpuTimeMillis = 0,
395 .cpuCycles = 0,
396 .totalMajorFaults = 0,
397 .totalTasksCount = 4,
398 .ioBlockedTasksCount = 0,
399 .processStatsByPid = {
400 {/*pid=*/2345,
401 {/*comm=*/"RadioApp", /*startTime=*/19789,
402 /*cpuTimeMillis=*/0,
403 /*totalCpuCycles=*/0,
404 /*totalMajorFaults=*/0,
405 /*totalTasksCount=*/4,
406 /*ioBlockedTasksCount=*/0,
407 /*cpuCyclesByTid=*/{}}}}}}};
408
409 UserPackageSummaryStats userPackageSummaryStats{
410 .topNCpuTimes = {{1012345, "1012345",
411 UserPackageStats::ProcCpuStatsView{uint64Multiplier(100),
412 50'000,
413 {{"MapsApp", uint64Multiplier(100),
414 50'000}}}},
415 {1002001, "com.google.android.car.kitchensink",
416 UserPackageStats::ProcCpuStatsView{uint64Multiplier(60),
417 10'000,
418 {{"CTS", uint64Multiplier(25),
419 5000},
420 {"KitchenSinkApp",
421 uint64Multiplier(25), 4000}}}},
422 {1009, "mount",
423 UserPackageStats::ProcCpuStatsView{uint64Multiplier(50),
424 4000,
425 {{"disk I/O", uint64Multiplier(50),
426 4000}}}}},
427 .topNIoReads = {{1009, "mount",
428 UserPackageStats::IoStatsView{{0, int64Multiplier(14'000)},
429 {0, int64Multiplier(100)}}},
430 {1012345, "1012345",
431 UserPackageStats::IoStatsView{{int64Multiplier(1'000),
432 int64Multiplier(4'200)},
433 {int64Multiplier(600),
434 int64Multiplier(300)}}},
435 {1002001, "com.google.android.car.kitchensink",
436 UserPackageStats::IoStatsView{{0, int64Multiplier(3'400)},
437 {0, int64Multiplier(200)}}}},
438 .topNIoWrites =
439 {{1009, "mount",
440 UserPackageStats::IoStatsView{{0, int64Multiplier(16'000)},
441 {0, int64Multiplier(100)}}},
442 {1002001, "com.google.android.car.kitchensink",
443 UserPackageStats::IoStatsView{{0, int64Multiplier(6'700)},
444 {0, int64Multiplier(200)}}},
445 {1012345, "1012345",
446 UserPackageStats::IoStatsView{{int64Multiplier(300), int64Multiplier(5'600)},
447 {int64Multiplier(600), int64Multiplier(300)}}}},
448 .topNIoBlocked =
449 {{1002001, "com.google.android.car.kitchensink",
450 UserPackageStats::ProcSingleStatsView{3,
451 {{"CTS", 2}, {"KitchenSinkApp", 1}}}},
452 {1012345, "1012345",
453 UserPackageStats::ProcSingleStatsView{2, {{"MapsApp", 2}}}},
454 {1009, "mount", UserPackageStats::ProcSingleStatsView{1, {{"disk I/O", 1}}}}},
455 .topNMajorFaults =
456 {{1012345, "1012345",
457 UserPackageStats::ProcSingleStatsView{uint64Multiplier(50'900),
458 {{"MapsApp",
459 uint64Multiplier(50'900)}}}},
460 {1002001, "com.google.android.car.kitchensink",
461 UserPackageStats::ProcSingleStatsView{uint64Multiplier(22'445),
462 {{"KitchenSinkApp",
463 uint64Multiplier(12'345)},
464 {"CTS", uint64Multiplier(10'100)}}}},
465 {1009, "mount",
466 UserPackageStats::ProcSingleStatsView{uint64Multiplier(11'000),
467 {{"disk I/O",
468 uint64Multiplier(11'000)}}}}},
469 .totalIoStats = {{int64Multiplier(1'000), int64Multiplier(21'600)},
470 {int64Multiplier(300), int64Multiplier(28'300)},
471 {int64Multiplier(600), int64Multiplier(600)}},
472 .taskCountByUid = {{1009, 1}, {1002001, 5}, {1012345, 4}},
473 .totalCpuTimeMillis = int64Multiplier(48'376),
474 .totalCpuCycles = 64'000,
475 .totalMajorFaults = uint64Multiplier(84'345),
476 .majorFaultsPercentChange = 0.0,
477 };
478 return std::make_tuple(uidStats, userPackageSummaryStats);
479 }
480
sampleProcStat(int multiplier=1)481 std::tuple<ProcStatInfo, SystemSummaryStats> sampleProcStat(int multiplier = 1) {
482 const auto int64Multiplier = [&](int64_t bytes) -> int64_t {
483 return static_cast<int64_t>(bytes * multiplier);
484 };
485 const auto uint64Multiplier = [&](uint64_t bytes) -> uint64_t {
486 return static_cast<uint64_t>(bytes * multiplier);
487 };
488 const auto uint32Multiplier = [&](uint32_t bytes) -> uint32_t {
489 return static_cast<uint32_t>(bytes * multiplier);
490 };
491 ProcStatInfo procStatInfo{/*stats=*/{int64Multiplier(2'900), int64Multiplier(7'900),
492 int64Multiplier(4'900), int64Multiplier(8'900),
493 /*ioWaitTimeMillis=*/int64Multiplier(5'900),
494 int64Multiplier(6'966), int64Multiplier(7'980), 0, 0,
495 int64Multiplier(2'930)},
496 /*ctxtSwitches=*/uint64Multiplier(500),
497 /*runnableCnt=*/uint32Multiplier(100),
498 /*ioBlockedCnt=*/uint32Multiplier(57)};
499 SystemSummaryStats systemSummaryStats{/*cpuIoWaitTimeMillis=*/int64Multiplier(5'900),
500 /*cpuIdleTimeMillis=*/int64Multiplier(8'900),
501 /*totalCpuTimeMillis=*/int64Multiplier(48'376),
502 /*totalCpuCycles=*/64'000,
503 /*contextSwitchesCount=*/uint64Multiplier(500),
504 /*ioBlockedProcessCount=*/uint32Multiplier(57),
505 /*totalProcessCount=*/uint32Multiplier(157)};
506 return std::make_tuple(procStatInfo, systemSummaryStats);
507 }
508
509 } // namespace
510
511 namespace internal {
512
513 class PerformanceProfilerPeer final : public RefBase {
514 public:
PerformanceProfilerPeer(sp<PerformanceProfiler> collector)515 explicit PerformanceProfilerPeer(sp<PerformanceProfiler> collector) : mCollector(collector) {}
516
517 PerformanceProfilerPeer() = delete;
~PerformanceProfilerPeer()518 ~PerformanceProfilerPeer() {
519 mCollector->terminate();
520 mCollector.clear();
521 }
522
init()523 Result<void> init() { return mCollector->init(); }
524
setTopNStatsPerCategory(int value)525 void setTopNStatsPerCategory(int value) { mCollector->mTopNStatsPerCategory = value; }
526
setTopNStatsPerSubcategory(int value)527 void setTopNStatsPerSubcategory(int value) { mCollector->mTopNStatsPerSubcategory = value; }
528
setMaxUserSwitchEvents(int value)529 void setMaxUserSwitchEvents(int value) { mCollector->mMaxUserSwitchEvents = value; }
530
setSystemEventDataCacheDuration(std::chrono::seconds value)531 void setSystemEventDataCacheDuration(std::chrono::seconds value) {
532 mCollector->mSystemEventDataCacheDurationSec = value;
533 }
534
setSendResourceUsageStatsEnabled(bool enable)535 void setSendResourceUsageStatsEnabled(bool enable) {
536 mCollector->mDoSendResourceUsageStats = enable;
537 }
538
getBoottimeCollectionInfo()539 const CollectionInfo& getBoottimeCollectionInfo() {
540 Mutex::Autolock lock(mCollector->mMutex);
541 return mCollector->mBoottimeCollection;
542 }
543
getPeriodicCollectionInfo()544 const CollectionInfo& getPeriodicCollectionInfo() {
545 Mutex::Autolock lock(mCollector->mMutex);
546 return mCollector->mPeriodicCollection;
547 }
548
getUserSwitchCollectionInfos()549 const std::vector<UserSwitchCollectionInfo>& getUserSwitchCollectionInfos() {
550 Mutex::Autolock lock(mCollector->mMutex);
551 return mCollector->mUserSwitchCollections;
552 }
553
getWakeUpCollectionInfo()554 const CollectionInfo& getWakeUpCollectionInfo() {
555 Mutex::Autolock lock(mCollector->mMutex);
556 return mCollector->mWakeUpCollection;
557 }
558
getCustomCollectionInfo()559 const CollectionInfo& getCustomCollectionInfo() {
560 Mutex::Autolock lock(mCollector->mMutex);
561 return mCollector->mCustomCollection;
562 }
563
564 private:
565 sp<PerformanceProfiler> mCollector;
566 };
567
568 } // namespace internal
569
570 class PerformanceProfilerTest : public Test {
571 protected:
SetUp()572 void SetUp() override {
573 mMockUidStatsCollector = sp<MockUidStatsCollector>::make();
574 mMockProcStatCollector = sp<MockProcStatCollector>::make();
575 mCollector = sp<PerformanceProfiler>::make();
576 mCollectorPeer = sp<internal::PerformanceProfilerPeer>::make(mCollector);
577 ASSERT_RESULT_OK(mCollectorPeer->init());
578 mCollectorPeer->setTopNStatsPerCategory(kTestTopNStatsPerCategory);
579 mCollectorPeer->setTopNStatsPerSubcategory(kTestTopNStatsPerSubcategory);
580 mCollectorPeer->setMaxUserSwitchEvents(kTestMaxUserSwitchEvents);
581 mCollectorPeer->setSystemEventDataCacheDuration(kTestSystemEventDataCacheDurationSec);
582 mCollectorPeer->setSendResourceUsageStatsEnabled(true);
583 }
584
TearDown()585 void TearDown() override {
586 mMockUidStatsCollector.clear();
587 mMockProcStatCollector.clear();
588 mCollector.clear();
589 mCollectorPeer.clear();
590 }
591
checkDumpContents(int wantedEmptyCollectionInstances)592 void checkDumpContents(int wantedEmptyCollectionInstances) {
593 TemporaryFile dump;
594 ASSERT_RESULT_OK(mCollector->onDump(dump.fd));
595
596 checkDumpFd(wantedEmptyCollectionInstances, dump.fd);
597 }
598
checkCustomDumpContents()599 void checkCustomDumpContents() {
600 TemporaryFile dump;
601 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(dump.fd));
602
603 checkDumpFd(/*wantedEmptyCollectionInstances=*/0, dump.fd);
604 }
605
606 private:
checkDumpFd(int wantedEmptyCollectionInstances,int fd)607 void checkDumpFd(int wantedEmptyCollectionInstances, int fd) {
608 lseek(fd, 0, SEEK_SET);
609 std::string dumpContents;
610 ASSERT_TRUE(ReadFdToString(fd, &dumpContents));
611 ASSERT_FALSE(dumpContents.empty());
612
613 ASSERT_EQ(countOccurrences(dumpContents, kEmptyCollectionMessage),
614 wantedEmptyCollectionInstances)
615 << "Dump contents: " << dumpContents;
616 }
617
618 protected:
619 sp<MockUidStatsCollector> mMockUidStatsCollector;
620 sp<MockProcStatCollector> mMockProcStatCollector;
621 sp<PerformanceProfiler> mCollector;
622 sp<internal::PerformanceProfilerPeer> mCollectorPeer;
623 };
624
TEST_F(PerformanceProfilerTest,TestOnBoottimeCollection)625 TEST_F(PerformanceProfilerTest, TestOnBoottimeCollection) {
626 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
627 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
628
629 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
630 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
631
632 ResourceStats actualResourceStats = {};
633 ASSERT_RESULT_OK(mCollector->onBoottimeCollection(kTestNow, mMockUidStatsCollector,
634 mMockProcStatCollector,
635 &actualResourceStats));
636
637 const auto actual = mCollectorPeer->getBoottimeCollectionInfo();
638
639 const CollectionInfo expected{
640 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
641 .records = {{
642 .systemSummaryStats = systemSummaryStats,
643 .userPackageSummaryStats = userPackageSummaryStats,
644 }},
645 };
646
647 EXPECT_THAT(actual, CollectionInfoEq(expected))
648 << "Boottime collection info doesn't match.\nExpected:\n"
649 << expected.toString() << "\nActual:\n"
650 << actual.toString();
651
652 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/3))
653 << "Periodic, wake-up and user-switch collections shouldn't be reported";
654 }
655
TEST_F(PerformanceProfilerTest,TestOnWakeUpCollection)656 TEST_F(PerformanceProfilerTest, TestOnWakeUpCollection) {
657 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
658 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
659
660 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
661 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
662
663 ASSERT_RESULT_OK(mCollector->onWakeUpCollection(kTestNow, mMockUidStatsCollector,
664 mMockProcStatCollector));
665
666 const auto actual = mCollectorPeer->getWakeUpCollectionInfo();
667
668 const CollectionInfo expected{
669 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
670 .records = {{
671 .systemSummaryStats = systemSummaryStats,
672 .userPackageSummaryStats = userPackageSummaryStats,
673 }},
674 };
675
676 EXPECT_THAT(actual, CollectionInfoEq(expected))
677 << "Wake-up collection info doesn't match.\nExpected:\n"
678 << expected.toString() << "\nActual:\n"
679 << actual.toString();
680
681 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/3))
682 << "Boot-time, periodic, and user-switch collections shouldn't be reported";
683 }
684
TEST_F(PerformanceProfilerTest,TestOnSystemStartup)685 TEST_F(PerformanceProfilerTest, TestOnSystemStartup) {
686 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
687 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
688
689 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
690 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
691
692 ResourceStats resourceStats = {};
693 ASSERT_RESULT_OK(mCollector->onBoottimeCollection(kTestNow, mMockUidStatsCollector,
694 mMockProcStatCollector, &resourceStats));
695 ASSERT_RESULT_OK(mCollector->onWakeUpCollection(kTestNow, mMockUidStatsCollector,
696 mMockProcStatCollector));
697
698 auto actualBoottimeCollection = mCollectorPeer->getBoottimeCollectionInfo();
699 auto actualWakeUpCollection = mCollectorPeer->getWakeUpCollectionInfo();
700
701 EXPECT_THAT(actualBoottimeCollection.records.size(), 1)
702 << "Boot-time collection records is empty.";
703 EXPECT_THAT(actualWakeUpCollection.records.size(), 1) << "Wake-up collection records is empty.";
704
705 ASSERT_RESULT_OK(mCollector->onSystemStartup());
706
707 actualBoottimeCollection = mCollectorPeer->getBoottimeCollectionInfo();
708 actualWakeUpCollection = mCollectorPeer->getWakeUpCollectionInfo();
709
710 EXPECT_THAT(actualBoottimeCollection.records.size(), 0)
711 << "Boot-time collection records is not empty.";
712 EXPECT_THAT(actualWakeUpCollection.records.size(), 0)
713 << "Wake-up collection records is not empty.";
714 }
715
TEST_F(PerformanceProfilerTest,TestOnUserSwitchCollection)716 TEST_F(PerformanceProfilerTest, TestOnUserSwitchCollection) {
717 auto [uidStats, userPackageSummaryStats] = sampleUidStats();
718 auto [procStatInfo, systemSummaryStats] = sampleProcStat();
719
720 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
721 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
722
723 ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow, 100, 101, mMockUidStatsCollector,
724 mMockProcStatCollector));
725
726 const auto& actualInfos = mCollectorPeer->getUserSwitchCollectionInfos();
727 const auto& actual = actualInfos[0];
728
729 UserSwitchCollectionInfo expected{
730 {
731 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
732 .records = {{
733 .systemSummaryStats = systemSummaryStats,
734 .userPackageSummaryStats = userPackageSummaryStats,
735 }},
736 },
737 .from = 100,
738 .to = 101,
739 };
740
741 EXPECT_THAT(actualInfos.size(), 1);
742
743 EXPECT_THAT(actual, UserSwitchCollectionInfoEq(expected))
744 << "User switch collection info doesn't match.\nExpected:\n"
745 << expected.toString() << "\nActual:\n"
746 << actual.toString();
747
748 // Continuation of the previous user switch collection
749 std::vector<UidStats> nextUidStats = {
750 {.packageInfo = constructPackageInfo("mount", 1009),
751 .cpuTimeMillis = 0, // No TopNCpuTimes will be registered
752 .ioStats = {/*fgRdBytes=*/0,
753 /*bgRdBytes=*/5'000,
754 /*fgWrBytes=*/0,
755 /*bgWrBytes=*/3'000,
756 /*fgFsync=*/0, /*bgFsync=*/50},
757 .procStats = {.cpuTimeMillis = 50,
758 .cpuCycles = 3'500,
759 .totalMajorFaults = 6'000,
760 .totalTasksCount = 1,
761 .ioBlockedTasksCount = 2,
762 .processStatsByPid = {{/*pid=*/100,
763 {/*comm=*/"disk I/O", /*startTime=*/234,
764 /*cpuTimeMillis=*/50,
765 /*totalCpuCycle=*/3'500,
766 /*totalMajorFaults=*/6'000,
767 /*totalTasksCount=*/1,
768 /*ioBlockedTasksCount=*/2,
769 /*cpuCyclesByTid=*/{{100, 3'500}}}}}}}};
770
771 UserPackageSummaryStats nextUserPackageSummaryStats = {
772 .topNIoReads = {{1009, "mount", UserPackageStats::IoStatsView{{0, 5'000}, {0, 50}}}},
773 .topNIoWrites = {{1009, "mount", UserPackageStats::IoStatsView{{0, 3'000}, {0, 50}}}},
774 .topNIoBlocked = {{1009, "mount",
775 UserPackageStats::ProcSingleStatsView{2, {{"disk I/O", 2}}}}},
776 .topNMajorFaults = {{1009, "mount",
777 UserPackageStats::ProcSingleStatsView{6'000,
778 {{"disk I/O", 6'000}}}}},
779 .totalIoStats = {{0, 5'000}, {0, 3'000}, {0, 50}},
780 .taskCountByUid = {{1009, 1}},
781 .totalCpuTimeMillis = 48'376,
782 .totalCpuCycles = 3'500,
783 .totalMajorFaults = 6'000,
784 .majorFaultsPercentChange = (6'000.0 - 84'345.0) / 84'345.0 * 100.0,
785 };
786
787 ProcStatInfo nextProcStatInfo = procStatInfo;
788 SystemSummaryStats nextSystemSummaryStats = systemSummaryStats;
789
790 nextProcStatInfo.contextSwitchesCount = 300;
791 nextSystemSummaryStats.totalCpuCycles = 3'500;
792 nextSystemSummaryStats.contextSwitchesCount = 300;
793
794 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(nextUidStats));
795 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(nextProcStatInfo));
796
797 ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow + 2, 100, 101,
798 mMockUidStatsCollector,
799 mMockProcStatCollector));
800
801 auto& continuationActualInfos = mCollectorPeer->getUserSwitchCollectionInfos();
802 auto& continuationActual = continuationActualInfos[0];
803
804 expected = {
805 {
806 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
807 .records = {{.systemSummaryStats = systemSummaryStats,
808 .userPackageSummaryStats = userPackageSummaryStats},
809 {.systemSummaryStats = nextSystemSummaryStats,
810 .userPackageSummaryStats = nextUserPackageSummaryStats}},
811 },
812 .from = 100,
813 .to = 101,
814 };
815
816 EXPECT_THAT(continuationActualInfos.size(), 1);
817
818 EXPECT_THAT(continuationActual, UserSwitchCollectionInfoEq(expected))
819 << "User switch collection info after continuation doesn't match.\nExpected:\n"
820 << expected.toString() << "\nActual:\n"
821 << actual.toString();
822
823 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/3))
824 << "Boot-time, wake-up and periodic collections shouldn't be reported";
825 }
826
TEST_F(PerformanceProfilerTest,TestUserSwitchCollectionsMaxCacheSize)827 TEST_F(PerformanceProfilerTest, TestUserSwitchCollectionsMaxCacheSize) {
828 auto [uidStats, userPackageSummaryStats] = sampleUidStats();
829 auto [procStatInfo, systemSummaryStats] = sampleProcStat();
830
831 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
832 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
833
834 std::vector<UserSwitchCollectionInfo> expectedEvents;
835 for (userid_t userId = 100; userId < 100 + kTestMaxUserSwitchEvents; ++userId) {
836 expectedEvents.push_back({
837 {
838 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
839 .records = {{
840 .systemSummaryStats = systemSummaryStats,
841 .userPackageSummaryStats = userPackageSummaryStats,
842 }},
843 },
844 .from = userId,
845 .to = userId + 1,
846 });
847 }
848
849 for (userid_t userId = 100; userId < 100 + kTestMaxUserSwitchEvents; ++userId) {
850 ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow, userId, userId + 1,
851 mMockUidStatsCollector,
852 mMockProcStatCollector));
853 }
854
855 const auto& actual = mCollectorPeer->getUserSwitchCollectionInfos();
856
857 EXPECT_THAT(actual.size(), kTestMaxUserSwitchEvents);
858
859 EXPECT_THAT(actual, UserSwitchCollectionsEq(expectedEvents))
860 << "User switch collection infos don't match.";
861
862 // Add new user switch event with max cache size. The oldest user switch event should be dropped
863 // and the new one added to the cache.
864 userid_t userId = 100 + kTestMaxUserSwitchEvents;
865
866 expectedEvents.push_back({
867 {
868 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
869 .records = {{
870 .systemSummaryStats = systemSummaryStats,
871 .userPackageSummaryStats = userPackageSummaryStats,
872 }},
873 },
874 .from = userId,
875 .to = userId + 1,
876 });
877 expectedEvents.erase(expectedEvents.begin());
878
879 ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(kTestNow, userId, userId + 1,
880 mMockUidStatsCollector,
881 mMockProcStatCollector));
882
883 const auto& actualInfos = mCollectorPeer->getUserSwitchCollectionInfos();
884
885 EXPECT_THAT(actualInfos.size(), kTestMaxUserSwitchEvents);
886
887 EXPECT_THAT(actualInfos, UserSwitchCollectionsEq(expectedEvents))
888 << "User switch collection infos don't match.";
889 }
890
TEST_F(PerformanceProfilerTest,TestOnPeriodicCollection)891 TEST_F(PerformanceProfilerTest, TestOnPeriodicCollection) {
892 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
893 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
894
895 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
896 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
897
898 ResourceStats actualResourceStats = {};
899 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(kTestNow, SystemState::NORMAL_MODE,
900 mMockUidStatsCollector,
901 mMockProcStatCollector,
902 &actualResourceStats));
903
904 const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
905
906 const CollectionInfo expected{
907 .maxCacheSize = static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
908 kDefaultPeriodicCollectionBufferSize)),
909 .records = {{
910 .systemSummaryStats = systemSummaryStats,
911 .userPackageSummaryStats = userPackageSummaryStats,
912 }},
913 };
914
915 EXPECT_THAT(actual, CollectionInfoEq(expected))
916 << "Periodic collection info doesn't match.\nExpected:\n"
917 << expected.toString() << "\nActual:\n"
918 << actual.toString();
919
920 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/3))
921 << "Boot-time, wake-up and user-switch collections shouldn't be reported";
922 }
923
TEST_F(PerformanceProfilerTest,TestOnCustomCollectionWithoutPackageFilter)924 TEST_F(PerformanceProfilerTest, TestOnCustomCollectionWithoutPackageFilter) {
925 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
926 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
927
928 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
929 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
930
931 ResourceStats actualResourceStats = {};
932 ASSERT_RESULT_OK(mCollector->onCustomCollection(kTestNow, SystemState::NORMAL_MODE, {},
933 mMockUidStatsCollector, mMockProcStatCollector,
934 &actualResourceStats));
935
936 const auto actual = mCollectorPeer->getCustomCollectionInfo();
937
938 CollectionInfo expected{
939 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
940 .records = {{
941 .systemSummaryStats = systemSummaryStats,
942 .userPackageSummaryStats = userPackageSummaryStats,
943 }},
944 };
945
946 EXPECT_THAT(actual, CollectionInfoEq(expected))
947 << "Custom collection info doesn't match.\nExpected:\n"
948 << expected.toString() << "\nActual:\n"
949 << actual.toString();
950
951 ASSERT_NO_FATAL_FAILURE(checkCustomDumpContents()) << "Custom collection should be reported";
952
953 TemporaryFile customDump;
954 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(customDump.fd));
955
956 // Should clear the cache.
957 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(-1));
958
959 expected.records.clear();
960 const CollectionInfo& emptyCollectionInfo = mCollectorPeer->getCustomCollectionInfo();
961 EXPECT_THAT(emptyCollectionInfo, CollectionInfoEq(expected))
962 << "Custom collection should be cleared.";
963 }
964
TEST_F(PerformanceProfilerTest,TestOnCustomCollectionWithPackageFilter)965 TEST_F(PerformanceProfilerTest, TestOnCustomCollectionWithPackageFilter) {
966 // Filter by package name should ignore this limit with package filter.
967 mCollectorPeer->setTopNStatsPerCategory(1);
968
969 const auto [uidStats, _] = sampleUidStats();
970 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
971
972 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
973 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
974
975 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
976 ResourceStats resourceStats = {};
977 ASSERT_RESULT_OK(mCollector->onCustomCollection(now, SystemState::NORMAL_MODE,
978 {"mount", "com.google.android.car.kitchensink"},
979 mMockUidStatsCollector, mMockProcStatCollector,
980 &resourceStats));
981
982 const auto actual = mCollectorPeer->getCustomCollectionInfo();
983
984 UserPackageSummaryStats userPackageSummaryStats{
985 .topNCpuTimes =
986 {{1009, "mount",
987 UserPackageStats::ProcCpuStatsView{50, 4'000, {{"disk I/O", 50, 4'000}}}},
988 {1002001, "com.google.android.car.kitchensink",
989 UserPackageStats::ProcCpuStatsView{60,
990 10'000,
991 {{"CTS", 25, 5'000},
992 {"KitchenSinkApp", 25, 4'000}}}}},
993 .topNIoReads = {{1009, "mount", UserPackageStats::IoStatsView{{0, 14'000}, {0, 100}}},
994 {1002001, "com.google.android.car.kitchensink",
995 UserPackageStats::IoStatsView{{0, 3'400}, {0, 200}}}},
996 .topNIoWrites = {{1009, "mount", UserPackageStats::IoStatsView{{0, 16'000}, {0, 100}}},
997 {1002001, "com.google.android.car.kitchensink",
998 UserPackageStats::IoStatsView{{0, 6'700}, {0, 200}}}},
999 .topNIoBlocked =
1000 {{1009, "mount", UserPackageStats::ProcSingleStatsView{1, {{"disk I/O", 1}}}},
1001 {1002001, "com.google.android.car.kitchensink",
1002 UserPackageStats::ProcSingleStatsView{3,
1003 {{"CTS", 2}, {"KitchenSinkApp", 1}}}}},
1004 .topNMajorFaults = {{1009, "mount",
1005 UserPackageStats::ProcSingleStatsView{11'000,
1006 {{"disk I/O", 11'000}}}},
1007 {1002001, "com.google.android.car.kitchensink",
1008 UserPackageStats::ProcSingleStatsView{22'445,
1009 {{"KitchenSinkApp", 12'345},
1010 {"CTS", 10'100}}}}},
1011 .totalIoStats = {{1000, 21'600}, {300, 28'300}, {600, 600}},
1012 .taskCountByUid = {{1009, 1}, {1002001, 5}},
1013 .totalCpuTimeMillis = 48'376,
1014 .totalCpuCycles = 64'000,
1015 .totalMajorFaults = 84'345,
1016 .majorFaultsPercentChange = 0.0,
1017 };
1018
1019 CollectionInfo expected{
1020 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
1021 .records = {{
1022 .systemSummaryStats = systemSummaryStats,
1023 .userPackageSummaryStats = userPackageSummaryStats,
1024 }},
1025 };
1026
1027 EXPECT_THAT(actual, CollectionInfoEq(expected))
1028 << "Custom collection info doesn't match.\nExpected:\n"
1029 << expected.toString() << "\nActual:\n"
1030 << actual.toString();
1031
1032 ASSERT_NO_FATAL_FAILURE(checkCustomDumpContents()) << "Custom collection should be reported";
1033
1034 TemporaryFile customDump;
1035 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(customDump.fd));
1036
1037 // Should clear the cache.
1038 ASSERT_RESULT_OK(mCollector->onCustomCollectionDump(-1));
1039
1040 expected.records.clear();
1041 const CollectionInfo& emptyCollectionInfo = mCollectorPeer->getCustomCollectionInfo();
1042 EXPECT_THAT(emptyCollectionInfo, CollectionInfoEq(expected))
1043 << "Custom collection should be cleared.";
1044 }
1045
TEST_F(PerformanceProfilerTest,TestOnPeriodicCollectionWithTrimmingStatsAfterTopN)1046 TEST_F(PerformanceProfilerTest, TestOnPeriodicCollectionWithTrimmingStatsAfterTopN) {
1047 mCollectorPeer->setTopNStatsPerCategory(1);
1048 mCollectorPeer->setTopNStatsPerSubcategory(1);
1049
1050 const auto [uidStats, _] = sampleUidStats();
1051 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
1052
1053 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(uidStats));
1054 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(procStatInfo));
1055
1056 time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
1057 ResourceStats resourceStats = {};
1058 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(now, SystemState::NORMAL_MODE,
1059 mMockUidStatsCollector,
1060 mMockProcStatCollector, &resourceStats));
1061
1062 const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
1063
1064 UserPackageSummaryStats userPackageSummaryStats{
1065 .topNCpuTimes =
1066 {{1012345, "1012345",
1067 UserPackageStats::ProcCpuStatsView{100, 50'000, {{"MapsApp", 100, 50'000}}}}},
1068 .topNIoReads = {{1009, "mount", UserPackageStats::IoStatsView{{0, 14'000}, {0, 100}}}},
1069 .topNIoWrites = {{1009, "mount", UserPackageStats::IoStatsView{{0, 16'000}, {0, 100}}}},
1070 .topNIoBlocked = {{1002001, "com.google.android.car.kitchensink",
1071 UserPackageStats::ProcSingleStatsView{3, {{"CTS", 2}}}}},
1072 .topNMajorFaults = {{1012345, "1012345",
1073 UserPackageStats::ProcSingleStatsView{50'900,
1074 {{"MapsApp", 50'900}}}}},
1075 .totalIoStats = {{1000, 21'600}, {300, 28'300}, {600, 600}},
1076 .taskCountByUid = {{1009, 1}, {1002001, 5}, {1012345, 4}},
1077 .totalCpuTimeMillis = 48'376,
1078 .totalCpuCycles = 64'000,
1079 .totalMajorFaults = 84'345,
1080 .majorFaultsPercentChange = 0.0,
1081 };
1082
1083 const CollectionInfo expected{
1084 .maxCacheSize = static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
1085 kDefaultPeriodicCollectionBufferSize)),
1086 .records = {{
1087 .systemSummaryStats = systemSummaryStats,
1088 .userPackageSummaryStats = userPackageSummaryStats,
1089 }},
1090 };
1091
1092 EXPECT_THAT(actual, CollectionInfoEq(expected))
1093 << "Periodic collection info doesn't match.\nExpected:\n"
1094 << expected.toString() << "\nActual:\n"
1095 << actual.toString();
1096
1097 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/3))
1098 << "Boot-time, wake-up and user-switch collections shouldn't be reported";
1099 }
1100
TEST_F(PerformanceProfilerTest,TestConsecutiveOnPeriodicCollection)1101 TEST_F(PerformanceProfilerTest, TestConsecutiveOnPeriodicCollection) {
1102 const auto [firstUidStats, firstUserPackageSummaryStats] = sampleUidStats();
1103 const auto [firstProcStatInfo, firstSystemSummaryStats] = sampleProcStat();
1104
1105 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(firstUidStats));
1106 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(firstProcStatInfo));
1107
1108 ResourceStats actualResourceStats = {};
1109 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(kTestNow, SystemState::NORMAL_MODE,
1110 mMockUidStatsCollector,
1111 mMockProcStatCollector,
1112 &actualResourceStats));
1113
1114 auto [secondUidStats, secondUserPackageSummaryStats] = sampleUidStats(/*multiplier=*/2);
1115 const auto [secondProcStatInfo, secondSystemSummaryStats] = sampleProcStat(/*multiplier=*/2);
1116
1117 secondUserPackageSummaryStats.majorFaultsPercentChange =
1118 (static_cast<double>(secondUserPackageSummaryStats.totalMajorFaults -
1119 firstUserPackageSummaryStats.totalMajorFaults) /
1120 static_cast<double>(firstUserPackageSummaryStats.totalMajorFaults)) *
1121 100.0;
1122
1123 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillOnce(Return(secondUidStats));
1124 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillOnce(Return(secondProcStatInfo));
1125
1126 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(kTestNow, SystemState::NORMAL_MODE,
1127 mMockUidStatsCollector,
1128 mMockProcStatCollector,
1129 &actualResourceStats));
1130
1131 const auto actual = mCollectorPeer->getPeriodicCollectionInfo();
1132
1133 const CollectionInfo expected{
1134 .maxCacheSize = static_cast<size_t>(sysprop::periodicCollectionBufferSize().value_or(
1135 kDefaultPeriodicCollectionBufferSize)),
1136 .records = {{.systemSummaryStats = firstSystemSummaryStats,
1137 .userPackageSummaryStats = firstUserPackageSummaryStats},
1138 {.systemSummaryStats = secondSystemSummaryStats,
1139 .userPackageSummaryStats = secondUserPackageSummaryStats}},
1140 };
1141
1142 EXPECT_THAT(actual, CollectionInfoEq(expected))
1143 << "Periodic collection info doesn't match.\nExpected:\n"
1144 << expected.toString() << "\nActual:\n"
1145 << actual.toString();
1146
1147 ASSERT_NO_FATAL_FAILURE(checkDumpContents(/*wantedEmptyCollectionInstances=*/3))
1148 << "Boot-time, wake-up and user-switch collection shouldn't be reported";
1149 }
1150
TEST_F(PerformanceProfilerTest,TestBoottimeCollectionCacheEviction)1151 TEST_F(PerformanceProfilerTest, TestBoottimeCollectionCacheEviction) {
1152 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
1153 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
1154
1155 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
1156 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
1157
1158 ResourceStats resourceStats = {};
1159 ASSERT_RESULT_OK(mCollector->onBoottimeCollection(kTestNow, mMockUidStatsCollector,
1160 mMockProcStatCollector, &resourceStats));
1161
1162 auto actual = mCollectorPeer->getBoottimeCollectionInfo();
1163
1164 EXPECT_THAT(actual.records.size(), 1) << "Boot-time collection info doesn't have size 1";
1165
1166 // Call |onPeriodicCollection| 1 hour past the last boot-time collection event.
1167 ASSERT_RESULT_OK(
1168 mCollector->onPeriodicCollection(kTestNow +
1169 kTestSystemEventDataCacheDurationSec.count(),
1170 SystemState::NORMAL_MODE, mMockUidStatsCollector,
1171 mMockProcStatCollector, &resourceStats));
1172
1173 actual = mCollectorPeer->getBoottimeCollectionInfo();
1174
1175 EXPECT_THAT(actual.records.empty(), true) << "Boot-time collection info records are not empty";
1176 }
1177
TEST_F(PerformanceProfilerTest,TestWakeUpCollectionCacheEviction)1178 TEST_F(PerformanceProfilerTest, TestWakeUpCollectionCacheEviction) {
1179 const auto [uidStats, userPackageSummaryStats] = sampleUidStats();
1180 const auto [procStatInfo, systemSummaryStats] = sampleProcStat();
1181
1182 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
1183 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
1184
1185 ASSERT_RESULT_OK(mCollector->onWakeUpCollection(kTestNow, mMockUidStatsCollector,
1186 mMockProcStatCollector));
1187
1188 auto actual = mCollectorPeer->getWakeUpCollectionInfo();
1189
1190 EXPECT_THAT(actual.records.size(), 1) << "Wake-up collection info doesn't have size 1";
1191
1192 ResourceStats resourceStats = {};
1193
1194 // Call |onPeriodicCollection| 1 hour past the last wake-up collection event.
1195 ASSERT_RESULT_OK(
1196 mCollector->onPeriodicCollection(kTestNow +
1197 kTestSystemEventDataCacheDurationSec.count(),
1198 SystemState::NORMAL_MODE, mMockUidStatsCollector,
1199 mMockProcStatCollector, &resourceStats));
1200
1201 actual = mCollectorPeer->getWakeUpCollectionInfo();
1202
1203 EXPECT_THAT(actual.records.empty(), true) << "Wake-up collection info records are not empty";
1204 }
1205
TEST_F(PerformanceProfilerTest,TestUserSwitchCollectionCacheEviction)1206 TEST_F(PerformanceProfilerTest, TestUserSwitchCollectionCacheEviction) {
1207 auto [uidStats, userPackageSummaryStats] = sampleUidStats();
1208 auto [procStatInfo, systemSummaryStats] = sampleProcStat();
1209
1210 EXPECT_CALL(*mMockUidStatsCollector, deltaStats()).WillRepeatedly(Return(uidStats));
1211 EXPECT_CALL(*mMockProcStatCollector, deltaStats()).WillRepeatedly(Return(procStatInfo));
1212
1213 time_t updatedNow = kTestNow;
1214
1215 for (userid_t userId = 100; userId < 100 + kTestMaxUserSwitchEvents; ++userId) {
1216 ASSERT_RESULT_OK(mCollector->onUserSwitchCollection(updatedNow, userId, userId + 1,
1217 mMockUidStatsCollector,
1218 mMockProcStatCollector));
1219 updatedNow += kTestSystemEventDataCacheDurationSec.count();
1220 }
1221
1222 const auto& actual = mCollectorPeer->getUserSwitchCollectionInfos();
1223
1224 EXPECT_THAT(actual.size(), kTestMaxUserSwitchEvents);
1225
1226 updatedNow = kTestNow + kTestSystemEventDataCacheDurationSec.count();
1227 ResourceStats resourceStats = {};
1228 for (int i = 1; i <= kTestMaxUserSwitchEvents; ++i) {
1229 ASSERT_RESULT_OK(mCollector->onPeriodicCollection(updatedNow, SystemState::NORMAL_MODE,
1230 mMockUidStatsCollector,
1231 mMockProcStatCollector, &resourceStats));
1232
1233 const auto& actual = mCollectorPeer->getUserSwitchCollectionInfos();
1234
1235 EXPECT_THAT(actual.size(), kTestMaxUserSwitchEvents - i)
1236 << "User-switch collection size is incorrect";
1237
1238 updatedNow += kTestSystemEventDataCacheDurationSec.count();
1239 }
1240 }
1241
1242 } // namespace watchdog
1243 } // namespace automotive
1244 } // namespace android
1245