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 = malloc(LINESIZE + 1); 414 char *path = malloc(LINESIZE + 1); 415 char *LineOut = malloc(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 if (Line && path && LineOut) 424 { 425 memset(Line, '\0', LINESIZE + 1); 426 if (opt_console) 427 { 428 while ((c = fgetc(inFile)) != EOF) 429 { 430 if (opt_quit)break; 431 432 ch = (unsigned char)c; 433 if (!opt_raw) 434 { 435 switch (ch) 436 { 437 case '\n': 438 if ( strncmp(Line, KDBG_DISCARD, sizeof(KDBG_DISCARD)-1) == 0 ) 439 { 440 memset(Line, '\0', LINESIZE); // flushed 441 } 442 else 443 { 444 Line[1] = handle_escape_cmd(outFile, Line, path, LineOut); 445 if (Line[1] != KDBG_ESC_CHAR) 446 { 447 if (p == p_eos) 448 { 449 // kdbg prompt, so already echoed char by char 450 memset(Line, '\0', LINESIZE); 451 translate_char(c, outFile); 452 } 453 else 454 { 455 if (match_line(outFile, Line)) 456 { 457 translate_line(outFile, Line, path, LineOut); 458 translate_char(c, outFile); 459 report(outFile); 460 } 461 } 462 } 463 } 464 i = 0; 465 p = kdbg_prompt; 466 pc = kdbg_cont; 467 break; 468 case '<': 469 i = 0; 470 Line[i++] = ch; 471 break; 472 case '>': 473 if (ch == *p) 474 { 475 p = p_eos; 476 translate_line(outFile, Line, path, LineOut); 477 } 478 479 if (p != p_eos) 480 { 481 if (i < LINESIZE) 482 { 483 Line[i++] = ch; 484 translate_line(outFile, Line, path, LineOut); 485 } 486 else 487 { 488 translate_line(outFile, Line, path, LineOut); 489 translate_char(c, outFile); 490 } 491 } 492 else 493 translate_char(c, outFile); 494 i = 0; 495 break; 496 default: 497 if (ch == *p)p++; 498 if (ch == *pc)pc++; 499 if (i < LINESIZE) 500 { 501 Line[i++] = ch; 502 if (p == p_eos) 503 { 504 translate_char(c, outFile); 505 } 506 else if (!*pc) 507 { 508 translate_line(outFile, Line, path, LineOut); 509 i = 0; 510 } 511 } 512 else 513 { 514 translate_line(outFile, Line, path, LineOut); 515 translate_char(c, outFile); 516 i = 0; 517 } 518 } 519 } 520 else 521 translate_char(c, outFile); 522 } 523 } 524 else 525 { // Line by line, slightly faster but less interactive 526 while (fgets(Line, LINESIZE, inFile) != NULL) 527 { 528 if (opt_quit)break; 529 530 if (!opt_raw) 531 { 532 translate_line(outFile, Line, path, LineOut); 533 report(outFile); 534 } 535 else 536 log(outFile, "%s", Line); 537 } 538 } 539 } 540 541 if (opt_Revision && (strstr(opt_Revision, "regscan") == opt_Revision)) 542 { 543 char *s = strchr(opt_Revision, ','); 544 if (s) 545 { 546 *s++ = '\0'; 547 revinfo.range = atoi(s); 548 } 549 regscan(outFile); 550 } 551 552 if (opt_stats) 553 { 554 stat_print(outFile, &summ); 555 if (logFile) 556 stat_print(logFile, &summ); 557 } 558 free(LineOut); 559 free(Line); 560 free(path); 561 return 0; 562 } 563 564 565 int 566 main(int argc, const char **argv) 567 { 568 int res = 0; 569 int optInit = 0; 570 int optCount = 0; 571 572 dbgIn = stdin; 573 conOut = stdout; 574 (void)conIn; 575 (void)dbgOut; 576 577 memset(&cache, 0, sizeof(LIST)); 578 memset(&sources, 0, sizeof(LIST)); 579 stat_clear(&summ); 580 memset(&revinfo, 0, sizeof(REVINFO)); 581 clearLastLine(); 582 583 optInit = optionInit(argc, argv); 584 optCount = optionParse(argc, argv); 585 586 if (optCount < 0 || optInit < 0) 587 { 588 return optCount; 589 } 590 591 argc -= optCount; 592 593 if (opt_Revision && (strcmp(opt_Revision, "update") == 0)) 594 { 595 res = updateSvnlog(); 596 return res; 597 } 598 599 if (check_directory(opt_force)) 600 return 3; 601 602 create_cache(opt_force, 0); 603 if (opt_exit) 604 return 0; 605 606 read_cache(); 607 l2l_dbg(4, "Cache read complete\n"); 608 609 if (set_LogFile(&logFile)) 610 return 2; 611 l2l_dbg(4, "opt_logFile processed\n"); 612 613 if (opt_Pipe) 614 { 615 l2l_dbg(3, "Command line: \"%s\"\n",opt_Pipe); 616 617 if (!(dbgIn = POPEN(opt_Pipe, "r"))) 618 { 619 dbgIn = stdin; //restore 620 l2l_dbg(0, "Could not popen '%s' (%s)\n", opt_Pipe, strerror(errno)); 621 free(opt_Pipe); opt_Pipe = NULL; 622 } 623 } 624 l2l_dbg(4, "opt_Pipe processed\n"); 625 626 if (argc > 1) 627 { // translate {<exefile> <offset>} 628 int i = 1; 629 const char *exefile = NULL; 630 const char *offset = NULL; 631 char Line[LINESIZE + 1]; 632 633 while (i < argc) 634 { 635 Line[0] = '\0'; 636 offset = argv[optCount + i++]; 637 if (isOffset(offset)) 638 { 639 if (exefile) 640 { 641 l2l_dbg(2, "translating %s %s\n", exefile, offset); 642 translate_file(exefile, my_atoi(offset), Line); 643 printf("%s\n", Line); 644 report(conOut); 645 } 646 else 647 { 648 l2l_dbg(0, "<exefile> expected\n"); 649 res = 3; 650 break; 651 } 652 } 653 else 654 { 655 // Not an offset so must be an exefile: 656 exefile = offset; 657 } 658 } 659 } 660 else 661 { // translate logging from stdin 662 translate_files(dbgIn, conOut); 663 } 664 665 if (logFile) 666 fclose(logFile); 667 668 if (opt_Pipe) 669 PCLOSE(dbgIn); 670 671 return res; 672 } 673 674 /* EOF */ 675