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 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 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 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 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 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 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 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 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 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 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 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 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 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