class ThreadController

Defined at line 70 of file ../../src/developer/debug/zxdb/client/thread_controller.h

Abstract base class that provides the policy decisions for various types of thread stepping.

HOW THREAD CONTROLLERS WORK

---------------------------

Thread controllers are responsible for implementing the various complex step operations that

are more complex that run/stop/single-step-instruction. They are composable ("next" is just a

sequence of "step into"/"step out" operations until a new line is reached) and there can be

multiple active ones (if a breakpoint is hit in a stack frame being stepped over, the stepping

can continue after the breakpoint is resumed from).

Once installed, the thread will ask the topmost thread controller how (and whether) to continue

via OnThreadStop(). This function is given the exception and breakpoint information regarding the

stop. The thread controllers installed on a thread will get notified for each exception and

indicate whether they want to handle the stop or continue. Each thread controller is queried for

each stop since completions could happen in the in any order.

The thread may also delete thread controllers. This can happen when the thread is terminated or

when there is an internal error stepping. If a controller has a callback it executes on

completion it should be prepared to issue the callback from its destructor in such a way to

indicate that the step operation failed.

"NONE" EXCEPTION TYPES

----------------------

The special exception type "kNone" should cause a thread controller to evaluate the current

state of the thread without making assumptions about the exact exception type. This is most

commonly used when a controller makes a child controller to perform some operation and wants to

immediately ask if it the thread should stop now. The current exception might be a breakpoint or

something that the parent controller set up that the child controller might otherwise ignore.

ASYNC COMPLETION

----------------

Some thread controller need to perform async operations from OnThreadStop(). In this case they

can return StopOp::kFuture. The thread will interpret this to mean leave the thread stopped but

not to issue notifications that it has done so. The thread controller is responsible for calling

Thread::ResumeFromAsyncThreadController() once its operation has completed.

Thread::ResumeFromAsyncThreadController() doesn't continue the thread (since the async operation

may want to report "stop"). Instead, it re-issues the same stop and the controllers should then

re-evaluate their location and issue a real stop or continue. See also

ThreadController::MakeResumeAsyncThreadCallback() for some extra complications.

There is also some opportunity for asynchronous work via the Thread's AddPostStopTask() function.

This can inject asynchronous work after the thread controllers run but before the stop or

continue is processed.

Public Methods

void ThreadController (fit::deferred_callback on_done)

The deferred callback is executed when this step controller has completed its work. To a first

approximation, this is when the step is complete.

BUT when a step is complete may not be obvious or well-defined. The user could step over

"MessageLoop::Run()" which might never complete. If you run "until", then hit a breakpoint, and

step from there, the "until" controller will still be pending even though other step operations

have been executed. The user can also type Control-C to clear all the current stepping state

which can clear the operation before it's conceptually complete.

Note that this is different than the callback on InitWithThread().

Defined at line 51 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

void ~ThreadController ()

Defined at line 53 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

void InitWithThread (Thread * thread, fit::callback<void (const Err &)> cb)

Registers the thread with the controller. The controller will be owned by the thread (possibly

indirectly) so the pointer will remain valid for the rest of the lifetime of the controller.

The implementation should call SetThread() with the thread.

When the implementation is ready, it will issue the given callback to run the thread. The

callback can be issued reentrantly from inside this function if the controller is ready

or fails synchronously.

If the callback does not specify an error, the thread will be resumed when it is called. If the

callback has an error, it will be reported and the thread will remain stopped.

The callback indicates that the initialization has completed, not that the thread controller

has completed. For controller completion, see the constructor.

ContinueOp GetContinueOp ()

Returns how to continue the thread when running this controller. This will be called after

InitWithThread and after every subsequent kContinue response from OnThreadStop to see how the

controller wishes to run.

A thread controller can return a "synthetic stop" from this function which will schedule an

OnThreadStop() call in the future without running the thread. This can be used to adjust the

ambiguous inline stack state (see Stack object) to implement step commands.

GetContinueOp() should not change thread state and controllers should be prepared for only

InitWithThread() followe by OnThreadStop() calls. When thread controllers embed other thread

controllers, the embedding controller may create the nested one and want it to evaluate the

current stop, and this happens without ever continuing.

StopOp OnThreadStop (debug_ipc::ExceptionType stop_type, const std::vector<fxl::WeakPtr<Breakpoint>> & hit_breakpoints)

Notification that the thread has stopped. The return value indicates what the thread should do

in response.

At this call, the stop location will be thread().GetStack()[0]. Thread controllers will only

be called when there is a valid location for the stop, so there is guaranteed to be at least

one stack entry (in constrast to general thread exception observers).

ARGUMENTS

---------

The exception type may be "kNone" if the exception type shouldn't matter to this controller.

Controllers should treak "kNone" as being relevant to themselves. When a controller is used as

a component of another controller, the exception type may have been "consumed" and a nested

controller merely needs to evaluate its opinion of the current location.

