4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
29 #include <sys/modhash_impl.h>
31 #include <mdb/mdb_modapi.h>
32 #include <mdb/mdb_ks.h>
36 /* This is passed to the modent callback; allows caller to get context */
37 typedef struct modent_step_data_s
{
38 struct mod_hash_entry msd_mhe
; /* must be first */
40 int msd_position
; /* entry position in chain */
41 uintptr_t msd_first_addr
; /* first address in chain */
44 /* Context for a walk over a modhash (variable length) */
45 typedef struct hash_walk_s
{
46 modent_step_data_t hwalk_msd
; /* current entry data */
47 mod_hash_t hwalk_hash
; /* always last (var. len) */
50 /* Computes number of bytes to allocate for hash_walk_t structure. */
51 #define HW_SIZE(n) (sizeof (modent_step_data_t) + MH_SIZE(n))
53 /* Used for decoding hash keys for display */
54 typedef struct hash_type_entry_s
{
55 const char *hte_type
; /* name of hash type for ::modent -t */
56 const char *hte_comparator
; /* name of comparator function */
57 void (*hte_format
)(const mod_hash_key_t
, char *, size_t);
60 static void format_strhash(const mod_hash_key_t
, char *, size_t);
61 static void format_ptrhash(const mod_hash_key_t
, char *, size_t);
62 static void format_idhash(const mod_hash_key_t
, char *, size_t);
63 static void format_default(const mod_hash_key_t
, char *, size_t);
65 static const hash_type_entry_t hte_table
[] = {
66 { "str", "mod_hash_strkey_cmp", format_strhash
},
67 { "ptr", "mod_hash_ptrkey_cmp", format_ptrhash
},
68 { "id", "mod_hash_idkey_cmp", format_idhash
},
69 { NULL
, NULL
, format_default
}
72 static int modent_print(uintptr_t, int, uint_t
, const hash_type_entry_t
*,
73 boolean_t
, uint_t
, uint_t
);
75 /* The information used during a walk */
76 typedef struct mod_walk_data_s
{
77 const hash_type_entry_t
*mwd_hte
; /* pointer to entry type */
78 int mwd_main_flags
; /* ::modhash flags */
79 int mwd_flags
; /* DCMD_* flags for looping */
80 uint_t mwd_opt_e
; /* call-modent mode */
81 uint_t mwd_opt_c
; /* chain head only mode */
82 uint_t mwd_opt_h
; /* hash index output */
83 boolean_t mwd_opt_k_set
; /* key supplied */
84 boolean_t mwd_opt_v_set
; /* value supplied */
85 uintptr_t mwd_opt_k
; /* key */
86 uintptr_t mwd_opt_v
; /* value */
87 int mwd_maxposn
; /* len of longest chain - 1 */
88 int mwd_maxidx
; /* hash idx of longest chain */
89 uintptr_t mwd_maxaddr
; /* addr of 1st elem @ maxidx */
90 uintptr_t mwd_idxtoprint
; /* desired hash pos to print */
91 uintptr_t mwd_addr
; /* 1st elem addr @idxtoprint */
95 * Initialize a walk over all the modhashes in the system.
98 modhash_walk_init(mdb_walk_state_t
*wsp
)
102 if (mdb_readvar(&mh_head
, "mh_head") == -1) {
103 mdb_warn("failed to read mh_head");
106 wsp
->walk_addr
= (uintptr_t)mh_head
;
112 * Step to the next modhash in the system.
115 modhash_walk_step(mdb_walk_state_t
*wsp
)
120 if (wsp
->walk_addr
== (uintptr_t)NULL
)
123 if (mdb_vread(&mh
, sizeof (mh
), wsp
->walk_addr
) == -1) {
124 mdb_warn("failed to read mod_hash_t at %p", wsp
->walk_addr
);
128 status
= wsp
->walk_callback(wsp
->walk_addr
, &mh
, wsp
->walk_cbdata
);
130 wsp
->walk_addr
= (uintptr_t)mh
.mh_next
;
136 * Initialize a walk over the entries in a given modhash.
139 modent_walk_init(mdb_walk_state_t
*wsp
)
145 if (wsp
->walk_addr
== (uintptr_t)NULL
) {
146 mdb_warn("mod_hash_t address required\n");
150 if (mdb_vread(&mh
, sizeof (mh
), wsp
->walk_addr
) == -1) {
151 mdb_warn("failed to read mod_hash_t at %p", wsp
->walk_addr
);
155 if (mh
.mh_nchains
<= 1) {
156 mdb_warn("impossible number of chains in mod_hash_t at %p",
162 * If the user presents us with a garbage pointer, and thus the number
163 * of chains is just absurd, we don't want to bail out of mdb. Fail to
166 hwp
= mdb_alloc(HW_SIZE(mh
.mh_nchains
), UM_NOSLEEP
);
168 mdb_warn("unable to allocate %#x bytes for mod_hash_t at %p",
169 HW_SIZE(mh
.mh_nchains
), wsp
->walk_addr
);
173 (void) memcpy(&hwp
->hwalk_hash
, &mh
, sizeof (hwp
->hwalk_hash
));
175 retv
= mdb_vread(hwp
->hwalk_hash
.mh_entries
+ 1,
176 (mh
.mh_nchains
- 1) * sizeof (struct mod_hash_entry
*),
177 wsp
->walk_addr
+ sizeof (mh
));
180 mdb_free(hwp
, HW_SIZE(mh
.mh_nchains
));
181 mdb_warn("failed to read %#x mod_hash_entry pointers at %p",
182 mh
.mh_nchains
- 1, wsp
->walk_addr
+ sizeof (mh
));
186 hwp
->hwalk_msd
.msd_hash_index
= -1;
187 hwp
->hwalk_msd
.msd_position
= 0;
188 hwp
->hwalk_msd
.msd_first_addr
= (uintptr_t)NULL
;
190 wsp
->walk_addr
= (uintptr_t)NULL
;
191 wsp
->walk_data
= hwp
;
197 * Step to the next entry in the modhash.
200 modent_walk_step(mdb_walk_state_t
*wsp
)
202 hash_walk_t
*hwp
= wsp
->walk_data
;
205 while (wsp
->walk_addr
== (uintptr_t)NULL
) {
206 hwp
->hwalk_msd
.msd_position
= 0;
207 if (++hwp
->hwalk_msd
.msd_hash_index
>=
208 hwp
->hwalk_hash
.mh_nchains
)
210 wsp
->walk_addr
= hwp
->hwalk_msd
.msd_first_addr
=
211 (uintptr_t)hwp
->hwalk_hash
.mh_entries
[
212 hwp
->hwalk_msd
.msd_hash_index
];
215 if (mdb_vread(&hwp
->hwalk_msd
.msd_mhe
, sizeof (hwp
->hwalk_msd
.msd_mhe
),
216 wsp
->walk_addr
) == -1) {
217 mdb_warn("failed to read mod_hash_entry at %p",
222 status
= wsp
->walk_callback(wsp
->walk_addr
, &hwp
->hwalk_msd
,
225 hwp
->hwalk_msd
.msd_position
++;
226 wsp
->walk_addr
= (uintptr_t)hwp
->hwalk_msd
.msd_mhe
.mhe_next
;
232 * Clean up after walking the entries in a modhash.
235 modent_walk_fini(mdb_walk_state_t
*wsp
)
237 hash_walk_t
*hwp
= wsp
->walk_data
;
239 mdb_free(hwp
, HW_SIZE(hwp
->hwalk_hash
.mh_nchains
));
240 wsp
->walk_data
= NULL
;
244 * Step to next entry on a hash chain.
247 modchain_walk_step(mdb_walk_state_t
*wsp
)
249 struct mod_hash_entry mhe
;
252 if (wsp
->walk_addr
== (uintptr_t)NULL
)
255 if (mdb_vread(&mhe
, sizeof (mhe
), wsp
->walk_addr
) == -1) {
256 mdb_warn("failed to read mod_hash_entry at %p",
261 status
= wsp
->walk_callback(wsp
->walk_addr
, &mhe
, wsp
->walk_cbdata
);
263 wsp
->walk_addr
= (uintptr_t)mhe
.mhe_next
;
269 * This is called by ::modhash (via a callback) when gathering data about the
270 * entries in a given modhash. It keeps track of the longest chain, finds a
271 * specific entry (if the user requested one) and prints out a summary of the
275 modent_format(uintptr_t addr
, const void *data
, void *private)
277 const modent_step_data_t
*msd
= data
;
278 mod_walk_data_t
*mwd
= private;
281 /* If this chain is longest seen, then save start of chain */
282 if (msd
->msd_position
> mwd
->mwd_maxposn
) {
283 mwd
->mwd_maxposn
= msd
->msd_position
;
284 mwd
->mwd_maxidx
= msd
->msd_hash_index
;
285 mwd
->mwd_maxaddr
= msd
->msd_first_addr
;
288 /* If the user specified a particular chain, then ignore others */
289 if (mwd
->mwd_idxtoprint
!= (uintptr_t)-1) {
290 /* Save address of *first* entry */
291 if (mwd
->mwd_idxtoprint
== msd
->msd_hash_index
)
292 mwd
->mwd_addr
= msd
->msd_first_addr
;
297 /* If the user specified a particular key, ignore others. */
298 if (mwd
->mwd_opt_k_set
&&
299 (uintptr_t)msd
->msd_mhe
.mhe_key
!= mwd
->mwd_opt_k
)
302 /* If the user specified a particular value, ignore others. */
303 if (mwd
->mwd_opt_v_set
&&
304 (uintptr_t)msd
->msd_mhe
.mhe_val
!= mwd
->mwd_opt_v
)
307 /* If the user just wants the chain heads, skip intermediate nodes. */
308 if (mwd
->mwd_opt_c
&& msd
->msd_position
!= 0)
311 /* If the user asked to have the entries printed, then do that. */
312 if (mwd
->mwd_opt_e
) {
313 /* If the output is to a pipeline, just print addresses */
314 if (mwd
->mwd_main_flags
& DCMD_PIPE_OUT
)
315 mdb_printf("%p\n", addr
);
317 retv
= modent_print(addr
, msd
->msd_hash_index
,
318 mwd
->mwd_flags
, mwd
->mwd_hte
, mwd
->mwd_opt_h
, 0, 0);
319 mwd
->mwd_flags
&= ~DCMD_LOOPFIRST
;
327 mdb_printf("Prints information about one or all mod_hash_t databases "
329 "This command has three basic forms, summarized below.\n\n"
330 " ::modhash [-t]\n <addr>::modhash\n"
331 " <addr>::modhash -e [-ch] [-k key] [-v val] [-i index]\n\n"
332 "In the first form, no address is provided, and a summary of all "
334 "hashes in the system is printed; adding the '-t' option shows"
336 "type instead of the limits. In the second form, the address of a"
338 "is provided, and the output is in a verbose format. The final "
340 "the elements of the hash, optionally selecting just those with a "
342 "key, value, and/or hash index, or just the chain heads (-c). "
344 "shows hash indices instead of addresses.\n");
348 modhash(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
354 uint_t opt_s
= FALSE
;
355 uint_t opt_t
= FALSE
;
356 char kfunc
[MDB_SYM_NAMLEN
];
357 const hash_type_entry_t
*htep
;
358 boolean_t elem_flags
;
360 (void) memset(&mwd
, 0, sizeof (mwd
));
361 mwd
.mwd_main_flags
= flags
;
362 mwd
.mwd_flags
= DCMD_ADDRSPEC
| DCMD_LOOP
| DCMD_LOOPFIRST
;
363 mwd
.mwd_maxposn
= -1;
364 mwd
.mwd_idxtoprint
= (uintptr_t)-1;
366 len
= mdb_getopts(argc
, argv
,
367 's', MDB_OPT_SETBITS
, TRUE
, &opt_s
,
368 't', MDB_OPT_SETBITS
, TRUE
, &opt_t
,
369 'c', MDB_OPT_SETBITS
, TRUE
, &mwd
.mwd_opt_c
,
370 'e', MDB_OPT_SETBITS
, TRUE
, &mwd
.mwd_opt_e
,
371 'h', MDB_OPT_SETBITS
, TRUE
, &mwd
.mwd_opt_h
,
372 'i', MDB_OPT_UINTPTR
, &mwd
.mwd_idxtoprint
,
373 'k', MDB_OPT_UINTPTR_SET
, &mwd
.mwd_opt_k_set
, &mwd
.mwd_opt_k
,
374 'v', MDB_OPT_UINTPTR_SET
, &mwd
.mwd_opt_v_set
, &mwd
.mwd_opt_v
,
379 if (argv
->a_type
== MDB_TYPE_STRING
)
380 mdb_warn("unexpected argument: %s\n",
383 mdb_warn("unexpected argument(s)\n");
387 /* true if any element-related flags are set */
388 elem_flags
= mwd
.mwd_opt_c
|| mwd
.mwd_opt_e
|| mwd
.mwd_opt_h
||
389 mwd
.mwd_opt_k_set
|| mwd
.mwd_opt_v_set
||
390 mwd
.mwd_idxtoprint
!= (uintptr_t)-1;
392 if (!(flags
& DCMD_ADDRSPEC
)) {
393 mdb_arg_t new_argv
[1];
397 * This isn't allowed so that the output doesn't become
398 * a confusing mix of hash table descriptions and
401 mdb_warn("printing elements from all hashes is not "
405 /* we force short mode here, no matter what it says */
406 new_argv
[0].a_type
= MDB_TYPE_STRING
;
407 new_argv
[0].a_un
.a_str
= opt_t
? "-st" : "-s";
408 if (mdb_walk_dcmd("modhash", "modhash", 1, new_argv
) == -1) {
409 mdb_warn("can't walk mod_hash structures");
417 mdb_warn("hash summary options not permitted when "
418 "displaying elements\n");
424 * This isn't allowed so that the output doesn't become
425 * a confusing mix of hash table description and
428 mdb_warn("printing elements requires -e\n");
433 if (mdb_vread(&mh
, sizeof (mh
), addr
) == -1) {
434 mdb_warn("failed to read mod_hash_t at %p", addr
);
438 if (mwd
.mwd_idxtoprint
!= (uintptr_t)-1 &&
439 mwd
.mwd_idxtoprint
>= mh
.mh_nchains
) {
440 mdb_warn("mod_hash chain index %x out of range 0..%x\n",
441 mwd
.mwd_idxtoprint
, mh
.mh_nchains
- 1);
445 if (DCMD_HDRSPEC(flags
) && opt_s
) {
447 mdb_printf("%<u>%?s %6s %5s %?s %s%</u>\n",
448 "ADDR", "CHAINS", "ELEMS", "TYPE", "NAME");
450 mdb_printf("%<u>%?s %6s %5s %6s %6s %s%</u>\n",
451 "ADDR", "CHAINS", "ELEMS", "MAXLEN", "MAXIDX",
456 len
= mdb_readstr(name
, sizeof (name
), (uintptr_t)mh
.mh_name
);
458 (void) strcpy(name
, "??");
460 if (mdb_lookup_by_addr((uintptr_t)mh
.mh_keycmp
, MDB_SYM_EXACT
, kfunc
,
461 sizeof (kfunc
), NULL
) == -1)
463 for (htep
= hte_table
; htep
->hte_type
!= NULL
; htep
++)
464 if (strcmp(kfunc
, htep
->hte_comparator
) == 0)
468 if (!mwd
.mwd_opt_e
&& !opt_s
) {
469 mdb_printf("mod_hash_t %?p %s%s:\n", addr
, name
,
470 len
== sizeof (name
) ? "..." : "");
471 mdb_printf("\tKey comparator: %?p %s\n",
472 mh
.mh_keycmp
, kfunc
);
473 mdb_printf("\tType: %s\n",
474 htep
->hte_type
== NULL
? "unknown" : htep
->hte_type
);
475 mdb_printf("\tSleep flag = %s, alloc failed = %#x\n",
476 mh
.mh_sleep
? "true" : "false",
477 mh
.mh_stat
.mhs_nomem
);
478 mdb_printf("\tNumber of chains = %#x, elements = %#x\n",
479 mh
.mh_nchains
, mh
.mh_stat
.mhs_nelems
);
480 mdb_printf("\tHits = %#x, misses = %#x, dups = %#x\n",
481 mh
.mh_stat
.mhs_hit
, mh
.mh_stat
.mhs_miss
,
482 mh
.mh_stat
.mhs_coll
);
484 if (mdb_pwalk("modent", modent_format
, &mwd
, addr
) == -1) {
485 mdb_warn("can't walk mod_hash entries");
492 if (htep
->hte_type
== NULL
) {
493 (void) mdb_snprintf(tbuf
, sizeof (tbuf
), "%p",
497 tname
= htep
->hte_type
;
499 mdb_printf("%?p %6x %5x ", addr
, mh
.mh_nchains
,
500 mh
.mh_stat
.mhs_nelems
);
502 mdb_printf("%?s", tname
);
504 mdb_printf("%6x %6x", mwd
.mwd_maxposn
+ 1,
507 mdb_printf(" %s%s\n", name
, len
== sizeof (name
) ? "..." : "");
508 } else if (!mwd
.mwd_opt_e
) {
509 mdb_printf("\tMaximum chain length = %x (at index %x, first "
510 "entry %p)\n", mwd
.mwd_maxposn
+ 1, mwd
.mwd_maxidx
,
517 format_strhash(const mod_hash_key_t key
, char *keystr
, size_t keystrlen
)
521 (void) mdb_snprintf(keystr
, keystrlen
, "%?p ", key
);
522 len
= strlen(keystr
);
523 (void) mdb_readstr(keystr
+ len
, keystrlen
- len
, (uintptr_t)key
);
527 format_ptrhash(const mod_hash_key_t key
, char *keystr
, size_t keystrlen
)
531 (void) mdb_snprintf(keystr
, keystrlen
, "%?p ", key
);
532 len
= strlen(keystr
);
533 (void) mdb_lookup_by_addr((uintptr_t)key
, MDB_SYM_EXACT
, keystr
+ len
,
534 keystrlen
- len
, NULL
);
538 format_idhash(const mod_hash_key_t key
, char *keystr
, size_t keystrlen
)
540 (void) mdb_snprintf(keystr
, keystrlen
, "%?x", (uint_t
)(uintptr_t)key
);
544 format_default(const mod_hash_key_t key
, char *keystr
, size_t keystrlen
)
546 (void) mdb_snprintf(keystr
, keystrlen
, "%?p", key
);
552 mdb_printf("Options are mutually exclusive:\n"
553 " -t <type> print key in symbolic form; <type> is one of str, "
555 " -v print value pointer alone\n"
556 " -k print key pointer alone\n");
560 modent_print(uintptr_t addr
, int hidx
, uint_t flags
,
561 const hash_type_entry_t
*htep
, boolean_t prtidx
, uint_t opt_k
,
565 struct mod_hash_entry mhe
;
567 if (DCMD_HDRSPEC(flags
) && opt_k
== 0 && opt_v
== 0) {
568 mdb_printf("%<u>%?s %?s %?s%</u>\n",
569 prtidx
? "HASH_IDX" : "ADDR", "VAL", "KEY");
572 if (mdb_vread(&mhe
, sizeof (mhe
), addr
) == -1) {
573 mdb_warn("failed to read mod_hash_entry at %p", addr
);
578 mdb_printf("%p\n", mhe
.mhe_key
);
580 mdb_printf("%p\n", mhe
.mhe_val
);
582 htep
->hte_format(mhe
.mhe_key
, keystr
, sizeof (keystr
));
584 mdb_printf("%?x", hidx
);
586 mdb_printf("%?p", addr
);
587 mdb_printf(" %?p %s\n", mhe
.mhe_val
, keystr
);
594 * This prints out a single mod_hash element, showing its value and its key.
595 * The key is decoded based on the type of hash keys in use.
598 modent(uintptr_t addr
, uint_t flags
, int argc
, const mdb_arg_t
*argv
)
600 const char *opt_t
= NULL
;
601 const hash_type_entry_t
*htep
;
606 if (!(flags
& DCMD_ADDRSPEC
)) {
607 mdb_warn("address of mod_hash_entry must be specified\n");
611 len
= mdb_getopts(argc
, argv
,
612 't', MDB_OPT_STR
, &opt_t
,
613 'k', MDB_OPT_SETBITS
, 1, &opt_k
,
614 'v', MDB_OPT_SETBITS
, 1, &opt_v
,
617 /* options are mutually exclusive */
618 if ((opt_k
&& opt_v
) || (opt_t
!= NULL
&& (opt_k
|| opt_v
)) ||
623 for (htep
= hte_table
; htep
->hte_type
!= NULL
; htep
++)
624 if (opt_t
!= NULL
&& strcmp(opt_t
, htep
->hte_type
) == 0)
627 if (opt_t
!= NULL
&& htep
->hte_type
== NULL
) {
628 mdb_warn("unknown hash type %s\n", opt_t
);
632 return (modent_print(addr
, 0, flags
, htep
, FALSE
, opt_k
, opt_v
));