Skip to content

Commit 62cc2cf

Browse files
leftwoAlan Hanson
and
Alan Hanson
authored
DTrace probes for IO on/off the network (#1284)
Put in DTrace probes for when a message is put on the wire to a downstairs, and a corresponding probe when a message is pulled off the wire from a downstairs. Updated the README, and moved a few D scripts around. Left the existing probes that were previously marking networking but had drifted off to measuring something else where they were, but updated the comments to reflect their new role. Added a new D script, `perf-ds-net.d`, for the new probes. Moved the old `perfdw.d` to be `perf-ds-client.d`. --------- Co-authored-by: Alan Hanson <alan@oxide.computer>
1 parent 11c2cad commit 62cc2cf

File tree

6 files changed

+129
-27
lines changed

6 files changed

+129
-27
lines changed

tools/dtrace/README.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,22 @@ queue of work for the three downstairs.
8282
IO that it is ready to ack.
8383
3: From the IO being ready to ack, to that ack being sent.
8484

85-
## perfdw.d
86-
This is a simple dtrace script that measures latency times for when a r/w/f
85+
## perf-ds-client.d
86+
A DTrace script that records the time in the Upstairs from when a Message
87+
is sent to a client task to when that client task returns the response.
88+
89+
## perf-ds-net.d
90+
This is a simple DTrace script that measures latency times for when a r/w/f
8791
job is sent over the network to each downstairs to when the ACK for that job
8892
is returned to the upstairs. Jobs are sorted by type (r/w/f) and by each
8993
downstairs client ID.
9094
```
91-
sudo dtrace -s perfdw.d
95+
sudo dtrace -s perf-net-ds.d
9296
```
9397

9498
Here is an example of how it might look:
9599
```
96-
final:crucible alan$ sudo sudo dtrace -Z -s perfdw.d
100+
final:crucible alan$ sudo sudo dtrace -Z -s perf-net-ds.d
97101
Password:
98102
dtrace: system integrity protection is on, some features will not be available
99103
@@ -220,7 +224,7 @@ dtrace: script 'perfdw.d' matched 0 probes
220224
```
221225

222226
## perfgw.d
223-
This is a simple dtrace script that measures latency times for when a r/w/f
227+
This is a simple DTrace script that measures latency times for when a r/w/f
224228
job is submitted to the internal upstairs work queue, to when that job has
225229
completed and the notification was sent back to the guest.
226230
If the upstairs is not yet running, add the -Z flag to dtrace so it will

tools/dtrace/perf-ds-client.d

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Trace all IOs from the Upstairs for each Downstairs from the time they
3+
* are sent to the client task who handles the network tranmission to the
4+
* time the result message is returned to the main task and processing
5+
* is about to begin.
6+
* Group by IO type (R/W/F) and client ID (Which downstairs).
7+
*
8+
* arg0 is the job ID number.
9+
* arg1 is the client ID
10+
*/
11+
crucible_upstairs*:::ds-*-client-start
12+
{
13+
start[arg0, arg1] = timestamp;
14+
}
15+
16+
crucible_upstairs*:::ds-*-client-done
17+
/start[arg0, arg1]/
18+
{
19+
strtok(probename, "-");
20+
this->cmd = strtok(NULL, "-");
21+
22+
@time[strjoin(this->cmd, " for downstairs client"), arg1] =
23+
quantize(timestamp - start[arg0, arg1]);
24+
start[arg0, arg1] = 0;
25+
}

tools/dtrace/perfdw.d tools/dtrace/perf-ds-net.d

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
* arg0 is the job ID number.
88
* arg1 is the client ID
99
*/
10-
crucible_upstairs*:::ds-*-io-start
10+
crucible_upstairs*:::ds-*-net-start
1111
{
1212
start[arg0, arg1] = timestamp;
1313
}
1414

15-
crucible_upstairs*:::ds-*-io-done
15+
crucible_upstairs*:::ds-*-net-done
1616
/start[arg0, arg1]/
1717
{
1818
strtok(probename, "-");
@@ -22,4 +22,3 @@ crucible_upstairs*:::ds-*-io-done
2222
quantize(timestamp - start[arg0, arg1]);
2323
start[arg0, arg1] = 0;
2424
}
25-

upstairs/src/client.rs

+42
Original file line numberDiff line numberDiff line change
@@ -2665,6 +2665,7 @@ impl ClientIoTask {
26652665
self.response_tx.clone(),
26662666
fr,
26672667
self.log.clone(),
2668+
self.client_id,
26682669
)));
26692670

26702671
let mut ping_interval = deadline_secs(PING_INTERVAL_SECS);
@@ -2759,6 +2760,7 @@ impl ClientIoTask {
27592760
}
27602761
}
27612762

