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