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