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 "MetricProducer.h"
28 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
29 #include "../stats_util.h"
30 
31 namespace android {
32 namespace os {
33 namespace statsd {
34 
35 struct GaugeAtom {
GaugeAtomGaugeAtom36     GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int wallClockNs)
37         : mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
38     }
39     std::shared_ptr<vector<FieldValue>> mFields;
40     int64_t mElapsedTimestamps;
41     int64_t mWallClockTimestampNs;
42 };
43 
44 struct GaugeBucket {
45     int64_t mBucketStartNs;
46     int64_t mBucketEndNs;
47     std::vector<GaugeAtom> mGaugeAtoms;
48 };
49 
50 typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
51     DimToGaugeAtomsMap;
52 
53 // This gauge metric producer first register the puller to automatically pull the gauge at the
54 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
55 // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
56 // producer always reports the guage at the earliest time of the bucket when the condition is met.
57 class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
58 public:
59     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
60                         const int conditionIndex, const sp<ConditionWizard>& wizard,
61                         const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
62 
63     virtual ~GaugeMetricProducer();
64 
65     // Handles when the pulled data arrives.
66     void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
67 
68     // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
notifyAppUpgrade(const int64_t & eventTimeNs,const string & apk,const int uid,const int64_t version)69     void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
70                           const int64_t version) override {
71         std::lock_guard<std::mutex> lock(mMutex);
72 
73         if (eventTimeNs > getCurrentBucketEndTimeNs()) {
74             // Flush full buckets on the normal path up to the latest bucket boundary.
75             flushIfNeededLocked(eventTimeNs);
76         }
77         flushCurrentBucketLocked(eventTimeNs);
78         mCurrentBucketStartTimeNs = eventTimeNs;
79         if (mPullTagId != -1) {
80             pullLocked(eventTimeNs);
81         }
82     };
83 
84 protected:
85     void onMatchedLogEventInternalLocked(
86             const size_t matcherIndex, const MetricDimensionKey& eventKey,
87             const ConditionKey& conditionKey, bool condition,
88             const LogEvent& event) override;
89 
90 private:
91     void onDumpReportLocked(const int64_t dumpTimeNs,
92                             const bool include_current_partial_bucket,
93                             std::set<string> *str_set,
94                             android::util::ProtoOutputStream* protoOutput) override;
95     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
96 
97     // for testing
98     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
99                         const int conditionIndex, const sp<ConditionWizard>& wizard,
100                         const int pullTagId,
101                         const int64_t timeBaseNs, const int64_t startTimeNs,
102                         std::shared_ptr<StatsPullerManager> statsPullerManager);
103 
104     // Internal interface to handle condition change.
105     void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
106 
107     // Internal interface to handle sliced condition change.
108     void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
109 
110     // Internal function to calculate the current used bytes.
111     size_t byteSizeLocked() const override;
112 
113     void dumpStatesLocked(FILE* out, bool verbose) const override;
114 
115     void dropDataLocked(const int64_t dropTimeNs) override;
116 
117     // Util function to flush the old packet.
118     void flushIfNeededLocked(const int64_t& eventTime) override;
119 
120     void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
121 
122     void pullLocked(const int64_t timestampNs);
123 
124     int mTagId;
125 
126     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
127     // tagId for pulled data. -1 if this is not pulled
128     const int mPullTagId;
129 
130     // Save the past buckets and we can clear when the StatsLogReport is dumped.
131     // TODO: Add a lock to mPastBuckets.
132     std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
133 
134     // The current partial bucket.
135     std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
136 
137     // The current full bucket for anomaly detection. This is updated to the latest value seen for
138     // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket).
139     std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
140 
141     // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
142     std::list<std::pair<int64_t, int64_t>> mSkippedBuckets;
143 
144     const int64_t mMinBucketSizeNs;
145 
146     // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map
147     // for each slice with the latest value.
148     void updateCurrentSlicedBucketForAnomaly();
149 
150     // Whitelist of fields to report. Empty means all are reported.
151     std::vector<Matcher> mFieldMatchers;
152 
153     GaugeMetric::SamplingType mSamplingType;
154 
155     // apply a whitelist on the original input
156     std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
157 
158     // Util function to check whether the specified dimension hits the guardrail.
159     bool hitGuardRailLocked(const MetricDimensionKey& newKey);
160 
161     static const size_t kBucketSize = sizeof(GaugeBucket{});
162 
163     const size_t mDimensionSoftLimit;
164 
165     const size_t mDimensionHardLimit;
166 
167     const size_t mGaugeAtomsPerDimensionLimit;
168 
169     FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
170     FRIEND_TEST(GaugeMetricProducerTest, TestWithSlicedCondition);
171     FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
172     FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
173     FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
174     FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
175 };
176 
177 }  // namespace statsd
178 }  // namespace os
179 }  // namespace android
180