supporting multiple path substituion rules in mspcodewatcher
This commit is contained in:
parent
65b5fd0dde
commit
0c94b567b9
1 changed files with 390 additions and 216 deletions
|
@ -32,12 +32,15 @@ package se.sics.cooja.mspmote.plugins;
|
|||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
|
@ -47,16 +50,17 @@ import javax.swing.Box;
|
|||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
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.JTable;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jdom.Element;
|
||||
|
@ -72,6 +76,7 @@ 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.dialogs.MessageList;
|
||||
import se.sics.cooja.mspmote.MspMote;
|
||||
import se.sics.cooja.mspmote.MspMoteType;
|
||||
import se.sics.mspsim.core.EmulationException;
|
||||
|
@ -103,11 +108,14 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
private WatchpointListener watchpointListener;
|
||||
|
||||
private JComboBox fileComboBox;
|
||||
private String[] debugInfoMap = null;
|
||||
private File[] sourceFiles;
|
||||
|
||||
private JTabbedPane mainPane;
|
||||
|
||||
private ArrayList<Rule> rules;
|
||||
private ELFDebug debug;
|
||||
private String[] debugSourceFiles;
|
||||
|
||||
/**
|
||||
* Mini-debugger for MSP Motes.
|
||||
* Visualizes instructions, source code and allows a user to manipulate breakpoints.
|
||||
|
@ -121,6 +129,21 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
simulation = simulationToVisualize;
|
||||
this.mspMote = (MspMote) mote;
|
||||
this.watchpointMote = (WatchpointMote) mote;
|
||||
try {
|
||||
debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
|
||||
if (debug == null) {
|
||||
throw new RuntimeException("No debugging info found in firmware, aborting");
|
||||
}
|
||||
debugSourceFiles = debug.getSourceFiles();
|
||||
if (debugSourceFiles == null) {
|
||||
throw new RuntimeException("No debugging info found in firmware, aborting");
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
throw new RuntimeException("No debugging info found in firmware, aborting");
|
||||
}
|
||||
rules = new ArrayList<Rule>();
|
||||
|
||||
loadDefaultRules();
|
||||
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
|
||||
|
@ -150,7 +173,6 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
return this;
|
||||
}
|
||||
});
|
||||
updateFileComboBox();
|
||||
|
||||
/* Browse code control (north) */
|
||||
Box sourceCodeControl = Box.createHorizontalBox();
|
||||
|
@ -223,8 +245,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
showCurrentPC();
|
||||
}
|
||||
|
||||
public void startPlugin() {
|
||||
super.startPlugin();
|
||||
updateFileComboBox();
|
||||
}
|
||||
|
||||
private void updateFileComboBox() {
|
||||
sourceFiles = getSourceFiles(mspMote, debugInfoMap);
|
||||
sourceFiles = getSourceFiles(mspMote, rules);
|
||||
fileComboBox.removeAllItems();
|
||||
fileComboBox.addItem("[view sourcefile]");
|
||||
for (File f: sourceFiles) {
|
||||
|
@ -288,39 +315,22 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
currentCodeFile = null;
|
||||
|
||||
try {
|
||||
ELFDebug debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
|
||||
if (debug == null) {
|
||||
return;
|
||||
}
|
||||
int pc = mspMote.getCPU().getPC();
|
||||
DebugInfo debugInfo = debug.getDebugInfo(pc);
|
||||
if (pc <= 0) {
|
||||
return;
|
||||
}
|
||||
if (debugInfo == null) {
|
||||
if (pc != 0) {
|
||||
logger.warn("No sourcecode info at " + String.format("0x%04x", mspMote.getCPU().getPC()));
|
||||
logger.warn("No source info at " + String.format("0x%04x", pc));
|
||||
return;
|
||||
}
|
||||
File f = applySubstitutionRules(new File(debugInfo.getPath(), debugInfo.getFile()), rules);
|
||||
if (f == null || !f.exists()) {
|
||||
logger.warn("Unknown source at " + String.format("0x%04x", pc) + ": " + debugInfo.getPath() + " " + debugInfo.getFile());
|
||||
return;
|
||||
}
|
||||
|
||||
String path = new File(debugInfo.getPath(), debugInfo.getFile()).getPath();
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
path = path.replace('\\', '/');
|
||||
|
||||
/* Debug info to source file map */
|
||||
if (debugInfoMap != null && debugInfoMap.length == 2) {
|
||||
if (path.startsWith(debugInfoMap[0])) {
|
||||
path = debugInfoMap[1] + path.substring(debugInfoMap[0].length());
|
||||
}
|
||||
}
|
||||
|
||||
/* Nasty Cygwin-Windows fix */
|
||||
if (path.length() > 10 && path.startsWith("/cygdrive/")) {
|
||||
char driveCharacter = path.charAt(10);
|
||||
path = driveCharacter + ":/" + path.substring(11);
|
||||
}
|
||||
|
||||
currentCodeFile = new File(path).getCanonicalFile();
|
||||
currentCodeFile = f;
|
||||
currentLineNumber = debugInfo.getLine();
|
||||
} catch (Exception e) {
|
||||
logger.fatal("Exception: " + e.getMessage(), e);
|
||||
|
@ -329,147 +339,332 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
}
|
||||
}
|
||||
|
||||
private void tryMapDebugInfo() {
|
||||
final String[] debugFiles;
|
||||
try {
|
||||
private int getLocatedSourcesCount() {
|
||||
File files[] = getSourceFiles(mspMote, rules);
|
||||
if (files == null) {
|
||||
return 0;
|
||||
}
|
||||
return files.length;
|
||||
}
|
||||
|
||||
ELFDebug debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
|
||||
debugFiles = debug != null ? debug.getSourceFiles() : null;
|
||||
if (debugFiles == null) {
|
||||
logger.fatal("Error: No debug information is available");
|
||||
return;
|
||||
private void updateRulesUsage() {
|
||||
for (Rule rule: rules) {
|
||||
rule.prefixMatches = 0;
|
||||
rule.locatesFile = 0;
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
logger.fatal("Error: " + e1.getMessage(), e1);
|
||||
return;
|
||||
getSourceFiles(mspMote, rules);
|
||||
rulesMatched = new int[rules.size()];
|
||||
rulesOK = new int[rules.size()];
|
||||
for (int i=0; i < rules.size(); i++) {
|
||||
rulesMatched[i] = rules.get(i).prefixMatches;
|
||||
rulesOK[i] = rules.get(i).locatesFile;
|
||||
}
|
||||
String[] map = new RunnableInEDT<String[]>() {
|
||||
public String[] work() {
|
||||
/* Select which source file to use */
|
||||
int counter = 0, n;
|
||||
File correspondingFile = null;
|
||||
while (true) {
|
||||
n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(),
|
||||
"Choose which source file to manually locate.\n\n" +
|
||||
"Some source files may not exist, as debug info is also inherited from the toolchain.\n" +
|
||||
"\"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,
|
||||
new String[] { "Next File", "Locate File", "Cancel"}, "Next File");
|
||||
if (n == JOptionPane.CANCEL_OPTION || n == JOptionPane.CLOSED_OPTION) {
|
||||
}
|
||||
|
||||
private class Rule {
|
||||
String from = "";
|
||||
String to = "";
|
||||
int prefixMatches = 0;
|
||||
int locatesFile = 0;
|
||||
public Rule(String from, String to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
File apply(File f) {
|
||||
if (f == null) {
|
||||
return null;
|
||||
}
|
||||
if (n == JOptionPane.NO_OPTION) {
|
||||
/* Locate file */
|
||||
final String filename = new File(debugFiles[counter]).getName();
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setFileFilter(new FileFilter() {
|
||||
public boolean accept(File file) {
|
||||
if (file.isDirectory()) { return true; }
|
||||
if (file.getName().equals(filename)) {
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
if (!f.getPath().startsWith(from)) {
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " does not match: " + f.getPath(), MessageList.ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
prefixMatches++;
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " testing on: " + f.getPath());
|
||||
}
|
||||
if (to == null) {
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " enter substition: " + f.getPath(), MessageList.ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
File file = new File(to + f.getPath().substring(from.length()));
|
||||
if (!file.exists()) {
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " not found: " + file.getPath(), MessageList.ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " OK: " + f.getPath());
|
||||
}
|
||||
locatesFile++;
|
||||
return file;
|
||||
}
|
||||
public String toString() {
|
||||
return "[" + from + "|" + to + "]";
|
||||
}
|
||||
}
|
||||
|
||||
private static File applySubstitutionRules(String file, Collection<Rule> rules) {
|
||||
return applySubstitutionRules(new File(file), rules);
|
||||
}
|
||||
private static File applySubstitutionRules(File file, Collection<Rule> rules) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
for (Rule rule: rules) {
|
||||
File f = rule.apply(file);
|
||||
if (f != null && f.exists()) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void loadDefaultRules() {
|
||||
String rulesString = GUI.getExternalToolsSetting("MSPCODEWATCHER_RULES", "/cygdrive/c/*c:/");
|
||||
String[] rulesArr = rulesString.split("\\*");
|
||||
rules.clear();
|
||||
for (int i=0; i < rulesArr.length/2; i++) {
|
||||
Rule r = new Rule(rulesArr[i*2], rulesArr[i*2+1]);
|
||||
if (r.from.equals("[empty]")) {
|
||||
r.from = "";
|
||||
}
|
||||
if (r.to.equals("[empty]")) {
|
||||
r.to = "";
|
||||
}
|
||||
rules.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageList rulesDebuggingOutput = new MessageList();
|
||||
private boolean rulesWithDebuggingOutput = false;
|
||||
private int[] rulesMatched = null;
|
||||
private int[] rulesOK = null;
|
||||
private JTable table = null;
|
||||
private void tryMapDebugInfo() {
|
||||
/* called from AWT */
|
||||
int r = JOptionPane.showConfirmDialog(GUI.getTopParentContainer(),
|
||||
"The firmware file " + mspMote.getType().getContikiFirmwareFile().getName() + " references " + debugSourceFiles.length + " source files.\n" +
|
||||
"This function tries to locate these files on disk with a set of simple substitution rules.\n" +
|
||||
"\n" +
|
||||
"Right now " + getLocatedSourcesCount() + "/" + debugSourceFiles.length + " source files can be found.",
|
||||
"Locate source files", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (r != JOptionPane.OK_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* table with rules */
|
||||
rulesDebuggingOutput.clearMessages();
|
||||
final JDialog dialog = new JDialog((Window)GUI.getTopParentContainer(), "Locate source files");
|
||||
dialog.setModal(true);
|
||||
updateRulesUsage();
|
||||
AbstractTableModel model = new AbstractTableModel() {
|
||||
public int getRowCount() {
|
||||
return 10;
|
||||
}
|
||||
public int getColumnCount() {
|
||||
return 4;
|
||||
}
|
||||
public String getColumnName(int col) {
|
||||
if (col == 0) {
|
||||
return "Prefix";
|
||||
}
|
||||
if (col == 1) {
|
||||
return "Substitution";
|
||||
}
|
||||
if (col == 2) {
|
||||
return "Matched";
|
||||
}
|
||||
if (col == 3) {
|
||||
return "OK";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (rowIndex < 0 || rowIndex >= rules.size()) {
|
||||
return null;
|
||||
}
|
||||
Rule rule = rules.get(rowIndex);
|
||||
if (columnIndex == 0) {
|
||||
return rule.from;
|
||||
}
|
||||
if (columnIndex == 1) {
|
||||
return rule.to;
|
||||
}
|
||||
if (columnIndex == 2) {
|
||||
if (rulesMatched == null || rowIndex >= rulesMatched.length) {
|
||||
return "[click Apply]";
|
||||
}
|
||||
return rulesMatched[rowIndex];
|
||||
}
|
||||
if (columnIndex == 3) {
|
||||
if (rulesOK == null || rowIndex >= rulesOK.length) {
|
||||
return "[click Apply]";
|
||||
}
|
||||
return rulesOK[rowIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
if (column == 0) {
|
||||
return true;
|
||||
}
|
||||
if (column == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Source file " + filename;
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
Rule rule;
|
||||
if (rowIndex < 0) {
|
||||
return;
|
||||
}
|
||||
if (rowIndex < rules.size()) {
|
||||
rule = rules.get(rowIndex);
|
||||
} else {
|
||||
do {
|
||||
rule = new Rule("", "");
|
||||
rules.add(rule);
|
||||
} while (rowIndex >= rules.size());
|
||||
}
|
||||
if (columnIndex == 0) {
|
||||
rule.from = (String) aValue;
|
||||
}
|
||||
if (columnIndex == 1) {
|
||||
rule.to = (String) aValue;
|
||||
}
|
||||
rulesMatched = null;
|
||||
rulesOK = null;
|
||||
table.invalidate();
|
||||
table.repaint();
|
||||
}
|
||||
};
|
||||
table = new JTable(model);
|
||||
|
||||
/* control panel: save/load, clear/apply/close */
|
||||
final JButton applyButton = new JButton("Apply");
|
||||
applyButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
/* Remove trailing empty rules */
|
||||
ArrayList<Rule> trimmedRules = new ArrayList<Rule>();
|
||||
for (Rule rule: rules) {
|
||||
if (rule.from == null || rule.from.trim().isEmpty()) {
|
||||
rule.from = "";
|
||||
}
|
||||
if (rule.to == null || rule.to.trim().isEmpty()) {
|
||||
rule.to = "";
|
||||
}
|
||||
if (rule.from.isEmpty() && rule.to.isEmpty()) {
|
||||
/* ignore */
|
||||
continue;
|
||||
}
|
||||
trimmedRules.add(rule);
|
||||
}
|
||||
rules = trimmedRules;
|
||||
|
||||
rulesDebuggingOutput.clearMessages();
|
||||
rulesDebuggingOutput.addMessage("Applying " + rules.size() + " substitution rules");
|
||||
rulesWithDebuggingOutput = true;
|
||||
updateRulesUsage();
|
||||
rulesWithDebuggingOutput = false;
|
||||
rulesDebuggingOutput.addMessage("Done! " + "Located sources: " + getLocatedSourcesCount() + "/" + debugSourceFiles.length);
|
||||
rulesDebuggingOutput.addMessage(" ");
|
||||
for (String s: debugSourceFiles) {
|
||||
File f = applySubstitutionRules(s, rules);
|
||||
if (f == null || !f.exists()) {
|
||||
rulesDebuggingOutput.addMessage("Not yet located: " + s, MessageList.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
table.invalidate();
|
||||
table.repaint();
|
||||
}
|
||||
});
|
||||
fc.setCurrentDirectory(new File(GUI.getExternalToolsSetting("PATH_CONTIKI", ".")));
|
||||
int returnVal = fc.showOpenDialog(GUI.getTopParentContainer());
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
correspondingFile = fc.getSelectedFile();
|
||||
break;
|
||||
JButton clearButton = new JButton("Clear");
|
||||
clearButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
rules.clear();
|
||||
applyButton.doClick();
|
||||
}
|
||||
});
|
||||
|
||||
JButton loadButton = new JButton("Load default");
|
||||
loadButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
loadDefaultRules();
|
||||
applyButton.doClick();
|
||||
}
|
||||
});
|
||||
JButton saveButton = new JButton("Save as default");
|
||||
saveButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Rule r: rules) {
|
||||
sb.append("*");
|
||||
if (r.from.isEmpty()) {
|
||||
sb.append("[empty]");
|
||||
} else {
|
||||
sb.append(r.from);
|
||||
}
|
||||
sb.append("*");
|
||||
if (r.to.isEmpty()) {
|
||||
sb.append("[empty]");
|
||||
} else {
|
||||
sb.append(r.to);
|
||||
}
|
||||
}
|
||||
|
||||
if (n == JOptionPane.YES_OPTION) {
|
||||
/* Next file */
|
||||
counter = (counter+1) % debugFiles.length;
|
||||
if (sb.length() >= 1) {
|
||||
GUI.setExternalToolsSetting("MSPCODEWATCHER_RULES", sb.substring(1));
|
||||
} else {
|
||||
GUI.setExternalToolsSetting("MSPCODEWATCHER_RULES", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Match files */
|
||||
try {
|
||||
String canonDebug = debugFiles[counter];
|
||||
String canonSelected = correspondingFile.getCanonicalFile().getPath().replace('\\', '/');
|
||||
|
||||
int offset = 0;
|
||||
while (canonDebug.regionMatches(
|
||||
true,
|
||||
canonDebug.length()-offset,
|
||||
canonSelected, canonSelected.length()-offset,
|
||||
offset)) {
|
||||
offset++;
|
||||
if (offset >= canonDebug.length() ||
|
||||
offset >= canonSelected.length())
|
||||
break;
|
||||
}
|
||||
offset--;
|
||||
String replace = canonDebug.substring(0, canonDebug.length() - offset);
|
||||
String replacement = canonSelected.substring(0, canonSelected.length() - offset);
|
||||
|
||||
{
|
||||
JTextField replaceInput = new JTextField(replace);
|
||||
replaceInput.setEditable(true);
|
||||
JTextField replacementInput = new JTextField(replacement);
|
||||
replacementInput.setEditable(true);
|
||||
|
||||
Box box = Box.createVerticalBox();
|
||||
box.add(new JLabel("Debug info file:"));
|
||||
box.add(new JLabel(canonDebug));
|
||||
box.add(new JLabel("Selected file:"));
|
||||
box.add(new JLabel(canonSelected));
|
||||
box.add(Box.createVerticalStrut(20));
|
||||
box.add(new JLabel("Replacing:"));
|
||||
box.add(replaceInput);
|
||||
box.add(new JLabel("with:"));
|
||||
box.add(replacementInput);
|
||||
|
||||
JOptionPane optionPane = new JOptionPane();
|
||||
optionPane.setMessage(box);
|
||||
optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
|
||||
optionPane.setOptions(new String[] { "OK" });
|
||||
optionPane.setInitialValue("OK");
|
||||
JDialog dialog = optionPane.createDialog(
|
||||
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 };
|
||||
} catch (IOException e) {
|
||||
logger.fatal("Error: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}.invokeAndWait();
|
||||
|
||||
if (map != null) {
|
||||
debugInfoMap = map;
|
||||
JButton closeButton = new JButton("Close");
|
||||
closeButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
updateFileComboBox();
|
||||
dialog.dispose();
|
||||
}
|
||||
});
|
||||
Box control = Box.createHorizontalBox();
|
||||
control.add(loadButton);
|
||||
control.add(saveButton);
|
||||
control.add(Box.createHorizontalGlue());
|
||||
control.add(clearButton);
|
||||
control.add(applyButton);
|
||||
control.add(closeButton);
|
||||
|
||||
final JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
|
||||
new JScrollPane(table),
|
||||
new JScrollPane(rulesDebuggingOutput));
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
split.setDividerLocation(0.5);
|
||||
applyButton.doClick();
|
||||
}
|
||||
});
|
||||
dialog.getContentPane().add(BorderLayout.CENTER, split);
|
||||
dialog.getContentPane().add(BorderLayout.SOUTH, control);
|
||||
dialog.getRootPane().setDefaultButton(closeButton);
|
||||
dialog.setSize(550, 500);
|
||||
dialog.setLocationRelativeTo(GUI.getTopParentContainer());
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private static File[] getSourceFiles(MspMote mote, String[] map) {
|
||||
final String[] sourceFiles;
|
||||
try {
|
||||
ELFDebug debug = ((MspMoteType)mote.getType()).getELF().getDebug();
|
||||
sourceFiles = debug != null ? debug.getSourceFiles() : null;
|
||||
if (sourceFiles == null) {
|
||||
logger.fatal("Error: No debug information is available");
|
||||
return new File[0];
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
logger.fatal("Error: " + e1.getMessage(), e1);
|
||||
return null;
|
||||
}
|
||||
private File[] getSourceFiles(MspMote mote, ArrayList<Rule> rules) {
|
||||
File contikiSource = mote.getType().getContikiSourceFile();
|
||||
if (contikiSource != null) {
|
||||
try {
|
||||
|
@ -480,50 +675,25 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
|
||||
/* Verify that files exist */
|
||||
ArrayList<File> existing = new ArrayList<File>();
|
||||
for (String sourceFile: sourceFiles) {
|
||||
|
||||
for (String sourceFile: debugSourceFiles) {
|
||||
/* Debug info to source file map */
|
||||
sourceFile = sourceFile.replace('\\', '/');
|
||||
if (map != null && map.length == 2) {
|
||||
if (sourceFile.startsWith(map[0])) {
|
||||
sourceFile = map[1] + sourceFile.substring(map[0].length());
|
||||
}
|
||||
}
|
||||
|
||||
/* Nasty Cygwin-Windows fix */
|
||||
if (sourceFile.length() > 10 && sourceFile.contains("/cygdrive/")) {
|
||||
char driveCharacter = sourceFile.charAt(10);
|
||||
sourceFile = driveCharacter + ":/" + sourceFile.substring(11);
|
||||
}
|
||||
|
||||
File file = new File(sourceFile);
|
||||
try {
|
||||
file = file.getCanonicalFile();
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
if (!GUI.isVisualizedInApplet()) {
|
||||
if (file.exists() && file.isFile()) {
|
||||
existing.add(file);
|
||||
} else {
|
||||
/*logger.warn("Can't locate source file, skipping: " + file.getPath());*/
|
||||
}
|
||||
} else {
|
||||
/* Accept all files without existence check */
|
||||
existing.add(file);
|
||||
File f = applySubstitutionRules(sourceFile, rules);
|
||||
if (f != null && f.exists()) {
|
||||
existing.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
/* If no files were found, suggest map function */
|
||||
if (sourceFiles.length > 0 && existing.isEmpty() && GUI.isVisualized()) {
|
||||
if (debugSourceFiles.length > 0 && existing.isEmpty() && GUI.isVisualized()) {
|
||||
new RunnableInEDT<Boolean>() {
|
||||
public Boolean work() {
|
||||
JOptionPane.showMessageDialog(
|
||||
GUI.getTopParentContainer(),
|
||||
"The firmware debug info specifies " + sourceFiles.length + " source files.\n" +
|
||||
"However, Msp Code Watcher could not find any of these files.\n" +
|
||||
"The firmware debug info specifies " + debugSourceFiles.length + " source files.\n" +
|
||||
"However, MspCodeWatcher could not find any of these files.\n" +
|
||||
"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.",
|
||||
"If you want to manually locate the sources, click the \"Locate sources\" button.",
|
||||
"No source files found",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return true;
|
||||
|
@ -532,23 +702,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
}
|
||||
|
||||
/* Sort alphabetically */
|
||||
ArrayList<File> sorted = new ArrayList<File>();
|
||||
for (File file: existing) {
|
||||
int index = 0;
|
||||
for (index=0; index < sorted.size(); index++) {
|
||||
if (file.getName().compareToIgnoreCase(sorted.get(index).getName()) < 0) {
|
||||
break;
|
||||
File[] sorted = existing.toArray(new File[0]);
|
||||
Arrays.sort(sorted, new Comparator<File>() {
|
||||
public int compare(File o1, File o2) {
|
||||
return o1.getName().compareToIgnoreCase(o2.getName());
|
||||
}
|
||||
}
|
||||
sorted.add(index, file);
|
||||
}
|
||||
|
||||
/* Add Contiki source first */
|
||||
if (contikiSource != null && contikiSource.exists()) {
|
||||
sorted.add(0, contikiSource);
|
||||
}
|
||||
|
||||
return sorted.toArray(new File[0]);
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
public Collection<Element> getConfigXML() {
|
||||
|
@ -559,13 +719,27 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
element.addContent("" + mainPane.getSelectedIndex());
|
||||
config.add(element);
|
||||
|
||||
for (Rule rule: rules) {
|
||||
element = new Element("rule");
|
||||
element.setAttribute("from", rule.from==null?"":rule.from);
|
||||
element.setAttribute("to", rule.to==null?"":rule.to);
|
||||
config.add(element);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
|
||||
boolean clearedRules = false;
|
||||
for (Element element : configXML) {
|
||||
if (element.getName().equals("tab")) {
|
||||
mainPane.setSelectedIndex(Integer.parseInt(element.getText()));
|
||||
} else if (element.getName().equals("rule")) {
|
||||
if (!clearedRules) {
|
||||
rules.clear();
|
||||
clearedRules = true;
|
||||
}
|
||||
rules.add(new Rule(element.getAttributeValue("from"), element.getAttributeValue("to")));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -582,7 +756,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
|||
}
|
||||
};
|
||||
|
||||
private AbstractAction mapAction = new AbstractAction("Locate sources") {
|
||||
private AbstractAction mapAction = new AbstractAction("Locate sources...") {
|
||||
private static final long serialVersionUID = -3929432830596292495L;
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
|
Loading…
Reference in a new issue