/*
* 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.
*
*/
package org.contikios.cooja.mspmote.plugins;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import org.apache.log4j.Logger;
import org.jdom.Element;
import org.contikios.cooja.ClassDescription;
import org.contikios.cooja.Cooja;
import org.contikios.cooja.Mote;
import org.contikios.cooja.MotePlugin;
import org.contikios.cooja.MoteTimeEvent;
import org.contikios.cooja.PluginType;
import org.contikios.cooja.Simulation;
import org.contikios.cooja.SupportedArguments;
import org.contikios.cooja.VisPlugin;
import org.contikios.cooja.mspmote.MspMote;
import org.contikios.cooja.mspmote.MspMoteType;
import se.sics.mspsim.core.MSP430;
import se.sics.mspsim.core.Memory.AccessMode;
import se.sics.mspsim.core.RegisterMonitor;
import se.sics.mspsim.ui.StackUI;
@ClassDescription("Msp Stack Watcher")
@PluginType(PluginType.MOTE_PLUGIN)
@SupportedArguments(motes = { MspMote.class })
public class MspStackWatcher extends VisPlugin implements MotePlugin {
private static Logger logger = Logger.getLogger(MspStackWatcher.class);
private Simulation simulation;
private MSP430 cpu;
private MspMote mspMote;
private StackUI stackUI;
private RegisterMonitor.Adapter registerMonitor = null;
private JToggleButton toggleButton;
private MoteTimeEvent increasePosTimeEvent;
private Integer userOverriddenStack = null;
private JLabel memLabel = new JLabel("");
public MspStackWatcher(Mote mote, Simulation simulationToVisualize, Cooja gui) {
super("Msp Stack Watcher: " + mote, gui);
this.mspMote = (MspMote) mote;
cpu = mspMote.getCPU();
simulation = simulationToVisualize;
getContentPane().setLayout(new BorderLayout());
toggleButton = new JToggleButton("Click to monitor for stack overflows");
toggleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (toggleButton.isSelected()) {
toggleButton.setText("Monitoring for stack overflows");
if (!activate(true)) {
toggleButton.setBackground(Color.RED);
toggleButton.setText("Monitoring for stack overflows - FAILED!");
toggleButton.setSelected(false);
}
toggleButton.setBackground(null);
} else {
toggleButton.setBackground(null);
toggleButton.setText("Click to monitor for stack overflows");
deactivate();
}
}
});
/* Create Mspsim stack viewer */
stackUI = new StackUI(cpu, -1); /* Needs manual updates */
stackUI.init("Stack usage", mspMote.registry);
stackUI.start();
increasePosTimeEvent = new MoteTimeEvent(mspMote, 0) {
public void execute(long t) {
stackUI.requestIncreasePos();
simulation.scheduleEvent(this, t + Simulation.MILLISECOND);
}
};
simulation.scheduleEvent(increasePosTimeEvent, simulation.getSimulationTime());
add(BorderLayout.NORTH, memLabel);
add(BorderLayout.CENTER, stackUI);
add(BorderLayout.SOUTH, toggleButton);
setSize(400, 300);
}
private boolean activate(boolean gui) {
try {
int stack = ((MspMoteType) mspMote.getType()).getELF().getMap().stackStartAddress;
if (gui) {
String s = (String)JOptionPane.showInputDialog(
Cooja.getTopParentContainer(),
"With default linker scripts the stack starts at 0x" + Integer.toHexString(stack) + ".\n" +
"If you are using a modified linker script, you may here correct the stack start address.",
"Enter stack start address",
JOptionPane.PLAIN_MESSAGE,
null,
null,
"0x" + Integer.toHexString(userOverriddenStack!=null?userOverriddenStack:stack));
userOverriddenStack = null;
if (s != null) {
try {
int newStack = Integer.decode(s);
if (newStack != stack) {
userOverriddenStack = newStack;
}
} catch (Exception e) {
logger.error("Error parsing provided stack address: " + s, e);
return false;
}
}
}
if (userOverriddenStack != null) {
stack = userOverriddenStack;
}
int heap = ((MspMoteType) mspMote.getType()).getELF().getMap().heapStartAddress;
if (stack < 0) {
stack = cpu.config.ramStart + cpu.config.ramSize;
}
logger.debug("SP starts at: 0x" + Integer.toHexString(stack));
logger.debug("Heap starts at: 0x" + Integer.toHexString(heap));
logger.debug("Available stack: " + (stack-heap) + " bytes");
memLabel.setText(String.format("Stack 0x%x, heap 0x%x", stack, heap));
if (stack < 0 || heap < 0) {
return false;
}
/*final int stackStartAddress = stack;*/
final int heapStartAddress = heap;
registerMonitor = new RegisterMonitor.Adapter() {
int min = Integer.MAX_VALUE;
public void notifyWriteBefore(int register, final int sp, AccessMode mode) {
/*logger.debug("SP is now: 0x" + Integer.toHexString(sp));*/
final int available = sp - heapStartAddress;
if (available < min) {
min = available;
String details = mspMote.getExecutionDetails();
if (details != null) {
logger.info(String.format(mspMote + ": Maximum stack usage: 0x%x, available stack 0x%x", sp, available));
logger.info(details);
}
}
if (available <= 0) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(Cooja.getTopParentContainer(),
String.format("Stack overflow!\n\n" +
"\tSP = 0x%05x\n" +
"\tHeap start = 0x%05x\n\n" +
"\tAvailable = %d\n", sp, heapStartAddress, available),
"Stack overflow on " + mspMote,
JOptionPane.ERROR_MESSAGE);
}
});
simulation.stopSimulation();
}
}
};
cpu.addRegisterWriteMonitor(MSP430.SP, registerMonitor);
} catch (IOException e) {
logger.warn("Stack monitoring failed: " + e.getMessage(), e);
registerMonitor = null;
return false;
}
return true;
}
private void deactivate() {
userOverriddenStack = null;
if (registerMonitor != null) {
cpu.removeRegisterWriteMonitor(MSP430.SP, registerMonitor);
registerMonitor = null;
}
}
public Collection<Element> getConfigXML() {
ArrayList<Element> config = new ArrayList<Element>();
Element element;
if (userOverriddenStack != null) {
element = new Element("stack");
element.setText("0x" + Integer.toHexString(userOverriddenStack));
config.add(element);
}
element = new Element("monitoring");
element.setText("" + toggleButton.isSelected());
config.add(element);
return config;
}
public boolean setConfigXML(Collection<Element> configXML,
boolean visAvailable) {
for (Element element : configXML) {
if (element.getName().equals("monitoring")) {
boolean monitor = Boolean.parseBoolean(element.getText());
if (monitor) {
if (activate(false)) {
toggleButton.setSelected(true);
}
}
} else if (element.getName().equals("stack")) {
userOverriddenStack = Integer.decode(element.getText());
}
}
return true;
}
public void closePlugin() {
increasePosTimeEvent.remove();
stackUI.stop();
deactivate();
}
public Mote getMote() {
return mspMote;
}
}