GP-5193: Delete Legacy UI table panels.

This commit is contained in:
Dan 2025-03-04 18:02:13 +00:00
parent 66ac37b368
commit a1ff800559
20 changed files with 18 additions and 2545 deletions

View file

@ -1,316 +0,0 @@
/* ###
* 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.awt.BorderLayout;
import java.awt.event.*;
import java.util.Collection;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import docking.ActionContext;
import docking.widgets.table.CustomToStringCellRenderer;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.utils.DebouncedRowWrappedEnumeratedColumnTableModel;
import ghidra.app.services.DebuggerListingService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.util.ProgramSelection;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.util.TraceEvents;
import ghidra.util.database.ObjectKey;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
public class DebuggerLegacyRegionsPanel extends JPanel {
protected enum RegionTableColumns
implements EnumeratedTableColumn<RegionTableColumns, RegionRow> {
NAME("Name", String.class, RegionRow::getName, RegionRow::setName),
START("Start", Address.class, RegionRow::getMinAddress),
END("End", Address.class, RegionRow::getMaxAddress),
LENGTH("Length", Long.class, RegionRow::getLength),
READ("Read", Boolean.class, RegionRow::isRead, RegionRow::setRead),
WRITE("Write", Boolean.class, RegionRow::isWrite, RegionRow::setWrite),
EXECUTE("Execute", Boolean.class, RegionRow::isExecute, RegionRow::setExecute),
VOLATILE("Volatile", Boolean.class, RegionRow::isVolatile, RegionRow::setVolatile);
private final String header;
private final Function<RegionRow, ?> getter;
private final BiConsumer<RegionRow, Object> setter;
private final Class<?> cls;
@SuppressWarnings("unchecked")
<T> RegionTableColumns(String header, Class<T> cls, Function<RegionRow, T> getter,
BiConsumer<RegionRow, T> setter) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<RegionRow, Object>) setter;
}
<T> RegionTableColumns(String header, Class<T> cls, Function<RegionRow, T> getter) {
this(header, cls, getter, null);
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public boolean isEditable(RegionRow row) {
return setter != null;
}
@Override
public void setValueOf(RegionRow row, Object value) {
setter.accept(row, value);
}
@Override
public Object getValueOf(RegionRow row) {
return getter.apply(row);
}
}
protected static class RegionTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< //
RegionTableColumns, ObjectKey, RegionRow, TraceMemoryRegion> {
public RegionTableModel(PluginTool tool) {
super(tool, "Regions", RegionTableColumns.class, TraceMemoryRegion::getObjectKey,
RegionRow::new, RegionRow::getRegion);
}
}
protected static Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) {
if (!(context instanceof DebuggerRegionActionContext ctx)) {
return null;
}
return ctx.getSelectedRegions();
}
private class RegionsListener extends TraceDomainObjectListener {
public RegionsListener() {
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
listenFor(TraceEvents.REGION_ADDED, this::regionAdded);
listenFor(TraceEvents.REGION_CHANGED, this::regionChanged);
listenFor(TraceEvents.REGION_LIFESPAN_CHANGED, this::regionChanged);
listenFor(TraceEvents.REGION_DELETED, this::regionDeleted);
}
private void objectRestored() {
loadRegions();
}
private void regionAdded(TraceMemoryRegion region) {
regionTableModel.addItem(region);
}
private void regionChanged(TraceMemoryRegion region) {
regionTableModel.updateItem(region);
}
private void regionDeleted(TraceMemoryRegion region) {
regionTableModel.deleteItem(region);
}
}
protected void activatedSelectAddresses(DebuggerRegionActionContext ctx) {
if (listingService == null) {
return;
}
Set<TraceMemoryRegion> regions = getSelectedRegions(ctx);
if (regions == null) {
return;
}
AddressSet sel = new AddressSet();
for (TraceMemoryRegion s : regions) {
sel.add(s.getRange(0));
}
ProgramSelection ps = new ProgramSelection(sel);
listingService.setCurrentSelection(ps);
}
final DebuggerRegionsProvider provider;
@AutoServiceConsumed
private DebuggerListingService listingService;
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
private Trace currentTrace;
private final RegionsListener regionsListener = new RegionsListener();
protected final RegionTableModel regionTableModel;
protected GhidraTable regionTable;
private GhidraTableFilterPanel<RegionRow> regionFilterPanel;
private DebuggerRegionActionContext myActionContext;
public DebuggerLegacyRegionsPanel(DebuggerRegionsProvider provider) {
super(new BorderLayout());
this.provider = provider;
this.autoServiceWiring = AutoService.wireServicesConsumed(provider.plugin, this);
regionTableModel = new RegionTableModel(provider.getTool());
regionTable = new GhidraTable(regionTableModel);
regionTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
add(new JScrollPane(regionTable));
regionFilterPanel = new GhidraTableFilterPanel<>(regionTable, regionTableModel);
add(regionFilterPanel, BorderLayout.SOUTH);
regionTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;
}
myActionContext = new DebuggerRegionActionContext(provider,
regionFilterPanel.getSelectedItems(), regionTable);
contextChanged();
});
// Note, ProgramTableModel will not work here, since that would navigate the "static" view
regionTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
navigateToSelectedRegion();
}
}
});
regionTable.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
navigateToSelectedRegion();
}
}
});
// TODO: Adjust default column widths?
TableColumnModel columnModel = regionTable.getColumnModel();
TableColumn startCol = columnModel.getColumn(RegionTableColumns.START.ordinal());
startCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn endCol = columnModel.getColumn(RegionTableColumns.END.ordinal());
endCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn lenCol = columnModel.getColumn(RegionTableColumns.LENGTH.ordinal());
lenCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX);
final int small = 100;
TableColumn rCol = columnModel.getColumn(RegionTableColumns.READ.ordinal());
rCol.setPreferredWidth(small);
TableColumn wCol = columnModel.getColumn(RegionTableColumns.WRITE.ordinal());
wCol.setPreferredWidth(small);
TableColumn eCol = columnModel.getColumn(RegionTableColumns.EXECUTE.ordinal());
eCol.setPreferredWidth(small);
TableColumn vCol = columnModel.getColumn(RegionTableColumns.VOLATILE.ordinal());
vCol.setPreferredWidth(small);
}
private void loadRegions() {
regionTableModel.clear();
if (currentTrace == null) {
return;
}
TraceMemoryManager memoryManager = currentTrace.getMemoryManager();
regionTableModel.addAllItems(memoryManager.getAllRegions());
}
public DebuggerRegionActionContext getActionContext() {
return myActionContext;
}
boolean isContextNonEmpty(DebuggerRegionActionContext ctx) {
return !ctx.getSelectedRegions().isEmpty();
}
protected void navigateToSelectedRegion() {
if (listingService != null) {
int selectedRow = regionTable.getSelectedRow();
int selectedColumn = regionTable.getSelectedColumn();
Object value = regionTable.getValueAt(selectedRow, selectedColumn);
if (value instanceof Address address) {
listingService.goTo(address, true);
}
}
}
public void setSelectedRegions(Set<TraceMemoryRegion> sel) {
DebuggerResources.setSelectedRows(sel, regionTableModel::getRow, regionTable,
regionTableModel, regionFilterPanel);
}
public Collection<RegionRow> getSelectedRows() {
return regionFilterPanel.getSelectedItems();
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
setTrace(coordinates.getTrace());
}
public void setTrace(Trace trace) {
if (currentTrace == trace) {
return;
}
removeOldListeners();
currentTrace = trace;
addNewListeners();
loadRegions();
}
public void contextChanged() {
provider.contextChanged();
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
}
currentTrace.removeListener(regionsListener);
}
private void addNewListeners() {
if (currentTrace == null) {
return;
}
currentTrace.addListener(regionsListener);
}
}

View file

@ -16,28 +16,16 @@
package ghidra.app.plugin.core.debug.gui.memory; package ghidra.app.plugin.core.debug.gui.memory;
import java.awt.Component; import java.awt.Component;
import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.DefaultActionContext; import docking.DefaultActionContext;
import docking.widgets.table.GTable;
import ghidra.trace.model.memory.TraceMemoryRegion; import ghidra.trace.model.memory.TraceMemoryRegion;
public class DebuggerRegionActionContext extends DefaultActionContext { public class DebuggerRegionActionContext extends DefaultActionContext {
private final Set<TraceMemoryRegion> selectedRegions; private final Set<TraceMemoryRegion> selectedRegions;
private final boolean forcedSingle; private final boolean forcedSingle;
private static Set<TraceMemoryRegion> toRegions(Collection<RegionRow> rows) {
return rows.stream().map(RegionRow::getRegion).collect(Collectors.toUnmodifiableSet());
}
public DebuggerRegionActionContext(DebuggerRegionsProvider provider,
Collection<RegionRow> rows, GTable table) {
this(provider, toRegions(rows), table, false);
}
public DebuggerRegionActionContext(ComponentProvider provider, public DebuggerRegionActionContext(ComponentProvider provider,
Set<TraceMemoryRegion> selected, Component sourceComponent, boolean forcedSingle) { Set<TraceMemoryRegion> selected, Component sourceComponent, boolean forcedSingle) {
super(provider, selected, sourceComponent); super(provider, selected, sourceComponent);

View file

@ -22,8 +22,6 @@ import java.util.Map.Entry;
import javax.swing.*; import javax.swing.*;
import org.apache.commons.lang3.ArrayUtils;
import db.Transaction; import db.Transaction;
import docking.ActionContext; import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
@ -226,7 +224,6 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
private final JPanel mainPanel = new JPanel(new BorderLayout()); private final JPanel mainPanel = new JPanel(new BorderLayout());
DebuggerRegionsPanel panel; DebuggerRegionsPanel panel;
DebuggerLegacyRegionsPanel legacyPanel;
// TODO: Lazy construction of these dialogs? // TODO: Lazy construction of these dialogs?
private final DebuggerBlockChooserDialog blockChooserDialog; private final DebuggerBlockChooserDialog blockChooserDialog;
@ -273,7 +270,6 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
protected void buildMainPanel() { protected void buildMainPanel() {
panel = new DebuggerRegionsPanel(this); panel = new DebuggerRegionsPanel(this);
mainPanel.add(panel); mainPanel.add(panel);
legacyPanel = new DebuggerLegacyRegionsPanel(this);
} }
protected void createActions() { protected void createActions() {
@ -317,13 +313,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
final ActionContext context; final ActionContext context = panel.getActionContext();
if (Trace.isLegacy(current.getTrace())) {
context = legacyPanel.getActionContext();
}
else {
context = panel.getActionContext();
}
if (context != null) { if (context != null) {
return context; return context;
} }
@ -331,10 +321,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
} }
private boolean isContextNonEmpty(ActionContext context) { private boolean isContextNonEmpty(ActionContext context) {
if (context instanceof DebuggerRegionActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return legacyPanel.isContextNonEmpty(ctx);
}
else if (context instanceof DebuggerObjectActionContext ctx) {
return panel.isContextNonEmpty(ctx); return panel.isContextNonEmpty(ctx);
} }
return false; return false;
@ -353,10 +340,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
} }
private Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) { private Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) {
if (context instanceof DebuggerRegionActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerLegacyRegionsPanel.getSelectedRegions(ctx);
}
else if (context instanceof DebuggerObjectActionContext ctx) {
return panel.getSelectedRegions(ctx); return panel.getSelectedRegions(ctx);
} }
return null; return null;
@ -495,13 +479,8 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
} }
public void setSelectedRegions(Set<TraceMemoryRegion> sel) { public void setSelectedRegions(Set<TraceMemoryRegion> sel) {
if (Trace.isLegacy(current.getTrace())) {
legacyPanel.setSelectedRegions(sel);
}
else {
panel.setSelectedRegions(sel); panel.setSelectedRegions(sel);
} }
}
@Override @Override
public JComponent getComponent() { public JComponent getComponent() {
@ -561,25 +540,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
} }
current = coordinates; current = coordinates;
if (Trace.isLegacy(coordinates.getTrace())) {
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
legacyPanel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyPanel) == -1) {
mainPanel.remove(panel);
mainPanel.add(legacyPanel);
mainPanel.validate();
}
}
else {
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
panel.coordinatesActivated(coordinates); panel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
mainPanel.remove(legacyPanel);
mainPanel.add(panel);
mainPanel.validate();
}
}
contextChanged(); contextChanged();
} }

View file

@ -1,103 +0,0 @@
/* ###
* 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 db.Transaction;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.memory.TraceMemoryRegion;
public class RegionRow {
private final TraceMemoryRegion region;
public RegionRow(TraceMemoryRegion region) {
this.region = region;
}
public TraceMemoryRegion getRegion() {
return region;
}
public void setName(String name) {
try (Transaction tx = region.getTrace().openTransaction("Rename region")) {
region.setName(0, name);
}
}
public String getName() {
return region.getName(0);
}
public AddressRange getRange() {
return region.getRange(0);
}
public Address getMaxAddress() {
return region.getMaxAddress(0);
}
public Address getMinAddress() {
return region.getMinAddress(0);
}
public long getLength() {
return region.getLength(0);
}
public void setRead(boolean read) {
try (Transaction tx =
region.getTrace().openTransaction("Toggle region read flag")) {
region.setRead(0, read);
}
}
public boolean isRead() {
return region.isRead(0);
}
public void setWrite(boolean write) {
try (Transaction tx =
region.getTrace().openTransaction("Toggle region write flag")) {
region.setWrite(0, write);
}
}
public boolean isWrite() {
return region.isWrite(0);
}
public void setExecute(boolean execute) {
try (Transaction tx =
region.getTrace().openTransaction("Toggle region execute flag")) {
region.setExecute(0, execute);
}
}
public boolean isExecute() {
return region.isExecute(0);
}
public void setVolatile(boolean vol) {
try (Transaction tx =
region.getTrace().openTransaction("Toggle region volatile flag")) {
region.setVolatile(0, vol);
}
}
public boolean isVolatile() {
return region.isVolatile(0);
}
}

View file

@ -1,288 +0,0 @@
/* ###
* 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.modules;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import docking.widgets.table.CustomToStringCellRenderer;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.utils.DebouncedRowWrappedEnumeratedColumnTableModel;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.trace.model.*;
import ghidra.trace.model.modules.*;
import ghidra.trace.util.TraceEvents;
import ghidra.util.database.ObjectKey;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
public class DebuggerLegacyModulesPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerModuleActionContext context) {
return context.getSelectedModules();
}
protected static Set<TraceSection> getSelectedSectionsFromContext(
DebuggerModuleActionContext context) {
return context.getSelectedModules()
.stream()
// snap does not matter for legacy module
.flatMap(m -> m.getSections(0).stream())
.collect(Collectors.toSet());
}
protected static AddressSetView getSelectedAddressesFromContext(
DebuggerModuleActionContext context) {
AddressSet sel = new AddressSet();
for (TraceModule module : getSelectedModulesFromContext(context)) {
// snap does not matter for legacy module
sel.add(module.getRange(0));
}
return sel;
}
protected enum ModuleTableColumns
implements EnumeratedTableColumn<ModuleTableColumns, ModuleRow> {
BASE("Base Address", Address.class, ModuleRow::getBase),
MAX("Max Address", Address.class, ModuleRow::getMaxAddress),
SHORT_NAME("Name", String.class, ModuleRow::getShortName),
NAME("Module Name", String.class, ModuleRow::getName, ModuleRow::setName),
MAPPING("Mapping", String.class, ModuleRow::getMapping),
LENGTH("Length", Long.class, ModuleRow::getLength);
private final String header;
private final Function<ModuleRow, ?> getter;
private final BiConsumer<ModuleRow, Object> setter;
private final Class<?> cls;
@SuppressWarnings("unchecked")
<T> ModuleTableColumns(String header, Class<T> cls, Function<ModuleRow, T> getter,
BiConsumer<ModuleRow, T> setter) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<ModuleRow, Object>) setter;
}
<T> ModuleTableColumns(String header, Class<T> cls, Function<ModuleRow, T> getter) {
this(header, cls, getter, null);
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public boolean isEditable(ModuleRow row) {
return setter != null;
}
@Override
public void setValueOf(ModuleRow row, Object value) {
setter.accept(row, value);
}
@Override
public Object getValueOf(ModuleRow row) {
return getter.apply(row);
}
}
protected static class ModuleTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< //
ModuleTableColumns, ObjectKey, ModuleRow, TraceModule> {
public ModuleTableModel(PluginTool tool, DebuggerModulesProvider provider) {
super(tool, "Modules", ModuleTableColumns.class, TraceModule::getObjectKey,
mod -> new ModuleRow(provider, mod), ModuleRow::getModule);
}
@Override
public List<ModuleTableColumns> defaultSortOrder() {
return List.of(ModuleTableColumns.BASE);
}
}
private class ModulesListener extends TraceDomainObjectListener {
public ModulesListener() {
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
listenFor(TraceEvents.MODULE_ADDED, this::moduleAdded);
listenFor(TraceEvents.MODULE_CHANGED, this::moduleChanged);
listenFor(TraceEvents.MODULE_LIFESPAN_CHANGED, this::moduleChanged);
listenFor(TraceEvents.MODULE_DELETED, this::moduleDeleted);
}
private void objectRestored() {
loadModules();
}
private void moduleAdded(TraceModule module) {
moduleTableModel.addItem(module);
}
private void moduleChanged(TraceModule module) {
moduleTableModel.updateItem(module);
}
private void moduleDeleted(TraceModule module) {
moduleTableModel.deleteItem(module);
}
}
private final DebuggerModulesProvider provider;
Trace currentTrace;
private final ModulesListener modulesListener = new ModulesListener();
protected final ModuleTableModel moduleTableModel;
protected final GhidraTable moduleTable;
final GhidraTableFilterPanel<ModuleRow> moduleFilterPanel;
private DebuggerModuleActionContext myActionContext;
public DebuggerLegacyModulesPanel(DebuggerModulesProvider provider) {
super(new BorderLayout());
this.provider = provider;
moduleTableModel = new ModuleTableModel(provider.getTool(), provider);
moduleTable = new GhidraTable(moduleTableModel);
moduleTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
add(new JScrollPane(moduleTable));
moduleFilterPanel = new GhidraTableFilterPanel<>(moduleTable, moduleTableModel);
add(moduleFilterPanel, BorderLayout.SOUTH);
moduleTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;
}
myActionContext = new DebuggerModuleActionContext(provider,
moduleFilterPanel.getSelectedItems(), moduleTable);
provider.legacyModulesPanelContextChanged();
});
moduleTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
navigateToSelectedModule();
}
}
});
moduleTable.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
navigateToSelectedModule();
e.consume();
}
}
});
// TODO: Adjust default column widths?
TableColumnModel colModel = moduleTable.getColumnModel();
TableColumn baseCol = colModel.getColumn(ModuleTableColumns.BASE.ordinal());
baseCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn maxCol = colModel.getColumn(ModuleTableColumns.MAX.ordinal());
maxCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn lenCol = colModel.getColumn(ModuleTableColumns.LENGTH.ordinal());
lenCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX);
}
protected void contextChanged() {
provider.contextChanged();
}
protected void navigateToSelectedModule() {
if (provider.listingService != null) {
int selectedRow = moduleTable.getSelectedRow();
int selectedColumn = moduleTable.getSelectedColumn();
Object value = moduleTable.getValueAt(selectedRow, selectedColumn);
if (value instanceof Address address) {
provider.listingService.goTo(address, true);
}
}
}
public DebuggerModuleActionContext getActionContext() {
return myActionContext;
}
private void loadModules() {
moduleTable.getSelectionModel().clearSelection();
moduleTableModel.clear();
if (currentTrace == null) {
return;
}
TraceModuleManager moduleManager = currentTrace.getModuleManager();
moduleTableModel.addAllItems(moduleManager.getAllModules());
}
private void setTrace(Trace trace) {
if (currentTrace == trace) {
return;
}
removeOldListeners();
currentTrace = trace;
addNewListeners();
loadModules();
contextChanged();
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
setTrace(coordinates.getTrace());
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
}
currentTrace.removeListener(modulesListener);
}
private void addNewListeners() {
if (currentTrace == null) {
return;
}
currentTrace.addListener(modulesListener);
}
public void setSelectedModules(Set<TraceModule> sel) {
DebuggerResources.setSelectedRows(sel, moduleTableModel::getRow, moduleTable,
moduleTableModel, moduleFilterPanel);
}
}

View file

@ -1,335 +0,0 @@
/* ###
* 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.modules;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import docking.widgets.table.CustomToStringCellRenderer;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import docking.widgets.table.TableFilter;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
import ghidra.app.plugin.core.debug.utils.DebouncedRowWrappedEnumeratedColumnTableModel;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.modules.*;
import ghidra.trace.util.TraceEvents;
import ghidra.util.database.ObjectKey;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
public class DebuggerLegacySectionsPanel extends JPanel {
protected static Set<TraceModule> getSelectedModulesFromContext(
DebuggerSectionActionContext context) {
return context.getSelectedSections(false, 0)
.stream()
.map(r -> r.getModule())
.collect(Collectors.toSet());
}
protected static Set<TraceSection> getSelectedSectionsFromContext(
DebuggerSectionActionContext context, boolean allowExpansion) {
return context.getSelectedSections(allowExpansion, 0);
}
protected static AddressSetView getSelectedAddressesFromContext(
DebuggerSectionActionContext context) {
AddressSet sel = new AddressSet();
for (TraceSection section : getSelectedSectionsFromContext(context, false)) {
sel.add(section.getRange(0));
}
return sel;
}
protected enum SectionTableColumns
implements EnumeratedTableColumn<SectionTableColumns, SectionRow> {
START("Start Address", Address.class, SectionRow::getStart),
END("End Address", Address.class, SectionRow::getEnd),
NAME("Section Name", String.class, SectionRow::getName, SectionRow::setName),
MODULE("Module Name", String.class, SectionRow::getModuleName),
LENGTH("Length", Long.class, SectionRow::getLength);
private final String header;
private final Function<SectionRow, ?> getter;
private final BiConsumer<SectionRow, Object> setter;
private final Class<?> cls;
@SuppressWarnings("unchecked")
<T> SectionTableColumns(String header, Class<T> cls, Function<SectionRow, T> getter,
BiConsumer<SectionRow, T> setter) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<SectionRow, Object>) setter;
}
<T> SectionTableColumns(String header, Class<T> cls, Function<SectionRow, T> getter) {
this(header, cls, getter, null);
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public boolean isEditable(SectionRow row) {
return setter != null;
}
@Override
public void setValueOf(SectionRow row, Object value) {
setter.accept(row, value);
}
@Override
public Object getValueOf(SectionRow row) {
return getter.apply(row);
}
}
protected static class SectionTableModel extends DebouncedRowWrappedEnumeratedColumnTableModel< //
SectionTableColumns, ObjectKey, SectionRow, TraceSection> {
public SectionTableModel(PluginTool tool) {
super(tool, "Sections", SectionTableColumns.class, TraceSection::getObjectKey,
SectionRow::new, SectionRow::getSection);
}
@Override
public List<SectionTableColumns> defaultSortOrder() {
return List.of(SectionTableColumns.START);
}
}
private class SectionsListener extends TraceDomainObjectListener {
public SectionsListener() {
listenForUntyped(DomainObjectEvent.RESTORED, e -> objectRestored());
/**
* NOTE: No need for Module.ADDED here. A TraceModule is created empty, so when each
* section is added, we'll get the call.
*/
listenFor(TraceEvents.MODULE_CHANGED, this::moduleChanged);
listenFor(TraceEvents.MODULE_LIFESPAN_CHANGED, this::moduleChanged);
listenFor(TraceEvents.MODULE_DELETED, this::moduleDeleted);
listenFor(TraceEvents.SECTION_ADDED, this::sectionAdded);
listenFor(TraceEvents.SECTION_CHANGED, this::sectionChanged);
listenFor(TraceEvents.SECTION_DELETED, this::sectionDeleted);
}
private void objectRestored() {
loadSections();
}
private void moduleChanged(TraceModule module) {
sectionTableModel.fireTableDataChanged(); // Because module name in section row
}
private void moduleDeleted(TraceModule module) {
// NOTE: module.getSections() will be empty, now
sectionTableModel.deleteAllItems(sectionTableModel.getMap()
.values()
.stream()
.filter(r -> r.getModule() == module)
.map(r -> r.getSection())
.collect(Collectors.toList()));
}
private void sectionAdded(TraceSection section) {
sectionTableModel.addItem(section);
}
private void sectionChanged(TraceSection section) {
sectionTableModel.updateItem(section);
}
private void sectionDeleted(TraceSection section) {
sectionTableModel.deleteItem(section);
}
}
class SectionsBySelectedModulesTableFilter implements TableFilter<SectionRow> {
@Override
public boolean acceptsRow(SectionRow sectionRow) {
List<ModuleRow> selModuleRows =
provider.legacyModulesPanel.moduleFilterPanel.getSelectedItems();
if (selModuleRows == null || selModuleRows.isEmpty()) {
return true;
}
for (ModuleRow moduleRow : selModuleRows) {
if (moduleRow.getModule() == sectionRow.getModule()) {
return true;
}
}
return false;
}
@Override
public boolean isSubFilterOf(TableFilter<?> tableFilter) {
return false;
}
}
private final DebuggerModulesProvider provider;
private Trace currentTrace;
private final SectionsListener sectionsListener = new SectionsListener();
protected final SectionTableModel sectionTableModel;
protected final GhidraTable sectionTable;
protected final GhidraTableFilterPanel<SectionRow> sectionFilterPanel;
private final SectionsBySelectedModulesTableFilter filterSectionsBySelectedModules =
new SectionsBySelectedModulesTableFilter();
private DebuggerSectionActionContext myActionContext;
public DebuggerLegacySectionsPanel(DebuggerModulesProvider provider) {
super(new BorderLayout());
this.provider = provider;
sectionTableModel = new SectionTableModel(provider.getTool());
sectionTable = new GhidraTable(sectionTableModel);
sectionTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
add(new JScrollPane(sectionTable));
sectionFilterPanel = new GhidraTableFilterPanel<>(sectionTable, sectionTableModel);
add(sectionFilterPanel, BorderLayout.SOUTH);
sectionTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;
}
myActionContext = new DebuggerSectionActionContext(provider,
sectionFilterPanel.getSelectedItems(), sectionTable);
provider.legacySectionsPanelContextChanged();
});
// Note, ProgramTableModel will not work here, since that would navigate the "static" view
sectionTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
navigateToSelectedSection();
}
}
});
sectionTable.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
navigateToSelectedSection();
e.consume();
}
}
});
TableColumnModel colModel = sectionTable.getColumnModel();
TableColumn startCol = colModel.getColumn(SectionTableColumns.START.ordinal());
startCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn endCol = colModel.getColumn(SectionTableColumns.END.ordinal());
endCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
TableColumn lenCol = colModel.getColumn(SectionTableColumns.LENGTH.ordinal());
lenCol.setCellRenderer(CustomToStringCellRenderer.MONO_ULONG_HEX);
}
protected void contextChanged() {
provider.contextChanged();
}
protected void navigateToSelectedSection() {
if (provider.listingService != null) {
int selectedRow = sectionTable.getSelectedRow();
int selectedColumn = sectionTable.getSelectedColumn();
Object value = sectionTable.getValueAt(selectedRow, selectedColumn);
if (value instanceof Address address) {
provider.listingService.goTo(address, true);
}
}
}
public DebuggerSectionActionContext getActionContext() {
return myActionContext;
}
void loadSections() {
sectionTable.getSelectionModel().clearSelection();
sectionTableModel.clear();
if (currentTrace == null) {
return;
}
TraceModuleManager moduleManager = currentTrace.getModuleManager();
sectionTableModel.addAllItems(moduleManager.getAllSections());
}
public void setTrace(Trace trace) {
if (currentTrace == trace) {
return;
}
removeOldListeners();
currentTrace = trace;
addNewListeners();
loadSections();
contextChanged();
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
setTrace(coordinates.getTrace());
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
}
currentTrace.removeListener(sectionsListener);
}
private void addNewListeners() {
if (currentTrace == null) {
return;
}
currentTrace.addListener(sectionsListener);
}
public void setSelectedSections(Set<TraceSection> sel) {
DebuggerResources.setSelectedRows(sel, sectionTableModel::getRow, sectionTable,
sectionTableModel, sectionFilterPanel);
}
public void setFilteredBySelectedModules(boolean filtered) {
sectionFilterPanel.setSecondaryFilter(filtered ? filterSectionsBySelectedModules : null);
}
}

