1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #define DEBUG false // STOPSHIP if true 17 #include "Log.h" 18 #include "MetricsManager.h" 19 #include "statslog.h" 20 21 #include "CountMetricProducer.h" 22 #include "condition/CombinationConditionTracker.h" 23 #include "condition/SimpleConditionTracker.h" 24 #include "guardrail/StatsdStats.h" 25 #include "matchers/CombinationLogMatchingTracker.h" 26 #include "matchers/SimpleLogMatchingTracker.h" 27 #include "metrics_manager_util.h" 28 #include "stats_util.h" 29 #include "stats_log_util.h" 30 31 #include <log/logprint.h> 32 #include <private/android_filesystem_config.h> 33 #include <utils/SystemClock.h> 34 35 using android::util::FIELD_COUNT_REPEATED; 36 using android::util::FIELD_TYPE_INT32; 37 using android::util::FIELD_TYPE_INT64; 38 using android::util::FIELD_TYPE_MESSAGE; 39 using android::util::FIELD_TYPE_STRING; 40 using android::util::ProtoOutputStream; 41 42 using std::make_unique; 43 using std::set; 44 using std::string; 45 using std::unordered_map; 46 using std::vector; 47 48 namespace android { 49 namespace os { 50 namespace statsd { 51 52 const int FIELD_ID_METRICS = 1; 53 const int FIELD_ID_ANNOTATIONS = 7; 54 const int FIELD_ID_ANNOTATIONS_INT64 = 1; 55 const int FIELD_ID_ANNOTATIONS_INT32 = 2; 56 57 // for ActiveConfig 58 const int FIELD_ID_ACTIVE_CONFIG_ID = 1; 59 const int FIELD_ID_ACTIVE_CONFIG_UID = 2; 60 const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3; 61 62 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, 63 const int64_t timeBaseNs, const int64_t currentTimeNs, 64 const sp<UidMap>& uidMap, 65 const sp<StatsPullerManager>& pullerManager, 66 const sp<AlarmMonitor>& anomalyAlarmMonitor, 67 const sp<AlarmMonitor>& periodicAlarmMonitor) 68 : mConfigKey(key), 69 mUidMap(uidMap), 70 mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1), 71 mTtlEndNs(-1), 72 mLastReportTimeNs(currentTimeNs), 73 mLastReportWallClockNs(getWallClockNs()), 74 mShouldPersistHistory(config.persist_locally()) { 75 // Init the ttl end timestamp. 76 refreshTtl(timeBaseNs); 77 78 mConfigValid = initStatsdConfig( 79 key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 80 timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers, 81 mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, 82 mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, 83 mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, 84 mMetricIndexesWithActivation, mNoReportMetricIds); 85 86 mHashStringsInReport = config.hash_strings_in_metric_report(); 87 mVersionStringsInReport = config.version_strings_in_metric_report(); 88 mInstallerInReport = config.installer_in_metric_report(); 89 90 if (config.allowed_log_source_size() == 0) { 91 mConfigValid = false; 92 ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at " 93 "least AID_SYSTEM and AID_STATSD to the allowed_log_source field."); 94 } else { 95 for (const auto& source : config.allowed_log_source()) { 96 auto it = UidMap::sAidToUidMapping.find(source); 97 if (it != UidMap::sAidToUidMapping.end()) { 98 mAllowedUid.push_back(it->second); 99 } else { 100 mAllowedPkg.push_back(source); 101 } 102 } 103 104 if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) { 105 ALOGE("Too many log sources. This is likely to be an error in the config."); 106 mConfigValid = false; 107 } else { 108 initLogSourceWhiteList(); 109 } 110 } 111 112 // Store the sub-configs used. 113 for (const auto& annotation : config.annotation()) { 114 mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32()); 115 } 116 117 // Guardrail. Reject the config if it's too big. 118 if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || 119 mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || 120 mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) { 121 ALOGE("This config is too big! Reject!"); 122 mConfigValid = false; 123 } 124 if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { 125 ALOGE("This config has too many alerts! Reject!"); 126 mConfigValid = false; 127 } 128 129 mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) || 130 (mAllMetricProducers.size() == 0); 131 bool isActive = mIsAlwaysActive; 132 for (int metric : mMetricIndexesWithActivation) { 133 isActive |= mAllMetricProducers[metric]->isActive(); 134 } 135 mIsActive = isActive; 136 VLOG("mIsActive is initialized to %d", mIsActive) 137 138 // no matter whether this config is valid, log it in the stats. 139 StatsdStats::getInstance().noteConfigReceived( 140 key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), 141 mAllAnomalyTrackers.size(), mAnnotations, mConfigValid); 142 // Check active 143 for (const auto& metric : mAllMetricProducers) { 144 if (metric->isActive()) { 145 mIsActive = true; 146 break; 147 } 148 } 149 } 150 151 MetricsManager::~MetricsManager() { 152 VLOG("~MetricsManager()"); 153 } 154 155 void MetricsManager::initLogSourceWhiteList() { 156 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); 157 mAllowedLogSources.clear(); 158 mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end()); 159 160 for (const auto& pkg : mAllowedPkg) { 161 auto uids = mUidMap->getAppUid(pkg); 162 mAllowedLogSources.insert(uids.begin(), uids.end()); 163 } 164 if (DEBUG) { 165 for (const auto& uid : mAllowedLogSources) { 166 VLOG("Allowed uid %d", uid); 167 } 168 } 169 } 170 171 bool MetricsManager::isConfigValid() const { 172 return mConfigValid; 173 } 174 175 void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, 176 const int64_t version) { 177 // check if we care this package 178 if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { 179 return; 180 } 181 // We will re-initialize the whole list because we don't want to keep the multi mapping of 182 // UID<->pkg inside MetricsManager to reduce the memory usage. 183 initLogSourceWhiteList(); 184 } 185 186 void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, 187 const int uid) { 188 // check if we care this package 189 if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { 190 return; 191 } 192 // We will re-initialize the whole list because we don't want to keep the multi mapping of 193 // UID<->pkg inside MetricsManager to reduce the memory usage. 194 initLogSourceWhiteList(); 195 } 196 197 void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) { 198 if (mAllowedPkg.size() == 0) { 199 return; 200 } 201 initLogSourceWhiteList(); 202 } 203 204 void MetricsManager::dumpStates(FILE* out, bool verbose) { 205 fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str()); 206 { 207 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); 208 for (const auto& source : mAllowedLogSources) { 209 fprintf(out, "%d ", source); 210 } 211 } 212 fprintf(out, "\n"); 213 for (const auto& producer : mAllMetricProducers) { 214 producer->dumpStates(out, verbose); 215 } 216 } 217 218 void MetricsManager::dropData(const int64_t dropTimeNs) { 219 for (const auto& producer : mAllMetricProducers) { 220 producer->dropData(dropTimeNs); 221 } 222 } 223 224 void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, 225 const bool include_current_partial_bucket, 226 const bool erase_data, 227 const DumpLatency dumpLatency, 228 std::set<string> *str_set, 229 ProtoOutputStream* protoOutput) { 230 VLOG("=========================Metric Reports Start=========================="); 231 // one StatsLogReport per MetricProduer 232 for (const auto& producer : mAllMetricProducers) { 233 if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { 234 uint64_t token = protoOutput->start( 235 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); 236 if (mHashStringsInReport) { 237 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, 238 dumpLatency, str_set, protoOutput); 239 } else { 240 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data, 241 dumpLatency, nullptr, protoOutput); 242 } 243 protoOutput->end(token); 244 } else { 245 producer->clearPastBuckets(dumpTimeStampNs); 246 } 247 } 248 for (const auto& annotation : mAnnotations) { 249 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | 250 FIELD_ID_ANNOTATIONS); 251 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64, 252 (long long)annotation.first); 253 protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second); 254 protoOutput->end(token); 255 } 256 257 mLastReportTimeNs = dumpTimeStampNs; 258 mLastReportWallClockNs = getWallClockNs(); 259 VLOG("=========================Metric Reports End=========================="); 260 } 261 262 263 bool MetricsManager::checkLogCredentials(const LogEvent& event) { 264 if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) != 265 android::util::AtomsInfo::kWhitelistedAtoms.end()) 266 { 267 return true; 268 } 269 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); 270 if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { 271 VLOG("log source %d not on the whitelist", event.GetUid()); 272 return false; 273 } 274 return true; 275 } 276 277 bool MetricsManager::eventSanityCheck(const LogEvent& event) { 278 if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { 279 // Check that app breadcrumb reported fields are valid. 280 status_t err = NO_ERROR; 281 282 // Uid is 3rd from last field and must match the caller's uid, 283 // unless that caller is statsd itself (statsd is allowed to spoof uids). 284 long appHookUid = event.GetLong(event.size()-2, &err); 285 if (err != NO_ERROR ) { 286 VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid"); 287 return false; 288 } 289 int32_t loggerUid = event.GetUid(); 290 if (loggerUid != appHookUid && loggerUid != AID_STATSD) { 291 VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d", 292 appHookUid, loggerUid); 293 return false; 294 } 295 296 // The state must be from 0,3. This part of code must be manually updated. 297 long appHookState = event.GetLong(event.size(), &err); 298 if (err != NO_ERROR ) { 299 VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field"); 300 return false; 301 } else if (appHookState < 0 || appHookState > 3) { 302 VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); 303 return false; 304 } 305 } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { 306 // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. 307 // Check that the davey duration is reasonable. Max length check is for privacy. 308 status_t err = NO_ERROR; 309 310 // Uid is the first field provided. 311 long jankUid = event.GetLong(1, &err); 312 if (err != NO_ERROR ) { 313 VLOG("Davey occurred had error when parsing the uid"); 314 return false; 315 } 316 int32_t loggerUid = event.GetUid(); 317 if (loggerUid != jankUid && loggerUid != AID_STATSD) { 318 VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid, 319 loggerUid); 320 return false; 321 } 322 323 long duration = event.GetLong(event.size(), &err); 324 if (err != NO_ERROR ) { 325 VLOG("Davey occurred had error when parsing the duration"); 326 return false; 327 } else if (duration > 100000) { 328 VLOG("Davey duration is unreasonably long: %ld", duration); 329 return false; 330 } 331 } 332 333 return true; 334 } 335 336 // Consume the stats log if it's interesting to this metric. 337 void MetricsManager::onLogEvent(const LogEvent& event) { 338 if (!mConfigValid) { 339 return; 340 } 341 342 if (!checkLogCredentials(event)) { 343 return; 344 } 345 346 if (!eventSanityCheck(event)) { 347 return; 348 } 349 350 int tagId = event.GetTagId(); 351 int64_t eventTimeNs = event.GetElapsedTimestampNs(); 352 353 bool isActive = mIsAlwaysActive; 354 355 // Set of metrics that are still active after flushing. 356 unordered_set<int> activeMetricsIndices; 357 358 // Update state of all metrics w/ activation conditions as of eventTimeNs. 359 for (int metricIndex : mMetricIndexesWithActivation) { 360 const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex]; 361 metric->flushIfExpire(eventTimeNs); 362 if (metric->isActive()) { 363 // If this metric w/ activation condition is still active after 364 // flushing, remember it. 365 activeMetricsIndices.insert(metricIndex); 366 } 367 } 368 369 mIsActive = isActive || !activeMetricsIndices.empty(); 370 371 if (mTagIds.find(tagId) == mTagIds.end()) { 372 // Not interesting... 373 return; 374 } 375 376 vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed); 377 378 // Evaluate all atom matchers. 379 for (auto& matcher : mAllAtomMatchers) { 380 matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); 381 } 382 383 // Set of metrics that received an activation cancellation. 384 unordered_set<int> metricIndicesWithCanceledActivations; 385 386 // Determine which metric activations received a cancellation and cancel them. 387 for (const auto& it : mDeactivationAtomTrackerToMetricMap) { 388 if (matcherCache[it.first] == MatchingState::kMatched) { 389 for (int metricIndex : it.second) { 390 mAllMetricProducers[metricIndex]->cancelEventActivation(it.first); 391 metricIndicesWithCanceledActivations.insert(metricIndex); 392 } 393 } 394 } 395 396 // Determine whether any metrics are no longer active after cancelling metric activations. 397 for (const int metricIndex : metricIndicesWithCanceledActivations) { 398 const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex]; 399 metric->flushIfExpire(eventTimeNs); 400 if (!metric->isActive()) { 401 activeMetricsIndices.erase(metricIndex); 402 } 403 } 404 405 isActive |= !activeMetricsIndices.empty(); 406 407 408 // Determine which metric activations should be turned on and turn them on 409 for (const auto& it : mActivationAtomTrackerToMetricMap) { 410 if (matcherCache[it.first] == MatchingState::kMatched) { 411 for (int metricIndex : it.second) { 412 mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs); 413 isActive |= mAllMetricProducers[metricIndex]->isActive(); 414 } 415 } 416 } 417 418 mIsActive = isActive; 419 420 // A bitmap to see which ConditionTracker needs to be re-evaluated. 421 vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); 422 423 for (const auto& pair : mTrackerToConditionMap) { 424 if (matcherCache[pair.first] == MatchingState::kMatched) { 425 const auto& conditionList = pair.second; 426 for (const int conditionIndex : conditionList) { 427 conditionToBeEvaluated[conditionIndex] = true; 428 } 429 } 430 } 431 432 vector<ConditionState> conditionCache(mAllConditionTrackers.size(), 433 ConditionState::kNotEvaluated); 434 // A bitmap to track if a condition has changed value. 435 vector<bool> changedCache(mAllConditionTrackers.size(), false); 436 for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { 437 if (conditionToBeEvaluated[i] == false) { 438 continue; 439 } 440 sp<ConditionTracker>& condition = mAllConditionTrackers[i]; 441 condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache, 442 changedCache); 443 } 444 445 for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { 446 if (changedCache[i] == false) { 447 continue; 448 } 449 auto pair = mConditionToMetricMap.find(i); 450 if (pair != mConditionToMetricMap.end()) { 451 auto& metricList = pair->second; 452 for (auto metricIndex : metricList) { 453 // Metric cares about non sliced condition, and it's changed. 454 // Push the new condition to it directly. 455 if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { 456 mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], 457 eventTimeNs); 458 // Metric cares about sliced conditions, and it may have changed. Send 459 // notification, and the metric can query the sliced conditions that are 460 // interesting to it. 461 } else { 462 mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], 463 eventTimeNs); 464 } 465 } 466 } 467 } 468 469 // For matched AtomMatchers, tell relevant metrics that a matched event has come. 470 for (size_t i = 0; i < mAllAtomMatchers.size(); i++) { 471 if (matcherCache[i] == MatchingState::kMatched) { 472 StatsdStats::getInstance().noteMatcherMatched(mConfigKey, 473 mAllAtomMatchers[i]->getId()); 474 auto pair = mTrackerToMetricMap.find(i); 475 if (pair != mTrackerToMetricMap.end()) { 476 auto& metricList = pair->second; 477 for (const int metricIndex : metricList) { 478 // pushed metrics are never scheduled pulls 479 mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event); 480 } 481 } 482 } 483 } 484 } 485 486 void MetricsManager::onAnomalyAlarmFired( 487 const int64_t& timestampNs, 488 unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { 489 for (const auto& itr : mAllAnomalyTrackers) { 490 itr->informAlarmsFired(timestampNs, alarmSet); 491 } 492 } 493 494 void MetricsManager::onPeriodicAlarmFired( 495 const int64_t& timestampNs, 496 unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { 497 for (const auto& itr : mAllPeriodicAlarmTrackers) { 498 itr->informAlarmsFired(timestampNs, alarmSet); 499 } 500 } 501 502 // Returns the total byte size of all metrics managed by a single config source. 503 size_t MetricsManager::byteSize() { 504 size_t totalSize = 0; 505 for (const auto& metricProducer : mAllMetricProducers) { 506 totalSize += metricProducer->byteSize(); 507 } 508 return totalSize; 509 } 510 511 void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) { 512 if (config.metric_size() == 0) { 513 ALOGW("No active metric for config %s", mConfigKey.ToString().c_str()); 514 return; 515 } 516 517 for (int i = 0; i < config.metric_size(); i++) { 518 const auto& activeMetric = config.metric(i); 519 for (int metricIndex : mMetricIndexesWithActivation) { 520 const auto& metric = mAllMetricProducers[metricIndex]; 521 if (metric->getMetricId() == activeMetric.id()) { 522 VLOG("Setting active metric: %lld", (long long)metric->getMetricId()); 523 metric->loadActiveMetric(activeMetric, currentTimeNs); 524 if (!mIsActive && metric->isActive()) { 525 StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey, 526 /*activate=*/ true); 527 } 528 mIsActive |= metric->isActive(); 529 } 530 } 531 } 532 } 533 534 void MetricsManager::writeActiveConfigToProtoOutputStream( 535 int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { 536 proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId()); 537 proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid()); 538 for (int metricIndex : mMetricIndexesWithActivation) { 539 const auto& metric = mAllMetricProducers[metricIndex]; 540 const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | 541 FIELD_ID_ACTIVE_CONFIG_METRIC); 542 metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto); 543 proto->end(metricToken); 544 } 545 } 546 547 548 549 550 } // namespace statsd 551 } // namespace os 552 } // namespace android 553