logd: separate LogStatistics from LogBuffer

LogStatistics is intertwined with LogBuffer, even relying on it for
thread safety.  This needs to change to have a proper
LogBufferInterface, so this CL separates them.  Specifically:

1) Adding a lock to LogStatistics and adding thread annotations to
   ensure that data structures are protected appropriately.
2) Moving prune_rows calculation into LogStatistics so it is done
   while holding this lock.
3) Using LogStatistics instead of LogBuffer where appropriate.

Note that there should not be a significant performance regression
with this lock, as it will almost always been uncontended.  If
anything, it should alleviate pressure from LogBuffer's lock.

Test: logging unit tests
Change-Id: I9d6dde2c96c9f024fa0341711c7bc63379e8e406
diff --git a/logd/Android.bp b/logd/Android.bp
index ea1054d..3df59f5 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -54,6 +54,7 @@
 
     cflags: [
         "-Wextra",
+        "-Wthread-safety",
     ] + event_flag,
 }
 
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 4044dc9..87402ac 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "CommandListener.h"
+
 #include <arpa/inet.h>
 #include <ctype.h>
 #include <dirent.h>
@@ -35,12 +37,11 @@
 #include <private/android_filesystem_config.h>
 #include <sysutils/SocketClient.h>
 
-#include "CommandListener.h"
-#include "LogCommand.h"
 #include "LogUtils.h"
 
-CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
-    : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune,
+                                 LogStatistics* stats)
+    : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune), stats_(stats) {
     registerCmd(new ClearCmd(this));
     registerCmd(new GetBufSizeCmd(this));
     registerCmd(new SetBufSizeCmd(this));
@@ -148,7 +149,7 @@
         return 0;
     }
 
-    unsigned long size = buf()->getSizeUsed((log_id_t)id);
+    unsigned long size = stats()->Sizes((log_id_t)id);
     char buf[512];
     snprintf(buf, sizeof(buf), "%lu", size);
     cli->sendMsg(buf);
@@ -209,7 +210,7 @@
         }
     }
 
-    cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
+    cli->sendMsg(PackageString(stats()->Format(uid, pid, logMask)).c_str());
     return 0;
 }
 
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index c90c247..fd934f7 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -22,12 +22,13 @@
 #include "LogCommand.h"
 #include "LogListener.h"
 #include "LogReader.h"
+#include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogWhiteBlackList.h"
 
 class CommandListener : public FrameworkListener {
   public:
-    CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+    CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune, LogStatistics* log_statistics);
     virtual ~CommandListener() {}
 
   private:
@@ -36,20 +37,22 @@
     LogBuffer* buf_;
     LogTags* tags_;
     PruneList* prune_;
+    LogStatistics* stats_;
 
-#define LogCmd(name, command_string)                            \
-    class name##Cmd : public LogCommand {                       \
-      public:                                                   \
-        explicit name##Cmd(CommandListener* parent)             \
-            : LogCommand(#command_string), parent_(parent) {}   \
-        virtual ~name##Cmd() {}                                 \
-        int runCommand(SocketClient* c, int argc, char** argv); \
-                                                                \
-      private:                                                  \
-        LogBuffer* buf() const { return parent_->buf_; }        \
-        LogTags* tags() const { return parent_->tags_; }        \
-        PruneList* prune() const { return parent_->prune_; }    \
-        CommandListener* parent_;                               \
+#define LogCmd(name, command_string)                             \
+    class name##Cmd : public LogCommand {                        \
+      public:                                                    \
+        explicit name##Cmd(CommandListener* parent)              \
+            : LogCommand(#command_string), parent_(parent) {}    \
+        virtual ~name##Cmd() {}                                  \
+        int runCommand(SocketClient* c, int argc, char** argv);  \
+                                                                 \
+      private:                                                   \
+        LogBuffer* buf() const { return parent_->buf_; }         \
+        LogTags* tags() const { return parent_->tags_; }         \
+        PruneList* prune() const { return parent_->prune_; }     \
+        LogStatistics* stats() const { return parent_->stats_; } \
+        CommandListener* parent_;                                \
     }
 
     LogCmd(Clear, clear);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index bcc187d..6c42a28 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogAudit.h"
+
 #include <ctype.h>
 #include <endian.h>
 #include <errno.h>
@@ -34,8 +36,6 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "LogAudit.h"
-#include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogReader.h"
 #include "LogUtils.h"
@@ -45,16 +45,15 @@
     '<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
         '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
 
-LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg)
+LogAudit::LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg, LogStatistics* stats)
     : SocketListener(getLogSocket(), false),
       logbuf(buf),
       reader(reader),
       fdDmesg(fdDmesg),
-      main(__android_logger_property_get_bool("ro.logd.auditd.main",
-                                              BOOL_DEFAULT_TRUE)),
-      events(__android_logger_property_get_bool("ro.logd.auditd.events",
-                                                BOOL_DEFAULT_TRUE)),
-      initialized(false) {
+      main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
+      events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+      initialized(false),
+      stats_(stats) {
     static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
                                            'l',
                                            'o',
@@ -211,9 +210,7 @@
             ++cp;
         }
         tid = pid;
-        logbuf->wrlock();
-        uid = logbuf->pidToUid(pid);
-        logbuf->unlock();
+        uid = stats_->PidToUid(pid);
         memmove(pidptr, cp, strlen(cp) + 1);
     }
 
@@ -301,9 +298,7 @@
         pid = tid;
         comm = "auditd";
     } else {
-        logbuf->wrlock();
-        comm = commfree = logbuf->pidToName(pid);
-        logbuf->unlock();
+        comm = commfree = stats_->PidToName(pid);
         if (!comm) {
             comm = "unknown";
         }
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 7df0a5d..ee6e579 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_AUDIT_H__
-#define _LOGD_LOG_AUDIT_H__
+#pragma once
 
 #include <map>
 
 #include <sysutils/SocketListener.h>
 
 #include "LogBuffer.h"
+#include "LogStatistics.h"
 
 class LogReader;
 
@@ -33,14 +33,14 @@
     bool events;
     bool initialized;
 
-   public:
-    LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg);
+  public:
+    LogAudit(LogBuffer* buf, LogReader* reader, int fdDmesg, LogStatistics* stats);
     int log(char* buf, size_t len);
 
-   protected:
+  protected:
     virtual bool onDataAvailable(SocketClient* cli);
 
-   private:
+  private:
     static int getLogSocket();
     std::map<std::string, std::string> populateDenialMap();
     std::string denialParse(const std::string& denial, char terminator,
@@ -48,6 +48,6 @@
     void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
     int logPrint(const char* fmt, ...)
         __attribute__((__format__(__printf__, 2, 3)));
-};
 
-#endif
+    LogStatistics* stats_;
+};
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index ad391e5..4fce751 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -63,8 +63,8 @@
     LogReaderThread::unlock();
 }
 
-LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
-    : mTimes(*times), tags_(tags), prune_(prune) {
+LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune, LogStatistics* stats)
+    : mTimes(*times), tags_(tags), prune_(prune), stats_(stats) {
     pthread_rwlock_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
@@ -197,9 +197,7 @@
     }
     if (!__android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE)) {
         // Log traffic received to total
-        wrlock();
-        stats.addTotal(elem);
-        unlock();
+        stats_->AddTotal(elem);
         delete elem;
         return -EACCES;
     }
@@ -308,7 +306,7 @@
                         unlock();
                         return len;
                     }
-                    stats.addTotal(currentLast);
+                    stats_->AddTotal(currentLast);
                     delete currentLast;
                     swab = total;
                     event->payload.data = htole32(swab);
@@ -324,7 +322,7 @@
                 }
             }
             if (count) {
-                stats.addTotal(currentLast);
+                stats_->AddTotal(currentLast);
                 currentLast->setDropped(count);
             }
             droppedElements[log_id] = currentLast;
@@ -355,31 +353,15 @@
 // assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
 void LogBuffer::log(LogBufferElement* elem) {
     mLogElements.push_back(elem);
-    stats.add(elem);
+    stats_->Add(elem);
     maybePrune(elem->getLogId());
 }
 
-// Prune at most 10% of the log entries or maxPrune, whichever is less.
-//
 // LogBuffer::wrlock() must be held when this function is called.
 void LogBuffer::maybePrune(log_id_t id) {
-    size_t sizes = stats.sizes(id);
-    unsigned long maxSize = log_buffer_size(id);
-    if (sizes > maxSize) {
-        size_t sizeOver = sizes - ((maxSize * 9) / 10);
-        size_t elements = stats.realElements(id);
-        size_t minElements = elements / 100;
-        if (minElements < minPrune) {
-            minElements = minPrune;
-        }
-        unsigned long pruneRows = elements * sizeOver / sizes;
-        if (pruneRows < minElements) {
-            pruneRows = minElements;
-        }
-        if (pruneRows > maxPrune) {
-            pruneRows = maxPrune;
-        }
-        prune(id, pruneRows);
+    unsigned long prune_rows;
+    if (stats_->ShouldPrune(id, log_buffer_size(id), &prune_rows)) {
+        prune(id, prune_rows);
     }
 }
 
@@ -452,9 +434,9 @@
     }
 #endif
     if (coalesce) {
-        stats.erase(element);
+        stats_->Erase(element);
     } else {
-        stats.subtract(element);
+        stats_->Subtract(element);
     }
     delete element;
 