View file

@ -16,28 +16,16 @@
package ghidra.app.plugin.core.debug.gui.modules; package ghidra.app.plugin.core.debug.gui.modules;
import java.awt.Component; import java.awt.Component;
import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.DefaultActionContext; import docking.DefaultActionContext;
import docking.widgets.table.GTable;
import ghidra.trace.model.modules.TraceModule; import ghidra.trace.model.modules.TraceModule;
public class DebuggerModuleActionContext extends DefaultActionContext { public class DebuggerModuleActionContext extends DefaultActionContext {
private final Set<TraceModule> selectedModules; private final Set<TraceModule> selectedModules;
private final boolean forcedSingle; private final boolean forcedSingle;
private static Set<TraceModule> toModules(Collection<ModuleRow> rows) {
return rows.stream().map(ModuleRow::getModule).collect(Collectors.toUnmodifiableSet());
}
public DebuggerModuleActionContext(DebuggerModulesProvider provider,
Collection<ModuleRow> rows, GTable table) {
this(provider, toModules(rows), table, false);
}
public DebuggerModuleActionContext(ComponentProvider provider, Set<TraceModule> selected, public DebuggerModuleActionContext(ComponentProvider provider, Set<TraceModule> selected,
Component sourceComponent, boolean forcedSingle) { Component sourceComponent, boolean forcedSingle) {
super(provider, selected, sourceComponent); super(provider, selected, sourceComponent);

View file

@ -26,8 +26,6 @@ import java.util.stream.Collectors;
import javax.swing.*; import javax.swing.*;
import org.apache.commons.lang3.ArrayUtils;
import docking.*; import docking.*;
import docking.action.*; import docking.action.*;
import docking.action.builder.*; import docking.action.builder.*;
@ -426,12 +424,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
} }
protected static Set<TraceModule> getSelectedModules(ActionContext context) { protected static Set<TraceModule> getSelectedModules(ActionContext context) {
if (context instanceof DebuggerModuleActionContext ctx) {
return DebuggerLegacyModulesPanel.getSelectedModulesFromContext(ctx);
}
if (context instanceof DebuggerSectionActionContext ctx) {
return DebuggerLegacySectionsPanel.getSelectedModulesFromContext(ctx);
}
if (context instanceof DebuggerObjectActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerModulesPanel.getSelectedModulesFromContext(ctx); return DebuggerModulesPanel.getSelectedModulesFromContext(ctx);
} }
@ -440,12 +432,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
protected static Set<TraceSection> getSelectedSections(ActionContext context, protected static Set<TraceSection> getSelectedSections(ActionContext context,
boolean allowExpansion) { boolean allowExpansion) {
if (context instanceof DebuggerModuleActionContext ctx) {
return DebuggerLegacyModulesPanel.getSelectedSectionsFromContext(ctx);
}
if (context instanceof DebuggerSectionActionContext ctx) {
return DebuggerLegacySectionsPanel.getSelectedSectionsFromContext(ctx, allowExpansion);
}
if (context instanceof DebuggerObjectActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerModulesPanel.getSelectedSectionsFromContext(ctx); return DebuggerModulesPanel.getSelectedSectionsFromContext(ctx);
} }
@ -453,12 +439,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
} }
protected static AddressSetView getSelectedAddresses(ActionContext context) { protected static AddressSetView getSelectedAddresses(ActionContext context) {
if (context instanceof DebuggerModuleActionContext ctx) {
return DebuggerLegacyModulesPanel.getSelectedAddressesFromContext(ctx);
}
if (context instanceof DebuggerSectionActionContext ctx) {
return DebuggerLegacySectionsPanel.getSelectedAddressesFromContext(ctx);
}
if (context instanceof DebuggerObjectActionContext ctx) { if (context instanceof DebuggerObjectActionContext ctx) {
return DebuggerModulesPanel.getSelectedAddressesFromContext(ctx); return DebuggerModulesPanel.getSelectedAddressesFromContext(ctx);
} }
@ -565,9 +545,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
private final int defaultDividerSize = mainPanel.getDividerSize(); private final int defaultDividerSize = mainPanel.getDividerSize();
DebuggerModulesPanel modulesPanel; DebuggerModulesPanel modulesPanel;
DebuggerLegacyModulesPanel legacyModulesPanel;
DebuggerSectionsPanel sectionsPanel; DebuggerSectionsPanel sectionsPanel;
DebuggerLegacySectionsPanel legacySectionsPanel;
// TODO: Lazy construction of these dialogs? // TODO: Lazy construction of these dialogs?
private final DebuggerBlockChooserDialog blockChooserDialog; private final DebuggerBlockChooserDialog blockChooserDialog;
@ -713,34 +691,19 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
contextChanged(); contextChanged();
} }
void legacyModulesPanelContextChanged() {
myActionContext = legacyModulesPanel.getActionContext();
if (isFilterSectionsByModules()) {
legacySectionsPanel.loadSections();
}
contextChanged();
}
void sectionsPanelContextChanged() { void sectionsPanelContextChanged() {
myActionContext = sectionsPanel.getActionContext(); myActionContext = sectionsPanel.getActionContext();
contextChanged(); contextChanged();
} }
void legacySectionsPanelContextChanged() {
myActionContext = legacySectionsPanel.getActionContext();
contextChanged();
}
protected void buildMainPanel() { protected void buildMainPanel() {
mainPanel.setContinuousLayout(true); mainPanel.setContinuousLayout(true);
modulesPanel = new DebuggerModulesPanel(this); modulesPanel = new DebuggerModulesPanel(this);
mainPanel.setLeftComponent(modulesPanel); mainPanel.setLeftComponent(modulesPanel);
legacyModulesPanel = new DebuggerLegacyModulesPanel(this);
sectionsPanel = new DebuggerSectionsPanel(this); sectionsPanel = new DebuggerSectionsPanel(this);
mainPanel.setRightComponent(sectionsPanel); mainPanel.setRightComponent(sectionsPanel);
legacySectionsPanel = new DebuggerLegacySectionsPanel(this);
mainPanel.setResizeWeight(0.5); mainPanel.setResizeWeight(0.5);
} }
@ -1071,7 +1034,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
actionShowSectionsTable.setSelected(showSectionsTable); actionShowSectionsTable.setSelected(showSectionsTable);
mainPanel.setDividerSize(showSectionsTable ? defaultDividerSize : 0); mainPanel.setDividerSize(showSectionsTable ? defaultDividerSize : 0);
sectionsPanel.setVisible(showSectionsTable); sectionsPanel.setVisible(showSectionsTable);
legacySectionsPanel.setVisible(showSectionsTable);
mainPanel.resetToPreferredSizes(); mainPanel.resetToPreferredSizes();
} }
@ -1090,7 +1052,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
this.filterSectionsByModules = filterSectionsByModules; this.filterSectionsByModules = filterSectionsByModules;
actionFilterSectionsByModules.setSelected(filterSectionsByModules); actionFilterSectionsByModules.setSelected(filterSectionsByModules);
sectionsPanel.setFilteredBySelectedModules(filterSectionsByModules); sectionsPanel.setFilteredBySelectedModules(filterSectionsByModules);
legacySectionsPanel.setFilteredBySelectedModules(filterSectionsByModules);
} }
private void activatedSelectCurrent(ActionContext ignored) { private void activatedSelectCurrent(ActionContext ignored) {
@ -1387,53 +1348,19 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
setAutoMapSpec(amState.spec); setAutoMapSpec(amState.spec);
} }
if (Trace.isLegacy(newTrace)) {
modulesPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
sectionsPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
legacyModulesPanel.coordinatesActivated(coordinates);
legacySectionsPanel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyModulesPanel) == -1) {
mainPanel.remove(modulesPanel);
mainPanel.remove(sectionsPanel);
mainPanel.setLeftComponent(legacyModulesPanel);
mainPanel.setRightComponent(legacySectionsPanel);
mainPanel.validate();
}
}
else {
legacyModulesPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
legacySectionsPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
modulesPanel.coordinatesActivated(coordinates); modulesPanel.coordinatesActivated(coordinates);
sectionsPanel.coordinatesActivated(coordinates); sectionsPanel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), modulesPanel) == -1) {
mainPanel.remove(legacyModulesPanel);
mainPanel.remove(legacySectionsPanel);
mainPanel.setLeftComponent(modulesPanel);
mainPanel.setRightComponent(sectionsPanel);
mainPanel.validate();
}
}
contextChanged(); contextChanged();
} }
public void setSelectedModules(Set<TraceModule> sel) { public void setSelectedModules(Set<TraceModule> sel) {
if (Trace.isLegacy(current.getTrace())) {
legacyModulesPanel.setSelectedModules(sel);
}
else {
modulesPanel.setSelectedModules(sel); modulesPanel.setSelectedModules(sel);
} }
}
public void setSelectedSections(Set<TraceSection> sel) { public void setSelectedSections(Set<TraceSection> sel) {
if (Trace.isLegacy(current.getTrace())) {
legacySectionsPanel.setSelectedSections(sel);
}
else {
sectionsPanel.setSelectedSections(sel); sectionsPanel.setSelectedSections(sel);
} }
}
private DataTreeDialog getProgramChooserDialog() { private DataTreeDialog getProgramChooserDialog() {

View file

@ -16,28 +16,17 @@
package ghidra.app.plugin.core.debug.gui.modules; package ghidra.app.plugin.core.debug.gui.modules;
import java.awt.Component; import java.awt.Component;
import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import docking.ComponentProvider; import docking.ComponentProvider;
import docking.DefaultActionContext; import docking.DefaultActionContext;
import docking.widgets.table.GTable;
import ghidra.trace.model.modules.TraceSection; import ghidra.trace.model.modules.TraceSection;
public class DebuggerSectionActionContext extends DefaultActionContext { public class DebuggerSectionActionContext extends DefaultActionContext {
private final Set<TraceSection> selectedSections; private final Set<TraceSection> selectedSections;
private final boolean forcedSingle; private final boolean forcedSingle;
private static Set<TraceSection> toSections(Collection<SectionRow> rows) {
return rows.stream().map(SectionRow::getSection).collect(Collectors.toUnmodifiableSet());
}
public DebuggerSectionActionContext(DebuggerModulesProvider provider,
Collection<SectionRow> rows, GTable table) {
this(provider, toSections(rows), table, false);
}
public DebuggerSectionActionContext(ComponentProvider provider, public DebuggerSectionActionContext(ComponentProvider provider,
Set<TraceSection> selected, Component sourceComponent, boolean forcedSingle) { Set<TraceSection> selected, Component sourceComponent, boolean forcedSingle) {
super(provider, selected, sourceComponent); super(provider, selected, sourceComponent);

View file

@ -1,70 +0,0 @@
/* ###
* 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.modules;
import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.program.model.address.Address;
import ghidra.trace.model.modules.TraceModule;
public class ModuleRow {
private final DebuggerModulesProvider provider;
private final TraceModule module;
public ModuleRow(DebuggerModulesProvider provider, TraceModule module) {
this.provider = provider;
this.module = module;
}
public TraceModule getModule() {
return module;
}
public void setName(String name) {
try (Transaction tx = module.getTrace().openTransaction("Renamed module")) {
module.setName(0, name);
}
}
public String getShortName() {
return DebuggerStaticMappingUtils.computeModuleShortName(module.getName(0));
}
public String getName() {
return module.getName(0);
}
public String getMapping() {
// TODO: Cache this? Would flush on:
// 1. Mapping changes
// 2. Range/Life changes to this module
// 3. Snapshot navigation
return DebuggerStaticMappingUtils.computeMappedFiles(module.getTrace(),
provider.current.getSnap(), module.getRange(0));
}
public Address getBase() {
return module.getBase(0);
}
public Address getMaxAddress() {
return module.getMaxAddress(0);
}
public long getLength() {
return module.getLength(0);
}
}

View file

@ -1,74 +0,0 @@
/* ###
* 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.modules;
import db.Transaction;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.modules.TraceSection;
import ghidra.util.Msg;
import ghidra.util.exception.DuplicateNameException;
public class SectionRow {
private final TraceSection section;
public SectionRow(TraceSection section) {
this.section = section;
}
public TraceModule getModule() {
return section.getModule();
}
public TraceSection getSection() {
return section;
}
public void setName(String name) {
try (Transaction tx = section.getTrace().openTransaction("Rename section")) {
section.setName(0, name);
}
catch (DuplicateNameException e) {
Msg.showError(this, null, "Rename Section",
"Section name is already taken by another in the same module");
}
}
public String getName() {
return section.getName(0);
}
public String getModuleName() {
return section.getModule().getName(0);
}
public AddressRange getRange() {
return section.getRange(0);
}
public Address getStart() {
return section.getStart(0);
}
public Address getEnd() {
return section.getEnd(0);
}
public long getLength() {
return section.getRange(0).getLength();
}
}

View file

@ -1,454 +0,0 @@
/* ###
* 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.stack;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.*;
import java.util.*;
import java.util.function.*;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.*;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.services.*;
import ghidra.debug.api.modules.DebuggerStaticMappingChangeListener;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.*;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.*;
import ghidra.util.Swing;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.table.column.AbstractGColumnRenderer;
public class DebuggerLegacyStackPanel extends JPanel {
protected enum StackTableColumns
implements EnumeratedTableColumn<StackTableColumns, StackFrameRow> {
LEVEL("Level", Integer.class, StackFrameRow::getFrameLevel),
PC("PC", Address.class, StackFrameRow::getProgramCounter),
FUNCTION("Function", ghidra.program.model.listing.Function.class, StackFrameRow::getFunction),
MODULE("Module", String.class, StackFrameRow::getModule),
COMMENT("Comment", String.class, StackFrameRow::getComment, StackFrameRow::setComment, StackFrameRow::isCommentable);
private final String header;
private final Function<StackFrameRow, ?> getter;
private final BiConsumer<StackFrameRow, Object> setter;
private final Predicate<StackFrameRow> editable;
private final Class<?> cls;
@SuppressWarnings("unchecked")
<T> StackTableColumns(String header, Class<T> cls, Function<StackFrameRow, T> getter,
BiConsumer<StackFrameRow, T> setter, Predicate<StackFrameRow> editable) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<StackFrameRow, Object>) setter;
this.editable = editable;
}
<T> StackTableColumns(String header, Class<T> cls, Function<StackFrameRow, T> getter) {
this(header, cls, getter, null, null);
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public Object getValueOf(StackFrameRow row) {
return getter.apply(row);
}
@Override
public void setValueOf(StackFrameRow row, Object value) {
setter.accept(row, value);
}
@Override
public String getHeader() {
return header;
}
@Override
public boolean isEditable(StackFrameRow row) {
return setter != null && editable.test(row);
}
}
protected static class StackTableModel
extends DefaultEnumeratedColumnTableModel<StackTableColumns, StackFrameRow> {
public StackTableModel(PluginTool tool) {
super(tool, "Stack", StackTableColumns.class);
}
@Override
public List<StackTableColumns> defaultSortOrder() {
return List.of(StackTableColumns.LEVEL);
}
}
class ForStackListener extends TraceDomainObjectListener {
public ForStackListener() {
listenFor(TraceEvents.STACK_ADDED, this::stackAdded);
listenFor(TraceEvents.STACK_CHANGED, this::stackChanged);
listenFor(TraceEvents.STACK_DELETED, this::stackDeleted);
listenFor(TraceEvents.BYTES_CHANGED, this::bytesChanged);
}
private void stackAdded(TraceStack stack) {
TraceThread curThread = current.getThread();
if (curThread != stack.getThread()) {
return;
}
loadStack();
}
private void stackChanged(TraceStack stack) {
if (currentStack != stack) {
return;
}
updateStack();
}
private void stackDeleted(TraceStack stack) {
if (currentStack != stack) {
return;
}
loadStack();
}
// For updating a synthetic frame
private void bytesChanged(TraceAddressSpace space, TraceAddressSnapRange range) {
TraceThread curThread = current.getThread();
if (space.getThread() != curThread || space.getFrameLevel() != 0) {
return;
}
TraceProgramView view = current.getView();
if (view == null) {
return;
}
if (!view.getViewport().containsAnyUpper(range.getLifespan())) {
return;
}
List<StackFrameRow> stackData = stackTableModel.getModelData();
if (stackData.isEmpty() || !(stackData.get(0) instanceof StackFrameRow.Synthetic)) {
return;
}
StackFrameRow.Synthetic frameRow = (StackFrameRow.Synthetic) stackData.get(0);
Trace trace = current.getTrace();
Register pc = trace.getBaseLanguage().getProgramCounter();
if (!TraceRegisterUtils.rangeForRegister(pc).intersects(range.getRange())) {
return;
}
TraceMemorySpace regs =
trace.getMemoryManager().getMemoryRegisterSpace(curThread, false);
RegisterValue value = regs.getViewValue(current.getViewSnap(), pc);
Address address = trace.getBaseLanguage()
.getDefaultSpace()
.getAddress(value.getUnsignedValue().longValue());
frameRow.updateProgramCounter(address);
stackTableModel.fireTableDataChanged();
}
}
class ForFunctionsListener implements DebuggerStaticMappingChangeListener {
@Override
public void mappingsChanged(Set<Trace> affectedTraces, Set<Program> affectedPrograms) {
Trace curTrace = current.getTrace();
if (curTrace == null || !affectedTraces.contains(curTrace)) {
return;
}
Swing.runIfSwingOrRunLater(() -> stackTableModel.fireTableDataChanged());
}
}
final DebuggerStackProvider provider;
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
// @AutoServiceConsumed via method
DebuggerStaticMappingService mappingService;
@AutoServiceConsumed
private DebuggerListingService listingService; // TODO: Goto pc on double-click
@AutoServiceConsumed
private MarkerService markerService; // TODO: Mark non-current frame PCs, too. (separate plugin?)
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
@Override
public String getFilterString(Object t, Settings settings) {
return t == null ? "<null>" : t.toString();
}
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
super.getTableCellRendererComponent(data);
StackFrameRow row = (StackFrameRow) data.getRowObject();
if (row != null && row.getFrameLevel() == current.getFrame()) {
setBold();
}
return this;
}
};
// Table rows access this for function name resolution
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private Trace currentTrace; // Copy for transition
private TraceStack currentStack;
private ForStackListener forStackListener = new ForStackListener();
private ForFunctionsListener forFunctionsListener = new ForFunctionsListener();
protected final StackTableModel stackTableModel;
protected GhidraTable stackTable;
protected GhidraTableFilterPanel<StackFrameRow> stackFilterPanel;
private DebuggerStackActionContext myActionContext;
public DebuggerLegacyStackPanel(DebuggerStackPlugin plugin, DebuggerStackProvider provider) {
super(new BorderLayout());
this.provider = provider;
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
stackTableModel = new StackTableModel(provider.getTool());
stackTable = new GhidraTable(stackTableModel);
add(new JScrollPane(stackTable));
stackFilterPanel = new GhidraTableFilterPanel<>(stackTable, stackTableModel);
add(stackFilterPanel, BorderLayout.SOUTH);
stackTable.getSelectionModel().addListSelectionListener(evt -> {
if (evt.getValueIsAdjusting()) {
return;
}
contextChanged();
});
stackTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
activateSelectedFrame();
}
}
});
stackTable.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
activateSelectedFrame();
e.consume(); // lest it select the next row down
}
}
});
// TODO: Adjust default column widths?
TableColumnModel columnModel = stackTable.getColumnModel();
TableColumn levelCol = columnModel.getColumn(StackTableColumns.LEVEL.ordinal());
levelCol.setPreferredWidth(25);
levelCol.setCellRenderer(boldCurrentRenderer);
TableColumn pcCol = columnModel.getColumn(StackTableColumns.PC.ordinal());
pcCol.setCellRenderer(CustomToStringCellRenderer.MONO_OBJECT);
pcCol.setCellRenderer(boldCurrentRenderer);
TableColumn funcCol = columnModel.getColumn(StackTableColumns.FUNCTION.ordinal());
funcCol.setCellRenderer(boldCurrentRenderer);
TableColumn modCol = columnModel.getColumn(StackTableColumns.MODULE.ordinal());
modCol.setCellRenderer(boldCurrentRenderer);
TableColumn commCol = columnModel.getColumn(StackTableColumns.COMMENT.ordinal());
commCol.setCellRenderer(boldCurrentRenderer);
}
protected void contextChanged() {
StackFrameRow row = stackFilterPanel.getSelectedItem();
myActionContext =
row == null ? null : new DebuggerStackActionContext(provider, row, stackTable);
provider.contextChanged();
}
protected void activateSelectedFrame() {
if (myActionContext == null) {
return;
}
if (traceManager == null) {
return;
}
traceManager.activateFrame(myActionContext.getFrame().getFrameLevel());
}
protected void updateStack() {
Set<TraceStackFrame> toAdd = new LinkedHashSet<>(currentStack.getFrames(current.getSnap()));
for (Iterator<StackFrameRow> it = stackTableModel.getModelData().iterator(); it
.hasNext();) {
StackFrameRow row = it.next();
if (!toAdd.remove(row.frame)) {
it.remove();
}
else {
row.update();
}
}
for (TraceStackFrame frame : toAdd) {
stackTableModel.add(new StackFrameRow(this, frame));
}
stackTableModel.fireTableDataChanged();
}
protected void doSetCurrentStack(TraceStack stack) {
if (stack == null) {
currentStack = null;
stackTableModel.clear();
contextChanged();
return;
}
if (currentStack == stack && stack.hasFixedFrames()) {
stackTableModel.fireTableDataChanged();
return;
}
currentStack = stack;
stackTableModel.clear();
for (TraceStackFrame frame : currentStack.getFrames(current.getSnap())) {
stackTableModel.add(new StackFrameRow(this, frame));
}
}
/**
* Synthesize a stack with only one frame, taking PC from the registers
*/
protected void doSetSyntheticStack() {
stackTableModel.clear();
currentStack = null;
Trace curTrace = current.getTrace();
Register pc = curTrace.getBaseLanguage().getProgramCounter();
if (pc == null) {
contextChanged();
return;
}
TraceMemorySpace regs = pc.getAddressSpace().isRegisterSpace()
? curTrace.getMemoryManager().getMemoryRegisterSpace(current.getThread(), false)
: curTrace.getMemoryManager().getMemorySpace(pc.getAddressSpace(), false);
if (regs == null) {
contextChanged();
return;
}
RegisterValue value = regs.getViewValue(current.getViewSnap(), pc);
if (value == null) {
contextChanged();
return;
}
Address address = curTrace.getBaseLanguage()
.getDefaultSpace()
.getAddress(value.getUnsignedValue().longValue(), true);
stackTableModel.add(new StackFrameRow.Synthetic(this, address));
}
protected void loadStack() {
TraceThread curThread = current.getThread();
if (curThread == null) {
doSetCurrentStack(null);
return;
}
// TODO: getLatestViewStack? Conventionally, I don't expect any scratch stacks, yet.
TraceStack stack =
current.getTrace().getStackManager().getLatestStack(curThread, current.getViewSnap());
if (stack == null) {
doSetSyntheticStack();
}
else {
doSetCurrentStack(stack);
}
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
}
currentTrace.removeListener(forStackListener);
}
private void addNewListeners() {
if (currentTrace == null) {
return;
}
currentTrace.addListener(forStackListener);
}
private void doSetTrace(Trace trace) {
if (currentTrace == trace) {
return;
}
removeOldListeners();
this.currentTrace = trace;
addNewListeners();
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
current = coordinates;
doSetTrace(current.getTrace());
loadStack();
selectCurrentFrame();
}
protected void selectCurrentFrame() {
StackFrameRow row = stackTableModel.findFirst(r -> r.getFrameLevel() == current.getFrame());
if (row == null) {
// Strange
stackTable.clearSelection();
}
else {
stackFilterPanel.setSelectedItem(row);
}
}
public DebuggerStackActionContext getActionContext() {
return myActionContext;
}
@AutoServiceConsumed
private void setMappingService(DebuggerStaticMappingService mappingService) {
if (this.mappingService != null) {
this.mappingService.removeChangeListener(forFunctionsListener);
}
this.mappingService = mappingService;
if (this.mappingService != null) {
this.mappingService.addChangeListener(forFunctionsListener);
}
}
}

View file

@ -1,35 +0,0 @@
/* ###
* 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.stack;
import java.awt.Component;
import docking.DefaultActionContext;
public class DebuggerStackActionContext extends DefaultActionContext {
private final StackFrameRow frame;
public DebuggerStackActionContext(DebuggerStackProvider provider, StackFrameRow frame,
Component sourceComponent) {
super(provider, frame, sourceComponent);
this.frame = frame;
}
public StackFrameRow getFrame() {
return frame;
}
}

View file

@ -22,8 +22,6 @@ import java.util.Objects;
import javax.swing.*; import javax.swing.*;
import org.apache.commons.lang3.ArrayUtils;
import docking.ActionContext; import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import docking.action.DockingAction; import docking.action.DockingAction;
@ -84,7 +82,6 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
private final JPanel mainPanel = new JPanel(new BorderLayout()); private final JPanel mainPanel = new JPanel(new BorderLayout());
/*testing*/ DebuggerStackPanel panel; /*testing*/ DebuggerStackPanel panel;
/*testing*/ DebuggerLegacyStackPanel legacyPanel;
DockingAction actionUnwindStack; DockingAction actionUnwindStack;
@ -109,7 +106,6 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
protected void buildMainPanel() { protected void buildMainPanel() {
panel = new DebuggerStackPanel(this); panel = new DebuggerStackPanel(this);
mainPanel.add(panel); mainPanel.add(panel);
legacyPanel = new DebuggerLegacyStackPanel(plugin, this);
} }
protected void createActions() { protected void createActions() {
@ -130,13 +126,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
final ActionContext context; final ActionContext context = panel.getActionContext();
if (Trace.isLegacy(current.getTrace())) {
context = legacyPanel.getActionContext();
}
else {
context = panel.getActionContext();
}
if (context != null) { if (context != null) {
return context; return context;
} }
@ -160,31 +150,13 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
current = coordinates; current = coordinates;
if (Trace.isLegacy(coordinates.getTrace())) {
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
legacyPanel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyPanel) == -1) {
mainPanel.remove(panel);
mainPanel.add(legacyPanel);
mainPanel.validate();
}
}
else {
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
panel.coordinatesActivated(coordinates); panel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
mainPanel.remove(legacyPanel);
mainPanel.add(panel);
mainPanel.validate();
}
}
updateSubTitle(); updateSubTitle();
} }
public void traceClosed(Trace trace) { public void traceClosed(Trace trace) {
if (trace == current.getTrace()) { if (trace == current.getTrace()) {
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE); panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
} }
} }
} }

