Beispiel #1
0
struct sr_core_stacktrace *
sr_core_stacktrace_from_json(struct sr_json_value *root,
                             char **error_message)
{
    if (!JSON_CHECK_TYPE(root, SR_JSON_OBJECT, "stacktrace"))
        return NULL;

    struct sr_core_stacktrace *result = sr_core_stacktrace_new();

    bool success =
        JSON_READ_UINT16(root, "signal", &result->signal) &&
        JSON_READ_STRING(root, "executable", &result->executable) &&
        JSON_READ_BOOL(root, "only_crash_thread", &result->only_crash_thread);

    if (!success)
        goto fail;

    /* Read threads. */
    struct sr_json_value *stacktrace = json_element(root, "stacktrace");
    if (stacktrace)
    {
        if (!JSON_CHECK_TYPE(stacktrace, SR_JSON_ARRAY, "stacktrace"))
            goto fail;

        struct sr_json_value *json_thread;
        FOR_JSON_ARRAY(stacktrace, json_thread)
        {
            struct sr_core_thread *thread = sr_core_thread_from_json(json_thread,
                    error_message);

            if (!thread)
                goto fail;

            struct sr_json_value *crash_thread = json_element(json_thread, "crash_thread");
            if (crash_thread)
            {
                if (!JSON_CHECK_TYPE(crash_thread, SR_JSON_BOOLEAN, "crash_thread"))
                {
                    sr_core_thread_free(thread);
                    goto fail;
                }

                if (crash_thread->u.boolean)
                    result->crash_thread = thread;
            }

            result->threads = sr_core_thread_append(result->threads, thread);
        }
    }

    return result;

fail:
    sr_core_stacktrace_free(result);
    return NULL;
}
Beispiel #2
0
static int
unwind_thread(Dwfl_Thread *thread, void *data)
{
    struct thread_callback_arg *thread_arg = data;
    char **error_msg = &thread_arg->error_msg;

    struct sr_core_thread *result = sr_core_thread_new();
    if (!result)
    {
        set_error("Failed to initialize stacktrace memory");
        return DWARF_CB_ABORT;
    }
    result->id = (int64_t)dwfl_thread_tid(thread);

    struct frame_callback_arg frame_arg =
    {
        .thread = result,
        .error_msg = NULL
    };

    int ret = dwfl_thread_getframes(thread, frame_callback, &frame_arg);
    if (ret == -1)
    {
        warn("dwfl_thread_getframes failed for thread id %d: %s",
             (int)result->id, dwfl_errmsg(-1));
    }
    else if (ret == DWARF_CB_ABORT)
    {
        *error_msg = frame_arg.error_msg;
        goto abort;
    }
    else if (ret != 0 && ret != CB_STOP_UNWIND)
    {
        *error_msg = sr_strdup("Unknown error in dwfl_thread_getframes");
        goto abort;
    }

    if (!error_msg && !frame_arg.thread->frames)
    {
        set_error("No frames found for thread id %d", (int)result->id);
        goto abort;
    }

    thread_arg->stacktrace->threads =
        sr_core_thread_append(thread_arg->stacktrace->threads, result);
    return DWARF_CB_OK;

abort:
    sr_core_thread_free(result);
    return DWARF_CB_ABORT;
}
Beispiel #3
0
struct sr_core_stacktrace *
sr_core_stacktrace_from_gdb(const char *gdb_output, const char *core_file,
                            const char *exe_file, char **error_msg)
{
    /* I'm not going to rewrite it now since the function is not being used. */
    assert(error_msg);
    /* Initialize error_msg to 'no error'. */
    *error_msg = NULL;

    struct core_handle *ch = open_coredump(core_file, exe_file, error_msg);
    if (*error_msg)
        return NULL;

    struct sr_gdb_stacktrace *gdb_stacktrace;
    struct sr_location location;
    sr_location_init(&location);

    gdb_stacktrace = sr_gdb_stacktrace_parse(&gdb_output, &location);
    if (!gdb_stacktrace)
    {
        *error_msg = sr_location_to_string(&location);
        core_handle_free(ch);
        return NULL;
    }

    struct sr_core_stacktrace *core_stacktrace = sr_core_stacktrace_new();

    for (struct sr_gdb_thread *gdb_thread = gdb_stacktrace->threads;
         gdb_thread;
         gdb_thread = gdb_thread->next)
    {
        struct sr_core_thread *core_thread = sr_core_thread_new();

        for (struct sr_gdb_frame *gdb_frame = gdb_thread->frames;
             gdb_frame;
             gdb_frame = gdb_frame->next)
        {
            if (gdb_frame->signal_handler_called)
                continue;

            struct sr_core_frame *core_frame = resolve_frame(ch->dwfl,
                    gdb_frame->address, false);

            core_thread->frames = sr_core_frame_append(core_thread->frames,
                    core_frame);
        }

        if (sr_gdb_stacktrace_find_crash_thread(gdb_stacktrace) == gdb_thread)
        {
            core_stacktrace->crash_thread = core_thread;
        }

        core_stacktrace->threads = sr_core_thread_append(
                core_stacktrace->threads, core_thread);
    }

    core_stacktrace->signal = get_signal_number(ch->eh, core_file);
    core_stacktrace->executable = realpath(exe_file, NULL);

    core_handle_free(ch);
    sr_gdb_stacktrace_free(gdb_stacktrace);
    return core_stacktrace;
}
Beispiel #4
0
struct sr_core_stacktrace *
sr_core_stacktrace_create(const char *gdb_stacktrace_text,
                          const char *unstrip_text,
                          const char *executable_path)
{
    // Parse the GDB stacktrace.
    struct sr_location location;
    sr_location_init(&location);

    struct sr_gdb_stacktrace *gdb_stacktrace =
        sr_gdb_stacktrace_parse(&gdb_stacktrace_text, &location);

    if (!gdb_stacktrace)
    {
        warn("Unable to parse stacktrace: %d:%d: %s\n",
             location.line, location.column, location.message);

        return NULL;
    }

    // Parse the unstrip output.
    struct sr_unstrip_entry *unstrip = sr_unstrip_parse(unstrip_text);
    if (!unstrip)
    {
        warn("Unable to parse unstrip output.");

        sr_gdb_stacktrace_free(gdb_stacktrace);
        return NULL;
    }

    // Create the core stacktrace
    struct sr_core_stacktrace *core_stacktrace =
        sr_core_stacktrace_new();

    struct sr_gdb_thread *gdb_thread = gdb_stacktrace->threads;
    while (gdb_thread)
    {
        struct sr_core_thread *core_thread = sr_core_thread_new();

        struct sr_gdb_frame *gdb_frame = gdb_thread->frames;
        while (gdb_frame)
        {
            gdb_frame = gdb_frame->next;

            struct sr_core_frame *core_frame = sr_core_frame_new();
            core_frame->address = gdb_frame->address;

            struct sr_unstrip_entry *unstrip_entry =
                sr_unstrip_find_address(unstrip, gdb_frame->address);

            if (unstrip_entry)
            {
                core_frame->build_id = sr_strdup(unstrip_entry->build_id);
                core_frame->build_id_offset = core_frame->address - unstrip_entry->start;
                core_frame->file_name = sr_strdup(unstrip_entry->file_name);
            }

            if (gdb_frame->function_name &&
                0 != strcmp(gdb_frame->function_name, "??"))
            {
                core_frame->function_name =
                    sr_strdup(gdb_frame->function_name);
            }

            core_thread->frames = sr_core_frame_append(core_thread->frames, core_frame);
        }

        core_stacktrace->threads =
            sr_core_thread_append(core_stacktrace->threads,
                                  core_thread);

        gdb_thread = gdb_thread->next;
    }

    sr_unstrip_free(unstrip);
    sr_gdb_stacktrace_free(gdb_stacktrace);
    return core_stacktrace;
}
Beispiel #5
0
struct sr_core_stacktrace *
sr_parse_coredump(const char *core_file,
                   const char *exe_file,
                   char **error_msg)
{
    struct sr_core_stacktrace *stacktrace = NULL;

    /* Initialize error_msg to 'no error'. */
    if (error_msg)
        *error_msg = NULL;

    struct core_handle *ch = open_coredump(core_file, exe_file, error_msg);
    if (*error_msg)
        return NULL;

    unw_addr_space_t as;
    struct UCD_info *ui;
    as = unw_create_addr_space(&_UCD_accessors, 0);
    if (!as)
    {
        set_error("Failed to create address space");
        goto fail_destroy_handle;
    }

    ui = _UCD_create(core_file);
    if (!ui)
    {
        set_error("Failed to set up core dump accessors for '%s'", core_file);
        goto fail_destroy_as;
    }

    struct exe_mapping_data *s;
    for (s = ch->segments; s != NULL; s = s->next)
    {
        if (_UCD_add_backing_file_at_vaddr(ui, s->start, s->filename) < 0)
        {
            /* Sometimes produces:
             * >_UCD_add_backing_file_at_segment:
             * Error reading from '/usr/lib/modules/3.6.9-2.fc17.x86_64/vdso/vdso.so'
             * Ignore errors for now & fail later.
             */
            warn("Can't add backing file '%s' at addr 0x%jx", s->filename,
                 (uintmax_t)s->start);
            /* goto fail_destroy_ui; */
        }
    }

    stacktrace = sr_core_stacktrace_new();

    int tnum, nthreads = _UCD_get_num_threads(ui);
    for (tnum = 0; tnum < nthreads; ++tnum)
    {
        struct sr_core_thread *trace = unwind_thread(ui, as, ch->dwfl, tnum, error_msg);
        if (trace)
        {
            stacktrace->threads = sr_core_thread_append(stacktrace->threads, trace);
        }
        else
        {
            sr_core_stacktrace_free(stacktrace);
            stacktrace = NULL;
            break;
        }
    }

    stacktrace->executable = realpath(exe_file, NULL);
    stacktrace->signal = get_signal_number_libunwind(ui);
    /* FIXME: is this the best we can do? */
    stacktrace->crash_thread = stacktrace->threads;

    _UCD_destroy(ui);
fail_destroy_as:
    unw_destroy_addr_space(as);
fail_destroy_handle:
    core_handle_free(ch);

    return stacktrace;
}