summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVova Sharaienko <sharaienko@google.com>2024-03-04 22:09:39 +0000
committerVova Sharaienko <sharaienko@google.com>2024-04-18 17:07:07 +0000
commit573d2765c1a842add15bfcf6cf4e532969c50c80 (patch)
tree428bd364aa977dd54e6943edba2bc03d61d44e63
parentcf30969a7981517e1ab43bdb3b70f6d3135e242e (diff)
downloadStatsD-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.cpp15
-rw-r--r--statsd/src/StatsLogProcessor.h2
-rw-r--r--statsd/src/metrics/EventMetricProducer.cpp12
-rw-r--r--statsd/src/metrics/MetricProducer.cpp6
-rw-r--r--statsd/src/metrics/MetricProducer.h27
-rw-r--r--statsd/src/metrics/MetricsManager.cpp68
-rw-r--r--statsd/src/metrics/MetricsManager.h13
-rw-r--r--statsd/src/stats_log.proto2
-rw-r--r--statsd/src/stats_log_util.cpp12
-rw-r--r--statsd/src/stats_log_util.h3
-rw-r--r--statsd/tests/MetricsManager_test.cpp108
-rw-r--r--statsd/tests/metrics/EventMetricProducer_test.cpp101
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