/*******************************************************************************
* 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.adapters;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.debug.ui.IDebugView;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.widgets.Display;
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.model.TCFModel;
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.TCFNodeExecContext.MemoryRegion;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExpression;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeLaunch;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeModule;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeRegister;
import org.eclipse.tcf.internal.debug.ui.model.TCFNodeStackFrame;
import org.eclipse.tcf.protocol.IChannel;
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.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.IViewPart;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;
import org.eclipse.ui.views.properties.PropertySheet;
import org.eclipse.ui.views.properties.PropertySheetPage;
/**
* Adapts TCFNode to IPropertySource.
*/
public class TCFNodePropertySource implements IPropertySource {
private final TCFNode node;
private final Map<String,Object> properties = new HashMap<String,Object>();
private IPropertyDescriptor[] descriptors;
public TCFNodePropertySource(TCFNode node) {
this.node = node;
}
public Object getEditableValue() {
return null;
}
public IPropertyDescriptor[] getPropertyDescriptors() {
if (descriptors == null) {
if (node == null) {
// A disconnected TCF launch was selected
return descriptors = new IPropertyDescriptor[0];
}
try {
final List<IPropertyDescriptor> list = new ArrayList<IPropertyDescriptor>();
descriptors = new TCFTask<IPropertyDescriptor[]>(node.getChannel()) {
public void run() {
list.clear();
properties.clear();
if (node instanceof TCFNodeLaunch) {
getLaunchDescriptors((TCFNodeLaunch)node);
}
else if (node instanceof TCFNodeExecContext) {
getExecContextDescriptors((TCFNodeExecContext)node);
}
else if (node instanceof TCFNodeStackFrame) {
getFrameDescriptors((TCFNodeStackFrame)node);
}
else if (node instanceof TCFNodeRegister) {
getRegisterDescriptors((TCFNodeRegister)node);
}
else if (node instanceof TCFNodeModule) {
getModuleDescriptors((TCFNodeModule)node);
}
else if (node instanceof TCFNodeExpression) {
getExpressionDescriptors((TCFNodeExpression)node);
}
else {
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
}
private void getExpressionDescriptors(TCFNodeExpression exp_node) {
TCFDataCache<IExpressions.Expression> exp_cache = exp_node.getExpression();
if (!exp_cache.validate(this)) return;
TCFDataCache<ISymbols.Symbol> type_cache = exp_node.getType();
if (!type_cache.validate(this)) return;
TCFDataCache<IExpressions.Value> value_cache = exp_node.getValue();
if (!value_cache.validate(this)) return;
Throwable exp_error = exp_cache.getError();
if (exp_error != null) addDescriptor("Expression", "Error", TCFModel.getErrorMessage(exp_error, false));
IExpressions.Expression exp_data = exp_cache.getData();
if (exp_data != null) {
Map<String,Object> props = exp_data.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Expression", key, value);
}
}
Throwable type_error = type_cache.getError();
if (type_error != null) addDescriptor("Expression Type", "Error", TCFModel.getErrorMessage(type_error, false));
ISymbols.Symbol type_data = type_cache.getData();
if (type_data != null) {
Map<String,Object> props = type_data.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Expression Type", key, value);
}
}
Throwable value_error = value_cache.getError();
if (value_error != null) addDescriptor("Expression Value", "Error", TCFModel.getErrorMessage(value_error, false));
IExpressions.Value value_data = value_cache.getData();
if (value_data != null) {
Map<String,Object> props = value_data.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Expression Value", key, value);
}
String sym_id = value_data.getSymbolID();
if (sym_id != null) {
TCFDataCache<ISymbols.Symbol> sym_cache = exp_node.getModel().getSymbolInfoCache(sym_id);
if (!sym_cache.validate(this)) return;
Throwable sym_error = sym_cache.getError();
if (sym_error != null) addDescriptor("Expression Value Symbol", "Error", TCFModel.getErrorMessage(sym_error, false));
ISymbols.Symbol sym_data = sym_cache.getData();
if (sym_data != null) {
props = sym_data.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Expression Value Symbol", key, value);
}
}
}
}
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
private void getModuleDescriptors(TCFNodeModule mod_node) {
TCFDataCache<MemoryRegion> mod_cache = mod_node.getRegion();
if (!mod_cache.validate(this)) return;
Throwable error = mod_cache.getError();
if (error != null) addDescriptor("Module Properties", "Error", TCFModel.getErrorMessage(error, false));
MemoryRegion ctx = mod_cache.getData();
if (ctx != null && ctx.region != null) {
Map<String,Object> props = ctx.region.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Module Properties", key, value);
}
}
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
private void getRegisterDescriptors(TCFNodeRegister reg_node) {
TCFDataCache<IRegisters.RegistersContext> ctx_cache = reg_node.getContext();
if (!ctx_cache.validate(this)) return;
Throwable error = ctx_cache.getError();
if (error != null) addDescriptor("Register Properties", "Error", TCFModel.getErrorMessage(error, false));
IRegisters.RegistersContext ctx = ctx_cache.getData();
if (ctx != null) {
Map<String,Object> props = ctx.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Register Properties", key, value);
}
}
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
private void getFrameDescriptors(TCFNodeStackFrame frameNode) {
TCFDataCache<IStackTrace.StackTraceContext> ctx_cache = frameNode.getStackTraceContext();
TCFDataCache<TCFSourceRef> line_info_cache = frameNode.getLineInfo();
if (!validateAll(ctx_cache, line_info_cache)) return;
IStackTrace.StackTraceContext ctx = ctx_cache.getData();
if (ctx != null) {
Map<String,Object> props = ctx.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Stack Frame Properties", key, value);
}
}
TCFSourceRef ref = line_info_cache.getData();
if (ref != null) {
if (ref.area != null) {
if (ref.area.directory != null) addDescriptor("Source", "Directory", ref.area.directory);
if (ref.area.file != null) addDescriptor("Source", "File", ref.area.file);
if (ref.area.start_line > 0) addDescriptor("Source", "Line", ref.area.start_line);
if (ref.area.start_column > 0) addDescriptor("Source", "Column", ref.area.start_column);
}
if (ref.error != null) {
addDescriptor("Source", "Error", TCFModel.getErrorMessage(ref.error, false));
}
}
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
private void getExecContextDescriptors(TCFNodeExecContext exe_node) {
TCFDataCache<IMemory.MemoryContext> mem_cache = exe_node.getMemoryContext();
TCFDataCache<IRunControl.RunControlContext> ctx_cache = exe_node.getRunContext();
TCFDataCache<TCFContextState> state_cache = exe_node.getState();
TCFDataCache<MemoryRegion[]> mem_map_cache = exe_node.getMemoryMap();
if (!validateAll(mem_cache, ctx_cache, state_cache, mem_map_cache)) return;
IMemory.MemoryContext mem = mem_cache.getData();
if (mem != null) {
Map<String,Object> props = mem.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Memory", key, value);
}
}
IRunControl.RunControlContext ctx = ctx_cache.getData();
if (ctx != null) {
Map<String,Object> props = ctx.getProperties();
for (String key : props.keySet()) {
Object value = props.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("Context", key, value);
}
}
TCFContextState state = state_cache.getData();
if (state != null) {
addDescriptor("State", "Suspended", state.is_suspended);
if (state.suspend_reason != null) addDescriptor("State", "Suspend reason", state.suspend_reason);
if (state.suspend_pc != null) addDescriptor("State", "PC", toHexAddrString(new BigInteger(state.suspend_pc)));
addDescriptor("State", "Active", !exe_node.isNotActive());
if (state.suspend_params != null) {
for (String key : state.suspend_params.keySet()) {
Object value = state.suspend_params.get(key);
if (value instanceof Number) {
value = toHexAddrString((Number)value);
}
addDescriptor("State Properties", key, value);
}
}
}
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
private void getLaunchDescriptors(TCFNodeLaunch launch_node) {
IChannel channel = launch_node.getChannel();
for (String s : channel.getRemoteServices()) {
addDescriptor("Target Services", s, "");
}
TCFLaunch launch = launch_node.getModel().getLaunch();
Set<String> filter = launch.getContextFilter();
if (filter != null) addDescriptor("Context Filter", "Context IDs", filter.toString());
done(list.toArray(new IPropertyDescriptor[list.size()]));
}
private void addDescriptor(String category, String key, Object value) {
String id = category + '.' + key;
PropertyDescriptor desc = new PropertyDescriptor(id, key);
desc.setCategory(category);
list.add(desc);
properties.put(id, value);
}
private boolean validateAll(TCFDataCache<?>... caches) {
TCFDataCache<?> pending = null;
for (TCFDataCache<?> cache : caches) {
if (!cache.validate()) pending = cache;
}
if (pending != null) {
pending.wait(this);
return false;
}
return true;
}
@Override
public void done(IPropertyDescriptor[] r) {
need_refresh = true;
super.done(r);
}
}.get();
}
catch (Exception e) {
if (node.getChannel().getState() != IChannel.STATE_CLOSED) {
Activator.log("Error retrieving property data", e);
}
descriptors = new IPropertyDescriptor[0];
}
}
return descriptors;
}
public Object getPropertyValue(final Object id) {
return properties.get(id);
}
public boolean isPropertySet(Object id) {
return false;
}
public void resetPropertyValue(Object id) {
}
public void setPropertyValue(Object id, Object value) {
}
private static String toHexAddrString(Number num) {
BigInteger n = JSON.toBigInteger(num);
String s = n.toString(16);
int sz = s.length() > 8 ? 16 : 8;
int l = sz - s.length();
if (l < 0) l = 0;
if (l > 16) l = 16;
return "0x0000000000000000".substring(0, 2 + l) + s;
}
private static final long REFRESH_DELAY = 250;
private static boolean refresh_posted = false;
private static long refresh_time = 0;
private static boolean need_refresh = false;
public static void refresh(TCFNode node) {
assert Protocol.isDispatchThread();
if (!need_refresh) return;
refresh_time = System.currentTimeMillis();
if (refresh_posted) return;
refresh_posted = true;
Protocol.invokeLater(REFRESH_DELAY, new Runnable() {
public void run() {
long time = System.currentTimeMillis();
if (time - refresh_time < REFRESH_DELAY * 3 / 4) {
Protocol.invokeLater(refresh_time + REFRESH_DELAY - time, this);
return;
}
refresh_posted = false;
need_refresh = false;
synchronized (Device.class) {
Display display = Display.getDefault();
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
public void run() {
for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
IWorkbenchPart active_part = window.getActivePage().getActivePart();
if (active_part instanceof IDebugView) {
IViewPart part = window.getActivePage().findView("org.eclipse.ui.views.PropertySheet");
if (part instanceof PropertySheet) {
PropertySheet props = (PropertySheet)part;
PropertySheetPage page = (PropertySheetPage)props.getCurrentPage();
// TODO: need to check Properties view selection to skip unnecessary refreshes
if (page != null) page.refresh();
}
}
}
}
});
}
}
}
});
}
}