GP-1890: Add diff coloring to Debugger memory bytes window

This commit is contained in:
Dan 2022-09-23 15:34:21 -04:00
parent f31e6bd52e
commit 8b4bf133b5
6 changed files with 135 additions and 22 deletions

View file

@ -0,0 +1,64 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.memory;
import java.nio.ByteBuffer;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.program.model.address.Address;
public class CachedBytePage {
private boolean valid = true;
private DebuggerCoordinates coordinates;
private Address start;
private byte[] page = new byte[4096];
private ByteBuffer buf = ByteBuffer.wrap(page);
public byte getByte(DebuggerCoordinates coordinates, Address address) {
long offset;
if (!valid || this.coordinates == null || !this.coordinates.equals(coordinates) ||
start == null || !start.hasSameAddressSpace(address)) {
offset = refresh(coordinates, address);
}
else {
offset = address.subtract(start);
if (offset < 0 || 4096 <= offset) {
offset = refresh(coordinates, address);
}
}
return page[(int) offset];
}
public void invalidate() {
valid = false;
}
private long refresh(DebuggerCoordinates coordinates, Address address) {
valid = false;
buf.clear();
Address min = address.getAddressSpace().getMinAddress();
start = address.subtractWrap(page.length / 2);
if (start.compareTo(min) < 0 || start.compareTo(address) > 0) {
start = min;
}
coordinates.getTrace()
.getMemoryManager()
.getViewBytes(coordinates.getViewSnap(), start, buf);
valid = true;
return address.subtract(start);
}
}

View file

@ -47,7 +47,10 @@ import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceMemoryBytesChangeType;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.Swing;
public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvider {
@ -75,6 +78,19 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
return true;
}
protected class ListenerForChanges extends TraceDomainObjectListener {
public ListenerForChanges() {
listenFor(TraceMemoryBytesChangeType.CHANGED, this::bytesChanged);
}
private void bytesChanged(TraceAddressSpace space) {
if (space.getAddressSpace().isMemorySpace()) {
currCache.invalidate();
prevCache.invalidate();
}
}
}
protected class ForMemoryBytesGoToTrait extends DebuggerGoToTrait {
public ForMemoryBytesGoToTrait() {
super(DebuggerMemoryBytesProvider.this.tool, DebuggerMemoryBytesProvider.this.plugin,
@ -151,7 +167,13 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
protected AutoReadMemorySpec autoReadMemorySpec = defaultReadMemorySpec;
// TODO: followsCurrentSnap?
private final ListenerForChanges listenerForChanges = new ListenerForChanges();
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private DebuggerCoordinates previous = DebuggerCoordinates.NOWHERE;
private final CachedBytePage currCache = new CachedBytePage();
private final CachedBytePage prevCache = new CachedBytePage();
protected final boolean isMainViewer;
@ -174,6 +196,27 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
setHelpLocation(DebuggerResources.HELP_PROVIDER_MEMORY_BYTES);
}
@Override
protected ByteBlockChangeManager newByteBlockChangeManager(ProgramByteBlockSet blockSet,
ByteBlockChangeManager bbcm) {
return new ByteBlockChangeManager(blockSet, bbcm) {
@Override
protected boolean isChanged(ByteBlock block, BigInteger offset, int unitByteSize) {
if (super.isChanged(block, offset, unitByteSize)) {
return true;
}
if (previous.getTrace() != current.getTrace()) {
return false;
}
Address address = blockSet.getAddress(block, offset);
if (address == null) {
return false;
}
return currCache.getByte(current, address) != prevCache.getByte(previous, address);
}
};
}
@Override
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
if (program == null) {
@ -304,6 +347,18 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
return result;
}
protected void removeOldListeners() {
if (current.getTrace() != null) {
current.getTrace().removeListener(listenerForChanges);
}
}
protected void addNewListeners() {
if (current.getTrace() != null) {
current.getTrace().addListener(listenerForChanges);
}
}
protected DebuggerCoordinates adjustCoordinates(DebuggerCoordinates coordinates) {
if (followsCurrentThread) {
return coordinates;
@ -318,7 +373,10 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
current = coordinates;
return;
}
previous = current;
removeOldListeners();
current = coordinates;
addNewListeners();
doSetProgram(current.getView());
goToTrait.goToCoordinates(coordinates);
trackingTrait.goToCoordinates(coordinates);

View file

@ -39,20 +39,12 @@ public class ByteBlockChangeManager {
private static String OLD_VALUE = "OldValue";
private static String NEW_VALUE = "NewValue";
private int dummy = 4;
/**
* Construct new change manager.
*/
ByteBlockChangeManager(ProgramByteBlockSet blockSet) {
protected ByteBlockChangeManager(ProgramByteBlockSet blockSet, ByteBlockChangeManager bbcm) {
this.blockSet = blockSet;
changeList = new ArrayList<ByteEditInfo>(3);
}
ByteBlockChangeManager(ProgramByteBlockSet blockSet, ByteBlockChangeManager bbcm) {
this.blockSet = blockSet;
changeList = bbcm.changeList;
changeList = bbcm == null ? new ArrayList<>() : bbcm.changeList;
}
/**
@ -61,7 +53,7 @@ public class ByteBlockChangeManager {
* @param edit edit object that has the old value and new value
*
*/
void add(ByteEditInfo edit) {
protected void add(ByteEditInfo edit) {
byte[] oldValue = edit.getOldValue();
byte[] newValue = edit.getNewValue();
@ -127,7 +119,7 @@ public class ByteBlockChangeManager {
*
* @return boolean true if an offset in the range was found
*/
boolean isChanged(ByteBlock block, BigInteger offset, int unitByteSize) {
protected boolean isChanged(ByteBlock block, BigInteger offset, int unitByteSize) {
Address blockAddr = blockSet.getBlockStart(block);
for (int i = 0; i < unitByteSize; i++) {

View file

@ -46,12 +46,7 @@ public class ProgramByteBlockSet implements ByteBlockSet {
this.provider = provider;
this.program = program;
if (bbcm == null) {
this.bbcm = new ByteBlockChangeManager(this);
}
else {
this.bbcm = new ByteBlockChangeManager(this, bbcm);
}
this.bbcm = provider.newByteBlockChangeManager(this, bbcm);
newMemoryBlocks();
}
@ -204,8 +199,7 @@ public class ProgramByteBlockSet implements ByteBlockSet {
/**
* Get the address for the given block and offset.
*/
protected Address getAddress(ByteBlock block, BigInteger offset) {
public Address getAddress(ByteBlock block, BigInteger offset) {
for (int i = 0; i < blocks.length; i++) {
if (blocks[i] != block) {
continue;

View file

@ -537,7 +537,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
event.containsEvent(DomainObject.DO_DOMAIN_FILE_CHANGED)) {
// drop all changes
blockSet.setByteBlockChangeManager(new ByteBlockChangeManager(blockSet));
blockSet.setByteBlockChangeManager(newByteBlockChangeManager(blockSet, null));
updateManager.update();
}
}
@ -563,6 +563,11 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
}
}
protected ByteBlockChangeManager newByteBlockChangeManager(ProgramByteBlockSet blockSet,
ByteBlockChangeManager bbcm) {
return new ByteBlockChangeManager(blockSet, bbcm);
}
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
if (program == null) {
return null;

View file

@ -125,7 +125,7 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testOpenProgramNoMemory() throws Exception {
tool.removePlugins(new Plugin[] { cbPlugin });
tool.removePlugins(List.of(cbPlugin));
runSwing(() -> {
ProgramManager pm = tool.getService(ProgramManager.class);