]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Implement support for 'catch syscall' in gdbserver.
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Fri, 22 Jan 2016 21:06:23 +0000 (21:06 +0000)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Fri, 22 Jan 2016 21:06:23 +0000 (21:06 +0000)
Note that catch syscall implies to use the soon to be released
gdb 7.11 version.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15770

NEWS
coregrind/m_gdbserver/m_gdbserver.c
coregrind/m_gdbserver/remote-utils.c
coregrind/m_gdbserver/server.c
coregrind/m_gdbserver/server.h
coregrind/m_gdbserver/target.c
coregrind/m_gdbserver/target.h
coregrind/m_syswrap/syswrap-main.c
coregrind/pub_core_gdbserver.h

diff --git a/NEWS b/NEWS
index fff9f76ec6d55a0ef4cc1db21aeba704c8294aa8..f3e65e2b9b6d4aac8bcb4a0baf937dea1cde9b7d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,12 @@ n-i-bz Improved thread startup time significantly on non-Linux platforms.
   "nouserintercepts" can be any non-existing library name).
   This new functionality is not implemented for darwin/macosx.
 
+* New and modified GDB server monitor features:
+
+  - Valgrind's gdbserver now accepts the command 'catch syscall'.
+    Note that you must have a GDB >= 7.11 to use 'catch syscall' with
+    gdbserver.
+
 * ==================== FIXED BUGS ====================
 
 The following bugs have been fixed or resolved.  Note that "n-i-bz"
index d5cc8eae4957b4b600d8cdc74550d85c2018f051..99122d28d1286dab72c5753ff1c31bfc797add68 100644 (file)
@@ -1012,6 +1012,44 @@ Bool VG_(gdbserver_report_signal) (vki_siginfo_t *info, ThreadId tid)
    }
 }
 
+Bool catching_syscalls = False; // True if catching all or some syscalls.
+/* If catching_syscalls is True, then syscalls_to_catch_size == 0 means
+   to catch all syscalls. Otherwise, it is the size of the syscalls_to_catch
+   array. */
+Int syscalls_to_catch_size = 0;
+Int *syscalls_to_catch;
+static Bool catch_this_syscall (Int sysno)
+{
+   Int i;
+
+   if (syscalls_to_catch_size == 0)
+      return True;
+
+   for (i = 0; i < syscalls_to_catch_size; i++)
+      if (syscalls_to_catch[i] == sysno)
+         return True;
+
+   return False;
+}
+
+void VG_(gdbserver_report_syscall) (Bool before, UWord sysno, ThreadId tid)
+{
+   dlog(4, "VG_(gdbserver_report_syscall) before %d sysno %lu tid %d\n",
+        before, sysno, tid);
+
+   if (UNLIKELY(catching_syscalls)) {
+      if (!remote_connected()) {
+         dlog(2, "not connected => no report\n");
+       }
+
+      if (catch_this_syscall ((Int)sysno)) {
+         /* let gdbserver do some work */
+         gdbserver_syscall_encountered (before, (Int)sysno);
+         call_gdbserver (tid, signal_reason);
+      }
+   }
+}
+
 void VG_(gdbserver_exit) (ThreadId tid, VgSchedReturnCode tids_schedretcode)
 {
    dlog(1, "VG core calling VG_(gdbserver_exit) tid %u will exit\n", tid);
@@ -1022,11 +1060,13 @@ void VG_(gdbserver_exit) (ThreadId tid, VgSchedReturnCode tids_schedretcode)
          vg_assert (0);
       case VgSrc_ExitThread:
       case VgSrc_ExitProcess:
-         gdbserver_process_exit_encountered ('W', VG_(threads)[tid].os_state.exitcode);
+         gdbserver_process_exit_encountered
+            ('W', VG_(threads)[tid].os_state.exitcode);
          call_gdbserver (tid, exit_reason);
          break;
       case VgSrc_FatalSig:
-         gdbserver_process_exit_encountered ('X', VG_(threads)[tid].os_state.fatalsig);
+         gdbserver_process_exit_encountered
+            ('X', VG_(threads)[tid].os_state.fatalsig);
          call_gdbserver (tid, exit_reason);
          break;
       default:
index f6f3d0bcf34431035c28ea4bace7bb1fce6cb9eb..2d4320c9c17dae74425656fcaf67ac9cb2c38a48 100644 (file)
@@ -1154,6 +1154,14 @@ void prepare_resume_reply (char *buf, char status, unsigned char sig)
          *buf++ = ';';
       }
 
