summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2009-11-25 16:39:18 +0100
committerKarel Klic <kklic@redhat.com>2009-11-25 16:39:18 +0100
commit02e46d27da349907b2a1f389ce7043b6e40c45cb (patch)
tree93726630ecefd98a9787fd4121b10ebc42165a42
parent6b30462c4b8f65e7a76009cfd1308e651a973c58 (diff)
Parser simplified and even more corner cases added. Backtrace file is now loaded into memory and preprocessed before parsing. Backtrace file size limit increased to ~20 MB
-rw-r--r--src/Backtrace/backtrace.h2
-rw-r--r--src/Backtrace/fallback.c30
-rw-r--r--src/Backtrace/fallback.h2
-rw-r--r--src/Backtrace/main.c74
-rw-r--r--src/Backtrace/parser.y120
5 files changed, 130 insertions, 98 deletions
diff --git a/src/Backtrace/backtrace.h b/src/Backtrace/backtrace.h
index 3091c63..ed6d315 100644
--- a/src/Backtrace/backtrace.h
+++ b/src/Backtrace/backtrace.h
@@ -89,6 +89,6 @@ extern void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth);
extern void backtrace_remove_exit_handlers(struct backtrace *backtrace);
/* Defined in parser.y. */
-extern struct backtrace *do_parse(FILE *input, bool debug_parser, bool debug_scanner);
+extern struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner);
#endif
diff --git a/src/Backtrace/fallback.c b/src/Backtrace/fallback.c
index b77a7cb..70e5e43 100644
--- a/src/Backtrace/fallback.c
+++ b/src/Backtrace/fallback.c
@@ -19,9 +19,6 @@
#include <stdlib.h>
#include <stdbool.h>
-/* Too large files are trimmed. */
-#define FILE_SIZE_LIMIT 10000000 /* ~ 10 MB */
-
struct header
{
struct strbuf *text;
@@ -70,30 +67,8 @@ static void header_set_insert(struct header *cur, struct strbuf *new)
header_set_insert(cur->next, new);
}
-struct strbuf *independent_backtrace(FILE *fp)
+struct strbuf *independent_backtrace(char *input)
{
- long pos = ftell(fp);
- fseek(fp, 0, SEEK_END);
- long size = ftell(fp) - pos;
- fseek(fp, pos, SEEK_SET);
-
- /* Silently handle large files. */
- if (size > FILE_SIZE_LIMIT)
- size = FILE_SIZE_LIMIT;
-
- char *contents = malloc(size);
- if (!contents)
- {
- puts("Error while allocating memory for independent backtrace.");
- exit(5);
- }
-
- if (fread(contents, size, 1, fp) != 1)
- {
- puts("Error while reading input file.");
- exit(6);
- }
-
struct strbuf *header = strbuf_new();
bool in_bracket = false;
bool in_quote = false;
@@ -104,7 +79,7 @@ struct strbuf *independent_backtrace(FILE *fp)
bool has_bracket = false;
struct header *headers = NULL;
- const char *bk = contents;
+ const char *bk = input;
while (*bk)
{
if (bk[0] == '#'
@@ -179,7 +154,6 @@ struct strbuf *independent_backtrace(FILE *fp)
}
strbuf_free(header);
- free(contents);
struct strbuf *result = strbuf_new();
struct header *loop = headers;
diff --git a/src/Backtrace/fallback.h b/src/Backtrace/fallback.h
index 9c31fec..85b0632 100644
--- a/src/Backtrace/fallback.h
+++ b/src/Backtrace/fallback.h
@@ -27,6 +27,6 @@
* The independent backtrace. Caller is responsible for calling
* strbuf_free() on it.
*/
-extern struct strbuf *independent_backtrace(FILE *fp);
+extern struct strbuf *independent_backtrace(char *input);
#endif
diff --git a/src/Backtrace/main.c b/src/Backtrace/main.c
index 459185e..315b6ef 100644
--- a/src/Backtrace/main.c
+++ b/src/Backtrace/main.c
@@ -20,10 +20,14 @@
#include <argp.h>
#include <stdlib.h>
#include <sysexits.h>
+#include <string.h>
#include "config.h"
#include "backtrace.h"
#include "fallback.h"
+/* Too large files are trimmed. */
+#define FILE_SIZE_LIMIT 20000000 /* ~ 20 MB */
+
#define EX_PARSINGFAILED EX__MAX + 1
#define EX_THREADDETECTIONFAILED EX__MAX + 2
@@ -125,7 +129,7 @@ int main(int argc, char **argv)
arguments.filename = 0;
argp_parse(&argp, argc, argv, 0, 0, &arguments);
- /* Open input file and parse it. */
+ /* Open input file, and parse it. */
FILE *fp = fopen(arguments.filename, "r");
if (!fp)
{
@@ -133,33 +137,85 @@ int main(int argc, char **argv)
exit(EX_NOINPUT); /* No such file or directory */
}
+ /* Header and footer of the backtrace is stripped to simplify the parser.
+ * A drawback is that the backtrace must be loaded to memory.
+ */
+ fseek(fp, 0, SEEK_END);
+ size_t size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ if (size > FILE_SIZE_LIMIT)
+ {
+ fprintf(stderr, "Input file too big (%zd). Maximum size is %zd",
+ size, FILE_SIZE_LIMIT);
+ exit(EX_IOERR);
+ }
+
+ char *bttext = malloc(size + 1);
+ if (1 != fread(bttext, size, 1, fp))
+ {
+ fprintf(stderr, "Unable to read from '%s'.\n", arguments.filename);
+ exit(EX_IOERR); /* IO Error */
+ }
+
+ bttext[size] = '\0';
+ fclose(fp);
+
/* Print independent backtrace and exit. */
if (arguments.independent)
{
- struct strbuf *ibt = independent_backtrace(fp);
- fclose(fp);
+ struct strbuf *ibt = independent_backtrace(bttext);
puts(ibt->buf);
strbuf_free(ibt);
+ free(bttext);
return 0; /* OK */
}
+ /* Skip the backtrace header information. */
+ char *btnoheader_a = strstr(bttext, "\nThread ");
+ char *btnoheader_b = strstr(bttext, "#");
+ char *btnoheader = bttext;
+ if (btnoheader < btnoheader_a)
+ btnoheader = btnoheader_a + 1;
+ if (btnoheader < btnoheader_b)
+ btnoheader = btnoheader_b;
+
+ /* Cut the backtrace footer.
+ * Footer: lines not starting with # or "Thread", and separated from
+ * the backtrace body by a newline.
+ */
+ int i;
+ for (i = size - 1; i > 0; --i)
+ {
+ if (bttext[i] != '\n')
+ continue;
+ if (strncmp(bttext + i + 1, "Thread", strlen("Thread")) == 0)
+ break;
+ if (bttext[i + 1] == '#')
+ break;
+ if (bttext[i - 1] == '\n')
+ {
+ bttext[i] = '\0';
+ break;
+ }
+ }
+
/* Try to parse the backtrace. */
struct backtrace *backtrace;
- backtrace = do_parse(fp, arguments.debug_parser, arguments.debug_scanner);
+ backtrace = do_parse(btnoheader, arguments.debug_parser, arguments.debug_scanner);
/* If the parser failed print independent backtrace. */
if (!backtrace)
{
- fseek(fp, 0, SEEK_SET);
- struct strbuf *ibt = independent_backtrace(fp);
- fclose(fp);
+ struct strbuf *ibt = independent_backtrace(bttext);
puts(ibt->buf);
strbuf_free(ibt);
- /* PARSING FAILED, BUT OUTPUT CAN BE USED */
+ free(bttext);
+ /* Parsing failed, but the output can be used. */
return EX_PARSINGFAILED;
}
- fclose(fp);
+ free(bttext);
/* If a single thread is requested, remove all other threads. */
int retval = 0;
diff --git a/src/Backtrace/parser.y b/src/Backtrace/parser.y
index 8512366..ae068a1 100644
--- a/src/Backtrace/parser.y
+++ b/src/Backtrace/parser.y
@@ -48,7 +48,7 @@ void yyerror(char const *s)
/* Bison declarations. */
%token END 0 "end of file"
-%type <backtrace> backtrace ignoredpart_backtrace
+%type <backtrace> backtrace
%type <thread> threads thread
%type <frame> frames frame frame_head frame_head_1 frame_head_2 frame_head_3 frame_head_4 frame_head_5
%type <strbuf> identifier hexadecimal_digit_sequence hexadecimal_number file_name file_location function_call function_name digit_sequence frame_address_in_function
@@ -74,33 +74,25 @@ void yyerror(char const *s)
%% /* The grammar follows. */
-backtrace : /* empty */ { $$ = g_backtrace = backtrace_new(); }
- | ignoredpart_backtrace wsa { $$ = g_backtrace = $1; }
-;
-
-/**/
-ignoredpart_backtrace : threads %dprec 2
- {
- $$ = backtrace_new();
- $$->threads = $1;
- }
- | frame_head wss threads %dprec 4
- {
- $$ = backtrace_new();
- $$->threads = $3;
- $$->crash = $1;
- }
- | frame wss threads %dprec 3
- {
- $$ = backtrace_new();
- $$->threads = $3;
- $$->crash = $1;
- }
- | anychar ignoredpart_backtrace %dprec 1 { $$ = $2; }
-;
-
-anychar : ws | digit | nondigit | '(' | ')' | '+' | '-' | '#' | '=' | ':' | ';'
- | '/' | '.' | '[' | ']' | '?' | '\'' | '`' | ',' | '<' | '>' | '"'
+backtrace : /* empty */ %dprec 1
+ { $$ = g_backtrace = backtrace_new(); }
+ | threads wsa %dprec 2
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $1;
+ }
+ | frame_head wss threads wsa %dprec 4
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $3;
+ $$->crash = $1;
+ }
+ | frame wss threads wsa %dprec 3
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $3;
+ $$->crash = $1;
+ }
;
threads : thread { $$ = $1; }
@@ -217,36 +209,32 @@ file_location : file_name ':' digit_sequence
variables : variables_line '\n'
| variables_line END
- | variables_line variables_wss '\n'
- | variables_line variables_wss END
+ | variables_line wss_nonl '\n'
+ | variables_line wss_nonl END
| variables variables_line '\n'
| variables variables_line END
- | variables variables_wss variables_line '\n'
- | variables variables_wss variables_line END
- | variables variables_wss variables_line variables_wss '\n'
- | variables variables_wss variables_line variables_wss END
+ | variables wss_nonl variables_line '\n'
+ | variables wss_nonl variables_line END
+ | variables wss_nonl variables_line wss_nonl '\n'
+ | variables wss_nonl variables_line wss_nonl END
;
variables_line : variables_char_no_framestart
| variables_line variables_char
- | variables_line variables_wss variables_char
-;
-
-variables_ws : '\t' | ' '
-;
-
-variables_wss : variables_ws
- | variables_wss variables_ws
+ | variables_line wss_nonl variables_char
;
variables_char : '#' | variables_char_no_framestart
;
-variables_char_no_framestart : digit | nondigit | '(' | ')' | '+' | '-' | '<'
- | '>' | '"' | '/' | '.' | '[' | ']' | '?' | '\''
- | '`' | ',' | '=' | '{' | '}' | '^' | '&' | '$'
- | ':' | ';' | '\\' | '!' | '@' | '*' | '%' | '|'
- | '~'
+/* Manually synchronized with function_args_char. */
+variables_char_no_framestart : digit | nondigit | '"' | '(' | ')'
+ | '+' | '-' | '<' | '>' | '/' | '.'
+ | '[' | ']' | '?' | '\'' | '`' | ','
+ | '=' | '{' | '}' | '^' | '&' | '$'
+ | ':' | ';' | '\\' | '!' | '@' | '*'
+ | '%' | '|' | '~'
+;
function_call : function_name wsa function_args
;
@@ -263,20 +251,24 @@ function_args : '(' wsa ')'
| '(' wsa function_args_sequence wsa ')'
;
- /* TODO: function arguments can contain strings in "". As the string can
- contain any ascii-visible character (nonvisible chars are escaped),
- this must be somehow handled, especially characters ( and ). */
function_args_sequence : function_args_char
+ | function_args_sequence wsa '(' wsa ')'
+ | function_args_sequence wsa '(' wsa function_args_sequence wsa ')'
| function_args_sequence wsa function_args_char
| function_args_sequence wsa function_args_string
;
function_args_string : '"' function_args_string_sequence '"'
+ | '"' '"'
;
-function_args_char : digit | nondigit | '{' | '}' | '<' | '>' | ':' | '~'
- | '=' | '-' | '+' | '@' | ',' | '.' | '[' | ']' | '/' | '%'
- | '\\' | '&'
+/* Manually synchronized with variables_char_no_framestart. */
+function_args_char : digit | nondigit
+ | '+' | '-' | '<' | '>' | '/' | '.'
+ | '[' | ']' | '?' | '\'' | '`' | ','
+ | '=' | '{' | '}' | '^' | '&' | '$'
+ | ':' | ';' | '\\' | '!' | '@' | '*'
+ | '%' | '|' | '~'
;
function_args_string_sequence : function_args_string_char
@@ -348,7 +340,16 @@ nondigit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'
;
/* whitespace */
-ws : '\t' | ' ' | '\n' | '\r'
+ws : ws_nonl | '\n' | '\r'
+;
+
+ /* No newline.*/
+ws_nonl : '\t' | ' '
+;
+
+ /* whitespace sequence without a newline */
+wss_nonl : ws_nonl
+ | wss_nonl ws_nonl
;
/* whitespace sequence */
@@ -379,13 +380,14 @@ keyword_sighandler: '<' 's' 'i' 'g' 'n' 'a' 'l' ' ' 'h' 'a' 'n' 'd' 'l' 'e' 'r'
%%
static bool scanner_echo = false;
-static FILE *yyin;
+static char *yyin;
int yylex()
{
- int c = fgetc(yyin);
- if (c == EOF)
- return 0;
+ char c = *yyin;
+ if (c == '\0')
+ return END;
+ ++yyin;
/* Debug output. */
if (scanner_echo)
@@ -403,7 +405,7 @@ int yylex()
* backtrace_free() on this.
* Returns NULL when parsing failed.
*/
-struct backtrace *do_parse(FILE *input, bool debug_parser, bool debug_scanner)
+struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner)
{
/* Prepare for running parser. */
g_backtrace = 0;