reactos/sdk/tools/log2lines/log2lines.c
2020-02-22 13:48:18 +01:00

673 lines
17 KiB
C

/*
* ReactOS log2lines
* Written by Jan Roeloffzen
*
* - Initialization, translation and main loop
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"
#include "version.h"
#include "compat.h"
#include "options.h"
#include "image.h"
#include "cache.h"
#include "log2lines.h"
#include "help.h"
#include "cmd.h"
#include "match.h"
static FILE *dbgIn = NULL;
static FILE *dbgOut = NULL;
static FILE *conIn = NULL;
static FILE *conOut = NULL;
static const char *kdbg_prompt = KDBG_PROMPT;
static const char *kdbg_cont = KDBG_CONT;
LIST sources;
LINEINFO lastLine;
FILE *logFile = NULL;
LIST cache;
SUMM summ;
static void
clearLastLine(void)
{
memset(&lastLine, 0, sizeof(LINEINFO));
}
static void
log_file(FILE *outFile, char *fileName, int line)
{
int i = 0, min = 0, max = 0;
char s[LINESIZE];
FILE *src;
strcpy(s, opt_SourcesPath);
strcat(s, fileName);
max = line + opt_SrcPlus;
if ((src = fopen(s, "r")))
{
min = line - opt_Source;
min = (min < 0) ? 0 : min;
while (i < max && fgets(s, LINESIZE, src))
{
if (i >= min)
{
if (i == line)
log(outFile, "| ----\n");
log(outFile, "| %4.4d %s", i + 1, s);
}
i++;
}
fclose(src);
if ( i < min )
log(outFile, "| S--- source has only %d lines! (check source/revision)\n", i);
}
else
l2l_dbg(1, "Can't open: %s (check " SOURCES_ENV ")\n", s);
}
static void
logSource(FILE *outFile)
{
log_file(outFile, lastLine.file1, lastLine.nr1);
if (lastLine.nr2)
{
log(outFile, "| ---- [%u] ----\n", lastLine.nr2);
log_file(outFile, lastLine.file2, lastLine.nr2);
}
}
static void
reportSource(FILE *outFile)
{
if (!opt_Source)
return;
if (lastLine.valid)
logSource(outFile);
}
static void
report(FILE *outFile)
{
reportSource(outFile);
clearLastLine();
}
static int
print_offset(void *data, size_t offset, char *toString)
{
PSYMBOLFILE_HEADER RosSymHeader = (PSYMBOLFILE_HEADER)data;
PROSSYM_ENTRY e = NULL;
PROSSYM_ENTRY e2 = NULL;
int bFileOffsetChanged = 0;
char fmt[LINESIZE];
char *Strings = (char *)data + RosSymHeader->StringsOffset;
fmt[0] = '\0';
e = find_offset(data, offset);
if (opt_twice)
{
e2 = find_offset(data, offset - 1);
if (e == e2)
e2 = NULL;
else
summ.diff++;
if (opt_Twice && e2)
{
e = e2;
e2 = NULL;
/* replaced (transparantly), but updated stats */
}
}
if (e || e2)
{
strcpy(lastLine.file1, &Strings[e->FileOffset]);
strcpy(lastLine.func1, &Strings[e->FunctionOffset]);
lastLine.nr1 = e->SourceLine;
sources_entry_create(&sources, lastLine.file1, SVN_PREFIX);
lastLine.valid = 1;
if (e2)
{
strcpy(lastLine.file2, &Strings[e2->FileOffset]);
strcpy(lastLine.func2, &Strings[e2->FunctionOffset]);
lastLine.nr2 = e2->SourceLine;
sources_entry_create(&sources, lastLine.file2, SVN_PREFIX);
bFileOffsetChanged = e->FileOffset != e2->FileOffset;
if (e->FileOffset != e2->FileOffset || e->FunctionOffset != e2->FunctionOffset)
summ.majordiff++;
/*
* - "%.0s" displays nothing, but processes argument
* - bFileOffsetChanged implies always display 2nd SourceLine even if the same
* - also for FunctionOffset
*/
strcat(fmt, "%s");
if (bFileOffsetChanged)
strcat(fmt, "[%s]");
else
strcat(fmt, "%.0s");
strcat(fmt, ":%u");
if (e->SourceLine != e2->SourceLine || bFileOffsetChanged)
strcat(fmt, "[%u]");
else
strcat(fmt, "%.0u");
strcat(fmt, " (%s");
if (e->FunctionOffset != e2->FunctionOffset || bFileOffsetChanged)
strcat(fmt, "[%s])");
else
strcat(fmt, "%.0s)");
if (toString)
{ // put in toString if provided
snprintf(toString, LINESIZE, fmt,
&Strings[e->FileOffset],
&Strings[e2->FileOffset],
(unsigned int)e->SourceLine,
(unsigned int)e2->SourceLine,
&Strings[e->FunctionOffset],
&Strings[e2->FunctionOffset]);
}
else
{
strcat(fmt, "\n");
printf(fmt,
&Strings[e->FileOffset],
&Strings[e2->FileOffset],
(unsigned int)e->SourceLine,
(unsigned int)e2->SourceLine,
&Strings[e->FunctionOffset],
&Strings[e2->FunctionOffset]);
}
}
else
{
if (toString)
{ // put in toString if provided
snprintf(toString, LINESIZE, "%s:%u (%s)",
&Strings[e->FileOffset],
(unsigned int)e->SourceLine,
&Strings[e->FunctionOffset]);
}
else
{
printf("%s:%u (%s)\n",
&Strings[e->FileOffset],
(unsigned int)e->SourceLine,
&Strings[e->FunctionOffset]);
}
}
return 0;
}
return 1;
}
static int
process_data(const void *FileData, size_t offset, char *toString)
{
int res;
PIMAGE_SECTION_HEADER PERosSymSectionHeader = get_sectionheader((char *)FileData);
if (!PERosSymSectionHeader)
return 2;
res = print_offset((char *)FileData + PERosSymSectionHeader->PointerToRawData, offset, toString);
if (res)
{
if (toString)
sprintf(toString, "??:0");
else
printf("??:0");
l2l_dbg(1, "Offset not found: %x\n", (unsigned int)offset);
summ.offset_errors++;
}
return res;
}
static int
process_file(const char *file_name, size_t offset, char *toString)
{
void *FileData;
size_t FileSize;
int res = 1;
FileData = load_file(file_name, &FileSize);
if (!FileData)
{
l2l_dbg(0, "An error occured loading '%s'\n", file_name);
}
else
{
res = process_data(FileData, offset, toString);
free(FileData);
}
return res;
}
static int
translate_file(const char *cpath, size_t offset, char *toString)
{
size_t base = 0;
LIST_MEMBER *pentry = NULL;
int res = 0;
char *path, *dpath;
dpath = path = convert_path(cpath);
if (!path)
return 1;
// The path could be absolute:
if (get_ImageBase(path, &base))
{
pentry = entry_lookup(&cache, path);
if (pentry)
{
path = pentry->path;
base = pentry->ImageBase;
if (base == INVALID_BASE)
{
l2l_dbg(1, "No, or invalid base address: %s\n", path);
res = 2;
}
}
else
{
l2l_dbg(1, "Not found in cache: %s\n", path);
res = 3;
}
}
if (!res)
{
res = process_file(path, offset, toString);
}
free(dpath);
return res;
}
static void
translate_char(int c, FILE *outFile)
{
fputc(c, outFile);
if (logFile)
fputc(c, logFile);
}
static char *
remove_mark(char *Line)
{
if (Line[1] == ' ' && Line[2] == '<')
if (Line[0] == '*' || Line[0] == '?')
return Line + 2;
return Line;
}
static void
translate_line(FILE *outFile, char *Line, char *path, char *LineOut)
{
unsigned int offset;
int cnt, res;
char *sep, *tail, *mark, *s;
unsigned char ch;
if (!*Line)
return;
res = 1;
mark = "";
s = remove_mark(Line);
if (opt_undo)
{
/* Strip all lines added by this tool: */
char buf[NAMESIZE];
if (sscanf(s, "| %s", buf) == 1)
if (buf[0] == '0' || strcmp(buf, "----") == 0 || strcmp(buf, "L2L-") == 0 || strcmp(buf, "S---") == 0 || strcmp(buf, "R---") == 0 || atoi(buf))
res = 0;
}
sep = strchr(s, ':');
if (sep)
{
*sep = ' ';
cnt = sscanf(s, "<%s %x%c", path, &offset, &ch);
if (opt_undo)
{
if (cnt == 3 && ch == ' ')
{
tail = strchr(s, '>');
tail = tail ? tail - 1 : tail;
if (tail && tail[0] == ')' && tail[1] == '>')
{
res = 0;
tail += 2;
mark = opt_mark ? "* " : "";
if (opt_redo && !(res = translate_file(path, offset, LineOut)))
{
log(outFile, "%s<%s:%x (%s)>%s", mark, path, offset, LineOut, tail);
summ.redo++;
}
else
{
log(outFile, "%s<%s:%x>%s", mark, path, offset, tail);
summ.undo++;
}
}
else
{
mark = opt_Mark ? "? " : "";
summ.skipped++;
}
summ.total++;
}
}
if (!opt_undo || opt_redo)
{
if (cnt == 3 && ch == '>')
{
tail = strchr(s, '>') + 1;
if (!(res = translate_file(path, offset, LineOut)))
{
mark = opt_mark ? "* " : "";
log(outFile, "%s<%s:%x (%s)>%s", mark, path, offset, LineOut, tail);
summ.translated++;
}
else
{
mark = opt_Mark ? "? " : "";
summ.skipped++;
}
summ.total++;
}
}
}
if (res)
{
if (sep)
*sep = ':'; // restore because not translated
log(outFile, "%s%s", mark, s);
}
memset(Line, '\0', LINESIZE); // flushed
}
static int
translate_files(FILE *inFile, FILE *outFile)
{
char Line[LINESIZE + 1];
char path[LINESIZE + 1];
char LineOut[LINESIZE + 1];
int c;
unsigned char ch;
int i = 0;
const char *pc = kdbg_cont;
const char *p = kdbg_prompt;
const char *p_eos = p + sizeof(KDBG_PROMPT) - 1; //end of string pos
memset(Line, '\0', LINESIZE + 1);
if (opt_console)
{
while ((c = fgetc(inFile)) != EOF)
{
if (opt_quit)break;
ch = (unsigned char)c;
if (!opt_raw)
{
switch (ch)
{
case '\n':
if ( strncmp(Line, KDBG_DISCARD, sizeof(KDBG_DISCARD)-1) == 0 )
{
memset(Line, '\0', LINESIZE); // flushed
}
else
{
Line[1] = handle_escape_cmd(outFile, Line);
if (Line[1] != KDBG_ESC_CHAR)
{
if (p == p_eos)
{
// kdbg prompt, so already echoed char by char
memset(Line, '\0', LINESIZE);
translate_char(c, outFile);
}
else
{
if (match_line(outFile, Line))
{
translate_line(outFile, Line, path, LineOut);
translate_char(c, outFile);
report(outFile);
}
}
}
}
i = 0;
p = kdbg_prompt;
pc = kdbg_cont;
break;
case '<':
i = 0;
Line[i++] = ch;
break;
case '>':
if (ch == *p)
{
p = p_eos;
translate_line(outFile, Line, path, LineOut);
}
if (p != p_eos)
{
if (i < LINESIZE)
{
Line[i++] = ch;
translate_line(outFile, Line, path, LineOut);
}
else
{
translate_line(outFile, Line, path, LineOut);
translate_char(c, outFile);
}
}
else
translate_char(c, outFile);
i = 0;
break;
default:
if (ch == *p)p++;
if (ch == *pc)pc++;
if (i < LINESIZE)
{
Line[i++] = ch;
if (p == p_eos)
{
translate_char(c, outFile);
}
else if (!*pc)
{
translate_line(outFile, Line, path, LineOut);
i = 0;
}
}
else
{
translate_line(outFile, Line, path, LineOut);
translate_char(c, outFile);
i = 0;
}
}
}
else
translate_char(c, outFile);
}
}
else
{ // Line by line, slightly faster but less interactive
while (fgets(Line, LINESIZE, inFile) != NULL)
{
if (opt_quit)break;
if (!opt_raw)
{
translate_line(outFile, Line, path, LineOut);
report(outFile);
}
else
log(outFile, "%s", Line);
}
}
if (opt_stats)
{
stat_print(outFile, &summ);
if (logFile)
stat_print(logFile, &summ);
}
return 0;
}
int
main(int argc, const char **argv)
{
int res = 0;
int optInit = 0;
int optCount = 0;
dbgIn = stdin;
conOut = stdout;
(void)conIn;
(void)dbgOut;
memset(&cache, 0, sizeof(LIST));
memset(&sources, 0, sizeof(LIST));
stat_clear(&summ);
clearLastLine();
optInit = optionInit(argc, argv);
optCount = optionParse(argc, argv);
if (optCount < 0 || optInit < 0)
{
res = optCount;
goto cleanup;
}
argc -= optCount;
if (check_directory(opt_force))
{
res = 3;
goto cleanup;
}
create_cache(opt_force, 0);
if (opt_exit)
{
res = 0;
goto cleanup;
}
read_cache();
l2l_dbg(4, "Cache read complete\n");
if (set_LogFile(&logFile))
{
res = 2;
goto cleanup;
}
l2l_dbg(4, "opt_logFile processed\n");
if (opt_Pipe)
{
l2l_dbg(3, "Command line: \"%s\"\n",opt_Pipe);
if (!(dbgIn = POPEN(opt_Pipe, "r")))
{
dbgIn = stdin; //restore
l2l_dbg(0, "Could not popen '%s' (%s)\n", opt_Pipe, strerror(errno));
free(opt_Pipe);
opt_Pipe = NULL;
}
}
l2l_dbg(4, "opt_Pipe processed\n");
if (argc > 1)
{ // translate {<exefile> <offset>}
int i = 1;
const char *exefile = NULL;
const char *offset = NULL;
char Line[LINESIZE + 1];
char PathBuffer[LINESIZE + 1];
char LineOutBuffer[LINESIZE + 1];
// TODO: Re-use one translate_files(), instead of repeated translate_line().
while (i < argc)
{
offset = argv[optCount + i++];
if (isOffset(offset))
{
if (exefile)
{
l2l_dbg(2, "translating %s %s\n", exefile, offset);
snprintf(Line, LINESIZE, "<%s:%s>\n", exefile, offset);
translate_line(conOut, Line, PathBuffer, LineOutBuffer);
report(conOut);
}
else
{
l2l_dbg(0, "<exefile> expected\n");
res = 3;
break;
}
}
else
{
// Not an offset so must be an exefile:
exefile = offset;
}
}
}
else
{ // translate logging from stdin
translate_files(dbgIn, conOut);
}
if (logFile)
fclose(logFile);
if (opt_Pipe)
PCLOSE(dbgIn);
cleanup:
// See optionInit().
if (opt_Pipe)
{
free(opt_Pipe);
opt_Pipe = NULL;
}
list_clear(&sources);
list_clear(&cache);
return res;
}
/* EOF */