summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2009-11-11 19:08:50 +0100
committerKarel Klic <kklic@redhat.com>2009-11-11 19:08:50 +0100
commit179fcfcc6d9d0aa19c6e7f36ce9e8d453bb99793 (patch)
tree0101b65845f7fa91794f99766d8f7ef2c95620c4 /src
parent3a0b5daaaa268e1ac384a4d754fdd2e9a9608171 (diff)
downloadabrt-179fcfcc6d9d0aa19c6e7f36ce9e8d453bb99793.tar.gz
abrt-179fcfcc6d9d0aa19c6e7f36ce9e8d453bb99793.tar.xz
abrt-179fcfcc6d9d0aa19c6e7f36ce9e8d453bb99793.zip
Backtrace tool WORK-IN-PROGRESS
Diffstat (limited to 'src')
-rw-r--r--src/Backtrace/Makefile.am22
-rwxr-xr-xsrc/Backtrace/abrt-bz-downloader60
-rw-r--r--src/Backtrace/backtrace.c138
-rw-r--r--src/Backtrace/backtrace.h63
-rw-r--r--src/Backtrace/fallback.c194
-rw-r--r--src/Backtrace/fallback.h32
-rw-r--r--src/Backtrace/main.c135
-rw-r--r--src/Backtrace/parser.y272
-rw-r--r--src/Backtrace/strbuf.c95
-rw-r--r--src/Backtrace/strbuf.h35
-rw-r--r--src/Makefile.am2
11 files changed, 1047 insertions, 1 deletions
diff --git a/src/Backtrace/Makefile.am b/src/Backtrace/Makefile.am
new file mode 100644
index 00000000..e188c460
--- /dev/null
+++ b/src/Backtrace/Makefile.am
@@ -0,0 +1,22 @@
+bin_PROGRAMS = abrt-backtrace
+
+#BUILT_SOURCES = parser.h
+#AM_YFLAGS = -d
+AM_YFLAGS = --verbose
+
+abrt_backtrace_SOURCES = \
+ main.c \
+ backtrace.h backtrace.c \
+ strbuf.h strbuf.c \
+ fallback.h fallback.c \
+ parser.y
+
+#abrt_backtrace_CPPFLAGS = \
+# -I$(srcdir)/../../inc \
+# -I$(srcdir)/../../lib/Utils
+
+#abrt_backtrace_LDADD = \
+# ../../lib/Utils/libABRTUtils.la
+
+#man_MANS = abrt-backtrace.1
+#EXTRA_DIST = $(man_MANS)
diff --git a/src/Backtrace/abrt-bz-downloader b/src/Backtrace/abrt-bz-downloader
new file mode 100755
index 00000000..44dc4e41
--- /dev/null
+++ b/src/Backtrace/abrt-bz-downloader
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+# -*- mode:python -*-
+
+from bugzilla import RHBugzilla
+from optparse import OptionParser
+import sys
+import os.path
+
+parser = OptionParser(version="%prog 1.0")
+parser.add_option("-u", "--user", dest="user",
+ help="Bugzilla user name (REQUIRED)", metavar="USERNAME")
+parser.add_option("-p", "--password", dest="password",
+ help="Bugzilla password (REQUIRED)", metavar="PASSWORD")
+parser.add_option("-b", "--bugzilla", dest="bugzilla",
+ help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL")
+parser.add_option("-f", "--fields",
+ action="store_true", dest="fields", default=False,
+ help="Print possible bug fields and exit.")
+
+(options, args) = parser.parse_args()
+
+if not options.user or len(options.user) == 0:
+ parser.error("User name is required.\nTry {0} --help".format(sys.argv[0]))
+
+if not options.password or len(options.password) == 0:
+ parser.error("Password is required.\nTry {0} --help".format(sys.argv[0]))
+
+if not options.bugzilla or len(options.bugzilla) == 0:
+ options.bugzilla = "https://bugzilla.redhat.com/xmlrpc.cgi"
+
+bz = RHBugzilla()
+bz.connect(options.bugzilla)
+bz.login(options.user, options.password)
+
+if options.fields:
+ print bz.bugfields
+ exit(0)
+
+buginfos = bz.query({'status_whiteboard_type':'allwordssubstr','status_whiteboard':'abrt_hash'})
+
+print "{0} bugs found.".format(len(buginfos))
+
+for buginfo in buginfos:
+ # Skip bugs with already downloaded backtraces.
+ filename = "{0}.bt".format(buginfo.bug_id)
+ if os.path.isfile(filename):
+ print "Skipping {0} (already exists).".format(filename)
+ continue
+
+ # Get backtrace from bug and store it as a file.
+ bug = bz.getbug(buginfo.bug_id)
+ for attachment in bug.attachments:
+ if attachment['filename'] == 'backtrace':
+ data = bz.openattachment(attachment['id'])
+ f = open(filename, 'w')
+ f.write(data.read())
+ f.close()
+ print "Attachment {0} downloaded.".format(filename)
+
+bz.logout()
diff --git a/src/Backtrace/backtrace.c b/src/Backtrace/backtrace.c
new file mode 100644
index 00000000..3777c0f4
--- /dev/null
+++ b/src/Backtrace/backtrace.c
@@ -0,0 +1,138 @@
+/*
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "backtrace.h"
+#include <stdlib.h>
+
+struct frame *frame_new()
+{
+ struct frame *f = malloc(sizeof(struct frame));
+ if (!f)
+ {
+ puts("Error while allocating memory for backtrace frame.");
+ exit(5);
+ }
+
+ f->function = NULL;
+ f->args = NULL;
+ f->number = 0;
+ f->binfile = NULL;
+ f->sourcefile = NULL;
+ f->crash = false;
+ f->next = NULL;
+ return f;
+}
+
+void frame_free(struct frame *f)
+{
+ if (f->function)
+ free(f->function);
+ if (f->args)
+ free(f->args);
+ if (f->binfile)
+ free(f->binfile);
+ if (f->sourcefile)
+ free(f->sourcefile);
+ free(f);
+}
+
+struct frame *frame_add_sibling(struct frame *a, struct frame *b)
+{
+ struct frame *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+struct thread *thread_new()
+{
+ struct thread *t = malloc(sizeof(struct thread));
+ if (!t)
+ {
+ puts("Error while allocating memory for backtrace thread.");
+ exit(5);
+ }
+
+ t->number = 0;
+ t->frames = NULL;
+ t->next = NULL;
+}
+
+void thread_free(struct thread *t)
+{
+ while (t->frames)
+ {
+ struct frame *rm = t->frames;
+ t->frames = rm->next;
+ frame_free(rm);
+ }
+
+ free(t);
+}
+
+struct thread *thread_add_sibling(struct thread *a, struct thread *b)
+{
+ struct thread *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+struct backtrace *backtrace_new()
+{
+ struct backtrace *bt = malloc(sizeof(struct backtrace));
+ if (!bt)
+ {
+ puts("Error while allocating memory for backtrace.");
+ exit(5);
+ }
+
+ bt->threads = NULL;
+}
+
+void backtrace_free(struct backtrace *bt)
+{
+ while (bt->threads)
+ {
+ struct thread *rm = bt->threads;
+ bt->threads = rm->next;
+ thread_free(rm);
+ }
+
+ free(bt);
+}
+
+static int backtrace_get_thread_count(struct backtrace *bt)
+{
+ struct thread *t = bt->threads;
+ int count = 0;
+ while (t)
+ {
+ t = t->next;
+ ++count;
+ }
+ return count;
+}
+
+void backtrace_print_tree(struct backtrace *bt)
+{
+ printf("Thread count: %d\n", backtrace_get_thread_count(bt));
+}
diff --git a/src/Backtrace/backtrace.h b/src/Backtrace/backtrace.h
new file mode 100644
index 00000000..f42cfcd8
--- /dev/null
+++ b/src/Backtrace/backtrace.h
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef BACKTRACE_H
+#define BACKTRACE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+
+struct frame
+{
+ char *function;
+ char *args;
+ int number;
+ char *binfile;
+ char *sourcefile;
+ bool crash;
+ struct frame *next;
+};
+
+struct thread
+{
+ int number;
+ struct frame *frames;
+ struct thread *next;
+};
+
+struct backtrace
+{
+ struct thread *threads;
+};
+
+extern struct frame *frame_new();
+extern void frame_free(struct frame *f);
+extern struct frame *frame_add_sibling(struct frame *a, struct frame *b);
+
+extern struct thread *thread_new();
+extern void thread_free(struct thread *t);
+extern struct thread *thread_add_sibling(struct thread *a, struct thread *b);
+
+extern struct backtrace *backtrace_new();
+extern void backtrace_free(struct backtrace *bt);
+/* Prints how internal backtrace representation looks to stdout. */
+extern void backtrace_print_tree(struct backtrace *bt);
+
+/* Defined in parser.y. */
+extern struct backtrace *do_parse(FILE *input, bool debug_parser, bool debug_scanner);
+
+#endif
diff --git a/src/Backtrace/fallback.c b/src/Backtrace/fallback.c
new file mode 100644
index 00000000..04425337
--- /dev/null
+++ b/src/Backtrace/fallback.c
@@ -0,0 +1,194 @@
+/*
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "fallback.h"
+#include <stdlib.h>
+#include <stdbool.h>
+
+/* Too large files are trimmed. */
+#define FILE_SIZE_LIMIT 10000000 /* ~ 10 MB */
+
+struct header
+{
+ struct strbuf *text;
+ struct header *next;
+};
+
+static struct header *header_new()
+{
+ struct header *head = malloc(sizeof(struct header));
+ if (!head)
+ {
+ puts("Error while allocating memory for backtrace header.");
+ exit(5);
+ }
+ head->text = NULL;
+ head->next = NULL;
+ return head;
+}
+
+/* Recursively frees siblings. */
+static void header_free(struct header *head)
+{
+ strbuf_free(head->text);
+ if (head->next)
+ header_free(head->next);
+ free(head);
+}
+
+/* Inserts new header to array if it is not already there. */
+static void header_set_insert(struct header *cur, struct strbuf *new)
+{
+ /* Duplicate found case. */
+ if (strcmp(cur->text->buf, new->buf) == 0)
+ return;
+
+ /* Last item case, insert new header here. */
+ if (cur->next == NULL)
+ {
+ cur->next = header_new();
+ cur->next->text = new;
+ return;
+ }
+
+ /* Move to next item in array case. */
+ header_set_insert(cur->next, new);
+}
+
+struct strbuf *independent_backtrace(FILE *fp)
+{
+ 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;
+ bool in_header = false;
+ bool in_digit = false;
+ bool has_at = false;
+ bool has_filename = false;
+ bool has_bracket = false;
+ struct header *headers = NULL;
+
+ const char *bk = contents;
+ while (*bk)
+ {
+ if (bk[0] == '#'
+ && bk[1] >= '0' && bk[1] <= '7'
+ && bk[2] == ' ' /* take only #0...#7 (8 last stack frames) */
+ && !in_quote)
+ {
+ if (in_header && !has_filename)
+ strbuf_clear(header);
+ in_header = true;
+ }
+
+ if (!in_header)
+ {
+ ++bk;
+ continue;
+ }
+
+ if (isdigit(*bk) && !in_quote && !has_at)
+ in_digit = true;
+ else if (bk[0] == '\\' && bk[1] == '\"')
+ bk++;
+ else if (*bk == '\"')
+ in_quote = in_quote == true ? false : true;
+ else if (*bk == '(' && !in_quote)
+ {
+ in_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == ')' && !in_quote)
+ {
+ in_bracket = false;
+ has_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == '\n' && has_filename)
+ {
+ if (headers == NULL)
+ {
+ headers = header_new();
+ headers->text = header;
+ }
+ else
+ header_set_insert(headers, header);
+
+ header = strbuf_new();
+ in_bracket = false;
+ in_quote = false;
+ in_header = false;
+ in_digit = false;
+ has_at = false;
+ has_filename = false;
+ has_bracket = false;
+ }
+ else if (*bk == ',' && !in_quote)
+ in_digit = false;
+ else if (isspace(*bk) && !in_quote)
+ in_digit = false;
+ else if (bk[0] == 'a' && bk[1] == 't' && has_bracket && !in_quote)
+ {
+ has_at = true;
+ strbuf_append_char(header, 'a');
+ }
+ else if (bk[0] == ':' && has_at && isdigit(bk[1]) && !in_quote)
+ has_filename = true;
+ else if (in_header && !in_digit && !in_quote && !in_bracket)
+ strbuf_append_char(header, *bk);
+
+ bk++;
+ }
+
+ strbuf_free(header);
+ free(contents);
+
+ struct strbuf *result = strbuf_new();
+ struct header *loop = headers;
+ while (loop)
+ {
+ strbuf_append_str(result, loop->text->buf);
+ strbuf_append_char(result, '\n');
+ loop = loop->next;
+ }
+
+ header_free(headers); /* recursive */
+ return result;
+}
diff --git a/src/Backtrace/fallback.h b/src/Backtrace/fallback.h
new file mode 100644
index 00000000..9c31fecf
--- /dev/null
+++ b/src/Backtrace/fallback.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef FALLBACK_H
+#define FALLBACK_H
+
+#include "strbuf.h"
+#include <stdio.h>
+
+/*
+ * Reads the input file and calculates independent backtrace from it.
+ * @returns
+ * The independent backtrace. Caller is responsible for calling
+ * strbuf_free() on it.
+ */
+extern struct strbuf *independent_backtrace(FILE *fp);
+
+#endif
diff --git a/src/Backtrace/main.c b/src/Backtrace/main.c
new file mode 100644
index 00000000..d4c860f1
--- /dev/null
+++ b/src/Backtrace/main.c
@@ -0,0 +1,135 @@
+/*
+ main.cpp - parses command line arguments
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <argp.h>
+#include <stdlib.h>
+#include "config.h"
+#include "backtrace.h"
+#include "fallback.h"
+
+const char *argp_program_version = "abrt-backtrace " VERSION;
+const char *argp_program_bug_address = "<crash-catcher@lists.fedorahosted.org>";
+
+static char doc[] = "abrt-backtrace -- backtrace analyzer";
+
+/* A description of the arguments we accept. */
+static char args_doc[] = "FILE";
+
+static struct argp_option options[] = {
+ {"independent" , 'i', 0, 0, "Prints independent backtrace (fallback)" },
+ {"tree" , 't', 0, 0, "Prints backtrace analysis tree"},
+ {"verbose" , 'v', 0, 0, "Prints debug information"},
+ {"debug-parser" , 'p', 0, 0, "Prints parser debug information"},
+ {"debug-scanner", 's', 0, 0, "Prints scanner debug information"},
+ { 0 }
+};
+
+struct arguments
+{
+ bool independent;
+ bool tree;
+ bool verbose;
+ bool debug_parser;
+ bool debug_scanner;
+ char *filename;
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* Get the input argument from argp_parse, which we
+ know is a pointer to our arguments structure. */
+ struct arguments *arguments = (struct arguments*)state->input;
+
+ switch (key)
+ {
+ case 'i': arguments->independent = true; break;
+ case 't': arguments->tree = true; break;
+ case 'v': arguments->verbose = true; break;
+ case 'p': arguments->debug_parser = true; break;
+ case 's': arguments->debug_scanner = true; break;
+
+ case ARGP_KEY_ARG:
+ if (arguments->filename)
+ {
+ /* Too many arguments. */
+ argp_usage(state);
+ exit(2);
+ }
+ arguments->filename = arg;
+ break;
+
+ case ARGP_KEY_END:
+ if (!arguments->filename)
+ {
+ /* Not enough arguments. */
+ argp_usage(state);
+ exit(1);
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Our argp parser. */
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+
+int main(int argc, char **argv)
+{
+ /* Set options default values and parse program command line. */
+ struct arguments arguments;
+ arguments.independent = false;
+ arguments.verbose = false;
+ arguments.tree = false;
+ arguments.debug_parser = false;
+ arguments.debug_scanner = false;
+ arguments.filename = 0;
+ argp_parse (&argp, argc, argv, 0, 0, &arguments);
+
+ /* Open input file and parse it. */
+ FILE *fp = fopen(arguments.filename, "r");
+ if (!fp)
+ {
+ printf("Unable to open '%s'.\n", arguments.filename);
+ exit(3);
+ }
+
+ if (arguments.independent)
+ {
+ struct strbuf *ibt = independent_backtrace(fp);
+ fclose(fp);
+ puts(ibt->buf);
+ strbuf_free(ibt);
+ return;
+ }
+
+ struct backtrace *bt;
+ bt = do_parse(fp, arguments.debug_parser, arguments.debug_scanner);
+ fclose(fp);
+
+ if (arguments.tree)
+ backtrace_print_tree(bt);
+
+ backtrace_free(bt);
+ return 0;
+}
diff --git a/src/Backtrace/parser.y b/src/Backtrace/parser.y
new file mode 100644
index 00000000..9b9caba0
--- /dev/null
+++ b/src/Backtrace/parser.y
@@ -0,0 +1,272 @@
+%{ /* -*- mode: yacc -*-
+/*
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "backtrace.h"
+#include "strbuf.h"
+
+struct backtrace *g_backtrace;
+
+#define YYDEBUG 1
+#define YYMAXDEPTH 10000000
+void yyerror(char const *s)
+{
+ fprintf (stderr, "\nParser error: %s\n", s);
+}
+
+%}
+
+/* This defines the type of yylval */
+%union {
+ struct backtrace *backtrace;
+ struct thread *thread;
+ struct frame *frame;
+ char *str;
+ int num;
+ char c;
+
+ struct strbuf *strbuf;
+}
+
+/* Bison declarations. */
+%type <backtrace> backtrace ignoredpart_backtrace
+%type <thread> threads thread
+%type <frame> frames frame
+%type <strbuf> identifier hexadecimal_digit_sequence hexadecimal_number file_name function_call function_name digit_sequence
+%type <c> nondigit digit hexadecimal_digit file_name_char '(' ')' '+' '-' '/' '.' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' '_' '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' ' '\n' '\t' '\\' '!' '*' '%' '|' '^' '&' '$'
+
+%start backtrace
+%glr-parser
+%error-verbose
+
+%% /* The grammar follows. */
+
+backtrace : /* empty */ { $$ = g_backtrace = backtrace_new(); }
+ | ignoredpart_backtrace wsa { $$ = g_backtrace = $1; }
+;
+
+/**/
+ignoredpart_backtrace : threads { $$ = backtrace_new(); $$->threads = $1; }
+ | anychar ignoredpart_backtrace { $$ = $2; }
+;
+
+anychar : ws | digit | nondigit | '(' | ')' | '+' | '-' | '#' | '=' | ':' | ';'
+ | '/' | '.' | '[' | ']' | '?' | '\'' | '`' | ',' | '<' | '>' | '"'
+;
+
+threads : thread { $$ = $1; }
+ | threads wsa thread { $$ = thread_add_sibling($1, $3); }
+;
+
+thread : keyword_thread wss digit_sequence wsa '(' keyword_thread wss digit_sequence wsa ')' ':' wsa frames { $$ = thread_new(); $$->frames = $13; }
+;
+
+frames : frame { $$ = $1; }
+ | frames wsa frame { $$ = frame_add_sibling($1, $3); }
+;
+
+frame : frame_head wss function_call wsa keyword_at wss file_location wss variables %dprec 2 { $$ = frame_new(); }
+ | frame_head wss frame_address_in_function wss keyword_at wss file_location wss variables %dprec 3 { $$ = frame_new(); }
+ | frame_head wss frame_address_in_function wss keyword_from wss file_location wss variables %dprec 3 { $$ = frame_new(); }
+ | frame_head wss frame_address_in_function wss variables %dprec 1 { $$ = frame_new(); }
+ | frame_head wss keyword_sighandler wss variables { $$ = frame_new(); }
+;
+
+
+frame_head : '#' digit_sequence
+;
+
+frame_address_in_function : hexadecimal_number wss keyword_in wss function_call
+;
+
+file_location : file_name ':' digit_sequence
+ | file_name
+;
+
+variables : variables_char
+ | variables variables_char
+ | variables variables_wss variables_char
+ | variables '\n' variables_char_no_framestart
+ | variables variables_wss '\n' variables_wss variables_char_no_framestart
+ | variables variables_wss '\n' variables_char_no_framestart
+ | variables '\n' variables_wss variables_char_no_framestart
+;
+
+variables_ws : '\t' | ' '
+;
+
+variables_wss : variables_ws
+ | variables_wss variables_ws
+;
+
+ /* We hope variables in frame never contain # character. */
+variables_char : '#' | variables_char_no_framestart
+;
+
+variables_char_no_framestart : digit | nondigit | '(' | ')' | '+' | '-' | '<'
+ | '>' | '"' | '/' | '.' | '[' | ']' | '?' | '\''
+ | '`' | ',' | '=' | '{' | '}' | '^' | '&' | '$'
+ | ':' | ';' | '\\' | '!' | '@' | '*' | '%' | '|'
+
+function_call : function_name wsa function_args
+;
+
+function_name : identifier
+ | '?' '?' { $$ = strbuf_new(); strbuf_append_str($$, "??"); }
+;
+
+function_args : '(' wsa ')'
+ | '(' wsa function_args_sequence wsa ')'
+;
+
+function_args_sequence : function_args_char
+ | function_args_sequence wsa function_args_char
+;
+
+function_args_char : digit | nondigit | '{' | '}' | '<' | '>'
+ | '=' | '-' | '+' | '@' | ',' | '.'
+;
+
+file_name : file_name_char { $$ = strbuf_new(); strbuf_append_char($$, $1); }
+ | file_name file_name_char { $$ = strbuf_append_char($1, $2); }
+;
+
+file_name_char : digit | nondigit | '-' | '+' | '/' | '.'
+;
+
+ /* Mangled function name. */
+identifier : nondigit { $$ = strbuf_new(); strbuf_append_char($$, $1); }
+ | identifier nondigit { $$ = strbuf_append_char($1, $2); }
+ | identifier digit { $$ = strbuf_append_char($1, $2); }
+ | identifier '@' { $$ = strbuf_append_char($1, $2); }
+ | identifier '.' { $$ = strbuf_append_char($1, $2); }
+ | identifier ':' { $$ = strbuf_append_char($1, $2); }
+;
+
+digit_sequence : digit { $$ = strbuf_new(); strbuf_append_char($$, $1); }
+ | digit_sequence digit { $$ = strbuf_append_char($1, $2); }
+;
+
+hexadecimal_number : '0' 'x' hexadecimal_digit_sequence { $$ = strbuf_new(); strbuf_append_str($$, "0x"); strbuf_append_str($$, $3->buf); }
+ | '0' 'X' hexadecimal_digit_sequence { $$ = strbuf_new(); strbuf_append_str($$, "0X"); strbuf_append_str($$, $3->buf); }
+;
+
+hexadecimal_digit_sequence : hexadecimal_digit { $$ = strbuf_new(); strbuf_append_char($$, $1); }
+ | hexadecimal_digit_sequence hexadecimal_digit { $$ = strbuf_append_char($1, $2); }
+;
+
+hexadecimal_digit : digit
+ | 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
+ | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
+;
+
+digit : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
+;
+
+nondigit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'
+ | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w'
+ | 'x' | 'y' | 'z'
+ | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K'
+ | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W'
+ | 'X' | 'Y' | 'Z'
+ | '_'
+;
+
+ /* whitespace */
+ws : '\t' | ' ' | '\n' | '\r'
+;
+
+ /* whitespace sequence */
+wss : ws
+ | wss ws
+;
+
+/* whitespace sequence allowed */
+wsa :
+ | wss
+;
+
+keyword_in : 'i' 'n'
+;
+
+keyword_at : 'a' 't'
+;
+
+keyword_from : 'f' 'r' 'o' 'm'
+;
+
+keyword_thread: 'T' 'h' 'r' 'e' 'a' 'd'
+;
+
+keyword_sighandler: '<' 's' 'i' 'g' 'n' 'a' 'l' ' ' 'h' 'a' 'n' 'd' 'l' 'e' 'r' ' ' 'c' 'a' 'l' 'l' 'e' 'd' '>'
+;
+
+%%
+
+static bool scanner_echo = false;
+static FILE *yyin;
+
+int yylex()
+{
+ int c = fgetc(yyin);
+ if (c == EOF)
+ return 0;
+
+ /* Debug output. */
+ if (scanner_echo)
+ putchar(c);
+
+ /* Return a single char. */
+ return c;
+}
+
+/* This is the function that is actually called from outside.
+ * @returns
+ * Backtrace structure. Caller is responsible for calling
+ * backtrace_free() on this.
+ */
+struct backtrace *do_parse(FILE *input, bool debug_parser, bool debug_scanner)
+{
+ /* Prepare for running parser. */
+ g_backtrace = 0;
+ yyin = input;
+#if YYDEBUG == 1
+ if (debug_parser)
+ yydebug = 1;
+#endif
+ scanner_echo = debug_scanner;
+
+ /* Parse. */
+ int failure = yyparse();
+
+ /* Separate debugging output. */
+ if (scanner_echo)
+ putchar('\n');
+
+ if (failure)
+ {
+ if (g_backtrace)
+ backtrace_free(g_backtrace);
+ puts("Error while parsing backtrace.");
+ exit(6);
+ }
+
+ return g_backtrace;
+}
diff --git a/src/Backtrace/strbuf.c b/src/Backtrace/strbuf.c
new file mode 100644
index 00000000..8cdb482e
--- /dev/null
+++ b/src/Backtrace/strbuf.c
@@ -0,0 +1,95 @@
+/*
+ strbuf.c - string buffer implementation
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "strbuf.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+struct strbuf *strbuf_new()
+{
+ struct strbuf *buf = malloc(sizeof(struct strbuf));
+ if (!buf)
+ {
+ puts("Error while allocating memory for string buffer.");
+ exit(5);
+ }
+
+ buf->alloc = 8;
+ buf->len = 0;
+ buf->buf = malloc(buf->alloc);
+ if (!buf->buf)
+ {
+ puts("Error while allocating memory for string buffer.");
+ exit(5);
+ }
+
+ buf->buf[buf->len] = '\0';
+ return buf;
+}
+
+void strbuf_free(struct strbuf *buf)
+{
+ free(buf->buf);
+ free(buf);
+}
+
+void strbuf_clear(struct strbuf *buf)
+{
+ assert(buf->alloc > 0);
+ buf->len = 0;
+ buf->buf[0] = '\0';
+}
+
+/* Ensures that the buffer can be extended by num characters
+ * without touching malloc/realloc.
+ */
+static void strbuf_grow(struct strbuf *buf, int num)
+{
+ if (buf->len + num + 1 > buf->alloc)
+ {
+ while (buf->len + num + 1 > buf->alloc)
+ buf->alloc *= 2; /* huge grow = infinite loop */
+
+ buf->buf = realloc(buf->buf, buf->alloc);
+ if (!buf->buf)
+ {
+ puts("Error while allocating memory for string buffer.");
+ exit(5);
+ }
+ }
+}
+
+struct strbuf *strbuf_append_char(struct strbuf *buf, char c)
+{
+ strbuf_grow(buf, 1);
+ buf->buf[buf->len++] = c;
+ buf->buf[buf->len] = '\0';
+ return buf;
+}
+
+struct strbuf *strbuf_append_str(struct strbuf *buf, char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(buf, len);
+ assert(buf->len + len < buf->alloc);
+ strcpy(buf->buf + buf->len, str);
+ buf->len += len;
+ return buf;
+}
diff --git a/src/Backtrace/strbuf.h b/src/Backtrace/strbuf.h
new file mode 100644
index 00000000..4375dd90
--- /dev/null
+++ b/src/Backtrace/strbuf.h
@@ -0,0 +1,35 @@
+/*
+ strbuf.h - string buffer
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef STRBUF_H
+#define STRBUF_H
+
+struct strbuf {
+ int alloc;
+ int len;
+ char *buf;
+};
+
+extern struct strbuf *strbuf_new();
+extern void strbuf_free(struct strbuf *buf);
+extern void strbuf_clear(struct strbuf *buf);
+extern struct strbuf *strbuf_append_char(struct strbuf *buf, char c);
+extern struct strbuf *strbuf_append_str(struct strbuf *buf, char *str);
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 2eb6c79d..dbd7ebbc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = Hooks Daemon Applet Gui CLI
+SUBDIRS = Hooks Daemon Applet Gui CLI Backtrace