summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Salyzyn <salyzyn@google.com>2014-02-06 14:48:50 -0800
committerMark Salyzyn <salyzyn@google.com>2014-03-13 14:47:58 -0700
commit34facab86b0fe7ec613de92b46b637f864fb0682 (patch)
treeb2587070fbb968d1e20954dc53d53faad9cca74a
parent897d345b1f2672480346e5dc8971a4626f3f1b8b (diff)
downloadsystem_core-34facab86b0fe7ec613de92b46b637f864fb0682.zip
system_core-34facab86b0fe7ec613de92b46b637f864fb0682.tar.gz
system_core-34facab86b0fe7ec613de92b46b637f864fb0682.tar.bz2
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
-rw-r--r--include/log/logger.h3
-rw-r--r--liblog/log_read.c26
-rw-r--r--liblog/log_read_kern.c14
-rw-r--r--logcat/logcat.cpp97
-rw-r--r--logd/Android.mk6
-rw-r--r--logd/CommandListener.cpp39
-rw-r--r--logd/CommandListener.h1
-rw-r--r--logd/LogBuffer.cpp45
-rw-r--r--logd/LogBuffer.h8
-rw-r--r--logd/LogStatistics.cpp570
-rw-r--r--logd/LogStatistics.h152
11 files changed, 938 insertions, 23 deletions
diff --git a/include/log/logger.h b/include/log/logger.h
index 8810615..8537c1d 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -146,6 +146,9 @@ int android_logger_get_log_version(struct logger *logger);
struct logger_list;
+ssize_t android_logger_get_statistics(struct logger_list *logger_list,
+ char *buf, size_t len);
+
struct logger_list *android_logger_list_alloc(int mode,
unsigned int tail,
pid_t pid);
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 889442d..6f6fe5f 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -357,6 +357,32 @@ int android_logger_get_log_version(struct logger *logger UNUSED)
return 3;
}
+/*
+ * returns statistics
+ */
+ssize_t android_logger_get_statistics(struct logger_list *logger_list,
+ char *buf, size_t len)
+{
+ struct listnode *node;
+ struct logger *logger;
+ char *cp = buf;
+ size_t remaining = len;
+ size_t n;
+
+ n = snprintf(cp, remaining, "getStatistics");
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+
+ logger_for_each(logger, logger_list) {
+ n = snprintf(cp, remaining, " %d", logger->id);
+ n = min(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
+ return send_log_msg(NULL, NULL, buf, len);
+}
+
struct logger_list *android_logger_list_alloc(int mode,
unsigned int tail,
pid_t pid)
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
index b454729..59a7a0b 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -51,6 +51,8 @@ typedef char bool;
logger != node_to_item(&(logger_list)->node, struct logger, node); \
logger = node_to_item((logger)->node.next, struct logger, node))
+#define UNUSED __attribute__((unused))
+
/* In the future, we would like to make this list extensible */
static const char *LOG_NAME[LOG_ID_MAX] = {
[LOG_ID_MAIN] = "main",
@@ -248,6 +250,18 @@ int android_logger_get_log_version(struct logger *logger)
return (ret < 0) ? 1 : ret;
}
+/*
+ * returns statistics
+ */
+
+ssize_t android_logger_get_statistics(struct logger_list *logger_list UNUSED,
+ char *buf, size_t len)
+{
+ static const char unsupported[] = "18\nNot Supported\n\f";
+ strncpy(buf, unsupported, len);
+ return -ENOTSUP;
+}
+
struct logger_list *android_logger_list_alloc(int mode,
unsigned int tail,
pid_t pid)
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 3c33938..812e2e0 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -225,10 +225,11 @@ static void show_help(const char *cmd)
" -t <count> print only the most recent <count> lines (implies -d)\n"
" -T <count> print only the most recent <count> lines (does not imply -d)\n"
" -g get the size of the log's ring buffer and exit\n"
- " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n"
- " or 'events'. Multiple -b parameters are allowed and the\n"
+ " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio',\n"
+ " 'events' or 'all'. Multiple -b parameters are allowed and\n"
" results are interleaved. The default is -b main -b system.\n"
- " -B output the log in binary");
+ " -B output the log in binary.\n"
+ " -S output statistics");
fprintf(stderr,"\nfilterspecs are a series of \n"
@@ -278,6 +279,7 @@ int main(int argc, char **argv)
int hasSetLogFormat = 0;
int clearLog = 0;
int getLogSize = 0;
+ int printStatistics = 0;
int mode = O_RDONLY;
const char *forceFilters = NULL;
log_device_t* devices = NULL;
@@ -303,7 +305,7 @@ int main(int argc, char **argv)
for (;;) {
int ret;
- ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B");
+ ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:BS");
if (ret < 0) {
break;
@@ -336,6 +338,39 @@ int main(int argc, char **argv)
break;
case 'b': {
+ if (strcmp(optarg, "all") == 0) {
+ while (devices) {
+ dev = devices;
+ devices = dev->next;
+ delete dev;
+ }
+
+ dev = devices = new log_device_t("main", false, 'm');
+ android::g_devCount = 1;
+ if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
+ dev->next = new log_device_t("system", false, 's');
+ if (dev->next) {
+ dev = dev->next;
+ android::g_devCount++;
+ }
+ }
+ if (android_name_to_log_id("radio") == LOG_ID_RADIO) {
+ dev->next = new log_device_t("radio", false, 'r');
+ if (dev->next) {
+ dev = dev->next;
+ android::g_devCount++;
+ }
+ }
+ if (android_name_to_log_id("events") == LOG_ID_EVENTS) {
+ dev->next = new log_device_t("events", true, 'e');
+ if (dev->next) {
+ android::g_devCount++;
+ needBinary = true;
+ }
+ }
+ break;
+ }
+
bool binary = strcmp(optarg, "events") == 0;
if (binary) {
needBinary = true;
@@ -470,6 +505,10 @@ int main(int argc, char **argv)
}
break;
+ case 'S':
+ printStatistics = 1;
+ break;
+
default:
fprintf(stderr,"Unrecognized Option\n");
android::show_help(argv[0]);
@@ -587,6 +626,56 @@ int main(int argc, char **argv)
dev = dev->next;
}
+ if (printStatistics) {
+ size_t len = 8192;
+ char *buf;
+
+ for(int retry = 32;
+ (retry >= 0) && ((buf = new char [len]));
+ delete [] buf, --retry) {
+ android_logger_get_statistics(logger_list, buf, len);
+
+ buf[len-1] = '\0';
+ size_t ret = atol(buf) + 1;
+ if (ret < 4) {
+ delete [] buf;
+ buf = NULL;
+ break;
+ }
+ bool check = ret <= len;
+ len = ret;
+ if (check) {
+ break;
+ }
+ }
+
+ if (!buf) {
+ perror("statistics read");
+ exit(EXIT_FAILURE);
+ }
+
+ // remove trailing FF
+ char *cp = buf + len - 1;
+ *cp = '\0';
+ bool truncated = *--cp != '\f';
+ if (!truncated) {
+ *cp = '\0';
+ }
+
+ // squash out the byte count
+ cp = buf;
+ if (!truncated) {
+ while (isdigit(*cp) || (*cp == '\n')) {
+ ++cp;
+ }
+ }
+
+ printf("%s", cp);
+ delete [] buf;
+ exit(0);
+ }
+
+
if (getLogSize) {
exit(0);
}
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 @@ LOCAL_SRC_FILES := \
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 @@ CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/,
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 @@ int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli,
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 @@ private:
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 @@ void LogBuffer::log(log_id_t log_id, log_time realtime,
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 @@ void LogBuffer::log(log_id_t log_id, log_time realtime,
//
// 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 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) {
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 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) {
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 @@ void LogBuffer::clear(log_id_t id) {
// 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 @@ log_time LogBuffer::flushTo(
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 @@ class LogBuffer {
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 @@ public:
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 @@ private:
};
-#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__