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