diff --git a/include/private/android_logger.h b/include/private/android_logger.h index 94095d1df..02764d3d3 100644 --- a/include/private/android_logger.h +++ b/include/private/android_logger.h @@ -21,6 +21,7 @@ /* Android private interfaces */ +#include #include #include @@ -132,6 +133,20 @@ int __android_log_security(); /* Device Owner is present */ int __android_log_is_debuggable(); +#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1 +#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */ +#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */ +#define BOOL_DEFAULT_FLAG_PERSIST 0x2 /* , persist., ro. */ +#define BOOL_DEFAULT_FLAG_ENG 0x4 /* off for user */ +#define BOOL_DEFAULT_FLAG_SVELTE 0x8 /* off for low_ram */ +bool __android_logger_property_get_bool(const char *key, int flag); + +#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform */ +#define LOG_BUFFER_MIN_SIZE (64 * 1024UL) +#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL) +unsigned long __android_logger_get_buffer_size(log_id_t logId); +bool __android_logger_valid_buffer_size(unsigned long value); + #if defined(__cplusplus) } #endif diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c index 3dfc6a6f7..132d96f38 100644 --- a/liblog/log_is_loggable.c +++ b/liblog/log_is_loggable.c @@ -16,12 +16,16 @@ #include #include +#include #include #include #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include +#include #include +#include +#include #include "log_portability.h" @@ -47,12 +51,16 @@ static void unlock() } struct cache { - const prop_info *pinfo; + const prop_info* pinfo; uint32_t serial; +}; + +struct cache_char { + struct cache cache; unsigned char c; }; -static int check_cache(struct cache *cache) +static int check_cache(struct cache* cache) { return cache->pinfo && __system_property_serial(cache->pinfo) != cache->serial; @@ -61,18 +69,18 @@ static int check_cache(struct cache *cache) #define BOOLEAN_TRUE 0xFF #define BOOLEAN_FALSE 0xFE -static void refresh_cache(struct cache *cache, const char *key) +static void refresh_cache(struct cache_char* cache, const char* key) { char buf[PROP_VALUE_MAX]; - if (!cache->pinfo) { - cache->pinfo = __system_property_find(key); - if (!cache->pinfo) { + if (!cache->cache.pinfo) { + cache->cache.pinfo = __system_property_find(key); + if (!cache->cache.pinfo) { return; } } - cache->serial = __system_property_serial(cache->pinfo); - __system_property_read(cache->pinfo, 0, buf); + cache->cache.serial = __system_property_serial(cache->cache.pinfo); + __system_property_read(cache->cache.pinfo, 0, buf); switch(buf[0]) { case 't': case 'T': cache->c = strcasecmp(buf + 1, "rue") ? buf[0] : BOOLEAN_TRUE; @@ -85,7 +93,7 @@ static void refresh_cache(struct cache *cache, const char *key) } } -static int __android_log_level(const char *tag, size_t len, int default_prio) +static int __android_log_level(const char* tag, size_t len, int default_prio) { /* sizeof() is used on this array below */ static const char log_namespace[] = "persist.log.tag."; @@ -93,8 +101,8 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) /* calculate the size of our key temporary buffer */ const size_t taglen = tag ? len : 0; /* sizeof(log_namespace) = strlen(log_namespace) + 1 */ - char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */ - char *kp; + char key[sizeof(log_namespace) + taglen]; /* may be > PROP_NAME_MAX */ + char* kp; size_t i; char c = 0; /* @@ -110,8 +118,8 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) static uint32_t global_serial; /* some compilers erroneously see uninitialized use. !not_locked */ uint32_t current_global_serial = 0; - static struct cache tag_cache[2]; - static struct cache global_cache[2]; + static struct cache_char tag_cache[2]; + static struct cache_char global_cache[2]; int change_detected; int global_change_detected; int not_locked; @@ -125,12 +133,12 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) * check all known serial numbers to changes. */ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { - if (check_cache(&tag_cache[i])) { + if (check_cache(&tag_cache[i].cache)) { change_detected = 1; } } for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) { - if (check_cache(&global_cache[i])) { + if (check_cache(&global_cache[i].cache)) { global_change_detected = 1; } } @@ -154,7 +162,7 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) || ((len < sizeof(last_tag)) && last_tag[len])) { /* invalidate log.tag. cache */ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { - tag_cache[i].pinfo = NULL; + tag_cache[i].cache.pinfo = NULL; tag_cache[i].c = '\0'; } last_tag[0] = '\0'; @@ -174,11 +182,11 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) kp = key; for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { - struct cache *cache = &tag_cache[i]; - struct cache temp_cache; + struct cache_char* cache = &tag_cache[i]; + struct cache_char temp_cache; if (not_locked) { - temp_cache.pinfo = NULL; + temp_cache.cache.pinfo = NULL; temp_cache.c = '\0'; cache = &temp_cache; } @@ -212,13 +220,13 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) kp = key; for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) { - struct cache *cache = &global_cache[i]; - struct cache temp_cache; + struct cache_char* cache = &global_cache[i]; + struct cache_char temp_cache; if (not_locked) { temp_cache = *cache; - if (temp_cache.pinfo != cache->pinfo) { /* check atomic */ - temp_cache.pinfo = NULL; + if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */ + temp_cache.cache.pinfo = NULL; temp_cache.c = '\0'; } cache = &temp_cache; @@ -257,7 +265,7 @@ static int __android_log_level(const char *tag, size_t len, int default_prio) } LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, - const char *tag, size_t len, + const char* tag, size_t len, int default_prio) { int logLevel = __android_log_level(tag, len, default_prio); @@ -265,7 +273,7 @@ LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, } LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, - const char *tag, + const char* tag, int default_prio) { int logLevel = __android_log_level(tag, @@ -277,18 +285,18 @@ LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() { static uint32_t serial; - static struct cache tag_cache; + static struct cache_char tag_cache; static const char key[] = "ro.debuggable"; int ret; if (tag_cache.c) { /* ro property does not change after set */ ret = tag_cache.c == '1'; } else if (lock()) { - struct cache temp_cache = { NULL, -1, '\0' }; + struct cache_char temp_cache = { { NULL, -1 }, '\0' }; refresh_cache(&temp_cache, key); ret = temp_cache.c == '1'; } else { - int change_detected = check_cache(&tag_cache); + int change_detected = check_cache(&tag_cache.cache); uint32_t current_serial = __system_property_area_serial(); if (current_serial != serial) { change_detected = 1; @@ -310,17 +318,17 @@ LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() * Since a change is rare, we will accept a trylock failure gracefully. * Use a separate lock from is_loggable to keep contention down b/25563384. */ -struct cache2 { +struct cache2_char { pthread_mutex_t lock; uint32_t serial; - const char *key_persist; - struct cache cache_persist; - const char *key_ro; - struct cache cache_ro; - unsigned char (*const evaluate)(const struct cache2 *self); + const char* key_persist; + struct cache_char cache_persist; + const char* key_ro; + struct cache_char cache_ro; + unsigned char (*const evaluate)(const struct cache2_char *self); }; -static inline unsigned char do_cache2(struct cache2 *self) +static inline unsigned char do_cache2_char(struct cache2_char *self) { uint32_t current_serial; int change_detected; @@ -331,8 +339,8 @@ static inline unsigned char do_cache2(struct cache2 *self) return self->evaluate(self); } - change_detected = check_cache(&self->cache_persist) - || check_cache(&self->cache_ro); + change_detected = check_cache(&self->cache_persist.cache) + || check_cache(&self->cache_ro.cache); current_serial = __system_property_area_serial(); if (current_serial != self->serial) { change_detected = 1; @@ -349,7 +357,7 @@ static inline unsigned char do_cache2(struct cache2 *self) return c; } -static unsigned char evaluate_persist_ro(const struct cache2 *self) +static unsigned char evaluate_persist_ro(const struct cache2_char *self) { unsigned char c = self->cache_persist.c; @@ -366,17 +374,17 @@ static unsigned char evaluate_persist_ro(const struct cache2 *self) */ LIBLOG_ABI_PUBLIC clockid_t android_log_clockid() { - static struct cache2 clockid = { + static struct cache2_char clockid = { PTHREAD_MUTEX_INITIALIZER, 0, "persist.logd.timestamp", - { NULL, -1, '\0' }, + { { NULL, -1 }, '\0' }, "ro.logd.timestamp", - { NULL, -1, '\0' }, + { { NULL, -1 }, '\0' }, evaluate_persist_ro }; - return (tolower(do_cache2(&clockid)) == 'm') + return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME; } @@ -385,7 +393,7 @@ LIBLOG_ABI_PUBLIC clockid_t android_log_clockid() * Security state generally remains constant, but the DO must be able * to turn off logging should it become spammy after an attack is detected. */ -static unsigned char evaluate_security(const struct cache2 *self) +static unsigned char evaluate_security(const struct cache2_char *self) { unsigned char c = self->cache_ro.c; @@ -394,15 +402,265 @@ static unsigned char evaluate_security(const struct cache2 *self) LIBLOG_ABI_PUBLIC int __android_log_security() { - static struct cache2 security = { + static struct cache2_char security = { PTHREAD_MUTEX_INITIALIZER, 0, "persist.logd.security", - { NULL, -1, BOOLEAN_FALSE }, + { { NULL, -1 }, BOOLEAN_FALSE }, "ro.device_owner", - { NULL, -1, BOOLEAN_FALSE }, + { { NULL, -1 }, BOOLEAN_FALSE }, evaluate_security }; - return do_cache2(&security); + return do_cache2_char(&security); +} + +/* + * Interface that represents the logd buffer size determination so that others + * need not guess our intentions. + */ + +/* Property helper */ +static bool check_flag(const char* prop, const char* flag) { + const char* cp = strcasestr(prop, flag); + if (!cp) { + return false; + } + /* We only will document comma (,) */ + static const char sep[] = ",:;|+ \t\f"; + if ((cp != prop) && !strchr(sep, cp[-1])) { + return false; + } + cp += strlen(flag); + return !*cp || !!strchr(sep, *cp); +} + +/* cache structure */ +struct cache_property { + struct cache cache; + char property[PROP_VALUE_MAX]; +}; + +static void refresh_cache_property(struct cache_property* cache, const char* key) +{ + if (!cache->cache.pinfo) { + cache->cache.pinfo = __system_property_find(key); + if (!cache->cache.pinfo) { + return; + } + } + cache->cache.serial = __system_property_serial(cache->cache.pinfo); + __system_property_read(cache->cache.pinfo, 0, cache->property); +} + +/* get boolean with the logger twist that supports eng adjustments */ +LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key, + int flag) +{ + struct cache_property property = { { NULL, -1 }, { 0 } }; + if (flag & BOOL_DEFAULT_FLAG_PERSIST) { + char newkey[PROP_NAME_MAX]; + snprintf(newkey, sizeof(newkey), "ro.%s", key); + refresh_cache_property(&property, newkey); + property.cache.pinfo = NULL; + property.cache.serial = -1; + snprintf(newkey, sizeof(newkey), "persist.%s", key); + refresh_cache_property(&property, newkey); + property.cache.pinfo = NULL; + property.cache.serial = -1; + } + + refresh_cache_property(&property, key); + + if (check_flag(property.property, "true")) { + return true; + } + if (check_flag(property.property, "false")) { + return false; + } + if (check_flag(property.property, "eng")) { + flag |= BOOL_DEFAULT_FLAG_ENG; + } + /* this is really a "not" flag */ + if (check_flag(property.property, "svelte")) { + flag |= BOOL_DEFAULT_FLAG_SVELTE; + } + + /* Sanity Check */ + if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) { + flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE; + flag |= BOOL_DEFAULT_TRUE; + } + + if ((flag & BOOL_DEFAULT_FLAG_SVELTE) + && __android_logger_property_get_bool("ro.config.low_ram", + BOOL_DEFAULT_FALSE)) { + return false; + } + if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) { + return false; + } + + return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE; +} + +LIBLOG_ABI_PRIVATE bool __android_logger_valid_buffer_size(unsigned long value) +{ + static long pages, pagesize; + unsigned long maximum; + + if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) { + return false; + } + + if (!pages) { + pages = sysconf(_SC_PHYS_PAGES); + } + if (pages < 1) { + return true; + } + + if (!pagesize) { + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize <= 1) { + pagesize = PAGE_SIZE; + } + } + + /* maximum memory impact a somewhat arbitrary ~3% */ + pages = (pages + 31) / 32; + maximum = pages * pagesize; + + if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) { + return true; + } + + return value <= maximum; +} + +struct cache2_property_size { + pthread_mutex_t lock; + uint32_t serial; + const char* key_persist; + struct cache_property cache_persist; + const char* key_ro; + struct cache_property cache_ro; + unsigned long (*const evaluate)(const struct cache2_property_size* self); +}; + +static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) +{ + uint32_t current_serial; + int change_detected; + unsigned long v; + + if (pthread_mutex_trylock(&self->lock)) { + /* We are willing to accept some race in this context */ + return self->evaluate(self); + } + + change_detected = check_cache(&self->cache_persist.cache) + || check_cache(&self->cache_ro.cache); + current_serial = __system_property_area_serial(); + if (current_serial != self->serial) { + change_detected = 1; + } + if (change_detected) { + refresh_cache_property(&self->cache_persist, self->key_persist); + refresh_cache_property(&self->cache_ro, self->key_ro); + self->serial = current_serial; + } + v = self->evaluate(self); + + pthread_mutex_unlock(&self->lock); + + return v; +} + +static unsigned long property_get_size_from_cache(const struct cache_property* cache) +{ + char* cp; + unsigned long value = strtoul(cache->property, &cp, 10); + + switch(*cp) { + case 'm': + case 'M': + value *= 1024; + /* FALLTHRU */ + case 'k': + case 'K': + value *= 1024; + /* FALLTHRU */ + case '\0': + break; + + default: + value = 0; + } + + if (!__android_logger_valid_buffer_size(value)) { + value = 0; + } + + return value; +} + +static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) +{ + unsigned long size = property_get_size_from_cache(&self->cache_persist); + if (size) { + return size; + } + return property_get_size_from_cache(&self->cache_ro); +} + +LIBLOG_ABI_PRIVATE unsigned long __android_logger_get_buffer_size(log_id_t logId) +{ + static const char global_tunable[] = "persist.logd.size"; /* Settings App */ + static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */ + static struct cache2_property_size global = { + PTHREAD_MUTEX_INITIALIZER, + 0, + global_tunable, + { { NULL, -1 }, {} }, + global_default, + { { NULL, -1 }, {} }, + evaluate_property_get_size + }; + char key_persist[PROP_NAME_MAX]; + char key_ro[PROP_NAME_MAX]; + struct cache2_property_size local = { + PTHREAD_MUTEX_INITIALIZER, + 0, + key_persist, + { { NULL, -1 }, {} }, + key_ro, + { { NULL, -1 }, {} }, + evaluate_property_get_size + }; + unsigned long property_size, default_size; + + default_size = do_cache2_property_size(&global); + if (!default_size) { + default_size = __android_logger_property_get_bool("ro.config.low_ram", + BOOL_DEFAULT_FALSE) + ? LOG_BUFFER_MIN_SIZE /* 64K */ + : LOG_BUFFER_SIZE; /* 256K */ + } + + snprintf(key_persist, sizeof(key_persist), "%s.%s", + global_tunable, android_log_id_to_name(logId)); + snprintf(key_ro, sizeof(key_ro), "%s.%s", + global_default, android_log_id_to_name(logId)); + property_size = do_cache2_property_size(&local); + + if (!property_size) { + property_size = default_size; + } + + if (!property_size) { + property_size = LOG_BUFFER_SIZE; + } + + return property_size; }