mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 10:52:19 +08:00
Merge remote-tracking branch 'origin/GP-3357_Dan_addRegionAction--SQUASHED'
This commit is contained in:
+11
@@ -101,6 +101,17 @@
|
||||
selects the region containing that cursor. If the dynamic listing has a selection, it selects
|
||||
all regions intersecting that selection.</P>
|
||||
|
||||
<H3><A name="add_region"></A>Add Region</H3>
|
||||
|
||||
<P>This action is available when a trace is active. It adds a new region to the memory map. It
|
||||
should only be used for emulation or to correct or diagnose trace recording issues.</P>
|
||||
|
||||
<H3><A name="delete_regions"></A>Delete Regions</H3>
|
||||
|
||||
<P>This action is available when at least one region is selected. It deletes those regions. Use
|
||||
this with caution, since recovering those regions could be difficult. In general, this should
|
||||
only be used to remove regions that were manually added.</P>
|
||||
|
||||
<H3><A name="force_full_view"></A>Force Full View</H3>
|
||||
|
||||
<P>This action is available when a trace is active. It forces all physical address spaces into
|
||||
|
||||
-16
@@ -1878,22 +1878,6 @@ public interface DebuggerResources {
|
||||
}
|
||||
}
|
||||
|
||||
interface ForceFullViewAction {
|
||||
String NAME = "Force Full View";
|
||||
String DESCRIPTION = "Ignore regions and fiew full address spaces";
|
||||
String GROUP = GROUP_GENERAL;
|
||||
String HELP_ANCHOR = "force_full_view";
|
||||
|
||||
static ToggleActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ToggleActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.menuGroup(GROUP_GENERAL)
|
||||
.menuPath(NAME)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface CompareTimesAction {
|
||||
String NAME = "Compare";
|
||||
String DESCRIPTION = "Compare this point in time to another";
|
||||
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/* ###
|
||||
* 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.Font;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import db.Transaction;
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import docking.widgets.model.GAddressRangeField;
|
||||
import docking.widgets.model.GSpanField;
|
||||
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceOverlappedRegionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
public class DebuggerAddRegionDialog extends ReusableDialogComponentProvider {
|
||||
private Trace trace;
|
||||
|
||||
private final JTextField fieldName = new JTextField();
|
||||
private final GAddressRangeField fieldRange = new GAddressRangeField();
|
||||
private final JTextField fieldLength = new JTextField();
|
||||
private final GSpanField fieldLifespan = new GSpanField();
|
||||
// NOTE: Flags can be toggled in table
|
||||
|
||||
public DebuggerAddRegionDialog() {
|
||||
super("Add Region", true, true, true, false);
|
||||
|
||||
populateComponents();
|
||||
}
|
||||
|
||||
protected void populateComponents() {
|
||||
JPanel panel = new JPanel(new PairLayout(5, 5));
|
||||
|
||||
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
panel.add(new JLabel("Name: "));
|
||||
panel.add(fieldName);
|
||||
|
||||
panel.add(new JLabel("Range: "));
|
||||
panel.add(fieldRange);
|
||||
|
||||
panel.add(new JLabel("Length: "));
|
||||
fieldLength.setFont(Font.decode("monospaced"));
|
||||
panel.add(fieldLength);
|
||||
|
||||
panel.add(new JLabel("Lifespan: "));
|
||||
panel.add(fieldLifespan);
|
||||
|
||||
MiscellaneousUtils.rigFocusAndEnter(fieldRange, this::rangeChanged);
|
||||
MiscellaneousUtils.rigFocusAndEnter(fieldLength, this::lengthChanged);
|
||||
|
||||
fieldLifespan.setLifespan(Lifespan.nowOn(0));
|
||||
|
||||
addWorkPanel(panel);
|
||||
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
protected static AddressRange range(Address min, long lengthMinus1) {
|
||||
return new AddressRangeImpl(min, min.addWrap(lengthMinus1));
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
fieldName.setText(name);
|
||||
}
|
||||
|
||||
protected void setFieldLength(long length) {
|
||||
fieldLength.setText(MiscellaneousUtils.lengthToString(length));
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return MiscellaneousUtils.parseLength(fieldLength.getText(), 1);
|
||||
}
|
||||
|
||||
protected void revalidateLength() {
|
||||
long length;
|
||||
if (fieldLength.getText().trim().startsWith("-")) {
|
||||
length = 1;
|
||||
}
|
||||
else {
|
||||
length = getLength();
|
||||
}
|
||||
length = MiscellaneousUtils.revalidateLengthByRange(fieldRange.getRange(), length);
|
||||
setFieldLength(length);
|
||||
}
|
||||
|
||||
protected void adjustLengthToRange() {
|
||||
AddressRange range = fieldRange.getRange();
|
||||
if (range == null) {
|
||||
return;
|
||||
}
|
||||
long length = range.getLength();
|
||||
setFieldLength(length);
|
||||
}
|
||||
|
||||
protected void adjustRangeToLength() {
|
||||
AddressRange range = fieldRange.getRange();
|
||||
if (range == null) {
|
||||
return;
|
||||
}
|
||||
Address min = range.getMinAddress();
|
||||
fieldRange.setRange(range(min, getLength() - 1));
|
||||
}
|
||||
|
||||
protected void rangeChanged() {
|
||||
adjustLengthToRange();
|
||||
}
|
||||
|
||||
protected void lengthChanged() {
|
||||
revalidateLength();
|
||||
adjustRangeToLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogShown() {
|
||||
super.dialogShown();
|
||||
setStatusText("");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
setStatusText("");
|
||||
super.cancelCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
addRegionAndClose();
|
||||
}
|
||||
|
||||
protected void setValues(Trace trace, Lifespan lifespan) {
|
||||
this.trace = trace;
|
||||
AddressFactory af = trace.getBaseAddressFactory();
|
||||
this.fieldRange.setAddressFactory(af);
|
||||
this.fieldRange.setRange(range(af.getDefaultAddressSpace().getAddress(0), 0));
|
||||
this.fieldLength.setText("0x1");
|
||||
this.fieldLifespan.setLifespan(lifespan);
|
||||
}
|
||||
|
||||
public void show(PluginTool tool, Trace trace, long snap) {
|
||||
setValues(trace, Lifespan.nowOn(snap));
|
||||
tool.showDialog(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
trace = null;
|
||||
fieldRange.setAddressFactory(null);
|
||||
super.close();
|
||||
}
|
||||
|
||||
protected void addRegionAndClose() {
|
||||
try (Transaction tx = trace.openTransaction("Add region: " + fieldName)) {
|
||||
trace.getMemoryManager()
|
||||
.addRegion(fieldName.getText(), fieldLifespan.getLifespan(),
|
||||
fieldRange.getRange());
|
||||
close();
|
||||
}
|
||||
catch (TraceOverlappedRegionException | DuplicateNameException e) {
|
||||
setStatusText(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
-4
@@ -24,15 +24,18 @@ import javax.swing.*;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import db.Transaction;
|
||||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerBlockChooserDialog;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractSelectAddressesAction;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.SelectRowsAction;
|
||||
import ghidra.app.plugin.core.debug.gui.model.DebuggerObjectActionContext;
|
||||
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProvider;
|
||||
import ghidra.app.plugin.core.debug.service.modules.MapRegionsBackgroundCommand;
|
||||
@@ -76,7 +79,8 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME, ownerName).description(DESCRIPTION)
|
||||
return new ActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.popupMenuPath(NAME)
|
||||
.popupMenuGroup(GROUP)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
@@ -91,7 +95,8 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME_PREFIX, ownerName).description(DESCRIPTION)
|
||||
return new ActionBuilder(NAME_PREFIX, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.popupMenuPath(NAME_PREFIX + "...")
|
||||
.popupMenuGroup(GROUP)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
@@ -107,13 +112,62 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME_PREFIX, ownerName).description(DESCRIPTION)
|
||||
return new ActionBuilder(NAME_PREFIX, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.popupMenuPath(NAME_PREFIX + "...")
|
||||
.popupMenuGroup(GROUP)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface AddRegionAction {
|
||||
String NAME = "Add Region";
|
||||
String DESCRIPTION = "Manually add a region to the memory map";
|
||||
String GROUP = DebuggerResources.GROUP_MAINTENANCE;
|
||||
String HELP_ANCHOR = "add_region";
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.menuGroup(GROUP)
|
||||
.menuPath(NAME)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface DeleteRegionsAction {
|
||||
String NAME = "Delete Regions";
|
||||
String DESCRIPTION = "Delete one or more regions from the memory map";
|
||||
String GROUP = DebuggerResources.GROUP_MAINTENANCE;
|
||||
String HELP_ANCHOR = "delete_regions";
|
||||
|
||||
static ActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.popupMenuGroup(GROUP)
|
||||
.popupMenuPath(NAME, "Yes, really. Delete them!")
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
interface ForceFullViewAction {
|
||||
String NAME = "Force Full View";
|
||||
String DESCRIPTION = "Ignore regions and fiew full address spaces";
|
||||
String GROUP = DebuggerResources.GROUP_GENERAL;
|
||||
String HELP_ANCHOR = "force_full_view";
|
||||
|
||||
static ToggleActionBuilder builder(Plugin owner) {
|
||||
String ownerName = owner.getName();
|
||||
return new ToggleActionBuilder(NAME, ownerName)
|
||||
.description(DESCRIPTION)
|
||||
.menuGroup(GROUP)
|
||||
.menuPath(NAME)
|
||||
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||
}
|
||||
}
|
||||
|
||||
protected class SelectAddressesAction extends AbstractSelectAddressesAction {
|
||||
public static final String GROUP = DebuggerResources.GROUP_GENERAL;
|
||||
|
||||
@@ -175,12 +229,16 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
// TODO: Lazy construction of these dialogs?
|
||||
private final DebuggerBlockChooserDialog blockChooserDialog;
|
||||
private final DebuggerRegionMapProposalDialog regionProposalDialog;
|
||||
private final DebuggerAddRegionDialog addRegionDialog;
|
||||
|
||||
DockingAction actionMapRegions;
|
||||
DockingAction actionMapRegionTo;
|
||||
DockingAction actionMapRegionsTo;
|
||||
|
||||
SelectAddressesAction actionSelectAddresses;
|
||||
DockingAction actionSelectRows;
|
||||
DockingAction actionAddRegion;
|
||||
DockingAction actionDeleteRegions;
|
||||
ToggleDockingAction actionForceFullView;
|
||||
|
||||
public DebuggerRegionsProvider(DebuggerRegionsPlugin plugin) {
|
||||
@@ -198,6 +256,7 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
|
||||
blockChooserDialog = new DebuggerBlockChooserDialog(tool);
|
||||
regionProposalDialog = new DebuggerRegionMapProposalDialog(this);
|
||||
addRegionDialog = new DebuggerAddRegionDialog();
|
||||
|
||||
setDefaultWindowPosition(WindowPosition.BOTTOM);
|
||||
setVisible(true);
|
||||
@@ -237,6 +296,14 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
.enabledWhen(ctx -> current.getTrace() != null)
|
||||
.onAction(this::activatedSelectCurrent)
|
||||
.buildAndInstallLocal(this);
|
||||
actionAddRegion = AddRegionAction.builder(plugin)
|
||||
.enabledWhen(ctx -> current.getTrace() != null)
|
||||
.onAction(this::activatedAddRegion)
|
||||
.buildAndInstallLocal(this);
|
||||
actionDeleteRegions = DeleteRegionsAction.builder(plugin)
|
||||
.enabledWhen(this::isContextNonEmpty)
|
||||
.onAction(this::activatedDeleteRegions)
|
||||
.buildAndInstallLocal(this);
|
||||
actionForceFullView = ForceFullViewAction.builder(plugin)
|
||||
.enabledWhen(ctx -> current.getTrace() != null)
|
||||
.onAction(this::activatedForceFullView)
|
||||
@@ -385,6 +452,25 @@ public class DebuggerRegionsProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void activatedAddRegion(ActionContext ignored) {
|
||||
if (current.getTrace() == null) {
|
||||
return;
|
||||
}
|
||||
addRegionDialog.show(tool, current.getTrace(), current.getSnap());
|
||||
}
|
||||
|
||||
private void activatedDeleteRegions(ActionContext ctx) {
|
||||
Set<TraceMemoryRegion> sel = getSelectedRegions(ctx);
|
||||
if (sel.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try (Transaction tx = current.getTrace().openTransaction("Delete regions")) {
|
||||
for (TraceMemoryRegion region : sel) {
|
||||
region.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void activatedForceFullView(ActionContext ignored) {
|
||||
if (current.getTrace() == null) {
|
||||
return;
|
||||
|
||||
+9
-94
@@ -15,10 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.modules;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.*;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
@@ -26,6 +23,7 @@ import javax.swing.border.EmptyBorder;
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import docking.widgets.model.GAddressRangeField;
|
||||
import docking.widgets.model.GSpanField;
|
||||
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.address.*;
|
||||
@@ -33,13 +31,10 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.modules.TraceConflictedMappingException;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.MessageType;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
|
||||
private static final String HEX_BIT64 = "0x" + BigInteger.ONE.shiftLeft(64).toString(16);
|
||||
|
||||
private DebuggerStaticMappingService mappingService;
|
||||
|
||||
private Program program;
|
||||
@@ -58,23 +53,6 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
|
||||
populateComponents();
|
||||
}
|
||||
|
||||
protected static void rigFocusAndEnter(Component c, Runnable runnable) {
|
||||
c.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
c.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void populateComponents() {
|
||||
JPanel panel = new JPanel(new PairLayout(5, 5));
|
||||
|
||||
@@ -99,10 +77,10 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
|
||||
panel.add(new JLabel("Lifespan: "));
|
||||
panel.add(fieldSpan);
|
||||
|
||||
rigFocusAndEnter(fieldProgRange, this::progRangeChanged);
|
||||
rigFocusAndEnter(fieldTraceRange, this::traceRangeChanged);
|
||||
rigFocusAndEnter(fieldLength, this::lengthChanged);
|
||||
rigFocusAndEnter(fieldSpan, this::spanChanged);
|
||||
MiscellaneousUtils.rigFocusAndEnter(fieldProgRange, this::progRangeChanged);
|
||||
MiscellaneousUtils.rigFocusAndEnter(fieldTraceRange, this::traceRangeChanged);
|
||||
MiscellaneousUtils.rigFocusAndEnter(fieldLength, this::lengthChanged);
|
||||
MiscellaneousUtils.rigFocusAndEnter(fieldSpan, this::spanChanged);
|
||||
|
||||
fieldSpan.setLifespan(Lifespan.nowOn(0));
|
||||
|
||||
@@ -149,33 +127,12 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
|
||||
revalidateByLength(fieldTraceRange, fieldProgRange);
|
||||
}
|
||||
|
||||
protected static long lengthMin(long a, long b) {
|
||||
if (a == 0) {
|
||||
return b;
|
||||
}
|
||||
if (b == 0) {
|
||||
return a;
|
||||
}
|
||||
return MathUtilities.unsignedMin(a, b);
|
||||
}
|
||||
|
||||
protected static long revalidateLengthByRange(AddressRange range, long length) {
|
||||
long maxLength =
|
||||
range.getAddressSpace().getMaxAddress().subtract(range.getMinAddress()) + 1;
|
||||
return lengthMin(length, maxLength);
|
||||
}
|
||||
|
||||
protected void setFieldLength(long length) {
|
||||
if (length == 0) {
|
||||
fieldLength.setText(HEX_BIT64);
|
||||
}
|
||||
else {
|
||||
fieldLength.setText("0x" + Long.toHexString(length));
|
||||
}
|
||||
fieldLength.setText(MiscellaneousUtils.lengthToString(length));
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return parseLength(fieldLength.getText(), 1);
|
||||
return MiscellaneousUtils.parseLength(fieldLength.getText(), 1);
|
||||
}
|
||||
|
||||
protected void revalidateLength() {
|
||||
@@ -186,8 +143,8 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
|
||||
else {
|
||||
length = getLength();
|
||||
}
|
||||
length = revalidateLengthByRange(fieldProgRange.getRange(), length);
|
||||
length = revalidateLengthByRange(fieldTraceRange.getRange(), length);
|
||||
length = MiscellaneousUtils.revalidateLengthByRange(fieldProgRange.getRange(), length);
|
||||
length = MiscellaneousUtils.revalidateLengthByRange(fieldTraceRange.getRange(), length);
|
||||
setFieldLength(length);
|
||||
}
|
||||
|
||||
@@ -248,48 +205,6 @@ public class DebuggerAddMappingDialog extends ReusableDialogComponentProvider {
|
||||
revalidateSpan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a value from 1 to 1<<64. Any value outside the range is "clipped" into the range.
|
||||
*
|
||||
* <p>
|
||||
* Note that a returned value of 0 indicates 2 to the power 64, which is just 1 too high to fit
|
||||
* into a 64-bit long.
|
||||
*
|
||||
* @param text the text to parse
|
||||
* @param defaultVal the default value should parsing fail altogether
|
||||
* @return the length, where 0 indicates {@code 1 << 64}.
|
||||
*/
|
||||
protected static long parseLength(String text, long defaultVal) {
|
||||
text = text.trim();
|
||||
String post;
|
||||
int radix;
|
||||
if (text.startsWith("-")) {
|
||||
return 0;
|
||||
}
|
||||
if (text.startsWith("0x")) {
|
||||
post = text.substring(2);
|
||||
radix = 16;
|
||||
}
|
||||
else {
|
||||
post = text;
|
||||
radix = 10;
|
||||
}
|
||||
BigInteger bi;
|
||||
try {
|
||||
bi = new BigInteger(post, radix);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return defaultVal;
|
||||
}
|
||||
if (bi.equals(BigInteger.ZERO)) {
|
||||
return 1;
|
||||
}
|
||||
if (bi.bitLength() > 64) {
|
||||
return 0; // indicates 2**64, the max length
|
||||
}
|
||||
return bi.longValue(); // Do not use exact. It checks bitLength again, and considers sign.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogShown() {
|
||||
super.dialogShown();
|
||||
|
||||
+86
@@ -16,17 +16,24 @@
|
||||
package ghidra.app.plugin.core.debug.utils;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.*;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.util.MathUtilities;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
public enum MiscellaneousUtils {
|
||||
;
|
||||
|
||||
public static final String HEX_BIT64 = "0x" + BigInteger.ONE.shiftLeft(64).toString(16);
|
||||
|
||||
/**
|
||||
* Obtain a swing component which may be used to edit the property.
|
||||
*
|
||||
@@ -63,6 +70,23 @@ public enum MiscellaneousUtils {
|
||||
"Ghidra does not know how to use PropertyEditor: " + editor.getClass().getName());
|
||||
}
|
||||
|
||||
public static void rigFocusAndEnter(Component c, Runnable runnable) {
|
||||
c.addFocusListener(new FocusAdapter() {
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
c.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> void collectUniqueInstances(Class<T> cls, Map<String, T> map,
|
||||
Function<T, String> keyFunc) {
|
||||
// This is wasteful. Existing instances will be re-instantiated and thrown away
|
||||
@@ -79,4 +103,66 @@ public enum MiscellaneousUtils {
|
||||
map.put(key, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static long lengthMin(long a, long b) {
|
||||
if (a == 0) {
|
||||
return b;
|
||||
}
|
||||
if (b == 0) {
|
||||
return a;
|
||||
}
|
||||
return MathUtilities.unsignedMin(a, b);
|
||||
}
|
||||
|
||||
public static String lengthToString(long length) {
|
||||
return length == 0 ? HEX_BIT64 : ("0x" + Long.toHexString(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a value from 1 to 1<<64. Any value outside the range is "clipped" into the range.
|
||||
*
|
||||
* <p>
|
||||
* Note that a returned value of 0 indicates 2 to the power 64, which is just 1 too high to fit
|
||||
* into a 64-bit long.
|
||||
*
|
||||
* @param text the text to parse
|
||||
* @param defaultVal the default value should parsing fail altogether
|
||||
* @return the length, where 0 indicates {@code 1 << 64}.
|
||||
*/
|
||||
public static long parseLength(String text, long defaultVal) {
|
||||
text = text.trim();
|
||||
String post;
|
||||
int radix;
|
||||
if (text.startsWith("-")) {
|
||||
return 0;
|
||||
}
|
||||
if (text.startsWith("0x")) {
|
||||
post = text.substring(2);
|
||||
radix = 16;
|
||||
}
|
||||
else {
|
||||
post = text;
|
||||
radix = 10;
|
||||
}
|
||||
BigInteger bi;
|
||||
try {
|
||||
bi = new BigInteger(post, radix);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return defaultVal;
|
||||
}
|
||||
if (bi.equals(BigInteger.ZERO)) {
|
||||
return 1;
|
||||
}
|
||||
if (bi.bitLength() > 64) {
|
||||
return 0; // indicates 2**64, the max length
|
||||
}
|
||||
return bi.longValue(); // Do not use exact. It checks bitLength again, and considers sign.
|
||||
}
|
||||
|
||||
public static long revalidateLengthByRange(AddressRange range, long length) {
|
||||
long maxLength =
|
||||
range.getAddressSpace().getMaxAddress().subtract(range.getMinAddress()) + 1;
|
||||
return MiscellaneousUtils.lengthMin(length, maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.memory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -354,6 +355,11 @@ public class DebuggerRegionsProviderLegacyTest extends AbstractGhidraHeadedDebug
|
||||
new AddressSet(listing.getSelection())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionAddRegion() throws Exception {
|
||||
createAndOpenTrace();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionSelectRows() throws Exception {
|
||||
addPlugin(tool, DebuggerListingPlugin.class);
|
||||
|
||||
+21
@@ -25,6 +25,7 @@ import org.junit.experimental.categories.Category;
|
||||
|
||||
import db.Transaction;
|
||||
import docking.widgets.table.DynamicTableColumn;
|
||||
import generic.Unique;
|
||||
import generic.test.category.NightlyCategory;
|
||||
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||
@@ -47,6 +48,7 @@ import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
|
||||
import ghidra.trace.model.modules.TraceStaticMapping;
|
||||
import ghidra.trace.model.target.*;
|
||||
@@ -471,6 +473,25 @@ public class DebuggerRegionsProviderTest extends AbstractGhidraHeadedDebuggerGUI
|
||||
new AddressSet(listing.getSelection())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionAddRegion() throws Exception {
|
||||
createAndOpenTrace();
|
||||
traceManager.activateTrace(tb.trace);
|
||||
|
||||
performEnabledAction(provider, provider.actionAddRegion, false);
|
||||
DebuggerAddRegionDialog dialog = waitForDialogComponent(DebuggerAddRegionDialog.class);
|
||||
runSwing(() -> {
|
||||
dialog.setName("Memory[heap]");
|
||||
dialog.setFieldLength(0x1000);
|
||||
dialog.lengthChanged(); // simulate ENTER/focus-exited
|
||||
dialog.okCallback();
|
||||
});
|
||||
waitForSwing();
|
||||
|
||||
TraceMemoryRegion region = Unique.assertOne(tb.trace.getMemoryManager().getAllRegions());
|
||||
assertEquals(tb.range(0, 0xfff), region.getRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionSelectRows() throws Exception {
|
||||
addPlugin(tool, DebuggerListingPlugin.class);
|
||||
|
||||
Reference in New Issue
Block a user