1use crate::platform::PlatformServices;
6use anyhow::{Error, anyhow};
7use fidl::endpoints::{create_proxy, create_request_stream};
8use fidl_fuchsia_virtualization::{
9 GuestManagerProxy, GuestMarker, GuestStatus, HostVsockAcceptorMarker, HostVsockEndpointMarker,
10};
11use fuchsia_async as fasync;
12use futures::{AsyncReadExt, AsyncWriteExt, FutureExt, TryStreamExt, select, try_join};
13use guest_cli_args as arguments;
14use prettytable::format::consts::FORMAT_CLEAN;
15use prettytable::{Table, row};
16use std::collections::{HashMap, HashSet};
17use std::fmt;
18use std::io::Write;
19
20const LATENCY_CHECK_SIZE_BYTES: usize = 4096;
21const THROUGHPUT_SIZE_MEBIBYTES: usize = 128;
22const THROUGHPUT_SIZE_BYTES: usize = (1 << 20) * THROUGHPUT_SIZE_MEBIBYTES;
23
24const HOST_PORT: u32 = 8500;
25const CONTROL_STREAM: u32 = 8501;
26const LATENCY_CHECK_STREAM: u32 = 8502;
27
28const SINGLE_STREAM_THROUGHPUT: u32 = 8503;
29const SINGLE_STREAM_MAGIC_NUM: u8 = 123;
30
31const MULTI_STREAM_THROUGHPUT1: u32 = 8504;
32const MULTI_STREAM_MAGIC_NUM1: u8 = 124;
33const MULTI_STREAM_THROUGHPUT2: u32 = 8505;
34const MULTI_STREAM_MAGIC_NUM2: u8 = 125;
35const MULTI_STREAM_THROUGHPUT3: u32 = 8506;
36const MULTI_STREAM_MAGIC_NUM3: u8 = 126;
37const MULTI_STREAM_THROUGHPUT4: u32 = 8507;
38const MULTI_STREAM_MAGIC_NUM4: u8 = 127;
39const MULTI_STREAM_THROUGHPUT5: u32 = 8508;
40const MULTI_STREAM_MAGIC_NUM5: u8 = 128;
41
42const SINGLE_STREAM_BIDIRECTIONAL: u32 = 8509;
43#[allow(dead_code)]
44const SINGLE_STREAM_BIDIRECTIONAL_MAGIC_NUM: u8 = 129;
45
46#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
47enum PercentileUnit {
48 Nanoseconds,
49 MebibytesPerSecond,
50}
51
52#[derive(serde::Serialize, serde::Deserialize)]
53pub struct Percentiles {
54 min: f64,
55 p_25th: f64,
56 p_50th: f64,
57 p_75th: f64,
58 p_99th: f64,
59 max: f64,
60 unit: PercentileUnit,
61}
62
63impl fmt::Display for Percentiles {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 let get_units = |val: f64, unit: PercentileUnit| -> String {
66 match unit {
67 PercentileUnit::Nanoseconds => {
68 format!("{}ns ({:.3}ms)", val as u64, val / 1_000_000.0)
69 }
70 PercentileUnit::MebibytesPerSecond => {
71 format!("{:.2}MiB/s", val)
72 }
73 }
74 };
75
76 let mut table = Table::new();
77 table.set_format(*FORMAT_CLEAN);
78
79 table.add_row(row!["\tMin:", get_units(self.min, self.unit)]);
80 table.add_row(row!["\t25th percentile:", get_units(self.p_25th, self.unit)]);
81 table.add_row(row!["\t50th percentile:", get_units(self.p_50th, self.unit)]);
82 table.add_row(row!["\t75th percentile:", get_units(self.p_75th, self.unit)]);
83 table.add_row(row!["\t99th percentile:", get_units(self.p_99th, self.unit)]);
84 table.add_row(row!["\tMax:", get_units(self.max, self.unit)]);
85
86 write!(f, "\n{}", table)
87 }
88}
89
90#[derive(Default, serde::Serialize, serde::Deserialize)]
91pub struct Measurements {
92 data_corruption: Option<bool>,
93 round_trip_page: Option<Percentiles>,
94 tx_throughput: Option<Percentiles>,
95 rx_throughput: Option<Percentiles>,
96 single_stream_unidirectional: Option<Percentiles>,
97 multi_stream_unidirectional: Option<Percentiles>,
98}
99
100impl fmt::Display for Measurements {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 let format_percentiles = |percentiles: &Option<Percentiles>| -> String {
103 match percentiles {
104 None => " NOT RUN".to_owned(),
105 Some(percentile) => percentile.to_string(),
106 }
107 };
108
109 writeln!(f, "\n\nMicrobenchmark Results\n------------------------")?;
110
111 writeln!(
112 f,
113 "* Data corruption check: {}",
114 match self.data_corruption {
115 None => "NOT RUN",
116 Some(result) =>
117 if result {
118 "PASSED"
119 } else {
120 "FAILED"
121 },
122 }
123 )?;
124
125 writeln!(
126 f,
127 "* Round trip latency of {LATENCY_CHECK_SIZE_BYTES} bytes:{}",
128 format_percentiles(&self.round_trip_page)
129 )?;
130 writeln!(
131 f,
132 "* TX (guest -> host, unreliable) throughput of {THROUGHPUT_SIZE_MEBIBYTES} MiB:{}",
133 format_percentiles(&self.tx_throughput)
134 )?;
135 writeln!(
136 f,
137 "* RX (host -> guest, unreliable) throughput of {THROUGHPUT_SIZE_MEBIBYTES} MiB:{}",
138 format_percentiles(&self.rx_throughput)
139 )?;
140 writeln!(
141 f,
142 "* Single stream unidirectional round trip throughput of {THROUGHPUT_SIZE_MEBIBYTES} MiB:{}",
143 format_percentiles(&self.single_stream_unidirectional)
144 )?;
145 writeln!(
146 f,
147 "* Multistream (5 connections) unidirectional round trip throughput of {THROUGHPUT_SIZE_MEBIBYTES} MiB:{}",
148 format_percentiles(&self.multi_stream_unidirectional)
149 )
150 }
151}
152
153#[derive(serde::Serialize, serde::Deserialize)]
154pub enum VsockPerfResult {
155 BenchmarkComplete(Box<Measurements>),
156 UnsupportedGuest(arguments::GuestType),
157 Internal(String),
158}
159
160impl fmt::Display for VsockPerfResult {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 VsockPerfResult::BenchmarkComplete(result) => write!(f, "{}", result),
164 VsockPerfResult::UnsupportedGuest(guest) => {
165 write!(f, "VsockPerf is not supported for '{}'. Only 'debian' is supported", guest)
166 }
167 VsockPerfResult::Internal(context) => {
168 write!(f, "Internal error: {}", context)
169 }
170 }
171 }
172}
173
174fn get_time_delta_nanos(before: fasync::MonotonicInstant, after: fasync::MonotonicInstant) -> i64 {
175 #[cfg(target_os = "fuchsia")]
176 {
177 (after - before).into_nanos()
178 }
179
180 #[cfg(not(target_os = "fuchsia"))]
181 {
182 (after - before).as_nanos().try_into().unwrap()
183 }
184}
185
186pub async fn handle_vsockperf<P: PlatformServices>(
187 services: &P,
188 args: &arguments::vsockperf_args::VsockPerfArgs,
189) -> Result<VsockPerfResult, Error> {
190 if args.guest_type != arguments::GuestType::Debian {
191 return Ok(VsockPerfResult::UnsupportedGuest(args.guest_type));
192 }
193
194 let guest_manager = services.connect_to_manager(args.guest_type).await?;
195 Ok(match run_micro_benchmark(guest_manager).await {
196 Err(err) => VsockPerfResult::Internal(format!("{}", err)),
197 Ok(result) => VsockPerfResult::BenchmarkComplete(Box::new(result)),
198 })
199}
200
201fn percentile(durations: &[u64], percentile: u8) -> u64 {
202 assert!(percentile <= 100 && !durations.is_empty());
203 let location = (((percentile as f64) / 100.0) * ((durations.len() - 1) as f64)) as usize;
205 durations[location]
206}
207
208fn latency_percentile(durations: &[u64]) -> Percentiles {
209 Percentiles {
210 min: percentile(&durations, 0) as f64,
211 p_25th: percentile(&durations, 25) as f64,
212 p_50th: percentile(&durations, 50) as f64,
213 p_75th: percentile(&durations, 75) as f64,
214 p_99th: percentile(&durations, 99) as f64,
215 max: percentile(&durations, 100) as f64,
216 unit: PercentileUnit::Nanoseconds,
217 }
218}
219
220fn throughput_percentile(durations: &[u64], bytes: usize) -> Percentiles {
221 let to_mebibytes_per_second = |nanos: u64| -> f64 {
222 let seconds = nanos as f64 / (1000.0 * 1000.0 * 1000.0);
223 let bytes_per_second = (bytes as f64) / seconds;
224 bytes_per_second / (1 << 20) as f64
225 };
226
227 Percentiles {
228 min: to_mebibytes_per_second(percentile(&durations, 0)),
229 p_25th: to_mebibytes_per_second(percentile(&durations, 25)),
230 p_50th: to_mebibytes_per_second(percentile(&durations, 50)),
231 p_75th: to_mebibytes_per_second(percentile(&durations, 75)),
232 p_99th: to_mebibytes_per_second(percentile(&durations, 99)),
233 max: to_mebibytes_per_second(percentile(&durations, 100)),
234 unit: PercentileUnit::MebibytesPerSecond,
235 }
236}
237
238async fn warmup_and_data_corruption_check(socket: &mut fasync::Socket) -> Result<bool, Error> {
239 let mut buffer = vec![0u8; LATENCY_CHECK_SIZE_BYTES];
241 for i in 0..100 {
242 let pattern = format!("DAVID{:0>3}", i).repeat(512);
243 let packet = pattern.as_bytes();
244 assert_eq!(packet.len(), buffer.len());
245
246 if packet.len() != socket.as_ref().write(&packet)? {
247 return Err(anyhow!("failed to write full packet"));
248 }
249
250 let timeout =
251 fasync::MonotonicInstant::now() + std::time::Duration::from_millis(100).into();
252 select! {
253 () = fasync::Timer::new(timeout).fuse() => {
254 return Err(anyhow!("warmup timed out waiting 100ms for a packet echoed"));
255 }
256 result = socket.read_exact(&mut buffer).fuse() => {
257 result.map_err(|err| anyhow!("failed to read from socket during warmup: {}", err))?;
258 }
259 }
260
261 if buffer != packet {
262 return Ok(false);
263 }
264 }
265
266 Ok(true)
267}
268
269async fn wait_for_magic_numbers(
271 mut numbers: HashSet<u8>,
272 control_socket: &mut fasync::Socket,
273) -> Result<(), Error> {
274 let timeout = fasync::MonotonicInstant::now() + std::time::Duration::from_secs(5).into();
275 let mut magic_buf = [0u8];
276 loop {
277 select! {
278 () = fasync::Timer::new(timeout).fuse() => {
279 return Err(anyhow!("timeout waiting 5s to get the test ready"));
280 }
281 result = control_socket.read_exact(&mut magic_buf).fuse() => {
282 result.map_err(|err| anyhow!("failed to read magic value from socket: {}", err))?;
283 match numbers.contains(&magic_buf[0]) {
284 false => Err(anyhow!("unexpected magic number from guest: {}", magic_buf[0])),
285 true => {
286 numbers.remove(&magic_buf[0]);
287 Ok(())
288 }
289 }?;
290
291 if numbers.is_empty() {
292 break;
293 }
294 }
295 }
296 }
297
298 Ok(())
299}
300
301async fn read_single_stream(
302 total_size: usize,
303 socket: &mut fasync::Socket,
304) -> Result<fasync::MonotonicInstant, Error> {
305 let timeout = fasync::MonotonicInstant::now() + std::time::Duration::from_secs(10).into();
306 let mut buffer = vec![0u8; LATENCY_CHECK_SIZE_BYTES]; let segments = total_size / buffer.len();
308
309 for _ in 0..segments {
310 select! {
311 () = fasync::Timer::new(timeout).fuse() => {
312 return Err(anyhow!("timeout waiting 10s for test iteration read to finish"));
313 }
314 result = socket.read_exact(&mut buffer).fuse() => {
315 result.map_err(|err| anyhow!("failed to read segment from socket: {}", err))?;
316 }
317 }
318 }
319
320 Ok(fasync::MonotonicInstant::now())
321}
322
323async fn write_single_stream(
324 total_size: usize,
325 socket: &mut fasync::Socket,
326) -> Result<fasync::MonotonicInstant, Error> {
327 let timeout = fasync::MonotonicInstant::now() + std::time::Duration::from_secs(10).into();
328 let buffer = vec![0u8; LATENCY_CHECK_SIZE_BYTES]; let segments = total_size / buffer.len();
330
331 for _ in 0..segments {
332 select! {
333 () = fasync::Timer::new(timeout).fuse() => {
334 return Err(anyhow!("timeout waiting 10s for test iteration write to finish"));
335 }
336 result = socket.write_all(&buffer).fuse() => {
337 result.map_err(
338 |err| anyhow!("failed to write segment to socket: {}", err))?;
339 }
340 }
341 }
342
343 Ok(fasync::MonotonicInstant::now())
344}
345
346async fn write_read_high_throughput(
347 total_size: usize,
348 socket: &mut fasync::Socket,
349) -> Result<(), Error> {
350 write_single_stream(total_size, socket).await?;
353 read_single_stream(total_size, socket).await?;
354 Ok(())
355}
356
357#[cfg(target_os = "fuchsia")]
358async fn run_single_stream_bidirectional_test(
359 mut read_socket: fasync::Socket,
360 control_socket: &mut fasync::Socket,
361 measurements: &mut Measurements,
362) -> Result<(), Error> {
363 println!("Starting single stream bidirectional round trip throughput test...");
364
365 let mut write_socket = fasync::Socket::from_socket(
366 read_socket.as_ref().duplicate_handle(fidl::Rights::SAME_RIGHTS)?,
367 );
368
369 wait_for_magic_numbers(HashSet::from([SINGLE_STREAM_BIDIRECTIONAL_MAGIC_NUM]), control_socket)
370 .await?;
371
372 let total_size = THROUGHPUT_SIZE_BYTES;
373 let mut rx_durations: Vec<u64> = Vec::new();
374 let mut tx_durations: Vec<u64> = Vec::new();
375
376 for i in 0..100 {
377 let before = fasync::MonotonicInstant::now();
378
379 let (write_finish, read_finish) = try_join!(
380 write_single_stream(total_size, &mut write_socket),
381 read_single_stream(total_size, &mut read_socket)
382 )?;
383
384 rx_durations.push(
385 get_time_delta_nanos(before, write_finish)
386 .try_into()
387 .expect("durations measured by the same thread must be greater than zero"),
388 );
389
390 tx_durations.push(
391 get_time_delta_nanos(before, read_finish)
392 .try_into()
393 .expect("durations measured by the same thread must be greater than zero"),
394 );
395
396 print!("\rFinished {} bidirectional throughput measurements", i + 1);
397 std::io::stdout().flush().expect("failed to flush stdout");
398 }
399
400 rx_durations.sort();
401 rx_durations.reverse();
402
403 tx_durations.sort();
404 tx_durations.reverse();
405
406 assert_eq!(rx_durations.len(), tx_durations.len());
407 println!("\rFinished {} bidirectional throughput measurements", rx_durations.len());
408
409 measurements.tx_throughput = Some(throughput_percentile(&tx_durations, total_size));
410 measurements.rx_throughput = Some(throughput_percentile(&rx_durations, total_size));
411
412 Ok(())
413}
414
415async fn run_single_stream_unidirectional_round_trip_test(
416 mut data_socket: fasync::Socket,
417 control_socket: &mut fasync::Socket,
418 measurements: &mut Measurements,
419) -> Result<(), Error> {
420 println!("Starting single stream unidirectional round trip throughput test...");
421
422 wait_for_magic_numbers(HashSet::from([SINGLE_STREAM_MAGIC_NUM]), control_socket).await?;
423
424 let total_size = THROUGHPUT_SIZE_BYTES;
425 let mut durations: Vec<u64> = Vec::new();
426
427 for i in 0..100 {
428 let before = fasync::MonotonicInstant::now();
429
430 write_read_high_throughput(total_size, &mut data_socket).await?;
431
432 let after = fasync::MonotonicInstant::now();
433 durations.push(
434 get_time_delta_nanos(before, after)
435 .try_into()
436 .expect("durations measured by the same thread must be greater than zero"),
437 );
438
439 print!("\rFinished {} round trip throughput measurements", i + 1);
440 std::io::stdout().flush().expect("failed to flush stdout");
441 }
442
443 durations.sort();
444 durations.reverse();
445 println!("\rFinished {} single stream round trip throughput measurements", durations.len());
446
447 measurements.single_stream_unidirectional =
448 Some(throughput_percentile(&durations, total_size * 2));
449
450 Ok(())
451}
452
453async fn run_multi_stream_unidirectional_round_trip_test(
454 mut data_socket1: fasync::Socket,
455 mut data_socket2: fasync::Socket,
456 mut data_socket3: fasync::Socket,
457 mut data_socket4: fasync::Socket,
458 mut data_socket5: fasync::Socket,
459 control_socket: &mut fasync::Socket,
460 measurements: &mut Measurements,
461) -> Result<(), Error> {
462 println!("Starting multistream unidirectional round trip throughput test...");
463
464 wait_for_magic_numbers(
465 HashSet::from([
466 MULTI_STREAM_MAGIC_NUM1,
467 MULTI_STREAM_MAGIC_NUM2,
468 MULTI_STREAM_MAGIC_NUM3,
469 MULTI_STREAM_MAGIC_NUM4,
470 MULTI_STREAM_MAGIC_NUM5,
471 ]),
472 control_socket,
473 )
474 .await?;
475
476 let total_size = THROUGHPUT_SIZE_BYTES;
477 let mut durations: Vec<u64> = Vec::new();
478
479 for i in 0..50 {
480 let before = fasync::MonotonicInstant::now();
481
482 try_join!(
483 write_read_high_throughput(total_size, &mut data_socket1),
484 write_read_high_throughput(total_size, &mut data_socket2),
485 write_read_high_throughput(total_size, &mut data_socket3),
486 write_read_high_throughput(total_size, &mut data_socket4),
487 write_read_high_throughput(total_size, &mut data_socket5)
488 )?;
489
490 let after = fasync::MonotonicInstant::now();
491 durations.push(
492 get_time_delta_nanos(before, after)
493 .try_into()
494 .expect("durations measured by the same thread must be greater than zero"),
495 );
496
497 print!("\rFinished {} multistream round trip throughput measurements", i + 1);
498 std::io::stdout().flush().expect("failed to flush stdout");
499 }
500
501 durations.sort();
502 durations.reverse();
503 println!("\rFinished {} multistream round trip throughput measurements", durations.len());
504
505 measurements.multi_stream_unidirectional =
506 Some(throughput_percentile(&durations, total_size * 2));
507
508 Ok(())
509}
510
511async fn run_latency_test(
512 mut socket: fasync::Socket,
513 measurements: &mut Measurements,
514) -> Result<(), Error> {
515 println!("Checking for data corruption...");
516 measurements.data_corruption = Some(warmup_and_data_corruption_check(&mut socket).await?);
517 println!("Finished data corruption check");
518
519 let packet = [42u8; LATENCY_CHECK_SIZE_BYTES];
520 let mut buffer = vec![0u8; packet.len()];
521 let mut latencies: Vec<u64> = Vec::new();
522
523 println!("Starting latency test...");
524 for i in 0..10000 {
525 let before = fasync::MonotonicInstant::now();
526 let timeout = before + std::time::Duration::from_millis(100).into();
527
528 if packet.len() != socket.as_ref().write(&packet)? {
529 return Err(anyhow!("failed to write full packet"));
530 }
531
532 select! {
533 () = fasync::Timer::new(timeout).fuse() => {
534 return Err(anyhow!("latency test timed out waiting 100ms for a packet echoed"));
535 }
536 result = socket.read_exact(&mut buffer).fuse() => {
537 result.map_err(
538 |err| anyhow!("failed to read from socket during latency test: {}", err))?;
539 }
540 }
541
542 let after = fasync::MonotonicInstant::now();
543 latencies.push(
544 get_time_delta_nanos(before, after)
545 .try_into()
546 .expect("durations measured by the same thread must be greater than zero"),
547 );
548
549 if (i + 1) % 50 == 0 {
550 print!("\rFinished measuring round trip latency for {} packets", i + 1);
551 std::io::stdout().flush().expect("failed to flush stdout");
552 }
553 }
554
555 latencies.sort();
556 println!("\rFinished measuring round trip latency for {} packets", latencies.len());
557
558 measurements.round_trip_page = Some(latency_percentile(&latencies));
559
560 Ok(())
561}
562
563async fn run_micro_benchmark(guest_manager: GuestManagerProxy) -> Result<Measurements, Error> {
564 let guest_info = guest_manager.get_info().await?;
565 if guest_info.guest_status.unwrap() != GuestStatus::Running {
566 return Err(anyhow!(zx_status::Status::NOT_CONNECTED));
567 }
568
569 let (guest_endpoint, guest_server_end) = create_proxy::<GuestMarker>();
570 guest_manager
571 .connect(guest_server_end)
572 .await
573 .map_err(|err| anyhow!("failed to get a connect response: {}", err))?
574 .map_err(|err| anyhow!("connect failed with: {:?}", err))?;
575
576 let (vsock_endpoint, vsock_server_end) = create_proxy::<HostVsockEndpointMarker>();
577 guest_endpoint
578 .get_host_vsock_endpoint(vsock_server_end)
579 .await?
580 .map_err(|err| anyhow!("failed to get HostVsockEndpoint: {:?}", err))?;
581
582 let (acceptor, mut client_stream) = create_request_stream::<HostVsockAcceptorMarker>();
583 vsock_endpoint
584 .listen(HOST_PORT, acceptor)
585 .await
586 .map_err(|err| anyhow!("failed to get a listen response: {}", err))?
587 .map_err(|err| anyhow!("listen failed with: {}", zx_status::Status::err_from_raw(err)))?;
588
589 let socket = guest_endpoint
590 .get_console()
591 .await
592 .map_err(|err| anyhow!("failed to get a get_console response: {}", err))?
593 .map_err(|err| anyhow!("get_console failed with: {:?}", err))?;
594
595 let command = b"../test_utils/virtio_vsock_test_util micro_benchmark\n";
598 let bytes_written = socket
599 .write(command)
600 .map_err(|err| anyhow!("failed to write command to socket: {}", err))?;
601 if bytes_written != command.len() {
602 return Err(anyhow!(
603 "attempted to send command '{}', but only managed to write '{}'",
604 std::str::from_utf8(command).expect("failed to parse as utf-8"),
605 std::str::from_utf8(&command[0..bytes_written]).expect("failed to parse as utf-8")
606 ));
607 }
608
609 let mut expected_connections = HashSet::from([
610 CONTROL_STREAM,
611 LATENCY_CHECK_STREAM,
612 SINGLE_STREAM_THROUGHPUT,
613 MULTI_STREAM_THROUGHPUT1,
614 MULTI_STREAM_THROUGHPUT2,
615 MULTI_STREAM_THROUGHPUT3,
616 MULTI_STREAM_THROUGHPUT4,
617 MULTI_STREAM_THROUGHPUT5,
618 SINGLE_STREAM_BIDIRECTIONAL,
619 ]);
620 let mut active_connections = HashMap::new();
621
622 let timeout = fasync::MonotonicInstant::now() + std::time::Duration::from_secs(15).into();
624 loop {
625 select! {
626 () = fasync::Timer::new(timeout).fuse() => {
627 return Err(anyhow!("vsockperf timed out waiting 15s for vsock connections"));
628 }
629 request = client_stream.try_next() => {
630 let request = request
631 .map_err(|err| anyhow!("failed to get acceptor request: {}", err))?
632 .ok_or_else(|| anyhow!("unexpected end of Listener stream"))?;
633 let (_src_cid, src_port, _port, responder) = request
634 .into_accept().ok_or_else(|| anyhow!("failed to parse message as Accept"))?;
635
636 match expected_connections.contains(&src_port) {
637 false => Err(anyhow!("unexpected connection from guest port: {}", src_port)),
638 true => {
639 expected_connections.remove(&src_port);
640 Ok(())
641 }
642 }?;
643
644 let (client_socket, device_socket) = fidl::Socket::create_stream();
645 let client_socket = fasync::Socket::from_socket(client_socket);
646
647 responder.send(Ok(device_socket))
648 .map_err(|err| anyhow!("failed to send response to device: {}", err))?;
649
650 if let Some(_) = active_connections.insert(src_port, client_socket) {
651 panic!("Connections must be unique");
652 }
653
654 if expected_connections.is_empty() {
655 break;
656 }
657 }
658 }
659 }
660
661 let mut measurements = Measurements::default();
662
663 run_latency_test(
664 active_connections.remove(&LATENCY_CHECK_STREAM).expect("socket should exist"),
665 &mut measurements,
666 )
667 .await?;
668
669 #[cfg(target_os = "fuchsia")]
671 run_single_stream_bidirectional_test(
672 active_connections.remove(&SINGLE_STREAM_BIDIRECTIONAL).expect("socket should exist"),
673 active_connections.get_mut(&CONTROL_STREAM).expect("socket should exist"),
674 &mut measurements,
675 )
676 .await?;
677
678 run_single_stream_unidirectional_round_trip_test(
679 active_connections.remove(&SINGLE_STREAM_THROUGHPUT).expect("socket should exist"),
680 active_connections.get_mut(&CONTROL_STREAM).expect("socket should exist"),
681 &mut measurements,
682 )
683 .await?;
684
685 run_multi_stream_unidirectional_round_trip_test(
686 active_connections.remove(&MULTI_STREAM_THROUGHPUT1).expect("socket should exist"),
687 active_connections.remove(&MULTI_STREAM_THROUGHPUT2).expect("socket should exist"),
688 active_connections.remove(&MULTI_STREAM_THROUGHPUT3).expect("socket should exist"),
689 active_connections.remove(&MULTI_STREAM_THROUGHPUT4).expect("socket should exist"),
690 active_connections.remove(&MULTI_STREAM_THROUGHPUT5).expect("socket should exist"),
691 active_connections.get_mut(&CONTROL_STREAM).expect("socket should exist"),
692 &mut measurements,
693 )
694 .await?;
695
696 return Ok(measurements);
697}