1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-22 06:02:11 +02:00

Bug 489512 - Show local variables in the traditional memory render

Change-Id: I48d633e8f6979fd91e9150856fd77fccffdee299
This commit is contained in:
Alvaro Sanchez-Leon 2016-03-13 17:48:33 -04:00
parent d608db2e7a
commit e21fc12f90
16 changed files with 1551 additions and 33 deletions

View file

@ -22,7 +22,8 @@ Export-Package: org.eclipse.cdt.debug.core,
x-friends:="org.eclipse.cdt.dsf.gdb, x-friends:="org.eclipse.cdt.dsf.gdb,
org.eclipse.cdt.dsf.gdb.ui, org.eclipse.cdt.dsf.gdb.ui,
org.eclipse.cdt.dsf, org.eclipse.cdt.dsf,
org.eclipse.cdt.dsf.ui", org.eclipse.cdt.dsf.ui,
org.eclipse.cdt.debug.ui.memory.traditional",
org.eclipse.cdt.debug.internal.core.breakpoints;x-friends:="org.eclipse.cdt.debug.edc,org.eclipse.cdt.dsf.gdb", org.eclipse.cdt.debug.internal.core.breakpoints;x-friends:="org.eclipse.cdt.debug.edc,org.eclipse.cdt.dsf.gdb",
org.eclipse.cdt.debug.internal.core.disassembly;x-internal:=true, org.eclipse.cdt.debug.internal.core.disassembly;x-internal:=true,
org.eclipse.cdt.debug.internal.core.executables;x-internal:=true, org.eclipse.cdt.debug.internal.core.executables;x-internal:=true,

View file

