d 有用的用户定义属性,用于标记TODO和FIXME,并在编译时通知代码,而不是使用注释和greppin

用于标记TODO和FIXME的有用的用户定义属性,其中包含要在编译时通知的代码,而不是稍后的注释和grepping。

example.d
module example;

import annotations;

/+
    At compile time, the following will be printed to STDOUT (when in debug mode only):
        FIXME @ example.randomNumber (example.d:11): The RNG is broken.
        TODO @ example.main (example.d:17): Properly implement main().
+/

@FixMe!( randomNumber, "The RNG is broken." )
int randomNumber()
{
    return 4;
}

@ToDo!( main, "Properly implement main()." )
void main()
{

}
annotations.d
module annotations;

private {
    import std.string;
    import std.traits;
    import std.array;
}

private template TargetInfo( alias target )
{
    enum parent     = __traits( identifier, __traits( parent, target ) );
    enum isTopLevel = __traits( isSame, mixin( parent ), mixin( __MODULE__ ) );
    enum name       = __traits( identifier, target );

    static if( isTopLevel )
        enum prettyName = "%s.%s".format( __MODULE__, name );
    else
        enum prettyName = "%s.%s.%s".format( __MODULE__, parent, name );
}

private template AnnotationBase( alias target, string prefix, string message )
{
    mixin TargetInfo!target;
    pragma( msg, message.replace( "{MODULE}", __MODULE__ ).replace( "{NAME}", name ).replace( "{FULL_NAME}", prettyName ) );
}

package template FixMe( alias target, string message = null ) if( isSomeFunction!target || isCallable!target )
{
    debug
    {
        mixin TargetInfo!target;

        static if( message is null || message.length == 0 )
            enum output = "FIXME: %s needs to be fixed in %s on line %u.".format( prettyName, __FILE__, __LINE__ );
        else
            enum output = "FIXME @ %s (%s:%u): %s".format( prettyName, __FILE__, __LINE__, message );

        mixin AnnotationBase!( target, "FIXME", output );
    }
}

package template ToDo( alias target, string message = null ) if( isSomeFunction!target || isCallable!target )
{
    debug
    {
        mixin TargetInfo!target;

        static if( message is null || message.length == 0 )
            enum output = "TODO: %s needs to be taken care of in %s on line %u.".format( prettyName, __FILE__, __LINE__ );
        else
            enum output = "TODO @ %s (%s:%u): %s".format( prettyName, __FILE__, __LINE__, message );

        mixin AnnotationBase!( target, "TODO", output );
    }
}

d [Dtrace NFS snippets]脚本和单行收集有关NFS操作和性能的数据#tags:nfs,nfsv3,nfsv4,latency,nfs oper

[Dtrace NFS snippets]脚本和单行收集有关NFS操作和性能的数据#tags:nfs,nfsv3,nfsv4,latency,nfs operations

nfsv3rwcntcsv.d
dtrace -qn'
    BEGIN {
        printf("timestamp,cmd,path,count\n");
        typ["op-access-done"] = "A";
        typ["op-create-done"] = "C";
        typ["op-getattr-done"] = "G";
        typ["op-read-done"] = "R";
        typ["op-write-done"] = "W";
        
}
    tick-5sec {
        ts = walltimestamp;
    }
    
    nfsv3:::op-access-done,
    nfsv3:::op-create-done,
    nfsv3:::op-getattr-done,
    nfsv3:::op-read-done,
    nfsv3:::op-write-done {
        this->path = args[1]->noi_curpath == NULL ? 
        "<Unknown>" : args[1]->noi_curpath;
    }
    nfsv3:::op-access-done,
    nfsv3:::op-create-done,
    nfsv3:::op-getattr-done,
    nfsv3:::op-read-done,
    nfsv3:::op-write-done /ts && strstr(this->path, "vmdk")!=0 / {
        @[ts, typ[probename], this->path] = count();
        data = 1;
    }
    tick-60sec /data/ {
        trunc(@, 10); 
        printa("%Y,%s,%s,%@d\n", @); 
        trunc(@); 
        data = 0;
    }'
nfsv3randseq.d
dtrace -qn '
  /*
   * Measure how random or sequential NFSv3 IO is, and bucket it by
   * client IP address. Print results every 5 seconds by default.
   * Uncomment lines with @h aggregates to get histograms which show
   * common offsets. This is a good way to know just how sequential
   * sequential IO is, and in what offsets it is happening, i.e. 4K, 64K, etc.
   */
  int offset[string, string];
  int lastdelta[string, string];
  int havedata;

  BEGIN {
    printf("command,ip.addr,rand.count,seq.count\n");
    map["op-read-start"] = "R";
    map["op-write-start"] = "W";
  }
  nfsv3:::op-read-start,nfsv3:::op-write-start
  /!offset[map[probename], args[0]->ci_remote]/ {
    offset[map[probename], args[0]->ci_remote] = args[2]->offset;
    lastdelta[map[probename], args[0]->ci_remote]  = 0;
  }
  nfsv3:::op-read-start,nfsv3:::op-write-start
  /* Previous offset is greater than this offset */
  /offset[map[probename], args[0]->ci_remote] > args[2]->offset/ {
    this->dist = offset[map[probename], args[0]->ci_remote] - args[2]->offset;
    /* @h[map[probename], args[0]->ci_remote] = quantize(this->dist); */
    @seq[map[probename], args[0]->ci_remote] = 
      sum(this->dist == lastdelta[map[probename], args[0]->ci_remote]);
    @rand[map[probename], args[0]->ci_remote] = 
      sum(this->dist != lastdelta[map[probename], args[0]->ci_remote]);
    offset[map[probename], args[0]->ci_remote] = args[2]->offset;
    lastdelta[map[probename], args[0]->ci_remote] = this->dist;
    havedata = 1;
  }
  nfsv3:::op-read-start,nfsv3:::op-write-start
  /* Previous offset is less than this offset */
  /offset[map[probename], args[0]->ci_remote] < args[2]->offset/ {
    this->dist = args[2]->offset - offset[map[probename], args[0]->ci_remote];
    /* @h[map[probename], args[0]->ci_remote] = quantize(this->dist); */
    @seq[map[probename], args[0]->ci_remote] = 
      sum(this->dist == lastdelta[map[probename], args[0]->ci_remote]);
    @rand[map[probename], args[0]->ci_remote] = 
      sum(this->dist != lastdelta[map[probename], args[0]->ci_remote]);
    offset[map[probename], args[0]->ci_remote] = args[2]->offset;
    lastdelta[map[probename], args[0]->ci_remote] = this->dist;
    havedata = 1;
  }
  tick-5sec /havedata/ {
    printa("%s,%s,%@d,%@d\n", @rand, @seq);
    trunc(@rand); trunc(@seq);
    /* trunc(@h); */
    havedata = 0;
  }'
