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