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 <unordered_map> 20 21 #include <android/util/ProtoOutputStream.h> 22 #include <gtest/gtest_prod.h> 23 #include "../condition/ConditionTracker.h" 24 #include "../external/PullDataReceiver.h" 25 #include "../external/StatsPullerManager.h" 26 #include "../matchers/matcher_util.h" 27 #include "../matchers/EventMatcherWizard.h" 28 #include "MetricProducer.h" 29 #include "src/statsd_config.pb.h" 30 #include "../stats_util.h" 31 32 namespace android { 33 namespace os { 34 namespace statsd { 35 36 struct GaugeAtom { GaugeAtomGaugeAtom37 GaugeAtom(const std::shared_ptr<vector<FieldValue>>& fields, int64_t elapsedTimeNs) 38 : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) { 39 } 40 std::shared_ptr<vector<FieldValue>> mFields; 41 int64_t mElapsedTimestampNs; 42 }; 43 44 struct GaugeBucket { 45 int64_t mBucketStartNs; 46 int64_t mBucketEndNs; 47 std::vector<GaugeAtom> mGaugeAtoms; 48 49 // Maps the field/value pairs of an atom to a list of timestamps used to deduplicate atoms. 50 std::unordered_map<AtomDimensionKey, std::vector<int64_t>> mAggregatedAtoms; 51 }; 52 53 typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> 54 DimToGaugeAtomsMap; 55 56 // This gauge metric producer first register the puller to automatically pull the gauge at the 57 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise 58 // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric 59 // producer always reports the gauge at the earliest time of the bucket when the condition is met. 60 class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver { 61 public: 62 GaugeMetricProducer( 63 const ConfigKey& key, const GaugeMetric& gaugeMetric, int conditionIndex, 64 const vector<ConditionState>& initialConditionCache, 65 const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash, 66 const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, 67 const int pullTagId, int triggerAtomId, int atomId, const int64_t timeBaseNs, 68 int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, 69 const wp<ConfigMetadataProvider> configMetadataProvider, 70 const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, 71 const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& 72 eventDeactivationMap = {}, 73 const size_t dimensionSoftLimit = StatsdStats::kDimensionKeySizeSoftLimit, 74 const size_t dimensionHardLimit = StatsdStats::kDimensionKeySizeHardLimit); 75 76 virtual ~GaugeMetricProducer(); 77 78 // Handles when the pulled data arrives. 79 void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data, PullResult pullResult, 80 int64_t originalPullTimeNs) override; 81 82 // Determine if metric needs to pull isPullNeeded()83 bool isPullNeeded() const override { 84 std::lock_guard<std::mutex> lock(mMutex); 85 return mIsActive && (mCondition == ConditionState::kTrue) && 86 shouldKeepRandomSample(mPullProbability); 87 }; 88 89 // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. notifyAppUpgradeInternalLocked(int64_t eventTimeNs)90 void notifyAppUpgradeInternalLocked(int64_t eventTimeNs) override { 91 flushLocked(eventTimeNs); 92 if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) { 93 pullAndMatchEventsLocked(eventTimeNs); 94 } 95 }; 96 97 // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. onStatsdInitCompleted(int64_t eventTimeNs)98 void onStatsdInitCompleted(int64_t eventTimeNs) override { 99 ATRACE_CALL(); 100 std::lock_guard<std::mutex> lock(mMutex); 101 flushLocked(eventTimeNs); 102 if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) { 103 pullAndMatchEventsLocked(eventTimeNs); 104 } 105 }; 106 getMetricType()107 MetricType getMetricType() const override { 108 return METRIC_TYPE_GAUGE; 109 } 110 111 protected: 112 void onMatchedLogEventInternalLocked( 113 const size_t matcherIndex, const MetricDimensionKey& eventKey, 114 const ConditionKey& conditionKey, bool condition, const LogEvent& event, 115 const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; 116 117 private: 118 void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket, 119 const bool erase_data, const DumpLatency dumpLatency, 120 std::set<string>* str_set, std::set<int32_t>& usedUids, 121 android::util::ProtoOutputStream* protoOutput) override; 122 void clearPastBucketsLocked(const int64_t dumpTimeNs) override; 123 124 // Internal interface to handle condition change. 125 void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override; 126 127 // Internal interface to handle active state change. 128 void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override; 129 130 // Internal interface to handle sliced condition change. 131 void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override; 132 133 // Internal function to calculate the current used bytes. 134 size_t byteSizeLocked() const override; 135 136 void dumpStatesLocked(int out, bool verbose) const override; 137 138 void dropDataLocked(const int64_t dropTimeNs) override; 139 140 // Util function to flush the old packet. 141 void flushIfNeededLocked(int64_t eventTime) override; 142 143 void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override; 144 145 void prepareFirstBucketLocked() override; 146 147 // Only call if mCondition == ConditionState::kTrue && metric is active. 148 void pullAndMatchEventsLocked(const int64_t timestampNs); 149 150 size_t computeGaugeBucketSizeLocked( 151 const bool isFullBucket, const MetricDimensionKey& dimKey, const bool isFirstBucket, 152 const std::unordered_map<AtomDimensionKey, std::vector<int64_t>>& aggregatedAtoms) 153 const; 154 155 optional<InvalidConfigReason> onConfigUpdatedLocked( 156 const StatsdConfig& config, int configIndex, int metricIndex, 157 const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, 158 const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, 159 const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, 160 const sp<EventMatcherWizard>& matcherWizard, 161 const std::vector<sp<ConditionTracker>>& allConditionTrackers, 162 const std::unordered_map<int64_t, int>& conditionTrackerMap, 163 const sp<ConditionWizard>& wizard, 164 const std::unordered_map<int64_t, int>& metricToActivationMap, 165 std::unordered_map<int, std::vector<int>>& trackerToMetricMap, 166 std::unordered_map<int, std::vector<int>>& conditionToMetricMap, 167 std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, 168 std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, 169 std::vector<int>& metricsWithActivation) override; 170 isRandomNSamples()171 inline bool isRandomNSamples() const { 172 return (mTriggerAtomId == -1 && mSamplingType == GaugeMetric::FIRST_N_SAMPLES) || 173 mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE; 174 } 175 176 DataCorruptionSeverity determineCorruptionSeverity(int32_t atomId, DataCorruptedReason reason, 177 LostAtomType atomType) const override; 178 179 int mWhatMatcherIndex; 180 181 sp<EventMatcherWizard> mEventMatcherWizard; 182 183 sp<StatsPullerManager> mPullerManager; 184 // tagId for pulled data. -1 if this is not pulled 185 const int mPullTagId; 186 187 // tagId for atoms that trigger the pulling, if any 188 const int mTriggerAtomId; 189 190 // tagId for output atom 191 const int mAtomId; 192 193 // if this is pulled metric 194 const bool mIsPulled; 195 196 // Save the past buckets and we can clear when the StatsLogReport is dumped. 197 std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets; 198 199 // The current partial bucket. 200 std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket; 201 202 // The current full bucket for anomaly detection. This is updated to the latest value seen for 203 // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket). 204 std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly; 205 206 const int64_t mMinBucketSizeNs; 207 208 // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map 209 // for each slice with the latest value. 210 void updateCurrentSlicedBucketForAnomaly(); 211 212 // Allowlist of fields to report. Empty means all are reported. 213 std::vector<Matcher> mFieldMatchers; 214 215 GaugeMetric::SamplingType mSamplingType; 216 217 const int64_t mMaxPullDelayNs; 218 219 // apply an allowlist on the original input 220 std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event); 221 222 // Util function to check whether the specified dimension hits the guardrail. 223 bool hitGuardRailLocked(const MetricDimensionKey& newKey); 224 225 static const size_t kBucketSize = sizeof(GaugeBucket{}); 226 227 const size_t mDimensionSoftLimit; 228 229 const size_t mDimensionHardLimit; 230 231 const size_t mGaugeAtomsPerDimensionLimit; 232 233 // Tracks if the dimension guardrail has been hit in the current report. 234 bool mDimensionGuardrailHit; 235 236 const int mSamplingPercentage; 237 238 const int mPullProbability; 239 240 FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition); 241 FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition); 242 FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition); 243 FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled); 244 FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection); 245 FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); 246 FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); 247 FRIEND_TEST(GaugeMetricProducerTest, TestPullNWithoutTrigger); 248 FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput); 249 FRIEND_TEST(GaugeMetricProducerTest, TestPullDimensionalSampling); 250 251 FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents); 252 FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled); 253 254 FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics); 255 256 FRIEND_TEST(MetricsManagerUtilDimLimitTest, TestDimLimit); 257 258 FRIEND_TEST(ConfigUpdateDimLimitTest, TestDimLimit); 259 }; 260 261 } // namespace statsd 262 } // namespace os 263 } // namespace android 264