diff options
Diffstat (limited to 'plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model')
43 files changed, 12356 insertions, 0 deletions
diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java new file mode 100644 index 000000000..4c9a8b773 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ICastToType.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; + +public interface ICastToType { + + void onCastToTypeChanged(); + + TCFDataCache<ISymbols.Symbol> getType(); +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java new file mode 100644 index 000000000..43595cd2c --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IDetailsProvider.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +public interface IDetailsProvider { + + boolean getDetailText(StyledStringBuffer buf, Runnable done); +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java new file mode 100644 index 000000000..1b098052d --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISourceNotFoundPresentation.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.ui.IEditorInput; + +/** + * Adapter to customize source presentation for the source-not-found case. + */ +public interface ISourceNotFoundPresentation { + + /** + * Returns an editor input that should be used to display a source-not-found editor + * for the given object or <code>null</code> if unable to provide an editor input + * for the given object. + * + * @param element a debug model element + * @param cfg the launch configuration of the debug element + * @param file unresolved source file path + * @return an editor input, or <code>null</code> if none + */ + public IEditorInput getEditorInput(Object element, ILaunchConfiguration cfg, String file); + + /** + * Returns the id of the source-not-found editor to use to display the + * given editor input and object, or <code>null</code> if + * unable to provide an editor id. + * + * @param input an editor input that was previously retrieved from this + * presentation's <code>getEditorInput</code> method + * @param element the object that was used in the call to + * <code>getEditorInput</code>, that corresponds to the given editor + * input + * @return an editor id, or <code>null</code> if none + */ + public String getEditorId(IEditorInput input, Object element); + +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java new file mode 100644 index 000000000..36ca46fea --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/ISymbolOwner.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +public interface ISymbolOwner { + + void addSymbol(TCFNodeSymbol s); + + void removeSymbol(TCFNodeSymbol s); +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java new file mode 100644 index 000000000..6fe499bd0 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/IWatchInExpressions.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.tcf.util.TCFDataCache; + +public interface IWatchInExpressions { + + TCFDataCache<String> getExpressionText(); +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java new file mode 100644 index 000000000..eb0b82526 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/StyledStringBuffer.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.swt.graphics.RGB; + +class StyledStringBuffer { + + private final StringBuffer bf = new StringBuffer(); + private final ArrayList<Style> styles = new ArrayList<Style>(); + + static class Style { + int pos; + int len; + int font; + RGB bg; + RGB fg; + } + + StyledStringBuffer append(int pos, int font, RGB bg, RGB fg) { + Style x = new Style(); + x.pos = pos; + x.len = bf.length() - pos; + x.font = font; + x.bg = bg; + x.fg = fg; + styles.add(x); + return this; + } + + StyledStringBuffer append(String s) { + bf.append(s); + return this; + } + + StyledStringBuffer append(char ch) { + bf.append(ch); + return this; + } + + StyledStringBuffer append(int i) { + bf.append(i); + return this; + } + + StyledStringBuffer append(String s, int font) { + Style x = new Style(); + x.pos = bf.length(); + x.len = s.length(); + x.font = font; + styles.add(x); + bf.append(s); + return this; + } + + StyledStringBuffer append(String s, int font, RGB bg, RGB fg) { + Style x = new Style(); + x.pos = bf.length(); + x.len = s.length(); + x.font = font; + x.bg = bg; + x.fg = fg; + styles.add(x); + bf.append(s); + return this; + } + + StyledStringBuffer append(StyledStringBuffer s) { + int offs = bf.length(); + for (Style y : s.styles) { + Style x = new Style(); + x.pos = y.pos + offs; + x.len = y.len; + x.font = y.font; + x.bg = y.bg; + x.fg = y.fg; + styles.add(x); + } + bf.append(s.bf); + return this; + } + + StringBuffer getStringBuffer() { + return bf; + } + + Collection<Style> getStyle() { + return styles; + } + + int length() { + return bf.length(); + } + + @Override + public String toString() { + return bf.toString(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java new file mode 100644 index 000000000..a3dcf8b2a --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationImageProvider.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.texteditor.IAnnotationImageProvider; + +public class TCFAnnotationImageProvider implements IAnnotationImageProvider { + + public Image getManagedImage(Annotation annotation) { + return ((TCFAnnotationManager.TCFAnnotation)annotation).image; + } + + public String getImageDescriptorId(Annotation annotation) { + return null; + } + + public ImageDescriptor getImageDescriptor(String imageDescritporId) { + return null; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java new file mode 100644 index 000000000..22e7ba3f4 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFAnnotationManager.java @@ -0,0 +1,622 @@ +/******************************************************************************* + * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationListener; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.ISourcePresentation; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupDirector; +import org.eclipse.tcf.internal.debug.model.ITCFBreakpointListener; +import org.eclipse.tcf.internal.debug.model.TCFBreakpointsStatus; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.internal.debug.model.TCFSourceRef; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.JSON; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IBreakpoints; +import org.eclipse.tcf.services.ILineNumbers; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.tcf.util.TCFTask; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWindowListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + +public class TCFAnnotationManager { + + private static final String + TYPE_BP_INSTANCE = "org.eclipse.tcf.debug.breakpoint_instance", + TYPE_TOP_FRAME = "org.eclipse.tcf.debug.top_frame", + TYPE_STACK_FRAME = "org.eclipse.tcf.debug.stack_frame"; + + class TCFAnnotation extends Annotation { + + final ILineNumbers.CodeArea area; + final Image image; + final String text; + final String type; + final int hash_code; + + IAnnotationModel model; + + TCFAnnotation(ILineNumbers.CodeArea area, Image image, String text, String type) { + this.area = area; + this.image = image; + this.text = text; + this.type = type; + hash_code = area.hashCode() + image.hashCode() + text.hashCode() + type.hashCode(); + setText(text); + setType(type); + } + + protected Image getImage() { + return image; + } + + void dispose() { + assert Thread.currentThread() == display.getThread(); + if (model != null) { + model.removeAnnotation(this); + model = null; + } + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof TCFAnnotation)) return false; + TCFAnnotation a = (TCFAnnotation)o; + if (!area.equals(a.area)) return false; + if (!image.equals(a.image)) return false; + if (!text.equals(a.text)) return false; + if (!type.equals(a.type)) return false; + return true; + } + + @Override + public int hashCode() { + return hash_code; + } + } + + private class WorkbenchWindowInfo { + final LinkedList<TCFAnnotation> annotations = new LinkedList<TCFAnnotation>(); + + void dispose() { + for (TCFAnnotation a : annotations) a.dispose(); + annotations.clear(); + } + } + + private final HashMap<IWorkbenchWindow,WorkbenchWindowInfo> windows = + new HashMap<IWorkbenchWindow,WorkbenchWindowInfo>(); + + private final HashSet<IWorkbenchWindow> dirty_windows = new HashSet<IWorkbenchWindow>(); + private final HashSet<TCFLaunch> dirty_launches = new HashSet<TCFLaunch>(); + private final HashSet<TCFLaunch> changed_launch_cfgs = new HashSet<TCFLaunch>(); + + private final TCFLaunch.LaunchListener launch_listener = new TCFLaunch.LaunchListener() { + + public void onCreated(TCFLaunch launch) { + } + + public void onConnected(final TCFLaunch launch) { + updateAnnotations(null, launch); + TCFBreakpointsStatus bps = launch.getBreakpointsStatus(); + if (bps == null) return; + bps.addListener(new ITCFBreakpointListener() { + + public void breakpointStatusChanged(String id) { + updateAnnotations(null, launch); + } + + public void breakpointRemoved(String id) { + updateAnnotations(null, launch); + } + + public void breakpointChanged(String id) { + } + }); + } + + public void onDisconnected(final TCFLaunch launch) { + assert Protocol.isDispatchThread(); + updateAnnotations(null, launch); + } + + public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data) { + } + + public void onProcessStreamError(TCFLaunch launch, String process_id, + int stream_id, Exception error, int lost_size) { + } + }; + + private final ISelectionListener selection_listener = new ISelectionListener() { + + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + updateAnnotations(part.getSite().getWorkbenchWindow(), (TCFLaunch)null); + if (selection instanceof IStructuredSelection) { + final Object obj = ((IStructuredSelection)selection).getFirstElement(); + if (obj instanceof TCFNodeStackFrame && ((TCFNodeStackFrame)obj).isTraceLimit()) { + Protocol.invokeLater(new Runnable() { + public void run() { + ((TCFNodeStackFrame)obj).riseTraceLimit(); + } + }); + } + } + } + }; + + private final IWindowListener window_listener = new IWindowListener() { + + public void windowActivated(IWorkbenchWindow window) { + } + + public void windowClosed(IWorkbenchWindow window) { + assert windows.get(window) != null; + window.getSelectionService().removeSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + windows.remove(window).dispose(); + } + + public void windowDeactivated(IWorkbenchWindow window) { + } + + public void windowOpened(IWorkbenchWindow window) { + if (windows.get(window) != null) return; + window.getSelectionService().addSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + windows.put(window, new WorkbenchWindowInfo()); + updateAnnotations(window, (TCFLaunch)null); + } + }; + + private final ILaunchConfigurationListener launch_conf_listener = new ILaunchConfigurationListener() { + + public void launchConfigurationAdded(ILaunchConfiguration cfg) { + } + + public void launchConfigurationChanged(final ILaunchConfiguration cfg) { + displayExec(new Runnable() { + public void run() { + ILaunch[] arr = launch_manager.getLaunches(); + for (ILaunch l : arr) { + if (l instanceof TCFLaunch) { + TCFLaunch t = (TCFLaunch)l; + if (cfg.equals(t.getLaunchConfiguration())) { + changed_launch_cfgs.add(t); + updateAnnotations(null, t); + } + } + } + } + }); + } + + public void launchConfigurationRemoved(ILaunchConfiguration cfg) { + } + }; + + private final Display display = Display.getDefault(); + private final ILaunchManager launch_manager = DebugPlugin.getDefault().getLaunchManager(); + private int update_unnotations_cnt = 0; + private boolean started; + private boolean disposed; + + public TCFAnnotationManager() { + assert Protocol.isDispatchThread(); + TCFLaunch.addListener(launch_listener); + launch_manager.addLaunchConfigurationListener(launch_conf_listener); + displayExec(new Runnable() { + public void run() { + if (!PlatformUI.isWorkbenchRunning() || PlatformUI.getWorkbench().isStarting()) { + display.timerExec(200, this); + } + else if (!PlatformUI.getWorkbench().isClosing()) { + started = true; + PlatformUI.getWorkbench().addWindowListener(window_listener); + for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { + window_listener.windowOpened(window); + } + IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (w != null) window_listener.windowActivated(w); + } + } + }); + } + + public void dispose() { + if (disposed) return; + assert Protocol.isDispatchThread(); + disposed = true; + launch_manager.removeLaunchConfigurationListener(launch_conf_listener); + TCFLaunch.removeListener(launch_listener); + displayExec(new Runnable() { + public void run() { + if (!started) return; + PlatformUI.getWorkbench().removeWindowListener(window_listener); + for (IWorkbenchWindow window : windows.keySet()) { + window.getSelectionService().removeSelectionListener( + IDebugUIConstants.ID_DEBUG_VIEW, selection_listener); + windows.get(window).dispose(); + } + windows.clear(); + } + }); + } + + private void displayExec(Runnable r) { + synchronized (Device.class) { + if (!display.isDisposed()) { + display.asyncExec(r); + } + } + } + + /** + * Return breakpoint status info for all active TCF debug sessions. + * @param breakpoint + * @return breakpoint status as defined by TCF Breakpoints service. + */ + Map<TCFLaunch,Map<String,Object>> getBreakpointStatus(IBreakpoint breakpoint) { + assert Protocol.isDispatchThread(); + Map<TCFLaunch,Map<String,Object>> map = new HashMap<TCFLaunch,Map<String,Object>>(); + if (disposed) return null; + ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches(); + for (ILaunch l : launches) { + if (l instanceof TCFLaunch) { + TCFLaunch launch = (TCFLaunch)l; + TCFBreakpointsStatus bs = launch.getBreakpointsStatus(); + if (bs != null) map.put(launch, bs.getStatus(breakpoint)); + } + } + return map; + } + + /** + * Return breakpoint status text for all active TCF debug sessions. + * @param breakpoint + * @return breakpoint status as a string. + */ + @SuppressWarnings("unchecked") + String getBreakpointStatusText(IBreakpoint breakpoint) { + assert Protocol.isDispatchThread(); + String error = null; + for (Map<String,Object> map : getBreakpointStatus(breakpoint).values()) { + if (map != null) { + String s = (String)map.get(IBreakpoints.STATUS_ERROR); + if (s != null && error == null) error = s; + Object planted = map.get(IBreakpoints.STATUS_INSTANCES); + if (planted != null) { + Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)planted; + for (Map<String,Object> m : list) { + if (m.get(IBreakpoints.INSTANCE_ERROR) == null) { + return "Planted"; + } + } + } + } + } + return error; + } + + @SuppressWarnings("unchecked") + private Object[] toObjectArray(Object o) { + if (o == null) return null; + Collection<Object> c = (Collection<Object>)o; + return (Object[])c.toArray(new Object[c.size()]); + } + + @SuppressWarnings("unchecked") + private Map<String,Object> toObjectMap(Object o) { + return (Map<String,Object>)o; + } + + private void addBreakpointErrorAnnotation(List<TCFAnnotation> set, TCFLaunch launch, String id, String error) { + Map<String,Object> props = launch.getBreakpointsStatus().getProperties(id); + if (props != null) { + String file = (String)props.get(IBreakpoints.PROP_FILE); + Number line = (Number)props.get(IBreakpoints.PROP_LINE); + if (file != null && line != null) { + ILineNumbers.CodeArea area = new ILineNumbers.CodeArea(null, file, + line.intValue(), 0, line.intValue() + 1, 0, + null, null, 0, false, false, false, false); + TCFAnnotation a = new TCFAnnotation(area, + ImageCache.getImage(ImageCache.IMG_BREAKPOINT_ERROR), + "Cannot plant breakpoint: " + error, + TYPE_BP_INSTANCE); + set.add(a); + } + } + } + + private void updateAnnotations(IWorkbenchWindow window, final TCFNode node) { + if (disposed) return; + assert Thread.currentThread() == display.getThread(); + final WorkbenchWindowInfo win_info = windows.get(window); + if (win_info == null) return; + List<TCFAnnotation> set = null; + if (node != null) { + set = new TCFTask<List<TCFAnnotation>>(node.getChannel()) { + public void run() { + if (node.isDisposed()) { + done(null); + return; + } + TCFNodeExecContext thread = null; + TCFNodeExecContext memory = null; + TCFNodeStackFrame frame = null; + TCFNodeStackFrame last_top_frame = null; + String bp_group = null; + boolean suspended = false; + if (node instanceof TCFNodeStackFrame) { + thread = (TCFNodeExecContext)node.parent; + frame = (TCFNodeStackFrame)node; + } + else if (node instanceof TCFNodeExecContext) { + thread = (TCFNodeExecContext)node; + TCFChildrenStackTrace trace = thread.getStackTrace(); + if (!trace.validate(this)) return; + frame = trace.getTopFrame(); + } + if (thread != null) { + TCFDataCache<IRunControl.RunControlContext> rc_ctx_cache = thread.getRunContext(); + if (!rc_ctx_cache.validate(this)) return; + IRunControl.RunControlContext rc_ctx_data = rc_ctx_cache.getData(); + if (rc_ctx_data != null) bp_group = rc_ctx_data.getBPGroup(); + TCFDataCache<TCFNodeExecContext> mem_cache = thread.getMemoryNode(); + if (!mem_cache.validate(this)) return; + memory = mem_cache.getData(); + if (bp_group == null && memory != null && rc_ctx_data != null && rc_ctx_data.hasState()) bp_group = memory.id; + last_top_frame = thread.getLastTopFrame(); + TCFDataCache<TCFContextState> state_cache = thread.getState(); + if (!state_cache.validate(this)) return; + suspended = state_cache.getData() != null && state_cache.getData().is_suspended; + } + List<TCFAnnotation> set = new ArrayList<TCFAnnotation>(); + if (memory != null) { + TCFLaunch launch = node.launch; + TCFBreakpointsStatus bs = launch.getBreakpointsStatus(); + if (bs != null) { + for (String id : bs.getStatusIDs()) { + Map<String,Object> map = bs.getStatus(id); + if (map == null) continue; + String error = (String)map.get(IBreakpoints.STATUS_ERROR); + if (error != null) addBreakpointErrorAnnotation(set, launch, id, error); + Object[] arr = toObjectArray(map.get(IBreakpoints.STATUS_INSTANCES)); + if (arr == null) continue; + for (Object o : arr) { + Map<String,Object> m = toObjectMap(o); + String ctx_id = (String)m.get(IBreakpoints.INSTANCE_CONTEXT); + if (ctx_id == null) continue; + if (!ctx_id.equals(node.id) && !ctx_id.equals(bp_group)) continue; + error = (String)m.get(IBreakpoints.INSTANCE_ERROR); + BigInteger addr = JSON.toBigInteger((Number)m.get(IBreakpoints.INSTANCE_ADDRESS)); + if (addr != null) { + ILineNumbers.CodeArea area = null; + TCFDataCache<TCFSourceRef> line_cache = memory.getLineInfo(addr); + if (line_cache != null) { + if (!line_cache.validate(this)) return; + TCFSourceRef line_data = line_cache.getData(); + if (line_data != null && line_data.area != null) area = line_data.area; + } + if (area == null) { + Map<String,Object> props = bs.getProperties(id); + if (props != null) { + String file = (String)props.get(IBreakpoints.PROP_FILE); + Number line = (Number)props.get(IBreakpoints.PROP_LINE); + if (file != null && line != null) { + area = new ILineNumbers.CodeArea(null, file, + line.intValue(), 0, line.intValue() + 1, 0, + null, null, 0, false, false, false, false); + } + } + } + if (area != null) { + if (error != null) { + TCFAnnotation a = new TCFAnnotation(area, + ImageCache.getImage(ImageCache.IMG_BREAKPOINT_ERROR), + "Cannot plant breakpoint at 0x" + addr.toString(16) + ": " + error, + TYPE_BP_INSTANCE); + set.add(a); + error = null; + } + else { + TCFAnnotation a = new TCFAnnotation(area, + ImageCache.getImage(ImageCache.IMG_BREAKPOINT_INSTALLED), + "Breakpoint planted at 0x" + addr.toString(16), + TYPE_BP_INSTANCE); + set.add(a); + } + } + } + if (error != null) addBreakpointErrorAnnotation(set, launch, id, error); + } + } + } + } + if (suspended && frame != null && frame.getFrameNo() >= 0) { + TCFDataCache<TCFSourceRef> line_cache = frame.getLineInfo(); + if (!line_cache.validate(this)) return; + TCFSourceRef line_data = line_cache.getData(); + if (line_data != null && line_data.area != null) { + TCFAnnotation a = null; + if (frame.getFrameNo() == 0) { + a = new TCFAnnotation(line_data.area, + DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER_TOP), + "Current Instruction Pointer", + TYPE_TOP_FRAME); + } + else { + a = new TCFAnnotation(line_data.area, + DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER), + "Stack Frame", + TYPE_STACK_FRAME); + } + set.add(a); + } + } + if (!suspended && last_top_frame != null) { + TCFDataCache<TCFSourceRef> line_cache = last_top_frame.getLineInfo(); + if (!line_cache.validate(this)) return; + TCFSourceRef line_data = line_cache.getData(); + if (line_data != null && line_data.area != null) { + TCFAnnotation a = new TCFAnnotation(line_data.area, + DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_INSTRUCTION_POINTER), + "Last Instruction Pointer position", + TYPE_STACK_FRAME); + set.add(a); + } + } + done(set); + } + }.getE(); + } + boolean flush_all = node == null || changed_launch_cfgs.contains(node.launch); + Iterator<TCFAnnotation> i = win_info.annotations.iterator(); + while (i.hasNext()) { + TCFAnnotation a = i.next(); + if (!flush_all && set != null && set.remove(a)) continue; + a.dispose(); + i.remove(); + } + if (set == null || set.size() == 0) return; + Map<IEditorInput,ITextEditor> editors = new HashMap<IEditorInput,ITextEditor>(); + for (IEditorReference ref : window.getActivePage().getEditorReferences()) { + IEditorPart part = ref.getEditor(false); + if (!(part instanceof ITextEditor)) continue; + ITextEditor editor = (ITextEditor)part; + editors.put(editor.getEditorInput(), editor); + } + ISourcePresentation presentation = TCFModelPresentation.getDefault(); + for (TCFAnnotation a : set) { + Object source_element = TCFSourceLookupDirector.lookup(node.launch, a.area); + if (source_element == null) continue; + IEditorInput editor_input = presentation.getEditorInput(source_element); + ITextEditor editor = editors.get(editor_input); + if (editor == null) continue; + IDocumentProvider doc_provider = editor.getDocumentProvider(); + IAnnotationModel ann_model = doc_provider.getAnnotationModel(editor_input); + if (ann_model == null) continue; + IRegion region = null; + try { + doc_provider.connect(editor_input); + } + catch (CoreException e) { + } + try { + IDocument document = doc_provider.getDocument(editor_input); + if (document != null) region = document.getLineInformation(a.area.start_line - 1); + } + catch (BadLocationException e) { + } + finally { + doc_provider.disconnect(editor_input); + } + if (region == null) continue; + ann_model.addAnnotation(a, new Position(region.getOffset(), region.getLength())); + a.model = ann_model; + win_info.annotations.add(a); + } + } + + private void updateAnnotations(final int cnt) { + displayExec(new Runnable() { + public void run() { + synchronized (TCFAnnotationManager.this) { + if (cnt != update_unnotations_cnt) return; + } + for (IWorkbenchWindow window : windows.keySet()) { + if (dirty_windows.contains(null) || dirty_windows.contains(window)) { + TCFNode node = null; + try { + ISelection active_context = DebugUITools.getDebugContextManager() + .getContextService(window).getActiveContext(); + if (active_context instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection)active_context; + if (!selection.isEmpty()) { + Object first_element = selection.getFirstElement(); + if (first_element instanceof IAdaptable) { + node = (TCFNode)((IAdaptable)first_element).getAdapter(TCFNode.class); + } + } + } + if (dirty_launches.contains(null) || node != null && dirty_launches.contains(node.launch)) { + updateAnnotations(window, node); + } + } + catch (Throwable x) { + if (node == null || !node.isDisposed()) { + Activator.log("Cannot update editor annotations", x); + } + } + } + } + for (TCFLaunch launch : dirty_launches) { + if (launch != null) launch.removePendingClient(TCFAnnotationManager.this); + } + changed_launch_cfgs.clear(); + dirty_windows.clear(); + dirty_launches.clear(); + } + }); + } + + synchronized void updateAnnotations(final IWorkbenchWindow window, final TCFLaunch launch) { + final int cnt = ++update_unnotations_cnt; + displayExec(new Runnable() { + public void run() { + dirty_windows.add(window); + dirty_launches.add(launch); + updateAnnotations(cnt); + } + }); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java new file mode 100644 index 000000000..1f98fa578 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildren.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.util.TCFDataCache; + +/** + * TCFChildren is a concrete type of TCF data cache that is used to cache a list of children. + */ +public abstract class TCFChildren extends TCFDataCache<Map<String,TCFNode>> { + + private final int pool_margin; + private final Map<String,TCFNode> node_pool = new LinkedHashMap<String,TCFNode>(32, 0.75f, true); + + protected final TCFNode node; + + private static final TCFNode[] EMPTY_NODE_ARRAY = new TCFNode[0]; + + private TCFNode[] array; + + TCFChildren(TCFNode node) { + super(node.channel); + this.node = node; + pool_margin = 0; + node.addDataCache(this); + } + + TCFChildren(TCFNode node, int pool_margin) { + super(node.channel); + this.node = node; + this.pool_margin = pool_margin; + node.addDataCache(this); + } + + /** + * Dispose the cache and all nodes in the nodes pool. + */ + @Override + public void dispose() { + assert !isDisposed(); + node.removeDataCache(this); + for (TCFNode n : node_pool.values()) n.dispose(); + node_pool.clear(); + super.dispose(); + } + + /** + * Remove a node from cache. + * The method is called every time a node is disposed. + * @param id - node ID + */ + void onNodeDisposed(String id) { + node_pool.remove(id); + if (isValid()) { + array = null; + Map<String,TCFNode> data = getData(); + if (data != null) data.remove(id); + } + } + + private void addToPool(Map<String,TCFNode> data) { + assert !isDisposed(); + for (TCFNode n : data.values()) { + assert data.get(n.id) == n; + assert n.parent == node; + node_pool.put(n.id, n); + } + if (node_pool.size() > data.size() + pool_margin) { + String[] arr = node_pool.keySet().toArray(new String[node_pool.size()]); + for (String id : arr) { + if (data.get(id) == null) { + node_pool.get(id).dispose(); + if (node_pool.size() <= data.size() + pool_margin) break; + } + } + } + } + + /** + * End cache pending state. + * @param token - pending command handle. + * @param error - data retrieval error or null + * @param data - up-to-date map of children nodes + */ + @Override + public void set(IToken token, Throwable error, Map<String,TCFNode> data) { + array = null; + if (isDisposed()) { + // A command can return data after the cache element has been disposed. + // Just ignore the data in such case. + super.set(token, null, null); + assert node_pool.isEmpty(); + } + else if (data != null) { + super.set(token, error, data); + addToPool(data); + } + else { + super.set(token, error, new HashMap<String,TCFNode>()); + } + } + + /** + * Set given data to the cache, mark cache as valid, cancel any pending data retrieval. + * @param data - up-to-date data to store in the cache, null means empty collection of nodes. + */ + @Override + public void reset(Map<String,TCFNode> data) { + assert !isDisposed(); + array = null; + if (data != null) { + super.reset(data); + addToPool(data); + } + else { + super.reset(new HashMap<String,TCFNode>()); + } + } + + /** + * Invalidate the cache. If retrieval is in progress - let it continue. + */ + @Override + public void reset() { + super.reset(); + array = null; + } + + /** + * Force cache to invalid state, cancel pending data retrieval if any. + */ + @Override + public void cancel() { + super.cancel(); + array = null; + } + + /** + * Add a node to collection of children. + * @param n - a node. + */ + void add(TCFNode n) { + assert !isDisposed(); + assert !n.isDisposed(); + assert node_pool.get(n.id) == null; + assert n.parent == node; + node_pool.put(n.id, n); + if (isValid()) { + array = null; + Map<String,TCFNode> data = getData(); + if (data != null) data.put(n.id, n); + } + } + + /** + * Return collection of all nodes, including current children as well as + * currently unused nodes from the pool. + * To get only current children use getData() method. + * @return Collection of nodes. + */ + Collection<TCFNode> getNodes() { + return node_pool.values(); + } + + /** + * Return current number of children. + * The cache must be valid for the method to work. + * @return number of children. + */ + public int size() { + assert isValid(); + Map<String,TCFNode> data = getData(); + return data == null ? 0 : data.size(); + } + + /** + * Return current children nodes as a sorted array. + * @return array of nodes. + */ + public TCFNode[] toArray() { + assert isValid(); + if (array != null) return array; + Map<String,TCFNode> data = getData(); + if (data == null || data.size() == 0) return array = EMPTY_NODE_ARRAY; + array = data.values().toArray(new TCFNode[data.size()]); + Arrays.sort(array); + return array; + } + + /** + * Return current children nodes in IChildrenUpdate object. + * @param update - children update request object. + * @param done - a call-back object, it is called when cache state changes. + * @return true if all done, false if data request is pending. + */ + boolean getData(IChildrenUpdate update, Runnable done) { + if (!validate(done)) return false; + TCFNode[] arr = toArray(); + int offset = 0; + int r_offset = update.getOffset(); + int r_length = update.getLength(); + for (TCFNode n : arr) { + if (offset >= r_offset && offset < r_offset + r_length) { + update.setChild(n, offset); + } + offset++; + } + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java new file mode 100644 index 000000000..eb4d6d85b --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExecContext.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IMemory; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.util.TCFDataCache; + + +/** + * This class is used to maintain a dynamic list of both executable contexts and memory spaces + * that are children of a given parent context. The job is slightly complicated by necessity + * to merge results from two independent services. + */ +public class TCFChildrenExecContext extends TCFChildren { + + private final TCFChildren mem_children; + private final TCFChildren run_children; + + TCFChildrenExecContext(final TCFNode node) { + super(node); + mem_children = new TCFChildren(node) { + @Override + protected boolean startDataRetrieval() { + IMemory mem = node.model.getLaunch().getService(IMemory.class); + if (mem == null) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + assert command == null; + command = mem.getChildren(node.id, new IMemory.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + int cnt = 0; + data = new HashMap<String,TCFNode>(); + for (String id : contexts) { + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeExecContext(node, id); + ((TCFNodeExecContext)n).setMemSeqNo(cnt++); + assert n.parent == node; + data.put(id, n); + } + } + set(token, error, data); + } + }); + return false; + } + }; + run_children = new TCFChildren(node) { + @Override + protected boolean startDataRetrieval() { + IRunControl run = node.model.getLaunch().getService(IRunControl.class); + if (run == null) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + assert command == null; + command = run.getChildren(node.id, new IRunControl.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + int cnt = 0; + data = new HashMap<String,TCFNode>(); + for (String id : contexts) { + TCFNode n = node.model.getNode(id); + if (n == null) n = new TCFNodeExecContext(node, id); + ((TCFNodeExecContext)n).setExeSeqNo(cnt++); + assert n.parent == node; + data.put(id, n); + } + } + set(token, error, data); + } + }); + return false; + } + }; + } + + @Override + protected boolean startDataRetrieval() { + TCFDataCache<?> pending = null; + if (!mem_children.validate()) pending = mem_children; + if (!run_children.validate()) pending = run_children; + if (pending != null) { + pending.wait(this); + return false; + } + Throwable error = mem_children.getError(); + if (error == null) error = run_children.getError(); + Map<String,TCFNode> data = new HashMap<String,TCFNode>(); + Map<String,TCFNode> m1 = mem_children.getData(); + Map<String,TCFNode> m2 = run_children.getData(); + if (m1 != null) data.putAll(m1); + if (m2 != null) data.putAll(m2); + set(null, error, data); + return true; + } + + void onContextAdded(IRunControl.RunControlContext context) { + // To preserve children order need to reset children list. + reset(); + run_children.reset(); + mem_children.reset(); + assert !node.isDisposed(); + String id = context.getID(); + TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); + if (n == null) { + n = new TCFNodeExecContext(node, id); + n.postContextAddedDelta(); + add(n); + } + else { + n.postAllChangedDelta(); + } + run_children.add(n); + n.setRunContext(context); + } + + void onContextAdded(IMemory.MemoryContext context) { + // To preserve children order need to reset children list. + reset(); + run_children.reset(); + mem_children.reset(); + assert !node.isDisposed(); + String id = context.getID(); + TCFNodeExecContext n = (TCFNodeExecContext)node.model.getNode(id); + if (n == null) { + n = new TCFNodeExecContext(node, id); + n.postContextAddedDelta(); + add(n); + } + else { + n.postAllChangedDelta(); + } + mem_children.add(n); + n.setMemoryContext(context); + } + + void onAncestorContextChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExecContext)n).onAncestorContextChanged(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java new file mode 100644 index 000000000..66deb3200 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenExpressions.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; + +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.core.model.IWatchExpression; + +public class TCFChildrenExpressions extends TCFChildren { + + TCFChildrenExpressions(TCFNode node) { + super(node, 128); + } + + void onSuspended() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended(); + } + + void onRegisterValueChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged(); + } + + void onMemoryChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged(); + } + + void onMemoryMapChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged(); + } + + private TCFNodeExpression findScript(String text) { + for (TCFNode n : getNodes()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (text.equals(e.getScript())) return e; + } + return null; + } + + @Override + protected boolean startDataRetrieval() { + int cnt = 0; + HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); + for (final IExpression e : node.model.getExpressionManager().getExpressions()) { + String text = e.getExpressionText(); + TCFNodeExpression n = findScript(text); + if (n == null) add(n = new TCFNodeExpression(node, text, null, null, null, -1, false)); + n.setSortPosition(cnt++); + if (e instanceof IWatchExpression) n.setEnabled(((IWatchExpression)e).isEnabled()); + data.put(n.id, n); + } + set(null, null, data); + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java new file mode 100644 index 000000000..79eef7dd3 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenHoverExpressions.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; + +/** + * Provides the cache of root nodes for the expression hover. + */ +class TCFChildrenHoverExpressions extends TCFChildren { + + private String expression; + + TCFChildrenHoverExpressions(TCFNode parent) { + super(parent, 16); + } + + void setExpression(String expression) { + if (expression == this.expression) return; + if (expression != null && expression.equals(this.expression)) return; + this.expression = expression; + cancel(); + } + + void onSuspended() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended(); + } + + void onRegisterValueChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged(); + } + + void onMemoryChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged(); + } + + void onMemoryMapChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged(); + } + + private TCFNodeExpression findScript(String text) { + for (TCFNode n : getNodes()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (text.equals(e.getScript())) return e; + } + return null; + } + + @Override + protected boolean startDataRetrieval() { + HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); + if (expression != null) { + TCFNodeExpression expression_node = findScript(expression); + if (expression_node == null) { + add(expression_node = new TCFNodeExpression(node, expression, null, null, null, -1, false)); + } + data.put(expression_node.id, expression_node); + } + set(null, null, data); + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java new file mode 100644 index 000000000..e87ef5b5c --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLocalVariables.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IExpressions; + +public class TCFChildrenLocalVariables extends TCFChildren { + + private final TCFNodeStackFrame node; + + TCFChildrenLocalVariables(TCFNodeStackFrame node) { + super(node, 128); + this.node = node; + } + + void onSuspended() { + reset(); + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended(); + } + + void onRegisterValueChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged(); + } + + void onMemoryChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged(); + } + + void onMemoryMapChanged() { + reset(); + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged(); + } + + @Override + protected boolean startDataRetrieval() { + IExpressions exps = node.model.getLaunch().getService(IExpressions.class); + if (exps == null || node.isEmulated()) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + TCFChildrenStackTrace stack_trace_cache = ((TCFNodeExecContext)node.parent).getStackTrace(); + if (!stack_trace_cache.validate(this)) return false; // node.getFrameNo() is not valid + if (node.getFrameNo() < 0) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + assert command == null; + command = exps.getChildren(node.id, new IExpressions.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + int cnt = 0; + data = new HashMap<String,TCFNode>(); + for (String id : contexts) { + TCFNodeExpression n = (TCFNodeExpression)node.model.getNode(id); + if (n == null) n = new TCFNodeExpression(node, null, null, id, null, -1, false); + assert n.id.equals(id); + assert n.parent == node; + n.setSortPosition(cnt++); + data.put(n.id, n); + } + } + set(token, error, data); + } + }); + return false; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java new file mode 100644 index 000000000..f5c2f8476 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenLogExpressions.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.HashSet; + +public class TCFChildrenLogExpressions extends TCFChildren { + + private final HashSet<String> scripts = new HashSet<String>(); + + TCFChildrenLogExpressions(TCFNodeExecContext node) { + super(node, 16); + } + + void onSuspended() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onSuspended(); + scripts.clear(); + reset(); + } + + void onRegisterValueChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onRegisterValueChanged(); + } + + void onMemoryChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryChanged(); + } + + void onMemoryMapChanged() { + for (TCFNode n : getNodes()) ((TCFNodeExpression)n).onMemoryMapChanged(); + } + + public TCFNodeExpression findScript(String script) { + for (TCFNode n : getNodes()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (script.equals(e.getScript())) return e; + } + return null; + } + + public void addScript(String script) { + if (scripts.add(script)) reset(); + } + + @Override + protected boolean startDataRetrieval() { + HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); + for (String script : scripts) { + TCFNodeExpression expression_node = findScript(script); + if (expression_node == null) { + add(expression_node = new TCFNodeExpression(node, script, null, null, null, -1, false)); + } + data.put(expression_node.id, expression_node); + } + set(null, null, data); + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java new file mode 100644 index 000000000..8e37eb98f --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenModules.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext.MemoryRegion; +import org.eclipse.tcf.util.TCFDataCache; + +/** + * Provides and caches memory regions (modules) for a context. + */ +public class TCFChildrenModules extends TCFChildren { + + public TCFChildrenModules(TCFNode node) { + super(node, 128); + } + + void onMemoryMapChanged() { + reset(); + } + + @Override + protected boolean startDataRetrieval() { + TCFNodeExecContext exe = (TCFNodeExecContext)node; + TCFDataCache<MemoryRegion[]> map_cache = exe.getMemoryMap(); + if (!map_cache.validate(this)) return false; + MemoryRegion[] map = map_cache.getData(); + Map<String, TCFNode> data = new HashMap<String, TCFNode>(); + if (map != null) { + for (MemoryRegion region : map) { + String id = node.id + ".Module-" + region.region.getFileName() + '@' + region.region.getAddress(); + TCFNodeModule module = (TCFNodeModule) node.model.getNode(id); + if (module == null) module = new TCFNodeModule(node, id); + module.setRegion(region.region); + data.put(id, module); + } + } + set(null, map_cache.getError(), data); + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java new file mode 100644 index 000000000..c2a91bb2e --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenRegisters.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IRegisters; + + +public class TCFChildrenRegisters extends TCFChildren { + + TCFChildrenRegisters(TCFNode node) { + super(node, 128); + } + + void onSuspended() { + for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onSuspended(); + } + + void onParentValueChanged() { + for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onParentValueChanged(); + } + + void onRegistersChanged() { + for (TCFNode n : getNodes()) ((TCFNodeRegister)n).onRegistersChanged(); + reset(); + } + + @Override + void onNodeDisposed(String id) { + super.onNodeDisposed(id); + if (node instanceof TCFNodeExecContext) { + // CPU register nodes are special case: + // they have executable node as parent, + // but they are also referenced as children of stack frames + for (TCFNode n : ((TCFNodeExecContext)node).getStackTrace().getNodes()) { + ((TCFNodeStackFrame)n).getRegisters().onNodeDisposed(id); + } + } + } + + @Override + protected boolean startDataRetrieval() { + IRegisters regs = node.model.getLaunch().getService(IRegisters.class); + if (regs == null) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + if (node instanceof TCFNodeStackFrame) { + TCFChildrenStackTrace stack_trace_cache = ((TCFNodeExecContext)node.parent).getStackTrace(); + if (!stack_trace_cache.validate(this)) return false; // node.getFrameNo() is not valid + final int frame_no = ((TCFNodeStackFrame)node).getFrameNo(); + if (frame_no < 0) { + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + } + assert command == null; + command = regs.getChildren(node.id, new IRegisters.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + Map<String,TCFNode> data = null; + if (command == token && error == null) { + int index = 0; + data = new HashMap<String,TCFNode>(); + for (String id : contexts) { + TCFNodeRegister n = (TCFNodeRegister)node.model.getNode(id); + if (n == null) n = new TCFNodeRegister(node, id); + n.setIndex(index++); + data.put(id, n); + } + } + set(token, error, data); + } + }); + return false; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java new file mode 100644 index 000000000..e62ec500a --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenStackTrace.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IStackTrace; +import org.eclipse.tcf.util.TCFDataCache; + + +public class TCFChildrenStackTrace extends TCFChildren { + + private final TCFNodeExecContext node; + + private String top_frame_id; + private int limit_factor = 1; + + TCFChildrenStackTrace(TCFNodeExecContext node) { + super(node, 16); + this.node = node; + } + + void onSourceMappingChange() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onSourceMappingChange(); + } + + void onExpressionAddedOrRemoved() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onExpressionAddedOrRemoved(); + } + + void onSuspended() { + limit_factor = 1; + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onSuspended(); + reset(); + } + + void onRegistersChanged() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onRegistersChanged(); + reset(); + } + + void onMemoryMapChanged() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onMemoryMapChanged(); + reset(); + } + + void onMemoryChanged() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onMemoryChanged(); + reset(); + } + + void onRegisterValueChanged() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).onRegisterValueChanged(); + reset(); + } + + void onResumed() { + reset(null); + } + + void onPreferencesChanged() { + reset(); + } + + void riseTraceLimit() { + limit_factor++; + reset(); + } + + void postAllChangedDelta() { + for (TCFNode n : getNodes()) ((TCFNodeStackFrame)n).postAllChangedDelta(); + } + + public TCFNodeStackFrame getTopFrame() { + assert isValid(); + return (TCFNodeStackFrame)node.model.getNode(top_frame_id); + } + + @Override + public void set(IToken token, Throwable error, Map<String,TCFNode> data) { + for (TCFNode n : getNodes()) { + if (data == null || data.get(n.id) == null) ((TCFNodeStackFrame)n).setFrameNo(-1); + } + super.set(token, error, data); + } + + private void addEmulatedTopFrame(HashMap<String,TCFNode> data) { + top_frame_id = node.id + "-TF"; + TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(top_frame_id); + if (n == null) n = new TCFNodeStackFrame(node, top_frame_id, true); + n.setFrameNo(0); + n.setTraceLimit(false); + data.put(n.id, n); + } + + @Override + protected boolean startDataRetrieval() { + TCFDataCache<TCFContextState> state = node.getState(); + if (!state.validate(this)) return false; + if (node.isNotActive()) { + top_frame_id = null; + set(null, null, new HashMap<String,TCFNode>()); + return true; + } + Throwable state_error = state.getError(); + TCFContextState state_data = state.getData(); + if (state_error != null || state_data == null || !state_data.is_suspended) { + set(null, state_error, null); + return true; + } + final HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); + IStackTrace st = node.model.getLaunch().getService(IStackTrace.class); + if (st == null) { + addEmulatedTopFrame(data); + set(null, null, data); + return true; + } + assert command == null; + command = st.getChildren(node.id, new IStackTrace.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] contexts) { + if (command == token) { + if (error == null && contexts != null) { + int limit_value = 0; + boolean limit_enabled = node.model.getStackFramesLimitEnabled(); + if (limit_enabled) { + limit_value = node.model.getStackFramesLimitValue() * limit_factor; + if (limit_value <= 0) limit_value = limit_factor; + } + int cnt = contexts.length; + for (String id : contexts) { + cnt--; + if (!limit_enabled || cnt <= limit_value) { + TCFNodeStackFrame n = (TCFNodeStackFrame)node.model.getNode(id); + if (n == null) n = new TCFNodeStackFrame(node, id, false); + assert n.parent == node; + n.setFrameNo(cnt); + n.setTraceLimit(limit_enabled && cnt == limit_value); + data.put(id, n); + if (cnt == 0) top_frame_id = id; + } + } + } + if (data.size() == 0) addEmulatedTopFrame(data); + } + set(token, error, data); + } + }); + return false; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java new file mode 100644 index 000000000..84907d355 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFChildrenSubExpressions.java @@ -0,0 +1,249 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.tcf.services.IExpressions; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; + +public class TCFChildrenSubExpressions extends TCFChildren { + + private final int par_level; + private final int par_offs; + private final int par_size; + + TCFChildrenSubExpressions(TCFNode node, int par_level, int par_offs, int par_size) { + super(node, 128); + this.par_level = par_level; + this.par_offs = par_offs; + this.par_size = par_size; + } + + void onSuspended() { + reset(); + for (TCFNode n : getNodes()) { + if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onSuspended(); + if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onSuspended(); + } + } + + void onValueChanged() { + reset(); + for (TCFNode n : getNodes()) { + if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onValueChanged(); + if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onValueChanged(); + } + } + + void onRegisterValueChanged() { + reset(); + for (TCFNode n : getNodes()) { + if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onRegisterValueChanged(); + if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onRegisterValueChanged(); + } + } + + void onMemoryChanged() { + reset(); + for (TCFNode n : getNodes()) { + if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onMemoryChanged(); + if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onMemoryChanged(); + } + } + + void onMemoryMapChanged() { + reset(); + for (TCFNode n : getNodes()) { + if (n instanceof TCFNodeExpression) ((TCFNodeExpression)n).onMemoryMapChanged(); + if (n instanceof TCFNodeArrayPartition) ((TCFNodeArrayPartition)n).onMemoryMapChanged(); + } + } + + void onCastToTypeChanged() { + cancel(); + TCFNode a[] = getNodes().toArray(new TCFNode[getNodes().size()]); + for (int i = 0; i < a.length; i++) a[i].dispose(); + } + + private TCFNodeExpression findField(String field_id, boolean deref) { + assert field_id != null; + for (TCFNode n : getNodes()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (field_id.equals(e.getFieldID()) && e.isDeref() == deref) return e; + } + return null; + } + + private boolean findFields(ISymbols.Symbol type, Map<String,TCFNode> map, boolean deref) { + TCFDataCache<String[]> children_cache = node.model.getSymbolChildrenCache(type.getID()); + if (children_cache == null) return true; + if (!children_cache.validate(this)) return false; + String[] children = children_cache.getData(); + if (children == null) return true; + TCFDataCache<?> pending = null; + for (String id : children) { + TCFDataCache<ISymbols.Symbol> sym_cache = node.model.getSymbolInfoCache(id); + if (!sym_cache.validate()) { + pending = sym_cache; + } + else { + ISymbols.Symbol sym_data = sym_cache.getData(); + if (sym_data == null) continue; + if (sym_data.getSymbolClass() != ISymbols.SymbolClass.reference) continue; + if (sym_data.getName() == null) { + if (!findFields(sym_data, map, deref)) return false; + } + else { + TCFNodeExpression n = findField(id, deref); + if (n == null) add(n = new TCFNodeExpression(node, null, id, null, null, -1, deref)); + n.setSortPosition(map.size()); + map.put(n.id, n); + } + } + } + if (pending == null) return true; + pending.wait(this); + return false; + } + + private TCFNodeExpression findReg(String reg_id) { + assert reg_id != null; + for (TCFNode n : getNodes()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (reg_id.equals(e.getRegisterID())) return e; + } + return null; + } + + private boolean findRegs(TCFNodeRegister reg_node, Map<String,TCFNode> map) { + TCFChildren reg_children = reg_node.getChildren(); + if (!reg_children.validate(this)) return false; + for (TCFNode subnode : reg_children.toArray()) { + TCFNodeExpression n = findReg(subnode.id); + if (n == null) add(n = new TCFNodeExpression(node, null, null, null, subnode.id, -1, false)); + n.setSortPosition(map.size()); + map.put(n.id, n); + } + return true; + } + + private TCFNodeExpression findIndex(int index, boolean deref) { + assert index >= 0; + for (TCFNode n : getNodes()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (e.getIndex() == index && e.isDeref() == deref) return e; + } + return null; + } + + private TCFNodeArrayPartition findPartition(int offs, int size) { + assert offs >= 0; + for (TCFNode n : getNodes()) { + TCFNodeArrayPartition e = (TCFNodeArrayPartition)n; + if (e.getOffset() == offs && e.getSize() == size) return e; + } + return null; + } + + @Override + protected boolean startDataRetrieval() { + assert !isDisposed(); + TCFNode exp = node; + while (!(exp instanceof TCFNodeExpression)) exp = exp.parent; + TCFDataCache<ISymbols.Symbol> type_cache = ((TCFNodeExpression)exp).getType(); + if (!type_cache.validate(this)) return false; + ISymbols.Symbol type_data = type_cache.getData(); + if (type_data == null) { + HashMap<String,TCFNode> data = new HashMap<String,TCFNode>(); + TCFDataCache<IExpressions.Value> val_cache = ((TCFNodeExpression)exp).getValue(); + if (!val_cache.validate(this)) return false; + IExpressions.Value val_data = val_cache.getData(); + if (val_data != null) { + String reg_id = val_data.getRegisterID(); + if (reg_id != null) { + if (!node.model.createNode(reg_id, this)) return false; + if (isValid()) return true; + TCFNodeRegister reg_node = (TCFNodeRegister)node.model.getNode(reg_id); + if (!findRegs(reg_node, data)) return false; + } + } + set(null, null, data); + return true; + } + ISymbols.TypeClass type_class = type_data.getTypeClass(); + Map<String,TCFNode> data = new HashMap<String,TCFNode>(); + if (par_level > 0 && type_class != ISymbols.TypeClass.array) { + // Nothing + } + else if (type_class == ISymbols.TypeClass.composite) { + if (!findFields(type_data, data, false)) return false; + } + else if (type_class == ISymbols.TypeClass.array) { + int offs = par_level > 0 ? par_offs : 0; + int size = par_level > 0 ? par_size : type_data.getLength(); + if (size <= 100) { + for (int i = offs; i < offs + size; i++) { + TCFNodeExpression n = findIndex(i, false); + if (n == null) n = new TCFNodeExpression(node, null, null, null, null, i, false); + n.setSortPosition(i); + data.put(n.id, n); + } + } + else { + int next_size = 100; + while (size / next_size > 100) next_size *= 100; + for (int i = offs; i < offs + size; i += next_size) { + int sz = next_size; + if (i + sz > offs + size) sz = offs + size - i; + TCFNodeArrayPartition n = findPartition(i, sz); + if (n == null) n = new TCFNodeArrayPartition(node, par_level + 1, i, sz); + data.put(n.id, n); + } + } + } + else if (type_class == ISymbols.TypeClass.pointer) { + TCFDataCache<IExpressions.Value> val_cache = ((TCFNodeExpression)exp).getValue(); + if (!val_cache.validate(this)) return false; + IExpressions.Value val_data = val_cache.getData(); + if (val_data != null && !isNull(val_data.getValue())) { + TCFDataCache<ISymbols.Symbol> base_type_cache = node.model.getSymbolInfoCache(type_data.getBaseTypeID()); + if (base_type_cache != null) { + if (!base_type_cache.validate(this)) return false; + ISymbols.Symbol base_type_data = base_type_cache.getData(); + if (base_type_data != null && base_type_data.getTypeClass() != ISymbols.TypeClass.function && base_type_data.getSize() > 0) { + if (base_type_data.getTypeClass() == ISymbols.TypeClass.composite) { + if (!findFields(base_type_data, data, true)) return false; + } + else { + TCFNodeExpression n = findIndex(0, true); + if (n == null) n = new TCFNodeExpression(node, null, null, null, null, 0, true); + n.setSortPosition(0); + data.put(n.id, n); + } + } + } + } + } + set(null, null, data); + return true; + } + + private boolean isNull(byte[] data) { + if (data == null) return true; + for (byte b : data) { + if (b != 0) return false; + } + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java new file mode 100644 index 000000000..44604a4b5 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationExpression.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +public class TCFColumnPresentationExpression implements IColumnPresentation { + + public static final String PRESENTATION_ID = "Expressions"; + + /** + * Presentation column IDs. + */ + public static final String + COL_NAME = "Name", + COL_TYPE = "Type", + COL_HEX_VALUE = "HexValue", + COL_DEC_VALUE = "DecValue"; + + private static String[] cols_all = { + COL_NAME, + COL_TYPE, + COL_DEC_VALUE, + COL_HEX_VALUE, + }; + + private static String[] headers = { + "Name", + "Type", + "Decimal", + "Hex", + }; + + private static String[] cols_ini = { + COL_NAME, + COL_DEC_VALUE, + COL_HEX_VALUE, + }; + + public void dispose() { + } + + public String[] getAvailableColumns() { + return cols_all; + } + + public String getHeader(String id) { + for (int i = 0; i < cols_all.length; i++) { + if (id.equals(cols_all[i])) return headers[i]; + } + return null; + } + + public String getId() { + return PRESENTATION_ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + public String[] getInitialColumns() { + return cols_ini; + } + + public void init(IPresentationContext context) { + } + + public boolean isOptional() { + return false; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java new file mode 100644 index 000000000..0a0e8c9c0 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationModules.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * Column presentation for the Modules view. + */ +public class TCFColumnPresentationModules implements IColumnPresentation { + + public static final String PRESENTATION_ID = "Modules"; + + /** + * Presentation column IDs. + */ + public static final String + COL_NAME = "Name", + COL_ADDRESS = "Address", + COL_SIZE = "Size", + COL_FLAGS = "Flags", + COL_OFFSET = "Offset", + COL_SECTION = "Section"; + + private static String[] cols_all = { + COL_NAME, + COL_ADDRESS, + COL_SIZE, + COL_FLAGS, + COL_OFFSET, + COL_SECTION + }; + + private static String[] headers = { + "File Name", + "Address", + "Size", + "Flags", + "Offset", + "Section" + }; + + private static String[] cols_ini = { + COL_NAME, + COL_ADDRESS, + COL_SIZE + }; + + public void dispose() { + } + + public String[] getAvailableColumns() { + return cols_all; + } + + public String getHeader(String id) { + for (int i = 0; i < cols_all.length; i++) { + if (id.equals(cols_all[i])) return headers[i]; + } + return null; + } + + public String getId() { + return PRESENTATION_ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + public String[] getInitialColumns() { + return cols_ini; + } + + public void init(IPresentationContext context) { + } + + public boolean isOptional() { + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java new file mode 100644 index 000000000..10bdc8947 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFColumnPresentationRegister.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +public class TCFColumnPresentationRegister implements IColumnPresentation { + + public static final String PRESENTATION_ID = "Registers"; + + /** + * Presentation column IDs. + */ + public static final String + COL_NAME = "Name", + COL_HEX_VALUE = "HexValue", + COL_DEC_VALUE = "DecValue", + COL_DESCRIPTION = "Description", + COL_READBLE = "Readable", + COL_READ_ONCE = "ReadOnce", + COL_WRITEABLE = "Writeable", + COL_WRITE_ONCE = "WriteOnce", + COL_SIDE_EFFECTS = "SideEffects", + COL_VOLATILE = "Volatile", + COL_FLOAT = "Float", + COL_MNEMONIC = "Menimonic"; + + private static String[] cols_all = { + COL_NAME, + COL_HEX_VALUE, + COL_DEC_VALUE, + COL_DESCRIPTION, + COL_READBLE, + COL_READ_ONCE, + COL_WRITEABLE, + COL_WRITE_ONCE, + COL_SIDE_EFFECTS, + COL_VOLATILE, + COL_FLOAT, + COL_MNEMONIC + }; + + private static String[] headers = { + "Name", + "Hex", + "Decimal", + "Description", + "Readable", + "Read Once", + "Writable", + "Write Once", + "Side Effects", + "Volatile", + "Float", + "Mnemonic" + }; + + private static String[] cols_ini = { + COL_NAME, + COL_HEX_VALUE, + COL_DEC_VALUE, + COL_DESCRIPTION, + COL_MNEMONIC + }; + + public void dispose() { + } + + public String[] getAvailableColumns() { + return cols_all; + } + + public String getHeader(String id) { + for (int i = 0; i < cols_all.length; i++) { + if (id.equals(cols_all[i])) return headers[i]; + } + return null; + } + + public String getId() { + return PRESENTATION_ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + public String[] getInitialColumns() { + return cols_ini; + } + + public void init(IPresentationContext context) { + } + + public boolean isOptional() { + return false; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java new file mode 100644 index 000000000..4977940d2 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFConsole.java @@ -0,0 +1,236 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tcf.internal.debug.cmdline.TCFCommandLine; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.IConsoleConstants; +import org.eclipse.ui.console.IConsoleManager; +import org.eclipse.ui.console.IConsoleView; +import org.eclipse.ui.console.IOConsole; +import org.eclipse.ui.console.IOConsoleInputStream; +import org.eclipse.ui.console.IOConsoleOutputStream; + +class TCFConsole { + private final TCFModel model; + private final IOConsole console; + private final Display display; + private final String process_id; + private final LinkedList<Message> out_queue; + private final TCFCommandLine cmd_line; + + private final byte[] prompt = { 't', 'c', 'f', '>' }; + private final StringBuffer cmd_buf = new StringBuffer(); + + private static class Message { + int stream_id; + byte[] data; + } + + private final Thread inp_thread = new Thread() { + public void run() { + try { + IOConsoleInputStream inp = console.getInputStream(); + final byte[] buf = new byte[0x100]; + for (;;) { + int len = inp.read(buf); + if (len < 0) break; + // TODO: Eclipse Console view has a bad habit of replacing CR with CR/LF + if (len == 2 && buf[0] == '\r' && buf[1] == '\n') len = 1; + final int n = len; + Protocol.invokeAndWait(new Runnable() { + public void run() { + try { + if (cmd_line != null) { + String s = new String(buf, 0, n, "UTF-8"); + int l = s.length(); + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + if (ch == '\r') { + String res = cmd_line.command(cmd_buf.toString()); + cmd_buf.setLength(0); + if (res != null) { + if (res.length() > 0 && res.charAt(res.length() - 1) != '\n') { + res += '\n'; + } + write(0, res.getBytes("UTF-8")); + } + write(0, prompt); + } + else if (ch == '\b') { + int n = cmd_buf.length(); + if (n > 0) n--; + cmd_buf.setLength(n); + } + else { + cmd_buf.append(ch); + } + } + } + else { + model.getLaunch().writeProcessInputStream(process_id, buf, 0, n); + } + } + catch (Exception x) { + model.onProcessStreamError(process_id, 0, x, 0); + } + } + }); + } + } + catch (Throwable x) { + Activator.log("Cannot read console input", x); + } + } + }; + + private final Thread out_thread = new Thread() { + public void run() { + Map<Integer,IOConsoleOutputStream> out_streams = + new HashMap<Integer,IOConsoleOutputStream>(); + try { + for (;;) { + Message m = null; + synchronized (out_queue) { + while (out_queue.size() == 0) out_queue.wait(); + m = out_queue.removeFirst(); + } + if (m.data == null) break; + IOConsoleOutputStream stream = out_streams.get(m.stream_id); + if (stream == null) { + final int id = m.stream_id; + final IOConsoleOutputStream s = stream = console.newOutputStream(); + display.syncExec(new Runnable() { + public void run() { + try { + int color_id = SWT.COLOR_BLACK; + switch (id) { + case 1: color_id = SWT.COLOR_RED; break; + case 2: color_id = SWT.COLOR_BLUE; break; + case 3: color_id = SWT.COLOR_GREEN; break; + } + s.setColor(display.getSystemColor(color_id)); + } + catch (Throwable x) { + Activator.log("Cannot open console view", x); + } + } + }); + out_streams.put(m.stream_id, stream); + } + stream.write(m.data, 0, m.data.length); + } + } + catch (Throwable x) { + Activator.log("Cannot write console output", x); + } + for (IOConsoleOutputStream stream : out_streams.values()) { + try { + stream.close(); + } + catch (IOException x) { + Activator.log("Cannot close console stream", x); + } + } + try { + console.getInputStream().close(); + } + catch (IOException x) { + Activator.log("Cannot close console stream", x); + } + try { + display.syncExec(new Runnable() { + public void run() { + IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager(); + manager.removeConsoles(new IOConsole[]{ console }); + } + }); + } + catch (SWTException x) { + if (x.code == SWT.ERROR_DEVICE_DISPOSED) return; + Activator.log("Cannot remove console", x); + } + } + }; + + /* process_id == null means debug console */ + TCFConsole(final TCFModel model, String process_id) { + this.model = model; + this.process_id = process_id; + display = model.getDisplay(); + out_queue = new LinkedList<Message>(); + String image = process_id != null ? ImageCache.IMG_PROCESS_RUNNING : ImageCache.IMG_TCF; + console = new IOConsole( + "TCF " + (process_id != null ? process_id : "Debugger"), null, + ImageCache.getImageDescriptor(image), "UTF-8", true); + cmd_line = process_id != null ? null : new TCFCommandLine(); + if (cmd_line != null) write(0, prompt); + display.asyncExec(new Runnable() { + public void run() { + if (!PlatformUI.isWorkbenchRunning() || PlatformUI.getWorkbench().isStarting()) { + display.timerExec(200, this); + } + else if (!PlatformUI.getWorkbench().isClosing()) { + try { + IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager(); + manager.addConsoles(new IConsole[]{ console }); + IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (w == null) return; + IWorkbenchPage page = w.getActivePage(); + if (page == null) return; + IConsoleView view = (IConsoleView)page.showView(IConsoleConstants.ID_CONSOLE_VIEW); + view.display(console); + } + catch (Throwable x) { + Activator.log("Cannot open console view", x); + } + } + } + }); + inp_thread.setName("TCF Console Input"); + out_thread.setName("TCF Console Output"); + inp_thread.start(); + out_thread.start(); + } + + void write(final int stream_id, byte[] data) { + if (data == null || data.length == 0) return; + synchronized (out_queue) { + Message m = new Message(); + m.stream_id = stream_id; + m.data = data; + out_queue.add(m); + out_queue.notify(); + } + } + + void close() { + synchronized (out_queue) { + out_queue.add(new Message()); + out_queue.notify(); + } + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java new file mode 100644 index 000000000..5c888b809 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDebugTask.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.util.TCFTask; + +/** + * An extension of TCFTask class that adds support for throwing DebugException. + */ +public abstract class TCFDebugTask<V> extends TCFTask<V> { + + public TCFDebugTask() { + } + + public TCFDebugTask(IChannel channel) { + super(channel); + } + + public synchronized V getD() throws DebugException { + assert !Protocol.isDispatchThread(); + while (!isDone()) { + try { + wait(); + } + catch (InterruptedException x) { + throw new DebugException(new Status( + IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Debugger requiest interrupted", x)); + } + } + assert isDone(); + Throwable x = getError(); + if (x instanceof DebugException) throw (DebugException)x; + if (x != null) throw new DebugException(new Status( + IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Debugger requiest failed", x)); + return getResult(); + } + + public void error(String msg) { + error(new DebugException(new Status( + IStatus.ERROR, Activator.PLUGIN_ID, DebugException.REQUEST_FAILED, + msg, null))); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java new file mode 100644 index 000000000..632147ece --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPane.java @@ -0,0 +1,293 @@ +/******************************************************************************* + * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.debug.ui.IDetailPane; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextPresentationListener; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.source.SourceViewerConfiguration; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchCommandConstants; +import org.eclipse.ui.IWorkbenchPartSite; + +/** + * This detail pane uses a source viewer to display detailed information about the current + * selection. + */ +public class TCFDetailPane implements IDetailPane { + + public static final String ID = "org.eclipse.tcf.debug.DetailPaneFactory"; + public static final String NAME = "TCF Detail Pane"; + public static final String DESC = "TCF Detail Pane"; + + private static final String DETAIL_COPY_ACTION = IDebugView.COPY_ACTION + ".DetailPane"; //$NON-NLS-1$ + private static final String DETAIL_SELECT_ALL_ACTION = IDebugView.SELECT_ALL_ACTION + ".DetailPane"; //$NON-NLS-1$ + + private SourceViewer source_viewer; + private Display display; + private int generation; + private IWorkbenchPartSite part_site; + private final Document document = new Document(); + private final ArrayList<StyleRange> style_ranges = new ArrayList<StyleRange>(); + private final HashMap<RGB,Color> colors = new HashMap<RGB,Color>(); + private final Map<String,IAction> action_map = new HashMap<String,IAction>(); + private final List<String> selection_actions = new ArrayList<String>(); + + private final ITextPresentationListener presentation_listener = new ITextPresentationListener() { + public void applyTextPresentation(TextPresentation presentation) { + for (StyleRange r : style_ranges) presentation.addStyleRange(r); + } + }; + + private class DetailPaneAction extends Action { + final int op_code; + DetailPaneAction(String text, int op_code) { + super(text); + this.op_code = op_code; + } + void update() { + boolean was_enabled = isEnabled(); + boolean is_enabled = (source_viewer != null && source_viewer.canDoOperation(op_code)); + setEnabled(is_enabled); + if (was_enabled == is_enabled) return; + firePropertyChange(ENABLED, was_enabled, is_enabled); + } + @Override + public void run() { + if (!isEnabled()) return; + source_viewer.doOperation(op_code); + } + } + + private void createActions() { + DetailPaneAction action = null; + + action = new DetailPaneAction("Select &All", ITextOperationTarget.SELECT_ALL); + action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL); + action_map.put(DETAIL_SELECT_ALL_ACTION, action); + + action = new DetailPaneAction("&Copy", ITextOperationTarget.COPY); + action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY); + action_map.put(DETAIL_COPY_ACTION, action); + + selection_actions.add(DETAIL_COPY_ACTION); + + updateSelectionDependentActions(); + } + + private IAction getAction(String id) { + return action_map.get(id); + } + + private void setGlobalAction(String id, IAction action){ + if (part_site instanceof IViewSite) { + ((IViewSite)part_site).getActionBars().setGlobalActionHandler(id, action); + } + } + + private void createDetailContextMenu(Control menuControl) { + if (part_site == null) return; + MenuManager manager = new MenuManager(); + manager.setRemoveAllWhenShown(true); + manager.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager mgr) { + fillDetailContextMenu(mgr); + } + }); + Menu menu = manager.createContextMenu(menuControl); + menuControl.setMenu(menu); + part_site.registerContextMenu(ID, manager, source_viewer.getSelectionProvider()); + } + + private void fillDetailContextMenu(IMenuManager menu) { + //menu.add(new Separator(MODULES_GROUP)); + //menu.add(new Separator()); + menu.add(getAction(DETAIL_COPY_ACTION)); + menu.add(getAction(DETAIL_SELECT_ALL_ACTION)); + menu.add(new Separator()); + menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + private void updateAction(String id) { + IAction action = getAction(id); + if (action instanceof DetailPaneAction) ((DetailPaneAction)action).update(); + } + + private void updateSelectionDependentActions() { + for (String id : selection_actions) updateAction(id); + } + + public Control createControl(Composite parent) { + assert source_viewer == null; + source_viewer = new SourceViewer(parent, null, SWT.V_SCROLL | SWT.H_SCROLL); + source_viewer.configure(new SourceViewerConfiguration()); + source_viewer.setDocument(document); + source_viewer.setEditable(false); + source_viewer.addTextPresentationListener(presentation_listener); + Control control = source_viewer.getControl(); + GridData gd = new GridData(GridData.FILL_BOTH); + control.setLayoutData(gd); + display = control.getDisplay(); + createActions(); + // Add the selection listener so selection dependent actions get updated. + document.addDocumentListener(new IDocumentListener() { + public void documentAboutToBeChanged(DocumentEvent event) {} + public void documentChanged(DocumentEvent event) { + updateSelectionDependentActions(); + } + }); + // Add the selection listener so selection dependent actions get updated. + source_viewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + updateSelectionDependentActions(); + } + }); + // Add a focus listener to update actions when details area gains focus + source_viewer.getControl().addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + if (part_site != null) part_site.setSelectionProvider(source_viewer.getSelectionProvider()); + setGlobalAction(IDebugView.SELECT_ALL_ACTION, getAction(DETAIL_SELECT_ALL_ACTION)); + setGlobalAction(IDebugView.COPY_ACTION, getAction(DETAIL_COPY_ACTION)); + if (part_site instanceof IViewSite) ((IViewSite)part_site).getActionBars().updateActionBars(); + } + public void focusLost(FocusEvent e) { + if (part_site != null) part_site.setSelectionProvider(null); + setGlobalAction(IDebugView.SELECT_ALL_ACTION, null); + setGlobalAction(IDebugView.COPY_ACTION, null); + if (part_site instanceof IViewSite) ((IViewSite)part_site).getActionBars().updateActionBars(); + } + }); + // Add a context menu to the detail area + createDetailContextMenu(source_viewer.getTextWidget()); + return control; + } + + public void display(IStructuredSelection selection) { + if (source_viewer == null) return; + generation++; + final int g = generation; + final ArrayList<TCFNode> nodes = new ArrayList<TCFNode>(); + if (selection != null) { + Iterator<?> iterator = selection.iterator(); + while (iterator.hasNext()) { + Object next = iterator.next(); + if (next instanceof TCFNode) nodes.add((TCFNode)next); + } + } + Protocol.invokeLater(new Runnable() { + public void run() { + if (g != generation) return; + final StyledStringBuffer s = getDetailText(nodes, this); + if (s == null) return; + display.asyncExec(new Runnable() { + public void run() { + if (g != generation) return; + document.set(getStyleRanges(s)); + } + }); + } + }); + } + + private StyledStringBuffer getDetailText(ArrayList<TCFNode> nodes, Runnable done) { + StyledStringBuffer bf = new StyledStringBuffer(); + for (TCFNode n : nodes) { + if (n instanceof IDetailsProvider) { + if (!((IDetailsProvider)n).getDetailText(bf, done)) return null; + } + } + return bf; + } + + private String getStyleRanges(StyledStringBuffer s) { + style_ranges.clear(); + for (StyledStringBuffer.Style x : s.getStyle()) { + style_ranges.add(new StyleRange(x.pos, x.len, getColor(x.fg), getColor(x.bg), x.font)); + } + return s.toString(); + } + + private Color getColor(RGB rgb) { + if (rgb == null) return null; + Color c = colors.get(rgb); + if (c == null) colors.put(rgb, c = new Color(display, rgb)); + return c; + } + + public void dispose() { + for (Color c : colors.values()) c.dispose(); + colors.clear(); + if (source_viewer == null) return; + generation++; + if (source_viewer.getControl() != null) { + source_viewer.getControl().dispose(); + } + source_viewer = null; + selection_actions.clear(); + action_map.clear(); + } + + public String getDescription() { + return DESC; + } + + public String getID() { + return ID; + } + + public String getName() { + return NAME; + } + + public void init(IWorkbenchPartSite part_site) { + this.part_site = part_site; + } + + public boolean setFocus() { + if (source_viewer == null) return false; + source_viewer.getTextWidget().setFocus(); + return true; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java new file mode 100644 index 000000000..25d988da9 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFDetailPaneFactory.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.debug.ui.IDetailPane; +import org.eclipse.debug.ui.IDetailPaneFactory; +import org.eclipse.jface.viewers.IStructuredSelection; + +/** + * The TCF detail pane factory is contributed to the <code>org.eclipse.debug.ui.detailPaneFactories</code> + * extension. For any selection that contains TCFNode the factory can produce a <code>IDetailPane</code> object. + */ +public class TCFDetailPaneFactory implements IDetailPaneFactory { + + public IDetailPane createDetailPane(String paneID) { + assert paneID.equals(TCFDetailPane.ID); + return new TCFDetailPane(); + } + + public String getDefaultDetailPane(IStructuredSelection selection) { + return TCFDetailPane.ID; + } + + public String getDetailPaneDescription(String paneID) { + return TCFDetailPane.NAME; + } + + public String getDetailPaneName(String paneID) { + return TCFDetailPane.DESC; + } + + @SuppressWarnings("rawtypes") + public Set getDetailPaneTypes(IStructuredSelection selection) { + HashSet<String> set = new HashSet<String>(); + set.add(TCFDetailPane.ID); + return set; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java new file mode 100644 index 000000000..219d01301 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFMemoryBlockRetrieval.java @@ -0,0 +1,508 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrieval; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.eclipse.debug.core.model.MemoryByte; +import org.eclipse.tcf.internal.debug.model.ITCFConstants; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IExpressions; +import org.eclipse.tcf.services.IMemory; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.services.IMemory.MemoryError; +import org.eclipse.tcf.util.TCFDataCache; + +/** + * A memory block retrieval allows the user interface to request a memory block from a debugger when needed. + * TCF memory block retrieval is based on TCF Memory service. + */ +class TCFMemoryBlockRetrieval implements IMemoryBlockRetrievalExtension { + + private final TCFNodeExecContext exec_ctx; + private final HashSet<MemoryBlock> mem_blocks = new HashSet<MemoryBlock>(); + + private static class MemData { + final BigInteger addr; + final MemoryByte[] data; + final byte[] bytes; + + MemData(BigInteger addr, MemoryByte[] data) { + int i = 0; + this.addr = addr; + this.data = data; + this.bytes = new byte[data.length]; + for (MemoryByte b : data) bytes[i++] = b.getValue(); + } + } + + private class MemoryBlock extends PlatformObject implements IMemoryBlockExtension { + + private final String expression; + private final long length; + private final Set<Object> connections = new HashSet<Object>(); + private final TCFDataCache<IExpressions.Expression> remote_expression; + private final TCFDataCache<IExpressions.Value> expression_value; + private final TCFDataCache<ISymbols.Symbol> expression_type; + + private boolean disposed; + + private MemData mem_data; // current memory block data + private MemData mem_prev; // previous data - before last suspend + private MemData mem_last; // last retrieved memory block data + + MemoryBlock(final String expression, long length) { + this.expression = expression; + this.length = length; + mem_blocks.add(this); + final TCFLaunch launch = exec_ctx.model.getLaunch(); + final IChannel channel = launch.getChannel(); + remote_expression = new TCFDataCache<IExpressions.Expression>(channel) { + @Override + protected boolean startDataRetrieval() { + IExpressions exps = launch.getService(IExpressions.class); + if (exps == null) { + set(null, new Exception("Expressions service not available"), null); + return true; + } + command = exps.create(exec_ctx.id, null, expression, new IExpressions.DoneCreate() { + public void doneCreate(IToken token, Exception error, IExpressions.Expression context) { + if (disposed) { + IExpressions exps = channel.getRemoteService(IExpressions.class); + exps.dispose(context.getID(), new IExpressions.DoneDispose() { + public void doneDispose(IToken token, Exception error) { + if (error == null) return; + if (channel.getState() != IChannel.STATE_OPEN) return; + Activator.log("Error disposing remote expression evaluator", error); + } + }); + return; + } + set(token, error, context); + } + }); + return false; + } + }; + expression_value = new TCFDataCache<IExpressions.Value>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!remote_expression.validate(this)) return false; + final IExpressions.Expression ctx = remote_expression.getData(); + if (ctx == null) { + set(null, null, null); + return true; + } + IExpressions exps = launch.getService(IExpressions.class); + command = exps.evaluate(ctx.getID(), new IExpressions.DoneEvaluate() { + public void doneEvaluate(IToken token, Exception error, IExpressions.Value value) { + set(token, error, value); + } + }); + return false; + } + }; + expression_type = new TCFDataCache<ISymbols.Symbol>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!expression_value.validate(this)) return false; + IExpressions.Value val = expression_value.getData(); + if (val == null) { + set(null, expression_value.getError(), null); + return true; + } + TCFDataCache<ISymbols.Symbol> type_cache = exec_ctx.model.getSymbolInfoCache(val.getTypeID()); + if (type_cache == null) { + set(null, null, null); + return true; + } + if (!type_cache.validate(this)) return false; + set(null, type_cache.getError(), type_cache.getData()); + return true; + } + }; + } + + public synchronized void connect(Object client) { + connections.add(client); + } + + public synchronized void disconnect(Object client) { + connections.remove(client); + } + + public synchronized Object[] getConnections() { + return connections.toArray(new Object[connections.size()]); + } + + public void dispose() throws DebugException { + new TCFDebugTask<Boolean>(exec_ctx.getChannel()) { + public void run() { + disposed = true; + expression_value.dispose(); + expression_type.dispose(); + if (remote_expression.isValid() && remote_expression.getData() != null) { + final IChannel channel = exec_ctx.channel; + if (channel.getState() == IChannel.STATE_OPEN) { + IExpressions exps = channel.getRemoteService(IExpressions.class); + exps.dispose(remote_expression.getData().getID(), new IExpressions.DoneDispose() { + public void doneDispose(IToken token, Exception error) { + if (error == null) return; + if (channel.getState() != IChannel.STATE_OPEN) return; + Activator.log("Error disposing remote expression evaluator", error); + } + }); + } + } + remote_expression.dispose(); + mem_blocks.remove(MemoryBlock.this); + done(Boolean.TRUE); + } + }.getD(); + } + + public int getAddressSize() throws DebugException { + return new TCFDebugTask<Integer>(exec_ctx.getChannel()) { + public void run() { + if (exec_ctx.isDisposed()) { + error("Context is disposed"); + } + else { + TCFDataCache<IMemory.MemoryContext> cache = exec_ctx.getMemoryContext(); + if (!cache.validate(this)) return; + if (cache.getError() != null) { + error(cache.getError()); + } + else { + IMemory.MemoryContext mem = cache.getData(); + if (mem == null) { + error("Context does not provide memory access"); + } + else { + done(mem.getAddressSize()); + } + } + } + } + }.getD(); + } + + public int getAddressableSize() throws DebugException { + // TODO: support for addressable size other then 1 byte + return 1; + } + + public BigInteger getBigBaseAddress() throws DebugException { + return new TCFDebugTask<BigInteger>(exec_ctx.getChannel()) { + public void run() { + if (!expression_value.validate()) { + expression_value.wait(this); + } + else if (expression_value.getError() != null) { + error(expression_value.getError()); + } + else if (expression_value.getData() == null) { + error("Address expression evaluation failed"); + } + else if (!expression_type.validate()) { + expression_type.wait(this); + } + else if (expression_type.getError() != null) { + error(expression_type.getError()); + } + else { + IExpressions.Value value = expression_value.getData(); + byte[] data = value.getValue(); + if (data == null || data.length == 0) { + error("Address expression value is empty (void)"); + } + else { + ISymbols.Symbol type = expression_type.getData(); + boolean signed = type != null && type.getTypeClass() == ISymbols.TypeClass.integer; + done(TCFNumberFormat.toBigInteger(data, 0, data.length, value.isBigEndian(), signed)); + } + } + } + }.getD(); + } + + public MemoryByte[] getBytesFromAddress(final BigInteger address, final long units) throws DebugException { + return new TCFDebugTask<MemoryByte[]>(exec_ctx.getChannel()) { + int offs = 0; + public void run() { + if (mem_data != null && + address.compareTo(mem_data.addr) >= 0 && + address.add(BigInteger.valueOf(units)).compareTo( + mem_data.addr.add(BigInteger.valueOf(mem_data.data.length))) <= 0) { + offs = address.subtract(mem_data.addr).intValue(); + MemoryByte[] res = mem_data.data; + if (units < mem_data.data.length) { + res = new MemoryByte[(int)units]; + System.arraycopy(mem_data, offs, res, 0, res.length); + } + setHistoryFlags(); + done(res); + return; + } + if (exec_ctx.isDisposed()) { + error("Context is disposed"); + return; + } + TCFDataCache<IMemory.MemoryContext> cache = exec_ctx.getMemoryContext(); + if (!cache.validate(this)) return; + if (cache.getError() != null) { + error(cache.getError()); + return; + } + final IMemory.MemoryContext mem = cache.getData(); + if (mem == null) { + error("Context does not provide memory access"); + return; + } + final int size = (int)units; + final int mode = IMemory.MODE_CONTINUEONERROR | IMemory.MODE_VERIFY; + final byte[] buf = new byte[size]; + final MemoryByte[] res = new MemoryByte[size]; + mem.get(address, 1, buf, 0, size, mode, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + int big_endian = 0; + if (mem.getProperties().get(IMemory.PROP_BIG_ENDIAN) != null) { + big_endian |= MemoryByte.ENDIANESS_KNOWN; + if (mem.isBigEndian()) big_endian |= MemoryByte.BIG_ENDIAN; + } + int cnt = 0; + while (offs < size) { + int flags = big_endian; + if (error instanceof IMemory.ErrorOffset) { + IMemory.ErrorOffset ofs = (IMemory.ErrorOffset)error; + int status = ofs.getStatus(cnt); + if (status == IMemory.ErrorOffset.BYTE_VALID) { + flags |= MemoryByte.READABLE | MemoryByte.WRITABLE; + } + else if ((status & IMemory.ErrorOffset.BYTE_UNKNOWN) != 0) { + if (cnt > 0) break; + } + } + else if (error == null) { + flags |= MemoryByte.READABLE | MemoryByte.WRITABLE; + } + res[offs] = new MemoryByte(buf[offs], (byte)flags); + offs++; + cnt++; + } + if (offs < size) { + mem.get(address.add(BigInteger.valueOf(offs)), 1, buf, offs, size - offs, mode, this); + } + else { + mem_last = mem_data = new MemData(address, res); + setHistoryFlags(); + done(res); + } + } + }); + } + }.getD(); + } + + private void setHistoryFlags() { + if (mem_data == null) return; + BigInteger addr = mem_data.addr; + BigInteger his_start = null; + BigInteger his_end = null; + if (mem_prev != null) { + his_start = mem_prev.addr; + his_end = mem_prev.addr.add(BigInteger.valueOf(mem_prev.data.length)); + } + for (MemoryByte b : mem_data.data) { + int flags = b.getFlags(); + if (mem_prev != null && addr.compareTo(his_start) >= 0 && addr.compareTo(his_end) < 0) { + flags |= MemoryByte.HISTORY_KNOWN; + int offs = addr.subtract(his_start).intValue(); + if (b.getValue() != mem_prev.data[offs].getValue()) { + flags |= MemoryByte.CHANGED; + } + } + else { + flags &= ~(MemoryByte.HISTORY_KNOWN | MemoryByte.CHANGED); + } + b.setFlags((byte)flags); + addr = addr.add(BigInteger.valueOf(1)); + } + } + + public MemoryByte[] getBytesFromOffset(BigInteger offset, long units) throws DebugException { + return getBytesFromAddress(getBigBaseAddress().add(offset), units); + } + + public String getExpression() { + return expression; + } + + public IMemoryBlockRetrieval getMemoryBlockRetrieval() { + return TCFMemoryBlockRetrieval.this; + } + + public long getStartAddress() { + return 0; // Unbounded + } + + public long getLength() { + return length; + } + + public BigInteger getMemoryBlockStartAddress() throws DebugException { + return null; // Unbounded + } + + public BigInteger getMemoryBlockEndAddress() throws DebugException { + return null; // Unbounded + } + + public BigInteger getBigLength() throws DebugException { + return BigInteger.valueOf(length); + } + + public void setBaseAddress(BigInteger address) throws DebugException { + } + + public void setValue(BigInteger offset, final byte[] bytes) throws DebugException { + final BigInteger address = getBigBaseAddress().add(offset); + new TCFDebugTask<Object>(exec_ctx.getChannel()) { + public void run() { + if (exec_ctx.isDisposed()) { + error("Context is disposed"); + return; + } + TCFDataCache<IMemory.MemoryContext> cache = exec_ctx.getMemoryContext(); + if (!cache.validate(this)) return; + if (cache.getError() != null) { + error(cache.getError()); + return; + } + final IMemory.MemoryContext mem = cache.getData(); + if (mem == null) { + error("Context does not provide memory access"); + return; + } + final int mode = IMemory.MODE_CONTINUEONERROR | IMemory.MODE_VERIFY; + mem.set(address, 1, bytes, 0, bytes.length, mode, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + error(error); + } + else { + done(null); + } + } + }); + } + }.getD(); + } + + public boolean supportBaseAddressModification() throws DebugException { + return false; + } + + public boolean supportsChangeManagement() { + return true; + } + + public byte[] getBytes() throws DebugException { + if (mem_data == null) return null; + return mem_data.bytes; + } + + public void setValue(long offset, byte[] bytes) throws DebugException { + setValue(BigInteger.valueOf(offset), bytes); + } + + public boolean supportsValueModification() { + return true; + } + + public IDebugTarget getDebugTarget() { + return null; + } + + public ILaunch getLaunch() { + return exec_ctx.model.getLaunch(); + } + + public String getModelIdentifier() { + return ITCFConstants.ID_TCF_DEBUG_MODEL; + } + + @Override + @SuppressWarnings("rawtypes") + public Object getAdapter(Class adapter) { + if (adapter == IMemoryBlockRetrieval.class) return TCFMemoryBlockRetrieval.this; + if (adapter == IMemoryBlockRetrievalExtension.class) return TCFMemoryBlockRetrieval.this; + return super.getAdapter(adapter); + } + } + + TCFMemoryBlockRetrieval(TCFNodeExecContext exec_ctx) { + this.exec_ctx = exec_ctx; + } + + public IMemoryBlockExtension getExtendedMemoryBlock(final String expression, Object context) throws DebugException { + return new TCFDebugTask<IMemoryBlockExtension>() { + public void run() { + done(new MemoryBlock(expression, -1)); + } + }.getD(); + } + + public IMemoryBlock getMemoryBlock(final long address, final long length) throws DebugException { + return new TCFDebugTask<IMemoryBlockExtension>() { + public void run() { + done(new MemoryBlock("0x" + Long.toHexString(address), length)); + } + }.getD(); + } + + public boolean supportsStorageRetrieval() { + return true; + } + + public String getMemoryID() { + return exec_ctx.id; + } + + void onMemoryChanged(boolean suspended) { + assert Protocol.isDispatchThread(); + if (mem_blocks.size() == 0) return; + ArrayList<DebugEvent> list = new ArrayList<DebugEvent>(); + for (MemoryBlock b : mem_blocks) { + if (suspended) b.mem_prev = b.mem_last; + b.mem_data = null; + list.add(new DebugEvent(b, DebugEvent.CHANGE, DebugEvent.CONTENT)); + } + DebugPlugin.getDefault().fireDebugEventSet(list.toArray(new DebugEvent[list.size()])); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java new file mode 100644 index 000000000..83faae53e --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModel.java @@ -0,0 +1,1801 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IExpressionManager; +import org.eclipse.debug.core.IExpressionsListener; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.commands.IDisconnectHandler; +import org.eclipse.debug.core.commands.IDropToFrameHandler; +import org.eclipse.debug.core.commands.IResumeHandler; +import org.eclipse.debug.core.commands.IStepIntoHandler; +import org.eclipse.debug.core.commands.IStepOverHandler; +import org.eclipse.debug.core.commands.IStepReturnHandler; +import org.eclipse.debug.core.commands.ISuspendHandler; +import org.eclipse.debug.core.commands.ITerminateHandler; +import org.eclipse.debug.core.model.IDebugModelProvider; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.core.model.IMemoryBlockRetrieval; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.debug.ui.ISourcePresentation; +import org.eclipse.debug.ui.contexts.ISuspendTrigger; +import org.eclipse.debug.ui.contexts.ISuspendTriggerListener; +import org.eclipse.debug.ui.sourcelookup.CommonSourceNotFoundEditorInput; +import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tcf.internal.debug.actions.TCFAction; +import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupDirector; +import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupParticipant; +import org.eclipse.tcf.internal.debug.model.ITCFConstants; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.internal.debug.model.TCFSourceRef; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.internal.debug.ui.adapters.TCFNodePropertySource; +import org.eclipse.tcf.internal.debug.ui.commands.BackIntoCommand; +import org.eclipse.tcf.internal.debug.ui.commands.BackOverCommand; +import org.eclipse.tcf.internal.debug.ui.commands.BackResumeCommand; +import org.eclipse.tcf.internal.debug.ui.commands.BackReturnCommand; +import org.eclipse.tcf.internal.debug.ui.commands.DisconnectCommand; +import org.eclipse.tcf.internal.debug.ui.commands.DropToFrameCommand; +import org.eclipse.tcf.internal.debug.ui.commands.ResumeCommand; +import org.eclipse.tcf.internal.debug.ui.commands.StepIntoCommand; +import org.eclipse.tcf.internal.debug.ui.commands.StepOverCommand; +import org.eclipse.tcf.internal.debug.ui.commands.StepReturnCommand; +import org.eclipse.tcf.internal.debug.ui.commands.SuspendCommand; +import org.eclipse.tcf.internal.debug.ui.commands.TerminateCommand; +import org.eclipse.tcf.internal.debug.ui.preferences.TCFPreferences; +import org.eclipse.tcf.core.Command; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.protocol.IErrorReport; +import org.eclipse.tcf.protocol.IService; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IDisassembly; +import org.eclipse.tcf.services.ILineNumbers; +import org.eclipse.tcf.services.IMemory; +import org.eclipse.tcf.services.IMemoryMap; +import org.eclipse.tcf.services.IProcesses; +import org.eclipse.tcf.services.IRegisters; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.services.IStackTrace; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.tcf.util.TCFTask; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IPersistableElement; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * TCFModel represents remote target state as it is known to host. + * The main job of the model is caching remote data, + * keeping the cache in a coherent state, + * and feeding UI with up-to-date data. + */ +@SuppressWarnings("restriction") +public class TCFModel implements IElementContentProvider, IElementLabelProvider, IViewerInputProvider, + IModelProxyFactory, IColumnPresentationFactory, ISourceDisplay, ISuspendTrigger { + + /** The id of the expression hover presentation context */ + public static final String ID_EXPRESSION_HOVER = Activator.PLUGIN_ID + ".expression_hover"; + + /** The id of a pinned view description presentation context */ + public static final String ID_PINNED_VIEW = Activator.PLUGIN_ID + ".pinned_view"; + + public static final int + UPDATE_POLICY_AUTOMATIC = 0, + UPDATE_POLICY_MANUAL = 1, + UPDATE_POLICY_BREAKPOINT = 2; + + /** + * A dummy editor input to open the disassembly view as editor. + */ + public static class DisassemblyEditorInput implements IEditorInput { + final static String EDITOR_ID = "org.eclipse.cdt.dsf.ui.disassembly"; + final static DisassemblyEditorInput INSTANCE = new DisassemblyEditorInput(); + + @SuppressWarnings("rawtypes") + public Object getAdapter(Class adapter) { + return null; + } + + public boolean exists() { + return false; + } + + public ImageDescriptor getImageDescriptor() { + return null; + } + + public String getName() { + return "Disassembly"; + } + + public IPersistableElement getPersistable() { + return null; + } + + public String getToolTipText() { + return "Disassembly"; + } + } + + private final TCFLaunch launch; + private final Display display; + private final IExpressionManager expr_manager; + private final TCFAnnotationManager annotation_manager; + + private final List<ISuspendTriggerListener> suspend_trigger_listeners = + new LinkedList<ISuspendTriggerListener>(); + + private int display_source_generation; + private int suspend_trigger_generation; + private int auto_disconnect_generation; + + // Debugger preferences: + private long min_view_updates_interval; + private boolean view_updates_throttle_enabled; + private boolean channel_throttle_enabled; + private boolean wait_for_pc_update_after_step; + private boolean wait_for_views_update_after_step; + private boolean delay_stack_update_until_last_step; + private boolean stack_frames_limit_enabled; + private int stack_frames_limit_value; + private boolean show_function_arg_names; + private boolean show_function_arg_values; + private boolean delay_children_list_updates; + private boolean auto_children_list_updates; + + private final Map<String,String> action_results = new HashMap<String,String>(); + private final HashMap<String,TCFAction> active_actions = new HashMap<String,TCFAction>(); + + private final Map<IPresentationContext,TCFModelProxy> model_proxies = + new HashMap<IPresentationContext,TCFModelProxy>(); + + private final Map<String,TCFNode> id2node = new HashMap<String,TCFNode>(); + + private final Map<Class<?>,Object> adapters = new HashMap<Class<?>,Object>(); + + private class MemoryBlocksUpdate extends TCFDataCache<Map<String,TCFMemoryBlockRetrieval>> { + + final Set<String> changeset = new HashSet<String>(); + final Set<String> suspended = new HashSet<String>(); + + MemoryBlocksUpdate(IChannel channel) { + super(channel); + Protocol.invokeLater(new Runnable() { + public void run() { + if (!validate(this)) return; + Map<String,TCFMemoryBlockRetrieval> map = getData(); + if (map != null) { // map can be null if, for example, the channel was closed + for (TCFMemoryBlockRetrieval r : map.values()) { + r.onMemoryChanged(suspended.contains(r.getMemoryID())); + } + } + launch.removePendingClient(mem_blocks_update); + mem_blocks_update = null; + } + }); + } + + void add(String id, boolean suspended) { + changeset.add(id); + if (suspended) this.suspended.add(id); + } + + public boolean startDataRetrieval() { + // Map changed contexts to memory nodes, and then to memory block retrieval objects + Map<String,TCFMemoryBlockRetrieval> map = new HashMap<String,TCFMemoryBlockRetrieval>(); + for (String id : changeset) { + if (map.get(id) != null) continue; + TCFNode node = id2node.get(id); + if (node == null) { + if (!createNode(id, this)) return false; + if (isValid()) { + Activator.log("Cannot create debug model node", getError()); + reset(); + continue; + } + node = id2node.get(id); + } + if (node instanceof TCFNodeExecContext) { + TCFDataCache<TCFNodeExecContext> c = ((TCFNodeExecContext)node).getMemoryNode(); + if (!c.validate(this)) return false; + node = c.getData(); + if (node == null) continue; + TCFMemoryBlockRetrieval r = mem_retrieval.get(node.id); + if (r != null) { + map.put(node.id, r); + if (suspended.contains(id)) suspended.add(node.id); + } + } + } + set(null, null, map); + return true; + } + } + + private final Map<String,TCFMemoryBlockRetrieval> mem_retrieval = new HashMap<String,TCFMemoryBlockRetrieval>(); + private MemoryBlocksUpdate mem_blocks_update; + + private final Map<String,String> cast_to_type_map = new HashMap<String,String>(); + + private final Map<String,Object> context_map = new HashMap<String,Object>(); + + private final Set<String> expanded_nodes = new HashSet<String>(); + + private final Map<IWorkbenchPart,TCFNode> pins = new HashMap<IWorkbenchPart,TCFNode>(); + private final Map<IWorkbenchPart,TCFSnapshot> locks = new HashMap<IWorkbenchPart,TCFSnapshot>(); + private final Map<IWorkbenchPart,Integer> lock_policy = new HashMap<IWorkbenchPart,Integer>(); + + private final Map<String,TCFConsole> process_consoles = new HashMap<String,TCFConsole>();; + private final List<TCFConsole> debug_consoles = new ArrayList<TCFConsole>(); + + private static final Map<ILaunchConfiguration,IEditorInput> editor_not_found = + new HashMap<ILaunchConfiguration,IEditorInput>(); + + private final IModelSelectionPolicyFactory model_selection_factory = new IModelSelectionPolicyFactory() { + + public IModelSelectionPolicy createModelSelectionPolicyAdapter( + Object element, IPresentationContext context) { + return selection_policy; + } + }; + + private final IModelSelectionPolicy selection_policy; + + private IChannel channel; + private TCFNodeLaunch launch_node; + private boolean disposed; + + private final IMemory.MemoryListener mem_listener = new IMemory.MemoryListener() { + + public void contextAdded(IMemory.MemoryContext[] contexts) { + for (IMemory.MemoryContext ctx : contexts) { + String id = ctx.getParentID(); + if (id == null) { + launch_node.onContextAdded(ctx); + } + else { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextAdded(ctx); + } + } + } + launch_node.onAnyContextAddedOrRemoved(); + } + + public void contextChanged(IMemory.MemoryContext[] contexts) { + for (IMemory.MemoryContext ctx : contexts) { + String id = ctx.getID(); + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextChanged(ctx); + } + onMemoryChanged(id, true, false); + } + } + + public void contextRemoved(final String[] context_ids) { + onContextRemoved(context_ids); + } + + public void memoryChanged(String id, Number[] addr, long[] size) { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onMemoryChanged(addr, size); + } + onMemoryChanged(id, true, false); + } + }; + + private final IRunControl.RunControlListener run_listener = new IRunControl.RunControlListener() { + + public void containerResumed(String[] context_ids) { + for (String id : context_ids) { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContainerResumed(); + } + } + annotation_manager.updateAnnotations(null, launch); + } + + public void containerSuspended(String context, String pc, String reason, + Map<String,Object> params, String[] suspended_ids) { + int action_cnt = 0; + for (String id : suspended_ids) { + TCFNode node = getNode(id); + action_results.remove(id); + if (active_actions.get(id) != null) action_cnt++; + if (!id.equals(context) && node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContainerSuspended(); + } + onMemoryChanged(id, false, true); + } + TCFNode node = getNode(context); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); + } + launch_node.onAnyContextSuspendedOrChanged(); + if (action_cnt == 0) { + setDebugViewSelection(node, reason); + annotation_manager.updateAnnotations(null, launch); + TCFNodePropertySource.refresh(node); + } + action_results.remove(context); + } + + public void contextAdded(IRunControl.RunControlContext[] contexts) { + for (IRunControl.RunControlContext ctx : contexts) { + String id = ctx.getParentID(); + if (id == null) { + launch_node.onContextAdded(ctx); + } + else { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextAdded(ctx); + } + } + context_map.put(ctx.getID(), ctx); + } + launch_node.onAnyContextAddedOrRemoved(); + } + + public void contextChanged(IRunControl.RunControlContext[] contexts) { + for (IRunControl.RunControlContext ctx : contexts) { + String id = ctx.getID(); + context_map.put(id, ctx); + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextChanged(ctx); + } + onMemoryChanged(id, true, false); + if (active_actions.get(id) == null) { + TCFNodePropertySource.refresh(node); + } + } + launch_node.onAnyContextSuspendedOrChanged(); + } + + public void contextException(String context, String msg) { + TCFNode node = getNode(context); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextException(msg); + } + } + + public void contextRemoved(final String[] context_ids) { + onContextRemoved(context_ids); + } + + public void contextResumed(String id) { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextResumed(); + } + annotation_manager.updateAnnotations(null, launch); + } + + public void contextSuspended(String id, String pc, String reason, Map<String,Object> params) { + TCFNode node = getNode(id); + action_results.remove(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextSuspended(pc, reason, params); + } + launch_node.onAnyContextSuspendedOrChanged(); + if (active_actions.get(id) == null) { + setDebugViewSelection(node, reason); + annotation_manager.updateAnnotations(null, launch); + TCFNodePropertySource.refresh(node); + } + onMemoryChanged(id, false, true); + } + }; + + private final IMemoryMap.MemoryMapListener mmap_listenr = new IMemoryMap.MemoryMapListener() { + + public void changed(String id) { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + TCFNodeExecContext exe = (TCFNodeExecContext)node; + exe.onMemoryMapChanged(); + } + onMemoryChanged(id, true, false); + display.asyncExec(new Runnable() { + public void run() { + if (PlatformUI.isWorkbenchRunning()) { + for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { + IWorkbenchPage page = window.getActivePage(); + if (page != null) displaySource(null, page, true); + } + } + } + }); + } + }; + + private final IRegisters.RegistersListener reg_listener = new IRegisters.RegistersListener() { + + public void contextChanged() { + for (TCFNode node : id2node.values()) { + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onRegistersChanged(); + } + } + } + + public void registerChanged(String context) { + TCFNode node = getNode(context); + if (node instanceof TCFNodeRegister) { + ((TCFNodeRegister)node).onValueChanged(); + } + } + }; + + private final IProcesses.ProcessesListener prs_listener = new IProcesses.ProcessesListener() { + + public void exited(String process_id, int exit_code) { + IProcesses.ProcessContext prs = launch.getProcessContext(); + if (prs != null && process_id.equals(prs.getID())) onContextOrProcessRemoved(); + } + }; + + private final IExpressionsListener expressions_listener = new IExpressionsListener() { + + int generation; + + public void expressionsAdded(IExpression[] expressions) { + expressionsRemoved(expressions); + } + + public void expressionsChanged(IExpression[] expressions) { + expressionsRemoved(expressions); + } + + public void expressionsRemoved(IExpression[] expressions) { + final int g = ++generation; + Protocol.invokeLater(new Runnable() { + public void run() { + if (g != generation) return; + for (TCFNode n : id2node.values()) { + if (n instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)n).onExpressionAddedOrRemoved(); + } + } + for (TCFModelProxy p : model_proxies.values()) { + String id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) { + Object o = p.getInput(); + if (o instanceof TCFNode) { + TCFNode n = (TCFNode)o; + if (n.model == TCFModel.this) p.addDelta(n, IModelDelta.CONTENT); + } + } + } + } + }); + } + }; + + private final TCFLaunch.ActionsListener actions_listener = new TCFLaunch.ActionsListener() { + + public void onContextActionStart(TCFAction action) { + final String id = action.getContextID(); + active_actions.put(id, action); + annotation_manager.updateAnnotations(null, launch); + } + + public void onContextActionResult(String id, String reason) { + if (reason == null) action_results.remove(id); + else action_results.put(id, reason); + } + + public void onContextActionDone(TCFAction action) { + String id = action.getContextID(); + active_actions.remove(id); + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextActionDone(); + } + setDebugViewSelection(id2node.get(id), "Action"); + for (TCFModelProxy p : model_proxies.values()) p.post(); + annotation_manager.updateAnnotations(null, launch); + TCFNodePropertySource.refresh(node); + } + }; + + private final IDebugModelProvider debug_model_provider = new IDebugModelProvider() { + public String[] getModelIdentifiers() { + return new String[] { ITCFConstants.ID_TCF_DEBUG_MODEL }; + } + }; + + private class InitialSelection implements Runnable { + boolean done; + public void run() { + if (done) return; + ArrayList<TCFNodeExecContext> nodes = new ArrayList<TCFNodeExecContext>(); + if (!searchSuspendedThreads(launch_node.getFilteredChildren(), nodes)) return; + if (nodes.size() == 0) { + setDebugViewSelection(launch_node, "Launch"); + } + else if (nodes.size() == 1) { + TCFNodeExecContext n = nodes.get(0); + setDebugViewSelection(n, "Launch"); + } + else { + for (TCFNodeExecContext n : nodes) { + String reason = n.getState().getData().suspend_reason; + setDebugViewSelection(n, reason); + } + } + done = true; + } + private boolean searchSuspendedThreads(TCFChildren c, ArrayList<TCFNodeExecContext> nodes) { + if (!c.validate(this)) return false; + for (TCFNode n : c.toArray()) { + if (!searchSuspendedThreads((TCFNodeExecContext)n, nodes)) return false; + } + return true; + } + private boolean searchSuspendedThreads(TCFNodeExecContext n, ArrayList<TCFNodeExecContext> nodes) { + TCFDataCache<IRunControl.RunControlContext> run_context = n.getRunContext(); + if (!run_context.validate(this)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { + TCFDataCache<TCFContextState> state = n.getState(); + if (!state.validate(this)) return false; + TCFContextState s = state.getData(); + if (s != null && s.is_suspended) nodes.add(n); + return true; + } + return searchSuspendedThreads(n.getChildren(), nodes); + } + } + + private volatile boolean instruction_stepping_enabled; + + TCFModel(final TCFLaunch launch) { + this.launch = launch; + display = PlatformUI.getWorkbench().getDisplay(); + selection_policy = new TCFModelSelectionPolicy(this); + adapters.put(ILaunch.class, launch); + adapters.put(IModelSelectionPolicy.class, selection_policy); + adapters.put(IModelSelectionPolicyFactory.class, model_selection_factory); + adapters.put(IDebugModelProvider.class, debug_model_provider); + adapters.put(ISuspendHandler.class, new SuspendCommand(this)); + adapters.put(IResumeHandler.class, new ResumeCommand(this)); + adapters.put(BackResumeCommand.class, new BackResumeCommand(this)); + adapters.put(ITerminateHandler.class, new TerminateCommand(this)); + adapters.put(IDisconnectHandler.class, new DisconnectCommand(this)); + adapters.put(IStepIntoHandler.class, new StepIntoCommand(this)); + adapters.put(IStepOverHandler.class, new StepOverCommand(this)); + adapters.put(IStepReturnHandler.class, new StepReturnCommand(this)); + adapters.put(BackIntoCommand.class, new BackIntoCommand(this)); + adapters.put(BackOverCommand.class, new BackOverCommand(this)); + adapters.put(BackReturnCommand.class, new BackReturnCommand(this)); + adapters.put(IDropToFrameHandler.class, new DropToFrameCommand(this)); + expr_manager = DebugPlugin.getDefault().getExpressionManager(); + expr_manager.addExpressionListener(expressions_listener); + annotation_manager = Activator.getAnnotationManager(); + launch.addActionsListener(actions_listener); + final IPreferenceStore prefs = TCFPreferences.getPreferenceStore(); + IPropertyChangeListener listener = new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + launch.setContextActionsInterval(prefs.getLong(TCFPreferences.PREF_MIN_STEP_INTERVAL)); + min_view_updates_interval = prefs.getLong(TCFPreferences.PREF_MIN_UPDATE_INTERVAL); + view_updates_throttle_enabled = prefs.getBoolean(TCFPreferences.PREF_VIEW_UPDATES_THROTTLE); + channel_throttle_enabled = prefs.getBoolean(TCFPreferences.PREF_TARGET_TRAFFIC_THROTTLE); + wait_for_pc_update_after_step = prefs.getBoolean(TCFPreferences.PREF_WAIT_FOR_PC_UPDATE_AFTER_STEP); + wait_for_views_update_after_step = prefs.getBoolean(TCFPreferences.PREF_WAIT_FOR_VIEWS_UPDATE_AFTER_STEP); + delay_stack_update_until_last_step = prefs.getBoolean(TCFPreferences.PREF_DELAY_STACK_UPDATE_UNTIL_LAST_STEP); + stack_frames_limit_enabled = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_LIMIT_ENABLED); + stack_frames_limit_value = prefs.getInt(TCFPreferences.PREF_STACK_FRAME_LIMIT_VALUE); + show_function_arg_names = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_ARG_NAMES); + show_function_arg_values = prefs.getBoolean(TCFPreferences.PREF_STACK_FRAME_ARG_VALUES); + auto_children_list_updates = prefs.getBoolean(TCFPreferences.PREF_AUTO_CHILDREN_LIST_UPDATES); + delay_children_list_updates = prefs.getBoolean(TCFPreferences.PREF_DELAY_CHILDREN_LIST_UPDATES); + Protocol.invokeLater(new Runnable() { + public void run() { + for (TCFNode n : id2node.values()) { + if (n instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)n).onPreferencesChanged(); + } + } + } + }); + } + }; + listener.propertyChange(null); + prefs.addPropertyChangeListener(listener); + } + + /** + * Add an adapter for given type. + * + * @param adapterType the type the adapter implements + * @param adapter the adapter implementing <code>adapterType</code> + */ + public void setAdapter(Class<?> adapterType, Object adapter) { + synchronized (adapters) { + assert adapterType.isInstance(adapter); + adapters.put(adapterType, adapter); + } + } + + @SuppressWarnings("rawtypes") + public Object getAdapter(final Class adapter, final TCFNode node) { + synchronized (adapters) { + Object o = adapters.get(adapter); + if (o != null) return o; + } + if (adapter == IMemoryBlockRetrieval.class || adapter == IMemoryBlockRetrievalExtension.class) { + return new TCFTask<Object>() { + public void run() { + Object o = null; + TCFDataCache<TCFNodeExecContext> cache = searchMemoryContext(node); + if (cache != null) { + if (!cache.validate(this)) return; + if (cache.getData() != null) { + TCFNodeExecContext ctx = cache.getData(); + o = mem_retrieval.get(ctx.id); + if (o == null) { + TCFMemoryBlockRetrieval m = new TCFMemoryBlockRetrieval(ctx); + mem_retrieval.put(ctx.id, m); + o = m; + } + } + } + assert o == null || adapter.isInstance(o); + done(o); + } + }.getE(); + } + return null; + } + + void onConnected() { + assert Protocol.isDispatchThread(); + assert launch_node == null; + channel = launch.getChannel(); + launch_node = new TCFNodeLaunch(this); + IMemory mem = launch.getService(IMemory.class); + if (mem != null) mem.addListener(mem_listener); + IRunControl run = launch.getService(IRunControl.class); + if (run != null) run.addListener(run_listener); + IMemoryMap mmap = launch.getService(IMemoryMap.class); + if (mmap != null) mmap.addListener(mmap_listenr); + IRegisters reg = launch.getService(IRegisters.class); + if (reg != null) reg.addListener(reg_listener); + IProcesses prs = launch.getService(IProcesses.class); + if (prs != null) prs.addListener(prs_listener); + launchChanged(); + for (TCFModelProxy p : model_proxies.values()) { + String id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) { + Protocol.invokeLater(new InitialSelection()); + } + } + } + + void onDisconnected() { + assert Protocol.isDispatchThread(); + if (locks.size() > 0) { + TCFSnapshot[] arr = locks.values().toArray(new TCFSnapshot[locks.size()]); + locks.clear(); + for (TCFSnapshot s : arr) s.dispose(); + } + if (launch_node != null) { + launch_node.dispose(); + launch_node = null; + } + refreshLaunchView(); + assert id2node.size() == 0; + } + + void onProcessOutput(String process_id, final int stream_id, byte[] data) { + TCFConsole c = process_consoles.get(process_id); + if (c == null) process_consoles.put(process_id, c = new TCFConsole(this, process_id)); + c.write(stream_id, data); + } + + void onProcessStreamError(String process_id, int stream_id, Exception x, int lost_size) { + if (channel != null && channel.getState() == IChannel.STATE_CLOSED) return; + StringBuffer bf = new StringBuffer(); + bf.append("Debugger console IO error"); + if (process_id != null) { + bf.append(". Process ID "); + bf.append(process_id); + } + bf.append(". Stream "); + bf.append(stream_id); + if (lost_size > 0) { + bf.append(". Lost data size "); + bf.append(lost_size); + } + Activator.log(bf.toString(), x); + } + + void onMemoryChanged(String id, boolean notify_references, boolean context_suspended) { + if (channel == null) return; + if (notify_references) { + for (Object obj : context_map.values()) { + if (obj instanceof IRunControl.RunControlContext) { + IRunControl.RunControlContext subctx = (IRunControl.RunControlContext)obj; + if (id.equals(subctx.getProcessID()) && !id.equals(subctx.getID())) { + TCFNode subnode = getNode(subctx.getID()); + if (subnode instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)subnode).onMemoryChanged(null, null); + } + } + } + } + } + if (mem_retrieval.size() == 0) return; + if (mem_blocks_update == null) { + mem_blocks_update = new MemoryBlocksUpdate(channel); + if (wait_for_views_update_after_step) { + launch.addPendingClient(mem_blocks_update); + } + } + mem_blocks_update.add(id, context_suspended); + } + + public TCFAction getActiveAction(String id) { + return active_actions.get(id); + } + + String getContextActionResult(String id) { + return action_results.get(id); + } + + public long getMinViewUpdatesInterval() { + return min_view_updates_interval; + } + + public boolean getViewUpdatesThrottleEnabled() { + return view_updates_throttle_enabled; + } + + public boolean getWaitForViewsUpdateAfterStep() { + return wait_for_views_update_after_step; + } + + public boolean getDelayStackUpdateUtilLastStep() { + return delay_stack_update_until_last_step; + } + + public boolean getChannelThrottleEnabled() { + return channel_throttle_enabled; + } + + public boolean getStackFramesLimitEnabled() { + return stack_frames_limit_enabled; + } + + public int getStackFramesLimitValue() { + return stack_frames_limit_value; + } + + public boolean getShowFunctionArgNames() { + return show_function_arg_names; + } + + public boolean getShowFunctionArgValues() { + return show_function_arg_values; + } + + public boolean getAutoChildrenListUpdates() { + return auto_children_list_updates; + } + + public boolean getDelayChildrenListUpdates() { + return delay_children_list_updates; + } + + void onProxyInstalled(TCFModelProxy mp) { + IPresentationContext pc = mp.getPresentationContext(); + model_proxies.put(mp.getPresentationContext(), mp); + if (launch_node != null && pc.getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) { + Protocol.invokeLater(new InitialSelection()); + } + } + + void onProxyDisposed(TCFModelProxy mp) { + IPresentationContext ctx = mp.getPresentationContext(); + assert model_proxies.get(ctx) == mp; + model_proxies.remove(ctx); + } + + private void onContextRemoved(String[] context_ids) { + for (String id : context_ids) { + TCFNode node = getNode(id); + if (node instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)node).onContextRemoved(); + } + action_results.remove(id); + context_map.remove(id); + expanded_nodes.remove(id); + if (mem_blocks_update != null) mem_blocks_update.changeset.remove(id); + } + launch_node.onAnyContextAddedOrRemoved(); + // Close debug session if the last context is removed: + onContextOrProcessRemoved(); + annotation_manager.updateAnnotations(null, launch); + } + + void onContextRunning() { + annotation_manager.updateAnnotations(null, launch); + } + + void updateContextMap(String id, IRunControl.RunControlContext ctx) { + context_map.put(id, ctx); + } + + private void onContextOrProcessRemoved() { + final int generation = ++auto_disconnect_generation; + Protocol.invokeLater(1000, new Runnable() { + public void run() { + if (generation != auto_disconnect_generation) return; + if (launch_node == null) return; + if (launch_node.isDisposed()) return; + TCFChildren children = launch_node.getFilteredChildren(); + if (!children.validate(this)) return; + if (children.size() > 0) return; + launch.onLastContextRemoved(); + } + }); + } + + void launchChanged() { + if (launch_node != null) { + for (TCFModelProxy p : model_proxies.values()) { + String id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) { + p.addDelta(launch_node, IModelDelta.STATE | IModelDelta.CONTENT); + } + } + } + else { + refreshLaunchView(); + } + } + + Collection<TCFModelProxy> getModelProxies() { + return model_proxies.values(); + } + + void dispose() { + launch.removeActionsListener(actions_listener); + expr_manager.removeExpressionListener(expressions_listener); + for (TCFConsole c : process_consoles.values()) c.close(); + for (TCFConsole c : debug_consoles) c.close(); + } + + void addNode(String id, TCFNode node) { + assert id != null; + assert Protocol.isDispatchThread(); + assert id2node.get(id) == null; + assert !node.isDisposed(); + id2node.put(id, node); + } + + void removeNode(String id) { + assert id != null; + assert Protocol.isDispatchThread(); + id2node.remove(id); + mem_retrieval.remove(id); + } + + void flushAllCaches() { + for (TCFNode n : id2node.values()) n.flushAllCaches(); + } + + public IExpressionManager getExpressionManager() { + return expr_manager; + } + + public Display getDisplay() { + return display; + } + + /** + * @return debug model launch object. + */ + public TCFLaunch getLaunch() { + return launch; + } + + /** + * @return communication channel that this model is using. + */ + public IChannel getChannel() { + return channel; + } + + /** + * Get top level (root) debug model node. + * Same as getNode(""). + * @return root node. + */ + public TCFNodeLaunch getRootNode() { + return launch_node; + } + + /** + * Set current hover expression for a given model node, + * and return a cache of expression nodes that represents given expression. + * The model allows only one current hover expression per node at any time, + * however it will cache results of recent expression evaluations, + * and it will re-use cached results when current hover expression changes. + * The cache getData() method should not return more then 1 node, + * and it can return an empty collection. + * @param parent - a thread or stack frame where the expression should be evaluated. + * @param expression - the expression text, can be null. + * @return a cache of expression nodes. + */ + public TCFChildren getHoverExpressionCache(TCFNode parent, String expression) { + assert Protocol.isDispatchThread(); + if (parent instanceof TCFNodeStackFrame) { + return ((TCFNodeStackFrame)parent).getHoverExpressionCache(expression); + } + if (parent instanceof TCFNodeExecContext) { + return ((TCFNodeExecContext)parent).getHoverExpressionCache(expression); + } + return null; + } + + /** + * Get a model node with given ID. + * ID == "" means launch node. + * @param id - node ID. + * @return debug model node or null if no node exists with such ID. + */ + public TCFNode getNode(String id) { + if (id == null) return null; + if (id.equals("")) return launch_node; + assert Protocol.isDispatchThread(); + return id2node.get(id); + } + + /** + * Get a type that should be used to cast a value of an expression when it is shown in a view. + * Return null if original type of the value should be used. + * @param id - expression node ID. + * @return a string that designates a type or null. + */ + public String getCastToType(String id) { + return cast_to_type_map.get(id); + } + + /** + * Register a type that should be used to cast a value of an expression when it is shown in a view. + * 'type' == null means original type of the value should be used. + * @param id - expression node ID. + * @param type - a string that designates a type. + */ + public void setCastToType(String id, String type) { + if (type != null && type.trim().length() == 0) type = null; + if (type == null) cast_to_type_map.remove(id); + else cast_to_type_map.put(id, type); + TCFNode node = id2node.get(id); + if (node instanceof ICastToType) { + ((ICastToType)node).onCastToTypeChanged(); + } + } + + /** + * Get a data cache that contains properties of a symbol. + * New cache object is created if it does not exist yet. + * @param sym_id - the symbol ID. + * @return data cache object. + */ + public TCFDataCache<ISymbols.Symbol> getSymbolInfoCache(final String sym_id) { + if (sym_id == null) return null; + TCFNodeSymbol n = (TCFNodeSymbol)getNode(sym_id); + if (n == null) n = new TCFNodeSymbol(launch_node, sym_id); + return n.getContext(); + } + + /** + * Get a data cache that contains children of a symbol. + * New cache object is created if it does not exist yet. + * @param sym_id - the symbol ID. + * @return data cache object. + */ + public TCFDataCache<String[]> getSymbolChildrenCache(final String sym_id) { + if (sym_id == null) return null; + TCFNodeSymbol n = (TCFNodeSymbol)getNode(sym_id); + if (n == null) n = new TCFNodeSymbol(launch_node, sym_id); + return n.getChildren(); + } + + /** + * Search memory context that owns the object represented by given node. + * @return data cache item that holds the memory context node. + */ + public TCFDataCache<TCFNodeExecContext> searchMemoryContext(final TCFNode node) { + TCFNode n = node; + while (n != null && !n.isDisposed()) { + if (n instanceof TCFNodeExecContext) return ((TCFNodeExecContext)n).getMemoryNode(); + n = n.parent; + } + return null; + } + + /** + * Asynchronously create model node for given ID. + * If 'done' is TCFDataCache and it is valid after the method returns, + * the node cannot be created because of an error, + * and the cache will contain the error report. + * @param id - context ID. + * @param done - an object waiting for cache validation. + * @return - true if all done, false if 'done' is waiting for remote data. + */ + public boolean createNode(String id, final Runnable done) { + TCFNode parent = getNode(id); + if (parent != null) return true; + LinkedList<Object> path = null; + for (;;) { + Object obj = context_map.get(id); + if (obj == null) obj = new CreateNodeRunnable(id); + if (obj instanceof CreateNodeRunnable) { + ((CreateNodeRunnable)obj).wait(done); + return false; + } + if (obj instanceof Throwable) { + if (done instanceof TCFDataCache<?>) { + ((TCFDataCache<?>)done).set(null, (Throwable)obj, null); + } + return true; + } + if (path == null) path = new LinkedList<Object>(); + path.add(obj); + String parent_id = null; + if (obj instanceof IRunControl.RunControlContext) { + parent_id = ((IRunControl.RunControlContext)obj).getParentID(); + } + else if (obj instanceof IStackTrace.StackTraceContext) { + parent_id = ((IStackTrace.StackTraceContext)obj).getParentID(); + } + else { + parent_id = ((IRegisters.RegistersContext)obj).getParentID(); + } + parent = parent_id == null ? launch_node : getNode(parent_id); + if (parent != null) break; + id = parent_id; + } + while (path.size() > 0) { + Object obj = path.removeLast(); + if (obj instanceof IRunControl.RunControlContext) { + IRunControl.RunControlContext ctx = (IRunControl.RunControlContext)obj; + TCFNodeExecContext n = new TCFNodeExecContext(parent, ctx.getID()); + if (parent instanceof TCFNodeLaunch) ((TCFNodeLaunch)parent).getChildren().add(n); + else ((TCFNodeExecContext)parent).getChildren().add(n); + n.setRunContext(ctx); + parent = n; + } + else if (obj instanceof IStackTrace.StackTraceContext) { + IStackTrace.StackTraceContext ctx = (IStackTrace.StackTraceContext)obj; + TCFNodeStackFrame n = new TCFNodeStackFrame((TCFNodeExecContext)parent, ctx.getID(), false); + ((TCFNodeExecContext)parent).getStackTrace().add(n); + parent = n; + } + else if (obj instanceof IRegisters.RegistersContext) { + IRegisters.RegistersContext ctx = (IRegisters.RegistersContext)obj; + TCFNodeRegister n = new TCFNodeRegister(parent, ctx.getID()); + if (parent instanceof TCFNodeRegister) ((TCFNodeRegister)parent).getChildren().add(n); + else if (parent instanceof TCFNodeStackFrame) ((TCFNodeStackFrame)parent).getRegisters().add(n); + else ((TCFNodeExecContext)parent).getRegisters().add(n); + parent = n; + } + else { + assert false; + } + } + return true; + } + + private class CreateNodeRunnable implements Runnable { + + final String id; + final ArrayList<Runnable> waiting_list = new ArrayList<Runnable>(); + final ArrayList<IService> service_list = new ArrayList<IService>(); + + CreateNodeRunnable(String id) { + this.id = id; + assert context_map.get(id) == null; + String[] arr = { IRunControl.NAME, IStackTrace.NAME, IRegisters.NAME }; + for (String nm : arr) { + IService s = channel.getRemoteService(nm); + if (s != null) service_list.add(s); + } + context_map.put(id, this); + Protocol.invokeLater(this); + } + + void wait(Runnable r) { + assert context_map.get(id) == this; + waiting_list.add(r); + } + + public void run() { + assert context_map.get(id) == this; + if (service_list.size() == 0) { + context_map.put(id, new Exception("Invalid context ID")); + for (Runnable r : waiting_list) r.run(); + } + else { + IService s = service_list.remove(0); + if (s instanceof IRunControl) { + ((IRunControl)s).getContext(id, new IRunControl.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) { + if (error == null && context != null) { + context_map.put(id, context); + for (Runnable r : waiting_list) r.run(); + } + else { + run(); + } + } + }); + } + else if (s instanceof IStackTrace) { + ((IStackTrace)s).getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { + if (error == null && context != null && context.length == 1 && context[0] != null) { + context_map.put(id, context[0]); + for (Runnable r : waiting_list) r.run(); + } + else { + run(); + } + } + }); + } + else { + ((IRegisters)s).getContext(id, new IRegisters.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { + if (error == null && context != null) { + context_map.put(id, context); + for (Runnable r : waiting_list) r.run(); + } + else { + run(); + } + } + }); + } + } + } + } + + public void update(IChildrenCountUpdate[] updates) { + for (IChildrenCountUpdate update : updates) { + Object o = update.getElement(); + if (o instanceof TCFLaunch) { + if (launch_node != null) { + launch_node.update(update); + } + else { + update.setChildCount(0); + update.done(); + } + } + else { + ((TCFNode)o).update(update); + } + } + } + + public void update(IChildrenUpdate[] updates) { + for (IChildrenUpdate update : updates) { + Object o = update.getElement(); + if (o instanceof TCFLaunch) { + if (launch_node != null) { + launch_node.update(update); + } + else { + update.done(); + } + } + else { + ((TCFNode)o).update(update); + } + } + } + + public void update(IHasChildrenUpdate[] updates) { + for (IHasChildrenUpdate update : updates) { + Object o = update.getElement(); + if (o instanceof TCFLaunch) { + if (launch_node != null) { + launch_node.update(update); + } + else { + update.setHasChilren(false); + update.done(); + } + } + else { + ((TCFNode)o).update(update); + } + } + } + + public void update(ILabelUpdate[] updates) { + for (ILabelUpdate update : updates) { + Object o = update.getElement(); + // Launch label is provided by TCFLaunchLabelProvider class. + assert !(o instanceof TCFLaunch); + ((TCFNode)o).update(update); + } + } + + public void update(final IViewerInputUpdate update) { + Protocol.invokeLater(new Runnable() { + public void run() { + TCFNode node = pins.get(update.getPresentationContext().getPart()); + if (node != null) { + node.update(update); + } + else { + if (IDebugUIConstants.ID_BREAKPOINT_VIEW.equals(update.getPresentationContext().getId())) { + // Current implementation does not support flexible hierarchy for breakpoints + IViewerInputProvider p = (IViewerInputProvider)launch.getAdapter(IViewerInputProvider.class); + if (p != null) { + p.update(update); + return; + } + } + Object o = update.getElement(); + if (o instanceof TCFLaunch) { + update.setInputElement(o); + update.done(); + } + else { + ((TCFNode)o).update(update); + } + } + } + }); + } + + public IModelProxy createModelProxy(Object element, IPresentationContext context) { + return new TCFModelProxy(this); + } + + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + String id = getColumnPresentationId(context, element); + if (id == null) return null; + if (id.equals(TCFColumnPresentationRegister.PRESENTATION_ID)) return new TCFColumnPresentationRegister(); + if (id.equals(TCFColumnPresentationExpression.PRESENTATION_ID)) return new TCFColumnPresentationExpression(); + if (id.equals(TCFColumnPresentationModules.PRESENTATION_ID)) return new TCFColumnPresentationModules(); + return null; + } + + public String getColumnPresentationId(IPresentationContext context, Object element) { + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId())) { + return TCFColumnPresentationRegister.PRESENTATION_ID; + } + if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId())) { + return TCFColumnPresentationExpression.PRESENTATION_ID; + } + if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId())) { + return TCFColumnPresentationExpression.PRESENTATION_ID; + } + if (ID_EXPRESSION_HOVER.equals(context.getId())) { + return TCFColumnPresentationExpression.PRESENTATION_ID; + } + if (IDebugUIConstants.ID_MODULE_VIEW.equals(context.getId())) { + return TCFColumnPresentationModules.PRESENTATION_ID; + } + return null; + } + + public void setPin(IWorkbenchPart part, TCFNode node) { + assert Protocol.isDispatchThread(); + if (node == null) pins.remove(part); + else pins.put(part, node); + } + + private IPresentationContext getPresentationContext(IWorkbenchPart part) { + if (part instanceof IDebugView) { + Viewer viewer = ((IDebugView)part).getViewer(); + if (viewer instanceof ITreeModelViewer) { + ITreeModelViewer t = ((ITreeModelViewer)viewer); + return t.getPresentationContext(); + } + } + return null; + } + + public void setLock(IWorkbenchPart part) { + if (launch_node == null) return; + IPresentationContext ctx = getPresentationContext(part); + if (ctx == null) return; + locks.put(part, new TCFSnapshot(ctx)); + TCFModelProxy proxy = model_proxies.get(ctx); + if (proxy == null) return; + proxy.addDelta((TCFNode)proxy.getInput(), IModelDelta.CONTENT); + } + + public boolean isLocked(IWorkbenchPart part) { + return locks.get(part) != null; + } + + public boolean clearLock(IWorkbenchPart part) { + TCFSnapshot snapshot = locks.remove(part); + if (snapshot == null) return false; + snapshot.dispose(); + IPresentationContext ctx = getPresentationContext(part); + if (ctx != null) { + TCFModelProxy proxy = model_proxies.get(ctx); + if (proxy != null) proxy.addDelta((TCFNode)proxy.getInput(), IModelDelta.CONTENT); + } + return true; + } + + public void setLockPolicy(IWorkbenchPart part, int policy) { + if (policy == UPDATE_POLICY_AUTOMATIC) { + clearLock(part); + lock_policy.remove(part); + } + else { + if (!isLocked(part)) setLock(part); + lock_policy.put(part, policy); + } + } + + public int getLockPolicy(IWorkbenchPart part) { + if (locks.get(part) == null) return UPDATE_POLICY_AUTOMATIC; + Integer i = lock_policy.get(part); + if (i == null || i.intValue() == 0) return UPDATE_POLICY_MANUAL; + return i.intValue(); + } + + TCFSnapshot getSnapshot(IPresentationContext ctx) { + return locks.get(ctx.getPart()); + } + + public void setDebugViewSelection(TCFNode node, String reason) { + assert Protocol.isDispatchThread(); + if (node == null) return; + if (node.isDisposed()) return; + runSuspendTrigger(node); + if (reason == null) return; + if (reason.equals(IRunControl.REASON_USER_REQUEST)) return; + for (TCFModelProxy proxy : model_proxies.values()) { + if (proxy.getPresentationContext().getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) { + proxy.setSelection(node); + if (reason.equals(IRunControl.REASON_STEP)) continue; + if (reason.equals(IRunControl.REASON_CONTAINER)) continue; + if (delay_stack_update_until_last_step && launch.getContextActionsCount(node.id) != 0) continue; + if (expanded_nodes.add(node.id)) proxy.expand(node); + } + if (reason.equals(IRunControl.REASON_BREAKPOINT)) { + IWorkbenchPart part = proxy.getPresentationContext().getPart(); + int policy = getLockPolicy(part); + if (policy == UPDATE_POLICY_BREAKPOINT) { + clearLock(part); + setLock(part); + } + } + } + } + + /** + * Reveal source code associated with given model element. + * The method is part of ISourceDisplay interface. + * The method is normally called from SourceLookupService. + */ + public void displaySource(Object model_element, final IWorkbenchPage page, boolean forceSourceLookup) { + if (wait_for_pc_update_after_step) launch.addPendingClient(TCFModel.this); + final int cnt = ++display_source_generation; + /* Because of racing in Eclipse Debug infrastructure, 'model_element' value can be invalid. + * As a workaround, get current debug view selection. + */ + if (page != null) { + ISelection context = DebugUITools.getDebugContextManager().getContextService(page.getWorkbenchWindow()).getActiveContext(); + if (context instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection)context; + model_element = selection.isEmpty() ? null : selection.getFirstElement(); + } + } + final Object element = model_element; + Protocol.invokeLater(25, new Runnable() { + public void run() { + if (cnt != display_source_generation) return; + TCFNodeStackFrame stack_frame = null; + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (element instanceof TCFNodeExecContext) { + TCFNodeExecContext exec_ctx = (TCFNodeExecContext)element; + if (!exec_ctx.isDisposed() && active_actions.get(exec_ctx.id) == null) { + TCFDataCache<TCFContextState> state_cache = exec_ctx.getState(); + if (!state_cache.validate(this)) return; + if (!exec_ctx.isNotActive()) { + TCFContextState state_data = state_cache.getData(); + if (state_data != null && state_data.is_suspended) { + TCFChildrenStackTrace stack_trace = exec_ctx.getStackTrace(); + if (!stack_trace.validate(this)) return; + stack_frame = stack_trace.getTopFrame(); + } + } + } + } + else if (element instanceof TCFNodeStackFrame) { + TCFNodeStackFrame f = (TCFNodeStackFrame)element; + TCFNodeExecContext exec_ctx = (TCFNodeExecContext)f.parent; + if (!f.isDisposed() && !exec_ctx.isDisposed() && active_actions.get(exec_ctx.id) == null) { + TCFDataCache<TCFContextState> state_cache = exec_ctx.getState(); + if (!state_cache.validate(this)) return; + if (!exec_ctx.isNotActive()) { + TCFContextState state_data = state_cache.getData(); + if (state_data != null && state_data.is_suspended) stack_frame = f; + } + } + } + } + String ctx_id = null; + boolean top_frame = false; + ILineNumbers.CodeArea area = null; + if (stack_frame != null) { + TCFDataCache<TCFSourceRef> line_info = stack_frame.getLineInfo(); + if (!line_info.validate(this)) return; + Throwable error = line_info.getError(); + TCFSourceRef src_ref = line_info.getData(); + if (error == null && src_ref != null) error = src_ref.error; + if (error != null) Activator.log("Error retrieving source mapping for a stack frame", error); + if (src_ref != null) area = src_ref.area; + top_frame = stack_frame.getFrameNo() == 0; + ctx_id = stack_frame.parent.id; + } + displaySource(cnt, page, element, ctx_id, top_frame, area); + } + }); + } + + private void displaySource(final int cnt, final IWorkbenchPage page, + final Object element, final String exe_id, final boolean top_frame, final ILineNumbers.CodeArea area) { + final boolean disassembly_available = channel.getRemoteService(IDisassembly.class) != null; + display.asyncExec(new Runnable() { + public void run() { + try { + if (cnt != display_source_generation) return; + String editor_id = null; + IEditorInput editor_input = null; + int line = 0; + if (area != null) { + Object source_element = TCFSourceLookupDirector.lookup(launch, area); + if (source_element != null) { + ISourcePresentation presentation = TCFModelPresentation.getDefault(); + editor_input = presentation.getEditorInput(source_element); + if (editor_input != null) editor_id = presentation.getEditorId(editor_input, source_element); + line = area.start_line; + } + } + if (area != null && !instruction_stepping_enabled && (editor_input == null || editor_id == null)) { + ILaunchConfiguration cfg = launch.getLaunchConfiguration(); + ISourceNotFoundPresentation presentation = (ISourceNotFoundPresentation)DebugPlugin.getAdapter(element, ISourceNotFoundPresentation.class); + if (presentation != null) { + String filename = TCFSourceLookupParticipant.toFileName(area); + editor_input = presentation.getEditorInput(element, cfg, filename); + editor_id = presentation.getEditorId(editor_input, element); + } + if (editor_id == null || editor_input == null) { + editor_id = IDebugUIConstants.ID_COMMON_SOURCE_NOT_FOUND_EDITOR; + editor_input = editor_not_found.get(cfg); + if (editor_input == null) { + editor_input = new CommonSourceNotFoundEditorInput(cfg); + editor_not_found.put(cfg, editor_input); + } + } + } + if (exe_id != null && disassembly_available && + (editor_input == null || editor_id == null || instruction_stepping_enabled) && + PlatformUI.getWorkbench().getEditorRegistry().findEditor( + DisassemblyEditorInput.EDITOR_ID) != null) { + editor_id = DisassemblyEditorInput.EDITOR_ID; + editor_input = DisassemblyEditorInput.INSTANCE; + } + if (cnt != display_source_generation) return; + ITextEditor text_editor = null; + if (page != null && editor_input != null && editor_id != null) { + IEditorPart editor = openEditor(editor_input, editor_id, page); + if (editor instanceof ITextEditor) { + text_editor = (ITextEditor)editor; + } + else { + text_editor = (ITextEditor)editor.getAdapter(ITextEditor.class); + } + } + IRegion region = null; + if (text_editor != null) { + region = getLineInformation(text_editor, line); + if (region != null) text_editor.selectAndReveal(region.getOffset(), 0); + } + if (wait_for_pc_update_after_step) launch.addPendingClient(annotation_manager); + annotation_manager.updateAnnotations(page.getWorkbenchWindow(), launch); + } + finally { + if (cnt == display_source_generation) launch.removePendingClient(TCFModel.this); + } + } + }); + } + + /* + * Refresh Launch View. + * Normally the view is updated by sending deltas through model proxy. + * This method is used only when launch is not yet connected or already disconnected. + */ + private void refreshLaunchView() { + // TODO: there should be a better way to refresh Launch View + synchronized (Device.class) { + if (display.isDisposed()) return; + display.asyncExec(new Runnable() { + public void run() { + IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows(); + if (windows == null) return; + for (IWorkbenchWindow window : windows) { + IDebugView view = (IDebugView)window.getActivePage().findView(IDebugUIConstants.ID_DEBUG_VIEW); + if (view != null) ((StructuredViewer)view.getViewer()).refresh(launch); + } + } + }); + } + } + + /** + * Open debugger console that provide command line UI for the debugger. + */ + public void showDebugConsole() { + debug_consoles.add(new TCFConsole(this, null)); + } + + /** + * Show error message box in active workbench window. + * @param title - message box title. + * @param error - error to be shown. + */ + public void showMessageBox(final String title, final Throwable error) { + display.asyncExec(new Runnable() { + public void run() { + Shell shell = display.getActiveShell(); + if (shell == null) { + Shell[] shells = display.getShells(); + HashSet<Shell> set = new HashSet<Shell>(); + for (Shell s : shells) set.add(s); + for (Shell s : shells) { + if (s.getParent() != null) set.remove(s.getParent().getShell()); + } + for (Shell s : shells) shell = s; + } + MessageBox mb = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK); + mb.setText(title); + mb.setMessage(getErrorMessage(error, true)); + mb.open(); + } + }); + } + + /** + * Create human readable error message from a Throwable object. + * @param error - a Throwable object. + * @param multiline - true if multi-line text is allowed. + * @return + */ + public static String getErrorMessage(Throwable error, boolean multiline) { + StringBuffer buf = new StringBuffer(); + while (error != null) { + String msg = null; + if (!multiline && error instanceof IErrorReport) { + msg = Command.toErrorString(((IErrorReport)error).getAttributes()); + } + else { + msg = error.getLocalizedMessage(); + } + if (msg == null || msg.length() == 0) msg = error.getClass().getName(); + buf.append(msg); + error = error.getCause(); + if (error != null) { + char ch = buf.charAt(buf.length() - 1); + if (multiline && ch != '\n') { + buf.append('\n'); + } + else if (ch != '.' && ch != ';') { + buf.append(';'); + } + buf.append("Caused by:"); + buf.append(multiline ? '\n' : ' '); + } + } + if (buf.length() > 0) { + char ch = buf.charAt(buf.length() - 1); + if (multiline && ch != '\n') { + buf.append('\n'); + } + } + return buf.toString(); + } + + /* + * Open an editor for given editor input. + * @param input - IEditorInput representing a source file to be shown in the editor + * @param id - editor type ID + * @param page - workbench page that will contain the editor + * @return - IEditorPart if the editor was opened successfully, or null otherwise. + */ + private IEditorPart openEditor(final IEditorInput input, final String id, final IWorkbenchPage page) { + final IEditorPart[] editor = new IEditorPart[]{ null }; + Runnable r = new Runnable() { + public void run() { + if (!page.getWorkbenchWindow().getWorkbench().isClosing()) { + try { + editor[0] = page.openEditor(input, id, false, IWorkbenchPage.MATCH_ID|IWorkbenchPage.MATCH_INPUT); + } + catch (PartInitException e) { + Activator.log("Cannot open editor", e); + } + } + } + }; + BusyIndicator.showWhile(display, r); + return editor[0]; + } + + /* + * Returns the line information for the given line in the given editor + */ + private IRegion getLineInformation(ITextEditor editor, int line) { + IDocumentProvider provider = editor.getDocumentProvider(); + IEditorInput input = editor.getEditorInput(); + try { + provider.connect(input); + } + catch (CoreException e) { + return null; + } + try { + IDocument document = provider.getDocument(input); + if (document != null) return document.getLineInformation(line - 1); + } + catch (BadLocationException e) { + } + finally { + provider.disconnect(input); + } + return null; + } + + /** + * Registers the given listener for suspend notifications. + * @param listener suspend listener + */ + public synchronized void addSuspendTriggerListener(ISuspendTriggerListener listener) { + suspend_trigger_listeners.add(listener); + } + + /** + * Unregisters the given listener for suspend notifications. + * @param listener suspend listener + */ + public synchronized void removeSuspendTriggerListener(ISuspendTriggerListener listener) { + suspend_trigger_listeners.remove(listener); + } + + /* + * Lazily run registered suspend listeners. + * @param node - suspended context. + */ + private synchronized void runSuspendTrigger(final TCFNode node) { + if (suspend_trigger_listeners.size() == 0) return; + final ISuspendTriggerListener[] listeners = suspend_trigger_listeners.toArray( + new ISuspendTriggerListener[suspend_trigger_listeners.size()]); + + final int generation = ++suspend_trigger_generation; + if (wait_for_pc_update_after_step || wait_for_views_update_after_step) { + launch.addPendingClient(suspend_trigger_listeners); + } + display.asyncExec(new Runnable() { + public void run() { + synchronized (TCFModel.this) { + if (generation != suspend_trigger_generation) return; + } + for (final ISuspendTriggerListener listener : listeners) { + try { + listener.suspended(launch, node); + } + catch (Throwable x) { + Activator.log(x); + } + } + synchronized (TCFModel.this) { + if (generation != suspend_trigger_generation) return; + launch.removePendingClient(suspend_trigger_listeners); + } + } + }); + } + + /** + * Set whether instruction stepping mode should be enabled or not. + * @param enabled + */ + public void setInstructionSteppingEnabled(boolean enabled) { + instruction_stepping_enabled = enabled; + } + + /** + * @return whether instruction stepping is enabled + */ + public boolean isInstructionSteppingEnabled() { + return instruction_stepping_enabled; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java new file mode 100644 index 000000000..47c05a3da --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelManager.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.protocol.Protocol; + + +public class TCFModelManager { + + public interface ModelManagerListener { + public void onConnected(TCFLaunch launch, TCFModel model); + public void onDisconnected(TCFLaunch launch, TCFModel model); + } + + private final Map<TCFLaunch,TCFModel> models = new HashMap<TCFLaunch,TCFModel>(); + private final List<ModelManagerListener> listeners = new ArrayList<ModelManagerListener>(); + + private final TCFLaunch.LaunchListener tcf_launch_listener = new TCFLaunch.LaunchListener() { + + public void onCreated(TCFLaunch launch) { + assert Protocol.isDispatchThread(); + assert models.get(launch) == null; + TCFModel model = new TCFModel(launch); + models.put(launch, model); + } + + public void onConnected(TCFLaunch launch) { + assert Protocol.isDispatchThread(); + TCFModel model = models.get(launch); + if (model != null) model.onConnected(); + for (ModelManagerListener l : listeners) { + try { + l.onConnected(launch, model); + } + catch (Throwable x) { + Activator.log(x); + } + } + } + + public void onDisconnected(TCFLaunch launch) { + assert Protocol.isDispatchThread(); + TCFModel model = models.get(launch); + if (model != null) model.onDisconnected(); + for (ModelManagerListener l : listeners) { + try { + l.onDisconnected(launch, model); + } + catch (Throwable x) { + Activator.log(x); + } + } + } + + public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data) { + assert Protocol.isDispatchThread(); + TCFModel model = models.get(launch); + if (model != null) model.onProcessOutput(process_id, stream_id, data); + } + + public void onProcessStreamError(TCFLaunch launch, String process_id, + int stream_id, Exception error, int lost_size) { + assert Protocol.isDispatchThread(); + TCFModel model = models.get(launch); + if (model != null) model.onProcessStreamError(process_id, stream_id, error, lost_size); + } + }; + + private final ILaunchesListener debug_launch_listener = new ILaunchesListener() { + + public void launchesAdded(final ILaunch[] launches) { + } + + public void launchesChanged(final ILaunch[] launches) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (ILaunch launch : launches) { + TCFModel model = models.get(launch); + if (model != null) model.launchChanged(); + } + } + }); + } + + public void launchesRemoved(final ILaunch[] launches) { + Protocol.invokeAndWait(new Runnable() { + public void run() { + for (ILaunch launch : launches) { + TCFModel model = models.remove(launch); + if (model != null) model.dispose(); + } + } + }); + } + }; + + public TCFModelManager() { + assert Protocol.isDispatchThread(); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(debug_launch_listener); + TCFLaunch.addListener(tcf_launch_listener); + } + + public void dispose() { + assert Protocol.isDispatchThread(); + DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(debug_launch_listener); + TCFLaunch.removeListener(tcf_launch_listener); + for (Iterator<TCFModel> i = models.values().iterator(); i.hasNext();) { + TCFModel model = i.next(); + model.dispose(); + i.remove(); + } + assert models.isEmpty(); + } + + public void addListener(ModelManagerListener l) { + listeners.add(l); + } + + public void removeListener(ModelManagerListener l) { + listeners.remove(l); + } + + public TCFModel getModel(TCFLaunch launch) { + assert Protocol.isDispatchThread(); + return models.get(launch); + } + + public TCFNode getRootNode(TCFLaunch launch) { + TCFModel model = getModel(launch); + if (model == null) return null; + return model.getRootNode(); + } + + public static TCFModelManager getModelManager() { + return Activator.getModelManager(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java new file mode 100644 index 000000000..2e644e46a --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelPresentation.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.URIUtil; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ILineBreakpoint; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.ui.IDebugModelPresentation; +import org.eclipse.debug.ui.IValueDetailListener; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.tcf.internal.debug.model.TCFBreakpointsModel; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.internal.debug.model.TCFSourceRef; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.JSON; +import org.eclipse.tcf.services.IBreakpoints; +import org.eclipse.tcf.services.ILineNumbers; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.tcf.util.TCFTask; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorRegistry; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.FileStoreEditorInput; +import org.eclipse.ui.part.FileEditorInput; + + +public class TCFModelPresentation implements IDebugModelPresentation { + + public final static String DISPLAY_FULL_PATHS = "DISPLAY_FULL_PATHS"; //$NON-NLS-1$ + + private static final String[] attr_names = { + TCFBreakpointsModel.ATTR_LINE, "line", + TCFBreakpointsModel.ATTR_ADDRESS, "address", + TCFBreakpointsModel.ATTR_FUNCTION, "location", + TCFBreakpointsModel.ATTR_EXPRESSION, "expression", + TCFBreakpointsModel.ATTR_CONDITION, "condition", + TCFBreakpointsModel.ATTR_CONTEXTNAMES, "scope (names)", + TCFBreakpointsModel.ATTR_CONTEXTIDS, "scope (IDs)", + TCFBreakpointsModel.ATTR_EXE_PATHS, "scope (modules)", + TCFBreakpointsModel.ATTR_STOP_GROUP, "stop group", + }; + + private final Collection<ILabelProviderListener> listeners = new HashSet<ILabelProviderListener>(); + private HashMap<String,Object> attrs = new HashMap<String,Object>(); + + private static final TCFModelPresentation default_instance = new TCFModelPresentation(); + + public static TCFModelPresentation getDefault() { + return default_instance; + } + + public void addListener(ILabelProviderListener listener) { + listeners.add(listener); + } + + public void removeListener(ILabelProviderListener listener) { + listeners.remove(listener); + } + + public void dispose() { + } + + public void computeDetail(IValue value, IValueDetailListener listener) { + } + + public Image getImage(Object element) { + ImageDescriptor descriptor = null; + if (element instanceof IBreakpoint) { + final IBreakpoint breakpoint = (IBreakpoint)element; + descriptor = ImageCache.getImageDescriptor(ImageCache.IMG_BREAKPOINT_DISABLED); + try { + if (breakpoint.isEnabled()) { + descriptor = new TCFTask<ImageDescriptor>() { + public void run() { + boolean installed = false; + boolean warning = false; + boolean error = false; + boolean moved = false; + ImageDescriptor d = ImageCache.getImageDescriptor(ImageCache.IMG_BREAKPOINT_ENABLED); + Map<TCFLaunch,Map<String,Object>> status = Activator.getAnnotationManager().getBreakpointStatus(breakpoint); + for (TCFLaunch launch : status.keySet()) { + Map<String,Object> map = status.get(launch); + if (map == null) continue; + if ((String)map.get(IBreakpoints.STATUS_ERROR) != null) error = true; + Object planted = map.get(IBreakpoints.STATUS_INSTANCES); + if (planted == null) continue; + @SuppressWarnings("unchecked") + Collection<Map<String,Object>> list = (Collection<Map<String,Object>>)planted; + for (Map<String,Object> m : list) { + if (m.get(IBreakpoints.INSTANCE_ERROR) == null) { + installed = true; + if (!moved) { + TCFNodeExecContext ctx = null; + String ctx_id = (String)m.get(IBreakpoints.INSTANCE_CONTEXT); + BigInteger addr = JSON.toBigInteger((Number)m.get(IBreakpoints.INSTANCE_ADDRESS)); + int line = breakpoint.getMarker().getAttribute(TCFBreakpointsModel.ATTR_LINE, 0); + if (ctx_id != null && addr != null && line > 0) { + TCFModel model = TCFModelManager.getModelManager().getModel(launch); + if (model != null) { + if (!model.createNode(ctx_id, this)) return; + TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext(model.getNode(ctx_id)); + if (mem != null) { + if (!mem.validate(this)) return; + ctx = mem.getData(); + } + } + } + if (ctx != null) { + ILineNumbers.CodeArea area = null; + TCFDataCache<TCFSourceRef> line_cache = ctx.getLineInfo(addr); + if (line_cache != null) { + if (!line_cache.validate(this)) return; + TCFSourceRef line_data = line_cache.getData(); + if (line_data != null && line_data.area != null) area = line_data.area; + } + if (area != null && area.start_line != line) moved = true; + } + } + } + else { + warning = true; + } + } + } + if (moved) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_MOVED, 0, 0); + else if (installed) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_INSTALLED, 0, 8); + if (warning) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_WARNING, 9, 8); + if (error) d = ImageCache.addOverlay(d, ImageCache.IMG_BREAKPOINT_ERROR, 9, 8); + done(d); + } + }.getE(); + } + String cond = breakpoint.getMarker().getAttribute(TCFBreakpointsModel.ATTR_CONDITION, null); + if (cond != null && cond.length() > 0) { + descriptor = ImageCache.addOverlay(descriptor, ImageCache.IMG_BREAKPOINT_CONDITIONAL); + } + } + catch (Throwable x) { + } + } + if (descriptor != null) return ImageCache.getImage(descriptor); + return null; + } + + public String getText(Object element) { + String text = null; + if (element instanceof IBreakpoint) { + final IBreakpoint breakpoint = (IBreakpoint)element; + IMarker marker = breakpoint.getMarker(); + if (marker == null) return null; + StringBuffer bf = new StringBuffer(); + try { + Map<String,Object> m = marker.getAttributes(); + String file = marker.getAttribute(TCFBreakpointsModel.ATTR_FILE, null); + if (file != null && file.length() > 0) { + IPath path = new Path(file); + if (path.isValidPath(file)) { + bf.append(isShowQualifiedNames() ? path.toOSString() : path.lastSegment()); + bf.append(' '); + } + } + for (int i = 0; i < attr_names.length; i += 2) { + Object obj = m.get(attr_names[i]); + if (obj == null) continue; + String s = obj.toString(); + if (s.length() == 0) continue; + bf.append('['); + bf.append(attr_names[i + 1]); + bf.append(": "); + bf.append(s); + bf.append(']'); + } + if (bf.length() == 0) { + String id = marker.getAttribute(TCFBreakpointsModel.ATTR_ID, null); + if (id == null) id = Long.toString(marker.getId()); + bf.append(id); + } + } + catch (Throwable x) { + return x.toString(); + } + text = bf.toString(); + String status = new TCFTask<String>() { + public void run() { + done(Activator.getAnnotationManager().getBreakpointStatusText(breakpoint)); + } + }.getE(); + if (status != null) text += " (" + status + ")"; + } + return text; + } + + protected boolean isShowQualifiedNames() { + Boolean show_qualified = (Boolean)attrs.get( DISPLAY_FULL_PATHS ); + if (show_qualified == null) return false; + return show_qualified.booleanValue(); + } + + public void setAttribute(String attribute, Object value) { + if (value == null) attrs.remove(attribute); + else attrs.put(attribute, value); + } + + public boolean isLabelProperty(Object element, String property) { + return true; + } + + public String getEditorId(IEditorInput input, Object element) { + String id = null; + if (input != null) { + IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry(); + IEditorDescriptor descriptor = registry.getDefaultEditor(input.getName()); + if (descriptor != null) id = descriptor.getId(); + } + return id; + } + + public IEditorInput getEditorInput(Object element) { + if (element instanceof ILineBreakpoint) { + element = ((ILineBreakpoint)element).getMarker(); + } + if (element instanceof IMarker) { + element = ((IMarker)element).getResource(); + } + if (element instanceof IFile) { + return new FileEditorInput((IFile)element); + } + if (element instanceof IStorage) { + IPath fullPath = ((IStorage)element).getFullPath(); + URI uri = URIUtil.toURI(fullPath); + if (uri != null) { + try { + return new FileStoreEditorInput(EFS.getStore(uri)); + } + catch (CoreException e) { + Activator.log(e); + } + } + } + return null; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java new file mode 100644 index 000000000..84972913d --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelProxy.java @@ -0,0 +1,510 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.InternalVirtualTreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.widgets.Display; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.protocol.Protocol; + +/** + * A model proxy represents a model for a specific presentation context and + * fires deltas to notify listeners of changes in the model. + * Model proxy listeners are debuggers views. + */ +public class TCFModelProxy extends AbstractModelProxy implements IModelProxy, Runnable { + + private static final TCFNode[] EMPTY_NODE_ARRAY = new TCFNode[0]; + + private final TCFModel model; + private final TCFLaunch launch; + private final Display display; + private final Map<TCFNode,Integer> node2flags = new HashMap<TCFNode,Integer>(); + private final Map<TCFNode,TCFNode[]> node2children = new HashMap<TCFNode,TCFNode[]>(); + private final Map<TCFNode,ModelDelta> node2delta = new HashMap<TCFNode,ModelDelta>(); + private final Set<ModelDelta> content_deltas = new HashSet<ModelDelta>(); + private final LinkedList<TCFNode> selection = new LinkedList<TCFNode>(); + + private boolean posted; + private boolean installed; + private boolean disposed; + private boolean realized; + private long last_update_time; + + private final Runnable timer = new Runnable() { + + public void run() { + posted = false; + long idle_time = System.currentTimeMillis() - last_update_time; + long min_idle_time = model.getMinViewUpdatesInterval(); + if (model.getViewUpdatesThrottleEnabled()) { + int congestion = Protocol.getCongestionLevel() + 50; + if (congestion > 0) min_idle_time += congestion * 10; + } + if (model.getChannelThrottleEnabled()) { + int congestion = model.getChannel().getCongestion() + 50; + if (congestion > 0) min_idle_time += congestion * 10; + } + if (idle_time < min_idle_time - 5) { + Protocol.invokeLater(min_idle_time - idle_time, this); + posted = true; + } + else { + TCFModelProxy.this.run(); + } + } + }; + + private class ViewerUpdate implements IViewerUpdate { + + IStatus status; + + public Object getElement() { + return null; + } + + public TreePath getElementPath() { + return null; + } + + public IPresentationContext getPresentationContext() { + return TCFModelProxy.this.getPresentationContext(); + } + + public Object getViewerInput() { + return TCFModelProxy.this.getInput(); + } + + public void cancel() { + } + + public void done() { + } + + public IStatus getStatus() { + return status; + } + + public boolean isCanceled() { + return false; + } + + public void setStatus(IStatus status) { + this.status = status; + } + } + + private class ChildrenCountUpdate extends ViewerUpdate implements IChildrenCountUpdate { + + int count; + + public void setChildCount(int count) { + this.count = count; + } + } + + private class ChildrenUpdate extends ViewerUpdate implements IChildrenUpdate { + + int length; + TCFNode[] children; + + void setLength(int length) { + this.length = length; + this.children = length == 0 ? EMPTY_NODE_ARRAY : new TCFNode[length]; + } + + public int getLength() { + return length; + } + + public int getOffset() { + return 0; + } + + public void setChild(Object child, int offset) { + children[offset] = (TCFNode)child; + } + } + + private final IViewerUpdateListener update_listener = new IViewerUpdateListener() { + + public void viewerUpdatesBegin() { + if (!model.getWaitForViewsUpdateAfterStep()) return; + launch.addPendingClient(this); + } + + public void viewerUpdatesComplete() { + launch.removePendingClient(this); + } + + public void updateStarted(IViewerUpdate update) { + } + + public void updateComplete(IViewerUpdate update) { + } + }; + + private final ChildrenCountUpdate children_count_update = new ChildrenCountUpdate(); + private final ChildrenUpdate children_update = new ChildrenUpdate(); + + private TCFNode pending_node; + + TCFModelProxy(TCFModel model) { + this.model = model; + launch = model.getLaunch(); + display = model.getDisplay(); + } + + public void installed(Viewer viewer) { + if (isDisposed()) return; + super.installed(viewer); + Protocol.invokeAndWait(new Runnable() { + public void run() { + assert !installed; + assert !disposed; + ((ITreeModelViewer)getViewer()).addViewerUpdateListener(update_listener); + model.onProxyInstalled(TCFModelProxy.this); + installed = true; + } + }); + } + + public void dispose() { + if (isDisposed()) return; + Protocol.invokeAndWait(new Runnable() { + public void run() { + assert !disposed; + if (installed) { + model.onProxyDisposed(TCFModelProxy.this); + ((ITreeModelViewer)getViewer()).removeViewerUpdateListener(update_listener); + launch.removePendingClient(update_listener); + launch.removePendingClient(TCFModelProxy.this); + } + disposed = true; + } + }); + super.dispose(); + } + + /** + * Add model change information (delta) to a buffer of pending deltas. + * Implementation will coalesce and post deltas to the view. + * @param node - a model node that changed. + * @param flags - flags that describe the change, see IModelDelta + */ + void addDelta(TCFNode node, int flags) { + assert Protocol.isDispatchThread(); + assert installed && !disposed; + if (flags == 0) return; + Integer delta = node2flags.get(node); + if (delta != null) flags |= delta.intValue(); + node2flags.put(node, flags); + post(); + } + + /** + * Request node to be expanded in the view. + * @param node - a model node that will become expanded. + */ + void expand(TCFNode node) { + Object input = getInput(); + IPresentationContext ctx = getPresentationContext(); + while (node != null && node != input) { + addDelta(node, IModelDelta.EXPAND); + node = node.getParent(ctx); + } + post(); + } + + /** + * Request view selection to be set to given node. + * @param node - a model node that will become new selection. + */ + void setSelection(TCFNode node) { + if (selection.size() > 0 && selection.getLast() == node) return; + selection.add(node); + expand(node.getParent(getPresentationContext())); + } + + /** + * Get current value of the view input. + * @return view input object. + */ + Object getInput() { + return getViewer().getInput(); + } + + public void post() { + assert Protocol.isDispatchThread(); + assert installed && !disposed; + if (!posted) { + long idle_time = System.currentTimeMillis() - last_update_time; + Protocol.invokeLater(model.getMinViewUpdatesInterval() - idle_time, timer); + if (model.getWaitForViewsUpdateAfterStep()) launch.addPendingClient(this); + posted = true; + } + } + + private TCFNode[] getNodeChildren(TCFNode node) { + TCFNode[] res = node2children.get(node); + if (res == null) { + if (node.isDisposed()) { + res = EMPTY_NODE_ARRAY; + } + else if (!node.getLockedData(children_count_update, null)) { + pending_node = node; + res = EMPTY_NODE_ARRAY; + } + else { + children_update.setLength(children_count_update.count); + if (!node.getLockedData(children_update, null)) { + assert false; + pending_node = node; + res = EMPTY_NODE_ARRAY; + } + else { + res = children_update.children; + } + } + node2children.put(node, res); + } + return res; + } + + private int getNodeIndex(TCFNode node) { + TCFNode p = node.getParent(getPresentationContext()); + if (p == null) return -1; + TCFNode[] arr = getNodeChildren(p); + for (int i = 0; i < arr.length; i++) { + if (arr[i] == node) return i; + } + return -1; + } + + private ModelDelta makeDelta(ModelDelta root, TCFNode node, TCFNode selection) { + ModelDelta delta = node2delta.get(node); + if (delta == null) { + if (node == root.getElement()) { + delta = root; + } + else { + int flags = 0; + Integer flags_obj = node2flags.get(node); + if (flags_obj != null) flags = flags_obj.intValue(); + if ((flags & IModelDelta.REMOVED) != 0 && (flags & (IModelDelta.INSERTED|IModelDelta.ADDED)) != 0) return null; + if (node == selection) { + // Bug in Eclipse 3.6.1: SELECT delta has no effect without STATE + flags |= IModelDelta.SELECT | IModelDelta.STATE; + if (this.selection.size() <= 1) flags |= IModelDelta.REVEAL; + } + if (node.parent == null) { + // The node is TCF launch node + if (root.getElement() instanceof TCFNode) return null; + int children = -1; + if (selection != null && selection != node || (flags & IModelDelta.EXPAND) != 0) { + children = getNodeChildren(node).length; + } + delta = root.addNode(launch, -1, flags, children); + } + else { + TCFNode parent = node.getParent(getPresentationContext()); + if (parent == null) return null; + ModelDelta up = makeDelta(root, parent, selection); + if (up == null) return null; + boolean content = content_deltas.contains(up); + if (content) { + assert selection == null; + flags &= ~(IModelDelta.ADDED | IModelDelta.REMOVED | + IModelDelta.REPLACED | IModelDelta.INSERTED | + IModelDelta.CONTENT | IModelDelta.STATE); + if (flags == 0) return null; + } + int index = -1; + int children = -1; + if (selection != null || (flags & IModelDelta.INSERTED) != 0 || (flags & IModelDelta.EXPAND) != 0) { + index = getNodeIndex(node); + } + if (selection != null && selection != node || (flags & IModelDelta.EXPAND) != 0) { + children = getNodeChildren(node).length; + } + delta = up.addNode(node, index, flags, children); + if (content) content_deltas.add(delta); + } + node2delta.put(node, delta); + } + } + int flags = delta.getFlags(); + if ((flags & IModelDelta.REMOVED) != 0) return null; + //if ((flags & IModelDelta.CONTENT) != 0 && (flags & IModelDelta.EXPAND) == 0) return null; + return delta; + } + + private void asyncExec(Runnable r) { + synchronized (Device.class) { + if (!display.isDisposed()) { + display.asyncExec(r); + } + } + } + + private final Comparator<IModelDelta> delta_comparator = new Comparator<IModelDelta>() { + public int compare(IModelDelta o1, IModelDelta o2) { + int f1 = o1.getFlags(); + int f2 = o2.getFlags(); + if ((f1 & IModelDelta.REMOVED) != 0 && (f2 & IModelDelta.REMOVED) == 0) return -1; + if ((f1 & IModelDelta.REMOVED) == 0 && (f2 & IModelDelta.REMOVED) != 0) return +1; + if ((f1 & IModelDelta.ADDED) != 0 && (f2 & IModelDelta.ADDED) == 0) return -1; + if ((f1 & IModelDelta.ADDED) == 0 && (f2 & IModelDelta.ADDED) != 0) return +1; + if ((f1 & IModelDelta.INSERTED) != 0 && (f2 & IModelDelta.INSERTED) == 0) return -1; + if ((f1 & IModelDelta.INSERTED) == 0 && (f2 & IModelDelta.INSERTED) != 0) return +1; + int i1 = o1.getIndex(); + int i2 = o2.getIndex(); + if (i1 < i2) return -1; + if (i1 > i2) return +1; + return 0; + } + }; + + private void sortDeltaChildren(IModelDelta delta) { + IModelDelta arr[] = delta.getChildDeltas(); + Arrays.sort(arr, delta_comparator); + for (IModelDelta d : arr) sortDeltaChildren(d); + } + + private void postDelta(final ModelDelta root) { + assert pending_node == null; + if (root.getFlags() != 0 || root.getChildDeltas().length > 0) { + last_update_time = System.currentTimeMillis(); + asyncExec(new Runnable() { + public void run() { + sortDeltaChildren(root); + fireModelChanged(root); + } + }); + } + } + + public void run() { + assert Protocol.isDispatchThread(); + if (disposed) return; + if (node2flags.isEmpty() && selection.isEmpty()) return; + if (!realized) { + if (getPresentationContext().getId().equals(IDebugUIConstants.ID_DEBUG_VIEW)) { + // Wait until launch manager done creating our launch item in the Debug view. + // Deltas do NOT work without the launch item. + asyncExec(new Runnable() { + boolean found; + public void run() { + if (getViewer() instanceof InternalTreeModelViewer) { + found = ((InternalTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0; + } else if (getViewer() instanceof InternalVirtualTreeModelViewer) { + found = ((InternalVirtualTreeModelViewer)getViewer()).findElementIndex(TreePath.EMPTY, launch) >= 0; + } + Protocol.invokeLater(new Runnable() { + public void run() { + if (disposed) return; + if (found) realized = true; + else last_update_time = System.currentTimeMillis() + 20; + post(); + } + }); + } + }); + return; + } + else { + realized = true; + } + } + Object input = getInput(); + int flags = 0; + if (input instanceof TCFNode) { + // Optimize away STATE delta on a view input node + TCFNode node = (TCFNode)input; + Integer i = node2flags.get(node); + if (i != null) { + flags = i; + if ((flags & IModelDelta.STATE) != 0) { + flags &= ~IModelDelta.STATE; + if (flags == 0) { + node2flags.remove(node); + if (node2flags.isEmpty() && selection.isEmpty()) return; + } + else { + node2flags.put(node, flags); + } + } + } + } + pending_node = null; + node2children.clear(); + if (flags != 0 || node2flags.size() > 0) { + node2delta.clear(); + content_deltas.clear(); + ModelDelta root = new ModelDelta(input, flags); + if ((flags & IModelDelta.CONTENT) != 0) content_deltas.add(root); + for (TCFNode node : node2flags.keySet()) makeDelta(root, node, null); + if (pending_node == null) { + node2flags.clear(); + postDelta(root); + } + } + node2delta.clear(); + content_deltas.clear(); + if (pending_node == null) { + while (!selection.isEmpty()) { + TCFNode node = selection.getFirst(); + if (!node.isDisposed()) { + ModelDelta root = new ModelDelta(input, IModelDelta.NO_CHANGE); + makeDelta(root, node, node); + node2delta.clear(); + content_deltas.clear(); + if (pending_node != null) break; + postDelta(root); + } + selection.remove(node); + } + } + + if (pending_node == null) { + launch.removePendingClient(this); + } + else if (pending_node.getLockedData(children_count_update, this)) { + assert false; + Protocol.invokeLater(this); + } + node2children.clear(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java new file mode 100644 index 000000000..9f5242bb5 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFModelSelectionPolicy.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.tcf.util.TCFTask; + +class TCFModelSelectionPolicy implements IModelSelectionPolicy { + + private final TCFModel model; + + TCFModelSelectionPolicy(TCFModel model) { + this.model = model; + } + + public boolean contains(ISelection selection, IPresentationContext context) { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection)selection; + Object e = ss.getFirstElement(); + if (e instanceof TCFNode) { + TCFNode n = (TCFNode)e; + return !n.isDisposed() && n.model == model; + } + } + return false; + } + + public boolean isSticky(ISelection selection, IPresentationContext context) { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection)selection; + Object e = ss.getFirstElement(); + if (e instanceof TCFNode) return getSuspendReason((TCFNode)e) != null; + } + return false; + } + + private String getSuspendReason(final TCFNode node) { + return new TCFTask<String>() { + public void run() { + TCFNode n = node; + while (n != null && !n.isDisposed()) { + if (n instanceof TCFNodeExecContext) { + TCFDataCache<TCFContextState> cache = ((TCFNodeExecContext)n).getState(); + if (!cache.validate(this)) return; + TCFContextState state = cache.getData(); + if (state != null && state.is_suspended) { + if (state.suspend_reason == null) { + done(IRunControl.REASON_USER_REQUEST); + } + else { + done(state.suspend_reason); + } + return; + } + } + n = n.parent; + } + done(null); + } + }.getE(); + } + + public boolean overrides(ISelection existing, ISelection candidate, IPresentationContext context) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId())) { + if (existing instanceof IStructuredSelection && candidate instanceof IStructuredSelection) { + Object el_existing = ((IStructuredSelection)existing).getFirstElement(); + Object el_candidate = ((IStructuredSelection)candidate).getFirstElement(); + if (el_existing == null) return true; + if (el_existing == el_candidate) return true; + if (el_existing instanceof TCFNode && el_candidate instanceof TCFNode) { + if (el_existing instanceof TCFNodeStackFrame && el_candidate instanceof TCFNodeStackFrame) { + TCFNodeStackFrame curr = (TCFNodeStackFrame)el_existing; + TCFNodeStackFrame next = (TCFNodeStackFrame)el_candidate; + if (curr.parent == next.parent) return true; + } + if (el_existing instanceof TCFNodeStackFrame && el_candidate instanceof TCFNodeExecContext) { + TCFNodeStackFrame curr = (TCFNodeStackFrame)el_existing; + TCFNodeExecContext next = (TCFNodeExecContext)el_candidate; + if (curr.parent == next) return true; + } + String s1 = getSuspendReason((TCFNode)el_existing); + if (s1 == null) return true; + String s2 = getSuspendReason((TCFNode)el_candidate); + if (s2 == null) return false; + if (s2.equals(IRunControl.REASON_USER_REQUEST)) return false; + if (s2.equals(IRunControl.REASON_CONTAINER)) return false; + if (s1.equals(IRunControl.REASON_USER_REQUEST)) return true; + if (s1.equals(IRunControl.REASON_CONTAINER)) return true; + return false; + } + } + } + return true; + } + + public ISelection replaceInvalidSelection(ISelection existing, ISelection candidate) { + if (existing instanceof IStructuredSelection && candidate instanceof IStructuredSelection) { + Object el_existing = ((IStructuredSelection)existing).getFirstElement(); + Object el_candidate = ((IStructuredSelection)candidate).getFirstElement(); + if (el_candidate == null) { + if (el_existing == null) return new StructuredSelection(model.getLaunch()); + if (el_existing instanceof TCFNode) { + TCFNode node = (TCFNode)el_existing; + if (node.parent == null || node.parent instanceof TCFNodeLaunch) { + return new StructuredSelection(model.getLaunch()); + } + return new StructuredSelection(node.parent); + } + } + } + return candidate; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java new file mode 100644 index 000000000..ff5cf76e4 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNode.java @@ -0,0 +1,471 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.LinkedList; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.tcf.debug.ui.ITCFObject; +import org.eclipse.tcf.internal.debug.model.TCFLaunch; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.ui.IWorkbenchPart; + +/** + * TCFNode is base class for all TCF debug model elements. + */ +public abstract class TCFNode extends PlatformObject implements ITCFObject, Comparable<TCFNode> { + + protected final String id; + protected final TCFNode parent; + protected final TCFModel model; + protected final TCFLaunch launch; + protected final IChannel channel; + + private boolean disposed; + + /** + * An extension of TCFDataCache class that is automatically disposed when the parent node is disposed. + */ + protected abstract class TCFData<V> extends TCFDataCache<V> { + + TCFData(IChannel channel) { + super(channel); + addDataCache(this); + } + + @Override + public void dispose() { + removeDataCache(this); + super.dispose(); + } + } + + private LinkedList<TCFDataCache<?>> caches; + + /** + * Constructor for a root node. There should be exactly one root in the model. + * @param model + */ + protected TCFNode(TCFModel model) { + id = null; + parent = null; + launch = model.getLaunch(); + channel = model.getChannel(); + this.model = model; + } + + /** + * Constructor for a node other then root. Node ID must be unique. + * @param parent - parent node. + * @param id - node ID. + */ + protected TCFNode(TCFNode parent, String id) { + assert Protocol.isDispatchThread(); + assert parent != null; + assert id != null; + assert !parent.disposed; + this.parent = parent; + this.id = id; + model = parent.model; + model.addNode(id, this); + launch = model.getLaunch(); + channel = model.getChannel(); + } + + /** + * Register a data cache object that caches data for this node. + * @param c - a TCFData object. + */ + final void addDataCache(TCFDataCache<?> c) { + if (caches == null) caches = new LinkedList<TCFDataCache<?>>(); + caches.add(c); + } + + /** + * Unregister a data cache object that caches children for this node. + * @param c - a TCFData object. + */ + final void removeDataCache(TCFDataCache<?> c) { + if (caches != null) caches.remove(c); + } + + /** + * Flush (reset) all node data caches. + */ + void flushAllCaches() { + if (caches == null) return; + for (TCFDataCache<?> c : caches) c.reset(); + } + + /** + * Dispose this node and its children. The node is removed from the model. + */ + void dispose() { + assert !disposed; + while (caches != null && caches.size() > 0) { + caches.getLast().dispose(); + } + if (parent != null && parent.caches != null) { + for (TCFDataCache<?> c : parent.caches) { + if (c instanceof TCFChildren) ((TCFChildren)c).onNodeDisposed(id); + } + } + if (id != null) { + assert model.getNode(id) == this; + model.removeNode(id); + } + disposed = true; + } + + /** + * Check if node is disposed. + * @return true if disposed. + */ + public final boolean isDisposed() { + return disposed; + } + + /** + * Get TCFModel that owns this node. + * @return TCFModel object + */ + public TCFModel getModel() { + return model; + } + + /** + * Get IChannel of TCFModel that owns this node. + * @return IChannel object + */ + public IChannel getChannel() { + return channel; + } + + /** + * Get TCF ID of the node. + * @return TCF ID + */ + public String getID() { + return id; + } + + /** + * Returns an object which is an instance of the given class + * associated with this object. Returns <code>null</code> if + * no such object can be found. + * + * @param adapter the class to adapt to + * @return the adapted object or <code>null</code> + * @see IAdaptable#getAdapter(Class) + */ + @Override + @SuppressWarnings("rawtypes") + public Object getAdapter(final Class adapter) { + if (adapter.isInstance(this)) return this; + if (adapter.isInstance(model)) return model; + Object o = model.getAdapter(adapter, TCFNode.this); + if (o != null) return o; + return Platform.getAdapterManager().loadAdapter(this, adapter.getName()); + } + + /** + * Get parent node. + * @return parent node or null if the node is a root + */ + public final TCFNode getParent() { + return parent; + } + + /** + * Get parent node in given presentation context. + * @param ctx - presentation context. + * @return parent node or null if the node is a root + */ + public TCFNode getParent(IPresentationContext ctx) { + assert Protocol.isDispatchThread(); + return parent; + } + + /** + * Retrieve children count for a presentation context. + * @param update - children count update request. + */ + final void update(final IChildrenCountUpdate update) { + new TCFRunnable(update) { + public void run() { + if (!done) { + if (!update.isCanceled()) { + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!getData(update, this)) return; + } + else { + update.setChildCount(0); + } + update.setStatus(Status.OK_STATUS); + } + done(); + } + } + }; + } + + /** + * Retrieve children for a presentation context. + * @param update - children update request. + */ + final void update(final IChildrenUpdate update) { + new TCFRunnable(update) { + public void run() { + if (!done) { + if (!update.isCanceled()) { + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!getLockedData(update, this)) return; + } + update.setStatus(Status.OK_STATUS); + } + done(); + } + } + }; + } + + /** + * Check if the node has children in a presentation context. + * @param update - "has children" update request. + */ + final void update(final IHasChildrenUpdate update) { + new TCFRunnable(update) { + public void run() { + if (!done) { + if (!update.isCanceled()) { + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!getLockedData(update, this)) return; + } + else { + update.setHasChilren(false); + } + update.setStatus(Status.OK_STATUS); + } + done(); + } + } + }; + } + + /** + * Retrieve node label for a presentation context. + * @param update - label update request. + */ + final void update(final ILabelUpdate update) { + new TCFRunnable(update) { + public void run() { + if (!done) { + if (!update.isCanceled()) { + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!getLockedData(update, this)) return; + } + else { + update.setLabel("...", 0); + } + update.setStatus(Status.OK_STATUS); + } + done(); + } + } + }; + } + + /** + * Retrieve viewer input object for a presentation context. + * Allows a view to translate the active debug context into an appropriate viewer input element. + * @param update - input update request. + */ + final void update(final IViewerInputUpdate update) { + new TCFRunnable(update) { + public void run() { + if (!done) { + if (!update.isCanceled()) { + if (!disposed && channel.getState() == IChannel.STATE_OPEN) { + if (!getData(update, this)) return; + } + else { + update.setInputElement(TCFNode.this); + } + update.setStatus(Status.OK_STATUS); + } + done(); + } + } + }; + } + + /** + * Retrieve children count for a presentation context. + * If the context is locked, return snapshot data. + * Otherwise return live data from the target. + * The method is always called on TCF dispatch thread. + * @param update - children count update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + final boolean getLockedData(IChildrenCountUpdate update, Runnable done) { + TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext()); + if (snapshot != null) return snapshot.getData(update, this, done); + return getData(update, done); + } + + /** + * Retrieve children for a presentation context. + * If the context is locked, return snapshot data. + * Otherwise return live data from the target. + * The method is always called on TCF dispatch thread. + * @param update - children update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + final boolean getLockedData(IChildrenUpdate update, Runnable done) { + TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext()); + if (snapshot != null) return snapshot.getData(update, this, done); + return getData(update, done); + } + + /** + * Check if the node has children in a presentation context. + * If the context is locked, return snapshot data. + * Otherwise return live data from the target. + * The method is always called on TCF dispatch thread. + * @param update - "has children" update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + final boolean getLockedData(IHasChildrenUpdate update, Runnable done) { + TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext()); + if (snapshot != null) return snapshot.getData(update, this, done); + return getData(update, done); + } + + /** + * Retrieve node label for a presentation context. + * If the context is locked, return snapshot data. + * Otherwise return live data from the target. + * The method is always called on TCF dispatch thread. + * @param update - label update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + final boolean getLockedData(ILabelUpdate update, Runnable done) { + TCFSnapshot snapshot = model.getSnapshot(update.getPresentationContext()); + if (snapshot != null) return snapshot.getData(update, this, done); + return getData(update, done); + } + + /** + * Retrieve children count for a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - children count update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + protected boolean getData(IChildrenCountUpdate update, Runnable done) { + update.setChildCount(0); + return true; + } + + /** + * Retrieve children for a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - children update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + protected boolean getData(IChildrenUpdate update, Runnable done) { + return true; + } + + /** + * Check if the node has children in a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - "has children" update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + protected boolean getData(IHasChildrenUpdate update, Runnable done) { + update.setHasChilren(false); + return true; + } + + /** + * Retrieve node label for a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - label update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + protected boolean getData(ILabelUpdate update, Runnable done) { + update.setLabel(id, 0); + return true; + } + + /** + * Retrieve viewer input object for a presentation context. + * Allows a view to translate the active debug context into an appropriate viewer input element. + * The method is always called on TCF dispatch thread. + * @param update - view input update request. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + protected boolean getData(IViewerInputUpdate update, Runnable done) { + update.setInputElement(this); + return true; + } + + /*--------------------------------------------------------------------------------------*/ + /* Misc */ + + public void refresh(IWorkbenchPart part) { + model.flushAllCaches(); + for (TCFModelProxy p : model.getModelProxies()) { + if (p.getPresentationContext().getPart() != part) continue; + p.addDelta(this, IModelDelta.STATE | IModelDelta.CONTENT); + } + } + + public int compareTo(TCFNode n) { + return id.compareTo(n.id); + } + + public String toString() { + String s = "[" + Integer.toHexString(hashCode()) + "] " + id; + if (disposed) s += ", disposed"; + return s; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java new file mode 100644 index 000000000..e3a12d201 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeArrayPartition.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.tcf.internal.debug.ui.ImageCache; + +public class TCFNodeArrayPartition extends TCFNode { + + private final int offs; + private final int size; + private final TCFChildrenSubExpressions children; + + TCFNodeArrayPartition(TCFNode parent, int level, int offs, int size) { + super(parent, "AP" + level + "." + offs + "." + parent.id); + this.offs = offs; + this.size = size; + children = new TCFChildrenSubExpressions(this, level, offs, size); + } + + int getOffset() { + return offs; + } + + int getSize() { + return size; + } + + @Override + protected boolean getData(IChildrenCountUpdate result, Runnable done) { + if (!children.validate(done)) return false; + result.setChildCount(children.size()); + return true; + } + + @Override + protected boolean getData(IChildrenUpdate result, Runnable done) { + return children.getData(result, done); + } + + @Override + protected boolean getData(IHasChildrenUpdate result, Runnable done) { + result.setHasChilren(true); + return true; + } + + @Override + protected boolean getData(ILabelUpdate result, Runnable done) { + result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_ARRAY_PARTITION), 0); + String[] cols = result.getColumnIds(); + String name = "[" + offs + ".." + (offs + size - 1) + "]"; + if (cols == null || cols.length <= 1) { + result.setLabel(name, 0); + } + else { + for (int i = 0; i < cols.length; i++) { + String c = cols[i]; + if (c.equals(TCFColumnPresentationExpression.COL_NAME)) { + result.setLabel(name, i); + } + else { + result.setLabel("", i); + } + } + } + return true; + } + + void onSuspended() { + children.onSuspended(); + } + + void onValueChanged() { + children.onValueChanged(); + } + + void onRegisterValueChanged() { + children.onRegisterValueChanged(); + } + + void onMemoryChanged() { + children.onMemoryChanged(); + } + + void onMemoryMapChanged() { + children.onMemoryMapChanged(); + } + + @Override + public int compareTo(TCFNode n) { + TCFNodeArrayPartition p = (TCFNodeArrayPartition)n; + if (offs < p.offs) return -1; + if (offs > p.offs) return +1; + return 0; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java new file mode 100644 index 000000000..29836cf61 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExecContext.java @@ -0,0 +1,1541 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.memory.IMemoryRenderingSite; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.internal.debug.model.TCFFunctionRef; +import org.eclipse.tcf.internal.debug.model.TCFSourceRef; +import org.eclipse.tcf.internal.debug.model.TCFSymFileRef; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.IErrorReport; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.JSON; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.ILineNumbers; +import org.eclipse.tcf.services.IMemory; +import org.eclipse.tcf.services.IMemoryMap; +import org.eclipse.tcf.services.IProcesses; +import org.eclipse.tcf.services.IRunControl; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.ui.IWorkbenchPart; + +public class TCFNodeExecContext extends TCFNode implements ISymbolOwner { + + private final TCFChildrenExecContext children_exec; + private final TCFChildrenStackTrace children_stack; + private final TCFChildrenRegisters children_regs; + private final TCFChildrenExpressions children_exps; + private final TCFChildrenHoverExpressions children_hover_exps; + private final TCFChildrenLogExpressions children_log_exps; + private final TCFChildrenModules children_modules; + + private final TCFData<IMemory.MemoryContext> mem_context; + private final TCFData<IRunControl.RunControlContext> run_context; + private final TCFData<MemoryRegion[]> memory_map; + private final TCFData<IProcesses.ProcessContext> prs_context; + private final TCFData<TCFContextState> state; + private final TCFData<BigInteger> address; // Current PC as BigInteger + private final TCFData<Collection<Map<String,Object>>> signal_list; + private final TCFData<SignalMask[]> signal_mask; + private final TCFData<TCFNodeExecContext> memory_node; + private final TCFData<TCFNodeExecContext> symbols_node; + private final TCFData<String> full_name; + + private LinkedHashMap<BigInteger,TCFDataCache<TCFSymFileRef>> syms_info_lookup_cache; + private LinkedHashMap<BigInteger,TCFDataCache<TCFSourceRef>> line_info_lookup_cache; + private LinkedHashMap<BigInteger,TCFDataCache<TCFFunctionRef>> func_info_lookup_cache; + private LookupCacheTimer lookup_cache_timer; + + private int mem_seq_no; + private int exe_seq_no; + + /* + * LookupCacheTimer is executed periodically to dispose least-recently + * accessed entries in line_info_lookup_cache and func_info_lookup_cache. + * The timer disposes itself when both caches become empty. + */ + private class LookupCacheTimer implements Runnable { + + LookupCacheTimer() { + Protocol.invokeLater(4000, this); + } + + public void run() { + if (isDisposed()) return; + if (syms_info_lookup_cache != null) { + BigInteger addr = syms_info_lookup_cache.keySet().iterator().next(); + TCFDataCache<?> cache = syms_info_lookup_cache.get(addr); + if (!cache.isPending()) { + syms_info_lookup_cache.remove(addr).dispose(); + if (syms_info_lookup_cache.size() == 0) syms_info_lookup_cache = null; + } + } + if (line_info_lookup_cache != null) { + BigInteger addr = line_info_lookup_cache.keySet().iterator().next(); + TCFDataCache<?> cache = line_info_lookup_cache.get(addr); + if (!cache.isPending()) { + line_info_lookup_cache.remove(addr).dispose(); + if (line_info_lookup_cache.size() == 0) line_info_lookup_cache = null; + } + } + if (func_info_lookup_cache != null) { + BigInteger addr = func_info_lookup_cache.keySet().iterator().next(); + TCFDataCache<?> cache = func_info_lookup_cache.get(addr); + if (!cache.isPending()) { + func_info_lookup_cache.remove(addr).dispose(); + if (func_info_lookup_cache.size() == 0) func_info_lookup_cache = null; + } + } + if (syms_info_lookup_cache == null && line_info_lookup_cache == null && func_info_lookup_cache == null) { + lookup_cache_timer = null; + } + else { + Protocol.invokeLater(2500, this); + } + } + } + + public static class ChildrenStateInfo { + public boolean running; + public boolean suspended; + public boolean not_active; + public boolean breakpoint; + } + + private final Map<String,TCFNodeSymbol> symbols = new HashMap<String,TCFNodeSymbol>(); + + private int resumed_cnt; + private boolean resume_pending; + private boolean resumed_by_action; + private TCFNode[] last_stack_trace; + private TCFNode[] last_children_list; + private String last_label; + private ImageDescriptor last_image; + private ChildrenStateInfo last_children_state_info; + private boolean delayed_children_list_delta; + + /** + * Wrapper class for IMemoryMap.MemoryRegion. + * The class help to search memory region by address by + * providing contains() method. + */ + public static class MemoryRegion { + + private final BigInteger addr_start; + private final BigInteger addr_end; + + public final IMemoryMap.MemoryRegion region; + + private MemoryRegion(IMemoryMap.MemoryRegion region) { + this.region = region; + Number addr = region.getAddress(); + Number size = region.getSize(); + if (addr == null || size == null) { + addr_start = null; + addr_end = null; + } + else { + addr_start = JSON.toBigInteger(addr); + addr_end = addr_start.add(JSON.toBigInteger(size)); + } + } + + public boolean contains(BigInteger addr) { + return + addr_start != null && addr_end != null && + addr_start.compareTo(addr) <= 0 && + addr_end.compareTo(addr) > 0; + } + + @Override + public String toString() { + return region.getProperties().toString(); + } + } + + public static class SignalMask { + + protected Map<String,Object> props; + protected boolean dont_stop; + protected boolean dont_pass; + protected boolean pending; + + public Number getIndex() { + return (Number)props.get(IProcesses.SIG_INDEX); + } + + public Number getCode() { + return (Number)props.get(IProcesses.SIG_CODE); + } + + public Map<String,Object> getProperties() { + return props; + } + + public boolean isDontStop() { + return dont_stop; + } + + public boolean isDontPass() { + return dont_pass; + } + + public boolean isPending() { + return pending; + } + + @Override + public String toString() { + StringBuffer bf = new StringBuffer(); + bf.append("[attrs="); + bf.append(props.toString()); + if (dont_stop) bf.append(",don't stop"); + if (dont_pass) bf.append(",don't pass"); + if (pending) bf.append(",pending"); + bf.append(']'); + return bf.toString(); + } + } + + TCFNodeExecContext(TCFNode parent, final String id) { + super(parent, id); + children_exec = new TCFChildrenExecContext(this); + children_stack = new TCFChildrenStackTrace(this); + children_regs = new TCFChildrenRegisters(this); + children_exps = new TCFChildrenExpressions(this); + children_hover_exps = new TCFChildrenHoverExpressions(this); + children_log_exps = new TCFChildrenLogExpressions(this); + children_modules = new TCFChildrenModules(this); + mem_context = new TCFData<IMemory.MemoryContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + IMemory mem = launch.getService(IMemory.class); + if (mem == null) { + set(null, null, null); + return true; + } + command = mem.getContext(id, new IMemory.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IMemory.MemoryContext context) { + set(token, error, context); + } + }); + return false; + } + }; + run_context = new TCFData<IRunControl.RunControlContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + IRunControl run = launch.getService(IRunControl.class); + if (run == null) { + set(null, null, null); + return true; + } + command = run.getContext(id, new IRunControl.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRunControl.RunControlContext context) { + if (context != null) model.updateContextMap(id, context); + set(token, error, context); + } + }); + return false; + } + }; + prs_context = new TCFData<IProcesses.ProcessContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + IProcesses prs = launch.getService(IProcesses.class); + if (prs == null) { + set(null, null, null); + return true; + } + command = prs.getContext(id, new IProcesses.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IProcesses.ProcessContext context) { + set(token, error, context); + } + }); + return false; + } + }; + memory_map = new TCFData<MemoryRegion[]>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + IMemoryMap mmap = launch.getService(IMemoryMap.class); + if (mmap == null) { + set(null, null, null); + return true; + } + command = mmap.get(id, new IMemoryMap.DoneGet() { + public void doneGet(IToken token, Exception error, IMemoryMap.MemoryRegion[] map) { + MemoryRegion[] arr = null; + if (map != null) { + int i = 0; + arr = new MemoryRegion[map.length]; + for (IMemoryMap.MemoryRegion r : map) arr[i++] = new MemoryRegion(r); + } + set(token, error, arr); + } + }); + return false; + } + }; + state = new TCFData<TCFContextState>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + if (!run_context.validate(this)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null || !ctx.hasState()) { + set(null, null, null); + return true; + } + command = ctx.getState(new IRunControl.DoneGetState() { + public void doneGetState(IToken token, Exception error, boolean suspended, String pc, String reason, Map<String,Object> params) { + TCFContextState s = new TCFContextState(); + s.is_suspended = suspended; + s.suspend_pc = pc; + s.suspend_reason = reason; + s.suspend_params = params; + set(token, error, s); + } + }); + return false; + } + }; + address = new TCFData<BigInteger>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!run_context.validate(this)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null || !ctx.hasState()) { + set(null, run_context.getError(), null); + return true; + } + if (!state.validate(this)) return false; + TCFContextState s = state.getData(); + if (s == null) { + set(null, state.getError(), null); + return true; + } + if (s.suspend_pc == null) { + set(null, null, null); + return true; + } + set(null, null, new BigInteger(s.suspend_pc)); + return true; + } + }; + signal_list = new TCFData<Collection<Map<String,Object>>>(channel) { + @Override + protected boolean startDataRetrieval() { + IProcesses prs = channel.getRemoteService(IProcesses.class); + if (prs == null) { + set(null, null, null); + return true; + } + command = prs.getSignalList(id, new IProcesses.DoneGetSignalList() { + public void doneGetSignalList(IToken token, Exception error, Collection<Map<String, Object>> list) { + set(token, error, list); + } + }); + return false; + } + }; + signal_mask = new TCFData<SignalMask[]>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!signal_list.validate(this)) return false; + IProcesses prs = channel.getRemoteService(IProcesses.class); + final Collection<Map<String,Object>> sigs = signal_list.getData(); + if (prs == null || sigs == null) { + set(null, signal_list.getError(), null); + return true; + } + command = prs.getSignalMask(id, new IProcesses.DoneGetSignalMask() { + public void doneGetSignalMask(IToken token, Exception error, int dont_stop, int dont_pass, int pending) { + int n = 0; + SignalMask[] list = new SignalMask[sigs.size()]; + for (Map<String,Object> m : sigs) { + SignalMask s = list[n++] = new SignalMask(); + s.props = m; + int mask = 1 << s.getIndex().intValue(); + s.dont_stop = (dont_stop & mask) != 0; + s.dont_pass = (dont_pass & mask) != 0; + s.pending = (pending & mask) != 0; + } + set(token, error, list); + } + }); + return false; + } + }; + memory_node = new TCFData<TCFNodeExecContext>(channel) { + @Override + protected boolean startDataRetrieval() { + String mem_id = null; + if (!run_context.validate(this)) return false; + Throwable err = run_context.getError(); + if (err == null) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null) mem_id = ctx.getProcessID(); + } + if (err != null) { + set(null, err, null); + } + else if (mem_id == null) { + set(null, new Exception("Context does not provide memory access"), null); + } + else { + if (!model.createNode(mem_id, this)) return false; + if (!isValid()) set(null, null, (TCFNodeExecContext)model.getNode(mem_id)); + } + return true; + } + }; + symbols_node = new TCFData<TCFNodeExecContext>(channel) { + @Override + protected boolean startDataRetrieval() { + String syms_id = null; + if (!run_context.validate(this)) return false; + Throwable err = run_context.getError(); + if (err == null) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null) { + syms_id = ctx.getSymbolsGroup(); + if (syms_id == null) syms_id = ctx.getProcessID(); + } + } + if (err != null) { + set(null, err, null); + } + else if (syms_id == null) { + set(null, new Exception("Context does not support symbol groups"), null); + } + else { + if (!model.createNode(syms_id, this)) return false; + if (!isValid()) set(null, null, (TCFNodeExecContext)model.getNode(syms_id)); + } + return true; + } + }; + full_name = new TCFData<String>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!run_context.validate(this)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + String res = null; + if (ctx != null) { + res = ctx.getName(); + if (res == null) { + res = ctx.getID(); + } + else { + // Add ancestor names + TCFNodeExecContext p = TCFNodeExecContext.this; + ArrayList<String> lst = new ArrayList<String>(); + lst.add(res); + while (p.parent instanceof TCFNodeExecContext) { + p = (TCFNodeExecContext)p.parent; + TCFDataCache<IRunControl.RunControlContext> run_ctx_cache = p.run_context; + if (!run_ctx_cache.validate(this)) return false; + IRunControl.RunControlContext run_ctx_data = run_ctx_cache.getData(); + String name = null; + if (run_ctx_data != null) name = run_ctx_data.getName(); + if (name == null) name = ""; + lst.add(name); + } + StringBuffer bf = new StringBuffer(); + for (int i = lst.size(); i > 0; i--) { + bf.append('/'); + bf.append(lst.get(i - 1)); + } + res = bf.toString(); + } + } + set(null, null, res); + return true; + } + }; + } + + @Override + void dispose() { + assert !isDisposed(); + ArrayList<TCFNodeSymbol> l = new ArrayList<TCFNodeSymbol>(symbols.values()); + for (TCFNodeSymbol s : l) s.dispose(); + assert symbols.size() == 0; + super.dispose(); + } + + void setMemSeqNo(int no) { + mem_seq_no = no; + } + + void setExeSeqNo(int no) { + exe_seq_no = no; + } + + TCFChildren getHoverExpressionCache(String expression) { + children_hover_exps.setExpression(expression); + return children_hover_exps; + } + + public TCFChildrenLogExpressions getLogExpressionCache() { + return children_log_exps; + } + + void setRunContext(IRunControl.RunControlContext ctx) { + run_context.reset(ctx); + } + + void setProcessContext(IProcesses.ProcessContext ctx) { + prs_context.reset(ctx); + } + + void setMemoryContext(IMemory.MemoryContext ctx) { + mem_context.reset(ctx); + } + + public TCFDataCache<TCFNodeExecContext> getSymbolsNode() { + return symbols_node; + } + + public TCFDataCache<TCFNodeExecContext> getMemoryNode() { + return memory_node; + } + + public TCFDataCache<MemoryRegion[]> getMemoryMap() { + return memory_map; + } + + public TCFDataCache<Collection<Map<String,Object>>> getSignalList() { + return signal_list; + } + + public TCFDataCache<SignalMask[]> getSignalMask() { + return signal_mask; + } + + public TCFDataCache<TCFSymFileRef> getSymFileInfo(final BigInteger addr) { + if (isDisposed()) return null; + TCFDataCache<TCFSymFileRef> ref_cache; + if (syms_info_lookup_cache != null) { + ref_cache = syms_info_lookup_cache.get(addr); + if (ref_cache != null) return ref_cache; + } + final ISymbols syms = launch.getService(ISymbols.class); + if (syms == null) return null; + if (syms_info_lookup_cache == null) { + syms_info_lookup_cache = new LinkedHashMap<BigInteger,TCFDataCache<TCFSymFileRef>>(11, 0.75f, true); + if (lookup_cache_timer == null) lookup_cache_timer = new LookupCacheTimer(); + } + syms_info_lookup_cache.put(addr, ref_cache = new TCFData<TCFSymFileRef>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!memory_node.validate(this)) return false; + IMemory.MemoryContext mem_data = null; + TCFNodeExecContext mem = memory_node.getData(); + if (mem != null) { + TCFDataCache<IMemory.MemoryContext> mem_cache = mem.mem_context; + if (!mem_cache.validate(this)) return false; + mem_data = mem_cache.getData(); + } + final TCFSymFileRef ref_data = new TCFSymFileRef(); + if (mem_data != null) { + ref_data.context_id = mem_data.getID(); + ref_data.address_size = mem_data.getAddressSize(); + } + command = syms.getSymFileInfo(ref_data.context_id, addr, new ISymbols.DoneGetSymFileInfo() { + public void doneGetSymFileInfo(IToken token, Exception error, Map<String,Object> props) { + ref_data.address = addr; + ref_data.error = error; + ref_data.props = props; + set(token, null, ref_data); + } + }); + return false; + } + }); + return ref_cache; + } + + public TCFDataCache<TCFSourceRef> getLineInfo(final BigInteger addr) { + if (isDisposed()) return null; + TCFDataCache<TCFSourceRef> ref_cache; + if (line_info_lookup_cache != null) { + ref_cache = line_info_lookup_cache.get(addr); + if (ref_cache != null) return ref_cache; + } + final ILineNumbers ln = launch.getService(ILineNumbers.class); + if (ln == null) return null; + final BigInteger n0 = addr; + final BigInteger n1 = n0.add(BigInteger.valueOf(1)); + if (line_info_lookup_cache == null) { + line_info_lookup_cache = new LinkedHashMap<BigInteger,TCFDataCache<TCFSourceRef>>(11, 0.75f, true); + if (lookup_cache_timer == null) lookup_cache_timer = new LookupCacheTimer(); + } + line_info_lookup_cache.put(addr, ref_cache = new TCFData<TCFSourceRef>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!memory_node.validate(this)) return false; + IMemory.MemoryContext mem_data = null; + TCFNodeExecContext mem = memory_node.getData(); + if (mem != null) { + TCFDataCache<IMemory.MemoryContext> mem_cache = mem.mem_context; + if (!mem_cache.validate(this)) return false; + mem_data = mem_cache.getData(); + } + final TCFSourceRef ref_data = new TCFSourceRef(); + if (mem_data != null) { + ref_data.context_id = mem_data.getID(); + ref_data.address_size = mem_data.getAddressSize(); + } + command = ln.mapToSource(id, n0, n1, new ILineNumbers.DoneMapToSource() { + public void doneMapToSource(IToken token, Exception error, ILineNumbers.CodeArea[] areas) { + ref_data.address = addr; + if (error == null && areas != null && areas.length > 0) { + for (ILineNumbers.CodeArea area : areas) { + BigInteger a0 = JSON.toBigInteger(area.start_address); + BigInteger a1 = JSON.toBigInteger(area.end_address); + if (n0.compareTo(a0) >= 0 && n0.compareTo(a1) < 0) { + if (ref_data.area == null || area.start_line < ref_data.area.start_line) { + if (area.start_address != a0 || area.end_address != a1) { + area = new ILineNumbers.CodeArea(area.directory, area.file, + area.start_line, area.start_column, + area.end_line, area.end_column, + a0, a1, area.isa, + area.is_statement, area.basic_block, + area.prologue_end, area.epilogue_begin); + } + ref_data.area = area; + } + } + } + } + ref_data.error = error; + set(token, null, ref_data); + } + }); + return false; + } + }); + return ref_cache; + } + + public TCFDataCache<TCFFunctionRef> getFuncInfo(final BigInteger addr) { + if (isDisposed()) return null; + TCFDataCache<TCFFunctionRef> ref_cache; + if (func_info_lookup_cache != null) { + ref_cache = func_info_lookup_cache.get(addr); + if (ref_cache != null) return ref_cache; + } + final ISymbols syms = launch.getService(ISymbols.class); + if (syms == null) return null; + if (func_info_lookup_cache == null) { + func_info_lookup_cache = new LinkedHashMap<BigInteger,TCFDataCache<TCFFunctionRef>>(11, 0.75f, true); + if (lookup_cache_timer == null) lookup_cache_timer = new LookupCacheTimer(); + } + func_info_lookup_cache.put(addr, ref_cache = new TCFData<TCFFunctionRef>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!memory_node.validate(this)) return false; + IMemory.MemoryContext mem_data = null; + TCFNodeExecContext mem = memory_node.getData(); + if (mem != null) { + TCFDataCache<IMemory.MemoryContext> mem_cache = mem.mem_context; + if (!mem_cache.validate(this)) return false; + mem_data = mem_cache.getData(); + } + final TCFFunctionRef ref_data = new TCFFunctionRef(); + if (mem_data != null) { + ref_data.context_id = mem_data.getID(); + ref_data.address_size = mem_data.getAddressSize(); + } + command = syms.findByAddr(id, addr, new ISymbols.DoneFind() { + public void doneFind(IToken token, Exception error, String symbol_id) { + ref_data.address = addr; + ref_data.error = error; + ref_data.symbol_id = symbol_id; + set(token, null, ref_data); + } + }); + return false; + } + }); + return ref_cache; + } + + private void clearLookupCaches() { + if (syms_info_lookup_cache != null) { + Iterator<TCFDataCache<TCFSymFileRef>> i = syms_info_lookup_cache.values().iterator(); + while (i.hasNext()) { + TCFDataCache<TCFSymFileRef> cache = i.next(); + if (cache.isPending()) continue; + cache.dispose(); + i.remove(); + } + if (syms_info_lookup_cache.size() == 0) syms_info_lookup_cache = null; + } + if (line_info_lookup_cache != null) { + Iterator<TCFDataCache<TCFSourceRef>> i = line_info_lookup_cache.values().iterator(); + while (i.hasNext()) { + TCFDataCache<TCFSourceRef> cache = i.next(); + if (cache.isPending()) continue; + cache.dispose(); + i.remove(); + } + if (line_info_lookup_cache.size() == 0) line_info_lookup_cache = null; + } + if (func_info_lookup_cache != null) { + Iterator<TCFDataCache<TCFFunctionRef>> i = func_info_lookup_cache.values().iterator(); + while (i.hasNext()) { + TCFDataCache<TCFFunctionRef> cache = i.next(); + if (cache.isPending()) continue; + cache.dispose(); + i.remove(); + } + if (func_info_lookup_cache.size() == 0) func_info_lookup_cache = null; + } + } + + @Override + public TCFNode getParent(IPresentationContext ctx) { + assert Protocol.isDispatchThread(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(ctx.getId())) { + Set<String> ids = launch.getContextFilter(); + if (ids != null) { + if (ids.contains(id)) return model.getRootNode(); + if (parent instanceof TCFNodeLaunch) return null; + } + } + return parent; + } + + public TCFDataCache<IRunControl.RunControlContext> getRunContext() { + return run_context; + } + + public TCFDataCache<IProcesses.ProcessContext> getProcessContext() { + return prs_context; + } + + public TCFDataCache<IMemory.MemoryContext> getMemoryContext() { + return mem_context; + } + + public TCFDataCache<BigInteger> getAddress() { + return address; + } + + public TCFDataCache<TCFContextState> getState() { + return state; + } + + public TCFChildrenStackTrace getStackTrace() { + return children_stack; + } + + public TCFChildren getRegisters() { + return children_regs; + } + + public TCFChildren getModules() { + return children_modules; + } + + public TCFChildren getChildren() { + return children_exec; + } + + public TCFNodeStackFrame getLastTopFrame() { + if (!resume_pending) return null; + if (last_stack_trace == null || last_stack_trace.length == 0) return null; + return (TCFNodeStackFrame)last_stack_trace[0]; + } + + public TCFNodeStackFrame getViewBottomFrame() { + if (last_stack_trace == null || last_stack_trace.length == 0) return null; + return (TCFNodeStackFrame)last_stack_trace[last_stack_trace.length - 1]; + } + + /** + * Get context full name - including all ancestor names. + * Return context ID if the context does not have a name. + * @return cache item with the context full name. + */ + public TCFDataCache<String> getFullName() { + return full_name; + } + + public void addSymbol(TCFNodeSymbol s) { + assert symbols.get(s.id) == null; + symbols.put(s.id, s); + } + + public void removeSymbol(TCFNodeSymbol s) { + assert symbols.get(s.id) == s; + symbols.remove(s.id); + } + + /** + * Return true if this context cannot be accessed because it is not active. + * Not active means the target is suspended but this context is not one that is + * currently scheduled to run on a target CPU. + * Some debuggers don't support access to register values and other properties of such contexts. + */ + public boolean isNotActive() { + TCFContextState state_data = state.getData(); + if (state_data != null && state_data.suspend_params != null) { + @SuppressWarnings("unchecked") + Map<String,Object> attrs = (Map<String,Object>)state_data.suspend_params.get(IRunControl.STATE_PC_ERROR); + if (attrs != null) { + Number n = (Number)attrs.get(IErrorReport.ERROR_CODE); + if (n != null) return n.intValue() == IErrorReport.TCF_ERROR_NOT_ACTIVE; + } + } + return false; + } + + @Override + protected boolean getData(IChildrenCountUpdate result, Runnable done) { + TCFChildren children = null; + String view_id = result.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { + if (resume_pending && last_stack_trace != null) { + result.setChildCount(last_stack_trace.length); + return true; + } + if (!state.validate(done)) return false; + if (isNotActive()) { + last_stack_trace = new TCFNode[0]; + result.setChildCount(0); + return true; + } + TCFContextState state_data = state.getData(); + if (state_data != null && !state_data.is_suspended) { + result.setChildCount(0); + return true; + } + children = children_stack; + } + else { + if (!model.getAutoChildrenListUpdates() && last_children_list != null) { + result.setChildCount(last_children_list.length); + return true; + } + children = children_exec; + } + } + else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_regs; + } + else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_exps; + } + else if (TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_hover_exps; + } + else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) { + if (!mem_context.validate(done)) return false; + IMemory.MemoryContext ctx = mem_context.getData(); + if (ctx != null) children = children_modules; + } + if (children != null) { + if (!children.validate(done)) return false; + if (children == children_stack) last_stack_trace = children_stack.toArray(); + if (children == children_exec) last_children_list = children_exec.toArray(); + result.setChildCount(children.size()); + } + else { + result.setChildCount(0); + } + return true; + } + + private void setResultChildren(IChildrenUpdate result, TCFNode[] arr) { + int offset = 0; + int r_offset = result.getOffset(); + int r_length = result.getLength(); + for (TCFNode n : arr) { + if (offset >= r_offset && offset < r_offset + r_length) { + result.setChild(n, offset); + } + offset++; + } + } + + @Override + protected boolean getData(IChildrenUpdate result, Runnable done) { + TCFChildren children = null; + String view_id = result.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { + if (resume_pending && last_stack_trace != null) { + setResultChildren(result, last_stack_trace); + return true; + } + if (!state.validate(done)) return false; + if (isNotActive()) { + last_stack_trace = new TCFNode[0]; + return true; + } + TCFContextState state_data = state.getData(); + if (state_data != null && !state_data.is_suspended) { + return true; + } + children = children_stack; + } + else { + if (!model.getAutoChildrenListUpdates() && last_children_list != null) { + setResultChildren(result, last_children_list); + return true; + } + children = children_exec; + } + } + else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_regs; + } + else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_exps; + } + else if (TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_hover_exps; + } + else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) { + if (!mem_context.validate(done)) return false; + IMemory.MemoryContext ctx = mem_context.getData(); + if (ctx != null) children = children_modules; + } + if (children == null) return true; + if (children == children_stack) { + if (!children.validate(done)) return false; + last_stack_trace = children_stack.toArray(); + } + if (children == children_exec) { + if (!children.validate(done)) return false; + last_children_list = children_exec.toArray(); + } + return children.getData(result, done); + } + + @Override + protected boolean getData(IHasChildrenUpdate result, Runnable done) { + TCFChildren children = null; + String view_id = result.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) { + if (resume_pending && last_stack_trace != null) { + result.setHasChilren(last_stack_trace.length > 0); + return true; + } + if (!state.validate(done)) return false; + if (isNotActive()) { + last_stack_trace = new TCFNode[0]; + result.setHasChilren(false); + return true; + } + TCFContextState state_data = state.getData(); + if (state_data != null) { + result.setHasChilren(state_data.is_suspended); + return true; + } + children = children_stack; + } + else { + if (!model.getAutoChildrenListUpdates() && last_children_list != null) { + result.setHasChilren(last_children_list.length > 0); + return true; + } + children = children_exec; + } + } + else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_regs; + } + else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_exps; + } + else if (TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) { + if (!run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && ctx.hasState()) children = children_hover_exps; + } + else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) { + if (!mem_context.validate(done)) return false; + IMemory.MemoryContext ctx = mem_context.getData(); + if (ctx != null) children = children_modules; + } + if (children != null) { + if (!children.validate(done)) return false; + if (children == children_stack) last_stack_trace = children_stack.toArray(); + if (children == children_exec) last_children_list = children_exec.toArray(); + result.setHasChilren(children.size() > 0); + } + else { + result.setHasChilren(false); + } + return true; + } + + @Override + protected boolean getData(ILabelUpdate result, Runnable done) { + result.getViewerInput(); + if (!run_context.validate(done)) return false; + String image_name = null; + boolean suspended_by_bp = false; + StringBuffer label = new StringBuffer(); + Throwable error = run_context.getError(); + if (error != null) { + result.setForeground(new RGB(255, 0, 0), 0); + label.append(id); + label.append(": "); + label.append(TCFModel.getErrorMessage(error, false)); + } + else { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx == null) { + label.append(id); + } + else { + String nm = ctx.getName(); + if (nm == null && !ctx.hasState()) { + String prs = ctx.getProcessID(); + if (prs != null) { + if (!prs_context.validate(done)) return false; + IProcesses.ProcessContext pctx = prs_context.getData(); + if (pctx != null) nm = pctx.getName(); + } + } + label.append(nm != null ? nm : id); + if (ctx.hasState() && !TCFModel.ID_PINNED_VIEW.equals(result.getPresentationContext().getId())) { + // Thread + if (resume_pending && resumed_by_action || model.getActiveAction(id) != null) { + image_name = ImageCache.IMG_THREAD_RUNNNIG; + if (resume_pending && last_label != null) { + result.setImageDescriptor(ImageCache.getImageDescriptor(image_name), 0); + result.setLabel(last_label, 0); + return true; + } + label.append(" (Running)"); + } + else if (resume_pending && last_label != null && last_image != null) { + result.setImageDescriptor(last_image, 0); + result.setLabel(last_label, 0); + return true; + } + else { + if (!state.validate(done)) return false; + TCFContextState state_data = state.getData(); + if (isNotActive()) { + image_name = ImageCache.IMG_THREAD_NOT_ACTIVE; + label.append(" (Not active)"); + if (state_data.suspend_reason != null && !state_data.suspend_reason.equals(IRunControl.REASON_USER_REQUEST)) { + label.append(" - "); + label.append(state_data.suspend_reason); + } + } + else { + if (state_data == null) image_name = ImageCache.IMG_THREAD_UNKNOWN_STATE; + else if (state_data.is_suspended) image_name = ImageCache.IMG_THREAD_SUSPENDED; + else image_name = ImageCache.IMG_THREAD_RUNNNIG; + if (state_data != null) { + if (!state_data.is_suspended) { + label.append(" (Running)"); + } + else { + String s = null; + String r = model.getContextActionResult(id); + if (r == null) { + r = state_data.suspend_reason; + if (state_data.suspend_params != null) { + s = (String)state_data.suspend_params.get(IRunControl.STATE_SIGNAL_DESCRIPTION); + if (s == null) s = (String)state_data.suspend_params.get(IRunControl.STATE_SIGNAL_NAME); + } + } + suspended_by_bp = IRunControl.REASON_BREAKPOINT.equals(r); + if (r == null) r = "Suspended"; + if (s != null) r += ": " + s; + label.append(" ("); + label.append(r); + if (state_data.suspend_params != null) { + String prs = (String)state_data.suspend_params.get("Context"); + if (prs != null) { + label.append(", "); + label.append(prs); + } + String cpu = (String)state_data.suspend_params.get("CPU"); + if (cpu != null) { + label.append(", "); + label.append(cpu); + } + } + label.append(")"); + } + } + } + } + last_children_state_info = null; + } + else { + // Thread container (process) + ChildrenStateInfo i = new ChildrenStateInfo(); + if (!hasSuspendedChildren(i, done)) return false; + if (i.suspended) image_name = ImageCache.IMG_PROCESS_SUSPENDED; + else image_name = ImageCache.IMG_PROCESS_RUNNING; + suspended_by_bp = i.breakpoint; + last_children_state_info = i; + } + } + } + last_image = ImageCache.getImageDescriptor(image_name); + if (suspended_by_bp) last_image = ImageCache.addOverlay(last_image, ImageCache.IMG_BREAKPOINT_OVERLAY); + result.setImageDescriptor(last_image, 0); + result.setLabel(last_label = label.toString(), 0); + return true; + } + + @Override + protected boolean getData(IViewerInputUpdate result, Runnable done) { + result.setInputElement(this); + String view_id = result.getPresentationContext().getId(); + if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(view_id)) { + if (!children_stack.validate(done)) return false; + TCFNodeStackFrame frame = children_stack.getTopFrame(); + if (frame != null) result.setInputElement(frame); + } + else if (IDebugUIConstants.ID_MODULE_VIEW.equals(view_id)) { + // TODO: need to post view input delta when memory context changes + TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext(this); + if (mem == null) return true; + if (!mem.validate(done)) return false; + if (mem.getData() == null) return true; + result.setInputElement(mem.getData()); + } + return true; + } + + @Override + public void refresh(IWorkbenchPart part) { + if (part instanceof IMemoryRenderingSite) { + model.onMemoryChanged(id, false, false); + } + else { + last_children_list = null; + last_children_state_info = null; + last_stack_trace = null; + last_label = null; + last_image = null; + super.refresh(part); + } + } + + void postAllChangedDelta() { + postContentChangedDelta(); + postStateChangedDelta(); + } + + void postContextAddedDelta() { + if (parent instanceof TCFNodeExecContext) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent; + ChildrenStateInfo info = exe.last_children_state_info; + if (info != null) { + if (!model.getAutoChildrenListUpdates()) { + // Manual manual updates. + return; + } + if (!info.suspended && !info.not_active && model.getDelayChildrenListUpdates()) { + // Delay content update until a child is suspended. + exe.delayed_children_list_delta = true; + return; + } + } + } + for (TCFModelProxy p : model.getModelProxies()) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) { + /* Note: should use IModelDelta.INSERTED but it is broken in Eclipse 3.6 */ + p.addDelta(this, IModelDelta.ADDED); + } + } + } + + private void postContextRemovedDelta() { + if (parent instanceof TCFNodeExecContext) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent; + ChildrenStateInfo info = exe.last_children_state_info; + if (info != null) { + if (!model.getAutoChildrenListUpdates()) { + // Manual manual updates. + return; + } + if (!info.suspended && !info.not_active && model.getDelayChildrenListUpdates()) { + // Delay content update until a child is suspended. + exe.delayed_children_list_delta = true; + return; + } + } + } + for (TCFModelProxy p : model.getModelProxies()) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) { + p.addDelta(this, IModelDelta.REMOVED); + } + } + } + + private void postContentChangedDelta() { + delayed_children_list_delta = false; + for (TCFModelProxy p : model.getModelProxies()) { + int flags = 0; + String view_id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id) && + (launch.getContextActionsCount(id) == 0 || + !model.getDelayStackUpdateUtilLastStep())) { + flags |= IModelDelta.CONTENT; + } + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(view_id) || + IDebugUIConstants.ID_EXPRESSION_VIEW.equals(view_id) || + TCFModel.ID_EXPRESSION_HOVER.equals(view_id)) { + if (p.getInput() == this) flags |= IModelDelta.CONTENT; + } + if (flags == 0) continue; + p.addDelta(this, flags); + } + } + + private void postAllAndParentsChangedDelta() { + postContentChangedDelta(); + TCFNode n = this; + while (n instanceof TCFNodeExecContext) { + TCFNodeExecContext e = (TCFNodeExecContext)n; + if (e.delayed_children_list_delta) e.postContentChangedDelta(); + e.postStateChangedDelta(); + n = n.parent; + } + } + + public void postStateChangedDelta() { + for (TCFModelProxy p : model.getModelProxies()) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) { + p.addDelta(this, IModelDelta.STATE); + } + } + } + + private void postModulesChangedDelta() { + for (TCFModelProxy p : model.getModelProxies()) { + if (IDebugUIConstants.ID_MODULE_VIEW.equals(p.getPresentationContext().getId())) { + p.addDelta(this, IModelDelta.CONTENT); + } + } + } + + private void postStackChangedDelta() { + for (TCFModelProxy p : model.getModelProxies()) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(p.getPresentationContext().getId())) { + p.addDelta(this, IModelDelta.CONTENT); + } + } + } + + void onContextAdded(IRunControl.RunControlContext context) { + children_exec.onContextAdded(context); + } + + void onContextChanged(IRunControl.RunControlContext context) { + assert !isDisposed(); + full_name.reset(); + run_context.reset(context); + symbols_node.reset(); + memory_node.reset(); + signal_mask.reset(); + if (state.isValid()) { + TCFContextState s = state.getData(); + if (s == null || s.is_suspended) state.reset(); + } + children_stack.reset(); + children_stack.onSourceMappingChange(); + children_regs.reset(); + children_exec.onAncestorContextChanged(); + for (TCFNodeSymbol s : symbols.values()) s.onMemoryMapChanged(); + postAllChangedDelta(); + } + + void onAncestorContextChanged() { + full_name.reset(); + } + + void onContextAdded(IMemory.MemoryContext context) { + children_exec.onContextAdded(context); + } + + void onContextChanged(IMemory.MemoryContext context) { + assert !isDisposed(); + clearLookupCaches(); + mem_context.reset(context); + for (TCFNodeSymbol s : symbols.values()) s.onMemoryMapChanged(); + postAllChangedDelta(); + } + + void onContextRemoved() { + assert !isDisposed(); + resumed_cnt++; + resume_pending = false; + resumed_by_action = false; + dispose(); + postContextRemovedDelta(); + launch.removeContextActions(id); + } + + void onExpressionAddedOrRemoved() { + children_exps.cancel(); + children_stack.onExpressionAddedOrRemoved(); + } + + void onContainerSuspended() { + assert !isDisposed(); + if (run_context.isValid()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && !ctx.hasState()) return; + } + onContextSuspended(null, null, null); + } + + void onContainerResumed() { + assert !isDisposed(); + if (run_context.isValid()) { + IRunControl.RunControlContext ctx = run_context.getData(); + if (ctx != null && !ctx.hasState()) return; + } + onContextResumed(); + } + + void onContextSuspended(String pc, String reason, Map<String,Object> params) { + assert !isDisposed(); + if (pc != null) { + TCFContextState s = new TCFContextState(); + s.is_suspended = true; + s.suspend_pc = pc; + s.suspend_reason = reason; + s.suspend_params = params; + state.reset(s); + } + else { + state.reset(); + } + address.reset(); + signal_mask.reset(); + children_stack.onSuspended(); + children_regs.onSuspended(); + children_exps.onSuspended(); + children_hover_exps.onSuspended(); + children_log_exps.onSuspended(); + for (TCFNodeSymbol s : symbols.values()) s.onExeStateChange(); + if (model.getActiveAction(id) == null) { + boolean update_now = pc != null || resumed_by_action; + resumed_cnt++; + resume_pending = false; + resumed_by_action = false; + if (update_now) { + children_stack.postAllChangedDelta(); + postAllAndParentsChangedDelta(); + } + else { + final int cnt = ++resumed_cnt; + Protocol.invokeLater(500, new Runnable() { + public void run() { + if (cnt != resumed_cnt) return; + if (isDisposed()) return; + children_stack.postAllChangedDelta(); + postAllAndParentsChangedDelta(); + } + }); + } + } + } + + void onContextResumed() { + assert !isDisposed(); + state.reset(new TCFContextState()); + if (!resume_pending) { + final int cnt = ++resumed_cnt; + resume_pending = true; + resumed_by_action = model.getActiveAction(id) != null; + if (resumed_by_action) postAllChangedDelta(); + Protocol.invokeLater(400, new Runnable() { + public void run() { + if (cnt != resumed_cnt) return; + if (isDisposed()) return; + children_stack.onResumed(); + resume_pending = false; + postAllAndParentsChangedDelta(); + model.onContextRunning(); + } + }); + } + } + + void onContextActionDone() { + if (!state.isValid() || state.getData() == null || state.getData().is_suspended) { + resumed_cnt++; + resume_pending = false; + resumed_by_action = false; + } + postAllChangedDelta(); + children_stack.postAllChangedDelta(); + } + + void onContextException(String msg) { + } + + void onMemoryChanged(Number[] addr, long[] size) { + assert !isDisposed(); + children_stack.onMemoryChanged(); + children_exps.onMemoryChanged(); + children_hover_exps.onMemoryChanged(); + children_log_exps.onMemoryChanged(); + postContentChangedDelta(); + } + + void onMemoryMapChanged() { + clearLookupCaches(); + memory_map.reset(); + children_modules.onMemoryMapChanged(); + children_stack.onMemoryMapChanged(); + children_exps.onMemoryMapChanged(); + children_hover_exps.onMemoryMapChanged(); + children_log_exps.onMemoryMapChanged(); + postModulesChangedDelta(); + } + + void onRegistersChanged() { + children_stack.onRegistersChanged(); + postContentChangedDelta(); + } + + void onRegisterValueChanged() { + if (state.isValid()) { + TCFContextState s = state.getData(); + if (s == null || s.is_suspended) state.reset(); + } + address.reset(); + children_stack.onRegisterValueChanged(); + children_exps.onRegisterValueChanged(); + children_hover_exps.onRegisterValueChanged(); + children_log_exps.onRegisterValueChanged(); + postContentChangedDelta(); + } + + void onPreferencesChanged() { + if (delayed_children_list_delta && !model.getDelayChildrenListUpdates() || + model.getAutoChildrenListUpdates()) postContentChangedDelta(); + children_stack.onPreferencesChanged(); + postStackChangedDelta(); + } + + void riseTraceLimit() { + children_stack.riseTraceLimit(); + postStackChangedDelta(); + } + + public boolean hasSuspendedChildren(ChildrenStateInfo info, Runnable done) { + if (!children_exec.validate(done)) return false; + Map<String,TCFNode> m = children_exec.getData(); + if (m == null || m.size() == 0) return true; + for (TCFNode n : m.values()) { + if (!(n instanceof TCFNodeExecContext)) continue; + TCFNodeExecContext e = (TCFNodeExecContext)n; + if (!e.run_context.validate(done)) return false; + IRunControl.RunControlContext ctx = e.run_context.getData(); + if (ctx != null && ctx.hasState()) { + TCFDataCache<TCFContextState> state_cache = e.getState(); + if (!state_cache.validate(done)) return false; + TCFContextState state_data = state_cache.getData(); + if (state_data != null) { + if (!state_data.is_suspended) { + info.running = true; + } + else if (e.isNotActive()) { + info.not_active = true; + } + else { + info.suspended = true; + String r = model.getContextActionResult(e.id); + if (r == null) r = state_data.suspend_reason; + if (IRunControl.REASON_BREAKPOINT.equals(r)) info.breakpoint = true; + } + } + } + else { + if (!e.hasSuspendedChildren(info, done)) return false; + } + if (info.breakpoint && info.running) break; + } + return true; + } + + @Override + public int compareTo(TCFNode n) { + if (n instanceof TCFNodeExecContext) { + TCFNodeExecContext f = (TCFNodeExecContext)n; + if (mem_seq_no < f.mem_seq_no) return -1; + if (mem_seq_no > f.mem_seq_no) return +1; + if (exe_seq_no < f.exe_seq_no) return -1; + if (exe_seq_no > f.exe_seq_no) return +1; + } + return id.compareTo(n.id); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java new file mode 100644 index 000000000..a83c10642 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeExpression.java @@ -0,0 +1,1618 @@ +/******************************************************************************* + * Copyright (c) 2008, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.util.LinkedList; +import java.util.Map; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IExpressionManager; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.core.model.IWatchExpression; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.internal.debug.ui.Activator; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.JSON; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IExpressions; +import org.eclipse.tcf.services.IMemory; +import org.eclipse.tcf.services.IMemory.MemoryError; +import org.eclipse.tcf.services.IRegisters; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.tcf.util.TCFTask; + +public class TCFNodeExpression extends TCFNode implements IElementEditor, ICastToType, IWatchInExpressions, IDetailsProvider { + + // TODO: User commands: Add Global Variables, Remove Global Variables + // TODO: enable Change Value user command + + private final String script; + private final int index; + private final boolean deref; + private final String field_id; + private final String reg_id; + private final TCFData<IExpressions.Expression> var_expression; + private final TCFData<String> base_text; + private final TCFData<Expression> expression; + private final TCFData<IExpressions.Value> value; + private final TCFData<ISymbols.Symbol> type; + private final TCFData<String> type_name; + private final TCFData<String> string; + private final TCFData<String> expression_text; + private final TCFChildrenSubExpressions children; + private int sort_pos; + private boolean enabled = true; + private IExpressions.Value prev_value; + private IExpressions.Value next_value; + + private static final RGB + rgb_error = new RGB(192, 0, 0), + rgb_highlight = new RGB(255, 255, 128), + rgb_disabled = new RGB(127, 127, 127); + + private static int expr_cnt; + + private class Expression { + + final IExpressions.Expression expression; + boolean must_be_disposed; + + Expression(IExpressions.Expression expression) { + assert expression != null; + this.expression = expression; + } + + void dispose() { + if (!must_be_disposed) return; + if (channel.getState() == IChannel.STATE_OPEN) { + IExpressions exps = channel.getRemoteService(IExpressions.class); + exps.dispose(expression.getID(), new IExpressions.DoneDispose() { + public void doneDispose(IToken token, Exception error) { + if (error == null) return; + if (channel.getState() != IChannel.STATE_OPEN) return; + Activator.log("Error disposing remote expression evaluator", error); + } + }); + } + must_be_disposed = false; + } + } + + TCFNodeExpression(final TCFNode parent, final String script, + final String field_id, final String var_id, final String reg_id, + final int index, final boolean deref) { + super(parent, var_id != null ? var_id : "Expr" + expr_cnt++); + assert script != null || field_id != null || var_id != null || reg_id != null || index >= 0; + this.script = script; + this.field_id = field_id; + this.reg_id = reg_id; + this.index = index; + this.deref = deref; + var_expression = new TCFData<IExpressions.Expression>(channel) { + @Override + protected boolean startDataRetrieval() { + IExpressions exps = launch.getService(IExpressions.class); + if (exps == null || var_id == null) { + set(null, null, null); + return true; + } + command = exps.getContext(var_id, new IExpressions.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IExpressions.Expression context) { + set(token, error, context); + } + }); + return false; + } + }; + base_text = new TCFData<String>(channel) { + @Override + protected boolean startDataRetrieval() { + /* Compute expression script, not including type cast */ + if (script != null) { + set(null, null, script); + return true; + } + if (var_id != null) { + if (!var_expression.validate(this)) return false; + Throwable err = null; + String exp = null; + if (var_expression.getData() == null) { + err = var_expression.getError(); + } + else { + exp = var_expression.getData().getExpression(); + if (exp == null) err = new Exception("Missing 'Expression' property"); + } + set(null, err, exp); + return true; + } + if (reg_id != null) { + set(null, null, "${" + reg_id + "}"); + return true; + } + TCFNode n = parent; + while (n instanceof TCFNodeArrayPartition) n = n.parent; + TCFDataCache<String> t = ((TCFNodeExpression)n).base_text; + if (!t.validate(this)) return false; + String e = t.getData(); + if (e == null) { + set(null, t.getError(), null); + return true; + } + String cast = model.getCastToType(n.id); + if (cast != null) e = "(" + cast + ")(" + e + ")"; + if (field_id != null) { + e = "(" + e + ")" + (deref ? "->" : ".") + "${" + field_id + "}"; + } + else if (index == 0) { + e = "*(" + e + ")"; + } + else if (index > 0) { + e = "(" + e + ")[" + index + "]"; + } + set(null, null, e); + return true; + } + }; + expression_text = new TCFData<String>(channel) { + @Override + protected boolean startDataRetrieval() { + /* Compute human readable expression script, + * including type cast, and using variable names instead of IDs */ + String expr_text = null; + if (script != null) { + expr_text = script; + } + else if (var_id != null) { + if (!var_expression.validate(this)) return false; + IExpressions.Expression e = var_expression.getData(); + if (e != null) { + TCFDataCache<ISymbols.Symbol> var = model.getSymbolInfoCache(e.getSymbolID()); + if (var != null) { + if (!var.validate(this)) return false; + if (var.getData() != null) expr_text = var.getData().getName(); + } + } + } + else if (reg_id != null) { + if (!model.createNode(reg_id, this)) return false; + if (isValid()) return true; + TCFNodeRegister reg_node = (TCFNodeRegister)model.getNode(reg_id); + for (;;) { + TCFDataCache<IRegisters.RegistersContext> ctx_cache = reg_node.getContext(); + if (!ctx_cache.validate(this)) return false; + IRegisters.RegistersContext ctx_data = ctx_cache.getData(); + if (ctx_data == null) { + set(null, ctx_cache.getError(), null); + return true; + } + expr_text = expr_text == null ? ctx_data.getName() : ctx_data.getName() + "." + expr_text; + if (!(reg_node.parent instanceof TCFNodeRegister)) break; + reg_node = (TCFNodeRegister)reg_node.parent; + } + expr_text = "$" + expr_text; + } + else { + TCFDataCache<?> pending = null; + TCFDataCache<ISymbols.Symbol> field = model.getSymbolInfoCache(field_id); + if (field != null && !field.validate()) pending = field; + if (!base_text.validate()) pending = base_text; + if (pending != null) { + pending.wait(this); + return false; + } + String parent_text = ""; + TCFNode n = parent; + while (n instanceof TCFNodeArrayPartition) n = n.parent; + TCFDataCache<String> parent_text_cache = ((TCFNodeExpression)n).expression_text; + if (!parent_text_cache.validate(this)) return false; + if (parent_text_cache.getData() != null) { + parent_text = parent_text_cache.getData(); + // surround with parentheses if not a simple identifier + if (!parent_text.matches("\\w*")) { + parent_text = '(' + parent_text + ')'; + } + } + if (index >= 0) { + if (index == 0 && deref) { + expr_text = "*" + parent_text; + } + else { + expr_text = parent_text + "[" + index + "]"; + } + } + if (expr_text == null && field != null && field.getData() != null) { + expr_text = parent_text + (deref ? "->" : ".") + field.getData().getName(); + } + if (expr_text == null && base_text.getData() != null) expr_text = base_text.getData(); + } + if (expr_text != null) { + String cast = model.getCastToType(id); + if (cast != null) expr_text = "(" + cast + ")(" + expr_text + ")"; + } + set(null, null, expr_text); + return true; + } + }; + expression = new TCFData<Expression>(channel) { + @Override + protected boolean startDataRetrieval() { + IExpressions exps = launch.getService(IExpressions.class); + if (exps == null) { + set(null, null, null); + return true; + } + String cast = model.getCastToType(id); + if (var_id != null && cast == null) { + if (!var_expression.validate(this)) return false; + Expression exp = null; + if (var_expression.getData() != null) exp = new Expression(var_expression.getData()); + set(null, var_expression.getError(), exp); + return true; + } + if (!base_text.validate(this)) return false; + String e = base_text.getData(); + if (e == null) { + set(null, base_text.getError(), null); + return true; + } + if (cast != null) e = "(" + cast + ")(" + e + ")"; + TCFNode n = getRootExpression().parent; + if (n instanceof TCFNodeStackFrame && ((TCFNodeStackFrame)n).isEmulated()) n = n.parent; + command = exps.create(n.id, null, e, new IExpressions.DoneCreate() { + public void doneCreate(IToken token, Exception error, IExpressions.Expression context) { + Expression e = null; + if (context != null) { + e = new Expression(context); + e.must_be_disposed = true; + } + if (!isDisposed()) set(token, error, e); + else if (e != null) e.dispose(); + } + }); + return false; + } + @Override + public void reset() { + if (isValid() && getData() != null) getData().dispose(); + super.reset(); + } + @Override + public void dispose() { + if (isValid() && getData() != null) getData().dispose(); + super.dispose(); + } + }; + value = new TCFData<IExpressions.Value>(channel) { + @Override + protected boolean startDataRetrieval() { + Boolean b = usePrevValue(this); + if (b == null) return false; + if (b) { + set(null, null, prev_value); + return true; + } + if (!expression.validate(this)) return false; + final Expression exp = expression.getData(); + if (exp == null) { + set(null, expression.getError(), null); + return true; + } + IExpressions exps = launch.getService(IExpressions.class); + command = exps.evaluate(exp.expression.getID(), new IExpressions.DoneEvaluate() { + public void doneEvaluate(IToken token, Exception error, IExpressions.Value value) { + if (error != null) { + Boolean b = usePrevValue(null); + if (b != null && b) { + set(token, null, prev_value); + return; + } + } + set(token, error, value); + } + }); + return false; + } + public void reset() { + super.reset(); + } + }; + type = new TCFData<ISymbols.Symbol>(channel) { + @Override + protected boolean startDataRetrieval() { + String type_id = null; + if (!value.validate(this)) return false; + IExpressions.Value val = value.getData(); + if (val != null) type_id = val.getTypeID(); + if (type_id == null) { + if (!expression.validate(this)) return false; + Expression exp = expression.getData(); + if (exp != null) type_id = exp.expression.getTypeID(); + } + if (type_id == null) { + set(null, value.getError(), null); + return true; + } + TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id); + if (type_cache == null) { + set(null, null, null); + return true; + } + if (!type_cache.validate(this)) return false; + set(null, type_cache.getError(), type_cache.getData()); + return true; + } + }; + string = new TCFData<String>(channel) { + IMemory.MemoryContext mem; + ISymbols.Symbol base_type_data; + BigInteger addr; + byte[] buf; + int size; + int offs; + @Override + protected boolean startDataRetrieval() { + if (addr != null) { + if (mem == null) { + TCFDataCache<TCFNodeExecContext> mem_node_cache = model.searchMemoryContext(parent); + if (mem_node_cache == null) { + set(null, new Exception("Context does not provide memory access"), null); + return true; + } + if (!mem_node_cache.validate(this)) return false; + if (mem_node_cache.getError() != null) { + set(null, mem_node_cache.getError(), null); + return true; + } + TCFNodeExecContext mem_node = mem_node_cache.getData(); + if (mem_node == null) { + set(null, new Exception("Context does not provide memory access"), null); + return true; + } + TCFDataCache<IMemory.MemoryContext> mem_ctx_cache = mem_node.getMemoryContext(); + if (!mem_ctx_cache.validate(this)) return false; + if (mem_ctx_cache.getError() != null) { + set(null, mem_ctx_cache.getError(), null); + return true; + } + mem = mem_ctx_cache.getData(); + if (mem == null) { + set(null, new Exception("Context does not provide memory access"), null); + return true; + } + } + if (size == 0) { + // data is ASCII string + if (buf == null) buf = new byte[256]; + if (offs >= buf.length) { + byte[] tmp = new byte[buf.length * 2]; + System.arraycopy(buf, 0, tmp, 0, buf.length); + buf = tmp; + } + command = mem.get(addr.add(BigInteger.valueOf(offs)), 1, buf, offs, 1, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + set(command, error, null); + } + else if (buf[offs] == 0 || offs >= 2048) { + set(command, null, toASCIIString(buf, 0, offs, '"')); + } + else if (command == token) { + command = null; + offs++; + run(); + } + } + }); + return false; + } + // data is a struct + if (offs != size) { + if (buf == null || buf.length < size) buf = new byte[size]; + command = mem.get(addr, 1, buf, 0, size, 0, new IMemory.DoneMemory() { + public void doneMemory(IToken token, MemoryError error) { + if (error != null) { + set(command, error, null); + } + else if (command == token) { + command = null; + offs = size; + run(); + } + } + }); + return false; + } + StyledStringBuffer bf = new StyledStringBuffer(); + bf.append('{'); + if (!appendCompositeValueText(bf, 1, base_type_data, buf, 0, size, mem.isBigEndian(), this)) return false; + bf.append('}'); + set(null, null, bf.toString()); + return true; + } + if (!type.validate(this)) return false; + ISymbols.Symbol type_data = type.getData(); + if (type_data != null) { + switch (type_data.getTypeClass()) { + case pointer: + case array: + TCFDataCache<ISymbols.Symbol> base_type_cahce = model.getSymbolInfoCache(type_data.getBaseTypeID()); + if (base_type_cahce != null) { + if (!base_type_cahce.validate(this)) return false; + base_type_data = base_type_cahce.getData(); + if (base_type_data != null) { + offs = 0; + size = base_type_data.getSize(); + switch (base_type_data.getTypeClass()) { + case integer: + case cardinal: + if (base_type_data.getSize() != 1) break; + size = 0; // read until character = 0 + case composite: + if (base_type_data.getSize() == 0) break; + if (type_data.getTypeClass() == ISymbols.TypeClass.array && + base_type_data.getTypeClass() == ISymbols.TypeClass.composite) break; + if (!value.validate(this)) return false; + IExpressions.Value v = value.getData(); + if (v != null) { + byte[] data = v.getValue(); + if (type_data.getTypeClass() == ISymbols.TypeClass.array) { + set(null, null, toASCIIString(data, 0, data.length, '"')); + return true; + } + BigInteger a = TCFNumberFormat.toBigInteger(data, 0, data.length, v.isBigEndian(), false); + if (!a.equals(BigInteger.valueOf(0))) { + addr = a; + Protocol.invokeLater(this); + return false; + } + } + } + } + } + break; + case integer: + case cardinal: + if (type_data.getSize() == 1) { + if (!value.validate(this)) return false; + IExpressions.Value v = value.getData(); + if (v != null) { + byte[] data = v.getValue(); + set(null, null, toASCIIString(data, 0, data.length, '\'')); + return true; + } + } + break; + } + } + set(null, null, null); + return true; + } + @Override + public void reset() { + super.reset(); + mem = null; + addr = null; + } + }; + type_name = new TCFData<String>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!type.validate(this)) return false; + if (type.getData() == null) { + if (!value.validate(this)) return false; + IExpressions.Value val = value.getData(); + if (val != null && val.getValue() != null) { + String s = getTypeName(val.getTypeClass(), val.getValue().length); + if (s != null) { + set(null, null, s); + return true; + } + } + } + StringBuffer bf = new StringBuffer(); + if (!getTypeName(bf, type, this)) return false; + set(null, null, bf.toString()); + return true; + } + }; + children = new TCFChildrenSubExpressions(this, 0, 0, 0); + } + + private TCFNodeExpression getRootExpression() { + TCFNode n = this; + while (n.parent instanceof TCFNodeExpression || n.parent instanceof TCFNodeArrayPartition) n = n.parent; + return (TCFNodeExpression)n; + } + + private void postAllChangedDelta() { + TCFNodeExpression n = getRootExpression(); + for (TCFModelProxy p : model.getModelProxies()) { + String id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id) && n.script != null || + TCFModel.ID_EXPRESSION_HOVER.equals(id) && n.script != null || + IDebugUIConstants.ID_VARIABLE_VIEW.equals(id) && n.script == null) { + p.addDelta(this, IModelDelta.STATE | IModelDelta.CONTENT); + } + } + } + + void onSuspended() { + prev_value = next_value; + if (expression.isValid() && expression.getError() != null) expression.reset(); + value.reset(); + type.reset(); + type_name.reset(); + string.reset(); + children.onSuspended(); + // No need to post delta: parent posted CONTENT + } + + void onRegisterValueChanged() { + value.reset(); + type.reset(); + type_name.reset(); + string.reset(); + children.onRegisterValueChanged(); + postAllChangedDelta(); + } + + void onMemoryChanged() { + value.reset(); + type.reset(); + type_name.reset(); + string.reset(); + children.onMemoryChanged(); + if (parent instanceof TCFNodeExpression) return; + if (parent instanceof TCFNodeArrayPartition) return; + postAllChangedDelta(); + } + + void onMemoryMapChanged() { + value.reset(); + type.reset(); + type_name.reset(); + string.reset(); + children.onMemoryMapChanged(); + if (parent instanceof TCFNodeExpression) return; + if (parent instanceof TCFNodeArrayPartition) return; + postAllChangedDelta(); + } + + void onValueChanged() { + value.reset(); + type.reset(); + type_name.reset(); + string.reset(); + children.onValueChanged(); + postAllChangedDelta(); + } + + public void onCastToTypeChanged() { + expression.cancel(); + value.cancel(); + type.cancel(); + type_name.cancel(); + string.cancel(); + expression_text.cancel(); + children.onCastToTypeChanged(); + postAllChangedDelta(); + } + + public String getScript() { + return script; + } + + String getFieldID() { + return field_id; + } + + String getRegisterID() { + return reg_id; + } + + int getIndex() { + return index; + } + + boolean isDeref() { + return deref; + } + + void setSortPosition(int sort_pos) { + this.sort_pos = sort_pos; + } + + void setEnabled(boolean enabled) { + if (this.enabled == enabled) return; + this.enabled = enabled; + postAllChangedDelta(); + } + + /** + * Get expression properties cache. + * The cache is empty is the expression does not represent a variable. + * @return The expression properties cache. + */ + public TCFDataCache<IExpressions.Expression> getVariable() { + return var_expression; + } + + /** + * Get expression value cache. + * @return The expression value cache. + */ + public TCFDataCache<IExpressions.Value> getValue() { + return value; + } + + /** + * Get expression type cache. + * @return The expression type cache. + */ + public TCFDataCache<ISymbols.Symbol> getType() { + return type; + } + + /** + * Get human readable expression script, + * including type cast, and using variable names instead of IDs. + */ + public TCFDataCache<String> getExpressionText() { + return expression_text; + } + + private Boolean usePrevValue(Runnable done) { + // Check if view should show old value. + // Old value is shown if context is running or + // stack trace does not contain expression parent frame. + // Return null if waiting for cache update. + if (prev_value == null) return false; + if (parent instanceof TCFNodeStackFrame) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent.parent; + TCFDataCache<TCFContextState> state_cache = exe.getState(); + if (!state_cache.validate(done)) return null; + TCFContextState state = state_cache.getData(); + if (state == null || !state.is_suspended) return true; + TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace(); + if (!stack_trace_cache.validate(done)) return null; + if (stack_trace_cache.getData().get(parent.id) == null) return true; + } + else if (parent instanceof TCFNodeExecContext) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent; + TCFDataCache<TCFContextState> state_cache = exe.getState(); + if (!state_cache.validate(done)) return null; + TCFContextState state = state_cache.getData(); + if (state == null || !state.is_suspended) return true; + } + return false; + } + + private String getTypeName(ISymbols.TypeClass type_class, int size) { + String s = null; + switch (type_class) { + case integer: + if (size == 0) s = "<Void>"; + else s = "<Integer-" + (size * 8) + ">"; + break; + case cardinal: + if (size == 0) s = "<Void>"; + else s = "<Unsigned-" + (size * 8) + ">"; + break; + case real: + if (size == 0) s = "<Void>"; + else s = "<Float-" + (size * 8) + ">"; + break; + } + return s; + } + + private boolean getTypeName(StringBuffer bf, TCFDataCache<ISymbols.Symbol> type_cache, Runnable done) { + String name = null; + for (;;) { + String s = null; + boolean get_base_type = false; + if (!type_cache.validate(done)) return false; + ISymbols.Symbol type_symbol = type_cache.getData(); + if (type_symbol != null) { + int flags = type_symbol.getFlags(); + s = type_symbol.getName(); + if (s != null && type_symbol.getTypeClass() == ISymbols.TypeClass.composite) { + if ((flags & ISymbols.SYM_FLAG_UNION_TYPE) != 0) s = "union " + s; + else if ((flags & ISymbols.SYM_FLAG_CLASS_TYPE) != 0) s = "class " + s; + else if ((flags & ISymbols.SYM_FLAG_INTERFACE_TYPE) != 0) s = "interface " + s; + else s = "struct " + s; + } + if (s == null) s = getTypeName(type_symbol.getTypeClass(), type_symbol.getSize()); + if (s == null) { + switch (type_symbol.getTypeClass()) { + case pointer: + s = "*"; + if ((flags & ISymbols.SYM_FLAG_REFERENCE) != 0) s = "&"; + get_base_type = true; + break; + case member_pointer: + { + String id = type_symbol.getContainerID(); + if (id != null) { + TCFDataCache<ISymbols.Symbol> cls_cache = model.getSymbolInfoCache(id); + if (!cls_cache.validate(done)) return false; + ISymbols.Symbol cls_data = cls_cache.getData(); + if (cls_data != null) { + String cls_name = cls_data.getName(); + if (cls_name != null) s = cls_name + "::*"; + } + } + if (s == null) s = "::*"; + } + get_base_type = true; + break; + case array: + s = "[" + type_symbol.getLength() + "]"; + get_base_type = true; + break; + case composite: + s = "<Structure>"; + break; + case function: + { + TCFDataCache<String[]> children_cache = model.getSymbolChildrenCache(type_symbol.getID()); + if (!children_cache.validate(done)) return false; + if (children_cache.getError() == null) { + String[] children = children_cache.getData(); + if (children != null) { + StringBuffer args = new StringBuffer(); + if (name != null) { + args.append('('); + args.append(name); + args.append(')'); + name = null; + } + args.append('('); + for (String id : children) { + if (id != children[0]) args.append(','); + if (!getTypeName(args, model.getSymbolInfoCache(id), done)) return false; + } + args.append(')'); + s = args.toString(); + get_base_type = true; + break; + } + } + } + s = "<Function>"; + break; + } + } + if (s != null) { + if ((flags & ISymbols.SYM_FLAG_VOLATILE_TYPE) != 0) s = "volatile " + s; + if ((flags & ISymbols.SYM_FLAG_CONST_TYPE) != 0) s = "const " + s; + } + } + if (s == null) { + name = "N/A"; + break; + } + if (name == null) name = s; + else if (!get_base_type) name = s + " " + name; + else name = s + name; + + if (!get_base_type) break; + + type_cache = model.getSymbolInfoCache(type_symbol.getBaseTypeID()); + if (type_cache == null) { + name = "N/A"; + break; + } + } + bf.append(name); + return true; + } + + private String toASCIIString(byte[] data, int offs, int size, char quote_char) { + StringBuffer bf = new StringBuffer(); + bf.append(quote_char); + for (int i = 0; i < size; i++) { + int ch = data[offs + i] & 0xff; + if (ch >= ' ' && ch < 0x7f) { + bf.append((char)ch); + } + else { + switch (ch) { + case '\r': bf.append("\\r"); break; + case '\n': bf.append("\\n"); break; + case '\b': bf.append("\\b"); break; + case '\t': bf.append("\\t"); break; + case '\f': bf.append("\\f"); break; + default: + bf.append('\\'); + bf.append((char)('0' + ch / 64)); + bf.append((char)('0' + ch / 8 % 8)); + bf.append((char)('0' + ch % 8)); + } + } + } + if (data.length <= offs + size || data[offs + size] == 0) bf.append(quote_char); + else bf.append("..."); + return bf.toString(); + } + + private String toNumberString(int radix, ISymbols.TypeClass t, byte[] data, int offs, int size, boolean big_endian) { + if (size <= 0 || size > 16) return ""; + if (radix != 16) { + switch (t) { + case array: + case composite: + return ""; + } + } + String s = null; + if (data == null) s = "N/A"; + if (s == null && radix == 10) { + if (t != null) { + switch (t) { + case integer: + s = TCFNumberFormat.toBigInteger(data, offs, size, big_endian, true).toString(); + break; + case real: + s = TCFNumberFormat.toFPString(data, offs, size, big_endian); + break; + } + } + } + if (s == null) { + s = TCFNumberFormat.toBigInteger(data, offs, size, big_endian, false).toString(radix); + switch (radix) { + case 8: + if (!s.startsWith("0")) s = "0" + s; + break; + case 16: + if (s.length() < size * 2) { + StringBuffer bf = new StringBuffer(); + while (bf.length() + s.length() < size * 2) bf.append('0'); + bf.append(s); + s = bf.toString(); + } + break; + } + } + if (s == null) s = "N/A"; + return s; + } + + private String toNumberString(int radix) { + String s = null; + IExpressions.Value val = value.getData(); + if (val != null) { + byte[] data = val.getValue(); + if (data != null) { + ISymbols.TypeClass t = val.getTypeClass(); + if (t == ISymbols.TypeClass.unknown && type.getData() != null) t = type.getData().getTypeClass(); + s = toNumberString(radix, t, data, 0, data.length, val.isBigEndian()); + } + } + if (s == null) s = "N/A"; + return s; + } + + private void setLabel(ILabelUpdate result, String name, int col, int radix) { + String s = toNumberString(radix); + if (name == null) { + result.setLabel(s, col); + } + else { + result.setLabel(name + " = " + s, col); + } + } + + private boolean isValueChanged(IExpressions.Value x, IExpressions.Value y) { + if (x == null || y == null) return false; + byte[] xb = x.getValue(); + byte[] yb = y.getValue(); + if (xb == null || yb == null) return false; + if (xb.length != yb.length) return true; + for (int i = 0; i < xb.length; i++) { + if (xb[i] != yb[i]) return true; + } + return false; + } + + @Override + protected boolean getData(ILabelUpdate result, Runnable done) { + if (enabled || script == null) { + TCFDataCache<ISymbols.Symbol> field = model.getSymbolInfoCache(field_id); + TCFDataCache<?> pending = null; + if (field != null && !field.validate()) pending = field; + if (reg_id != null && !expression_text.validate(done)) pending = expression_text; + if (!var_expression.validate()) pending = var_expression; + if (!base_text.validate()) pending = base_text; + if (!value.validate()) pending = value; + if (!type.validate()) pending = type; + if (pending != null) { + pending.wait(done); + return false; + } + String name = null; + if (index >= 0) { + if (index == 0 && deref) { + name = "*"; + } + else { + name = "[" + index + "]"; + } + } + if (name == null && field != null && field.getData() != null) name = field.getData().getName(); + if (name == null && reg_id != null && expression_text.getData() != null) name = expression_text.getData(); + if (name == null && var_expression.getData() != null) { + TCFDataCache<ISymbols.Symbol> var = model.getSymbolInfoCache(var_expression.getData().getSymbolID()); + if (var != null) { + if (!var.validate(done)) return false; + ISymbols.Symbol var_data = var.getData(); + if (var_data != null) { + name = var_data.getName(); + if (name == null && var_data.getFlag(ISymbols.SYM_FLAG_VARARG)) name = "<VarArg>"; + if (name == null) name = "<" + var_data.getID() + ">"; + } + } + } + if (name == null && base_text.getData() != null) name = base_text.getData(); + if (name != null) { + String cast = model.getCastToType(id); + if (cast != null) name = "(" + cast + ")(" + name + ")"; + } + Throwable error = base_text.getError(); + if (error == null) error = value.getError(); + String[] cols = result.getColumnIds(); + if (error != null) { + if (cols == null || cols.length <= 1) { + result.setForeground(rgb_error, 0); + result.setLabel(name + ": N/A", 0); + } + else { + for (int i = 0; i < cols.length; i++) { + String c = cols[i]; + if (c.equals(TCFColumnPresentationExpression.COL_NAME)) { + result.setLabel(name, i); + } + else if (c.equals(TCFColumnPresentationExpression.COL_TYPE)) { + if (!type_name.validate(done)) return false; + result.setLabel(type_name.getData(), i); + } + else { + result.setForeground(rgb_error, i); + result.setLabel("N/A", i); + } + } + } + } + else { + if (cols == null) { + setLabel(result, name, 0, 16); + } + else { + for (int i = 0; i < cols.length; i++) { + String c = cols[i]; + if (c.equals(TCFColumnPresentationExpression.COL_NAME)) { + result.setLabel(name, i); + } + else if (c.equals(TCFColumnPresentationExpression.COL_TYPE)) { + if (!type_name.validate(done)) return false; + result.setLabel(type_name.getData(), i); + } + else if (c.equals(TCFColumnPresentationExpression.COL_HEX_VALUE)) { + setLabel(result, null, i, 16); + } + else if (c.equals(TCFColumnPresentationExpression.COL_DEC_VALUE)) { + setLabel(result, null, i, 10); + } + } + } + } + next_value = value.getData(); + if (isValueChanged(prev_value, next_value)) { + result.setBackground(rgb_highlight, 0); + if (cols != null) { + for (int i = 1; i < cols.length; i++) { + result.setBackground(rgb_highlight, i); + } + } + } + ISymbols.TypeClass type_class = ISymbols.TypeClass.unknown; + ISymbols.Symbol type_symbol = type.getData(); + if (type_symbol != null) { + type_class = type_symbol.getTypeClass(); + } + switch (type_class) { + case pointer: + result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE_POINTER), 0); + break; + case composite: + case array: + result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE_AGGREGATE), 0); + break; + default: + result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_VARIABLE), 0); + } + } + else { + String[] cols = result.getColumnIds(); + if (cols == null || cols.length <= 1) { + result.setForeground(rgb_disabled, 0); + result.setLabel(script, 0); + } + else { + for (int i = 0; i < cols.length; i++) { + String c = cols[i]; + if (c.equals(TCFColumnPresentationExpression.COL_NAME)) { + result.setForeground(rgb_disabled, i); + result.setLabel(script, i); + } + } + } + } + return true; + } + + private void appendErrorText(StringBuffer bf, Throwable error) { + if (error == null) return; + bf.append("Exception: "); + bf.append(TCFModel.getErrorMessage(error, true)); + } + + private boolean appendArrayValueText(StyledStringBuffer bf, int level, ISymbols.Symbol type, + byte[] data, int offs, int size, boolean big_endian, Runnable done) { + assert offs + size <= data.length; + int length = type.getLength(); + bf.append('['); + if (length > 0) { + int elem_size = size / length; + for (int n = 0; n < length; n++) { + if (n >= 100) { + bf.append("..."); + break; + } + if (n > 0) bf.append(", "); + if (!appendValueText(bf, level + 1, type.getBaseTypeID(), + data, offs + n * elem_size, elem_size, big_endian, done)) return false; + } + } + bf.append(']'); + return true; + } + + private boolean appendCompositeValueText(StyledStringBuffer bf, int level, ISymbols.Symbol type, + byte[] data, int offs, int size, boolean big_endian, Runnable done) { + TCFDataCache<String[]> children_cache = model.getSymbolChildrenCache(type.getID()); + if (children_cache == null) { + bf.append("..."); + return true; + } + if (!children_cache.validate(done)) return false; + String[] children_data = children_cache.getData(); + if (children_data == null) { + bf.append("..."); + return true; + } + int cnt = 0; + TCFDataCache<?> pending = null; + for (String id : children_data) { + TCFDataCache<ISymbols.Symbol> field_cache = model.getSymbolInfoCache(id); + if (!field_cache.validate()) { + pending = field_cache; + continue; + } + ISymbols.Symbol field_data = field_cache.getData(); + if (field_data == null) continue; + if (field_data.getSymbolClass() != ISymbols.SymbolClass.reference) continue; + String name = field_data.getName(); + int f_offs = field_data.getOffset(); + int f_size = field_data.getSize(); + if (name == null) { + // Super-class members + if (offs + f_offs + f_size > data.length) { + continue; + } + else { + StyledStringBuffer bf1 = new StyledStringBuffer(); + if (!appendCompositeValueText(bf1, level, field_data, data, + offs + f_offs, f_size, big_endian, done)) return false; + if (bf1.length() > 0) { + if (cnt > 0) bf.append(", "); + bf.append(bf1); + cnt++; + } + } + } + else { + if (cnt > 0) bf.append(", "); + bf.append(name); + bf.append('='); + if (offs + f_offs + f_size > data.length) { + bf.append('?'); + } + else { + if (!appendValueText(bf, level + 1, field_data.getTypeID(), + data, offs + f_offs, f_size, big_endian, done)) return false; + } + cnt++; + } + } + if (pending == null) return true; + pending.wait(done); + return false; + } + + private boolean appendValueText(StyledStringBuffer bf, int level, String type_id, + byte[] data, int offs, int size, boolean big_endian, Runnable done) { + if (data == null) return true; + ISymbols.Symbol type_data = null; + if (type_id != null) { + TCFDataCache<ISymbols.Symbol> type_cache = model.getSymbolInfoCache(type_id); + if (!type_cache.validate(done)) return false; + type_data = type_cache.getData(); + } + if (type_data == null) { + ISymbols.TypeClass type_class = ISymbols.TypeClass.unknown; + if (!value.validate(done)) return false; + if (value.getData() != null) type_class = value.getData().getTypeClass(); + if (level == 0) { + assert offs == 0; + assert size == data.length; + String s = getTypeName(type_class, size); + if (s == null) s = "not available"; + bf.append("Type: ", SWT.BOLD); + bf.append(s); + bf.append('\n'); + bf.append("Size: ", SWT.BOLD); + bf.append(size); + bf.append(size == 1 ? " byte\n" : " bytes\n"); + if (size > 0) { + if (type_class == ISymbols.TypeClass.integer || type_class == ISymbols.TypeClass.real) { + bf.append("Dec: ", SWT.BOLD); + bf.append(toNumberString(10, type_class, data, offs, size, big_endian)); + bf.append("\n"); + } + bf.append("Hex: ", SWT.BOLD); + bf.append(toNumberString(16, type_class, data, offs, size, big_endian)); + bf.append("\n"); + } + } + else if (type_class == ISymbols.TypeClass.integer || type_class == ISymbols.TypeClass.real) { + bf.append(toNumberString(10, type_class, data, offs, size, big_endian)); + } + else { + bf.append(toNumberString(16, type_class, data, offs, size, big_endian)); + } + return true; + } + if (level == 0) { + if (!string.validate(done)) return false; + Throwable e = string.getError(); + String s = string.getData(); + if (s != null) { + bf.append(s); + bf.append("\n"); + } + else if (e != null) { + bf.append("Cannot read pointed value: ", SWT.BOLD, null, rgb_error); + bf.append(TCFModel.getErrorMessage(e, true), SWT.ITALIC, null, rgb_error); + } + } + if (type_data.getSize() > 0) { + ISymbols.TypeClass type_class = type_data.getTypeClass(); + switch (type_class) { + case enumeration: + case integer: + case cardinal: + case real: + if (level == 0) { + bf.append("Dec: ", SWT.BOLD); + bf.append(toNumberString(10, type_class, data, offs, size, big_endian)); + bf.append("\n"); + bf.append("Oct: ", SWT.BOLD); + bf.append(toNumberString(8, type_class, data, offs, size, big_endian)); + bf.append("\n"); + bf.append("Hex: ", SWT.BOLD); + bf.append(toNumberString(16, type_class, data, offs, size, big_endian)); + bf.append("\n"); + } + else if (type_data.getTypeClass() == ISymbols.TypeClass.cardinal) { + bf.append("0x"); + bf.append(toNumberString(16, type_class, data, offs, size, big_endian)); + } + else { + bf.append(toNumberString(10, type_class, data, offs, size, big_endian)); + } + break; + case pointer: + case function: + case member_pointer: + if (level == 0) { + bf.append("Oct: ", SWT.BOLD); + bf.append(toNumberString(8, type_class, data, offs, size, big_endian)); + bf.append("\n"); + bf.append("Hex: ", SWT.BOLD); + bf.append(toNumberString(16, type_class, data, offs, size, big_endian)); + bf.append("\n"); + } + else { + bf.append("0x"); + bf.append(toNumberString(16, type_class, data, offs, size, big_endian)); + } + break; + case array: + if (!appendArrayValueText(bf, level, type_data, data, offs, size, big_endian, done)) return false; + if (level == 0) bf.append("\n"); + break; + case composite: + bf.append('{'); + if (!appendCompositeValueText(bf, level, type_data, data, offs, size, big_endian, done)) return false; + bf.append('}'); + if (level == 0) bf.append("\n"); + break; + } + } + if (level == 0) { + if (!type_name.validate(done)) return false; + String nm = type_name.getData(); + if (nm != null) { + bf.append("Type: ", SWT.BOLD); + bf.append(nm); + bf.append("\n"); + } + bf.append("Size: ", SWT.BOLD); + bf.append(type_data.getSize()); + bf.append(type_data.getSize() == 1 ? " byte\n" : " bytes\n"); + } + return true; + } + + private String getRegisterName(String reg_id, Runnable done) { + String name = reg_id; + TCFDataCache<?> pending = null; + TCFNodeRegister reg_node = null; + LinkedList<TCFChildren> queue = new LinkedList<TCFChildren>(); + TCFNode n = parent; + while (n != null) { + if (n instanceof TCFNodeStackFrame) { + queue.add(((TCFNodeStackFrame)n).getRegisters()); + } + if (n instanceof TCFNodeExecContext) { + queue.add(((TCFNodeExecContext)n).getRegisters()); + break; + } + n = n.parent; + } + while (!queue.isEmpty()) { + TCFChildren reg_list = queue.removeFirst(); + if (!reg_list.validate()) { + pending = reg_list; + } + else { + Map<String,TCFNode> reg_map = reg_list.getData(); + if (reg_map != null) { + reg_node = (TCFNodeRegister)reg_map.get(reg_id); + if (reg_node != null) break; + for (TCFNode node : reg_map.values()) { + queue.add(((TCFNodeRegister)node).getChildren()); + } + } + } + } + if (pending != null) { + pending.wait(done); + return null; + } + if (reg_node != null) { + TCFDataCache<IRegisters.RegistersContext> reg_ctx_cache = reg_node.getContext(); + if (!reg_ctx_cache.validate(done)) return null; + IRegisters.RegistersContext reg_ctx_data = reg_ctx_cache.getData(); + if (reg_ctx_data != null && reg_ctx_data.getName() != null) name = reg_ctx_data.getName(); + } + return name; + } + + public boolean getDetailText(StyledStringBuffer bf, Runnable done) { + if (!enabled) { + bf.append("Disabled"); + return true; + } + if (!expression.validate(done)) return false; + if (!value.validate(done)) return false; + int pos = bf.length(); + appendErrorText(bf.getStringBuffer(), expression.getError()); + if (bf.length() == pos) appendErrorText(bf.getStringBuffer(), value.getError()); + if (bf.length() > pos) { + bf.append(pos, SWT.ITALIC, null, rgb_error); + } + else { + IExpressions.Value v = value.getData(); + if (v != null) { + byte[] data = v.getValue(); + if (data != null) { + boolean big_endian = v.isBigEndian(); + if (!appendValueText(bf, 0, v.getTypeID(), + data, 0, data.length, big_endian, done)) return false; + } + String reg_id = v.getRegisterID(); + if (reg_id != null) { + String nm = getRegisterName(reg_id, done); + if (nm == null) return false; + bf.append("Register: ", SWT.BOLD); + bf.append(nm); + bf.append('\n'); + } + Number addr = v.getAddress(); + if (addr != null) { + BigInteger i = JSON.toBigInteger(addr); + bf.append("Address: ", SWT.BOLD); + bf.append("0x"); + bf.append(i.toString(16)); + bf.append('\n'); + } + } + } + return true; + } + + public String getValueText(boolean add_error_text, Runnable done) { + if (!expression.validate(done)) return null; + if (!value.validate(done)) return null; + StyledStringBuffer bf = new StyledStringBuffer(); + IExpressions.Value v = value.getData(); + if (v != null) { + byte[] data = v.getValue(); + if (data != null) { + boolean big_endian = v.isBigEndian(); + if (!appendValueText(bf, 1, v.getTypeID(), + data, 0, data.length, big_endian, done)) return null; + } + } + if (add_error_text) { + if (bf.length() == 0 && expression.getError() != null) { + bf.append(TCFModel.getErrorMessage(expression.getError(), false)); + } + if (bf.length() == 0 && value.getError() != null) { + bf.append(TCFModel.getErrorMessage(value.getError(), false)); + } + } + return bf.toString(); + } + + @Override + protected boolean getData(IChildrenCountUpdate result, Runnable done) { + if (enabled) { + if (!children.validate(done)) return false; + result.setChildCount(children.size()); + } + else { + result.setChildCount(0); + } + return true; + } + + @Override + protected boolean getData(IChildrenUpdate result, Runnable done) { + if (!enabled) return true; + return children.getData(result, done); + } + + @Override + protected boolean getData(IHasChildrenUpdate result, Runnable done) { + if (enabled) { + if (!children.validate(done)) return false; + result.setHasChilren(children.size() > 0); + } + else { + result.setHasChilren(false); + } + return true; + } + + @Override + public int compareTo(TCFNode n) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (sort_pos < e.sort_pos) return -1; + if (sort_pos > e.sort_pos) return +1; + return 0; + } + + public CellEditor getCellEditor(IPresentationContext context, String column_id, Object element, Composite parent) { + assert element == this; + if (TCFColumnPresentationExpression.COL_NAME.equals(column_id) && script != null) { + return new TextCellEditor(parent); + } + if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(column_id)) { + return new TextCellEditor(parent); + } + if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(column_id)) { + return new TextCellEditor(parent); + } + return null; + } + + private static final ICellModifier cell_modifier = new ICellModifier() { + + public boolean canModify(Object element, final String property) { + final TCFNodeExpression node = (TCFNodeExpression)element; + return new TCFTask<Boolean>() { + public void run() { + if (TCFColumnPresentationExpression.COL_NAME.equals(property)) { + done(node.script != null); + return; + } + if (node.enabled) { + if (!node.expression.validate(this)) return; + if (node.expression.getData() != null && node.expression.getData().expression.canAssign()) { + if (!node.value.validate(this)) return; + if (!node.type.validate(this)) return; + if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) { + done(TCFNumberFormat.isValidHexNumber(node.toNumberString(16)) == null); + return; + } + if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) { + done(TCFNumberFormat.isValidDecNumber(true, node.toNumberString(10)) == null); + return; + } + } + } + done(Boolean.FALSE); + } + }.getE(); + } + + public Object getValue(Object element, final String property) { + final TCFNodeExpression node = (TCFNodeExpression)element; + return new TCFTask<String>() { + public void run() { + if (TCFColumnPresentationExpression.COL_NAME.equals(property)) { + done(node.script); + return; + } + if (!node.value.validate(this)) return; + if (node.value.getData() != null) { + if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) { + done(node.toNumberString(16)); + return; + } + if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) { + done(node.toNumberString(10)); + return; + } + } + done(null); + } + }.getE(); + } + + public void modify(Object element, final String property, final Object value) { + if (value == null) return; + final TCFNodeExpression node = (TCFNodeExpression)element; + new TCFTask<Boolean>() { + public void run() { + try { + if (TCFColumnPresentationExpression.COL_NAME.equals(property)) { + if (!node.script.equals(value)) { + IExpressionManager m = DebugPlugin.getDefault().getExpressionManager(); + for (final IExpression e : m.getExpressions()) { + if (node.script.equals(e.getExpressionText())) m.removeExpression(e); + } + IExpression e = m.newWatchExpression((String)value); + m.addExpression(e); + } + done(Boolean.TRUE); + return; + } + if (!node.expression.validate(this)) return; + if (node.expression.getData() != null) { + IExpressions.Expression exp = node.expression.getData().expression; + if (exp.canAssign()) { + byte[] bf = null; + int size = exp.getSize(); + boolean is_float = false; + boolean big_endian = false; + boolean signed = false; + if (!node.value.validate(this)) return; + IExpressions.Value eval = node.value.getData(); + if (eval != null) { + switch(eval.getTypeClass()) { + case real: + is_float = true; + case integer: + signed = true; + break; + } + big_endian = eval.isBigEndian(); + size = eval.getValue().length; + } + String input = (String)value; + String error = null; + if (TCFColumnPresentationExpression.COL_HEX_VALUE.equals(property)) { + error = TCFNumberFormat.isValidHexNumber(input); + if (error == null) bf = TCFNumberFormat.toByteArray(input, 16, false, size, signed, big_endian); + } + else if (TCFColumnPresentationExpression.COL_DEC_VALUE.equals(property)) { + error = TCFNumberFormat.isValidDecNumber(is_float, input); + if (error == null) bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, signed, big_endian); + } + if (error != null) throw new Exception("Invalid value: " + value, new Exception(error)); + if (bf != null) { + IExpressions exps = node.launch.getService(IExpressions.class); + exps.assign(exp.getID(), bf, new IExpressions.DoneAssign() { + public void doneAssign(IToken token, Exception error) { + node.getRootExpression().onValueChanged(); + if (error != null) { + node.model.showMessageBox("Cannot modify element value", error); + done(Boolean.FALSE); + } + else { + done(Boolean.TRUE); + } + } + }); + return; + } + } + } + done(Boolean.FALSE); + } + catch (Throwable x) { + node.model.showMessageBox("Cannot modify element value", x); + done(Boolean.FALSE); + } + } + }.getE(); + } + }; + + public ICellModifier getCellModifier(IPresentationContext context, Object element) { + assert element == this; + return cell_modifier; + } + + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + if (script != null) { + if (adapter == IExpression.class) { + IExpressionManager m = DebugPlugin.getDefault().getExpressionManager(); + for (final IExpression e : m.getExpressions()) { + if (script.equals(e.getExpressionText())) return e; + } + } + if (adapter == IWatchExpression.class) { + IExpressionManager m = DebugPlugin.getDefault().getExpressionManager(); + for (final IExpression e : m.getExpressions()) { + if (e instanceof IWatchExpression && script.equals(e.getExpressionText())) return e; + } + } + } + return super.getAdapter(adapter); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java new file mode 100644 index 000000000..f5060c61c --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeLaunch.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.tcf.services.IMemory; +import org.eclipse.tcf.services.IRunControl; + + +public class TCFNodeLaunch extends TCFNode implements ISymbolOwner { + + private final TCFChildrenExecContext children; + private final TCFChildren filtered_children; + private final Map<String,TCFNodeSymbol> symbols = new HashMap<String,TCFNodeSymbol>(); + + TCFNodeLaunch(final TCFModel model) { + super(model); + children = new TCFChildrenExecContext(this); + filtered_children = new TCFChildren(this) { + @Override + protected boolean startDataRetrieval() { + Set<String> filter = launch.getContextFilter(); + if (filter == null) { + if (!children.validate(this)) return false; + set(null, children.getError(), children.getData()); + return true; + } + Map<String,TCFNode> nodes = new HashMap<String,TCFNode>(); + for (String id : filter) { + if (!model.createNode(id, this)) return false; + if (isValid()) { + // Ignore invalid IDs + reset(); + } + else { + nodes.put(id, model.getNode(id)); + } + } + set(null, null, nodes); + return true; + } + @Override + public void dispose() { + getNodes().clear(); + super.dispose(); + } + }; + } + + @Override + void dispose() { + ArrayList<TCFNodeSymbol> l = new ArrayList<TCFNodeSymbol>(symbols.values()); + for (TCFNodeSymbol s : l) s.dispose(); + assert symbols.size() == 0; + super.dispose(); + } + + @Override + protected boolean getData(IChildrenCountUpdate result, Runnable done) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(result.getPresentationContext().getId())) { + if (!filtered_children.validate(done)) return false; + result.setChildCount(filtered_children.size()); + } + else { + result.setChildCount(0); + } + return true; + } + + @Override + protected boolean getData(IChildrenUpdate result, Runnable done) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(result.getPresentationContext().getId())) { + return filtered_children.getData(result, done); + } + return true; + } + + @Override + protected boolean getData(IHasChildrenUpdate result, Runnable done) { + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(result.getPresentationContext().getId())) { + if (!filtered_children.validate(done)) return false; + result.setHasChilren(filtered_children.size() > 0); + } + else { + result.setHasChilren(false); + } + return true; + } + + void onContextAdded(IRunControl.RunControlContext context) { + children.onContextAdded(context); + } + + void onContextAdded(IMemory.MemoryContext context) { + children.onContextAdded(context); + } + + void onAnyContextSuspendedOrChanged() { + for (TCFNodeSymbol s : symbols.values()) s.onMemoryMapChanged(); + } + + void onAnyContextAddedOrRemoved() { + filtered_children.reset(); + } + + public void addSymbol(TCFNodeSymbol s) { + assert symbols.get(s.id) == null; + symbols.put(s.id, s); + } + + public void removeSymbol(TCFNodeSymbol s) { + assert symbols.get(s.id) == s; + symbols.remove(s.id); + } + + public TCFChildren getChildren() { + return children; + } + + public TCFChildren getFilteredChildren() { + return filtered_children; + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java new file mode 100644 index 000000000..dd7d32d24 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeModule.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.util.Map; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tcf.core.ErrorReport; +import org.eclipse.tcf.internal.debug.model.TCFSymFileRef; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.JSON; +import org.eclipse.tcf.services.IMemoryMap; +import org.eclipse.tcf.util.TCFDataCache; + +/** + * A node representing a memory region (module). + */ +public class TCFNodeModule extends TCFNode implements IDetailsProvider { + + private IMemoryMap.MemoryRegion region; + + private static final RGB + rgb_error = new RGB(192, 0, 0); + + protected TCFNodeModule(TCFNode parent, String id) { + super(parent, id); + } + + void setRegion(IMemoryMap.MemoryRegion region) { + this.region = region; + } + + @Override + protected boolean getData(ILabelUpdate update, Runnable done) { + String[] col_ids = update.getColumnIds(); + if (col_ids == null) { + update.setLabel(region.getFileName(), 0); + } + else { + for (int i=0; i < col_ids.length; ++i) { + String col_id = col_ids[i]; + if (TCFColumnPresentationModules.COL_NAME.equals(col_id)) { + update.setLabel(region.getFileName(), i); + } + else if (TCFColumnPresentationModules.COL_ADDRESS.equals(col_id)) { + update.setLabel(toHexString(region.getAddress()), i); + } + else if (TCFColumnPresentationModules.COL_SIZE.equals(col_id)) { + update.setLabel(toHexString(region.getSize()), i); + } + else if (TCFColumnPresentationModules.COL_FLAGS.equals(col_id)) { + update.setLabel(getFlagsLabel(region.getFlags()), i); + } + else if (TCFColumnPresentationModules.COL_OFFSET.equals(col_id)) { + update.setLabel(toHexString(region.getOffset()), i); + } + else if (TCFColumnPresentationModules.COL_SECTION.equals(col_id)) { + String sectionName = region.getSectionName(); + update.setLabel(sectionName != null ? sectionName : "", i); + } + } + } + update.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_MEMORY_MAP), 0); + return true; + } + + public boolean getDetailText(StyledStringBuffer bf, Runnable done) { + String file_name = region.getFileName(); + if (file_name != null) { + bf.append("File name: ", SWT.BOLD).append(file_name).append('\n'); + TCFNodeExecContext exe = (TCFNodeExecContext)parent; + TCFDataCache<TCFSymFileRef> sym_cache = exe.getSymFileInfo(JSON.toBigInteger(region.getAddress())); + if (sym_cache != null) { + if (!sym_cache.validate(done)) return false; + TCFSymFileRef sym_data = sym_cache.getData(); + if (sym_data != null) { + if (sym_data.props != null) { + String sym_file_name = (String)sym_data.props.get("FileName"); + if (sym_file_name != null) bf.append("Symbol file name: ", SWT.BOLD).append(sym_file_name).append('\n'); + @SuppressWarnings("unchecked") + Map<String,Object> map = (Map<String,Object>)sym_data.props.get("FileError"); + if (map != null) { + String msg = TCFModel.getErrorMessage(new ErrorReport("", map), false); + bf.append("Symbol file error: ", SWT.BOLD).append(msg, SWT.ITALIC, null, rgb_error).append('\n'); + } + } + if (sym_data.error != null) bf.append("Symbol file error: ", SWT.BOLD).append( + TCFModel.getErrorMessage(sym_data.error, false), + SWT.ITALIC, null, rgb_error).append('\n'); + } + } + String section = region.getSectionName(); + if (section != null) bf.append("File section: ", SWT.BOLD).append(section).append('\n'); + else bf.append("File offset: ", SWT.BOLD).append(toHexString(region.getOffset())).append('\n'); + } + bf.append("Address: ", SWT.BOLD).append(toHexString(region.getAddress())).append('\n'); + bf.append("Size: ", SWT.BOLD).append(toHexString(region.getSize())).append('\n'); + bf.append("Flags: ", SWT.BOLD).append(getFlagsLabel(region.getFlags())).append('\n'); + return true; + } + + private String toHexString(Number address) { + if (address == null) return ""; + BigInteger addr = JSON.toBigInteger(address); + String s = addr.toString(16); + int sz = s.length() <= 8 ? 8 : 16; + int l = sz - s.length(); + if (l < 0) l = 0; + if (l > 16) l = 16; + return "0x0000000000000000".substring(0, 2 + l) + s; + } + + private String getFlagsLabel(int flags) { + StringBuilder flagsLabel = new StringBuilder(3); + if ((flags & IMemoryMap.FLAG_READ) != 0) flagsLabel.append('r'); + else flagsLabel.append('-'); + if ((flags & IMemoryMap.FLAG_WRITE) != 0) flagsLabel.append('w'); + else flagsLabel.append('-'); + if ((flags & IMemoryMap.FLAG_EXECUTE) != 0) flagsLabel.append('x'); + else flagsLabel.append('-'); + return flagsLabel.toString(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java new file mode 100644 index 000000000..2f635e6e4 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeRegister.java @@ -0,0 +1,631 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.services.IRegisters; +import org.eclipse.tcf.util.TCFDataCache; +import org.eclipse.tcf.util.TCFTask; + + +public class TCFNodeRegister extends TCFNode implements IElementEditor, IWatchInExpressions, IDetailsProvider { + + private final TCFChildrenRegisters children; + private final TCFData<IRegisters.RegistersContext> context; + private final TCFData<String> expression_text; + private final TCFData<byte[]> value; + private final boolean is_stack_frame_register; + + private byte[] prev_value; + private byte[] next_value; + + private static final RGB + rgb_error = new RGB(192, 0, 0), + rgb_highlight = new RGB(255, 255, 128); + + private int index; + + TCFNodeRegister(TCFNode parent, final String id) { + super(parent, id); + if (parent instanceof TCFNodeStackFrame) is_stack_frame_register = true; + else if (parent instanceof TCFNodeRegister) is_stack_frame_register = ((TCFNodeRegister)parent).is_stack_frame_register; + else is_stack_frame_register = false; + children = new TCFChildrenRegisters(this); + context = new TCFData<IRegisters.RegistersContext>(channel) { + @Override + protected boolean startDataRetrieval() { + IRegisters regs = launch.getService(IRegisters.class); + command = regs.getContext(id, new IRegisters.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IRegisters.RegistersContext context) { + set(token, error, context); + } + }); + return false; + } + }; + expression_text = new TCFData<String>(channel) { + @Override + protected boolean startDataRetrieval() { + Throwable err = null; + TCFNodeRegister n = TCFNodeRegister.this; + ArrayList<String> names = new ArrayList<String>(); + for (;;) { + if (!n.context.validate(this)) return false; + IRegisters.RegistersContext ctx = n.context.getData(); + if (ctx == null) { + err = n.context.getError(); + break; + } + String s = ctx.getName(); + if (s == null) break; + names.add(s); + if (!(n.parent instanceof TCFNodeRegister)) break; + n = (TCFNodeRegister)n.parent; + } + if (names.size() == 0 || err != null) { + set(null, err, null); + } + else { + StringBuffer bf = new StringBuffer(); + boolean first = true; + int m = names.size(); + while (m > 0) { + String s = names.get(--m); + boolean need_quotes = false; + int l = s.length(); + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + if (ch >= 'A' && ch <= 'Z') continue; + if (ch >= 'a' && ch <= 'z') continue; + if (ch >= '0' && ch <= '9') continue; + need_quotes = true; + break; + } + if (!first) bf.append('.'); + if (need_quotes) bf.append("$\""); + if (first) bf.append('$'); + bf.append(s); + if (need_quotes) bf.append('"'); + first = false; + } + set(null, null, bf.toString()); + } + return true; + } + }; + value = new TCFData<byte[]>(channel) { + @Override + protected boolean startDataRetrieval() { + Boolean b = usePrevValue(this); + if (b == null) return false; + if (b) { + set(null, null, prev_value); + return true; + } + if (!context.validate(this)) return false; + IRegisters.RegistersContext ctx = context.getData(); + if (ctx == null || ctx.getSize() <= 0) { + set(null, null, null); + return true; + } + command = ctx.get(new IRegisters.DoneGet() { + public void doneGet(IToken token, Exception error, byte[] value) { + if (error != null) { + Boolean b = usePrevValue(null); + if (b != null && b) { + set(token, null, prev_value); + return; + } + } + set(token, error, value); + } + }); + return false; + } + }; + } + + public TCFDataCache<IRegisters.RegistersContext> getContext() { + return context; + } + + public TCFDataCache<byte[]> getValue() { + return value; + } + + public TCFChildren getChildren() { + return children; + } + + public TCFDataCache<String> getExpressionText() { + return expression_text; + } + + void setIndex(int index) { + this.index = index; + } + + private Boolean usePrevValue(Runnable done) { + // Check if view should show old value. + // Old value is shown if context is running or + // stack trace does not contain expression parent frame. + // Return null if waiting for cache update. + if (prev_value == null) return false; + if (parent instanceof TCFNodeStackFrame) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent.parent; + TCFDataCache<TCFContextState> state_cache = exe.getState(); + if (!state_cache.validate(done)) return null; + TCFContextState state = state_cache.getData(); + if (state == null || !state.is_suspended) return true; + TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace(); + if (!stack_trace_cache.validate(done)) return null; + if (stack_trace_cache.getData().get(parent.id) == null) return true; + } + else if (parent instanceof TCFNodeExecContext) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent; + TCFDataCache<TCFContextState> state_cache = exe.getState(); + if (!state_cache.validate(done)) return null; + TCFContextState state = state_cache.getData(); + if (state == null || !state.is_suspended) return true; + } + return false; + } + + private void appendErrorText(StringBuffer bf, Throwable error) { + if (error == null) return; + bf.append("Exception: "); + bf.append(TCFModel.getErrorMessage(error, true)); + } + + public boolean getDetailText(StyledStringBuffer bf, Runnable done) { + if (!context.validate(done)) return false; + if (!value.validate(done)) return false; + int pos = bf.length(); + appendErrorText(bf.getStringBuffer(), context.getError()); + if (bf.length() == 0) appendErrorText(bf.getStringBuffer(), value.getError()); + if (bf.length() > pos) { + bf.append(pos, 0, null, rgb_error); + } + else { + IRegisters.RegistersContext ctx = context.getData(); + if (ctx != null) { + if (ctx.getDescription() != null) { + bf.append(ctx.getDescription()); + bf.append('\n'); + } + int l = bf.length(); + if (ctx.isReadable()) { + bf.append("readable"); + } + if (ctx.isReadOnce()) { + if (l < bf.length()) bf.append(", "); + bf.append("read once"); + } + if (ctx.isWriteable()) { + if (l < bf.length()) bf.append(", "); + bf.append("writable"); + } + if (ctx.isWriteOnce()) { + if (l < bf.length()) bf.append(", "); + bf.append("write once"); + } + if (ctx.hasSideEffects()) { + if (l < bf.length()) bf.append(", "); + bf.append("side effects"); + } + if (l < bf.length()) bf.append('\n'); + } + byte[] v = value.getData(); + if (v != null) { + bf.append("Hex: ", SWT.BOLD); + bf.append(toNumberString(16)); + bf.append(", "); + bf.append("Dec: ", SWT.BOLD); + bf.append(toNumberString(10)); + bf.append(", "); + bf.append("Oct: ", SWT.BOLD); + bf.append(toNumberString(8)); + bf.append('\n'); + bf.append("Bin: ", SWT.BOLD); + bf.append(toNumberString(2)); + bf.append('\n'); + } + } + return true; + } + + @Override + protected boolean getData(IHasChildrenUpdate result, Runnable done) { + if (!children.validate(done)) return false; + result.setHasChilren(children.size() > 0); + return true; + } + + @Override + protected boolean getData(IChildrenCountUpdate result, Runnable done) { + if (!children.validate(done)) return false; + result.setChildCount(children.size()); + return true; + } + + @Override + protected boolean getData(IChildrenUpdate result, Runnable done) { + return children.getData(result, done); + } + + @Override + protected boolean getData(ILabelUpdate result, Runnable done) { + TCFDataCache<?> pending = null; + if (!context.validate()) pending = context; + if (!value.validate()) pending = value; + if (pending != null) { + pending.wait(done); + return false; + } + String[] cols = result.getColumnIds(); + if (cols == null) { + setLabel(result, -1, 16); + } + else { + IRegisters.RegistersContext ctx = context.getData(); + for (int i = 0; i < cols.length; i++) { + String c = cols[i]; + if (ctx == null) { + result.setForeground(rgb_error, i); + result.setLabel("N/A", i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_NAME)) { + result.setLabel(ctx.getName(), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_HEX_VALUE)) { + setLabel(result, i, 16); + } + else if (c.equals(TCFColumnPresentationRegister.COL_DEC_VALUE)) { + setLabel(result, i, 10); + } + else if (c.equals(TCFColumnPresentationRegister.COL_DESCRIPTION)) { + result.setLabel(ctx.getDescription(), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_READBLE)) { + result.setLabel(bool(ctx.isReadable()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_READ_ONCE)) { + result.setLabel(bool(ctx.isReadOnce()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_WRITEABLE)) { + result.setLabel(bool(ctx.isWriteable()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_WRITE_ONCE)) { + result.setLabel(bool(ctx.isWriteOnce()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_SIDE_EFFECTS)) { + result.setLabel(bool(ctx.hasSideEffects()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_VOLATILE)) { + result.setLabel(bool(ctx.isVolatile()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_FLOAT)) { + result.setLabel(bool(ctx.isFloat()), i); + } + else if (c.equals(TCFColumnPresentationRegister.COL_MNEMONIC)) { + result.setLabel(getMnemonic(ctx), i); + } + } + } + boolean changed = false; + next_value = value.getData(); + if (prev_value != null && next_value != null) { + if (prev_value.length != next_value.length) { + changed = true; + } + else { + for (int i = 0; i < prev_value.length; i++) { + if (prev_value[i] != next_value[i]) changed = true; + } + } + } + if (changed) { + result.setBackground(rgb_highlight, 0); + if (cols != null) { + for (int i = 1; i < cols.length; i++) { + result.setBackground(rgb_highlight, i); + } + } + } + result.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_REGISTER), 0); + return true; + } + + private void setLabel(ILabelUpdate result, int col, int radix) { + IRegisters.RegistersContext ctx = context.getData(); + Throwable error = context.getError(); + if (error == null) error = value.getError(); + byte[] data = value.getData(); + if (error != null || ctx == null) { + result.setForeground(rgb_error, col); + result.setLabel("N/A", col); + } + else if (data != null) { + String s = toNumberString(radix); + if (col >= 0) { + result.setLabel(s, col); + } + else { + result.setLabel(ctx.getName() + " = " + s, 0); + } + } + } + + private String toNumberString(int radix) { + IRegisters.RegistersContext ctx = context.getData(); + byte[] data = value.getData(); + if (ctx == null || data == null) return "N/A"; + if (radix == 2) { + StringBuffer bf = new StringBuffer(); + int i = data.length * 8; + while (i > 0) { + if (i % 4 == 0 && bf.length() > 0) bf.append(','); + i--; + int j = i / 8; + if (ctx.isBigEndian()) j = data.length - j - 1; + if ((data[j] & (1 << (i % 8))) != 0) { + bf.append('1'); + } + else { + bf.append('0'); + } + } + return bf.toString(); + } + if (radix == 10 && ctx.isFloat()) { + String s = TCFNumberFormat.toFPString(data, 0, data.length, ctx.isBigEndian()); + if (s != null) return s; + } + BigInteger b = TCFNumberFormat.toBigInteger(data, 0, data.length, ctx.isBigEndian(), false); + String s = b.toString(radix); + switch (radix) { + case 8: + if (!s.startsWith("0")) s = "0" + s; + break; + case 16: + if (s.length() < data.length * 2) { + StringBuffer bf = new StringBuffer(); + while (bf.length() + s.length() < data.length * 2) bf.append('0'); + bf.append(s); + s = bf.toString(); + } + break; + } + return s; + } + + private String bool(boolean b) { + return b ? "yes" : "no"; + } + + private String getMnemonic(IRegisters.RegistersContext ctx) { + if (value.getData() != null) { + IRegisters.NamedValue[] arr = ctx.getNamedValues(); + if (arr != null) { + for (IRegisters.NamedValue n : arr) { + if (Arrays.equals(n.getValue(), value.getData())) return n.getName(); + } + } + } + return ""; + } + + private void postStateChangedDelta() { + for (TCFModelProxy p : model.getModelProxies()) { + if (!IDebugUIConstants.ID_REGISTER_VIEW.equals(p.getPresentationContext().getId())) continue; + p.addDelta(this, IModelDelta.STATE); + } + } + + void onValueChanged() { + prev_value = next_value; + value.reset(); + TCFNode n = parent; + while (n != null) { + if (n instanceof TCFNodeExecContext) { + ((TCFNodeExecContext)n).onRegisterValueChanged(); + break; + } + else if (n instanceof TCFNodeRegister) { + TCFNodeRegister r = (TCFNodeRegister)n; + if (r.value.isValid() && r.value.getData() != null) { + r.value.reset(); + r.postStateChangedDelta(); + } + } + n = n.parent; + } + children.onParentValueChanged(); + postStateChangedDelta(); + } + + void onParentValueChanged() { + value.reset(); + children.onParentValueChanged(); + postStateChangedDelta(); + } + + void onSuspended() { + prev_value = next_value; + value.reset(); + children.onSuspended(); + // Unlike thread registers, stack frame register list must be retrieved on every suspend + if (is_stack_frame_register) children.reset(); + // No need to post delta: parent posted CONTENT + } + + void onRegistersChanged() { + children.onRegistersChanged(); + expression_text.reset(); + context.reset(); + value.reset(); + // No need to post delta: parent posted CONTENT + } + + public CellEditor getCellEditor(IPresentationContext context, String column_id, Object element, Composite parent) { + assert element == this; + if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(column_id)) { + return new TextCellEditor(parent); + } + if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(column_id)) { + return new TextCellEditor(parent); + } + return null; + } + + private static final ICellModifier cell_modifier = new ICellModifier() { + + public boolean canModify(Object element, final String property) { + final TCFNodeRegister node = (TCFNodeRegister)element; + return new TCFTask<Boolean>() { + public void run() { + if (!node.context.validate(this)) return; + IRegisters.RegistersContext ctx = node.context.getData(); + if (ctx != null && ctx.isWriteable()) { + if (!ctx.isReadable()) { + done(Boolean.TRUE); + return; + } + if (!node.value.validate(this)) return; + if (node.value.getError() == null) { + if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) { + done(TCFNumberFormat.isValidHexNumber(node.toNumberString(16)) == null); + return; + } + if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) { + done(TCFNumberFormat.isValidDecNumber(true, node.toNumberString(10)) == null); + return; + } + } + } + done(Boolean.FALSE); + } + }.getE(); + } + + public Object getValue(Object element, final String property) { + final TCFNodeRegister node = (TCFNodeRegister)element; + return new TCFTask<String>() { + public void run() { + if (!node.context.validate(this)) return; + IRegisters.RegistersContext ctx = node.context.getData(); + if (!ctx.isReadable()) { + done("0"); + return; + } + if (!node.value.validate(this)) return; + if (node.value.getError() == null) { + if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) { + done(node.toNumberString(16)); + return; + } + if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) { + done(node.toNumberString(10)); + return; + } + } + done(null); + } + }.getE(); + } + + public void modify(Object element, final String property, final Object value) { + if (value == null) return; + final TCFNodeRegister node = (TCFNodeRegister)element; + new TCFTask<Boolean>() { + public void run() { + try { + if (!node.context.validate(this)) return; + IRegisters.RegistersContext ctx = node.context.getData(); + if (ctx != null && ctx.isWriteable()) { + byte[] bf = null; + boolean is_float = ctx.isFloat(); + int size = ctx.getSize(); + boolean big_endian = ctx.isBigEndian(); + String input = (String)value; + String error = null; + if (TCFColumnPresentationRegister.COL_HEX_VALUE.equals(property)) { + error = TCFNumberFormat.isValidHexNumber(input); + if (error == null) bf = TCFNumberFormat.toByteArray(input, 16, false, size, false, big_endian); + } + else if (TCFColumnPresentationRegister.COL_DEC_VALUE.equals(property)) { + error = TCFNumberFormat.isValidDecNumber(is_float, input); + if (error == null) bf = TCFNumberFormat.toByteArray(input, 10, is_float, size, is_float, big_endian); + } + if (error != null) throw new Exception("Invalid value: " + value, new Exception(error)); + if (bf != null) { + ctx.set(bf, new IRegisters.DoneSet() { + public void doneSet(IToken token, Exception error) { + if (error != null) { + node.model.showMessageBox("Cannot modify register value", error); + done(Boolean.FALSE); + } + else { + node.value.reset(); + node.postStateChangedDelta(); + done(Boolean.TRUE); + } + } + }); + return; + } + } + done(Boolean.FALSE); + } + catch (Throwable x) { + node.model.showMessageBox("Cannot modify register value", x); + done(Boolean.FALSE); + } + } + }.getE(); + } + }; + + public ICellModifier getCellModifier(IPresentationContext context, Object element) { + assert element == this; + return cell_modifier; + } + + @Override + public int compareTo(TCFNode n) { + if (n instanceof TCFNodeRegister) { + TCFNodeRegister r = (TCFNodeRegister)n; + if (index < r.index) return -1; + if (index > r.index) return +1; + } + return id.compareTo(n.id); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java new file mode 100644 index 000000000..5a8d7fb51 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java @@ -0,0 +1,541 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigInteger; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputUpdate; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tcf.internal.debug.model.TCFContextState; +import org.eclipse.tcf.internal.debug.model.TCFFunctionRef; +import org.eclipse.tcf.internal.debug.model.TCFSourceRef; +import org.eclipse.tcf.internal.debug.ui.ImageCache; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.JSON; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IExpressions; +import org.eclipse.tcf.services.IStackTrace; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; + +public class TCFNodeStackFrame extends TCFNode { + + private int frame_no; + private boolean trace_limit; + private final boolean emulated; + private final TCFChildrenRegisters children_regs; + private final TCFChildrenLocalVariables children_vars; + private final TCFChildrenExpressions children_exps; + private final TCFChildrenHoverExpressions children_hover_exps; + private final TCFData<IStackTrace.StackTraceContext> stack_trace_context; + private final TCFData<TCFSourceRef> line_info; + private final TCFData<TCFFunctionRef> func_info; + private final TCFData<BigInteger> address; + + TCFNodeStackFrame(final TCFNodeExecContext parent, final String id, final boolean emulated) { + super(parent, id); + this.emulated = emulated; + children_regs = new TCFChildrenRegisters(this); + children_vars = new TCFChildrenLocalVariables(this); + children_exps = new TCFChildrenExpressions(this); + children_hover_exps = new TCFChildrenHoverExpressions(this); + stack_trace_context = new TCFData<IStackTrace.StackTraceContext>(channel) { + @Override + protected boolean startDataRetrieval() { + assert command == null; + if (emulated) { + set(null, null, null); + return true; + } + TCFDataCache<TCFContextState> parent_state_cache = parent.getState(); + if (!parent_state_cache.validate(this)) return false; + TCFContextState parent_state_data = parent_state_cache.getData(); + if (parent_state_data == null || !parent_state_data.is_suspended) { + set(null, null, null); + return true; + } + TCFChildrenStackTrace stack_trace_cache = parent.getStackTrace(); + if (!stack_trace_cache.validate(this)) return false; + if (frame_no < 0) { + set(null, null, null); + return true; + } + IStackTrace st = launch.getService(IStackTrace.class); + if (st == null) { + assert frame_no == 0; + set(null, null, null); + return true; + } + command = st.getContext(new String[]{ id }, new IStackTrace.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, IStackTrace.StackTraceContext[] context) { + set(token, error, context == null || context.length == 0 ? null : context[0]); + } + }); + return false; + } + }; + line_info = new TCFData<TCFSourceRef>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!stack_trace_context.validate(this)) return false; + if (!address.validate(this)) return false; + BigInteger n = address.getData(); + if (n == null) { + set(null, address.getError(), null); + return true; + } + if (frame_no > 0) n = n.subtract(BigInteger.valueOf(1)); + TCFDataCache<TCFNodeExecContext> mem_cache = ((TCFNodeExecContext)parent).getMemoryNode(); + if (!mem_cache.validate(this)) return false; + if (mem_cache.getError() != null || mem_cache.getData() == null) { + set(null, mem_cache.getError(), null); + return true; + } + TCFDataCache<TCFSourceRef> info_cache = mem_cache.getData().getLineInfo(n); + if (info_cache == null) { + set(null, null, null); + return true; + } + if (!info_cache.validate(this)) return false; + set(null, info_cache.getError(), info_cache.getData()); + return true; + } + }; + func_info = new TCFData<TCFFunctionRef>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!address.validate(this)) return false; + BigInteger n = address.getData(); + if (n == null) { + set(null, address.getError(), null); + return true; + } + TCFDataCache<TCFNodeExecContext> mem_cache = ((TCFNodeExecContext)parent).getMemoryNode(); + if (!mem_cache.validate(this)) return false; + if (mem_cache.getError() != null || mem_cache.getData() == null) { + set(null, mem_cache.getError(), null); + return true; + } + TCFDataCache<TCFFunctionRef> info_cache = mem_cache.getData().getFuncInfo(n); + if (info_cache == null) { + set(null, null, null); + return true; + } + if (!info_cache.validate(this)) return false; + set(null, info_cache.getError(), info_cache.getData()); + return true; + } + }; + address = new TCFData<BigInteger>(channel) { + @Override + protected boolean startDataRetrieval() { + if (!stack_trace_context.validate(this)) return false; + IStackTrace.StackTraceContext ctx = stack_trace_context.getData(); + if (ctx != null) { + Number n = ctx.getInstructionAddress(); + if (n instanceof BigInteger) { + set(null, null, (BigInteger)n); + return true; + } + if (n != null) { + set(null, null, JSON.toBigInteger(n)); + return true; + } + } + if (frame_no == 0) { + TCFDataCache<BigInteger> addr_cache = parent.getAddress(); + if (!addr_cache.validate(this)) return false; + set(null, addr_cache.getError(), addr_cache.getData()); + return true; + } + set(null, stack_trace_context.getError(), null); + return true; + } + }; + } + + /** + * Get frame position in the parent's stack trace. + * Top frame position is 0. + * @return frame position or -1 if the frame is not part of the trace. + */ + public int getFrameNo() { + assert Protocol.isDispatchThread(); + return frame_no; + } + + void setFrameNo(int frame_no) { + this.frame_no = frame_no; + } + + void setTraceLimit(boolean trace_limit) { + this.trace_limit = trace_limit; + } + + TCFChildren getHoverExpressionCache(String expression) { + children_hover_exps.setExpression(expression); + return children_hover_exps; + } + + public TCFDataCache<TCFSourceRef> getLineInfo() { + return line_info; + } + + public TCFDataCache<IStackTrace.StackTraceContext> getStackTraceContext() { + return stack_trace_context; + } + + public TCFDataCache<BigInteger> getAddress() { + return address; + } + + TCFChildren getRegisters() { + return children_regs; + } + + public BigInteger getReturnAddress() { + assert Protocol.isDispatchThread(); + if (!stack_trace_context.isValid()) return null; + IStackTrace.StackTraceContext ctx = stack_trace_context.getData(); + if (ctx != null) return JSON.toBigInteger(ctx.getReturnAddress()); + return null; + } + + public boolean isEmulated() { + return emulated; + } + + boolean isTraceLimit() { + return trace_limit && ((TCFNodeExecContext)parent).getViewBottomFrame() == this; + } + + void riseTraceLimit() { + ((TCFNodeExecContext)parent).riseTraceLimit(); + } + + private TCFChildren getChildren(IPresentationContext ctx) { + String id = ctx.getId(); + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(id)) return children_regs; + if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(id)) return children_vars; + if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) return children_exps; + if (TCFModel.ID_EXPRESSION_HOVER.equals(id)) return children_hover_exps; + return null; + } + + @Override + protected boolean getData(IHasChildrenUpdate result, Runnable done) { + TCFChildren c = getChildren(result.getPresentationContext()); + if (c != null) { + if (!c.validate(done)) return false; + result.setHasChilren(c.size() > 0); + } + else { + result.setHasChilren(false); + } + return true; + } + + @Override + protected boolean getData(IChildrenCountUpdate result, Runnable done) { + TCFChildren c = getChildren(result.getPresentationContext()); + if (c != null) { + if (!c.validate(done)) return false; + result.setChildCount(c.size()); + } + else { + result.setChildCount(0); + } + return true; + } + + @Override + protected boolean getData(IChildrenUpdate result, Runnable done) { + TCFChildren children = getChildren(result.getPresentationContext()); + if (children == null) return true; + return children.getData(result, done); + } + + @Override + protected boolean getData(ILabelUpdate result, Runnable done) { + TCFChildrenStackTrace stack_trace_cache = ((TCFNodeExecContext)parent).getStackTrace(); + if (!stack_trace_cache.validate(done)) return false; + if (stack_trace_cache.getData().get(id) == null) { + result.setLabel("", 0); + } + else if (isTraceLimit()) { + result.setLabel("<select to see more frames>", 0); + } + else { + boolean show_arg_names = model.getShowFunctionArgNames(); + boolean show_arg_values = model.getShowFunctionArgValues(); + TCFDataCache<TCFContextState> state_cache = ((TCFNodeExecContext)parent).getState(); + TCFDataCache<TCFNodeExecContext> mem_cache = ((TCFNodeExecContext)parent).getMemoryNode(); + TCFDataCache<?> pending = null; + if (!state_cache.validate()) pending = state_cache; + if (!mem_cache.validate()) pending = mem_cache; + if (!stack_trace_context.validate()) pending = stack_trace_context; + if (!address.validate()) pending = address; + if (!line_info.validate()) pending = line_info; + if (!func_info.validate()) pending = func_info; + if (show_arg_names || show_arg_values) { + if (!children_vars.validate()) { + pending = children_vars; + } + else { + for (TCFNode n : children_vars.toArray()) { + TCFNodeExpression e = (TCFNodeExpression)n; + if (!e.getVariable().validate()) pending = e.getVariable(); + } + } + } + if (pending != null) { + pending.wait(done); + return false; + } + Throwable error = state_cache.getError(); + if (error == null) error = stack_trace_cache.getError(); + if (error == null) error = stack_trace_context.getError(); + if (error == null) error = address.getError(); + if (error == null) error = line_info.getError(); + BigInteger addr = address.getData(); + TCFSourceRef sref = line_info.getData(); + TCFContextState state = state_cache.getData(); + StringBuffer bf = new StringBuffer(); + if (addr != null) { + bf.append(makeHexAddrString(sref != null ? sref.address_size : 0, addr)); + TCFNodeExecContext mem_node = mem_cache.getData(); + if (mem_node != null) { + TCFDataCache<TCFNodeExecContext.MemoryRegion[]> map_dc = mem_node.getMemoryMap(); + if (!map_dc.validate(done)) return false; + TCFNodeExecContext.MemoryRegion[] map = map_dc.getData(); + if (map != null) { + for (TCFNodeExecContext.MemoryRegion r : map) { + String fnm = r.region.getFileName(); + if (fnm != null && r.contains(addr)) { + fnm = fnm.replace('\\', '/'); + int x = fnm.lastIndexOf('/'); + if (x >= 0) fnm = fnm.substring(x + 1); + bf.append(" ["); + bf.append(fnm); + bf.append("]"); + break; + } + } + } + } + } + TCFFunctionRef ref = func_info.getData(); + if (ref != null && ref.symbol_id != null) { + TCFDataCache<ISymbols.Symbol> sym_cache = model.getSymbolInfoCache(ref.symbol_id); + if (!sym_cache.validate(done)) return false; + ISymbols.Symbol sym_data = sym_cache.getData(); + if (sym_data != null && sym_data.getName() != null) { + bf.append(" "); + bf.append(sym_data.getName()); + bf.append('('); + if (show_arg_names || show_arg_values) { + if (children_vars.getError() != null) { + bf.append('?'); + } + else { + int cnt = 0; + for (TCFNode n : children_vars.toArray()) { + ISymbols.Symbol sym = null; + TCFNodeExpression expr_node = (TCFNodeExpression)n; + IExpressions.Expression expr_props = expr_node.getVariable().getData(); + if (expr_props != null) { + TCFDataCache<ISymbols.Symbol> s = model.getSymbolInfoCache(expr_props.getSymbolID()); + if (!s.validate(done)) return false; + sym = s.getData(); + } + if (sym == null) continue; + if (!sym.getFlag(ISymbols.SYM_FLAG_PARAMETER)) continue; + if (cnt > 0) bf.append(','); + if (show_arg_names) { + String name = "?"; + if (sym != null && sym.getName() != null) name = sym.getName(); + bf.append(name); + if (show_arg_values) bf.append('='); + } + if (show_arg_values) { + String s = expr_node.getValueText(false, done); + if (s == null) return false; + bf.append(s.length() == 0 ? "?" : s); + } + cnt++; + } + } + } + bf.append(')'); + } + } + if (sref != null && sref.area != null && sref.area.file != null) { + bf.append(": "); + int l = sref.area.file.length(); + if (l > 32) { + bf.append("..."); + bf.append(sref.area.file.substring(l - 32)); + } + else { + bf.append(sref.area.file); + } + bf.append(", line "); + bf.append(sref.area.start_line); + } + if (error != null) { + if (state == null || state.is_suspended) { + result.setForeground(new RGB(255, 0, 0), 0); + if (bf.length() > 0) bf.append(": "); + bf.append(TCFModel.getErrorMessage(error, false)); + } + else { + result.setLabel("...", 0); + } + } + if (bf.length() == 0) bf.append("..."); + result.setLabel(bf.toString(), 0); + String image_name = state != null && state.is_suspended ? + ImageCache.IMG_STACK_FRAME_SUSPENDED : + ImageCache.IMG_STACK_FRAME_RUNNING; + result.setImageDescriptor(ImageCache.getImageDescriptor(image_name), 0); + } + return true; + } + + @Override + protected boolean getData(IViewerInputUpdate result, Runnable done) { + result.setInputElement(this); + String id = result.getPresentationContext().getId(); + if (IDebugUIConstants.ID_REGISTER_VIEW.equals(id) || IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) { + TCFNodeExecContext exe = (TCFNodeExecContext)parent; + TCFChildrenStackTrace stack_trace_cache = exe.getStackTrace(); + if (!stack_trace_cache.validate(done)) return false; + if (stack_trace_cache.getTopFrame() == this) result.setInputElement(exe); + } + else if (IDebugUIConstants.ID_MODULE_VIEW.equals(id)) { + // TODO: need to post view input delta when memory context changes + TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext(this); + if (mem == null) return true; + if (!mem.validate(done)) return false; + if (mem.getData() == null) return true; + result.setInputElement(mem.getData()); + } + return true; + } + + private String makeHexAddrString(int addr_size, BigInteger n) { + String s = n.toString(16); + int sz = (addr_size != 0 ? addr_size : 4) * 2; + int l = sz - s.length(); + if (l < 0) l = 0; + if (l > 16) l = 16; + return "0x0000000000000000".substring(0, 2 + l) + s; + } + + void postAllChangedDelta() { + for (TCFModelProxy p : model.getModelProxies()) { + int flags = 0; + String view_id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(view_id) && + (launch.getContextActionsCount(parent.id) == 0 || + !model.getDelayStackUpdateUtilLastStep())) { + flags |= IModelDelta.STATE; + } + if (getChildren(p.getPresentationContext()) != null && p.getInput() == this) flags |= IModelDelta.CONTENT; + if (flags == 0) continue; + p.addDelta(this, flags); + } + } + + private void postStateChangedDelta() { + for (TCFModelProxy p : model.getModelProxies()) { + String id = p.getPresentationContext().getId(); + if (IDebugUIConstants.ID_DEBUG_VIEW.equals(id)) { + p.addDelta(this, IModelDelta.STATE); + } + } + } + + void onExpressionAddedOrRemoved() { + children_exps.cancel(); + } + + void onSourceMappingChange() { + line_info.reset(); + postStateChangedDelta(); + } + + void onSuspended() { + stack_trace_context.cancel(); + line_info.cancel(); + func_info.cancel(); + address.cancel(); + children_regs.onSuspended(); + // Unlike thread registers, stack frame register list must be retrieved on every suspend + children_regs.reset(); + children_vars.onSuspended(); + children_exps.onSuspended(); + children_hover_exps.onSuspended(); + // delta is posted by the parent node + } + + void onMemoryMapChanged() { + line_info.reset(); + func_info.reset(); + children_vars.onMemoryMapChanged(); + children_exps.onMemoryMapChanged(); + postAllChangedDelta(); + } + + void onMemoryChanged() { + stack_trace_context.cancel(); + line_info.cancel(); + func_info.cancel(); + address.cancel(); + children_vars.onMemoryChanged(); + children_exps.onMemoryChanged(); + children_hover_exps.onMemoryChanged(); + postStateChangedDelta(); + } + + void onRegistersChanged() { + children_regs.onRegistersChanged(); + postAllChangedDelta(); + } + + void onRegisterValueChanged() { + stack_trace_context.cancel(); + line_info.cancel(); + func_info.cancel(); + address.cancel(); + children_vars.onRegisterValueChanged(); + children_exps.onRegisterValueChanged(); + children_hover_exps.onRegisterValueChanged(); + postStateChangedDelta(); + } + + @Override + public int compareTo(TCFNode n) { + if (n instanceof TCFNodeStackFrame) { + TCFNodeStackFrame f = (TCFNodeStackFrame)n; + if (frame_no < f.frame_no) return -1; + if (frame_no > f.frame_no) return +1; + } + return id.compareTo(n.id); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java new file mode 100644 index 000000000..855ae0fc2 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeSymbol.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.ISymbols; +import org.eclipse.tcf.util.TCFDataCache; + +public class TCFNodeSymbol extends TCFNode { + + private final TCFData<ISymbols.Symbol> context; + private final TCFData<String[]> children; + + private int update_policy; + private ISymbolOwner owner; + + private TCFNodeSymbol prev; + private TCFNodeSymbol next; + + private static final int MAX_SYMBOL_COUNT = 64; + private static TCFNodeSymbol sym_list; + private static int sym_count; + private static boolean gc_posted; + + protected TCFNodeSymbol(final TCFNode parent, final String id) { + super(parent, id); + context = new TCFData<ISymbols.Symbol>(channel) { + @Override + protected boolean startDataRetrieval() { + ISymbols syms = launch.getService(ISymbols.class); + if (id == null || syms == null) { + set(null, null, null); + return true; + } + command = syms.getContext(id, new ISymbols.DoneGetContext() { + public void doneGetContext(IToken token, Exception error, ISymbols.Symbol sym) { + set(token, error, sym); + if (error != null || sym == null) setUpdatePolicy(null, 0); + else setUpdatePolicy(sym.getOwnerID(), sym.getUpdatePolicy()); + } + }); + return false; + } + }; + children = new TCFData<String[]>(channel) { + @Override + protected boolean startDataRetrieval() { + ISymbols syms = launch.getService(ISymbols.class); + if (id == null || syms == null) { + set(null, null, null); + return true; + } + command = syms.getChildren(id, new ISymbols.DoneGetChildren() { + public void doneGetChildren(IToken token, Exception error, String[] ids) { + set(token, error, ids); + } + }); + return false; + } + }; + setUpdatePolicy(null, 0); + if (sym_list == null) { + prev = next = this; + } + else { + prev = sym_list; + next = sym_list.next; + prev.next = next.prev = this; + } + sym_list = this; + if (!gc_posted) { + // Garbage collection: dispose unused symbols + gc_posted = true; + Protocol.invokeLater(5000, new Runnable() { + public void run() { + gc_posted = false; + int cnt = sym_count / 16; + while (sym_count > MAX_SYMBOL_COUNT) { + TCFNodeSymbol s = sym_list.next; + if (s.context.isPending()) break; + if (s.children.isPending()) break; + s.dispose(); + if (cnt == 0) break; + cnt--; + } + if (sym_count > 0) { + gc_posted = true; + Protocol.invokeLater(5000, this); + } + } + }); + } + sym_count++; + } + + @Override + public void dispose() { + assert !isDisposed(); + if (owner != null) { + owner.removeSymbol(this); + owner = null; + } + if (sym_list == this) sym_list = prev; + if (sym_list == this) { + sym_list = null; + } + else { + prev.next = next; + next.prev = prev; + } + prev = next = null; + sym_count--; + assert (sym_count == 0) == (sym_list == null); + super.dispose(); + } + + public TCFDataCache<ISymbols.Symbol> getContext() { + if (sym_list != this) { + prev.next = next; + next.prev = prev; + prev = sym_list; + next = sym_list.next; + prev.next = next.prev = this; + sym_list = this; + } + return context; + } + + public TCFDataCache<String[]> getChildren() { + if (sym_list != this) { + prev.next = next; + next.prev = prev; + prev = sym_list; + next = sym_list.next; + prev.next = next.prev = this; + sym_list = this; + } + return children; + } + + private void setUpdatePolicy(String id, int policy) { + update_policy = policy; + if (!isDisposed()) { + TCFNode n = model.getNode(id); + if (!(n instanceof ISymbolOwner)) n = parent; + if (n != owner) { + if (owner != null) owner.removeSymbol(this); + owner = (ISymbolOwner)n; + owner.addSymbol(this); + } + } + } + + void onMemoryMapChanged() { + context.reset(); + children.reset(); + } + + void onExeStateChange() { + if (update_policy == ISymbols.UPDATE_ON_MEMORY_MAP_CHANGES) return; + context.reset(); + children.reset(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java new file mode 100644 index 000000000..fd9c63c3c --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNumberFormat.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; + +public class TCFNumberFormat { + + public static String isValidHexNumber(String s) { + int l = s.length(); + if (l == 0) return "Need at least one digit"; + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + if (ch >= '0' && ch <= '9') continue; + if (ch >= 'A' && ch <= 'F') continue; + if (ch >= 'a' && ch <= 'f') continue; + return "Hex digit expected"; + } + return null; + } + + public static String isValidDecNumber(boolean fp, String s) { + int i = 0; + int l = s.length(); + if (l == 0) return "Need at least one digit"; + char ch = s.charAt(i++); + if (ch == '-' || ch == '+') { + if (i >= l) return "Need at least one digit"; + ch = s.charAt(i++); + } + if (fp) { + String n = s.substring(i - 1); + if (n.equals("NaN")) return null; + if (n.equals("Infinity")) return null; + } + while (ch >= '0' && ch <= '9') { + if (i >= l) return null; + ch = s.charAt(i++); + } + if (fp) { + if (ch == '.') { + if (i >= l) return null; + ch = s.charAt(i++); + while (ch >= '0' && ch <= '9') { + if (i >= l) return null; + ch = s.charAt(i++); + } + } + if (ch == 'e' || ch == 'E') { + if (i >= l) return "Invalid exponent: need at least one digit"; + ch = s.charAt(i++); + if (ch == '-' || ch == '+') { + if (i >= l) return "Invalid exponent: need at least one digit"; + ch = s.charAt(i++); + } + while (ch >= '0' && ch <= '9') { + if (i >= l) return null; + ch = s.charAt(i++); + } + return "Invalid exponent: decimal digit expected"; + } + } + return "Decimal digit expected"; + } + + public static byte[] toByteArray(String s, int radix, boolean fp, int size, boolean signed, boolean big_endian) throws Exception { + byte[] bf = null; + if (!fp) { + bf = new BigInteger(s, radix).toByteArray(); + } + else if (size == 4) { + int n = Float.floatToIntBits(Float.parseFloat(s)); + bf = new byte[size]; + for (int i = 0; i < size; i++) { + bf[i] = (byte)((n >> ((size - 1 - i) * 8)) & 0xff); + } + } + else if (size == 8) { + long n = Double.doubleToLongBits(Double.parseDouble(s)); + bf = new byte[size]; + for (int i = 0; i < size; i++) { + bf[i] = (byte)((n >> ((size - 1 - i) * 8)) & 0xff); + } + } + else { + throw new Exception("Unsupported floating point format"); + } + byte[] rs = new byte[size]; + if (signed && rs.length > bf.length && (bf[0] & 0x80) != 0) { + // Sign extension + for (int i = 0; i < rs.length; i++) rs[i] = (byte)0xff; + } + for (int i = 0; i < bf.length; i++) { + // i == 0 -> least significant byte + byte b = bf[bf.length - i - 1]; + int j = big_endian ? rs.length - i - 1 : i; + if (j >= 0 && j < rs.length) rs[j] = b; + } + return rs; + } + + public static String toFPString(byte[] data, int offs, int size, boolean big_endian) { + assert offs + size <= data.length; + byte[] arr = new byte[size]; + if (big_endian) { + System.arraycopy(data, offs, arr, 0, size); + } + else { + for (int i = 0; i < size; i++) { + arr[arr.length - i - 1] = data[offs + i]; + } + } + + boolean neg = (arr[0] & 0x80) != 0; + arr[0] &= 0x7f; + + int precision = 0; + int exponent = 0; + boolean nan = false; + switch (size) { + case 2: + precision = 3; + exponent = (arr[0] & 0x7c) >> 2; + nan = exponent == 0x1f; + arr[0] &= 0x03; + if (exponent == 0) exponent = 1; + else arr[0] |= 0x04; + exponent -= 10; // Significand + exponent -= 15; // Exponent bias + break; + case 4: + precision = 7; + exponent = ((arr[0] & 0x7f) << 1) | ((arr[1] & 0x80) >> 7); + nan = exponent == 0xff; + arr[0] = 0; + arr[1] &= 0x7f; + if (exponent == 0) exponent = 1; + else arr[1] |= 0x80; + exponent -= 23; // Significand + exponent -= 127; // Exponent bias + break; + case 8: + precision = 16; + exponent = ((arr[0] & 0x7f) << 4) | ((arr[1] & 0xf0) >> 4); + nan = exponent == 0x7ff; + arr[0] = 0; + arr[1] &= 0x0f; + if (exponent == 0) exponent = 1; + else arr[1] |= 0x10; + exponent -= 52; // Significand + exponent -= 1023; // Exponent bias + break; + case 10: + case 16: + precision = 34; + exponent = ((arr[0] & 0x7f) << 8) | (arr[1] & 0xff); + nan = exponent == 0x7fff; + arr[0] = arr[1] = 0; + if (size == 10) { + exponent -= 63; // Significand + } + else { + if (exponent == 0) exponent = 1; + else arr[1] = 1; + exponent -= 112; // Significand + } + exponent -= 16383; // Exponent bias + break; + default: + return null; + } + if (nan) { + for (int i = 0; i < arr.length; i++) { + int n = arr[i] & 0xff; + if (size == 10 && i == 2) n &= 0x7f; + if (n != 0) return neg ? "-NaN" : "+NaN"; + } + return neg ? "-Infinity" : "+Infinity"; + } + BigDecimal a = new BigDecimal(new BigInteger(arr), 0); + if (a.signum() != 0 && exponent != 0) { + BigDecimal p = new BigDecimal(BigInteger.valueOf(2), 0); + if (exponent > 0) { + a = a.multiply(p.pow(exponent)); + } + else { + BigDecimal b = p.pow(-exponent); + a = a.divide(b, b.precision(), RoundingMode.HALF_DOWN); + } + if (precision != 0 && a.precision() > precision) { + int scale = a.scale() - a.precision() + precision; + a = a.setScale(scale, RoundingMode.HALF_DOWN); + } + } + String s = a.toString(); + if (neg) s = "-" + s; + return s; + } + + public static BigInteger toBigInteger(byte[] data, int offs, int size, boolean big_endian, boolean sign_extension) { + assert offs + size <= data.length; + byte[] temp = null; + if (sign_extension) { + temp = new byte[size]; + } + else { + temp = new byte[size + 1]; + temp[0] = 0; // Extra byte to avoid sign extension by BigInteger + } + if (big_endian) { + System.arraycopy(data, offs, temp, sign_extension ? 0 : 1, size); + } + else { + for (int i = 0; i < size; i++) { + temp[temp.length - i - 1] = data[i + offs]; + } + } + return new BigInteger(temp); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java new file mode 100644 index 000000000..b2ecb0ae2 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFRunnable.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import org.eclipse.debug.core.IRequest; +import org.eclipse.tcf.protocol.Protocol; + + +public abstract class TCFRunnable implements Runnable { + + private final IRequest request; + + protected boolean done; + + public TCFRunnable(IRequest request) { + this.request = request; + Protocol.invokeLater(this); + } + + public void done() { + assert !done; + done = true; + request.done(); + } +} diff --git a/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java new file mode 100644 index 000000000..c5205f777 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFSnapshot.java @@ -0,0 +1,353 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. and others. + * 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.internal.debug.ui.model; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.tcf.protocol.Protocol; + +/** + * TCFSnapshot is used to create snapshots of debug views presentation data. + * Such snapshots are used to implement various view update policies. + */ +class TCFSnapshot { + + private final IPresentationContext ctx; + + private final HashMap<TCFNode,PresentationData> cache = new HashMap<TCFNode,PresentationData>(); + + private final String[] columns; + private final RGB rgb_stalled = new RGB(128, 128, 128); + + private boolean ignore_bg_color = true; + + private class PresentationData implements IChildrenCountUpdate, IChildrenUpdate, ILabelUpdate, Runnable { + + IViewerUpdate update; + Runnable done; + boolean canceled; + IStatus status; + + String[] label; + FontData[] font_data; + ImageDescriptor[] image_desc; + RGB[] fg_color; + RGB[] bg_color; + boolean label_done; + + TCFNode[] children; + boolean children_done; + + boolean stalled; + + private final ArrayList<Runnable> waiting_list = new ArrayList<Runnable>(); + + public IPresentationContext getPresentationContext() { + return ctx; + } + + public Object getElement() { + return update.getElement(); + } + + public TreePath getElementPath() { + return update.getElementPath(); + } + + public Object getViewerInput() { + return update.getViewerInput(); + } + + public void setStatus(IStatus status) { + this.status = status; + } + + public IStatus getStatus() { + return status; + } + + public void done() { + assert false; + } + + public void cancel() { + canceled = true; + } + + public boolean isCanceled() { + return canceled; + } + + public String[] getColumnIds() { + return columns; + } + + public void setLabel(String text, int col) { + if (!label_done) { + if (label == null) { + int cnt = columns == null ? 1 : columns.length; + label = new String[cnt]; + } + label[col] = text; + } + else { + if (col >= label.length) stalled = true; + else if (label[col] != text) { + if (label[col] == null || text == null || !text.equals(label[col])) { + stalled = true; + } + } + } + } + + public void setFontData(FontData fnt, int col) { + if (!label_done) { + if (font_data == null) { + int cnt = columns == null ? 1 : columns.length; + font_data = new FontData[cnt]; + } + font_data[col] = fnt; + } + } + + public void setImageDescriptor(ImageDescriptor image, int col) { + if (!label_done) { + if (image_desc == null) { + int cnt = columns == null ? 1 : columns.length; + image_desc = new ImageDescriptor[cnt]; + } + image_desc[col] = image; + } + } + + public void setForeground(RGB rgb, int col) { + if (!label_done) { + if (fg_color == null) { + int cnt = columns == null ? 1 : columns.length; + fg_color = new RGB[cnt]; + } + fg_color[col] = rgb; + } + } + + public void setBackground(RGB rgb, int col) { + if (!label_done) { + if (bg_color == null) { + int cnt = columns == null ? 1 : columns.length; + bg_color = new RGB[cnt]; + } + bg_color[col] = rgb; + } + } + + public int getOffset() { + return 0; + } + + public int getLength() { + return children.length; + } + + public void setChild(Object child, int offset) { + if (!children_done) { + children[offset] = (TCFNode)child; + } + } + + public void setChildCount(int cnt) { + if (!children_done) { + children = new TCFNode[cnt]; + } + } + + public void run() { + Runnable d = done; + update = null; + done = null; + for (Runnable r : waiting_list) Protocol.invokeLater(r); + waiting_list.clear(); + d.run(); + } + } + + private PresentationData data; + + TCFSnapshot(IPresentationContext ctx) { + this.ctx = ctx; + columns = ctx.getColumns(); + } + + void dispose() { + for (PresentationData d : cache.values()) { + for (Runnable r : d.waiting_list) Protocol.invokeLater(r); + } + cache.clear(); + } + + /** + * Retrieve children count for a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - children count update request. + * @param node - debug model node. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + public boolean getData(IChildrenCountUpdate update, TCFNode node, Runnable done) { + if (!getChildren(update, node, done)) return false; + update.setChildCount(data.children.length); + return true; + } + + /** + * Retrieve children for a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - children update request. + * @param node - debug model node. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + public boolean getData(IChildrenUpdate update, TCFNode node, Runnable done) { + if (!getChildren(update, node, done)) return false; + int offset = 0; + int r_offset = update.getOffset(); + int r_length = update.getLength(); + for (TCFNode n : data.children) { + if (offset >= r_offset && offset < r_offset + r_length) { + update.setChild(n, offset); + } + offset++; + } + return true; + } + + /** + * Check if the node has children in a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - "has children" update request. + * @param node - debug model node. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + public boolean getData(IHasChildrenUpdate update, TCFNode node, Runnable done) { + if (!getChildren(update, node, done)) return false; + update.setHasChilren(data.children.length > 0); + return true; + } + + /** + * Retrieve node label for a presentation context. + * The method is always called on TCF dispatch thread. + * @param update - label update request. + * @param node - debug model node. + * @param done - client call back interface, during data waiting it is + * called every time new portion of data becomes available. + * @return false if waiting data retrieval, true if all done. + */ + public boolean getData(ILabelUpdate update, TCFNode node, Runnable done) { + if (!getLabel(update, node, done)) return false; + String[] ids_update = update.getColumnIds(); + String[] ids_data = columns; + if (ids_update != ids_data && !Arrays.equals(ids_update, ids_data)) { + int n = ids_update == null ? 1 : ids_update.length; + for (int i = 0; i < n; i++) update.setBackground(rgb_stalled, i); + } + else { + if (data.label != null) { + for (int i = 0; i < data.label.length; i++) { + if (data.label[i] != null) update.setLabel(data.label[i], i); + } + } + if (data.font_data != null) { + for (int i = 0; i < data.font_data.length; i++) { + if (data.font_data[i] != null) update.setFontData(data.font_data[i], i); + } + } + if (data.image_desc != null) { + for (int i = 0; i < data.image_desc.length; i++) { + if (data.image_desc[i] != null) update.setImageDescriptor(data.image_desc[i], i); + } + } + if (data.stalled) { + int n = ids_update == null ? 1 : ids_update.length; + for (int i = 0; i < n; i++) update.setForeground(rgb_stalled, i); + } + else { + if (data.fg_color != null) { + for (int i = 0; i < data.fg_color.length; i++) { + if (data.fg_color[i] != null) update.setForeground(data.fg_color[i], i); + } + } + } + if (!ignore_bg_color && data.bg_color != null) { + for (int i = 0; i < data.bg_color.length; i++) { + if (data.bg_color[i] != null) update.setBackground(data.bg_color[i], i); + } + } + } + return true; + } + + private boolean getChildren(IViewerUpdate update, TCFNode node, Runnable done) { + data = cache.get(node); + if (data == null) cache.put(node, data = new PresentationData()); + assert data.update != update; + if (data.children_done) return true; + if (data.update != null) { + data.waiting_list.add(done); + return false; + } + data.update = update; + data.done = done; + if (data.children == null) { + if (!node.getData((IChildrenCountUpdate)data, data)) return false; + assert data.children != null; + } + if (!node.getData((IChildrenUpdate)data, data)) return false; + data.children_done = true; + data.update = null; + data.done = null; + return true; + } + + private boolean getLabel(IViewerUpdate update, TCFNode node, Runnable done) { + data = cache.get(node); + if (data == null) cache.put(node, data = new PresentationData()); + assert data.update != update; + if (data.label_done && data.stalled) return true; + if (data.update != null) { + data.waiting_list.add(done); + return false; + } + data.update = update; + data.done = done; + if (!node.getData((ILabelUpdate)data, data)) return false; + data.label_done = true; + data.update = null; + data.done = null; + return true; + } +} |