Merge changes I70ab37d5,I716f89c0,I34c96adf,I77650923,I35b0d1ee, ...

* changes:
  libsysutils: SocketListener export release
  libsysutils: Add iovec/runOnEachSocket
  liblog: support struct logger_event_v2 format
  liblog: update timestamp on NOTICE file
  libcutils: resolve warning in iosched_policy.c
  liblog: Add const pedantics
  logcat: Add -T flag (-t w/o assumption of -d)
  logcat: Add logcat test suite
  liblog: Add cpu utilization test
  liblog: Add liblog test suite
  debuggerd: Support newline split in log messages
  liblog: deprecate export LOGGER ioctl definitions
  liblog: deprecate export of LOGGER_LOG_* defines
  liblog: Add README
  liblog: resolve build warning messages
  liblog: high CPU usage from logcat
  liblog: fix build again
  liblog: drop use of sys/cdefs.h
  liblog: git_master@964770 build problem
  logcat: Incorporate liblog reading API
  debuggerd: Incorporate liblog reading API
  liblog: Interface to support abstracting log read
  adb: deprecate legacy log service interface
  adb: regression from Move list.c to inlines
  liblog: whitespace cleanup
  libcutils: bug str_parms.c:str_parms_get_float().
  libcutils: UNUSED argument warnings
  libsysutils: Get rid of warnings
  libcutils: Move list.c to inlines on list.h
This commit is contained in:
Mark Salyzyn 2014-01-28 21:09:37 +00:00 committed by Gerrit Code Review
commit d2acdd82e6
40 changed files with 2978 additions and 580 deletions

View File

@ -34,7 +34,7 @@ endif
ifeq ($(HOST_OS),windows)
USB_SRCS := usb_windows.c
EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c
EXTRA_SRCS := get_my_path_windows.c
EXTRA_STATIC_LIBS := AdbWinApi
ifneq ($(strip $(USE_CYGWIN)),)
# Pure cygwin case
@ -114,8 +114,7 @@ LOCAL_SRC_FILES := \
jdwp_service.c \
framebuffer_service.c \
remount_service.c \
usb_linux_client.c \
log_service.c
usb_linux_client.c
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE

View File

@ -198,11 +198,6 @@ localfilesystem:<path>
Variants of local:<path> that are used to access other Android
socket namespaces.
log:<name>
Opens one of the system logs (/dev/log/<name>) and allows the client
to read them directly. Used to implement 'adb logcat'. The stream
will be read-only for the client.
framebuffer:
This service is used to send snapshots of the framebuffer to a client.
It requires sufficient privileges but works as follow:

View File

@ -330,9 +330,7 @@ typedef enum {
} BackupOperation;
int backup_service(BackupOperation operation, char* args);
void framebuffer_service(int fd, void *cookie);
void log_service(int fd, void *cookie);
void remount_service(int fd, void *cookie);
char * get_log_file_path(const char * log_name);
#endif
/* packet allocator */

View File

@ -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);
}

View File

