Skip to content

kern: stack watermark support#1956

Open
cbiffle wants to merge 3 commits intooxidecomputer:masterfrom
cbiffle:cbiffle/stack-watermark
Open

kern: stack watermark support#1956
cbiffle wants to merge 3 commits intooxidecomputer:masterfrom
cbiffle:cbiffle/stack-watermark

Conversation

@cbiffle
Copy link
Copy Markdown
Collaborator

@cbiffle cbiffle commented Dec 16, 2024

Each task has grown a new field, low_stack_pointer, that tracks the lowest stack pointer value seen so far (because stacks grow down). We update this value on any kernel entry (interrupt, timer, fault, syscall), so while we will miss very brief excursions of stack between syscalls or interrupts, we should still get a useful value.

In addition, the field past_low_stack_pointer tracks the lowest stack pointer value across all previous incarnations of the task. This way if the task is periodically restarting, we can still get useful stack stats, including at stack overflow.

All of this stuff is conditional on the stack-watermark feature, since it increases the kernel RAM requirement, which is explicit in Oxide's Hubris build system.

@cbiffle cbiffle requested review from hawkw and mkeeter December 16, 2024 20:25
Comment thread sys/kern/src/arch/arm_m.rs Outdated
let current = CURRENT_TASK_PTR.load(Ordering::Relaxed);
uassert!(!current.is_null()); // irq before kernel started?

let current_prio = {

This comment was marked as resolved.

Copy link
Copy Markdown
Member

@hawkw hawkw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, very straightforward! Looks good to me once Clippy is made happy again.


// We don't need the current task pointer in this routine, but we'll access
// it briefly to update the stack watermark:
{
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if this whole block should be conditional on the feature flag? I see that update_stack_watermark is a nop if the feature isn't enabled, and the compiler is probably smart enough to eliminate the CURRENT_TASK_PTR access when the function that's called on it doesn't do anything, but... I just kinda don't trust it...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to make as little code conditional as possible, so that it always gets compiled; this is why it's only the body of update_watermark that's cfg'd. The compiler does appear to optimize this load out, fwiw.

Comment thread sys/kern/src/task.rs Outdated
/// restarted.
descriptor: &'static TaskDesc,

/// Tracks the lowest stack pointer value (e.g. fullest stack) observed on
Copy link
Copy Markdown
Collaborator

@mkeeter mkeeter Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vibes – I'd vote to put this into a separate object with helper functions, e.g.

#[cfg(feature = "stack-watermark")]
struct StackWatermark {
    /// Tracks the lowest stack pointer value (e.g. fullest stack) observed on
    /// any kernel entry for this instance of this task.
    ///
    /// Initialized to `u32::MAX` if the task has not yet run.
    #[cfg(feature = "stack-watermark")]
    stack_pointer_low: u32,

    /// Tracks the lowest stack pointer value (e.g. fullest stack) observed on
    /// any kernel entry across *any* instance of this task.
    ///
    /// Initialized to `u32::MAX` if the task has not yet run.
    #[cfg(feature = "stack-watermark")]
    past_stack_pointer_low: u32,
}

#[cfg(feature = "stack-watermark")]
impl Default for StackWatermark {
    fn default() -> Self {
        StackWatermark {
            stack_pointer_low: u32::MAX,
            past_stack_pointer_low: u32::MAX,
        }
    }
}

#[cfg(feature = "stack-watermark")]
impl StackWatermark {
    fn reinitialize() {
        self.past_stack_pointer_low =
            u32::min(self.past_stack_pointer_low, self.stack_pointer_low);
        self.stack_pointer_low = u32::MAX;
    }

    fn update(&mut self, stack_pointer: u32) {
        self.stack_pointer_low =
            u32::min(self.stack_pointer_low, self.save().stack_pointer());
    }
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sold.

Comment thread sys/kern/src/arch/arm_m.rs Outdated
};

let current = CURRENT_TASK_PTR.load(Ordering::Relaxed);
uassert!(!current.is_null()); // irq before kernel started?
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: why is this chunk of code different from the diff above, with the uassert! and no safety comment?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Probably some historical reason and/or author-in-a-hurry. I'll take a pass over it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I have rearranged the code and added more explanatory comments. Will post the new commits shortly.

cbiffle and others added 3 commits January 16, 2025 12:06
Each task has grown a new field, `low_stack_pointer`, that tracks the
lowest stack pointer value seen so far (because stacks grow down). We
update this value on any kernel entry (interrupt, timer, fault,
syscall), so while we will miss very brief excursions of stack _between_
syscalls or interrupts, we should still get a useful value.

In addition, the field `past_low_stack_pointer` tracks the lowest stack
pointer value across _all previous incarnations_ of the task. This way
if the task is periodically restarting, we can still get useful stack
stats, including at stack overflow.
@cbiffle cbiffle force-pushed the cbiffle/stack-watermark branch from b885386 to 5b7b031 Compare January 16, 2025 20:21
@cbiffle
Copy link
Copy Markdown
Collaborator Author

cbiffle commented Jan 16, 2025

Alright, pushed fixes for your comments, PTAL!

@cbiffle cbiffle requested review from aapoalas, hawkw and mkeeter January 16, 2025 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants