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