@ -368,8 +368,6 @@ int service_to_fd(const char *name)
ret = create_service_thread(framebuffer_service, 0);
} else if (!strncmp(name, "jdwp:", 5)) {
ret = create_jdwp_connection_fd(atoi(name+5));
} else if (!strncmp(name, "log:", 4)) {
ret = create_service_thread(log_service, get_log_file_path(name + 4));
} else if(!HOST && !strncmp(name, "shell:", 6)) {
if(name[6]) {
ret = create_subproc_thread(name + 6);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* Copyright (C) 2012-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@
#include <private/android_filesystem_config.h>
#include <log/log.h>
#include <log/logger.h>
#include <cutils/properties.h>
@ -453,43 +454,40 @@ static bool dump_sibling_thread_report(
// Reads the contents of the specified log device, filters out the entries
// that don't match the specified pid, and writes them to the tombstone file.
//
// If "tailOnly" is set, we only print the last few lines.
static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tailOnly) {
// If "tail" is set, we only print the last few lines.
static void dump_log_file(log_t* log, pid_t pid, const char* filename,
unsigned int tail) {
bool first = true;
struct logger_list *logger_list;
// circular buffer, for "tailOnly" mode
const int kShortLogMaxLines = 5;
const int kShortLogLineLen = 256;
char shortLog[kShortLogMaxLines][kShortLogLineLen];
int shortLogCount = 0;
int shortLogNext = 0;
logger_list = android_logger_list_open(
android_name_to_log_id(filename), O_RDONLY | O_NONBLOCK, tail, pid);
int logfd = open(filename, O_RDONLY | O_NONBLOCK);
if (logfd < 0) {
if (!logger_list) {
XLOG("Unable to open %s: %s\n", filename, strerror(errno));
return;
}
union {
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
struct logger_entry entry;
} log_entry;
struct log_msg log_entry;
while (true) {
ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN);
ssize_t actual = android_logger_list_read(logger_list, &log_entry);
if (actual < 0) {
if (errno == EINTR) {
if (actual == -EINTR) {
// interrupted by signal, retry
continue;
} else if (errno == EAGAIN) {
} else if (actual == -EAGAIN) {
// non-blocking EOF; we're done
break;
} else {
_LOG(log, 0, "Error while reading log: %s\n", strerror(errno));
_LOG(log, 0, "Error while reading log: %s\n",
strerror(-actual));
break;
}
} else if (actual == 0) {
_LOG(log, 0, "Got zero bytes while reading log: %s\n", strerror(errno));
_LOG(log, 0, "Got zero bytes while reading log: %s\n",
strerror(errno));
break;
}
@ -497,7 +495,7 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail
// because you will be writing as fast as you're reading. Any
// high-frequency debug diagnostics should just be written to
// the tombstone file.
struct logger_entry* entry = &log_entry.entry;
struct logger_entry* entry = &log_entry.entry_v1;
if (entry->pid != static_cast<int32_t>(pid)) {
// wrong pid, ignore
@ -505,7 +503,8 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail
}
if (first) {
_LOG(log, 0, "--------- %slog %s\n", tailOnly ? "tail end of " : "", filename);
_LOG(log, 0, "--------- %slog %s\n",
tail ? "tail end of " : "", filename);
first = false;
}
@ -513,18 +512,20 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail
//
// We want to display it in the same format as "logcat -v threadtime"
// (although in this case the pid is redundant).
//
// TODO: scan for line breaks ('\n') and display each text line
// on a separate line, prefixed with the header, like logcat does.
static const char* kPrioChars = "!.VDIWEFS";
unsigned char prio = entry->msg[0];
char* tag = entry->msg + 1;
char* msg = tag + strlen(tag) + 1;
unsigned hdr_size = log_entry.entry.hdr_size;
if (!hdr_size) {
hdr_size = sizeof(log_entry.entry_v1);
}
char* msg = (char *)log_entry.buf + hdr_size;
unsigned char prio = msg[0];
char* tag = msg + 1;
msg = tag + strlen(tag) + 1;
// consume any trailing newlines
char* eatnl = msg + strlen(msg) - 1;
while (eatnl >= msg && *eatnl == '\n') {
*eatnl-- = '\0';
char* nl = msg + strlen(msg) - 1;
while (nl >= msg && *nl == '\n') {
*nl-- = '\0';
}
char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
@ -536,44 +537,30 @@ static void dump_log_file(log_t* log, pid_t pid, const char* filename, bool tail
ptm = localtime_r(&sec, &tmBuf);
strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
if (tailOnly) {
snprintf(shortLog[shortLogNext], kShortLogLineLen,
"%s.%03d %5d %5d %c %-8s: %s",
timeBuf, entry->nsec / 1000000, entry->pid, entry->tid,
prioChar, tag, msg);
shortLogNext = (shortLogNext + 1) % kShortLogMaxLines;
shortLogCount++;
} else {
// 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) {
@ -649,7 +636,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a
}
if (want_logs) {
dump_logs(log, pid, true);
dump_logs(log, pid, 5);
}
bool detach_failed = false;
@ -661,7 +648,7 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a
delete map;
if (want_logs) {
dump_logs(log, pid, false);
dump_logs(log, pid, 0);
}
// send EOD to the Activity Manager, then wait for its ack to avoid racing ahead

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2008-2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -49,9 +49,25 @@ struct listnode
node != (list); \
node = next, next = node->next)
void list_init(struct listnode *list);
void list_add_tail(struct listnode *list, struct listnode *item);
void list_remove(struct listnode *item);
static inline void list_init(struct listnode *node)
{
node->next = node;
node->prev = node;
}
static inline void list_add_tail(struct listnode *head, struct listnode *item)
{
item->next = head;
item->prev = head->prev;
head->prev->next = item;
head->prev = item;
}
static inline void list_remove(struct listnode *item)
{
item->next->prev = item->prev;
item->prev->next = item->next;
}
#define list_empty(list) ((list) == (list)->next)
#define list_head(list) ((list)->next)

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2005 The Android Open Source Project
* Copyright (C) 2005-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,17 +28,16 @@
#ifndef _LIBS_LOG_LOG_H
#define _LIBS_LOG_LOG_H
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_PTHREADS
#include <pthread.h>
#endif
#include <stdarg.h>
#include <log/uio.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <log/logd.h>
#include <log/uio.h>
#ifdef __cplusplus
extern "C" {
@ -470,7 +469,8 @@ typedef enum {
EVENT_TYPE_STRING = 2,
EVENT_TYPE_LIST = 3,
} AndroidEventLogType;
#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
#define typeof_AndroidEventLogType unsigned char
#ifndef LOG_EVENT_INT
#define LOG_EVENT_INT(_tag, _value) { \
@ -540,7 +540,9 @@ typedef enum {
#define android_logToFile(tag, file) (0)
#define android_logToFd(tag, fd) (0)
typedef enum {
typedef enum log_id {
LOG_ID_MIN = 0,
LOG_ID_MAIN = 0,
LOG_ID_RADIO = 1,
LOG_ID_EVENTS = 2,
@ -548,6 +550,8 @@ typedef enum {
LOG_ID_MAX
} log_id_t;
#define sizeof_log_id_t sizeof(typeof_log_id_t)
#define typeof_log_id_t unsigned char
/*
* Send a simple string to the log.
@ -555,9 +559,8 @@ typedef enum {
int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text);
int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...);
#ifdef __cplusplus
}
#endif
#endif // _LIBS_CUTILS_LOG_H
#endif /* _LIBS_LOG_LOG_H */

79
include/log/log_read.h Normal file
View File

@ -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 */

View File

@ -1,16 +1,21 @@
/* utils/logger.h
**
** Copyright 2007, The Android Open Source Project
/*
**
** Copyright 2007-2014, The Android Open Source Project
**
** This file is dual licensed. It may be redistributed and/or modified
** under the terms of the Apache 2.0 License OR version 2 of the GNU
** General Public License.
*/
#ifndef _UTILS_LOGGER_H
#define _UTILS_LOGGER_H
#ifndef _LIBS_LOG_LOGGER_H
#define _LIBS_LOG_LOGGER_H
#include <stdint.h>
#include <log/log.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* The userspace structure for version 1 of the logger_entry ABI.
@ -43,11 +48,6 @@ struct logger_entry_v2 {
char msg[0]; /* the entry's payload */
};
#define LOGGER_LOG_MAIN "log/main"
#define LOGGER_LOG_RADIO "log/radio"
#define LOGGER_LOG_EVENTS "log/events"
#define LOGGER_LOG_SYSTEM "log/system"
/*
* The maximum size of the log entry payload that can be
* written to the kernel logger driver. An attempt to write
@ -63,19 +63,108 @@ struct logger_entry_v2 {
*/
#define LOGGER_ENTRY_MAX_LEN (5*1024)
#ifdef HAVE_IOCTL
#define NS_PER_SEC 1000000000ULL
#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 */

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (C) 2007-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,8 +31,8 @@ extern "C" {
#include <stddef.h>
struct iovec {
const void* iov_base;
size_t iov_len;
void* iov_base;
size_t iov_len;
};
extern int readv( int fd, struct iovec* vecs, int count );

View File

@ -6,22 +6,23 @@
#include <pthread.h>
#include <cutils/atomic.h>
#include <sys/types.h>
#include <sys/uio.h>
class SocketClient {
int mSocket;
bool mSocketOwned;
pthread_mutex_t mWriteMutex;
/* Peer process ID */
// Peer process ID
pid_t mPid;
/* Peer user ID */
// Peer user ID
uid_t mUid;
/* Peer group ID */
// Peer group ID
gid_t mGid;
/* Reference count (starts at 1) */
// Reference count (starts at 1)
pthread_mutex_t mRefCountMutex;
int mRefCount;
@ -38,12 +39,15 @@ public:
pid_t getPid() const { return mPid; }
uid_t getUid() const { return mUid; }
gid_t getGid() const { return mGid; }
void setCmdNum(int cmdNum) { android_atomic_release_store(cmdNum, &mCmdNum); }
void setCmdNum(int cmdNum) {
android_atomic_release_store(cmdNum, &mCmdNum);
}
int getCmdNum() { return mCmdNum; }
// Send null-terminated C strings:
int sendMsg(int code, const char *msg, bool addErrno);
int sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum);
int sendMsg(const char *msg);
// Provides a mechanism to send a response code to the client.
// Sends the code and a null character.
@ -56,6 +60,8 @@ public:
// Sending binary data:
int sendData(const void *data, int len);
// iovec contents not preserved through call
int sendDatav(struct iovec *iov, int iovcnt);
// Optional reference counting. Reference count starts at 1. If
// it's decremented to 0, it deletes itself.
@ -64,19 +70,18 @@ public:
void incRef();
bool decRef(); // returns true at 0 (but note: SocketClient already deleted)
// return a new string in quotes with '\\' and '\"' escaped for "my arg" transmissions
// return a new string in quotes with '\\' and '\"' escaped for "my arg"
// transmissions
static char *quoteArg(const char *arg);
private:
// Send null-terminated C strings
int sendMsg(const char *msg);
void init(int socket, bool owned, bool useCmdNum);
// Sending binary data. The caller should use make sure this is protected
// Sending binary data. The caller should make sure this is protected
// from multiple threads entering simultaneously.
// returns 0 if successful, -1 if there is a 0 byte write and -2 if any other
// error occurred (use errno to get the error)
int sendDataLocked(const void *data, int len);
// returns 0 if successful, -1 if there is a 0 byte write or if any
// other error occurred (use errno to get the error)
int sendDataLockedv(struct iovec *iov, int iovcnt);
};
typedef android::sysutils::List<SocketClient *> SocketClientCollection;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,25 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _SOCKETCLIENTCOMMAND_H
#define _SOCKETCLIENTCOMMAND_H
#include <cutils/list.h>
#include <sysutils/SocketClient.h>
void list_init(struct listnode *node)
{
node->next = node;
node->prev = node;
}
class SocketClientCommand {
public:
virtual ~SocketClientCommand() { }
virtual void runSocketCommand(SocketClient *client) = 0;
};
void list_add_tail(struct listnode *head, struct listnode *item)
{
item->next = head;
item->prev = head->prev;
head->prev->next = item;
head->prev = item;
}
void list_remove(struct listnode *item)
{
item->next->prev = item->prev;
item->prev->next = item->next;
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2008-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,7 @@
#include <pthread.h>
#include <sysutils/SocketClient.h>
#include "SocketClientCommand.h"
class SocketListener {
bool mListen;
@ -41,10 +42,15 @@ public:
void sendBroadcast(int code, const char *msg, bool addErrno);
void runOnEachSocket(SocketClientCommand *command);
bool release(SocketClient *c) { return release(c, true); }
protected:
virtual bool onDataAvailable(SocketClient *c) = 0;
private:
bool release(SocketClient *c, bool wakeup);
static void *threadStart(void *obj);
void runListener();
void init(const char *socketName, int socketFd, bool listen, bool useCmdNum);

View File

@ -37,7 +37,6 @@ commonSources := \
config_utils.c \
cpu_info.c \
load_file.c \
list.c \
open_memstream.c \
strdup16to8.c \
strdup8to16.c \

View File

@ -25,6 +25,8 @@
#include <cutils/android_reboot.h>
#define UNUSED __attribute__((unused))
/* Check to see if /proc/mounts contains any writeable filesystems
* backed by a block device.
* Return true if none found, else return false.
@ -102,7 +104,7 @@ static void remount_ro(void)
}
int android_reboot(int cmd, int flags, char *arg)
int android_reboot(int cmd, int flags UNUSED, char *arg)
{
int ret;

View File

@ -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,

View File

@ -39,6 +39,8 @@ int socket_local_client(const char *name, int namespaceId, int type)
#include "socket_local.h"
#define UNUSED __attribute__((unused))
#define LISTEN_BACKLOG 4
/* Documented in header file. */
@ -122,7 +124,7 @@ error:
* Used by AndroidSocketImpl
*/
int socket_local_client_connect(int fd, const char *name, int namespaceId,
int type)
int type UNUSED)
{
struct sockaddr_un addr;
socklen_t alen;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011 The Android Open Source Project
* Copyright (C) 2011-2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,6 +30,8 @@
#include <cutils/str_parms.h>
#define UNUSED __attribute__((unused))
struct str_parms {
Hashmap *map;
};
@ -278,10 +280,11 @@ int str_parms_get_float(struct str_parms *str_parms, const char *key,
return -ENOENT;
out = strtof(value, &end);
if (*value != '\0' && *end == '\0')
return 0;
if (*value == '\0' || *end != '\0')
return -EINVAL;
return -EINVAL;
*val = out;
return 0;
}
static bool combine_strings(void *key, void *value, void *context)
@ -318,7 +321,7 @@ char *str_parms_to_str(struct str_parms *str_parms)
return str;
}
static bool dump_entry(void *key, void *value, void *context)
static bool dump_entry(void *key, void *value, void *context UNUSED)
{
ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value);
return true;

View File

@ -1,5 +1,5 @@
#
# Copyright (C) 2008 The Android Open Source Project
# Copyright (C) 2008-2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -42,7 +42,7 @@ else
endif
liblog_host_sources := $(liblog_sources) fake_log_device.c
liblog_target_sources = $(liblog_sources) log_read.c
# Shared and static library for host
# ========================================================
@ -67,15 +67,16 @@ LOCAL_LDLIBS := -lpthread
LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64
include $(BUILD_HOST_STATIC_LIBRARY)
# Shared and static library for target
# ========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_SRC_FILES := $(liblog_sources)
LOCAL_SRC_FILES := $(liblog_target_sources)
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := liblog
include $(BUILD_SHARED_LIBRARY)
include $(call first-makefiles-under,$(LOCAL_PATH))

View File

@ -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.

134
liblog/README Normal file
View File

@ -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)

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2008-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -322,11 +322,11 @@ static const char* getPriorityString(int priority)
* Make up something to replace it.
*/
static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) {
int result = 0;
struct iovec* end = iov + iovcnt;
ssize_t result = 0;
const struct iovec* end = iov + iovcnt;
for (; iov < end; iov++) {
int w = write(fd, iov->iov_base, iov->iov_len);
if (w != iov->iov_len) {
ssize_t w = write(fd, iov->iov_base, iov->iov_len);
if (w != (ssize_t) iov->iov_len) {
if (w < 0)
return w;
return result + w;

665
liblog/log_read.c Normal file
View File

@ -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);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (C) 2007-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,7 +31,12 @@
#include <log/logd.h>
#include <log/log.h>
#define LOG_BUF_SIZE 1024
#define LOGGER_LOG_MAIN "log/main"
#define LOGGER_LOG_RADIO "log/radio"
#define LOGGER_LOG_EVENTS "log/events"
#define LOGGER_LOG_SYSTEM "log/system"
#define LOG_BUF_SIZE 1024
#if FAKE_LOG_DEVICE
// This will be defined when building for the host.
@ -240,7 +245,7 @@ int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fm
}
void __android_log_assert(const char *cond, const char *tag,
const char *fmt, ...)
const char *fmt, ...)
{
char buf[LOG_BUF_SIZE];

View File

@ -1,6 +1,6 @@
/* //device/libs/cutils/logprint.c
/*
**
** Copyright 2006, The Android Open Source Project
** Copyright 2006-2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@ -377,8 +377,13 @@ int android_log_processLogBuffer(struct logger_entry *buf,
int msgEnd = -1;
int i;
char *msg = buf->msg;
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
msg = ((char *)buf2) + buf2->hdr_size;
}
for (i = 1; i < buf->len; i++) {
if (buf->msg[i] == '\0') {
if (msg[i] == '\0') {
if (msgStart == -1) {
msgStart = i + 1;
} else {
@ -395,12 +400,12 @@ int android_log_processLogBuffer(struct logger_entry *buf,
if (msgEnd == -1) {
// incoming message not null-terminated; force it
msgEnd = buf->len - 1;
buf->msg[msgEnd] = '\0';
msg[msgEnd] = '\0';
}
entry->priority = buf->msg[0];
entry->tag = buf->msg + 1;
entry->message = buf->msg + msgStart;
entry->priority = msg[0];
entry->tag = msg + 1;
entry->message = msg + msgStart;
entry->messageLen = msgEnd - msgStart;
return 0;
@ -614,6 +619,10 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf,
* Pull the tag out.
*/
eventData = (const unsigned char*) buf->msg;
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
eventData = ((unsigned char *)buf2) + buf2->hdr_size;
}
inCount = buf->len;
if (inCount < 4)
return -1;

77
liblog/tests/Android.mk Normal file
View File

@ -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)

147
liblog/tests/benchmark.h Normal file
View File

@ -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_

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (C) 2007-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,8 +24,8 @@ int readv( int fd, struct iovec* vecs, int count )
int total = 0;
for ( ; count > 0; count--, vecs++ ) {
const char* buf = vecs->iov_base;
int len = vecs->iov_len;
char* buf = vecs->iov_base;
int len = vecs->iov_len;
while (len > 0) {
int ret = read( fd, buf, len );
@ -51,8 +51,8 @@ int writev( int fd, const struct iovec* vecs, int count )
int total = 0;
for ( ; count > 0; count--, vecs++ ) {
const char* buf = (const char*)vecs->iov_base;
int len = (int)vecs->iov_len;
const char* buf = vecs->iov_base;
int len = vecs->iov_len;
while (len > 0) {
int ret = write( fd, buf, len );

View File

@ -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;

View File

@ -27,6 +27,8 @@
static const int CMD_BUF_SIZE = 1024;
#define UNUSED __attribute__((unused))
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);
@ -37,7 +39,7 @@ FrameworkListener::FrameworkListener(const char *socketName) :
init(socketName, false);
}
void FrameworkListener::init(const char *socketName, bool withSeq) {
void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
mCommands = new FrameworkCommandCollection();
errorRate = 0;
mCommandCount = 0;

View File

@ -71,7 +71,7 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN
ret = asprintf(&buf, "%d %s", code, msg);
}
}
/* Send the zero-terminated message */
// Send the zero-terminated message
if (ret != -1) {
ret = sendMsg(buf);
free(buf);
@ -79,22 +79,25 @@ int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdN
return ret;
}
/** send 3-digit code, null, binary-length, binary data */
// send 3-digit code, null, binary-length, binary data
int SocketClient::sendBinaryMsg(int code, const void *data, int len) {
/* 4 bytes for the code & null + 4 bytes for the len */
// 4 bytes for the code & null + 4 bytes for the len
char buf[8];
/* Write the code */
// Write the code
snprintf(buf, 4, "%.3d", code);
/* Write the len */
// Write the len
uint32_t tmp = htonl(len);
memcpy(buf + 4, &tmp, sizeof(uint32_t));
struct iovec vec[2];
vec[0].iov_base = (void *) buf;
vec[0].iov_len = sizeof(buf);
vec[1].iov_base = (void *) data;
vec[1].iov_len = len;
pthread_mutex_lock(&mWriteMutex);
int result = sendDataLocked(buf, sizeof(buf));
if (result == 0 && len > 0) {
result = sendDataLocked(data, len);
}
int result = sendDataLockedv(vec, (len > 0) ? 2 : 1);
pthread_mutex_unlock(&mWriteMutex);
return result;
@ -147,33 +150,51 @@ int SocketClient::sendMsg(const char *msg) {
}
int SocketClient::sendData(const void *data, int len) {
struct iovec vec[1];
vec[0].iov_base = (void *) data;
vec[0].iov_len = len;
pthread_mutex_lock(&mWriteMutex);
int rc = sendDataLocked(data, len);
int rc = sendDataLockedv(vec, 1);
pthread_mutex_unlock(&mWriteMutex);
return rc;
}
int SocketClient::sendDataLocked(const void *data, int len) {
int rc = 0;
const char *p = (const char*) data;
int brtw = len;
int SocketClient::sendDatav(struct iovec *iov, int iovcnt) {
pthread_mutex_lock(&mWriteMutex);
int rc = sendDataLockedv(iov, iovcnt);
pthread_mutex_unlock(&mWriteMutex);
return rc;
}
int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
if (mSocket < 0) {
errno = EHOSTUNREACH;
return -1;
}
if (len == 0) {
if (iovcnt <= 0) {
return 0;
}
while (brtw > 0) {
rc = send(mSocket, p, brtw, MSG_NOSIGNAL);
int current = 0;
for (;;) {
ssize_t rc = writev(mSocket, iov + current, iovcnt - current);
if (rc > 0) {
p += rc;
brtw -= rc;
size_t written = rc;
while ((current < iovcnt) && (written >= iov[current].iov_len)) {
written -= iov[current].iov_len;
current++;
}
if (current == iovcnt) {
break;
}
iov[current].iov_base = (char *)iov[current].iov_base + written;
iov[current].iov_len -= written;
continue;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2008-2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,7 +29,8 @@
#include <sysutils/SocketListener.h>
#include <sysutils/SocketClient.h>
#define LOG_NDEBUG 0
#define CtrlPipe_Shutdown 0
#define CtrlPipe_Wakeup 1
SocketListener::SocketListener(const char *socketName, bool listen) {
init(socketName, -1, listen, false);
@ -103,7 +104,7 @@ int SocketListener::startListener() {
}
int SocketListener::stopListener() {
char c = 0;
char c = CtrlPipe_Shutdown;
int rc;
rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1));
@ -145,7 +146,7 @@ void *SocketListener::threadStart(void *obj) {
void SocketListener::runListener() {
SocketClientCollection *pendingList = new SocketClientCollection();
SocketClientCollection pendingList;
while(1) {
SocketClientCollection::iterator it;
@ -166,10 +167,12 @@ void SocketListener::runListener() {
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
// NB: calling out to an other object with mClientsLock held (safe)
int fd = (*it)->getSocket();
FD_SET(fd, &read_fds);
if (fd > max)
if (fd > max) {
max = fd;
}
}
pthread_mutex_unlock(&mClientsLock);
SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
@ -182,8 +185,14 @@ void SocketListener::runListener() {
} else if (!rc)
continue;
if (FD_ISSET(mCtrlPipe[0], &read_fds))
break;
if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
if (c == CtrlPipe_Shutdown) {
break;
}
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) {
struct sockaddr addr;
socklen_t alen;
@ -205,53 +214,111 @@ void SocketListener::runListener() {
}
/* Add all active clients to the pending list first */
pendingList->clear();
pendingList.clear();
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
int fd = (*it)->getSocket();
SocketClient* c = *it;
// NB: calling out to an other object with mClientsLock held (safe)
int fd = c->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList->push_back(*it);
pendingList.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
/* Process the pending list, since it is owned by the thread,
* there is no need to lock it */
while (!pendingList->empty()) {
while (!pendingList.empty()) {
/* Pop the first item from the list */
it = pendingList->begin();
it = pendingList.begin();
SocketClient* c = *it;
pendingList->erase(it);
/* Process it, if false is returned and our sockets are
* connection-based, remove and destroy it */
if (!onDataAvailable(c) && mListen) {
/* Remove the client from our array */
SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
if (*it == c) {
mClients->erase(it);
break;
}
}
pthread_mutex_unlock(&mClientsLock);
/* Remove our reference to the client */
c->decRef();
pendingList.erase(it);
/* Process it, if false is returned, remove from list */
if (!onDataAvailable(c)) {
release(c, false);
}
c->decRef();
}
}
}
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();
}
}

View File

@ -1,4 +1,4 @@
# Copyright 2006 The Android Open Source Project
# Copyright 2006-2014 The Android Open Source Project
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@ -10,3 +10,5 @@ LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE:= logcat
include $(BUILD_EXECUTABLE)
include $(call first-makefiles-under,$(LOCAL_PATH))

View File

@ -1,88 +1,52 @@
// Copyright 2006 The Android Open Source Project
#include <log/logger.h>
#include <log/logd.h>
#include <log/logprint.h>
#include <log/event_tag_map.h>
#include <cutils/sockets.h>
// Copyright 2006-2014 The Android Open Source Project
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <cutils/sockets.h>
#include <log/log.h>
#include <log/logger.h>
#include <log/logd.h>
#include <log/logprint.h>
#include <log/event_tag_map.h>
#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
#define DEFAULT_MAX_ROTATED_LOGS 4
static AndroidLogFormat * g_logformat;
static bool g_nonblock = false;
static int g_tail_lines = 0;
/* logd prefixes records with a length field */
#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
#define LOG_FILE_DIR "/dev/log/"
struct queued_entry_t {
union {
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
struct logger_entry entry __attribute__((aligned(4)));
};
queued_entry_t* next;
queued_entry_t() {
next = NULL;
}
};
static int cmp(queued_entry_t* a, queued_entry_t* b) {
int n = a->entry.sec - b->entry.sec;
if (n != 0) {
return n;
}
return a->entry.nsec - b->entry.nsec;
}
struct log_device_t {
char* device;
const char* device;
bool binary;
int fd;
struct logger *logger;
struct logger_list *logger_list;
bool printed;
char label;
queued_entry_t* queue;
log_device_t* next;
log_device_t(char* d, bool b, char l) {
log_device_t(const char* d, bool b, char l) {
device = d;
binary = b;
label = l;
queue = NULL;
next = NULL;
printed = false;
}
void enqueue(queued_entry_t* entry) {
if (this->queue == NULL) {
this->queue = entry;
} else {
queued_entry_t** e = &this->queue;
while (*e && cmp(entry, *e) >= 0) {
e = &((*e)->next);
}
entry->next = *e;
*e = entry;
}
}
};
namespace android {
@ -147,17 +111,14 @@ static void rotateLogs()
}
void printBinary(struct logger_entry *buf)
void printBinary(struct log_msg *buf)
{
size_t size = sizeof(logger_entry) + buf->len;
int ret;
do {
ret = write(g_outFD, buf, size);
} while (ret < 0 && errno == EINTR);
size_t size = buf->len();
TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
}
static void processBuffer(log_device_t* dev, struct logger_entry *buf)
static void processBuffer(log_device_t* dev, struct log_msg *buf)
{
int bytesWritten = 0;
int err;
@ -165,12 +126,14 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf)
char binaryMsgBuf[1024];
if (dev->binary) {
err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,
binaryMsgBuf, sizeof(binaryMsgBuf));
err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
g_eventTagMap,
binaryMsgBuf,
sizeof(binaryMsgBuf));
//printf(">>> pri=%d len=%d msg='%s'\n",
// entry.priority, entry.messageLen, entry.message);
} else {
err = android_log_processLogBuffer(buf, &entry);
err = android_log_processLogBuffer(&buf->entry_v1, &entry);
}
if (err < 0) {
goto error;
@ -197,7 +160,7 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf)
g_outByteCount += bytesWritten;
if (g_logRotateSizeKBytes > 0
if (g_logRotateSizeKBytes > 0
&& (g_outByteCount / 1024) >= g_logRotateSizeKBytes
) {
rotateLogs();
@ -208,20 +171,13 @@ error:
return;
}
static void chooseFirst(log_device_t* dev, log_device_t** firstdev) {
for (*firstdev = NULL; dev != NULL; dev = dev->next) {
if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) {
*firstdev = dev;
}
}
}
static void maybePrintStart(log_device_t* dev) {
if (!dev->printed) {
dev->printed = true;
if (g_devCount > 1 && !g_printBinary) {
char buf[1024];
snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device);
snprintf(buf, sizeof(buf), "--------- beginning of %s\n",
dev->device);
if (write(g_outFD, buf, strlen(buf)) < 0) {
perror("output error");
exit(-1);
@ -230,145 +186,6 @@ static void maybePrintStart(log_device_t* dev) {
}
}
static void skipNextEntry(log_device_t* dev) {
maybePrintStart(dev);
queued_entry_t* entry = dev->queue;
dev->queue = entry->next;
delete entry;
}
static void printNextEntry(log_device_t* dev) {
maybePrintStart(dev);
if (g_printBinary) {
printBinary(&dev->queue->entry);
} else {
processBuffer(dev, &dev->queue->entry);
}
skipNextEntry(dev);
}
static void readLogLines(log_device_t* devices)
{
log_device_t* dev;
int max = 0;
int ret;
int queued_lines = 0;
bool sleep = false;
int result;
fd_set readset;
for (dev=devices; dev; dev = dev->next) {
if (dev->fd > max) {
max = dev->fd;
}
}
while (1) {
do {
timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.
FD_ZERO(&readset);
for (dev=devices; dev; dev = dev->next) {
FD_SET(dev->fd, &readset);
}
result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
} while (result == -1 && errno == EINTR);
if (result >= 0) {
for (dev=devices; dev; dev = dev->next) {
if (FD_ISSET(dev->fd, &readset)) {
queued_entry_t* entry = new queued_entry_t();
/* NOTE: driver guarantees we read exactly one full entry */
ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);
if (ret < 0) {
if (errno == EINTR) {
delete entry;
goto next;
}
if (errno == EAGAIN) {
delete entry;
break;
}
perror("logcat read");
exit(EXIT_FAILURE);
}
else if (!ret) {
fprintf(stderr, "read: Unexpected EOF!\n");
exit(EXIT_FAILURE);
}
else if (entry->entry.len != ret - sizeof(struct logger_entry)) {
fprintf(stderr, "read: unexpected length. Expected %d, got %d\n",
entry->entry.len, ret - (int) sizeof(struct logger_entry));
exit(EXIT_FAILURE);
}
entry->entry.msg[entry->entry.len] = '\0';
dev->enqueue(entry);
++queued_lines;
}
}
if (result == 0) {
// we did our short timeout trick and there's nothing new
// print everything we have and wait for more data
sleep = true;
while (true) {
chooseFirst(devices, &dev);
if (dev == NULL) {
break;
}
if (g_tail_lines == 0 || queued_lines <= g_tail_lines) {
printNextEntry(dev);
} else {
skipNextEntry(dev);
}
--queued_lines;
}
// the caller requested to just dump the log and exit
if (g_nonblock) {
return;
}
} else {
// print all that aren't the last in their list
sleep = false;
while (g_tail_lines == 0 || queued_lines > g_tail_lines) {
chooseFirst(devices, &dev);
if (dev == NULL || dev->queue->next == NULL) {
break;
}
if (g_tail_lines == 0) {
printNextEntry(dev);
} else {
skipNextEntry(dev);
}
--queued_lines;
}
}
}
next:
;
}
}
static int clearLog(int logfd)
{
return ioctl(logfd, LOGGER_FLUSH_LOG);
}
/* returns the total size of the log's ring buffer */
static int getLogSize(int logfd)
{
return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
}
/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
static int getLogReadableSize(int logfd)
{
return ioctl(logfd, LOGGER_GET_LOG_LEN);
}
static void setupOutput()
{
@ -406,6 +223,7 @@ static void show_help(const char *cmd)
" -c clear (flush) the entire log and exit\n"
" -d dump the log and then exit (don't block)\n"
" -t <count> print only the most recent <count> lines (implies -d)\n"
" -T <count> print only the most recent <count> lines (does not imply -d)\n"
" -g get the size of the log's ring buffer and exit\n"
" -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n"
" or 'events'. Multiple -b parameters are allowed and the\n"
@ -465,6 +283,10 @@ int main(int argc, char **argv)
log_device_t* devices = NULL;
log_device_t* dev;
bool needBinary = false;
struct logger_list *logger_list;
int tail_lines = 0;
signal(SIGPIPE, exit);
g_logformat = android_log_format_new();
@ -481,14 +303,14 @@ int main(int argc, char **argv)
for (;;) {
int ret;
ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B");
ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B");
if (ret < 0) {
break;
}
switch(ret) {
case 's':
case 's':
// default to all silent
android_log_addFilterRule(g_logformat, "*:s");
break;
@ -499,12 +321,14 @@ int main(int argc, char **argv)
break;
case 'd':
g_nonblock = true;
mode = O_RDONLY | O_NDELAY;
break;
case 't':
g_nonblock = true;
g_tail_lines = atoi(optarg);
mode = O_RDONLY | O_NDELAY;
/* FALLTHRU */
case 'T':
tail_lines = atoi(optarg);
break;
case 'g':
@ -512,10 +336,6 @@ int main(int argc, char **argv)
break;
case 'b': {
char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
strcpy(buf, LOG_FILE_DIR);
strcat(buf, optarg);
bool binary = strcmp(optarg, "events") == 0;
if (binary) {
needBinary = true;
@ -526,9 +346,9 @@ int main(int argc, char **argv)
while (dev->next) {
dev = dev->next;
}
dev->next = new log_device_t(buf, binary, optarg[0]);
dev->next = new log_device_t(optarg, binary, optarg[0]);
} else {
devices = new log_device_t(buf, binary, optarg[0]);
devices = new log_device_t(optarg, binary, optarg[0]);
}
android::g_devCount++;
}
@ -546,8 +366,8 @@ int main(int argc, char **argv)
break;
case 'r':
if (optarg == NULL) {
android::g_logRotateSizeKBytes
if (optarg == NULL) {
android::g_logRotateSizeKBytes
= DEFAULT_LOG_ROTATE_SIZE_KBYTES;
} else {
long logRotateSize;
@ -659,19 +479,15 @@ int main(int argc, char **argv)
}
if (!devices) {
devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm');
devices = new log_device_t("main", false, 'm');
android::g_devCount = 1;
int accessmode =
(mode & O_RDONLY) ? R_OK : 0
| (mode & O_WRONLY) ? W_OK : 0;
// only add this if it's available
if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) {
devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's');
if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
devices->next = new log_device_t("system", false, 's');
android::g_devCount++;
}
}
if (android::g_logRotateSizeKBytes != 0
if (android::g_logRotateSizeKBytes != 0
&& android::g_outputFileName == NULL
) {
fprintf(stderr,"-r requires -f as well\n");
@ -688,7 +504,7 @@ int main(int argc, char **argv)
err = setLogFormat(logFormat);
if (err < 0) {
fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
logFormat);
}
}
@ -707,8 +523,8 @@ int main(int argc, char **argv)
if (env_tags_orig != NULL) {
err = android_log_addFilterString(g_logformat, env_tags_orig);
if (err < 0) {
fprintf(stderr, "Invalid filter expression in"
if (err < 0) {
fprintf(stderr, "Invalid filter expression in"
" ANDROID_LOG_TAGS\n");
android::show_help(argv[0]);
exit(-1);
@ -719,7 +535,7 @@ int main(int argc, char **argv)
for (int i = optind ; i < argc ; i++) {
err = android_log_addFilterString(g_logformat, argv[i]);
if (err < 0) {
if (err < 0) {
fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
android::show_help(argv[0]);
exit(-1);
@ -728,19 +544,21 @@ int main(int argc, char **argv)
}
dev = devices;
logger_list = android_logger_list_alloc(mode, tail_lines, 0);
while (dev) {
dev->fd = open(dev->device, mode);
if (dev->fd < 0) {
fprintf(stderr, "Unable to open log device '%s': %s\n",
dev->device, strerror(errno));
dev->logger_list = logger_list;
dev->logger = android_logger_open(logger_list,
android_name_to_log_id(dev->device));
if (!dev->logger) {
fprintf(stderr, "Unable to open log device '%s'\n", dev->device);
exit(EXIT_FAILURE);
}
if (clearLog) {
int ret;
ret = android::clearLog(dev->fd);
ret = android_logger_clear(dev->logger);
if (ret) {
perror("ioctl");
perror("clearLog");
exit(EXIT_FAILURE);
}
}
@ -748,15 +566,15 @@ int main(int argc, char **argv)
if (getLogSize) {
int size, readable;
size = android::getLogSize(dev->fd);
size = android_logger_get_log_size(dev->logger);
if (size < 0) {
perror("ioctl");
perror("getLogSize");
exit(EXIT_FAILURE);
}
readable = android::getLogReadableSize(dev->fd);
readable = android_logger_get_log_readable_size(dev->logger);
if (readable < 0) {
perror("ioctl");
perror("getLogReadableSize");
exit(EXIT_FAILURE);
}
@ -783,7 +601,51 @@ int main(int argc, char **argv)
if (needBinary)
android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
android::readLogLines(devices);
while (1) {
struct log_msg log_msg;
int ret = android_logger_list_read(logger_list, &log_msg);
if (ret == 0) {
fprintf(stderr, "read: Unexpected EOF!\n");
exit(EXIT_FAILURE);
}
if (ret < 0) {
if (ret == -EAGAIN) {
break;
}
if (ret == -EIO) {
fprintf(stderr, "read: Unexpected EOF!\n");
exit(EXIT_FAILURE);
}
if (ret == -EINVAL) {
fprintf(stderr, "read: unexpected length.\n");
exit(EXIT_FAILURE);
}
perror("logcat read");
exit(EXIT_FAILURE);
}
for(dev = devices; dev; dev = dev->next) {
if (android_name_to_log_id(dev->device) == log_msg.id()) {
break;
}
}
if (!dev) {
fprintf(stderr, "read: Unexpected log ID!\n");
exit(EXIT_FAILURE);
}
android::maybePrintStart(dev);
if (android::g_printBinary) {
android::printBinary(&log_msg);
} else {
android::processBuffer(dev, &log_msg);
}
}
android_logger_list_free(logger_list);
return 0;
}

46
logcat/tests/Android.mk Normal file
View File

@ -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)

View File

@ -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);
}