Enabling node profiling panics on the first processed block after any FirewheelContext::activate, even with an empty graph. This appears to be a residual of #134, but at the first NewSchedule instead of at construction.
Reproduction
firewheel = { version = "0.12.1", features = ["node_profiling"] } and
audioadapter-buffers = { version = "4.0", default-features = false, features = ["std"] }:
use std::num::NonZeroU32;
use std::time::Duration;
use audioadapter_buffers::direct::InterleavedSlice;
use firewheel::backend::BackendProcessInfo;
use firewheel::node::StreamStatus;
use firewheel::{ActivateInfo, FirewheelConfig, FirewheelContext, FirewheelFlags};
const BLOCK_FRAMES: usize = 128;
fn main() {
let mut cx = FirewheelContext::new(FirewheelConfig {
flags: FirewheelFlags {
profile_nodes: true,
..Default::default()
},
..Default::default()
});
// No nodes need be added.
let mut processor = cx
.activate(ActivateInfo {
sample_rate: NonZeroU32::new(48_000).unwrap(),
max_block_frames: NonZeroU32::new(BLOCK_FRAMES as u32).unwrap(),
num_stream_in_channels: 0,
num_stream_out_channels: 2,
input_to_output_latency_seconds: 0.0,
})
.unwrap();
let input_storage: [f32; 0] = [];
let mut output_storage = vec![0.0f32; BLOCK_FRAMES * 2];
let input = InterleavedSlice::new(&input_storage, 0, 0).unwrap();
let mut output = InterleavedSlice::new_mut(&mut output_storage, 2, BLOCK_FRAMES).unwrap();
processor.process(
&input,
&mut output,
BackendProcessInfo {
frames: BLOCK_FRAMES,
process_timestamp: None,
duration_since_stream_start: Duration::ZERO,
input_stream_status: StreamStatus::empty(),
output_stream_status: StreamStatus::empty(),
dropped_frames: 0,
process_to_playback_delay: None,
},
);
}
thread 'main' panicked at
firewheel-graph-0.12.1/src/processor/profiling.rs:226:41:
index out of bounds: the len is 0 but the index is 0
Ordering
FirewheelProcessorInner::process calls ProfilerTx::new_process_loop (processor/process.rs:57) before polling context messages.
new_process_loop resizes node_cpu_sums to the profiler's current nodes length.
poll_messages (processor/process.rs:92) then consumes the NewSchedule that FirewheelContext::activate sends unconditionally (context.rs:534), on first activation as well as reactivation.
ProfilerTx::new_schedule clears and refills nodes, but only clears node_cpu_sums, leaving it empty.
- The same block goes on to visit the schedule's nodes, and the first
node_completed indexes the now-empty node_cpu_sums.
Suggested fix
Resize node_cpu_sums to nodes.len() at the end of new_schedule while
is_profiling_nodes is set:
if self.is_profiling_nodes {
self.heap_data
.node_cpu_sums
.resize(self.heap_data.nodes.len(), 0.0);
}
Enabling node profiling panics on the first processed block after any
FirewheelContext::activate, even with an empty graph. This appears to be a residual of #134, but at the firstNewScheduleinstead of at construction.Reproduction
firewheel = { version = "0.12.1", features = ["node_profiling"] }andaudioadapter-buffers = { version = "4.0", default-features = false, features = ["std"] }:Ordering
FirewheelProcessorInner::processcallsProfilerTx::new_process_loop(processor/process.rs:57) before polling context messages.new_process_loopresizesnode_cpu_sumsto the profiler's currentnodeslength.poll_messages(processor/process.rs:92) then consumes theNewSchedulethatFirewheelContext::activatesends unconditionally (context.rs:534), on first activation as well as reactivation.ProfilerTx::new_scheduleclears and refillsnodes, but only clearsnode_cpu_sums, leaving it empty.node_completedindexes the now-emptynode_cpu_sums.Suggested fix
Resize
node_cpu_sumstonodes.len()at the end ofnew_schedulewhileis_profiling_nodesis set: