1N/A#!/usr/sbin/dtrace -s
1N/A/*
1N/A * weblatency.d - website latency statistics.
1N/A * Written using DTrace (Solaris 10 3/05).
1N/A *
1N/A * $Id: weblatency.d 3 2007-08-01 10:50:08Z brendan $
1N/A *
1N/A * USAGE: weblatency.d # hit Ctrl-C to end sample
1N/A *
1N/A * See the code below for the "BROWSER" variable, which sets the browser
1N/A * to trace (currently set to "mozilla-bin").
1N/A *
1N/A * This is written as an experimental tool, and may not work at all with
1N/A * your browser.
1N/A *
1N/A * FIELDS:
1N/A * HOST Hostname from URL
1N/A * NUM Number of GETs
1N/A * AVGTIME(ms) Average time for response, ms
1N/A * MAXTIME(ms) Maximum time for response, ms
1N/A *
1N/A * NOTE:
1N/A *
1N/A * The latency measured here is from the browser sending the GET
1N/A * request to when the browser begins to recieve the response. It
1N/A * is an overall response time for the client, and encompasses
1N/A * connection speed delays, DNS lookups, proxy delays, and web server
1N/A * response time.
1N/A *
1N/A * IDEA: Bryan Cantrill (who wrote an elegant version for Sol 10 update 1)
1N/A *
1N/A * COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg.
1N/A *
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License, Version 1.0 only
1N/A * (the "License"). You may not use this file except in compliance
1N/A * with the License.
1N/A *
1N/A * You can obtain a copy of the license at Docs/cddl1.txt
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * CDDL HEADER END
1N/A *
1N/A * ToDo:
1N/A * Check write fd for socket, not file.
1N/A *
1N/A * 30-Nov-2005 Brendan Gregg Created this.
1N/A * 20-Apr-2006 " " Last update.
1N/A */
1N/A
1N/A#pragma D option quiet
1N/A
1N/A/* browser's execname */
1N/Ainline string BROWSER = "mozilla-bin";
1N/A
1N/A/* maximum expected hostname length + "GET http://" */
1N/Ainline int MAX_REQ = 64;
1N/A
1N/Adtrace:::BEGIN
1N/A{
1N/A printf("Tracing... Hit Ctrl-C to end.\n");
1N/A}
1N/A
1N/A/*
1N/A * Trace brower request
1N/A *
1N/A * This is achieved by matching writes for the browser's execname that
1N/A * start with "GET", and then timing from the return of the write to
1N/A * the return of the next read in the same thread. Various stateful flags
1N/A * are used: self->fd, self->read.
1N/A *
1N/A * For performance reasons, I'd like to only process writes that follow a
1N/A * connect(), however this approach fails to process keepalives.
1N/A */
1N/Asyscall::write:entry
1N/A/execname == BROWSER/
1N/A{
1N/A self->buf = arg1;
1N/A self->fd = arg0 + 1;
1N/A self->nam = "";
1N/A}
1N/A
1N/Asyscall::write:return
1N/A/self->fd/
1N/A{
1N/A this->str = (char *)copyin(self->buf, MAX_REQ);
1N/A this->str[4] = '\0';
1N/A self->fd = stringof(this->str) == "GET " ? self->fd : 0;
1N/A}
1N/A
1N/Asyscall::write:return
1N/A/self->fd/
1N/A{
1N/A /* fetch browser request */
1N/A this->str = (char *)copyin(self->buf, MAX_REQ);
1N/A this->str[MAX_REQ] = '\0';
1N/A
1N/A /*
1N/A * This unrolled loop strips down a URL to it's hostname.
1N/A * We ought to use strtok(), but it's not available on Sol 10 3/05,
1N/A * so instead I used dirname(). It's not pretty - it's done so that
1N/A * this works on all Sol 10 versions.
1N/A */
1N/A self->req = stringof(this->str);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->req = dirname(self->req);
1N/A self->nam = strlen(self->req) > 15 ? self->req : self->nam;
1N/A self->nam = basename(self->nam);
1N/A
1N/A /* start the timer */
1N/A start[pid, self->fd - 1] = timestamp;
1N/A host[pid, self->fd - 1] = self->nam;
1N/A self->buf = 0;
1N/A self->fd = 0;
1N/A self->req = 0;
1N/A self->nam = 0;
1N/A}
1N/A
1N/A/* this one wasn't a GET */
1N/Asyscall::write:return
1N/A/self->buf/
1N/A{
1N/A self->buf = 0;
1N/A self->fd = 0;
1N/A}
1N/A
1N/Asyscall::read:entry
1N/A/execname == BROWSER && start[pid, arg0]/
1N/A{
1N/A self->fd = arg0 + 1;
1N/A}
1N/A
1N/A/*
1N/A * Record host details
1N/A */
1N/Asyscall::read:return
1N/A/self->fd/
1N/A{
1N/A /* fetch details */
1N/A self->host = stringof(host[pid, self->fd - 1]);
1N/A this->start = start[pid, self->fd - 1];
1N/A
1N/A /* save details */
1N/A @Avg[self->host] = avg((timestamp - this->start)/1000000);
1N/A @Max[self->host] = max((timestamp - this->start)/1000000);
1N/A @Num[self->host] = count();
1N/A
1N/A /* clear vars */
1N/A start[pid, self->fd - 1] = 0;
1N/A host[pid, self->fd - 1] = 0;
1N/A self->host = 0;
1N/A self->fd = 0;
1N/A}
1N/A
1N/A/*
1N/A * Output report
1N/A */
1N/Adtrace:::END
1N/A{
1N/A printf("%-32s %11s\n", "HOST", "NUM");
1N/A printa("%-32s %@11d\n", @Num);
1N/A
1N/A printf("\n%-32s %11s\n", "HOST", "AVGTIME(ms)");
1N/A printa("%-32s %@11d\n", @Avg);
1N/A
1N/A printf("\n%-32s %11s\n", "HOST", "MAXTIME(ms)");
1N/A printa("%-32s %@11d\n", @Max);
1N/A}