@@ -535,7 +517,7 @@
 // If the selected reader is blocking our pruning progress, decide on
 // what kind of mitigation is necessary to unblock the situation.
 void LogBuffer::kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows) {
-    if (stats.sizes(id) > (2 * log_buffer_size(id))) {  // +100%
+    if (stats_->Sizes(id) > (2 * log_buffer_size(id))) {  // +100%
         // A misbehaving or slow reader has its connection
         // dropped if we hit too much memory pressure.
         android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
@@ -664,13 +646,13 @@
             size_t threshold = log_buffer_size(id) / 8;
 
             if (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) {
-                stats.WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
+                stats_->WorstTwoTags(threshold, &worst, &worst_sizes, &second_worst_sizes);
                 // per-pid filter for AID_SYSTEM sources is too complex
             } else {
-                stats.WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
+                stats_->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
 
                 if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
-                    stats.WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
+                    stats_->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
                 }
             }
         }
@@ -819,7 +801,7 @@
             if (leading) {
                 it = erase(it);
             } else {
-                stats.drop(element);
+                stats_->Drop(element);
                 element->setDropped(1);
                 if (last.coalesce(element, 1)) {
                     it = erase(it, true);
@@ -952,14 +934,6 @@
     return busy;
 }
 
-// get the used space associated with "id".
-unsigned long LogBuffer::getSizeUsed(log_id_t id) {
-    rdlock();
-    size_t retval = stats.sizes(id);
-    unlock();
-    return retval;
-}
-
 // set the total space allocated to "id"
 int LogBuffer::setSize(log_id_t id, unsigned long size) {
     // Reasonable limits ...
@@ -1044,7 +1018,7 @@
         unlock();
 
         // range locking in LastLogTimes looks after us
-        curr = element->flushTo(reader, this, sameTid);
+        curr = element->flushTo(reader, stats_, sameTid);
 
         if (curr == element->FLUSH_ERROR) {
             return curr;
@@ -1056,14 +1030,3 @@
 
     return curr;
 }
-
-std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
-                                        unsigned int logMask) {
-    wrlock();
-
-    std::string ret = stats.format(uid, pid, logMask);
-
-    unlock();
-
-    return ret;
-}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 3c45667..3c1ea5a 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -38,8 +38,6 @@
     LogBufferElementCollection mLogElements;
     pthread_rwlock_t mLogElementsLock;
 
-    LogStatistics stats;
-
     // watermark of any worst/chatty uid processing
     typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
         LogBufferIteratorMap;
@@ -58,7 +56,7 @@
    public:
     LastLogTimes& mTimes;
 
-    LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
+    LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune, LogStatistics* stats);
     ~LogBuffer();
     void init();
 
@@ -75,22 +73,8 @@
     bool clear(log_id_t id, uid_t uid = AID_ROOT);
     unsigned long getSize(log_id_t id);
     int setSize(log_id_t id, unsigned long size);
-    unsigned long getSizeUsed(log_id_t id);
 
-    std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
-
-    void enableStatistics() {
-        stats.enableStatistics();
-    }
-
-    // helper must be protected directly or implicitly by wrlock()/unlock()
-    const char* pidToName(pid_t pid) {
-        return stats.pidToName(pid);
-    }
-    uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
-    const char* uidToName(uid_t uid) {
-        return stats.uidToName(uid);
-    }
+  private:
     void wrlock() {
         pthread_rwlock_wrlock(&mLogElementsLock);
     }
@@ -101,10 +85,6 @@
         pthread_rwlock_unlock(&mLogElementsLock);
     }
 
-   private:
-    static constexpr size_t minPrune = 4;
-    static constexpr size_t maxPrune = 256;
-
     void maybePrune(log_id_t id);
     void kickMe(LogReaderThread* me, log_id_t id, unsigned long pruneRows);
 
@@ -118,6 +98,7 @@
 
     LogTags* tags_;
     PruneList* prune_;
+    LogStatistics* stats_;
 
     // Keeps track of the iterator to the oldest log message of a given log type, as an
     // optimization when pruning logs.  Use GetOldest() to retrieve.
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 916ed42..cc68ba4 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogBufferElement.h"
+
 #include <ctype.h>
 #include <endian.h>
 #include <fcntl.h>
@@ -26,7 +28,6 @@
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
-#include "LogBufferElement.h"
 #include "LogCommand.h"
 #include "LogReader.h"
 #include "LogUtils.h"
@@ -153,7 +154,7 @@
 }
 
 // assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogBuffer* parent,
+size_t LogBufferElement::populateDroppedMessage(char*& buffer, LogStatistics* stats,
                                                 bool lastSame) {
     static const char tag[] = "chatty";
 
@@ -163,17 +164,13 @@
     }
 
     static const char format_uid[] = "uid=%u%s%s %s %u line%s";
-    parent->wrlock();
-    const char* name = parent->uidToName(mUid);
-    parent->unlock();
+    const char* name = stats->UidToName(mUid);
     const char* commName = android::tidToName(mTid);
     if (!commName && (mTid != mPid)) {
         commName = android::tidToName(mPid);
     }
     if (!commName) {
-        parent->wrlock();
-        commName = parent->pidToName(mPid);
-        parent->unlock();
+        commName = stats->PidToName(mPid);
     }
     if (name && name[0] && commName && (name[0] == commName[0])) {
         size_t len = strlen(name + 1);
@@ -246,7 +243,7 @@
     return retval;
 }
 
-uint64_t LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+uint64_t LogBufferElement::flushTo(SocketClient* reader, LogStatistics* stats, bool lastSame) {
     struct logger_entry entry = {};
 
     entry.hdr_size = sizeof(struct logger_entry);
@@ -264,7 +261,7 @@
     char* buffer = nullptr;
 
     if (mDropped) {
-        entry.len = populateDroppedMessage(buffer, parent, lastSame);
+        entry.len = populateDroppedMessage(buffer, stats, lastSame);
         if (!entry.len) return mSequence;
         iovec[1].iov_base = buffer;
     } else {
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 434b7db..8676cf1 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -25,6 +25,7 @@
 #include <sysutils/SocketClient.h>
 
 class LogBuffer;
+class LogStatistics;
 
 #define EXPIRE_HOUR_THRESHOLD 24  // Only expire chatty UID logs to preserve
                                   // non-chatty UIDs less than this age in hours
@@ -55,10 +56,9 @@
     static atomic_int_fast64_t sequence;
 
     // assumption: mDropped == true
-    size_t populateDroppedMessage(char*& buffer, LogBuffer* parent,
-                                  bool lastSame);
+    size_t populateDroppedMessage(char*& buffer, LogStatistics* parent, bool lastSame);
 
-   public:
+  public:
     LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
                      pid_t tid, const char* msg, uint16_t len);
     LogBufferElement(const LogBufferElement& elem);
@@ -98,5 +98,5 @@
     }
 
     static const uint64_t FLUSH_ERROR;
