reworked MspCodeWatcher plugin: using jsyntaxpane to display Contiki code, using tabs instead of splitpanes, easier to

configure watch-/breakpoints, lots of bug fixes and minor improvements
This commit is contained in:
Fredrik Osterlind 2012-03-21 16:58:26 +01:00
parent 2e583c733e
commit 7cfa8e28d3
5 changed files with 616 additions and 791 deletions

View File

@ -31,26 +31,32 @@
package se.sics.cooja.mspmote.plugins;
import java.awt.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JColorChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import org.apache.log4j.Logger;
import se.sics.cooja.GUI;
import se.sics.cooja.Mote;
import se.sics.cooja.MoteType;
import se.sics.cooja.Simulation;
import se.sics.cooja.mspmote.MspMote;
import se.sics.cooja.Watchpoint;
import se.sics.cooja.WatchpointMote;
/**
* Displays a set of breakpoints.
@ -60,27 +66,27 @@ import se.sics.cooja.mspmote.MspMote;
public class BreakpointsUI extends JPanel {
private static Logger logger = Logger.getLogger(BreakpointsUI.class);
private static final int COLUMN_INFO = 0;
private static final int COLUMN_ADDRESS = 1;
private static final int COLUMN_FILELINE = 2;
private static final int COLUMN_ADDRESS = 0;
private static final int COLUMN_FILELINE = 1;
private static final int COLUMN_INFO = 2;
private static final int COLUMN_STOP = 3;
private static final int COLUMN_REMOVE = 4;
private static final String[] COLUMN_NAMES = {
"Info",
"Address",
"File",
"Stop",
"Remove"
"Source",
"Info",
"Stops simulation"
};
private MspBreakpointContainer breakpoints = null;
private WatchpointMote mote;
private MspCodeWatcher codeWatcher;
private JTable table = null;
private MspBreakpoint popupBreakpoint = null;
private Watchpoint selectedWatchpoint = null;
public BreakpointsUI(MspBreakpointContainer breakpoints, final MspCodeWatcher codeWatcher) {
this.breakpoints = breakpoints;
public BreakpointsUI(WatchpointMote mote, final MspCodeWatcher codeWatcher) {
this.mote = mote;
this.codeWatcher = codeWatcher;
/* Breakpoints table */
table = new JTable(tableModel) {
@ -93,15 +99,20 @@ public class BreakpointsUI extends JPanel {
int realColumnIndex = table.convertColumnIndexToModel(colIndex);
if (realColumnIndex == COLUMN_FILELINE) {
MspBreakpoint[] allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints();
Watchpoint[] allBreakpoints = BreakpointsUI.this.mote.getBreakpoints();
if (rowIndex < 0 || rowIndex >= allBreakpoints.length) {
return null;
}
File file = allBreakpoints[rowIndex].getCodeFile();
Watchpoint watchpoint = allBreakpoints[rowIndex];
File file = watchpoint.getCodeFile();
if (file == null) {
return null;
return String.format("[unknown @ 0x%04x]", watchpoint.getExecutableAddress());
}
return file.getPath() + ":" + allBreakpoints[rowIndex].getLineNumber();
Integer line = watchpoint.getLineNumber();
if (line == null) {
return file.getPath() + ":?";
}
return file.getPath() + ":" + line;
}
if (realColumnIndex == COLUMN_INFO) {
@ -111,53 +122,41 @@ public class BreakpointsUI extends JPanel {
if (realColumnIndex == COLUMN_STOP) {
return "Indicates whether the watchpoint will stop the simulation when triggered";
}
if (realColumnIndex == COLUMN_REMOVE) {
return "Remove breakpoint from this mote only. (Right-click for more options)";
}
return null;
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumnModel().getColumn(COLUMN_ADDRESS).setPreferredWidth(60); /* XXX */
table.getColumnModel().getColumn(COLUMN_ADDRESS).setMaxWidth(60);
table.getColumnModel().getColumn(COLUMN_INFO).setPreferredWidth(60);
table.getColumnModel().getColumn(COLUMN_INFO).setMaxWidth(60);
table.getColumnModel().getColumn(COLUMN_INFO).setCellRenderer(
new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
if (column != COLUMN_INFO) {
return c;
}
MspBreakpoint[] allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints();
if (row < 0 || row >= allBreakpoints.length) {
return c;
}
MspBreakpoint breakpoint = allBreakpoints[row];
if (breakpoint.getColor() == null) {
return c;
}
/* Use watchpoint color */
c.setForeground(breakpoint.getColor());
return c;
}
});
table.getColumnModel().getColumn(COLUMN_STOP).setPreferredWidth(60);
table.getColumnModel().getColumn(COLUMN_STOP).setMaxWidth(60);
table.getColumnModel().getColumn(COLUMN_REMOVE).setPreferredWidth(60);
table.getColumnModel().getColumn(COLUMN_REMOVE).setMaxWidth(60);
table.getColumnModel().getColumn(COLUMN_INFO).setCellRenderer(new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
if (column != COLUMN_INFO) {
return c;
}
Watchpoint[] allBreakpoints = BreakpointsUI.this.mote.getBreakpoints();
if (row < 0 || row >= allBreakpoints.length) {
return c;
}
Watchpoint breakpoint = allBreakpoints[row];
if (breakpoint.getColor() == null) {
return c;
}
/* Use watchpoint color */
c.setBackground(Color.WHITE);
c.setForeground(breakpoint.getColor());
return c;
}
});
/* Popup menu: register on all motes */
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem(addToMoteTypeAction));
popupMenu.add(new JMenuItem(delFromMoteTypeAction));
popupMenu.add(new JMenuItem(gotoCodeAction));
popupMenu.add(new JSeparator());
popupMenu.add(new JMenuItem(removeWatchpointAction));
popupMenu.add(new JMenuItem(configureWatchpointAction));
/* Show source file on breakpoint mouse click */
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
java.awt.Point p = e.getPoint();
@ -167,63 +166,32 @@ public class BreakpointsUI extends JPanel {
if (realColumnIndex != COLUMN_ADDRESS
&& realColumnIndex != COLUMN_FILELINE
&& realColumnIndex != COLUMN_REMOVE
&& realColumnIndex != COLUMN_INFO) {
return;
}
MspBreakpoint[] allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints();
Watchpoint[] allBreakpoints = BreakpointsUI.this.mote.getBreakpoints();
if (rowIndex < 0 || rowIndex >= allBreakpoints.length) {
return;
}
MspBreakpoint breakpoint = allBreakpoints[rowIndex];
Watchpoint breakpoint = allBreakpoints[rowIndex];
if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e)) {
popupBreakpoint = breakpoint;
selectedWatchpoint = breakpoint;
popupMenu.show(table, e.getX(), e.getY());
return;
}
if (realColumnIndex == COLUMN_INFO) {
String msg = JOptionPane.showInputDialog(
GUI.getTopParentContainer(),
"Enter description",
"Watchpoint Description",
JOptionPane.QUESTION_MESSAGE);
if (msg != null) {
breakpoint.setUserMessage(msg);
}
Color newColor = JColorChooser.showDialog(
GUI.getTopParentContainer(),
"Watchpoint Color",
breakpoint.getColor());
if (newColor != null) {
breakpoint.setColor(newColor);
}
configureWatchpointInfo(breakpoint);
return;
}
File file = allBreakpoints[rowIndex].getCodeFile();
/*File file = allBreakpoints[rowIndex].getCodeFile();
int line = allBreakpoints[rowIndex].getLineNumber();
if (file == null) {
return;
}
/* Display source code */
codeWatcher.displaySourceFile(file, line);
}
});
/* Update when breakpoints are triggered/added/removed */
breakpoints.addWatchpointListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MspBreakpoint triggered = BreakpointsUI.this.breakpoints.getLastWatchpoint();
if (triggered == null) {
table.repaint();
return;
}
flashBreakpoint(triggered);
}*/
}
});
@ -232,24 +200,41 @@ public class BreakpointsUI extends JPanel {
add(BorderLayout.CENTER, table);
}
private void flashBreakpoint(MspBreakpoint breakpoint) {
/* Locate breakpoints table index */
int index = -1;
MspBreakpoint[] all = breakpoints.getBreakpoints();
for (int i=0; i < breakpoints.getBreakpointsCount(); i++) {
if (breakpoint == all[i]) {
index = i;
break;
}
}
if (index < 0) {
private void configureWatchpointInfo(Watchpoint breakpoint) {
String msg = (String) JOptionPane.showInputDialog(
GUI.getTopParentContainer(),
"Enter description;",
"Watchpoint description",
JOptionPane.QUESTION_MESSAGE, null, null, breakpoint.getUserMessage());
if (msg == null) {
return;
}
breakpoint.setUserMessage(msg);
Color newColor = JColorChooser.showDialog(
GUI.getTopParentContainer(),
"Watchpoint color",
breakpoint.getColor());
if (newColor == null) {
return;
}
breakpoint.setColor(newColor);
}
final int breakpointIndex = index;
public void selectBreakpoint(final Watchpoint breakpoint) {
if (breakpoint == null) {
return;
}
/* Locate breakpoints table index */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
table.setRowSelectionInterval(breakpointIndex, breakpointIndex);
Watchpoint[] watchpoints = mote.getBreakpoints();
for (int i=0; i < watchpoints.length; i++) {
if (breakpoint == watchpoints[i]) {
/* Select */
table.setRowSelectionInterval(i, i);
return;
}
}
}
});
}
@ -259,18 +244,18 @@ public class BreakpointsUI extends JPanel {
return COLUMN_NAMES[col].toString();
}
public int getRowCount() {
return breakpoints.getBreakpointsCount();
return mote.getBreakpoints().length;
}
public int getColumnCount() {
return COLUMN_NAMES.length;
}
public Object getValueAt(int row, int col) {
MspBreakpoint breakpoint = breakpoints.getBreakpoints()[row];
Watchpoint breakpoint = mote.getBreakpoints()[row];
/* Executable address in hexadecimal */
if (col == COLUMN_ADDRESS) {
Integer address = breakpoint.getExecutableAddress();
return "0x" + Integer.toHexString(address.intValue());
return String.format("0x%04x", address.intValue());
}
/* Source file + line number */
@ -300,7 +285,7 @@ public class BreakpointsUI extends JPanel {
return getColumnClass(col) == Boolean.class;
}
public void setValueAt(Object value, int row, int col) {
MspBreakpoint breakpoint = breakpoints.getBreakpoints()[row];
Watchpoint breakpoint = mote.getBreakpoints()[row];
if (col == COLUMN_STOP) {
/* Toggle stop state */
@ -308,109 +293,36 @@ public class BreakpointsUI extends JPanel {
fireTableCellUpdated(row, col);
return;
}
if (col == COLUMN_REMOVE) {
/* Remove breakpoint */
Integer address = breakpoint.getExecutableAddress();
breakpoints.removeBreakpoint(address);
fireTableCellUpdated(row, col);
return;
}
}
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
};
private Action addToMoteTypeAction = new AbstractAction("Register on all motes (mote type)") {
private Action gotoCodeAction = new AbstractAction("Show in source code") {
public void actionPerformed(ActionEvent e) {
if (popupBreakpoint == null) {
logger.fatal("No breakpoint to add");
if (selectedWatchpoint == null) {
return;
}
/* Extract all motes of the same mote type */
Simulation sim = popupBreakpoint.getMote().getSimulation();
MoteType type = popupBreakpoint.getMote().getType();
ArrayList<MspMote> motes = new ArrayList<MspMote>();
for (Mote m: sim.getMotes()) {
if (m.getType() == type) {
if (!(m instanceof MspMote)) {
logger.fatal("At least one mote was not a MSP mote: " + m);
return;
}
motes.add((MspMote)m);
}
}
/* Register breakpoints */
int reregistered = 0;
for (MspMote m: motes) {
/* Avoid duplicates (match executable addresses) */
MspBreakpointContainer container = m.getBreakpointsContainer();
MspBreakpoint[] breakpoints = container.getBreakpoints();
for (MspBreakpoint w: breakpoints) {
if (popupBreakpoint.getExecutableAddress().intValue() ==
w.getExecutableAddress().intValue()) {
logger.info("Reregistering breakpoint at mote: " + m);
container.removeBreakpoint(w.getExecutableAddress());
reregistered++;
}
}
MspBreakpoint newBreakpoint = container.addBreakpoint(
popupBreakpoint.getCodeFile(),
popupBreakpoint.getLineNumber(),
popupBreakpoint.getExecutableAddress());
newBreakpoint.setUserMessage(popupBreakpoint.getUserMessage());
newBreakpoint.setColor(popupBreakpoint.getColor());
newBreakpoint.setStopsSimulation(popupBreakpoint.stopsSimulation());
}
JOptionPane.showMessageDialog(GUI.getTopParentContainer(),
"Registered " + motes.size() + " breakpoints (" + reregistered + " re-registered)",
"Breakpoints added", JOptionPane.INFORMATION_MESSAGE);
codeWatcher.displaySourceFile(selectedWatchpoint.getCodeFile(), selectedWatchpoint.getLineNumber(), false);
}
};
private Action delFromMoteTypeAction = new AbstractAction("Delete from all motes (mote type)") {
private Action removeWatchpointAction = new AbstractAction("Remove watchpoint") {
public void actionPerformed(ActionEvent e) {
if (popupBreakpoint == null) {
logger.fatal("No breakpoint to delete");
if (selectedWatchpoint == null) {
return;
}
/* Extract all motes of the same mote type */
Simulation sim = popupBreakpoint.getMote().getSimulation();
MoteType type = popupBreakpoint.getMote().getType();
ArrayList<MspMote> motes = new ArrayList<MspMote>();
for (Mote m: sim.getMotes()) {
if (m.getType() == type) {
if (!(m instanceof MspMote)) {
logger.fatal("At least one mote was not a MSP mote: " + m);
return;
}
motes.add((MspMote)m);
}
mote.removeBreakpoint(selectedWatchpoint);
table.invalidate();
table.repaint();
}
};
private Action configureWatchpointAction = new AbstractAction("Configure watchpoint information") {
public void actionPerformed(ActionEvent e) {
if (selectedWatchpoint == null) {
return;
}
/* Delete breakpoints */
int deleted = 0;
for (MspMote m: motes) {
/* Avoid duplicates (match executable addresses) */
MspBreakpointContainer container = m.getBreakpointsContainer();
MspBreakpoint[] breakpoints = container.getBreakpoints();
for (MspBreakpoint w: breakpoints) {
if (popupBreakpoint.getExecutableAddress().intValue() ==
w.getExecutableAddress().intValue()) {
container.removeBreakpoint(w.getExecutableAddress());
deleted++;
}
}
}
JOptionPane.showMessageDialog(GUI.getTopParentContainer(),
"Deleted " + deleted + " breakpoints",
"Breakpoints deleted", JOptionPane.INFORMATION_MESSAGE);
configureWatchpointInfo(selectedWatchpoint);
}
};
}

View File

@ -34,32 +34,33 @@ package se.sics.cooja.mspmote.plugins;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
import java.util.HashMap;
import javax.swing.AbstractListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JEditorPane;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.ListCellRenderer;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Highlighter;
import javax.swing.text.Highlighter.HighlightPainter;
import jsyntaxpane.DefaultSyntaxKit;
import jsyntaxpane.components.Markers.SimpleMarker;
import org.apache.log4j.Logger;
import se.sics.mspsim.extutil.highlight.CScanner;
import se.sics.mspsim.extutil.highlight.Token;
import se.sics.mspsim.extutil.highlight.TokenTypes;
import se.sics.cooja.Watchpoint;
import se.sics.cooja.WatchpointMote;
import se.sics.cooja.util.JSyntaxAddBreakpoint;
import se.sics.cooja.util.JSyntaxRemoveBreakpoint;
import se.sics.cooja.util.StringUtils;
/**
* Displays source code and allows a user to add and remove breakpoints.
@ -69,184 +70,231 @@ import se.sics.mspsim.extutil.highlight.TokenTypes;
public class CodeUI extends JPanel {
private static Logger logger = Logger.getLogger(CodeUI.class);
private JPanel panel = null;
private JList codeList = null;
{
DefaultSyntaxKit.initKit();
}
private MspBreakpointContainer breakpoints = null;
private JEditorPane codeEditor = null;
private HashMap<Integer, Integer> codeEditorLines = null;
protected File displayedFile = null;
private Token tokensArray[][] = null;
private int tokensStartPos[] = null;
private static final HighlightPainter CURRENT_LINE_MARKER = new SimpleMarker(Color.ORANGE);
private static final HighlightPainter SELECTED_LINE_MARKER = new SimpleMarker(Color.GREEN);
private static final HighlightPainter BREAKPOINTS_MARKER = new SimpleMarker(Color.LIGHT_GRAY);
private final Object currentLineTag;
private final Object selectedLineTag;
private final ArrayList<Object> breakpointsLineTags = new ArrayList<Object>();
/**
* @param breakpoints Breakpoints
*/
public CodeUI(MspBreakpointContainer breakpoints) {
this.breakpoints = breakpoints;
private JSyntaxAddBreakpoint actionAddBreakpoint = null;
private JSyntaxRemoveBreakpoint actionRemoveBreakpoint = null;
private WatchpointMote mote;
public CodeUI(WatchpointMote mote) {
this.mote = mote;
{
/* Workaround to configure jsyntaxpane */
JEditorPane e = new JEditorPane();
new JScrollPane(e);
e.setContentType("text/c");
DefaultSyntaxKit kit = (DefaultSyntaxKit) e.getEditorKit();
kit.setProperty("Action.addbreakpoint", JSyntaxAddBreakpoint.class.getName());
kit.setProperty("Action.removebreakpoint", JSyntaxRemoveBreakpoint.class.getName());
kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,addbreakpoint,removebreakpoint");
}
setLayout(new BorderLayout());
codeEditor = new JEditorPane();
add(new JScrollPane(codeEditor), BorderLayout.CENTER);
doLayout();
panel = new JPanel(new BorderLayout());
add(panel, BorderLayout.CENTER);
displayNoCode();
codeEditorLines = new HashMap<Integer, Integer>();
codeEditor.setContentType("text/c");
DefaultSyntaxKit kit = (DefaultSyntaxKit) codeEditor.getEditorKit();
kit.setProperty("Action.addbreakpoint", JSyntaxAddBreakpoint.class.getName());
kit.setProperty("Action.removebreakpoint", JSyntaxRemoveBreakpoint.class.getName());
kit.setProperty("PopupMenu", "copy-to-clipboard,-,find,find-next,goto-line,-,addbreakpoint,removebreakpoint");
breakpoints.addWatchpointListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* Only update code list if simulation is not running */
if (CodeUI.this.breakpoints.getMote().getSimulation().isRunning() ||
CodeUI.this.breakpoints.getLastWatchpoint() != null) {
JPopupMenu p = codeEditor.getComponentPopupMenu();
for (Component c: p.getComponents()) {
if (c instanceof JMenuItem) {
if (((JMenuItem) c).getAction() != null &&
((JMenuItem) c).getAction() instanceof JSyntaxAddBreakpoint) {
actionAddBreakpoint = (JSyntaxAddBreakpoint)(((JMenuItem) c).getAction());
actionAddBreakpoint.setMenuText("Add breakpoint");
}
if (((JMenuItem) c).getAction() != null &&
((JMenuItem) c).getAction() instanceof JSyntaxRemoveBreakpoint) {
actionRemoveBreakpoint = (JSyntaxRemoveBreakpoint)(((JMenuItem) c).getAction());
actionRemoveBreakpoint.setMenuText("Remove breakpoint");
}
}
}
codeEditor.setText("");
codeEditorLines.clear();
codeEditor.setEditable(false);
Highlighter hl = codeEditor.getHighlighter();
Object o = null;
try {
o = hl.addHighlight(0, 0, CURRENT_LINE_MARKER);
} catch (BadLocationException e1) {
}
currentLineTag = o;
o = null;
try {
o = hl.addHighlight(0, 0, SELECTED_LINE_MARKER);
} catch (BadLocationException e1) {
}
selectedLineTag = o;
codeEditor.getComponentPopupMenu().addPopupMenuListener(new PopupMenuListener() {
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
/* Disable breakpoint actions */
actionAddBreakpoint.setEnabled(false);
actionRemoveBreakpoint.setEnabled(false);
int line = getCodeEditorMouseLine();
if (line < 1) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (codeList != null) {
codeList.updateUI();
}
}
});
/* Configure breakpoint menu options */
Integer address =
CodeUI.this.mote.getExecutableAddressOf(displayedFile, line);
if (address == null) {
return;
}
final int start = codeEditorLines.get(line);
int end = codeEditorLines.get(line+1);
Highlighter hl = codeEditor.getHighlighter();
try {
hl.changeHighlight(selectedLineTag, start, end);
} catch (BadLocationException e1) {
}
boolean hasBreakpoint =
CodeUI.this.mote.breakpointExists(address);
if (!hasBreakpoint) {
actionAddBreakpoint.setEnabled(true);
actionAddBreakpoint.putValue("WatchpointMote", CodeUI.this.mote);
actionAddBreakpoint.putValue("WatchpointFile", displayedFile);
actionAddBreakpoint.putValue("WatchpointLine", new Integer(line));
actionAddBreakpoint.putValue("WatchpointAddress", new Integer(address));
} else {
actionRemoveBreakpoint.setEnabled(true);
actionRemoveBreakpoint.putValue("WatchpointMote", CodeUI.this.mote);
actionRemoveBreakpoint.putValue("WatchpointFile", displayedFile);
actionRemoveBreakpoint.putValue("WatchpointLine", new Integer(line));
actionRemoveBreakpoint.putValue("WatchpointAddress", new Integer(address));
}
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
Highlighter hl = codeEditor.getHighlighter();
try {
hl.changeHighlight(selectedLineTag, 0, 0);
} catch (BadLocationException e1) {
}
}
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
displayNoCode(true);
}
public void updateBreakpoints() {
Highlighter hl = codeEditor.getHighlighter();
for (Object breakpointsLineTag: breakpointsLineTags) {
hl.removeHighlight(breakpointsLineTag);
}
breakpointsLineTags.clear();
for (Watchpoint w: mote.getBreakpoints()) {
if (!w.getCodeFile().equals(displayedFile)) {
continue;
}
final int start = codeEditorLines.get(w.getLineNumber());
int end = codeEditorLines.get(w.getLineNumber()+1);
try {
breakpointsLineTags.add(hl.addHighlight(start, end, BREAKPOINTS_MARKER));
} catch (BadLocationException e1) {
}
}
}
private int getCodeEditorMouseLine() {
if (codeEditorLines == null) {
return -1;
}
Point mousePos = codeEditor.getMousePosition();
if (mousePos == null) {
return -1;
}
int modelPos = codeEditor.viewToModel(mousePos);
int line = 1;
while (codeEditorLines.containsKey(line+1)) {
int next = codeEditorLines.get(line+1);
if (modelPos < next) {
return line;
}
line++;
}
return -1;
}
/**
* Remove any shown source code.
*/
public void displayNoCode() {
// Display "no code" message
public void displayNoCode(final boolean markCurrent) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.removeAll();
panel.repaint();
displayedFile = null;
codeEditor.setText(null);
codeEditorLines.clear();
displayLine(-1, markCurrent);
}
});
displayedFile = null;
return;
}
private void createTokens(String[] codeData) {
/* Merge code lines */
StringBuilder sb = new StringBuilder();
for (String line: codeData) {
sb.append(line);
sb.append('\n');
}
String code = sb.toString();
/* Scan code */
CScanner cScanner = new CScanner();
cScanner.change(0, 0, code.length());
int nrTokens;
nrTokens = cScanner.scan(code.toCharArray(), 0, code.length());
/* Extract tokens */
ArrayList<Token> codeTokensVector = new ArrayList<Token>();
for (int i=0; i < nrTokens; i++) {
Token token = cScanner.getToken(i);
codeTokensVector.add(token);
}
/* Create new line token array */
Token newTokensArray[][] = new Token[codeData.length][];
int[] newTokensStartPos = new int[codeData.length];
int lineStart=0, lineEnd=-1;
Iterator<Token> tokens = codeTokensVector.iterator();
Token currentToken = tokens.next();
for (int i=0; i < newTokensArray.length; i++) {
lineStart = lineEnd + 1;
lineEnd = lineStart + codeData[i].length();
newTokensStartPos[i] = lineStart;;
/* Advance tokens until correct line */
while (currentToken.position + currentToken.symbol.name.length() < lineStart) {
if (!tokens.hasNext()) {
break;
}
currentToken = tokens.next();
}
/* Advance tokens until last token on line */
Vector<Token> lineTokens = new Vector<Token>();
while (currentToken.position < lineEnd) {
lineTokens.add(currentToken);
if (!tokens.hasNext()) {
break;
}
currentToken = tokens.next();
}
if (currentToken == null) {
break;
}
/* Store line tokens */
Token[] lineTokensArray = new Token[lineTokens.size()];
for (int j=0; j < lineTokens.size(); j++) {
lineTokensArray[j] = lineTokens.get(j);
}
newTokensArray[i] = lineTokensArray;
}
/* Start using tokens array */
tokensArray = newTokensArray;
tokensStartPos = newTokensStartPos;
}
/**
* Display given source code and mark given line.
*
* @param codeFile Source code file
* @param codeData Source code
* @param lineNr Line numer
*/
public void displayNewCode(File codeFile, String[] codeData, final int lineNr) {
displayedFile = codeFile;
public void displayNewCode(final File codeFile, final int lineNr, final boolean markCurrent) {
if (!codeFile.equals(displayedFile)) {
/* Read from disk */
final String data = StringUtils.loadFromFile(codeFile);
if (data == null || data.length() == 0) {
displayNoCode(markCurrent);
return;
}
if (codeData == null || codeData.length == 0) {
displayNoCode();
return;
String[] lines = data.split("\n");
logger.info("Opening " + codeFile + " (" + lines.length + " lines)");
int length = 0;
codeEditorLines.clear();
for (int line=1; line-1 < lines.length; line++) {
codeEditorLines.put(line, length);
length += lines[line-1].length()+1;
}
codeEditor.setText(data.toString());
displayedFile = codeFile;
updateBreakpoints();
}
logger.info("Opening " + codeFile + " (" + codeData.length + " lines)");
/* Create new list */
final JList newList = new JList(new CodeListModel(codeData));
newList.setBackground(Color.WHITE);
newList.setFont(new Font("courier", 0, 12));
newList.setCellRenderer(new CodeCellRenderer(lineNr));
((CodeCellRenderer)newList.getCellRenderer()).setNice(false);
newList.setFixedCellHeight(12);
newList.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
handleMouseEvent(e);
}
public void mouseReleased(MouseEvent e) {
handleMouseEvent(e);
}
public void mouseEntered(MouseEvent e) {
handleMouseEvent(e);
}
public void mouseExited(MouseEvent e) {
handleMouseEvent(e);
}
public void mouseClicked(MouseEvent e) {
handleMouseEvent(e);
}
});
createTokens(codeData);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.removeAll();
codeList = newList;
panel.add(codeList);
panel.validate();
displayLine(lineNr);
displayLine(lineNr, markCurrent);
}
});
}
/**
@ -255,290 +303,35 @@ public class CodeUI extends JPanel {
*
* @param lineNumber Line number
*/
public void displayLine(int lineNumber) {
if (codeList == null) {
return;
}
((CodeCellRenderer) codeList.getCellRenderer()).setNice(false);
((CodeCellRenderer) codeList.getCellRenderer()).changeCurrentLine(lineNumber);
((CodeCellRenderer) codeList.getCellRenderer()).validate();
if (lineNumber > 0) {
int index = lineNumber - 1;
codeList.setSelectedIndex(index);
codeList.ensureIndexIsVisible(Math.max(0, index-3));
codeList.ensureIndexIsVisible(Math.min(index+3, codeList.getModel().getSize()));
codeList.ensureIndexIsVisible(index);
}
codeList.updateUI();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
((CodeCellRenderer) codeList.getCellRenderer()).setNice(true);
codeList.repaint();
}
});
}
private void handleMouseEvent(MouseEvent event) {
if (event.isPopupTrigger()) {
Point menuLocation = codeList.getPopupLocation(event);
if (menuLocation == null) {
menuLocation = new Point(
codeList.getLocationOnScreen().x + event.getX(),
codeList.getLocationOnScreen().y + event.getY());
private void displayLine(int lineNumber, boolean markCurrent) {
try {
if (markCurrent) {
/* remove previous highlight */
Highlighter hl = codeEditor.getHighlighter();
hl.changeHighlight(currentLineTag, 0, 0);
}
final int currentLine = codeList.locationToIndex(new Point(event.getX(), event.getY())) + 1;
codeList.setSelectedIndex(currentLine - 1);
JPopupMenu popupMenu = createPopupMenu(displayedFile, currentLine);
popupMenu.setLocation(menuLocation);
popupMenu.setInvoker(codeList);
popupMenu.setVisible(true);
}
}
private JPopupMenu createPopupMenu(final File codeFile, final int lineNr) {
final Integer executableAddress = breakpoints.getExecutableAddressOf(codeFile, lineNr);
boolean breakpointExists = breakpoints.breakpointExists(codeFile, lineNr);
JPopupMenu menuMotePlugins = new JPopupMenu();
JMenuItem headerMenuItem = new JMenuItem("Breakpoints:");
headerMenuItem.setEnabled(false);
menuMotePlugins.add(headerMenuItem);
menuMotePlugins.add(new JSeparator());
JMenuItem addBreakpointMenuItem = new JMenuItem("Add breakpoint on line " + lineNr);
if (executableAddress == null || breakpointExists) {
addBreakpointMenuItem.setEnabled(false);
} else {
addBreakpointMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
breakpoints.addBreakpoint(codeFile, lineNr, executableAddress);
if (lineNumber >= 0) {
final int start = codeEditorLines.get(lineNumber);
int end = codeEditorLines.get(lineNumber+1);
if (markCurrent) {
/* highlight code */
Highlighter hl = codeEditor.getHighlighter();
hl.changeHighlight(currentLineTag, start, end);
}
});
}
menuMotePlugins.add(addBreakpointMenuItem);
JMenuItem delBreakpointMenuItem = new JMenuItem("Delete breakpoint on line " + lineNr);
if (executableAddress == null || !breakpointExists) {
delBreakpointMenuItem.setEnabled(false);
} else {
delBreakpointMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
breakpoints.removeBreakpoint(executableAddress);
}
});
}
menuMotePlugins.add(delBreakpointMenuItem);
return menuMotePlugins;
}
private class CodeListModel extends AbstractListModel {
private String[] codeData;
public CodeListModel(String[] codeData) {
super();
this.codeData = codeData;
}
public int getSize() {
if (codeData == null || codeData.length == 0) {
return 0;
}
return codeData.length;
}
public Object getElementAt(int index) {
if (codeData == null || codeData.length == 0) {
return "No code to display";
}
return codeData[index];
}
}
/* FROM: http://www.rgagnon.com/javadetails/java-0306.html, 03/19/2008 */
private static String stringToHTMLString(String string) {
StringBuffer sb = new StringBuffer(string.length());
boolean lastWasBlankChar = false;
int len = string.length();
char c;
for (int i = 0; i < len; i++)
{
c = string.charAt(i);
if (c == ' ') {
if (lastWasBlankChar) {
lastWasBlankChar = false;
sb.append("&nbsp;");
}
else {
lastWasBlankChar = true;
sb.append(' ');
}
}
else {
lastWasBlankChar = false;
//
// HTML Special Chars
if (c == '"') {
sb.append("&quot;");
} else if (c == '&') {
sb.append("&amp;");
} else if (c == '<') {
sb.append("&lt;");
} else if (c == '>') {
sb.append("&gt;");
} else if (c == '\n') {
// Handle Newline
sb.append("&lt;br/&gt;");
} else {
int ci = 0xffff & c;
if (ci < 160 ) {
// nothing special only 7 Bit
sb.append(c);
} else {
// Not 7 Bit use the unicode system
sb.append("&#");
sb.append(new Integer(ci).toString());
sb.append(';');
/* ensure visible */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
codeEditor.scrollRectToVisible(codeEditor.modelToView(start));
} catch (BadLocationException e) {
}
}
}
});
}
} catch (Exception e) {
logger.warn("Error when highlighting current line: " + e.getMessage(), e);
}
return sb.toString();
}
private class CodeCellRenderer extends JLabel implements ListCellRenderer {
private int currentIndex;
private boolean nice = true;
public CodeCellRenderer(int currentLineNr) {
this.currentIndex = currentLineNr - 1;
}
public void setNice(boolean b) {
nice = b;
}
public void changeCurrentLine(int currentLineNr) {
this.currentIndex = currentLineNr - 1;
}
private String getColoredLabelText(int lineNr, int lineStartPos, Token[] tokens, String code) {
StringBuilder sb = new StringBuilder();
sb.append("<html>");
/* Add line number */
String lineString = "0000" + Integer.toString(lineNr);
lineString = lineString.substring(lineString.length() - 4);
sb.append("<font color=\"333333\">");
sb.append(lineString);
sb.append(": </font>");
/* Add code */
if (tokens == null || tokens.length == 0 || lineStartPos < 0) {
sb.append("<font color=\"000000\">");
sb.append(code);
sb.append("</font>");
} else {
for (int i=tokens.length-1; i >= 0; i--) {
Token subToken = tokens[i];
String colorString = "000000";
/* Determine code color */
final int type = subToken.symbol.type;
switch (type) {
case TokenTypes.COMMENT:
case TokenTypes.START_COMMENT:
case TokenTypes.MID_COMMENT:
case TokenTypes.END_COMMENT:
colorString = "00AA00";
break;
case TokenTypes.STRING:
colorString = "0000AA";
break;
case TokenTypes.KEYWORD:
case TokenTypes.KEYWORD2:
colorString = "AA0000";
break;
}
/* Extract part of token residing in current line */
int tokenLinePos;
String subCode;
if (subToken.position < lineStartPos) {
subCode = subToken.symbol.name.substring(lineStartPos - subToken.position);
tokenLinePos = 0;
} else if (subToken.position + subToken.symbol.name.length() > lineStartPos + code.length()) {
subCode = subToken.symbol.name.substring(0, code.length() + lineStartPos - subToken.position);
tokenLinePos = subToken.position - lineStartPos;
} else {
subCode = subToken.symbol.name;
tokenLinePos = subToken.position - lineStartPos;
}
subCode = stringToHTMLString(subCode);
String firstPart = code.substring(0, tokenLinePos);
String coloredSubCode = "<font color=\"" + colorString + "\">" + subCode + "</font>";
String lastPart =
tokenLinePos + subToken.symbol.name.length() >= code.length()?
"":code.substring(tokenLinePos + subToken.symbol.name.length());
code = firstPart + coloredSubCode + lastPart;
}
code = code.replace(" ", " &nbsp;");
sb.append(code);
}
sb.append("</html>");
return sb.toString();
}
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
int lineNr = index + 1;
if (!nice) {
setText((String) value);
} else if (tokensArray != null && index < tokensArray.length && tokensArray[index] != null) {
setText(getColoredLabelText(lineNr, tokensStartPos[index], tokensArray[index], (String) value));
} else {
setText(getColoredLabelText(lineNr, 0, null, (String) value));
}
if (index == currentIndex) {
setBackground(Color.green);
} else if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setEnabled(list.isEnabled());
if (breakpoints.breakpointExists(displayedFile, lineNr)) {
setFont(list.getFont().deriveFont(Font.BOLD));
} else {
setFont(list.getFont());
}
setOpaque(true);
return this;
}
}
}

