summaryrefslogtreecommitdiffstats
path: root/src/btparser/backtrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/btparser/backtrace.c')
-rw-r--r--src/btparser/backtrace.c445
1 files changed, 0 insertions, 445 deletions
diff --git a/src/btparser/backtrace.c b/src/btparser/backtrace.c
deleted file mode 100644
index 139b315d..00000000
--- a/src/btparser/backtrace.c
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- 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;
-}