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 */ 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 /* 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 */ 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 */ 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 */ 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 */ 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 */ 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