1// Copyright (C) 2017 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "src/guardrail/StatsdStats.h"
16#include "statslog.h"
17#include "tests/statsd_test_util.h"
18
19#include <gtest/gtest.h>
20#include <vector>
21
22#ifdef __ANDROID__
23
24namespace android {
25namespace os {
26namespace statsd {
27
28using std::vector;
29
30TEST(StatsdStatsTest, TestValidConfigAdd) {
31    StatsdStats stats;
32    ConfigKey key(0, 12345);
33    const int metricsCount = 10;
34    const int conditionsCount = 20;
35    const int matchersCount = 30;
36    const int alertsCount = 10;
37    stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
38                             true /*valid config*/);
39    vector<uint8_t> output;
40    stats.dumpStats(&output, false /*reset stats*/);
41
42    StatsdStatsReport report;
43    bool good = report.ParseFromArray(&output[0], output.size());
44    EXPECT_TRUE(good);
45    EXPECT_EQ(1, report.config_stats_size());
46    const auto& configReport = report.config_stats(0);
47    EXPECT_EQ(0, configReport.uid());
48    EXPECT_EQ(12345, configReport.id());
49    EXPECT_EQ(metricsCount, configReport.metric_count());
50    EXPECT_EQ(conditionsCount, configReport.condition_count());
51    EXPECT_EQ(matchersCount, configReport.matcher_count());
52    EXPECT_EQ(alertsCount, configReport.alert_count());
53    EXPECT_EQ(true, configReport.is_valid());
54    EXPECT_FALSE(configReport.has_deletion_time_sec());
55}
56
57TEST(StatsdStatsTest, TestInvalidConfigAdd) {
58    StatsdStats stats;
59    ConfigKey key(0, 12345);
60    const int metricsCount = 10;
61    const int conditionsCount = 20;
62    const int matchersCount = 30;
63    const int alertsCount = 10;
64    stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
65                             false /*bad config*/);
66    vector<uint8_t> output;
67    stats.dumpStats(&output, false);
68
69    StatsdStatsReport report;
70    bool good = report.ParseFromArray(&output[0], output.size());
71    EXPECT_TRUE(good);
72    EXPECT_EQ(1, report.config_stats_size());
73    const auto& configReport = report.config_stats(0);
74    // The invalid config should be put into icebox with a deletion time.
75    EXPECT_TRUE(configReport.has_deletion_time_sec());
76}
77
78TEST(StatsdStatsTest, TestConfigRemove) {
79    StatsdStats stats;
80    ConfigKey key(0, 12345);
81    const int metricsCount = 10;
82    const int conditionsCount = 20;
83    const int matchersCount = 30;
84    const int alertsCount = 10;
85    stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
86                             true);
87    vector<uint8_t> output;
88    stats.dumpStats(&output, false);
89    StatsdStatsReport report;
90    bool good = report.ParseFromArray(&output[0], output.size());
91    EXPECT_TRUE(good);
92    EXPECT_EQ(1, report.config_stats_size());
93    const auto& configReport = report.config_stats(0);
94    EXPECT_FALSE(configReport.has_deletion_time_sec());
95
96    stats.noteConfigRemoved(key);
97    stats.dumpStats(&output, false);
98    good = report.ParseFromArray(&output[0], output.size());
99    EXPECT_TRUE(good);
100    EXPECT_EQ(1, report.config_stats_size());
101    const auto& configReport2 = report.config_stats(0);
102    EXPECT_TRUE(configReport2.has_deletion_time_sec());
103}
104
105TEST(StatsdStatsTest, TestSubStats) {
106    StatsdStats stats;
107    ConfigKey key(0, 12345);
108    stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, true);
109
110    stats.noteMatcherMatched(key, StringToId("matcher1"));
111    stats.noteMatcherMatched(key, StringToId("matcher1"));
112    stats.noteMatcherMatched(key, StringToId("matcher2"));
113
114    stats.noteConditionDimensionSize(key, StringToId("condition1"), 250);
115    stats.noteConditionDimensionSize(key, StringToId("condition1"), 240);
116
117    stats.noteMetricDimensionSize(key, StringToId("metric1"), 201);
118    stats.noteMetricDimensionSize(key, StringToId("metric1"), 202);
119
120    stats.noteAnomalyDeclared(key, StringToId("alert1"));
121    stats.noteAnomalyDeclared(key, StringToId("alert1"));
122    stats.noteAnomalyDeclared(key, StringToId("alert2"));
123
124    // broadcast-> 2
125    stats.noteBroadcastSent(key);
126    stats.noteBroadcastSent(key);
127
128    // data drop -> 1
129    stats.noteDataDropped(key);
130
131    // dump report -> 3
132    stats.noteMetricsReportSent(key, 0);
133    stats.noteMetricsReportSent(key, 0);
134    stats.noteMetricsReportSent(key, 0);
135
136    vector<uint8_t> output;
137    stats.dumpStats(&output, true);  // Dump and reset stats
138    StatsdStatsReport report;
139    bool good = report.ParseFromArray(&output[0], output.size());
140    EXPECT_TRUE(good);
141    EXPECT_EQ(1, report.config_stats_size());
142    const auto& configReport = report.config_stats(0);
143    EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size());
144    EXPECT_EQ(1, configReport.data_drop_time_sec_size());
145    EXPECT_EQ(3, configReport.dump_report_time_sec_size());
146    EXPECT_EQ(3, configReport.dump_report_data_size_size());
147    EXPECT_EQ(1, configReport.annotation_size());
148    EXPECT_EQ(123, configReport.annotation(0).field_int64());
149    EXPECT_EQ(456, configReport.annotation(0).field_int32());
150
151    EXPECT_EQ(2, configReport.matcher_stats_size());
152    // matcher1 is the first in the list
153    if (configReport.matcher_stats(0).id() == StringToId("matcher1")) {
154        EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
155        EXPECT_EQ(1, configReport.matcher_stats(1).matched_times());
156        EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id());
157    } else {
158        // matcher1 is the second in the list.
159        EXPECT_EQ(1, configReport.matcher_stats(0).matched_times());
160        EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id());
161
162        EXPECT_EQ(2, configReport.matcher_stats(1).matched_times());
163        EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id());
164    }
165
166    EXPECT_EQ(2, configReport.alert_stats_size());
167    bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1");
168    EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id());
169    EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times());
170    EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id());
171    EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times());
172
173    EXPECT_EQ(1, configReport.condition_stats_size());
174    EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id());
175    EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
176
177    EXPECT_EQ(1, configReport.metric_stats_size());
178    EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id());
179    EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts());
180
181    // after resetting the stats, some new events come
182    stats.noteMatcherMatched(key, StringToId("matcher99"));
183    stats.noteConditionDimensionSize(key, StringToId("condition99"), 300);
184    stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270);
185    stats.noteAnomalyDeclared(key, StringToId("alert99"));
186
187    // now the config stats should only contain the stats about the new event.
188    stats.dumpStats(&output, false);
189    good = report.ParseFromArray(&output[0], output.size());
190    EXPECT_TRUE(good);
191    EXPECT_EQ(1, report.config_stats_size());
192    const auto& configReport2 = report.config_stats(0);
193    EXPECT_EQ(1, configReport2.matcher_stats_size());
194    EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id());
195    EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times());
196
197    EXPECT_EQ(1, configReport2.condition_stats_size());
198    EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id());
199    EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts());
200
201    EXPECT_EQ(1, configReport2.metric_stats_size());
202    EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id());
203    EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
204
205    EXPECT_EQ(1, configReport2.alert_stats_size());
206    EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id());
207    EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times());
208}
209
210TEST(StatsdStatsTest, TestAtomLog) {
211    StatsdStats stats;
212    time_t now = time(nullptr);
213    // old event, we get it from the stats buffer. should be ignored.
214    stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, 1000);
215
216    stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
217    stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
218    stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3);
219    // pulled event, should ignore
220    stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFER, now + 4);
221
222    vector<uint8_t> output;
223    stats.dumpStats(&output, false);
224    StatsdStatsReport report;
225    bool good = report.ParseFromArray(&output[0], output.size());
226    EXPECT_TRUE(good);
227
228    EXPECT_EQ(2, report.atom_stats_size());
229    bool sensorAtomGood = false;
230    bool dropboxAtomGood = false;
231
232    for (const auto& atomStats : report.atom_stats()) {
233        if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
234            sensorAtomGood = true;
235        }
236        if (atomStats.tag() == android::util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
237            dropboxAtomGood = true;
238        }
239    }
240
241    EXPECT_TRUE(dropboxAtomGood);
242    EXPECT_TRUE(sensorAtomGood);
243}
244
245
246TEST(StatsdStatsTest, TestAnomalyMonitor) {
247    StatsdStats stats;
248    stats.noteRegisteredAnomalyAlarmChanged();
249    stats.noteRegisteredAnomalyAlarmChanged();
250
251    vector<uint8_t> output;
252    stats.dumpStats(&output, false);
253    StatsdStatsReport report;
254    bool good = report.ParseFromArray(&output[0], output.size());
255    EXPECT_TRUE(good);
256
257    EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered());
258}
259
260TEST(StatsdStatsTest, TestTimestampThreshold) {
261    StatsdStats stats;
262    vector<int32_t> timestamps;
263    for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
264        timestamps.push_back(i);
265    }
266    ConfigKey key(0, 12345);
267    stats.noteConfigReceived(key, 2, 3, 4, 5, {}, true);
268
269    for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
270        stats.noteDataDropped(key, timestamps[i]);
271        stats.noteBroadcastSent(key, timestamps[i]);
272        stats.noteMetricsReportSent(key, 0, timestamps[i]);
273    }
274
275    int32_t newTimestamp = 10000;
276
277    // now it should trigger removing oldest timestamp
278    stats.noteDataDropped(key, 10000);
279    stats.noteBroadcastSent(key, 10000);
280    stats.noteMetricsReportSent(key, 0, 10000);
281
282    EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end());
283    const auto& configStats = stats.mConfigStats[key];
284
285    size_t maxCount = StatsdStats::kMaxTimestampCount;
286    EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
287    EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size());
288    EXPECT_EQ(maxCount, configStats->dump_report_stats.size());
289
290    // the oldest timestamp is the second timestamp in history
291    EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
292    EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
293    EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
294
295    // the last timestamp is the newest timestamp.
296    EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
297    EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
298    EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
299}
300
301TEST(StatsdStatsTest, TestSystemServerCrash) {
302    StatsdStats stats;
303    vector<int32_t> timestamps;
304    for (int i = 0; i < StatsdStats::kMaxSystemServerRestarts; i++) {
305        timestamps.push_back(i);
306        stats.noteSystemServerRestart(timestamps[i]);
307    }
308    vector<uint8_t> output;
309    stats.dumpStats(&output, false);
310    StatsdStatsReport report;
311    EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
312    const int maxCount = StatsdStats::kMaxSystemServerRestarts;
313    EXPECT_EQ(maxCount, (int)report.system_restart_sec_size());
314
315    stats.noteSystemServerRestart(StatsdStats::kMaxSystemServerRestarts + 1);
316    output.clear();
317    stats.dumpStats(&output, false);
318    EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
319    EXPECT_EQ(maxCount, (int)report.system_restart_sec_size());
320    EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1));
321}
322
323}  // namespace statsd
324}  // namespace os
325}  // namespace android
326#else
327GTEST_LOG_(INFO) << "This test does nothing.\n";
328#endif
329