mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-617: Fixing stepping. Fixes for object tree.
This commit is contained in:
parent
97b43a4c4e
commit
3ae09277f0
40 changed files with 773 additions and 260 deletions
|
@ -41,7 +41,7 @@ public interface DebugClient extends DebugClientReentrant {
|
|||
STEP_INTO(true, ExecutionState.RUNNING, 5), //
|
||||
BREAK(false, ExecutionState.STOPPED, 0), //
|
||||
NO_DEBUGGEE(true, null, 1), // shouldWait is true to handle process creation
|
||||
STEP_BRANCH(true, null, 6), //
|
||||
STEP_BRANCH(true, ExecutionState.RUNNING, 6), //
|
||||
IGNORE_EVENT(false, null, 11), //
|
||||
RESTART_REQUESTED(true, null, 12), //
|
||||
REVERSE_GO(true, null, 0xff), //
|
||||
|
|
|
@ -45,7 +45,9 @@ public interface DbgManager extends AutoCloseable, DbgBreakpointInsertions {
|
|||
/** Equivalent to {@code stepi} in the CLI */
|
||||
STEP_INSTRUCTION("step-instruction"),
|
||||
/** Equivalent to {@code until} in the CLI */
|
||||
UNTIL("until"),;
|
||||
UNTIL("until"),
|
||||
/** Equivalent to {@code ext} in the CLI */
|
||||
EXTENDED("ext"),;
|
||||
|
||||
final String str;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
|||
|
||||
import agent.dbgeng.dbgeng.DebugProcessId;
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
import agent.dbgeng.manager.DbgManager.ExecSuffix;
|
||||
import agent.dbgeng.manager.impl.DbgSectionImpl;
|
||||
import ghidra.dbg.attributes.TypedTargetObjectRef;
|
||||
import ghidra.dbg.target.TargetAttachable;
|
||||
|
@ -182,6 +183,32 @@ public interface DbgProcess extends DbgMemoryOperations {
|
|||
*/
|
||||
CompletableFuture<Void> cont();
|
||||
|
||||
/**
|
||||
* Step the process
|
||||
*
|
||||
* Note that the command can complete before the process has finished stepping. The command
|
||||
* completes as soon as the process is running. A separate stop event is emitted when the step is
|
||||
* completed.
|
||||
*
|
||||
* @param suffix specifies how far to step, or on what conditions stepping ends.
|
||||
*
|
||||
* @return a future that completes once the process is running
|
||||
*/
|
||||
CompletableFuture<Void> step(ExecSuffix suffix);
|
||||
|
||||
/**
|
||||
* Step the process
|
||||
*
|
||||
* Note that the command can complete before the process has finished stepping. The command
|
||||
* completes as soon as the process is running. A separate stop event is emitted when the step is
|
||||
* completed.
|
||||
*
|
||||
* @param args specifies how far to step, or on what conditions stepping ends.
|
||||
*
|
||||
* @return a future that completes once the process is running
|
||||
*/
|
||||
CompletableFuture<Void> step(Map<String, ?> args);
|
||||
|
||||
/**
|
||||
* Evaluate an expression
|
||||
*
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package agent.dbgeng.manager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
|
@ -106,6 +107,19 @@ public interface DbgThread
|
|||
*/
|
||||
CompletableFuture<Void> step(ExecSuffix suffix);
|
||||
|
||||
/**
|
||||
* Step the thread
|
||||
*
|
||||
* Note that the command can complete before the thread has finished stepping. The command
|
||||
* completes as soon as the thread is running. A separate stop event is emitted when the step is
|
||||
* completed.
|
||||
*
|
||||
* @param args specifies how far to step, or on what conditions stepping ends.
|
||||
*
|
||||
* @return a future that completes once the thread is running
|
||||
*/
|
||||
CompletableFuture<Void> step(Map<String, ?> args);
|
||||
|
||||
/**
|
||||
* Detach from the entire process
|
||||
*
|
||||
|
|
|
@ -20,6 +20,7 @@ import agent.dbgeng.manager.DbgProcess;
|
|||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
|
||||
public class DbgProcessSelectCommand extends AbstractDbgCommand<Void> {
|
||||
|
||||
private DbgProcess process;
|
||||
|
||||
/**
|
||||
|
|
|
@ -55,7 +55,7 @@ public class DbgReadRegistersCommand extends AbstractDbgCommand<Map<DbgRegister,
|
|||
}
|
||||
}
|
||||
}
|
||||
so.setCurrentThreadId(previous);
|
||||
//so.setCurrentThreadId(previous);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,6 @@ public class DbgStackListFramesCommand extends AbstractDbgCommand<List<DbgStackF
|
|||
tf.Params[3].longValue());
|
||||
result.add(frame);
|
||||
}
|
||||
so.setCurrentThreadId(previous);
|
||||
//so.setCurrentThreadId(previous);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,25 +15,40 @@
|
|||
*/
|
||||
package agent.dbgeng.manager.cmd;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
|
||||
import java.util.Map;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugControl;
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
import agent.dbgeng.manager.DbgEvent;
|
||||
import agent.dbgeng.manager.DbgManager.ExecSuffix;
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import agent.dbgeng.manager.evt.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.manager.impl.DbgThreadImpl;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Implementation of {@link DbgThread#stepInstruction()}
|
||||
*/
|
||||
public class DbgStepCommand extends AbstractDbgCommand<Void> {
|
||||
protected final ExecSuffix suffix;
|
||||
|
||||
public DbgStepCommand(DbgManagerImpl manager, ExecSuffix suffix) {
|
||||
private DebugThreadId id;
|
||||
protected final ExecSuffix suffix;
|
||||
private String lastCommand = "tct";
|
||||
|
||||
public DbgStepCommand(DbgManagerImpl manager, DebugThreadId id, ExecSuffix suffix) {
|
||||
super(manager);
|
||||
this.id = id;
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public DbgStepCommand(DbgManagerImpl manager, DebugThreadId id, Map<String, ?> args) {
|
||||
super(manager);
|
||||
this.id = id;
|
||||
this.suffix = ExecSuffix.EXTENDED;
|
||||
this.lastCommand = (String) args.get("Command");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(DbgEvent<?> evt, DbgPendingCommand<?> pending) {
|
||||
if (evt instanceof AbstractDbgCompletedCommandEvent && pending.getCommand().equals(this)) {
|
||||
|
@ -48,17 +63,53 @@ public class DbgStepCommand extends AbstractDbgCommand<Void> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// NB: Would really prefer to do this through the API, but the API does
|
||||
// not appear to support freeze/unfreeze and suspend/resume thread. These appear
|
||||
// to be applied via the kernel32 API. Worse, the Windbg/KD API appears to lack
|
||||
// commands to query the freeze/suspend count for a given thread. Rather than
|
||||
// wrestle with the underlying API, we're going to just use the WIndbg commands.
|
||||
// Note that the thread-restricted form is used iff we're stepping a thread other
|
||||
// then the event thread.
|
||||
@Override
|
||||
public void invoke() {
|
||||
String cmd = "";
|
||||
String prefix = id == null ? "" : "~" + id.id + " ";
|
||||
DebugControl control = manager.getControl();
|
||||
if (suffix.equals(ExecSuffix.STEP_INSTRUCTION)) {
|
||||
control.setExecutionStatus(DebugStatus.STEP_INTO);
|
||||
cmd = "t";
|
||||
//control.setExecutionStatus(DebugStatus.STEP_INTO);
|
||||
}
|
||||
else if (suffix.equals(ExecSuffix.NEXT_INSTRUCTION)) {
|
||||
control.setExecutionStatus(DebugStatus.STEP_OVER);
|
||||
cmd = "p";
|
||||
//control.setExecutionStatus(DebugStatus.STEP_OVER);
|
||||
}
|
||||
else if (suffix.equals(ExecSuffix.FINISH)) {
|
||||
control.setExecutionStatus(DebugStatus.STEP_BRANCH);
|
||||
cmd = "gu";
|
||||
//control.setExecutionStatus(DebugStatus.STEP_BRANCH);
|
||||
}
|
||||
else if (suffix.equals(ExecSuffix.EXTENDED)) {
|
||||
cmd = getLastCommand();
|
||||
}
|
||||
DbgThreadImpl eventThread = manager.getEventThread();
|
||||
if (eventThread != null && eventThread.getId().equals(id)) {
|
||||
control.execute(cmd);
|
||||
}
|
||||
else {
|
||||
if (manager.isKernelMode()) {
|
||||
Msg.info(this, "Thread-specific stepping ignored in kernel-mode");
|
||||
control.execute(cmd);
|
||||
}
|
||||
else {
|
||||
control.execute(prefix + cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getLastCommand() {
|
||||
return lastCommand;
|
||||
}
|
||||
|
||||
public void setLastCommand(String lastCommand) {
|
||||
this.lastCommand = lastCommand;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* ###
|
||||
* 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.dbgeng.DebugControl;
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
|
||||
public class DbgThreadHoldCommand extends AbstractDbgCommand<Void> {
|
||||
|
||||
static final String FREEZE_ALL_THREADS_COMMAND = "~* f";
|
||||
static final String FREEZE_CURRENT_THREAD_COMMAND = "~. f";
|
||||
static final String UNFREEZE_CURRENT_THREAD_COMMAND = "~. u";
|
||||
static final String UNFREEZE_ALL_THREADS_COMMAND = "~* u";
|
||||
|
||||
static final String SUSPEND_ALL_THREADS_COMMAND = "~* n";
|
||||
static final String SUSPEND_CURRENT_THREAD_COMMAND = "~. n";
|
||||
static final String RESUME_CURRENT_THREAD_COMMAND = "~. m";
|
||||
static final String RESUME_ALL_THREADS_COMMAND = "~* m";
|
||||
|
||||
static final Boolean preferFreeze = true;
|
||||
|
||||
private DbgThread thread;
|
||||
private Boolean set;
|
||||
|
||||
/**
|
||||
* Select the given thread and frame level
|
||||
*
|
||||
* To simply select a thread, you should use frame 0 as the default.
|
||||
*
|
||||
* @param manager the manager to execute the command
|
||||
* @param thread the desired thread
|
||||
* @param set hold or release
|
||||
*/
|
||||
public DbgThreadHoldCommand(DbgManagerImpl manager, DbgThread thread, Boolean set) {
|
||||
super(manager);
|
||||
this.thread = thread;
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke() {
|
||||
DebugThreadId id = thread.getId();
|
||||
if (id != null) {
|
||||
manager.getSystemObjects().setCurrentThreadId(id);
|
||||
if (!manager.isKernelMode()) {
|
||||
DebugControl control = manager.getControl();
|
||||
if (preferFreeze) {
|
||||
control.execute(
|
||||
set ? FREEZE_CURRENT_THREAD_COMMAND : UNFREEZE_CURRENT_THREAD_COMMAND);
|
||||
}
|
||||
else {
|
||||
control.execute(
|
||||
set ? SUSPEND_CURRENT_THREAD_COMMAND : RESUME_CURRENT_THREAD_COMMAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import agent.dbgeng.manager.DbgThread;
|
|||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
|
||||
public class DbgThreadSelectCommand extends AbstractDbgCommand<Void> {
|
||||
|
||||
private DbgThread thread;
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,6 +66,6 @@ public class DbgWriteRegistersCommand extends AbstractDbgCommand<Void> {
|
|||
}
|
||||
}
|
||||
registers.setValues(DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE, values);
|
||||
so.setCurrentThreadId(previous);
|
||||
//so.setCurrentThreadId(previous);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.google.common.collect.RangeSet;
|
|||
import agent.dbgeng.dbgeng.*;
|
||||
import agent.dbgeng.dbgeng.DebugClient.DebugAttachFlags;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.DbgManager.ExecSuffix;
|
||||
import agent.dbgeng.manager.cmd.*;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.comm.util.BitmaskSet;
|
||||
|
@ -304,6 +305,24 @@ public class DbgProcessImpl implements DbgProcess {
|
|||
}).finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(ExecSuffix suffix) {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgStepCommand(manager, null, suffix)).handle(seq::exit);
|
||||
}).finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgStepCommand(manager, null, args)).handle(seq::exit);
|
||||
}).finish();
|
||||
}
|
||||
|
||||
protected <T> CompletableFuture<T> preferThread(
|
||||
Function<DbgThreadImpl, CompletableFuture<T>> viaThread,
|
||||
Supplier<CompletableFuture<T>> viaThis) {
|
||||
|
|
|
@ -220,7 +220,16 @@ public class DbgThreadImpl implements DbgThread {
|
|||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgStepCommand(manager, suffix)).handle(seq::exit);
|
||||
manager.execute(new DbgStepCommand(manager, id, suffix)).handle(seq::exit);
|
||||
}).finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgStepCommand(manager, id, args)).handle(seq::exit);
|
||||
}).finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
*/
|
||||
package agent.dbgeng.model.iface1;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.manager.DbgManager.ExecSuffix;
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetObject;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetSteppable;
|
||||
|
||||
/**
|
||||
|
@ -48,6 +49,8 @@ public interface DbgModelTargetSteppable<T extends TargetSteppable<T>>
|
|||
return ExecSuffix.RETURN;
|
||||
case UNTIL:
|
||||
return ExecSuffix.UNTIL;
|
||||
case EXTENDED:
|
||||
return ExecSuffix.EXTENDED;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
@ -62,7 +65,21 @@ public interface DbgModelTargetSteppable<T extends TargetSteppable<T>>
|
|||
case ADVANCE: // Why no exec-advance in dbgeng?
|
||||
return thread.console("advance");
|
||||
default:
|
||||
if (this instanceof DbgModelTargetThread) {
|
||||
DbgModelTargetThread targetThread = (DbgModelTargetThread) this;
|
||||
return targetThread.getThread().step(convertToDbg(kind));
|
||||
}
|
||||
if (this instanceof DbgModelTargetProcess) {
|
||||
DbgModelTargetProcess targetProcess = (DbgModelTargetProcess) this;
|
||||
return targetProcess.getProcess().step(convertToDbg(kind));
|
||||
}
|
||||
return thread.step(convertToDbg(kind));
|
||||
}
|
||||
}
|
||||
|
||||
default CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
DbgThread thread = getManager().getCurrentThread();
|
||||
return thread.step(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -180,17 +180,21 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(TargetStepKind kind) {
|
||||
DbgThread thread = getManager().getCurrentThread();
|
||||
switch (kind) {
|
||||
case SKIP:
|
||||
throw new UnsupportedOperationException(kind.name());
|
||||
case ADVANCE: // Why no exec-advance in dbgeng?
|
||||
return thread.console("advance");
|
||||
throw new UnsupportedOperationException(kind.name());
|
||||
default:
|
||||
return thread.step(convertToDbg(kind));
|
||||
return process.step(convertToDbg(kind));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return process.step(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processStarted(Long pid) {
|
||||
if (pid != null) {
|
||||
|
|
|
@ -87,9 +87,24 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
|||
boolean doFire;
|
||||
synchronized (this) {
|
||||
doFire = !Objects.equals(this.focus, sel);
|
||||
this.focus = sel;
|
||||
if (doFire && focus != null) {
|
||||
List<String> focusPath = focus.getPath();
|
||||
List<String> selPath = sel.getPath();
|
||||
for (int i = 0; i < focusPath.size(); i++) {
|
||||
if (i >= selPath.size()) {
|
||||
doFire = false;
|
||||
break;
|
||||
}
|
||||
if (!focusPath.get(i).equals(selPath.get(i))) {
|
||||
doFire = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//doFire = !focusPath.containsAll(selPath);
|
||||
}
|
||||
}
|
||||
if (doFire) {
|
||||
this.focus = sel;
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus //
|
||||
), "Focus changed");
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.DbgManager.ExecSuffix;
|
||||
import agent.dbgeng.manager.cmd.DbgThreadSelectCommand;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
|
||||
|
@ -46,8 +45,14 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
|||
implements DbgModelTargetThread {
|
||||
|
||||
protected static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of( //
|
||||
TargetStepKind.ADVANCE, TargetStepKind.FINISH, TargetStepKind.LINE, TargetStepKind.OVER,
|
||||
TargetStepKind.OVER_LINE, TargetStepKind.RETURN, TargetStepKind.UNTIL);
|
||||
TargetStepKind.ADVANCE, //
|
||||
TargetStepKind.FINISH, //
|
||||
TargetStepKind.LINE, //
|
||||
TargetStepKind.OVER, //
|
||||
TargetStepKind.OVER_LINE, //
|
||||
TargetStepKind.RETURN, //
|
||||
TargetStepKind.UNTIL, //
|
||||
TargetStepKind.EXTENDED);
|
||||
|
||||
protected static String indexThread(DebugThreadId debugThreadId) {
|
||||
return PathUtils.makeIndex(debugThreadId.id);
|
||||
|
@ -117,33 +122,12 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
|||
TargetExecutionState targetState = convertState(state);
|
||||
String executionType = thread.getExecutingProcessorType().description;
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
STATE_ATTRIBUTE_NAME, targetState, //
|
||||
TargetEnvironment.ARCH_ATTRIBUTE_NAME, executionType //
|
||||
), reason.desc());
|
||||
setExecutionState(targetState, reason.desc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecSuffix convertToDbg(TargetStepKind kind) {
|
||||
switch (kind) {
|
||||
case FINISH:
|
||||
return ExecSuffix.FINISH;
|
||||
case INTO:
|
||||
return ExecSuffix.STEP_INSTRUCTION;
|
||||
case LINE:
|
||||
return ExecSuffix.STEP;
|
||||
case OVER:
|
||||
return ExecSuffix.NEXT_INSTRUCTION;
|
||||
case OVER_LINE:
|
||||
return ExecSuffix.NEXT;
|
||||
case RETURN:
|
||||
return ExecSuffix.RETURN;
|
||||
case UNTIL:
|
||||
return ExecSuffix.UNTIL;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(TargetStepKind kind) {
|
||||
switch (kind) {
|
||||
|
@ -156,6 +140,11 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return thread.step(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> select() {
|
||||
DbgManagerImpl manager = getManager();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue