]> Git Repo - binutils.git/blobdiff - gdb/gdbtk.c
* gdbtk.c (gdbtk_init): Prevent segfault when gdbtk.tcl can't be
[binutils.git] / gdb / gdbtk.c
index e3d8412c047124fe0548623ba311c59286bc24bf..35b2a91f7ffde4efc6a44ba275691d9e216b3417 100644 (file)
@@ -1,5 +1,7 @@
-/* TK interface routines.
-   Copyright 1994 Free Software Foundation, Inc.
+/* Tcl/Tk interface routines.
+   Copyright 1994, 1995 Free Software Foundation, Inc.
+
+   Written by Stu Grossman <[email protected]> of Cygnus Support.
 
 This file is part of GDB.
 
@@ -33,10 +35,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <unistd.h>
 #include <setjmp.h>
 #include "top.h"
-#ifndef FASYNC
+#include <sys/ioctl.h>
+#include <string.h>
+#include "dis-asm.h"
+
+#ifndef FIOASYNC
 #include <sys/stropts.h>
 #endif
-#include <string.h>
 
 /* Non-zero means that we're doing the gdbtk interface. */
 int gdbtk = 0;
@@ -53,6 +58,17 @@ static Tk_Window mainWindow = NULL;
 
 static int x_fd;               /* X network socket */
 
+/* This variable determines where memory used for disassembly is read from.
+
+   If > 0, then disassembly comes from the exec file rather than the target
+   (which might be at the other end of a slow serial link).  If == 0 then
+   disassembly comes from target.  If < 0 disassembly is automatically switched
+   to the target if it's an inferior process, otherwise the exec file is
+   used.
+ */
+
+static int disassemble_from_exec = -1;
+
 static void
 null_routine(arg)
      int arg;
@@ -93,6 +109,9 @@ start_saving_output ()
 static void
 finish_saving_output ()
 {
+  if (!saving_output)
+    return;
+
   saving_output = 0;
 
   Tcl_DStringFree (&stdout_buffer);
@@ -199,7 +218,7 @@ breakpoint_notify(b, action)
                   "gdbtk_tcl_breakpoint ",
                   action,
                   " ", bpnum,
-                  " ", filename,
+                  " ", filename ? filename : "{}",
                   " ", line,
                   " ", pc,
                   NULL);
@@ -305,6 +324,48 @@ gdb_loc (clientData, interp, argc, argv)
   return TCL_OK;
 }
 \f
+/* This implements the TCL command `gdb_eval'. */
+
+static int
+gdb_eval (clientData, interp, argc, argv)
+     ClientData clientData;
+     Tcl_Interp *interp;
+     int argc;
+     char *argv[];
+{
+  struct expression *expr;
+  struct cleanup *old_chain;
+  value_ptr val;
+
+  if (argc != 2)
+    {
+      Tcl_SetResult (interp, "wrong # args", TCL_STATIC);
+      return TCL_ERROR;
+    }
+
+  expr = parse_expression (argv[1]);
+
+  old_chain = make_cleanup (free_current_contents, &expr);
+
+  val = evaluate_expression (expr);
+
+  start_saving_output ();      /* Start collecting stdout */
+
+  val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), VALUE_ADDRESS (val),
+            gdb_stdout, 0, 0, 0, 0);
+#if 0
+  value_print (val, gdb_stdout, 0, 0);
+#endif
+
+  Tcl_AppendElement (interp, get_saved_output ());
+
+  finish_saving_output ();     /* Set stdout back to normal */
+
+  do_cleanups (old_chain);
+
+  return TCL_OK;
+}
+\f
 /* This implements the TCL command `gdb_sourcelines', which returns a list of
    all of the lines containing executable code for the specified source file
    (ie: lines where you can put breakpoints). */
@@ -606,8 +667,12 @@ call_wrapper (clientData, interp, argc, argv)
 
       finish_saving_output (); /* Restore stdout to normal */
 
+      dis_asm_read_memory_hook = 0; /* Restore disassembly hook */
+
       gdb_flush (gdb_stderr);  /* Flush error output */
 
+      gdb_flush (gdb_stdout);  /* Sometimes error output comes here as well */
+
 /* In case of an error, we may need to force the GUI into idle mode because
    gdbtk_call_command may have bombed out while in the command routine.  */
 
@@ -655,7 +720,251 @@ gdb_stop (clientData, interp, argc, argv)
 
   return TCL_OK;
 }
