Home | History | Annotate | Download | only in liblog
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #include <log/event_tag_map.h>
     17 #include <log/log.h>
     18 
     19 #include <stdlib.h>
     20 #include <string.h>
     21 #include <fcntl.h>
     22 #include <sys/mman.h>
     23 #include <errno.h>
     24 #include <assert.h>
     25 
     26 #define OUT_TAG "EventTagMap"
     27 
     28 /*
     29  * Single entry.
     30  */
     31 typedef struct EventTag {
     32     unsigned int    tagIndex;
     33     const char*     tagStr;
     34 } EventTag;
     35 
     36 /*
     37  * Map.
     38  */
     39 struct EventTagMap {
     40     /* memory-mapped source file; we get strings from here */
     41     void*           mapAddr;
     42     size_t          mapLen;
     43 
     44     /* array of event tags, sorted numerically by tag index */
     45     EventTag*       tagArray;
     46     int             numTags;
     47 };
     48 
     49 /* fwd */
     50 static int processFile(EventTagMap* map);
     51 static int countMapLines(const EventTagMap* map);
     52 static int parseMapLines(EventTagMap* map);
     53 static int scanTagLine(char** pData, EventTag* tag, int lineNum);
     54 static int sortTags(EventTagMap* map);
     55 static void dumpTags(const EventTagMap* map);
     56 
     57 
     58 /*
     59  * Open the map file and allocate a structure to manage it.
     60  *
     61  * We create a private mapping because we want to terminate the log tag
     62  * strings with '\0'.
     63  */
     64 EventTagMap* android_openEventTagMap(const char* fileName)
     65 {
     66     EventTagMap* newTagMap;
     67     off_t end;
     68     int fd = -1;
     69 
     70     newTagMap = calloc(1, sizeof(EventTagMap));
     71     if (newTagMap == NULL)
     72         return NULL;
     73 
     74     fd = open(fileName, O_RDONLY);
     75     if (fd < 0) {
     76         fprintf(stderr, "%s: unable to open map '%s': %s\n",
     77             OUT_TAG, fileName, strerror(errno));
     78         goto fail;
     79     }
     80 
     81     end = lseek(fd, 0L, SEEK_END);
     82     (void) lseek(fd, 0L, SEEK_SET);
     83     if (end < 0) {
     84         fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName);
     85         goto fail;
     86     }
     87 
     88     newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE,
     89                                 fd, 0);
     90     if (newTagMap->mapAddr == MAP_FAILED) {
     91         fprintf(stderr, "%s: mmap(%s) failed: %s\n",
     92             OUT_TAG, fileName, strerror(errno));
     93         goto fail;
     94     }
     95     newTagMap->mapLen = end;
     96 
     97     if (processFile(newTagMap) != 0)
     98         goto fail;
     99 
    100     return newTagMap;
    101 
    102 fail:
    103     android_closeEventTagMap(newTagMap);
    104     if (fd >= 0)
    105         close(fd);
    106     return NULL;
    107 }
    108 
    109 /*
    110  * Close the map.
    111  */
    112 void android_closeEventTagMap(EventTagMap* map)
    113 {
    114     if (map == NULL)
    115         return;
    116 
    117     munmap(map->mapAddr, map->mapLen);
    118     free(map);
    119 }
    120 
    121 /*
    122  * Look up an entry in the map.
    123  *
    124  * The entries are sorted by tag number, so we can do a binary search.
    125  */
    126 const char* android_lookupEventTag(const EventTagMap* map, int tag)
    127 {
    128     int hi, lo, mid;
    129 
    130     lo = 0;
    131     hi = map->numTags-1;
    132 
    133     while (lo <= hi) {
    134         int cmp;
    135 
    136         mid = (lo+hi)/2;
    137         cmp = map->tagArray[mid].tagIndex - tag;
    138         if (cmp < 0) {
    139             /* tag is bigger */
    140             lo = mid + 1;
    141         } else if (cmp > 0) {
    142             /* tag is smaller */
    143             hi = mid - 1;
    144         } else {
    145             /* found */
    146             return map->tagArray[mid].tagStr;
    147         }
    148     }
    149 
    150     return NULL;
    151 }
    152 
    153 
    154 
    155 /*
    156  * Determine whether "c" is a whitespace char.
    157  */
    158 static inline int isCharWhitespace(char c)
    159 {
    160     return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
    161 }
    162 
    163 /*
    164  * Determine whether "c" is a valid tag char.
    165  */
    166 static inline int isCharValidTag(char c)
    167 {
    168     return ((c >= 'A' && c <= 'Z') ||
    169             (c >= 'a' && c <= 'z') ||
    170             (c >= '0' && c <= '9') ||
    171             (c == '_'));
    172 }
    173 
    174 /*
    175  * Determine whether "c" is a valid decimal digit.
    176  */
    177 static inline int isCharDigit(char c)
    178 {
    179     return (c >= '0' && c <= '9');
    180 }
    181 
    182 
    183 /*
    184  * Crunch through the file, parsing the contents and creating a tag index.
    185  */
    186 static int processFile(EventTagMap* map)
    187 {
    188     EventTag* tagArray = NULL;
    189 
    190     /* get a tag count */
    191     map->numTags = countMapLines(map);
    192     if (map->numTags < 0)
    193         return -1;
    194 
    195     //printf("+++ found %d tags\n", map->numTags);
    196 
    197     /* allocate storage for the tag index array */
    198     map->tagArray = calloc(1, sizeof(EventTag) * map->numTags);
    199     if (map->tagArray == NULL)
    200         return -1;
    201 
    202     /* parse the file, null-terminating tag strings */
    203     if (parseMapLines(map) != 0) {
    204         fprintf(stderr, "%s: file parse failed\n", OUT_TAG);
    205         return -1;
    206     }
    207 
    208     /* sort the tags and check for duplicates */
    209     if (sortTags(map) != 0)
    210         return -1;
    211 
    212     return 0;
    213 }
    214 
    215 /*
    216  * Run through all lines in the file, determining whether they're blank,
    217  * comments, or possibly have a tag entry.
    218  *
    219  * This is a very "loose" scan.  We don't try to detect syntax errors here.
    220  * The later pass is more careful, but the number of tags found there must
    221  * match the number of tags found here.
    222  *
    223  * Returns the number of potential tag entries found.
    224  */
    225 static int countMapLines(const EventTagMap* map)
    226 {
    227     int numTags, unknown;
    228     const char* cp;
    229     const char* endp;
    230 
    231     cp = (const char*) map->mapAddr;
    232     endp = cp + map->mapLen;
    233 
    234     numTags = 0;
    235     unknown = 1;
    236     while (cp < endp) {
    237         if (*cp == '\n') {
    238             unknown = 1;
    239         } else if (unknown) {
    240             if (isCharDigit(*cp)) {
    241                 /* looks like a tag to me */
    242                 numTags++;
    243                 unknown = 0;
    244             } else if (isCharWhitespace(*cp)) {
    245                 /* might be leading whitespace before tag num, keep going */
    246             } else {
    247                 /* assume comment; second pass can complain in detail */
    248                 unknown = 0;
    249             }
    250         } else {
    251             /* we've made up our mind; just scan to end of line */
    252         }
    253         cp++;
    254     }
    255 
    256     return numTags;
    257 }
    258 
    259 /*
    260  * Parse the tags out of the file.
    261  */
    262 static int parseMapLines(EventTagMap* map)
    263 {
    264     int tagNum, lineStart, lineNum;
    265     char* cp;
    266     char* endp;
    267 
    268     cp = (char*) map->mapAddr;
    269     endp = cp + map->mapLen;
    270 
    271     /* insist on EOL at EOF; simplifies parsing and null-termination */
    272     if (*(endp-1) != '\n') {
    273         fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
    274         return -1;
    275     }
    276 
    277     tagNum = 0;
    278     lineStart = 1;
    279     lineNum = 1;
    280     while (cp < endp) {
    281         //printf("{%02x}", *cp); fflush(stdout);
    282         if (*cp == '\n') {
    283             lineStart = 1;
    284             lineNum++;
    285         } else if (lineStart) {
    286             if (*cp == '#') {
    287                 /* comment; just scan to end */
    288                 lineStart = 0;
    289             } else if (isCharDigit(*cp)) {
    290                 /* looks like a tag; scan it out */
    291                 if (tagNum >= map->numTags) {
    292                     fprintf(stderr,
    293                         "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
    294                     return -1;
    295                 }
    296                 if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0)
    297                     return -1;
    298                 tagNum++;
    299                 lineNum++;      // we eat the '\n'
    300                 /* leave lineStart==1 */
    301             } else if (isCharWhitespace(*cp)) {
    302                 /* looks like leading whitespace; keep scanning */
    303             } else {
    304                 fprintf(stderr,
    305                     "%s: unexpected chars (0x%02x) in tag number on line %d\n",
    306                     OUT_TAG, *cp, lineNum);
    307                 return -1;
    308             }
    309         } else {
    310             /* this is a blank or comment line */
    311         }
    312         cp++;
    313     }
    314 
    315     if (tagNum != map->numTags) {
    316         fprintf(stderr, "%s: parsed %d tags, expected %d\n",
    317             OUT_TAG, tagNum, map->numTags);
    318         return -1;
    319     }
    320 
    321     return 0;
    322 }
    323 
    324 /*
    325  * Scan one tag line.
    326  *
    327  * "*pData" should be pointing to the first digit in the tag number.  On
    328  * successful return, it will be pointing to the last character in the
    329  * tag line (i.e. the character before the start of the next line).
    330  *
    331  * Returns 0 on success, nonzero on failure.
    332  */
    333 static int scanTagLine(char** pData, EventTag* tag, int lineNum)
    334 {
    335     char* cp = *pData;
    336     char* startp;
    337     char* endp;
    338     unsigned long val;
    339 
    340     startp = cp;
    341     while (isCharDigit(*++cp))
    342         ;
    343     *cp = '\0';
    344 
    345     val = strtoul(startp, &endp, 10);
    346     assert(endp == cp);
    347     if (endp != cp)
    348         fprintf(stderr, "ARRRRGH\n");
    349 
    350     tag->tagIndex = val;
    351 
    352     while (*++cp != '\n' && isCharWhitespace(*cp))
    353         ;
    354 
    355     if (*cp == '\n') {
    356         fprintf(stderr,
    357             "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
    358         return -1;
    359     }
    360 
    361     tag->tagStr = cp;
    362 
    363     while (isCharValidTag(*++cp))
    364         ;
    365 
    366     if (*cp == '\n') {
    367         /* null terminate and return */
    368         *cp = '\0';
    369     } else if (isCharWhitespace(*cp)) {
    370         /* CRLF or trailin spaces; zap this char, then scan for the '\n' */
    371         *cp = '\0';
    372 
    373         /* just ignore the rest of the line till \n
    374         TODO: read the tag description that follows the tag name
    375         */
    376         while (*++cp != '\n') {
    377         }
    378     } else {
    379         fprintf(stderr,
    380             "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
    381         return -1;
    382     }
    383 
    384     *pData = cp;
    385 
    386     //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr);
    387     return 0;
    388 }
    389 
    390 /*
    391  * Compare two EventTags.
    392  */
    393 static int compareEventTags(const void* v1, const void* v2)
    394 {
    395     const EventTag* tag1 = (const EventTag*) v1;
    396     const EventTag* tag2 = (const EventTag*) v2;
    397 
    398     return tag1->tagIndex - tag2->tagIndex;
    399 }
    400 
    401 /*
    402  * Sort the EventTag array so we can do fast lookups by tag index.  After
    403  * the sort we do a quick check for duplicate tag indices.
    404  *
    405  * Returns 0 on success.
    406  */
    407 static int sortTags(EventTagMap* map)
    408 {
    409     int i;
    410 
    411     qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
    412 
    413     for (i = 1; i < map->numTags; i++) {
    414         if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) {
    415             fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n",
    416                 OUT_TAG,
    417                 map->tagArray[i].tagIndex, map->tagArray[i].tagStr,
    418                 map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr);
    419             return -1;
    420         }
    421     }
    422 
    423     return 0;
    424 }
    425 
    426 /*
    427  * Dump the tag array for debugging.
    428  */
    429 static void dumpTags(const EventTagMap* map)
    430 {
    431     int i;
    432 
    433     for (i = 0; i < map->numTags; i++) {
    434         const EventTag* tag = &map->tagArray[i];
    435         printf("  %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr);
    436     }
    437 }
    438 
    439