mem.c 37.5 KB
Newer Older
Bob Halley's avatar
base  
Bob Halley committed
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 1997-2001  Internet Software Consortium.
3
 *
Bob Halley's avatar
base  
Bob Halley committed
4
5
6
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
7
 *
8
9
10
11
12
13
14
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Bob Halley's avatar
base  
Bob Halley committed
16
17
 */

18
/* $Id: mem.c,v 1.102 2001/09/06 23:03:01 gson Exp $ */
David Lawrence's avatar
David Lawrence committed
19

Bob Halley's avatar
Bob Halley committed
20
#include <config.h>
Bob Halley's avatar
base  
Bob Halley committed
21
22
23

#include <stdio.h>
#include <stdlib.h>
Bob Halley's avatar
Bob Halley committed
24
#include <stddef.h>
Bob Halley's avatar
base  
Bob Halley committed
25

Michael Graff's avatar
Michael Graff committed
26
27
#include <limits.h>

28
#include <isc/magic.h>
Bob Halley's avatar
Bob Halley committed
29
#include <isc/mem.h>
30
#include <isc/msgs.h>
31
#include <isc/ondestroy.h>
32
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
33

Bob Halley's avatar
update    
Bob Halley committed
34
#include <isc/mutex.h>
Michael Graff's avatar
Michael Graff committed
35
#include <isc/util.h>
Bob Halley's avatar
update    
Bob Halley committed
36

37
38
39
#ifndef ISC_MEM_DEBUGGING
#define ISC_MEM_DEBUGGING 0
#endif
40
LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
41

42
43
44
45
46
47
48
49
50
51
/*
 * Define ISC_MEM_USE_INTERNAL_MALLOC=1 to use the internal malloc()
 * implementation in preference to the system one.  The internal malloc()
 * is very space-efficient, and quite fast on uniprocessor systems.  It
 * performs poorly on multiprocessor machines.
 */
#ifndef ISC_MEM_USE_INTERNAL_MALLOC
#define ISC_MEM_USE_INTERNAL_MALLOC 0
#endif

52
53
54
55
56
57
/*
 * Constants.
 */

#define DEF_MAX_SIZE		1100
#define DEF_MEM_TARGET		4096
58
59
#define ALIGNMENT_SIZE		8		/* must be a power of 2 */
#define NUM_BASIC_BLOCKS	64		/* must be > 1 */
60
#define TABLE_INCREMENT		1024
Michael Graff's avatar
Michael Graff committed
61
#define DEBUGLIST_COUNT		1024
62

Bob Halley's avatar
base  
Bob Halley committed
63
64
65
/*
 * Types.
 */
66
#if ISC_MEM_TRACKLINES
Michael Graff's avatar
Michael Graff committed
67
68
69
70
71
72
73
74
typedef struct debuglink debuglink_t;
struct debuglink {
	ISC_LINK(debuglink_t)	link;
	const void	       *ptr[DEBUGLIST_COUNT];
	const char	       *file[DEBUGLIST_COUNT];
	unsigned int		line[DEBUGLIST_COUNT];
	unsigned int		count;
};
Bob Halley's avatar
base  
Bob Halley committed
75

Michael Graff's avatar
Michael Graff committed
76
77
78
79
80
81
#define FLARG_PASS	, file, line
#define FLARG		, const char *file, int line
#else
#define FLARG_PASS
#define FLARG
#endif
82

Michael Graff's avatar
Michael Graff committed
83
typedef struct element element;
84
85
86
struct element {
	element *		next;
};
Bob Halley's avatar
base  
Bob Halley committed
87
88
89
90
91

typedef struct {
	/*
	 * This structure must be ALIGNMENT_SIZE bytes.
	 */
92
93
94
95
	union {
		size_t		size;
		char		bytes[ALIGNMENT_SIZE];
	} u;
Bob Halley's avatar
Bob Halley committed
96
} size_info;
Bob Halley's avatar
base  
Bob Halley committed
97
98

struct stats {
Bob Halley's avatar
Bob Halley committed
99
100
	unsigned long		gets;
	unsigned long		totalgets;
101
#if ISC_MEM_USE_INTERNAL_MALLOC
Bob Halley's avatar
Bob Halley committed
102
103
	unsigned long		blocks;
	unsigned long		freefrags;
104
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */
Bob Halley's avatar
base  
Bob Halley committed
105
106
};

107
108
#define MEM_MAGIC		ISC_MAGIC('M', 'e', 'm', 'C')
#define VALID_CONTEXT(c)	ISC_MAGIC_VALID(c, MEM_MAGIC)
Bob Halley's avatar
Bob Halley committed
109

110
#if ISC_MEM_TRACKLINES
111
typedef ISC_LIST(debuglink_t)	debuglist_t;
112
#endif
113

114
struct isc_mem {
Bob Halley's avatar
Bob Halley committed
115
	unsigned int		magic;
116
	isc_ondestroy_t		ondestroy;
Bob Halley's avatar
Bob Halley committed
117
	isc_mutex_t		lock;
118
119
120
	isc_memalloc_t		memalloc;
	isc_memfree_t		memfree;
	void *			arg;
Bob Halley's avatar
base  
Bob Halley committed
121
	size_t			max_size;
122
	isc_boolean_t		checkfree;
Bob Halley's avatar
base  
Bob Halley committed
123
	struct stats *		stats;
124
	unsigned int		references;
125
	size_t			quota;
Bob Halley's avatar
Bob Halley committed
126
	size_t			total;
127
	size_t			inuse;
128
	size_t			maxinuse;
129
130
131
132
133
	size_t			hi_water;
	size_t			lo_water;
	isc_boolean_t		hi_called;
	isc_mem_water_t		water;
	void *			water_arg;
Michael Graff's avatar
Michael Graff committed
134
	ISC_LIST(isc_mempool_t)	pools;
135
136
137
138
139
140
141
142
143
144
145
146

#if ISC_MEM_USE_INTERNAL_MALLOC
	size_t			mem_target;
	element **		freelists;
	element *		basic_blocks;
	unsigned char **	basic_table;
	unsigned int		basic_table_count;
	unsigned int		basic_table_size;
	unsigned char *		lowest;
	unsigned char *		highest;
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

147
#if ISC_MEM_TRACKLINES
148
	debuglist_t *	 	debuglist;
Michael Graff's avatar
Michael Graff committed
149
#endif
150
151

