/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.memory;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import jpcsp.Allegrex.compiler.RuntimeContext;
import jpcsp.Debugger.MemoryBreakpoints.MemoryBreakpoint;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.Processor;
import jpcsp.util.Utilities;
public class DebuggerMemory extends Memory {
public boolean traceMemoryRead = false;
public boolean traceMemoryWrite = false;
public boolean traceMemoryRead8 = false;
public boolean traceMemoryWrite8 = false;
public boolean traceMemoryRead16 = false;
public boolean traceMemoryWrite16 = false;
public boolean traceMemoryRead32 = false;
public boolean traceMemoryWrite32 = false;
public boolean pauseEmulatorOnMemoryBreakpoint = false;
private HashSet<Integer> memoryReadBreakpoint;
private HashSet<Integer> memoryWriteBreakpoint;
private List<MemoryBreakpoint> memoryBreakpoints;
private Memory mem;
// external breakpoint list
public static String mBrkFilePath = "Memory.mbrk";
public DebuggerMemory(Memory mem) {
this.mem = mem;
memoryReadBreakpoint = new HashSet<Integer>();
memoryWriteBreakpoint = new HashSet<Integer>();
memoryBreakpoints = new LinkedList<MemoryBreakpoint>();
// backwards compatibility
if (new File(mBrkFilePath).exists()) {
importBreakpoints(mBrkFilePath);
}
}
public List<MemoryBreakpoint> getMemoryBreakpoints() {
return memoryBreakpoints;
}
private void setBreakpointToken(String token) {
if (token.isEmpty()) {
return;
}
if (token.equals("read")) {
traceMemoryRead = true;
} else if (token.equals("read8")) {
traceMemoryRead8 = true;
} else if (token.equals("read16")) {
traceMemoryRead16 = true;
} else if (token.equals("read32")) {
traceMemoryRead32 = true;
} else if (token.equals("write")) {
traceMemoryWrite = true;
} else if (token.equals("write8")) {
traceMemoryWrite8 = true;
} else if (token.equals("write16")) {
traceMemoryWrite16 = true;
} else if (token.equals("write32")) {
traceMemoryWrite32 = true;
} else if (token.equals("pause")) {
pauseEmulatorOnMemoryBreakpoint = true;
} else {
log.error(String.format("Unknown token '%s'", token));
}
}
final public void exportBreakpoints(String filename) {
exportBreakpoints(new File(filename));
}
final public void exportBreakpoints(File f) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new FileWriter(f));
Iterator<MemoryBreakpoint> it = memoryBreakpoints.iterator();
while (it.hasNext()) {
MemoryBreakpoint mbp = it.next();
String line = "";
switch (mbp.getAccess()) {
case READ:
line += "R ";
break;
case WRITE:
line += "W ";
break;
case READWRITE:
line += "RW ";
break;
default:
// ignore - but should never happen
continue;
}
line += String.format("0x%08X", mbp.getStartAddress());
if (mbp.getStartAddress() != mbp.getEndAddress()) {
line += String.format(" - 0x%08X", mbp.getEndAddress());
}
line += System.getProperty("line.separator");
out.write(line);
}
if (traceMemoryRead) {
out.write("read|");
}
if (traceMemoryWrite) {
out.write("write|");
}
if (traceMemoryRead8) {
out.write("read8|");
}
if (traceMemoryWrite8) {
out.write("write8|");
}
if (traceMemoryRead16) {
out.write("read16|");
}
if (traceMemoryWrite16) {
out.write("write16|");
}
if (traceMemoryRead32) {
out.write("read32|");
}
if (traceMemoryWrite32) {
out.write("write32|");
}
if (pauseEmulatorOnMemoryBreakpoint) {
out.write("pause");
}
out.write(System.getProperty("line.separator"));
} catch (IOException ioe) {
// ignore
} finally {
Utilities.close(out);
}
}
final public void importBreakpoints(String filename) {
importBreakpoints(new File(filename));
}
final public void importBreakpoints(File f) {
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(f));
// reset current configuration
traceMemoryRead = false;
traceMemoryWrite = false;
traceMemoryRead8 = false;
traceMemoryWrite8 = false;
traceMemoryRead16 = false;
traceMemoryWrite16 = false;
traceMemoryRead32 = false;
traceMemoryWrite32 = false;
pauseEmulatorOnMemoryBreakpoint = false;
while (true) {
String line = in.readLine();
if (line == null) {
break;
}
line = line.trim();
int rangeIndex = line.indexOf("-");
if (rangeIndex >= 0) {
// Range parsing
if (line.startsWith("RW ")) {
int start = Utilities.parseAddress(line.substring(2, rangeIndex));
int end = Utilities.parseAddress(line.substring(rangeIndex + 1));
memoryBreakpoints.add(new MemoryBreakpoint(this, start, end, MemoryBreakpoint.AccessType.READWRITE));
} else if (line.startsWith("R ")) {
int start = Utilities.parseAddress(line.substring(1, rangeIndex));
int end = Utilities.parseAddress(line.substring(rangeIndex + 1));
memoryBreakpoints.add(new MemoryBreakpoint(this, start, end, MemoryBreakpoint.AccessType.READ));
} else if (line.startsWith("W ")) {
int start = Utilities.parseAddress(line.substring(1, rangeIndex));
int end = Utilities.parseAddress(line.substring(rangeIndex + 1));
memoryBreakpoints.add(new MemoryBreakpoint(this, start, end, MemoryBreakpoint.AccessType.WRITE));
}
} else if (line.startsWith("RW ")) {
int address = Utilities.parseAddress(line.substring(2));
memoryBreakpoints.add(new MemoryBreakpoint(this, address, MemoryBreakpoint.AccessType.READWRITE));
} else if (line.startsWith("R ")) {
int address = Utilities.parseAddress(line.substring(1));
memoryBreakpoints.add(new MemoryBreakpoint(this, address, MemoryBreakpoint.AccessType.READ));
} else if (line.startsWith("W ")) {
int address = Utilities.parseAddress(line.substring(1));
memoryBreakpoints.add(new MemoryBreakpoint(this, address, MemoryBreakpoint.AccessType.WRITE));
} else if (!line.startsWith("#")) {
String[] tokens = line.split("\\|");
for (int i = 0; tokens != null && i < tokens.length; i++) {
String token = tokens[i].trim().toLowerCase();
setBreakpointToken(token);
}
}
}
} catch (IOException ioe) {
// ignore
} finally {
Utilities.close(in);
}
log.info(String.format("%d memory breakpoint(s) imported", memoryBreakpoints.size()));
}
public static boolean isInstalled() {
return Memory.getInstance() instanceof DebuggerMemory;
}
public static void install() {
if (!isInstalled()) {
log.info("Using DebuggerMemory");
DebuggerMemory debuggerMemory = new DebuggerMemory(Memory.getInstance());
Memory.setInstance(debuggerMemory);
RuntimeContext.updateMemory();
}
}
public static void deinstall() {
if (isInstalled()) {
DebuggerMemory debuggerMemory = (DebuggerMemory) Memory.getInstance();
Memory.setInstance(debuggerMemory.mem);
}
}
public void addReadBreakpoint(int address) {
address &= Memory.addressMask;
memoryReadBreakpoint.add(address);
}
public void removeReadBreakpoint(int address) {
address &= Memory.addressMask;
memoryReadBreakpoint.remove(address);
}
public void addRangeReadBreakpoint(int start, int end) {
for (int address = start; address <= end; address++) {
addReadBreakpoint(address);
}
}
public void removeRangeReadBreakpoint(int start, int end) {
for (int address = start; address <= end; address++) {
removeReadBreakpoint(address);
}
}
public void addWriteBreakpoint(int address) {
address &= Memory.addressMask;
memoryWriteBreakpoint.add(address);
}
public void removeWriteBreakpoint(int address) {
address &= Memory.addressMask;
memoryWriteBreakpoint.remove(address);
}
public void addRangeWriteBreakpoint(int start, int end) {
for (int address = start; address <= end; address++) {
addWriteBreakpoint(address);
}
}
public void removeRangeWriteBreakpoint(int start, int end) {
for (int address = start; address <= end; address++) {
removeWriteBreakpoint(address);
}
}
public void addReadWriteBreakpoint(int address) {
address &= Memory.addressMask;
memoryReadBreakpoint.add(address);
memoryWriteBreakpoint.add(address);
}
public void removeReadWriteBreakpoint(int address) {
address &= Memory.addressMask;
memoryReadBreakpoint.remove(address);
memoryWriteBreakpoint.remove(address);
}
public void addRangeReadWriteBreakpoint(int start, int end) {
for (int address = start; address <= end; address++) {
addReadWriteBreakpoint(address);
}
}
public void removeRangeReadWriteBreakpoint(int start, int end) {
for (int address = start; address <= end; address++) {
removeReadWriteBreakpoint(address);
}
}
protected String getMemoryReadMessage(int address, int width) {
StringBuilder message = new StringBuilder();
Processor processor = Emulator.getProcessor();
if (processor != null) {
message.append(String.format("0x%08X - ", processor.cpu.pc));
}
if (width == 8 || width == 16 || width == 32) {
message.append(String.format("read%d(0x%08X)=0x", width, address));
if (width == 8) {
message.append(String.format("%02X", mem.read8(address)));
} else if (width == 16) {
message.append(String.format("%04X", mem.read16(address)));
} else if (width == 32) {
int value = mem.read32(address);
message.append(String.format("%08X (%f)", value, Float.intBitsToFloat(value)));
}
} else {
int length = width / 8;
message.append(String.format("read 0x%08X-0x%08X (length=%d)", address, address + length, length));
}
return message.toString();
}
protected void memoryRead(int address, int width, boolean trace) {
address &= Memory.addressMask;
if ((traceMemoryRead || trace) && log.isTraceEnabled()) {
log.trace(getMemoryReadMessage(address, width));
}
if ((pauseEmulatorOnMemoryBreakpoint || log.isInfoEnabled()) && memoryReadBreakpoint.contains(address)) {
log.info(getMemoryReadMessage(address, width));
if (pauseEmulatorOnMemoryBreakpoint) {
Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_BREAKPOINT);
}
}
}
protected String getMemoryWriteMessage(int address, int value, int width) {
StringBuilder message = new StringBuilder();
Processor processor = Emulator.getProcessor();
if (processor != null) {
message.append(String.format("0x%08X - ", processor.cpu.pc));
}
message.append(String.format("write%d(0x%08X, 0x", width, address));
if (width == 8) {
message.append(String.format("%02X", value & 0xFF));
} else if (width == 16) {
message.append(String.format("%04X", value & 0xFFFF));
} else if (width == 32) {
message.append(String.format("%08X (%f)", value, Float.intBitsToFloat(value)));
}
message.append(")");
return message.toString();
}
protected void memoryWrite(int address, int value, int width, boolean trace) {
address &= Memory.addressMask;
if ((traceMemoryWrite || trace) && log.isTraceEnabled()) {
log.trace(getMemoryWriteMessage(address, value, width));
}
if ((pauseEmulatorOnMemoryBreakpoint || log.isInfoEnabled()) && memoryWriteBreakpoint.contains(address)) {
log.info(getMemoryWriteMessage(address, value, width));
if (pauseEmulatorOnMemoryBreakpoint) {
Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_BREAKPOINT);
}
}
}
@Override
public void Initialise() {
mem.Initialise();
}
@Override
public boolean allocate() {
return mem.allocate();
}
@Override
public void copyToMemory(int address, ByteBuffer source, int length) {
// Perform copyToMemory using write8 to check memory access
for (int i = 0; i < length && source.hasRemaining(); i++) {
byte value = source.get();
write8(address + i, value);
}
}
@Override
public Buffer getBuffer(int address, int length) {
memoryRead(address, length * 8, false);
return mem.getBuffer(address, length);
}
@Override
public Buffer getMainMemoryByteBuffer() {
return mem.getMainMemoryByteBuffer();
}
@Override
protected void memcpy(int destination, int source, int length, boolean checkOverlap) {
// Perform memcpy using read8/write8 to check memory access
for (int i = 0; i < length; i++) {
write8(destination + i, (byte) read8(source + i));
}
}
@Override
public void memset(int address, byte data, int length) {
// Perform memset using write8 to check memory access
for (int i = 0; i < length; i++) {
write8(address + i, data);
}
}
@Override
public int read8(int address) {
memoryRead(address, 8, traceMemoryRead8);
return mem.read8(address);
}
@Override
public int read16(int address) {
memoryRead(address, 16, traceMemoryRead16);
return mem.read16(address);
}
@Override
public int read32(int address) {
memoryRead(address, 32, traceMemoryRead32);
return mem.read32(address);
}
@Override
public void write8(int address, byte data) {
memoryWrite(address, data, 8, traceMemoryWrite8);
mem.write8(address, data);
}
@Override
public void write16(int address, short data) {
memoryWrite(address, data, 16, traceMemoryWrite16);
mem.write16(address, data);
}
@Override
public void write32(int address, int data) {
memoryWrite(address, data, 32, traceMemoryWrite32);
mem.write32(address, data);
}
@Override
public void setIgnoreInvalidMemoryAccess(boolean ignoreInvalidMemoryAccess) {
super.setIgnoreInvalidMemoryAccess(ignoreInvalidMemoryAccess);
if (mem != null) {
mem.setIgnoreInvalidMemoryAccess(ignoreInvalidMemoryAccess);
}
}
}