@ -0,0 +1,147 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.core.model;
import java.math.BigInteger;
import org.eclipse.debug.core.IRequest;
import org.eclipse.debug.core.model.IMemoryBlock;
/**
* An interface that offers the possibility to request information related to addresses for a given memory block
* and within a specific context. It also offers the possibility to register listeners, listeners that can receive
* notifications of changes/updates to the address information
*
* @since 8.0
*/
public interface IMemoryBlockAddressInfoRetrieval {
/**
* An indication of the type of change which may render the current memory address information out of date
*/
enum EventType {
STOPPED, RESUMED, VALUE_CHANGED
}
/**
* Information item for a memory address or range
*/
public interface IMemoryBlockAddressInfoItem {
/**
* @return The unique id for this item
*/
String getId();
/**
* @return The type of information
*/
String getInfoType();
/**
* @return The label or name for this entry
*/
String getLabel();
/**
* Update the label or name for this entry
*/
void setLabel(String label);
/**
* @return The memory address this information is associated to
*/
BigInteger getAddress();
/**
* @param address Set / Update this item's address
*/
void setAddress(BigInteger address);
/**
* @return The range of addressable units this information applies to
*/
BigInteger getRangeInAddressableUnits();
void setRangeInAddressableUnits(BigInteger length);
/**
* @return A preferred color to mark this entry
*/
int getRegionRGBColor();
void setRegionRGBColor(int color);
}
/**
* An async request for information items, triggering the callback via the method done().
* The method done() is expected to be overridden so when the request is successful this additional API
* can be used to retrieve the item information collected.
*/
public interface IGetMemoryBlockAddressInfoReq extends IRequest {
/**
* @return The different types of items available
*/
String[] getAddressInfoItemTypes();
/**
* @return The subset of items of the given type
*/
IMemoryBlockAddressInfoItem[] getAddressInfoItems(String type);
/**
* @return The full set of items i.e. including all types
*/
IMemoryBlockAddressInfoItem[] getAllAddressInfoItems();
/**
* Sets the address information items of the given type
*/
void setAddressInfoItems(String type, IMemoryBlockAddressInfoItem[] items);
}
/**
* Call-back interface used to receive notification of changes to address information items
*/
public interface IAddressInfoUpdateListener {
/**
* This method will be called for each registered listener, when there is a session change that may render
* previous memory address information out of date
* @param update optional General purpose update object to e.g. determine changed values
*/
void handleAddressInfoUpdate(EventType type, Object update);
}
/**
* Triggers an asynchronous request for Memory block address information
*
* @param context A reference to a session context
* @param memoryBlock The memory block to be used in conjunction with the requested Address information
* @param request This is the async request instance. Overriding its method "done()" allows to read and
* use the information items collected
*/
void getMemoryBlockAddressInfo(Object context, IMemoryBlock memoryBlock, IGetMemoryBlockAddressInfoReq request);
/**
* Register a listener so it can receive notifications of changes to address information items
*
* @param listener
*/
void addAddressInfoUpdateListener(IAddressInfoUpdateListener listener);
/**
* Removes a listener so it no longer receives notifications
*
* @param The listener to remove. Nothing will happen if that listener is not registered already
*/
void removeAddressInfoUpdateListener(IAddressInfoUpdateListener listener);
}

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2015 Ericsson and others. * Copyright (c) 2016 Ericsson and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -23,6 +23,7 @@ import java.util.Map;
import org.eclipse.cdt.debug.core.model.ICBreakpoint; import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.cdt.debug.core.model.IConnectHandler; import org.eclipse.cdt.debug.core.model.IConnectHandler;
import org.eclipse.cdt.debug.core.model.IDebugNewExecutableHandler; import org.eclipse.cdt.debug.core.model.IDebugNewExecutableHandler;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval;
import org.eclipse.cdt.debug.core.model.IResumeWithoutSignalHandler; import org.eclipse.cdt.debug.core.model.IResumeWithoutSignalHandler;
import org.eclipse.cdt.debug.core.model.IReverseResumeHandler; import org.eclipse.cdt.debug.core.model.IReverseResumeHandler;
import org.eclipse.cdt.debug.core.model.IReverseStepIntoHandler; import org.eclipse.cdt.debug.core.model.IReverseStepIntoHandler;
@ -68,6 +69,7 @@ import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbSelectPrevTraceRecordComm
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbStartTracingCommand; import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbStartTracingCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbStopTracingCommand; import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbStopTracingCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbUncallCommand; import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbUncallCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.memory.GdbMemoryBlockAddressInfoRetrieval;
import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbViewModelAdapter; import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbViewModelAdapter;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate;
import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.service.DsfSession;
@ -214,7 +216,8 @@ public class GdbSessionAdapters {
IPinProvider.class, IPinProvider.class,
IDebugModelProvider.class, IDebugModelProvider.class,
ILaunch.class, ILaunch.class,
ICEditorTextHover.class)); ICEditorTextHover.class,
IMemoryBlockAddressInfoRetrieval.class));
} }
/** /**
@ -363,6 +366,11 @@ public class GdbSessionAdapters {
if (ICEditorTextHover.class.equals(adapterType)) { if (ICEditorTextHover.class.equals(adapterType)) {
return (T)new GdbDebugTextHover(); return (T)new GdbDebugTextHover();
} }
if (IMemoryBlockAddressInfoRetrieval.class.equals(adapterType)) {
return (T) new GdbMemoryBlockAddressInfoRetrieval(session);
}
return null; return null;
} }

View file

@ -0,0 +1,169 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.memory;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.model.DsfMemoryBlock;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterChangedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.gdb.internal.memory.GdbMemoryAddressInfoVariablesRetrieval;
import org.eclipse.cdt.dsf.gdb.memory.IGdbMemoryAddressInfoTypeRetrieval;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.debug.core.model.IMemoryBlock;
public class GdbMemoryBlockAddressInfoRetrieval implements IMemoryBlockAddressInfoRetrieval {
private final DsfSession fSession;
private final Set<IAddressInfoUpdateListener> fListeners = new HashSet<>();
public GdbMemoryBlockAddressInfoRetrieval(DsfSession session) {
fSession = session;
fSession.getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
fSession.addServiceEventListener(GdbMemoryBlockAddressInfoRetrieval.this, null);
}
});
}
protected IGdbMemoryAddressInfoTypeRetrieval[] resolveMemoryAddressInfoProviders() {
return new IGdbMemoryAddressInfoTypeRetrieval[] {new GdbMemoryAddressInfoVariablesRetrieval(fSession)};
}
@Override
public void getMemoryBlockAddressInfo(Object selection, final IMemoryBlock memBlock,
final IGetMemoryBlockAddressInfoReq request) {
IDMContext memBlockContext = null;
if (memBlock instanceof DsfMemoryBlock) {
memBlockContext = ((DsfMemoryBlock) memBlock).getContext();
if (selection instanceof IDMVMContext) {
IDMContext context = ((IDMVMContext) selection).getDMContext();
final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(context, IFrameDMContext.class);
if (frameCtx != null) {
// Resolve container context of selection
IContainerDMContext selectedContainerCtx = DMContexts.getAncestorOfType(frameCtx,
IContainerDMContext.class);
// Resolve container context of memory block
IContainerDMContext memoryContainerCtx = DMContexts.getAncestorOfType(memBlockContext,
IContainerDMContext.class);
// Continue if the selected container matches the container for the memory context
if (memoryContainerCtx != null && memoryContainerCtx.equals(selectedContainerCtx)) {
fSession.getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
// Resolve the memory address info providers
IGdbMemoryAddressInfoTypeRetrieval[] infoTypeProviders = resolveMemoryAddressInfoProviders();
if (infoTypeProviders == null || infoTypeProviders.length == 0) {
// No providers available
request.done();
return;
}
final CountingRequestMonitor crm = new CountingRequestMonitor(fSession.getExecutor(),
null) {
// mark the request done when all available infoTypeProviders have
// returned its information
@Override
protected void handleCompleted() {
request.done();
};
};
for (final IGdbMemoryAddressInfoTypeRetrieval infoProvider : infoTypeProviders) {
infoProvider.itemsRequest(frameCtx, memBlock,
new DataRequestMonitor<IMemoryBlockAddressInfoItem[]>(
fSession.getExecutor(), crm) {
@Override
protected void handleCompleted() {
if (isSuccess()) {
// Load the information from this provider
request.setAddressInfoItems(infoProvider.getInfoType(), getData());
} else {
request.setStatus(getStatus());
}
crm.done();
}
});
}
crm.setDoneCount(infoTypeProviders.length);
}
});
} else {
request.done();
}
} else {
// The selection context does not match the block memory context,
// Simply close the request
request.done();
}
} else {
request.done();
}
} else {
request.done();
}
}
// The GdbSessionAdapters class will call this method automatically when it cleans up
public void dispose() {
fListeners.clear();
}
@Override
public void addAddressInfoUpdateListener(IAddressInfoUpdateListener listener) {
synchronized(fListeners) {
fListeners.add(listener);
}
}
@Override
public void removeAddressInfoUpdateListener(IAddressInfoUpdateListener listener) {
synchronized(fListeners) {
fListeners.remove(listener);
}
}
@DsfServiceEventHandler
public void eventDispatched(IRegisterChangedDMEvent e) {
synchronized(fListeners) {
for (IAddressInfoUpdateListener listener : fListeners) {
listener.handleAddressInfoUpdate(EventType.VALUE_CHANGED, null);
}
}
}
@DsfServiceEventHandler
public void eventDispatched(IExpressionChangedDMEvent e) {
synchronized(fListeners) {
for (IAddressInfoUpdateListener listener : fListeners) {
listener.handleAddressInfoUpdate(EventType.VALUE_CHANGED, null);
}
}
}
}

View file

@ -27,13 +27,14 @@ Export-Package: org.eclipse.cdt.dsf.gdb,
org.eclipse.cdt.tests.dsf.gdb, org.eclipse.cdt.tests.dsf.gdb,
org.eclipse.cdt.examples.dsf.gdb", org.eclipse.cdt.examples.dsf.gdb",
org.eclipse.cdt.dsf.gdb.internal.commands;x-friends:="org.eclipse.cdt.dsf.gdb.ui", org.eclipse.cdt.dsf.gdb.internal.commands;x-friends:="org.eclipse.cdt.dsf.gdb.ui",
org.eclipse.cdt.dsf.gdb.internal.memory;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.memory;x-friends:="org.eclipse.cdt.dsf.gdb.ui",
org.eclipse.cdt.dsf.gdb.internal.service.command.commands;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.service.command.commands;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.service.command.events;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.service.command.events;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.service.command.output;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.service.command.output;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.service.control;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.service.control;x-internal:=true,
org.eclipse.cdt.dsf.gdb.internal.tracepointactions;x-friends:="org.eclipse.cdt.dsf.gdb.ui,org.eclipse.cdt.tests.dsf.gdb", org.eclipse.cdt.dsf.gdb.internal.tracepointactions;x-friends:="org.eclipse.cdt.dsf.gdb.ui,org.eclipse.cdt.tests.dsf.gdb",
org.eclipse.cdt.dsf.gdb.launching, org.eclipse.cdt.dsf.gdb.launching,
org.eclipse.cdt.dsf.gdb.memory,
org.eclipse.cdt.dsf.gdb.service, org.eclipse.cdt.dsf.gdb.service,
org.eclipse.cdt.dsf.gdb.service.command, org.eclipse.cdt.dsf.gdb.service.command,
org.eclipse.cdt.dsf.gdb.service.extensions, org.eclipse.cdt.dsf.gdb.service.extensions,

View file

@ -0,0 +1,312 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.memory;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem;
import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlock;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IExpressions;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
import org.eclipse.cdt.dsf.debug.service.IMemorySpaces2;
import org.eclipse.cdt.dsf.debug.service.IStack;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.memory.IGdbMemoryAddressInfoTypeRetrieval;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.model.IMemoryBlock;
public class GdbMemoryAddressInfoVariablesRetrieval implements IGdbMemoryAddressInfoTypeRetrieval {
private final static String VARIABLES_INFO_TYPE = "Variables"; //$NON-NLS-1$
private final static int LOCALS_COLOR = 0xB630D1;
private final static int POINTER_COLOR = 0xFF0000;
private final static String DEREF_CHAR = "*"; //$NON-NLS-1$
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
private final DsfSession fSession;
public GdbMemoryAddressInfoVariablesRetrieval(DsfSession session) {
fSession = session;
}
private static class MemoryBlockAddressVariableItem extends MemoryBlockAddressInfoItem {
public MemoryBlockAddressVariableItem(String name, String value) {
super(name, value);
}
public MemoryBlockAddressVariableItem(String name, BigInteger addressValue, BigInteger dataTypeSize,
int color) {
super(name, addressValue, dataTypeSize, color);
}
@Override
public String getInfoType() {
return VARIABLES_INFO_TYPE;
}
}
private final static class ExpressionBin {
private final IExpressionDMContext fContext;
private IExpressionDMAddress fAddress;
private boolean fDereferenced;
public ExpressionBin(IExpressionDMContext expDmc, boolean dereferenced) {
fContext = expDmc;
fDereferenced = dereferenced;
}
boolean isDereferenced() {
return fDereferenced;
}
boolean isComplete() {
if (fContext != null && fAddress != null) {
return true;
}
return false;
}
}
@Override
public void itemsRequest(final IDMContext context, final IMemoryBlock memBlock,
final DataRequestMonitor<IMemoryBlockAddressInfoItem[]> rm) {
if (fSession == null || fSession.getExecutor() == null) {
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, "Initialization problem, invalid session")); //$NON-NLS-1$
return;
}
// resolve handles to the current context
final IFrameDMContext frameDmc = DMContexts.getAncestorOfType(context, IFrameDMContext.class);
final IStack stackFrameService = resolveService(IStack.class);
final IExpressions expressionService = resolveService(IExpressions.class);
// validate context
if (frameDmc == null || expressionService == null || stackFrameService == null) {
rm.done(new Status(IStatus.INFO, GdbPlugin.PLUGIN_ID,
"Unable to resolve Variables for the currently selected context")); //$NON-NLS-1$
return;
}
// Call IStack.getLocals() to get an array of IVariableDMContext objects representing the local
// variables in the stack frame represented by frameDmc.
final DsfExecutor dsfExecutor = fSession.getExecutor();
stackFrameService.getLocals(frameDmc, new DataRequestMonitor<IVariableDMContext[]>(dsfExecutor, rm) {
@Override
protected void handleSuccess() {
// For each IVariableDMContext object returned by IStack.getLocals(), call
// MIStackFrameService.getModelData() to get the IVariableDMData object. This requires
// a MultiRequestMonitor object.
// First, get the data model context objects for the local variables.
IVariableDMContext[] localsDMCs = getData();
if (localsDMCs == null || localsDMCs.length == 0) {
// There are no locals so just complete the request
rm.setData(new IMemoryBlockAddressInfoItem[0]);
rm.done();
return;
}
// Create a List in which we store the DM data objects for the local variables. This is
// necessary because there is no MultiDataRequestMonitor. :)
final List<IVariableDMData> localsDMData = new ArrayList<IVariableDMData>();
// Create the MultiRequestMonitor to handle completion of the set of getModelData() calls.
final CountingRequestMonitor crm = new CountingRequestMonitor(dsfExecutor, rm) {
@Override
public void handleSuccess() {
// Now that all the calls to getModelData() are complete, we create an
// IExpressionDMContext object for each local variable name, saving them all
// in an array.
ExpressionBin[] expressionBins = new ExpressionBin[localsDMData.size() * 2];
int i = 0;
for (IVariableDMData localDMData : localsDMData) {
expressionBins[i++] = createExpression(expressionService, frameDmc, localDMData.getName(),
false);
expressionBins[i++] = createExpression(expressionService, frameDmc,
DEREF_CHAR + localDMData.getName(), true);
}
// Lastly, we fill the update from the array of view model context objects
// that reference the ExpressionDMC objects for the local variables. This is
// the last code to run for a given call to updateElementsInSessionThread().
// We can now leave anonymous-inner-class hell.
resolveItems(expressionService, expressionBins, frameDmc, memBlock, rm);
}
};
int countRM = 0;
// Perform a set of getModelData() calls, one for each local variable's data model
// context object. In the handleCompleted() method of the DataRequestMonitor, add the
// IVariableDMData object to the localsDMData List for later processing (see above).
for (IVariableDMContext localDMC : localsDMCs) {
stackFrameService.getVariableData(localDMC,
new DataRequestMonitor<IVariableDMData>(dsfExecutor, crm) {
@Override
public void handleSuccess() {
localsDMData.add(getData());
crm.done();
}
});
countRM++;
}
crm.setDoneCount(countRM);
}
});
}
@Override
public String getInfoType() {
return VARIABLES_INFO_TYPE;
}
private void resolveItems(final IExpressions expressionService, final ExpressionBin[] expressionsBins,
final IFrameDMContext frameDmc, final IMemoryBlock memBlock,
final DataRequestMonitor<IMemoryBlockAddressInfoItem[]> rm) {
final DsfExecutor executor = expressionService.getExecutor();
resolveAddressData(expressionService, expressionsBins, new RequestMonitor(executor, rm) {
@Override
protected void handleCompleted() {
// resolve the default memory space id for the current context
IMemorySpaces2 memSpaceService = resolveService(IMemorySpaces2.class);
if (memSpaceService != null) {
memSpaceService.getDefaultMemorySpace(frameDmc, new DataRequestMonitor<String>(executor, rm) {
@Override
protected void handleCompleted() {
String defaultMemSpaceId = getData();
rm.setData(createAddressInfoItems(expressionsBins, memBlock, defaultMemSpaceId));
rm.done();
}
});
} else {
rm.setData(createAddressInfoItems(expressionsBins, memBlock, EMPTY_STRING));
rm.done();
}
}
});
}
private <V> V resolveService(Class<V> type) {
V service = null;
if (fSession != null) {
DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getDefault().getBundle().getBundleContext(),
fSession.getId());
service = tracker.getService(type);
tracker.dispose();
}
return service;
}
private ExpressionBin createExpression(IExpressions expressionService, final IDMContext dmc,
final String expression, boolean dereferenced) {
IExpressionDMContext exprDMC = expressionService.createExpression(dmc, expression);
// if (fCastToTypeSupport != null) {
// exprDMC = fCastToTypeSupport.replaceWithCastedExpression(exprDMC);
// }
return new ExpressionBin(exprDMC, dereferenced);
}
private void resolveAddressData(IExpressions expressionService, ExpressionBin[] expressionsBins,
final RequestMonitor rm) {
DsfExecutor executor = expressionService.getExecutor();
final CountingRequestMonitor crm = new CountingRequestMonitor(executor, rm);
for (final ExpressionBin expBin : expressionsBins) {
expressionService.getExpressionAddressData(expBin.fContext,
new DataRequestMonitor<IExpressionDMAddress>(executor, crm) {
@Override
protected void handleCompleted() {
if (isSuccess()) {
expBin.fAddress = getData();
}
crm.done();
}
});
}
crm.setDoneCount(expressionsBins.length);
}
private IMemoryBlockAddressInfoItem[] createAddressInfoItems(ExpressionBin[] contentsBins, IMemoryBlock memBlock,
String ctxDefaultMemSpaceId) {
int length = contentsBins.length;
final List<IMemoryBlockAddressInfoItem> infoItems = new ArrayList<IMemoryBlockAddressInfoItem>();
// Resolve the memory space id of the memory block
String memBlockMemSpaceId = EMPTY_STRING;
if (memBlock instanceof IMemorySpaceAwareMemoryBlock) {
String tMemBlockMemSpace = ((IMemorySpaceAwareMemoryBlock) memBlock).getMemorySpaceID();
memBlockMemSpaceId = tMemBlockMemSpace == null ? EMPTY_STRING : tMemBlockMemSpace;
}
for (int i = 0; i < length; i++) {
ExpressionBin expBin = contentsBins[i];
if (!expBin.isComplete()) {
// invalid item
continue;
}
IExpressionDMAddress dmAddress = expBin.fAddress;
BigInteger addressValue = dmAddress.getAddress().getValue();
// Skip addresses of zero, likely not yet initialized
if (!addressValue.equals(BigInteger.ZERO)) {
String name = expBin.fContext.getExpression();
BigInteger exprSize = BigInteger.valueOf(dmAddress.getSize());
final int color;
if (expBin.isDereferenced()) {
color = POINTER_COLOR;
} else {
color = LOCALS_COLOR;
}
// Resolve the memory space of the expression
String exprMemSpaceId = EMPTY_STRING;
String exprMemSpace = dmAddress.getMemorySpaceID();
exprMemSpaceId = exprMemSpace != null ? exprMemSpace : EMPTY_STRING;
// if the memory space of the block is valid and the memory space id of the expression is empty,
// use the context default memory space id for the expression.
if (memBlockMemSpaceId.length() > 0 && exprMemSpaceId.length() == 0) {
exprMemSpaceId = ctxDefaultMemSpaceId;
}
// If the expression's address is in the same memory space as the memory block create a new item
if (exprMemSpaceId.equals(memBlockMemSpaceId)) {
infoItems.add(new MemoryBlockAddressVariableItem(name, addressValue, exprSize, color));
}
}
}
return infoItems.toArray(new IMemoryBlockAddressInfoItem[infoItems.size()]);
}
}

View file

@ -0,0 +1,110 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.memory;
import java.math.BigInteger;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem;
/**
* A base partial implementation of a a memory block address information item
*
*/
public abstract class MemoryBlockAddressInfoItem implements IMemoryBlockAddressInfoItem {
private final String fName;
private String fLabel;
private BigInteger fAddress;
private BigInteger fLength = BigInteger.ONE;
private int fColor = 0;
private MemoryBlockAddressInfoItem(String id, BigInteger address) {
fName = id;
fLabel = id;
fAddress = address;
}
/**
* @param address String representation of a memory address in hex format
*/
public MemoryBlockAddressInfoItem(String id, String address) {
this(id, convertValue(address));
}
/**
* @param color int value where the lowest three octets represent the corresponding RGB value
*/
public MemoryBlockAddressInfoItem(String id, BigInteger address, BigInteger length, int color) {
this(id, address);
fLength = length;
fColor = color;
}
@Override
public String getId() {
return fName;
}
@Override
public BigInteger getRangeInAddressableUnits() {
return fLength;
}
@Override
public int getRegionRGBColor() {
return fColor;
}
@Override
public String getLabel() {
return fLabel;
}
@Override
public void setLabel(String label) {
fLabel = label;
}
@Override
public BigInteger getAddress() {
return fAddress;
}
private static BigInteger convertValue(String inValue) {
// Make sure we provide a valid hex representation or zero
int radix = 16;
BigInteger hexValue = null;
String value = inValue.replaceAll("0x", ""); //$NON-NLS-1$ //$NON-NLS-2$
try {
hexValue = new BigInteger(value, radix);
} catch (NumberFormatException e) {
hexValue = BigInteger.ZERO;
}
return hexValue;
}
@Override
public void setRangeInAddressableUnits(BigInteger length) {
fLength = length;
}
@Override
public void setRegionRGBColor(int color) {
fColor = color;
}
@Override
public void setAddress(BigInteger address) {
fAddress = address;
}
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.memory;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.debug.core.model.IMemoryBlock;
/**
* @since 5.0
*/
public interface IGdbMemoryAddressInfoTypeRetrieval {
/**
* @return the String representing the type of information items being provided
*/
String getInfoType();
/**
* The implementation provides the items of an associated type which could be pointing to a memory address
*/
void itemsRequest(IDMContext selectionContext, IMemoryBlock memoryBlock,
DataRequestMonitor<IMemoryBlockAddressInfoItem[]> rm);
}

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2007, 2014 Wind River Systems and others. * Copyright (c) 2007, 2016 Wind River Systems and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -26,9 +26,9 @@ import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider; import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider;
import org.eclipse.cdt.dsf.debug.service.IMemory; import org.eclipse.cdt.dsf.debug.service.IMemory;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl;
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
import org.eclipse.cdt.dsf.internal.DsfPlugin; import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
@ -782,9 +782,9 @@ public class DsfMemoryBlock extends PlatformObject implements IMemoryBlockExtens
/** /**
* Get the context specified at construction. * Get the context specified at construction.
* *
* @since 2.1 * @since 2.7
*/ */
protected IMemoryDMContext getContext() { public IMemoryDMContext getContext() {
return fContext; return fContext;
} }
} }

