summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichal Toman <mtoman@redhat.com>2010-10-19 10:57:16 +0200
committerMichal Toman <mtoman@redhat.com>2010-10-19 10:57:16 +0200
commit8ccb8e59a6f295481b1d0b664701bb421b041e28 (patch)
treeb05ba340714831c049dc830c5d6db0b9f313d46f /src
parent2ba325a217d4d08fb867d1197a89c53f49ab85d5 (diff)
parent41473dbc4a685a4e33debaf2a9103da8bcd6429f (diff)
Merge branch 'master' of ssh://git.fedorahosted.org/git/abrt
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/btparser/Makefile.am44
-rw-r--r--src/btparser/backtrace.c445
-rw-r--r--src/btparser/backtrace.h269
-rw-r--r--src/btparser/frame.c1027
-rw-r--r--src/btparser/frame.h470
-rw-r--r--src/btparser/location.c78
-rw-r--r--src/btparser/location.h118
-rw-r--r--src/btparser/normalize.c68
-rw-r--r--src/btparser/normalize.h74
-rw-r--r--src/btparser/normalize_dbus.c44
-rw-r--r--src/btparser/normalize_gdk.c44
-rw-r--r--src/btparser/normalize_glib.c60
-rw-r--r--src/btparser/normalize_glibc.c120
-rw-r--r--src/btparser/normalize_libstdcpp.c46
-rw-r--r--src/btparser/normalize_linux.c41
-rw-r--r--src/btparser/normalize_xorg.c46
-rw-r--r--src/btparser/thread.c364
-rw-r--r--src/btparser/thread.h204
-rw-r--r--src/btparser/utils.c423
-rw-r--r--src/btparser/utils.h284
-rw-r--r--src/daemon/Daemon.cpp7
-rw-r--r--src/daemon/Makefile.am22
-rw-r--r--src/daemon/MiddleWare.cpp22
-rw-r--r--src/daemon/abrt-action-analyze-c.c238
-rw-r--r--src/daemon/abrt-action-bugzilla.cpp4
-rw-r--r--src/daemon/abrt-action-generate-backtrace.c162
-rw-r--r--src/daemon/abrt-action-save-package-data.cpp25
-rw-r--r--src/daemon/abrt-server.c6
-rw-r--r--src/gui/CCDump.py4
-rw-r--r--src/hooks/abrt-hook-ccpp.cpp11
-rw-r--r--src/utils/Makefile.am11
-rw-r--r--src/utils/abrt-backtrace.159
-rw-r--r--src/utils/abrt-backtrace.c339
34 files changed, 4646 insertions, 535 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index c2576c4e..00227a47 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = hooks daemon applet gui cli utils
+SUBDIRS = hooks btparser daemon applet gui cli
diff --git a/src/btparser/Makefile.am b/src/btparser/Makefile.am
new file mode 100644
index 00000000..b610f30c
--- /dev/null
+++ b/src/btparser/Makefile.am
@@ -0,0 +1,44 @@
+lib_LTLIBRARIES = libbtparser.la
+libbtparser_la_SOURCES = \
+ backtrace.h backtrace.c \
+ frame.h frame.c \
+ location.h location.c \
+ normalize.h normalize.c \
+ normalize_dbus.c \
+ normalize_gdk.c \
+ normalize_glib.c \
+ normalize_glibc.c \
+ normalize_libstdcpp.c \
+ normalize_linux.c \
+ normalize_xorg.c \
+ thread.h thread.c \
+ utils.h utils.c
+libbtparser_la_CFLAGS = -Wall -Werror -D_GNU_SOURCE -I../../lib/utils
+libbtparser_la_LDFLAGS = -version-info 1:1:0
+libbtparser_la_LIBADD = ../../lib/utils/libABRTUtils.la
+
+# From http://www.seul.org/docs/autotut/
+# Version consists 3 numbers: CURRENT, REVISION, AGE.
+# CURRENT is the version of the interface the library implements.
+# Whenever a new function is added, or its name changed, or
+# the number or type of its parameters (the prototype -- in
+# libraries we call this the function signature), this number
+# goes up. And it goes up exactly by one.
+#
+# REVISION is the revision of the implementation of this
+# interface, i.e., when you change the library by only modifying
+# code inside the functions (fixing bugs, optimizing internal
+# behavior, or adding/removing/changing signatures of functions
+# that are private to the library -- used only internally) you
+# raise the revision number only.
+#
+# Age is the difference between the newest and oldest interface
+# the library currently implements. Let's say you had 8 versions
+# of your library's interface, 0 through 7. You are now on
+# the 4th revision of the 8th interface, that is, 7:3:X (remember
+# we start counting on zero). And when you had to make choices
+# for what old interfaces you would keep support -- for backward
+# compatibility purposes, you chose to keep support for
+# interfaces 5, 6 and (obviously) the current, 7. The libtool
+# version of your library would be 7:3:2 , because the Age
+# is 7-5 = 2.
diff --git a/src/btparser/backtrace.c b/src/btparser/backtrace.c
new file mode 100644
index 00000000..139b315d
--- /dev/null
+++ b/src/btparser/backtrace.c
@@ -0,0 +1,445 @@
+/*
+ backtrace.c
+
+ Copyright (C) 2010 Red Hat, 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 "thread.h"
+#include "frame.h"
+#include "utils.h"
+#include "strbuf.h"
+#include "location.h"
+#include "normalize.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+struct btp_backtrace *
+btp_backtrace_new()
+{
+ struct btp_backtrace *backtrace = btp_malloc(sizeof(struct btp_backtrace));
+ btp_backtrace_init(backtrace);
+ return backtrace;
+}
+
+void
+btp_backtrace_init(struct btp_backtrace *backtrace)
+{
+ backtrace->threads = NULL;
+ backtrace->crash = NULL;
+}
+
+void
+btp_backtrace_free(struct btp_backtrace *backtrace)
+{
+ if (!backtrace)
+ return;
+
+ while (backtrace->threads)
+ {
+ struct btp_thread *rm = backtrace->threads;
+ backtrace->threads = rm->next;
+ btp_thread_free(rm);
+ }
+
+ if (backtrace->crash)
+ btp_frame_free(backtrace->crash);
+
+ free(backtrace);
+}
+
+struct btp_backtrace *
+btp_backtrace_dup(struct btp_backtrace *backtrace)
+{
+ struct btp_backtrace *result = btp_backtrace_new();
+ memcpy(result, backtrace, sizeof(struct btp_backtrace));
+
+ if (backtrace->crash)
+ backtrace->crash = btp_frame_dup(backtrace->crash, false);
+ if (backtrace->threads)
+ backtrace->threads = btp_thread_dup(backtrace->threads, true);
+
+ return result;
+}
+
+int
+btp_backtrace_get_thread_count(struct btp_backtrace *backtrace)
+{
+ struct btp_thread *thread = backtrace->threads;
+ int count = 0;
+ while (thread)
+ {
+ thread = thread->next;
+ ++count;
+ }
+ return count;
+}
+
+void
+btp_backtrace_remove_threads_except_one(struct btp_backtrace *backtrace,
+ struct btp_thread *thread)
+{
+ while (backtrace->threads)
+ {
+ struct btp_thread *delete_thread = backtrace->threads;
+ backtrace->threads = delete_thread->next;
+ if (delete_thread != thread)
+ btp_thread_free(delete_thread);
+ }
+
+ thread->next = NULL;
+ backtrace->threads = thread;
+}
+
+/**
+ * Loop through all threads and if a single one contains the crash
+ * frame on the top, return it. Otherwise, return NULL.
+ *
+ * If require_abort is true, it is also required that the thread
+ * containing the crash frame contains some known "abort" function. In
+ * this case there can be multiple threads with the crash frame on the
+ * top, but only one of them might contain the abort function to
+ * succeed.
+ */
+static struct btp_thread *
+btp_backtrace_find_crash_thread_from_crash_frame(struct btp_backtrace *backtrace,
+ bool require_abort)
+{
+ if (btp_debug_parser)
+ printf("%s(backtrace, %s)\n", __FUNCTION__, require_abort ? "true" : "false");
+
+ assert(backtrace->threads); /* checked by the caller */
+ if (!backtrace->crash || !backtrace->crash->function_name)
+ return NULL;
+
+ struct btp_thread *result = NULL;
+ struct btp_thread *thread = backtrace->threads;
+ while (thread)
+ {
+ struct btp_frame *top_frame = thread->frames;
+ bool same_name = top_frame &&
+ top_frame->function_name &&
+ 0 == strcmp(top_frame->function_name, backtrace->crash->function_name);
+ bool abort_requirement_satisfied = !require_abort ||
+ btp_glibc_thread_find_exit_frame(thread);
+ if (btp_debug_parser)
+ {
+ printf(" - thread #%d: same_name %s, abort_satisfied %s\n",
+ thread->number,
+ same_name ? "true" : "false",
+ abort_requirement_satisfied ? "true" : "false");
+ }
+
+ if (same_name && abort_requirement_satisfied)
+ {
+ if (NULL == result)
+ result = thread;
+ else
+ {
+ /* Second frame with the same function. Failure. */
+ return NULL;
+ }
+ }
+
+ thread = thread->next;
+ }
+
+ return result;
+}
+
+struct btp_thread *
+btp_backtrace_find_crash_thread(struct btp_backtrace *backtrace)
+{
+ /* If there is no thread, be silent and report NULL. */
+ if (!backtrace->threads)
+ return NULL;
+
+ /* If there is just one thread, it is simple. */
+ if (!backtrace->threads->next)
+ return backtrace->threads;
+
+ /* If we have a crash frame *and* there is just one thread which has
+ * this frame on the top, it is also simple.
+ */
+ struct btp_thread *thread;
+ thread = btp_backtrace_find_crash_thread_from_crash_frame(backtrace, false);
+ if (thread)
+ return thread;
+
+ /* There are multiple threads with a frame indistinguishable from
+ * the crash frame on the top of stack.
+ * Try to search for known abort functions.
+ */
+ thread = btp_backtrace_find_crash_thread_from_crash_frame(backtrace, true);
+
+ /* We might want to search a thread with known abort function, and
+ * without the crash frame here. However, it hasn't been needed so
+ * far.
+ */
+ return thread; /* result or null */
+}
+
+
+void
+btp_backtrace_limit_frame_depth(struct btp_backtrace *backtrace,
+ int depth)
+{
+ assert(depth > 0);
+ struct btp_thread *thread = backtrace->threads;
+ while (thread)
+ {
+ btp_thread_remove_frames_below_n(thread, depth);
+ thread = thread->next;
+ }
+}
+
+float
+btp_backtrace_quality_simple(struct btp_backtrace *backtrace)
+{
+ int ok_count = 0, all_count = 0;
+ struct btp_thread *thread = backtrace->threads;
+ while (thread)
+ {
+ btp_thread_quality_counts(thread, &ok_count, &all_count);
+ thread = thread->next;
+ }
+
+ if (all_count == 0)
+ return 0;
+
+ return ok_count / (float)all_count;
+}
+
+float
+btp_backtrace_quality_complex(struct btp_backtrace *backtrace)
+{
+ backtrace = btp_backtrace_dup(backtrace);
+
+ /* Find the crash thread, and then normalize the backtrace. It is
+ * not possible to find the crash thread after the backtrace has
+ * been normalized.
+ */
+ struct btp_thread *crash_thread = btp_backtrace_find_crash_thread(backtrace);
+ btp_normalize_backtrace(backtrace);
+
+ /* Get the quality q1 of the full backtrace. */
+ float q1 = btp_backtrace_quality_simple(backtrace);
+
+ if (!crash_thread)
+ {
+ btp_backtrace_free(backtrace);
+ return q1;
+ }
+
+ /* Get the quality q2 of the crash thread. */
+ float q2 = btp_thread_quality(crash_thread);
+
+ /* Get the quality q3 of the frames around the crash. First,
+ * duplicate the crash thread so we can cut it. Then find an exit
+ * frame, and remove it and everything above it
+ * (__run_exit_handlers and such). Then remove all the redundant
+ * frames (assert calls etc.) Then limit the frame count to 5.
+ */
+ btp_thread_remove_frames_below_n(crash_thread, 5);
+ float q3 = btp_thread_quality(crash_thread);
+
+ btp_backtrace_free(backtrace);
+
+ /* Compute and return the final backtrace quality q. */
+ return 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
+}
+
+char *
+btp_backtrace_to_text(struct btp_backtrace *backtrace, bool verbose)
+{
+ struct strbuf *str = strbuf_new();
+ if (verbose)
+ {
+ strbuf_append_strf(str, "Thread count: %d\n",
+ btp_backtrace_get_thread_count(backtrace));
+ }
+
+ if (backtrace->crash && verbose)
+ {
+ strbuf_append_str(str, "Crash frame: ");
+ btp_frame_append_to_str(backtrace->crash, str, verbose);
+ }
+
+ struct btp_thread *thread = backtrace->threads;
+ while (thread)
+ {
+ btp_thread_append_to_str(thread, str, verbose);
+ thread = thread->next;
+ }
+
+ return strbuf_free_nobuf(str);
+}
+
+struct btp_frame *
+btp_backtrace_get_crash_frame(struct btp_backtrace *backtrace)
+{
+ backtrace = btp_backtrace_dup(backtrace);
+
+ struct btp_thread *crash_thread = btp_backtrace_find_crash_thread(backtrace);
+ if (!crash_thread)
+ {
+ btp_backtrace_free(backtrace);
+ return NULL;
+ }
+
+ btp_normalize_backtrace(backtrace);
+ struct btp_frame *crash_frame = crash_thread->frames;
+ crash_frame = btp_frame_dup(crash_frame, false);
+ btp_backtrace_free(backtrace);
+ return crash_frame;
+}
+
+char *
+btp_backtrace_get_duplication_hash(struct btp_backtrace *backtrace)
+{
+ backtrace = btp_backtrace_dup(backtrace);
+ struct btp_thread *crash_thread = btp_backtrace_find_crash_thread(backtrace);
+ if (crash_thread)
+ btp_backtrace_remove_threads_except_one(backtrace, crash_thread);
+
+ btp_normalize_backtrace(backtrace);
+ btp_backtrace_limit_frame_depth(backtrace, 3);
+ char *hash = btp_backtrace_to_text(backtrace, false);
+ btp_backtrace_free(backtrace);
+ return hash;
+}
+
+struct btp_backtrace *
+btp_backtrace_parse(char **input,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ struct btp_backtrace *imbacktrace = btp_backtrace_new(); /* im - intermediate */
+
+ /* The header is mandatory, but it might contain no frame header,
+ * in some broken backtraces. In that case, backtrace.crash value
+ * is kept as NULL.
+ */
+ if (!btp_backtrace_parse_header(&local_input,
+ &imbacktrace->crash,
+ location))
+ {
+ btp_backtrace_free(imbacktrace);
+ return NULL;
+ }
+
+ struct btp_thread *thread, *prevthread = NULL;
+ while ((thread = btp_thread_parse(&local_input, location)))
+ {
+ if (prevthread)
+ {
+ btp_thread_add_sibling(prevthread, thread);
+ prevthread = thread;
+ }
+ else
+ imbacktrace->threads = prevthread = thread;
+ }
+ if (!imbacktrace->threads)
+ {
+ btp_backtrace_free(imbacktrace);
+ return NULL;
+ }
+
+ *input = local_input;
+ return imbacktrace;
+}
+
+bool
+btp_backtrace_parse_header(char **input,
+ struct btp_frame **frame,
+ struct btp_location *location)
+{
+ int first_thread_line, first_thread_column;
+ char *first_thread = btp_strstr_location(*input,
+ "\nThread ",
+ &first_thread_line,
+ &first_thread_column);
+
+ /* Skip the newline. */
+ if (first_thread)
+ {
+ ++first_thread;
+ first_thread_line += 1;
+ first_thread_column = 0;
+ }
+
+ int first_frame_line, first_frame_column;
+ char *first_frame = btp_strstr_location(*input,
+ "\n#",
+ &first_frame_line,
+ &first_frame_column);
+
+ /* Skip the newline. */
+ if (first_frame)
+ {
+ ++first_frame;
+ first_frame_line += 1;
+ first_frame_column = 0;
+ }
+
+ if (first_thread)
+ {
+ if (first_frame && first_frame < first_thread)
+ {
+ /* Common case. The crash frame is present in the input
+ * before the list of threads begins.
+ */
+ *input = first_frame;
+ btp_location_add(location, first_frame_line, first_frame_column);
+ }
+ else
+ {
+ /* Uncommon case (caused by some kernel bug) where the
+ * frame is missing from the header. The backtrace
+ * contains just threads. We silently skip the header and
+ * return true.
+ */
+ *input = first_thread;
+ btp_location_add(location,
+ first_thread_line,
+ first_thread_column);
+ *frame = NULL;
+ return true;
+ }
+ }
+ else if (first_frame)
+ {
+ /* Degenerate case when the backtrace contains no thread, but
+ * the frame is there.
+ */
+ *input = first_frame;
+ btp_location_add(location, first_frame_line, first_frame_column);
+ }
+ else
+ {
+ /* Degenerate case where the input is empty or completely
+ * meaningless. Report a failure.
+ */
+ location->message = "No frame and no thread found.";
+ return false;
+ }
+
+ /* Parse the frame header. */
+ *frame = btp_frame_parse(input, location);
+ return *frame;
+}
diff --git a/src/btparser/backtrace.h b/src/btparser/backtrace.h
new file mode 100644
index 00000000..d5de3ff3
--- /dev/null
+++ b/src/btparser/backtrace.h
@@ -0,0 +1,269 @@
+/*
+ backtrace.h
+
+ Copyright (C) 2010 Red Hat, 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 BTPARSER_BACKTRACE_H
+#define BTPARSER_BACKTRACE_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct btp_thread;
+struct btp_frame;
+struct btp_location;
+
+/**
+ * A backtrace obtained at the time of a program crash, consisting of
+ * several threads which contains frames.
+ */
+struct btp_backtrace
+{
+ struct btp_thread *threads;
+ /**
+ * The frame where the crash happened according to debugger. It
+ * might be that we can not tell to which thread this frame
+ * belongs, because some threads end with mutually
+ * indistinguishable frames.
+ */
+ struct btp_frame *crash;
+};
+
+/**
+ * Creates and initializes a new backtrace structure.
+ * @returns
+ * It never returns NULL. The returned pointer must be released by
+ * calling the function btp_backtrace_free().
+ */
+struct btp_backtrace *
+btp_backtrace_new();
+
+/**
+ * Initializes all members of the backtrace structure to their default
+ * values. No memory is released, members are simply overwritten.
+ * This is useful for initializing a backtrace structure placed on the
+ * stack.
+ */
+void
+btp_backtrace_init(struct btp_backtrace *backtrace);
+
+/**
+ * Releases the memory held by the backtrace, its threads and frames.
+ * @param backtrace
+ * If the backtrace is NULL, no operation is performed.
+ */
+void
+btp_backtrace_free(struct btp_backtrace *backtrace);
+
+/**
+ * Creates a duplicate of the backtrace.
+ * @param backtrace
+ * The backtrace to be copied. It's not modified by this function.
+ * @returns
+ * This function never returns NULL. If the returned duplicate is not
+ * shallow, it must be released by calling the function
+ * btp_backtrace_free().
+ */
+struct btp_backtrace *
+btp_backtrace_dup(struct btp_backtrace *backtrace);
+
+/**
+ * Returns a number of threads in the backtrace.
+ * @param backtrace
+ * It's not modified by calling this function.
+ */
+int
+btp_backtrace_get_thread_count(struct btp_backtrace *backtrace);
+
+/**
+ * Removes all threads from the backtrace and deletes them, except the
+ * one provided as a parameter.
+ * @param thread
+ * This function does not check whether the thread is a member of the backtrace.
+ * If it's not, all threads are removed from the backtrace and then deleted.
+ */
+void
+btp_backtrace_remove_threads_except_one(struct btp_backtrace *backtrace,
+ struct btp_thread *thread);
+
+/**
+ * Search all threads and tries to find the one that caused the crash.
+ * It might return NULL if the thread cannot be determined.
+ * @param backtrace
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ */
+struct btp_thread *
+btp_backtrace_find_crash_thread(struct btp_backtrace *backtrace);
+
+/**
+ * Remove frames from the bottom of threads in the backtrace, until
+ * all threads have at most 'depth' frames.
+ * @param backtrace
+ * Must be non-NULL pointer.
+ */
+void
+btp_backtrace_limit_frame_depth(struct btp_backtrace *backtrace,
+ int depth);
+
+/**
+ * Evaluates the quality of the backtrace. The quality is the ratio of
+ * the number of frames with function name fully known to the number
+ * of all frames. This function does not take into account that some
+ * frames are more important than others.
+ * @param backtrace
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @returns
+ * A number between 0 and 1. 0 means the lowest quality, 1 means full
+ * backtrace is known.
+ */
+float
+btp_backtrace_quality_simple(struct btp_backtrace *backtrace);
+
+/**
+ * Evaluates the quality of the backtrace. The quality is determined
+ * depending on the ratio of frames with function name fully known to
+ * all frames.
+ * @param backtrace
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @returns
+ * A number between 0 and 1. 0 means the lowest quality, 1 means full
+ * backtrace is known. The returned value takes into account that the
+ * thread which caused the crash is more important than the other
+ * threads, and the frames around the crash frame are more important
+ * than distant frames.
+ */
+float
+btp_backtrace_quality_complex(struct btp_backtrace *backtrace);
+
+/**
+ * Returns textual representation of the backtrace.
+ * @param backtrace
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @returns
+ * This function never returns NULL. The caller is responsible for
+ * releasing the returned memory using function free().
+ */
+char *
+btp_backtrace_to_text(struct btp_backtrace *backtrace,
+ bool verbose);
+
+/**
+ * Analyzes the backtrace to get the frame where a crash occurred.
+ * @param backtrace
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @returns
+ * The returned value must be released by calling btp_frame_free(),
+ * when it's no longer needed. NULL is returned if the crash frame is
+ * not found.
+ */
+struct btp_frame *
+btp_backtrace_get_crash_frame(struct btp_backtrace *backtrace);
+
+/**
+ * Calculates the duplication hash string of the backtrace.
+ * @param backtrace
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @returns
+ * This function never returns NULL. The caller is responsible for
+ * releasing the returned memory using function free().
+ */
+char *
+btp_backtrace_get_duplication_hash(struct btp_backtrace *backtrace);
+
+/**
+ * Parses a textual backtrace and puts it into a structure. If
+ * parsing fails, the input parameter is not changed and NULL is
+ * returned.
+ * @code
+ * struct btp_location location;
+ * btp_location_init(&location);
+ * char *input = "...";
+ * struct btp_backtrace *backtrace = btp_backtrace_parse(input, location;
+ * if (!backtrace)
+ * {
+ * fprintf(stderr,
+ * "Failed to parse the backtrace.\n"
+ * "Line %d, column %d: %s\n",
+ * location.line,
+ * location.column,
+ * location.message);
+ * exit(-1);
+ * }
+ * btp_backtrace_free(backtrace);
+ * @endcode
+ * @param input
+ * Pointer to the string with the backtrace. If this function returns
+ * true, this pointer is modified to point after the backtrace that
+ * was just parsed.
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ * @returns
+ * A newly allocated backtrace structure or NULL. A backtrace struct
+ * is returned when at least one thread was parsed from the input and
+ * no error occurred. The returned structure should be released by
+ * btp_backtrace_free().
+ */
+struct btp_backtrace *
+btp_backtrace_parse(char **input,
+ struct btp_location *location);
+
+/**
+ * Parse backtrace header if it is available in the backtrace. The
+ * header usually contains frame where the program crashed.
+ * @param input
+ * Pointer moved to point behind the header if the header is
+ * successfully detected and parsed.
+ * @param frame
+ * If this function succeeds and returns true, *frame contains the
+ * crash frame that is usually a part of the header. If no frame is
+ * detected in the header, *frame is set to NULL.
+ * @code
+ * [New Thread 11919]
+ * [New Thread 11917]
+ * Core was generated by `evince file:///tmp/Factura04-05-2010.pdf'.
+ * Program terminated with signal 8, Arithmetic exception.
+ * #0 0x000000322a2362b9 in repeat (image=<value optimized out>,
+ * mask=<value optimized out>, mask_bits=<value optimized out>)
+ * at pixman-bits-image.c:145
+ * 145 pixman-bits-image.c: No such file or directory.
+ * in pixman-bits-image.c
+ * @endcode
+ */
+bool
+btp_backtrace_parse_header(char **input,
+ struct btp_frame **frame,
+ struct btp_location *location);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/btparser/frame.c b/src/btparser/frame.c
new file mode 100644
index 00000000..2bfae070
--- /dev/null
+++ b/src/btparser/frame.c
@@ -0,0 +1,1027 @@
+/*
+ frame.c
+
+ Copyright (C) 2010 Red Hat, 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 "frame.h"
+#include "strbuf.h"
+#include "utils.h"
+#include "location.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+struct btp_frame *
+btp_frame_new()
+{
+ struct btp_frame *frame = btp_malloc(sizeof(struct btp_frame));
+ btp_frame_init(frame);
+ return frame;
+}
+
+void
+btp_frame_init(struct btp_frame *frame)
+{
+ frame->function_name = NULL;
+ frame->function_type = NULL;
+ frame->number = 0;
+ frame->source_file = NULL;
+ frame->source_line = -1;
+ frame->signal_handler_called = false;
+ frame->address = -1;
+ frame->next = NULL;
+}
+
+void
+btp_frame_free(struct btp_frame *frame)
+{
+ if (!frame)
+ return;
+ free(frame->function_name);
+ free(frame->function_type);
+ free(frame->source_file);
+ free(frame);
+}
+
+struct btp_frame *
+btp_frame_dup(struct btp_frame *frame, bool siblings)
+{
+ struct btp_frame *result = btp_frame_new();
+ memcpy(result, frame, sizeof(struct btp_frame));
+
+ /* Handle siblings. */
+ if (siblings)
+ {
+ if (result->next)
+ result->next = btp_frame_dup(result->next, true);
+ }
+ else
+ result->next = NULL; /* Do not copy that. */
+
+ /* Duplicate all strings if the copy is not shallow. */
+ if (result->function_name)
+ result->function_name = btp_strdup(result->function_name);
+ if (result->function_type)
+ result->function_type = btp_strdup(result->function_type);
+ if (result->source_file)
+ result->source_file = btp_strdup(result->source_file);
+
+ return result;
+}
+
+bool
+btp_frame_calls_func(struct btp_frame *frame,
+ const char *function_name)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name);
+}
+
+bool
+btp_frame_calls_func_in_file(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ NULL != strstr(frame->source_file, source_file);
+}
+
+bool
+btp_frame_calls_func_in_file2(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ (NULL != strstr(frame->source_file, source_file0) ||
+ NULL != strstr(frame->source_file, source_file1));
+}
+
+bool
+btp_frame_calls_func_in_file3(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1,
+ const char *source_file2)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ (NULL != strstr(frame->source_file, source_file0) ||
+ NULL != strstr(frame->source_file, source_file1) ||
+ NULL != strstr(frame->source_file, source_file2));
+}
+
+bool
+btp_frame_calls_func_in_file4(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1,
+ const char *source_file2,
+ const char *source_file3)
+{
+ return frame->function_name &&
+ 0 == strcmp(frame->function_name, function_name) &&
+ frame->source_file &&
+ (NULL != strstr(frame->source_file, source_file0) ||
+ NULL != strstr(frame->source_file, source_file1) ||
+ NULL != strstr(frame->source_file, source_file2) ||
+ NULL != strstr(frame->source_file, source_file3));
+}
+
+int
+btp_frame_cmp(struct btp_frame *f1,
+ struct btp_frame *f2,
+ bool compare_number)
+{
+ /* Singnal handler. */
+ if (f1->signal_handler_called)
+ {
+ if (!f2->signal_handler_called)
+ return 1;
+
+ /* Both contain signal handler called. */
+ return 0;
+ }
+ else
+ {
+ if (f2->signal_handler_called)
+ return -1;
+ /* No signal handler called, continue. */
+ }
+
+ /* Function. */
+ int function_name = btp_strcmp0(f1->function_name, f2->function_name);
+ if (function_name != 0)
+ return function_name;
+ int function_type = btp_strcmp0(f1->function_type, f2->function_type);
+ if (function_type != 0)
+ return function_type;
+
+ /* Sourcefile. */
+ int source_file = btp_strcmp0(f1->source_file, f2->source_file);
+ if (source_file != 0)
+ return source_file;
+
+ /* Sourceline. */
+ int source_line = f1->source_line - f2->source_line;
+ if (source_line != 0)
+ return source_line;
+
+ /* Frame number. */
+ if (compare_number)
+ {
+ int number = f1->number - f2->number;
+ if (number != 0)
+ return number;
+ }
+
+ return 0;
+}
+
+void
+btp_frame_add_sibling(struct btp_frame *a, struct btp_frame *b)
+{
+ struct btp_frame *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+}
+
+void
+btp_frame_append_to_str(struct btp_frame *frame,
+ struct strbuf *str,
+ bool verbose)
+{
+ if (verbose)
+ strbuf_append_strf(str, " #%d", frame->number);
+ else
+ strbuf_append_str(str, " ");
+
+ if (frame->function_type)
+ strbuf_append_strf(str, " %s", frame->function_type);
+ if (frame->function_name)
+ strbuf_append_strf(str, " %s", frame->function_name);
+ if (verbose && frame->source_file)
+ {
+ if (frame->function_name)
+ strbuf_append_str(str, " at");
+ strbuf_append_strf(str, " %s", frame->source_file);
+ }
+
+ if (frame->signal_handler_called)
+ strbuf_append_str(str, " <signal handler called>");
+
+ strbuf_append_str(str, "\n");
+}
+
+/**
+ * Find string a or b in input, whatever comes first.
+ * If no string is found, return the \0 character at the end of input.
+ */
+static char *
+findfirstabnul(char *input, const char *a, const char *b)
+{
+ size_t alen = strlen(a);
+ size_t blen = strlen(b);
+ char *p = input;
+ while (*p)
+ {
+ if (strncmp(p, a, alen) == 0)
+ return p;
+ if (strncmp(p, b, blen) == 0)
+ return p;
+ ++p;
+ }
+ return p;
+}
+
+struct btp_frame *
+btp_frame_parse(char **input,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ struct btp_frame *header = btp_frame_parse_header(input, location);
+ if (!header)
+ return NULL;
+
+ /* Skip the variables section for now. */
+ /* Todo: speedup by implementing strstrnul. */
+ local_input = findfirstabnul(local_input, "\n#", "\nThread");
+ if (*local_input != '\0')
+ ++local_input; /* ++ skips the newline */
+
+ if (btp_debug_parser)
+ {
+ printf("frame #%u %s\n",
+ header->number,
+ header->function_name ? header->function_name : "signal handler called");
+ }
+
+ *input = local_input;
+ return header;
+}
+
+int
+btp_frame_parse_frame_start(char **input, unsigned *number)
+{
+ char *local_input = *input;
+
+ /* Read the hash sign. */
+ if (!btp_skip_char(&local_input, '#'))
+ return 0;
+ int count = 1;
+
+ /* Read the frame position. */
+ int digits = btp_parse_unsigned_integer(&local_input, number);
+ count += digits;
+ if (0 == digits)
+ return 0;
+
+ /* Read all the spaces after the positon. */
+ int spaces = btp_skip_char_sequence(&local_input, ' ');
+ count += spaces;
+ if (0 == spaces)
+ return 0;
+
+ *input = local_input;
+ return count;
+}
+
+int
+btp_frame_parseadd_operator(char **input, struct strbuf *target)
+{
+ char *local_input = *input;
+ if (0 == btp_skip_string(&local_input, "operator"))
+ return 0;
+
+#define OP(x) \
+ if (0 < btp_skip_string(&local_input, x)) \
+ { \
+ strbuf_append_str(target, "operator"); \
+ strbuf_append_str(target, x); \
+ int length = local_input - *input; \
+ *input = local_input; \
+ return length; \
+ }
+
+ OP(">>=")OP(">>")OP(">=")OP(">");
+ OP("<<=")OP("<<")OP("<=")OP("<");
+ OP("->*")OP("->")OP("-");
+ OP("==")OP("=");
+ OP("&&")OP("&=")OP("&");
+ OP("||")OP("|=")OP("|");
+ OP("++")OP("+=")OP("+");
+ OP("--")OP("-=")OP("-");
+ OP("/=")OP("/");
+ OP("*=")OP("*");
+ OP("%=")OP("%");
+ OP("!=")OP("!");
+ OP("~");
+ OP("()");
+ OP("[]");
+ OP(",");
+ OP("^=")OP("^");
+ OP(" new[]")OP(" new");
+ OP(" delete[]")OP(" delete");
+ /* User defined operators are not parsed.
+ Should they be? */
+#undef OP
+ return 0;
+}
+
+#define FUNCTION_NAME_CHARS BTP_alnum "@.:=!*+-[]~&/%^|,_"
+
+int
+btp_frame_parse_function_name_chunk(char **input,
+ bool space_allowed,
+ char **target)
+{
+ char *local_input = *input;
+ struct strbuf *buf = strbuf_new();
+ while (*local_input)
+ {
+ if (0 < btp_frame_parseadd_operator(&local_input, buf))
+ {
+ /* Space is allowed after operator even when it
+ is not normally allowed. */
+ if (btp_skip_char(&local_input, ' '))
+ {
+ /* ...but if ( follows, it is not allowed. */
+ if (btp_skip_char(&local_input, '('))
+ {
+ /* Return back both the space and (. */
+ local_input -= 2;
+ }
+ else
+ strbuf_append_char(buf, ' ');
+ }
+ }
+
+ if (strchr(FUNCTION_NAME_CHARS, *local_input) == NULL)
+ {
+ if (!space_allowed || strchr(" ", *local_input) == NULL)
+ break;
+ }
+
+ strbuf_append_char(buf, *local_input);
+ ++local_input;
+ }
+
+ if (buf->len == 0)
+ {
+ strbuf_free(buf);
+ return 0;
+ }
+
+ *target = strbuf_free_nobuf(buf);
+ int total_char_count = local_input - *input;
+ *input = local_input;
+ return total_char_count;
+}
+
+int
+btp_frame_parse_function_name_braces(char **input, char **target)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '('))
+ return 0;
+
+ struct strbuf *buf = strbuf_new();
+ strbuf_append_char(buf, '(');
+ while (true)
+ {
+ char *namechunk = NULL;
+ if (0 < btp_frame_parse_function_name_chunk(&local_input, true, &namechunk) ||
+ 0 < btp_frame_parse_function_name_braces(&local_input, &namechunk) ||
+ 0 < btp_frame_parse_function_name_template(&local_input, &namechunk))
+ {
+ strbuf_append_str(buf, namechunk);
+ free(namechunk);
+ }
+ else
+ break;
+ }
+
+ if (!btp_skip_char(&local_input, ')'))
+ {
+ strbuf_free(buf);
+ return 0;
+ }
+
+ strbuf_append_char(buf, ')');
+ *target = strbuf_free_nobuf(buf);
+ int total_char_count = local_input - *input;
+ *input = local_input;
+ return total_char_count;
+}
+
+int
+btp_frame_parse_function_name_template(char **input, char **target)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '<'))
+ return 0;
+
+ struct strbuf *buf = strbuf_new();
+ strbuf_append_char(buf, '<');
+ while (true)
+ {
+ char *namechunk = NULL;
+ if (0 < btp_frame_parse_function_name_chunk(&local_input, true, &namechunk) ||
+ 0 < btp_frame_parse_function_name_braces(&local_input, &namechunk) ||
+ 0 < btp_frame_parse_function_name_template(&local_input, &namechunk))
+ {
+ strbuf_append_str(buf, namechunk);
+ free(namechunk);
+ }
+ else
+ break;
+ }
+
+ if (!btp_skip_char(&local_input, '>'))
+ {
+ strbuf_free(buf);
+ return 0;
+ }
+
+ strbuf_append_char(buf, '>');
+ *target = strbuf_free_nobuf(buf);
+ int total_char_count = local_input - *input;
+ *input = local_input;
+ return total_char_count;
+}
+
+bool
+btp_frame_parse_function_name(char **input,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location)
+{
+ /* Handle unknown function name, represended by double question
+ mark. */
+ if (btp_parse_string(input, "??", function_name))
+ {
+ *function_type = NULL;
+ location->column += 2;
+ return true;
+ }
+
+ char *local_input = *input;
+ /* Up to three parts of function name. */
+ struct strbuf *buf0 = strbuf_new(), *buf1 = NULL;
+
+ /* First character:
+ '~' for destructor
+ '*' for ????
+ '_a-zA-Z' for mangled/nonmangled function name
+ '(' to start "(anonymous namespace)::" or something
+ */
+ char first;
+ char *namechunk;
+ if (btp_parse_char_limited(&local_input, "~*_" BTP_alpha, &first))
+ {
+ /* If it's a start of 'o'perator, put the 'o' back! */
+ if (first == 'o')
+ --local_input;
+ else
+ {
+ strbuf_append_char(buf0, first);
+ ++location->column;
+ }
+ }
+ else
+ {
+ int chars = btp_frame_parse_function_name_braces(&local_input,
+ &namechunk);
+ if (0 < chars)
+ {
+ strbuf_append_str(buf0, namechunk);
+ free(namechunk);
+ location->column += chars;
+ }
+ else
+ {
+ location->message = "Expected function name.";
+ strbuf_free(buf0);
+ return false;
+ }
+ }
+
+ /* The rest consists of function name, braces, templates...*/
+ while (true)
+ {
+ char *namechunk = NULL;
+ int chars = btp_frame_parse_function_name_chunk(&local_input,
+ false,
+ &namechunk);
+
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_braces(&local_input,
+ &namechunk);
+ }
+
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_template(&local_input,
+ &namechunk);
+ }
+
+ if (0 == chars)
+ break;
+
+ strbuf_append_str(buf0, namechunk);
+ free(namechunk);
+ location->column += chars;
+ }
+
+ /* Function name MUST be ended by empty space. */
+ char space;
+ if (!btp_parse_char_limited(&local_input, BTP_space, &space))
+ {
+ strbuf_free(buf0);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+
+ /* Some C++ function names and function types might contain suffix
+ " const". */
+ int chars = btp_skip_string(&local_input, "const");
+ if (0 < chars)
+ {
+ strbuf_append_char(buf0, space);
+ btp_location_eat_char(location, space);
+ strbuf_append_str(buf0, "const");
+ location->column += chars;
+
+ /* Check the empty space after function name again.*/
+ if (!btp_parse_char_limited(&local_input, BTP_space, &space))
+ {
+ /* Function name MUST be ended by empty space. */
+ strbuf_free(buf0);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+ }
+
+ /* Maybe the first series was just a type of the function, and now
+ the real function follows. Now, we know it must not start with
+ '(', nor with '<'. */
+ chars = btp_frame_parse_function_name_chunk(&local_input,
+ false,
+ &namechunk);
+ if (0 < chars)
+ {
+ /* Eat the space separator first. */
+ btp_location_eat_char(location, space);
+
+ buf1 = strbuf_new();
+ strbuf_append_str(buf1, namechunk);
+ free(namechunk);
+ location->column += chars;
+
+ /* The rest consists of a function name parts, braces, templates...*/
+ while (true)
+ {
+ char *namechunk = NULL;
+ chars = btp_frame_parse_function_name_chunk(&local_input,
+ false,
+ &namechunk);
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_braces(&local_input,
+ &namechunk);
+ }
+ if (0 == chars)
+ {
+ chars = btp_frame_parse_function_name_template(&local_input,
+ &namechunk);
+ }
+ if (0 == chars)
+ break;
+
+ strbuf_append_str(buf1, namechunk);
+ free(namechunk);
+ location->column += chars;
+ }
+
+ /* Function name MUST be ended by empty space. */
+ if (!btp_parse_char_limited(&local_input, BTP_space, &space))
+ {
+ strbuf_free(buf0);
+ strbuf_free(buf1);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+ }
+
+ /* Again, some C++ function names might contain suffix " const" */
+ chars = btp_skip_string(&local_input, "const");
+ if (0 < chars)
+ {
+ struct strbuf *buf = buf1 ? buf1 : buf0;
+ strbuf_append_char(buf, space);
+ btp_location_eat_char(location, space);
+ strbuf_append_str(buf, "const");
+ location->column += chars;
+
+ /* Check the empty space after function name again.*/
+ if (!btp_skip_char_limited(&local_input, BTP_space))
+ {
+ /* Function name MUST be ended by empty space. */
+ strbuf_free(buf0);
+ strbuf_free(buf1);
+ location->message = "Space or newline expected after function name.";
+ return false;
+ }
+ }
+
+ /* Return back to the empty space. */
+ --local_input;
+
+ if (buf1)
+ {
+ *function_name = strbuf_free_nobuf(buf1);
+ *function_type = strbuf_free_nobuf(buf0);
+ }
+ else
+ {
+ *function_name = strbuf_free_nobuf(buf0);
+ *function_type = NULL;
+ }
+
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_skip_function_args(char **input, struct btp_location *location)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '('))
+ {
+ location->message = "Expected '(' to start function argument list.";
+ return false;
+ }
+ location->column += 1;
+
+ int depth = 0;
+ bool string = false;
+ bool escape = false;
+ do
+ {
+ if (string)
+ {
+ if (escape)
+ escape = false;
+ else if (*local_input == '\\')
+ escape = true;
+ else if (*local_input == '"')
+ string = false;
+ }
+ else
+ {
+ if (*local_input == '"')
+ string = true;
+ else if (*local_input == '(')
+ ++depth;
+ else if (*local_input == ')')
+ {
+ if (depth > 0)
+ --depth;
+ else
+ break;
+ }
+ }
+ btp_location_eat_char(location, *local_input);
+ ++local_input;
+ }
+ while (*local_input);
+
+ if (depth != 0 || string || escape)
+ {
+ location->message = "Unbalanced function parameter list.";
+ return false;
+ }
+
+ if (!btp_skip_char(&local_input, ')'))
+ {
+ location->message = "Expected ')' to close the function parameter list.";
+ return false;
+ }
+ location->column += 1;
+
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_parse_function_call(char **input,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ char *name = NULL, *type = NULL;
+ if (!btp_frame_parse_function_name(&local_input,
+ &name,
+ &type,
+ location))
+ {
+ /* The location message is set by the function returning
+ * false, no need to update it here. */
+ return false;
+ }
+
+ int line, column;
+ if (0 == btp_skip_char_span_location(&local_input,
+ " \n",
+ &line,
+ &column))
+ {
+ free(name);
+ free(type);
+ location->message = "Expected a space or newline after the function name.";
+ return false;
+ }
+ btp_location_add(location, line, column);
+
+ if (!btp_frame_skip_function_args(&local_input, location))
+ {
+ free(name);
+ free(type);
+ /* The location message is set by the function returning
+ * false, no need to update it here. */
+ return false;
+ }
+
+ *function_name = name;
+ *function_type = type;
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_parse_address_in_function(char **input,
+ uint64_t *address,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+
+ /* Read memory address in hexadecimal format. */
+ int digits = btp_parse_hexadecimal_number(&local_input, address);
+ location->column += digits;
+ if (0 == digits)
+ {
+ location->message = "Hexadecimal number representing memory address expected.";
+ return false;
+ }
+
+ /* Skip spaces. */
+ int chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after memory address.";
+ return false;
+ }
+
+ /* Skip keyword "in". */
+ chars = btp_skip_string(&local_input, "in");
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Keyword \"in\" expected after memory address.";
+ return false;
+ }
+
+ /* Skip spaces. */
+ chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after 'in'.";
+ return false;
+ }
+
+ /* C++ specific case for "0xfafa in vtable for function ()" */
+ chars = btp_skip_string(&local_input, "vtable");
+ location->column += chars;
+ if (0 < chars)
+ {
+ chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after 'vtable'.";
+ return false;
+ }
+
+ chars = btp_skip_string(&local_input, "for");
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Keyword \"for\" expected.";
+ return false;
+ }
+
+ chars = btp_skip_char_sequence(&local_input, ' ');
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Space expected after 'for'.";
+ return false;
+ }
+ }
+
+ if (!btp_frame_parse_function_call(&local_input,
+ function_name,
+ function_type,
+ location))
+ {
+ /* Do not update location here, it has been modified by the
+ called function. */
+ return false;
+ }
+
+ *input = local_input;
+ return true;
+}
+
+bool
+btp_frame_parse_file_location(char **input,
+ char **file,
+ unsigned *fileline,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ int line, column;
+ if (0 == btp_skip_char_span_location(&local_input, " \n", &line, &column))
+ {
+ location->message = "Expected a space or a newline.";
+ return false;
+ }
+ btp_location_add(location, line, column);
+
+ int chars = btp_skip_string(&local_input, "at");
+ if (0 == chars)
+ {
+ chars = btp_skip_string(&local_input, "from");
+ if (0 == chars)
+ {
+ location->message = "Expected 'at' or 'from'.";
+ return false;
+ }
+ }
+ location->column += chars;
+
+ int spaces = btp_skip_char_sequence(&local_input, ' ');
+ location->column += spaces;
+ if (0 == spaces)
+ {
+ location->message = "Expected a space before file location.";
+ return false;
+ }
+
+ char *file_name;
+ chars = btp_parse_char_span(&local_input, BTP_alnum "_/\\+.-", &file_name);
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Expected a file name.";
+ return false;
+ }
+
+ if (btp_skip_char(&local_input, ':'))
+ {
+ location->column += 1;
+ int digits = btp_parse_unsigned_integer(&local_input, fileline);
+ location->column += digits;
+ if (0 == digits)
+ {
+ free(file_name);
+ location->message = "Expected a line number.";
+ return false;
+ }
+ }
+ else
+ *fileline = -1;
+
+ *file = file_name;
+ *input = local_input;
+ return true;
+}
+
+struct btp_frame *
+btp_frame_parse_header(char **input,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+ struct btp_frame *imframe = btp_frame_new(); /* im - intermediate */
+ int chars = btp_frame_parse_frame_start(&local_input,
+ &imframe->number);
+
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Frame start sequence expected.";
+ btp_frame_free(imframe);
+ return NULL;
+ }
+
+ struct btp_location internal_location;
+ btp_location_init(&internal_location);
+ if (btp_frame_parse_address_in_function(&local_input,
+ &imframe->address,
+ &imframe->function_name,
+ &imframe->function_type,
+ &internal_location))
+ {
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+
+ /* Optional section " from file.c:65" */
+ /* Optional section " at file.c:65" */
+ btp_location_init(&internal_location);
+ if (btp_frame_parse_file_location(&local_input,
+ &imframe->source_file,
+ &imframe->source_line,
+ &internal_location))
+ {
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+ }
+ }
+ else
+ {
+ btp_location_init(&internal_location);
+ if (btp_frame_parse_function_call(&local_input,
+ &imframe->function_name,
+ &imframe->function_type,
+ &internal_location))
+ {
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+
+ /* Mandatory section " at file.c:65" */
+ btp_location_init(&internal_location);
+ if (!btp_frame_parse_file_location(&local_input,
+ &imframe->source_file,
+ &imframe->source_line,
+ &internal_location))
+ {
+ location->message = "Function call in the frame header "
+ "misses mandatory \"at file.c:xy\" section";
+ btp_frame_free(imframe);
+ return NULL;
+ }
+
+ btp_location_add(location,
+ internal_location.line,
+ internal_location.column);
+ }
+ else
+ {
+ int chars = btp_skip_string(&local_input, "<signal handler called>");
+ if (0 < chars)
+ {
+ location->column += chars;
+ imframe->signal_handler_called = true;
+ }
+ else
+ {
+ location->message = "Frame header variant not recognized.";
+ btp_frame_free(imframe);
+ return NULL;
+ }
+ }
+ }
+
+ *input = local_input;
+ return imframe;
+}
diff --git a/src/btparser/frame.h b/src/btparser/frame.h
new file mode 100644
index 00000000..966dd5d2
--- /dev/null
+++ b/src/btparser/frame.h
@@ -0,0 +1,470 @@
+/*
+ frame.h
+
+ Copyright (C) 2010 Red Hat, 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 BTPARSER_FRAME_H
+#define BTPARSER_FRAME_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct strbuf;
+struct btp_location;
+
+/**
+ * A frame representing a function call or a signal handler on a call
+ * stack of a thread.
+ */
+struct btp_frame
+{
+ /**
+ * A function name or NULL. If it's NULL, signal_handler_called is
+ * true.
+ */
+ char *function_name;
+ /**
+ * A function type, or NULL if it isn't present.
+ */
+ char *function_type;
+ /**
+ * A frame number in a thread. It does not necessarily show the
+ * actual position in the thread, as this number is set by the
+ * parser and never updated.
+ */
+ unsigned number;
+ /**
+ * The name of the source file containing the function definition,
+ * or the name of the binary file (.so) with the binary code of
+ * the function, or NULL.
+ */
+ char *source_file;
+ /**
+ * A line number in the source file, determining the position of
+ * the function definition, or -1 when unknown.
+ */
+ unsigned source_line;
+ /**
+ * Signal handler was called on this frame.
+ */
+ bool signal_handler_called;
+ /**
+ * The function address in the computer memory, or -1 when the
+ * address is unknown.
+ */
+ uint64_t address;
+ /**
+ * A sibling frame residing below this one, or NULL if this is the
+ * last frame in the parent thread.
+ */
+ struct btp_frame *next;
+};
+
+/**
+ * Creates and initializes a new frame structure.
+ * @returns
+ * It never returns NULL. The returned pointer must be released by
+ * calling the function btp_frame_free().
+ */
+struct btp_frame *
+btp_frame_new();
+
+/**
+ * Initializes all members of the frame structure to their default
+ * values. No memory is released, members are simply overwritten.
+ * This is useful for initializing a frame structure placed on the
+ * stack.
+ */
+void
+btp_frame_init(struct btp_frame *frame);
+
+/**
+ * Releases the memory held by the frame. The frame siblings are not
+ * released.
+ * @param frame
+ * If the frame is NULL, no operation is performed.
+ */
+void
+btp_frame_free(struct btp_frame *frame);
+
+/**
+ * Creates a duplicate of the frame.
+ * @param frame
+ * It must be non-NULL pointer. The frame is not modified by calling
+ * this function.
+ * @param siblings
+ * Whether to duplicate also siblings referenced by frame->next. If
+ * false, frame->next is not duplicated for the new frame, but it is
+ * set to NULL.
+ * @returns
+ * This function never returns NULL. If the returned duplicate is not
+ * shallow, it must be released by calling the function
+ * btp_frame_free().
+ */
+struct btp_frame *
+btp_frame_dup(struct btp_frame *frame,
+ bool siblings);
+
+/**
+ * Checks whether the frame represents a call of function with certain
+ * function name.
+ */
+bool
+btp_frame_calls_func(struct btp_frame *frame,
+ const char *function_name);
+
+/**
+ * Checks whether the frame represents a call of function with certain
+ * function name, which resides in a source file.
+ * @param source_file
+ * The frame's source_file is searched for the source_file as a
+ * substring.
+ */
+bool
+btp_frame_calls_func_in_file(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file);
+
+/**
+ * Checks whether the frame represents a call of function with certain
+ * function name, which resides in one of the source files.
+ * @param source_file0
+ * The frame's source_file is searched for the source_file0 as a
+ * substring.
+ * @returns
+ * True if the frame corresponds to a function with function_name,
+ * residing in the source_file0, or source_file1.
+ */
+bool
+btp_frame_calls_func_in_file2(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1);
+
+/**
+ * Checks whether the frame represents a call of function with certain
+ * function name, which resides in one of the source files.
+ * @param source_file0
+ * The frame's source_file is searched for the source_file0 as a
+ * substring.
+ * @returns
+ * True if the frame corresponds to a function with function_name,
+ * residing in the source_file0, source_file1, or source_file2.
+ */
+bool
+btp_frame_calls_func_in_file3(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1,
+ const char *source_file2);
+
+/**
+ * Checks whether the frame represents a call of function with certain
+ * function name, which resides in one of the source files.
+ * @param source_file0
+ * The frame's source_file is searched for the source_file0 as a
+ * substring.
+ * @returns
+ * True if the frame corresponds to a function with function_name,
+ * residing in the source_file0, source_file1, source_file2, or
+ * source_file3.
+ */
+bool
+btp_frame_calls_func_in_file4(struct btp_frame *frame,
+ const char *function_name,
+ const char *source_file0,
+ const char *source_file1,
+ const char *source_file2,
+ const char *source_file3);
+
+/**
+ * Compares two frames.
+ * @param f1
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @param f2
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ * @param compare_number
+ * Indicates whether to include the frame numbers in the
+ * comparsion. If set to false, the frame numbers are ignored.
+ * @returns
+ * Returns 0 if the frames are same. Returns negative number if f1 is
+ * found to be 'less' than f2. Returns positive number if f1 is found
+ * to be 'greater' than f2.
+ */
+int
+btp_frame_cmp(struct btp_frame *f1,
+ struct btp_frame *f2,
+ bool compare_number);
+
+/**
+ * Puts the frame 'b' to the bottom of the stack 'a'. In other words,
+ * it finds the last sibling of the frame 'a', and appends the frame
+ * 'b' to this last sibling.
+ */
+void
+btp_frame_add_sibling(struct btp_frame *a,
+ struct btp_frame *b);
+
+/**
+ * Appends the textual representation of the frame to the string
+ * buffer.
+ * @param frame
+ * It must be non-NULL pointer. It's not modified by calling this
+ * function.
+ */
+void
+btp_frame_append_to_str(struct btp_frame *frame,
+ struct strbuf *str,
+ bool verbose);
+
+/**
+ * If the input contains a complete frame, this function parses the
+ * frame text, returns it in a structure, and moves the input pointer
+ * after the frame. If the input does not contain proper, complete
+ * frame, the function does not modify input and returns NULL.
+ * @returns
+ * Allocated pointer with a frame structure. The pointer should be
+ * released by btp_frame_free().
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. When this function returns NULL, the structure will contain
+ * the error line, column, and message. The line and column members
+ * of the location are gradually increased as the parser handles the
+ * input, so the location should be initialized before calling this
+ * function to get reasonable values.
+ */
+struct btp_frame *
+btp_frame_parse(char **input,
+ struct btp_location *location);
+
+/**
+ * If the input contains a proper frame start section, parse the frame
+ * number, and move the input pointer after this section. Otherwise do
+ * not modify input.
+ * @returns
+ * The number of characters parsed from input. 0 if the input does not
+ * contain a frame start.
+ * @code
+ * "#1 "
+ * "#255 "
+ * @endcode
+ */
+int
+btp_frame_parse_frame_start(char **input, unsigned *number);
+
+/**
+ * Parses C++ operator on input.
+ * Supports even 'operator new[]' and 'operator delete[]'.
+ * @param target
+ * The parsed operator name is appened to the string buffer provided,
+ * if an operator is found. Otherwise the string buffer is not
+ * changed.
+ * @returns
+ * The number of characters parsed from input. 0 if the input does not
+ * contain operator.
+ */
+int
+btp_frame_parseadd_operator(char **input,
+ struct strbuf *target);
+
+/**
+ * Parses a part of function name from the input.
+ * @param target
+ * Pointer to a non-allocated pointer. This function will set
+ * the pointer to newly allocated memory containing the name chunk,
+ * if it returns positive, nonzero value.
+ * @returns
+ * The number of characters parsed from input. 0 if the input does not
+ * contain a part of function name.
+ */
+int
+btp_frame_parse_function_name_chunk(char **input,
+ bool space_allowed,
+ char **target);
+
+/**
+ * If the input buffer contains part of function name containing braces,
+ * for example "(anonymous namespace)", parse it, append the contents
+ * to target and move input after the braces.
+ * Otherwise do not modify niether the input nor the target.
+ * @returns
+ * The number of characters parsed from input. 0 if the input does not
+ * contain a braced part of function name.
+ */
+int
+btp_frame_parse_function_name_braces(char **input,
+ char **target);
+
+/**
+ * @returns
+ * The number of characters parsed from input. 0 if the input does not
+ * contain a template part of function name.
+ */
+int
+btp_frame_parse_function_name_template(char **input,
+ char **target);
+
+/**
+ * Parses the function name, which is a part of the frame header, from
+ * the input. If the frame header contains also the function type,
+ * it's also parsed.
+ * @param function_name
+ * A pointer pointing to an uninitialized pointer. This function
+ * allocates a string and sets the pointer to it if it parses the
+ * function name from the input successfully. The memory returned
+ * this way must be released by the caller using the function free().
+ * If this function returns true, this pointer is guaranteed to be
+ * non-NULL.
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ * @returns
+ * True if the input stream contained a function name, which has been
+ * parsed. False otherwise.
+ */
+bool
+btp_frame_parse_function_name(char **input,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location);
+
+/**
+ * Skips function arguments which are a part of the frame header, in
+ * the input stream.
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ */
+bool
+btp_frame_skip_function_args(char **input,
+ struct btp_location *location);
+
+/**
+ * If the input contains proper function call, parse the function
+ * name and store it to result, move the input pointer after whole
+ * function call, and return true. Otherwise do not modify the input
+ * and return false.
+ *
+ * If this function returns true, the caller is responsible to free
+ * the the function_name.
+ * @todo
+ * Parse and return the function call arguments.
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ */
+bool
+btp_frame_parse_function_call(char **input,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location);
+
+/**
+ * If the input contains address and function call, parse them, move
+ * the input pointer after this sequence, and return true.
+ * Otherwise do not modify the input and return false.
+ *
+ * If this function returns true, the caller is responsible to free
+ * the parameter function.
+ *
+ * @code
+ * 0x000000322160e7fd in fsync ()
+ * 0x000000322222987a in write_to_temp_file (
+ * filename=0x18971b0 "/home/jfclere/.recently-used.xbel",
+ * contents=<value optimized out>, length=29917, error=0x7fff3cbe4110)
+ * @endcode
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ */
+bool
+btp_frame_parse_address_in_function(char **input,
+ uint64_t *address,
+ char **function_name,
+ char **function_type,
+ struct btp_location *location);
+
+/**
+ * If the input contains sequence "from path/to/file:fileline" or "at
+ * path/to/file:fileline", parse it, move the input pointer after this
+ * sequence and return true. Otherwise do not modify the input and
+ * return false.
+ *
+ * The ':' followed by line number is optional. If it is not present,
+ * the fileline is set to -1.
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ */
+bool
+btp_frame_parse_file_location(char **input,
+ char **file,
+ unsigned *fileline,
+ struct btp_location *location);
+
+/**
+ * If the input contains proper frame header, this function
+ * parses the frame header text, moves the input pointer
+ * after the frame header, and returns a frame struct.
+ * If the input does not contain proper frame header, this function
+ * returns NULL and does not modify input.
+ * @param location
+ * The caller must provide a pointer to an instance of btp_location
+ * here. The line and column members of the location are gradually
+ * increased as the parser handles the input, so the location should
+ * be initialized before calling this function to get reasonable
+ * values. When this function returns false (an error occurred), the
+ * structure will contain the error line, column, and message.
+ * @returns
+ * Newly created frame struct or NULL. The returned frame struct
+ * should be released by btp_frame_free().
+ */
+struct btp_frame *
+btp_frame_parse_header(char **input,
+ struct btp_location *location);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/btparser/location.c b/src/btparser/location.c
new file mode 100644
index 00000000..ade706f2
--- /dev/null
+++ b/src/btparser/location.c
@@ -0,0 +1,78 @@
+/*
+ location.c
+
+ Copyright (C) 2010 Red Hat, 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 "location.h"
+#include <stdlib.h> /* contains NULL */
+
+void
+btp_location_init(struct btp_location *location)
+{
+ location->line = 1;
+ location->column = 0;
+ location->message = NULL;
+}
+
+void
+btp_location_add(struct btp_location *location,
+ int add_line,
+ int add_column)
+{
+ btp_location_add_ext(&location->line,
+ &location->column,
+ add_line,
+ add_column);
+}
+
+void
+btp_location_add_ext(int *line,
+ int *column,
+ int add_line,
+ int add_column)
+{
+ if (add_line > 1)
+ {
+ *line += add_line - 1;
+ *column = add_column;
+ }
+ else
+ *column += add_column;
+}
+
+void
+btp_location_eat_char(struct btp_location *location,
+ char c)
+{
+ btp_location_eat_char_ext(&location->line,
+ &location->column,
+ c);
+}
+
+void
+btp_location_eat_char_ext(int *line,
+ int *column,
+ char c)
+{
+ if (c == '\n')
+ {
+ *line += 1;
+ *column = 0;
+ }
+ else
+ *column += 1;
+}
diff --git a/src/btparser/location.h b/src/btparser/location.h
new file mode 100644
index 00000000..0d620205
--- /dev/null
+++ b/src/btparser/location.h
@@ -0,0 +1,118 @@
+/*
+ location.h
+
+ Copyright (C) 2010 Red Hat, 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 BTPARSER_LOCATION_H
+#define BTPARSER_LOCATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A location in the backtrace file with an attached message.
+ * It's used for error reporting: the line and the column points to
+ * the place where a parser error occurred, and the message explains
+ * what the parser expected and didn't find on that place.
+ */
+struct btp_location
+{
+ /** Starts from 1. */
+ int line;
+ /** Starts from 0. */
+ int column;
+ /**
+ * Error message related to the line and column. Do not release
+ * the memory this pointer points to.
+ */
+ const char *message;
+};
+
+/**
+ * Initializes all members of the location struct to their default
+ * values. No memory is allocated or released by this function.
+ */
+void
+btp_location_init(struct btp_location *location);
+
+/**
+ * Adds a line and a column to specific location.
+ * @note
+ * If the line is not 1 (meaning the first line), the column in the
+ * location structure is overwritten by the provided add_column value.
+ * Otherwise the add_column value is added to the column member of the
+ * location structure.
+ * @param location
+ * The structure to be modified. It must be a valid pointer.
+ * @param add_line
+ * Starts from 1. It means that if add_line is 1, the line member of the
+ * location structure is not changed.
+ * @param add_column
+ * Starts from 0.
+ */
+void
+btp_location_add(struct btp_location *location,
+ int add_line,
+ int add_column);
+
+/**
+ * Adds a line column pair to another line column pair.
+ * @note
+ * If the add_line is not 1 (meaning the frist line), the column is
+ * overwritten by the provided add_column value. Otherwise the
+ * add_column value is added to the column.
+ * @param add_line
+ * Starts from 1. It means that if add_line is 1, the line is not
+ * changed.
+ * @param add_column
+ * Starts from 0.
+ */
+void
+btp_location_add_ext(int *line,
+ int *column,
+ int add_line,
+ int add_column);
+
+/**
+ * Updates the line and column of the location by moving "after" the
+ * char c. If c is a newline character, the line number is increased
+ * and the column is set to 0. Otherwise the column is increased by 1.
+ */
+void
+btp_location_eat_char(struct btp_location *location,
+ char c);
+
+/**
+ * Updates the line and the column by moving "after" the char c. If c
+ * is a newline character, the line number is increased and the column
+ * is set to 0. Otherwise the column is increased.
+ * @param line
+ * Must be a valid pointer.
+ * @param column
+ * Must be a valid pointer.
+ */
+void
+btp_location_eat_char_ext(int *line,
+ int *column,
+ char c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/btparser/normalize.c b/src/btparser/normalize.c
new file mode 100644
index 00000000..4bbee99c
--- /dev/null
+++ b/src/btparser/normalize.c
@@ -0,0 +1,68 @@
+/*
+ normalize.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include "backtrace.h"
+#include <string.h>
+
+void
+btp_normalize_thread(struct btp_thread *thread)
+{
+ btp_normalize_dbus_thread(thread);
+ btp_normalize_gdk_thread(thread);
+ btp_normalize_glib_thread(thread);
+ btp_normalize_glibc_thread(thread);
+ btp_normalize_libstdcpp_thread(thread);
+ btp_normalize_linux_thread(thread);
+ btp_normalize_xorg_thread(thread);
+
+ /* If the last frame has address 0x0000 and its name is '??',
+ * remove it. This frame is not really invalid, and it affects
+ * backtrace quality rating. See Red Hat Bugzilla bug #592523.
+ * @code
+ * #2 0x00007f4dcebbd62d in clone ()
+ * at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
+ * No locals.
+ * #3 0x0000000000000000 in ?? ()
+ * @endcode
+ */
+ struct btp_frame *last = thread->frames;
+ while (last && last->next)
+ last = last->next;
+ if (last &&
+ last->address == 0x0000 &&
+ last->function_name &&
+ 0 == strcmp(last->function_name, "??"))
+ {
+ btp_thread_remove_frame(thread, last);
+ }
+}
+
+void
+btp_normalize_backtrace(struct btp_backtrace *backtrace)
+{
+ struct btp_thread *thread = backtrace->threads;
+ while (thread)
+ {
+ btp_normalize_thread(thread);
+ thread = thread->next;
+ }
+}
diff --git a/src/btparser/normalize.h b/src/btparser/normalize.h
new file mode 100644
index 00000000..35fd2836
--- /dev/null
+++ b/src/btparser/normalize.h
@@ -0,0 +1,74 @@
+/*
+ normalize.h
+
+ Copyright (C) 2010 Red Hat, 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 BTPARSER_NORMALIZE_H
+#define BTPARSER_NORMALIZE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct btp_thread;
+struct btp_backtrace;
+
+void
+btp_normalize_thread(struct btp_thread *thread);
+
+void
+btp_normalize_backtrace(struct btp_backtrace *backtrace);
+
+/**
+ */
+void
+btp_normalize_dbus_thread(struct btp_thread *thread);
+
+void
+btp_normalize_gdk_thread(struct btp_thread *thread);
+
+void
+btp_normalize_glib_thread(struct btp_thread *thread);
+
+/**
+ * Checks whether the thread it contains some function used to exit
+ * application. If a frame with the function is found, it is
+ * returned. If there are multiple frames with abort function, the
+ * lowest one is returned.
+ * @returns
+ * Returns NULL if such a frame is not found.
+ */
+struct btp_frame *
+btp_glibc_thread_find_exit_frame(struct btp_thread *thread);
+
+void
+btp_normalize_glibc_thread(struct btp_thread *thread);
+
+void
+btp_normalize_libstdcpp_thread(struct btp_thread *thread);
+
+void
+btp_normalize_linux_thread(struct btp_thread *thread);
+
+void
+btp_normalize_xorg_thread(struct btp_thread *thread);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/btparser/normalize_dbus.c b/src/btparser/normalize_dbus.c
new file mode 100644
index 00000000..d3d6a13a
--- /dev/null
+++ b/src/btparser/normalize_dbus.c
@@ -0,0 +1,44 @@
+/*
+ normalize_dbus.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include <stdbool.h>
+
+void
+btp_normalize_dbus_thread(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable =
+ btp_frame_calls_func_in_file(frame, "gerror_to_dbus_error_message", "dbus-gobject.c") ||
+ btp_frame_calls_func_in_file(frame, "dbus_g_method_return_error", "dbus-gobject.c");
+ if (removable)
+ {
+ btp_thread_remove_frame(thread, frame);
+ }
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/normalize_gdk.c b/src/btparser/normalize_gdk.c
new file mode 100644
index 00000000..e9c4ef6a
--- /dev/null
+++ b/src/btparser/normalize_gdk.c
@@ -0,0 +1,44 @@
+/*
+ normalize_gdk.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include <stdbool.h>
+
+void
+btp_normalize_gdk_thread(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable =
+ btp_frame_calls_func_in_file(frame, "gdk_x_error", "gdkmain-x11.c");
+
+ if (removable)
+ {
+ btp_thread_remove_frame(thread, frame);
+ }
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/normalize_glib.c b/src/btparser/normalize_glib.c
new file mode 100644
index 00000000..b5c12b60
--- /dev/null
+++ b/src/btparser/normalize_glib.c
@@ -0,0 +1,60 @@
+/*
+ normalize_glib.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include "utils.h"
+#include <stdbool.h>
+#include <string.h>
+
+void
+btp_normalize_glib_thread(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Normalize frame names. */
+ if (frame->function_name &&
+ 0 == strncmp(frame->function_name, "IA__g_", strlen("IA__g_")))
+ {
+ /* Remove the IA__ prefix. The strcpy function cannot be
+ * used for that because the source and destination
+ * pointers overlap. */
+ char *p = frame->function_name;
+ while ((*p = p[4]) != '\0')
+ ++p;
+ }
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable =
+ btp_frame_calls_func_in_file2(frame, "g_log", "gmessages.c", "libglib") ||
+ btp_frame_calls_func_in_file2(frame, "g_logv", "gmessages.c", "libglib") ||
+ btp_frame_calls_func_in_file2(frame, "g_assertion_message", "gtestutils.c", "libglib") ||
+ btp_frame_calls_func_in_file2(frame, "g_assertion_message_expr", "gtestutils.c", "libglib");
+ if (removable)
+ {
+ btp_thread_remove_frame(thread, frame);
+ }
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/normalize_glibc.c b/src/btparser/normalize_glibc.c
new file mode 100644
index 00000000..ea40ba9d
--- /dev/null
+++ b/src/btparser/normalize_glibc.c
@@ -0,0 +1,120 @@
+/*
+ normalize_glibc.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include "utils.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+struct btp_frame *
+btp_glibc_thread_find_exit_frame(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ struct btp_frame *result = NULL;
+ while (frame)
+ {
+ bool is_exit_frame =
+ btp_frame_calls_func_in_file(frame, "__run_exit_handlers", "exit.c") ||
+ btp_frame_calls_func_in_file4(frame, "raise", "pt-raise.c", "libc.so", "libc-", "libpthread.so") ||
+ btp_frame_calls_func_in_file(frame, "exit", "exit.c") ||
+ btp_frame_calls_func_in_file3(frame, "abort", "abort.c", "libc.so", "libc-") ||
+ /* Terminates a function in case of buffer overflow. */
+ btp_frame_calls_func_in_file2(frame, "__chk_fail", "chk_fail.c", "libc.so");
+
+ if (is_exit_frame)
+ result = frame;
+
+ frame = frame->next;
+ }
+
+ return result;
+}
+
+
+void
+btp_normalize_glibc_thread(struct btp_thread *thread)
+{
+ /* Find the exit frame and remove everything above it. */
+ struct btp_frame *exit_frame = btp_glibc_thread_find_exit_frame(thread);
+ if (exit_frame)
+ {
+ bool success = btp_thread_remove_frames_above(thread, exit_frame);
+ assert(success); /* if this fails, some code become broken */
+ success = btp_thread_remove_frame(thread, exit_frame);
+ assert(success); /* if this fails, some code become broken */
+ }
+
+ /* Standard function filtering loop. */
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Normalize frame names. */
+#define NORMALIZE_ARCH_SPECIFIC(func) \
+ if (btp_frame_calls_func_in_file2(frame, "__" func "_sse2", func ".S", "libc.so") || \
+ btp_frame_calls_func_in_file2(frame, "__" func "_ssse3", func ".S", "libc.so") || \
+ btp_frame_calls_func_in_file2(frame, "__" func "_ia32", func ".S", "libc.so")) \
+ { \
+ strcpy(frame->function_name, func); \
+ }
+
+ NORMALIZE_ARCH_SPECIFIC("memchr");
+ NORMALIZE_ARCH_SPECIFIC("memcmp");
+ NORMALIZE_ARCH_SPECIFIC("memcpy");
+ NORMALIZE_ARCH_SPECIFIC("memset");
+ NORMALIZE_ARCH_SPECIFIC("rawmemchr");
+ NORMALIZE_ARCH_SPECIFIC("strcat");
+ NORMALIZE_ARCH_SPECIFIC("strchr");
+ NORMALIZE_ARCH_SPECIFIC("strchrnul");
+ NORMALIZE_ARCH_SPECIFIC("strcmp");
+ NORMALIZE_ARCH_SPECIFIC("strcpy");
+ NORMALIZE_ARCH_SPECIFIC("strcspn");
+ NORMALIZE_ARCH_SPECIFIC("strlen");
+ NORMALIZE_ARCH_SPECIFIC("strncmp");
+ NORMALIZE_ARCH_SPECIFIC("strpbrk");
+ NORMALIZE_ARCH_SPECIFIC("strrchr");
+ NORMALIZE_ARCH_SPECIFIC("strspn");
+ NORMALIZE_ARCH_SPECIFIC("strtok");
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable =
+ btp_frame_calls_func(frame, "__assert_fail") ||
+ btp_frame_calls_func(frame, "__strcat_chk") ||
+ btp_frame_calls_func(frame, "__strcpy_chk") ||
+ btp_frame_calls_func(frame, "__strncpy_chk") ||
+ btp_frame_calls_func(frame, "__vsnprintf_chk") ||
+ btp_frame_calls_func(frame, "___vsnprintf_chk") ||
+ btp_frame_calls_func(frame, "__snprintf_chk") ||
+ btp_frame_calls_func(frame, "___snprintf_chk") ||
+ btp_frame_calls_func(frame, "__vasprintf_chk");
+ if (removable)
+ {
+ bool success = btp_thread_remove_frames_above(thread, frame);
+ assert(success);
+ success = btp_thread_remove_frame(thread, frame);
+ assert(success);
+ }
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/normalize_libstdcpp.c b/src/btparser/normalize_libstdcpp.c
new file mode 100644
index 00000000..1f833ded
--- /dev/null
+++ b/src/btparser/normalize_libstdcpp.c
@@ -0,0 +1,46 @@
+/*
+ normalize_libstdcpp.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include <stdbool.h>
+
+void
+btp_normalize_libstdcpp_thread(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable =
+ btp_frame_calls_func_in_file(frame, "__gnu_cxx::__verbose_terminate_handler", "vterminate.cc") ||
+ btp_frame_calls_func_in_file(frame, "__cxxabiv1::__terminate", "eh_terminate.cc") ||
+ btp_frame_calls_func_in_file(frame, "std::terminate", "eh_terminate.cc") ||
+ btp_frame_calls_func_in_file(frame, "__cxxabiv1::__cxa_throw", "eh_throw.cc");
+ if (removable)
+ {
+ btp_thread_remove_frame(thread, frame);
+ }
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/normalize_linux.c b/src/btparser/normalize_linux.c
new file mode 100644
index 00000000..8df9f9c2
--- /dev/null
+++ b/src/btparser/normalize_linux.c
@@ -0,0 +1,41 @@
+/*
+ normalize_linux.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "utils.h"
+#include "frame.h"
+#include "thread.h"
+#include <stdbool.h>
+
+void
+btp_normalize_linux_thread(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable = btp_frame_calls_func(frame, "__kernel_vsyscall");
+ if (removable)
+ btp_thread_remove_frame(thread, frame);
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/normalize_xorg.c b/src/btparser/normalize_xorg.c
new file mode 100644
index 00000000..11e8d624
--- /dev/null
+++ b/src/btparser/normalize_xorg.c
@@ -0,0 +1,46 @@
+/*
+ normalize_xorg.c
+
+ Copyright (C) 2010 Red Hat, 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 "normalize.h"
+#include "frame.h"
+#include "thread.h"
+#include <stdbool.h>
+
+void
+btp_normalize_xorg_thread(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ struct btp_frame *next_frame = frame->next;
+
+ /* Remove frames which are not a cause of the crash. */
+ bool removable =
+ btp_frame_calls_func_in_file(frame, "_XReply", "xcb_io.c") ||
+ btp_frame_calls_func_in_file(frame, "_XError", "XlibInt.c") ||
+ btp_frame_calls_func_in_file(frame, "XSync", "Sync.c") ||
+ btp_frame_calls_func_in_file(frame, "process_responses", "xcb_io.c");
+ if (removable)
+ {
+ btp_thread_remove_frame(thread, frame);
+ }
+
+ frame = next_frame;
+ }
+}
diff --git a/src/btparser/thread.c b/src/btparser/thread.c
new file mode 100644
index 00000000..af480eb3
--- /dev/null
+++ b/src/btparser/thread.c
@@ -0,0 +1,364 @@
+/*
+ thread.c
+
+ Copyright (C) 2010 Red Hat, 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 "thread.h"
+#include "frame.h"
+#include "strbuf.h"
+#include "utils.h"
+#include "location.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct btp_thread *
+btp_thread_new()
+{
+ struct btp_thread *thread = btp_malloc(sizeof(struct btp_thread));
+ btp_thread_init(thread);
+ return thread;
+}
+
+void
+btp_thread_init(struct btp_thread *thread)
+{
+ thread->number = -1;
+ thread->frames = NULL;
+ thread->next = NULL;
+}
+
+void
+btp_thread_free(struct btp_thread *thread)
+{
+ if (!thread)
+ return;
+
+ while (thread->frames)
+ {
+ struct btp_frame *rm = thread->frames;
+ thread->frames = rm->next;
+ btp_frame_free(rm);
+ }
+
+ free(thread);
+}
+
+struct btp_thread *
+btp_thread_dup(struct btp_thread *thread, bool siblings)
+{
+ struct btp_thread *result = btp_thread_new();
+ memcpy(result, thread, sizeof(struct btp_thread));
+
+ /* Handle siblings. */
+ if (siblings)
+ {
+ if (result->next)
+ result->next = btp_thread_dup(result->next, true);
+ }
+ else
+ result->next = NULL; /* Do not copy that. */
+
+ result->frames = btp_frame_dup(result->frames, true);
+
+ return result;
+}
+
+int
+btp_thread_cmp(struct btp_thread *t1, struct btp_thread *t2)
+{
+ int number = t1->number - t2->number;
+ if (number != 0)
+ return number;
+ struct btp_frame *f1 = t1->frames, *f2 = t2->frames;
+ do {
+ if (f1 && !f2)
+ return 1;
+ else if (f2 && !f1)
+ return -1;
+ else if (f1 && f2)
+ {
+ int frames = btp_frame_cmp(f1, f2, true);
+ if (frames != 0)
+ return frames;
+ f1 = f1->next;
+ f2 = f2->next;
+ }
+ } while (f1 || f2);
+
+ return 0;
+}
+
+struct btp_thread *
+btp_thread_add_sibling(struct btp_thread *a, struct btp_thread *b)
+{
+ struct btp_thread *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+int
+btp_thread_get_frame_count(struct btp_thread *thread)
+{
+ struct btp_frame *frame = thread->frames;
+ int count = 0;
+ while (frame)
+ {
+ frame = frame->next;
+ ++count;
+ }
+ return count;
+}
+
+void
+btp_thread_quality_counts(struct btp_thread *thread,
+ int *ok_count,
+ int *all_count)
+{
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ *all_count += 1;
+ if (frame->signal_handler_called ||
+ (frame->function_name
+ && 0 != strcmp(frame->function_name, "??")))
+ {
+ *ok_count += 1;
+ }
+ frame = frame->next;
+ }
+}
+
+float
+btp_thread_quality(struct btp_thread *thread)
+{
+ int ok_count = 0, all_count = 0;
+ btp_thread_quality_counts(thread, &ok_count, &all_count);
+ if (0 == all_count)
+ return 1;
+ return ok_count/(float)all_count;
+}
+
+bool
+btp_thread_remove_frame(struct btp_thread *thread,
+ struct btp_frame *frame)
+{
+ struct btp_frame *loop_frame = thread->frames, *prev_frame = NULL;
+ while (loop_frame)
+ {
+ if (loop_frame == frame)
+ {
+ if (prev_frame)
+ prev_frame->next = loop_frame->next;
+ else
+ thread->frames = loop_frame->next;
+
+ btp_frame_free(loop_frame);
+ return true;
+ }
+ prev_frame = loop_frame;
+ loop_frame = loop_frame->next;
+ }
+ return false;
+}
+
+bool
+btp_thread_remove_frames_above(struct btp_thread *thread,
+ struct btp_frame *frame)
+{
+ /* Check that the frame is present in the thread. */
+ struct btp_frame *loop_frame = thread->frames;
+ while (loop_frame)
+ {
+ if (loop_frame == frame)
+ break;
+ loop_frame = loop_frame->next;
+ }
+
+ if (!loop_frame)
+ return false;
+
+ /* Delete all the frames up to the frame. */
+ while (thread->frames != frame)
+ {
+ loop_frame = thread->frames->next;
+ btp_frame_free(thread->frames);
+ thread->frames = loop_frame;
+ }
+
+ return true;
+}
+
+void
+btp_thread_remove_frames_below_n(struct btp_thread *thread,
+ int n)
+{
+ assert(n >= 0);
+
+ /* Skip some frames to get the required stack depth. */
+ int i = n;
+ struct btp_frame *frame = thread->frames, *last_frame = NULL;
+ while (frame && i)
+ {
+ last_frame = frame;
+ frame = frame->next;
+ --i;
+ }
+
+ /* Delete the remaining frames. */
+ if (last_frame)
+ last_frame->next = NULL;
+ else
+ thread->frames = NULL;
+
+ while (frame)
+ {
+ struct btp_frame *delete_frame = frame;
+ frame = frame->next;
+ btp_frame_free(delete_frame);
+ }
+}
+
+void
+btp_thread_append_to_str(struct btp_thread *thread,
+ struct strbuf *str,
+ bool verbose)
+{
+ int framecount = btp_thread_get_frame_count(thread);
+ if (verbose)
+ {
+ strbuf_append_strf(str, "Thread no. %d (%d frames)\n",
+ thread->number, framecount);
+ }
+ else
+ strbuf_append_str(str, "Thread\n");
+
+ struct btp_frame *frame = thread->frames;
+ while (frame)
+ {
+ btp_frame_append_to_str(frame, str, verbose);
+ frame = frame->next;
+ }
+}
+
+struct btp_thread *
+btp_thread_parse(char **input,
+ struct btp_location *location)
+{
+ char *local_input = *input;
+
+ /* Read the Thread keyword, which is mandatory. */
+ int chars = btp_skip_string(&local_input, "Thread");
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "\"Thread\" header expected";
+ return NULL;
+ }
+
+ /* Skip spaces, at least one space is mandatory. */
+ int spaces = btp_skip_char_sequence(&local_input, ' ');
+ location->column += spaces;
+ if (0 == spaces)
+ {
+ location->message = "Space expected after the \"Thread\" keyword.";
+ return NULL;
+ }
+
+ /* Read thread number. */
+ struct btp_thread *imthread = btp_thread_new();
+ int digits = btp_parse_unsigned_integer(&local_input, &imthread->number);
+ location->column += digits;
+ if (0 == digits)
+ {
+ location->message = "Thread number expected.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
+
+ /* Skip spaces after the thread number and before the parenthesis. */
+ spaces = btp_skip_char_sequence(&local_input, ' ');
+ location->column += spaces;
+ if (0 == spaces)
+ {
+ location->message = "Space expected after the thread number.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
+
+ /* Read the Thread keyword in parenthesis, which is mandatory. */
+ chars = btp_skip_string(&local_input, "(Thread ");
+ location->column += chars;
+ if (0 == chars)
+ {
+ location->message = "Thread keyword in the parenthesis expected in the form '(Thread '.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
+
+ /* Read the thread identification number. */
+ digits = btp_skip_unsigned_integer(&local_input);
+ location->column += digits;
+ if (0 == digits)
+ {
+ location->message = "The thread identification number expected.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
+
+ /* Read the end of the parenthesis. */
+ chars = btp_skip_string(&local_input, "):\n");
+ if (0 == chars)
+ {
+ location->message = "The end of the parenthesis expected in the form of '):\\n'.";
+ btp_thread_free(imthread);
+ return NULL;
+ }
+ /* Add the newline from the last btp_skip_string. */
+ btp_location_add(location, 2, 0);
+
+ /* Read the frames. */
+ struct btp_frame *frame, *prevframe = NULL;
+ struct btp_location frame_location;
+ btp_location_init(&frame_location);
+ while ((frame = btp_frame_parse(&local_input, &frame_location)))
+ {
+ if (prevframe)
+ {
+ btp_frame_add_sibling(prevframe, frame);
+ prevframe = frame;
+ }
+ else
+ imthread->frames = prevframe = frame;
+
+ btp_location_add(location,
+ frame_location.line,
+ frame_location.column);
+ }
+ if (!imthread->frames)
+ {
+ location->message = frame_location.message;
+ btp_thread_free(imthread);
+ return NULL;
+ }
+
+ *input = local_input;
+ return imthread;
+}
diff --git a/src/btparser/thread.h b/src/btparser/thread.h
new file mode 100644
index 00000000..f7287385
--- /dev/null
+++ b/src/btparser/thread.h
@@ -0,0 +1,204 @@
+/*
+ thread.h
+
+ Copyright (C) 2010 Red Hat, 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 BTPARSER_THREAD_H
+#define BTPARSER_THREAD_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct btp_frame;
+struct strbuf;
+struct btp_location;
+
+/**
+ * Represents a thread containing frames.
+ */
+struct btp_thread
+{
+ unsigned number;
+ /**
+ * Thread's frames, starting from the top of the stack.
+ */
+ struct btp_frame *frames;
+ /**
+ * A sibling thread, or NULL if this is the last thread in a backtrace.
+ */
+ struct btp_thread *next;
+};
+
+/**
+ * Creates and initializes a new frame structure.
+ * @returns
+ * It never returns NULL. The returned pointer must be released by
+ * calling the function btp_thread_free().
+ */
+struct btp_thread *
+btp_thread_new();
+
+/**
+ * Initializes all members of the thread to default values.
+ * No memory is released, members are simply overwritten.
+ * This is useful for initializing a thread structure placed on the
+ * stack.
+ */
+void
+btp_thread_init(struct btp_thread *thread);
+
+/**
+ * Releases the memory held by the thread. The thread siblings are not
+ * released.
+ * @param thread
+ * If thread is NULL, no operation is performed.
+ */
+void
+btp_thread_free(struct btp_thread *thread);
+
+/**
+ * Creates a duplicate of the thread.
+ * @param thread
+ * It must be non-NULL pointer. The thread is not modified by calling
+ * this function.
+ * @param siblings
+ * Whether to duplicate also siblings referenced by thread->next. If
+ * false, thread->next is not duplicated for the new frame, but it is
+ * set to NULL.
+ */
+struct btp_thread *
+btp_thread_dup(struct btp_thread *thread,
+ bool siblings);
+
+/**
+ * Compares two threads. When comparing the threads, it compares also
+ * their frames, including the frame numbers.
+ * @returns
+ * Returns 0 if the threads are same. Returns negative number if t1
+ * is found to be 'less' than t2. Returns positive number if t1 is
+ * found to be 'greater' than t2.
+ */
+int
+btp_thread_cmp(struct btp_thread *t1, struct btp_thread *t2);
+
+/**
+ * Puts the thread 'b' to the bottom of the stack 'a'. In other words,
+ * it finds the last sibling of the thread 'a', and appends the thread
+ * 'b' to this last sibling.
+ */
+struct btp_thread *
+btp_thread_add_sibling(struct btp_thread *a, struct btp_thread *b);
+
+/**
+ * Returns the number of frames of the thread.
+ */
+int
+btp_thread_get_frame_count(struct btp_thread *thread);
+
+/**
+ * Counts the number of 'good' frames and the number of all frames in
+ * a thread. Good means that the function name is known (so it's not
+ * just '??').
+ * @param ok_count
+ * @param all_count
+ * Not zeroed. This function just adds the numbers to ok_count and
+ * all_count.
+ */
+void
+btp_thread_quality_counts(struct btp_thread *thread,
+ int *ok_count,
+ int *all_count);
+
+/**
+ * Returns the quality of the thread. The quality is the ratio of the
+ * number of frames with function name fully known to the number of
+ * all frames. This function does not take into account that some
+ * frames are more important than others.
+ * @param thread
+ * Must be a non-NULL pointer. It's not modified in this function.
+ * @returns
+ * A number between 0 and 1. 0 means the lowest quality, 1 means full
+ * thread backtrace is known. If the thread contains no frames, this
+ * function returns 1.
+ */
+float
+btp_thread_quality(struct btp_thread *thread);
+
+/**
+ * Removes the frame from the thread and then deletes it.
+ * @returns
+ * True if the frame was found in the thread and removed and deleted.
+ * False if the frame was not found in the thread.
+ */
+bool
+btp_thread_remove_frame(struct btp_thread *thread,
+ struct btp_frame *frame);
+
+/**
+ * Removes all the frames from the thread that are above certain
+ * frame.
+ * @returns
+ * True if the frame was found, and all the frames that were above the
+ * frame in the thread were removed from the thread and then deleted.
+ * False if the frame was not found in the thread.
+ */
+bool
+btp_thread_remove_frames_above(struct btp_thread *thread,
+ struct btp_frame *frame);
+
+/**
+ * Keeps only the top n frames in the thread.
+ */
+void
+btp_thread_remove_frames_below_n(struct btp_thread *thread,
+ int n);
+
+/**
+ * Appends a textual representation of 'thread' to the 'str'.
+ */
+void
+btp_thread_append_to_str(struct btp_thread *thread,
+ struct strbuf *str,
+ bool verbose);
+
+/**
+ * If the input contains proper thread with frames, parse the thread,
+ * move the input pointer after the thread, and return a structure
+ * representing the thread. Otherwise to not modify the input pointer
+ * and return NULL.
+ * @param location
+ * The caller must provide a pointer to struct btp_location here. The
+ * line and column members are gradually increased as the parser
+ * handles the input, keep this in mind to get reasonable values.
+ * When this function returns NULL (an error occurred), the structure
+ * will contain the error line, column, and message.
+ * @returns
+ * NULL or newly allocated structure, which should be released by
+ * calling btp_thread_free().
+ */
+struct btp_thread *
+btp_thread_parse(char **input,
+ struct btp_location *location);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/btparser/utils.c b/src/btparser/utils.c
new file mode 100644
index 00000000..1de329a7
--- /dev/null
+++ b/src/btparser/utils.c
@@ -0,0 +1,423 @@
+/*
+ utils.c
+
+ Copyright (C) 2010 Red Hat, 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 "utils.h"
+#include "location.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+bool btp_debug_parser = false;
+
+void *
+btp_malloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (ptr == NULL)
+ {
+ fprintf(stderr, "btp: out of memory");
+ exit(1);
+ }
+ return ptr;
+}
+
+char *
+btp_vasprintf(const char *format, va_list p)
+{
+ int r;
+ char *string_ptr;
+
+#if 1
+ // GNU extension
+ r = vasprintf(&string_ptr, format, p);
+#else
+ // Bloat for systems that haven't got the GNU extension.
+ va_list p2;
+ va_copy(p2, p);
+ r = vsnprintf(NULL, 0, format, p);
+ string_ptr = xmalloc(r+1);
+ r = vsnprintf(string_ptr, r+1, format, p2);
+ va_end(p2);
+#endif
+
+ if (r < 0)
+ {
+ fprintf(stderr, "btp: out of memory");
+ exit(1);
+ }
+
+ return string_ptr;
+}
+
+char *
+btp_strdup(const char *s)
+{
+ return btp_strndup(s, strlen(s));
+}
+
+char *
+btp_strndup(const char *s, size_t n)
+{
+ char *result = strndup(s, n);
+ if (result == NULL)
+ {
+ fprintf(stderr, "btp: out of memory");
+ exit(1);
+ }
+ return result;
+}
+
+int
+btp_strcmp0(const char *s1, const char *s2)
+{
+ if (!s1)
+ {
+ if (s2)
+ return -1;
+ return 0;
+ }
+ else
+ {
+ if (!s2)
+ return 1;
+ /* Both are non-null. */
+ return strcmp(s1, s2);
+ }
+}
+
+char *
+btp_strchr_location(const char *s, int c, int *line, int *column)
+{
+ *line = 1;
+ *column = 0;
+
+ /* Scan s for the character. When this loop is finished,
+ s will either point to the end of the string or the
+ character we were looking for. */
+ while (*s != '\0' && *s != (char)c)
+ {
+ btp_location_eat_char_ext(line, column, *s);
+ ++s;
+ }
+ return ((*s == c) ? (char*)s : NULL);
+}
+
+char *
+btp_strstr_location(const char *haystack,
+ const char *needle,
+ int *line,
+ int *column)
+{
+ *line = 1;
+ *column = 0;
+ size_t needlelen;
+
+ /* Check for the null needle case. */
+ if (*needle == '\0')
+ return (char*)haystack;
+
+ needlelen = strlen(needle);
+ int chrline, chrcolumn;
+ for (;(haystack = btp_strchr_location(haystack, *needle, &chrline, &chrcolumn)) != NULL; ++haystack)
+ {
+ btp_location_add_ext(line, column, chrline, chrcolumn);
+
+ if (strncmp(haystack, needle, needlelen) == 0)
+ return (char*)haystack;
+
+ btp_location_eat_char_ext(line, column, *haystack);
+ }
+ return NULL;
+}
+
+size_t
+btp_strspn_location(const char *s,
+ const char *accept,
+ int *line,
+ int *column)
+{
+ *line = 1;
+ *column = 0;
+ const char *sc;
+ for (sc = s; *sc != '\0'; ++sc)
+ {
+ if (strchr(accept, *sc) == NULL)
+ return (sc - s);
+
+ btp_location_eat_char_ext(line, column, *sc);
+ }
+ return sc - s; /* terminating nulls don't match */
+}
+
+char *
+btp_file_to_string(const char *filename)
+{
+ /* Open input file, and parse it. */
+ int fd = open(filename, O_RDONLY | O_LARGEFILE);
+ if (fd < 0)
+ {
+ fprintf(stderr, "Unable to open '%s': %s.\n",
+ filename, strerror(errno));
+ return NULL;
+ }
+
+ off_t size = lseek(fd, 0, SEEK_END);
+ if (size < 0) /* EOVERFLOW? */
+ {
+ fprintf(stderr, "Unable to seek in '%s': %s.\n",
+ filename, strerror(errno));
+ }
+
+ lseek(fd, 0, SEEK_SET); /* No reason to fail. */
+
+ static const size_t FILE_SIZE_LIMIT = 20000000; /* ~ 20 MB */
+ if (size > FILE_SIZE_LIMIT)
+ {
+ fprintf(stderr, "Input file too big (%lld). Maximum size is %zd.\n",
+ (long long)size, FILE_SIZE_LIMIT);
+ close(fd);
+ return NULL;
+ }
+
+ char *contents = btp_malloc(size + 1);
+ if (size != read(fd, contents, size))
+ {
+ fprintf(stderr, "Unable to read from '%s'.\n", filename);
+ close(fd);
+ free(contents);
+ return NULL;
+ }
+
+ /* Just reading, so no need to check the returned value. */
+ close(fd);
+
+ contents[size] = '\0';
+ return contents;
+}
+
+bool
+btp_skip_char(char **input, char c)
+{
+ if (**input != c)
+ return false;
+ ++*input;
+ return true;
+}
+
+bool
+btp_skip_char_limited(char **input, const char *allowed)
+{
+ if (strchr(allowed, **input) == NULL)
+ return false;
+ ++*input;
+ return true;
+}
+
+bool
+btp_parse_char_limited(char **input, const char *allowed, char *result)
+{
+ if (**input == '\0')
+ return false;
+ if (strchr(allowed, **input) == NULL)
+ return false;
+ *result = **input;
+ ++*input;
+ return true;
+}
+
+int
+btp_skip_char_sequence(char **input, char c)
+{
+ int count = 0;
+
+ /* Skip all the occurences of c. */
+ while (btp_skip_char(input, c))
+ ++count;
+
+ return count;
+}
+
+int
+btp_skip_char_span(char **input, const char *chars)
+{
+ size_t count = strspn(*input, chars);
+ if (0 == count)
+ return count;
+ *input += count;
+ return count;
+}
+
+int
+btp_skip_char_span_location(char **input,
+ const char *chars,
+ int *line,
+ int *column)
+{
+ size_t count = btp_strspn_location(*input, chars, line, column);
+ if (0 == count)
+ return count;
+ *input += count;
+ return count;
+}
+
+int
+btp_parse_char_span(char **input, const char *accept, char **result)
+{
+ size_t count = strspn(*input, accept);
+ if (count == 0)
+ return 0;
+ *result = btp_strndup(*input, count);
+ *input += count;
+ return count;
+}
+
+bool
+btp_parse_char_cspan(char **input, const char *reject, char **result)
+{
+ size_t count = strcspn(*input, reject);
+ if (count == 0)
+ return false;
+ *result = btp_strndup(*input, count);
+ *input += count;
+ return true;
+}
+
+int
+btp_skip_string(char **input, const char *string)
+{
+ char *local_input = *input;
+ const char *local_string = string;
+ while (*local_string && *local_input && *local_input == *local_string)
+ {
+ ++local_input;
+ ++local_string;
+ }
+ if (*local_string != '\0')
+ return 0;
+ int count = local_input - *input;
+ *input = local_input;
+ return count;
+}
+
+bool
+btp_parse_string(char **input, const char *string, char **result)
+{
+ char *local_input = *input;
+ const char *local_string = string;
+ while (*local_string && *local_input && *local_input == *local_string)
+ {
+ ++local_input;
+ ++local_string;
+ }
+ if (*local_string != '\0')
+ return false;
+ *result = btp_strndup(string, local_input - *input);
+ *input = local_input;
+ return true;
+}
+
+char
+btp_parse_digit(char **input)
+{
+ char digit = **input;
+ if (digit < '0' || digit > '9')
+ return '\0';
+ ++*input;
+ return digit;
+}
+
+int
+btp_skip_unsigned_integer(char **input)
+{
+ return btp_skip_char_span(input, "0123456789");
+}
+
+int
+btp_parse_unsigned_integer(char **input, unsigned *result)
+{
+ char *local_input = *input;
+ char *numstr;
+ int length = btp_parse_char_span(&local_input,
+ "0123456789",
+ &numstr);
+ if (0 == length)
+ return 0;
+
+ char *endptr;
+ errno = 0;
+ unsigned long r = strtoul(numstr, &endptr, 10);
+ bool failure = (errno || numstr == endptr || *endptr != '\0'
+ || r > UINT_MAX);
+ free(numstr);
+ if (failure) /* number too big or some other error */
+ return 0;
+ *result = r;
+ *input = local_input;
+ return length;
+}
+
+int
+btp_skip_hexadecimal_number(char **input)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '0'))
+ return 0;
+ if (!btp_skip_char(&local_input, 'x'))
+ return 0;
+ int count = 2;
+ count += btp_skip_char_span(&local_input, "abcdef0123456789");
+ if (2 == count) /* btp_skip_char_span returned 0 */
+ return 0;
+ *input = local_input;
+ return count;
+}
+
+int
+btp_parse_hexadecimal_number(char **input, uint64_t *result)
+{
+ char *local_input = *input;
+ if (!btp_skip_char(&local_input, '0'))
+ return 0;
+ if (!btp_skip_char(&local_input, 'x'))
+ return 0;
+ int count = 2;
+ char *numstr;
+ count += btp_parse_char_span(&local_input,
+ "abcdef0123456789",
+ &numstr);
+
+ if (2 == count) /* btp_parse_char_span returned 0 */
+ return 0;
+ char *endptr;
+ errno = 0;
+ unsigned long long r = strtoull(numstr, &endptr, 16);
+ bool failure = (errno || numstr == endptr || *endptr != '\0');
+ free(numstr);
+ if (failure) /* number too big or some other error */
+ return 0;
+ *result = r;
+ *input = local_input;
+ return count;
+}
diff --git a/src/btparser/utils.h b/src/btparser/utils.h
new file mode 100644
index 00000000..680e67e0
--- /dev/null
+++ b/src/btparser/utils.h
@@ -0,0 +1,284 @@
+/*
+ utils.h
+
+ Copyright (C) 2010 Red Hat, 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 BTPARSER_UTILS_H
+#define BTPARSER_UTILS_H
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BTP_lower "abcdefghijklmnopqrstuvwxyz"
+#define BTP_upper "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define BTP_alpha BTP_lower BTP_upper
+#define BTP_space " \t\r\n\v\f"
+#define BTP_digit "0123456789"
+#define BTP_alnum BTP_alpha BTP_digit
+
+/**
+ * Debugging output to stdout while parsing.
+ * Default value is false.
+ */
+extern bool
+btp_debug_parser;
+
+/**
+ * Never returns NULL.
+ */
+void *
+btp_malloc(size_t size);
+
+/**
+ * Never returns NULL.
+ */
+char *
+btp_vasprintf(const char *format, va_list p);
+
+/**
+ * Never returns NULL.
+ */
+char *
+btp_strdup(const char *s);
+
+/**
+ * Never returns NULL.
+ */
+char *
+btp_strndup(const char *s, size_t n);
+
+/**
+ * A strcmp() variant that works also with NULL parameters. NULL is
+ * considered to be less than a string.
+ */
+int
+btp_strcmp0(const char *s1, const char *s2);
+
+/**
+ * A strchr() variant providing line and column in the string s
+ * indicating where the char c was found.
+ * @param line
+ * Starts from 1. Its value is valid only when this function does not
+ * return NULL.
+ * @param column
+ * Starts from 0. Its value is valid only when this function does not
+ * return NULL.
+ */
+char *
+btp_strchr_location(const char *s, int c, int *line, int *column);
+
+/**
+ * A strstr() variant providing line and column of the haystick
+ * indicating where the needle was found.
+ * @param line
+ * Starts from 1. Its value is valid only when this function does not
+ * return NULL.
+ * @param column
+ * Starts from 0. Its value is valid only when this function does not
+ * return NULL.
+ */
+char *
+btp_strstr_location(const char *haystack,
+ const char *needle,
+ int *line,
+ int *column);
+
+/**
+ * A strspn() variant providing line and column of the string s which
+ * corresponds to the returned length.
+ * @param line
+ * Starts from 1.
+ * @param column
+ * Starts from 0.
+ */
+size_t
+btp_strspn_location(const char *s,
+ const char *accept,
+ int *line,
+ int *column);
+
+/**
+ * Loads file contents to a string.
+ * @returns
+ * File contents. If file opening/reading fails, NULL is returned.
+ */
+char *
+btp_file_to_string(const char *filename);
+
+/**
+ * If the input contains character c in the current positon, move the
+ * input pointer after the character, and return true. Otherwise do
+ * not modify the input and return false.
+ */
+bool
+btp_skip_char(char **input, char c);
+
+/**
+ * If the input contains one of allowed characters, move
+ * the input pointer after that character, and return true.
+ * Otherwise do not modify the input and return false.
+ */
+bool
+btp_skip_char_limited(char **input, const char *allowed);
+
+/**
+ * If the input contains one of allowed characters, store
+ * the character to the result, move the input pointer after
+ * that character, and return true. Otherwise do not modify
+ * the input and return false.
+ */
+bool
+btp_parse_char_limited(char **input, const char *allowed, char *result);
+
+/**
+ * If the input contains the character c one or more times, update it
+ * so that the characters are skipped. Returns the number of characters
+ * skipped, thus zero if **input does not contain c.
+ */
+int
+btp_skip_char_sequence(char **input, char c);
+
+/**
+ * If the input contains one or more characters from string chars,
+ * move the input pointer after the sequence. Otherwise do not modify
+ * the input.
+ * @returns
+ * The number of characters skipped.
+ */
+int
+btp_skip_char_span(char **input, const char *chars);
+
+/**
+ * If the input contains one or more characters from string chars,
+ * move the input pointer after the sequence. Otherwise do not modify
+ * the input.
+ * @param line
+ * Starts from 1. Corresponds to the returned number.
+ * @param column
+ * Starts from 0. Corresponds to the returned number.
+ * @returns
+ * The number of characters skipped.
+ */
+int
+btp_skip_char_span_location(char **input,
+ const char *chars,
+ int *line,
+ int *column);
+
+/**
+ * If the input contains one or more characters from string accept,
+ * create a string from this sequence and store it to the result, move
+ * the input pointer after the sequence, and return the lenght of the
+ * sequence. Otherwise do not modify the input and return 0.
+ *
+ * If this function returns nonzero value, the caller is responsible
+ * to free the result.
+ */
+int
+btp_parse_char_span(char **input, const char *accept, char **result);
+
+/**
+ * If the input contains characters which are not in string reject,
+ * create a string from this sequence and store it to the result,
+ * move the input pointer after the sequence, and return true.
+ * Otherwise do not modify the input and return false.
+ *
+ * If this function returns true, the caller is responsible to
+ * free the result.
+ */
+bool
+btp_parse_char_cspan(char **input, const char *reject, char **result);
+
+/**
+ * If the input contains the string, move the input pointer after
+ * the sequence. Otherwise do not modify the input.
+ * @returns
+ * Number of characters skipped. 0 if the input does not contain the
+ * string.
+ */
+int
+btp_skip_string(char **input, const char *string);
+
+/**
+ * If the input contains the string, copy the string to result,
+ * move the input pointer after the string, and return true.
+ * Otherwise do not modify the input and return false.
+ *
+ * If this function returns true, the caller is responsible to free
+ * the result.
+ */
+bool
+btp_parse_string(char **input, const char *string, char **result);
+
+/**
+ * If the input contains digit 0-9, return it as a character
+ * and move the input pointer after it. Otherwise return
+ * '\0' and do not modify the input.
+ */
+char
+btp_parse_digit(char **input);
+
+/**
+ * If the input contains [0-9]+, move the input pointer
+ * after the number.
+ * @returns
+ * The number of skipped characters. 0 if input does not start with a
+ * digit.
+ */
+int
+btp_skip_unsigned_integer(char **input);
+
+/**
+ * If the input contains [0-9]+, parse it, move the input pointer
+ * after the number.
+ * @returns
+ * Number of parsed characters. 0 if input does not contain a number.
+ */
+int
+btp_parse_unsigned_integer(char **input, unsigned *result);
+
+/**
+ * If the input contains 0x[0-9a-f]+, move the input pointer
+ * after that.
+ * @returns
+ * The number of characters processed from input. 0 if the input does
+ * not contain a hexadecimal number.
+ */
+int
+btp_skip_hexadecimal_number(char **input);
+
+/**
+ * If the input contains 0x[0-9a-f]+, parse the number, and move the
+ * input pointer after it. Otherwise do not modify the input.
+ * @returns
+ * The number of characters read from input. 0 if the input does not
+ * contain a hexadecimal number.
+ */
+int
+btp_parse_hexadecimal_number(char **input, uint64_t *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp
index b8c23edd..d7ff5781 100644
--- a/src/daemon/Daemon.cpp
+++ b/src/daemon/Daemon.cpp
@@ -898,6 +898,13 @@ int main(int argc, char** argv)
if (opts & OPT_s)
start_syslog_logging();
+ /* When dbus daemon starts us, it doesn't set PATH
+ * (I saw it set only DBUS_STARTER_ADDRESS and DBUS_STARTER_BUS_TYPE).
+ * In this case, set something sane:
+ */
+ if (!getenv("PATH"))
+ putenv((char*)"PATH=/usr/sbin:/usr/bin:/sbin:/bin");
+
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
msg_prefix = "abrtd"; /* for log(), error_msg() and such */
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 177d4f37..3d307128 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -4,6 +4,7 @@ bin_SCRIPTS = \
sbin_PROGRAMS = abrtd \
abrt-server \
+ abrt-action-analyze-c \
abrt-action-generate-backtrace \
abrt-action-save-package-data
@@ -58,6 +59,24 @@ abrt_server_CPPFLAGS = \
abrt_server_LDADD = \
../../lib/utils/libABRTUtils.la
+abrt_action_analyze_c_SOURCES = \
+ abrt-action-analyze-c.c
+abrt_action_analyze_c_CPPFLAGS = \
+ -I$(srcdir)/../../inc \
+ -I$(srcdir)/../../lib/utils \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -D_GNU_SOURCE \
+ -Wall -Werror
+abrt_action_analyze_c_LDADD = \
+ ../../lib/utils/libABRTUtils.la
+
abrt_action_generate_backtrace_SOURCES = \
abrt-action-generate-backtrace.c
abrt_action_generate_backtrace_CPPFLAGS = \
@@ -74,7 +93,8 @@ abrt_action_generate_backtrace_CPPFLAGS = \
-D_GNU_SOURCE \
-Wall -Werror
abrt_action_generate_backtrace_LDADD = \
- ../../lib/utils/libABRTUtils.la
+ ../../lib/utils/libABRTUtils.la \
+ ../btparser/libbtparser.la
abrt_action_save_package_data_SOURCES = \
rpm.h rpm.c \
diff --git a/src/daemon/MiddleWare.cpp b/src/daemon/MiddleWare.cpp
index f499f3b7..9e7f96ab 100644
--- a/src/daemon/MiddleWare.cpp
+++ b/src/daemon/MiddleWare.cpp
@@ -64,8 +64,8 @@ static bool DebugDumpToCrashReport(const char *pDebugDumpDir, map_crash_data_t&
{
VERB3 log(" DebugDumpToCrashReport('%s')", pDebugDumpDir);
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, pDebugDumpDir, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(pDebugDumpDir, /*flags:*/ 0);
+ if (!dd)
return false;
const char *const *v = must_have_files;
@@ -177,8 +177,8 @@ mw_result_t CreateCrashReport(const char *crash_id,
mw_result_t r = MW_OK;
try
{
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, row->db_dump_dir, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(row->db_dump_dir, /*flags:*/ 0);
+ if (!dd)
{
db_row_free(row);
return MW_ERROR;
@@ -333,8 +333,8 @@ report_status_t Report(const map_crash_data_t& client_report,
const char *backtrace = get_crash_data_item_content_or_NULL(client_report, FILENAME_BACKTRACE);
if (comment || reproduce || backtrace)
{
- struct dump_dir *dd = dd_init();
- if (dd_opendir(dd, pDumpDir.c_str(), 0))
+ struct dump_dir *dd = dd_opendir(pDumpDir.c_str(), /*flags:*/ 0);
+ if (dd)
{
if (comment)
{
@@ -351,8 +351,8 @@ report_status_t Report(const map_crash_data_t& client_report,
dd_save_text(dd, FILENAME_BACKTRACE, backtrace);
add_to_crash_data_ext(stored_report, FILENAME_BACKTRACE, CD_TXT, CD_ISEDITABLE, backtrace);
}
+ dd_close(dd);
}
- dd_close(dd);
}
/* Remove BIN filenames from stored_report if they are not present in client's data */
@@ -700,8 +700,8 @@ mw_result_t SaveDebugDump(const char *pDebugDumpDir,
{
mw_result_t res;
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, pDebugDumpDir, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(pDebugDumpDir, /*flags:*/ 0);
+ if (!dd)
return MW_ERROR;
char *time = dd_load_text(dd, FILENAME_TIME);
@@ -770,8 +770,8 @@ mw_result_t FillCrashInfo(const char *crash_id,
if (!row)
return MW_ERROR;
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, row->db_dump_dir, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(row->db_dump_dir, /*flags:*/ 0);
+ if (!dd)
{
db_row_free(row);
return MW_ERROR;
diff --git a/src/daemon/abrt-action-analyze-c.c b/src/daemon/abrt-action-analyze-c.c
new file mode 100644
index 00000000..de454daf
--- /dev/null
+++ b/src/daemon/abrt-action-analyze-c.c
@@ -0,0 +1,238 @@
+/*
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ 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 "abrtlib.h"
+#include "parse_options.h"
+
+#define PROGNAME "abrt-action-analyze-c"
+
+static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput)
+{
+ unsigned len;
+ unsigned char hash2[SHA1_RESULT_LEN];
+ sha1_ctx_t sha1ctx;
+
+ sha1_begin(&sha1ctx);
+ sha1_hash(pInput, strlen(pInput), &sha1ctx);
+ sha1_end(hash2, &sha1ctx);
+ len = SHA1_RESULT_LEN;
+
+ char *d = hash_str;
+ unsigned char *s = hash2;
+ while (len)
+ {
+ *d++ = "0123456789abcdef"[*s >> 4];
+ *d++ = "0123456789abcdef"[*s & 0xf];
+ s++;
+ len--;
+ }
+ *d = '\0';
+ //log("hash2:%s str:'%s'", hash_str, pInput);
+}
+
+static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec)
+{
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ return NULL;
+ char *uid_str = dd_load_text(dd, CD_UID);
+ dd_close(dd);
+ unsigned uid = xatoi_u(uid_str);
+ free(uid_str);
+
+ int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETGUID | EXECFLG_SETSID | EXECFLG_QUIET;
+ VERB1 flags &= ~EXECFLG_QUIET;
+ int pipeout[2];
+ char* args[4];
+ args[0] = (char*)"eu-unstrip";
+ args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, dump_dir_name);
+ args[2] = (char*)"-n";
+ args[3] = NULL;
+ pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*unsetenv_vec:*/ NULL, /*dir:*/ NULL, uid);
+ free(args[1]);
+
+ /* Bugs in unstrip or corrupted coredumps can cause it to enter infinite loop.
+ * Therefore we have a (largish) timeout, after which we kill the child.
+ */
+ int t = time(NULL); /* int is enough, no need to use time_t */
+ int endtime = t + timeout_sec;
+ struct strbuf *buf_out = strbuf_new();
+ while (1)
+ {
+ int timeout = endtime - t;
+ if (timeout < 0)
+ {
+ kill(child, SIGKILL);
+ strbuf_append_strf(buf_out, "\nTimeout exceeded: %u seconds, killing %s\n", timeout_sec, args[0]);
+ break;
+ }
+
+ /* We don't check poll result - checking read result is enough */
+ struct pollfd pfd;
+ pfd.fd = pipeout[0];
+ pfd.events = POLLIN;
+ poll(&pfd, 1, timeout * 1000);
+
+ char buff[1024];
+ int r = read(pipeout[0], buff, sizeof(buff) - 1);
+ if (r <= 0)
+ break;
+ buff[r] = '\0';
+ strbuf_append_str(buf_out, buff);
+ t = time(NULL);
+ }
+ close(pipeout[0]);
+
+ /* Prevent having zombie child process */
+ int status;
+ waitpid(child, &status, 0);
+
+ if (status != 0)
+ {
+ /* unstrip didnt exit with exitcode 0 */
+ strbuf_free(buf_out);
+ return NULL;
+ }
+
+ return strbuf_free_nobuf(buf_out);
+}
+
+static void trim_unstrip_output(char *result, const char *unstrip_n_output)
+{
+ // lines look like this:
+ // 0x400000+0x209000 23c77451cf6adff77fc1f5ee2a01d75de6511dda@0x40024c - - [exe]
+ // 0x400000+0x209000 ab3c8286aac6c043fd1bb1cc2a0b88ec29517d3e@0x40024c /bin/sleep /usr/lib/debug/bin/sleep.debug [exe]
+ // 0x7fff313ff000+0x1000 389c7475e3d5401c55953a425a2042ef62c4c7df@0x7fff313ff2f8 . - linux-vdso.so.1
+ // ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ // we drop everything except the marked part ^
+
+ char *dst = result;
+ const char *line = unstrip_n_output;
+ while (*line)
+ {
+ const char *eol = strchrnul(line, '\n');
+ const char *plus = (char*)memchr(line, '+', eol - line);
+ if (plus)
+ {
+ while (++plus < eol && *plus != '@')
+ {
+ if (!isspace(*plus))
+ {
+ *dst++ = *plus;
+ }
+ }
+ }
+ if (*eol != '\n') break;
+ line = eol + 1;
+ }
+ *dst = '\0';
+}
+
+int main(int argc, char **argv)
+{
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ PROGNAME" [-vs] -d DIR\n\n"
+ "Calculates and saves UUID of coredumps"
+ );
+ const char *dump_dir_name = ".";
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_s = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Crash dump directory")),
+ OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+
+//Maybe we will want this... later
+// msg_prefix = xasprintf(PROGNAME"[%u]", getpid());
+// if (opts & OPT_s)
+// {
+// openlog(msg_prefix, 0, LOG_DAEMON);
+// logmode = LOGMODE_SYSLOG;
+// }
+
+ /* Run unstrip -n and trim its output, leaving only sizes and build ids */
+
+ char *unstrip_n_output = run_unstrip_n(dump_dir_name, /*timeout_sec:*/ 30);
+ if (!unstrip_n_output)
+ return 1; /* bad dump_dir_name, can't run unstrip, etc... */
+ /* modifies unstrip_n_output in-place: */
+ trim_unstrip_output(unstrip_n_output, unstrip_n_output);
+
+ /* Hash package + executable + unstrip_n_output and save it as UUID */
+
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ return 1;
+
+ char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
+ char *package = dd_load_text(dd, FILENAME_PACKAGE);
+ /* Package variable has "firefox-3.5.6-1.fc11[.1]" format */
+ /* Remove distro suffix and maybe least significant version number */
+ char *p = package;
+ while (*p)
+ {
+ if (*p == '.' && (p[1] < '0' || p[1] > '9'))
+ {
+ /* We found "XXXX.nondigitXXXX", trim this part */
+ *p = '\0';
+ break;
+ }
+ p++;
+ }
+ char *first_dot = strchr(package, '.');
+ if (first_dot)
+ {
+ char *last_dot = strrchr(first_dot, '.');
+ if (last_dot != first_dot)
+ {
+ /* There are more than one dot: "1.2.3"
+ * Strip last part, we don't want to distinquish crashes
+ * in packages which differ only by minor release number.
+ */
+ *last_dot = '\0';
+ }
+ }
+
+ char *string_to_hash = xasprintf("%s%s%s", package, executable, unstrip_n_output);
+ /*free(package);*/
+ /*free(executable);*/
+ /*free(unstrip_n_output);*/
+
+ char hash_str[SHA1_RESULT_LEN*2 + 1];
+ create_hash(hash_str, string_to_hash);
+ /*free(hash_str);*/
+
+ dd_save_text(dd, CD_UUID, hash_str);
+ dd_close(dd);
+
+ return 0;
+}
diff --git a/src/daemon/abrt-action-bugzilla.cpp b/src/daemon/abrt-action-bugzilla.cpp
index c7768556..6c989ea0 100644
--- a/src/daemon/abrt-action-bugzilla.cpp
+++ b/src/daemon/abrt-action-bugzilla.cpp
@@ -569,8 +569,8 @@ static void report_to_bugzilla(
const char *dump_dir_name,
/*const*/ map_plugin_settings_t& settings)
{
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
{
throw CABRTException(EXCEP_PLUGIN, _("Can't open '%s'"), dump_dir_name);
}
diff --git a/src/daemon/abrt-action-generate-backtrace.c b/src/daemon/abrt-action-generate-backtrace.c
index b87c7c9e..9e4fc078 100644
--- a/src/daemon/abrt-action-generate-backtrace.c
+++ b/src/daemon/abrt-action-generate-backtrace.c
@@ -17,7 +17,9 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "abrtlib.h"
-#include "backtrace.h"
+#include "../btparser/backtrace.h"
+#include "../btparser/frame.h"
+#include "../btparser/location.h"
#include "parse_options.h"
@@ -252,10 +254,10 @@ enum {
/* Keep enum above and order of options below in sync! */
static struct options abrt_action_generate_backtrace_options[] = {
OPT__VERBOSE(&g_verbose),
- OPT_STRING( 'd' , 0, &dump_dir_name, "dir", "Crash dump directory"),
- OPT_STRING( 'i' , 0, &i_opt, "dir1[:dir2]...", "Additional debuginfo directories"),
- OPT_INTEGER( 't' , 0, &exec_timeout_sec, "Kill gdb if it runs for more than SECONDS"),
- OPT_BOOL( 's' , 0, NULL, "Log to syslog even with -d"),
+ OPT_STRING( 'd', NULL, &dump_dir_name, "DIR", "Crash dump directory"),
+ OPT_STRING( 'i', NULL, &i_opt, "dir1[:dir2]...", "Additional debuginfo directories"),
+ OPT_INTEGER('t', NULL, &exec_timeout_sec, "Kill gdb if it runs for more than N seconds"),
+ OPT_BOOL( 's', NULL, NULL, "Log to syslog"),
OPT_END()
};
@@ -283,8 +285,8 @@ int main(int argc, char **argv)
logmode = LOGMODE_SYSLOG;
}
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
return 1;
char *package = dd_load_text(dd, FILENAME_PACKAGE);
@@ -299,104 +301,84 @@ int main(int argc, char **argv)
VERB3 log("get_backtrace() returns NULL, broken core/gdb?");
}
- dd = dd_init();
- if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
return 1;
dd_save_text(dd, FILENAME_BACKTRACE, backtrace_str);
/* Compute and store backtrace hash. */
- char *backtrace_cpy = xstrdup(backtrace_str);
- struct backtrace *backtrace = backtrace_parse(backtrace_cpy, false, false);
- free(backtrace_cpy);
- if (backtrace)
+ struct btp_location location;
+ btp_location_init(&location);
+ char *backtrace_str_ptr = backtrace_str;
+ struct btp_backtrace *backtrace = btp_backtrace_parse(&backtrace_str_ptr, &location);
+ if (!backtrace)
{
- /* Get the quality of the full backtrace. */
- float q1 = backtrace_quality(backtrace);
-
- /* Remove all the other threads except the crash thread. */
- struct thread *crash_thread = backtrace_find_crash_thread(backtrace);
- if (crash_thread)
- backtrace_remove_threads_except_one(backtrace, crash_thread);
- else
- log_msg("Detection of crash thread failed");
-
- /* Get the quality of the crash thread. */
- float q2 = backtrace_quality(backtrace);
-
- backtrace_remove_noncrash_frames(backtrace);
-
- /* Do the frame removal now. */
- backtrace_limit_frame_depth(backtrace, 5);
- /* Frame removal can be done before removing exit handlers. */
- backtrace_remove_exit_handlers(backtrace);
-
- /* Get the quality of frames around the crash. */
- float q3 = backtrace_quality(backtrace);
-
- /* Compute UUID. */
- struct strbuf *bt = backtrace_tree_as_str(backtrace, false);
- strbuf_prepend_str(bt, executable);
- strbuf_prepend_str(bt, package);
+ VERB1 log(_("Backtrace parsing failed for %s"), dump_dir_name);
+ VERB1 log("%d:%d: %s", location.line, location.column, location.message);
+ /* If the parser failed compute the UUID from the executable
+ and package only. This is not supposed to happen often.
+ Do not store the rating, as we do not know how good the
+ backtrace is. */
+ struct strbuf *emptybt = strbuf_new();
+ strbuf_prepend_str(emptybt, executable);
+ strbuf_prepend_str(emptybt, package);
char hash_str[SHA1_RESULT_LEN*2 + 1];
- create_hash(hash_str, bt->buf);
+ create_hash(hash_str, emptybt->buf);
dd_save_text(dd, FILENAME_DUPHASH, hash_str);
- strbuf_free(bt);
-
- /* Compute and store backtrace rating. The crash frame
- is more important that the others. The frames around
- the crash are more important than the rest. */
- float qtot = 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
-
- /* Turn the quality to rating. */
- const char *rating;
- if (qtot < 0.6f) rating = "0";
- else if (qtot < 0.7f) rating = "1";
- else if (qtot < 0.8f) rating = "2";
- else if (qtot < 0.9f) rating = "3";
- else rating = "4";
- dd_save_text(dd, FILENAME_RATING, rating);
-
- /* Get the function name from the crash frame. */
- if (crash_thread)
- {
- struct frame *crash_frame = crash_thread->frames;
- struct frame *abort_frame = thread_find_abort_frame(crash_thread);
- if (abort_frame)
- crash_frame = abort_frame->next;
- if (crash_frame && crash_frame->function && 0 != strcmp(crash_frame->function, "??"))
- dd_save_text(dd, FILENAME_CRASH_FUNCTION, crash_frame->function);
- }
- backtrace_free(backtrace);
- }
- else
- {
- /* If the parser failed fall back to the independent backtrace. */
- /* If we write and use a hand-written parser instead of the bison one,
- the parser never fails, and it will be possible to get rid of
- the independent_backtrace and backtrace_rate_old. */
- struct strbuf *ibt = independent_backtrace(backtrace_str);
- strbuf_prepend_str(ibt, executable);
- strbuf_prepend_str(ibt, package);
- char hash_str[SHA1_RESULT_LEN*2 + 1];
- create_hash(hash_str, ibt->buf);
- dd_save_text(dd, FILENAME_DUPHASH, hash_str);
- strbuf_free(ibt);
-
- /* Compute and store backtrace rating. */
- /* Crash frame is not known so store nothing. */
- int rate = backtrace_rate_old(backtrace_str);
- char rate_buf[sizeof(rate)*3 + 2];
- sprintf(rate_buf, "%d", rate);
- dd_save_text(dd, FILENAME_RATING, rate_buf);
+ strbuf_free(emptybt);
+ free(backtrace_str);
+ free(package);
+ free(executable);
+ dd_close(dd);
+ return 2;
}
+ free(backtrace_str);
+ /* Compute duplication hash. */
+ char *str_hash_core = btp_backtrace_get_duplication_hash(backtrace);
+ struct strbuf *str_hash = strbuf_new();
+ strbuf_append_str(str_hash, package);
+ strbuf_append_str(str_hash, executable);
+ strbuf_append_str(str_hash, str_hash_core);
+ char hash_str[SHA1_RESULT_LEN*2 + 1];
+ create_hash(hash_str, str_hash->buf);
+ dd_save_text(dd, FILENAME_DUPHASH, hash_str);
+ strbuf_free(str_hash);
+ free(str_hash_core);
+
+ /* Compute the backtrace rating. */
+ float quality = btp_backtrace_quality_complex(backtrace);
+ const char *rating;
+ if (quality < 0.6f)
+ rating = "0";
+ else if (quality < 0.7f)
+ rating = "1";
+ else if (quality < 0.8f)
+ rating = "2";
+ else if (quality < 0.9f)
+ rating = "3";
+ else
+ rating = "4";
+ dd_save_text(dd, FILENAME_RATING, rating);
+
+ /* Get the function name from the crash frame. */
+ struct btp_frame *crash_frame = btp_backtrace_get_crash_frame(backtrace);
+ if (crash_frame)
+ {
+ if (crash_frame->function_name &&
+ 0 != strcmp(crash_frame->function_name, "??"))
+ {
+ dd_save_text(dd, FILENAME_CRASH_FUNCTION, crash_frame->function_name);
+ }
+ btp_frame_free(crash_frame);
+ }
+ btp_backtrace_free(backtrace);
dd_close(dd);
free(executable);
free(package);
- free(backtrace_str);
return 0;
}
diff --git a/src/daemon/abrt-action-save-package-data.cpp b/src/daemon/abrt-action-save-package-data.cpp
index 9dbddac4..4d397071 100644
--- a/src/daemon/abrt-action-save-package-data.cpp
+++ b/src/daemon/abrt-action-save-package-data.cpp
@@ -23,6 +23,8 @@
#include "rpm.h"
#include "parse_options.h"
+#define PROGNAME "abrt-action-save-package-data"
+
/**
* Returns the first full path argument in the command line or NULL.
* Skips options are in form "-XXX".
@@ -76,8 +78,8 @@ static bool is_path_blacklisted(const char *path)
static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
{
- struct dump_dir *dd = dd_init();
- if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
return 1;
char *remote_str = dd_load_text(dd, FILENAME_REMOTE);
@@ -120,8 +122,8 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
if (g_settings_bProcessUnpackaged || remote)
{
VERB2 log("Crash in unpackaged executable '%s', proceeding without packaging information", executable);
- dd = dd_init();
- if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
goto ret; /* return 1 (failure) */
dd_save_text(dd, FILENAME_PACKAGE, "");
dd_save_text(dd, FILENAME_DESCRIPTION, "Crashed executable does not belong to any installed package");
@@ -222,8 +224,8 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
component = rpm_get_component(executable);
dsc = rpm_get_description(package_short_name);
- dd = dd_init();
- if (!dd_opendir(dd, dump_dir_name, DD_CLOSE_ON_OPEN_ERR))
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
goto ret; /* return 1 (failure) */
}
@@ -268,7 +270,10 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
}
static const char *dump_dir_name = ".";
-static const char abrt_action_save_package_data_usage[] = "abrt-action-save-package-data [options] -d DIR";
+static const char abrt_action_save_package_data_usage[] =
+ PROGNAME" [options] -d DIR\n"
+ "\n"
+ "Query package database and save package name, component, and description";
enum {
OPT_v = 1 << 0,
OPT_d = 1 << 1,
@@ -277,8 +282,8 @@ enum {
/* Keep enum above and order of options below in sync! */
static struct options abrt_action_save_package_data_options[] = {
OPT__VERBOSE(&g_verbose),
- OPT_STRING( 'd' , 0, &dump_dir_name, "dir", "Crash dump directory"),
- OPT_BOOL( 's' , 0, NULL, "Log to syslog"),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR", "Crash dump directory"),
+ OPT_BOOL( 's', NULL, NULL, "Log to syslog"),
OPT_END()
};
@@ -292,7 +297,7 @@ int main(int argc, char **argv)
abrt_action_save_package_data_usage);
putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- msg_prefix = xasprintf("abrt-action-save-package-data[%u]", getpid());
+ msg_prefix = xasprintf(PROGNAME"[%u]", getpid());
if (opts & OPT_s)
{
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
index 231fc0bd..67978561 100644
--- a/src/daemon/abrt-server.c
+++ b/src/daemon/abrt-server.c
@@ -115,11 +115,9 @@ static void create_debug_dump()
/* No need to check the path length, as all variables used are limited, and dd_create()
fails if the path is too long. */
- struct dump_dir *dd = dd_init();
- if (!dd_create(dd, path, client_uid))
+ struct dump_dir *dd = dd_create(path, client_uid);
+ if (!dd)
{
- dd_delete(dd);
- dd_close(dd);
error_msg_and_die("Error creating crash dump %s", path);
}
diff --git a/src/gui/CCDump.py b/src/gui/CCDump.py
index e94d0c9b..e02cb7ad 100644
--- a/src/gui/CCDump.py
+++ b/src/gui/CCDump.py
@@ -36,7 +36,7 @@ FILENAME_HOSTNAME = "hostname"
FILENAME_REMOTE = "remote"
CD_UID = "uid"
-CD_UUID = "UUID"
+CD_UUID = "uuid"
CD_INFORMALL = "InformAll"
CD_DUMPDIR = "DumpDir"
CD_COUNT = "Count"
@@ -70,7 +70,7 @@ class Dump():
return "Dump instance"
def getUUID(self):
- return self.UUID
+ return self.uuid
def getUID(self):
return self.uid
diff --git a/src/hooks/abrt-hook-ccpp.cpp b/src/hooks/abrt-hook-ccpp.cpp
index a35bba11..146de6e0 100644
--- a/src/hooks/abrt-hook-ccpp.cpp
+++ b/src/hooks/abrt-hook-ccpp.cpp
@@ -388,14 +388,15 @@ int main(int argc, char** argv)
close(fd);
}
- if (strstr(executable, "/abrtd"))
+ const char *last_slash = strrchr(executable, '/');
+ if (last_slash && strncmp(++last_slash, "abrt", 4) == 0)
{
- /* If abrtd crashes, we don't want to create a _directory_,
+ /* If abrtd/abrt-foo crashes, we don't want to create a _directory_,
* since that can make new copy of abrtd to process it,
* and maybe crash again...
* Unlike dirs, mere files are ignored by abrtd.
*/
- snprintf(path, sizeof(path), "%s/abrtd-coredump", dddir);
+ snprintf(path, sizeof(path), "%s/%s-coredump", dddir, last_slash);
int abrt_core_fd = xopen3(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
off_t core_size = copyfd_eof(STDIN_FILENO, abrt_core_fd, COPYFD_SPARSE);
if (core_size < 0 || fsync(abrt_core_fd) != 0)
@@ -414,8 +415,8 @@ int main(int argc, char** argv)
if (path_len >= (sizeof(path) - sizeof("/"FILENAME_COREDUMP)))
return 1;
- struct dump_dir *dd = dd_init();
- if (dd_create(dd, path, uid))
+ struct dump_dir *dd = dd_create(path, uid);
+ if (dd)
{
char *cmdline = get_cmdline(pid); /* never NULL */
char *reason = xasprintf("Process %s was killed by signal %s (SIG%s)", executable, signal_str, signame ? signame : signal_str);
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
deleted file mode 100644
index 29e06645..00000000
--- a/src/utils/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-bin_PROGRAMS = abrt-backtrace
-abrt_backtrace_CFLAGS = -Wall
-abrt_backtrace_SOURCES = abrt-backtrace.c
-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/utils/abrt-backtrace.1 b/src/utils/abrt-backtrace.1
deleted file mode 100644
index 4383f1d0..00000000
--- a/src/utils/abrt-backtrace.1
+++ /dev/null
@@ -1,59 +0,0 @@
-.TH abrt\-backtrace "1" "23 Nov 2009" ""
-.SH NAME
-abrt\-backtrace \- a backtrace analyzer for abrt
-.SH SYNOPSIS
-.B abrt\-backtrace
-[option]... [FILE]
-.SH DESCRIPTION
-.I abrt\-backtrace
-is a command line tool that analyzes backtraces produced by
-GDB and provides their textual representation useful for
-crash duplication detection.
-
-By default, abrt\-backtrace prints the backtrace tree created by
-parsing the input file.
-
-.SH OPTIONS
-.B Basic startup options
-.IP "\-V, \-\-version"
-Displays version of abrt\-backtrace.
-.IP "\-?, \-\-help"
-Print a help message describing all of abrt-backtrace’s command-line options.
-
-.PP
-.B Actions
-.IP "\-i, \-\-independent"
-Prints independent backtrace fallback. The "independent backtrace"
-is used as a fallback internal representation of backtrace
-when the internal parser fails to process the input due to
-unsupported format.
-
-.PP
-.B Various options
-.IP "\-n, \-\-single\-thread"
-Removes all threads except the one that caused the crash from
-the internal representation of the backtrace.
-.IP "\-d=N, \-\-frame\-depth=N"
-Display only the top N frames in every thread.
-Frames that are a part of system error handling do not count to the N.
-So more than N frames can be displayed, but N of them are useful for examination.
-N must be larger than 1.
-.IP "\-r, \-\-remove\-exit\-handlers"
-Do not display exit handlers and frames that comes after them in the backtrace.
-.IP "\-m, \-\-remove\-noncrash\-frames"
-Do not display frames known as not causing the crash, but which are a common
-part of backtraces.
-.IP "\-a, \-\-rate"
-Print backtrace rating from 0 to 4.
-.IP "\-c, \-\-crash\-function"
-Print crash function if it's successfully detected.
-.IP "\-p, \-\-debug\-parser"
-Prints debug information when parsing the backtrace.
-.IP "\-s, \-\-debug\-scanner"
-Prints debug information when scanning the input file for the parser.
-
-.SH "SEE ALSO"
-.IR abrtd (8),
-.IR abrt.conf (5),
-.IR abrt-cli (1),
-.IR abrt-plugins (7)
diff --git a/src/utils/abrt-backtrace.c b/src/utils/abrt-backtrace.c
deleted file mode 100644
index d02c9633..00000000
--- a/src/utils/abrt-backtrace.c
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- abrt-backtrace.c - 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 <sysexits.h>
-#include <string.h>
-#include "config.h"
-#include "backtrace.h"
-#include "strbuf.h"
-
-/* Too large files are trimmed. */
-#define FILE_SIZE_LIMIT 20000000 /* ~ 20 MB */
-
-#define EX_PARSINGFAILED EX__MAX + 1 /* = 79 */
-#define EX_THREADDETECTIONFAILED EX__MAX + 2 /* = 80 */
-
-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)"},
- {"single-thread" , 'n', 0 , 0, "Display the crash thread only in the backtrace"},
- {"frame-depth" , 'd', "N", 0, "Display only top N frames under the crash frame"},
- {"remove-exit-handlers" , 'r', 0 , 0, "Removes exit handler frames from the displayed backtrace"},
- {"remove-noncrash-frames", 'm', 0 , 0, "Removes common frames known as not causing crash"},
- {"rate" , 'a', 0 , 0, "Prints the backtrace rating from 0 to 4"},
- {"crash-function" , 'c', 0 , 0, "Prints crash function"},
- {"debug-parser" , 'p', 0 , 0, "Prints parser debug information"},
- {"debug-scanner" , 's', 0 , 0, "Prints scanner debug information"},
- {"verbose" , 'v', 0 , 0, "Prints human-friendly superfluous output."},
- { 0 }
-};
-
-struct arguments
-{
- bool independent;
- bool single_thread;
- int frame_depth; /* negative == do not limit the depth */
- bool remove_exit_handlers;
- bool remove_noncrash_frames;
- bool debug_parser;
- bool debug_scanner;
- bool verbose;
- bool rate;
- bool crash_function;
- 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 'n': arguments->single_thread = true; break;
- case 'd':
- if (1 != sscanf(arg, "%d", &arguments->frame_depth))
- {
- /* Must be a number. */
- argp_usage(state);
- exit(EX_USAGE); /* Invalid argument */
- }
- break;
- case 'r': arguments->remove_exit_handlers = true; break;
- case 'm': arguments->remove_noncrash_frames = true; break;
- case 'p': arguments->debug_parser = true; break;
- case 's': arguments->debug_scanner = true; break;
- case 'v': arguments->verbose = true; break;
- case 'a': arguments->rate = true; break;
- case 'c': arguments->crash_function = true; break;
-
- case ARGP_KEY_ARG:
- if (arguments->filename)
- {
- /* Too many arguments. */
- argp_usage(state);
- exit(EX_USAGE); /* Invalid argument */
- }
- arguments->filename = arg;
- break;
-
- case ARGP_KEY_END:
- break;
-
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return 0;
-}
-
-/* Our argp parser. */
-static struct argp argp = { options, parse_opt, args_doc, doc };
-
-#define PYTHON_BACKTRACE_ID1 "\n\nTraceback (most recent call last):\n"
-#define PYTHON_BACKTRACE_ID2 "\n\nLocal variables in innermost frame:\n"
-
-int main(int argc, char **argv)
-{
- /* Set options default values and parse program command line. */
- struct arguments arguments;
- arguments.independent = false;
- arguments.frame_depth = -1;
- arguments.single_thread = false;
- arguments.remove_exit_handlers = false;
- arguments.remove_noncrash_frames = false;
- arguments.debug_parser = false;
- arguments.debug_scanner = false;
- arguments.verbose = false;
- arguments.filename = 0;
- arguments.rate = false;
- arguments.crash_function = false;
- argp_parse(&argp, argc, argv, 0, 0, &arguments);
-
- /* If we are about to rate a backtrace, the other values must be set accordingly,
- no matter what the user set on the command line. */
- if (arguments.rate)
- {
- arguments.independent = false;
- arguments.frame_depth = 5;
- arguments.single_thread = true;
- arguments.remove_exit_handlers = true;
- arguments.remove_noncrash_frames = true;
- }
-
- /* If we are about to print the crash function, the other values must be set
- accordingly. */
- if (arguments.crash_function)
- {
- arguments.independent = false;
- arguments.single_thread = true;
- }
-
- char *bttext = NULL;
-
- /* If a filename was provided, read input from file.
- Otherwise read from stdin. */
- if (arguments.filename)
- {
- /* Open input file, and parse it. */
- FILE *fp = fopen(arguments.filename, "r");
- if (!fp)
- {
- fprintf(stderr, "Unable to open '%s'.\n", arguments.filename);
- 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 %d.\n",
- size, FILE_SIZE_LIMIT);
- exit(EX_IOERR);
- }
-
- /* Handle the case that the input file is empty.
- * The code is not designed to support completely empty backtrace.
- * Silently exit indicating success.
- */
- if (size == 0)
- {
- fclose(fp);
- exit(0);
- }
-
- bttext = malloc(size + 1);
- if (!bttext)
- {
- fclose(fp);
- fputs("malloc failed", stderr);
- exit(EX_OSERR);
- }
-
- if (1 != fread(bttext, size, 1, fp))
- {
- fclose(fp);
- fprintf(stderr, "Unable to read from '%s'.\n", arguments.filename);
- exit(EX_IOERR); /* IO Error */
- }
-
- bttext[size] = '\0';
- fclose(fp);
- }
- else
- {
- struct strbuf *btin = strbuf_new();
- int c;
- while ((c = getchar()) != EOF && c != '\0')
- strbuf_append_char(btin, (char)c);
-
- strbuf_append_char(btin, '\0');
- bttext = btin->buf;
- strbuf_free_nobuf(btin); /* free btin, but not its internal buffer */
- }
-
- /* Detect Python backtraces. If it is a Python backtrace,
- * silently exit for now.
- */
- if (strstr(bttext, PYTHON_BACKTRACE_ID1) != NULL
- && strstr(bttext, PYTHON_BACKTRACE_ID2) != NULL)
- {
- if (arguments.rate)
- puts("4");
- exit(0);
- }
-
- /* Print independent backtrace and exit. */
- if (arguments.independent)
- {
- struct strbuf *ibt = independent_backtrace(bttext);
- puts(ibt->buf);
- strbuf_free(ibt);
- free(bttext);
- return 0; /* OK */
- }
-
- /* Try to parse the backtrace. */
- struct backtrace *backtrace;
- backtrace = backtrace_parse(bttext, arguments.debug_parser, arguments.debug_scanner);
-
- /* If the parser failed print independent backtrace. */
- if (!backtrace)
- {
- if (arguments.rate)
- {
- free(bttext);
- puts("0");
- /* Parsing failed, but the output can be used. */
- return EX_PARSINGFAILED;
- }
- struct strbuf *ibt = independent_backtrace(bttext);
- puts(ibt->buf);
- strbuf_free(ibt);
- free(bttext);
- /* Parsing failed, but the output can be used. */
- return EX_PARSINGFAILED;
- }
-
- free(bttext);
-
- /* [--rate] Get the quality of the full backtrace. */
- float q1 = backtrace_quality(backtrace);
-
- /* If a single thread is requested, remove all other threads. */
- int retval = 0;
- struct thread *crash_thread = NULL;
- if (arguments.single_thread)
- {
- crash_thread = backtrace_find_crash_thread(backtrace);
- if (crash_thread)
- backtrace_remove_threads_except_one(backtrace, crash_thread);
- else
- {
- fprintf(stderr, "Detection of crash thread failed.\n");
- /* THREAD DETECTION FAILED, BUT THE OUTPUT CAN BE USED */
- retval = EX_THREADDETECTIONFAILED;
- }
- }
-
- /* [--rate] Get the quality of the crash thread. */
- float q2 = backtrace_quality(backtrace);
-
- if (arguments.remove_noncrash_frames)
- backtrace_remove_noncrash_frames(backtrace);
-
- /* If a frame removal is requested, do it now. */
- if (arguments.frame_depth > 0)
- backtrace_limit_frame_depth(backtrace, arguments.frame_depth);
-
- /* Frame removal can be done before removing exit handlers */
- if (arguments.remove_exit_handlers > 0)
- backtrace_remove_exit_handlers(backtrace);
-
- /* [--rate] Get the quality of frames around the crash. */
- float q3 = backtrace_quality(backtrace);
-
- if (arguments.rate)
- {
- /* Compute and store backtrace rating. */
- /* Compute and store backtrace rating. The crash frame
- is more important that the others. The frames around
- the crash are more important than the rest. */
- float qtot = 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
-
- /* Turn the quality to rating. */
- const char *rating;
- if (qtot < 0.6f) rating = "0";
- else if (qtot < 0.7f) rating = "1";
- else if (qtot < 0.8f) rating = "2";
- else if (qtot < 0.9f) rating = "3";
- else rating = "4";
- puts(rating);
- }
-
- if (arguments.crash_function && crash_thread)
- {
- struct frame *crash_frame = crash_thread->frames;
- struct frame *abort_frame = thread_find_abort_frame(crash_thread);
- if (abort_frame)
- crash_frame = abort_frame->next;
- if (crash_frame && crash_frame->function)
- puts(crash_frame->function);
- }
-
- backtrace_print_tree(backtrace, arguments.verbose);
- backtrace_free(backtrace);
- return retval;
-}