187 lines
4.8 KiB
D
Executable File
187 lines
4.8 KiB
D
Executable File
#!/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);
|
|
}
|