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 #include <string.h> 29 #include <sys/modhash_impl.h> 30 31 #include <mdb/mdb_modapi.h> 32 #include <mdb/mdb_ks.h> 33 34 #include "modhash.h" 35 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 */ 39 int msd_hash_index; 40 int msd_position; /* entry position in chain */ 41 uintptr_t msd_first_addr; /* first address in chain */ 42 } modent_step_data_t; 43 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) */ 48 } hash_walk_t; 49 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)) 52 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); 58 } hash_type_entry_t; 59 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); 64 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 } 70 }; 71 72 static int modent_print(uintptr_t, int, uint_t, const hash_type_entry_t *, 73 boolean_t, uint_t, uint_t); 74 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 */ 92 } mod_walk_data_t; 93 94 /* 95 * Initialize a walk over all the modhashes in the system. 96 */ 97 int 98 modhash_walk_init(mdb_walk_state_t *wsp) 99 { 100 mod_hash_t *mh_head; 101 102 if (mdb_readvar(&mh_head, "mh_head") == -1) { 103 mdb_warn("failed to read mh_head"); 104 return (WALK_ERR); 105 } 106 wsp->walk_addr = (uintptr_t)mh_head; 107 108 return (WALK_NEXT); 109 } 110 111 /* 112 * Step to the next modhash in the system. 113 */ 114 int 115 modhash_walk_step(mdb_walk_state_t *wsp) 116 { 117 mod_hash_t mh; 118 int status; 119 120 if (wsp->walk_addr == 0) 121 return (WALK_DONE); 122 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); 125 return (WALK_ERR); 126 } 127 128 status = wsp->walk_callback(wsp->walk_addr, &mh, wsp->walk_cbdata); 129 130 wsp->walk_addr = (uintptr_t)mh.mh_next; 131 132 return (status); 133 } 134 135 /* 136 * Initialize a walk over the entries in a given modhash. 137 */ 138 int 139 modent_walk_init(mdb_walk_state_t *wsp) 140 { 141 mod_hash_t mh; 142 hash_walk_t *hwp; 143 int retv; 144 145 if (wsp->walk_addr == 0) { 146 mdb_warn("mod_hash_t address required\n"); 147 return (WALK_ERR); 148 } 149 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); 152 return (WALK_ERR); 153 } 154 155 if (mh.mh_nchains <= 1) { 156 mdb_warn("impossible number of chains in mod_hash_t at %p", 157 wsp->walk_addr); 158 return (WALK_ERR); 159 } 160 161 /* 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 164 * walk instead. 165 */ 166 hwp = mdb_alloc(HW_SIZE(mh.mh_nchains), UM_NOSLEEP); 167 if (hwp == NULL) { 168 mdb_warn("unable to allocate %#x bytes for mod_hash_t at %p", 169 HW_SIZE(mh.mh_nchains), wsp->walk_addr); 170 return (WALK_ERR); 171 } 172 173 (void) memcpy(&hwp->hwalk_hash, &mh, sizeof (hwp->hwalk_hash)); 174 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)); 178 179 if (retv == -1) { 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)); 183 return (WALK_ERR); 184 } 185 186 hwp->hwalk_msd.msd_hash_index = -1; 187 hwp->hwalk_msd.msd_position = 0; 188 hwp->hwalk_msd.msd_first_addr = 0; 189 190 wsp->walk_addr = 0; 191 wsp->walk_data = hwp; 192 193 return (WALK_NEXT); 194 } 195 196 /* 197 * Step to the next entry in the modhash. 198 */ 199 int 200 modent_walk_step(mdb_walk_state_t *wsp) 201 { 202 hash_walk_t *hwp = wsp->walk_data; 203 int status; 204 205 while (wsp->walk_addr == 0) { 206 hwp->hwalk_msd.msd_position = 0; 207 if (++hwp->hwalk_msd.msd_hash_index >= 208 hwp->hwalk_hash.mh_nchains) 209 return (WALK_DONE); 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]; 213 } 214 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", 218 wsp->walk_addr); 219 return (WALK_ERR); 220 } 221 222 status = wsp->walk_callback(wsp->walk_addr, &hwp->hwalk_msd, 223 wsp->walk_cbdata); 224 225 hwp->hwalk_msd.msd_position++; 226 wsp->walk_addr = (uintptr_t)hwp->hwalk_msd.msd_mhe.mhe_next; 227 228 return (status); 229 } 230 231 /* 232 * Clean up after walking the entries in a modhash. 233 */ 234 void 235 modent_walk_fini(mdb_walk_state_t *wsp) 236 { 237 hash_walk_t *hwp = wsp->walk_data; 238 239 mdb_free(hwp, HW_SIZE(hwp->hwalk_hash.mh_nchains)); 240 wsp->walk_data = NULL; 241 } 242 243 /* 244 * Step to next entry on a hash chain. 245 */ 246 int 247 modchain_walk_step(mdb_walk_state_t *wsp) 248 { 249 struct mod_hash_entry mhe; 250 int status; 251 252 if (wsp->walk_addr == 0) 253 return (WALK_DONE); 254 255 if (mdb_vread(&mhe, sizeof (mhe), wsp->walk_addr) == -1) { 256 mdb_warn("failed to read mod_hash_entry at %p", 257 wsp->walk_addr); 258 return (WALK_ERR); 259 } 260 261 status = wsp->walk_callback(wsp->walk_addr, &mhe, wsp->walk_cbdata); 262 263 wsp->walk_addr = (uintptr_t)mhe.mhe_next; 264 265 return (status); 266 } 267 268 /* 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 272 * entry or entries. 273 */ 274 static int 275 modent_format(uintptr_t addr, const void *data, void *private) 276 { 277 const modent_step_data_t *msd = data; 278 mod_walk_data_t *mwd = private; 279 int retv = DCMD_OK; 280 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; 286 } 287 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; 293 else 294 return (retv); 295 } 296 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) 300 return (retv); 301 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) 305 return (retv); 306 307 /* If the user just wants the chain heads, skip intermediate nodes. */ 308 if (mwd->mwd_opt_c && msd->msd_position != 0) 309 return (retv); 310 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); 316 else 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; 320 } 321 return (retv); 322 } 323 324 void 325 modhash_help(void) 326 { 327 mdb_printf("Prints information about one or all mod_hash_t databases " 328 "in the system.\n" 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 " 333 "registered\n" 334 "hashes in the system is printed; adding the '-t' option shows" 335 " the hash\n" 336 "type instead of the limits. In the second form, the address of a" 337 " mod_hash_t\n" 338 "is provided, and the output is in a verbose format. The final " 339 "form prints\n" 340 "the elements of the hash, optionally selecting just those with a " 341 "particular\n" 342 "key, value, and/or hash index, or just the chain heads (-c). " 343 "The -h option\n" 344 "shows hash indices instead of addresses.\n"); 345 } 346 347 int 348 modhash(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 349 { 350 mod_hash_t mh; 351 char name[256]; 352 int len; 353 mod_walk_data_t mwd; 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; 359 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; 365 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, 375 NULL); 376 377 if (len < argc) { 378 argv += len; 379 if (argv->a_type == MDB_TYPE_STRING) 380 mdb_warn("unexpected argument: %s\n", 381 argv->a_un.a_str); 382 else 383 mdb_warn("unexpected argument(s)\n"); 384 return (DCMD_USAGE); 385 } 386 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; 391 392 if (!(flags & DCMD_ADDRSPEC)) { 393 mdb_arg_t new_argv[1]; 394 395 if (elem_flags) { 396 /* 397 * This isn't allowed so that the output doesn't become 398 * a confusing mix of hash table descriptions and 399 * element entries. 400 */ 401 mdb_warn("printing elements from all hashes is not " 402 "permitted\n"); 403 return (DCMD_USAGE); 404 } 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"); 410 return (DCMD_ERR); 411 } 412 return (DCMD_OK); 413 } 414 415 if (mwd.mwd_opt_e) { 416 if (opt_s | opt_t) { 417 mdb_warn("hash summary options not permitted when " 418 "displaying elements\n"); 419 return (DCMD_USAGE); 420 } 421 } else { 422 if (elem_flags) { 423 /* 424 * This isn't allowed so that the output doesn't become 425 * a confusing mix of hash table description and 426 * element entries. 427 */ 428 mdb_warn("printing elements requires -e\n"); 429 return (DCMD_USAGE); 430 } 431 } 432 433 if (mdb_vread(&mh, sizeof (mh), addr) == -1) { 434 mdb_warn("failed to read mod_hash_t at %p", addr); 435 return (DCMD_ERR); 436 } 437 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); 442 return (DCMD_ERR); 443 } 444 445 if (DCMD_HDRSPEC(flags) && opt_s) { 446 if (opt_t != 0) { 447 mdb_printf("%<u>%?s %6s %5s %?s %s%</u>\n", 448 "ADDR", "CHAINS", "ELEMS", "TYPE", "NAME"); 449 } else { 450 mdb_printf("%<u>%?s %6s %5s %6s %6s %s%</u>\n", 451 "ADDR", "CHAINS", "ELEMS", "MAXLEN", "MAXIDX", 452 "NAME"); 453 } 454 } 455 456 len = mdb_readstr(name, sizeof (name), (uintptr_t)mh.mh_name); 457 if (len < 0) 458 (void) strcpy(name, "??"); 459 460 if (mdb_lookup_by_addr((uintptr_t)mh.mh_keycmp, MDB_SYM_EXACT, kfunc, 461 sizeof (kfunc), NULL) == -1) 462 kfunc[0] = '\0'; 463 for (htep = hte_table; htep->hte_type != NULL; htep++) 464 if (strcmp(kfunc, htep->hte_comparator) == 0) 465 break; 466 mwd.mwd_hte = htep; 467 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); 483 } 484 if (mdb_pwalk("modent", modent_format, &mwd, addr) == -1) { 485 mdb_warn("can't walk mod_hash entries"); 486 return (DCMD_ERR); 487 } 488 if (opt_s) { 489 const char *tname; 490 char tbuf[64]; 491 492 if (htep->hte_type == NULL) { 493 (void) mdb_snprintf(tbuf, sizeof (tbuf), "%p", 494 mh.mh_keycmp); 495 tname = tbuf; 496 } else { 497 tname = htep->hte_type; 498 } 499 mdb_printf("%?p %6x %5x ", addr, mh.mh_nchains, 500 mh.mh_stat.mhs_nelems); 501 if (opt_t != 0) { 502 mdb_printf("%?s", tname); 503 } else { 504 mdb_printf("%6x %6x", mwd.mwd_maxposn + 1, 505 mwd.mwd_maxidx); 506 } 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, 511 mwd.mwd_maxaddr); 512 } 513 return (DCMD_OK); 514 } 515 516 static void 517 format_strhash(const mod_hash_key_t key, char *keystr, size_t keystrlen) 518 { 519 int len; 520 521 (void) mdb_snprintf(keystr, keystrlen, "%?p ", key); 522 len = strlen(keystr); 523 (void) mdb_readstr(keystr + len, keystrlen - len, (uintptr_t)key); 524 } 525 526 static void 527 format_ptrhash(const mod_hash_key_t key, char *keystr, size_t keystrlen) 528 { 529 int len; 530 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); 535 } 536 537 static void 538 format_idhash(const mod_hash_key_t key, char *keystr, size_t keystrlen) 539 { 540 (void) mdb_snprintf(keystr, keystrlen, "%?x", (uint_t)(uintptr_t)key); 541 } 542 543 static void 544 format_default(const mod_hash_key_t key, char *keystr, size_t keystrlen) 545 { 546 (void) mdb_snprintf(keystr, keystrlen, "%?p", key); 547 } 548 549 void 550 modent_help(void) 551 { 552 mdb_printf("Options are mutually exclusive:\n" 553 " -t <type> print key in symbolic form; <type> is one of str, " 554 "ptr, or id\n" 555 " -v print value pointer alone\n" 556 " -k print key pointer alone\n"); 557 } 558 559 static int 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, 562 uint_t opt_v) 563 { 564 char keystr[256]; 565 struct mod_hash_entry mhe; 566 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"); 570 } 571 572 if (mdb_vread(&mhe, sizeof (mhe), addr) == -1) { 573 mdb_warn("failed to read mod_hash_entry at %p", addr); 574 return (DCMD_ERR); 575 } 576 577 if (opt_k) { 578 mdb_printf("%p\n", mhe.mhe_key); 579 } else if (opt_v) { 580 mdb_printf("%p\n", mhe.mhe_val); 581 } else { 582 htep->hte_format(mhe.mhe_key, keystr, sizeof (keystr)); 583 if (prtidx) 584 mdb_printf("%?x", hidx); 585 else 586 mdb_printf("%?p", addr); 587 mdb_printf(" %?p %s\n", mhe.mhe_val, keystr); 588 } 589 590 return (DCMD_OK); 591 } 592 593 /* 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. 596 */ 597 int 598 modent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 599 { 600 const char *opt_t = NULL; 601 const hash_type_entry_t *htep; 602 int len; 603 uint_t opt_k = 0; 604 uint_t opt_v = 0; 605 606 if (!(flags & DCMD_ADDRSPEC)) { 607 mdb_warn("address of mod_hash_entry must be specified\n"); 608 return (DCMD_ERR); 609 } 610 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, 615 NULL); 616 617 /* options are mutually exclusive */ 618 if ((opt_k && opt_v) || (opt_t != NULL && (opt_k || opt_v)) || 619 len < argc) { 620 return (DCMD_USAGE); 621 } 622 623 for (htep = hte_table; htep->hte_type != NULL; htep++) 624 if (opt_t != NULL && strcmp(opt_t, htep->hte_type) == 0) 625 break; 626 627 if (opt_t != NULL && htep->hte_type == NULL) { 628 mdb_warn("unknown hash type %s\n", opt_t); 629 return (DCMD_ERR); 630 } 631 632 return (modent_print(addr, 0, flags, htep, FALSE, opt_k, opt_v)); 633 } 634