1 /*-
2 * Copyright (c) 2007-2015 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32 #include "config.h"
33
34 #include "cache/cache_varnishd.h"
35
36 #include "cache/cache_obj.h"
37 #include "cache/cache_objhead.h"
38
39 #include "storage/storage.h"
40 #include "storage/storage_simple.h"
41
42 #include "vtim.h"
43
44 /* Flags for allocating memory in sml_stv_alloc */
45 #define LESS_MEM_ALLOCED_IS_OK 1
46
47 /*-------------------------------------------------------------------*/
48
49 static struct storage *
sml_stv_alloc(const struct stevedore * stv,ssize_t size,int flags)50 sml_stv_alloc(const struct stevedore *stv, ssize_t size, int flags)
51 {
52 struct storage *st;
53
54 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
55 AN(stv->sml_alloc);
56
57 if (!(flags & LESS_MEM_ALLOCED_IS_OK)) {
58 if (size > cache_param->fetch_maxchunksize)
59 return (NULL);
60 else
61 return (stv->sml_alloc(stv, size));
62 }
63
64 if (size > cache_param->fetch_maxchunksize)
65 size = cache_param->fetch_maxchunksize;
66
67 assert(size <= UINT_MAX); /* field limit in struct storage */
68
69 for (;;) {
70 /* try to allocate from it */
71 assert(size > 0);
72 st = stv->sml_alloc(stv, size);
73 if (st != NULL)
74 break;
75
76 if (size <= cache_param->fetch_chunksize)
77 break;
78
79 size /= 2;
80 }
81 CHECK_OBJ_ORNULL(st, STORAGE_MAGIC);
82 return (st);
83 }
84
85 static void
sml_stv_free(const struct stevedore * stv,struct storage * st)86 sml_stv_free(const struct stevedore *stv, struct storage *st)
87 {
88
89 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
90 CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
91 if (stv->sml_free != NULL)
92 stv->sml_free(st);
93 }
94
95 /*--------------------------------------------------------------------
96 * This function is called by stevedores ->allocobj() method, which
97 * very often will be SML_allocobj() below, to convert a slab
98 * of storage into object which the stevedore can then register in its
99 * internal state, before returning it to STV_NewObject().
100 * As you probably guessed: All this for persistence.
101 */
102
103 struct object *
SML_MkObject(const struct stevedore * stv,struct objcore * oc,void * ptr)104 SML_MkObject(const struct stevedore *stv, struct objcore *oc, void *ptr)
105 {
106 struct object *o;
107
108 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
109 AN(stv->methods);
110 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
111
112 assert(PAOK(ptr));
113
114 o = ptr;
115 INIT_OBJ(o, OBJECT_MAGIC);
116
117 VTAILQ_INIT(&o->list);
118
119 oc->stobj->stevedore = stv;
120 oc->stobj->priv = o;
121 oc->stobj->priv2 = 0;
122 return (o);
123 }
124
125 /*--------------------------------------------------------------------
126 * This is the default ->allocobj() which all stevedores who do not
127 * implement persistent storage can rely on.
128 */
129
v_matchproto_(storage_allocobj_f)130 int v_matchproto_(storage_allocobj_f)
131 SML_allocobj(struct worker *wrk, const struct stevedore *stv,
132 struct objcore *oc, unsigned wsl)
133 {
134 struct object *o;
135 struct storage *st = NULL;
136 unsigned ltot;
137
138 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
139 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
140 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
141
142 AN(stv->sml_alloc);
143
144 ltot = sizeof(struct object) + PRNDUP(wsl);
145
146 do {
147 st = stv->sml_alloc(stv, ltot);
148 if (st != NULL && st->space < ltot) {
149 stv->sml_free(st);
150 st = NULL;
151 }
152 } while (st == NULL && LRU_NukeOne(wrk, stv->lru));
153 if (st == NULL)
154 return (0);
155
156 CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
157 o = SML_MkObject(stv, oc, st->ptr);
158 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
159 st->len = sizeof(*o);
160 o->objstore = st;
161 return (1);
162 }
163
164 /*---------------------------------------------------------------------
165 */
166
167 static struct object *
sml_getobj(struct worker * wrk,struct objcore * oc)168 sml_getobj(struct worker *wrk, struct objcore *oc)
169 {
170 const struct stevedore *stv;
171 struct object *o;
172
173 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
174 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
175 stv = oc->stobj->stevedore;
176 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
177 if (stv->sml_getobj != NULL)
178 return (stv->sml_getobj(wrk, oc));
179 if (oc->stobj->priv == NULL)
180 return (NULL);
181 CAST_OBJ_NOTNULL(o, oc->stobj->priv, OBJECT_MAGIC);
182 return (o);
183 }
184
v_matchproto_(objslim_f)185 static void v_matchproto_(objslim_f)
186 sml_slim(struct worker *wrk, struct objcore *oc)
187 {
188 const struct stevedore *stv;
189 struct object *o;
190 struct storage *st, *stn;
191
192 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
193
194 stv = oc->stobj->stevedore;
195 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
196 o = sml_getobj(wrk, oc);
197 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
198
199 #define OBJ_AUXATTR(U, l) \
200 if (o->aa_##l != NULL) { \
201 sml_stv_free(stv, o->aa_##l); \
202 o->aa_##l = NULL; \
203 }
204 #include "tbl/obj_attr.h"
205
206 VTAILQ_FOREACH_SAFE(st, &o->list, list, stn) {
207 CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
208 VTAILQ_REMOVE(&o->list, st, list);
209 sml_stv_free(stv, st);
210 }
211 }
212
v_matchproto_(objfree_f)213 static void v_matchproto_(objfree_f)
214 sml_objfree(struct worker *wrk, struct objcore *oc)
215 {
216 struct object *o;
217
218 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
219 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
220 sml_slim(wrk, oc);
221 CAST_OBJ_NOTNULL(o, oc->stobj->priv, OBJECT_MAGIC);
222 o->magic = 0;
223
224 if (oc->boc == NULL && oc->stobj->stevedore->lru != NULL)
225 LRU_Remove(oc);
226
227 sml_stv_free(oc->stobj->stevedore, o->objstore);
228
229 memset(oc->stobj, 0, sizeof oc->stobj);
230
231 wrk->stats->n_object--;
232 }
233
v_matchproto_(objiterate_f)234 static int v_matchproto_(objiterate_f)
235 sml_iterator(struct worker *wrk, struct objcore *oc,
236 void *priv, objiterate_f *func, int final)
237 {
238 struct boc *boc;
239 enum boc_state_e state;
240 struct object *obj;
241 struct storage *st;
242 struct storage *checkpoint = NULL;
243 const struct stevedore *stv;
244 ssize_t checkpoint_len = 0;
245 ssize_t len = 0;
246 int ret = 0, ret2;
247 ssize_t ol;
248 ssize_t nl;
249 ssize_t sl;
250 void *p;
251 ssize_t l;
252 unsigned u;
253
254 obj = sml_getobj(wrk, oc);
255 CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
256 stv = oc->stobj->stevedore;
257 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
258
259 boc = HSH_RefBoc(oc);
260
261 if (boc == NULL) {
262 VTAILQ_FOREACH_SAFE(st, &obj->list, list, checkpoint) {
263 u = 0;
264 if (VTAILQ_NEXT(st, list) == NULL)
265 u |= OBJ_ITER_END;
266 if (final)
267 u |= OBJ_ITER_FLUSH;
268 if (ret == 0 && st->len > 0)
269 ret = func(priv, u, st->ptr, st->len);
270 if (final) {
271 VTAILQ_REMOVE(&obj->list, st, list);
272 sml_stv_free(stv, st);
273 } else if (ret)
274 break;
275 }
276 return (ret);
277 }
278
279 p = NULL;
280 l = 0;
281
282 u = 0;
283 while (1) {
284 ol = len;
285 nl = ObjWaitExtend(wrk, oc, ol);
286 if (boc->state == BOS_FAILED) {
287 ret = -1;
288 break;
289 }
290 if (nl == ol) {
291 if (boc->state == BOS_FINISHED)
292 break;
293 continue;
294 }
295 Lck_Lock(&boc->mtx);
296 AZ(VTAILQ_EMPTY(&obj->list));
297 if (checkpoint == NULL) {
298 st = VTAILQ_FIRST(&obj->list);
299 sl = 0;
300 } else {
301 st = checkpoint;
302 sl = checkpoint_len;
303 ol -= checkpoint_len;
304 }
305 while (st != NULL) {
306 if (st->len > ol) {
307 p = st->ptr + ol;
308 l = st->len - ol;
309 len += l;
310 break;
311 }
312 ol -= st->len;
313 assert(ol >= 0);
314 nl -= st->len;
315 assert(nl > 0);
316 sl += st->len;
317 st = VTAILQ_NEXT(st, list);
318 if (VTAILQ_NEXT(st, list) != NULL) {
319 if (final && checkpoint != NULL) {
320 VTAILQ_REMOVE(&obj->list,
321 checkpoint, list);
322 sml_stv_free(stv, checkpoint);
323 }
324 checkpoint = st;
325 checkpoint_len = sl;
326 }
327 }
328 CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
329 CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
330 st = VTAILQ_NEXT(st, list);
331 if (st != NULL && st->len == 0)
332 st = NULL;
333 state = boc->state;
334 Lck_Unlock(&boc->mtx);
335 assert(l > 0 || state == BOS_FINISHED);
336 u = 0;
337 if (st == NULL || final)
338 u |= OBJ_ITER_FLUSH;
339 if (st == NULL && state == BOS_FINISHED)
340 u |= OBJ_ITER_END;
341 ret = func(priv, u, p, l);
342 if (ret)
343 break;
344 }
345 HSH_DerefBoc(wrk, oc);
346 if ((u & OBJ_ITER_END) == 0) {
347 ret2 = func(priv, OBJ_ITER_END, NULL, 0);
348 if (ret == 0)
349 ret = ret2;
350 }
351 return (ret);
352 }
353
354 /*--------------------------------------------------------------------
355 */
356
357 static struct storage *
objallocwithnuke(struct worker * wrk,const struct stevedore * stv,ssize_t size,int flags)358 objallocwithnuke(struct worker *wrk, const struct stevedore *stv, ssize_t size,
359 int flags)
360 {
361 struct storage *st = NULL;
362
363 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
364 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
365
366 if (size > cache_param->fetch_maxchunksize) {
367 if (!(flags & LESS_MEM_ALLOCED_IS_OK))
368 return (NULL);
369 size = cache_param->fetch_maxchunksize;
370 }
371
372 assert(size <= UINT_MAX); /* field limit in struct storage */
373
374 do {
375 /* try to allocate from it */
376 st = sml_stv_alloc(stv, size, flags);
377 if (st != NULL)
378 break;
379
380 /* no luck; try to free some space and keep trying */
381 if (stv->lru == NULL)
382 break;
383 } while (LRU_NukeOne(wrk, stv->lru));
384
385 CHECK_OBJ_ORNULL(st, STORAGE_MAGIC);
386 return (st);
387 }
388
v_matchproto_(objgetspace_f)389 static int v_matchproto_(objgetspace_f)
390 sml_getspace(struct worker *wrk, struct objcore *oc, ssize_t *sz,
391 uint8_t **ptr)
392 {
393 struct object *o;
394 struct storage *st;
395
396 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
397 AN(sz);
398 AN(ptr);
399 assert(*sz > 0);
400
401 o = sml_getobj(wrk, oc);
402 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
403 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
404
405 st = VTAILQ_LAST(&o->list, storagehead);
406 if (st != NULL && st->len < st->space) {
407 *sz = st->space - st->len;
408 *ptr = st->ptr + st->len;
409 assert (*sz > 0);
410 return (1);
411 }
412
413 st = objallocwithnuke(wrk, oc->stobj->stevedore, *sz,
414 LESS_MEM_ALLOCED_IS_OK);
415 if (st == NULL)
416 return (0);
417
418 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
419 Lck_Lock(&oc->boc->mtx);
420 VTAILQ_INSERT_TAIL(&o->list, st, list);
421 Lck_Unlock(&oc->boc->mtx);
422
423 *sz = st->space - st->len;
424 assert (*sz > 0);
425 *ptr = st->ptr + st->len;
426 return (1);
427 }
428
v_matchproto_(objextend_f)429 static void v_matchproto_(objextend_f)
430 sml_extend(struct worker *wrk, struct objcore *oc, ssize_t l)
431 {
432 struct object *o;
433 struct storage *st;
434
435 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
436 assert(l > 0);
437
438 o = sml_getobj(wrk, oc);
439 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
440 st = VTAILQ_LAST(&o->list, storagehead);
441 CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
442 assert(st->len + l <= st->space);
443 st->len += l;
444 }
445
v_matchproto_(objtrimstore_f)446 static void v_matchproto_(objtrimstore_f)
447 sml_trimstore(struct worker *wrk, struct objcore *oc)
448 {
449 const struct stevedore *stv;
450 struct storage *st, *st1;
451 struct object *o;
452
453 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
454 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
455 CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
456
457 stv = oc->stobj->stevedore;
458 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
459
460 if (stv->sml_free == NULL)
461 return;
462
463 o = sml_getobj(wrk, oc);
464 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
465 st = VTAILQ_LAST(&o->list, storagehead);
466
467 if (st == NULL)
468 return;
469
470 if (st->len == 0) {
471 Lck_Lock(&oc->boc->mtx);
472 VTAILQ_REMOVE(&o->list, st, list);
473 Lck_Unlock(&oc->boc->mtx);
474 sml_stv_free(stv, st);
475 return;
476 }
477
478 if (st->space - st->len < 512)
479 return;
480
481 st1 = sml_stv_alloc(stv, st->len, 0);
482 if (st1 == NULL)
483 return;
484 assert(st1->space >= st->len);
485
486 memcpy(st1->ptr, st->ptr, st->len);
487 st1->len = st->len;
488 Lck_Lock(&oc->boc->mtx);
489 VTAILQ_REMOVE(&o->list, st, list);
490 VTAILQ_INSERT_TAIL(&o->list, st1, list);
491 Lck_Unlock(&oc->boc->mtx);
492 /* sml_bocdone frees this */
493 AZ(oc->boc->stevedore_priv);
494 oc->boc->stevedore_priv = st;
495 }
496
v_matchproto_(objbocdone_f)497 static void v_matchproto_(objbocdone_f)
498 sml_bocdone(struct worker *wrk, struct objcore *oc, struct boc *boc)
499 {
500 const struct stevedore *stv;
501 struct storage *st;
502
503 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
504 CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
505 CHECK_OBJ_NOTNULL(boc, BOC_MAGIC);
506 stv = oc->stobj->stevedore;
507 CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
508
509 if (boc->stevedore_priv != NULL) {
510 /* Free any leftovers from Trim */
511 CAST_OBJ_NOTNULL(st, boc->stevedore_priv, STORAGE_MAGIC);
512 boc->stevedore_priv = NULL;
513 sml_stv_free(stv, st);
514 }
515
516 if (stv->lru != NULL) {
517 if (isnan(wrk->lastused))
518 wrk->lastused = VTIM_real();
519 LRU_Add(oc, wrk->lastused); // approx timestamp is OK
520 }
521 }
522
v_matchproto_(objgetattr_f)523 static const void * v_matchproto_(objgetattr_f)
524 sml_getattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr,
525 ssize_t *len)
526 {
527 struct object *o;
528 ssize_t dummy;
529
530 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
531
532 if (len == NULL)
533 len = &dummy;
534 o = sml_getobj(wrk, oc);
535 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
536
537 switch (attr) {
538 /* Fixed size attributes */
539 #define OBJ_FIXATTR(U, l, s) \
540 case OA_##U: \
541 *len = sizeof o->fa_##l; \
542 return (o->fa_##l);
543 #include "tbl/obj_attr.h"
544
545 /* Variable size attributes */
546 #define OBJ_VARATTR(U, l) \
547 case OA_##U: \
548 if (o->va_##l == NULL) \
549 return (NULL); \
550 *len = o->va_##l##_len; \
551 return (o->va_##l);
552 #include "tbl/obj_attr.h"
553
554 /* Auxiliary attributes */
555 #define OBJ_AUXATTR(U, l) \
556 case OA_##U: \
557 if (o->aa_##l == NULL) \
558 return (NULL); \
559 CHECK_OBJ_NOTNULL(o->aa_##l, STORAGE_MAGIC); \
560 *len = o->aa_##l->len; \
561 return (o->aa_##l->ptr);
562 #include "tbl/obj_attr.h"
563
564 default:
565 break;
566 }
567 WRONG("Unsupported OBJ_ATTR");
568 }
569
v_matchproto_(objsetattr_f)570 static void * v_matchproto_(objsetattr_f)
571 sml_setattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr,
572 ssize_t len, const void *ptr)
573 {
574 struct object *o;
575 void *retval = NULL;
576 struct storage *st;
577
578 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
579
580 o = sml_getobj(wrk, oc);
581 CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
582 st = o->objstore;
583
584 switch (attr) {
585 /* Fixed size attributes */
586 #define OBJ_FIXATTR(U, l, s) \
587 case OA_##U: \
588 assert(len == sizeof o->fa_##l); \
589 retval = o->fa_##l; \
590 break;
591 #include "tbl/obj_attr.h"
592
593 /* Variable size attributes */
594 #define OBJ_VARATTR(U, l) \
595 case OA_##U: \
596 if (o->va_##l##_len > 0) { \
597 AN(o->va_##l); \
598 assert(len == o->va_##l##_len); \
599 retval = o->va_##l; \
600 } else if (len > 0) { \
601 assert(len <= UINT_MAX); \
602 assert(st->len + len <= st->space); \
603 o->va_##l = st->ptr + st->len; \
604 st->len += len; \
605 o->va_##l##_len = len; \
606 retval = o->va_##l; \
607 } \
608 break;
609 #include "tbl/obj_attr.h"
610
611 /* Auxiliary attributes */
612 #define OBJ_AUXATTR(U, l) \
613 case OA_##U: \
614 if (o->aa_##l != NULL) { \
615 CHECK_OBJ_NOTNULL(o->aa_##l, STORAGE_MAGIC); \
616 assert(len == o->aa_##l->len); \
617 retval = o->aa_##l->ptr; \
618 break; \
619 } \
620 if (len == 0) \
621 break; \
622 o->aa_##l = objallocwithnuke(wrk, oc->stobj->stevedore, \
623 len, 0); \
624 if (o->aa_##l == NULL) \
625 break; \
626 CHECK_OBJ_NOTNULL(o->aa_##l, STORAGE_MAGIC); \
627 assert(len <= o->aa_##l->space); \
628 o->aa_##l->len = len; \
629 retval = o->aa_##l->ptr; \
630 break;
631 #include "tbl/obj_attr.h"
632
633 default:
634 WRONG("Unsupported OBJ_ATTR");
635 break;
636 }
637
638 if (retval != NULL && ptr != NULL)
639 memcpy(retval, ptr, len);
640 return (retval);
641 }
642
643 const struct obj_methods SML_methods = {
644 .objfree = sml_objfree,
645 .objiterator = sml_iterator,
646 .objgetspace = sml_getspace,
647 .objextend = sml_extend,
648 .objtrimstore = sml_trimstore,
649 .objbocdone = sml_bocdone,
650 .objslim = sml_slim,
651 .objgetattr = sml_getattr,
652 .objsetattr = sml_setattr,
653 .objtouch = LRU_Touch,
654 };
655
656 static void
sml_panic_st(struct vsb * vsb,const char * hd,const struct storage * st)657 sml_panic_st(struct vsb *vsb, const char *hd, const struct storage *st)
658 {
659 VSB_printf(vsb, "%s = %p {priv=%p, ptr=%p, len=%u, space=%u},\n",
660 hd, st, st->priv, st->ptr, st->len, st->space);
661 }
662
663 void
SML_panic(struct vsb * vsb,const struct objcore * oc)664 SML_panic(struct vsb *vsb, const struct objcore *oc)
665 {
666 struct object *o;
667 struct storage *st;
668
669 VSB_printf(vsb, "Simple = %p,\n", oc->stobj->priv);
670 if (oc->stobj->priv == NULL)
671 return;
672 CAST_OBJ_NOTNULL(o, oc->stobj->priv, OBJECT_MAGIC);
673 sml_panic_st(vsb, "Obj", o->objstore);
674
675 #define OBJ_FIXATTR(U, l, sz) \
676 VSB_printf(vsb, "%s = ", #U); \
677 VSB_quote(vsb, (const void*)o->fa_##l, sz, VSB_QUOTE_HEX); \
678 VSB_printf(vsb, ",\n");
679
680 #define OBJ_VARATTR(U, l) \
681 VSB_printf(vsb, "%s = {len=%u, ptr=%p},\n", \
682 #U, o->va_##l##_len, o->va_##l);
683
684 #define OBJ_AUXATTR(U, l) \
685 if (o->aa_##l != NULL) sml_panic_st(vsb, #U, o->aa_##l);
686
687 #include "tbl/obj_attr.h"
688
689 VTAILQ_FOREACH(st, &o->list, list) {
690 sml_panic_st(vsb, "Body", st);
691 }
692 }
693