The stop type and breakpoint information should be passed to the first thread controller that

handles the stop (this might be a sub controller if a controller is delegating the current

execution to another one). Other controllers that might handle the stop (say, if a second

sub-controller is created when the first one is done) don't care and might get confused by stop

information originally handled by another one. In this second case, "kNone" and an empty

breakpoint list should be sent to OnThreadStop().

RETURN VALUE

------------

If the ThreadController returns |kStop|, its assumed the controller has completed its job and

it will be deleted. |kContinue| doesn't necessarily mean the thread will continue, as there

could be multiple controllers active and any of them can report "stop". When a thread is being

continued, the main controller will get GetContinueOp() called to see what type of continuation

it wants.

void Log (const char * format)

Writes the log message prefixed with the thread controller type. Callers should pass constant

strings through here so the Log function takes almost no time if it's disabled: in the future

we may want to make this run-time enable-able

Defined at line 55 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

std::string FrameFunctionNameForLog (const Frame * frame)

Returns the given frame's function name or a placeholder string if unavailable. Does nothing if

logging is disabled (computing this is non-trivial).

Defined at line 74 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

Protected Methods

void SetThread (Thread * thread)

Defined at line 88 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

const char * GetName ()

Returns the name of this thread controller. This will be visible in logs. This should be

something simple and short like "Step" or "Step Over".

void SetInlineFrameIfAmbiguous (InlineFrameIs comparison, FrameFingerprint fingerprint)

The beginning of an inline function is ambiguous about whether you're at the beginning of the

function or about to call it (see Stack object for more).

Many stepping functions know what frame they think they should be in, and identify this based

on the frame fingerprint. As a concrete example, if a "finish" command exits a stack frame, but

the next instruction is the beginning of an inlined function, the "finish" controller would

like to say you're in the stack it returned to, not the inlined function.

This function checks if there is ambiguity of inline frames and whether one of those ambiguous

frames matches the given fingerprint. In this case, it will set the top stack frame to be the

requested one.

If there is no ambiguity or one of the possibly ambiguous frames doesn't match the given

fingerprint, the inline frame hide count will be unchanged.

Defined at line 93 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

void NotifyControllerDone ()

Tells the owner of this class that this ThreadController has completed its work. Normally

returning kStop from OnThreadStop() will do this, but if the controller has another way to get

events (like breakpoints), it may notice out-of-band that its work is done.

This function will likely cause |this| to be deleted.

Defined at line 142 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

ResumeAsyncCallbackInfo MakeResumeAsyncThreadCallback (debug_ipc::ExceptionType type)

Makes a callback that calls the current thread ResumeFromAsyncThreadController() function to

resume from a previous "kFuture" stop operation. This is a convenience function to deal with

some delicacies including weak Thread pointers and the sync/async issue described below.

The Err parameter to the callback is ignored, we use this function type to match the callback

to InitWithThread().

The type parameter is passed to ResumeFromAsyncThreadController().

SYNC/ASYNC ISSUES

-----------------

The normal sync pattern is to make a new ThreadController, call its InitWithThread(), and

then send it a "kNone" exception if you need to ask it about the current location. The problem

is the callback may:

- Complete synchronously from within InitWithThread(), in which case you want to immediately

do e.g. "return controller->OnThreadStop(kNone)") and do nothing from the callback.

- Complete asynchronously in which case you want to return kFuture to the Thread and have the

callback issue ResumeFromAsyncThreadController() when it completes.

This function makes a ResumeAsyncCallbackInfo which implements this behavior. The callback will

only call ResumeFromAsyncThreadController() if it is issued after the ResumeAsyncCallbackInfo

is destructed.

Typical use:

sub_controller_ = std::make_unique

<MyController

>();

ResumeAsyncCallbackInfo resume_info =

MakeResumeAsyncThreadCallback(debug_ipc::ExceptionType::kNone);

sub_controller_->InitWithThread(std::move(resume_info.callback));

return resume_info.ForwardStopOrReturnFuture(sub_controller_.get(), hit_bps);

Defined at line 147 of file ../../src/developer/debug/zxdb/client/thread_controller.cc

Thread * thread ()

Defined at line 258 of file ../../src/developer/debug/zxdb/client/thread_controller.h

bool enable_debug_logging ()

Returns true if this controller has debug logging enabled. This is only valid after the thread

has been set.

Defined at line 325 of file ../../src/developer/debug/zxdb/client/thread_controller.h

Enumerations

enum StopOp
Name Value
kContinue 0
kStopDone 1
kUnexpected 2
kFuture 3

Defined at line 72 of file ../../src/developer/debug/zxdb/client/thread_controller.h

enum InlineFrameIs
Name Value
kEqual 0
kOneBefore 1

How the frame argument to SetInlineFrameIfAmbiguous() is interpreted.

Defined at line 247 of file ../../src/developer/debug/zxdb/client/thread_controller.h

Records