Merge "UseV2 soft memory calculations" into main
diff --git a/statsd/src/guardrail/StatsdStats.cpp b/statsd/src/guardrail/StatsdStats.cpp
index 2d96ce3..16354b7 100644
--- a/statsd/src/guardrail/StatsdStats.cpp
+++ b/statsd/src/guardrail/StatsdStats.cpp
@@ -63,6 +63,7 @@
 const int FIELD_ID_SUBSCRIPTION_STATS = 23;
 const int FIELD_ID_SOCKET_LOSS_STATS = 24;
 const int FIELD_ID_QUEUE_STATS = 25;
+const int FIELD_ID_SOCKET_READ_STATS = 26;
 
 const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CALLING_UID = 1;
 const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_ID = 2;
@@ -201,13 +202,17 @@
 const int FIELD_ID_PER_SUBSCRIPTION_STATS_END_TIME = 5;
 const int FIELD_ID_PER_SUBSCRIPTION_STATS_FLUSH_COUNT = 6;
 
+// Socket read stats
+const int FIELD_ID_SOCKET_READ_STATS_BATCHED_READ_SIZE = 1;
+
 const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
         {util::BINDER_CALLS, {6000, 10000}},
         {util::LOOPER_STATS, {1500, 2500}},
         {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
 };
 
-StatsdStats::StatsdStats() : mStatsdStatsId(rand()) {
+StatsdStats::StatsdStats()
+    : mStatsdStatsId(rand()), mSocketBatchReadHistogram(kNumBinsInSocketBatchReadHistogram) {
     mPushedAtomStats.resize(kMaxPushedAtomId + 1);
     mStartTimeSec = getWallClockSec();
 }
@@ -293,6 +298,28 @@
     mLogLossStats.emplace_back(wallClockTimeSec, count, lastError, lastTag, uid, pid);
 }
 
+void StatsdStats::noteBatchSocketRead(int32_t size) {
+    // Calculate the bin.
+    int bin = 0;
+    if (size < 0) {
+        ALOGE("Unexpected negative size read from socket. This should never happen");
+        bin = 0;
+    } else if (size < 5) {
+        bin = size;  // bin = [0,4].
+    } else if (size < 10) {
+        bin = 4 + (size / 5);  // bin = 5.
+    } else if (size < 100) {
+        bin = 5 + (size / 10);  // bin = [6,14].
+    } else if (size < 1000) {
+        bin = 14 + (size / 100);  // bin = [15-23].
+    } else if (size < 2000) {
+        bin = 19 + (size / 200);  // bin = [24-28].
+    } else {                      // 2000+
+        bin = 29;
+    }
+    lock_guard<std::mutex> lock(mLock);
+    mSocketBatchReadHistogram[bin] += 1;
+}
 void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
     noteBroadcastSent(key, getWallClockSec());
 }
@@ -1112,6 +1139,7 @@
     mPushedAtomDropsStats.clear();
     mRestrictedMetricQueryStats.clear();
     mSubscriptionPullThreadWakeupCount = 0;
+    std::fill(mSocketBatchReadHistogram.begin(), mSocketBatchReadHistogram.end(), 0);
 
     for (auto it = mSubscriptionStats.begin(); it != mSubscriptionStats.end();) {
         if (it->second.end_time_sec > 0) {
@@ -1429,6 +1457,28 @@
                 (long long)restart);
     }
 
+    dprintf(out, "********Socket batch read size stats***********\n");
+    for (int i = 0; i < kNumBinsInSocketBatchReadHistogram; i++) {
+        if (mSocketBatchReadHistogram[i] == 0) {
+            continue;
+        }
+        string range;
+        if (i < 5) {
+            range = "[" + to_string(i) + "]";
+        } else if (i == 5) {
+            range = "[5-9]";
+        } else if (i < 15) {
+            range = "[" + to_string(i - 5) + "0-" + to_string(i - 5) + "9]";
+        } else if (i < 24) {
+            range = "[" + to_string(i - 14) + "00-" + to_string(i - 14) + "99]";
+        } else if (i < 29) {
+            range = "[" + to_string((i - 19) * 2) + "00-" + to_string((i - 19) * 2 + 1) + "99]";
+        } else {
+            range = "[2000+]";
+        }
+        dprintf(out, "%s: %lld\n", range.c_str(), (long long)mSocketBatchReadHistogram[i]);
+    }
+
     for (const auto& loss : mLogLossStats) {
         dprintf(out,
                 "Log loss: %lld (wall clock sec) - %d (count), %d (last error), %d (last tag), %d "
@@ -1508,6 +1558,7 @@
     dprintf(out, "Statsd Stats Id %d\n", mStatsdStatsId);
     dprintf(out, "********Shard Offset Provider stats***********\n");
     dprintf(out, "Shard Offset: %u\n", ShardOffsetProvider::getInstance().getShardOffset());
+    dprintf(out, "\n");
 }
 
 void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) {
@@ -1949,6 +2000,16 @@
 
     proto.end(socketLossStatsToken);
 
+    // Socket batch read stats.
+    const uint64_t socketReadStatsToken =
+            proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_READ_STATS);
+    for (const auto& it : mSocketBatchReadHistogram) {
+        proto.write(FIELD_TYPE_INT64 | FIELD_ID_SOCKET_READ_STATS_BATCHED_READ_SIZE |
+                            FIELD_COUNT_REPEATED,
+                    it);
+    }
+    proto.end(socketReadStatsToken);
+
     output->clear();
     proto.serializeToVector(output);
 
