xref: /csrg-svn/lib/libc/db/mpool/mpool.c (revision 66216)
150982Sbostic /*-
262485Sbostic  * Copyright (c) 1990, 1993
362485Sbostic  *	The Regents of the University of California.  All rights reserved.
450982Sbostic  *
550982Sbostic  * %sccs.include.redist.c%
650982Sbostic  */
750982Sbostic 
850982Sbostic #if defined(LIBC_SCCS) && !defined(lint)
9*66216Sbostic static char sccsid[] = "@(#)mpool.c	8.2 (Berkeley) 02/21/94";
1050982Sbostic #endif /* LIBC_SCCS and not lint */
1150982Sbostic 
1250982Sbostic #include <sys/param.h>
1350982Sbostic #include <sys/stat.h>
1457933Sbostic 
1550982Sbostic #include <errno.h>
1650982Sbostic #include <stdio.h>
1750982Sbostic #include <stdlib.h>
1850982Sbostic #include <string.h>
1957933Sbostic #include <unistd.h>
2057933Sbostic 
2157933Sbostic #include <db.h>
2250982Sbostic #define	__MPOOLINTERFACE_PRIVATE
2350982Sbostic #include "mpool.h"
2450982Sbostic 
2550982Sbostic static BKT *mpool_bkt __P((MPOOL *));
2650982Sbostic static BKT *mpool_look __P((MPOOL *, pgno_t));
2750982Sbostic static int  mpool_write __P((MPOOL *, BKT *));
2850982Sbostic #ifdef DEBUG
2962484Sbostic static void __mpoolerr __P((const char *fmt, ...));
3050982Sbostic #endif
3150982Sbostic 
3250982Sbostic /*
3350982Sbostic  * MPOOL_OPEN -- initialize a memory pool.
3450982Sbostic  *
3550982Sbostic  * Parameters:
3650982Sbostic  *	key:		Shared buffer key.
3750982Sbostic  *	fd:		File descriptor.
3850982Sbostic  *	pagesize:	File page size.
3950982Sbostic  *	maxcache:	Max number of cached pages.
4050982Sbostic  *
4150982Sbostic  * Returns:
4250982Sbostic  *	MPOOL pointer, NULL on error.
4350982Sbostic  */
4450982Sbostic MPOOL *
mpool_open(key,fd,pagesize,maxcache)4550982Sbostic mpool_open(key, fd, pagesize, maxcache)
4650982Sbostic 	DBT *key;
4750982Sbostic 	int fd;
4850982Sbostic 	pgno_t pagesize, maxcache;
4950982Sbostic {
5050982Sbostic 	struct stat sb;
5150982Sbostic 	MPOOL *mp;
5250982Sbostic 	int entry;
5350982Sbostic 
5450982Sbostic 	if (fstat(fd, &sb))
5550982Sbostic 		return (NULL);
5650982Sbostic 	/* XXX
5750982Sbostic 	 * We should only set st_size to 0 for pipes -- 4.4BSD has the fix so
5850982Sbostic 	 * that stat(2) returns true for ISSOCK on pipes.  Until then, this is
5950982Sbostic 	 * fairly close.
6050982Sbostic 	 */
6150982Sbostic 	if (!S_ISREG(sb.st_mode)) {
6250982Sbostic 		errno = ESPIPE;
6350982Sbostic 		return (NULL);
6450982Sbostic 	}
6550982Sbostic 
66*66216Sbostic 	if ((mp = (MPOOL *)malloc(sizeof(MPOOL))) == NULL)
6750982Sbostic 		return (NULL);
6850982Sbostic 	mp->free.cnext = mp->free.cprev = (BKT *)&mp->free;
6950982Sbostic 	mp->lru.cnext = mp->lru.cprev = (BKT *)&mp->lru;
7050982Sbostic 	for (entry = 0; entry < HASHSIZE; ++entry)
7150982Sbostic 		mp->hashtable[entry].hnext = mp->hashtable[entry].hprev =
7250982Sbostic 		    mp->hashtable[entry].cnext = mp->hashtable[entry].cprev =
7350982Sbostic 		    (BKT *)&mp->hashtable[entry];
7450982Sbostic 	mp->curcache = 0;
7550982Sbostic 	mp->maxcache = maxcache;
7650982Sbostic 	mp->pagesize = pagesize;
7750982Sbostic 	mp->npages = sb.st_size / pagesize;
7850982Sbostic 	mp->fd = fd;
7958072Sbostic 	mp->pgcookie = NULL;
8058072Sbostic 	mp->pgin = mp->pgout = NULL;
8150982Sbostic 
8250982Sbostic #ifdef STATISTICS
8350982Sbostic 	mp->cachehit = mp->cachemiss = mp->pagealloc = mp->pageflush =
8450982Sbostic 	    mp->pageget = mp->pagenew = mp->pageput = mp->pageread =
8550982Sbostic 	    mp->pagewrite = 0;
8650982Sbostic #endif
8750982Sbostic 	return (mp);
8850982Sbostic }
8950982Sbostic 
9050982Sbostic /*
9150982Sbostic  * MPOOL_FILTER -- initialize input/output filters.
9250982Sbostic  *
9350982Sbostic  * Parameters:
9450982Sbostic  *	pgin:		Page in conversion routine.
9550982Sbostic  *	pgout:		Page out conversion routine.
9650982Sbostic  *	pgcookie:	Cookie for page in/out routines.
9750982Sbostic  */
9850982Sbostic void
mpool_filter(mp,pgin,pgout,pgcookie)9950982Sbostic mpool_filter(mp, pgin, pgout, pgcookie)
10050982Sbostic 	MPOOL *mp;
10150982Sbostic 	void (*pgin) __P((void *, pgno_t, void *));
10250982Sbostic 	void (*pgout) __P((void *, pgno_t, void *));
10350982Sbostic 	void *pgcookie;
10450982Sbostic {
10550982Sbostic 	mp->pgin = pgin;
10650982Sbostic 	mp->pgout = pgout;
10750982Sbostic 	mp->pgcookie = pgcookie;
10850982Sbostic }
10950982Sbostic 
11050982Sbostic /*
11150982Sbostic  * MPOOL_NEW -- get a new page
11250982Sbostic  *
11350982Sbostic  * Parameters:
11450982Sbostic  *	mp:		mpool cookie
11550982Sbostic  *	pgnoadddr:	place to store new page number
11650982Sbostic  * Returns:
11750982Sbostic  *	RET_ERROR, RET_SUCCESS
11850982Sbostic  */
11950982Sbostic void *
mpool_new(mp,pgnoaddr)12050982Sbostic mpool_new(mp, pgnoaddr)
12150982Sbostic 	MPOOL *mp;
12250982Sbostic 	pgno_t *pgnoaddr;
12350982Sbostic {
12450982Sbostic 	BKT *b;
12550982Sbostic 	BKTHDR *hp;
12650982Sbostic 
12750982Sbostic #ifdef STATISTICS
12850982Sbostic 	++mp->pagenew;
12950982Sbostic #endif
13050982Sbostic 	/*
13150982Sbostic 	 * Get a BKT from the cache.  Assign a new page number, attach it to
13250982Sbostic 	 * the hash and lru chains and return.
13350982Sbostic 	 */
13450982Sbostic 	if ((b = mpool_bkt(mp)) == NULL)
13550982Sbostic 		return (NULL);
13650982Sbostic 	*pgnoaddr = b->pgno = mp->npages++;
13750982Sbostic 	b->flags = MPOOL_PINNED;
13850982Sbostic 	inshash(b, b->pgno);
13950982Sbostic 	inschain(b, &mp->lru);
14050982Sbostic 	return (b->page);
14150982Sbostic }
14250982Sbostic 
14350982Sbostic /*
14450982Sbostic  * MPOOL_GET -- get a page from the pool
14550982Sbostic  *
14650982Sbostic  * Parameters:
14750982Sbostic  *	mp:	mpool cookie
14850982Sbostic  *	pgno:	page number
14950982Sbostic  *	flags:	not used
15050982Sbostic  *
15150982Sbostic  * Returns:
15250982Sbostic  *	RET_ERROR, RET_SUCCESS
15350982Sbostic  */
15450982Sbostic void *
mpool_get(mp,pgno,flags)15550982Sbostic mpool_get(mp, pgno, flags)
15650982Sbostic 	MPOOL *mp;
15750982Sbostic 	pgno_t pgno;
15850982Sbostic 	u_int flags;		/* XXX not used? */
15950982Sbostic {
16050982Sbostic 	BKT *b;
16150982Sbostic 	BKTHDR *hp;
16250982Sbostic 	off_t off;
16350982Sbostic 	int nr;
16450982Sbostic 
16550982Sbostic 	/*
16650982Sbostic 	 * If asking for a specific page that is already in the cache, find
16750982Sbostic 	 * it and return it.
16850982Sbostic 	 */
16950982Sbostic 	if (b = mpool_look(mp, pgno)) {
17050982Sbostic #ifdef STATISTICS
17150982Sbostic 		++mp->pageget;
17250982Sbostic #endif
17350982Sbostic #ifdef DEBUG
17450982Sbostic 		if (b->flags & MPOOL_PINNED)
17562484Sbostic 			__mpoolerr("mpool_get: page %d already pinned",
17662484Sbostic 			    b->pgno);
17750982Sbostic #endif
17850982Sbostic 		rmchain(b);
17950982Sbostic 		inschain(b, &mp->lru);
18050982Sbostic 		b->flags |= MPOOL_PINNED;
18150982Sbostic 		return (b->page);
18250982Sbostic 	}
18350982Sbostic 
18450982Sbostic 	/* Not allowed to retrieve a non-existent page. */
18550982Sbostic 	if (pgno >= mp->npages) {
18650982Sbostic 		errno = EINVAL;
18750982Sbostic 		return (NULL);
18850982Sbostic 	}
18950982Sbostic 
19050982Sbostic 	/* Get a page from the cache. */
19150982Sbostic 	if ((b = mpool_bkt(mp)) == NULL)
19250982Sbostic 		return (NULL);
19350982Sbostic 	b->pgno = pgno;
19450982Sbostic 	b->flags = MPOOL_PINNED;
19550982Sbostic 
19650982Sbostic #ifdef STATISTICS
19750982Sbostic 	++mp->pageread;
19850982Sbostic #endif
19950982Sbostic 	/* Read in the contents. */
20050982Sbostic 	off = mp->pagesize * pgno;
20150982Sbostic 	if (lseek(mp->fd, off, SEEK_SET) != off)
20250982Sbostic 		return (NULL);
20350982Sbostic 	if ((nr = read(mp->fd, b->page, mp->pagesize)) != mp->pagesize) {
20450982Sbostic 		if (nr >= 0)
20550982Sbostic 			errno = EFTYPE;
20650982Sbostic 		return (NULL);
20750982Sbostic 	}
20850982Sbostic 	if (mp->pgin)
20950982Sbostic 		(mp->pgin)(mp->pgcookie, b->pgno, b->page);
21050982Sbostic 
21150982Sbostic 	inshash(b, b->pgno);
21250982Sbostic 	inschain(b, &mp->lru);
21350982Sbostic #ifdef STATISTICS
21450982Sbostic 	++mp->pageget;
21550982Sbostic #endif
21650982Sbostic 	return (b->page);
21750982Sbostic }
21850982Sbostic 
21950982Sbostic /*
22050982Sbostic  * MPOOL_PUT -- return a page to the pool
22150982Sbostic  *
22250982Sbostic  * Parameters:
22350982Sbostic  *	mp:	mpool cookie
22450982Sbostic  *	page:	page pointer
22550982Sbostic  *	pgno:	page number
22650982Sbostic  *
22750982Sbostic  * Returns:
22850982Sbostic  *	RET_ERROR, RET_SUCCESS
22950982Sbostic  */
23050982Sbostic int
mpool_put(mp,page,flags)23150982Sbostic mpool_put(mp, page, flags)
23250982Sbostic 	MPOOL *mp;
23350982Sbostic 	void *page;
23450982Sbostic 	u_int flags;
23550982Sbostic {
23650982Sbostic 	BKT *baddr;
23750982Sbostic #ifdef DEBUG
23850982Sbostic 	BKT *b;
23950982Sbostic #endif
24050982Sbostic 
24150982Sbostic #ifdef STATISTICS
24250982Sbostic 	++mp->pageput;
24350982Sbostic #endif
24451766Sbostic 	baddr = (BKT *)((char *)page - sizeof(BKT));
24550982Sbostic #ifdef DEBUG
24650982Sbostic 	if (!(baddr->flags & MPOOL_PINNED))
24762484Sbostic 		__mpoolerr("mpool_put: page %d not pinned", b->pgno);
24850982Sbostic 	for (b = mp->lru.cnext; b != (BKT *)&mp->lru; b = b->cnext) {
24950982Sbostic 		if (b == (BKT *)&mp->lru)
25062484Sbostic 			__mpoolerr("mpool_put: %0x: bad address", baddr);
25150982Sbostic 		if (b == baddr)
25250982Sbostic 			break;
25350982Sbostic 	}
25450982Sbostic #endif
25550982Sbostic 	baddr->flags &= ~MPOOL_PINNED;
25650982Sbostic 	baddr->flags |= flags & MPOOL_DIRTY;
25750982Sbostic 	return (RET_SUCCESS);
25850982Sbostic }
25950982Sbostic 
26050982Sbostic /*
26150982Sbostic  * MPOOL_CLOSE -- close the buffer pool
26250982Sbostic  *
26350982Sbostic  * Parameters:
26450982Sbostic  *	mp:	mpool cookie
26550982Sbostic  *
26650982Sbostic  * Returns:
26750982Sbostic  *	RET_ERROR, RET_SUCCESS
26850982Sbostic  */
26950982Sbostic int
mpool_close(mp)27050982Sbostic mpool_close(mp)
27150982Sbostic 	MPOOL *mp;
27250982Sbostic {
27350982Sbostic 	BKT *b, *next;
27450982Sbostic 
27550982Sbostic 	/* Free up any space allocated to the lru pages. */
27650982Sbostic 	for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = next) {
27750982Sbostic 		next = b->cprev;
27850982Sbostic 		free(b);
27950982Sbostic 	}
28051802Sbostic 	free(mp);
28150982Sbostic 	return (RET_SUCCESS);
28250982Sbostic }
28350982Sbostic 
28450982Sbostic /*
28550982Sbostic  * MPOOL_SYNC -- sync the file to disk.
28650982Sbostic  *
28750982Sbostic  * Parameters:
28850982Sbostic  *	mp:	mpool cookie
28950982Sbostic  *
29050982Sbostic  * Returns:
29150982Sbostic  *	RET_ERROR, RET_SUCCESS
29250982Sbostic  */
29350982Sbostic int
mpool_sync(mp)29450982Sbostic mpool_sync(mp)
29550982Sbostic 	MPOOL *mp;
29650982Sbostic {
29750982Sbostic 	BKT *b;
29850982Sbostic 
29950982Sbostic 	for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = b->cprev)
30050982Sbostic 		if (b->flags & MPOOL_DIRTY && mpool_write(mp, b) == RET_ERROR)
30150982Sbostic 			return (RET_ERROR);
30250982Sbostic 	return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
30350982Sbostic }
30450982Sbostic 
30550982Sbostic /*
30650982Sbostic  * MPOOL_BKT -- get/create a BKT from the cache
30750982Sbostic  *
30850982Sbostic  * Parameters:
30950982Sbostic  *	mp:	mpool cookie
31050982Sbostic  *
31150982Sbostic  * Returns:
31250982Sbostic  *	NULL on failure and a pointer to the BKT on success
31350982Sbostic  */
31450982Sbostic static BKT *
mpool_bkt(mp)31550982Sbostic mpool_bkt(mp)
31650982Sbostic 	MPOOL *mp;
31750982Sbostic {
31850982Sbostic 	BKT *b;
31950982Sbostic 
32050982Sbostic 	if (mp->curcache < mp->maxcache)
32150982Sbostic 		goto new;
32250982Sbostic 
32350982Sbostic 	/*
32450982Sbostic 	 * If the cache is maxxed out, search the lru list for a buffer we
32550982Sbostic 	 * can flush.  If we find one, write it if necessary and take it off
32650982Sbostic 	 * any lists.  If we don't find anything we grow the cache anyway.
32750982Sbostic 	 * The cache never shrinks.
32850982Sbostic 	 */
32950982Sbostic 	for (b = mp->lru.cprev; b != (BKT *)&mp->lru; b = b->cprev)
33050982Sbostic 		if (!(b->flags & MPOOL_PINNED)) {
33150982Sbostic 			if (b->flags & MPOOL_DIRTY &&
33250982Sbostic 			    mpool_write(mp, b) == RET_ERROR)
33350982Sbostic 				return (NULL);
33450982Sbostic 			rmhash(b);
33550982Sbostic 			rmchain(b);
33650982Sbostic #ifdef STATISTICS
33750982Sbostic 			++mp->pageflush;
33850982Sbostic #endif
33950982Sbostic #ifdef DEBUG
34050982Sbostic 			{
34150982Sbostic 				void *spage;
34250982Sbostic 				spage = b->page;
34350982Sbostic 				memset(b, 0xff, sizeof(BKT) + mp->pagesize);
34450982Sbostic 				b->page = spage;
34550982Sbostic 			}
34650982Sbostic #endif
34750982Sbostic 			return (b);
34850982Sbostic 		}
34950982Sbostic 
350*66216Sbostic new:	if ((b = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
35150982Sbostic 		return (NULL);
35250982Sbostic #ifdef STATISTICS
35350982Sbostic 	++mp->pagealloc;
35450982Sbostic #endif
35550982Sbostic #ifdef DEBUG
35650982Sbostic 	memset(b, 0xff, sizeof(BKT) + mp->pagesize);
35750982Sbostic #endif
35850982Sbostic 	b->page = (char *)b + sizeof(BKT);
35950982Sbostic 	++mp->curcache;
36050982Sbostic 	return (b);
36150982Sbostic }
36250982Sbostic 
36350982Sbostic /*
36450982Sbostic  * MPOOL_WRITE -- sync a page to disk
36550982Sbostic  *
36650982Sbostic  * Parameters:
36750982Sbostic  *	mp:	mpool cookie
36850982Sbostic  *
36950982Sbostic  * Returns:
37050982Sbostic  *	RET_ERROR, RET_SUCCESS
37150982Sbostic  */
37250982Sbostic static int
mpool_write(mp,b)37350982Sbostic mpool_write(mp, b)
37450982Sbostic 	MPOOL *mp;
37550982Sbostic 	BKT *b;
37650982Sbostic {
37750982Sbostic 	off_t off;
37850982Sbostic 
37950982Sbostic 	if (mp->pgout)
38050982Sbostic 		(mp->pgout)(mp->pgcookie, b->pgno, b->page);
38150982Sbostic 
38250982Sbostic #ifdef STATISTICS
38350982Sbostic 	++mp->pagewrite;
38450982Sbostic #endif
38550982Sbostic 	off = mp->pagesize * b->pgno;
38650982Sbostic 	if (lseek(mp->fd, off, SEEK_SET) != off)
38750982Sbostic 		return (RET_ERROR);
38850982Sbostic 	if (write(mp->fd, b->page, mp->pagesize) != mp->pagesize)
38950982Sbostic 		return (RET_ERROR);
39050982Sbostic 	b->flags &= ~MPOOL_DIRTY;
39150982Sbostic 	return (RET_SUCCESS);
39250982Sbostic }
39350982Sbostic 
39450982Sbostic /*
39550982Sbostic  * MPOOL_LOOK -- lookup a page
39650982Sbostic  *
39750982Sbostic  * Parameters:
39850982Sbostic  *	mp:	mpool cookie
39950982Sbostic  *	pgno:	page number
40050982Sbostic  *
40150982Sbostic  * Returns:
40250982Sbostic  *	NULL on failure and a pointer to the BKT on success
40350982Sbostic  */
40450982Sbostic static BKT *
mpool_look(mp,pgno)40550982Sbostic mpool_look(mp, pgno)
40650982Sbostic 	MPOOL *mp;
40750982Sbostic 	pgno_t pgno;
40850982Sbostic {
40950982Sbostic 	register BKT *b;
41050982Sbostic 	register BKTHDR *tb;
41150982Sbostic 
41250982Sbostic 	/* XXX
41350982Sbostic 	 * If find the buffer, put it first on the hash chain so can
41450982Sbostic 	 * find it again quickly.
41550982Sbostic 	 */
41650982Sbostic 	tb = &mp->hashtable[HASHKEY(pgno)];
41750982Sbostic 	for (b = tb->hnext; b != (BKT *)tb; b = b->hnext)
41850982Sbostic 		if (b->pgno == pgno) {
41950982Sbostic #ifdef STATISTICS
42050982Sbostic 			++mp->cachehit;
42150982Sbostic #endif
42250982Sbostic 			return (b);
42350982Sbostic 		}
42450982Sbostic #ifdef STATISTICS
42550982Sbostic 	++mp->cachemiss;
42650982Sbostic #endif
42750982Sbostic 	return (NULL);
42850982Sbostic }
42950982Sbostic 
43050982Sbostic #ifdef STATISTICS
43150982Sbostic /*
43250982Sbostic  * MPOOL_STAT -- cache statistics
43350982Sbostic  *
43450982Sbostic  * Parameters:
43550982Sbostic  *	mp:	mpool cookie
43650982Sbostic  */
43750982Sbostic void
mpool_stat(mp)43850982Sbostic mpool_stat(mp)
43950982Sbostic 	MPOOL *mp;
44050982Sbostic {
44150982Sbostic 	BKT *b;
44250982Sbostic 	int cnt;
44350982Sbostic 	char *sep;
44450982Sbostic 
44550982Sbostic 	(void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
44650982Sbostic 	(void)fprintf(stderr,
44750982Sbostic 	    "page size %lu, cacheing %lu pages of %lu page max cache\n",
44850982Sbostic 	    mp->pagesize, mp->curcache, mp->maxcache);
44950982Sbostic 	(void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
45050982Sbostic 	    mp->pageput, mp->pageget, mp->pagenew);
45150982Sbostic 	(void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
45250982Sbostic 	    mp->pagealloc, mp->pageflush);
45350982Sbostic 	if (mp->cachehit + mp->cachemiss)
45450982Sbostic 		(void)fprintf(stderr,
45550982Sbostic 		    "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
45650982Sbostic 		    ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
45750982Sbostic 		    * 100, mp->cachehit, mp->cachemiss);
45850982Sbostic 	(void)fprintf(stderr, "%lu page reads, %lu page writes\n",
45950982Sbostic 	    mp->pageread, mp->pagewrite);
46050982Sbostic 
46150982Sbostic 	sep = "";
46250982Sbostic 	cnt = 0;
46350982Sbostic 	for (b = mp->lru.cnext; b != (BKT *)&mp->lru; b = b->cnext) {
46450982Sbostic 		(void)fprintf(stderr, "%s%d", sep, b->pgno);
46550982Sbostic 		if (b->flags & MPOOL_DIRTY)
46650982Sbostic 			(void)fprintf(stderr, "d");
46750982Sbostic 		if (b->flags & MPOOL_PINNED)
46850982Sbostic 			(void)fprintf(stderr, "P");
46950982Sbostic 		if (++cnt == 10) {
47050982Sbostic 			sep = "\n";
47150982Sbostic 			cnt = 0;
47250982Sbostic 		} else
47350982Sbostic 			sep = ", ";
47450982Sbostic 
47550982Sbostic 	}
47650982Sbostic 	(void)fprintf(stderr, "\n");
47750982Sbostic }
47850982Sbostic #endif
47950982Sbostic 
48050982Sbostic #ifdef DEBUG
48150982Sbostic #if __STDC__
48250982Sbostic #include <stdarg.h>
48350982Sbostic #else
48450982Sbostic #include <varargs.h>
48550982Sbostic #endif
48650982Sbostic 
48750982Sbostic static void
48850982Sbostic #if __STDC__
__mpoolerr(const char * fmt,...)48962484Sbostic __mpoolerr(const char *fmt, ...)
49050982Sbostic #else
49162484Sbostic __mpoolerr(fmt, va_alist)
49250982Sbostic 	char *fmt;
49350982Sbostic 	va_dcl
49450982Sbostic #endif
49550982Sbostic {
49650982Sbostic 	va_list ap;
49750982Sbostic #if __STDC__
49850982Sbostic 	va_start(ap, fmt);
49950982Sbostic #else
50050982Sbostic 	va_start(ap);
50150982Sbostic #endif
50250982Sbostic 	(void)vfprintf(stderr, fmt, ap);
50350982Sbostic 	va_end(ap);
50450982Sbostic 	(void)fprintf(stderr, "\n");
50550982Sbostic 	abort();
50650982Sbostic 	/* NOTREACHED */
50750982Sbostic }
50850982Sbostic #endif
509