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