	unsigned int		memalloc_failures;
Michael Graff's avatar
Michael Graff committed
152
153
};

154
155
#define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
#define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
Michael Graff's avatar
Michael Graff committed
156
157

struct isc_mempool {
Michael Graff's avatar
Michael Graff committed
158
	/* always unlocked */
Michael Graff's avatar
Michael Graff committed
159
	unsigned int	magic;		/* magic number */
Michael Graff's avatar
Michael Graff committed
160
	isc_mutex_t    *lock;		/* optional lock */
Michael Graff's avatar
Michael Graff committed
161
	isc_mem_t      *mctx;		/* our memory context */
Michael Graff's avatar
Michael Graff committed
162
163
164
	/* locked via the memory context's lock */
	ISC_LINK(isc_mempool_t)	link;	/* next pool in this mem context */
	/* optionally locked from here down */
Michael Graff's avatar
Michael Graff committed
165
166
167
168
169
170
171
172
173
	element	       *items;		/* low water item list */
	size_t		size;		/* size of each item on this pool */
	unsigned int	maxalloc;	/* max number of items allowed */
	unsigned int	allocated;	/* # of items currently given out */
	unsigned int	freecount;	/* # of items on reserved list */
	unsigned int	freemax;	/* # of items allowed on free list */
	unsigned int	fillcount;	/* # of items to fetch on each fill */
	/* Stats only. */
	unsigned int	gets;		/* # of requests to this pool */
Michael Graff's avatar
Michael Graff committed
174
	/* Debugging only. */
175
#if ISC_MEMPOOL_NAMES
Michael Graff's avatar
Michael Graff committed
176
177
	char		name[16];	/* printed name in stats reports */
#endif
Bob Halley's avatar
base  
Bob Halley committed
178
179
};

180
181
182
/*
 * Private Inline-able.
 */
Bob Halley's avatar
base  
Bob Halley committed
183

184
#if ! ISC_MEM_TRACKLINES
Michael Graff's avatar
Michael Graff committed
185
186
187
#define ADD_TRACE(a, b, c, d, e)
#define DELETE_TRACE(a, b, c, d, e)
#else
188
#define ADD_TRACE(a, b, c, d, e) \
189
190
191
192
193
194
	do { \
		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
					  ISC_MEM_DEBUGRECORD)) != 0 && \
		     b != NULL) \
		         add_trace_entry(a, b, c, d, e); \
	} while (0)
Michael Graff's avatar
Michael Graff committed
195
196
#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)

197
198
199
static void
print_active(isc_mem_t *ctx, FILE *out);

Michael Graff's avatar
Michael Graff committed
200
201
202
203
204
/*
 * mctx must be locked.
 */
static inline void
add_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size
Michael Graff's avatar
pasto    
Michael Graff committed
205
		FLARG)
Michael Graff's avatar
Michael Graff committed
206
207
208
209
{
	debuglink_t *dl;
	unsigned int i;

210
	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
211
212
213
214
		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					       ISC_MSG_ADDTRACE,
					       "add %p size %u "
					       "file %s line %u mctx %p\n"),
215
			ptr, size, file, line, mctx);
Michael Graff's avatar
Michael Graff committed
216

217
	if (mctx->debuglist == NULL)
Michael Graff's avatar
Michael Graff committed
218
219
		return;

220
221
222
223
	if (size > mctx->max_size)
		size = mctx->max_size;

	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
Michael Graff's avatar
Michael Graff committed
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
	while (dl != NULL) {
		if (dl->count == DEBUGLIST_COUNT)
			goto next;
		for (i = 0 ; i < DEBUGLIST_COUNT ; i++) {
			if (dl->ptr[i] == NULL) {
				dl->ptr[i] = ptr;
				dl->file[i] = file;
				dl->line[i] = line;
				dl->count++;
				return;
			}
		}
	next:
		dl = ISC_LIST_NEXT(dl, link);
	}

	dl = malloc(sizeof(debuglink_t));
	INSIST(dl != NULL);

	ISC_LINK_INIT(dl, link);
	for (i = 1 ; i < DEBUGLIST_COUNT ; i++) {
		dl->ptr[i] = NULL;
		dl->file[i] = NULL;
		dl->line[i] = 0;
	}

	dl->ptr[0] = ptr;
	dl->file[0] = file;
	dl->line[0] = line;
	dl->count = 1;

255
	ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);
Michael Graff's avatar
Michael Graff committed
256
257
258
259
260
261
262
263
264
}

static inline void
delete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size,
		   const char *file, unsigned int line)
{
	debuglink_t *dl;
	unsigned int i;

265
	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
266
267
268
269
		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					       ISC_MSG_DELTRACE,
					       "del %p size %u "
					       "file %s line %u mctx %p\n"),
270
			ptr, size, file, line, mctx);
Michael Graff's avatar
Michael Graff committed
271

272
	if (mctx->debuglist == NULL)
Michael Graff's avatar
Michael Graff committed
273
274
		return;

275
276
277
278
	if (size > mctx->max_size)
		size = mctx->max_size;

	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
Michael Graff's avatar
Michael Graff committed
279
280
281
282
283
284
285
286
287
288
	while (dl != NULL) {
		for (i = 0 ; i < DEBUGLIST_COUNT ; i++) {
			if (dl->ptr[i] == ptr) {
				dl->ptr[i] = NULL;
				dl->file[i] = NULL;
				dl->line[i] = 0;

				INSIST(dl->count > 0);
				dl->count--;
				if (dl->count == 0) {
289
					ISC_LIST_UNLINK(mctx->debuglist[size],
Michael Graff's avatar
Michael Graff committed
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
							dl, link);
					free(dl);
				}
				return;
			}
		}
		dl = ISC_LIST_NEXT(dl, link);
	}

	/*
	 * If we get here, we didn't find the item on the list.  We're
	 * screwed.
	 */
	INSIST(dl != NULL);
}
#endif /* ISC_MEM_TRACKLINES */

