diff options
-rw-r--r-- | statsd/src/guardrail/StatsdStats.cpp | 105 | ||||
-rw-r--r-- | statsd/src/guardrail/StatsdStats.h | 34 | ||||
-rw-r--r-- | statsd/src/stats_log.proto | 26 | ||||
-rw-r--r-- | statsd/tests/guardrail/StatsdStats_test.cpp | 80 | ||||
-rw-r--r-- | tests/Android.bp | 2 | ||||
-rw-r--r-- | tests/AndroidTest.xml | 6 | ||||
-rw-r--r-- | tests/apps/atomstormapp/Android.bp | 64 | ||||
-rw-r--r-- | tests/apps/atomstormapp/AndroidManifest.xml | 29 | ||||
-rw-r--r-- | tests/apps/atomstormapp/AndroidManifest2.xml | 29 | ||||
-rw-r--r-- | tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java | 48 | ||||
-rw-r--r-- | tests/src/android/cts/statsd/metadata/MetadataTests.java | 49 |
11 files changed, 445 insertions, 27 deletions
diff --git a/statsd/src/guardrail/StatsdStats.cpp b/statsd/src/guardrail/StatsdStats.cpp index d659de7d..939d39fa 100644 --- a/statsd/src/guardrail/StatsdStats.cpp +++ b/statsd/src/guardrail/StatsdStats.cpp @@ -61,6 +61,7 @@ const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS = 20; const int FIELD_ID_SHARD_OFFSET = 21; const int FIELD_ID_STATSD_STATS_ID = 22; const int FIELD_ID_SUBSCRIPTION_STATS = 23; +const int FIELD_ID_SOCKET_LOSS_STATS = 24; const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CALLING_UID = 1; const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_ID = 2; @@ -158,6 +159,25 @@ const int FIELD_ID_UID_MAP_DELETED_APPS = 4; const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1; const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2; +// SocketLossStats +const int FIELD_ID_SOCKET_LOSS_STATS_PER_UID = 1; +const int FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS = 2; + +// for LossStatsOverflowCounters proto +const int FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_UID = 1; +const int FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_COUNT = 2; + +// for LossStatsPerUid proto +const int FIELD_ID_SOCKET_LOSS_STATS_UID = 1; +const int FIELD_ID_SOCKET_LOSS_STATS_FIRST_TIMESTAMP_NANOS = 2; +const int FIELD_ID_SOCKET_LOSS_STATS_LAST_TIMESTAMP_NANOS = 3; +const int FIELD_ID_SOCKET_LOSS_ATOM_ID_LOSS_STATS = 4; + +// for AtomIdLossStats proto +const int FIELD_ID_ATOM_ID_LOSS_STATS_ATOM_ID = 1; +const int FIELD_ID_ATOM_ID_LOSS_STATS_ERROR = 2; +const int FIELD_ID_ATOM_ID_LOSS_STATS_COUNT = 3; + // for RestrictedMetricStats proto const int FIELD_ID_RESTRICTED_STATS_METRIC_ID = 1; const int FIELD_ID_RESTRICTED_STATS_INSERT_ERROR = 2; @@ -353,25 +373,26 @@ void StatsdStats::noteAtomSocketLoss(const SocketLossInfo& lossInfo) { ALOGW("SocketLossEvent detected: %lld (firstLossTsNanos), %lld (lastLossTsNanos)", (long long)lossInfo.firstLossTsNanos, (long long)lossInfo.lastLossTsNanos); lock_guard<std::mutex> lock(mLock); + + if (mSocketLossStats.size() == kMaxSocketLossStatsSize) { + // erase the oldest record + mSocketLossStats.pop_front(); + } + mSocketLossStats.emplace_back(lossInfo.uid, lossInfo.firstLossTsNanos, + lossInfo.lastLossTsNanos); for (size_t i = 0; i < lossInfo.atomIds.size(); i++) { ALOGW("For uid %d atom %d was lost %d times with error %d", lossInfo.uid, lossInfo.atomIds[i], lossInfo.counts[i], lossInfo.errors[i]); - const auto lossInfoKey = - std::make_tuple(lossInfo.uid, lossInfo.atomIds[i], lossInfo.errors[i]); - auto counterIt = mSocketLossStats.find(lossInfoKey); - if (counterIt != mSocketLossStats.end()) { - counterIt->second += lossInfo.counts[i]; - } else if (mSocketLossStats.size() < kMaxSocketLossStatsSize) { - mSocketLossStats[lossInfoKey] = lossInfo.counts[i]; - } + mSocketLossStats.back().mLossCountPerErrorAtomId.emplace_back( + lossInfo.atomIds[i], lossInfo.errors[i], lossInfo.counts[i]); } if (lossInfo.overflowCounter > 0) { - auto overflowPerUid = mSocketLossStatsOverflowCounter.find(lossInfo.uid); - if (overflowPerUid != mSocketLossStatsOverflowCounter.end()) { + auto overflowPerUid = mSocketLossStatsOverflowCounters.find(lossInfo.uid); + if (overflowPerUid != mSocketLossStatsOverflowCounters.end()) { overflowPerUid->second += lossInfo.overflowCounter; - } else if (mSocketLossStatsOverflowCounter.size() < kMaxSocketLossStatsSize) { - mSocketLossStatsOverflowCounter[lossInfo.uid] = lossInfo.overflowCounter; + } else if (mSocketLossStatsOverflowCounters.size() < kMaxSocketLossStatsSize) { + mSocketLossStatsOverflowCounters[lossInfo.uid] = lossInfo.overflowCounter; } } } @@ -1060,7 +1081,7 @@ void StatsdStats::resetInternalLocked() { mActivationBroadcastGuardrailStats.clear(); mPushedAtomErrorStats.clear(); mSocketLossStats.clear(); - mSocketLossStatsOverflowCounter.clear(); + mSocketLossStatsOverflowCounters.clear(); mPushedAtomDropsStats.clear(); mRestrictedMetricQueryStats.clear(); mSubscriptionPullThreadWakeupCount = 0; @@ -1387,17 +1408,18 @@ void StatsdStats::dumpStats(int out) const { if (mSocketLossStats.size()) { dprintf(out, "********SocketLossStats stats***********\n"); for (const auto& loss : mSocketLossStats) { - const int32_t uid = std::get<0>(loss.first); - const int32_t tag = std::get<1>(loss.first); - const int32_t error = std::get<2>(loss.first); - dprintf(out, "Socket loss: %d (uid), %d (tag) %d (error), %d (count)\n", uid, tag, - error, loss.second); + dprintf(out, "Socket loss: %d (uid), first loss at %lld, last loss at %lld\n", + loss.mUid, (long long)loss.mFirstLossTsNanos, (long long)loss.mLastLossTsNanos); + for (const auto& counterInfo : loss.mLossCountPerErrorAtomId) { + dprintf(out, "\t\t %d (atomId) %d (error), %d (count)\n", counterInfo.mAtomId, + counterInfo.mError, counterInfo.mCount); + } } } - if (mSocketLossStatsOverflowCounter.size()) { - dprintf(out, "********mSocketLossStatsOverflowCounter stats***********\n"); - for (const auto& overflow : mSocketLossStatsOverflowCounter) { + if (mSocketLossStatsOverflowCounters.size()) { + dprintf(out, "********mSocketLossStatsOverflowCounters stats***********\n"); + for (const auto& overflow : mSocketLossStatsOverflowCounters) { dprintf(out, "Socket loss overflow for %d uid is %d times\n", overflow.first, overflow.second); } @@ -1843,6 +1865,47 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { mSubscriptionPullThreadWakeupCount, &proto); proto.end(token); + // libstatssocket specific stats + + const uint64_t socketLossStatsToken = + proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_STATS); + + // socket loss stats info per uid/error/atom id counter + for (const auto& perUidLossInfo : mSocketLossStats) { + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_STATS_PER_UID | + FIELD_COUNT_REPEATED); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_UID, perUidLossInfo.mUid); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_FIRST_TIMESTAMP_NANOS, + perUidLossInfo.mFirstLossTsNanos); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_LAST_TIMESTAMP_NANOS, + perUidLossInfo.mLastLossTsNanos); + for (const auto& counterInfo : perUidLossInfo.mLossCountPerErrorAtomId) { + uint64_t token = + proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_ATOM_ID_LOSS_STATS | + FIELD_COUNT_REPEATED); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ID_LOSS_STATS_ATOM_ID, + counterInfo.mAtomId); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ID_LOSS_STATS_ERROR, counterInfo.mError); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ID_LOSS_STATS_COUNT, counterInfo.mCount); + proto.end(token); + } + proto.end(token); + } + + // socket loss stats overflow counters + for (const auto& overflowInfo : mSocketLossStatsOverflowCounters) { + uint64_t token = + proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS | + FIELD_COUNT_REPEATED); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_UID, + overflowInfo.first); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_COUNT, + overflowInfo.second); + proto.end(token); + } + + proto.end(socketLossStatsToken); + output->clear(); proto.serializeToVector(output); diff --git a/statsd/src/guardrail/StatsdStats.h b/statsd/src/guardrail/StatsdStats.h index adf9c49b..c7ca8d0c 100644 --- a/statsd/src/guardrail/StatsdStats.h +++ b/statsd/src/guardrail/StatsdStats.h @@ -273,7 +273,7 @@ public: static const int kMaxPushedAtomErrorStatsSize = 100; // Maximum number of socket loss stats to track. - static const int kMaxSocketLossStatsSize = 100; + static const int kMaxSocketLossStatsSize = 50; // Maximum atom id value that we consider a platform pushed atom. // This should be updated once highest pushed atom id in atoms.proto approaches this value. @@ -855,15 +855,35 @@ private: std::map<int, int> mPushedAtomErrorStats; // Stores the number of times a pushed atom was lost due to socket error. - // Represents counter per uid per tag per error - // The max size of this map is kMaxSocketLossStatsSize. - std::map<std::tuple<int32_t, int32_t, int32_t>, int32_t> mSocketLossStats; + // Represents counter per uid per tag per error with indication when the loss event was observed + // first & last time. + struct SocketLossStats { + SocketLossStats(int32_t uid, int64_t firstLossTsNanos, int64_t lastLossTsNanos) + : mUid(uid), mFirstLossTsNanos(firstLossTsNanos), mLastLossTsNanos(lastLossTsNanos) { + } + + int32_t mUid; + int64_t mFirstLossTsNanos; + int64_t mLastLossTsNanos; + // atom loss count per error, atom id + struct AtomLossInfo { + AtomLossInfo(int32_t atomId, int32_t error, int32_t count) + : mAtomId(atomId), mError(error), mCount(count) { + } + int mAtomId; + int mError; + int mCount; + }; + std::vector<AtomLossInfo> mLossCountPerErrorAtomId; + }; + // The max size of this list is kMaxSocketLossStatsSize. + std::list<SocketLossStats> mSocketLossStats; - // Stores the number of times a pushed atom loss info was dropped to be added into stats + // Stores the number of times a pushed atom loss info was dropped from the stats // on libstatssocket side due to guardrail hit. // Represents counter per uid. // The max size of this map is kMaxSocketLossStatsSize. - std::map<int32_t, int32_t> mSocketLossStatsOverflowCounter; + std::map<int32_t, int32_t> mSocketLossStatsOverflowCounters; // Maps metric ID to its stats. The size is capped by the number of metrics. std::map<int64_t, AtomMetricStats> mAtomMetricStats; @@ -1019,6 +1039,8 @@ private: FRIEND_TEST(StatsdStatsTest, TestSubscriptionPullThreadWakeup); FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedMaxActiveSubscriptions); FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedRemoveFinishedSubscription); + FRIEND_TEST(StatsdStatsTest, TestSocketLossStats); + FRIEND_TEST(StatsdStatsTest, TestSocketLossStatsOverflowCounter); FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved); }; diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto index 972f760e..b0955467 100644 --- a/statsd/src/stats_log.proto +++ b/statsd/src/stats_log.proto @@ -656,6 +656,32 @@ message StatsdStatsReport { } optional SubscriptionStats subscription_stats = 23; + + message SocketLossStats { + message LossStatsPerUid { + message AtomIdLossStats { + optional int32 atom_id = 1; + optional int32 error = 2; + optional int32 count = 3; + } + optional int32 uid = 1; + optional int64 first_timestamp_nanos = 2; + optional int64 last_timestamp_nanos = 3; + repeated AtomIdLossStats atom_id_loss_stats = 4; + } + + repeated LossStatsPerUid loss_stats_per_uid = 1; + + // tracks overflow of stats container on the libstatssocket side per logging application + message LossStatsOverflowCounters { + optional int32 uid = 1; + optional int32 count = 2; + } + + repeated LossStatsOverflowCounters loss_stats_overflow_counters = 2; + } + + optional SocketLossStats socket_loss_stats = 24; } message AlertTriggerDetails { diff --git a/statsd/tests/guardrail/StatsdStats_test.cpp b/statsd/tests/guardrail/StatsdStats_test.cpp index 7835391e..6e61b620 100644 --- a/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/statsd/tests/guardrail/StatsdStats_test.cpp @@ -1010,6 +1010,86 @@ TEST(StatsdStatsTest, TestEnforceDimensionKeySizeLimit) { StatsdStats::kDimensionKeySizeHardLimitMax); } +TEST(StatsdStatsTest, TestSocketLossStats) { + StatsdStats stats; + + const int maxLossEvents = StatsdStats::kMaxSocketLossStatsSize; + + // Note maxLossEvents + 1 + for (int eventId = 0; eventId <= maxLossEvents; eventId++) { + SocketLossInfo info; + + info.uid = eventId; + info.firstLossTsNanos = 10 * eventId; + info.lastLossTsNanos = 10 * eventId + 1; + + info.atomIds.push_back(eventId * 10); + info.errors.push_back(eventId * 20); + info.counts.push_back(eventId * 30); + + stats.noteAtomSocketLoss(info); + } + + StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false); + + auto socketLossStats = report.socket_loss_stats(); + ASSERT_EQ(socketLossStats.loss_stats_per_uid().size(), maxLossEvents); + + for (int i = 0; i < socketLossStats.loss_stats_per_uid().size(); i++) { + const auto& info = report.socket_loss_stats().loss_stats_per_uid(i); + + // due to the very first one with id 0 is popped out from the list ids (index) start from 1 + const int index = i + 1; + + ASSERT_EQ(info.uid(), index); + ASSERT_EQ(info.first_timestamp_nanos(), 10 * index); + ASSERT_EQ(info.last_timestamp_nanos(), 10 * index + 1); + + ASSERT_EQ(info.atom_id_loss_stats().size(), 1); + + ASSERT_EQ(info.atom_id_loss_stats(0).atom_id(), index * 10); + ASSERT_EQ(info.atom_id_loss_stats(0).error(), index * 20); + ASSERT_EQ(info.atom_id_loss_stats(0).count(), index * 30); + } +} + +TEST(StatsdStatsTest, TestSocketLossStatsOverflowCounter) { + StatsdStats stats; + + const int uidsCount = 5; + const int lossEventCount = 5; + + for (int uid = 0; uid < uidsCount; uid++) { + for (int eventId = 0; eventId < lossEventCount; eventId++) { + SocketLossInfo info; + + info.uid = uid; + info.firstLossTsNanos = 10 * eventId; + info.lastLossTsNanos = 10 * eventId + 1; + // the counter value will be accumulated + info.overflowCounter = 1; + + info.atomIds.push_back(eventId * 10); + info.errors.push_back(eventId * 20); + info.counts.push_back(eventId * 30); + + stats.noteAtomSocketLoss(info); + } + } + StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false); + + auto socketLossStatsOverflowCounters = + report.socket_loss_stats().loss_stats_overflow_counters(); + ASSERT_EQ(socketLossStatsOverflowCounters.size(), uidsCount); + + for (int i = 0; i < socketLossStatsOverflowCounters.size(); i++) { + const auto& counters = report.socket_loss_stats().loss_stats_overflow_counters(i); + + ASSERT_EQ(counters.uid(), i); + ASSERT_EQ(counters.count(), lossEventCount); + } +} + TEST_P(StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap, TestGetAtomDimensionKeySizeLimits) { const auto& [atomId, defaultHardLimit] = GetParam(); EXPECT_EQ(StatsdStats::getAtomDimensionKeySizeLimits(atomId, defaultHardLimit), diff --git a/tests/Android.bp b/tests/Android.bp index 0ec80caf..e67a76c2 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -46,5 +46,7 @@ java_test_host { data: [ "**/*.pbtxt", ":CtsStatsdApp", + ":StatsdAtomStormApp", + ":StatsdAtomStormApp2", ], } diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml index e33c2a0e..781813a5 100644 --- a/tests/AndroidTest.xml +++ b/tests/AndroidTest.xml @@ -36,4 +36,10 @@ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" /> <option name="restore-settings" value="true" /> </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="StatsdAtomStormApp.apk" /> + <option name="test-file-name" value="StatsdAtomStormApp2.apk" /> + </target_preparer> </configuration> diff --git a/tests/apps/atomstormapp/Android.bp b/tests/apps/atomstormapp/Android.bp new file mode 100644 index 00000000..86e5953f --- /dev/null +++ b/tests/apps/atomstormapp/Android.bp @@ -0,0 +1,64 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "StatsdAtomStormApp", + defaults: ["cts_defaults"], + platform_apis: true, + min_sdk_version: "24", + srcs: [ + "src/**/*.java", + ], + libs: [ + "android.test.runner", + "junit", + "org.apache.http.legacy", + ], + privileged: true, + static_libs: [ + "ctstestrunner-axt", + "compatibility-device-util-axt", + "androidx.legacy_legacy-support-v4", + "androidx.test.rules", + ], + compile_multilib: "both", +} + +android_test_helper_app { + name: "StatsdAtomStormApp2", + manifest: "AndroidManifest2.xml", + defaults: ["cts_defaults"], + platform_apis: true, + min_sdk_version: "24", + srcs: [ + "src/**/*.java", + ], + libs: [ + "android.test.runner", + "junit", + "org.apache.http.legacy", + ], + privileged: true, + static_libs: [ + "ctstestrunner-axt", + "compatibility-device-util-axt", + "androidx.legacy_legacy-support-v4", + "androidx.test.rules", + ], + compile_multilib: "both", +} diff --git a/tests/apps/atomstormapp/AndroidManifest.xml b/tests/apps/atomstormapp/AndroidManifest.xml new file mode 100644 index 00000000..2993928f --- /dev/null +++ b/tests/apps/atomstormapp/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statsd.app.atomstorm"> + <!-- Using gms shared uid is necessary for the receivers to work. --> + + <!-- GTS started at 14; do not change minSdkVersion. --> + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.statsd.app.atomstorm" + android:label="CTS tests of android.os.statsd stats collection"> + </instrumentation> +</manifest> diff --git a/tests/apps/atomstormapp/AndroidManifest2.xml b/tests/apps/atomstormapp/AndroidManifest2.xml new file mode 100644 index 00000000..310397fd --- /dev/null +++ b/tests/apps/atomstormapp/AndroidManifest2.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statsd.app.atomstorm.copy"> + <!-- Using gms shared uid is necessary for the receivers to work. --> + + <!-- GTS started at 14; do not change minSdkVersion. --> + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.statsd.app.atomstorm.copy" + android:label="CTS tests of android.os.statsd stats collection"> + </instrumentation> +</manifest> diff --git a/tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java b/tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java new file mode 100644 index 00000000..ce4ced99 --- /dev/null +++ b/tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Google LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.statsd.app.atomstorm; + +import android.util.StatsLog; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +@MediumTest +@RunWith(AndroidJUnit4.class) +public class StatsdAtomStorm { + private static final int EventStormAtomsCount = 100000; + + /** Tests socket overflow. */ + @Test + public void testLogManyAtomsBackToBack() throws Exception { + // logging back to back many atoms to force socket overflow + performAtomStorm(EventStormAtomsCount); + // make pause to resolve socket overflow + Thread.sleep(100); + // give chance for libstatssocket send loss stats to statsd triggering successful logging + performAtomStorm(1); + } + + private void performAtomStorm(int iterations) { + // single atom logging takes ~2us excluding JNI interactions + for (int i = 0; i < iterations; i++) { + StatsLog.logStart(i); + StatsLog.logStop(i); + } + } +} diff --git a/tests/src/android/cts/statsd/metadata/MetadataTests.java b/tests/src/android/cts/statsd/metadata/MetadataTests.java index 6d834043..f912e81e 100644 --- a/tests/src/android/cts/statsd/metadata/MetadataTests.java +++ b/tests/src/android/cts/statsd/metadata/MetadataTests.java @@ -24,7 +24,11 @@ import com.android.os.AtomsProto.Atom; import com.android.os.StatsLog.StatsdStatsReport; import com.android.os.StatsLog.StatsdStatsReport.ConfigStats; import com.android.os.StatsLog.StatsdStatsReport.LogLossStats; +import com.android.os.StatsLog.StatsdStatsReport.SocketLossStats; +import com.android.os.StatsLog.StatsdStatsReport.SocketLossStats.LossStatsPerUid; +import com.android.os.StatsLog.StatsdStatsReport.SocketLossStats.LossStatsPerUid.AtomIdLossStats; import com.android.tradefed.log.LogUtil; +import java.util.HashSet; /** * Statsd Metadata tests. @@ -155,6 +159,51 @@ public class MetadataTests extends MetadataTestCase { assertThat(detectedLossEventForAppBreadcrumbAtom).isTrue(); assertThat(detectedLossEventForSystemServer).isFalse(); + + boolean detectedLossEventForAppBreadcrumbAtomViaSocketLossStats = false; + for (LossStatsPerUid lossStats : report.getSocketLossStats().getLossStatsPerUidList()) { + for (AtomIdLossStats atomLossStats : lossStats.getAtomIdLossStatsList()) { + if (atomLossStats.getAtomId() == Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) { + detectedLossEventForAppBreadcrumbAtomViaSocketLossStats = true; + } + } + } + + assertThat(detectedLossEventForAppBreadcrumbAtomViaSocketLossStats).isTrue(); + } + } + + /** Test libstatssocket logging queue atom id distribution collection */ + public void testAtomIdLossDistributionCollection() throws Exception { + if (!ApiLevelUtil.codenameEquals(getDevice(), "VanillaIceCream")) { + return; + } + + final String appTestApk = "StatsdAtomStormApp.apk"; + final String app2TestApk = "StatsdAtomStormApp2.apk"; + + final String appTestPkg = "com.android.statsd.app.atomstorm"; + final String app2TestPkg = "com.android.statsd.app.atomstorm.copy"; + + getDevice().uninstallPackage(appTestPkg); + getDevice().uninstallPackage(app2TestPkg); + installPackage(appTestApk, true); + installPackage(app2TestApk, true); + + // run reference test app with UID 1 + runDeviceTests(appTestPkg, null, null); + // run reference test app with UID 2 + runDeviceTests(app2TestPkg, null, null); + + StatsdStatsReport report = getStatsdStatsReport(); + assertThat(report).isNotNull(); + HashSet<Integer> reportedUids = new HashSet<Integer>(); + for (LossStatsPerUid lossStats : report.getSocketLossStats().getLossStatsPerUidList()) { + reportedUids.add(lossStats.getUid()); } + assertThat(reportedUids.size()).isGreaterThan(1); + + getDevice().uninstallPackage(appTestPkg); + getDevice().uninstallPackage(app2TestPkg); } } |