xref: /reactos/sdk/tools/log2lines/log2lines.c (revision e08ae510)
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