+\f
+/* This implements the TCL command `gdb_disassemble'.  */
+
+static int
+gdbtk_dis_asm_read_memory (memaddr, myaddr, len, info)
+     bfd_vma memaddr;
+     bfd_byte *myaddr;
+     int len;
+     disassemble_info *info;
+{
+  extern struct target_ops exec_ops;
+  int res;
+
+  errno = 0;
+  res = xfer_memory (memaddr, myaddr, len, 0, &exec_ops);
+
+  if (res == len)
+    return 0;
+  else
+    if (errno == 0)
+      return EIO;
+    else
+      return errno;
+}
+
+/* We need a different sort of line table from the normal one cuz we can't
+   depend upon implicit line-end pc's for lines.  This is because of the
+   reordering we are about to do.  */
+
+struct my_line_entry {
+  int line;
+  CORE_ADDR start_pc;
+  CORE_ADDR end_pc;
+};
+
+static int
+compare_lines (mle1p, mle2p)
+     const PTR mle1p;
+     const PTR mle2p;
+{
+  struct my_line_entry *mle1, *mle2;
+  int val;
+
+  mle1 = (struct my_line_entry *) mle1p;
+  mle2 = (struct my_line_entry *) mle2p;
+
+  val =  mle1->line - mle2->line;
+
+  if (val != 0)
+    return val;
+
+  return mle1->start_pc - mle2->start_pc;
+}
+
+static int
+gdb_disassemble (clientData, interp, argc, argv)
+     ClientData clientData;
+     Tcl_Interp *interp;
+     int argc;
+     char *argv[];
+{
+  CORE_ADDR pc, low, high;
+  int mixed_source_and_assembly;
+
+  if (argc != 3 && argc != 4)
+    {
+      Tcl_SetResult (interp, "wrong # args", TCL_STATIC);
+      return TCL_ERROR;
+    }
+
+  if (strcmp (argv[1], "source") == 0)
+    mixed_source_and_assembly = 1;
+  else if (strcmp (argv[1], "nosource") == 0)
+    mixed_source_and_assembly = 0;
+  else
+    {
+      Tcl_SetResult (interp, "First arg must be 'source' or 'nosource'",
+                    TCL_STATIC);
+      return TCL_ERROR;
+    }
+
+  low = parse_and_eval_address (argv[2]);
+
+  if (argc == 3)
+    {
+      if (find_pc_partial_function (low, NULL, &low, &high) == 0)
+       {
+         Tcl_SetResult (interp, "No function contains specified address",
+                        TCL_STATIC);
+         return TCL_ERROR;
+       }
+    }
+  else
+    high = parse_and_eval_address (argv[3]);
+
+  /* If disassemble_from_exec == -1, then we use the following heuristic to
+     determine whether or not to do disassembly from target memory or from the
+     exec file:
+
+     If we're debugging a local process, read target memory, instead of the
+     exec file.  This makes disassembly of functions in shared libs work
+     correctly.
+
+     Else, we're debugging a remote process, and should disassemble from the
+     exec file for speed.  However, this is no good if the target modifies it's
+     code (for relocation, or whatever).
+   */
+
+  if (disassemble_from_exec == -1)
+    if (strcmp (target_shortname, "child") == 0
+       || strcmp (target_shortname, "procfs") == 0)
+      disassemble_from_exec = 0; /* It's a child process, read inferior mem */
+    else
+      disassemble_from_exec = 1; /* It's remote, read the exec file */
+
+  if (disassemble_from_exec)
+    dis_asm_read_memory_hook = gdbtk_dis_asm_read_memory;
+
+  /* If just doing straight assembly, all we need to do is disassemble
+     everything between low and high.  If doing mixed source/assembly, we've
+     got a totally different path to follow.  */
+
+  if (mixed_source_and_assembly)
+    {                          /* Come here for mixed source/assembly */
+      /* The idea here is to present a source-O-centric view of a function to
+        the user.  This means that things are presented in source order, with
+        (possibly) out of order assembly immediately following.  */
+      struct symtab *symtab;
+      struct linetable_entry *le;
+      int nlines;
+      int newlines;
+      struct my_line_entry *mle;
+      struct symtab_and_line sal;
+      int i;
+      int out_of_order;
+      int next_line;
 
+      symtab = find_pc_symtab (low); /* Assume symtab is valid for whole PC range */
+
+      if (!symtab)
+       goto assembly_only;
+
+/* First, convert the linetable to a bunch of my_line_entry's.  */
+
+      le = symtab->linetable->item;
+      nlines = symtab->linetable->nitems;
+
+      if (nlines <= 0)
+       goto assembly_only;
+
+      mle = (struct my_line_entry *) alloca (nlines * sizeof (struct my_line_entry));
+
+      out_of_order = 0;
+
+/* Copy linetable entries for this function into our data structure, creating
+   end_pc's and setting out_of_order as appropriate.  */
+
+/* First, skip all the preceding functions.  */
+
+      for (i = 0; i < nlines - 1 && le[i].pc < low; i++) ;
+
+/* Now, copy all entries before the end of this function.  */
+
+      newlines = 0;
+      for (; i < nlines - 1 && le[i].pc < high; i++)
+       {
+         if (le[i].line == le[i + 1].line
+             && le[i].pc == le[i + 1].pc)
+           continue;           /* Ignore duplicates */
+
+         mle[newlines].line = le[i].line;
+         if (le[i].line > le[i + 1].line)
+           out_of_order = 1;
+         mle[newlines].start_pc = le[i].pc;
+         mle[newlines].end_pc = le[i + 1].pc;
+         newlines++;
+       }
+
+/* If we're on the last line, and it's part of the function, then we need to
+   get the end pc in a special way.  */
+
+      if (i == nlines - 1
+         && le[i].pc < high)
+       {
+         mle[newlines].line = le[i].line;
+         mle[newlines].start_pc = le[i].pc;
+         sal = find_pc_line (le[i].pc, 0);
+         mle[newlines].end_pc = sal.end;
+         newlines++;
+       }
+
+/* Now, sort mle by line #s (and, then by addresses within lines). */
+
+      if (out_of_order)
+       qsort (mle, newlines, sizeof (struct my_line_entry), compare_lines);
+
+/* Now, for each line entry, emit the specified lines (unless they have been
+   emitted before), followed by the assembly code for that line.  */
+
+      next_line = 0;           /* Force out first line */
+      for (i = 0; i < newlines; i++)
+       {
+/* Print out everything from next_line to the current line.  */
+
+         if (mle[i].line >= next_line)
+           {
+             if (next_line != 0)
+               print_source_lines (symtab, next_line, mle[i].line + 1, 0);
+             else
+               print_source_lines (symtab, mle[i].line, mle[i].line + 1, 0);
+
+             next_line = mle[i].line + 1;
+           }
+
+         for (pc = mle[i].start_pc; pc < mle[i].end_pc; )
+           {
+             QUIT;
+             fputs_unfiltered ("    ", gdb_stdout);
+             print_address (pc, gdb_stdout);
+             fputs_unfiltered (":\t    ", gdb_stdout);
+             pc += print_insn (pc, gdb_stdout);
+             fputs_unfiltered ("\n", gdb_stdout);
+           }
+       }
+    }
+  else
+    {
+assembly_only:
+      for (pc = low; pc < high; )
+       {
+         QUIT;
+         fputs_unfiltered ("    ", gdb_stdout);
+         print_address (pc, gdb_stdout);
+         fputs_unfiltered (":\t    ", gdb_stdout);
+         pc += print_insn (pc, gdb_stdout);
+         fputs_unfiltered ("\n", gdb_stdout);
+       }
+    }
+
+  dis_asm_read_memory_hook = 0;
+
+  gdb_flush (gdb_stdout);
+
+  return TCL_OK;
+}
 \f
 static void
 tk_command (cmd, from_tty)
