GP-4353: Fix columns. Refactor and fix tests.

This commit is contained in:
Dan 2024-02-22 19:47:30 +00:00
parent c5bad0a88f
commit 13da53176f
9 changed files with 144 additions and 95 deletions

View File

@ -73,7 +73,14 @@ public enum PCByStackLocationTrackingSpec implements LocationTrackingSpec, Locat
return null;
}
long snap = coordinates.getSnap();
TraceStack stack = trace.getStackManager().getLatestStack(thread, snap);
TraceStack stack;
try {
stack = trace.getStackManager().getLatestStack(thread, snap);
}
catch (IllegalStateException e) {
// Schema does not specify a stack
return null;
}
if (stack == null) {
return null;
}

View File

@ -18,18 +18,17 @@ package ghidra.app.plugin.core.debug.gui.model;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.event.ListSelectionListener;
import docking.widgets.table.DynamicTableColumn;
import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.Plugin;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace;
import ghidra.trace.model.target.TraceObject;
import ghidra.util.datastruct.ListenerSet;
import ghidra.util.table.GhidraTable;
@ -209,24 +208,6 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
return List.copyOf(tableModel.getModelData());
}
@SuppressWarnings("unchecked")
public <V> Map.Entry<Integer, DynamicTableColumn<T, V, Trace>> getColumnByNameAndType(
String name, Class<V> type) {
int count = tableModel.getColumnCount();
for (int i = 0; i < count; i++) {
DynamicTableColumn<T, ?, ?> column = tableModel.getColumn(i);
if (!name.equals(column.getColumnName())) {
continue;
}
if (column.getColumnClass() != type) {
continue;
}
return Map.entry(table.convertColumnIndexToView(i),
(DynamicTableColumn<T, V, Trace>) column);
}
return null;
}
public void setDiffColor(Color diffColor) {
tableModel.setDiffColor(diffColor);
}

View File

@ -84,6 +84,9 @@ public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObje
ServiceProvider serviceProvider) throws IllegalArgumentException {
TraceObjectValue value =
rowObject.getAttributeEntry(TargetStackFrame.PC_ATTRIBUTE_NAME);
if (value == null) {
return null;
}
return DebuggerStaticMappingUtils.getFunction(value.castValue(), provider.current,
serviceProvider);
}
@ -100,6 +103,9 @@ public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObje
ServiceProvider serviceProvider) throws IllegalArgumentException {
TraceObjectValue value =
rowObject.getAttributeEntry(TargetStackFrame.PC_ATTRIBUTE_NAME);
if (value == null) {
return null;
}
return DebuggerStaticMappingUtils.getModuleName(value.castValue(), provider.current);
}
}

View File

