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 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, 58 const int64_t timeBaseNs, const int64_t currentTimeNs, 59 const sp<UidMap> &uidMap, 60 const sp<AlarmMonitor>& anomalyAlarmMonitor, 61 const sp<AlarmMonitor>& periodicAlarmMonitor) 62 : mConfigKey(key), mUidMap(uidMap), 63 mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1), 64 mTtlEndNs(-1), 65 mLastReportTimeNs(currentTimeNs), 66 mLastReportWallClockNs(getWallClockNs()) { 67 // Init the ttl end timestamp. 68 refreshTtl(timeBaseNs); 69 70 mConfigValid = 71 initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, 72 timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, 73 mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, 74 mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, 75 mTrackerToConditionMap, mNoReportMetricIds); 76 77 mHashStringsInReport = config.hash_strings_in_metric_report(); 78 79 if (config.allowed_log_source_size() == 0) { 80 mConfigValid = false; 81 ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at " 82 "least AID_SYSTEM and AID_STATSD to the allowed_log_source field."); 83 } else { 84 for (const auto& source : config.allowed_log_source()) { 85 auto it = UidMap::sAidToUidMapping.find(source); 86 if (it != UidMap::sAidToUidMapping.end()) { 87 mAllowedUid.push_back(it->second); 88 } else { 89 mAllowedPkg.push_back(source); 90 } 91 } 92 93 if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) { 94 ALOGE("Too many log sources. This is likely to be an error in the config."); 95 mConfigValid = false; 96 } else { 97 initLogSourceWhiteList(); 98 } 99 } 100 101 // Store the sub-configs used. 102 for (const auto& annotation : config.annotation()) { 103 mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32()); 104 } 105 106 // Guardrail. Reject the config if it's too big. 107 if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || 108 mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || 109 mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) { 110 ALOGE("This config is too big! Reject!"); 111 mConfigValid = false; 112 } 113 if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) { 114 ALOGE("This config has too many alerts! Reject!"); 115 mConfigValid = false; 116 } 117 // no matter whether this config is valid, log it in the stats. 118 StatsdStats::getInstance().noteConfigReceived( 119 key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(), 120 mAllAnomalyTrackers.size(), mAnnotations, mConfigValid); 121 } 122 123 MetricsManager::~MetricsManager() { 124 VLOG("~MetricsManager()"); 125 } 126 127 void MetricsManager::initLogSourceWhiteList() { 128 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); 129 mAllowedLogSources.clear(); 130 mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end()); 131 132 for (const auto& pkg : mAllowedPkg) { 133 auto uids = mUidMap->getAppUid(pkg); 134 mAllowedLogSources.insert(uids.begin(), uids.end()); 135 } 136 if (DEBUG) { 137 for (const auto& uid : mAllowedLogSources) { 138 VLOG("Allowed uid %d", uid); 139 } 140 } 141 } 142 143 bool MetricsManager::isConfigValid() const { 144 return mConfigValid; 145 } 146 147 void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, 148 const int64_t version) { 149 // check if we care this package 150 if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { 151 return; 152 } 153 // We will re-initialize the whole list because we don't want to keep the multi mapping of 154 // UID<->pkg inside MetricsManager to reduce the memory usage. 155 initLogSourceWhiteList(); 156 } 157 158 void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, 159 const int uid) { 160 // check if we care this package 161 if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { 162 return; 163 } 164 // We will re-initialize the whole list because we don't want to keep the multi mapping of 165 // UID<->pkg inside MetricsManager to reduce the memory usage. 166 initLogSourceWhiteList(); 167 } 168 169 void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) { 170 if (mAllowedPkg.size() == 0) { 171 return; 172 } 173 initLogSourceWhiteList(); 174 } 175 176 void MetricsManager::dumpStates(FILE* out, bool verbose) { 177 fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str()); 178 { 179 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); 180 for (const auto& source : mAllowedLogSources) { 181 fprintf(out, "%d ", source); 182 } 183 } 184 fprintf(out, "\n"); 185 for (const auto& producer : mAllMetricProducers) { 186 producer->dumpStates(out, verbose); 187 } 188 } 189 190 void MetricsManager::dropData(const int64_t dropTimeNs) { 191 for (const auto& producer : mAllMetricProducers) { 192 producer->dropData(dropTimeNs); 193 } 194 } 195 196 void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, 197 const bool include_current_partial_bucket, 198 std::set<string> *str_set, 199 ProtoOutputStream* protoOutput) { 200 VLOG("=========================Metric Reports Start=========================="); 201 // one StatsLogReport per MetricProduer 202 for (const auto& producer : mAllMetricProducers) { 203 if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) { 204 uint64_t token = protoOutput->start( 205 FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS); 206 if (mHashStringsInReport) { 207 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, str_set, 208 protoOutput); 209 } else { 210 producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, nullptr, 211 protoOutput); 212 } 213 protoOutput->end(token); 214 } else { 215 producer->clearPastBuckets(dumpTimeStampNs); 216 } 217 } 218 for (const auto& annotation : mAnnotations) { 219 uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | 220 FIELD_ID_ANNOTATIONS); 221 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64, 222 (long long)annotation.first); 223 protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second); 224 protoOutput->end(token); 225 } 226 227 mLastReportTimeNs = dumpTimeStampNs; 228 mLastReportWallClockNs = getWallClockNs(); 229 VLOG("=========================Metric Reports End=========================="); 230 } 231 232 // Consume the stats log if it's interesting to this metric. 233 void MetricsManager::onLogEvent(const LogEvent& event) { 234 if (!mConfigValid) { 235 return; 236 } 237 238 if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { 239 // Check that app breadcrumb reported fields are valid. 240 // TODO: Find a way to make these checks easier to maintain. 241 status_t err = NO_ERROR; 242 243 // Uid is 3rd from last field and must match the caller's uid, 244 // unless that caller is statsd itself (statsd is allowed to spoof uids). 245 long appHookUid = event.GetLong(event.size()-2, &err); 246 if (err != NO_ERROR ) { 247 VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid"); 248 return; 249 } 250 int32_t loggerUid = event.GetUid(); 251 if (loggerUid != appHookUid && loggerUid != AID_STATSD) { 252 VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d", 253 appHookUid, loggerUid); 254 return; 255 } 256 257 // The state must be from 0,3. This part of code must be manually updated. 258 long appHookState = event.GetLong(event.size(), &err); 259 if (err != NO_ERROR ) { 260 VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field"); 261 return; 262 } else if (appHookState < 0 || appHookState > 3) { 263 VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState); 264 return; 265 } 266 } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) { 267 // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp. 268 // Check that the davey duration is reasonable. Max length check is for privacy. 269 status_t err = NO_ERROR; 270 271 // Uid is the first field provided. 272 long jankUid = event.GetLong(1, &err); 273 if (err != NO_ERROR ) { 274 VLOG("Davey occurred had error when parsing the uid"); 275 return; 276 } 277 int32_t loggerUid = event.GetUid(); 278 if (loggerUid != jankUid && loggerUid != AID_STATSD) { 279 VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid, 280 loggerUid); 281 return; 282 } 283 284 long duration = event.GetLong(event.size(), &err); 285 if (err != NO_ERROR ) { 286 VLOG("Davey occurred had error when parsing the duration"); 287 return; 288 } else if (duration > 100000) { 289 VLOG("Davey duration is unreasonably long: %ld", duration); 290 return; 291 } 292 } else { 293 std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); 294 if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { 295 VLOG("log source %d not on the whitelist", event.GetUid()); 296 return; 297 } 298 } 299 300 int tagId = event.GetTagId(); 301 int64_t eventTime = event.GetElapsedTimestampNs(); 302 if (mTagIds.find(tagId) == mTagIds.end()) { 303 // not interesting... 304 return; 305 } 306 307 vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed); 308 309 for (auto& matcher : mAllAtomMatchers) { 310 matcher->onLogEvent(event, mAllAtomMatchers, matcherCache); 311 } 312 313 // A bitmap to see which ConditionTracker needs to be re-evaluated. 314 vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); 315 316 for (const auto& pair : mTrackerToConditionMap) { 317 if (matcherCache[pair.first] == MatchingState::kMatched) { 318 const auto& conditionList = pair.second; 319 for (const int conditionIndex : conditionList) { 320 conditionToBeEvaluated[conditionIndex] = true; 321 } 322 } 323 } 324 325 vector<ConditionState> conditionCache(mAllConditionTrackers.size(), 326 ConditionState::kNotEvaluated); 327 // A bitmap to track if a condition has changed value. 328 vector<bool> changedCache(mAllConditionTrackers.size(), false); 329 for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { 330 if (conditionToBeEvaluated[i] == false) { 331 continue; 332 } 333 sp<ConditionTracker>& condition = mAllConditionTrackers[i]; 334 condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache, 335 changedCache); 336 } 337 338 for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { 339 if (changedCache[i] == false) { 340 continue; 341 } 342 auto pair = mConditionToMetricMap.find(i); 343 if (pair != mConditionToMetricMap.end()) { 344 auto& metricList = pair->second; 345 for (auto metricIndex : metricList) { 346 // metric cares about non sliced condition, and it's changed. 347 // Push the new condition to it directly. 348 if (!mAllMetricProducers[metricIndex]->isConditionSliced()) { 349 mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i], 350 eventTime); 351 // metric cares about sliced conditions, and it may have changed. Send 352 // notification, and the metric can query the sliced conditions that are 353 // interesting to it. 354 } else { 355 mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i], 356 eventTime); 357 } 358 } 359 } 360 } 361 362 // For matched AtomMatchers, tell relevant metrics that a matched event has come. 363 for (size_t i = 0; i < mAllAtomMatchers.size(); i++) { 364 if (matcherCache[i] == MatchingState::kMatched) { 365 StatsdStats::getInstance().noteMatcherMatched(mConfigKey, 366 mAllAtomMatchers[i]->getId()); 367 auto pair = mTrackerToMetricMap.find(i); 368 if (pair != mTrackerToMetricMap.end()) { 369 auto& metricList = pair->second; 370 for (const int metricIndex : metricList) { 371 // pushed metrics are never scheduled pulls 372 mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event); 373 } 374 } 375 } 376 } 377 } 378 379 void MetricsManager::onAnomalyAlarmFired( 380 const int64_t& timestampNs, 381 unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { 382 for (const auto& itr : mAllAnomalyTrackers) { 383 itr->informAlarmsFired(timestampNs, alarmSet); 384 } 385 } 386 387 void MetricsManager::onPeriodicAlarmFired( 388 const int64_t& timestampNs, 389 unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { 390 for (const auto& itr : mAllPeriodicAlarmTrackers) { 391 itr->informAlarmsFired(timestampNs, alarmSet); 392 } 393 } 394 395 // Returns the total byte size of all metrics managed by a single config source. 396 size_t MetricsManager::byteSize() { 397 size_t totalSize = 0; 398 for (auto metricProducer : mAllMetricProducers) { 399 totalSize += metricProducer->byteSize(); 400 } 401 return totalSize; 402 } 403 404 } // namespace statsd 405 } // namespace os 406 } // namespace android 407