diff options
40 files changed, 2974 insertions, 576 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 155c6e5..62f012c 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -34,7 +34,7 @@ endif ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c - EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c + EXTRA_SRCS := get_my_path_windows.c EXTRA_STATIC_LIBS := AdbWinApi ifneq ($(strip $(USE_CYGWIN)),) # Pure cygwin case @@ -114,8 +114,7 @@ LOCAL_SRC_FILES := \ jdwp_service.c \ framebuffer_service.c \ remount_service.c \ - usb_linux_client.c \ - log_service.c + usb_linux_client.c LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index 5966686..7f85dc3 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -198,11 +198,6 @@ localfilesystem:<path> Variants of local:<path> that are used to access other Android socket namespaces. -log:<name> - Opens one of the system logs (/dev/log/<name>) and allows the client - to read them directly. Used to implement 'adb logcat'. The stream - will be read-only for the client. - framebuffer: This service is used to send snapshots of the framebuffer to a client. It requires sufficient privileges but works as follow: @@ -330,9 +330,7 @@ typedef enum { } BackupOperation; int backup_service(BackupOperation operation, char* args); void framebuffer_service(int fd, void *cookie); -void log_service(int fd, void *cookie); void remount_service(int fd, void *cookie); -char * get_log_file_path(const char * log_name); #endif /* packet allocator */ diff --git a/adb/log_service.c b/adb/log_service.c deleted file mode 100644 index af24356..0000000 --- a/adb/log_service.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2007 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 <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/socket.h> -#include <log/logger.h> -#include "sysdeps.h" -#include "adb.h" - -#define LOG_FILE_DIR "/dev/log/" - -void write_log_entry(int fd, struct logger_entry *buf); - -void log_service(int fd, void *cookie) -{ - /* get the name of the log filepath to read */ - char * log_filepath = cookie; - - /* open the log file. */ - int logfd = unix_open(log_filepath, O_RDONLY); - if (logfd < 0) { - goto done; - } - - // temp buffer to read the entries - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry *entry = (struct logger_entry *) buf; - - while (1) { - int ret; - - ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - // perror("logcat read"); - goto done; - } - else if (!ret) { - // fprintf(stderr, "read: Unexpected EOF!\n"); - goto done; - } - - /* NOTE: driver guarantees we read exactly one full entry */ - - entry->msg[entry->len] = '\0'; - - write_log_entry(fd, entry); - } - -done: - unix_close(fd); - free(log_filepath); -} - -/* returns the full path to the log file in a newly allocated string */ -char * get_log_file_path(const char * log_name) { - char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1); - - strcpy(log_device, LOG_FILE_DIR); - strcat(log_device, log_name); - - return log_device; -} - - -/* prints one log entry into the file descriptor fd */ -void write_log_entry(int fd, struct logger_entry *buf) -{ - size_t size = sizeof(struct logger_entry) + buf->len; - - writex(fd, buf, size); -} diff --git a/adb/services.c b/adb/services.c index 17d40a0..dcaf276 100644 --- a/adb/services.c +++ b/adb/services.c @@ -355,8 +355,6 @@ int service_to_fd(const char *name) ret = create_service_thread(framebuffer_service, 0); } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); - } else if (!strncmp(name, "log:", 4)) { - ret = create_service_thread(log_service, get_log_file_path(name + 4)); } else if(!HOST && !strncmp(name, "shell:", 6)) { if(name[6]) { ret = create_subproc_thread(name + 6); diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index acb6ed6..b4c57cc 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2012-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. @@ -29,6 +29,7 @@ #include <private/android_filesystem_config.h> +#include <log/log.h> #include <log/logger.h> #include <cutils/properties.h> @@ -453,43 +454,40 @@ static bool dump_sibling_thread_report( // Reads the contents of the specified log device, filters out the entries // that don't match the specified pid, and writes them to the tombstone file. // -// If "tailOnly" is set, we only print the last few lines. -static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tailOnly) { +// If "tail" is set, we only print the last few lines. +static void dump_log_file(log_t* log, pid_t pid, const char* filename, + unsigned int tail) { bool first = true; + struct logger_list *logger_list; - // circular buffer, for "tailOnly" mode - const int kShortLogMaxLines = 5; - const int kShortLogLineLen = 256; - char shortLog[kShortLogMaxLines][kShortLogLineLen]; - int shortLogCount = 0; - int shortLogNext = 0; + logger_list = android_logger_list_open( + android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid); - int logfd = open(filename, O_RDONLY | O_NONBLOCK); - if (logfd < 0) { + if (!logger_list) { XLOG("Unable to open %s: %s\n", filename, strerror(errno)); return; } - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry entry; - } log_entry; + struct log_msg log_entry; while (true) { - ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + ssize_t actual = android_logger_list_read(logger_list, &log_entry); + if (actual < 0) { - if (errno == EINTR) { + if (actual == -EINTR) { // interrupted by signal, retry continue; - } else if (errno == EAGAIN) { + } else if (actual == -EAGAIN) { // non-blocking EOF; we're done break; } else { - _LOG(log, 0, "Error while reading log: %s\n", strerror(errno)); + _LOG(log, 0, "Error while reading log: %s\n", + strerror(-actual)); break; } } else if (actual == 0) { - _LOG(log, 0, "Got zero bytes while reading log: %s\n", strerror(errno)); + _LOG(log, 0, "Got zero bytes while reading log: %s\n", + strerror(errno)); break; } @@ -497,7 +495,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail // because you will be writing as fast as you're reading. Any // high-frequency debug diagnostics should just be written to // the tombstone file. - struct logger_entry* entry = &log_entry.entry; + struct logger_entry* entry = &log_entry.entry_v1; if (entry->pid != static_cast<int32_t>(pid)) { // wrong pid, ignore @@ -505,7 +503,8 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail } if (first) { - _LOG(log, 0, "--------- %slog %s\n", tailOnly ? "tail end of " : "", filename); + _LOG(log, 0, "--------- %slog %s\n", + tail ? "tail end of " : "", filename); first = false; } @@ -513,18 +512,20 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail // // We want to display it in the same format as "logcat -v threadtime" // (although in this case the pid is redundant). - // - // TODO: scan for line breaks ('\n') and display each text line - // on a separate line, prefixed with the header, like logcat does. static const char* kPrioChars = "!.VDIWEFS"; - unsigned char prio = entry->msg[0]; - char* tag = entry->msg + 1; - char* msg = tag + strlen(tag) + 1; + unsigned hdr_size = log_entry.entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(log_entry.entry_v1); + } + char* msg = (char *)log_entry.buf + hdr_size; + unsigned char prio = msg[0]; + char* tag = msg + 1; + msg = tag + strlen(tag) + 1; // consume any trailing newlines - char* eatnl = msg + strlen(msg) - 1; - while (eatnl >= msg && *eatnl == '\n') { - *eatnl-- = '\0'; + char* nl = msg + strlen(msg) - 1; + while (nl >= msg && *nl == '\n') { + *nl-- = '\0'; } char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); @@ -536,44 +537,30 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail ptm = localtime_r(&sec, &tmBuf); strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - if (tailOnly) { - snprintf(shortLog[shortLogNext], kShortLogLineLen, - "%s.%03d %5d %5d %c %-8s: %s", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - shortLogCount++; - } else { - _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, prioChar, tag, msg); - } - } - - if (tailOnly) { - int i; + // Look for line breaks ('\n') and display each text line + // on a separate line, prefixed with the header, like logcat does. + do { + nl = strchr(msg, '\n'); + if (nl) { + *nl = '\0'; + ++nl; + } - // If we filled the buffer, we want to start at "next", which has - // the oldest entry. If we didn't, we want to start at zero. - if (shortLogCount < kShortLogMaxLines) { - shortLogNext = 0; - } else { - shortLogCount = kShortLogMaxLines; // cap at window size - } + _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); - for (i = 0; i < shortLogCount; i++) { - _LOG(log, 0, "%s\n", shortLog[shortLogNext]); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - } + } while ((msg = nl)); } - close(logfd); + android_logger_list_free(logger_list); } // Dumps the logs generated by the specified pid to the tombstone, from both // "system" and "main" log devices. Ideally we'd interleave the output. -static void dump_logs(log_t* log, pid_t pid, bool tailOnly) { - dump_log_file(log, pid, "/dev/log/system", tailOnly); - dump_log_file(log, pid, "/dev/log/main", tailOnly); +static void dump_logs(log_t* log, pid_t pid, unsigned tail) { + dump_log_file(log, pid, "system", tail); + dump_log_file(log, pid, "main", tail); } static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) { @@ -649,7 +636,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a } if (want_logs) { - dump_logs(log, pid, true); + dump_logs(log, pid, 5); } bool detach_failed = false; @@ -661,7 +648,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a delete map; if (want_logs) { - dump_logs(log, pid, false); + dump_logs(log, pid, 0); } // send EOD to the Activity Manager, then wait for its ack to avoid racing ahead diff --git a/include/cutils/list.h b/include/cutils/list.h index 72395f4..945729a 100644 --- a/include/cutils/list.h +++ b/include/cutils/list.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2008-2013 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. @@ -49,9 +49,25 @@ struct listnode node != (list); \ node = next, next = node->next) -void list_init(struct listnode *list); -void list_add_tail(struct listnode *list, struct listnode *item); -void list_remove(struct listnode *item); +static inline void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +static inline void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +static inline void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} #define list_empty(list) ((list) == (list)->next) #define list_head(list) ((list)->next) diff --git a/include/log/log.h b/include/log/log.h index 7faddea..7f952ff 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 The Android Open Source Project + * Copyright (C) 2005-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. @@ -28,17 +28,16 @@ #ifndef _LIBS_LOG_LOG_H #define _LIBS_LOG_LOG_H -#include <stdio.h> -#include <time.h> #include <sys/types.h> -#include <unistd.h> #ifdef HAVE_PTHREADS #include <pthread.h> #endif #include <stdarg.h> - -#include <log/uio.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> #include <log/logd.h> +#include <log/uio.h> #ifdef __cplusplus extern "C" { @@ -470,7 +469,8 @@ typedef enum { EVENT_TYPE_STRING = 2, EVENT_TYPE_LIST = 3, } AndroidEventLogType; - +#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType) +#define typeof_AndroidEventLogType unsigned char #ifndef LOG_EVENT_INT #define LOG_EVENT_INT(_tag, _value) { \ @@ -540,7 +540,9 @@ typedef enum { #define android_logToFile(tag, file) (0) #define android_logToFd(tag, fd) (0) -typedef enum { +typedef enum log_id { + LOG_ID_MIN = 0, + LOG_ID_MAIN = 0, LOG_ID_RADIO = 1, LOG_ID_EVENTS = 2, @@ -548,6 +550,8 @@ typedef enum { LOG_ID_MAX } log_id_t; +#define sizeof_log_id_t sizeof(typeof_log_id_t) +#define typeof_log_id_t unsigned char /* * Send a simple string to the log. @@ -555,9 +559,8 @@ typedef enum { int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); - #ifdef __cplusplus } #endif -#endif // _LIBS_CUTILS_LOG_H +#endif /* _LIBS_LOG_LOG_H */ diff --git a/include/log/log_read.h b/include/log/log_read.h new file mode 100644 index 0000000..861c192 --- /dev/null +++ b/include/log/log_read.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 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. + */ + +#ifndef _LIBS_LOG_LOG_READ_H +#define _LIBS_LOG_LOG_READ_H + +#include <time.h> + +#define NS_PER_SEC 1000000000ULL +#ifdef __cplusplus +struct log_time : public timespec { +public: + log_time(timespec &T) + { + tv_sec = T.tv_sec; + tv_nsec = T.tv_nsec; + } + log_time(void) + { + } + log_time(clockid_t id) + { + clock_gettime(id, (timespec *) this); + } + log_time(const char *T) + { + const uint8_t *c = (const uint8_t *) T; + tv_sec = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); + tv_nsec = c[4] | (c[5] << 8) | (c[6] << 16) | (c[7] << 24); + } + bool operator== (const timespec &T) const + { + return (tv_sec == T.tv_sec) && (tv_nsec == T.tv_nsec); + } + bool operator!= (const timespec &T) const + { + return !(*this == T); + } + bool operator< (const timespec &T) const + { + return (tv_sec < T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec < T.tv_nsec)); + } + bool operator>= (const timespec &T) const + { + return !(*this < T); + } + bool operator> (const timespec &T) const + { + return (tv_sec > T.tv_sec) + || ((tv_sec == T.tv_sec) && (tv_nsec > T.tv_nsec)); + } + bool operator<= (const timespec &T) const + { + return !(*this > T); + } + uint64_t nsec(void) const + { + return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec; + } +}; +#else +typedef struct timespec log_time; +#endif + +#endif /* define _LIBS_LOG_LOG_READ_H */ diff --git a/include/log/logger.h b/include/log/logger.h index 04f3fb0..966397a 100644 --- a/include/log/logger.h +++ b/include/log/logger.h @@ -1,16 +1,21 @@ -/* utils/logger.h -** -** Copyright 2007, The Android Open Source Project +/* +** +** Copyright 2007-2014, The Android Open Source Project ** ** This file is dual licensed. It may be redistributed and/or modified ** under the terms of the Apache 2.0 License OR version 2 of the GNU ** General Public License. */ -#ifndef _UTILS_LOGGER_H -#define _UTILS_LOGGER_H +#ifndef _LIBS_LOG_LOGGER_H +#define _LIBS_LOG_LOGGER_H #include <stdint.h> +#include <log/log.h> + +#ifdef __cplusplus +extern "C" { +#endif /* * The userspace structure for version 1 of the logger_entry ABI. @@ -43,11 +48,6 @@ struct logger_entry_v2 { char msg[0]; /* the entry's payload */ }; -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" -#define LOGGER_LOG_SYSTEM "log/system" - /* * The maximum size of the log entry payload that can be * written to the kernel logger driver. An attempt to write @@ -63,19 +63,108 @@ struct logger_entry_v2 { */ #define LOGGER_ENTRY_MAX_LEN (5*1024) -#ifdef HAVE_IOCTL +#define NS_PER_SEC 1000000000ULL + +struct log_msg { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry_v2 entry; + 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 */ + bool operator== (const log_msg &T) const + { + return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec); + } + bool operator!= (const log_msg &T) const + { + return !(*this == T); + } + bool operator< (const log_msg &T) const + { + return (entry.sec < T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec < T.entry.nsec)); + } + bool operator>= (const log_msg &T) const + { + return !(*this < T); + } + bool operator> (const log_msg &T) const + { + return (entry.sec > T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec > T.entry.nsec)); + } + bool operator<= (const log_msg &T) const + { + return !(*this > T); + } + uint64_t nsec(void) const + { + return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec; + } + + /* packet methods */ + log_id_t id(void) + { + return extra.id; + } + char *msg(void) + { + return entry.hdr_size ? (char *) buf + entry.hdr_size : entry_v1.msg; + } + unsigned int len(void) + { + return (entry.hdr_size ? entry.hdr_size : sizeof(entry_v1)) + entry.len; + } +#endif +}; -#include <sys/ioctl.h> +struct logger; -#define __LOGGERIO 0xAE +log_id_t android_logger_get_id(struct logger *logger); -#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 */ +int android_logger_clear(struct logger *logger); +int android_logger_get_log_size(struct logger *logger); +int android_logger_get_log_readable_size(struct logger *logger); +int android_logger_get_log_version(struct logger *logger); + +struct logger_list; + +struct logger_list *android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid); +void android_logger_list_free(struct logger_list *logger_list); +/* In the purest sense, the following two are orthogonal interfaces */ +int android_logger_list_read(struct logger_list *logger_list, + struct log_msg *log_msg); + +/* Multiple log_id_t opens */ +struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id); +#define android_logger_close android_logger_free +/* Single log_id_t open */ +struct logger_list *android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid); +#define android_logger_list_close android_logger_list_free + +/* + * log_id_t helpers + */ +log_id_t android_name_to_log_id(const char *logName); +const char *android_log_id_to_name(log_id_t log_id); -#endif // HAVE_IOCTL +#ifdef __cplusplus +} +#endif -#endif /* _UTILS_LOGGER_H */ +#endif /* _LIBS_LOG_LOGGER_H */ diff --git a/include/log/uio.h b/include/log/uio.h index 01a74d2..a71f515 100644 --- a/include/log/uio.h +++ b/include/log/uio.h @@ -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. @@ -31,8 +31,8 @@ extern "C" { #include <stddef.h> struct iovec { - const void* iov_base; - size_t iov_len; + void* iov_base; + size_t iov_len; }; extern int readv( int fd, struct iovec* vecs, int count ); diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h index 85b58ef..1004f06 100644 --- a/include/sysutils/SocketClient.h +++ b/include/sysutils/SocketClient.h @@ -6,22 +6,23 @@ #include <pthread.h> #include <cutils/atomic.h> #include <sys/types.h> +#include <sys/uio.h> class SocketClient { int mSocket; bool mSocketOwned; pthread_mutex_t mWriteMutex; - /* Peer process ID */ + // Peer process ID pid_t mPid; - /* Peer user ID */ + // Peer user ID uid_t mUid; - /* Peer group ID */ + // Peer group ID gid_t mGid; - /* Reference count (starts at 1) */ + // Reference count (starts at 1) pthread_mutex_t mRefCountMutex; int mRefCount; @@ -38,12 +39,15 @@ public: pid_t getPid() const { return mPid; } uid_t getUid() const { return mUid; } gid_t getGid() const { return mGid; } - void setCmdNum(int cmdNum) { android_atomic_release_store(cmdNum, &mCmdNum); } + void setCmdNum(int cmdNum) { + android_atomic_release_store(cmdNum, &mCmdNum); + } int getCmdNum() { return mCmdNum; } // Send null-terminated C strings: int sendMsg(int code, const char *msg, bool addErrno); int sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum); + int sendMsg(const char *msg); // Provides a mechanism to send a response code to the client. // Sends the code and a null character. @@ -56,6 +60,8 @@ public: // Sending binary data: int sendData(const void *data, int len); + // iovec contents not preserved through call + int sendDatav(struct iovec *iov, int iovcnt); // Optional reference counting. Reference count starts at 1. If // it's decremented to 0, it deletes itself. @@ -64,19 +70,18 @@ public: void incRef(); bool decRef(); // returns true at 0 (but note: SocketClient already deleted) - // return a new string in quotes with '\\' and '\"' escaped for "my arg" transmissions + // return a new string in quotes with '\\' and '\"' escaped for "my arg" + // transmissions static char *quoteArg(const char *arg); private: - // Send null-terminated C strings - int sendMsg(const char *msg); void init(int socket, bool owned, bool useCmdNum); - // Sending binary data. The caller should use make sure this is protected + // Sending binary data. The caller should make sure this is protected // from multiple threads entering simultaneously. - // returns 0 if successful, -1 if there is a 0 byte write and -2 if any other - // error occurred (use errno to get the error) - int sendDataLocked(const void *data, int len); + // returns 0 if successful, -1 if there is a 0 byte write or if any + // other error occurred (use errno to get the error) + int sendDataLockedv(struct iovec *iov, int iovcnt); }; typedef android::sysutils::List<SocketClient *> SocketClientCollection; diff --git a/libcutils/list.c b/include/sysutils/SocketClientCommand.h index e13452d..746bc25 100644 --- a/libcutils/list.c +++ b/include/sysutils/SocketClientCommand.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 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. @@ -13,25 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#ifndef _SOCKETCLIENTCOMMAND_H +#define _SOCKETCLIENTCOMMAND_H -#include <cutils/list.h> +#include <sysutils/SocketClient.h> -void list_init(struct listnode *node) -{ - node->next = node; - node->prev = node; -} +class SocketClientCommand { +public: + virtual ~SocketClientCommand() { } + virtual void runSocketCommand(SocketClient *client) = 0; +}; -void list_add_tail(struct listnode *head, struct listnode *item) -{ - item->next = head; - item->prev = head->prev; - head->prev->next = item; - head->prev = item; -} - -void list_remove(struct listnode *item) -{ - item->next->prev = item->prev; - item->prev->next = item->next; -} +#endif diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h index 8f56230..c204a0f 100644 --- a/include/sysutils/SocketListener.h +++ b/include/sysutils/SocketListener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-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. @@ -19,6 +19,7 @@ #include <pthread.h> #include <sysutils/SocketClient.h> +#include "SocketClientCommand.h" class SocketListener { bool mListen; @@ -41,10 +42,15 @@ public: void sendBroadcast(int code, const char *msg, bool addErrno); + void runOnEachSocket(SocketClientCommand *command); + + bool release(SocketClient *c) { return release(c, true); } + protected: virtual bool onDataAvailable(SocketClient *c) = 0; private: + bool release(SocketClient *c, bool wakeup); static void *threadStart(void *obj); void runListener(); void init(const char *socketName, int socketFd, bool listen, bool useCmdNum); diff --git a/libcutils/Android.mk b/libcutils/Android.mk index c08a50f..93bccb0 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -37,7 +37,6 @@ commonSources := \ config_utils.c \ cpu_info.c \ load_file.c \ - list.c \ open_memstream.c \ strdup16to8.c \ strdup8to16.c \ diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c index aef3054..b7895fa 100644 --- a/libcutils/android_reboot.c +++ b/libcutils/android_reboot.c @@ -25,6 +25,8 @@ #include <cutils/android_reboot.h> +#define UNUSED __attribute__((unused)) + /* Check to see if /proc/mounts contains any writeable filesystems * backed by a block device. * Return true if none found, else return false. @@ -102,7 +104,7 @@ static void remount_ro(void) } -int android_reboot(int cmd, int flags, char *arg) +int android_reboot(int cmd, int flags UNUSED, char *arg) { int ret; diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c index f350f58..5d90a01 100644 --- a/libcutils/iosched_policy.c +++ b/libcutils/iosched_policy.c @@ -1,7 +1,6 @@ - -/* libs/cutils/iosched_policy.c +/* ** -** Copyright 2007, The Android Open Source Project +** Copyright 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. @@ -27,7 +26,11 @@ #include <cutils/iosched_policy.h> +#ifdef HAVE_ANDROID_OS +/* #include <linux/ioprio.h> */ extern int ioprio_set(int which, int who, int ioprio); +extern int ioprio_get(int which, int who); +#endif enum { WHO_PROCESS = 1, diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c index 036ce2e..5310516 100644 --- a/libcutils/socket_local_client.c +++ b/libcutils/socket_local_client.c @@ -39,6 +39,8 @@ int socket_local_client(const char *name, int namespaceId, int type) #include "socket_local.h" +#define UNUSED __attribute__((unused)) + #define LISTEN_BACKLOG 4 /* Documented in header file. */ @@ -122,7 +124,7 @@ error: * Used by AndroidSocketImpl */ int socket_local_client_connect(int fd, const char *name, int namespaceId, - int type) + int type UNUSED) { struct sockaddr_un addr; socklen_t alen; diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 9e1d2dc..7cfbcb3 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2013 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. @@ -30,6 +30,8 @@ #include <cutils/str_parms.h> +#define UNUSED __attribute__((unused)) + struct str_parms { Hashmap *map; }; @@ -278,10 +280,11 @@ int str_parms_get_float(struct str_parms *str_parms, const char *key, return -ENOENT; out = strtof(value, &end); - if (*value != '\0' && *end == '\0') - return 0; + if (*value == '\0' || *end != '\0') + return -EINVAL; - return -EINVAL; + *val = out; + return 0; } static bool combine_strings(void *key, void *value, void *context) @@ -318,7 +321,7 @@ char *str_parms_to_str(struct str_parms *str_parms) return str; } -static bool dump_entry(void *key, void *value, void *context) +static bool dump_entry(void *key, void *value, void *context UNUSED) { ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value); return true; diff --git a/liblog/Android.mk b/liblog/Android.mk index 6bfb119..0d6c970 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -1,5 +1,5 @@ # -# Copyright (C) 2008 The Android Open Source Project +# Copyright (C) 2008-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. @@ -42,7 +42,7 @@ else endif liblog_host_sources := $(liblog_sources) fake_log_device.c - +liblog_target_sources = $(liblog_sources) log_read.c # Shared and static library for host # ======================================================== @@ -67,15 +67,16 @@ LOCAL_LDLIBS := -lpthread LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64 include $(BUILD_HOST_STATIC_LIBRARY) - # Shared and static library for target # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := liblog -LOCAL_SRC_FILES := $(liblog_sources) +LOCAL_SRC_FILES := $(liblog_target_sources) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := liblog LOCAL_WHOLE_STATIC_LIBRARIES := liblog include $(BUILD_SHARED_LIBRARY) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/liblog/NOTICE b/liblog/NOTICE index c5b1efa..06a9081 100644 --- a/liblog/NOTICE +++ b/liblog/NOTICE @@ -1,5 +1,5 @@ - Copyright (c) 2005-2008, The Android Open Source Project + Copyright (c) 2005-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. diff --git a/liblog/README b/liblog/README new file mode 100644 index 0000000..d7472e4 --- /dev/null +++ b/liblog/README @@ -0,0 +1,134 @@ +LIBLOG(3) Android NDK Programming Manual LIBLOG(3) + + + +NAME + liblog - Android NDK logger interfaces + +SYNOPSIS + #include <log/log.h> + + ALOG(android_priority, tag, format, ...) + IF_ALOG(android_priority, tag) + LOG_PRI(priority, tag, format, ...) + LOG_PRI_VA(priority, tag, format, args) + #define LOG_TAG NULL + ALOGV(format, ...) + SLOGV(format, ...) + RLOGV(format, ...) + ALOGV_IF(cond, format, ...) + SLOGV_IF(cond, format, ...) + RLOGV_IF(cond, format, ...) + IF_ALOGC() + ALOGD(format, ...) + SLOGD(format, ...) + RLOGD(format, ...) + ALOGD_IF(cond, format, ...) + SLOGD_IF(cond, format, ...) + RLOGD_IF(cond, format, ...) + IF_ALOGD() + ALOGI(format, ...) + SLOGI(format, ...) + RLOGI(format, ...) + ALOGI_IF(cond, format, ...) + SLOGI_IF(cond, format, ...) + RLOGI_IF(cond, format, ...) + IF_ALOGI() + ALOGW(format, ...) + SLOGW(format, ...) + RLOGW(format, ...) + ALOGW_IF(cond, format, ...) + SLOGW_IF(cond, format, ...) + RLOGW_IF(cond, format, ...) + IF_ALOGW() + ALOGE(format, ...) + SLOGE(format, ...) + RLOGE(format, ...) + ALOGE_IF(cond, format, ...) + SLOGE_IF(cond, format, ...) + RLOGE_IF(cond, format, ...) + IF_ALOGE() + LOG_FATAL(format, ...) + LOG_ALWAYS_FATAL(format, ...) + LOG_FATAL_IF(cond, format, ...) + LOG_ALWAYS_FATAL_IF(cond, format, ...) + ALOG_ASSERT(cond, format, ...) + LOG_EVENT_INT(tag, value) + LOG_EVENT_LONG(tag, value) + + Link with -llog + + #include <log/logger.h> + + log_id_t android_logger_get_id(struct logger *logger) + int android_logger_clear(struct logger *logger) + int android_logger_get_log_size(struct logger *logger) + int android_logger_get_log_readable_size(struct logger *logger) + int android_logger_get_log_version(struct logger *logger) + + struct logger_list *android_logger_list_alloc(int mode, unsigned int + tail, pid_t pid) + struct logger *android_logger_open(struct logger_list *logger_list, + log_id_t id) + struct logger_list *android_logger_list_open(log_id_t id, int mode, + unsigned int tail, pid_t pid) + + int android_logger_list_read(struct logger_list *logger_list, struct + log_msg *log_msg + + void android_logger_list_free(struct logger_list *logger_list) + + log_id_t android_name_to_log_id(const char *logName) + const char *android_log_id_to_name(log_id_t log_id) + + Link with -llog + +DESCRIPTION + liblog represents an interface to the volatile Android Logging system + for NDK (Native) applications and libraries. Interfaces for either + writing or reading logs. The log buffers are divided up in Main, Sys‐ + tem, Radio and Events sub-logs. + + The logging interfaces are a series of macros, all of which can be + overridden individually in order to control the verbosity of the appli‐ + cation or library. [ASR]LOG[VDIWE] calls are used to log to BAsic, + System or Radio sub-logs in either the Verbose, Debug, Info, Warning or + Error priorities. [ASR]LOG[VDIWE]_IF calls are used to perform thus + based on a condition being true. IF_ALOG[VDIWE] calls are true if the + current LOG_TAG is enabled at the specified priority. LOG_ALWAYS_FATAL + is used to ALOG a message, then kill the process. LOG_FATAL call is a + variant of LOG_ALWAYS_FATAL, only enabled in engineering, and not + release builds. ALOG_ASSERT is used to ALOG a message if the condition + is false; the condition is part of the logged message. + LOG_EVENT_(INT|LONG) is used to drop binary content into the Events + sub-log. + + The log reading interfaces permit opening the logs either singly or + multiply, retrieving a log entry at a time in time sorted order, + optionally limited to a specific pid and tail of the log(s) and finally + a call closing the logs. A single log can be opened with android_log‐ + ger_list_open; or multiple logs can be opened with android_log‐ + 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. + + 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. + + 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. + +SEE ALSO + syslogd(8) + + + + 17 Dec 2013 LIBLOG(3) diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index be34f01..da83a85 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-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. @@ -322,11 +322,11 @@ static const char* getPriorityString(int priority) * Make up something to replace it. */ static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { - int result = 0; - struct iovec* end = iov + iovcnt; + ssize_t result = 0; + const struct iovec* end = iov + iovcnt; for (; iov < end; iov++) { - int w = write(fd, iov->iov_base, iov->iov_len); - if (w != iov->iov_len) { + ssize_t w = write(fd, iov->iov_base, iov->iov_len); + if (w != (ssize_t) iov->iov_len) { if (w < 0) return w; return result + w; diff --git a/liblog/log_read.c b/liblog/log_read.c new file mode 100644 index 0000000..47aa711 --- /dev/null +++ b/liblog/log_read.c @@ -0,0 +1,665 @@ +/* +** 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 <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <cutils/list.h> +#include <log/log.h> +#include <log/logger.h> + +#include <sys/ioctl.h> + +#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)) { + 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) +{ + 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; + + 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; + } + + 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); + } + + 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 19d7166..6b35a0f 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.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. @@ -31,7 +31,12 @@ #include <log/logd.h> #include <log/log.h> -#define LOG_BUF_SIZE 1024 +#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. @@ -240,7 +245,7 @@ int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fm } void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) + const char *fmt, ...) { char buf[LOG_BUF_SIZE]; diff --git a/liblog/logprint.c b/liblog/logprint.c index 508c825..a7480d5 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -1,6 +1,6 @@ -/* //device/libs/cutils/logprint.c +/* ** -** Copyright 2006, The Android Open Source Project +** Copyright 2006-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. @@ -377,8 +377,13 @@ int android_log_processLogBuffer(struct logger_entry *buf, int msgEnd = -1; int i; + char *msg = buf->msg; + struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; + if (buf2->hdr_size) { + msg = ((char *)buf2) + buf2->hdr_size; + } for (i = 1; i < buf->len; i++) { - if (buf->msg[i] == '\0') { + if (msg[i] == '\0') { if (msgStart == -1) { msgStart = i + 1; } else { @@ -395,12 +400,12 @@ int android_log_processLogBuffer(struct logger_entry *buf, if (msgEnd == -1) { // incoming message not null-terminated; force it msgEnd = buf->len - 1; - buf->msg[msgEnd] = '\0'; + msg[msgEnd] = '\0'; } - entry->priority = buf->msg[0]; - entry->tag = buf->msg + 1; - entry->message = buf->msg + msgStart; + entry->priority = msg[0]; + entry->tag = msg + 1; + entry->message = msg + msgStart; entry->messageLen = msgEnd - msgStart; return 0; @@ -614,6 +619,10 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, * Pull the tag out. */ eventData = (const unsigned char*) buf->msg; + struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf; + if (buf2->hdr_size) { + eventData = ((unsigned char *)buf2) + buf2->hdr_size; + } inCount = buf->len; if (inCount < 4) return -1; diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk new file mode 100644 index 0000000..db06cf7 --- /dev/null +++ b/liblog/tests/Android.mk @@ -0,0 +1,77 @@ +# +# Copyright (C) 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------------- +# Benchmarks. +# ----------------------------------------------------------------------------- + +test_module_prefix := liblog- +test_tags := tests + +benchmark_c_flags := \ + -Ibionic/tests \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + -std=gnu++11 + +benchmark_src_files := \ + benchmark_main.cpp \ + liblog_benchmark.cpp \ + +# Build benchmarks for the device. Run with: +# adb shell liblog-benchmarks +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) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +test_src_files := \ + liblog_test.cpp \ + +# Build tests for the device (with .so). Run with: +# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests +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_LDLIBS := -lpthread +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h new file mode 100644 index 0000000..7f96e6d --- /dev/null +++ b/liblog/tests/benchmark.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2012-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 <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <vector> + +#ifndef BIONIC_BENCHMARK_H_ +#define BIONIC_BENCHMARK_H_ + +namespace testing { + +class Benchmark; +template <typename T> class BenchmarkWantsArg; +template <typename T> class BenchmarkWithArg; + +void BenchmarkRegister(Benchmark* bm); +int PrettyPrintInt(char* str, int len, unsigned int arg); + +class Benchmark { + public: + Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) { + BenchmarkRegister(this); + } + Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {} + + virtual ~Benchmark() { + free(name_); + } + + const char* Name() { return name_; } + virtual const char* ArgName() { return NULL; } + virtual void RunFn(int iterations) { fn_(iterations); } + + protected: + char* name_; + + private: + void (*fn_)(int); +}; + +template <typename T> +class BenchmarkWantsArgBase : public Benchmark { + public: + BenchmarkWantsArgBase(const char* name, void (*fn)(int, T)) : Benchmark(name) { + fn_arg_ = fn; + } + + BenchmarkWantsArgBase<T>* Arg(const char* arg_name, T arg) { + BenchmarkRegister(new BenchmarkWithArg<T>(name_, fn_arg_, arg_name, arg)); + return this; + } + + protected: + virtual void RunFn(int) { printf("can't run arg benchmark %s without arg\n", Name()); } + void (*fn_arg_)(int, T); +}; + +template <typename T> +class BenchmarkWithArg : public BenchmarkWantsArg<T> { + public: + BenchmarkWithArg(const char* name, void (*fn)(int, T), const char* arg_name, T arg) : + BenchmarkWantsArg<T>(name, fn), arg_(arg) { + arg_name_ = strdup(arg_name); + } + + virtual ~BenchmarkWithArg() { + free(arg_name_); + } + + virtual const char* ArgName() { return arg_name_; } + + protected: + virtual void RunFn(int iterations) { BenchmarkWantsArg<T>::fn_arg_(iterations, arg_); } + + private: + T arg_; + char* arg_name_; +}; + +template <typename T> +class BenchmarkWantsArg : public BenchmarkWantsArgBase<T> { + public: + BenchmarkWantsArg<T>(const char* name, void (*fn)(int, T)) : + BenchmarkWantsArgBase<T>(name, fn) { } +}; + +template <> +class BenchmarkWantsArg<int> : public BenchmarkWantsArgBase<int> { + public: + BenchmarkWantsArg<int>(const char* name, void (*fn)(int, int)) : + BenchmarkWantsArgBase<int>(name, fn) { } + + BenchmarkWantsArg<int>* Arg(int arg) { + char arg_name[100]; + PrettyPrintInt(arg_name, sizeof(arg_name), arg); + BenchmarkRegister(new BenchmarkWithArg<int>(name_, fn_arg_, arg_name, arg)); + return this; + } +}; + +static inline Benchmark* BenchmarkFactory(const char* name, void (*fn)(int)) { + return new Benchmark(name, fn); +} + +template <typename T> +static inline BenchmarkWantsArg<T>* BenchmarkFactory(const char* name, void (*fn)(int, T)) { + return new BenchmarkWantsArg<T>(name, fn); +} + +} // namespace testing + +template <typename T> +static inline void BenchmarkAddArg(::testing::Benchmark* b, const char* name, T arg) { + ::testing::BenchmarkWantsArg<T>* ba; + ba = static_cast< ::testing::BenchmarkWantsArg<T>* >(b); + ba->Arg(name, arg); +} + +void SetBenchmarkBytesProcessed(uint64_t); +void ResetBenchmarkTiming(void); +void StopBenchmarkTiming(void); +void StartBenchmarkTiming(void); +void StartBenchmarkTiming(uint64_t); +void StopBenchmarkTiming(uint64_t); + +#define BENCHMARK(f) \ + static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = \ + (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f) + +#endif // BIONIC_BENCHMARK_H_ diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp new file mode 100644 index 0000000..02df460 --- /dev/null +++ b/liblog/tests/benchmark_main.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012-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 <benchmark.h> + +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> +#include <map> +#include <vector> + +static uint64_t gBytesProcessed; +static uint64_t gBenchmarkTotalTimeNs; +static uint64_t gBenchmarkTotalTimeNsSquared; +static uint64_t gBenchmarkNum; +static uint64_t gBenchmarkStartTimeNs; + +typedef std::vector< ::testing::Benchmark* > BenchmarkList; +static BenchmarkList* gBenchmarks; + +static int Round(int n) { + int base = 1; + while (base*10 < n) { + base *= 10; + } + if (n < 2*base) { + return 2*base; + } + if (n < 5*base) { + return 5*base; + } + return 10*base; +} + +static uint64_t NanoTime() { + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t); + return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec; +} + +namespace testing { + +int PrettyPrintInt(char* str, int len, unsigned int arg) +{ + if (arg >= (1<<30) && arg % (1<<30) == 0) { + return snprintf(str, len, "%uGi", arg/(1<<30)); + } else if (arg >= (1<<20) && arg % (1<<20) == 0) { + return snprintf(str, len, "%uMi", arg/(1<<20)); + } else if (arg >= (1<<10) && arg % (1<<10) == 0) { + return snprintf(str, len, "%uKi", arg/(1<<10)); + } else if (arg >= 1000000000 && arg % 1000000000 == 0) { + return snprintf(str, len, "%uG", arg/1000000000); + } else if (arg >= 1000000 && arg % 1000000 == 0) { + return snprintf(str, len, "%uM", arg/1000000); + } else if (arg >= 1000 && arg % 1000 == 0) { + return snprintf(str, len, "%uK", arg/1000); + } else { + return snprintf(str, len, "%u", arg); + } +} + +bool ShouldRun(Benchmark* b, int argc, char* argv[]) { + if (argc == 1) { + return true; // With no arguments, we run all benchmarks. + } + // Otherwise, we interpret each argument as a regular expression and + // see if any of our benchmarks match. + for (int i = 1; i < argc; i++) { + regex_t re; + if (regcomp(&re, argv[i], 0) != 0) { + fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]); + exit(EXIT_FAILURE); + } + int match = regexec(&re, b->Name(), 0, NULL, 0); + regfree(&re); + if (match != REG_NOMATCH) { + return true; + } + } + return false; +} + +void BenchmarkRegister(Benchmark* b) { + if (gBenchmarks == NULL) { + gBenchmarks = new BenchmarkList; + } + gBenchmarks->push_back(b); +} + +void RunRepeatedly(Benchmark* b, int iterations) { + gBytesProcessed = 0; + ResetBenchmarkTiming(); + uint64_t StartTimeNs = NanoTime(); + b->RunFn(iterations); + // Catch us if we fail to log anything. + if ((gBenchmarkTotalTimeNs == 0) + && (StartTimeNs != 0) + && (gBenchmarkStartTimeNs == 0)) { + gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs; + } +} + +void Run(Benchmark* b) { + // run once in case it's expensive + unsigned iterations = 1; + uint64_t s = NanoTime(); + RunRepeatedly(b, iterations); + s = NanoTime() - s; + while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) { + unsigned last = iterations; + if (gBenchmarkTotalTimeNs/iterations == 0) { + iterations = 1e9; + } else { + iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations); + } + iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last)); + iterations = Round(iterations); + s = NanoTime(); + RunRepeatedly(b, iterations); + s = NanoTime() - s; + } + + char throughput[100]; + throughput[0] = '\0'; + if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) { + double mib_processed = static_cast<double>(gBytesProcessed)/1e6; + double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9; + snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds); + } + + char full_name[100]; + snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(), + b->ArgName() ? "/" : "", + b->ArgName() ? b->ArgName() : ""); + + uint64_t mean = gBenchmarkTotalTimeNs / iterations; + uint64_t sdev = 0; + if (gBenchmarkNum == iterations) { + mean = gBenchmarkTotalTimeNs / gBenchmarkNum; + uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum + - (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs); + sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5; + } + if (mean > (10000 * sdev)) { + printf("%-25s %10llu %10llu%s\n", full_name, + static_cast<uint64_t>(iterations), mean, throughput); + } else { + printf("%-25s %10llu %10llu(\317\203%llu)%s\n", full_name, + static_cast<uint64_t>(iterations), mean, sdev, throughput); + } + fflush(stdout); +} + +} // namespace testing + +void SetBenchmarkBytesProcessed(uint64_t x) { + gBytesProcessed = x; +} + +void ResetBenchmarkTiming() { + gBenchmarkStartTimeNs = 0; + gBenchmarkTotalTimeNs = 0; + gBenchmarkTotalTimeNsSquared = 0; + gBenchmarkNum = 0; +} + +void StopBenchmarkTiming(void) { + if (gBenchmarkStartTimeNs != 0) { + int64_t diff = NanoTime() - gBenchmarkStartTimeNs; + gBenchmarkTotalTimeNs += diff; + gBenchmarkTotalTimeNsSquared += diff * diff; + ++gBenchmarkNum; + } + gBenchmarkStartTimeNs = 0; +} + +void StartBenchmarkTiming(void) { + if (gBenchmarkStartTimeNs == 0) { + gBenchmarkStartTimeNs = NanoTime(); + } +} + +void StopBenchmarkTiming(uint64_t NanoTime) { + if (gBenchmarkStartTimeNs != 0) { + int64_t diff = NanoTime - gBenchmarkStartTimeNs; + gBenchmarkTotalTimeNs += diff; + gBenchmarkTotalTimeNsSquared += diff * diff; + if (NanoTime != 0) { + ++gBenchmarkNum; + } + } + gBenchmarkStartTimeNs = 0; +} + +void StartBenchmarkTiming(uint64_t NanoTime) { + if (gBenchmarkStartTimeNs == 0) { + gBenchmarkStartTimeNs = NanoTime; + } +} + +int main(int argc, char* argv[]) { + if (gBenchmarks->empty()) { + fprintf(stderr, "No benchmarks registered!\n"); + exit(EXIT_FAILURE); + } + + bool need_header = true; + for (auto b : *gBenchmarks) { + if (ShouldRun(b, argc, argv)) { + if (need_header) { + printf("%-25s %10s %10s\n", "", "iterations", "ns/op"); + fflush(stdout); + need_header = false; + } + Run(b); + } + } + + if (need_header) { + fprintf(stderr, "No matching benchmarks!\n"); + fprintf(stderr, "Available benchmarks:\n"); + for (auto b : *gBenchmarks) { + fprintf(stderr, " %s\n", b->Name()); + } + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp new file mode 100644 index 0000000..19406fb --- /dev/null +++ b/liblog/tests/liblog_benchmark.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 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. + */ + +#include <sys/socket.h> +#include <cutils/sockets.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +#include "benchmark.h" + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are benchmarking, or using this in the emergency +// signal to stuff a terminating code, we do NOT want to introduce +// a syscall or usleep on EAGAIN retry. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +/* + * Measure the fastest rate we can reliabley stuff print messages into + * the log at high pressure. Expect this to be less than double the process + * wakeup time (2ms?) + */ +static void BM_log_maximum_retry(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + LOG_FAILURE_RETRY( + __android_log_print(ANDROID_LOG_INFO, + "BM_log_maximum_retry", "%d", i)); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_log_maximum_retry); + +/* + * Measure the fastest rate we can stuff print messages into the log + * at high pressure. Expect this to be less than double the process wakeup + * time (2ms?) + */ +static void BM_log_maximum(int iters) { + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%d", i); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_log_maximum); + +/* + * Measure the time it takes to submit the android logging call using + * discrete acquisition under light load. Expect this to be a pair of + * syscall periods (2us). + */ +static void BM_clock_overhead(int iters) { + for (int i = 0; i < iters; ++i) { + StartBenchmarkTiming(); + StopBenchmarkTiming(); + } +} +BENCHMARK(BM_clock_overhead); + +/* + * Measure the time it takes to submit the android logging call using + * discrete acquisition under light load. Expect this to be a dozen or so + * syscall periods (40us). + */ +static void BM_log_overhead(int iters) { + for (int i = 0; i < iters; ++i) { + StartBenchmarkTiming(); + __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i); + StopBenchmarkTiming(); + usleep(1000); + } +} +BENCHMARK(BM_log_overhead); + +static void caught_latency(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A5AA5ULL; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +static unsigned long long caught_convert(char *cp) +{ + unsigned long long l = cp[0] & 0xFF; + l |= (unsigned long long) (cp[1] & 0xFF) << 8; + l |= (unsigned long long) (cp[2] & 0xFF) << 16; + l |= (unsigned long long) (cp[3] & 0xFF) << 24; + l |= (unsigned long long) (cp[4] & 0xFF) << 32; + l |= (unsigned long long) (cp[5] & 0xFF) << 40; + l |= (unsigned long long) (cp[6] & 0xFF) << 48; + l |= (unsigned long long) (cp[7] & 0xFF) << 56; + return l; +} + +static const int alarm_time = 3; + +/* + * Measure the time it takes for the logd posting call to acquire the + * timestamp to place into the internal record. Expect this to be less than + * 4 syscalls (3us). + */ +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); + + if (!logger_list) { + fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + signal(SIGALRM, caught_latency); + alarm(alarm_time); + + for (int j = 0, i = 0; i < iters && j < 10*iters; ++i, ++j) { + log_time ts; + LOG_FAILURE_RETRY(( + clock_gettime(CLOCK_REALTIME, &ts), + android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)))); + + for (;;) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + alarm(alarm_time); + + if (ret <= 0) { + iters = i; + break; + } + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char* eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + log_time tx(eventData + 4 + 1); + if (ts != tx) { + if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) { + iters = i; + break; + } + continue; + } + + uint64_t start = ts.nsec(); + uint64_t end = log_msg.nsec(); + if (end >= start) { + StartBenchmarkTiming(start); + StopBenchmarkTiming(end); + } else { + --i; + } + break; + } + } + + signal(SIGALRM, SIG_DFL); + alarm(0); + + android_logger_list_free(logger_list); +} +BENCHMARK(BM_log_latency); + +static void caught_delay(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A5AA6ULL; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +/* + * Measure the time it takes for the logd posting call to make it into + * the logs. Expect this to be less than double the process wakeup time (2ms). + */ +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); + + if (!logger_list) { + fprintf(stderr, "Unable to open events log: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + signal(SIGALRM, caught_delay); + alarm(alarm_time); + + StartBenchmarkTiming(); + + for (int i = 0; i < iters; ++i) { + log_time ts(CLOCK_REALTIME); + + LOG_FAILURE_RETRY( + android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + + for (;;) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + alarm(alarm_time); + + if (ret <= 0) { + iters = i; + break; + } + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char* eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + log_time tx(eventData + 4 + 1); + if (ts != tx) { + if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) { + iters = i; + break; + } + continue; + } + + break; + } + } + + signal(SIGALRM, SIG_DFL); + alarm(0); + + StopBenchmarkTiming(); + + android_logger_list_free(logger_list); +} +BENCHMARK(BM_log_delay); diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp new file mode 100644 index 0000000..9ae8f22 --- /dev/null +++ b/liblog/tests/liblog_test.cpp @@ -0,0 +1,319 @@ +/* + * Copyright (C) 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. + */ + +#include <fcntl.h> +#include <signal.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are only using this in the emergency of +// a signal to stuff a terminating code into the logs, we will spin rather +// than try a usleep. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +TEST(liblog, __android_log_buf_print) { + ASSERT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "radio")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "system")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_buf_print", + "main")); + usleep(1000); +} + +TEST(liblog, __android_log_buf_write) { + ASSERT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "radio")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "system")); + usleep(1000); + ASSERT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_buf_write", + "main")); + usleep(1000); +} + +TEST(liblog, __android_log_btwrite) { + int intBuf = 0xDEADBEEF; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_INT, + &intBuf, sizeof(intBuf))); + long long longBuf = 0xDEADBEEFA55A5AA5; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_LONG, + &longBuf, sizeof(longBuf))); + usleep(1000); + char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5"; + ASSERT_LT(0, __android_log_btwrite(0, + EVENT_TYPE_STRING, + Buf, sizeof(Buf) - 1)); + usleep(1000); +} + +static void* ConcurrentPrintFn(void *arg) { + int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, + "TEST__android_log_print", "Concurrent %d", + reinterpret_cast<int>(arg)); + return reinterpret_cast<void*>(ret); +} + +#define NUM_CONCURRENT 64 +#define _concurrent_name(a,n) a##__concurrent##n +#define concurrent_name(a,n) _concurrent_name(a,n) + +TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) { + pthread_t t[NUM_CONCURRENT]; + int i; + for (i=0; i < NUM_CONCURRENT; i++) { + ASSERT_EQ(0, pthread_create(&t[i], NULL, + ConcurrentPrintFn, + reinterpret_cast<void *>(i))); + } + int ret = 0; + for (i=0; i < NUM_CONCURRENT; i++) { + void* result; + ASSERT_EQ(0, pthread_join(t[i], &result)); + if ((0 == ret) && (0 != reinterpret_cast<int>(result))) { + ret = reinterpret_cast<int>(result); + } + } + ASSERT_LT(0, ret); +} + +TEST(liblog, __android_log_btwrite__android_logger_list_read) { + struct logger_list *logger_list; + + pid_t pid = getpid(); + + ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY | O_NDELAY, 1000, pid))); + + log_time ts(CLOCK_MONOTONIC); + + ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + usleep(1000000); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + + log_time tx(eventData + 4 + 1); + if (ts == tx) { + ++count; + } + } + + ASSERT_EQ(1, count); + + android_logger_list_close(logger_list); +} + +static unsigned signaled; +log_time signal_time; + +static void caught_blocking(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + v += getpid() & 0xFFFF; + + ++signaled; + if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) { + clock_gettime(CLOCK_MONOTONIC, &signal_time); + signal_time.tv_sec += 2; + } + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +// Fill in current process user and system time in 10ms increments +static void get_ticks(unsigned long long *uticks, unsigned long long *sticks) +{ + *uticks = *sticks = 0; + + pid_t pid = getpid(); + + char buffer[512]; + snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid); + + FILE *fp = fopen(buffer, "r"); + if (!fp) { + return; + } + + char *cp = fgets(buffer, sizeof(buffer), fp); + fclose(fp); + if (!cp) { + return; + } + + pid_t d; + char s[sizeof(buffer)]; + char c; + long long ll; + unsigned long long ull; + + if (15 != sscanf(buffer, + "%d %s %c %lld %lld %lld %lld %lld %llu %llu %llu %llu %llu %llu %llu ", + &d, s, &c, &ll, &ll, &ll, &ll, &ll, &ull, &ull, &ull, &ull, &ull, + uticks, sticks)) { + *uticks = *sticks = 0; + } +} + +TEST(liblog, android_logger_list_read__cpu) { + struct logger_list *logger_list; + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (logger_list = android_logger_list_open( + LOG_ID_EVENTS, O_RDONLY, 1000, pid))); + + int count = 0; + + int signals = 0; + + unsigned long long uticks_start; + unsigned long long sticks_start; + get_ticks(&uticks_start, &sticks_start); + + const unsigned alarm_time = 10; + + memset(&signal_time, 0, sizeof(signal_time)); + + signal(SIGALRM, caught_blocking); + alarm(alarm_time); + + signaled = 0; + + do { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) { + break; + } + + alarm(alarm_time); + + ++count; + + ASSERT_EQ(log_msg.entry.pid, pid); + + if ((log_msg.entry.len != (4 + 1 + 8)) + || (log_msg.id() != LOG_ID_EVENTS)) { + continue; + } + + char *eventData = log_msg.msg(); + + if (eventData[4] != EVENT_TYPE_LONG) { + continue; + } + + unsigned long long l = eventData[4 + 1 + 0] & 0xFF; + l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8; + l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16; + l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24; + l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32; + l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40; + l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48; + l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56; + + if (l == v) { + ++signals; + break; + } + } while (!signaled || ({log_time t(CLOCK_MONOTONIC); t < signal_time;})); + alarm(0); + signal(SIGALRM, SIG_DFL); + + ASSERT_LT(1, count); + + ASSERT_EQ(1, signals); + + android_logger_list_close(logger_list); + + unsigned long long uticks_end; + unsigned long long sticks_end; + get_ticks(&uticks_end, &sticks_end); + + // Less than 1% in either user or system time, or both + const unsigned long long one_percent_ticks = alarm_time; + unsigned long long user_ticks = uticks_end - uticks_start; + unsigned long long system_ticks = sticks_end - sticks_start; + ASSERT_GT(one_percent_ticks, user_ticks); + ASSERT_GT(one_percent_ticks, system_ticks); + ASSERT_GT(one_percent_ticks, user_ticks + system_ticks); +} + +TEST(liblog, android_logger_get_) { + struct logger_list * logger_list = android_logger_list_alloc(O_WRONLY, 0, 0); + + for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + log_id_t id = static_cast<log_id_t>(i); + const char *name = android_log_id_to_name(id); + if (id != android_name_to_log_id(name)) { + continue; + } + struct logger * logger; + ASSERT_EQ(0, NULL == (logger = android_logger_open(logger_list, id))); + ASSERT_EQ(id, android_logger_get_id(logger)); + ASSERT_LT(0, android_logger_get_log_size(logger)); + ASSERT_LT(0, android_logger_get_log_readable_size(logger)); + ASSERT_LT(0, android_logger_get_log_version(logger)); + } + + android_logger_list_close(logger_list); +} diff --git a/liblog/uio.c b/liblog/uio.c index cfa4cb1..24a6507 100644 --- a/liblog/uio.c +++ b/liblog/uio.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. @@ -24,8 +24,8 @@ int readv( int fd, struct iovec* vecs, int count ) int total = 0; for ( ; count > 0; count--, vecs++ ) { - const char* buf = vecs->iov_base; - int len = vecs->iov_len; + char* buf = vecs->iov_base; + int len = vecs->iov_len; while (len > 0) { int ret = read( fd, buf, len ); @@ -51,8 +51,8 @@ int writev( int fd, const struct iovec* vecs, int count ) int total = 0; for ( ; count > 0; count--, vecs++ ) { - const char* buf = (const char*)vecs->iov_base; - int len = (int)vecs->iov_len; + const char* buf = vecs->iov_base; + int len = vecs->iov_len; while (len > 0) { int ret = write( fd, buf, len ); diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp index 038d87e..0b95a81 100644 --- a/libsysutils/src/FrameworkCommand.cpp +++ b/libsysutils/src/FrameworkCommand.cpp @@ -21,11 +21,14 @@ #include <sysutils/FrameworkCommand.h> +#define UNUSED __attribute__((unused)) + FrameworkCommand::FrameworkCommand(const char *cmd) { mCommand = cmd; } -int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) { +int FrameworkCommand::runCommand(SocketClient *c UNUSED, int argc UNUSED, + char **argv UNUSED) { SLOGW("Command %s has no run handler!", getCommand()); errno = ENOSYS; return -1; diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 02a401d..a5ffda2 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -27,6 +27,8 @@ static const int CMD_BUF_SIZE = 1024; +#define UNUSED __attribute__((unused)) + FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : SocketListener(socketName, true, withSeq) { init(socketName, withSeq); @@ -37,7 +39,7 @@ FrameworkListener::FrameworkListener(const char *socketName) : init(socketName, false); } -void FrameworkListener::init(const char *socketName, bool withSeq) { +void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) { mCommands = new FrameworkCommandCollection(); errorRate = 0; mCommandCount = 0; diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index ae0e077..3625d93 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -71,7 +71,7 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN ret = asprintf(&buf, "%d %s", code, msg); } } - /* Send the zero-terminated message */ + // Send the zero-terminated message if (ret != -1) { ret = sendMsg(buf); free(buf); @@ -79,22 +79,25 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN return ret; } -/** send 3-digit code, null, binary-length, binary data */ +// send 3-digit code, null, binary-length, binary data int SocketClient::sendBinaryMsg(int code, const void *data, int len) { - /* 4 bytes for the code & null + 4 bytes for the len */ + // 4 bytes for the code & null + 4 bytes for the len char buf[8]; - /* Write the code */ + // Write the code snprintf(buf, 4, "%.3d", code); - /* Write the len */ + // Write the len uint32_t tmp = htonl(len); memcpy(buf + 4, &tmp, sizeof(uint32_t)); + struct iovec vec[2]; + vec[0].iov_base = (void *) buf; + vec[0].iov_len = sizeof(buf); + vec[1].iov_base = (void *) data; + vec[1].iov_len = len; + pthread_mutex_lock(&mWriteMutex); - int result = sendDataLocked(buf, sizeof(buf)); - if (result == 0 && len > 0) { - result = sendDataLocked(data, len); - } + int result = sendDataLockedv(vec, (len > 0) ? 2 : 1); pthread_mutex_unlock(&mWriteMutex); return result; @@ -147,33 +150,51 @@ int SocketClient::sendMsg(const char *msg) { } int SocketClient::sendData(const void *data, int len) { + struct iovec vec[1]; + vec[0].iov_base = (void *) data; + vec[0].iov_len = len; pthread_mutex_lock(&mWriteMutex); - int rc = sendDataLocked(data, len); + int rc = sendDataLockedv(vec, 1); pthread_mutex_unlock(&mWriteMutex); return rc; } -int SocketClient::sendDataLocked(const void *data, int len) { - int rc = 0; - const char *p = (const char*) data; - int brtw = len; +int SocketClient::sendDatav(struct iovec *iov, int iovcnt) { + pthread_mutex_lock(&mWriteMutex); + int rc = sendDataLockedv(iov, iovcnt); + pthread_mutex_unlock(&mWriteMutex); + + return rc; +} + +int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) { if (mSocket < 0) { errno = EHOSTUNREACH; return -1; } - if (len == 0) { + if (iovcnt <= 0) { return 0; } - while (brtw > 0) { - rc = send(mSocket, p, brtw, MSG_NOSIGNAL); + int current = 0; + + for (;;) { + ssize_t rc = writev(mSocket, iov + current, iovcnt - current); if (rc > 0) { - p += rc; - brtw -= rc; + size_t written = rc; + while ((current < iovcnt) && (written >= iov[current].iov_len)) { + written -= iov[current].iov_len; + current++; + } + if (current == iovcnt) { + break; + } + iov[current].iov_base = (char *)iov[current].iov_base + written; + iov[current].iov_len -= written; continue; } diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 0361641..5c75206 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2008-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. @@ -29,7 +29,8 @@ #include <sysutils/SocketListener.h> #include <sysutils/SocketClient.h> -#define LOG_NDEBUG 0 +#define CtrlPipe_Shutdown 0 +#define CtrlPipe_Wakeup 1 SocketListener::SocketListener(const char *socketName, bool listen) { init(socketName, -1, listen, false); @@ -103,7 +104,7 @@ int SocketListener::startListener() { } int SocketListener::stopListener() { - char c = 0; + char c = CtrlPipe_Shutdown; int rc; rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1)); @@ -145,7 +146,7 @@ void *SocketListener::threadStart(void *obj) { void SocketListener::runListener() { - SocketClientCollection *pendingList = new SocketClientCollection(); + SocketClientCollection pendingList; while(1) { SocketClientCollection::iterator it; @@ -166,10 +167,12 @@ void SocketListener::runListener() { pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { + // NB: calling out to an other object with mClientsLock held (safe) int fd = (*it)->getSocket(); FD_SET(fd, &read_fds); - if (fd > max) + if (fd > max) { max = fd; + } } pthread_mutex_unlock(&mClientsLock); SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); @@ -182,8 +185,14 @@ void SocketListener::runListener() { } else if (!rc) continue; - if (FD_ISSET(mCtrlPipe[0], &read_fds)) - break; + if (FD_ISSET(mCtrlPipe[0], &read_fds)) { + char c = CtrlPipe_Shutdown; + TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); + if (c == CtrlPipe_Shutdown) { + break; + } + continue; + } if (mListen && FD_ISSET(mSock, &read_fds)) { struct sockaddr addr; socklen_t alen; @@ -205,53 +214,111 @@ void SocketListener::runListener() { } /* Add all active clients to the pending list first */ - pendingList->clear(); + pendingList.clear(); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { - int fd = (*it)->getSocket(); + SocketClient* c = *it; + // NB: calling out to an other object with mClientsLock held (safe) + int fd = c->getSocket(); if (FD_ISSET(fd, &read_fds)) { - pendingList->push_back(*it); + pendingList.push_back(c); + c->incRef(); } } pthread_mutex_unlock(&mClientsLock); /* Process the pending list, since it is owned by the thread, * there is no need to lock it */ - while (!pendingList->empty()) { + while (!pendingList.empty()) { /* Pop the first item from the list */ - it = pendingList->begin(); + it = pendingList.begin(); SocketClient* c = *it; - pendingList->erase(it); - /* Process it, if false is returned and our sockets are - * connection-based, remove and destroy it */ - if (!onDataAvailable(c) && mListen) { - /* Remove the client from our array */ - SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); - pthread_mutex_lock(&mClientsLock); - for (it = mClients->begin(); it != mClients->end(); ++it) { - if (*it == c) { - mClients->erase(it); - break; - } - } - pthread_mutex_unlock(&mClientsLock); - /* Remove our reference to the client */ - c->decRef(); + pendingList.erase(it); + /* Process it, if false is returned, remove from list */ + if (!onDataAvailable(c)) { + release(c, false); } + c->decRef(); } } - delete pendingList; +} + +bool SocketListener::release(SocketClient* c, bool wakeup) { + bool ret = false; + /* if our sockets are connection-based, remove and destroy it */ + if (mListen && c) { + /* Remove the client from our array */ + SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); + pthread_mutex_lock(&mClientsLock); + SocketClientCollection::iterator it; + for (it = mClients->begin(); it != mClients->end(); ++it) { + if (*it == c) { + mClients->erase(it); + ret = true; + break; + } + } + pthread_mutex_unlock(&mClientsLock); + if (ret) { + ret = c->decRef(); + if (wakeup) { + char b = CtrlPipe_Wakeup; + TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &b, 1)); + } + } + } + return ret; } void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { + SocketClientCollection safeList; + + /* Add all active clients to the safe list first */ + safeList.clear(); pthread_mutex_lock(&mClientsLock); SocketClientCollection::iterator i; for (i = mClients->begin(); i != mClients->end(); ++i) { + SocketClient* c = *i; + c->incRef(); + safeList.push_back(c); + } + pthread_mutex_unlock(&mClientsLock); + + while (!safeList.empty()) { + /* Pop the first item from the list */ + i = safeList.begin(); + SocketClient* c = *i; + safeList.erase(i); // broadcasts are unsolicited and should not include a cmd number - if ((*i)->sendMsg(code, msg, addErrno, false)) { + if (c->sendMsg(code, msg, addErrno, false)) { SLOGW("Error sending broadcast (%s)", strerror(errno)); } + c->decRef(); + } +} + +void SocketListener::runOnEachSocket(SocketClientCommand *command) { + SocketClientCollection safeList; + + /* Add all active clients to the safe list first */ + safeList.clear(); + pthread_mutex_lock(&mClientsLock); + SocketClientCollection::iterator i; + + for (i = mClients->begin(); i != mClients->end(); ++i) { + SocketClient* c = *i; + c->incRef(); + safeList.push_back(c); } pthread_mutex_unlock(&mClientsLock); + + while (!safeList.empty()) { + /* Pop the first item from the list */ + i = safeList.begin(); + SocketClient* c = *i; + safeList.erase(i); + command->runSocketCommand(c); + c->decRef(); + } } diff --git a/logcat/Android.mk b/logcat/Android.mk index 7b8eb01..b5e27eb 100644 --- a/logcat/Android.mk +++ b/logcat/Android.mk @@ -1,4 +1,4 @@ -# Copyright 2006 The Android Open Source Project +# Copyright 2006-2014 The Android Open Source Project LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -10,3 +10,5 @@ LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE:= logcat include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 4c6139c..3c33938 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -1,88 +1,52 @@ -// Copyright 2006 The Android Open Source Project - -#include <log/logger.h> -#include <log/logd.h> -#include <log/logprint.h> -#include <log/event_tag_map.h> -#include <cutils/sockets.h> +// Copyright 2006-2014 The Android Open Source Project +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <unistd.h> -#include <fcntl.h> +#include <signal.h> #include <time.h> -#include <errno.h> -#include <assert.h> -#include <ctype.h> +#include <unistd.h> #include <sys/socket.h> #include <sys/stat.h> #include <arpa/inet.h> +#include <cutils/sockets.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/logd.h> +#include <log/logprint.h> +#include <log/event_tag_map.h> + #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 #define DEFAULT_MAX_ROTATED_LOGS 4 static AndroidLogFormat * g_logformat; -static bool g_nonblock = false; -static int g_tail_lines = 0; /* logd prefixes records with a length field */ #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) -#define LOG_FILE_DIR "/dev/log/" - -struct queued_entry_t { - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); - struct logger_entry entry __attribute__((aligned(4))); - }; - queued_entry_t* next; - - queued_entry_t() { - next = NULL; - } -}; - -static int cmp(queued_entry_t* a, queued_entry_t* b) { - int n = a->entry.sec - b->entry.sec; - if (n != 0) { - return n; - } - return a->entry.nsec - b->entry.nsec; -} - struct log_device_t { - char* device; + const char* device; bool binary; - int fd; + struct logger *logger; + struct logger_list *logger_list; bool printed; char label; - queued_entry_t* queue; log_device_t* next; - log_device_t(char* d, bool b, char l) { + log_device_t(const char* d, bool b, char l) { device = d; binary = b; label = l; - queue = NULL; next = NULL; printed = false; } - - void enqueue(queued_entry_t* entry) { - if (this->queue == NULL) { - this->queue = entry; - } else { - queued_entry_t** e = &this->queue; - while (*e && cmp(entry, *e) >= 0) { - e = &((*e)->next); - } - entry->next = *e; - *e = entry; - } - } }; namespace android { @@ -147,17 +111,14 @@ static void rotateLogs() } -void printBinary(struct logger_entry *buf) +void printBinary(struct log_msg *buf) { - size_t size = sizeof(logger_entry) + buf->len; - int ret; - - do { - ret = write(g_outFD, buf, size); - } while (ret < 0 && errno == EINTR); + size_t size = buf->len(); + + TEMP_FAILURE_RETRY(write(g_outFD, buf, size)); } -static void processBuffer(log_device_t* dev, struct logger_entry *buf) +static void processBuffer(log_device_t* dev, struct log_msg *buf) { int bytesWritten = 0; int err; @@ -165,12 +126,14 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf) char binaryMsgBuf[1024]; if (dev->binary) { - err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap, - binaryMsgBuf, sizeof(binaryMsgBuf)); + err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry, + g_eventTagMap, + binaryMsgBuf, + sizeof(binaryMsgBuf)); //printf(">>> pri=%d len=%d msg='%s'\n", // entry.priority, entry.messageLen, entry.message); } else { - err = android_log_processLogBuffer(buf, &entry); + err = android_log_processLogBuffer(&buf->entry_v1, &entry); } if (err < 0) { goto error; @@ -197,7 +160,7 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf) g_outByteCount += bytesWritten; - if (g_logRotateSizeKBytes > 0 + if (g_logRotateSizeKBytes > 0 && (g_outByteCount / 1024) >= g_logRotateSizeKBytes ) { rotateLogs(); @@ -208,20 +171,13 @@ error: return; } -static void chooseFirst(log_device_t* dev, log_device_t** firstdev) { - for (*firstdev = NULL; dev != NULL; dev = dev->next) { - if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) { - *firstdev = dev; - } - } -} - static void maybePrintStart(log_device_t* dev) { if (!dev->printed) { dev->printed = true; if (g_devCount > 1 && !g_printBinary) { char buf[1024]; - snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device); + snprintf(buf, sizeof(buf), "--------- beginning of %s\n", + dev->device); if (write(g_outFD, buf, strlen(buf)) < 0) { perror("output error"); exit(-1); @@ -230,145 +186,6 @@ static void maybePrintStart(log_device_t* dev) { } } -static void skipNextEntry(log_device_t* dev) { - maybePrintStart(dev); - queued_entry_t* entry = dev->queue; - dev->queue = entry->next; - delete entry; -} - -static void printNextEntry(log_device_t* dev) { - maybePrintStart(dev); - if (g_printBinary) { - printBinary(&dev->queue->entry); - } else { - processBuffer(dev, &dev->queue->entry); - } - skipNextEntry(dev); -} - -static void readLogLines(log_device_t* devices) -{ - log_device_t* dev; - int max = 0; - int ret; - int queued_lines = 0; - bool sleep = false; - - int result; - fd_set readset; - - for (dev=devices; dev; dev = dev->next) { - if (dev->fd > max) { - max = dev->fd; - } - } - - while (1) { - do { - timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR. - FD_ZERO(&readset); - for (dev=devices; dev; dev = dev->next) { - FD_SET(dev->fd, &readset); - } - result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); - } while (result == -1 && errno == EINTR); - - if (result >= 0) { - for (dev=devices; dev; dev = dev->next) { - if (FD_ISSET(dev->fd, &readset)) { - queued_entry_t* entry = new queued_entry_t(); - /* NOTE: driver guarantees we read exactly one full entry */ - ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); - if (ret < 0) { - if (errno == EINTR) { - delete entry; - goto next; - } - if (errno == EAGAIN) { - delete entry; - break; - } - perror("logcat read"); - exit(EXIT_FAILURE); - } - else if (!ret) { - fprintf(stderr, "read: Unexpected EOF!\n"); - exit(EXIT_FAILURE); - } - else if (entry->entry.len != ret - sizeof(struct logger_entry)) { - fprintf(stderr, "read: unexpected length. Expected %d, got %d\n", - entry->entry.len, ret - (int) sizeof(struct logger_entry)); - exit(EXIT_FAILURE); - } - - entry->entry.msg[entry->entry.len] = '\0'; - - dev->enqueue(entry); - ++queued_lines; - } - } - - if (result == 0) { - // we did our short timeout trick and there's nothing new - // print everything we have and wait for more data - sleep = true; - while (true) { - chooseFirst(devices, &dev); - if (dev == NULL) { - break; - } - if (g_tail_lines == 0 || queued_lines <= g_tail_lines) { - printNextEntry(dev); - } else { - skipNextEntry(dev); - } - --queued_lines; - } - - // the caller requested to just dump the log and exit - if (g_nonblock) { - return; - } - } else { - // print all that aren't the last in their list - sleep = false; - while (g_tail_lines == 0 || queued_lines > g_tail_lines) { - chooseFirst(devices, &dev); - if (dev == NULL || dev->queue->next == NULL) { - break; - } - if (g_tail_lines == 0) { - printNextEntry(dev); - } else { - skipNextEntry(dev); - } - --queued_lines; - } - } - } -next: - ; - } -} - -static int clearLog(int logfd) -{ - return ioctl(logfd, LOGGER_FLUSH_LOG); -} - -/* returns the total size of the log's ring buffer */ -static int getLogSize(int logfd) -{ - return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE); -} - -/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */ -static int getLogReadableSize(int logfd) -{ - return ioctl(logfd, LOGGER_GET_LOG_LEN); -} - static void setupOutput() { @@ -406,6 +223,7 @@ static void show_help(const char *cmd) " -c clear (flush) the entire log and exit\n" " -d dump the log and then exit (don't block)\n" " -t <count> print only the most recent <count> lines (implies -d)\n" + " -T <count> print only the most recent <count> lines (does not imply -d)\n" " -g get the size of the log's ring buffer and exit\n" " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n" " or 'events'. Multiple -b parameters are allowed and the\n" @@ -465,6 +283,10 @@ int main(int argc, char **argv) log_device_t* devices = NULL; log_device_t* dev; bool needBinary = false; + struct logger_list *logger_list; + int tail_lines = 0; + + signal(SIGPIPE, exit); g_logformat = android_log_format_new(); @@ -481,14 +303,14 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B"); + ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B"); if (ret < 0) { break; } switch(ret) { - case 's': + case 's': // default to all silent android_log_addFilterRule(g_logformat, "*:s"); break; @@ -499,12 +321,14 @@ int main(int argc, char **argv) break; case 'd': - g_nonblock = true; + mode = O_RDONLY | O_NDELAY; break; case 't': - g_nonblock = true; - g_tail_lines = atoi(optarg); + mode = O_RDONLY | O_NDELAY; + /* FALLTHRU */ + case 'T': + tail_lines = atoi(optarg); break; case 'g': @@ -512,10 +336,6 @@ int main(int argc, char **argv) break; case 'b': { - char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); - strcpy(buf, LOG_FILE_DIR); - strcat(buf, optarg); - bool binary = strcmp(optarg, "events") == 0; if (binary) { needBinary = true; @@ -526,9 +346,9 @@ int main(int argc, char **argv) while (dev->next) { dev = dev->next; } - dev->next = new log_device_t(buf, binary, optarg[0]); + dev->next = new log_device_t(optarg, binary, optarg[0]); } else { - devices = new log_device_t(buf, binary, optarg[0]); + devices = new log_device_t(optarg, binary, optarg[0]); } android::g_devCount++; } @@ -546,8 +366,8 @@ int main(int argc, char **argv) break; case 'r': - if (optarg == NULL) { - android::g_logRotateSizeKBytes + if (optarg == NULL) { + android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { long logRotateSize; @@ -659,19 +479,15 @@ int main(int argc, char **argv) } if (!devices) { - devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm'); + devices = new log_device_t("main", false, 'm'); android::g_devCount = 1; - int accessmode = - (mode & O_RDONLY) ? R_OK : 0 - | (mode & O_WRONLY) ? W_OK : 0; - // only add this if it's available - if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) { - devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's'); + if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { + devices->next = new log_device_t("system", false, 's'); android::g_devCount++; } } - if (android::g_logRotateSizeKBytes != 0 + if (android::g_logRotateSizeKBytes != 0 && android::g_outputFileName == NULL ) { fprintf(stderr,"-r requires -f as well\n"); @@ -688,7 +504,7 @@ int main(int argc, char **argv) err = setLogFormat(logFormat); if (err < 0) { - fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", + fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", logFormat); } } @@ -707,8 +523,8 @@ int main(int argc, char **argv) if (env_tags_orig != NULL) { err = android_log_addFilterString(g_logformat, env_tags_orig); - if (err < 0) { - fprintf(stderr, "Invalid filter expression in" + if (err < 0) { + fprintf(stderr, "Invalid filter expression in" " ANDROID_LOG_TAGS\n"); android::show_help(argv[0]); exit(-1); @@ -719,7 +535,7 @@ int main(int argc, char **argv) for (int i = optind ; i < argc ; i++) { err = android_log_addFilterString(g_logformat, argv[i]); - if (err < 0) { + if (err < 0) { fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]); android::show_help(argv[0]); exit(-1); @@ -728,19 +544,21 @@ int main(int argc, char **argv) } dev = devices; + logger_list = android_logger_list_alloc(mode, tail_lines, 0); while (dev) { - dev->fd = open(dev->device, mode); - if (dev->fd < 0) { - fprintf(stderr, "Unable to open log device '%s': %s\n", - dev->device, strerror(errno)); + dev->logger_list = logger_list; + dev->logger = android_logger_open(logger_list, + android_name_to_log_id(dev->device)); + if (!dev->logger) { + fprintf(stderr, "Unable to open log device '%s'\n", dev->device); exit(EXIT_FAILURE); } if (clearLog) { int ret; - ret = android::clearLog(dev->fd); + ret = android_logger_clear(dev->logger); if (ret) { - perror("ioctl"); + perror("clearLog"); exit(EXIT_FAILURE); } } @@ -748,15 +566,15 @@ int main(int argc, char **argv) if (getLogSize) { int size, readable; - size = android::getLogSize(dev->fd); + size = android_logger_get_log_size(dev->logger); if (size < 0) { - perror("ioctl"); + perror("getLogSize"); exit(EXIT_FAILURE); } - readable = android::getLogReadableSize(dev->fd); + readable = android_logger_get_log_readable_size(dev->logger); if (readable < 0) { - perror("ioctl"); + perror("getLogReadableSize"); exit(EXIT_FAILURE); } @@ -783,7 +601,51 @@ int main(int argc, char **argv) if (needBinary) android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); - android::readLogLines(devices); + while (1) { + struct log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + + if (ret == 0) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + + if (ret < 0) { + if (ret == -EAGAIN) { + break; + } + + if (ret == -EIO) { + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + if (ret == -EINVAL) { + fprintf(stderr, "read: unexpected length.\n"); + exit(EXIT_FAILURE); + } + perror("logcat read"); + exit(EXIT_FAILURE); + } + + for(dev = devices; dev; dev = dev->next) { + if (android_name_to_log_id(dev->device) == log_msg.id()) { + break; + } + } + if (!dev) { + fprintf(stderr, "read: Unexpected log ID!\n"); + exit(EXIT_FAILURE); + } + + android::maybePrintStart(dev); + if (android::g_printBinary) { + android::printBinary(&log_msg); + } else { + android::processBuffer(dev, &log_msg); + } + } + + android_logger_list_free(logger_list); return 0; } diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk new file mode 100644 index 0000000..bdaec14 --- /dev/null +++ b/logcat/tests/Android.mk @@ -0,0 +1,46 @@ +# +# Copyright (C) 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. +# + +LOCAL_PATH := $(call my-dir) + +# ----------------------------------------------------------------------------- +# Unit tests. +# ----------------------------------------------------------------------------- + +test_module := logcat-unit-tests +test_tags := tests + +test_c_flags := \ + -fstack-protector-all \ + -g \ + -Wall -Wextra \ + -Werror \ + -fno-builtin \ + +test_src_files := \ + logcat_test.cpp \ + +# Build tests for the device (with .so). Run with: +# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests +include $(CLEAR_VARS) +LOCAL_MODULE := $(test_module) +LOCAL_MODULE_TAGS := $(test_tags) +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_CFLAGS += $(test_c_flags) +LOCAL_LDLIBS := -lpthread +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := $(test_src_files) +include $(BUILD_NATIVE_TEST) diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp new file mode 100644 index 0000000..f963a3a --- /dev/null +++ b/logcat/tests/logcat_test.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 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. + */ + +#include <signal.h> +#include <stdio.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <log/logger.h> +#include <log/log_read.h> + +// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and +// non-syscall libs. Since we are only using this in the emergency of +// a signal to stuff a terminating code into the logs, we will spin rather +// than try a usleep. +#define LOG_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (((_rc == -1) \ + && ((errno == EINTR) \ + || (errno == EAGAIN))) \ + || (_rc == -EINTR) \ + || (_rc == -EAGAIN)); \ + _rc; }) + +static const char begin[] = "--------- beginning of "; + +TEST(logcat, sorted_order) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + class timestamp { + private: + int month; + int day; + int hour; + int minute; + int second; + int millisecond; + bool ok; + + public: + void init(const char *buffer) + { + ok = false; + if (buffer != NULL) { + ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", + &month, &day, &hour, &minute, &second, &millisecond) == 6; + } + } + + timestamp(const char *buffer) + { + init(buffer); + } + + bool operator< (timestamp &T) + { + return !ok || !T.ok + || (month < T.month) + || ((month == T.month) + && ((day < T.day) + || ((day == T.day) + && ((hour < T.hour) + || ((hour == T.hour) + && ((minute < T.minute) + || ((minute == T.minute) + && ((second < T.second) + || ((second == T.second) + && (millisecond < T.millisecond)))))))))); + } + + bool valid(void) + { + return ok; + } + } last(NULL); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + continue; + } + if (!last.valid()) { + last.init(buffer); + } + timestamp next(buffer); + ASSERT_EQ(0, next < last); + if (next.valid()) { + last.init(buffer); + } + ++count; + } + + pclose(fp); + + ASSERT_LT(100, count); +} + +TEST(logcat, buckets) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b radio -b events -b system -b main -d 2>/dev/null", + "r"))); + + char buffer[5120]; + + int ids = 0; + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(begin, buffer, sizeof(begin) - 1)) { + while (char *cp = strrchr(buffer, '\n')) { + *cp = '\0'; + } + log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1); + ids |= 1 << id; + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(15, ids); + + ASSERT_EQ(4, count); +} + +TEST(logcat, tail_3) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(3, count); +} + +TEST(logcat, tail_10) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(10, count); +} + +TEST(logcat, tail_100) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(100, count); +} + +TEST(logcat, tail_1000) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + if ((buffer[0] == '[') && (buffer[1] == ' ') + && isdigit(buffer[2]) && isdigit(buffer[3]) + && (buffer[4] == '-')) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(1000, count); +} + +TEST(logcat, End_to_End) { + pid_t pid = getpid(); + + log_time ts(CLOCK_MONOTONIC); + + ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); + + FILE *fp; + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b events -t 100 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + int p; + unsigned long long t; + + if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) + || (p != pid)) { + continue; + } + + log_time tx((const char *) &t); + if (ts == tx) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(1, count); +} + +TEST(logcat, get_) { + FILE *fp; + + ASSERT_EQ(0, NULL == (fp = popen( + "logcat -b radio -b events -b system -b main -g 2>/dev/null", + "r"))); + + char buffer[5120]; + + int count = 0; + + while (fgets(buffer, sizeof(buffer), fp)) { + int size, consumed, max, payload; + + size = consumed = max = payload = 0; + if ((4 == sscanf(buffer, "%*s ring buffer is %dKb (%dKb consumed)," + " max entry is %db, max payload is %db", + &size, &consumed, &max, &payload)) + && ((size * 3) >= consumed) + && ((size * 1024) > max) + && (max > payload)) { + ++count; + } + } + + pclose(fp); + + ASSERT_EQ(4, count); +} + +static void caught_blocking(int signum) +{ + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking) { + FILE *fp; + unsigned long long v = 0xDEADBEEFA55A0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + alarm(2); + + ++count; + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + ++signals; + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + // Generate SIGPIPE + fclose(fp); + caught_blocking(0); + + pclose(fp); + + ASSERT_LT(10, count); + + ASSERT_EQ(1, signals); +} + +static void caught_blocking_tail(int signum) +{ + unsigned long long v = 0xA55ADEADBEEF0000ULL; + + v += getpid() & 0xFFFF; + + LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); +} + +TEST(logcat, blocking_tail) { + FILE *fp; + unsigned long long v = 0xA55ADEADBEEF0000ULL; + + pid_t pid = getpid(); + + v += pid & 0xFFFF; + + ASSERT_EQ(0, NULL == (fp = popen( + "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" + " logcat -b events -T 5 2>&1", + "r"))); + + char buffer[5120]; + + int count = 0; + + int signals = 0; + + signal(SIGALRM, caught_blocking_tail); + alarm(2); + while (fgets(buffer, sizeof(buffer), fp)) { + alarm(2); + + ++count; + + if (!strncmp(buffer, "DONE", 4)) { + break; + } + + int p; + unsigned long long l; + + if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) + || (p != pid)) { + continue; + } + + if (l == v) { + if (count >= 5) { + ++signals; + } + break; + } + } + alarm(0); + signal(SIGALRM, SIG_DFL); + + /* Generate SIGPIPE */ + fclose(fp); + caught_blocking_tail(0); + + pclose(fp); + + ASSERT_LT(5, count); + + ASSERT_EQ(1, signals); +} |