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  */
android_openEventTagMap(const char * fileName)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  */
android_closeEventTagMap(EventTagMap * map)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  */
android_lookupEventTag(const EventTagMap * map,int tag)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  */
isCharWhitespace(char c)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  */
isCharValidTag(char c)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  */
isCharDigit(char c)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  */
processFile(EventTagMap * map)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  */
countMapLines(const EventTagMap * map)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  */
parseMapLines(EventTagMap * map)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  */
scanTagLine(char ** pData,EventTag * tag,int lineNum)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  */
compareEventTags(const void * v1,const void * v2)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  */
sortTags(EventTagMap * map)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