Implement a GDB server in Valgrind.  See #214909.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11727 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/massif/ms_main.c b/massif/ms_main.c
index 67702cd..6bc4925 100644
--- a/massif/ms_main.c
+++ b/massif/ms_main.c
@@ -180,6 +180,7 @@
 #include "pub_tool_tooliface.h"
 #include "pub_tool_xarray.h"
 #include "pub_tool_clientstate.h"
+#include "pub_tool_gdbserver.h"
 
 #include "valgrind.h"           // For {MALLOC,FREE}LIKE_BLOCK
 
@@ -1977,6 +1978,21 @@
 //--- Client Requests                                      ---//
 //------------------------------------------------------------//
 
+static void print_monitor_help ( void )
+{
+   VG_(gdb_printf) ("\n");
+   VG_(gdb_printf) ("massif monitor commands:\n");
+   VG_(gdb_printf) ("  ms.snapshot [<filename>] [detailed]\n");
+   VG_(gdb_printf) ("       takes a snapshot and saves it in <filename>\n");
+   VG_(gdb_printf) ("             default <filename> is massif.vgdb.out\n");
+   VG_(gdb_printf) ("             if present, detailed argument indicates to take a detailed snapshot\n");
+   VG_(gdb_printf) ("\n");
+}
+
+
+/* Forward declaration.
+   return True if request recognised, False otherwise */
+static Bool handle_gdb_monitor_command (ThreadId tid, Char *req);
 static Bool ms_handle_client_request ( ThreadId tid, UWord* argv, UWord* ret )
 {
    switch (argv[0]) {
@@ -2003,6 +2019,15 @@
       *ret = 0;
       return True;
    }
+   case VG_USERREQ__GDB_MONITOR_COMMAND: {
+     Bool handled = handle_gdb_monitor_command (tid, (Char*)argv[1]);
+     if (handled)
+       *ret = 1;
+     else
+       *ret = 0;
+     return handled;
+   }
+
    default:
       *ret = 0;
       return False;
@@ -2287,19 +2312,13 @@
    }
 }
 
-static void write_snapshots_to_file(void)
+static void write_snapshots_to_file(Char* massif_out_file, 
+                                    Snapshot snapshots_array[], 
+                                    Int nr_elements)
 {
    Int i, fd;
    SysRes sres;
 
-   // Setup output filename.  Nb: it's important to do this now, ie. as late
-   // as possible.  If we do it at start-up and the program forks and the
-   // output file format string contains a %p (pid) specifier, both the
-   // parent and child will incorrectly write to the same file;  this
-   // happened in 3.3.0.
-   Char* massif_out_file =
-      VG_(expand_file_name)("--massif-out-file", clo_massif_out_file);
-
    sres = VG_(open)(massif_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY,
                                      VKI_S_IRUSR|VKI_S_IWUSR);
    if (sr_isError(sres)) {
@@ -2307,11 +2326,9 @@
       // between multiple cachegrinded processes?), give up now.
       VG_(umsg)("error: can't open output file '%s'\n", massif_out_file );
       VG_(umsg)("       ... so profiling results will be missing.\n");
-      VG_(free)(massif_out_file);
       return;
    } else {
       fd = sr_Res(sres);
-      VG_(free)(massif_out_file);
    }
 
    // Print massif-specific options that were used.
@@ -2342,12 +2359,75 @@
 
    FP("time_unit: %s\n", TimeUnit_to_string(clo_time_unit));
 
-   for (i = 0; i < next_snapshot_i; i++) {
-      Snapshot* snapshot = & snapshots[i];
+   for (i = 0; i < nr_elements; i++) {
+      Snapshot* snapshot = & snapshots_array[i];
       pp_snapshot(fd, snapshot, i);     // Detailed snapshot!
    }
+   VG_(close) (fd);
 }
 
+static void write_snapshots_array_to_file(void)
+{
+   // Setup output filename.  Nb: it's important to do this now, ie. as late
+   // as possible.  If we do it at start-up and the program forks and the
+   // output file format string contains a %p (pid) specifier, both the
+   // parent and child will incorrectly write to the same file;  this
+   // happened in 3.3.0.
+   Char* massif_out_file =
+      VG_(expand_file_name)("--massif-out-file", clo_massif_out_file);
+   write_snapshots_to_file (massif_out_file, snapshots, next_snapshot_i);
+   VG_(free)(massif_out_file);
+}
+
+static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
+{
+   Char* wcmd;
+   Char s[VG_(strlen(req))]; /* copy for strtok_r */
+   Char *ssaveptr;
+
+   VG_(strcpy) (s, req);
+
+   wcmd = VG_(strtok_r) (s, " ", &ssaveptr);
+   switch (VG_(keyword_id) ("help ms.snapshot", 
+                            wcmd, kwd_report_duplicated_matches)) {
+   case -2: /* multiple matches */
+      return True;
+   case -1: /* not found */
+      return False;
+   case  0: /* help */
+      print_monitor_help();
+      return True;
+   case  1: { /* ms.snapshot */
+      Char* kw;
+      Char* filename = NULL;
+      Bool detailed = False;
+      Snapshot snapshot;
+      
+      for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr); 
+           kw != NULL; 
+           kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) {
+        if (filename == NULL)
+           filename = kw;
+        else if (0 == VG_(strncmp)(kw, "detailed", VG_(strlen) (kw))) 
+           detailed = True;
+        else {
+           VG_(gdb_printf) ("invalid 2nd arg\n");
+           return True;
+        }
+      }
+      clear_snapshot(&snapshot, /* do_sanity_check */ False);
+      take_snapshot(&snapshot, Normal, get_time(), detailed);
+      write_snapshots_to_file ((filename == NULL) ? (Char*) "massif.vgdb.out" : filename,
+                               &snapshot,
+                               1);
+      delete_snapshot(&snapshot);
+      return True;
+   }
+   default: 
+      tl_assert(0);
+      return False;
+   }
+}
 
 //------------------------------------------------------------//
 //--- Finalisation                                         ---//
@@ -2356,7 +2436,7 @@
 static void ms_fini(Int exit_status)
 {
    // Output.
-   write_snapshots_to_file();
+   write_snapshots_array_to_file();
 
    // Stats
    tl_assert(n_xpts > 0);  // always have alloc_xpt