szaydel
7/21/2018 - 1:09 AM

Dtrace snippet for gathering basic details about NFSv4 IO

Used this primarily to sanity-check what synthetic tools claim they are doing.

dtrace -qn '
long lastoff[char *];
long nextoff[char *];
long lastdist[char *];
long bytesW;
int first[char *];

nfsv4:::op-write-start /!first[args[1]->noi_curpath]/ {
  first[args[1]->noi_curpath]++;
  lastoff[args[1]->noi_curpath] = args[2]->offset / 1024;
  bytesW += args[2]->data_len;
  /* We predict next offset, assuming that size of IO is constant, which 
   * is something we expect if IO is sequential with fix length chunks.
   */
   nextoff[args[1]->noi_curpath] =
    (args[2]->offset / 1024) + (args[2]->data_len / 1024);
}
nfsv4:::op-write-start
/ lastoff[args[1]->noi_curpath] != (args[2]->offset / 1024) / {
  this->p = args[1]->noi_curpath;
  this->cur = args[2]->offset / 1024;
  this->iosz = args[2]->data_len / 1024;
  bytesW += args[2]->data_len;

  @qoff[this->iosz] = quantize(lastoff[this->p]);
  this->dist = this->cur > lastoff[this->p] ?
    this->cur - lastoff[this->p] :
    lastoff[this->p] - this->cur;
  
  /* If our current offset is greater than previous offset, we are moving
   * in the forward direction, and if it is actually lower than previous
   * offset, we are moving in reverse direction. Direction changes are
   * indicative of random IO. 
   */
  this->is_fw = this->cur > lastoff[this->p] ? 1 : 0;

  /* nextoff[this->p] was computed previously, by adding size of last
   * io to offset at that point. If our current position in the file
   * equals our predicted position, stored in nextoff[this->p], we are
   * doing fixed size sequential IO.
   */
  this->is_seq = this->cur == nextoff[this->p];
  @seqcnt[this->is_seq ? "SEQUENTIAL" : "RANDOM" ] = count();
  @tot[this->is_seq ? "SEQUENTIAL" : "RANDOM" ]    = sum(this->iosz);

  this->dir_label = this->is_fw ? "Forward [ => ]" : "Reverse [ <= ]";
  @qdist[this->dir_label] = quantize(this->dist);
  
  lastoff[this->p] = (this->cur) - lastoff[this->p];
  /* We predict next offset, assuming that size of IO is constant, which 
   * is something we expect if IO is sequential with fix length chunks.
   */
  nextoff[this->p] = this->cur + this->iosz;
}
tick-1sec /bytesW/ {
  this->b = bytesW;
  bytesW = 0;
  @avKBps   = avg(this->b >> 10);
  @minKBps  = min(this->b >> 10);
  @maxKBps  = max(this->b >> 10);
}
END {
  printa("           Seek Distance with IO Size: %d(KB) %@d\n", @qoff);
  printa("           %s %@d\n", @qdist);
  printf("-/- Basic Statistics -/-\n");
  printa("Throughput [Avg]: %8@d(KB) | [Min]: %8@d(KB) | [Max]: %8@d(KB)\n",
    @avKBps, @minKBps, @maxKBps);
  printa("%s IOs count: %@d totaling %@d(KB)\n", @seqcnt, @tot);
}'