diff --git a/statsd/src/guardrail/StatsdStats.h b/statsd/src/guardrail/StatsdStats.h
index 106eed8..03266ee 100644
--- a/statsd/src/guardrail/StatsdStats.h
+++ b/statsd/src/guardrail/StatsdStats.h
@@ -307,6 +307,8 @@
 
     static const int32_t kMaxLoggedBucketDropEvents = 10;
 
+    static const int32_t kNumBinsInSocketBatchReadHistogram = 30;
+
     /**
      * Report a new config has been received and report the static stats about the config.
      *
@@ -717,6 +719,8 @@
      */
     void noteSubscriptionPullThreadWakeup();
 
+    void noteBatchSocketRead(int32_t readSize);
+
     /**
      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
      * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
@@ -947,6 +951,8 @@
 
     std::list<int32_t> mSystemServerRestartSec;
 
+    std::vector<int64_t> mSocketBatchReadHistogram;
+
     struct RestrictedMetricQueryStats {
         RestrictedMetricQueryStats(int32_t callingUid, int64_t configId,
                                    const string& configPackage, std::optional<int32_t> configUid,
@@ -1066,6 +1072,7 @@
     FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
     FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
     FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
+    FRIEND_TEST(StatsdStatsTest, TestSocketBatchReadStats);
 };
 
 InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason,
diff --git a/statsd/src/socket/StatsSocketListener.cpp b/statsd/src/socket/StatsSocketListener.cpp
index 7f0eb43..c9f8b89 100644
--- a/statsd/src/socket/StatsSocketListener.cpp
+++ b/statsd/src/socket/StatsSocketListener.cpp
@@ -64,41 +64,45 @@
     };
 
     const int socket = cli->getSocket();
-
-    // To clear the entire buffer is secure/safe, but this contributes to 1.68%
-    // overhead under logging load. We are safe because we check counts, but
-    // still need to clear null terminator
-    // memset(buffer, 0, sizeof(buffer));
-    ssize_t n = recvmsg(socket, &hdr, 0);
-    if (n <= (ssize_t)(sizeof(android_log_header_t))) {
-        return false;
-    }
-
-    buffer[n] = 0;
-
-    struct ucred* cred = NULL;
-
-    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-    while (cmsg != NULL) {
-        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
-            cred = (struct ucred*)CMSG_DATA(cmsg);
-            break;
+    int i = 0;
+    ssize_t n = 0;
+    while (n = recvmsg(socket, &hdr, MSG_DONTWAIT), n > 0) {
+        // To clear the entire buffer is secure/safe, but this contributes to 1.68%
+        // overhead under logging load. We are safe because we check counts, but
+        // still need to clear null terminator.
+        // Note that the memset, if needed, should happen before each read in the while loop.
+        // memset(buffer, 0, sizeof(buffer));
+        if (n <= (ssize_t)(sizeof(android_log_header_t))) {
+            return false;
         }
-        cmsg = CMSG_NXTHDR(&hdr, cmsg);
+        buffer[n] = 0;
+        i++;
+
+        struct ucred* cred = NULL;
+
+        struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+        while (cmsg != NULL) {
+            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+                cred = (struct ucred*)CMSG_DATA(cmsg);
+                break;
+            }
+            cmsg = CMSG_NXTHDR(&hdr, cmsg);
+        }
+
+        struct ucred fake_cred;
+        if (cred == NULL) {
+            cred = &fake_cred;
+            cred->pid = 0;
+            cred->uid = DEFAULT_OVERFLOWUID;
+        }
+
+        const uint32_t uid = cred->uid;
+        const uint32_t pid = cred->pid;
+
+        processSocketMessage(buffer, n, uid, pid, *mQueue, *mLogEventFilter);
     }
 
-    struct ucred fake_cred;
-    if (cred == NULL) {
-        cred = &fake_cred;
-        cred->pid = 0;
-        cred->uid = DEFAULT_OVERFLOWUID;
-    }
-
-    const uint32_t uid = cred->uid;
-    const uint32_t pid = cred->pid;
-
-    processSocketMessage(buffer, n, uid, pid, *mQueue, *mLogEventFilter);
-
+    StatsdStats::getInstance().noteBatchSocketRead(i);
     return true;
 }
 
diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto
index 9d5a376..93b0d8c 100644
--- a/statsd/src/stats_log.proto
+++ b/statsd/src/stats_log.proto
@@ -698,6 +698,17 @@
     }
 
     optional EventQueueStats event_queue_stats = 25;
+
+    // Tracks info about reads from the socket.
+    message SocketReadStats {
+        // This is a histogram of sizes of the batched reads. The bins are as follows:
+        // [0, 1, 2, 3, 4, 5-9, 10-19, 20-29, 30-39, 40-49, 50-59, 60-69, 70-79, 80-89, 90-99,
+        //  100-199, 200-299, 300-399, 400-499, 500-599, 600-699, 700-799, 800-899, 900-999,
+        //  1000-1199, 1200-1399, 1400-1599, 1600-1799, 1800-1999, 2000+]
+        repeated int64 batched_read_size = 1;
+    }
+
+    optional SocketReadStats socket_read_stats = 26;
 }
 
 message AlertTriggerDetails {
diff --git a/statsd/tests/guardrail/StatsdStats_test.cpp b/statsd/tests/guardrail/StatsdStats_test.cpp
index a2da3c4..9232f5b 100644
--- a/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -1114,6 +1114,47 @@
     }
 }
 
+TEST(StatsdStatsTest, TestSocketBatchReadStats) {
+    StatsdStats stats;
+    stats.noteBatchSocketRead(1);        // bin 1
+    stats.noteBatchSocketRead(2);        // bin 2
+    stats.noteBatchSocketRead(2);        // bin 2
+    stats.noteBatchSocketRead(4);        // bin 4
+    stats.noteBatchSocketRead(5);        // bin 5
+    stats.noteBatchSocketRead(9);        // bin 5
+    stats.noteBatchSocketRead(9);        // bin 5
+    stats.noteBatchSocketRead(10);       // bin 6
+    stats.noteBatchSocketRead(19);       // bin 6
+    stats.noteBatchSocketRead(30);       // bin 8
+    stats.noteBatchSocketRead(32);       // bin 8
+    stats.noteBatchSocketRead(39);       // bin 8
+    stats.noteBatchSocketRead(90);       // bin 14
+    stats.noteBatchSocketRead(99);       // bin 14
+    stats.noteBatchSocketRead(100);      // bin 15
+    stats.noteBatchSocketRead(100);      // bin 15
+    stats.noteBatchSocketRead(199);      // bin 15
+    stats.noteBatchSocketRead(200);      // bin 16
+    stats.noteBatchSocketRead(299);      // bin 16
+    stats.noteBatchSocketRead(999);      // bin 23
+    stats.noteBatchSocketRead(1000);     // bin 24
+    stats.noteBatchSocketRead(1199);     // bin 24
+    stats.noteBatchSocketRead(1200);     // bin 25
+    stats.noteBatchSocketRead(1800);     // bin 28
+    stats.noteBatchSocketRead(1999);     // bin 28
+    stats.noteBatchSocketRead(2000);     // bin 29
+    stats.noteBatchSocketRead(1200000);  // bin 29
+
+    StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false);
+    EXPECT_THAT(report.socket_read_stats().batched_read_size(),
+                ElementsAre(0, 1, 2, 0, 1, 3, 2, 0, 3, 0, 0, 0, 0, 0, 2, 3, 2, 0, 0, 0, 0, 0, 0, 1,
+                            2, 1, 0, 0, 2, 2));
+
+    stats.reset();
+    report = getStatsdStatsReport(stats, /* reset stats */ false);
+    EXPECT_THAT(report.socket_read_stats().batched_read_size(),
+                AllOf(SizeIs(StatsdStats::kNumBinsInSocketBatchReadHistogram), Each(0)));
+}
+
 TEST_P(StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap, TestGetAtomDimensionKeySizeLimits) {
     const auto& [atomId, defaultHardLimit] = GetParam();
     EXPECT_EQ(StatsdStats::getAtomDimensionKeySizeLimits(atomId, defaultHardLimit),