@ -110,7 +110,8 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
@Override
public Function getValue(ValueRow rowObject, Settings settings, Trace data,
ServiceProvider serviceProvider) throws IllegalArgumentException {
DebuggerCoordinates coords = provider.current.object(rowObject.currentObject());
TraceObject obj = rowObject.getValue().getChild();
DebuggerCoordinates coords = provider.current.object(obj);
Address pc = computeProgramCounter(coords);
if (pc == null) {
return null;
@ -128,7 +129,8 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
@Override
public String getValue(ValueRow rowObject, Settings settings, Trace data,
ServiceProvider serviceProvider) throws IllegalArgumentException {
DebuggerCoordinates coords = provider.current.object(rowObject.currentObject());
TraceObject obj = rowObject.getValue().getChild();
DebuggerCoordinates coords = provider.current.object(obj);
Address pc = computeProgramCounter(coords);
if (pc == null) {
return null;
@ -144,7 +146,8 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
@Override
public ValueProperty<Address> getProperty(ValueRow row) {
DebuggerCoordinates coords = provider.current.object(row.currentObject());
TraceObject obj = row.getValue().getChild();
DebuggerCoordinates coords = provider.current.object(obj);
return new ValueAddressProperty(row) {
@Override
public Address getValue() {

View File

@ -39,9 +39,9 @@ import ghidra.app.plugin.core.debug.gui.model.QueryPanelTestHelper;
import ghidra.dbg.target.TargetMemoryRegion;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.debug.api.modules.RegionMapProposal.RegionMapEntry;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
@ -151,20 +151,29 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerTes
protected void assertRow(int position, Object object, String name, Address start,
Address end, long length, String flags) {
ValueRow row = provider.panel.getAllItems().get(position);
DynamicTableColumn<ValueRow, ?, Trace> nameCol =
provider.panel.getColumnByNameAndType("Name", ValueRow.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> startCol =
provider.panel.getColumnByNameAndType("Start", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> endCol =
provider.panel.getColumnByNameAndType("End", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> lengthCol =
provider.panel.getColumnByNameAndType("Length", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> readCol =
provider.panel.getColumnByNameAndType("Read", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> writeCol =
provider.panel.getColumnByNameAndType("Write", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> executeCol =
provider.panel.getColumnByNameAndType("Execute", ValueProperty.class).getValue();
var tableModel = QueryPanelTestHelper.getTableModel(provider.panel);
GhidraTable table = QueryPanelTestHelper.getTable(provider.panel);
DynamicTableColumn<ValueRow, ?, Trace> nameCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Name", ValueRow.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> startCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Start", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> endCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "End", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> lengthCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Length", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> readCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Read", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> writeCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Write", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> executeCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Execute", ValueProperty.class)
.column();
assertSame(object, row.getValue().getValue());
assertEquals(name, rowColDisplay(row, nameCol));
@ -349,10 +358,14 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerTes
});
waitForPass(() -> assertFalse(tb.trace.getProgramView().getMemory().isEmpty()));
int startColIdx =
provider.panel.getColumnByNameAndType("Start", ValueProperty.class).getKey();
int endColIdx = provider.panel.getColumnByNameAndType("End", ValueProperty.class).getKey();
var tableModel = QueryPanelTestHelper.getTableModel(provider.panel);
GhidraTable table = QueryPanelTestHelper.getTable(provider.panel);
int startColIdx = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Start", ValueProperty.class)
.viewIndex();
int endColIdx = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "End", ValueProperty.class)
.viewIndex();
clickTableCell(table, 0, startColIdx, 2);
waitForPass(() -> assertEquals(tb.addr(0x00400000), listing.getLocation().getAddress()));

View File

@ -16,14 +16,37 @@
package ghidra.app.plugin.core.debug.gui.model;
import docking.widgets.table.*;
import ghidra.app.plugin.core.debug.gui.thread.DebuggerThreadsPanel;
import ghidra.trace.model.Trace;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.table.column.GColumnRenderer;
public class QueryPanelTestHelper {
public static ObjectTableModel getTableModel(DebuggerThreadsPanel panel) {
public record ColumnAndIndex<T, V>(DynamicTableColumn<T, V, Trace> column, int modelIndex,
int viewIndex) {
}
@SuppressWarnings("unchecked")
public static <T, V> ColumnAndIndex<T, V> getColumnByNameAndType(
AbstractQueryTableModel<T> tableModel, GhidraTable table, String name, Class<V> type) {
int count = tableModel.getColumnCount();
for (int i = 0; i < count; i++) {
DynamicTableColumn<T, ?, ?> column = tableModel.getColumn(i);
if (!name.equals(column.getColumnName())) {
continue;
}
if (column.getColumnClass() != type) {
continue;
}
return new ColumnAndIndex<>((DynamicTableColumn<T, V, Trace>) column,
i, table.convertColumnIndexToView(i));
}
return null;
}
public static <T> AbstractQueryTableModel<T> getTableModel(
AbstractQueryTablePanel<T, ?> panel) {
return panel.tableModel;
}

View File

@ -232,14 +232,20 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
protected void assertModuleRow(int pos, Object object, String name, Address start, Address end,
long length) {
ValueRow row = provider.modulesPanel.getAllItems().get(pos);
DynamicTableColumn<ValueRow, ?, Trace> nameCol =
provider.modulesPanel.getColumnByNameAndType("Name", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> baseCol =
provider.modulesPanel.getColumnByNameAndType("Base", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> maxCol =
provider.modulesPanel.getColumnByNameAndType("Max", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> lengthCol =
provider.modulesPanel.getColumnByNameAndType("Length", ValueProperty.class).getValue();
var tableModel = QueryPanelTestHelper.getTableModel(provider.modulesPanel);
GhidraTable table = QueryPanelTestHelper.getTable(provider.modulesPanel);
DynamicTableColumn<ValueRow, ?, Trace> nameCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Name", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> baseCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Base", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> maxCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Max", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> lengthCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Length", ValueProperty.class)
.column();
assertSame(object, row.getValue().getValue());
assertEquals(name, rowColVal(row, nameCol));
@ -251,17 +257,23 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
protected void assertSectionRow(int pos, Object object, String moduleName, String name,
Address start, Address end, long length) {
ValueRow row = provider.sectionsPanel.getAllItems().get(pos);
DynamicTableColumn<ValueRow, ?, Trace> moduleNameCol =
provider.sectionsPanel.getColumnByNameAndType("Module Name", ValueProperty.class)
.getValue();
DynamicTableColumn<ValueRow, ?, Trace> nameCol =
provider.sectionsPanel.getColumnByNameAndType("Name", String.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> startCol =
provider.sectionsPanel.getColumnByNameAndType("Start", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> endCol =
provider.sectionsPanel.getColumnByNameAndType("End", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> lengthCol =
provider.sectionsPanel.getColumnByNameAndType("Length", ValueProperty.class).getValue();
var tableModel = QueryPanelTestHelper.getTableModel(provider.sectionsPanel);
GhidraTable table = QueryPanelTestHelper.getTable(provider.sectionsPanel);
DynamicTableColumn<ValueRow, ?, Trace> moduleNameCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Module Name", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> nameCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Name", String.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> startCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Start", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> endCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "End", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> lengthCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Length", ValueProperty.class)
.column();
assertSame(object, row.getValue().getValue());
assertEquals(moduleName, rowColVal(row, moduleNameCol));

View File

@ -15,7 +15,8 @@
*/
package ghidra.app.plugin.core.debug.gui.stack;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.List;
@ -52,6 +53,7 @@ import ghidra.trace.model.stack.TraceObjectStack;
import ghidra.trace.model.target.*;
import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.thread.TraceObjectThread;
import ghidra.util.table.GhidraTable;
import ghidra.util.task.TaskMonitor;
/**
@ -215,13 +217,18 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerTest
protected void assertRow(int level, Address pcVal, Function func) {
ValueRow row = stackProvider.panel.getAllItems().get(level);
var tableModel = QueryPanelTestHelper.getTableModel(stackProvider.panel);
GhidraTable table = QueryPanelTestHelper.getTable(stackProvider.panel);
DynamicTableColumn<ValueRow, String, Trace> levelCol =
stackProvider.panel.getColumnByNameAndType("Level", String.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> pcCol =
stackProvider.panel.getColumnByNameAndType("PC", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, Function, Trace> funcCol =
stackProvider.panel.getColumnByNameAndType("Function", Function.class).getValue();
DynamicTableColumn<ValueRow, String, Trace> levelCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Level", String.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> pcCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "PC", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, Function, Trace> funcCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Function", Function.class)
.column();
assertEquals(PathUtils.makeKey(PathUtils.makeIndex(level)), rowColVal(row, levelCol));
assertEquals(pcVal, rowColVal(row, pcCol));
@ -308,8 +315,6 @@ public class DebuggerStackProviderTest extends AbstractGhidraHeadedDebuggerTest
/**
* Because keys are strings, we need to ensure they get sorted numerically
*
* @throws Exception
*/
@Test
public void testTableSortedCorrectly() throws Exception {

View File

@ -29,7 +29,6 @@ import db.Transaction;
import docking.widgets.table.*;
import generic.test.category.NightlyCategory;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel;
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.*;
import ghidra.app.plugin.core.debug.gui.model.QueryPanelTestHelper;
import ghidra.dbg.target.TargetExecutionStateful;
@ -154,25 +153,24 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerTes
assertThreadsTableSize(0);
}
protected void assertThreadRow(int position, Object object, String name, Long created,
Long destroyed, TargetExecutionState state, String comment) {
protected void assertThreadRow(int position, Object object, String name,
TargetExecutionState state, String comment) {
// NB. Not testing plot, since that's unmodified from generic ObjectTable
ValueRow row = provider.panel.getAllItems().get(position);
DynamicTableColumn<ValueRow, ?, Trace> nameCol =
provider.panel.getColumnByNameAndType("Name", ValueRow.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> createdCol =
provider.panel.getColumnByNameAndType("Created", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> destroyedCol =
provider.panel.getColumnByNameAndType("Destroyed", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> stateCol =
provider.panel.getColumnByNameAndType("State", ValueProperty.class).getValue();
DynamicTableColumn<ValueRow, ?, Trace> commentCol =
provider.panel.getColumnByNameAndType("Comment", ValueProperty.class).getValue();
var tableModel = QueryPanelTestHelper.getTableModel(provider.panel);
GhidraTable table = QueryPanelTestHelper.getTable(provider.panel);
DynamicTableColumn<ValueRow, ?, Trace> nameCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Name", ValueRow.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> stateCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "State", ValueProperty.class)
.column();
DynamicTableColumn<ValueRow, ?, Trace> commentCol = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Comment", ValueProperty.class)
.column();
assertSame(object, row.getValue().getValue());
assertEquals(name, rowColDisplay(row, nameCol));
assertEquals(created, rowColVal(row, createdCol));
assertEquals(destroyed, rowColVal(row, destroyedCol));
assertEquals(state.name(), rowColVal(row, stateCol));
assertEquals(comment, rowColVal(row, commentCol));
}
@ -180,9 +178,9 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerTes
protected void assertThreadsPopulated() {
assertThreadsTableSize(2);
assertThreadRow(0, thread1.getObject(), "Processes[1].Threads[1]", 0L, null,
assertThreadRow(0, thread1.getObject(), "Processes[1].Threads[1]",
TargetExecutionState.STOPPED, "A comment");
assertThreadRow(1, thread2.getObject(), "Processes[1].Threads[2]", 0L, 10L,
assertThreadRow(1, thread2.getObject(), "Processes[1].Threads[2]",
TargetExecutionState.STOPPED, "Another comment");
}
@ -399,7 +397,7 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerTes
waitForTasks();
waitForPass(() -> {
assertThreadRow(0, thread1.getObject(), "Processes[1].Threads[1]", 0L, 15L,
assertThreadRow(0, thread1.getObject(), "Processes[1].Threads[1]",
TargetExecutionState.STOPPED, "A comment");
});
// NOTE: Destruction will not be visible in plot unless snapshot 15 is created
@ -429,11 +427,12 @@ public class DebuggerThreadsProviderTest extends AbstractGhidraHeadedDebuggerTes
traceManager.activateTrace(tb.trace);
waitForTasks();
int commentViewIdx =
provider.panel.getColumnByNameAndType("Comment", ValueProperty.class).getKey();
ObjectTableModel tableModel = QueryPanelTestHelper.getTableModel(provider.panel);
var tableModel = QueryPanelTestHelper.getTableModel(provider.panel);
GhidraTable table = QueryPanelTestHelper.getTable(provider.panel);
int commentModelIdx = table.convertColumnIndexToModel(commentViewIdx);
int commentModelIdx = QueryPanelTestHelper
.getColumnByNameAndType(tableModel, table, "Comment", ValueProperty.class)
.modelIndex();
assertNotEquals(-1, commentModelIdx);
runSwing(() -> {
tableModel.setValueAt(new ValueFixedProperty<>("A different comment"), 0,