mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-5193: Delete Legacy UI table panels.
This commit is contained in:
parent
66ac37b368
commit
a1ff800559
20 changed files with 18 additions and 2545 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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,12 +479,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedRegions(Set<TraceMemoryRegion> sel) {
|
public void setSelectedRegions(Set<TraceMemoryRegion> sel) {
|
||||||
if (Trace.isLegacy(current.getTrace())) {
|
panel.setSelectedRegions(sel);
|
||||||
legacyPanel.setSelectedRegions(sel);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panel.setSelectedRegions(sel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -561,25 +540,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
current = coordinates;
|
current = coordinates;
|
||||||
|
panel.coordinatesActivated(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);
|
|
||||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
|
|
||||||
mainPanel.remove(legacyPanel);
|
|
||||||
mainPanel.add(panel);
|
|
||||||
mainPanel.validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contextChanged();
|
contextChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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,52 +1348,18 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
||||||
setAutoMapSpec(amState.spec);
|
setAutoMapSpec(amState.spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Trace.isLegacy(newTrace)) {
|
modulesPanel.coordinatesActivated(coordinates);
|
||||||
modulesPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
sectionsPanel.coordinatesActivated(coordinates);
|
||||||
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);
|
|
||||||
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())) {
|
modulesPanel.setSelectedModules(sel);
|
||||||
legacyModulesPanel.setSelectedModules(sel);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
modulesPanel.setSelectedModules(sel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedSections(Set<TraceSection> sel) {
|
public void setSelectedSections(Set<TraceSection> sel) {
|
||||||
if (Trace.isLegacy(current.getTrace())) {
|
sectionsPanel.setSelectedSections(sel);
|
||||||
legacySectionsPanel.setSelectedSections(sel);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sectionsPanel.setSelectedSections(sel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTreeDialog getProgramChooserDialog() {
|
private DataTreeDialog getProgramChooserDialog() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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(coordinates);
|
||||||
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);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -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(coordinates);
|
||||||
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);
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue