xref: /reactos/sdk/tools/log2lines/log2lines.c (revision 6b0ddf68)
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
clearLastLine(void)40 clearLastLine(void)
41 {
42     memset(&lastLine, 0, sizeof(LINEINFO));
43 }
44 
45 static void
log_file(FILE * outFile,char * fileName,int line)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
logSource(FILE * outFile)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
reportSource(FILE * outFile)90 reportSource(FILE *outFile)
91 {
92     if (!opt_Source)
93         return;
94     if (lastLine.valid)
95         logSource(outFile);
96 }
97 
98 static void
report(FILE * outFile)99 report(FILE *outFile)
100 {
101     reportSource(outFile);
102     clearLastLine();
103 }
104 
105 
106 static int
print_offset(void * data,size_t offset,char * toString)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
process_data(const void * FileData,size_t offset,char * toString)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
process_file(const char * file_name,size_t offset,char * toString)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
translate_file(const char * cpath,size_t offset,char * toString)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
translate_char(int c,FILE * outFile)304 translate_char(int c, FILE *outFile)
305 {
306     fputc(c, outFile);
307     if (logFile)
308         fputc(c, logFile);
309 }
310 
311 static char *
remove_mark(char * Line)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
translate_line(FILE * outFile,char * Line,char * path,char * LineOut)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
translate_files(FILE * inFile,FILE * outFile)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
main(int argc,const char ** argv)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