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 b42f44b7c09697f4a699f167531742bbf9e00ac7)
Change-Id: I521753b1969ecd4590c956aeeb1557d101059d67
diff --git a/logd/Android.mk b/logd/Android.mk
index 744c9d3..3dd8e0f 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -13,12 +13,14 @@
FlushCommand.cpp \
LogBuffer.cpp \
LogBufferElement.cpp \
- LogTimes.cpp
+ LogTimes.cpp \
+ LogStatistics.cpp
LOCAL_SHARED_LIBRARIES := \
libsysutils \
liblog \
- libcutils
+ libcutils \
+ libutils
LOCAL_MODULE_TAGS := optional
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 202d542..43a283c 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -38,6 +38,7 @@
registerCmd(new ClearCmd(buf));
registerCmd(new GetBufSizeCmd(buf));
registerCmd(new GetBufSizeUsedCmd(buf));
+ registerCmd(new GetStatisticsCmd(buf));
}
CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
@@ -133,3 +134,41 @@
cli->sendMsg(buf);
return 0;
}
+
+CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf)
+ : LogCommand("getStatistics")
+ , mBuf(*buf)
+{ }
+
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ uid_t uid = cli->getUid();
+ gid_t gid = cli->getGid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ unsigned int logMask = -1;
+ if (argc > 1) {
+ logMask = 0;
+ for (int i = 1; i < argc; ++i) {
+ int id = atoi(argv[i]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+ logMask |= 1 << id;
+ }
+ }
+
+ char *buf = NULL;
+
+ mBuf.formatStatistics(&buf, uid, logMask);
+ if (!buf) {
+ cli->sendMsg("Failed");
+ } else {
+ cli->sendMsg(buf);
+ free(buf);
+ }
+ return 0;
+}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 861abbf..a841610 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -54,6 +54,7 @@
LogBufferCmd(Clear)
LogBufferCmd(GetBufSize)
LogBufferCmd(GetBufSizeUsed)
+ LogBufferCmd(GetStatistics)
};
#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index c5760f7..e16aa69 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -22,17 +22,13 @@
#include <log/logger.h>
#include "LogBuffer.h"
+#include "LogStatistics.h"
#include "LogReader.h"
#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
LogBuffer::LogBuffer(LastLogTimes *times)
: mTimes(*times) {
- int i;
- for (i = 0; i < LOG_ID_MAX; i++) {
- mSizes[i] = 0;
- mElements[i] = 0;
- }
pthread_mutex_init(&mLogElementsLock, NULL);
}
@@ -93,8 +89,7 @@
LogTimeEntry::unlock();
}
- mSizes[log_id] += len;
- mElements[log_id]++;
+ stats.add(len, log_id, uid, pid);
maybePrune(log_id);
pthread_mutex_unlock(&mLogElementsLock);
}
@@ -104,10 +99,10 @@
//
// mLogElementsLock must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
- unsigned long sizes = mSizes[id];
+ size_t sizes = stats.sizes(id);
if (sizes > LOG_BUFFER_SIZE) {
- unsigned long sizeOver90Percent = sizes - ((LOG_BUFFER_SIZE * 9) / 10);
- unsigned long elements = mElements[id];
+ size_t sizeOver90Percent = sizes - ((LOG_BUFFER_SIZE * 9) / 10);
+ size_t elements = stats.elements(id);
unsigned long pruneRows = elements * sizeOver90Percent / sizes;
elements /= 10;
if (pruneRows <= elements) {
@@ -141,7 +136,7 @@
LogBufferElement *e = *it;
if (e->getLogId() == id) {
if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
- if (mSizes[id] > (2 * LOG_BUFFER_SIZE)) {
+ if (stats.sizes(id) > (2 * LOG_BUFFER_SIZE)) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
} else {
@@ -150,8 +145,7 @@
break;
}
it = mLogElements.erase(it);
- mSizes[id] -= e->getMsgLen();
- mElements[id]--;
+ stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
delete e;
pruneRows--;
} else {
@@ -172,7 +166,7 @@
// get the used space associated with "id".
unsigned long LogBuffer::getSizeUsed(log_id_t id) {
pthread_mutex_lock(&mLogElementsLock);
- unsigned long retval = mSizes[id];
+ size_t retval = stats.sizes(id);
pthread_mutex_unlock(&mLogElementsLock);
return retval;
}
@@ -221,3 +215,26 @@
return max;
}
+
+size_t LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+ log_time oldest(CLOCK_MONOTONIC);
+
+ pthread_mutex_lock(&mLogElementsLock);
+
+ // Find oldest element in the log(s)
+ LogBufferElementCollection::iterator it;
+ for (it = mLogElements.begin(); it != mLogElements.end(); ++it) {
+ LogBufferElement *element = *it;
+
+ if ((logMask & (1 << element->getLogId()))) {
+ oldest = element->getMonotonicTime();
+ break;
+ }
+ }
+
+ size_t ret = stats.format(strp, uid, logMask, oldest);
+
+ pthread_mutex_unlock(&mLogElementsLock);
+
+ return ret;
+}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 1b50a8f..92dd107 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -25,6 +25,7 @@
#include "LogBufferElement.h"
#include "LogTimes.h"
+#include "LogStatistics.h"
typedef android::List<LogBufferElement *> LogBufferElementCollection;
@@ -32,8 +33,7 @@
LogBufferElementCollection mLogElements;
pthread_mutex_t mLogElementsLock;
- unsigned long mSizes[LOG_ID_MAX];
- unsigned long mElements[LOG_ID_MAX];
+ LogStatistics stats;
public:
LastLogTimes &mTimes;
@@ -50,6 +50,8 @@
void clear(log_id_t id);
unsigned long getSize(log_id_t id);
unsigned long getSizeUsed(log_id_t id);
+ // *strp uses malloc, use free to release.
+ size_t formatStatistics(char **strp, uid_t uid, unsigned int logMask);
private:
void maybePrune(log_id_t id);
@@ -57,4 +59,4 @@
};
-#endif
+#endif // _LOGD_LOG_BUFFER_H__
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 <stdarg.h>
+#include <time.h>
+
+#include <log/logger.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String8.h>
+
+#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;
+}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
new file mode 100644
index 0000000..c8eeb45
--- /dev/null
+++ b/logd/LogStatistics.h
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#ifndef _LOGD_LOG_STATISTICS_H__
+#define _LOGD_LOG_STATISTICS_H__
+
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <log/log_read.h>
+#include <utils/List.h>
+
+#define log_id_for_each(i) \
+ for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
+
+class PidStatistics {
+ const pid_t pid;
+
+ // Total
+ size_t mSizesTotal;
+ size_t mElementsTotal;
+ // Current
+ size_t mSizes;
+ size_t mElements;
+
+public:
+ static const pid_t gone = (pid_t) -1;
+
+ PidStatistics(pid_t pid);
+
+ pid_t getPid() const { return pid; }
+
+ void add(unsigned short size);
+ bool subtract(unsigned short size); // returns true if stats and PID gone
+ void addTotal(size_t size, size_t element);
+
+ size_t sizes() const { return mSizes; }
+ size_t elements() const { return mElements; }
+
+ size_t sizesTotal() const { return mSizesTotal; }
+ size_t elementsTotal() const { return mElementsTotal; }
+};
+
+typedef android::List<PidStatistics *> PidStatisticsCollection;
+
+class UidStatistics {
+ const uid_t uid;
+
+ PidStatisticsCollection Pids;
+
+public:
+ UidStatistics(uid_t uid);
+ ~UidStatistics();
+
+ PidStatisticsCollection::iterator begin() { return Pids.begin(); }
+ PidStatisticsCollection::iterator end() { return Pids.end(); }
+
+ uid_t getUid() { return uid; }
+
+ void add(unsigned short size, pid_t pid);
+ void subtract(unsigned short size, pid_t pid);
+
+ static const pid_t pid_all = (pid_t) -1;
+
+ size_t sizes(pid_t pid = pid_all);
+ size_t elements(pid_t pid = pid_all);
+
+ size_t sizesTotal(pid_t pid = pid_all);
+ size_t elementsTotal(pid_t pid = pid_all);
+};
+
+typedef android::List<UidStatistics *> UidStatisticsCollection;
+
+class LidStatistics {
+ UidStatisticsCollection Uids;
+
+public:
+ LidStatistics();
+ ~LidStatistics();
+
+ UidStatisticsCollection::iterator begin() { return Uids.begin(); }
+ UidStatisticsCollection::iterator end() { return Uids.end(); }
+
+ void add(unsigned short size, uid_t uid, pid_t pid);
+ void subtract(unsigned short size, uid_t uid, pid_t pid);
+
+ static const pid_t pid_all = (pid_t) -1;
+ static const uid_t uid_all = (uid_t) -1;
+
+ size_t sizes(uid_t uid = uid_all, pid_t pid = pid_all);
+ size_t elements(uid_t uid = uid_all, pid_t pid = pid_all);
+
+ size_t sizesTotal(uid_t uid = uid_all, pid_t pid = pid_all);
+ size_t elementsTotal(uid_t uid = uid_all, pid_t pid = pid_all);
+};
+
+// Log Statistics
+class LogStatistics {
+ LidStatistics LogIds[LOG_ID_MAX];
+
+ size_t mSizes[LOG_ID_MAX];
+ size_t mElements[LOG_ID_MAX];
+
+public:
+ const log_time start;
+
+ LogStatistics();
+
+ LidStatistics &id(log_id_t log_id) { return LogIds[log_id]; }
+
+ void add(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
+ void subtract(unsigned short size, log_id_t log_id, uid_t uid, pid_t pid);
+
+ // fast track current value by id only
+ size_t sizes(log_id_t id) const { return mSizes[id]; }
+ size_t elements(log_id_t id) const { return mElements[id]; }
+
+ // statistical track
+ static const log_id_t log_id_all = (log_id_t) -1;
+ static const uid_t uid_all = (uid_t) -1;
+ static const pid_t pid_all = (pid_t) -1;
+
+ size_t sizes(log_id_t id, uid_t uid, pid_t pid = pid_all);
+ size_t elements(log_id_t id, uid_t uid, pid_t pid = pid_all);
+ size_t sizes() { return sizes(log_id_all, uid_all); }
+ size_t elements() { return elements(log_id_all, uid_all); }
+
+ size_t sizesTotal(log_id_t id = log_id_all,
+ uid_t uid = uid_all,
+ pid_t pid = pid_all);
+ size_t elementsTotal(log_id_t id = log_id_all,
+ uid_t uid = uid_all,
+ pid_t pid = pid_all);
+
+ // *strp = malloc, balance with free
+ size_t format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+};
+
+#endif // _LOGD_LOG_STATISTICS_H__