-    uint64_t flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
+    uint64_t flushTo(SocketClient* writer, LogStatistics* parent, bool lastSame);
 };
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index c308073..bc94b45 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogKlog.h"
+
 #include <ctype.h>
 #include <errno.h>
 #include <inttypes.h>
@@ -29,7 +31,6 @@
 #include <private/android_logger.h>
 
 #include "LogBuffer.h"
-#include "LogKlog.h"
 #include "LogReader.h"
 
 #define KMSG_PRIORITY(PRI) \
@@ -201,15 +202,16 @@
                                        ? log_time(log_time::EPOCH)
                                        : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
 
-LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
-                 bool auditd)
+LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead, bool auditd,
+                 LogStatistics* stats)
     : SocketListener(fdRead, false),
       logbuf(buf),
       reader(reader),
       signature(CLOCK_MONOTONIC),
       initialized(false),
       enableLogging(true),
-      auditd(auditd) {
+      auditd(auditd),
+      stats_(stats) {
     static const char klogd_message[] = "%s%s%" PRIu64 "\n";
     char buffer[strlen(priority_message) + strlen(klogdStr) +
                 strlen(klogd_message) + 20];
@@ -528,9 +530,7 @@
     const pid_t tid = pid;
     uid_t uid = AID_ROOT;
     if (pid) {
-        logbuf->wrlock();
-        uid = logbuf->pidToUid(pid);
-        logbuf->unlock();
+        uid = stats_->PidToUid(pid);
     }
 
     // Parse (rules at top) to pull out a tag from the incoming kernel message.
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 4c09751..a7dbe64 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_KLOG_H__
-#define _LOGD_LOG_KLOG_H__
+#pragma once
 
 #include <private/android_logger.h>
 #include <sysutils/SocketListener.h>
 
+#include "LogStatistics.h"
+
 class LogBuffer;
 class LogReader;
 
@@ -38,20 +39,19 @@
 
     static log_time correction;
 
-   public:
-    LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
-            bool auditd);
+  public:
+    LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead, bool auditd,
+            LogStatistics* stats);
     int log(const char* buf, ssize_t len);
 
-    static void convertMonotonicToReal(log_time& real) {
-        real += correction;
-    }
+    static void convertMonotonicToReal(log_time& real) { real += correction; }
 
-   protected:
-     log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
-     pid_t sniffPid(const char*& buf, ssize_t len);
-     void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
-     virtual bool onDataAvailable(SocketClient* cli);
+  protected:
+    log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+    pid_t sniffPid(const char*& buf, ssize_t len);
+    void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+    virtual bool onDataAvailable(SocketClient* cli);
+
+  private:
+    LogStatistics* stats_;
 };
-
-#endif
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 4c4c1db..14bcb63 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "LogStatistics.h"
+
 #include <ctype.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -27,14 +29,12 @@
 
 #include <private/android_logger.h>
 
-#include "LogStatistics.h"
-
 static const uint64_t hourSec = 60 * 60;
 static const uint64_t monthSec = 31 * 24 * hourSec;
 
-size_t LogStatistics::SizesTotal;
+std::atomic<size_t> LogStatistics::SizesTotal;
 
-LogStatistics::LogStatistics() : enable(false) {
+LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
     log_time now(CLOCK_REALTIME);
     log_id_for_each(id) {
         mSizes[id] = 0;
@@ -79,7 +79,8 @@
 }
 }
 
-void LogStatistics::addTotal(LogBufferElement* element) {
+void LogStatistics::AddTotal(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     if (element->getDropped()) return;
 
     log_id_t log_id = element->getLogId();
@@ -89,7 +90,8 @@
     ++mElementsTotal[log_id];
 }
 
-void LogStatistics::add(LogBufferElement* element) {
+void LogStatistics::Add(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     log_id_t log_id = element->getLogId();
     uint16_t size = element->getMsgLen();
     mSizes[log_id] += size;
@@ -159,7 +161,8 @@
     }
 }
 
-void LogStatistics::subtract(LogBufferElement* element) {
+void LogStatistics::Subtract(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     log_id_t log_id = element->getLogId();
     uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
@@ -204,7 +207,8 @@
 
 // Atomically set an entry to drop
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement* element) {
+void LogStatistics::Drop(LogBufferElement* element) {
+    auto lock = std::lock_guard{lock_};
     log_id_t log_id = element->getLogId();
     uint16_t size = element->getMsgLen();
     mSizes[log_id] -= size;
@@ -238,9 +242,13 @@
     tagNameTable.subtract(TagNameKey(element), element);
 }
 
+const char* LogStatistics::UidToName(uid_t uid) const {
+    auto lock = std::lock_guard{lock_};
+    return UidToNameLocked(uid);
+}
+
 // caller must own and free character string
-// Requires parent LogBuffer::wrlock() to be held
-const char* LogStatistics::uidToName(uid_t uid) const {
+const char* LogStatistics::UidToNameLocked(uid_t uid) const {
     // Local hard coded favourites
     if (uid == AID_LOGD) {
         return strdup("auditd");
@@ -300,7 +308,7 @@
 template <typename TKey, typename TEntry>
 void LogStatistics::WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
                                           int* worst, size_t* worst_sizes,
-                                          size_t* second_worst_sizes) {
+                                          size_t* second_worst_sizes) const {
     std::array<const TEntry*, 2> max_entries;
     table.MaxEntries(AID_ROOT, 0, &max_entries);
     if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
@@ -319,17 +327,20 @@
 }
 
 void LogStatistics::WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
-                                 size_t* second_worst_sizes) {
+                                 size_t* second_worst_sizes) const {
+    auto lock = std::lock_guard{lock_};
     WorstTwoWithThreshold(uidTable[id], threshold, worst, worst_sizes, second_worst_sizes);
 }
 
 void LogStatistics::WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
-                                 size_t* second_worst_sizes) {
+                                 size_t* second_worst_sizes) const {
+    auto lock = std::lock_guard{lock_};
     WorstTwoWithThreshold(tagTable, threshold, worst, worst_sizes, second_worst_sizes);
 }
 
 void LogStatistics::WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
-                                       size_t* second_worst_sizes) {
+                                       size_t* second_worst_sizes) const {
+    auto lock = std::lock_guard{lock_};
     std::array<const PidEntry*, 2> max_entries;
     pidSystemTable[id].MaxEntries(AID_SYSTEM, 0, &max_entries);
     if (max_entries[0] == nullptr || max_entries[1] == nullptr) {
@@ -340,6 +351,34 @@
     *second_worst_sizes = worst_uid_sizes - max_entries[0]->getSizes() + max_entries[1]->getSizes();
 }
 
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
+bool LogStatistics::ShouldPrune(log_id id, unsigned long max_size,
+                                unsigned long* prune_rows) const {
+    static constexpr size_t kMinPrune = 4;
+    static constexpr size_t kMaxPrune = 256;
+
+    auto lock = std::lock_guard{lock_};
+    size_t sizes = mSizes[id];
+    if (sizes <= max_size) {
+        return false;
+    }
+    size_t size_over = sizes - ((max_size * 9) / 10);
+    size_t elements = mElements[id] - mDroppedElements[id];
+    size_t min_elements = elements / 100;
+    if (min_elements < kMinPrune) {
+        min_elements = kMinPrune;
+    }
+    *prune_rows = elements * size_over / sizes;
+    if (*prune_rows < min_elements) {
+        *prune_rows = min_elements;
+    }
+    if (*prune_rows > kMaxPrune) {
+        *prune_rows = kMaxPrune;
+    }
+
+    return true;
+}
+
 std::string UidEntry::formatHeader(const std::string& name, log_id_t id) const {
     bool isprune = worstUidEnabledForLogid(id);
     return formatLine(android::base::StringPrintf(name.c_str(),
@@ -351,10 +390,10 @@
 }
 
 // Helper to truncate name, if too long, and add name dressings
-static void formatTmp(const LogStatistics& stat, const char* nameTmp, uid_t uid,
-                      std::string& name, std::string& size, size_t nameLen) {
+void LogStatistics::FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+                              size_t nameLen) const {
     const char* allocNameTmp = nullptr;
-    if (!nameTmp) nameTmp = allocNameTmp = stat.uidToName(uid);
+    if (!nameTmp) nameTmp = allocNameTmp = UidToNameLocked(uid);
     if (nameTmp) {
         size_t lenSpace = std::max(nameLen - name.length(), (size_t)1);
         size_t len = EntryBaseConstants::total_len -
@@ -375,12 +414,12 @@
     }
 }
 
-std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const {
+std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const REQUIRES(stat.lock_) {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%u", uid);
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
-    formatTmp(stat, nullptr, uid, name, size, 6);
+    stat.FormatTmp(nullptr, uid, name, size, 6);
 
     std::string pruned = "";
     if (worstUidEnabledForLogid(id)) {
@@ -390,9 +429,9 @@
              it != stat.uidTable[id].end(); ++it) {
             totalDropped += it->second.getDropped();
         }
-        size_t sizes = stat.sizes(id);
-        size_t totalSize = stat.sizesTotal(id);
-        size_t totalElements = stat.elementsTotal(id);
+        size_t sizes = stat.mSizes[id];
+        size_t totalSize = stat.mSizesTotal[id];
+        size_t totalElements = stat.mElementsTotal[id];
         float totalVirtualSize =
             (float)sizes + (float)totalDropped * totalSize / totalElements;
         size_t entrySize = getSizes();
@@ -480,14 +519,14 @@
                       std::string("BYTES"), std::string("NUM"));
 }
 
-std::string PidEntry::format(const LogStatistics& stat,
-                             log_id_t /* id */) const {
+std::string PidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
+        REQUIRES(stat.lock_) {
     uid_t uid = getUid();
     pid_t pid = getPid();
     std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
-    formatTmp(stat, getName(), uid, name, size, 12);
+    stat.FormatTmp(getName(), uid, name, size, 12);
 
     std::string pruned = "";
     size_t dropped = getDropped();
@@ -505,13 +544,13 @@
                       std::string("NUM"));
 }
 
-std::string TidEntry::format(const LogStatistics& stat,
-                             log_id_t /* id */) const {
+std::string TidEntry::format(const LogStatistics& stat, log_id_t /* id */) const
+        REQUIRES(stat.lock_) {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid);
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
-    formatTmp(stat, getName(), uid, name, size, 12);
+    stat.FormatTmp(getName(), uid, name, size, 12);
 
     std::string pruned = "";
     size_t dropped = getDropped();
@@ -651,8 +690,36 @@
     return output;
 }
 
-std::string LogStatistics::format(uid_t uid, pid_t pid,
-                                  unsigned int logMask) const {
+template <typename TKey, typename TEntry>
+std::string LogStatistics::FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid,
+                                       pid_t pid, const std::string& name, log_id_t id) const
+        REQUIRES(lock_) {
+    static const size_t maximum_sorted_entries = 32;
+    std::string output;
+    std::array<const TEntry*, maximum_sorted_entries> sorted;
+    table.MaxEntries(uid, pid, &sorted);
+    bool header_printed = false;
+    for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+        const TEntry* entry = sorted[index];
+        if (!entry) {
+            break;
+        }
+        if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+            break;
+        }
+        if (!header_printed) {
+            output += "\n\n";
+            output += entry->formatHeader(name, id);
+            header_printed = true;
+        }
+        output += entry->format(*this, id);
+    }
+    return output;
+}
+
+std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
+    auto lock = std::lock_guard{lock_};
+
     static const uint16_t spaces_total = 19;
 
     // Report on total logging, current and for all time
@@ -682,9 +749,9 @@
         if (!(logMask & (1 << id))) continue;
         oldLength = output.length();
         if (spaces < 0) spaces = 0;
-        size_t szs = sizesTotal(id);
+        size_t szs = mSizesTotal[id];
         totalSize += szs;
-        size_t els = elementsTotal(id);
+        size_t els = mElementsTotal[id];
         totalEls += els;
         output +=
             android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els);
@@ -703,11 +770,11 @@
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) continue;
 
-        size_t els = elements(id);
+        size_t els = mElements[id];
         if (els) {
             oldLength = output.length();
             if (spaces < 0) spaces = 0;
-            size_t szs = sizes(id);
+            size_t szs = mSizes[id];
             totalSize += szs;
             totalEls += els;
             output +=
@@ -789,7 +856,7 @@
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) continue;
 
-        size_t els = elements(id);
+        size_t els = mElements[id];
         if (els) {
             oldLength = output.length();
             if (spaces < 0) spaces = 0;
@@ -798,7 +865,7 @@
                 ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
                  -sizeof(uint64_t)) +
                 sizeof(std::list<LogBufferElement*>);
-            size_t szs = sizes(id) + els * overhead;
+            size_t szs = mSizes[id] + els * overhead;
             totalSize += szs;
             output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
             spaces -= output.length() - oldLength;
@@ -819,39 +886,38 @@
 
         name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:"
                                  : "Logging for your UID in %s log buffer:";
-        output += uidTable[id].format(*this, uid, pid, name, id);
+        output += FormatTable(uidTable[id], uid, pid, name, id);
     }
 
     if (enable) {
         name = ((uid == AID_ROOT) && !pid) ? "Chattiest PIDs:"
                                            : "Logging for this PID:";
-        output += pidTable.format(*this, uid, pid, name);
+        output += FormatTable(pidTable, uid, pid, name);
         name = "Chattiest TIDs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output += tidTable.format(*this, uid, pid, name);
+        output += FormatTable(tidTable, uid, pid, name);
     }
 
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
         name = "Chattiest events log buffer TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
