Merge "liblog: resolve deadlocks" am: 5559f1d6e8
am: 650a06e01b
* commit '650a06e01bb3bf80a4223fcb9117f59fdcff4eb5':
liblog: resolve deadlocks
This commit is contained in:
commit
9ed07e3eee
6 changed files with 141 additions and 53 deletions
|
|
@ -616,7 +616,12 @@ typedef enum log_id {
|
||||||
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
|
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
|
||||||
* result of non-zero to expose a log.
|
* result of non-zero to expose a log.
|
||||||
*/
|
*/
|
||||||
int __android_log_is_loggable(int prio, const char *tag, int def);
|
/* default prio ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL if no property */
|
||||||
|
#define ANDROID_LOGGABLE_FLAG_DEFAULT_MASK 0x000F
|
||||||
|
/* Save 2 syscalls if caller guarantees to never call within signal handler */
|
||||||
|
#define ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL 0x8000
|
||||||
|
|
||||||
|
int __android_log_is_loggable(int prio, const char *tag, int flag);
|
||||||
|
|
||||||
int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
|
int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
|
||||||
uint32_t dataLen);
|
uint32_t dataLen);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
@ -97,18 +98,33 @@ typedef struct LogState {
|
||||||
*/
|
*/
|
||||||
static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static void lock()
|
static void lock(sigset_t *sigflags)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If we trigger a signal handler in the middle of locked activity and the
|
||||||
|
* signal handler logs a message, we could get into a deadlock state.
|
||||||
|
*/
|
||||||
|
sigset_t all;
|
||||||
|
|
||||||
|
sigfillset(&all);
|
||||||
|
pthread_sigmask(SIG_BLOCK, &all, sigflags);
|
||||||
pthread_mutex_lock(&fakeLogDeviceLock);
|
pthread_mutex_lock(&fakeLogDeviceLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unlock()
|
static void unlock(sigset_t *sigflags)
|
||||||
{
|
{
|
||||||
pthread_mutex_unlock(&fakeLogDeviceLock);
|
pthread_mutex_unlock(&fakeLogDeviceLock);
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, sigflags, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_SIGSET(name) sigset_t name
|
||||||
|
|
||||||
#else // !defined(_WIN32)
|
#else // !defined(_WIN32)
|
||||||
#define lock() ((void)0)
|
|
||||||
#define unlock() ((void)0)
|
#define lock(sigflags) ((void)0)
|
||||||
|
#define unlock(sigflags) ((void)0)
|
||||||
|
#define DECLARE_SIGSET(name)
|
||||||
|
|
||||||
#endif // !defined(_WIN32)
|
#endif // !defined(_WIN32)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -154,8 +170,9 @@ static LogState *fdToLogState(int fd)
|
||||||
static void deleteFakeFd(int fd)
|
static void deleteFakeFd(int fd)
|
||||||
{
|
{
|
||||||
LogState *ls;
|
LogState *ls;
|
||||||
|
DECLARE_SIGSET(sigflags);
|
||||||
|
|
||||||
lock();
|
lock(&sigflags);
|
||||||
|
|
||||||
ls = fdToLogState(fd);
|
ls = fdToLogState(fd);
|
||||||
if (ls != NULL) {
|
if (ls != NULL) {
|
||||||
|
|
@ -164,7 +181,7 @@ static void deleteFakeFd(int fd)
|
||||||
free(ls);
|
free(ls);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock();
|
unlock(&sigflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -548,12 +565,13 @@ static void showLog(LogState *state,
|
||||||
static ssize_t logWritev(int fd, const struct iovec* vector, int count)
|
static ssize_t logWritev(int fd, const struct iovec* vector, int count)
|
||||||
{
|
{
|
||||||
LogState* state;
|
LogState* state;
|
||||||
|
DECLARE_SIGSET(sigflags);
|
||||||
|
|
||||||
/* Make sure that no-one frees the LogState while we're using it.
|
/* Make sure that no-one frees the LogState while we're using it.
|
||||||
* Also guarantees that only one thread is in showLog() at a given
|
* Also guarantees that only one thread is in showLog() at a given
|
||||||
* time (if it matters).
|
* time (if it matters).
|
||||||
*/
|
*/
|
||||||
lock();
|
lock(&sigflags);
|
||||||
|
|
||||||
state = fdToLogState(fd);
|
state = fdToLogState(fd);
|
||||||
if (state == NULL) {
|
if (state == NULL) {
|
||||||
|
|
@ -598,10 +616,10 @@ static ssize_t logWritev(int fd, const struct iovec* vector, int count)
|
||||||
}
|
}
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
unlock();
|
unlock(&sigflags);
|
||||||
return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
|
return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
|
||||||
error:
|
error:
|
||||||
unlock();
|
unlock(&sigflags);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -621,8 +639,9 @@ static int logOpen(const char* pathName, int flags __unused)
|
||||||
{
|
{
|
||||||
LogState *logState;
|
LogState *logState;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
DECLARE_SIGSET(sigflags);
|
||||||
|
|
||||||
lock();
|
lock(&sigflags);
|
||||||
|
|
||||||
logState = createLogState();
|
logState = createLogState();
|
||||||
if (logState != NULL) {
|
if (logState != NULL) {
|
||||||
|
|
@ -632,7 +651,7 @@ static int logOpen(const char* pathName, int flags __unused)
|
||||||
errno = ENFILE;
|
errno = ENFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock();
|
unlock(&sigflags);
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,39 @@
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||||
#include <sys/_system_properties.h>
|
#include <sys/_system_properties.h>
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
#include <log/log.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
static void lock(sigset_t *sigflags)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we trigger a signal handler in the middle of locked activity and the
|
||||||
|
* signal handler logs a message, we could get into a deadlock state.
|
||||||
|
*/
|
||||||
|
if (sigflags) {
|
||||||
|
sigset_t all;
|
||||||
|
|
||||||
|
sigfillset(&all);
|
||||||
|
pthread_sigmask(SIG_BLOCK, &all, sigflags);
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&lock_loggable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock(sigset_t *sigflags)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&lock_loggable);
|
||||||
|
if (sigflags) {
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, sigflags, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct cache {
|
struct cache {
|
||||||
const prop_info *pinfo;
|
const prop_info *pinfo;
|
||||||
|
|
@ -49,9 +76,7 @@ static void refresh_cache(struct cache *cache, const char *key)
|
||||||
cache->c = buf[0];
|
cache->c = buf[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
static int __android_log_level(const char *tag, int flag)
|
||||||
|
|
||||||
static int __android_log_level(const char *tag, int def)
|
|
||||||
{
|
{
|
||||||
/* sizeof() is used on this array below */
|
/* sizeof() is used on this array below */
|
||||||
static const char log_namespace[] = "persist.log.tag.";
|
static const char log_namespace[] = "persist.log.tag.";
|
||||||
|
|
@ -83,10 +108,11 @@ static int __android_log_level(const char *tag, int def)
|
||||||
{ NULL, -1, 0 },
|
{ NULL, -1, 0 },
|
||||||
{ NULL, -1, 0 }
|
{ NULL, -1, 0 }
|
||||||
};
|
};
|
||||||
|
sigset_t sigflags;
|
||||||
|
|
||||||
strcpy(key, log_namespace);
|
strcpy(key, log_namespace);
|
||||||
|
|
||||||
pthread_mutex_lock(&lock);
|
lock((flag & ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL) ? NULL : &sigflags);
|
||||||
|
|
||||||
current_global_serial = __system_property_area_serial();
|
current_global_serial = __system_property_area_serial();
|
||||||
|
|
||||||
|
|
@ -156,7 +182,7 @@ static int __android_log_level(const char *tag, int def)
|
||||||
|
|
||||||
global_serial = current_global_serial;
|
global_serial = current_global_serial;
|
||||||
|
|
||||||
pthread_mutex_unlock(&lock);
|
unlock((flag & ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL) ? NULL : &sigflags);
|
||||||
|
|
||||||
switch (toupper(c)) {
|
switch (toupper(c)) {
|
||||||
case 'V': return ANDROID_LOG_VERBOSE;
|
case 'V': return ANDROID_LOG_VERBOSE;
|
||||||
|
|
@ -168,36 +194,46 @@ static int __android_log_level(const char *tag, int def)
|
||||||
case 'A': return ANDROID_LOG_FATAL;
|
case 'A': return ANDROID_LOG_FATAL;
|
||||||
case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
|
case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
|
||||||
}
|
}
|
||||||
return def;
|
return flag & ANDROID_LOGGABLE_FLAG_DEFAULT_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __android_log_is_loggable(int prio, const char *tag, int def)
|
int __android_log_is_loggable(int prio, const char *tag, int flag)
|
||||||
{
|
{
|
||||||
int logLevel = __android_log_level(tag, def);
|
int logLevel = __android_log_level(tag, flag);
|
||||||
return logLevel >= 0 && prio >= logLevel;
|
return logLevel >= 0 && prio >= logLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timestamp state generally remains constant, since a change is
|
||||||
|
* rare, we can accept a trylock failure gracefully.
|
||||||
|
*/
|
||||||
|
static pthread_mutex_t lock_timestamp = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
char android_log_timestamp()
|
char android_log_timestamp()
|
||||||
{
|
{
|
||||||
static struct cache r_time_cache = { NULL, -1, 0 };
|
static struct cache r_time_cache = { NULL, -1, 0 };
|
||||||
static struct cache p_time_cache = { NULL, -1, 0 };
|
static struct cache p_time_cache = { NULL, -1, 0 };
|
||||||
static uint32_t serial;
|
|
||||||
uint32_t current_serial;
|
|
||||||
char retval;
|
char retval;
|
||||||
|
|
||||||
pthread_mutex_lock(&lock);
|
if (pthread_mutex_trylock(&lock_timestamp)) {
|
||||||
|
/* We are willing to accept some race in this context */
|
||||||
|
if (!(retval = p_time_cache.c)) {
|
||||||
|
retval = r_time_cache.c;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static uint32_t serial;
|
||||||
|
uint32_t current_serial = __system_property_area_serial();
|
||||||
|
if (current_serial != serial) {
|
||||||
|
refresh_cache(&r_time_cache, "ro.logd.timestamp");
|
||||||
|
refresh_cache(&p_time_cache, "persist.logd.timestamp");
|
||||||
|
serial = current_serial;
|
||||||
|
}
|
||||||
|
if (!(retval = p_time_cache.c)) {
|
||||||
|
retval = r_time_cache.c;
|
||||||
|
}
|
||||||
|
|
||||||
current_serial = __system_property_area_serial();
|
pthread_mutex_unlock(&lock_timestamp);
|
||||||
if (current_serial != serial) {
|
|
||||||
refresh_cache(&r_time_cache, "ro.logd.timestamp");
|
|
||||||
refresh_cache(&p_time_cache, "persist.logd.timestamp");
|
|
||||||
serial = current_serial;
|
|
||||||
}
|
}
|
||||||
if (!(retval = p_time_cache.c)) {
|
|
||||||
retval = r_time_cache.c;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
|
|
||||||
return tolower(retval ?: 'r');
|
return tolower(retval ?: 'r');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
|
@ -54,14 +55,43 @@
|
||||||
|
|
||||||
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
|
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
|
||||||
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
|
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
|
||||||
#if !defined(_WIN32)
|
|
||||||
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __unused
|
#ifndef __unused
|
||||||
#define __unused __attribute__((__unused__))
|
#define __unused __attribute__((__unused__))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
static void lock(sigset_t *sigflags)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we trigger a signal handler in the middle of locked activity and the
|
||||||
|
* signal handler logs a message, we could get into a deadlock state.
|
||||||
|
*/
|
||||||
|
sigset_t all;
|
||||||
|
|
||||||
|
sigfillset(&all);
|
||||||
|
pthread_sigmask(SIG_BLOCK, &all, sigflags);
|
||||||
|
pthread_mutex_lock(&log_init_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unlock(sigset_t *sigflags)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&log_init_lock);
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, sigflags, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECLARE_SIGSET(name) sigset_t name
|
||||||
|
|
||||||
|
#else /* !defined(_WIN32) */
|
||||||
|
|
||||||
|
#define lock(sigflags) ((void)0)
|
||||||
|
#define unlock(sigflags) ((void)0)
|
||||||
|
#define DECLARE_SIGSET(name)
|
||||||
|
|
||||||
|
#endif /* !defined(_WIN32) */
|
||||||
|
|
||||||
#if FAKE_LOG_DEVICE
|
#if FAKE_LOG_DEVICE
|
||||||
static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
|
static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 };
|
||||||
#else
|
#else
|
||||||
|
|
@ -275,17 +305,15 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
|
||||||
*/
|
*/
|
||||||
ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
|
ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
DECLARE_SIGSET(sigflags);
|
||||||
|
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
if (ret == -ENOTCONN) {
|
if (ret == -ENOTCONN) {
|
||||||
#if !defined(_WIN32)
|
lock(&sigflags);
|
||||||
pthread_mutex_lock(&log_init_lock);
|
|
||||||
#endif
|
|
||||||
close(logd_fd);
|
close(logd_fd);
|
||||||
logd_fd = -1;
|
logd_fd = -1;
|
||||||
ret = __write_to_log_initialize();
|
ret = __write_to_log_initialize();
|
||||||
#if !defined(_WIN32)
|
unlock(&sigflags);
|
||||||
pthread_mutex_unlock(&log_init_lock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -329,18 +357,16 @@ const char *android_log_id_to_name(log_id_t log_id)
|
||||||
|
|
||||||
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
|
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
|
||||||
{
|
{
|
||||||
#if !defined(_WIN32)
|
DECLARE_SIGSET(sigflags);
|
||||||
pthread_mutex_lock(&log_init_lock);
|
|
||||||
#endif
|
lock(&sigflags);
|
||||||
|
|
||||||
if (write_to_log == __write_to_log_init) {
|
if (write_to_log == __write_to_log_init) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = __write_to_log_initialize();
|
ret = __write_to_log_initialize();
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
#if !defined(_WIN32)
|
unlock(&sigflags);
|
||||||
pthread_mutex_unlock(&log_init_lock);
|
|
||||||
#endif
|
|
||||||
#if (FAKE_LOG_DEVICE == 0)
|
#if (FAKE_LOG_DEVICE == 0)
|
||||||
if (pstore_fd >= 0) {
|
if (pstore_fd >= 0) {
|
||||||
__write_to_log_daemon(log_id, vec, nr);
|
__write_to_log_daemon(log_id, vec, nr);
|
||||||
|
|
@ -352,9 +378,7 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
|
||||||
write_to_log = __write_to_log_daemon;
|
write_to_log = __write_to_log_daemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
unlock(&sigflags);
|
||||||
pthread_mutex_unlock(&log_init_lock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return write_to_log(log_id, vec, nr);
|
return write_to_log(log_id, vec, nr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,9 @@ int LogBuffer::log(log_id_t log_id, log_time realtime,
|
||||||
prio = *msg;
|
prio = *msg;
|
||||||
tag = msg + 1;
|
tag = msg + 1;
|
||||||
}
|
}
|
||||||
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
|
if (!__android_log_is_loggable(prio, tag,
|
||||||
|
ANDROID_LOG_VERBOSE |
|
||||||
|
ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL)) {
|
||||||
// Log traffic received to total
|
// Log traffic received to total
|
||||||
pthread_mutex_lock(&mLogElementsLock);
|
pthread_mutex_lock(&mLogElementsLock);
|
||||||
stats.add(elem);
|
stats.add(elem);
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,9 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer,
|
||||||
LogBuffer *parent) {
|
LogBuffer *parent) {
|
||||||
static const char tag[] = "chatty";
|
static const char tag[] = "chatty";
|
||||||
|
|
||||||
if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, ANDROID_LOG_VERBOSE)) {
|
if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag,
|
||||||
|
ANDROID_LOG_VERBOSE |
|
||||||
|
ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue