1235368Sgnn#!/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 52 2007-09-24 04:28:01Z brendan $
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
39inline int STDIN = 0;
40
41enum tracing_state {
42	BEGIN,
43	TRACING
44};
45
46dtrace:::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
56dtrace:::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
67profile:::tick-1sec
68/--countdown >= 0/
69{
70	printf("\b\b\b\b\b\b\b\b\b\b%2d seconds", countdown);
71}
72
73profile:::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
81syscall::read:entry
82/state == TRACING && execname == $$1 && arg0 == STDIN/
83{
84	self->buf = arg1;
85}
86
87syscall::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
97syscall::read:return
98/self->buf/
99{
100	keys++;
101	wordsize++;
102	this->key = stringof(copyin(self->buf, arg0));
103	last = timestamp;
104}
105
106syscall::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
115syscall::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
123syscall::read:return
124/self->buf/
125{
126	self->buf = 0;
127}
128
129profile:::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