This is the mail archive of the frysk@sources.redhat.com mailing list for the frysk project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Mixing single-stepping and breakpointing


Hi,

On Wed, 2007-02-07 at 19:08 +0100, Mark Wielaard wrote:
> The other thing that makes stepping slightly inconvenient is that on
> some architectures you will get spurious trap events (for example for
> system calls). So the Isa now defines a method hasExecutedSpuriousTrap()
> that checks whether the last instruction was a trapping instruction that
> the kernel will handle (if not, then we do need to signal a SIGTRAP to
> the inferior). This only happens on x86, but not on x86_64 as far as I
> can see. PowerPC is a stub for now (please check if you have access to
> such a machine).
> 
> We also need to keep track of whether a signal was just send to the
> inferior. In that case we do get a SIGTRAP, but not a step (the kernel
> adjusts the pc to point to the start of the signal handle). We need to
> distinguish this case from a real trap signal that also doesn't generate
> a step, but which indicates that we need to signal the SIGTRAP to the
> inferior.

And there is a third reason the pc can be "suddenly" adjusted while
stepping which is when the inferior returns from a signal handler
through the sigreturn system call. The following patch adds support for
detecting that case, plus a new test. Again only implemented and tested
on x86 and x86_64.

2007-02-12  Mark Wielaard  <mark@klomp.org>

        * funit-alarm.c: New test program.

2007-02-12  Mark Wielaard  <mark@klomp.org>

        * Isa.java (isAtSyscallSigReturn): New method.
        * IsaIA32.java (isAtSyscallSigReturn): Likewise.
        * IsaPowerPC.java (isAtSyscallSigReturn): Likewise.
        * IsaX8664.java (isAtSyscallSigReturn): Likewise.
        * LinuxPtraceTask.java (sendStepInstruction): Set syscall_sigret.
        * LinuxPtraceTaskState.java (Running.handleTrapEvent): Check
        syscall_sigret.
        (BlockedSignal.unblock): SendContinue with pending signal.
        * Task.java (syscall_sigret): New field.
        * TestTaskObserverInstructionSigReturn.java: New test.

This patch also fixes a problem where a blocked Task would not get the
pending signal delivered. The new test also triggers this issue.

In theory we could extend this new Isa isAtSyscallSigReturn() support
for more reliably reporting syscall enter and exit even during stepping.
But for now I haven't done this because syscall tracing still is not
really supported even in the non-stepping case. If that is fixed then
this new Isa method could return the actual system call being executed
during stepping and the TaskState machine could use that to supply
syscall enter/exit events to the observers.

Committed,

Mark
Index: frysk-core/frysk/pkglibdir/funit-alarm.c
===================================================================
RCS file: frysk-core/frysk/pkglibdir/funit-alarm.c
diff -N frysk-core/frysk/pkglibdir/funit-alarm.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk-core/frysk/pkglibdir/funit-alarm.c	12 Feb 2007 15:05:34 -0000
@@ -0,0 +1,98 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2007 Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+// When counter is zero stop.
+static volatile int counter;
+
+// Dummy counter to increment so we look busy.
+static volatile int i;
+
+// struct to set itimer.
+static struct itimerval ival;
+
+static void
+signal_handler(int sig)
+{
+  if (sig == SIGPROF)
+    {
+      counter--;
+      if (counter == 0)
+	{
+	  // Shutdown timer.
+	  ival.it_value.tv_sec = 0;
+	  ival.it_value.tv_usec = 0;
+	  setitimer (ITIMER_PROF, &ival, NULL);
+	}
+    }
+  else
+    {
+      fprintf (stderr, "Wrong signal recieved %d\n", sig);
+      exit (-1);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  counter = 3;
+
+  signal (SIGPROF, &signal_handler);
+
+  // Setup a timer to fire after 0.005 seconds again and again.
+  ival.it_value.tv_sec = 0;
+  ival.it_value.tv_usec = 5000;
+  ival.it_interval.tv_sec = 0;
+  ival.it_interval.tv_usec = 5000;
+  if (setitimer (ITIMER_PROF, &ival, NULL) != 0)
+    {
+      perror ("setitimer failed");
+      exit (-1);
+    }
+
+  while (counter > 0)
+    i += counter; // Do something useful...
+
+  return 0;
+}
Index: frysk-core/frysk/proc/Isa.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/Isa.java,v
retrieving revision 1.17
diff -u -u -r1.17 Isa.java
--- frysk-core/frysk/proc/Isa.java	7 Feb 2007 18:08:25 -0000	1.17
+++ frysk-core/frysk/proc/Isa.java	12 Feb 2007 15:05:34 -0000
@@ -1,6 +1,6 @@
 // This file is part of the program FRYSK.
 //
-// Copyright 2005, 2006 Red Hat Inc.
+// Copyright 2005, 2006, 2007 Red Hat Inc.
 //
 // FRYSK is free software; you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by
@@ -127,6 +127,12 @@
   boolean hasExecutedSpuriousTrap(Task task);
 
   /**
+   * Returns true if the given Task is at an instruction that will invoke
+   * the sig return system call.
+   */
+  boolean isAtSyscallSigReturn(Task task);
+
+  /**
    * Return an array of ByteBuffers for accessing the register
    * banks. It's possible for different elements of the array to be
    * shared.
Index: frysk-core/frysk/proc/IsaIA32.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/IsaIA32.java,v
retrieving revision 1.16
diff -u -u -r1.16 IsaIA32.java
--- frysk-core/frysk/proc/IsaIA32.java	7 Feb 2007 18:08:25 -0000	1.16
+++ frysk-core/frysk/proc/IsaIA32.java	12 Feb 2007 15:05:34 -0000
@@ -325,6 +325,27 @@
 	    && task.getMemory().getByte(address - 2) == (byte) 0xcd);
   }
 
+  /**
+   * Returns true if the given Task is at an instruction that will invoke
+   * the sig return system call.
+   *
+   * On x86 this is when the pc is at a int 0x80 instruction and the
+   * eax register contains 0x77.
+   */
+  public boolean isAtSyscallSigReturn(Task task)
+  {
+    long address = pc(task);
+    boolean result = (task.getMemory().getByte(address) == (byte) 0xcd
+		      && task.getMemory().getByte(address + 1) == (byte) 0x80);
+    if (result)
+      {
+	Register eax = getRegisterByName("eax");
+	long syscall_num = eax.get(task);
+	result &= syscall_num == 0x77;
+      }
+    return result;
+  }
+
   public Syscall[] getSyscallList ()
   {
     return LinuxIa32Syscall.syscallList;
Index: frysk-core/frysk/proc/IsaPowerPC.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/IsaPowerPC.java,v
retrieving revision 1.3
diff -u -u -r1.3 IsaPowerPC.java
--- frysk-core/frysk/proc/IsaPowerPC.java	7 Feb 2007 18:08:25 -0000	1.3
+++ frysk-core/frysk/proc/IsaPowerPC.java	12 Feb 2007 15:05:34 -0000
@@ -133,6 +133,18 @@
     return false;
   }
 
+  /**
+   * Returns true if the given Task is at an instruction that will invoke
+   * the sig return system call.
+   *
+   * FIXME On powerpc this method is not yet implemented and always
+   * return false.
+   */
+  public boolean isAtSyscallSigReturn(Task task)
+  {
+    return false;
+  }
+
   public Syscall[] getSyscallList ()
   {
     return LinuxPowerPCSyscall.syscallList;
Index: frysk-core/frysk/proc/IsaX8664.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/IsaX8664.java,v
retrieving revision 1.8
diff -u -u -r1.8 IsaX8664.java
--- frysk-core/frysk/proc/IsaX8664.java	7 Feb 2007 18:08:25 -0000	1.8
+++ frysk-core/frysk/proc/IsaX8664.java	12 Feb 2007 15:05:34 -0000
@@ -314,6 +314,27 @@
     return false;
   }
 
+  /**
+   * Returns true if the given Task is at an instruction that will invoke
+   * the sig return system call.
+   *
+   * On x86_64 this is when the pc is at a 'syscall' instruction and the
+   * rax register contains 0x0f.
+   */
+  public boolean isAtSyscallSigReturn(Task task)
+  {
+    long address = pc(task);
+    boolean result = (task.getMemory().getByte(address) == (byte) 0x0f
+		      && task.getMemory().getByte(address + 1) == (byte) 0x05);
+    if (result)
+      {
+	Register rax = getRegisterByName("rax");
+	long syscall_num = rax.get(task);
+	result &= syscall_num == 0x0f;
+      }
+    return result;
+  }
+
   public Syscall[] getSyscallList ()
   {
     return LinuxX8664Syscall.syscallList;
Index: frysk-core/frysk/proc/LinuxPtraceTask.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/LinuxPtraceTask.java,v
retrieving revision 1.7
diff -u -u -r1.7 LinuxPtraceTask.java
--- frysk-core/frysk/proc/LinuxPtraceTask.java	7 Feb 2007 18:08:25 -0000	1.7
+++ frysk-core/frysk/proc/LinuxPtraceTask.java	12 Feb 2007 15:05:34 -0000
@@ -1,6 +1,6 @@
 // This file is part of the program FRYSK.
 //
-// Copyright 2005, 2006, Red Hat Inc.
+// Copyright 2005, 2006, 2007 Red Hat Inc.
 //
 // FRYSK is free software; you can redistribute it and/or modify it
 // under the terms of the GNU General Public License as published by
@@ -163,6 +163,7 @@
     logger.log(Level.FINE, "{0} sendStepInstruction\n", this);
     step_send = true;
     sig_send = sig;
+    syscall_sigret = getIsa().isAtSyscallSigReturn(this);
     try
       {
         Ptrace.singleStep(getTid(), sig);
Index: frysk-core/frysk/proc/LinuxPtraceTaskState.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/LinuxPtraceTaskState.java,v
retrieving revision 1.8
diff -u -u -r1.8 LinuxPtraceTaskState.java
--- frysk-core/frysk/proc/LinuxPtraceTaskState.java	8 Feb 2007 22:29:47 -0000	1.8
+++ frysk-core/frysk/proc/LinuxPtraceTaskState.java	12 Feb 2007 15:05:35 -0000
@@ -942,6 +942,7 @@
 		  // architectures).
 		  if (task.step_send
 		      && (task.sig_send != 0
+			  || task.syscall_sigret
 			  || isa.hasExecutedSpuriousTrap(task)))
 		    {
 		      sendContinue(task, 0);
@@ -1167,7 +1168,7 @@
 	    newState = insyscall ? inSyscallRunningTraced : syscallRunning;
 	  else
 	    newState = running;
-	  newState.sendContinue(task, 0);
+	  newState.sendContinue(task, sig);
 	  return newState;
 	}
 	
Index: frysk-core/frysk/proc/Task.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/Task.java,v
retrieving revision 1.121
diff -u -u -r1.121 Task.java
--- frysk-core/frysk/proc/Task.java	7 Feb 2007 18:08:25 -0000	1.121
+++ frysk-core/frysk/proc/Task.java	12 Feb 2007 15:05:35 -0000
@@ -907,6 +907,11 @@
   // The signal, or zero, send last to the task.
   int sig_send;
 
+  // When the last request to the process was a step request, whether
+  // it was a request to step a sigreturn syscall.
+  // Set by sendStepInstruction().
+  boolean syscall_sigret;
+
   /**
    * Notify all Code observers of the breakpoint. Return the number of
    * blocking observers or -1 if no Code observer were installed on this
Index: frysk-core/frysk/proc/TestTaskObserverInstructionSigReturn.java
===================================================================
RCS file: frysk-core/frysk/proc/TestTaskObserverInstructionSigReturn.java
diff -N frysk-core/frysk/proc/TestTaskObserverInstructionSigReturn.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk-core/frysk/proc/TestTaskObserverInstructionSigReturn.java	12 Feb 2007 15:05:35 -0000
@@ -0,0 +1,150 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2007, Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+package frysk.proc;
+
+import java.io.File;
+
+import frysk.Config;
+import frysk.sys.Sig;
+
+public class TestTaskObserverInstructionSigReturn
+  extends TestLib
+  implements TaskObserver.Attached,
+  TaskObserver.Instruction,
+  TaskObserver.Terminating,
+  TaskObserver.Signaled
+{
+  // Counter for instruction observer hits.
+  long hit;
+
+  // How the process exited.
+  int exit;
+
+  // What process task are we talking about?
+  Task task;
+
+  // How many times have we been signaled?
+  int signaled;
+
+  public void testStepSigReturn()
+  {
+    // Init.
+    hit = 0;
+    signaled = 0;
+    exit = -1;
+
+    // Start and attach.
+    String command = new File(Config.getPkgLibDir(), "funit-alarm").getPath();
+    Manager.host.requestCreateAttachedProc(new String[] {command}, this);
+    assertRunUntilStop("Creating process");
+
+    // Add a terminated and signaled observer.
+    task.requestAddTerminatingObserver(this);
+    task.requestAddSignaledObserver(this);
+    task.requestUnblock(this);
+    assertRunUntilStop("Waiting for first PROF signal");
+
+    // Add a stepping observer.
+    task.requestAddInstructionObserver(this);
+    task.requestUnblock(this);
+    assertRunUntilStop("Stepping through till completion");
+
+    assertTrue("steps were made",
+	       hit > 5 * signaled /* random very low bound guess really */);
+    assertEquals("signaled", 3, signaled);
+    assertEquals("process exited nicely", 0, exit);
+  }
+
+  // Common interface methods
+  public void addedTo (Object observable)
+  {
+    // ignored
+  }
+
+  public void addFailed (Object observable, Throwable w)
+  {
+    w.printStackTrace();
+  }
+
+  public void deletedFrom (Object observable)
+  {
+    // ignored
+  }
+
+  // TaskObserver.Attached interface
+  public Action updateAttached(Task task)
+  {
+    this.task = task;
+    Manager.eventLoop.requestStop();
+    return Action.BLOCK;
+  }
+
+  // TaskObserver.Instruction interface
+  public Action updateExecuted(Task task)
+  {
+    hit++;
+    return Action.CONTINUE;
+  }
+
+  // TaskObserver.Terminated interface
+  public Action updateTerminating(Task task, boolean signal, int exit)
+  {
+    Manager.eventLoop.requestStop();
+
+    this.exit = exit;
+    return Action.CONTINUE;
+  }
+
+  // TaskObserver.Signaled interface
+  public Action updateSignaled (Task task, int signal)
+  {
+    if (signal != Sig.PROF_)
+      fail("Wrong signal received: " + signal);
+    
+    signaled++;
+    if (signaled == 1)
+      {
+	Manager.eventLoop.requestStop();
+	return Action.BLOCK;
+      }
+
+    return Action.CONTINUE;
+  }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]