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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 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. 13 * 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] 19 * 20 * CDDL HEADER END 21 */ 22 /* 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. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <string.h> 31 #include <sys/modhash_impl.h> 32 33 #include <mdb/mdb_modapi.h> 34 #include <mdb/mdb_ks.h> 35 36 #include "modhash.h" 37 38 /* This is passed to the modent callback; allows caller to get context */ 39 typedef struct modent_step_data_s { 40 struct mod_hash_entry msd_mhe; /* must be first */ 41 int msd_hash_index; 42 int msd_position; /* entry position in chain */ 43 uintptr_t msd_first_addr; /* first address in chain */ 44 } modent_step_data_t; 45 46 /* Context for a walk over a modhash (variable length) */ 47 typedef struct hash_walk_s { 48 modent_step_data_t hwalk_msd; /* current entry data */ 49 mod_hash_t hwalk_hash; /* always last (var. len) */ 50 } hash_walk_t; 51 52 /* Computes number of bytes to allocate for hash_walk_t structure. */ 53 #define HW_SIZE(n) (sizeof (modent_step_data_t) + MH_SIZE(n)) 54 55 /* Used for decoding hash keys for display */ 56 typedef struct hash_type_entry_s { 57 const char *hte_type; /* name of hash type for ::modent -t */ 58 const char *hte_comparator; /* name of comparator function */ 59 void (*hte_format)(const mod_hash_key_t, char *, size_t); 60 } hash_type_entry_t; 61 62 static void format_strhash(const mod_hash_key_t, char *, size_t); 63 static void format_ptrhash(const mod_hash_key_t, char *, size_t); 64 static void format_idhash(const mod_hash_key_t, char *, size_t); 65 static void format_default(const mod_hash_key_t, char *, size_t); 66 67 static const hash_type_entry_t hte_table[] = { 68 { "str", "mod_hash_strkey_cmp", format_strhash }, 69 { "ptr", "mod_hash_ptrkey_cmp", format_ptrhash }, 70 { "id", "mod_hash_idkey_cmp", format_idhash }, 71 { NULL, NULL, format_default } 72 }; 73 74 static int modent_print(uintptr_t, int, uint_t, const hash_type_entry_t *, 75 boolean_t, uint_t, uint_t); 76 77 /* The information used during a walk */ 78 typedef struct mod_walk_data_s { 79 const hash_type_entry_t *mwd_hte; /* pointer to entry type */ 80 int mwd_main_flags; /* ::modhash flags */ 81 int mwd_flags; /* DCMD_* flags for looping */ 82 uint_t mwd_opt_e; /* call-modent mode */ 83 uint_t mwd_opt_c; /* chain head only mode */ 84 uint_t mwd_opt_h; /* hash index output */ 85 boolean_t mwd_opt_k_set; /* key supplied */ 86 boolean_t mwd_opt_v_set; /* value supplied */ 87 uintptr_t mwd_opt_k; /* key */ 88 uintptr_t mwd_opt_v; /* value */ 89 int mwd_maxposn; /* len of longest chain - 1 */ 90 int mwd_maxidx; /* hash idx of longest chain */ 91 uintptr_t mwd_maxaddr; /* addr of 1st elem @ maxidx */ 92 uintptr_t mwd_idxtoprint; /* desired hash pos to print */ 93 uintptr_t mwd_addr; /* 1st elem addr @idxtoprint */ 94 } mod_walk_data_t; 95 96 /* 97 * Initialize a walk over all the modhashes in the system. 98 */ 99 int 100 modhash_walk_init(mdb_walk_state_t *wsp) 101 { 102 mod_hash_t *mh_head; 103 104 if (mdb_readvar(&mh_head, "mh_head") == -1) { 105 mdb_warn("failed to read mh_head"); 106 return (WALK_ERR); 107 } 108 wsp->walk_addr = (uintptr_t)mh_head; 109 110 return (WALK_NEXT); 111 } 112 113 /* 114 * Step to the next modhash in the system. 115 */ 116 int 117 modhash_walk_step(mdb_walk_state_t *wsp) 118 { 119 mod_hash_t mh; 120 int status; 121 122 if (wsp->walk_addr == NULL) 123 return (WALK_DONE); 124 125 if (mdb_vread(&mh, sizeof (mh), wsp->walk_addr) == -1) { 126 mdb_warn("failed to read mod_hash_t at %p", wsp->walk_addr); 127 return (WALK_ERR); 128 } 129 130 status = wsp->walk_callback(wsp->walk_addr, &mh, wsp->walk_cbdata); 131 132 wsp->walk_addr = (uintptr_t)mh.mh_next; 133 134 return (status); 135 } 136 137 /* 138 * Initialize a walk over the entries in a given modhash. 139 */ 140 int 141 modent_walk_init(mdb_walk_state_t *wsp) 142 { 143 mod_hash_t mh; 144 hash_walk_t *hwp; 145 int retv; 146 147 if (wsp->walk_addr == NULL) { 148 mdb_warn("mod_hash_t address required\n"); 149 return (WALK_ERR); 150 } 151 152 if (mdb_vread(&mh, sizeof (mh), wsp->walk_addr) == -1) { 153 mdb_warn("failed to read mod_hash_t at %p", wsp->walk_addr); 154 return (WALK_ERR); 155 } 156 157 if (mh.mh_nchains <= 1) { 158 mdb_warn("impossible number of chains in mod_hash_t at %p", 159 wsp->walk_addr); 160 return (WALK_ERR); 161 } 162 163 /* 164 * If the user presents us with a garbage pointer, and thus the number 165 * of chains is just absurd, we don't want to bail out of mdb. Fail to 166 * walk instead. 167 */ 168 hwp = mdb_alloc(HW_SIZE(mh.mh_nchains), UM_NOSLEEP); 169 if (hwp == NULL) { 170 mdb_warn("unable to allocate %#x bytes for mod_hash_t at %p", 171 HW_SIZE(mh.mh_nchains), wsp->walk_addr); 172 return (WALK_ERR); 173 } 174 175 (void) memcpy(&hwp->hwalk_hash, &mh, sizeof (hwp->hwalk_hash)); 176 177 retv = mdb_vread(hwp->hwalk_hash.mh_entries + 1, 178 (mh.mh_nchains - 1) * sizeof (struct mod_hash_entry *), 179 wsp->walk_addr + sizeof (mh)); 180 181 if (retv == -1) { 182 mdb_free(hwp, HW_SIZE(mh.mh_nchains)); 183 mdb_warn("failed to read %#x mod_hash_entry pointers at %p", 184 mh.mh_nchains - 1, wsp->walk_addr + sizeof (mh)); 185 return (WALK_ERR); 186 } 187 188 hwp->hwalk_msd.msd_hash_index = -1; 189 hwp->hwalk_msd.msd_position = 0; 190 hwp->hwalk_msd.msd_first_addr = NULL; 191 192 wsp->walk_addr = NULL; 193 wsp->walk_data = hwp; 194 195 return (WALK_NEXT); 196 } 197 198 /* 199 * Step to the next entry in the modhash. 200 */ 201 int 202 modent_walk_step(mdb_walk_state_t *wsp) 203 { 204 hash_walk_t *hwp = wsp->walk_data; 205 int status; 206 207 while (wsp->walk_addr == NULL) { 208 hwp->hwalk_msd.msd_position = 0; 209 if (++hwp->hwalk_msd.msd_hash_index >= 210 hwp->hwalk_hash.mh_nchains) 211 return (WALK_DONE); 212 wsp->walk_addr = hwp->hwalk_msd.msd_first_addr = 213 (uintptr_t)hwp->hwalk_hash.mh_entries[ 214 hwp->hwalk_msd.msd_hash_index]; 215 } 216 217 if (mdb_vread(&hwp->hwalk_msd.msd_mhe, sizeof (hwp->hwalk_msd.msd_mhe), 218 wsp->walk_addr) == -1) { 219 mdb_warn("failed to read mod_hash_entry at %p", 220 wsp->walk_addr); 221 return (WALK_ERR); 222 } 223 224 status = wsp->walk_callback(wsp->walk_addr, &hwp->hwalk_msd, 225 wsp->walk_cbdata); 226 227 hwp->hwalk_msd.msd_position++; 228 wsp->walk_addr = (uintptr_t)hwp->hwalk_msd.msd_mhe.mhe_next; 229 230 return (status); 231 } 232 233 /* 234 * Clean up after walking the entries in a modhash. 235 */ 236 void 237 modent_walk_fini(mdb_walk_state_t *wsp) 238 { 239 hash_walk_t *hwp = wsp->walk_data; 240 241 mdb_free(hwp, HW_SIZE(hwp->hwalk_hash.mh_nchains)); 242 wsp->walk_data = NULL; 243 } 244 245 /* 246 * Step to next entry on a hash chain. 247 */ 248 int 249 modchain_walk_step(mdb_walk_state_t *wsp) 250 { 251 struct mod_hash_entry mhe; 252 int status; 253 254 if (wsp->walk_addr == NULL) 255 return (WALK_DONE); 256 257 if (mdb_vread(&mhe, sizeof (mhe), wsp->walk_addr) == -1) { 258 mdb_warn("failed to read mod_hash_entry at %p", 259 wsp->walk_addr); 260 return (WALK_ERR); 261 } 262 263 status = wsp->walk_callback(wsp->walk_addr, &mhe, wsp->walk_cbdata); 264 265 wsp->walk_addr = (uintptr_t)mhe.mhe_next; 266 267 return (status); 268 } 269 270 /* 271 * This is called by ::modhash (via a callback) when gathering data about the 272 * entries in a given modhash. It keeps track of the longest chain, finds a 273 * specific entry (if the user requested one) and prints out a summary of the 274 * entry or entries. 275 */ 276 static int 277 modent_format(uintptr_t addr, const void *data, void *private) 278 { 279 const modent_step_data_t *msd = data; 280 mod_walk_data_t *mwd = private; 281 int retv = DCMD_OK; 282 283 /* If this chain is longest seen, then save start of chain */ 284 if (msd->msd_position > mwd->mwd_maxposn) { 285 mwd->mwd_maxposn = msd->msd_position; 286 mwd->mwd_maxidx = msd->msd_hash_index; 287 mwd->mwd_maxaddr = msd->msd_first_addr; 288 } 289 290 /* If the user specified a particular chain, then ignore others */ 291 if (mwd->mwd_idxtoprint != (uintptr_t)-1) { 292 /* Save address of *first* entry */ 293 if (mwd->mwd_idxtoprint == msd->msd_hash_index) 294 mwd->mwd_addr = msd->msd_first_addr; 295 else 296 return (retv); 297 } 298 299 /* If the user specified a particular key, ignore others. */ 300 if (mwd->mwd_opt_k_set && 301 (uintptr_t)msd->msd_mhe.mhe_key != mwd->mwd_opt_k) 302 return (retv); 303 304 /* If the user specified a particular value, ignore others. */ 305 if (mwd->mwd_opt_v_set && 306 (uintptr_t)msd->msd_mhe.mhe_val != mwd->mwd_opt_v) 307 return (retv); 308 309 /* If the user just wants the chain heads, skip intermediate nodes. */ 310 if (mwd->mwd_opt_c && msd->msd_position != 0) 311 return (retv); 312 313 /* If the user asked to have the entries printed, then do that. */ 314 if (mwd->mwd_opt_e) { 315 /* If the output is to a pipeline, just print addresses */ 316 if (mwd->mwd_main_flags & DCMD_PIPE_OUT) 317 mdb_printf("%p\n", addr); 318 else 319 retv = modent_print(addr, msd->msd_hash_index, 320 mwd->mwd_flags, mwd->mwd_hte, mwd->mwd_opt_h, 0, 0); 321 mwd->mwd_flags &= ~DCMD_LOOPFIRST; 322 } 323 return (retv); 324 } 325 326 void 327 modhash_help(void) 328 { 329 mdb_printf("Prints information about one or all mod_hash_t databases " 330 "in the system.\n" 331 "This command has three basic forms, summarized below.\n\n" 332 " ::modhash [-t]\n <addr>::modhash\n" 333 " <addr>::modhash -e [-ch] [-k key] [-v val] [-i index]\n\n" 334 "In the first form, no address is provided, and a summary of all " 335 "registered\n" 336 "hashes in the system is printed; adding the '-t' option shows" 337 " the hash\n" 338 "type instead of the limits. In the second form, the address of a" 339 " mod_hash_t\n" 340 "is provided, and the output is in a verbose format. The final " 341 "form prints\n" 342 "the elements of the hash, optionally selecting just those with a " 343 "particular\n" 344 "key, value, and/or hash index, or just the chain heads (-c). " 345 "The -h option\n" 346 "shows hash indices instead of addresses.\n"); 347 } 348 349 int 350 modhash(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 351 { 352 mod_hash_t mh; 353 char name[256]; 354 int len; 355 mod_walk_data_t mwd; 356 uint_t opt_s = FALSE; 357 uint_t opt_t = FALSE; 358 char kfunc[MDB_SYM_NAMLEN]; 359 const hash_type_entry_t *htep; 360 boolean_t elem_flags; 361 362 (void) memset(&mwd, 0, sizeof (mwd)); 363 mwd.mwd_main_flags = flags; 364 mwd.mwd_flags = DCMD_ADDRSPEC | DCMD_LOOP | DCMD_LOOPFIRST; 365 mwd.mwd_maxposn = -1; 366 mwd.mwd_idxtoprint = (uintptr_t)-1; 367 368 len = mdb_getopts(argc, argv, 369 's', MDB_OPT_SETBITS, TRUE, &opt_s, 370 't', MDB_OPT_SETBITS, TRUE, &opt_t, 371 'c', MDB_OPT_SETBITS, TRUE, &mwd.mwd_opt_c, 372 'e', MDB_OPT_SETBITS, TRUE, &mwd.mwd_opt_e, 373 'h', MDB_OPT_SETBITS, TRUE, &mwd.mwd_opt_h, 374 'i', MDB_OPT_UINTPTR, &mwd.mwd_idxtoprint, 375 'k', MDB_OPT_UINTPTR_SET, &mwd.mwd_opt_k_set, &mwd.mwd_opt_k, 376 'v', MDB_OPT_UINTPTR_SET, &mwd.mwd_opt_v_set, &mwd.mwd_opt_v, 377 NULL); 378 379 if (len < argc) { 380 argv += len; 381 if (argv->a_type == MDB_TYPE_STRING) 382 mdb_warn("unexpected argument: %s\n", 383 argv->a_un.a_str); 384 else 385 mdb_warn("unexpected argument(s)\n"); 386 return (DCMD_USAGE); 387 } 388 389 /* true if any element-related flags are set */ 390 elem_flags = mwd.mwd_opt_c || mwd.mwd_opt_e || mwd.mwd_opt_h || 391 mwd.mwd_opt_k_set || mwd.mwd_opt_v_set || 392 mwd.mwd_idxtoprint != (uintptr_t)-1; 393 394 if (!(flags & DCMD_ADDRSPEC)) { 395 mdb_arg_t new_argv[1]; 396 397 if (elem_flags) { 398 /* 399 * This isn't allowed so that the output doesn't become 400 * a confusing mix of hash table descriptions and 401 * element entries. 402 */ 403 mdb_warn("printing elements from all hashes is not " 404 "permitted\n"); 405 return (DCMD_USAGE); 406 } 407 /* we force short mode here, no matter what it says */ 408 new_argv[0].a_type = MDB_TYPE_STRING; 409 new_argv[0].a_un.a_str = opt_t ? "-st" : "-s"; 410 if (mdb_walk_dcmd("modhash", "modhash", 1, new_argv) == -1) { 411 mdb_warn("can't walk mod_hash structures"); 412 return (DCMD_ERR); 413 } 414 return (DCMD_OK); 415 } 416 417 if (mwd.mwd_opt_e) { 418 if (opt_s | opt_t) { 419 mdb_warn("hash summary options not permitted when " 420 "displaying elements\n"); 421 return (DCMD_USAGE); 422 } 423 } else { 424 if (elem_flags) { 425 /* 426 * This isn't allowed so that the output doesn't become 427 * a confusing mix of hash table description and 428 * element entries. 429 */ 430 mdb_warn("printing elements requires -e\n"); 431 return (DCMD_USAGE); 432 } 433 } 434 435 if (mdb_vread(&mh, sizeof (mh), addr) == -1) { 436 mdb_warn("failed to read mod_hash_t at %p", addr); 437 return (DCMD_ERR); 438 } 439 440 if (mwd.mwd_idxtoprint != (uintptr_t)-1 && 441 mwd.mwd_idxtoprint >= mh.mh_nchains) { 442 mdb_warn("mod_hash chain index %x out of range 0..%x\n", 443 mwd.mwd_idxtoprint, mh.mh_nchains - 1); 444 return (DCMD_ERR); 445 } 446 447 if (DCMD_HDRSPEC(flags) && opt_s) { 448 if (opt_t != 0) { 449 mdb_printf("%<u>%?s %6s %5s %?s %s%</u>\n", 450 "ADDR", "CHAINS", "ELEMS", "TYPE", "NAME"); 451 } else { 452 mdb_printf("%<u>%?s %6s %5s %6s %6s %s%</u>\n", 453 "ADDR", "CHAINS", "ELEMS", "MAXLEN", "MAXIDX", 454 "NAME"); 455 } 456 } 457 458 len = mdb_readstr(name, sizeof (name), (uintptr_t)mh.mh_name); 459 if (len < 0) 460 (void) strcpy(name, "??"); 461 462 if (mdb_lookup_by_addr((uintptr_t)mh.mh_keycmp, MDB_SYM_EXACT, kfunc, 463 sizeof (kfunc), NULL) == -1) 464 kfunc[0] = '\0'; 465 for (htep = hte_table; htep->hte_type != NULL; htep++) 466 if (strcmp(kfunc, htep->hte_comparator) == 0) 467 break; 468 mwd.mwd_hte = htep; 469 470 if (!mwd.mwd_opt_e && !opt_s) { 471 mdb_printf("mod_hash_t %?p %s%s:\n", addr, name, 472 len == sizeof (name) ? "..." : ""); 473 mdb_printf("\tKey comparator: %?p %s\n", 474 mh.mh_keycmp, kfunc); 475 mdb_printf("\tType: %s\n", 476 htep->hte_type == NULL ? "unknown" : htep->hte_type); 477 mdb_printf("\tSleep flag = %s, alloc failed = %#x\n", 478 mh.mh_sleep ? "true" : "false", 479 mh.mh_stat.mhs_nomem); 480 mdb_printf("\tNumber of chains = %#x, elements = %#x\n", 481 mh.mh_nchains, mh.mh_stat.mhs_nelems); 482 mdb_printf("\tHits = %#x, misses = %#x, dups = %#x\n", 483 mh.mh_stat.mhs_hit, mh.mh_stat.mhs_miss, 484 mh.mh_stat.mhs_coll); 485 } 486 if (mdb_pwalk("modent", modent_format, &mwd, addr) == -1) { 487 mdb_warn("can't walk mod_hash entries"); 488 return (DCMD_ERR); 489 } 490 if (opt_s) { 491 const char *tname; 492 char tbuf[64]; 493 494 if (htep->hte_type == NULL) { 495 (void) mdb_snprintf(tbuf, sizeof (tbuf), "%p", 496 mh.mh_keycmp); 497 tname = tbuf; 498 } else { 499 tname = htep->hte_type; 500 } 501 mdb_printf("%?p %6x %5x ", addr, mh.mh_nchains, 502 mh.mh_stat.mhs_nelems); 503 if (opt_t != 0) { 504 mdb_printf("%?s", tname); 505 } else { 506 mdb_printf("%6x %6x", mwd.mwd_maxposn + 1, 507 mwd.mwd_maxidx); 508 } 509 mdb_printf(" %s%s\n", name, len == sizeof (name) ? "..." : ""); 510 } else if (!mwd.mwd_opt_e) { 511 mdb_printf("\tMaximum chain length = %x (at index %x, first " 512 "entry %p)\n", mwd.mwd_maxposn + 1, mwd.mwd_maxidx, 513 mwd.mwd_maxaddr); 514 } 515 return (DCMD_OK); 516 } 517 518 static void 519 format_strhash(const mod_hash_key_t key, char *keystr, size_t keystrlen) 520 { 521 int len; 522 523 (void) mdb_snprintf(keystr, keystrlen, "%?p ", key); 524 len = strlen(keystr); 525 (void) mdb_readstr(keystr + len, keystrlen - len, (uintptr_t)key); 526 } 527 528 static void 529 format_ptrhash(const mod_hash_key_t key, char *keystr, size_t keystrlen) 530 { 531 int len; 532 533 (void) mdb_snprintf(keystr, keystrlen, "%?p ", key); 534 len = strlen(keystr); 535 (void) mdb_lookup_by_addr((uintptr_t)key, MDB_SYM_EXACT, keystr + len, 536 keystrlen - len, NULL); 537 } 538 539 static void 540 format_idhash(const mod_hash_key_t key, char *keystr, size_t keystrlen) 541 { 542 (void) mdb_snprintf(keystr, keystrlen, "%?x", (uint_t)(uintptr_t)key); 543 } 544 545 static void 546 format_default(const mod_hash_key_t key, char *keystr, size_t keystrlen) 547 { 548 (void) mdb_snprintf(keystr, keystrlen, "%?p", key); 549 } 550 551 void 552 modent_help(void) 553 { 554 mdb_printf("Options are mutually exclusive:\n" 555 " -t <type> print key in symbolic form; <type> is one of str, " 556 "ptr, or id\n" 557 " -v print value pointer alone\n" 558 " -k print key pointer alone\n"); 559 } 560 561 static int 562 modent_print(uintptr_t addr, int hidx, uint_t flags, 563 const hash_type_entry_t *htep, boolean_t prtidx, uint_t opt_k, 564 uint_t opt_v) 565 { 566 char keystr[256]; 567 struct mod_hash_entry mhe; 568 569 if (DCMD_HDRSPEC(flags) && opt_k == 0 && opt_v == 0) { 570 mdb_printf("%<u>%?s %?s %?s%</u>\n", 571 prtidx ? "HASH_IDX" : "ADDR", "VAL", "KEY"); 572 } 573 574 if (mdb_vread(&mhe, sizeof (mhe), addr) == -1) { 575 mdb_warn("failed to read mod_hash_entry at %p", addr); 576 return (DCMD_ERR); 577 } 578 579 if (opt_k) { 580 mdb_printf("%p\n", mhe.mhe_key); 581 } else if (opt_v) { 582 mdb_printf("%p\n", mhe.mhe_val); 583 } else { 584 htep->hte_format(mhe.mhe_key, keystr, sizeof (keystr)); 585 if (prtidx) 586 mdb_printf("%?x", hidx); 587 else 588 mdb_printf("%?p", addr); 589 mdb_printf(" %?p %s\n", mhe.mhe_val, keystr); 590 } 591 592 return (DCMD_OK); 593 } 594 595 /* 596 * This prints out a single mod_hash element, showing its value and its key. 597 * The key is decoded based on the type of hash keys in use. 598 */ 599 int 600 modent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 601 { 602 const char *opt_t = NULL; 603 const hash_type_entry_t *htep; 604 int len; 605 uint_t opt_k = 0; 606 uint_t opt_v = 0; 607 608 if (!(flags & DCMD_ADDRSPEC)) { 609 mdb_warn("address of mod_hash_entry must be specified\n"); 610 return (DCMD_ERR); 611 } 612 613 len = mdb_getopts(argc, argv, 614 't', MDB_OPT_STR, &opt_t, 615 'k', MDB_OPT_SETBITS, 1, &opt_k, 616 'v', MDB_OPT_SETBITS, 1, &opt_v, 617 NULL); 618 619 /* options are mutually exclusive */ 620 if ((opt_k && opt_v) || (opt_t != NULL && (opt_k || opt_v)) || 621 len < argc) { 622 return (DCMD_USAGE); 623 } 624 625 for (htep = hte_table; htep->hte_type != NULL; htep++) 626 if (opt_t != NULL && strcmp(opt_t, htep->hte_type) == 0) 627 break; 628 629 if (opt_t != NULL && htep->hte_type == NULL) { 630 mdb_warn("unknown hash type %s\n", opt_t); 631 return (DCMD_ERR); 632 } 633 634 return (modent_print(addr, 0, flags, htep, FALSE, opt_k, opt_v)); 635 } 636