Merge "Switch arm libcorkscrew over to <sys/ucontext.h>."
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 @@
 
 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 @@
 	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 @@
     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 @@
 } 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 1c9db0d..5b63a43 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -368,8 +368,6 @@
         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..11e9af5 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 @@
 // 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 @@
     // 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 @@
     }
 
     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 @@
     //
     // 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 @@
     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 {
+    // 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;
+      }
+
       _LOG(log, 0, "%s.%03d %5d %5d %c %-8s: %s\n",
-           timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, prioChar, tag, msg);
-    }
+         timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
+         prioChar, tag, msg);
+
+    } while ((msg = nl));
   }
 
-  if (tailOnly) {
-    int i;
-
-    // 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
-    }
-
-    for (i = 0; i < shortLogCount; i++) {
-      _LOG(log, 0, "%s\n", shortLog[shortLogNext]);
-      shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
-    }
-  }
-
-  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) {
@@ -638,30 +625,24 @@
     dump_fault_addr(log, tid, signal);
   }
 
-  BacktraceMap* map = NULL;
-  UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid));
+  UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
+  UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
   if (backtrace->Unwind(0)) {
-    // Grab the map that was created and share it with the siblings.
-    map = backtrace->TakeMapOwnership();
-
     dump_abort_message(backtrace.get(), log, abort_msg_address);
     dump_thread(backtrace.get(), log, SCOPE_AT_FAULT, total_sleep_time_usec);
   }
 
   if (want_logs) {
-    dump_logs(log, pid, true);
+    dump_logs(log, pid, 5);
   }
 
   bool detach_failed = false;
   if (dump_sibling_threads) {
-    detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map);
+    detach_failed = dump_sibling_thread_report(log, pid, tid, total_sleep_time_usec, map.get());
   }
 
-  // Destroy the BacktraceMap object.
-  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/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index f0fb0cd..bd4134c 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -64,10 +64,6 @@
   // Find the map associated with the given pc.
   virtual const backtrace_map_t* FindMap(uintptr_t pc);
 
-  // Take ownership of the BacktraceMap object associated with the backtrace.
-  // If this is called, the caller must handle deleting the object themselves.
-  virtual BacktraceMap* TakeMapOwnership();
-
   // Read the data at a specific address.
   virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0;
 
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index a53293a..06da2f4 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -28,8 +28,8 @@
 #include <sys/mman.h>
 #endif
 
+#include <deque>
 #include <string>
-#include <vector>
 
 struct backtrace_map_t {
   uintptr_t start;
@@ -40,7 +40,8 @@
 
 class BacktraceMap {
 public:
-  BacktraceMap(pid_t pid);
+  static BacktraceMap* Create(pid_t pid);
+
   virtual ~BacktraceMap();
 
   // Get the map data structure for the given address.
@@ -60,20 +61,22 @@
   bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
   bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }
 
-  typedef std::vector<backtrace_map_t>::iterator iterator;
+  typedef std::deque<backtrace_map_t>::iterator iterator;
   iterator begin() { return maps_.begin(); }
   iterator end() { return maps_.end(); }
 
-  typedef std::vector<backtrace_map_t>::const_iterator const_iterator;
+  typedef std::deque<backtrace_map_t>::const_iterator const_iterator;
   const_iterator begin() const { return maps_.begin(); }
   const_iterator end() const { return maps_.end(); }
 
   virtual bool Build();
 
 protected:
+  BacktraceMap(pid_t pid);
+
   virtual bool ParseLine(const char* line, backtrace_map_t* map);
 
-  std::vector<backtrace_map_t> maps_;
+  std::deque<backtrace_map_t> maps_;
   pid_t pid_;
 };
 
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 @@
          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 @@
     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 @@
 #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 @@
 
     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 @@
 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 @@
     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 @@
  */
 #define LOGGER_ENTRY_MAX_LEN		(5*1024)
 
-#ifdef HAVE_IOCTL
+#define NS_PER_SEC 1000000000ULL
 
-#include <sys/ioctl.h>
+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;
+    }
 
-#define __LOGGERIO	0xAE
+    /* 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
+};
 
-#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 */
+struct logger;
 
-#endif // HAVE_IOCTL
+log_id_t android_logger_get_id(struct logger *logger);
 
-#endif /* _UTILS_LOGGER_H */
+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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#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 @@
 #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 @@
     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 @@
 
     // 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 @@
     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/include/sysutils/SocketClientCommand.h b/include/sysutils/SocketClientCommand.h
new file mode 100644
index 0000000..746bc25
--- /dev/null
+++ b/include/sysutils/SocketClientCommand.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ * 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 _SOCKETCLIENTCOMMAND_H
+#define _SOCKETCLIENTCOMMAND_H
+
+#include <sysutils/SocketClient.h>
+
+class SocketClientCommand {
+public:
+    virtual ~SocketClientCommand() { }
+    virtual void runSocketCommand(SocketClient *client) = 0;
+};
+
+#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 @@
 
     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/init/init.c b/init/init.c
index 4266a73..0250e97 100644
--- a/init/init.c
+++ b/init/init.c
@@ -868,6 +868,7 @@
 void selinux_init_all_handles(void)
 {
     sehandle = selinux_android_file_context_handle();
+    selinux_android_set_sehandle(sehandle);
     sehandle_prop = selinux_android_prop_context_handle();
 }
 
diff --git a/init/util.c b/init/util.c
index 5efd5be..e772342 100644
--- a/init/util.c
+++ b/init/util.c
@@ -25,6 +25,7 @@
 #include <ftw.h>
 
 #include <selinux/label.h>
+#include <selinux/android.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -524,60 +525,12 @@
     return rc;
 }
 
-static int restorecon_sb(const char *pathname, const struct stat *sb)
+int restorecon(const char* pathname)
 {
-    char *secontext = NULL;
-    char *oldsecontext = NULL;
-    int i;
-
-    if (selabel_lookup(sehandle, &secontext, pathname, sb->st_mode) < 0)
-        return -errno;
-
-    if (lgetfilecon(pathname, &oldsecontext) < 0) {
-        freecon(secontext);
-        return -errno;
-    }
-
-    if (strcmp(oldsecontext, secontext) != 0) {
-        if (lsetfilecon(pathname, secontext) < 0) {
-            freecon(oldsecontext);
-            freecon(secontext);
-            return -errno;
-        }
-    }
-    freecon(oldsecontext);
-    freecon(secontext);
-    return 0;
-}
-
-int restorecon(const char *pathname)
-{
-    struct stat sb;
-
-    if (is_selinux_enabled() <= 0 || !sehandle)
-        return 0;
-
-    if (lstat(pathname, &sb) < 0)
-        return -errno;
-
-    return restorecon_sb(pathname, &sb);
-}
-
-static int nftw_restorecon(const char* filename, const struct stat* statptr,
-    int fileflags __attribute__((unused)),
-    struct FTW* pftw __attribute__((unused)))
-{
-    restorecon_sb(filename, statptr);
-    return 0;
+    return selinux_android_restorecon(pathname);
 }
 
 int restorecon_recursive(const char* pathname)
 {
-    int fd_limit = 20;
-    int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS;
-
-    if (is_selinux_enabled() <= 0 || !sehandle)
-        return 0;
-
-    return nftw(pathname, nftw_restorecon, fd_limit, flags);
+    return selinux_android_restorecon_recursive(pathname);
 }
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index f23eefd..0ae8839 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 
 common_src := \
-	Backtrace.cpp \
+	BacktraceImpl.cpp \
 	BacktraceMap.cpp \
 	BacktraceThread.cpp \
 	thread_utils.c \
@@ -23,7 +23,7 @@
 	liblog \
 
 # To enable using libunwind on each arch, add it to this list.
-libunwind_architectures := arm64
+libunwind_architectures := arm arm64
 
 ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),$(libunwind_architectures)))
 
@@ -35,6 +35,7 @@
 LOCAL_SRC_FILES:= \
 	$(common_src) \
 	UnwindCurrent.cpp \
+	UnwindMap.cpp \
 	UnwindPtrace.cpp \
 
 LOCAL_CFLAGS := \
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/BacktraceImpl.cpp
similarity index 92%
rename from libbacktrace/Backtrace.cpp
rename to libbacktrace/BacktraceImpl.cpp
index d7b40ab..39296b4 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/BacktraceImpl.cpp
@@ -29,7 +29,7 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
-#include "Backtrace.h"
+#include "BacktraceImpl.h"
 #include "thread_utils.h"
 
 //-------------------------------------------------------------------------
