Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f8252df authored by Mark Salyzyn's avatar Mark Salyzyn Committed by Android Git Automerger
Browse files

am 7bc60e38: am e89360dd: am 87e16036: Merge "liblog:...

am 7bc60e38: am e89360dd: am 87e16036: Merge "liblog: __android_log_is_loggable support global properties"

* commit '7bc60e38':
  liblog: __android_log_is_loggable support global properties
parents 143dae15 7bc60e38
Loading
Loading
Loading
Loading
+138 −21
Original line number Diff line number Diff line
@@ -15,33 +15,150 @@
*/

#include <ctype.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/system_properties.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>

#include <android/log.h>

static int __android_log_level(const char *tag, int def)
struct cache {
    const prop_info *pinfo;
    uint32_t serial;
    char c;
};

static void refresh_cache(struct cache *cache, const char *key)
{
    uint32_t serial;
    char buf[PROP_VALUE_MAX];

    if (!tag || !*tag) {
        return def;
    if (!cache->pinfo) {
        cache->pinfo = __system_property_find(key);
        if (!cache->pinfo) {
            return;
        }
    }
    serial = __system_property_serial(cache->pinfo);
    if (serial == cache->serial) {
        return;
    }
    cache->serial = serial;
    __system_property_read(cache->pinfo, 0, buf);
    cache->c = buf[0];
}

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

static int __android_log_level(const char *tag, int def)
{
    /* sizeof() is used on this array below */
    static const char log_namespace[] = "persist.log.tag.";
        char key[sizeof(log_namespace) + strlen(tag)];
    static const size_t base_offset = 8; /* skip "persist." */
    /* calculate the size of our key temporary buffer */
    const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
    /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
    char key[sizeof(log_namespace) + taglen];
    char *kp;
    size_t i;
    char c = 0;
    /*
     * Single layer cache of four properties. Priorities are:
     *    log.tag.<tag>
     *    persist.log.tag.<tag>
     *    log.tag
     *    persist.log.tag
     * Where the missing tag matches all tags and becomes the
     * system global default. We do not support ro.log.tag* .
     */
    static char *last_tag;
    static uint32_t global_serial;
    uint32_t current_global_serial;
    static struct cache tag_cache[2] = {
        { NULL, -1, 0 },
        { NULL, -1, 0 }
    };
    static struct cache global_cache[2] = {
        { NULL, -1, 0 },
        { NULL, -1, 0 }
    };

    strcpy(key, log_namespace);

    pthread_mutex_lock(&lock);

    current_global_serial = __system_property_area_serial();

    if (taglen) {
        uint32_t current_local_serial = current_global_serial;

        if (!last_tag || strcmp(last_tag, tag)) {
            /* 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].serial = -1;
                tag_cache[i].c = '\0';
            }
            free(last_tag);
            last_tag = NULL;
            current_global_serial = -1;
        }
        if (!last_tag) {
            last_tag = strdup(tag);
        }
        strcpy(key + sizeof(log_namespace) - 1, tag);

        if (__system_property_get(key + 8, buf) <= 0) {
            buf[0] = '\0';
        kp = key;
        for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
            if (current_local_serial != global_serial) {
                refresh_cache(&tag_cache[i], kp);
            }

            if (tag_cache[i].c) {
                c = tag_cache[i].c;
                break;
            }

            kp = key + base_offset;
        }
    }

    switch (toupper(c)) { /* if invalid, resort to global */
    case 'V':
    case 'D':
    case 'I':
    case 'W':
    case 'E':
    case 'F': /* Not officially supported */
    case 'A':
    case 'S':
        break;
    default:
        /* clear '.' after log.tag */
        key[sizeof(log_namespace) - 2] = '\0';

        kp = key;
        for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
            if (current_global_serial != global_serial) {
                refresh_cache(&global_cache[i], kp);
            }

            if (global_cache[i].c) {
                c = global_cache[i].c;
                break;
            }
        if (!buf[0] && __system_property_get(key, buf) <= 0) {
            buf[0] = '\0';

            kp = key + base_offset;
        }
        break;
    }
    switch (toupper(buf[0])) {

    global_serial = current_global_serial;

    pthread_mutex_unlock(&lock);

    switch (toupper(c)) {
    case 'V': return ANDROID_LOG_VERBOSE;
    case 'D': return ANDROID_LOG_DEBUG;
    case 'I': return ANDROID_LOG_INFO;
+1 −1
Original line number Diff line number Diff line
@@ -77,6 +77,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
+195 −1
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#include <fcntl.h>
#include <inttypes.h>
#include <signal.h>

#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <log/log.h>
#include <log/logger.h>
@@ -439,6 +441,7 @@ TEST(liblog, max_payload) {

    LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
                                              tag, max_payload_buf));
    sleep(2);

    struct logger_list *logger_list;

@@ -603,10 +606,14 @@ TEST(liblog, android_logger_get_) {
        if (id != android_name_to_log_id(name)) {
            continue;
        }
        fprintf(stderr, "log buffer %s\r", name);
        struct logger * logger;
        EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
        EXPECT_EQ(id, android_logger_get_id(logger));
        /* crash buffer is allowed to be empty, that is actually healthy! */
        if (android_logger_get_log_size(logger) || strcmp("crash", name)) {
            EXPECT_LT(0, android_logger_get_log_size(logger));
        }
        EXPECT_LT(0, android_logger_get_log_readable_size(logger));
        EXPECT_LT(0, android_logger_get_log_version(logger));
    }
@@ -682,3 +689,190 @@ TEST(liblog, filterRule) {

    android_log_format_free(p_format);
}

TEST(liblog, is_loggable) {
    static const char tag[] = "is_loggable";
    static const char log_namespace[] = "persist.log.tag.";
    static const size_t base_offset = 8; /* skip "persist." */
    // sizeof("string") = strlen("string") + 1
    char key[sizeof(log_namespace) + sizeof(tag) - 1];
    char hold[4][PROP_VALUE_MAX];
    static const struct {
        int level;
        char type;
    } levels[] = {
        { ANDROID_LOG_VERBOSE, 'v' },
        { ANDROID_LOG_DEBUG  , 'd' },
        { ANDROID_LOG_INFO   , 'i' },
        { ANDROID_LOG_WARN   , 'w' },
        { ANDROID_LOG_ERROR  , 'e' },
        { ANDROID_LOG_FATAL  , 'a' },
        { -1                 , 's' },
        { -2                 , 'g' }, // Illegal value, resort to default
    };

    // Set up initial test condition
    memset(hold, 0, sizeof(hold));
    snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
    property_get(key, hold[0], "");
    property_set(key, "");
    property_get(key + base_offset, hold[1], "");
    property_set(key + base_offset, "");
    strcpy(key, log_namespace);
    key[sizeof(log_namespace) - 2] = '\0';
    property_get(key, hold[2], "");
    property_set(key, "");
    property_get(key, hold[3], "");
    property_set(key + base_offset, "");

    // All combinations of level and defaults
    for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
        if (levels[i].level == -2) {
            continue;
        }
        for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
            if (levels[j].level == -2) {
                continue;
            }
            fprintf(stderr, "i=%zu j=%zu\r", i, j);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       levels[j].level));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      levels[j].level));
            }
        }
    }

    // All combinations of level and tag and global properties
    for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
        if (levels[i].level == -2) {
            continue;
        }
        for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
            char buf[2];
            buf[0] = levels[j].type;
            buf[1] = '\0';

            snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                    i, j, key, buf);
            property_set(key, buf);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)
                    || ((levels[i].level < ANDROID_LOG_DEBUG)
                        && (levels[j].level == -2))) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       ANDROID_LOG_DEBUG));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      ANDROID_LOG_DEBUG));
            }
            property_set(key, "");

            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                    i, j, key + base_offset, buf);
            property_set(key + base_offset, buf);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)
                    || ((levels[i].level < ANDROID_LOG_DEBUG)
                        && (levels[j].level == -2))) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       ANDROID_LOG_DEBUG));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      ANDROID_LOG_DEBUG));
            }
            property_set(key + base_offset, "");

            strcpy(key, log_namespace);
            key[sizeof(log_namespace) - 2] = '\0';
            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                    i, j, key, buf);
            property_set(key, buf);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)
                    || ((levels[i].level < ANDROID_LOG_DEBUG)
                        && (levels[j].level == -2))) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       ANDROID_LOG_DEBUG));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      ANDROID_LOG_DEBUG));
            }
            property_set(key, "");

            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                    i, j, key + base_offset, buf);
            property_set(key + base_offset, buf);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)
                    || ((levels[i].level < ANDROID_LOG_DEBUG)
                        && (levels[j].level == -2))) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       ANDROID_LOG_DEBUG));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      ANDROID_LOG_DEBUG));
            }
            property_set(key + base_offset, "");
        }
    }

    // All combinations of level and tag properties, but with global set to INFO
    strcpy(key, log_namespace);
    key[sizeof(log_namespace) - 2] = '\0';
    property_set(key, "I");
    snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
    for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
        if (levels[i].level == -2) {
            continue;
        }
        for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
            char buf[2];
            buf[0] = levels[j].type;
            buf[1] = '\0';

            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                    i, j, key, buf);
            property_set(key, buf);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)
                    || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
                        && (levels[j].level == -2))) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       ANDROID_LOG_DEBUG));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      ANDROID_LOG_DEBUG));
            }
            property_set(key, "");

            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                    i, j, key + base_offset, buf);
            property_set(key + base_offset, buf);
            if ((levels[i].level < levels[j].level)
                    || (levels[j].level == -1)
                    || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
                        && (levels[j].level == -2))) {
                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
                                                       ANDROID_LOG_DEBUG));
            } else {
                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
                                                      ANDROID_LOG_DEBUG));
            }
            property_set(key + base_offset, "");
        }
    }

    // reset parms
    snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
    property_set(key, hold[0]);
    property_set(key + base_offset, hold[1]);
    strcpy(key, log_namespace);
    key[sizeof(log_namespace) - 2] = '\0';
    property_set(key, hold[2]);
    property_set(key + base_offset, hold[3]);
}