diff options
author | gdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4> | 2012-08-24 09:19:05 +0000 |
---|---|---|
committer | gdisirio <gdisirio@35acf78f-673a-0410-8e92-d51de3d6d3f4> | 2012-08-24 09:19:05 +0000 |
commit | 5e4b039d56d93520f36f0c775e3cdc95c2f03e04 (patch) | |
tree | 6f23be1f25ce4a86f353aa75b1a44402cc6575e9 /tools/eclipse | |
parent | 546a832f81364c1b8f8ed3fc0850d2b32a6cbffe (diff) | |
download | ChibiOS-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/eclipse')
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 Binary files differnew file mode 100644 index 000000000..34fb3c9d8 --- /dev/null +++ b/tools/eclipse/debug_support/icons/sample.gif 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 |