@@ -40,21 +40,21 @@
   impl_->SetParent(this);
 
   if (map_ == NULL) {
-    // The map will be created when needed.
+    map_ = BacktraceMap::Create(pid);
     map_shared_ = false;
   }
 }
 
 Backtrace::~Backtrace() {
-  if (map_ && !map_shared_) {
-    delete map_;
-    map_ = NULL;
-  }
-
   if (impl_) {
     delete impl_;
     impl_ = NULL;
   }
+
+  if (map_ && !map_shared_) {
+    delete map_;
+    map_ = NULL;
+  }
 }
 
 bool Backtrace::Unwind(size_t num_ignore_frames) {
@@ -129,30 +129,10 @@
   return buf;
 }
 
-bool Backtrace::BuildMap() {
-  map_ = impl_->CreateBacktraceMap(pid_);
-  if (!map_->Build()) {
-    BACK_LOGW("Failed to build map for process %d", pid_);
-    return false;
-  }
-  return true;
-}
-
 const backtrace_map_t* Backtrace::FindMap(uintptr_t pc) {
-  if (map_ == NULL) {
-    // Lazy eval, time to build the map.
-    if (!BuildMap()) {
-      return NULL;
-    }
-  }
   return map_->Find(pc);
 }
 
-BacktraceMap* Backtrace::TakeMapOwnership() {
-  map_shared_ = true;
-  return map_;
-}
-
 //-------------------------------------------------------------------------
 // BacktraceCurrent functions.
 //-------------------------------------------------------------------------
diff --git a/libbacktrace/Backtrace.h b/libbacktrace/BacktraceImpl.h
similarity index 78%
rename from libbacktrace/Backtrace.h
rename to libbacktrace/BacktraceImpl.h
index 31fcaf2..af014d5 100644
--- a/libbacktrace/Backtrace.h
+++ b/libbacktrace/BacktraceImpl.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _LIBBACKTRACE_BACKTRACE_H
-#define _LIBBACKTRACE_BACKTRACE_H
+#ifndef _LIBBACKTRACE_BACKTRACE_IMPL_H
+#define _LIBBACKTRACE_BACKTRACE_IMPL_H
 
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
@@ -39,13 +39,20 @@
 
   void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; }
 
-  virtual BacktraceMap* CreateBacktraceMap(pid_t pid) = 0;
+  inline pid_t Pid() { return backtrace_obj_->Pid(); }
+  inline pid_t Tid() { return backtrace_obj_->Tid(); }
+
+  inline const backtrace_map_t* FindMap(uintptr_t addr) {
+    return backtrace_obj_->FindMap(addr);
+  }
+  inline std::string GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+    return backtrace_obj_->GetFunctionName(pc, offset);
+  }
+  inline BacktraceMap* GetMap() { return backtrace_obj_->GetMap(); }
 
 protected:
   inline std::vector<backtrace_frame_data_t>* GetFrames() { return &backtrace_obj_->frames_; }
 
-  inline bool BuildMap() { return backtrace_obj_->BuildMap(); }
-
   Backtrace* backtrace_obj_;
 };
 
@@ -69,4 +76,4 @@
 Backtrace* CreatePtraceObj(pid_t pid, pid_t tid, BacktraceMap* map);
 Backtrace* CreateThreadObj(pid_t tid, BacktraceMap* map);
 
-#endif // _LIBBACKTRACE_BACKTRACE_H
+#endif // _LIBBACKTRACE_BACKTRACE_IMPL_H
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 78f5b6b..6320800 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -21,9 +21,13 @@
 #include <string>
 #include <vector>
 
+#include <backtrace/backtrace_constants.h>
 #include <backtrace/BacktraceMap.h>
 #include <log/log.h>
 
+#include "thread_utils.h"
+#include "BacktraceImpl.h"
+
 BacktraceMap::BacktraceMap(pid_t pid) : pid_(pid) {
   if (pid_ < 0) {
     pid_ = getpid();
@@ -128,3 +132,16 @@
 
   return true;
 }
+
+#if defined(__APPLE__)
+// Corkscrew and libunwind don't compile on the mac, so create a generic
+// map object.
+BacktraceMap* BacktraceMap::Create(pid_t pid) {
+  BacktraceMap* map = new BacktraceMap(pid);
+  if (!map->Build()) {
+    delete map;
+    return NULL;
+  }
+  return map;
+}
+#endif
diff --git a/libbacktrace/BacktraceThread.cpp b/libbacktrace/BacktraceThread.cpp
index 9953dc1..5ffe516 100644
--- a/libbacktrace/BacktraceThread.cpp
+++ b/libbacktrace/BacktraceThread.cpp
@@ -180,10 +180,6 @@
 }
 
 bool BacktraceThread::Unwind(size_t num_ignore_frames) {
-  if (!thread_intf_->Init()) {
-    return false;
-  }
-
   ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
       thread_intf_, Pid(), Tid(), num_ignore_frames);
   if (!entry) {
diff --git a/libbacktrace/BacktraceThread.h b/libbacktrace/BacktraceThread.h
index cae496a..3412d58 100644
--- a/libbacktrace/BacktraceThread.h
+++ b/libbacktrace/BacktraceThread.h
@@ -20,7 +20,7 @@
 #include <inttypes.h>
 #include <sys/types.h>
 
-#include "Backtrace.h"
+#include "BacktraceImpl.h"
 
 enum state_e {
   STATE_WAITING = 0,
@@ -58,8 +58,6 @@
 public:
   virtual ~BacktraceThreadInterface() { }
 
-  virtual bool Init() = 0;
-
   virtual void ThreadUnwind(
       siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0;
 };
diff --git a/libbacktrace/Corkscrew.cpp b/libbacktrace/Corkscrew.cpp
index fd31a26..efeee2e 100644
--- a/libbacktrace/Corkscrew.cpp
+++ b/libbacktrace/Corkscrew.cpp
@@ -65,7 +65,8 @@
     }
     map.name = cur_map->name;
 
-    maps_.push_back(map);
+    // The maps are in descending order, but we want them in ascending order.
+    maps_.push_front(map);
 
     cur_map = cur_map->next;
   }
@@ -93,8 +94,8 @@
     it->stack_size = cork_frames[i].stack_size;
     it->func_offset = 0;
 
-    it->map = backtrace_obj_->FindMap(it->pc);
-    it->func_name = backtrace_obj_->GetFunctionName(it->pc, &it->func_offset);
+    it->map = FindMap(it->pc);
+    it->func_name = GetFunctionName(it->pc, &it->func_offset);
   }
   return true;
 }
@@ -119,7 +120,7 @@
   *offset = 0;
 
   Dl_info info;
-  const backtrace_map_t* map = backtrace_obj_->FindMap(pc);
+  const backtrace_map_t* map = FindMap(pc);
   if (map) {
     if (dladdr((const void*)pc, &info)) {
       if (info.dli_sname) {
@@ -158,19 +159,10 @@
 CorkscrewThread::~CorkscrewThread() {
 }
 
-bool CorkscrewThread::Init() {
-  if (backtrace_obj_->GetMap() == NULL) {
-    // Trigger the map object creation, which will create the corkscrew
-    // map information.
-    return BuildMap();
-  }
-  return true;
-}
-
 void CorkscrewThread::ThreadUnwind(
     siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
   backtrace_frame_t cork_frames[MAX_BACKTRACE_FRAMES];
-  CorkscrewMap* map = static_cast<CorkscrewMap*>(backtrace_obj_->GetMap());
+  CorkscrewMap* map = static_cast<CorkscrewMap*>(GetMap());
   ssize_t num_frames = unwind_backtrace_signal_arch(
       siginfo, sigcontext, map->GetMapInfo(), cork_frames,
       num_ignore_frames, MAX_BACKTRACE_FRAMES);
@@ -204,12 +196,11 @@
 }
 
 bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) {
-  ptrace_context_ = load_ptrace_context(backtrace_obj_->Tid());
+  ptrace_context_ = load_ptrace_context(Tid());
 
   backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
   ssize_t num_frames = unwind_backtrace_ptrace(
-      backtrace_obj_->Tid(), ptrace_context_, frames, num_ignore_frames,
-      MAX_BACKTRACE_FRAMES);
+      Tid(), ptrace_context_, frames, num_ignore_frames, MAX_BACKTRACE_FRAMES);
 
   return GenerateFrameData(frames, num_frames);
 }
@@ -246,3 +237,15 @@
   CorkscrewThread* thread_obj = new CorkscrewThread();
   return new BacktraceThread(thread_obj, thread_obj, tid, map);
 }
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid) {
+  BacktraceMap* map = new CorkscrewMap(pid);
+  if (!map->Build()) {
+    delete map;
+    return NULL;
+  }
+  return map;
+}
diff --git a/libbacktrace/Corkscrew.h b/libbacktrace/Corkscrew.h
index 229bb01..1633398 100644
--- a/libbacktrace/Corkscrew.h
+++ b/libbacktrace/Corkscrew.h
@@ -26,26 +26,9 @@
 
 #include <corkscrew/backtrace.h>
 