+        output += FormatTable(tagTable, uid, pid, name, LOG_ID_EVENTS);
     }
 
     if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
         name = "Chattiest security log buffer TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output +=
-            securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
+        output += FormatTable(securityTagTable, uid, pid, name, LOG_ID_SECURITY);
     }
 
     if (enable) {
         name = "Chattiest TAGs";
         if (pid) name += android::base::StringPrintf(" for PID %d", pid);
         name += ":";
-        output += tagNameTable.format(*this, uid, pid, name);
+        output += FormatTable(tagNameTable, uid, pid, name);
     }
 
     return output;
@@ -879,12 +945,14 @@
 }
 }
 
-uid_t LogStatistics::pidToUid(pid_t pid) {
+uid_t LogStatistics::PidToUid(pid_t pid) {
+    auto lock = std::lock_guard{lock_};
     return pidTable.add(pid)->second.getUid();
 }
 
 // caller must free character string
-const char* LogStatistics::pidToName(pid_t pid) const {
+const char* LogStatistics::PidToName(pid_t pid) const {
+    auto lock = std::lock_guard{lock_};
     // An inconvenient truth ... getName() can alter the object
     pidTable_t& writablePidTable = const_cast<pidTable_t&>(pidTable);
     const char* name = writablePidTable.add(pid)->second.getName();
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index ed9002a..53c9bb6 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -26,11 +26,13 @@
 #include <algorithm>  // std::max
 #include <array>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <string_view>
 #include <unordered_map>
 
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <android/log.h>
 #include <log/log_time.h>
 #include <private/android_filesystem_config.h>
@@ -164,32 +166,6 @@
     inline const_iterator end() const {
         return map.end();
     }
-
-    std::string format(const LogStatistics& stat, uid_t uid, pid_t pid,
-                       const std::string& name = std::string(""),
-                       log_id_t id = LOG_ID_MAX) const {
-        static const size_t maximum_sorted_entries = 32;
-        std::string output;
-        std::array<const TEntry*, maximum_sorted_entries> sorted;
-        MaxEntries(uid, pid, &sorted);
-        bool headerPrinted = false;
-        for (size_t index = 0; index < maximum_sorted_entries; ++index) {
-            const TEntry* entry = sorted[index];
-            if (!entry) {
-                break;
-            }
-            if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
-                break;
-            }
-            if (!headerPrinted) {
-                output += "\n\n";
-                output += entry->formatHeader(name, id);
-                headerPrinted = true;
-            }
-            output += entry->format(stat, id);
-        }
-        return output;
-    }
 };
 
 namespace EntryBaseConstants {
@@ -621,46 +597,48 @@
 // Log Statistics
 class LogStatistics {
     friend UidEntry;
+    friend PidEntry;
+    friend TidEntry;
 
-    size_t mSizes[LOG_ID_MAX];
-    size_t mElements[LOG_ID_MAX];
-    size_t mDroppedElements[LOG_ID_MAX];
-    size_t mSizesTotal[LOG_ID_MAX];
-    size_t mElementsTotal[LOG_ID_MAX];
-    log_time mOldest[LOG_ID_MAX];
-    log_time mNewest[LOG_ID_MAX];
-    log_time mNewestDropped[LOG_ID_MAX];
-    static size_t SizesTotal;
+    size_t mSizes[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mElements[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mDroppedElements[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mSizesTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+    size_t mElementsTotal[LOG_ID_MAX] GUARDED_BY(lock_);
+    log_time mOldest[LOG_ID_MAX] GUARDED_BY(lock_);
+    log_time mNewest[LOG_ID_MAX] GUARDED_BY(lock_);
+    log_time mNewestDropped[LOG_ID_MAX] GUARDED_BY(lock_);
+    static std::atomic<size_t> SizesTotal;
     bool enable;
 
     // uid to size list
     typedef LogHashtable<uid_t, UidEntry> uidTable_t;
-    uidTable_t uidTable[LOG_ID_MAX];
+    uidTable_t uidTable[LOG_ID_MAX] GUARDED_BY(lock_);
 
     // pid of system to size list
     typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t;
-    pidSystemTable_t pidSystemTable[LOG_ID_MAX];
+    pidSystemTable_t pidSystemTable[LOG_ID_MAX] GUARDED_BY(lock_);
 
     // pid to uid list
     typedef LogHashtable<pid_t, PidEntry> pidTable_t;
-    pidTable_t pidTable;
+    pidTable_t pidTable GUARDED_BY(lock_);
 
     // tid to uid list
     typedef LogHashtable<pid_t, TidEntry> tidTable_t;
-    tidTable_t tidTable;
+    tidTable_t tidTable GUARDED_BY(lock_);
 
     // tag list
     typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
-    tagTable_t tagTable;
+    tagTable_t tagTable GUARDED_BY(lock_);
 
     // security tag list
-    tagTable_t securityTagTable;
+    tagTable_t securityTagTable GUARDED_BY(lock_);
 
     // global tag list
     typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
     tagNameTable_t tagNameTable;
 
-    size_t sizeOf() const {
+    size_t sizeOf() const REQUIRES(lock_) {
         size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
                       tagTable.sizeOf() + securityTagTable.sizeOf() +
                       tagNameTable.sizeOf() +
@@ -685,61 +663,59 @@
         return size;
     }
 
-   public:
-    LogStatistics();
+  public:
+    LogStatistics(bool enable_statistics);
 
-    void enableStatistics() {
-        enable = true;
-    }
-
-    void addTotal(LogBufferElement* entry);
-    void add(LogBufferElement* entry);
-    void subtract(LogBufferElement* entry);
+    void AddTotal(LogBufferElement* entry) EXCLUDES(lock_);
+    void Add(LogBufferElement* entry) EXCLUDES(lock_);
+    void Subtract(LogBufferElement* entry) EXCLUDES(lock_);
     // entry->setDropped(1) must follow this call
-    void drop(LogBufferElement* entry);
+    void Drop(LogBufferElement* entry) EXCLUDES(lock_);
     // Correct for coalescing two entries referencing dropped content
-    void erase(LogBufferElement* element) {
+    void Erase(LogBufferElement* element) EXCLUDES(lock_) {
+        auto lock = std::lock_guard{lock_};
         log_id_t log_id = element->getLogId();
         --mElements[log_id];
         --mDroppedElements[log_id];
     }
 
     void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
-                      size_t* second_worst_sizes);
+                      size_t* second_worst_sizes) const EXCLUDES(lock_);
     void WorstTwoTags(size_t threshold, int* worst, size_t* worst_sizes,
-                      size_t* second_worst_sizes);
+                      size_t* second_worst_sizes) const EXCLUDES(lock_);
     void WorstTwoSystemPids(log_id id, size_t worst_uid_sizes, int* worst,
-                            size_t* second_worst_sizes);
+                            size_t* second_worst_sizes) const EXCLUDES(lock_);
 
-    // fast track current value by id only
-    size_t sizes(log_id_t id) const {
+    bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
+            EXCLUDES(lock_);
+
+    // Snapshot of the sizes for a given log buffer.
+    size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
+        auto lock = std::lock_guard{lock_};
         return mSizes[id];
     }
-    size_t elements(log_id_t id) const {
-        return mElements[id];
-    }
-    size_t realElements(log_id_t id) const {
-        return mElements[id] - mDroppedElements[id];
-    }
-    size_t sizesTotal(log_id_t id) const {
-        return mSizesTotal[id];
-    }
-    size_t elementsTotal(log_id_t id) const {
-        return mElementsTotal[id];
-    }
+    // TODO: Get rid of this entirely.
     static size_t sizesTotal() {
         return SizesTotal;
     }
 
-    std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
+    std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
 
-    // helper (must be locked directly or implicitly by mLogElementsLock)
-    const char* pidToName(pid_t pid) const;
-    uid_t pidToUid(pid_t pid);
-    const char* uidToName(uid_t uid) const;
+    const char* PidToName(pid_t pid) const EXCLUDES(lock_);
+    uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
+    const char* UidToName(uid_t uid) const EXCLUDES(lock_);
 
   private:
     template <typename TKey, typename TEntry>
     void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
-                               int* worst, size_t* worst_sizes, size_t* second_worst_sizes);
+                               int* worst, size_t* worst_sizes, size_t* second_worst_sizes) const;
+    template <typename TKey, typename TEntry>
+    std::string FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid, pid_t pid,
+                            const std::string& name = std::string(""),
+                            log_id_t id = LOG_ID_MAX) const REQUIRES(lock_);
+    void FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size,
+                   size_t nameLen) const REQUIRES(lock_);
+    const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
+
+    mutable std::mutex lock_;
 };
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 082e4a1..8250808 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -37,6 +37,7 @@
 #include <log/log_read.h>
 #include <private/android_filesystem_config.h>
 
