Skip to main content

qlog_dancer/plots/
rtt.rs

1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use minmax::XYMinMax;
28use plotters::coord::types::RangedCoordf32;
29use plotters::coord::types::RangedCoordf64;
30
31use plotters::coord::Shift;
32
33use crate::plots::colors::*;
34use crate::plots::*;
35
36use crate::seriesstore::SeriesStore;
37
38fn draw_rtt_series<DB: DrawingBackend>(
39    data: &[(f64, f32)], label: &str, colour: RGBColor,
40    rtt_chart: &mut ChartContext<DB, Cartesian2d<RangedCoordf64, RangedCoordf32>>,
41) {
42    rtt_chart
43        .draw_series(LineSeries::new(data.to_vec(), colour))
44        .unwrap()
45        .label(label)
46        .legend(move |(x, y)| {
47            PathElement::new(vec![(x, y), (x + 20, y)], colour)
48        });
49}
50
51fn draw_min_rtt<DB: DrawingBackend>(
52    data: &[(f64, f32)],
53    rtt_chart: &mut ChartContext<DB, Cartesian2d<RangedCoordf64, RangedCoordf32>>,
54) {
55    draw_rtt_series(data, "Min RTT", SOFT_PINK, rtt_chart);
56}
57
58fn draw_latest_rtt<DB: DrawingBackend>(
59    data: &[(f64, f32)],
60    rtt_chart: &mut ChartContext<DB, Cartesian2d<RangedCoordf64, RangedCoordf32>>,
61) {
62    draw_rtt_series(data, "Latest RTT", ORANGE, rtt_chart);
63}
64
65fn draw_smoothed_rtt<DB: DrawingBackend>(
66    data: &[(f64, f32)],
67    rtt_chart: &mut ChartContext<DB, Cartesian2d<RangedCoordf64, RangedCoordf32>>,
68) {
69    draw_rtt_series(data, "Smoothed RTT", BROWN, rtt_chart);
70}
71
72pub fn draw_rtt_plot<'a, DB: DrawingBackend + 'a>(
73    params: &PlotParameters, axis: &XYMinMax<u64>, ss: &SeriesStore,
74    plot: &plotters::drawing::DrawingArea<DB, Shift>,
75) -> ChartContext<'a, DB, Cartesian2d<RangedCoordf64, RangedCoordf32>> {
76    let mut builder = ChartBuilder::on(plot);
77    builder
78        .x_label_area_size(params.area_margin.x)
79        .y_label_area_size(params.area_margin.y);
80
81    if params.display_chart_title {
82        builder.caption("RTT", chart_subtitle_style(&params.colors.caption));
83    }
84    let mut chart = builder
85        .build_cartesian_2d(
86            axis.x.range(),
87            0.0f32..(ss.y_max_rtt_plot + ss.y_max_rtt_plot / 10.0),
88        )
89        .unwrap();
90
91    draw_mesh(
92        &params.colors,
93        "Relative time (ms)",
94        "RTT (ms)",
95        false,
96        &mut chart,
97    );
98
99    draw_min_rtt(&ss.local_min_rtt, &mut chart);
100    draw_latest_rtt(&ss.local_latest_rtt, &mut chart);
101    draw_smoothed_rtt(&ss.local_smoothed_rtt, &mut chart);
102
103    if params.display_legend {
104        chart
105            .configure_series_labels()
106            .label_font(chart_label_style(&params.colors.caption))
107            .background_style(params.colors.fill.mix(0.8))
108            .border_style(params.colors.axis)
109            .position(SeriesLabelPosition::UpperLeft)
110            .draw()
111            .unwrap();
112    }
113
114    chart
115}
116
117#[cfg(target_arch = "wasm32")]
118pub fn plot_rtt_plot<'a>(
119    params: &PlotParameters, ss: &SeriesStore, canvas_id: &str,
120) -> ChartContext<'a, CanvasBackend, Cartesian2d<RangedCoordf64, RangedCoordf32>>
121{
122    let root =
123        make_chart_canvas_area(&canvas_id, params.colors, params.chart_margin);
124
125    let cwnd_y_max = if let Some(y_max) = params.cwnd_y_max {
126        y_max
127    } else {
128        // add a bit of margin
129        ss.y_max_congestion_plot + ss.y_max_congestion_plot / 10
130    };
131
132    // TODO set minimum
133    let axis = super::minmax::XYMinMax::init(
134        ss.sent_x_min..ss.sent_x_max,
135        params.clamp.start,
136        params.clamp.end,
137        0..cwnd_y_max,
138    );
139
140    draw_rtt_plot(params, &axis, ss, &root)
141}