View file

@ -1,100 +0,0 @@
/* ###
* 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.stack;
import db.Transaction;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.stack.TraceStackFrame;
public class StackFrameRow {
public static class Synthetic extends StackFrameRow {
private Address pc;
public Synthetic(DebuggerLegacyStackPanel panel, Address pc) {
super(panel);
this.pc = pc;
}
public void updateProgramCounter(Address pc) {
this.pc = pc;
}
@Override
public Address getProgramCounter() {
return pc;
}
}
private final DebuggerLegacyStackPanel panel;
final TraceStackFrame frame;
private int level;
public StackFrameRow(DebuggerLegacyStackPanel panel, TraceStackFrame frame) {
this.panel = panel;
this.frame = frame;
this.level = frame.getLevel();
}
private StackFrameRow(DebuggerLegacyStackPanel panel) {
this.panel = panel;
this.frame = null;
this.level = 0;
}
public int getFrameLevel() {
return level;
}
public long getSnap() {
return panel.current.getSnap();
}
public Address getProgramCounter() {
return frame.getProgramCounter(getSnap());
}
public String getComment() {
return frame == null ? "" : frame.getComment(getSnap());
}
public void setComment(String comment) {
try (Transaction tx =
frame.getStack().getThread().getTrace().openTransaction("Frame comment")) {
frame.setComment(getSnap(), comment);
}
}
public boolean isCommentable() {
return frame != null;
}
public Function getFunction() {
return DebuggerStaticMappingUtils.getFunction(getProgramCounter(), panel.current,
panel.provider.getTool());
}
public String getModule() {
return DebuggerStaticMappingUtils.getModuleName(getProgramCounter(), panel.current);
}
protected void update() {
assert frame != null; // Should never update a synthetic stack
level = frame.getLevel();
}
}

View file

@ -1,334 +0,0 @@
/* ###
* 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.thread;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.*;
import docking.ActionContext;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import docking.widgets.table.RangeCursorTableHeaderRenderer.SeekListener;
import ghidra.app.plugin.core.debug.gui.DebuggerSnapActionContext;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.docking.settings.Settings;
import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.Address;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.thread.TraceThreadManager;
import ghidra.trace.util.TraceEvents;
import ghidra.util.database.ObjectKey;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.table.column.AbstractGColumnRenderer;
public class DebuggerLegacyThreadsPanel extends JPanel {
protected static long orZero(Long l) {
return l == null ? 0 : l;
}
protected enum ThreadTableColumns
implements EnumeratedTableColumn<ThreadTableColumns, ThreadRow> {
NAME("Name", String.class, ThreadRow::getName, ThreadRow::setName, true, true),
PC("PC", Address.class, ThreadRow::getProgramCounter, true, true),
FUNCTION("Function", ghidra.program.model.listing.Function.class, ThreadRow::getFunction, //
true, true),
MODULE("Module", String.class, ThreadRow::getModule, true, false),
SP("SP", Address.class, ThreadRow::getStackPointer, true, false),
STATE("State", ThreadState.class, ThreadRow::getState, true, true),
COMMENT("Comment", String.class, ThreadRow::getComment, ThreadRow::setComment, true, false);
private final String header;
private final Function<ThreadRow, ?> getter;
private final BiConsumer<ThreadRow, Object> setter;
private final boolean sortable;
private final boolean visible;
private final Class<?> cls;
<T> ThreadTableColumns(String header, Class<T> cls, Function<ThreadRow, T> getter,
boolean sortable, boolean visible) {
this(header, cls, getter, null, sortable, visible);
}
@SuppressWarnings("unchecked")
<T> ThreadTableColumns(String header, Class<T> cls, Function<ThreadRow, T> getter,
BiConsumer<ThreadRow, T> setter, boolean sortable, boolean visible) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<ThreadRow, Object>) setter;
this.sortable = sortable;
this.visible = visible;
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public Object getValueOf(ThreadRow row) {
return getter.apply(row);
}
@Override
public boolean isEditable(ThreadRow row) {
return setter != null;
}
@Override
public boolean isSortable() {
return sortable;
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public void setValueOf(ThreadRow row, Object value) {
setter.accept(row, value);
}
}
protected static class ThreadTableModel extends RowWrappedEnumeratedColumnTableModel< //
ThreadTableColumns, ObjectKey, ThreadRow, TraceThread> {
public ThreadTableModel(DebuggerThreadsProvider provider) {
super(provider.getTool(), "Threads", ThreadTableColumns.class,
TraceThread::getObjectKey, t -> new ThreadRow(provider, t), ThreadRow::getThread);
}
}
private class ForThreadsListener extends TraceDomainObjectListener {
public ForThreadsListener() {
listenForUntyped(DomainObjectEvent.RESTORED, this::objectRestored);
listenFor(TraceEvents.THREAD_ADDED, this::threadAdded);
listenFor(TraceEvents.THREAD_CHANGED, this::threadChanged);
listenFor(TraceEvents.THREAD_LIFESPAN_CHANGED, this::threadChanged);
listenFor(TraceEvents.THREAD_DELETED, this::threadDeleted);
}
private void objectRestored(DomainObjectChangeRecord rec) {
loadThreads();
}
private void threadAdded(TraceThread thread) {
threadTableModel.addItem(thread);
}
private void threadChanged(TraceThread thread) {
threadTableModel.updateItem(thread);
}
private void threadDeleted(TraceThread thread) {
threadTableModel.deleteItem(thread);
}
}
private final DebuggerThreadsProvider provider;
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private Trace currentTrace; // Copy for transition
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@SuppressWarnings("unused")
private final AutoService.Wiring autoServiceWiring;
private final ForThreadsListener forThreadsListener = new ForThreadsListener();
final TableCellRenderer boldCurrentRenderer = new AbstractGColumnRenderer<Object>() {
@Override
public String getFilterString(Object t, Settings settings) {
return t == null ? "<null>" : t.toString();
}
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
super.getTableCellRendererComponent(data);
ThreadRow row = (ThreadRow) data.getRowObject();
if (row != null && row.getThread() == current.getThread()) {
setBold();
}
return this;
}
};
final ThreadTableModel threadTableModel;
final GTable threadTable;
final GhidraTableFilterPanel<ThreadRow> threadFilterPanel;
private ActionContext myActionContext;
// strong ref
SeekListener seekListener;
public DebuggerLegacyThreadsPanel(DebuggerThreadsPlugin plugin,
DebuggerThreadsProvider provider) {
super(new BorderLayout());
this.provider = provider;
this.autoServiceWiring = AutoService.wireServicesConsumed(plugin, this);
threadTableModel = new ThreadTableModel(provider);
threadTable = new GhidraTable(threadTableModel);
threadTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
add(new JScrollPane(threadTable));
threadFilterPanel = new GhidraTableFilterPanel<>(threadTable, threadTableModel);
add(threadFilterPanel, BorderLayout.SOUTH);
myActionContext = new DebuggerSnapActionContext(current.getTrace(), current.getViewSnap());
threadTable.getSelectionModel().addListSelectionListener(this::threadRowSelected);
threadTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
activateSelectedThread();
}
}
});
threadTable.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
activateSelectedThread();
e.consume(); // lest it select the next row down
}
}
});
threadTable.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
setThreadRowActionContext();
}
});
threadTable.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
setThreadRowActionContext();
}
});
TableColumnModel columnModel = threadTable.getColumnModel();
TableColumn colName = columnModel.getColumn(ThreadTableColumns.NAME.ordinal());
colName.setCellRenderer(boldCurrentRenderer);
TableColumn colState = columnModel.getColumn(ThreadTableColumns.STATE.ordinal());
colState.setCellRenderer(boldCurrentRenderer);
TableColumn colComment = columnModel.getColumn(ThreadTableColumns.COMMENT.ordinal());
colComment.setCellRenderer(boldCurrentRenderer);
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
}
currentTrace.removeListener(forThreadsListener);
}
private void addNewListeners() {
if (currentTrace == null) {
return;
}
currentTrace.addListener(forThreadsListener);
}
private void doSetTrace(Trace trace) {
if (currentTrace == trace) {
return;
}
removeOldListeners();
currentTrace = trace;
addNewListeners();
loadThreads();
}
protected void coordinatesActivated(DebuggerCoordinates coordinates) {
current = coordinates;
doSetTrace(coordinates.getTrace());
doSetThread(coordinates.getThread());
}
private void doSetThread(TraceThread thread) {
if (thread != null) {
threadFilterPanel.setSelectedItem(threadTableModel.getRow(thread));
}
else {
threadTable.clearSelection();
}
threadTableModel.fireTableDataChanged();
}
protected void loadThreads() {
threadTableModel.clear();
Trace curTrace = current.getTrace();
if (curTrace == null) {
return;
}
TraceThreadManager manager = curTrace.getThreadManager();
threadTableModel.addAllItems(manager.getAllThreads());
}
private void threadRowSelected(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
setThreadRowActionContext();
}
private void activateSelectedThread() {
ThreadRow row = setThreadRowActionContext();
if (row != null && traceManager != null) {
traceManager.activateThread(row.getThread());
}
}
public ActionContext getActionContext() {
return myActionContext;
}
private ThreadRow setThreadRowActionContext() {
ThreadRow row = threadFilterPanel.getSelectedItem();
myActionContext = new DebuggerThreadActionContext(current.getTrace(),
row == null ? null : row.getThread());
provider.legacyThreadsPanelContextChanged();
return row;
}
}

View file

@ -1,38 +0,0 @@
/* ###
* 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.thread;
import docking.DefaultActionContext;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
public class DebuggerThreadActionContext extends DefaultActionContext {
private final Trace trace;
private final TraceThread thread;
public DebuggerThreadActionContext(Trace trace, TraceThread thread) {
this.trace = trace;
this.thread = thread;
}
public Trace getTrace() {
return trace;
}
public TraceThread getThread() {
return thread;
}
}

View file

@ -21,8 +21,6 @@ import java.util.Objects;
import javax.swing.*; import javax.swing.*;
import org.apache.commons.lang3.ArrayUtils;
import docking.ActionContext; import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
@ -107,7 +105,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
JPopupMenu traceTabPopupMenu; JPopupMenu traceTabPopupMenu;
DebuggerThreadsPanel panel; DebuggerThreadsPanel panel;
DebuggerLegacyThreadsPanel legacyPanel;
ActionContext myActionContext; ActionContext myActionContext;
@ -144,24 +141,7 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
current = coordinates; current = coordinates;
if (Trace.isLegacy(coordinates.getTrace())) {
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
legacyPanel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyPanel) == -1) {
mainPanel.remove(panel);
mainPanel.add(legacyPanel);
mainPanel.validate();
}
}
else {
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
panel.coordinatesActivated(coordinates); panel.coordinatesActivated(coordinates);
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
mainPanel.remove(legacyPanel);
mainPanel.add(panel);
mainPanel.validate();
}
}
forSnapsListener.setTrace(coordinates.getTrace()); forSnapsListener.setTrace(coordinates.getTrace());
@ -178,10 +158,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
myActionContext = panel.getActionContext(); myActionContext = panel.getActionContext();
} }
void legacyThreadsPanelContextChanged() {
myActionContext = legacyPanel.getActionContext();
}
@Override @Override
public ActionContext getActionContext(MouseEvent event) { public ActionContext getActionContext(MouseEvent event) {
if (myActionContext == null) { if (myActionContext == null) {
@ -196,7 +172,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
mainPanel = new JPanel(new BorderLayout()); mainPanel = new JPanel(new BorderLayout());
panel = new DebuggerThreadsPanel(this); panel = new DebuggerThreadsPanel(this);
legacyPanel = new DebuggerLegacyThreadsPanel(plugin, this);
mainPanel.add(panel); mainPanel.add(panel);
} }

View file

@ -1,137 +0,0 @@
/* ###
* 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.thread;
import db.Transaction;
import ghidra.app.plugin.core.debug.gui.action.PCLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.action.SPLocationTrackingSpec;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.trace.model.Trace;
import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.Msg;
public class ThreadRow {
private final DebuggerThreadsProvider provider;
private final TraceThread thread;
public ThreadRow(DebuggerThreadsProvider provider, TraceThread thread) {
this.provider = provider;
this.thread = thread;
}
public TraceThread getThread() {
return thread;
}
public Trace getTrace() {
return thread.getTrace();
}
public void setName(String name) {
try (Transaction tx = thread.getTrace().openTransaction("Rename thread")) {
thread.setName(0, name);
}
}
public String getName() {
return thread.getName(0);
}
private Address computeProgramCounter(DebuggerCoordinates coords) {
// TODO: Cheating a bit. Also, can user configure whether by stack or regs?
return PCLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(),
coords);
}
public Address getProgramCounter() {
DebuggerCoordinates coords = provider.current.thread(thread);
return computeProgramCounter(coords);
}
public Function getFunction() {
DebuggerCoordinates coords = provider.current.thread(thread);
Address pc = computeProgramCounter(coords);
return DebuggerStaticMappingUtils.getFunction(pc, coords, provider.getTool());
}
public String getModule() {
DebuggerCoordinates coords = provider.current.thread(thread);
Address pc = computeProgramCounter(coords);
return DebuggerStaticMappingUtils.getModuleName(pc, coords);
}
public Address getStackPointer() {
DebuggerCoordinates coords = provider.current.thread(thread);
return SPLocationTrackingSpec.INSTANCE.computeTraceAddress(provider.getTool(), coords);
}
public void setComment(String comment) {
try (Transaction tx = thread.getTrace().openTransaction("Set thread comment")) {
thread.setComment(0, comment);
}
}
public String getComment() {
return thread.getComment(0);
}
public ThreadState getState() {
// TODO: Once transition to TraceRmi is complete, this is all in TraceObjectManager
if (!thread.isValid(Long.MAX_VALUE)) {
return ThreadState.TERMINATED;
}
if (provider.targetService == null) {
return ThreadState.ALIVE;
}
Target target = provider.targetService.getTarget(thread.getTrace());
if (target == null) {
return ThreadState.ALIVE;
}
TraceExecutionState state = target.getThreadExecutionState(thread);
if (state == null) {
return ThreadState.UNKNOWN;
}
switch (state) {
case ALIVE:
return ThreadState.ALIVE;
case INACTIVE:
return ThreadState.UNKNOWN;
case RUNNING:
return ThreadState.RUNNING;
case STOPPED:
return ThreadState.STOPPED;
case TERMINATED:
return ThreadState.TERMINATED;
}
throw new AssertionError();
}
@Override
public String toString() {
try {
return getName();
}
catch (Exception e) {
Msg.error(this, "Error rendering as string: " + e);
return "<ERROR>";
}
}
}

View file

@ -1,43 +0,0 @@
/* ###
* 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.thread;
public enum ThreadState {
/**
* The last recorded state is alive, but the recorder is not tracking the live thread
*
* <p>
* This state is generally erroneous. If it is seen, the recorder has fallen out of sync with
* the live session and/or the trace.
*/
UNKNOWN,
/**
* The last recorded state is alive, but there is no live session to know STOPPED or RUNNING
*/
ALIVE,
/**
* The thread is alive, but suspended
*/
STOPPED,
/**
* The thread is alive and running
*/
RUNNING,
/**
* The thread has been terminated (either as recorded, or as reported by the live session)
*/
TERMINATED;
}