1use log::warn;
31
32use crate::datastore::VantagePoint;
33use crate::plots::colors::*;
34use crate::plots::*;
35
36use crate::datastore::Datastore;
37use crate::seriesstore::SeriesStore;
38
39pub struct MultiplexPlotsParams {
40 pub clamp: ClampParams,
41 pub bidi_only: bool,
42 pub width: u32,
43 pub y_spacer: u32,
44 pub right_shrink_margin: u32,
45 pub combined_plot_height: u32,
46 pub frame_swimlane_height: u32,
47 pub bubbles_swimlane_height: u32,
48 pub colors: PlotColors,
49 pub area_margin: AreaMargin,
50}
51
52impl Default for MultiplexPlotsParams {
53 fn default() -> Self {
54 Self {
55 bidi_only: true,
56 width: 4000,
57 y_spacer: 20,
58 right_shrink_margin: 50,
59 combined_plot_height: 300,
60 frame_swimlane_height: 60,
61 bubbles_swimlane_height: 100,
62 clamp: Default::default(),
63 colors: Default::default(),
64 area_margin: AreaMargin { x: 40, y: 0 },
65 }
66 }
67}
68
69pub fn plot_stream_multiplexing(
70 params: &MultiplexPlotsParams, filename: &str, ss: &SeriesStore,
71 ds: &Datastore, ty: &ChartOutputType,
72) {
73 let (mut x_min, mut x_max) = match ds.vantage_point {
74 VantagePoint::Client => (ss.received_x_min, ss.received_x_max),
75 VantagePoint::Server => (ss.sent_x_min, ss.sent_x_max),
76 };
77
78 if let Some(s) = params.clamp.start {
80 x_min = s;
81 }
82
83 if let Some(e) = params.clamp.end {
84 x_max = x_max.min(e);
85 }
86
87 let http_requests = match ds.vantage_point {
88 VantagePoint::Client => &ds.http_requests,
89 VantagePoint::Server => &ds.http_requests,
90 };
91
92 let y_spacer = 50;
93
94 let data_frames_all_combined_x = params.width - params.right_shrink_margin;
95 let data_frames_all_combined_y: i32 = params.combined_plot_height as i32;
96
97 let data_frames_mini_offset_y = data_frames_all_combined_y + y_spacer;
98 let data_frames_mini_x = params.width - params.right_shrink_margin;
99 let data_frames_mini_y = params.frame_swimlane_height;
100
101 let bubbles_x = params.width - params.right_shrink_margin;
102 let bubbles_y = params.bubbles_swimlane_height;
103 let bubbles_offset_y =
104 data_frames_mini_offset_y + (http_requests.len() * 25) as i32;
105
106 let stream_frames_all_combined_x = params.width - params.right_shrink_margin;
107 let stream_frames_all_combined_y = params.combined_plot_height;
108 let stream_frames_all_combined_offset_y =
109 bubbles_offset_y + (http_requests.len() * 70) as i32;
110
111 let stream_frames_mini_offset_y = stream_frames_all_combined_offset_y +
112 stream_frames_all_combined_y as i32 +
113 y_spacer;
114 let stream_frames_mini_x = params.width - params.right_shrink_margin;
115 let stream_frames_mini_y = params.frame_swimlane_height;
116
117 #[cfg(not(target_arch = "wasm32"))]
118 let full_y =
119 stream_frames_mini_offset_y as u32 + (http_requests.len() * 25) as u32;
120
121 let chart_config = ChartConfig {
122 title: "stream-multiplexing".into(),
123 input_filename: filename.into(),
124 clamp: params.clamp.clone(),
125 app_proto: ds.application_proto,
126 host: ds.host.clone(),
127 session_id: ds.session_id,
128 ty: ty.clone(),
129 };
130
131 chart_config.init_chart_dir();
132
133 let margin = ChartMargin {
134 top: 20,
135 bottom: 20,
136 left: 20,
137 right: 20,
138 };
139
140 #[cfg(not(target_arch = "wasm32"))]
141 let size = ChartSize {
142 width: params.width,
143 height: full_y,
144 };
145
146 #[cfg(not(target_arch = "wasm32"))]
147 let chart_path = chart_config.chart_filepath();
148
149 #[cfg(not(target_arch = "wasm32"))]
150 let root = make_chart_bitmap_area(&chart_path, size, params.colors, margin);
151
152 #[cfg(target_arch = "wasm32")]
153 let canvas_id: String = chart_config.canvas_id().unwrap_or_default();
154
155 #[cfg(target_arch = "wasm32")]
156 let root = make_chart_canvas_area(&canvas_id, params.colors, margin);
157
158 let stream_frame_series = match ds.vantage_point {
159 VantagePoint::Client => &ss.received_stream_frames_series,
160 VantagePoint::Server => &ss.sent_stream_frames_series,
161 };
162
163 let mut color_cyle = ColorCycle::default();
165
166 color_cyle.reset();
167
168 let data_frames_all_combined_temp_root = root.clone().shrink(
169 (0, 0),
170 (data_frames_all_combined_x, data_frames_all_combined_y),
171 );
172
173 let mut data_frames_all_combined_chart =
174 ChartBuilder::on(&data_frames_all_combined_temp_root)
175 .caption(
176 "DATA frame events",
177 chart_title_style(¶ms.colors.caption),
178 )
179 .x_label_area_size(params.area_margin.x)
180 .y_label_area_size(params.area_margin.y)
181 .build_cartesian_2d(x_min..x_max, 0..2)
182 .unwrap();
183
184 data_frames_all_combined_chart
185 .configure_mesh()
186 .disable_mesh()
187 .axis_style(params.colors.axis)
188 .label_style(chart_label_style(¶ms.colors.caption))
189 .draw()
190 .unwrap();
191
192 for (id, stub) in http_requests {
193 if params.bidi_only && (id & 0x2) != 0 {
194 continue;
195 }
196
197 let data_frames = match ds.vantage_point {
198 VantagePoint::Client => &stub.time_data_rx_set,
199 VantagePoint::Server => &stub.time_data_tx_set,
200 };
201
202 let style = color_cyle.next_color();
203
204 let lines = data_frames
205 .iter()
206 .map(|point| PathElement::new([(point.0, 2), (point.0, 0)], style));
207
208 data_frames_all_combined_chart
209 .draw_series(lines.clone())
210 .unwrap();
211 }
212
213 color_cyle.reset();
215
216 let data_frames_all_combined_temp_root = root.clone().shrink(
217 (0, stream_frames_all_combined_offset_y),
218 (stream_frames_all_combined_x, stream_frames_all_combined_y),
219 );
220
221 let mut stream_frames_all_combined_chart =
222 ChartBuilder::on(&data_frames_all_combined_temp_root)
223 .caption(
224 "STREAM frame events",
225 chart_title_style(¶ms.colors.caption),
226 )
227 .x_label_area_size(params.area_margin.x)
228 .y_label_area_size(params.area_margin.y)
229 .build_cartesian_2d(x_min..x_max, 0..1)
230 .unwrap();
231
232 stream_frames_all_combined_chart
233 .configure_mesh()
234 .disable_mesh()
235 .axis_style(params.colors.axis)
236 .label_style(chart_label_style(¶ms.colors.caption))
237 .draw()
238 .unwrap();
239
240 for (id, stream_frames) in stream_frame_series {
241 if params.bidi_only && (id & 0x2) != 0 {
242 continue;
243 }
244
245 let style = color_cyle.next_color();
246
247 let lines = stream_frames
248 .iter()
249 .map(|point| PathElement::new([(point.0, 2), (point.0, 0)], style));
250
251 stream_frames_all_combined_chart.draw_series(lines).unwrap();
252 }
253
254 color_cyle.reset();
256
257 let mut upper_y = 0;
258 let mut bubbles_upper_y = 0;
259 let mini_chart_axis_y_range = 0..4;
260 let marker_y = 2;
261 let marker_size = 5;
262
263 let max_data_len = match ds.vantage_point {
264 VantagePoint::Client => ds.largest_data_frame_rx_length_global,
265 VantagePoint::Server => ds.largest_data_frame_tx_length_global,
266 };
267
268 if max_data_len == 0 {
269 warn!("max_data_len = 0, skipping remaining charts");
270 return;
271 }
272
273 for (id, stub) in http_requests {
274 if params.bidi_only && (id & 0x2) != 0 {
275 continue;
276 }
277
278 let data_frames = match ds.vantage_point {
279 VantagePoint::Client => &stub.time_data_rx_set,
280 VantagePoint::Server => &stub.time_data_tx_set,
281 };
282
283 let headers_init = match ds.vantage_point {
284 VantagePoint::Client => stub.time_first_headers_tx,
285 VantagePoint::Server => stub.time_first_headers_rx,
286 };
287
288 let headers_actioned = match ds.vantage_point {
289 VantagePoint::Client => stub.time_first_headers_rx,
290 VantagePoint::Server => stub.time_first_headers_tx,
291 };
292
293 let data_frames_temp_root = root.clone().shrink(
294 (0, upper_y + data_frames_mini_offset_y),
295 (data_frames_mini_x, data_frames_mini_y),
296 );
297
298 let stream_frames_temp_root = root.clone().shrink(
299 (0, upper_y + stream_frames_mini_offset_y),
300 (stream_frames_mini_x, stream_frames_mini_y),
301 );
302
303 upper_y += 20;
304
305 let mut data_frames_mini_independent_chart =
306 ChartBuilder::on(&data_frames_temp_root)
307 .x_label_area_size(params.area_margin.x)
308 .y_label_area_size(params.area_margin.y)
309 .build_cartesian_2d(x_min..x_max, mini_chart_axis_y_range.clone())
310 .unwrap();
311
312 data_frames_mini_independent_chart
313 .configure_mesh()
314 .disable_y_mesh()
315 .disable_y_axis()
316 .axis_style(params.colors.axis)
317 .draw()
318 .unwrap();
319
320 let mut stream_frames_mini_independent_chart =
323 ChartBuilder::on(&stream_frames_temp_root)
324 .x_label_area_size(params.area_margin.x)
325 .y_label_area_size(params.area_margin.y)
326 .build_cartesian_2d(x_min..x_max, mini_chart_axis_y_range.clone())
327 .unwrap();
328
329 stream_frames_mini_independent_chart
330 .configure_mesh()
331 .disable_y_mesh()
332 .disable_y_axis()
333 .axis_style(params.colors.axis)
334 .draw()
335 .unwrap();
336
337 let style = color_cyle.next_color();
338
339 if let Some(h) = headers_init {
340 data_frames_mini_independent_chart
341 .draw_series([TriangleMarker::new(
342 (h, marker_y),
343 marker_size,
344 style,
345 )])
346 .unwrap();
347
348 stream_frames_mini_independent_chart
349 .draw_series([TriangleMarker::new(
350 (h, marker_y),
351 marker_size,
352 style,
353 )])
354 .unwrap();
355 }
356
357 if let Some(h) = headers_actioned {
358 data_frames_mini_independent_chart
359 .draw_series([TriangleMarker::new(
360 (h, marker_y),
361 marker_size,
362 style,
363 )])
364 .unwrap();
365
366 stream_frames_mini_independent_chart
367 .draw_series([TriangleMarker::new(
368 (h, marker_y),
369 marker_size,
370 style,
371 )])
372 .unwrap();
373 }
374
375 let lines = data_frames
376 .iter()
377 .map(|point| PathElement::new([(point.0, 2), (point.0, 0)], style));
378
379 data_frames_mini_independent_chart
380 .draw_series(lines.clone())
381 .unwrap();
382
383 if let Some(stream_frames) = stream_frame_series.get(id) {
384 let lines = stream_frames.iter().map(|point| {
385 PathElement::new([(point.0, 2), (point.0, 0)], style)
386 });
387
388 stream_frames_mini_independent_chart
389 .draw_series(lines.clone())
390 .unwrap();
391 }
392
393 let bubbles_temp_root = root.clone().shrink(
394 (0, bubbles_upper_y + bubbles_offset_y),
395 (bubbles_x, bubbles_y),
396 );
397
398 bubbles_upper_y += 70;
399
400 let mut bubbles_mini_chart = ChartBuilder::on(&bubbles_temp_root)
401 .x_label_area_size(params.area_margin.x)
402 .y_label_area_size(params.area_margin.y)
403 .build_cartesian_2d(
404 x_min..x_max,
405 0..max_data_len, )
407 .unwrap();
408
409 bubbles_mini_chart
410 .configure_mesh()
411 .disable_y_mesh()
412 .disable_y_axis()
413 .axis_style(params.colors.axis)
414 .draw()
415 .unwrap();
416
417 let bucket_size = if max_data_len >= 5 {
420 max_data_len / 5
421 } else {
422 1
423 };
424
425 let mut bubbles: Vec<Circle<(f64, i32), i32>> = vec![];
426 let mut lines: Vec<PathElement<(f64, i32)>> = vec![];
427 let mut lines2: Vec<PathElement<(f64, u64)>> = vec![];
428
429 let bubble_sizes = [1, 6, 11, 16, 21, 26, 31];
430
431 for (time, length) in data_frames {
432 let i: usize = (length / bucket_size) as usize +
434 usize::from(length % bucket_size != 0);
435 let i = i.saturating_sub(1);
436
437 if time > &x_max {
440 continue;
441 }
442
443 lines.push(PathElement::new(
444 [(*time, 2), (*time, 2 + bubble_sizes[i])],
445 style,
446 ));
447
448 lines2.push(PathElement::new([(*time, 0), (*time, *length)], style));
449
450 bubbles.push(Circle::new((*time, 2), bubble_sizes[i], style));
451 }
452
453 bubbles_mini_chart.draw_series(lines2).unwrap();
455 }
456}