#include "stdinc.h"#include "dat.h"#include "fns.h"#include "xml.h" typedef struct HttpObj HttpObj;extern QLock memdrawlock; enum{ ObjNameSize = 64, MaxObjs = 64}; struct HttpObj{ char name[ObjNameSize]; int (*f)(HConnect*);}; static HttpObj objs[MaxObjs]; static char *webroot; static void listenproc(void*);static int estats(HConnect *c);static int dindex(HConnect *c);static int xindex(HConnect *c);static int xlog(HConnect *c);static int sindex(HConnect *c);static int hempty(HConnect *c);static int hlcacheempty(HConnect *c);static int hdcacheempty(HConnect *c);static int hicacheempty(HConnect *c);static int hicachekick(HConnect *c);static int hdcachekick(HConnect *c);static int hicacheflush(HConnect *c);static int hdcacheflush(HConnect *c);static int httpdobj(char *name, int (*f)(HConnect*));static int xgraph(HConnect *c);static int xset(HConnect *c);static int fromwebdir(HConnect *c); inthttpdinit(char *address, char *dir){ fmtinstall('D', hdatefmt);/* fmtinstall('H', httpfmt); */ fmtinstall('U', hurlfmt); if(address == nil) address = "tcp!*!http"; webroot = dir; httpdobj("/stats", estats); httpdobj("/index", dindex); httpdobj("/storage", sindex); httpdobj("/xindex", xindex); httpdobj("/flushicache", hicacheflush); httpdobj("/flushdcache", hdcacheflush); httpdobj("/kickicache", hicachekick); httpdobj("/kickdcache", hdcachekick); httpdobj("/graph", xgraph); httpdobj("/set", xset); httpdobj("/log", xlog); httpdobj("/empty", hempty); httpdobj("/emptyicache", hicacheempty); httpdobj("/emptylumpcache", hlcacheempty); httpdobj("/emptydcache", hdcacheempty); httpdobj("/disk", hdisk); httpdobj("/debug", hdebug); httpdobj("/proc/", hproc); if(vtproc(listenproc, address) < 0) return -1; return 0;} static inthttpdobj(char *name, int (*f)(HConnect*)){ int i; if(name == nil || strlen(name) >= ObjNameSize) return -1; for(i = 0; i < MaxObjs; i++){ if(objs[i].name[0] == '\0'){ strcpy(objs[i].name, name); objs[i].f = f; return 0; } if(strcmp(objs[i].name, name) == 0) return -1; } return -1;} static HConnect*mkconnect(void){ HConnect *c; c = mallocz(sizeof(HConnect), 1); if(c == nil) sysfatal("out of memory"); c->replog = nil; c->hpos = c->header; c->hstop = c->header; return c;} void httpproc(void*); static voidlistenproc(void *vaddress){ HConnect *c; char *address, ndir[NETPATHLEN], dir[NETPATHLEN]; int ctl, nctl, data; address = vaddress; ctl = announce(address, dir); if(ctl < 0){ fprint(2, "venti: httpd can't announce on %s: %r\n", address); return; } if(0) print("announce ctl %d dir %s\n", ctl, dir); for(;;){ /* * wait for a call (or an error) */ nctl = listen(dir, ndir); if(0) print("httpd listen %d %s...\n", nctl, ndir); if(nctl < 0){ fprint(2, "venti: httpd can't listen on %s: %r\n", address); return; } data = accept(ctl, ndir); if(0) print("httpd accept %d...\n", data); if(data < 0){ fprint(2, "venti: httpd accept: %r\n"); close(nctl); continue; } if(0) print("httpd close nctl %d\n", nctl); close(nctl); c = mkconnect(); hinit(&c->hin, data, Hread); hinit(&c->hout, data, Hwrite); vtproc(httpproc, c); }} voidhttpproc(void *v){ HConnect *c; int ok, i, n; c = v; for(;;){ /* * No timeout because the signal appears to hit every * proc, not just us. */ if(hparsereq(c, 0) < 0) break; for(i = 0; i < MaxObjs && objs[i].name[0]; i++){ n = strlen(objs[i].name); if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0) || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){ ok = (*objs[i].f)(c); goto found; } } ok = fromwebdir(c); found: hflush(&c->hout); if(c->head.closeit) ok = -1; hreqcleanup(c); if(ok < 0) break; } hreqcleanup(c); close(c->hin.fd); free(c);} char*hargstr(HConnect *c, char *name, char *def){ HSPairs *p; for(p=c->req.searchpairs; p; p=p->next) if(strcmp(p->s, name) == 0) return p->t; return def;} vlonghargint(HConnect *c, char *name, vlong def){ char *a; if((a = hargstr(c, name, nil)) == nil) return def; return atoll(a);} static intpercent(ulong v, ulong total){ if(total == 0) total = 1; if(v < 1000*1000) return (v * 100) / total; total /= 100; if(total == 0) total = 1; return v / total;} static intpreq(HConnect *c){ if(hparseheaders(c, 0) < 0) return -1; if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0) return hunallowed(c, "GET, HEAD"); if(c->head.expectother || c->head.expectcont) return hfail(c, HExpectFail, nil); return 0;} inthsettype(HConnect *c, char *type){ Hio *hout; int r; r = preq(c); if(r < 0) return r; hout = &c->hout; if(c->req.vermaj){ hokheaders(c); hprint(hout, "Content-type: %s\r\n", type); if(http11(c)) hprint(hout, "Transfer-Encoding: chunked\r\n"); hprint(hout, "\r\n"); } if(http11(c)) hxferenc(hout, 1); else c->head.closeit = 1; return 0;} inthsethtml(HConnect *c){ return hsettype(c, "text/html; charset=utf-8");} inthsettext(HConnect *c){ return hsettype(c, "text/plain; charset=utf-8");} static intherror(HConnect *c){ int n; Hio *hout; hout = &c->hout; n = snprint(c->xferbuf, HBufSize, "<html><head><title>Error</title></head>\n<body><h1>Error</h1>\n<pre>%r</pre>\n</body></html>"); hprint(hout, "%s %s\r\n", hversion, "400 Bad Request"); hprint(hout, "Date: %D\r\n", time(nil)); hprint(hout, "Server: Venti\r\n"); hprint(hout, "Content-Type: text/html\r\n"); hprint(hout, "Content-Length: %d\r\n", n); if(c->head.closeit) hprint(hout, "Connection: close\r\n"); else if(!http11(c)) hprint(hout, "Connection: Keep-Alive\r\n"); hprint(hout, "\r\n"); if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0) hwrite(hout, c->xferbuf, n); return hflush(hout);} inthnotfound(HConnect *c){ int r; r = preq(c); if(r < 0) return r; return hfail(c, HNotFound, c->req.uri);} struct { char *ext; char *type;} exttab[] = { ".html", "text/html", ".txt", "text/plain", ".xml", "text/xml", ".png", "image/png", ".gif", "image/gif", 0}; static intfromwebdir(HConnect *c){ char buf[4096], *p, *ext, *type; int i, fd, n, defaulted; Dir *d; if(webroot == nil || strstr(c->req.uri, "..")) return hnotfound(c); snprint(buf, sizeof buf-20, "%s/%s", webroot, c->req.uri+1); defaulted = 0;reopen: if((fd = open(buf, OREAD)) < 0) return hnotfound(c); d = dirfstat(fd); if(d == nil){ close(fd); return hnotfound(c); } if(d->mode&DMDIR){ if(!defaulted){ defaulted = 1; strcat(buf, "/index.html"); free(d); close(fd); goto reopen; } free(d); return hnotfound(c); } free(d); p = buf+strlen(buf); type = "application/octet-stream"; for(i=0; exttab[i].ext; i++){ ext = exttab[i].ext; if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){ type = exttab[i].type; break; } } if(hsettype(c, type) < 0){ close(fd); return 0; } while((n = read(fd, buf, sizeof buf)) > 0) if(hwrite(&c->hout, buf, n) < 0) break; close(fd); hflush(&c->hout); return 0;} static struct{ char *name; int *p;} namedints[] ={ "compress", &compressblocks, "devnull", &writestodevnull, "logging", &ventilogging, "stats", &collectstats, "icachesleeptime", &icachesleeptime, "minicachesleeptime", &minicachesleeptime, "arenasumsleeptime", &arenasumsleeptime, "l0quantum", &l0quantum, "l1quantum", &l1quantum, "manualscheduling", &manualscheduling, "ignorebloom", &ignorebloom, "syncwrites", &syncwrites, "icacheprefetch", &icacheprefetch, 0}; static intxset(HConnect *c){ int i, old; char *name, *value; if(hsettext(c) < 0) return -1; if((name = hargstr(c, "name", nil)) == nil || name[0] == 0){ for(i=0; namedints[i].name; i++) hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p); hflush(&c->hout); return 0; } for(i=0; namedints[i].name; i++) if(strcmp(name, namedints[i].name) == 0) break; if(!namedints[i].name){ hprint(&c->hout, "%s not found\n", name); hflush(&c->hout); return 0; } if((value = hargstr(c, "value", nil)) == nil || value[0] == 0){ hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p); hflush(&c->hout); return 0; } old = *namedints[i].p; *namedints[i].p = atoll(value); hprint(&c->hout, "%s = %d (was %d)\n", name, *namedints[i].p, old); hflush(&c->hout); return 0;} static intestats(HConnect *c){ Hio *hout; int r; r = hsettext(c); if(r < 0) return r; hout = &c->hout;/* hprint(hout, "lump writes=%,ld\n", stats.lumpwrites); hprint(hout, "lump reads=%,ld\n", stats.lumpreads); hprint(hout, "lump cache read hits=%,ld\n", stats.lumphit); hprint(hout, "lump cache read misses=%,ld\n", stats.lumpmiss); hprint(hout, "clump disk writes=%,ld\n", stats.clumpwrites); hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpbwrites); hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpbcomp); hprint(hout, "clump disk reads=%,ld\n", stats.clumpreads); hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpbreads); hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpbuncomp); hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites); hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads); hprint(hout, "index disk writes=%,ld\n", stats.indexwrites); hprint(hout, "index disk reads=%,ld\n", stats.indexreads); hprint(hout, "index disk bloom filter hits=%,ld %d%% falsemisses=%,ld %d%%\n", stats.indexbloomhits, percent(stats.indexbloomhits, stats.indexreads), stats.indexbloomfalsemisses, percent(stats.indexbloomfalsemisses, stats.indexreads)); hprint(hout, "bloom filter bits=%,ld of %,ld %d%%\n", stats.bloomones, stats.bloombits, percent(stats.bloomones, stats.bloombits)); hprint(hout, "index disk reads for modify=%,ld\n", stats.indexwreads); hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexareads); hprint(hout, "index block splits=%,ld\n", stats.indexsplits); hprint(hout, "index cache lookups=%,ld\n", stats.iclookups); hprint(hout, "index cache hits=%,ld %d%%\n", stats.ichits, percent(stats.ichits, stats.iclookups)); hprint(hout, "index cache fills=%,ld %d%%\n", stats.icfills, percent(stats.icfills, stats.iclookups)); hprint(hout, "index cache inserts=%,ld\n", stats.icinserts); hprint(hout, "disk cache hits=%,ld\n", stats.pchit); hprint(hout, "disk cache misses=%,ld\n", stats.pcmiss); hprint(hout, "disk cache reads=%,ld\n", stats.pcreads); hprint(hout, "disk cache bytes read=%,lld\n", stats.pcbreads); hprint(hout, "disk cache writes=%,ld\n", stats.dirtydblocks); hprint(hout, "disk cache writes absorbed=%,ld %d%%\n", stats.absorbedwrites, percent(stats.absorbedwrites, stats.dirtydblocks)); hprint(hout, "disk cache flushes=%,ld\n", stats.dcacheflushes); hprint(hout, "disk cache flush writes=%,ld (%,ld per flush)\n", stats.dcacheflushwrites, stats.dcacheflushwrites/(stats.dcacheflushes ? stats.dcacheflushes : 1));Lines 1–500 of 1177. Load more
#include "stdinc.h" #include "dat.h" #include "fns.h" #include "xml.h" typedef struct HttpObj HttpObj; extern QLock memdrawlock; enum { ObjNameSize = 64, MaxObjs = 64 }; struct HttpObj { char name[ObjNameSize]; int (*f)(HConnect*); }; static HttpObj objs[MaxObjs]; static char *webroot; static void listenproc(void*); static int estats(HConnect *c); static int dindex(HConnect *c); static int xindex(HConnect *c); static int xlog(HConnect *c); static int sindex(HConnect *c); static int hempty(HConnect *c); static int hlcacheempty(HConnect *c); static int hdcacheempty(HConnect *c); static int hicacheempty(HConnect *c); static int hicachekick(HConnect *c); static int hdcachekick(HConnect *c); static int hicacheflush(HConnect *c); static int hdcacheflush(HConnect *c); static int httpdobj(char *name, int (*f)(HConnect*)); static int xgraph(HConnect *c); static int xset(HConnect *c); static int fromwebdir(HConnect *c); int httpdinit(char *address, char *dir) { fmtinstall('D', hdatefmt); /* fmtinstall('H', httpfmt); */ fmtinstall('U', hurlfmt); if(address == nil) address = "tcp!*!http"; webroot = dir; httpdobj("/stats", estats); httpdobj("/index", dindex); httpdobj("/storage", sindex); httpdobj("/xindex", xindex); httpdobj("/flushicache", hicacheflush); httpdobj("/flushdcache", hdcacheflush); httpdobj("/kickicache", hicachekick); httpdobj("/kickdcache", hdcachekick); httpdobj("/graph", xgraph); httpdobj("/set", xset); httpdobj("/log", xlog); httpdobj("/empty", hempty); httpdobj("/emptyicache", hicacheempty); httpdobj("/emptylumpcache", hlcacheempty); httpdobj("/emptydcache", hdcacheempty); httpdobj("/disk", hdisk); httpdobj("/debug", hdebug); httpdobj("/proc/", hproc); if(vtproc(listenproc, address) < 0) return -1; return 0; } static int httpdobj(char *name, int (*f)(HConnect*)) { int i; if(name == nil || strlen(name) >= ObjNameSize) return -1; for(i = 0; i < MaxObjs; i++){ if(objs[i].name[0] == '\0'){ strcpy(objs[i].name, name); objs[i].f = f; return 0; } if(strcmp(objs[i].name, name) == 0) return -1; } return -1; } static HConnect* mkconnect(void) { HConnect *c; c = mallocz(sizeof(HConnect), 1); if(c == nil) sysfatal("out of memory"); c->replog = nil; c->hpos = c->header; c->hstop = c->header; return c; } void httpproc(void*); static void listenproc(void *vaddress) { HConnect *c; char *address, ndir[NETPATHLEN], dir[NETPATHLEN]; int ctl, nctl, data; address = vaddress; ctl = announce(address, dir); if(ctl < 0){ fprint(2, "venti: httpd can't announce on %s: %r\n", address); return; } if(0) print("announce ctl %d dir %s\n", ctl, dir); for(;;){ /* * wait for a call (or an error) */ nctl = listen(dir, ndir); if(0) print("httpd listen %d %s...\n", nctl, ndir); if(nctl < 0){ fprint(2, "venti: httpd can't listen on %s: %r\n", address); return; } data = accept(ctl, ndir); if(0) print("httpd accept %d...\n", data); if(data < 0){ fprint(2, "venti: httpd accept: %r\n"); close(nctl); continue; } if(0) print("httpd close nctl %d\n", nctl); close(nctl); c = mkconnect(); hinit(&c->hin, data, Hread); hinit(&c->hout, data, Hwrite); vtproc(httpproc, c); } } void httpproc(void *v) { HConnect *c; int ok, i, n; c = v; for(;;){ /* * No timeout because the signal appears to hit every * proc, not just us. */ if(hparsereq(c, 0) < 0) break; for(i = 0; i < MaxObjs && objs[i].name[0]; i++){ n = strlen(objs[i].name); if((objs[i].name[n-1] == '/' && strncmp(c->req.uri, objs[i].name, n) == 0) || (objs[i].name[n-1] != '/' && strcmp(c->req.uri, objs[i].name) == 0)){ ok = (*objs[i].f)(c); goto found; } } ok = fromwebdir(c); found: hflush(&c->hout); if(c->head.closeit) ok = -1; hreqcleanup(c); if(ok < 0) break; } hreqcleanup(c); close(c->hin.fd); free(c); } char* hargstr(HConnect *c, char *name, char *def) { HSPairs *p; for(p=c->req.searchpairs; p; p=p->next) if(strcmp(p->s, name) == 0) return p->t; return def; } vlong hargint(HConnect *c, char *name, vlong def) { char *a; if((a = hargstr(c, name, nil)) == nil) return def; return atoll(a); } static int percent(ulong v, ulong total) { if(total == 0) total = 1; if(v < 1000*1000) return (v * 100) / total; total /= 100; if(total == 0) total = 1; return v / total; } static int preq(HConnect *c) { if(hparseheaders(c, 0) < 0) return -1; if(strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0) return hunallowed(c, "GET, HEAD"); if(c->head.expectother || c->head.expectcont) return hfail(c, HExpectFail, nil); return 0; } int hsettype(HConnect *c, char *type) { Hio *hout; int r; r = preq(c); if(r < 0) return r; hout = &c->hout; if(c->req.vermaj){ hokheaders(c); hprint(hout, "Content-type: %s\r\n", type); if(http11(c)) hprint(hout, "Transfer-Encoding: chunked\r\n"); hprint(hout, "\r\n"); } if(http11(c)) hxferenc(hout, 1); else c->head.closeit = 1; return 0; } int hsethtml(HConnect *c) { return hsettype(c, "text/html; charset=utf-8"); } int hsettext(HConnect *c) { return hsettype(c, "text/plain; charset=utf-8"); } static int herror(HConnect *c) { int n; Hio *hout; hout = &c->hout; n = snprint(c->xferbuf, HBufSize, "<html><head><title>Error</title></head>\n<body><h1>Error</h1>\n<pre>%r</pre>\n</body></html>"); hprint(hout, "%s %s\r\n", hversion, "400 Bad Request"); hprint(hout, "Date: %D\r\n", time(nil)); hprint(hout, "Server: Venti\r\n"); hprint(hout, "Content-Type: text/html\r\n"); hprint(hout, "Content-Length: %d\r\n", n); if(c->head.closeit) hprint(hout, "Connection: close\r\n"); else if(!http11(c)) hprint(hout, "Connection: Keep-Alive\r\n"); hprint(hout, "\r\n"); if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0) hwrite(hout, c->xferbuf, n); return hflush(hout); } int hnotfound(HConnect *c) { int r; r = preq(c); if(r < 0) return r; return hfail(c, HNotFound, c->req.uri); } struct { char *ext; char *type; } exttab[] = { ".html", "text/html", ".txt", "text/plain", ".xml", "text/xml", ".png", "image/png", ".gif", "image/gif", 0 }; static int fromwebdir(HConnect *c) { char buf[4096], *p, *ext, *type; int i, fd, n, defaulted; Dir *d; if(webroot == nil || strstr(c->req.uri, "..")) return hnotfound(c); snprint(buf, sizeof buf-20, "%s/%s", webroot, c->req.uri+1); defaulted = 0; reopen: if((fd = open(buf, OREAD)) < 0) return hnotfound(c); d = dirfstat(fd); if(d == nil){ close(fd); return hnotfound(c); } if(d->mode&DMDIR){ if(!defaulted){ defaulted = 1; strcat(buf, "/index.html"); free(d); close(fd); goto reopen; } free(d); return hnotfound(c); } free(d); p = buf+strlen(buf); type = "application/octet-stream"; for(i=0; exttab[i].ext; i++){ ext = exttab[i].ext; if(p-strlen(ext) >= buf && strcmp(p-strlen(ext), ext) == 0){ type = exttab[i].type; break; } } if(hsettype(c, type) < 0){ close(fd); return 0; } while((n = read(fd, buf, sizeof buf)) > 0) if(hwrite(&c->hout, buf, n) < 0) break; close(fd); hflush(&c->hout); return 0; } static struct { char *name; int *p; } namedints[] = { "compress", &compressblocks, "devnull", &writestodevnull, "logging", &ventilogging, "stats", &collectstats, "icachesleeptime", &icachesleeptime, "minicachesleeptime", &minicachesleeptime, "arenasumsleeptime", &arenasumsleeptime, "l0quantum", &l0quantum, "l1quantum", &l1quantum, "manualscheduling", &manualscheduling, "ignorebloom", &ignorebloom, "syncwrites", &syncwrites, "icacheprefetch", &icacheprefetch, 0 }; static int xset(HConnect *c) { int i, old; char *name, *value; if(hsettext(c) < 0) return -1; if((name = hargstr(c, "name", nil)) == nil || name[0] == 0){ for(i=0; namedints[i].name; i++) hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p); hflush(&c->hout); return 0; } for(i=0; namedints[i].name; i++) if(strcmp(name, namedints[i].name) == 0) break; if(!namedints[i].name){ hprint(&c->hout, "%s not found\n", name); hflush(&c->hout); return 0; } if((value = hargstr(c, "value", nil)) == nil || value[0] == 0){ hprint(&c->hout, "%s = %d\n", namedints[i].name, *namedints[i].p); hflush(&c->hout); return 0; } old = *namedints[i].p; *namedints[i].p = atoll(value); hprint(&c->hout, "%s = %d (was %d)\n", name, *namedints[i].p, old); hflush(&c->hout); return 0; } static int estats(HConnect *c) { Hio *hout; int r; r = hsettext(c); if(r < 0) return r; hout = &c->hout; /* hprint(hout, "lump writes=%,ld\n", stats.lumpwrites); hprint(hout, "lump reads=%,ld\n", stats.lumpreads); hprint(hout, "lump cache read hits=%,ld\n", stats.lumphit); hprint(hout, "lump cache read misses=%,ld\n", stats.lumpmiss); hprint(hout, "clump disk writes=%,ld\n", stats.clumpwrites); hprint(hout, "clump disk bytes written=%,lld\n", stats.clumpbwrites); hprint(hout, "clump disk bytes compressed=%,lld\n", stats.clumpbcomp); hprint(hout, "clump disk reads=%,ld\n", stats.clumpreads); hprint(hout, "clump disk bytes read=%,lld\n", stats.clumpbreads); hprint(hout, "clump disk bytes uncompressed=%,lld\n", stats.clumpbuncomp); hprint(hout, "clump directory disk writes=%,ld\n", stats.ciwrites); hprint(hout, "clump directory disk reads=%,ld\n", stats.cireads); hprint(hout, "index disk writes=%,ld\n", stats.indexwrites); hprint(hout, "index disk reads=%,ld\n", stats.indexreads); hprint(hout, "index disk bloom filter hits=%,ld %d%% falsemisses=%,ld %d%%\n", stats.indexbloomhits, percent(stats.indexbloomhits, stats.indexreads), stats.indexbloomfalsemisses, percent(stats.indexbloomfalsemisses, stats.indexreads)); hprint(hout, "bloom filter bits=%,ld of %,ld %d%%\n", stats.bloomones, stats.bloombits, percent(stats.bloomones, stats.bloombits)); hprint(hout, "index disk reads for modify=%,ld\n", stats.indexwreads); hprint(hout, "index disk reads for allocation=%,ld\n", stats.indexareads); hprint(hout, "index block splits=%,ld\n", stats.indexsplits); hprint(hout, "index cache lookups=%,ld\n", stats.iclookups); hprint(hout, "index cache hits=%,ld %d%%\n", stats.ichits, percent(stats.ichits, stats.iclookups)); hprint(hout, "index cache fills=%,ld %d%%\n", stats.icfills, percent(stats.icfills, stats.iclookups)); hprint(hout, "index cache inserts=%,ld\n", stats.icinserts); hprint(hout, "disk cache hits=%,ld\n", stats.pchit); hprint(hout, "disk cache misses=%,ld\n", stats.pcmiss); hprint(hout, "disk cache reads=%,ld\n", stats.pcreads); hprint(hout, "disk cache bytes read=%,lld\n", stats.pcbreads); hprint(hout, "disk cache writes=%,ld\n", stats.dirtydblocks); hprint(hout, "disk cache writes absorbed=%,ld %d%%\n", stats.absorbedwrites, percent(stats.absorbedwrites, stats.dirtydblocks)); hprint(hout, "disk cache flushes=%,ld\n", stats.dcacheflushes); hprint(hout, "disk cache flush writes=%,ld (%,ld per flush)\n", stats.dcacheflushwrites, stats.dcacheflushwrites/(stats.dcacheflushes ? stats.dcacheflushes : 1));