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 
     17 #include <assert.h>
     18 #include <errno.h>
     19 #include <fcntl.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <sys/mman.h>
     23 
     24 #include <log/event_tag_map.h>
     25 #include <log/log.h>
     26 
     27 #define OUT_TAG "EventTagMap"
     28 
     29 /*
     30  * Single entry.
     31  */
     32 typedef struct EventTag {
     33     unsigned int    tagIndex;
     34     const char*     tagStr;
     35 } EventTag;
     36 
     37 /*
     38  * Map.
     39  */
     40 struct EventTagMap {
     41     /* memory-mapped source file; we get strings from here */
     42     void*           mapAddr;
     43     size_t          mapLen;
     44 
     45     /* array of event tags, sorted numerically by tag index */
     46     EventTag*       tagArray;
     47     int             numTags;
     48 };
     49 
     50 /* fwd */
     51 static int processFile(EventTagMap* map);
     52 static int countMapLines(const EventTagMap* map);
     53 static int parseMapLines(EventTagMap* map);
     54 static int scanTagLine(char** pData, EventTag* tag, int lineNum);
     55 static int sortTags(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     /* get a tag count */
    189     map->numTags = countMapLines(map);
    190     if (map->numTags < 0)
    191         return -1;
    192 
    193     //printf("+++ found %d tags\n", map->numTags);
    194 
    195     /* allocate storage for the tag index array */
    196     map->tagArray = calloc(1, sizeof(EventTag) * map->numTags);
    197     if (map->tagArray == NULL)
    198         return -1;
    199 
    200     /* parse the file, null-terminating tag strings */
    201     if (parseMapLines(map) != 0) {
    202         fprintf(stderr, "%s: file parse failed\n", OUT_TAG);
    203         return -1;
    204     }
    205 
    206     /* sort the tags and check for duplicates */
    207     if (sortTags(map) != 0)
    208         return -1;
    209 
    210     return 0;
    211 }
    212 
    213 /*
    214  * Run through all lines in the file, determining whether they're blank,
    215  * comments, or possibly have a tag entry.
    216  *
    217  * This is a very "loose" scan.  We don't try to detect syntax errors here.
    218  * The later pass is more careful, but the number of tags found there must
    219  * match the number of tags found here.
    220  *
    221  * Returns the number of potential tag entries found.
    222  */
    223 static int countMapLines(const EventTagMap* map)
    224 {
    225     int numTags, unknown;
    226     const char* cp;
    227     const char* endp;
    228 
    229     cp = (const char*) map->mapAddr;
    230     endp = cp + map->mapLen;
    231 
    232     numTags = 0;
    233     unknown = 1;
    234     while (cp < endp) {
    235         if (*cp == '\n') {
    236             unknown = 1;
    237         } else if (unknown) {
    238             if (isCharDigit(*cp)) {
    239                 /* looks like a tag to me */
    240                 numTags++;
    241                 unknown = 0;
    242             } else if (isCharWhitespace(*cp)) {
    243                 /* might be leading whitespace before tag num, keep going */
    244             } else {
    245                 /* assume comment; second pass can complain in detail */
    246                 unknown = 0;
    247             }
    248         } else {
    249             /* we've made up our mind; just scan to end of line */
    250         }
    251         cp++;
    252     }
    253 
    254     return numTags;
    255 }
    256 
    257 /*
    258  * Parse the tags out of the file.
    259  */
    260 static int parseMapLines(EventTagMap* map)
    261 {
    262     int tagNum, lineStart, lineNum;
    263     char* cp;
    264     char* endp;
    265 
    266     cp = (char*) map->mapAddr;
    267     endp = cp + map->mapLen;
    268 
    269     /* insist on EOL at EOF; simplifies parsing and null-termination */
    270     if (*(endp-1) != '\n') {
    271         fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
    272         return -1;
    273     }
    274 
    275     tagNum = 0;
    276     lineStart = 1;
    277     lineNum = 1;
    278     while (cp < endp) {
    279         //printf("{%02x}", *cp); fflush(stdout);
    280         if (*cp == '\n') {
    281             lineStart = 1;
    282             lineNum++;
    283         } else if (lineStart) {
    284             if (*cp == '#') {
    285                 /* comment; just scan to end */
    286                 lineStart = 0;
    287             } else if (isCharDigit(*cp)) {
    288                 /* looks like a tag; scan it out */
    289                 if (tagNum >= map->numTags) {
    290                     fprintf(stderr,
    291                         "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
    292                     return -1;
    293                 }
    294                 if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0)
    295                     return -1;
    296                 tagNum++;
    297                 lineNum++;      // we eat the '\n'
    298                 /* leave lineStart==1 */
    299             } else if (isCharWhitespace(*cp)) {
    300                 /* looks like leading whitespace; keep scanning */
    301             } else {
    302                 fprintf(stderr,
    303                     "%s: unexpected chars (0x%02x) in tag number on line %d\n",
    304                     OUT_TAG, *cp, lineNum);
    305                 return -1;
    306             }
    307         } else {
    308             /* this is a blank or comment line */
    309         }
    310         cp++;
    311     }
    312 
    313     if (tagNum != map->numTags) {
    314         fprintf(stderr, "%s: parsed %d tags, expected %d\n",
    315             OUT_TAG, tagNum, map->numTags);
    316         return -1;
    317     }
    318 
    319     return 0;
    320 }
    321 
    322 /*
    323  * Scan one tag line.
    324  *
    325  * "*pData" should be pointing to the first digit in the tag number.  On
    326  * successful return, it will be pointing to the last character in the
    327  * tag line (i.e. the character before the start of the next line).
    328  *
    329  * Returns 0 on success, nonzero on failure.
    330  */
    331 static int scanTagLine(char** pData, EventTag* tag, int lineNum)
    332 {
    333     char* cp = *pData;
    334     char* startp;
    335     char* endp;
    336     unsigned long val;
    337 
    338     startp = cp;
    339     while (isCharDigit(*++cp))
    340         ;
    341     *cp = '\0';
    342 
    343     val = strtoul(startp, &endp, 10);
    344     assert(endp == cp);
    345     if (endp != cp)
    346         fprintf(stderr, "ARRRRGH\n");
    347 
    348     tag->tagIndex = val;
    349 
    350     while (*++cp != '\n' && isCharWhitespace(*cp))
    351         ;
    352 
    353     if (*cp == '\n') {
    354         fprintf(stderr,
    355             "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
    356         return -1;
    357     }
    358 
    359     tag->tagStr = cp;
    360 
    361     while (isCharValidTag(*++cp))
    362         ;
    363 
    364     if (*cp == '\n') {
    365         /* null terminate and return */
    366         *cp = '\0';
    367     } else if (isCharWhitespace(*cp)) {
    368         /* CRLF or trailin spaces; zap this char, then scan for the '\n' */
    369         *cp = '\0';
    370 
    371         /* just ignore the rest of the line till \n
    372         TODO: read the tag description that follows the tag name
    373         */
    374         while (*++cp != '\n') {
    375         }
    376     } else {
    377         fprintf(stderr,
    378             "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
    379         return -1;
    380     }
    381 
    382     *pData = cp;
    383 
    384     //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr);
    385     return 0;
    386 }
    387 
    388 /*
    389  * Compare two EventTags.
    390  */
    391 static int compareEventTags(const void* v1, const void* v2)
    392 {
    393     const EventTag* tag1 = (const EventTag*) v1;
    394     const EventTag* tag2 = (const EventTag*) v2;
    395 
    396     return tag1->tagIndex - tag2->tagIndex;
    397 }
    398 
    399 /*
    400  * Sort the EventTag array so we can do fast lookups by tag index.  After
    401  * the sort we do a quick check for duplicate tag indices.
    402  *
    403  * Returns 0 on success.
    404  */
    405 static int sortTags(EventTagMap* map)
    406 {
    407     int i;
    408 
    409     qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
    410 
    411     for (i = 1; i < map->numTags; i++) {
    412         if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) {
    413             fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n",
    414                 OUT_TAG,
    415                 map->tagArray[i].tagIndex, map->tagArray[i].tagStr,
    416                 map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr);
    417             return -1;
    418         }
    419     }
    420 
    421     return 0;
    422 }
    423