Merge "logd: trailing spaces in log statistics (part deux)"
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 6e6d69f..ba44a5a 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -464,7 +464,7 @@
     if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
       errno = EINVAL;
       if (end_idx) *end_idx = start_idx;
-      if (attempted_idx) *end_idx = start_idx;
+      if (attempted_idx) *attempted_idx = start_idx;
       return -1;
     }
 
diff --git a/logd/Android.mk b/logd/Android.mk
index 7fe48d7..2da9782 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -40,6 +40,7 @@
 #  event_flag += $(call event_logtags,logd)
 # so make sure we do not regret hard-coding it as follows:
 event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+event_flag += -DLIBLOG_LOG_TAG=1006
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index d476596..a5d435f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -17,6 +17,7 @@
 //#define DEBUG_CHECK_FOR_STALE_ENTRIES
 
 #include <ctype.h>
+#include <endian.h>
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
@@ -126,27 +127,41 @@
     }
 }
 
-static bool identical(LogBufferElement* elem, LogBufferElement* last) {
+enum match_type {
+    DIFFERENT,
+    SAME,
+    SAME_LIBLOG
+};
+
+static enum match_type identical(LogBufferElement* elem, LogBufferElement* last) {
     // is it mostly identical?
-//  if (!elem) return false;
+//  if (!elem) return DIFFERENT;
     unsigned short lenl = elem->getMsgLen();
-    if (!lenl) return false;
-//  if (!last) return false;
+    if (!lenl) return DIFFERENT;
+//  if (!last) return DIFFERENT;
     unsigned short lenr = last->getMsgLen();
-    if (!lenr) return false;
-//  if (elem->getLogId() != last->getLogId()) return false;
-    if (elem->getUid() != last->getUid()) return false;
-    if (elem->getPid() != last->getPid()) return false;
-    if (elem->getTid() != last->getTid()) return false;
+    if (!lenr) return DIFFERENT;
+//  if (elem->getLogId() != last->getLogId()) return DIFFERENT;
+    if (elem->getUid() != last->getUid()) return DIFFERENT;
+    if (elem->getPid() != last->getPid()) return DIFFERENT;
+    if (elem->getTid() != last->getTid()) return DIFFERENT;
 
     // last is more than a minute old, stop squashing identical messages
     if (elem->getRealTime().nsec() >
-        (last->getRealTime().nsec() + 60 * NS_PER_SEC)) return false;
+        (last->getRealTime().nsec() + 60 * NS_PER_SEC)) return DIFFERENT;
 
     // Identical message
     const char* msgl = elem->getMsg();
     const char* msgr = last->getMsg();
-    if ((lenl == lenr) && !fastcmp<memcmp>(msgl, msgr, lenl)) return true;
+    if (lenl == lenr) {
+        if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+        // liblog tagged messages (content gets summed)
+        if ((elem->getLogId() == LOG_ID_EVENTS) &&
+            (lenl == sizeof(android_log_event_int_t)) &&
+            !fastcmp<memcmp>(msgl, msgr,
+                             sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+            (elem->getTag() == LIBLOG_LOG_TAG)) return SAME_LIBLOG;
+    }
 
     // audit message (except sequence number) identical?
     static const char avc[] = "): avc: ";
@@ -154,20 +169,22 @@
     if (last->isBinary()) {
         if (fastcmp<memcmp>(msgl, msgr,
                             sizeof(android_log_event_string_t) -
-                                sizeof(int32_t))) return false;
+                                sizeof(int32_t))) return DIFFERENT;
         msgl += sizeof(android_log_event_string_t);
         lenl -= sizeof(android_log_event_string_t);
         msgr += sizeof(android_log_event_string_t);
         lenr -= sizeof(android_log_event_string_t);
     }
     const char *avcl = android::strnstr(msgl, lenl, avc);
-    if (!avcl) return false;
+    if (!avcl) return DIFFERENT;
     lenl -= avcl - msgl;
     const char *avcr = android::strnstr(msgr, lenr, avc);
-    if (!avcr) return false;
+    if (!avcr) return DIFFERENT;
     lenr -= avcr - msgr;
