1 1.1 christos #!/usr/sbin/dtrace -s 2 1.1 christos /* 3 1.1 christos * weblatency.d - website latency statistics. 4 1.1 christos * Written using DTrace (Solaris 10 3/05). 5 1.1 christos * 6 1.1 christos * $Id: weblatency.d,v 1.1.1.1 2015/09/30 22:01:06 christos Exp $ 7 1.1 christos * 8 1.1 christos * USAGE: weblatency.d # hit Ctrl-C to end sample 9 1.1 christos * 10 1.1 christos * See the code below for the "BROWSER" variable, which sets the browser 11 1.1 christos * to trace (currently set to "mozilla-bin"). 12 1.1 christos * 13 1.1 christos * This is written as an experimental tool, and may not work at all with 14 1.1 christos * your browser. 15 1.1 christos * 16 1.1 christos * FIELDS: 17 1.1 christos * HOST Hostname from URL 18 1.1 christos * NUM Number of GETs 19 1.1 christos * AVGTIME(ms) Average time for response, ms 20 1.1 christos * MAXTIME(ms) Maximum time for response, ms 21 1.1 christos * 22 1.1 christos * NOTE: 23 1.1 christos * 24 1.1 christos * The latency measured here is from the browser sending the GET 25 1.1 christos * request to when the browser begins to recieve the response. It 26 1.1 christos * is an overall response time for the client, and encompasses 27 1.1 christos * connection speed delays, DNS lookups, proxy delays, and web server 28 1.1 christos * response time. 29 1.1 christos * 30 1.1 christos * IDEA: Bryan Cantrill (who wrote an elegant version for Sol 10 update 1) 31 1.1 christos * 32 1.1 christos * COPYRIGHT: Copyright (c) 2005, 2006 Brendan Gregg. 33 1.1 christos * 34 1.1 christos * CDDL HEADER START 35 1.1 christos * 36 1.1 christos * The contents of this file are subject to the terms of the 37 1.1 christos * Common Development and Distribution License, Version 1.0 only 38 1.1 christos * (the "License"). You may not use this file except in compliance 39 1.1 christos * with the License. 40 1.1 christos * 41 1.1 christos * You can obtain a copy of the license at Docs/cddl1.txt 42 1.1 christos * or http://www.opensolaris.org/os/licensing. 43 1.1 christos * See the License for the specific language governing permissions 44 1.1 christos * and limitations under the License. 45 1.1 christos * 46 1.1 christos * CDDL HEADER END 47 1.1 christos * 48 1.1 christos * ToDo: 49 1.1 christos * Check write fd for socket, not file. 50 1.1 christos * 51 1.1 christos * 30-Nov-2005 Brendan Gregg Created this. 52 1.1 christos * 20-Apr-2006 " " Last update. 53 1.1 christos */ 54 1.1 christos 55 1.1 christos #pragma D option quiet 56 1.1 christos 57 1.1 christos /* browser's execname */ 58 1.1 christos inline string BROWSER = "mozilla-bin"; 59 1.1 christos 60 1.1 christos /* maximum expected hostname length + "GET http://" */ 61 1.1 christos inline int MAX_REQ = 64; 62 1.1 christos 63 1.1 christos dtrace:::BEGIN 64 1.1 christos { 65 1.1 christos printf("Tracing... Hit Ctrl-C to end.\n"); 66 1.1 christos } 67 1.1 christos 68 1.1 christos /* 69 1.1 christos * Trace brower request 70 1.1 christos * 71 1.1 christos * This is achieved by matching writes for the browser's execname that 72 1.1 christos * start with "GET", and then timing from the return of the write to 73 1.1 christos * the return of the next read in the same thread. Various stateful flags 74 1.1 christos * are used: self->fd, self->read. 75 1.1 christos * 76 1.1 christos * For performance reasons, I'd like to only process writes that follow a 77 1.1 christos * connect(), however this approach fails to process keepalives. 78 1.1 christos */ 79 1.1 christos syscall::write:entry 80 1.1 christos /execname == BROWSER/ 81 1.1 christos { 82 1.1 christos self->buf = arg1; 83 1.1 christos self->fd = arg0 + 1; 84 1.1 christos self->nam = ""; 85 1.1 christos } 86 1.1 christos 87 1.1 christos syscall::write:return 88 1.1 christos /self->fd/ 89 1.1 christos { 90 1.1 christos this->str = (char *)copyin(self->buf, MAX_REQ); 91 1.1 christos this->str[4] = '\0'; 92 1.1 christos self->fd = stringof(this->str) == "GET " ? self->fd : 0; 93 1.1 christos } 94 1.1 christos 95 1.1 christos syscall::write:return 96 1.1 christos /self->fd/ 97 1.1 christos { 98 1.1 christos /* fetch browser request */ 99 1.1 christos this->str = (char *)copyin(self->buf, MAX_REQ); 100 1.1 christos this->str[MAX_REQ] = '\0'; 101 1.1 christos 102 1.1 christos /* 103 1.1 christos * This unrolled loop strips down a URL to it's hostname. 104 1.1 christos * We ought to use strtok(), but it's not available on Sol 10 3/05, 105 1.1 christos * so instead I used dirname(). It's not pretty - it's done so that 106 1.1 christos * this works on all Sol 10 versions. 107 1.1 christos */ 108 1.1 christos self->req = stringof(this->str); 109 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 110 1.1 christos self->req = dirname(self->req); 111 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 112 1.1 christos self->req = dirname(self->req); 113 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 114 1.1 christos self->req = dirname(self->req); 115 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 116 1.1 christos self->req = dirname(self->req); 117 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 118 1.1 christos self->req = dirname(self->req); 119 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 120 1.1 christos self->req = dirname(self->req); 121 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 122 1.1 christos self->req = dirname(self->req); 123 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 124 1.1 christos self->req = dirname(self->req); 125 1.1 christos self->nam = strlen(self->req) > 15 ? self->req : self->nam; 126 1.1 christos self->nam = basename(self->nam); 127 1.1 christos 128 1.1 christos /* start the timer */ 129 1.1 christos start[pid, self->fd - 1] = timestamp; 130 1.1 christos host[pid, self->fd - 1] = self->nam; 131 1.1 christos self->buf = 0; 132 1.1 christos self->fd = 0; 133 1.1 christos self->req = 0; 134 1.1 christos self->nam = 0; 135 1.1 christos } 136 1.1 christos 137 1.1 christos /* this one wasn't a GET */ 138 1.1 christos syscall::write:return 139 1.1 christos /self->buf/ 140 1.1 christos { 141 1.1 christos self->buf = 0; 142 1.1 christos self->fd = 0; 143 1.1 christos } 144 1.1 christos 145 1.1 christos syscall::read:entry 146 1.1 christos /execname == BROWSER && start[pid, arg0]/ 147 1.1 christos { 148 1.1 christos self->fd = arg0 + 1; 149 1.1 christos } 150 1.1 christos 151 1.1 christos /* 152 1.1 christos * Record host details 153 1.1 christos */ 154 1.1 christos syscall::read:return 155 1.1 christos /self->fd/ 156 1.1 christos { 157 1.1 christos /* fetch details */ 158 1.1 christos self->host = stringof(host[pid, self->fd - 1]); 159 1.1 christos this->start = start[pid, self->fd - 1]; 160 1.1 christos 161 1.1 christos /* save details */ 162 1.1 christos @Avg[self->host] = avg((timestamp - this->start)/1000000); 163 1.1 christos @Max[self->host] = max((timestamp - this->start)/1000000); 164 1.1 christos @Num[self->host] = count(); 165 1.1 christos 166 1.1 christos /* clear vars */ 167 1.1 christos start[pid, self->fd - 1] = 0; 168 1.1 christos host[pid, self->fd - 1] = 0; 169 1.1 christos self->host = 0; 170 1.1 christos self->fd = 0; 171 1.1 christos } 172 1.1 christos 173 1.1 christos /* 174 1.1 christos * Output report 175 1.1 christos */ 176 1.1 christos dtrace:::END 177 1.1 christos { 178 1.1 christos printf("%-32s %11s\n", "HOST", "NUM"); 179 1.1 christos printa("%-32s %@11d\n", @Num); 180 1.1 christos 181 1.1 christos printf("\n%-32s %11s\n", "HOST", "AVGTIME(ms)"); 182 1.1 christos printa("%-32s %@11d\n", @Avg); 183 1.1 christos 184 1.1 christos printf("\n%-32s %11s\n", "HOST", "MAXTIME(ms)"); 185 1.1 christos printa("%-32s %@11d\n", @Max); 186 1.1 christos } 187