+      if (valgrind_stopped_by_syscall () >= 0) {
+         VG_(sprintf) (buf, "%s:%x;",
+                       valgrind_stopped_before_syscall ()
+                       ? "syscall_entry" : "syscall_return",
+                       valgrind_stopped_by_syscall ());
+         buf += strlen (buf);
+      }
+
       while (*regp) {
          buf = outreg (find_regno (*regp), buf);
          regp ++;
index ea2bdc849ad82fbbd2496689706e713cf3705ab0..2f26178e728b9a6ac258d8254d93d37a7f047843 100644 (file)
@@ -624,6 +624,52 @@ void handle_set (char *arg_own_buf, int *new_packet_len_p)
       return;
    }
 
+   if (strcmp ("QCatchSyscalls:0", arg_own_buf) == 0) {
+      dlog (3, "catch syscall all off\n");
+      catching_syscalls = False;
+      write_ok (arg_own_buf);
+      return;
+   }
+
+   const char *q1 = "QCatchSyscalls:1";
+   if (strncmp (q1, arg_own_buf, strlen(q1)) == 0) {
+      Int i;
+      const char *p;
+
+      if (syscalls_to_catch != NULL) {
+         free (syscalls_to_catch);
+         syscalls_to_catch = NULL;
+      }
+      syscalls_to_catch_size = 0;
+      p = arg_own_buf + strlen(q1);
+      while (*p) {
+         if (*p++ == ';')
+           syscalls_to_catch_size++;
+      }
+      if (syscalls_to_catch_size > 0) {
+         CORE_ADDR sysno;
+         char *from, *to;
+
+         syscalls_to_catch = malloc (syscalls_to_catch_size * sizeof (int));
+
+         from = strchr (arg_own_buf, ';') + 1;
+         for (i = 0; i < syscalls_to_catch_size; i++) {
+            to = strchr (from, ';');
+            if (to == NULL)
+               to = arg_own_buf + strlen (arg_own_buf);
+            decode_address (&sysno, from, to - from);
+            syscalls_to_catch[i] = (Int)sysno;
+            dlog(4, "catch syscall sysno %d\n", (int)sysno);
+            from = to;
+            if (*from == ';') from++;
+         }
+      } else
+         dlog (4, "catch syscall all sysno\n");
+      catching_syscalls = True;
+      write_ok (arg_own_buf);
+      return;
+   }
+
    if (strncmp ("QPassSignals:", arg_own_buf, 13) == 0) {
       int i;
       char *from, *to;
@@ -1042,6 +1088,7 @@ void handle_query (char *arg_own_buf, int *new_packet_len_p)
 
       strcat (arg_own_buf, ";QStartNoAckMode+");
       strcat (arg_own_buf, ";QPassSignals+");
+      strcat (arg_own_buf, ";QCatchSyscalls+");
       if (VG_(client_auxv))
          strcat (arg_own_buf, ";qXfer:auxv:read+");
 
index d2bbfd7c49953f1ac4b0d0c2a4329072fc038d83..1c2d8612e371e04b8f6b57744b8182f618f6bd38 100644 (file)
@@ -116,6 +116,14 @@ extern Addr thumb_pc (Addr pc);
    or tid 1 otherwise). */
 extern ThreadId vgdb_interrupted_tid;
 
+/* True if GDB is catching client syscalls. */
+extern Bool catching_syscalls;
+
+/* Size of the syscalls_to_catch. Only useful if catching_syscalls True.
+   syscalls_to_catch_size 0 means all syscalls are caught. */
+extern Int syscalls_to_catch_size;
+extern Int *syscalls_to_catch;
+
 /*------------ end of interface to low level gdbserver */
 
 
index 3d54b7503b2e4c25ad12d0eb50653956d46a50a0..488730a0f9d043a07b72cc4d7d867bc044f4972c 100644 (file)
@@ -177,6 +177,26 @@ Bool gdbserver_deliver_signal (vki_siginfo_t *info)
    return vki_signal_to_deliver.si_signo != 0;
 }
 
+static Bool before_syscall;
+static Int sysno_to_report = -1;
+void gdbserver_syscall_encountered (Bool before, Int sysno)
+{
+   before_syscall = before;
+   sysno_to_report = sysno;
+}
+
+Int valgrind_stopped_by_syscall (void)
+{
+   return sysno_to_report;
+}
+
+Bool valgrind_stopped_before_syscall()
+{
+   vg_assert (sysno_to_report >= 0);
+   return before_syscall;
+}
+
+
 static unsigned char exit_status_to_report;
 static int exit_code_to_report;
 void gdbserver_process_exit_encountered (unsigned char status, Int code)
