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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <mdb/mdb_modapi.h>
27 #include <mdb/mdb_ks.h>
29 #include <sys/types.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>
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"
45 * Bitmap data for page protection flags suitable for use with %b.
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
},
56 printtime_nice(const char *str
, time_t time
)
59 mdb_printf("%s%Y\n", str
, time
);
61 mdb_printf("%sn/a\n", str
);
65 * Print header common to all IPC types.
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");
76 * Print data common to all IPC types.
79 ipcperm_print(uintptr_t addr
, kipc_perm_t
*perm
)
84 res
= mdb_vread(&proj
, sizeof (kproject_t
), (uintptr_t)perm
->ipc_proj
);
87 mdb_warn("failed to read kproject_t at %#p", perm
->ipc_proj
);
89 mdb_printf("%0?p %5d %5d", addr
, perm
->ipc_ref
, perm
->ipc_id
);
91 mdb_printf(" %8x", perm
->ipc_key
);
93 mdb_printf(" %8s", "private");
94 mdb_printf(" %5#o", perm
->ipc_mode
& 07777);
96 mdb_printf(" %5s %5s", "<flt>", "<flt>");
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
);
106 ipcperm(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
110 if (!(flags
& DCMD_ADDRSPEC
))
113 if (DCMD_HDRSPEC(flags
))
116 if (mdb_vread(&perm
, sizeof (kipc_perm_t
), addr
) == -1) {
117 mdb_warn("failed to read kipc_perm_t at %#lx", addr
);
121 ipcperm_print(addr
, &perm
);
126 #define MSG_SND_SIZE 0x1
128 msgq_check_for_waiters(list_t
*walk_this
, int min
, int max
,
129 int copy_wait
, uintptr_t addr
, int flag
)
134 msgq_wakeup_t
*walker
, next
;
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
) {
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) {
147 "Failed to read message queue\n");
151 if (flag
& MSG_SND_SIZE
) {
152 mdb_printf("%15lx\t%6d\t%15lx\t%15d\n",
153 next
.msgw_thrd
, next
.msgw_type
,
155 OFFSETOF(msgq_wakeup_t
,
156 msgw_wake_cv
), next
.msgw_snd_size
);
158 mdb_printf("%15lx\t%6d\t%15lx\t%15s\n",
159 next
.msgw_thrd
, next
.msgw_type
,
161 OFFSETOF(msgq_wakeup_t
,
163 (copy_wait
? "yes":"no"));
167 (msgq_wakeup_t
*)next
.msgw_list
.list_next
;
175 msq_print(kmsqid_t
*msqid
, uintptr_t addr
)
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
),
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
,
208 mdb_printf("Total number of waiters: %d\n", total
);
214 shm_print(kshmid_t
*shmid
, uintptr_t addr
)
218 nattch
= shmid
->shm_perm
.ipc_ref
- (IPC_FREE(&shmid
->shm_perm
) ? 0 : 1);
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
);
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
);
238 sem_print(ksemid_t
*semid
, uintptr_t addr
)
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");
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 */
255 ipc_ops_vec_t msq_ops_vec
= {
259 (void(*)(void *, uintptr_t))msq_print
,
263 ipc_ops_vec_t shm_ops_vec
= {
267 (void(*)(void *, uintptr_t))shm_print
,
271 ipc_ops_vec_t sem_ops_vec
= {
275 (void(*)(void *, uintptr_t))sem_print
,
281 * Generic IPC data structure display code
284 ds_print(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
,
289 if (!(flags
& DCMD_ADDRSPEC
)) {
292 if (mdb_getopts(argc
, argv
, 'l', MDB_OPT_SETBITS
, 1, &oflags
,
296 if (mdb_walk_dcmd(iv
->iv_wcmd
, oflags
? iv
->iv_ocmd
: "ipcperm",
298 mdb_warn("can't walk '%s'", iv
->iv_wcmd
);
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
);
310 if (!DCMD_HDRSPEC(flags
) && iv
->iv_print
)
313 if (DCMD_HDRSPEC(flags
) || iv
->iv_print
)
316 ipcperm_print(addr
, (struct kipc_perm
*)iddata
);
318 mdb_inc_indent(CMN_INDENT
);
319 iv
->iv_print(iddata
, addr
);
320 mdb_dec_indent(CMN_INDENT
);
328 * Stubs to call ds_print with the appropriate ops vector
331 cmd_kshmid(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
333 return (ds_print(addr
, flags
, argc
, argv
, &shm_ops_vec
));
338 cmd_kmsqid(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
340 return (ds_print(addr
, flags
, argc
, argv
, &msq_ops_vec
));
344 cmd_ksemid(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
346 return (ds_print(addr
, flags
, argc
, argv
, &sem_ops_vec
));
354 ds_walk_init(mdb_walk_state_t
*wsp
)
356 ipc_ops_vec_t
*iv
= wsp
->walk_arg
;
358 if (wsp
->walk_arg
!= NULL
&& wsp
->walk_addr
!= NULL
)
359 mdb_printf("ignoring provided address\n");
362 if (mdb_readvar(&wsp
->walk_addr
, iv
->iv_service
) == -1) {
363 mdb_printf("failed to read '%s'; module not present\n",
368 wsp
->walk_addr
= wsp
->walk_addr
+
369 OFFSETOF(ipc_service_t
, ipcs_usedids
);
371 if (mdb_layered_walk("list", wsp
) == -1)
379 ds_walk_step(mdb_walk_state_t
*wsp
)
381 return (wsp
->walk_callback(wsp
->walk_addr
, wsp
->walk_layer
,
386 * Generic IPC ID/key to pointer code
390 ipcid_impl(uintptr_t svcptr
, uintptr_t id
, uintptr_t *addr
)
392 ipc_service_t service
;
399 mdb_warn("id out of range\n");
403 if (mdb_vread(&service
, sizeof (ipc_service_t
), svcptr
) == -1) {
404 mdb_warn("failed to read ipc_service_t at %#lx", svcptr
);
408 index
= (uint_t
)id
& (service
.ipcs_tabsz
- 1);
409 slotptr
= (uintptr_t)(service
.ipcs_table
+ index
);
411 if (mdb_vread(&slot
, sizeof (ipc_slot_t
), slotptr
) == -1) {
412 mdb_warn("failed to read ipc_slot_t at %#lx", slotptr
);
416 if (slot
.ipct_data
== NULL
)
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",
426 if (perm
.ipc_id
!= (uint_t
)id
)
429 *addr
= (uintptr_t)slot
.ipct_data
;
435 typedef struct findkey_data
{
442 findkey(uintptr_t addr
, kipc_perm_t
*perm
, findkey_data_t
*arg
)
444 if (perm
->ipc_key
== arg
->fk_key
) {
445 arg
->fk_found
= B_TRUE
;
453 ipckey_impl(uintptr_t svcptr
, uintptr_t key
, uintptr_t *addr
)
455 ipc_service_t service
;
456 findkey_data_t fkdata
;
458 if ((key
== IPC_PRIVATE
) || (key
> INT_MAX
)) {
459 mdb_warn("key out of range\n");
463 if (mdb_vread(&service
, sizeof (ipc_service_t
), svcptr
) == -1) {
464 mdb_warn("failed to read ipc_service_t at %#lx", svcptr
);
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) ||
475 *addr
= fkdata
.fk_addr
;
481 ipckeyid(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
,
482 int(*fp
)(uintptr_t, uintptr_t, uintptr_t *))
488 if (!(flags
& DCMD_ADDRSPEC
) || (argc
!= 1))
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
);
498 result
= fp(addr
, val
, &raddr
);
500 if (result
== DCMD_OK
)
501 mdb_printf("%lx", raddr
);
507 ipckey(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
509 return (ipckeyid(addr
, flags
, argc
, argv
, ipckey_impl
));
513 ipcid(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
515 return (ipckeyid(addr
, flags
, argc
, argv
, ipcid_impl
));
519 ds_ptr(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
,
522 uint_t kflag
= FALSE
;
523 uintptr_t svcptr
, raddr
;
526 if (!(flags
& DCMD_ADDRSPEC
))
529 if (mdb_getopts(argc
, argv
,
530 'k', MDB_OPT_SETBITS
, TRUE
, &kflag
, NULL
) != argc
)
533 if (mdb_readvar(&svcptr
, iv
->iv_service
) == -1) {
534 mdb_warn("failed to read '%s'; module not present\n",
539 result
= kflag
? ipckey_impl(svcptr
, addr
, &raddr
) :
540 ipcid_impl(svcptr
, addr
, &raddr
);
542 if (result
== DCMD_OK
)
543 mdb_printf("%lx", raddr
);
549 * Stubs to call ds_ptr with the appropriate ops vector
552 id2shm(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
554 return (ds_ptr(addr
, flags
, argc
, argv
, &shm_ops_vec
));
558 id2msq(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
560 return (ds_ptr(addr
, flags
, argc
, argv
, &msq_ops_vec
));
564 id2sem(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
566 return (ds_ptr(addr
, flags
, argc
, argv
, &sem_ops_vec
));
571 * The message queue contents walker
575 msg_walk_init(mdb_walk_state_t
*wsp
)
577 wsp
->walk_addr
+= OFFSETOF(kmsqid_t
, msg_list
);
578 if (mdb_layered_walk("list", wsp
) == -1)
585 msg_walk_step(mdb_walk_state_t
*wsp
)
587 return (wsp
->walk_callback(wsp
->walk_addr
, wsp
->walk_layer
,
592 * The "::ipcs" command itself. Just walks each IPC type in turn.
597 ipcs(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
601 if ((flags
& DCMD_ADDRSPEC
) || mdb_getopts(argc
, argv
, 'l',
602 MDB_OPT_SETBITS
, 1, &oflags
, NULL
) != argc
)
605 mdb_printf("Message queues:\n");
606 if (mdb_walk_dcmd("msq", oflags
? "kmsqid" : "ipcperm", argc
, argv
) ==
608 mdb_warn("can't walk 'msq'");
612 mdb_printf("\nShared memory:\n");
613 if (mdb_walk_dcmd("shm", oflags
? "kshmid" : "ipcperm", argc
, argv
) ==
615 mdb_warn("can't walk 'shm'");
619 mdb_printf("\nSemaphores:\n");
620 if (mdb_walk_dcmd("sem", oflags
? "ksemid" : "ipcperm", argc
, argv
) ==
622 mdb_warn("can't walk 'sem'");
630 msgprint(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
633 uint_t lflag
= FALSE
;
637 if (!(flags
& DCMD_ADDRSPEC
) || (mdb_getopts(argc
, argv
,
638 'l', MDB_OPT_SETBITS
, TRUE
, &lflag
,
639 't', MDB_OPT_STR
, &tflag
, NULL
) != argc
))
643 * Handle negative values.
652 type
*= mdb_strtoull(tflag
);
655 if (DCMD_HDRSPEC(flags
))
656 mdb_printf("%<u>%?s %?s %8s %8s %8s%</u>\n",
657 "ADDR", "TEXT", "SIZE", "TYPE", "REF");
659 if (mdb_vread(&message
, sizeof (struct msg
), addr
) == -1) {
660 mdb_warn("failed to read msg at %#lx", addr
);
665 * If we are meeting our type contraints, display the message.
666 * If -l was specified, we will also display the message
670 (type
> 0 && message
.msg_type
== type
) ||
671 (type
< 0 && message
.msg_type
<= -type
)) {
673 if (lflag
&& !DCMD_HDRSPEC(flags
))
676 mdb_printf("%0?lx %?p %8ld %8ld %8ld\n", addr
, message
.msg_addr
,
677 message
.msg_size
, message
.msg_type
, message
.msg_copycnt
);
681 mdb_inc_indent(CMN_INDENT
);
683 (uintptr_t)message
.msg_addr
, message
.msg_size
,
684 MDB_DUMP_RELATIVE
| MDB_DUMP_TRIM
|
685 MDB_DUMP_ASCII
| MDB_DUMP_HEADER
|
687 (mdb_dumpptr_cb_t
)mdb_vread
, NULL
)) {
688 mdb_dec_indent(CMN_INDENT
);
691 mdb_dec_indent(CMN_INDENT
);
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
},
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
},
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
},
718 { "ipcs", "[-l]", "display System V IPC information", ipcs
},
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
},
736 static const mdb_modinfo_t modinfo
= { MDB_API_VERSION
, dcmds
, walkers
};
738 const mdb_modinfo_t
*