David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 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 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 17 | #include <binder/IPCThreadState.h> |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 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 | |
Jeff Sharkey | 6b64925 | 2018-04-16 09:50:22 -0600 | [diff] [blame] | 31 | const string kAndroid = "android"; |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 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; |
Jeff Sharkey | 6b64925 | 2018-04-16 09:50:22 -0600 | [diff] [blame] | 40 | service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str())); |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 41 | } |
| 42 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 43 | ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp, |
| 44 | bool include_current = false) { |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 45 | vector<uint8_t> output; |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 46 | IPCThreadState* ipc = IPCThreadState::self(); |
| 47 | ConfigKey configKey(ipc->getCallingUid(), kConfigKey); |
| 48 | processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/, |
| 49 | &output); |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 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 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 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 | return config; |
| 88 | } |
| 89 | |
| 90 | StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { |
| 91 | StatsdConfig config; |
| 92 | config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. |
| 93 | |
| 94 | auto temperatureAtomMatcher = CreateTemperatureAtomMatcher(); |
| 95 | *config.add_atom_matcher() = temperatureAtomMatcher; |
| 96 | *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); |
| 97 | *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); |
| 98 | |
| 99 | auto gaugeMetric = config.add_gauge_metric(); |
| 100 | gaugeMetric->set_id(123456); |
| 101 | gaugeMetric->set_what(temperatureAtomMatcher.id()); |
| 102 | gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true); |
| 103 | *gaugeMetric->mutable_dimensions_in_what() = |
| 104 | CreateDimensions(android::util::TEMPERATURE, {2 /* sensor name field */}); |
| 105 | gaugeMetric->set_bucket(FIVE_MINUTES); |
| 106 | gaugeMetric->set_min_bucket_size_nanos(minTime); |
| 107 | return config; |
| 108 | } |
| 109 | |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 110 | TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { |
| 111 | StatsService service(nullptr); |
| 112 | SendConfig(service, MakeConfig()); |
| 113 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 114 | // initialized with. |
| 115 | |
| 116 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); |
| 117 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get()); |
| 118 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 119 | ConfigMetricsReport report = GetReports(service.mProcessor, start + 3); |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 120 | // Expect no metrics since the bucket has not finished yet. |
| 121 | EXPECT_EQ(0, report.metrics_size()); |
| 122 | } |
| 123 | |
| 124 | TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { |
| 125 | StatsService service(nullptr); |
| 126 | SendConfig(service, MakeConfig()); |
| 127 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 128 | // initialized with. |
| 129 | |
| 130 | // Force the uidmap to update at timestamp 2. |
| 131 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); |
| 132 | // This is a new installation, so there shouldn't be a split (should be same as the without |
| 133 | // split case). |
| 134 | service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2); |
| 135 | // Goes into the second bucket. |
| 136 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); |
| 137 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 138 | ConfigMetricsReport report = GetReports(service.mProcessor, start + 4); |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 139 | EXPECT_EQ(0, report.metrics_size()); |
| 140 | } |
| 141 | |
| 142 | TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { |
| 143 | StatsService service(nullptr); |
| 144 | SendConfig(service, MakeConfig()); |
| 145 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 146 | // initialized with. |
| 147 | service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())}); |
| 148 | |
| 149 | // Force the uidmap to update at timestamp 2. |
| 150 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); |
| 151 | service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2); |
| 152 | // Goes into the second bucket. |
| 153 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); |
| 154 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 155 | ConfigMetricsReport report = GetReports(service.mProcessor, start + 4); |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 156 | EXPECT_EQ(1, report.metrics_size()); |
| 157 | EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); |
| 158 | } |
| 159 | |
| 160 | TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { |
| 161 | StatsService service(nullptr); |
| 162 | SendConfig(service, MakeConfig()); |
| 163 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 164 | // initialized with. |
| 165 | service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())}); |
| 166 | |
| 167 | // Force the uidmap to update at timestamp 2. |
| 168 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); |
| 169 | service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1); |
| 170 | // Goes into the second bucket. |
| 171 | service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get()); |
| 172 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 173 | ConfigMetricsReport report = GetReports(service.mProcessor, start + 4); |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 174 | EXPECT_EQ(1, report.metrics_size()); |
| 175 | EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); |
| 176 | } |
| 177 | |
David Chen | 81245fd | 2018-04-12 14:33:37 -0700 | [diff] [blame^] | 178 | TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { |
| 179 | StatsService service(nullptr); |
| 180 | // Partial buckets don't occur when app is first installed. |
| 181 | service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); |
| 182 | SendConfig(service, MakeValueMetricConfig(0)); |
| 183 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 184 | // initialized with. |
| 185 | |
| 186 | service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); |
| 187 | service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2); |
| 188 | |
| 189 | ConfigMetricsReport report = |
| 190 | GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true); |
| 191 | EXPECT_EQ(1, report.metrics_size()); |
| 192 | EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size()); |
| 193 | } |
| 194 | |
| 195 | TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { |
| 196 | StatsService service(nullptr); |
| 197 | // Partial buckets don't occur when app is first installed. |
| 198 | service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); |
| 199 | SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); |
| 200 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 201 | // initialized with. |
| 202 | |
| 203 | const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; |
| 204 | service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); |
| 205 | service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2); |
| 206 | |
| 207 | ConfigMetricsReport report = |
| 208 | GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); |
| 209 | EXPECT_EQ(1, report.metrics_size()); |
| 210 | EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size()); |
| 211 | // Can't test the start time since it will be based on the actual time when the pulling occurs. |
| 212 | EXPECT_EQ(endSkipped, report.metrics(0).value_metrics().skipped(0).end_elapsed_nanos()); |
| 213 | } |
| 214 | |
| 215 | TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { |
| 216 | StatsService service(nullptr); |
| 217 | // Partial buckets don't occur when app is first installed. |
| 218 | service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); |
| 219 | SendConfig(service, MakeGaugeMetricConfig(0)); |
| 220 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 221 | // initialized with. |
| 222 | |
| 223 | service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); |
| 224 | service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2); |
| 225 | |
| 226 | ConfigMetricsReport report = |
| 227 | GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true); |
| 228 | EXPECT_EQ(1, report.metrics_size()); |
| 229 | EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); |
| 230 | } |
| 231 | |
| 232 | TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { |
| 233 | StatsService service(nullptr); |
| 234 | // Partial buckets don't occur when app is first installed. |
| 235 | service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); |
| 236 | SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); |
| 237 | const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are |
| 238 | // initialized with. |
| 239 | |
| 240 | const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; |
| 241 | service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); |
| 242 | service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2); |
| 243 | |
| 244 | ConfigMetricsReport report = |
| 245 | GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); |
| 246 | EXPECT_EQ(1, report.metrics_size()); |
| 247 | EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); |
| 248 | // Can't test the start time since it will be based on the actual time when the pulling occurs. |
| 249 | EXPECT_EQ(endSkipped, report.metrics(0).gauge_metrics().skipped(0).end_elapsed_nanos()); |
| 250 | } |
| 251 | |
David Chen | bd12527 | 2018-04-04 19:02:50 -0700 | [diff] [blame] | 252 | #else |
| 253 | GTEST_LOG_(INFO) << "This test does nothing.\n"; |
| 254 | #endif |
| 255 | |
| 256 | } // namespace statsd |
| 257 | } // namespace os |
Jeff Sharkey | 6b64925 | 2018-04-16 09:50:22 -0600 | [diff] [blame] | 258 | } // namespace android |