an early version of chakana, an automated test framework used with COOJA
This commit is contained in:
parent
9be473e4b9
commit
65533c0c00
29 changed files with 4983 additions and 0 deletions
13
tools/chakana/CYGWIN_USERS.txt
Normal file
13
tools/chakana/CYGWIN_USERS.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
If you're on Cygwin and run into the 'sem_init: Resource temporarily unavailable' problem:
|
||||
|
||||
Current Cygwin python versions have problems with running out of PIDs, it is a known problem and there has been several discussions and bugreports related to it.
|
||||
As of today, the best solution seems to be to rebase the Cygwin libraries:
|
||||
0> Make sure you have the rebaseall program, it can be downloaded using Cygwin's setup.exe
|
||||
|
||||
1> Shut down all Cygwin processes and services (including shells)
|
||||
2> Start a regular Win command prompt ('cmd')
|
||||
3> Start 'ash'
|
||||
4> Start '/bin/rebaseall', expect this step to take some time..
|
||||
5> All done! This worked for me.
|
||||
|
||||
-- Fredrik <fros@sics.se>, 2007
|
1
tools/chakana/__init__.py
Normal file
1
tools/chakana/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Chakana, an automated test harness for networked embedded systems."""
|
30
tools/chakana/build.xml
Normal file
30
tools/chakana/build.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<project name="Chakana COOJA" default="compile_plugin" basedir=".">
|
||||
<property name="contiki" value="../.."/>
|
||||
<property name="cooja" value="${contiki}/tools/cooja"/>
|
||||
<property name="cooja_jar" value="${cooja}/dist/cooja.jar"/>
|
||||
|
||||
<property name="build" location="build"/>
|
||||
|
||||
<property name="tools_config" value="cooja.chakana.properties"/>
|
||||
|
||||
<target name="init">
|
||||
<tstamp/>
|
||||
</target>
|
||||
|
||||
<target name="compile_cooja" depends="init">
|
||||
<ant antfile="build.xml" dir="${cooja}" target="jar" inheritAll="false"/>
|
||||
</target>
|
||||
|
||||
<target name="compile_plugin" depends="init, compile_cooja">
|
||||
<ant antfile="build.xml" dir="cooja_plugin" target="jar" inheritAll="false">
|
||||
<property name="contiki" value="${contiki}/.."/>
|
||||
</ant>
|
||||
</target>
|
||||
|
||||
<target name="clean" depends="init">
|
||||
<delete dir="${build}"/>
|
||||
</target>
|
||||
|
||||
</project>
|
190
tools/chakana/command.py
Normal file
190
tools/chakana/command.py
Normal file
|
@ -0,0 +1,190 @@
|
|||
#
|
||||
# Copyright 2005-2007 Swedish Institute of Computer Science.
|
||||
#
|
||||
# Please refer to the file named LICENSE in the same directory as this file
|
||||
# for licensing information.
|
||||
#
|
||||
# Written and maintained by Lars Albertsson <lalle@sics.se>.
|
||||
#
|
||||
|
||||
# $Id: command.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
|
||||
|
||||
"""Utility routines for running external commands."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import popen2
|
||||
import re
|
||||
import signal
|
||||
import threading
|
||||
|
||||
import chakana.error
|
||||
import chakana.linux
|
||||
import chakana.threads
|
||||
|
||||
def quote(argument, useTick = 1):
|
||||
"""Quote an argument, or a list of arguments, in order to pass it
|
||||
through a shell."""
|
||||
if type(argument) in (type([]), type(())):
|
||||
return " ".join(map(quote, argument))
|
||||
if useTick and not "'" in argument:
|
||||
return "'" + argument + "'"
|
||||
return '"' + argument.replace('\\', '\\\\').replace('"', '\\"').replace(
|
||||
'`', '\\`').replace('$', '\\$') + '"'
|
||||
|
||||
def softQuote(argument):
|
||||
"""Quote an argument, or a list of arguments, in order to pass it through
|
||||
a shell if it is necessary. Always quotes with quotation mark, never
|
||||
with tick."""
|
||||
if type(argument) in (type([]), type(())):
|
||||
return " ".join(map(softQuote, argument))
|
||||
if re.match(r'^[\w,./=-]*$', argument):
|
||||
return argument
|
||||
else:
|
||||
return quote(argument, useTick = 0)
|
||||
|
||||
def deQuote(argument):
|
||||
"""Remove quoting in the same manner as the shell (bash) does."""
|
||||
if argument == "":
|
||||
return ""
|
||||
if argument[0] == "'":
|
||||
nextQuote = argument.find("'", 1)
|
||||
if nextQuote == -1:
|
||||
raise ValueError("Unmatched quote \"'\"")
|
||||
return argument[1 : nextQuote] + deQuote(argument[nextQuote + 1 :])
|
||||
if len(argument) > 1 and argument[-1] == "'":
|
||||
return argument[1:-1]
|
||||
elif argument[0] == '"':
|
||||
ret = []
|
||||
index = 1
|
||||
try:
|
||||
while True:
|
||||
if argument[index] == '"':
|
||||
return "".join(ret) + deQuote(argument[index + 1 :])
|
||||
if argument[index] == '\\':
|
||||
index += 1
|
||||
if argument[index] != '\n':
|
||||
ret.append(argument[index])
|
||||
else:
|
||||
ret.append(argument[index])
|
||||
index += 1
|
||||
except IndexError:
|
||||
raise ValueError("Unmatched quote '\"'")
|
||||
elif argument[0] == '\\':
|
||||
if len(argument) == 1:
|
||||
return argument[0]
|
||||
if argument[1] != '\n':
|
||||
return argument[1] + deQuote(argument[2:])
|
||||
else:
|
||||
return argument[2:]
|
||||
return argument[0] + deQuote(argument[1:])
|
||||
|
||||
|
||||
def runString(argv, environmentVars = ("PYTHONPATH", )):
|
||||
"""Return a quoted string that can be pasted into a shell in order
|
||||
to rerun a command."""
|
||||
|
||||
envPrefix = ""
|
||||
for envVar in environmentVars:
|
||||
envPrefix += envVar + "=" + os.environ.get(envVar, "") + " "
|
||||
return "( cd " + os.getcwd() + " && " + envPrefix + quote(argv) + " )"
|
||||
|
||||
class Reader:
|
||||
def __call__(self, fileObj):
|
||||
self._data = []
|
||||
while 1:
|
||||
newData = fileObj.read()
|
||||
if newData == "":
|
||||
return
|
||||
self._data.append(newData)
|
||||
|
||||
def result(self):
|
||||
return "".join(self._data)
|
||||
|
||||
class Writer:
|
||||
def __init__(self, fileObj, data):
|
||||
self._fileObj = fileObj
|
||||
self._data = data
|
||||
if self._data == "":
|
||||
self._fileObj.close()
|
||||
self._threads = []
|
||||
else:
|
||||
self._threads = [threading.Thread(target = self.write)]
|
||||
self._threads[0].start()
|
||||
|
||||
def write(self):
|
||||
self._fileObj.write(self._data)
|
||||
self._fileObj.close()
|
||||
|
||||
def activeThreads(self):
|
||||
return self._threads
|
||||
|
||||
class Runner:
|
||||
def __init__(self, child, timeout):
|
||||
self._child = child
|
||||
self._timeout = timeout
|
||||
self._threads = []
|
||||
self._output = []
|
||||
if self._timeout is None:
|
||||
self.read()
|
||||
self.wait()
|
||||
else:
|
||||
self._threads.append(threading.Thread(target = self.read))
|
||||
self._threads[-1].start()
|
||||
self._threads.append(threading.Thread(target = self.wait))
|
||||
self._threads[-1].start()
|
||||
|
||||
def activeThreads(self):
|
||||
return self._threads
|
||||
|
||||
def read(self):
|
||||
while 1:
|
||||
newData = self._child.fromchild.read()
|
||||
if newData == "":
|
||||
return
|
||||
self._output.append(newData)
|
||||
|
||||
def wait(self):
|
||||
self._status = self._child.wait()
|
||||
|
||||
def output(self):
|
||||
return "".join(self._output)
|
||||
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
def output(command, inputData = "", timeout = None):
|
||||
"""Run command in a separate subprocess. Send inputData to stdin.
|
||||
Kill subprocess and raise error.Timeout after timeout seconds,
|
||||
unless timeout is None."""
|
||||
|
||||
# Due to python bug 1183780 (Popen4.wait not thread-safe), avoid
|
||||
# threads if we don't need them.
|
||||
|
||||
child = popen2.Popen4(command)
|
||||
writer = Writer(child.tochild, inputData)
|
||||
runner = Runner(child, timeout)
|
||||
|
||||
try:
|
||||
chakana.threads.waitForAll(writer.activeThreads() + runner.activeThreads(),
|
||||
timeout)
|
||||
except chakana.error.Timeout, timeoutErr:
|
||||
try:
|
||||
childProc = chakana.linux.Process(child.pid)
|
||||
childProc.killAllBelow()
|
||||
childProc.reallyKill()
|
||||
except OSError, err:
|
||||
if not err.errno in (errno.ESRCH, errno.ECHILD):
|
||||
raise
|
||||
raise chakana.error.CommandFailed(command, timeoutErr, runner.output())
|
||||
|
||||
if runner.status() != 0:
|
||||
raise chakana.error.CommandFailed(
|
||||
command, runner.status(), runner.output())
|
||||
if re.search(r"\blibefence", os.environ.get("LD_PRELOAD", "")):
|
||||
lines = runner.output().splitlines()
|
||||
if (len(lines) >= 2) and (lines[0] == "") and \
|
||||
lines[1].strip().startswith("Electric Fence "):
|
||||
return "\n".join(lines[2:] + [""])
|
||||
return runner.output()
|
||||
|
1
tools/chakana/cooja.chakana.properties
Normal file
1
tools/chakana/cooja.chakana.properties
Normal file
|
@ -0,0 +1 @@
|
|||
DEFAULT_PROJECTDIRS=../cooja_plugin
|
3
tools/chakana/cooja_plugin/README
Normal file
3
tools/chakana/cooja_plugin/README
Normal file
|
@ -0,0 +1,3 @@
|
|||
Main Chakana COOJA plugin.
|
||||
|
||||
Communicates with Chakana, and should automatically be started with the simulator.
|
40
tools/chakana/cooja_plugin/build.xml
Normal file
40
tools/chakana/cooja_plugin/build.xml
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<project name="Chakana COOJA plugin" default="jar" basedir=".">
|
||||
<property name="source" location="java"/>
|
||||
<property name="build" location="build"/>
|
||||
<property name="lib" location="lib"/>
|
||||
|
||||
<property name="contiki" location="../../.."/>
|
||||
<property name="cooja" location="${contiki}/tools/cooja"/>
|
||||
<property name="cooja_jar" value="${cooja}/dist/cooja.jar"/>
|
||||
|
||||
<target name="init">
|
||||
<tstamp/>
|
||||
</target>
|
||||
|
||||
<target name="compile" depends="init">
|
||||
<mkdir dir="${build}"/>
|
||||
<javac srcdir="${source}" destdir="${build}">
|
||||
<classpath>
|
||||
<pathelement path="."/>
|
||||
<pathelement location="${cooja_jar}"/>
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="clean" depends="init">
|
||||
<delete dir="${build}"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="init, compile">
|
||||
<mkdir dir="${lib}"/>
|
||||
<jar destfile="${lib}/chakana.jar" basedir="${build}">
|
||||
<fileset dir="${build}"/>
|
||||
<manifest>
|
||||
<attribute name="Class-Path" value="."/>
|
||||
</manifest>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
</project>
|
2
tools/chakana/cooja_plugin/cooja.config
Normal file
2
tools/chakana/cooja_plugin/cooja.config
Normal file
|
@ -0,0 +1,2 @@
|
|||
se.sics.cooja.GUI.PLUGINS = + se.sics.chakana.ChakanaPlugin
|
||||
se.sics.cooja.GUI.JARFILES = + chakana.jar
|
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: ChakanaPlugin.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Collection;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jdom.Element;
|
||||
|
||||
import se.sics.cooja.*;
|
||||
|
||||
/**
|
||||
* Main Chakana plugin.
|
||||
*
|
||||
* Provides remote control functionality to COOJA via socket connections.
|
||||
*
|
||||
* @see #SERVER_PORT
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
@ClassDescription("Chakana COOJA Server")
|
||||
@PluginType(PluginType.COOJA_STANDARD_PLUGIN)
|
||||
public class ChakanaPlugin implements Plugin {
|
||||
public static final String VERSION = "0.1";
|
||||
|
||||
/**
|
||||
* COOJA's Chakana-plugin server port
|
||||
*/
|
||||
public static final int SERVER_PORT = 1234;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChakanaPlugin.class);
|
||||
|
||||
private GUI myGUI;
|
||||
|
||||
// Chakana communication
|
||||
private Thread serverThread = null;
|
||||
|
||||
private ServerSocket serverSocket = null;
|
||||
|
||||
private Thread clientThread = null;
|
||||
|
||||
private Socket clientSocket = null;
|
||||
|
||||
private EventpointEvaluator myEvaluator = null;
|
||||
|
||||
private XMLCommandHandler myCommandHandler = null;
|
||||
|
||||
private boolean shutdown = false;
|
||||
|
||||
private Object tag = null;
|
||||
|
||||
/**
|
||||
* Creates new Chakana remote control plugin.
|
||||
*
|
||||
* @param gui COOJA Simulator
|
||||
*/
|
||||
public ChakanaPlugin(GUI gui) {
|
||||
myGUI = gui;
|
||||
|
||||
// Create eventpoint evaluator
|
||||
myEvaluator = new EventpointEvaluator(myGUI);
|
||||
|
||||
// Create remote command handler
|
||||
myCommandHandler = new XMLCommandHandler(myGUI, this, myEvaluator);
|
||||
|
||||
// Open server socket
|
||||
serverThread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
serverSocket = new ServerSocket(SERVER_PORT);
|
||||
logger.info("Chakana server listening on port " + SERVER_PORT + ".");
|
||||
} catch (Exception e) {
|
||||
logger.fatal("Could not start server thread: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle incoming connections
|
||||
while (serverSocket != null) {
|
||||
try {
|
||||
Socket skt = serverSocket.accept();
|
||||
handleNewConnection(skt);
|
||||
} catch (Exception e) {
|
||||
if (!shutdown)
|
||||
logger.fatal("Server thread exception: " + e.getMessage());
|
||||
if (serverThread != null && serverThread.isInterrupted()) {
|
||||
serverThread = null;
|
||||
}
|
||||
serverSocket = null;
|
||||
}
|
||||
}
|
||||
logger.info("Chakana server thread terminating");
|
||||
}
|
||||
}, "chakana listen thread");
|
||||
serverThread.start();
|
||||
}
|
||||
|
||||
public void performCOOJAShutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming connection.
|
||||
*
|
||||
* @param socket Socket
|
||||
*/
|
||||
private void handleNewConnection(Socket socket) {
|
||||
logger.info("Received request from " + socket.getInetAddress() + ":"
|
||||
+ socket.getPort());
|
||||
|
||||
if (clientThread != null) {
|
||||
// Refuse connection
|
||||
logger.warn("A client is already connected, refusing new connection");
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Start thread handling connection
|
||||
clientSocket = socket;
|
||||
clientThread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
// Open stream
|
||||
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket
|
||||
.getInputStream()));
|
||||
|
||||
// Send welcome message
|
||||
writer.print(XMLCommandHandler.createInfoMessage("Chakana COOJA plugin, version " + VERSION) + "\n");
|
||||
writer.flush();
|
||||
|
||||
// Handle incoming data (blocks until connection terminated)
|
||||
String line;
|
||||
String command = "";
|
||||
while ((!shutdown) && ((line = reader.readLine()) != null)) {
|
||||
logger.debug("<-- " + line);
|
||||
command += line;
|
||||
|
||||
if (myCommandHandler.containsEntireCommand(command)) {
|
||||
String reply = myCommandHandler.handleCommand(command);
|
||||
logger.debug("--> " + reply);
|
||||
writer.print(reply + "\n");
|
||||
writer.flush();
|
||||
command = "";
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Terminating Chakana connection");
|
||||
|
||||
// Clean up connection
|
||||
if (writer != null) {
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
if (clientSocket != null && !clientSocket.isClosed())
|
||||
clientSocket.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.fatal("Client connection exception: " + e);
|
||||
}
|
||||
|
||||
clientThread = null;
|
||||
clientSocket = null;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myGUI.doQuit(false);
|
||||
}
|
||||
});
|
||||
logger.debug("Chakana client thread terminating");
|
||||
}
|
||||
}, "chakana client thread");
|
||||
clientThread.start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shut down any open server socket, and kill server thread.
|
||||
*/
|
||||
protected void closeServer() {
|
||||
// Shut down server
|
||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||
logger.info("Closing server socket");
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.fatal("Error when closing server socket: " + e);
|
||||
}
|
||||
serverSocket = null;
|
||||
}
|
||||
|
||||
if (serverThread != null && serverThread.isAlive()) {
|
||||
logger.info("Interrupting server thread");
|
||||
serverThread.interrupt();
|
||||
serverThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect all connected clients. Currently only one client can be
|
||||
* connected at the same time.
|
||||
*/
|
||||
protected void disconnectClients() {
|
||||
// Disconnect any current accepted client
|
||||
if (clientSocket != null && !clientSocket.isClosed()) {
|
||||
logger.info("Closing client socket");
|
||||
try {
|
||||
clientSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.fatal("Error when closing client socket: " + e);
|
||||
}
|
||||
clientSocket = null;
|
||||
}
|
||||
|
||||
if (clientThread != null && clientThread.isAlive()) {
|
||||
logger.info("Interrupting client thread");
|
||||
clientThread.interrupt();
|
||||
clientThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void closePlugin() {
|
||||
closeServer();
|
||||
disconnectClients();
|
||||
}
|
||||
|
||||
public void tagWithObject(Object tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public Object getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public Collection<Element> getConfigXML() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean setConfigXML(Collection<Element> configXML,
|
||||
boolean visAvailable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: EventpointEvaluator.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana;
|
||||
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.Vector;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import se.sics.cooja.GUI;
|
||||
import se.sics.cooja.Simulation;
|
||||
import se.sics.chakana.eventpoints.*;
|
||||
|
||||
/**
|
||||
* Keeps track of all active eventpoints. The control method blocks until any of
|
||||
* the active eventpoints trigger.
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class EventpointEvaluator {
|
||||
private static Logger logger = Logger.getLogger(EventpointEvaluator.class);
|
||||
|
||||
private GUI myGUI;
|
||||
private Vector<Eventpoint> allEventpoints;
|
||||
private Eventpoint lastTriggeredEventpoint;
|
||||
private Simulation sim = null;
|
||||
|
||||
private int counterEventpointID = 0;
|
||||
|
||||
private Observer tickObserver = new Observer() {
|
||||
public void update(Observable obs, Object obj) {
|
||||
// Evaluate active eventpoints
|
||||
for (Eventpoint eventpoint: allEventpoints) {
|
||||
if (eventpoint.evaluate()) {
|
||||
lastTriggeredEventpoint = eventpoint;
|
||||
sim.stopSimulation();
|
||||
logger.info("Eventpoint triggered: " + eventpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new eventpoint evaluator.
|
||||
*
|
||||
* @param gui GUI with simulation
|
||||
*/
|
||||
public EventpointEvaluator(GUI gui) {
|
||||
myGUI = gui;
|
||||
allEventpoints = new Vector<Eventpoint>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Blocks until an active eventpoint triggers.
|
||||
*
|
||||
* @return Triggered eventpoint
|
||||
* @throws EventpointException At evenpoint setup errors
|
||||
*/
|
||||
public Eventpoint resumeSimulation() throws EventpointException {
|
||||
if (myGUI.getSimulation() == null)
|
||||
throw new EventpointException("No simulation to observe");
|
||||
if (allEventpoints == null || allEventpoints.isEmpty())
|
||||
throw new EventpointException("No eventpoints exist");
|
||||
|
||||
// Make sure tick observer is observing the current simulation
|
||||
myGUI.getSimulation().deleteTickObserver(tickObserver);
|
||||
myGUI.getSimulation().addTickObserver(tickObserver);
|
||||
|
||||
// Evaluate active eventpoints before starting simulation
|
||||
for (Eventpoint eventpoint: allEventpoints) {
|
||||
if (eventpoint.evaluate()) {
|
||||
lastTriggeredEventpoint = eventpoint;
|
||||
logger.info("Eventpoint triggered (EARLY): " + eventpoint);
|
||||
return lastTriggeredEventpoint;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset last triggered eventpoint and start simulation
|
||||
lastTriggeredEventpoint = null;
|
||||
sim = myGUI.getSimulation();
|
||||
sim.startSimulation();
|
||||
|
||||
// Block until tickobserver stops simulation
|
||||
while (lastTriggeredEventpoint == null || myGUI.getSimulation().isRunning()) {
|
||||
Thread.yield();
|
||||
}
|
||||
|
||||
if (lastTriggeredEventpoint == null)
|
||||
throw new EventpointException("Simulation was stopped without eventpoint triggering");
|
||||
|
||||
return lastTriggeredEventpoint;
|
||||
}
|
||||
|
||||
public Eventpoint getTriggeredEventpoint() {
|
||||
return lastTriggeredEventpoint;
|
||||
}
|
||||
|
||||
public int getLastTriggeredEventpointID() {
|
||||
return lastTriggeredEventpoint.getID();
|
||||
}
|
||||
|
||||
public Eventpoint getEventpoint(int id) {
|
||||
for (Eventpoint eventpoint: allEventpoints) {
|
||||
if (eventpoint.getID() == id) {
|
||||
return eventpoint;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addEventpoint(Eventpoint eventpoint) {
|
||||
eventpoint.setID(counterEventpointID++);
|
||||
allEventpoints.add(eventpoint);
|
||||
}
|
||||
|
||||
public void clearAllEventpoints() {
|
||||
allEventpoints.clear();
|
||||
}
|
||||
|
||||
public void deleteEventpoint(int id) {
|
||||
Eventpoint eventpointToDelete = null;
|
||||
for (Eventpoint eventpoint: allEventpoints) {
|
||||
if (eventpoint.getID() == id) {
|
||||
eventpointToDelete = eventpoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eventpointToDelete != null)
|
||||
allEventpoints.remove(eventpointToDelete);
|
||||
}
|
||||
|
||||
class EventpointException extends Exception {
|
||||
public EventpointException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: Eventpoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
public interface Eventpoint {
|
||||
|
||||
/**
|
||||
* Evaluates eventpoint.
|
||||
*
|
||||
* @return True if eventpoint triggered, false otherwise
|
||||
*/
|
||||
public boolean evaluate();
|
||||
|
||||
/**
|
||||
* @return Optional information of triggered eventpoint
|
||||
*/
|
||||
public String getMessage();
|
||||
|
||||
/**
|
||||
* @param id Eventpoint ID
|
||||
*/
|
||||
public void setID(int id);
|
||||
|
||||
/**
|
||||
* @return Unique eventpoint ID
|
||||
*/
|
||||
public int getID();
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: IntegerWatchpoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
import se.sics.cooja.Mote;
|
||||
|
||||
/**
|
||||
* TODO Document
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class IntegerWatchpoint extends VariableWatchpoint {
|
||||
public IntegerWatchpoint(Mote mote, String varName) {
|
||||
super(mote, varName, 4);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: RadioMediumEventpoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
import se.sics.cooja.RadioMedium;
|
||||
|
||||
/**
|
||||
* Triggers if radio medium changes
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class RadioMediumEventpoint implements Eventpoint {
|
||||
protected boolean shouldBreak = false;
|
||||
private int myID = 0;
|
||||
protected RadioMedium radioMedium;
|
||||
private int count = 1;
|
||||
|
||||
public RadioMediumEventpoint(RadioMedium radioMedium) {
|
||||
if (radioMedium == null) {
|
||||
// TODO Throw exception
|
||||
return;
|
||||
}
|
||||
|
||||
this.radioMedium = radioMedium;
|
||||
|
||||
// Register as radio medium observer
|
||||
radioMedium.addRadioMediumObserver(new Observer() {
|
||||
public void update(Observable obs, Object obj) {
|
||||
handleRadioMediumChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public RadioMediumEventpoint(RadioMedium radioMedium, int count) {
|
||||
this(radioMedium);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
protected void handleRadioMediumChange() {
|
||||
count--;
|
||||
if (count < 1)
|
||||
shouldBreak = true;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Radio medium changed";
|
||||
}
|
||||
|
||||
public boolean evaluate() {
|
||||
return shouldBreak;
|
||||
}
|
||||
|
||||
public void setID(int id) {
|
||||
myID = id;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return myID;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: RealTimepoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
/**
|
||||
* TODO Document
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class RealTimepoint implements Timepoint {
|
||||
private int myID = 0;
|
||||
private long endTime;
|
||||
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* TODO Document
|
||||
*
|
||||
* TODO Use timer instead, much faster!
|
||||
*
|
||||
* @param time Duration in milliseconds
|
||||
*/
|
||||
public RealTimepoint(long time) {
|
||||
this.endTime = System.currentTimeMillis() + time;
|
||||
}
|
||||
|
||||
public boolean evaluate() {
|
||||
if (System.currentTimeMillis() >= endTime) {
|
||||
message = "Real time = " + System.currentTimeMillis() + " >= " + endTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Real timepoint: " + endTime;
|
||||
}
|
||||
|
||||
public void setID(int id) {
|
||||
myID = id;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return myID;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: SimulationTimepoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
import se.sics.cooja.Simulation;
|
||||
|
||||
/**
|
||||
* TODO Document
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class SimulationTimepoint implements Timepoint {
|
||||
private int myID = 0;
|
||||
private Simulation simulation;
|
||||
private int time;
|
||||
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* TODO Document
|
||||
*
|
||||
* @param simulation
|
||||
* @param time
|
||||
*/
|
||||
public SimulationTimepoint(Simulation simulation, int time) {
|
||||
this.simulation = simulation;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public boolean evaluate() {
|
||||
if (simulation.getSimulationTime() >= time) {
|
||||
message = "Simulation time = " + simulation.getSimulationTime() + " >= " + time;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Simulation timepoint: " + time;
|
||||
}
|
||||
|
||||
public void setID(int id) {
|
||||
myID = id;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return myID;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: Timepoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
/**
|
||||
* A timepoint triggers at and after a given time.
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public interface Timepoint extends Eventpoint {
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: TransmissionRadioMediumEventpoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
import se.sics.cooja.RadioMedium;
|
||||
|
||||
/**
|
||||
* Triggers when a radio medium transmission has been completed
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class TransmissionRadioMediumEventpoint extends RadioMediumEventpoint {
|
||||
private String message = "";
|
||||
private int count = 1;
|
||||
|
||||
public TransmissionRadioMediumEventpoint(RadioMedium radioMedium) {
|
||||
super(radioMedium);
|
||||
}
|
||||
|
||||
public TransmissionRadioMediumEventpoint(RadioMedium radioMedium, int count) {
|
||||
this(radioMedium);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
protected void handleRadioMediumChange() {
|
||||
if (radioMedium.getLastTickConnections() != null) {
|
||||
count -= radioMedium.getLastTickConnections().length;
|
||||
if (count < 1) {
|
||||
shouldBreak = true;
|
||||
message = radioMedium.getLastTickConnections().length + " transmissions were completed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: VariableWatchpoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
import se.sics.cooja.Mote;
|
||||
import se.sics.cooja.SectionMoteMemory;
|
||||
|
||||
/**
|
||||
* TODO Document
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class VariableWatchpoint extends Watchpoint {
|
||||
private Mote mote;
|
||||
private String name;
|
||||
|
||||
private String reason;
|
||||
|
||||
public VariableWatchpoint(Mote mote, String variableName, int size) {
|
||||
super(mote, ((SectionMoteMemory) mote.getMemory())
|
||||
.getVariableAddress(variableName), size);
|
||||
this.mote = mote;
|
||||
this.name = variableName;
|
||||
}
|
||||
|
||||
public boolean evaluate() {
|
||||
boolean shouldBreak = super.evaluate();
|
||||
|
||||
if (shouldBreak) {
|
||||
reason = "Variable '" + name + "' changed";
|
||||
}
|
||||
return shouldBreak;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Variable watchpoint: " + name + " @ " + mote;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Swedish Institute of Computer Science. All rights
|
||||
* reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of the
|
||||
* Institute nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: Watchpoint.java,v 1.1 2007/08/21 14:39:58 fros4943 Exp $
|
||||
*/
|
||||
|
||||
package se.sics.chakana.eventpoints;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import se.sics.cooja.Mote;
|
||||
|
||||
/**
|
||||
* A watchpoint watches a memory area, such as a variable, and triggers at changes.
|
||||
*
|
||||
* @author Fredrik Osterlind
|
||||
*/
|
||||
public class Watchpoint implements Eventpoint {
|
||||
private static Logger logger = Logger.getLogger(Watchpoint.class);
|
||||
private int myID = 0;
|
||||
|
||||
private Mote mote;
|
||||
private int address;
|
||||
private int size;
|
||||
byte[] initialMemory;
|
||||
|
||||
private String reason;
|
||||
|
||||
public Watchpoint(Mote mote, int address, int size) {
|
||||
this.mote = mote;
|
||||
this.address = address;
|
||||
this.size = size;
|
||||
|
||||
logger.debug("Fetching initial memory");
|
||||
initialMemory = mote.getMemory().getMemorySegment(address, size);
|
||||
|
||||
// TODO Throw exception if memory area not valid
|
||||
}
|
||||
|
||||
public boolean evaluate() {
|
||||
byte[] currentMemory = mote.getMemory().getMemorySegment(address, size);
|
||||
boolean shouldBreak = !Arrays.equals(initialMemory, currentMemory);
|
||||
|
||||
if (shouldBreak) {
|
||||
reason = "Memory interval " + "0x" + Integer.toHexString(address) + ":" + size + " changed";
|
||||
}
|
||||
return shouldBreak;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Memory change breakpoint: " + "0x" + Integer.toHexString(address) + ":" + size + " @ " + mote;
|
||||
}
|
||||
|
||||
public void setID(int id) {
|
||||
myID = id;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return myID;
|
||||
}
|
||||
}
|
279
tools/chakana/debug.py
Normal file
279
tools/chakana/debug.py
Normal file
|
@ -0,0 +1,279 @@
|
|||
#
|
||||
# Copyright (C) 2003-2007 Swedish Institute of Computer Science.
|
||||
#
|
||||
# Please refer to the file named LICENSE in the same directory as this
|
||||
# file for licensing information.
|
||||
#
|
||||
|
||||
# $Id: debug.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import StringIO
|
||||
|
||||
DebugMin = 0
|
||||
Always = 0
|
||||
Fatal = 1
|
||||
Critical = 2
|
||||
Error = 3
|
||||
Warning = 4
|
||||
Check = 5
|
||||
Information = 6
|
||||
MajorEvent = 7
|
||||
Event = 8
|
||||
MinorEvent = 9
|
||||
Debug = 10
|
||||
Debug2 = 11
|
||||
Debug3 = 12
|
||||
Debug4 = 13
|
||||
Disabled = 14
|
||||
DebugMax = 15
|
||||
|
||||
DefaultDebugLevel = Information
|
||||
|
||||
def levelName(level):
|
||||
for (varName, value) in globals().items():
|
||||
if not varName in ("DebugMin", "DebugMax"):
|
||||
if value == level:
|
||||
return varName
|
||||
return "Unknown"
|
||||
|
||||
class DebugSingleton:
|
||||
def __init__(self, output = sys.stderr, defaultLevel = DefaultDebugLevel,
|
||||
logLevel = Debug4):
|
||||
self.maxBufferLen = 1024 * 1024
|
||||
self.__level = defaultLevel
|
||||
self.__output = output
|
||||
self.__logBuffer = []
|
||||
self.__logBufferSize = 0
|
||||
self.__logLevel = Debug4
|
||||
self.__logFile = None
|
||||
self.__logFileName = None
|
||||
self.__lock = threading.RLock()
|
||||
self._transformers = []
|
||||
|
||||
def level(self):
|
||||
return self.__level
|
||||
|
||||
def setLog(self, fileName):
|
||||
assert(not self.__logBuffer is None)
|
||||
if not fileName is None:
|
||||
self.__logFileName = fileName
|
||||
self.__logFile = open(fileName, "w")
|
||||
self.__logFile.write("".join(self.__logBuffer))
|
||||
self.write(MajorEvent, "Saving debug log in " + fileName + "\n")
|
||||
self.__logBuffer = None
|
||||
|
||||
def logFileName(self):
|
||||
return self.__logFileName
|
||||
|
||||
def logLevel(self):
|
||||
return self.__logLevel
|
||||
|
||||
def cleanLog(self):
|
||||
self.setLog(None)
|
||||
|
||||
def setLogLevel(self, level):
|
||||
self.__logLevel = level
|
||||
self.write(MinorEvent, "Changed debug log verbosity to " +
|
||||
levelName(self.__logLevel) + " (" +
|
||||
str(self.__logLevel) + ")\n")
|
||||
|
||||
def setLevel(self, level):
|
||||
self.__level = level
|
||||
if self.__level < Always:
|
||||
self.__level = Always
|
||||
if self.__level >= Disabled:
|
||||
self.__level = Disabled - 1
|
||||
self.write(MinorEvent, "Changed debug verbosity to " +
|
||||
levelName(self.__level) + " (" + str(self.__level) + ")\n")
|
||||
|
||||
def increaseLevel(self, amount = 1):
|
||||
self.setLevel(self.level() + amount)
|
||||
|
||||
def decreaseLevel(self, amount = 1):
|
||||
self.increaseLevel(- amount)
|
||||
|
||||
def write(self, level, * messages):
|
||||
"""Write messages if current verbosity level >= level. Messages should
|
||||
either be callable functors taking a writer function parameter, or they
|
||||
should be strings or convertible to strings."""
|
||||
|
||||
writers = []
|
||||
if self.level() >= level:
|
||||
writers.append(self.__output.write)
|
||||
if self.logLevel() >= level:
|
||||
writers.append(self.logWrite)
|
||||
if writers != []:
|
||||
self.__lock.acquire()
|
||||
try:
|
||||
for writer in writers:
|
||||
writer(self.transform(level = level,
|
||||
message = self.format(messages)))
|
||||
finally:
|
||||
self.__lock.release()
|
||||
|
||||
def format(self, messages):
|
||||
buf = StringIO.StringIO()
|
||||
for msg in messages:
|
||||
if callable(msg):
|
||||
msg(buf.write)
|
||||
else:
|
||||
buf.write(msg)
|
||||
return buf.getvalue()
|
||||
|
||||
def transform(self, level, message):
|
||||
for transform in self._transformers:
|
||||
message = transform(level = level, message = message)
|
||||
return message
|
||||
|
||||
def writer(self, level):
|
||||
return lambda what: self.write(level, what)
|
||||
|
||||
def logWrite(self, what):
|
||||
if self.__logFile is None:
|
||||
if not self.__logBuffer is None:
|
||||
self.__logBuffer.append(what)
|
||||
self.__logBufferSize += len(what)
|
||||
if self.__logBufferSize > self.maxBufferLen:
|
||||
self.__logBuffer = None
|
||||
self.write(Warning, "Cleaned log buffer since it overflowed " +
|
||||
str(self.maxBufferLen) + " bytes\n")
|
||||
else:
|
||||
self.__logFile.write(what)
|
||||
self.__logFile.flush()
|
||||
|
||||
def pushTransformer(self, transformer):
|
||||
assert(callable(transformer))
|
||||
if not transformer in self._transformers:
|
||||
self._transformers.append(transformer)
|
||||
|
||||
debugStream = DebugSingleton()
|
||||
|
||||
def debug(level, message):
|
||||
debugStream.write(level, message, "\n")
|
||||
|
||||
|
||||
class DebugDumper:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __call__(self, writer):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __add__(self, other):
|
||||
return DebugCombiner(self, other)
|
||||
|
||||
def __radd__(self, other):
|
||||
return DebugCombiner(other, self)
|
||||
|
||||
|
||||
class DebugCombiner:
|
||||
def __init__(self, first, second):
|
||||
self.__first = first
|
||||
self.__second = second
|
||||
|
||||
def __call__(self, writer):
|
||||
for elem in [self.__first, self.__second]:
|
||||
if callable(elem):
|
||||
elem(writer)
|
||||
else:
|
||||
writer(str(elem))
|
||||
|
||||
class DebugFile:
|
||||
def __init__(self):
|
||||
self.__str = ""
|
||||
|
||||
def write(self, msg):
|
||||
self.__str += msg
|
||||
|
||||
def __str__(self):
|
||||
return self.__str
|
||||
|
||||
DebugStderr = DebugFile
|
||||
|
||||
def exceptionDump((type, value, traceback)):
|
||||
"""Print an exception stack trace to DebugStderr(). Pass
|
||||
sys.exc_info() as parameter."""
|
||||
if type is None:
|
||||
return ""
|
||||
oldStderr = sys.stderr
|
||||
debugStderr = DebugStderr()
|
||||
sys.stderr = debugStderr
|
||||
sys.__excepthook__(type, value, traceback)
|
||||
sys.stderr = oldStderr
|
||||
return str(debugStderr)
|
||||
|
||||
def exceptionMessage((type, value, traceback)):
|
||||
import urllib2
|
||||
if isinstance(value, urllib2.HTTPError):
|
||||
return str(value) + ": " + value.filename
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
class CallTracer:
|
||||
def __init__(self, level, prefix = lambda f, e, a: "", traceSystem = 0,
|
||||
traceDebug = 0):
|
||||
self._level = level
|
||||
self._prefix = prefix
|
||||
self._traceSystem = traceSystem
|
||||
self._traceDebug = traceDebug
|
||||
|
||||
def indent(self, frame):
|
||||
stack = inspect.stack()
|
||||
depth = len(stack) - 4
|
||||
assert(depth >= 0)
|
||||
while len(stack) > 0:
|
||||
del stack[0]
|
||||
del stack
|
||||
return " " * depth
|
||||
|
||||
def prefix(self, frame, event, arg):
|
||||
return self._prefix(frame, event, arg) + self.indent(frame)
|
||||
|
||||
def inDebugCode(self, frame):
|
||||
if frame is None:
|
||||
return 0
|
||||
if os.path.basename(frame.f_code.co_filename) == "debug.py":
|
||||
ret = 1
|
||||
else:
|
||||
ret = self.inDebugCode(frame.f_back)
|
||||
del frame
|
||||
return ret
|
||||
|
||||
def __call__(self, frame, event, arg):
|
||||
if frame.f_back is None:
|
||||
callerDir = ""
|
||||
else:
|
||||
callerDir = os.path.dirname(frame.f_back.f_code.co_filename)
|
||||
sourceFile = os.path.basename(frame.f_code.co_filename)
|
||||
funcName = sourceFile + ":" + frame.f_code.co_name + "()"
|
||||
if (not self._traceDebug) and self.inDebugCode(frame):
|
||||
return None
|
||||
if self._traceSystem or \
|
||||
not (callerDir.startswith(sys.prefix + "/lib/python")):
|
||||
if event == "call":
|
||||
debug(self._level, self.prefix(
|
||||
frame = frame, event = event, arg = arg) + "-> " + funcName)
|
||||
elif event == "return":
|
||||
debug(self._level, self.prefix(
|
||||
frame = frame, event = event, arg = arg) + "<- " + funcName)
|
||||
elif event == "exception":
|
||||
debug(self._level, self.prefix(
|
||||
frame = frame, event = event, arg = arg) + "X " + funcName)
|
||||
return self
|
||||
|
||||
def pidPrefix(message = "", ** kwArgs):
|
||||
return str(os.getpid()) + ": " + message
|
||||
|
||||
def threadPrefix(message = "", ** kwArgs):
|
||||
return threading.currentThread().getName() + ": " + message
|
||||
|
||||
def objectAsString(obj):
|
||||
names = [(name, value) for name,value in vars(obj).iteritems()]
|
||||
names.insert(0,("Instace of", obj.__class__.__name__))
|
||||
max_length = len(max([name for (name, value) in names]))
|
||||
return "\n".join([name.rjust(max_length) + ": " + str(value) for (name,value) in names])
|
||||
|
89
tools/chakana/error.py
Normal file
89
tools/chakana/error.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
#
|
||||
# Copyright (C) 2003-2007 Swedish Institute of Computer Science.
|
||||
#
|
||||
# Please refer to the file named LICENSE in the same directory as this
|
||||
# file for licensing information.
|
||||
#
|
||||
|
||||
# $Id: error.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
|
||||
|
||||
import errno
|
||||
import os
|
||||
|
||||
import chakana.debug
|
||||
|
||||
class Error(Exception):
|
||||
def __init__(self, message):
|
||||
Exception.__init__(self, message)
|
||||
|
||||
class CommandFailed(Error):
|
||||
"""Exception thrown when a system command fails. The command string,
|
||||
environment, working directory, output, and exit status are recorded.
|
||||
The exit status is typically the value returned by os.system, but may
|
||||
also be an exception, for example an error.Timeout object."""
|
||||
|
||||
def __init__(self, command, status, output):
|
||||
Error.__init__(self, "Command failed with exit code " + str(status) +
|
||||
": " + command + "\n" + "Output:\n" + output + "\ncwd: " +
|
||||
os.getcwd() + "\n")
|
||||
self._command = command
|
||||
self._status = status
|
||||
self._output = output
|
||||
self._environ = os.environ.copy()
|
||||
self._cwd = os.getcwd()
|
||||
|
||||
def command(self):
|
||||
return self._command
|
||||
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
def output(self):
|
||||
return self._output
|
||||
|
||||
def environ(self):
|
||||
return self._environ
|
||||
|
||||
def cwd(self):
|
||||
return self._cwd
|
||||
|
||||
class SanityCheckFailed(Error):
|
||||
def __init__(self, message):
|
||||
Error.__init__(self, "Sanity check failed: " + message)
|
||||
|
||||
class NoSuchProcess(OSError):
|
||||
def __init__(self):
|
||||
OSError.__init__(self, errno.ESRCH, "No such process")
|
||||
|
||||
class Timeout(Error):
|
||||
def __init__(self, child, timeout):
|
||||
Error.__init__(self, "Timeout (" + str(timeout) + " s) executing " +
|
||||
child.getName())
|
||||
self.child = child
|
||||
self.timeout = timeout
|
||||
|
||||
class Discarded(Error):
|
||||
def __init__(self):
|
||||
Error.__init__(self, "Monitor was discarded")
|
||||
|
||||
class ChildException(Error):
|
||||
def __init__(self, error, exc_info):
|
||||
Error.__init__(self, "Exception in child thread: " + str(error) +
|
||||
":\n" + chakana.debug.exceptionDump(exc_info))
|
||||
self.error = error
|
||||
self.exc_info = exc_info
|
||||
|
||||
class CoojaError(Error):
|
||||
def __init__(self, response):
|
||||
Error.__init__(self, "COOJA error: " +
|
||||
response.documentElement.childNodes[0].nodeValue)
|
||||
|
||||
class CoojaExit(Error):
|
||||
def __init__(self):
|
||||
Error.__init__(self, "COOJA has exited")
|
||||
|
||||
def errnoError(errNum, fileName = None):
|
||||
if isinstance(errNum, str):
|
||||
return errnoError(getattr(errno, errNum), fileName)
|
||||
return OSError(errNum, os.strerror(errNum), fileName)
|
||||
|
175
tools/chakana/event.py
Normal file
175
tools/chakana/event.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
import time
|
||||
import thread
|
||||
|
||||
from debug import *
|
||||
import threads
|
||||
|
||||
class Eventpoint(threads.ManagedThread):
|
||||
|
||||
""" The Eventpoint class and its subclasses represent important events
|
||||
happening in the debugged system, and are the primitives that Chakana
|
||||
debugging scripts are based around. When the user calls waitFor with an
|
||||
Eventpoint list argument, each Eventpoint thread is started. The
|
||||
Eventpoint inserts appropriate eventpoints into COOJA or creates child
|
||||
Eventpoints. An Eventpoint that is hit posts itself on a trigger queue
|
||||
(Queue.Queue), where it is picked up by waitFor. The Eventpoints that
|
||||
have not been triggered are then discarded by the waitFor routine."""
|
||||
|
||||
def __init__(self, shepherd):
|
||||
threads.ManagedThread.__init__(self, shepherd.threadManager())
|
||||
self._shepherd = shepherd
|
||||
self._activated = 0
|
||||
self._discarded = 0
|
||||
self._triggerQueue = None
|
||||
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
self._isWaitingEvent = threading.Event()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
shepherd.threadManager().registerPIDerror()
|
||||
|
||||
def await(self, triggerQueue):
|
||||
assert not self._activated, "Eventpoints can only be used once"
|
||||
self._triggerQueue = triggerQueue
|
||||
self._activated = 1
|
||||
self.activatedHook()
|
||||
self.start()
|
||||
|
||||
def doRun(self):
|
||||
debug(Debug, "Waiting for event: " + repr(self))
|
||||
if not self._discarded:
|
||||
self.doWait()
|
||||
debug(MinorEvent, "Event was triggered: " + repr(self))
|
||||
if not self._discarded:
|
||||
self._triggerQueue.put(self)
|
||||
else:
|
||||
debug(Debug, "Event is no longer waited for: " + repr(self))
|
||||
|
||||
def activatedHook(self):
|
||||
pass
|
||||
|
||||
def doWait(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def discard(self):
|
||||
debug(Debug, "Discarding eventpoint:\n" + repr(self))
|
||||
self._discarded = 1
|
||||
if (not self._activated):
|
||||
self.start()
|
||||
for (waitingEv, event) in self.shepherd()._waitMap.values():
|
||||
if waitingEv == self:
|
||||
event.set()
|
||||
self.join()
|
||||
|
||||
def isDiscarded(self):
|
||||
return self._discarded
|
||||
|
||||
def shepherd(self):
|
||||
return self._shepherd
|
||||
|
||||
def __repr__(self):
|
||||
return "\n ".join([
|
||||
"event.Eventpoint:",
|
||||
"Id: %#x" % ((id(self) + (long(1) << 32)) % (long(1) << 32)),
|
||||
])
|
||||
|
||||
class BasicEventpoint(Eventpoint):
|
||||
"""Eventpoint corresponding to a single COOJA Eventpoint."""
|
||||
|
||||
def __init__(self, shepherd, ** kwArgs):
|
||||
Eventpoint.__init__(self, shepherd, ** kwArgs)
|
||||
|
||||
def activatedHook(self):
|
||||
self.shepherd().registerBasicEventpoint(self)
|
||||
|
||||
def doWait(self):
|
||||
self.shepherd().waitForCoojaEventpoint(self)
|
||||
self.shepherd().unregisterBasicEventpoint(self)
|
||||
|
||||
class Watchpoint(BasicEventpoint):
|
||||
def __init__(self, shepherd, type, mote, variable, ** kwArgs):
|
||||
self._type = type
|
||||
self._mote = mote
|
||||
self._variable = variable
|
||||
BasicEventpoint.__init__(self, shepherd, ** kwArgs)
|
||||
|
||||
def coojaArguments(self):
|
||||
return { "type" : self._type,
|
||||
"mote" : self._mote,
|
||||
"variable" : self._variable }
|
||||
|
||||
def __repr__(self):
|
||||
return "\n ".join([
|
||||
BasicEventpoint.__repr__(self),
|
||||
"event.Watchpoint:",
|
||||
"Type: " + repr(self._type),
|
||||
"Mote: " + repr(self._mote),
|
||||
"Variable: " + repr(self._variable)
|
||||
])
|
||||
|
||||
class TimeEventpoint(BasicEventpoint):
|
||||
def __init__(self, shepherd, time, ** kwArgs):
|
||||
self._time = time
|
||||
BasicEventpoint.__init__(self, shepherd, ** kwArgs)
|
||||
|
||||
def coojaArguments(self):
|
||||
return { "type" : "time",
|
||||
"time" : self._time }
|
||||
|
||||
def __repr__(self):
|
||||
return "\n ".join([
|
||||
BasicEventpoint.__repr__(self),
|
||||
"event.TimeEventpoint:",
|
||||
"Time: " + repr(self._time)
|
||||
])
|
||||
|
||||
|
||||
class RadioMessageCompletedEventpoint(BasicEventpoint):
|
||||
def __init__(self, shepherd, count = 1, ** kwArgs):
|
||||
self._count = count
|
||||
BasicEventpoint.__init__(self, shepherd, ** kwArgs)
|
||||
|
||||
def coojaArguments(self):
|
||||
return { "type" : "radiomedium",
|
||||
"triggeron" : "completed",
|
||||
"count" : str(self._count) }
|
||||
|
||||
def __repr__(self):
|
||||
return "\n ".join([
|
||||
BasicEventpoint.__repr__(self),
|
||||
"event.RadioMessageCompletedEventpoint",
|
||||
])
|
||||
|
||||
class MonitorEventpoint(Eventpoint):
|
||||
"""Eventpoint that triggers after a monitor has finished."""
|
||||
|
||||
def __init__(self, monitor):
|
||||
self._monitor = monitor
|
||||
Eventpoint.__init__(self, monitor.shepherd())
|
||||
|
||||
def doWait(self):
|
||||
debug(Debug, "MonitorEventpoint starting " + repr(self))
|
||||
self._monitor.startMonitor()
|
||||
self._monitor.waitMonitor(self)
|
||||
|
||||
def discard(self):
|
||||
self._discarded = 1
|
||||
self._monitor.discard()
|
||||
if (not self._activated):
|
||||
self.start()
|
||||
self._monitor.startMonitor()
|
||||
self._monitor.waitMonitor(self)
|
||||
else:
|
||||
self._monitor.waitMonitor(self)
|
||||
self.join()
|
||||
|
||||
def __repr__(self):
|
||||
return "\n ".join([
|
||||
Eventpoint.__repr__(self),
|
||||
"event.MonitorEventpoint:",
|
||||
"Monitor: " + repr(self._monitor)
|
||||
])
|
||||
|
126
tools/chakana/harness.py
Normal file
126
tools/chakana/harness.py
Normal file
|
@ -0,0 +1,126 @@
|
|||
import errno
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
import chakana.command
|
||||
import chakana.error
|
||||
import chakana.shepherd
|
||||
import chakana.threads
|
||||
import chakana.utils
|
||||
|
||||
from chakana.debug import *
|
||||
|
||||
class Harness:
|
||||
def __init__(self, chakanaRoot, coojaTimeout, doCompile="True", withGUI="False"):
|
||||
self._chakanaRoot = os.path.abspath(chakanaRoot)
|
||||
self._coojaTimeout = coojaTimeout
|
||||
self._threadManager = chakana.threads.ThreadManager()
|
||||
self._doCompile = doCompile
|
||||
self._withGUI = withGUI
|
||||
debug(MajorEvent, "Chakana harness created")
|
||||
|
||||
def start(self):
|
||||
port = self.startCooja()
|
||||
self._shepherd = chakana.shepherd.Shepherd(self._threadManager, port)
|
||||
|
||||
def startCooja(self):
|
||||
# COMPILE COOJA AND PLUGIN
|
||||
if self._doCompile == "True":
|
||||
for target in ("cooja", "plugin"):
|
||||
buildCommand = 'cd ' + chakana.command.quote(self._chakanaRoot) + ' && ant compile_' + target
|
||||
debug(MajorEvent, "Building " + target)
|
||||
debug(Event, buildCommand)
|
||||
output = chakana.command.output(buildCommand)
|
||||
debug(MinorEvent, output)
|
||||
|
||||
coojaOutputFile = os.path.join(self._chakanaRoot, "build/cooja.out")
|
||||
# START COOJA
|
||||
|
||||
if os.path.isfile(coojaOutputFile):
|
||||
os.remove(coojaOutputFile)
|
||||
coojaThread = CoojaThread(self._threadManager, self._chakanaRoot,
|
||||
coojaOutputFile, self._coojaTimeout, withGUI=self._withGUI)
|
||||
coojaThread.start()
|
||||
return coojaThread.port()
|
||||
|
||||
def shepherd(self):
|
||||
return self._shepherd
|
||||
|
||||
def quit(self):
|
||||
self._shepherd.quit()
|
||||
|
||||
def killAllProcesses(self):
|
||||
self._threadManager.killAll()
|
||||
|
||||
def waitForAllThreads(self, timeout):
|
||||
self._threadManager.waitAll(timeout)
|
||||
|
||||
class CoojaThread(chakana.threads.ManagedThread):
|
||||
def __init__(self, threadManager, chakanaRoot, outputFile,
|
||||
timeout = 3600, name = "COOJA", withGUI="False", ** kwArgs):
|
||||
chakana.threads.ManagedThread.__init__(
|
||||
self, threadManager, name = name, **kwArgs)
|
||||
self._chakanaRoot = chakanaRoot
|
||||
self._outputFile = outputFile
|
||||
self._timeout = timeout
|
||||
self._port = None
|
||||
self._withGUI = withGUI
|
||||
|
||||
def doRun(self):
|
||||
debug(MajorEvent, "Starting COOJA")
|
||||
buildDir = os.path.dirname(self._outputFile)
|
||||
chakana.utils.makeDirsSafe(buildDir)
|
||||
contikiRoot = os.path.join(self._chakanaRoot, '../..')
|
||||
contikiRoot = contikiRoot.replace('/cygdrive/c', 'c:')
|
||||
if self._withGUI == "True":
|
||||
coojaCommand = '( cd ' + chakana.command.quote(buildDir) + ' && java -jar ' + \
|
||||
chakana.command.quote(os.path.join(contikiRoot, 'tools/cooja/dist/cooja.jar')) + ' ' + \
|
||||
'-external_tools_config=../cooja.chakana.properties ' + \
|
||||
'-contiki=' + chakana.command.quote(contikiRoot) + ' < /dev/null > ' + \
|
||||
os.path.basename(self._outputFile) + ' 2>&1 )'
|
||||
else:
|
||||
coojaCommand = '( cd ' + chakana.command.quote(buildDir) + ' && java -jar ' + \
|
||||
chakana.command.quote(os.path.join(contikiRoot, 'tools/cooja/dist/cooja.jar')) + ' ' + \
|
||||
'-nogui ' + \
|
||||
'-external_tools_config=../cooja.chakana.properties ' + \
|
||||
'-contiki=' + chakana.command.quote(contikiRoot) + ' < /dev/null > ' + \
|
||||
os.path.basename(self._outputFile) + ' 2>&1 )'
|
||||
|
||||
debug(Event, coojaCommand)
|
||||
os.system(coojaCommand)
|
||||
debug(MajorEvent, "COOJA has finished")
|
||||
|
||||
def port(self):
|
||||
if self._port is None:
|
||||
laps = 0
|
||||
debug(Event, "Waiting for COOJA to open server socket")
|
||||
debug(Debug, "Reading: " + self._outputFile)
|
||||
while 1:
|
||||
if self._timeout > 0 and laps > self._timeout:
|
||||
raise chakana.error.Timeout(self, self._timeout)
|
||||
logContents = ""
|
||||
try:
|
||||
logContents = chakana.utils.readFile(self._outputFile)
|
||||
except IOError, err:
|
||||
if err.errno != errno.ENOENT:
|
||||
raise
|
||||
match = re.search(r"Chakana server listening on port (\d+).",
|
||||
logContents)
|
||||
debug(Debug, "Log contents: " + logContents)
|
||||
if match:
|
||||
self._port = int(match.group(1))
|
||||
debug(Event, "COOJA is now listening on port " + str(self._port))
|
||||
break
|
||||
else:
|
||||
debug(Debug, "Waiting for COOJA to start")
|
||||
time.sleep(1)
|
||||
laps += 1
|
||||
|
||||
match = re.search(r"Unable to access jarfile",
|
||||
logContents)
|
||||
if match:
|
||||
raise RuntimeError("Could not locate COOJA JAR: " + logContents)
|
||||
|
||||
return self._port
|
||||
|
268
tools/chakana/linux.py
Normal file
268
tools/chakana/linux.py
Normal file
|
@ -0,0 +1,268 @@
|
|||
#
|
||||
# Copyright (C) 2004-2007 Swedish Institute of Computer Science.
|
||||
#
|
||||
# Please refer to the file named LICENSE in the same directory as this
|
||||
# file for licensing information.
|
||||
#
|
||||
|
||||
|
||||
# $Id: linux.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
|
||||
|
||||
import errno
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import string
|
||||
import time
|
||||
|
||||
import chakana.command
|
||||
import chakana.error
|
||||
import chakana.utils
|
||||
|
||||
from debug import *
|
||||
|
||||
def numProcessors(physical = 0, cpuInfo = None):
|
||||
return Host().CpuInfo(cpuInfo).count(physical = physical)
|
||||
|
||||
class CpuInfo:
|
||||
"Parser for /proc/cpuinfo."
|
||||
def __init__(self, cpuInfo = None):
|
||||
if cpuInfo is None:
|
||||
cpuInfo = chakana.utils.readFile("/proc/cpuinfo")
|
||||
self.__values = [map(string.strip, l.split(":"))
|
||||
for l in cpuInfo.splitlines()]
|
||||
|
||||
def values(self):
|
||||
return self.__values
|
||||
|
||||
def find(self, name):
|
||||
debug(Debug3, "Looking for " + name + " in cpuinfo:")
|
||||
ret = [val[1] for val in self.values() if val[0] == name]
|
||||
debug(Debug3, repr(ret))
|
||||
return ret
|
||||
|
||||
class IfconfigInfo:
|
||||
"Parser for ifconfig output."
|
||||
def __init__(self, ifconfigOutput = None):
|
||||
if ifconfigOutput is None:
|
||||
self._ifconfigOutput = chakana.command.output("/sbin/ifconfig")
|
||||
else:
|
||||
self._ifconfigOutput = ifconfigOutput
|
||||
|
||||
def interfaces(self):
|
||||
return map(InterfaceInfo,
|
||||
re.compile("^\\w+", re.M).findall(self._ifconfigOutput))
|
||||
|
||||
class InterfaceInfo:
|
||||
"Parser for ifconfig output for a single interface."
|
||||
def __init__(self, interface = None, ifconfigOutput = None):
|
||||
if interface is None:
|
||||
self._interface = chakana.command.output("/sbin/ifconfig").split()[0]
|
||||
else:
|
||||
self._interface = interface
|
||||
if ifconfigOutput is None:
|
||||
self._ifconfigOutput = chakana.command.output("/sbin/ifconfig " +
|
||||
self._interface)
|
||||
else:
|
||||
self._ifconfigOutput = ifconfigOutput
|
||||
|
||||
def name(self):
|
||||
return self._ifconfigOutput.split()[0]
|
||||
|
||||
def address(self):
|
||||
match = re.search("inet addr:((\d{1,3}\.){3}\d{1,3})",
|
||||
self._ifconfigOutput)
|
||||
if match:
|
||||
return match.group(1)
|
||||
else:
|
||||
return None
|
||||
|
||||
__str__ = name
|
||||
|
||||
class MemInfo(dict):
|
||||
"Parser for /proc/meminfo"
|
||||
def __init__(self, memInfo = None):
|
||||
if memInfo is None:
|
||||
memInfo = chakana.utils.readFile("/proc/meminfo")
|
||||
for line in memInfo.splitlines():
|
||||
if len(line.split(":")) == 2:
|
||||
key, value = line.split(":")
|
||||
if len(value.strip().split()) <= 2:
|
||||
if len(value.strip().split()) == 2:
|
||||
unit = {"KB" : 1024, "MB" : 1024 * 1024,
|
||||
"GB" : 1024 * 1024 * 1024 }[value.split()[-1].upper()]
|
||||
else:
|
||||
unit = 1
|
||||
self[key] = long(value.strip().split()[0]) * unit
|
||||
|
||||
class UnameInfo:
|
||||
"Parser for output from uname -a"
|
||||
def __init__(self, unameInfo = None):
|
||||
if unameInfo is None:
|
||||
unameInfo = chakana.command.output("uname -a")
|
||||
self._unameInfo = unameInfo.split()
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self._unameInfo[i]
|
||||
|
||||
def __len__(self):
|
||||
return len(self._unameInfo)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
ret = self[self.indexMap()[attr]]
|
||||
if (attr == "architecture") and \
|
||||
(re.match(r"(.*intel|i[0-9]86)", ret, re.I)):
|
||||
# Work around bogus uname output from e.g. Gentoo
|
||||
return "i386"
|
||||
else:
|
||||
return ret
|
||||
|
||||
def smp(self):
|
||||
return "SMP" in self._unameInfo
|
||||
|
||||
def indexMap(self):
|
||||
return {
|
||||
"os" : 0,
|
||||
"host" : 1,
|
||||
"version" : 2,
|
||||
"architecture" : -2,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return repr(dict([(attr, getattr(self, attr))
|
||||
for attr in self.indexMap()]))
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._unameInfo)
|
||||
|
||||
class X86:
|
||||
class CpuInfo(CpuInfo):
|
||||
def count(self, physical = 0):
|
||||
logical = len(self.find("processor"))
|
||||
if physical:
|
||||
siblings = self.find("siblings")
|
||||
if len(siblings) > 0:
|
||||
return logical / int(siblings[0])
|
||||
return logical
|
||||
|
||||
def type(self, num = 0):
|
||||
return re.match(r"Intel(\(R\))? (.*) processor [\d]+MHz",
|
||||
self.find("model name")[num]).group(2)
|
||||
|
||||
class X86_64(X86):
|
||||
pass
|
||||
|
||||
class Sparc:
|
||||
class CpuInfo(CpuInfo):
|
||||
def count(self):
|
||||
return int(self.find("ncpus active")[0])
|
||||
|
||||
def type(self, num = 0):
|
||||
return " ".join(self.find("cpu")[num].split()[1:3])
|
||||
|
||||
class UnameInfo(UnameInfo):
|
||||
pass
|
||||
|
||||
def freeSpace(path):
|
||||
"Return available disk space on device corresponding to path."
|
||||
# If the device name is long, df may split the relevant line, so
|
||||
# count lines and words from end.
|
||||
return long(
|
||||
chakana.command.output("df -k " + path).splitlines()[-1].split()[-3]) * 1024
|
||||
|
||||
def macAddresses():
|
||||
"Return list of mac addresses for all network interfaces."
|
||||
return re.findall('..:..:..:..:..:..', os.popen(
|
||||
'/sbin/ifconfig -a | grep HWaddr', 'r').read().lower())
|
||||
|
||||
class Process:
|
||||
"""Representation of a running process."""
|
||||
|
||||
def __init__(self, pid):
|
||||
self._pid = pid
|
||||
|
||||
def pid(self):
|
||||
return self._pid
|
||||
|
||||
def attribute(self, name):
|
||||
try:
|
||||
lines = chakana.utils.readFile("/proc/" + str(self._pid) +
|
||||
"/status").splitlines()
|
||||
for line in lines:
|
||||
(key, value) = line.split(":", 1)
|
||||
if key.strip().lower() == name.lower():
|
||||
return value.strip()
|
||||
except EnvironmentError, err:
|
||||
if err.errno == errno.ENOENT:
|
||||
raise chakana.error.errnoError(errno.ESRCH)
|
||||
raise
|
||||
|
||||
def name(self):
|
||||
return self.attribute("name")
|
||||
|
||||
def parent(self):
|
||||
return Process(int(self.attribute("ppid")))
|
||||
|
||||
def children(self):
|
||||
ret = []
|
||||
for proc in self.all():
|
||||
try:
|
||||
if proc.parent().pid() == self.pid():
|
||||
ret.append(proc)
|
||||
except EnvironmentError, err:
|
||||
if err.errno != errno.ESRCH:
|
||||
raise
|
||||
return ret
|
||||
|
||||
def reallyKill(self, graceTime = 1, sigKillTime = 3, debugLevel = Event):
|
||||
"""Really kill process after waiting for graceTime. Send SIGKILL after
|
||||
sigKillTime."""
|
||||
try:
|
||||
debug(debugLevel, "Killing process " + str(self.pid()) +
|
||||
" (" + str(self.name()) + ")")
|
||||
os.kill(self.pid(), 0)
|
||||
time.sleep(graceTime)
|
||||
debug(debugLevel + 1, "kill -TERM " + str(self.pid()))
|
||||
os.kill(self.pid(), signal.SIGTERM)
|
||||
for tick in range(sigKillTime):
|
||||
os.kill(self.pid(), 0)
|
||||
debug(debugLevel + 1, "kill -KILL " + str(self.pid()))
|
||||
os.kill(self.pid(), signal.SIGKILL)
|
||||
time.sleep(graceTime)
|
||||
if self.attribute("state")[0] != "Z":
|
||||
debug(debugLevel, "Process refused to die: " + str(self.pid()) +
|
||||
" (" + self.name() + ")")
|
||||
except EnvironmentError, err:
|
||||
if err.errno != errno.ESRCH:
|
||||
raise
|
||||
debug(debugLevel + 2, "Pid " + str(self.pid()) + " no longer exists")
|
||||
|
||||
def killAllBelow(self, debugLevel = Event, ** kwArgs):
|
||||
"""Kill all processes below (but not including) a process. Start from
|
||||
bottom of tree."""
|
||||
debug(debugLevel, "Killing all processes below " + str(self.pid()) +
|
||||
" (" + self.name() + ")")
|
||||
children = self.children()
|
||||
debug(debugLevel + 2, "Pid " + str(self.pid()) + " has " +
|
||||
str(len(children)) + " children")
|
||||
for child in children:
|
||||
child.killAllBelow(debugLevel = debugLevel, ** kwArgs)
|
||||
child.reallyKill(debugLevel = debugLevel, ** kwArgs)
|
||||
|
||||
def all(cls):
|
||||
ret = []
|
||||
for statusFile in glob.glob("/proc/*/status"):
|
||||
try:
|
||||
ret.append(cls(int(os.path.basename(os.path.dirname(statusFile)))))
|
||||
except ValueError:
|
||||
continue
|
||||
return ret
|
||||
all = classmethod(all)
|
||||
|
||||
def Host():
|
||||
architecture = chakana.command.output("uname -m").strip()
|
||||
if re.match("i[0-9]86", architecture):
|
||||
return X86
|
||||
elif architecture == "x86_64":
|
||||
return X86_64
|
184
tools/chakana/monitor.py
Normal file
184
tools/chakana/monitor.py
Normal file
|
@ -0,0 +1,184 @@
|
|||
import Queue
|
||||
import thread
|
||||
|
||||
import chakana.event
|
||||
import chakana.threads
|
||||
from chakana.debug import *
|
||||
|
||||
class Monitor(chakana.threads.ManagedThread):
|
||||
"""Base class for causal path monitors. Pass the threadManager to the
|
||||
constructor. Override the doRun method or pass a routine argument. In
|
||||
the latter case, the routine will be called with the Monitor object as
|
||||
argument plus any extra arguments and keyword arguments specified."""
|
||||
|
||||
def __init__(self, shepherd, routine = None, * args, ** kwArgs):
|
||||
chakana.threads.ManagedThread.__init__(self, shepherd.threadManager())
|
||||
self._shepherd = shepherd
|
||||
self._routine = routine
|
||||
self._args = args
|
||||
self._kwArgs = kwArgs
|
||||
self._ready = 0
|
||||
self._discarded = 0
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
self._isWaitingMonitor = threading.Event()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
shepherd.threadManager().registerPIDerror()
|
||||
debug(Debug, "Monitor created")
|
||||
self._shepherd.registerMonitor(self)
|
||||
|
||||
def startMonitor(self):
|
||||
self.start()
|
||||
debug(Debug, "New monitor thread started: " + repr(self))
|
||||
|
||||
def discard(self):
|
||||
self._discarded = 1
|
||||
self._ready = 0
|
||||
debug(Debug, "Discarding monitor:\n" + repr(self))
|
||||
|
||||
# Wake up thread with empty results
|
||||
if hasattr(self, "_resultQueue"):
|
||||
self._resultQueue.put(None)
|
||||
|
||||
def waitMonitor(self, parentEventpoint = None):
|
||||
debug(Debug, "Waiting for monitor to finish: " + repr(self))
|
||||
|
||||
# While monitor is registered, check for continuations
|
||||
while self in self.shepherd()._monitors:
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
self._isWaitingMonitor.wait()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.shepherd().threadManager().registerPIDerror()
|
||||
|
||||
self._isWaitingMonitor.clear()
|
||||
self._ready = 1
|
||||
|
||||
# If all monitors ready, poll continuation
|
||||
allMonitorsReady = 1
|
||||
for monitor in self.shepherd()._monitors:
|
||||
if not monitor._ready:
|
||||
allMonitorsReady = 0
|
||||
if allMonitorsReady:
|
||||
if self in self.shepherd()._monitors:
|
||||
self.shepherd().pollContinuation()
|
||||
else:
|
||||
# A monitor may be waiting for us, tell parent we are ready
|
||||
if parentEventpoint is not None:
|
||||
parentEventpoint._isWaitingEvent.set()
|
||||
self.join()
|
||||
|
||||
def doRun(self):
|
||||
debug(Debug, "Running monitor")
|
||||
try:
|
||||
if (not self._discarded):
|
||||
self.runMonitor()
|
||||
except chakana.error.Discarded:
|
||||
pass
|
||||
self.shepherd().unregisterMonitor(self)
|
||||
self._isWaitingMonitor.set()
|
||||
debug(Debug, "Monitor exiting")
|
||||
|
||||
def runMonitor(self):
|
||||
self._routine(self, * self._args, ** self._kwArgs)
|
||||
|
||||
def waitFor(self, eventList):
|
||||
"""Waits until one of the Events given in 'eventList' have triggered and
|
||||
returns that Event."""
|
||||
if isinstance(eventList, chakana.event.Eventpoint):
|
||||
eventList = [eventList]
|
||||
debug(MinorEvent, "Waiting for events to trigger. Event list: " +
|
||||
str(eventList))
|
||||
assert eventList != []
|
||||
|
||||
# Clean up eventpoints and abort monitor if discarded
|
||||
if self._discarded:
|
||||
for ev in eventList:
|
||||
ev.discard()
|
||||
raise chakana.error.Discarded()
|
||||
|
||||
self.shepherd().registerWaitingMonitor(self)
|
||||
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
self._resultQueue = Queue.Queue()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.shepherd().threadManager().registerPIDerror()
|
||||
|
||||
for ev in eventList:
|
||||
# Start thread and wait until eventpoint is waiting
|
||||
ev.await(self._resultQueue)
|
||||
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
ev._isWaitingEvent.wait()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.shepherd().threadManager().registerPIDerror()
|
||||
|
||||
ev._isWaitingEvent.clear()
|
||||
|
||||
debug(Event, "Waiting for events: " + repr(eventList))
|
||||
|
||||
assert(not self._discarded)
|
||||
|
||||
self._isWaitingMonitor.set()
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
triggeredEvent = self._resultQueue.get()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.shepherd().threadManager().registerPIDerror()
|
||||
|
||||
self._ready = 0
|
||||
self.shepherd().unregisterWaitingMonitor(self)
|
||||
debug(Event, "Eventpoint was triggered: " + repr(eventList))
|
||||
for ev in eventList:
|
||||
if not (ev is triggeredEvent):
|
||||
ev.discard()
|
||||
|
||||
return triggeredEvent
|
||||
|
||||
def shepherd(self):
|
||||
return self._shepherd
|
||||
|
||||
def readVariable(self, ** kwArgs):
|
||||
return self.shepherd().readVariable(** kwArgs)
|
||||
|
||||
def readInt(self, ** kwArgs):
|
||||
return self.shepherd().readInt(** kwArgs)
|
||||
|
||||
def readMemory(self, ** kwArgs):
|
||||
return self.shepherd().readMemory(** kwArgs)
|
||||
|
||||
class TimeoutWrapperMonitor(Monitor):
|
||||
def __init__(self, shepherd, routine, eventpoint = None, timeout = 1000):
|
||||
chakana.monitor.Monitor.__init__(self, shepherd)
|
||||
self._eventpoint = eventpoint
|
||||
self._timeout = timeout
|
||||
self._triggeredEventpoint = None
|
||||
|
||||
def runMonitor(self):
|
||||
timeEventpoint = chakana.event.TimeEventpoint(self.shepherd(), self._timeout)
|
||||
self._triggeredEventpoint = self.waitFor([timeEventpoint, self._eventpoint])
|
||||
|
||||
def getTriggeredEventpoint(self):
|
||||
return self._triggeredEventpoint
|
||||
|
||||
def timedOut(self):
|
||||
return self.getTriggeredEventpoint() != self._eventpoint
|
||||
|
||||
|
344
tools/chakana/shepherd.py
Normal file
344
tools/chakana/shepherd.py
Normal file
|
@ -0,0 +1,344 @@
|
|||
import re
|
||||
import socket
|
||||
import threads
|
||||
import xml.dom.minidom
|
||||
import thread
|
||||
|
||||
import chakana.error
|
||||
import chakana.event
|
||||
import chakana.monitor
|
||||
import chakana.threads
|
||||
import chakana.utils
|
||||
from chakana.debug import *
|
||||
|
||||
class Shepherd:
|
||||
"""The shepherd is a singleton class that manages the connection to COOJA
|
||||
and keeps track of user monitor threads. The user initialises a
|
||||
debugging scenario by creating a Shepherd object, and then calls the
|
||||
newMonitor method to create Monitor objects, one for each causal
|
||||
path through the system that he wishes to monitor.
|
||||
|
||||
The monitor threads automatically register with the shepherd. The user
|
||||
can use the debugger instances to probe variables or create eventpoints.
|
||||
When the monitor thread routine decides that is ready for simulation to
|
||||
proceed, it calls the waitFor method to block until one of the
|
||||
eventpoints occur.
|
||||
|
||||
When a Monitor object enters waitFor, it informs shepherd about the
|
||||
eventpoints that the user is waiting for, and waits for the eventpoints
|
||||
to happen. When all threads enter waiting state, the shepherd asks COOJA
|
||||
to resume simulation. When an eventpoint is hit, the shepherd wakes the
|
||||
Monitor that is waiting for that eventpoint.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, threadManager, port, coojaHost = "localhost"):
|
||||
debug(MajorEvent, "Creating shepherd")
|
||||
self._pollConnLock = threading.Lock()
|
||||
self._threadManager = threadManager
|
||||
self._port = port
|
||||
self._monitors = []
|
||||
self._waitingMonitors = []
|
||||
# Only basic eventpoints in these lists
|
||||
self._eventpoints = []
|
||||
self._waitingEventpoints = []
|
||||
self._coojaContinueEvent = threading.Event()
|
||||
self._waitMap = {}
|
||||
self._coojaHost = coojaHost
|
||||
self._coojaConnection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
debug(MajorEvent, "Connecting to COOJA at " + coojaHost + ":" +
|
||||
str(self._port))
|
||||
self._connectionLock = threading.Lock()
|
||||
self._coojaConnection.connect((self._coojaHost, self._port))
|
||||
self._coojaStream = self._coojaConnection.makefile()
|
||||
self._coojaConnection.close()
|
||||
del self._coojaConnection
|
||||
self._connectionLock.acquire()
|
||||
hello = self.readFromCooja()
|
||||
debug(Debug, hello.toprettyxml())
|
||||
self._connectionLock.release()
|
||||
self._shepherdThread = threads.ManagedThread(
|
||||
self._threadManager, target = self._shepherdMainLoop)
|
||||
self._shepherdThread.start()
|
||||
debug(Debug, "Shepherd created:\n" + repr(self))
|
||||
|
||||
def _shepherdMainLoop(self):
|
||||
debug(Event, "Starting shepherd main loop")
|
||||
while 1:
|
||||
debug(Debug, "Waiting for eventpoints to enter wait state")
|
||||
debug(Debug2, repr(self))
|
||||
self._coojaContinueEvent.wait()
|
||||
self._coojaContinueEvent.clear()
|
||||
if self._coojaStream.closed:
|
||||
debug(Event, "Terminating shepherd main loop")
|
||||
return
|
||||
debug(Debug, "Resuming COOJA")
|
||||
assert(len(self._eventpoints) == len(self._waitingEventpoints))
|
||||
response = self.runCommand("CONTROL_SIM", { "RESUME" : None })
|
||||
id = int(response.documentElement.childNodes[0].nodeValue)
|
||||
debug(MinorEvent, "Eventpoint " + str(id) + " was triggered")
|
||||
|
||||
# Wake up triggered eventpoint
|
||||
self._waitMap[id][1].set()
|
||||
|
||||
# Allow new main loop startups
|
||||
self._pollConnLock.release()
|
||||
|
||||
|
||||
def quit(self):
|
||||
"""Send a termination request to the debugger broker, and discard all
|
||||
debuggers."""
|
||||
|
||||
debug(Information, "Terminating the shepherd session")
|
||||
self.runCommand("EXIT_COOJA")
|
||||
self._connectionLock.acquire()
|
||||
self._coojaStream.close()
|
||||
self._connectionLock.release()
|
||||
self._coojaContinueEvent.set()
|
||||
debug(Information, "Number of PID errors reported: " + str(self.threadManager().nrPIDerrors()))
|
||||
|
||||
for (eventpoint, event) in self._waitMap.values():
|
||||
debug(Debug2, "Awakening eventpoint at exit: " + repr(eventpoint))
|
||||
event.set()
|
||||
|
||||
def loadConfiguration(self, fileName):
|
||||
return self.runCommand(
|
||||
"CREATE_SIM", xmlContent = chakana.utils.readFile(fileName))
|
||||
|
||||
def loadConfigurationXML(self, xmlContent):
|
||||
return self.runCommand(
|
||||
"CREATE_SIM", xmlContent = xmlContent)
|
||||
|
||||
def setConfigurationXML(self, xmlContent):
|
||||
return self.runCommand(
|
||||
"CONF_SIM", xmlContent = xmlContent)
|
||||
|
||||
def setPluginsXML(self, xmlContent):
|
||||
return self.runCommand(
|
||||
"CONF_PLUGINS", xmlContent = xmlContent)
|
||||
|
||||
def newMonitor(self, routine, * args, ** kwArgs):
|
||||
return self.newCustomMonitor(
|
||||
chakana.monitor.Monitor, routine, * args, ** kwArgs)
|
||||
|
||||
def newCustomMonitor(self, Type, routine = None, * args, ** kwArgs):
|
||||
"""Start a new monitor thread running routine, with * args and ** kwArgs
|
||||
as arguments."""
|
||||
debug(Debug, "Creating new monitor of type " + Type.__name__)
|
||||
debug(Debug3, "Routine: " + repr(routine) + ", arguments: " + repr(args) +
|
||||
", keyword arguments: " + repr(kwArgs))
|
||||
thread = Type(self, routine, * args, ** kwArgs)
|
||||
debug(MinorEvent, "New monitor thread created: " + repr(thread))
|
||||
return thread
|
||||
|
||||
def newTimeoutMonitor(self, eventpoint, timeout, routine = None):
|
||||
return self.newCustomMonitor(
|
||||
chakana.monitor.TimeoutWrapperMonitor, routine, eventpoint = eventpoint, timeout = timeout)
|
||||
|
||||
def threadManager(self):
|
||||
return self._threadManager
|
||||
|
||||
def registerMonitor(self, monitor):
|
||||
debug(Debug, "Registering monitor: " + repr(monitor))
|
||||
assert(isinstance(monitor, chakana.monitor.Monitor))
|
||||
assert(not (monitor in self._monitors))
|
||||
assert(not (monitor in self._waitingMonitors))
|
||||
self._monitors.append(monitor)
|
||||
self._monitors.sort()
|
||||
debug(Debug2, repr(self))
|
||||
|
||||
def unregisterMonitor(self, monitor):
|
||||
debug(Debug, "Unregistering monitor: " + repr(monitor))
|
||||
assert(isinstance(monitor, chakana.monitor.Monitor))
|
||||
assert(monitor in self._monitors)
|
||||
assert(not (monitor in self._waitingMonitors))
|
||||
self._monitors.remove(monitor)
|
||||
|
||||
def registerWaitingMonitor(self, monitor):
|
||||
debug(Debug, "Registering waiting monitor: " + repr(monitor))
|
||||
assert(isinstance(monitor, chakana.monitor.Monitor))
|
||||
assert(monitor in self._monitors)
|
||||
assert(not (monitor in self._waitingMonitors))
|
||||
self._waitingMonitors.append(monitor)
|
||||
self._waitingMonitors.sort()
|
||||
debug(Debug2, repr(self))
|
||||
|
||||
def unregisterWaitingMonitor(self, monitor):
|
||||
debug(Debug, "Unregistering waiting monitor: " + repr(monitor))
|
||||
assert(isinstance(monitor, chakana.monitor.Monitor))
|
||||
assert(monitor in self._monitors)
|
||||
assert(monitor in self._waitingMonitors)
|
||||
self._waitingMonitors.remove(monitor)
|
||||
debug(Debug2, repr(self))
|
||||
|
||||
def registerBasicEventpoint(self, eventpoint):
|
||||
debug(Debug2, "Registering basic eventpoint: " + repr(eventpoint))
|
||||
assert(isinstance(eventpoint, chakana.event.BasicEventpoint))
|
||||
assert(not (eventpoint in self._eventpoints))
|
||||
self._eventpoints.append(eventpoint)
|
||||
self._eventpoints.sort()
|
||||
debug(Debug2, repr(self))
|
||||
|
||||
def unregisterBasicEventpoint(self, eventpoint):
|
||||
debug(Debug2, "Unregistering basic eventpoint: " + repr(eventpoint))
|
||||
assert(not (eventpoint in self._waitingEventpoints))
|
||||
assert(eventpoint in self._eventpoints)
|
||||
self._eventpoints.remove(eventpoint)
|
||||
debug(Debug2, repr(self))
|
||||
|
||||
def _registerWaitingEventpoint(self, eventpoint):
|
||||
debug(Debug2, "Registering waiting eventpoint: " + repr(eventpoint))
|
||||
assert(not (eventpoint in self._waitingEventpoints))
|
||||
assert(eventpoint in self._eventpoints)
|
||||
self._waitingEventpoints.append(eventpoint)
|
||||
self._waitingEventpoints.sort()
|
||||
|
||||
def _unregisterWaitingEventpoint(self, eventpoint):
|
||||
debug(Debug2, "Unregistering waiting eventpoint: " + repr(eventpoint))
|
||||
assert(eventpoint in self._waitingEventpoints)
|
||||
assert(eventpoint in self._eventpoints)
|
||||
self._waitingEventpoints.remove(eventpoint)
|
||||
debug(Debug2, repr(self))
|
||||
|
||||
def pollContinuation(self):
|
||||
self._pollConnLock.acquire()
|
||||
if len(self._waitingMonitors) != len(self._monitors):
|
||||
self._pollConnLock.release()
|
||||
return
|
||||
for monitor in self._monitors:
|
||||
if not monitor._ready:
|
||||
self._pollConnLock.release()
|
||||
return
|
||||
|
||||
for monitor in self._monitors:
|
||||
assert(not monitor._discarded)
|
||||
|
||||
for eventpoint in self._eventpoints:
|
||||
assert(not eventpoint._discarded)
|
||||
|
||||
assert(self._waitingMonitors == self._monitors)
|
||||
assert(self._waitingEventpoints == self._eventpoints)
|
||||
debug(MinorEvent, "All monitors are waiting, activating COOJA")
|
||||
assert(not self._coojaContinueEvent.isSet())
|
||||
self._coojaContinueEvent.set()
|
||||
|
||||
def waitForCoojaEventpoint(self, eventpoint):
|
||||
response = self.runCommand("ADD_EVENTPOINT", eventpoint.coojaArguments())
|
||||
id = int(response.documentElement.childNodes[0].nodeValue)
|
||||
debug(Debug, "Waiting for COOJA eventpoint " + str(id))
|
||||
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
self._waitMap[id] = (eventpoint, threading.Event())
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.threadManager().registerPIDerror()
|
||||
|
||||
self._registerWaitingEventpoint(eventpoint)
|
||||
eventpoint._isWaitingEvent.set()
|
||||
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
self._waitMap[id][1].wait()
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.threadManager().registerPIDerror()
|
||||
|
||||
del self._waitMap[id]
|
||||
self._unregisterWaitingEventpoint(eventpoint)
|
||||
try:
|
||||
self.runCommand("DELETE_EVENTPOINT", { "id" : id })
|
||||
debug(Debug, "COOJA event " + str(id) + " has occurred")
|
||||
except chakana.error.CoojaExit:
|
||||
pass
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
def runCommand(self, name, args = None, xmlContent = ""):
|
||||
debug(Debug3, "Running cooja command " + name + ", args: " + repr(args) +
|
||||
" xml content: " + repr(xmlContent))
|
||||
if args is None:
|
||||
args = {}
|
||||
self._connectionLock.acquire()
|
||||
try:
|
||||
if self._coojaStream.closed:
|
||||
raise chakana.error.CoojaExit()
|
||||
command = ['<command value="' + name + '">']
|
||||
for (key, value) in args.items():
|
||||
if value is None:
|
||||
command.append('<' + key + '/>')
|
||||
else:
|
||||
command.append('<' + key + '>' + str(value) + '</' + key + '>')
|
||||
command.append(xmlContent + '</command>')
|
||||
commandStr = "\n".join(command + ['\n'])
|
||||
debug(MinorEvent, '--> ' + commandStr)
|
||||
self._coojaStream.write(commandStr)
|
||||
self._coojaStream.flush()
|
||||
response = self.readFromCooja()
|
||||
debug(Debug, response.toprettyxml())
|
||||
if response.documentElement.tagName == 'error':
|
||||
raise chakana.error.CoojaError(response)
|
||||
return response
|
||||
finally:
|
||||
self._connectionLock.release()
|
||||
|
||||
def readFromCooja(self):
|
||||
# XXX: Assume message ends with a newline
|
||||
debug(Debug, "Reading message from COOJA")
|
||||
responseLines = [self._coojaStream.readline()]
|
||||
debug(Debug2, "First line: " + repr(responseLines[0]))
|
||||
(rootElement, slash) = re.match(r"^\s*<(\w+)(/?)>",
|
||||
responseLines[0]).groups()
|
||||
debug(Debug3, "Root element: " + rootElement)
|
||||
if slash != "/":
|
||||
while 1:
|
||||
if re.search("</" + re.escape(rootElement) + ">$", responseLines[-1],
|
||||
re.M):
|
||||
break
|
||||
responseLines.append(self._coojaStream.readline())
|
||||
debug(Debug3, "Read line: " + repr(responseLines[-1]))
|
||||
result = "".join(responseLines)
|
||||
debug(MinorEvent, '<-- ' + result)
|
||||
return xml.dom.minidom.parseString(result)
|
||||
|
||||
def readVariable(self, variable, ** kwArgs):
|
||||
response = self.readMemory(
|
||||
type = "variable", variable = variable, ** kwArgs)
|
||||
arrayString = response.documentElement.childNodes[0].nodeValue.split()
|
||||
bytes = map(eval, arrayString)
|
||||
debug(Debug, "Read variable " + variable + " as byte array: " +
|
||||
repr(bytes))
|
||||
return bytes
|
||||
|
||||
def readInt(self, ** kwArgs):
|
||||
response = self.readMemory(type = "int", ** kwArgs)
|
||||
return eval(response.documentElement.childNodes[0].nodeValue)
|
||||
|
||||
def readMemory(self, ** kwArgs):
|
||||
return self.runCommand("READ_MEMORY", kwArgs)
|
||||
|
||||
def killNodesInInterval(self, ** kwArgs):
|
||||
return self.runCommand("KILL_NODES", kwArgs)
|
||||
|
||||
def sendCustomCommand(self, ** kwArgs):
|
||||
return self.runCommand("CUSTOM_COMMAND", kwArgs)
|
||||
|
||||
def getSimulationInfo(self, ** kwArgs):
|
||||
response = self.runCommand("GET_INFO", kwArgs)
|
||||
return eval(response.documentElement.childNodes[0].nodeValue)
|
||||
|
||||
def __repr__(self):
|
||||
return "\n ".join([
|
||||
"Shepherd:",
|
||||
"Port: " + repr(self._port),
|
||||
"Monitors: " + repr(self._monitors),
|
||||
"Waiting monitors: " + repr(self._waitingMonitors),
|
||||
"Basic eventpoints: [" + "\n".join(map(repr, self._eventpoints)) + "]",
|
||||
"Waiting eventpoints: [" + "\n".join(
|
||||
map(repr, self._waitingEventpoints)) + "]",
|
||||
"Wait map: " + repr(self._waitMap),
|
||||
])
|
287
tools/chakana/threads.py
Normal file
287
tools/chakana/threads.py
Normal file
|
@ -0,0 +1,287 @@
|
|||
#
|
||||
# Copyright (C) 2005-2007 Swedish Institute of Computer Science.
|
||||
#
|
||||
# Please refer to the file named LICENSE in the same directory as this file
|
||||
# for licensing information.
|
||||
#
|
||||
# Written and maintained by Lars Albertsson <lalle@sics.se>.
|
||||
#
|
||||
|
||||
# $Id: threads.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
|
||||
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import Queue
|
||||
import thread
|
||||
|
||||
import chakana.error
|
||||
import chakana.linux
|
||||
from chakana.debug import *
|
||||
|
||||
class ManagedThread(threading.Thread):
|
||||
def __init__(self, threadManager, ** kwArgs):
|
||||
self._threadManager = threadManager
|
||||
self._exceptionInfo = None
|
||||
resourceAllocated = 0
|
||||
while resourceAllocated == 0:
|
||||
try:
|
||||
threading.Thread.__init__(self, ** kwArgs)
|
||||
resourceAllocated = 1
|
||||
except thread.error:
|
||||
resourceAllocated = 0
|
||||
self.threadManager().registerPIDerror()
|
||||
|
||||
debug(Event, "Initialising thread " + self.getName())
|
||||
self._threadManager._registerThread(self)
|
||||
|
||||
def threadManager(self):
|
||||
return self._threadManager
|
||||
|
||||
def start(self):
|
||||
try:
|
||||
self._threadManager
|
||||
except:
|
||||
raise RuntimeError("ManagedThread started, but not initialised properly")
|
||||
return threading.Thread.start(self)
|
||||
|
||||
def run(self):
|
||||
debug(Event, self.getName() + " thread is now running")
|
||||
self.threadManager()._registerThreadStart(self)
|
||||
try:
|
||||
self.doRun()
|
||||
except Exception, err:
|
||||
debug(MajorEvent, "Caught exception in " + self.getName() + " thread" +
|
||||
":\n" + exceptionDump(sys.exc_info()))
|
||||
self._exceptionInfo = sys.exc_info()
|
||||
except:
|
||||
debug(MajorEvent, "Uncaught exception in " + self.getName() + " thread")
|
||||
self._exceptionInfo = sys.exc_info()
|
||||
|
||||
if not self._exceptionInfo is None:
|
||||
debug(Event, "Exception caught:\n" + exceptionDump(self._exceptionInfo))
|
||||
|
||||
debug(Event, self.getName() + " thread is terminating")
|
||||
if not self.getName() == 'COOJA' and self._exceptionInfo is None:
|
||||
debug(MinorEvent, self.getName() + " terminated quietly")
|
||||
self.threadManager()._listLock.acquire()
|
||||
del self.threadManager()._startedThreads[self.getName()]
|
||||
del self.threadManager()._activeThreads[self.getName()]
|
||||
self.threadManager()._listLock.release()
|
||||
else:
|
||||
debug(MinorEvent, self.getName() + " terminated normally (slow)")
|
||||
self.threadManager()._registerResult(self, self._exceptionInfo)
|
||||
|
||||
debug(Debug, "End of " + self.getName() + " thread")
|
||||
|
||||
def doRun(self):
|
||||
threading.Thread.run(self)
|
||||
|
||||
|
||||
class ThreadManager(object):
|
||||
"""Singleton class for managing active python threads."""
|
||||
def __init__(self):
|
||||
self._activeThreads = {}
|
||||
self._startedThreads = {}
|
||||
self._threadTerminations = []
|
||||
self._pidErrors = 0
|
||||
# Protects the three lists above
|
||||
self._listLock = threading.RLock()
|
||||
self._threadTerminationQueue = Queue.Queue()
|
||||
debug(Debug, self.summary())
|
||||
|
||||
def _registerThread(self, thread):
|
||||
"Called by thread classes when a thread is created."
|
||||
assert(isinstance(thread, ManagedThread))
|
||||
self._listLock.acquire()
|
||||
try:
|
||||
assert(not thread.getName() in self._activeThreads)
|
||||
debug(Debug, "Registering " + thread.getName() + " thread")
|
||||
self._activeThreads[thread.getName()] = thread
|
||||
debug(Debug, self.summary())
|
||||
finally:
|
||||
self._listLock.release()
|
||||
|
||||
def _registerThreadStart(self, thread):
|
||||
"Called by the thread classes' run method."
|
||||
assert(isinstance(thread, ManagedThread))
|
||||
self._listLock.acquire()
|
||||
try:
|
||||
assert(thread.getName() in self._activeThreads)
|
||||
assert(not thread.getName() in self._startedThreads)
|
||||
debug(Debug, "Registering " + thread.getName() + " thread start")
|
||||
self._startedThreads[thread.getName()] = thread
|
||||
debug(Debug, self.summary())
|
||||
finally:
|
||||
self._listLock.release()
|
||||
|
||||
def _registerResult(self, thread, exceptionInfo):
|
||||
self._threadTerminationQueue.put((thread.getName(), exceptionInfo))
|
||||
|
||||
def runningThreads(self):
|
||||
self._listLock.acquire()
|
||||
try:
|
||||
ret = []
|
||||
for thread in self._startedThreads.values():
|
||||
if not thread.getName() in [
|
||||
te[0].getName() for te in self._threadTerminations]:
|
||||
ret.append(thread)
|
||||
return ret
|
||||
finally:
|
||||
self._listLock.release()
|
||||
|
||||
def unstartedThreads(self):
|
||||
self._listLock.acquire()
|
||||
try:
|
||||
ret = []
|
||||
for thread in self._activeThreads.values():
|
||||
if not thread.getName() in self._startedThreads:
|
||||
ret.append(thread)
|
||||
return ret
|
||||
finally:
|
||||
self._listLock.release()
|
||||
|
||||
def numThreadsLeft(self):
|
||||
return len(self.unfinishedThreads())
|
||||
|
||||
def unfinishedThreads(self):
|
||||
"Return created threads that have not terminated."
|
||||
self._listLock.acquire()
|
||||
try:
|
||||
return self._activeThreads.values()
|
||||
finally:
|
||||
self._listLock.release()
|
||||
|
||||
def waitAll(self, timeout = 3600, mourningTime = 5):
|
||||
"Wait for all threads to terminate."
|
||||
debug(MajorEvent, "Waiting for all test threads to terminate")
|
||||
debug(MinorEvent, self.summary())
|
||||
limit = time.time() + timeout
|
||||
while self.numThreadsLeft() > 0:
|
||||
try:
|
||||
nextTimeout = limit - time.time()
|
||||
if nextTimeout <= 0:
|
||||
raise chakana.error.Timeout(self.unfinishedThreads()[0], timeout)
|
||||
else:
|
||||
self.waitOne(nextTimeout)
|
||||
except chakana.error.Timeout, err:
|
||||
debug(Error, "Timeout waiting for " + err.child.getName() +
|
||||
" thread, killing subprocesses.")
|
||||
self.killAll()
|
||||
while self.numThreadsLeft() > 0:
|
||||
try:
|
||||
self.waitOne(mourningTime)
|
||||
except chakana.error.Timeout:
|
||||
debug(Error, "Timeout while mourning threads, aborting")
|
||||
signal.signal(signal.SIGABRT, signal.SIG_DFL)
|
||||
os.abort()
|
||||
raise
|
||||
debug(MinorEvent, "Done waiting for " + str(len(self._startedThreads)) +
|
||||
" threads")
|
||||
debug(Debug, self.summary())
|
||||
for (thread, excInfo) in self._threadTerminations:
|
||||
if not excInfo is None:
|
||||
debug(MinorEvent, "Rethrowing exception from " + thread.getName() +
|
||||
" thread")
|
||||
raise chakana.error.ChildException(excInfo[1], excInfo)
|
||||
|
||||
def waitOne(self, timeout):
|
||||
"Wait for any one thread."
|
||||
debug(Event, "Waiting for some thread to finish, timeout = " +
|
||||
str(timeout))
|
||||
debug(MinorEvent, self.summary())
|
||||
debug(Debug, "Threads left: " +
|
||||
str(map(ManagedThread.getName, self.unfinishedThreads())))
|
||||
assert(self.numThreadsLeft() > 0)
|
||||
try:
|
||||
(threadName, exception) = self._threadTerminationQueue.get(
|
||||
timeout = timeout)
|
||||
except Queue.Empty:
|
||||
raise chakana.error.Timeout(self.unfinishedThreads()[0], timeout)
|
||||
debug(MinorEvent, "Received termination signal from thread " + threadName)
|
||||
self._listLock.acquire()
|
||||
try:
|
||||
assert(threadName in self._activeThreads)
|
||||
terminatedThread = self._activeThreads[threadName]
|
||||
assert(terminatedThread.getName() == threadName)
|
||||
if exception is None:
|
||||
debug(Debug, "Thread " + threadName + " completed successfully")
|
||||
else:
|
||||
debug(Debug, "Thread " + threadName + " raised an exception")
|
||||
self._threadTerminations.append((terminatedThread, exception))
|
||||
self.killAll()
|
||||
terminatedThread.join()
|
||||
debug(Debug, "Deleting " + threadName + " thread from active threads")
|
||||
del self._activeThreads[threadName]
|
||||
del self._startedThreads[threadName]
|
||||
finally:
|
||||
self._listLock.release()
|
||||
|
||||
def killAll(self, reason = "Error detected, killing remaining processes"):
|
||||
debug(MajorEvent, reason)
|
||||
chakana.linux.Process(os.getpid()).killAllBelow()
|
||||
|
||||
def registerPIDerror(self):
|
||||
self._pidErrors = self._pidErrors + 1
|
||||
if self._pidErrors > 100000:
|
||||
self.killAll(reason = "PID allocation errors > 100000")
|
||||
|
||||
def nrPIDerrors(self):
|
||||
return self._pidErrors
|
||||
|
||||
def summary(self):
|
||||
return "ThreadManager: " + str(self.numThreadsLeft()) + \
|
||||
" threads left, " + \
|
||||
str(len(self.unstartedThreads())) + " unstarted threads,\n" + \
|
||||
str(len(self.runningThreads())) + " running threads"
|
||||
|
||||
class TimeoutHelperThread(threading.Thread):
|
||||
def __init__(self, func, funcArgs, funcKwArgs, ** kwArgs):
|
||||
threading.Thread.__init__(self, ** kwArgs)
|
||||
self._func = func
|
||||
self._args = funcArgs
|
||||
self._kwArgs = funcKwArgs
|
||||
self.result = None
|
||||
self.error = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.result = self._func(* self._args, ** self._kwArgs)
|
||||
except Exception, err:
|
||||
debug(Debug, "Caught exception in timeout function: " + str(err) +
|
||||
":\n" + exceptionDump(sys.exc_info()))
|
||||
self.error = err
|
||||
self.exc_info = sys.exc_info()
|
||||
|
||||
class RunWithTimeout:
|
||||
"""Run func in a separate thread. If timeout seconds elapse, give
|
||||
up, raise Timeout(thread, timeout)"""
|
||||
|
||||
def __init__(self, timeout, func, name = None):
|
||||
self._timeout = timeout
|
||||
self._func = func
|
||||
if name is None:
|
||||
self._name = "thread running " + str(func)
|
||||
else:
|
||||
self._name = name
|
||||
|
||||
def __call__(self, * args, ** kwArgs):
|
||||
thread = TimeoutHelperThread(self._func, args, kwArgs, name = self._name)
|
||||
thread.start()
|
||||
thread.join(self._timeout)
|
||||
if thread.isAlive():
|
||||
raise chakana.error.Timeout(thread, self._timeout)
|
||||
if thread.error is None:
|
||||
return thread.result
|
||||
raise chakana.error.ChildException(thread.error, thread.exc_info)
|
||||
|
||||
def waitForAll(threads, timeout):
|
||||
startTime = time.time()
|
||||
for t in threads:
|
||||
if timeout is None:
|
||||
t.join()
|
||||
else:
|
||||
t.join(startTime + timeout - time.time())
|
||||
if t.isAlive():
|
||||
raise chakana.error.Timeout(t, timeout)
|
736
tools/chakana/utils.py
Normal file
736
tools/chakana/utils.py
Normal file
|
@ -0,0 +1,736 @@
|
|||
|
||||
# Copyright (C) 2003-2007 Swedish Institute of Computer Science.
|
||||
#
|
||||
# Please refer to the file named LICENSE in the same directory as this
|
||||
# file for licensing information.
|
||||
#
|
||||
|
||||
|
||||
# $Id: utils.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
|
||||
|
||||
import errno
|
||||
import math
|
||||
import operator
|
||||
import os
|
||||
import popen2
|
||||
import re
|
||||
import string
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import traceback
|
||||
import types
|
||||
|
||||
import chakana.command
|
||||
import chakana.error
|
||||
from chakana.debug import *
|
||||
|
||||
try:
|
||||
global False
|
||||
global True
|
||||
_ = False
|
||||
except NameError:
|
||||
False = 0
|
||||
True = 1
|
||||
|
||||
class DefaultDict(dict):
|
||||
def __init__(self, defaultCreator, initialiser = (), inject = 0):
|
||||
dict.__init__(self, initialiser)
|
||||
if callable(defaultCreator):
|
||||
self._defaultCreator = defaultCreator
|
||||
else:
|
||||
self._defaultCreator = lambda k: defaultCreator
|
||||
self._inject = inject
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return dict.__getitem__(self, key)
|
||||
except KeyError, err:
|
||||
value = self._defaultCreator(key)
|
||||
if self._inject:
|
||||
self[key] = value
|
||||
return self[key]
|
||||
else:
|
||||
return value
|
||||
|
||||
class UnbufferedFile:
|
||||
def __init__(self, fileObj):
|
||||
self._fileObj = fileObj
|
||||
|
||||
def __getattr__(self, attribute):
|
||||
return getattr(self._fileObj, attribute)
|
||||
|
||||
def write(self, what):
|
||||
self._fileObj.write(what)
|
||||
self._fileObj.flush()
|
||||
|
||||
def copyFile(src, dest):
|
||||
debug(Event, "cp " + src + " " + dest)
|
||||
srcFile = file(src)
|
||||
destFile = file(dest, "w")
|
||||
destFile.write(srcFile.read())
|
||||
srcFile.close()
|
||||
destFile.close()
|
||||
|
||||
def unique(list):
|
||||
ret = []
|
||||
inList = {}
|
||||
for elem in list:
|
||||
if not elem in inList:
|
||||
ret.append(elem)
|
||||
inList[elem] = 1
|
||||
return ret
|
||||
|
||||
def unorderedEqual(listOne, listTwo, predicate = lambda x, y: x == y):
|
||||
if len(listOne) != len(listTwo):
|
||||
return 0
|
||||
list1 = list(listOne)
|
||||
list2 = list(listTwo)
|
||||
while list1 != []:
|
||||
found = 0
|
||||
for index in range(len(list2)):
|
||||
if predicate(list1[0], list2[index]):
|
||||
del list1[0]
|
||||
del list2[index]
|
||||
found = 1
|
||||
break
|
||||
if not found:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def reduceSequences(tupleSeq, oper = operator.add):
|
||||
"""Return a list, where the item at position i is the reduced value
|
||||
of all items at position i."""
|
||||
|
||||
if len(tupleSeq) == 0:
|
||||
return []
|
||||
return [reduce(oper, [s[i] for s in tupleSeq])
|
||||
for i in range(len(tupleSeq[0]))]
|
||||
|
||||
def listRemove(theList, predicate):
|
||||
"""Remove all items that match predicate in theList."""
|
||||
if not callable(predicate):
|
||||
return listRemove(theList, lambda x: x == predicate)
|
||||
else:
|
||||
index = 0
|
||||
while index < len(theList):
|
||||
if predicate(theList[index]):
|
||||
del theList[index]
|
||||
else:
|
||||
index += 1
|
||||
return theList
|
||||
|
||||
def logn(n, x):
|
||||
return math.log(x) / math.log(n)
|
||||
|
||||
def longRange(* args):
|
||||
"""As range, but works with long integers."""
|
||||
if len(args) == 1:
|
||||
start = 0L
|
||||
stop = args[0]
|
||||
step = 1L
|
||||
elif len(args) == 2:
|
||||
(start, stop) = args
|
||||
step = 1L
|
||||
else:
|
||||
(start, stop, step) = args
|
||||
if step == 0:
|
||||
raise ValueError("longRange() arg 3 must not be zero")
|
||||
ret = []
|
||||
while cmp(start, stop) == cmp(0, step):
|
||||
ret.append(start)
|
||||
start += step
|
||||
return ret
|
||||
|
||||
integerSuffixes = ("", "K", "M", "G", "T")
|
||||
|
||||
def abbreviateInteger(i, factor = 1024, minimise = 0):
|
||||
suf = 0
|
||||
while suf < len(integerSuffixes) - 1 and \
|
||||
((i > 9999) or (minimise and (i != 0) and (i % factor == 0))):
|
||||
suf += 1
|
||||
i /= factor
|
||||
return str(i) + integerSuffixes[suf]
|
||||
|
||||
def readSuffixedInt(intString, factor = 1024):
|
||||
if intString[-1].isalpha():
|
||||
return int(intString[: -1]) * (
|
||||
factor ** list(integerSuffixes).index(intString[-1].upper()))
|
||||
else:
|
||||
return int(intString)
|
||||
|
||||
def normaliseIndex(index, seq):
|
||||
"""Return a non-negative index relating to a sequence, or negative if
|
||||
index < - len(seq)."""
|
||||
|
||||
if index < 0:
|
||||
return len(seq) + index
|
||||
else:
|
||||
return index
|
||||
|
||||
def truncateIndex(index, seq):
|
||||
"""Return a non-negative index relating to a sequence, less than or equal
|
||||
to the length of the sequence."""
|
||||
ret = normaliseIndex(index, seq)
|
||||
if ret < 0:
|
||||
return 0
|
||||
length = len(seq)
|
||||
if ret > length:
|
||||
return length
|
||||
return ret
|
||||
|
||||
class LazySlice:
|
||||
"""Representation of slice into other sequence."""
|
||||
def __init__(self, seq, start = None, stop = None, step = None):
|
||||
self._sequence = seq
|
||||
if step is None:
|
||||
self._step = 1
|
||||
else:
|
||||
self._step = step
|
||||
if self._step == 0:
|
||||
# Provoke ValueError
|
||||
[][0:1:0]
|
||||
|
||||
if self._step > 0:
|
||||
startDefault = 0
|
||||
stopDefault = len(seq)
|
||||
indexShift = 0
|
||||
else:
|
||||
startDefault = len(seq) - 1
|
||||
stopDefault = -1
|
||||
indexShift = 1
|
||||
|
||||
if start is None:
|
||||
self._start = startDefault
|
||||
else:
|
||||
self._start = truncateIndex(start + indexShift, seq) - indexShift
|
||||
if stop is None:
|
||||
self._stop = stopDefault
|
||||
else:
|
||||
self._stop = truncateIndex(stop + indexShift, seq) - indexShift
|
||||
|
||||
if (self._step > 0) != (self._start < self._stop):
|
||||
self._stop = self._start
|
||||
|
||||
def __getitem__(self, key):
|
||||
# debug(Debug2, "LS getitem " + str(key) + ": " + repr(self))
|
||||
if type(key) == types.SliceType:
|
||||
ret = LazySlice(self, key.start, key.stop, key.step)
|
||||
#debug(Debug2, "LS getitem " + str(key) + " = " +
|
||||
# repr(ret) + ", LS: " + repr(self))
|
||||
return ret
|
||||
index = normaliseIndex(key, self)
|
||||
if index < 0:
|
||||
# Provoke IndexError
|
||||
[][1]
|
||||
seqIndex = self._start + index * self._step
|
||||
if self._step > 0:
|
||||
if seqIndex >= self._stop:
|
||||
[][1]
|
||||
elif seqIndex <= self._stop:
|
||||
[][1]
|
||||
ret = self._sequence[seqIndex]
|
||||
#debug(Debug2, "LS getitem " + str(key) + " (" + str(seqIndex) + ") = " +
|
||||
# str(ret) + ", LS: " + repr(self))
|
||||
return ret
|
||||
|
||||
def __len__(self):
|
||||
return (abs(self._stop - self._start) + abs(self._step) - 1) / \
|
||||
abs(self._step)
|
||||
|
||||
def __str__(self):
|
||||
return str(list(self))
|
||||
|
||||
def __repr__(self):
|
||||
return "LazySlice(start = " + str(self._start) + ", stop = " + \
|
||||
str(self._stop) + ", step = " + str(self._step) + ",\n " + \
|
||||
repr(self._sequence) + ")"
|
||||
|
||||
def getItem(sequence, key):
|
||||
"""Obtain key in sequence. Key may be an integer or a slice object."""
|
||||
if type(key) == types.SliceType:
|
||||
if key.step is None:
|
||||
return sequence[key.start : key.stop]
|
||||
return sequence[key.start : key.stop : key.step]
|
||||
return sequence[key]
|
||||
|
||||
def binarySearch(list, value, start = 0, end = None):
|
||||
"Return the position where value should be inserted in a sorted list."
|
||||
if end is None:
|
||||
end = len(list)
|
||||
if start == end:
|
||||
return end
|
||||
middle = start + (end - start) / 2
|
||||
if list[middle] < value:
|
||||
return binarySearch(list, value, middle + 1, end)
|
||||
else:
|
||||
return binarySearch(list, value, start, middle)
|
||||
|
||||
def binaryFind(list, value, *args, ** kwArgs):
|
||||
"Return position of value in sorted list, or None if not found."
|
||||
position = binarySearch(list, value, *args, ** kwArgs)
|
||||
if (position == len(list)) or (list[position] != value):
|
||||
return None
|
||||
else:
|
||||
return position
|
||||
|
||||
def compactIntListRepr(input, sort = 0):
|
||||
if sort:
|
||||
list = input[:]
|
||||
list.sort()
|
||||
else:
|
||||
list = input
|
||||
if __debug__:
|
||||
tmp = list[:]
|
||||
tmp.sort()
|
||||
assert(tmp == list)
|
||||
index = 0
|
||||
ret = "["
|
||||
sep = ""
|
||||
compact = lambda l, i: (i + 1 < len(l)) and (l[i] + 1 == l[i + 1])
|
||||
while index < len(list):
|
||||
ret += sep + str(list[index])
|
||||
if compact(list, index):
|
||||
ret += "-"
|
||||
while compact(list, index):
|
||||
index += 1
|
||||
ret += str(list[index])
|
||||
index += 1
|
||||
sep = ","
|
||||
return ret + "]"
|
||||
|
||||
def predicatedIndex(sequence, predicate):
|
||||
for i in range(len(sequence)):
|
||||
if predicate(sequence[i]):
|
||||
return i
|
||||
raise ValueError()
|
||||
|
||||
class ArgumentBinder:
|
||||
def __init__(self, function, argument, position = 0):
|
||||
self.__function = function
|
||||
self.__argument = argument
|
||||
self.__position = position
|
||||
|
||||
def __call__(self, *args, **keywords):
|
||||
newArgs = list(args)
|
||||
newArgs[self.__position : self.__position] = [self.__argument]
|
||||
return self.__function(*newArgs, **keywords)
|
||||
|
||||
class ArgumentTupleBinder:
|
||||
def __init__(self, function, arguments):
|
||||
self._function = function
|
||||
self._arguments = arguments
|
||||
|
||||
def __call__(self, * args, ** kwArgs):
|
||||
return self._function(* (self._arguments + args), ** kwArgs)
|
||||
|
||||
class KeywordArgumentBinder:
|
||||
def __init__(self, function, ** keywords):
|
||||
self._function = function
|
||||
self._keywords = keywords
|
||||
|
||||
def __call__(self, * args, ** kwArgs):
|
||||
kw = self._keywords
|
||||
kw.update(kwArgs)
|
||||
return self._function(* args, ** kw)
|
||||
|
||||
cppKeywords = ["asm", "do", "inline", "short", "typeid", "auto",
|
||||
"double", "int", "signed", "typename", "bool",
|
||||
"dynamic_cast", "long", "sizeof", "union", "break",
|
||||
"else", "mutable", "static", "unsigned", "case",
|
||||
"enum", "namespace", "static_cast", "using", "catch",
|
||||
"explicit", "new", "struct", "virtual", "char",
|
||||
"extern", "operator", "switch", "void", "class",
|
||||
"false", "private", "template", "volatile", "const",
|
||||
"float", "protected", "this", "wchar_t", "const_cast",
|
||||
"for", "public", "throw", "while", "continue",
|
||||
"friend", "register", "true", "default", "goto",
|
||||
"reinterpret_cast", "try", "delete", "if", "return",
|
||||
"typedef"]
|
||||
|
||||
def isCWord(str):
|
||||
return not re.match("^[a-zA-Z_][a-zA-Z0-9_]*$", str) is None
|
||||
|
||||
def isCppIdentifier(str):
|
||||
return isCWord(str) and (str not in cppKeywords)
|
||||
|
||||
def find(seq, predicate = lambda x: x):
|
||||
for item in seq:
|
||||
if predicate(item):
|
||||
return item
|
||||
return None
|
||||
|
||||
def allBest(seq, comparator = cmp):
|
||||
if len(seq) < 2:
|
||||
return seq
|
||||
seq = seq[:]
|
||||
ret = [seq.pop()]
|
||||
for item in seq:
|
||||
c = comparator(item, ret[0])
|
||||
if c < 0:
|
||||
ret = [item]
|
||||
elif c == 0:
|
||||
ret.append(item)
|
||||
return ret
|
||||
|
||||
def readFile(fileName):
|
||||
f = open(fileName)
|
||||
try:
|
||||
ret = f.read()
|
||||
return ret
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def writeFile(fileName, contents):
|
||||
f = open(fileName, "w")
|
||||
try:
|
||||
f.write(contents)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def writeFileAtomic(fileName, contents, suffix = ".tmp"):
|
||||
tmpFile = fileName + suffix
|
||||
writeFile(tmpFile, contents)
|
||||
try:
|
||||
os.rename(tmpFile, fileName)
|
||||
except:
|
||||
if os.path.exists(fileName) and os.path.exists(tmpFile):
|
||||
os.remove(tmpFile)
|
||||
raise
|
||||
|
||||
|
||||
class LineIndexed:
|
||||
"""Read-only file object indexed by line number. The file object must
|
||||
support seeking."""
|
||||
|
||||
def __init__(self, fileName, offset = 0):
|
||||
if type(fileName) == type(""):
|
||||
self._file = file(fileName)
|
||||
else:
|
||||
self._file = fileName
|
||||
self._offset = offset
|
||||
self._length = None
|
||||
self._lineOffsets = [0]
|
||||
|
||||
def close(self):
|
||||
self._file.close()
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def _seekForward(self, index):
|
||||
if self._length == 0:
|
||||
raise IndexError()
|
||||
for index in range(len(self._lineOffsets), index + 1):
|
||||
if self._lineOffsets[-1] == self._length:
|
||||
raise IndexError()
|
||||
self._file.seek(self._lineOffsets[-1] + self._offset)
|
||||
line = self._file.readline()
|
||||
if line == "":
|
||||
self._length = self._lineOffsets[-1]
|
||||
else:
|
||||
self._lineOffsets.append(self._lineOffsets[-1] + len(line))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if type(key) == types.SliceType:
|
||||
ret = LazySlice(self, key.start, key.stop, key.step)
|
||||
return ret
|
||||
if key < 0:
|
||||
positiveLength = len(self) + key
|
||||
if positiveLength < 0:
|
||||
raise IndexError()
|
||||
return self[positiveLength]
|
||||
self._seekForward(key)
|
||||
offset = self._lineOffsets[key]
|
||||
if offset == self._length:
|
||||
raise IndexError()
|
||||
self._file.seek(offset + self._offset)
|
||||
return self._file.readline()
|
||||
|
||||
def __len__(self):
|
||||
if self._length == 0:
|
||||
return 0
|
||||
while self._lineOffsets[-1] != self._length:
|
||||
self._seekForward(len(self._lineOffsets))
|
||||
return len(self._lineOffsets) - 1
|
||||
|
||||
def lineOffset(self, index):
|
||||
self._seekForward(index)
|
||||
return self._lineOffsets[index]
|
||||
|
||||
def __str__(self):
|
||||
return str(list(self))
|
||||
|
||||
def __repr__(self):
|
||||
return "LineIndexed(offset = " + str(self._offset) + ", length = " + \
|
||||
str(self._length) + ", lineOffsets: " + \
|
||||
str(self._lineOffsets) + ")"
|
||||
|
||||
def getLine(fileName, count):
|
||||
fileObj = file(fileName)
|
||||
if count < 0:
|
||||
return fileObj.readlines()[count]
|
||||
for i in range(count + 1):
|
||||
ret = fileObj.readline()
|
||||
if ret == "":
|
||||
raise IndexError()
|
||||
return ret
|
||||
|
||||
def conditionalJoin(list, separator = " ", predicate = lambda e: e != ""):
|
||||
if len(list) == 0:
|
||||
return ""
|
||||
rest = conditionalJoin(list[1:], separator, predicate)
|
||||
if not predicate(rest):
|
||||
return list[0]
|
||||
if not predicate(list[0]):
|
||||
return rest
|
||||
else:
|
||||
return list[0] + separator + rest
|
||||
|
||||
def pathComponents(path):
|
||||
(head, tail) = os.path.split(path)
|
||||
if tail == "":
|
||||
if head == "":
|
||||
return []
|
||||
if head == "/":
|
||||
return ["/"]
|
||||
else:
|
||||
return pathComponents(head)
|
||||
else:
|
||||
return pathComponents(head) + [tail]
|
||||
|
||||
def commonFirstElements(listOfLists):
|
||||
if len(listOfLists) == 0:
|
||||
return []
|
||||
if len(listOfLists) == 1:
|
||||
return listOfLists[0]
|
||||
if (len(listOfLists[0]) == 0) or (len(listOfLists[1]) == 0) or \
|
||||
(listOfLists[0][0] != listOfLists[1][0]):
|
||||
return []
|
||||
else:
|
||||
return commonFirstElements([
|
||||
[listOfLists[0][0]] +
|
||||
commonFirstElements([listOfLists[0][1:], listOfLists[1][1:]])]
|
||||
+ listOfLists[2:])
|
||||
|
||||
def commonPrefix(pathList):
|
||||
"""Similar to os.path.commonprefix, but works on path components instead
|
||||
of individual characters."""
|
||||
|
||||
assert(not isinstance(pathList, str))
|
||||
components = commonFirstElements(map(pathComponents, pathList))
|
||||
if components == []:
|
||||
return ""
|
||||
else:
|
||||
return os.path.join(* components)
|
||||
|
||||
def pathIsBelow(path, directory):
|
||||
"""Check if path lies below directory."""
|
||||
return commonPrefix([path, directory]) == directory
|
||||
|
||||
def realPath(path):
|
||||
"""Similar to os.path.realpath, but handles amd gracefully."""
|
||||
ret = os.path.realpath(path)
|
||||
for binDir in os.environ["PATH"].split(":") + ["/usr/sbin", "/sbin"]:
|
||||
amq = os.path.join(binDir, "amq")
|
||||
if os.path.isfile(amq):
|
||||
try:
|
||||
output = chakana.command.output(amq)
|
||||
except chakana.error.CommandFailed, err:
|
||||
debug(Debug, str(err))
|
||||
# Assume amd is not running
|
||||
return ret
|
||||
for line in output.splitlines():
|
||||
amdDir = line.split()[0]
|
||||
mountDir = line.split()[-1]
|
||||
if mountDir[0] == "/":
|
||||
match = re.match('^(' + re.escape(line) + ')(/$)', ret)
|
||||
if match:
|
||||
return amdDir + ret[len(line) :]
|
||||
return ret
|
||||
return ret
|
||||
|
||||
def removeTree(path, ignoreErrors = False):
|
||||
debug(Debug, "rm -rf " + path)
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path, ignore_errors = ignoreErrors)
|
||||
|
||||
def makeSymlink(value, dest, force = True, debugLevel = MinorEvent,
|
||||
dryRun = False):
|
||||
if os.path.islink(dest):
|
||||
if os.readlink(dest) == value:
|
||||
debug(Debug, "Link " + dest + " already points to " + value)
|
||||
return
|
||||
elif force:
|
||||
debug(debugLevel, "Removing " + dest)
|
||||
if not dryRun:
|
||||
os.remove(dest)
|
||||
else:
|
||||
raise OSError((errno.EEXIST, "Link already exists", dest))
|
||||
absValue = os.path.join(os.path.dirname(dest), value)
|
||||
if (not dryRun) and (not force) and (not os.path.isfile(absValue)):
|
||||
raise OSError((errno.ENOENT, "Link destination does not exist", absDest))
|
||||
debug(debugLevel, "Linking " + dest + " to " + value)
|
||||
if not dryRun:
|
||||
os.symlink(value, dest)
|
||||
|
||||
def copyTree(src, dst, symlinks = False, predicate = lambda path: 1,
|
||||
preserveTimes = False):
|
||||
"""Similar to shutil.copytree, but allows existing destination and
|
||||
passes exceptions."""
|
||||
names = filter(predicate, os.listdir(src))
|
||||
if not os.path.isdir(dst):
|
||||
os.mkdir(dst)
|
||||
for name in names:
|
||||
srcname = os.path.join(src, name)
|
||||
dstname = os.path.join(dst, name)
|
||||
try:
|
||||
if symlinks and os.path.islink(srcname):
|
||||
linkto = os.readlink(srcname)
|
||||
os.symlink(linkto, dstname)
|
||||
elif os.path.isdir(srcname):
|
||||
copyTree(srcname, dstname, symlinks, predicate, preserveTimes)
|
||||
else:
|
||||
if preserveTimes:
|
||||
shutil.copy2(srcname, dstname)
|
||||
else:
|
||||
shutil.copy(srcname, dstname)
|
||||
# XXX What about devices, sockets etc.?
|
||||
except (IOError, os.error), why:
|
||||
debug(Error, "Can't copy %s to %s: %s" %
|
||||
(`srcname`, `dstname`, str(why)))
|
||||
raise
|
||||
|
||||
def makeDirsSafe(dir, mode = 0777):
|
||||
"""Similar to os.makedirs, but does not fail if another process is making
|
||||
the directory simultaneously."""
|
||||
while not os.path.isdir(dir):
|
||||
try:
|
||||
os.makedirs(dir, mode)
|
||||
except OSError, err:
|
||||
if err.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
def typeAssert(variable, type):
|
||||
assert(isinstance(variable, type))
|
||||
|
||||
def intervalsOverlap(range1, range2):
|
||||
if range1 > range2:
|
||||
return intervalsOverlap(range2, range1)
|
||||
if (range1[0] == range1[1]) or (range2[0] == range2[1]):
|
||||
return 0
|
||||
return range2[0] < range1[1]
|
||||
|
||||
class LexicalDistance:
|
||||
def __init__(self, caseChange = 1, whiteSpaceChange = 2,
|
||||
whiteSpaceRemoval = 3, ampersand = 5, other = 10,
|
||||
whiteSpace = " _-'\""):
|
||||
self.__caseChange = caseChange
|
||||
self.__whiteSpaceChange = whiteSpaceChange
|
||||
self.__whiteSpaceRemoval = whiteSpaceRemoval
|
||||
self.__ampersand = ampersand
|
||||
self.__other = other
|
||||
self.__whiteSpace = whiteSpace
|
||||
|
||||
def removeWhiteSpace(self, str1, str2, limit):
|
||||
if str1[0] in self.__whiteSpace:
|
||||
if limit > self.__whiteSpaceRemoval:
|
||||
return self(str1[1:], str2, limit - self.__whiteSpaceRemoval) + \
|
||||
self.__whiteSpaceRemoval
|
||||
return limit
|
||||
|
||||
def changeWhiteSpace(self, str1, str2, limit):
|
||||
if (str1[0] in self.__whiteSpace) and (str2[0] in self.__whiteSpace):
|
||||
if limit > self.__whiteSpaceChange:
|
||||
return self(str1[1:], str2[1:], limit - self.__whiteSpaceChange) + \
|
||||
self.__whiteSpaceChange
|
||||
return limit
|
||||
|
||||
def changeCase(self, str1, str2, limit):
|
||||
if str1[0].upper() == str2[0].upper():
|
||||
if limit > self.__caseChange:
|
||||
return self(str1[1:], str2[1:], limit - self.__caseChange) + \
|
||||
self.__caseChange
|
||||
return limit
|
||||
|
||||
def changeAmpersand(self, str1, str2, limit):
|
||||
if (str1[0] == "&") and (str2[:3].lower() == "and"):
|
||||
if limit > self.__ampersand:
|
||||
return self(str1[1:], str2[3:], limit - self.__ampersand) + \
|
||||
self.__ampersand
|
||||
return limit
|
||||
|
||||
def removeOther(self, str1, str2, limit):
|
||||
if limit > self.__other:
|
||||
return self(str1[1:], str2, limit - self.__other) + self.__other
|
||||
return limit
|
||||
|
||||
def changeOther(self, str1, str2, limit):
|
||||
if limit > self.__other:
|
||||
return self(str1[1:], str2[1:], limit - self.__other) + self.__other
|
||||
return limit
|
||||
|
||||
def __call__(self, str1, str2, limit = 50):
|
||||
ret = limit
|
||||
if str1 == "":
|
||||
if str2 == "":
|
||||
return 0
|
||||
ret = self.removeWhiteSpace(str2, str1, ret)
|
||||
ret = self.removeOther(str2, str1, ret)
|
||||
elif str2 == "":
|
||||
ret = self(str2, str1, ret)
|
||||
else:
|
||||
if str1[0] == str2[0]:
|
||||
ret = self(str1[1:], str2[1:], ret)
|
||||
else:
|
||||
ret = self.removeWhiteSpace(str1, str2, ret)
|
||||
ret = self.removeWhiteSpace(str2, str1, ret)
|
||||
ret = self.changeWhiteSpace(str1, str2, ret)
|
||||
ret = self.changeCase(str1, str2, ret)
|
||||
ret = self.changeAmpersand(str1, str2, ret)
|
||||
ret = self.changeAmpersand(str2, str1, ret)
|
||||
ret = self.changeOther(str1, str2, ret)
|
||||
ret = self.removeOther(str1, str2, ret)
|
||||
ret = self.removeOther(str2, str1, ret)
|
||||
assert(ret <= limit)
|
||||
return ret
|
||||
|
||||
class TryOperation:
|
||||
def __init__(self, operation, description = None):
|
||||
self.__operation = operation
|
||||
if description is None:
|
||||
self.__description = str(operation)
|
||||
else:
|
||||
self.__description = description
|
||||
|
||||
def __call__(self, * args, ** kwargs):
|
||||
try:
|
||||
self.__operation(* args, ** kwargs)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
import debug as d
|
||||
d.debug(d.Error, self.__description + " failed\n" +
|
||||
d.exceptionDump(sys.exc_info()))
|
||||
|
||||
def lineDirective():
|
||||
fileInfo = traceback.extract_stack(None, 2)[0]
|
||||
return "#line " + repr(fileInfo[1] + 1) + " \"" + fileInfo[0] + "\""
|
||||
|
||||
try:
|
||||
mkstemp = tempfile.mkstemp
|
||||
except AttributeError:
|
||||
def mkstemp(suffix = "", prefix = None, dir = None, text = 0):
|
||||
if prefix is None:
|
||||
prefix = tempfile.gettempprefix()
|
||||
fileName = tempfile.mktemp(suffix)
|
||||
if dir is None:
|
||||
dir = os.path.dirname(fileName)
|
||||
newFileName = os.path.join(dir, prefix + os.path.basename(fileName))
|
||||
if text:
|
||||
flags = "w"
|
||||
else:
|
||||
flags = "wb"
|
||||
return (file(newFileName, flags), newFileName)
|
||||
|
Loading…
Reference in a new issue