As I have often remarked, DTrace is a workhorse, not a show horse: the features that we have added to DTrace over the years come not from theoretical notions but rather from actual needs in production environments. This is as true now as it was a decade ago, and even in core abstractions, extensive use of DTrace seems to give rise to new ways of thinking about them. In particular, Brendan recently had a couple of feature ideas for aggregation processing that have turned out to be really interesting…
agghist
When one performs a count() or sum() aggregating action, the result is a table that consists of values and numbers, e.g.:
# dtrace -n syscall:::entry'{@[execname] = count()}' dtrace: description 'syscall:::entry' matched 233 probes ^C utmpd 2 metadata 5 pickup 14 ur-agent 18 epmd 20 fmd 20 jsontool.js 33 vmadmd 33 master 41 mdnsd 41 intrd 45 devfsadm 52 bridged 81 mkdir 83 ipmgmtd 95 cat 96 ls 100 truss 101 ipmon 106 sendmail 111 dirname 115 svc.startd 125 svc.configd 153 ksh93 164 zoneadmd 165 svcprop 258 sshd 262 bash 291 zpool 436 date 448 cron 470 lldpd 584 dump-minutely-sd 611 haproxy 760 nscd 1275 zfs 1966 zoneadm 2414 java 2916 tail 3750 postgres 6702 redis-server 21283 dtrace 28308 node 39836 beam.smp 55940
Brendan’s observation was that it would be neat to (optionally) use the histogram-style aggregation printing with these count()/sum() aggregations to be able to more easily differentiate values. For that, we have the new “-x agghist” option:
# dtrace -n syscall:::entry'{@[execname] = count()}' -x agghist dtrace: description 'syscall:::entry' matched 233 probes ^C key ------------- Distribution ------------- count utmpd | 2 metadata | 5 pickup | 14 epmd | 20 fmd | 23 ur-agent | 25 init | 27 mdnsd | 30 jsontool.js | 33 vmadmd | 36 master | 41 intrd | 45 devfsadm | 52 bridged | 78 mkdir | 83 cat | 96 ipmgmtd | 97 ls | 100 sendmail | 101 ipmon | 102 dirname | 115 svc.startd | 125 truss | 140 svc.configd | 153 ksh93 | 164 sshd | 248 svcprop | 258 bash | 290 zoneadmd | 387 zpool | 436 date | 448 cron | 470 lldpd | 562 dump-minutely-sd | 615 haproxy | 622 nscd | 808 zfs | 1966 zoneadm |@ 2414 java |@ 3003 tail |@ 3607 postgres |@ 5728 redis-server |@@@@@ 20611 dtrace |@@@@@@ 26966 node |@@@@@@@@@@ 39825 beam.smp |@@@@@@@@@@@@@ 55942
It’s obviously the same information, but presented with a quicker visual cue as to the distribution. Now, one may well note that this output has a lot of dead whitespace in it — read on.
aggzoom
The DTrace histogram-style output came directly from lockstat, which proceeded it by several years. lockstat was important for DTrace in several ways: aside from showing the power of production instrumentation, lockstat pointed to the need for first-class aggregations, for disjoint instrumentation providers and for multiplexed consumers (for which it was repaid by having its guts ripped out, being reimplemented as both a DTrace provider and a DTrace consumer). Due to some combination of my laziness and my reverence for the lockstat-style histogram, I simply lifted the lockstat processing code — which means I inherited (or stole, depending on your perspective) the decisions Jeff had made with regard to histogram rendering. In particular, lockstat determines the height of a bar by taking the bucket’s share of the total and multiplying it by the full height of the histogram. That is, if you add up all of the heights of all of the bars, they will add up to the full height of the histogram. The benefit of this is that it quickly tells you relative distribution: if you see a full-height bar, you know that that bucket represents essentially all of the values. But the problem is that if the number of buckets is large and/or the values of those buckets are relatively evenly distributed, the result is a bunch of very short bars (or worse, zero height bars) and dead whitespace. This can become so annoying to DTrace users that Brendan has been known to observe that this is the one improvement over DTrace that he can point to in SystemTap: that instead of dividing the height of the histogram among all of the bars, each bar has a height in proportion to the histogram height that is its value in proportion to the bucket of greatest value. Of course, the shape of the distribution doesn’t change — one simply is automatically scaling the height of the highest bar to the height of the histogram and adjusting all other bars accordingly.
To allow for this behavior, I added “-x aggzoom“; running the same example as above with this new option:
# dtrace -n syscall:::entry'{@[execname] = count()}' -x agghist -x aggzoom dtrace: description 'syscall:::entry' matched 233 probes ^C key ------------- Distribution ------------- count utmpd | 2 metadata | 7 pickup | 14 ur-agent | 20 fmd | 25 epmd | 26 jsontool.js | 33 mdnsd | 39 master | 41 vmadmd | 44 rsyslogd | 57 devfsadm | 66 mkdir | 83 intrd | 90 cat | 96 bridged | 99 ls | 100 dirname | 115 truss | 125 ipmon | 130 sendmail | 131 ipmgmtd | 133 ksh93 | 164 svc.startd | 179 init | 189 sshd | 253 svcprop | 258 bash | 283 zpool | 436 date | 448 cron | 470 dump-minutely-sd | 611 lldpd | 716 svc.configd |@ 939 haproxy |@ 1097 zoneadmd |@ 1455 zfs |@ 1966 zoneadm |@@ 2414 nscd |@@ 2867 java |@@@ 4277 tail |@@@ 4579 postgres |@@@@@@ 9389 redis-server |@@@@@@@@@@@@@@@@@@ 26177 dtrace |@@@@@@@@@@@@@@@@@@@@@@@ 34530 node |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 48151 beam.smp |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 55940
It’s zoomtastic!
aggpack
If you follow Brendan’s blog, you know that he’s always experimenting with ways of visually communicating a maximal amount of system data. One recent visualization that has been particularly interesting are his frequency trails — a kind of stacked sparkline. Thinking about density of presentation sparked Brendan to observe that he often needs to try to visually correlate multiple quantize()/lquantize() aggregation keys, e.g. this classic DTrace “one-liner” to show the distribution of system call time (in nanoseconds) by system call:
# dtrace -n syscall:::entry'{self->ts = timestamp}' \ -n syscall:::return'/self->ts/{@[probefunc] = \ quantize(timestamp - self->ts); self->ts = 0}' dtrace: description 'syscall:::entry' matched 233 probes dtrace: description 'syscall:::return' matched 233 probes ^C sigpending value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 2048 | 0 llseek value ------------- Distribution ------------- count 256 | 0 512 |@@@@@@@@@@@@@@@@@@@@ 1 1024 |@@@@@@@@@@@@@@@@@@@@ 1 2048 | 0 fstat value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 4096 | 0 setcontext value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 4096 | 0 fstat64 value ------------- Distribution ------------- count 256 | 0 512 |@@@@@@@@@@@@@@@@@@@@ 2 1024 |@@@@@@@@@@@@@@@@@@@@ 2 2048 | 0 sigaction value ------------- Distribution ------------- count 256 | 0 512 |@@@@@@@@@@@@@@@@@@@@ 2 1024 |@@@@@@@@@@@@@@@@@@@@ 2 2048 | 0 getuid value ------------- Distribution ------------- count 128 | 0 256 |@@@@@@@@@@@@@@@@@@@@ 1 512 | 0 1024 | 0 2048 | 0 4096 |@@@@@@@@@@@@@@@@@@@@ 1 8192 | 0 accept value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@ 1 4096 |@@@@@@@@@@@@@@@@@@@@ 1 8192 | 0 sysconfig value ------------- Distribution ------------- count 256 | 0 512 |@@@@@@@@@@@@@ 2 1024 |@@@@@@@@@@@@@@@@@@@@ 3 2048 |@@@@@@@ 1 4096 | 0 bind value ------------- Distribution ------------- count 2048 | 0 4096 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2 8192 | 0 lwp_sigmask value ------------- Distribution ------------- count 256 | 0 512 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 11 1024 |@@@ 1 2048 |@@@ 1 4096 | 0 recvfrom value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 5 4096 | 0 setsockopt value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@ 1 2048 |@@@@@@@@@@ 1 4096 |@@@@@@@@@@@@@@@@@@@@ 2 8192 | 0 fcntl value ------------- Distribution ------------- count 128 | 0 256 |@@@ 1 512 |@@@@@@@@@@@@@@@@@@@@ 8 1024 |@@@@@@@@@@@@@@@ 6 2048 |@@@ 1 4096 | 0 systeminfo value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@@@@@@ 3 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@ 5 4096 | 0 schedctl value ------------- Distribution ------------- count 8192 | 0 16384 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 32768 | 0 getsockopt value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 11 2048 |@@@@@@@@@ 3 4096 | 0 stat value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ 4 4096 | 0 8192 |@@@@@@@@@@@@@ 2 16384 | 0 recvmsg value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 15 4096 |@@@@ 2 8192 |@@ 1 16384 | 0 connect value ------------- Distribution ------------- count 2048 | 0 4096 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ 4 8192 | 0 16384 |@@@@@@@@@@@@@ 2 32768 | 0 brk value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@ 2 2048 |@@@@@@@@@@@@@@@ 3 4096 | 0 8192 |@@@@@@@@@@ 2 16384 | 0 32768 |@@@@@ 1 65536 | 0 so_socket value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@@@@@@@@@@ 5 4096 | 0 8192 |@@@@@@@@@ 2 16384 |@@@@@@@@@ 2 32768 | 0 mmap value ------------- Distribution ------------- count 32768 | 0 65536 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 131072 | 0 putmsg value ------------- Distribution ------------- count 32768 | 0 65536 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 131072 | 0 p_online value ------------- Distribution ------------- count 128 | 0 256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 250 512 |@ 5 1024 | 0 2048 | 1 4096 | 0 getpid value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@ 9 2048 |@@@@@@@@@@@@@@@@@@@@@@@ 21 4096 |@@ 2 8192 |@@@@ 4 16384 | 0 close value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@@@@@@@@@@ 13 2048 |@@@@@@@ 5 4096 |@@@@@@@ 5 8192 |@@@ 2 16384 |@@@@ 3 32768 | 0 lseek value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@@@@@ 22 2048 |@@@@@@@@@@@@@@@@@@@@@@@ 37 4096 |@@ 3 8192 |@ 2 16384 | 0 open value ------------- Distribution ------------- count 1024 | 0 2048 |@@@@@@@@@@@@@ 6 4096 |@@@@@@@ 3 8192 |@@@@@@@@@@@@@ 6 16384 |@@ 1 32768 |@@@@ 2 65536 | 0 lwp_cond_signal value ------------- Distribution ------------- count 2048 | 0 4096 |@@@@@@ 2 8192 | 0 16384 |@@@@@@@@@@@@@@@@@@@@@@@ 8 32768 |@@@@@@@@@@@ 4 65536 | 0 sendmsg value ------------- Distribution ------------- count 8192 | 0 16384 |@@@@ 1 32768 |@@@@@@@@@@@@@@@@@@@@@@@@@@@ 6 65536 |@@@@@@@@@ 2 131072 | 0 gtime value ------------- Distribution ------------- count 128 | 0 256 |@@@@@@@@@@@@@@@@@@@@ 576 512 |@@@@@@@@@@@@@@@@ 452 1024 |@@ 56 2048 |@ 34 4096 | 3 8192 | 8 16384 | 1 32768 | 1 65536 | 0 read value ------------- Distribution ------------- count 256 | 0 512 | 1 1024 |@@@@@@ 19 2048 |@@@@@@@@@@ 34 4096 |@@@@@ 15 8192 |@@@@@@@@@@@@@@@@@@ 61 16384 |@ 2 32768 | 0 stat64 value ------------- Distribution ------------- count 1024 | 0 2048 |@ 2 4096 |@ 1 8192 |@@@ 5 16384 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 52 32768 |@@@@ 7 65536 | 0 send value ------------- Distribution ------------- count 1024 | 0 2048 |@@ 2 4096 |@ 1 8192 |@@@@@@@@@@@@@@@@@@ 22 16384 |@@@@@@@@@@ 12 32768 |@@@@@ 6 65536 |@@@ 4 131072 |@@ 3 262144 | 0 write value ------------- Distribution ------------- count 256 | 0 512 |@@ 5 1024 |@@@@ 8 2048 | 0 4096 |@@@ 6 8192 |@@@@@@@@@@@ 23 16384 |@@@@@@@@ 16 32768 |@@@@@@@@@@ 22 65536 |@@ 4 131072 | 0 262144 | 1 524288 | 0 doorfs value ------------- Distribution ------------- count 2048 | 0 4096 |@@@@@@@@@@@@@@@@@@@@ 1 8192 | 0 16384 | 0 32768 | 0 65536 | 0 131072 | 0 262144 | 0 524288 | 0 1048576 | 0 2097152 |@@@@@@@@@@@@@@@@@@@@ 1 4194304 | 0 yield value ------------- Distribution ------------- count 512 | 0 1024 |@@@@@@@@@@@@@@@@@@@@@ 198 2048 |@ 5 4096 | 1 8192 |@@@@@@ 61 16384 |@@@@@@@@@@@@ 110 32768 | 2 65536 | 2 131072 | 0 262144 | 1 524288 | 0 recv value ------------- Distribution ------------- count 256 | 0 512 |@@@@@@@@@@@@ 17 1024 |@@@@ 6 2048 |@@@@@@@@@@@@@ 19 4096 |@@@@ 6 8192 | 0 16384 | 0 32768 | 0 65536 | 0 131072 |@@ 3 262144 |@@@@ 5 524288 | 0 1048576 | 0 2097152 | 0 4194304 | 0 8388608 | 0 16777216 |@ 1 33554432 | 0 nanosleep value ------------- Distribution ------------- count 4194304 | 0 8388608 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 201 16777216 | 0 33554432 | 0 67108864 | 0 134217728 | 0 268435456 | 0 536870912 | 2 1073741824 | 0 ioctl value ------------- Distribution ------------- count 128 | 0 256 |@ 50 512 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2157 1024 | 17 2048 | 8 4096 | 10 8192 | 13 16384 | 6 32768 | 3 65536 | 7 131072 | 0 262144 | 0 524288 | 0 1048576 | 0 2097152 | 0 4194304 | 0 8388608 | 2 16777216 | 0 33554432 | 0 67108864 | 6 134217728 | 0 268435456 | 1 536870912 | 4 1073741824 | 0 lwp_cond_wait value ------------- Distribution ------------- count 16777216 | 0 33554432 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 46 67108864 |@@@@ 6 134217728 | 0 268435456 |@ 2 536870912 |@@@@ 6 1073741824 | 0 pollsys value ------------- Distribution ------------- count 512 | 0 1024 |@ 2 2048 | 1 4096 | 1 8192 | 1 16384 | 0 32768 | 0 65536 |@ 2 131072 | 0 262144 |@@ 6 524288 |@ 2 1048576 |@@ 5 2097152 |@ 2 4194304 | 0 8388608 |@@@ 8 16777216 | 0 33554432 | 1 67108864 |@@@@@@@@@@@@@@@@@@ 56 134217728 |@@@@@@@@@ 28 268435456 | 0 536870912 |@@@ 8 1073741824 | 1 2147483648 | 0 lwp_park value ------------- Distribution ------------- count 1024 | 0 2048 | 6 4096 |@ 21 8192 |@ 20 16384 |@@@@@@@@@ 134 32768 |@@@@@@@@@@ 145 65536 |@@@@@@@@@@ 153 131072 |@ 11 262144 | 3 524288 | 0 1048576 | 0 2097152 | 0 4194304 | 0 8388608 |@@@@@@ 89 16777216 | 0 33554432 | 0 67108864 | 0 134217728 | 2 268435456 | 4 536870912 |@ 18 1073741824 | 2 2147483648 | 0 portfs value ------------- Distribution ------------- count 256 | 0 512 | 4 1024 |@@@ 41 2048 |@@@@ 44 4096 |@ 14 8192 | 2 16384 | 1 32768 | 2 65536 | 1 131072 | 3 262144 |@ 10 524288 | 3 1048576 | 6 2097152 | 3 4194304 | 0 8388608 |@@@@@@@@@@@@@@@@@@@@@@@ 272 16777216 | 1 33554432 | 1 67108864 | 4 134217728 | 2 268435456 | 5 536870912 |@@@@@ 58 1073741824 | 3 2147483648 | 1 4294967296 | 0
For those keeping score, that would be 482 lines of output — and that was for just a couple of seconds. Brendan’s observation was that it would be great to tip these aggregations on their side (so their bars point up-and-down, not left-to-right) — with the output for each key appearing on one line. This is clearly a great idea, and “-x aggpack” was born:
# dtrace -n syscall:::entry'{self->ts = vtimestamp}' \ -n syscall:::return'/self->ts/{@[probefunc] = \ quantize(vtimestamp - self->ts); self->ts = 0}' -x aggpack dtrace: description 'syscall:::entry' matched 233 probes dtrace: description 'syscall:::return' matched 233 probes ^C key min .------------------. max | count sigpending 8 : X : 1048576 | 1 lwp_continue 8 : X : 1048576 | 1 uucopy 8 : X : 1048576 | 1 getuid 8 : x x : 1048576 | 2 lwp_kill 8 : X : 1048576 | 1 sigaction 8 : xx x : 1048576 | 4 llseek 8 : x x : 1048576 | 2 fstat64 8 : xx : 1048576 | 4 setcontext 8 : xx : 1048576 | 2 lwp_sigmask 8 : xx _ : 1048576 | 10 systeminfo 8 : xx : 1048576 | 4 sysconfig 8 : ____x : 1048576 | 6 accept 8 : xx : 1048576 | 2 bind 8 : X : 1048576 | 2 fstat 8 : x x : 1048576 | 2 recvfrom 8 : X : 1048576 | 5 doorfs 8 : xx : 1048576 | 2 brk 8 : ___xx : 1048576 | 8 lwp_create 8 : X : 1048576 | 1 schedctl 8 : x x : 1048576 | 2 p_online 8 : X_ _ : 1048576 | 256 getpid 8 : xx_ _ : 1048576 | 28 recvmsg 8 : xx_ : 1048576 | 18 open 8 : xx x : 1048576 | 4 lseek 8 : xx _ _ : 1048576 | 9 getsockopt 8 : _xx : 1048576 | 43 stat 8 : x_x _ : 1048576 | 7 mmap 8 : X : 1048576 | 1 putmsg 8 : X : 1048576 | 1 recv 8 : _xxx__ : 1048576 | 65 gtime 8 : X_____ : 1048576 | 899 fcntl 8 : ___xx : 1048576 | 208 setsockopt 8 : __x__ : 1048576 | 55 sendmsg 8 : xxx : 1048576 | 9 lwp_cond_signal 8 : _x__x : 1048576 | 33 close 8 : __xx_ : 1048576 | 65 lwp_cond_wait 8 : _xx_ : 1048576 | 69 munmap 8 : X : 1048576 | 1 read 8 : ___x_x_ _ _ : 1048576 | 137 stat64 8 : ___X : 1048576 | 49 yield 8 : _ X___ : 1048576 | 1314 so_socket 8 : ___x_ : 1048576 | 60 nanosleep 8 : _x__ : 1048576 | 77 send 8 : ____xx__ : 1048576 | 66 pollsys 8 : _ _xx_ : 1048576 | 86 ioctl 8 : X_________ : 1048576 | 3259 connect 8 : _ _xx_ : 1048576 | 57 mmap64 8 : __x _ x : 1048576 | 14 write 8 : __ __x_____ : 1048576 | 115 portfs 8 : ______x_ : 1048576 | 619 lwp_park 8 : _xx_x_ : 1048576 | 654
This communicates essentially the same amount of information in just a fraction of the output (55 lines versus 482) — and makes it (much) easier to quickly compare distributions across aggregation keys. That said, one of the challenges here is that ASCII can be very limiting when trying to come with characters of clearly different height. When I demonstrated a prototype of this at the illumos BOF at Surge, a couple of folks volunteered that I should look into using the Unicode Block Elements for this output. Here’s the same enabling, but rendered using these elements:
# dtrace -n syscall:::entry'{self->ts = vtimestamp}' \ -n syscall:::return'/self->ts/{@[probefunc] = \ quantize(vtimestamp - self->ts); self->ts = 0}' -x aggpack dtrace: description 'syscall:::entry' matched 233 probes dtrace: description 'syscall:::return' matched 233 probes ^C key min .-------------------. max | count lwp_self 32 : █ : 8388608 | 1 sysconfig 32 : ▃▃▃ : 8388608 | 3 sigpending 32 : █ : 8388608 | 1 pset 32 : ▆ ▃ : 8388608 | 3 getsockname 32 : █ : 8388608 | 1 systeminfo 32 : ▅▄ : 8388608 | 5 fstat 32 : █ : 8388608 | 1 llseek 32 : ▆▃ : 8388608 | 3 memcntl 32 : █ : 8388608 | 1 resolvepath 32 : █ : 8388608 | 1 semsys 32 : █ : 8388608 | 1 setcontext 32 : █ : 8388608 | 2 setpgrp 32 : █ : 8388608 | 1 fstat64 32 : ▃▃▃ : 8388608 | 6 sigaction 32 : ▃▃▂▃ : 8388608 | 17 accept 32 : █ : 8388608 | 1 access 32 : █ : 8388608 | 1 munmap 32 : █ : 8388608 | 1 setitimer 32 : ▅▃▃ : 8388608 | 4 lwp_sigmask 32 : ▄▃▃▂ : 8388608 | 26 pipe 32 : █ : 8388608 | 1 waitsys 32 : ▅ ▅ : 8388608 | 2 stat 32 : ▃ ▆ : 8388608 | 3 bind 32 : ▅▄ : 8388608 | 5 mmapobj 32 : █ : 8388608 | 1 open64 32 : █ : 8388608 | 1 p_online 32 : █▁ ▁ : 8388608 | 256 getpid 32 : ▁▆▂ : 8388608 | 45 schedctl 32 : █ : 8388608 | 2 getsockopt 32 : ▃▅▂ : 8388608 | 32 setsockopt 32 : ▁▁▃▆ : 8388608 | 31 fcntl 32 : ▂▁▃▃▂ : 8388608 | 114 recvmsg 32 : ▁▄▃▂▁▁ : 8388608 | 17 putmsg 32 : █ : 8388608 | 1 mmap 32 : ▅ ▅ : 8388608 | 2 writev 32 : ▆ ▃ : 8388608 | 3 lseek 32 : ▁▃▃▄▁▁ : 8388608 | 76 brk 32 : ▃▂▃▃▂▁▁ ▁ : 8388608 | 124 gtime 32 : ▇▁▁▁▁▁ ▁ : 8388608 | 1405 sendmsg 32 : ▂▅▄ : 8388608 | 10 recv 32 : ▂▃▃▂▂▁ : 8388608 | 159 yield 32 : █▁▁▁ : 8388608 | 409 open 32 : ▃▂▄▁▂ : 8388608 | 30 close 32 : ▂▃▂▃▂▁ : 8388608 | 60 so_socket 32 : ▁▃▆ : 8388608 | 29 lwp_cond_signal 32 : ▁▃▂▂▁▃▁ : 8388608 | 59 nanosleep 32 : ▂▆▂ : 8388608 | 74 lwp_cond_wait 32 : ▂▃▄▂ : 8388608 | 120 connect 32 : ▂▅▃ : 8388608 | 24 stat64 32 : ▁▂▇▁ : 8388608 | 77 pollsys 32 : ▁▁▁ ▁▅▄ : 8388608 | 148 send 32 : ▁▂▂▂▂▃▂▁ : 8388608 | 137 read 32 : ▁▂▃▂▃▂▁▁▁▁ : 8388608 | 283 ioctl 32 : ▇▁▁▁▁▁▁▁▁▁▁ : 8388608 | 3697 write 32 : ▁▁▁▂▂▃▂▁▁ ▁▁ : 8388608 | 207 forksys 32 : █ : 8388608 | 1 portfs 32 : ▁▂▂▁▁▂▄▁ ▁ : 8388608 | 807 lwp_park 32 : ▁ ▁▁▁▃▃▂▃▁ : 8388608 | 919
Delicious! (Assuming, of course, that these look right for you — which they may or may not, depending on how your monospaced font renders the Unicode Block Elements.) After one look at the Unicode Block Elements, it clearly had to be the default behavior — but if your terminal is rendered in a font that can’t display the UTF-8 encodings of these characters (less common) or if they render in a way that is not monospaced despite being in a putatively monospaced font (more common), I also added a “-x encoding” option that can be set to “ascii” to force the ASCII output.
Returning to our example, if the above is too much dead space for you, you can combine it with aggzoom:
# dtrace -n syscall:::entry'{self->ts = vtimestamp}' \ -n syscall:::return'/self->ts/{@[probefunc] = \ quantize(vtimestamp - self->ts); self->ts = 0}' -x aggpack -x aggzoom dtrace: description 'syscall:::entry' matched 233 probes dtrace: description 'syscall:::return' matched 233 probes ^C key min .----------------. max | count lwp_continue 32 : █ : 1048576 | 1 sigpending 32 : █ : 1048576 | 1 uucopy 32 : █ : 1048576 | 1 lwp_kill 32 : █ : 1048576 | 1 sigaction 32 : ▄▄ █ : 1048576 | 4 pset 32 : █ ▄ : 1048576 | 3 sysconfig 32 : ███ █ : 1048576 | 4 setcontext 32 : ██ : 1048576 | 2 fstat 32 : █ : 1048576 | 1 recvfrom 32 : █ : 1048576 | 1 systeminfo 32 : ▅█ : 1048576 | 5 lwp_sigmask 32 : ██▂▅ : 1048576 | 14 accept 32 : █ █ : 1048576 | 2 recvmsg 32 : █ █ : 1048576 | 2 stat 32 : █ : 1048576 | 2 p_online 32 : █▁ ▁ : 1048576 | 256 schedctl 32 : █ █ : 1048576 | 2 brk 32 : ▄███▄ : 1048576 | 8 getsockopt 32 : ▅██ : 1048576 | 21 getpid 32 : █▇ : 1048576 | 39 setsockopt 32 : ▃█▂ : 1048576 | 16 lwp_create 32 : █ : 1048576 | 1 sendmsg 32 : █ : 1048576 | 1 fcntl 32 : ▃▄▃▆█▁ : 1048576 | 60 writev 32 : █ : 1048576 | 1 lseek 32 : ▆▇█▂ : 1048576 | 65 recv 32 : ▁▆▃█▆▅ : 1048576 | 62 putmsg 32 : █ : 1048576 | 2 open 32 : █ ▅▂▃ : 1048576 | 16 gtime 32 : █▁▁▁▁ ▁ : 1048576 | 1365 close 32 : ▂█▂█▄▃ : 1048576 | 36 lwp_cond_signal 32 : ▂▂█▅▅▂ : 1048576 | 18 so_socket 32 : ▁▂▁█ : 1048576 | 19 mmap 32 : ▆▃ █▄ ▆ : 1048576 | 13 nanosleep 32 : ▃█▃ : 1048576 | 34 read 32 : ▁▂▅▄█▅▂ : 1048576 | 144 lwp_cond_wait 32 : ▃█▂▁ : 1048576 | 74 yield 32 : █▁▁▁ ▁ : 1048576 | 1187 send 32 : ▁▂▅█▅▄▂ : 1048576 | 50 connect 32 : ▂▂ ▅██▃ : 1048576 | 20 stat64 32 : ▁▁▁▃█▁ : 1048576 | 80 pollsys 32 : ▁▁▁█▆ : 1048576 | 136 write 32 : ▂▄ ▃▂▄█▆▃▂ ▂ : 1048576 | 77 ioctl 32 : █▂▁▁▁▁▁▁ ▁ : 1048576 | 4587 mmap64 32 : ▂▄▄▂▅ ▂ █ : 1048576 | 20 portfs 32 : ▁▁▂▂▁▂▂█▂ : 1048576 | 601 lwp_park 32 : ▁▁▂▆▇▃█▁▁ : 1048576 | 787
Here’s another fun one:
# dtrace -n BEGIN'{start = timestamp}' \ -n sched:::on-cpu'{@[execname] = \ lquantize((timestamp - start) / 1000000000, 0, 30, 1)}' \ -x aggpack -n tick-1sec'/++i >= 30/{exit(0)}' -q key min .--------------------------------. max | count sshd < 0 : █ : >= 30 | 1 zpool < 0 : █ : >= 30 | 8 zfs < 0 : █ : >= 30 | 19 zoneadm < 0 : █ : >= 30 | 19 utmpd < 0 : █ : >= 30 | 1 intrd < 0 : █ : >= 30 | 2 epmd < 0 : ▂ ▂ ▂ ▂ ▂ ▂ : >= 30 | 6 mdnsd < 0 : ▂ ▂ ▂ ▂ ▂ ▂ : >= 30 | 6 sendmail < 0 : ▂ ▂ ▂ ▂ ▂ ▂ : >= 30 | 6 ur-agent < 0 : ▂▂ ▂ ▂ ▃▂ : >= 30 | 14 vmadmd < 0 : ▃ ▂ ▂ ▂ ▂ ▃ : >= 30 | 22 devfsadm < 0 : ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ ▁ : >= 30 | 30 bridged < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 30 fsflush < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 32 fmd < 0 : ▁▁ ▁▇ ▁▁ : >= 30 | 41 ipmon < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 51 ipmgmtd < 0 : ▃ ▃ ▃ : >= 30 | 66 lldpd < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 90 dtrace < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 111 haproxy < 0 : ▂▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▁▁ : >= 30 | 113 svc.configd < 0 : ▂▁ ▄▁ ▂▁ : >= 30 | 126 svc.startd < 0 : ▃▁ ▃▁ ▃▁ : >= 30 | 150 beam.smp < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 209 nscd < 0 : ▁ ▃▂ ▃▂ ▃▂ : >= 30 | 192 postgres < 0 : ▁▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 598 redis-server < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 598 zpool-zones < 0 : ▂ ▁ ▂ ▃ ▁ ▂ ▂ ▁ ▃ : >= 30 | 623 java < 0 : ▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 1211 tail < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 1178 node < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▁ : >= 30 | 11195 sched < 0 : ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ : >= 30 | 22101
This (quickly) shows you the second offset for CPU scheduling events from the start of the D script for various applications, and points to some obvious conclusions: there are clearly a bunch of programs running once every ten seconds, a couple more running once every five seconds, and so on.
encoding
Seeing this, why should aggpack hog all that Unicode hawtness? While only aggpack will use the UTF-8 encodings by default, I also extended the encoding option to allow a “utf8” setting that forces the UTF-8 encoding of the Unicode Block Elements to be used for all aggregation display. For example, to return to our earlier agghist example:
# dtrace -n syscall:::entry'{@[execname] = count()}' \ -x agghist -x aggzoom -x encoding=utf8 dtrace: description 'syscall:::entry' matched 233 probes dtrace: description 'syscall:::entry' matched 233 probes ^C key ------------- Distribution ------------- count utmpd | 2 sshd | 10 pickup | 14 fmd | 20 ur-agent | 21 epmd | 22 vmadmd | 31 jsontool.js | 33 mdnsd | 33 master | 41 intrd | 45 devfsadm | 54 ipmgmtd | 55 bridged | 81 mkdir | 83 cat | 96 sendmail | 101 ipmon | 108 dirname | 115 svc.startd | 129 ksh93 | 164 ls | 182 svcprop |▏ 258 bash |▏ 290 zpool |▎ 436 date |▎ 448 cron |▎ 470 lldpd |▍ 595 dump-minutely-sd |▍ 609 haproxy |▍ 654 svc.configd |▌ 907 zoneadmd |█ 1554 zfs |█▎ 1979 zoneadm |█▋ 2414 nscd |█▋ 2522 java |██▏ 3205 tail |██▌ 3775 postgres |████▏ 6083 redis-server |██████████████▋ 21710 dtrace |███████████████████▎ 28509 node |█████████████████████████▍ 37427 beam.smp |██████████████████████████████████████ 55948
To paraphrase Kent Brockman, I for one welcome our new Unicode Block Element overlords — and I look forward to toiling in their underground sugar caves!
Availability
All of these new options have been integrated into SmartOS (and we aim to get them up into illumos). If you’re a Joyent public cloud customer, you already all have these improvements or they will be coming to you when your platform is next upgraded (depending on the compute node that you have landed on). And if you’re a DTrace user and like Brendan and have some new way that you’d like to see data visualized (or have any other DTrace feature request), don’t hesitate to speak up — DTrace only improves when those of us who depend on it every day envision a way that it could be even better!