1 #include "stdinc.h"
2 #include "dat.h"
3 #include "fns.h"
4 #include "xml.h"
5
6 typedef struct HttpObj HttpObj;
7 extern QLock memdrawlock;
8
9 enum
10 {
11 ObjNameSize = 64,
12 MaxObjs = 64
13 };
14
15 struct HttpObj
16 {
17 char name[ObjNameSize];
18 int (*f)(HConnect*);
19 };
20
21 static HttpObj objs[MaxObjs];
22
23 static char *webroot;
24
25 static void listenproc(void*);
26 static int estats(HConnect *c);
27 static int dindex(HConnect *c);
28 static int xindex(HConnect *c);
29 static int xlog(HConnect *c);
30 static int sindex(HConnect *c);
31 static int hempty(HConnect *c);
32 static int hlcacheempty(HConnect *c);
33 static int hdcacheempty(HConnect *c);
34 static int hicacheempty(HConnect *c);
35 static int hicachekick(HConnect *c);
36 static int hdcachekick(HConnect *c);
37 static int hicacheflush(HConnect *c);
38 static int hdcacheflush(HConnect *c);
39 static int httpdobj(char *name, int (*f)(HConnect*));
40 static int xgraph(HConnect *c);
41 static int xset(HConnect *c);
42 static int fromwebdir(HConnect *c);
43
44 int
httpdinit(char * address,char * dir)45 httpdinit(char *address, char *dir)
46 {
47 fmtinstall('D', hdatefmt);
48 /* fmtinstall('H', httpfmt); */
49 fmtinstall('U', hurlfmt);
50
51 if(address == nil)
52 address = "tcp!*!http";
53 webroot = dir;
54
55 httpdobj("/stats", estats);
56 httpdobj("/index", dindex);
57 httpdobj("/storage", sindex);
58 httpdobj("/xindex", xindex);
59 httpdobj("/flushicache", hicacheflush);
60 httpdobj("/flushdcache", hdcacheflush);
61 httpdobj("/kickicache", hicachekick);
62 httpdobj("/kickdcache", hdcachekick);
63 httpdobj("/graph", xgraph);
64 httpdobj("/set", xset);
65 httpdobj("/log", xlog);
66 httpdobj("/empty", hempty);
67 httpdobj("/emptyicache", hicacheempty);
68 httpdobj("/emptylumpcache", hlcacheempty);
69 httpdobj("/emptydcache", hdcacheempty);
70 httpdobj("/disk", hdisk);
71 httpdobj("/debug", hdebug);
72 httpdobj("/proc/", hproc);
73
74 if(vtproc(listenproc, address) < 0)
75 return -1;
76 return 0;
77 }
78
79 static int
httpdobj(char * name,int (* f)(HConnect *))80 httpdobj(char *name, int (*f)(HConnect*))
81 {
82 int i;
83
84 if(name == nil || strlen(name) >= ObjNameSize)
85 return -1;
86 for(i = 0; i < MaxObjs; i++){
87 if(objs[i].name[0] == '\0'){
88 strcpy(objs[i].name, name);
89 objs[i].f = f;
90 return 0;
91 }
92 if(strcmp(objs[i].name, name) == 0)
93 return -1;
94 }
95 return -1;
96 }
97
98 static HConnect*
mkconnect(void)99 mkconnect(void)
100 {
101 HConnect *c;
102
103 c = mallocz(sizeof(HConnect), 1);
104 if(c == nil)
105 sysfatal("out of memory");
106 c->replog = nil;
107 c->hpos = c->header;
108 c->hstop = c->header;
109 return c;
110 }
111
112 void httpproc(void*);
113
114 static void
listenproc(void * vaddress)115 listenproc(void *vaddress)
116 {
117 HConnect *c;
118 char *address, ndir[NETPATHLEN], dir[NETPATHLEN];
119 int ctl, nctl, data;
120
121 address = vaddress;
122 ctl = announce(address, dir);
123 if(ctl < 0){
124 fprint(2, "venti: httpd can't announce on %s: %r\n", address);
125 return;
126 }
127
128 if(0) print("announce ctl %d dir %s\n", ctl, dir);
129 for(;;){
130 /*
131 * wait for a call (or an error)
132 */
133 nctl = listen(dir, ndir);
134 if(0) print("httpd listen %d %s...\n", nctl, ndir);
135 if(nctl < 0){
136 fprint(2, "venti: httpd can't listen on %s: %r\n", address);
137 return;
138 }
139
140 data = accept(ctl, ndir);
141 if(0) print("httpd accept %d...\n", data);
142 if(data < 0){
143 fprint(2, "venti: httpd accept: %r\n");
144 close(nctl);
145 continue;
146 }
147 if(0) print("httpd close nctl %d\n", nctl);
148 close(nctl);
149 c = mkconnect();
150 hinit(&c->hin, data, Hread);
151 hinit(&c->hout, data, Hwrite);
152 vtproc(httpproc, c);
153 }
154 }
155
156 void
httpproc(void * v)157 httpproc(void *v)
158 {
159 HConnect *c;
160 int ok, i, n;
161
162 c = v;
163
164 for(;;){
165 /*
166 * No timeout because the signal appears to hit every
167 * proc, not just us.
168 */
169 if(hparsereq(c, 0) < 0)
170 break;
171
172 for(i = 0; i < MaxObjs && objs[i].name[0]; i++){
173 n = strlen(objs[i].name);
174 if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0)
175 || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){
176 ok = (*objs[i].f)(c);
177 goto found;
178 }
179 }
180 ok = fromwebdir(c);
181 found:
182 hflush(&c->hout);
183 if(c->head.closeit)
184 ok = -1;
185 hreqcleanup(c);
186
187 if(ok < 0)
188 break;
189 }
190 hreqcleanup(c);
191 close(c->hin.fd);
192 free(c);
193 }
194
195 char*
hargstr(HConnect * c,char * name,char * def)196 hargstr(HConnect *c, char *name, char *def)
197 {
198 HSPairs *p;
199
200 for(p=c->req.searchpairs; p; p=p->next)
201 if(strcmp(p->s, name) == 0)
202 return p->t;
203 return def;
204 }
205
206 vlong
hargint(HConnect * c,char * name,vlong def)207 hargint(HConnect *c, char *name, vlong def)
208 {
209 char *a;
210
211 if((a = hargstr(c, name, nil)) == nil)
212 return def;
213 return atoll(a);
214 }
215
216 static int
percent(ulong v,ulong total)217 percent(ulong v, ulong total)
218 {
219 if(total == 0)
220 total = 1;
221 if(v < 1000*1000)
222 return (v * 100) / total;
223 total /= 100;
224 if(total == 0)
225 total = 1;
226 return v / total;
227 }
228
229 static int
preq(HConnect * c)230 preq(HConnect *c)
231 {
232 if(hparseheaders(c, 0) < 0)
233 return -1;
234 if(strcmp(c->req.meth, "GET") != 0
235 && strcmp(c->req.meth, "HEAD") != 0)
236 return hunallowed(c, "GET, HEAD");
237 if(c->head.expectother || c->head.expectcont)
238 return hfail(c, HExpectFail, nil);
239 return 0;
240 }
241
242 int
hsettype(HConnect * c,char * type)243 hsettype(HConnect *c, char *type)
244 {
245 Hio *hout;
246 int r;
247
248 r = preq(c);
249 if(r < 0)
250 return r;
251
252 hout = &c->hout;
253 if(c->req.vermaj){
254 hokheaders(c);
255 hprint(hout, "Content-type: %s\r\n", type);
256 if(http11(c))
257 hprint(hout, "Transfer-Encoding: chunked\r\n");
258 hprint(hout, "\r\n");
259 }
260
261 if(http11(c))
262 hxferenc(hout, 1);
263 else
264 c->head.closeit = 1;
265 return 0;
266 }
267
268 int
hsethtml(HConnect * c)269 hsethtml(HConnect *c)
270 {
271 return hsettype(c, "text/html; charset=utf-8");
272 }
273
274 int
hsettext(HConnect * c)275 hsettext(HConnect *c)
276 {
277 return hsettype(c, "text/plain; charset=utf-8");
278 }
279
280 static int
herror(HConnect * c)281 herror(HConnect *c)
282 {
283 int n;
284 Hio *hout;
285
286 hout = &c->hout;
287 n = snprint(c->xferbuf, HBufSize, "<html><head><title>Error</title></head>\n<body><h1>Error</h1>\n<pre>%r</pre>\n</body></html>");
288 hprint(hout, "%s %s\r\n", hversion, "400 Bad Request");
289 hprint(hout, "Date: %D\r\n", time(nil));
290 hprint(hout, "Server: Venti\r\n");
291 hprint(hout, "Content-Type: text/html\r\n");
292 hprint(hout, "Content-Length: %d\r\n", n);
293 if(c->head.closeit)
294 hprint(hout, "Connection: close\r\n");
295 else if(!http11(c))
296 hprint(hout, "Connection: Keep-Alive\r\n");
297 hprint(hout, "\r\n");
298
299 if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0)
300 hwrite(hout, c->xferbuf, n);
301
302 return hflush(hout);
303 }
304
305 int
hnotfound(HConnect * c)306 hnotfound(HConnect *c)
307 {
308 int r;
309
310 r = preq(c);
311 if(r < 0)
312 return r;
313 return hfail(c, HNotFound, c->req.uri);
314 }
315
316 struct {
317 char *ext;
318 char *type;
319 } exttab[] = {
320 ".html", "text/html",
321 ".txt", "text/plain",
322 ".xml", "text/xml",
323 ".png", "image/png",
324 ".gif", "image/gif",
325 0
326 };
327
328 static int
fromwebdir(HConnect * c)329 fromwebdir(HConnect *c)
330 {
331 char buf[4096], *p, *ext, *type;
332 int i, fd, n, defaulted;
333 Dir *d;
334
335 if(webroot == nil || strstr(c->req.uri, ".."))
336 return hnotfound(c);
337 snprint(buf, sizeof buf-20, "%s/%s", webroot, c->req.uri+1);
338 defaulted = 0;
339 reopen:
340 if((fd = open(buf, OREAD)) < 0)
341 return hnotfound(c);
342 d = dirfstat(fd);
343 if(d == nil){
344 close(fd);
345 return hnotfound(c);
346 }
347 if(d->mode&DMDIR){
348 if(!defaulted){
349 defaulted = 1;
350 strcat(buf, "/index.html");
351 free(d);
352 close(fd);
353 goto reopen;
354 }
355 free(d);
356 return hnotfound(c);
357 }
358 free(d);
359 p = buf+strlen(buf);
360 type = "application/octet-stream";
361 for(i=0; exttab[i].ext; i++){
362 ext = exttab[i].ext;
363 if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){
364 type = exttab[i].type;
365 break;
366 }
367 }
368 if(hsettype(c, type) < 0){
369 close(fd);
370 return 0;
371 }
372 while((n = read(fd, buf, sizeof buf)) > 0)
373 if(hwrite(&c->hout, buf, n) < 0)
374 break;
375 close(fd);
376 hflush(&c->hout);
377 return 0;
378 }
379
380 static struct
381 {
382 char *name;
383 int *p;
384 } namedints[] =
385 {
386 "compress", &compressblocks,
387 "devnull", &writestodevnull,
388 "logging", &ventilogging,
389 "stats", &collectstats,
390 "icachesleeptime", &icachesleeptime,
391 "minicachesleeptime", &minicachesleeptime,
392 "arenasumsleeptime", &arenasumsleeptime,
393 "l0quantum", &l0quantum,
394 "l1quantum", &l1quantum,
395 "manualscheduling", &manualscheduling,
396 "ignorebloom", &ignorebloom,
397 "syncwrites", &syncwrites,
398 "icacheprefetch", &icacheprefetch,
399 0
400 };
401
402 static int
xset(HConnect * c)403 xset(HConnect *c)
404 {
405 int i, old;
406 char *name, *value;
407
408 if(hsettext(c) < 0)
409 return -1;
410
411 if((name = hargstr(c, "name", nil)) == nil || name[0] == 0){
412 for(i=0; namedints[i].name; i++)
413 hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p);
414 hflush(&c->hout);
415 return 0;
416 }
417
418 for(i=0; namedints[i].name; i++)
419 if(strcmp(name, namedints[i].name) == 0)
420 break;
421 if(!namedints[i].name){
422 hprint(&c->hout, "%s not found\n", name);
423 hflush(&c->hout);
424 return 0;
425 }
426
427 if((value = hargstr(c, "value", nil)) == nil || value[0] == 0){
428 hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p);
429 hflush(&c->hout);
430 return 0;
431 }
432
433 old = *namedints[i].p;
434 *namedints[i].p = atoll(value);
435 hprint(&c->hout, "%s = %d (was %d)\n", name, *namedints[i].p, old);
436 hflush(&c->hout);
437 return 0;
438 }
439
440 static int
estats(HConnect * c)441 estats(HConnect *c)
442 {
443 Hio *hout;
444 int r;
445
446 r = hsettext(c);
447 if(r < 0)
448 return r;
449
450
451 hout = &c->hout;
452 /*
453 hprint(hout, "lump writes=%,ld\n", stats.lumpwrites);
454 hprint(hout, "lump reads=%,ld\n", stats.lumpreads);
455 hprint(hout, "lump cache read hits=%,ld\n", stats.lumphit);
456 hprint(hout, "lump cache read misses=%,ld\n", stats.lumpmiss);
457
458 hprint(hout, "clump disk writes=%,ld\n", stats.clumpwrites);
459 hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpbwrites);
460 hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpbcomp);
461 hprint(hout, "clump disk reads=%,ld\n", stats.clumpreads);
462 hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpbreads);
463 hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpbuncomp);
464
465 hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites);
466 hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads);
467
468 hprint(hout, "index disk writes=%,ld\n", stats.indexwrites);
469 hprint(hout, "index disk reads=%,ld\n", stats.indexreads);
470 hprint(hout, "index disk bloom filter hits=%,ld %d%% falsemisses=%,ld %d%%\n",
471 stats.indexbloomhits,
472 percent(stats.indexbloomhits, stats.indexreads),
473 stats.indexbloomfalsemisses,
474 percent(stats.indexbloomfalsemisses, stats.indexreads));
475 hprint(hout, "bloom filter bits=%,ld of %,ld %d%%\n",
476 stats.bloomones, stats.bloombits, percent(stats.bloomones, stats.bloombits));
477 hprint(hout, "index disk reads for modify=%,ld\n", stats.indexwreads);
478 hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexareads);
479 hprint(hout, "index block splits=%,ld\n", stats.indexsplits);
480
481 hprint(hout, "index cache lookups=%,ld\n", stats.iclookups);
482 hprint(hout, "index cache hits=%,ld %d%%\n", stats.ichits,
483 percent(stats.ichits, stats.iclookups));
484 hprint(hout, "index cache fills=%,ld %d%%\n", stats.icfills,
485 percent(stats.icfills, stats.iclookups));
486 hprint(hout, "index cache inserts=%,ld\n", stats.icinserts);
487
488 hprint(hout, "disk cache hits=%,ld\n", stats.pchit);
489 hprint(hout, "disk cache misses=%,ld\n", stats.pcmiss);
490 hprint(hout, "disk cache reads=%,ld\n", stats.pcreads);
491 hprint(hout, "disk cache bytes read=%,lld\n", stats.pcbreads);
492
493 hprint(hout, "disk cache writes=%,ld\n", stats.dirtydblocks);
494 hprint(hout, "disk cache writes absorbed=%,ld %d%%\n", stats.absorbedwrites,
495 percent(stats.absorbedwrites, stats.dirtydblocks));
496
497 hprint(hout, "disk cache flushes=%,ld\n", stats.dcacheflushes);
498 hprint(hout, "disk cache flush writes=%,ld (%,ld per flush)\n",
499 stats.dcacheflushwrites,
500 stats.dcacheflushwrites/(stats.dcacheflushes ? stats.dcacheflushes : 1));
501
502 hprint(hout, "disk writes=%,ld\n", stats.diskwrites);
503 hprint(hout, "disk bytes written=%,lld\n", stats.diskbwrites);
504 hprint(hout, "disk reads=%,ld\n", stats.diskreads);
505 hprint(hout, "disk bytes read=%,lld\n", stats.diskbreads);
506 */
507
508 hflush(hout);
509 return 0;
510 }
511
512 static int
sindex(HConnect * c)513 sindex(HConnect *c)
514 {
515 Hio *hout;
516 Index *ix;
517 Arena *arena;
518 vlong clumps, cclumps, uncsize, used, size;
519 int i, r, active;
520
521 r = hsettext(c);
522 if(r < 0)
523 return r;
524 hout = &c->hout;
525
526 ix = mainindex;
527
528 hprint(hout, "index=%s\n", ix->name);
529
530 active = 0;
531 clumps = 0;
532 cclumps = 0;
533 uncsize = 0;
534 used = 0;
535 size = 0;
536 for(i = 0; i < ix->narenas; i++){
537 arena = ix->arenas[i];
538 if(arena != nil && arena->memstats.clumps != 0){
539 active++;
540 clumps += arena->memstats.clumps;
541 cclumps += arena->memstats.cclumps;
542 uncsize += arena->memstats.uncsize;
543 used += arena->memstats.used;
544 }
545 size += arena->size;
546 }
547 hprint(hout, "total arenas=%,d active=%,d\n", ix->narenas, active);
548 hprint(hout, "total space=%,lld used=%,lld\n", size, used + clumps * ClumpInfoSize);
549 hprint(hout, "clumps=%,lld compressed clumps=%,lld data=%,lld compressed data=%,lld\n",
550 clumps, cclumps, uncsize, used - clumps * ClumpSize);
551 hflush(hout);
552 return 0;
553 }
554
555 static void
darena(Hio * hout,Arena * arena)556 darena(Hio *hout, Arena *arena)
557 {
558 hprint(hout, "arena='%s' on %s at [%lld,%lld)\n\tversion=%d created=%d modified=%d",
559 arena->name, arena->part->name, arena->base, arena->base + arena->size + 2 * arena->blocksize,
560 arena->version, arena->ctime, arena->wtime);
561 if(arena->memstats.sealed)
562 hprint(hout, " mem=sealed");
563 if(arena->diskstats.sealed)
564 hprint(hout, " disk=sealed");
565 hprint(hout, "\n");
566 if(scorecmp(zeroscore, arena->score) != 0)
567 hprint(hout, "\tscore=%V\n", arena->score);
568
569 hprint(hout, "\twritten: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n",
570 arena->memstats.clumps, arena->memstats.cclumps, arena->memstats.uncsize,
571 arena->memstats.used - arena->memstats.clumps * ClumpSize,
572 arena->memstats.used + arena->memstats.clumps * ClumpInfoSize);
573 hprint(hout, "\tindexed: clumps=%d compressed clumps=%d data=%,lld compressed data=%,lld storage=%,lld\n",
574 arena->diskstats.clumps, arena->diskstats.cclumps, arena->diskstats.uncsize,
575 arena->diskstats.used - arena->diskstats.clumps * ClumpSize,
576 arena->diskstats.used + arena->diskstats.clumps * ClumpInfoSize);
577 }
578
579 static int
hempty(HConnect * c)580 hempty(HConnect *c)
581 {
582 Hio *hout;
583 int r;
584
585 r = hsettext(c);
586 if(r < 0)
587 return r;
588 hout = &c->hout;
589
590 emptylumpcache();
591 emptydcache();
592 emptyicache();
593 hprint(hout, "emptied all caches\n");
594 hflush(hout);
595 return 0;
596 }
597
598 static int
hlcacheempty(HConnect * c)599 hlcacheempty(HConnect *c)
600 {
601 Hio *hout;
602 int r;
603
604 r = hsettext(c);
605 if(r < 0)
606 return r;
607 hout = &c->hout;
608
609 emptylumpcache();
610 hprint(hout, "emptied lumpcache\n");
611 hflush(hout);
612 return 0;
613 }
614
615 static int
hicacheempty(HConnect * c)616 hicacheempty(HConnect *c)
617 {
618 Hio *hout;
619 int r;
620
621 r = hsettext(c);
622 if(r < 0)
623 return r;
624 hout = &c->hout;
625
626 emptyicache();
627 hprint(hout, "emptied icache\n");
628 hflush(hout);
629 return 0;
630 }
631
632 static int
hdcacheempty(HConnect * c)633 hdcacheempty(HConnect *c)
634 {
635 Hio *hout;
636 int r;
637
638 r = hsettext(c);
639 if(r < 0)
640 return r;
641 hout = &c->hout;
642
643 emptydcache();
644 hprint(hout, "emptied dcache\n");
645 hflush(hout);
646 return 0;
647 }
648 static int
hicachekick(HConnect * c)649 hicachekick(HConnect *c)
650 {
651 Hio *hout;
652 int r;
653
654 r = hsettext(c);
655 if(r < 0)
656 return r;
657 hout = &c->hout;
658
659 kickicache();
660 hprint(hout, "kicked icache\n");
661 hflush(hout);
662 return 0;
663 }
664
665 static int
hdcachekick(HConnect * c)666 hdcachekick(HConnect *c)
667 {
668 Hio *hout;
669 int r;
670
671 r = hsettext(c);
672 if(r < 0)
673 return r;
674 hout = &c->hout;
675
676 kickdcache();
677 hprint(hout, "kicked dcache\n");
678 hflush(hout);
679 return 0;
680 }
681 static int
hicacheflush(HConnect * c)682 hicacheflush(HConnect *c)
683 {
684 Hio *hout;
685 int r;
686
687 r = hsettext(c);
688 if(r < 0)
689 return r;
690 hout = &c->hout;
691
692 flushicache();
693 hprint(hout, "flushed icache\n");
694 hflush(hout);
695 return 0;
696 }
697
698 static int
hdcacheflush(HConnect * c)699 hdcacheflush(HConnect *c)
700 {
701 Hio *hout;
702 int r;
703
704 r = hsettext(c);
705 if(r < 0)
706 return r;
707 hout = &c->hout;
708
709 flushdcache();
710 hprint(hout, "flushed dcache\n");
711 hflush(hout);
712 return 0;
713 }
714
715 static int
dindex(HConnect * c)716 dindex(HConnect *c)
717 {
718 Hio *hout;
719 Index *ix;
720 int i, r;
721
722 r = hsettext(c);
723 if(r < 0)
724 return r;
725 hout = &c->hout;
726
727
728 ix = mainindex;
729 hprint(hout, "index=%s version=%d blocksize=%d tabsize=%d\n",
730 ix->name, ix->version, ix->blocksize, ix->tabsize);
731 hprint(hout, "\tbuckets=%d div=%d\n", ix->buckets, ix->div);
732 for(i = 0; i < ix->nsects; i++)
733 hprint(hout, "\tsect=%s for buckets [%lld,%lld) buckmax=%d\n", ix->smap[i].name, ix->smap[i].start, ix->smap[i].stop, ix->sects[i]->buckmax);
734 for(i = 0; i < ix->narenas; i++){
735 if(ix->arenas[i] != nil && ix->arenas[i]->memstats.clumps != 0){
736 hprint(hout, "arena=%s at index [%lld,%lld)\n\t", ix->amap[i].name, ix->amap[i].start, ix->amap[i].stop);
737 darena(hout, ix->arenas[i]);
738 }
739 }
740 hflush(hout);
741 return 0;
742 }
743
744 typedef struct Arg Arg;
745 struct Arg
746 {
747 int index;
748 int index2;
749 };
750
751 static long
rawgraph(Stats * s,Stats * t,void * va)752 rawgraph(Stats *s, Stats *t, void *va)
753 {
754 Arg *a;
755
756 USED(s);
757 a = va;
758 return t->n[a->index];
759 }
760
761 static long
diffgraph(Stats * s,Stats * t,void * va)762 diffgraph(Stats *s, Stats *t, void *va)
763 {
764 Arg *a;
765
766 a = va;
767 return t->n[a->index] - s->n[a->index];
768 }
769
770 static long
pctgraph(Stats * s,Stats * t,void * va)771 pctgraph(Stats *s, Stats *t, void *va)
772 {
773 Arg *a;
774
775 USED(s);
776 a = va;
777 return percent(t->n[a->index], t->n[a->index2]);
778 }
779
780 static long
pctdiffgraph(Stats * s,Stats * t,void * va)781 pctdiffgraph(Stats *s, Stats *t, void *va)
782 {
783 Arg *a;
784
785 a = va;
786 return percent(t->n[a->index]-s->n[a->index], t->n[a->index2]-s->n[a->index2]);
787 }
788
789 static long
xdiv(long a,long b)790 xdiv(long a, long b)
791 {
792 if(b == 0)
793 b++;
794 return a/b;
795 }
796
797 static long
divdiffgraph(Stats * s,Stats * t,void * va)798 divdiffgraph(Stats *s, Stats *t, void *va)
799 {
800 Arg *a;
801
802 a = va;
803 return xdiv(t->n[a->index] - s->n[a->index], t->n[a->index2] - s->n[a->index2]);
804 }
805
806 static long
netbw(Stats * s)807 netbw(Stats *s)
808 {
809 ulong *n;
810
811 n = s->n;
812 return n[StatRpcReadBytes]+n[StatRpcWriteBytes]; /* not exactly right */
813 }
814
815 static long
diskbw(Stats * s)816 diskbw(Stats *s)
817 {
818 ulong *n;
819
820 n = s->n;
821 return n[StatApartReadBytes]+n[StatApartWriteBytes]
822 + n[StatIsectReadBytes]+n[StatIsectWriteBytes]
823 + n[StatSumReadBytes];
824 }
825
826 static long
iobw(Stats * s)827 iobw(Stats *s)
828 {
829 return netbw(s)+diskbw(s);
830 }
831
832 static long
diskgraph(Stats * s,Stats * t,void * va)833 diskgraph(Stats *s, Stats *t, void *va)
834 {
835 USED(va);
836 return diskbw(t)-diskbw(s);
837 }
838
839 static long
netgraph(Stats * s,Stats * t,void * va)840 netgraph(Stats *s, Stats *t, void *va)
841 {
842 USED(va);
843 return netbw(t)-netbw(s);
844 }
845
846 static long
iograph(Stats * s,Stats * t,void * va)847 iograph(Stats *s, Stats *t, void *va)
848 {
849 USED(va);
850 return iobw(t)-iobw(s);
851 }
852
853
854 static char* graphname[] =
855 {
856 "rpctotal",
857 "rpcread",
858 "rpcreadok",
859 "rpcreadfail",
860 "rpcreadbyte",
861 "rpcreadtime",
862 "rpcreadcached",
863 "rpcreadcachedtime",
864 "rpcreaduncached",
865 "rpcreaduncachedtime",
866 "rpcwrite",
867 "rpcwritenew",
868 "rpcwriteold",
869 "rpcwritefail",
870 "rpcwritebyte",
871 "rpcwritetime",
872 "rpcwritenewtime",
873 "rpcwriteoldtime",
874
875 "lcachehit",
876 "lcachemiss",
877 "lcachelookup",
878 "lcachewrite",
879 "lcachesize",
880 "lcachestall",
881 "lcachelookuptime",
882
883 "dcachehit",
884 "dcachemiss",
885 "dcachelookup",
886 "dcacheread",
887 "dcachewrite",
888 "dcachedirty",
889 "dcachesize",
890 "dcacheflush",
891 "dcachestall",
892 "dcachelookuptime",
893
894 "dblockstall",
895 "lumpstall",
896
897 "icachehit",
898 "icachemiss",
899 "icacheread",
900 "icachewrite",
901 "icachefill",
902 "icacheprefetch",
903 "icachedirty",
904 "icachesize",
905 "icacheflush",
906 "icachestall",
907 "icachelookuptime",
908 "icachelookup",
909 "scachehit",
910 "scacheprefetch",
911
912 "bloomhit",
913 "bloommiss",
914 "bloomfalsemiss",
915 "bloomlookup",
916 "bloomones",
917 "bloombits",
918
919 "apartread",
920 "apartreadbyte",
921 "apartwrite",
922 "apartwritebyte",
923
924 "isectread",
925 "isectreadbyte",
926 "isectwrite",
927 "isectwritebyte",
928
929 "sumread",
930 "sumreadbyte",
931
932 "cigload",
933 "cigloadtime",
934 };
935
936 static int
findname(char * s)937 findname(char *s)
938 {
939 int i;
940
941 for(i=0; i<nelem(graphname); i++)
942 if(strcmp(graphname[i], s) == 0)
943 return i;
944 return -1;
945 }
946
947 static void
dotextbin(Hio * io,Graph * g)948 dotextbin(Hio *io, Graph *g)
949 {
950 int i, nbin;
951 Statbin *b, bin[2000]; /* 32 kB, but whack is worse */
952
953 needstack(8192); /* double check that bin didn't kill us */
954 nbin = 100;
955 binstats(g->fn, g->arg, g->t0, g->t1, bin, nbin);
956
957 hprint(io, "stats\n\n");
958 for(i=0; i<nbin; i++){
959 b = &bin[i];
960 hprint(io, "%d: nsamp=%d min=%d max=%d avg=%d\n",
961 i, b->nsamp, b->min, b->max, b->avg);
962 }
963 }
964
965 static int
xgraph(HConnect * c)966 xgraph(HConnect *c)
967 {
968 char *name;
969 Hio *hout;
970 Memimage *m;
971 int dotext;
972 Graph g;
973 Arg arg;
974 char *graph, *a;
975
976 name = hargstr(c, "arg", "");
977 if((arg.index = findname(name)) == -1 && strcmp(name, "*") != 0){
978 werrstr("unknown name %s", name);
979 goto error;
980 }
981 a = hargstr(c, "arg2", "");
982 if(a[0] && (arg.index2 = findname(a)) == -1){
983 werrstr("unknown name %s", a);
984 goto error;
985 }
986
987 g.arg = &arg;
988 g.t0 = hargint(c, "t0", -120);
989 g.t1 = hargint(c, "t1", 0);
990 g.min = hargint(c, "min", -1);
991 g.max = hargint(c, "max", -1);
992 g.wid = hargint(c, "wid", -1);
993 g.ht = hargint(c, "ht", -1);
994 dotext = hargstr(c, "text", "")[0] != 0;
995 g.fill = hargint(c, "fill", -1);
996
997 graph = hargstr(c, "graph", "raw");
998 if(strcmp(graph, "raw") == 0)
999 g.fn = rawgraph;
1000 else if(strcmp(graph, "diskbw") == 0)
1001 g.fn = diskgraph;
1002 else if(strcmp(graph, "iobw") == 0)
1003 g.fn = iograph;
1004 else if(strcmp(graph, "netbw") == 0)
1005 g.fn = netgraph;
1006 else if(strcmp(graph, "diff") == 0)
1007 g.fn = diffgraph;
1008 else if(strcmp(graph, "pct") == 0)
1009 g.fn = pctgraph;
1010 else if(strcmp(graph, "pctdiff") == 0)
1011 g.fn = pctdiffgraph;
1012 else if(strcmp(graph, "divdiff") == 0)
1013 g.fn = divdiffgraph;
1014 else{
1015 werrstr("unknown graph %s", graph);
1016 goto error;
1017 }
1018
1019 if(dotext){
1020 hsettype(c, "text/plain");
1021 dotextbin(&c->hout, &g);
1022 hflush(&c->hout);
1023 return 0;
1024 }
1025
1026 m = statgraph(&g);
1027 if(m == nil)
1028 goto error;
1029
1030 if(hsettype(c, "image/png") < 0)
1031 return -1;
1032 hout = &c->hout;
1033 writepng(hout, m);
1034 qlock(&memdrawlock);
1035 freememimage(m);
1036 qunlock(&memdrawlock);
1037 hflush(hout);
1038 return 0;
1039
1040 error:
1041 return herror(c);
1042 }
1043
1044 static int
xloglist(HConnect * c)1045 xloglist(HConnect *c)
1046 {
1047 if(hsettype(c, "text/html") < 0)
1048 return -1;
1049 vtloghlist(&c->hout);
1050 hflush(&c->hout);
1051 return 0;
1052 }
1053
1054 static int
xlog(HConnect * c)1055 xlog(HConnect *c)
1056 {
1057 char *name;
1058 VtLog *l;
1059
1060 name = hargstr(c, "log", "");
1061 if(!name[0])
1062 return xloglist(c);
1063 l = vtlogopen(name, 0);
1064 if(l == nil)
1065 return hnotfound(c);
1066 if(hsettype(c, "text/html") < 0){
1067 vtlogclose(l);
1068 return -1;
1069 }
1070 vtloghdump(&c->hout, l);
1071 vtlogclose(l);
1072 hflush(&c->hout);
1073 return 0;
1074 }
1075
1076 static int
xindex(HConnect * c)1077 xindex(HConnect *c)
1078 {
1079 if(hsettype(c, "text/xml") < 0)
1080 return -1;
1081 xmlindex(&c->hout, mainindex, "index", 0);
1082 hflush(&c->hout);
1083 return 0;
1084 }
1085
1086 void
xmlindent(Hio * hout,int indent)1087 xmlindent(Hio *hout, int indent)
1088 {
1089 int i;
1090
1091 for(i = 0; i < indent; i++)
1092 hputc(hout, '\t');
1093 }
1094
1095 void
xmlaname(Hio * hout,char * v,char * tag)1096 xmlaname(Hio *hout, char *v, char *tag)
1097 {
1098 hprint(hout, " %s=\"%s\"", tag, v);
1099 }
1100
1101 void
xmlscore(Hio * hout,u8int * v,char * tag)1102 xmlscore(Hio *hout, u8int *v, char *tag)
1103 {
1104 if(scorecmp(zeroscore, v) == 0)
1105 return;
1106 hprint(hout, " %s=\"%V\"", tag, v);
1107 }
1108
1109 void
xmlsealed(Hio * hout,int v,char * tag)1110 xmlsealed(Hio *hout, int v, char *tag)
1111 {
1112 if(!v)
1113 return;
1114 hprint(hout, " %s=\"yes\"", tag);
1115 }
1116
1117 void
xmlu32int(Hio * hout,u32int v,char * tag)1118 xmlu32int(Hio *hout, u32int v, char *tag)
1119 {
1120 hprint(hout, " %s=\"%ud\"", tag, v);
1121 }
1122
1123 void
xmlu64int(Hio * hout,u64int v,char * tag)1124 xmlu64int(Hio *hout, u64int v, char *tag)
1125 {
1126 hprint(hout, " %s=\"%llud\"", tag, v);
1127 }
1128
1129 void
vtloghdump(Hio * h,VtLog * l)1130 vtloghdump(Hio *h, VtLog *l)
1131 {
1132 int i;
1133 VtLogChunk *c;
1134 char *name;
1135
1136 name = l ? l->name : "<nil>";
1137
1138 hprint(h, "<html><head>\n");
1139 hprint(h, "<title>Venti Server Log: %s</title>\n", name);
1140 hprint(h, "</head><body>\n");
1141 hprint(h, "<b>Venti Server Log: %s</b>\n<p>\n", name);
1142
1143 if(l){
1144 c = l->w;
1145 for(i=0; i<l->nchunk; i++){
1146 if(++c == l->chunk+l->nchunk)
1147 c = l->chunk;
1148 hwrite(h, c->p, c->wp-c->p);
1149 }
1150 }
1151 hprint(h, "</body></html>\n");
1152 }
1153
1154 static int
strpcmp(const void * va,const void * vb)1155 strpcmp(const void *va, const void *vb)
1156 {
1157 return strcmp(*(char**)va, *(char**)vb);
1158 }
1159
1160 void
vtloghlist(Hio * h)1161 vtloghlist(Hio *h)
1162 {
1163 char **p;
1164 int i, n;
1165
1166 hprint(h, "<html><head>\n");
1167 hprint(h, "<title>Venti Server Logs</title>\n");
1168 hprint(h, "</head><body>\n");
1169 hprint(h, "<b>Venti Server Logs</b>\n<p>\n");
1170
1171 p = vtlognames(&n);
1172 qsort(p, n, sizeof(p[0]), strpcmp);
1173 for(i=0; i<n; i++)
1174 hprint(h, "<a href=\"/log?log=%s\">%s</a><br>\n", p[i], p[i]);
1175 vtfree(p);
1176 hprint(h, "</body></html>\n");
1177 }
1178