logd: Add TID statistics

Bug: 19608965
Change-Id: Ifbf0b00c48ef12b5970b9f9f217bd1dd8f587f2c
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 164faa9..975265b 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -58,7 +58,7 @@
 }
 
 // caller must own and free character string
-static char *tidToName(pid_t tid) {
+char *android::tidToName(pid_t tid) {
     char *retval = NULL;
     char buffer[256];
     snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
@@ -108,9 +108,9 @@
     static const char format_uid[] = "uid=%u%s too chatty%s, expire %u line%s";
 
     char *name = parent->uidToName(mUid);
-    char *commName = tidToName(mTid);
+    char *commName = android::tidToName(mTid);
     if (!commName && (mTid != mPid)) {
-        commName = tidToName(mPid);
+        commName = android::tidToName(mPid);
     }
     if (!commName) {
         commName = parent->pidToName(mPid);
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 75ec59e..55f3d44 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -35,6 +35,7 @@
 
 // Furnished in LogStatistics.cpp. Caller must own and free returned value
 char *pidToName(pid_t pid);
+char *tidToName(pid_t tid);
 
 // Furnished in main.cpp. Thread safe.
 const char *tagToName(uint32_t tag);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 4511e0b..8fc4e56 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -80,6 +80,7 @@
     }
 
     pidTable.add(e->getPid(), e);
+    tidTable.add(e->getTid(), e);
 
     uint32_t tag = e->getTag();
     if (tag) {
@@ -100,6 +101,7 @@
     }
 
     pidTable.subtract(e->getPid(), e);
+    tidTable.subtract(e->getTid(), e);
 
     uint32_t tag = e->getTag();
     if (tag) {
@@ -121,6 +123,7 @@
     }
 
     pidTable.drop(e->getPid(), e);
+    tidTable.drop(e->getTid(), e);
 }
 
 // caller must own and free character string
@@ -383,6 +386,63 @@
         }
     }
 
+    if (enable) {
+        // Tid table
+        bool headerPrinted = false;
+        // sort() returns list of references, unique_ptr makes sure self-delete
+        std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
+        ssize_t index = -1;
+        while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
+            const TidEntry *entry = sorted[index];
+            uid_t u = entry->getUid();
+            if ((uid != AID_ROOT) && (u != uid)) {
+                continue;
+            }
+
+            if (!headerPrinted) { // Only print header if we have table to print
+                output.appendFormat("\n\n");
+                android::String8 name("Chattiest TIDs:");
+                android::String8 size("Size");
+                android::String8 pruned("Pruned");
+                format_line(output, name, size, pruned);
+
+                name.setTo("  TID/UID   COMM");
+                size.setTo("BYTES");
+                pruned.setTo("LINES");
+                format_line(output, name, size, pruned);
+
+                headerPrinted = true;
+            }
+
+            android::String8 name("");
+            name.appendFormat("%5u/%u", entry->getKey(), u);
+            const char *n = entry->getName();
+            if (n) {
+                name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", n);
+            } else {
+                // if we do not have a PID name, lets punt to try UID name?
+                char *un = uidToName(u);
+                if (un) {
+                    name.appendFormat("%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", un);
+                    free(un);
+                }
+                // We tried, better to not have a name at all, we still
+                // have TID/UID by number to report in any case.
+            }
+
+            android::String8 size("");
+            size.appendFormat("%zu", entry->getSizes());
+
+            android::String8 pruned("");
+            size_t dropped = entry->getDropped();
+            if (dropped) {
+                pruned.appendFormat("%zu", dropped);
+            }
+
+            format_line(output, name, size, pruned);
+        }
+    }
+
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
         // Tag table
         bool headerPrinted = false;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d21a75b..f60f3ed 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -209,6 +209,58 @@
     }
 };
 
+struct TidEntry : public EntryBaseDropped {
+    const pid_t tid;
+    uid_t uid;
+    char *name;
+
+    TidEntry(pid_t t):
+        EntryBaseDropped(),
+        tid(t),
+        uid(android::pidToUid(t)),
+        name(android::tidToName(tid)) { }
+    TidEntry(LogBufferElement *e):
+        EntryBaseDropped(e),
+        tid(e->getTid()),
+        uid(e->getUid()),
+        name(android::tidToName(e->getTid())) { }
+    TidEntry(const TidEntry &c):
+        EntryBaseDropped(c),
+        tid(c.tid),
+        uid(c.uid),
+        name(c.name ? strdup(c.name) : NULL) { }
+    ~TidEntry() { free(name); }
+
+    const pid_t&getKey() const { return tid; }
+    const uid_t&getUid() const { return uid; }
+    const char*getName() const { return name; }
+
+    inline void add(pid_t t) {
+        if (name && !strncmp(name, "zygote", 6)) {
+            free(name);
+            name = NULL;
+        }
+        if (!name) {
+            char *n = android::tidToName(t);
+            if (n) {
+                name = n;
+            }
+        }
+    }
+
+    inline void add(LogBufferElement *e) {
+        uid_t u = e->getUid();
+        if (getUid() != u) {
+            uid = u;
+            free(name);
+            name = android::tidToName(e->getTid());
+        } else {
+            add(e->getTid());
+        }
+        EntryBaseDropped::add(e);
+    }
+};
+
 struct TagEntry : public EntryBase {
     const uint32_t tag;
     uid_t uid;
@@ -247,6 +299,10 @@
     typedef LogHashtable<pid_t, PidEntry> pidTable_t;
     pidTable_t pidTable;
 
+    // tid to uid list
+    typedef LogHashtable<pid_t, TidEntry> tidTable_t;
+    tidTable_t tidTable;
+
     // tag list
     typedef LogHashtable<uint32_t, TagEntry> tagTable_t;
     tagTable_t tagTable;