View file

@ -874,21 +874,16 @@ public abstract class AbstractPane extends Canvas
{ {
super.setFont(font); super.setFont(font);
fCharacterWidth = -1; fCharacterWidth = -1;
fCellHeight = -1;
fTextHeight = -1; fTextHeight = -1;
} }
private int fCellHeight = -1; // called often, cache protected int getCellHeight() {
// If additional information is to be inserted between lines
// double the height
int multiplier = fRendering.hasVisibleRangeInfo() ? 2 : 1;
int cellHeight = getCellTextHeight() * multiplier + (fRendering.getCellPadding() * 2);
protected int getCellHeight() return cellHeight;
{
if(fCellHeight == -1)
{
fCellHeight = getCellTextHeight()
+ (fRendering.getCellPadding() * 2);
}
return fCellHeight;
} }
private int fCharacterWidth = -1; // called often, cache private int fCharacterWidth = -1; // called often, cache

View file

@ -15,7 +15,6 @@ import java.math.BigInteger;
import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugException;
import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Point;
@ -211,15 +210,13 @@ public class AddressPane extends AbstractPane
GC gc = pe.gc; GC gc = pe.gc;
FontMetrics fontMetrics = gc.getFontMetrics(); int cellHeight = getCellHeight();
int textHeight = fontMetrics.getHeight();
int cellHeight = textHeight + (fRendering.getCellPadding() * 2);
try try
{ {
BigInteger start = fRendering.getViewportStartAddress(); BigInteger start = fRendering.getViewportStartAddress();
for(int i = 0; i < this.getBounds().height / cellHeight; i++) for(int i = 0; i < fRendering.getRowCount(); i++)
{ {
gc.setForeground(fRendering.getTraditionalRendering().getColorText()); gc.setForeground(fRendering.getTraditionalRendering().getColorText());
BigInteger lineAddress = start.add(BigInteger.valueOf(i BigInteger lineAddress = start.add(BigInteger.valueOf(i

View file

@ -13,13 +13,20 @@
package org.eclipse.cdt.debug.ui.memory.traditional; package org.eclipse.cdt.debug.ui.memory.traditional;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem;
import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.MemoryByte; import org.eclipse.debug.core.model.MemoryByte;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Rectangle;
@ -33,6 +40,8 @@ import org.eclipse.swt.widgets.Shell;
public class DataPane extends AbstractPane public class DataPane extends AbstractPane
{ {
private Shell fToolTipShell; private Shell fToolTipShell;
private final static String UNICODE_NORTH_WEST_ARROW = "\u2196";
public DataPane(Rendering parent) public DataPane(Rendering parent)
{ {
super(parent); super(parent);
@ -144,8 +153,8 @@ public class DataPane extends AbstractPane
* @return The width length in pixels needed to draw the characters of an addressable unit * @return The width length in pixels needed to draw the characters of an addressable unit
*/ */
private int getAddressableWidth() { private int getAddressableWidth() {
// derive the number of characters per addressable size e.g. 2 * NumOfOctets // derive the number of characters per addressable size e.g. 2 * NumOfOctets for hex representation
int charsPerOctet = 2; int charsPerOctet = fRendering.getRadixCharacterCount(fRendering.getRadix(), 1);
int addressCharacterCount = fRendering.getAddressableSize() * charsPerOctet; int addressCharacterCount = fRendering.getAddressableSize() * charsPerOctet;
// derive width by multiplying by the size of a character // derive width by multiplying by the size of a character
return addressCharacterCount * getCellCharacterWidth(); return addressCharacterCount * getCellCharacterWidth();
@ -221,6 +230,30 @@ public class DataPane extends AbstractPane
return cellBaseAddress.add(BigInteger.valueOf(addressableOffset)); return cellBaseAddress.add(BigInteger.valueOf(addressableOffset));
} }
private Point getAddressLocation(BigInteger address) {
// Resolve the location of the cell
Point cellLocation = getCellLocation(address);
// Resolve the first address in the cell
BigInteger baseAddress;
try {
baseAddress = getCellAddressAt(cellLocation.x, cellLocation.y);
} catch (DebugException e) {
return null;
}
if (baseAddress == null) {
return null;
}
int addressSpan = address.subtract(baseAddress).intValue();
// Resolve the horizontal distance from base address to given address in octets
int charsWidth = fRendering.getRadixCharacterCount(fRendering.getRadix(), addressSpan) * getCellCharacterWidth();
return new Point(cellLocation.x + charsWidth, cellLocation.y);
}
@Override @Override
protected Point getCellLocation(BigInteger cellAddress) protected Point getCellLocation(BigInteger cellAddress)
{ {
@ -254,6 +287,51 @@ public class DataPane extends AbstractPane
} }
} }
private Point getRowFirstCellLocation(BigInteger cellAddress) {
try {
BigInteger address = fRendering.getViewportStartAddress();
// cell offset from base address in octets
int cellOffset = cellAddress.subtract(address).intValue();
cellOffset *= fRendering.getAddressableSize();
int row = cellOffset / (fRendering.getColumnCount() * fRendering.getBytesPerColumn());
// column zero plus cell padding
int x = fRendering.getCellPadding();
int y = row * getCellHeight() + fRendering.getCellPadding();
return new Point(x, y);
} catch (Exception e) {
fRendering.logError(
TraditionalRenderingMessages.getString("TraditionalRendering.FAILURE_DETERMINE_CELL_LOCATION"), e); //$NON-NLS-1$
return null;
}
}
private Point getRowLastCellLocation(BigInteger cellAddress) {
try {
BigInteger address = fRendering.getViewportStartAddress();
// cell offset from base address in octets
int cellOffset = cellAddress.subtract(address).intValue();
cellOffset *= fRendering.getAddressableSize();
int row = cellOffset / (fRendering.getColumnCount() * fRendering.getBytesPerColumn());
int col = fRendering.getColumnCount() - 1;
int x = col * getCellWidth() + fRendering.getCellPadding();
int y = row * getCellHeight() + fRendering.getCellPadding();
return new Point(x, y);
} catch (Exception e) {
fRendering.logError(
TraditionalRenderingMessages.getString("TraditionalRendering.FAILURE_DETERMINE_CELL_LOCATION"), e); //$NON-NLS-1$
return null;
}
}
@Override @Override
protected void positionCaret(int x, int y) protected void positionCaret(int x, int y)
{ {
@ -327,7 +405,7 @@ public class DataPane extends AbstractPane
{ {
BigInteger startAddress = fRendering.getViewportStartAddress(); BigInteger startAddress = fRendering.getViewportStartAddress();
for(int i = 0; i < this.getBounds().height / cellHeight; i++) for(int i = 0; i < fRendering.getRowCount(); i++)
{ {
for(int col = 0; col < columns; col++) for(int col = 0; col < columns; col++)
{ {
@ -405,6 +483,8 @@ public class DataPane extends AbstractPane
cellHeight); cellHeight);
} }
} }
markAddressesWithAdditionalInfo(gc);
} }
catch(Exception e) catch(Exception e)
{ {
@ -414,6 +494,219 @@ public class DataPane extends AbstractPane
} }
private void markAddressesWithAdditionalInfo(GC gc) {
if (fRendering.isDisposed() || !fRendering.isVisible() || isDisposed()) {
return;
}
final Map<BigInteger, List<IMemoryBlockAddressInfoItem>> addressToInfoItems = fRendering
.getVisibleValueToAddressInfoItems();
// Check if there are information items available
if (addressToInfoItems.size() < 1) {
return;
}
// Prepare to enclose addresses with additional info in a rectangle
int addressableWidth = getAddressableWidth();
assert addressableWidth > 0;
// Initialize the dimensions for the rectangle
int width = 1;
int leftMargin = 1;
int rightMargin = leftMargin + 1;
int lowerMargin = 1;
int lineWidth = 2;
int height = getCellTextHeight() - fRendering.getCellPadding() + lowerMargin;
// Save current GC settings
Color origColor = gc.getForeground();
int origLineWidth = gc.getLineWidth();
gc.setForeground(fRendering.getTraditionalRendering().getColorChanged());
// Set the thickness of the lines being drawn, i.e. thicker than the default
gc.setLineWidth(lineWidth);
// Loop for each address from lowest to highest value
BigInteger[] sortedAddresses = orderItemsAscending(addressToInfoItems.keySet());
// Define rectangle margin space
for (BigInteger startAddress : sortedAddresses) {
// Resolve rectangle starting point and start / end row references
Point location = getAddressLocation(startAddress);
Point firstCellInRow = getRowFirstCellLocation(startAddress);
Point lastCellInRow = getRowLastCellLocation(startAddress);
// Mark each item even if they point to the same start address,
// so the end address is visible on each of them
List<IMemoryBlockAddressInfoItem> sameStartAddressitems = addressToInfoItems.get(startAddress);
// Sort items starting in the same address to draw longest first, this will give more visibility to the embedded markings
IMemoryBlockAddressInfoItem[] sameStartOrderedItems = orderItemsByLengthDescending(sameStartAddressitems);
for (IMemoryBlockAddressInfoItem item : sameStartOrderedItems) {
BigInteger addressUnits = item.getRangeInAddressableUnits();
// Resolve the color for the rectangle
Color rangeColor = resolveColor(item.getRegionRGBColor());
if (rangeColor != null) {
gc.setForeground(rangeColor);
}
// The start and end address are part of the length so we need to decrement / adjust by one
BigInteger endAddress = startAddress.add(addressUnits.subtract(BigInteger.ONE));
// End location to the start of next address may change to a different row
// So it's best to add the addressable width to the beginning of the last address
Point endLocation = getAddressLocation(endAddress);
endLocation.x = endLocation.x + addressableWidth;
// Resolve the rows index as the selection may span multiple rows
int rowsIndex = (endLocation.y - location.y) / getCellHeight();
for (int i = 0; i <= rowsIndex; i++) {
Point rowLocation = new Point(firstCellInRow.x, firstCellInRow.y + i * getCellHeight());
if (!isRowVisible(rowLocation.y)) {
// No need to draw the portion of lines outside the visible area
continue;
}
if (i == 0) {
// Enclosing range in first row
if (endLocation.y == location.y) {
// End and beginning locations are in the same row
width = endLocation.x - location.x + rightMargin;
gc.drawRectangle(location.x - leftMargin, location.y, width, height);
} else {
// The end cell is in a different row,
// mark from the location to the end of this row
width = lastCellInRow.x + addressableWidth * fRendering.getAddressesPerColumn() - location.x + rightMargin;
// open ended first row
location.x -= leftMargin;
drawRectangleOpenEnd(location, width, height, gc);
}
} else if (i > 0 && i < rowsIndex) {
// The marking started before this row and finishes after this row
// we need to mark the whole row with opened ends i.e. two bordering lines top / bottom
width = lastCellInRow.x + addressableWidth * fRendering.getAddressesPerColumn() - firstCellInRow.x + rightMargin;
// parallel lines row
assert width > 0;
rowLocation.x -= leftMargin;
drawParallelLines(rowLocation, width, height, gc);
} else if (i == rowsIndex) {
// The last row to highlight
width = endLocation.x - firstCellInRow.x + rightMargin;
// Draw a colored rectangle around the addressable units
rowLocation.x -= leftMargin;
drawRectangleOpenStart(rowLocation, width, height, gc);
}
}
// Display the associated textual information
String info = fRendering.buildAddressInfoString(startAddress, ",", false);
if (info.length() > 0) {
// Add one character e.g. up arrow, to indicate the start of the data i.e. upper or lower row
gc.drawText(UNICODE_NORTH_WEST_ARROW + info, location.x, location.y + getCellTextHeight());
}
if (rangeColor != null) {
rangeColor.dispose();
}
}
}
// Restore the original color
gc.setForeground(origColor);
gc.setLineWidth(origLineWidth);
}
private IMemoryBlockAddressInfoItem[] orderItemsByLengthDescending(
List<IMemoryBlockAddressInfoItem> sameStartAddressitems) {
if (sameStartAddressitems.isEmpty() || sameStartAddressitems.size() == 1) {
// One item, nothing to sort
return sameStartAddressitems
.toArray(new IMemoryBlockAddressInfoItem[sameStartAddressitems.size()]);
}
// Perform a bubble sort
boolean swapped = true;
IMemoryBlockAddressInfoItem temp;
for (int i = 0; i < sameStartAddressitems.size() - 1; i++) {
swapped = false;
for (int j = 0; j < sameStartAddressitems.size() - i - 1; j++) {
// If current index item is smaller then swap to get reverse sorting
if (sameStartAddressitems.get(j).getRangeInAddressableUnits()
.compareTo(sameStartAddressitems.get(j + 1).getRangeInAddressableUnits()) < 0) {
temp = sameStartAddressitems.get(j);
sameStartAddressitems.set(j, sameStartAddressitems.get(j + 1));
sameStartAddressitems.set(j + 1, temp);
swapped = true;
}
}
if (swapped == false) {
// No swaps were needed, we are done!
break;
}
}
return sameStartAddressitems.toArray(new IMemoryBlockAddressInfoItem[sameStartAddressitems.size()]);
}
private BigInteger[] orderItemsAscending(Set<BigInteger> keySet) {
List<BigInteger> collection = new ArrayList<>(keySet);
Collections.sort(collection);
return collection.toArray(new BigInteger[collection.size()]);
}
/**
* Convert from int to RGB octets to then create the corresponding Color
*/
private Color resolveColor(int intColor) {
return new Color(getDisplay(), intColor >> 16, (intColor >> 8) & 0xff, intColor & 0xff);
}
private boolean isRowVisible(int y) {
int firstVisibleRow = getAddressLocation(fRendering.getViewportStartAddress()).y;
int lastVisibleRow = getAddressLocation(fRendering.getViewportEndAddress()).y;
if (y >= firstVisibleRow && y <= lastVisibleRow) {
return true;
}
return false;
}
private void drawRectangleOpenStart(Point location, int width, int height, GC gc) {
gc.drawRectangle(location.x, location.y, width, height);
// clear start border
eraseVerticalLine(location, height, gc);
}
private void drawRectangleOpenEnd(Point location, int width, int height, GC gc) {
gc.drawRectangle(location.x, location.y, width, height);
// clear end border
Point erasep = new Point(location.x + width, location.y);
eraseVerticalLine(erasep, height, gc);
}
private void eraseVerticalLine(Point erasep, int height, GC gc) {
Color currentColor = gc.getForeground();
gc.setForeground(fRendering.getTraditionalRendering().getColorBackground());
gc.drawLine(erasep.x, erasep.y, erasep.x, erasep.y + height);
gc.setForeground(currentColor);
}
private void drawParallelLines(Point location, int width, int height, GC gc) {
// NOTE: Writing parallel lines would be preferred, however this did not work in my environment
// gc.drawLine(location.x, location.y , location.x + width, location.y);
// gc.drawLine(location.x, location.y + height, location.x + width, location.y + height);
// So we use the work around of writing a rectangle and erase start / end borders
gc.drawRectangle(location.x, location.y, width, height);
// clear start border
eraseVerticalLine(location, height, gc);
// clear end border
Point erasep = new Point(location.x + width, location.y);
eraseVerticalLine(erasep, height, gc);
}
// Allow subclasses to override this method to do their own coloring // Allow subclasses to override this method to do their own coloring
protected void applyCustomColor(GC gc, TraditionalMemoryByte bytes[], int col) protected void applyCustomColor(GC gc, TraditionalMemoryByte bytes[], int col)
{ {
@ -532,6 +825,14 @@ public class DataPane extends AbstractPane
// Show the current hovering address as the first line in the tooltip // Show the current hovering address as the first line in the tooltip
StringBuilder sb = new StringBuilder("0x").append(subAddress.toString(16)); StringBuilder sb = new StringBuilder("0x").append(subAddress.toString(16));
// Add additional address information, if available
if (fRendering.hasAddressInfo(subAddress)) {
String info = fRendering.buildAddressInfoString(subAddress, "\n", true);
if (info.length() > 0) {
sb.append("\n").append(info);
}
}
fLabelContent.setText(sb.toString()); fLabelContent.setText(sb.toString());
// Setting location of the tool tip // Setting location of the tool tip

View file

@ -17,12 +17,15 @@ package org.eclipse.cdt.debug.ui.memory.traditional;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Vector; import java.util.Vector;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugEvent;
@ -156,6 +159,14 @@ public class Rendering extends Composite implements IDebugEventSetListener
public final static int UPDATE_MANUAL = 3; public final static int UPDATE_MANUAL = 3;
public int fUpdateMode = UPDATE_ALWAYS; public int fUpdateMode = UPDATE_ALWAYS;
/**
* Maintains the subset of items visible in the current view address range.
* This information is refreshed when the associated Panes are about to be redrawn
* @since 1.4
*/
protected final Map<BigInteger, List<IMemoryBlockAddressInfoItem>> fMapStartAddrToInfoItems = Collections
.synchronizedMap(new HashMap<BigInteger, List<IMemoryBlockAddressInfoItem>>());
public Rendering(Composite parent, TraditionalRendering renderingParent) public Rendering(Composite parent, TraditionalRendering renderingParent)
{ {
super(parent, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.H_SCROLL super(parent, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.H_SCROLL
@ -1184,6 +1195,8 @@ public class Rendering extends Composite implements IDebugEventSetListener
fViewportCache.dispose(); fViewportCache.dispose();
fViewportCache = null; fViewportCache = null;
} }
fMapStartAddrToInfoItems.clear();
super.dispose(); super.dispose();
} }
@ -1625,7 +1638,10 @@ public class Rendering extends Composite implements IDebugEventSetListener
((AbstractPane) panes[i]).getRowCount()); ((AbstractPane) panes[i]).getRowCount());
} }
return rowCount; // Add an extra row of information as we can present part of the information on
// the remaining space of the canvas
int extra = 1;
return rowCount + extra;
} }
public int getBytesPerColumn() public int getBytesPerColumn()
@ -2293,4 +2309,38 @@ public class Rendering extends Composite implements IDebugEventSetListener
final List<AbstractPane> panes = Arrays.asList(getRenderingPanes()); final List<AbstractPane> panes = Arrays.asList(getRenderingPanes());
return panes.get((panes.indexOf(currentPane) + offset) % panes.size()); return panes.get((panes.indexOf(currentPane) + offset) % panes.size());
} }
/**
* Indicates if additional address information is available to display in the current visible range
*/
boolean hasVisibleRangeInfo() {
return false;
}
/**
* @return True if the given address has additional information to display e.g. variables, registers, etc.
*/
boolean hasAddressInfo(BigInteger address) {
return false;
}
/**
* @return The items that would be visible in the current viewable area if the rows were to use a single
* height
*/
Map<BigInteger, List<IMemoryBlockAddressInfoItem>> getVisibleValueToAddressInfoItems() {
return fMapStartAddrToInfoItems;
}
/**
* Provides a string with the information relevant to a given address, the separator helps to format it
* e.g. Separated items by comma, new line, etc.
*
* @param addTypeHeaders
* Indicates if the string shall include a data type name before each list of items of the same
* type e.g. Variables, Regitsters, etc.
*/
String buildAddressInfoString(BigInteger address, String separator, boolean addTypeHeaders) {
return "";
}
} }

View file

@ -0,0 +1,395 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson AB 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:
* Ericsson - initial API and implementation
* *******************************************************************************/
package org.eclipse.cdt.debug.ui.memory.traditional;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.EventType;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IAddressInfoUpdateListener;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IGetMemoryBlockAddressInfoReq;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem;
import org.eclipse.cdt.debug.internal.core.CRequest;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.debug.ui.contexts.IDebugContextService;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPartSite;
/**
* @since 1.4
*/
public class RenderingAddressInfo extends Rendering
implements IDebugContextListener, IAddressInfoUpdateListener {
private final TraditionalRendering fParent;
/**
* Simple tracker of selected context, to reduce the number of asynchronous calls to resolve the
* information items related to a selected context
*/
private volatile Object fSelectedContext;
private IMemoryBlockAddressInfoRetrieval fAddressInfoRetrieval = null;
/**
* This maintains the full set of information items retrieved for the currently selected context. This is
* updated each time a context selection change is detected
*/
private volatile IMemoryBlockAddressInfoItem[] fAddressInfoItems;
public RenderingAddressInfo(Composite parent, TraditionalRendering renderingParent) {
super(parent, renderingParent);
fParent = renderingParent;
// Register as Debug context listener
IWorkbenchPartSite site = fParent.getMemoryRenderingContainer().getMemoryRenderingSite().getSite();
DebugUITools.addPartDebugContextListener(site, this);
IDebugContextService contextService = DebugUITools.getDebugContextManager()
.getContextService(site.getWorkbenchWindow());
resolveAddressInfoForCurrentSelection(contextService);
}
public void dispose() {
fSelectedContext = null;
fMapStartAddrToInfoItems.clear();
fAddressInfoItems = null;
IWorkbenchPartSite site = fParent.getMemoryRenderingContainer().getMemoryRenderingSite().getSite();
DebugUITools.removePartDebugContextListener(site, this);
if (fAddressInfoRetrieval != null) {
fAddressInfoRetrieval.removeAddressInfoUpdateListener(this);
}
super.dispose();
}
private class GetMemoryBlockAddressInfoReq extends CRequest implements IGetMemoryBlockAddressInfoReq {
private Map<String, IMemoryBlockAddressInfoItem[]> fInfoTypeToItems = Collections
.synchronizedMap(new HashMap<String, IMemoryBlockAddressInfoItem[]>());
private final Object fContext;
GetMemoryBlockAddressInfoReq(Object context) {
fContext = context;
}
@Override
public IMemoryBlockAddressInfoItem[] getAddressInfoItems(String type) {
return fInfoTypeToItems.get(type);
}
@Override
public void setAddressInfoItems(String type, IMemoryBlockAddressInfoItem[] items) {
fInfoTypeToItems.put(type, items);
}
public Object getContext() {
return fContext;
}
@Override
public String[] getAddressInfoItemTypes() {
return fInfoTypeToItems.keySet().toArray(new String[fInfoTypeToItems.size()]);
}
@Override
public IMemoryBlockAddressInfoItem[] getAllAddressInfoItems() {
// concatenate the different type of items received into a single list
List<IMemoryBlockAddressInfoItem> allItemsList = new ArrayList<>();
// For each set of items
for (IMemoryBlockAddressInfoItem[] partialItems : fInfoTypeToItems.values()) {
if (partialItems != null && partialItems.length > 0) {
allItemsList.addAll(Arrays.asList(partialItems));
}
}
return allItemsList.toArray(new IMemoryBlockAddressInfoItem[allItemsList.size()]);
}
}
/**
* @since 1.4
*/
@Override
public void debugContextChanged(DebugContextEvent event) {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
// Resolve selection
ISelection selection = event.getContext();
if (!(selection instanceof IStructuredSelection)) {
return;
}
Object elem = ((IStructuredSelection) selection).getFirstElement();
handleDebugContextChanged(elem);
}
}
private void resolveAddressInfoForCurrentSelection(IDebugContextService contextService) {
IWorkbenchPartSite site = fParent.getMemoryRenderingContainer().getMemoryRenderingSite().getSite();
// Check current selection
ISelection selection = contextService.getActiveContext(site.getId(),
((IViewSite) site).getSecondaryId());
if (selection instanceof StructuredSelection) {
handleDebugContextChanged(((StructuredSelection) selection).getFirstElement());
}
}
private void handleDebugContextChanged(final Object context) {
if (isDisposed() || context == null) {
// Invalid context or Data pane is not visible
return;
}
if (context instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) context;
final IMemoryBlockAddressInfoRetrieval addrInfo = ((IMemoryBlockAddressInfoRetrieval) adaptable
.getAdapter(IMemoryBlockAddressInfoRetrieval.class));
if (addrInfo == null) {
// No information retrieval available
return;
}
// Save the selected context to later help us determine if the selection has really changed
fSelectedContext = context;
final Display display = getDisplay();
addrInfo.getMemoryBlockAddressInfo(context, getMemoryBlock(),
new GetMemoryBlockAddressInfoReq(context) {
@Override
public void done() {
// If the context is still valid
if (getContext().equals(fSelectedContext)) {
final IMemoryBlockAddressInfoItem[] addressInfoItems = getAllAddressInfoItems();
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
@Override
public void run() {
// The selection has changed, so our Address information may no longer be valid
fAddressInfoItems = addressInfoItems;
fMapStartAddrToInfoItems.clear();
if (fBinaryPane.isVisible()) {
redrawPanes();
}
refreshUpdateListener(addrInfo);
}
});
}
}
}
private void refreshUpdateListener(final IMemoryBlockAddressInfoRetrieval addrInfo) {
if (fAddressInfoRetrieval == null) {
// One retrieval per session,
// Register this rendering to listen for info updates
fAddressInfoRetrieval = addrInfo;
addrInfo.addAddressInfoUpdateListener(RenderingAddressInfo.this);
}
}
});
}
}
/**
* @return Return the view port end address, if the DataPane displays information with a single height per
* row i.e. single height is used when no additional address information is available for any of
* the addresses in the view port
*/
private BigInteger getViewportEndAddressSingleHeight() {
int cellHeight = fBinaryPane.getCellTextHeight() + (getCellPadding() * 2);
int rowCount = getBounds().height / cellHeight;
BigInteger endAddress = fViewportAddress
.add(BigInteger.valueOf(this.getBytesPerRow() * rowCount / getAddressableSize()));
return endAddress;
}
private boolean isWithinRange(BigInteger item, BigInteger start, BigInteger end) {
if (item.compareTo(start) > -1 && item.compareTo(end) < 1) {
return true;
}
return false;
}
private String[] orderTypesAscending(Set<String> items) {
List<String> collection = new ArrayList<String>(items);
Collections.sort(collection);
return collection.toArray(new String[collection.size()]);
}
@Override
protected void redrawPanes() {
if (!isDisposed() && this.isVisible()) {
// Refresh address information visible in the current viewport
getVisibleValueToAddressInfoItems();
}
super.redrawPanes();
}
@Override
public void handleAddressInfoUpdate(EventType type, Object update) {
fAddressInfoItems = null;
IWorkbenchPartSite site = fParent.getMemoryRenderingContainer().getMemoryRenderingSite().getSite();
IDebugContextService contextService = DebugUITools.getDebugContextManager()
.getContextService(site.getWorkbenchWindow());
resolveAddressInfoForCurrentSelection(contextService);
}
@Override
Map<BigInteger, List<IMemoryBlockAddressInfoItem>> getVisibleValueToAddressInfoItems() {
IMemoryBlockAddressInfoItem[] items = fAddressInfoItems;
if (items == null) {
fMapStartAddrToInfoItems.clear();
return fMapStartAddrToInfoItems;
}
if (getRadix() != RADIX_HEX && getRadix() != RADIX_BINARY) {
// If not using Hex or Binary radix, we can not accurately determine the location of cross
// reference information
// unless the cell size matches the addressable size of the target system
if (fParent.getAddressableSize() != getBytesPerColumn()) {
fMapStartAddrToInfoItems.clear();
return fMapStartAddrToInfoItems;
}
}
Map<BigInteger, List<IMemoryBlockAddressInfoItem>> allValuesMap = new HashMap<>(items.length);
// This local variable will hold the same values as the instance variable fMapAddressToInfoItems, and
// be used as
// return value. The reason for the duplication is to prevent concurrent access exceptions
Map<BigInteger, List<IMemoryBlockAddressInfoItem>> filteredValuesMap = new HashMap<>(items.length);
synchronized (fMapStartAddrToInfoItems) {
// Refreshing the Address to InfoItem data map
fMapStartAddrToInfoItems.clear();
BigInteger startAddress = getViewportStartAddress();
// Get the endAddress considering a page that uses single height,
// Note: The UI may some times present rows with double height even if the user does not see items
// with additional info, the reason is that the second part of a view port page may contain all
// the items with info.
// if we were to use and endAddress for a page that uses double height, but end up not having
// items with additional information, then it would need to switch to single height to compact the
// information in the view but since an endAddress for double height was used it will not consider
// half of the items for additional information, so marking info. would not be shown.
BigInteger endAddress = getViewportEndAddressSingleHeight();
for (IMemoryBlockAddressInfoItem item : items) {
List<IMemoryBlockAddressInfoItem> containers = allValuesMap.get(item.getAddress());
if (containers == null) {
containers = new ArrayList<>();
allValuesMap.put(item.getAddress(), containers);
}
containers.add(item);
// If any address within the item width is within the visible range we want it in the filtered
// result
BigInteger itemStart = item.getAddress();
BigInteger itemEnd = item.getAddress().add(item.getRangeInAddressableUnits());
boolean itemStartIsInRange = isWithinRange(itemStart, startAddress, endAddress);
boolean itemEndIsInRange = isWithinRange(itemEnd, startAddress, endAddress);
boolean itemSpansOverVisibleRange = isWithinRange(startAddress, itemStart, itemEnd)
&& isWithinRange(endAddress, itemStart, itemEnd);
if (itemStartIsInRange || itemEndIsInRange || itemSpansOverVisibleRange) {
fMapStartAddrToInfoItems.put(item.getAddress(), allValuesMap.get(item.getAddress()));
filteredValuesMap.put(item.getAddress(), allValuesMap.get(item.getAddress()));
}
}
}
return filteredValuesMap;
}
@Override
String buildAddressInfoString(BigInteger cellAddress, String separator, boolean addTypeHeaders) {
List<IMemoryBlockAddressInfoItem> infoItems = fMapStartAddrToInfoItems.get(cellAddress);
if (infoItems == null || infoItems.size() < 1) {
// No information to display
return "";
}
// The container string builder for all types
StringBuilder sb = new StringBuilder();
Map<String, StringBuilder> infoTypeToStringBuilder = new HashMap<>();
for (int i = 0; i < infoItems.size(); i++) {
String infoType = infoItems.get(i).getInfoType();
// Resolve string builder for this info type
StringBuilder typeBuilder = infoTypeToStringBuilder.get(infoType);
if (typeBuilder == null) {
// Create a String builder per information type
if (addTypeHeaders) {
typeBuilder = new StringBuilder(infoType).append(":").append(separator);
} else {
typeBuilder = new StringBuilder();
}
infoTypeToStringBuilder.put(infoType, typeBuilder);
}
// append the new item information to the string builder associated to its type
typeBuilder.append(infoItems.get(i).getLabel()).append(separator);
}
// Present the group of items sorted by type name
String[] sortedTypes = orderTypesAscending(infoTypeToStringBuilder.keySet());
// Consolidate the String builders per type into a single one
int i = 0;
for (String type : sortedTypes) {
StringBuilder builder = infoTypeToStringBuilder.get(type);
String text = builder.toString();
text = text.substring(0, text.length() - 1);
sb.append(text);
if (i < infoTypeToStringBuilder.keySet().size() - 1) {
sb.append(separator);
}
i++;
}
return sb.toString();
}
@Override
boolean hasAddressInfo(BigInteger cellAddress) {
return fMapStartAddrToInfoItems.keySet().contains(cellAddress);
}
/**
* Indicates if additional address information is available to display in the current visible range
*/
@Override
boolean hasVisibleRangeInfo() {
return (fBinaryPane.fPaneVisible && fMapStartAddrToInfoItems.size() > 0);
}
}

View file

@ -238,7 +238,7 @@ public class TextPane extends AbstractPane
{ {
BigInteger start = fRendering.getViewportStartAddress(); BigInteger start = fRendering.getViewportStartAddress();
for(int i = 0; i < this.getBounds().height / cellHeight; i++) for(int i = 0; i < fRendering.getRowCount(); i++)
{ {
for(int col = 0; col < columns; col++) for(int col = 0; col < columns; col++)
{ {

View file

@ -428,7 +428,7 @@ public class TraditionalRendering extends AbstractMemoryRendering implements IRe
{ {
allocateColors(); allocateColors();
this.fRendering = new Rendering(parent, this); this.fRendering = new RenderingAddressInfo(parent, this);
applyPreferences(); applyPreferences();