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

Commit 8b99ef6c authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "liblog: event_tag_map use unordered_map"

parents bb612a35 700fb876
Loading
Loading
Loading
Loading
+217 −357
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * Copyright (C) 2007-2016 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,440 +24,300 @@
#include <string.h>
#include <sys/mman.h>

#include <android/log.h>
#include <experimental/string_view>
#include <functional>
#include <unordered_map>

#include <log/event_tag_map.h>

#include "log_portability.h"

#define OUT_TAG "EventTagMap"

/*
 * Single entry.
 */
typedef struct EventTag {
    uint32_t tagIndex;
    char*    tagStr;
    size_t   tagLen;
    char*    fmtStr;
    size_t   fmtLen;
} EventTag;
typedef std::experimental::string_view MapString;

/*
 * Map.
 */
typedef std::pair<MapString, MapString> TagFmt;

template <> struct _LIBCPP_TYPE_VIS_ONLY std::hash<TagFmt>
        : public std::unary_function<const TagFmt&, size_t> {
    _LIBCPP_INLINE_VISIBILITY
    size_t operator()(const TagFmt& __t) const _NOEXCEPT {
        // Tag is typically unique.  Will cost us an extra 100ns for the
        // unordered_map lookup if we instead did a hash that combined
        // both of tag and fmt members, e.g.:
        //
        // return std::hash<MapString>()(__t.first) ^
        //        std::hash<MapString>()(__t.second);
        return std::hash<MapString>()(__t.first);
    }
};

// Map
struct EventTagMap {
    /* memory-mapped source file; we get strings from here */
    // memory-mapped source file; we get strings from here
    void*  mapAddr;
    size_t mapLen;

    /* array of event tags, sorted numerically by tag index */
    EventTag*       tagArray;
    int             numTags;
};
private:
    std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;

/* fwd */
static int processFile(EventTagMap* map);
static int countMapLines(const EventTagMap* map);
static int parseMapLines(EventTagMap* map);
static int scanTagLine(char** pData, EventTag* tag, int lineNum);
static int sortTags(EventTagMap* map);
public:
    EventTagMap() : mapAddr(NULL), mapLen(0) { }

/*
 * Open the map file and allocate a structure to manage it.
 *
 * We create a private mapping because we want to terminate the log tag
 * strings with '\0'.
 */
LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
{
    EventTagMap* newTagMap;
    off_t end;
    int save_errno;
    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;

    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
    if (fd < 0) {
        save_errno = errno;
        fprintf(stderr, "%s: unable to open map '%s': %s\n",
                OUT_TAG, tagfile, strerror(save_errno));
        goto fail_errno;
    ~EventTagMap() {
        Idx2TagFmt.clear();
        if (mapAddr) {
            munmap(mapAddr, mapLen);
            mapAddr = 0;
        }

    end = lseek(fd, 0L, SEEK_END);
    save_errno = errno;
    (void) lseek(fd, 0L, SEEK_SET);
    if (end < 0) {
        fprintf(stderr, "%s: unable to seek map '%s' %s\n",
                OUT_TAG, tagfile, strerror(save_errno));
        goto fail_close;
    }

    newTagMap = (EventTagMap*)calloc(1, sizeof(EventTagMap));
    if (newTagMap == NULL) {
        save_errno = errno;
        goto fail_close;
    }
    bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
    const TagFmt* find(uint32_t tag) const;
};

    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    save_errno = errno;
    close(fd);
    fd = -1;
    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
        fprintf(stderr, "%s: mmap(%s) failed: %s\n",
                OUT_TAG, tagfile, strerror(save_errno));
        goto fail_free;
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
    it = Idx2TagFmt.find(tag);
    if (it != Idx2TagFmt.end()) {
        if (verbose) {
            fprintf(stderr,
                    OUT_TAG ": duplicate tag entries %" PRIu32
                        ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
                    it->first,
                    (int)it->second.first.length(), it->second.first.data(),
                    (int)it->second.second.length(), it->second.second.data(),
                    tag,
                    (int)tagfmt.first.length(), tagfmt.first.data(),
                    (int)tagfmt.second.length(), tagfmt.second.data());
        }

    newTagMap->mapLen = end;

    if (processFile(newTagMap) != 0) goto fail_unmap;

    return newTagMap;

fail_unmap:
    munmap(newTagMap->mapAddr, newTagMap->mapLen);
    save_errno = EINVAL;
fail_free:
    free(newTagMap);
fail_close:
    close(fd);
fail_errno:
    errno = save_errno;
fail:
    return NULL;
        return false;
    }

/*
 * Close the map.
 */
LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
{
    if (map == NULL) return;

    munmap(map->mapAddr, map->mapLen);
    free(map->tagArray);
    free(map);
    Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
    return true;
}

/*
 * Look up an entry in the map.
 *
 * The entries are sorted by tag number, so we can do a binary search.
 */
LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
                                                         size_t *len,
                                                         unsigned int tag)
{
    int lo = 0;
    int hi = map->numTags - 1;

    while (lo <= hi) {
        int mid = (lo + hi) / 2;
        int cmp = map->tagArray[mid].tagIndex - tag;

        if (cmp < 0) {
            /* tag is bigger */
            lo = mid + 1;
        } else if (cmp > 0) {
            /* tag is smaller */
            hi = mid - 1;
        } else {
            /* found */
            if (len) *len = map->tagArray[mid].tagLen;
            /*
             * b/31456426 to check if gTest can detect copy-on-write issue
             * add the following line to break us:
             *     map->tagArray[mid].tagStr[map->tagArray[mid].tagLen] = '\0';
             * or explicitly use deprecated android_lookupEventTag().
             */
            return map->tagArray[mid].tagStr;
        }
    }

    errno = ENOENT;
    if (len) *len = 0;
    return NULL;
const TagFmt* EventTagMap::find(uint32_t tag) const {
    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
    it = Idx2TagFmt.find(tag);
    if (it == Idx2TagFmt.end()) return NULL;
    return &(it->second);
}

/*
 * Look up an entry in the map.
 *
 * The entries are sorted by tag number, so we can do a binary search.
 */
LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
    const EventTagMap* map, size_t *len, unsigned int tag)
{
    int lo = 0;
    int hi = map->numTags - 1;

    while (lo <= hi) {
        int mid = (lo + hi) / 2;
        int cmp = map->tagArray[mid].tagIndex - tag;

        if (cmp < 0) {
            /* tag is bigger */
            lo = mid + 1;
        } else if (cmp > 0) {
            /* tag is smaller */
            hi = mid - 1;
        } else {
            /* found */
            if (len) *len = map->tagArray[mid].fmtLen;
            return map->tagArray[mid].fmtStr;
        }
// Scan one tag line.
//
// "*pData" should be pointing to the first digit in the tag number.  On
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
// Returns 0 on success, nonzero on failure.
static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
    char* cp;
    unsigned long val = strtoul(*pData, &cp, 10);
    if (cp == *pData) {
        fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
        errno = EINVAL;
        return -1;
    }

    errno = ENOENT;
    if (len) *len = 0;
    return NULL;
    uint32_t tagIndex = val;
    if (tagIndex != val) {
        fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
        errno = ERANGE;
        return -1;
    }

LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
                                                     unsigned int tag)
{
    size_t len;
    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
    char* cp;

    if (!tagStr) return tagStr;
    cp = (char*)tagStr;
    cp += len;
    if (*cp) *cp = '\0'; /* Trigger copy on write :-( */
    return tagStr;
    while ((*++cp != '\n') && isspace(*cp)) {
    }

/*
 * Crunch through the file, parsing the contents and creating a tag index.
 */
static int processFile(EventTagMap* map)
{
    /* get a tag count */
    map->numTags = countMapLines(map);
    if (map->numTags < 0) {
        errno = ENOENT;
    if (*cp == '\n') {
        fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
        errno = EINVAL;
        return -1;
    }

    /* allocate storage for the tag index array */
    map->tagArray = (EventTag*)calloc(1, sizeof(EventTag) * map->numTags);
    if (map->tagArray == NULL) return -1;

    /* parse the file, null-terminating tag strings */
    if (parseMapLines(map) != 0) return -1;
    const char* tag = cp;
    // Determine whether "c" is a valid tag char.
    while (isalnum(*++cp) || (*cp == '_')) { }
    size_t tagLen = cp - tag;

    /* sort the tags and check for duplicates */
    if (sortTags(map) != 0) return -1;
    if (!isspace(*cp)) {
        fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
        errno = EINVAL;
        return -1;
    }

    return 0;
    while (isspace(*cp) && (*cp != '\n')) ++cp;
    const char* fmt = NULL;
    size_t fmtLen = 0;
    if (*cp != '#') {
        fmt = cp;
        while ((*cp != '\n') && (*cp != '#')) ++cp;
        while ((cp > fmt) && isspace(*(cp - 1))) --cp;
        fmtLen = cp - fmt;
    }

/*
 * Run through all lines in the file, determining whether they're blank,
 * comments, or possibly have a tag entry.
 *
 * This is a very "loose" scan.  We don't try to detect syntax errors here.
 * The later pass is more careful, but the number of tags found there must
 * match the number of tags found here.
 *
 * Returns the number of potential tag entries found.
 */
static int countMapLines(const EventTagMap* map)
{
    const char* cp = (const char*) map->mapAddr;
    const char* endp = cp + map->mapLen;
    int numTags = 0;
    int unknown = 1;
    while (*cp != '\n') ++cp;
#ifdef DEBUG
    fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
#endif
    *pData = cp;

    while (cp < endp) {
        if (*cp == '\n') {
            unknown = 1;
        } else if (unknown) {
            if (isdigit(*cp)) {
                /* looks like a tag to me */
                numTags++;
                unknown = 0;
            } else if (isspace(*cp)) {
                /* might be leading whitespace before tag num, keep going */
            } else {
                /* assume comment; second pass can complain in detail */
                unknown = 0;
            }
        } else {
            /* we've made up our mind; just scan to end of line */
    if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
            MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
        return 0;
    }
        cp++;
    errno = EMLINK;
    return -1;
}

    return numTags;
}
// Parse the tags out of the file.
static int parseMapLines(EventTagMap* map) {
    char* cp = static_cast<char*>(map->mapAddr);
    size_t len = map->mapLen;
    char* endp = cp + len;

/*
 * Parse the tags out of the file.
 */
static int parseMapLines(EventTagMap* map)
{
    int tagNum, lineStart, lineNum;
    char* cp = (char*) map->mapAddr;
    char* endp = cp + map->mapLen;

    /* insist on EOL at EOF; simplifies parsing and null-termination */
    if (*(endp - 1) != '\n') {
        fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
    // insist on EOL at EOF; simplifies parsing and null-termination
    if (!len || (*(endp - 1) != '\n')) {
#ifdef DEBUG
        fprintf(stderr, OUT_TAG ": map file missing EOL on last line\n");
#endif
        errno = EINVAL;
        return -1;
    }

    tagNum = 0;
    lineStart = 1;
    lineNum = 1;
    bool lineStart = true;
    int lineNum = 1;
    while (cp < endp) {
        if (*cp == '\n') {
            lineStart = 1;
            lineStart = true;
            lineNum++;
        } else if (lineStart) {
            if (*cp == '#') {
                /* comment; just scan to end */
                lineStart = 0;
                // comment; just scan to end
                lineStart = false;
            } else if (isdigit(*cp)) {
                /* looks like a tag; scan it out */
                if (tagNum >= map->numTags) {
                    fprintf(stderr,
                        "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
                    errno = EMFILE;
                    return -1;
                }
                if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) {
                // looks like a tag; scan it out
                if (scanTagLine(map, &cp, lineNum) != 0) {
                    return -1;
                }
                tagNum++;
                lineNum++;      // we eat the '\n'
                /* leave lineStart==1 */
                // leave lineStart==true
            } else if (isspace(*cp)) {
                /* looks like leading whitespace; keep scanning */
                // looks like leading whitespace; keep scanning
            } else {
                fprintf(stderr,
                    "%s: unexpected chars (0x%02x) in tag number on line %d\n",
                    OUT_TAG, *cp, lineNum);
                        OUT_TAG ": unexpected chars (0x%02x) in tag number on line %d\n",
                        *cp, lineNum);
                errno = EINVAL;
                return -1;
            }
        } else {
            /* this is a blank or comment line */
            // this is a blank or comment line
        }
        cp++;
    }

    if (tagNum != map->numTags) {
        fprintf(stderr, "%s: parsed %d tags, expected %d\n",
            OUT_TAG, tagNum, map->numTags);
        errno = EINVAL;
        return -1;
    }

    return 0;
}

/*
 * Scan one tag line.
 *
 * "*pData" should be pointing to the first digit in the tag number.  On
 * successful return, it will be pointing to the last character in the
 * tag line (i.e. the character before the start of the next line).
 *
 * Returns 0 on success, nonzero on failure.
 */
static int scanTagLine(char** pData, EventTag* tag, int lineNum)
{
    char* cp;
// Open the map file and allocate a structure to manage it.
//
// We create a private mapping because we want to terminate the log tag
// strings with '\0'.
LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
    int save_errno;

    unsigned long val = strtoul(*pData, &cp, 10);
    if (cp == *pData) {
        fprintf(stderr, "%s: malformed tag number on line %d\n", OUT_TAG, lineNum);
        errno = EINVAL;
        return -1;
    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
    if (fd < 0) {
        save_errno = errno;
        fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
                tagfile, strerror(save_errno));
        errno = save_errno;
        return NULL;
    }

    tag->tagIndex = val;
    if (tag->tagIndex != val) {
        fprintf(stderr, "%s: tag number too large on line %d\n", OUT_TAG, lineNum);
        errno = ERANGE;
        return -1;
    off_t end = lseek(fd, 0L, SEEK_END);
    save_errno = errno;
    (void)lseek(fd, 0L, SEEK_SET);
    if (end < 0) {
        fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
                tagfile, strerror(save_errno));
        close(fd);
        errno = save_errno;
        return NULL;
    }

    while ((*++cp != '\n') && isspace(*cp)) {
    EventTagMap* newTagMap = new EventTagMap;
    if (newTagMap == NULL) {
        save_errno = errno;
        close(fd);
        errno = save_errno;
        return NULL;
    }

    if (*cp == '\n') {
        fprintf(stderr, "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
        errno = EINVAL;
        return -1;
    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
                              MAP_PRIVATE, fd, 0);
    save_errno = errno;
    close(fd);
    fd = -1;
    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
        fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
                tagfile, strerror(save_errno));
        delete newTagMap;
        errno = save_errno;
        return NULL;
    }

    tag->tagStr = cp;
    newTagMap->mapLen = end;

    /* Determine whether "c" is a valid tag char. */
    while (isalnum(*++cp) || (*cp == '_')) {
    if (parseMapLines(newTagMap) != 0) {
        delete newTagMap;
        return NULL;
    }
    tag->tagLen = cp - tag->tagStr;

    if (!isspace(*cp)) {
        fprintf(stderr, "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
        errno = EINVAL;
        return -1;
    return newTagMap;
}

    while (isspace(*cp) && (*cp != '\n')) ++cp;
    if (*cp != '#') {
        tag->fmtStr = cp;
        while ((*cp != '\n') && (*cp != '#')) ++cp;
        while ((cp > tag->fmtStr) && isspace(*(cp - 1))) --cp;
        tag->fmtLen = cp - tag->fmtStr;
// Close the map.
LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
    if (map) delete map;
}

    while (*cp != '\n') ++cp;
    *pData = cp;

    return 0;
// Look up an entry in the map.
LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
                                                         size_t *len,
                                                         unsigned int tag) {
    if (len) *len = 0;
    const TagFmt* str = map->find(tag);
    if (!str) return NULL;
    if (len) *len = str->first.length();
    return str->first.data();
}

/*
 * Compare two EventTags.
 */
static int compareEventTags(const void* v1, const void* v2)
{
    const EventTag* tag1 = (const EventTag*) v1;
    const EventTag* tag2 = (const EventTag*) v2;

    return tag1->tagIndex - tag2->tagIndex;
// Look up an entry in the map.
LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
        const EventTagMap* map, size_t *len, unsigned int tag) {
    if (len) *len = 0;
    const TagFmt* str = map->find(tag);
    if (!str) return NULL;
    if (len) *len = str->second.length();
    return str->second.data();
}

/*
 * Sort the EventTag array so we can do fast lookups by tag index.  After
 * the sort we do a quick check for duplicate tag indices.
 *
 * Returns 0 on success.
 */
static int sortTags(EventTagMap* map)
{
    int i;

    qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);

    for (i = 1; i < map->numTags; i++) {
        if (map->tagArray[i].tagIndex == map->tagArray[i - 1].tagIndex) {
            fprintf(stderr,
                "%s: duplicate tag entries (%" PRIu32 ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
                OUT_TAG,
                map->tagArray[i].tagIndex,
                (int)map->tagArray[i].tagLen, map->tagArray[i].tagStr,
                (int)map->tagArray[i].fmtLen, map->tagArray[i].fmtStr,
                map->tagArray[i - 1].tagIndex,
                (int)map->tagArray[i - 1].tagLen, map->tagArray[i - 1].fmtStr,
                (int)map->tagArray[i - 1].fmtLen, map->tagArray[i - 1].fmtStr);
            errno = EMLINK;
            return -1;
        }
    }
// This function is deprecated and replaced with android_lookupEventTag_len
// since it will cause the map to change from Shared and backed by a file,
// to Private Dirty and backed up by swap, albeit highly compressible. By
// deprecating this function everywhere, we save 100s of MB of memory space.
LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
                                                     unsigned int tag) {
    size_t len;
    const char* tagStr = android_lookupEventTag_len(map, &len, tag);

    return 0;
    if (!tagStr) return tagStr;
    char* cp = const_cast<char*>(tagStr);
    cp += len;
    if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
    return tagStr;
}