307
#if ISC_MEM_USE_INTERNAL_MALLOC
308
309
310
311
312
static inline size_t
rmsize(size_t size) {
	/*
 	 * round down to ALIGNMENT_SIZE
	 */
313
	return (size & (~(ALIGNMENT_SIZE - 1)));
314
315
}

316
static inline size_t
Bob Halley's avatar
base  
Bob Halley committed
317
318
quantize(size_t size) {
	/*
319
	 * Round up the result in order to get a size big
Bob Halley's avatar
base  
Bob Halley committed
320
321
322
	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
	 * byte boundaries.
	 */
323

324
325
	if (size == 0)
		return (ALIGNMENT_SIZE);
326
	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
Bob Halley's avatar
base  
Bob Halley committed
327
328
}

329
static inline isc_boolean_t
330
more_basic_blocks(isc_mem_t *ctx) {
331
332
333
334
335
	void *new;
	unsigned char *curr, *next;
	unsigned char *first, *last;
	unsigned char **table;
	unsigned int table_size;
Bob Halley's avatar
Bob Halley committed
336
	size_t increment;
337
338
339
340
	int i;

	/* Require: we hold the context lock. */

341
342
343
	/*
	 * Did we hit the quota for this context?
	 */
Bob Halley's avatar
Bob Halley committed
344
345
	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
	if (ctx->quota != 0 && ctx->total + increment > ctx->quota)
346
		return (ISC_FALSE);
347
348
349

	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
	if (ctx->basic_table_count == ctx->basic_table_size) {
350
		table_size = ctx->basic_table_size + TABLE_INCREMENT;
351
352
		table = (ctx->memalloc)(ctx->arg,
					table_size * sizeof (unsigned char *));
353
354
		if (table == NULL) {
			ctx->memalloc_failures++;
355
			return (ISC_FALSE);
356
		}
357
358
359
360
		if (ctx->basic_table_size != 0) {
			memcpy(table, ctx->basic_table,
			       ctx->basic_table_size *
			       sizeof (unsigned char *));
361
			(ctx->memfree)(ctx->arg, ctx->basic_table);
362
		}
363
364
		ctx->basic_table = table;
		ctx->basic_table_size = table_size;
365
	}
366

367
	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
368
369
	if (new == NULL) {
		ctx->memalloc_failures++;
370
		return (ISC_FALSE);
371
	}
Bob Halley's avatar
Bob Halley committed
372
	ctx->total += increment;
373
374
	ctx->basic_table[ctx->basic_table_count] = new;
	ctx->basic_table_count++;
375

376
377
378
	curr = new;
	next = curr + ctx->mem_target;
	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
379
		((element *)curr)->next = (element *)next;
380
381
382
383
384
385
386
		curr = next;
		next += ctx->mem_target;
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
Bob Halley's avatar
Bob Halley committed
387
	((element *)curr)->next = NULL;
388
389
390
391
392
393
394
	first = new;
	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
	if (first < ctx->lowest || ctx->lowest == NULL)
		ctx->lowest = first;
	if (last > ctx->highest)
		ctx->highest = last;
	ctx->basic_blocks = new;
395
396

	return (ISC_TRUE);
397
398
}

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
static inline isc_boolean_t
more_frags(isc_mem_t *ctx, size_t new_size) {
	int i, frags;
	size_t total_size;
	void *new;
	unsigned char *curr, *next;

	/*
	 * Try to get more fragments by chopping up a basic block.
	 */

	if (ctx->basic_blocks == NULL) {
		if (!more_basic_blocks(ctx)) {
			/*
			 * We can't get more memory from the OS, or we've
			 * hit the quota for this context.
			 */
			/*
417
			 * XXXRTH  "At quota" notification here.
418
			 */
419
			return (ISC_FALSE);
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
		}
	}

	total_size = ctx->mem_target;
	new = ctx->basic_blocks;
	ctx->basic_blocks = ctx->basic_blocks->next;
	frags = total_size / new_size;
	ctx->stats[new_size].blocks++;
	ctx->stats[new_size].freefrags += frags;
	/*
	 * Set up a linked-list of blocks of size
	 * "new_size".
	 */
	curr = new;
	next = curr + new_size;
435
	total_size -= new_size;
436
437
438
439
	for (i = 0; i < (frags - 1); i++) {
		((element *)curr)->next = (element *)next;
		curr = next;
		next += new_size;
440
441
442
443
444
445
446
447
448
449
		total_size -= new_size;
	}
	/*
	 * Add the remaining fragment of the basic block to a free list.
	 */
	total_size = rmsize(total_size);
	if (total_size > 0) {
		((element *)next)->next = ctx->freelists[total_size];
		ctx->freelists[total_size] = (element *)next;
		ctx->stats[total_size].freefrags++;
450
451
452
453
454
455
456
457
458
459
460
	}
	/*
	 * curr is now pointing at the last block in the
	 * array.
	 */
	((element *)curr)->next = NULL;
	ctx->freelists[new_size] = new;

	return (ISC_TRUE);
}