-    if (lenl != lenr) return false;
-    return !fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc), lenl);
+    if (lenl != lenr) return DIFFERENT;
+    if (fastcmp<memcmp>(avcl + strlen(avc),
+                        avcr + strlen(avc), lenl)) return DIFFERENT;
+    return SAME;
 }
 
 int LogBuffer::log(log_id_t log_id, log_time realtime,
@@ -206,8 +223,113 @@
     if (currentLast) {
         LogBufferElement *dropped = droppedElements[log_id];
         unsigned short count = dropped ? dropped->getDropped() : 0;
-        if (identical(elem, currentLast)) {
+        //
+        // State Init
+        //     incoming:
+        //         dropped = NULL
+        //         currentLast = NULL;
+        //         elem = incoming message
+        //     outgoing:
+        //         dropped = NULL -> State 0
+        //         currentLast = copy of elem
+        //         log elem
+        // State 0
+        //     incoming:
+        //         count = 0
+        //         dropped = NULL
+        //         currentLast = copy of last message
+        //         elem = incoming message
+        //     outgoing: if match != DIFFERENT
+        //         dropped = copy of first identical message -> State 1
+        //         currentLast = reference to elem
+        //     break: if match == DIFFERENT
+        //         dropped = NULL -> State 0
+        //         delete copy of last message (incoming currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        // State 1
+        //     incoming:
+        //         count = 0
+        //         dropped = copy of first identical message
+        //         currentLast = reference to last held-back incoming
+        //                       message
+        //         elem = incoming message
+        //     outgoing: if match == SAME
+        //         delete copy of first identical message (dropped)
+        //         dropped = reference to last held-back incoming
+        //                   message set to chatty count of 1 -> State 2
+        //         currentLast = reference to elem
+        //     outgoing: if match == SAME_LIBLOG
+        //         dropped = copy of first identical message -> State 1
+        //         take sum of currentLast and elem
+        //         if sum overflows:
+        //             log currentLast
+        //             currentLast = reference to elem
+        //         else
+        //             delete currentLast
+        //             currentLast = reference to elem, sum liblog.
+        //     break: if match == DIFFERENT
+        //         delete dropped
+        //         dropped = NULL -> State 0
+        //         log reference to last held-back (currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        // State 2
+        //     incoming:
+        //         count = chatty count
+        //         dropped = chatty message holding count
+        //         currentLast = reference to last held-back incoming
+        //                       message.
+        //         dropped = chatty message holding count
+        //         elem = incoming message
+        //     outgoing: if match != DIFFERENT
+        //         delete chatty message holding count
+        //         dropped = reference to last held-back incoming
+        //                   message, set to chatty count + 1
+        //         currentLast = reference to elem
+        //     break: if match == DIFFERENT
+        //         log dropped (chatty message)
+        //         dropped = NULL -> State 0
+        //         log reference to last held-back (currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        //
+        enum match_type match = identical(elem, currentLast);
+        if (match != DIFFERENT) {
             if (dropped) {
+                // Sum up liblog tag messages?
+                if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
+                    android_log_event_int_t* event =
+                        reinterpret_cast<android_log_event_int_t*>(
+                            const_cast<char*>(currentLast->getMsg()));
+                    //
+                    // To unit test, differentiate with something like:
+                    //    event->header.tag = htole32(CHATTY_LOG_TAG);
+                    // here, then instead of delete currentLast below,
+                    // log(currentLast) to see the incremental sums form.
+                    //
+                    uint32_t swab = event->payload.data;
+                    unsigned long long total = htole32(swab);
+                    event = reinterpret_cast<android_log_event_int_t*>(
+                            const_cast<char*>(elem->getMsg()));
+                    swab = event->payload.data;
+
+                    lastLoggedElements[LOG_ID_EVENTS] = elem;
+                    total += htole32(swab);
+                    // check for overflow
+                    if (total >= UINT32_MAX) {
+                        log(currentLast);
+                        pthread_mutex_unlock(&mLogElementsLock);
+                        return len;
+                    }
+                    stats.add(currentLast);
+                    stats.subtract(currentLast);
+                    delete currentLast;
+                    swab = total;
+                    event->payload.data = htole32(swab);
+                    pthread_mutex_unlock(&mLogElementsLock);
+                    return len;
+                }
                 if (count == USHRT_MAX) {
                     log(dropped);
                     count = 1;
@@ -226,13 +348,15 @@
             pthread_mutex_unlock(&mLogElementsLock);
             return len;
         }
-        if (dropped) {
-            log(dropped);
+        if (dropped) { // State 1 or 2
+            if (count) { // State 2
+               log(dropped); // report chatty
+            } else { // State 1
+               delete dropped;
+            }
             droppedElements[log_id] = NULL;
-        }
-        if (count) {
-            log(currentLast);
-        } else {
+            log(currentLast); // report last message in the series
+        } else { // State 0
             delete currentLast;
         }
     }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 31de2a0..273150e 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -27,6 +27,8 @@
 
 #include "LogStatistics.h"
 
+size_t LogStatistics::SizesTotal;
+
 LogStatistics::LogStatistics() : enable(false) {
     log_id_for_each(id) {
         mSizes[id] = 0;
@@ -39,6 +41,8 @@
 
 namespace android {
 
+size_t sizesTotal() { return LogStatistics::sizesTotal(); }
+
 // caller must own and free character string
 char *pidToName(pid_t pid) {
     char *retval = NULL;
@@ -80,6 +84,7 @@
         // elements, but we must recognize the manufactured dropped
         // entry as not contributing to the lifetime totals.
         mSizesTotal[log_id] += size;
+        SizesTotal += size;
         ++mElementsTotal[log_id];
     }
 
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 1d687bc..7acef6d 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -475,6 +475,7 @@
     size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
+    static size_t SizesTotal;
     bool enable;
 
     // uid to size list
@@ -557,6 +558,7 @@
     }
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
+    static size_t sizesTotal() { return SizesTotal; }
 
     std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
 
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index d300a2a..70f24e4 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -33,7 +33,9 @@
 char *uidToName(uid_t uid);
 void prdebug(const char *fmt, ...) __printflike(1, 2);
 
-// Furnished in LogStatistics.cpp. Caller must own and free returned value
+// Furnished in LogStatistics.cpp.
+size_t sizesTotal();
+// Caller must own and free returned value
 char *pidToName(pid_t pid);
 char *tidToName(pid_t tid);