aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorgdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2012-08-24 09:19:05 +0000
committergdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4>2012-08-24 09:19:05 +0000
commit5e4b039d56d93520f36f0c775e3cdc95c2f03e04 (patch)
tree6f23be1f25ce4a86f353aa75b1a44402cc6575e9 /tools
parent546a832f81364c1b8f8ed3fc0850d2b32a6cbffe (diff)
downloadChibiOS-5e4b039d56d93520f36f0c775e3cdc95c2f03e04.tar.gz
ChibiOS-5e4b039d56d93520f36f0c775e3cdc95c2f03e04.tar.bz2
ChibiOS-5e4b039d56d93520f36f0c775e3cdc95c2f03e04.zip
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@4621 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'tools')
-rw-r--r--tools/eclipse/debug_support/.classpath7
-rw-r--r--tools/eclipse/debug_support/.project28
-rw-r--r--tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs8
-rw-r--r--tools/eclipse/debug_support/META-INF/MANIFEST.MF14
-rw-r--r--tools/eclipse/debug_support/build.properties7
-rw-r--r--tools/eclipse/debug_support/contexts.xml12
-rw-r--r--tools/eclipse/debug_support/icons/sample.gifbin0 -> 983 bytes
-rw-r--r--tools/eclipse/debug_support/plugin.xml38
-rw-r--r--tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java68
-rw-r--r--tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java562
-rw-r--r--tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java14
-rw-r--r--tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java94
-rw-r--r--tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java565
-rw-r--r--tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java415
-rw-r--r--tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java447
15 files changed, 2279 insertions, 0 deletions
diff --git a/tools/eclipse/debug_support/.classpath b/tools/eclipse/debug_support/.classpath
new file mode 100644
index 000000000..8a8f1668c
--- /dev/null
+++ b/tools/eclipse/debug_support/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tools/eclipse/debug_support/.project b/tools/eclipse/debug_support/.project
new file mode 100644
index 000000000..eb8a22dc0
--- /dev/null
+++ b/tools/eclipse/debug_support/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>Tool Debug Support</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs b/tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..c63fde790
--- /dev/null
+++ b/tools/eclipse/debug_support/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+#Fri Jul 01 10:57:07 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/tools/eclipse/debug_support/META-INF/MANIFEST.MF b/tools/eclipse/debug_support/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..2908664cf
--- /dev/null
+++ b/tools/eclipse/debug_support/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: ChibiOS-RT_Debug_Support
+Bundle-SymbolicName: org.chibios.tools.eclipse.debug; singleton:=true
+Bundle-Version: 1.1.0
+Bundle-Activator: org.chibios.tools.eclipse.debug.activator.Activator
+Bundle-Vendor: chibios.org
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.cdt.debug.mi.core,
+ org.eclipse.debug.ui,
+ org.eclipse.cdt.debug.core
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ClassPath: .
diff --git a/tools/eclipse/debug_support/build.properties b/tools/eclipse/debug_support/build.properties
new file mode 100644
index 000000000..255e23b61
--- /dev/null
+++ b/tools/eclipse/debug_support/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+ META-INF/,\
+ .,\
+ icons/,\
+ contexts.xml
diff --git a/tools/eclipse/debug_support/contexts.xml b/tools/eclipse/debug_support/contexts.xml
new file mode 100644
index 000000000..02e26e45c
--- /dev/null
+++ b/tools/eclipse/debug_support/contexts.xml
@@ -0,0 +1,12 @@
+<contexts>
+ <context id="viewer" title="Sample View">
+ <description>This is the context help for the sample view with a table viewer. It was generated by a PDE template.</description>
+ <topic href="/PLUGINS_ROOT/org.eclipse.platform.doc.isv/guide/ua_help_context.htm" label="Context-sensitive help">
+ <enablement>
+ <with variable="platform">
+ <test property="org.eclipse.core.runtime.isBundleInstalled" args="org.eclipse.platform.doc.isv"/>
+ </with>
+ </enablement>
+ </topic>
+ </context>
+</contexts>
diff --git a/tools/eclipse/debug_support/icons/sample.gif b/tools/eclipse/debug_support/icons/sample.gif
new file mode 100644
index 000000000..34fb3c9d8
--- /dev/null
+++ b/tools/eclipse/debug_support/icons/sample.gif
Binary files differ
diff --git a/tools/eclipse/debug_support/plugin.xml b/tools/eclipse/debug_support/plugin.xml
new file mode 100644
index 000000000..b6102196c
--- /dev/null
+++ b/tools/eclipse/debug_support/plugin.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+
+ <extension
+ point="org.eclipse.ui.views">
+ <category
+ name="ChibiOS/RT"
+ id="org.chibios.tools.eclipse.debug">
+ </category>
+ <view
+ category="org.chibios.tools.eclipse.debug"
+ class="org.chibios.tools.eclipse.debug.views.ChibiView"
+ icon="icons/sample.gif"
+ id="org.chibios.tools.eclipse.debug.views.ChibiView"
+ name="ChibiOS/RT">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.jdt.ui.JavaPerspective">
+ <view
+ id="org.chibios.tools.eclipse.debug.views.ChibiView"
+ ratio="0.5"
+ relationship="right"
+ relative="org.eclipse.ui.views.TaskList">
+ </view>
+ </perspectiveExtension>
+ </extension>
+ <extension
+ point="org.eclipse.help.contexts">
+ <contexts
+ file="contexts.xml">
+ </contexts>
+ </extension>
+
+</plugin>
diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java
new file mode 100644
index 000000000..0e3b8d1e9
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/activator/Activator.java
@@ -0,0 +1,68 @@
+package org.chibios.tools.eclipse.debug.activator;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.chibios.tools.eclipse.debug"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+ * )
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+ * )
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Returns an image descriptor for the image file at the given plug-in
+ * relative path
+ *
+ * @param path
+ * the path
+ * @return the image descriptor
+ */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}
diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java
new file mode 100644
index 000000000..b673d9610
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxy.java
@@ -0,0 +1,562 @@
+package org.chibios.tools.eclipse.debug.utils;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
+import org.eclipse.cdt.debug.internal.core.model.CDebugTarget;
+import org.eclipse.cdt.debug.mi.core.MIException;
+import org.eclipse.cdt.debug.mi.core.MIFormat;
+import org.eclipse.cdt.debug.mi.core.MISession;
+import org.eclipse.cdt.debug.mi.core.cdi.model.Target;
+import org.eclipse.cdt.debug.mi.core.command.CommandFactory;
+import org.eclipse.cdt.debug.mi.core.command.MIDataEvaluateExpression;
+import org.eclipse.cdt.debug.mi.core.command.MIDataReadMemory;
+import org.eclipse.cdt.debug.mi.core.output.MIDataEvaluateExpressionInfo;
+import org.eclipse.cdt.debug.mi.core.output.MIDataReadMemoryInfo;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.model.IDebugTarget;
+
+@SuppressWarnings("restriction")
+public class DebugProxy {
+
+ private CommandFactory cmd_factory;
+ private MISession mi_session;
+
+ protected final static String[] threadStates = {
+ "READY",
+ "CURRENT",
+ "SUSPENDED",
+ "WTSEM",
+ "WTMTX",
+ "WTCOND",
+ "SLEEPING",
+ "WTEXIT",
+ "WTOREVT",
+ "WTANDEVT",
+ "SNDMSGQ",
+ "SNDMSG",
+ "WTMSG",
+ "WTQUEUE",
+ "FINAL"
+ };
+
+ private void getSession(CDebugTarget target)
+ throws DebugProxyException {
+ ICDITarget[] targets = target.getCDISession().getTargets();
+ ICDITarget cdi_target = null;
+ for (int i = 0; i < targets.length; i++) {
+ if (targets[i] instanceof Target) {
+ cdi_target = targets[i];
+ break;
+ }
+ }
+ if (cdi_target == null)
+ throw new DebugProxyException("no CDI session found");
+ mi_session = ((Target)cdi_target).getMISession();
+ cmd_factory = mi_session.getCommandFactory();
+ }
+
+ public DebugProxy()
+ throws DebugProxyException {
+ IDebugTarget[] targets = DebugPlugin.getDefault().getLaunchManager().getDebugTargets();
+ for (IDebugTarget target:targets) {
+ if(target instanceof CDebugTarget) {
+ getSession((CDebugTarget)target);
+ return;
+ }
+ }
+ }
+
+ public DebugProxy(CDebugTarget target)
+ throws DebugProxyException {
+ getSession(target);
+ }
+
+ public String evaluateExpression(String expression)
+ throws DebugProxyException {
+ if (mi_session.getMIInferior().isRunning())
+ return null;
+ MIDataEvaluateExpression expr = cmd_factory.createMIDataEvaluateExpression(expression);
+ try {
+ mi_session.postCommand(expr);
+ MIDataEvaluateExpressionInfo info = expr.getMIDataEvaluateExpressionInfo();
+ if (info != null)
+ return info.getExpression();
+ } catch (MIException e) {}
+ throw new DebugProxyException("error evaluating the expression: '" +
+ expression + "'");
+ }
+
+ public long scanStack(long base, long end, long pattern)
+ throws DebugProxyException {
+ if (mi_session.getMIInferior().isRunning())
+ return -1;
+ if (end > base) {
+ MIDataReadMemory mem = cmd_factory.createMIDataReadMemory(0,
+ Long.toString(base),
+ MIFormat.HEXADECIMAL,
+ 4,
+ 1,
+ (int)(end - base),
+ '.');
+ try {
+ mi_session.postCommand(mem);
+ MIDataReadMemoryInfo info = mem.getMIDataReadMemoryInfo();
+ if (info != null) {
+ long[] data = info.getMemories()[0].getData();
+ int i = 0;
+ while ((i < data.length) && (data[i] == pattern))
+ i++;
+ return i * 4;
+ }
+ } catch (MIException e) {}
+ throw new DebugProxyException("error reading memory at " +
+ base);
+ }
+ return 0;
+ }
+
+ public String readCString(long address, int max)
+ throws DebugProxyException {
+ if (mi_session.getMIInferior().isRunning())
+ return null;
+ MIDataReadMemory mem = cmd_factory.createMIDataReadMemory(0,
+ Long.toString(address),
+ MIFormat.HEXADECIMAL,
+ 1,
+ 1,
+ max,
+ '.');
+ try {
+ mi_session.postCommand(mem);
+ MIDataReadMemoryInfo info = mem.getMIDataReadMemoryInfo();
+ if (info != null) {
+ String s = info.getMemories()[0].getAscii();
+ int i = s.indexOf('.');
+ if (i >= 0)
+ return s.substring(0, s.indexOf('.'));
+ else
+ return s;
+ }
+ } catch (MIException e) {}
+ throw new DebugProxyException("error reading memory at " +
+ address);
+ }
+
+ /**
+ * @brief Return the list of threads.
+ * @details The threads list is fetched from memory by scanning the
+ * registry.
+ *
+ * @return A @p LinkedHashMap object whose keys are the threads addresses
+ * as decimal strings, the value is an @p HashMap of the thread
+ * fields:
+ * - stack
+ * - stklimit
+ * - name
+ * - state
+ * - state_s
+ * - flags
+ * - prio
+ * - refs
+ * - time
+ * - wtobjp
+ * .
+ * Missing fields are set to "-".
+ * @retval null If the debugger encountered an error or
+ * the target is running.
+ *
+ * @throws DebugProxyException If the debugger is active but the registry
+ * is not found, not initialized or corrupted.
+ */
+ public LinkedHashMap<String, HashMap<String, String>> readThreads()
+ throws DebugProxyException {
+ // rlist structure address.
+ String rlist;
+ try {
+ rlist = evaluateExpression("(uint32_t)&rlist");
+ if (rlist == null)
+ return null;
+ } catch (DebugProxyException e) {
+ throw new DebugProxyException("ChibiOS/RT not found on target");
+ } catch (Exception e) {
+ return null;
+ }
+
+ // Scanning registry.
+ LinkedHashMap<String, HashMap<String, String>> lhm =
+ new LinkedHashMap<String, HashMap<String, String>>(10);
+ String current = rlist;
+ String previous = rlist;
+ while (true) {
+
+ // Fetching next thread in the registry (newer link). This fetch fails
+ // if the register is not enabled in the kernel and the p_newer field
+ // does not exist.
+ try {
+ current = evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_newer");
+ } catch (DebugProxyException e1) {
+ throw new DebugProxyException("ChibiOS/RT registry not enabled in kernel");
+ }
+
+ // This can happen if the kernel is not initialized yet or if the
+ // registry is corrupted.
+ if (current.compareTo("0") == 0)
+ throw new DebugProxyException("ChibiOS/RT registry integrity check failed, NULL pointer");
+
+ // TODO: integrity check on the pointer value (alignment, range).
+
+ // The previous thread in the list is fetched as a integrity check.
+ String older = evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_older");
+ if (older.compareTo("0") == 0)
+ throw new DebugProxyException("ChibiOS/RT registry integrity check failed, NULL pointer");
+ if (previous.compareTo(older) != 0)
+ throw new DebugProxyException("ChibiOS/RT registry integrity check failed, double linked list violation");
+
+ // End of the linked list condition.
+ if (current.compareTo(rlist) == 0)
+ break;
+
+ // Hash of threads fields.
+ HashMap<String, String> map = new HashMap<String, String>(16);
+
+ // Fetch of the various fields in the Thread structure. Some fields
+ // are optional so are placed within try-catch.
+ long stklimit;
+ try {
+ stklimit = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_stklimit"));
+ map.put("stklimit", Long.toString(stklimit));
+ } catch (DebugProxyException e) {
+ map.put("stklimit", "-");
+ stklimit = -1;
+ }
+
+ long stack;
+ try {
+ stack = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_ctx.r13"));
+ map.put("stack", Long.toString(stack));
+ } catch (DebugProxyException e) {
+ try {
+ stack = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_ctx.sp"));
+ map.put("stack", Long.toString(stack));
+ } catch (DebugProxyException ex) {
+ map.put("stack", "-");
+ stack = -1;
+ }
+ }
+
+ if ((stklimit < 0) || (stack < 0))
+ map.put("stkunused", "-");
+ else {
+ if ((stack < 0) || (stack < stklimit))
+ map.put("stkunused", "overflow");
+ else {
+ long stkunused = scanStack(stklimit, stack, 0x55555555);
+ map.put("stkunused", Long.toString(stkunused));
+ }
+ }
+
+ long n;
+ try {
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_name"));
+ if (n == 0)
+ map.put("name", "<no name>");
+ else
+ map.put("name", readCString(n, 16));
+ } catch (DebugProxyException e) {
+ map.put("name", "-");
+ }
+
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_state"));
+ map.put("state", Long.toString(n));
+ if ((n >= 0) && (n < threadStates.length)) {
+ map.put("state_s", threadStates[(int)n]);
+ }
+ else
+ map.put("state_s", "unknown");
+
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_flags"));
+ map.put("flags", Long.toString(n));
+
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_prio"));
+ map.put("prio", Long.toString(n));
+
+ try {
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_refs"));
+ map.put("refs", Long.toString(n));
+ } catch (DebugProxyException e) {
+ map.put("refs", "-");
+ }
+
+ try {
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_time"));
+ map.put("time", Long.toString(n));
+ } catch (DebugProxyException e) {
+ map.put("time", "-");
+ }
+
+ try {
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + current + ")->p_u.wtobjp"));
+ map.put("wtobjp", Long.toString(n));
+ } catch (DebugProxyException e) {
+ map.put("wtobjp", "-");
+ }
+
+ // Inserting the new thread map into the threads list.
+ lhm.put(current, map);
+
+ previous = current;
+ }
+ return lhm;
+ }
+
+ /**
+ * @brief Return the list of timers.
+ * @details The timers list is fetched from memory by scanning the
+ * @p vtlist structure.
+ *
+ * @return A @p LinkedHashMap object whose keys are the timers addresses
+ * as decimal strings, the value is an @p HashMap of the timers
+ * fields:
+ * - delta
+ * - func
+ * - par
+ * .
+ * @retval null If the debugger encountered an error or
+ * the target is running.
+ *
+ * @throws DebugProxyException If the debugger is active but the structure
+ * @p vtlist is not found, not initialized or
+ * corrupted.
+ */
+ public LinkedHashMap<String, HashMap<String, String>> readTimers()
+ throws DebugProxyException {
+ // Delta list structure address.
+ String vtlist;
+ try {
+ vtlist = evaluateExpression("(uint32_t)&vtlist");
+ if (vtlist == null)
+ return null;
+ } catch (DebugProxyException e) {
+ throw new DebugProxyException("ChibiOS/RT not found on target");
+ } catch (Exception e) {
+ return null;
+ }
+
+ // Scanning delta list.
+ LinkedHashMap<String, HashMap<String, String>> lhm =
+ new LinkedHashMap<String, HashMap<String, String>>(10);
+ String current = vtlist;
+ String previous = vtlist;
+ while (true) {
+
+ // Fetching next timer in the delta list (vt_next link).
+ current = evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_next");
+
+ // This can happen if the kernel is not initialized yet or if the
+ // delta list is corrupted.
+ if (current.compareTo("0") == 0)
+ throw new DebugProxyException("ChibiOS/RT delta list integrity check failed, NULL pointer");
+
+ // TODO: integrity check on the pointer value (alignment, range).
+
+ // The previous timer in the delta list is fetched as a integrity check.
+ String prev = evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_prev");
+ if (prev.compareTo("0") == 0)
+ throw new DebugProxyException("ChibiOS/RT delta list integrity check failed, NULL pointer");
+ if (previous.compareTo(prev) != 0)
+ throw new DebugProxyException("ChibiOS/RT delta list integrity check failed, double linked list violation");
+
+ // End of the linked list condition.
+ if (current.compareTo(vtlist) == 0)
+ break;
+
+ // Hash of timers fields.
+ HashMap<String, String> map = new HashMap<String, String>(16);
+
+ // Fetch of the various fields in the Thread structure. Some fields
+ // are optional so are placed within try-catch.
+ long n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_time"));
+ map.put("delta", Long.toString(n));
+
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_func"));
+ map.put("func", Long.toString(n));
+
+ n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((VirtualTimer *)" + current + ")->vt_par"));
+ map.put("par", Long.toString(n));
+
+ // Inserting the new thread map into the threads list.
+ lhm.put(current, map);
+
+ previous = current;
+ }
+ return lhm;
+ }
+
+ /**
+ * @brief Return the list of trace buffer entries.
+ * @details The trace buffer is fetched from memory by scanning the
+ * @p dbg_trace_buffer array.
+ *
+ * @return A @p LinkedHashMap object whose keys are the timers addresses
+ * as decimal strings, the value is an @p HashMap of the timers
+ * fields:
+ * - time
+ * - tp
+ * - wtobjp
+ * - state
+ * - state_s
+ * .
+ * @retval null If the debugger encountered an error or
+ * the target is running.
+ *
+ * @throws DebugProxyException If the debugger is active but the structure
+ * @p dbg_trace_buffer is not found, not
+ * initialized or corrupted.
+ */
+ public LinkedHashMap<String, HashMap<String, String>> readTraceBuffer()
+ throws DebugProxyException {
+
+ // Trace buffer size.
+ String s;
+ try {
+ s = evaluateExpression("(uint32_t)dbg_trace_buffer.tb_size");
+ if (s == null)
+ return null;
+ } catch (DebugProxyException e) {
+ throw new DebugProxyException("trace buffer not found on target");
+ } catch (Exception e) {
+ return null;
+ }
+
+ int tbsize = (int)HexUtils.parseNumber(s);
+ int tbrecsize = (int)HexUtils.parseNumber(evaluateExpression("(uint32_t)sizeof (ch_swc_event_t)"));
+ long tbstart = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_trace_buffer.tb_buffer"));
+ long tbend = HexUtils.parseNumber(evaluateExpression("(uint32_t)&dbg_trace_buffer.tb_buffer[" + tbsize + "]"));
+ long tbptr = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_trace_buffer.tb_ptr"));
+
+ // Scanning the trace buffer from the oldest event to the newest.
+ LinkedHashMap<String, HashMap<String, String>> lhm =
+ new LinkedHashMap<String, HashMap<String, String>>(64);
+ int n = tbsize;
+ int i = -tbsize + 1;
+ while (n > 0) {
+ // Hash of timers fields.
+ HashMap<String, String> map = new HashMap<String, String>(16);
+
+ String time = evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_time)");
+ map.put("time", time);
+
+ String tp = evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_tp)");
+ map.put("tp", tp);
+
+ String wtobjp = evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_wtobjp)");
+ map.put("wtobjp", wtobjp);
+
+ long state = HexUtils.parseNumber(evaluateExpression("(uint32_t)(((ch_swc_event_t *)" + tbptr + ")->se_state)"));
+ map.put("state", Long.toString(state));
+ if ((state >= 0) && (state < threadStates.length))
+ map.put("state_s", threadStates[(int)state]);
+ else
+ map.put("state_s", "unknown");
+
+ // Inserting the new event map into the events list.
+ if (tp.compareTo("0") != 0)
+ lhm.put(Integer.toString(i), map);
+
+ tbptr += tbrecsize;
+ if (tbptr >= tbend)
+ tbptr = tbstart;
+ n--;
+ i++;
+ }
+ return lhm;
+ }
+
+ /**
+ * @brief Return the list of the system global variables.
+ *
+ * @return A @p LinkedHashMap object whose keys are the variable names and
+ * the values are the variable values.
+ *
+ * @retval null If the debugger encountered an error or
+ * the target is running.
+ *
+ * @throws DebugProxyException If the debugger is active but the structure
+ * @p dbg_trace_buffer is not found, not
+ * initialized or corrupted.
+ */
+ public LinkedHashMap<String, String> readGlobalVariables()
+ throws DebugProxyException {
+
+ LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(16);
+
+ try {
+ String vt_systime = evaluateExpression("(uint32_t)vtlist.vt_systime");
+ if (vt_systime == null)
+ return null;
+ map.put("vt_systime", vt_systime);
+ } catch (DebugProxyException e) {
+ throw new DebugProxyException("ChibiOS/RT not found on target");
+ } catch (Exception e) {
+ return null;
+ }
+
+ try {
+ long r_current = HexUtils.parseNumber(evaluateExpression("(uint32_t)rlist.r_current"));
+ if (r_current != 0) {
+ String name;
+ try {
+ long n = HexUtils.parseNumber(evaluateExpression("(uint32_t)((Thread *)" + r_current + ")->p_name"));
+ if (n == 0)
+ name = "<no name>";
+ else
+ name = readCString(n, 16);
+ } catch (DebugProxyException e) {
+ name = "-";
+ }
+ map.put("r_current", HexUtils.dword2HexString((int)r_current) + " \"" + name + "\"");
+ }
+ else
+ map.put("r_current", "0");
+ } catch (DebugProxyException e) {}
+
+ try {
+ String r_preempt = evaluateExpression("(uint32_t)rlist.r_preempt");
+ map.put("r_preempt", r_preempt);
+ } catch (DebugProxyException e) {}
+
+ try {
+ Long addr = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_panic_msg"));
+ if (addr == 0)
+ map.put("dbg_panic_msg", "<NULL>");
+ else
+ map.put("dbg_panic_msg", readCString(addr, 32));
+ } catch (DebugProxyException e) {
+ map.put("dbg_panic_msg", "<not enabled>");
+ }
+
+ try {
+ Long isr_cnt = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_isr_cnt"));
+ if (isr_cnt == 0)
+ map.put("dbg_isr_cnt", "not within ISR");
+ else
+ map.put("dbg_isr_cnt", "within ISR");
+ } catch (DebugProxyException e) {
+ map.put("dbg_isr_cnt", "<not enabled>");
+ }
+
+ try {
+ Long lock_cnt = HexUtils.parseNumber(evaluateExpression("(uint32_t)dbg_lock_cnt"));
+ if (lock_cnt == 0)
+ map.put("dbg_lock_cnt", "not within lock");
+ else
+ map.put("dbg_lock_cnt", "within lock");
+ } catch (DebugProxyException e) {
+ map.put("dbg_lock_cnt", "<not enabled>");
+ }
+
+ return map;
+ }
+}
diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java
new file mode 100644
index 000000000..ce4a0331f
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/DebugProxyException.java
@@ -0,0 +1,14 @@
+package org.chibios.tools.eclipse.debug.utils;
+
+public class DebugProxyException extends Exception {
+
+ private static final long serialVersionUID = 6860700758297226746L;
+
+ public DebugProxyException() {
+ super("Debug Proxy Exception");
+ }
+
+ public DebugProxyException(String s) {
+ super(s);
+ }
+}
diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java
new file mode 100644
index 000000000..54727e573
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/utils/HexUtils.java
@@ -0,0 +1,94 @@
+package org.chibios.tools.eclipse.debug.utils;
+
+public class HexUtils {
+
+ protected final static String[] hexChars = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"
+ };
+
+ static public boolean isHexChar(char c) {
+
+ if (Character.isDigit(c) || (c >= 'a') && (c <= 'f') ||
+ (c >= 'A') && (c <= 'F'))
+ return true;
+ return false;
+ }
+
+ static public boolean isHexString(String hstring) {
+ int l = hstring.length();
+
+ if ((l & 1) == 1)
+ return false;
+
+ for (int i = 0; i < l; i++)
+ if (!isHexChar(hstring.charAt(i)))
+ return false;
+ return true;
+ }
+
+ static public byte[] hexStringToByteArray(String hstring) {
+
+ if (!isHexString(hstring))
+ throw new NumberFormatException("not a hex string");
+
+ byte[] result = new byte[hstring.length() / 2];
+
+ for (int i = 0; i < hstring.length(); i += 2) {
+ String toParse = hstring.substring(i, i + 2);
+
+ result[i / 2] = (byte)Integer.parseInt(toParse, 16);
+ }
+ return result;
+ }
+
+ static public byte hexStringToByte(String hstring) {
+
+ if (hstring.length() != 2)
+ throw new NumberFormatException("not a byte hex string");
+
+ return (byte)Integer.parseInt(hstring, 16);
+ }
+
+ static public String byteArrayToHexString(byte[] data) {
+ StringBuffer out = new StringBuffer(data.length * 2);
+
+ for (int i = 0; i < data.length; i++) {
+ out.append(hexChars[(data[i] >> 4) & 15]);
+ out.append(hexChars[data[i] & 15]);
+ }
+ return out.toString();
+ }
+
+ static public String byte2HexString(int b) {
+
+ return hexChars[(b >> 4) & 15] + hexChars[b & 15];
+ }
+
+ static public String word2HexString(int w) {
+
+ return hexChars[(w >> 12) & 15]
+ + hexChars[(w >> 8) & 15]
+ + hexChars[(w >> 4) & 15]
+ + hexChars[w & 15];
+ }
+
+ static public String dword2HexString(int w) {
+
+ return hexChars[(w >> 28) & 15]
+ + hexChars[(w >> 24) & 15]
+ + hexChars[(w >> 20) & 15]
+ + hexChars[(w >> 16) & 15]
+ + hexChars[(w >> 12) & 15]
+ + hexChars[(w >> 8) & 15]
+ + hexChars[(w >> 4) & 15]
+ + hexChars[w & 15];
+ }
+
+ static public long parseNumber(String s) {
+
+ if (s.toLowerCase().startsWith("0x"))
+ return Long.parseLong(s.substring(2), 16);
+ return Long.parseLong(s);
+ }
+}
+
diff --git a/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java
new file mode 100644
index 000000000..4cc7fd48e
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/chibios/tools/eclipse/debug/views/ChibiView.java
@@ -0,0 +1,565 @@
+package org.chibios.tools.eclipse.debug.views;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.chibios.tools.eclipse.debug.utils.DebugProxy;
+import org.chibios.tools.eclipse.debug.utils.DebugProxyException;
+import org.chibios.tools.eclipse.debug.utils.HexUtils;
+
+import org.eclipse.ui.internal.IWorkbenchThemeConstants;
+import org.eclipse.ui.part.*;
+import org.eclipse.ui.themes.ITheme;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.ui.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.cdt.debug.internal.core.model.CDebugTarget;
+import org.eclipse.wb.swt.SWTResourceManager;
+import org.eclipse.wb.swt.ResourceManager;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+/**
+ * This sample class demonstrates how to plug-in a new workbench view. The view
+ * shows data obtained from the model. The sample creates a dummy model on the
+ * fly, but a real implementation would connect to the model available either in
+ * this or another plug-in (e.g. the workspace). The view is connected to the
+ * model using a content provider.
+ * <p>
+ * The view uses a label provider to define how model objects should be
+ * presented in the view. Each view can present the same model objects using
+ * different labels and icons, if needed. Alternatively, a single label provider
+ * can be shared between views in order to ensure that objects of the same type
+ * are presented in the same way everywhere.
+ * <p>
+ */
+
+@SuppressWarnings("restriction")
+public class ChibiView extends ViewPart implements IDebugEventSetListener {
+
+ /**
+ * The ID of the view as specified by the extension.
+ */
+ public static final String ID = "org.chibios.tools.eclipse.debug.views.ChibiView";
+
+ private CTabFolder tabFolder;
+ private CTabItem tbtmGlobal;
+ private CTabItem tbtmThreads;
+ private CTabItem tbtmTimers;
+ private CTabItem tbtmTraceBuffer;
+
+ private Action refreshAction;
+ private Table threadsTable;
+ private Table timersTable;
+
+ private DebugProxy debugger;
+ private Table tbTable;
+ private Table globalTable;
+
+ private ITheme theme;
+
+ private FocusAdapter focus = new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ setInactive();
+ }
+ @Override
+ public void focusGained(FocusEvent e) {
+ setActive();
+ }
+ };
+
+ /**
+ * The constructor.
+ */
+ public ChibiView() {
+
+ theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme();
+ }
+
+ private void setActive() {
+ tabFolder.setSelectionBackground(
+ new org.eclipse.swt.graphics.Color[] {
+ theme.getColorRegistry().get(IWorkbenchThemeConstants.ACTIVE_TAB_BG_START),
+ theme.getColorRegistry().get(IWorkbenchThemeConstants.ACTIVE_TAB_BG_END)
+ },
+ new int[] {100},
+ true);
+ tabFolder.setSelectionForeground(theme.getColorRegistry().get(IWorkbenchThemeConstants.ACTIVE_TAB_TEXT_COLOR));
+ }
+
+ private void setInactive() {
+ tabFolder.setSelectionBackground(
+ new org.eclipse.swt.graphics.Color[] {
+ theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_BG_START),
+ theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_BG_END)
+ },
+ new int[] {theme.getInt(IWorkbenchThemeConstants.ACTIVE_TAB_PERCENT)},
+ true);
+ tabFolder.setSelectionForeground(theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_TEXT_COLOR));
+ }
+
+ /**
+ * This is a callback that will allow us to create the viewer and initialize
+ * it.
+ */
+ public void createPartControl(Composite parent) {
+
+ tabFolder = new CTabFolder(parent, SWT.BORDER | SWT.BOTTOM);
+ tabFolder.setFont(theme.getFontRegistry().get(IWorkbenchThemeConstants.TAB_TEXT_FONT));
+ tabFolder.setBackground(theme.getColorRegistry().get(IWorkbenchThemeConstants.INACTIVE_TAB_BG_END));
+ tabFolder.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setActive();
+ tabFolder.getSelection().getControl().setFocus();
+ }
+ });
+ tabFolder.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ tabFolder.getSelection().getControl().setFocus();
+ }
+ });
+ setInactive();
+ tabFolder.setSimple(false);
+
+ tbtmGlobal = new CTabItem(tabFolder, SWT.NONE);
+ tbtmGlobal.setText("Global");
+
+ globalTable = new Table(tabFolder, SWT.FULL_SELECTION);
+ globalTable.addFocusListener(focus);
+ globalTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL));
+ tbtmGlobal.setControl(globalTable);
+ globalTable.setHeaderVisible(true);
+
+ TableColumn tblclmnGlobalHidden = new TableColumn(globalTable, SWT.RIGHT);
+ tblclmnGlobalHidden.setWidth(0);
+ tblclmnGlobalHidden.setText("");
+
+ TableColumn tblclmnGlobalVariableName = new TableColumn(globalTable, SWT.LEFT);
+ tblclmnGlobalVariableName.setWidth(150);
+ tblclmnGlobalVariableName.setText("Variable");
+
+ TableColumn tblclmnGlobalVariableValue = new TableColumn(globalTable, SWT.LEFT);
+ tblclmnGlobalVariableValue.setWidth(300);
+ tblclmnGlobalVariableValue.setText("Value");
+
+ tbtmThreads = new CTabItem(tabFolder, SWT.NONE);
+ tbtmThreads.setText("Threads");
+
+ threadsTable = new Table(tabFolder, SWT.FULL_SELECTION);
+ threadsTable.addFocusListener(focus);
+ tbtmThreads.setControl(threadsTable);
+ threadsTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL));
+ threadsTable.setHeaderVisible(true);
+
+ TableColumn tblclmnThreadAddress = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadAddress.setWidth(72);
+ tblclmnThreadAddress.setText("Address");
+
+ TableColumn tblclmnThreadLimit = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadLimit.setWidth(72);
+ tblclmnThreadLimit.setText("StkLimit");
+
+ TableColumn tblclmnThreadStack = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadStack.setWidth(72);
+ tblclmnThreadStack.setText("Stack");
+
+ TableColumn tblclmnThreadUsed = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadUsed.setWidth(72);
+ tblclmnThreadUsed.setText("StkUnused");
+
+ TableColumn tblclmnThreadName = new TableColumn(threadsTable, SWT.LEFT);
+ tblclmnThreadName.setWidth(144);
+ tblclmnThreadName.setText("Name");
+
+ TableColumn tblclmnThreadState = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadState.setWidth(72);
+ tblclmnThreadState.setText("State");
+
+ TableColumn tblclmnThreadFlags = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadFlags.setWidth(40);
+ tblclmnThreadFlags.setText("Flgs");
+
+ TableColumn tblclmnThreadPriority = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadPriority.setWidth(40);
+ tblclmnThreadPriority.setText("Prio");
+
+ TableColumn tblclmnThreadRefs = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadRefs.setWidth(40);
+ tblclmnThreadRefs.setText("Refs");
+
+ TableColumn tblclmnThreadTime = new TableColumn(threadsTable, SWT.RIGHT);
+ tblclmnThreadTime.setWidth(64);
+ tblclmnThreadTime.setText("Time");
+
+ TableColumn tblclmnThreadShared = new TableColumn(threadsTable, SWT.LEFT);
+ tblclmnThreadShared.setWidth(72);
+ tblclmnThreadShared.setText("Obj/Msg");
+
+ tbtmTimers = new CTabItem(tabFolder, SWT.NONE);
+ tbtmTimers.setText("Timers");
+
+ timersTable = new Table(tabFolder, SWT.FULL_SELECTION);
+ timersTable.addFocusListener(focus);
+ tbtmTimers.setControl(timersTable);
+ timersTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL));
+ timersTable.setHeaderVisible(true);
+
+ TableColumn tblclmnTimerAddress = new TableColumn(timersTable, SWT.RIGHT);
+ tblclmnTimerAddress.setWidth(72);
+ tblclmnTimerAddress.setText("Address");
+
+ TableColumn tblclmnTimerTime = new TableColumn(timersTable, SWT.RIGHT);
+ tblclmnTimerTime.setWidth(72);
+ tblclmnTimerTime.setText("Time");
+
+ TableColumn tblclmnTimerDelta = new TableColumn(timersTable, SWT.RIGHT);
+ tblclmnTimerDelta.setWidth(72);
+ tblclmnTimerDelta.setText("Delta");
+
+ TableColumn tblclmnTimerCallback = new TableColumn(timersTable, SWT.RIGHT);
+ tblclmnTimerCallback.setWidth(72);
+ tblclmnTimerCallback.setText("Callback");
+
+ TableColumn tblclmnTimerParameter = new TableColumn(timersTable, SWT.LEFT);
+ tblclmnTimerParameter.setWidth(72);
+ tblclmnTimerParameter.setText("Param");
+
+ tbtmTraceBuffer = new CTabItem(tabFolder, SWT.NONE);
+ tbtmTraceBuffer.setText("TraceBuffer");
+
+ tbTable = new Table(tabFolder, SWT.FULL_SELECTION);
+ tbTable.addFocusListener(focus);
+ tbTable.setFont(SWTResourceManager.getFont("Courier New", 8, SWT.NORMAL));
+ tbtmTraceBuffer.setControl(tbTable);
+ tbTable.setHeaderVisible(true);
+
+ TableColumn tblclmnTraceBufferHidden = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferHidden.setWidth(0);
+ tblclmnTraceBufferHidden.setText("");
+
+ TableColumn tblclmnTraceBufferIndex = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferIndex.setWidth(48);
+ tblclmnTraceBufferIndex.setText("Event");
+
+ TableColumn tblclmnTraceBufferTime = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferTime.setWidth(64);
+ tblclmnTraceBufferTime.setText("Time");
+
+ TableColumn tblclmnTraceBufferPrevAddress = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferPrevAddress.setWidth(72);
+ tblclmnTraceBufferPrevAddress.setText("Previous");
+
+ TableColumn tblclmnTraceBufferPrevName = new TableColumn(tbTable, SWT.LEFT);
+ tblclmnTraceBufferPrevName.setWidth(144);
+ tblclmnTraceBufferPrevName.setText("Previous Name");
+
+ TableColumn tblclmnTraceBufferState = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferState.setWidth(72);
+ tblclmnTraceBufferState.setText("State");
+
+ TableColumn tblclmnTraceBufferShared = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferShared.setWidth(72);
+ tblclmnTraceBufferShared.setText("Obj/Msg");
+
+ TableColumn tblclmnTraceBufferCurrentAddress = new TableColumn(tbTable, SWT.RIGHT);
+ tblclmnTraceBufferCurrentAddress.setWidth(72);
+ tblclmnTraceBufferCurrentAddress.setText("Current");
+
+ TableColumn tblclmnTraceBufferCurrentName = new TableColumn(tbTable, SWT.LEFT);
+ tblclmnTraceBufferCurrentName.setWidth(144);
+ tblclmnTraceBufferCurrentName.setText("Current Name");
+
+ makeActions();
+ hookContextMenu();
+ contributeToActionBars();
+
+ tabFolder.setSelection(tbtmGlobal);
+
+ DebugPlugin.getDefault().addDebugEventListener(this);
+
+ try {
+ debugger = new DebugProxy();
+ } catch (DebugProxyException e) {}
+ }
+
+ /**
+ * @brief Handling events from the debugger.
+ */
+ @Override
+ public void handleDebugEvents(DebugEvent[] events) {
+ for (DebugEvent event : events) {
+ switch (event.getKind()) {
+ case DebugEvent.CREATE:
+ Object source = event.getSource();
+ if (source instanceof CDebugTarget) {
+ try {
+ debugger = new DebugProxy((CDebugTarget)source);
+ } catch (DebugProxyException e) {}
+ }
+ break;
+ }
+ }
+ }
+
+ private void hookContextMenu() {
+ MenuManager menuMgr = new MenuManager("#PopupMenu");
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ ChibiView.this.fillContextMenu(manager);
+ }
+ });
+ }
+
+ private void contributeToActionBars() {
+ IActionBars bars = getViewSite().getActionBars();
+ fillLocalPullDown(bars.getMenuManager());
+ fillLocalToolBar(bars.getToolBarManager());
+ }
+
+ private void fillLocalPullDown(IMenuManager manager) {
+ manager.add(refreshAction);
+/* manager.add(new Separator());
+ manager.add(refreshAction);*/
+ }
+
+ private void fillContextMenu(IMenuManager manager) {
+ manager.add(refreshAction);
+ // Other plug-ins can contribute there actions here
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ private void fillLocalToolBar(IToolBarManager manager) {
+ manager.add(refreshAction);
+ }
+
+ private void fillGlobalTable() {
+ LinkedHashMap<String, String> lhm;
+
+ // If the debugger is not yet present then do nothing.
+ if (debugger == null)
+ return;
+
+ // Reading the list of global variables, null can be returned if the debugger
+ // does not respond.
+ try {
+ lhm = debugger.readGlobalVariables();
+ if (lhm == null)
+ return;
+ } catch (DebugProxyException e) {
+ showMessage("Error: " + e.getMessage() + ".");
+ return;
+ }
+
+ globalTable.removeAll();
+
+ Set<Entry<String, String>> set = lhm.entrySet();
+ for (Entry<String, String> entry : set) {
+ TableItem tableItem = new TableItem(globalTable, SWT.NONE);
+ tableItem.setText(new String[] {
+ "",
+ entry.getKey(),
+ entry.getValue()
+ });
+ }
+ }
+
+ private String makeHex(String s) {
+ try {
+ s = HexUtils.dword2HexString((int)HexUtils.parseNumber(s));
+ } catch (Exception e) {}
+ return s;
+ }
+
+ private void fillThreadsTable() {
+ LinkedHashMap<String, HashMap<String, String>> lhm;
+
+ // If the debugger is not yet present then do nothing.
+ if (debugger == null)
+ return;
+
+ // Reading the list of threads, null can be returned if the debugger
+ // does not respond.
+ try {
+ lhm = debugger.readThreads();
+ if (lhm == null)
+ return;
+ } catch (DebugProxyException e) {
+ showMessage("Error: " + e.getMessage() + ".");
+ return;
+ }
+
+ threadsTable.removeAll();
+
+ Set<Entry<String, HashMap<String, String>>> set = lhm.entrySet();
+ for (Entry<String, HashMap<String, String>> entry : set) {
+ HashMap<String, String> map = entry.getValue();
+ TableItem tableItem = new TableItem(threadsTable, SWT.NONE);
+ tableItem.setText(new String[] {
+ makeHex(entry.getKey()),
+ makeHex(map.get("stklimit")),
+ makeHex(map.get("stack")),
+ map.get("stkunused"),
+ map.get("name"),
+ map.get("state_s"),
+ HexUtils.byte2HexString((int)HexUtils.parseNumber(map.get("flags"))),
+ map.get("prio"),
+ map.get("refs"),
+ map.get("time"),
+ makeHex(map.get("wtobjp"))
+ });
+ }
+ }
+
+ private void fillTimersTable() {
+ LinkedHashMap<String, HashMap<String, String>> lhm;
+
+ // If the debugger is not yet present then do nothing.
+ if (debugger == null)
+ return;
+
+ // Reading the list of threads, null can be returned if the debugger
+ // does not respond.
+ try {
+ lhm = debugger.readTimers();
+ if (lhm == null)
+ return;
+ } catch (DebugProxyException e) {
+ showMessage("Error: " + e.getMessage() + ".");
+ return;
+ }
+
+ timersTable.removeAll();
+
+ Set<Entry<String, HashMap<String, String>>> set = lhm.entrySet();
+ long time = 0;
+ for (Entry<String, HashMap<String, String>> entry : set) {
+ HashMap<String, String> map = entry.getValue();
+ time = time + HexUtils.parseNumber(map.get("delta"));
+ TableItem tableItem = new TableItem(timersTable, SWT.NONE);
+ tableItem.setText(new String[] {
+ makeHex(entry.getKey()),
+ Long.toString(time),
+ "+" + HexUtils.parseNumber(map.get("delta")),
+ makeHex(map.get("func")),
+ makeHex(map.get("par"))
+ });
+ }
+ }
+
+ private void fillTraceBufferTable() {
+ LinkedHashMap<String, HashMap<String, String>> lhm, lhmthreads;
+
+ // If the debugger is not yet present then do nothing.
+ if (debugger == null)
+ return;
+
+ // Read active threads for retrieving names.
+ try {
+ lhmthreads = debugger.readThreads();
+ if (lhmthreads == null)
+ return;
+ } catch (DebugProxyException e) {
+ lhmthreads = new LinkedHashMap<String, HashMap<String, String>>(0);
+ }
+
+ // Reading the list of threads, null can be returned if the debugger
+ // does not respond.
+ try {
+ lhm = debugger.readTraceBuffer();
+ if (lhm == null)
+ return;
+ } catch (DebugProxyException e) {
+ showMessage("Error: " + e.getMessage() + ".");
+ return;
+ }
+
+ tbTable.removeAll();
+
+ Set<Entry<String, HashMap<String, String>>> set = lhm.entrySet();
+ String prev = "";
+ String prevname = "";
+ for (Entry<String, HashMap<String, String>> entry : set) {
+ HashMap<String, String> map = entry.getValue();
+ TableItem tableItem = new TableItem(tbTable, SWT.NONE);
+
+ // Searches the current thread into the threads map.
+ String currentaddr = map.get("tp");
+ HashMap<String, String> thread = lhmthreads.get(currentaddr);
+ String currentname;
+ if (thread != null)
+ currentname = thread.get("name");
+ else
+ currentname = "";
+
+ String current = makeHex(currentaddr);
+ tableItem.setText(new String[] {
+ "",
+ entry.getKey(),
+ map.get("time"),
+ prev,
+ prevname,
+ map.get("state_s"),
+ makeHex(map.get("wtobjp")),
+ current,
+ currentname
+ });
+ prev = current;
+ prevname = currentname;
+ }
+ }
+
+ private void makeActions() {
+
+ // Refresh action.
+ refreshAction = new Action() {
+ public void run() {
+ CTabItem tabitem = tabFolder.getSelection();
+ if (tabitem == null)
+ return;
+ if (tabitem == tbtmGlobal)
+ fillGlobalTable();
+ else if (tabitem == tbtmThreads)
+ fillThreadsTable();
+ else if (tabitem == tbtmTimers)
+ fillTimersTable();
+ else if (tabitem == tbtmTraceBuffer)
+ fillTraceBufferTable();
+ }
+ };
+ refreshAction.setDisabledImageDescriptor(ResourceManager.getPluginImageDescriptor("org.eclipse.cdt.ui", "/icons/dlcl16/refresh_nav.gif"));
+ refreshAction.setImageDescriptor(ResourceManager.getPluginImageDescriptor("org.eclipse.cdt.ui", "/icons/elcl16/refresh_nav.gif"));
+ refreshAction.setText("Refresh");
+ refreshAction.setToolTipText("Refresh timers list");
+ }
+
+ private void showMessage(String message) {
+ MessageDialog.openInformation(tabFolder.getShell(),
+ "ChibiOS/RT Views", message);
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ public void setFocus() {
+ tabFolder.setFocus();
+ }
+}
diff --git a/tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java
new file mode 100644
index 000000000..4bfbc6b6e
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/ResourceManager.java
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.swt;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.osgi.framework.Bundle;
+
+/**
+ * Utility class for managing OS resources associated with SWT/JFace controls such as colors, fonts, images,
+ * etc.
+ *
+ * !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the
+ * operating system resources managed by cached objects when those objects and OS resources are no longer
+ * needed (e.g. on application shutdown)
+ *
+ * This class may be freely distributed as part of any application or plugin.
+ * <p>
+ *
+ * @author scheglov_ke
+ * @author Dan Rubel
+ */
+public class ResourceManager extends SWTResourceManager {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static Map<ImageDescriptor, Image> m_descriptorImageMap = new HashMap<ImageDescriptor, Image>();
+ /**
+ * Returns an {@link ImageDescriptor} stored in the file at the specified path relative to the specified
+ * class.
+ *
+ * @param clazz
+ * the {@link Class} relative to which to find the image descriptor.
+ * @param path
+ * the path to the image file.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ */
+ public static ImageDescriptor getImageDescriptor(Class<?> clazz, String path) {
+ return ImageDescriptor.createFromFile(clazz, path);
+ }
+ /**
+ * Returns an {@link ImageDescriptor} stored in the file at the specified path.
+ *
+ * @param path
+ * the path to the image file.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ try {
+ return ImageDescriptor.createFromURL(new File(path).toURI().toURL());
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+ /**
+ * Returns an {@link Image} based on the specified {@link ImageDescriptor}.
+ *
+ * @param descriptor
+ * the {@link ImageDescriptor} for the {@link Image}.
+ * @return the {@link Image} based on the specified {@link ImageDescriptor}.
+ */
+ public static Image getImage(ImageDescriptor descriptor) {
+ if (descriptor == null) {
+ return null;
+ }
+ Image image = m_descriptorImageMap.get(descriptor);
+ if (image == null) {
+ image = descriptor.createImage();
+ m_descriptorImageMap.put(descriptor, image);
+ }
+ return image;
+ }
+ /**
+ * Maps images to decorated images.
+ */
+ @SuppressWarnings("unchecked")
+ private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated.
+ * @param decorator
+ * the {@link Image} to decorate the base image.
+ * @return {@link Image} The resulting decorated image.
+ */
+ public static Image decorateImage(Image baseImage, Image decorator) {
+ return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+ }
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated.
+ * @param decorator
+ * the {@link Image} to decorate the base image.
+ * @param corner
+ * the corner to place decorator image.
+ * @return the resulting decorated {@link Image}.
+ */
+ public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+ if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+ throw new IllegalArgumentException("Wrong decorate corner");
+ }
+ Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+ if (cornerDecoratedImageMap == null) {
+ cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
+ m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+ }
+ Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
+ if (decoratedMap == null) {
+ decoratedMap = new HashMap<Image, Image>();
+ cornerDecoratedImageMap.put(baseImage, decoratedMap);
+ }
+ //
+ Image result = decoratedMap.get(decorator);
+ if (result == null) {
+ final Rectangle bib = baseImage.getBounds();
+ final Rectangle dib = decorator.getBounds();
+ final Point baseImageSize = new Point(bib.width, bib.height);
+ CompositeImageDescriptor compositImageDesc = new CompositeImageDescriptor() {
+ @Override
+ protected void drawCompositeImage(int width, int height) {
+ drawImage(baseImage.getImageData(), 0, 0);
+ if (corner == TOP_LEFT) {
+ drawImage(decorator.getImageData(), 0, 0);
+ } else if (corner == TOP_RIGHT) {
+ drawImage(decorator.getImageData(), bib.width - dib.width, 0);
+ } else if (corner == BOTTOM_LEFT) {
+ drawImage(decorator.getImageData(), 0, bib.height - dib.height);
+ } else if (corner == BOTTOM_RIGHT) {
+ drawImage(decorator.getImageData(), bib.width - dib.width, bib.height - dib.height);
+ }
+ }
+ @Override
+ protected Point getSize() {
+ return baseImageSize;
+ }
+ };
+ //
+ result = compositImageDesc.createImage();
+ decoratedMap.put(decorator, result);
+ }
+ return result;
+ }
+ /**
+ * Dispose all of the cached images.
+ */
+ public static void disposeImages() {
+ SWTResourceManager.disposeImages();
+ // dispose ImageDescriptor images
+ {
+ for (Iterator<Image> I = m_descriptorImageMap.values().iterator(); I.hasNext();) {
+ I.next().dispose();
+ }
+ m_descriptorImageMap.clear();
+ }
+ // dispose decorated images
+ for (int i = 0; i < m_decoratedImageMap.length; i++) {
+ Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];
+ if (cornerDecoratedImageMap != null) {
+ for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) {
+ for (Image image : decoratedMap.values()) {
+ image.dispose();
+ }
+ decoratedMap.clear();
+ }
+ cornerDecoratedImageMap.clear();
+ }
+ }
+ // dispose plugin images
+ {
+ for (Iterator<Image> I = m_URLImageMap.values().iterator(); I.hasNext();) {
+ I.next().dispose();
+ }
+ m_URLImageMap.clear();
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Plugin images support
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps URL to images.
+ */
+ private static Map<String, Image> m_URLImageMap = new HashMap<String, Image>();
+ /**
+ * Provider for plugin resources, used by WindowBuilder at design time.
+ */
+ public interface PluginResourceProvider {
+ URL getEntry(String symbolicName, String path);
+ }
+ /**
+ * Instance of {@link PluginResourceProvider}, used by WindowBuilder at design time.
+ */
+ private static PluginResourceProvider m_designTimePluginResourceProvider = null;
+ /**
+ * Returns an {@link Image} based on a plugin and file path.
+ *
+ * @param plugin
+ * the plugin {@link Object} containing the image
+ * @param name
+ * the path to the image within the plugin
+ * @return the {@link Image} stored in the file at the specified path
+ *
+ * @deprecated Use {@link #getPluginImage(String, String)} instead.
+ */
+ @Deprecated
+ public static Image getPluginImage(Object plugin, String name) {
+ try {
+ URL url = getPluginImageURL(plugin, name);
+ if (url != null) {
+ return getPluginImageFromUrl(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+ /**
+ * Returns an {@link Image} based on a {@link Bundle} and resource entry path.
+ *
+ * @param symbolicName
+ * the symbolic name of the {@link Bundle}.
+ * @param path
+ * the path of the resource entry.
+ * @return the {@link Image} stored in the file at the specified path.
+ */
+ public static Image getPluginImage(String symbolicName, String path) {
+ try {
+ URL url = getPluginImageURL(symbolicName, path);
+ if (url != null) {
+ return getPluginImageFromUrl(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+ /**
+ * Returns an {@link Image} based on given {@link URL}.
+ */
+ private static Image getPluginImageFromUrl(URL url) {
+ try {
+ try {
+ String key = url.toExternalForm();
+ Image image = m_URLImageMap.get(key);
+ if (image == null) {
+ InputStream stream = url.openStream();
+ try {
+ image = getImage(stream);
+ m_URLImageMap.put(key, image);
+ } finally {
+ stream.close();
+ }
+ }
+ return image;
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+ /**
+ * Returns an {@link ImageDescriptor} based on a plugin and file path.
+ *
+ * @param plugin
+ * the plugin {@link Object} containing the image.
+ * @param name
+ * the path to th eimage within the plugin.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ *
+ * @deprecated Use {@link #getPluginImageDescriptor(String, String)} instead.
+ */
+ @Deprecated
+ public static ImageDescriptor getPluginImageDescriptor(Object plugin, String name) {
+ try {
+ try {
+ URL url = getPluginImageURL(plugin, name);
+ return ImageDescriptor.createFromURL(url);
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+ /**
+ * Returns an {@link ImageDescriptor} based on a {@link Bundle} and resource entry path.
+ *
+ * @param symbolicName
+ * the symbolic name of the {@link Bundle}.
+ * @param path
+ * the path of the resource entry.
+ * @return the {@link ImageDescriptor} based on a {@link Bundle} and resource entry path.
+ */
+ public static ImageDescriptor getPluginImageDescriptor(String symbolicName, String path) {
+ try {
+ URL url = getPluginImageURL(symbolicName, path);
+ if (url != null) {
+ return ImageDescriptor.createFromURL(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+ /**
+ * Returns an {@link URL} based on a {@link Bundle} and resource entry path.
+ */
+ private static URL getPluginImageURL(String symbolicName, String path) {
+ // try runtime plugins
+ {
+ Bundle bundle = Platform.getBundle(symbolicName);
+ if (bundle != null) {
+ return bundle.getEntry(path);
+ }
+ }
+ // try design time provider
+ if (m_designTimePluginResourceProvider != null) {
+ return m_designTimePluginResourceProvider.getEntry(symbolicName, path);
+ }
+ // no such resource
+ return null;
+ }
+ /**
+ * Returns an {@link URL} based on a plugin and file path.
+ *
+ * @param plugin
+ * the plugin {@link Object} containing the file path.
+ * @param name
+ * the file path.
+ * @return the {@link URL} representing the file at the specified path.
+ * @throws Exception
+ */
+ private static URL getPluginImageURL(Object plugin, String name) throws Exception {
+ // try to work with 'plugin' as with OSGI BundleContext
+ try {
+ Class<?> BundleClass = Class.forName("org.osgi.framework.Bundle"); //$NON-NLS-1$
+ Class<?> BundleContextClass = Class.forName("org.osgi.framework.BundleContext"); //$NON-NLS-1$
+ if (BundleContextClass.isAssignableFrom(plugin.getClass())) {
+ Method getBundleMethod = BundleContextClass.getMethod("getBundle", new Class[0]); //$NON-NLS-1$
+ Object bundle = getBundleMethod.invoke(plugin, new Object[0]);
+ //
+ Class<?> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
+ Constructor<?> pathConstructor = PathClass.getConstructor(new Class[]{String.class});
+ Object path = pathConstructor.newInstance(new Object[]{name});
+ //
+ Class<?> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
+ Class<?> PlatformClass = Class.forName("org.eclipse.core.runtime.Platform"); //$NON-NLS-1$
+ Method findMethod = PlatformClass.getMethod("find", new Class[]{BundleClass, IPathClass}); //$NON-NLS-1$
+ return (URL) findMethod.invoke(null, new Object[]{bundle, path});
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ // else work with 'plugin' as with usual Eclipse plugin
+ {
+ Class<?> PluginClass = Class.forName("org.eclipse.core.runtime.Plugin"); //$NON-NLS-1$
+ if (PluginClass.isAssignableFrom(plugin.getClass())) {
+ //
+ Class<?> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
+ Constructor<?> pathConstructor = PathClass.getConstructor(new Class[]{String.class});
+ Object path = pathConstructor.newInstance(new Object[]{name});
+ //
+ Class<?> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
+ Method findMethod = PluginClass.getMethod("find", new Class[]{IPathClass}); //$NON-NLS-1$
+ return (URL) findMethod.invoke(plugin, new Object[]{path});
+ }
+ }
+ return null;
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // General
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Dispose of cached objects and their underlying OS resources. This should only be called when the cached
+ * objects are no longer needed (e.g. on application shutdown).
+ */
+ public static void dispose() {
+ disposeColors();
+ disposeFonts();
+ disposeImages();
+ }
+} \ No newline at end of file
diff --git a/tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java
new file mode 100644
index 000000000..8b6d4cc3f
--- /dev/null
+++ b/tools/eclipse/debug_support/src/org/eclipse/wb/swt/SWTResourceManager.java
@@ -0,0 +1,447 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.swt;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
+ * <p>
+ * !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the
+ * operating system resources managed by cached objects when those objects and OS resources are no longer
+ * needed (e.g. on application shutdown)
+ * <p>
+ * This class may be freely distributed as part of any application or plugin.
+ * <p>
+ * @author scheglov_ke
+ * @author Dan Rubel
+ */
+public class SWTResourceManager {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Color
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>();
+ /**
+ * Returns the system {@link Color} matching the specific ID.
+ *
+ * @param systemColorID
+ * the ID value for the color
+ * @return the system {@link Color} matching the specific ID
+ */
+ public static Color getColor(int systemColorID) {
+ Display display = Display.getCurrent();
+ return display.getSystemColor(systemColorID);
+ }
+ /**
+ * Returns a {@link Color} given its red, green and blue component values.
+ *
+ * @param r
+ * the red component of the color
+ * @param g
+ * the green component of the color
+ * @param b
+ * the blue component of the color
+ * @return the {@link Color} matching the given red, green and blue component values
+ */
+ public static Color getColor(int r, int g, int b) {
+ return getColor(new RGB(r, g, b));
+ }
+ /**
+ * Returns a {@link Color} given its RGB value.
+ *
+ * @param rgb
+ * the {@link RGB} value of the color
+ * @return the {@link Color} matching the RGB value
+ */
+ public static Color getColor(RGB rgb) {
+ Color color = m_colorMap.get(rgb);
+ if (color == null) {
+ Display display = Display.getCurrent();
+ color = new Color(display, rgb);
+ m_colorMap.put(rgb, color);
+ }
+ return color;
+ }
+ /**
+ * Dispose of all the cached {@link Color}'s.
+ */
+ public static void disposeColors() {
+ for (Color color : m_colorMap.values()) {
+ color.dispose();
+ }
+ m_colorMap.clear();
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps image paths to images.
+ */
+ private static Map<String, Image> m_imageMap = new HashMap<String, Image>();
+ /**
+ * Returns an {@link Image} encoded by the specified {@link InputStream}.
+ *
+ * @param stream
+ * the {@link InputStream} encoding the image data
+ * @return the {@link Image} encoded by the specified input stream
+ */
+ protected static Image getImage(InputStream stream) throws IOException {
+ try {
+ Display display = Display.getCurrent();
+ ImageData data = new ImageData(stream);
+ if (data.transparentPixel > 0) {
+ return new Image(display, data, data.getTransparencyMask());
+ }
+ return new Image(display, data);
+ } finally {
+ stream.close();
+ }
+ }
+ /**
+ * Returns an {@link Image} stored in the file at the specified path.
+ *
+ * @param path
+ * the path to the image file
+ * @return the {@link Image} stored in the file at the specified path
+ */
+ public static Image getImage(String path) {
+ Image image = m_imageMap.get(path);
+ if (image == null) {
+ try {
+ image = getImage(new FileInputStream(path));
+ m_imageMap.put(path, image);
+ } catch (Exception e) {
+ image = getMissingImage();
+ m_imageMap.put(path, image);
+ }
+ }
+ return image;
+ }
+ /**
+ * Returns an {@link Image} stored in the file at the specified path relative to the specified class.
+ *
+ * @param clazz
+ * the {@link Class} relative to which to find the image
+ * @param path
+ * the path to the image file, if starts with <code>'/'</code>
+ * @return the {@link Image} stored in the file at the specified path
+ */
+ public static Image getImage(Class<?> clazz, String path) {
+ String key = clazz.getName() + '|' + path;
+ Image image = m_imageMap.get(key);
+ if (image == null) {
+ try {
+ image = getImage(clazz.getResourceAsStream(path));
+ m_imageMap.put(key, image);
+ } catch (Exception e) {
+ image = getMissingImage();
+ m_imageMap.put(key, image);
+ }
+ }
+ return image;
+ }
+ private static final int MISSING_IMAGE_SIZE = 10;
+ /**
+ * @return the small {@link Image} that can be used as placeholder for missing image.
+ */
+ private static Image getMissingImage() {
+ Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+ //
+ GC gc = new GC(image);
+ gc.setBackground(getColor(SWT.COLOR_RED));
+ gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+ gc.dispose();
+ //
+ return image;
+ }
+ /**
+ * Style constant for placing decorator image in top left corner of base image.
+ */
+ public static final int TOP_LEFT = 1;
+ /**
+ * Style constant for placing decorator image in top right corner of base image.
+ */
+ public static final int TOP_RIGHT = 2;
+ /**
+ * Style constant for placing decorator image in bottom left corner of base image.
+ */
+ public static final int BOTTOM_LEFT = 3;
+ /**
+ * Style constant for placing decorator image in bottom right corner of base image.
+ */
+ public static final int BOTTOM_RIGHT = 4;
+ /**
+ * Internal value.
+ */
+ protected static final int LAST_CORNER_KEY = 5;
+ /**
+ * Maps images to decorated images.
+ */
+ @SuppressWarnings("unchecked")
+ private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated
+ * @param decorator
+ * the {@link Image} to decorate the base image
+ * @return {@link Image} The resulting decorated image
+ */
+ public static Image decorateImage(Image baseImage, Image decorator) {
+ return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+ }
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated
+ * @param decorator
+ * the {@link Image} to decorate the base image
+ * @param corner
+ * the corner to place decorator image
+ * @return the resulting decorated {@link Image}
+ */
+ public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+ if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+ throw new IllegalArgumentException("Wrong decorate corner");
+ }
+ Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+ if (cornerDecoratedImageMap == null) {
+ cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
+ m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+ }
+ Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
+ if (decoratedMap == null) {
+ decoratedMap = new HashMap<Image, Image>();
+ cornerDecoratedImageMap.put(baseImage, decoratedMap);
+ }
+ //
+ Image result = decoratedMap.get(decorator);
+ if (result == null) {
+ Rectangle bib = baseImage.getBounds();
+ Rectangle dib = decorator.getBounds();
+ //
+ result = new Image(Display.getCurrent(), bib.width, bib.height);
+ //
+ GC gc = new GC(result);
+ gc.drawImage(baseImage, 0, 0);
+ if (corner == TOP_LEFT) {
+ gc.drawImage(decorator, 0, 0);
+ } else if (corner == TOP_RIGHT) {
+ gc.drawImage(decorator, bib.width - dib.width, 0);
+ } else if (corner == BOTTOM_LEFT) {
+ gc.drawImage(decorator, 0, bib.height - dib.height);
+ } else if (corner == BOTTOM_RIGHT) {
+ gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height);
+ }
+ gc.dispose();
+ //
+ decoratedMap.put(decorator, result);
+ }
+ return result;
+ }
+ /**
+ * Dispose all of the cached {@link Image}'s.
+ */
+ public static void disposeImages() {
+ // dispose loaded images
+ {
+ for (Image image : m_imageMap.values()) {
+ image.dispose();
+ }
+ m_imageMap.clear();
+ }
+ // dispose decorated images
+ for (int i = 0; i < m_decoratedImageMap.length; i++) {
+ Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];
+ if (cornerDecoratedImageMap != null) {
+ for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) {
+ for (Image image : decoratedMap.values()) {
+ image.dispose();
+ }
+ decoratedMap.clear();
+ }
+ cornerDecoratedImageMap.clear();
+ }
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Font
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps font names to fonts.
+ */
+ private static Map<String, Font> m_fontMap = new HashMap<String, Font>();
+ /**
+ * Maps fonts to their bold versions.
+ */
+ private static Map<Font, Font> m_fontToBoldFontMap = new HashMap<Font, Font>();
+ /**
+ * Returns a {@link Font} based on its name, height and style.
+ *
+ * @param name
+ * the name of the font
+ * @param height
+ * the height of the font
+ * @param style
+ * the style of the font
+ * @return {@link Font} The font matching the name, height and style
+ */
+ public static Font getFont(String name, int height, int style) {
+ return getFont(name, height, style, false, false);
+ }
+ /**
+ * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
+ * flags are also supported.
+ *
+ * @param name
+ * the name of the font
+ * @param size
+ * the size of the font
+ * @param style
+ * the style of the font
+ * @param strikeout
+ * the strikeout flag (warning: Windows only)
+ * @param underline
+ * the underline flag (warning: Windows only)
+ * @return {@link Font} The font matching the name, height, style, strikeout and underline
+ */
+ public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) {
+ String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline;
+ Font font = m_fontMap.get(fontName);
+ if (font == null) {
+ FontData fontData = new FontData(name, size, style);
+ if (strikeout || underline) {
+ try {
+ Class<?> logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$
+ Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$
+ if (logFont != null && logFontClass != null) {
+ if (strikeout) {
+ logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+ }
+ if (underline) {
+ logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+ }
+ }
+ } catch (Throwable e) {
+ System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ font = new Font(Display.getCurrent(), fontData);
+ m_fontMap.put(fontName, font);
+ }
+ return font;
+ }
+ /**
+ * Returns a bold version of the given {@link Font}.
+ *
+ * @param baseFont
+ * the {@link Font} for which a bold version is desired
+ * @return the bold version of the given {@link Font}
+ */
+ public static Font getBoldFont(Font baseFont) {
+ Font font = m_fontToBoldFontMap.get(baseFont);
+ if (font == null) {
+ FontData fontDatas[] = baseFont.getFontData();
+ FontData data = fontDatas[0];
+ font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD);
+ m_fontToBoldFontMap.put(baseFont, font);
+ }
+ return font;
+ }
+ /**
+ * Dispose all of the cached {@link Font}'s.
+ */
+ public static void disposeFonts() {
+ // clear fonts
+ for (Font font : m_fontMap.values()) {
+ font.dispose();
+ }
+ m_fontMap.clear();
+ // clear bold fonts
+ for (Font font : m_fontToBoldFontMap.values()) {
+ font.dispose();
+ }
+ m_fontToBoldFontMap.clear();
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Cursor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps IDs to cursors.
+ */
+ private static Map<Integer, Cursor> m_idToCursorMap = new HashMap<Integer, Cursor>();
+ /**
+ * Returns the system cursor matching the specific ID.
+ *
+ * @param id
+ * int The ID value for the cursor
+ * @return Cursor The system cursor matching the specific ID
+ */
+ public static Cursor getCursor(int id) {
+ Integer key = Integer.valueOf(id);
+ Cursor cursor = m_idToCursorMap.get(key);
+ if (cursor == null) {
+ cursor = new Cursor(Display.getDefault(), id);
+ m_idToCursorMap.put(key, cursor);
+ }
+ return cursor;
+ }
+ /**
+ * Dispose all of the cached cursors.
+ */
+ public static void disposeCursors() {
+ for (Cursor cursor : m_idToCursorMap.values()) {
+ cursor.dispose();
+ }
+ m_idToCursorMap.clear();
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // General
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Dispose of cached objects and their underlying OS resources. This should only be called when the cached
+ * objects are no longer needed (e.g. on application shutdown).
+ */
+ public static void dispose() {
+ disposeColors();
+ disposeImages();
+ disposeFonts();
+ disposeCursors();
+ }
+} \ No newline at end of file