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]

New syscall/signal test


Hi,

I wrote this because I wanted to make sure I didn't introduce any
regressions while rewriting some parts of TaskState. And now it actually
caught a bug I was about to introduce that none of the existing tests
would have caught. So I am adding it to CVS to make sure it is always
run.

2006-09-07  Mark Wielaard  <mark@klomp.org>

        * TestSyscallSignal.java: New file.

2006-09-07  Mark Wielaard  <mark@klomp.org>

        * funit-syscall-signal.c: New test prog.

It is just a simple mixing of syscall and signal tracing.

Cheers,

Mark
// This file is part of the program FRYSK.
//
// Copyright 2006, 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.*;

import frysk.sys.Sig;
import frysk.sys.Signal;
import frysk.sys.SyscallNum;

public class TestSyscallSignal
  extends TestLib
{
  // Process id and Proc representation of our test program.
  int pid;
  Proc proc;

  // How we communicate with the test program.
  BufferedReader in;
  DataOutputStream out;

  // The thread that handles the event loop.
  EventLoopRunner eventloop;

  // Monitor to notify and wait on for state of event changes..
  static Object monitor = new Object();

  /**
   * Launch our test program and setup clean environment with a runner
   * eventloop.
   */
  public void setUp()
  {
    // Make sure everything is setup so spawned processes are recognized
    // and destroyed in tearDown().
    super.setUp();

    // Create a process that we will communicate with through stdin/out.
    String command = TestLib.getExecPrefix() + "funit-syscall-signal";
    ForkTestLib.ForkedProcess process;
    process = ForkTestLib.fork(new String[] { command });
    pid = process.pid;

    in = new BufferedReader(new InputStreamReader(process.in));
    out = new DataOutputStream(process.out);

    // Make sure the core knows about it.
    Manager.host.requestRefreshXXX(true);
    Manager.eventLoop.runPending();
    proc = Manager.host.getProc(new ProcId(pid));

    // Start an EventLoop so we don't have to poll for events all the time.
    eventloop = new EventLoopRunner();
    eventloop.start();
  }

  /**
   * Make sure the test program is really gone and the event loop is
   * stopped.  Individual tests are responsible for nice termination
   * if the want to.
   */
  public void tearDown()
  {
    // Make sure event loop is gone.
    eventloop.requestStop();
    synchronized (monitor)
      {
	while (!eventloop.isStopped())
	  {
	    try
	      {
		monitor.wait();
	      }
	    catch (InterruptedException ie)
	      {
		// Ignored
	      }
	  }
      }

    // And kill off any remaining processes we spawned
    super.tearDown();
  }

  public void testIt() throws IOException
  {
    String line;

    // Make sure the process is "ready"
    line = in.readLine();

    final Task task = proc.getMainTask();

    final SignalObserver sigo = new SignalObserver(Sig.HUP_);
    task.requestAddSignaledObserver(sigo);
    final SyscallObserver syso = new SyscallObserver(42);
    task.requestAddSyscallObserver(syso);

    // Make sure the observers are properly installed.
    synchronized (monitor)
      {
	while (! sigo.isAdded() || ! syso.isAdded())
	  {
	    try
	      {
		monitor.wait();
	      }
	    catch (InterruptedException ie)
	      {
		// ignored
	      }
	  }
      }

    // Kill 1...
    Signal.tkill(pid, Sig.HUP);

    // Tell the process to go some rounds!
    out.writeByte(42);
    out.flush();

    // Wait till our syscall observer triggers and blocks
    // (which is "half way" through the run, there are 42 * 2 syscalls).
    synchronized (monitor)
      {
        while (syso.getEntered() != 42)
          {
            try
              {
                monitor.wait();
              }
            catch (InterruptedException ie)
              {
                // ignored
              }
          }
      }
    
    // Now send a signal to the process while blocked. Then unblock.
    // Do all this on the eventloop so properly serialize calls.
    Manager.eventLoop.add(new TaskEvent()
      {
	public void execute ()
	{
	  Signal.tkill(task.getTid(), Sig.HUP);
	  
	  // And continue running.
	  task.requestUnblock(syso);
	}
      });

    // Sanity check that the functions have actually been run.
    line = in.readLine();
    int hup_cnt = Integer.decode(line).intValue();

    assertEquals(2, hup_cnt);
    assertEquals(2, sigo.getTriggered());

    assertEquals(2 * 42, syso.getEntered());
    assertEquals(2 * 42, syso.getExited());

    // Kill 3...
    Signal.tkill(pid, Sig.HUP);

    // Run some more
    out.writeByte(100);
    out.flush();

    // Sanity check that the functions have actually been run.
    line = in.readLine();
    hup_cnt = Integer.decode(line).intValue();

    assertEquals(3, hup_cnt);
    assertEquals(3, sigo.getTriggered());

    assertEquals(2 * 142, syso.getEntered());
    assertEquals(2 * 142, syso.getExited());

    // We are all done, you can go now.
    out.writeByte(0);
    out.flush();
  }

  class SignalObserver implements TaskObserver.Signaled
  {
    private final int sig;

    private int triggered;
    private boolean added;
    private boolean removed;

    SignalObserver(int sig)
    {
      this.sig = sig;
    }

    public Action updateSignaled(Task task, int signal)
    {
      if (signal == sig)
	triggered++;
      return Action.CONTINUE;
    }

    int getTriggered()
    {
      return triggered;
    }

    public void addFailed(Object observable, Throwable w)
    {
      w.printStackTrace();
      fail(w.getMessage());
    }
    
    public void addedTo(Object observable)
    {
      // Hurray! Lets notify everybody.
      synchronized (monitor)
	{
	  added = true;
	  removed = false;
	  monitor.notifyAll();
	}
    }

    public boolean isAdded()
    {
      return added;
    }
    
    public void deletedFrom(Object observable)
    {
      synchronized (monitor)
	{
	  removed = true;
	  added = true;
	  monitor.notifyAll();
	}
    }

    public boolean isRemoved()
    {
      return removed;
    }
  }

  /**
   * Observer that looks for open and close syscalls.
   * After a given number of calls it will BLOCK from the syscall enter.
   */
  class SyscallObserver implements TaskObserver.Syscall
  {
    private final int stophits;

    private int entered;
    private int exited;
    private boolean added;
    private boolean removed;

    SyscallObserver(int stophits)
    {
      this.stophits = stophits;
    }

    public Action updateSyscallEnter(Task task)
    {
      SyscallEventInfo syscallEventInfo = getSyscallEventInfo(task);
      int syscallNum = syscallEventInfo.number (task);
      if (syscallNum == SyscallNum.SYSopen
	  || syscallNum == SyscallNum.SYSclose)
	{
	  entered++;
	  if (entered == stophits)
	    {
	      synchronized(monitor)
		{
		  monitor.notifyAll();
		  return Action.BLOCK;
		}
	    }
	}
      return Action.CONTINUE;
    }

    public Action updateSyscallExit(Task task)
    {
      SyscallEventInfo syscallEventInfo = getSyscallEventInfo(task);
      int syscallNum = syscallEventInfo.number (task);
      if (syscallNum == SyscallNum.SYSopen
	  || syscallNum == SyscallNum.SYSclose)
	{
	  exited++;
	}
      return Action.CONTINUE;
    }

    int getEntered()
    {
      return entered;
    }

    int getExited()
    {
      return exited;
    }

    public void addFailed(Object observable, Throwable w)
    {
      w.printStackTrace();
      fail(w.getMessage());
    }
    
    public void addedTo(Object observable)
    {
      // Hurray! Lets notify everybody.
      synchronized (monitor)
	{
	  added = true;
	  removed = false;
	  monitor.notifyAll();
	}
    }

    public boolean isAdded()
    {
      return added;
    }
    
    public void deletedFrom(Object observable)
    {
      synchronized (monitor)
	{
	  removed = true;
	  added = true;
	  monitor.notifyAll();
	}
    }

    public boolean isRemoved()
    {
      return removed;
    }

    private SyscallEventInfo getSyscallEventInfo(Task task)
    {
      try
	{
	  return task.getSyscallEventInfo();
	}
      catch (TaskException e)
	{
	  fail("task exception " + e);
	  return null; // not reached
	}
    }
  }

  static class EventLoopRunner extends Thread
  {
    private boolean stopped;

    public void run()
    {
      stopped = false;
      try
	{
	  Manager.eventLoop.run();
	}
      finally
	{
	  synchronized (monitor)
	    {
	      stopped = true;
	      monitor.notifyAll();
	    }
	}
    }

    public void requestStop()
    {
      Manager.eventLoop.requestStop();
    }

    public boolean isStopped()
    {
      return stopped;
    }

    public String toString()
    {
      return "EventLoop-" + super.toString();
    }
  }
}
// This file is part of the program FRYSK.
//
// Copyright 2006, 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

// How many times our sighup handler was called.
static volatile int hupcnt;

static volatile int runs;

static void
hup_handler (int sig)
{
   hupcnt++;
}

int
main ()
{
  hupcnt = 0;

  signal(SIGHUP, &hup_handler);

  printf("ready\n");
  fflush(stdout);

  // Go round and round.
  while (1)
    {
      // Wait till we are OK to go!
      runs = getchar();
      if (runs == 0)
	break;
      if (runs < 0) 
        {
	  fprintf(stderr, "Couldn't read runs\n");
	  break;
        }

      while(runs--)
	{
	  int fd = open ("a.file", O_RDONLY);
	  close (fd);
	}

      printf("%d\n", hupcnt);
      fflush(stdout);
    }

  printf("%d\n", hupcnt);
  fflush(stdout);

  return runs;
}

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