diff options
Diffstat (limited to 'plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java')
-rw-r--r-- | plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java | 550 |
1 files changed, 550 insertions, 0 deletions
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..250e28031 --- /dev/null +++ b/plugins/org.eclipse.tcf.debug.ui/src/org/eclipse/tcf/internal/debug/ui/model/TCFNodeStackFrame.java @@ -0,0 +1,550 @@ +/******************************************************************************* + * 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.tm.internal.tcf.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.tm.internal.tcf.debug.model.TCFContextState; +import org.eclipse.tm.internal.tcf.debug.model.TCFFunctionRef; +import org.eclipse.tm.internal.tcf.debug.model.TCFSourceRef; +import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; +import org.eclipse.tm.tcf.protocol.IToken; +import org.eclipse.tm.tcf.protocol.JSON; +import org.eclipse.tm.tcf.protocol.Protocol; +import org.eclipse.tm.tcf.services.IExpressions; +import org.eclipse.tm.tcf.services.IStackTrace; +import org.eclipse.tm.tcf.services.ISymbols; +import org.eclipse.tm.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; + + private String hover_expression; + + 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) { + if (expression != hover_expression && (expression == null || !expression.equals(hover_expression))) { + hover_expression = expression; + children_hover_exps.cancel(); + } + return children_hover_exps; + } + + String getHoverExpression() { + return hover_expression; + } + + 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); + } +} |