aboutsummaryrefslogtreecommitdiff
path: root/Apps/weblatency.d
blob: 8d96d5cdd88bc1900c4b8636dcd262e45f0b0508 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/sbin/dtrace -s
/*
 * weblatency.d - website latency statistics.
 *		  Written using DTrace (Solaris 10 3/05).
 *
 * $Id: weblatency.d 3 2007-08-01 10:50:08Z brendan $
 *
 * USAGE:	weblatency.d 	# hit Ctrl-C to end sample
 *
 * See the code below for the "BROWSER" variable, which sets the browser
 * to trace (currently set to "mozilla-bin").
 *
 * This is written as an experimental tool, and may not work at all with
 * your browser.
 *
 * FIELDS:
 *		HOST		Hostname from URL
 *		NUM		Number of GETs
 *		AVGTIME(ms)	Average time for response, ms
 *		MAXTIME(ms)	Maximum time for response, ms
 *
 * NOTE:
 *
 * The latency measured here is from the browser sending the GET
 * request to when the browser begins to recieve the response. It
 * is an overall response time for the client, and encompasses
 * connection speed delays, DNS lookups, proxy delays, and web server
 * response time.
 *
 * IDEA: Bryan Cantrill (who wrote an elegant version for Sol 10 update 1)
 *
 * COPYRIGHT: Copyright (c) 2005, 2006 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
 *
 * ToDo:
 *	Check write fd for socket, not file.
 *
 * 30-Nov-2005  Brendan Gregg   Created this.
 * 20-Apr-2006	   "	  "	Last update.
 */

#pragma D option quiet

/* browser's execname */
inline string BROWSER = "mozilla-bin";

/* maximum expected hostname length + "GET http://" */
inline int MAX_REQ = 64;

dtrace:::BEGIN
{
	printf("Tracing... Hit Ctrl-C to end.\n");
}

/*
 * Trace brower request
 *
 * This is achieved by matching writes for the browser's execname that
 * start with "GET", and then timing from the return of the write to
 * the return of the next read in the same thread. Various stateful flags
 * are used: self->fd, self->read.
 *
 * For performance reasons, I'd like to only process writes that follow a
 * connect(), however this approach fails to process keepalives.
 */
syscall::write:entry
/execname == BROWSER/
{
	self->buf = arg1;
	self->fd = arg0 + 1;
	self->nam = "";
}

syscall::write:return
/self->fd/
{
	this->str = (char *)copyin(self->buf, MAX_REQ);
	this->str[4] = '\0';
	self->fd = stringof(this->str) == "GET " ? self->fd : 0;
}

syscall::write:return
/self->fd/
{
	/* fetch browser request */
	this->str = (char *)copyin(self->buf, MAX_REQ);
	this->str[MAX_REQ] = '\0';

	/*
	 * This unrolled loop strips down a URL to it's hostname.
	 * We ought to use strtok(), but it's not available on Sol 10 3/05,
	 * so instead I used dirname(). It's not pretty - it's done so that
	 * this works on all Sol 10 versions.
	 */
	self->req = stringof(this->str);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->req = dirname(self->req);
	self->nam = strlen(self->req) > 15 ? self->req : self->nam;
	self->nam = basename(self->nam);

	/* start the timer */
	start[pid, self->fd - 1] = timestamp;
	host[pid, self->fd - 1] = self->nam;
	self->buf = 0;
	self->fd  = 0;
	self->req = 0;
	self->nam = 0;
}

/* this one wasn't a GET */
syscall::write:return
/self->buf/
{
	self->buf = 0;
	self->fd  = 0;
}

syscall::read:entry
/execname == BROWSER && start[pid, arg0]/
{
	self->fd = arg0 + 1;
}

/*
 * Record host details
 */
syscall::read:return
/self->fd/
{
	/* fetch details */
	self->host = stringof(host[pid, self->fd - 1]);
	this->start = start[pid, self->fd - 1];

	/* save details */
	@Avg[self->host] = avg((timestamp - this->start)/1000000);
	@Max[self->host] = max((timestamp - this->start)/1000000);
	@Num[self->host] = count();

	/* clear vars */
	start[pid, self->fd - 1] = 0;
	host[pid, self->fd - 1] = 0;
	self->host = 0;
	self->fd = 0;
}

/*
 * Output report
 */
dtrace:::END
{
	printf("%-32s %11s\n", "HOST", "NUM");
	printa("%-32s %@11d\n", @Num);

	printf("\n%-32s %11s\n", "HOST", "AVGTIME(ms)");
	printa("%-32s %@11d\n", @Avg);

	printf("\n%-32s %11s\n", "HOST", "MAXTIME(ms)");
	printa("%-32s %@11d\n", @Max);
}