From f99a7d602a6fe90058e47d5e6140dfc9b30f7481 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 19 Apr 2017 14:39:21 -0700 Subject: [PATCH] logd: add Chattiest LOG_TAG statistics Report global LOG_TAG usage. Switch NULL to nullptr and use const more accurately. Test: gTest liblog-unit-tests, logd-unit-tests & logcat-unit-tests Test: manual: inspect logcat -S results around 'Chattiest TAGs' Test: logcat -b all -c ; logcat -b all -S ; then confirm clear Bug: 37254265 Change-Id: I3696c0d8da3ba24f99f670aafba1e45f8cb3ab14 --- logd/LogStatistics.cpp | 73 +++++++++++++++ logd/LogStatistics.h | 203 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 253 insertions(+), 23 deletions(-) diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp index 0b6b28cf8..d20d90e58 100644 --- a/logd/LogStatistics.cpp +++ b/logd/LogStatistics.cpp @@ -152,6 +152,10 @@ void LogStatistics::add(LogBufferElement* element) { tagTable.add(tag, element); } } + + if (!element->getDropped()) { + tagNameTable.add(TagNameKey(element), element); + } } void LogStatistics::subtract(LogBufferElement* element) { @@ -191,6 +195,10 @@ void LogStatistics::subtract(LogBufferElement* element) { tagTable.subtract(tag, element); } } + + if (!element->getDropped()) { + tagNameTable.subtract(TagNameKey(element), element); + } } // Atomically set an entry to drop @@ -225,6 +233,8 @@ void LogStatistics::drop(LogBufferElement* element) { tagTable.drop(tag, element); } } + + tagNameTable.subtract(TagNameKey(element), element); } // caller must own and free character string @@ -505,6 +515,62 @@ std::string TagEntry::format(const LogStatistics& /* stat */, return formatLine(name, size, pruned); } +std::string TagNameEntry::formatHeader(const std::string& name, + log_id_t /* id */) const { + return formatLine(name, std::string("Size"), std::string("")) + + formatLine(std::string(" TID/PID/UID LOG_TAG NAME"), + std::string("BYTES"), std::string("")); +} + +std::string TagNameEntry::format(const LogStatistics& /* stat */, + log_id_t /* id */) const { + std::string name; + pid_t tid = getTid(); + pid_t pid = getPid(); + std::string pidstr; + if (pid != (pid_t)-1) { + pidstr = android::base::StringPrintf("%u", pid); + if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr; + } + int len = 9 - pidstr.length(); + if (len < 0) len = 0; + if ((tid == (pid_t)-1) || (tid == pid)) { + name = android::base::StringPrintf("%*s", len, ""); + } else { + name = android::base::StringPrintf("%*u", len, tid); + } + name += pidstr; + uid_t uid = getUid(); + if (uid != (uid_t)-1) { + name += android::base::StringPrintf("/%u", uid); + } + + std::string size = android::base::StringPrintf("%zu", getSizes()); + + const char* nameTmp = getName(); + if (nameTmp) { + size_t lenSpace = std::max(16 - name.length(), (size_t)1); + size_t len = EntryBaseConstants::total_len - + EntryBaseConstants::pruned_len - size.length() - + name.length() - lenSpace - 2; + size_t lenNameTmp = strlen(nameTmp); + while ((len < lenNameTmp) && (lenSpace > 1)) { + ++len; + --lenSpace; + } + name += android::base::StringPrintf("%*s", (int)lenSpace, ""); + if (len < lenNameTmp) { + name += "..."; + nameTmp += lenNameTmp - std::max(len - 3, (size_t)1); + } + name += nameTmp; + } + + std::string pruned = ""; + + return formatLine(name, size, pruned); +} + static std::string formatMsec(uint64_t val) { static const unsigned subsecDigits = 3; static const uint64_t sec = MS_PER_SEC; @@ -740,6 +806,13 @@ std::string LogStatistics::format(uid_t uid, pid_t pid, securityTagTable.format(*this, 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); + } + return output; } diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h index e6f01d64d..945fc0ab6 100644 --- a/logd/LogStatistics.h +++ b/logd/LogStatistics.h @@ -18,10 +18,14 @@ #define _LOGD_LOG_STATISTICS_H__ #include +#include +#include #include +#include #include #include // std::max +#include #include #include // std::string #include @@ -30,6 +34,7 @@ #include #include #include +#include #include "LogBufferElement.h" #include "LogUtils.h" @@ -77,7 +82,7 @@ class LogHashtable { std::unique_ptr sort(uid_t uid, pid_t pid, size_t len) const { if (!len) { - std::unique_ptr sorted(NULL); + std::unique_ptr sorted(nullptr); return sorted; } @@ -112,7 +117,7 @@ class LogHashtable { return sorted; } - inline iterator add(TKey key, LogBufferElement* element) { + inline iterator add(const TKey& key, const LogBufferElement* element) { iterator it = map.find(key); if (it == map.end()) { it = map.insert(std::make_pair(key, TEntry(element))).first; @@ -132,14 +137,21 @@ class LogHashtable { return it; } - void subtract(TKey key, LogBufferElement* element) { + void subtract(TKey&& key, const LogBufferElement* element) { + iterator it = map.find(std::move(key)); + if ((it != map.end()) && it->second.subtract(element)) { + map.erase(it); + } + } + + void subtract(const TKey& key, const LogBufferElement* element) { iterator it = map.find(key); if ((it != map.end()) && it->second.subtract(element)) { map.erase(it); } } - inline void drop(TKey key, LogBufferElement* element) { + inline void drop(TKey key, const LogBufferElement* element) { iterator it = map.find(key); if (it != map.end()) { it->second.drop(element); @@ -199,17 +211,18 @@ struct EntryBase { EntryBase() : size(0) { } - explicit EntryBase(LogBufferElement* element) : size(element->getMsgLen()) { + explicit EntryBase(const LogBufferElement* element) + : size(element->getMsgLen()) { } size_t getSizes() const { return size; } - inline void add(LogBufferElement* element) { + inline void add(const LogBufferElement* element) { size += element->getMsgLen(); } - inline bool subtract(LogBufferElement* element) { + inline bool subtract(const LogBufferElement* element) { size -= element->getMsgLen(); return !size; } @@ -240,7 +253,7 @@ struct EntryBaseDropped : public EntryBase { EntryBaseDropped() : dropped(0) { } - explicit EntryBaseDropped(LogBufferElement* element) + explicit EntryBaseDropped(const LogBufferElement* element) : EntryBase(element), dropped(element->getDropped()) { } @@ -248,15 +261,15 @@ struct EntryBaseDropped : public EntryBase { return dropped; } - inline void add(LogBufferElement* element) { + inline void add(const LogBufferElement* element) { dropped += element->getDropped(); EntryBase::add(element); } - inline bool subtract(LogBufferElement* element) { + inline bool subtract(const LogBufferElement* element) { dropped -= element->getDropped(); return EntryBase::subtract(element) && !dropped; } - inline void drop(LogBufferElement* element) { + inline void drop(const LogBufferElement* element) { dropped += 1; EntryBase::subtract(element); } @@ -266,7 +279,7 @@ struct UidEntry : public EntryBaseDropped { const uid_t uid; pid_t pid; - explicit UidEntry(LogBufferElement* element) + explicit UidEntry(const LogBufferElement* element) : EntryBaseDropped(element), uid(element->getUid()), pid(element->getPid()) { @@ -282,7 +295,7 @@ struct UidEntry : public EntryBaseDropped { return pid; } - inline void add(LogBufferElement* element) { + inline void add(const LogBufferElement* element) { if (pid != element->getPid()) { pid = -1; } @@ -308,7 +321,7 @@ struct PidEntry : public EntryBaseDropped { uid(android::pidToUid(pid)), name(android::pidToName(pid)) { } - explicit PidEntry(LogBufferElement* element) + explicit PidEntry(const LogBufferElement* element) : EntryBaseDropped(element), pid(element->getPid()), uid(element->getUid()), @@ -318,7 +331,7 @@ struct PidEntry : public EntryBaseDropped { : EntryBaseDropped(element), pid(element.pid), uid(element.uid), - name(element.name ? strdup(element.name) : NULL) { + name(element.name ? strdup(element.name) : nullptr) { } ~PidEntry() { free(name); @@ -340,14 +353,14 @@ struct PidEntry : public EntryBaseDropped { inline void add(pid_t newPid) { if (name && !fastcmp(name, "zygote", 6)) { free(name); - name = NULL; + name = nullptr; } if (!name) { name = android::pidToName(newPid); } } - inline void add(LogBufferElement* element) { + inline void add(const LogBufferElement* element) { uid_t incomingUid = element->getUid(); if (getUid() != incomingUid) { uid = incomingUid; @@ -376,7 +389,7 @@ struct TidEntry : public EntryBaseDropped { uid(android::pidToUid(tid)), name(android::tidToName(tid)) { } - explicit TidEntry(LogBufferElement* element) + explicit TidEntry(const LogBufferElement* element) : EntryBaseDropped(element), tid(element->getTid()), pid(element->getPid()), @@ -388,7 +401,7 @@ struct TidEntry : public EntryBaseDropped { tid(element.tid), pid(element.pid), uid(element.uid), - name(element.name ? strdup(element.name) : NULL) { + name(element.name ? strdup(element.name) : nullptr) { } ~TidEntry() { free(name); @@ -413,14 +426,14 @@ struct TidEntry : public EntryBaseDropped { inline void add(pid_t incomingTid) { if (name && !fastcmp(name, "zygote", 6)) { free(name); - name = NULL; + name = nullptr; } if (!name) { name = android::tidToName(incomingTid); } } - inline void add(LogBufferElement* element) { + inline void add(const LogBufferElement* element) { uid_t incomingUid = element->getUid(); pid_t incomingPid = element->getPid(); if ((getUid() != incomingUid) || (getPid() != incomingPid)) { @@ -443,7 +456,7 @@ struct TagEntry : public EntryBaseDropped { pid_t pid; uid_t uid; - explicit TagEntry(LogBufferElement* element) + explicit TagEntry(const LogBufferElement* element) : EntryBaseDropped(element), tag(element->getTag()), pid(element->getPid()), @@ -463,7 +476,7 @@ struct TagEntry : public EntryBaseDropped { return android::tagToName(tag); } - inline void add(LogBufferElement* element) { + inline void add(const LogBufferElement* element) { if (uid != element->getUid()) { uid = -1; } @@ -477,6 +490,144 @@ struct TagEntry : public EntryBaseDropped { std::string format(const LogStatistics& stat, log_id_t id) const; }; +struct TagNameKey { + std::string* alloc; + std::experimental::string_view name; // Saves space if const char* + + explicit TagNameKey(const LogBufferElement* element) + : alloc(nullptr), name("", strlen("")) { + if (element->isBinary()) { + uint32_t tag = element->getTag(); + if (tag) { + const char* cp = android::tagToName(tag); + if (cp) { + name = std::experimental::string_view(cp, strlen(cp)); + return; + } + } + alloc = new std::string( + android::base::StringPrintf("[%" PRIu32 "]", tag)); + if (!alloc) return; + name = std::experimental::string_view(alloc->c_str(), alloc->size()); + return; + } + const char* msg = element->getMsg(); + if (!msg) { + name = std::experimental::string_view("chatty", strlen("chatty")); + return; + } + ++msg; + unsigned short len = element->getMsgLen(); + len = (len <= 1) ? 0 : strnlen(msg, len - 1); + if (!len) { + name = std::experimental::string_view("", strlen("")); + return; + } + alloc = new std::string(msg, len); + if (!alloc) return; + name = std::experimental::string_view(alloc->c_str(), alloc->size()); + } + + explicit TagNameKey(TagNameKey&& rval) + : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) { + rval.alloc = nullptr; + } + + explicit TagNameKey(const TagNameKey& rval) + : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr), + name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) { + } + + ~TagNameKey() { + if (alloc) delete alloc; + } + + operator const std::experimental::string_view() const { + return name; + } + + const char* data() const { + return name.data(); + } + size_t length() const { + return name.length(); + } + + bool operator==(const TagNameKey& rval) const { + if (length() != rval.length()) return false; + if (length() == 0) return true; + return fastcmp(data(), rval.data(), length()) == 0; + } + bool operator!=(const TagNameKey& rval) const { + return !(*this == rval); + } + + size_t getAllocLength() const { + return alloc ? alloc->length() + 1 + sizeof(std::string) : 0; + } +}; + +// Hash for TagNameKey +template <> +struct std::hash + : public std::unary_function { + size_t operator()(const TagNameKey& __t) const noexcept { + if (!__t.length()) return 0; + return std::hash()( + std::experimental::string_view(__t)); + } +}; + +struct TagNameEntry : public EntryBase { + pid_t tid; + pid_t pid; + uid_t uid; + TagNameKey name; + + explicit TagNameEntry(const LogBufferElement* element) + : EntryBase(element), + tid(element->getTid()), + pid(element->getPid()), + uid(element->getUid()), + name(element) { + } + + const TagNameKey& getKey() const { + return name; + } + const pid_t& getTid() const { + return tid; + } + const pid_t& getPid() const { + return pid; + } + const uid_t& getUid() const { + return uid; + } + const char* getName() const { + return name.data(); + } + size_t getNameAllocLength() const { + return name.getAllocLength(); + } + + inline void add(const LogBufferElement* element) { + if (uid != element->getUid()) { + uid = -1; + } + if (pid != element->getPid()) { + pid = -1; + } + if (tid != element->getTid()) { + tid = -1; + } + EntryBase::add(element); + } + + std::string formatHeader(const std::string& name, log_id_t id) const; + std::string format(const LogStatistics& stat, log_id_t id) const; +}; + template class LogFindWorst { std::unique_ptr sorted; @@ -550,9 +701,14 @@ class LogStatistics { // security tag list tagTable_t securityTagTable; + // global tag list + typedef LogHashtable tagNameTable_t; + tagNameTable_t tagNameTable; + size_t sizeOf() const { size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() + tagTable.sizeOf() + securityTagTable.sizeOf() + + tagNameTable.sizeOf() + (pidTable.size() * sizeof(pidTable_t::iterator)) + (tagTable.size() * sizeof(tagTable_t::iterator)); for (auto it : pidTable) { @@ -563,6 +719,7 @@ class LogStatistics { const char* name = it.second.getName(); if (name) size += strlen(name) + 1; } + for (auto it : tagNameTable) size += it.second.getNameAllocLength(); log_id_for_each(id) { size += uidTable[id].sizeOf(); size += uidTable[id].size() * sizeof(uidTable_t::iterator);