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");
|
||||
* 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.
|
||||
|
@ -16,28 +16,16 @@
|
|||
package ghidra.app.plugin.core.debug.gui.memory;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.DefaultActionContext;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
|
||||
public class DebuggerRegionActionContext extends DefaultActionContext {
|
||||
private final Set<TraceMemoryRegion> selectedRegions;
|
||||
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,
|
||||
Set<TraceMemoryRegion> selected, Component sourceComponent, boolean forcedSingle) {
|
||||
super(provider, selected, sourceComponent);
|
||||
|
|
|
@ -22,8 +22,6 @@ import java.util.Map.Entry;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import db.Transaction;
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
|
@ -226,7 +224,6 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
private final JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
|
||||
DebuggerRegionsPanel panel;
|
||||
DebuggerLegacyRegionsPanel legacyPanel;
|
||||
|
||||
// TODO: Lazy construction of these dialogs?
|
||||
private final DebuggerBlockChooserDialog blockChooserDialog;
|
||||
|
@ -273,7 +270,6 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
protected void buildMainPanel() {
|
||||
panel = new DebuggerRegionsPanel(this);
|
||||
mainPanel.add(panel);
|
||||
legacyPanel = new DebuggerLegacyRegionsPanel(this);
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
|
@ -317,13 +313,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
final ActionContext context;
|
||||
if (Trace.isLegacy(current.getTrace())) {
|
||||
context = legacyPanel.getActionContext();
|
||||
}
|
||||
else {
|
||||
context = panel.getActionContext();
|
||||
}
|
||||
final ActionContext context = panel.getActionContext();
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
@ -331,10 +321,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private boolean isContextNonEmpty(ActionContext context) {
|
||||
if (context instanceof DebuggerRegionActionContext ctx) {
|
||||
return legacyPanel.isContextNonEmpty(ctx);
|
||||
}
|
||||
else if (context instanceof DebuggerObjectActionContext ctx) {
|
||||
if (context instanceof DebuggerObjectActionContext ctx) {
|
||||
return panel.isContextNonEmpty(ctx);
|
||||
}
|
||||
return false;
|
||||
|
@ -353,10 +340,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
private Set<TraceMemoryRegion> getSelectedRegions(ActionContext context) {
|
||||
if (context instanceof DebuggerRegionActionContext ctx) {
|
||||
return DebuggerLegacyRegionsPanel.getSelectedRegions(ctx);
|
||||
}
|
||||
else if (context instanceof DebuggerObjectActionContext ctx) {
|
||||
if (context instanceof DebuggerObjectActionContext ctx) {
|
||||
return panel.getSelectedRegions(ctx);
|
||||
}
|
||||
return null;
|
||||
|
@ -495,12 +479,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
public void setSelectedRegions(Set<TraceMemoryRegion> sel) {
|
||||
if (Trace.isLegacy(current.getTrace())) {
|
||||
legacyPanel.setSelectedRegions(sel);
|
||||
}
|
||||
else {
|
||||
panel.setSelectedRegions(sel);
|
||||
}
|
||||
panel.setSelectedRegions(sel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -561,25 +540,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
current = coordinates;
|
||||
|
||||
if (Trace.isLegacy(coordinates.getTrace())) {
|
||||
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
legacyPanel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyPanel) == -1) {
|
||||
mainPanel.remove(panel);
|
||||
mainPanel.add(legacyPanel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
else {
|
||||
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
panel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
|
||||
mainPanel.remove(legacyPanel);
|
||||
mainPanel.add(panel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
panel.coordinatesActivated(coordinates);
|
||||
|
||||
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");
|
||||
* 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.
|
||||
|
@ -16,28 +16,16 @@
|
|||
package ghidra.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.DefaultActionContext;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
|
||||
public class DebuggerModuleActionContext extends DefaultActionContext {
|
||||
private final Set<TraceModule> selectedModules;
|
||||
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,
|
||||
Component sourceComponent, boolean forcedSingle) {
|
||||
super(provider, selected, sourceComponent);
|
||||
|
|
|
@ -26,8 +26,6 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.*;
|
||||
|
@ -426,12 +424,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
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) {
|
||||
return DebuggerModulesPanel.getSelectedModulesFromContext(ctx);
|
||||
}
|
||||
|
@ -440,12 +432,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
|
||||
protected static Set<TraceSection> getSelectedSections(ActionContext context,
|
||||
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) {
|
||||
return DebuggerModulesPanel.getSelectedSectionsFromContext(ctx);
|
||||
}
|
||||
|
@ -453,12 +439,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
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) {
|
||||
return DebuggerModulesPanel.getSelectedAddressesFromContext(ctx);
|
||||
}
|
||||
|
@ -565,9 +545,7 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
private final int defaultDividerSize = mainPanel.getDividerSize();
|
||||
|
||||
DebuggerModulesPanel modulesPanel;
|
||||
DebuggerLegacyModulesPanel legacyModulesPanel;
|
||||
DebuggerSectionsPanel sectionsPanel;
|
||||
DebuggerLegacySectionsPanel legacySectionsPanel;
|
||||
|
||||
// TODO: Lazy construction of these dialogs?
|
||||
private final DebuggerBlockChooserDialog blockChooserDialog;
|
||||
|
@ -713,34 +691,19 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
contextChanged();
|
||||
}
|
||||
|
||||
void legacyModulesPanelContextChanged() {
|
||||
myActionContext = legacyModulesPanel.getActionContext();
|
||||
if (isFilterSectionsByModules()) {
|
||||
legacySectionsPanel.loadSections();
|
||||
}
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
void sectionsPanelContextChanged() {
|
||||
myActionContext = sectionsPanel.getActionContext();
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
void legacySectionsPanelContextChanged() {
|
||||
myActionContext = legacySectionsPanel.getActionContext();
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
protected void buildMainPanel() {
|
||||
mainPanel.setContinuousLayout(true);
|
||||
|
||||
modulesPanel = new DebuggerModulesPanel(this);
|
||||
mainPanel.setLeftComponent(modulesPanel);
|
||||
legacyModulesPanel = new DebuggerLegacyModulesPanel(this);
|
||||
|
||||
sectionsPanel = new DebuggerSectionsPanel(this);
|
||||
mainPanel.setRightComponent(sectionsPanel);
|
||||
legacySectionsPanel = new DebuggerLegacySectionsPanel(this);
|
||||
|
||||
mainPanel.setResizeWeight(0.5);
|
||||
}
|
||||
|
@ -1071,7 +1034,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
actionShowSectionsTable.setSelected(showSectionsTable);
|
||||
mainPanel.setDividerSize(showSectionsTable ? defaultDividerSize : 0);
|
||||
sectionsPanel.setVisible(showSectionsTable);
|
||||
legacySectionsPanel.setVisible(showSectionsTable);
|
||||
mainPanel.resetToPreferredSizes();
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1052,6 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
this.filterSectionsByModules = filterSectionsByModules;
|
||||
actionFilterSectionsByModules.setSelected(filterSectionsByModules);
|
||||
sectionsPanel.setFilteredBySelectedModules(filterSectionsByModules);
|
||||
legacySectionsPanel.setFilteredBySelectedModules(filterSectionsByModules);
|
||||
}
|
||||
|
||||
private void activatedSelectCurrent(ActionContext ignored) {
|
||||
|
@ -1387,52 +1348,18 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter
|
|||
setAutoMapSpec(amState.spec);
|
||||
}
|
||||
|
||||
if (Trace.isLegacy(newTrace)) {
|
||||
modulesPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
sectionsPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
legacyModulesPanel.coordinatesActivated(coordinates);
|
||||
legacySectionsPanel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyModulesPanel) == -1) {
|
||||
mainPanel.remove(modulesPanel);
|
||||
mainPanel.remove(sectionsPanel);
|
||||
mainPanel.setLeftComponent(legacyModulesPanel);
|
||||
mainPanel.setRightComponent(legacySectionsPanel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
else {
|
||||
legacyModulesPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
legacySectionsPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
modulesPanel.coordinatesActivated(coordinates);
|
||||
sectionsPanel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), modulesPanel) == -1) {
|
||||
mainPanel.remove(legacyModulesPanel);
|
||||
mainPanel.remove(legacySectionsPanel);
|
||||
mainPanel.setLeftComponent(modulesPanel);
|
||||
mainPanel.setRightComponent(sectionsPanel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
modulesPanel.coordinatesActivated(coordinates);
|
||||
sectionsPanel.coordinatesActivated(coordinates);
|
||||
|
||||
contextChanged();
|
||||
}
|
||||
|
||||
public void setSelectedModules(Set<TraceModule> sel) {
|
||||
if (Trace.isLegacy(current.getTrace())) {
|
||||
legacyModulesPanel.setSelectedModules(sel);
|
||||
}
|
||||
else {
|
||||
modulesPanel.setSelectedModules(sel);
|
||||
}
|
||||
modulesPanel.setSelectedModules(sel);
|
||||
}
|
||||
|
||||
public void setSelectedSections(Set<TraceSection> sel) {
|
||||
if (Trace.isLegacy(current.getTrace())) {
|
||||
legacySectionsPanel.setSelectedSections(sel);
|
||||
}
|
||||
else {
|
||||
sectionsPanel.setSelectedSections(sel);
|
||||
}
|
||||
sectionsPanel.setSelectedSections(sel);
|
||||
}
|
||||
|
||||
private DataTreeDialog getProgramChooserDialog() {
|
||||
|
|
|
@ -16,28 +16,17 @@
|
|||
package ghidra.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.DefaultActionContext;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.trace.model.modules.TraceSection;
|
||||
|
||||
public class DebuggerSectionActionContext extends DefaultActionContext {
|
||||
private final Set<TraceSection> selectedSections;
|
||||
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,
|
||||
Set<TraceSection> selected, Component sourceComponent, boolean forcedSingle) {
|
||||
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 org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.action.DockingAction;
|
||||
|
@ -84,7 +82,6 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
|||
private final JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
|
||||
/*testing*/ DebuggerStackPanel panel;
|
||||
/*testing*/ DebuggerLegacyStackPanel legacyPanel;
|
||||
|
||||
DockingAction actionUnwindStack;
|
||||
|
||||
|
@ -109,7 +106,6 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
|||
protected void buildMainPanel() {
|
||||
panel = new DebuggerStackPanel(this);
|
||||
mainPanel.add(panel);
|
||||
legacyPanel = new DebuggerLegacyStackPanel(plugin, this);
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
|
@ -130,13 +126,7 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
final ActionContext context;
|
||||
if (Trace.isLegacy(current.getTrace())) {
|
||||
context = legacyPanel.getActionContext();
|
||||
}
|
||||
else {
|
||||
context = panel.getActionContext();
|
||||
}
|
||||
final ActionContext context = panel.getActionContext();
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
@ -160,31 +150,13 @@ public class DebuggerStackProvider extends ComponentProviderAdapter {
|
|||
|
||||
current = coordinates;
|
||||
|
||||
if (Trace.isLegacy(coordinates.getTrace())) {
|
||||
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
legacyPanel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyPanel) == -1) {
|
||||
mainPanel.remove(panel);
|
||||
mainPanel.add(legacyPanel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
else {
|
||||
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
panel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
|
||||
mainPanel.remove(legacyPanel);
|
||||
mainPanel.add(panel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
panel.coordinatesActivated(coordinates);
|
||||
updateSubTitle();
|
||||
}
|
||||
|
||||
public void traceClosed(Trace trace) {
|
||||
if (trace == current.getTrace()) {
|
||||
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");
|
||||
* 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.
|
||||
|
@ -21,8 +21,6 @@ import java.util.Objects;
|
|||
|
||||
import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.action.DockingActionIf;
|
||||
|
@ -107,7 +105,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
|||
|
||||
JPopupMenu traceTabPopupMenu;
|
||||
DebuggerThreadsPanel panel;
|
||||
DebuggerLegacyThreadsPanel legacyPanel;
|
||||
|
||||
ActionContext myActionContext;
|
||||
|
||||
|
@ -144,24 +141,7 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
|||
|
||||
current = coordinates;
|
||||
|
||||
if (Trace.isLegacy(coordinates.getTrace())) {
|
||||
panel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
legacyPanel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), legacyPanel) == -1) {
|
||||
mainPanel.remove(panel);
|
||||
mainPanel.add(legacyPanel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
else {
|
||||
legacyPanel.coordinatesActivated(DebuggerCoordinates.NOWHERE);
|
||||
panel.coordinatesActivated(coordinates);
|
||||
if (ArrayUtils.indexOf(mainPanel.getComponents(), panel) == -1) {
|
||||
mainPanel.remove(legacyPanel);
|
||||
mainPanel.add(panel);
|
||||
mainPanel.validate();
|
||||
}
|
||||
}
|
||||
panel.coordinatesActivated(coordinates);
|
||||
|
||||
forSnapsListener.setTrace(coordinates.getTrace());
|
||||
|
||||
|
@ -178,10 +158,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
|||
myActionContext = panel.getActionContext();
|
||||
}
|
||||
|
||||
void legacyThreadsPanelContextChanged() {
|
||||
myActionContext = legacyPanel.getActionContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
if (myActionContext == null) {
|
||||
|
@ -196,7 +172,6 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
|
|||
mainPanel = new JPanel(new BorderLayout());
|
||||
|
||||
panel = new DebuggerThreadsPanel(this);
|
||||
legacyPanel = new DebuggerLegacyThreadsPanel(plugin, this);
|
||||
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