From 154f4608aac6218af0e25c98b71d0803278c047e Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Thu, 20 Feb 2014 14:59:07 -0800 Subject: [PATCH] liblog: enable logging to logd. * Modify liblog to send all messages to the new syslog user space daemon. Original-Change-Id: I0ce439738cd921efb2db4c1d6a289a96bdbc8bc2 Original-Change-Id: If4eb0d09409f7e9be3eb4bb7017073dc7e931ab4 Signed-off-by: Nick Kralevich * Add a TARGET_USES_LOGD make flag for BoardConfig.mk to manage whether logd is enabled for use or not. * rename syslog to logd to avert confusion with bionic syslog * Add fake log support back in * prefilter for logging messages from logd * Fill in timestamps at logging source * update abstract log reader * switch from using suffix for id to v3 format * log a message when creating devices that a deprecated interface is being utilized. Signed-off-by: Mark Salyzyn (cherry pick from commit 099e2c1f6f706a8600c1cef74cce9066fc315480) Change-Id: I47929a5432977a1d7235267a435cec0a7d6bd440 --- include/log/logger.h | 8 +- init/devices.c | 12 +- liblog/Android.mk | 8 + liblog/log_read.c | 735 ++++++++++++++++++--------------------- liblog/log_read_kern.c | 695 ++++++++++++++++++++++++++++++++++++ liblog/logd_write.c | 147 +++++--- liblog/logd_write_kern.c | 304 ++++++++++++++++ logd/Android.mk | 3 + 8 files changed, 1452 insertions(+), 460 deletions(-) create mode 100644 liblog/log_read_kern.c create mode 100644 liblog/logd_write_kern.c diff --git a/include/log/logger.h b/include/log/logger.h index 6414d844b..bea002234 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -79,14 +79,10 @@ struct logger_entry_v3 { struct log_msg { union { unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry_v2 entry; + struct logger_entry_v3 entry; struct logger_entry_v3 entry_v3; struct logger_entry_v2 entry_v2; struct logger_entry entry_v1; - struct { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - log_id_t id; - } extra; } __attribute__((aligned(4))); #ifdef __cplusplus /* Matching log_time operators */ @@ -126,7 +122,7 @@ struct log_msg { /* packet methods */ log_id_t id() { - return extra.id; + return (log_id_t) entry.lid; } char *msg() { diff --git a/init/devices.c b/init/devices.c index a70c61dd0..80c6d750e 100644 --- a/init/devices.c +++ b/init/devices.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2007-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. @@ -47,6 +47,8 @@ #include "util.h" #include "log.h" +#define UNUSED __attribute__((__unused__)) + #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" @@ -193,7 +195,7 @@ static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) } static void make_device(const char *path, - const char *upath, + const char *upath UNUSED, int block, int major, int minor) { unsigned uid; @@ -589,6 +591,11 @@ static void mkdir_recursive_for_devpath(const char *devpath) mkdir_recursive(dir, 0755); } +static inline void __attribute__((__deprecated__)) kernel_logger() +{ + INFO("kernel logger is deprecated\n"); +} + static void handle_generic_device_event(struct uevent *uevent) { char *base; @@ -675,6 +682,7 @@ static void handle_generic_device_event(struct uevent *uevent) make_dir(base, 0755); } else if(!strncmp(uevent->subsystem, "misc", 4) && !strncmp(name, "log_", 4)) { + kernel_logger(); base = "/dev/log/"; make_dir(base, 0755); name += 4; diff --git a/liblog/Android.mk b/liblog/Android.mk index 0d6c970bf..174ca55ab 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -16,7 +16,11 @@ LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) +ifeq ($(TARGET_USES_LOGD),true) liblog_sources := logd_write.c +else +liblog_sources := logd_write_kern.c +endif # some files must not be compiled when building against Mingw # they correspond to features not used by our host development tools @@ -42,7 +46,11 @@ else endif liblog_host_sources := $(liblog_sources) fake_log_device.c +ifeq ($(TARGET_USES_LOGD),true) liblog_target_sources = $(liblog_sources) log_read.c +else +liblog_target_sources = $(liblog_sources) log_read_kern.c +endif # Shared and static library for host # ======================================================== diff --git a/liblog/log_read.c b/liblog/log_read.c index 47aa711d0..889442db9 100644 --- a/liblog/log_read.c +++ b/liblog/log_read.c @@ -14,37 +14,176 @@ ** limitations under the License. */ -#define _GNU_SOURCE /* asprintf for x86 host */ #include #include -#include -#include -#include +#include +#include +#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */ #include +#include +#include + #include +#include #include #include -#include +/* branchless on many architectures. */ +#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) -#define __LOGGERIO 0xAE +#define WEAK __attribute__((weak)) +#define UNUSED __attribute__((unused)) -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ -#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ -#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ +/* Private copy of ../libcutils/socket_local_client.c prevent library loops */ -typedef char bool; -#define false (const bool)0 -#define true (const bool)1 +#ifdef HAVE_WINSOCK -#define LOG_FILE_DIR "/dev/log/" +int WEAK socket_local_client(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -ENOSYS; +} -/* timeout in milliseconds */ -#define LOG_TIMEOUT_FLUSH 5 -#define LOG_TIMEOUT_NEVER -1 +#else /* !HAVE_WINSOCK */ + +#include +#include +#include +#include + +/* Private copy of ../libcutils/socket_local.h prevent library loops */ +#define FILESYSTEM_SOCKET_PREFIX "/tmp/" +#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/" +/* End of ../libcutils/socket_local.h */ + +#define LISTEN_BACKLOG 4 + +/* Documented in header file. */ +int WEAK socket_make_sockaddr_un(const char *name, int namespaceId, + struct sockaddr_un *p_addr, socklen_t *alen) +{ + memset (p_addr, 0, sizeof (*p_addr)); + size_t namelen; + + switch (namespaceId) { + case ANDROID_SOCKET_NAMESPACE_ABSTRACT: +#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE + namelen = strlen(name); + + /* Test with length +1 for the *initial* '\0'. */ + if ((namelen + 1) > sizeof(p_addr->sun_path)) { + goto error; + } + + /* + * Note: The path in this case is *not* supposed to be + * '\0'-terminated. ("man 7 unix" for the gory details.) + */ + + p_addr->sun_path[0] = 0; + memcpy(p_addr->sun_path + 1, name, namelen); +#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + /* this OS doesn't have the Linux abstract namespace */ + + namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX); + strcat(p_addr->sun_path, name); +#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + break; + + case ANDROID_SOCKET_NAMESPACE_RESERVED: + namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX); + strcat(p_addr->sun_path, name); + break; + + case ANDROID_SOCKET_NAMESPACE_FILESYSTEM: + namelen = strlen(name); + /* unix_path_max appears to be missing on linux */ + if (namelen > sizeof(*p_addr) + - offsetof(struct sockaddr_un, sun_path) - 1) { + goto error; + } + + strcpy(p_addr->sun_path, name); + break; + + default: + /* invalid namespace id */ + return -1; + } + + p_addr->sun_family = AF_LOCAL; + *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; + return 0; +error: + return -1; +} + +/** + * connect to peer named "name" on fd + * returns same fd or -1 on error. + * fd is not closed on error. that's your job. + * + * Used by AndroidSocketImpl + */ +int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId, + int type UNUSED) +{ + struct sockaddr_un addr; + socklen_t alen; + size_t namelen; + int err; + + err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen); + + if (err < 0) { + goto error; + } + + if(connect(fd, (struct sockaddr *) &addr, alen) < 0) { + goto error; + } + + return fd; + +error: + return -1; +} + +/** + * connect to peer named "name" + * returns fd or -1 on error + */ +int WEAK socket_local_client(const char *name, int namespaceId, int type) +{ + int s; + + s = socket(AF_LOCAL, type, 0); + if(s < 0) return -1; + + if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) { + close(s); + return -1; + } + + return s; +} + +#endif /* !HAVE_WINSOCK */ +/* End of ../libcutils/socket_local_client.c */ #define logger_for_each(logger, logger_list) \ for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ @@ -59,44 +198,17 @@ static const char *LOG_NAME[LOG_ID_MAX] = { [LOG_ID_SYSTEM] = "system" }; -const char *android_log_id_to_name(log_id_t log_id) { +const char *android_log_id_to_name(log_id_t log_id) +{ if (log_id >= LOG_ID_MAX) { log_id = LOG_ID_MAIN; } return LOG_NAME[log_id]; } -static int accessmode(int mode) +log_id_t android_name_to_log_id(const char *logName) { - if ((mode & O_ACCMODE) == O_WRONLY) { - return W_OK; - } - if ((mode & O_ACCMODE) == O_RDWR) { - return R_OK | W_OK; - } - return R_OK; -} - -/* repeated fragment */ -static int check_allocate_accessible(char **n, const char *b, int mode) -{ - *n = NULL; - - if (!b) { - return -EINVAL; - } - - asprintf(n, LOG_FILE_DIR "%s", b); - if (!*n) { - return -1; - } - - return access(*n, accessmode(mode)); -} - -log_id_t android_name_to_log_id(const char *logName) { const char *b; - char *n; int ret; if (!logName) { @@ -109,12 +221,6 @@ log_id_t android_name_to_log_id(const char *logName) { ++b; } - ret = check_allocate_accessible(&n, b, O_RDONLY); - free(n); - if (ret) { - return ret; - } - for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { const char *l = LOG_NAME[ret]; if (l && !strcmp(b, l)) { @@ -129,26 +235,13 @@ struct logger_list { int mode; unsigned int tail; pid_t pid; - unsigned int queued_lines; - int timeout_ms; - int error; - bool flush; - bool valid_entry; /* valiant(?) effort to deal with memory starvation */ - struct log_msg entry; -}; - -struct log_list { - struct listnode node; - struct log_msg entry; /* Truncated to event->len() + 1 to save space */ + int sock; }; struct logger { struct listnode node; struct logger_list *top; - int fd; log_id_t id; - short *revents; - struct listnode log_list; }; /* android_logger_alloc unimplemented, no use case */ @@ -159,73 +252,81 @@ static void android_logger_free(struct logger *logger) return; } - while (!list_empty(&logger->log_list)) { - struct log_list *entry = node_to_item( - list_head(&logger->log_list), struct log_list, node); - list_remove(&entry->node); - free(entry); - if (logger->top->queued_lines) { - logger->top->queued_lines--; - } - } - - if (logger->fd >= 0) { - close(logger->fd); - } - list_remove(&logger->node); free(logger); } +/* android_logger_alloc unimplemented, no use case */ + +/* method for getting the associated sublog id */ log_id_t android_logger_get_id(struct logger *logger) { return logger->id; } /* worker for sending the command to the logger */ -static int logger_ioctl(struct logger *logger, int cmd, int mode) +static ssize_t send_log_msg(struct logger *logger, + const char *msg, char *buf, size_t buf_size) { - char *n; - int f, ret; - - if (!logger || !logger->top) { - return -EFAULT; + ssize_t ret; + int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM); + if (sock < 0) { + ret = sock; + goto done; } - if (((mode & O_ACCMODE) == O_RDWR) - || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { - return ioctl(logger->fd, cmd); + if (msg) { + snprintf(buf, buf_size, msg, logger ? logger->id : (unsigned) -1); } - /* We go here if android_logger_list_open got mode wrong for this ioctl */ - ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); - if (ret) { - free(n); - return ret; + ret = write(sock, buf, strlen(buf) + 1); + if (ret <= 0) { + goto done; } - f = open(n, mode); - free(n); - if (f < 0) { - return f; + ret = read(sock, buf, buf_size); + +done: + if ((ret == -1) && errno) { + ret = -errno; } - - ret = ioctl(f, cmd); - close (f); - + close(sock); return ret; } int android_logger_clear(struct logger *logger) { - return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); + 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)) { + return -1; + } + + return 0; } /* returns the total size of the log's ring buffer */ int android_logger_get_log_size(struct logger *logger) { - return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); + char buf[512]; + + ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if ((buf[0] < '0') || ('9' < buf[0])) { + return -1; + } + + return atoi(buf); } /* @@ -234,16 +335,26 @@ int android_logger_get_log_size(struct logger *logger) */ int android_logger_get_log_readable_size(struct logger *logger) { - return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); + char buf[512]; + + ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + if ((buf[0] < '0') || ('9' < buf[0])) { + return -1; + } + + return atoi(buf); } /* * returns the logger version */ -int android_logger_get_log_version(struct logger *logger) +int android_logger_get_log_version(struct logger *logger UNUSED) { - int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); - return (ret < 0) ? 1 : ret; + return 3; } struct logger_list *android_logger_list_alloc(int mode, @@ -256,10 +367,13 @@ struct logger_list *android_logger_list_alloc(int mode, if (!logger_list) { return NULL; } + list_init(&logger_list->node); logger_list->mode = mode; logger_list->tail = tail; logger_list->pid = pid; + logger_list->sock = -1; + return logger_list; } @@ -289,28 +403,11 @@ struct logger *android_logger_open(struct logger_list *logger_list, goto err; } - if (check_allocate_accessible(&n, android_log_id_to_name(id), - logger_list->mode)) { - goto err_name; - } - - logger->fd = open(n, logger_list->mode); - if (logger->fd < 0) { - goto err_name; - } - - free(n); logger->id = id; - list_init(&logger->log_list); list_add_tail(&logger_list->node, &logger->node); logger->top = logger_list; - logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; goto ok; -err_name: - free(n); -err_logger: - free(logger); err: logger = NULL; ok: @@ -336,315 +433,137 @@ struct logger_list *android_logger_list_open(log_id_t id, return logger_list; } -/* prevent memory starvation when backfilling */ -static unsigned int queue_threshold(struct logger_list *logger_list) +static void caught_signal(int signum UNUSED) { - return (logger_list->tail < 64) ? 64 : logger_list->tail; -} - -static bool low_queue(struct listnode *node) -{ - /* low is considered less than 2 */ - return list_head(node) == list_tail(node); -} - -/* Flush queues in sequential order, one at a time */ -static int android_logger_list_flush(struct logger_list *logger_list, - struct log_msg *log_msg) -{ - int ret = 0; - struct log_list *firstentry = NULL; - - while ((ret == 0) - && (logger_list->flush - || (logger_list->queued_lines > logger_list->tail))) { - struct logger *logger; - - /* Merge sort */ - bool at_least_one_is_low = false; - struct logger *firstlogger = NULL; - firstentry = NULL; - - logger_for_each(logger, logger_list) { - struct listnode *node; - struct log_list *oldest = NULL; - - /* kernel logger channels not necessarily time-sort order */ - list_for_each(node, &logger->log_list) { - struct log_list *entry = node_to_item(node, - struct log_list, node); - if (!oldest - || (entry->entry.entry.sec < oldest->entry.entry.sec) - || ((entry->entry.entry.sec == oldest->entry.entry.sec) - && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { - oldest = entry; - } - } - - if (!oldest) { - at_least_one_is_low = true; - continue; - } else if (low_queue(&logger->log_list)) { - at_least_one_is_low = true; - } - - if (!firstentry - || (oldest->entry.entry.sec < firstentry->entry.entry.sec) - || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) - && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { - firstentry = oldest; - firstlogger = logger; - } - } - - if (!firstentry) { - break; - } - - /* when trimming list, tries to keep one entry behind in each bucket */ - if (!logger_list->flush - && at_least_one_is_low - && (logger_list->queued_lines < queue_threshold(logger_list))) { - break; - } - - /* within tail?, send! */ - if ((logger_list->tail == 0) - || (logger_list->queued_lines <= logger_list->tail)) { - ret = firstentry->entry.entry.hdr_size; - if (!ret) { - ret = sizeof(firstentry->entry.entry_v1); - } - ret += firstentry->entry.entry.len; - - memcpy(log_msg->buf, firstentry->entry.buf, ret + 1); - log_msg->extra.id = firstlogger->id; - } - - /* next entry */ - list_remove(&firstentry->node); - free(firstentry); - if (logger_list->queued_lines) { - logger_list->queued_lines--; - } - } - - /* Flushed the list, no longer in tail mode for continuing content */ - if (logger_list->flush && !firstentry) { - logger_list->tail = 0; - } - return ret; } /* Read from the selected logs */ int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg) { + int ret, e; struct logger *logger; - nfds_t nfds; - struct pollfd *p, *pollfds = NULL; - int error = 0, ret = 0; - - memset(log_msg, 0, sizeof(struct log_msg)); + struct sigaction ignore; + struct sigaction old_sigaction; + unsigned int old_alarm = 0; if (!logger_list) { - return -ENODEV; + return -EINVAL; } - if (!(accessmode(logger_list->mode) & R_OK)) { - logger_list->error = EPERM; - goto done; + if (logger_list->mode & O_NONBLOCK) { + memset(&ignore, 0, sizeof(ignore)); + ignore.sa_handler = caught_signal; + sigemptyset(&ignore.sa_mask); } - nfds = 0; - logger_for_each(logger, logger_list) { - ++nfds; - } - if (nfds <= 0) { - error = ENODEV; - goto done; - } + if (logger_list->sock < 0) { + char buffer[256], *cp, c; - /* Do we have anything to offer from the buffer or state? */ - if (logger_list->valid_entry) { /* implies we are also in a flush state */ - goto flush; - } - - ret = android_logger_list_flush(logger_list, log_msg); - if (ret) { - goto done; - } - - if (logger_list->error) { /* implies we are also in a flush state */ - goto done; - } - - /* Lets start grinding on metal */ - pollfds = calloc(nfds, sizeof(struct pollfd)); - if (!pollfds) { - error = ENOMEM; - goto flush; - } - - p = pollfds; - logger_for_each(logger, logger_list) { - p->fd = logger->fd; - p->events = POLLIN; - logger->revents = &p->revents; - ++p; - } - - while (!ret && !error) { - int result; - - /* If we oversleep it's ok, i.e. ignore EINTR. */ - result = TEMP_FAILURE_RETRY( - poll(pollfds, nfds, logger_list->timeout_ms)); - - if (result <= 0) { - if (result) { - error = errno; - } else if (logger_list->mode & O_NDELAY) { - error = EAGAIN; - } else { - logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + int sock = socket_local_client("logdr", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_SEQPACKET); + if (sock < 0) { + if ((sock == -1) && errno) { + return -errno; } - - logger_list->flush = true; - goto try_flush; + return sock; } - logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; - - /* Anti starvation */ - if (!logger_list->flush - && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { - /* Any queues with input pending that is low? */ - bool starving = false; - logger_for_each(logger, logger_list) { - if ((*(logger->revents) & POLLIN) - && low_queue(&logger->log_list)) { - starving = true; - break; - } - } - - /* pushback on any queues that are not low */ - if (starving) { - logger_for_each(logger, logger_list) { - if ((*(logger->revents) & POLLIN) - && !low_queue(&logger->log_list)) { - *(logger->revents) &= ~POLLIN; - } - } - } - } + strcpy(buffer, + (logger_list->mode & O_NONBLOCK) ? "dumpAndClose" : "stream"); + cp = buffer + strlen(buffer); + strcpy(cp, " lids"); + cp += 5; + c = '='; + int remaining = sizeof(buffer) - (cp - buffer); logger_for_each(logger, logger_list) { - unsigned int hdr_size; - struct log_list *entry; + ret = snprintf(cp, remaining, "%c%u", c, logger->id); + ret = min(ret, remaining); + remaining -= ret; + cp += ret; + c = ','; + } - if (!(*(logger->revents) & POLLIN)) { - continue; + if (logger_list->tail) { + ret = snprintf(cp, remaining, " tail=%u", logger_list->tail); + 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); + remaining -= ret; + cp += ret; + } + + if (logger_list->mode & O_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 (e == EINTR) { + e = ETIMEDOUT; } - - memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); - /* NOTE: driver guarantees we read exactly one full entry */ - result = read(logger->fd, logger_list->entry.buf, - LOGGER_ENTRY_MAX_LEN); - if (result <= 0) { - if (!result) { - error = EIO; - } else if (errno != EINTR) { - error = errno; - } - continue; - } - - if (logger_list->pid - && (logger_list->pid != logger_list->entry.entry.pid)) { - continue; - } - - hdr_size = logger_list->entry.entry.hdr_size; - if (!hdr_size) { - hdr_size = sizeof(logger_list->entry.entry_v1); - } - - if ((hdr_size > sizeof(struct log_msg)) - || (logger_list->entry.entry.len - > sizeof(logger_list->entry.buf) - hdr_size) - || (logger_list->entry.entry.len != result - hdr_size)) { - error = EINVAL; - continue; - } - - logger_list->entry.extra.id = logger->id; - - /* speedup: If not tail, and only one list, send directly */ - if (!logger_list->tail - && (list_head(&logger_list->node) - == list_tail(&logger_list->node))) { - ret = result; - memcpy(log_msg->buf, logger_list->entry.buf, result + 1); - log_msg->extra.id = logger->id; - break; - } - - entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); - - if (!entry) { - logger_list->valid_entry = true; - error = ENOMEM; - break; - } - - logger_list->queued_lines++; - - memcpy(entry->entry.buf, logger_list->entry.buf, result); - entry->entry.buf[result] = '\0'; - list_add_tail(&logger->log_list, &entry->node); + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); } if (ret <= 0) { -try_flush: - ret = android_logger_list_flush(logger_list, log_msg); - } - } - - free(pollfds); - -flush: - if (error) { - logger_list->flush = true; - } - - if (ret <= 0) { - ret = android_logger_list_flush(logger_list, log_msg); - - if (!ret && logger_list->valid_entry) { - ret = logger_list->entry.entry.hdr_size; - if (!ret) { - ret = sizeof(logger_list->entry.entry_v1); + close(sock); + if ((ret == -1) && e) { + return -e; } - ret += logger_list->entry.entry.len; - - memcpy(log_msg->buf, logger_list->entry.buf, - sizeof(struct log_msg)); - logger_list->valid_entry = false; + if (ret == 0) { + return -EIO; + } + return ret; } + + logger_list->sock = sock; } -done: - if (logger_list->error) { - error = logger_list->error; - } - if (error) { - logger_list->error = error; - if (!ret) { - ret = -error; + ret = 0; + while(1) { + memset(log_msg, 0, sizeof(*log_msg)); + + if (logger_list->mode & O_NONBLOCK) { + /* particularily useful if tombstone is reporting for logd */ + sigaction(SIGALRM, &ignore, &old_sigaction); + old_alarm = alarm(30); + } + /* 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 ((ret == 0) || (e == EINTR)) { + e = EAGAIN; + ret = -1; + } + alarm(old_alarm); + sigaction(SIGALRM, &old_sigaction, NULL); + } + + if (ret <= 0) { + if ((ret == -1) && e) { + return -e; + } + return ret; + } + + logger_for_each(logger, logger_list) { + if (log_msg->entry.lid == logger->id) { + return ret; + } } } + /* NOTREACH */ return ret; } @@ -661,5 +580,9 @@ void android_logger_list_free(struct logger_list *logger_list) android_logger_free(logger); } + if (logger_list->sock >= 0) { + close (logger_list->sock); + } + free(logger_list); } diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c new file mode 100644 index 000000000..b4547295f --- /dev/null +++ b/liblog/log_read_kern.c @@ -0,0 +1,695 @@ +/* +** Copyright 2013-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. +*/ + +#define _GNU_SOURCE /* asprintf for x86 host */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + +typedef char bool; +#define false (const bool)0 +#define true (const bool)1 + +#define LOG_FILE_DIR "/dev/log/" + +/* timeout in milliseconds */ +#define LOG_TIMEOUT_FLUSH 5 +#define LOG_TIMEOUT_NEVER -1 + +#define logger_for_each(logger, logger_list) \ + for (logger = node_to_item((logger_list)->node.next, struct logger, node); \ + logger != node_to_item(&(logger_list)->node, struct logger, node); \ + logger = node_to_item((logger)->node.next, struct logger, node)) + +/* In the future, we would like to make this list extensible */ +static const char *LOG_NAME[LOG_ID_MAX] = { + [LOG_ID_MAIN] = "main", + [LOG_ID_RADIO] = "radio", + [LOG_ID_EVENTS] = "events", + [LOG_ID_SYSTEM] = "system" +}; + +const char *android_log_id_to_name(log_id_t log_id) +{ + if (log_id >= LOG_ID_MAX) { + log_id = LOG_ID_MAIN; + } + return LOG_NAME[log_id]; +} + +static int accessmode(int mode) +{ + if ((mode & O_ACCMODE) == O_WRONLY) { + return W_OK; + } + if ((mode & O_ACCMODE) == O_RDWR) { + return R_OK | W_OK; + } + return R_OK; +} + +/* repeated fragment */ +static int check_allocate_accessible(char **n, const char *b, int mode) +{ + *n = NULL; + + if (!b) { + return -EINVAL; + } + + asprintf(n, LOG_FILE_DIR "%s", b); + if (!*n) { + return -1; + } + + return access(*n, accessmode(mode)); +} + +log_id_t android_name_to_log_id(const char *logName) +{ + const char *b; + char *n; + int ret; + + if (!logName) { + return -1; /* NB: log_id_t is unsigned */ + } + b = strrchr(logName, '/'); + if (!b) { + b = logName; + } else { + ++b; + } + + ret = check_allocate_accessible(&n, b, O_RDONLY); + free(n); + if (ret) { + return ret; + } + + for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) { + const char *l = LOG_NAME[ret]; + if (l && !strcmp(b, l)) { + return ret; + } + } + return -1; /* should never happen */ +} + +struct logger_list { + struct listnode node; + int mode; + unsigned int tail; + pid_t pid; + unsigned int queued_lines; + int timeout_ms; + int error; + bool flush; + bool valid_entry; /* valiant(?) effort to deal with memory starvation */ + struct log_msg entry; +}; + +struct log_list { + struct listnode node; + struct log_msg entry; /* Truncated to event->len() + 1 to save space */ +}; + +struct logger { + struct listnode node; + struct logger_list *top; + int fd; + log_id_t id; + short *revents; + struct listnode log_list; +}; + +/* android_logger_alloc unimplemented, no use case */ +/* android_logger_free not exported */ +static void android_logger_free(struct logger *logger) +{ + if (!logger) { + return; + } + + while (!list_empty(&logger->log_list)) { + struct log_list *entry = node_to_item( + list_head(&logger->log_list), struct log_list, node); + list_remove(&entry->node); + free(entry); + if (logger->top->queued_lines) { + logger->top->queued_lines--; + } + } + + if (logger->fd >= 0) { + close(logger->fd); + } + + list_remove(&logger->node); + + free(logger); +} + +log_id_t android_logger_get_id(struct logger *logger) +{ + return logger->id; +} + +/* worker for sending the command to the logger */ +static int logger_ioctl(struct logger *logger, int cmd, int mode) +{ + char *n; + int f, ret; + + if (!logger || !logger->top) { + return -EFAULT; + } + + if (((mode & O_ACCMODE) == O_RDWR) + || (((mode ^ logger->top->mode) & O_ACCMODE) == 0)) { + return ioctl(logger->fd, cmd); + } + + /* We go here if android_logger_list_open got mode wrong for this ioctl */ + ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode); + if (ret) { + free(n); + return ret; + } + + f = open(n, mode); + free(n); + if (f < 0) { + return f; + } + + ret = ioctl(f, cmd); + close (f); + + return ret; +} + +int android_logger_clear(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_FLUSH_LOG, O_WRONLY); +} + +/* returns the total size of the log's ring buffer */ +int android_logger_get_log_size(struct logger *logger) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, O_RDWR); +} + +/* + * 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) +{ + return logger_ioctl(logger, LOGGER_GET_LOG_LEN, O_RDONLY); +} + +/* + * returns the logger version + */ +int android_logger_get_log_version(struct logger *logger) +{ + int ret = logger_ioctl(logger, LOGGER_GET_VERSION, O_RDWR); + return (ret < 0) ? 1 : ret; +} + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + 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->tail = tail; + logger_list->pid = pid; + return logger_list; +} + +/* android_logger_list_register unimplemented, no use case */ +/* android_logger_list_unregister unimplemented, no use case */ + +/* Open the named log and add it to the logger list */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) +{ + struct listnode *node; + struct logger *logger; + char *n; + + if (!logger_list || (id >= LOG_ID_MAX)) { + goto err; + } + + logger_for_each(logger, logger_list) { + if (logger->id == id) { + goto ok; + } + } + + logger = calloc(1, sizeof(*logger)); + if (!logger) { + goto err; + } + + if (check_allocate_accessible(&n, android_log_id_to_name(id), + logger_list->mode)) { + goto err_name; + } + + logger->fd = open(n, logger_list->mode); + if (logger->fd < 0) { + goto err_name; + } + + free(n); + logger->id = id; + list_init(&logger->log_list); + list_add_tail(&logger_list->node, &logger->node); + logger->top = logger_list; + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + goto ok; + +err_name: + free(n); +err_logger: + free(logger); +err: + logger = NULL; +ok: + return logger; +} + +/* Open the single named log and make it part of a new logger list */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid) +{ + struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid); + if (!logger_list) { + return NULL; + } + + if (!android_logger_open(logger_list, id)) { + android_logger_list_free(logger_list); + return NULL; + } + + return logger_list; +} + +/* prevent memory starvation when backfilling */ +static unsigned int queue_threshold(struct logger_list *logger_list) +{ + return (logger_list->tail < 64) ? 64 : logger_list->tail; +} + +static bool low_queue(struct listnode *node) +{ + /* low is considered less than 2 */ + return list_head(node) == list_tail(node); +} + +/* Flush queues in sequential order, one at a time */ +static int android_logger_list_flush(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + int ret = 0; + struct log_list *firstentry = NULL; + + while ((ret == 0) + && (logger_list->flush + || (logger_list->queued_lines > logger_list->tail))) { + struct logger *logger; + + /* Merge sort */ + bool at_least_one_is_low = false; + struct logger *firstlogger = NULL; + firstentry = NULL; + + logger_for_each(logger, logger_list) { + struct listnode *node; + struct log_list *oldest = NULL; + + /* kernel logger channels not necessarily time-sort order */ + list_for_each(node, &logger->log_list) { + struct log_list *entry = node_to_item(node, + struct log_list, node); + if (!oldest + || (entry->entry.entry.sec < oldest->entry.entry.sec) + || ((entry->entry.entry.sec == oldest->entry.entry.sec) + && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) { + oldest = entry; + } + } + + if (!oldest) { + at_least_one_is_low = true; + continue; + } else if (low_queue(&logger->log_list)) { + at_least_one_is_low = true; + } + + if (!firstentry + || (oldest->entry.entry.sec < firstentry->entry.entry.sec) + || ((oldest->entry.entry.sec == firstentry->entry.entry.sec) + && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) { + firstentry = oldest; + firstlogger = logger; + } + } + + if (!firstentry) { + break; + } + + /* when trimming list, tries to keep one entry behind in each bucket */ + if (!logger_list->flush + && at_least_one_is_low + && (logger_list->queued_lines < queue_threshold(logger_list))) { + break; + } + + /* within tail?, send! */ + if ((logger_list->tail == 0) + || (logger_list->queued_lines <= logger_list->tail)) { + int diff; + ret = firstentry->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(firstentry->entry.entry_v1); + } + + /* Promote entry to v3 format */ + memcpy(log_msg->buf, firstentry->entry.buf, ret); + diff = sizeof(firstentry->entry.entry_v3) - ret; + if (diff < 0) { + diff = 0; + } else if (diff > 0) { + memset(log_msg->buf + ret, 0, diff); + } + memcpy(log_msg->buf + ret + diff, firstentry->entry.buf + ret, + firstentry->entry.entry.len + 1); + ret += diff; + log_msg->entry.hdr_size = ret; + log_msg->entry.lid = firstlogger->id; + + ret += firstentry->entry.entry.len; + } + + /* next entry */ + list_remove(&firstentry->node); + free(firstentry); + if (logger_list->queued_lines) { + logger_list->queued_lines--; + } + } + + /* Flushed the list, no longer in tail mode for continuing content */ + if (logger_list->flush && !firstentry) { + logger_list->tail = 0; + } + return ret; +} + +/* Read from the selected logs */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg) +{ + struct logger *logger; + nfds_t nfds; + struct pollfd *p, *pollfds = NULL; + int error = 0, ret = 0; + + memset(log_msg, 0, sizeof(struct log_msg)); + + if (!logger_list) { + return -ENODEV; + } + + if (!(accessmode(logger_list->mode) & R_OK)) { + logger_list->error = EPERM; + goto done; + } + + nfds = 0; + logger_for_each(logger, logger_list) { + ++nfds; + } + if (nfds <= 0) { + error = ENODEV; + goto done; + } + + /* Do we have anything to offer from the buffer or state? */ + if (logger_list->valid_entry) { /* implies we are also in a flush state */ + goto flush; + } + + ret = android_logger_list_flush(logger_list, log_msg); + if (ret) { + goto done; + } + + if (logger_list->error) { /* implies we are also in a flush state */ + goto done; + } + + /* Lets start grinding on metal */ + pollfds = calloc(nfds, sizeof(struct pollfd)); + if (!pollfds) { + error = ENOMEM; + goto flush; + } + + p = pollfds; + logger_for_each(logger, logger_list) { + p->fd = logger->fd; + p->events = POLLIN; + logger->revents = &p->revents; + ++p; + } + + while (!ret && !error) { + int result; + + /* If we oversleep it's ok, i.e. ignore EINTR. */ + result = TEMP_FAILURE_RETRY( + poll(pollfds, nfds, logger_list->timeout_ms)); + + if (result <= 0) { + if (result) { + error = errno; + } else if (logger_list->mode & O_NDELAY) { + error = EAGAIN; + } else { + logger_list->timeout_ms = LOG_TIMEOUT_NEVER; + } + + logger_list->flush = true; + goto try_flush; + } + + logger_list->timeout_ms = LOG_TIMEOUT_FLUSH; + + /* Anti starvation */ + if (!logger_list->flush + && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) { + /* Any queues with input pending that is low? */ + bool starving = false; + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && low_queue(&logger->log_list)) { + starving = true; + break; + } + } + + /* pushback on any queues that are not low */ + if (starving) { + logger_for_each(logger, logger_list) { + if ((*(logger->revents) & POLLIN) + && !low_queue(&logger->log_list)) { + *(logger->revents) &= ~POLLIN; + } + } + } + } + + logger_for_each(logger, logger_list) { + unsigned int hdr_size; + struct log_list *entry; + int diff; + + if (!(*(logger->revents) & POLLIN)) { + continue; + } + + memset(logger_list->entry.buf, 0, sizeof(struct log_msg)); + /* NOTE: driver guarantees we read exactly one full entry */ + result = read(logger->fd, logger_list->entry.buf, + LOGGER_ENTRY_MAX_LEN); + if (result <= 0) { + if (!result) { + error = EIO; + } else if (errno != EINTR) { + error = errno; + } + continue; + } + + if (logger_list->pid + && (logger_list->pid != logger_list->entry.entry.pid)) { + continue; + } + + hdr_size = logger_list->entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(logger_list->entry.entry_v1); + } + + if ((hdr_size > sizeof(struct log_msg)) + || (logger_list->entry.entry.len + > sizeof(logger_list->entry.buf) - hdr_size) + || (logger_list->entry.entry.len != result - hdr_size)) { + error = EINVAL; + continue; + } + + /* Promote entry to v3 format */ + diff = sizeof(logger_list->entry.entry_v3) - hdr_size; + if (diff > 0) { + if (logger_list->entry.entry.len + > sizeof(logger_list->entry.buf) - hdr_size - diff) { + error = EINVAL; + continue; + } + result += diff; + memmove(logger_list->entry.buf + hdr_size + diff, + logger_list->entry.buf + hdr_size, + logger_list->entry.entry.len + 1); + memset(logger_list->entry.buf + hdr_size, 0, diff); + logger_list->entry.entry.hdr_size = hdr_size + diff; + } + logger_list->entry.entry.lid = logger->id; + + /* speedup: If not tail, and only one list, send directly */ + if (!logger_list->tail + && (list_head(&logger_list->node) + == list_tail(&logger_list->node))) { + ret = result; + memcpy(log_msg->buf, logger_list->entry.buf, result + 1); + break; + } + + entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1); + + if (!entry) { + logger_list->valid_entry = true; + error = ENOMEM; + break; + } + + logger_list->queued_lines++; + + memcpy(entry->entry.buf, logger_list->entry.buf, result); + entry->entry.buf[result] = '\0'; + list_add_tail(&logger->log_list, &entry->node); + } + + if (ret <= 0) { +try_flush: + ret = android_logger_list_flush(logger_list, log_msg); + } + } + + free(pollfds); + +flush: + if (error) { + logger_list->flush = true; + } + + if (ret <= 0) { + ret = android_logger_list_flush(logger_list, log_msg); + + if (!ret && logger_list->valid_entry) { + ret = logger_list->entry.entry.hdr_size; + if (!ret) { + ret = sizeof(logger_list->entry.entry_v1); + } + ret += logger_list->entry.entry.len; + + memcpy(log_msg->buf, logger_list->entry.buf, + sizeof(struct log_msg)); + logger_list->valid_entry = false; + } + } + +done: + if (logger_list->error) { + error = logger_list->error; + } + if (error) { + logger_list->error = error; + if (!ret) { + ret = -error; + } + } + return ret; +} + +/* Close all the logs */ +void android_logger_list_free(struct logger_list *logger_list) +{ + if (logger_list == NULL) { + return; + } + + while (!list_empty(&logger_list->node)) { + struct listnode *node = list_head(&logger_list->node); + struct logger *logger = node_to_item(node, struct logger, node); + android_logger_free(logger); + } + + free(logger_list); +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c index 5766f8cac..d3ee167bd 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -13,41 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -#include +#include +#include #ifdef HAVE_PTHREADS #include #endif -#include -#include -#include -#include -#include #include -#include +#include +#include +#include #include +#include +#if (FAKE_LOG_DEVICE == 0) +#include +#include +#endif +#include +#include -#include #include -#include - -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" -#define LOGGER_LOG_SYSTEM "log/system" +#include +#include +#include #define LOG_BUF_SIZE 1024 #if FAKE_LOG_DEVICE -// This will be defined when building for the host. +/* 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); @@ -58,11 +51,15 @@ static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; #define UNUSED __attribute__((__unused__)) +static int logd_fd = -1; +#if FAKE_LOG_DEVICE +#define WEAK __attribute__((weak)) static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; +#endif /* * This is used by the C++ code to decide if it should write logs through - * the C code. Basically, if /dev/log/... is available, we're running in + * the C code. Basically, if /dev/socket/logd is available, we're running in * the simulator rather than a desktop tool and want to use the device. */ static enum { @@ -71,7 +68,7 @@ static enum { int __android_log_dev_available(void) { if (g_log_status == kLogUninitialized) { - if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + if (access("/dev/socket/logdw", W_OK) == 0) g_log_status = kLogAvailable; else g_log_status = kLogNotAvailable; @@ -81,13 +78,14 @@ int __android_log_dev_available(void) } static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED, - size_t nr UNUSED) + size_t nr UNUSED) { return -1; } static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) { +#if FAKE_LOG_DEVICE ssize_t ret; int log_fd; @@ -96,14 +94,63 @@ static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) } else { return EBADF; } - do { - ret = log_writev(log_fd, vec, nr); + ret = fakeLogWritev(log_fd, vec, nr); } while (ret < 0 && errno == EINTR); return ret; +#else + if (logd_fd == -1) { + return -1; + } + if (getuid() == AID_LOGD) { + /* + * ignore log messages we send to ourself. + * Such log messages are often generated by libraries we depend on + * which use standard Android logging. + */ + return 0; + } + struct iovec newVec[nr + 2]; + typeof_log_id_t log_id_buf = log_id; + + newVec[0].iov_base = (unsigned char *) &log_id_buf; + newVec[0].iov_len = sizeof_log_id_t; + + log_time realtime_ts; + clock_gettime(CLOCK_REALTIME, &realtime_ts); + + newVec[1].iov_base = (unsigned char *) &realtime_ts; + newVec[1].iov_len = sizeof(log_time); + + size_t i; + for (i = 2; i < nr + 2; i++) { + newVec[i].iov_base = vec[i-2].iov_base; + newVec[i].iov_len = vec[i-2].iov_len; + } + + /* The write below could be lost, but will never block. */ + return writev(logd_fd, newVec, nr + 2); +#endif } +#if FAKE_LOG_DEVICE +static const char *LOG_NAME[LOG_ID_MAX] = { + [LOG_ID_MAIN] = "main", + [LOG_ID_RADIO] = "radio", + [LOG_ID_EVENTS] = "events", + [LOG_ID_SYSTEM] = "system" +}; + +const WEAK char *android_log_id_to_name(log_id_t log_id) +{ + if (log_id >= LOG_ID_MAX) { + log_id = LOG_ID_MAIN; + } + return LOG_NAME[log_id]; +} +#endif + static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { #ifdef HAVE_PTHREADS @@ -111,27 +158,35 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) #endif if (write_to_log == __write_to_log_init) { - log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); - log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); - log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); - log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); - write_to_log = __write_to_log_kernel; - if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || - log_fds[LOG_ID_EVENTS] < 0) { - log_close(log_fds[LOG_ID_MAIN]); - log_close(log_fds[LOG_ID_RADIO]); - log_close(log_fds[LOG_ID_EVENTS]); - log_fds[LOG_ID_MAIN] = -1; - log_fds[LOG_ID_RADIO] = -1; - log_fds[LOG_ID_EVENTS] = -1; +#if FAKE_LOG_DEVICE + int i; + for (i = 0; i < LOG_ID_MAX; i++) { + char buf[sizeof("/dev/log_system")]; + snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i)); + log_fds[i] = fakeLogOpen(buf, O_WRONLY); + } +#else + int sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (sock != -1) { + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + /* NB: Loss of content */ + close(sock); + sock = -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"); + + connect(sock, (struct sockaddr *)&un, sizeof(struct sockaddr_un)); + } + } else { write_to_log = __write_to_log_null; } - - if (log_fds[LOG_ID_SYSTEM] < 0) { - log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; - } + logd_fd = sock; +#endif } #ifdef HAVE_PTHREADS @@ -288,7 +343,7 @@ int __android_log_bwrite(int32_t tag, const void *payload, size_t len) * handy if we just want to dump an integer into the log. */ int __android_log_btwrite(int32_t tag, char type, const void *payload, - size_t len) + size_t len) { struct iovec vec[3]; diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c new file mode 100644 index 000000000..32a202b7d --- /dev/null +++ b/liblog/logd_write_kern.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2007-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 +#include +#ifdef HAVE_PTHREADS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" + +#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 + +#define UNUSED __attribute__((__unused__)) + +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; + +/* + * This is used by the C++ code to decide if it should write logs through + * the C code. Basically, if /dev/log/... is available, we're running in + * the simulator rather than a desktop tool and want to use the device. + */ +static enum { + kLogUninitialized, kLogNotAvailable, kLogAvailable +} g_log_status = kLogUninitialized; +int __android_log_dev_available(void) +{ + if (g_log_status == kLogUninitialized) { + if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + g_log_status = kLogAvailable; + else + g_log_status = kLogNotAvailable; + } + + return (g_log_status == kLogAvailable); +} + +static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED, + size_t nr UNUSED) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +{ + ssize_t ret; + int log_fd; + + if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { + log_fd = log_fds[(int)log_id]; + } else { + return EBADF; + } + + do { + ret = log_writev(log_fd, vec, nr); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +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); + log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); + + write_to_log = __write_to_log_kernel; + + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || + log_fds[LOG_ID_EVENTS] < 0) { + log_close(log_fds[LOG_ID_MAIN]); + log_close(log_fds[LOG_ID_RADIO]); + log_close(log_fds[LOG_ID_EVENTS]); + log_fds[LOG_ID_MAIN] = -1; + log_fds[LOG_ID_RADIO] = -1; + log_fds[LOG_ID_EVENTS] = -1; + write_to_log = __write_to_log_null; + } + + if (log_fds[LOG_ID_SYSTEM] < 0) { + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + } + } + +#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; + } + + 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); +} + +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + char tmp_tag[32]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if ((bufID != LOG_ID_RADIO) && + (!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"))) { + bufID = 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; + } + + 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(bufID, vec, 3); +} + +int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) +{ + char buf[LOG_BUF_SIZE]; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_buf_write(bufID, prio, tag, buf); +} + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +{ + char buf[LOG_BUF_SIZE]; + + if (fmt) { + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + } else { + /* Msg not provided, log condition. N.B. Do not use cond directly as + * format string as it could contain spurious '%' syntax (e.g. + * "%d" in "blocks%devs == 0"). + */ + if (cond) + snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond); + else + strcpy(buf, "Unspecified assertion failed"); + } + + __android_log_write(ANDROID_LOG_FATAL, tag, buf); + + __builtin_trap(); /* trap so we have a chance to debug the situation */ +} + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len) +{ + struct iovec vec[2]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = (void*)payload; + vec[1].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 2); +} + +/* + * Like __android_log_bwrite, but takes the type as well. Doesn't work + * for the general case where we're generating lists of stuff, but very + * handy if we just want to dump an integer into the log. + */ +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len) +{ + struct iovec vec[3]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = (void*)payload; + vec[2].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 3); +} diff --git a/logd/Android.mk b/logd/Android.mk index a99c7486c..1ca186978 100644 --- a/logd/Android.mk +++ b/logd/Android.mk @@ -1,3 +1,5 @@ +ifeq ($(TARGET_USES_LOGD),true) + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -24,3 +26,4 @@ LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) +endif # TARGET_USES_LOGD