re-implemented stack overflow monitoring, and user can now manually override the stack addresses for use with
custom linker scripts
This commit is contained in:
parent
11d124882c
commit
8a084926e2
1 changed files with 182 additions and 72 deletions
|
@ -31,126 +31,236 @@
|
|||
package se.sics.cooja.mspmote.plugins;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jdom.Element;
|
||||
|
||||
import se.sics.cooja.ClassDescription;
|
||||
import se.sics.cooja.GUI;
|
||||
import se.sics.cooja.Mote;
|
||||
import se.sics.cooja.MotePlugin;
|
||||
import se.sics.cooja.MoteTimeEvent;
|
||||
import se.sics.cooja.PluginType;
|
||||
import se.sics.cooja.Simulation;
|
||||
import se.sics.cooja.SupportedArguments;
|
||||
import se.sics.cooja.VisPlugin;
|
||||
import se.sics.cooja.mspmote.MspMote;
|
||||
import se.sics.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;
|
||||
import se.sics.mspsim.util.Utils;
|
||||
|
||||
@ClassDescription("Msp Stack Watcher")
|
||||
@PluginType(PluginType.MOTE_PLUGIN)
|
||||
@SupportedArguments(motes = {MspMote.class})
|
||||
@SupportedArguments(motes = { MspMote.class })
|
||||
public class MspStackWatcher extends VisPlugin implements MotePlugin {
|
||||
private static Logger logger = Logger.getLogger(MspStackWatcher.class);
|
||||
|
||||
private MspMote mspMote;
|
||||
private MSP430 cpu;
|
||||
private StackUI stackUI;
|
||||
|
||||
private Simulation simulation;
|
||||
private Observer stackObserver = null;
|
||||
private JButton startButton;
|
||||
private JButton stopButton;
|
||||
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, GUI gui) {
|
||||
super("Msp Stack Watcher", gui);
|
||||
super("Msp Stack Watcher: " + mote, 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
|
||||
);
|
||||
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 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
|
||||
stackUI = new StackUI(cpu);
|
||||
stackUI.init("MSPSim stack", mspMote.registry);
|
||||
/* 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());
|
||||
|
||||
// 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().getLastLogMessage());
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
JPanel controlPanel = new JPanel(new GridLayout(2,1));
|
||||
controlPanel.add(startButton);
|
||||
controlPanel.add(stopButton);
|
||||
|
||||
add(BorderLayout.NORTH, memLabel);
|
||||
add(BorderLayout.CENTER, stackUI);
|
||||
add(BorderLayout.SOUTH, controlPanel);
|
||||
add(BorderLayout.SOUTH, toggleButton);
|
||||
|
||||
setSize(240, 300);
|
||||
setSize(400, 300);
|
||||
}
|
||||
|
||||
// Tries to select this plugin
|
||||
private boolean activate(boolean gui) {
|
||||
try {
|
||||
setSelected(true);
|
||||
} catch (java.beans.PropertyVetoException e) {
|
||||
// Could not select
|
||||
int stack = ((MspMoteType) mspMote.getType()).getELF().getMap().stackStartAddress;
|
||||
|
||||
if (gui) {
|
||||
String s = (String)JOptionPane.showInputDialog(
|
||||
GUI.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(GUI.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() {
|
||||
mspMote.getStackOverflowObservable().deleteObserver(stackObserver);
|
||||
increasePosTimeEvent.remove();
|
||||
stackUI.stop();
|
||||
deactivate();
|
||||
}
|
||||
|
||||
public Mote getMote() {
|
||||
|
|
Loading…
Reference in a new issue