1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gtest/gtest.h>
16
17 #include <binder/IPCThreadState.h>
18 #include "src/StatsLogProcessor.h"
19 #include "src/StatsService.h"
20 #include "src/stats_log_util.h"
21 #include "tests/statsd_test_util.h"
22
23 #include <vector>
24
25 namespace android {
26 namespace os {
27 namespace statsd {
28
29 #ifdef __ANDROID__
30
31 const string kAndroid = "android";
32 const string kApp1 = "app1.sharing.1";
33 const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
34
SendConfig(StatsService & service,const StatsdConfig & config)35 void SendConfig(StatsService& service, const StatsdConfig& config) {
36 string str;
37 config.SerializeToString(&str);
38 std::vector<uint8_t> configAsVec(str.begin(), str.end());
39 bool success;
40 service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str()));
41 }
42
GetReports(sp<StatsLogProcessor> processor,int64_t timestamp,bool include_current=false)43 ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
44 bool include_current = false) {
45 vector<uint8_t> output;
46 IPCThreadState* ipc = IPCThreadState::self();
47 ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
48 processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
49 true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
50 ConfigMetricsReportList reports;
51 reports.ParseFromArray(output.data(), output.size());
52 EXPECT_EQ(1, reports.reports_size());
53 return reports.reports(0);
54 }
55
MakeConfig()56 StatsdConfig MakeConfig() {
57 StatsdConfig config;
58 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
59
60 auto appCrashMatcher = CreateProcessCrashAtomMatcher();
61 *config.add_atom_matcher() = appCrashMatcher;
62 auto countMetric = config.add_count_metric();
63 countMetric->set_id(StringToId("AppCrashes"));
64 countMetric->set_what(appCrashMatcher.id());
65 countMetric->set_bucket(FIVE_MINUTES);
66 return config;
67 }
68
MakeValueMetricConfig(int64_t minTime)69 StatsdConfig MakeValueMetricConfig(int64_t minTime) {
70 StatsdConfig config;
71 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
72
73 auto pulledAtomMatcher =
74 CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
75 *config.add_atom_matcher() = pulledAtomMatcher;
76 *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
77 *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
78
79 auto valueMetric = config.add_value_metric();
80 valueMetric->set_id(123456);
81 valueMetric->set_what(pulledAtomMatcher.id());
82 *valueMetric->mutable_value_field() =
83 CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
84 *valueMetric->mutable_dimensions_in_what() =
85 CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
86 valueMetric->set_bucket(FIVE_MINUTES);
87 valueMetric->set_min_bucket_size_nanos(minTime);
88 valueMetric->set_use_absolute_value_on_reset(true);
89 return config;
90 }
91
MakeGaugeMetricConfig(int64_t minTime)92 StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
93 StatsdConfig config;
94 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
95
96 auto pulledAtomMatcher =
97 CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
98 *config.add_atom_matcher() = pulledAtomMatcher;
99 *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
100 *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
101
102 auto gaugeMetric = config.add_gauge_metric();
103 gaugeMetric->set_id(123456);
104 gaugeMetric->set_what(pulledAtomMatcher.id());
105 gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
106 *gaugeMetric->mutable_dimensions_in_what() =
107 CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
108 gaugeMetric->set_bucket(FIVE_MINUTES);
109 gaugeMetric->set_min_bucket_size_nanos(minTime);
110 return config;
111 }
112
TEST(PartialBucketE2eTest,TestCountMetricWithoutSplit)113 TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
114 StatsService service(nullptr, nullptr);
115 SendConfig(service, MakeConfig());
116 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
117 // initialized with.
118
119 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
120 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
121
122 ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
123 // Expect no metrics since the bucket has not finished yet.
124 EXPECT_EQ(1, report.metrics_size());
125 EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
126 }
127
TEST(PartialBucketE2eTest,TestCountMetricNoSplitOnNewApp)128 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
129 StatsService service(nullptr, nullptr);
130 SendConfig(service, MakeConfig());
131 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
132 // initialized with.
133
134 // Force the uidmap to update at timestamp 2.
135 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
136 // This is a new installation, so there shouldn't be a split (should be same as the without
137 // split case).
138 service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
139 String16(""));
140 // Goes into the second bucket.
141 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
142
143 ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
144 EXPECT_EQ(1, report.metrics_size());
145 EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
146 }
147
TEST(PartialBucketE2eTest,TestCountMetricSplitOnUpgrade)148 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
149 StatsService service(nullptr, nullptr);
150 SendConfig(service, MakeConfig());
151 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
152 // initialized with.
153 service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
154 {String16("")});
155
156 // Force the uidmap to update at timestamp 2.
157 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
158 service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
159 String16(""));
160 // Goes into the second bucket.
161 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
162
163 ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
164 backfillStartEndTimestamp(&report);
165 EXPECT_EQ(1, report.metrics_size());
166 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
167 has_start_bucket_elapsed_nanos());
168 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
169 has_end_bucket_elapsed_nanos());
170 EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
171 }
172
TEST(PartialBucketE2eTest,TestCountMetricSplitOnRemoval)173 TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
174 StatsService service(nullptr, nullptr);
175 SendConfig(service, MakeConfig());
176 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
177 // initialized with.
178 service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
179 {String16("")});
180
181 // Force the uidmap to update at timestamp 2.
182 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
183 service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
184 // Goes into the second bucket.
185 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
186
187 ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
188 backfillStartEndTimestamp(&report);
189 EXPECT_EQ(1, report.metrics_size());
190 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
191 has_start_bucket_elapsed_nanos());
192 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
193 has_end_bucket_elapsed_nanos());
194 EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
195 }
196
TEST(PartialBucketE2eTest,TestValueMetricWithoutMinPartialBucket)197 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
198 StatsService service(nullptr, nullptr);
199 // Partial buckets don't occur when app is first installed.
200 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
201 SendConfig(service, MakeValueMetricConfig(0));
202 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
203 // initialized with.
204
205 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
206 service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
207 String16("v2"), String16(""));
208
209 ConfigMetricsReport report =
210 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
211 EXPECT_EQ(1, report.metrics_size());
212 EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
213 }
214
TEST(PartialBucketE2eTest,TestValueMetricWithMinPartialBucket)215 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
216 StatsService service(nullptr, nullptr);
217 // Partial buckets don't occur when app is first installed.
218 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
219 SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
220 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
221 // initialized with.
222
223 const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
224 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
225 service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
226 String16(""));
227
228 ConfigMetricsReport report =
229 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
230 backfillStartEndTimestamp(&report);
231 EXPECT_EQ(1, report.metrics_size());
232 EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
233 EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
234 // Can't test the start time since it will be based on the actual time when the pulling occurs.
235 EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
236 report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
237 }
238
TEST(PartialBucketE2eTest,TestGaugeMetricWithoutMinPartialBucket)239 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
240 StatsService service(nullptr, nullptr);
241 // Partial buckets don't occur when app is first installed.
242 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
243 SendConfig(service, MakeGaugeMetricConfig(0));
244 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
245 // initialized with.
246
247 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
248 service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
249 String16("v2"), String16(""));
250
251 ConfigMetricsReport report =
252 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
253 EXPECT_EQ(1, report.metrics_size());
254 EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
255 }
256
TEST(PartialBucketE2eTest,TestGaugeMetricWithMinPartialBucket)257 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
258 StatsService service(nullptr, nullptr);
259 // Partial buckets don't occur when app is first installed.
260 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
261 SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
262 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
263 // initialized with.
264
265 const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
266 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
267 service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
268 String16(""));
269
270 ConfigMetricsReport report =
271 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
272 backfillStartEndTimestamp(&report);
273 EXPECT_EQ(1, report.metrics_size());
274 EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
275 // Can't test the start time since it will be based on the actual time when the pulling occurs.
276 EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
277 EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
278 report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
279 }
280
281 #else
282 GTEST_LOG_(INFO) << "This test does nothing.\n";
283 #endif
284
285 } // namespace statsd
286 } // namespace os
287 } // namespace android
288