@@ -249,6 +269,12 @@ void valgrind_resume (struct thread_resume *resume_info)
            C2v(stopped_data_address));
       VG_(set_watchpoint_stop_address) ((Addr) 0);
    }
+   if (valgrind_stopped_by_syscall () >= 0) {
+      dlog(1, "clearing stopped by syscall %d\n",
+           valgrind_stopped_by_syscall ());
+      gdbserver_syscall_encountered (False, -1);
+   }
+
    vki_signal_to_deliver.si_signo = resume_info->sig;
    /* signal was reported to GDB, GDB told us to resume execution.
       So, reset the signal to report to 0. */
index 2ea8de9ca6bcb56a8db6a6f8cafb53534f32dbb3..9a8b5f2b4e0d8027a988ee1826f4323e04bd2609 100644 (file)
@@ -144,11 +144,23 @@ extern int valgrind_stopped_by_watchpoint (void);
    returns 0 otherwise.  */
 extern CORE_ADDR valgrind_stopped_data_address (void);
 
+
+/* Inform GDB (if needed) that client is before (or after) syscall sysno.
+   sysno -1 is used to clear the fact that a syscall has been encountered. */
+extern void gdbserver_syscall_encountered (Bool before, Int sysno);
+
+/* >= 0 if valgrind stopped due to syscall, -1 if not stopped due to syscall. */
+extern Int valgrind_stopped_by_syscall (void);
+
+/* if valgrind_stopped_by_syscall() >= 0, tells if stopped before or after
+   syscall. */
+extern Bool valgrind_stopped_before_syscall (void);
+
 /* True if gdbserver is single stepping the valgrind process */
-extern Bool valgrind_single_stepping(void);
+extern Bool valgrind_single_stepping (void);
 
 /* Set Valgrind in single stepping mode or not according to Bool. */
-extern void valgrind_set_single_stepping(Bool);
+extern void valgrind_set_single_stepping (Bool);
 
 /* -------------------------------------------------------------------------- */
 /* ----------------- Examining/modifying data while stopped ----------------- */
index f4d1fdd47f468d884b1d260a6ab2037674899170..054891f7ebe7b6afe9f15f6059b8d41373923fc4 100644 (file)
@@ -50,6 +50,7 @@
 #include "pub_core_machine.h"
 #include "pub_core_mallocfree.h"
 #include "pub_core_syswrap.h"
+#include "pub_core_gdbserver.h"     // VG_(gdbserver_report_syscall)
 
 #include "priv_types_n_macros.h"
 #include "priv_syswrap-main.h"
@@ -1906,6 +1907,9 @@ void VG_(client_syscall) ( ThreadId tid, UInt trc )
                   &layout, 
                   &sci->args, &sci->status, &sci->flags );
    
+   /* If needed, gdbserver will report syscall entry to GDB */
+   VG_(gdbserver_report_syscall)(True, sysno, tid);
+
    /* The pre-handler may have modified:
          sci->args
          sci->status
@@ -2063,6 +2067,9 @@ void VG_(client_syscall) ( ThreadId tid, UInt trc )
    if (!(sci->flags & SfNoWriteResult))
       putSyscallStatusIntoGuestState( tid, &sci->status, &tst->arch.vex );
 
+   /* If needed, gdbserver will report syscall return to GDB */
+   VG_(gdbserver_report_syscall)(False, sysno, tid);
+
    /* Situation now:
       - the guest state is now correctly modified following the syscall
       - modified args, original args and syscall status are still
index fa69863667127f0b46ada76892729754de979629..eca92a0dda3e06510c21f8a0c286dbc0b0344fb2 100644 (file)
@@ -119,6 +119,13 @@ extern Bool VG_(gdbserver_report_signal) (vki_siginfo_t *info, ThreadId tid);
 extern void VG_(gdbserver_report_fatal_signal) (const vki_siginfo_t *info,
                                                 ThreadId tid);
 
+// To be called by core before and after a client syscall.
+// If GDB has asked to observe the syscall, control will be given to GDB.
+// When Before is True, it is a report before the syscall,
+// False means a report after the syscall.
+extern void VG_(gdbserver_report_syscall) (Bool before, UWord sysno,
+                                           ThreadId tid);
+
 /* Entry point invoked by scheduler.c to execute the request 
    VALGRIND_CLIENT_MONITOR_COMMAND.
    Returns True if command was not recognised. */