• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "cutils/event_tag_map.h"
17 #include "cutils/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  */
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     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  */
countMapLines(const EventTagMap * map)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  */
parseMapLines(EventTagMap * map)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  */
scanTagLine(char ** pData,EventTag * tag,int lineNum)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  */
compareEventTags(const void * v1,const void * v2)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  */
sortTags(EventTagMap * map)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  */
dumpTags(const EventTagMap * map)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