home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / exit.C < prev    next >
C/C++ Source or Header  |  1998-11-29  |  27KB  |  1,111 lines

  1. // $Id: exit.C,v 1.79 1998/11/29 11:26:12 zeller Exp $ -*- C++ -*-
  2. // Exit DDD (including fatal exits)
  3.  
  4. // Copyright (C) 1996-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char exit_rcsid[] = 
  30.     "$Id: exit.C,v 1.79 1998/11/29 11:26:12 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. // Debug the Debugger
  37. // ------------------
  38. // 
  39. // I appreciate that the April 1997 _Communications_
  40. // covers the issue of debugging
  41. // and hope we programmers can be relieved
  42. // from the pain of debugging
  43. // with the help of advanced tools.
  44. // However, all I'm looking for today
  45. // is a well-debugged debugger.
  46. // I have been working with C/C++ debuggers
  47. // from several Unix vendors
  48. // as well as a Microsoft Windows-based debugger.
  49. // All of them more than occasionally fail
  50. // to perform some very basic function,
  51. // such as setting a break point,
  52. // displaying a data structure,
  53. // evaluating an expression,
  54. // and looking up a symbol.
  55. // Some of them simply crash too often.
  56. //
  57. // My observation is that the debugger
  58. // is often buggier
  59. // than the program being debugged.
  60. // One debugging difficulty Eisenstadt points out
  61. // is the inability of the debugging tool.
  62. // This is certainly true.
  63. // I should emphasize that the difficulty
  64. // is often a debugger-reliability problem
  65. // rather than a capability problem.
  66. //
  67. // W. QUOCK, San Mateo, California
  68. // Communications of the ACM, August 1997/Vol.40, No. 8, p. 31
  69.  
  70. #include "exit.h"
  71.  
  72. #include "AgentM.h"
  73. #include "AppData.h"
  74. #include "DestroyCB.h"
  75. #include "ExitCB.h"
  76. #include "GDBAgent.h"
  77. #include "HelpCB.h"
  78. #include "TimeOut.h"
  79. #include "charsets.h"
  80. #include "Command.h"
  81. #include "cmdtty.h"
  82. #include "converters.h"
  83. #include "ddd.h"
  84. #include "exectty.h"
  85. #include "findParent.h"
  86. #include "filetype.h"
  87. #include "fonts.h"
  88. #include "history.h"
  89. #include "host.h"
  90. #include "longName.h"
  91. #include "mainloop.h"
  92. #include "misc.h"
  93. #include "options.h"
  94. #include "post.h"
  95. #include "question.h"
  96. #include "session.h"
  97. #include "shell.h"
  98. #include "sigName.h"
  99. #include "status.h"
  100. #include "string-fun.h"
  101. #include "verify.h"
  102. #include "version.h"
  103. #include "windows.h"
  104. #include "wm.h"
  105.  
  106. #include <signal.h>
  107. #include <iostream.h>
  108. #include <ctype.h>
  109. #include <errno.h>
  110. #include <sys/wait.h>
  111. #include <stdlib.h>
  112. #include <stdio.h>
  113.  
  114. #include <Xm/Xm.h>
  115. #include <Xm/MessageB.h>
  116. #include <Xm/PushB.h>
  117. #include <Xm/Text.h>
  118.  
  119. #if HAVE_RAISE
  120. #if !HAVE_RAISE_DECL
  121. extern int raise(int sig);
  122. #endif
  123. #else // !HAVE_RAISE
  124. #define raise(sig) kill(getpid(), sig)
  125. #endif
  126.  
  127. static void ddd_signal(int sig...);
  128. static void ddd_fatal(int sig...);
  129. static bool ddd_dump_core(int sig...);
  130. static void debug_ddd(bool core_dumped = true);
  131.  
  132. // True if GDB is about to exit
  133. bool gdb_is_exiting = false;
  134.  
  135. // True if DDD is about to exit
  136. bool ddd_is_exiting = false;
  137.  
  138. // True if DDD is about to restart
  139. bool ddd_is_restarting = false;
  140.  
  141. // True if DDD has crashed and needs restarting
  142. bool ddd_has_crashed = false;
  143.  
  144. static void DDDDoneAnywayCB(Widget w, XtPointer client_data, 
  145.                 XtPointer call_data);
  146.  
  147. //-----------------------------------------------------------------------------
  148. // General clean-up actions before exiting DDD
  149. //-----------------------------------------------------------------------------
  150.  
  151. // Some clean-up actions before exiting
  152. void ddd_cleanup()
  153. {
  154.     if (ddd_is_exiting)
  155.     return;
  156.  
  157.     ddd_is_exiting = true;
  158.  
  159.     kill_exec_tty();
  160.     if (gdb)
  161.     {
  162.     gdb->shutdown();
  163.     gdb->terminate(true);
  164.     }
  165.  
  166.     if (gdb_initialized)
  167.     {
  168.     // If GDB created a history file, overwrite it such that it is not
  169.     // cluttered with internal DDD commands.
  170.     if (is_regular_file(gdb_history_file()))
  171.         save_history(gdb_history_file(), command_shell);
  172.  
  173.     // Now write the session-specific DDD history file.
  174.     if (app_data.save_history_on_exit)
  175.         save_history(session_history_file(app_data.session));
  176.     }
  177.  
  178.     // Famous last words
  179.     string last_words = "Thanks for using " DDD_NAME " " DDD_VERSION "!";
  180.     if (ddd_has_crashed)
  181.     last_words += "  (We apologize for the inconvenience.)";
  182.     set_status_mstring(rm(last_words));
  183.  
  184.     // Flush log file
  185.     dddlog.flush();
  186.  
  187.     // Unlock `~/.ddd/'.
  188.     unlock_session_dir(DEFAULT_SESSION);
  189. }
  190.  
  191.  
  192. //-----------------------------------------------------------------------------
  193. // Signal handling
  194. //-----------------------------------------------------------------------------
  195.  
  196. // Setup signals: Cleanup on termination
  197. void ddd_install_signal()
  198. {
  199. #ifdef SIGHUP
  200.     signal(SIGHUP, SignalProc(ddd_signal));
  201. #endif
  202.  
  203. #ifdef SIGTERM
  204.     signal(SIGTERM, SignalProc(ddd_signal));
  205. #endif
  206.  
  207. #ifdef SIGQUIT
  208.     signal(SIGQUIT, SignalProc(ddd_signal));
  209. #endif
  210. }
  211.  
  212. // Setup signals: Issue message on fatal errors
  213. void ddd_install_fatal(char * /* program_name */)
  214. {
  215.     // Make sure strsignal() is initialized properly
  216.     (void)sigName(1);
  217.  
  218.     // Install signal handlers
  219. #ifdef SIGINT
  220.     signal(SIGINT, SignalProc(ddd_fatal));
  221. #endif
  222.  
  223. #ifdef SIGFPE
  224.     signal(SIGFPE, SignalProc(ddd_fatal));
  225. #endif
  226.  
  227. #ifdef SIGILL
  228.     signal(SIGILL, SignalProc(ddd_fatal));
  229. #endif
  230.  
  231. #ifdef SIGSEGV
  232.     signal(SIGSEGV, SignalProc(ddd_fatal));
  233. #endif
  234.  
  235. #ifdef SIGBUS
  236.     signal(SIGBUS, SignalProc(ddd_fatal));
  237. #endif
  238.  
  239. #ifdef SIGABRT
  240.     signal(SIGABRT, SignalProc(ddd_fatal));
  241. #endif
  242.  
  243. #ifdef SIGIOT
  244.     signal(SIGIOT, SignalProc(ddd_fatal));
  245. #endif
  246.  
  247. #ifdef SIGTRAP
  248.     signal(SIGTRAP, SignalProc(ddd_fatal));
  249. #endif
  250.  
  251. #ifdef SIGEMT
  252.     signal(SIGEMT, SignalProc(ddd_fatal));
  253. #endif
  254.  
  255. #ifdef SIGSYS
  256.     signal(SIGSYS, SignalProc(ddd_fatal));
  257. #endif
  258.  
  259. #ifdef SIGUSR1
  260.     signal(SIGUSR1, SignalProc(ddd_dump_core));
  261. #endif
  262. }
  263.  
  264. // Post a dialog containing TITLE and CAUSE
  265. static void post_fatal(string title, string cause, string cls, 
  266.                bool core_dumped = false)
  267. {
  268.     (void) core_dumped;        // Use it
  269.  
  270.     static Widget fatal_dialog = 0;
  271.  
  272. #if DEBUG_BUTTON
  273.     static Widget debug = 0;
  274. #endif
  275.  
  276.     if (fatal_dialog == 0)
  277.     {
  278.     fatal_dialog = verify(XmCreateErrorDialog (find_shell(),
  279.                            "fatal_dialog", 0, 0));
  280.     Delay::register_shell(fatal_dialog);
  281.  
  282.     XtAddCallback(fatal_dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  283.     XtAddCallback(fatal_dialog, XmNokCallback, 
  284.               DDDRestartCB, XtPointer(EXIT_FAILURE));
  285.  
  286. #if XmVersion >= 1002
  287.     Widget exit = verify(XmCreatePushButton(fatal_dialog, "exit", 0, 0));
  288.     XtManageChild(exit);
  289.     XtAddCallback(exit, XmNactivateCallback,
  290.               DDDExitCB, XtPointer(EXIT_FAILURE));
  291.  
  292. #if DEBUG_BUTTON
  293.     debug = verify(XmCreatePushButton(fatal_dialog, "debug", 0, 0));
  294.     XtAddCallback(debug, XmNactivateCallback, DDDDebugCB, XtPointer(True));
  295. #endif
  296.  
  297. #endif
  298.     }
  299.  
  300. #if DEBUG_BUTTON
  301.     if (core_dumped)
  302.     XtManageChild(debug);
  303.     else
  304.     XtUnmanageChild(debug);
  305. #endif
  306.  
  307.     defineConversionMacro("CLASS", cls);
  308.     defineConversionMacro("TITLE", title);
  309.     defineConversionMacro("CAUSE", cause);
  310.  
  311.     string msg = cls + ": " + title;
  312.     MString mtext = rm(msg);
  313.     XtVaSetValues (fatal_dialog,
  314.            XmNmessageString, mtext.xmstring(),
  315.            0);
  316.  
  317.     manage_and_raise(fatal_dialog);
  318.  
  319.     // Wait until dialog is mapped and synchronize, such that DDD will
  320.     // exit if we get another signal or X error during that time.
  321.     wait_until_mapped(fatal_dialog);
  322.     XSync(XtDisplay(fatal_dialog), False);
  323. }
  324.  
  325. // Show the user that a signal has been raised
  326. void ddd_show_signal(int sig)
  327. {
  328.     bool core_dumped = false;
  329.     if (sig < 0)
  330.     {
  331.     core_dumped = true;
  332.     sig = -sig;
  333.     }
  334.  
  335.     // Show diagnostic
  336.     if (sig > 0)
  337.     {
  338.     string s = sigName(sig);
  339.     if (core_dumped)
  340.         s += " (core dumped)";
  341.     cerr << s << "\n";
  342.     }
  343.     gdb_question_running = false;
  344.  
  345.     // Interrupt current GDB action
  346.     gdb_keyboard_command = true;
  347.     gdb_command("\003", gdb_w);
  348.  
  349.     // Show the message in an error dialog,
  350.     // allowing the user to clean up manually.
  351.     if (sig > 0 && sig != SIGINT)
  352.     {
  353.     string title = sigName(sig);
  354.     string cause = "`" + title + "' signal";
  355.  
  356.     if (core_dumped)
  357.         title += " (core dumped)";
  358.  
  359.     post_fatal(title, cause, "Internal error", core_dumped);
  360.     }
  361. }
  362.  
  363. // Signal handler: clean up and re-raise signal
  364. static void ddd_signal(int sig...)
  365. {
  366.     ddd_cleanup();
  367.     signal(sig, SignalProc(SIG_DFL));
  368.     raise(sig);
  369. }
  370.  
  371. // Show the user that a C++ exception has been raised
  372. void ddd_show_exception(const char *c, const char *what)
  373. {
  374.     gdb_question_running = false;
  375.  
  376.     // Interrupt current GDB action
  377.     gdb_keyboard_command = true;
  378.     gdb_command("\003", gdb_w);
  379.  
  380.     // Show the message in an error dialog,
  381.     // allowing the user to clean up manually.
  382.  
  383.     if (what == 0)
  384.     what = "C++ exception";
  385.  
  386.     string title = what;
  387.     string cause = what;
  388.     string cls   = "Internal error";
  389.  
  390.     // EGCS 1.0 prepends the name length to type names; fix this
  391.     while (isdigit(*c))
  392.     c++;
  393.  
  394.     if (c != 0)
  395.     cls = c;
  396.  
  397.     cls.gsub('_', ' ');
  398.     cls[0] = toupper(cls[0]);
  399.  
  400.     if (c != 0)
  401.     cause.prepend(cls + ": ");
  402.  
  403.     post_fatal(title, cause, cls);
  404. }
  405.  
  406. // Issue fatal message on stderr
  407. static void print_fatal_msg(char *title, char *cause, char *cls)
  408. {
  409.     static const char *msg =
  410.     "\n%s (%s).\n"
  411.     "\n"
  412.     "Oops!  You have found a bug in " DDD_NAME ".\n"
  413.     "\n"
  414.     "If you can reproduce this bug, please send a bug report\n"
  415.     "to <ddd-bugs@ips.cs.tu-bs.de>, giving a subject like\n"
  416.     "\n"
  417.     "    " DDD_NAME " " DDD_VERSION 
  418.     " (" DDD_HOST ") gets %s\n"
  419.     "\n"
  420.     "To enable us to fix the bug, you should include "
  421.     "the following information:\n"
  422.     "* What you were doing to get this message.  Report all the facts.\n"
  423.     "* The contents of the `~/." ddd_NAME "/log' file "
  424.     "as generated by this session.\n"
  425.     "Please read also the section \"Reporting Bugs\" "
  426.     "in the " DDD_NAME " manual.\n"
  427.     "\n"
  428.     "We thank you for your support.\n\n";
  429.  
  430.     fprintf(stderr, msg, cls, title, cause);
  431. }
  432.  
  433. // Fatal signal handler: issue error message and re-raise signal
  434. static void ddd_fatal(int sig...)
  435. {
  436.     // IF YOU GET HERE WHILE DEBUGGING DDD, READ THIS
  437.     // ----------------------------------------------
  438.     //
  439.     // On some systems, especially HP-UX, the stack frame gets
  440.     // corrupted when a program exits via a signal handler - one
  441.     // cannot determine the place the signal handler was called from,
  442.     // which makes debugging impossible.
  443.     // 
  444.     // You can circumvent this problem by invoking the debugged DDD
  445.     // process with the environment variable DDD_NO_SIGNAL_HANDLERS
  446.     // set.  This disables installation of the `ddd_fatal' signal
  447.     // handler and makes it possible for you to determine the problem
  448.     // cause.
  449.  
  450.     if (sig != SIGINT)
  451.     ddd_has_crashed = true;
  452.  
  453.     // Make sure we have a ~/.ddd/log file
  454.     init_dddlog();
  455.  
  456.     dddlog << "!  " << sigName(sig) << "\n";
  457.     dddlog.flush();
  458.  
  459.     // Reinstall fatal error handlers (for SVR4 and others)
  460.     ddd_install_fatal();
  461.  
  462.     static int fatal_entered = 0;
  463.     fatal_entered++;
  464.  
  465.     bool have_core_file = false;
  466.     if (fatal_entered == 1 && sig != SIGINT && 
  467.     (!main_loop_entered || app_data.dump_core))
  468.     {
  469.     // Create core file (without interrupting DDD)
  470.     if (ddd_dump_core(sig))
  471.     {
  472.         // We are the child: return to originating sequence such
  473.         // that we can dump core.
  474.         return;
  475.     }
  476.  
  477.     have_core_file = is_core_file("core");
  478.     }
  479.  
  480.     if (fatal_entered > 1 || !main_loop_entered || ddd_is_exiting)
  481.     {
  482.     // We're done.  Suicide is painless.
  483.     if (have_core_file)
  484.         report_core(dddlog);
  485.  
  486.     if (sig != SIGINT)
  487.     {
  488.         char *title = sigName(sig);
  489.         char cause[BUFSIZ];
  490.         sprintf(cause, "`%s' signal", title);
  491.         print_fatal_msg(title, cause, "Internal error");
  492.     }
  493.  
  494.     // Re-raise signal.  This should kill us as we return.
  495.     ddd_signal(sig);
  496.     return;
  497.     }
  498.  
  499.     // Return to main event loop
  500.     fatal_entered--;
  501.     goto_main_loop(have_core_file ? -sig : sig);
  502. }
  503.  
  504.  
  505. //-----------------------------------------------------------------------------
  506. // Dump core (for debugging)
  507. //-----------------------------------------------------------------------------
  508.  
  509. // Dump core using SIG; return true iff child
  510. static bool ddd_dump_core(int sig...)
  511. {
  512.     unlink("core");
  513.  
  514.     int core_pid;
  515.     if ((core_pid = fork()) == 0)
  516.     {
  517.     // I am the child: re-raise signal
  518. #if defined(SIGUSR1)
  519.     if (sig == SIGUSR1)
  520.     {
  521.         // SIGUSR1 does not cause a core dump; use abort() instead
  522. #if defined(SIGABRT)
  523.         sig = SIGABRT;
  524. #elif defined(SIGIOT)
  525.         sig = SIGIOT;
  526. #endif // defined(SIGIOT)
  527.     }
  528. #endif // defined(SIGUSR1)
  529.  
  530.     // Re-raise signal.  This should kill us as we return.
  531.     signal(sig, SignalProc(SIG_DFL));
  532.     raise(sig);
  533.     return true;
  534.     }
  535.  
  536.     if (core_pid < 0)
  537.     {
  538.     perror(ddd_NAME);
  539.     }
  540.     else
  541.     {
  542.     // Wait for the child to finish
  543.     int status;
  544.     pid_t ret = waitpid(core_pid, &status, 0);
  545.     if (ret < 0)
  546.         perror(ddd_NAME);
  547.     }
  548.  
  549. #if defined(SIGUSR1)
  550.     if (sig == SIGUSR1)
  551.     {
  552.     // Re-install handler (for SVR4 and others)
  553.     signal(sig, SignalProc(ddd_dump_core));
  554.     }
  555. #endif // defined(SIGUSR1)
  556.  
  557.     return false;
  558. }
  559.  
  560. void DDDDumpCoreCB(Widget, XtPointer, XtPointer)
  561. {
  562.     StatusDelay delay("Dumping core");
  563.  
  564. #if defined(SIGABRT)
  565.     if (ddd_dump_core(SIGABRT))
  566.     exit(EXIT_FAILURE);
  567. #elif defined(SIGIOT)
  568.     if (ddd_dump_core(SIGIOT))
  569.     exit(EXIT_FAILURE);
  570. #else
  571.     abort();
  572. #endif
  573.  
  574.     if (!is_core_file("core"))
  575.     delay.outcome = "failed";
  576. }
  577.  
  578. //-----------------------------------------------------------------------------
  579. // X I/O error
  580. //-----------------------------------------------------------------------------
  581.  
  582. static int (*old_x_fatal_handler)(Display *display) = 0;
  583.  
  584. // Fatal X I/O error handler: cleanup and issue error message
  585. static int ddd_x_fatal(Display *display)
  586. {
  587.     int saved_errno = errno;
  588.  
  589.     if (errno != EPIPE)
  590.     {
  591.     ddd_has_crashed = true;
  592.     dddlog << "!  X I/O error\n";
  593.     dddlog.flush();
  594.     }
  595.     ddd_cleanup();
  596.  
  597.     errno = saved_errno;
  598.     return old_x_fatal_handler(display);
  599. }
  600.  
  601. // Cleanup on fatal X I/O errors
  602. void ddd_install_x_fatal()
  603. {
  604.     old_x_fatal_handler = XSetIOErrorHandler(ddd_x_fatal);
  605. }
  606.  
  607.  
  608. //-----------------------------------------------------------------------------
  609. // Xt and Motif errors
  610. //-----------------------------------------------------------------------------
  611.  
  612. static void PostXtErrorCB(XtPointer client_data, XtIntervalId *)
  613. {
  614.     string *msg_ptr = (string *)client_data;
  615.     string msg = *msg_ptr;
  616.     delete msg_ptr;
  617.  
  618.     string title = msg.before('\v');
  619.     string cause = msg.after('\v');
  620.     post_fatal(title, cause, "X Toolkit Error");
  621. }
  622.  
  623. static XtAppContext xt_error_app_context = 0;
  624.  
  625. static void ddd_xt_error(String message = 0)
  626. {
  627.     ddd_has_crashed = true;
  628.  
  629.     dddlog << "!  Xt error";
  630.     if (message != 0)
  631.     dddlog << ": " << message;
  632.     dddlog << "\n";
  633.     dddlog.flush();
  634.  
  635.     if (message == 0 || message[0] == '\0')
  636.     {
  637.     // Occurred twice in a row
  638.     ddd_cleanup();
  639.     exit(EXIT_FAILURE);
  640.     }
  641.  
  642.     static int entered = 0;
  643.  
  644.     // Issue error on stderr
  645.     cerr << "Error: " << message << "\n";
  646.  
  647.     string title = message;
  648.     string cause = "Xt error";
  649.  
  650.     if (entered++ || !main_loop_entered || ddd_is_exiting)
  651.     {
  652.     print_fatal_msg(title, cause, "Xt error");
  653.     ddd_xt_error();
  654.     }
  655.  
  656.     // Issue error in dialog
  657.     string *msg_ptr = new string(title + '\v' + cause);
  658.     XtAppAddTimeOut(xt_error_app_context, 0, PostXtErrorCB, 
  659.             XtPointer(msg_ptr));
  660.  
  661.     // Return to main event loop
  662.     entered--;
  663.     goto_main_loop(-1);
  664. }
  665.  
  666. void ddd_install_xt_error(XtAppContext app_context)
  667. {
  668.     (void) XtAppSetErrorHandler(app_context, ddd_xt_error);
  669.     xt_error_app_context = app_context;
  670. }
  671.  
  672.  
  673. //-----------------------------------------------------------------------------
  674. // Other X errors
  675. //-----------------------------------------------------------------------------
  676.  
  677. static void PostXErrorCB(XtPointer client_data, XtIntervalId *)
  678. {
  679.     string *msg_ptr = (string *)client_data;
  680.     string msg = *msg_ptr;
  681.     delete msg_ptr;
  682.  
  683.     string title = msg.before('\v');
  684.     string cause = msg.after('\v');
  685.     post_fatal(title, cause, "X Error");
  686. }
  687.  
  688. static string xtext(Display *display, char *code, char *def, int arg = 0)
  689. {
  690.     char *mtype = "XlibMessage";
  691.     char format[BUFSIZ];
  692.     char message[BUFSIZ];
  693.     XGetErrorDatabaseText(display, mtype, code, def, format, sizeof format);
  694.     sprintf(message, format, arg);
  695.     return message;
  696. }
  697.  
  698. // Give a diagnostic on EVENT on OS.  Patterned after
  699. // _XPrintDefaultError(dpy, event, fp) in X11R6.3, but also issue the
  700. // widget associated with the resource (as in Netscape).
  701. static void print_x_error(Display *display, XErrorEvent *event, ostream& os)
  702. {
  703.     char buffer[BUFSIZ];
  704.     XGetErrorText(display, event->error_code, buffer, sizeof buffer);
  705.  
  706.     os << xtext(display, "XError", "X Error")
  707.        << ":  " << buffer << "\n  "
  708.        << xtext(display, "MajorCode", "Request Major code %d", 
  709.         event->request_code);
  710.     
  711.     char number[32];
  712.     sprintf(number, "%d", event->request_code);
  713.     XGetErrorDatabaseText(display, "XRequest", 
  714.               number, "", buffer, sizeof buffer);
  715.     os << " (" << buffer << ")\n";
  716.  
  717.     if (event->request_code >= 128)
  718.     os << xtext(display, "MinorCode", "Request Minor code %d\n",
  719.             event->minor_code);
  720.  
  721.     int resourceid = 0;
  722.     switch (event->error_code)
  723.     {
  724.     case BadWindow:
  725.     case BadPixmap:
  726.     case BadCursor:
  727.     case BadFont:
  728.     case BadDrawable:
  729.     case BadColor:
  730.     case BadGC:
  731.     case BadIDChoice:
  732.     case BadMatch:
  733.     os << "  "
  734.        << xtext(display, "ResourceID", "ResourceID 0x%x", 
  735.             event->resourceid)
  736.        << "\n";
  737.     resourceid = event->resourceid;
  738.     break;
  739.  
  740.     case BadValue:
  741.     os << "  "
  742.        << xtext(display, "Value", "Value 0x%x", event->resourceid)
  743.        << "\n";
  744.     break;
  745.  
  746.     case BadAtom:
  747.     os << "  "
  748.        << xtext(display, "AtomID", "AtomID 0x%x", event->resourceid)
  749.        << "\n";
  750.     break;
  751.  
  752.     case BadAccess:
  753.     case BadAlloc:
  754.     case BadImplementation:
  755.     case BadLength:
  756.     case BadName:
  757.     case BadRequest:
  758.     // Nothing special
  759.     break;
  760.     }
  761.  
  762.     os << "  " 
  763.        << xtext(display, "ErrorSerial", "Error Serial #%d", event->serial)
  764.        << "\n"
  765.        << "  "
  766.        << xtext(display, "CurrentSerial", "Current Serial #%d", 
  767.         NextRequest(display) - 1) 
  768.        << "\n";
  769.  
  770.     if (resourceid != 0)
  771.     {
  772.     os << "  Widget hierarchy of resource:  ";
  773.     Widget w = XtWindowToWidget(display, resourceid);
  774.     if (w == 0)
  775.         os << "unknown";
  776.     else
  777.         os << longName(w);
  778.     os << '\n';
  779.     }
  780.  
  781.     os.flush();
  782. }
  783.  
  784. static int (*old_x_error_handler)(Display *, XErrorEvent *) = 0;
  785.  
  786. static bool recovered_from_x_error = true;
  787.  
  788. static Boolean recovery_done(XtPointer)
  789. {
  790.     recovered_from_x_error = true;
  791.     return True;        // Remove from the list of work procs
  792. }
  793.  
  794. // X error handler: cleanup and issue error message
  795. static int ddd_x_error(Display *display, XErrorEvent *event)
  796. {
  797.     if (event->error_code == BadImplementation)
  798.     {
  799.     // Taken care of by the standard X handler - just proceed
  800.     return old_x_error_handler(display, event);
  801.     }
  802.  
  803.     ddd_has_crashed = true;
  804.  
  805.     dddlog << "!  X error\n";
  806.     dddlog.flush();
  807.  
  808.     // Fetch precise diagnostics
  809.     char buffer[BUFSIZ];
  810.     XGetErrorText(display, event->error_code, buffer, sizeof buffer);
  811.     string cause = buffer;
  812.     if (cause.contains(" ("))
  813.     cause = cause.before(" (");
  814.     cause = "`" + cause + "' error";
  815.  
  816.     string title = buffer;
  817.     if (title.contains('('))
  818.     title = title.after('(');
  819.     if (title.contains(')'))
  820.     title = title.before(')');
  821.  
  822.     if (!recovered_from_x_error)
  823.     {
  824.     // Not recovered from last X error
  825.  
  826.     // Ignore being called while cleaning up
  827.     static bool entered = false;
  828.     if (entered)
  829.         return 0;
  830.     entered = true;
  831.  
  832.     // Exit after diagnostics
  833.     ddd_cleanup();
  834.     print_x_error(display, event, cerr);
  835.     print_x_error(display, event, dddlog);
  836.     print_fatal_msg(title, cause, "X error");
  837.     exit(EXIT_FAILURE);
  838.     }
  839.  
  840.     // Issue error on stderr and DDD log
  841.     print_x_error(display, event, cerr);
  842.     print_x_error(display, event, dddlog);
  843.  
  844.     if (xt_error_app_context != 0)
  845.     {
  846.     // Prepare for issuing error in dialog
  847.     string *msg_ptr = new string(title + '\v' + cause);
  848.     XtAppAddTimeOut(xt_error_app_context, 0, 
  849.             PostXErrorCB, XtPointer(msg_ptr));
  850.  
  851.     // Set RECOVERED_FROM_X_ERROR to FALSE until DDD is idle again
  852.     recovered_from_x_error = false;
  853.     XtAppAddWorkProc(xt_error_app_context, recovery_done, XtPointer(0));
  854.     }
  855.  
  856.     return 0;            // Keep on acting
  857. }
  858.  
  859. // Cleanup on fatal X I/O errors
  860. void ddd_install_x_error()
  861. {
  862.     old_x_error_handler = XSetErrorHandler(ddd_x_error);
  863. }
  864.  
  865.  
  866.  
  867.  
  868. //-----------------------------------------------------------------------------
  869. // GDB I/O error
  870. //-----------------------------------------------------------------------------
  871.  
  872. // EOF on input/output detected
  873. void gdb_eofHP(Agent *agent, void *, void *)
  874. {
  875.     GDBAgent *gdb = ptr_cast(GDBAgent, agent);
  876.     if (gdb != 0)
  877.     {
  878.     set_status(gdb->title() + ": EOF detected");
  879.     }
  880.  
  881.     if (app_data.terminate_on_eof)
  882.     {
  883.     // Kill GDB
  884.     agent->terminate();
  885.     }
  886. }
  887.  
  888.  
  889. // GDB died
  890. void gdb_diedHP(Agent *gdb, void *, void *call_data)
  891. {
  892.     if (running_shells() > 0)
  893.     {
  894.     char *reason = (char *)call_data;
  895.     post_gdb_died(reason, gdb->lastStatus());
  896.     }
  897.     else
  898.     {
  899.     // No shell open (yet).  If we get here, this is usually
  900.     // because we could not invoke the inferior debugger.
  901.     
  902.     if (!tty_running())
  903.     {
  904.         // Forward diagnostics from debugger console to stderr
  905.         String s = XmTextGetString(gdb_w);
  906.         string message = s + messagePosition;
  907.         XtFree(s);
  908.         cerr << message;
  909.     }
  910.  
  911.     _DDDExitCB(gdb_w, XtPointer(EXIT_FAILURE), XtPointer(0));
  912.     }
  913. }
  914.  
  915.  
  916. //-----------------------------------------------------------------------------
  917. // Controlled exiting
  918. //-----------------------------------------------------------------------------
  919.  
  920. // Exit callback
  921. void _DDDExitCB(Widget w, XtPointer client_data, XtPointer call_data)
  922. {
  923.     ddd_cleanup();
  924.  
  925.     XtCallbackProc closure = ddd_is_restarting ? RestartCB : ExitCB;
  926.     closure(w, client_data, call_data);
  927. }
  928.  
  929. // `quit' has been canceled
  930. static void DDDQuitCanceledCB(const string&, void *)
  931. {
  932.     gdb_is_exiting = false;
  933. }
  934.  
  935. // Exit/Restart after confirmation, depending on the setting of
  936. // DDD_IS_RESTARTING
  937. static void DDDDoneCB(Widget w, XtPointer client_data, XtPointer call_data)
  938. {
  939.     gdb_is_exiting = true;
  940.  
  941.     if (gdb == 0 || !gdb->running())
  942.     {
  943.     _DDDExitCB(w, client_data, call_data);
  944.     return;
  945.     }
  946.  
  947.     if (gdb->isReadyWithPrompt())
  948.     {
  949.     Command c("quit", w);    // This works for all inferior debuggers
  950.     c.callback = DDDQuitCanceledCB;
  951.     gdb_command(c);
  952.     return;
  953.     }
  954.  
  955.     // Debugger is still running; request confirmation
  956.     Arg args[10];
  957.     int arg;
  958.  
  959.     static Widget quit_dialog = 0;
  960.     if (quit_dialog)
  961.     DestroyWhenIdle(quit_dialog);
  962.  
  963.     arg = 0;
  964.     MString msg = rm(gdb->title() + " is still busy.  "
  965.              + (ddd_is_restarting ? "Restart" : "Exit")
  966.              + " anyway (and kill it)?");
  967.     XtSetArg(args[arg], XmNmessageString, msg.xmstring()); arg++;
  968.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  969.     quit_dialog = verify(XmCreateQuestionDialog(find_shell(w),
  970.                           "quit_dialog", args, arg));
  971.     Delay::register_shell(quit_dialog);
  972.     XtAddCallback(quit_dialog, XmNokCallback,   DDDDoneAnywayCB, client_data);
  973.     XtAddCallback(quit_dialog, XmNcancelCallback, UnmanageThisCB, quit_dialog);
  974.     XtAddCallback(quit_dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  975.  
  976.     manage_and_raise(quit_dialog);
  977. }
  978.  
  979. // Exit immediately if DDD is not ready
  980. static void DDDDoneAnywayCB(Widget w, XtPointer client_data, 
  981.                 XtPointer call_data)
  982. {
  983.     // If GDB has gotten ready in between, use controlled exit.
  984.     if (gdb && gdb->isReadyWithPrompt())
  985.     DDDDoneCB(w, client_data, call_data);
  986.     else
  987.     _DDDExitCB(w, client_data, call_data);
  988. }
  989.  
  990. // Exit after confirmation
  991. void DDDExitCB(Widget w, XtPointer client_data, XtPointer call_data)
  992. {
  993.     // Delete temporary restart session, if any
  994.     delete_session(restart_session(), true);
  995.     set_restart_session();
  996.  
  997.     ddd_is_restarting = false;
  998.     DDDDoneCB(w, client_data, call_data);
  999. }
  1000.  
  1001.  
  1002. // Restart unconditionally
  1003. static void _DDDRestartCB(Widget w, XtPointer client_data, XtPointer call_data)
  1004. {
  1005.     static string initial_session;
  1006.     initial_session = app_data.session;
  1007.     app_data.initial_session = initial_session;
  1008.  
  1009.     set_session(RESTART_SESSION);
  1010.  
  1011.     unsigned long flags = (unsigned long)client_data;
  1012.     DDDSaveOptionsCB(w, XtPointer(flags), call_data);
  1013.  
  1014.     set_restart_session(app_data.session);
  1015.     register_environ();
  1016.  
  1017.     ddd_is_restarting = true;
  1018.     DDDDoneCB(w, client_data, call_data);
  1019. }
  1020.  
  1021.  
  1022. // Restart after confirmation
  1023. void DDDRestartCB(Widget w, XtPointer, XtPointer call_data)
  1024. {
  1025.     unsigned long flags = 
  1026.     SAVE_SESSION | SAVE_GEOMETRY | DONT_RELOAD_CORE | DONT_COPY_CORE;
  1027.     if (gdb->running())
  1028.     flags |= SAVE_CORE;
  1029.  
  1030.     if (saving_options_kills_program(flags))
  1031.     {
  1032.     // Saving session would kill program; request confirmation
  1033.     static Widget dialog = 0;
  1034.     if (dialog)
  1035.         DestroyWhenIdle(dialog);
  1036.  
  1037.     dialog = verify(XmCreateQuestionDialog(find_shell(w),
  1038.                            "confirm_restart_dialog",
  1039.                            0, 0));
  1040.     Delay::register_shell(dialog);
  1041.     XtAddCallback(dialog, XmNokCallback, _DDDRestartCB,
  1042.               XtPointer(flags | MAY_KILL));
  1043.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  1044.     
  1045.     manage_and_raise(dialog);
  1046.     }
  1047.     else
  1048.     _DDDRestartCB(w, XtPointer(flags), call_data);
  1049. }
  1050.  
  1051. static void debug_ddd(bool core_dumped)
  1052. {
  1053.     StatusDelay delay("Invoking Debug Window");
  1054.  
  1055.     string term_command = app_data.term_command;
  1056.     term_command.gsub("Execution", "Debug");
  1057.     term_command.gsub("@FONT@", make_font(app_data, FixedWidthDDDFont));
  1058.  
  1059.     string gdb_command = string("gdb ") + saved_argv()[0] + " ";
  1060.  
  1061.     if (core_dumped)
  1062.     gdb_command += "core";
  1063.     else
  1064.     gdb_command += itostring(getpid());
  1065.  
  1066. #if 0
  1067.     gdb_command.prepend("echo \"Debugging " DDD_NAME ".  "
  1068.             "Enter \\`quit' to quit.\"; ");
  1069. #endif
  1070.  
  1071.     term_command += " " + sh_quote(gdb_command) + " &";
  1072.     system(sh_command(term_command, true));
  1073. }
  1074.  
  1075. // Insert `where' info into LOG
  1076. void report_core(ostream& log)
  1077. {
  1078.     if (!is_core_file("core"))
  1079.     return;
  1080.  
  1081.     string tempfile = tmpnam(0);
  1082.     ofstream os(tempfile);
  1083.     os << 
  1084.     "set verbose off\n"
  1085.     "set height 0\n"
  1086.     "where\n"
  1087.     "quit\n";
  1088.     os.close();
  1089.  
  1090.     string gdb_command = 
  1091.     sh_command("gdb -x " + tempfile + " " + saved_argv()[0] + " core", 
  1092.            true);
  1093.  
  1094.     FILE *fp = popen(gdb_command, "r");
  1095.     int c;
  1096.     while ((c = getc(fp)) != EOF)
  1097.     log << (char)c;
  1098.     log.flush();
  1099.  
  1100.     pclose(fp);
  1101.  
  1102.     unlink(tempfile);
  1103. }
  1104.  
  1105. // Debug DDD
  1106. void DDDDebugCB(Widget, XtPointer client_data, XtPointer)
  1107. {
  1108.     bool core_dumped = (int)(long)client_data;
  1109.     debug_ddd(core_dumped);
  1110. }
  1111.