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

Change-Id: I521753b1969ecd4590c956aeeb1557d101059d67
This commit is contained in:
Mark Salyzyn 2014-02-06 14:48:50 -08:00
parent ffcbc3f1d4
commit 51a29c8dc4
11 changed files with 938 additions and 23 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -54,6 +54,7 @@ private:
LogBufferCmd(Clear)
LogBufferCmd(GetBufSize)
LogBufferCmd(GetBufSizeUsed)
LogBufferCmd(GetStatistics)
};
#endif

View File

@ -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;
}

View File

@ -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__

570
logd/LogStatistics.cpp Normal file
View File

@ -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;
}

152
logd/LogStatistics.h Normal file
View File

@ -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__