From 7cfa8e28d3cbf0785b4774cb08a4e900ca81bdef Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Wed, 21 Mar 2012 16:58:26 +0100 Subject: [PATCH] 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 --- .../cooja/mspmote/plugins/BreakpointsUI.java | 330 ++++----- .../se/sics/cooja/mspmote/plugins/CodeUI.java | 663 ++++++------------ .../cooja/mspmote/plugins/MspCodeWatcher.java | 263 +++---- .../sics/cooja/util/JSyntaxAddBreakpoint.java | 74 ++ .../cooja/util/JSyntaxRemoveBreakpoint.java | 77 ++ 5 files changed, 616 insertions(+), 791 deletions(-) create mode 100644 tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java create mode 100644 tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java index 7a9d0bdb9..ede9fc111 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java @@ -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 motes = new ArrayList(); - 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 motes = new ArrayList(); - 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); } }; } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java index c034d6f0a..e58257b66 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java @@ -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 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 breakpointsLineTags = new ArrayList(); - /** - * @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(); + 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 codeTokensVector = new ArrayList(); - 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 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 lineTokens = new Vector(); - 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(" "); - } - else { - lastWasBlankChar = true; - sb.append(' '); - } - } - else { - lastWasBlankChar = false; - // - // HTML Special Chars - if (c == '"') { - sb.append("""); - } else if (c == '&') { - sb.append("&"); - } else if (c == '<') { - sb.append("<"); - } else if (c == '>') { - sb.append(">"); - } else if (c == '\n') { - // Handle Newline - sb.append("<br/>"); - } 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(""); - - /* Add line number */ - String lineString = "0000" + Integer.toString(lineNr); - lineString = lineString.substring(lineString.length() - 4); - sb.append(""); - sb.append(lineString); - sb.append(": "); - - /* Add code */ - if (tokens == null || tokens.length == 0 || lineStartPos < 0) { - sb.append(""); - sb.append(code); - sb.append(""); - } 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 = "" + subCode + ""; - String lastPart = - tokenLinePos + subToken.symbol.name.length() >= code.length()? - "":code.substring(tokenLinePos + subToken.symbol.name.length()); - - code = firstPart + coloredSubCode + lastPart; - } - - code = code.replace(" ", "  "); - sb.append(code); - } - - sb.append(""); - 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; - } - } - } diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java index 4ad0ca2e1..a11e31592 100644 --- a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java @@ -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 existing = new ArrayList(); 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 getConfigXML() { - Vector config = new Vector(); + ArrayList config = new ArrayList(); 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 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(); } }; diff --git a/tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java b/tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java new file mode 100644 index 000000000..b2e394c8c --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/util/JSyntaxAddBreakpoint.java @@ -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); + } +} diff --git a/tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java b/tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java new file mode 100644 index 000000000..85bb3245d --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/util/JSyntaxRemoveBreakpoint.java @@ -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); + } + } + + } +}