GP-844: Fixed focus issues, introduced "activation"

This commit is contained in:
Dan 2021-04-12 10:15:48 -04:00
parent 62bd317380
commit 997ab4d9a0
71 changed files with 554 additions and 300 deletions

View File

@ -133,7 +133,7 @@ public interface DbgProcess extends DbgMemoryOperations {
*
* @return a future that completes when dbgeng has executed the command
*/
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
/**
* Specify a binary image for execution and debug symbols

View File

@ -48,7 +48,7 @@ public interface DbgStackFrame extends DbgStackFrameOperations {
*
* @return a future that completes when the frame is the current frame
*/
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
/**
* Get the thread for this frame

View File

@ -61,7 +61,7 @@ public interface DbgThread
*
* @return a future that completes when the thread is the current thread
*/
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
/**
* Get the process to which this thread belongs

View File

@ -0,0 +1,45 @@
/* ###
* 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 agent.dbgeng.manager.cmd;
import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface1.DbgModelTargetActiveScope;
import ghidra.dbg.target.TargetObject;
public class DbgRequestActivationCommand extends AbstractDbgCommand<Void> {
private DbgModelTargetActiveScope activator;
private TargetObject obj;
/**
* Set focus for the current ref
*
* @param manager the manager to execute the command
* @param activator in most cases the root object (must be an ancestor for the ref)
* @param obj the desired object to be made active
*/
public DbgRequestActivationCommand(DbgManagerImpl manager, DbgModelTargetActiveScope activator,
TargetObject obj) {
super(manager);
this.activator = activator;
this.obj = obj;
}
@Override
public void invoke() {
activator.doRequestActivation(obj);
}
}

View File

