G''Day Bryan,
On Tue, 29 Nov 2005, Bryan Cantrill wrote:
> Hey Brendan,
>
> > Quick question - who wrote the first version of this script?
> >
> > # weblatency.d
> > Sampling... Hit Ctrl-C to end.
> > ^C
> > HOST NUM
> > static.flickr.com 1
> > images.pegasosppc.com 1
> > www.planetsolaris.org 5
> > blogs.sun.com 7
[...]> > I''d like to see the original version - I wrote it for Sol 10
3/05, and it
> > was a somewhat difficult to pull out the hostname in D without using
> > strtok(). :-)
>
> I wrote it, and I wrote it as demonstration for strtok(). ;)
LOL! Yes, I remember now.
> Here''s the script as I wrote it:
>
> ---8<--- sitelat.d ---8<---
[...]> ---8<--- sitelat.d ---8<---
Very nice, and very concise! I laughed at seeing fd + 1 as a flag,
something I had used too (maybe I picked that up from the DTrace Guide).
I''ve put my version below, which works without strtok() and strstr(),
although it''s far from pretty.
> No guarantees that this works for all browsers. (Indeed, looking at
> it, I''m not sure why I picked up syscall::write:entry but not
> syscall::send:entry -- probably because I didn''t need to snag send
to
> get it to work on my browser.) Anyway, it''s pretty interesting to
run
> this and head off to osnews or slashdot or whatever...
Oh. I didn''t think of recv/send at all. hmm. I have read/write target
fixation.
> - Bryan
>
> --------------------------------------------------------------------------
> Bryan Cantrill, Solaris Kernel Development. http://blogs.sun.com/bmc
>
> P.S. strtok() and the gang are in Update 1.
>
> P.P.S. I just putback multiple-aggregation printa() today; look for it
> in Solaris Express and OpenSolaris!
Yay! - I wasn''t going to ask, but the output of weblatency.d begs a
multiple-aggregate print. :)
cheers,
Brendan
[Sydney, Australia]
(I''m still testing this script, so I may have missed something,)
::::::::::::::
weblatency.d
::::::::::::::
#!/usr/sbin/dtrace -s
/*
* weblatency.d - website latency statistics.
* Written using DTrace (Solaris 10 3/05).
*
* 30-Sep-2005, ver 0.60 (first release)
*
* 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").
*
* 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 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.
*/
#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("Sampling... 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);
}