@@ -718,28 +1027,23 @@ gdbtk_wait (pid, ourstatus)
      int pid;
      struct target_waitstatus *ourstatus;
 {
-#ifdef FASYNC
-  signal (SIGIO, x_event);
-#else
-#if 1
-  sigset (SIGIO, x_event);
-#else
-  /* This is possibly needed for SVR4... */
-  {
-    struct sigaction action;
-    static sigset_t nullsigmask = {0};
-
-    action.sa_handler = iosig;
-    action.sa_mask = nullsigmask;
-    action.sa_flags = SA_RESTART;
-    sigaction(SIGIO, &action, NULL);
-  }
-#endif
+  struct sigaction action;
+  static sigset_t nullsigmask = {0};
+
+#ifndef SA_RESTART
+  /* Needed for SunOS 4.1.x */
+#define SA_RESTART 0
 #endif
 
+  action.sa_handler = x_event;
+  action.sa_mask = nullsigmask;
+  action.sa_flags = SA_RESTART;
+  sigaction(SIGIO, &action, NULL);
+
   pid = target_wait (pid, ourstatus);
 
-  signal (SIGIO, SIG_IGN);
+  action.sa_handler = SIG_IGN;
+  sigaction(SIGIO, &action, NULL);
 
   return pid;
 }
@@ -772,6 +1076,10 @@ gdbtk_init ()
   struct cleanup *old_chain;
   char *gdbtk_filename;
   int i;
+  struct sigaction action;
+  static sigset_t nullsigmask = {0};
+  extern struct cmd_list_element *setlist;
+  extern struct cmd_list_element *showlist;
 
   old_chain = make_cleanup (cleanup_init, 0);
 
@@ -807,36 +1115,11 @@ gdbtk_init ()
                     gdb_fetch_registers, NULL);
   Tcl_CreateCommand (interp, "gdb_changed_register_list", call_wrapper,
                     gdb_changed_register_list, NULL);
