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 17 #pragma once 18 19 #include <gtest/gtest_prod.h> 20 21 #include <optional> 22 23 #include "FieldValue.h" 24 #include "HashableDimensionKey.h" 25 #include "MetricProducer.h" 26 #include "anomaly/AnomalyTracker.h" 27 #include "condition/ConditionTimer.h" 28 #include "condition/ConditionTracker.h" 29 #include "external/PullDataReceiver.h" 30 #include "external/StatsPullerManager.h" 31 #include "matchers/EventMatcherWizard.h" 32 #include "src/statsd_config.pb.h" 33 #include "stats_log_util.h" 34 #include "stats_util.h" 35 36 namespace android { 37 namespace os { 38 namespace statsd { 39 40 template <typename AggregatedValue> 41 struct PastBucket { 42 int64_t mBucketStartNs; 43 int64_t mBucketEndNs; 44 std::vector<int> aggIndex; 45 std::vector<AggregatedValue> aggregates; 46 47 /** 48 * If the metric has no condition, then this field is just wasted. 49 * When we tune statsd memory usage in the future, this is a candidate to optimize. 50 */ 51 int64_t mConditionTrueNs; 52 53 /** 54 * The semantic is the value which needs to be applied to mConditionTrueNs for correction 55 * to be performed prior normalization calculation on the user (read server) side. Applied only 56 * to ValueMetrics with pulled atoms. 57 */ 58 int64_t mConditionCorrectionNs; 59 }; 60 61 // Aggregates values within buckets. 62 // 63 // There are different events that might complete a bucket 64 // - a condition change 65 // - an app upgrade 66 // - an alarm set to the end of the bucket 67 template <typename AggregatedValue, typename DimExtras> 68 class ValueMetricProducer : public MetricProducer, public virtual PullDataReceiver { 69 public: 70 struct PullOptions { 71 const int pullAtomId; 72 const sp<StatsPullerManager>& pullerManager; 73 }; 74 75 struct BucketOptions { 76 const int64_t timeBaseNs; 77 const int64_t startTimeNs; 78 const int64_t bucketSizeNs; 79 const int64_t minBucketSizeNs; 80 const optional<int64_t> conditionCorrectionThresholdNs; 81 const optional<bool> splitBucketForAppUpgrade; 82 }; 83 84 struct WhatOptions { 85 const bool containsAnyPositionInDimensionsInWhat; 86 const bool shouldUseNestedDimensions; 87 const int whatMatcherIndex; 88 const sp<EventMatcherWizard>& matcherWizard; 89 const FieldMatcher& dimensionsInWhat; 90 const vector<Matcher>& fieldMatchers; 91 }; 92 93 struct ConditionOptions { 94 const int conditionIndex; 95 const ConditionLinks& conditionLinks; 96 const vector<ConditionState>& initialConditionCache; 97 const sp<ConditionWizard>& conditionWizard; 98 }; 99 100 struct StateOptions { 101 const StateLinks& stateLinks; 102 const vector<int>& slicedStateAtoms; 103 const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap; 104 }; 105 106 struct ActivationOptions { 107 const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap; 108 const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& 109 eventDeactivationMap; 110 }; 111 112 struct GuardrailOptions { 113 const size_t dimensionSoftLimit; 114 const size_t dimensionHardLimit; 115 }; 116 117 virtual ~ValueMetricProducer(); 118 119 // Process data pulled on bucket boundary. onDataPulled(const std::vector<std::shared_ptr<LogEvent>> & data,bool pullSuccess,int64_t originalPullTimeNs)120 virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, bool pullSuccess, 121 int64_t originalPullTimeNs) override { 122 } 123 124 125 // ValueMetric needs special logic if it's a pulled atom. 126 void onStatsdInitCompleted(const int64_t& eventTimeNs) override; 127 128 void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, 129 const FieldValue& oldState, const FieldValue& newState) override; 130 131 protected: 132 ValueMetricProducer(const int64_t& metricId, const ConfigKey& key, const uint64_t protoHash, 133 const PullOptions& pullOptions, const BucketOptions& bucketOptions, 134 const WhatOptions& whatOptions, const ConditionOptions& conditionOptions, 135 const StateOptions& stateOptions, 136 const ActivationOptions& activationOptions, 137 const GuardrailOptions& guardrailOptions); 138 139 void onMatchedLogEventInternalLocked( 140 const size_t matcherIndex, const MetricDimensionKey& eventKey, 141 const ConditionKey& conditionKey, bool condition, const LogEvent& event, 142 const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; 143 144 // Determine whether or not a LogEvent can be skipped. 145 virtual inline bool canSkipLogEventLocked( 146 const MetricDimensionKey& eventKey, bool condition, int64_t eventTimeNs, 147 const std::map<int, HashableDimensionKey>& statePrimaryKeys) const = 0; 148 149 void notifyAppUpgradeInternalLocked(const int64_t eventTimeNs) override; 150 151 void onDumpReportLocked(const int64_t dumpTimeNs, const bool includeCurrentPartialBucket, 152 const bool eraseData, const DumpLatency dumpLatency, 153 std::set<string>* strSet, 154 android::util::ProtoOutputStream* protoOutput) override; 155 156 struct DumpProtoFields { 157 const int metricTypeFieldId; 158 const int bucketNumFieldId; 159 const int startBucketMsFieldId; 160 const int endBucketMsFieldId; 161 const int conditionTrueNsFieldId; 162 const optional<int> conditionCorrectionNsFieldId; 163 }; 164 165 virtual DumpProtoFields getDumpProtoFields() const = 0; 166 167 void clearPastBucketsLocked(const int64_t dumpTimeNs) override; 168 169 // ValueMetricProducer internal interface to handle active state change. 170 void onActiveStateChangedLocked(const int64_t eventTimeNs) override; 171 onActiveStateChangedInternalLocked(const int64_t eventTimeNs)172 virtual void onActiveStateChangedInternalLocked(const int64_t eventTimeNs) { 173 } 174 175 // ValueMetricProducer internal interface to handle condition change. 176 void onConditionChangedLocked(const bool condition, const int64_t eventTimeNs) override; 177 178 // Only called when mIsActive, the event is NOT too late, and after pulling. onConditionChangedInternalLocked(const ConditionState oldCondition,const ConditionState newCondition,const int64_t eventTimeNs)179 virtual void onConditionChangedInternalLocked(const ConditionState oldCondition, 180 const ConditionState newCondition, 181 const int64_t eventTimeNs) { 182 } 183 184 // Internal interface to handle sliced condition change. 185 void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override; 186 187 void dumpStatesLocked(FILE* out, bool verbose) const override; 188 189 virtual std::string aggregatedValueToString(const AggregatedValue& aggregate) const = 0; 190 191 // For pulled metrics, this method should only be called if a pull has been done. Else we will 192 // not have complete data for the bucket. 193 void flushIfNeededLocked(const int64_t& eventTime) override; 194 195 // For pulled metrics, this method should only be called if a pulled has been done. Else we will 196 // not have complete data for the bucket. 197 void flushCurrentBucketLocked(const int64_t& eventTimeNs, 198 const int64_t& nextBucketStartTimeNs) override; 199 200 void dropDataLocked(const int64_t dropTimeNs) override; 201 202 // Calculate how many buckets are present between the current bucket and eventTimeNs. 203 int64_t calcBucketsForwardCount(const int64_t eventTimeNs) const; 204 205 // Mark the data as invalid. 206 virtual void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); 207 208 // Skips the current bucket without notifying StatsdStats of the skipped bucket. 209 // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that 210 // causes the bucket to be invalidated will not notify StatsdStats. 211 void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason); 212 213 bool onConfigUpdatedLocked( 214 const StatsdConfig& config, const int configIndex, const int metricIndex, 215 const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, 216 const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, 217 const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, 218 const sp<EventMatcherWizard>& matcherWizard, 219 const std::vector<sp<ConditionTracker>>& allConditionTrackers, 220 const std::unordered_map<int64_t, int>& conditionTrackerMap, 221 const sp<ConditionWizard>& wizard, 222 const std::unordered_map<int64_t, int>& metricToActivationMap, 223 std::unordered_map<int, std::vector<int>>& trackerToMetricMap, 224 std::unordered_map<int, std::vector<int>>& conditionToMetricMap, 225 std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, 226 std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, 227 std::vector<int>& metricsWithActivation) override; 228 229 virtual optional<int64_t> getConditionIdForMetric(const StatsdConfig& config, 230 const int configIndex) const = 0; 231 232 virtual int64_t getWhatAtomMatcherIdForMetric(const StatsdConfig& config, 233 const int configIndex) const = 0; 234 235 virtual ConditionLinks getConditionLinksForMetric(const StatsdConfig& config, 236 const int configIndex) const = 0; 237 238 int mWhatMatcherIndex; 239 240 sp<EventMatcherWizard> mEventMatcherWizard; 241 242 const sp<StatsPullerManager> mPullerManager; 243 244 // Value fields for matching. 245 const std::vector<Matcher> mFieldMatchers; 246 247 // Value fields for matching. 248 std::set<HashableDimensionKey> mMatchedMetricDimensionKeys; 249 250 // Holds the atom id, primary key pair from a state change. 251 // Only used for pulled metrics. 252 // TODO(b/185796114): can be passed as function arguments instead. 253 pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey; 254 255 // Atom Id for pulled data. -1 if this is not pulled. 256 const int mPullAtomId; 257 258 // Tracks the value information of one value field. 259 struct Interval { 260 // Index in multi value aggregation. 261 int aggIndex; 262 263 // Current aggregation, depending on the aggregation type. 264 AggregatedValue aggregate; 265 266 // Number of samples collected. 267 int sampleSize = 0; 268 hasValueInterval269 inline bool hasValue() const { 270 return sampleSize > 0; 271 } 272 }; 273 274 // Internal state of an ongoing aggregation bucket. 275 struct CurrentBucket { 276 // If the `MetricDimensionKey` state key is the current state key, then 277 // the condition timer will be updated later (e.g. condition/state/active 278 // state change) with the correct condition and time. CurrentBucketCurrentBucket279 CurrentBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) { 280 } 281 // Value information for each value field of the metric. 282 std::vector<Interval> intervals; 283 // Tracks how long the condition is true. 284 ConditionTimer conditionTimer; 285 }; 286 287 // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat 288 // key and StateValuesKey pair. 289 std::unordered_map<MetricDimensionKey, CurrentBucket> mCurrentSlicedBucket; 290 291 // State key and any extra information for a specific DimensionsInWhat key. 292 struct DimensionsInWhatInfo { DimensionsInWhatInfoDimensionsInWhatInfo293 DimensionsInWhatInfo(const HashableDimensionKey& stateKey) 294 : dimExtras(), currentState(stateKey), hasCurrentState(false) { 295 } 296 297 DimExtras dimExtras; 298 299 // Whether new data is seen in the bucket. 300 // TODO, this could be per base in the dim extras. 301 bool seenNewData = false; 302 303 // Last seen state value(s). 304 HashableDimensionKey currentState; 305 // Whether this dimensions in what key has a current state key. 306 bool hasCurrentState; 307 }; 308 309 // Tracks current state key and other information for each DimensionsInWhat key. 310 std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mDimInfos; 311 312 // Save the past buckets and we can clear when the StatsLogReport is dumped. 313 std::unordered_map<MetricDimensionKey, std::vector<PastBucket<AggregatedValue>>> mPastBuckets; 314 315 const int64_t mMinBucketSizeNs; 316 317 // Util function to check whether the specified dimension hits the guardrail. 318 bool hitGuardRailLocked(const MetricDimensionKey& newKey) const; 319 320 bool hasReachedGuardRailLimit() const; 321 pullAndMatchEventsLocked(const int64_t timestampNs)322 virtual void pullAndMatchEventsLocked(const int64_t timestampNs) { 323 } 324 325 virtual bool multipleBucketsSkipped(const int64_t numBucketsForward) const = 0; 326 327 virtual PastBucket<AggregatedValue> buildPartialBucket(int64_t bucketEndTime, 328 std::vector<Interval>& intervals) = 0; 329 330 virtual void closeCurrentBucket(const int64_t eventTimeNs, const int64_t nextBucketStartTimeNs); 331 332 virtual void initNextSlicedBucket(int64_t nextBucketStartTimeNs); 333 334 // Updates the condition timers in the current sliced bucket when there is a 335 // condition change or an active state change. 336 void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs); 337 338 virtual void writePastBucketAggregateToProto(const int aggIndex, 339 const AggregatedValue& aggregate, 340 ProtoOutputStream* const protoOutput) const = 0; 341 342 static const size_t kBucketSize = sizeof(PastBucket<AggregatedValue>{}); 343 344 const size_t mDimensionSoftLimit; 345 346 const size_t mDimensionHardLimit; 347 348 // This is to track whether or not the bucket is skipped for any of the reasons listed in 349 // BucketDropReason, many of which make the bucket potentially invalid. 350 bool mCurrentBucketIsSkipped; 351 352 ConditionTimer mConditionTimer; 353 354 /** Stores condition correction threshold from the ValueMetric configuration */ 355 optional<int64_t> mConditionCorrectionThresholdNs; 356 isEventLateLocked(const int64_t eventTimeNs)357 inline bool isEventLateLocked(const int64_t eventTimeNs) const { 358 return eventTimeNs < mCurrentBucketStartTimeNs; 359 } 360 361 // Returns true if any of the intervals have seen new data. 362 // This should return true unless there is an error parsing the value fields from the event. 363 virtual bool aggregateFields(const int64_t eventTimeNs, const MetricDimensionKey& eventKey, 364 const LogEvent& event, std::vector<Interval>& intervals, 365 DimExtras& dimExtras) = 0; 366 367 // If this is a pulled metric isPulled()368 inline bool isPulled() const { 369 return mPullAtomId != -1; 370 } 371 372 private: 373 }; // ValueMetricProducer 374 375 } // namespace statsd 376 } // namespace os 377 } // namespace android 378