-#include "Backtrace.h"
+#include "BacktraceImpl.h"
 #include "BacktraceThread.h"
 
-class CorkscrewCommon : public BacktraceImpl {
-public:
-  bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames);
-
-  virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new BacktraceMap(pid); }
-};
-
-class CorkscrewCurrent : public CorkscrewCommon {
-public:
-  CorkscrewCurrent();
-  virtual ~CorkscrewCurrent();
-
-  virtual bool Unwind(size_t num_ignore_threads);
-
-  virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
-};
-
 class CorkscrewMap : public BacktraceMap {
 public:
   CorkscrewMap(pid_t pid);
@@ -59,17 +42,28 @@
   map_info_t* map_info_;
 };
 
+class CorkscrewCommon : public BacktraceImpl {
+public:
+  bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames);
+};
+
+class CorkscrewCurrent : public CorkscrewCommon {
+public:
+  CorkscrewCurrent();
+  virtual ~CorkscrewCurrent();
+
+  virtual bool Unwind(size_t num_ignore_threads);
+
+  virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
+};
+
 class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface {
 public:
   CorkscrewThread();
   virtual ~CorkscrewThread();
 
-  virtual bool Init();
-
   virtual void ThreadUnwind(
       siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
-
-  virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new CorkscrewMap(pid); }
 };
 
 class CorkscrewPtrace : public CorkscrewCommon {
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index d1195ee..17b71b9 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -25,6 +25,7 @@
 #include <libunwind.h>
 
 #include "UnwindCurrent.h"
+#include "UnwindMap.h"
 
 // Define the ucontext_t structures needed for each supported arch.
 #if defined(__arm__)
@@ -119,8 +120,8 @@
       }
 
       if (resolve) {
-        frame->func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
-        frame->map = backtrace_obj_->FindMap(frame->pc);
+        frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+        frame->map = FindMap(frame->pc);
       } else {
         frame->map = NULL;
         frame->func_offset = 0;
@@ -171,10 +172,6 @@
 UnwindThread::~UnwindThread() {
 }
 
-bool UnwindThread::Init() {
-  return true;
-}
-
 void UnwindThread::ThreadUnwind(
     siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) {
   ExtractContext(sigcontext);
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
index 8302c0b..acce110 100644
--- a/libbacktrace/UnwindCurrent.h
+++ b/libbacktrace/UnwindCurrent.h
@@ -19,7 +19,7 @@
 
 #include <string>
 
-#include "Backtrace.h"
+#include "BacktraceImpl.h"
 #include "BacktraceThread.h"
 
 #define UNW_LOCAL_ONLY
@@ -38,8 +38,6 @@
 
   void ExtractContext(void* sigcontext);
 
-  virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new BacktraceMap(pid); }
-
 protected:
   unw_context_t context_;
 };
@@ -49,8 +47,6 @@
   UnwindThread();
   virtual ~UnwindThread();
 