nfsv3-top-100-eps-csv.d
dtrace -qn'
  #pragma D option aggsize=16m
  #pragma D option bufsize=8m
  #pragma D option dynvarsize=8m

  string typ[string];
  
  BEGIN 
  {
    printf("source,cmd,path,IOs,avg.lat.us,bytes\n");
    typ["op-access-done"] = "A";
    typ["op-create-done"] = "C";
    typ["op-getattr-done"] = "G";
    typ["op-read-done"] = "R";
    typ["op-write-done"] = "W";
  }
  /*
   * Ignoring all R/W IOs less than 1K. If doing some really tiny <1K IOs,
   * lets pretend these IOs did not happen.
   */
  nfsv3:::op-read-start,
  nfsv3:::op-write-start /args[2]->count > 1 << 10/ { self->bytes = args[2]->count ; }

  nfsv3:::op-access-start, nfsv3:::op-create-start, nfsv3:::op-getattr-start,
  nfsv3:::op-read-start, nfsv3:::op-write-start
  {
    self->src_addr = args[0]->ci_remote;
    tm[self->src_addr, args[1]->noi_xid] = timestamp;

    self->have_path = args[1]->noi_curpath == NULL ? 0 : 1;
    
    /* We should make sure we handle empty value as well */
    self->path = args[1]->noi_curpath != "" ? 
              args[1]->noi_curpath  : "NOPATH";
  }

  /*
   * Continue to ignore any R/W IOs that are less than 1K.
   */
  nfsv3:::op-read-done, nfsv3:::op-write-done 
  /self->have_path &&
    tm[self->src_addr, args[1]->noi_xid] &&
    self->bytes > 1 << 10
  / 
  {
    @ios[self->src_addr,
        typ[probename], self->path] = count();
    @lat_us[self->src_addr,
        typ[probename], self->path] = 
          avg((timestamp - tm[self->src_addr, args[1]->noi_xid]) / 1000);
    @s[self->src_addr,
        typ[probename], self->path] = sum(self->bytes);
    ++data;
  }

  nfsv3:::op-access-done, nfsv3:::op-create-done, nfsv3:::op-getattr-done /self->have_path && tm[self->src_addr, args[1]->noi_xid]/
  {
    @ios[self->src_addr,
        typ[probename], self->path] =
      count();
    @lat_us[self->src_addr,
        typ[probename], self->path] =
      avg((timestamp - tm[self->src_addr, args[1]->noi_xid]) / 1000);
    @s[self->src_addr,
        typ[probename], self->path] =
      sum(0);
    ++data;
  }
  
  nfsv3:::op-access-done,
  nfsv3:::op-create-done,
  nfsv3:::op-getattr-done,
  nfsv3:::op-read-done,
  nfsv3:::op-write-done {
    /* Zero out things to make sure we avoid issues with dynvar drops */
    self->bytes = 0;
    self->have_path = 0;
    tm[self->src_addr, args[1]->noi_xid] = 0;
    self->src_addr = 0;
    self->path = 0;
  }

  tick-1sec /data/
  {
    /*
     * Shrink each aggregation to 100 observations first, then print those
     * 100 observations, which will not mean we are limited to 100 unique
     * connection addresses, or 100 unique paths. As it stands, aggregations
     * right now use a tuple for their key, which is a set of three values:
     * client address, command, path. This means that in theory we could have:
     * (client address) x (command) x (path) number of unique keys.
     */
    trunc(@ios, 100); trunc(@lat_us, 100); trunc(@s, 100);
    printa("%s,%s,%s,%@d,%@d,%@d\n", @ios, @lat_us, @s);
    
    trunc(@ios); trunc(@lat_us); trunc(@s);
    data = 0;
  }'
nfsstatcsv.d
dtrace -qn '

inline const int NORM_KB = 1024;
inline const int NS_S = 1000000000;
inline const int NS_MS = 1000000;
inline const int NS_US = 1000;
int cnt, realtm, start, epoch;
self int nfs_r;

#pragma D option aggsize=16m
#pragma D option bufsize=50m
#pragma D option dynvarsize=50m

BEGIN {
printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
    "io.type", "runtime.ms", "min.lat", "max.lat", "avg.lat",
    "max.io.size", "iops", "throughput.KBps",
    "512b.ct", "1024b.ct", "2048b.ct", "4096b.ct", "8192b.ct", 
    "16384b.ct", "32768b.ct", "65536b.ct", "131072b.ct", 
    "volatile", "fsync", "dsync", "arc.hit.ct", "arc.miss.ct",
    "arc.size.av", "arc.size.min", "arc.size.max", "arc.mru.size.av",
    "arc.mru.size.min", "arc.mru.size.max", "arc.mfu.size.av",
    "arc.mfu.size.min", "arc.mfu.size.max", "total.ios", "avg.bytes.per.us"
);
hdr = 1;
epoch = timestamp;
start = timestamp;
  /* How stable data is expected to be. */
  stable_how[0] = "NOSYNC";
  stable_how[1] = "DSYNC";
  stable_how[2] = "FSYNC";
  ts = walltimestamp;
}

tick-1sec /(timestamp - epoch) >= 0 && cnt > 0/ {
  realtm = timestamp - epoch; /* Update real time passed since last sample. */
}

tick-1sec /!cnt/ {
  /* Set realtime to 0 as first run, to make sure first reported line starts
   * at 0 and increments from there. This should only happen once.
   */
  realtm = 0;
  ++cnt;
}

nfsv3:::op-read-start, 
nfsv3:::op-write-start,
nfsv4:::op-read-start {
  /* If this is a read, increment counter by one, else leave previous value */
  self->nfs_r += probename == "op-read-start" ? 1 : 0;
  tm[args[0]->ci_remote, args[1]->noi_xid] = timestamp;
  opsz[args[0]->ci_remote, args[1]->noi_xid] = args[2]->count;
  flag[args[0]->ci_remote, args[1]->noi_xid] = 0;
}

/*
 * sdt:::arc-hit and sdt:::arc-miss give info on direct users of the arc 
 * if arc-miss, then we do an I/O.
 * Unfortunately, we cannot directly correlate a NFS Read with event in the
 * ARC, at least not easily.
 * We are likely to miss at least some ARC hits and misses here, but this
 * should be good enough to get a sense for hits and misses in general.
 */
sdt:::arc-miss /self->nfs_r/ { @arcm["R"] = count(); }
sdt:::arc-hit /self->nfs_r/ { @arch["R"] = count(); }