@ -19,19 +19,17 @@ import agent.dbgeng.dbgeng.DebugProcessId;
import agent.dbgeng.manager.DbgProcess;
import agent.dbgeng.manager.impl.DbgManagerImpl;
public class DbgProcessSelectCommand extends AbstractDbgCommand<Void> {
public class DbgSetActiveProcessCommand extends AbstractDbgCommand<Void> {
private DbgProcess process;
/**
* Select the given thread and frame level
*
* To simply select a thread, you should use frame 0 as the default.
* Set the active process
*
* @param manager the manager to execute the command
* @param process the desired process
*/
public DbgProcessSelectCommand(DbgManagerImpl manager, DbgProcess process) {
public DbgSetActiveProcessCommand(DbgManagerImpl manager, DbgProcess process) {
super(manager);
this.process = process;
}

View File

@ -19,18 +19,16 @@ import agent.dbgeng.dbgeng.DebugSessionId;
import agent.dbgeng.manager.DbgSession;
import agent.dbgeng.manager.impl.DbgManagerImpl;
public class DbgSessionSelectCommand extends AbstractDbgCommand<Void> {
public class DbgSetActiveSessionCommand extends AbstractDbgCommand<Void> {
private DbgSession session;
/**
* Select the given thread and frame level
*
* To simply select a thread, you should use frame 0 as the default.
* Set the active session
*
* @param manager the manager to execute the command
* @param process the desired process
*/
public DbgSessionSelectCommand(DbgManagerImpl manager, DbgSession session) {
public DbgSetActiveSessionCommand(DbgManagerImpl manager, DbgSession session) {
super(manager);
this.session = session;
}

View File

@ -19,20 +19,18 @@ import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.manager.DbgThread;
import agent.dbgeng.manager.impl.DbgManagerImpl;
public class DbgThreadSelectCommand extends AbstractDbgCommand<Void> {
public class DbgSetActiveThreadCommand extends AbstractDbgCommand<Void> {
private DbgThread thread;
/**
* Select the given thread and frame level
*
* To simply select a thread, you should use frame 0 as the default.
* Set the active thread
*
* @param manager the manager to execute the command
* @param thread the desired thread
* @param frameId the desired frame level
*/
public DbgThreadSelectCommand(DbgManagerImpl manager, DbgThread thread, Integer frameId) {
public DbgSetActiveThreadCommand(DbgManagerImpl manager, DbgThread thread, Integer frameId) {
super(manager);
this.thread = thread;
}

View File

@ -41,6 +41,7 @@ import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
import agent.dbgeng.manager.breakpoint.DbgBreakpointType;
import agent.dbgeng.manager.cmd.*;
import agent.dbgeng.manager.evt.*;
import agent.dbgeng.model.iface1.DbgModelTargetActiveScope;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import ghidra.async.*;
import ghidra.comm.util.BitmaskSet;
@ -1402,25 +1403,30 @@ public class DbgManagerImpl implements DbgManager {
return (DbgSessionImpl) eventSession;
}
public CompletableFuture<Void> selectThread(DbgThread thread) {
public CompletableFuture<Void> setActiveThread(DbgThread thread) {
currentThread = thread;
return execute(new DbgThreadSelectCommand(this, thread, null));
return execute(new DbgSetActiveThreadCommand(this, thread, null));
}
public CompletableFuture<Void> selectProcess(DbgProcess process) {
public CompletableFuture<Void> setActiveProcess(DbgProcess process) {
currentProcess = process;
return execute(new DbgProcessSelectCommand(this, process));
return execute(new DbgSetActiveProcessCommand(this, process));
}
public CompletableFuture<Void> selectSession(DbgSession session) {
public CompletableFuture<Void> setActiveSession(DbgSession session) {
currentSession = session;
return execute(new DbgSessionSelectCommand(this, session));
return execute(new DbgSetActiveSessionCommand(this, session));
}
public CompletableFuture<Void> requestFocus(DbgModelTargetFocusScope scope, TargetObject obj) {
return execute(new DbgRequestFocusCommand(this, scope, obj));
}
public CompletableFuture<Void> requestActivation(DbgModelTargetActiveScope activator,
TargetObject obj) {
return execute(new DbgRequestActivationCommand(this, activator, obj));
}
@Override
public CompletableFuture<Void> console(String command) {
return execute(

View File

@ -231,14 +231,14 @@ public class DbgProcessImpl implements DbgProcess {
}
@Override
public CompletableFuture<Void> select() {
return manager.selectProcess(this);
public CompletableFuture<Void> setActive() {
return manager.setActiveProcess(this);
}
@Override
public CompletableFuture<Void> fileExecAndSymbols(String file) {
return sequence(TypeSpec.VOID).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgFileExecAndSymbolsCommand(manager, file)).handle(seq::exit);
}).finish();
@ -247,7 +247,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<DbgThread> run() {
return sequence(TypeSpec.cls(DbgThread.class)).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgRunCommand(manager)).handle(seq::exit);
}).finish();
@ -256,7 +256,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Set<DbgThread>> attach(long toPid) {
return sequence(TypeSpec.cls(DbgThread.class).set()).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
pid = toPid; // TODO: Wait for successful completion?
manager.execute(
@ -268,7 +268,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Set<DbgThread>> reattach(TargetAttachable attachable) {
return sequence(TypeSpec.cls(DbgThread.class).set()).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(
new DbgAttachCommand(manager, this, BitmaskSet.of(DebugAttachFlags.EXISTING)))
@ -279,7 +279,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Void> detach() {
return sequence(TypeSpec.VOID).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgDetachCommand(manager, this)).handle(seq::exit);
}).finish();
@ -288,7 +288,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Void> kill() {
return sequence(TypeSpec.VOID).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgKillCommand(manager)).handle(seq::exit);
}).finish();
@ -297,7 +297,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Void> cont() {
return sequence(TypeSpec.VOID).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgContinueCommand(manager)).handle(seq::exit);
}).finish();
@ -306,7 +306,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Void> step(ExecSuffix suffix) {
return sequence(TypeSpec.VOID).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgStepCommand(manager, null, suffix)).handle(seq::exit);
}).finish();
@ -315,7 +315,7 @@ public class DbgProcessImpl implements DbgProcess {
@Override
public CompletableFuture<Void> step(Map<String, ?> args) {
return sequence(TypeSpec.VOID).then((seq) -> {
select().handle(seq::next);
setActive().handle(seq::next);
}).then((seq) -> {
manager.execute(new DbgStepCommand(manager, null, args)).handle(seq::exit);
}).finish();
@ -328,7 +328,7 @@ public class DbgProcessImpl implements DbgProcess {
if (first.isPresent()) {
return viaThread.apply(first.get());
}
return select().thenCompose(__ -> viaThis.get());
return setActive().thenCompose(__ -> viaThis.get());
}
@Override

View File

@ -124,8 +124,8 @@ public class DbgStackFrameImpl implements DbgStackFrame {
}
@Override
public CompletableFuture<Void> select() {
return thread.select();
public CompletableFuture<Void> setActive() {
return thread.setActive();
}
@Override

View File

@ -130,8 +130,8 @@ public class DbgThreadImpl implements DbgThread {
}
@Override
public CompletableFuture<Void> select() {
return manager.selectThread(this);
public CompletableFuture<Void> setActive() {
return manager.setActiveThread(this);
}
@Override
@ -207,35 +207,35 @@ public class DbgThreadImpl implements DbgThread {
@Override
public CompletableFuture<Void> cont() {
return select().thenCompose(__ -> {
return setActive().thenCompose(__ -> {
return manager.execute(new DbgContinueCommand(manager));
});
}
@Override
public CompletableFuture<Void> step(ExecSuffix suffix) {
return select().thenCompose(__ -> {
return setActive().thenCompose(__ -> {
return manager.execute(new DbgStepCommand(manager, id, suffix));
});
}
@Override
public CompletableFuture<Void> step(Map<String, ?> args) {
return select().thenCompose(__ -> {
return setActive().thenCompose(__ -> {
return manager.execute(new DbgStepCommand(manager, id, args));
});
}
@Override
public CompletableFuture<Void> kill() {
return select().thenCompose(__ -> {
return setActive().thenCompose(__ -> {
return manager.execute(new DbgKillCommand(manager));
});
}
@Override
public CompletableFuture<Void> detach() {
return select().thenCompose(__ -> {
return setActive().thenCompose(__ -> {
return manager.execute(new DbgDetachCommand(manager, process));
});
}

View File

@ -25,21 +25,21 @@ import ghidra.dbg.target.TargetObject;
public interface DbgModelSelectableObject extends DbgModelTargetObject {
public default CompletableFuture<Void> select() {
public default CompletableFuture<Void> setActive() {
if (this instanceof DbgModelTargetSession) {
DbgManagerImpl manager = getManager();
DbgProcess process = manager.getCurrentProcess();
return process.select();
return process.setActive();
}
if (this instanceof DbgModelTargetProcess) {
DbgModelTargetProcess tp = (DbgModelTargetProcess) this;
DbgProcess process = tp.getProcess();
return process.select();
return process.setActive();
}
if (this instanceof DbgModelTargetThread) {
DbgModelTargetThread tt = (DbgModelTargetThread) this;
DbgThread thread = tt.getThread();
return thread.select();
return thread.setActive();
}
if (this instanceof DbgModelTargetStackFrame) {
DbgModelTargetStackFrame tf = (DbgModelTargetStackFrame) this;
@ -47,7 +47,7 @@ public interface DbgModelSelectableObject extends DbgModelTargetObject {
if (ref instanceof DbgModelTargetThread) {
DbgModelTargetThread tt = (DbgModelTargetThread) ref;
DbgThread thread = tt.getThread();
return thread.select();
return thread.setActive();
}
}
return CompletableFuture.completedFuture(null);

View File

@ -0,0 +1,68 @@
/* ###
* 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 agent.dbgeng.model.iface1;
import java.util.concurrent.CompletableFuture;
import agent.dbgeng.model.iface2.DbgModelTargetObject;
import ghidra.async.AsyncUtils;
import ghidra.dbg.error.DebuggerIllegalArgumentException;
import ghidra.dbg.target.TargetActiveScope;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.util.PathUtils;
/**
* An interface which indicates this object is capable of launching targets.
*
* The targets this launcher creates ought to appear in its successors.
*
* @param <T> type for this
*/
public interface DbgModelTargetActiveScope extends DbgModelTargetObject, TargetActiveScope {
// NB: requesActivation request change in active object - propagates down to manager
// (but, of course, may then cause change in state)
@Override
public default CompletableFuture<Void> requestActivation(TargetObject obj) {
return getModel().gateFuture(getManager().requestActivation(this, obj));
}
public default CompletableFuture<Void> doRequestActivation(TargetObject obj) {
if (getManager().isWaiting()) {
return AsyncUtils.NIL;
}
getModel().assertMine(TargetObject.class, obj);
if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) {
throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope");
}
TargetObject cur = obj;
while (cur != null) {
if (cur instanceof DbgModelSelectableObject) {
DbgModelSelectableObject sel = (DbgModelSelectableObject) cur;
System.err.println("requestActivation " + obj);
return sel.setActive();
}
if (cur instanceof DbgModelTargetObject) {
DbgModelTargetObject def = (DbgModelTargetObject) cur;
cur = def.getParent();
continue;
}
throw new AssertionError();
}
return AsyncUtils.NIL;
}
}

View File

@ -39,7 +39,7 @@ public interface DbgModelTargetFocusScope extends DbgModelTargetObject, TargetFo
// NB: setFocus changes attributes - propagates up to client
public boolean setFocus(DbgModelSelectableObject sel);
// NB: requestFocus request change in active object - propagates down to manager
// NB: requestFocus request change in focused object - propagates down to manager
// (but, of course, may then cause change in state)
@Override
public default CompletableFuture<Void> requestFocus(TargetObject obj) {
@ -61,8 +61,9 @@ public interface DbgModelTargetFocusScope extends DbgModelTargetObject, TargetFo
while (cur != null) {
if (cur instanceof DbgModelSelectableObject) {
DbgModelSelectableObject sel = (DbgModelSelectableObject) cur;
System.err.println("requestFocus " + obj);
setFocus(sel);
return sel.select();
return AsyncUtils.NIL;
}
if (cur instanceof DbgModelTargetObject) {
DbgModelTargetObject def = (DbgModelTargetObject) cur;

View File

@ -31,7 +31,7 @@ public interface DbgModelTargetConnector
}
@Override
public CompletableFuture<Void> select();
public CompletableFuture<Void> setActive();
@Override
public TargetParameterMap getParameters();

View File

@ -69,13 +69,13 @@ public interface DbgModelTargetProcess extends //
}
@Override
public default CompletableFuture<Void> select() {
public default CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
DbgProcess process = getProcess();
if (process == null) {
process = manager.getEventProcess();
}
return manager.selectProcess(process);
return manager.setActiveProcess(process);
}
}

View File

@ -22,6 +22,7 @@ public interface DbgModelTargetRoot extends //
///DbgModelTargetObject,
DbgModelTargetAccessConditioned, //
DbgModelTargetAttacher, //
DbgModelTargetActiveScope, //
DbgModelTargetEventScope, //
DbgModelTargetLauncher, //
DbgModelTargetFocusScope, //
@ -29,5 +30,6 @@ public interface DbgModelTargetRoot extends //
void setDefaultConnector(DbgModelTargetConnector defaultConnector);
// getActive & requestActivation implemented by DbgModelTargetObject & DbgModelTargetActiveScope
// getFocus & requestFocus implemented by DbgModelTargetObject & DbgModelTargetFocusScope
}

View File

@ -71,12 +71,12 @@ public interface DbgModelTargetSession extends //
}
@Override
public default CompletableFuture<Void> select() {
public default CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
DbgSession session = getSession();
if (session == null) {
session = manager.getEventSession();
}
return manager.selectSession(session);
return manager.setActiveSession(session);
}
}

View File

@ -48,10 +48,10 @@ public interface DbgModelTargetStackFrame extends //
public static final String FROM_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "from"; // TODO
@Override
public default CompletableFuture<Void> select() {
public default CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
DbgThreadImpl thread = manager.getCurrentThread();
return manager.selectThread(thread);
return manager.setActiveThread(thread);
}
@Override

View File

@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture;
import agent.dbgeng.dbgeng.DebugSystemObjects;
import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.manager.*;
import agent.dbgeng.manager.cmd.DbgThreadSelectCommand;
import agent.dbgeng.manager.cmd.DbgSetActiveThreadCommand;
import agent.dbgeng.manager.impl.*;
import agent.dbgeng.model.iface1.*;
import agent.dbgeng.model.impl.DbgModelTargetStackImpl;
@ -58,10 +58,10 @@ public interface DbgModelTargetThread extends //
public void threadStateChangedSpecific(DbgState state, DbgReason reason);
@Override
public default CompletableFuture<Void> select() {
public default CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
DbgThread thread = getThread();
return manager.execute(new DbgThreadSelectCommand(manager, thread, null));
return manager.execute(new DbgSetActiveThreadCommand(manager, thread, null));
}
public DbgModelTargetStackImpl getStack();

View File

@ -55,7 +55,7 @@ public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
connectors.setDefaultConnector(this);
return CompletableFuture.completedFuture(null);
}

View File

@ -56,7 +56,7 @@ public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObje
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
connectors.setDefaultConnector(this);
return CompletableFuture.completedFuture(null);
}

View File

@ -197,9 +197,9 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
return manager.selectProcess(process);
return manager.setActiveProcess(process);
}
@Override

View File

@ -54,7 +54,7 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
connectors.setDefaultConnector(this);
return CompletableFuture.completedFuture(null);
}

View File

@ -21,8 +21,7 @@ import java.util.concurrent.CompletableFuture;
import agent.dbgeng.manager.*;
import agent.dbgeng.manager.impl.DbgProcessImpl;
import agent.dbgeng.model.iface1.DbgModelSelectableObject;
import agent.dbgeng.model.iface2.DbgModelTargetConnector;
import agent.dbgeng.model.iface2.DbgModelTargetRoot;
import agent.dbgeng.model.iface2.*;
import ghidra.dbg.error.DebuggerUserException;
import ghidra.dbg.target.*;
import ghidra.dbg.target.schema.*;
@ -119,10 +118,10 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
@Override
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
DbgReason reason) {
DbgProcess process = thread.getProcess();
DbgModelTargetThread targetThread =
(DbgModelTargetThread) getModel().getModelObject(thread);
changeAttributes(List.of(), List.of(), Map.of( //
TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME, Long.toHexString(process.getPid()), //
TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME, Long.toHexString(thread.getTid()) //
TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread //
), reason.desc());
}

View File

@ -87,7 +87,7 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
//DbgManagerImpl manager = getManager();
//DbgProcessImpl process = manager.getCurrentProcess();
//return manager.execute(new DbgProcessSelectCommand(manager, process));

View File

@ -138,9 +138,9 @@ public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
return manager.selectThread(thread.getThread());
return manager.setActiveThread(thread.getThread());
}
@Override

View File

@ -21,7 +21,7 @@ import java.util.concurrent.CompletableFuture;
import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.manager.*;
import agent.dbgeng.manager.cmd.DbgThreadSelectCommand;
import agent.dbgeng.manager.cmd.DbgSetActiveThreadCommand;
import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import agent.dbgeng.model.iface2.*;
@ -138,9 +138,9 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
DbgManagerImpl manager = getManager();
return manager.execute(new DbgThreadSelectCommand(manager, thread, null));
return manager.execute(new DbgSetActiveThreadCommand(manager, thread, null));
}
public DbgModelTargetRegisterContainerAndBank getRegisters() {

View File

@ -54,7 +54,7 @@ public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObject
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
connectors.setDefaultConnector(this);
return CompletableFuture.completedFuture(null);
}

View File

@ -406,7 +406,7 @@ public abstract class AbstractDbgManagerTest extends AbstractGhidraHeadlessInteg
}).then(seq -> {
mgr.waitForPrompt().handle(seq::next);
}).then(seq -> {
thread.get().select().handle(seq::next);
thread.get().setActive().handle(seq::next);
}).finish());
}
}

View File

@ -114,7 +114,7 @@ public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot
), "Focus changed");
intrinsics.put(TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus);
DbgModelTargetSession session = focus.getParentSession();
session.select();
session.setActive();
}
return doFire;
}
@ -314,15 +314,10 @@ public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot
if (targetThread == null) {
return;
}
DbgProcess process = thread.getProcess();
changeAttributes(List.of(), List.of(), Map.of( //
TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME, Long.toHexString(process.getPid()), //
TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME, Long.toHexString(thread.getTid()) //
TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread //
), reason.desc());
intrinsics.put(TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME,
Long.toHexString(process.getPid()));
intrinsics.put(TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME,
Long.toHexString(thread.getTid()));
intrinsics.put(TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread);
TargetEventType eventType = getEventType(state, cause, reason);
getListeners().fire.event(getProxy(), targetThread, eventType,
"Thread " + thread.getId() + " state changed", List.of(targetThread));

View File

@ -154,7 +154,7 @@ public interface GdbInferior extends GdbMemoryOperations {
*
* @return a future that completes when GDB has executed the command
*/
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
/**
* Specify a binary image for execution and debug symbols

View File

@ -258,7 +258,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
/**
* Get the inferior which currently has focus
*
* @see GdbInferior#select()
* @see GdbInferior#setActive()
* @return a handle to the inferior with focus
*/
GdbInferior currentInferior();

View File

@ -49,7 +49,7 @@ public interface GdbStackFrame extends GdbStackFrameOperations {
*
* @return a future that completes when the frame is the current frame
*/
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
/**
* Get the thread for this frame

View File

@ -70,7 +70,7 @@ public interface GdbThread
*
* @return a future that completes when the thread is the current thread
*/
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
/**
* Set the value of an internal GDB variable

View File

@ -208,7 +208,7 @@ public class GdbInferiorImpl implements GdbInferior {
* longer be current for the actual command execution. NB: The select command will cancel
* itself if this inferior is already current.
*/
return select().thenCombine(manager.execute(cmd), (s, e) -> e);
return setActive().thenCombine(manager.execute(cmd), (s, e) -> e);
}
@Override
@ -347,8 +347,8 @@ public class GdbInferiorImpl implements GdbInferior {
}
@Override
public CompletableFuture<Void> select() {
return manager.selectInferior(this);
public CompletableFuture<Void> setActive() {
return manager.setActiveInferior(this);
}
@Override

View File

@ -957,7 +957,7 @@ public class GdbManagerImpl implements GdbManager {
event(() -> listenersEvent.fire.inferiorSelected(cur, evt.getCause()),
"groupRemoved-sel");
// Also cause GDB to generate thread selection events, if applicable
selectInferior(cur);
setActiveInferior(cur);
}
}
@ -1524,7 +1524,7 @@ public class GdbManagerImpl implements GdbManager {
* @param inferior the inferior to select
* @return a future that completes when GDB has executed the command
*/
CompletableFuture<Void> selectInferior(GdbInferior inferior) {
CompletableFuture<Void> setActiveInferior(GdbInferior inferior) {
return execute(new GdbInferiorSelectCommand(this, inferior.getId()));
}

View File

@ -104,8 +104,8 @@ public class GdbStackFrameImpl implements GdbStackFrame {
}
@Override
public CompletableFuture<Void> select() {
return manager.execute(new GdbThreadSelectCommand(manager, thread.getId(), level));
public CompletableFuture<Void> setActive() {
return manager.execute(new GdbSetActiveThreadCommand(manager, thread.getId(), level));
}
@Override

View File

@ -128,7 +128,7 @@ public class GdbThreadImpl implements GdbThread {
protected <T> CompletableFuture<T> execute(AbstractGdbCommand<T> cmd) {
switch (cmd.getInterpreter()) {
case CLI:
return select().thenCombine(manager.execute(cmd), (__, v) -> v);
return setActive().thenCombine(manager.execute(cmd), (__, v) -> v);
case MI2:
return manager.execute(cmd);
default:
@ -137,9 +137,9 @@ public class GdbThreadImpl implements GdbThread {
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
// Bypass the select-me-first logic
return manager.execute(new GdbThreadSelectCommand(manager, id, null));
return manager.execute(new GdbSetActiveThreadCommand(manager, id, null));
}
@Override

View File

@ -19,7 +19,7 @@ import agent.gdb.manager.evt.*;
import agent.gdb.manager.impl.*;
import agent.gdb.manager.parsing.GdbMiParser.GdbMiFieldList;
public class GdbThreadSelectCommand extends AbstractGdbCommandWithThreadAndFrameId<Void> {
public class GdbSetActiveThreadCommand extends AbstractGdbCommandWithThreadAndFrameId<Void> {
/**
* Select the given thread and frame level
*
@ -30,7 +30,7 @@ public class GdbThreadSelectCommand extends AbstractGdbCommandWithThreadAndFrame
* @param threadId the desired thread Id
* @param frameId the desired frame level
*/
public GdbThreadSelectCommand(GdbManagerImpl manager, int threadId, Integer frameId) {
public GdbSetActiveThreadCommand(GdbManagerImpl manager, int threadId, Integer frameId) {
super(manager, threadId, frameId);
}

View File

@ -20,5 +20,5 @@ import java.util.concurrent.CompletableFuture;
import ghidra.dbg.target.TargetObject;
interface GdbModelSelectableObject extends TargetObject {
CompletableFuture<Void> select();
CompletableFuture<Void> setActive();
}

View File

@ -360,8 +360,8 @@ public class GdbModelTargetInferior
@Override
@Internal
public CompletableFuture<Void> select() {
return impl.gateFuture(inferior.select());
public CompletableFuture<Void> setActive() {
return impl.gateFuture(inferior.setActive());
}
@TargetAttributeType(name = EXIT_CODE_ATTRIBUTE_NAME)

View File

@ -34,16 +34,13 @@ import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils;
import ghidra.util.Msg;
@TargetObjectSchemaInfo(
name = "Session",
elements = {
@TargetElementType(type = Void.class) },
attributes = {
@TargetObjectSchemaInfo(name = "Session", elements = {
@TargetElementType(type = Void.class) }, attributes = {
@TargetAttributeType(type = Void.class) })
public class GdbModelTargetSession extends DefaultTargetModelRoot
implements TargetAccessConditioned, TargetAttacher, TargetFocusScope, TargetInterpreter,
TargetInterruptible, TargetCmdLineLauncher, TargetEventScope, GdbConsoleOutputListener,
GdbEventsListenerAdapter {
implements TargetAccessConditioned, TargetAttacher, TargetInterpreter, TargetInterruptible,
TargetCmdLineLauncher, TargetActiveScope, TargetEventScope, TargetFocusScope,
GdbConsoleOutputListener, GdbEventsListenerAdapter {
protected static final String GDB_PROMPT = "(gdb)";
protected final GdbModelImpl impl;
@ -88,18 +85,12 @@ public class GdbModelTargetSession extends DefaultTargetModelRoot
return inferiors;
}
@TargetAttributeType(
name = GdbModelTargetAvailableContainer.NAME,
required = true,
fixed = true)
@TargetAttributeType(name = GdbModelTargetAvailableContainer.NAME, required = true, fixed = true)
public GdbModelTargetAvailableContainer getAvailable() {
return available;
}
@TargetAttributeType(
name = GdbModelTargetBreakpointContainer.NAME,
required = true,
fixed = true)
@TargetAttributeType(name = GdbModelTargetBreakpointContainer.NAME, required = true, fixed = true)
public GdbModelTargetBreakpointContainer getBreakpoints() {
return breakpoints;
}
@ -240,7 +231,8 @@ public class GdbModelTargetSession extends DefaultTargetModelRoot
}
@Override
public CompletableFuture<Void> requestFocus(TargetObject obj) {
public CompletableFuture<Void> requestActivation(TargetObject obj) {
System.err.println("requestActivation " + obj);
impl.assertMine(TargetObject.class, obj);
/**
* Yes, this is pointless, since I'm the root, but do it right (TM), since this may change
@ -253,13 +245,31 @@ public class GdbModelTargetSession extends DefaultTargetModelRoot
while (cur != null) {
if (cur instanceof GdbModelSelectableObject) {
GdbModelSelectableObject sel = (GdbModelSelectableObject) cur;
/**
* Have to call setFocus here, since the call to select() is considered
* "internally-driven"
*/
return sel.select().thenRun(() -> {
setFocus(sel);
});
return sel.setActive();
}
cur = cur.getParent();
}
return AsyncUtils.NIL;
}
@Override
public CompletableFuture<Void> requestFocus(TargetObject obj) {
System.err.println("requestFocus " + obj);
impl.assertMine(TargetObject.class, obj);
/**
* Yes, this is pointless, since I'm the root, but do it right (TM), since this may change
* or be used as an example for other implementations.
*/
if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) {
throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope");
}
TargetObject cur = obj;
while (cur != null) {
if (cur instanceof GdbModelSelectableObject) {
GdbModelSelectableObject sel = (GdbModelSelectableObject) cur;
setFocus(sel);
return AsyncUtils.NIL;
}
cur = cur.getParent();
}
@ -292,9 +302,8 @@ public class GdbModelTargetSession extends DefaultTargetModelRoot
GdbStateChangeRecord sco =
new GdbStateChangeRecord(inf, threads, state, thread, cause, reason);
CompletableFuture<Void> infUpdates = CompletableFuture.allOf(
breakpoints.stateChanged(sco),
inferiors.stateChanged(sco));
CompletableFuture<Void> infUpdates =
CompletableFuture.allOf(breakpoints.stateChanged(sco), inferiors.stateChanged(sco));
infUpdates.whenComplete((v, t) -> {
if (thread == null) {
return;
@ -303,10 +312,24 @@ public class GdbModelTargetSession extends DefaultTargetModelRoot
* I have to do this for all inferiors, because I don't know in what order they will
* complete.
*/
thread.select().exceptionally(ex -> {
thread.setActive().exceptionally(ex -> {
impl.reportError(this, "Could not restore event thread", ex);
return null;
});
});
}
@Override
public void threadStateChanged(GdbThread thread, GdbState state, GdbCause cause,
GdbReason reason) {
/* REQUIRES GP-762
TargetThread targetThread = (TargetThread) impl.getModelObject(thread);
changeAttributes(List.of(), List.of(), Map.of( //
TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread //
), reason.desc());
*/
}
}

View File

@ -114,8 +114,8 @@ public class GdbModelTargetStackFrame extends DefaultTargetObject<TargetObject,
@Override
@Internal
public CompletableFuture<Void> select() {
return impl.gateFuture(frame.select());
public CompletableFuture<Void> setActive() {
return impl.gateFuture(frame.setActive());
}
@TargetAttributeType(name = FUNC_ATTRIBUTE_NAME)

View File

@ -216,8 +216,8 @@ public class GdbModelTargetThread
@Override
@Internal
public CompletableFuture<Void> select() {
return impl.gateFuture(thread.select());
public CompletableFuture<Void> setActive() {
return impl.gateFuture(thread.setActive());
}
public GdbModelTargetBreakpointLocation breakpointHit(GdbBreakpointHitReason reason) {

View File

@ -412,7 +412,7 @@ public abstract class AbstractGdbManagerTest extends AbstractGhidraHeadlessInteg
GdbThread thread = waitOn(mgr.currentInferior().run());
waitOn(mgr.waitForState(GdbState.STOPPED));
//waitOn(mgr.waitForPrompt());
waitOn(thread.select());
waitOn(thread.setActive());
}
}

View File

@ -65,6 +65,7 @@ public enum GadpRegistry {
registerInterface(TargetEnvironment.class, GadpClientTargetEnvironment.class);
registerInterface(TargetEventScope.class, GadpClientTargetEventScope.class);
registerInterface(TargetExecutionStateful.class, GadpClientTargetExecutionStateful.class);
registerInterface(TargetActiveScope.class, GadpClientTargetActiveScope.class);
registerInterface(TargetFocusScope.class, GadpClientTargetFocusScope.class);
registerInterface(TargetInterpreter.class, GadpClientTargetInterpreter.class);
registerInterface(TargetInterruptible.class, GadpClientTargetInterruptible.class);

View File

@ -0,0 +1,45 @@
/* ###
* 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.dbg.gadp.client;
import java.util.concurrent.CompletableFuture;
import ghidra.dbg.error.DebuggerIllegalArgumentException;
import ghidra.dbg.gadp.protocol.Gadp;
import ghidra.dbg.target.TargetActiveScope;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.util.PathUtils;
public interface GadpClientTargetActiveScope extends GadpClientTargetObject, TargetActiveScope {
@Override
default CompletableFuture<Void> requestActivation(TargetObject obj) {
getDelegate().assertValid();
getModel().assertMine(TargetObject.class, obj);
// The server should detect this error, but we can detect it here without sending a request
if (!PathUtils.isAncestor(getPath(), obj.getPath())) {
throw new DebuggerIllegalArgumentException(
"Can only activate a successor of the scope");
}
return getModel()
.sendChecked(
Gadp.ActivationRequest.newBuilder()
.setPath(GadpValueUtils.makePath(getPath()))
.setActive(GadpValueUtils.makePath(obj.getPath())),
Gadp.ActivationReply.getDefaultInstance())
.thenApply(__ -> null);
}
}

View File

@ -417,6 +417,8 @@ public class GadpClientHandler
return processResume(msg.getSequence(), msg.getResumeRequest());
case STEP_REQUEST:
return processStep(msg.getSequence(), msg.getStepRequest());
case ACTIVATION_REQUEST:
return processActivation(msg.getSequence(), msg.getActivationRequest());
default:
throw new GadpErrorException(Gadp.ErrorCode.EC_BAD_REQUEST,
"Unrecognized request: " + msg.getMsgCase());
@ -466,8 +468,8 @@ public class GadpClientHandler
return ref;
}
protected <T extends TargetObject> CompletableFuture<Integer> sendDelta(
List<String> parentPath, Delta<TargetObject, T> deltaE, Delta<?, ?> deltaA) {
protected <T extends TargetObject> CompletableFuture<Integer> sendDelta(List<String> parentPath,
Delta<TargetObject, T> deltaE, Delta<?, ?> deltaA) {
return channel.write(Gadp.RootMessage.newBuilder()
.setEventNotification(Gadp.EventNotification.newBuilder()
.setPath(GadpValueUtils.makePath(parentPath))
@ -627,6 +629,19 @@ public class GadpClientHandler
});
}
protected CompletableFuture<?> processActivation(int seqno, Gadp.ActivationRequest req) {
TargetActiveScope scope = getObjectChecked(req.getPath()).as(TargetActiveScope.class);
TargetObject active = getObjectChecked(req.getActive());
return scope.requestActivation(active).thenCompose(__ -> {
return model.flushEvents();
}).thenCompose(__ -> {
return channel.write(Gadp.RootMessage.newBuilder()
.setSequence(seqno)
.setActivationReply(Gadp.ActivationReply.getDefaultInstance())
.build());
});
}
protected CompletableFuture<?> processFocus(int seqno, Gadp.FocusRequest req) {
TargetFocusScope scope = getObjectChecked(req.getPath()).as(TargetFocusScope.class);
TargetObject focus = getObjectChecked(req.getFocus());
@ -705,8 +720,7 @@ public class GadpClientHandler
return channel.write(Gadp.RootMessage.newBuilder()
.setSequence(seqno)
.setMemoryReadReply(
Gadp.MemoryReadReply.newBuilder()
.setContent(ByteString.copyFrom(data)))
Gadp.MemoryReadReply.newBuilder().setContent(ByteString.copyFrom(data)))
.build());
});
}

View File

@ -435,6 +435,15 @@ message CacheInvalidateEvent {
message CacheInvalidateReply {
}
message ActivationRequest {
Path path = 1;
Path active = 2;
}
message ActivationReply {
}
message FocusRequest {
Path path = 1;
Path focus = 2;
@ -578,5 +587,9 @@ message RootMessage {
ResyncRequest resync_request = 125;
ResyncReply resync_reply = 225;
ActivationRequest activation_request = 126;
ActivationReply activation_reply = 226;
}
}

View File

@ -89,7 +89,7 @@ public class JdiModelTargetConnector extends JdiModelTargetObjectImpl
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
connectors.setDefaultConnector(this);
return CompletableFuture.completedFuture(null);
}

View File

@ -89,7 +89,7 @@ public class JdiModelTargetProcess extends JdiModelTargetObjectImpl
@Override
@Internal
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
return CompletableFuture.completedFuture(null);
///return thread.select();
}

View File

@ -126,7 +126,7 @@ public class JdiModelTargetStackFrame extends JdiModelTargetObjectImpl implement
}
@Override
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
///return frame.select();
return CompletableFuture.completedFuture(null);
}

View File

@ -299,7 +299,7 @@ public class JdiModelTargetThread extends JdiModelTargetObjectReference implemen
@Override
@Internal
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
return CompletableFuture.completedFuture(null);
}

View File

@ -370,7 +370,7 @@ public class JdiModelTargetVM extends JdiModelTargetObjectImpl implements //
@Override
@Internal
public CompletableFuture<Void> select() {
public CompletableFuture<Void> setActive() {
return CompletableFuture.completedFuture(null);
}

View File

@ -21,6 +21,6 @@ import ghidra.dbg.jdi.model.iface2.JdiModelTargetObject;
public interface JdiModelSelectableObject extends JdiModelTargetObject {
public CompletableFuture<Void> select();
public CompletableFuture<Void> setActive();
}

View File

@ -55,7 +55,7 @@ public interface JdiModelTargetFocusScope extends JdiModelTargetObject, TargetFo
if (cur instanceof JdiModelSelectableObject) {
JdiModelSelectableObject sel = (JdiModelSelectableObject) cur;
setFocus(sel);
return sel.select();
return sel.setActive();
}
cur = cur.getParent();
}

View File

@ -205,7 +205,7 @@ public class MemviewPanel extends JPanel implements MouseListener, MouseMotionLi
@Override
public void mousePressed(MouseEvent e) {
requestFocus();
requestFocus(); // COMPONENT
ctrlPressed = false;
currentRectangle = null;

View File

@ -481,7 +481,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
public void modelActivated(DebuggerObjectModel model) {
//TODO: what do we want here - change of focus, selection?
if (model != null && model.equals(currentModel)) {
this.requestFocus();
this.requestFocus(); // COMPONENT
this.toFront();
}
}

View File

@ -156,16 +156,10 @@ public class ObjectNode extends GTreeSlowLoadingNode { //extends GTreeNode
if (provider != null) {
ObjectContainer rootContainer = provider.getRoot();
Map<String, Object> rootMap = rootContainer.getAttributeMap();
String cname = container.getDecoratedName();
if (rootMap.containsKey(TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME)) {
String id = (String) rootMap.get(TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME);
if (cname.contains("0x" + id)) {
return ICON_EVENT;
}
}
if (rootMap.containsKey(TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME)) {
String id = (String) rootMap.get(TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME);
if (cname.contains("0x" + id)) {
if (rootMap.containsKey(TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME)) {
TargetThread targetProcess =
(TargetThread) rootMap.get(TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME);
if (container.getTargetObject().equals(targetProcess)) {
return ICON_EVENT;
}
}

View File

@ -85,6 +85,10 @@ public class ObjectTree implements ObjectPane {
TargetObject targetObject = node.getTargetObject();
if (targetObject != null && !(targetObject instanceof DummyTargetObject) &&
e.getEventOrigin().equals(EventOrigin.USER_GENERATED)) {
DebugModelConventions.requestActivation(targetObject).exceptionally(ex -> {
Msg.error(this, "Could not activate " + targetObject, ex);
return null;
});
DebugModelConventions.requestFocus(targetObject).exceptionally(ex -> {
Msg.error(this, "Could not focus " + targetObject, ex);
return null;

View File

@ -31,6 +31,7 @@ import docking.widgets.EventTrigger;
import docking.widgets.HorizontalTabPanel;
import docking.widgets.HorizontalTabPanel.TabListCellRenderer;
import docking.widgets.table.*;
import docking.widgets.timeline.TimelineListener;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@ -39,6 +40,8 @@ import ghidra.app.plugin.core.debug.gui.DebuggerSnapActionContext;
import ghidra.app.plugin.core.debug.gui.thread.DebuggerThreadsTimelinePanel.VetoableSnapRequestListener;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerTraceManagerService.BooleanChangeAdapter;
import ghidra.dbg.DebugModelConventions;
import ghidra.dbg.target.TargetThread;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.ComponentProviderAdapter;
@ -345,6 +348,13 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
private ActionContext myActionContext;
private final TimelineListener timelineListener = new TimelineListener() {
@Override
public void itemActivated(int index) {
timelineItemActivated(index);
}
};
DockingAction actionSaveTrace;
StepSnapBackwardAction actionStepSnapBackward;
StepTickBackwardAction actionStepTickBackward;
@ -543,6 +553,23 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
contextChanged();
}
private void timelineItemActivated(int index) {
ThreadRow row = threadTableModel.getRowObject(index);
rowActivated(row);
}
private void rowActivated(ThreadRow row) {
TraceThread thread = row.getThread();
Trace trace = thread.getTrace();
TraceRecorder recorder = modelService.getRecorder(trace);
if (recorder != null) {
TargetThread targetThread = recorder.getTargetThread(thread);
if (targetThread != null && targetThread.isValid()) {
DebugModelConventions.requestActivation(targetThread);
}
}
}
protected void buildMainPanel() {
traceTabPopupMenu = new JPopupMenu("Trace");
@ -566,6 +593,16 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
threadTable.getSelectionModel().addListSelectionListener(this::threadRowSelected);
threadTimeline.setSelectionModel(threadTable.getSelectionModel());
threadTimeline.addSnapRequestedListener(snapListener);
threadTimeline.addTimelineListener(timelineListener);
threadTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
int selectedRow = threadTable.getSelectedRow();
ThreadRow row = threadTableModel.getRowObject(selectedRow);
rowActivated(row);
}
});
mainPanel.add(splitPane, BorderLayout.CENTER);
@ -628,18 +665,15 @@ public class DebuggerThreadsProvider extends ComponentProviderAdapter {
closeItem.addActionListener(evt -> {
traceManager.closeTrace(trace);
});
JMenuItem closeOthers =
new JMenuItem("Close Others", DebuggerResources.ICON_CLOSE);
JMenuItem closeOthers = new JMenuItem("Close Others", DebuggerResources.ICON_CLOSE);
closeOthers.addActionListener(evt -> {
traceManager.closeOtherTraces(trace);
});
JMenuItem closeDead =
new JMenuItem("Close Dead", DebuggerResources.ICON_CLOSE);
JMenuItem closeDead = new JMenuItem("Close Dead", DebuggerResources.ICON_CLOSE);
closeDead.addActionListener(evt -> {
traceManager.closeDeadTraces();
});
JMenuItem closeAll =
new JMenuItem("Close All", DebuggerResources.ICON_CLOSE);
JMenuItem closeAll = new JMenuItem("Close All", DebuggerResources.ICON_CLOSE);
closeAll.addActionListener(evt -> {
for (Trace t : List.copyOf(traceManager.getOpenTraces())) {
traceManager.closeTrace(t);

View File

@ -27,8 +27,9 @@ import com.google.common.collect.Range;
import docking.widgets.*;
import docking.widgets.RangeCursorPanel.Direction;
import docking.widgets.table.RowObjectTableModel;
import docking.widgets.timeline.TimelineListener;
import docking.widgets.timeline.TimelinePanel;
import docking.widgets.timeline.TimelineViewRangeListener;
import ghidra.app.services.DebuggerModelService;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.datastruct.ListenerSet;
@ -56,8 +57,11 @@ public class DebuggerThreadsTimelinePanel extends JScrollPane {
}
protected class ThreadTimelinePanel extends TimelinePanel<ThreadRow, Long> {
protected final RowObjectTableModel<ThreadRow> model;
public ThreadTimelinePanel(RowObjectTableModel<ThreadRow> model) {
super(model, ThreadRow::getLifespan);
this.model = model;
}
}
@ -95,9 +99,17 @@ public class DebuggerThreadsTimelinePanel extends JScrollPane {
protected final ListenerSet<VetoableSnapRequestListener> listeners =
new ListenerSet<>(VetoableSnapRequestListener.class);
protected final RangeCursorValueListener valueListener = this::cursorValueChanged;
protected final TimelineViewRangeListener viewRangeListener = this::viewRangeChanged;
protected final TimelineListener timelineListener = new TimelineListener() {
@Override
public void viewRangeChanged(Range<Double> range) {
timelineViewRangeChanged(range);
}
};
private DebuggerModelService modelService;
private RowObjectTableModel<ThreadRow> model;
public DebuggerThreadsTimelinePanel(RowObjectTableModel<ThreadRow> model) {
this.model = model;
setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
@ -106,7 +118,7 @@ public class DebuggerThreadsTimelinePanel extends JScrollPane {
setColumnHeaderView(topCursor);
topCursor.addValueListener(valueListener);
timeline.addViewRangeListener(viewRangeListener);
timeline.addTimelineListener(timelineListener);
}
public void addSnapRequestedListener(VetoableSnapRequestListener listener) {
@ -122,7 +134,7 @@ public class DebuggerThreadsTimelinePanel extends JScrollPane {
}
}
private void viewRangeChanged(Range<Double> range) {
private void timelineViewRangeChanged(Range<Double> range) {
topCursor.setRange(range);
}
@ -166,4 +178,8 @@ public class DebuggerThreadsTimelinePanel extends JScrollPane {
}
return timeline.getCellBounds(row);
}
public void addTimelineListener(TimelineListener listener) {
timeline.addTimelineListener(listener);
}
}

View File

@ -78,8 +78,7 @@ import ghidra.util.task.*;
ModelObjectFocusedPluginEvent.class,
TraceRecorderAdvancedPluginEvent.class,
},
servicesRequired = {
},
servicesRequired = {},
servicesProvided = {
DebuggerTraceManagerService.class,
})

View File

@ -105,11 +105,6 @@ public class DebuggerObjectsPluginScreenShots extends GhidraScreenShotGenerator
@Test
public void testCaptureDebuggerObjectsPlugin() throws Throwable {
mb.createTestModel("Debugger");
mb.testModel.session.setAttributes(List.of(), Map.of(
TargetEventScope.EVENT_PROCESS_ATTRIBUTE_NAME, "1a12", // Get arrow icon on process
TargetEventScope.EVENT_THREAD_ATTRIBUTE_NAME, "1a34", // Get arrow icon on thread
TargetObject.DISPLAY_ATTRIBUTE_NAME, "Debugger"),
"TestOverride");
DefaultTestTargetObject<?, ?> available =
new DefaultTestTargetObject<>(mb.testModel.session, "Available", "");
@ -135,8 +130,7 @@ public class DebuggerObjectsPluginScreenShots extends GhidraScreenShotGenerator
new DefaultTestTargetObject<>(process1a12, "Devices", "");
DefaultTestTargetObject<?, ?> pEnvironment =
new DefaultTestTargetObject<>(process1a12, "Environment", "");
DefaultTestTargetObject<?, ?> pIo =
new DefaultTestTargetObject<>(process1a12, "Io", "");
DefaultTestTargetObject<?, ?> pIo = new DefaultTestTargetObject<>(process1a12, "Io", "");
DefaultTestTargetObject<?, ?> pMemory =
new DefaultTestTargetObject<>(process1a12, "Memory", "");
DefaultTestTargetObject<?, ?> pModules =
@ -147,6 +141,11 @@ public class DebuggerObjectsPluginScreenShots extends GhidraScreenShotGenerator
DefaultTestTargetObject<?, ?> thread1a34 =
new ActionyTestTargetObject(pThreads, "[0x1a34]", "");
mb.testModel.session.setAttributes(List.of(),
Map.of(TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, thread1a34, // Get arrow icon on thread
TargetObject.DISPLAY_ATTRIBUTE_NAME, "Debugger"),
"TestOverride");
DefaultTestTargetObject<?, ?> tEnvironment =
new DefaultTestTargetObject<>(thread1a34, "Environment", "");
@ -155,81 +154,45 @@ public class DebuggerObjectsPluginScreenShots extends GhidraScreenShotGenerator
DefaultTestTargetObject<?, ?> activationContextStackPointer =
new DefaultTestTargetObject<>(teEnvBlock, "ActivationContextStackPointer", "");
activationContextStackPointer.setAttributes(List.of(), Map.of(
TargetObject.DISPLAY_ATTRIBUTE_NAME, "ActivationContextStackPointer : 789abc",
"_modified", true),
"Initialized");
activationContextStackPointer
.setAttributes(List.of(),
Map.of(TargetObject.DISPLAY_ATTRIBUTE_NAME,
"ActivationContextStackPointer : 789abc", "_modified", true),
"Initialized");
DefaultTestTargetObject<?, ?> activeFrame =
new DefaultTestTargetObject<>(teEnvBlock, "ActiveFrame", "");
activeFrame.setAttributes(List.of(), Map.of(
TargetObject.DISPLAY_ATTRIBUTE_NAME, "ActiveFrame : 0",
"_modified", true),
activeFrame.setAttributes(List.of(),
Map.of(TargetObject.DISPLAY_ATTRIBUTE_NAME, "ActiveFrame : 0", "_modified", true),
"Initialized");
DefaultTestTargetObject<?, ?> bogusFocus =
new DefaultTestTargetObject<>(teEnvBlock, "BOGUS FOCUS", "");
teEnvBlock.setAttributes(
List.of(
activationContextStackPointer,
activeFrame,
bogusFocus),
Map.of(
"_kind", "OBJECT_TARGET_OBJECT"), // Makes it magenta
teEnvBlock.setAttributes(List.of(activationContextStackPointer, activeFrame, bogusFocus),
Map.of("_kind", "OBJECT_TARGET_OBJECT"), // Makes it magenta
"Initialized");
tEnvironment.setAttributes(List.of(
teEnvBlock),
Map.of(), "Initialized");
tEnvironment.setAttributes(List.of(teEnvBlock), Map.of(), "Initialized");
thread1a34.setAttributes(
List.of(
tEnvironment),
Map.of(
TargetObject.DISPLAY_ATTRIBUTE_NAME, "0x1a34"),
"Initialized");
thread1a34.setAttributes(List.of(tEnvironment),
Map.of(TargetObject.DISPLAY_ATTRIBUTE_NAME, "0x1a34"), "Initialized");
pThreads.setElements(List.of(
thread1a34),
Map.of(), "Initialized");
pThreads.setElements(List.of(thread1a34), Map.of(), "Initialized");
process1a12.setAttributes(
List.of(
pDebug,
pDevices,
pEnvironment,
pIo,
pMemory,
pModules,
pThreads),
Map.of(
TargetObject.DISPLAY_ATTRIBUTE_NAME, "0x1a12",
"Handle", "321",
"Id", "1a12",
List.of(pDebug, pDevices, pEnvironment, pIo, pMemory, pModules, pThreads),
Map.of(TargetObject.DISPLAY_ATTRIBUTE_NAME, "0x1a12", "Handle", "321", "Id", "1a12",
"Name", "winmine.exe"),
"Initialized");
sProcesses.setElements(List.of(
process1a12),
Map.of(), "Initialized");
sProcesses.setElements(List.of(process1a12), Map.of(), "Initialized");
session0.setAttributes(
List.of(
sAttributes,
sDevices,
sProcesses),
Map.of(
TargetObject.DISPLAY_ATTRIBUTE_NAME, "0x0",
"Id", 0),
session0.setAttributes(List.of(sAttributes, sDevices, sProcesses),
Map.of(TargetObject.DISPLAY_ATTRIBUTE_NAME, "0x0", "Id", 0), "Initialized");
sessions.setElements(List.of(session0), Map.of(), "Initialized");
mb.testModel.session.changeAttributes(List.of(), List.of(available, sessions), Map.of(),
"Initialized");
sessions.setElements(List.of(
session0),
Map.of(), "Initialized");
mb.testModel.session.changeAttributes(List.of(), List.of(
available,
sessions),
Map.of(), "Initialized");
Swing.runNow(() -> {
modelService.addModel(mb.testModel);
modelService.activateModel(mb.testModel);

View File

@ -813,6 +813,26 @@ public enum DebugModelConventions {
return collectAncestors.thenApply(AllRequiredAccess::new);
}
/**
* Request activation of the given object in its nearest active scope
*
* <p>
* Note if the object has no suitable active scope, this method fails silently.
*
* @param obj the object on which to request activation
* @return a future which completes when activation is granted, or exceptionally
*/
public static CompletableFuture<Void> requestActivation(TargetObject obj) {
CompletableFuture<? extends TargetActiveScope> futureActivator =
DebugModelConventions.findSuitable(TargetActiveScope.class, obj);
return futureActivator.thenCompose(activator -> {
if (activator == null) {
return AsyncUtils.NIL;
}
return activator.requestActivation(obj);
});
}
/**
* Request focus on the given object in its nearest focus scope
*

View File

@ -0,0 +1,38 @@
/* ###
* 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.dbg.target;
import java.util.concurrent.CompletableFuture;
import ghidra.dbg.DebuggerTargetObjectIface;
/**
* An object made active
*
* "Active" here describes which object in a given class the target should operate on
*/
@DebuggerTargetObjectIface("ActiveScope")
public interface TargetActiveScope extends TargetObject {
/**
* Set the given object as the target's active object for the given type
*
* @param obj the object to setActive
* @return a future which completes upon successfully changing focus.
*/
CompletableFuture<Void> requestActivation(TargetObject obj);
}

View File

@ -27,8 +27,7 @@ import ghidra.dbg.target.schema.TargetAttributeType;
@DebuggerTargetObjectIface("EventScope")
public interface TargetEventScope extends TargetObject {
String EVENT_PROCESS_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "event_process";
String EVENT_THREAD_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "event_thread";
String EVENT_OBJECT_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "event_thread";
public enum TargetEventType {
/**
@ -116,33 +115,13 @@ public interface TargetEventScope extends TargetObject {
}
}
/**
* If applicable, get the process producing the last reported event
*
* <p>
* TODO: This is currently the hexadecimal PID. It should really be a ref to the process object.
*
* <p>
* TODO: Since the event thread will be a successor of the event process, this may not be
* needed, but perhaps keep it for convenience.
*
* @return the process or reference
*/
@TargetAttributeType(name = EVENT_PROCESS_ATTRIBUTE_NAME, hidden = true)
public default /*TODO: TargetProcess*/ String getEventProcess() {
return getTypedAttributeNowByName(EVENT_PROCESS_ATTRIBUTE_NAME, String.class, null);
}
/**
* If applicable, get the thread producing the last reported event
*
* <p>
* TODO: This is currently the hexadecimal TID. It should really be a ref to the thread object.
*
* @return the thread or reference
*/
@TargetAttributeType(name = EVENT_THREAD_ATTRIBUTE_NAME, hidden = true)
public default /*TODO: TargetThread*/ String getEventThread() {
return getTypedAttributeNowByName(EVENT_THREAD_ATTRIBUTE_NAME, String.class, null);
@TargetAttributeType(name = EVENT_OBJECT_ATTRIBUTE_NAME, hidden = true)
public default TargetThread getEventThread() {
return getTypedAttributeNowByName(EVENT_OBJECT_ATTRIBUTE_NAME, TargetThread.class, null);
}
}

View File

@ -158,20 +158,21 @@ import ghidra.lifecycle.Internal;
*/
public interface TargetObject extends Comparable<TargetObject> {
Set<Class<? extends TargetObject>> ALL_INTERFACES = Set.of(TargetAccessConditioned.class,
TargetAggregate.class, TargetAttachable.class, TargetAttacher.class,
TargetBreakpointLocation.class, TargetBreakpointLocationContainer.class,
TargetBreakpointSpec.class, TargetBreakpointSpecContainer.class, TargetConsole.class,
TargetDataTypeMember.class, TargetDataTypeNamespace.class, TargetDeletable.class,
TargetDetachable.class, TargetEnvironment.class, TargetEventScope.class,
TargetExecutionStateful.class, TargetFocusScope.class, TargetInterpreter.class,
TargetInterruptible.class, TargetKillable.class, TargetLauncher.class, TargetMemory.class,
TargetMemoryRegion.class, TargetMethod.class, TargetModule.class,
TargetModuleContainer.class, TargetNamedDataType.class, TargetProcess.class,
TargetRegister.class, TargetRegisterBank.class, TargetRegisterContainer.class,
TargetResumable.class, TargetSection.class, TargetSectionContainer.class, TargetStack.class,
TargetStackFrame.class, TargetSteppable.class, TargetSymbol.class,
TargetSymbolNamespace.class, TargetThread.class, TargetTogglable.class);
Set<Class<? extends TargetObject>> ALL_INTERFACES =
Set.of(TargetAccessConditioned.class, TargetAggregate.class, TargetAttachable.class,
TargetAttacher.class, TargetBreakpointLocation.class,
TargetBreakpointLocationContainer.class, TargetBreakpointSpec.class,
TargetBreakpointSpecContainer.class, TargetConsole.class, TargetDataTypeMember.class,
TargetDataTypeNamespace.class, TargetDeletable.class, TargetDetachable.class,
TargetEnvironment.class, TargetEventScope.class, TargetExecutionStateful.class,
TargetActiveScope.class, TargetFocusScope.class, TargetInterpreter.class,
TargetInterruptible.class, TargetKillable.class, TargetLauncher.class,
TargetMemory.class, TargetMemoryRegion.class, TargetMethod.class, TargetModule.class,
TargetModuleContainer.class, TargetNamedDataType.class, TargetProcess.class,
TargetRegister.class, TargetRegisterBank.class, TargetRegisterContainer.class,
TargetResumable.class, TargetSection.class, TargetSectionContainer.class,
TargetStack.class, TargetStackFrame.class, TargetSteppable.class, TargetSymbol.class,
TargetSymbolNamespace.class, TargetThread.class, TargetTogglable.class);
Map<String, Class<? extends TargetObject>> INTERFACES_BY_NAME = initInterfacesByName();
/**
@ -300,8 +301,7 @@ public interface TargetObject extends Comparable<TargetObject> {
return false;
}
TargetObject that = (TargetObject) obj;
return this.getModel() == that.getModel() &&
Objects.equals(this.getPath(), that.getPath());
return this.getModel() == that.getModel() && Objects.equals(this.getPath(), that.getPath());
}
/**
@ -371,8 +371,7 @@ public interface TargetObject extends Comparable<TargetObject> {
}
int result = thisModel.toString().compareTo(thatModel.toString());
if (result == 0) {
return Integer.compare(
System.identityHashCode(thisModel),
return Integer.compare(System.identityHashCode(thisModel),
System.identityHashCode(thatModel));
}
return result;
@ -707,8 +706,7 @@ public interface TargetObject extends Comparable<TargetObject> {
* interface
*/
public default <T extends TargetObject> //
CompletableFuture<? extends Map<String, ? extends T>> fetchChildrenSupporting(
Class<T> iface) {
CompletableFuture<? extends Map<String, ? extends T>> fetchChildrenSupporting(Class<T> iface) {
return fetchChildren().thenApply(m -> m.entrySet()
.stream()
.filter(e -> iface.isAssignableFrom(e.getValue().getClass()))
@ -793,8 +791,7 @@ public interface TargetObject extends Comparable<TargetObject> {
/**
* @see #fetchSubAttributes(List)
*/
public default CompletableFuture<? extends Map<String, ?>> fetchSubAttributes(
String... sub) {
public default CompletableFuture<? extends Map<String, ?>> fetchSubAttributes(String... sub) {
return fetchSubAttributes(List.of(sub));
}

View File

@ -17,6 +17,10 @@ package docking.widgets.timeline;
import com.google.common.collect.Range;
public interface TimelineViewRangeListener {
void viewRangeChanged(Range<Double> viewRange);
public interface TimelineListener {
default void viewRangeChanged(Range<Double> viewRange) {
}
default void itemActivated(int index) {
}
}

View File

@ -433,6 +433,7 @@ public class TimelinePanel<T, N extends Number & Comparable<N>> extends JPanel {
}
protected class CellMouseListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
Component cell = e.getComponent();
@ -454,6 +455,7 @@ public class TimelinePanel<T, N extends Number & Comparable<N>> extends JPanel {
else {
selectionModel.setSelectionInterval(index, index);
}
timelineListeners.fire.itemActivated(index);
}
}
@ -481,8 +483,8 @@ public class TimelinePanel<T, N extends Number & Comparable<N>> extends JPanel {
protected final ItemTracker rows = new ItemTracker();
protected final List<TimelineTrack<T, N>> tracks = new ArrayList<>();
protected final Map<T, TimelineTrack<T, N>> trackMap = new HashMap<>();
protected final ListenerSet<TimelineViewRangeListener> viewRangeListeners =
new ListenerSet<>(TimelineViewRangeListener.class);
protected final ListenerSet<TimelineListener> timelineListeners =
new ListenerSet<>(TimelineListener.class);
protected final MouseListener mouseListener = new CellMouseListener();
protected final FocusListener focusListener = new CellFocusListener();
@ -497,12 +499,12 @@ public class TimelinePanel<T, N extends Number & Comparable<N>> extends JPanel {
setSelectionModel(new DefaultListSelectionModel());
}
public void addViewRangeListener(TimelineViewRangeListener listener) {
viewRangeListeners.add(listener);
public void addTimelineListener(TimelineListener listener) {
timelineListeners.add(listener);
}
public void removeViewRangeListener(TimelineViewRangeListener listener) {
viewRangeListeners.remove(listener);
public void removeTimelineListener(TimelineListener listener) {
timelineListeners.remove(listener);
}
protected Range<Double> computeViewRange() {
@ -765,7 +767,7 @@ public class TimelinePanel<T, N extends Number & Comparable<N>> extends JPanel {
track.setViewRange(newViewRange);
}
validate();
viewRangeListeners.fire.viewRangeChanged(newViewRange);
timelineListeners.fire.viewRangeChanged(newViewRange);
}
public Range<Double> getViewRange() {
@ -777,10 +779,8 @@ public class TimelinePanel<T, N extends Number & Comparable<N>> extends JPanel {
UIManager.getDefaults()
.entrySet()
.stream()
.filter(
ent -> cls.isInstance(ent.getValue()))
.forEach(
ent -> sorted.put(ent.getKey(), cls.cast(ent.getValue())));
.filter(ent -> cls.isInstance(ent.getValue()))
.forEach(ent -> sorted.put(ent.getKey(), cls.cast(ent.getValue())));
for (Entry<Object, C> ent : sorted.entrySet()) {
System.out.println(String.format("%s=%s", ent.getKey(), fmt.apply(ent.getValue())));
}