Blame src/plugins/abrt-action-analyze-c.c

Packit 8ea169
/*
Packit 8ea169
    Copyright (C) 2009  Zdenek Prikryl (zprikryl@redhat.com)
Packit 8ea169
    Copyright (C) 2009  RedHat inc.
Packit 8ea169
Packit 8ea169
    This program is free software; you can redistribute it and/or modify
Packit 8ea169
    it under the terms of the GNU General Public License as published by
Packit 8ea169
    the Free Software Foundation; either version 2 of the License, or
Packit 8ea169
    (at your option) any later version.
Packit 8ea169
Packit 8ea169
    This program is distributed in the hope that it will be useful,
Packit 8ea169
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ea169
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8ea169
    GNU General Public License for more details.
Packit 8ea169
Packit 8ea169
    You should have received a copy of the GNU General Public License along
Packit 8ea169
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 8ea169
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 8ea169
*/
Packit 8ea169
#include "libabrt.h"
Packit 8ea169
Packit 8ea169
#include <glib.h>
Packit 8ea169
Packit 8ea169
#include <satyr/thread.h>
Packit 8ea169
#include <satyr/core/stacktrace.h>
Packit 8ea169
#include <satyr/core/thread.h>
Packit 8ea169
#include <satyr/core/frame.h>
Packit 8ea169
#include <satyr/normalize.h>
Packit 8ea169
Packit 8ea169
static void trim_unstrip_output(char *result, const char *unstrip_n_output)
Packit 8ea169
{
Packit 8ea169
    // lines look like this:
Packit 8ea169
    // 0x400000+0x209000 23c77451cf6adff77fc1f5ee2a01d75de6511dda@0x40024c - - [exe]
Packit 8ea169
    // 0x400000+0x209000 ab3c8286aac6c043fd1bb1cc2a0b88ec29517d3e@0x40024c /bin/sleep /usr/lib/debug/bin/sleep.debug [exe]
Packit 8ea169
    // 0x7fff313ff000+0x1000 389c7475e3d5401c55953a425a2042ef62c4c7df@0x7fff313ff2f8 . - linux-vdso.so.1
Packit 8ea169
    //                ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Packit 8ea169
    // we drop everything except the marked part ^
Packit 8ea169
Packit 8ea169
    char *dst = result;
Packit 8ea169
    const char *line = unstrip_n_output;
Packit 8ea169
    while (*line)
Packit 8ea169
    {
Packit 8ea169
        const char *eol = strchrnul(line, '\n');
Packit 8ea169
        const char *plus = (char*)memchr(line, '+', eol - line);
Packit 8ea169
        if (plus)
Packit 8ea169
        {
Packit 8ea169
            while (++plus < eol && *plus != '@')
Packit 8ea169
            {
Packit 8ea169
                if (!isspace(*plus))
Packit 8ea169
                {
Packit 8ea169
                    *dst++ = *plus;
Packit 8ea169
                }
Packit 8ea169
            }
Packit 8ea169
        }
Packit 8ea169
        if (*eol != '\n') break;
Packit 8ea169
        line = eol + 1;
Packit 8ea169
    }
Packit 8ea169
    *dst = '\0';
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static struct sr_core_thread *
Packit 8ea169
core_thread_from_core_stacktrace(struct sr_core_stacktrace *stacktrace)
Packit 8ea169
{
Packit 8ea169
    struct sr_core_thread *thread = sr_core_stacktrace_find_crash_thread(stacktrace);
Packit 8ea169
    if (!thread)
Packit 8ea169
    {
Packit 8ea169
        log_info("Failed to find crash thread");
Packit 8ea169
        return NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return thread;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static struct sr_core_stacktrace *
Packit 8ea169
core_stacktrace_from_core_json(char *core_backtrace)
Packit 8ea169
{
Packit 8ea169
    char *error = NULL;
Packit 8ea169
    struct sr_core_stacktrace *stacktrace = sr_core_stacktrace_from_json_text(core_backtrace, &error);
Packit 8ea169
    if (!stacktrace)
Packit 8ea169
    {
Packit 8ea169
        if (error)
Packit 8ea169
        {
Packit 8ea169
            log_info("Failed to parse core backtrace: %s", error);
Packit 8ea169
            free(error);
Packit 8ea169
        }
Packit 8ea169
        return NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    return stacktrace;
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
static char *build_ids_from_core_backtrace(const char *dump_dir_name)
Packit 8ea169
{
Packit 8ea169
    char *core_backtrace_path = xasprintf("%s/"FILENAME_CORE_BACKTRACE, dump_dir_name);
Packit 8ea169
    char *json = xmalloc_open_read_close(core_backtrace_path, /*maxsize:*/ NULL);
Packit 8ea169
    free(core_backtrace_path);
Packit 8ea169
Packit 8ea169
    if (!json)
Packit 8ea169
        return NULL;
Packit 8ea169
Packit 8ea169
    struct sr_core_stacktrace *stacktrace = core_stacktrace_from_core_json(json);
Packit 8ea169
    free(json);
Packit 8ea169
Packit 8ea169
    if (!stacktrace)
Packit 8ea169
        return NULL;
Packit 8ea169
Packit 8ea169
    struct sr_core_thread *thread = core_thread_from_core_stacktrace(stacktrace);
Packit 8ea169
Packit 8ea169
    if (!thread)
Packit 8ea169
    {
Packit 8ea169
        sr_core_stacktrace_free(stacktrace);
Packit 8ea169
        return NULL;
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    void *build_id_list = NULL;
Packit 8ea169
Packit 8ea169
    struct strbuf *strbuf = strbuf_new();
Packit 8ea169
    for (struct sr_core_frame *frame = thread->frames;
Packit 8ea169
         frame;
Packit 8ea169
         frame = frame->next)
Packit 8ea169
    {
Packit 8ea169
        if (frame->build_id)
Packit 8ea169
            build_id_list = g_list_prepend(build_id_list, frame->build_id);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    build_id_list = g_list_sort(build_id_list, (GCompareFunc)strcmp);
Packit 8ea169
    for (GList *iter = build_id_list; iter; iter = g_list_next(iter))
Packit 8ea169
    {
Packit 8ea169
        GList *next = g_list_next(iter);
Packit 8ea169
        if (next == NULL || 0 != strcmp(iter->data, next->data))
Packit 8ea169
        {
Packit 8ea169
            strbuf = strbuf_append_strf(strbuf, "%s\n", (char *)iter->data);
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
    g_list_free(build_id_list);
Packit 8ea169
    sr_core_stacktrace_free(stacktrace);
Packit 8ea169
Packit 8ea169
    return strbuf_free_nobuf(strbuf);
Packit 8ea169
}
Packit 8ea169
Packit 8ea169
int main(int argc, char **argv)
Packit 8ea169
{
Packit 8ea169
    /* I18n */
Packit 8ea169
    setlocale(LC_ALL, "");
Packit 8ea169
#if ENABLE_NLS
Packit 8ea169
    bindtextdomain(PACKAGE, LOCALEDIR);
Packit 8ea169
    textdomain(PACKAGE);
Packit 8ea169
#endif
Packit 8ea169
Packit 8ea169
    abrt_init(argv);
Packit 8ea169
Packit 8ea169
    const char *dump_dir_name = ".";
Packit 8ea169
Packit 8ea169
    /* Can't keep these strings/structs static: _() doesn't support that */
Packit 8ea169
    const char *program_usage_string = _(
Packit 8ea169
        "& [-v] -d DIR\n"
Packit 8ea169
        "\n"
Packit 8ea169
        "Calculates and saves UUID of coredump in problem directory DIR"
Packit 8ea169
    );
Packit 8ea169
    enum {
Packit 8ea169
        OPT_v = 1 << 0,
Packit 8ea169
        OPT_d = 1 << 1,
Packit 8ea169
    };
Packit 8ea169
    /* Keep enum above and order of options below in sync! */
Packit 8ea169
    struct options program_options[] = {
Packit 8ea169
        OPT__VERBOSE(&g_verbose),
Packit 8ea169
        OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Problem directory")),
Packit 8ea169
        OPT_END()
Packit 8ea169
    };
Packit 8ea169
    /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
Packit 8ea169
Packit 8ea169
    export_abrt_envvars(0);
Packit 8ea169
Packit 8ea169
    char *unstrip_n_output = NULL;
Packit 8ea169
    char *coredump_path = xasprintf("%s/"FILENAME_COREDUMP, dump_dir_name);
Packit 8ea169
    if (access(coredump_path, R_OK) == 0)
Packit 8ea169
        unstrip_n_output = run_unstrip_n(dump_dir_name, /*timeout_sec:*/ 30);
Packit 8ea169
Packit 8ea169
    free(coredump_path);
Packit 8ea169
Packit 8ea169
    if (unstrip_n_output)
Packit 8ea169
    {
Packit 8ea169
        /* Run unstrip -n and trim its output, leaving only sizes and build ids */
Packit 8ea169
        /* modifies unstrip_n_output in-place: */
Packit 8ea169
        trim_unstrip_output(unstrip_n_output, unstrip_n_output);
Packit 8ea169
    }
Packit 8ea169
    else
Packit 8ea169
    {
Packit 8ea169
        /* bad dump_dir_name, can't run unstrip, etc...
Packit 8ea169
         * or maybe missing coredump - try generating it from core_backtrace
Packit 8ea169
         */
Packit 8ea169
Packit 8ea169
        unstrip_n_output = build_ids_from_core_backtrace(dump_dir_name);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    /* Hash package + executable + unstrip_n_output and save it as UUID */
Packit 8ea169
Packit 8ea169
    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
Packit 8ea169
    if (!dd)
Packit 8ea169
        return 1;
Packit 8ea169
Packit 8ea169
    char *executable = dd_load_text(dd, FILENAME_EXECUTABLE);
Packit 8ea169
    /* FILENAME_PACKAGE may be missing if ProcessUnpackaged = yes... */
Packit 8ea169
    char *package = dd_load_text_ext(dd, FILENAME_PACKAGE, DD_FAIL_QUIETLY_ENOENT);
Packit 8ea169
    /* Package variable has "firefox-3.5.6-1.fc11[.1]" format */
Packit 8ea169
    /* Remove distro suffix and maybe least significant version number */
Packit 8ea169
    char *p = package;
Packit 8ea169
    while (*p)
Packit 8ea169
    {
Packit 8ea169
        if (*p == '.' && (p[1] < '0' || p[1] > '9'))
Packit 8ea169
        {
Packit 8ea169
            /* We found "XXXX.nondigitXXXX", trim this part */
Packit 8ea169
            *p = '\0';
Packit 8ea169
            break;
Packit 8ea169
        }
Packit 8ea169
        p++;
Packit 8ea169
    }
Packit 8ea169
    char *first_dot = strchr(package, '.');
Packit 8ea169
    if (first_dot)
Packit 8ea169
    {
Packit 8ea169
        char *last_dot = strrchr(first_dot, '.');
Packit 8ea169
        if (last_dot != first_dot)
Packit 8ea169
        {
Packit 8ea169
            /* There are more than one dot: "1.2.3"
Packit 8ea169
             * Strip last part, we don't want to distinguish crashes
Packit 8ea169
             * in packages which differ only by minor release number.
Packit 8ea169
             */
Packit 8ea169
            *last_dot = '\0';
Packit 8ea169
        }
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    char *string_to_hash = xasprintf("%s%s%s", package, executable, unstrip_n_output);
Packit 8ea169
    /*free(package);*/
Packit 8ea169
    /*free(executable);*/
Packit 8ea169
    /*free(unstrip_n_output);*/
Packit 8ea169
Packit 8ea169
    log_debug("String to hash: %s", string_to_hash);
Packit 8ea169
Packit 8ea169
    char hash_str[SHA1_RESULT_LEN*2 + 1];
Packit 8ea169
    str_to_sha1str(hash_str, string_to_hash);
Packit 8ea169
Packit 8ea169
    dd_save_text(dd, FILENAME_UUID, hash_str);
Packit 8ea169
Packit 8ea169
    /* Create crash_function element from core_backtrace */
Packit 8ea169
    char *core_backtrace_json = dd_load_text_ext(dd, FILENAME_CORE_BACKTRACE,
Packit 8ea169
                                                 DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE);
Packit 8ea169
    if (core_backtrace_json)
Packit 8ea169
    {
Packit 8ea169
        struct sr_core_stacktrace *stacktrace = core_stacktrace_from_core_json(core_backtrace_json);
Packit 8ea169
        free(core_backtrace_json);
Packit 8ea169
Packit 8ea169
        if (!stacktrace)
Packit 8ea169
            goto next;
Packit 8ea169
Packit 8ea169
        struct sr_core_thread *thread = core_thread_from_core_stacktrace(stacktrace);
Packit 8ea169
Packit 8ea169
        if (!thread)
Packit 8ea169
            goto next;
Packit 8ea169
Packit 8ea169
        sr_normalize_core_thread(thread);
Packit 8ea169
Packit 8ea169
        struct sr_core_frame *frame = thread->frames;
Packit 8ea169
        if (frame->function_name)
Packit 8ea169
            dd_save_text(dd, FILENAME_CRASH_FUNCTION, frame->function_name);
Packit 8ea169
Packit 8ea169
next:
Packit 8ea169
        /* can be NULL */
Packit 8ea169
        sr_core_stacktrace_free(stacktrace);
Packit 8ea169
    }
Packit 8ea169
Packit 8ea169
    dd_close(dd);
Packit 8ea169
Packit 8ea169
    return 0;
Packit 8ea169
}