1 /* 2 * ReactOS log2lines 3 * Written by Jan Roeloffzen 4 * 5 * - Initialization, translation and main loop 6 */ 7 8 #include <errno.h> 9 #include <stdio.h> 10 #include <string.h> 11 #include <stdlib.h> 12 13 #include "util.h" 14 #include "version.h" 15 #include "compat.h" 16 #include "options.h" 17 #include "image.h" 18 #include "cache.h" 19 #include "log2lines.h" 20 #include "help.h" 21 #include "cmd.h" 22 #include "match.h" 23 24 25 static FILE *dbgIn = NULL; 26 static FILE *dbgOut = NULL; 27 static FILE *conIn = NULL; 28 static FILE *conOut = NULL; 29 static const char *kdbg_prompt = KDBG_PROMPT; 30 static const char *kdbg_cont = KDBG_CONT; 31 32 LIST sources; 33 LINEINFO lastLine; 34 FILE *logFile = NULL; 35 LIST cache; 36 SUMM summ; 37 REVINFO revinfo; 38 39 40 static void 41 clearLastLine(void) 42 { 43 memset(&lastLine, 0, sizeof(LINEINFO)); 44 } 45 46 static void 47 log_file(FILE *outFile, char *fileName, int line) 48 { 49 int i = 0, min = 0, max = 0; 50 char s[LINESIZE]; 51 FILE *src; 52 53 strcpy(s, opt_SourcesPath); 54 strcat(s, fileName); 55 56 max = line + opt_SrcPlus; 57 if ((src = fopen(s, "r"))) 58 { 59 min = line - opt_Source; 60 min = (min < 0) ? 0 : min; 61 while (i < max && fgets(s, LINESIZE, src)) 62 { 63 if (i >= min) 64 { 65 if (i == line) 66 log(outFile, "| ----\n"); 67 log(outFile, "| %4.4d %s", i + 1, s); 68 } 69 i++; 70 } 71 fclose(src); 72 if ( i < min ) 73 log(outFile, "| S--- source has only %d lines! (check source/revision)\n", i); 74 } 75 else 76 l2l_dbg(1, "Can't open: %s (check " SOURCES_ENV ")\n", s); 77 } 78 79 static void 80 logSource(FILE *outFile) 81 { 82 log_file(outFile, lastLine.file1, lastLine.nr1); 83 if (lastLine.nr2) 84 { 85 log(outFile, "| ---- [%u] ----\n", lastLine.nr2); 86 log_file(outFile, lastLine.file2, lastLine.nr2); 87 } 88 } 89 90 static void 91 reportSource(FILE *outFile) 92 { 93 if (!opt_Source) 94 return; 95 if (lastLine.valid) 96 logSource(outFile); 97 } 98 99 static void 100 report(FILE *outFile) 101 { 102 reportRevision(outFile); 103 reportSource(outFile); 104 clearLastLine(); 105 } 106 107 108 static int 109 print_offset(void *data, size_t offset, char *toString) 110 { 111 PSYMBOLFILE_HEADER RosSymHeader = (PSYMBOLFILE_HEADER)data; 112 PROSSYM_ENTRY e = NULL; 113 PROSSYM_ENTRY e2 = NULL; 114 int bFileOffsetChanged = 0; 115 char fmt[LINESIZE]; 116 char *Strings = (char *)data + RosSymHeader->StringsOffset; 117 118 fmt[0] = '\0'; 119 e = find_offset(data, offset); 120 if (opt_twice) 121 { 122 e2 = find_offset(data, offset - 1); 123 124 if (e == e2) 125 e2 = NULL; 126 else 127 summ.diff++; 128 129 if (opt_Twice && e2) 130 { 131 e = e2; 132 e2 = NULL; 133 /* replaced (transparantly), but updated stats */ 134 } 135 } 136 if (e || e2) 137 { 138 strcpy(lastLine.file1, &Strings[e->FileOffset]); 139 strcpy(lastLine.func1, &Strings[e->FunctionOffset]); 140 lastLine.nr1 = e->SourceLine; 141 sources_entry_create(&sources, lastLine.file1, SVN_PREFIX); 142 lastLine.valid = 1; 143 if (e2) 144 { 145 strcpy(lastLine.file2, &Strings[e2->FileOffset]); 146 strcpy(lastLine.func2, &Strings[e2->FunctionOffset]); 147 lastLine.nr2 = e2->SourceLine; 148 sources_entry_create(&sources, lastLine.file2, SVN_PREFIX); 149 bFileOffsetChanged = e->FileOffset != e2->FileOffset; 150 if (e->FileOffset != e2->FileOffset || e->FunctionOffset != e2->FunctionOffset) 151 summ.majordiff++; 152 153 /* 154 * - "%.0s" displays nothing, but processes argument 155 * - bFileOffsetChanged implies always display 2nd SourceLine even if the same 156 * - also for FunctionOffset 157 */ 158 strcat(fmt, "%s"); 159 if (bFileOffsetChanged) 160 strcat(fmt, "[%s]"); 161 else 162 strcat(fmt, "%.0s"); 163 164 strcat(fmt, ":%u"); 165 if (e->SourceLine != e2->SourceLine || bFileOffsetChanged) 166 strcat(fmt, "[%u]"); 167 else 168 strcat(fmt, "%.0u"); 169 170 strcat(fmt, " (%s"); 171 if (e->FunctionOffset != e2->FunctionOffset || bFileOffsetChanged) 172 strcat(fmt, "[%s])"); 173 else 174 strcat(fmt, "%.0s)"); 175 176 if (toString) 177 { // put in toString if provided 178 snprintf(toString, LINESIZE, fmt, 179 &Strings[e->FileOffset], 180 &Strings[e2->FileOffset], 181 (unsigned int)e->SourceLine, 182 (unsigned int)e2->SourceLine, 183 &Strings[e->FunctionOffset], 184 &Strings[e2->FunctionOffset]); 185 } 186 else 187 { 188 strcat(fmt, "\n"); 189 printf(fmt, 190 &Strings[e->FileOffset], 191 &Strings[e2->FileOffset], 192 (unsigned int)e->SourceLine, 193 (unsigned int)e2->SourceLine, 194 &Strings[e->FunctionOffset], 195 &Strings[e2->FunctionOffset]); 196 } 197 } 198 else 199 { 200 if (toString) 201 { // put in toString if provided 202 snprintf(toString, LINESIZE, "%s:%u (%s)", 203 &Strings[e->FileOffset], 204 (unsigned int)e->SourceLine, 205 &Strings[e->FunctionOffset]); 206 } 207 else 208 { 209 printf("%s:%u (%s)\n", 210 &Strings[e->FileOffset], 211 (unsigned int)e->SourceLine, 212 &Strings[e->FunctionOffset]); 213 } 214 } 215 return 0; 216 } 217 return 1; 218 } 219 220 static int 221 process_data(const void *FileData, size_t offset, char *toString) 222 { 223 int res; 224 225 PIMAGE_SECTION_HEADER PERosSymSectionHeader = get_sectionheader((char *)FileData); 226 if (!PERosSymSectionHeader) 227 return 2; 228 229 res = print_offset((char *)FileData + PERosSymSectionHeader->PointerToRawData, offset, toString); 230 if (res) 231 { 232 if (toString) 233 sprintf(toString, "??:0"); 234 else 235 printf("??:0"); 236 l2l_dbg(1, "Offset not found: %x\n", (unsigned int)offset); 237 summ.offset_errors++; 238 } 239 240 return res; 241 } 242 243 static int 244 process_file(const char *file_name, size_t offset, char *toString) 245 { 246 void *FileData; 247 size_t FileSize; 248 int res = 1; 249 250 FileData = load_file(file_name, &FileSize); 251 if (!FileData) 252 { 253 l2l_dbg(0, "An error occured loading '%s'\n", file_name); 254 } 255 else 256 { 257 res = process_data(FileData, offset, toString); 258 free(FileData); 259 } 260 return res; 261 } 262 263 static int 264 translate_file(const char *cpath, size_t offset, char *toString) 265 { 266 size_t base = 0; 267 LIST_MEMBER *pentry = NULL; 268 int res = 0; 269 char *path, *dpath; 270 271 dpath = path = convert_path(cpath); 272 if (!path) 273 return 1; 274 275 // The path could be absolute: 276 if (get_ImageBase(path, &base)) 277 { 278 pentry = entry_lookup(&cache, path); 279 if (pentry) 280 { 281 path = pentry->path; 282 base = pentry->ImageBase; 283 if (base == INVALID_BASE) 284 { 285 l2l_dbg(1, "No, or invalid base address: %s\n", path); 286 res = 2; 287 } 288 } 289 else 290 { 291 l2l_dbg(1, "Not found in cache: %s\n", path); 292 res = 3; 293 } 294 } 295 296 if (!res) 297 { 298 res = process_file(path, offset, toString); 299 } 300 301 free(dpath); 302 return res; 303 } 304 305 static void 306 translate_char(int c, FILE *outFile) 307 { 308 fputc(c, outFile); 309 if (logFile) 310 fputc(c, logFile); 311 } 312 313 static char * 314 remove_mark(char *Line) 315 { 316 if (Line[1] == ' ' && Line[2] == '<') 317 if (Line[0] == '*' || Line[0] == '?') 318 return Line + 2; 319 return Line; 320 } 321 322 static void 323 translate_line(FILE *outFile, char *Line, char *path, char *LineOut) 324 { 325 unsigned int offset; 326 int cnt, res; 327 char *sep, *tail, *mark, *s; 328 unsigned char ch; 329 330 if (!*Line) 331 return; 332 333 res = 1; 334 mark = ""; 335 s = remove_mark(Line); 336 if (opt_undo) 337 { 338 /* Strip all lines added by this tool: */ 339 char buf[NAMESIZE]; 340 if (sscanf(s, "| %s", buf) == 1) 341 if (buf[0] == '0' || strcmp(buf, "----") == 0 || strcmp(buf, "L2L-") == 0 || strcmp(buf, "S---") == 0 || strcmp(buf, "R---") == 0 || atoi(buf)) 342 res = 0; 343 } 344 345 sep = strchr(s, ':'); 346 if (sep) 347 { 348 *sep = ' '; 349 cnt = sscanf(s, "<%s %x%c", path, &offset, &ch); 350 if (opt_undo) 351 { 352 if (cnt == 3 && ch == ' ') 353 { 354 tail = strchr(s, '>'); 355 tail = tail ? tail - 1 : tail; 356 if (tail && tail[0] == ')' && tail[1] == '>') 357 { 358 res = 0; 359 tail += 2; 360 mark = opt_mark ? "* " : ""; 361 if (opt_redo && !(res = translate_file(path, offset, LineOut))) 362 { 363 log(outFile, "%s<%s:%x (%s)>%s", mark, path, offset, LineOut, tail); 364 summ.redo++; 365 } 366 else 367 { 368 log(outFile, "%s<%s:%x>%s", mark, path, offset, tail); 369 summ.undo++; 370 } 371 } 372 else 373 { 374 mark = opt_Mark ? "? " : ""; 375 summ.skipped++; 376 } 377 summ.total++; 378 } 379 } 380 381 if (!opt_undo || opt_redo) 382 { 383 if (cnt == 3 && ch == '>') 384 { 385 tail = strchr(s, '>') + 1; 386 if (!(res = translate_file(path, offset, LineOut))) 387 { 388 mark = opt_mark ? "* " : ""; 389 log(outFile, "%s<%s:%x (%s)>%s", mark, path, offset, LineOut, tail); 390 summ.translated++; 391 } 392 else 393 { 394 mark = opt_Mark ? "? " : ""; 395 summ.skipped++; 396 } 397 summ.total++; 398 } 399 } 400 } 401 if (res) 402 { 403 if (sep) 404 *sep = ':'; // restore because not translated 405 log(outFile, "%s%s", mark, s); 406 } 407 memset(Line, '\0', LINESIZE); // flushed 408 } 409 410 static int 411 translate_files(FILE *inFile, FILE *outFile) 412 { 413 char Line[LINESIZE + 1]; 414 char path[LINESIZE + 1]; 415 char LineOut[LINESIZE + 1]; 416 int c; 417 unsigned char ch; 418 int i = 0; 419 const char *pc = kdbg_cont; 420 const char *p = kdbg_prompt; 421 const char *p_eos = p + sizeof(KDBG_PROMPT) - 1; //end of string pos 422 423 memset(Line, '\0', LINESIZE + 1); 424 if (opt_console) 425 { 426 while ((c = fgetc(inFile)) != EOF) 427 { 428 if (opt_quit)break; 429 430 ch = (unsigned char)c; 431 if (!opt_raw) 432 { 433 switch (ch) 434 { 435 case '\n': 436 if ( strncmp(Line, KDBG_DISCARD, sizeof(KDBG_DISCARD)-1) == 0 ) 437 { 438 memset(Line, '\0', LINESIZE); // flushed 439 } 440 else 441 { 442 Line[1] = handle_escape_cmd(outFile, Line, path, LineOut); 443 if (Line[1] != KDBG_ESC_CHAR) 444 { 445 if (p == p_eos) 446 { 447 // kdbg prompt, so already echoed char by char 448 memset(Line, '\0', LINESIZE); 449 translate_char(c, outFile); 450 } 451 else 452 { 453 if (match_line(outFile, Line)) 454 { 455 translate_line(outFile, Line, path, LineOut); 456 translate_char(c, outFile); 457 report(outFile); 458 } 459 } 460 } 461 } 462 i = 0; 463 p = kdbg_prompt; 464 pc = kdbg_cont; 465 break; 466 case '<': 467 i = 0; 468 Line[i++] = ch; 469 break; 470 case '>': 471 if (ch == *p) 472 { 473 p = p_eos; 474 translate_line(outFile, Line, path, LineOut); 475 } 476 477 if (p != p_eos) 478 { 479 if (i < LINESIZE) 480 { 481 Line[i++] = ch; 482 translate_line(outFile, Line, path, LineOut); 483 } 484 else 485 { 486 translate_line(outFile, Line, path, LineOut); 487 translate_char(c, outFile); 488 } 489 } 490 else 491 translate_char(c, outFile); 492 i = 0; 493 break; 494 default: 495 if (ch == *p)p++; 496 if (ch == *pc)pc++; 497 if (i < LINESIZE) 498 { 499 Line[i++] = ch; 500 if (p == p_eos) 501 { 502 translate_char(c, outFile); 503 } 504 else if (!*pc) 505 { 506 translate_line(outFile, Line, path, LineOut); 507 i = 0; 508 } 509 } 510 else 511 { 512 translate_line(outFile, Line, path, LineOut); 513 translate_char(c, outFile); 514 i = 0; 515 } 516 } 517 } 518 else 519 translate_char(c, outFile); 520 } 521 } 522 else 523 { // Line by line, slightly faster but less interactive 524 while (fgets(Line, LINESIZE, inFile) != NULL) 525 { 526 if (opt_quit)break; 527 528 if (!opt_raw) 529 { 530 translate_line(outFile, Line, path, LineOut); 531 report(outFile); 532 } 533 else 534 log(outFile, "%s", Line); 535 } 536 } 537 538 if (opt_Revision && (strstr(opt_Revision, "regscan") == opt_Revision)) 539 { 540 char *s = strchr(opt_Revision, ','); 541 if (s) 542 { 543 *s++ = '\0'; 544 revinfo.range = atoi(s); 545 } 546 regscan(outFile); 547 } 548 549 if (opt_stats) 550 { 551 stat_print(outFile, &summ); 552 if (logFile) 553 stat_print(logFile, &summ); 554 } 555 return 0; 556 } 557 558 559 int 560 main(int argc, const char **argv) 561 { 562 int res = 0; 563 int optInit = 0; 564 int optCount = 0; 565 566 dbgIn = stdin; 567 conOut = stdout; 568 (void)conIn; 569 (void)dbgOut; 570 571 memset(&cache, 0, sizeof(LIST)); 572 memset(&sources, 0, sizeof(LIST)); 573 stat_clear(&summ); 574 memset(&revinfo, 0, sizeof(REVINFO)); 575 clearLastLine(); 576 577 optInit = optionInit(argc, argv); 578 optCount = optionParse(argc, argv); 579 580 if (optCount < 0 || optInit < 0) 581 { 582 res = optCount; 583 goto cleanup; 584 } 585 586 argc -= optCount; 587 588 if (opt_Revision && (strcmp(opt_Revision, "update") == 0)) 589 { 590 res = updateSvnlog(); 591 goto cleanup; 592 } 593 594 if (check_directory(opt_force)) 595 { 596 res = 3; 597 goto cleanup; 598 } 599 600 create_cache(opt_force, 0); 601 if (opt_exit) 602 { 603 res = 0; 604 goto cleanup; 605 } 606 607 read_cache(); 608 l2l_dbg(4, "Cache read complete\n"); 609 610 if (set_LogFile(&logFile)) 611 { 612 res = 2; 613 goto cleanup; 614 } 615 l2l_dbg(4, "opt_logFile processed\n"); 616 617 if (opt_Pipe) 618 { 619 l2l_dbg(3, "Command line: \"%s\"\n",opt_Pipe); 620 621 if (!(dbgIn = POPEN(opt_Pipe, "r"))) 622 { 623 dbgIn = stdin; //restore 624 l2l_dbg(0, "Could not popen '%s' (%s)\n", opt_Pipe, strerror(errno)); 625 free(opt_Pipe); 626 opt_Pipe = NULL; 627 } 628 } 629 l2l_dbg(4, "opt_Pipe processed\n"); 630 631 if (argc > 1) 632 { // translate {<exefile> <offset>} 633 int i = 1; 634 const char *exefile = NULL; 635 const char *offset = NULL; 636 char Line[LINESIZE + 1]; 637 638 while (i < argc) 639 { 640 Line[0] = '\0'; 641 offset = argv[optCount + i++]; 642 if (isOffset(offset)) 643 { 644 if (exefile) 645 { 646 l2l_dbg(2, "translating %s %s\n", exefile, offset); 647 translate_file(exefile, my_atoi(offset), Line); 648 printf("%s\n", Line); 649 report(conOut); 650 } 651 else 652 { 653 l2l_dbg(0, "<exefile> expected\n"); 654 res = 3; 655 break; 656 } 657 } 658 else 659 { 660 // Not an offset so must be an exefile: 661 exefile = offset; 662 } 663 } 664 } 665 else 666 { // translate logging from stdin 667 translate_files(dbgIn, conOut); 668 } 669 670 if (logFile) 671 fclose(logFile); 672 673 if (opt_Pipe) 674 PCLOSE(dbgIn); 675 676 cleanup: 677 // See optionParse(). 678 if (opt_Revision) 679 { 680 free(opt_Revision); 681 opt_Revision = NULL; 682 } 683 684 // See optionInit(). 685 if (opt_Pipe) 686 { 687 free(opt_Pipe); 688 opt_Pipe = NULL; 689 } 690 691 list_clear(&sources); 692 list_clear(&cache); 693 694 return res; 695 } 696 697 /* EOF */ 698