xref: /netbsd-src/external/bsd/libbind/dist/isc/memcluster.c (revision 5bbd2a12505d72a8177929a37b5cee489d0a1cfd)
1*5bbd2a12Schristos /*	$NetBSD: memcluster.c,v 1.1.1.2 2012/09/09 16:08:02 christos Exp $	*/
2b5677b36Schristos 
3b5677b36Schristos /*
4b5677b36Schristos  * Copyright (c) 2005 by Internet Systems Consortium, Inc. ("ISC")
5b5677b36Schristos  * Copyright (c) 1997,1999 by Internet Software Consortium.
6b5677b36Schristos  *
7b5677b36Schristos  * Permission to use, copy, modify, and distribute this software for any
8b5677b36Schristos  * purpose with or without fee is hereby granted, provided that the above
9b5677b36Schristos  * copyright notice and this permission notice appear in all copies.
10b5677b36Schristos  *
11b5677b36Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12b5677b36Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b5677b36Schristos  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14b5677b36Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b5677b36Schristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b5677b36Schristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17b5677b36Schristos  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b5677b36Schristos  */
19b5677b36Schristos 
20b5677b36Schristos 
21b5677b36Schristos /* When this symbol is defined allocations via memget are made slightly
22b5677b36Schristos    bigger and some debugging info stuck before and after the region given
23b5677b36Schristos    back to the caller. */
24b5677b36Schristos /* #define DEBUGGING_MEMCLUSTER */
25b5677b36Schristos #define MEMCLUSTER_ATEND
26b5677b36Schristos 
27b5677b36Schristos 
28b5677b36Schristos #if !defined(LINT) && !defined(CODECENTER)
29b5677b36Schristos static const char rcsid[] = "Id: memcluster.c,v 1.11 2006/08/30 23:34:38 marka Exp ";
30b5677b36Schristos #endif /* not lint */
31b5677b36Schristos 
32b5677b36Schristos #include "port_before.h"
33b5677b36Schristos 
34b5677b36Schristos #include <sys/types.h>
35b5677b36Schristos #include <sys/uio.h>
36b5677b36Schristos #include <sys/param.h>
37b5677b36Schristos #include <sys/stat.h>
38b5677b36Schristos 
39b5677b36Schristos #include <netinet/in.h>
40b5677b36Schristos #include <arpa/inet.h>
41b5677b36Schristos #include <arpa/nameser.h>
42b5677b36Schristos 
43b5677b36Schristos #include <errno.h>
44b5677b36Schristos #include <stdio.h>
45b5677b36Schristos #include <stdlib.h>
46b5677b36Schristos #include <string.h>
47b5677b36Schristos #include <time.h>
48b5677b36Schristos 
49b5677b36Schristos #include <isc/memcluster.h>
50b5677b36Schristos #include <isc/assertions.h>
51b5677b36Schristos 
52b5677b36Schristos #include "port_after.h"
53b5677b36Schristos 
54b5677b36Schristos #ifdef MEMCLUSTER_RECORD
55b5677b36Schristos #ifndef DEBUGGING_MEMCLUSTER
56b5677b36Schristos #define DEBUGGING_MEMCLUSTER
57b5677b36Schristos #endif
58b5677b36Schristos #endif
59b5677b36Schristos 
60b5677b36Schristos #define DEF_MAX_SIZE		1100
61b5677b36Schristos #define DEF_MEM_TARGET		4096
62b5677b36Schristos 
63b5677b36Schristos typedef u_int32_t fence_t;
64b5677b36Schristos 
65b5677b36Schristos typedef struct {
66b5677b36Schristos 	void *			next;
67b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
68b5677b36Schristos #if defined(MEMCLUSTER_RECORD)
69b5677b36Schristos 	const char *		file;
70b5677b36Schristos 	int			line;
71b5677b36Schristos #endif
72b5677b36Schristos 	size_t			size;
73b5677b36Schristos 	fence_t			fencepost;
74b5677b36Schristos #endif
75b5677b36Schristos } memcluster_element;
76b5677b36Schristos 
77b5677b36Schristos #define SMALL_SIZE_LIMIT sizeof(memcluster_element)
78b5677b36Schristos #define P_SIZE sizeof(void *)
79b5677b36Schristos #define FRONT_FENCEPOST 0xfebafeba
80b5677b36Schristos #define BACK_FENCEPOST 0xabefabef
81b5677b36Schristos #define FENCEPOST_SIZE 4
82b5677b36Schristos 
83b5677b36Schristos #ifndef MEMCLUSTER_LITTLE_MALLOC
84b5677b36Schristos #define MEMCLUSTER_BIG_MALLOC 1
85b5677b36Schristos #define NUM_BASIC_BLOCKS 64
86b5677b36Schristos #endif
87b5677b36Schristos 
88b5677b36Schristos struct stats {
89b5677b36Schristos 	u_long			gets;
90b5677b36Schristos 	u_long			totalgets;
91b5677b36Schristos 	u_long			blocks;
92b5677b36Schristos 	u_long			freefrags;
93b5677b36Schristos };
94b5677b36Schristos 
95b5677b36Schristos #ifdef DO_PTHREADS
96b5677b36Schristos #include <pthread.h>
97b5677b36Schristos static pthread_mutex_t	memlock = PTHREAD_MUTEX_INITIALIZER;
98b5677b36Schristos #define MEMLOCK		(void)pthread_mutex_lock(&memlock)
99b5677b36Schristos #define MEMUNLOCK	(void)pthread_mutex_unlock(&memlock)
100b5677b36Schristos #else
101b5677b36Schristos /*
102b5677b36Schristos  * Catch bad lock usage in non threaded build.
103b5677b36Schristos  */
104b5677b36Schristos static unsigned int	memlock = 0;
105b5677b36Schristos #define MEMLOCK		do { INSIST(memlock == 0); memlock = 1; } while (0)
106b5677b36Schristos #define MEMUNLOCK	do { INSIST(memlock == 1); memlock = 0; } while (0)
107b5677b36Schristos #endif  /* DO_PTHEADS */
108b5677b36Schristos 
109b5677b36Schristos /* Private data. */
110b5677b36Schristos 
111b5677b36Schristos static size_t			max_size;
112b5677b36Schristos static size_t			mem_target;
113b5677b36Schristos #ifndef MEMCLUSTER_BIG_MALLOC
114b5677b36Schristos static size_t			mem_target_half;
115b5677b36Schristos static size_t			mem_target_fudge;
116b5677b36Schristos #endif
117b5677b36Schristos static memcluster_element **	freelists;
118b5677b36Schristos #ifdef MEMCLUSTER_RECORD
119b5677b36Schristos static memcluster_element **	activelists;
120b5677b36Schristos #endif
121b5677b36Schristos #ifdef MEMCLUSTER_BIG_MALLOC
122b5677b36Schristos static memcluster_element *	basic_blocks;
123b5677b36Schristos #endif
124b5677b36Schristos static struct stats *		stats;
125b5677b36Schristos 
126b5677b36Schristos /* Forward. */
127b5677b36Schristos 
128b5677b36Schristos static size_t			quantize(size_t);
129b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
130b5677b36Schristos static void			check(unsigned char *, int, size_t);
131b5677b36Schristos #endif
132b5677b36Schristos 
133b5677b36Schristos /* Public. */
134b5677b36Schristos 
135b5677b36Schristos int
meminit(size_t init_max_size,size_t target_size)136b5677b36Schristos meminit(size_t init_max_size, size_t target_size) {
137b5677b36Schristos 
138b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
139b5677b36Schristos 	INSIST(sizeof(fence_t) == FENCEPOST_SIZE);
140b5677b36Schristos #endif
141b5677b36Schristos 	if (freelists != NULL) {
142b5677b36Schristos 		errno = EEXIST;
143b5677b36Schristos 		return (-1);
144b5677b36Schristos 	}
145b5677b36Schristos 	if (init_max_size == 0U)
146b5677b36Schristos 		max_size = DEF_MAX_SIZE;
147b5677b36Schristos 	else
148b5677b36Schristos 		max_size = init_max_size;
149b5677b36Schristos 	if (target_size == 0U)
150b5677b36Schristos 		mem_target = DEF_MEM_TARGET;
151b5677b36Schristos 	else
152b5677b36Schristos 		mem_target = target_size;
153b5677b36Schristos #ifndef MEMCLUSTER_BIG_MALLOC
154b5677b36Schristos 	mem_target_half = mem_target / 2;
155b5677b36Schristos 	mem_target_fudge = mem_target + mem_target / 4;
156b5677b36Schristos #endif
157b5677b36Schristos 	freelists = malloc(max_size * sizeof (memcluster_element *));
158b5677b36Schristos 	stats = malloc((max_size+1) * sizeof (struct stats));
159b5677b36Schristos 	if (freelists == NULL || stats == NULL) {
160b5677b36Schristos 		errno = ENOMEM;
161b5677b36Schristos 		return (-1);
162b5677b36Schristos 	}
163b5677b36Schristos 	memset(freelists, 0,
164b5677b36Schristos 	       max_size * sizeof (memcluster_element *));
165b5677b36Schristos 	memset(stats, 0, (max_size + 1) * sizeof (struct stats));
166b5677b36Schristos #ifdef MEMCLUSTER_RECORD
167b5677b36Schristos 	activelists = malloc((max_size + 1) * sizeof (memcluster_element *));
168b5677b36Schristos 	if (activelists == NULL) {
169b5677b36Schristos 		errno = ENOMEM;
170b5677b36Schristos 		return (-1);
171b5677b36Schristos 	}
172b5677b36Schristos 	memset(activelists, 0,
173b5677b36Schristos 	       (max_size + 1) * sizeof (memcluster_element *));
174b5677b36Schristos #endif
175b5677b36Schristos #ifdef MEMCLUSTER_BIG_MALLOC
176b5677b36Schristos 	basic_blocks = NULL;
177b5677b36Schristos #endif
178b5677b36Schristos 	return (0);
179b5677b36Schristos }
180b5677b36Schristos 
181b5677b36Schristos void *
__memget(size_t size)182b5677b36Schristos __memget(size_t size) {
183b5677b36Schristos 	return (__memget_record(size, NULL, 0));
184b5677b36Schristos }
185b5677b36Schristos 
186b5677b36Schristos void *
__memget_record(size_t size,const char * file,int line)187b5677b36Schristos __memget_record(size_t size, const char *file, int line) {
188b5677b36Schristos 	size_t new_size = quantize(size);
189b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
190b5677b36Schristos 	memcluster_element *e;
191b5677b36Schristos 	char *p;
192b5677b36Schristos 	fence_t fp = BACK_FENCEPOST;
193b5677b36Schristos #endif
194b5677b36Schristos 	void *ret;
195b5677b36Schristos 
196b5677b36Schristos 	MEMLOCK;
197b5677b36Schristos 
198b5677b36Schristos #if !defined(MEMCLUSTER_RECORD)
199b5677b36Schristos 	UNUSED(file);
200b5677b36Schristos 	UNUSED(line);
201b5677b36Schristos #endif
202b5677b36Schristos 	if (freelists == NULL) {
203b5677b36Schristos 		if (meminit(0, 0) == -1) {
204b5677b36Schristos 			MEMUNLOCK;
205b5677b36Schristos 			return (NULL);
206b5677b36Schristos 		}
207b5677b36Schristos 	}
208b5677b36Schristos 	if (size == 0U) {
209b5677b36Schristos 		MEMUNLOCK;
210b5677b36Schristos 		errno = EINVAL;
211b5677b36Schristos 		return (NULL);
212b5677b36Schristos 	}
213b5677b36Schristos 	if (size >= max_size || new_size >= max_size) {
214b5677b36Schristos 		/* memget() was called on something beyond our upper limit. */
215b5677b36Schristos 		stats[max_size].gets++;
216b5677b36Schristos 		stats[max_size].totalgets++;
217b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
218b5677b36Schristos 		e = malloc(new_size);
219b5677b36Schristos 		if (e == NULL) {
220b5677b36Schristos 			MEMUNLOCK;
221b5677b36Schristos 			errno = ENOMEM;
222b5677b36Schristos 			return (NULL);
223b5677b36Schristos 		}
224b5677b36Schristos 		e->next = NULL;
225b5677b36Schristos 		e->size = size;
226b5677b36Schristos #ifdef MEMCLUSTER_RECORD
227b5677b36Schristos 		e->file = file;
228b5677b36Schristos 		e->line = line;
229b5677b36Schristos 		e->next = activelists[max_size];
230b5677b36Schristos 		activelists[max_size] = e;
231b5677b36Schristos #endif
232b5677b36Schristos 		MEMUNLOCK;
233b5677b36Schristos 		e->fencepost = FRONT_FENCEPOST;
234b5677b36Schristos 		p = (char *)e + sizeof *e + size;
235b5677b36Schristos 		memcpy(p, &fp, sizeof fp);
236b5677b36Schristos 		return ((char *)e + sizeof *e);
237b5677b36Schristos #else
238b5677b36Schristos 		MEMUNLOCK;
239b5677b36Schristos 		return (malloc(size));
240b5677b36Schristos #endif
241b5677b36Schristos 	}
242b5677b36Schristos 
243b5677b36Schristos 	/*
244b5677b36Schristos 	 * If there are no blocks in the free list for this size, get a chunk
245b5677b36Schristos 	 * of memory and then break it up into "new_size"-sized blocks, adding
246b5677b36Schristos 	 * them to the free list.
247b5677b36Schristos 	 */
248b5677b36Schristos 	if (freelists[new_size] == NULL) {
249b5677b36Schristos 		int i, frags;
250b5677b36Schristos 		size_t total_size;
251b5677b36Schristos 		void *new;
252b5677b36Schristos 		char *curr, *next;
253b5677b36Schristos 
254b5677b36Schristos #ifdef MEMCLUSTER_BIG_MALLOC
255b5677b36Schristos 		if (basic_blocks == NULL) {
256b5677b36Schristos 			new = malloc(NUM_BASIC_BLOCKS * mem_target);
257b5677b36Schristos 			if (new == NULL) {
258b5677b36Schristos 				MEMUNLOCK;
259b5677b36Schristos 				errno = ENOMEM;
260b5677b36Schristos 				return (NULL);
261b5677b36Schristos 			}
262b5677b36Schristos 			curr = new;
263b5677b36Schristos 			next = curr + mem_target;
264b5677b36Schristos 			for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
265b5677b36Schristos 				((memcluster_element *)curr)->next = next;
266b5677b36Schristos 				curr = next;
267b5677b36Schristos 				next += mem_target;
268b5677b36Schristos 			}
269b5677b36Schristos 			/*
270b5677b36Schristos 			 * curr is now pointing at the last block in the
271b5677b36Schristos 			 * array.
272b5677b36Schristos 			 */
273b5677b36Schristos 			((memcluster_element *)curr)->next = NULL;
274b5677b36Schristos 			basic_blocks = new;
275b5677b36Schristos 		}
276b5677b36Schristos 		total_size = mem_target;
277b5677b36Schristos 		new = basic_blocks;
278b5677b36Schristos 		basic_blocks = basic_blocks->next;
279b5677b36Schristos #else
280b5677b36Schristos 		if (new_size > mem_target_half)
281b5677b36Schristos 			total_size = mem_target_fudge;
282b5677b36Schristos 		else
283b5677b36Schristos 			total_size = mem_target;
284b5677b36Schristos 		new = malloc(total_size);
285b5677b36Schristos 		if (new == NULL) {
286b5677b36Schristos 			MEMUNLOCK;
287b5677b36Schristos 			errno = ENOMEM;
288b5677b36Schristos 			return (NULL);
289b5677b36Schristos 		}
290b5677b36Schristos #endif
291b5677b36Schristos 		frags = total_size / new_size;
292b5677b36Schristos 		stats[new_size].blocks++;
293b5677b36Schristos 		stats[new_size].freefrags += frags;
294b5677b36Schristos 		/* Set up a linked-list of blocks of size "new_size". */
295b5677b36Schristos 		curr = new;
296b5677b36Schristos 		next = curr + new_size;
297b5677b36Schristos 		for (i = 0; i < (frags - 1); i++) {
298b5677b36Schristos #if defined (DEBUGGING_MEMCLUSTER)
299b5677b36Schristos 			memset(curr, 0xa5, new_size);
300b5677b36Schristos #endif
301b5677b36Schristos 			((memcluster_element *)curr)->next = next;
302b5677b36Schristos 			curr = next;
303b5677b36Schristos 			next += new_size;
304b5677b36Schristos 		}
305b5677b36Schristos 		/* curr is now pointing at the last block in the array. */
306b5677b36Schristos #if defined (DEBUGGING_MEMCLUSTER)
307b5677b36Schristos 		memset(curr, 0xa5, new_size);
308b5677b36Schristos #endif
309b5677b36Schristos 		((memcluster_element *)curr)->next = freelists[new_size];
310b5677b36Schristos 		freelists[new_size] = new;
311b5677b36Schristos 	}
312b5677b36Schristos 
313b5677b36Schristos 	/* The free list uses the "rounded-up" size "new_size". */
314b5677b36Schristos #if defined (DEBUGGING_MEMCLUSTER)
315b5677b36Schristos 	e = freelists[new_size];
316b5677b36Schristos 	ret = (char *)e + sizeof *e;
317b5677b36Schristos 	/*
318b5677b36Schristos 	 * Check to see if this buffer has been written to while on free list.
319b5677b36Schristos 	 */
320b5677b36Schristos 	check(ret, 0xa5, new_size - sizeof *e);
321b5677b36Schristos 	/*
322b5677b36Schristos 	 * Mark memory we are returning.
323b5677b36Schristos 	 */
324b5677b36Schristos 	memset(ret, 0xe5, size);
325b5677b36Schristos #else
326b5677b36Schristos 	ret = freelists[new_size];
327b5677b36Schristos #endif
328b5677b36Schristos 	freelists[new_size] = freelists[new_size]->next;
329b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
330b5677b36Schristos 	e->next = NULL;
331b5677b36Schristos 	e->size = size;
332b5677b36Schristos 	e->fencepost = FRONT_FENCEPOST;
333b5677b36Schristos #ifdef MEMCLUSTER_RECORD
334b5677b36Schristos 	e->file = file;
335b5677b36Schristos 	e->line = line;
336b5677b36Schristos 	e->next = activelists[size];
337b5677b36Schristos 	activelists[size] = e;
338b5677b36Schristos #endif
339b5677b36Schristos 	p = (char *)e + sizeof *e + size;
340b5677b36Schristos 	memcpy(p, &fp, sizeof fp);
341b5677b36Schristos #endif
342b5677b36Schristos 
343b5677b36Schristos 	/*
344b5677b36Schristos 	 * The stats[] uses the _actual_ "size" requested by the
345b5677b36Schristos 	 * caller, with the caveat (in the code above) that "size" >= the
346b5677b36Schristos 	 * max. size (max_size) ends up getting recorded as a call to
347b5677b36Schristos 	 * max_size.
348b5677b36Schristos 	 */
349b5677b36Schristos 	stats[size].gets++;
350b5677b36Schristos 	stats[size].totalgets++;
351b5677b36Schristos 	stats[new_size].freefrags--;
352b5677b36Schristos 	MEMUNLOCK;
353b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
354b5677b36Schristos 	return ((char *)e + sizeof *e);
355b5677b36Schristos #else
356b5677b36Schristos 	return (ret);
357b5677b36Schristos #endif
358b5677b36Schristos }
359b5677b36Schristos 
360b5677b36Schristos /*%
361b5677b36Schristos  * This is a call from an external caller,
362b5677b36Schristos  * so we want to count this as a user "put".
363b5677b36Schristos  */
364b5677b36Schristos void
__memput(void * mem,size_t size)365b5677b36Schristos __memput(void *mem, size_t size) {
366b5677b36Schristos 	__memput_record(mem, size, NULL, 0);
367b5677b36Schristos }
368b5677b36Schristos 
369b5677b36Schristos void
__memput_record(void * mem,size_t size,const char * file,int line)370b5677b36Schristos __memput_record(void *mem, size_t size, const char *file, int line) {
371b5677b36Schristos 	size_t new_size = quantize(size);
372b5677b36Schristos #if defined (DEBUGGING_MEMCLUSTER)
373b5677b36Schristos 	memcluster_element *e;
374b5677b36Schristos 	memcluster_element *el;
375b5677b36Schristos #ifdef MEMCLUSTER_RECORD
376b5677b36Schristos 	memcluster_element *prev;
377b5677b36Schristos #endif
378b5677b36Schristos 	fence_t fp;
379b5677b36Schristos 	char *p;
380b5677b36Schristos #endif
381b5677b36Schristos 
382b5677b36Schristos 	MEMLOCK;
383b5677b36Schristos 
384b5677b36Schristos #if !defined (MEMCLUSTER_RECORD)
385b5677b36Schristos 	UNUSED(file);
386b5677b36Schristos 	UNUSED(line);
387b5677b36Schristos #endif
388b5677b36Schristos 
389b5677b36Schristos 	REQUIRE(freelists != NULL);
390b5677b36Schristos 
391b5677b36Schristos 	if (size == 0U) {
392b5677b36Schristos 		MEMUNLOCK;
393b5677b36Schristos 		errno = EINVAL;
394b5677b36Schristos 		return;
395b5677b36Schristos 	}
396b5677b36Schristos 
397b5677b36Schristos #if defined (DEBUGGING_MEMCLUSTER)
398b5677b36Schristos 	e = (memcluster_element *) ((char *)mem - sizeof *e);
399b5677b36Schristos 	INSIST(e->fencepost == FRONT_FENCEPOST);
400b5677b36Schristos 	INSIST(e->size == size);
401b5677b36Schristos 	p = (char *)e + sizeof *e + size;
402b5677b36Schristos 	memcpy(&fp, p, sizeof fp);
403b5677b36Schristos 	INSIST(fp == BACK_FENCEPOST);
404b5677b36Schristos 	INSIST(((u_long)mem % 4) == 0);
405b5677b36Schristos #ifdef MEMCLUSTER_RECORD
406b5677b36Schristos 	prev = NULL;
407b5677b36Schristos 	if (size == max_size || new_size >= max_size)
408b5677b36Schristos 		el = activelists[max_size];
409b5677b36Schristos 	else
410b5677b36Schristos 		el = activelists[size];
411b5677b36Schristos 	while (el != NULL && el != e) {
412b5677b36Schristos 		prev = el;
413b5677b36Schristos 		el = el->next;
414b5677b36Schristos 	}
415b5677b36Schristos 	INSIST(el != NULL);	/*%< double free */
416b5677b36Schristos 	if (prev == NULL) {
417b5677b36Schristos 		if (size == max_size || new_size >= max_size)
418b5677b36Schristos 			activelists[max_size] = el->next;
419b5677b36Schristos 		else
420b5677b36Schristos 			activelists[size] = el->next;
421b5677b36Schristos 	} else
422b5677b36Schristos 		prev->next = el->next;
423b5677b36Schristos #endif
424b5677b36Schristos #endif
425b5677b36Schristos 
426b5677b36Schristos 	if (size == max_size || new_size >= max_size) {
427b5677b36Schristos 		/* memput() called on something beyond our upper limit */
428b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
429b5677b36Schristos 		free(e);
430b5677b36Schristos #else
431b5677b36Schristos 		free(mem);
432b5677b36Schristos #endif
433b5677b36Schristos 
434b5677b36Schristos 		INSIST(stats[max_size].gets != 0U);
435b5677b36Schristos 		stats[max_size].gets--;
436b5677b36Schristos 		MEMUNLOCK;
437b5677b36Schristos 		return;
438b5677b36Schristos 	}
439b5677b36Schristos 
440b5677b36Schristos 	/* The free list uses the "rounded-up" size "new_size": */
441b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
442b5677b36Schristos 	memset(mem, 0xa5, new_size - sizeof *e); /*%< catch write after free */
443b5677b36Schristos 	e->size = 0;	/*%< catch double memput() */
444b5677b36Schristos #ifdef MEMCLUSTER_RECORD
445b5677b36Schristos 	e->file = file;
446b5677b36Schristos 	e->line = line;
447b5677b36Schristos #endif
448b5677b36Schristos #ifdef MEMCLUSTER_ATEND
449b5677b36Schristos 	e->next = NULL;
450b5677b36Schristos 	el = freelists[new_size];
451b5677b36Schristos 	while (el != NULL && el->next != NULL)
452b5677b36Schristos 		el = el->next;
453b5677b36Schristos 	if (el)
454b5677b36Schristos 		el->next = e;
455b5677b36Schristos 	else
456b5677b36Schristos 		freelists[new_size] = e;
457b5677b36Schristos #else
458b5677b36Schristos 	e->next = freelists[new_size];
459b5677b36Schristos 	freelists[new_size] = (void *)e;
460b5677b36Schristos #endif
461b5677b36Schristos #else
462b5677b36Schristos 	((memcluster_element *)mem)->next = freelists[new_size];
463b5677b36Schristos 	freelists[new_size] = (memcluster_element *)mem;
464b5677b36Schristos #endif
465b5677b36Schristos 
466b5677b36Schristos 	/*
467b5677b36Schristos 	 * The stats[] uses the _actual_ "size" requested by the
468b5677b36Schristos 	 * caller, with the caveat (in the code above) that "size" >= the
469b5677b36Schristos 	 * max. size (max_size) ends up getting recorded as a call to
470b5677b36Schristos 	 * max_size.
471b5677b36Schristos 	 */
472b5677b36Schristos 	INSIST(stats[size].gets != 0U);
473b5677b36Schristos 	stats[size].gets--;
474b5677b36Schristos 	stats[new_size].freefrags++;
475b5677b36Schristos 	MEMUNLOCK;
476b5677b36Schristos }
477b5677b36Schristos 
478b5677b36Schristos void *
__memget_debug(size_t size,const char * file,int line)479b5677b36Schristos __memget_debug(size_t size, const char *file, int line) {
480b5677b36Schristos 	void *ptr;
481b5677b36Schristos 	ptr = __memget_record(size, file, line);
482b5677b36Schristos 	fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line,
483b5677b36Schristos 		(u_long)size, ptr);
484b5677b36Schristos 	return (ptr);
485b5677b36Schristos }
486b5677b36Schristos 
487b5677b36Schristos void
__memput_debug(void * ptr,size_t size,const char * file,int line)488b5677b36Schristos __memput_debug(void *ptr, size_t size, const char *file, int line) {
489b5677b36Schristos 	fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, ptr,
490b5677b36Schristos 		(u_long)size);
491b5677b36Schristos 	__memput_record(ptr, size, file, line);
492b5677b36Schristos }
493b5677b36Schristos 
494b5677b36Schristos /*%
495b5677b36Schristos  * Print the stats[] on the stream "out" with suitable formatting.
496b5677b36Schristos  */
497b5677b36Schristos void
memstats(FILE * out)498b5677b36Schristos memstats(FILE *out) {
499b5677b36Schristos 	size_t i;
500b5677b36Schristos #ifdef MEMCLUSTER_RECORD
501b5677b36Schristos 	memcluster_element *e;
502b5677b36Schristos #endif
503b5677b36Schristos 
504b5677b36Schristos 	MEMLOCK;
505b5677b36Schristos 
506b5677b36Schristos 	if (freelists == NULL) {
507b5677b36Schristos 		MEMUNLOCK;
508b5677b36Schristos 		return;
509b5677b36Schristos 	}
510b5677b36Schristos 	for (i = 1; i <= max_size; i++) {
511b5677b36Schristos 		const struct stats *s = &stats[i];
512b5677b36Schristos 
513b5677b36Schristos 		if (s->totalgets == 0U && s->gets == 0U)
514b5677b36Schristos 			continue;
515b5677b36Schristos 		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
516b5677b36Schristos 			(i == max_size) ? ">=" : "  ",
517b5677b36Schristos 			(unsigned long)i, s->totalgets, s->gets);
518b5677b36Schristos 		if (s->blocks != 0U)
519b5677b36Schristos 			fprintf(out, " (%lu bl, %lu ff)",
520b5677b36Schristos 				s->blocks, s->freefrags);
521b5677b36Schristos 		fputc('\n', out);
522b5677b36Schristos 	}
523b5677b36Schristos #ifdef MEMCLUSTER_RECORD
524b5677b36Schristos 	fprintf(out, "Active Memory:\n");
525b5677b36Schristos 	for (i = 1; i <= max_size; i++) {
526b5677b36Schristos 		if ((e = activelists[i]) != NULL)
527b5677b36Schristos 			while (e != NULL) {
528b5677b36Schristos 				fprintf(out, "%s:%d %p:%lu\n",
529b5677b36Schristos 				        e->file != NULL ? e->file :
530b5677b36Schristos 						"<UNKNOWN>", e->line,
531b5677b36Schristos 					(char *)e + sizeof *e,
532b5677b36Schristos 					(u_long)e->size);
533b5677b36Schristos 				e = e->next;
534b5677b36Schristos 			}
535b5677b36Schristos 	}
536b5677b36Schristos #endif
537b5677b36Schristos 	MEMUNLOCK;
538b5677b36Schristos }
539b5677b36Schristos 
540b5677b36Schristos int
memactive(void)541b5677b36Schristos memactive(void) {
542b5677b36Schristos 	size_t i;
543b5677b36Schristos 
544b5677b36Schristos 	if (stats == NULL)
545b5677b36Schristos 		return (0);
546b5677b36Schristos 	for (i = 1; i <= max_size; i++)
547b5677b36Schristos 		if (stats[i].gets != 0U)
548b5677b36Schristos 			return (1);
549b5677b36Schristos 	return (0);
550b5677b36Schristos }
551b5677b36Schristos 
552b5677b36Schristos /* Private. */
553b5677b36Schristos 
554b5677b36Schristos /*%
555b5677b36Schristos  * Round up size to a multiple of sizeof(void *).  This guarantees that a
556b5677b36Schristos  * block is at least sizeof void *, and that we won't violate alignment
557b5677b36Schristos  * restrictions, both of which are needed to make lists of blocks.
558b5677b36Schristos  */
559b5677b36Schristos static size_t
quantize(size_t size)560b5677b36Schristos quantize(size_t size) {
561b5677b36Schristos 	int remainder;
562b5677b36Schristos 	/*
563b5677b36Schristos 	 * If there is no remainder for the integer division of
564b5677b36Schristos 	 *
565b5677b36Schristos 	 *	(rightsize/P_SIZE)
566b5677b36Schristos 	 *
567b5677b36Schristos 	 * then we already have a good size; if not, then we need
568b5677b36Schristos 	 * to round up the result in order to get a size big
569b5677b36Schristos 	 * enough to satisfy the request _and_ aligned on P_SIZE boundaries.
570b5677b36Schristos 	 */
571b5677b36Schristos 	remainder = size % P_SIZE;
572b5677b36Schristos 	if (remainder != 0)
573b5677b36Schristos 		size += P_SIZE - remainder;
574b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
575b5677b36Schristos 	return (size + SMALL_SIZE_LIMIT + sizeof (int));
576b5677b36Schristos #else
577b5677b36Schristos 	return (size);
578b5677b36Schristos #endif
579b5677b36Schristos }
580b5677b36Schristos 
581b5677b36Schristos #if defined(DEBUGGING_MEMCLUSTER)
582b5677b36Schristos static void
check(unsigned char * a,int value,size_t len)583b5677b36Schristos check(unsigned char *a, int value, size_t len) {
584b5677b36Schristos 	size_t i;
585b5677b36Schristos 	for (i = 0; i < len; i++)
586b5677b36Schristos 		INSIST(a[i] == value);
587b5677b36Schristos }
588b5677b36Schristos #endif
589b5677b36Schristos 
590b5677b36Schristos /*! \file */
591