Merge "Cpu usage optimization: 1/ Avoid unnecessary field/dimension proto construction. 2/ use unordered_map for slicing. 3/ Use dimension fields to compare dimension keys."
diff --git a/bin/Android.mk b/bin/Android.mk
index 565b092..eabbb96 100644
--- a/bin/Android.mk
+++ b/bin/Android.mk
@@ -137,7 +137,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-#LOCAL_INIT_RC := statsd.rc
+LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/bin/src/StatsLogProcessor.cpp b/bin/src/StatsLogProcessor.cpp
index 90639b4..a4066aa 100644
--- a/bin/src/StatsLogProcessor.cpp
+++ b/bin/src/StatsLogProcessor.cpp
@@ -195,6 +195,14 @@
     return it->second->byteSize();
 }
 
+void StatsLogProcessor::dumpStates(FILE* out, bool verbose) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    fprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
+    for (auto metricsManager : mMetricsManagers) {
+        metricsManager.second->dumpStates(out, verbose);
+    }
+}
+
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs,
                                      ConfigMetricsReportList* report) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
diff --git a/bin/src/StatsLogProcessor.h b/bin/src/StatsLogProcessor.h
index fb85aa8..c19ff63 100644
--- a/bin/src/StatsLogProcessor.h
+++ b/bin/src/StatsLogProcessor.h
@@ -61,6 +61,8 @@
         return mUidMap;
     }
 
+    void dumpStates(FILE* out, bool verbose);
+
 private:
     mutable mutex mMetricsMutex;
 
diff --git a/bin/src/StatsService.cpp b/bin/src/StatsService.cpp
index 31994e1..f545bb0 100644
--- a/bin/src/StatsService.cpp
+++ b/bin/src/StatsService.cpp
@@ -175,8 +175,13 @@
         return NO_MEMORY;  // the fd is already open
     }
 
+    bool verbose = false;
+    if (args.size() > 0 && !args[0].compare(String16("-v"))) {
+        verbose = true;
+    }
+
     // TODO: Proto format for incident reports
-    dump_impl(out);
+    dump_impl(out, verbose);
 
     fclose(out);
     return NO_ERROR;
@@ -185,9 +190,9 @@
 /**
  * Write debugging data about statsd in text format.
  */
