mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-13 21:56:19 +00:00
Merge remote-tracking branch 'origin/GP-2593_Dan_optimizeTraceProgramViewMemory--SQUASHED'
This commit is contained in:
commit
5b0d0d396b
|
@ -812,4 +812,8 @@ public class DBTrace extends DBCachedDomainObjectAdapter implements Trace, Trace
|
||||||
public void updateViewsRefreshBlocks() {
|
public void updateViewsRefreshBlocks() {
|
||||||
allViews(v -> v.updateMemoryRefreshBlocks());
|
allViews(v -> v.updateMemoryRefreshBlocks());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateViewsBytesChanged(AddressRange range) {
|
||||||
|
allViews(v -> v.updateBytesChanged(range));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -676,16 +676,18 @@ public class DBTraceMemorySpace
|
||||||
|
|
||||||
// Read back the written bytes and fire event
|
// Read back the written bytes and fire event
|
||||||
byte[] bytes = Arrays.copyOfRange(buf.array(), arrOff, arrOff + result);
|
byte[] bytes = Arrays.copyOfRange(buf.array(), arrOff, arrOff + result);
|
||||||
|
ImmutableTraceAddressSnapRange tasr = new ImmutableTraceAddressSnapRange(start,
|
||||||
|
start.add(result - 1), snap, lastSnap.snap);
|
||||||
trace.setChanged(new TraceChangeRecord<>(TraceMemoryBytesChangeType.CHANGED,
|
trace.setChanged(new TraceChangeRecord<>(TraceMemoryBytesChangeType.CHANGED,
|
||||||
this, new ImmutableTraceAddressSnapRange(start, start.add(result - 1),
|
this, tasr, oldBytes.array(), bytes));
|
||||||
snap, lastSnap.snap),
|
|
||||||
oldBytes.array(), bytes));
|
|
||||||
|
|
||||||
// Fixup affected code units
|
// Fixup affected code units
|
||||||
DBTraceCodeSpace codeSpace = trace.getCodeManager().get(this, false);
|
DBTraceCodeSpace codeSpace = trace.getCodeManager().get(this, false);
|
||||||
if (codeSpace != null) {
|
if (codeSpace != null) {
|
||||||
codeSpace.bytesChanged(changed, snap, start, oldBytes.array(), bytes);
|
codeSpace.bytesChanged(changed, snap, start, oldBytes.array(), bytes);
|
||||||
}
|
}
|
||||||
|
// Clear program view caches
|
||||||
|
trace.updateViewsBytesChanged(tasr.getRange());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1047,6 +1049,7 @@ public class DBTraceMemorySpace
|
||||||
regionMapSpace.invalidateCache();
|
regionMapSpace.invalidateCache();
|
||||||
regionCache.clear();
|
regionCache.clear();
|
||||||
trace.updateViewsRefreshBlocks();
|
trace.updateViewsRefreshBlocks();
|
||||||
|
trace.updateViewsBytesChanged(null);
|
||||||
stateMapSpace.invalidateCache();
|
stateMapSpace.invalidateCache();
|
||||||
bufferStore.invalidateCache();
|
bufferStore.invalidateCache();
|
||||||
blockStore.invalidateCache();
|
blockStore.invalidateCache();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.mem.*;
|
import ghidra.program.model.mem.*;
|
||||||
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||||
import ghidra.trace.model.memory.TraceMemorySpaceInputStream;
|
import ghidra.trace.model.memory.TraceMemorySpaceInputStream;
|
||||||
|
import ghidra.util.LockHold;
|
||||||
import ghidra.util.MathUtilities;
|
import ghidra.util.MathUtilities;
|
||||||
|
|
||||||
public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlock {
|
public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlock {
|
||||||
|
@ -96,6 +97,19 @@ public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlo
|
||||||
private final List<MemoryBlockSourceInfo> info =
|
private final List<MemoryBlockSourceInfo> info =
|
||||||
Collections.singletonList(new MyMemoryBlockSourceInfo());
|
Collections.singletonList(new MyMemoryBlockSourceInfo());
|
||||||
|
|
||||||
|
private static final int CACHE_PAGE_COUNT = 3;
|
||||||
|
private final ByteCache cache = new ByteCache(CACHE_PAGE_COUNT) {
|
||||||
|
@Override
|
||||||
|
protected int doLoad(Address address, ByteBuffer buf) throws MemoryAccessException {
|
||||||
|
DBTraceMemorySpace space =
|
||||||
|
program.trace.getMemoryManager().getMemorySpace(getAddressSpace(), false);
|
||||||
|
if (space == null) {
|
||||||
|
throw new MemoryAccessException("Space does not exist");
|
||||||
|
}
|
||||||
|
return space.getViewBytes(program.snap, address, buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected AbstractDBTraceProgramViewMemoryBlock(DBTraceProgramView program) {
|
protected AbstractDBTraceProgramViewMemoryBlock(DBTraceProgramView program) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +120,15 @@ public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlo
|
||||||
return getStart().getAddressSpace();
|
return getStart().getAddressSpace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be called when the snap changes or when bytes change
|
||||||
|
*/
|
||||||
|
protected void invalidateBytesCache(AddressRange range) {
|
||||||
|
if (range == null || range.intersects(getAddressRange())) {
|
||||||
|
cache.invalidate(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected DBTraceMemorySpace getMemorySpace() {
|
protected DBTraceMemorySpace getMemorySpace() {
|
||||||
return program.trace.getMemoryManager().getMemorySpace(getAddressSpace(), false);
|
return program.trace.getMemoryManager().getMemorySpace(getAddressSpace(), false);
|
||||||
}
|
}
|
||||||
|
@ -167,20 +190,13 @@ public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte getByte(Address addr) throws MemoryAccessException {
|
public byte getByte(Address addr) throws MemoryAccessException {
|
||||||
AddressRange range = getAddressRange();
|
try (LockHold hold = program.trace.lockRead()) {
|
||||||
if (!range.contains(addr)) {
|
AddressRange range = getAddressRange();
|
||||||
throw new MemoryAccessException();
|
if (!range.contains(addr)) {
|
||||||
|
throw new MemoryAccessException();
|
||||||
|
}
|
||||||
|
return cache.read(addr);
|
||||||
}
|
}
|
||||||
DBTraceMemorySpace space =
|
|
||||||
program.trace.getMemoryManager().getMemorySpace(range.getAddressSpace(), false);
|
|
||||||
if (space == null) {
|
|
||||||
throw new MemoryAccessException("Space does not exist");
|
|
||||||
}
|
|
||||||
ByteBuffer buf = ByteBuffer.allocate(1);
|
|
||||||
if (space.getViewBytes(program.snap, addr, buf) != 1) {
|
|
||||||
throw new MemoryAccessException();
|
|
||||||
}
|
|
||||||
return buf.get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -190,17 +206,22 @@ public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
public int getBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
||||||
AddressRange range = getAddressRange();
|
try (LockHold hold = program.trace.lockRead()) {
|
||||||
if (!range.contains(addr)) {
|
AddressRange range = getAddressRange();
|
||||||
throw new MemoryAccessException();
|
if (!range.contains(addr)) {
|
||||||
|
throw new MemoryAccessException();
|
||||||
|
}
|
||||||
|
if (cache.canCache(addr, len)) {
|
||||||
|
return cache.read(addr, ByteBuffer.wrap(b, off, len));
|
||||||
|
}
|
||||||
|
DBTraceMemorySpace space =
|
||||||
|
program.trace.getMemoryManager().getMemorySpace(range.getAddressSpace(), false);
|
||||||
|
if (space == null) {
|
||||||
|
throw new MemoryAccessException("Space does not exist");
|
||||||
|
}
|
||||||
|
len = MathUtilities.unsignedMin(len, range.getMaxAddress().subtract(addr) + 1);
|
||||||
|
return space.getViewBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
|
||||||
}
|
}
|
||||||
DBTraceMemorySpace space =
|
|
||||||
program.trace.getMemoryManager().getMemorySpace(range.getAddressSpace(), false);
|
|
||||||
if (space == null) {
|
|
||||||
throw new MemoryAccessException("Space does not exist");
|
|
||||||
}
|
|
||||||
len = MathUtilities.unsignedMin(len, range.getMaxAddress().subtract(addr) + 1);
|
|
||||||
return space.getViewBytes(program.snap, addr, ByteBuffer.wrap(b, off, len));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -217,6 +238,7 @@ public abstract class AbstractDBTraceProgramViewMemoryBlock implements MemoryBlo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
||||||
|
// NB. The trace will notify us of the write, and we invalidate then
|
||||||
AddressRange range = getAddressRange();
|
AddressRange range = getAddressRange();
|
||||||
if (!range.contains(addr)) {
|
if (!range.contains(addr)) {
|
||||||
throw new MemoryAccessException();
|
throw new MemoryAccessException();
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
/* ###
|
||||||
|
* 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.trace.database.program;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRange;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.util.MathUtilities;
|
||||||
|
|
||||||
|
public abstract class ByteCache {
|
||||||
|
public static final int BITS = 12;
|
||||||
|
public static final long OFFSET_MASK = -1L << BITS;
|
||||||
|
public static final int SIZE = 1 << BITS;
|
||||||
|
|
||||||
|
private class Page {
|
||||||
|
private volatile boolean valid = false;
|
||||||
|
private Address start = null;
|
||||||
|
private final byte[] bytes = new byte[SIZE];
|
||||||
|
private final ByteBuffer buf = ByteBuffer.wrap(bytes);
|
||||||
|
private int len;
|
||||||
|
|
||||||
|
public final boolean contains(Address address, int length) {
|
||||||
|
if (!valid || start == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long offset = address.subtract(start);
|
||||||
|
return Long.compareUnsigned(offset + length, len) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int load(Address address, int length) throws MemoryAccessException {
|
||||||
|
valid = false;
|
||||||
|
start = address.getNewAddress(address.getOffset() & OFFSET_MASK);
|
||||||
|
long offset = address.subtract(start);
|
||||||
|
buf.clear(); // NB. Do not limit. Cache what we can.
|
||||||
|
len = doLoad(address, buf);
|
||||||
|
if (len < offset + length) {
|
||||||
|
throw new MemoryAccessException();
|
||||||
|
}
|
||||||
|
valid = true;
|
||||||
|
return (int) offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate(AddressRange range) {
|
||||||
|
// TODO: Is it worth it to check for intersection?
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int pageCount;
|
||||||
|
private final Page[] pages;
|
||||||
|
|
||||||
|
public ByteCache(int pageCount) {
|
||||||
|
this.pageCount = pageCount;
|
||||||
|
pages = new Page[pageCount];
|
||||||
|
for (int i = 0; i < pageCount; i++) {
|
||||||
|
pages[i] = newPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Page newPage() {
|
||||||
|
return new Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canCache(Address address, int len) {
|
||||||
|
long cacheBufOff = address.getOffset() & ~OFFSET_MASK;
|
||||||
|
return cacheBufOff + len < pageCount * SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte read(Address address) throws MemoryAccessException {
|
||||||
|
Address pageStart = address.getNewAddress(address.getOffset() & OFFSET_MASK);
|
||||||
|
Page page = ensurePageCached(pageStart, 1);
|
||||||
|
int cacheBufOff = (int) address.subtract(pageStart);
|
||||||
|
return page.bytes[cacheBufOff];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(Address address, ByteBuffer buf) throws MemoryAccessException {
|
||||||
|
long startOff = address.getOffset();
|
||||||
|
long startPage = startOff & OFFSET_MASK;
|
||||||
|
int bufStart = buf.position();
|
||||||
|
|
||||||
|
long memOffset = startPage;
|
||||||
|
int cacheBufOff = (int) (startOff - startPage);
|
||||||
|
while (buf.hasRemaining()) {
|
||||||
|
int required = MathUtilities.unsignedMin(SIZE - cacheBufOff, buf.remaining());
|
||||||
|
Page page = ensurePageCached(address.getNewAddress(memOffset), required);
|
||||||
|
buf.put(page.bytes, cacheBufOff, required);
|
||||||
|
memOffset += SIZE;
|
||||||
|
cacheBufOff = 0;
|
||||||
|
}
|
||||||
|
return buf.position() - bufStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int choosePage(Address address, int len) {
|
||||||
|
for (int i = 0; i < pageCount; i++) {
|
||||||
|
Page page = pages[i];
|
||||||
|
if (page.contains(address, len)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure a page is cached
|
||||||
|
*
|
||||||
|
* @param address the start address of the page
|
||||||
|
* @param len the minimum number of bytes required from the page
|
||||||
|
* @return the chosen cache page
|
||||||
|
* @throws MemoryAccessException if the required bytes cannot be read
|
||||||
|
*/
|
||||||
|
private Page ensurePageCached(Address address, int len) throws MemoryAccessException {
|
||||||
|
int chosen = choosePage(address, len);
|
||||||
|
if (chosen == -1) {
|
||||||
|
pages[pageCount - 1].load(address, len);
|
||||||
|
chosen = pageCount - 1;
|
||||||
|
}
|
||||||
|
if (chosen == 0) {
|
||||||
|
return pages[0];
|
||||||
|
}
|
||||||
|
synchronized (pages) {
|
||||||
|
Page temp = pages[chosen];
|
||||||
|
pages[chosen] = pages[0];
|
||||||
|
pages[0] = temp;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int doLoad(Address address, ByteBuffer buf) throws MemoryAccessException;
|
||||||
|
|
||||||
|
public void invalidate(AddressRange range) {
|
||||||
|
synchronized (pages) {
|
||||||
|
for (Page p : pages) {
|
||||||
|
p.invalidate(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1637,6 +1637,10 @@ public class DBTraceProgramView implements TraceProgramView {
|
||||||
memory.updateRefreshBlocks();
|
memory.updateRefreshBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateBytesChanged(AddressRange range) {
|
||||||
|
memory.updateBytesChanged(range);
|
||||||
|
}
|
||||||
|
|
||||||
protected DomainObjectEventQueues getEventQueues(TraceAddressSpace space) {
|
protected DomainObjectEventQueues getEventQueues(TraceAddressSpace space) {
|
||||||
// TODO: Should there be views on other frames?
|
// TODO: Should there be views on other frames?
|
||||||
// IIRC, this was an abandoned experiment for "register listings"
|
// IIRC, this was an abandoned experiment for "register listings"
|
||||||
|
|
|
@ -67,6 +67,12 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setSnap(long snap) {
|
||||||
|
super.setSnap(snap);
|
||||||
|
updateBytesChanged(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void recomputeAddressSet() {
|
protected void recomputeAddressSet() {
|
||||||
AddressSet temp = new AddressSet();
|
AddressSet temp = new AddressSet();
|
||||||
|
@ -155,4 +161,15 @@ public class DBTraceProgramViewMemory extends AbstractDBTraceProgramViewMemory {
|
||||||
spaceBlocks.clear();
|
spaceBlocks.clear();
|
||||||
recomputeAddressSet();
|
recomputeAddressSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateBytesChanged(AddressRange range) {
|
||||||
|
if (regionBlocks == null) { // <init> order
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (AbstractDBTraceProgramViewMemoryBlock block : forceFullView
|
||||||
|
? spaceBlocks.values()
|
||||||
|
: regionBlocks.values()) {
|
||||||
|
block.invalidateBytesCache(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue