/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.backend.java.visualization; import java.io.IOException; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import abs.backend.java.lib.runtime.ABSException; import abs.backend.java.lib.types.ABSValue; import abs.backend.java.observing.COGView; import abs.backend.java.observing.EmptyTaskObserver; import abs.backend.java.observing.FutView; import abs.backend.java.observing.GuardView; import abs.backend.java.observing.ObjectCreationObserver; import abs.backend.java.observing.ObjectView; import abs.backend.java.observing.SystemObserver; import abs.backend.java.observing.TaskObserver; import abs.backend.java.observing.TaskSchedulerObserver; import abs.backend.java.observing.TaskStackFrameView; import abs.backend.java.observing.TaskView; public class SequenceDiagramVisualization implements SystemObserver, TaskObserver, ObjectCreationObserver, TaskSchedulerObserver { private static final int MAX_ARG_LENGTH = 30; Set<FutView> waitingFutures = new HashSet<FutView>(); Set<FutView> resolvedFutures = new HashSet<FutView>(); final Map<COGView, String> createdCOGClasses = new HashMap<COGView, String>(); final Map<COGView, Integer> objectIds = new HashMap<COGView, Integer>(); final Map<String, AtomicInteger> idCounters = new HashMap<String, AtomicInteger>(); protected String getName() { return "Simulation"; } protected boolean abstractEnvironment = false; boolean showStartMsg = true; boolean staticActors = false; boolean firstMessage = true; private Worker worker; @Override public synchronized void newCOGCreated(COGView cog, ObjectView initialObject) { // writeOut(initialObject.getClassName() + ":" + // initialObject.getClassName() + "[ap]"); // System.writeOut(initialObject.getClassName()); String className = initialObject.getClassName(); createdCOGClasses.put(cog,className); if (isObservedClass(className)) { cog.getScheduler().registerTaskSchedulerObserver(this); if (!staticActors) { if (!firstMessage) { // special support for adding nodes later to a diagram writeOut("#newobj "); } // use sdedit's <label> syntax to create a label that has the class name and the COG ID // the "a" flag suppresses the object's name (before the colon) - which is useless in this case, as the label is used writeOutLn(getActorName(initialObject) + ":" + className + "[a]" + "\"" + className + " [COG " + cog.getID() + "]\""); } } cog.registerObjectCreationListener(this); } @Override public synchronized void objectCreated(ObjectView o) { /* if (!staticActors) { writeOut(getActorName(o) + ":" + o.getClassName() + "[a]"); } */ } @Override public synchronized void objectInitialized(ObjectView o) { /* if (!staticActors) { writeOut(getActorName(o) + ":" + o.getClassName() + "[a]"); } */ } protected synchronized Integer getID(ObjectView v) { Integer id = objectIds.get(v.getCOG()); if (id == null) { AtomicInteger counter = idCounters.get(v.getClassName()); if (counter == null) { counter = new AtomicInteger(); idCounters.put(v.getClassName(), counter); } id = counter.incrementAndGet(); objectIds.put(v.getCOG(), id); } return id; } public boolean isObservedClass(String className) { return getObservedClasses().contains(className); } @Override public synchronized void systemStarted() { worker = new Worker(); worker.start(); writeOutLn(getName()); initializeActors(); } protected void writeOutLn(String s) { writeOut(s + "\n"); } protected void writeOut(String s) { worker.write(s); } protected void writeOutLn() { writeOutLn(""); } protected void initializeActors() { if (showStartMsg) writeOutLn("HiddenEnv:HiddenEnv[pe]"); if (abstractEnvironment) writeOutLn("ENVIRONMENT:ENVIRONMENT[ap]"); } public String getActorName(ObjectView v) { if (abstractEnvironment && getEnvironmentClasses().contains(getClassName(v))) { return "ENVIRONMENT"; } Integer id = getID(v); return getClassName(v) + "_" + id.intValue(); } public String getActorName(String className) { if (abstractEnvironment && getEnvironmentClasses().contains(className)) { return "ENVIRONMENT"; } return className; } private final TaskBlockingObserver TASK_BLOCKING_OBSERVER = new TaskBlockingObserver(); class TaskBlockingObserver extends EmptyTaskObserver { @Override public void taskBlockedOnFuture(TaskView task, FutView fut) { synchronized (SequenceDiagramVisualization.this) { if (!isObserved(task)) return; waitingFutures.add(fut); String actorName = getActorName(task.getTarget()); writeOutLn("*" + fut.getID() + " " + actorName); writeOutLn("future get"); writeOutLn("*" + fut.getID()); writeOutLn("(" + fut.getID() + ") " + actorName); } } @Override public void taskRunningAfterWaiting(TaskView task, FutView fut) { synchronized (SequenceDiagramVisualization.this) { if (!isObserved(task)) return; if (!waitingFutures.contains(fut) || resolvedFutures.contains(fut)) return; resolvedFutures.add(fut); TaskView futTask = fut.getResolvingTask(); String sourceClass = futTask.getTarget().getClassName(); writeOut(getActorName(futTask.getTarget())); if (isSystemClass(sourceClass)) { //if (futTask.getID() != 1) // no main task writeOut("[" + "Task" + futTask.getID() + "]"); writeOut(":>"); } else { writeOut(":"); } String futTaskName = getActorName(task.getTarget()) + "[" + "FutTask" + futTask.getID() + "]"; writeOut(futTaskName); writeOutLn(".future resolved\\:" + shorten(String.valueOf(fut.getValue()))); writeOutLn(futTaskName + ":stop"); writeOutLn("(" + fut.getID() + ") " + getActorName(task.getTarget())); writeOutLn(getActorName(futTask.getTarget())+ "[Task" + futTask.getID() + "]:stop"); } } } @Override public synchronized void taskCreated(TaskView task) { task.registerTaskListener(this); if (task.getSource() == null) { return; } String sourceClass = getClassName(task.getSource()); String targetClass = getClassName(task.getTarget()); if (isObservedClass(sourceClass) && isObservedClass(targetClass)) { String msgAction = ":>"; if (isEnvironmentClass(sourceClass) || isEnvironmentClass(targetClass)) msgAction = ":"; String source = getActorName(task.getSource()); if (isSystemClass(sourceClass)) source = source + "[" + "Task" + task.getSender().getID() + "]"; if (firstMessage) { writeOutLn(); if (showStartMsg) writeOutLn("HiddenEnv:Main_1[Task1].start()"); firstMessage = false; } if (isObservedClass(targetClass)) task.registerTaskListener(TASK_BLOCKING_OBSERVER); writeOut(source); writeOut(msgAction); /* * if (systemClasses.contains(task.getSource().getClassName())) { * writeOut(":>"); } */ writeOut(getActorName(task.getTarget())); if (isSystemClass(targetClass)) { //if (task.getID() != 1) // no main task writeOut("[" + "Task" + task.getID() + "]"); } writeOut("."); writeOut(task.getMethodName()); writeOut("("); StringBuffer argString = new StringBuffer(); boolean first = true; for (ABSValue v : task.getArgs()) { if (first) first = false; else argString.append(", "); argString.append(""+v); } writeOut(shorten(String.valueOf(argString.toString()))); writeOutLn(")"); } } private String escapeColons(String s) { return s.replaceAll(":", "\\\\:"); } private String getClassName(ObjectView obj) { return createdCOGClasses.get(obj.getCOG()); } private String shorten(String arg) { String escapedArg = escapeColons(arg); if (escapedArg.length() > MAX_ARG_LENGTH) { StringBuffer sb = new StringBuffer(escapedArg); int halfLength = MAX_ARG_LENGTH / 2; String rest = escapedArg.substring(escapedArg.length() - halfLength); sb.setLength(halfLength); sb.append(" .. "); sb.append(rest); return sb.toString(); } return escapedArg; } protected boolean isSystemClass(String source) { return getSystemClasses().contains(source); } @Override public synchronized void taskFinished(TaskView task) { if (!isObserved(task)) return; // writeOut("Future " + task.getFuture().getID() + " resolved\\: " + // task.getFuture().getValue()); if (!waitingFutures.contains(task.getFuture())) { String taskName = getActorName(task.getTarget()); //if (task.getID() != 1) // no main task taskName += "[" + "Task" + task.getID() + "]"; writeOutLn(taskName + ":"); // do something to avoid empty tasks writeOutLn(taskName + ":stop"); resolvedFutures.add(task.getFuture()); } } @Override public synchronized void taskStarted(TaskView task) { if (!isObserved(task)) return; String target = task.getTarget().getClassName(); // writeOut(target+"[" + "Task" + task.getID() + "]:<started>"); // // do something to avoid empty tasks } @Override public synchronized void taskSuspended(TaskView task, GuardView guard) { if (!isObserved(task)) return; if (guard.isFutureGuard()) { FutView fut = guard.getFuture(); if (isObserved(fut.getResolvingTask())) { waitingFutures.add(fut); String actorName = getActorName(task.getTarget()); writeOutLn("*" + fut.getID() + " " + actorName); // writeOut("[" + "Task" + task.getID() + "]"); // writeOut(":"); if (guard.isTrue()) { writeOut("yielding"); } else { writeOut("waiting"); } // writeOut(" on future "+fut.getID()); writeOutLn(); writeOutLn("*" + fut.getID()); writeOutLn("(" + fut.getID() + ") " + actorName); } } } public boolean isObserved(TaskView task) { String source = task.getSource().getClassName(); String target = task.getTarget().getClassName(); return isObservedClass(source) && isObservedClass(target) && isSystemClass(target); } @Override public synchronized void taskRunningAfterWaiting(TaskView task, FutView fut) { } @Override public synchronized void taskResumed(TaskView task, GuardView guard) { if (!isObserved(task)) return; if (guard.isFutureGuard()) { FutView fut = guard.getFuture(); TaskView resolvingTask = fut.getResolvingTask(); if (isObserved(resolvingTask)) { if (!waitingFutures.contains(fut) || resolvedFutures.contains(fut)) return; resolvedFutures.add(fut); writeOut(getActorName(resolvingTask.getTarget())); writeOut("[" + "Task" + resolvingTask.getID() + "]"); writeOut(":>"); writeOut(getActorName(task.getTarget())); writeOut("[" + "Taskx" + resolvingTask.getID() + "]"); writeOut(".resolved\\: "); writeOut(shorten(String.valueOf(fut.getValue()))); writeOutLn(); writeOut(getActorName(resolvingTask.getTarget())); writeOut("[" + "Task" + resolvingTask.getID() + "]"); writeOut(":stop"); writeOutLn(); writeOut(getActorName(task.getTarget())); writeOut("[" + "Taskx" + resolvingTask.getID() + "]"); writeOut(":stop"); writeOutLn(); writeOutLn("(" + fut.getID() + ") " + getActorName(task.getTarget())); } } } @Override public void taskBlockedOnFuture(TaskView task, FutView fut) { // never invoked } public static final List<String> EMPTY_STRING_LIST = new ArrayList<String>(0); Collection<String> getObservedClasses() { return createdCOGClasses.values(); } List<String> getSystemClasses() { return EMPTY_STRING_LIST; } List<String> getEnvironmentClasses() { return EMPTY_STRING_LIST; } boolean isEnvironmentClass(String s) { if (s.equals("ENVIRONMENT")) return true; return getEnvironmentClasses().contains(s); } @Override public void taskStep(TaskView task, String fileName, int line) { // TODO Auto-generated method stub } @Override public void taskReady(TaskView view) { // TODO Auto-generated method stub } @Override public void taskDeadlocked(TaskView task) { // TODO Auto-generated method stub } @Override public void stackFrameCreated(TaskView task, TaskStackFrameView stackFrame) { // TODO Auto-generated method stub } @Override public void localVariableChanged(TaskStackFrameView stackFrame, String name, ABSValue v) { // TODO Auto-generated method stub } @Override public void systemFinished() { worker.stopMe(); } private static class Worker extends Thread { private PrintWriter out; volatile private boolean stopped = false; private final ArrayBlockingQueue<String> buffer = new ArrayBlockingQueue<String>(10000000,true); private Worker() { super("SDEdit Communication Thread"); super.setDaemon(true); } @Override public void run() { connect(); while(!stopped || !buffer.isEmpty()) { try { String msg = buffer.take(); out.print(msg); out.flush(); } catch (InterruptedException e) { // terminate after buffer is empty } } } void stopMe() { stopped = true; interrupt(); } private void connect() { try { long startms= System.currentTimeMillis(); while (true) { try { Socket sk = new Socket("localhost", 60001); out = new PrintWriter(sk.getOutputStream()); return; } catch (java.net.SocketException e) { try { System.out.println("Waiting for sdedit server on localhost:60001 ... "+((System.currentTimeMillis()-startms) / 1000)+"s"); Thread.sleep(500); } catch (InterruptedException e2) { e2.printStackTrace(); } } } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void write(String s) { boolean f = false; while(!f) { f = buffer.offer(s); } } } @Override public void systemError(ABSException e) { // do nothing } @Override public void stackFrameRemoved(TaskView task, TaskStackFrameView oldFrame) { // TODO Auto-generated method stub } }