::arc_read:entry {
    this->cmd = self->nfs_r ? "R" : "W";
    @arcszmin[this->cmd]     = min(`arc_stats.arcstat_size.value.i64);
    @arcszmax[this->cmd]     = max(`arc_stats.arcstat_size.value.i64);
    @arcszav[this->cmd]      = avg(`arc_stats.arcstat_size.value.i64);
    @arcmruszmin[this->cmd]  = min(`arc_stats.arcstat_mru_size.value.i64);
    @arcmruszmax[this->cmd]  = max(`arc_stats.arcstat_mru_size.value.i64);
    @arcmruszav[this->cmd]   = avg(`arc_stats.arcstat_mru_size.value.i64);
    @arcmfuszmin[this->cmd]  = min(`arc_stats.arcstat_mfu_size.value.i64);
    @arcmfuszmax[this->cmd]  = max(`arc_stats.arcstat_mfu_size.value.i64);
    @arcmfuszav[this->cmd]   = avg(`arc_stats.arcstat_mfu_size.value.i64);
}

nfsv3:::op-write-start {
    flag[args[0]->ci_remote,args[1]->noi_xid] = args[2]->stable;
}
nfsv4:::op-write-start {
    flag[args[0]->ci_remote,args[1]->noi_xid] = args[2]->stable;
    tm[args[0]->ci_remote,args[1]->noi_xid] = timestamp;
    opsz[args[0]->ci_remote,args[1]->noi_xid] = args[2]->data_len;
}

nfsv3:::op-read-done,
nfsv3:::op-write-done,
nfsv4:::op-read-done,
nfsv4:::op-write-done
/tm[args[0]->ci_remote,args[1]->noi_xid]/ {
  /* Total time from start of operation to end of same operation. */
  this->delta = (timestamp - tm[args[0]->ci_remote, args[1]->noi_xid]);
  /*
   * Flag indicates volatility mode for this operation, i.e.
   * `Unstable`, `Data Sync`, or `File Sync`.
   */
  this->flag =  flag[args[0]->ci_remote,args[1]->noi_xid];
  this->name =  probename == "op-write-done" ? "W" : "R";

  /*
  * This is a hack, it takes advantage of the fact that realtm value will
  * always increase, and never decrease. Because we do not have an aggregate
  * for storing only last value, this is the best we can do. We are
  * effectively storing latest computed value.
  */
  @t[this->name]      = max(realtm);

  this->size = opsz[args[0]->ci_remote,args[1]->noi_xid];
  this->buck = 512; /* We assume this is smallest possible size */
  this->buck = ( this->size > 512 ) ? 1024 : this->buck ;
  this->buck = ( this->size > 1024 ) ? 2048 : this->buck ;
  this->buck = ( this->size > 2048 ) ? 4096 : this->buck ;
  this->buck = ( this->size > 4096 ) ? 8192 : this->buck ;
  this->buck = ( this->size > 8192 ) ?  16384 : this->buck ;
  this->buck = ( this->size > 16384 ) ?  32768 : this->buck ;
  this->buck = ( this->size > 32768 ) ?  65536 : this->buck ;
  this->buck = ( this->size > 65536 ) ?  0x20000 : this->buck ;
  this->buck = ( this->size > 0x20000 ) ?  0x40000 : this->buck ;
  this->buck = ( this->size > 0x40000 ) ?  0x80000 : this->buck ;
  this->buck = ( this->size > 0x80000 ) ?  0x100000 : this->buck ;
  this->buck = ( this->size > 0x100000 ) ?  0x200000 : this->buck ;

  @lmin[this->name]   = min(this->delta);
  @lmax[this->name]   = max(this->delta);
  @a[this->name]      = avg(this->delta);
  @maxiosz[this->name]  = max(this->size);
  @i[this->name]      = count();
  /* Average bytes per microsecond of IO time. */
  this->bpus = this->delta > 0 ? (this->size*1000 / this->delta) : this->size;
  @abus[this->name]   = avg(this->bpus);
  /* Do not normalize (@ios) to get total I/Os per interval. */
  @ios[this->name]    = count();
  @b[this->name]      = sum(opsz[args[0]->ci_remote, args[1]->noi_xid]);
  @sz512[this->name]  = sum(this->buck == 512 ? 1 : 0 );
  @sz1024[this->name]  = sum(this->buck == 1024 ? 1 : 0 );
  @sz2048[this->name]  = sum(this->buck == 2048 ? 1 : 0 );
  @sz4096[this->name]  = sum(this->buck == 4096 ? 1 : 0 );
  @sz8192[this->name]  = sum(this->buck == 8192 ? 1 : 0 );
  @sz16384[this->name]  = sum(this->buck == 16384 ? 1 : 0 );
  @sz32768[this->name]  = sum(this->buck == 32768 ? 1 : 0 );
  @sz65536[this->name]  = sum(this->buck == 65536 ? 1 : 0 );
  @sz131072[this->name]  = sum(this->buck == 131072 ? 1 : 0 );

  /*
   * There is an aggregate for each possible flag, and we are counting
   * number of occurrences of each, reporting each as a separate field.
   */
  @nos[this->name]   = sum(this->flag == 0 ? 1 : 0 );
  @fs[this->name]    = sum(this->flag == 1 ? 1 : 0 );
  @ds[this->name]    = sum(this->flag == 2 ? 1 : 0 );
  tm[args[0]->ci_remote,args[1]->noi_xid]   = 0;
  opsz[args[0]->ci_remote,args[1]->noi_xid] = 0;
  flag[args[0]->ci_remote,args[1]->noi_xid] = 0;
  self->nfs_r -= probename == "op-read-done" ? 1 : 0;
}

tick-1sec /(timestamp - start) > 0 && cnt/ {
/*
 * Normalizing by 1024 can introduce sample error when the value being 
 * divided is less than 1024. Since there is no floating point division,
 * resulting value will be 0, which if data is routinely less than 1024,
 * can create a serious discontinuity between real data and normalized
 * data.
 */
normalize(@lmin, NS_US);
normalize(@lmax, NS_US);
normalize(@a, NS_US);
normalize(@b, NORM_KB);
normalize(@t, NS_MS);
printa(
  "%s,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d,%@d\n", 
                    @t, @lmin, @lmax, @a, @maxiosz, @i, @b, 
                    @sz512, @sz1024, @sz2048, @sz4096, @sz8192, @sz16384,
                    @sz32768, @sz65536, @sz131072, @nos, @fs, @ds, 
                    @arch, @arcm, @arcszav, @arcszmin, @arcszmax, 
                    @arcmruszav, @arcmruszmin, @arcmruszmax, @arcmfuszav, @arcmfuszmin, @arcmfuszmax, @ios, @abus);
/*
 * Truncate aggregations and re-set timestamp for next sampling period.
 */
trunc(@t); trunc(@lmin); trunc(@lmax); trunc(@a); trunc(@maxiosz); trunc(@i); 
trunc(@b); trunc(@sz512); trunc(@sz1024); trunc(@sz2048); trunc(@sz4096);
trunc(@sz8192); trunc(@sz16384); trunc(@sz32768); trunc(@sz65536);
trunc(@sz131072); trunc(@nos); trunc(@fs); trunc(@ds); 
trunc(@ios); trunc(@abus); trunc(@arch); trunc(@arcm);
trunc(@arcszav); trunc(@arcszmin); trunc(@arcszmax);
trunc(@arcmruszav); trunc(@arcmruszmin); trunc(@arcmruszmax); 
trunc(@arcmfuszav); trunc(@arcmfuszmin); trunc(@arcmfuszmax);

start = timestamp;
}'
nfs-v3-one-liners.txt
// What is the recordsize of operations, both reads and writes?
dtrace -qn 'nfsv3:::op-commit-start,nfsv3:::op-read-start,nfsv3:::op-write-start {@[probename=="op-write-start" ? "W" : "R"] = quantize(args[2]->count);}'
nfs-file-read-write-commit-ops.d
dtrace -n '
BEGIN {
    command["op-commit-start"] = "COMMIT";
    command["op-read-start"] = "READ";
    command["op-write-start"] = "WRITE";

}
nfsv3:::op-commit-start,nfsv3:::op-read-start,nfsv3:::op-write-start 
{
    @[command[probename], args[1]->noi_curpath] = quantize(args[2]->count); 
}
tick-5sec {
    trunc(@, 5); printa(@); trunc(@);
}'

d 用于https://github.com/Evairfairy/PacketRogue的UI归档压缩工具

用于https://github.com/Evairfairy/PacketRogue的UI归档压缩工具

pack.d
/+
Compile with:
    dmd pack.d
    
Options:
    Flag         Type    Required    Default      Description
    ---------------------------------------------------------
    --out        string  yes         <none>       The name of the .ui file to output.
    --dir        string  yes         <none>       The source directory to pack.
    --width      uint    yes         <none>       Window client size width.
    --height     uint    yes         <none>       Window client size height.
    --silent     bool    no          flase        Do not print to the console.
    --recurse    bool    no          true         Resursively search --dir
    --overwrite  bool    no          false        Force overwrite of --out if it already exists.
+/

import std.path,
       std.file,
       std.stream,
       std.zlib,
       std.getopt,
       std.array,
       std.random,
       std.string,
       io = std.stdio;

string output;
string root;
bool overwrite = false;
bool silent = false;
bool recurse = true;
int width = -1;
int height = -1;

enum BUFFER_SIZE = 10_240;
enum ubyte[] MAGIC = [ 0x50, 0x52, 0x55, 0x49 ];
enum ubyte VERSION_MAJOR = 1;
enum ubyte VERSION_MINOR = 0;

void main( string[] args )
{
    args.getopt(
        "out",       &output,
        "dir",       &root,
        "width",     &width,
        "height",    &height,
        "silent",    &silent,
        "recurse",   &recurse,
        "overwrite", &overwrite
    );
    
    if( width <= 0 || height <= 0 )
    {
        io.stderr.writeln( "Width and height must be greater than zero." );
        return;
    }
    
    if( output is null || output.length == 0 )
    {
        io.stderr.writeln( "Output file is required." );
        return;
    }
    
    if( output.exists && output.isDir )
    {
        io.stderr.writeln( "Output is a directory, not a file." );
        return;
    }
    
    if( output.exists && !overwrite )
    {
        io.stdout.writef( "'%s' already exists. Overwrite? [Y/n] ", output );
        auto _in = io.stdin.readln().chomp.strip.toLower;
        
        if( _in.length < 1 || _in[0] != 'y' )
            return;
    }
    
    if( !output.toLower.endsWith( ".ui" ) )
        output ~= ".ui";
    
    if( root is null || ( !root.exists && !root.isDir ) )
    {
        io.stderr.writefln( "The directory '%s' does not exist or is not a directory.",
                            root is null ? "<null>" : root );
        
        return;
    }
    
    if( output.exists )
        output.remove();

    auto mode = !recurse ? SpanMode.shallow : SpanMode.breadth;
    auto entries = root.dirEntries( mode ).array;
    auto tempName = "~" ~ randomString() ~ ".tmp";
    auto arch = new File( output, FileMode.In | FileMode.Out );
    auto temp = new File( tempName, FileMode.In | FileMode.OutNew );
    
    assert( temp.seekable );
    
    Entry[] files;
    
    auto getHeaderSize()
    {
        auto size = 4  //Magic ID length
                  + 2  //version length
                  + 4  //width length
                  + 4  //height length
                  + 4; //count length
        
        if( files.length > 0 )
        {
            foreach( e; files )
            {
                size += 4  //compressed size
                      + 4  //uncompressed size
                      + 4; //offset
                
                size += 2 + e.MIME.length;
                size += 2 + e.Name.length;
            }
        }
        
        return size;
    }
    
    arch.write( MAGIC );
    arch.write( VERSION_MAJOR );
    arch.write( VERSION_MINOR );
    arch.write( width );
    arch.write( height );
    
    while( entries.length )
    {
        auto entry = entries.back;
        entries.popBack();
        
        if( entry.isDir && recurse )
        {
            entries ~= entry.name.dirEntries( mode ).array;
            continue;
        }
        
        string name = entry.name.normalize();
        Entry file = {
            0, 0, 0,
            name.getMimeType(),
            name,
            entry
        };
        
        files ~= file;
    }
    
    arch.write( files.length );
    arch.flush();
    auto headerSize = getHeaderSize();
    
    if( files.length == 0 )
        goto End;
    
    foreach( ref entry; files )
    {
        printfln( "Packing '%s'.", entry.Name );
        
        auto input = new File( entry.path, FileMode.In | FileMode.Out );
        auto compressor = new Compress( 9,  HeaderFormat.deflate );
        auto before = temp.position;
        
        const( void )[] chunk;
        
        while( !input.eof )
        {
            auto left = input.available > BUFFER_SIZE
                        ? BUFFER_SIZE
                        : input.available;
            
            ubyte[] buf = new ubyte[]( cast(uint)left );
            input.read( buf );
            
            chunk = compressor.compress( cast(void[])buf );
            temp.write( cast(ubyte[])chunk );
        }
        
        chunk = compressor.flush();
        temp.write( cast(ubyte[])chunk );
        temp.flush();
        
        entry.UncompressedSize = cast(int)input.size;
        entry.CompressedSize = cast(int)( temp.position - before );
        entry.Offset = cast(int)( headerSize + before );
        
        input.close();
        
        arch.write( entry.CompressedSize );
        arch.write( entry.UncompressedSize );
        arch.write( entry.Offset );

        short len = cast(short)entry.MIME.length;
        ubyte[] buf = cast(ubyte[])entry.MIME.dup;
        
        arch.write( len );
        arch.write( buf );
        
        len = cast(short)entry.Name.length;
        buf = cast(ubyte[])entry.Name.dup;
        
        arch.write( len );
        arch.write( buf );
        
        arch.flush();
    }
    
    temp.seekSet( 0 );
    arch.copyFrom( temp );
    arch.flush();
    
    End:
    temp.close();
    arch.close();
    tempName.remove();
    
    println( "Done." );
}

enum MimeLookup = [
    "ai": "application/postscript",
    "aif": "audio/x-aiff",
    "aifc": "audio/x-aiff",
    "aiff": "audio/x-aiff",
    "asc": "text/plain",
    "atom": "application/atom+xml",
    "au": "audio/basic",
    "avi": "video/x-msvideo",
    "bcpio": "application/x-bcpio",
    "bin": "application/octet-stream",
    "bmp": "image/bmp",
    "cdf": "application/x-netcdf",
    "cgm": "image/cgm",
    "class": "application/octet-stream",
    "cpio": "application/x-cpio",
    "cpt": "application/mac-compactpro",
    "csh": "application/x-csh",
    "css": "text/css",
    "dcr": "application/x-director",
    "dif": "video/x-dv",
    "dir": "application/x-director",
    "djv": "image/vnd.djvu",
    "djvu": "image/vnd.djvu",
    "dll": "application/octet-stream",
    "dmg": "application/octet-stream",
    "dms": "application/octet-stream",
    "doc": "application/msword",
    "dtd": "application/xml-dtd",
    "dv": "video/x-dv",
    "dvi": "application/x-dvi",
    "dxr": "application/x-director",
    "eps": "application/postscript",
    "etx": "text/x-setext",
    "exe": "application/octet-stream",
    "ez": "application/andrew-inset",
    "gif": "image/gif",
    "gram": "application/srgs",
    "grxml": "application/srgs+xml",
    "gtar": "application/x-gtar",
    "hdf": "application/x-hdf",
    "hqx": "application/mac-binhex40",
    "htm": "text/html",
    "html": "text/html",
    "ice": "x-conference/x-cooltalk",
    "ico": "image/x-icon",
    "ics": "text/calendar",
    "ief": "image/ief",
    "ifb": "text/calendar",
    "iges": "model/iges",
    "igs": "model/iges",
    "jnlp": "application/x-java-jnlp-file",
    "jp2": "image/jp2",
    "jpe": "image/jpeg",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "js": "application/x-javascript",
    "kar": "audio/midi",
    "latex": "application/x-latex",
    "lha": "application/octet-stream",
    "lzh": "application/octet-stream",
    "m3u": "audio/x-mpegurl",
    "m4a": "audio/mp4a-latm",
    "m4b": "audio/mp4a-latm",
    "m4p": "audio/mp4a-latm",
    "m4u": "video/vnd.mpegurl",
    "m4v": "video/x-m4v",
    "mac": "image/x-macpaint",
    "man": "application/x-troff-man",
    "mathml": "application/mathml+xml",
    "me": "application/x-troff-me",
    "mesh": "model/mesh",
    "mid": "audio/midi",
    "midi": "audio/midi",
    "mif": "application/vnd.mif",
    "mov": "video/quicktime",
    "movie": "video/x-sgi-movie",
    "mp2": "audio/mpeg",
    "mp3": "audio/mpeg",
    "mp4": "video/mp4",
    "mpe": "video/mpeg",
    "mpeg": "video/mpeg",
    "mpg": "video/mpeg",
    "mpga": "audio/mpeg",
    "ms": "application/x-troff-ms",
    "msh": "model/mesh",
    "mxu": "video/vnd.mpegurl",
    "nc": "application/x-netcdf",
    "oda": "application/oda",
    "ogg": "application/ogg",
    "pbm": "image/x-portable-bitmap",
    "pct": "image/pict",
    "pdb": "chemical/x-pdb",
    "pdf": "application/pdf",
    "pgm": "image/x-portable-graymap",
    "pgn": "application/x-chess-pgn",
    "pic": "image/pict",
    "pict": "image/pict",
    "png": "image/png",
    "pnm": "image/x-portable-anymap",
    "pnt": "image/x-macpaint",
    "pntg": "image/x-macpaint",
    "ppm": "image/x-portable-pixmap",
    "ppt": "application/vnd.ms-powerpoint",
    "ps": "application/postscript",
    "qt": "video/quicktime",
    "qti": "image/x-quicktime",
    "qtif": "image/x-quicktime",
    "ra": "audio/x-pn-realaudio",
    "ram": "audio/x-pn-realaudio",
    "ras": "image/x-cmu-raster",
    "rdf": "application/rdf+xml",
    "rgb": "image/x-rgb",
    "rm": "application/vnd.rn-realmedia",
    "roff": "application/x-troff",
    "rtf": "text/rtf",
    "rtx": "text/richtext",
    "sgm": "text/sgml",
    "sgml": "text/sgml",
    "sh": "application/x-sh",
    "shar": "application/x-shar",
    "silo": "model/mesh",
    "sit": "application/x-stuffit",
    "skd": "application/x-koan",
    "skm": "application/x-koan",
    "skp": "application/x-koan",
    "skt": "application/x-koan",
    "smi": "application/smil",
    "smil": "application/smil",
    "snd": "audio/basic",
    "so": "application/octet-stream",
    "spl": "application/x-futuresplash",
    "src": "application/x-wais-source",
    "sv4cpio": "application/x-sv4cpio",
    "sv4crc": "application/x-sv4crc",
    "svg": "image/svg+xml",
    "swf": "application/x-shockwave-flash",
    "t": "application/x-troff",
    "tar": "application/x-tar",
    "tcl": "application/x-tcl",
    "tex": "application/x-tex",
    "texi": "application/x-texinfo",
    "texinfo": "application/x-texinfo",
    "tif": "image/tiff",
    "tiff": "image/tiff",
    "tr": "application/x-troff",
    "tsv": "text/tab-separated-values",
    "txt": "text/plain",
    "ustar": "application/x-ustar",
    "vcd": "application/x-cdlink",
    "vrml": "model/vrml",
    "vxml": "application/voicexml+xml",
    "wav": "audio/x-wav",
    "wbmp": "image/vnd.wap.wbmp",
    "wbmxl": "application/vnd.wap.wbxml",
    "wml": "text/vnd.wap.wml",
    "wmlc": "application/vnd.wap.wmlc",
    "wmls": "text/vnd.wap.wmlscript",
    "wmlsc": "application/vnd.wap.wmlscriptc",
    "wrl": "model/vrml",
    "xbm": "image/x-xbitmap",
    "xht": "application/xhtml+xml",
    "xhtml": "application/xhtml+xml",
    "xls": "application/vnd.ms-excel",
    "xml": "application/xml",
    "xpm": "image/x-xpixmap",
    "xsl": "application/xml",
    "xslt": "application/xslt+xml",
    "xul": "application/vnd.mozilla.xul+xml",
    "xwd": "image/x-xwindowdump",
    "xyz": "chemical/x-xyz",
    "zip": "application/zip"
];

struct Entry
{
    int CompressedSize;
    int UncompressedSize;
    int Offset;
    string MIME;
    string Name;
    string path; //This is an internal field and
                 //won't be written to the archive.
}

string getMimeType( in string file )
{
    auto ext = file.extension.toLower[1 .. $];
    auto mime = ext in MimeLookup;
    
    if( !mime )
        return "application/octet-stream";
    else
        return *mime;
}

string randomString( int length = 15, char[] chars = null )
{
    if( chars is null )
        chars = "ABCDEFGIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz".dup;
    
    string result;
    foreach( _; 0 .. length )
        result ~= chars[uniform( 0, chars.length )];
    
    return result;
}

void printfln( T... )( string format, T args )
{
    if( silent )
        return;

    io.writefln( format, args );
}

void println( T... )( T args )
{
    if( silent )
        return;

    io.writeln( args );
}

string normalize( in string str )
{
    return str[root.length + 1 .. $].replace( "\\", "/" );
}

bool endsWith( in string str, in string value )
{
    if( value.length > str.length )
        return false;
    
    if( value == str )
        return true;
    
    return str[str.length - value.length .. $] == value;
}

d gistfile1.d

gistfile1.d
import carbon.monad;

import std.stdio;
import std.range;
import std.random;
import std.string;

pure:  // 以下全部pure !!

// pure
alias readlnM = monad!readln;

// pure
alias getArgs = global!(string[], "mainArgs").get;

// pure
// 入力を一行受け取って、1~4回(ランダム)だけ繰り返すようなレンジを作って、それをコンソールに表示する
alias main = haskMain!(() => readlnM.bind!(a => a.chomp.repeat(uniform(1, 5))).bind!writeln);

d D编程语言的一小组实用程序函数。

D编程语言的一小组实用程序函数。

util.d
module util;

bool contains( T )( T[] a, T item )
{
	foreach( x; a )
		if( x == item )
			return true;
	
	return false;
}

bool contains( TKey, TValue )( TValue[TKey] map, TValue item )
{
	foreach( k, v; map )
		if( v == item )
			return true;
	
	return false;
}

bool containsKey( TKey, TValue )( TValue[TKey] map, TKey key )
{
	foreach( k, v; map )
		if( k == key )
			return true;
	
	return false;
}

TValue[] values( TKey, TValue )( TValue[TKey] map )
{
	TValue[] values;
	
	foreach( k, v; map )
		values ~= v;
	
	return values;
}

R[] select( T, R )( T[] a, R delegate( T ) selector )
{
	R[] ret;
	
	foreach( x; a )
		ret ~= selector( x );
	
	return ret;
}

char toUpper( in char c )
{ 
	if ( c >= 'a' && c <= 'z' )
		return cast( char )( c - 0x20 );
	else
		return c;
}

char toLower( in char c )
{
	if( c >= 'A' && c <= 'z' )
		return cast( char )( c + 0x20 );
	else
		return c;
}

string titleCase( string s )
{
	import std.uni : isAlpha;
	
	bool capNext = true;
	char[] n;
	
	foreach( c; s.dup )
	{
		if( !c.isAlpha() )
			capNext = true;
		else if( capNext )
		{
			n ~= c.toUpper();
			capNext = false;
			continue;
		}
		
		n ~= c;
	}
	
	return n.idup;
}

//Alternative to std.algorithm.sort.
//Implements a simple, non-optomized, and unstable quicksort.
T[] qsort( T )( T[] arr, int delegate( T, T ) cmp )
{
	if( arr.length < 2 )
		return arr;
	
	T pivot = arr[0];
	T[] a, b;
	
	for( int i = 1; i < arr.length; ++ i )
	{
		T x = arr[i];
		
		if( cmp( x, pivot ) < cmp( pivot, x ) )
			a ~= x;
		else
			b ~= x;
	}
	
	return qsort( a, cmp ) ~ pivot ~ qsort( b, cmp );
}

//Alternative to std.algorithm.group
uint[T] group( T )( T[] a )
{
	uint[T] counts;
	
	foreach( x; a )
		counts[x] += 1;
	
	return counts;
}

string padRight( in string s, char padChar, int len )
{
	if( s.length >= len )
		return s;
	
	int diff = len - s.length;
	string n = s;
	
	foreach( _; 0 .. diff )
		n ~= padChar;
	
	return n;
}

string padLeft( in string s, char padChar, int len )
{
	if( s.length >= len )
		return s;
	
	int diff = len - s.length;
	string n;
	
	foreach( _; 0 .. diff )
		n ~= padChar;
	
	return n ~ s;
}

string pad( in string s, char padChar, int len )
{
	if( s.length >= len )
		return s;
	
	int diff = len - s.length;
	string n;
	
	int leftPad  = ( diff % 2 ) ? diff / 2 : ( diff - 1 ) / 2;
	int rightPad = diff - leftPad;
	
	foreach( _; 0 .. leftPad )
		n ~= padChar;
	
	n ~= s;
	
	foreach( _; 0 .. rightPad )
		n ~= padChar;
	
	return n;
}

d 一个简单的包装器,用于处理事件回调的代理和函数,类似于.NET风格的事件。

一个简单的包装器,用于处理事件回调的代理和函数,类似于.NET风格的事件。

Example.d
/+
    A simple example showing usage of the
    EventHandler struct.
+/

import eventhandler;
import std.stdio;

class List( T )
{
    private alias EventHandler!( T ) AddedHandler;
    private AddedHandler onItemAdded;
    
    public ref AddedHandler ItemAdded() @property
    {
        return this.onItemAdded;
    }
    
    private T[] array;
    
    public T[] Items() @property
    {
        return this.array.dup;
    }
    
    public void Add( T item )
    {
        this.array ~= item;
        this.onItemAdded( item );
    }
}

void printNewItem( string item )
{
    "'%s' was added to the list.".writefln( item );
}

void main()
{
    auto list = new List!( string );
    list.ItemAdded += &printNewItem;
    
    list.Add( "foo" );
    // Output: 'foo' was added to the list.
}
EventHandler.d
module eventhandler;

/+
    EventHandler implementation.
+/

struct EventHandler( TArgs... )
{
    private import std.algorithm;
    
    public alias void delegate( TArgs ) DelegateType;
    public alias void function( TArgs ) FunctionType;
    
    private FunctionType[] functions;
    private DelegateType[] delegates;
    
    public void opAssign( T... )( T args )
    {
        throw new Exception( "EventHandler objects may not be assigned to directly, please use += or -= instead." );
    }
    
    public void opOpAssign( string op )( FunctionType fn ) if( op == "+" )
    {
        this.functions ~= fn;
    }
    
    public void opOpAssign( string op )( DelegateType dg ) if( op == "+" )
    {
        this.delegates ~= dg;
    }
    
    public void opOpAssign( string op )( FunctionType fn ) if( op == "-" )
    {
        if( !this.functions.canFind( fn ) )
            return;
        else
            this.functions = this.functions.remove!( x => x == fn );
    }
    
    public void opOpAssign( string op )( DelegateType dg ) if( op == "-" )
    {
        if( !this.delegates.canFind( dg ) )
            return;
        else
            this.delegates = this.delegates.remove!( x => x == dg );
    }
    
    public void opCall( TArgs args )
    {
        int len = max( this.delegates.length, this.functions.length );
        
        for( int i = 0; i < len; ++ i )
        {
            if( i < this.functions.length )
                this.functions[i]( args );
            
            if( i < this.delegates.length )
                this.delegates[i]( args );
        }
    }
}

d graphiteつかってTwitter API叩く的なサムシング

graphiteつかってTwitter API叩く的なサムシング

gistfile1.d
import std.stdio;
import core.thread;
import std.net.curl;
import std.algorithm;
import std.regex;
import std.string;
import std.range;
import std.array;
import std.file;
import std.path;
import std.datetime;
import graphite.twitter;


immutable consumerToken =
    ConsumerToken("xxxxxxxxxxxxxxxxxxxx",
                  "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

immutable accessToken =
    AccessToken(consumerToken,
        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

enum downloadedHTMLFilePath = "last.html";
enum lectureInfoWebURL = "https://www.ead.tut.ac.jp/board/main.aspx";


string getClassInfoHTML()
{
    return std.net.curl.get(lectureInfoWebURL).idup;
}


void main()
{
    auto twitter = Twitter(accessToken);
    twitter.callAPI!"statuses.update"(["status" : ".@k3_kaimu 広報用プログラムが起動しました on " ~ Clock.currTime.toSimpleString()]);

    if(exists(downloadedHTMLFilePath)){
        immutable newVersion = getClassInfoHTML(),
                  oldVersion = std.file.readText(downloadedHTMLFilePath);

        if(newVersion != oldVersion){
            twitter.callAPI!"statuses.update"(["status" : ".@k3_kaimu 補講・休講情報にアップデートがありました " ~ lectureInfoWebURL ~ " " ~ Clock.currTime.toSimpleString()]);
            std.file.write(downloadedHTMLFilePath, newVersion);
        }
    }else
        std.file.write(downloadedHTMLFilePath, getClassInfoHTML());
}

d [Dtrace与SMB相关的片段]所有东西SMB #tags:smb,dtrace,io

[Dtrace与SMB相关的片段]所有东西SMB #tags:smb,dtrace,io

smb_vop_lookup_access.d
dtrace -qn '
  ::smb_vop_lookup:entry {
    self->x = 1}
  ::zfs_zaccess:entry /self->x/ {
    self->y = 1;
    self->x = 0;
    self->flags = args[0]->z_pflags;
    self->mode = args[1];
  }
  ::vn_rele:entry /self->y/ {
    @r[stringof(args[0]->v_path), self->flags, self->mode] = count();
    self->rc = 0;
    self->y = 0;
    self->flags = 0;
    self->mode = 0;
  }
  END {printa("%s flags: 0x%x mode: 0x%x\n", @r);}'
smb.d
# Trace smb2 write events
dtrace -qn 'fbt:smbsrv:smb2_write:entry {
    @[stringof(args[0]->uid_user->u_name),
    (uint16_t)args[0]->uid_user->u_uid,
    args[0]->smb_error.status,
    (uint16_t)args[0]->smb_error.errcode,
    stringof(args[0]->tid_tree->t_sharename), 
    stringof(args[0]->tid_tree->t_volume), 
    stringof(args[0]->tid_tree->t_resource),
    stringof(args[0]->tid_tree->t_snode->vp->v_path)
    ]= count(); }'
    
# Trace mkdir and rename operations with vnode info and name of file or dir being changed
dtrace -qn 'fbt:smbsrv:smb_vop_mkdir:entry,
    fbt:smbsrv:smb_vop_rename:entry {
    @[probefunc, stringof(args[1]), stringof(args[0]->v_path)] = count(); }
    tick-1sec {printa(@); trunc(@);}'
smb-write-offsets.d
// Measure Offsets of IOs issued, by file. We can observe with this snippet
// how IOs are issued, in terms of whether offsets are pretty constant or
// there is a healthy mix of different sizes. In general, differently sized
// offsets is a pretty solid indicator that IO is mostly random.
// This is an example of what we might see. In this instance all offsets are
// 1MB in size.
//   /storage/p01/global/smb/00/CentOS-6.8-x86_64-LiveDVD.iso
//            value  ------------- Distribution ------------- count
//           524288 |                                         0
//          1048576 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 391
//          2097152 |
dtrace -qn '
self offset_t prev[smb_node_t *];
::smb_fsop_write:entry /!self->prev[args[2]]/ {
self->prev[args[2]] = args[3]->_uio_offset._f;
}
::smb_fsop_write:entry
/self->prev[args[2]] != args[3]->_uio_offset._f/ {
    this->o = args[3]->_uio_offset._f;
    this->diff = this->o > self->prev[args[2]] ?
                this->o - self->prev[args[2]] :
                self->prev[args[2]] - this->o;
    @[stringof(args[2]->vp->v_path)] = quantize(this->diff);
    self->prev[args[2]] = 0;
}'
smb-vop_lookup-access.d
dtrace -qn '
  /* 
   * See /usr/src/uts/common/sys/errno.h for all error codes. 
   */
  ::smb_vop_lookup:entry /args[0]->v_path != NULL/ {
    self->path  = (stringof(*(struct vnode *)args[0]).v_path);
    self->user  = args[8]->cr_uid;
    self->group = args[8]->cr_uid;
}
  ::smb_vop_lookup:return /self->path != NULL/ {
  this->err = args[1]; /* non-zero here implies a problem */
  @[self->path, self->user, self->group, this->err] = count();
}
END {
  printa("Path: %s uid: %d gid: %d retcode: %d Count: %@d\n", @);
}'
smb-oplock-acquire-time.d
dtrace -qn '
  BEGIN {
  ts = walltimestamp;
  /* print header for CSV */
  printf("timestamp,filename,avg.duration.ns,total.duration.ns\n");
  }
  ::smb_oplock_acquire:entry {
    this->start = timestamp;
    this->fname = stringof(args[2]->f_node->od_name);
  }
  ::smb_oplock_acquire:return {
    @[ts, this->fname] = sum(timestamp - this->start);
    /* average amount of time per op-lock acquisition */
    @av_dur[ts, this->fname] = avg(timestamp - this->start);
  }
  tick-5sec {
    printa("%Y,%s,%@d,%@d\n", @, @av_dur); 
    trunc(@); trunc(@av_dur);
    ts = walltimestamp;
  }'
smb-ofiles-count.d
dtrace -qn '
  BEGIN {
    ts = walltimestamp;
    printf("timestamp,address,mean,max\n");
  }
  ::smb_open_subr:entry /ts/ {
    @fopenmax[ts, args[0]->session->ip_addr_str] =
     max(args[0]->session->s_file_cnt);
    @fopenav[ts, args[0]->session->ip_addr_str]  =
     avg(args[0]->session->s_file_cnt);
  }
  tick-60sec {
    printa("%Y,%s,%@d,%@d\n", @fopenav, @fopenmax);
    trunc(@fopenav); trunc(@fopenmax);
    ts = walltimestamp;
  }'
smb-is-read_control-set.d
dtrace -qn '
  ::smb_access_generic_to_file:entry {
    this->entry = args[0];
  }
  ::smb_access_generic_to_file:return {
    @[this->entry, args[1], args[1] == (args[1]|0x00020000L) ? "Y" : "N"] = count();
  }
  tick-5sec {
    printa("StartV: 0x%x, EndV: 0x%x READ_CONTROL: %s Count: %@d\n", @);
    trunc(@);
  }'
idmap-sid-lookup.d
#!/usr/sbin/dtrace -qCs
#include <sys/idmap.h>

BEGIN {
  m[IDMAP_SUCCESS] = "found, OK" ;
  m[IDMAP_ERR_NOTFOUND] = "not found" ;

}
::smb_idmap_getsid:entry { this->uid = args[0] ; }
::smb_idmap_getsid:return { 
  printf("uid: %d result: %s\n", 
          this->uid, m[args[1]]) ; 
}
smb-read-write-lat-by-path.d
dtrace -qn '
  char m[string];
  BEGIN {
    m["smb_fsop_read"] = 0x52;
    m["smb_fsop_write"] = 0x57;
  }
  ::smb_fsop_read:entry,::smb_fsop_write:entry {
    self->arg0 = args[0];
    self->node = args[2];
    self->t = timestamp;
    
    /* printf("%s\n", inet_ntop(this->a_family, this->addr));
    printf("%s\n", inet_ntop(this->l_a_family, this->l_addr)); */
}

::smb_fsop_read:return,::smb_fsop_write:return /self->arg0/ {
  this->delta = timestamp - self->t;
  this->a_family = self->arg0->session->ipaddr.a_family;
  this->l_a_family = self->arg0->session->local_ipaddr.a_family;
  this->addr = &self->arg0->session->ipaddr.au_addr.au_ipv4;
  this->l_addr = &self->arg0->session->local_ipaddr.au_addr.au_ipv4;
    @[m[probefunc], stringof(self->node->vp->v_path),
    inet_ntop(this->a_family, this->addr),
    inet_ntop(this->l_a_family, this->l_addr)] = quantize(this->delta);
  self->arg0 = 0; self->t = 0; self->node = 0;
}

END {
  printa("\t   (%c) %s\n\t   src: %s dst: %s%@d\n", @);
}'

d gistfile1.d

gistfile1.d
import std.stdio;
import std.algorithm;
import std.typecons;

void main()
{

}


uint solveMaxProdN(real s, real c, real r, uint money)
{
    immutable sumP = s + c + r;
    s /= sumP;
    c /= sumP;
    r /= sumP;
    Tuple!(uint, real)[] ds;    // 生産数Nと期待値E

    auto N_1 = min(money / 80u, 100u),
         E_1 = N_1 * 20.0L;

    ds ~= tuple(N_1, E_1);

    if(money / 80 > 100){
        auto N_2 = min(money / 80u, r > 0.2 ? 100u : 300u),
             E_2 = 10_000 * r + N_2 * (20 * (1 - r) - 80 * r);

        ds ~= tuple(N_2, E_2);
    }

    if(money / 80 > 300){
        auto N_3 = min(money / 80u, s > 0.8 ? 500u : 300u),
             E_3 = 10_000 * r + 30_000 * c + N_3 * (20 * s - 80 * (1 - s));

        ds ~= tuple(N_3, E_3);
    }

    // 期待値が最大となる生産数Nを返す
    return reduce!"a[1]>b[1] ? a : b"(tuple(0u, -100_000.0L), ds)[0];
}

unittest{
    assert(solveMaxProdN(1, 1, 1, 80*500) == 100);
    assert(solveMaxProdN(1, 1, 1, 80*30) == 30);
    assert(solveMaxProdN(1, 0, 0, 80*500) == 500);
    assert(solveMaxProdN(1, 0, 0, 80*210) == 210);
    assert(solveMaxProdN(0, 1, 0, 80*500) == 300);
    assert(solveMaxProdN(0, 1, 0, 80*150) == 150);
    assert(solveMaxProdN(0, 0, 1, 80*500) == 100);
    assert(solveMaxProdN(0, 0, 1, 80*40) == 40);
}

d Dtrace片段用于观察ZFS TXG操作。

Dtrace片段用于观察ZFS TXG操作。

zfs-txg-observability.d
# dtrace -qn '::BEGIN {x["rand"] = 0} ::txg_delay:entry { @[walltimestamp] = count(); x["rand"] = 1; } tick-1sec /x["rand"] == 1/ {printa("%Y %@d\n", @); trunc(@); x["rand"] = 0; }'
zfs-bio-tput+latency.d
#!/usr/sbin/dtrace -s
#pragma D option quiet
/*
  This script should give us a quantized distribution of
  time delta from io:::start to io:::done, i.e. amount of
  time it takes to complete IO operations, separated by
  reads and writes.
*/
BEGIN
{
        start = timestamp;
}

io:::start
{
        ts[args[0]->b_edev, args[0]->b_lblkno] = timestamp;
}

io:::done
/ts[args[0]->b_edev, args[0]->b_lblkno]/
{
        this->delta = (timestamp - ts[args[0]->b_edev, args[0]->b_lblkno]) / 1000;
        this->name = (args[0]->b_flags & (B_READ | B_WRITE)) == B_READ ?
            "read " : "write ";

        @q[this->name] = quantize(this->delta);
        @a[this->name] = avg(this->delta);
        @v[this->name] = stddev(this->delta);
        @i[this->name] = count();
        @b[this->name] = sum(args[0]->b_bcount);

        ts[args[0]->b_edev, args[0]->b_lblkno] = 0;
}

END
{
        printa(@q);

        normalize(@i, (timestamp - start) / 1000000000);
        normalize(@b, (timestamp - start) / 1000000000 * 1024);

        printf("%-30s %11s %11s %11s %11s\n", "", "avg latency", "stddev",
            "iops", "throughput");
        printa("%-30s %@9uus %@9uus %@9u/s %@8uk/s\n", @a, @v, @i, @b);
}
txg-syncing-size.d
// On a healthy system we should see these events roughly every 5 seconds.
// Give name of pool as single argument to this snippet.
# dtrace -qn '
txg-syncing
{
        this->dp = (dsl_pool_t *)arg0;
}

txg-syncing
/this->dp->dp_spa->spa_name == $$1/
{
        printf("%Y %4dMB of %4dMB used\n", walltimestamp, this->dp->dp_dirty_total >> 20,
            `zfs_dirty_data_max >> 20);
}
'

// A more generic version where all pools are given to us as the syncs happen.
# dtrace -qn '
txg-syncing
{
        this->dp = (dsl_pool_t *)arg0;
        this->spa = *((dsl_pool_t *)arg0)->dp_spa;
}

txg-syncing
/this->spa.spa_name != NULL/
{
        printf("%Y %-16s %4dMB of %4dMB used\n", 
            walltimestamp,
            this->spa.spa_name, 
            this->dp->dp_dirty_total >> 20,
            `zfs_dirty_data_max >> 20);
}
'