Skip to main content

Stop

Struct Stop 

Source
pub struct Stop<'m> { /* private fields */ }
Expand description

One instruction’s worth of standing still.

It borrows the machine for the length of the call and nothing longer, which is the whole of why the call is inverted. Every method answers with an owned snapshot, so a debugger keeps what it asks for and holds nothing of the machine.

The frame’s pc is truthful here: the dispatch loop syncs it before asking, which it does not do between instructions, and a view built anywhere else would name the instruction after the one being stopped at.

Implementations§

Source§

impl<'m> Stop<'m>

Source

pub fn instructions(&self) -> u64

How many instructions this task has run, this one included.

The count crate::Vm::instructions reports, read at the moment the instruction about to run was counted. It is this task’s, and Stop::task is what says whose.

Source

pub fn allocated_words(&self) -> u64

Words this task’s heap has handed out, reuse counted each time.

Read at the moment the instruction about to run was counted, so the difference between two consecutive stops is what the instruction between them allocated. That is how crate::vm::profile::Profiler attributes a heap to the code that asked for it: there is no seam inside Memory::alloc that knows which instruction it is serving, and a difference needs none.

Source

pub fn allocations(&self) -> u64

Objects this task’s heap has handed out, reuse counted each time.

Beside Stop::allocated_words because allocating often and allocating large are different faults with different repairs.

Source

pub fn task(&self) -> u64

Which task this stop is in.

Everything else a Stop answers is one task’s — the count, the depth, the frames — because a spawned task runs on a machine of its own, and until this was here a debugger could not tell two of them apart. A policy stated in frame depth is then a policy that a second task can satisfy by accident: a step asked in the entry, finished by an instruction of a task the entry spawned.

The number is crate::ENTRY_TASK for the entry and a spawned task’s own id otherwise, which is to say it is the number crate::trace’s events carry under task. That is deliberate and it is the whole of the choice here: a debugger and a trace of the same run must name the same task the same way, or a person holding both has to work out the correspondence themselves.

It is opaque. Two stops with the same id are the same task and two with different ids are not; nothing else about the number is promised, and no ordering of it means anything.

Source

pub fn function(&self) -> String

module.name of the function this stop is in.

The function the source would say it is in, which is not always the one whose frame the machine pushed: an instruction inside a body lower::inline expanded belongs to the body that was written, and a session that named the caller would tell a person their breakpoint had stopped somewhere they had not asked about. Function::inlined is what says otherwise, and Stop::function_id is deliberately the other answer.

A String, built here, because that is what a session prints and a name is what a person reads. A caller that will look the function up again — a profiler counting instructions, above all — wants Stop::function_id instead: this allocates, and a debugger that stops at every instruction would allocate at every instruction.

Source

pub fn function_id(&self) -> FunctionId

Which function this stop is in, as the program names it.

The identity rather than the name: two stops in one function answer the same id, and an id indexes Program::functions — so a caller can hold one per stop without holding a string per stop.

It is the machine’s answer and not the source’s: an instruction of an expanded body reports the function whose frame and whose code hold it, where Stop::function reports the body that was written. A profiler keys a count by this and prints function+pc, and a pc is a counter of the function this names; naming the callee there would make the pair disagree.

Source

pub fn pc(&self) -> u32

Which instruction of that function is about to run.

A counter of the function whose code holds it, which for an instruction inside an expanded body is the caller’s rather than the body’s: lower::inline wrote the body there and there is no other numbering. So a stop reported as m.inner at pc 1 is not m.inner’s second instruction, it is m.inner’s first, standing at m.outer’s counter 1. Stop::code numbers the same way, which is what keeps a listing and the pc beside it agreeing.

Source

pub fn span(&self) -> Span

Where that instruction was written.

Source

pub fn depth(&self) -> usize

How many calls are live, this one included.

An expanded body counts. lower::inline writes a small leaf’s instructions into its caller’s code and pushes no frame for them, and a debugger that counted only the frames the machine pushed would say a stop inside such a body was a stop in the caller — finish would run past the body it was asked to finish, next would step over nothing, and a backtrace would be one name short. Function::inlined is what says otherwise, and this is the one place the count comes from.

Counted rather than built, because State::wanted asks this at every stop of a stepping session and the frames themselves are only wanted when something is shown.

Source

pub fn backtrace(&self) -> Vec<Call>

Every live call, innermost first.

This renders every local of every frame — and every word of every local — so a debugger that stops at every instruction and takes a whole backtrace at each one is doing real work per instruction. Stop::frame is the same view of one call, for a session that only shows what it was asked for.

Source

pub fn frame(&self, at: usize) -> Option<Call>

The call at levels out from this one, or None past the outermost.

A frame that is not the innermost is suspended at the instruction after the call that led one level deeper, and that resume address is not where the frame is. The instruction it names is whatever runs next — frequently the next statement’s, or the return the body ends with — so a line built from it points away from the call as often as at it, and a name the call site had in scope may already have gone out of it. - 1 is always the call itself, because a pc is only ever synced after the instruction it dispatched.

This is the rule Machine::calls’s own documentation states for the error chain, which had made the same choice for the same reason and left this view alone. Issue #302 is what settled that the two should agree: with the copy after a call gone, the resume address in a one-expression body is the return, and a suspended frame answered that its caller’s locals were out of scope.

The innermost frame keeps its own pc. It is not suspended at a resume address — the machine is about to execute the instruction it names — so there is nothing to look back past.

Source

pub fn object(&self, at: u64) -> Option<Object>

What the word at names, if it names an object of this run’s heap.

The one place a raw word crosses, and it crosses inward: what comes back is a rendered snapshot. It is for the view VM development wants — a Word showed an address, and this says what is there — and it promises nothing about what a word means. A word that is not an object this memory holds answers None.

Source

pub fn code(&self, at: usize, reach: usize) -> Vec<Line>

The instructions around frame at’s pc, reach either side of it, or nothing past the outermost frame.

The disassembly a session shows beside a stop. cove_ir::print::one renders each, which is the same rendering cove ir prints, so a debugger and a dump do not disagree about what an instruction is called.

at names a frame the way Stop::frame names one, and for the same reason: a session that lets a person select a frame has to be able to show that frame’s code, and one that could only ever disassemble the innermost would answer frame 2 with frame 0’s instructions. It is a parameter here rather than a method on Call because a Call is an owned snapshot: giving it a disassembly would mean rendering every instruction of every live function at every stop, and a backtrace is already the expensive view.

The pc it reads is the one Stop::frame reports, which for a suspended frame is the call it is waiting on rather than the resume address after it. The two panes of a session are one view: a backtrace naming a line and a disassembly marking a different instruction would be the debugger disagreeing with itself.

Auto Trait Implementations§

§

impl<'m> !RefUnwindSafe for Stop<'m>

§

impl<'m> !UnwindSafe for Stop<'m>

§

impl<'m> Freeze for Stop<'m>

§

impl<'m> Send for Stop<'m>

§

impl<'m> Sync for Stop<'m>

§

impl<'m> Unpin for Stop<'m>

§

impl<'m> UnsafeUnpin for Stop<'m>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.