View File

@ -30,18 +30,16 @@
package se.sics.cooja.mspmote.plugins;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.Action;
@ -54,8 +52,7 @@ import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
@ -72,11 +69,12 @@ import se.sics.cooja.MotePlugin;
import se.sics.cooja.PluginType;
import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin;
import se.sics.cooja.Watchpoint;
import se.sics.cooja.WatchpointMote;
import se.sics.cooja.WatchpointMote.WatchpointListener;
import se.sics.cooja.mspmote.MspMote;
import se.sics.cooja.mspmote.MspMoteType;
import se.sics.cooja.util.StringUtils;
import se.sics.mspsim.core.EmulationException;
import se.sics.mspsim.core.MSP430;
import se.sics.mspsim.ui.DebugUI;
import se.sics.mspsim.util.DebugInfo;
import se.sics.mspsim.util.ELFDebug;
@ -85,25 +83,31 @@ import se.sics.mspsim.util.ELFDebug;
@PluginType(PluginType.MOTE_PLUGIN)
public class MspCodeWatcher extends VisPlugin implements MotePlugin {
private static final long serialVersionUID = -8463196456352243367L;
private static final int SOURCECODE = 0;
private static final int BREAKPOINTS = 2;
private static Logger logger = Logger.getLogger(MspCodeWatcher.class);
private Simulation simulation;
private Observer simObserver;
private MspMote mspMote;
private File currentCodeFile = null;
private int currentLineNumber = -1;
private JSplitPane leftSplitPane, rightSplitPane;
private DebugUI assCodeUI;
private CodeUI sourceCodeUI;
private BreakpointsUI breakpointsUI;
private MspBreakpointContainer breakpoints = null;
private MspMote mspMote; /* currently the only supported mote */
private WatchpointMote watchpointMote;
private WatchpointListener watchpointListener;
private JComboBox fileComboBox;
private String[] debugInfoMap = null;
private File[] sourceFiles;
private JTabbedPane mainPane;
/**
* Mini-debugger for MSP Motes.
* Visualizes instructions, source code and allows a user to manipulate breakpoints.
@ -113,15 +117,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
* @param gui Simulator
*/
public MspCodeWatcher(Mote mote, Simulation simulationToVisualize, GUI gui) {
super("Msp Code Watcher", gui);
this.mspMote = (MspMote) mote;
super("Msp Code Watcher - " + mote, gui);
simulation = simulationToVisualize;
this.mspMote = (MspMote) mote;
this.watchpointMote = (WatchpointMote) mote;
getContentPane().setLayout(new BorderLayout());
/* Breakpoints */
breakpoints = mspMote.getBreakpointsContainer();
/* Create source file list */
fileComboBox = new JComboBox();
fileComboBox.addActionListener(new ActionListener() {
@ -149,58 +151,68 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
}
});
updateFileComboBox();
/* Browse code control (north) */
JButton currentFileButton = new JButton(currentFileAction);
JButton mapButton = new JButton(mapAction);
Box browseBox = Box.createHorizontalBox();
browseBox.add(Box.createHorizontalStrut(10));
browseBox.add(new JLabel("Program counter: "));
browseBox.add(currentFileButton);
browseBox.add(Box.createHorizontalGlue());
browseBox.add(new JLabel("Browse: "));
browseBox.add(fileComboBox);
browseBox.add(Box.createHorizontalStrut(10));
browseBox.add(mapButton);
browseBox.add(Box.createHorizontalStrut(10));
Box sourceCodeControl = Box.createHorizontalBox();
sourceCodeControl.add(new JButton(stepAction));
sourceCodeControl.add(Box.createHorizontalStrut(10));
sourceCodeControl.add(new JLabel("Current location: "));
sourceCodeControl.add(new JButton(currentFileAction));
sourceCodeControl.add(Box.createHorizontalGlue());
sourceCodeControl.add(new JLabel("Source files: "));
sourceCodeControl.add(fileComboBox);
sourceCodeControl.add(Box.createHorizontalStrut(5));
sourceCodeControl.add(new JButton(mapAction));
sourceCodeControl.add(Box.createHorizontalStrut(10));
/* Execution control panel (south) */
JPanel controlPanel = new JPanel();
JButton button = new JButton(stepAction);
controlPanel.add(button);
/* Execution control panel (south of source code panel) */
/* Layout */
mainPane = new JTabbedPane();
sourceCodeUI = new CodeUI(watchpointMote);
JPanel sourceCodePanel = new JPanel(new BorderLayout());
sourceCodePanel.add(BorderLayout.CENTER, sourceCodeUI);
sourceCodePanel.add(BorderLayout.SOUTH, sourceCodeControl);
mainPane.addTab("Source code", null, sourceCodePanel, null); /* SOURCECODE */
/* Main components: assembler and C code + breakpoints (center) */
assCodeUI = new DebugUI(this.mspMote.getCPU(), true);
breakpointsUI = new BreakpointsUI(breakpoints, this);
sourceCodeUI = new CodeUI(breakpoints);
leftSplitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
new JScrollPane(assCodeUI),
new JScrollPane(breakpointsUI)
);
leftSplitPane.setOneTouchExpandable(true);
leftSplitPane.setDividerLocation(0.0);
rightSplitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
leftSplitPane,
new JScrollPane(sourceCodeUI)
);
rightSplitPane.setOneTouchExpandable(true);
rightSplitPane.setDividerLocation(0.0);
for (Component c: assCodeUI.getComponents()) {
c.setBackground(Color.WHITE);
}
mainPane.addTab("Instructions", null, assCodeUI, null);
add(BorderLayout.NORTH, browseBox);
add(BorderLayout.CENTER, rightSplitPane);
add(BorderLayout.SOUTH, controlPanel);
breakpointsUI = new BreakpointsUI(mspMote, this);
mainPane.addTab("Breakpoints", null, breakpointsUI, "Right-click source code to add"); /* BREAKPOINTS */
add(BorderLayout.CENTER, mainPane);
/* Listen for breakpoint changes */
watchpointMote.addWatchpointListener(watchpointListener = new WatchpointListener() {
public void watchpointTriggered(final Watchpoint watchpoint) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
logger.info("Watchpoint triggered: " + watchpoint);
if (simulation.isRunning()) {
return;
}
breakpointsUI.selectBreakpoint(watchpoint);
sourceCodeUI.updateBreakpoints();
showCurrentPC();
}
});
}
public void watchpointsChanged() {
sourceCodeUI.updateBreakpoints();
}
});
/* Observe when simulation starts/stops */
simulation.addObserver(simObserver = new Observer() {
public void update(Observable obs, Object obj) {
if (!simulation.isRunning()) {
stepAction.setEnabled(true);
updateInfo();
showCurrentPC();
} else {
stepAction.setEnabled(false);
}
@ -208,7 +220,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
});
setSize(750, 500);
updateInfo();
showCurrentPC();
}
private void updateFileComboBox() {
@ -221,25 +233,12 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
fileComboBox.setSelectedIndex(0);
}
public void displaySourceFile(File file, final int line) {
if (file != null &&
sourceCodeUI.displayedFile != null &&
file.compareTo(sourceCodeUI.displayedFile) == 0) {
/* No need to reload source file */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
sourceCodeUI.displayLine(line);
}
});
return;
}
/* Load source file from disk */
String[] codeData = readTextFile(file);
if (codeData == null) {
return;
}
sourceCodeUI.displayNewCode(file, codeData, line);
public void displaySourceFile(final File file, final int line, final boolean markCurrent) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
mainPane.setSelectedIndex(SOURCECODE); /* code */
sourceCodeUI.displayNewCode(file, line, markCurrent);
}});
}
private void sourceFileSelectionChanged() {
@ -249,32 +248,40 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
}
File selectedFile = sourceFiles[index-1];
displaySourceFile(selectedFile, -1);
displaySourceFile(selectedFile, -1, false);
}
private void updateInfo() {
private void showCurrentPC() {
/* Instructions */
assCodeUI.updateRegs();
assCodeUI.repaint();
/* Source */
updateCurrentSourceCodeFile();
if (currentCodeFile == null) {
File file = currentCodeFile;
Integer line = currentLineNumber;
if (file == null || line == null) {
currentFileAction.setEnabled(false);
currentFileAction.putValue(Action.NAME, "[unknown]");
currentFileAction.putValue(Action.SHORT_DESCRIPTION, null);
return;
}
currentFileAction.setEnabled(true);
currentFileAction.putValue(Action.NAME, currentCodeFile.getName() + ":" + currentLineNumber);
currentFileAction.putValue(Action.SHORT_DESCRIPTION, currentCodeFile.getAbsolutePath() + ":" + currentLineNumber);
fileComboBox.setSelectedItem(currentCodeFile.getName());
currentFileAction.putValue(Action.NAME, file.getName() + ":" + line);
currentFileAction.putValue(Action.SHORT_DESCRIPTION, file.getAbsolutePath() + ":" + line);
fileComboBox.setSelectedItem(file.getName());
displaySourceFile(currentCodeFile, currentLineNumber);
displaySourceFile(file, line, true);
}
public void closePlugin() {
watchpointMote.removeWatchpointListener(watchpointListener);
watchpointListener = null;
simulation.deleteObserver(simObserver);
simObserver = null;
/* TODO XXX Unregister breakpoints? */
}
private void updateCurrentSourceCodeFile() {
@ -285,8 +292,12 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
if (debug == null) {
return;
}
DebugInfo debugInfo = debug.getDebugInfo(mspMote.getCPU().reg[MSP430.PC]);
int pc = mspMote.getCPU().getPC();
DebugInfo debugInfo = debug.getDebugInfo(pc);
if (debugInfo == null) {
if (pc != 0) {
logger.warn("No sourcecode info at " + String.format("0x%04x", mspMote.getCPU().getPC()));
}
return;
}
@ -321,6 +332,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
private void tryMapDebugInfo() {
final String[] debugFiles;
try {
ELFDebug debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
debugFiles = debug != null ? debug.getSourceFiles() : null;
if (debugFiles == null) {
@ -343,7 +355,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
"\"Next File\" proceeds to the next source file in the debug info.\n\n" +
debugFiles[counter] + " (" + (counter+1) + '/' + debugFiles.length + ')',
"Select source file to locate", JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, null,
JOptionPane.QUESTION_MESSAGE, null,
new String[] { "Next File", "Locate File", "Cancel"}, "Next File");
if (n == JOptionPane.CANCEL_OPTION || n == JOptionPane.CLOSED_OPTION) {
return null;
@ -421,14 +433,14 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
optionPane.setOptions(new String[] { "OK" });
optionPane.setInitialValue("OK");
JDialog dialog = optionPane.createDialog(
GUI.getTopParentContainer(),
"Mapping debug info to real sources");
GUI.getTopParentContainer(),
"Mapping debug info to real sources");
dialog.setVisible(true);
replace = replaceInput.getText();
replacement = replacementInput.getText();
}
replace = replace.replace('\\', '/');
replacement = replacement.replace('\\', '/');
return new String[] { replace, replacement };
@ -444,7 +456,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
updateFileComboBox();
}
}
private static File[] getSourceFiles(MspMote mote, String[] map) {
final String[] sourceFiles;
try {
@ -465,7 +477,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
} catch (IOException e1) {
}
}
/* Verify that files exist */
ArrayList<File> existing = new ArrayList<File>();
for (String sourceFile: sourceFiles) {
@ -512,7 +524,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
"Make sure the source files were not moved after the firmware compilation.\n" +
"\n" +
"If you want to manually locate the sources, click \"Map\" button.",
"No source files found",
"No source files found",
JOptionPane.WARNING_MESSAGE);
return true;
}
@ -530,7 +542,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
}
sorted.add(index, file);
}
/* Add Contiki source first */
if (contikiSource != null && contikiSource.exists()) {
sorted.add(0, contikiSource);
@ -539,63 +551,21 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
return sorted.toArray(new File[0]);
}
/**
* Tries to open and read given text file.
*
* @param file File
* @return Line-by-line text in file
*/
public static String[] readTextFile(File file) {
if (GUI.isVisualizedInApplet()) {
/* Download from web server instead */
String path = file.getPath();
/* Extract Contiki build path */
String contikiBuildPath = GUI.getExternalToolsSetting("PATH_CONTIKI_BUILD");
String contikiWebPath = GUI.getExternalToolsSetting("PATH_CONTIKI_WEB");
if (!path.startsWith(contikiBuildPath)) {
return null;
}
try {
/* Replace Contiki parent path with web server code base */
path = contikiWebPath + '/' + path.substring(contikiBuildPath.length());
path = path.replace('\\', '/');
URL url = new URL(GUI.getAppletCodeBase(), path);
String data = StringUtils.loadFromURL(url);
return data!=null?data.split("\n"):null;
} catch (MalformedURLException e) {
logger.warn("Failure to read source code: " + e);
return null;
}
}
String data = StringUtils.loadFromFile(file);
return data!=null?data.split("\n"):null;
}
public Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
ArrayList<Element> config = new ArrayList<Element>();
Element element;
element = new Element("split_1");
element.addContent("" + leftSplitPane.getDividerLocation());
element = new Element("tab");
element.addContent("" + mainPane.getSelectedIndex());
config.add(element);
element = new Element("split_2");
element.addContent("" + rightSplitPane.getDividerLocation());
config.add(element);
return config;
}
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
for (Element element : configXML) {
if (element.getName().equals("split_1")) {
leftSplitPane.setDividerLocation(Integer.parseInt(element.getText()));
} else if (element.getName().equals("split_2")) {
rightSplitPane.setDividerLocation(Integer.parseInt(element.getText()));
if (element.getName().equals("tab")) {
mainPane.setSelectedIndex(Integer.parseInt(element.getText()));
}
}
return true;
@ -608,11 +578,11 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
if (currentCodeFile == null) {
return;
}
displaySourceFile(currentCodeFile, currentLineNumber);
displaySourceFile(currentCodeFile, currentLineNumber, true);
}
};
private AbstractAction mapAction = new AbstractAction("Map") {
private AbstractAction mapAction = new AbstractAction("Locate sources") {
private static final long serialVersionUID = -3929432830596292495L;
public void actionPerformed(ActionEvent e) {
@ -622,14 +592,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
private AbstractAction stepAction = new AbstractAction("Step instruction") {
private static final long serialVersionUID = 3520750710757816575L;
public void actionPerformed(ActionEvent e) {
try {
mspMote.getCPU().stepInstructions(1);
} catch (EmulationException ex) {
logger.fatal("Error: ", ex);
}
updateInfo();
showCurrentPC();
}
};

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2012, 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: CodeUI.java,v 1.8 2009/09/23 08:16:06 fros4943 Exp $
*/
package se.sics.cooja.util;
import java.awt.event.ActionEvent;
import java.io.File;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.text.JTextComponent;
import jsyntaxpane.SyntaxDocument;
import jsyntaxpane.actions.DefaultSyntaxAction;
import org.apache.log4j.Logger;
import se.sics.cooja.WatchpointMote;
public class JSyntaxAddBreakpoint extends DefaultSyntaxAction {
private static Logger logger = Logger.getLogger(JSyntaxAddBreakpoint.class);
public JSyntaxAddBreakpoint() {
super("addbreakpoint");
}
public void actionPerformed(ActionEvent e) {
JMenuItem menuItem = (JMenuItem) e.getSource();
Action action = menuItem.getAction();
WatchpointMote watchpointMote = (WatchpointMote) action.getValue("WatchpointMote");
if (watchpointMote == null) {
logger.warn("Error: No source, cannot configure breakpoint");
return;
}
File file = (File) action.getValue("WatchpointFile");
Integer line = (Integer) action.getValue("WatchpointLine");
Integer address = (Integer) action.getValue("WatchpointAddress");
if (file == null || line == null || address == null) {
logger.warn("Error: Bad breakpoint info, cannot add breakpoint");
return;
}
watchpointMote.addBreakpoint(file, line, address);
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2012, 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: CodeUI.java,v 1.8 2009/09/23 08:16:06 fros4943 Exp $
*/
package se.sics.cooja.util;
import java.awt.event.ActionEvent;
import java.io.File;
import javax.swing.Action;
import javax.swing.JMenuItem;
import jsyntaxpane.actions.DefaultSyntaxAction;
import org.apache.log4j.Logger;
import se.sics.cooja.Watchpoint;
import se.sics.cooja.WatchpointMote;
public class JSyntaxRemoveBreakpoint extends DefaultSyntaxAction {
private static Logger logger = Logger.getLogger(JSyntaxRemoveBreakpoint.class);
public JSyntaxRemoveBreakpoint() {
super("removebreakpoint");
}
public void actionPerformed(ActionEvent e) {
JMenuItem menuItem = (JMenuItem) e.getSource();
Action action = menuItem.getAction();
WatchpointMote watchpointMote = (WatchpointMote) action.getValue("WatchpointMote");
if (watchpointMote == null) {
logger.warn("Error: No source, cannot configure breakpoint");
return;
}
File file = (File) action.getValue("WatchpointFile");
Integer line = (Integer) action.getValue("WatchpointLine");
Integer address = (Integer) action.getValue("WatchpointAddress");
if (file == null || line == null || address == null) {
logger.warn("Error: Bad breakpoint info, cannot remove breakpoint");
return;
}
for (Watchpoint w: watchpointMote.getBreakpoints()) {
if (file.equals(w.getCodeFile()) && line.equals(w.getLineNumber()) && address.equals(w.getExecutableAddress())) {
watchpointMote.removeBreakpoint(w);
}
}
}
}