liblog: Add private interfaces for buffer size properties
Add private function __android_logger_get_buffer_size() to read
properties and compose the default buffer size. This interface
complements the existing android_looger_get_size() which returns
the logd setting which can differ at runtime. For use in logd
and dumpstate. Side effect is we also add the private functions
__android_logger_property_get_bool() and
__android_logger_valid_buffer_size() for reuse in logd.
Test: gTest liblog-unit-test, logd-unit-tests and logcat-unit-tests in
combination with commit 'logd: Use private interfaces for
buffer size properties'
Bug: 31750617
Change-Id: Id95cb68f775ef6b427c122e10f6f8291d336d184
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
index 94095d1..02764d3 100644
--- a/include/private/android_logger.h
+++ b/include/private/android_logger.h
@@ -21,6 +21,7 @@
/* Android private interfaces */
+#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
@@ -132,6 +133,20 @@
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 /* <key>, persist.<key>, ro.<key> */
+#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 3dfc6a6..132d96f 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -16,12 +16,16 @@
#include <ctype.h>
#include <pthread.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
+#include <unistd.h>
#include <android/log.h>
+#include <log/logger.h>
+#include <private/android_logger.h>
#include "log_portability.h"
@@ -47,12 +51,16 @@
}
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 @@
#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 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 @@
/* 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 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 @@
* 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 @@
|| ((len < sizeof(last_tag)) && last_tag[len])) {
/* invalidate log.tag.<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 @@
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 @@
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 @@
}
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(int prio,
- const char *tag,
+ const char* tag,
int default_prio)
{
int logLevel = __android_log_level(tag,
@@ -277,18 +285,18 @@
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 @@
* 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 @@
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 @@
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 @@
*/
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 @@
* 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 @@
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;
}