From 749c0aa63995b7578f0c512f1a01efdbbaeffcdd Mon Sep 17 00:00:00 2001 From: fros4943 Date: Thu, 7 Feb 2008 14:55:18 +0000 Subject: [PATCH] msp430 specific plugins --- .../cooja/mspmote/plugins/BreakpointsUI.java | 188 +++++ .../se/sics/cooja/mspmote/plugins/CodeUI.java | 299 +++++++ .../cooja/mspmote/plugins/MspCodeWatcher.java | 751 ++++++++++++++++++ .../mspmote/plugins/MspCycleWatcher.java | 124 +++ .../mspmote/plugins/MspStackWatcher.java | 139 ++++ 5 files changed, 1501 insertions(+) create mode 100644 tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java create mode 100644 tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java create mode 100644 tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java create mode 100644 tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCycleWatcher.java create mode 100644 tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspStackWatcher.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 new file mode 100644 index 000000000..2c5dd3902 --- /dev/null +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/BreakpointsUI.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: BreakpointsUI.java,v 1.1 2008/02/07 14:55:18 fros4943 Exp $ + */ + +package se.sics.cooja.mspmote.plugins; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.Vector; +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import org.apache.log4j.Logger; + +import se.sics.cooja.mspmote.plugins.MspCodeWatcher.Breakpoints; +import se.sics.cooja.mspmote.plugins.MspCodeWatcher.Breakpoints.Breakpoint; + +public class BreakpointsUI extends JPanel { + private static Logger logger = Logger.getLogger(BreakpointsUI.class); + + private JTable breakpointsTable = null; + private Breakpoints breakpoints = null; + + private AbstractTableModel tableModel = new AbstractTableModel() { + private String[] tableColumnNames = { + "Address", + "File", + "Line", + "Remove" + }; + + public String getColumnName(int col) { return tableColumnNames[col].toString(); } + public int getRowCount() { return breakpoints.getBreakpoints().size(); } + public int getColumnCount() { return tableColumnNames.length; } + public Object getValueAt(int row, int col) { + // Display executable address in hexadecimal + if (col == 0) { + Integer address = breakpoints.getBreakpoints().get(row).getExecutableAddress(); + return "0x" + Integer.toHexString(address.intValue()); + } + + // Display only name of file + if (col == 1) { + File file = breakpoints.getBreakpoints().get(row).getCodeFile(); + if (file == null) { + return ""; + } + return file.getName(); + } + + // Display line number + if (col == 2) { + Integer line = breakpoints.getBreakpoints().get(row).getLineNumber(); + if (line == null) { + return ""; + } + return line; + } + + return new Boolean(false); + } + public boolean isCellEditable(int row, int col){ + return getColumnClass(col) == Boolean.class; + } + public void setValueAt(Object value, int row, int col) { + fireTableCellUpdated(row, col); + Integer address = breakpoints.getBreakpoints().get(row).getExecutableAddress(); + breakpoints.removeBreakpoint(address); + } + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + }; + + public BreakpointsUI(Breakpoints breakpoints) { + this.breakpoints = breakpoints; + breakpoints.addBreakpointListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Breakpoint triggered = BreakpointsUI.this.breakpoints.getLastTriggered(); + if (triggered != null) { + flashBreakpoint(triggered); + } + + breakpointsTable.repaint(); + } + }); + + breakpointsTable = new JTable(tableModel) { + public String getToolTipText(MouseEvent e) { + String tip = null; + java.awt.Point p = e.getPoint(); + int rowIndex = breakpointsTable.rowAtPoint(p); + int colIndex = breakpointsTable.columnAtPoint(p); + int realColumnIndex = breakpointsTable.convertColumnIndexToModel(colIndex); + + if (realColumnIndex == 1) { + Vector allBreakpoints = BreakpointsUI.this.breakpoints.getBreakpoints(); + if (rowIndex < 0 || rowIndex >= allBreakpoints.size()) { + return ""; + } + File file = allBreakpoints.get(rowIndex).getCodeFile(); + if (file == null) { + return ""; + } else { + tip = file.getPath(); + } + } + return tip; + } + }; + + setLayout(new BorderLayout()); + add(breakpointsTable.getTableHeader(), BorderLayout.PAGE_START); + add(breakpointsTable, BorderLayout.CENTER); + } + + private int flashCounter = 0; + private void flashBreakpoint(Breakpoint breakpoint) { + int index = breakpoints.getBreakpoints().indexOf(breakpoint); + breakpointsTable.setRowSelectionInterval(index, index); + breakpointsTable.setSelectionBackground(Color.RED); + + flashCounter = 8; + + final Timer timer = new Timer(100, null); + timer.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (flashCounter-- <= 0) { + timer.stop(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + breakpointsTable.setSelectionBackground(Color.WHITE); + } + }); + return; + } + // Toggle background color + if (breakpointsTable.getSelectionBackground() != Color.RED) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + breakpointsTable.setSelectionBackground(Color.RED); + } + }); + } else { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + breakpointsTable.setSelectionBackground(Color.WHITE); + } + }); + } + } + }); + + timer.start(); + + } + +} 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 new file mode 100644 index 000000000..534603f8c --- /dev/null +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/CodeUI.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: CodeUI.java,v 1.1 2008/02/07 14:55:18 fros4943 Exp $ + */ + +package se.sics.cooja.mspmote.plugins; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.util.Vector; +import javax.swing.*; +import org.apache.log4j.Logger; + +import se.sics.cooja.mspmote.plugins.MspCodeWatcher.Breakpoints; + +/** + * Displays source code and allows a user to add and remove breakpoints. + * + * @author Fredrik Österlind + */ +public class CodeUI extends JPanel { + private static Logger logger = Logger.getLogger(CodeUI.class); + + private JPanel panel = null; + private JList codeList = null; + private File currentFile = null; + + private Breakpoints breakpoints = null; + + /** + * @param breakpoints Breakpoints + */ + public CodeUI(Breakpoints breakpoints) { + this.breakpoints = breakpoints; + + setLayout(new BorderLayout()); + panel = new JPanel(new BorderLayout()); + add(panel, BorderLayout.CENTER); + displayNoCode(); + + breakpoints.addBreakpointListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (codeList != null) { + codeList.updateUI(); + } + } + }); + } + + /** + * Remove any shown source code. + */ + public void displayNoCode() { + // Display "no code" message + SwingUtilities.invokeLater(new Runnable() { + public void run() { + panel.removeAll(); + panel.repaint(); + } + }); + currentFile = null; + return; + } + + + /** + * Display given source code and mark given line. + * + * @param codeFile Source code file + * @param codeData Source code + * @param lineNr Line numer + */ + public void displayNewCode(final File codeFile, final Vector codeData, final int lineNr) { + currentFile = codeFile; + + if (codeData == null || codeData.size() == 0) { + displayNoCode(); + return; + } + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + // Display code + codeList = new JList(new CodeListModel(codeData)); + codeList.setFont(new Font("courier", 0, 12)); + codeList.setCellRenderer(new CodeCellRenderer(lineNr)); + codeList.addMouseListener(new MouseListener() { + 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); + } + }); + panel.removeAll(); + panel.add(codeList); + displayLine(lineNr); + } + }); + } + + /** + * Mark given line number in shown source code. + * + * @param lineNumber Line number + */ + public void displayLine(final int lineNumber) { + if (codeList == null) { + return; + } + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (lineNumber > 0) { + ((CodeCellRenderer) codeList.getCellRenderer()).changeCurrentLine(lineNumber); + 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(); + } + }); + } + + 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()); + } + + final int currentLine = codeList.locationToIndex(new Point(event.getX(), event.getY())) + 1; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + codeList.setSelectedIndex(currentLine - 1); + } + }); + JPopupMenu popupMenu = createPopupMenu(currentFile, 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 = false; + if (executableAddress != null) { + breakpointExists = breakpoints.breakpointExists(executableAddress); + } + + 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); + } + }); + } + 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 Vector codeData; + + public CodeListModel(Vector codeData) { + super(); + this.codeData = codeData; + } + + public int getSize() { + if (codeData == null || codeData.isEmpty()) { + return 0; + } + + return codeData.size(); + } + + public Object getElementAt(int index) { + if (codeData == null || codeData.isEmpty()) { + return "No code to display"; + } + + return codeData.get(index); + } + } + + private class CodeCellRenderer extends JLabel implements ListCellRenderer { + private int currentIndex; + + public CodeCellRenderer(int currentLineNr) { + this.currentIndex = currentLineNr - 1; + } + + public void changeCurrentLine(int currentLineNr) { + this.currentIndex = currentLineNr - 1; + } + + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + int lineNr = index + 1; + + setText(lineNr + ": " + 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()); + + Integer executableAddress = breakpoints.getExecutableAddressOf(currentFile, lineNr); + if (breakpoints.breakpointExists(executableAddress)) { + 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 new file mode 100644 index 000000000..2ccaffdf5 --- /dev/null +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCodeWatcher.java @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: MspCodeWatcher.java,v 1.1 2008/02/07 14:55:18 fros4943 Exp $ + */ + +package se.sics.cooja.mspmote.plugins; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.InputStreamReader; +import java.util.*; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicComboBoxRenderer; +import org.apache.log4j.Logger; +import org.jdom.Element; + +import se.sics.cooja.*; +import se.sics.cooja.mspmote.MspMote; +import se.sics.cooja.mspmote.MspMoteType; +import se.sics.mspsim.core.CPUMonitor; +import se.sics.mspsim.core.MSP430; +import se.sics.mspsim.util.DebugUI; +import se.sics.mspsim.util.Utils; + +@ClassDescription("Msp Code Watcher") +@PluginType(PluginType.MOTE_PLUGIN) +public class MspCodeWatcher extends VisPlugin { + private static Logger logger = Logger.getLogger(MspCodeWatcher.class); + private Simulation mySimulation; + private Observer simObserver; + private MspMote mspMote; + private MspMoteType moteType; + private JButton stepButton; + + private File codeFile = null; + private int lineNumber = -1; + + private DebugUI instructionsUI; + private CodeUI codeUI; + private BreakpointsUI breakpointsUI; + + private Breakpoints breakpoints = null; + + private JLabel filenameLabel; + + /** + * Mini-debugger for MSP Motes. + * Visualizes instructions, source code and allows a user to manipulate breakpoints. + * + * @param mote MSP Mote + * @param simulationToVisualize Simulation + * @param gui Simulator + */ + public MspCodeWatcher(Mote mote, Simulation simulationToVisualize, GUI gui) { + super("Msp Code Watcher", gui); + this.mspMote = (MspMote) mote; + this.moteType = (MspMoteType) mote.getType(); + mySimulation = simulationToVisualize; + + Hashtable> debuggingInfo = getFirmwareDebugInfo(); + breakpoints = new Breakpoints(debuggingInfo, mspMote); + + getContentPane().setLayout(new BorderLayout()); + + instructionsUI = new DebugUI(this.mspMote.getCPU(), true); + breakpointsUI = new BreakpointsUI(breakpoints); + codeUI = new CodeUI(breakpoints); + + JSplitPane leftPanel = new JSplitPane( + JSplitPane.VERTICAL_SPLIT, + new JScrollPane(instructionsUI, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), + new JScrollPane(breakpointsUI, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED) + ); + + JPanel controlPanel = new JPanel(); + + // Extract files found in debugging info + final Vector files = new Vector(); + Enumeration fileEnum = debuggingInfo.keys(); + while (fileEnum.hasMoreElements()) { + File file = (File) fileEnum.nextElement(); + + // Insert file on correct position + int index = 0; + for (index=0; index < files.size(); index++) { + if (file.getName().compareToIgnoreCase(files.get(index).getName()) < 0) { + break; + } + } + files.add(index, file); + } + String[] fileNames = new String[files.size() + 1]; + fileNames[0] = "[select file]"; + for (int i=0; i < files.size(); i++) { + fileNames[i+1] = files.get(i).getName(); + } + JComboBox fileComboBox = new JComboBox(fileNames); + fileComboBox.setSelectedIndex(0); + fileComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComboBox source = (JComboBox)e.getSource(); + int index = source.getSelectedIndex(); + if (index == 0) { + return; + } + + File selectedFile = files.get(index-1); + Vector codeData = readTextFile(selectedFile); + codeUI.displayNewCode(selectedFile, codeData, -1); + codeFile = null; + } + }); + fileComboBox.setRenderer(new BasicComboBoxRenderer() { + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + if (isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + if (index > 0) { + list.setToolTipText(files.get(index-1).getPath()); + } + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + setFont(list.getFont()); + setText((value == null) ? "" : value.toString()); + return this; + } + }); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BorderLayout()); + topPanel.add(BorderLayout.EAST, fileComboBox); + topPanel.add(BorderLayout.CENTER, filenameLabel = new JLabel("")); + + + + // Add single step button + stepButton = new JButton("Single step"); + stepButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + // TODO Perform single step here + mspMote.getCPU().step(); + updateInfo(); + } + }); + controlPanel.add(stepButton); + + add(BorderLayout.CENTER, new JSplitPane( + JSplitPane.HORIZONTAL_SPLIT, + leftPanel, + new JScrollPane(codeUI) + )); + add(BorderLayout.SOUTH, controlPanel); + add(BorderLayout.NORTH, topPanel); + + // Register as tickobserver + mySimulation.addObserver(simObserver = new Observer() { + public void update(Observable obs, Object obj) { + if (!mySimulation.isRunning()) { + stepButton.setEnabled(true); + updateInfo(); + } else { + stepButton.setEnabled(false); + } + } + }); + + setSize(350, 250); + pack(); + + // Tries to select this plugin + try { + setSelected(true); + } catch (java.beans.PropertyVetoException e) { + // Could not select + } + } + + /** + * Contains currently active breakpoints. + * + * @author Fredrik Österlind + */ + static class Breakpoints { + private Hashtable> debuggingInfo = null; + private Vector breakpoints = new Vector(); + private Vector listeners = new Vector(); + private Breakpoint lastBreakpoint = null; + private MspMote mspMote = null; + private boolean stopOnBreakpointTriggered = true; + + /** + * @param debuggingInfo Debugging information read from firmware file + * @param mote MspMote + */ + public Breakpoints(Hashtable> debuggingInfo, MspMote mote) { + this.debuggingInfo = debuggingInfo; + this.mspMote = mote; + } + + /** + * @param debuggingInfo Debugging information read from firmware file + * @param mote MspMote + */ + public Breakpoints(Hashtable> debuggingInfo, MspMote mote, boolean stopOnBreakpointTriggered) { + this(debuggingInfo, mote); + this.stopOnBreakpointTriggered = stopOnBreakpointTriggered; + } + + /** + * Add breakpoint at given address. + * + * @param address Executable address + */ + public void addBreakpoint(Integer address) { + addBreakpoint((File) null, (Integer) null, address); + } + + /** + * Add breakpoint at given address with given meta data. + * + * @param codeFile Source code file + * @param lineNr Source code file line number + * @param address Executable address + * @return Added breakpoint + */ + public Breakpoint addBreakpoint(File codeFile, int lineNr, Integer address) { + Breakpoint bp = new Breakpoint(codeFile, new Integer(lineNr), address, mspMote); + breakpoints.add(bp); + lastBreakpoint = null; + for (ActionListener listener: listeners) { + listener.actionPerformed(null); + } + return bp; + } + + /** + * Remove breakpoint at given address. + * + * @param address Executable address + */ + public void removeBreakpoint(Integer address) { + Breakpoint breakpointToRemove = null; + for (Breakpoint breakpoint: breakpoints) { + if (breakpoint.getExecutableAddress().intValue() == address.intValue()) { + breakpointToRemove = breakpoint; + break; + } + } + if (breakpointToRemove == null) { + return; + } + + breakpointToRemove.unregisterBreakpoint(); + breakpoints.remove(breakpointToRemove); + + // Notify listeners + lastBreakpoint = null; + for (ActionListener listener: listeners) { + listener.actionPerformed(null); + } + } + + /** + * Checks if a breakpoint exists at given address. + * + * @param address Executable address + * @return True if breakpoint exists, false otherwise + */ + public boolean breakpointExists(Integer address) { + if (address == null) { + return false; + } + + for (Breakpoint breakpoint: breakpoints) { + if (breakpoint.getExecutableAddress().intValue() == address.intValue()) { + return true; + } + } + return false; + } + + /** + * @return All breakpoints + */ + public Vector getBreakpoints() { + return breakpoints; + } + + /** + * Adds a breakpoint listener. + * The listener will be notified when breakpoints are added are removed. + * + * @param listener Breakpoint listener + */ + public void addBreakpointListener(ActionListener listener) { + listeners.add(listener); + } + + private void breakpointReached(Breakpoint b) { + if (stopOnBreakpointTriggered) { + mspMote.getSimulation().stopSimulation(); + mspMote.stopNextInstruction(); + } + + // Notify listeners + lastBreakpoint = b; + for (ActionListener listener: listeners) { + listener.actionPerformed(null); + } + } + + public MspMote getMote() { + return mspMote; + } + + public Breakpoint getLastTriggered() { + return lastBreakpoint; + } + + /** + * Breakpoint wrapper class. + * May contain breakpoint meta data such source code file and line number. + * + * @author Fredrik Österlind + */ + class Breakpoint { + private CPUMonitor cpuMonitor = null; + private File codeFile = null; + private Integer lineNr = null; + private Integer address = null; + private MspMote mspMote = null; + + /** + * Create new uninitialized breakpoint. Used with setConfigXML(). + */ + public Breakpoint(MspMote mote) { + this.mspMote = mote; + } + + /** + * Creates new breakpoint wrapper at given address. + * + * @param address Executable address + * @param mote MSP mote + */ + public Breakpoint(Integer address, MspMote mote) { + this.mspMote = mote; + + cpuMonitor = new CPUMonitor() { + public void cpuAction(int type, int adr, int data) { + breakpointReached(Breakpoint.this); + } + }; + + mspMote.getCPU().setBreakPoint(address, cpuMonitor); + this.address = address; + } + + /** + * Creates new breakpoint wrapper at given address with given meta data. + * + * @param codeFile Source code file + * @param lineNr Source code file line number + * @param address Executable address + * @param mote MSP mote + */ + public Breakpoint(File codeFile, Integer lineNr, Integer address, MspMote mote) { + this(address, mote); + this.codeFile = codeFile; + this.lineNr = lineNr; + } + + /** + * @return MSP mote + */ + public MspMote getMote() { + return mspMote; + } + + /** + * @return Source code file + */ + public File getCodeFile() { + return codeFile; + } + + /** + * @return Source code file line number + */ + public Integer getLineNumber() { + return lineNr; + } + + /** + * @return Executable address + */ + public Integer getExecutableAddress() { + return address; + } + + private void unregisterBreakpoint() { + mspMote.getCPU().setBreakPoint(address, null); + } + + public Collection getConfigXML() { + Vector config = new Vector(); + Element element; + + element = new Element("address"); + element.setText(address.toString()); + config.add(element); + + if (codeFile != null) { + element = new Element("codefile"); + element.setText(codeFile.getAbsolutePath()); + config.add(element); + } + + if (lineNr != null) { + element = new Element("line"); + element.setText(lineNr.toString()); + config.add(element); + } + + return config; + } + + public boolean setConfigXML(Collection configXML, boolean visAvailable) { + for (Element element : configXML) { + if (element.getName().equals("codefile")) { + codeFile = new File(element.getText()); + if (!codeFile.exists()) { + return false; + } + } else if (element.getName().equals("line")) { + lineNr = Integer.parseInt(element.getText()); + } else if (element.getName().equals("address")) { + address = Integer.parseInt(element.getText()); + } + } + + if (address == null) { + return false; + } + + cpuMonitor = new CPUMonitor() { + public void cpuAction(int type, int adr, int data) { + breakpointReached(Breakpoint.this); + } + }; + + mspMote.getCPU().setBreakPoint(address, cpuMonitor); + return true; + } + } + + /** + * Tries to, using debugging information from firmware file, calculate the executable address of given meta data. + * + * @param codeFile Source code file + * @param lineNr Source code file line number + * @return Executable address or null if not found + */ + public Integer getExecutableAddressOf(File codeFile, int lineNr) { + if (codeFile == null || lineNr < 0) { + return null; + } + + // Match file names + Enumeration fileEnum = debuggingInfo.keys(); + while (fileEnum.hasMoreElements()) { + File file = (File) fileEnum.nextElement(); + if (file != null && file.getName().equals(codeFile.getName())) { + /* Found source code file */ + Hashtable lineTable = debuggingInfo.get(file); + Enumeration lineEnum = lineTable.keys(); + while (lineEnum.hasMoreElements()) { + Integer line = (Integer) lineEnum.nextElement(); + if (line != null && line.intValue() == lineNr) { + /* Found line address */ + return lineTable.get(line); + } + } + // TODO Return null here to only allow unique source files + } + } + return null; + } + + public Collection getConfigXML() { + Vector config = new Vector(); + Element element; + + for (Breakpoint breakpoint: breakpoints) { + element = new Element("breakpoint"); + + Collection breakpointXML = breakpoint.getConfigXML(); + if (breakpointXML != null) { + element.addContent(breakpointXML); + config.add(element); + } + } + + return config; + } + + public boolean setConfigXML(Collection configXML, boolean visAvailable) { + for (Element element : configXML) { + if (element.getName().equals("breakpoint")) { + Breakpoint breakpoint = new Breakpoint(mspMote); + boolean ret = breakpoint.setConfigXML(element.getChildren(), visAvailable); + if (!ret) { + return false; + } + + breakpoints.add(breakpoint); + lastBreakpoint = null; + } + } + return true; + } + + } + + private void updateInfo() { + // Update instructions view + instructionsUI.updateRegs(); + instructionsUI.repaint(); + + // Try locate source file + File oldCodeFile = codeFile; + updateCurrentSourceCodeFile(); + + // If found and not already loaded, load source file + if (oldCodeFile == null || !oldCodeFile.getPath().equals(codeFile.getPath())) { + Vector codeData = readTextFile(codeFile); + codeUI.displayNewCode(codeFile, codeData, lineNumber); + } else { + codeUI.displayLine(lineNumber); + } + + codeUI.repaint(); + + if (codeFile != null) { + filenameLabel.setText(codeFile.getName() + ":" + lineNumber); + } else { + filenameLabel.setText(""); + } +} + + public void closePlugin() { + mySimulation.deleteObserver(simObserver); + while (breakpoints.getBreakpoints().size() > 0) { + breakpoints.removeBreakpoint(breakpoints.getBreakpoints().firstElement().getExecutableAddress()); + } + + } + + private void updateCurrentSourceCodeFile() { + codeFile = null; + + try { + String[] cmd = new String[]{ + "addr2line", + "-e", + moteType.getELFFile().getPath(), + Utils.hex16(mspMote.getCPU().reg[MSP430.PC]) + }; + + Process p = Runtime.getRuntime().exec(cmd); + + BufferedReader input = + new BufferedReader( + new InputStreamReader( + p.getInputStream())); + + String output = input.readLine(); + input.close(); + + if (output == null || output.startsWith("?")) { + return; + } + + String[] fileInfo = output.split(":"); + if (fileInfo.length != 2) { + return; + } + + // TODO Ugly Cygwin-Windows support + fileInfo[0] = fileInfo[0].replace("/cygdrive/c/", "c:/"); + + codeFile = new File(fileInfo[0]); + lineNumber = Integer.parseInt(fileInfo[1]); + + } catch (Exception e) { + logger.fatal("Error while calling addr2line: " + e); + codeFile = null; + lineNumber = -1; + } + } + + private Hashtable> getFirmwareDebugInfo() { + return getFirmwareDebugInfo(moteType.getELFFile()); + } + + /** + * Tries to read debugging information from firmware file using objdump. + * + * @return Hashtable with debugging information + */ + public static Hashtable> getFirmwareDebugInfo(File firmware) { + Hashtable> debuggingInfo = + new Hashtable>(); + + Hashtable currentHashtable = null; + try { + String[] cmd = new String[]{ + "objdump", + "-g", + firmware.getPath() + }; + + Process p = Runtime.getRuntime().exec(cmd); + + BufferedReader input = + new BufferedReader( + new InputStreamReader( + p.getInputStream())); + + String line = null; + while ((line = input.readLine()) != null) { + line = line.trim(); + + /* Check for new source file */ + if (line.endsWith(":")) { + String fileName = line.substring(0, line.length()-1); + fileName = fileName.replace("/cygdrive/c/", "c:/"); + currentHashtable = null; + + // Try path unchanged + File file = new File(fileName); + if (currentHashtable == null && file.exists()) { + currentHashtable = new Hashtable(); + debuggingInfo.put(file, currentHashtable); + } + + // Try adding firmwarepath + file = new File(firmware.getParent(), fileName); + if (currentHashtable == null && file.exists()) { + currentHashtable = new Hashtable(); + debuggingInfo.put(file, currentHashtable); + } + + if (currentHashtable == null) { // TODO Currently always true + logger.warn("Can't locate file: " + fileName); + } + } + + /* Check for source file line info */ + if (currentHashtable != null && line.startsWith("/* file ")) { + String[] lineInfo = line.split(" "); + + String lineNrString = lineInfo[lineInfo.length-4]; + String addressString = lineInfo[lineInfo.length-2]; + + int lineNr = Integer.parseInt(lineNrString); + int address = Integer.parseInt(addressString.substring(2), 16); + + currentHashtable.put(new Integer(lineNr), new Integer(address)); + } + } + + input.close(); + + } catch (Exception e) { + logger.fatal("Error while calling objdump: " + e); + } + return debuggingInfo; + } + + /** + * Tries to open and read given text file. + * + * @param textFile File + * @return Line-by-line text in file + */ + public static Vector readTextFile(File textFile) { + try { + BufferedReader in = + new BufferedReader( + new FileReader(textFile)); + + String line; + Vector textData = new Vector(); + while ((line = in.readLine()) != null) { + textData.add(line); + } + + in.close(); + + return textData; + + } catch (Exception e) { + return null; + } + } + + public Collection getConfigXML() { + return breakpoints.getConfigXML(); + } + + public boolean setConfigXML(Collection configXML, boolean visAvailable) { + return breakpoints.setConfigXML(configXML, visAvailable); + } + + +} diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCycleWatcher.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCycleWatcher.java new file mode 100644 index 000000000..baed3ddac --- /dev/null +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspCycleWatcher.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: MspCycleWatcher.java,v 1.1 2008/02/07 14:55:18 fros4943 Exp $ + */ + +package se.sics.cooja.mspmote.plugins; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; + +import javax.swing.*; +import org.apache.log4j.Logger; + +import se.sics.cooja.*; +import se.sics.cooja.mspmote.MspMote; +import se.sics.mspsim.core.MSP430; + +@ClassDescription("Msp Cycle Watcher") +@PluginType(PluginType.MOTE_PLUGIN) +public class MspCycleWatcher extends VisPlugin { + private static Logger logger = Logger.getLogger(MspStackWatcher.class); + private MspMote mspMote; + private MSP430 cpu; + private Simulation simulation; + private Observer simObserver = null; + private JTextField cycleTextField = new JTextField(""); + private JTextField resetTextField = new JTextField(""); + private long cycleReset = 0; + + public MspCycleWatcher(Mote mote, Simulation simulationToVisualize, GUI gui) { + super("Msp Cycle Watcher", gui); + this.mspMote = (MspMote) mote; + cpu = mspMote.getCPU(); + simulation = simulationToVisualize; + + cycleTextField.setEditable(false); + resetTextField.setEditable(false); + + getContentPane().setLayout(new BorderLayout()); + + JButton updateButton = new JButton("Update"); + updateButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + updateLabels(); + } + }); + + JButton resetButton = new JButton("Reset"); + resetButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + cycleReset = cpu.cycles; + updateLabels(); + } + }); + + JPanel controlPanel = new JPanel(new GridLayout(2,3,5,5)); + controlPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + controlPanel.add(new JLabel("Total cycles:")); + controlPanel.add(cycleTextField); + controlPanel.add(updateButton); + controlPanel.add(new JLabel("Since reset:")); + controlPanel.add(resetTextField); + controlPanel.add(resetButton); + + add(BorderLayout.CENTER, controlPanel); + + setSize(370, 100); + + simulation.addObserver(simObserver = new Observer() { + public void update(Observable obs, Object obj) { + updateLabels(); + } + }); + + updateLabels(); + + // Tries to select this plugin + try { + setSelected(true); + } catch (java.beans.PropertyVetoException e) { + // Could not select + } + } + + private void updateLabels() { + cycleTextField.setText("" + cpu.cycles); + resetTextField.setText("" + (cpu.cycles - cycleReset)); + } + + public void closePlugin() { + simulation.deleteObserver(simObserver); + } + + +} diff --git a/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspStackWatcher.java b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspStackWatcher.java new file mode 100644 index 000000000..6a46b3093 --- /dev/null +++ b/tools/cooja/apps/mspsim/src/se/sics/cooja/mspmote/plugins/MspStackWatcher.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: MspStackWatcher.java,v 1.1 2008/02/07 14:55:18 fros4943 Exp $ + */ + +package se.sics.cooja.mspmote.plugins; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; +import org.apache.log4j.Logger; + +import se.sics.cooja.*; +import se.sics.cooja.mspmote.MspMote; +import se.sics.mspsim.core.MSP430; +import se.sics.mspsim.util.StackUI; +import se.sics.mspsim.util.Utils; + +@ClassDescription("Msp Stack Watcher") +@PluginType(PluginType.MOTE_PLUGIN) +public class MspStackWatcher extends VisPlugin { + private static Logger logger = Logger.getLogger(MspStackWatcher.class); + private MspMote mspMote; + private MSP430 cpu; + private Simulation simulation; + private Observer stackObserver = null; + private Observer logObserver = null; + private JButton startButton; + private JButton stopButton; + + public MspStackWatcher(Mote mote, Simulation simulationToVisualize, GUI gui) { + super("Msp Stack Watcher", gui); + this.mspMote = (MspMote) mote; + cpu = mspMote.getCPU(); + simulation = simulationToVisualize; + + getContentPane().setLayout(new BorderLayout()); + + // Register as stack observable + if (stackObserver == null) { + mspMote.getStackOverflowObservable().addObserver(stackObserver = new Observer() { + public void update(Observable obs, Object obj) { + simulation.stopSimulation(); + JOptionPane.showMessageDialog( + MspStackWatcher.this, + "Bad memory access!\nSimulation stopped.\n" + + "\nCurrent stack pointer = 0x" + Utils.hex16(cpu.reg[MSP430.SP]) + + "\nStart of heap = 0x" + Utils.hex16(cpu.getDisAsm().getMap().heapStartAddress), + "Stack overflow", JOptionPane.ERROR_MESSAGE + ); + } + }); + } + + // Create stack overflow controls + startButton = new JButton("Stop simulation on stack overflow"); + startButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + startButton.setEnabled(false); + stopButton.setEnabled(true); + + mspMote.monitorStack(true); + } + }); + + stopButton = new JButton("Cancel"); + stopButton.setEnabled(false); + stopButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + startButton.setEnabled(true); + stopButton.setEnabled(false); + + mspMote.monitorStack(false); + } + }); + + // Create nfi's stack viewer + final StackUI stackUI = new StackUI(cpu, MspMote.NR_CYCLES_PER_MSEC); + + // Register as log listener + if (logObserver == null && mspMote.getInterfaces().getLog() != null) { + mspMote.getInterfaces().getLog().addObserver(logObserver = new Observer() { + public void update(Observable obs, Object obj) { + stackUI.addNote(mspMote.getInterfaces().getLog().getLastLogMessages()); + } + }); + } + + JPanel controlPanel = new JPanel(new GridLayout(2,1)); + controlPanel.add(startButton); + controlPanel.add(stopButton); + + add(BorderLayout.CENTER, stackUI); + add(BorderLayout.SOUTH, controlPanel); + + setSize(240, 300); + + // Tries to select this plugin + try { + setSelected(true); + } catch (java.beans.PropertyVetoException e) { + // Could not select + } + } + + public void closePlugin() { + mspMote.getStackOverflowObservable().deleteObserver(stackObserver); + } + + +}