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