/******************************************************************************* * Copyright (c) 2011, 2015 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.properties; import java.io.File; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IMarker; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupParticipant; import org.eclipse.tcf.internal.debug.model.TCFBreakpointsModel; import org.eclipse.tcf.internal.debug.model.TCFBreakpointsStatus; import org.eclipse.tcf.internal.debug.model.TCFLaunch; import org.eclipse.tcf.internal.debug.model.TCFSourceRef; import org.eclipse.tcf.internal.debug.ui.ImageCache; import org.eclipse.tcf.internal.debug.ui.model.TCFModel; import org.eclipse.tcf.internal.debug.ui.model.TCFModelManager; import org.eclipse.tcf.internal.debug.ui.model.TCFNode; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeLaunch; import org.eclipse.tcf.protocol.JSON; import org.eclipse.tcf.services.IBreakpoints; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.util.TCFDataCache; import org.eclipse.tcf.util.TCFTask; import org.eclipse.ui.dialogs.PropertyPage; public class TCFBreakpointStatusPage extends PropertyPage { private TreeViewer viewer; private List<StatusItem> status; private static class StatusItem implements Comparable<StatusItem> { Object object; IMarker marker; String text; boolean has_state; boolean planted_ok; List<StatusItem> children; StatusItem parent; void add(StatusItem i) { i.parent = this; if (children == null) children = new ArrayList<StatusItem>(); children.add(i); } void add(String text) { StatusItem i = new StatusItem(); i.text = text; add(i); } @SuppressWarnings({ "rawtypes", "unchecked" }) public int compareTo(StatusItem n) { if (object instanceof TCFNode) { if (n.object instanceof TCFNode) { return ((Comparable)object).compareTo(n.object); } return 1; } if (n.object instanceof TCFNode) return -1; return 0; } @Override public boolean equals(Object o) { if (o instanceof StatusItem) { return compareTo((StatusItem)o) == 0; } return false; } } private class StatusCache extends TCFDataCache<StatusItem> { final TCFLaunch launch; TCFDataCache<?> pending; public StatusCache(TCFLaunch launch) { super(launch.getChannel()); this.launch = launch; } @Override protected boolean startDataRetrieval() { pending = null; TCFBreakpointsStatus status = launch.getBreakpointsStatus(); if (status == null) { set(null, null, null); return true; } Map<String,Object> map = status.getStatus(getBreakpoint()); if (map == null || map.size() == 0) { set(null, null, null); return true; } StatusItem x = new StatusItem(); x.object = launch; Object planted = map.get(IBreakpoints.STATUS_INSTANCES); if (planted != null) { TCFModel model = TCFModelManager.getModelManager().getModel(launch); for (Object o : toObjectArray(planted)) { Map<String,Object> m = toObjectMap(o); String ctx_id = (String)m.get(IBreakpoints.INSTANCE_CONTEXT); if (!model.createNode(ctx_id, this)) return false; if (isValid()) { /* Invalid context ID, ignore */ reset(); continue; } StatusItem y = getNodeItem(x, model.getNode(ctx_id)); if (y == null) continue; StatusItem z = new StatusItem(); z.marker = getBreakpoint().getMarker(); z.text = z.marker.getAttribute(TCFBreakpointsModel.ATTR_MESSAGE, ""); String error = (String)m.get(IBreakpoints.INSTANCE_ERROR); if (error != null) z.add("Error: " + error); String condition_error = (String)m.get(IBreakpoints.INSTANCE_CONDITION_ERROR); if (condition_error != null) z.add("Condition evaluation error: " + condition_error); Number addr = (Number)m.get(IBreakpoints.INSTANCE_ADDRESS); z.planted_ok = error == null; if (addr != null) { BigInteger i = JSON.toBigInteger(addr); z.add("Address: 0x" + i.toString(16)); } Number size = (Number)m.get(IBreakpoints.INSTANCE_SIZE); if (size != null) z.add("Size: " + size); String type = (String)m.get(IBreakpoints.INSTANCE_TYPE); if (type != null) z.add("Type: " + type); if (addr != null && y.object instanceof TCFNode) { TCFDataCache<TCFNodeExecContext> mem = model.searchMemoryContext((TCFNode)y.object); if (mem != null) { if (!mem.validate()) { pending = mem; } else { TCFNodeExecContext ctx = mem.getData(); if (ctx != null) { BigInteger i = JSON.toBigInteger(addr); TCFDataCache<TCFSourceRef> ln_cache = ctx.getLineInfo(i); if (ln_cache != null) { if (!ln_cache.validate()) { pending = ln_cache; } else { addLocationInfo(z, ln_cache.getData()); } } } } } } String mem_id = (String)m.get(IBreakpoints.INSTANCE_MEMORY_CONTEXT); if (mem_id != null) { if (!model.createNode(mem_id, this)) return false; if (isValid()) reset(); else addMemoryContext(z, model.getNode(mem_id)); } Number hit_count = (Number)m.get(IBreakpoints.INSTANCE_HIT_COUNT); if (hit_count != null) z.add("Hit count: " + hit_count); y.add(z); } } if (pending != null) { pending.wait(this); return false; } String error = (String)map.get(IBreakpoints.STATUS_ERROR); if (error != null) { StatusItem y = new StatusItem(); y.text = error; x.add(y); } set(null, null, x); return true; } private void addLocationInfo(StatusItem z, TCFSourceRef ref) { if (ref == null) return; if (ref.area == null) return; if (ref.area.file == null) return; String req_file = z.marker.getAttribute(TCFBreakpointsModel.ATTR_REQESTED_FILE, null); if (req_file == null) req_file = z.marker.getAttribute(TCFBreakpointsModel.ATTR_FILE, null); int req_line = z.marker.getAttribute(TCFBreakpointsModel.ATTR_REQESTED_LINE, -1); if (req_line < 0) req_line = z.marker.getAttribute(TCFBreakpointsModel.ATTR_LINE, -1); int req_char = z.marker.getAttribute(TCFBreakpointsModel.ATTR_REQESTED_CHAR, -1); if (req_char < 0) req_char = z.marker.getAttribute(TCFBreakpointsModel.ATTR_CHAR, -1); String area_file = TCFSourceLookupParticipant.toFileName(ref.area); if (req_file != null && req_line >= 0) { String req_file_name = new File(req_file).getName(); String file_name = new File(ref.area.file).getName(); if (!req_file_name.equals(file_name) || req_line != ref.area.start_line) { addLocationInfo(z, "Requested location", req_file, req_line, req_char); addLocationInfo(z, "Adjusted location", area_file, ref.area.start_line, ref.area.start_column); return; } } addLocationInfo(z, "Location", area_file, ref.area.start_line, ref.area.start_column); } private void addLocationInfo(StatusItem z, String name, String file, int line, int column) { String text = name + ": " + file; text += "; line: " + line; if (column > 0) text += "; column: " + column; z.add(text); } private void addMemoryContext(StatusItem z, TCFNode node) { if (node instanceof TCFNodeExecContext) { TCFNodeExecContext exe_node = (TCFNodeExecContext)node; TCFDataCache<String> cache = exe_node.getFullName(); if (!cache.validate()) { pending = cache; return; } z.add("Memory context: " + cache.getData()); } } private StatusItem getNodeItem(StatusItem root, TCFNode node) { TCFNode parent = node.getParent(); if (parent == null) return root; StatusItem x = null; // parent status item Set<String> filter = launch.getContextFilter(); if (filter != null) { if (filter.contains(node.getID())) x = root; else if (parent instanceof TCFNodeLaunch) return null; } if (x == null) x = getNodeItem(root, parent); if (x == null) return null; if (x.children != null) { for (StatusItem y : x.children) { if (y.object == node) return y; } } StatusItem y = new StatusItem(); y.object = node; TCFDataCache<IRunControl.RunControlContext> cache = ((TCFNodeExecContext)node).getRunContext(); if (!cache.validate()) { pending = cache; } else { IRunControl.RunControlContext ctx = cache.getData(); if (ctx != null) { y.text = ctx.getName(); if (y.text == null) y.text = node.getID(); String additionalInfo = (String) ctx.getProperties().get("AdditionalInfo"); if (additionalInfo != null) y.text += additionalInfo; y.has_state = ctx.hasState(); } else { y.text = node.getID(); } } x.add(y); return y; } } private final ITreeContentProvider content_provider = new ITreeContentProvider() { public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } public Object[] getElements(Object input) { return status.toArray(new StatusItem[status.size()]); } public Object[] getChildren(Object parent) { StatusItem x = (StatusItem)parent; if (x.children == null) return new Object[0]; Object[] arr = x.children.toArray(new StatusItem[x.children.size()]); Arrays.sort(arr); return arr; } public Object getParent(Object element) { StatusItem x = (StatusItem)element; return x.parent; } public boolean hasChildren(Object element) { StatusItem x = (StatusItem)element; return x.children != null && x.children.size() > 0; } }; private final LabelProvider label_provider = new LabelProvider() { @Override public Image getImage(Object element) { StatusItem x = (StatusItem)element; if (x.object instanceof ILaunch) { ImageDescriptor desc = DebugUITools.getDefaultImageDescriptor(x.object); if (desc != null) return ImageCache.getImage(desc); } if (x.has_state) return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING); if (x.object != null) return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_DEBUG_TARGET); if (x.marker != null) { if (x.planted_ok) return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_BREAKPOINT); return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_BREAKPOINT_DISABLED); } return null; } @Override public String getText(Object element) { StatusItem x = (StatusItem)element; if (x.object instanceof ILaunch) { ILaunchConfiguration cfg = ((ILaunch)x.object).getLaunchConfiguration(); if (cfg != null) return cfg.getName(); } return x.text; } }; @Override protected Control createContents(Composite parent) { noDefaultAndApplyButton(); Composite composite = new Composite(parent, SWT.NONE); composite.setFont(parent.getFont()); composite.setLayout(new GridLayout()); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); status = getCurrentStatus(); createStatusViewer(composite); setValid(true); return composite; } private void createStatusViewer(Composite parent) { Label label = new Label(parent, SWT.NONE); label.setText("Breakpoint planting status:"); label.setFont(parent.getFont()); label.setLayoutData(new GridData()); GridData data = new GridData(GridData.FILL_BOTH); data.heightHint = 100; viewer = new TreeViewer(parent, SWT.BORDER); viewer.getTree().setLayoutData(data); viewer.getTree().setFont(parent.getFont()); viewer.setContentProvider(content_provider); viewer.setLabelProvider(label_provider); viewer.setInput(this); viewer.expandAll(); } private IBreakpoint getBreakpoint() { return (IBreakpoint)getElement().getAdapter(IBreakpoint.class); } private List<StatusItem> getCurrentStatus() { final List<StatusCache> caches = new ArrayList<StatusCache>(); final ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches(); for (ILaunch launch : launches) { if (!(launch instanceof TCFLaunch)) continue; TCFLaunch tcf_launch = (TCFLaunch)launch; if (tcf_launch.isConnecting()) continue; if (tcf_launch.isDisconnected()) continue; caches.add(new StatusCache(tcf_launch)); } List<StatusItem> status = new TCFTask<List<StatusItem>>(10000) { public void run() { StatusCache pending = null; for (StatusCache cache : caches) { if (!cache.validate()) pending = cache; } if (pending != null) { pending.wait(this); return; } List<StatusItem> roots = new ArrayList<StatusItem>(); for (StatusCache cache : caches) { StatusItem x = cache.getData(); if (x != null) roots.add(x); } for (StatusCache cache : caches) cache.dispose(); if (roots.size() == 0) { StatusItem x = new StatusItem(); x.text = "Not planted"; roots.add(x); } done(roots); } }.getE(); return status; } @SuppressWarnings("unchecked") private Object[] toObjectArray(Object o) { 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; } }