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