Michael Graff's avatar
Michael Graff committed
461
static inline void *
462
mem_getunlocked(isc_mem_t *ctx, size_t size) {
Michael Graff's avatar
Michael Graff committed
463
464
	size_t new_size = quantize(size);
	void *ret;
Bob Halley's avatar
base  
Bob Halley committed
465
466

	if (size >= ctx->max_size || new_size >= ctx->max_size) {
467
468
469
		/*
		 * memget() was called on something beyond our upper limit.
		 */
Bob Halley's avatar
Bob Halley committed
470
471
472
473
		if (ctx->quota != 0 && ctx->total + size > ctx->quota) {
			ret = NULL;
			goto done;
		}
Mark Andrews's avatar
Mark Andrews committed
474
		ret = (ctx->memalloc)(ctx->arg, size);
475
476
477
		if (ret == NULL) {
			ctx->memalloc_failures++;
			goto done;
Bob Halley's avatar
base  
Bob Halley committed
478
		}
479
480
481
482
483
484
485
486
487
488
		ctx->total += size;
		ctx->inuse += size;
		ctx->stats[ctx->max_size].gets++;
		ctx->stats[ctx->max_size].totalgets++;
		/*
		 * If we don't set new_size to size, then the
		 * ISC_MEM_FILL code might write over bytes we
		 * don't own.
		 */
		new_size = size;
Bob Halley's avatar
base  
Bob Halley committed
489
490
	}

491
	/*
Bob Halley's avatar
base  
Bob Halley committed
492
493
494
495
	 * If there are no blocks in the free list for this size, get a chunk
	 * of memory and then break it up into "new_size"-sized blocks, adding
	 * them to the free list.
	 */
496
497
	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
		return (NULL);
Bob Halley's avatar
base  
Bob Halley committed
498

499
500
501
	/*
	 * The free list uses the "rounded-up" size "new_size".
	 */
Bob Halley's avatar
base  
Bob Halley committed
502
503
504
	ret = ctx->freelists[new_size];
	ctx->freelists[new_size] = ctx->freelists[new_size]->next;

505
	/*
Bob Halley's avatar
base  
Bob Halley committed
506
507
508
509
510
511
512
513
	 * The stats[] uses the _actual_ "size" requested by the
	 * caller, with the caveat (in the code above) that "size" >= the
	 * max. size (max_size) ends up getting recorded as a call to
	 * max_size.
	 */
	ctx->stats[size].gets++;
	ctx->stats[size].totalgets++;
	ctx->stats[new_size].freefrags--;
514
	ctx->inuse += new_size;
Bob Halley's avatar
base  
Bob Halley committed
515
516
517

 done:

518
#if ISC_MEM_FILL
519
520
	if (ret != NULL)
		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
David Lawrence's avatar
David Lawrence committed
521
522
523
#endif

	return (ret);
Michael Graff's avatar
Michael Graff committed
524
525
}

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
static inline void
check_overrun(void *mem, size_t size, size_t new_size) {
	unsigned char *cp;

	cp = (unsigned char *)mem;
	cp += size;
	while (size < new_size) {
		INSIST(*cp == 0xbe);
		cp++;
		size++;
	}
}
#endif

Michael Graff's avatar
Michael Graff committed
541
static inline void
542
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
Michael Graff's avatar
Michael Graff committed
543
	size_t new_size = quantize(size);
Bob Halley's avatar
base  
Bob Halley committed
544

545
	if (size == ctx->max_size || new_size >= ctx->max_size) {
546
547
548
		/*
		 * memput() called on something beyond our upper limit.
		 */
549
#if ISC_MEM_FILL
Mark Andrews's avatar
Mark Andrews committed
550
551
		memset(mem, 0xde, size); /* Mnemonic for "dead". */
#endif
552
		(ctx->memfree)(ctx->arg, mem);
Bob Halley's avatar
base  
Bob Halley committed
553
554
		INSIST(ctx->stats[ctx->max_size].gets != 0);
		ctx->stats[ctx->max_size].gets--;
Bob Halley's avatar
Bob Halley committed
555
		INSIST(size <= ctx->total);
556
		ctx->inuse -= size;
Bob Halley's avatar
Bob Halley committed
557
		ctx->total -= size;
Michael Graff's avatar
Michael Graff committed
558
		return;
Bob Halley's avatar
base  
Bob Halley committed
559
560
	}

561
562
#if ISC_MEM_FILL
#if ISC_MEM_CHECKOVERRUN
563
564
	check_overrun(mem, size, new_size);
#endif
Mark Andrews's avatar
Mark Andrews committed
565
566
	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
#endif
567

568
569
570
	/*
	 * The free list uses the "rounded-up" size "new_size".
	 */
Mark Andrews's avatar
Mark Andrews committed
571
572
	((element *)mem)->next = ctx->freelists[new_size];
	ctx->freelists[new_size] = (element *)mem;
Bob Halley's avatar
base  
Bob Halley committed
573

574
	/*
Bob Halley's avatar
base  
Bob Halley committed
575
576
577
578
579
580
581
582
	 * The stats[] uses the _actual_ "size" requested by the
	 * caller, with the caveat (in the code above) that "size" >= the
	 * max. size (max_size) ends up getting recorded as a call to
	 * max_size.
	 */
	INSIST(ctx->stats[size].gets != 0);
	ctx->stats[size].gets--;
	ctx->stats[new_size].freefrags++;
583
	ctx->inuse -= new_size;
Bob Halley's avatar
base  
Bob Halley committed
584
585
}

586
587
588
589
590
591
592
593
594
595
596
597
598
599
#else /* ISC_MEM_USE_INTERNAL_MALLOC */

/*
 * Perform a malloc, doing memory filling and overrun detection as necessary.
 */
static inline void *
mem_get(isc_mem_t *ctx, size_t size) {
	char *ret;

#if ISC_MEM_CHECKOVERRUN
	size += 1;
#endif

	ret = (ctx->memalloc)(ctx->arg, size);
600
601
	if (ret == NULL)
		ctx->memalloc_failures++;	
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621

#if ISC_MEM_FILL
	if (ret != NULL)
		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
#else
#  if ISC_MEM_CHECKOVERRUN
	if (ret != NULL)
		ret[size-1] = 0xbe;
#  endif
#endif

	return (ret);
}

/*
 * Perform a free, doing memory filling and overrun detection as necessary.
 */
static inline void
mem_put(isc_mem_t *ctx, void *mem, size_t size) {
#if ISC_MEM_CHECKOVERRUN
622
	INSIST(((unsigned char *)mem)[size] == 0xbe);
623
624
625
#endif
#if ISC_MEM_FILL
	memset(mem, 0xde, size); /* Mnemonic for "dead". */
Andreas Gustafsson's avatar
Andreas Gustafsson committed
626
627
#else
	UNUSED(size);
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
#endif
	(ctx->memfree)(ctx->arg, mem);
}

/*
 * Update internal counters after a memory get.
 */