+#include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogUtils.h"
 
@@ -493,7 +494,7 @@
 
     // Every 16K (half the smallest configurable pmsg buffer size) record
     static const size_t rate_to_pmsg = 16 * 1024;
-    if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+    if (lastTotal && (LogStatistics::sizesTotal() - lastTotal) < rate_to_pmsg) {
         return;
     }
 
@@ -663,7 +664,7 @@
         }
     }
 
-    lastTotal = android::sizesTotal();
+    lastTotal = LogStatistics::sizesTotal();
     if (!lastTotal) ++lastTotal;
 
     // record totals for next watermark.
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index f9cd42d..3fe1bbe 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -32,8 +32,6 @@
 char* uidToName(uid_t uid);
 void prdebug(const char* fmt, ...) __printflike(1, 2);
 
-// Furnished in LogStatistics.cpp.
-size_t sizesTotal();
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
 char* tidToName(pid_t tid);
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 8156612..58610c4 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -17,6 +17,7 @@
 
 #include "../LogBuffer.h"
 #include "../LogReaderThread.h"
+#include "../LogStatistics.h"
 
 // We don't want to waste a lot of entropy on messages
 #define MAX_MSG_LENGTH 5
@@ -36,7 +37,8 @@
     unsigned int log_mask;
 };
 
