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 ADB_DUMP, &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 temperatureAtomMatcher = CreateTemperatureAtomMatcher();
74 *config.add_atom_matcher() = temperatureAtomMatcher;
75 *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
76 *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
77
78 auto valueMetric = config.add_value_metric();
79 valueMetric->set_id(123456);
80 valueMetric->set_what(temperatureAtomMatcher.id());
81 *valueMetric->mutable_value_field() =
82 CreateDimensions(android::util::TEMPERATURE, {3 /* temperature degree field */});
83 *valueMetric->mutable_dimensions_in_what() =
84 CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */});
85 valueMetric->set_bucket(FIVE_MINUTES);
86 valueMetric->set_min_bucket_size_nanos(minTime);
87 valueMetric->set_use_absolute_value_on_reset(true);
88 return config;
89 }
90
MakeGaugeMetricConfig(int64_t minTime)91 StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
92 StatsdConfig config;
93 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
94
95 auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
96 *config.add_atom_matcher() = temperatureAtomMatcher;
97 *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
98 *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
99
100 auto gaugeMetric = config.add_gauge_metric();
101 gaugeMetric->set_id(123456);
102 gaugeMetric->set_what(temperatureAtomMatcher.id());
103 gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
104 *gaugeMetric->mutable_dimensions_in_what() =
105 CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */});
106 gaugeMetric->set_bucket(FIVE_MINUTES);
107 gaugeMetric->set_min_bucket_size_nanos(minTime);
108 return config;
109 }
110
TEST(PartialBucketE2eTest,TestCountMetricWithoutSplit)111 TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
112 StatsService service(nullptr);
113 SendConfig(service, MakeConfig());
114 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
115 // initialized with.
116
117 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
118 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
119
120 ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
121 // Expect no metrics since the bucket has not finished yet.
122 EXPECT_EQ(0, report.metrics_size());
123 }
124
TEST(PartialBucketE2eTest,TestCountMetricNoSplitOnNewApp)125 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
126 StatsService service(nullptr);
127 SendConfig(service, MakeConfig());
128 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
129 // initialized with.
130
131 // Force the uidmap to update at timestamp 2.
132 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
133 // This is a new installation, so there shouldn't be a split (should be same as the without
134 // split case).
135 service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
136 // Goes into the second bucket.
137 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
138
139 ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
140 EXPECT_EQ(0, report.metrics_size());
141 }
142
TEST(PartialBucketE2eTest,TestCountMetricSplitOnUpgrade)143 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
144 StatsService service(nullptr);
145 SendConfig(service, MakeConfig());
146 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
147 // initialized with.
148 service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
149
150 // Force the uidmap to update at timestamp 2.
151 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
152 service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
153 // Goes into the second bucket.
154 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
155
156 ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
157 backfillStartEndTimestamp(&report);
158 EXPECT_EQ(1, report.metrics_size());
159 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
160 has_start_bucket_elapsed_nanos());
161 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
162 has_end_bucket_elapsed_nanos());
163 EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
164 }
165
TEST(PartialBucketE2eTest,TestCountMetricSplitOnRemoval)166 TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
167 StatsService service(nullptr);
168 SendConfig(service, MakeConfig());
169 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
170 // initialized with.
171 service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
172
173 // Force the uidmap to update at timestamp 2.
174 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
175 service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
176 // Goes into the second bucket.
177 service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
178
179 ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
180 backfillStartEndTimestamp(&report);
181 EXPECT_EQ(1, report.metrics_size());
182 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
183 has_start_bucket_elapsed_nanos());
184 EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
185 has_end_bucket_elapsed_nanos());
186 EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
187 }
188
TEST(PartialBucketE2eTest,TestValueMetricWithoutMinPartialBucket)189 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
190 StatsService service(nullptr);
191 // Partial buckets don't occur when app is first installed.
192 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
193 SendConfig(service, MakeValueMetricConfig(0));
194 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
195 // initialized with.
196
197 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
198 service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
199
200 ConfigMetricsReport report =
201 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
202 EXPECT_EQ(1, report.metrics_size());
203 EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
204 }
205
TEST(PartialBucketE2eTest,TestValueMetricWithMinPartialBucket)206 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
207 StatsService service(nullptr);
208 // Partial buckets don't occur when app is first installed.
209 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
210 SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
211 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
212 // initialized with.
213
214 const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
215 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
216 service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
217
218 ConfigMetricsReport report =
219 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
220 backfillStartEndTimestamp(&report);
221 EXPECT_EQ(1, report.metrics_size());
222 EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
223 EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
224 // Can't test the start time since it will be based on the actual time when the pulling occurs.
225 EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
226 report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
227 }
228
TEST(PartialBucketE2eTest,TestGaugeMetricWithoutMinPartialBucket)229 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
230 StatsService service(nullptr);
231 // Partial buckets don't occur when app is first installed.
232 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
233 SendConfig(service, MakeGaugeMetricConfig(0));
234 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
235 // initialized with.
236
237 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
238 service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
239
240 ConfigMetricsReport report =
241 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
242 EXPECT_EQ(1, report.metrics_size());
243 EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
244 }
245
TEST(PartialBucketE2eTest,TestGaugeMetricWithMinPartialBucket)246 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
247 StatsService service(nullptr);
248 // Partial buckets don't occur when app is first installed.
249 service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
250 SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
251 int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
252 // initialized with.
253
254 const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
255 service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
256 service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
257
258 ConfigMetricsReport report =
259 GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
260 backfillStartEndTimestamp(&report);
261 EXPECT_EQ(1, report.metrics_size());
262 EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
263 // Can't test the start time since it will be based on the actual time when the pulling occurs.
264 EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
265 EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
266 report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
267 }
268
269 #else
270 GTEST_LOG_(INFO) << "This test does nothing.\n";
271 #endif
272
273 } // namespace statsd
274 } // namespace os
275 } // namespace android
276