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