1use minmax::XMinMax;
28use plotters::chart::ChartContext;
29use plotters::coord::types::RangedCoordf64;
30use plotters::coord::types::RangedCoordu64;
31use plotters::prelude::Cartesian2d;
32
33use crate::plots::colors::*;
34use crate::plots::*;
35
36use crate::datastore::Datastore;
37use crate::seriesstore::SeriesStore;
38
39const Y_WIGGLE: f64 = 1.2;
40
41struct XYMinMax {
42 pub x: XMinMax,
43 pub y_min: u64,
44 pub y_max: u64,
45}
46
47impl XYMinMax {
48 fn init(params: &PlotParameters, ss: &SeriesStore, ds: &Datastore) -> Self {
49 let x = XMinMax::new(
50 ss.received_x_min,
51 ss.received_x_max,
52 params.clamp.start,
53 params.clamp.end,
54 );
55
56 Self {
57 x,
58 y_min: 0,
59 y_max: Self::y_max(ss, &ds.application_proto),
60 }
61 }
62
63 fn y_range(&self) -> std::ops::Range<u64> {
64 self.y_min..self.y_max
65 }
66
67 fn y_max(ss: &SeriesStore, proto: &ApplicationProto) -> u64 {
68 let y = match proto {
69 ApplicationProto::Http2 =>
70 *(ss.h2_send_window_absolute_max.get(&0).unwrap_or(&0)),
71 ApplicationProto::Http3 =>
72 ss.netlog_quic_stream_received_connection_cumulative
73 .last()
74 .unwrap_or(&(0.0, 0))
75 .1,
76 };
77
78 (y as f64 * Y_WIGGLE) as u64
79 }
80}
81
82fn blocked_lines<DB: DrawingBackend>(
83 ds: &Datastore, y_max: u64,
84 chart: &mut ChartContext<'_, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>>,
85) {
86 if let Some(blocked) = ds.netlog_quic_server_window_blocked.get(&-1).cloned()
87 {
88 let blocked_lines = blocked
89 .iter()
90 .map(|t| PathElement::new([(*t, 0), (*t, y_max)], GREEN));
91
92 chart
93 .draw_series(blocked_lines)
94 .unwrap()
95 .label("server conn blocked")
96 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], GREEN));
97 }
98}
99
100fn received_data<DB: DrawingBackend>(
101 ss: &SeriesStore, proto: &ApplicationProto,
102 chart: &mut ChartContext<'_, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>>,
103) {
104 let points = match proto {
105 ApplicationProto::Http2 =>
106 ss.netlog_h2_stream_received_connection_cumulative.clone(),
107 ApplicationProto::Http3 =>
108 ss.netlog_quic_stream_received_connection_cumulative.clone(),
109 };
110
111 chart
112 .draw_series(LineSeries::new(points, ORANGE))
113 .unwrap()
114 .label("cumulative received stream data")
115 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], ORANGE));
116}
117
118fn window_updates<DB: DrawingBackend>(
119 ss: &SeriesStore, ds: &Datastore,
120 chart: &mut ChartContext<'_, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>>,
121) {
122 let points = match ds.application_proto {
123 ApplicationProto::Http2 => {
124 if let Some(conn_win_updates) =
125 ss.h2_send_window_series_absolute.get(&0).cloned()
126 {
127 conn_win_updates
128 } else {
129 return;
130 }
131 },
132
133 ApplicationProto::Http3 => {
134 if let Some(conn_win_updates) =
135 ss.netlog_quic_client_side_window_updates.get(&-1).cloned()
136 {
137 conn_win_updates
138 } else {
139 return;
140 }
141 },
142 };
143
144 let circles: Vec<Circle<(f64, u64), i32>> = points
146 .iter()
147 .map(|point| Circle::new(*point, 2, BLUE))
148 .collect();
149
150 chart
151 .draw_series(LineSeries::new(points, BLUE))
152 .unwrap()
153 .label("sent window updates")
154 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
155
156 chart.draw_series(circles).unwrap();
157}
158
159fn draw_series<'a, DB: DrawingBackend + 'a>(
160 chart: &mut ChartContext<'a, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>>,
161 params: &PlotParameters, ss: &SeriesStore, ds: &Datastore, axis: XYMinMax,
162) {
163 draw_mesh(
164 ¶ms.colors,
165 "Relative time (ms)",
166 "Bytes",
167 params.display_minor_lines,
168 chart,
169 );
170
171 blocked_lines(ds, axis.y_max, chart);
173 received_data(ss, &ds.application_proto, chart);
174 window_updates(ss, ds, chart);
175
176 if params.display_legend {
177 chart
179 .configure_series_labels()
180 .label_font(chart_label_style(¶ms.colors.caption))
181 .background_style(params.colors.fill.mix(0.8))
182 .border_style(params.colors.axis)
183 .position(SeriesLabelPosition::LowerRight)
184 .draw()
185 .unwrap();
186 }
187}
188
189#[cfg(target_arch = "wasm32")]
190pub fn plot_conn_flow_control_canvas<'a>(
191 params: &PlotParameters, filename: &str, ss: &SeriesStore, ds: &Datastore,
192 ty: &ChartOutputType,
193) -> ChartContext<'a, CanvasBackend, Cartesian2d<RangedCoordf64, RangedCoordu64>>
194{
195 let chart_config =
196 make_chart_config("flow_control", params, filename, ds, ty);
197
198 let canvas_id: String = chart_config.canvas_id().unwrap_or_default();
199 let root =
200 make_chart_canvas_area(&canvas_id, params.colors, params.chart_margin);
201
202 let axis = XYMinMax::init(params, ss, ds);
203
204 let mut builder = ChartBuilder::on(&root);
205 builder
206 .x_label_area_size(params.area_margin.x)
207 .y_label_area_size(params.area_margin.y);
208
209 if params.display_chart_title {
210 let caption = format!("{} Connection Flow Control timeline", filename);
211 builder.caption(caption, chart_title_style(¶ms.colors.caption));
212 }
213
214 let mut fc_chart = builder
215 .build_cartesian_2d(axis.x.range(), axis.y_range())
216 .unwrap();
217
218 draw_series(&mut fc_chart, params, ss, ds, axis);
219
220 fc_chart
221}
222
223#[cfg(not(target_arch = "wasm32"))]
224pub fn plot_conn_flow_control(
225 params: &PlotParameters, filename: &str, ss: &SeriesStore, ds: &Datastore,
226 ty: &ChartOutputType,
227) {
228 let chart_config =
229 make_chart_config("flow_control", params, filename, ds, ty);
230 let chart_path = chart_config.chart_filepath();
231
232 let root = make_chart_bitmap_area(
233 &chart_path,
234 params.chart_size,
235 params.colors,
236 params.chart_margin,
237 );
238
239 let axis = XYMinMax::init(params, ss, ds);
240
241 let mut builder = ChartBuilder::on(&root);
242 builder
243 .x_label_area_size(params.area_margin.x)
244 .y_label_area_size(params.area_margin.y);
245
246 if params.display_chart_title {
247 let caption = format!("{} Connection Flow Control timeline", filename);
248 builder.caption(caption, chart_title_style(¶ms.colors.caption));
249 }
250
251 let mut fc_chart = builder
252 .build_cartesian_2d(axis.x.range(), axis.y_range())
253 .unwrap();
254
255 draw_mesh(
256 ¶ms.colors,
257 "Relative time (ms)",
258 "Bytes",
259 params.display_minor_lines,
260 &mut fc_chart,
261 );
262
263 draw_series(&mut fc_chart, params, ss, ds, axis);
264}