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 <log/event_tag_map.h> 17 #include <log/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 439