Merge remote-tracking branch 'origin/GP-1225_Dan_pcodeFrameUseropDisplay--SQUASHED'

This commit is contained in:
Ryan Kurtz 2021-11-23 13:52:57 -05:00
commit 185bc0b2c2
9 changed files with 163 additions and 16 deletions

View file

@ -215,7 +215,10 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
boolean isCurrent = counter == data.getRowModelIndex();
if (data.isSelected()) {
if (isCurrent) {
setBackground(ColorUtils.blend(counterColor, cursorColor, 0.5f));
Color blend = ColorUtils.blend(counterColor, cursorColor, 0.5f);
if (blend != null) {
setBackground(blend);
}
}
// else background is already set. Leave it alone
}
@ -574,7 +577,8 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
int index = frame.index();
List<PcodeRow> toAdd = frame.getCode()
.stream()
.map(op -> new OpPcodeRow(language, op, index == op.getSeqnum().getTime()))
.map(op -> new OpPcodeRow(language, op, index == op.getSeqnum().getTime(),
frame.getUseropNames()))
.collect(Collectors.toCollection(ArrayList::new));
if (frame.isBranch()) {
counter = toAdd.size();

View file

@ -15,6 +15,8 @@
*/
package ghidra.app.plugin.core.debug.gui.pcode;
import java.util.Map;
import ghidra.pcode.exec.PcodeProgram;
import ghidra.program.model.lang.Language;
import ghidra.program.model.pcode.PcodeOp;
@ -23,11 +25,14 @@ public class OpPcodeRow implements PcodeRow {
protected final Language language;
protected final PcodeOp op;
protected final boolean isNext;
protected final Map<Integer, String> useropNames;
public OpPcodeRow(Language language, PcodeOp op, boolean isNext) {
public OpPcodeRow(Language language, PcodeOp op, boolean isNext,
Map<Integer, String> useropNames) {
this.language = language;
this.op = op;
this.isNext = isNext;
this.useropNames = useropNames;
}
@Override
@ -37,7 +42,7 @@ public class OpPcodeRow implements PcodeRow {
@Override
public String getCode() {
return "<html>" + PcodeProgram.opToString(language, op, true) + "</html>";
return "<html>" + PcodeProgram.opToString(language, op, true, useropNames) + "</html>";
}
public boolean isNext() {

View file

@ -56,7 +56,7 @@ public interface DebuggerEmulationService {
*
* <p>
* This is the preferred means of performing emulation. Because the underlying emulator may
* request <em>blocking</em> read of a target, it is important that
* request a <em>blocking</em> read from a target, it is important that
* {@link #emulate(Trace, TraceSchedule, TaskMonitor)} is <em>never</em> called by the Swing
* thread.
*
@ -72,7 +72,9 @@ public interface DebuggerEmulationService {
*
* <p>
* To guarantee the emulator is present, call {@link #backgroundEmulate(Trace, TraceSchedule)}
* first. WARNING: This emulator belongs to this service. Stepping it, or otherwise manipulating
* first.
* <p>
* <b>WARNING:</b> This emulator belongs to this service. Stepping it, or otherwise manipulating
* it without the service's knowledge can lead to unintended consequences.
*
* @param trace the trace containing the initial state

View file

@ -0,0 +1,122 @@
/* ###
* 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.pcode;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.Range;
import ghidra.app.plugin.assembler.Assembler;
import ghidra.app.plugin.assembler.Assemblers;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerTracePcodeEmulator;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.services.DebuggerEmulationService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.PcodeExecutor;
import ghidra.pcode.exec.trace.TraceSleighUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSchedule;
import ghidra.util.database.UndoableTransaction;
public class DebuggerPcodeStepperProviderTest extends AbstractGhidraHeadedDebuggerGUITest {
protected DebuggerTraceManagerService traceManager;
protected DebuggerPcodeStepperPlugin pcodePlugin;
protected DebuggerListingPlugin listingPlugin;
protected DebuggerPcodeStepperProvider pcodeProvider;
protected DebuggerEmulationService emuService;
@Before
public void setUpPcodeStepperProviderTest() throws Exception {
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
pcodePlugin = addPlugin(tool, DebuggerPcodeStepperPlugin.class);
listingPlugin = addPlugin(tool, DebuggerListingPlugin.class); // For colors
emuService = tool.getService(DebuggerEmulationService.class);
pcodeProvider = waitForComponentProvider(DebuggerPcodeStepperProvider.class);
createTrace();
}
@Test
public void testCustomUseropDisplay() throws Exception {
Address start = tb.addr(0x00400000);
TraceThread thread;
InstructionIterator iit;
try (UndoableTransaction tid = tb.startTransaction()) {
tb.trace.getMemoryManager()
.addRegion("echo:.text", Range.atLeast(0L), tb.range(0x00400000, 0x0040ffff),
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
thread = tb.getOrAddThread("1", 0);
PcodeExecutor<byte[]> init = TraceSleighUtils.buildByteExecutor(tb.trace, 0, thread, 0);
init.executeLine("pc = 0x00400000");
Assembler asm = Assemblers.getAssembler(tb.trace.getFixedProgramView(0));
iit = asm.assemble(start,
"imm r0, #1234",
"imm r1, #2045"); // 11 bits unsigned
}
Instruction imm1234 = iit.next();
Instruction imm2045 = iit.next();
TraceSchedule schedule1 = TraceSchedule.parse("0:.t0-1");
traceManager.openTrace(tb.trace);
traceManager.activateThread(thread);
traceManager.activateTime(schedule1);
DebuggerTracePcodeEmulator emu =
waitForValue(() -> emuService.getCachedEmulator(tb.trace, schedule1));
assertNotNull(emu);
// P-code step to decode already done. One for each op. One to retire.
TraceSchedule schedule2 =
schedule1.steppedPcodeForward(thread, imm1234.getPcode().length + 1);
PcodeThread<byte[]> et = emu.getThread(thread.getPath(), false);
traceManager.activateTime(schedule2);
waitForPass(() -> assertNull(et.getFrame()));
/**
* NB. at the moment, there is no API to customize the service's emulator. In the meantime,
* the vanilla PcodeThread does inject a custom library for breakpoints, so we'll use that
* as our "custom userop" test case. It might also be nice if the emulator service placed
* breakpoints, no?
*/
emu.addBreakpoint(imm2045.getAddress(), "1:1");
// Just one to decode is necessary
TraceSchedule schedule3 = schedule2.steppedPcodeForward(thread, 1);
traceManager.activateTime(schedule3);
waitForPass(() -> assertEquals(schedule3, pcodeProvider.current.getTime()));
assertTrue(pcodeProvider.pcodeTableModel.getModelData()
.stream()
.anyMatch(r -> r.getCode().contains("emu_swi")));
}
}

View file

@ -258,7 +258,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
}
protected void beginInstructionOrInject() {
PcodeProgram inj = injects.get(counter);
PcodeProgram inj = getInject(counter);
if (inj != null) {
instruction = null;
frame = executor.begin(inj);

View file

@ -69,7 +69,8 @@ public class PcodeFrame {
sb.append(" ");
}
PcodeOp op = code.get(i);
sb.append(op.getSeqnum() + ": " + PcodeProgram.opToString(language, op, false));
sb.append(
op.getSeqnum() + ": " + PcodeProgram.opToString(language, op, false, useropNames));
}
if (index == code.size()) {
sb.append("\n *> fall-through");
@ -98,6 +99,10 @@ public class PcodeFrame {
return useropNames.get(userop);
}
public Map<Integer, String> getUseropNames() {
return useropNames;
}
public boolean isFallThrough() {
return index == code.size();
}

View file

@ -102,11 +102,12 @@ public class PcodeProgram {
}
}
public static String useropToString(Language language, Varnode vn, boolean markup) {
public static String useropToString(Language language, Varnode vn, boolean markup,
Map<Integer, String> useropNames) {
if (!vn.isConstant()) {
throw new IllegalArgumentException("userop index must be a constant varnode");
}
String display = "\"" + language.getUserDefinedOpName((int) vn.getOffset()) + "\"";
String display = "\"" + useropNames.get((int) vn.getOffset()) + "\"";
if (markup) {
return htmlSpan("userop", display);
}
@ -124,7 +125,8 @@ public class PcodeProgram {
}
}
public static String opToString(Language language, PcodeOp op, boolean markup) {
public static String opToString(Language language, PcodeOp op, boolean markup,
Map<Integer, String> useropNames) {
StringBuilder sb = new StringBuilder();
Varnode output = op.getOutput();
if (output != null) {
@ -146,7 +148,7 @@ public class PcodeProgram {
}
else if (isUserop) {
sb.append(' ');
sb.append(useropToString(language, op.getInput(0), markup));
sb.append(useropToString(language, op.getInput(0), markup, useropNames));
i = 1;
}
else {
@ -208,7 +210,8 @@ public class PcodeProgram {
public String toString() {
StringBuilder sb = new StringBuilder("<" + getHead() + ":");
for (PcodeOp op : code) {
sb.append("\n " + op.getSeqnum() + ": " + opToString(language, op, false));
sb.append(
"\n " + op.getSeqnum() + ": " + opToString(language, op, false, useropNames));
}
sb.append("\n>");
return sb.toString();

View file

@ -168,7 +168,12 @@ public class ColorUtils {
* @return the new color
*/
public static Color blend(Color c1, Color c2, float ratio) {
if (c1 == null) {
return c2;
}
if (c2 == null) {
return c1;
}
float rgb1[] = new float[3];
float rgb2[] = new float[3];
c1.getColorComponents(rgb1);

View file

@ -113,8 +113,9 @@ public class SleighAssembler implements Assembler {
Address end = at.add(insbytes.length - 1);
listing.clearCodeUnits(at, end, false);
memory.setBytes(at, insbytes);
dis.disassemble(at, new AddressSet(at));
return listing.getInstructions(new AddressSet(at, end), true);
AddressSet set = new AddressSet(at, end);
dis.disassemble(at, set);
return listing.getInstructions(set, true);
}
@Override