xref: /illumos-gate/usr/src/cmd/mdb/common/modules/ipc/ipc.c (revision f03c4264b1f7479fb49e982c2b53cb6a359e0a2f)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <mdb/mdb_modapi.h>
29 #include <mdb/mdb_ks.h>
30 
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/project.h>
34 #include <sys/ipc_impl.h>
35 #include <sys/shm_impl.h>
36 #include <sys/sem_impl.h>
37 #include <sys/msg_impl.h>
38 
39 #include <vm/anon.h>
40 
41 #define	CMN_HDR_START	"%<u>"
42 #define	CMN_HDR_END	"%</u>\n"
43 #define	CMN_INDENT	(4)
44 #define	CMN_INACTIVE	"%s facility inactive.\n"
45 
46 /*
47  * Bitmap data for page protection flags suitable for use with %b.
48  */
49 const mdb_bitmask_t prot_flag_bits[] = {
50 	{ "PROT_READ", PROT_READ, PROT_READ },
51 	{ "PROT_WRITE", PROT_WRITE, PROT_WRITE },
52 	{ "PROT_EXEC", PROT_EXEC, PROT_EXEC },
53 	{ "PROT_USER", PROT_USER, PROT_USER },
54 	{ NULL, 0, 0 }
55 };
56 
57 static void
58 printtime_nice(const char *str, time_t time)
59 {
60 	if (time)
61 		mdb_printf("%s%Y\n", str, time);
62 	else
63 		mdb_printf("%sn/a\n", str);
64 }
65 
66 /*
67  * Print header common to all IPC types.
68  */
69 static void
70 ipcperm_header()
71 {
72 	mdb_printf(CMN_HDR_START "%?s %5s %5s %8s %5s %5s %6s %5s %5s %5s %5s"
73 	    CMN_HDR_END, "ADDR", "REF", "ID", "KEY", "MODE", "PRJID", "ZONEID",
74 	    "OWNER", "GROUP", "CREAT", "CGRP");
75 }
76 
77 /*
78  * Print data common to all IPC types.
79  */
80 static void
81 ipcperm_print(uintptr_t addr, kipc_perm_t *perm)
82 {
83 	kproject_t proj;
84 	int res;
85 
86 	res = mdb_vread(&proj, sizeof (kproject_t), (uintptr_t)perm->ipc_proj);
87 
88 	if (res == -1)
89 		mdb_warn("failed to read kproject_t at %#p", perm->ipc_proj);
90 
91 	mdb_printf("%0?p %5d %5d", addr, perm->ipc_ref, perm->ipc_id);
92 	if (perm->ipc_key)
93 		mdb_printf(" %8x", perm->ipc_key);
94 	else
95 		mdb_printf(" %8s", "private");
96 	mdb_printf(" %5#o", perm->ipc_mode & 07777);
97 	if (res == -1)
98 		mdb_printf(" %5s %5s", "<flt>", "<flt>");
99 	else
100 		mdb_printf(" %5d %6d", proj.kpj_id, proj.kpj_zoneid);
101 	mdb_printf(" %5d %5d %5d %5d\n", perm->ipc_uid, perm->ipc_gid,
102 	    perm->ipc_cuid, perm->ipc_cgid);
103 
104 }
105 
106 /*ARGSUSED*/
107 static int
108 ipcperm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
109 {
110 	kipc_perm_t perm;
111 
112 	if (!(flags & DCMD_ADDRSPEC))
113 		return (DCMD_USAGE);
114 
115 	if (DCMD_HDRSPEC(flags))
116 		ipcperm_header();
117 
118 	if (mdb_vread(&perm, sizeof (kipc_perm_t), addr) == -1) {
119 		mdb_warn("failed to read kipc_perm_t at %#lx", addr);
120 		return (DCMD_ERR);
121 	}
122 
123 	ipcperm_print(addr, &perm);
124 	return (DCMD_OK);
125 }
126 
127 
128 static int
129 msgq_check_for_rwaiters(list_t *walk_this, int min, int max,
130 	int copy_wait, uintptr_t addr)
131 
132 {
133 	int found = 0;
134 	int ii;
135 	msgq_wakeup_t *walker, next;
136 	uintptr_t head;
137 
138 	for (ii = min; ii < max; ii++) {
139 		head = ((ulong_t)addr) + sizeof (list_t)*ii +
140 		    sizeof (list_node_t);
141 		if (head != (uintptr_t)walk_this[ii].list_head.list_next) {
142 			walker =
143 			    (msgq_wakeup_t *)walk_this[ii].list_head.list_next;
144 			while (head != (uintptr_t)walker) {
145 				if (mdb_vread(&next, sizeof (msgq_wakeup_t),
146 				    (uintptr_t)walker) == -1) {
147 					mdb_warn(
148 					    "Failed to read message queue\n");
149 					return (0);
150 				}
151 				mdb_printf("%15lx\t%6d\t%15lx\t%15s\n",
152 				    next.msgw_thrd, next.msgw_type,
153 				    walker + (uintptr_t)
154 				    OFFSETOF(msgq_wakeup_t, msgw_wake_cv),
155 				    (copy_wait ? "yes":"no"));
156 				found++;
157 				walker =
158 				    (msgq_wakeup_t *)next.msgw_list.list_next;
159 			}
160 		}
161 	}
162 	return (found);
163 }
164 
165 static void
166 msq_print(kmsqid_t *msqid, uintptr_t addr)
167 {
168 	int	total = 0;
169 
170 	mdb_printf("&list: %-?p\n", addr + OFFSETOF(kmsqid_t, msg_list));
171 	mdb_printf("cbytes: 0t%lu    qnum: 0t%lu    qbytes: 0t%lu"
172 	    "    qmax: 0t%lu\n", msqid->msg_cbytes, msqid->msg_qnum,
173 	    msqid->msg_qbytes, msqid->msg_qmax);
174 	mdb_printf("lspid: 0t%d    lrpid: 0t%d\n",
175 	    (int)msqid->msg_lspid, (int)msqid->msg_lrpid);
176 	printtime_nice("stime: ", msqid->msg_stime);
177 	printtime_nice("rtime: ", msqid->msg_rtime);
178 	printtime_nice("ctime: ", msqid->msg_ctime);
179 	mdb_printf("snd_cnt: 0t%lld    snd_cv: %hd (%p)\n",
180 	    msqid->msg_snd_cnt, msqid->msg_snd_cv._opaque,
181 	    addr + (uintptr_t)OFFSETOF(kmsqid_t, msg_snd_cv));
182 	mdb_printf("Blocked recievers\n");
183 	mdb_printf("%15s\t%6s\t%15s\t%15s\n", "Thread Addr",
184 	    "Type", "cv addr", "copyout-wait?");
185 	total += msgq_check_for_rwaiters(&msqid->msg_cpy_block,
186 	    0, 1, 1, addr + OFFSETOF(kmsqid_t, msg_cpy_block));
187 	total += msgq_check_for_rwaiters(msqid->msg_wait_snd,
188 	    0, MSG_MAX_QNUM + 1, 0, addr + OFFSETOF(kmsqid_t, msg_wait_snd));
189 	total += msgq_check_for_rwaiters(msqid->msg_wait_snd_ngt,
190 	    0, MSG_MAX_QNUM + 1, 0,
191 	    addr + OFFSETOF(kmsqid_t, msg_wait_snd_ngt));
192 	mdb_printf("Total number of waiters: %d\n", total);
193 }
194 
195 
196 /*ARGSUSED1*/
197 static void
198 shm_print(kshmid_t *shmid, uintptr_t addr)
199 {
200 	shmatt_t nattch;
201 
202 	nattch = shmid->shm_perm.ipc_ref - (IPC_FREE(&shmid->shm_perm) ? 0 : 1);
203 
204 	mdb_printf(CMN_HDR_START "%10s %?s %5s %7s %7s %7s %7s" CMN_HDR_END,
205 	    "SEGSZ", "AMP", "LKCNT", "LPID", "CPID", "NATTCH", "CNATTCH");
206 	mdb_printf("%10#lx %?p %5u %7d %7d %7lu %7lu\n",
207 	    shmid->shm_segsz, shmid->shm_amp, shmid->shm_lkcnt,
208 	    (int)shmid->shm_lpid, (int)shmid->shm_cpid, nattch,
209 	    shmid->shm_ismattch);
210 
211 	printtime_nice("atime: ", shmid->shm_atime);
212 	printtime_nice("dtime: ", shmid->shm_dtime);
213 	printtime_nice("ctime: ", shmid->shm_ctime);
214 	mdb_printf("sptinfo: %-?p    sptseg: %-?p\n",
215 	    shmid->shm_sptinfo, shmid->shm_sptseg);
216 	mdb_printf("sptprot: <%lb>\n", shmid->shm_sptprot, prot_flag_bits);
217 }
218 
219 
220 /*ARGSUSED1*/
221 static void
222 sem_print(ksemid_t *semid, uintptr_t addr)
223 {
224 	mdb_printf("base: %-?p    nsems: 0t%u\n",
225 	    semid->sem_base, semid->sem_nsems);
226 	printtime_nice("otime: ", semid->sem_otime);
227 	printtime_nice("ctime: ", semid->sem_ctime);
228 	mdb_printf("binary: %s\n", semid->sem_binary ? "yes" : "no");
229 }
230 
231 typedef struct ipc_ops_vec {
232 	char	*iv_wcmd;	/* walker name		*/
233 	char	*iv_ocmd;	/* output dcmd		*/
234 	char	*iv_service;	/* service pointer	*/
235 	void	(*iv_print)(void *, uintptr_t); /* output callback */
236 	size_t	iv_idsize;
237 } ipc_ops_vec_t;
238 
239 ipc_ops_vec_t msq_ops_vec = {
240 	"msq",
241 	"kmsqid",
242 	"msq_svc",
243 	(void(*)(void *, uintptr_t))msq_print,
244 	sizeof (kmsqid_t)
245 };
246 
247 ipc_ops_vec_t shm_ops_vec = {
248 	"shm",
249 	"kshmid",
250 	"shm_svc",
251 	(void(*)(void *, uintptr_t))shm_print,
252 	sizeof (kshmid_t)
253 };
254 
255 ipc_ops_vec_t sem_ops_vec = {
256 	"sem",
257 	"ksemid",
258 	"sem_svc",
259 	(void(*)(void *, uintptr_t))sem_print,
260 	sizeof (ksemid_t)
261 };
262 
263 
264 /*
265  * Generic IPC data structure display code
266  */
267 static int
268 ds_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
269     ipc_ops_vec_t *iv)
270 {
271 	void *iddata;
272 
273 	if (!(flags & DCMD_ADDRSPEC)) {
274 		uint_t oflags = 0;
275 
276 		if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, 1, &oflags,
277 		    NULL) != argc)
278 			return (DCMD_USAGE);
279 
280 		if (mdb_walk_dcmd(iv->iv_wcmd, oflags ? iv->iv_ocmd : "ipcperm",
281 		    argc, argv) == -1) {
282 			mdb_warn("can't walk '%s'", iv->iv_wcmd);
283 			return (DCMD_ERR);
284 		}
285 		return (DCMD_OK);
286 	}
287 
288 	iddata = mdb_alloc(iv->iv_idsize, UM_SLEEP | UM_GC);
289 	if (mdb_vread(iddata, iv->iv_idsize, addr) == -1) {
290 		mdb_warn("failed to read %s at %#lx", iv->iv_ocmd, addr);
291 		return (DCMD_ERR);
292 	}
293 
294 	if (!DCMD_HDRSPEC(flags) && iv->iv_print)
295 		mdb_printf("\n");
296 
297 	if (DCMD_HDRSPEC(flags) || iv->iv_print)
298 		ipcperm_header();
299 
300 	ipcperm_print(addr, (struct kipc_perm *)iddata);
301 	if (iv->iv_print) {
302 		mdb_inc_indent(CMN_INDENT);
303 		iv->iv_print(iddata, addr);
304 		mdb_dec_indent(CMN_INDENT);
305 	}
306 
307 	return (DCMD_OK);
308 }
309 
310 
311 /*
312  * Stubs to call ds_print with the appropriate ops vector
313  */
314 static int
315 cmd_kshmid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
316 {
317 	return (ds_print(addr, flags, argc, argv, &shm_ops_vec));
318 }
319 
320 
321 static int
322 cmd_kmsqid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
323 {
324 	return (ds_print(addr, flags, argc, argv, &msq_ops_vec));
325 }
326 
327 static int
328 cmd_ksemid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
329 {
330 	return (ds_print(addr, flags, argc, argv, &sem_ops_vec));
331 }
332 
333 /*
334  * Generic IPC walker
335  */
336 
337 static int
338 ds_walk_init(mdb_walk_state_t *wsp)
339 {
340 	ipc_ops_vec_t	*iv = wsp->walk_arg;
341 
342 	if (wsp->walk_arg != NULL && wsp->walk_addr != NULL)
343 		mdb_printf("ignoring provided address\n");
344 
345 	if (wsp->walk_arg)
346 		if (mdb_readvar(&wsp->walk_addr, iv->iv_service) == -1) {
347 			mdb_printf("failed to read '%s'; module not present\n",
348 			    iv->iv_service);
349 			return (WALK_DONE);
350 		}
351 	else
352 		wsp->walk_addr = wsp->walk_addr +
353 		    OFFSETOF(ipc_service_t, ipcs_usedids);
354 
355 	if (mdb_layered_walk("list", wsp) == -1)
356 		return (WALK_ERR);
357 
358 	return (WALK_NEXT);
359 }
360 
361 
362 static int
363 ds_walk_step(mdb_walk_state_t *wsp)
364 {
365 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
366 	    wsp->walk_cbdata));
367 }
368 
369 /*
370  * Generic IPC ID/key to pointer code
371  */
372 
373 static int
374 ipcid_impl(uintptr_t svcptr, uintptr_t id, uintptr_t *addr)
375 {
376 	ipc_service_t service;
377 	kipc_perm_t perm;
378 	ipc_slot_t slot;
379 	uintptr_t slotptr;
380 	uint_t index;
381 
382 	if (id > INT_MAX) {
383 		mdb_warn("id out of range\n");
384 		return (DCMD_ERR);
385 	}
386 
387 	if (mdb_vread(&service, sizeof (ipc_service_t), svcptr) == -1) {
388 		mdb_warn("failed to read ipc_service_t at %#lx", svcptr);
389 		return (DCMD_ERR);
390 	}
391 
392 	index = (uint_t)id & (service.ipcs_tabsz - 1);
393 	slotptr = (uintptr_t)(service.ipcs_table + index);
394 
395 	if (mdb_vread(&slot, sizeof (ipc_slot_t), slotptr) == -1) {
396 		mdb_warn("failed to read ipc_slot_t at %#lx", slotptr);
397 		return (DCMD_ERR);
398 	}
399 
400 	if (slot.ipct_data == NULL)
401 		return (DCMD_ERR);
402 
403 	if (mdb_vread(&perm, sizeof (kipc_perm_t),
404 	    (uintptr_t)slot.ipct_data) == -1) {
405 		mdb_warn("failed to read kipc_perm_t at %#p",
406 		    slot.ipct_data);
407 		return (DCMD_ERR);
408 	}
409 
410 	if (perm.ipc_id != (uint_t)id)
411 		return (DCMD_ERR);
412 
413 	*addr = (uintptr_t)slot.ipct_data;
414 
415 	return (DCMD_OK);
416 }
417 
418 
419 typedef struct findkey_data {
420 	key_t fk_key;
421 	uintptr_t fk_addr;
422 	boolean_t fk_found;
423 } findkey_data_t;
424 
425 static int
426 findkey(uintptr_t addr, kipc_perm_t *perm, findkey_data_t *arg)
427 {
428 	if (perm->ipc_key == arg->fk_key) {
429 		arg->fk_found = B_TRUE;
430 		arg->fk_addr = addr;
431 		return (WALK_DONE);
432 	}
433 	return (WALK_NEXT);
434 }
435 
436 static int
437 ipckey_impl(uintptr_t svcptr, uintptr_t key, uintptr_t *addr)
438 {
439 	ipc_service_t	service;
440 	findkey_data_t	fkdata;
441 
442 	if ((key == IPC_PRIVATE) || (key > INT_MAX)) {
443 		mdb_warn("key out of range\n");
444 		return (DCMD_ERR);
445 	}
446 
447 	if (mdb_vread(&service, sizeof (ipc_service_t), svcptr) == -1) {
448 		mdb_warn("failed to read ipc_service_t at %#lx", svcptr);
449 		return (DCMD_ERR);
450 	}
451 
452 	fkdata.fk_key = (key_t)key;
453 	fkdata.fk_found = B_FALSE;
454 	if ((mdb_pwalk("avl", (mdb_walk_cb_t)findkey, &fkdata,
455 	    svcptr + OFFSETOF(ipc_service_t, ipcs_keys)) == -1) ||
456 	    !fkdata.fk_found)
457 		return (DCMD_ERR);
458 
459 	*addr = fkdata.fk_addr;
460 
461 	return (DCMD_OK);
462 }
463 
464 static int
465 ipckeyid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
466     int(*fp)(uintptr_t, uintptr_t, uintptr_t *))
467 {
468 	uintmax_t val;
469 	uintptr_t raddr;
470 	int result;
471 
472 	if (!(flags & DCMD_ADDRSPEC) || (argc != 1))
473 		return (DCMD_USAGE);
474 
475 	if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
476 		val = argv[0].a_un.a_val;
477 	else if (argv[0].a_type == MDB_TYPE_STRING)
478 		val = mdb_strtoull(argv[0].a_un.a_str);
479 	else
480 		return (DCMD_USAGE);
481 
482 	result = fp(addr, val, &raddr);
483 
484 	if (result == DCMD_OK)
485 		mdb_printf("%lx", raddr);
486 
487 	return (result);
488 }
489 
490 static int
491 ipckey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
492 {
493 	return (ipckeyid(addr, flags, argc, argv, ipckey_impl));
494 }
495 
496 static int
497 ipcid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
498 {
499 	return (ipckeyid(addr, flags, argc, argv, ipcid_impl));
500 }
501 
502 static int
503 ds_ptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
504     ipc_ops_vec_t *iv)
505 {
506 	uint_t		kflag = FALSE;
507 	uintptr_t	svcptr, raddr;
508 	int		result;
509 
510 	if (!(flags & DCMD_ADDRSPEC))
511 		return (DCMD_USAGE);
512 
513 	if (mdb_getopts(argc, argv,
514 	    'k', MDB_OPT_SETBITS, TRUE, &kflag, NULL) != argc)
515 		return (DCMD_USAGE);
516 
517 	if (mdb_readvar(&svcptr, iv->iv_service) == -1) {
518 		mdb_warn("failed to read '%s'; module not present\n",
519 		    iv->iv_service);
520 		return (DCMD_ERR);
521 	}
522 
523 	result = kflag ? ipckey_impl(svcptr, addr, &raddr) :
524 	    ipcid_impl(svcptr, addr, &raddr);
525 
526 	if (result == DCMD_OK)
527 		mdb_printf("%lx", raddr);
528 
529 	return (result);
530 }
531 
532 /*
533  * Stubs to call ds_ptr with the appropriate ops vector
534  */
535 static int
536 id2shm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
537 {
538 	return (ds_ptr(addr, flags, argc, argv, &shm_ops_vec));
539 }
540 
541 static int
542 id2msq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
543 {
544 	return (ds_ptr(addr, flags, argc, argv, &msq_ops_vec));
545 }
546 
547 static int
548 id2sem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
549 {
550 	return (ds_ptr(addr, flags, argc, argv, &sem_ops_vec));
551 }
552 
553 
554 /*
555  * The message queue contents walker
556  */
557 
558 static int
559 msg_walk_init(mdb_walk_state_t *wsp)
560 {
561 	wsp->walk_addr += OFFSETOF(kmsqid_t, msg_list);
562 	if (mdb_layered_walk("list", wsp) == -1)
563 		return (WALK_ERR);
564 
565 	return (WALK_NEXT);
566 }
567 
568 static int
569 msg_walk_step(mdb_walk_state_t *wsp)
570 {
571 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
572 	    wsp->walk_cbdata));
573 }
574 
575 /*
576  * The "::ipcs" command itself.  Just walks each IPC type in turn.
577  */
578 
579 /*ARGSUSED*/
580 static int
581 ipcs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
582 {
583 	uint_t	oflags = 0;
584 
585 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv, 'l',
586 	    MDB_OPT_SETBITS, 1, &oflags, NULL) != argc)
587 		return (DCMD_USAGE);
588 
589 	mdb_printf("Message queues:\n");
590 	if (mdb_walk_dcmd("msq", oflags ? "kmsqid" : "ipcperm", argc, argv) ==
591 	    -1) {
592 		mdb_warn("can't walk 'msq'");
593 		return (DCMD_ERR);
594 	}
595 
596 	mdb_printf("\nShared memory:\n");
597 	if (mdb_walk_dcmd("shm", oflags ? "kshmid" : "ipcperm", argc, argv) ==
598 	    -1) {
599 		mdb_warn("can't walk 'shm'");
600 		return (DCMD_ERR);
601 	}
602 
603 	mdb_printf("\nSemaphores:\n");
604 	if (mdb_walk_dcmd("sem", oflags ? "ksemid" : "ipcperm", argc, argv) ==
605 	    -1) {
606 		mdb_warn("can't walk 'sem'");
607 		return (DCMD_ERR);
608 	}
609 
610 	return (DCMD_OK);
611 }
612 
613 static int
614 msgprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
615 {
616 	struct msg message;
617 	uint_t	lflag = FALSE;
618 	long	type = 0;
619 	char	*tflag = NULL;
620 
621 	if (!(flags & DCMD_ADDRSPEC) || (mdb_getopts(argc, argv,
622 	    'l', MDB_OPT_SETBITS, TRUE, &lflag,
623 	    't', MDB_OPT_STR, &tflag, NULL) != argc))
624 		return (DCMD_USAGE);
625 
626 	/*
627 	 * Handle negative values.
628 	 */
629 	if (tflag != NULL) {
630 		if (*tflag == '-') {
631 			tflag++;
632 			type = -1;
633 		} else {
634 			type = 1;
635 		}
636 		type *= mdb_strtoull(tflag);
637 	}
638 
639 	if (DCMD_HDRSPEC(flags))
640 		mdb_printf("%<u>%?s %?s %8s %8s %8s%</u>\n",
641 		    "ADDR", "TEXT", "SIZE", "TYPE", "REF");
642 
643 	if (mdb_vread(&message, sizeof (struct msg), addr) == -1) {
644 		mdb_warn("failed to read msg at %#lx", addr);
645 		return (DCMD_ERR);
646 	}
647 
648 	/*
649 	 * If we are meeting our type contraints, display the message.
650 	 * If -l was specified, we will also display the message
651 	 * contents.
652 	 */
653 	if ((type == 0) ||
654 	    (type > 0 && message.msg_type == type) ||
655 	    (type < 0 && message.msg_type <= -type)) {
656 
657 		if (lflag && !DCMD_HDRSPEC(flags))
658 			mdb_printf("\n");
659 
660 		mdb_printf("%0?lx %?p %8ld %8ld %8ld\n", addr, message.msg_addr,
661 		    message.msg_size, message.msg_type, message.msg_copycnt);
662 
663 		if (lflag) {
664 			mdb_printf("\n");
665 			mdb_inc_indent(CMN_INDENT);
666 			if (mdb_dumpptr(
667 			    (uintptr_t)message.msg_addr, message.msg_size,
668 			    MDB_DUMP_RELATIVE | MDB_DUMP_TRIM |
669 			    MDB_DUMP_ASCII | MDB_DUMP_HEADER |
670 			    MDB_DUMP_GROUP(4),
671 			    (mdb_dumpptr_cb_t)mdb_vread, NULL)) {
672 				mdb_dec_indent(CMN_INDENT);
673 				return (DCMD_ERR);
674 			}
675 			mdb_dec_indent(CMN_INDENT);
676 		}
677 	}
678 
679 	return (DCMD_OK);
680 }
681 
682 /*
683  * MDB module linkage
684  */
685 static const mdb_dcmd_t dcmds[] = {
686 	/* Generic routines */
687 	{ "ipcperm", ":", "display an IPC perm structure", ipcperm },
688 	{ "ipcid", ":id", "perform an IPC id lookup", ipcid },
689 	{ "ipckey", ":key", "perform an IPC key lookup", ipckey },
690 
691 	/* Specific routines */
692 	{ "kshmid", "?[-l]", "display a struct kshmid", cmd_kshmid },
693 	{ "kmsqid", "?[-l]", "display a struct kmsqid", cmd_kmsqid },
694 	{ "ksemid", "?[-l]", "display a struct ksemid", cmd_ksemid },
695 	{ "msg", ":[-l] [-t type]", "display contents of a message", msgprint },
696 
697 	/* Convenience routines */
698 	{ "id2shm", ":[-k]", "convert shared memory ID to pointer", id2shm },
699 	{ "id2msq", ":[-k]", "convert message queue ID to pointer", id2msq },
700 	{ "id2sem", ":[-k]", "convert semaphore ID to pointer", id2sem },
701 
702 	{ "ipcs", "[-l]", "display System V IPC information", ipcs },
703 	{ NULL }
704 };
705 
706 static const mdb_walker_t walkers[] = {
707 	{ "ipcsvc", "walk a System V IPC service",
708 		ds_walk_init, ds_walk_step },
709 	{ "shm", "walk the active shmid_ds structures",
710 		ds_walk_init, ds_walk_step, NULL, &shm_ops_vec },
711 	{ "msq", "walk the active msqid_ds structures",
712 		ds_walk_init, ds_walk_step, NULL, &msq_ops_vec },
713 	{ "sem", "walk the active semid_ds structures",
714 		ds_walk_init, ds_walk_step, NULL, &sem_ops_vec },
715 	{ "msgqueue", "walk messages on a message queue",
716 		msg_walk_init, msg_walk_step },
717 	{ NULL }
718 };
719 
720 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
721 
722 const mdb_modinfo_t *
723 _mdb_init(void)
724 {
725 	return (&modinfo);
726 }
727