-
-  gdbtk_filename = getenv ("GDBTK_FILENAME");
-  if (!gdbtk_filename)
-    if (access ("gdbtk.tcl", R_OK) == 0)
-      gdbtk_filename = "gdbtk.tcl";
-    else
-      gdbtk_filename = GDBTK_FILENAME;
-
-  if (Tcl_EvalFile (interp, gdbtk_filename) != TCL_OK)
-    error ("Failure reading %s: %s", gdbtk_filename, interp->result);
-
-  /* Get the file descriptor for the X server */
-
-  x_fd = ConnectionNumber (Tk_Display (mainWindow));
-
-  /* Setup for I/O interrupts */
-
-  signal (SIGIO, SIG_IGN);
-
-#ifdef FASYNC
-  i = fcntl (x_fd, F_GETFL, 0);
-  fcntl (x_fd, F_SETFL, i|FASYNC);
-  fcntl (x_fd, F_SETOWN, getpid()); 
-#else
-  if (ioctl (x_fd,  I_SETSIG, S_INPUT|S_RDNORM) < 0)
-    perror ("gdbtk_init: ioctl I_SETSIG failed");
-#endif /* ifndef FASYNC */
+  Tcl_CreateCommand (interp, "gdb_disassemble", call_wrapper,
+                    gdb_disassemble, NULL);
+  Tcl_CreateCommand (interp, "gdb_eval", call_wrapper, gdb_eval, NULL);
 
   command_loop_hook = Tk_MainLoop;
-  fputs_unfiltered_hook = gdbtk_fputs;
   print_frame_info_listing_hook = null_routine;
   query_hook = gdbtk_query;
   flush_hook = gdbtk_flush;
@@ -848,10 +1131,72 @@ gdbtk_init ()
   target_wait_hook = gdbtk_wait;
   call_command_hook = gdbtk_call_command;
 
-  discard_cleanups (old_chain);
+  /* Get the file descriptor for the X server */
+
+  x_fd = ConnectionNumber (Tk_Display (mainWindow));
+
+  /* Setup for I/O interrupts */
+
+  action.sa_mask = nullsigmask;
+  action.sa_flags = 0;
+  action.sa_handler = SIG_IGN;
+  sigaction(SIGIO, &action, NULL);
+
+#ifdef FIOASYNC
+  i = 1;
+  if (ioctl (x_fd, FIOASYNC, &i))
+    perror_with_name ("gdbtk_init: ioctl FIOASYNC failed");
+
+  i = getpid();
+  if (ioctl (x_fd, SIOCSPGRP, &i))
+    perror_with_name ("gdbtk_init: ioctl SIOCSPGRP failed");
+#else
+  if (ioctl (x_fd,  I_SETSIG, S_INPUT|S_RDNORM) < 0)
+    perror_with_name ("gdbtk_init: ioctl I_SETSIG failed");
+#endif /* ifndef FIOASYNC */
 
   add_com ("tk", class_obscure, tk_command,
           "Send a command directly into tk.");
+
+#if 0
+  add_show_from_set (add_set_cmd ("disassemble-from-exec", class_support,
+                                 var_boolean, (char *)&disassemble_from_exec,
+                                 "Set ", &setlist),
+                    &showlist);
+#endif
+
+  Tcl_LinkVar (interp, "disassemble-from-exec", (char *)&disassemble_from_exec,
+              TCL_LINK_INT);
+
+  /* Load up gdbtk.tcl after all the environment stuff has been setup.  */
+
+  gdbtk_filename = getenv ("GDBTK_FILENAME");
+  if (!gdbtk_filename)
+    if (access ("gdbtk.tcl", R_OK) == 0)
+      gdbtk_filename = "gdbtk.tcl";
+    else
+      gdbtk_filename = GDBTK_FILENAME;
+
+/* Defer setup of fputs_unfiltered_hook to near the end so that error messages
+   prior to this point go to stdout/stderr.  */
+
+  fputs_unfiltered_hook = gdbtk_fputs;
+
+  if (Tcl_EvalFile (interp, gdbtk_filename) != TCL_OK)
+    {
+      char *err;
+
+      fputs_unfiltered_hook = NULL; /* Force errors to stdout/stderr */
+
+      fprintf_unfiltered (stderr, "%s:%d: %s\n", gdbtk_filename,
+                         interp->errorLine, interp->result);
+
+      fputs_unfiltered ("Stack trace:\n", gdb_stderr);
+      fputs_unfiltered (Tcl_GetVar (interp, "errorInfo", 0), gdb_stderr);
+      error ("");
+    }
+
+  discard_cleanups (old_chain);
 }
 
 /* Come here during initialze_all_files () */
This page took 0.038209 seconds and 4 git commands to generate.