1 #!/usr/sbin/dtrace -s
2 /*
3  * wpm.d - Measure words per minute of typing.
4  *         Written in DTrace (Solaris 10 3/05).
5  *
6  * $Id: wpm.d,v 1.1.1.1 2015/09/30 22:01:07 christos Exp $
7  *
8  * USAGE:       wpm.d commandname
9  *   eg,
10  *		wpm.d bash
11  *		wpm.d vim
12  *
13  * This script assumes that keystrokes arrive one at a time on STDIN. This
14  * isn't the case for all processes that read keyboard input (eg, sh).
15  *
16  * COPYRIGHT: Copyright (c) 2007 Brendan Gregg.
17  *
18  * CDDL HEADER START
19  *
20  *  The contents of this file are subject to the terms of the
21  *  Common Development and Distribution License, Version 1.0 only
22  *  (the "License").  You may not use this file except in compliance
23  *  with the License.
24  *
25  *  You can obtain a copy of the license at Docs/cddl1.txt
26  *  or http://www.opensolaris.org/os/licensing.
27  *  See the License for the specific language governing permissions
28  *  and limitations under the License.
29  *
30  * CDDL HEADER END
31  *
32  * 05-Aug-2007	Brendan Gregg	Created this.
33  */
34 
35 #pragma D option quiet
36 #pragma D option switchrate=10
37 #pragma D option defaultargs
38 
39 inline int STDIN = 0;
40 
41 enum tracing_state {
42 	BEGIN,
43 	TRACING
44 };
45 
46 dtrace:::BEGIN
47 /$$1 == ""/
48 {
49 	trace("USAGE: wpm.d commandname\n");
50 	trace("  eg,\n");
51 	trace("       wpm.d bash\n");
52 	trace("       wpm.d vim\n");
53 	exit(1);
54 }
55 
56 dtrace:::BEGIN
57 {
58 	state = BEGIN;
59 	keys = 0;
60 	words = 0;
61 	wordsize = 0;
62 	countdown = 5;
63 	last = 0;
64 	printf("Measuring will start in : %2d seconds", countdown);
65 }
66 
67 profile:::tick-1sec
68 /--countdown >= 0/
69 {
70 	printf("\b\b\b\b\b\b\b\b\b\b%2d seconds", countdown);
71 }
72 
73 profile:::tick-1sec
74 /state == BEGIN && countdown == -1/
75 {
76 	state = TRACING;
77 	countdown = 60;
78 	printf("\nMeasuring will stop in  : %2d seconds", countdown);
79 }
80 
81 syscall::read:entry
82 /state == TRACING && execname == $$1 && arg0 == STDIN/
83 {
84 	self->buf = arg1;
85 }
86 
87 syscall::read:return
88 /self->buf && last/
89 {
90 	this->elapsed = (timestamp - last) / 1000000;
91 	@dist = quantize(this->elapsed);
92 	@avg = avg(this->elapsed);
93 	@min = min(this->elapsed);
94 	@max = max(this->elapsed);
95 }
96 
97 syscall::read:return
98 /self->buf/
99 {
100 	keys++;
101 	wordsize++;
102 	this->key = stringof(copyin(self->buf, arg0));
103 	last = timestamp;
104 }
105 
106 syscall::read:return
107 /self->buf && (this->key == " " || this->key == "\n" || this->key == "\r") &&
108     wordsize == 1/
109 {
110 	/* recurring space */
111 	wordsize = 0;
112 	self->buf = 0;
113 }
114 
115 syscall::read:return
116 /self->buf && (this->key == " " || this->key == "\n" || this->key == "\r")/
117 {
118 	words++;
119 	@sizes = lquantize(wordsize - 1, 0, 32, 1);
120 	wordsize = 0;
121 }
122 
123 syscall::read:return
124 /self->buf/
125 {
126 	self->buf = 0;
127 }
128 
129 profile:::tick-1sec
130 /state == TRACING && countdown == -1/
131 {
132 	printf("\n\nCharacters typed : %d\n", keys);
133 	printf("Words per minute : %d\n\n", words);
134 
135 	printa("Minimum keystroke latency : %@d ms\n", @min);
136 	printa("Average keystroke latency : %@d ms\n", @avg);
137 	printa("Maximum keystroke latency : %@d ms\n\n", @max);
138 
139 	printa("Word size distribution (letters),\n%@d\n", @sizes);
140 	printa("Keystroke latency distribution (ms),\n%@d\n", @dist);
141 
142 	exit(0);
143 }
144