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