/*******************************************************************************
* Copyright (c) 2007, 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.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.tcf.debug.ui.ITCFStackFrame;
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.ColorCache;
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.ILineNumbers;
import org.eclipse.tcf.services.IMemory;
import org.eclipse.tcf.services.IStackTrace;
import org.eclipse.tcf.services.ISymbols;
import org.eclipse.tcf.util.TCFDataCache;
public class TCFNodeStackFrame extends TCFNode implements ITCFStackFrame {
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;
// At first, validate stack trace to make sure frame_no is valid
TCFChildrenStackTrace stack_trace_cache = parent.getStackTrace();
if (!stack_trace_cache.validate(this)) return false;
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;
}
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) {
if (context != null && context.length == 1) model.getContextMap().put(id, context[0]);
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;
IStackTrace.StackTraceContext ctx = stack_trace_context.getData();
if (ctx == null) {
set(null, stack_trace_context.getError(), null);
return true;
}
TCFDataCache<TCFNodeExecContext> mem_node_cache = ((TCFNodeExecContext)parent).getMemoryNode();
if (!mem_node_cache.validate(this)) return false;
if (mem_node_cache.getError() != null || mem_node_cache.getData() == null) {
set(null, mem_node_cache.getError(), null);
return true;
}
TCFNodeExecContext mem_node = mem_node_cache.getData();
ILineNumbers.CodeArea area = ctx.getCodeArea();
if (area != null) {
IMemory.MemoryContext mem_ctx_data = null;
if (mem_node != null) {
TCFDataCache<IMemory.MemoryContext> mem_ctx_cache = mem_node.getMemoryContext();
if (!mem_ctx_cache.validate(this)) return false;
mem_ctx_data = mem_ctx_cache.getData();
}
final TCFSourceRef ref_data = new TCFSourceRef();
if (mem_ctx_data != null) {
ref_data.context_id = mem_ctx_data.getID();
ref_data.address_size = mem_ctx_data.getAddressSize();
}
ref_data.area = area;
set(null, null, ref_data);
}
else {
if (!address.validate(this)) return false;
BigInteger n = address.getData();
if (n == null) {
set(null, address.getError(), null);
return true;
}
assert parent.getStackTrace().isValid();
if (frame_no > 0) n = n.subtract(BigInteger.valueOf(1));
TCFDataCache<TCFSourceRef> info_cache = mem_node.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 (!stack_trace_context.validate(this)) return false;
IStackTrace.StackTraceContext ctx = stack_trace_context.getData();
if (ctx == null) {
set(null, stack_trace_context.getError(), null);
return true;
}
TCFDataCache<TCFNodeExecContext> mem_node_cache = ((TCFNodeExecContext)parent).getMemoryNode();
if (!mem_node_cache.validate(this)) return false;
if (mem_node_cache.getError() != null || mem_node_cache.getData() == null) {
set(null, mem_node_cache.getError(), null);
return true;
}
TCFNodeExecContext mem_node = mem_node_cache.getData();
String func_id = ctx.getFuncID();
if (func_id != null) {
IMemory.MemoryContext mem_ctx_data = null;
if (mem_node != null) {
TCFDataCache<IMemory.MemoryContext> mem_ctx_cache = mem_node.getMemoryContext();
if (!mem_ctx_cache.validate(this)) return false;
mem_ctx_data = mem_ctx_cache.getData();
}
TCFFunctionRef ref = new TCFFunctionRef();
if (mem_ctx_data != null) {
ref.context_id = mem_ctx_data.getID();
ref.address_size = mem_ctx_data.getAddressSize();
}
ref.symbol_id = func_id;
set(null, null, ref);
}
else {
assert parent.getStackTrace().isValid();
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<TCFFunctionRef> info_cache = mem_node.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;
}
}
assert parent.getStackTrace().isValid();
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();
assert ((TCFNodeExecContext)parent).getStackTrace().isValid();
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;
}
public 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) {
BigInteger n = addr;
assert ((TCFNodeExecContext)parent).getStackTrace().isValid();
if (frame_no > 0) n = n.subtract(BigInteger.valueOf(1));
for (TCFNodeExecContext.MemoryRegion r : map) {
String fnm = r.region.getFileName();
if (fnm != null && r.contains(n)) {
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 = sym.getName();
if (name == null) name = "?";
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(ColorCache.rgb_error, 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 = null;
if (state == null) image_name = ImageCache.IMG_STACK_FRAME_SUSPENDED;
else if (state.is_suspended) image_name = ImageCache.IMG_STACK_FRAME_SUSPENDED;
else if (state.isReversing()) image_name = ImageCache.IMG_STACK_FRAME_REVERSING;
else image_name = 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(boolean func_call) {
stack_trace_context.cancel();
line_info.cancel();
func_info.cancel();
address.cancel();
if (!func_call) {
// Unlike thread registers, stack frame register list must be retrieved on every suspend
children_regs.reset();
}
children_regs.onSuspended(func_call);
children_vars.onSuspended(func_call);
children_exps.onSuspended(func_call);
children_hover_exps.onSuspended(func_call);
// delta is posted by the parent node
}
void onMemoryMapChanged() {
stack_trace_context.cancel();
line_info.reset();
func_info.reset();
address.cancel();
children_vars.onMemoryMapChanged();
children_exps.onMemoryMapChanged();
children_hover_exps.onMemoryMapChanged();
if (!((TCFNodeExecContext)parent).getStackTrace().isValid() || frame_no > 0) {
children_regs.onRegistersChanged();
}
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();
if (frame_no > 0) children_regs.onRegistersChanged();
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);
}
}