From 34facab86b0fe7ec613de92b46b637f864fb0682 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 6 Feb 2014 14:48:50 -0800 Subject: logd: liblog: logcat: Add Statistics - logd add statistical collection and formatting - liblog add android_logger_get_statistics call - logcat add -S flag - logcat add -b all (cherry picked from commit 51a29c8dc445e4fb89860561933e54a231e6ffb4) Change-Id: I521753b1969ecd4590c956aeeb1557d101059d67 --- logd/LogStatistics.cpp | 570 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 logd/LogStatistics.cpp (limited to 'logd/LogStatistics.cpp') diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp new file mode 100644 index 0000000..50ce442 --- /dev/null +++ b/logd/LogStatistics.cpp @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include "LogStatistics.h" + +PidStatistics::PidStatistics(pid_t pid) + : pid(pid) + , mSizesTotal(0) + , mElementsTotal(0) + , mSizes(0) + , mElements(0) { } + +void PidStatistics::add(unsigned short size) { + mSizesTotal += size; + ++mElementsTotal; + mSizes += size; + ++mElements; +} + +bool PidStatistics::subtract(unsigned short size) { + mSizes -= size; + --mElements; + return mElements == 0 && kill(pid, 0); +} + +void PidStatistics::addTotal(size_t size, size_t element) { + if (pid == gone) { + mSizesTotal += size; + mElementsTotal += element; + } +} + +UidStatistics::UidStatistics(uid_t uid) + : uid(uid) { + Pids.clear(); +} + +UidStatistics::~UidStatistics() { + PidStatisticsCollection::iterator it; + for (it = begin(); it != end();) { + delete (*it); + it = Pids.erase(it); + } +} + +void UidStatistics::add(unsigned short size, pid_t pid) { + PidStatistics *p; + PidStatisticsCollection::iterator last; + PidStatisticsCollection::iterator it; + for (last = it = begin(); it != end(); last = it, ++it) { + p = *it; + if (pid == p->getPid()) { + p->add(size); + // poor-man sort, bubble upwards if bigger than last + if ((last != it) && ((*last)->sizesTotal() < p->sizesTotal())) { + Pids.erase(it); + Pids.insert(last, p); + } + return; + } + } + // poor-man sort, insert if bigger than last or last is the gone entry. + bool insert = (last != it) + && ((p->getPid() == p->gone) + || ((*last)->sizesTotal() < (size_t) size)); + p = new PidStatistics(pid); + if (insert) { + Pids.insert(last, p); + } else { + Pids.push_back(p); + } + p->add(size); +} + +void UidStatistics::subtract(unsigned short size, pid_t pid) { + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if (pid == p->getPid()) { + if (p->subtract(size)) { + size_t szsTotal = p->sizesTotal(); + size_t elsTotal = p->elementsTotal(); + delete p; + Pids.erase(it); + it = end(); + --it; + if (it == end()) { + p = new PidStatistics(p->gone); + Pids.push_back(p); + } else { + p = *it; + if (p->getPid() != p->gone) { + p = new PidStatistics(p->gone); + Pids.push_back(p); + } + } + p->addTotal(szsTotal, elsTotal); + } + return; + } + } +} + +size_t UidStatistics::sizes(pid_t pid) { + size_t sizes = 0; + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if ((pid == pid_all) || (pid == p->getPid())) { + sizes += p->sizes(); + } + } + return sizes; +} + +size_t UidStatistics::elements(pid_t pid) { + size_t elements = 0; + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if ((pid == pid_all) || (pid == p->getPid())) { + elements += p->elements(); + } + } + return elements; +} + +size_t UidStatistics::sizesTotal(pid_t pid) { + size_t sizes = 0; + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if ((pid == pid_all) || (pid == p->getPid())) { + sizes += p->sizesTotal(); + } + } + return sizes; +} + +size_t UidStatistics::elementsTotal(pid_t pid) { + size_t elements = 0; + PidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + PidStatistics *p = *it; + if ((pid == pid_all) || (pid == p->getPid())) { + elements += p->elementsTotal(); + } + } + return elements; +} + +LidStatistics::LidStatistics() { + Uids.clear(); +} + +LidStatistics::~LidStatistics() { + UidStatisticsCollection::iterator it; + for (it = begin(); it != end();) { + delete (*it); + it = Uids.erase(it); + } +} + +void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) { + UidStatistics *u; + UidStatisticsCollection::iterator it; + UidStatisticsCollection::iterator last; + + if (uid == (uid_t) -1) { // init + uid = (uid_t) AID_ROOT; + } + + for (last = it = begin(); it != end(); last = it, ++it) { + u = *it; + if (uid == u->getUid()) { + u->add(size, pid); + if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) { + Uids.erase(it); + Uids.insert(last, u); + } + return; + } + } + u = new UidStatistics(uid); + if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) { + Uids.insert(last, u); + } else { + Uids.push_back(u); + } + u->add(size, pid); +} + +void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) { + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if (uid == u->getUid()) { + u->subtract(size, pid); + return; + } + } +} + +size_t LidStatistics::sizes(uid_t uid, pid_t pid) { + size_t sizes = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + sizes += u->sizes(pid); + } + } + return sizes; +} + +size_t LidStatistics::elements(uid_t uid, pid_t pid) { + size_t elements = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + elements += u->elements(pid); + } + } + return elements; +} + +size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) { + size_t sizes = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + sizes += u->sizesTotal(pid); + } + } + return sizes; +} + +size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) { + size_t elements = 0; + UidStatisticsCollection::iterator it; + for (it = begin(); it != end(); ++it) { + UidStatistics *u = *it; + if ((uid == uid_all) || (uid == u->getUid())) { + elements += u->elementsTotal(pid); + } + } + return elements; +} + +LogStatistics::LogStatistics() + : start(CLOCK_MONOTONIC) { + log_id_for_each(i) { + mSizes[i] = 0; + mElements[i] = 0; + } +} + +void LogStatistics::add(unsigned short size, + log_id_t log_id, uid_t uid, pid_t pid) { + mSizes[log_id] += size; + ++mElements[log_id]; + id(log_id).add(size, uid, pid); +} + +void LogStatistics::subtract(unsigned short size, + log_id_t log_id, uid_t uid, pid_t pid) { + mSizes[log_id] -= size; + --mElements[log_id]; + id(log_id).subtract(size, uid, pid); +} + +size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).sizes(uid, pid); + } + size_t sizes = 0; + log_id_for_each(i) { + sizes += id(i).sizes(uid, pid); + } + return sizes; +} + +size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).elements(uid, pid); + } + size_t elements = 0; + log_id_for_each(i) { + elements += id(i).elements(uid, pid); + } + return elements; +} + +size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).sizesTotal(uid, pid); + } + size_t sizes = 0; + log_id_for_each(i) { + sizes += id(i).sizesTotal(uid, pid); + } + return sizes; +} + +size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) { + if (log_id != log_id_all) { + return id(log_id).elementsTotal(uid, pid); + } + size_t elements = 0; + log_id_for_each(i) { + elements += id(i).elementsTotal(uid, pid); + } + return elements; +} + +size_t LogStatistics::format(char **buf, + uid_t uid, unsigned int logMask, log_time oldest) { + const unsigned short spaces_current = 13; + const unsigned short spaces_total = 19; + + if (*buf) { + free(buf); + *buf = NULL; + } + + android::String8 string(" span -> size/num"); + size_t oldLength; + short spaces = 2; + + log_id_for_each(i) { + if (logMask & (1 << i)) { + oldLength = string.length(); + string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i)); + spaces += spaces_total + oldLength - string.length(); + } + } + + spaces = 1; + log_time t(CLOCK_MONOTONIC); + unsigned long long d = t.nsec() - start.nsec(); + string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu", + d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60, + (d / NS_PER_SEC) % 60, d % NS_PER_SEC); + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + oldLength = string.length(); + string.appendFormat("%*s%zu/%zu", spaces, "", + sizesTotal(i), elementsTotal(i)); + spaces += spaces_total + oldLength - string.length(); + } + + spaces = 1; + d = t.nsec() - oldest.nsec(); + string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu", + d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60, + (d / NS_PER_SEC) % 60, d % NS_PER_SEC); + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + + size_t els = elements(i); + if (els) { + oldLength = string.length(); + string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els); + spaces -= string.length() - oldLength; + } + spaces += spaces_total; + } + + log_id_for_each(i) { + if (!(logMask & (1 << i))) { + continue; + } + + bool header = false; + bool first = true; + + UidStatisticsCollection::iterator ut; + for(ut = id(i).begin(); ut != id(i).end(); ++ut) { + UidStatistics *up = *ut; + if ((uid != AID_ROOT) && (uid != up->getUid())) { + continue; + } + + PidStatisticsCollection::iterator pt = up->begin(); + if (pt == up->end()) { + continue; + } + + android::String8 intermediate; + + if (!header) { + // header below tuned to match spaces_total and spaces_current + spaces = 0; + intermediate = string.format("%s: UID/PID Total size/num", + android_log_id_to_name(i)); + string.appendFormat("\n\n%-31sNow " + "UID/PID[?] Total Now", + intermediate.string()); + intermediate.clear(); + header = true; + } + + bool oneline = ++pt == up->end(); + --pt; + + if (!oneline) { + first = true; + } else if (!first && spaces) { + string.appendFormat("%*s", spaces, ""); + } + spaces = 0; + + uid_t u = up->getUid(); + pid_t p = (*pt)->getPid(); + + intermediate = string.format(oneline + ? ((p == PidStatistics::gone) + ? "%d/?" + : "%d/%d") + : "%d", + u, p); + string.appendFormat((first) ? "\n%-12s" : "%-12s", + intermediate.string()); + intermediate.clear(); + + size_t elsTotal = up->elementsTotal(); + oldLength = string.length(); + string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal); + spaces += spaces_total + oldLength - string.length(); + + size_t els = up->elements(); + if (els == elsTotal) { + string.appendFormat("%*s=", spaces, ""); + spaces = -1; + } else if (els) { + oldLength = string.length(); + string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els); + spaces -= string.length() - oldLength; + } + spaces += spaces_current; + + first = !first; + + if (oneline) { + continue; + } + + size_t gone_szs = 0; + size_t gone_els = 0; + + for(; pt != up->end(); ++pt) { + PidStatistics *pp = *pt; + pid_t p = pp->getPid(); + + // If a PID no longer has any current logs, and is not + // active anymore, skip & report totals for gone. + elsTotal = pp->elementsTotal(); + size_t szsTotal = pp->sizesTotal(); + if (p == pp->gone) { + gone_szs += szsTotal; + gone_els += elsTotal; + continue; + } + els = pp->elements(); + bool gone = kill(p, 0); + if (gone && (els == 0)) { + // ToDo: garbage collection: move this statistical bucket + // from its current UID/PID to UID/? (races and + // wrap around are our achilles heel). Below is + // merely lipservice to catch PIDs that were still + // around when the stats were pruned to zero. + gone_szs += szsTotal; + gone_els += elsTotal; + continue; + } + + if (!first && spaces) { + string.appendFormat("%*s", spaces, ""); + } + spaces = 0; + + intermediate = string.format((gone) ? "%d/%d?" : "%d/%d", u, p); + string.appendFormat((first) ? "\n%-12s" : "%-12s", + intermediate.string()); + intermediate.clear(); + + oldLength = string.length(); + string.appendFormat("%zu/%zu", szsTotal, elsTotal); + spaces += spaces_total + oldLength - string.length(); + + if (els == elsTotal) { + string.appendFormat("%*s=", spaces, ""); + spaces = -1; + } else if (els) { + oldLength = string.length(); + string.appendFormat("%*s%zu/%zu", spaces, "", + pp->sizes(), els); + spaces -= string.length() - oldLength; + } + spaces += spaces_current; + + first = !first; + } + + if (gone_els) { + if (!first && spaces) { + string.appendFormat("%*s", spaces, ""); + } + + intermediate = string.format("%d/?", u); + string.appendFormat((first) ? "\n%-12s" : "%-12s", + intermediate.string()); + intermediate.clear(); + + spaces = spaces_total + spaces_current; + + oldLength = string.length(); + string.appendFormat("%zu/%zu", gone_szs, gone_els); + spaces -= string.length() - oldLength; + + first = !first; + } + } + } + + // Calculate total buffer size prefix + char re_fmt[32]; + size_t ret; + for(size_t l = string.length(), y = 0, x = 6; + y != x; + y = x, x = strlen(re_fmt) - 2) { + snprintf(re_fmt, sizeof(re_fmt), "%zu\n%%s\n\f", l + x); + ret = l + x; + } + + android::String8 intermediate = string.format(re_fmt, string.string()); + string.clear(); + + *buf = strdup(intermediate.string()); + + return ret; +} -- cgit v1.1