/* 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; }
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; } }