bevy/crates/bevy_ecs/src/schedule/executor/simple.rs
SpecificProtagonist aea9b4a9e4
Simplified backtraces (#12305)
# Objective

Remove Bevy internals from backtraces

## Solution

Executors insert `__rust_begin_short_backtrace` into the callstack
before running a system.

<details>
<summary>Example current output</summary>

```
thread 'Compute Task Pool (3)' panicked at src/main.rs:7:33:
Foo
stack backtrace:
   0: rust_begin_unwind
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:647:5
   1: core::panicking::panic_fmt
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panicking.rs:72:14
   2: foo::main::{{closure}}
             at ./src/main.rs:7:33
   3: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:294:13
   4: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run::call_inner
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:661:21
   5: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:664:17
   6: <bevy_ecs::system::function_system::FunctionSystem<Marker,F> as bevy_ecs::system::system::System>::run_unsafe
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:504:19
   7: bevy_ecs::schedule::executor::multi_threaded::ExecutorState::spawn_system_task::{{closure}}::{{closure}}
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs:621:26
   8: core::ops::function::FnOnce::call_once
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:250:5
   9: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:272:9
  10: std::panicking::try::do_call
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40
  11: __rust_try
  12: std::panicking::try
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19
  13: std::panic::catch_unwind
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14
  14: bevy_ecs::schedule::executor::multi_threaded::ExecutorState::spawn_system_task::{{closure}}
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs:614:23
  15: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::future::future::Future>::poll
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:297:9
  16: <futures_lite::future::CatchUnwind<F> as core::future::future::Future>::poll::{{closure}}
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:588:42
  17: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:272:9
  18: std::panicking::try::do_call
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40
  19: __rust_try
  20: std::panicking::try
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19
  21: std::panic::catch_unwind
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14
  22: <futures_lite::future::CatchUnwind<F> as core::future::future::Future>::poll
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:588:9
  23: async_executor::Executor::spawn::{{closure}}
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.8.0/src/lib.rs:158:20
  24: async_task::raw::RawTask<F,T,S,M>::run::{{closure}}
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.0/src/raw.rs:550:21
  25: core::ops::function::FnOnce::call_once
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:250:5
  26: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:272:9
  27: std::panicking::try::do_call
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40
  28: __rust_try
  29: std::panicking::try
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19
  30: std::panic::catch_unwind
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14
  31: async_task::raw::RawTask<F,T,S,M>::run
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.0/src/raw.rs:549:23
  32: async_task::runnable::Runnable<M>::run
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.0/src/runnable.rs:781:18
  33: async_executor::Executor::run::{{closure}}::{{closure}}
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.8.0/src/lib.rs:254:21
  34: <futures_lite::future::Or<F1,F2> as core::future::future::Future>::poll
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:449:33
  35: async_executor::Executor::run::{{closure}}
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.8.0/src/lib.rs:261:32
  36: futures_lite::future::block_on::{{closure}}
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:99:19
  37: std:🧵:local::LocalKey<T>::try_with
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:286:16
  38: std:🧵:local::LocalKey<T>::with
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:262:9
  39: futures_lite::future::block_on
             at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:78:5
  40: bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}::{{closure}}::{{closure}}
             at /home/vj/workspace/rust/bevy/crates/bevy_tasks/src/task_pool.rs:180:37
  41: std::panicking::try::do_call
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40
  42: __rust_try
  43: std::panicking::try
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19
  44: std::panic::catch_unwind
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14
  45: bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}::{{closure}}
             at /home/vj/workspace/rust/bevy/crates/bevy_tasks/src/task_pool.rs:174:43
  46: std:🧵:local::LocalKey<T>::try_with
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:286:16
  47: std:🧵:local::LocalKey<T>::with
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:262:9
  48: bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}
             at /home/vj/workspace/rust/bevy/crates/bevy_tasks/src/task_pool.rs:167:25
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Encountered a panic in system `foo::main::{{closure}}`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
get on your knees and beg mommy for forgiveness you pervert~ 💖
```
</details>

<details>
<summary>Example output with this PR</summary>

```
Panic at src/main.rs:7:33:
Foo
stack backtrace:
   0: rust_begin_unwind
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:647:5
   1: core::panicking::panic_fmt
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panicking.rs:72:14
   2: foo::main::{{closure}}
             at ./src/main.rs:7:59
   3: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
             at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:294:13
   4: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run::call_inner
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:661:21
   5: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:664:17
   6: <bevy_ecs::system::function_system::FunctionSystem<Marker,F> as bevy_ecs::system::system::System>::run_unsafe
             at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:504:19
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Encountered a panic in system `foo::main::{{closure}}`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
```
</details>

Full backtraces (`RUST_BACKTRACE=full`) are unchanged.

## Alternative solutions

Write a custom panic hook. This could potentially let use exclude a few
more callstack frames but requires a dependency on `backtrace` and is
incompatible with user-provided panic hooks.

---

## Changelog

- Backtraces now exclude many Bevy internals (unless
`RUST_BACKTRACE=full` is used)

---------

Co-authored-by: James Liu <contact@jamessliu.com>
2024-03-10 12:18:59 +00:00

147 lines
5.0 KiB
Rust

#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use fixedbitset::FixedBitSet;
use std::panic::AssertUnwindSafe;
use crate::{
schedule::{
executor::is_apply_deferred, BoxedCondition, ExecutorKind, SystemExecutor, SystemSchedule,
},
world::World,
};
use super::__rust_begin_short_backtrace;
/// A variant of [`SingleThreadedExecutor`](crate::schedule::SingleThreadedExecutor) that calls
/// [`apply_deferred`](crate::system::System::apply_deferred) immediately after running each system.
#[derive(Default)]
pub struct SimpleExecutor {
/// Systems sets whose conditions have been evaluated.
evaluated_sets: FixedBitSet,
/// Systems that have run or been skipped.
completed_systems: FixedBitSet,
}
impl SystemExecutor for SimpleExecutor {
fn kind(&self) -> ExecutorKind {
ExecutorKind::Simple
}
fn init(&mut self, schedule: &SystemSchedule) {
let sys_count = schedule.system_ids.len();
let set_count = schedule.set_ids.len();
self.evaluated_sets = FixedBitSet::with_capacity(set_count);
self.completed_systems = FixedBitSet::with_capacity(sys_count);
}
fn run(
&mut self,
schedule: &mut SystemSchedule,
world: &mut World,
_skip_systems: Option<&FixedBitSet>,
) {
// If stepping is enabled, make sure we skip those systems that should
// not be run.
#[cfg(feature = "bevy_debug_stepping")]
if let Some(skipped_systems) = _skip_systems {
// mark skipped systems as completed
self.completed_systems |= skipped_systems;
}
for system_index in 0..schedule.systems.len() {
#[cfg(feature = "trace")]
let name = schedule.systems[system_index].name();
#[cfg(feature = "trace")]
let should_run_span = info_span!("check_conditions", name = &*name).entered();
let mut should_run = !self.completed_systems.contains(system_index);
for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() {
if self.evaluated_sets.contains(set_idx) {
continue;
}
// evaluate system set's conditions
let set_conditions_met =
evaluate_and_fold_conditions(&mut schedule.set_conditions[set_idx], world);
if !set_conditions_met {
self.completed_systems
.union_with(&schedule.systems_in_sets_with_conditions[set_idx]);
}
should_run &= set_conditions_met;
self.evaluated_sets.insert(set_idx);
}
// evaluate system's conditions
let system_conditions_met =
evaluate_and_fold_conditions(&mut schedule.system_conditions[system_index], world);
should_run &= system_conditions_met;
#[cfg(feature = "trace")]
should_run_span.exit();
// system has either been skipped or will run
self.completed_systems.insert(system_index);
if !should_run {
continue;
}
let system = &mut schedule.systems[system_index];
if is_apply_deferred(system) {
continue;
}
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
__rust_begin_short_backtrace::run(&mut **system, world);
}));
if let Err(payload) = res {
eprintln!("Encountered a panic in system `{}`!", &*system.name());
std::panic::resume_unwind(payload);
}
}
self.evaluated_sets.clear();
self.completed_systems.clear();
}
fn set_apply_final_deferred(&mut self, _: bool) {
// do nothing. simple executor does not do a final sync
}
}
impl SimpleExecutor {
/// Creates a new simple executor for use in a [`Schedule`](crate::schedule::Schedule).
/// This calls each system in order and immediately calls [`System::apply_deferred`](crate::system::System::apply_deferred).
pub const fn new() -> Self {
Self {
evaluated_sets: FixedBitSet::new(),
completed_systems: FixedBitSet::new(),
}
}
}
fn evaluate_and_fold_conditions(conditions: &mut [BoxedCondition], world: &mut World) -> bool {
// not short-circuiting is intentional
#[allow(clippy::unnecessary_fold)]
conditions
.iter_mut()
.map(|condition| __rust_begin_short_backtrace::readonly_run(&mut **condition, world))
.fold(true, |acc, res| acc && res)
}
#[cfg(test)]
#[test]
fn skip_automatic_sync_points() {
// Schedules automatically insert apply_deferred systems, but these should
// not be executed as they only serve as markers and are not initialized
use crate::prelude::*;
let mut sched = Schedule::default();
sched.set_executor_kind(ExecutorKind::Simple);
sched.add_systems((|_: Commands| (), || ()).chain());
let mut world = World::new();
sched.run(&mut world);
}