summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--adb/Android.mk5
-rw-r--r--adb/SERVICES.TXT5
-rw-r--r--adb/adb.h2
-rw-r--r--adb/log_service.c92
-rw-r--r--adb/services.c2
-rw-r--r--debuggerd/tombstone.cpp113
-rw-r--r--include/cutils/list.h24
-rw-r--r--include/log/log.h23
-rw-r--r--include/log/log_read.h79
-rw-r--r--include/log/logger.h131
-rw-r--r--include/log/uio.h6
-rw-r--r--include/sysutils/SocketClient.h29
-rw-r--r--include/sysutils/SocketClientCommand.h (renamed from libcutils/list.c)30
-rw-r--r--include/sysutils/SocketListener.h8
-rw-r--r--libcutils/Android.mk1
-rw-r--r--libcutils/android_reboot.c4
-rw-r--r--libcutils/iosched_policy.c9
-rw-r--r--libcutils/socket_local_client.c4
-rw-r--r--libcutils/str_parms.c13
-rw-r--r--liblog/Android.mk9
-rw-r--r--liblog/NOTICE2
-rw-r--r--liblog/README134
-rw-r--r--liblog/fake_log_device.c10
-rw-r--r--liblog/log_read.c665
-rw-r--r--liblog/logd_write.c11
-rw-r--r--liblog/logprint.c23
-rw-r--r--liblog/tests/Android.mk77
-rw-r--r--liblog/tests/benchmark.h147
-rw-r--r--liblog/tests/benchmark_main.cpp245
-rw-r--r--liblog/tests/liblog_benchmark.cpp268
-rw-r--r--liblog/tests/liblog_test.cpp319
-rw-r--r--liblog/uio.c10
-rw-r--r--libsysutils/src/FrameworkCommand.cpp5
-rw-r--r--libsysutils/src/FrameworkListener.cpp4
-rw-r--r--libsysutils/src/SocketClient.cpp59
-rw-r--r--libsysutils/src/SocketListener.cpp127
-rw-r--r--logcat/Android.mk4
-rw-r--r--logcat/logcat.cpp362
-rw-r--r--logcat/tests/Android.mk46
-rw-r--r--logcat/tests/logcat_test.cpp443
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:
diff --git a/adb/adb.h b/adb/adb.h
index c85b02a..6f5c93c 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -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);
+}