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 /*
27 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
28 */
29
30 #include "umem.h"
31 #include <libproc.h>
32 #include <mdb/mdb_modapi.h>
33
34 #include "kgrep.h"
35 #include "leaky.h"
36 #include "misc.h"
37 #include "proc_kludges.h"
38
39 #include <umem_impl.h>
40 #include <sys/vmem_impl_user.h>
41 #include <thr_uberdata.h>
42
43 #include "umem_pagesize.h"
44
45 typedef struct datafmt {
46 char *hdr1;
47 char *hdr2;
48 char *dashes;
49 char *fmt;
50 } datafmt_t;
51
52 static datafmt_t ptcfmt[] = {
53 { " ", "tid", "---", "%3u " },
54 { " memory", " cached", "-------", "%7lH " },
55 { " %", "cap", "---", "%3u " },
56 { " %", NULL, "---", "%3u " },
57 { NULL, NULL, NULL, NULL }
58 };
59
60 static datafmt_t umemfmt[] = {
61 { "cache ", "name ",
62 "-------------------------", "%-25s " },
63 { " buf", " size", "------", "%6u " },
64 { " buf", " in use", "-------", "%7u " },
65 { " buf", " in ptc", "-------", "%7s " },
66 { " buf", " total", "-------", "%7u " },
67 { " memory", " in use", "-------", "%7H " },
68 { " alloc", " succeed", "---------", "%9u " },
69 { "alloc", " fail", "-----", "%5llu" },
70 { NULL, NULL, NULL, NULL }
71 };
72
73 static datafmt_t vmemfmt[] = {
74 { "vmem ", "name ",
75 "-------------------------", "%-*s " },
76 { " memory", " in use", "---------", "%9H " },
77 { " memory", " total", "----------", "%10H " },
78 { " memory", " import", "---------", "%9H " },
79 { " alloc", " succeed", "---------", "%9llu " },
80 { "alloc", " fail", "-----", "%5llu " },
81 { NULL, NULL, NULL, NULL }
82 };
83
84 /*ARGSUSED*/
85 static int
umastat_cpu_avail(uintptr_t addr,const umem_cpu_cache_t * ccp,int * avail)86 umastat_cpu_avail(uintptr_t addr, const umem_cpu_cache_t *ccp, int *avail)
87 {
88 if (ccp->cc_rounds > 0)
89 *avail += ccp->cc_rounds;
90 if (ccp->cc_prounds > 0)
91 *avail += ccp->cc_prounds;
92
93 return (WALK_NEXT);
94 }
95
96 /*ARGSUSED*/
97 static int
umastat_cpu_alloc(uintptr_t addr,const umem_cpu_cache_t * ccp,int * alloc)98 umastat_cpu_alloc(uintptr_t addr, const umem_cpu_cache_t *ccp, int *alloc)
99 {
100 *alloc += ccp->cc_alloc;
101
102 return (WALK_NEXT);
103 }
104
105 /*ARGSUSED*/
106 static int
umastat_slab_avail(uintptr_t addr,const umem_slab_t * sp,int * avail)107 umastat_slab_avail(uintptr_t addr, const umem_slab_t *sp, int *avail)
108 {
109 *avail += sp->slab_chunks - sp->slab_refcnt;
110
111 return (WALK_NEXT);
112 }
113
114 typedef struct umastat_vmem {
115 uintptr_t kv_addr;
116 struct umastat_vmem *kv_next;
117 int kv_meminuse;
118 int kv_alloc;
119 int kv_fail;
120 } umastat_vmem_t;
121
122 /*ARGSUSED*/
123 static int
umastat_cache_nptc(uintptr_t addr,const umem_cache_t * cp,int * nptc)124 umastat_cache_nptc(uintptr_t addr, const umem_cache_t *cp, int *nptc)
125 {
126 if (!(cp->cache_flags & UMF_PTC))
127 return (WALK_NEXT);
128
129 (*nptc)++;
130 return (WALK_NEXT);
131 }
132
133 /*ARGSUSED*/
134 static int
umastat_cache_hdr(uintptr_t addr,const umem_cache_t * cp,void * ignored)135 umastat_cache_hdr(uintptr_t addr, const umem_cache_t *cp, void *ignored)
136 {
137 if (!(cp->cache_flags & UMF_PTC))
138 return (WALK_NEXT);
139
140 mdb_printf("%3d ", cp->cache_bufsize);
141 return (WALK_NEXT);
142 }
143
144 /*ARGSUSED*/
145 static int
umastat_lwp_ptc(uintptr_t addr,void * buf,int * nbufs)146 umastat_lwp_ptc(uintptr_t addr, void *buf, int *nbufs)
147 {
148 (*nbufs)++;
149 return (WALK_NEXT);
150 }
151
152 /*ARGSUSED*/
153 static int
umastat_lwp_cache(uintptr_t addr,const umem_cache_t * cp,ulwp_t * ulwp)154 umastat_lwp_cache(uintptr_t addr, const umem_cache_t *cp, ulwp_t *ulwp)
155 {
156 char walk[60];
157 int nbufs = 0;
158
159 if (!(cp->cache_flags & UMF_PTC))
160 return (WALK_NEXT);
161
162 (void) mdb_snprintf(walk, sizeof (walk), "umem_ptc_%d",
163 cp->cache_bufsize);
164
165 if (mdb_pwalk(walk, (mdb_walk_cb_t)umastat_lwp_ptc,
166 &nbufs, (uintptr_t)ulwp->ul_self) == -1) {
167 mdb_warn("unable to walk '%s'", walk);
168 return (WALK_ERR);
169 }
170
171 mdb_printf("%3d ", ulwp->ul_tmem.tm_size ?
172 (nbufs * cp->cache_bufsize * 100) / ulwp->ul_tmem.tm_size : 0);
173
174 return (WALK_NEXT);
175 }
176
177 /*ARGSUSED*/
178 static int
umastat_lwp(uintptr_t addr,const ulwp_t * ulwp,void * ignored)179 umastat_lwp(uintptr_t addr, const ulwp_t *ulwp, void *ignored)
180 {
181 size_t size;
182 datafmt_t *dfp = ptcfmt;
183
184 mdb_printf((dfp++)->fmt, ulwp->ul_lwpid);
185 mdb_printf((dfp++)->fmt, ulwp->ul_tmem.tm_size);
186
187 if (umem_readvar(&size, "umem_ptc_size") == -1) {
188 mdb_warn("unable to read 'umem_ptc_size'");
189 return (WALK_ERR);
190 }
191
192 mdb_printf((dfp++)->fmt, (ulwp->ul_tmem.tm_size * 100) / size);
193
194 if (mdb_walk("umem_cache",
195 (mdb_walk_cb_t)umastat_lwp_cache, (void *)ulwp) == -1) {
196 mdb_warn("can't walk 'umem_cache'");
197 return (WALK_ERR);
198 }
199
200 mdb_printf("\n");
201
202 return (WALK_NEXT);
203 }
204
205 /*ARGSUSED*/
206 static int
umastat_cache_ptc(uintptr_t addr,const void * ignored,int * nptc)207 umastat_cache_ptc(uintptr_t addr, const void *ignored, int *nptc)
208 {
209 (*nptc)++;
210 return (WALK_NEXT);
211 }
212
213 static int
umastat_cache(uintptr_t addr,const umem_cache_t * cp,umastat_vmem_t ** kvp)214 umastat_cache(uintptr_t addr, const umem_cache_t *cp, umastat_vmem_t **kvp)
215 {
216 umastat_vmem_t *kv;
217 datafmt_t *dfp = umemfmt;
218 char buf[10];
219 int magsize;
220
221 int avail, alloc, total, nptc = 0;
222 size_t meminuse = (cp->cache_slab_create - cp->cache_slab_destroy) *
223 cp->cache_slabsize;
224
225 mdb_walk_cb_t cpu_avail = (mdb_walk_cb_t)umastat_cpu_avail;
226 mdb_walk_cb_t cpu_alloc = (mdb_walk_cb_t)umastat_cpu_alloc;
227 mdb_walk_cb_t slab_avail = (mdb_walk_cb_t)umastat_slab_avail;
228
229 magsize = umem_get_magsize(cp);
230
231 alloc = cp->cache_slab_alloc + cp->cache_full.ml_alloc;
232 avail = cp->cache_full.ml_total * magsize;
233 total = cp->cache_buftotal;
234
235 (void) mdb_pwalk("umem_cpu_cache", cpu_alloc, &alloc, addr);
236 (void) mdb_pwalk("umem_cpu_cache", cpu_avail, &avail, addr);
237 (void) mdb_pwalk("umem_slab_partial", slab_avail, &avail, addr);
238
239 if (cp->cache_flags & UMF_PTC) {
240 char walk[60];
241
242 (void) mdb_snprintf(walk, sizeof (walk),
243 "umem_ptc_%d", cp->cache_bufsize);
244
245 if (mdb_walk(walk,
246 (mdb_walk_cb_t)umastat_cache_ptc, &nptc) == -1) {
247 mdb_warn("unable to walk '%s'", walk);
248 return (WALK_ERR);
249 }
250
251 (void) mdb_snprintf(buf, sizeof (buf), "%d", nptc);
252 }
253
254 for (kv = *kvp; kv != NULL; kv = kv->kv_next) {
255 if (kv->kv_addr == (uintptr_t)cp->cache_arena)
256 goto out;
257 }
258
259 kv = mdb_zalloc(sizeof (umastat_vmem_t), UM_SLEEP | UM_GC);
260 kv->kv_next = *kvp;
261 kv->kv_addr = (uintptr_t)cp->cache_arena;
262 *kvp = kv;
263 out:
264 kv->kv_meminuse += meminuse;
265 kv->kv_alloc += alloc;
266 kv->kv_fail += cp->cache_alloc_fail;
267
268 mdb_printf((dfp++)->fmt, cp->cache_name);
269 mdb_printf((dfp++)->fmt, cp->cache_bufsize);
270 mdb_printf((dfp++)->fmt, total - avail);
271 mdb_printf((dfp++)->fmt, cp->cache_flags & UMF_PTC ? buf : "-");
272 mdb_printf((dfp++)->fmt, total);
273 mdb_printf((dfp++)->fmt, meminuse);
274 mdb_printf((dfp++)->fmt, alloc);
275 mdb_printf((dfp++)->fmt, cp->cache_alloc_fail);
276 mdb_printf("\n");
277
278 return (WALK_NEXT);
279 }
280
281 static int
umastat_vmem_totals(uintptr_t addr,const vmem_t * v,umastat_vmem_t * kv)282 umastat_vmem_totals(uintptr_t addr, const vmem_t *v, umastat_vmem_t *kv)
283 {
284 while (kv != NULL && kv->kv_addr != addr)
285 kv = kv->kv_next;
286
287 if (kv == NULL || kv->kv_alloc == 0)
288 return (WALK_NEXT);
289
290 mdb_printf("Total [%s]%*s %6s %7s %7s %7s %7H %9u %5u\n", v->vm_name,
291 17 - strlen(v->vm_name), "", "", "", "", "",
292 kv->kv_meminuse, kv->kv_alloc, kv->kv_fail);
293
294 return (WALK_NEXT);
295 }
296
297 /*ARGSUSED*/
298 static int
umastat_vmem(uintptr_t addr,const vmem_t * v,void * ignored)299 umastat_vmem(uintptr_t addr, const vmem_t *v, void *ignored)
300 {
301 datafmt_t *dfp = vmemfmt;
302 uintptr_t paddr;
303 vmem_t parent;
304 int ident = 0;
305
306 for (paddr = (uintptr_t)v->vm_source; paddr != 0; ident += 4) {
307 if (mdb_vread(&parent, sizeof (parent), paddr) == -1) {
308 mdb_warn("couldn't trace %p's ancestry", addr);
309 ident = 0;
310 break;
311 }
312 paddr = (uintptr_t)parent.vm_source;
313 }
314
315 mdb_printf("%*s", ident, "");
316 mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name);
317 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_inuse);
318 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_total);
319 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_mem_import);
320 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_alloc);
321 mdb_printf((dfp++)->fmt, v->vm_kstat.vk_fail);
322
323 mdb_printf("\n");
324
325 return (WALK_NEXT);
326 }
327
328 /*ARGSUSED*/
329 int
umastat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)330 umastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
331 {
332 umastat_vmem_t *kv = NULL;
333 datafmt_t *dfp;
334 int nptc = 0, i;
335
336 if (argc != 0)
337 return (DCMD_USAGE);
338
339 /*
340 * We need to determine if we have any caches that have per-thread
341 * caching enabled.
342 */
343 if (mdb_walk("umem_cache",
344 (mdb_walk_cb_t)umastat_cache_nptc, &nptc) == -1) {
345 mdb_warn("can't walk 'umem_cache'");
346 return (DCMD_ERR);
347 }
348
349 if (nptc) {
350 for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
351 mdb_printf("%s ", dfp->hdr1);
352
353 for (i = 0; i < nptc; i++)
354 mdb_printf("%s ", dfp->hdr1);
355
356 mdb_printf("\n");
357
358 for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
359 mdb_printf("%s ", dfp->hdr2);
360
361 if (mdb_walk("umem_cache",
362 (mdb_walk_cb_t)umastat_cache_hdr, NULL) == -1) {
363 mdb_warn("can't walk 'umem_cache'");
364 return (DCMD_ERR);
365 }
366
367 mdb_printf("\n");
368
369 for (dfp = ptcfmt; dfp->hdr2 != NULL; dfp++)
370 mdb_printf("%s ", dfp->dashes);
371
372 for (i = 0; i < nptc; i++)
373 mdb_printf("%s ", dfp->dashes);
374
375 mdb_printf("\n");
376
377 if (mdb_walk("ulwp", (mdb_walk_cb_t)umastat_lwp, NULL) == -1) {
378 mdb_warn("can't walk 'ulwp'");
379 return (DCMD_ERR);
380 }
381
382 mdb_printf("\n");
383 }
384
385 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
386 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->hdr1);
387 mdb_printf("\n");
388
389 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
390 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->hdr2);
391 mdb_printf("\n");
392
393 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
394 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->dashes);
395 mdb_printf("\n");
396
397 if (mdb_walk("umem_cache", (mdb_walk_cb_t)umastat_cache, &kv) == -1) {
398 mdb_warn("can't walk 'umem_cache'");
399 return (DCMD_ERR);
400 }
401
402 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
403 mdb_printf("%s%s", dfp == umemfmt ? "" : " ", dfp->dashes);
404 mdb_printf("\n");
405
406 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem_totals, kv) == -1) {
407 mdb_warn("can't walk 'vmem'");
408 return (DCMD_ERR);
409 }
410
411 for (dfp = umemfmt; dfp->hdr1 != NULL; dfp++)
412 mdb_printf("%s ", dfp->dashes);
413 mdb_printf("\n");
414
415 mdb_printf("\n");
416
417 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
418 mdb_printf("%s ", dfp->hdr1);
419 mdb_printf("\n");
420
421 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
422 mdb_printf("%s ", dfp->hdr2);
423 mdb_printf("\n");
424
425 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
426 mdb_printf("%s ", dfp->dashes);
427 mdb_printf("\n");
428
429 if (mdb_walk("vmem", (mdb_walk_cb_t)umastat_vmem, NULL) == -1) {
430 mdb_warn("can't walk 'vmem'");
431 return (DCMD_ERR);
432 }
433
434 for (dfp = vmemfmt; dfp->hdr1 != NULL; dfp++)
435 mdb_printf("%s ", dfp->dashes);
436 mdb_printf("\n");
437 return (DCMD_OK);
438 }
439
440 /*
441 * kmdb doesn't use libproc, and thus doesn't have any prmap_t's to walk.
442 * We have other ways to grep kmdb's address range.
443 */
444 #ifndef _KMDB
445
446 typedef struct ugrep_walk_data {
447 kgrep_cb_func *ug_cb;
448 void *ug_cbdata;
449 } ugrep_walk_data_t;
450
451 /*ARGSUSED*/
452 int
ugrep_mapping_cb(uintptr_t addr,const void * prm_arg,void * data)453 ugrep_mapping_cb(uintptr_t addr, const void *prm_arg, void *data)
454 {
455 ugrep_walk_data_t *ug = data;
456 const prmap_t *prm = prm_arg;
457
458 return (ug->ug_cb(prm->pr_vaddr, prm->pr_vaddr + prm->pr_size,
459 ug->ug_cbdata));
460 }
461
462 int
kgrep_subr(kgrep_cb_func * cb,void * cbdata)463 kgrep_subr(kgrep_cb_func *cb, void *cbdata)
464 {
465 ugrep_walk_data_t ug;
466
467 prockludge_add_walkers();
468
469 ug.ug_cb = cb;
470 ug.ug_cbdata = cbdata;
471
472 if (mdb_walk(KLUDGE_MAPWALK_NAME, ugrep_mapping_cb, &ug) == -1) {
473 mdb_warn("Unable to walk "KLUDGE_MAPWALK_NAME);
474 return (DCMD_ERR);
475 }
476
477 prockludge_remove_walkers();
478 return (DCMD_OK);
479 }
480
481 size_t
kgrep_subr_pagesize(void)482 kgrep_subr_pagesize(void)
483 {
484 return (PAGESIZE);
485 }
486
487 #endif /* !_KMDB */
488
489 static const mdb_dcmd_t dcmds[] = {
490
491 /* from libumem.c */
492 { "umastat", NULL, "umem allocator stats", umastat },
493
494 /* from misc.c */
495 { "umem_debug", NULL, "toggle umem dcmd/walk debugging", umem_debug},
496
497 /* from umem.c */
498 { "umem_status", NULL, "Print umem status and message buffer",
499 umem_status },
500 { "allocdby", ":", "given a thread, print its allocated buffers",
501 allocdby },
502 { "bufctl", ":[-vh] [-a addr] [-c caller] [-e earliest] [-l latest] "
503 "[-t thd]", "print or filter a bufctl", bufctl, bufctl_help },
504 { "bufctl_audit", ":", "print a bufctl_audit", bufctl_audit },
505 { "freedby", ":", "given a thread, print its freed buffers", freedby },
506 { "umalog", "[ fail | slab ]",
507 "display umem transaction log and stack traces", umalog },
508 { "umausers", "[-ef] [cache ...]", "display current medium and large "
509 "users of the umem allocator", umausers },
510 { "umem_cache", "?", "print a umem cache", umem_cache },
511 { "umem_log", "?", "dump umem transaction log", umem_log },
512 { "umem_malloc_dist", "[-dg] [-b maxbins] [-B minbinsize]",
513 "report distribution of outstanding malloc()s",
514 umem_malloc_dist, umem_malloc_dist_help },
515 { "umem_malloc_info", "?[-dg] [-b maxbins] [-B minbinsize]",
516 "report information about malloc()s by cache",
517 umem_malloc_info, umem_malloc_info_help },
518 { "umem_verify", "?", "check integrity of umem-managed memory",
519 umem_verify },
520 { "vmem", "?", "print a vmem_t", vmem },
521 { "vmem_seg", ":[-sv] [-c caller] [-e earliest] [-l latest] "
522 "[-m minsize] [-M maxsize] [-t thread] [-T type]",
523 "print or filter a vmem_seg", vmem_seg, vmem_seg_help },
524
525 #ifndef _KMDB
526 /* from ../genunix/kgrep.c + libumem.c */
527 { "ugrep", KGREP_USAGE, "search user address space for a pointer",
528 kgrep, kgrep_help },
529
530 /* from ../genunix/leaky.c + leaky_subr.c */
531 { "findleaks", FINDLEAKS_USAGE, "search for potential memory leaks",
532 findleaks, findleaks_help },
533 #endif
534
535 { NULL }
536 };
537
538 static const mdb_walker_t walkers[] = {
539
540 /* from umem.c */
541 { "allocdby", "given a thread, walk its allocated bufctls",
542 allocdby_walk_init, allocdby_walk_step, allocdby_walk_fini },
543 { "bufctl", "walk a umem cache's bufctls",
544 bufctl_walk_init, umem_walk_step, umem_walk_fini },
545 { "bufctl_history", "walk the available history of a bufctl",
546 bufctl_history_walk_init, bufctl_history_walk_step,
547 bufctl_history_walk_fini },
548 { "freectl", "walk a umem cache's free bufctls",
549 freectl_walk_init, umem_walk_step, umem_walk_fini },
550 { "freedby", "given a thread, walk its freed bufctls",
551 freedby_walk_init, allocdby_walk_step, allocdby_walk_fini },
552 { "freemem", "walk a umem cache's free memory",
553 freemem_walk_init, umem_walk_step, umem_walk_fini },
554 { "umem", "walk a umem cache",
555 umem_walk_init, umem_walk_step, umem_walk_fini },
556 { "umem_cpu", "walk the umem CPU structures",
557 umem_cpu_walk_init, umem_cpu_walk_step, umem_cpu_walk_fini },
558 { "umem_cpu_cache", "given a umem cache, walk its per-CPU caches",
559 umem_cpu_cache_walk_init, umem_cpu_cache_walk_step, NULL },
560 { "umem_hash", "given a umem cache, walk its allocated hash table",
561 umem_hash_walk_init, umem_hash_walk_step, umem_hash_walk_fini },
562 { "umem_log", "walk the umem transaction log",
563 umem_log_walk_init, umem_log_walk_step, umem_log_walk_fini },
564 { "umem_slab", "given a umem cache, walk its slabs",
565 umem_slab_walk_init, umem_slab_walk_step, NULL },
566 { "umem_slab_partial",
567 "given a umem cache, walk its partially allocated slabs (min 1)",
568 umem_slab_walk_partial_init, umem_slab_walk_step, NULL },
569 { "vmem", "walk vmem structures in pre-fix, depth-first order",
570 vmem_walk_init, vmem_walk_step, vmem_walk_fini },
571 { "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs",
572 vmem_alloc_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
573 { "vmem_free", "given a vmem_t, walk its free vmem_segs",
574 vmem_free_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
575 { "vmem_postfix", "walk vmem structures in post-fix, depth-first order",
576 vmem_walk_init, vmem_postfix_walk_step, vmem_walk_fini },
577 { "vmem_seg", "given a vmem_t, walk all of its vmem_segs",
578 vmem_seg_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
579 { "vmem_span", "given a vmem_t, walk its spanning vmem_segs",
580 vmem_span_walk_init, vmem_seg_walk_step, vmem_seg_walk_fini },
581
582 #ifndef _KMDB
583 /* from ../genunix/leaky.c + leaky_subr.c */
584 { "leak", "given a leak ctl, walk other leaks w/ that stacktrace",
585 leaky_walk_init, leaky_walk_step, leaky_walk_fini },
586 { "leakbuf", "given a leak ctl, walk addr of leaks w/ that stacktrace",
587 leaky_walk_init, leaky_buf_walk_step, leaky_walk_fini },
588 #endif
589
590 { NULL }
591 };
592
593 static const mdb_modinfo_t modinfo = {MDB_API_VERSION, dcmds, walkers};
594
595 const mdb_modinfo_t *
_mdb_init(void)596 _mdb_init(void)
597 {
598 if (umem_init() != 0)
599 return (NULL);
600
601 return (&modinfo);
602 }
603
604 void
_mdb_fini(void)605 _mdb_fini(void)
606 {
607 #ifndef _KMDB
608 leaky_cleanup(1);
609 #endif
610 }
611