static inline void
mem_getstats(isc_mem_t *ctx, size_t size) {
	ctx->total += size;
	ctx->inuse += size;

	if (size > ctx->max_size) {
		ctx->stats[ctx->max_size].gets++;
		ctx->stats[ctx->max_size].totalgets++;
	} else {
		ctx->stats[size].gets++;
		ctx->stats[size].totalgets++;
	}
}

/*
 * Update internal counters after a memory put.
 */
static inline void
mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
	UNUSED(ptr);

	INSIST(ctx->inuse >= size);
	ctx->inuse -= size;

	if (size > ctx->max_size) {
		INSIST(ctx->stats[ctx->max_size].gets > 0);
		ctx->stats[ctx->max_size].gets--;
	} else {
		INSIST(ctx->stats[size].gets > 0);
		ctx->stats[size].gets--;
	}
}

#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

David Lawrence's avatar
David Lawrence committed
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
/*
 * Private.
 */

static void *
default_memalloc(void *arg, size_t size) {
	UNUSED(arg);
	return (malloc(size));
}

static void
default_memfree(void *arg, void *ptr) {
	UNUSED(arg);
	free(ptr);
}

/*
 * Public.
 */

isc_result_t
isc_mem_createx(size_t init_max_size, size_t target_size,
		isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
		isc_mem_t **ctxp)
{
	isc_mem_t *ctx;
696
	isc_result_t result;
David Lawrence's avatar
David Lawrence committed
697
698
699
700
701

	REQUIRE(ctxp != NULL && *ctxp == NULL);
	REQUIRE(memalloc != NULL);
	REQUIRE(memfree != NULL);

702
703
	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);

704
705
706
707
#if !ISC_MEM_USE_INTERNAL_MALLOC
	UNUSED(target_size);
#endif

David Lawrence's avatar
David Lawrence committed
708
709
710
711
712
713
714
715
	ctx = (memalloc)(arg, sizeof *ctx);
	if (ctx == NULL)
		return (ISC_R_NOMEMORY);

	if (init_max_size == 0)
		ctx->max_size = DEF_MAX_SIZE;
	else
		ctx->max_size = init_max_size;
716
717
718
719
	ctx->references = 1;
	ctx->quota = 0;
	ctx->total = 0;
	ctx->inuse = 0;
720
	ctx->maxinuse = 0;
721
722
723
724
725
726
727
728
729
730
731
	ctx->hi_water = 0;
	ctx->lo_water = 0;
	ctx->hi_called = ISC_FALSE;
	ctx->water = NULL;
	ctx->water_arg = NULL;
	ctx->magic = MEM_MAGIC;
	isc_ondestroy_init(&ctx->ondestroy);
	ctx->memalloc = memalloc;
	ctx->memfree = memfree;
	ctx->arg = arg;
	ctx->stats = NULL;
732
	ctx->checkfree = ISC_TRUE;
733
#if ISC_MEM_TRACKLINES
734
	ctx->debuglist = NULL;
735
#endif
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
	ISC_LIST_INIT(ctx->pools);

#if ISC_MEM_USE_INTERNAL_MALLOC
	ctx->freelists = NULL;
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

	ctx->stats = (memalloc)(arg,
				(ctx->max_size+1) * sizeof (struct stats));
	if (ctx->stats == NULL) {
		result = ISC_R_NOMEMORY;
		goto error;
	}
	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof (struct stats));

#if ISC_MEM_USE_INTERNAL_MALLOC
David Lawrence's avatar
David Lawrence committed
751
752
753
754
755
756
	if (target_size == 0)
		ctx->mem_target = DEF_MEM_TARGET;
	else
		ctx->mem_target = target_size;
	ctx->freelists = (memalloc)(arg, ctx->max_size * sizeof (element *));
	if (ctx->freelists == NULL) {
757
758
		result = ISC_R_NOMEMORY;
		goto error;
David Lawrence's avatar
David Lawrence committed
759
760
761
762
763
764
765
766
767
	}
	memset(ctx->freelists, 0,
	       ctx->max_size * sizeof (element *));
	ctx->basic_blocks = NULL;
	ctx->basic_table = NULL;
	ctx->basic_table_count = 0;
	ctx->basic_table_size = 0;
	ctx->lowest = NULL;
	ctx->highest = NULL;
768
769
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

David Lawrence's avatar
David Lawrence committed
770
771
	if (isc_mutex_init(&ctx->lock) != ISC_R_SUCCESS) {
		UNEXPECTED_ERROR(__FILE__, __LINE__,
772
773
774
				 "isc_mutex_init() %s",
				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
						ISC_MSG_FAILED, "failed"));
775
776
		result = ISC_R_UNEXPECTED;
		goto error;
David Lawrence's avatar
David Lawrence committed
777
	}
778

779
#if ISC_MEM_TRACKLINES
780
781
782
783
784
785
786
787
788
789
790
791
	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
		unsigned int i;

		ctx->debuglist = (memalloc)(arg,
				      (ctx->max_size+1) * sizeof (debuglist_t));
		if (ctx->debuglist == NULL) {
			result = ISC_R_NOMEMORY;
			goto error;
		}
		for (i = 0; i <= ctx->max_size; i++)
			ISC_LIST_INIT(ctx->debuglist[i]);
	}
Michael Graff's avatar
Michael Graff committed
792
#endif
David Lawrence's avatar
David Lawrence committed
793

794
795
	ctx->memalloc_failures = 0;

David Lawrence's avatar
David Lawrence committed
796
797
	*ctxp = ctx;
	return (ISC_R_SUCCESS);
798
799
800
801
802
803
804
805
806

  error:
	if (ctx) {
		if (ctx->stats)
			(memfree)(arg, ctx->stats);
#if ISC_MEM_USE_INTERNAL_MALLOC
		if (ctx->freelists)
			(memfree)(arg, ctx->freelists);
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */
807
808
809
810
#if ISC_MEM_TRACKLINES
		if (ctx->debuglist)
			(ctx->memfree)(ctx->arg, ctx->debuglist);
#endif /* ISC_MEM_TRACKLINES */
811
812
813
814
		(memfree)(arg, ctx);
	}

	return (result);
David Lawrence's avatar
David Lawrence committed
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
}

