|
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 |
}
|