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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include "umem.h"
27 #include <libproc.h>
28 #include <mdb/mdb_modapi.h>
29
30 #include "kgrep.h"
31 #include "leaky.h"
32 #include "misc.h"
33 #include "proc_kludges.h"
34
35 #include <umem_impl.h>
36 #include <sys/vmem_impl_user.h>
37
38 #include "umem_pagesize.h"
39
40 typedef struct datafmt {
41 char *hdr1;
42 char *hdr2;
43 char *dashes;
44 char *fmt;
45 } datafmt_t;
46
47 static datafmt_t umemfmt[] = {
48 { "cache ", "name ",
49 "-------------------------", "%-25s " },
50 { " buf", " size", "------", "%6u " },
51 { " buf", "in use", "------", "%6u " },
52 { " buf", " total", "------", "%6u " },
53 { " memory", " in use", "---------", "%9u " },
54 { " alloc", " succeed", "---------", "%9u " },
55 { "alloc", " fail", "-----", "%5llu " },
56 { NULL, NULL, NULL, NULL }
57 };
58
59 static datafmt_t vmemfmt[] = {
60 { "vmem ", "name ",
61 "-------------------------", "%-*s " },
62 { " memory", " in use", "---------", "%9llu " },
63 { " memory", " total", "----------", "%10llu " },
64 { " memory", " import", "---------", "%9llu " },
65 { " alloc", " succeed", "---------", "%9llu " },
66 { "alloc", " fail", "-----", "%5llu " },
67 { NULL, NULL, NULL, NULL }
68 };
69
70 /*ARGSUSED*/
71 static int
umastat_cpu_avail(uintptr_t addr,const umem_cpu_cache_t * ccp,int * avail)72 umastat_cpu_avail(uintptr_t addr, const umem_cpu_cache_t *ccp, int *avail)
73 {
74 if (ccp->cc_rounds > 0)
75 *avail += ccp->cc_rounds;
76 if (ccp->cc_prounds > 0)
77 *avail += ccp->cc_prounds;
78
79 return (WALK_NEXT);
80 }
81
82 /*ARGSUSED*/
83 static int
umastat_cpu_alloc(uintptr_t addr,const umem_cpu_cache_t * ccp,int * alloc)84 umastat_cpu_alloc(uintptr_t addr, const umem_cpu_cache_t *ccp, int *alloc)
85 {
86 *alloc += ccp->cc_alloc;
87
88 return (WALK_NEXT);
89 }
90
91 /*ARGSUSED*/
92 static int
umastat_slab_avail(uintptr_t addr,const umem_slab_t * sp,int * avail)93 umastat_slab_avail(uintptr_t addr, const umem_slab_t *sp, int *avail)
94 {
95 *avail += sp->slab_chunks - sp->slab_refcnt;
96
97 return (WALK_NEXT);
98 }
99
100 typedef struct umastat_vmem {
101 uintptr_t kv_addr;
102 struct umastat_vmem *kv_next;
103 int kv_meminuse;
104 int kv_alloc;
105 int kv_fail;
106 } umastat_vmem_t;
107
108 static int
umastat_cache(uintptr_t addr,const umem_cache_t * cp,umastat_vmem_t ** kvp)109 umastat_cache(uintptr_t addr, const umem_cache_t *cp, umastat_vmem_t **kvp)
110 {
111 umastat_vmem_t *kv;
112 datafmt_t *dfp = umemfmt;
113 int magsize;
114
115 int avail, alloc, total;
116 size_t meminuse = (cp->cache_slab_create - cp->cache_slab_destroy) *
117 cp->cache_slabsize;
118
119 mdb_walk_cb_t cpu_avail = (mdb_walk_cb_t)umastat_cpu_avail;
120 mdb_walk_cb_t cpu_alloc = (mdb_walk_cb_t)umastat_cpu_alloc;
121 mdb_walk_cb_t slab_avail = (mdb_walk_cb_t)umastat_slab_avail;
122
123 magsize = umem_get_magsize(cp);
124
125 alloc = cp->cache_slab_alloc + cp->cache_full.ml_alloc;
126 avail = cp->cache_full.ml_total * magsize;
127 total = cp->cache_buftotal;
128
129 (void) mdb_pwalk("umem_cpu_cache", cpu_alloc, &alloc, addr);
130 (void) mdb_pwalk("umem_cpu_cache", cpu_avail, &avail, addr);
131 (void) mdb_pwalk("umem_slab_partial", slab_avail, &avail, addr);
132
133 for (kv = *kvp; kv != NULL; kv = kv->kv_next) {
134 if (kv->kv_addr == (uintptr_t)cp->cache_arena)
135 goto out;
136 }
137
138 kv = mdb_zalloc(sizeof (umastat_vmem_t), UM_SLEEP | UM_GC);
139 kv->kv_next = *kvp;
140 kv->kv_addr = (uintptr_t)cp->cache_arena;
141 *kvp = kv;
142 out:
143 kv->kv_meminuse += meminuse;
144 kv->kv_alloc += alloc;
145 kv->kv_fail += cp->cache_alloc_fail;
146
147 mdb_printf((dfp++)->fmt, cp->cache_name);
148 mdb_printf((dfp++)->fmt, cp->cache_bufsize);
149 mdb_printf((dfp++)->fmt, total - avail);
150 mdb_printf((dfp++)->fmt, total);
151 mdb_printf((dfp++)->fmt, meminuse);
152 mdb_printf((dfp++)->fmt, alloc);
153 mdb_printf((dfp++)->fmt, cp->cache_alloc_fail);
154 mdb_printf("\n");
155
156 return (WALK_NEXT);
157 }
158
159 static int
umastat_vmem_totals(uintptr_t addr,const vmem_t * v,umastat_vmem_t * kv)160 umastat_vmem_totals(uintptr_t addr, const vmem_t *v, umastat_vmem_t *kv)
161 {
162 while (kv != NULL && kv->kv_addr != addr)
163 kv = kv->kv_next;
164
165 if (kv == NULL || kv->kv_alloc == 0)
166 return (WALK_NEXT);
167
168 mdb_printf("Total [%s]%*s %6s %6s %6s %9u %9u %5u\n", v->vm_name,
169 17 - strlen(v->vm_name), "", "", "", "",
170 kv->kv_meminuse, kv->kv_alloc, kv->kv_fail);
171
172 return (WALK_NEXT);
173 }
174
175 /*ARGSUSED*/
176 static int
umastat_vmem(uintptr_t addr,const vmem_t * v,void * ignored)177 umastat_vmem(uintptr_t addr, const vmem_t *v, void *ignored)
178 {
179 datafmt_t *dfp = vmemfmt;
180 uintptr_t paddr;
181 vmem_t parent;
182 int ident = 0;
183
184 for (paddr = (uintptr_t)v->vm_source; paddr != NULL; ident += 4) {
185 if (mdb_vread(&parent, sizeof (parent), paddr) == -1) {
186 mdb_warn("couldn't trace %p's ancestry", addr);
187 ident = 0;
188 break;
189 }
190 paddr = (uintptr_t)parent.vm_source;
191 }
192
193 mdb_printf("%*s", ident, "");
194 mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name);
195 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_inuse);
196 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_total);
197 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_import);
198 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_alloc);
199 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_fail);
200
201 mdb_printf("\n");
202
203 return (WALK_NEXT);
204 }
205
206 /*ARGSUSED*/
207 int
umastat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)208 umastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
209 {
210 umastat_vmem_t *kv = NULL;
211 datafmt_t *dfp;
212
213 if (argc != 0)
214 return (DCMD_USAGE);
215
216 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
217 mdb_printf("%s ", dfp->hdr1);
218 mdb_printf("\n");
219
220 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
221 mdb_printf("%s ", dfp->hdr2);
222 mdb_printf("\n");
223
224 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
225 mdb_printf("%s ", dfp->dashes);
226 mdb_printf("\n");
227
228 if (mdb_walk("umem_cache", (mdb_walk_cb_t)umastat_cache, &kv) == -1) {
229 mdb_warn("can't walk 'umem_cache'");
230 return (DCMD_ERR);
231 }
232
233 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
234 mdb_printf("%s ", dfp->dashes);
235 mdb_printf("\n");
236
237 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem_totals, kv) == -1) {
238 mdb_warn("can't walk 'vmem'");
239 return (DCMD_ERR);
240 }
241
242 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
243 mdb_printf("%s ", dfp->dashes);
244 mdb_printf("\n");
245
246 mdb_printf("\n");
247
248 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
249 mdb_printf("%s ", dfp->hdr1);
250 mdb_printf("\n");
251
252 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
253 mdb_printf("%s ", dfp->hdr2);
254 mdb_printf("\n");
255
256 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
257 mdb_printf("%s ", dfp->dashes);
258 mdb_printf("\n");
259
260 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem, NULL) == -1) {
261 mdb_warn("can't walk 'vmem'");
262 return (DCMD_ERR);
263 }
264
265 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
266 mdb_printf("%s ", dfp->dashes);
267 mdb_printf("\n");
268 return (DCMD_OK);
269 }
270
271 /*
272 * kmdb doesn't use libproc, and thus doesn't have any prmap_t's to walk.
273 * We have other ways to grep kmdb's address range.
274 */
275 #ifndef _KMDB
276
277 typedef struct ugrep_walk_data {
278 kgrep_cb_func *ug_cb;
279 void *ug_cbdata;
280 } ugrep_walk_data_t;
281
282 /*ARGSUSED*/
283 int
ugrep_mapping_cb(uintptr_t addr,const void * prm_arg,void * data)284 ugrep_mapping_cb(uintptr_t addr, const void *prm_arg, void *data)
285 {
286 ugrep_walk_data_t *ug = data;
287 const prmap_t *prm = prm_arg;
288
289 return (ug->ug_cb(prm->pr_vaddr, prm->pr_vaddr + prm->pr_size,
290 ug->ug_cbdata));
291 }
292
293 int
kgrep_subr(kgrep_cb_func * cb,void * cbdata)294 kgrep_subr(kgrep_cb_func *cb, void *cbdata)
295 {
296 ugrep_walk_data_t ug;
297
298 prockludge_add_walkers();
299
300 ug.ug_cb = cb;
301 ug.ug_cbdata = cbdata;
302
303 if (mdb_walk(KLUDGE_MAPWALK_NAME, ugrep_mapping_cb, &ug) == -1) {
304 mdb_warn("Unable to walk "KLUDGE_MAPWALK_NAME);
305 return (DCMD_ERR);
306 }
307
308 prockludge_remove_walkers();
309 return (DCMD_OK);
310 }
311
312 size_t
kgrep_subr_pagesize(void)313 kgrep_subr_pagesize(void)
314 {
315 return (PAGESIZE);
316 }
317
318 #endif /* !_KMDB */
319
320 static const mdb_dcmd_t dcmds[] = {
321
322 /* from libumem.c */
323 { "umastat", NULL, "umem allocator stats", umastat },
324
325 /* from misc.c */
326 { "umem_debug", NULL, "toggle umem dcmd/walk debugging", umem_debug},
327
328 /* from umem.c */
329 { "umem_status", NULL, "Print umem status and message buffer",
330 umem_status },
331 { "allocdby", ":", "given a thread, print its allocated buffers",
332 allocdby },
333 { "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] "
334 "[-t thd]", "print or filter a bufctl", bufctl, bufctl_help },
335 { "bufctl_audit", ":", "print a bufctl_audit", bufctl_audit },
336 { "freedby", ":", "given a thread, print its freed buffers", freedby },
337 { "umalog", "[ fail | slab ]",
338 "display umem transaction log and stack traces", umalog },
339 { "umausers", "[-ef] [cache ...]", "display current medium and large "
340 "users of the umem allocator", umausers },
341 { "umem_cache", "?", "print a umem cache", umem_cache },
342 { "umem_log", "?", "dump umem transaction log", umem_log },
343 { "umem_malloc_dist", "[-dg] [-b maxbins] [-B minbinsize]",
344 "report distribution of outstanding malloc()s",
345 umem_malloc_dist, umem_malloc_dist_help },
346 { "umem_malloc_info", "?[-dg] [-b maxbins] [-B minbinsize]",
347 "report information about malloc()s by cache",
348 umem_malloc_info, umem_malloc_info_help },
349 { "umem_verify", "?", "check integrity of umem-managed memory",
350 umem_verify },
351 { "vmem", "?", "print a vmem_t", vmem },
352 { "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
353 "[-m minsize] [-M maxsize] [-t thread] [-T type]",
354 "print or filter a vmem_seg", vmem_seg, vmem_seg_help },
355
356 #ifndef _KMDB
357 /* from ../genunix/kgrep.c + libumem.c */
358 { "ugrep", KGREP_USAGE, "search user address space for a pointer",
359 kgrep, kgrep_help },
360
361 /* from ../genunix/leaky.c + leaky_subr.c */
362 { "findleaks", FINDLEAKS_USAGE, "search for potential memory leaks",
363 findleaks, findleaks_help },
364 #endif
365
366 { NULL }
367 };
368
369 static const mdb_walker_t walkers[] = {
370
371 /* from umem.c */
372 { "allocdby", "given a thread, walk its allocated bufctls",
373 allocdby_walk_init, allocdby_walk_step, allocdby_walk_fini },
374 { "bufctl", "walk a umem cache's bufctls",
375 bufctl_walk_init, umem_walk_step, umem_walk_fini },
376 { "bufctl_history", "walk the available history of a bufctl",
377 bufctl_history_walk_init, bufctl_history_walk_step,
378 bufctl_history_walk_fini },
379 { "freectl", "walk a umem cache's free bufctls",
380 freectl_walk_init, umem_walk_step, umem_walk_fini },
381 { "freedby", "given a thread, walk its freed bufctls",
382 freedby_walk_init, allocdby_walk_step, allocdby_walk_fini },
383 { "freemem", "walk a umem cache's free memory",
384 freemem_walk_init, umem_walk_step, umem_walk_fini },
385 { "umem", "walk a umem cache",
386 umem_walk_init, umem_walk_step, umem_walk_fini },
387 { "umem_cpu", "walk the umem CPU structures",
388 umem_cpu_walk_init, umem_cpu_walk_step, umem_cpu_walk_fini },
389 { "umem_cpu_cache", "given a umem cache, walk its per-CPU caches",
390 umem_cpu_cache_walk_init, umem_cpu_cache_walk_step, NULL },
391 { "umem_hash", "given a umem cache, walk its allocated hash table",
392 umem_hash_walk_init, umem_hash_walk_step, umem_hash_walk_fini },
393 { "umem_log", "walk the umem transaction log",
394 umem_log_walk_init, umem_log_walk_step, umem_log_walk_fini },
395 { "umem_slab", "given a umem cache, walk its slabs",
396 umem_slab_walk_init, umem_slab_walk_step, NULL },
397 { "umem_slab_partial",
398 "given a umem cache, walk its partially allocated slabs (min 1)",
399 umem_slab_walk_partial_init, umem_slab_walk_step, NULL },
400 { "vmem", "walk vmem structures in pre-fix, depth-first order",
401 vmem_walk_init, vmem_walk_step, vmem_walk_fini },
402 { "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs",
403 vmem_alloc_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
404 { "vmem_free", "given a vmem_t, walk its free vmem_segs",
405 vmem_free_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
406 { "vmem_postfix", "walk vmem structures in post-fix, depth-first order",
407 vmem_walk_init, vmem_postfix_walk_step, vmem_walk_fini },
408 { "vmem_seg", "given a vmem_t, walk all of its vmem_segs",
409 vmem_seg_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
410 { "vmem_span", "given a vmem_t, walk its spanning vmem_segs",
411 vmem_span_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
412
413 #ifndef _KMDB
414 /* from ../genunix/leaky.c + leaky_subr.c */
415 { "leak", "given a leak ctl, walk other leaks w/ that stacktrace",
416 leaky_walk_init, leaky_walk_step, leaky_walk_fini },
417 { "leakbuf", "given a leak ctl, walk addr of leaks w/ that stacktrace",
418 leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini },
419 #endif
420
421 { NULL }
422 };
423
424 static const mdb_modinfo_t modinfo = {MDB_API_VERSION, dcmds, walkers};
425
426 const mdb_modinfo_t *
_mdb_init(void)427 _mdb_init(void)
428 {
429 if (umem_init() != 0)
430 return (NULL);
431
432 return (&modinfo);
433 }
434
435 void
_mdb_fini(void)436 _mdb_fini(void)
437 {
438 #ifndef _KMDB
439 leaky_cleanup(1);
440 #endif
441 }
442