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