-  virtual bool Init();
-
   virtual void ThreadUnwind(
       siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
 };
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
new file mode 100644
index 0000000..03bb192
--- /dev/null
+++ b/libbacktrace/UnwindMap.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libbacktrace"
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <backtrace/BacktraceMap.h>
+
+#include <libunwind.h>
+
+#include "UnwindMap.h"
+
+//-------------------------------------------------------------------------
+// libunwind has a single shared address space for the current process
+// aka local. If multiple maps are created for the current pid, then
+// only update the local address space once, and keep a reference count
+// of maps using the same map cursor.
+//-------------------------------------------------------------------------
+static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER;
+static unw_map_cursor_t g_map_cursor;
+static int g_map_references = 0;
+
+UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
+  map_cursor_.map_list = NULL;
+}
+
+UnwindMap::~UnwindMap() {
+  if (pid_ == getpid()) {
+    pthread_mutex_lock(&g_map_mutex);
+    if (--g_map_references == 0) {
+      // Clear the local address space map.
+      unw_map_set(unw_local_addr_space, NULL);
+      unw_map_cursor_destroy(&map_cursor_);
+    }
+    pthread_mutex_unlock(&g_map_mutex);
+  } else {
+    unw_map_cursor_destroy(&map_cursor_);
+  }
+}
+
+bool UnwindMap::Build() {
+  bool return_value = true;
+  if (pid_ == getpid()) {
+    pthread_mutex_lock(&g_map_mutex);
+    if (g_map_references == 0) {
+      return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
+      if (return_value) {
+        // Set the local address space to this cursor map.
+        unw_map_set(unw_local_addr_space, &map_cursor_);
+        g_map_references = 1;
+        g_map_cursor = map_cursor_;
+      }
+    } else {
+      g_map_references++;
+      map_cursor_ = g_map_cursor;
+    }
+    pthread_mutex_unlock(&g_map_mutex);
+  } else {
+    return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
+  }
+
+  if (!return_value)
+    return false;
+
+  // Use the map_cursor information to construct the BacktraceMap data
+  // rather than reparsing /proc/self/maps.
+  unw_map_cursor_reset(&map_cursor_);
+  unw_map_t unw_map;
+  while (unw_map_cursor_get(&map_cursor_, &unw_map)) {
+    backtrace_map_t map;
+
+    map.start = unw_map.start;
+    map.end = unw_map.end;
+    map.flags = unw_map.flags;
+    map.name = unw_map.path;
+
+    // The maps are in descending order, but we want them in ascending order.
+    maps_.push_front(map);
+  }
+
+  return true;
+}
+
+//-------------------------------------------------------------------------
+// BacktraceMap create function.
+//-------------------------------------------------------------------------
+BacktraceMap* BacktraceMap::Create(pid_t pid) {
+  BacktraceMap* map = new UnwindMap(pid);
+  if (!map->Build()) {
+    delete map;
+    return NULL;
+  }
+  return map;
+}
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
new file mode 100644
index 0000000..5a874e8
--- /dev/null
+++ b/libbacktrace/UnwindMap.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_MAP_H
+#define _LIBBACKTRACE_UNWIND_MAP_H
+
+#include <backtrace/BacktraceMap.h>
+
+// The unw_map_cursor_t structure is different depending on whether it is
+// the local or remote version. In order to get the correct version, include
+// libunwind.h first then this header.
+
+class UnwindMap : public BacktraceMap {
+public:
+  UnwindMap(pid_t pid);
+  virtual ~UnwindMap();
+
+  virtual bool Build();
+
+  unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
+
+private:
+  unw_map_cursor_t map_cursor_;
+};
+
+#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index e45c5f8..732dae8 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -25,6 +25,7 @@
 #include <libunwind.h>
 #include <libunwind-ptrace.h>
 
+#include "UnwindMap.h"
 #include "UnwindPtrace.h"
 
 UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) {
@@ -36,6 +37,10 @@
     upt_info_ = NULL;
   }
   if (addr_space_) {
+    // Remove the map from the address space before destroying it.
+    // It will be freed in the UnwindMap destructor.
+    unw_map_set(addr_space_, NULL);
+
     unw_destroy_addr_space(addr_space_);
     addr_space_ = NULL;
   }
@@ -48,7 +53,10 @@
     return false;
   }
 
-  upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(backtrace_obj_->Tid()));
+  UnwindMap* map = static_cast<UnwindMap*>(GetMap());
+  unw_map_set(addr_space_, map->GetMapCursor());
+
+  upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
   if (!upt_info_) {
     BACK_LOGW("Failed to create upt info.");
     return false;
@@ -91,9 +99,9 @@
         prev->stack_size = frame->sp - prev->sp;
       }
 
-      frame->func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
+      frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
 
