summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'liblog/event_tag_map.c')
-rw-r--r--liblog/event_tag_map.c438
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 */
31typedef struct EventTag {
32 unsigned int tagIndex;
33 const char* tagStr;
34} EventTag;
35
36/*
37 * Map.
38 */
39struct 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 */
50static int processFile(EventTagMap* map);
51static int countMapLines(const EventTagMap* map);
52static int parseMapLines(EventTagMap* map);
53static int scanTagLine(char** pData, EventTag* tag, int lineNum);
54static int sortTags(EventTagMap* map);
55static 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 */
64EventTagMap* 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
102fail:
103 android_closeEventTagMap(newTagMap);
104 if (fd >= 0)
105 close(fd);
106 return NULL;
107}
108
109/*
110 * Close the map.
111 */
112void 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 */
126const 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 */
158static 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 */
166static 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 */
177static 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 */
186static 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 */
225static 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 */
262static 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 */
333static 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 */
393static 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 */
407static 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 */
429static 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