diff options
author | Vova Sharaienko <sharaienko@google.com> | 2024-03-04 22:09:39 +0000 |
---|---|---|
committer | Vova Sharaienko <sharaienko@google.com> | 2024-04-18 17:07:07 +0000 |
commit | 573d2765c1a842add15bfcf6cf4e532969c50c80 (patch) | |
tree | 428bd364aa977dd54e6943edba2bc03d61d44e63 | |
parent | cf30969a7981517e1ab43bdb3b70f6d3135e242e (diff) | |
download | StatsD-573d2765c1a842add15bfcf6cf4e532969c50c80.tar.gz |
[statsd] Added data corruption reason propagation to EventMetric
BYPASS_INCLUSIVE_LANGUAGE_REASON=Usage of existing properties not relevant to be changed in scope of this CL
Bug: 312565736
Test: statsd_test
Change-Id: I492d7d20318bfd20c575b033985c3cbd7289de69
-rw-r--r-- | statsd/src/StatsLogProcessor.cpp | 15 | ||||
-rw-r--r-- | statsd/src/StatsLogProcessor.h | 2 | ||||
-rw-r--r-- | statsd/src/metrics/EventMetricProducer.cpp | 12 | ||||
-rw-r--r-- | statsd/src/metrics/MetricProducer.cpp | 6 | ||||
-rw-r--r-- | statsd/src/metrics/MetricProducer.h | 27 | ||||
-rw-r--r-- | statsd/src/metrics/MetricsManager.cpp | 68 | ||||
-rw-r--r-- | statsd/src/metrics/MetricsManager.h | 13 | ||||
-rw-r--r-- | statsd/src/stats_log.proto | 2 | ||||
-rw-r--r-- | statsd/src/stats_log_util.cpp | 12 | ||||
-rw-r--r-- | statsd/src/stats_log_util.h | 3 | ||||
-rw-r--r-- | statsd/tests/MetricsManager_test.cpp | 108 | ||||
-rw-r--r-- | statsd/tests/metrics/EventMetricProducer_test.cpp | 101 |
12 files changed, 337 insertions, 32 deletions
diff --git a/statsd/src/StatsLogProcessor.cpp b/statsd/src/StatsLogProcessor.cpp index 02356e83..9fe282a3 100644 --- a/statsd/src/StatsLogProcessor.cpp +++ b/statsd/src/StatsLogProcessor.cpp @@ -808,7 +808,9 @@ void StatsLogProcessor::onConfigMetricsReportLocked( } // Data corrupted reason - writeDataCorruptedReasons(tempProto); + writeDataCorruptedReasons(tempProto, FIELD_ID_DATA_CORRUPTED_REASON, + StatsdStats::getInstance().hasEventQueueOverflow(), + StatsdStats::getInstance().hasSocketLoss()); // Estimated memory bytes tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_DATA_BYTES, totalSize); @@ -1514,17 +1516,6 @@ void StatsLogProcessor::updateLogEventFilterLocked() const { mLogEventFilter->setAtomIds(std::move(allAtomIds), this); } -void StatsLogProcessor::writeDataCorruptedReasons(ProtoOutputStream& proto) { - if (StatsdStats::getInstance().hasEventQueueOverflow()) { - proto.write(FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED | FIELD_ID_DATA_CORRUPTED_REASON, - DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW); - } - if (StatsdStats::getInstance().hasSocketLoss()) { - proto.write(FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED | FIELD_ID_DATA_CORRUPTED_REASON, - DATA_CORRUPTED_SOCKET_LOSS); - } -} - bool StatsLogProcessor::validateAppBreadcrumbEvent(const LogEvent& event) const { if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) { // Check that app breadcrumb reported fields are valid. diff --git a/statsd/src/StatsLogProcessor.h b/statsd/src/StatsLogProcessor.h index b6d66af9..c5faaf25 100644 --- a/statsd/src/StatsLogProcessor.h +++ b/statsd/src/StatsLogProcessor.h @@ -327,8 +327,6 @@ private: /* Tells LogEventFilter about atom ids to parse */ void updateLogEventFilterLocked() const; - void writeDataCorruptedReasons(ProtoOutputStream& proto); - bool validateAppBreadcrumbEvent(const LogEvent& event) const; // Function used to send a broadcast so that receiver for the config key can call getData diff --git a/statsd/src/metrics/EventMetricProducer.cpp b/statsd/src/metrics/EventMetricProducer.cpp index a8f45391..648e21c9 100644 --- a/statsd/src/metrics/EventMetricProducer.cpp +++ b/statsd/src/metrics/EventMetricProducer.cpp @@ -49,6 +49,8 @@ const int FIELD_ID_ID = 1; const int FIELD_ID_EVENT_METRICS = 4; const int FIELD_ID_IS_ACTIVE = 14; const int FIELD_ID_ESTIMATED_MEMORY_BYTES = 18; +const int FIELD_ID_DATA_CORRUPTED_REASON = 19; + // for EventMetricDataWrapper const int FIELD_ID_DATA = 1; // for EventMetricData @@ -138,6 +140,8 @@ optional<InvalidConfigReason> EventMetricProducer::onConfigUpdatedLocked( void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) { mAggregatedAtoms.clear(); mTotalSize = 0; + mDataCorruptedDueToSocketLoss = false; + mDataCorruptedDueToQueueOverflow = false; StatsdStats::getInstance().noteBucketDropped(mMetricId); } @@ -165,6 +169,8 @@ std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& pr void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { mAggregatedAtoms.clear(); mTotalSize = 0; + mDataCorruptedDueToSocketLoss = false; + mDataCorruptedDueToQueueOverflow = false; } void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, @@ -177,6 +183,9 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked()); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_MEMORY_BYTES, (long long)byteSizeLocked()); + // Data corrupted reason + writeDataCorruptedReasons(*protoOutput, FIELD_ID_DATA_CORRUPTED_REASON, + mDataCorruptedDueToQueueOverflow, mDataCorruptedDueToSocketLoss); uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS); for (const auto& [atomDimensionKey, elapsedTimestampsNs] : mAggregatedAtoms) { @@ -197,10 +206,13 @@ void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, protoOutput->end(aggregatedToken); protoOutput->end(wrapperToken); } + protoOutput->end(protoToken); if (erase_data) { mAggregatedAtoms.clear(); mTotalSize = 0; + mDataCorruptedDueToSocketLoss = false; + mDataCorruptedDueToQueueOverflow = false; } } diff --git a/statsd/src/metrics/MetricProducer.cpp b/statsd/src/metrics/MetricProducer.cpp index 14c07a8a..ad2c8ccc 100644 --- a/statsd/src/metrics/MetricProducer.cpp +++ b/statsd/src/metrics/MetricProducer.cpp @@ -34,7 +34,6 @@ namespace android { namespace os { namespace statsd { - // for ActiveMetric const int FIELD_ID_ACTIVE_METRIC_ID = 1; const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2; @@ -187,6 +186,11 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo statePrimaryKeys); } +void MetricProducer::onMatchedLogEventLostLocked(int32_t /*atomId*/, DataCorruptedReason reason) { + mDataCorruptedDueToSocketLoss |= reason == DATA_CORRUPTED_SOCKET_LOSS; + mDataCorruptedDueToQueueOverflow |= reason == DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW; +} + bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { bool isActive = mEventActivationMap.empty(); for (auto& it : mEventActivationMap) { diff --git a/statsd/src/metrics/MetricProducer.h b/statsd/src/metrics/MetricProducer.h index 8bf3998d..d134425e 100644 --- a/statsd/src/metrics/MetricProducer.h +++ b/statsd/src/metrics/MetricProducer.h @@ -18,6 +18,7 @@ #define METRIC_PRODUCER_H #include <src/active_config_list.pb.h> +#include <src/guardrail/stats_log_enums.pb.h> #include <utils/RefBase.h> #include <unordered_map> @@ -209,6 +210,11 @@ public: onMatchedLogEventLocked(matcherIndex, event); } + void onMatchedLogEventLost(int32_t atomId, DataCorruptedReason reason) { + std::lock_guard<std::mutex> lock(mMutex); + onMatchedLogEventLostLocked(atomId, reason); + } + void onConditionChanged(const bool condition, int64_t eventTime) { std::lock_guard<std::mutex> lock(mMutex); onConditionChangedLocked(condition, eventTime); @@ -237,8 +243,8 @@ public: std::set<string> *str_set, android::util::ProtoOutputStream* protoOutput) { std::lock_guard<std::mutex> lock(mMutex); - return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data, - dumpLatency, str_set, protoOutput); + onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data, dumpLatency, + str_set, protoOutput); } virtual optional<InvalidConfigReason> onConfigUpdatedLocked( @@ -259,7 +265,7 @@ public: void clearPastBuckets(const int64_t dumpTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - return clearPastBucketsLocked(dumpTimeNs); + clearPastBucketsLocked(dumpTimeNs); } void prepareFirstBucket() { @@ -433,14 +439,14 @@ protected: // Consume the parsed stats log entry that already matched the "what" of the metric. virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); + virtual void onMatchedLogEventLostLocked(int32_t atomId, DataCorruptedReason reason); virtual void onConditionChangedLocked(const bool condition, int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) = 0; virtual void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, + const bool erase_data, const DumpLatency dumpLatency, + std::set<string>* str_set, android::util::ProtoOutputStream* protoOutput) = 0; virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0; virtual void prepareFirstBucketLocked(){}; @@ -587,6 +593,8 @@ protected: } wp<ConfigMetadataProvider> mConfigMetadataProvider; + bool mDataCorruptedDueToSocketLoss = false; + bool mDataCorruptedDueToQueueOverflow = false; FRIEND_TEST(CountMetricE2eTest, TestSlicedState); FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap); @@ -625,6 +633,9 @@ protected: FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges); + FRIEND_TEST(MetricsManagerTest, TestOnLogEventLossForAllowedFromAnyUidAtom); + FRIEND_TEST(MetricsManagerTest, TestOnLogEventLossForNotAllowedAtom); + FRIEND_TEST(MetricsManagerUtilTest, TestInitialConditions); FRIEND_TEST(MetricsManagerUtilTest, TestSampledMetrics); @@ -635,6 +646,10 @@ protected: FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes); FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts); + + FRIEND_TEST(EventMetricProducerTest, TestCorruptedDataReasonSocketLoss_OnDumpReport); + FRIEND_TEST(EventMetricProducerTest, TestCorruptedDataReasonSocketLoss_OnDropData); + FRIEND_TEST(EventMetricProducerTest, TestCorruptedDataReasonSocketLoss_OnClearPastBuckets); }; } // namespace statsd diff --git a/statsd/src/metrics/MetricsManager.cpp b/statsd/src/metrics/MetricsManager.cpp index 9b126408..1ab1e382 100644 --- a/statsd/src/metrics/MetricsManager.cpp +++ b/statsd/src/metrics/MetricsManager.cpp @@ -515,20 +515,19 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, const int64_t w VLOG("=========================Metric Reports End=========================="); } -bool MetricsManager::checkLogCredentials(const LogEvent& event) { - if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) { +bool MetricsManager::checkLogCredentials(const int32_t uid, const int32_t atomId) const { + if (mWhitelistedAtomIds.find(atomId) != mWhitelistedAtomIds.end()) { return true; } - if (event.GetUid() == AID_ROOT || - (event.GetUid() >= AID_SYSTEM && event.GetUid() < AID_SHELL)) { + if (uid == AID_ROOT || (uid >= AID_SYSTEM && uid < AID_SHELL)) { // enable atoms logged from pre-installed Android system services return true; } std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex); - if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) { - VLOG("log source %d not on the whitelist", event.GetUid()); + if (mAllowedLogSources.find(uid) == mAllowedLogSources.end()) { + VLOG("log source %d not on the whitelist", uid); return false; } return true; @@ -540,11 +539,24 @@ void MetricsManager::onLogEvent(const LogEvent& event) { return; } + const int tagId = event.GetTagId(); + + if (tagId == util::STATS_SOCKET_LOSS_REPORTED) { + // Hard coded logic to handle socket loss info to highlight metric corruption reason + // STATS_SOCKET_LOSS_REPORTED might not be part of atoms allow list - but some of lost + // atoms can be always allowed - that is the reason to evaluate SocketLossInfo content prior + // the checkLogCredentials below + const std::optional<SocketLossInfo>& lossInfo = toSocketLossInfo(event); + if (lossInfo) { + onLogEventLost(*lossInfo); + } + // next, atom is going to be propagated to be consumed by metrics if any + } + if (!checkLogCredentials(event)) { return; } - const int tagId = event.GetTagId(); const int64_t eventTimeNs = event.GetElapsedTimestampNs(); bool isActive = mIsAlwaysActive; @@ -703,6 +715,48 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } } +void MetricsManager::onLogEventLost(const SocketLossInfo& socketLossInfo) { + // socketLossInfo stores atomId per UID - to eliminate duplicates using set + const set<int> uniqueLostAtomIds(socketLossInfo.atomIds.begin(), socketLossInfo.atomIds.end()); + + // pass lost atom id to all relevant metrics + for (const auto lostAtomId : uniqueLostAtomIds) { + /** + * Socket loss atom: + * - comes from a specific uid (originUid) + * - specifies the uid in the atom payload (socketLossInfo.uid) + * - provides a list of atom ids that are lost + * + * For atom id that is lost (lostAtomId below): + * - if that atom id is allowed from any uid, then always count this atom as lost + * - else, if the originUid (from ucred) (socketLossInfo.uid below - is the same for all + * uniqueLostAtomIds) is in the allowed log sources - count this atom as lost + */ + + if (!checkLogCredentials(socketLossInfo.uid, lostAtomId)) { + continue; + } + + const auto matchersIt = mTagIdsToMatchersMap.find(lostAtomId); + if (matchersIt == mTagIdsToMatchersMap.end()) { + // atom is lost - but no metrics in config reference it + continue; + } + const auto& matchersIndexesListForLostAtom = matchersIt->second; + for (const auto matcherIndex : matchersIndexesListForLostAtom) { + auto it = mTrackerToMetricMap.find(matcherIndex); + if (it == mTrackerToMetricMap.end()) { + continue; + } + auto& metricsList = it->second; + for (const int metricIndex : metricsList) { + mAllMetricProducers[metricIndex]->onMatchedLogEventLost(lostAtomId, + DATA_CORRUPTED_SOCKET_LOSS); + } + } + } +} + void MetricsManager::onAnomalyAlarmFired( const int64_t timestampNs, unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) { diff --git a/statsd/src/metrics/MetricsManager.h b/statsd/src/metrics/MetricsManager.h index 3b28b6a3..22d5ecf1 100644 --- a/statsd/src/metrics/MetricsManager.h +++ b/statsd/src/metrics/MetricsManager.h @@ -57,8 +57,6 @@ public: // Return whether the configuration is valid. bool isConfigValid() const; - bool checkLogCredentials(const LogEvent& event); - virtual void onLogEvent(const LogEvent& event); void onAnomalyAlarmFired( @@ -325,6 +323,12 @@ private: std::vector<int> mMetricIndexesWithActivation; + inline bool checkLogCredentials(const LogEvent& event) const { + return checkLogCredentials(event.GetUid(), event.GetTagId()); + } + + bool checkLogCredentials(int32_t uid, int32_t atomId) const; + void initAllowedLogSources(); void initPullAtomSources(); @@ -365,6 +369,8 @@ private: // metrics. void setTriggerGetDataBytesFromConfig(const StatsdConfig& config); + void onLogEventLost(const SocketLossInfo& socketLossInfo); + // The memory limit in bytes for storing metrics size_t mMaxMetricsBytes; @@ -406,7 +412,10 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); FRIEND_TEST(MetricsManagerTest, TestLogSources); + FRIEND_TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom); FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate); + FRIEND_TEST(MetricsManagerTest, TestOnLogEventLossForAllowedFromAnyUidAtom); + FRIEND_TEST(MetricsManagerTest, TestOnLogEventLossForNotAllowedAtom); FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfig); FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfigUpdate); FRIEND_TEST(MetricsManagerUtilTest, TestSampledMetrics); diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto index 09ba6a87..ff219519 100644 --- a/statsd/src/stats_log.proto +++ b/statsd/src/stats_log.proto @@ -320,6 +320,8 @@ message StatsLogReport { optional int64 estimated_data_bytes = 18; + repeated DataCorruptedReason data_corrupted_reason = 19; + // Do not use. reserved 13, 15; } diff --git a/statsd/src/stats_log_util.cpp b/statsd/src/stats_log_util.cpp index 6d473447..f7c9996b 100644 --- a/statsd/src/stats_log_util.cpp +++ b/statsd/src/stats_log_util.cpp @@ -554,6 +554,18 @@ void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetr protoOutput->end(token); } +void writeDataCorruptedReasons(ProtoOutputStream& proto, int fieldIdDataCorruptedReason, + bool hasQueueOverflow, bool hasSocketLoss) { + if (hasQueueOverflow) { + proto.write(FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED | fieldIdDataCorruptedReason, + DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW); + } + if (hasSocketLoss) { + proto.write(FIELD_TYPE_INT32 | FIELD_COUNT_REPEATED | fieldIdDataCorruptedReason, + DATA_CORRUPTED_SOCKET_LOSS); + } +} + int64_t getElapsedRealtimeNs() { return ::android::elapsedRealtimeNano(); } diff --git a/statsd/src/stats_log_util.h b/statsd/src/stats_log_util.h index 16cdb35c..5aa4c0b5 100644 --- a/statsd/src/stats_log_util.h +++ b/statsd/src/stats_log_util.h @@ -92,6 +92,9 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair, ProtoOutputStream *protoOutput); +void writeDataCorruptedReasons(ProtoOutputStream& proto, int fieldIdDataCorruptedReason, + bool hasQueueOverflow, bool hasSocketLoss); + template<class T> bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) { std::string pbBytes; diff --git a/statsd/tests/MetricsManager_test.cpp b/statsd/tests/MetricsManager_test.cpp index d86e9757..a45fa247 100644 --- a/statsd/tests/MetricsManager_test.cpp +++ b/statsd/tests/MetricsManager_test.cpp @@ -32,6 +32,7 @@ #include "src/state/StateManager.h" #include "src/statsd_config.pb.h" #include "statsd_test_util.h" +#include "statslog_statsdtest.h" using namespace testing; using android::sp; @@ -53,10 +54,12 @@ const ConfigKey kConfigKey(0, kConfigId); const long timeBaseSec = 1000; -StatsdConfig buildGoodRestrictedConfig() { +StatsdConfig buildEventConfig(bool isRestricted) { StatsdConfig config; config.set_id(kConfigId); - config.set_restricted_metrics_delegate_package_name("delegate"); + if (isRestricted) { + config.set_restricted_metrics_delegate_package_name("delegate"); + } AtomMatcher* eventMatcher = config.add_atom_matcher(); eventMatcher->set_id(StringToId("SCREEN_IS_ON")); @@ -69,6 +72,14 @@ StatsdConfig buildGoodRestrictedConfig() { return config; } +StatsdConfig buildGoodRestrictedConfig() { + return buildEventConfig(/*isRestricted*/ true); +} + +StatsdConfig buildGoodEventConfig() { + return buildEventConfig(/*isRestricted*/ false); +} + set<int32_t> unionSet(const vector<set<int32_t>> sets) { set<int32_t> toRet; for (const set<int32_t>& s : sets) { @@ -76,6 +87,36 @@ set<int32_t> unionSet(const vector<set<int32_t>> sets) { } return toRet; } + +SocketLossInfo createSocketLossInfo(int32_t uid, int32_t atomId) { + SocketLossInfo lossInfo; + lossInfo.uid = uid; + lossInfo.errors.push_back(-11); + lossInfo.atomIds.push_back(atomId); + lossInfo.counts.push_back(1); + return lossInfo; +} + +// helper API to create STATS_SOCKET_LOSS_REPORTED LogEvent +LogEvent createSocketLossInfoLogEvent(int32_t uid, int32_t lossAtomId) { + const SocketLossInfo lossInfo = createSocketLossInfo(uid, lossAtomId); + + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, util::STATS_SOCKET_LOSS_REPORTED); + AStatsEvent_writeInt32(statsEvent, lossInfo.uid); + AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true); + AStatsEvent_writeInt64(statsEvent, lossInfo.firstLossTsNanos); + AStatsEvent_writeInt64(statsEvent, lossInfo.lastLossTsNanos); + AStatsEvent_writeInt32(statsEvent, lossInfo.overflowCounter); + AStatsEvent_writeInt32Array(statsEvent, lossInfo.errors.data(), lossInfo.errors.size()); + AStatsEvent_writeInt32Array(statsEvent, lossInfo.atomIds.data(), lossInfo.atomIds.size()); + AStatsEvent_writeInt32Array(statsEvent, lossInfo.counts.data(), lossInfo.counts.size()); + + LogEvent event(uid /* uid */, 0 /* pid */); + parseStatsEventToLogEvent(statsEvent, &event); + return event; +} + } // anonymous namespace TEST(MetricsManagerTest, TestLogSources) { @@ -299,6 +340,69 @@ TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) { EXPECT_TRUE(metricsManager.checkLogCredentials(event)); } +TEST(MetricsManagerTest, TestOnLogEventLossForAllowedFromAnyUidAtom) { + sp<UidMap> uidMap; + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> periodicAlarmMonitor; + + StatsdConfig config = buildGoodEventConfig(); + config.add_whitelisted_atom_ids(2); + + MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, + pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); + + const int32_t customAppUid = AID_APP_START + 1; + LogEvent eventOfInterest(customAppUid /* uid */, 0 /* pid */); + CreateNoValuesLogEvent(&eventOfInterest, 2 /* atom id */, 0 /* timestamp */); + EXPECT_TRUE(metricsManager.checkLogCredentials(eventOfInterest)); + EXPECT_FALSE(metricsManager.mAllMetricProducers[0]->mDataCorruptedDueToSocketLoss); + + LogEvent eventSocketLossReported = createSocketLossInfoLogEvent(customAppUid, 2); + + // the STATS_SOCKET_LOSS_REPORTED on its own will not be propagated/consumed by any metric + EXPECT_FALSE(metricsManager.checkLogCredentials(eventSocketLossReported)); + + // the loss info for an atom of interest (2) will be evaluated even when + // STATS_SOCKET_LOSS_REPORTED atom is not explicitly in allowed list + metricsManager.onLogEvent(eventSocketLossReported); + + // check that corresponding event metric was updated with loss info + // the invariant is there is only one metric in the config + EXPECT_TRUE(metricsManager.mAllMetricProducers[0]->mDataCorruptedDueToSocketLoss); +} + +TEST(MetricsManagerTest, TestOnLogEventLossForNotAllowedAtom) { + sp<UidMap> uidMap; + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> periodicAlarmMonitor; + + StatsdConfig config = buildGoodEventConfig(); + + MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap, + pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor); + + const int32_t customAppUid = AID_APP_START + 1; + LogEvent eventOfInterest(customAppUid /* uid */, 0 /* pid */); + CreateNoValuesLogEvent(&eventOfInterest, 2 /* atom id */, 0 /* timestamp */); + EXPECT_FALSE(metricsManager.checkLogCredentials(eventOfInterest)); + EXPECT_FALSE(metricsManager.mAllMetricProducers[0]->mDataCorruptedDueToSocketLoss); + + LogEvent eventSocketLossReported = createSocketLossInfoLogEvent(customAppUid, 2); + + // the STATS_SOCKET_LOSS_REPORTED on its own will not be propagated/consumed by any metric + EXPECT_FALSE(metricsManager.checkLogCredentials(eventSocketLossReported)); + + // the loss info for an atom of interest (2) will not be evaluated since atom of interest does + // not pass credential check + metricsManager.onLogEvent(eventSocketLossReported); + + // check that corresponding event metric was not updated with loss info + // the invariant is there is only one metric in the config + EXPECT_FALSE(metricsManager.mAllMetricProducers[0]->mDataCorruptedDueToSocketLoss); +} + TEST(MetricsManagerTest, TestWhitelistedAtomStateTracker) { sp<UidMap> uidMap; sp<StatsPullerManager> pullerManager = new StatsPullerManager(); diff --git a/statsd/tests/metrics/EventMetricProducer_test.cpp b/statsd/tests/metrics/EventMetricProducer_test.cpp index 6e25f791..a789f3ba 100644 --- a/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -53,6 +53,7 @@ void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, strin parseStatsEventToLogEvent(statsEvent, logEvent); } + } // anonymous namespace class EventMetricProducerTest : public ::testing::Test { @@ -350,6 +351,106 @@ TEST_F(EventMetricProducerTest, TestTwoAtomTagAggregatedEvents) { } } +TEST_F(EventMetricProducerTest, TestCorruptedDataReasonSocketLoss_OnDumpReport) { + int64_t bucketStartTimeNs = 10000000000; + int tagId = 1; + + EventMetric metric; + metric.set_id(1); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111"); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false); + EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, protoHash, bucketStartTimeNs, provider); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + + EXPECT_FALSE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); + + eventProducer.onMatchedLogEventLost(tagId, DATA_CORRUPTED_SOCKET_LOSS); + + EXPECT_TRUE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); + + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 50, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + ASSERT_EQ(1, report.event_metrics().data_size()); + ASSERT_EQ(1, report.data_corrupted_reason_size()); + ASSERT_EQ(DATA_CORRUPTED_SOCKET_LOSS, report.data_corrupted_reason()[0]); + + EXPECT_FALSE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); +} + +TEST_F(EventMetricProducerTest, TestCorruptedDataReasonSocketLoss_OnDropData) { + int64_t bucketStartTimeNs = 10000000000; + int tagId = 1; + + EventMetric metric; + metric.set_id(1); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111"); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false); + EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, protoHash, bucketStartTimeNs, provider); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + + EXPECT_FALSE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); + + eventProducer.onMatchedLogEventLost(tagId, DATA_CORRUPTED_SOCKET_LOSS); + + EXPECT_TRUE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); + + eventProducer.dropData(bucketStartTimeNs + 100); + + EXPECT_FALSE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); +} + +TEST_F(EventMetricProducerTest, TestCorruptedDataReasonSocketLoss_OnClearPastBuckets) { + int64_t bucketStartTimeNs = 10000000000; + int tagId = 1; + + EventMetric metric; + metric.set_id(1); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111"); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false); + EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, + wizard, protoHash, bucketStartTimeNs, provider); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + + EXPECT_FALSE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); + + eventProducer.onMatchedLogEventLost(tagId, DATA_CORRUPTED_SOCKET_LOSS); + + EXPECT_TRUE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); + + eventProducer.clearPastBuckets(bucketStartTimeNs + 100); + + EXPECT_FALSE(eventProducer.mDataCorruptedDueToSocketLoss); + EXPECT_FALSE(eventProducer.mDataCorruptedDueToQueueOverflow); +} + } // namespace statsd } // namespace os } // namespace android |