Esempio n. 1
0
/* destructor */
void
sr_py_gdb_frame_free(PyObject *object)
{
    struct sr_py_gdb_frame *this = (struct sr_py_gdb_frame*)object;
    sr_gdb_frame_free(this->frame);
    PyObject_Del(object);
}
int main(int argc, char **argv)
{
    /* I18n */
    setlocale(LC_ALL, "");
#if ENABLE_NLS
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

    abrt_init(argv);

    /* Can't keep these strings/structs static: _() doesn't support that */
    const char *program_usage_string = _(
        "& [options] -d DIR\n"
        "\n"
        "Analyzes C/C++ backtrace, generates duplication hash, backtrace rating,\n"
        "and identifies crash function in problem directory DIR"
    );
    enum {
        OPT_v = 1 << 0,
        OPT_d = 1 << 1
    };
    /* 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", _("Problem directory")),
        OPT_END()
    };
    /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);

    export_abrt_envvars(0);

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        return 1;

    char *component = dd_load_text(dd, FILENAME_COMPONENT);

    /* Read backtrace */
    char *backtrace_str = dd_load_text_ext(dd, FILENAME_BACKTRACE,
                                           DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
    if (!backtrace_str)
    {
        dd_close(dd);
        return 1;
    }

    /* Compute backtrace hash */
    struct sr_location location;
    sr_location_init(&location);
    const char *backtrace_str_ptr = backtrace_str;
    struct sr_gdb_stacktrace *backtrace = sr_gdb_stacktrace_parse(&backtrace_str_ptr, &location);
    free(backtrace_str);

    /* Store backtrace hash */
    if (!backtrace)
    {
        /*
         * The parser failed. Compute the duphash from the executable
         * instead of a backtrace.
         * and component only.  This is not supposed to happen often.
         */
        log(_("Backtrace parsing failed for %s"), dump_dir_name);
        log("%d:%d: %s", location.line, location.column, location.message);
        struct strbuf *emptybt = strbuf_new();

        char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
        strbuf_prepend_str(emptybt, executable);
        free(executable);

        strbuf_prepend_str(emptybt, component);

        log_debug("Generating duphash: %s", emptybt->buf);
        char hash_str[SHA1_RESULT_LEN*2 + 1];
        str_to_sha1str(hash_str, emptybt->buf);

        dd_save_text(dd, FILENAME_DUPHASH, hash_str);
        /*
         * Other parts of ABRT assume that if no rating is available,
         * it is ok to allow reporting of the bug. To be sure no bad
         * backtrace is reported, rate the backtrace with the lowest
         * rating.
         */
        dd_save_text(dd, FILENAME_RATING, "0");

        strbuf_free(emptybt);
        free(component);
        dd_close(dd);

        /* Report success even if the parser failed, as the backtrace
         * has been created and rated. The failure is caused by a flaw
         * in the parser, not in the backtrace.
         */
        return 0;
    }

    /* Compute duplication hash. */
    struct sr_thread *crash_thread =
        (struct sr_thread *)sr_gdb_stacktrace_find_crash_thread(backtrace);

    if (crash_thread)
    {
        char *hash_str;

        if (g_verbose >= 3)
        {
            hash_str = sr_thread_get_duphash(crash_thread, 3, component,
                                             SR_DUPHASH_NOHASH);
            log("Generating duphash: %s", hash_str);
            free(hash_str);
        }

        hash_str = sr_thread_get_duphash(crash_thread, 3, component,
                                         SR_DUPHASH_NORMAL);
        dd_save_text(dd, FILENAME_DUPHASH, hash_str);
        free(hash_str);
    }
    else
        log(_("Crash thread not found"));


    /* Compute the backtrace rating. */
    float quality = sr_gdb_stacktrace_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 sr_gdb_frame *crash_frame = sr_gdb_stacktrace_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);
        }
        sr_gdb_frame_free(crash_frame);
    }
    sr_gdb_stacktrace_free(backtrace);
    dd_close(dd);
    free(component);
    return 0;
}
Esempio n. 3
0
void
sr_normalize_gdb_thread(struct sr_gdb_thread *thread)
{
    /* Find the exit frame and remove everything above it. */
    struct sr_gdb_frame *exit_frame = sr_glibc_thread_find_exit_frame(thread);
    if (exit_frame)
    {
        bool success = sr_gdb_thread_remove_frames_above(thread, exit_frame);
        assert(success); /* if this fails, some code become broken */
        success = sr_gdb_thread_remove_frame(thread, exit_frame);
        assert(success); /* if this fails, some code become broken */
    }

    /* Normalize function names by removing various prefixes that
     * occur only in some cases.
     */
    struct sr_gdb_frame *frame = thread->frames;
    while (frame)
    {
        if (frame->source_file)
        {
            /* Remove IA__ prefix used in GLib, GTK and GDK. */
            remove_func_prefix(frame->function_name, "IA__gdk", strlen("IA__"));
            remove_func_prefix(frame->function_name, "IA__g_", strlen("IA__"));
            remove_func_prefix(frame->function_name, "IA__gtk", strlen("IA__"));

            /* Remove __GI_ (glibc internal) prefix. */
            remove_func_prefix(frame->function_name, "__GI_", strlen("__GI_"));
        }

        frame = frame->next;
    }

    /* Unify some functions by renaming them.
     */
    frame = thread->frames;
    while (frame)
    {
        char *new_function_name =
            find_new_function_name_glibc(frame->function_name, frame->source_file);

        if (new_function_name)
        {
            free(frame->function_name);
            frame->function_name = new_function_name;
        }

        frame = frame->next;
    }

    /* Remove redundant frames from the thread.
     */
    frame = thread->frames;
    while (frame)
    {
        struct sr_gdb_frame *next_frame = frame->next;

        /* Remove frames which are not a cause of the crash. */
        bool removable =
            is_removable_dbus(frame->function_name, frame->source_file) ||
            is_removable_gdk(frame->function_name, frame->source_file) ||
            is_removable_glib(frame->function_name, frame->source_file) ||
            is_removable_glibc(frame->function_name, frame->source_file) ||
            is_removable_libstdcpp(frame->function_name, frame->source_file) ||
            is_removable_linux(frame->function_name, frame->source_file) ||
            is_removable_xorg(frame->function_name, frame->source_file) ||
            is_removable_jvm(frame->function_name, frame->source_file) ||
            is_removable_vim(frame->function_name, frame->source_file);

        bool removable_with_above =
            is_removable_glibc_with_above(frame->function_name, frame->source_file) ||
            sr_gdb_is_exit_frame(frame);

        if (removable_with_above)
        {
            bool success = sr_gdb_thread_remove_frames_above(thread, frame);
            assert(success);
        }

        if (removable || removable_with_above)
            sr_gdb_thread_remove_frame(thread, frame);

        frame = next_frame;
    }

    /* If the first frame has address 0x0000 and its name is '??', it
     * is a dereferenced null, and we remove it. This frame is not
     * really invalid, but it affects stacktrace quality rating. See
     * Red Hat Bugzilla bug #639038.
     * @code
     * #0  0x0000000000000000 in ?? ()
     * No symbol table info available.
     * #1  0x0000000000422648 in main (argc=1, argv=0x7fffa57cf0d8) at totem.c:242
     *       error = 0x0
     *       totem = 0xdee070 [TotemObject]
     * @endcode
     */
    if (thread->frames &&
        thread->frames->address == 0x0000 &&
        thread->frames->function_name &&
        0 == strcmp(thread->frames->function_name, "??"))
    {
        sr_gdb_thread_remove_frame(thread, thread->frames);
    }

    /* If the last frame has address 0x0000 and its name is '??',
     * remove it. This frame is not really invalid, but it affects
     * stacktrace 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 sr_gdb_frame *last = thread->frames;
    while (last && last->next)
        last = last->next;
    if (last &&
        last->address == 0x0000 &&
        last->function_name &&
        0 == strcmp(last->function_name, "??"))
    {
        sr_gdb_thread_remove_frame(thread, last);
    }

    /* Merge recursively called functions into single frame */
    struct sr_gdb_frame *curr_frame = thread->frames;
    struct sr_gdb_frame *prev_frame = NULL;
    while (curr_frame)
    {
        if (prev_frame &&
            0 != sr_strcmp0(prev_frame->function_name, "??") &&
            0 == sr_strcmp0(prev_frame->function_name, curr_frame->function_name))
        {
            prev_frame->next = curr_frame->next;
            sr_gdb_frame_free(curr_frame);
            curr_frame = prev_frame->next;
            continue;
        }

        prev_frame = curr_frame;
        curr_frame = curr_frame->next;
    }
}