logd: liblog: logcat: Add LogWhiteBlackList
- liblog android_logger_get_log_size and android_logger_get_readable_size
adjusted to return long instead of int because of -G flag extending range
NB: ifdef'd only for userdebug and eng builds
- liblog Add android_logger_[sg]et_prune_list and android_logger_set_log_size
- logcat Add -P, -p and -G flags
- logd Add LogWhiteBlackList and configurable log size
Change-Id: I1572338c1b34bd968ad7867857ef708156ec3b6a
diff --git a/include/log/logger.h b/include/log/logger.h
index 8537c1d..8dab234 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -140,14 +140,23 @@
log_id_t android_logger_get_id(struct logger *logger);
int android_logger_clear(struct logger *logger);
-int android_logger_get_log_size(struct logger *logger);
-int android_logger_get_log_readable_size(struct logger *logger);
+long android_logger_get_log_size(struct logger *logger);
+#ifdef USERDEBUG_BUILD
+int android_logger_set_log_size(struct logger *logger, unsigned long size);
+#endif
+long android_logger_get_log_readable_size(struct logger *logger);
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);
+#ifdef USERDEBUG_BUILD
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list,
+ char *buf, size_t len);
+int android_logger_set_prune_list(struct logger_list *logger_list,
+ char *buf, size_t len);
+#endif
struct logger_list *android_logger_list_alloc(int mode,
unsigned int tail,
diff --git a/liblog/Android.mk b/liblog/Android.mk
index c18dc48..4fe20db 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -22,6 +22,10 @@
liblog_sources := logd_write_kern.c
endif
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+liblog_cflags := -DUSERDEBUG_BUILD=1
+endif
+
# some files must not be compiled when building against Mingw
# they correspond to features not used by our host development tools
# which are also hard or even impossible to port to native Win32
@@ -80,11 +84,13 @@
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_SRC_FILES := $(liblog_target_sources)
+LOCAL_CFLAGS := $(liblog_cflags)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+LOCAL_CFLAGS := $(liblog_cflags)
include $(BUILD_SHARED_LIBRARY)
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 6f6fe5f..e4acac2 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -296,11 +296,8 @@
return ret;
}
-int android_logger_clear(struct logger *logger)
+static int check_log_success(char *buf, ssize_t ret)
{
- char buf[512];
-
- ssize_t ret = send_log_msg(logger, "clear %d", buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -312,8 +309,16 @@
return 0;
}
+int android_logger_clear(struct logger *logger)
+{
+ char buf[512];
+
+ return check_log_success(buf,
+ send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+}
+
/* returns the total size of the log's ring buffer */
-int android_logger_get_log_size(struct logger *logger)
+long android_logger_get_log_size(struct logger *logger)
{
char buf[512];
@@ -326,14 +331,28 @@
return -1;
}
- return atoi(buf);
+ return atol(buf);
}
+#ifdef USERDEBUG_BUILD
+
+int android_logger_set_log_size(struct logger *logger, unsigned long size)
+{
+ char buf[512];
+
+ snprintf(buf, sizeof(buf), "setLogSize %d %lu",
+ logger ? logger->id : (unsigned) -1, size);
+
+ return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+}
+
+#endif /* USERDEBUG_BUILD */
+
/*
* returns the readable size of the log's ring buffer (that is, amount of the
* log consumed)
*/
-int android_logger_get_log_readable_size(struct logger *logger)
+long android_logger_get_log_readable_size(struct logger *logger)
{
char buf[512];
@@ -346,7 +365,7 @@
return -1;
}
- return atoi(buf);
+ return atol(buf);
}
/*
@@ -383,6 +402,32 @@
return send_log_msg(NULL, NULL, buf, len);
}
+#ifdef USERDEBUG_BUILD
+
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED,
+ char *buf, size_t len)
+{
+ return send_log_msg(NULL, "getPruneList", buf, len);
+}
+
+int android_logger_set_prune_list(struct logger_list *logger_list UNUSED,
+ char *buf, size_t len)
+{
+ const char cmd[] = "setPruneList ";
+ const size_t cmdlen = sizeof(cmd) - 1;
+
+ if (strlen(buf) > (len - cmdlen)) {
+ return -ENOMEM; /* KISS */
+ }
+ memmove(buf + cmdlen, buf, len - cmdlen);
+ buf[len - 1] = '\0';
+ memcpy(buf, cmd, cmdlen);
+
+ return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+}
+
+#endif /* USERDEBUG_BUILD */
+
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 59a7a0b..483b6b6 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -227,16 +227,26 @@
}
/* returns the total size of the log's ring buffer */
-int android_logger_get_log_size(struct logger *logger)
+long android_logger_get_log_size(struct logger *logger)
{
return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR);
}
+#ifdef USERDEBUG_BUILD
+
+int android_logger_set_log_size(struct logger *logger UNUSED,
+ unsigned long size UNUSED)
+{
+ return -ENOTSUP;
+}
+
+#endif /* USERDEBUG_BUILD */
+
/*
* returns the readable size of the log's ring buffer (that is, amount of the
* log consumed)
*/
-int android_logger_get_log_readable_size(struct logger *logger)
+long android_logger_get_log_readable_size(struct logger *logger)
{
return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY);
}
@@ -253,15 +263,34 @@
/*
* returns statistics
*/
+static const char unsupported[] = "18\nNot Supported\n\f";
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;
}
+#ifdef USERDEBUG_BUILD
+
+ssize_t android_logger_get_prune_list(struct logger_list *logger_list UNUSED,
+ char *buf, size_t len)
+{
+ strncpy(buf, unsupported, len);
+ return -ENOTSUP;
+}
+
+int android_logger_set_prune_list(struct logger_list *logger_list UNUSED,
+ char *buf, size_t len)
+{
+ static const char unsupported_error[] = "Unsupported";
+ strncpy(buf, unsupported, len);
+ return -ENOTSUP;
+}
+
+#endif /* USERDEBUG_BUILD */
+
struct logger_list *android_logger_list_alloc(int mode,
unsigned int tail,
pid_t pid)
diff --git a/logcat/Android.mk b/logcat/Android.mk
index b5e27eb..dd15cb3 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -3,6 +3,10 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
+endif
+
LOCAL_SRC_FILES:= logcat.cpp event.logtags
LOCAL_SHARED_LIBRARIES := liblog
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 812e2e0..00b5ba9 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -229,8 +229,19 @@
" '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.\n"
- " -S output statistics");
+ " -S output statistics.\n");
+#ifdef USERDEBUG_BUILD
+
+ fprintf(stderr, "--------------------- eng & userdebug builds only ---------------------------\n"
+ " -G <count> set size of log's ring buffer and exit\n"
+ " -p output prune white and ~black list\n"
+ " -P '<list> ...' set prune white and ~black list; UID, /PID or !(worst UID)\n"
+ " default is ~!, prune worst UID.\n"
+ "-----------------------------------------------------------------------------\n"
+ );
+
+#endif
fprintf(stderr,"\nfilterspecs are a series of \n"
" <tag>[:priority]\n\n"
@@ -279,6 +290,11 @@
int hasSetLogFormat = 0;
int clearLog = 0;
int getLogSize = 0;
+#ifdef USERDEBUG_BUILD
+ unsigned long setLogSize = 0;
+ int getPruneList = 0;
+ char *setPruneList = NULL;
+#endif
int printStatistics = 0;
int mode = O_RDONLY;
const char *forceFilters = NULL;
@@ -305,7 +321,13 @@
for (;;) {
int ret;
- ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:BS");
+ ret = getopt(argc, argv,
+#ifdef USERDEBUG_BUILD
+ "cdt:T:gG:sQf:r::n:v:b:BSpP:"
+#else
+ "cdt:T:gsQf:r::n:v:b:BS"
+#endif
+ );
if (ret < 0) {
break;
@@ -337,6 +359,55 @@
getLogSize = 1;
break;
+#ifdef USERDEBUG_BUILD
+
+ case 'G': {
+ // would use atol if not for the multiplier
+ char *cp = optarg;
+ setLogSize = 0;
+ while (('0' <= *cp) && (*cp <= '9')) {
+ setLogSize *= 10;
+ setLogSize += *cp - '0';
+ ++cp;
+ }
+
+ switch(*cp) {
+ case 'g':
+ case 'G':
+ setLogSize *= 1024;
+ /* FALLTHRU */
+ case 'm':
+ case 'M':
+ setLogSize *= 1024;
+ /* FALLTHRU */
+ case 'k':
+ case 'K':
+ setLogSize *= 1024;
+ /* FALLTHRU */
+ case '\0':
+ break;
+
+ default:
+ setLogSize = 0;
+ }
+
+ if (!setLogSize) {
+ fprintf(stderr, "ERROR: -G <num><multiplier>\n");
+ exit(1);
+ }
+ }
+ break;
+
+ case 'p':
+ getPruneList = 1;
+ break;
+
+ case 'P':
+ setPruneList = optarg;
+ break;
+
+#endif
+
case 'b': {
if (strcmp(optarg, "all") == 0) {
while (devices) {
@@ -602,8 +673,17 @@
}
}
+#ifdef USERDEBUG_BUILD
+
+ if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
+ perror("setLogSize");
+ exit(EXIT_FAILURE);
+ }
+
+#endif
+
if (getLogSize) {
- int size, readable;
+ long size, readable;
size = android_logger_get_log_size(dev->logger);
if (size < 0) {
@@ -617,7 +697,7 @@
exit(EXIT_FAILURE);
}
- printf("%s: ring buffer is %dKb (%dKb consumed), "
+ printf("%s: ring buffer is %ldKb (%ldKb consumed), "
"max entry is %db, max payload is %db\n", dev->device,
size / 1024, readable / 1024,
(int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
@@ -626,15 +706,46 @@
dev = dev->next;
}
- if (printStatistics) {
+#ifdef USERDEBUG_BUILD
+
+ if (setPruneList) {
+ size_t len = strlen(setPruneList) + 32; // margin to allow rc
+ char *buf = (char *) malloc(len);
+
+ strcpy(buf, setPruneList);
+ int ret = android_logger_set_prune_list(logger_list, buf, len);
+ free(buf);
+
+ if (ret) {
+ perror("setPruneList");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+#endif
+
+ if (
+#ifdef USERDEBUG_BUILD
+ printStatistics || getPruneList
+#else
+ printStatistics
+#endif
+ ) {
size_t len = 8192;
char *buf;
for(int retry = 32;
(retry >= 0) && ((buf = new char [len]));
delete [] buf, --retry) {
+#ifdef USERDEBUG_BUILD
+ if (getPruneList) {
+ android_logger_get_prune_list(logger_list, buf, len);
+ } else {
+ android_logger_get_statistics(logger_list, buf, len);
+ }
+#else
android_logger_get_statistics(logger_list, buf, len);
-
+#endif
buf[len-1] = '\0';
size_t ret = atol(buf) + 1;
if (ret < 4) {
@@ -650,7 +761,7 @@
}
if (!buf) {
- perror("statistics read");
+ perror("response read");
exit(EXIT_FAILURE);
}
@@ -679,6 +790,11 @@
if (getLogSize) {
exit(0);
}
+#ifdef USERDEBUG_BUILD
+ if (setLogSize || setPruneList) {
+ exit(0);
+ }
+#endif
if (clearLog) {
exit(0);
}
diff --git a/logd/Android.mk b/logd/Android.mk
index 3dd8e0f..b0bc746 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -4,6 +4,10 @@
LOCAL_MODULE:= logd
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1
+endif
+
LOCAL_SRC_FILES := \
main.cpp \
LogCommand.cpp \
@@ -14,7 +18,8 @@
LogBuffer.cpp \
LogBufferElement.cpp \
LogTimes.cpp \
- LogStatistics.cpp
+ LogStatistics.cpp \
+ LogWhiteBlackList.cpp
LOCAL_SHARED_LIBRARIES := \
libsysutils \
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 43a283c..12b10ca 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -37,8 +37,15 @@
// registerCmd(new ShutdownCmd(buf, writer, swl));
registerCmd(new ClearCmd(buf));
registerCmd(new GetBufSizeCmd(buf));
+#ifdef USERDEBUG_BUILD
+ registerCmd(new SetBufSizeCmd(buf));
+#endif
registerCmd(new GetBufSizeUsedCmd(buf));
registerCmd(new GetStatisticsCmd(buf));
+#ifdef USERDEBUG_BUILD
+ registerCmd(new SetPruneListCmd(buf));
+ registerCmd(new GetPruneListCmd(buf));
+#endif
}
CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader,
@@ -110,6 +117,43 @@
return 0;
}
+#ifdef USERDEBUG_BUILD
+
+CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer *buf)
+ : LogCommand("setLogSize")
+ , mBuf(*buf)
+{ }
+
+int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (!clientHasLogCredentials(cli)) {
+ cli->sendMsg("Permission Denied");
+ return 0;
+ }
+
+ if (argc < 3) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int id = atoi(argv[1]);
+ if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ unsigned long size = atol(argv[2]);
+ if (mBuf.setSize((log_id_t) id, size)) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ cli->sendMsg("success");
+ return 0;
+}
+
+#endif // USERDEBUG_BUILD
+
CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer *buf)
: LogCommand("getLogSizeUsed")
, mBuf(*buf)
@@ -140,6 +184,24 @@
, mBuf(*buf)
{ }
+static void package_string(char **strp) {
+ const char *a = *strp;
+ if (!a) {
+ a = "";
+ }
+
+ // Calculate total buffer size prefix, count is the string length w/o nul
+ char fmt[32];
+ for(size_t l = strlen(a), y = 0, x = 6; y != x; y = x, x = strlen(fmt) - 2) {
+ snprintf(fmt, sizeof(fmt), "%zu\n%%s\n\f", l + x);
+ }
+
+ char *b = *strp;
+ *strp = NULL;
+ asprintf(strp, fmt, a);
+ free(b);
+}
+
int CommandListener::GetStatisticsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
uid_t uid = cli->getUid();
@@ -167,8 +229,69 @@
if (!buf) {
cli->sendMsg("Failed");
} else {
+ package_string(&buf);
cli->sendMsg(buf);
free(buf);
}
return 0;
}
+
+#ifdef USERDEBUG_BUILD
+
+CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer *buf)
+ : LogCommand("getPruneList")
+ , mBuf(*buf)
+{ }
+
+int CommandListener::GetPruneListCmd::runCommand(SocketClient *cli,
+ int /*argc*/, char ** /*argv*/) {
+ char *buf = NULL;
+ mBuf.formatPrune(&buf);
+ if (!buf) {
+ cli->sendMsg("Failed");
+ } else {
+ package_string(&buf);
+ cli->sendMsg(buf);
+ free(buf);
+ }
+ return 0;
+}
+
+CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer *buf)
+ : LogCommand("setPruneList")
+ , mBuf(*buf)
+{ }
+
+int CommandListener::SetPruneListCmd::runCommand(SocketClient *cli,
+ int argc, char **argv) {
+ if (!clientHasLogCredentials(cli)) {
+ cli->sendMsg("Permission Denied");
+ return 0;
+ }
+
+ char *cp = NULL;
+ for (int i = 1; i < argc; ++i) {
+ char *p = cp;
+ if (p) {
+ cp = NULL;
+ asprintf(&cp, "%s %s", p, argv[i]);
+ free(p);
+ } else {
+ asprintf(&cp, "%s", argv[i]);
+ }
+ }
+
+ int ret = mBuf.initPrune(cp);
+ free(cp);
+
+ if (ret) {
+ cli->sendMsg("Invalid");
+ return 0;
+ }
+
+ cli->sendMsg("success");
+
+ return 0;
+}
+
+#endif // USERDEBUG_BUILD
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index a841610..de1dcb9 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -53,8 +53,15 @@
LogBufferCmd(Clear)
LogBufferCmd(GetBufSize)
+#ifdef USERDEBUG_BUILD
+ LogBufferCmd(SetBufSize)
+#endif
LogBufferCmd(GetBufSizeUsed)
LogBufferCmd(GetStatistics)
+#ifdef USERDEBUG_BUILD
+ LogBufferCmd(GetPruneList)
+ LogBufferCmd(SetPruneList)
+#endif
};
#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index cf172d1..197b7e8 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -23,13 +23,25 @@
#include "LogBuffer.h"
#include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
#include "LogReader.h"
+// Default
#define LOG_BUFFER_SIZE (256 * 1024) // Tuned on a per-platform basis here?
+#ifdef USERDEBUG_BUILD
+#define log_buffer_size(id) mMaxSize[id]
+#else
+#define log_buffer_size(id) LOG_BUFFER_SIZE
+#endif
LogBuffer::LogBuffer(LastLogTimes *times)
: mTimes(*times) {
pthread_mutex_init(&mLogElementsLock, NULL);
+#ifdef USERDEBUG_BUILD
+ log_id_for_each(i) {
+ mMaxSize[i] = LOG_BUFFER_SIZE;
+ }
+#endif
}
void LogBuffer::log(log_id_t log_id, log_time realtime,
@@ -100,8 +112,8 @@
// mLogElementsLock must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
size_t sizes = stats.sizes(id);
- if (sizes > LOG_BUFFER_SIZE) {
- size_t sizeOver90Percent = sizes - ((LOG_BUFFER_SIZE * 9) / 10);
+ if (sizes > log_buffer_size(id)) {
+ size_t sizeOver90Percent = sizes - ((log_buffer_size(id) * 9) / 10);
size_t elements = stats.elements(id);
unsigned long pruneRows = elements * sizeOver90Percent / sizes;
elements /= 10;
@@ -140,18 +152,23 @@
size_t worst_sizes = 0;
size_t second_worst_sizes = 0;
- LidStatistics &l = stats.id(id);
- UidStatisticsCollection::iterator iu;
- for (iu = l.begin(); iu != l.end(); ++iu) {
- UidStatistics *u = (*iu);
- size_t sizes = u->sizes();
- if (worst_sizes < sizes) {
- second_worst_sizes = worst_sizes;
- worst_sizes = sizes;
- worst = u->getUid();
- }
- if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
- second_worst_sizes = sizes;
+#ifdef USERDEBUG_BUILD
+ if (mPrune.worstUidEnabled())
+#endif
+ {
+ LidStatistics &l = stats.id(id);
+ UidStatisticsCollection::iterator iu;
+ for (iu = l.begin(); iu != l.end(); ++iu) {
+ UidStatistics *u = (*iu);
+ size_t sizes = u->sizes();
+ if (worst_sizes < sizes) {
+ second_worst_sizes = worst_sizes;
+ worst_sizes = sizes;
+ worst = u->getUid();
+ }
+ if ((second_worst_sizes < sizes) && (sizes < worst_sizes)) {
+ second_worst_sizes = sizes;
+ }
}
}
@@ -163,7 +180,14 @@
break;
}
- if ((e->getLogId() == id) && (e->getUid() == worst)) {
+ if (e->getLogId() != id) {
+ ++it;
+ continue;
+ }
+
+ uid_t uid = e->getUid();
+
+ if (uid == worst) {
it = mLogElements.erase(it);
unsigned short len = e->getMsgLen();
stats.subtract(len, id, worst, e->getPid());
@@ -174,29 +198,60 @@
break;
}
worst_sizes -= len;
- } else {
+ }
+#ifdef USERDEBUG_BUILD
+ else if (mPrune.naughty(e)) { // BlackListed
+ it = mLogElements.erase(it);
+ stats.subtract(e->getMsgLen(), id, uid, e->getPid());
+ delete e;
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+ }
+#endif
+ else {
++it;
}
}
- if (!kick) {
+ if (!kick
+#ifdef USERDEBUG_BUILD
+ || !mPrune.worstUidEnabled()
+#endif
+ ) {
break; // the following loop will ask bad clients to skip/drop
}
}
+#ifdef USERDEBUG_BUILD
+ bool whitelist = false;
+#endif
it = mLogElements.begin();
while((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement *e = *it;
if (e->getLogId() == id) {
if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
- if (stats.sizes(id) > (2 * LOG_BUFFER_SIZE)) {
- // kick a misbehaving log reader client off the island
- oldest->release_Locked();
- } else {
- oldest->triggerSkip_Locked(pruneRows);
+#ifdef USERDEBUG_BUILD
+ if (!whitelist)
+#endif
+ {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+ // kick a misbehaving log reader client off the island
+ oldest->release_Locked();
+ } else {
+ oldest->triggerSkip_Locked(pruneRows);
+ }
}
break;
}
+#ifdef USERDEBUG_BUILD
+ if (mPrune.nice(e)) { // WhiteListed
+ whitelist = true;
+ it++;
+ continue;
+ }
+#endif
it = mLogElements.erase(it);
stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
delete e;
@@ -206,6 +261,32 @@
}
}
+#ifdef USERDEBUG_BUILD
+ if (whitelist && (pruneRows > 0)) {
+ it = mLogElements.begin();
+ while((it != mLogElements.end()) && (pruneRows > 0)) {
+ LogBufferElement *e = *it;
+ if (e->getLogId() == id) {
+ if (oldest && (oldest->mStart <= e->getMonotonicTime())) {
+ if (stats.sizes(id) > (2 * log_buffer_size(id))) {
+ // kick a misbehaving log reader client off the island
+ oldest->release_Locked();
+ } else {
+ oldest->triggerSkip_Locked(pruneRows);
+ }
+ break;
+ }
+ it = mLogElements.erase(it);
+ stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid());
+ delete e;
+ pruneRows--;
+ } else {
+ it++;
+ }
+ }
+ }
+#endif
+
LogTimeEntry::unlock();
}
@@ -224,11 +305,37 @@
return retval;
}
+#ifdef USERDEBUG_BUILD
+
+// set the total space allocated to "id"
+int LogBuffer::setSize(log_id_t id, unsigned long size) {
+ // Reasonable limits ...
+ if ((size < (64 * 1024)) || ((256 * 1024 * 1024) < size)) {
+ return -1;
+ }
+ pthread_mutex_lock(&mLogElementsLock);
+ log_buffer_size(id) = size;
+ pthread_mutex_unlock(&mLogElementsLock);
+ return 0;
+}
+
+// get the total space allocated to "id"
+unsigned long LogBuffer::getSize(log_id_t id) {
+ pthread_mutex_lock(&mLogElementsLock);
+ size_t retval = log_buffer_size(id);
+ pthread_mutex_unlock(&mLogElementsLock);
+ return retval;
+}
+
+#else // ! USERDEBUG_BUILD
+
// get the total space allocated to "id"
unsigned long LogBuffer::getSize(log_id_t /*id*/) {
- return LOG_BUFFER_SIZE;
+ return log_buffer_size(id);
}
+#endif
+
log_time LogBuffer::flushTo(
SocketClient *reader, const log_time start, bool privileged,
bool (*filter)(const LogBufferElement *element, void *arg), void *arg) {
@@ -269,7 +376,7 @@
return max;
}
-size_t LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
+void LogBuffer::formatStatistics(char **strp, uid_t uid, unsigned int logMask) {
log_time oldest(CLOCK_MONOTONIC);
pthread_mutex_lock(&mLogElementsLock);
@@ -285,9 +392,7 @@
}
}
- size_t ret = stats.format(strp, uid, logMask, oldest);
+ stats.format(strp, uid, logMask, oldest);
pthread_mutex_unlock(&mLogElementsLock);
-
- return ret;
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 92dd107..0745e56 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -26,6 +26,7 @@
#include "LogBufferElement.h"
#include "LogTimes.h"
#include "LogStatistics.h"
+#include "LogWhiteBlackList.h"
typedef android::List<LogBufferElement *> LogBufferElementCollection;
@@ -35,6 +36,12 @@
LogStatistics stats;
+#ifdef USERDEBUG_BUILD
+ PruneList mPrune;
+
+ unsigned long mMaxSize[LOG_ID_MAX];
+#endif
+
public:
LastLogTimes &mTimes;
@@ -49,9 +56,18 @@
void clear(log_id_t id);
unsigned long getSize(log_id_t id);
+#ifdef USERDEBUG_BUILD
+ int setSize(log_id_t id, unsigned long size);
+#endif
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);
+ void formatStatistics(char **strp, uid_t uid, unsigned int logMask);
+
+#ifdef USERDEBUG_BUILD
+ int initPrune(char *cp) { return mPrune.init(cp); }
+ // *strp uses malloc, use free to release.
+ void formatPrune(char **strp) { mPrune.format(strp); }
+#endif
private:
void maybePrune(log_id_t id);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 50ce442..49ee50d 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -335,8 +335,8 @@
return elements;
}
-size_t LogStatistics::format(char **buf,
- uid_t uid, unsigned int logMask, log_time oldest) {
+void 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;
@@ -551,20 +551,5 @@
}
}
- // 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;
+ *buf = strdup(string.string());
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index c8eeb45..d44afa2 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -146,7 +146,7 @@
pid_t pid = pid_all);
// *strp = malloc, balance with free
- size_t format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
+ void format(char **strp, uid_t uid, unsigned int logMask, log_time oldest);
};
#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
new file mode 100644
index 0000000..d0ceb9f
--- /dev/null
+++ b/logd/LogWhiteBlackList.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+
+#ifdef USERDEBUG_BUILD
+
+#include <ctype.h>
+
+#include <utils/String8.h>
+
+#include "LogWhiteBlackList.h"
+
+// White and Black list
+
+Prune::Prune(uid_t uid, pid_t pid)
+ : mUid(uid)
+ , mPid(pid)
+{ }
+
+int Prune::cmp(uid_t uid, pid_t pid) const {
+ if ((mUid == uid_all) || (mUid == uid)) {
+ if (mPid == pid_all) {
+ return 0;
+ }
+ return pid - mPid;
+ }
+ return uid - mUid;
+}
+
+void Prune::format(char **strp) {
+ if (mUid != uid_all) {
+ asprintf(strp, (mPid != pid_all) ? "%u/%u" : "%u", mUid, mPid);
+ } else {
+ // NB: mPid == pid_all can not happen if mUid == uid_all
+ asprintf(strp, (mPid != pid_all) ? "/%u" : "/", mPid);
+ }
+}
+
+PruneList::PruneList()
+ : mWorstUidEnabled(true) {
+ mNaughty.clear();
+ mNice.clear();
+}
+
+PruneList::~PruneList() {
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end();) {
+ delete (*it);
+ it = mNice.erase(it);
+ }
+ for (it = mNaughty.begin(); it != mNaughty.end();) {
+ delete (*it);
+ it = mNaughty.erase(it);
+ }
+}
+
+int PruneList::init(char *str) {
+ mWorstUidEnabled = true;
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end();) {
+ delete (*it);
+ it = mNice.erase(it);
+ }
+ for (it = mNaughty.begin(); it != mNaughty.end();) {
+ delete (*it);
+ it = mNaughty.erase(it);
+ }
+
+ if (!str) {
+ return 0;
+ }
+
+ mWorstUidEnabled = false;
+
+ for(; *str; ++str) {
+ if (isspace(*str)) {
+ continue;
+ }
+
+ PruneCollection *list;
+ if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
+ ++str;
+ // special case, translates to worst UID at priority in blacklist
+ if (*str == '!') {
+ mWorstUidEnabled = true;
+ ++str;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ return 1;
+ }
+ continue;
+ }
+ if (!*str) {
+ return 1;
+ }
+ list = &mNaughty;
+ } else {
+ list = &mNice;
+ }
+
+ uid_t uid = Prune::uid_all;
+ if (isdigit(*str)) {
+ uid = 0;
+ do {
+ uid = uid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+
+ pid_t pid = Prune::pid_all;
+ if (*str == '/') {
+ ++str;
+ if (isdigit(*str)) {
+ pid = 0;
+ do {
+ pid = pid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+ }
+
+ if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
+ return 1;
+ }
+
+ if (*str && !isspace(*str)) {
+ return 1;
+ }
+
+ // insert sequentially into list
+ PruneCollection::iterator it = list->begin();
+ while (it != list->end()) {
+ Prune *p = *it;
+ int m = uid - p->mUid;
+ if (m == 0) {
+ if (p->mPid == p->pid_all) {
+ break;
+ }
+ if ((pid == p->pid_all) && (p->mPid != p->pid_all)) {
+ it = list->erase(it);
+ continue;
+ }
+ m = pid - p->mPid;
+ }
+ if (m >= 0) {
+ if (m > 0) {
+ list->insert(it, new Prune(uid,pid));
+ }
+ break;
+ }
+ ++it;
+ }
+ if (it == list->end()) {
+ list->push_back(new Prune(uid,pid));
+ }
+ if (!*str) {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void PruneList::format(char **strp) {
+ if (*strp) {
+ free(*strp);
+ *strp = NULL;
+ }
+
+ static const char nice_format[] = " %s";
+ const char *fmt = nice_format + 1;
+
+ android::String8 string;
+
+ if (mWorstUidEnabled) {
+ string.setTo("~!");
+ fmt = nice_format;
+ }
+
+ PruneCollection::iterator it;
+
+ for (it = mNice.begin(); it != mNice.end(); ++it) {
+ char *a = NULL;
+ (*it)->format(&a);
+
+ string.appendFormat(fmt, a);
+ fmt = nice_format;
+
+ free(a);
+ }
+
+ static const char naughty_format[] = " ~%s";
+ fmt = naughty_format + (*fmt != ' ');
+ for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+ char *a = NULL;
+ (*it)->format(&a);
+
+ string.appendFormat(fmt, a);
+ fmt = naughty_format;
+
+ free(a);
+ }
+
+ *strp = strdup(string.string());
+}
+
+// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
+// If there is scaling issues, resort to a better algorithm than linear
+// based on these assumptions.
+
+bool PruneList::naughty(LogBufferElement *element) {
+ PruneCollection::iterator it;
+ for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
+ if (!(*it)->cmp(element)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PruneList::nice(LogBufferElement *element) {
+ PruneCollection::iterator it;
+ for (it = mNice.begin(); it != mNice.end(); ++it) {
+ if (!(*it)->cmp(element)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif // USERDEBUG_BUILD
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
new file mode 100644
index 0000000..769d651
--- /dev/null
+++ b/logd/LogWhiteBlackList.h
@@ -0,0 +1,71 @@
+/*
+ * 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_WHITE_BLACK_LIST_H__
+#define _LOGD_LOG_WHITE_BLACK_LIST_H__
+
+#include <sys/types.h>
+
+#include <utils/List.h>
+
+#include <LogBufferElement.h>
+
+// White and Blacklist
+
+class Prune {
+ friend class PruneList;
+
+ const uid_t mUid;
+ const pid_t mPid;
+ int cmp(uid_t uid, pid_t pid) const;
+
+public:
+ static const uid_t uid_all = (uid_t) -1;
+ static const pid_t pid_all = (pid_t) -1;
+
+ Prune(uid_t uid, pid_t pid);
+
+ uid_t getUid() const { return mUid; }
+ pid_t getPid() const { return mPid; }
+
+ int cmp(LogBufferElement *e) const { return cmp(e->getUid(), e->getPid()); }
+
+ // *strp is malloc'd, use free to release
+ void format(char **strp);
+};
+
+typedef android::List<Prune *> PruneCollection;
+
+class PruneList {
+ PruneCollection mNaughty;
+ PruneCollection mNice;
+ bool mWorstUidEnabled;
+
+public:
+ PruneList();
+ ~PruneList();
+
+ int init(char *str);
+
+ bool naughty(LogBufferElement *element);
+ bool nice(LogBufferElement *element);
+ bool worstUidEnabled() const { return mWorstUidEnabled; }
+
+ // *strp is malloc'd, use free to release
+ void format(char **strp);
+};
+
+#endif // _LOGD_LOG_WHITE_BLACK_LIST_H__