isc_result_t
isc_mem_create(size_t init_max_size, size_t target_size,
	       isc_mem_t **ctxp)
{
	return (isc_mem_createx(init_max_size, target_size,
				default_memalloc, default_memfree, NULL,
				ctxp));
}

static void
destroy(isc_mem_t *ctx) {
	unsigned int i;
	isc_ondestroy_t ondest;

	ctx->magic = 0;

833
#if ISC_MEM_USE_INTERNAL_MALLOC
David Lawrence's avatar
David Lawrence committed
834
	INSIST(ISC_LIST_EMPTY(ctx->pools));
835
836
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

837
#if ISC_MEM_TRACKLINES
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
	if (ctx->debuglist != NULL) {
		if (ctx->checkfree) {
			for (i = 0; i <= ctx->max_size; i++) {
				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
					print_active(ctx, stderr);
				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
			}
		} else {
			debuglink_t *dl;

			for (i = 0; i <= ctx->max_size; i++)
				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
				     dl != NULL;
				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
					ISC_LIST_UNLINK(ctx->debuglist[i],
						 	dl, link);
					free(dl);
				}
856
		}
857
		(ctx->memfree)(ctx->arg, ctx->debuglist);
858
	}
Michael Graff's avatar
Michael Graff committed
859
#endif
David Lawrence's avatar
David Lawrence committed
860
861
862
	INSIST(ctx->references == 0);

	if (ctx->checkfree) {
863
864
865
866
867
		for (i = 0; i <= ctx->max_size; i++) {
#if ISC_MEM_TRACKLINES
			if (ctx->stats[i].gets != 0)
				print_active(ctx, stderr);
#endif
David Lawrence's avatar
David Lawrence committed
868
			INSIST(ctx->stats[i].gets == 0);
869
		}
David Lawrence's avatar
David Lawrence committed
870
871
	}

872
	(ctx->memfree)(ctx->arg, ctx->stats);
David Lawrence's avatar
David Lawrence committed
873

874
#if ISC_MEM_USE_INTERNAL_MALLOC
David Lawrence's avatar
David Lawrence committed
875
876
877
878
	for (i = 0; i < ctx->basic_table_count; i++)
		(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
	(ctx->memfree)(ctx->arg, ctx->freelists);
	(ctx->memfree)(ctx->arg, ctx->basic_table);
879
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */
David Lawrence's avatar
David Lawrence committed
880
881
882

	ondest = ctx->ondestroy;

883
	DESTROYLOCK(&ctx->lock);
David Lawrence's avatar
David Lawrence committed
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
	(ctx->memfree)(ctx->arg, ctx);

	isc_ondestroy_notify(&ondest, ctx);
}

void
isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
	REQUIRE(VALID_CONTEXT(source));
	REQUIRE(targetp != NULL && *targetp == NULL);

	LOCK(&source->lock);
	source->references++;
	UNLOCK(&source->lock);

	*targetp = source;
}

void
isc_mem_detach(isc_mem_t **ctxp) {
	isc_mem_t *ctx;
	isc_boolean_t want_destroy = ISC_FALSE;

	REQUIRE(ctxp != NULL);
	ctx = *ctxp;
	REQUIRE(VALID_CONTEXT(ctx));

	LOCK(&ctx->lock);
	INSIST(ctx->references > 0);
	ctx->references--;
	if (ctx->references == 0)
		want_destroy = ISC_TRUE;
	UNLOCK(&ctx->lock);

	if (want_destroy)
		destroy(ctx);

	*ctxp = NULL;
}

923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
/*
 * isc_mem_putanddetach() is the equivalent of:
 *
 * mctx = NULL;
 * isc_mem_attach(ptr->mctx, &mctx);
 * isc_mem_detach(&ptr->mctx);
 * isc_mem_put(mctx, ptr, sizeof(*ptr);
 * isc_mem_detach(&mctx);
 */

void
isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
	isc_mem_t *ctx;
	isc_boolean_t want_destroy = ISC_FALSE;

	REQUIRE(ctxp != NULL);
	ctx = *ctxp;
	REQUIRE(VALID_CONTEXT(ctx));
	REQUIRE(ptr != NULL);

	/*
	 * Must be before mem_putunlocked() as ctxp is usually within
	 * [ptr..ptr+size).
	 */
	*ctxp = NULL;

949
#if ISC_MEM_USE_INTERNAL_MALLOC
950
951
	LOCK(&ctx->lock);
	mem_putunlocked(ctx, ptr, size);
952
953
954
955
956
957
958
#else /* ISC_MEM_USE_INTERNAL_MALLOC */
	mem_put(ctx, ptr, size);
	LOCK(&ctx->lock);
	mem_putstats(ctx, ptr, size);
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

	DELETE_TRACE(ctx, ptr, size, file, line);
959
960
961
962
	INSIST(ctx->references > 0);
	ctx->references--;
	if (ctx->references == 0)
		want_destroy = ISC_TRUE;
963

964
965
966
967
968
969
	UNLOCK(&ctx->lock);

	if (want_destroy)
		destroy(ctx);
}

David Lawrence's avatar
David Lawrence committed
970
971
972
973
974
975
976
977
978
979
980
981
982
983
void
isc_mem_destroy(isc_mem_t **ctxp) {
	isc_mem_t *ctx;

	/*
	 * This routine provides legacy support for callers who use mctxs
	 * without attaching/detaching.
	 */

	REQUIRE(ctxp != NULL);
	ctx = *ctxp;
	REQUIRE(VALID_CONTEXT(ctx));

	LOCK(&ctx->lock);
984
#if ISC_MEM_TRACKLINES
985
986
987
	if (ctx->references != 1)
		print_active(ctx, stderr);
#endif
David Lawrence's avatar
David Lawrence committed
988
989
990
991
	REQUIRE(ctx->references == 1);
	ctx->references--;
	UNLOCK(&ctx->lock);

Brian Wellington's avatar
Brian Wellington committed
992
	destroy(ctx);
David Lawrence's avatar
David Lawrence committed
993
994
995
996
997
998
999

	*ctxp = NULL;
}

isc_result_t
isc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) {
	isc_result_t res;
1000

David Lawrence's avatar
David Lawrence committed
1001
1002
1003
1004
1005
1006
1007
1008
	LOCK(&ctx->lock);
	res = isc_ondestroy_register(&ctx->ondestroy, task, event);
	UNLOCK(&ctx->lock);

	return (res);
}


Michael Graff's avatar
Michael Graff committed
1009
1010
1011
void *
isc__mem_get(isc_mem_t *ctx, size_t size FLARG) {
	void *ptr;
1012
	isc_boolean_t call_water = ISC_FALSE;
Michael Graff's avatar
Michael Graff committed
1013

David Lawrence's avatar
David Lawrence committed
1014
1015
	REQUIRE(VALID_CONTEXT(ctx));

1016
#if ISC_MEM_USE_INTERNAL_MALLOC
David Lawrence's avatar
David Lawrence committed
1017
	LOCK(&ctx->lock);
Michael Graff's avatar
Michael Graff committed
1018
	ptr = mem_getunlocked(ctx, size);
1019
1020
1021
#else /* ISC_MEM_USE_INTERNAL_MALLOC */
	ptr = mem_get(ctx, size);
	LOCK(&ctx->lock);
1022
1023
	if (ptr)
		mem_getstats(ctx, size);
1024
1025
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

Michael Graff's avatar
Michael Graff committed
1026
	ADD_TRACE(ctx, ptr, size, file, line);
1027
1028
1029
1030
1031
	if (ctx->hi_water != 0 && !ctx->hi_called &&
	    ctx->inuse > ctx->hi_water) {
		ctx->hi_called = ISC_TRUE;
		call_water = ISC_TRUE;
	}
1032
1033
1034
1035
1036
1037
	if (ctx->inuse > ctx->maxinuse) {
		ctx->maxinuse = ctx->inuse;
		if (ctx->hi_water != 0 && ctx->inuse > ctx->hi_water &&
		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
			fprintf(stderr, "maxinuse = %d\n", ctx->inuse);
	}
David Lawrence's avatar
David Lawrence committed
1038
	UNLOCK(&ctx->lock);
1039
1040
1041
1042
1043

	if (call_water) {
		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
	}

Bob Halley's avatar
base  
Bob Halley committed
1044
1045
1046
1047
	return (ptr);
}

void
Michael Graff's avatar
Michael Graff committed
1048
isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG)
Bob Halley's avatar
base  
Bob Halley committed
1049
{
1050
1051
	isc_boolean_t call_water = ISC_FALSE;

Michael Graff's avatar
Michael Graff committed
1052
1053
1054
	REQUIRE(VALID_CONTEXT(ctx));
	REQUIRE(ptr != NULL);

1055
#if ISC_MEM_USE_INTERNAL_MALLOC
Michael Graff's avatar
Michael Graff committed
1056
1057
	LOCK(&ctx->lock);
	mem_putunlocked(ctx, ptr, size);
1058
1059
1060
1061
1062
1063
1064
#else /* ISC_MEM_USE_INTERNAL_MALLOC */
	mem_put(ctx, ptr, size);
	LOCK(&ctx->lock);
	mem_putstats(ctx, ptr, size);
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

	DELETE_TRACE(ctx, ptr, size, file, line);
1065
1066
1067
1068
1069
1070
1071
1072

	/*
	 * The check against ctx->lo_water == 0 is for the condition
	 * when the context was pushed over hi_water but then had
	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
	 */
	if (ctx->hi_called && 
	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0)) {
1073
		ctx->hi_called = ISC_FALSE;
1074
1075
1076

		if (ctx->water != NULL)
			call_water = ISC_TRUE;
1077
	}
Michael Graff's avatar
Michael Graff committed
1078
	UNLOCK(&ctx->lock);
1079
1080
1081
1082

	if (call_water) {
		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
	}
Bob Halley's avatar
base  
Bob Halley committed
1083
1084
}

1085
#if ISC_MEM_TRACKLINES
1086
static void
1087
print_active(isc_mem_t *mctx, FILE *out) {
1088
	if (mctx->debuglist != NULL) {
1089
		debuglink_t *dl;
1090
1091
1092
		unsigned int i, j;
		const char *format;
		isc_boolean_t found;
1093
1094
1095

		fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					    ISC_MSG_DUMPALLOC,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
1096
1097
					    "Dump of all outstanding "
					    "memory allocations:\n"));
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
		found = ISC_FALSE;
		format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				        ISC_MSG_PTRFILELINE,
					"\tptr %p file %s line %u\n");
		for (i = 0; i <= mctx->max_size; i++) {
			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
			
			if (dl != NULL)
				found = ISC_TRUE;

			while (dl != NULL) {
				for (j = 0 ; j < DEBUGLIST_COUNT ; j++)
					if (dl->ptr[j] != NULL)
						fprintf(out, format,
							dl->ptr[j], dl->file[j],
							dl->line[j]);
				dl = ISC_LIST_NEXT(dl, link);
			}
1116
		}
1117
1118
1119
		if (!found)
			fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
						    ISC_MSG_NONE, "\tNone.\n"));
1120
1121
1122
1123
1124
1125
1126
	}
}
#endif

/*
 * Print the stats[] on the stream "out" with suitable formatting.
 */
Bob Halley's avatar
base  
Bob Halley committed
1127
void
1128
isc_mem_stats(isc_mem_t *ctx, FILE *out) {
Bob Halley's avatar
base  
Bob Halley committed
1129
	size_t i;
Michael Graff's avatar
Michael Graff committed
1130
1131
	const struct stats *s;
	const isc_mempool_t *pool;
Bob Halley's avatar
base  
Bob Halley committed
1132

Bob Halley's avatar
Bob Halley committed
1133
1134
	REQUIRE(VALID_CONTEXT(ctx));
	LOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
1135

1136
1137
	for (i = 0; i <= ctx->max_size; i++) {
		s = &ctx->stats[i];
Michael Graff's avatar
Michael Graff committed
1138

1139
1140
1141
1142
1143
		if (s->totalgets == 0 && s->gets == 0)
			continue;
		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
			(i == ctx->max_size) ? ">=" : "  ",
			(unsigned long) i, s->totalgets, s->gets);
1144
#if ISC_MEM_USE_INTERNAL_MALLOC
1145
1146
1147
		if (s->blocks != 0 || s->freefrags != 0)
			fprintf(out, " (%lu bl, %lu ff)",
				s->blocks, s->freefrags);
1148
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */
1149
		fputc('\n', out);
Michael Graff's avatar
Michael Graff committed
1150
1151
	}

