logd: improve details on chatty records

- Report applications UID, TID/PID by name.
- change wording to have an accurate connotation
- drop privilege check since filtered upstream

Bug: 19608965
Bug: 20334069
Bug: 20370119
Change-Id: I2b1c26580b4c2de293874214ff5ae745546f3cca
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 62f29e7..b6b6124 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -631,7 +631,7 @@
         pthread_mutex_unlock(&mLogElementsLock);
 
         // range locking in LastLogTimes looks after us
-        max = element->flushTo(reader);
+        max = element->flushTo(reader, this);
 
         if (max == element->FLUSH_ERROR) {
             return max;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 9ee243d..00b19b6 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -74,6 +74,7 @@
     // helper
     char *pidToName(pid_t pid) { return stats.pidToName(pid); }
     uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
+    char *uidToName(uid_t uid) { return stats.uidToName(uid); }
 
 private:
     void maybePrune(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index a173e63..6a05700 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
 #include <endian.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
@@ -48,19 +50,89 @@
     delete [] mMsg;
 }
 
-// assumption: mMsg == NULL
-size_t LogBufferElement::populateDroppedMessage(char *&buffer, bool privileged) {
-    static const char format_uid[] = "uid=%u dropped=%u";
-    static const size_t unprivileged_offset = 7;
-    static const char tag[] = "logd";
-
-    size_t len;
-    if (privileged) {
-        len = snprintf(NULL, 0, format_uid, mUid, mDropped);
-    } else {
-        len = snprintf(NULL, 0, format_uid + unprivileged_offset, mDropped);
+// caller must own and free character string
+static char *tidToName(pid_t tid) {
+    char *retval = NULL;
+    char buffer[256];
+    snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
+    int fd = open(buffer, O_RDONLY);
+    if (fd >= 0) {
+        ssize_t ret = read(fd, buffer, sizeof(buffer));
+        if (ret >= (ssize_t)sizeof(buffer)) {
+            ret = sizeof(buffer) - 1;
+        }
+        while ((ret > 0) && isspace(buffer[ret - 1])) {
+            --ret;
+        }
+        if (ret > 0) {
+            buffer[ret] = '\0';
+            retval = strdup(buffer);
+        }
+        close(fd);
     }
 
+    // if nothing for comm, check out cmdline
+    char *name = android::pidToName(tid);
+    if (!retval) {
+        retval = name;
+        name = NULL;
+    }
+
+    // check if comm is truncated, see if cmdline has full representation
+    if (name) {
+        // impossible for retval to be NULL if name not NULL
+        size_t retval_len = strlen(retval);
+        size_t name_len = strlen(name);
+        // KISS: ToDo: Only checks prefix truncated, not suffix, or both
+        if ((retval_len < name_len) && !strcmp(retval, name + name_len - retval_len)) {
+            free(retval);
+            retval = name;
+        } else {
+            free(name);
+        }
+    }
+    return retval;
+}
+
+// assumption: mMsg == NULL
+size_t LogBufferElement::populateDroppedMessage(char *&buffer,
+        LogBuffer *parent) {
+    static const char tag[] = "logd";
+    static const char format_uid[] = "uid=%u%s too chatty%s, expire %u line%s";
+
+    char *name = parent->uidToName(mUid);
+    char *commName = tidToName(mTid);
+    if (!commName && (mTid != mPid)) {
+        commName = tidToName(mPid);
+    }
+    if (!commName) {
+        commName = parent->pidToName(mPid);
+    }
+    if (name && commName && !strcmp(name, commName)) {
+        free(commName);
+        commName = NULL;
+    }
+    if (name) {
+        char *p = NULL;
+        asprintf(&p, "(%s)", name);
+        if (p) {
+            free(name);
+            name = p;
+        }
+    }
+    if (commName) {
+        char *p = NULL;
+        asprintf(&p, " comm=%s", commName);
+        if (p) {
+            free(commName);
+            commName = p;
+        }
+    }
+    // identical to below to calculate the buffer size required
+    size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "",
+                          commName ? commName : "",
+                          mDropped, (mDropped > 1) ? "s" : "");
+
     size_t hdrLen;
     if (mLogId == LOG_ID_EVENTS) {
         hdrLen = sizeof(android_log_event_string_t);
@@ -70,6 +142,8 @@
 
     buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
     if (!buffer) {
+        free(name);
+        free(commName);
         return 0;
     }
 
@@ -86,16 +160,16 @@
         strcpy(buffer + 1, tag);
     }
 
-    if (privileged) {
-        snprintf(buffer + hdrLen, len + 1, format_uid, mUid, mDropped);
-    } else {
-        snprintf(buffer + hdrLen, len + 1, format_uid + unprivileged_offset, mDropped);
-    }
+    snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
+             commName ? commName : "",
+             mDropped, (mDropped > 1) ? "s" : "");
+    free(name);
+    free(commName);
 
     return retval;
 }
 
-uint64_t LogBufferElement::flushTo(SocketClient *reader) {
+uint64_t LogBufferElement::flushTo(SocketClient *reader, LogBuffer *parent) {
     struct logger_entry_v3 entry;
 
     memset(&entry, 0, sizeof(struct logger_entry_v3));
@@ -114,7 +188,7 @@
     char *buffer = NULL;
 
     if (!mMsg) {
-        entry.len = populateDroppedMessage(buffer, clientHasLogCredentials(reader));
+        entry.len = populateDroppedMessage(buffer, parent);
         if (!entry.len) {
             return mSequence;
         }
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 7b6456d..059c031 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -28,15 +28,19 @@
 namespace android {
 
 // Furnished in main.cpp. Caller must own and free returned value
-// This function is designed for a single caller and is NOT thread-safe
 char *uidToName(uid_t uid);
 
+// Furnished in LogStatistics.cpp. Caller must own and free returned value
+char *pidToName(pid_t pid);
+
 }
 
 static inline bool worstUidEnabledForLogid(log_id_t id) {
     return (id != LOG_ID_CRASH) && (id != LOG_ID_EVENTS);
 }
 
+class LogBuffer;
+
 class LogBufferElement {
     const log_id_t mLogId;
     const uid_t mUid;
@@ -52,7 +56,8 @@
     static atomic_int_fast64_t sequence;
 
     // assumption: mMsg == NULL
-    size_t populateDroppedMessage(char *&buffer, bool privileged);
+    size_t populateDroppedMessage(char *&buffer,
+                                  LogBuffer *parent);
 
 public:
     LogBufferElement(log_id_t log_id, log_time realtime,
@@ -78,7 +83,7 @@
     log_time getRealTime(void) const { return mRealTime; }
 
     static const uint64_t FLUSH_ERROR;
-    uint64_t flushTo(SocketClient *writer);
+    uint64_t flushTo(SocketClient *writer, LogBuffer *parent);
 };
 
 #endif
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index eadc4dd..b063630 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -39,7 +39,7 @@
 namespace android {
 
 // caller must own and free character string
-static char *pidToName(pid_t pid) {
+char *pidToName(pid_t pid) {
     char *retval = NULL;
     if (pid == 0) { // special case from auditd for kernel
         retval = strdup("logd.auditd");
diff --git a/logd/main.cpp b/logd/main.cpp
index eb29596..237c7c1 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -210,18 +210,26 @@
     return NULL;
 }
 
+static sem_t sem_name;
+
 char *android::uidToName(uid_t u) {
     if (!u || !reinit_running) {
         return NULL;
     }
 
-    // Not multi-thread safe, we know there is only one caller
+    sem_wait(&sem_name);
+
+    // Not multi-thread safe, we use sem_name to protect
     uid = u;
 
     name = NULL;
     sem_post(&reinit);
     sem_wait(&uidName);
-    return name;
+    char *ret = name;
+
+    sem_post(&sem_name);
+
+    return ret;
 }
 
 // Serves as a global method to trigger reinitialization
@@ -277,6 +285,7 @@
     // Reinit Thread
     sem_init(&reinit, 0, 0);
     sem_init(&uidName, 0, 0);
+    sem_init(&sem_name, 0, 1);
     pthread_attr_t attr;
     if (!pthread_attr_init(&attr)) {
         struct sched_param param;