• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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