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