-      frame->map = backtrace_obj_->FindMap(frame->pc);
+      frame->map = FindMap(frame->pc);
 
       num_frames++;
     } else {
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
index 05375dd..1e82117 100644
--- a/libbacktrace/UnwindPtrace.h
+++ b/libbacktrace/UnwindPtrace.h
@@ -19,7 +19,7 @@
 
 #include <string>
 
-#include "Backtrace.h"
+#include "BacktraceImpl.h"
 
 #include <libunwind.h>
 
@@ -32,8 +32,6 @@
 
   virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
 
-  virtual BacktraceMap* CreateBacktraceMap(pid_t pid) { return new BacktraceMap(pid); }
-
 private:
   unw_addr_space_t addr_space_;
   struct UPT_info* upt_info_;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 0ff7897..23eaf92 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -247,7 +247,7 @@
   ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0);
 }
 
-void VerifyProcTest(pid_t pid, pid_t tid,
+void VerifyProcTest(pid_t pid, pid_t tid, bool share_map,
                     bool (*ReadyFunc)(Backtrace*),
                     void (*VerifyFunc)(Backtrace*)) {
   pid_t ptrace_tid;
@@ -264,7 +264,11 @@
       // Wait for the process to get to a stopping point.
       WaitForStop(ptrace_tid);
 
-      UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid));
+      UniquePtr<BacktraceMap> map;
+      if (share_map) {
+        map.reset(BacktraceMap::Create(pid));
+      }
+      UniquePtr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
       ASSERT_TRUE(backtrace->Unwind(0));
       ASSERT_TRUE(backtrace.get() != NULL);
       if (ReadyFunc(backtrace.get())) {
@@ -285,7 +289,21 @@
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
     exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyLevelDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
+
+  kill(pid, SIGKILL);
+  int status;
+  ASSERT_EQ(waitpid(pid, &status, 0), pid);
+}
+
+TEST(libbacktrace, ptrace_trace_shared_map) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
+    exit(1);
+  }
+
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
 
   kill(pid, SIGKILL);
   int status;
@@ -298,7 +316,7 @@
     ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
     exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyMaxBacktrace, VerifyMaxDump);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
 
   kill(pid, SIGKILL);
   int status;
@@ -323,7 +341,7 @@
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
     exit(1);
   }
-  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
+  VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
 
   kill(pid, SIGKILL);
   int status;
@@ -387,7 +405,7 @@
     if (pid == *it) {
       continue;
     }
-    VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump);
+    VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
   }
   ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
 
@@ -586,6 +604,29 @@
   }
 }
 
+// This test is for UnwindMaps that should share the same map cursor when
+// multiple maps are created for the current process at the same time.
+TEST(libbacktrace, simultaneous_maps) {
+  BacktraceMap* map1 = BacktraceMap::Create(getpid());
+  BacktraceMap* map2 = BacktraceMap::Create(getpid());
+  BacktraceMap* map3 = BacktraceMap::Create(getpid());
+
+  Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
+  EXPECT_TRUE(back1->Unwind(0));
+  delete back1;
+  delete map1;
+
+  Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
+  EXPECT_TRUE(back2->Unwind(0));
+  delete back2;
+  delete map2;
+
+  Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
+  EXPECT_TRUE(back3->Unwind(0));
+  delete back3;
+  delete map3;
+}
+
 TEST(libbacktrace, format_test) {
   UniquePtr<Backtrace> backtrace(Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != NULL);
diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c
index ef22821..df486de 100755
--- a/libcorkscrew/arch-x86/backtrace-x86.c
+++ b/libcorkscrew/arch-x86/backtrace-x86.c
@@ -36,46 +36,7 @@
 #include <sys/ptrace.h>
 #include <cutils/log.h>
 
-#if defined(__BIONIC__)
-
-#if defined(__BIONIC_HAVE_UCONTEXT_T)
-
-// Bionic offers the Linux kernel headers.
-#include <asm/sigcontext.h>
-#include <asm/ucontext.h>
-typedef struct ucontext ucontext_t;
-
-#else /* __BIONIC_HAVE_UCONTEXT_T */
-
-/* Old versions of the Android <signal.h> didn't define ucontext_t. */
-
-typedef struct {
-  uint32_t  gregs[32];
-  void*     fpregs;
-  uint32_t  oldmask;
-  uint32_t  cr2;
-} mcontext_t;
-
-enum {
-  REG_GS = 0, REG_FS, REG_ES, REG_DS,
-  REG_EDI, REG_ESI, REG_EBP, REG_ESP,
-  REG_EBX, REG_EDX, REG_ECX, REG_EAX,
-  REG_TRAPNO, REG_ERR, REG_EIP, REG_CS,
-  REG_EFL, REG_UESP, REG_SS
-};
-
-/* Machine context at the time a signal was raised. */
-typedef struct ucontext {
-    uint32_t uc_flags;
-    struct ucontext* uc_link;
-    stack_t uc_stack;
-    mcontext_t uc_mcontext;
-    uint32_t uc_sigmask;
-} ucontext_t;
-
-#endif /* __BIONIC_HAVE_UCONTEXT_T */
-
-#elif defined(__APPLE__)
+#if defined(__APPLE__)
 
 #define _XOPEN_SOURCE
 #include <ucontext.h>
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 @@
 	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 @@
 }
 
 
