• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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 #pragma once
17 
18 #include "config/ConfigKey.h"
19 #include "statslog.h"
20 
21 #include <gtest/gtest_prod.h>
22 #include <log/log_time.h>
23 #include <list>
24 #include <mutex>
25 #include <string>
26 #include <vector>
27 #include <unordered_map>
28 
29 namespace android {
30 namespace os {
31 namespace statsd {
32 
33 struct ConfigStats {
34     int32_t uid;
35     int64_t id;
36     int32_t creation_time_sec;
37     int32_t deletion_time_sec = 0;
38     int32_t reset_time_sec = 0;
39     int32_t metric_count;
40     int32_t condition_count;
41     int32_t matcher_count;
42     int32_t alert_count;
43     bool is_valid;
44 
45     std::list<int32_t> broadcast_sent_time_sec;
46 
47     // Times at which this config is activated.
48     std::list<int32_t> activation_time_sec;
49 
50     // Times at which this config is deactivated.
51     std::list<int32_t> deactivation_time_sec;
52 
53     std::list<int32_t> data_drop_time_sec;
54     // Number of bytes dropped at corresponding time.
55     std::list<int64_t> data_drop_bytes;
56     std::list<std::pair<int32_t, int64_t>> dump_report_stats;
57 
58     // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
59     std::map<const int64_t, int> matcher_stats;
60 
61     // Stores the number of output tuple of condition trackers when it's bigger than
62     // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
63     // it means some data has been dropped. The map size is capped by kMaxConfigCount.
64     std::map<const int64_t, int> condition_stats;
65 
66     // Stores the number of output tuple of metric producers when it's bigger than
67     // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
68     // it means some data has been dropped. The map size is capped by kMaxConfigCount.
69     std::map<const int64_t, int> metric_stats;
70 
71     // Stores the max number of output tuple of dimensions in condition across dimensions in what
72     // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is
73     // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by
74     // kMaxConfigCount.
75     std::map<const int64_t, int> metric_dimension_in_condition_stats;
76 
77     // Stores the number of times an anomaly detection alert has been declared.
78     // The map size is capped by kMaxConfigCount.
79     std::map<const int64_t, int> alert_stats;
80 
81     // Stores the config ID for each sub-config used.
82     std::list<std::pair<const int64_t, const int32_t>> annotations;
83 };
84 
85 struct UidMapStats {
86     int32_t changes;
87     int32_t bytes_used;
88     int32_t dropped_changes;
89     int32_t deleted_apps = 0;
90 };
91 
92 // Keeps track of stats of statsd.
93 // Single instance shared across the process. All public methods are thread safe.
94 class StatsdStats {
95 public:
96     static StatsdStats& getInstance();
~StatsdStats()97     ~StatsdStats(){};
98 
99     const static int kDimensionKeySizeSoftLimit = 500;
100     const static int kDimensionKeySizeHardLimit = 800;
101 
102     // Per atom dimension key size limit
103     static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
104 
105     const static int kMaxConfigCountPerUid = 10;
106     const static int kMaxAlertCountPerConfig = 100;
107     const static int kMaxConditionCountPerConfig = 300;
108     const static int kMaxMetricCountPerConfig = 1000;
109     const static int kMaxMatcherCountPerConfig = 800;
110 
111     // The max number of old config stats we keep.
112     const static int kMaxIceBoxSize = 20;
113 
114     const static int kMaxLoggerErrors = 20;
115 
116     const static int kMaxSystemServerRestarts = 20;
117 
118     const static int kMaxTimestampCount = 20;
119 
120     const static int kMaxLogSourceCount = 50;
121 
122     // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
123     // drops the metrics data in memory.
124     static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
125 
126     // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
127     // data subscriber that it's time to call getData.
128     static const size_t kBytesPerConfigTriggerGetData = 192 * 1024;
129 
130     // Cap the UID map's memory usage to this. This should be fairly high since the UID information
131     // is critical for understanding the metrics.
132     const static size_t kMaxBytesUsedUidMap = 50 * 1024;
133 
134     // The number of deleted apps that are stored in the uid map.
135     const static int kMaxDeletedAppsInUidMap = 100;
136 
137     /* Minimum period between two broadcasts in nanoseconds. */
138     static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
139 
140     /* Min period between two checks of byte size per config key in nanoseconds. */
141     static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
142 
143     /* Minimum period between two activation broadcasts in nanoseconds. */
144     static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC;
145 
146     // Maximum age (30 days) that files on disk can exist in seconds.
147     static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
148 
149     // Maximum age (2 days) that local history files on disk can exist in seconds.
150     static const int kMaxLocalHistoryAgeSecond = 60 * 60 * 24 * 2;
151 
152     // Maximum number of files (1000) that can be in stats directory on disk.
153     static const int kMaxFileNumber = 1000;
154 
155     // Maximum size of all files that can be written to stats directory on disk.
156     static const int kMaxFileSize = 50 * 1024 * 1024;
157 
158     // How long to try to clear puller cache from last time
159     static const long kPullerCacheClearIntervalSec = 1;
160 
161     // Max time to do a pull.
162     static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
163 
164     // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
165     static const int kMaxNonPlatformPushedAtoms = 100;
166 
167     // Max platform atom tag number.
168     static const int32_t kMaxPlatformAtomTag = 100000;
169 
170     // Vendor pulled atom start id.
171     static const int32_t kVendorPulledAtomStartTag = 150000;
172 
173     // Beginning of range for timestamp truncation.
174     static const int32_t kTimestampTruncationStartTag = 300000;
175 
176     // End of range for timestamp truncation.
177     static const int32_t kTimestampTruncationEndTag = 304999;
178 
179     // Max accepted atom id.
180     static const int32_t kMaxAtomTag = 200000;
181 
182     static const int64_t kInt64Max = 0x7fffffffffffffffLL;
183 
184     /**
185      * Report a new config has been received and report the static stats about the config.
186      *
187      * The static stats include: the count of metrics, conditions, matchers, and alerts.
188      * If the config is not valid, this config stats will be put into icebox immediately.
189      */
190     void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
191                             int matchersCount, int alertCount,
192                             const std::list<std::pair<const int64_t, const int32_t>>& annotations,
193                             bool isValid);
194     /**
195      * Report a config has been removed.
196      */
197     void noteConfigRemoved(const ConfigKey& key);
198     /**
199      * Report a config has been reset when ttl expires.
200      */
201     void noteConfigReset(const ConfigKey& key);
202 
203     /**
204      * Report a broadcast has been sent to a config owner to collect the data.
205      */
206     void noteBroadcastSent(const ConfigKey& key);
207 
208     /**
209      * Report that a config has become activated or deactivated.
210      * This can be different from whether or not a broadcast is sent if the
211      * guardrail prevented the broadcast from being sent.
212      */
213     void noteActiveStatusChanged(const ConfigKey& key, bool activate);
214 
215     /**
216      * Report a config's metrics data has been dropped.
217      */
218     void noteDataDropped(const ConfigKey& key, const size_t totalBytes);
219 
220     /**
221      * Report metrics data report has been sent.
222      *
223      * The report may be requested via StatsManager API, or through adb cmd.
224      */
225     void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes);
226 
227     /**
228      * Report the size of output tuple of a condition.
229      *
230      * Note: only report when the condition has an output dimension, and the tuple
231      * count > kDimensionKeySizeSoftLimit.
232      *
233      * [key]: The config key that this condition belongs to.
234      * [id]: The id of the condition.
235      * [size]: The output tuple size.
236      */
237     void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size);
238 
239     /**
240      * Report the size of output tuple of a metric.
241      *
242      * Note: only report when the metric has an output dimension, and the tuple
243      * count > kDimensionKeySizeSoftLimit.
244      *
245      * [key]: The config key that this metric belongs to.
246      * [id]: The id of the metric.
247      * [size]: The output tuple size.
248      */
249     void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
250 
251     /**
252      * Report the max size of output tuple of dimension in condition across dimensions in what.
253      *
254      * Note: only report when the metric has an output dimension in condition, and the max tuple
255      * count > kDimensionKeySizeSoftLimit.
256      *
257      * [key]: The config key that this metric belongs to.
258      * [id]: The id of the metric.
259      * [size]: The output tuple size.
260      */
261     void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
262 
263     /**
264      * Report a matcher has been matched.
265      *
266      * [key]: The config key that this matcher belongs to.
267      * [id]: The id of the matcher.
268      */
269     void noteMatcherMatched(const ConfigKey& key, const int64_t& id);
270 
271     /**
272      * Report that an anomaly detection alert has been declared.
273      *
274      * [key]: The config key that this alert belongs to.
275      * [id]: The id of the alert.
276      */
277     void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id);
278 
279     /**
280      * Report an atom event has been logged.
281      */
282     void noteAtomLogged(int atomId, int32_t timeSec);
283 
284     /**
285      * Report that statsd modified the anomaly alarm registered with StatsCompanionService.
286      */
287     void noteRegisteredAnomalyAlarmChanged();
288 
289     /**
290      * Report that statsd modified the periodic alarm registered with StatsCompanionService.
291      */
292     void noteRegisteredPeriodicAlarmChanged();
293 
294     /**
295      * Records the number of delta entries that are being dropped from the uid map.
296      */
297     void noteUidMapDropped(int deltas);
298 
299     /**
300      * Records that an app was deleted (from statsd's map).
301      */
302     void noteUidMapAppDeletionDropped();
303 
304     /**
305      * Updates the number of changes currently stored in the uid map.
306      */
307     void setUidMapChanges(int changes);
308     void setCurrentUidMapMemory(int bytes);
309 
310     /*
311      * Updates minimum interval between pulls for an pulled atom.
312      */
313     void updateMinPullIntervalSec(int pullAtomId, long intervalSec);
314 
315     /*
316      * Notes an atom is pulled.
317      */
318     void notePull(int pullAtomId);
319 
320     /*
321      * Notes an atom is served from puller cache.
322      */
323     void notePullFromCache(int pullAtomId);
324 
325     /*
326      * Notify data error for pulled atom.
327      */
328     void notePullDataError(int pullAtomId);
329 
330     /*
331      * Records time for actual pulling, not including those served from cache and not including
332      * statsd processing delays.
333      */
334     void notePullTime(int pullAtomId, int64_t pullTimeNs);
335 
336     /*
337      * Records pull delay for a pulled atom, including those served from cache and including statsd
338      * processing delays.
339      */
340     void notePullDelay(int pullAtomId, int64_t pullDelayNs);
341 
342     /*
343      * Records pull exceeds timeout for the puller.
344      */
345     void notePullTimeout(int pullAtomId);
346 
347     /*
348      * Records pull exceeds max delay for a metric.
349      */
350     void notePullExceedMaxDelay(int pullAtomId);
351 
352     /*
353      * Records when system server restarts.
354      */
355     void noteSystemServerRestart(int32_t timeSec);
356 
357     /**
358      * Records statsd skipped an event.
359      */
360     void noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError,
361                      int32_t lastAtomTag, int32_t uid, int32_t pid);
362 
363     /**
364      * Records that the pull of an atom has failed
365      */
366     void notePullFailed(int atomId);
367 
368     /**
369      * Records that the pull of StatsCompanionService atom has failed
370      */
371     void noteStatsCompanionPullFailed(int atomId);
372 
373     /**
374      * Records that the pull of a StatsCompanionService atom has failed due to a failed binder
375      * transaction. This can happen when StatsCompanionService returns too
376      * much data (the max Binder parcel size is 1MB)
377      */
378     void noteStatsCompanionPullBinderTransactionFailed(int atomId);
379 
380     /**
381      * A pull with no data occurred
382      */
383     void noteEmptyData(int atomId);
384 
385     /**
386      * Records that a puller callback for the given atomId was registered or unregistered.
387      *
388      * @param registered True if the callback was registered, false if was unregistered.
389      */
390     void notePullerCallbackRegistrationChanged(int atomId, bool registered);
391 
392     /**
393      * Hard limit was reached in the cardinality of an atom
394      */
395     void noteHardDimensionLimitReached(int64_t metricId);
396 
397     /**
398      * A log event was too late, arrived in the wrong bucket and was skipped
399      */
400     void noteLateLogEventSkipped(int64_t metricId);
401 
402     /**
403      * Buckets were skipped as time elapsed without any data for them
404      */
405     void noteSkippedForwardBuckets(int64_t metricId);
406 
407     /**
408      * An unsupported value type was received
409      */
410     void noteBadValueType(int64_t metricId);
411 
412     /**
413      * Buckets were dropped due to reclaim memory.
414      */
415     void noteBucketDropped(int64_t metricId);
416 
417     /**
418      * A condition change was too late, arrived in the wrong bucket and was skipped
419      */
420     void noteConditionChangeInNextBucket(int64_t metricId);
421 
422     /**
423      * A bucket has been tagged as invalid.
424      */
425     void noteInvalidatedBucket(int64_t metricId);
426 
427     /**
428      * Tracks the total number of buckets (include skipped/invalid buckets).
429      */
430     void noteBucketCount(int64_t metricId);
431 
432     /**
433      * For pulls at bucket boundaries, it represents the misalignment between the real timestamp and
434      * the end of the bucket.
435      */
436     void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs);
437 
438     /**
439      * Number of buckets with unknown condition.
440      */
441     void noteBucketUnknownCondition(int64_t metricId);
442 
443     /* Reports one event has been dropped due to queue overflow, and the oldest event timestamp in
444      * the queue */
445     void noteEventQueueOverflow(int64_t oldestEventTimestampNs);
446 
447     /**
448      * Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast
449      * should have been sent, but instead was skipped due to hitting the guardrail.
450      */
451      void noteActivationBroadcastGuardrailHit(const int uid);
452 
453     /**
454      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
455      * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
456      * to collect stats after reset() has been called.
457      */
458     void reset();
459 
460     /**
461      * Output the stats in protobuf binary format to [buffer].
462      *
463      * [reset]: whether to clear the historical stats after the call.
464      */
465     void dumpStats(std::vector<uint8_t>* buffer, bool reset);
466 
467     /**
468      * Output statsd stats in human readable format to [out] file descriptor.
469      */
470     void dumpStats(int outFd) const;
471 
472     typedef struct {
473         long totalPull = 0;
474         long totalPullFromCache = 0;
475         long minPullIntervalSec = LONG_MAX;
476         int64_t avgPullTimeNs = 0;
477         int64_t maxPullTimeNs = 0;
478         long numPullTime = 0;
479         int64_t avgPullDelayNs = 0;
480         int64_t maxPullDelayNs = 0;
481         long numPullDelay = 0;
482         long dataError = 0;
483         long pullTimeout = 0;
484         long pullExceedMaxDelay = 0;
485         long pullFailed = 0;
486         long statsCompanionPullFailed = 0;
487         long statsCompanionPullBinderTransactionFailed = 0;
488         long emptyData = 0;
489         long registeredCount = 0;
490         long unregisteredCount = 0;
491     } PulledAtomStats;
492 
493     typedef struct {
494         long hardDimensionLimitReached = 0;
495         long lateLogEventSkipped = 0;
496         long skippedForwardBuckets = 0;
497         long badValueType = 0;
498         long conditionChangeInNextBucket = 0;
499         long invalidatedBucket = 0;
500         long bucketDropped = 0;
501         int64_t minBucketBoundaryDelayNs = 0;
502         int64_t maxBucketBoundaryDelayNs = 0;
503         long bucketUnknownCondition = 0;
504         long bucketCount = 0;
505     } AtomMetricStats;
506 
507 private:
508     StatsdStats();
509 
510     mutable std::mutex mLock;
511 
512     int32_t mStartTimeSec;
513 
514     // Track the number of dropped entries used by the uid map.
515     UidMapStats mUidMapStats;
516 
517     // The stats about the configs that are still in use.
518     // The map size is capped by kMaxConfigCount.
519     std::map<const ConfigKey, std::shared_ptr<ConfigStats>> mConfigStats;
520 
521     // Stores the stats for the configs that are no longer in use.
522     // The size of the vector is capped by kMaxIceBoxSize.
523     std::list<const std::shared_ptr<ConfigStats>> mIceBox;
524 
525     // Stores the number of times a pushed atom is logged.
526     // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
527     // out of that range will be put in mNonPlatformPushedAtomStats.
528     // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
529     std::vector<int> mPushedAtomStats;
530 
531     // Stores the number of times a pushed atom is logged for atom ids above kMaxPushedAtomId.
532     // The max size of the map is kMaxNonPlatformPushedAtoms.
533     std::unordered_map<int, int> mNonPlatformPushedAtomStats;
534 
535     // Maps PullAtomId to its stats. The size is capped by the puller atom counts.
536     std::map<int, PulledAtomStats> mPulledAtomStats;
537 
538     // Maps metric ID to its stats. The size is capped by the number of metrics.
539     std::map<int64_t, AtomMetricStats> mAtomMetricStats;
540 
541     // Maps uids to times when the activation changed broadcast not sent due to hitting the
542     // guardrail. The size is capped by the number of configs, and up to 20 times per uid.
543     std::map<int, std::list<int32_t>> mActivationBroadcastGuardrailStats;
544 
545     struct LogLossStats {
LogLossStatsLogLossStats546         LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid,
547                      int32_t pid)
548             : mWallClockSec(sec),
549               mCount(count),
550               mLastError(error),
551               mLastTag(tag),
552               mUid(uid),
553               mPid(pid) {
554         }
555         int32_t mWallClockSec;
556         int32_t mCount;
557         // error code defined in linux/errno.h
558         int32_t mLastError;
559         int32_t mLastTag;
560         int32_t mUid;
561         int32_t mPid;
562     };
563 
564     // Max of {(now - oldestEventTimestamp) when overflow happens}.
565     // This number is helpful to understand how SLOW statsd can be.
566     int64_t mMaxQueueHistoryNs = 0;
567 
568     // Min of {(now - oldestEventTimestamp) when overflow happens}.
569     // This number is helpful to understand how FAST the events floods to statsd.
570     int64_t mMinQueueHistoryNs = kInt64Max;
571 
572     // Total number of events that are lost due to queue overflow.
573     int32_t mOverflowCount = 0;
574 
575     // Timestamps when we detect log loss, and the number of logs lost.
576     std::list<LogLossStats> mLogLossStats;
577 
578     std::list<int32_t> mSystemServerRestartSec;
579 
580     // Stores the number of times statsd modified the anomaly alarm registered with
581     // StatsCompanionService.
582     int mAnomalyAlarmRegisteredStats = 0;
583 
584     // Stores the number of times statsd registers the periodic alarm changes
585     int mPeriodicAlarmRegisteredStats = 0;
586 
587     void noteConfigResetInternalLocked(const ConfigKey& key);
588 
589     void noteConfigRemovedInternalLocked(const ConfigKey& key);
590 
591     void resetInternalLocked();
592 
593     void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec);
594 
595     void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec);
596 
597     void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
598 
599     void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec);
600 
601     void noteActivationBroadcastGuardrailHit(const int uid, int32_t timeSec);
602 
603     void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
604 
605     /**
606      * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
607      * will live as long as `this`.
608      */
609     StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
610 
611     FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
612     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
613     FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
614     FRIEND_TEST(StatsdStatsTest, TestSubStats);
615     FRIEND_TEST(StatsdStatsTest, TestAtomLog);
616     FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog);
617     FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
618     FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
619     FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
620     FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
621     FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
622     FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
623 };
624 
625 }  // namespace statsd
626 }  // namespace os
627 }  // namespace android
628