Package debugger

Source Code of debugger.ManagedThreadPane

package debugger;


import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.MatteBorder;
import observer.PseucoObservable;
import observer.PseucoObserver;
import resources.ResourceLoader;
import debugger.debuggerComponents.StackTraceFrame;
import debugger.debuggerComponents.ThreadActionStatus;
import debugger.debuggerComponents.ThreadStatus;
import debugger.debuggerComponents.ThreadStatus.ThreadState;


/**
* Stores informations and methods about a specific agent (thread) and
* displays them on a frame.
*
* Warning: It is not guaranteed that this component will have a parent (or ever had one).
* @author Markus
*
*/
public class ManagedThreadPane extends JPanel implements PseucoObservable {
  private static final long serialVersionUID = 1L;

  private static final int height  = 30;
  private static final int height2 = 30;
  private static boolean lineMapperInited= false;
  private static HashMap<String, HashMap<Integer, HashSet<Integer>>> lineNumberMapping;

  private Set<PseucoObserver> observers= new HashSet<PseucoObserver>();
 
  private Thread thread;
  private Thread originThread;
  private DebuggerHandler dbhandler;
  private ManagedThreadPane mtp = this;
  private boolean expanded = false;
 
  private JPanel line1, line2;
 
  private ThreadStatus status;
  private JLabel threadname;
  private JLabel startedBy;
 
  private JButton btnResume;
  private JButton btnSuspend;
  private JButton btnStop;
  private JButton btnSingleStep;
 
  private final Dimension spacer = new Dimension(10,0);
  private final Color odd_color = new Color(0xfff0ffff);
 
  /**
   * Create a new ManagedThreadPane if a new thread starts.
   * @param thread
   */
  public ManagedThreadPane(final DebuggerHandler dbhandler, final Thread thread, final Thread starter) {
    this.thread = thread;
    this.dbhandler = dbhandler;
    setPreferredSize(new Dimension(0,height));
    setMaximumSize(new Dimension(Short.MAX_VALUE, height));
    setBorder(new MatteBorder(0, 0, 1, 0, new Color(0xffdddddd)));
    setBackground(Color.WHITE);
    setOpaque(true);
   
    line1 = new JPanel();
    line2 = new JPanel();
    line2.setVisible(expanded);
   
    makeLine1(starter);
    makeLine2();
 
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    add(line1);
    add(line2);
    add(Box.createVerticalGlue());
   
    //Init
    setThreadState(ThreadState.RUNNING);
    hideSuspendedInfos();
    refreshCommandCount();
    addMouseListener(new MouseAdapter(){
      @Override
      public void mouseReleased(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON1){
          dbhandler.expand(mtp);
        }
      }
    });
  }
 
 
  //private JLabel lblInternalState;
  private JLabel lblState;
  private JLabel lblCommandCount;
  private ThreadActionStatus taStatus;
  private JButton btnStackTrace;
 
  private void makeLine2() {
    line2.setLayout(new BoxLayout(line2, BoxLayout.X_AXIS));
    line2.setPreferredSize(new Dimension(0,height2));
    line2.setMaximumSize(new Dimension(Short.MAX_VALUE, height2));
    line2.setOpaque(false);
   
    line2.add(Box.createRigidArea(spacer));
   
    //State
    lblState = new JLabel();
    lblState.setText("State: ");
    lblState.setToolTipText("The state of an agent can only be determined, if the agent is suspended. ");
    line2.add(lblState);
    taStatus = new ThreadActionStatus(thread);
    line2.add(taStatus);
   
    line2.add(Box.createHorizontalGlue());
   
    lblCommandCount = new JLabel();
    lblCommandCount.setToolTipText("<html>The number of commands this agent has already executed. <br>" +
        "More technically, this is the count of <code>Simulate.HWInterrupt()</code>, <br>" +
        "a statement that the PseuCo-Compiler places after every single command. <br>" +
        "That means for example: <code>while(true){}</code> does not execute a single command, <br>" +
        "but <code>while(true){ i=i; }</code> does. <br>" +
        "ATTENTION: The compiler might place more than one call after a single command, so <br>" +
        "don't see this as an absolute value.");
    line2.add(lblCommandCount);
   
    line2.add(Box.createHorizontalGlue());
   
    /*lblInternalState = new JLabel();
    line2.add(lblInternalState);*/
    btnStackTrace = new JButton(actionStackTrace);
    line2.add(btnStackTrace);
   
    line2.add(Box.createRigidArea(spacer));
  }

  private void makeLine1(Thread starter) {
    line1.setLayout(new BoxLayout(line1, BoxLayout.X_AXIS));
    line1.setPreferredSize(new Dimension(0,height));
    line1.setMaximumSize(new Dimension(Short.MAX_VALUE, height));
    line1.setOpaque(false);
    line1.add(Box.createRigidArea(spacer));
   
    //Status
    status = new ThreadStatus(thread);
    line1.add(status);
   
    line1.add(Box.createRigidArea(spacer));
   
    //Name
    threadname = new JLabel(thread.getName());
    line1.add(threadname);
   
    line1.add(Box.createRigidArea(spacer));
   
    //started by
    startedBy = new JLabel();
    line1.add(startedBy);
    setOriginThread(starter);
   
    line1.add(Box.createHorizontalGlue());
   
    //Buttons
    btnSingleStep = makeButton(actionSingleStep);
    btnResume  = makeButton(actionResume);
    btnSuspend = makeButton(actionSuspend);
    btnStop    = makeButton(actionStop);
    line1.add(btnSingleStep);
    line1.add(btnResume);
    line1.add(btnSuspend);
    line1.add(btnStop);
   
    line1.add(Box.createRigidArea(spacer));
  }

  private JButton makeButton(Action a){
    JButton result = new JButton(a);
    result.setPreferredSize(new Dimension(28,28));
    result.setSize(result.getPreferredSize());
    result.setMaximumSize(result.getSize());
    result.setIconTextGap(0);
    return result;
  }
 
 
 
  private static final Icon iconResume     = ResourceLoader.loadIcon("resumebtn.png");
  private static final Icon iconSuspend    = ResourceLoader.loadIcon("suspendbtn.png");
  private static final Icon iconStop       = ResourceLoader.loadIcon("stop_small.png");
  private static final Icon iconStacktrace = ResourceLoader.loadIcon("stackframe.gif");
  private static final Icon iconSingleStep = ResourceLoader.loadIcon("stepover.gif");
 
  /**
   * Resumes the thread
   */
  private Action actionResume = new ActionResume("", iconResume);
  private class ActionResume extends AbstractAction{
    private static final long serialVersionUID = 1L;
    public ActionResume(String string, Icon icon) {
      super(string, icon);
      putValue(SHORT_DESCRIPTION, "Resumes this agent");
    }
    @Override
    public void actionPerformed(ActionEvent e) {
      dbhandler.resumeThreadFromEDT(mtp);
    }
  };
 
  /**
   * Suspends the thread
   */
  private Action actionSuspend = new ActionSuspend("", iconSuspend);
  private class ActionSuspend extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public ActionSuspend(String string, Icon icon) {
      super(string, icon);
      putValue(SHORT_DESCRIPTION, "Suspends this agent");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
      dbhandler.suspendThreadFromEDT(mtp);
    }
  };

  /**
   * Stops the thread
   */
  private Action actionStop = new ActionStop("", iconStop);
  private class ActionStop extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public ActionStop(String string, Icon icon) {
      super(string, icon);
      putValue(SHORT_DESCRIPTION, "Terminates this agent");
    }

    @Override
    public void actionPerformed(ActionEvent e) {
      dbhandler.stopThreadFromEDT(mtp);
    }
  };

  private Action actionStackTrace = new ActionStacktrace("Java-Stacktrace",iconStacktrace);
  private class ActionStacktrace extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public ActionStacktrace(String string, Icon icon) {
      super(string, icon);
      putValue(SHORT_DESCRIPTION, "Shows a stacktrace of this thread. The stacktrace corresponds to the java version. ");
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
      new StackTraceFrame(thread.getStackTrace(), thread);
    }
  };

  private Action actionSingleStep = new ActionSingleStep("", iconSingleStep);
  private class ActionSingleStep extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public ActionSingleStep(String string, Icon icon) {
      super(string, icon);
      putValue(SHORT_DESCRIPTION, "<html>Does a <b>single step</b> with this agent and suspends it afterwards. <br>" +
        "More technically, this allows the agent to run to the next call of <code>Simulate.HWInterrupt()</code>, <br>" +
        "a statement that the PseuCo-Compiler places after every single command. <br>" +
        "That means for example: <code>while(true){}</code> does not execute such a command and will not <br>" +
        "be suspended, but <code>while(true){ i=i; }</code> does, so you can do a single step in this loop. <br>" +
        "<b>Attention</b>: The compiler might place more than one call after a single command, so <br>" +
        "you might have to do multiple steps until a single PseuCo command is executed.");
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
      setSingleStep(true);
      if (status.getState() == ThreadState.SUSPENDED)
        dbhandler.resumeThreadFromEDT(mtp);
    }
  };
 
 
 
  /**
   * Will be called when the thread stops
   */
  public void threadStopped(){
    setThreadState(ThreadState.TERMINATED);
    btnStackTrace.setEnabled(false);
    showSuspendedInfos();
    refreshCommandCount();
    notifyThreadStopping();
  }
 
  /**
   * Will be called when the thread gets suspended
   */
  public void threadSuspended(){
    setThreadState(ThreadState.SUSPENDED);
    showSuspendedInfos();
    refreshCommandCount();
    notifyCodeLineChanges();
  }
 
  /**
   * Will be called when the thread gets resumed
   */
  public void threadResumed(){
    setThreadState(ThreadState.RUNNING);
    hideSuspendedInfos();
  }
 
  public ThreadState getThreadState(){
    return status.getState();
  }
 
  public void setThreadState(ThreadState s){
    status.setState(s);
    btnSingleStep.setEnabled(s == ThreadState.SUSPENDED || s == ThreadState.RUNNING);
    btnResume.setEnabled(s == ThreadState.SUSPENDED);
    btnSuspend.setEnabled(s == ThreadState.RUNNING);
    btnStop.setEnabled(s == ThreadState.SUSPENDED || s == ThreadState.RUNNING);
  }
 
 
  public Thread getThread(){
    return thread;
  }
 
  public void removeSelf(){
    Container c = getParent();
    if (c != null){
      c.remove(this);
      c.validate();
      validate();
      c.repaint(50L);
    }
  }

  public Thread getOriginThread() {
    return originThread;
  }

  public void setOriginThread(Thread originThread) {
    this.originThread = originThread;
    startedBy.setText("(started by "+this.originThread.getName()+")");
  }

 
 
  //private Thread.State internalState = Thread.State.NEW;
  /**
   * Warning: not called in EDT. Do not do any gui operation!
   * @param state
   */
  /*public void setThreadInternalState(State state) {
    internalState = state;   
  }*/
 
 
  private void showSuspendedInfos(){
    /*lblInternalState.setText("Status: "+internalState.toString());
    String hint ="";
    switch (internalState) {
    case NEW:        hint="A new, not startet agent, ready to run. "; break;
    case RUNNABLE:   hint="The agent is running, not blocked or waiting. "; break;
    case BLOCKED:    hint="The agent is blocked, because he waits for a lock or a monitor. "; break;
    case TERMINATED: hint="The agent has terminated. "; break;
    case WAITING:    hint="The agent is waiting for another agent to perform an action. " +
        "Examples are waiting for a signal or for another agent's termination (join). "; break;
    case TIMED_WAITING: hint="The agent is waiting for something, but has a timeout (sleep, join with timeout, ...). "; break;
    default:
      break;
    }
    lblInternalState.setToolTipText(hint);*/
    //taStatus.setVisible(true);
    //lblState.setText("State: ");
    taStatus.determineState();
  }
 
 
  private void hideSuspendedInfos(){
    /*lblInternalState.setText("Status: ?");
    lblInternalState.setToolTipText("The status can only be monitored if the agent is suspended. ");*/
    //taStatus.setVisible(false);
    //lblState.setText("State: -?-");
  }
 
 
 
  public void expand(){
    expanded = true;
    setPreferredSize(new Dimension(0,height+height2));
    setMaximumSize(new Dimension(Short.MAX_VALUE, height+height2));
    line2.setVisible(expanded);
    invalidate();
  }
 
  public void collapse(){
    expanded = false;
    setPreferredSize(new Dimension(0,height));
    setMaximumSize(new Dimension(Short.MAX_VALUE, height));
    line2.setVisible(expanded);
    invalidate();
  }

  public void refreshState() {
    taStatus.determineState();
  }

  public void setLastThreadJoined(Thread joined){
    taStatus.setLastThreadJoined(joined);
  }
 
 
  private int commandCount = 0;
  private ReentrantLock lockCommandCount = new ReentrantLock();
  public void incCommandCount(){
    lockCommandCount.lock();
    try{
      commandCount++;
    }finallylockCommandCount.unlock()}
  }
 
  public int getCommandCount(){
    int res;
    lockCommandCount.lock();
    try{
      res = commandCount;
    }finallylockCommandCount.unlock()}
    return res;
  }
 
  private void notifyThreadStopping() {
    notifyObservers(null, -1);
  }

  @SuppressWarnings("unchecked")
  private void notifyCodeLineChanges() {
    Runnable runningCode= null;
    try {
      Field fTarget = Thread.class.getDeclaredField("target");
          fTarget.setAccessible(true);
          runningCode = (Runnable) fTarget.get(thread);
    } catch (NoSuchFieldException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }
   
    if (runningCode == null)
      runningCode= thread;
   
    Class<?> settingsClass;
    try {
      settingsClass = Class.forName("include.Settings");
      Method[] methods= settingsClass.getDeclaredMethods();
      Method lineMethod= null;
      Method javaFilesMethod= null;
      Method initMaps= null;
      for (Method mthd : methods) {
        if (mthd.getName().equals("getPseuCoLineNumber"))
          lineMethod= mthd;
        if (mthd.getName().equals("getListOfExternJavaFiles"))
          javaFilesMethod= mthd;
        if (mthd.getName().equals("initLineNumberMapping"))
          initMaps= mthd;       
      }
     
      if (lineMethod == null || javaFilesMethod == null || initMaps == null)
        throw new NoSuchMethodException();
     
      if (!lineMapperInited) {
        initMaps.invoke(null);
        lineNumberMapping = (HashMap<String, HashMap<Integer, HashSet<Integer>>>)
                    Class.forName("include.Settings").getDeclaredField("lineNumberMapping").get(null);
        lineMapperInited= true;
      }

      StackTraceElement[] stacktrace = thread.getStackTrace();
      // two steps, first look for the HWInterrupt call, then backtrack to the enclosing method (and possibly
      // backtrack lines until finding a relevant one)
      int simulatePos= -1;
      for (int i = 0; i < stacktrace.length; i++) {
        String javaClass = stacktrace[i].getClassName();
        String javaMethod= stacktrace[i].getMethodName();
        if (javaClass.equals("include.Simulate") && javaMethod.equals("HWInterrupt")) {
          // possible code line change
          simulatePos= i;
          break;
        }
      }

      Set<Integer> pseucoLines= null;
      String javaClass= "";
      if (simulatePos > 0) {
        javaClass= stacktrace[simulatePos+1].getClassName();
        int line= stacktrace[simulatePos+1].getLineNumber();
        String[] tokenized= javaClass.split("\\.");
        javaClass= tokenized[tokenized.length-1];
        HashMap<Integer, HashSet<Integer>> mappingForClass = lineNumberMapping.get(javaClass);
        if (mappingForClass != null) {
          while (line > 0) {
            if (mappingForClass.containsKey(line)) {
              pseucoLines= mappingForClass.get(line);
              if (pseucoLines.size() > 0)
                break;
            }
            line--;
          }
        }
      }

      if (pseucoLines != null && pseucoLines.size() > 0) {
        List<Integer> list= new ArrayList<Integer>(pseucoLines);
        Integer[] lineArray= list.toArray(new Integer[0]);
        Arrays.sort(lineArray);
        int currentLine= lineArray[lineArray.length - 1];
        notifyObservers(javaClass + ".java", currentLine);
      }
    } catch (ClassNotFoundException e) {
      //
    } catch (NoSuchMethodException e) {
      //
    } catch (IllegalAccessException e) {
      //e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.getCause().printStackTrace();
    } catch (NoSuchFieldException e) {
      e.printStackTrace();
    } catch (SecurityException e) {
      e.printStackTrace();
    }
  }
 
  public void refreshCommandCount() {
    lblCommandCount.setText("#Commands = "+getCommandCount());
  }
 
  private boolean doSingleStep = false;
  public boolean singleStep(){
    boolean res = doSingleStep;
    if (doSingleStep){
      if (SwingUtilities.isEventDispatchThread()) setSingleStep(false);
      else SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
          setSingleStep(false);         
        }
      });
    }
    return res;
  }
 
  public void setSingleStep(boolean s){
    if (s == doSingleStep) return;
    doSingleStep = s;
    btnSingleStep.setEnabled(! doSingleStep);
  }
 
 
  private boolean isOdd = false;
  public void setOdd(boolean odd){
    if (odd == isOdd) return;
    isOdd = odd;
    setBackground(isOdd ? odd_color : Color.WHITE);
    repaint(50L);
  }

  @Override
  public void registerObserver(PseucoObserver obs) {
    observers.add(obs);
  }

  @Override
  public void deregisterObserver(PseucoObserver obs) {
    observers.remove(obs);
  }

  @Override
  public void notifyObservers(Object ... args) {
    for (PseucoObserver obs : observers) {
      obs.update(this, args);
    }
  }
}
TOP

Related Classes of debugger.ManagedThreadPane

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.