2763+
update_net_start_probes(&m, self.client_id);
27622764
// There's some duplication between this function and `cmd_loop` above,
27632765
// but it's not obvious whether there's a cleaner way to organize stuff.
27642766
tokio::select! {
@@ -2795,6 +2797,7 @@ async fn rx_loop<R>(
27952797
response_tx: mpsc::UnboundedSender<ClientResponse>,
27962798
mut fr: FramedRead<R, crucible_protocol::CrucibleDecoder>,
27972799
log: Logger,
2800+
cid: ClientId,
27982801
) -> ClientRunResult
27992802
where
28002803
R: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send + 'static,
@@ -2804,6 +2807,7 @@ where
28042807
f = fr.next() => {
28052808
match f {
28062809
Some(Ok(m)) => {
2810+
update_net_done_probes(&m, cid);
28072811
if let Err(e) =
28082812
response_tx.send(ClientResponse::Message(m))
28092813
{
@@ -2833,6 +2837,44 @@ where
28332837
}
28342838
}
28352839

2840+
fn update_net_start_probes(m: &Message, cid: ClientId) {
2841+
match m {
2842+
Message::ReadRequest { job_id, .. } => {
2843+
cdt::ds__read__net__start!(|| (job_id.0, cid.get()));
2844+
}
2845+
Message::Write { ref header, .. } => {
2846+
cdt::ds__write__net__start!(|| (header.job_id.0, cid.get()));
2847+
}
2848+
Message::WriteUnwritten { ref header, .. } => {
2849+
cdt::ds__write__unwritten__net__start!(|| (
2850+
header.job_id.0,
2851+
cid.get()
2852+
));
2853+
}
2854+
Message::Flush { job_id, .. } => {
2855+
cdt::ds__flush__net__start!(|| (job_id.0, cid.get()));
2856+
}
2857+
_ => {}
2858+
}
2859+
}
2860+
fn update_net_done_probes(m: &Message, cid: ClientId) {
2861+
match m {
2862+
Message::ReadResponse { ref header, .. } => {
2863+
cdt::ds__read__net__done!(|| (header.job_id.0, cid.get()));
2864+
}
2865+
Message::WriteAck { job_id, .. } => {
2866+
cdt::ds__write__net__done!(|| (job_id.0, cid.get()));
2867+
}
2868+
Message::WriteUnwrittenAck { job_id, .. } => {
2869+
cdt::ds__write__unwritten__net__done!(|| (job_id.0, cid.get()));
2870+
}
2871+
Message::FlushAck { job_id, .. } => {
2872+
cdt::ds__flush__net__done!(|| (job_id.0, cid.get()));
2873+
}
2874+
_ => {}
2875+
}
2876+
}
2877+
28362878
/// Returns:
28372879
/// - Ok(Some(valid_hash)) for successfully decrypted data
28382880
/// - Ok(None) if there were no block contexts and block was all 0

upstairs/src/downstairs.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,10 @@ impl Downstairs {
529529
blocks,
530530
data,
531531
} => {
532-
cdt::ds__write__io__start!(|| (new_id.0, client_id.get()));
532+
cdt::ds__write__client__start!(|| (
533+
new_id.0,
534+
client_id.get()
535+
));
533536
Message::Write {
534537
header: WriteHeader {
535538
upstairs_id: self.cfg.upstairs_id,
@@ -546,7 +549,7 @@ impl Downstairs {
546549
dependencies,
547550
data,
548551
} => {
549-
cdt::ds__write__unwritten__io__start!(|| (
552+
cdt::ds__write__unwritten__client__start!(|| (
550553
new_id.0,
551554
client_id.get()
552555
));
@@ -568,7 +571,10 @@ impl Downstairs {
568571
snapshot_details,
569572
extent_limit,
570573
} => {
571-
cdt::ds__flush__io__start!(|| (new_id.0, client_id.get()));
574+
cdt::ds__flush__client__start!(|| (
575+
new_id.0,
576+
client_id.get()
577+
));
572578
Message::Flush {
573579
upstairs_id: self.cfg.upstairs_id,
574580
session_id: self.cfg.session_id,
@@ -584,7 +590,10 @@ impl Downstairs {
584590
dependencies,
585591
requests,
586592
} => {
587-
cdt::ds__read__io__start!(|| (new_id.0, client_id.get()));
593+
cdt::ds__read__client__start!(|| (
594+
new_id.0,
595+
client_id.get()
596+
));
588597
Message::ReadRequest {
589598
upstairs_id: self.cfg.upstairs_id,
590599
session_id: self.cfg.session_id,
@@ -3090,7 +3099,7 @@ impl Downstairs {
30903099
job_id,
30913100
result,
30923101
} => {
3093-
cdt::ds__write__io__done!(|| (job_id.0, client_id.get()));
3102+
cdt::ds__write__client__done!(|| (job_id.0, client_id.get()));
30943103
(
30953104
upstairs_id,
30963105
session_id,
@@ -3105,7 +3114,7 @@ impl Downstairs {
31053114
job_id,
31063115
result,
31073116
} => {
3108-
cdt::ds__write__unwritten__io__done!(|| (
3117+
cdt::ds__write__unwritten__client__done!(|| (
31093118
job_id.0,
31103119
client_id.get()
31113120
));
@@ -3123,7 +3132,7 @@ impl Downstairs {
31233132
job_id,
31243133
result,
31253134
} => {
3126-
cdt::ds__flush__io__done!(|| (job_id.0, client_id.get()));
3135+
cdt::ds__flush__client__done!(|| (job_id.0, client_id.get()));
31273136
(
31283137
upstairs_id,
31293138
session_id,
@@ -3133,7 +3142,10 @@ impl Downstairs {
31333142
)
31343143
}
31353144
Message::ReadResponse { header, data } => {
3136-
cdt::ds__read__io__done!(|| (header.job_id.0, client_id.get()));
3145+
cdt::ds__read__client__done!(|| (
3146+
header.job_id.0,
3147+
client_id.get()
3148+
));
31373149
let upstairs_id = header.upstairs_id;
31383150
let session_id = header.session_id;
31393151
let job_id = header.job_id;

upstairs/src/lib.rs

+31-11
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,22 @@ pub type CrucibleBlockIOFuture<'a> = Pin<
270270
/// has received or is acting on the IO yet, it just means the notification
271271
/// has been sent.
272272
///
273-
/// ds__*__io__start: This is when a downstairs task puts an IO on the
274-
/// wire to the actual downstairs that will do the work. This probe has
273+
/// ds__*__client__start: This is when a job is sent to the client task
274+
/// who will handle the network transfer
275+
///
276+
/// ds__*__net__start: This is when a downstairs client task puts an IO on
277+
/// the wire to the actual downstairs that will do the work. This probe has
275278
/// both the job ID and the client ID so we can tell the individual
276279
/// downstairs apart.
277280
///
278-
/// ds__*__io_done: An ACK has been received from a downstairs for an IO
281+
/// ds__*__net__done: An ACK has been received from a downstairs for an IO
279282
/// sent to it. At the point of this probe the IO has just come off the
280283
/// wire and we have not processed it yet.
281284
///
285+
/// ds__*__client__done: This probe indicates a message off the wire has
286+
/// been sent back from the client rx task to the main task and is now being
287+
/// processed.
288+
///
282289
/// up__to__ds__*__done: (Upstairs__to__Downstairs) This is the point where
283290
/// the upstairs has decided that it has enough data to complete an IO
284291
/// and send an ACK back to the guest. For a read, this could be the the
@@ -328,18 +335,31 @@ mod cdt {
328335
fn up__to__ds__write__unwritten__start(_: u64) {}
329336
fn up__to__ds__flush__start(_: u64) {}
330337
fn up__block__req__dropped() {}
331-
fn ds__read__io__start(_: u64, _: u8) {}
332-
fn ds__write__io__start(_: u64, _: u8) {}
333-
fn ds__write__unwritten__io__start(_: u64, _: u8) {}
334-
fn ds__flush__io__start(_: u64, _: u8) {}
338+
fn ds__read__client__start(_: u64, _: u8) {}
339+
fn ds__write__client__start(_: u64, _: u8) {}
340+
fn ds__write__unwritten__client__start(_: u64, _: u8) {}
341+
fn ds__flush__client__start(_: u64, _: u8) {}
335342
fn ds__close__start(_: u64, _: u8, _: usize) {}
336343
fn ds__repair__start(_: u64, _: u8, _: usize) {}
337344
fn ds__noop__start(_: u64, _: u8) {}
338345
fn ds__reopen__start(_: u64, _: u8, _: usize) {}
339-
fn ds__read__io__done(_: u64, _: u8) {}
340-
fn ds__write__io__done(_: u64, _: u8) {}
341-
fn ds__write__unwritten__io__done(_: u64, _: u8) {}
342-
fn ds__flush__io__done(_: u64, _: u8) {}
346+
fn ds__read__net__start(_: u64, _: u8) {}
347+
fn ds__write__net__start(_: u64, _: u8) {}
348+
fn ds__write__unwritten__net__start(_: u64, _: u8) {}
349+
fn ds__flush__net__start(_: u64, _: u8) {}
350+
fn ds__close__net__start(_: u64, _: u8, _: usize) {}
351+
fn ds__repair__net__start(_: u64, _: u8, _: usize) {}
352+
fn ds__noop__net__start(_: u64, _: u8) {}
353+
fn ds__reopen__net__start(_: u64, _: u8, _: usize) {}
354+
fn ds__read__net__done(_: u64, _: u8) {}
355+
fn ds__write__net__done(_: u64, _: u8) {}
356+
fn ds__write__unwritten__net__done(_: u64, _: u8) {}
357+
fn ds__flush__net__done(_: u64, _: u8) {}
358+
fn ds__close__net__done(_: u64, _: u8) {}
359+
fn ds__read__client__done(_: u64, _: u8) {}
360+
fn ds__write__client__done(_: u64, _: u8) {}
361+
fn ds__write__unwritten__client__done(_: u64, _: u8) {}
362+
fn ds__flush__client__done(_: u64, _: u8) {}
343363
fn ds__close__done(_: u64, _: u8) {}
344364
fn ds__repair__done(_: u64, _: u8) {}
345365
fn ds__noop__done(_: u64, _: u8) {}

0 commit comments

Comments
 (0)