diff options
Diffstat (limited to 'liblog/event_tag_map.c')
-rw-r--r-- | liblog/event_tag_map.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c new file mode 100644 index 0000000..e70754e --- /dev/null +++ b/liblog/event_tag_map.c @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2007 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "cutils/event_tag_map.h" +#include "cutils/log.h" + +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <errno.h> +#include <assert.h> + +#define OUT_TAG "EventTagMap" + +/* + * Single entry. + */ +typedef struct EventTag { + unsigned int tagIndex; + const char* tagStr; +} EventTag; + +/* + * Map. + */ +struct EventTagMap { + /* 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; +}; + +/* 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); +static void dumpTags(const EventTagMap* map); + + +/* + * 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'. + */ +EventTagMap* android_openEventTagMap(const char* fileName) +{ + EventTagMap* newTagMap; + off_t end; + int fd = -1; + + newTagMap = calloc(1, sizeof(EventTagMap)); + if (newTagMap == NULL) + return NULL; + + fd = open(fileName, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s: unable to open map '%s': %s\n", + OUT_TAG, fileName, strerror(errno)); + goto fail; + } + + end = lseek(fd, 0L, SEEK_END); + (void) lseek(fd, 0L, SEEK_SET); + if (end < 0) { + fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName); + goto fail; + } + + newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + if (newTagMap->mapAddr == MAP_FAILED) { + fprintf(stderr, "%s: mmap(%s) failed: %s\n", + OUT_TAG, fileName, strerror(errno)); + goto fail; + } + newTagMap->mapLen = end; + + if (processFile(newTagMap) != 0) + goto fail; + + return newTagMap; + +fail: + android_closeEventTagMap(newTagMap); + if (fd >= 0) + close(fd); + return NULL; +} + +/* + * Close the map. + */ +void android_closeEventTagMap(EventTagMap* map) +{ + if (map == NULL) + return; + + munmap(map->mapAddr, map->mapLen); + free(map); +} + +/* + * Look up an entry in the map. + * + * The entries are sorted by tag number, so we can do a binary search. + */ +const char* android_lookupEventTag(const EventTagMap* map, int tag) +{ + int hi, lo, mid; + + lo = 0; + hi = map->numTags-1; + + while (lo <= hi) { + int cmp; + + mid = (lo+hi)/2; + 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 */ + return map->tagArray[mid].tagStr; + } + } + + return NULL; +} + + + +/* + * Determine whether "c" is a whitespace char. + */ +static inline int isCharWhitespace(char c) +{ + return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); +} + +/* + * Determine whether "c" is a valid tag char. + */ +static inline int isCharValidTag(char c) +{ + return ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '_')); +} + +/* + * Determine whether "c" is a valid decimal digit. + */ +static inline int isCharDigit(char c) +{ + return (c >= '0' && c <= '9'); +} + + +/* + * Crunch through the file, parsing the contents and creating a tag index. + */ +static int processFile(EventTagMap* map) +{ + EventTag* tagArray = NULL; + + /* get a tag count */ + map->numTags = countMapLines(map); + if (map->numTags < 0) + return -1; + + //printf("+++ found %d tags\n", map->numTags); + + /* allocate storage for the tag index array */ + map->tagArray = calloc(1, sizeof(EventTag) * map->numTags); + if (map->tagArray == NULL) + return -1; + + /* parse the file, null-terminating tag strings */ + if (parseMapLines(map) != 0) { + fprintf(stderr, "%s: file parse failed\n", OUT_TAG); + return -1; + } + + /* sort the tags and check for duplicates */ + if (sortTags(map) != 0) + return -1; + + return 0; +} + +/* + * 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) +{ + int numTags, unknown; + const char* cp; + const char* endp; + + cp = (const char*) map->mapAddr; + endp = cp + map->mapLen; + + numTags = 0; + unknown = 1; + while (cp < endp) { + if (*cp == '\n') { + unknown = 1; + } else if (unknown) { + if (isCharDigit(*cp)) { + /* looks like a tag to me */ + numTags++; + unknown = 0; + } else if (isCharWhitespace(*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 */ + } + cp++; + } + + return numTags; +} + +/* + * Parse the tags out of the file. + */ +static int parseMapLines(EventTagMap* map) +{ + int tagNum, lineStart, lineNum; + char* cp; + char* endp; + + cp = (char*) map->mapAddr; + 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); + return -1; + } + + tagNum = 0; + lineStart = 1; + lineNum = 1; + while (cp < endp) { + //printf("{%02x}", *cp); fflush(stdout); + if (*cp == '\n') { + lineStart = 1; + lineNum++; + } else if (lineStart) { + if (*cp == '#') { + /* comment; just scan to end */ + lineStart = 0; + } else if (isCharDigit(*cp)) { + /* looks like a tag; scan it out */ + if (tagNum >= map->numTags) { + fprintf(stderr, + "%s: more tags than expected (%d)\n", OUT_TAG, tagNum); + return -1; + } + if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) + return -1; + tagNum++; + lineNum++; // we eat the '\n' + /* leave lineStart==1 */ + } else if (isCharWhitespace(*cp)) { + /* 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); + return -1; + } + } else { + /* 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); + 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 = *pData; + char* startp; + char* endp; + unsigned long val; + + startp = cp; + while (isCharDigit(*++cp)) + ; + *cp = '\0'; + + val = strtoul(startp, &endp, 10); + assert(endp == cp); + if (endp != cp) + fprintf(stderr, "ARRRRGH\n"); + + tag->tagIndex = val; + + while (*++cp != '\n' && isCharWhitespace(*cp)) + ; + + if (*cp == '\n') { + fprintf(stderr, + "%s: missing tag string on line %d\n", OUT_TAG, lineNum); + return -1; + } + + tag->tagStr = cp; + + while (isCharValidTag(*++cp)) + ; + + if (*cp == '\n') { + /* null terminate and return */ + *cp = '\0'; + } else if (isCharWhitespace(*cp)) { + /* CRLF or trailin spaces; zap this char, then scan for the '\n' */ + *cp = '\0'; + + /* just ignore the rest of the line till \n + TODO: read the tag description that follows the tag name + */ + while (*++cp != '\n') { + } + } else { + fprintf(stderr, + "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum); + return -1; + } + + *pData = cp; + + //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr); + return 0; +} + +/* + * 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; +} + +/* + * 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 (%d:%s and %d:%s)\n", + OUT_TAG, + map->tagArray[i].tagIndex, map->tagArray[i].tagStr, + map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr); + return -1; + } + } + + return 0; +} + +/* + * Dump the tag array for debugging. + */ +static void dumpTags(const EventTagMap* map) +{ + int i; + + for (i = 0; i < map->numTags; i++) { + const EventTag* tag = &map->tagArray[i]; + printf(" %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr); + } +} + |