From 1f39f9e73521ae0aef4f6a093b5f358d70252e8d Mon Sep 17 00:00:00 2001 From: Daniel Hodges Date: Thu, 9 Jan 2025 21:28:03 -0500 Subject: [PATCH] scxtop: Refactor bpf event handling and TUI fixes - Simplify bpf program attachment - Refactor bpf handling of sched_swith events - Add barchart support for LLCs - Add DSQ slice consumed metrics Signed-off-by: Daniel Hodges --- tools/scxtop/src/app.rs | 271 ++++++++++++++++++++++---------- tools/scxtop/src/bpf/intf.h | 14 +- tools/scxtop/src/bpf/main.bpf.c | 147 ++++++++--------- tools/scxtop/src/lib.rs | 10 +- tools/scxtop/src/main.rs | 34 ++-- 5 files changed, 285 insertions(+), 191 deletions(-) diff --git a/tools/scxtop/src/app.rs b/tools/scxtop/src/app.rs index d10f5841a..82cd40ede 100644 --- a/tools/scxtop/src/app.rs +++ b/tools/scxtop/src/app.rs @@ -382,12 +382,6 @@ impl<'a> App<'a> { let [top_left, bottom_left] = Layout::vertical([Constraint::Fill(1); 2]).areas(left); let num_llcs = self.topo.all_llcs.len(); - let mut llcs_constraints = vec![Constraint::Length(1)]; - for _ in 0..num_llcs { - llcs_constraints.push(Constraint::Ratio(1, num_llcs as u32)); - } - let llcs_verticle = Layout::vertical(llcs_constraints).split(right); - let llc_iter = self .llc_data .values() @@ -397,41 +391,85 @@ impl<'a> App<'a> { .collect::>(); let stats = VecStats::new(&llc_iter, true, true, true, None); - let llc_sparklines: Vec = self - .topo - .all_llcs - .keys() - .map(|llc_id| self.llc_sparkline(llc_id.clone(), *llc_id == num_llcs - 1)) - .collect(); + match self.view_state { + ViewState::Sparkline => { + let mut llcs_constraints = vec![Constraint::Length(1)]; + for _ in 0..num_llcs { + llcs_constraints.push(Constraint::Ratio(1, num_llcs as u32)); + } + let llcs_verticle = Layout::vertical(llcs_constraints).split(right); + + let llc_block = Block::bordered() + .title_top( + Line::from(format!( + "LLCs ({}) avg {} max {} min {}", + self.active_hw_event.event, stats.avg, stats.max, stats.min + )) + .style(self.theme.title_style()) + .centered(), + ) + .border_type(BorderType::Rounded) + .title_top( + Line::from(format!("{}ms", self.tick_rate_ms)) + .style(self.theme.text_important_color()) + .right_aligned(), + ) + .title_alignment(Alignment::Center) + .style(self.theme.border_style()); + + frame.render_widget(llc_block, llcs_verticle[0]); + + let llc_sparklines: Vec = self + .topo + .all_llcs + .keys() + .map(|llc_id| self.llc_sparkline(llc_id.clone(), *llc_id == num_llcs - 1)) + .collect(); + + let _ = llc_sparklines + .iter() + .enumerate() + .for_each(|(i, llc_sparkline)| { + frame.render_widget(llc_sparkline, llcs_verticle[i + 1]); + }); + } + ViewState::BarChart => { + let llc_block = Block::default() + .title_top( + Line::from(format!( + "LLCs ({}) avg {} max {} min {}", + self.active_hw_event.event, stats.avg, stats.max, stats.min, + )) + .style(self.theme.title_style()) + .centered(), + ) + .title_top( + Line::from(format!("{}ms", self.tick_rate_ms)) + .style(self.theme.text_important_color()) + .right_aligned(), + ) + .style(self.theme.border_style()) + .borders(Borders::TOP | Borders::BOTTOM | Borders::LEFT | Borders::RIGHT) + .border_type(BorderType::Rounded); - let llc_block = Block::bordered() - .title_top( - Line::from(format!( - "LLCs ({}) avg {} max {} min {}", - self.active_hw_event.event, stats.avg, stats.max, stats.min - )) - .style(self.theme.title_style()) - .centered(), - ) - .border_type(BorderType::Rounded) - .title_top( - Line::from(format!("{}ms", self.tick_rate_ms)) - .style(self.theme.text_important_color()) - .right_aligned(), - ) - .title_alignment(Alignment::Center) - .style(self.theme.border_style()); + let llc_bars: Vec = self.llc_bars(self.active_hw_event.event.clone()); - frame.render_widget(llc_block, llcs_verticle[0]); - let _ = llc_sparklines - .iter() - .enumerate() - .for_each(|(i, llc_sparkline)| { - frame.render_widget(llc_sparkline, llcs_verticle[i + 1]); - }); + let barchart = BarChart::default() + .data(BarGroup::default().bars(&llc_bars)) + .block(llc_block) + .max(stats.max) + .direction(Direction::Horizontal) + .bar_style(self.theme.sparkline_style()) + .bar_gap(0) + .bar_width(1); - self.render_scheduler(frame, top_left, true)?; + frame.render_widget(barchart, right); + } + } + + self.render_scheduler("dsq_lat_us".to_string(), frame, top_left, true)?; self.render_dsq_vtime(frame, bottom_left, false)?; + Ok(()) } @@ -488,7 +526,7 @@ impl<'a> App<'a> { frame.render_widget(node_sparkline, nodes_verticle[i + 1]); }); - self.render_scheduler(frame, top_left, true)?; + self.render_scheduler("dsq_lat_us".to_string(), frame, top_left, true)?; self.render_dsq_vtime(frame, bottom_left, false)?; Ok(()) } @@ -566,9 +604,35 @@ impl<'a> App<'a> { .collect() } + /// Generates a LLC bar chart. + fn llc_bar(&self, llc: usize, value: u64, avg: u64, max: u64, min: u64) -> Bar { + Bar::default() + .value(value) + .label(Line::from(format!( + "{} avg {} max {} min {}", + llc, avg, max, min + ))) + .text_value(format!("{}", value)) + } + + /// Generates LLC bar charts. + fn llc_bars(&self, event: String) -> Vec { + self.llc_data + .iter() + .filter(|(_llc_id, llc_data)| llc_data.data.data.contains_key(&event.clone())) + .map(|(llc_id, llc_data)| { + let values = llc_data.event_data_immut(event.clone()); + let value = values.last().copied().unwrap_or(0 as u64); + let stats = VecStats::new(&values, true, true, true, None); + self.llc_bar(*llc_id, value, stats.avg, stats.max, stats.min) + }) + .collect() + } + /// Renders the scheduler state as sparklines. fn render_scheduler_sparklines( &mut self, + event: String, frame: &mut Frame, area: Rect, render_sample_rate: bool, @@ -598,7 +662,7 @@ impl<'a> App<'a> { let dsq_global_iter = self .dsq_data .values() - .map(|dsq_data| dsq_data.event_data_immut("dsq_lat_us".to_string())) + .map(|dsq_data| dsq_data.event_data_immut(event.clone())) .into_iter() .flatten() .collect::>(); @@ -608,8 +672,8 @@ impl<'a> App<'a> { let block = Block::default() .title_top( Line::from(format!( - "{} DSQ Latency (us) avg {} max {} min {}", - self.scheduler, stats.avg, stats.max, stats.min, + "{} {} avg {} max {} min {}", + self.scheduler, event, stats.avg, stats.max, stats.min, )) .style(self.theme.title_style()) .centered(), @@ -627,7 +691,7 @@ impl<'a> App<'a> { frame.render_widget(block, dsqs_verticle[0]); let _ = self - .dsq_sparklines("dsq_lat_us".to_string()) + .dsq_sparklines(event.clone()) .iter() .enumerate() .for_each(|(j, dsq_sparkline)| { @@ -640,6 +704,7 @@ impl<'a> App<'a> { /// Returns the dsq vtime chart. fn render_dsq_vtime_sparklines( &self, + event: String, frame: &mut Frame, area: Rect, render_sample_rate: bool, @@ -647,7 +712,7 @@ impl<'a> App<'a> { let num_dsqs = self .dsq_data .iter() - .filter(|(_dsq_id, dsq_data)| dsq_data.data.contains_key("dsq_vtime")) + .filter(|(_dsq_id, dsq_data)| dsq_data.data.contains_key(&event.clone())) .count(); if num_dsqs == 0 { let block = Block::default() @@ -672,7 +737,7 @@ impl<'a> App<'a> { let dsq_global_iter = self .dsq_data .values() - .map(|dsq_data| dsq_data.event_data_immut("dsq_vtime".to_string())) + .map(|dsq_data| dsq_data.event_data_immut(event.clone())) .into_iter() .flatten() .collect::>(); @@ -701,7 +766,7 @@ impl<'a> App<'a> { frame.render_widget(block, dsqs_verticle[0]); let _ = self - .dsq_sparklines("dsq_vtime".to_string()) + .dsq_sparklines(event.clone()) .iter() .enumerate() .for_each(|(j, dsq_sparkline)| { @@ -714,6 +779,7 @@ impl<'a> App<'a> { /// Returns the dsq vtime chart. fn render_dsq_vtime_barchart( &self, + event: String, frame: &mut Frame, area: Rect, render_sample_rate: bool, @@ -721,7 +787,7 @@ impl<'a> App<'a> { let num_dsqs = self .dsq_data .iter() - .filter(|(_dsq_id, dsq_data)| dsq_data.data.contains_key("dsq_vtime")) + .filter(|(_dsq_id, dsq_data)| dsq_data.data.contains_key(&event.clone())) .count(); if num_dsqs == 0 { let block = Block::default() @@ -746,8 +812,8 @@ impl<'a> App<'a> { let vtime_global_iter: Vec = self .dsq_data .iter() - .filter(|(_dsq_id, event_data)| event_data.data.contains_key("dsq_vtime")) - .map(|(_dsq_id, event_data)| event_data.event_data_immut("dsq_vtime".to_string())) + .filter(|(_dsq_id, event_data)| event_data.data.contains_key(&event.clone())) + .map(|(_dsq_id, event_data)| event_data.event_data_immut(event.clone())) .into_iter() .flatten() .collect::>(); @@ -757,8 +823,8 @@ impl<'a> App<'a> { let bar_block = Block::default() .title_top( Line::from(format!( - "{} DSQ vtime delta avg {} max {} min {}", - self.scheduler, stats.avg, stats.max, stats.min, + "{} {} avg {} max {} min {}", + self.scheduler, event, stats.avg, stats.max, stats.min, )) .style(self.theme.title_style()) .centered(), @@ -774,7 +840,7 @@ impl<'a> App<'a> { .borders(Borders::TOP | Borders::BOTTOM | Borders::LEFT | Borders::RIGHT) .border_type(BorderType::Rounded); - let dsq_bars: Vec = self.dsq_bars("dsq_vtime".to_string()); + let dsq_bars: Vec = self.dsq_bars(event.clone()); let barchart = BarChart::default() .data(BarGroup::default().bars(&dsq_bars)) @@ -792,6 +858,7 @@ impl<'a> App<'a> { /// Renders the scheduler state as barcharts. fn render_scheduler_barchart( &mut self, + event: String, frame: &mut Frame, area: Rect, render_sample_rate: bool, @@ -819,7 +886,7 @@ impl<'a> App<'a> { let dsq_global_iter = self .dsq_data .values() - .map(|dsq_data| dsq_data.event_data_immut("dsq_lat_us".to_string())) + .map(|dsq_data| dsq_data.event_data_immut(event.clone())) .into_iter() .flatten() .collect::>(); @@ -828,8 +895,8 @@ impl<'a> App<'a> { let bar_block = Block::default() .title_top( Line::from(format!( - "{} DSQ Latency (us) avg {} max {} min {}", - self.scheduler, stats.avg, stats.max, stats.min, + "{} {} avg {} max {} min {}", + self.scheduler, event, stats.avg, stats.max, stats.min, )) .style(self.theme.title_style()) .centered(), @@ -845,7 +912,7 @@ impl<'a> App<'a> { .borders(Borders::TOP | Borders::BOTTOM | Borders::LEFT | Borders::RIGHT) .border_type(BorderType::Rounded); - let dsq_bars: Vec = self.dsq_bars("dsq_lat_us".to_string()); + let dsq_bars: Vec = self.dsq_bars(event.clone()); let barchart = BarChart::default() .data(BarGroup::default().bars(&dsq_bars)) @@ -863,15 +930,18 @@ impl<'a> App<'a> { /// Renders the scheduler application state. fn render_scheduler( &mut self, + event: String, frame: &mut Frame, area: Rect, render_sample_rate: bool, ) -> Result<()> { match self.view_state { ViewState::Sparkline => { - self.render_scheduler_sparklines(frame, area, render_sample_rate) + self.render_scheduler_sparklines(event, frame, area, render_sample_rate) + } + ViewState::BarChart => { + self.render_scheduler_barchart(event, frame, area, render_sample_rate) } - ViewState::BarChart => self.render_scheduler_barchart(frame, area, render_sample_rate), } } @@ -883,10 +953,18 @@ impl<'a> App<'a> { render_sample_rate: bool, ) -> Result<()> { match self.view_state { - ViewState::Sparkline => { - self.render_dsq_vtime_sparklines(frame, area, render_sample_rate) - } - ViewState::BarChart => self.render_dsq_vtime_barchart(frame, area, render_sample_rate), + ViewState::Sparkline => self.render_dsq_vtime_sparklines( + "dsq_vtime_delta".to_string(), + frame, + area, + render_sample_rate, + ), + ViewState::BarChart => self.render_dsq_vtime_barchart( + "dsq_vtime_delta".to_string(), + frame, + area, + render_sample_rate, + ), } } @@ -1096,7 +1174,7 @@ impl<'a> App<'a> { let [top_left, bottom_left] = Layout::vertical([Constraint::Fill(1); 2]).areas(left); self.render_event(frame, right)?; - self.render_scheduler(frame, top_left, true)?; + self.render_scheduler("dsq_lat_us".to_string(), frame, top_left, true)?; self.render_dsq_vtime(frame, bottom_left, false)?; Ok(()) } @@ -1293,7 +1371,13 @@ impl<'a> App<'a> { AppState::Event => self.render_event_list(frame), AppState::Node => self.render_node(frame), AppState::Llc => self.render_llc(frame), - AppState::Scheduler => self.render_scheduler(frame, frame.area(), true), + AppState::Scheduler => { + let [top, center, bottom] = + Layout::vertical([Constraint::Fill(1); 3]).areas(frame.area()); + self.render_scheduler("dsq_lat_us".to_string(), frame, top, true)?; + self.render_scheduler("dsq_slice_consumed".to_string(), frame, center, false)?; + self.render_scheduler("dsq_vtime_delta".to_string(), frame, bottom, false) + } _ => self.render_default(frame), } } @@ -1351,7 +1435,17 @@ impl<'a> App<'a> { } /// Updates the app when a task is scheduled. - fn on_sched_switch(&mut self, cpu: u32, dsq_id: u64, dsq_lat_us: u64, dsq_vtime: u64) { + fn on_sched_switch( + &mut self, + cpu: u32, + next_dsq_id: u64, + next_dsq_lat_us: u64, + next_dsq_vtime: u64, + _next_slice_ns: u64, + prev_dsq_id: u64, + prev_used_slice_ns: u64, + _prev_slice_ns: u64, + ) { if self.scheduler == "none" { return; } @@ -1363,26 +1457,32 @@ impl<'a> App<'a> { 0, self.max_cpu_events, )); - cpu_data.add_event_data("dsq_lat_us".to_string(), dsq_lat_us); - let dsq_data = self + cpu_data.add_event_data("dsq_lat_us".to_string(), next_dsq_lat_us); + let next_dsq_data = self .dsq_data - .entry(dsq_id) + .entry(next_dsq_id) .or_insert(EventData::new(self.max_cpu_events * 2)); - dsq_data.add_event_data("dsq_lat_us".to_string(), dsq_lat_us); - if dsq_vtime > 0 { + next_dsq_data.add_event_data("dsq_lat_us".to_string(), next_dsq_lat_us); + if next_dsq_vtime > 0 { // vtime is special because we want the delta - let last = dsq_data - .event_data_immut("dsq_vtime".to_string()) + let last = next_dsq_data + .event_data_immut("dsq_vtime_delta".to_string()) .last() .copied() .unwrap_or(0 as u64); - if dsq_vtime - last < DSQ_VTIME_CUTOFF { - dsq_data.add_event_data( - "dsq_vtime".to_string(), - if last > 0 { dsq_vtime - last } else { 0 }, + if next_dsq_vtime - last < DSQ_VTIME_CUTOFF { + next_dsq_data.add_event_data( + "dsq_vtime_delta".to_string(), + if last > 0 { next_dsq_vtime - last } else { 0 }, ); } } + + let prev_dsq_data = self + .dsq_data + .entry(prev_dsq_id) + .or_insert(EventData::new(self.max_cpu_events * 2)); + prev_dsq_data.add_event_data("dsq_slice_consumed".to_string(), prev_used_slice_ns); } /// Updates the bpf bpf sampling rate. @@ -1433,11 +1533,24 @@ impl<'a> App<'a> { } Action::SchedSwitch { cpu, - dsq_id, - dsq_lat_us, - dsq_vtime, + next_dsq_id, + next_dsq_lat_us, + next_dsq_vtime, + next_slice_ns, + prev_dsq_id, + prev_used_slice_ns, + prev_slice_ns, } => { - self.on_sched_switch(cpu, dsq_id, dsq_lat_us, dsq_vtime); + self.on_sched_switch( + cpu, + next_dsq_id, + next_dsq_lat_us, + next_dsq_vtime, + next_slice_ns, + prev_dsq_id, + prev_used_slice_ns, + prev_slice_ns, + ); } Action::ClearEvent => self.stop_perf_events(), Action::ChangeTheme => { diff --git a/tools/scxtop/src/bpf/intf.h b/tools/scxtop/src/bpf/intf.h index b7f592083..e9432f61d 100644 --- a/tools/scxtop/src/bpf/intf.h +++ b/tools/scxtop/src/bpf/intf.h @@ -26,11 +26,14 @@ struct bpf_event { int type; u32 cpu; u32 perf; - u64 dsq_id; - u64 dsq_lat_us; - u32 dsq_nr; - u64 dsq_vtime; - u64 slice_ns; + u64 next_dsq_id; + u64 next_dsq_lat_us; + u32 next_dsq_nr; + u64 next_dsq_vtime; + u64 next_slice_ns; + u64 prev_dsq_id; + u64 prev_used_slice_ns; + u64 prev_slice_ns; }; struct task_ctx { @@ -38,6 +41,7 @@ struct task_ctx { u64 dsq_insert_time; u64 dsq_vtime; u64 slice_ns; + u64 last_run_ns; }; #endif /* __INTF_H */ diff --git a/tools/scxtop/src/bpf/main.bpf.c b/tools/scxtop/src/bpf/main.bpf.c index ee1078677..d23a435b2 100644 --- a/tools/scxtop/src/bpf/main.bpf.c +++ b/tools/scxtop/src/bpf/main.bpf.c @@ -49,7 +49,7 @@ struct { } task_data SEC(".maps"); -static u64 t_to_tptr(struct task_struct *p) +static __always_inline u64 t_to_tptr(struct task_struct *p) { u64 tptr; int err; @@ -63,10 +63,30 @@ static u64 t_to_tptr(struct task_struct *p) static struct task_ctx *try_lookup_task_ctx(struct task_struct *p) { + struct task_ctx *tctx; u64 tptr; + + if (!p) + return NULL; + tptr = t_to_tptr(p); + if (tptr == 0) + return NULL; + + tctx = bpf_map_lookup_elem(&task_data, &tptr); + if (!tctx) { + struct task_ctx new_tctx; + new_tctx.dsq_id = 0; + new_tctx.dsq_vtime = 0; + new_tctx.slice_ns = 0; + new_tctx.last_run_ns = 0; + + if (!bpf_map_update_elem(&task_data, &tptr, &new_tctx, BPF_ANY)) + return NULL; - return bpf_map_lookup_elem(&task_data, &tptr); + tctx = bpf_map_lookup_elem(&task_data, &tptr); + } + return tctx; } static int update_task_ctx(struct task_struct *p, u64 dsq, u64 vtime, u64 slice_ns) @@ -76,20 +96,13 @@ static int update_task_ctx(struct task_struct *p, u64 dsq, u64 vtime, u64 slice_ struct task_ctx *tctx; - tctx = try_lookup_task_ctx(p); - if (!tctx) { - struct task_ctx new_tctx; - tctx = &new_tctx; - } - - u64 tptr; - tptr = t_to_tptr(p); + if (!(tctx = try_lookup_task_ctx(p))) + return -ENOENT; tctx->dsq_insert_time = bpf_ktime_get_ns(); tctx->dsq_id = dsq; tctx->dsq_vtime = vtime; tctx->slice_ns = slice_ns; - bpf_map_update_elem(&task_data, &tptr, tctx, BPF_ANY); return 0; } @@ -104,7 +117,7 @@ int BPF_KPROBE(scx_sched_reg) event = bpf_ringbuf_reserve(&events, sizeof(struct bpf_event), 0); if (!event) - return 1; + return -ENOENT; event->type = SCHED_REG; bpf_ringbuf_submit(event, 0); @@ -122,7 +135,7 @@ int BPF_KPROBE(scx_sched_unreg) event = bpf_ringbuf_reserve(&events, sizeof(struct bpf_event), 0); if (!event) - return 1; + return -ENOENT; event->type = SCHED_UNREG; bpf_ringbuf_submit(event, 0); @@ -140,7 +153,7 @@ int BPF_KPROBE(on_sched_cpu_perf, s32 cpu, u32 perf) event = bpf_ringbuf_reserve(&events, sizeof(struct bpf_event), 0); if (!event) - return 1; + return -ENOENT; event->type = CPU_PERF_SET; event->cpu = cpu; @@ -169,19 +182,12 @@ static int on_insert(struct task_struct *p, u64 dsq) struct task_ctx *tctx; - tctx = try_lookup_task_ctx(p); - if (!tctx) { - struct task_ctx new_tctx; - tctx = &new_tctx; - } - - u64 tptr; - tptr = t_to_tptr(p); + if (!(tctx = try_lookup_task_ctx(p))) + return -ENOENT; tctx->dsq_insert_time = bpf_ktime_get_ns(); tctx->dsq_id = dsq; tctx->dsq_vtime = 0; - bpf_map_update_elem(&task_data, &tptr, tctx, BPF_ANY); return 0; } @@ -206,18 +212,11 @@ static int on_dsq_move(struct task_struct *p, u64 dsq) struct task_ctx *tctx; - tctx = try_lookup_task_ctx(p); - if (!tctx) { - struct task_ctx new_tctx; - tctx = &new_tctx; - } - - u64 tptr; - tptr = t_to_tptr(p); + if (!(tctx = try_lookup_task_ctx(p))) + return -ENOENT; tctx->dsq_id = dsq; tctx->dsq_vtime = 0; - bpf_map_update_elem(&task_data, &tptr, tctx, BPF_ANY); return 0; } @@ -243,18 +242,11 @@ static int on_dsq_move_vtime(struct task_struct *p, u64 dsq) struct task_ctx *tctx; - tctx = try_lookup_task_ctx(p); - if (!tctx) { - struct task_ctx new_tctx; - tctx = &new_tctx; - } - - u64 tptr; - tptr = t_to_tptr(p); + if (!(tctx = try_lookup_task_ctx(p))) + return -ENOENT; tctx->dsq_id = dsq; bpf_core_read(&tctx->dsq_vtime, sizeof(u64), &p->scx.dsq_vtime); - bpf_map_update_elem(&task_data, &tptr, tctx, BPF_ANY); return 0; } @@ -280,17 +272,10 @@ static int on_move_set_slice(struct task_struct *p, u64 slice) struct task_ctx *tctx; - tctx = try_lookup_task_ctx(p); - if (!tctx) { - struct task_ctx new_tctx; - tctx = &new_tctx; - } - - u64 tptr; - tptr = t_to_tptr(p); + if (!(tctx = try_lookup_task_ctx(p))) + return -ENOENT; tctx->slice_ns = slice; - bpf_map_update_elem(&task_data, &tptr, tctx, BPF_ANY); return 0; } @@ -317,17 +302,10 @@ static int on_move_set_vtime(struct task_struct *p, u64 vtime) struct task_ctx *tctx; - tctx = try_lookup_task_ctx(p); - if (!tctx) { - struct task_ctx new_tctx; - tctx = &new_tctx; - } - - u64 tptr; - tptr = t_to_tptr(p); + if (!(tctx = try_lookup_task_ctx(p))) + return -ENOENT; tctx->dsq_vtime = vtime; - bpf_map_update_elem(&task_data, &tptr, tctx, BPF_ANY); return 0; } @@ -364,8 +342,8 @@ static __always_inline int __on_sched_wakeup(struct task_struct *p) event->type = SCHED_WAKEUP; event->cpu = bpf_get_smp_processor_id(); - bpf_core_read(&event->dsq_id, sizeof(u64), &p->scx.dsq->id); - bpf_core_read(&event->dsq_nr, sizeof(u32), &p->scx.dsq->nr); + bpf_core_read(&event->next_dsq_id, sizeof(u64), &p->scx.dsq->id); + bpf_core_read(&event->next_dsq_nr, sizeof(u32), &p->scx.dsq->nr); bpf_ringbuf_submit(event, 0); return 0; @@ -383,37 +361,37 @@ int BPF_PROG(on_sched_wakeup_new, struct task_struct *p) return __on_sched_wakeup(p); } -SEC("raw_tracepoint/sched_switch") -int on_sched_switch(struct pt_regs *ctx) + +SEC("tp_btf/sched_switch") +int BPF_PROG(on_sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next) { - struct task_struct *p; - struct task_ctx *tctx; + struct task_ctx *next_tctx, *prev_tctx; struct bpf_event *event; if (!enable_bpf_events) return 0; - p = (struct task_struct*)bpf_get_current_task(); - if (!p) - return 0; - u32 val = get_random_sample(sample_rate); if (val > 1) { - return 0; + return -EAGAIN; } - tctx = try_lookup_task_ctx(p); - if (!tctx || tctx->dsq_id == SCX_DSQ_INVALID || tctx->dsq_insert_time == 0) - return 0; + next_tctx = try_lookup_task_ctx(next); + if (!next_tctx || next_tctx->dsq_id == SCX_DSQ_INVALID || next_tctx->dsq_insert_time == 0) + return -ENOENT; - u64 now = bpf_ktime_get_ns(); + prev_tctx = try_lookup_task_ctx(prev); + if (!prev_tctx || prev_tctx->dsq_id == SCX_DSQ_INVALID) + return -ENOENT; event = bpf_ringbuf_reserve(&events, sizeof(struct bpf_event), 0); if (!event) - return 1; + return -ENOENT; event->type = SCHED_SWITCH; event->cpu = bpf_get_smp_processor_id(); + u64 now = bpf_ktime_get_ns(); + /* * Tracking vtime **and** the dsq a task was inserted to is kind of * tricky. We could read dsq_vtime directly of the sched_ext_entity on @@ -423,8 +401,12 @@ int on_sched_switch(struct pt_regs *ctx) * store it in a map for the task. There still needs to be handling for * when tasks are moved from iterators. */ - event->dsq_id = tctx->dsq_id; - event->dsq_lat_us = (now - tctx->dsq_insert_time) / 1000; + event->next_dsq_id = next_tctx->dsq_id; + event->next_dsq_lat_us = (now - next_tctx->dsq_insert_time) / 1000; + event->prev_used_slice_ns = prev_tctx->last_run_ns - now; + event->prev_dsq_id = prev_tctx->dsq_id; + // bpf_core_read(&event->prev_slice_ns, sizeof(u64), &prev->scx.slice); + event->prev_slice_ns = prev_tctx->slice_ns; /* * XXX: if a task gets moved to another dsq and the vtime is updated @@ -433,12 +415,15 @@ int on_sched_switch(struct pt_regs *ctx) * updated the tctx needs to be updated. */ // bpf_core_read(&event->dsq_vtime, sizeof(u64), &p->scx.dsq_vtime); - event->dsq_vtime = tctx->dsq_vtime; + event->next_dsq_vtime = next_tctx->dsq_vtime; bpf_ringbuf_submit(event, 0); - tctx->dsq_vtime = 0; - tctx->dsq_id = 0; - tctx->dsq_insert_time = 0; + next_tctx->last_run_ns = bpf_ktime_get_ns(); + next_tctx->dsq_vtime = 0; + next_tctx->dsq_insert_time = 0; + prev_tctx->dsq_id = 0; + prev_tctx->dsq_vtime = 0; + next_tctx->dsq_insert_time = 0; return 0; } diff --git a/tools/scxtop/src/lib.rs b/tools/scxtop/src/lib.rs index 3846701fb..eb24bf0de 100644 --- a/tools/scxtop/src/lib.rs +++ b/tools/scxtop/src/lib.rs @@ -109,9 +109,13 @@ pub enum Action { }, SchedSwitch { cpu: u32, - dsq_id: u64, - dsq_lat_us: u64, - dsq_vtime: u64, + next_dsq_id: u64, + next_dsq_lat_us: u64, + next_dsq_vtime: u64, + next_slice_ns: u64, + prev_dsq_id: u64, + prev_used_slice_ns: u64, + prev_slice_ns: u64, }, SetState { state: AppState, diff --git a/tools/scxtop/src/main.rs b/tools/scxtop/src/main.rs index 7629696be..50ef589e0 100644 --- a/tools/scxtop/src/main.rs +++ b/tools/scxtop/src/main.rs @@ -71,26 +71,10 @@ async fn run() -> Result<()> { let mut links = Vec::new(); // Attach probes - links.push( - skel.progs - .on_sched_cpu_perf - .attach_kprobe(false, "scx_bpf_cpuperf_set")?, - ); - links.push( - skel.progs - .scx_sched_reg - .attach_kprobe(false, "bpf_scx_reg")?, - ); - links.push( - skel.progs - .scx_sched_unreg - .attach_kprobe(false, "bpf_scx_unreg")?, - ); - links.push( - skel.progs - .on_sched_switch - .attach_raw_tracepoint("sched_switch")?, - ); + links.push(skel.progs.on_sched_cpu_perf.attach()?); + links.push(skel.progs.scx_sched_reg.attach()?); + links.push(skel.progs.scx_sched_unreg.attach()?); + links.push(skel.progs.on_sched_switch.attach()?); // 6.13 compatability if let Ok(link) = skel @@ -195,9 +179,13 @@ async fn run() -> Result<()> { event_type_SCHED_SWITCH => { let action = Action::SchedSwitch { cpu: event.cpu, - dsq_id: event.dsq_id, - dsq_lat_us: event.dsq_lat_us, - dsq_vtime: event.dsq_vtime, + next_dsq_id: event.next_dsq_id, + next_dsq_lat_us: event.next_dsq_lat_us, + next_dsq_vtime: event.next_dsq_vtime, + next_slice_ns: event.next_slice_ns, + prev_dsq_id: event.prev_dsq_id, + prev_used_slice_ns: event.prev_slice_ns, + prev_slice_ns: event.prev_slice_ns, }; tx.send(action).ok(); }