Michael Graff's avatar
Michael Graff committed
1152
1153
1154
1155
1156
1157
1158
	/*
	 * Note that since a pool can be locked now, these stats might be
	 * somewhat off if the pool is in active use at the time the stats
	 * are dumped.  The link fields are protected by the isc_mem_t's
	 * lock, however, so walking this list and extracting integers from
	 * stats fields is always safe.
	 */
Michael Graff's avatar
Michael Graff committed
1159
1160
	pool = ISC_LIST_HEAD(ctx->pools);
	if (pool != NULL) {
1161
1162
1163
		fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
					    ISC_MSG_POOLSTATS,
					    "[Pool statistics]\n"));
Michael Graff's avatar
Michael Graff committed
1164
		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLNAME, "name"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLSIZE, "size"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLMAXALLOC, "maxalloc"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLALLOCATED, "allocated"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLFREECOUNT, "freecount"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLFREEMAX, "freemax"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLFILLCOUNT, "fillcount"),
			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
				       ISC_MSG_POOLGETS, "gets"),
			"L");
Michael Graff's avatar
Michael Graff committed
1182
1183
	}
	while (pool != NULL) {
1184
1185
		fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
			pool->name, (unsigned long) pool->size, pool->maxalloc,
Michael Graff's avatar
Michael Graff committed
1186
1187
			pool->allocated, pool->freecount, pool->freemax,
			pool->fillcount, pool->gets,
Michael Graff's avatar
Michael Graff committed
1188
			(pool->lock == NULL ? "N" : "Y"));
Michael Graff's avatar
Michael Graff committed
1189
		pool = ISC_LIST_NEXT(pool, link);
Bob Halley's avatar
base  
Bob Halley committed
1190
1191
	}

1192
#if ISC_MEM_TRACKLINES
1193
	print_active(ctx, out);
Michael Graff's avatar
Michael Graff committed
1194
1195
#endif

Bob Halley's avatar
Bob Halley committed
1196
	UNLOCK(&ctx->lock);
Bob Halley's avatar
base  
Bob Halley committed
1197
1198
1199
}

/*
1200
1201
 * Replacements for malloc() and free() -- they implicitly remember the
 * size of the object allocated (with some additional overhead).
Bob Halley's avatar
base  
Bob Halley committed
1202
1203
 */

Michael Graff's avatar
Michael Graff committed
1204
1205
static void *
isc__mem_allocateunlocked(isc_mem_t *ctx, size_t size) {
Bob Halley's avatar
Bob Halley committed
1206
	size_info *si;
Bob Halley's avatar
base  
Bob Halley committed
1207
1208

	size += ALIGNMENT_SIZE;
1209
#if ISC_MEM_USE_INTERNAL_MALLOC
Michael Graff's avatar
Michael Graff committed
1210
	si = mem_getunlocked(ctx, size);
1211
1212
1213
#else /* ISC_MEM_USE_INTERNAL_MALLOC */
	si = mem_get(ctx, size);
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */
Bob Halley's avatar
base  
Bob Halley committed
1214
1215
	if (si == NULL)
		return (NULL);
1216
	si->u.size = size;
Bob Halley's avatar
base  
Bob Halley committed
1217
1218
1219
	return (&si[1]);
}

1220
void *
Michael Graff's avatar
Michael Graff committed
1221
isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
1222
1223
	size_info *si;

Michael Graff's avatar
Michael Graff committed
1224
	REQUIRE(VALID_CONTEXT(ctx));
1225

1226
#if ISC_MEM_USE_INTERNAL_MALLOC
Michael Graff's avatar
Michael Graff committed
1227
1228
	LOCK(&ctx->lock);
	si = isc__mem_allocateunlocked(ctx, size);
1229
1230
1231
#else /* ISC_MEM_USE_INTERNAL_MALLOC */
	si = isc__mem_allocateunlocked(ctx, size);
	LOCK(&ctx->lock);
1232
1233
	if (si != NULL)
		mem_getstats(ctx, si[-1].u.size);
1234
1235
#endif /* ISC_MEM_USE_INTERNAL_MALLOC */

1236
#if ISC_MEM_TRACKLINES
1237
	ADD_TRACE(ctx, si, si[-1].u.size, file, line);
Michael Graff's avatar
Michael Graff committed
1238
#endif
1239

Michael Graff's avatar
Michael Graff committed
1240
	UNLOCK(&ctx->lock);
1241

Michael Graff's avatar
Michael Graff committed
1242
	return (si);
1243
1244
}

Bob Halley's avatar
base  
Bob Halley committed
1245
void
Michael Graff's avatar
Michael Graff committed
1246
isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) {
Bob Halley's avatar
Bob Halley committed
1247
	size_info *si;
1248
	size_t size;
Bob Halley's avatar
base  
Bob Halley committed
1249

Michael Graff's avatar
Michael Graff committed
1250
1251
1252
	REQUIRE(VALID_CONTEXT(ctx));
	REQUIRE(ptr != NULL);

Bob Halley's avatar
Bob Halley committed
1253
	si = &(((size_info *)ptr)[-1]);
1254
1255
1256
1257
1258
1259
1260
	size = si->u.size;

#if ISC_MEM_USE_INTERNAL_MALLOC
	LOCK(&ctx->lock);
	mem_putunlocked(ctx, si, size);
#else /* ISC_MEM_USE_INTERNAL_MALLOC */
	mem_put(ctx, si, size);
Michael Graff's avatar
Michael Graff committed
1261
	LOCK(&ctx->lock);
1262
1263
1264
	mem_putstats(ctx, si, size