xref: /plan9/sys/src/cmd/venti/srv/hdisk.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
1 #include "stdinc.h"
2 #include "dat.h"
3 #include "fns.h"
4 #include "whack.h"
5 
6 static int disksummary(HConnect*);
7 static int diskarenapart(HConnect*, char*, Part*);
8 static int diskbloom(HConnect*, char*, Part*);
9 static int diskisect(HConnect*, char*, Part*);
10 
11 int
hdisk(HConnect * c)12 hdisk(HConnect *c)
13 {
14 	char *disk, *type;
15 	Part *p;
16 	int ret;
17 
18 	if(hsethtml(c) < 0)
19 		return -1;
20 
21 	disk = hargstr(c, "disk", "");
22 	if(!disk[0])
23 		return disksummary(c);
24 	if((p = initpart(disk, OREAD)) == nil){
25 		hprint(&c->hout, "open %s: %r", disk);
26 		return 0;
27 	}
28 
29 	type = hargstr(c, "type", "");
30 	switch(type[0]){
31 	case 'a':
32 		ret = diskarenapart(c, disk, p);
33 		break;
34 	case 'b':
35 		ret = diskbloom(c, disk, p);
36 		break;
37 	case 'i':
38 		ret = diskisect(c, disk, p);
39 		break;
40 	default:
41 		hprint(&c->hout, "unknown disk type %s", type);
42 		return 0;
43 	}
44 	freepart(p);
45 	return ret;
46 }
47 
48 static int
disksummary(HConnect * c)49 disksummary(HConnect *c)
50 {
51 	int i;
52 	Index *ix;
53 	Part *p;
54 
55 	hprint(&c->hout, "<h1>venti disks</h1>\n");
56 	hprint(&c->hout, "<pre>\n");
57 	ix = mainindex;
58 	p = nil;
59 	for(i=0; i<ix->narenas; i++){
60 		if(ix->arenas[i]->part == p)
61 			continue;
62 		p = ix->arenas[i]->part;
63 		hprint(&c->hout, "<a href=\"/disk?disk=%s&type=a\">%s</a> %s\n", p->name, p->name, ix->arenas[i]->name);
64 	}
65 	hprint(&c->hout, "\n");
66 	p = nil;
67 	for(i=0; i<ix->nsects; i++){
68 		if(ix->sects[i]->part == p)
69 			continue;
70 		p = ix->sects[i]->part;
71 		hprint(&c->hout, "<a href=\"/disk?disk=%s&type=i\">%s</a> %s\n", p->name, p->name, ix->sects[i]->name);
72 	}
73 	hprint(&c->hout, "\n");
74 	if(ix->bloom){
75 		p = ix->bloom->part;
76 		hprint(&c->hout, "<a href=\"/disk?disk=%s&type=b\">%s</a> %s\n", p->name, p->name, "bloom filter");
77 	}
78 	return 0;
79 }
80 
81 static char*
readap(Part * p,ArenaPart * ap)82 readap(Part *p, ArenaPart *ap)
83 {
84 	uchar *blk;
85 	char *table;
86 
87 	blk = vtmalloc(8192);
88 	if(readpart(p, PartBlank, blk, 8192) != 8192)
89 		return nil;
90 	if(unpackarenapart(ap, blk) < 0){
91 		werrstr("corrupt arena part header: %r");
92 		return nil;
93 	}
94 	vtfree(blk);
95 	ap->tabbase = (PartBlank+HeadSize+ap->blocksize-1)&~(ap->blocksize-1);
96 	ap->tabsize = ap->arenabase - ap->tabbase;
97 	table = vtmalloc(ap->tabsize+1);
98 	if(readpart(p, ap->tabbase, (uchar*)table, ap->tabsize) != ap->tabsize){
99 		werrstr("reading arena part directory: %r");
100 		return nil;
101 	}
102 	table[ap->tabsize] = 0;
103 	return table;
104 }
105 
106 static int
xfindarena(char * table,char * name,vlong * start,vlong * end)107 xfindarena(char *table, char *name, vlong *start, vlong *end)
108 {
109 	int i, nline;
110 	char *p, *q, *f[4], line[256];
111 
112 	nline = atoi(table);
113 	p = strchr(table, '\n');
114 	if(p)
115 		p++;
116 	for(i=0; i<nline; i++){
117 		if(p == nil)
118 			break;
119 		q = strchr(p, '\n');
120 		if(q)
121 			*q++ = 0;
122 		if(strlen(p) >= sizeof line){
123 			p = q;
124 			continue;
125 		}
126 		strcpy(line, p);
127 		memset(f, 0, sizeof f);
128 		if(tokenize(line, f, nelem(f)) < 3){
129 			p = q;
130 			continue;
131 		}
132 		if(strcmp(f[0], name) == 0){
133 			*start = strtoull(f[1], 0, 0);
134 			*end = strtoull(f[2], 0, 0);
135 			return 0;
136 		}
137 		p = q;
138 	}
139 	return -1;
140 }
141 
142 static void
diskarenatable(HConnect * c,char * disk,char * table)143 diskarenatable(HConnect *c, char *disk, char *table)
144 {
145 	char *p, *q;
146 	int i, nline;
147 	char *f[4], line[256], base[256];
148 
149 	hprint(&c->hout, "<h2>table</h2>\n");
150 	hprint(&c->hout, "<pre>\n");
151 	nline = atoi(table);
152 	snprint(base, sizeof base, "/disk?disk=%s&type=a", disk);
153 	p = strchr(table, '\n');
154 	if(p)
155 		p++;
156 	for(i=0; i<nline; i++){
157 		if(p == nil){
158 			hprint(&c->hout, "<b><i>unexpected end of table</i></b>\n");
159 			break;
160 		}
161 		q = strchr(p, '\n');
162 		if(q)
163 			*q++ = 0;
164 		if(strlen(p) >= sizeof line){
165 			hprint(&c->hout, "%s\n", p);
166 			p = q;
167 			continue;
168 		}
169 		strcpy(line, p);
170 		memset(f, 0, sizeof f);
171 		if(tokenize(line, f, 3) < 3){
172 			hprint(&c->hout, "%s\n", p);
173 			p = q;
174 			continue;
175 		}
176 		p = q;
177 		hprint(&c->hout, "<a href=\"%s&arena=%s\">%s</a> %s %s\n",
178 			base, f[0], f[0], f[1], f[2]);
179 	}
180 	hprint(&c->hout, "</pre>\n");
181 }
182 
183 static char*
fmttime(char * buf,ulong time)184 fmttime(char *buf, ulong time)
185 {
186 	strcpy(buf, ctime(time));
187 	buf[28] = 0;
188 	return buf;
189 }
190 
191 
192 static int diskarenaclump(HConnect*, Arena*, vlong, char*);
193 static int diskarenatoc(HConnect*, Arena*);
194 
195 static int
diskarenapart(HConnect * c,char * disk,Part * p)196 diskarenapart(HConnect *c, char *disk, Part *p)
197 {
198 	char *arenaname;
199 	ArenaPart ap;
200 	ArenaHead head;
201 	Arena arena;
202 	char *table;
203 	char *score;
204 	char *clump;
205 	uchar *blk;
206 	vlong start, end, off;
207 	char tbuf[60];
208 
209 	hprint(&c->hout, "<h1>arena partition %s</h1>\n", disk);
210 
211 	if((table = readap(p, &ap)) == nil){
212 		hprint(&c->hout, "%r\n");
213 		goto out;
214 	}
215 
216 	hprint(&c->hout, "<pre>\n");
217 	hprint(&c->hout, "version=%d blocksize=%d base=%d\n",
218 		ap.version, ap.blocksize, ap.arenabase);
219 	hprint(&c->hout, "</pre>\n");
220 
221 	arenaname = hargstr(c, "arena", "");
222 	if(arenaname[0] == 0){
223 		diskarenatable(c, disk, table);
224 		goto out;
225 	}
226 
227 	if(xfindarena(table, arenaname, &start, &end) < 0){
228 		hprint(&c->hout, "no such arena %s\n", arenaname);
229 		goto out;
230 	}
231 
232 	hprint(&c->hout, "<h2>arena %s</h2>\n", arenaname);
233 	hprint(&c->hout, "<pre>start=%#llx end=%#llx<pre>\n", start, end);
234 	if(end < start || end - start < HeadSize){
235 		hprint(&c->hout, "bad size %#llx\n", end - start);
236 		goto out;
237 	}
238 
239 	// read arena header, tail
240 	blk = vtmalloc(HeadSize);
241 	if(readpart(p, start, blk, HeadSize) != HeadSize){
242 		hprint(&c->hout, "reading header: %r\n");
243 		vtfree(blk);
244 		goto out;
245 	}
246 	if(unpackarenahead(&head, blk) < 0){
247 		hprint(&c->hout, "corrupt arena header: %r\n");
248 		// hhex(blk, HeadSize);
249 		vtfree(blk);
250 		goto out;
251 	}
252 	vtfree(blk);
253 
254 	hprint(&c->hout, "head:\n<pre>\n");
255 	hprint(&c->hout, "version=%d name=%s blocksize=%d size=%#llx clumpmagic=%#ux\n",
256 		head.version, head.name, head.blocksize, head.size,
257 		head.clumpmagic);
258 	hprint(&c->hout, "</pre><br><br>\n");
259 
260 	if(head.blocksize > MaxIoSize || head.blocksize >= end - start){
261 		hprint(&c->hout, "corrupt block size %d\n", head.blocksize);
262 		goto out;
263 	}
264 
265 	blk = vtmalloc(head.blocksize);
266 	if(readpart(p, end - head.blocksize, blk, head.blocksize) < 0){
267 		hprint(&c->hout, "reading tail: %r\n");
268 		vtfree(blk);
269 		goto out;
270 	}
271 	memset(&arena, 0, sizeof arena);
272 	arena.part = p;
273 	arena.blocksize = head.blocksize;
274 	arena.clumpmax = head.blocksize / ClumpInfoSize;
275 	arena.base = start + head.blocksize;
276 	arena.size = end - start - 2 * head.blocksize;
277 	if(unpackarena(&arena, blk) < 0){
278 		vtfree(blk);
279 		goto out;
280 	}
281 	scorecp(arena.score, blk+head.blocksize - VtScoreSize);
282 
283 	vtfree(blk);
284 
285 	hprint(&c->hout, "tail:\n<pre>\n");
286 	hprint(&c->hout, "version=%d name=%s\n", arena.version, arena.name);
287 	hprint(&c->hout, "ctime=%d %s\n", arena.ctime, fmttime(tbuf, arena.ctime));
288 	hprint(&c->hout, "wtime=%d %s\n", arena.wtime, fmttime(tbuf, arena.wtime));
289 	hprint(&c->hout, "clumpmagic=%#ux\n", arena.clumpmagic);
290 	hprint(&c->hout, "score %V\n", arena.score);
291 	hprint(&c->hout, "diskstats:\n");
292 	hprint(&c->hout, "\tclumps=%,d cclumps=%,d used=%,lld uncsize=%,lld sealed=%d\n",
293 		arena.diskstats.clumps, arena.diskstats.cclumps,
294 		arena.diskstats.used, arena.diskstats.uncsize,
295 		arena.diskstats.sealed);
296 	hprint(&c->hout, "memstats:\n");
297 	hprint(&c->hout, "\tclumps=%,d cclumps=%,d used=%,lld uncsize=%,lld sealed=%d\n",
298 		arena.memstats.clumps, arena.memstats.cclumps,
299 		arena.memstats.used, arena.memstats.uncsize,
300 		arena.memstats.sealed);
301 	if(arena.clumpmax == 0){
302 		hprint(&c->hout, "bad clumpmax\n");
303 		goto out;
304 	}
305 
306 	score = hargstr(c, "score", "");
307 	clump = hargstr(c, "clump", "");
308 
309 	if(clump[0]){
310 		off = strtoull(clump, 0, 0);
311 		diskarenaclump(c, &arena, off, score[0] ? score : nil);
312 	}else if(score[0]){
313 		diskarenaclump(c, &arena, -1, score);
314 	}else{
315 		diskarenatoc(c, &arena);
316 	}
317 
318 out:
319 	free(table);
320 	return 0;
321 }
322 
323 static vlong
findintoc(HConnect * c,Arena * arena,uchar * score)324 findintoc(HConnect *c, Arena *arena, uchar *score)
325 {
326 	uchar *blk;
327 	int i;
328 	vlong off;
329 	vlong coff;
330 	ClumpInfo ci;
331 
332 	blk = vtmalloc(arena->blocksize);
333 	off = arena->base + arena->size;
334 	coff = 0;
335 	for(i=0; i<arena->memstats.clumps; i++){
336 		if(i%arena->clumpmax == 0){
337 			off -= arena->blocksize;
338 			if(readpart(arena->part, off, blk, arena->blocksize) != arena->blocksize){
339 				if(c)
340 					hprint(&c->hout, "<i>clump info directory at %#llx: %r</i>\n<br>\n",
341 						off);
342 				break;
343 			}
344 		}
345 		unpackclumpinfo(&ci, blk+(i%arena->clumpmax)*ClumpInfoSize);
346 		if(scorecmp(ci.score, score) == 0){
347 			vtfree(blk);
348 			return coff;
349 		}
350 		coff += ClumpSize + ci.size;
351 	}
352 	vtfree(blk);
353 	return -1;
354 }
355 
356 
357 static int
diskarenatoc(HConnect * c,Arena * arena)358 diskarenatoc(HConnect *c, Arena *arena)
359 {
360 	uchar *blk;
361 	int i;
362 	vlong off;
363 	vlong coff;
364 	ClumpInfo ci;
365 	char base[512];
366 	int cib;
367 
368 	snprint(base, sizeof base, "/disk?disk=%s&type=a&arena=%s",
369 		arena->part->name, arena->name);
370 
371 	blk = vtmalloc(arena->blocksize);
372 	off = arena->base + arena->size;
373 	hprint(&c->hout, "<h2>table of contents</h2>\n");
374 	hprint(&c->hout, "<pre>\n");
375 	hprint(&c->hout, "%5s %6s %7s %s\n", "type", "size", "uncsize", "score");
376 	coff = 0;
377 	cib = hargint(c, "cib", 0);
378 
379 	for(i=0; i<arena->memstats.clumps; i++){
380 		if(i%arena->clumpmax == 0){
381 			off -= arena->blocksize;
382 			if(readpart(arena->part, off, blk, arena->blocksize) != arena->blocksize){
383 				hprint(&c->hout, "<i>clump info directory at %#llx: %r</i>\n<br>\n",
384 					off);
385 				i += arena->clumpmax-1;
386 				coff = -1;
387 				continue;
388 			}
389 		}
390 		unpackclumpinfo(&ci, blk+(i%arena->clumpmax)*ClumpInfoSize);
391 		if(i/arena->clumpmax == cib || i%arena->clumpmax == 0){
392 			hprint(&c->hout, "%5d %6d %7d %V",
393 				ci.type, ci.size, ci.uncsize, ci.score);
394 			if(coff >= 0)
395 				hprint(&c->hout, " at <a href=\"%s&clump=%#llx&score=%V\">%#llx</a>",
396 					base, coff, ci.score, coff);
397 			if(i/arena->clumpmax != cib)
398 				hprint(&c->hout, "  <font size=-1><a href=\"%s&cib=%d\">more</a></font>", base, i/arena->clumpmax);
399 			hprint(&c->hout, "\n");
400 		}
401 		if(coff >= 0)
402 			coff += ClumpSize + ci.size;
403 	}
404 	hprint(&c->hout, "</pre>\n");
405 	return 0;
406 }
407 
408 #define	U32GET(p)	((u32int)(((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3]))
409 static int
diskarenaclump(HConnect * c,Arena * arena,vlong off,char * scorestr)410 diskarenaclump(HConnect *c, Arena *arena, vlong off, char *scorestr)
411 {
412 	uchar *blk, *blk2;
413 	Clump cl;
414 	char err[ERRMAX];
415 	uchar xscore[VtScoreSize], score[VtScoreSize];
416 	Unwhack uw;
417 	int n;
418 
419 	if(scorestr){
420 		if(vtparsescore(scorestr, nil, score) < 0){
421 			hprint(&c->hout, "bad score %s: %r\n", scorestr);
422 			return -1;
423 		}
424 		if(off < 0){
425 			off = findintoc(c, arena, score);
426 			if(off < 0){
427 				hprint(&c->hout, "score %V not found in arena %s\n", score, arena->name);
428 				return -1;
429 			}
430 			hprint(&c->hout, "score %V at %#llx\n", score, off);
431 		}
432 	}else
433 		memset(score, 0, sizeof score);
434 
435 	if(off < 0){
436 		hprint(&c->hout, "bad offset %#llx\n", off);
437 		return -1;
438 	}
439 
440 	off += arena->base;
441 
442 	blk = vtmalloc(ClumpSize + VtMaxLumpSize);
443 	if(readpart(arena->part, off, blk, ClumpSize + VtMaxLumpSize) != ClumpSize + VtMaxLumpSize){
444 		hprint(&c->hout, "reading at %#llx: %r\n", off);
445 		vtfree(blk);
446 		return -1;
447 	}
448 
449 	if(unpackclump(&cl, blk, arena->clumpmagic) < 0){
450 		hprint(&c->hout, "unpackclump: %r\n<br>");
451 		rerrstr(err, sizeof err);
452 		if(strstr(err, "magic")){
453 			hprint(&c->hout, "trying again with magic=%#ux<br>\n", U32GET(blk));
454 			if(unpackclump(&cl, blk, U32GET(blk)) < 0){
455 				hprint(&c->hout, "unpackclump: %r\n<br>\n");
456 				goto error;
457 			}
458 		}else
459 			goto error;
460 	}
461 
462 	hprint(&c->hout, "<pre>type=%d size=%d uncsize=%d score=%V\n", cl.info.type, cl.info.size, cl.info.uncsize, cl.info.score);
463 	hprint(&c->hout, "encoding=%d creator=%d time=%d %s</pre>\n", cl.encoding, cl.creator, cl.time, fmttime(err, cl.time));
464 
465 	if(cl.info.type == VtCorruptType)
466 		hprint(&c->hout, "clump is marked corrupt<br>\n");
467 
468 	if(cl.info.size >= VtMaxLumpSize){
469 		hprint(&c->hout, "clump too big\n");
470 		goto error;
471 	}
472 
473 	switch(cl.encoding){
474 	case ClumpECompress:
475 		blk2 = vtmalloc(VtMaxLumpSize);
476 		unwhackinit(&uw);
477 		n = unwhack(&uw, blk2, cl.info.uncsize, blk+ClumpSize, cl.info.size);
478 		if(n < 0){
479 			hprint(&c->hout, "decompression failed\n");
480 			vtfree(blk2);
481 			goto error;
482 		}
483 		if(n != cl.info.uncsize){
484 			hprint(&c->hout, "got wrong amount: %d wanted %d\n", n, cl.info.uncsize);
485 			// hhex(blk2, n);
486 			vtfree(blk2);
487 			goto error;
488 		}
489 		scoremem(xscore, blk2, cl.info.uncsize);
490 		vtfree(blk2);
491 		break;
492 	case ClumpENone:
493 		scoremem(xscore, blk+ClumpSize, cl.info.size);
494 		break;
495 	}
496 
497 	hprint(&c->hout, "score=%V<br>\n", xscore);
498 	if(scorestr && scorecmp(score, xscore) != 0)
499 		hprint(&c->hout, "score does NOT match expected %V\n", score);
500 
501 	vtfree(blk);
502 	return 0;
503 
504 error:
505 	// hhex(blk, ClumpSize + VtMaxLumpSize);
506 	vtfree(blk);
507 	return -1;
508 }
509 
510 static int
diskbloom(HConnect * c,char * disk,Part * p)511 diskbloom(HConnect *c, char *disk, Part *p)
512 {
513 	USED(c);
514 	USED(disk);
515 	USED(p);
516 	return 0;
517 }
518 
519 static int
diskisect(HConnect * c,char * disk,Part * p)520 diskisect(HConnect *c, char *disk, Part *p)
521 {
522 	USED(c);
523 	USED(disk);
524 	USED(p);
525 	return 0;
526 }
527 
528 static void
debugamap(HConnect * c)529 debugamap(HConnect *c)
530 {
531 	int i;
532 	AMap *amap;
533 
534 	hprint(&c->hout, "<h2>arena map</h2>\n");
535 	hprint(&c->hout, "<pre>\n");
536 
537 	amap = mainindex->amap;
538 	for(i=0; i<mainindex->narenas; i++)
539 		hprint(&c->hout, "%s %#llx %#llx\n",
540 			amap[i].name, amap[i].start, amap[i].stop);
541 }
542 
543 static void
debugread(HConnect * c,u8int * score)544 debugread(HConnect *c, u8int *score)
545 {
546 	int type;
547 	Lump *u;
548 	IAddr ia;
549 	IEntry ie;
550 	int i;
551 	Arena *arena;
552 	u64int aa;
553 	ZBlock *zb;
554 	Clump cl;
555 	vlong off;
556 	u8int sc[VtScoreSize];
557 
558 	if(scorecmp(score, zeroscore) == 0){
559 		hprint(&c->hout, "zero score\n");
560 		return;
561 	}
562 
563 	hprint(&c->hout, "<h2>index search %V</h2><pre>\n", score);
564 	if(icachelookup(score, -1, &ia) < 0)
565 		hprint(&c->hout, "  icache: not found\n");
566 	else
567 		hprint(&c->hout, "  icache: addr=%#llx size=%d type=%d blocks=%d\n",
568 			ia.addr, ia.size, ia.type, ia.blocks);
569 
570 	if(loadientry(mainindex, score, -1, &ie) < 0)
571 		hprint(&c->hout, "  idisk: not found\n");
572 	else
573 		hprint(&c->hout, "  idisk: addr=%#llx size=%d type=%d blocks=%d\n",
574 			ie.ia.addr, ie.ia.size, ie.ia.type, ie.ia.blocks);
575 
576 	hprint(&c->hout, "</pre><h2>lookup %V</h2>\n", score);
577 	hprint(&c->hout, "<pre>\n");
578 
579 	for(type=0; type < VtMaxType; type++){
580 		hprint(&c->hout, "%V type %d:", score, type);
581 		u = lookuplump(score, type);
582 		if(u->data != nil)
583 			hprint(&c->hout, " +cache");
584 		else
585 			hprint(&c->hout, " -cache");
586 		putlump(u);
587 
588 		if(lookupscore(score, type, &ia) < 0){
589 			hprint(&c->hout, " -lookup\n");
590 			continue;
591 		}
592 		hprint(&c->hout, "\n  lookupscore: addr=%#llx size=%d blocks=%d\n",
593 			ia.addr, ia.size, ia.blocks);
594 
595 		arena = amapitoa(mainindex, ia.addr, &aa);
596 		if(arena == nil){
597 			hprint(&c->hout, "  amapitoa failed: %r\n");
598 			continue;
599 		}
600 
601 		hprint(&c->hout, "  amapitoa: aa=%#llx arena="
602 			"<a href=\"/disk?disk=%s&type=a&arena=%s&score=%V\">%s</a>\n",
603 			aa, arena->part->name, arena->name, score, arena->name);
604 		zb = loadclump(arena, aa, ia.blocks, &cl, sc, 1);
605 		if(zb == nil){
606 			hprint(&c->hout, "  loadclump failed: %r\n");
607 			continue;
608 		}
609 
610 		hprint(&c->hout, "  loadclump: uncsize=%d type=%d score=%V\n",
611 			cl.info.uncsize, cl.info.type, sc);
612 		if(ia.size != cl.info.uncsize || ia.type != cl.info.type || scorecmp(score, sc) != 0){
613 			hprint(&c->hout, "    clump info mismatch\n");
614 			continue;
615 		}
616 	}
617 
618 	if(hargstr(c, "brute", "")[0] == 'y'){
619 		hprint(&c->hout, "</pre>\n");
620 		hprint(&c->hout, "<h2>brute force arena search %V</h2>\n", score);
621 		hprint(&c->hout, "<pre>\n");
622 
623 		for(i=0; i<mainindex->narenas; i++){
624 			arena = mainindex->arenas[i];
625 			hprint(&c->hout, "%s...\n", arena->name);
626 			hflush(&c->hout);
627 			off = findintoc(nil, arena, score);
628 			if(off >= 0)
629 				hprint(&c->hout, "%s %#llx (%#llx)\n", arena->name, off, mainindex->amap[i].start + off);
630 		}
631 	}
632 
633 	hprint(&c->hout, "</pre>\n");
634 }
635 
636 static void
debugmem(HConnect * c)637 debugmem(HConnect *c)
638 {
639 	Index *ix;
640 
641 	ix = mainindex;
642 	hprint(&c->hout, "<h2>memory</h2>\n");
643 
644 	hprint(&c->hout, "<pre>\n");
645 	hprint(&c->hout, "ix=%p\n", ix);
646 	hprint(&c->hout, "\tarenas=%p\n", ix->arenas);
647 	if(ix->narenas > 0)
648 		hprint(&c->hout, "\tarenas[...] = %p...%p\n", ix->arenas[0], ix->arenas[ix->narenas-1]);
649 	hprint(&c->hout, "\tsmap=%p\n", ix->smap);
650 	hprint(&c->hout, "\tamap=%p\n", ix->amap);
651 	hprint(&c->hout, "\tbloom=%p\n", ix->bloom);
652 	hprint(&c->hout, "\tbloom->data=%p\n", ix->bloom ? ix->bloom->data : nil);
653 	hprint(&c->hout, "\tisects=%p\n", ix->sects);
654 	if(ix->nsects > 0)
655 		hprint(&c->hout, "\tsects[...] = %p...%p\n", ix->sects[0], ix->sects[ix->nsects-1]);
656 }
657 
658 int
hdebug(HConnect * c)659 hdebug(HConnect *c)
660 {
661 	char *scorestr, *op;
662 	u8int score[VtScoreSize];
663 
664 	if(hsethtml(c) < 0)
665 		return -1;
666 	hprint(&c->hout, "<h1>venti debug</h1>\n");
667 
668 	op = hargstr(c, "op", "");
669 	if(!op[0]){
670 		hprint(&c->hout, "no op\n");
671 		return 0;
672 	}
673 
674 	if(strcmp(op, "amap") == 0){
675 		debugamap(c);
676 		return 0;
677 	}
678 
679 	if(strcmp(op, "mem") == 0){
680 		debugmem(c);
681 		return 0;
682 	}
683 
684 	if(strcmp(op, "read") == 0){
685 		scorestr = hargstr(c, "score", "");
686 		if(vtparsescore(scorestr, nil, score) < 0){
687 			hprint(&c->hout, "bad score %s: %r\n", scorestr);
688 			return 0;
689 		}
690 		debugread(c, score);
691 		return 0;
692 	}
693 
694 	hprint(&c->hout, "unknown op %s", op);
695 	return 0;
696 }
697