mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
commit
598efa66d9
9 changed files with 151 additions and 19 deletions
|
@ -700,4 +700,15 @@ public interface Target {
|
||||||
* @return true if busy
|
* @return true if busy
|
||||||
*/
|
*/
|
||||||
boolean isBusy();
|
boolean isBusy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcibly commit all of the back-ends transactions on this target's trace.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is generally not a recommended course of action, except that sometimes the back-end
|
||||||
|
* crashes and fails to close a transaction. It should only be invoked by a relatively hidden
|
||||||
|
* menu option, and mediated by a warning of some sort. Closing a transaction prematurely, when
|
||||||
|
* the back-end actually <em>does</em> still need it may cause a host of other problems.
|
||||||
|
*/
|
||||||
|
void forciblyCloseTransactions();
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,8 +171,19 @@ public interface TraceRmiConnection extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Check if the given target has a transaction open
|
* Check if the given target has a transaction open
|
||||||
*
|
*
|
||||||
* @param target
|
* @param target the target
|
||||||
* @return true if busy
|
* @return true if busy
|
||||||
*/
|
*/
|
||||||
boolean isBusy(Target target);
|
boolean isBusy(Target target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcibly commit all transactions this connection has on the given trace
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This may cause undefined behavior in the back-end, especially if it still needs the
|
||||||
|
* transaction.
|
||||||
|
*
|
||||||
|
* @param target the the target
|
||||||
|
*/
|
||||||
|
void forciblyCloseTransactions(Target target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,5 +105,15 @@
|
||||||
|
|
||||||
<P>Stop the server. This closes the persistent server. This does not affect pending acceptors
|
<P>Stop the server. This closes the persistent server. This does not affect pending acceptors
|
||||||
or established connections.</P>
|
or established connections.</P>
|
||||||
|
|
||||||
|
<H3><A name="forcibly_close_txes"></A>Forcibly Close Transactions</H3>
|
||||||
|
|
||||||
|
<P>Forcibly close all the back-end's transactions on the target trace. This is generally not a
|
||||||
|
recommended course of action, except that sometimes the back-end crashes and fails to close a
|
||||||
|
transaction. Un-closed transactions from the back-end can leave most, if not all, of the UI in
|
||||||
|
a stale state, since event processing on the trace is disabled. If there is good reason to
|
||||||
|
believe the back-end has forgotten to close a transaction, this action will forcibly close all
|
||||||
|
of them and re-enable event processing. If, however, the back-end was in fact still doing work
|
||||||
|
with that transaction, it may crash and/or corrupt the connection.</P>
|
||||||
</BODY>
|
</BODY>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
|
@ -161,6 +161,24 @@ public class TraceRmiConnectionManagerProvider extends ComponentProviderAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ForceCloseTransactionsActions {
|
||||||
|
String NAME = "Forcibly Close Transactions";
|
||||||
|
String DESCRIPTION = "Forcibly commit all remote transactions on the trace";
|
||||||
|
String GROUP = GROUP_MAINTENANCE;
|
||||||
|
String HELP_ANCHOR = "forcibly_close_txes";
|
||||||
|
|
||||||
|
static ActionBuilder builder(Plugin owner) {
|
||||||
|
String ownerName = owner.getName();
|
||||||
|
return new ActionBuilder(NAME, ownerName)
|
||||||
|
.description(DESCRIPTION)
|
||||||
|
.menuPath(NAME)
|
||||||
|
.popupMenuPath(NAME)
|
||||||
|
.menuGroup(GROUP)
|
||||||
|
.popupMenuGroup(GROUP)
|
||||||
|
.helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class InjectableGTree extends GTree {
|
class InjectableGTree extends GTree {
|
||||||
public InjectableGTree(GTreeNode root) {
|
public InjectableGTree(GTreeNode root) {
|
||||||
super(root);
|
super(root);
|
||||||
|
@ -200,6 +218,7 @@ public class TraceRmiConnectionManagerProvider extends ComponentProviderAdapter
|
||||||
DockingAction actionConnectOutbound;
|
DockingAction actionConnectOutbound;
|
||||||
DockingAction actionCloseConnection;
|
DockingAction actionCloseConnection;
|
||||||
DockingAction actionCloseAll;
|
DockingAction actionCloseAll;
|
||||||
|
DockingAction actionForceCloseTransactions;
|
||||||
|
|
||||||
TraceRmiManagerActionContext myActionContext;
|
TraceRmiManagerActionContext myActionContext;
|
||||||
|
|
||||||
|
@ -309,6 +328,12 @@ public class TraceRmiConnectionManagerProvider extends ComponentProviderAdapter
|
||||||
.enabledWhen(this::isActionCloseAllEnabled)
|
.enabledWhen(this::isActionCloseAllEnabled)
|
||||||
.onAction(this::doActionCloseAllActivated)
|
.onAction(this::doActionCloseAllActivated)
|
||||||
.buildAndInstallLocal(this);
|
.buildAndInstallLocal(this);
|
||||||
|
|
||||||
|
actionForceCloseTransactions = ForceCloseTransactionsActions.builder(plugin)
|
||||||
|
.withContext(TraceRmiManagerActionContext.class)
|
||||||
|
.enabledWhen(this::isActionForceCloseTransactionsEnabled)
|
||||||
|
.onAction(this::doActionCloseTransactionsActivated)
|
||||||
|
.buildAndInstallLocal(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -482,6 +507,21 @@ public class TraceRmiConnectionManagerProvider extends ComponentProviderAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isActionForceCloseTransactionsEnabled(TraceRmiManagerActionContext context) {
|
||||||
|
TraceRmiManagerNode node = context.getSelectedNode();
|
||||||
|
if (node instanceof TraceRmiTargetNode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doActionCloseTransactionsActivated(TraceRmiManagerActionContext context) {
|
||||||
|
TraceRmiManagerNode node = context.getSelectedNode();
|
||||||
|
if (node instanceof TraceRmiTargetNode tNode) {
|
||||||
|
tNode.getTarget().forciblyCloseTransactions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AutoServiceConsumed
|
@AutoServiceConsumed
|
||||||
private void setTraceRmiService(TraceRmiService traceRmiService) {
|
private void setTraceRmiService(TraceRmiService traceRmiService) {
|
||||||
if (this.traceRmiService != null) {
|
if (this.traceRmiService != null) {
|
||||||
|
|
|
@ -950,6 +950,18 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void checkRestoreEvents(OpenTrace open) {
|
||||||
|
final boolean restoreEvents;
|
||||||
|
synchronized (openTxes) {
|
||||||
|
restoreEvents = openTxes.keySet()
|
||||||
|
.stream()
|
||||||
|
.noneMatch(id -> id.doId.equals(open.doId));
|
||||||
|
}
|
||||||
|
if (restoreEvents) {
|
||||||
|
open.trace.setEventsEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected ReplyEndTx handleEndTx(RequestEndTx req) {
|
protected ReplyEndTx handleEndTx(RequestEndTx req) {
|
||||||
OpenTx tx;
|
OpenTx tx;
|
||||||
synchronized (openTxes) {
|
synchronized (openTxes) {
|
||||||
|
@ -973,16 +985,8 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.tx.close();
|
tx.tx.close();
|
||||||
|
checkRestoreEvents(open);
|
||||||
|
|
||||||
final boolean restoreEvents;
|
|
||||||
synchronized (openTxes) {
|
|
||||||
restoreEvents = openTxes.keySet()
|
|
||||||
.stream()
|
|
||||||
.noneMatch(id -> id.doId.domObjId == req.getOid().getId());
|
|
||||||
}
|
|
||||||
if (restoreEvents) {
|
|
||||||
open.trace.setEventsEnabled(true);
|
|
||||||
}
|
|
||||||
Swing.runLater(
|
Swing.runLater(
|
||||||
() -> plugin.listeners.invoke().transactionClosed(this, open.target, req.getAbort()));
|
() -> plugin.listeners.invoke().transactionClosed(this, open.target, req.getAbort()));
|
||||||
return ReplyEndTx.getDefaultInstance();
|
return ReplyEndTx.getDefaultInstance();
|
||||||
|
@ -1329,8 +1333,10 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBusy() {
|
public boolean isBusy() {
|
||||||
|
synchronized (openTxes) {
|
||||||
return !openTxes.isEmpty();
|
return !openTxes.isEmpty();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBusy(Target target) {
|
public boolean isBusy(Target target) {
|
||||||
|
@ -1339,11 +1345,33 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized (openTxes) {
|
||||||
for (Tid tid : openTxes.keySet()) {
|
for (Tid tid : openTxes.keySet()) {
|
||||||
if (Objects.equals(openTrace.doId, tid.doId)) {
|
if (Objects.equals(openTrace.doId, tid.doId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forciblyCloseTransactions(Target target) {
|
||||||
|
OpenTrace open = openTraces.getByTrace(target.getTrace());
|
||||||
|
if (open == null || open.target != target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (openTxes) {
|
||||||
|
for (OpenTx tx : List.copyOf(openTxes.values())) {
|
||||||
|
if (Objects.equals(open.doId, tx.txId.doId)) {
|
||||||
|
openTxes.remove(tx.txId);
|
||||||
|
tx.tx.commit();
|
||||||
|
tx.tx.close();
|
||||||
|
Swing.runLater(
|
||||||
|
() -> plugin.listeners.invoke().transactionClosed(this, target, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open.trace.setEventsEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1716,4 +1716,9 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||||
public boolean isBusy() {
|
public boolean isBusy() {
|
||||||
return connection.isBusy(this);
|
return connection.isBusy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forciblyCloseTransactions() {
|
||||||
|
connection.forciblyCloseTransactions(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,4 +277,8 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
|
||||||
public boolean isBusy(Target target) {
|
public boolean isBusy(Target target) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forciblyCloseTransactions(Target target) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,16 @@ public class TargetActionTask extends Task {
|
||||||
/**
|
/**
|
||||||
* Execute an {@link ActionEntry}
|
* Execute an {@link ActionEntry}
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* If the {@link ProgressService} is available, we will not enforce a timeout, because it should
|
||||||
|
* be relatively easy for the user to manage the pending tasks. Otherwise, we'll enforce the
|
||||||
|
* timeout. The rationale here is that some tasks do actually take a good bit of time. For
|
||||||
|
* example, some targets just have a large module list. Often a GUI component is asking for a
|
||||||
|
* reason, and if we time it out, that thing doesn't get what it needs. Furthermore, the entry
|
||||||
|
* disappears from the task list, even though the back-end is likely still working on it. That's
|
||||||
|
* not good, actually. Since we have a cancel button, let the user decide when it's had enough
|
||||||
|
* time.
|
||||||
|
*
|
||||||
* @param tool the tool in which to execute
|
* @param tool the tool in which to execute
|
||||||
* @param title the title, often {@link ActionEntry#display()}
|
* @param title the title, often {@link ActionEntry#display()}
|
||||||
* @param entry the action to execute
|
* @param entry the action to execute
|
||||||
|
@ -144,11 +154,13 @@ public class TargetActionTask extends Task {
|
||||||
*/
|
*/
|
||||||
public static CompletableFuture<Void> runAction(PluginTool tool, String title,
|
public static CompletableFuture<Void> runAction(PluginTool tool, String title,
|
||||||
ActionEntry entry) {
|
ActionEntry entry) {
|
||||||
return executeTask(tool, new TargetActionTask(tool, title, entry));
|
return executeTask(tool, new TargetActionTask(tool, title, entry,
|
||||||
|
tool.getService(ProgressService.class) == null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PluginTool tool;
|
private final PluginTool tool;
|
||||||
private final ActionEntry entry;
|
private final ActionEntry entry;
|
||||||
|
private final boolean timeout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a task fore the given action
|
* Construct a task fore the given action
|
||||||
|
@ -156,18 +168,25 @@ public class TargetActionTask extends Task {
|
||||||
* @param tool the plugin tool
|
* @param tool the plugin tool
|
||||||
* @param title the title, often {@link ActionEntry#display()}
|
* @param title the title, often {@link ActionEntry#display()}
|
||||||
* @param entry the action to execute
|
* @param entry the action to execute
|
||||||
|
* @param timeout whether or not to enforce the timeout
|
||||||
*/
|
*/
|
||||||
public TargetActionTask(PluginTool tool, String title, ActionEntry entry) {
|
public TargetActionTask(PluginTool tool, String title, ActionEntry entry, boolean timeout) {
|
||||||
super(title, false, false, false);
|
super(title, false, false, false);
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.entry = entry;
|
this.entry = entry;
|
||||||
|
this.timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
try {
|
try {
|
||||||
|
if (timeout) {
|
||||||
entry.run(entry.requiresPrompt());
|
entry.run(entry.requiresPrompt());
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
entry.invokeAsyncWithoutTimeout(entry.requiresPrompt()).get();
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (Throwable e) {
|
catch (Throwable e) {
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,4 +277,8 @@ public class MockTarget implements Target {
|
||||||
public boolean isBusy() {
|
public boolean isBusy() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forciblyCloseTransactions() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue