• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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