-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/list.c b/libcutils/list.c
deleted file mode 100644
index e13452d..0000000
--- a/libcutils/list.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2008 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 <cutils/list.h>
-
-void list_init(struct listnode *node)
-{
-    node->next = node;
-    node->prev = node;
-}
-
-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;
-}
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 @@
 
 #include "socket_local.h"
 
+#define UNUSED __attribute__((unused))
+
 #define LISTEN_BACKLOG 4
 
 /* Documented in header file. */
@@ -122,7 +124,7 @@
  * 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 @@
         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 @@
     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 @@
 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_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 5283619..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.
@@ -19,6 +19,8 @@
  * passed on to the underlying (fake) log device.  When not in the
  * simulator, messages are printed to stderr.
  */
+#include "fake_log_device.h"
+
 #include <log/logd.h>
 
 #include <stdlib.h>
@@ -320,11 +322,11 @@
  * 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/fake_log_device.h b/liblog/fake_log_device.h
new file mode 100644
index 0000000..9d168cd
--- /dev/null
+++ b/liblog/fake_log_device.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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.
+ * 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 _LIBLOG_FAKE_LOG_DEVICE_H
+#define _LIBLOG_FAKE_LOG_DEVICE_H
+
+#include <sys/types.h>
+
+struct iovec;
+
+int fakeLogOpen(const char *pathName, int flags);
+int fakeLogClose(int fd);
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+
+#endif // _LIBLOG_FAKE_LOG_DEVICE_H
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 fff7cc4..5766f8c 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,10 +31,16 @@
 #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.
+#include "fake_log_device.h"
 #define log_open(pathname, flags) fakeLogOpen(pathname, flags)
 #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
 #define log_close(filedes) fakeLogClose(filedes)
@@ -50,6 +56,8 @@
 static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
 #endif
 
+#define UNUSED  __attribute__((__unused__))
+
 static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };
 
 /*
@@ -72,7 +80,8 @@
     return (g_log_status == kLogAvailable);
 }
 
-static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr)
+static int __write_to_log_null(log_id_t log_fd UNUSED, struct iovec *vec UNUSED,
+        size_t nr UNUSED)
 {
     return -1;
 }
@@ -236,7 +245,7 @@
 }
 
 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 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 @@
     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 @@
      * 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   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   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 @@
     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 @@
             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 @@
     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::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::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::runListener() {
 
-    SocketClientCollection *pendingList = new SocketClientCollection();
+    SocketClientCollection pendingList;
 
     while(1) {
         SocketClientCollection::iterator it;
@@ -166,10 +167,12 @@
 
         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 @@
         } 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 @@
         }
 
         /* 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();
+        }
+    }
+}
+
+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));
             }
         }
     }
-    delete pendingList;
+    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) {
-        // broadcasts are unsolicited and should not include a cmd number
-        if ((*i)->sendMsg(code, msg, addErrno, false)) {
-            SLOGW("Error sending broadcast (%s)", strerror(errno));
-        }
+        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 (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_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
+// Copyright 2006-2014 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>
-
+#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 @@
 
 }
 
-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 @@
     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 @@
 
     g_outByteCount += bytesWritten;
 
-    if (g_logRotateSizeKBytes > 0 
+    if (g_logRotateSizeKBytes > 0
         && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
     ) {
         rotateLogs();
@@ -208,20 +171,13 @@
     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 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 @@
                     "  -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 @@
     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 @@
     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 @@
             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 @@
             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 @@
                     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 @@
             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 @@
     }
 
     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 @@
             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 @@
         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 @@
         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 @@
     }
 
     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 @@
         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 @@
     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);
+}