/* * $Id$ * * Ecrasher, a crash handler tool * Part of Equinox Desktop Environment (EDE). * Copyright (c) 2008 EDE Authors. * * This program is licensed under terms of the * GNU General Public License version 2 or newer. * See COPYING for details. */ #ifdef HAVE_CONFIG_H #include #endif #include "icons/core.xpm" #include "CrashDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define DIALOG_W 380 #define DIALOG_H 130 #define DIALOG_W_EXPANDED 380 #define DIALOG_H_EXPANDED 340 int spawn_backtrace(const char* gdb_path, const char* program, const char* core, const char* output, const char* script) { const char* gdb_script = "bt\nquit\n"; const int gdb_script_len = 8; // file with gdb commands int sfd = open(script, O_WRONLY | O_TRUNC | O_CREAT, 0770); if(sfd == -1) return -1; write(sfd, gdb_script, gdb_script_len); close(sfd); // output file with gdb backtrace int ofd = open(output, O_WRONLY | O_TRUNC | O_CREAT, 0770); if(ofd == -1) return -1; pid_t pid = fork(); if(pid == -1) { close(ofd); return -1; } else if(pid == 0) { dup2(ofd, 1); close(ofd); char* argv[8]; argv[0] = (char*)gdb_path; argv[1] = "--quiet"; argv[2] = "--batch"; argv[3] = "-x"; argv[4] = (char*)script; argv[5] = (char*)program; argv[6] = (char*)core; argv[7] = 0; execvp(argv[0], argv); return -1; } else { int status; if(waitpid(pid, &status, 0) != pid) return -1; } return 0; } edelib::String get_uname(void) { struct utsname ut; uname(&ut); edelib::String ret; ret.printf("%s %s %s %s %s", ut.sysname, ut.nodename, ut.release, ut.version, ut.machine); return ret; } void show_details_cb(Fl_Widget*, void* cd) { CrashDialog* c = (CrashDialog*)cd; c->show_details(); } void close_cb(Fl_Widget*, void* cd) { CrashDialog* c = (CrashDialog*)cd; c->hide(); } void save_cb(Fl_Widget*, void* cd) { CrashDialog* c = (CrashDialog*)cd; c->save(); } CrashDialog::CrashDialog() : Fl_Window(DIALOG_W, DIALOG_H, _("EDE crash handler")), appname(NULL), apppath(NULL), bugaddress(NULL), pid(NULL), signal_num(NULL) { details_shown = false; begin(); pix = new Fl_Pixmap((const char**)core_xpm); icon_box = new Fl_Box(10, 10, 70, 75); icon_box->image(pix); txt_box = new Fl_Box(85, 10, 285, 75); txt_box->align(FL_ALIGN_WRAP | FL_ALIGN_LEFT | FL_ALIGN_INSIDE); close = new Fl_Button(280, 95, 90, 25, _("&Close")); close->callback(close_cb, this); details = new Fl_Button(10, 95, 265, 25, _("@> Show details")); details->box(FL_FLAT_BOX); details->align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT); details->callback(show_details_cb, this); // widgets for expanded dialog trace_log = new Fl_Text_Display(10, 130, 360, 165); trace_buff = new Fl_Text_Buffer(); trace_log->buffer(trace_buff); trace_log->hide(); save_as = new Fl_Button(280, 305, 90, 25, _("&Save As...")); save_as->callback(save_cb, this); save_as->hide(); end(); } CrashDialog::~CrashDialog() { // looks like fltk does not clean image() assigned data delete pix; } void CrashDialog::show_details(void) { if(trace_log->visible()) { trace_log->hide(); save_as->hide(); details->label(_("@> Show details")); size(DIALOG_W, DIALOG_H); } else { trace_log->show(); save_as->show(); details->label(_("@< Hide details")); size(DIALOG_W_EXPANDED, DIALOG_H_EXPANDED); if(!details_shown) { trace_buff->remove(0, trace_buff->length()); edelib::String address = _("\nPlease report this at: "); if(bugaddress) address += bugaddress; else address += "bugs@equinox-project.org"; trace_buff->append(address.c_str()); trace_buff->append("\n\n"); trace_buff->append("---------- short summary ----------\n"); trace_buff->append("\nEDE version: " PACKAGE_VERSION); trace_buff->append("\nSystem info: "); trace_buff->append(get_uname().c_str()); trace_buff->append("\nProgram name: "); if(appname) trace_buff->append(appname); else trace_buff->append("(unknown)"); trace_buff->append("\nExecutable path: "); if(apppath) trace_buff->append(apppath); else trace_buff->append("(unknown)"); trace_buff->append("\nRunning PID: "); if(pid) trace_buff->append(pid); else trace_buff->append("(unknown)"); trace_buff->append("\nSignal received: "); if(signal_num) trace_buff->append(signal_num); else trace_buff->append("(unknown)"); // try backtrace via gdb trace_buff->append("\n\n---------- backtrace ----------\n"); const char* core_file = "core"; if(!edelib::file_exists(core_file)) { trace_buff->append("\nUnable to find 'core' file. Backtrace will not be done."); details_shown = false; return; } edelib::String gdb_path = edelib::file_path("gdb"); if(gdb_path.empty()) { trace_buff->append("\nUnable to find gdb. Is it installed ?"); // set to false so next 'Show Details' click can try again with the debugger details_shown = false; return; } // check if we can write in /tmp; if not, try with $HOME edelib::String dir = "/tmp"; if(!edelib::dir_writeable(dir.c_str())) { dir = edelib::dir_home(); if(!edelib::dir_writeable(dir.c_str())) { trace_buff->append("\nDon't have permissions to write either to /tmp or $HOME"); details_shown = false; return; } } edelib::String gdb_output, gdb_script; gdb_output = gdb_script = dir; // TODO: these files should be unique per session gdb_output += "/.gdb_output"; gdb_script += "/.gdb_script"; if(spawn_backtrace(gdb_path.c_str(), cmd.c_str(), core_file, gdb_output.c_str(), gdb_script.c_str()) == -1) { trace_buff->append("\nUnable to properly execute gdb"); details_shown = false; return; } if(!edelib::file_exists(gdb_output.c_str())) { trace_buff->append("\nStrange, can't find gdb output that I was just wrote to"); details_shown = false; return; } trace_buff->appendfile(gdb_output.c_str()); edelib::file_remove(gdb_output.c_str()); edelib::file_remove(gdb_script.c_str()); edelib::file_remove(core_file); details_shown = true; } } } void CrashDialog::save(void) { const char* p = fl_file_chooser(_("Save details to..."), "Text Files (*.txt)\tAll Files(*)", "dump.txt"); if(!p) return; // so we can have EOL in file trace_buff->append("\n"); if(trace_buff->savefile(p) != 0) edelib::alert(_("Unable to save to %s. Please check permissions to write in this directory or file"), p); } void CrashDialog::run(void) { edelib::String l; if(appname || apppath) { const char* p = (appname ? appname : apppath); l.printf(_("Program '%s' just crashed!"), p); } else l += _("Program just crashed!"); l += _("\n\nYou can inspect details about this crash by clicking on 'Show details' below"); txt_box->copy_label(l.c_str()); if(!shown()) show(); while(shown()) Fl::wait(); }