szaydel
3/17/2016 - 6:27 PM

Dtrace one-liners for gathering insight from IO probes.

Dtrace one-liners for gathering insight from IO probes.

dtrace -qn '
  BEGIN {
    start = walltimestamp;
  }
  io:::start {
    @bytes[args[0]->b_flags & B_READ ? "R" : "W"] = quantize(args[0]->b_bcount);
    @s[args[0]->b_flags & B_READ ? "R" : "W"] = sum(args[0]->b_bcount);
    @iops[args[0]->b_flags & B_READ ? "R" : "W"] = count();
    @avsz[args[0]->b_flags & B_READ ? "R" : "W"] = avg(args[0]->b_bcount);
    @bytesbycmd[execname, args[0]->b_flags & B_READ ?
                "R" : "W"] = quantize(args[0]->b_bcount);
  } 
  END {
    /* Convert runtime to seconds to calculate IOPS.
       However, this does not account for idle time. */
    this->runtime = (walltimestamp - start) / 1000000000;
    normalize(@iops, this->runtime);
    normalize(@s, 1024); 
    printa("Operation [%s]: %@d(KB) IOPS: %@d Avg. IOsize: %@d%@d\n", 
            @s, @iops, @avsz, @bytes);
    printa("Program: %s [%s] %@d\n", @bytesbycmd);
  }'
// Basic IO size and byte sum, split by READ/WRITE.
dtrace -qn '
  BEGIN {
    start = walltimestamp;
  }
  io:::start {
    @bytes[args[0]->b_flags & B_READ ? "R" : "W"] = quantize(args[0]->b_bcount);
    @s[args[0]->b_flags & B_READ ? "R" : "W"] = sum(args[0]->b_bcount);
    @iops[args[0]->b_flags & B_READ ? "R" : "W"] = count();
    @avsz[args[0]->b_flags & B_READ ? "R" : "W"] = avg(args[0]->b_bcount);
    @bytesbycmd[execname, args[0]->b_flags & B_READ ?
                "R" : "W"] = quantize(args[0]->b_bcount);
  } 
  END {
    /* Convert runtime to seconds to calculate IOPS.
       However, this does not account for idle time. */
    this->runtime = (walltimestamp - start) / 1000000000;
    normalize(@iops, this->runtime);
    normalize(@s, 1024); 
    printa("Operation [%s]: %@d(KB) IOPS: %@d Avg. IOsize: %@d%@d\n", 
            @s, @iops, @avsz, @bytes);
    printa("Program: %s [%s] %@d\n", @bytesbycmd);
  }'

// Observe by device name count of IOs of whatever IO sizes are being issued.
// This can become severely verbose on real, active systems. For lab use only.
dtrace -qn 'int x; io:::start {@[args[1]->dev_pathname, args[0]->b_bcount] = count(); x++;} tick-1sec /x/ {printa("Device: %s IO Bytes: %d Count: %@d\n", @); x = 0; trunc(@);}'

// Focus on a specific IO size, only selecting scsi_vhci devices. 
// In this case we select only 8K IOs.
dtrace -qn 'int x; io:::start 
    /strstr(args[1]->dev_pathname, "/devices/scsi_vhci") != NULL && args[0]->b_bcount == 8192/ {
    @cnt[args[1]->dev_pathname, args[0]->b_flags & B_READ ? "R" : "W"] = count();
    @s[args[1]->dev_pathname, args[0]->b_flags & B_READ ? "R" : "W"] = sum(args[0]->b_bcount);
    x=1;
    }
    END /x/ {
    printa("Device: %s CMD: %s Count: %@d Sum: %@d\n", @cnt, @s);
    }'

// Focus on IOs smaller than X size, otherwise identical to above.
// In this case we are focused on 8K< IOs.
dtrace -qn 'int x; io:::start 
    /strstr(args[1]->dev_pathname, "/devices/scsi_vhci") != NULL && args[0]->b_bcount <= 8 << 10/ {
    @rcnt[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 1 : 0);
    @wcnt[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 0 : 1);
    @rs[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? args[0]->b_bcount : 0);
    @ws[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount);
    x=1;
    }
    END /x/ {
    printa("Device: %s IOSize: %d R: %@d W: %@d Bytes[R]: %@d Bytes[W]: %@d\n", 
            @rcnt, @wcnt, @rs, @ws);
    }'

// Get a detailed output where each line is a one-line summary for single drive
// over sampling period.
// Example output:
// Device: /devices/scsi_vhci/disk@g500117310073da20:a IOSize: 8192 R: 0 W: 464 Bytes[R]: 0 Bytes[W]: 3801088
dtrace -qn 'int x; io:::start 
    /strstr(args[1]->dev_pathname, "/devices/scsi_vhci") != NULL/ {
    @rcnt[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 1 : 0);
    @wcnt[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 0 : 1);
    @rs[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? args[0]->b_bcount : 0);
    @ws[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount);
    x=1;
    }
    END /x/ {
    printa("Device: %s IOSize: %d R: %@d W: %@d Bytes[R]: %@d Bytes[W]: %@d\n", 
            @rcnt, @wcnt, @rs, @ws);
    }'

// Same as above, but only active while the command is being exec'd. This is useful for counting
// blocks sent to device with dd.
# dtrace -qn 'int x; io:::start {@[args[1]->dev_pathname, args[0]->b_bcount] = count(); x++; } END /x/ {printa("Device: %s IO Bytes: %d Count: %@d\n", @); x = 0; trunc(@);}' \
-c 'dd if=/dev/rdsk/c1t5d0s0 of=/dev/null bs=512 count=1'


// Select devices with blkdev@ in the path. This seems to be the case for NMVe devices,
// which is what I am interested in at the moment.
dtrace -qn '
  int x;
  io:::start /strstr(args[1]->dev_pathname, "blkdev@") !=NULL/ 
  {
    @[args[1]->dev_pathname, args[0]->b_bcount] = count(); 
    x++;
  }
  tick-1sec /x/ {
    printa("Device: %s IO Bytes: %d Count: %@d\n", @); 
    x = 0; trunc(@);
  }'
dtrace -qn 'int x; io:::start 
    /strstr(args[1]->dev_pathname, "/devices/scsi_vhci") != NULL && args[0]->b_bcount <= 8 << 10/ {
    @rcnt[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 1 : 0);
    @wcnt[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 0 : 1);
    @rs[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? args[0]->b_bcount : 0);
    @ws[args[1]->dev_pathname, args[0]->b_bcount] = sum(args[0]->b_flags & B_READ ? 0 : args[0]->b_bcount);
    x=1;
    }
    END /x/ {
    printa("Device: %s IOSize: %d R: %@d W: %@d Bytes[R]: %@d Bytes[W]: %@d\n", 
            @rcnt, @wcnt, @rs, @ws);
    }'