diff options
Diffstat (limited to 'liblog/event_tag_map.c')
-rw-r--r-- | liblog/event_tag_map.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c new file mode 100644 index 000000000..e70754e17 --- /dev/null +++ b/liblog/event_tag_map.c | |||
@@ -0,0 +1,438 @@ | |||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | */ | ||
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 | |||