diff options
Diffstat (limited to 'liblog')
-rw-r--r-- | liblog/Android.mk | 38 | ||||
-rw-r--r-- | liblog/README | 46 | ||||
-rw-r--r-- | liblog/event.logtags | 36 | ||||
-rw-r--r-- | liblog/fake_log_device.c | 22 | ||||
-rw-r--r-- | liblog/log_is_loggable.c | 61 | ||||
-rw-r--r-- | liblog/log_read.c | 193 | ||||
-rw-r--r-- | liblog/log_read_kern.c | 22 | ||||
-rw-r--r-- | liblog/log_time.cpp | 26 | ||||
-rw-r--r-- | liblog/logd_write.c | 239 | ||||
-rw-r--r-- | liblog/logd_write_kern.c | 63 | ||||
-rw-r--r-- | liblog/logprint.c | 121 | ||||
-rw-r--r-- | liblog/tests/Android.mk | 11 | ||||
-rw-r--r-- | liblog/tests/benchmark_main.cpp | 1 | ||||
-rw-r--r-- | liblog/tests/libc_test.cpp | 4 | ||||
-rw-r--r-- | liblog/tests/liblog_benchmark.cpp | 18 | ||||
-rw-r--r-- | liblog/tests/liblog_test.cpp | 14 | ||||
-rw-r--r-- | liblog/uio.c | 4 |
17 files changed, 634 insertions, 285 deletions
diff --git a/liblog/Android.mk b/liblog/Android.mk index a4e5f5e..70aff83 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -16,6 +16,14 @@ LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) +# This is what we want to do: +# liblog_cflags := $(shell \ +# sed -n \ +# 's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \ +# $(LOCAL_PATH)/event.logtags) +# so make sure we do not regret hard-coding it as follows: +liblog_cflags := -DLIBLOG_LOG_TAG=1005 + ifneq ($(TARGET_USES_LOGD),false) liblog_sources := logd_write.c else @@ -25,28 +33,20 @@ 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 -WITH_MINGW := -ifeq ($(HOST_OS),windows) - ifeq ($(strip $(USE_CYGWIN)),) - WITH_MINGW := true - endif -endif -# USE_MINGW is defined when we build against Mingw on Linux -ifneq ($(strip $(USE_MINGW)),) - WITH_MINGW := true -endif -ifndef WITH_MINGW +ifeq ($(strip $(USE_MINGW)),) liblog_sources += \ - logprint.c \ event_tag_map.c else liblog_sources += \ uio.c endif -liblog_host_sources := $(liblog_sources) fake_log_device.c -liblog_target_sources := $(liblog_sources) log_time.cpp +liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags +liblog_target_sources := $(liblog_sources) log_time.cpp log_is_loggable.c +ifeq ($(strip $(USE_MINGW)),) +liblog_target_sources += logprint.c +endif ifneq ($(TARGET_USES_LOGD),false) liblog_target_sources += log_read.c else @@ -57,7 +57,7 @@ endif # ======================================================== LOCAL_MODULE := liblog LOCAL_SRC_FILES := $(liblog_host_sources) -LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags) LOCAL_MULTILIB := both include $(BUILD_HOST_STATIC_LIBRARY) @@ -76,13 +76,17 @@ include $(BUILD_HOST_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_SRC_FILES := $(liblog_target_sources) -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Werror $(liblog_cflags) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblog -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := -Werror $(liblog_cflags) + +# TODO: This is to work around b/19059885. Remove after root cause is fixed +LOCAL_LDFLAGS_arm := -Wl,--hash-style=sysv + include $(BUILD_SHARED_LIBRARY) include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/liblog/README b/liblog/README index d7472e4..f29ac04 100644 --- a/liblog/README +++ b/liblog/README @@ -111,24 +111,56 @@ DESCRIPTION ger_list_alloc, calling in turn the android_logger_open for each log id. Each entry can be retrieved with android_logger_list_read. The log(s) can be closed with android_logger_list_free. The logs should be - opened with an O_RDONLY mode. O_NDELAY mode will report when the log - reading is done with an EAGAIN error return code, otherwise the - android_logger_list_read call will block for new entries. + opened with an ANDROID_LOG_RDONLY mode. ANDROID_LOG_NONBLOCK mode + will report when the log reading is done with an EAGAIN error return + code, otherwise the android_logger_list_read call will block for new + entries. + + The ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to + switch from the active logs to the persistent logs from before the last + reboot. The value returned by android_logger_open can be used as a parameter to the android_logger_clear function to empty the sub-log. It is recom‐ - mended to only open log O_WRONLY. + mended to only open log ANDROID_LOG_WRONLY in that case. The value returned by android_logger_open can be used as a parameter to the android_logger_get_log_(size|readable_size|version) to retrieve the sub-log maximum size, readable size and log buffer format protocol ver‐ sion respectively. android_logger_get_id returns the id that was used - when opening the sub-log. It is recommended to open the log O_RDONLY - in these cases. + when opening the sub-log. It is recommended to open the log + ANDROID_LOG_RDONLY in these cases. + +ERRORS + If messages fail, a negative error code will be returned to the caller. + + The -ENOTCONN return code indicates that the logger daemon is stopped. + + The -EBADF return code indicates that the log access point can not be + opened, or the log buffer id is out of range. + + For the -EAGAIN return code, this means that the logging message was + temporarily backed-up either because of Denial Of Service (DOS) logging + pressure from some chatty application or service in the Android system, + or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen. + To aid in diagnosing the occurence of this, a binary event from liblog + will be sent to the log daemon once a new message can get through + indicating how many messages were dropped as a result. Please take + action to resolve the structural problems at the source. + + It is generally not advised for the caller to retry the -EAGAIN return + code as this will only make the problem(s) worse and cause your + application to temporarily drop to the logger daemon priority, BATCH + scheduling policy and background task cgroup. If you require a group of + messages to be passed atomically, merge them into one message with + embedded newlines to the maximum length LOGGER_ENTRY_MAX_PAYLOAD. + + Other return codes from writing operation can be returned. Since the + library retries on EINTR, -EINTR should never be returned. SEE ALSO syslogd(8) - 17 Dec 2013 LIBLOG(3) + 24 Jan 2014 LIBLOG(3) diff --git a/liblog/event.logtags b/liblog/event.logtags new file mode 100644 index 0000000..72ecab1 --- /dev/null +++ b/liblog/event.logtags @@ -0,0 +1,36 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (<name>|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# TODO: generate ".java" and ".h" files with integer constants from this file. + +1005 liblog (dropped|1) diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index 136792d..8a8ece2 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -29,7 +29,7 @@ #include <log/logd.h> -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) #include <pthread.h> #endif @@ -88,7 +88,7 @@ typedef struct LogState { } LogState; -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) /* * Locking. Since we're emulating a device, we need to be prepared * to have multiple callers at the same time. This lock is used @@ -106,10 +106,10 @@ static void unlock() { pthread_mutex_unlock(&fakeLogDeviceLock); } -#else // !HAVE_PTHREADS +#else // !defined(_WIN32) #define lock() ((void)0) #define unlock() ((void)0) -#endif // !HAVE_PTHREADS +#endif // !defined(_WIN32) /* @@ -320,9 +320,9 @@ static const char* getPriorityString(int priority) return priorityStrings[idx]; } -#ifndef HAVE_WRITEV +#if defined(_WIN32) /* - * Some platforms like WIN32 do not have writev(). + * WIN32 does not have writev(). * Make up something to replace it. */ static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { @@ -352,7 +352,7 @@ static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { static void showLog(LogState *state, int logPrio, const char* tag, const char* msg) { -#if defined(HAVE_LOCALTIME_R) +#if !defined(_WIN32) struct tm tmBuf; #endif struct tm* ptm; @@ -377,7 +377,7 @@ static void showLog(LogState *state, * in the time stamp. Don't use forward slashes, parenthesis, * brackets, asterisks, or other special chars here. */ -#if defined(HAVE_LOCALTIME_R) +#if !defined(_WIN32) ptm = localtime_r(&when, &tmBuf); #else ptm = localtime(&when); @@ -689,3 +689,9 @@ ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) /* Assume that open() was called first. */ return redirectWritev(fd, vector, count); } + +int __android_log_is_loggable(int prio, const char *tag __unused, int def) +{ + int logLevel = def; + return logLevel >= 0 && prio >= logLevel; +} diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c new file mode 100644 index 0000000..2e09192 --- /dev/null +++ b/liblog/log_is_loggable.c @@ -0,0 +1,61 @@ +/* +** Copyright 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 <string.h> +#include <sys/system_properties.h> + +#include <android/log.h> + +static int __android_log_level(const char *tag, int def) +{ + char buf[PROP_VALUE_MAX]; + + if (!tag || !*tag) { + return def; + } + { + static const char log_namespace[] = "persist.log.tag."; + char key[sizeof(log_namespace) + strlen(tag)]; + + strcpy(key, log_namespace); + strcpy(key + sizeof(log_namespace) - 1, tag); + + if (__system_property_get(key + 8, buf) <= 0) { + buf[0] = '\0'; + } + if (!buf[0] && __system_property_get(key, buf) <= 0) { + buf[0] = '\0'; + } + } + switch (toupper(buf[0])) { + case 'V': return ANDROID_LOG_VERBOSE; + case 'D': return ANDROID_LOG_DEBUG; + case 'I': return ANDROID_LOG_INFO; + case 'W': return ANDROID_LOG_WARN; + case 'E': return ANDROID_LOG_ERROR; + case 'F': /* FALLTHRU */ /* Not officially supported */ + case 'A': return ANDROID_LOG_FATAL; + case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ + } + return def; +} + +int __android_log_is_loggable(int prio, const char *tag, int def) +{ + int logLevel = __android_log_level(tag, def); + return logLevel >= 0 && prio >= logLevel; +} diff --git a/liblog/log_read.c b/liblog/log_read.c index ca5a1a7..5364e4f 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -19,6 +19,7 @@ #include <inttypes.h> #include <poll.h> #include <signal.h> +#include <stdbool.h> #include <stddef.h> #define NOMINMAX /* for windows to suppress definition of min in stdlib.h */ #include <stdlib.h> @@ -30,11 +31,17 @@ #include <cutils/sockets.h> #include <log/log.h> #include <log/logger.h> +#include <private/android_filesystem_config.h> +#include <private/android_logger.h> /* branchless on many architectures. */ #define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) +#if (defined(USE_MINGW) || defined(HAVE_WINSOCK)) +#define WEAK static +#else #define WEAK __attribute__((weak)) +#endif #ifndef __unused #define __unused __attribute__((unused)) #endif @@ -72,7 +79,7 @@ int WEAK socket_make_sockaddr_un(const char *name, int namespaceId, switch (namespaceId) { case ANDROID_SOCKET_NAMESPACE_ABSTRACT: -#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE +#if defined(__linux__) namelen = strlen(name); /* Test with length +1 for the *initial* '\0'. */ @@ -87,7 +94,7 @@ int WEAK socket_make_sockaddr_un(const char *name, int namespaceId, p_addr->sun_path[0] = 0; memcpy(p_addr->sun_path + 1, name, namelen); -#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ +#else /* this OS doesn't have the Linux abstract namespace */ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX); @@ -99,7 +106,7 @@ int WEAK socket_make_sockaddr_un(const char *name, int namespaceId, strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX); strcat(p_addr->sun_path, name); -#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ +#endif break; case ANDROID_SOCKET_NAMESPACE_RESERVED: @@ -353,10 +360,64 @@ static int check_log_success(char *buf, ssize_t ret) return 0; } +/* Determine the credentials of the caller */ +static bool uid_has_log_permission(uid_t uid) +{ + return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT); +} + +static uid_t get_best_effective_uid() +{ + uid_t euid; + uid_t uid; + gid_t gid; + ssize_t i; + static uid_t last_uid = (uid_t) -1; + + if (last_uid != (uid_t) -1) { + return last_uid; + } + uid = getuid(); + if (uid_has_log_permission(uid)) { + return last_uid = uid; + } + euid = geteuid(); + if (uid_has_log_permission(euid)) { + return last_uid = euid; + } + gid = getgid(); + if (uid_has_log_permission(gid)) { + return last_uid = gid; + } + gid = getegid(); + if (uid_has_log_permission(gid)) { + return last_uid = gid; + } + i = getgroups((size_t) 0, NULL); + if (i > 0) { + gid_t list[i]; + + getgroups(i, list); + while (--i >= 0) { + if (uid_has_log_permission(list[i])) { + return last_uid = list[i]; + } + } + } + return last_uid = uid; +} + int android_logger_clear(struct logger *logger) { char buf[512]; + if (logger->top->mode & ANDROID_LOG_PSTORE) { + if (uid_has_log_permission(get_best_effective_uid())) { + return unlink("/sys/fs/pstore/pmsg-ramoops-0"); + } + errno = EPERM; + return -1; + } return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf))); } @@ -560,6 +621,116 @@ struct logger_list *android_logger_list_open(log_id_t id, return logger_list; } +static int android_logger_list_read_pstore(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + ssize_t ret; + off_t current, next; + uid_t uid; + struct logger *logger; + struct __attribute__((__packed__)) { + android_pmsg_log_header_t p; + android_log_header_t l; + } buf; + static uint8_t preread_count; + + memset(log_msg, 0, sizeof(*log_msg)); + + if (logger_list->sock < 0) { + int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY); + + if (fd < 0) { + return -errno; + } + logger_list->sock = fd; + preread_count = 0; + } + + ret = 0; + while(1) { + if (preread_count < sizeof(buf)) { + ret = TEMP_FAILURE_RETRY(read(logger_list->sock, + &buf.p.magic + preread_count, + sizeof(buf) - preread_count)); + if (ret < 0) { + return -errno; + } + preread_count += ret; + } + if (preread_count != sizeof(buf)) { + return preread_count ? -EIO : -EAGAIN; + } + if ((buf.p.magic != LOGGER_MAGIC) + || (buf.p.len <= sizeof(buf)) + || (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) + || (buf.l.id >= LOG_ID_MAX) + || (buf.l.realtime.tv_nsec >= NS_PER_SEC)) { + do { + memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count); + } while (preread_count && (buf.p.magic != LOGGER_MAGIC)); + continue; + } + preread_count = 0; + + logger_for_each(logger, logger_list) { + if (buf.l.id != logger->id) { + continue; + } + + if ((logger_list->start.tv_sec || logger_list->start.tv_nsec) + && ((logger_list->start.tv_sec > buf.l.realtime.tv_sec) + || ((logger_list->start.tv_sec == buf.l.realtime.tv_sec) + && (logger_list->start.tv_nsec > buf.l.realtime.tv_nsec)))) { + break; + } + + if (logger_list->pid && (logger_list->pid != buf.p.pid)) { + break; + } + + uid = get_best_effective_uid(); + if (!uid_has_log_permission(uid) && (uid != buf.p.uid)) { + break; + } + + ret = TEMP_FAILURE_RETRY(read(logger_list->sock, + log_msg->entry_v3.msg, + buf.p.len - sizeof(buf))); + if (ret < 0) { + return -errno; + } + if (ret != (ssize_t)(buf.p.len - sizeof(buf))) { + return -EIO; + } + + log_msg->entry_v3.len = buf.p.len - sizeof(buf); + log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3); + log_msg->entry_v3.pid = buf.p.pid; + log_msg->entry_v3.tid = buf.l.tid; + log_msg->entry_v3.sec = buf.l.realtime.tv_sec; + log_msg->entry_v3.nsec = buf.l.realtime.tv_nsec; + log_msg->entry_v3.lid = buf.l.id; + + return ret; + } + + current = TEMP_FAILURE_RETRY(lseek(logger_list->sock, + (off_t)0, SEEK_CUR)); + if (current < 0) { + return -errno; + } + next = TEMP_FAILURE_RETRY(lseek(logger_list->sock, + (off_t)(buf.p.len - sizeof(buf)), + SEEK_CUR)); + if (next < 0) { + return -errno; + } + if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) { + return -EIO; + } + } +} + static void caught_signal(int signum __unused) { } @@ -578,7 +749,11 @@ int android_logger_list_read(struct logger_list *logger_list, return -EINVAL; } - if (logger_list->mode & O_NONBLOCK) { + if (logger_list->mode & ANDROID_LOG_PSTORE) { + return android_logger_list_read_pstore(logger_list, log_msg); + } + + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { memset(&ignore, 0, sizeof(ignore)); ignore.sa_handler = caught_signal; sigemptyset(&ignore.sa_mask); @@ -598,7 +773,7 @@ int android_logger_list_read(struct logger_list *logger_list, } strcpy(buffer, - (logger_list->mode & O_NONBLOCK) ? "dumpAndClose" : "stream"); + (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream"); cp = buffer + strlen(buffer); strcpy(cp, " lids"); @@ -636,14 +811,14 @@ int android_logger_list_read(struct logger_list *logger_list, cp += ret; } - if (logger_list->mode & O_NONBLOCK) { + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { /* Deal with an unresponsive logd */ sigaction(SIGALRM, &ignore, &old_sigaction); old_alarm = alarm(30); } ret = write(sock, buffer, cp - buffer); e = errno; - if (logger_list->mode & O_NONBLOCK) { + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { if (e == EINTR) { e = ETIMEDOUT; } @@ -669,7 +844,7 @@ int android_logger_list_read(struct logger_list *logger_list, while(1) { memset(log_msg, 0, sizeof(*log_msg)); - if (logger_list->mode & O_NONBLOCK) { + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { /* particularily useful if tombstone is reporting for logd */ sigaction(SIGALRM, &ignore, &old_sigaction); old_alarm = alarm(30); @@ -677,7 +852,7 @@ int android_logger_list_read(struct logger_list *logger_list, /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */ ret = recv(logger_list->sock, log_msg, LOGGER_ENTRY_MAX_LEN, 0); e = errno; - if (logger_list->mode & O_NONBLOCK) { + if (logger_list->mode & ANDROID_LOG_NONBLOCK) { if ((ret == 0) || (e == EINTR)) { e = EAGAIN; ret = -1; diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c index 41b8a51..bdc7b18 100644 --- a/liblog/log_read_kern.c +++ b/liblog/log_read_kern.c @@ -75,10 +75,10 @@ const char *android_log_id_to_name(log_id_t log_id) static int accessmode(int mode) { - if ((mode & O_ACCMODE) == O_WRONLY) { + if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_WRONLY) { return W_OK; } - if ((mode & O_ACCMODE) == O_RDWR) { + if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR) { return R_OK | W_OK; } return R_OK; @@ -117,7 +117,7 @@ log_id_t android_name_to_log_id(const char *logName) ++b; } - ret = check_allocate_accessible(&n, b, O_RDONLY); + ret = check_allocate_accessible(&n, b, ANDROID_LOG_RDONLY); free(n); if (ret) { return ret; @@ -201,8 +201,8 @@ static int logger_ioctl(struct logger *logger, int cmd, int mode) return -EFAULT; } - if (((mode & O_ACCMODE) == O_RDWR) - || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { + if (((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR) + || (((mode ^ logger->top->mode) & ANDROID_LOG_ACCMODE) == 0)) { return ioctl(logger->fd, cmd); } @@ -227,13 +227,13 @@ static int logger_ioctl(struct logger *logger, int cmd, int mode) int android_logger_clear(struct logger *logger) { - return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); + return logger_ioctl(logger, LOGGER_FLUSH_LOG, ANDROID_LOG_WRONLY); } /* returns the total size of the log's ring buffer */ long android_logger_get_log_size(struct logger *logger) { - return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); + return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, ANDROID_LOG_RDWR); } int android_logger_set_log_size(struct logger *logger __unused, @@ -248,7 +248,7 @@ int android_logger_set_log_size(struct logger *logger __unused, */ long android_logger_get_log_readable_size(struct logger *logger) { - return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); + return logger_ioctl(logger, LOGGER_GET_LOG_LEN, ANDROID_LOG_RDONLY); } /* @@ -256,7 +256,7 @@ long android_logger_get_log_readable_size(struct logger *logger) */ int android_logger_get_log_version(struct logger *logger) { - int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); + int ret = logger_ioctl(logger, LOGGER_GET_VERSION, ANDROID_LOG_RDWR); return (ret < 0) ? 1 : ret; } @@ -342,7 +342,7 @@ struct logger *android_logger_open(struct logger_list *logger_list, goto err_name; } - logger->fd = open(n, logger_list->mode); + logger->fd = open(n, logger_list->mode & (ANDROID_LOG_ACCMODE | ANDROID_LOG_NONBLOCK)); if (logger->fd < 0) { goto err_name; } @@ -565,7 +565,7 @@ int android_logger_list_read(struct logger_list *logger_list, if (result <= 0) { if (result) { error = errno; - } else if (logger_list->mode & O_NDELAY) { + } else if (logger_list->mode & ANDROID_LOG_NONBLOCK) { error = EAGAIN; } else { logger_list->timeout_ms = LOG_TIMEOUT_NEVER; diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp index 755c2d9..50742df 100644 --- a/liblog/log_time.cpp +++ b/liblog/log_time.cpp @@ -39,7 +39,7 @@ char *log_time::strptime(const char *s, const char *format) { #endif struct tm *ptm; -#if (defined(HAVE_LOCALTIME_R)) +#if !defined(_WIN32) struct tm tmBuf; ptm = localtime_r(&now, &tmBuf); #else @@ -78,7 +78,7 @@ char *log_time::strptime(const char *s, const char *format) { ++ret; } now = tv_sec; -#if (defined(HAVE_LOCALTIME_R)) +#if !defined(_WIN32) ptm = localtime_r(&now, &tmBuf); #else ptm = localtime(&now); @@ -150,6 +150,17 @@ log_time log_time::operator-= (const timespec &T) { return *this; } +log_time log_time::operator+= (const timespec &T) { + this->tv_nsec += (unsigned long int)T.tv_nsec; + if (this->tv_nsec >= NS_PER_SEC) { + this->tv_nsec -= NS_PER_SEC; + ++this->tv_sec; + } + 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) { @@ -166,3 +177,14 @@ log_time log_time::operator-= (const log_time &T) { return *this; } + +log_time log_time::operator+= (const log_time &T) { + this->tv_nsec += T.tv_nsec; + if (this->tv_nsec >= NS_PER_SEC) { + this->tv_nsec -= NS_PER_SEC; + ++this->tv_sec; + } + this->tv_sec += T.tv_sec; + + return *this; +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c index b2668ce..c62a246 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -13,12 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#if (FAKE_LOG_DEVICE == 0) +#include <endian.h> +#endif #include <errno.h> #include <fcntl.h> -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) #include <pthread.h> #endif #include <stdarg.h> +#include <stdatomic.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -39,6 +43,7 @@ #include <log/logger.h> #include <log/log_read.h> #include <private/android_filesystem_config.h> +#include <private/android_logger.h> #define LOG_BUF_SIZE 1024 @@ -49,7 +54,7 @@ static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #endif @@ -61,6 +66,7 @@ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 }; #else static int logd_fd = -1; +static int pstore_fd = -1; #endif /* @@ -71,6 +77,7 @@ static int logd_fd = -1; static enum { kLogUninitialized, kLogNotAvailable, kLogAvailable } g_log_status = kLogUninitialized; + int __android_log_dev_available(void) { if (g_log_status == kLogUninitialized) { @@ -83,15 +90,6 @@ int __android_log_dev_available(void) return (g_log_status == kLogAvailable); } -#if !FAKE_LOG_DEVICE -/* give up, resources too limited */ -static int __write_to_log_null(log_id_t log_fd __unused, struct iovec *vec __unused, - size_t nr __unused) -{ - return -1; -} -#endif - /* log_init_lock assumed */ static int __write_to_log_initialize() { @@ -104,40 +102,38 @@ static int __write_to_log_initialize() log_fds[i] = fakeLogOpen(buf, O_WRONLY); } #else - if (logd_fd >= 0) { - i = logd_fd; - logd_fd = -1; - close(i); + if (pstore_fd < 0) { + pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY)); } - i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (i < 0) { - ret = -errno; - write_to_log = __write_to_log_null; - } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) { - ret = -errno; - close(i); - i = -1; - write_to_log = __write_to_log_null; - } else { - struct sockaddr_un un; - memset(&un, 0, sizeof(struct sockaddr_un)); - un.sun_family = AF_UNIX; - strcpy(un.sun_path, "/dev/socket/logdw"); - - if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) { + if (logd_fd < 0) { + i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)); + if (i < 0) { + ret = -errno; + } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) { ret = -errno; close(i); - i = -1; + } else { + struct sockaddr_un un; + memset(&un, 0, sizeof(struct sockaddr_un)); + un.sun_family = AF_UNIX; + strcpy(un.sun_path, "/dev/socket/logdw"); + + if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un, + sizeof(struct sockaddr_un))) < 0) { + ret = -errno; + close(i); + } else { + logd_fd = i; + } } } - logd_fd = i; #endif return ret; } -static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) { ssize_t ret; #if FAKE_LOG_DEVICE @@ -155,37 +151,32 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) } } while (ret == -EINTR); #else - static const unsigned header_length = 3; + static const unsigned header_length = 2; struct iovec newVec[nr + header_length]; - typeof_log_id_t log_id_buf; - uint16_t tid; + android_log_header_t header; + android_pmsg_log_header_t pmsg_header; struct timespec ts; - log_time realtime_ts; size_t i, payload_size; static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */ + static pid_t last_pid = (pid_t) -1; + static atomic_int_fast32_t dropped; + + if (!nr) { + return -EINVAL; + } if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */ last_uid = getuid(); } - if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */ - /* - * ignore log messages we send to ourself (logd). - * Such log messages are often generated by libraries we depend on - * which use standard Android logging. - */ - return 0; - } - - if (logd_fd < 0) { - return -EBADF; + if (last_pid == (pid_t) -1) { + last_pid = getpid(); } - /* * struct { - * // what we provide - * typeof_log_id_t log_id; - * u16 tid; - * log_time realtime; + * // what we provide to pstore + * android_pmsg_log_header_t pmsg_header; + * // what we provide to socket + * android_log_header_t header; * // caller provides * union { * struct { @@ -201,18 +192,42 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) */ clock_gettime(CLOCK_REALTIME, &ts); - realtime_ts.tv_sec = ts.tv_sec; - realtime_ts.tv_nsec = ts.tv_nsec; - log_id_buf = log_id; - tid = gettid(); + pmsg_header.magic = LOGGER_MAGIC; + pmsg_header.len = sizeof(pmsg_header) + sizeof(header); + pmsg_header.uid = last_uid; + pmsg_header.pid = last_pid; + + header.tid = gettid(); + header.realtime.tv_sec = ts.tv_sec; + header.realtime.tv_nsec = ts.tv_nsec; + + newVec[0].iov_base = (unsigned char *) &pmsg_header; + newVec[0].iov_len = sizeof(pmsg_header); + newVec[1].iov_base = (unsigned char *) &header; + newVec[1].iov_len = sizeof(header); + + if (logd_fd > 0) { + int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed); + if (snapshot) { + android_log_event_int_t buffer; + + header.id = LOG_ID_EVENTS; + buffer.header.tag = htole32(LIBLOG_LOG_TAG); + buffer.payload.type = EVENT_TYPE_INT; + buffer.payload.data = htole32(snapshot); + + newVec[2].iov_base = &buffer; + newVec[2].iov_len = sizeof(buffer); + + ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2)); + if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { + atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed); + } + } + } - newVec[0].iov_base = (unsigned char *) &log_id_buf; - newVec[0].iov_len = sizeof_log_id_t; - newVec[1].iov_base = (unsigned char *) &tid; - newVec[1].iov_len = sizeof(tid); - newVec[2].iov_base = (unsigned char *) &realtime_ts; - newVec[2].iov_len = sizeof(log_time); + header.id = log_id; for (payload_size = 0, i = header_length; i < nr + header_length; i++) { newVec[i].iov_base = vec[i - header_length].iov_base; @@ -223,25 +238,48 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) if (newVec[i].iov_len) { ++i; } + payload_size = LOGGER_ENTRY_MAX_PAYLOAD; break; } } + pmsg_header.len += payload_size; + + if (pstore_fd >= 0) { + TEMP_FAILURE_RETRY(writev(pstore_fd, newVec, i)); + } + + if (last_uid == AID_LOGD) { /* logd, after initialization and priv drop */ + /* + * ignore log messages we send to ourself (logd). + * Such log messages are often generated by libraries we depend on + * which use standard Android logging. + */ + return 0; + } + + if (logd_fd < 0) { + return -EBADF; + } /* * The write below could be lost, but will never block. * + * To logd, we drop the pmsg_header + * * ENOTCONN occurs if logd dies. * EAGAIN occurs if logd is overloaded. */ - ret = writev(logd_fd, newVec, i); + ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1)); if (ret < 0) { ret = -errno; if (ret == -ENOTCONN) { -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) pthread_mutex_lock(&log_init_lock); #endif + close(logd_fd); + logd_fd = -1; ret = __write_to_log_initialize(); -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) pthread_mutex_unlock(&log_init_lock); #endif @@ -249,15 +287,17 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) return ret; } - ret = writev(logd_fd, newVec, nr + header_length); + ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1)); if (ret < 0) { ret = -errno; } } } - if (ret > (ssize_t)(sizeof_log_id_t + sizeof(tid) + sizeof(log_time))) { - ret -= sizeof_log_id_t + sizeof(tid) + sizeof(log_time); + if (ret > (ssize_t)sizeof(header)) { + ret -= sizeof(header); + } else if (ret == -EAGAIN) { + atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed); } #endif @@ -284,7 +324,7 @@ const char *android_log_id_to_name(log_id_t log_id) static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) pthread_mutex_lock(&log_init_lock); #endif @@ -293,16 +333,21 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) ret = __write_to_log_initialize(); if (ret < 0) { -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) pthread_mutex_unlock(&log_init_lock); #endif +#if (FAKE_LOG_DEVICE == 0) + if (pstore_fd >= 0) { + __write_to_log_daemon(log_id, vec, nr); + } +#endif return ret; } - write_to_log = __write_to_log_kernel; + write_to_log = __write_to_log_daemon; } -#ifdef HAVE_PTHREADS +#if !defined(_WIN32) pthread_mutex_unlock(&log_init_lock); #endif @@ -311,43 +356,7 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) int __android_log_write(int prio, const char *tag, const char *msg) { - struct iovec vec[3]; - log_id_t log_id = LOG_ID_MAIN; - char tmp_tag[32]; - - if (!tag) - tag = ""; - - /* XXX: This needs to go! */ - if (!strcmp(tag, "HTC_RIL") || - !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ - !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ - !strcmp(tag, "AT") || - !strcmp(tag, "GSM") || - !strcmp(tag, "STK") || - !strcmp(tag, "CDMA") || - !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) { - log_id = LOG_ID_RADIO; - /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ - snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); - tag = tmp_tag; - } - -#if __BIONIC__ - if (prio == ANDROID_LOG_FATAL) { - android_set_abort_message(msg); - } -#endif - - vec[0].iov_base = (unsigned char *) &prio; - vec[0].iov_len = 1; - vec[1].iov_base = (void *) tag; - vec[1].iov_len = strlen(tag) + 1; - vec[2].iov_base = (void *) msg; - vec[2].iov_len = strlen(msg) + 1; - - return write_to_log(log_id, vec, 3); + return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); } int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) @@ -375,6 +384,12 @@ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *ms tag = tmp_tag; } +#if __BIONIC__ + if (prio == ANDROID_LOG_FATAL) { + android_set_abort_message(msg); + } +#endif + vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; vec[1].iov_base = (void *) tag; @@ -440,7 +455,7 @@ void __android_log_assert(const char *cond, const char *tag, } __android_log_write(ANDROID_LOG_FATAL, tag, buf); - __builtin_trap(); /* trap so we have a chance to debug the situation */ + abort(); /* abort so we have a chance to debug the situation */ /* NOTREACHED */ } diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c index ae621cb..8742b34 100644 --- a/liblog/logd_write_kern.c +++ b/liblog/logd_write_kern.c @@ -16,9 +16,7 @@ #include <errno.h> #include <fcntl.h> -#ifdef HAVE_PTHREADS #include <pthread.h> -#endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -28,9 +26,7 @@ #include <time.h> #include <unistd.h> -#ifdef __BIONIC__ #include <android/set_abort_message.h> -#endif #include <log/log.h> #include <log/logd.h> @@ -43,23 +39,14 @@ #define LOG_BUF_SIZE 1024 -#if FAKE_LOG_DEVICE -/* This will be defined when building for the host. */ -#include "fake_log_device.h" -#define log_open(pathname, flags) fakeLogOpen(pathname, flags) -#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) -#define log_close(filedes) fakeLogClose(filedes) -#else #define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC) #define log_writev(filedes, vector, count) writev(filedes, vector, count) #define log_close(filedes) close(filedes) -#endif static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; -#ifdef HAVE_PTHREADS + static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; -#endif #ifndef __unused #define __unused __attribute__((__unused__)) @@ -119,9 +106,7 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { -#ifdef HAVE_PTHREADS pthread_mutex_lock(&log_init_lock); -#endif if (write_to_log == __write_to_log_init) { log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); @@ -147,52 +132,14 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) } } -#ifdef HAVE_PTHREADS pthread_mutex_unlock(&log_init_lock); -#endif return write_to_log(log_id, vec, nr); } int __android_log_write(int prio, const char *tag, const char *msg) { - struct iovec vec[3]; - log_id_t log_id = LOG_ID_MAIN; - char tmp_tag[32]; - - if (!tag) - tag = ""; - - /* XXX: This needs to go! */ - if (!strcmp(tag, "HTC_RIL") || - !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ - !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ - !strcmp(tag, "AT") || - !strcmp(tag, "GSM") || - !strcmp(tag, "STK") || - !strcmp(tag, "CDMA") || - !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) { - log_id = LOG_ID_RADIO; - /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ - snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); - tag = tmp_tag; - } - -#if __BIONIC__ - if (prio == ANDROID_LOG_FATAL) { - android_set_abort_message(msg); - } -#endif - - vec[0].iov_base = (unsigned char *) &prio; - vec[0].iov_len = 1; - vec[1].iov_base = (void *) tag; - vec[1].iov_len = strlen(tag) + 1; - vec[2].iov_base = (void *) msg; - vec[2].iov_len = strlen(msg) + 1; - - return write_to_log(log_id, vec, 3); + return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); } int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) @@ -220,6 +167,10 @@ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *ms tag = tmp_tag; } + if (prio == ANDROID_LOG_FATAL) { + android_set_abort_message(msg); + } + vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; vec[1].iov_base = (void *) tag; @@ -285,7 +236,7 @@ void __android_log_assert(const char *cond, const char *tag, } __android_log_write(ANDROID_LOG_FATAL, tag, buf); - __builtin_trap(); /* trap so we have a chance to debug the situation */ + abort(); /* abort so we have a chance to debug the situation */ /* NOTREACHED */ } diff --git a/liblog/logprint.c b/liblog/logprint.c index 08e830a..7ba4c8e 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -21,10 +21,12 @@ #include <assert.h> #include <ctype.h> #include <errno.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/param.h> #include <log/logd.h> #include <log/logprint.h> @@ -39,8 +41,23 @@ struct AndroidLogFormat_t { android_LogPriority global_pri; FilterInfo *filters; AndroidLogPrintFormat format; + bool colored_output; }; +/* + * gnome-terminal color tags + * See http://misc.flogisoft.com/bash/tip_colors_and_formatting + * for ideas on how to set the forground color of the text for xterm. + * The color manipulation character stream is defined as: + * ESC [ 3 8 ; 5 ; <color#> m + */ +#define ANDROID_COLOR_BLUE 75 +#define ANDROID_COLOR_DEFAULT 231 +#define ANDROID_COLOR_GREEN 40 +#define ANDROID_COLOR_ORANGE 166 +#define ANDROID_COLOR_RED 196 +#define ANDROID_COLOR_YELLOW 226 + static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) { FilterInfo *p_ret; @@ -110,6 +127,23 @@ static char filterPriToChar (android_LogPriority pri) } } +static int colorFromPri (android_LogPriority pri) +{ + switch (pri) { + case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT; + case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE; + case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN; + case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE; + case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED; + case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED; + case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT; + + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: return ANDROID_COLOR_DEFAULT; + } +} + static android_LogPriority filterPriForTag( AndroidLogFormat *p_format, const char *tag) { @@ -149,6 +183,7 @@ AndroidLogFormat *android_log_format_new() p_ret->global_pri = ANDROID_LOG_VERBOSE; p_ret->format = FORMAT_BRIEF; + p_ret->colored_output = false; return p_ret; } @@ -174,7 +209,10 @@ void android_log_format_free(AndroidLogFormat *p_format) void android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format) { - p_format->format=format; + if (format == FORMAT_COLOR) + p_format->colored_output = true; + else + p_format->format = format; } /** @@ -192,6 +230,7 @@ AndroidLogPrintFormat android_log_formatFromString(const char * formatString) else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; + else if (strcmp(formatString, "color") == 0) format = FORMAT_COLOR; else format = FORMAT_OFF; return format; @@ -305,15 +344,6 @@ error: return -1; } -static inline char * strip_end(char *str) -{ - char *end = str + strlen(str) - 1; - - while (end >= str && isspace(*end)) - *end-- = '\0'; - return str; -} - /** * Splits a wire-format buffer into an AndroidLogEntry * entry allocated by caller. Pointers will point directly into buf @@ -687,7 +717,7 @@ char *android_log_formatLogLine ( const AndroidLogEntry *entry, size_t *p_outLength) { -#if defined(HAVE_LOCALTIME_R) +#if !defined(_WIN32) struct tm tmBuf; #endif struct tm* ptm; @@ -698,6 +728,8 @@ char *android_log_formatLogLine ( char * ret = NULL; priChar = filterPriToChar(entry->priority); + size_t prefixLen = 0, suffixLen = 0; + size_t len; /* * Get the current date/time in pretty form @@ -708,7 +740,7 @@ char *android_log_formatLogLine ( * in the time stamp. Don't use forward slashes, parenthesis, * brackets, asterisks, or other special chars here. */ -#if defined(HAVE_LOCALTIME_R) +#if !defined(_WIN32) ptm = localtime_r(&(entry->tv_sec), &tmBuf); #else ptm = localtime(&(entry->tv_sec)); @@ -719,73 +751,80 @@ char *android_log_formatLogLine ( /* * Construct a buffer containing the log header and log message. */ - size_t prefixLen, suffixLen; + if (p_format->colored_output) { + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", + colorFromPri(entry->priority)); + prefixLen = MIN(prefixLen, sizeof(prefixBuf)); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m"); + suffixLen = MIN(suffixLen, sizeof(suffixBuf)); + } switch (p_format->format) { case FORMAT_TAG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8s: ", priChar, entry->tag); - strcpy(suffixBuf, "\n"); suffixLen = 1; + strcpy(suffixBuf + suffixLen, "\n"); + ++suffixLen; break; case FORMAT_PROCESS: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d) ", priChar, entry->pid); - suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, " (%s)\n", entry->tag); + suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen); + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, + "%c(%5d) ", priChar, entry->pid); break; case FORMAT_THREAD: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%5d:%5d) ", priChar, entry->pid, entry->tid); - strcpy(suffixBuf, "\n"); - suffixLen = 1; + strcpy(suffixBuf + suffixLen, "\n"); + ++suffixLen; break; case FORMAT_RAW: - prefixBuf[0] = 0; - prefixLen = 0; - strcpy(suffixBuf, "\n"); - suffixLen = 1; + prefixBuf[prefixLen] = 0; + len = 0; + strcpy(suffixBuf + suffixLen, "\n"); + ++suffixLen; break; case FORMAT_TIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, priChar, entry->tag, entry->pid); - strcpy(suffixBuf, "\n"); - suffixLen = 1; + strcpy(suffixBuf + suffixLen, "\n"); + ++suffixLen; break; case FORMAT_THREADTIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, entry->pid, entry->tid, priChar, entry->tag); - strcpy(suffixBuf, "\n"); - suffixLen = 1; + strcpy(suffixBuf + suffixLen, "\n"); + ++suffixLen; break; case FORMAT_LONG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "[ %s.%03ld %5d:%5d %c/%-8s ]\n", timeBuf, entry->tv_nsec / 1000000, entry->pid, entry->tid, priChar, entry->tag); - strcpy(suffixBuf, "\n\n"); - suffixLen = 2; + strcpy(suffixBuf + suffixLen, "\n\n"); + suffixLen += 2; prefixSuffixIsHeaderFooter = 1; break; case FORMAT_BRIEF: default: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); - strcpy(suffixBuf, "\n"); - suffixLen = 1; + strcpy(suffixBuf + suffixLen, "\n"); + ++suffixLen; break; } + /* snprintf has a weird return value. It returns what would have been * written given a large enough buffer. In the case that the prefix is * longer then our buffer(128), it messes up the calculations below * possibly causing heap corruption. To avoid this we double check and * set the length at the maximum (size minus null byte) */ - if(prefixLen >= sizeof(prefixBuf)) - prefixLen = sizeof(prefixBuf) - 1; - if(suffixLen >= sizeof(suffixBuf)) - suffixLen = sizeof(suffixBuf) - 1; + prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen); + suffixLen = MIN(suffixLen, sizeof(suffixBuf)); /* the following code is tragically unreadable */ diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk index cd1bf33..d75bbc9 100644 --- a/liblog/tests/Android.mk +++ b/liblog/tests/Android.mk @@ -39,16 +39,10 @@ benchmark_src_files := \ include $(CLEAR_VARS) LOCAL_MODULE := $(test_module_prefix)benchmarks LOCAL_MODULE_TAGS := $(test_tags) -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += $(benchmark_c_flags) LOCAL_SHARED_LIBRARIES += liblog libm LOCAL_SRC_FILES := $(benchmark_src_files) -ifndef LOCAL_SDK_VERSION -LOCAL_C_INCLUDES += bionic bionic/libstdc++/include external/stlport/stlport -LOCAL_SHARED_LIBRARIES += libstlport -endif -LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) -include $(BUILD_EXECUTABLE) +include $(BUILD_NATIVE_TEST) # ----------------------------------------------------------------------------- # Unit tests. @@ -71,7 +65,7 @@ ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp) test_src_files += \ libc_test.cpp -ifndef ($(TARGET_USES_LOGD),false) +ifneq ($(TARGET_USES_LOGD),false) test_c_flags += -DTARGET_USES_LOGD endif @@ -82,7 +76,6 @@ endif include $(CLEAR_VARS) LOCAL_MODULE := $(test_module_prefix)unit-tests LOCAL_MODULE_TAGS := $(test_tags) -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += $(test_c_flags) LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := $(test_src_files) diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp index 090394c..e5ef970 100644 --- a/liblog/tests/benchmark_main.cpp +++ b/liblog/tests/benchmark_main.cpp @@ -17,6 +17,7 @@ #include <benchmark.h> #include <inttypes.h> +#include <math.h> #include <regex.h> #include <stdio.h> #include <stdlib.h> diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp index 9839729..29501be 100644 --- a/liblog/tests/libc_test.cpp +++ b/liblog/tests/libc_test.cpp @@ -39,7 +39,7 @@ TEST(libc, __libc_android_log_event_int) { pid_t pid = getpid(); ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( - LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid))); struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -99,7 +99,7 @@ TEST(libc, __libc_fatal_no_abort) { pid_t pid = getpid(); ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( - (log_id_t)LOG_ID_CRASH, O_RDONLY | O_NDELAY, 1000, pid))); + (log_id_t)LOG_ID_CRASH, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid))); char b[80]; struct timespec ts; diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp index 549d79e..b594634 100644 --- a/liblog/tests/liblog_benchmark.cpp +++ b/liblog/tests/liblog_benchmark.cpp @@ -130,7 +130,7 @@ static void BM_log_latency(int iters) { pid_t pid = getpid(); struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, - O_RDONLY, 0, pid); + ANDROID_LOG_RDONLY, 0, pid); if (!logger_list) { fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); @@ -208,7 +208,7 @@ static void BM_log_delay(int iters) { pid_t pid = getpid(); struct logger_list * logger_list = android_logger_list_open(LOG_ID_EVENTS, - O_RDONLY, 0, pid); + ANDROID_LOG_RDONLY, 0, pid); if (!logger_list) { fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); @@ -266,3 +266,17 @@ static void BM_log_delay(int iters) { android_logger_list_free(logger_list); } BENCHMARK(BM_log_delay); + +/* + * Measure the time it takes for __android_log_is_loggable. + */ +static void BM_is_loggable(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + __android_log_is_loggable(ANDROID_LOG_WARN, "logd", ANDROID_LOG_VERBOSE); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_is_loggable); diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 393e2cd..33f6481 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -122,7 +122,7 @@ TEST(liblog, __android_log_btwrite__android_logger_list_read) { pid_t pid = getpid(); ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( - LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid))); log_time ts(CLOCK_MONOTONIC); @@ -223,7 +223,7 @@ TEST(liblog, android_logger_list_read__cpu) { v += pid & 0xFFFF; ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( - LOG_ID_EVENTS, O_RDONLY, 1000, pid))); + LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid))); int count = 0; @@ -443,7 +443,7 @@ TEST(liblog, max_payload) { struct logger_list *logger_list; ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( - LOG_ID_SYSTEM, O_RDONLY, 100, 0))); + LOG_ID_SYSTEM, ANDROID_LOG_RDONLY, 100, 0))); bool matches = false; ssize_t max_len = 0; @@ -505,7 +505,7 @@ TEST(liblog, too_big_payload) { struct logger_list *logger_list; ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( - LOG_ID_SYSTEM, O_RDONLY | O_NDELAY, 100, 0))); + LOG_ID_SYSTEM, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 100, 0))); ssize_t max_len = 0; @@ -552,12 +552,12 @@ TEST(liblog, dual_reader) { // >25 messages due to liblog.__android_log_buf_print__concurrentXX above. ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open( - LOG_ID_MAIN, O_RDONLY | O_NDELAY, 25, 0))); + LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 25, 0))); struct logger_list *logger_list2; if (NULL == (logger_list2 = android_logger_list_open( - LOG_ID_MAIN, O_RDONLY | O_NDELAY, 15, 0))) { + LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 15, 0))) { android_logger_list_close(logger_list1); ASSERT_TRUE(NULL != logger_list2); } @@ -595,7 +595,7 @@ TEST(liblog, dual_reader) { } TEST(liblog, android_logger_get_) { - struct logger_list * logger_list = android_logger_list_alloc(O_WRONLY, 0, 0); + struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0); for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { log_id_t id = static_cast<log_id_t>(i); diff --git a/liblog/uio.c b/liblog/uio.c index 24a6507..f77cc49 100644 --- a/liblog/uio.c +++ b/liblog/uio.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#ifndef HAVE_SYS_UIO_H +#if defined(_WIN32) #include <log/uio.h> #include <unistd.h> @@ -73,4 +73,4 @@ Exit: return total; } -#endif /* !HAVE_SYS_UIO_H */ +#endif |