-int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
+int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer,
+                       LogStatistics* stats) {
     const uint8_t* data = *pdata;
     const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
     data += sizeof(LogInput);
@@ -71,7 +73,7 @@
     log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
     log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
                     sizeof(uint32_t) + msg_length + 1);
-    log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
+    stats->Format(logInput->uid, logInput->pid, logInput->log_mask);
     *pdata = data;
     return 1;
 }
@@ -96,17 +98,17 @@
     LastLogTimes times;
     LogTags tags;
     PruneList prune_list;
-    LogBuffer log_buffer(&times, &tags, &prune_list);
+    LogStatistics stats(true);
+    LogBuffer log_buffer(&times, &tags, &prune_list, &stats);
     size_t data_left = size;
     const uint8_t** pdata = &data;
 
-    log_buffer.enableStatistics();
     prune_list.init(nullptr);
     // We want to get pruning code to get called.
     log_id_for_each(i) { log_buffer.setSize(i, 10000); }
 
     while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        if (!write_log_messages(pdata, &data_left, &log_buffer)) {
+        if (!write_log_messages(pdata, &data_left, &log_buffer, &stats)) {
             return 0;
         }
     }
diff --git a/logd/main.cpp b/logd/main.cpp
index d9dd249..d1e05b9 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -52,6 +52,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogListener.h"
+#include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogUtils.h"
 
@@ -272,6 +273,11 @@
     LogTags log_tags;
     // Pruning configuration.
     PruneList prune_list;
+    // Partial (required for chatty) or full logging statistics.
+    bool enable_full_log_statistics = __android_logger_property_get_bool(
+            "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
+                                       BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+    LogStatistics log_statistics(enable_full_log_statistics);
 
     // Serves the purpose of managing the last logs times read on a
     // socket connection, and as a reader lock on a range of log
@@ -282,14 +288,7 @@
     // LogBuffer is the object which is responsible for holding all
     // log entries.
 
-    LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
-
-    if (__android_logger_property_get_bool(
-            "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
-                                   BOOL_DEFAULT_FLAG_ENG |
-                                   BOOL_DEFAULT_FLAG_SVELTE)) {
-        logBuf->enableStatistics();
-    }
+    LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list, &log_statistics);
 
     // LogReader listens on /dev/socket/logdr. When a client
     // connects, log entries in the LogBuffer are written to the client.
@@ -312,7 +311,7 @@
     // Command listener listens on /dev/socket/logd for incoming logd
     // administrative commands.
 
-    CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
+    CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
     if (cl->startListener()) {
         return EXIT_FAILURE;
     }
@@ -323,16 +322,17 @@
 
     LogAudit* al = nullptr;
     if (auditd) {
-        al = new LogAudit(logBuf, reader,
-                          __android_logger_property_get_bool(
-                              "ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
-                              ? fdDmesg
-                              : -1);
+        al = new LogAudit(
+                logBuf, reader,
+                __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
+                        ? fdDmesg
+                        : -1,
+                &log_statistics);
     }
 
     LogKlog* kl = nullptr;
     if (klogd) {
-        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
+        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
     }
 
     readDmesg(al, kl);