#!/usr/sbin/dtrace -s
/*
* wpm.d - Measure words per minute of typing.
* Written in DTrace (Solaris 10 3/05).
*
* $Id: wpm.d 52 2007-09-24 04:28:01Z brendan $
*
* USAGE: wpm.d commandname
* eg,
* wpm.d bash
* wpm.d vim
*
* This script assumes that keystrokes arrive one at a time on STDIN. This
* isn't the case for all processes that read keyboard input (eg, sh).
*
* COPYRIGHT: Copyright (c) 2007 Brendan Gregg.
*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at Docs/cddl1.txt
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* CDDL HEADER END
*
* 05-Aug-2007 Brendan Gregg Created this.
*/
#pragma D option quiet
#pragma D option switchrate=10
#pragma D option defaultargs
inline int STDIN = 0;
enum tracing_state {
BEGIN,
TRACING
};
dtrace:::BEGIN
/$$1 == ""/
{
trace("USAGE: wpm.d commandname\n");
trace(" eg,\n");
trace(" wpm.d bash\n");
trace(" wpm.d vim\n");
exit(1);
}
dtrace:::BEGIN
{
state = BEGIN;
keys = 0;
words = 0;
wordsize = 0;
countdown = 5;
last = 0;
printf("Measuring will start in : %2d seconds", countdown);
}
profile:::tick-1sec
/--countdown >= 0/
{
printf("\b\b\b\b\b\b\b\b\b\b%2d seconds", countdown);
}
profile:::tick-1sec
/state == BEGIN && countdown == -1/
{
state = TRACING;
countdown = 60;
printf("\nMeasuring will stop in : %2d seconds", countdown);
}
syscall::read:entry
/state == TRACING && execname == $$1 && arg0 == STDIN/
{
self->buf = arg1;
}
syscall::read:return
/self->buf && last/
{
this->elapsed = (timestamp - last) / 1000000;
@dist = quantize(this->elapsed);
@avg = avg(this->elapsed);
@min = min(this->elapsed);
@max = max(this->elapsed);
}
syscall::read:return
/self->buf/
{
keys++;
wordsize++;
this->key = stringof(copyin(self->buf, arg0));
last = timestamp;
}
syscall::read:return
/self->buf && (this->key == " " || this->key == "\n" || this->key == "\r") &&
wordsize == 1/
{
/* recurring space */
wordsize = 0;
self->buf = 0;
}
syscall::read:return
/self->buf && (this->key == " " || this->key == "\n" || this->key == "\r")/
{
words++;
@sizes = lquantize(wordsize - 1, 0, 32, 1);
wordsize = 0;
}
syscall::read:return
/self->buf/
{
self->buf = 0;
}
profile:::tick-1sec
/state == TRACING && countdown == -1/
{
printf("\n\nCharacters typed : %d\n", keys);
printf("Words per minute : %d\n\n", words);
printa("Minimum keystroke latency : %@d ms\n", @min);
printa("Average keystroke latency : %@d ms\n", @avg);
printa("Maximum keystroke latency : %@d ms\n\n", @max);
printa("Word size distribution (letters),\n%@d\n", @sizes);
printa("Keystroke latency distribution (ms),\n%@d\n", @dist);
exit(0);
}