mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Merge remote-tracking branch 'origin/GP-1890_Dan_memoryBytesDiffColoring--SQUASHED'
This commit is contained in:
commit
5ee752891b
6 changed files with 135 additions and 22 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,7 +47,10 @@ import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.trace.model.Trace;
|
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.model.program.TraceProgramView;
|
||||||
|
import ghidra.trace.util.TraceAddressSpace;
|
||||||
import ghidra.util.Swing;
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvider {
|
public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvider {
|
||||||
|
@ -75,6 +78,19 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
return true;
|
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 {
|
protected class ForMemoryBytesGoToTrait extends DebuggerGoToTrait {
|
||||||
public ForMemoryBytesGoToTrait() {
|
public ForMemoryBytesGoToTrait() {
|
||||||
super(DebuggerMemoryBytesProvider.this.tool, DebuggerMemoryBytesProvider.this.plugin,
|
super(DebuggerMemoryBytesProvider.this.tool, DebuggerMemoryBytesProvider.this.plugin,
|
||||||
|
@ -151,7 +167,13 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
protected AutoReadMemorySpec autoReadMemorySpec = defaultReadMemorySpec;
|
protected AutoReadMemorySpec autoReadMemorySpec = defaultReadMemorySpec;
|
||||||
// TODO: followsCurrentSnap?
|
// TODO: followsCurrentSnap?
|
||||||
|
|
||||||
|
private final ListenerForChanges listenerForChanges = new ListenerForChanges();
|
||||||
|
|
||||||
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
|
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;
|
protected final boolean isMainViewer;
|
||||||
|
|
||||||
|
@ -174,6 +196,27 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
setHelpLocation(DebuggerResources.HELP_PROVIDER_MEMORY_BYTES);
|
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
|
@Override
|
||||||
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
|
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
|
@ -304,6 +347,18 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
return result;
|
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) {
|
protected DebuggerCoordinates adjustCoordinates(DebuggerCoordinates coordinates) {
|
||||||
if (followsCurrentThread) {
|
if (followsCurrentThread) {
|
||||||
return coordinates;
|
return coordinates;
|
||||||
|
@ -318,7 +373,10 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
|
||||||
current = coordinates;
|
current = coordinates;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
previous = current;
|
||||||
|
removeOldListeners();
|
||||||
current = coordinates;
|
current = coordinates;
|
||||||
|
addNewListeners();
|
||||||
doSetProgram(current.getView());
|
doSetProgram(current.getView());
|
||||||
goToTrait.goToCoordinates(coordinates);
|
goToTrait.goToCoordinates(coordinates);
|
||||||
trackingTrait.goToCoordinates(coordinates);
|
trackingTrait.goToCoordinates(coordinates);
|
||||||
|
|
|
@ -39,20 +39,12 @@ public class ByteBlockChangeManager {
|
||||||
private static String OLD_VALUE = "OldValue";
|
private static String OLD_VALUE = "OldValue";
|
||||||
private static String NEW_VALUE = "NewValue";
|
private static String NEW_VALUE = "NewValue";
|
||||||
|
|
||||||
private int dummy = 4;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct new change manager.
|
* Construct new change manager.
|
||||||
*/
|
*/
|
||||||
ByteBlockChangeManager(ProgramByteBlockSet blockSet) {
|
protected ByteBlockChangeManager(ProgramByteBlockSet blockSet, ByteBlockChangeManager bbcm) {
|
||||||
this.blockSet = blockSet;
|
this.blockSet = blockSet;
|
||||||
changeList = new ArrayList<ByteEditInfo>(3);
|
changeList = bbcm == null ? new ArrayList<>() : bbcm.changeList;
|
||||||
}
|
|
||||||
|
|
||||||
ByteBlockChangeManager(ProgramByteBlockSet blockSet, ByteBlockChangeManager bbcm) {
|
|
||||||
|
|
||||||
this.blockSet = blockSet;
|
|
||||||
changeList = bbcm.changeList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,7 +53,7 @@ public class ByteBlockChangeManager {
|
||||||
* @param edit edit object that has the old value and new value
|
* @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[] oldValue = edit.getOldValue();
|
||||||
byte[] newValue = edit.getNewValue();
|
byte[] newValue = edit.getNewValue();
|
||||||
|
|
||||||
|
@ -127,7 +119,7 @@ public class ByteBlockChangeManager {
|
||||||
*
|
*
|
||||||
* @return boolean true if an offset in the range was found
|
* @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);
|
Address blockAddr = blockSet.getBlockStart(block);
|
||||||
for (int i = 0; i < unitByteSize; i++) {
|
for (int i = 0; i < unitByteSize; i++) {
|
||||||
|
|
||||||
|
|
|
@ -46,12 +46,7 @@ public class ProgramByteBlockSet implements ByteBlockSet {
|
||||||
|
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.program = program;
|
this.program = program;
|
||||||
if (bbcm == null) {
|
this.bbcm = provider.newByteBlockChangeManager(this, bbcm);
|
||||||
this.bbcm = new ByteBlockChangeManager(this);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.bbcm = new ByteBlockChangeManager(this, bbcm);
|
|
||||||
}
|
|
||||||
|
|
||||||
newMemoryBlocks();
|
newMemoryBlocks();
|
||||||
}
|
}
|
||||||
|
@ -204,8 +199,7 @@ public class ProgramByteBlockSet implements ByteBlockSet {
|
||||||
/**
|
/**
|
||||||
* Get the address for the given block and offset.
|
* 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++) {
|
for (int i = 0; i < blocks.length; i++) {
|
||||||
if (blocks[i] != block) {
|
if (blocks[i] != block) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -537,7 +537,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
|
||||||
event.containsEvent(DomainObject.DO_DOMAIN_FILE_CHANGED)) {
|
event.containsEvent(DomainObject.DO_DOMAIN_FILE_CHANGED)) {
|
||||||
// drop all changes
|
// drop all changes
|
||||||
|
|
||||||
blockSet.setByteBlockChangeManager(new ByteBlockChangeManager(blockSet));
|
blockSet.setByteBlockChangeManager(newByteBlockChangeManager(blockSet, null));
|
||||||
updateManager.update();
|
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) {
|
protected ProgramByteBlockSet newByteBlockSet(ByteBlockChangeManager changeManager) {
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -125,7 +125,7 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpenProgramNoMemory() throws Exception {
|
public void testOpenProgramNoMemory() throws Exception {
|
||||||
tool.removePlugins(new Plugin[] { cbPlugin });
|
tool.removePlugins(List.of(cbPlugin));
|
||||||
|
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
ProgramManager pm = tool.getService(ProgramManager.class);
|
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue