Merge "update minui library call"
diff --git a/adb/transport_local.c b/adb/transport_local.c
index d2dbca6..948cc15 100644
--- a/adb/transport_local.c
+++ b/adb/transport_local.c
@@ -48,7 +48,7 @@
* local transport it is connected. The list is used to detect when we're
* trying to connect twice to a given local transport.
*/
-#define ADB_LOCAL_TRANSPORT_MAX 16
+#define ADB_LOCAL_TRANSPORT_MAX 64
ADB_MUTEX_DEFINE( local_transports_lock );
diff --git a/include/log/log_read.h b/include/log/log_read.h
index 7edfe3c..bd9de12 100644
--- a/include/log/log_read.h
+++ b/include/log/log_read.h
@@ -17,11 +17,17 @@
#ifndef _LIBS_LOG_LOG_READ_H
#define _LIBS_LOG_LOG_READ_H
+#include <stdint.h>
#include <time.h>
/* struct log_time is a wire-format variant of struct timespec */
#define NS_PER_SEC 1000000000ULL
+
#ifdef __cplusplus
+
+// NB: do NOT define a copy constructor. This will result in structure
+// no longer being compatible with pass-by-value which is desired
+// efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
struct log_time {
public:
uint32_t tv_sec; // good to Feb 5 2106
@@ -32,16 +38,12 @@
tv_sec = T.tv_sec;
tv_nsec = T.tv_nsec;
}
- log_time(const log_time &T)
- {
- tv_sec = T.tv_sec;
- tv_nsec = T.tv_nsec;
- }
log_time(uint32_t sec, uint32_t nsec)
{
tv_sec = sec;
tv_nsec = nsec;
}
+ static const timespec EPOCH;
log_time()
{
}
@@ -86,6 +88,12 @@
{
return !(*this > T);
}
+ log_time operator-= (const timespec &T);
+ log_time operator- (const timespec &T) const
+ {
+ log_time local(*this);
+ return local -= T;
+ }
// log_time
bool operator== (const log_time &T) const
@@ -114,17 +122,31 @@
{
return !(*this > T);
}
+ log_time operator-= (const log_time &T);
+ log_time operator- (const log_time &T) const
+ {
+ log_time local(*this);
+ return local -= T;
+ }
uint64_t nsec() const
{
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
}
+
+ static const char default_format[];
+
+ // Add %#q for the fraction of a second to the standard library functions
+ char *strptime(const char *s, const char *format = default_format);
} __attribute__((__packed__));
+
#else
+
typedef struct log_time {
uint32_t tv_sec;
uint32_t tv_nsec;
} __attribute__((__packed__)) log_time;
+
#endif
#endif /* define _LIBS_LOG_LOG_READ_H */
diff --git a/include/log/logger.h b/include/log/logger.h
index 8810615..3c6ea30 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -12,6 +12,7 @@
#include <stdint.h>
#include <log/log.h>
+#include <log/log_read.h>
#ifdef __cplusplus
extern "C" {
@@ -140,15 +141,30 @@
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);
+struct logger_list *android_logger_list_alloc_time(int mode,
+ log_time start,
+ pid_t pid);
void android_logger_list_free(struct logger_list *logger_list);
/* In the purest sense, the following two are orthogonal interfaces */
int android_logger_list_read(struct logger_list *logger_list,
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
index 6fd81b7..1167d4b 100644
--- a/libdiskconfig/diskconfig.c
+++ b/libdiskconfig/diskconfig.c
@@ -30,7 +30,7 @@
#include <linux/fs.h>
#include <cutils/config_utils.h>
-#include <cutils/log.h>
+#include <log/log.h>
#include <diskconfig/diskconfig.h>
@@ -337,7 +337,7 @@
}
#if 1
- ALOGV("Device/file %s: size=%llu bytes, num_lba=%u, sect_size=%d",
+ ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d",
dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size);
#endif
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
index e325735..7659632 100644
--- a/libdiskconfig/diskutils.c
+++ b/libdiskconfig/diskutils.c
@@ -19,13 +19,14 @@
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
-#include <cutils/log.h>
+#include <log/log.h>
#include <diskconfig/diskconfig.h>
@@ -35,8 +36,8 @@
int dst_fd = -1;
int src_fd = -1;
uint8_t buffer[2048];
- int nr_bytes;
- int tmp;
+ ssize_t nr_bytes;
+ ssize_t tmp;
int done = 0;
uint64_t total = 0;
@@ -101,7 +102,7 @@
if (dst_fd >= 0)
fsync(dst_fd);
- ALOGI("Wrote %llu bytes to %s @ %lld", total, dst, offset);
+ ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, offset);
close(src_fd);
if (dst_fd >= 0)
diff --git a/liblog/Android.mk b/liblog/Android.mk
index c18dc48..a23de2d 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
@@ -46,10 +50,11 @@
endif
liblog_host_sources := $(liblog_sources) fake_log_device.c
+liblog_target_sources := $(liblog_sources) log_time.cpp
ifneq ($(TARGET_USES_LOGD),false)
-liblog_target_sources = $(liblog_sources) log_read.c
+liblog_target_sources += log_read.c
else
-liblog_target_sources = $(liblog_sources) log_read_kern.c
+liblog_target_sources += log_read_kern.c
endif
# Shared and static library for host
@@ -57,12 +62,18 @@
LOCAL_MODULE := liblog
LOCAL_SRC_FILES := $(liblog_host_sources)
LOCAL_LDLIBS := -lpthread
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS += -lrt
+endif
LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1
include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := liblog
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS := -lrt
+endif
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -72,6 +83,9 @@
LOCAL_MODULE := lib64log
LOCAL_SRC_FILES := $(liblog_host_sources)
LOCAL_LDLIBS := -lpthread
+ifeq ($(strip $(HOST_OS)),linux)
+LOCAL_LDLIBS += -lrt
+endif
LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -80,11 +94,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 889442d..2dd07e6 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <signal.h>
#include <stddef.h>
#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
@@ -234,6 +235,7 @@
struct listnode node;
int mode;
unsigned int tail;
+ log_time start;
pid_t pid;
int sock;
};
@@ -270,11 +272,11 @@
const char *msg, char *buf, size_t buf_size)
{
ssize_t ret;
+ int errno_save = 0;
int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (sock < 0) {
- ret = sock;
- goto done;
+ return sock;
}
if (msg) {
@@ -290,30 +292,39 @@
done:
if ((ret == -1) && errno) {
- ret = -errno;
+ errno_save = errno;
}
close(sock);
+ if (errno_save) {
+ errno = errno_save;
+ }
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;
}
if (strncmp(buf, "success", 7)) {
+ errno = EINVAL;
return -1;
}
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 +337,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 +371,7 @@
return -1;
}
- return atoi(buf);
+ return atol(buf);
}
/*
@@ -357,6 +382,58 @@
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)
@@ -370,6 +447,8 @@
list_init(&logger_list->node);
logger_list->mode = mode;
+ logger_list->start.tv_sec = 0;
+ logger_list->start.tv_nsec = 0;
logger_list->tail = tail;
logger_list->pid = pid;
logger_list->sock = -1;
@@ -377,6 +456,27 @@
return logger_list;
}
+struct logger_list *android_logger_list_alloc_time(int mode,
+ log_time start,
+ pid_t pid)
+{
+ struct logger_list *logger_list;
+
+ logger_list = calloc(1, sizeof(*logger_list));
+ if (!logger_list) {
+ return NULL;
+ }
+
+ list_init(&logger_list->node);
+ logger_list->mode = mode;
+ logger_list->start = start;
+ logger_list->tail = 0;
+ logger_list->pid = pid;
+ logger_list->sock = -1;
+
+ return logger_list;
+}
+
/* android_logger_list_register unimplemented, no use case */
/* android_logger_list_unregister unimplemented, no use case */
@@ -493,6 +593,15 @@
cp += ret;
}
+ if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
+ ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
+ logger_list->start.tv_sec,
+ logger_list->start.tv_nsec);
+ ret = min(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ }
+
if (logger_list->pid) {
ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
ret = min(ret, remaining);
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
index b454729..9cccb1d 100644
--- a/liblog/log_read_kern.c
+++ b/liblog/log_read_kern.c
@@ -51,6 +51,8 @@
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 @@
}
/* 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 @@
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)
@@ -265,6 +308,13 @@
return logger_list;
}
+struct logger_list *android_logger_list_alloc_time(int mode,
+ log_time start UNUSED,
+ pid_t pid)
+{
+ return android_logger_list_alloc(mode, 0, pid);
+}
+
/* android_logger_list_register unimplemented, no use case */
/* android_logger_list_unregister unimplemented, no use case */
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
new file mode 100644
index 0000000..755c2d9
--- /dev/null
+++ b/liblog/log_time.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <log/log_read.h>
+
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%3q";
+const timespec log_time::EPOCH = { 0, 0 };
+
+// Add %#q for fractional seconds to standard strptime function
+
+char *log_time::strptime(const char *s, const char *format) {
+ time_t now;
+#ifdef __linux__
+ *this = log_time(CLOCK_REALTIME);
+ now = tv_sec;
+#else
+ time(&now);
+ tv_sec = now;
+ tv_nsec = 0;
+#endif
+
+ struct tm *ptm;
+#if (defined(HAVE_LOCALTIME_R))
+ struct tm tmBuf;
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+
+ char fmt[strlen(format) + 1];
+ strcpy(fmt, format);
+
+ char *ret = const_cast<char *> (s);
+ char *cp;
+ for (char *f = cp = fmt; ; ++cp) {
+ if (!*cp) {
+ if (f != cp) {
+ ret = ::strptime(ret, f, ptm);
+ }
+ break;
+ }
+ if (*cp != '%') {
+ continue;
+ }
+ char *e = cp;
+ ++e;
+#if (defined(__BIONIC__))
+ if (*e == 's') {
+ *cp = '\0';
+ if (*f) {
+ ret = ::strptime(ret, f, ptm);
+ if (!ret) {
+ break;
+ }
+ }
+ tv_sec = 0;
+ while (isdigit(*ret)) {
+ tv_sec = tv_sec * 10 + *ret - '0';
+ ++ret;
+ }
+ now = tv_sec;
+#if (defined(HAVE_LOCALTIME_R))
+ ptm = localtime_r(&now, &tmBuf);
+#else
+ ptm = localtime(&now);
+#endif
+ } else
+#endif
+ {
+ unsigned num = 0;
+ while (isdigit(*e)) {
+ num = num * 10 + *e - '0';
+ ++e;
+ }
+ if (*e != 'q') {
+ continue;
+ }
+ *cp = '\0';
+ if (*f) {
+ ret = ::strptime(ret, f, ptm);
+ if (!ret) {
+ break;
+ }
+ }
+ unsigned long mul = NS_PER_SEC;
+ if (num == 0) {
+ num = INT_MAX;
+ }
+ tv_nsec = 0;
+ while (isdigit(*ret) && num && (mul > 1)) {
+ --num;
+ mul /= 10;
+ tv_nsec = tv_nsec + (*ret - '0') * mul;
+ ++ret;
+ }
+ }
+ f = cp = e;
+ ++f;
+ }
+
+ if (ret) {
+ tv_sec = mktime(ptm);
+ return ret;
+ }
+
+ // Upon error, place a known value into the class, the current time.
+#ifdef __linux__
+ *this = log_time(CLOCK_REALTIME);
+#else
+ time(&now);
+ tv_sec = now;
+ tv_nsec = 0;
+#endif
+ return ret;
+}
+
+log_time log_time::operator-= (const timespec &T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = EPOCH;
+ }
+
+ if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+}
+
+log_time log_time::operator-= (const log_time &T) {
+ // No concept of negative time, clamp to EPOCH
+ if (*this <= T) {
+ return *this = EPOCH;
+ }
+
+ if (this->tv_nsec < T.tv_nsec) {
+ --this->tv_sec;
+ this->tv_nsec = NS_PER_SEC + this->tv_nsec - T.tv_nsec;
+ } else {
+ this->tv_nsec -= T.tv_nsec;
+ }
+ this->tv_sec -= T.tv_sec;
+
+ return *this;
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 01f9249..aebddc8 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -17,22 +17,23 @@
/*
* Read-only access to Zip archives, with minimal heap allocation.
*/
-#include "ziparchive/zip_archive.h"
-
-#include <zlib.h>
#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <limits.h>
#include <log/log.h>
-#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utils/FileMap.h>
+#include <zlib.h>
#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd
+#include "ziparchive/zip_archive.h"
+
// This is for windows. If we don't open a file in binary mode, weirds
// things will happen.
#ifndef O_BINARY
@@ -217,8 +218,7 @@
ssize_t actual = TEMP_FAILURE_RETRY(read(fd, buf, get_size));
if (actual != get_size) {
- ALOGW("CopyFileToFile: copy read failed (%d vs %zd)",
- (int) actual, get_size);
+ ALOGW("CopyFileToFile: copy read failed (%zd vs %zd)", actual, get_size);
return kIoError;
}
@@ -337,12 +337,12 @@
const off64_t search_start = file_length - read_amount;
if (lseek64(fd, search_start, SEEK_SET) != search_start) {
- ALOGW("Zip: seek %lld failed: %s", search_start, strerror(errno));
+ ALOGW("Zip: seek %" PRId64 " failed: %s", search_start, strerror(errno));
return kIoError;
}
ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scan_buffer, read_amount));
if (actual != (ssize_t) read_amount) {
- ALOGW("Zip: read %zd failed: %s", read_amount, strerror(errno));
+ ALOGW("Zip: read %u failed: %s", read_amount, strerror(errno));
return kIoError;
}
@@ -379,7 +379,7 @@
const off64_t dir_offset = get4LE(eocd_ptr + kEOCDFileOffset);
if (dir_offset + dir_size > eocd_offset) {
- ALOGW("Zip: bad offsets (dir %lld, size %lld, eocd %lld)",
+ ALOGW("Zip: bad offsets (dir %" PRId64 ", size %" PRId64 ", eocd %" PRId64 ")",
dir_offset, dir_size, eocd_offset);
return kInvalidOffset;
}
@@ -388,8 +388,8 @@
return kEmptyArchive;
}
- ALOGV("+++ num_entries=%d dir_size=%d dir_offset=%d", num_entries, dir_size,
- dir_offset);
+ ALOGV("+++ num_entries=%d dir_size=%" PRId64 " dir_offset=%" PRId64,
+ num_entries, dir_size, dir_offset);
/*
* It all looks good. Create a mapping for the CD, and set the fields
@@ -430,12 +430,12 @@
}
if (file_length > (off64_t) 0xffffffff) {
- ALOGV("Zip: zip file too long %d", file_length);
+ ALOGV("Zip: zip file too long %" PRId64, file_length);
return kInvalidFile;
}
if (file_length < (int64_t) kEOCDLen) {
- ALOGV("Zip: length %ld is too small to be zip", file_length);
+ ALOGV("Zip: length %" PRId64 " is too small to be zip", file_length);
return kInvalidFile;
}
@@ -503,7 +503,7 @@
const off64_t local_header_offset = get4LE(ptr + kCDELocalOffset);
if (local_header_offset >= archive->directory_offset) {
- ALOGW("Zip: bad LFH offset %lld at entry %d", local_header_offset, i);
+ ALOGW("Zip: bad LFH offset %" PRId64 " at entry %d", local_header_offset, i);
goto bail;
}
@@ -522,8 +522,8 @@
ptr += kCDELen + file_name_length + extra_length + comment_length;
if ((size_t)(ptr - cd_ptr) > cd_length) {
- ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d",
- (int) (ptr - cd_ptr), cd_length, i);
+ ALOGW("Zip: bad CD advance (%zu vs %zu) at entry %d",
+ (size_t) (ptr - cd_ptr), cd_length, i);
goto bail;
}
}
@@ -630,7 +630,7 @@
// is Windows. Only recent versions of windows support unix like forks,
// and even there the semantics are quite different.
if (lseek64(fd, off, SEEK_SET) != off) {
- ALOGW("Zip: failed seek to offset %lld", off);
+ ALOGW("Zip: failed seek to offset %" PRId64, off);
return kIoError;
}
@@ -686,12 +686,12 @@
ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
local_header_offset);
if (actual != sizeof(lfh_buf)) {
- ALOGW("Zip: failed reading lfh name from offset %lld", local_header_offset);
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64, local_header_offset);
return kIoError;
}
if (get4LE(lfh_buf) != kLFHSignature) {
- ALOGW("Zip: didn't find signature at start of lfh, offset=%lld",
+ ALOGW("Zip: didn't find signature at start of lfh, offset=%" PRId64,
local_header_offset);
return kInvalidOffset;
}
@@ -733,7 +733,7 @@
name_offset);
if (actual != nameLen) {
- ALOGW("Zip: failed reading lfh name from offset %lld", name_offset);
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64, name_offset);
free(name_buf);
return kIoError;
}
@@ -751,19 +751,19 @@
const off64_t data_offset = local_header_offset + kLFHLen + lfhNameLen + lfhExtraLen;
if (data_offset > cd_offset) {
- ALOGW("Zip: bad data offset %lld in zip", (off64_t) data_offset);
+ ALOGW("Zip: bad data offset %" PRId64 " in zip", data_offset);
return kInvalidOffset;
}
if ((off64_t)(data_offset + data->compressed_length) > cd_offset) {
- ALOGW("Zip: bad compressed length in zip (%lld + %zd > %lld)",
+ ALOGW("Zip: bad compressed length in zip (%" PRId64 " + %zd > %" PRId64 ")",
data_offset, data->compressed_length, cd_offset);
return kInvalidOffset;
}
if (data->method == kCompressStored &&
(off64_t)(data_offset + data->uncompressed_length) > cd_offset) {
- ALOGW("Zip: bad uncompressed length in zip (%lld + %zd > %lld)",
+ ALOGW("Zip: bad uncompressed length in zip (%" PRId64 " + %d > %" PRId64 ")",
data_offset, data->uncompressed_length, cd_offset);
return kInvalidOffset;
}
@@ -903,7 +903,7 @@
const ssize_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, read_buf, getSize));
if (actual != getSize) {
- ALOGW("Zip: inflate read failed (%d vs %zd)", actual, getSize);
+ ALOGW("Zip: inflate read failed (%zd vs %zd)", actual, getSize);
result = kIoError;
goto z_bail;
}
@@ -946,7 +946,7 @@
*crc_out = zstream.adler;
if (zstream.total_out != uncompressed_length || compressed_length != 0) {
- ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)",
+ ALOGW("Zip: size mismatch on inflated file (%ld vs %u)",
zstream.total_out, uncompressed_length);
result = kInconsistentInformation;
goto z_bail;
@@ -967,7 +967,7 @@
off64_t data_offset = entry->offset;
if (lseek64(archive->fd, data_offset, SEEK_SET) != data_offset) {
- ALOGW("Zip: lseek to data at %lld failed", (off64_t) data_offset);
+ ALOGW("Zip: lseek to data at %" PRId64 " failed", data_offset);
return kIoError;
}
@@ -990,7 +990,7 @@
// TODO: Fix this check by passing the right flags to inflate2 so that
// it calculates the CRC for us.
if (entry->crc32 != crc && false) {
- ALOGW("Zip: crc mismatch: expected %u, was %llu", entry->crc32, crc);
+ ALOGW("Zip: crc mismatch: expected %u, was %" PRIu64, entry->crc32, crc);
return kInconsistentInformation;
}
@@ -1010,8 +1010,8 @@
int result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
if (result == -1) {
- ALOGW("Zip: unable to truncate file to %lld: %s", declared_length + current_offset,
- strerror(errno));
+ ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+ declared_length + current_offset, strerror(errno));
return kIoError;
}
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..7bf91a9 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -17,6 +17,7 @@
#include <cutils/sockets.h>
#include <log/log.h>
+#include <log/log_read.h>
#include <log/logger.h>
#include <log/logd.h>
#include <log/logprint.h>
@@ -225,11 +226,23 @@
" -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,13 +291,20 @@
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;
log_device_t* dev;
bool needBinary = false;
struct logger_list *logger_list;
- int tail_lines = 0;
+ unsigned int tail_lines = 0;
+ log_time tail_time(log_time::EPOCH);
signal(SIGPIPE, exit);
@@ -303,7 +323,13 @@
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;
@@ -328,14 +354,121 @@
mode = O_RDONLY | O_NDELAY;
/* FALLTHRU */
case 'T':
- tail_lines = atoi(optarg);
+ if (strspn(optarg, "0123456789") != strlen(optarg)) {
+ char *cp = tail_time.strptime(optarg,
+ log_time::default_format);
+ if (!cp) {
+ fprintf(stderr,
+ "ERROR: -%c \"%s\" not in \"%s\" time format\n",
+ ret, optarg, log_time::default_format);
+ exit(1);
+ }
+ if (*cp) {
+ char c = *cp;
+ *cp = '\0';
+ fprintf(stderr,
+ "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+ ret, optarg, c, cp + 1);
+ *cp = c;
+ }
+ } else {
+ tail_lines = atoi(optarg);
+ if (!tail_lines) {
+ fprintf(stderr,
+ "WARNING: -%c %s invalid, setting to 1\n",
+ ret, optarg);
+ tail_lines = 1;
+ }
+ }
break;
case 'g':
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 +603,10 @@
}
break;
+ case 'S':
+ printStatistics = 1;
+ break;
+
default:
fprintf(stderr,"Unrecognized Option\n");
android::show_help(argv[0]);
@@ -544,7 +681,11 @@
}
dev = devices;
- logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+ if (tail_time != log_time::EPOCH) {
+ logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
+ } else {
+ logger_list = android_logger_list_alloc(mode, tail_lines, 0);
+ }
while (dev) {
dev->logger_list = logger_list;
dev->logger = android_logger_open(logger_list,
@@ -563,8 +704,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) {
@@ -578,7 +728,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);
@@ -587,9 +737,95 @@
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/logcat/tests/Android.mk b/logcat/tests/Android.mk
index bdaec14..733af31 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -28,7 +28,11 @@
-g \
-Wall -Wextra \
-Werror \
- -fno-builtin \
+ -fno-builtin
+
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+test_c_flags += -DUSERDEBUG_BUILD=1
+endif
test_src_files := \
logcat_test.cpp \
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index fc696bb..818a978 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -527,3 +527,100 @@
ASSERT_EQ(1, signals);
}
+
+#ifdef USERDEBUG_BUILD
+static bool get_white_black(char **list) {
+ FILE *fp;
+
+ fp = popen("logcat -p 2>/dev/null", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
+ return false;
+ }
+
+ char buffer[5120];
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char *hold = *list;
+ char *buf = buffer;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ char *end = buf + strlen(buf);
+ while (isspace(*--end) && (end >= buf)) {
+ *end = '\0';
+ }
+ if (end < buf) {
+ continue;
+ }
+ if (hold) {
+ asprintf(list, "%s %s", hold, buf);
+ free(hold);
+ } else {
+ asprintf(list, "%s", buf);
+ }
+ }
+ pclose(fp);
+ return *list != NULL;
+}
+
+static bool set_white_black(const char *list) {
+ FILE *fp;
+
+ char buffer[5120];
+
+ snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list);
+ fp = popen(buffer, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: %s\n", buffer);
+ return false;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ char *buf = buffer;
+ while (isspace(*buf)) {
+ ++buf;
+ }
+ char *end = buf + strlen(buf);
+ while (isspace(*--end) && (end >= buf)) {
+ *end = '\0';
+ }
+ if (end < buf) {
+ continue;
+ }
+ fprintf(stderr, "%s\n", buf);
+ pclose(fp);
+ return false;
+ }
+ return pclose(fp) == 0;
+}
+
+TEST(logcat, white_black_adjust) {
+ char *list = NULL;
+ char *adjust = NULL;
+
+ ASSERT_EQ(true, get_white_black(&list));
+
+ static const char adjustment[] = "~! ~1000";
+ ASSERT_EQ(true, set_white_black(adjustment));
+ ASSERT_EQ(true, get_white_black(&adjust));
+ if (strcmp(adjustment, adjust)) {
+ fprintf(stderr, "ERROR: '%s' != '%s'\n", adjustment, adjust);
+ }
+ ASSERT_STREQ(adjustment, adjust);
+ free(adjust);
+ adjust = NULL;
+
+ ASSERT_EQ(true, set_white_black(list));
+ ASSERT_EQ(true, get_white_black(&adjust));
+ if (strcmp(list, adjust)) {
+ fprintf(stderr, "ERROR: '%s' != '%s'\n", list, adjust);
+ }
+ ASSERT_STREQ(list, adjust);
+ free(adjust);
+ adjust = NULL;
+
+ free(list);
+ list = NULL;
+}
+#endif // USERDEBUG_BUILD
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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/FlushCommand.cpp b/logd/FlushCommand.cpp
index 0b8c31b..3be07c0 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -26,12 +26,14 @@
bool nonBlock,
unsigned long tail,
unsigned int logMask,
- pid_t pid)
+ pid_t pid,
+ log_time start)
: mReader(reader)
, mNonBlock(nonBlock)
, mTail(tail)
, mLogMask(logMask)
, mPid(pid)
+ , mStart(start)
{ }
// runSocketCommand is called once for every open client on the
@@ -69,7 +71,7 @@
LogTimeEntry::unlock();
return;
}
- entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid);
+ entry = new LogTimeEntry(mReader, client, mNonBlock, mTail, mLogMask, mPid, mStart);
times.push_back(entry);
}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
index 715daac..f34c06a 100644
--- a/logd/FlushCommand.h
+++ b/logd/FlushCommand.h
@@ -16,8 +16,13 @@
#ifndef _FLUSH_COMMAND_H
#define _FLUSH_COMMAND_H
+#include <log/log_read.h>
#include <sysutils/SocketClientCommand.h>
+class LogBufferElement;
+
+#include "LogTimes.h"
+
class LogReader;
class FlushCommand : public SocketClientCommand {
@@ -26,13 +31,15 @@
unsigned long mTail;
unsigned int mLogMask;
pid_t mPid;
+ log_time mStart;
public:
FlushCommand(LogReader &mReader,
bool nonBlock = false,
unsigned long tail = -1,
unsigned int logMask = -1,
- pid_t pid = 0);
+ pid_t pid = 0,
+ log_time start = LogTimeEntry::EPOCH);
virtual void runSocketCommand(SocketClient *client);
static bool hasReadLogs(SocketClient *client);
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 @@
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 @@
//
// 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 @@
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 @@
}
}
+#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 @@
// 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 @@
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 @@
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 @@
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 @@
};
-#endif
+#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 01cc9de..8d45f34 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -24,7 +24,7 @@
#include "LogBufferElement.h"
#include "LogReader.h"
-const log_time LogBufferElement::FLUSH_ERROR(0, 0);
+const log_time LogBufferElement::FLUSH_ERROR((uint32_t)0, (uint32_t)0);
LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, const char *msg,
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 5b540bf..60a3507 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ctype.h>
#include <poll.h>
#include <sys/socket.h>
#include <cutils/sockets.h>
@@ -50,6 +51,14 @@
tail = atol(cp + sizeof(_tail) - 1);
}
+ log_time start(log_time::EPOCH);
+ static const char _start[] = " start=";
+ cp = strstr(buffer, _start);
+ if (cp) {
+ // Parse errors will result in current time
+ start.strptime(cp + sizeof(_start) - 1, "%s.%q");
+ }
+
unsigned int logMask = -1;
static const char _logIds[] = " lids=";
cp = strstr(buffer, _logIds);
@@ -58,9 +67,8 @@
cp += sizeof(_logIds) - 1;
while (*cp && *cp != '\0') {
int val = 0;
- while (('0' <= *cp) && (*cp <= '9')) {
- val *= 10;
- val += *cp - '0';
+ while (isdigit(*cp)) {
+ val = val * 10 + *cp - '0';
++cp;
}
logMask |= 1 << val;
@@ -83,7 +91,63 @@
nonBlock = true;
}
- FlushCommand command(*this, nonBlock, tail, logMask, pid);
+ // Convert realtime to monotonic time
+ if (start == log_time::EPOCH) {
+ start = LogTimeEntry::EPOCH;
+ } else {
+ class LogFindStart {
+ const pid_t mPid;
+ const unsigned mLogMask;
+ bool startTimeSet;
+ log_time &start;
+ log_time last;
+
+ public:
+ LogFindStart(unsigned logMask, pid_t pid, log_time &start)
+ : mPid(pid)
+ , mLogMask(logMask)
+ , startTimeSet(false)
+ , start(start)
+ , last(LogTimeEntry::EPOCH)
+ { }
+
+ static bool callback(const LogBufferElement *element, void *obj) {
+ LogFindStart *me = reinterpret_cast<LogFindStart *>(obj);
+ if (!me->startTimeSet
+ && (!me->mPid || (me->mPid == element->getPid()))
+ && (me->mLogMask & (1 << element->getLogId()))) {
+ if (me->start == element->getRealTime()) {
+ me->start = element->getMonotonicTime();
+ me->startTimeSet = true;
+ } else {
+ if (me->start < element->getRealTime()) {
+ me->start = me->last;
+ me->startTimeSet = true;
+ }
+ me->last = element->getMonotonicTime();
+ }
+ }
+ return false;
+ }
+
+ bool found() { return startTimeSet; }
+ } logFindStart(logMask, pid, start);
+
+ logbuf().flushTo(cli, LogTimeEntry::EPOCH,
+ FlushCommand::hasReadLogs(cli),
+ logFindStart.callback, &logFindStart);
+
+ if (!logFindStart.found()) {
+ if (nonBlock) {
+ doSocketDelete(cli);
+ return false;
+ }
+ log_time now(CLOCK_MONOTONIC);
+ start = now;
+ }
+ }
+
+ FlushCommand command(*this, nonBlock, tail, logMask, pid, start);
command.runSocketCommand(cli);
return true;
}
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/LogTimes.cpp b/logd/LogTimes.cpp
index 67cc65e..8cb015c 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -25,7 +25,8 @@
LogTimeEntry::LogTimeEntry(LogReader &reader, SocketClient *client,
bool nonBlock, unsigned long tail,
- unsigned int logMask, pid_t pid)
+ unsigned int logMask, pid_t pid,
+ log_time start)
: mRefCount(1)
, mRelease(false)
, mError(false)
@@ -39,7 +40,7 @@
, mTail(tail)
, mIndex(0)
, mClient(client)
- , mStart(EPOCH)
+ , mStart(start)
, mNonBlock(nonBlock)
, mEnd(CLOCK_MONOTONIC)
{ }
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index cb6f566..beaf646 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -45,7 +45,8 @@
public:
LogTimeEntry(LogReader &reader, SocketClient *client, bool nonBlock,
- unsigned long tail, unsigned int logMask, pid_t pid);
+ unsigned long tail, unsigned int logMask, pid_t pid,
+ log_time start);
SocketClient *mClient;
static const struct timespec EPOCH;
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__
diff --git a/toolbox/ls.c b/toolbox/ls.c
index 3cc5bb2..06910ee 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -137,7 +137,7 @@
/* blocks are 512 bytes, we want output to be KB */
if ((flags & LIST_SIZE) != 0) {
- printf("%lld ", s->st_blocks / 2);
+ printf("%lld ", (long long)s->st_blocks / 2);
}
if ((flags & LIST_CLASSIFY) != 0) {
@@ -205,7 +205,7 @@
break;
case S_IFREG:
printf("%s %-8s %-8s %8lld %s %s\n",
- mode, user, group, s->st_size, date, name);
+ mode, user, group, (long long)s->st_size, date, name);
break;
case S_IFLNK: {
char linkto[256];
@@ -321,7 +321,7 @@
}
if(flags & LIST_INODE) {
- printf("%8llu ", s.st_ino);
+ printf("%8llu ", (unsigned long long)s.st_ino);
}
if ((flags & LIST_MACLABEL) != 0) {
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c
index a76f968..0c85e76 100644
--- a/toolbox/schedtop.c
+++ b/toolbox/schedtop.c
@@ -1,26 +1,23 @@
-#include <stdio.h>
-#include <stdlib.h>
#include <ctype.h>
+#include <dirent.h>
#include <fcntl.h>
-#include <unistd.h>
-
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
-
#include <sys/stat.h>
#include <sys/types.h>
-#include <dirent.h>
-#include <signal.h>
-
-#include <pwd.h>
+#include <unistd.h>
struct thread_info {
int pid;
int tid;
char name[64];
- uint64_t exec_time;
- uint64_t delay_time;
- uint32_t run_count;
+ unsigned long long exec_time;
+ unsigned long long delay_time;
+ unsigned long long run_count;
};
struct thread_table {
@@ -110,7 +107,8 @@
sprintf(line, "/proc/%d/schedstat", pid);
if (read_line(line, sizeof(line)))
return;
- if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3)
+ if(sscanf(line, "%llu %llu %llu",
+ &info->exec_time, &info->delay_time, &info->run_count) != 3)
return;
if (proc_info) {
proc_info->exec_time += info->exec_time;
@@ -183,7 +181,7 @@
if (j == threads.active)
printf(" %5u died\n", tid);
else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count)
- printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", tid,
+ printf(" %5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", tid,
NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time),
NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time),
threads.data[j].run_count - last_threads.data[i].run_count,
@@ -236,7 +234,7 @@
if (j == processes.active)
printf("%5u died\n", pid);
else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
- printf("%5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid,
+ printf("%5u %2u.%0*u %2u.%0*u %5llu %5u.%0*u %5u.%0*u %7llu %s\n", pid,
NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
processes.data[j].run_count - last_processes.data[i].run_count,