-void StatsService::dump_impl(FILE* out) {
-    mConfigManager->Dump(out);
+void StatsService::dump_impl(FILE* out, bool verbose) {
     StatsdStats::getInstance().dumpStats(out);
+    mProcessor->dumpStates(out, verbose);
 }
 
 /**
diff --git a/bin/src/StatsService.h b/bin/src/StatsService.h
index ba6bd24..be20893 100644
--- a/bin/src/StatsService.h
+++ b/bin/src/StatsService.h
@@ -140,7 +140,7 @@
     /**
      * Text output of dumpsys.
      */
-    void dump_impl(FILE* out);
+    void dump_impl(FILE* out, bool verbose);
 
     /**
      * Print usage information for the commands
diff --git a/bin/src/atoms.proto b/bin/src/atoms.proto
index 99e871f..b32af02 100644
--- a/bin/src/atoms.proto
+++ b/bin/src/atoms.proto
@@ -23,6 +23,7 @@
 
 import "frameworks/base/core/proto/android/app/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
+import "frameworks/base/core/proto/android/server/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
 
@@ -87,33 +88,36 @@
         AppStartChanged app_start_changed = 48;
         AppStartCancelChanged app_start_cancel_changed = 49;
         AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
-        LmkEventOccurred lmk_event_occurred = 51;
+        LmkKillOccurred lmk_kill_occurred = 51;
         PictureInPictureStateChanged picture_in_picture_state_changed = 52;
         WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
+        LmkStateChanged lmk_state_changed = 54;
+        AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
     // Pulled events will start at field 10000.
+    // Next: 10019
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
         MobileBytesTransfer mobile_bytes_transfer = 10002;
         MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003;
+        BluetoothBytesTransfer bluetooth_bytes_transfer = 10006;
         KernelWakelock kernel_wakelock = 10004;
         SubsystemSleepState subsystem_sleep_state = 10005;
-        // 10006 and 10007 are free to use.
         CpuTimePerFreq cpu_time_per_freq = 10008;
         CpuTimePerUid cpu_time_per_uid = 10009;
         CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
         WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
         ModemActivityInfo modem_activity_info = 10012;
-        ProcessMemoryStat process_memory_stat = 10013;
-        CpuSuspendTime cpu_suspend_time = 10014;
-        CpuIdleTime cpu_idle_time = 10015;
+        BluetoothActivityInfo bluetooth_activity_info = 10007;
+        ProcessMemoryState process_memory_state = 10013;
+        SystemElapsedRealtime system_elapsed_realtime = 10014;
+        SystemUptime system_uptime = 10015;
         CpuActiveTime cpu_active_time = 10016;
         CpuClusterTime cpu_cluster_time = 10017;
         DiskSpace disk_space = 10018;
-        SystemUptime system_uptime = 10019;
     }
 }
 
@@ -463,13 +467,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message DeviceIdleModeStateChanged {
-    // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
-    enum State {
-        DEVICE_IDLE_MODE_OFF = 0;
-        DEVICE_IDLE_MODE_LIGHT = 1;
-        DEVICE_IDLE_MODE_DEEP = 2;
-    }
-    optional State state = 1;
+    optional android.server.DeviceIdleModeEnum state = 1;
 }
 
 
@@ -480,13 +478,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message DeviceIdlingModeStateChanged {
-    // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
-    enum State {
-        DEVICE_IDLE_MODE_OFF = 0;
-        DEVICE_IDLE_MODE_LIGHT = 1;
-        DEVICE_IDLE_MODE_DEEP = 2;
-    }
-    optional State state = 1;
+    optional android.server.DeviceIdleModeEnum state = 1;
 }
 
 /**
@@ -999,6 +991,20 @@
 }
 
 /**
+ * Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller.
+ *
+ * Pulled from:
+ *   StatsCompanionService
+ */
+message BluetoothBytesTransfer {
+    optional int32 uid = 1;
+
+    optional int64 rx_bytes = 2;
+
+    optional int64 tx_bytes = 3;
+}
+
+/**
  * Pulls the kernel wakelock durations. This atom is adapted from
  * android/internal/os/KernelWakelockStats.java
  *
@@ -1168,32 +1174,60 @@
     optional uint64 energy_used = 10;
 }
 
-/*
- * Logs the memory stats for a process
+/**
+ * Pulls Bluetooth Activity Energy Info
+ * Note: BluetoothBytesTransfer is pulled at the same time from the controller.
  */
-message ProcessMemoryStat {
+message BluetoothActivityInfo {
+    // timestamp(wall clock) of record creation
+    optional uint64 timestamp_ms = 1;
+    // bluetooth stack state
+    optional int32 bluetooth_stack_state = 2;
+    // tx time in ms
+    optional uint64 controller_tx_time_ms = 3;
+    // rx time in ms
+    optional uint64 controller_rx_time_ms = 4;
+    // idle time in ms
+    optional uint64 controller_idle_time_ms = 5;
+    // product of current(mA), voltage(V) and time(ms)
+    optional uint64 energy_used = 6;
+}
+
+/*
+ * Logs the memory stats for an app on startup.
+ * Logged from:
+ *     frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message AppStartMemoryStateCaptured {
     // The uid if available. -1 means not available.
     optional int32 uid = 1;
 
     // The process name.
     optional string process_name = 2;
 
+    // The activity name.
+    optional string activity_name = 3;
+
     // # of page-faults
-    optional int64 pgfault = 3;
+    optional int64 pgfault = 4;
 
     // # of major page-faults
-    optional int64 pgmajfault = 4;
+    optional int64 pgmajfault = 5;
 
-    // RSS+CACHE(+SWAP)
-    optional int64 usage_in_bytes = 5;
+    // RSS
+    optional int64 rss_in_bytes = 6;
+
+    // CACHE
+    optional int64 cache_in_bytes = 7;
+
+    // SWAP
+    optional int64 swap_in_bytes = 8;
 }
 
 /*
- * Logs the event when LMKD kills a process to reduce memory pressure
- * Logged from:
- *      system/core/lmkd/lmkd.c
+ * Logs the memory stats for a process.
  */
-message LmkEventOccurred {
+message ProcessMemoryState {
     // The uid if available. -1 means not available.
     optional int32 uid = 1;
 
@@ -1209,22 +1243,78 @@
     // # of major page-faults
     optional int64 pgmajfault = 5;
 
-    // RSS+CACHE(+SWAP)
-    optional int64 usage_in_bytes = 6;
+    // RSS
+    optional int64 rss_in_bytes = 6;
+
+    // CACHE
+    optional int64 cache_in_bytes = 7;
+
+    // SWAP
+    optional int64 swap_in_bytes = 8;
 }
 
 /*
- * Cpu syspend time for cpu power calculation.
+ * Logs the change in Low Memory Killer Daemon (LMKD) state which is used as start/stop boundaries
+ * for LMK event.
+ * Logged from:
+ *      system/core/lmkd/lmkd.c
  */
-message CpuSuspendTime {
-    optional uint64 time = 1;
+message LmkStateChanged {
+    enum State {
+        UNKNOWN = 0;
+        START = 1;
+        STOP = 2;
+    }
+    optional State state = 1;
 }
 
 /*
- * Cpu idle time for cpu power calculation.
+ * Logs the event when Low Memory Killer Daemon (LMKD) kills a process to reduce memory pressure.
+ * Logged from:
+ *      system/core/lmkd/lmkd.c
  */
-message CpuIdleTime {
-    optional uint64 time = 1;
+message LmkKillOccurred {
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1;
+
+    // The process name.
+    optional string process_name = 2;
+
+    // oom adj score.
+    optional int32 oom_score = 3;
+
+    // # of page-faults
+    optional int64 pgfault = 4;
+
+    // # of major page-faults
+    optional int64 pgmajfault = 5;
+
+    // RSS
+    optional int64 rss_in_bytes = 6;
+
+    // CACHE
+    optional int64 cache_in_bytes = 7;
+
+    // SWAP
+    optional int64 swap_in_bytes = 8;
+}
+
+/*
+ * Elapsed real time from SystemClock.
+ */
+message SystemElapsedRealtime {
+    optional uint64 time_ms = 1;
+}
+
+/*
+ * Up time from SystemClock.
+ */
+message SystemUptime {
+    // Milliseconds since the system was booted.
+    // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting
+    // for external input).
+    // It is not affected by clock scaling, idle, or other power saving mechanisms.
+    optional uint64 uptime_ms = 1;
 }
 
 /*
@@ -1268,14 +1358,3 @@
     // available bytes in download cache or temp directories
     optional uint64 temp_available_bytes = 3;
 }
-
-/*
- * Pulls system up time.
- */
-message SystemUptime {
-    // Milliseconds since the system was booted.
-    // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting
-    // for external input).
-    // It is not affected by clock scaling, idle, or other power saving mechanisms.
-    optional uint64 uptime_ms = 1;
-}
diff --git a/bin/src/external/StatsPullerManagerImpl.cpp b/bin/src/external/StatsPullerManagerImpl.cpp
index e06ae48..148c9ae 100644
--- a/bin/src/external/StatsPullerManagerImpl.cpp
+++ b/bin/src/external/StatsPullerManagerImpl.cpp
@@ -64,12 +64,20 @@
     mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
     mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
     mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
-    mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)});
-    mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)});
-    mPullers.insert({android::util::DISK_SPACE,
-                     make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
+    mPullers.insert(
+            {android::util::SYSTEM_ELAPSED_REALTIME,
+             make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_ELAPSED_REALTIME)});
     mPullers.insert({android::util::SYSTEM_UPTIME,
                      make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
+    mPullers.insert({android::util::DISK_SPACE,
+                     make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
+    mPullers.insert(
+            {android::util::BLUETOOTH_ACTIVITY_INFO,
+             make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_ACTIVITY_INFO)});
+
+    mPullers.insert(
+            {android::util::BLUETOOTH_BYTES_TRANSFER,
+             make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_BYTES_TRANSFER)});
     mPullers.insert(
             {android::util::WIFI_ACTIVITY_ENERGY_INFO,
              make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
diff --git a/bin/src/guardrail/StatsdStats.cpp b/bin/src/guardrail/StatsdStats.cpp
index 63bde7d..77f5456 100644
--- a/bin/src/guardrail/StatsdStats.cpp
+++ b/bin/src/guardrail/StatsdStats.cpp
@@ -45,6 +45,8 @@
 const int FIELD_ID_ATOM_STATS = 7;
 const int FIELD_ID_UIDMAP_STATS = 8;
 const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
+const int FIELD_ID_PULLED_ATOM_STATS = 10;
+const int FIELD_ID_LOGGER_ERROR_STATS = 11;
 
 const int FIELD_ID_MATCHER_STATS_NAME = 1;
 const int FIELD_ID_MATCHER_STATS_COUNT = 2;
@@ -60,6 +62,9 @@
 
 const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
 
+const int FIELD_ID_LOGGER_STATS_TIME = 1;
+const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
+
 std::map<int, long> StatsdStats::kPullerCooldownMap = {
         {android::util::KERNEL_WAKELOCK, 1},
         {android::util::WIFI_BYTES_TRANSFER, 1},
@@ -282,6 +287,15 @@
     mPushedAtomStats[atomId]++;
 }
 
+void StatsdStats::noteLoggerError(int error) {
+    lock_guard<std::mutex> lock(mLock);
+    // grows strictly one at a time. so it won't > kMaxLoggerErrors
+    if (mLoggerErrors.size() == kMaxLoggerErrors) {
+        mLoggerErrors.pop_front();
+    }
+    mLoggerErrors.push_back(std::make_pair(time(nullptr), error));
+}
+
 void StatsdStats::reset() {
     lock_guard<std::mutex> lock(mLock);
     resetInternalLocked();
@@ -297,6 +311,7 @@
     mAlertStats.clear();
     mAnomalyAlarmRegisteredStats = 0;
     mMatcherStats.clear();
+    mLoggerErrors.clear();
     for (auto& config : mConfigStats) {
         config.second.clear_broadcast_sent_time_sec();
         config.second.clear_data_drop_time_sec();
@@ -465,6 +480,14 @@
             "lost=%d\n",
             mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(),
             mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes());
+
+    for (const auto& error : mLoggerErrors) {
+        time_t error_time = error.first;
+        struct tm* error_tm = localtime(&error_time);
+        char buffer[80];
+        strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M%p\n", error_tm);
+        fprintf(out, "Logger error %d at %s\n", error.second, buffer);
+    }
 }
 
 void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
@@ -526,6 +549,14 @@
     mUidMapStats.SerializeToArray(&buffer[0], numBytes);
     proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size());
 
+    for (const auto& error : mLoggerErrors) {
+        long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS |
+                                      FIELD_COUNT_REPEATED);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_TIME, error.first);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOGGER_STATS_ERROR_CODE, error.second);
+        proto.end(token);
+    }
+
     output->clear();
     size_t bufferSize = proto.size();
     output->resize(bufferSize);
diff --git a/bin/src/guardrail/StatsdStats.h b/bin/src/guardrail/StatsdStats.h
index 7cb48ea..1f4bfa6 100644
--- a/bin/src/guardrail/StatsdStats.h
+++ b/bin/src/guardrail/StatsdStats.h
@@ -49,6 +49,8 @@
     // The max number of old config stats we keep.
     const static int kMaxIceBoxSize = 20;
 
+    const static int kMaxLoggerErrors = 10;
+
     const static int kMaxTimestampCount = 20;
 
     const static int kMaxLogSourceCount = 50;
@@ -185,6 +187,11 @@
     void notePullFromCache(int pullAtomId);
 
     /**
+     * Records statsd met an error while reading from logd.
+     */
+    void noteLoggerError(int error);
+
+    /**
      * 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
      * to collect stats after reset() has been called.
@@ -246,6 +253,9 @@
     // Maps PullAtomId to its stats. The size is capped by the puller atom counts.
     std::map<int, PulledAtomStats> mPulledAtomStats;
 
+    // Logd errors. Size capped by kMaxLoggerErrors.
+    std::list<const std::pair<int, int>> mLoggerErrors;
+
     // Stores the number of times statsd modified the anomaly alarm registered with
     // StatsCompanionService.
     int mAnomalyAlarmRegisteredStats = 0;
diff --git a/bin/src/logd/LogReader.cpp b/bin/src/logd/LogReader.cpp
index 5d43ef3..0fe896b 100644
--- a/bin/src/logd/LogReader.cpp
+++ b/bin/src/logd/LogReader.cpp
@@ -16,10 +16,11 @@
 
 #include "logd/LogReader.h"
 
-#include <utils/Errors.h>
+#include "guardrail/StatsdStats.h"
 
 #include <time.h>
 #include <unistd.h>
+#include <utils/Errors.h>
 
 using namespace android;
 using namespace std;
@@ -92,16 +93,15 @@
 
     // Read forever
     if (eventLogger) {
-
+        log_msg msg;
         while (true) {
-            log_msg msg;
-
             // Read a message
             err = android_logger_list_read(loggers, &msg);
             // err = 0 - no content, unexpected connection drop or EOF.
             // err = +ive number - size of retrieved data from logger
             // err = -ive number, OS supplied error _except_ for -EAGAIN
             if (err <= 0) {
+                StatsdStats::getInstance().noteLoggerError(err);
                 fprintf(stderr, "logcat read failure: %s\n", strerror(err));
                 break;
             }
diff --git a/bin/src/metrics/CountMetricProducer.h b/bin/src/metrics/CountMetricProducer.h
index 16fc7ee..061b7a3 100644
--- a/bin/src/metrics/CountMetricProducer.h
+++ b/bin/src/metrics/CountMetricProducer.h
@@ -68,6 +68,8 @@
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
 
+    void dumpStatesLocked(FILE* out, bool verbose) const override{};
+
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& newEventTime);
 
diff --git a/bin/src/metrics/DurationMetricProducer.cpp b/bin/src/metrics/DurationMetricProducer.cpp
index d233a84..000874c 100644
--- a/bin/src/metrics/DurationMetricProducer.cpp
+++ b/bin/src/metrics/DurationMetricProducer.cpp
@@ -233,6 +233,21 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
+void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
+    if (mCurrentSlicedDuration.size() == 0) {
+        return;
+    }
+
+    fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
+            (unsigned long)mCurrentSlicedDuration.size());
+    if (verbose) {
+        for (const auto& slice : mCurrentSlicedDuration) {
+            fprintf(out, "\t%s\n", slice.first.c_str());
+            slice.second->dumpStates(out, verbose);
+        }
+    }
+}
+
 bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
     // the key is not new, we are good.
     if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
diff --git a/bin/src/metrics/DurationMetricProducer.h b/bin/src/metrics/DurationMetricProducer.h
index e06b9a1..d8cab92 100644
--- a/bin/src/metrics/DurationMetricProducer.h
+++ b/bin/src/metrics/DurationMetricProducer.h
@@ -68,6 +68,8 @@
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
 
+    void dumpStatesLocked(FILE* out, bool verbose) const override;
+
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
diff --git a/bin/src/metrics/EventMetricProducer.h b/bin/src/metrics/EventMetricProducer.h
index a57b07d..9da0dd0 100644
--- a/bin/src/metrics/EventMetricProducer.h
+++ b/bin/src/metrics/EventMetricProducer.h
@@ -62,6 +62,8 @@
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
 
+    void dumpStatesLocked(FILE* out, bool verbose) const override{};
+
     // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream
     // is more space efficient than storing LogEvent.
     std::unique_ptr<android::util::ProtoOutputStream> mProto;
diff --git a/bin/src/metrics/GaugeMetricProducer.h b/bin/src/metrics/GaugeMetricProducer.h
index f267e98..1895edf 100644
--- a/bin/src/metrics/GaugeMetricProducer.h
+++ b/bin/src/metrics/GaugeMetricProducer.h
@@ -83,6 +83,8 @@
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
 
+    void dumpStatesLocked(FILE* out, bool verbose) const override{};
+
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
diff --git a/bin/src/metrics/MetricProducer.h b/bin/src/metrics/MetricProducer.h
index 3779c44..6f33073 100644
--- a/bin/src/metrics/MetricProducer.h
+++ b/bin/src/metrics/MetricProducer.h
@@ -96,6 +96,11 @@
         return onDumpReportLocked(dumpTimeNs, report);
     }
 
+    void dumpStates(FILE* out, bool verbose) const {
+        std::lock_guard<std::mutex> lock(mMutex);
+        dumpStatesLocked(out, verbose);
+    }
+
     // Returns the memory in bytes currently used to store this metric's data. Does not change
     // state.
     size_t byteSize() const {
@@ -128,6 +133,7 @@
                                     android::util::ProtoOutputStream* protoOutput) = 0;
     virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0;
     virtual size_t byteSizeLocked() const = 0;
+    virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
 
     const int64_t mMetricId;
 
diff --git a/bin/src/metrics/MetricsManager.cpp b/bin/src/metrics/MetricsManager.cpp
index f929517..d0737de 100644
--- a/bin/src/metrics/MetricsManager.cpp
+++ b/bin/src/metrics/MetricsManager.cpp
@@ -154,6 +154,20 @@
     }
 }
 
+void MetricsManager::dumpStates(FILE* out, bool verbose) {
+    fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
+    {
+        std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+        for (const auto& source : mAllowedLogSources) {
+            fprintf(out, "%d ", source);
+        }
+    }
+    fprintf(out, "\n");
+    for (const auto& producer : mAllMetricProducers) {
+        producer->dumpStates(out, verbose);
+    }
+}
+
 void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) {
     VLOG("=========================Metric Reports Start==========================");
     uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC;
diff --git a/bin/src/metrics/MetricsManager.h b/bin/src/metrics/MetricsManager.h
index a0239fc..9cdbafc 100644
--- a/bin/src/metrics/MetricsManager.h
+++ b/bin/src/metrics/MetricsManager.h
@@ -61,6 +61,8 @@
         return !mAllowedPkg.empty();
     }
 
+    void dumpStates(FILE* out, bool verbose);
+
     // Config source owner can call onDumpReport() to get all the metrics collected.
     virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
     virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
@@ -68,7 +70,6 @@
     // Computes the total byte size of all metrics managed by a single config source.
     // Does not change the state.
     virtual size_t byteSize();
-
 private:
     const ConfigKey mConfigKey;
 
diff --git a/bin/src/metrics/ValueMetricProducer.h b/bin/src/metrics/ValueMetricProducer.h
index 3e7032d..9f750cf 100644
--- a/bin/src/metrics/ValueMetricProducer.h
+++ b/bin/src/metrics/ValueMetricProducer.h
@@ -67,6 +67,8 @@
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
 
+    void dumpStatesLocked(FILE* out, bool verbose) const override{};
+
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
diff --git a/bin/src/metrics/duration_helper/DurationTracker.h b/bin/src/metrics/duration_helper/DurationTracker.h
index 371460e..c2d2cea 100644
--- a/bin/src/metrics/duration_helper/DurationTracker.h
+++ b/bin/src/metrics/duration_helper/DurationTracker.h
@@ -97,6 +97,8 @@
     // Predict the anomaly timestamp given the current status.
     virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
                                               const uint64_t currentTimestamp) const = 0;
+    // Dump internal states for debugging
+    virtual void dumpStates(FILE* out, bool verbose) const = 0;
 
 protected:
     // Starts the anomaly alarm.
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
index 0c99391..412a0c9 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -291,6 +291,11 @@
     return currentTimestamp;
 }
 
+void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const {
+    fprintf(out, "\t\t sub-durations %lu\n", (unsigned long)mInfos.size());
+    fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.h b/bin/src/metrics/duration_helper/MaxDurationTracker.h
index 5053bde..661d131 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.h
@@ -48,6 +48,7 @@
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
                                       const uint64_t currentTimestamp) const override;
+    void dumpStates(FILE* out, bool verbose) const override;
 
 private:
     std::unordered_map<HashableDimensionKey, DurationInfo> mInfos;
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.cpp b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
index 6bf4228..75d7c08 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -314,6 +314,12 @@
     return eventTimestampNs + thresholdNs;
 }
 
+void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
+    fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size());
+    fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size());
+    fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.h b/bin/src/metrics/duration_helper/OringDurationTracker.h
index 9093a51..43469ca 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.h
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.h
@@ -48,6 +48,7 @@
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
                                       const uint64_t currentTimestamp) const override;
+    void dumpStates(FILE* out, bool verbose) const override;
 
 private:
     // We don't need to keep track of individual durations. The information that's needed is:
diff --git a/bin/src/stats_log.proto b/bin/src/stats_log.proto
index f73c4a5..a4ccbd4 100644
--- a/bin/src/stats_log.proto
+++ b/bin/src/stats_log.proto
@@ -262,4 +262,10 @@
     optional int64 min_pull_interval_sec = 4;
   }
   repeated PulledAtomStats pulled_atom_stats = 10;
+
+  message LoggerErrorStats {
+    optional int32 logger_disconnection_sec = 1;
+    optional int32 error_code = 2;
+  }
+  repeated LoggerErrorStats logger_error_stats = 11;
 }
\ No newline at end of file