ref: 875ae911d2e652816ff8fb91ab252dee9e342be8
parent: 8a309819457c3d0b1a248eac4d6effb1e989423d
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Sep 24 09:27:54 EDT 2025
gefs: dump block state on assertion failures with luck, this can lead to better bug reports.
--- a/sys/src/cmd/gefs/blk.c
+++ b/sys/src/cmd/gefs/blk.c
@@ -1,5 +1,6 @@
#include <u.h>
#include <libc.h>
+#include <bio.h>
#include <fcall.h>
#include <avl.h>
@@ -37,8 +38,8 @@
void
syncblk(Blk *b)
{- assert(checkflag(b, Bfinal, 0));
- assert(b->bp.addr >= 0);
+ bassert(b, checkflag(b, Bfinal, 0));
+ bassert(b, b->bp.addr >= 0);
tracex("syncblk", b->bp, b->type, -1);if(pwrite(fs->fd, b->buf, Blksz, b->bp.addr) == -1)
broke("%B %s: %r", b->bp, Eio);@@ -92,7 +93,7 @@
b->logsz = UNPACK16(p); p += 2;
b->logh = UNPACK64(p); p += 8;
b->logp = unpackbp(p, Ptrsz); p += Ptrsz;
- assert(p - b->buf == Loghdsz);
+ bassert(b, p - b->buf == Loghdsz);
b->data = p;
break;
case Tpivot:
@@ -100,13 +101,13 @@
b->valsz = UNPACK16(p); p += 2;
b->nbuf = UNPACK16(p); p += 2;
b->bufsz = UNPACK16(p); p += 2;
- assert(p - b->buf == Pivhdsz);
+ bassert(b, p - b->buf == Pivhdsz);
b->data = p;
break;
case Tleaf:
b->nval = UNPACK16(p); p += 2;
b->valsz = UNPACK16(p); p += 2;
- assert(p - b->buf == Leafhdsz);
+ bassert(b, p - b->buf == Leafhdsz);
b->data = p;
break;
}
@@ -119,7 +120,7 @@
}
if((!flg&GBnochk) && ck != xh)
broke("%s: %ullx %llux != %llux", Ecorrupt, bp.addr, xh, ck);- assert(b->magic == Magic);
+ bassert(b, b->magic == Magic);
}
static Arena*
@@ -244,7 +245,7 @@
lb = a->logbuf[0];
if(lb == a->logtl)
lb = a->logbuf[1];
- assert(agetl(&lb->ref) == 1);
+ bassert(lb, agetl(&lb->ref) == 1);
aswapl(&lb->flag, Bstatic);
initblk(lb, o, -1, Tlog);
traceb("logblk" , lb->bp);@@ -276,9 +277,9 @@
assert(off < end);
}
lb = a->logtl;
- assert(agetl(&lb->ref) > 0);
- assert(lb->type == Tlog);
- assert(lb->logsz >= 0);
+ bassert(lb, agetl(&lb->ref) > 0);
+ bassert(lb, lb->type == Tlog);
+ bassert(lb, lb->logsz >= 0);
dprint("logop %d: %llx+%llx@%x\n", op, off, len, lb->logsz);if(checkflag(lb, 0, Bdirty))
@@ -341,7 +342,7 @@
traceb("loadlog", bp);b = a->logbuf[0];
while(1){- assert(checkflag(b, Bstatic, Bcached));
+ bassert(b, checkflag(b, Bstatic, Bcached));
holdblk(b);
readblk(b, bp, 0);
dprint("\tload %B chain %B\n", bp, b->logp);@@ -736,8 +737,8 @@
nexterror();
}
if((b = cacheget(bp.addr)) != nil){- assert(checkflag(b, 0, Bfreed));
- assert(b->bp.gen == bp.gen);
+ bassert(b, checkflag(b, 0, Bfreed));
+ bassert(b, b->bp.gen == bp.gen);
} else {b = cachepluck();
b->alloced = getcallerpc(&bp);
@@ -823,7 +824,7 @@
tracex("freeb", b->bp, getcallerpc(&t), -1);setflag(b, Blimbo, 0);
holdblk(b);
- assert(agetl(&b->ref) > 1);
+ bassert(b, agetl(&b->ref) > 1);
limbo(DFblk, b);
}
@@ -966,8 +967,8 @@
Arena *a;
Qent qe;
- assert(checkflag(b, Bdirty, Bqueued|Bstatic));
- assert(b->bp.addr >= 0);
+ bassert(b, checkflag(b, Bdirty, Bqueued|Bstatic));
+ bassert(b, b->bp.addr >= 0);
finalize(b);
if(checkflag(b, 0, Bcached)){cacheins(b);
@@ -1018,7 +1019,7 @@
else
abort();
if(qe.b != nil)
- assert(agetl(&qe.b->ref) > 0);
+ bassert(qe.b, agetl(&qe.b->ref) > 0);
qlock(&q->lk);
qe.qgen = agetv(&fs->qgen);
while(q->nheap == q->heapsz)
--- a/sys/src/cmd/gefs/cache.c
+++ b/sys/src/cmd/gefs/cache.c
@@ -31,8 +31,8 @@
* to put this into the LRU, because
* its now in use.
*/
- assert(b->magic == Magic);
- assert(checkflag(b, 0, Bstatic));
+ bassert(b, b->magic == Magic);
+ bassert(b, checkflag(b, 0, Bstatic));
if(agetl(&b->ref) != 0){qunlock(&fs->lrulk);
return;
@@ -58,8 +58,8 @@
* to put this into the LRU, because
* its now in use.
*/
- assert(b->magic == Magic);
- assert(checkflag(b, 0, Bstatic));
+ bassert(b, b->magic == Magic);
+ bassert(b, checkflag(b, 0, Bstatic));
if(agetl(&b->ref) != 0){qunlock(&fs->lrulk);
return;
@@ -81,16 +81,16 @@
Bucket *bkt;
u32int h;
- assert(b->magic == Magic);
+ bassert(b, b->magic == Magic);
h = ihash(b->bp.addr);
bkt = &fs->bcache[h % fs->cmax];
qlock(&fs->lrulk);
traceb("cache", b->bp);- assert(checkflag(b, 0, Bstatic|Bcached));
+ bassert(b, checkflag(b, 0, Bstatic|Bcached));
setflag(b, Bcached, 0);
- assert(b->hnext == nil);
+ bassert(b, b->hnext == nil);
for(Blk *bb = bkt->b; bb != nil; bb = bb->hnext)
- assert(b != bb && b->bp.addr != bb->bp.addr);
+ bassert(b, b != bb && b->bp.addr != bb->bp.addr);
b->cached = getcallerpc(&b);
b->hnext = bkt->b;
bkt->b = b;
@@ -115,7 +115,7 @@
for(b = bkt->b; b != nil; b = b->hnext){ if(b->bp.addr == addr){/* FIXME: Until we clean up snap.c, we can have dirty blocks in cache */
- assert(checkflag(b, Bcached, Bstatic)); //Bdirty));
+ bassert(b, checkflag(b, Bcached, Bstatic)); //Bdirty));
*p = b->hnext;
b->uncached = getcallerpc(&addr);
b->hnext = nil;
@@ -171,13 +171,13 @@
rsleep(&fs->lrurz);
b = fs->ctail;
- assert(b->magic == Magic);
- assert(agetl(&b->ref) == 0);
+ bassert(b, b->magic == Magic);
+ bassert(b, agetl(&b->ref) == 0);
if(checkflag(b, Bcached, 0))
cachedel_lk(b->bp.addr);
if(checkflag(b, Bcached, 0))
fprint(2, "%B cached %#p freed %#p\n", b->bp, b->cached, b->freed);
- assert(checkflag(b, 0, Bcached));
+ bassert(b, checkflag(b, 0, Bcached));
lrudel(b);
aswapl(&b->flag, 0);
b->lasthold = 0;
--- a/sys/src/cmd/gefs/fns.h
+++ b/sys/src/cmd/gefs/fns.h
@@ -128,23 +128,19 @@
void showtreeroot(int, Tree*);
int checkfs(int);
+
#define dprint(...) \
- do{ \- if(debug) fprint(2, __VA_ARGS__); \
- }while(0)
+ if(debug) fprint(2, __VA_ARGS__)
#define fatal(...) \
- do{ \- fprint(2, __VA_ARGS__); \
- abort(); \
- }while(0)
+ fprint(2, __VA_ARGS__), abort()
#define tracex(msg, bp, v0, v1) \
- do{ \- if(fs->trace != nil) \
- _trace(msg, bp, v0, v1); \
- } while(0)
+ if(fs->trace != nil) _trace(msg, bp, v0, v1)
+#define bassert(b, cond) \
+ if(cond){}else _babort(b->bp.addr, "cond")+
#define traceb(msg, bp) tracex(msg, bp, -1, -1)
#define tracev(msg, v0) tracex(msg, Zb, v0, -1)
#define tracem(msg) tracex(msg, Zb, -1, -1)
@@ -153,10 +149,12 @@
_Noreturn void error(char*, ...);
_Noreturn void broke(char*, ...);
_Noreturn void nexterror(void);
+_Noreturn void _babort(vlong, char*);
#define waserror() (setjmp(*_waserror()))
#define errmsg() ((*errctx)->err)
#define poperror() assert((*errctx)->nerrlab-- > 0)
#define estacksz() ((*errctx)->nerrlab)
+void writetrace(void*, vlong);
void _trace(char*, Bptr, vlong, vlong);
char* packstr(char*, char*, char*);
@@ -210,4 +208,4 @@
void runtasks(int, void*);
void runsync(int, void*);
void runsweep(int, void*);
-void runsweep(int, void*);
+void runsnap(int, void*);
--- a/sys/src/cmd/gefs/main.c
+++ b/sys/src/cmd/gefs/main.c
@@ -31,6 +31,43 @@
Errctx **errctx;
void
+writetrace(void *bfd, vlong addr)
+{+ Trace *t;
+ long ti;
+ int i;
+
+ ti = agetl(&fs->traceidx);
+ for(i = 0; i < fs->ntrace; i++){+ t = &fs->trace[(ti+ i) % fs->ntrace];
+ if(t->msg[0] == 0)
+ continue;
+ if(t->bp.addr != -1 && t->bp.addr != addr)
+ continue;
+ Bprint(bfd, "[%d@%d] %s", t->tid, t->qgen, t->msg);
+ if(t->bp.addr != -1)
+ Bprint(bfd, " %B", t->bp);
+ if(t->v0 != -1)
+ Bprint(bfd, " %llx", t->v0);
+ if(t->v1 != -1)
+ Bprint(bfd, " %llx", t->v1);
+ Bprint(bfd, "\n");
+ }
+}
+
+_Noreturn void
+_babort(vlong addr, char *msg)
+{+ Biobuf *bfd;
+
+ bfd = Bfdopen(2, OWRITE);
+ writetrace(bfd, addr);
+ Bprint(bfd, "assert failed for [%llx]: %s\n", addr, msg);
+ Bterm(bfd);
+ abort();
+}
+
+void
_trace(char *msg, Bptr bp, vlong v0, vlong v1)
{Trace *t;
--- a/sys/src/cmd/gefs/tree.c
+++ b/sys/src/cmd/gefs/tree.c
@@ -104,7 +104,7 @@
char *p;
int o;
- assert(i >= 0 && i < b->nval);
+ bassert(b, i >= 0 && i < b->nval);
p = b->data + 2*i;
o = UNPACK16(p); p = b->data + o;
kv->nk = UNPACK16(p); p += 2;
@@ -133,8 +133,8 @@
off = spc - b->valsz;
if(kv->k[0] == Kent) assert(kv->nv != 0);
- assert(2*(b->nval+1) + b->valsz <= spc);
- assert(2*(b->nval+1) <= off);
+ bassert(b, 2*(b->nval+1) + b->valsz <= spc);
+ bassert(b, 2*(b->nval+1) <= off);
p = b->data + 2*b->nval;
PACK16(p, off);
@@ -169,7 +169,7 @@
char *p;
int o;
- assert(b->type == Tpivot);
+ bassert(b, b->type == Tpivot);
b->bufsz += msgsz(m)-2;
p = b->data + Pivspc + 2*b->nbuf;
@@ -192,8 +192,8 @@
char *p;
int o;
- assert(b->type == Tpivot);
- assert(i >= 0 && i < b->nbuf);
+ bassert(b, b->type == Tpivot);
+ bassert(b, i >= 0 && i < b->nbuf);
p = b->data + Pivspc + 2*i;
o = UNPACK16(p);
p = b->data + Pivspc + o;
@@ -284,7 +284,7 @@
static int
buffill(Blk *b)
{- assert(b->type == Tpivot);
+ bassert(b, b->type == Tpivot);
return 2*b->nbuf + b->bufsz;
}
@@ -291,7 +291,7 @@
static int
filledbuf(Blk *b, int nmsg, int needed)
{- assert(b->type == Tpivot);
+ bassert(b, b->type == Tpivot);
return 2*(b->nbuf+nmsg) + b->bufsz + needed > Bufspc;
}
@@ -298,7 +298,7 @@
static int
filledleaf(Blk *b, int needed)
{- assert(b->type == Tleaf);
+ bassert(b, b->type == Tleaf);
return 2*(b->nval+1) + b->valsz + needed > Leafspc;
}
@@ -310,7 +310,7 @@
* at all times, so that splits along the whole path
* have somewhere to go as they propagate up.
*/
- assert(b->type == Tpivot);
+ bassert(b, b->type == Tpivot);
return 2*(b->nval+1) + b->valsz + reserve*Kpmax > Pivspc;
}
@@ -435,7 +435,6 @@
default:
fatal("invalid op %d\n", m->op);}
- return 0;
}
static Blk*
@@ -707,7 +706,7 @@
if(halfsz > Leafspc/2)
halfsz = Leafspc/2;
spc = Leafspc - (halfsz + Msgmax);
- assert(b->nval >= 4);
+ bassert(b, b->nval >= 4);
while(i < b->nval){/*
* We're trying to balance size,
@@ -815,7 +814,7 @@
d = l;
copied = 0;
halfsz = (2*b->nval + b->valsz)/2;
- assert(b->nval >= 4);
+ bassert(b, b->nval >= 4);
for(i = 0; i < b->nval; i++){/*
* We're trying to balance size,
@@ -1000,7 +999,7 @@
{int na, nb, ma, mb, imbalance;
- assert(a->type == b->type);
+ bassert(a, a->type == b->type);
na = 2*a->nval + a->valsz;
nb = 2*b->nval + b->valsz;
@@ -1327,8 +1326,8 @@
else
fatal("broken path change");- assert(rb->bp.addr != 0);
- assert(rb->bp.addr != 0);
+ bassert(rb, rb->bp.addr != 0);
+ bassert(rb, rb->bp.addr != 0);
lock(&t->lk);
traceb("setroot", rb->bp);--
⑨