Loading liblog/event_tag_map.cpp +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. Loading @@ -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; } Loading
liblog/event_tag_map.cpp +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. Loading @@ -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; }