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:
commit
d2acdd82e6
|
@ -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
|
||||
|
|
|
@ -198,11 +198,6 @@ localfilesystem:<path>
|
|||
Variants of local:<path> that are used to access other Android
|
||||
socket namespaces.
|
||||
|
||||
log:<name>
|
||||
Opens one of the system logs (/dev/log/<name>) and allows the client
|
||||
to read them directly. Used to implement 'adb logcat'. The stream
|
||||
will be read-only for the client.
|
||||
|
||||
framebuffer:
|
||||
This service is used to send snapshots of the framebuffer to a client.
|
||||
It requires sufficient privileges but works as follow:
|
||||
|
|
|
@ -330,9 +330,7 @@ typedef enum {
|
|||
} BackupOperation;
|
||||
int backup_service(BackupOperation operation, char* args);
|
||||
void framebuffer_service(int fd, void *cookie);
|
||||
void log_service(int fd, void *cookie);
|
||||
void remount_service(int fd, void *cookie);
|
||||
char * get_log_file_path(const char * log_name);
|
||||
#endif
|
||||
|
||||
/* packet allocator */
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -37,7 +37,6 @@ commonSources := \
|
|||
config_utils.c \
|
||||
cpu_info.c \
|
||||
load_file.c \
|
||||
list.c \
|
||||
open_memstream.c \
|
||||
strdup16to8.c \
|
||||
strdup8to16.c \
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include <cutils/android_reboot.h>
|
||||
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
/* Check to see if /proc/mounts contains any writeable filesystems
|
||||
* backed by a block device.
|
||||
* Return true if none found, else return false.
|
||||
|
@ -102,7 +104,7 @@ static void remount_ro(void)
|
|||
}
|
||||
|
||||
|
||||
int android_reboot(int cmd, int flags, char *arg)
|
||||
int android_reboot(int cmd, int flags UNUSED, char *arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
|
@ -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_
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
10
liblog/uio.c
10
liblog/uio.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* Copyright (C) 2007-2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,8 +24,8 @@ int readv( int fd, struct iovec* vecs, int count )
|
|||
int total = 0;
|
||||
|
||||
for ( ; count > 0; count--, vecs++ ) {
|
||||
const char* buf = vecs->iov_base;
|
||||
int len = vecs->iov_len;
|
||||
char* buf = vecs->iov_base;
|
||||
int len = vecs->iov_len;
|
||||
|
||||
while (len > 0) {
|
||||
int ret = read( fd, buf, len );
|
||||
|
@ -51,8 +51,8 @@ int writev( int fd, const struct iovec* vecs, int count )
|
|||
int total = 0;
|
||||
|
||||
for ( ; count > 0; count--, vecs++ ) {
|
||||
const char* buf = (const char*)vecs->iov_base;
|
||||
int len = (int)vecs->iov_len;
|
||||
const char* buf = vecs->iov_base;
|
||||
int len = vecs->iov_len;
|
||||
|
||||
while (len > 0) {
|
||||
int ret = write( fd, buf, len );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue