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 */ 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 if (fd >= 0) 103 close(fd); 104 105 return newTagMap; 106 107 fail: 108 android_closeEventTagMap(newTagMap); 109 if (fd >= 0) 110 close(fd); 111 return NULL; 112 } 113 114 /* 115 * Close the map. 116 */ 117 LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) 118 { 119 if (map == NULL) 120 return; 121 122 munmap(map->mapAddr, map->mapLen); 123 free(map); 124 } 125 126 /* 127 * Look up an entry in the map. 128 * 129 * The entries are sorted by tag number, so we can do a binary search. 130 */ 131 LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map, 132 int tag) 133 { 134 int hi, lo, mid; 135 136 lo = 0; 137 hi = map->numTags-1; 138 139 while (lo <= hi) { 140 int cmp; 141 142 mid = (lo+hi)/2; 143 cmp = map->tagArray[mid].tagIndex - tag; 144 if (cmp < 0) { 145 /* tag is bigger */ 146 lo = mid + 1; 147 } else if (cmp > 0) { 148 /* tag is smaller */ 149 hi = mid - 1; 150 } else { 151 /* found */ 152 return map->tagArray[mid].tagStr; 153 } 154 } 155 156 return NULL; 157 } 158 159 160 161 /* 162 * Determine whether "c" is a whitespace char. 163 */ 164 static inline int isCharWhitespace(char c) 165 { 166 return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); 167 } 168 169 /* 170 * Determine whether "c" is a valid tag char. 171 */ 172 static inline int isCharValidTag(char c) 173 { 174 return ((c >= 'A' && c <= 'Z') || 175 (c >= 'a' && c <= 'z') || 176 (c >= '0' && c <= '9') || 177 (c == '_')); 178 } 179 180 /* 181 * Determine whether "c" is a valid decimal digit. 182 */ 183 static inline int isCharDigit(char c) 184 { 185 return (c >= '0' && c <= '9'); 186 } 187 188 189 /* 190 * Crunch through the file, parsing the contents and creating a tag index. 191 */ 192 static int processFile(EventTagMap* map) 193 { 194 /* get a tag count */ 195 map->numTags = countMapLines(map); 196 if (map->numTags < 0) 197 return -1; 198 199 //printf("+++ found %d tags\n", map->numTags); 200 201 /* allocate storage for the tag index array */ 202 map->tagArray = calloc(1, sizeof(EventTag) * map->numTags); 203 if (map->tagArray == NULL) 204 return -1; 205 206 /* parse the file, null-terminating tag strings */ 207 if (parseMapLines(map) != 0) { 208 fprintf(stderr, "%s: file parse failed\n", OUT_TAG); 209 return -1; 210 } 211 212 /* sort the tags and check for duplicates */ 213 if (sortTags(map) != 0) 214 return -1; 215 216 return 0; 217 } 218 219 /* 220 * Run through all lines in the file, determining whether they're blank, 221 * comments, or possibly have a tag entry. 222 * 223 * This is a very "loose" scan. We don't try to detect syntax errors here. 224 * The later pass is more careful, but the number of tags found there must 225 * match the number of tags found here. 226 * 227 * Returns the number of potential tag entries found. 228 */ 229 static int countMapLines(const EventTagMap* map) 230 { 231 int numTags, unknown; 232 const char* cp; 233 const char* endp; 234 235 cp = (const char*) map->mapAddr; 236 endp = cp + map->mapLen; 237 238 numTags = 0; 239 unknown = 1; 240 while (cp < endp) { 241 if (*cp == '\n') { 242 unknown = 1; 243 } else if (unknown) { 244 if (isCharDigit(*cp)) { 245 /* looks like a tag to me */ 246 numTags++; 247 unknown = 0; 248 } else if (isCharWhitespace(*cp)) { 249 /* might be leading whitespace before tag num, keep going */ 250 } else { 251 /* assume comment; second pass can complain in detail */ 252 unknown = 0; 253 } 254 } else { 255 /* we've made up our mind; just scan to end of line */ 256 } 257 cp++; 258 } 259 260 return numTags; 261 } 262 263 /* 264 * Parse the tags out of the file. 265 */ 266 static int parseMapLines(EventTagMap* map) 267 { 268 int tagNum, lineStart, lineNum; 269 char* cp; 270 char* endp; 271 272 cp = (char*) map->mapAddr; 273 endp = cp + map->mapLen; 274 275 /* insist on EOL at EOF; simplifies parsing and null-termination */ 276 if (*(endp-1) != '\n') { 277 fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG); 278 return -1; 279 } 280 281 tagNum = 0; 282 lineStart = 1; 283 lineNum = 1; 284 while (cp < endp) { 285 //printf("{%02x}", *cp); fflush(stdout); 286 if (*cp == '\n') { 287 lineStart = 1; 288 lineNum++; 289 } else if (lineStart) { 290 if (*cp == '#') { 291 /* comment; just scan to end */ 292 lineStart = 0; 293 } else if (isCharDigit(*cp)) { 294 /* looks like a tag; scan it out */ 295 if (tagNum >= map->numTags) { 296 fprintf(stderr, 297 "%s: more tags than expected (%d)\n", OUT_TAG, tagNum); 298 return -1; 299 } 300 if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) 301 return -1; 302 tagNum++; 303 lineNum++; // we eat the '\n' 304 /* leave lineStart==1 */ 305 } else if (isCharWhitespace(*cp)) { 306 /* looks like leading whitespace; keep scanning */ 307 } else { 308 fprintf(stderr, 309 "%s: unexpected chars (0x%02x) in tag number on line %d\n", 310 OUT_TAG, *cp, lineNum); 311 return -1; 312 } 313 } else { 314 /* this is a blank or comment line */ 315 } 316 cp++; 317 } 318 319 if (tagNum != map->numTags) { 320 fprintf(stderr, "%s: parsed %d tags, expected %d\n", 321 OUT_TAG, tagNum, map->numTags); 322 return -1; 323 } 324 325 return 0; 326 } 327 328 /* 329 * Scan one tag line. 330 * 331 * "*pData" should be pointing to the first digit in the tag number. On 332 * successful return, it will be pointing to the last character in the 333 * tag line (i.e. the character before the start of the next line). 334 * 335 * Returns 0 on success, nonzero on failure. 336 */ 337 static int scanTagLine(char** pData, EventTag* tag, int lineNum) 338 { 339 char* cp = *pData; 340 char* startp; 341 char* endp; 342 unsigned long val; 343 344 startp = cp; 345 while (isCharDigit(*++cp)) 346 ; 347 *cp = '\0'; 348 349 val = strtoul(startp, &endp, 10); 350 assert(endp == cp); 351 if (endp != cp) 352 fprintf(stderr, "ARRRRGH\n"); 353 354 tag->tagIndex = val; 355 356 while (*++cp != '\n' && isCharWhitespace(*cp)) 357 ; 358 359 if (*cp == '\n') { 360 fprintf(stderr, 361 "%s: missing tag string on line %d\n", OUT_TAG, lineNum); 362 return -1; 363 } 364 365 tag->tagStr = cp; 366 367 while (isCharValidTag(*++cp)) 368 ; 369 370 if (*cp == '\n') { 371 /* null terminate and return */ 372 *cp = '\0'; 373 } else if (isCharWhitespace(*cp)) { 374 /* CRLF or trailin spaces; zap this char, then scan for the '\n' */ 375 *cp = '\0'; 376 377 /* just ignore the rest of the line till \n 378 TODO: read the tag description that follows the tag name 379 */ 380 while (*++cp != '\n') { 381 } 382 } else { 383 fprintf(stderr, 384 "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum); 385 return -1; 386 } 387 388 *pData = cp; 389 390 //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr); 391 return 0; 392 } 393 394 /* 395 * Compare two EventTags. 396 */ 397 static int compareEventTags(const void* v1, const void* v2) 398 { 399 const EventTag* tag1 = (const EventTag*) v1; 400 const EventTag* tag2 = (const EventTag*) v2; 401 402 return tag1->tagIndex - tag2->tagIndex; 403 } 404 405 /* 406 * Sort the EventTag array so we can do fast lookups by tag index. After 407 * the sort we do a quick check for duplicate tag indices. 408 * 409 * Returns 0 on success. 410 */ 411 static int sortTags(EventTagMap* map) 412 { 413 int i; 414 415 qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags); 416 417 for (i = 1; i < map->numTags; i++) { 418 if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) { 419 fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n", 420 OUT_TAG, 421 map->tagArray[i].tagIndex, map->tagArray[i].tagStr, 422 map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr); 423 return -1; 424 } 425 } 426 427 return 0; 428 } 429