From e21fc12f9052e99dcf545df4a2ae2aba1c90c1cc Mon Sep 17 00:00:00 2001 From: Alvaro Sanchez-Leon Date: Sun, 13 Mar 2016 17:48:33 -0400 Subject: [PATCH] Bug 489512 - Show local variables in the traditional memory render Change-Id: I48d633e8f6979fd91e9150856fd77fccffdee299 --- .../META-INF/MANIFEST.MF | 3 +- .../IMemoryBlockAddressInfoRetrieval.java | 147 +++++++ .../gdb/internal/ui/GdbSessionAdapters.java | 12 +- .../GdbMemoryBlockAddressInfoRetrieval.java | 169 ++++++++ .../META-INF/MANIFEST.MF | 3 +- ...dbMemoryAddressInfoVariablesRetrieval.java | 312 ++++++++++++++ .../memory/MemoryBlockAddressInfoItem.java | 110 +++++ .../IGdbMemoryAddressInfoTypeRetrieval.java | 32 ++ .../cdt/dsf/debug/model/DsfMemoryBlock.java | 8 +- .../ui/memory/traditional/AbstractPane.java | 19 +- .../ui/memory/traditional/AddressPane.java | 7 +- .../debug/ui/memory/traditional/DataPane.java | 307 +++++++++++++- .../ui/memory/traditional/Rendering.java | 56 ++- .../traditional/RenderingAddressInfo.java | 395 ++++++++++++++++++ .../debug/ui/memory/traditional/TextPane.java | 2 +- .../traditional/TraditionalRendering.java | 2 +- 16 files changed, 1551 insertions(+), 33 deletions(-) create mode 100644 debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IMemoryBlockAddressInfoRetrieval.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/memory/GdbMemoryBlockAddressInfoRetrieval.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/GdbMemoryAddressInfoVariablesRetrieval.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/MemoryBlockAddressInfoItem.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/memory/IGdbMemoryAddressInfoTypeRetrieval.java create mode 100644 memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/RenderingAddressInfo.java diff --git a/debug/org.eclipse.cdt.debug.core/META-INF/MANIFEST.MF b/debug/org.eclipse.cdt.debug.core/META-INF/MANIFEST.MF index 6a0ca4b12e0..1b05a7dae81 100644 --- a/debug/org.eclipse.cdt.debug.core/META-INF/MANIFEST.MF +++ b/debug/org.eclipse.cdt.debug.core/META-INF/MANIFEST.MF @@ -22,7 +22,8 @@ Export-Package: org.eclipse.cdt.debug.core, x-friends:="org.eclipse.cdt.dsf.gdb, org.eclipse.cdt.dsf.gdb.ui, 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.disassembly;x-internal:=true, org.eclipse.cdt.debug.internal.core.executables;x-internal:=true, diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IMemoryBlockAddressInfoRetrieval.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IMemoryBlockAddressInfoRetrieval.java new file mode 100644 index 00000000000..f08040ecb1d --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IMemoryBlockAddressInfoRetrieval.java @@ -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); + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbSessionAdapters.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbSessionAdapters.java index 08df30bf374..a28af4c0e54 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbSessionAdapters.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbSessionAdapters.java @@ -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 * are made available under the terms of the Eclipse Public License v1.0 * 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.IConnectHandler; 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.IReverseResumeHandler; 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.GdbStopTracingCommand; 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.launching.GdbLaunchDelegate; import org.eclipse.cdt.dsf.service.DsfSession; @@ -214,7 +216,8 @@ public class GdbSessionAdapters { IPinProvider.class, IDebugModelProvider.class, ILaunch.class, - ICEditorTextHover.class)); + ICEditorTextHover.class, + IMemoryBlockAddressInfoRetrieval.class)); } /** @@ -363,6 +366,11 @@ public class GdbSessionAdapters { if (ICEditorTextHover.class.equals(adapterType)) { return (T)new GdbDebugTextHover(); } + + if (IMemoryBlockAddressInfoRetrieval.class.equals(adapterType)) { + return (T) new GdbMemoryBlockAddressInfoRetrieval(session); + } + return null; } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/memory/GdbMemoryBlockAddressInfoRetrieval.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/memory/GdbMemoryBlockAddressInfoRetrieval.java new file mode 100644 index 00000000000..f126ebb1f43 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/memory/GdbMemoryBlockAddressInfoRetrieval.java @@ -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 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( + 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); + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index dba9e848f2d..73872b5105b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -27,13 +27,14 @@ Export-Package: org.eclipse.cdt.dsf.gdb, org.eclipse.cdt.tests.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.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.events;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.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.memory, org.eclipse.cdt.dsf.gdb.service, org.eclipse.cdt.dsf.gdb.service.command, org.eclipse.cdt.dsf.gdb.service.extensions, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/GdbMemoryAddressInfoVariablesRetrieval.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/GdbMemoryAddressInfoVariablesRetrieval.java new file mode 100644 index 00000000000..a9cc4f6a12b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/GdbMemoryAddressInfoVariablesRetrieval.java @@ -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 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(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 localsDMData = new ArrayList(); + + // 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(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 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(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 resolveService(Class 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(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 infoItems = new ArrayList(); + + // 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()]); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/MemoryBlockAddressInfoItem.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/MemoryBlockAddressInfoItem.java new file mode 100644 index 00000000000..088e5039185 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/memory/MemoryBlockAddressInfoItem.java @@ -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; + } + +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/memory/IGdbMemoryAddressInfoTypeRetrieval.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/memory/IGdbMemoryAddressInfoTypeRetrieval.java new file mode 100644 index 00000000000..2012c577044 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/memory/IGdbMemoryAddressInfoTypeRetrieval.java @@ -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 rm); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java index a2065fa6e5e..a469d184baa 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/model/DsfMemoryBlock.java @@ -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 * are made available under the terms of the Eclipse Public License v1.0 * 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.debug.internal.provisional.model.IMemoryBlockUpdatePolicyProvider; 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.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.cdt.dsf.internal.DsfPlugin; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; @@ -782,9 +782,9 @@ public class DsfMemoryBlock extends PlatformObject implements IMemoryBlockExtens /** * Get the context specified at construction. * - * @since 2.1 + * @since 2.7 */ - protected IMemoryDMContext getContext() { + public IMemoryDMContext getContext() { return fContext; } } diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AbstractPane.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AbstractPane.java index 62bf19282ce..24c341ed5ed 100755 --- a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AbstractPane.java +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AbstractPane.java @@ -874,24 +874,19 @@ public abstract class AbstractPane extends Canvas { super.setFont(font); fCharacterWidth = -1; - fCellHeight = -1; fTextHeight = -1; } - - private int fCellHeight = -1; // called often, cache - protected int getCellHeight() - { - if(fCellHeight == -1) - { - fCellHeight = getCellTextHeight() - + (fRendering.getCellPadding() * 2); - } + 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); - return fCellHeight; + return cellHeight; } - private int fCharacterWidth = -1; // called often, cache + private int fCharacterWidth = -1; // called often, cache protected int getCellCharacterWidth() { diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AddressPane.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AddressPane.java index 520f6cc8aca..86e7ed4bd0e 100644 --- a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AddressPane.java +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/AddressPane.java @@ -15,7 +15,6 @@ import java.math.BigInteger; import org.eclipse.debug.core.DebugException; import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; @@ -211,15 +210,13 @@ public class AddressPane extends AbstractPane GC gc = pe.gc; - FontMetrics fontMetrics = gc.getFontMetrics(); - int textHeight = fontMetrics.getHeight(); - int cellHeight = textHeight + (fRendering.getCellPadding() * 2); + int cellHeight = getCellHeight(); try { 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()); BigInteger lineAddress = start.add(BigInteger.valueOf(i diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/DataPane.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/DataPane.java index 9488b3bbac9..3e13968dbb8 100644 --- a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/DataPane.java +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/DataPane.java @@ -13,13 +13,20 @@ package org.eclipse.cdt.debug.ui.memory.traditional; 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.model.MemoryByte; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; @@ -33,6 +40,8 @@ import org.eclipse.swt.widgets.Shell; public class DataPane extends AbstractPane { private Shell fToolTipShell; + private final static String UNICODE_NORTH_WEST_ARROW = "\u2196"; + public DataPane(Rendering 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 */ private int getAddressableWidth() { - // derive the number of characters per addressable size e.g. 2 * NumOfOctets - int charsPerOctet = 2; + // derive the number of characters per addressable size e.g. 2 * NumOfOctets for hex representation + int charsPerOctet = fRendering.getRadixCharacterCount(fRendering.getRadix(), 1); int addressCharacterCount = fRendering.getAddressableSize() * charsPerOctet; // derive width by multiplying by the size of a character return addressCharacterCount * getCellCharacterWidth(); @@ -221,6 +230,30 @@ public class DataPane extends AbstractPane 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 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 protected void positionCaret(int x, int y) { @@ -327,7 +405,7 @@ public class DataPane extends AbstractPane { 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++) { @@ -405,6 +483,8 @@ public class DataPane extends AbstractPane cellHeight); } } + + markAddressesWithAdditionalInfo(gc); } 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> 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 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 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 keySet) { + List 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 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 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()); // Setting location of the tool tip diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/Rendering.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/Rendering.java index 0439739a98b..91ea1fc432a 100755 --- a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/Rendering.java +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/Rendering.java @@ -17,12 +17,15 @@ package org.eclipse.cdt.debug.ui.memory.traditional; import java.math.BigInteger; import java.text.MessageFormat; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.Vector; +import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval.IMemoryBlockAddressInfoItem; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; 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 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> fMapStartAddrToInfoItems = Collections + .synchronizedMap(new HashMap>()); + public Rendering(Composite parent, TraditionalRendering renderingParent) { super(parent, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.H_SCROLL @@ -351,7 +362,7 @@ public class Rendering extends Composite implements IDebugEventSetListener fViewportAddress = fViewportAddress.add(BigInteger .valueOf(getAddressableCellsPerRow())); ensureViewportAddressDisplayable(); - redrawPanes(); + redrawPanes(); } protected void handleUpArrow() @@ -435,7 +446,7 @@ public class Rendering extends Composite implements IDebugEventSetListener if(fAddressPane.isPaneVisible()) { fAddressPane.redraw(); - } + } redrawPanes(); break; } @@ -1184,6 +1195,8 @@ public class Rendering extends Composite implements IDebugEventSetListener fViewportCache.dispose(); fViewportCache = null; } + + fMapStartAddrToInfoItems.clear(); super.dispose(); } @@ -1625,7 +1638,10 @@ public class Rendering extends Composite implements IDebugEventSetListener ((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() @@ -2293,4 +2309,38 @@ public class Rendering extends Composite implements IDebugEventSetListener final List panes = Arrays.asList(getRenderingPanes()); 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> 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 ""; + } } diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/RenderingAddressInfo.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/RenderingAddressInfo.java new file mode 100644 index 00000000000..1f6abd17e23 --- /dev/null +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/RenderingAddressInfo.java @@ -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 fInfoTypeToItems = Collections + .synchronizedMap(new HashMap()); + 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 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 items) { + List collection = new ArrayList(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> 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> 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> 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 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 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 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); + } + +} diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TextPane.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TextPane.java index 038a9dc67ba..9b1a330d13c 100644 --- a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TextPane.java +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TextPane.java @@ -238,7 +238,7 @@ public class TextPane extends AbstractPane { 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++) { diff --git a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TraditionalRendering.java b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TraditionalRendering.java index a4c43de8d33..6695cbcbe9f 100644 --- a/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TraditionalRendering.java +++ b/memory/org.eclipse.cdt.debug.ui.memory.traditional/src/org/eclipse/cdt/debug/ui/memory/traditional/TraditionalRendering.java @@ -428,7 +428,7 @@ public class TraditionalRendering extends AbstractMemoryRendering implements IRe { allocateColors(); - this.fRendering = new Rendering(parent, this); + this.fRendering = new RenderingAddressInfo(parent, this); applyPreferences();