Skip to content

Commit

Permalink
Backtrace support, part 1: Use StackChain to represent chains of stac…
Browse files Browse the repository at this point in the history
…ks (#98)

This is the first PR in a series to add support for generating
backtraces in the presence of stack switching.

Currently, the `VMContext` contains a (possibly `null`) pointer to the
currently running continuation. Every `ContinuationObject` in turn
contains a (possibly `null`) pointer to its parent. This effectively
yields a linked list of continuations.

This PR changes this situation in two ways. See the comment on
`wasmtime_continuations::StackChain` for more details.
1. Instead of a (possibly `null`) pointer to a `ContinuationObject`),
both `VMContext` and `ContinuationObject` now contain an object of type
`StackChain` to represent their "parent". This is still effectively a
linked list of stacks.
2. However, by using the new `StackChain` type, it is now possible to
have an explicit representation of the main stack at the end of the list
of active continuations. In other words, a `StackChain` linked list now
ends with a `MainStack` variant.

In addition, we now associate a `StackLimits` object with each element
in the `StackChain` (including the main stack). This will be used in
subsequent PRs to store a subset of the values in `VMRuntimeLimits` for
each stack.
  • Loading branch information
frank-emrich authored Feb 14, 2024
1 parent b37fc76 commit ceb9967
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 146 deletions.
3 changes: 2 additions & 1 deletion cranelift/wasm/src/environ/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,8 @@ pub trait FuncEnvironment: TargetEnvironment {
/// TODO
fn tag_returns(&self, tag_index: u32) -> &[wasmtime_types::WasmValType];

/// TODO
/// Returns a pointer to the currently running continuation object.
/// Traps if not currently running inside a continuation.
fn typed_continuations_load_continuation_object(
&mut self,
builder: &mut FunctionBuilder,
Expand Down
101 changes: 98 additions & 3 deletions crates/continuations/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,87 @@ pub mod types {

pub type ContinuationFiber = Fiber<'static, (), u32, ()>;

/// This type is used to save (and subsequently restore) a subset of the data in
/// `VMRuntimeLimits`. See documentation of `StackChain` for the exact uses.
#[repr(C)]
#[derive(Debug, Clone)]
pub struct StackLimits {
pub stack_limit: usize,
pub last_wasm_exit_fp: usize,
pub last_wasm_exit_pc: usize,
pub last_wasm_entry_sp: usize,
}

impl Default for StackLimits {
fn default() -> Self {
Self {
stack_limit: 0,
last_wasm_exit_fp: 0,
last_wasm_exit_pc: 0,
last_wasm_entry_sp: 0,
}
}
}

impl StackLimits {
pub fn with_stack_limit(stack_limit: usize) -> Self {
Self {
stack_limit,
..Default::default()
}
}
}

const STACK_CHAIN_ABSENT_DISCRIMINANT: usize = 0;
const STACK_CHAIN_MAIN_STACK_DISCRIMINANT: usize = 1;
const STACK_CHAIN_CONTINUATION_DISCRIMINANT: usize = 2;

/// This type represents a linked lists of stacks, additionally associating a
/// `StackLimits` object with each element of the list. Here, a "stack" is
/// either a continuation or the main stack. Note that the linked list character
/// arises from the fact that `StackChain::Continuation` variants have a pointer
/// to have `ContinuationObject`, which in turn has a `parent_chain` value of
/// type `StackChain`.
///
/// There are generally two uses of such chains:
///
/// 1. The `typed_continuations_chain` field in the VMContext contains such a
/// chain of stacks, where the head of the list denotes the stack that is
/// currently executing (either a continuation or the main stack), as well as
/// the parent stacks, in case of a continuation currently running. Note that in
/// this case, the linked list must contains 0 or more `Continuation` elements,
/// followed by a final `MainStack` element. In particular, this list always
/// ends with `MainStack` and never contains an `Absent` variant.
///
/// 2. When a continuation is suspended, its chain of parents eventually ends
/// with an `Absent` variant in its `parent_chain` field. Note that a suspended
/// continuation never appears in the stack chain in the VMContext!
///
///
/// As mentioned before, each stack in a `StackChain` has a corresponding
/// `StackLimits` object. For continuations, this is stored in the `limits`
/// fields of the corresponding `ContinuationObject`. For the main stack, the
/// `MainStack` variant contains a pointer to the
/// `typed_continuations_main_stack_limits` field of the VMContext.
// FIXME(frank-emrich) Note that the data within the StackLimits objects is
// currently not used or updated in any way.
#[derive(Debug, Clone, PartialEq)]
#[repr(usize, C)]
pub enum StackChain {
/// If stored in the VMContext, used to indicate that the MainStack entry
/// has not been set, yet. If stored in a ContinuationObject's parent_chain
/// field, means that there is currently no parent.
Absent = STACK_CHAIN_ABSENT_DISCRIMINANT,
MainStack(*mut StackLimits) = STACK_CHAIN_MAIN_STACK_DISCRIMINANT,
Continuation(*mut ContinuationObject) = STACK_CHAIN_CONTINUATION_DISCRIMINANT,
}

impl StackChain {
pub const ABSENT_DISCRIMINANT: usize = STACK_CHAIN_ABSENT_DISCRIMINANT;
pub const MAIN_STACK_DISCRIMINANT: usize = STACK_CHAIN_MAIN_STACK_DISCRIMINANT;
pub const CONTINUATION_DISCRIMINANT: usize = STACK_CHAIN_CONTINUATION_DISCRIMINANT;
}

pub struct Payloads {
/// Number of currently occupied slots.
pub length: types::payloads::Length,
Expand Down Expand Up @@ -92,7 +173,9 @@ impl From<State> for i32 {
/// TODO
#[repr(C)]
pub struct ContinuationObject {
pub parent: *mut ContinuationObject,
pub limits: StackLimits,

pub parent_chain: StackChain,

pub fiber: *mut ContinuationFiber,

Expand Down Expand Up @@ -137,13 +220,25 @@ pub mod offsets {
use crate::ContinuationObject;
use memoffset::offset_of;

/// Offset of `limits` field
pub const LIMITS: i32 = offset_of!(ContinuationObject, limits) as i32;
/// Offset of `args` field
pub const ARGS: i32 = offset_of!(ContinuationObject, args) as i32;
/// Offset of `parent` field
pub const PARENT: i32 = offset_of!(ContinuationObject, parent) as i32;
/// Offset of `parent_chain` field
pub const PARENT_CHAIN: i32 = offset_of!(ContinuationObject, parent_chain) as i32;
/// Offset of `state` field
pub const STATE: i32 = offset_of!(ContinuationObject, state) as i32;
/// Offset of `tag_return_values` field
pub const TAG_RETURN_VALUES: i32 = offset_of!(ContinuationObject, tag_return_values) as i32;
}

pub mod stack_limits {
use crate::StackLimits;
use memoffset::offset_of;

pub const STACK_LIMIT: i32 = offset_of!(StackLimits, stack_limit) as i32;
pub const LAST_WASM_EXIT_FP: i32 = offset_of!(StackLimits, last_wasm_exit_fp) as i32;
pub const LAST_WASM_EXIT_PC: i32 = offset_of!(StackLimits, last_wasm_exit_pc) as i32;
pub const LAST_WASM_ENTRY_SP: i32 = offset_of!(StackLimits, last_wasm_entry_sp) as i32;
}
}
Loading

0 comments on commit ceb9967

Please sign in to comment.