xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_umem.c (revision 258e8624229ac7ff3af9890752a92cd251b83825)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * These routines simply provide wrappers around malloc(3C) and free(3C)
29  * for now.  In the future we hope to provide a userland equivalent to
30  * the kmem allocator, including cache allocators.
31  */
32 
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <poll.h>
36 
37 #ifdef _KMDB
38 #include <kmdb/kmdb_fault.h>
39 #endif
40 #include <mdb/mdb_debug.h>
41 #include <mdb/mdb_stdlib.h>
42 #include <mdb/mdb_frame.h>
43 #include <mdb/mdb_umem.h>
44 #include <mdb/mdb_err.h>
45 #include <mdb/mdb.h>
46 
47 #define	UMF_DEBUG			0x1
48 
49 #ifdef DEBUG
50 int mdb_umem_flags = UMF_DEBUG;
51 #else
52 int mdb_umem_flags = 0;
53 #endif
54 
55 struct mdb_mblk {
56 	void *blk_addr;			/* address of allocated block */
57 	size_t blk_size;		/* size of block in bytes */
58 	struct mdb_mblk *blk_next;	/* link to next block */
59 };
60 
61 /*ARGSUSED*/
62 static void *
mdb_umem_handler(size_t nbytes,size_t align,uint_t flags)63 mdb_umem_handler(size_t nbytes, size_t align, uint_t flags)
64 {
65 #ifdef _KMDB
66 
67 	/*
68 	 * kmdb has a fixed, dedicated VA range in which to play.  This range
69 	 * won't change size while the debugger is running, regardless of how
70 	 * long we wait.  As a result, the only sensible course of action is
71 	 * to fail the request.  If we're here, however, the request was made
72 	 * with UM_SLEEP.  The caller is thus not expecting a NULL back.  We'll
73 	 * have to fail the current dcmd set.
74 	 */
75 	if (mdb.m_depth > 0) {
76 		warn("failed to allocate %lu bytes -- recovering\n",
77 		    (ulong_t)nbytes);
78 
79 		kmdb_print_stack();
80 
81 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
82 	}
83 
84 #else
85 
86 	/*
87 	 * mdb, on the other hand, can afford to wait, as someone may actually
88 	 * free something.
89 	 */
90 	if (errno == EAGAIN) {
91 		void *ptr = NULL;
92 		char buf[64];
93 
94 		(void) mdb_iob_snprintf(buf, sizeof (buf),
95 		    "[ sleeping for %lu bytes of free memory ... ]",
96 		    (ulong_t)nbytes);
97 
98 		(void) mdb_iob_puts(mdb.m_err, buf);
99 		(void) mdb_iob_flush(mdb.m_err);
100 
101 		do {
102 			(void) poll(NULL, 0, 1000);
103 			if (align != 0)
104 				ptr = memalign(align, nbytes);
105 			else
106 				ptr = malloc(nbytes);
107 		} while (ptr == NULL && errno == EAGAIN);
108 
109 		if (ptr != NULL)
110 			return (ptr);
111 
112 		(void) memset(buf, '\b', strlen(buf));
113 		(void) mdb_iob_puts(mdb.m_err, buf);
114 		(void) mdb_iob_flush(mdb.m_err);
115 
116 		(void) memset(buf, ' ', strlen(buf));
117 		(void) mdb_iob_puts(mdb.m_err, buf);
118 		(void) mdb_iob_flush(mdb.m_err);
119 
120 		(void) memset(buf, '\b', strlen(buf));
121 		(void) mdb_iob_puts(mdb.m_err, buf);
122 		(void) mdb_iob_flush(mdb.m_err);
123 	}
124 #endif
125 
126 	die("failed to allocate %lu bytes -- terminating\n", (ulong_t)nbytes);
127 
128 	/*NOTREACHED*/
129 
130 	return (NULL);
131 }
132 
133 static void
mdb_umem_gc_enter(void * ptr,size_t nbytes)134 mdb_umem_gc_enter(void *ptr, size_t nbytes)
135 {
136 	mdb_mblk_t *blkp = mdb_alloc(sizeof (mdb_mblk_t), UM_SLEEP);
137 
138 	blkp->blk_addr = ptr;
139 	blkp->blk_size = nbytes;
140 	blkp->blk_next = mdb.m_frame->f_mblks;
141 
142 	mdb.m_frame->f_mblks = blkp;
143 }
144 
145 /*
146  * If we're compiled in debug mode, we use this function (gratuitously
147  * stolen from kmem.c) to set uninitialized and freed regions to
148  * special bit patterns.
149  */
150 static void
mdb_umem_copy_pattern(uint32_t pattern,void * buf_arg,size_t size)151 mdb_umem_copy_pattern(uint32_t pattern, void *buf_arg, size_t size)
152 {
153 	/* LINTED - alignment of bufend */
154 	uint32_t *bufend = (uint32_t *)((char *)buf_arg + size);
155 	uint32_t *buf = buf_arg;
156 
157 	while (buf < bufend - 3) {
158 		buf[3] = buf[2] = buf[1] = buf[0] = pattern;
159 		buf += 4;
160 	}
161 
162 	while (buf < bufend)
163 		*buf++ = pattern;
164 }
165 
166 void *
mdb_alloc_align(size_t nbytes,size_t align,uint_t flags)167 mdb_alloc_align(size_t nbytes, size_t align, uint_t flags)
168 {
169 	void *ptr;
170 	size_t obytes = nbytes;
171 
172 	if (nbytes == 0 || nbytes > MDB_ALLOC_MAX)
173 		return (NULL);
174 
175 	nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1);
176 	if (nbytes < obytes || nbytes == 0)
177 		return (NULL);
178 
179 	if (align != 0)
180 		ptr = memalign(align, nbytes);
181 	else
182 		ptr = malloc(nbytes);
183 
184 	if (flags & UM_SLEEP) {
185 		while (ptr == NULL)
186 			ptr = mdb_umem_handler(nbytes, align, flags);
187 	}
188 
189 	if (ptr != NULL && (mdb_umem_flags & UMF_DEBUG) != 0)
190 		mdb_umem_copy_pattern(UMEM_UNINITIALIZED_PATTERN, ptr, nbytes);
191 
192 	if (flags & UM_GC)
193 		mdb_umem_gc_enter(ptr, nbytes);
194 
195 	return (ptr);
196 }
197 
198 void *
mdb_alloc(size_t nbytes,uint_t flags)199 mdb_alloc(size_t nbytes, uint_t flags)
200 {
201 	return (mdb_alloc_align(nbytes, 0, flags));
202 }
203 
204 void *
mdb_zalloc(size_t nbytes,uint_t flags)205 mdb_zalloc(size_t nbytes, uint_t flags)
206 {
207 	void *ptr = mdb_alloc(nbytes, flags);
208 
209 	if (ptr != NULL)
210 		bzero(ptr, nbytes);
211 
212 	return (ptr);
213 }
214 
215 void
mdb_free(void * ptr,size_t nbytes)216 mdb_free(void *ptr, size_t nbytes)
217 {
218 	ASSERT(ptr != NULL || nbytes == 0);
219 
220 	nbytes = (nbytes + sizeof (uint32_t) - 1) & ~(sizeof (uint32_t) - 1);
221 
222 	if (ptr != NULL) {
223 		if (mdb_umem_flags & UMF_DEBUG)
224 			mdb_umem_copy_pattern(UMEM_FREE_PATTERN, ptr, nbytes);
225 		free(ptr);
226 	}
227 }
228 
229 void
mdb_free_align(void * ptr,size_t nbytes)230 mdb_free_align(void *ptr, size_t nbytes)
231 {
232 	mdb_free(ptr, nbytes);
233 }
234 
235 void
mdb_recycle(mdb_mblk_t ** blkpp)236 mdb_recycle(mdb_mblk_t **blkpp)
237 {
238 	mdb_mblk_t *blkp, *nblkp;
239 
240 	for (blkp = *blkpp; blkp != NULL; blkp = nblkp) {
241 		mdb_dprintf(MDB_DBG_UMEM,
242 		    "garbage collect %p size %lu bytes\n", blkp->blk_addr,
243 		    (ulong_t)blkp->blk_size);
244 
245 		nblkp = blkp->blk_next;
246 		mdb_free(blkp->blk_addr, blkp->blk_size);
247 		mdb_free(blkp, sizeof (mdb_mblk_t));
248 	}
249 
250 	*blkpp = NULL;
251 }
252