diff options
-rw-r--r-- | include/log/logger.h | 16 | ||||
-rw-r--r-- | liblog/Android.mk | 6 | ||||
-rw-r--r-- | liblog/log_read.c | 87 | ||||
-rw-r--r-- | liblog/log_read_kern.c | 47 | ||||
-rw-r--r-- | logcat/Android.mk | 4 | ||||
-rw-r--r-- | logcat/logcat.cpp | 217 | ||||
-rw-r--r-- | logd/Android.mk | 11 | ||||
-rw-r--r-- | logd/CommandListener.cpp | 162 | ||||
-rw-r--r-- | logd/CommandListener.h | 8 | ||||
-rw-r--r-- | logd/LogBuffer.cpp | 217 | ||||
-rw-r--r-- | logd/LogBuffer.h | 24 | ||||
-rw-r--r-- | logd/LogStatistics.cpp | 555 | ||||
-rw-r--r-- | logd/LogStatistics.h | 152 | ||||
-rw-r--r-- | logd/LogWhiteBlackList.cpp | 243 | ||||
-rw-r--r-- | logd/LogWhiteBlackList.h | 71 |
15 files changed, 1776 insertions, 44 deletions
diff --git a/include/log/logger.h b/include/log/logger.h index 8810615..8dab234 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -140,12 +140,24 @@ struct logger; 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, pid_t pid); 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 @@ else 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 $(BUILD_HOST_STATIC_LIBRARY) 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 889442d..e4acac2 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -296,11 +296,8 @@ done: 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 @@ int android_logger_clear(struct logger *logger) 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 @@ int android_logger_get_log_size(struct logger *logger) 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 @@ int android_logger_get_log_readable_size(struct logger *logger) return -1; } - return atoi(buf); + return atol(buf); } /* @@ -357,6 +376,58 @@ 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); +} + +#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 b454729..483b6b6 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", @@ -225,16 +227,26 @@ int android_logger_clear(struct logger *logger) } /* 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); } @@ -248,6 +260,37 @@ int android_logger_get_log_version(struct logger *logger) return (ret < 0) ? 1 : ret; } +/* + * 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) +{ + 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 3c33938..00b5ba9 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -225,11 +225,23 @@ 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.\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" @@ -278,6 +290,12 @@ int main(int argc, char **argv) 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; log_device_t* devices = NULL; @@ -303,7 +321,13 @@ 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, +#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; @@ -335,7 +359,89 @@ int main(int argc, char **argv) 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) { + 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 +576,10 @@ int main(int argc, char **argv) } break; + case 'S': + printStatistics = 1; + break; + default: fprintf(stderr,"Unrecognized Option\n"); android::show_help(argv[0]); @@ -563,8 +673,17 @@ int main(int argc, char **argv) } } +#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) { @@ -578,7 +697,7 @@ int main(int argc, char **argv) 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); @@ -587,9 +706,95 @@ int main(int argc, char **argv) dev = dev->next; } +#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) { + delete [] buf; + buf = NULL; + break; + } + bool check = ret <= len; + len = ret; + if (check) { + break; + } + } + + if (!buf) { + perror("response 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); } +#ifdef USERDEBUG_BUILD + if (setLogSize || setPruneList) { + exit(0); + } +#endif if (clearLog) { exit(0); } diff --git a/logd/Android.mk b/logd/Android.mk index 744c9d3..b0bc746 100644 --- a/logd/Android.mk +++ b/logd/Android.mk @@ -4,6 +4,10 @@ include $(CLEAR_VARS) LOCAL_MODULE:= logd +ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),) +LOCAL_CFLAGS += -DUSERDEBUG_BUILD=1 +endif + LOCAL_SRC_FILES := \ main.cpp \ LogCommand.cpp \ @@ -13,12 +17,15 @@ LOCAL_SRC_FILES := \ FlushCommand.cpp \ LogBuffer.cpp \ LogBufferElement.cpp \ - LogTimes.cpp + LogTimes.cpp \ + LogStatistics.cpp \ + LogWhiteBlackList.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..12b10ca 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp @@ -37,7 +37,15 @@ CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, // 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, @@ -109,6 +117,43 @@ int CommandListener::GetBufSizeCmd::runCommand(SocketClient *cli, 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) @@ -133,3 +178,120 @@ int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient *cli, cli->sendMsg(buf); return 0; } + +CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer *buf) + : LogCommand("getStatistics") + , 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(); + 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 { + 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 861abbf..de1dcb9 100644 --- a/logd/CommandListener.h +++ b/logd/CommandListener.h @@ -53,7 +53,15 @@ private: 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 c5760f7..197b7e8 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp @@ -22,18 +22,26 @@ #include <log/logger.h> #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) { - int i; - for (i = 0; i < LOG_ID_MAX; i++) { - mSizes[i] = 0; - mElements[i] = 0; - } 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, @@ -93,8 +101,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 +111,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]; - if (sizes > LOG_BUFFER_SIZE) { - unsigned long sizeOver90Percent = sizes - ((LOG_BUFFER_SIZE * 9) / 10); - unsigned long elements = mElements[id]; + size_t sizes = stats.sizes(id); + 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; if (pruneRows <= elements) { @@ -136,22 +143,117 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { t++; } - LogBufferElementCollection::iterator it = mLogElements.begin(); + LogBufferElementCollection::iterator it; + + // prune by worst offender by uid + while (pruneRows > 0) { + // recalculate the worst offender on every batched pass + uid_t worst = (uid_t) -1; + size_t worst_sizes = 0; + size_t second_worst_sizes = 0; + +#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; + } + } + } + + bool kick = false; + for(it = mLogElements.begin(); it != mLogElements.end();) { + LogBufferElement *e = *it; + + if (oldest && (oldest->mStart <= e->getMonotonicTime())) { + break; + } + + 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()); + delete e; + kick = true; + pruneRows--; + if ((pruneRows == 0) || (worst_sizes < second_worst_sizes)) { + break; + } + worst_sizes -= len; + } +#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 +#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 (mSizes[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); - mSizes[id] -= e->getMsgLen(); - mElements[id]--; + stats.subtract(e->getMsgLen(), id, e->getUid(), e->getPid()); delete e; pruneRows--; } else { @@ -159,6 +261,32 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows) { } } +#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(); } @@ -172,16 +300,42 @@ 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; +} + +#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) { @@ -221,3 +375,24 @@ log_time LogBuffer::flushTo( return max; } + +void 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; + } + } + + stats.format(strp, uid, logMask, oldest); + + pthread_mutex_unlock(&mLogElementsLock); +} diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h index 1b50a8f..0745e56 100644 --- a/logd/LogBuffer.h +++ b/logd/LogBuffer.h @@ -25,6 +25,8 @@ #include "LogBufferElement.h" #include "LogTimes.h" +#include "LogStatistics.h" +#include "LogWhiteBlackList.h" typedef android::List<LogBufferElement *> LogBufferElementCollection; @@ -32,8 +34,13 @@ class LogBuffer { LogBufferElementCollection mLogElements; pthread_mutex_t mLogElementsLock; - unsigned long mSizes[LOG_ID_MAX]; - unsigned long mElements[LOG_ID_MAX]; + LogStatistics stats; + +#ifdef USERDEBUG_BUILD + PruneList mPrune; + + unsigned long mMaxSize[LOG_ID_MAX]; +#endif public: LastLogTimes &mTimes; @@ -49,7 +56,18 @@ public: 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. + 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); @@ -57,4 +75,4 @@ private: }; -#endif +#endif // _LOGD_LOG_BUFFER_H__ diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp new file mode 100644 index 0000000..49ee50d --- /dev/null +++ b/logd/LogStatistics.cpp @@ -0,0 +1,555 @@ +/* + * 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; +} + +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; + + 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; + } + } + } + + *buf = strdup(string.string()); +} diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h new file mode 100644 index 0000000..d44afa2 --- /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 + 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__ |