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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <mdb/mdb_ctf.h> 29 #include <sys/zfs_context.h> 30 #include <sys/mdb_modapi.h> 31 #include <sys/dbuf.h> 32 #include <sys/dmu_objset.h> 33 #include <sys/dsl_dir.h> 34 #include <sys/dsl_pool.h> 35 #include <sys/metaslab_impl.h> 36 #include <sys/space_map.h> 37 #include <sys/list.h> 38 #include <sys/spa_impl.h> 39 #include <sys/vdev_impl.h> 40 #include <sys/zio_compress.h> 41 42 #ifndef _KERNEL 43 #include "../genunix/list.h" 44 #endif 45 46 #ifdef _KERNEL 47 #define ZFS_OBJ_NAME "zfs" 48 #else 49 #define ZFS_OBJ_NAME "libzpool.so.1" 50 #endif 51 52 static char * 53 local_strdup(const char *s) 54 { 55 char *s1 = mdb_alloc(strlen(s) + 1, UM_SLEEP); 56 57 (void) strcpy(s1, s); 58 return (s1); 59 } 60 61 static int 62 getmember(uintptr_t addr, const char *type, mdb_ctf_id_t *idp, 63 const char *member, int len, void *buf) 64 { 65 mdb_ctf_id_t id; 66 ulong_t off; 67 char name[64]; 68 69 if (idp == NULL) { 70 if (mdb_ctf_lookup_by_name(type, &id) == -1) { 71 mdb_warn("couldn't find type %s", type); 72 return (DCMD_ERR); 73 } 74 idp = &id; 75 } else { 76 type = name; 77 mdb_ctf_type_name(*idp, name, sizeof (name)); 78 } 79 80 if (mdb_ctf_offsetof(*idp, member, &off) == -1) { 81 mdb_warn("couldn't find member %s of type %s\n", member, type); 82 return (DCMD_ERR); 83 } 84 if (off % 8 != 0) { 85 mdb_warn("member %s of type %s is unsupported bitfield", 86 member, type); 87 return (DCMD_ERR); 88 } 89 off /= 8; 90 91 if (mdb_vread(buf, len, addr + off) == -1) { 92 mdb_warn("failed to read %s from %s at %p", 93 member, type, addr + off); 94 return (DCMD_ERR); 95 } 96 /* mdb_warn("read %s from %s at %p+%llx\n", member, type, addr, off); */ 97 98 return (0); 99 } 100 101 #define GETMEMB(addr, type, member, dest) \ 102 getmember(addr, #type, NULL, #member, sizeof (dest), &(dest)) 103 104 #define GETMEMBID(addr, ctfid, member, dest) \ 105 getmember(addr, NULL, ctfid, #member, sizeof (dest), &(dest)) 106 107 static int 108 getrefcount(uintptr_t addr, mdb_ctf_id_t *id, 109 const char *member, uint64_t *rc) 110 { 111 static int gotid; 112 static mdb_ctf_id_t rc_id; 113 ulong_t off; 114 115 if (!gotid) { 116 if (mdb_ctf_lookup_by_name("struct refcount", &rc_id) == -1) { 117 mdb_warn("couldn't find struct refcount"); 118 return (DCMD_ERR); 119 } 120 gotid = TRUE; 121 } 122 123 if (mdb_ctf_offsetof(*id, member, &off) == -1) { 124 char name[64]; 125 mdb_ctf_type_name(*id, name, sizeof (name)); 126 mdb_warn("couldn't find member %s of type %s\n", member, name); 127 return (DCMD_ERR); 128 } 129 off /= 8; 130 131 return (GETMEMBID(addr + off, &rc_id, rc_count, *rc)); 132 } 133 134 static int 135 read_symbol(char *sym_name, void **bufp) 136 { 137 GElf_Sym sym; 138 139 if (mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, sym_name, &sym)) { 140 mdb_warn("can't find symbol %s", sym_name); 141 return (DCMD_ERR); 142 } 143 144 *bufp = mdb_alloc(sym.st_size, UM_SLEEP); 145 146 if (mdb_vread(*bufp, sym.st_size, sym.st_value) == -1) { 147 mdb_warn("can't read data for symbol %s", sym_name); 148 mdb_free(*bufp, sym.st_size); 149 return (DCMD_ERR); 150 } 151 152 return (DCMD_OK); 153 } 154 155 static int verbose; 156 157 static int 158 freelist_walk_init(mdb_walk_state_t *wsp) 159 { 160 if (wsp->walk_addr == NULL) { 161 mdb_warn("must supply starting address\n"); 162 return (WALK_ERR); 163 } 164 165 wsp->walk_data = 0; /* Index into the freelist */ 166 return (WALK_NEXT); 167 } 168 169 static int 170 freelist_walk_step(mdb_walk_state_t *wsp) 171 { 172 uint64_t entry; 173 uintptr_t number = (uintptr_t)wsp->walk_data; 174 char *ddata[] = { "ALLOC", "FREE", "CONDENSE", "INVALID" }; 175 int mapshift = SPA_MINBLOCKSHIFT; 176 177 if (mdb_vread(&entry, sizeof (entry), wsp->walk_addr) == -1) { 178 mdb_warn("failed to read freelist entry %p", wsp->walk_addr); 179 return (WALK_DONE); 180 } 181 wsp->walk_addr += sizeof (entry); 182 wsp->walk_data = (void *)(number + 1); 183 184 if (SM_DEBUG_DECODE(entry)) { 185 mdb_printf("DEBUG: %3u %10s: txg=%llu pass=%llu\n", 186 number, 187 ddata[SM_DEBUG_ACTION_DECODE(entry)], 188 SM_DEBUG_TXG_DECODE(entry), 189 SM_DEBUG_SYNCPASS_DECODE(entry)); 190 } else { 191 mdb_printf("Entry: %3u offsets=%08llx-%08llx type=%c " 192 "size=%06llx", number, 193 SM_OFFSET_DECODE(entry) << mapshift, 194 (SM_OFFSET_DECODE(entry) + SM_RUN_DECODE(entry)) << 195 mapshift, 196 SM_TYPE_DECODE(entry) == SM_ALLOC ? 'A' : 'F', 197 SM_RUN_DECODE(entry) << mapshift); 198 if (verbose) 199 mdb_printf(" (raw=%012llx)\n", entry); 200 mdb_printf("\n"); 201 } 202 return (WALK_NEXT); 203 } 204 205 /* ARGSUSED */ 206 static void 207 freelist_walk_fini(mdb_walk_state_t *wsp) 208 { 209 } 210 211 typedef struct dbuf_walk_data { 212 dbuf_hash_table_t ht; 213 int64_t bucket; 214 uintptr_t dbp; 215 dmu_buf_impl_t db; 216 } dbuf_walk_data_t; 217 218 static int 219 dbuf_walk_init(mdb_walk_state_t *wsp) 220 { 221 dbuf_walk_data_t *dwd; 222 223 if (wsp->walk_addr != NULL) { 224 mdb_warn("must supply starting address\n"); 225 return (WALK_ERR); 226 } 227 228 dwd = mdb_alloc(sizeof (dbuf_walk_data_t), UM_SLEEP); 229 230 if (mdb_readvar(&dwd->ht, "dbuf_hash_table") == -1) { 231 mdb_warn("failed to read 'dbuf_hash_table'"); 232 mdb_free(dwd, sizeof (dbuf_walk_data_t)); 233 return (WALK_ERR); 234 } 235 dwd->bucket = -1; 236 dwd->dbp = 0; 237 wsp->walk_data = dwd; 238 return (WALK_NEXT); 239 } 240 241 static int 242 dbuf_walk_step(mdb_walk_state_t *wsp) 243 { 244 int status; 245 dbuf_walk_data_t *dwd = wsp->walk_data; 246 247 while (dwd->dbp == 0) { 248 dwd->bucket++; 249 if (dwd->bucket == dwd->ht.hash_table_mask+1) 250 return (WALK_DONE); 251 252 if (mdb_vread(&dwd->dbp, sizeof (void *), 253 (uintptr_t)(dwd->ht.hash_table+dwd->bucket)) == -1) { 254 mdb_warn("failed to read hash bucket %u at %p", 255 dwd->bucket, dwd->ht.hash_table+dwd->bucket); 256 return (WALK_DONE); 257 } 258 } 259 260 wsp->walk_addr = dwd->dbp; 261 if (mdb_vread(&dwd->db, sizeof (dmu_buf_impl_t), 262 wsp->walk_addr) == -1) { 263 mdb_warn("failed to read dbuf at %p", wsp->walk_addr); 264 return (WALK_DONE); 265 } 266 status = wsp->walk_callback(wsp->walk_addr, &dwd->db, wsp->walk_cbdata); 267 268 dwd->dbp = (uintptr_t)dwd->db.db_hash_next; 269 return (status); 270 } 271 272 static void 273 dbuf_walk_fini(mdb_walk_state_t *wsp) 274 { 275 dbuf_walk_data_t *dwd = wsp->walk_data; 276 mdb_free(dwd, sizeof (dbuf_walk_data_t)); 277 } 278 279 static int 280 dataset_name(uintptr_t addr, char *buf) 281 { 282 static int gotid; 283 static mdb_ctf_id_t dd_id; 284 uintptr_t dd_parent; 285 char dd_myname[MAXNAMELEN]; 286 287 if (!gotid) { 288 if (mdb_ctf_lookup_by_name("struct dsl_dir", 289 &dd_id) == -1) { 290 mdb_warn("couldn't find struct dsl_dir"); 291 return (DCMD_ERR); 292 } 293 gotid = TRUE; 294 } 295 if (GETMEMBID(addr, &dd_id, dd_parent, dd_parent) || 296 GETMEMBID(addr, &dd_id, dd_myname, dd_myname)) { 297 return (DCMD_ERR); 298 } 299 300 if (dd_parent) { 301 if (dataset_name(dd_parent, buf)) 302 return (DCMD_ERR); 303 strcat(buf, "/"); 304 } 305 306 if (dd_myname[0]) 307 strcat(buf, dd_myname); 308 else 309 strcat(buf, "???"); 310 311 return (0); 312 } 313 314 static int 315 objset_name(uintptr_t addr, char *buf) 316 { 317 static int gotid; 318 static mdb_ctf_id_t osi_id, ds_id; 319 uintptr_t os_dsl_dataset; 320 char ds_snapname[MAXNAMELEN]; 321 uintptr_t ds_dir; 322 323 buf[0] = '\0'; 324 325 if (!gotid) { 326 if (mdb_ctf_lookup_by_name("struct objset_impl", 327 &osi_id) == -1) { 328 mdb_warn("couldn't find struct objset_impl"); 329 return (DCMD_ERR); 330 } 331 if (mdb_ctf_lookup_by_name("struct dsl_dataset", 332 &ds_id) == -1) { 333 mdb_warn("couldn't find struct dsl_dataset"); 334 return (DCMD_ERR); 335 } 336 337 gotid = TRUE; 338 } 339 340 if (GETMEMBID(addr, &osi_id, os_dsl_dataset, os_dsl_dataset)) 341 return (DCMD_ERR); 342 343 if (os_dsl_dataset == 0) { 344 strcat(buf, "mos"); 345 return (0); 346 } 347 348 if (GETMEMBID(os_dsl_dataset, &ds_id, ds_snapname, ds_snapname) || 349 GETMEMBID(os_dsl_dataset, &ds_id, ds_dir, ds_dir)) { 350 return (DCMD_ERR); 351 } 352 353 if (ds_dir && dataset_name(ds_dir, buf)) 354 return (DCMD_ERR); 355 356 if (ds_snapname[0]) { 357 strcat(buf, "@"); 358 strcat(buf, ds_snapname); 359 } 360 return (0); 361 } 362 363 static void 364 enum_lookup(char *out, size_t size, mdb_ctf_id_t id, int val, 365 const char *prefix) 366 { 367 const char *cp; 368 size_t len = strlen(prefix); 369 370 if ((cp = mdb_ctf_enum_name(id, val)) != NULL) { 371 if (strncmp(cp, prefix, len) == 0) 372 cp += len; 373 (void) strncpy(out, cp, size); 374 } else { 375 mdb_snprintf(out, size, "? (%d)", val); 376 } 377 } 378 379 /* ARGSUSED */ 380 static int 381 zio_pipeline(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 382 { 383 mdb_ctf_id_t pipe_enum; 384 int i; 385 char stage[1024]; 386 387 if (mdb_ctf_lookup_by_name("enum zio_stage", &pipe_enum) == -1) { 388 mdb_warn("Could not find enum zio_stage"); 389 return (DCMD_ERR); 390 } 391 392 for (i = 0; i < 32; i++) { 393 if (addr & (1U << i)) { 394 enum_lookup(stage, sizeof (stage), pipe_enum, i, 395 "ZIO_STAGE_"); 396 mdb_printf(" %s\n", stage); 397 } 398 } 399 400 return (DCMD_OK); 401 } 402 403 /* ARGSUSED */ 404 static int 405 blkptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 406 { 407 blkptr_t bp; 408 dmu_object_type_info_t *doti; 409 zio_compress_info_t *zct; 410 zio_checksum_info_t *zci; 411 int i; 412 char buf[MAXPATHLEN]; 413 414 if (mdb_vread(&bp, sizeof (blkptr_t), addr) == -1) { 415 mdb_warn("failed to read blkptr_t"); 416 return (DCMD_ERR); 417 } 418 419 if (read_symbol("dmu_ot", (void **)&doti) != DCMD_OK) 420 return (DCMD_ERR); 421 for (i = 0; i < DMU_OT_NUMTYPES; i++) { 422 mdb_readstr(buf, sizeof (buf), (uintptr_t)doti[i].ot_name); 423 doti[i].ot_name = local_strdup(buf); 424 } 425 426 if (read_symbol("zio_checksum_table", (void **)&zci) != DCMD_OK) 427 return (DCMD_ERR); 428 for (i = 0; i < ZIO_CHECKSUM_FUNCTIONS; i++) { 429 mdb_readstr(buf, sizeof (buf), (uintptr_t)zci[i].ci_name); 430 zci[i].ci_name = local_strdup(buf); 431 } 432 433 if (read_symbol("zio_compress_table", (void **)&zct) != DCMD_OK) 434 return (DCMD_ERR); 435 for (i = 0; i < ZIO_COMPRESS_FUNCTIONS; i++) { 436 mdb_readstr(buf, sizeof (buf), (uintptr_t)zct[i].ci_name); 437 zct[i].ci_name = local_strdup(buf); 438 } 439 440 for (i = 0; i < SPA_DVAS_PER_BP; i++) { 441 dva_t *dva = &bp.blk_dva[i]; 442 mdb_printf("DVA[%d]: GANG: %-5s GRID: %2x ASIZE: %5x " 443 "vdev %llu offset %llx\n", 444 i, 445 DVA_GET_GANG(dva) ? "TRUE" : "FALSE", 446 DVA_GET_GRID(dva), 447 DVA_GET_ASIZE(dva), 448 DVA_GET_VDEV(dva), 449 DVA_GET_OFFSET(dva)); 450 } 451 mdb_printf("LSIZE: %-16llx\t\tPSIZE: %llx\n", 452 BP_GET_LSIZE(&bp), BP_GET_PSIZE(&bp)); 453 mdb_printf("ENDIAN: %-6s TYPE: %s\n", 454 BP_GET_BYTEORDER(&bp) ? "LITTLE" : "BIG", 455 doti[BP_GET_TYPE(&bp)].ot_name); 456 mdb_printf("BIRTH: %-16llx LEVEL: %-2d\tFILL: %llx\n", 457 bp.blk_birth, BP_GET_LEVEL(&bp), bp.blk_fill); 458 mdb_printf("CKFUNC: %-16s\t\tCOMP: %s\n", 459 zci[BP_GET_CHECKSUM(&bp)].ci_name, 460 zct[BP_GET_COMPRESS(&bp)].ci_name); 461 mdb_printf("CKSUM: %llx:%llx:%llx:%llx\n", 462 bp.blk_cksum.zc_word[0], 463 bp.blk_cksum.zc_word[1], 464 bp.blk_cksum.zc_word[2], 465 bp.blk_cksum.zc_word[3]); 466 467 return (DCMD_OK); 468 } 469 470 /* ARGSUSED */ 471 static int 472 dbuf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 473 { 474 mdb_ctf_id_t id; 475 dmu_buf_t db; 476 uintptr_t objset; 477 uint8_t level; 478 uint64_t blkid; 479 uint64_t holds; 480 char objectname[32]; 481 char blkidname[32]; 482 char path[MAXNAMELEN]; 483 484 if (DCMD_HDRSPEC(flags)) { 485 mdb_printf(" addr object lvl blkid holds os\n"); 486 } 487 488 if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &id) == -1) { 489 mdb_warn("couldn't find struct dmu_buf_impl_t"); 490 return (DCMD_ERR); 491 } 492 493 if (GETMEMBID(addr, &id, db_objset, objset) || 494 GETMEMBID(addr, &id, db, db) || 495 GETMEMBID(addr, &id, db_level, level) || 496 GETMEMBID(addr, &id, db_blkid, blkid)) { 497 return (WALK_ERR); 498 } 499 500 if (getrefcount(addr, &id, "db_holds", &holds)) { 501 return (WALK_ERR); 502 } 503 504 if (db.db_object == DMU_META_DNODE_OBJECT) 505 (void) strcpy(objectname, "mdn"); 506 else 507 (void) mdb_snprintf(objectname, sizeof (objectname), "%llx", 508 (u_longlong_t)db.db_object); 509 510 if (blkid == DB_BONUS_BLKID) 511 (void) strcpy(blkidname, "bonus"); 512 else 513 (void) mdb_snprintf(blkidname, sizeof (blkidname), "%llx", 514 (u_longlong_t)blkid); 515 516 if (objset_name(objset, path)) { 517 return (WALK_ERR); 518 } 519 520 mdb_printf("%p %8s %1u %9s %2llu %s\n", 521 addr, objectname, level, blkidname, holds, path); 522 523 return (DCMD_OK); 524 } 525 526 /* ARGSUSED */ 527 static int 528 dbuf_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 529 { 530 #define HISTOSZ 32 531 uintptr_t dbp; 532 dmu_buf_impl_t db; 533 dbuf_hash_table_t ht; 534 uint64_t bucket, ndbufs; 535 uint64_t histo[HISTOSZ]; 536 uint64_t histo2[HISTOSZ]; 537 int i, maxidx; 538 539 if (mdb_readvar(&ht, "dbuf_hash_table") == -1) { 540 mdb_warn("failed to read 'dbuf_hash_table'"); 541 return (DCMD_ERR); 542 } 543 544 for (i = 0; i < HISTOSZ; i++) { 545 histo[i] = 0; 546 histo2[i] = 0; 547 } 548 549 ndbufs = 0; 550 for (bucket = 0; bucket < ht.hash_table_mask+1; bucket++) { 551 int len; 552 553 if (mdb_vread(&dbp, sizeof (void *), 554 (uintptr_t)(ht.hash_table+bucket)) == -1) { 555 mdb_warn("failed to read hash bucket %u at %p", 556 bucket, ht.hash_table+bucket); 557 return (DCMD_ERR); 558 } 559 560 len = 0; 561 while (dbp != 0) { 562 if (mdb_vread(&db, sizeof (dmu_buf_impl_t), 563 dbp) == -1) { 564 mdb_warn("failed to read dbuf at %p", dbp); 565 return (DCMD_ERR); 566 } 567 dbp = (uintptr_t)db.db_hash_next; 568 for (i = MIN(len, HISTOSZ - 1); i >= 0; i--) 569 histo2[i]++; 570 len++; 571 ndbufs++; 572 } 573 574 if (len >= HISTOSZ) 575 len = HISTOSZ-1; 576 histo[len]++; 577 } 578 579 mdb_printf("hash table has %llu buckets, %llu dbufs " 580 "(avg %llu buckets/dbuf)\n", 581 ht.hash_table_mask+1, ndbufs, 582 (ht.hash_table_mask+1)/ndbufs); 583 584 mdb_printf("\n"); 585 maxidx = 0; 586 for (i = 0; i < HISTOSZ; i++) 587 if (histo[i] > 0) 588 maxidx = i; 589 mdb_printf("hash chain length number of buckets\n"); 590 for (i = 0; i <= maxidx; i++) 591 mdb_printf("%u %llu\n", i, histo[i]); 592 593 mdb_printf("\n"); 594 maxidx = 0; 595 for (i = 0; i < HISTOSZ; i++) 596 if (histo2[i] > 0) 597 maxidx = i; 598 mdb_printf("hash chain depth number of dbufs\n"); 599 for (i = 0; i <= maxidx; i++) 600 mdb_printf("%u or more %llu %llu%%\n", 601 i, histo2[i], histo2[i]*100/ndbufs); 602 603 604 return (DCMD_OK); 605 } 606 607 typedef struct dbufs_data { 608 mdb_ctf_id_t id; 609 uint64_t objset; 610 uint64_t object; 611 uint64_t level; 612 uint64_t blkid; 613 char *osname; 614 } dbufs_data_t; 615 616 #define DBUFS_UNSET (0xbaddcafedeadbeefULL) 617 618 /* ARGSUSED */ 619 static int 620 dbufs_cb(uintptr_t addr, const void *unknown, void *arg) 621 { 622 dbufs_data_t *data = arg; 623 uintptr_t objset; 624 dmu_buf_t db; 625 uint8_t level; 626 uint64_t blkid; 627 char osname[MAXNAMELEN]; 628 629 if (GETMEMBID(addr, &data->id, db_objset, objset) || 630 GETMEMBID(addr, &data->id, db, db) || 631 GETMEMBID(addr, &data->id, db_level, level) || 632 GETMEMBID(addr, &data->id, db_blkid, blkid)) { 633 return (WALK_ERR); 634 } 635 636 if ((data->objset == DBUFS_UNSET || data->objset == objset) && 637 (data->osname == NULL || (objset_name(objset, osname) == 0 && 638 strcmp(data->osname, osname) == 0)) && 639 (data->object == DBUFS_UNSET || data->object == db.db_object) && 640 (data->level == DBUFS_UNSET || data->level == level) && 641 (data->blkid == DBUFS_UNSET || data->blkid == blkid)) { 642 mdb_printf("%#lr\n", addr); 643 } 644 return (WALK_NEXT); 645 } 646 647 /* ARGSUSED */ 648 static int 649 dbufs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 650 { 651 dbufs_data_t data; 652 char *object = NULL; 653 char *blkid = NULL; 654 655 data.objset = data.object = data.level = data.blkid = DBUFS_UNSET; 656 data.osname = NULL; 657 658 if (mdb_getopts(argc, argv, 659 'O', MDB_OPT_UINT64, &data.objset, 660 'n', MDB_OPT_STR, &data.osname, 661 'o', MDB_OPT_STR, &object, 662 'l', MDB_OPT_UINT64, &data.level, 663 'b', MDB_OPT_STR, &blkid) != argc) { 664 return (DCMD_USAGE); 665 } 666 667 if (object) { 668 if (strcmp(object, "mdn") == 0) { 669 data.object = DMU_META_DNODE_OBJECT; 670 } else { 671 data.object = mdb_strtoull(object); 672 } 673 } 674 675 if (blkid) { 676 if (strcmp(blkid, "bonus") == 0) { 677 data.blkid = DB_BONUS_BLKID; 678 } else { 679 data.blkid = mdb_strtoull(blkid); 680 } 681 } 682 683 if (mdb_ctf_lookup_by_name("struct dmu_buf_impl", &data.id) == -1) { 684 mdb_warn("couldn't find struct dmu_buf_impl_t"); 685 return (DCMD_ERR); 686 } 687 688 if (mdb_pwalk("dbufs", dbufs_cb, &data, 0) != 0) { 689 mdb_warn("can't walk dbufs"); 690 return (DCMD_ERR); 691 } 692 693 return (DCMD_OK); 694 } 695 696 typedef struct abuf_find_data { 697 dva_t dva; 698 mdb_ctf_id_t id; 699 } abuf_find_data_t; 700 701 /* ARGSUSED */ 702 static int 703 abuf_find_cb(uintptr_t addr, const void *unknown, void *arg) 704 { 705 abuf_find_data_t *data = arg; 706 dva_t dva; 707 708 if (GETMEMBID(addr, &data->id, b_dva, dva)) { 709 return (WALK_ERR); 710 } 711 712 if (dva.dva_word[0] == data->dva.dva_word[0] && 713 dva.dva_word[1] == data->dva.dva_word[1]) { 714 mdb_printf("%#lr\n", addr); 715 } 716 return (WALK_NEXT); 717 } 718 719 /* ARGSUSED */ 720 static int 721 abuf_find(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 722 { 723 abuf_find_data_t data; 724 GElf_Sym sym; 725 int i; 726 const char *syms[] = { 727 "ARC_mru_top", 728 "ARC_mru_bot", 729 "ARC_mfu_top", 730 "ARC_mfu_bot", 731 }; 732 733 if (argc != 2) 734 return (DCMD_USAGE); 735 736 for (i = 0; i < 2; i ++) { 737 switch (argv[i].a_type) { 738 case MDB_TYPE_STRING: 739 data.dva.dva_word[i] = mdb_strtoull(argv[i].a_un.a_str); 740 break; 741 case MDB_TYPE_IMMEDIATE: 742 data.dva.dva_word[i] = argv[i].a_un.a_val; 743 break; 744 default: 745 return (DCMD_USAGE); 746 } 747 } 748 749 if (mdb_ctf_lookup_by_name("struct arc_buf_hdr", &data.id) == -1) { 750 mdb_warn("couldn't find struct arc_buf_hdr"); 751 return (DCMD_ERR); 752 } 753 754 for (i = 0; i < sizeof (syms) / sizeof (syms[0]); i++) { 755 if (mdb_lookup_by_name(syms[i], &sym)) { 756 mdb_warn("can't find symbol %s", syms[i]); 757 return (DCMD_ERR); 758 } 759 760 if (mdb_pwalk("list", abuf_find_cb, &data, sym.st_value) != 0) { 761 mdb_warn("can't walk %s", syms[i]); 762 return (DCMD_ERR); 763 } 764 } 765 766 return (DCMD_OK); 767 } 768 769 void 770 abuf_help(void) 771 { 772 mdb_printf("::abuf_find dva_word[0] dva_word[1]\n"); 773 } 774 775 /* 776 * ::spa 777 * 778 * -c Print configuration information as well 779 * -v Print vdev state 780 * -e Print vdev error stats 781 * 782 * Print a summarized spa_t. When given no arguments, prints out a table of all 783 * active pools on the system. 784 */ 785 /* ARGSUSED */ 786 static int 787 spa_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 788 { 789 spa_t spa; 790 char poolname[MAXNAMELEN]; 791 const char *statetab[] = { "ACTIVE", "EXPORTED", "DESTROYED", 792 "UNINIT", "UNAVAIL" }; 793 const char *state; 794 int config = FALSE; 795 int vdevs = FALSE; 796 int errors = FALSE; 797 798 if (mdb_getopts(argc, argv, 799 'c', MDB_OPT_SETBITS, TRUE, &config, 800 'v', MDB_OPT_SETBITS, TRUE, &vdevs, 801 'e', MDB_OPT_SETBITS, TRUE, &errors, 802 NULL) != argc) 803 return (DCMD_USAGE); 804 805 if (!(flags & DCMD_ADDRSPEC)) { 806 if (mdb_walk_dcmd("spa", "spa", argc, argv) == -1) { 807 mdb_warn("can't walk spa"); 808 return (DCMD_ERR); 809 } 810 811 return (DCMD_OK); 812 } 813 814 if (flags & DCMD_PIPE_OUT) { 815 mdb_printf("%#lr\n", addr); 816 return (DCMD_OK); 817 } 818 819 if (DCMD_HDRSPEC(flags)) 820 mdb_printf("%<u>%-?s %9s %-*s%</u>\n", "ADDR", "STATE", 821 sizeof (uintptr_t) == 4 ? 60 : 52, "NAME"); 822 823 if (mdb_vread(&spa, sizeof (spa), addr) == -1) { 824 mdb_warn("failed to read spa_t at %p", addr); 825 return (DCMD_ERR); 826 } 827 828 if (mdb_readstr(poolname, sizeof (poolname), (uintptr_t)spa.spa_name) 829 == -1) { 830 mdb_warn("failed to read pool name at %p", spa.spa_name); 831 return (DCMD_ERR); 832 } 833 834 if (spa.spa_state < 0 || spa.spa_state > POOL_STATE_UNAVAIL) 835 state = "UNKNOWN"; 836 else 837 state = statetab[spa.spa_state]; 838 839 mdb_printf("%0?p %9s %s\n", addr, state, poolname); 840 841 if (config) { 842 mdb_printf("\n"); 843 mdb_inc_indent(4); 844 if (mdb_call_dcmd("spa_config", addr, flags, 0, 845 NULL) != DCMD_OK) 846 return (DCMD_ERR); 847 mdb_dec_indent(4); 848 } 849 850 if (vdevs || errors) { 851 mdb_arg_t v; 852 853 v.a_type = MDB_TYPE_STRING; 854 v.a_un.a_str = "-e"; 855 856 mdb_printf("\n"); 857 mdb_inc_indent(4); 858 if (mdb_call_dcmd("spa_vdevs", addr, flags, errors ? 1 : 0, 859 &v) != DCMD_OK) 860 return (DCMD_ERR); 861 mdb_dec_indent(4); 862 } 863 864 return (DCMD_OK); 865 } 866 867 /* 868 * ::spa_config 869 * 870 * Given a spa_t, print the configuration information stored in spa_config. 871 * Since it's just an nvlist, format it as an indented list of name=value pairs. 872 * We simply read the value of spa_config and pass off to ::nvlist. 873 */ 874 /* ARGSUSED */ 875 static int 876 spa_print_config(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 877 { 878 spa_t spa; 879 880 if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 881 return (DCMD_USAGE); 882 883 if (mdb_vread(&spa, sizeof (spa), addr) == -1) { 884 mdb_warn("failed to read spa_t at %p", addr); 885 return (DCMD_ERR); 886 } 887 888 if (spa.spa_config == NULL) { 889 mdb_printf("(none)\n"); 890 return (DCMD_OK); 891 } 892 893 return (mdb_call_dcmd("nvlist", (uintptr_t)spa.spa_config, flags, 894 0, NULL)); 895 } 896 897 void 898 vdev_help(void) 899 { 900 mdb_printf("[vdev_t*]::vdev [-qr]\n" 901 "\t-> -q display vdev_queue parameters\n" 902 "\t-> -r recursive (visit all children)\n"); 903 } 904 905 /* 906 * ::vdev 907 * 908 * Print out a summarized vdev_t, in the following form: 909 * 910 * ADDR STATE AUX DESC 911 * fffffffbcde23df0 HEALTHY - /dev/dsk/c0t0d0 912 * 913 * or with "-q" to print out a vdev_t's vdev_queue parameters: 914 * 915 * vdev_t: c26ae4c0 916 * c26ae73c min pending 0x2 917 * c26ae744 max pending 0x23 918 * c26ae74c agg limit 0x20000 919 * c26ae754 time shift 0x4 920 * c26ae75c ramp rate 0x2 921 * 922 * If '-r' is specified, recursively visit all children. 923 * 924 * With '-e', the statistics associated with the vdev are printed as well. 925 */ 926 static int 927 do_print_vdev(uintptr_t addr, int flags, int depth, int queue, int stats, 928 int recursive) 929 { 930 vdev_t vdev; 931 char desc[MAXNAMELEN]; 932 int c, children; 933 uintptr_t *child; 934 const char *state, *aux; 935 936 if (mdb_vread(&vdev, sizeof (vdev), (uintptr_t)addr) == -1) { 937 mdb_warn("failed to read vdev_t at %p\n", (uintptr_t)addr); 938 return (DCMD_ERR); 939 } 940 941 if (flags & DCMD_PIPE_OUT) { 942 mdb_printf("%#lr", addr); 943 } else { 944 if (vdev.vdev_path != NULL) { 945 if (mdb_readstr(desc, sizeof (desc), 946 (uintptr_t)vdev.vdev_path) == -1) { 947 mdb_warn("failed to read vdev_path at %p\n", 948 vdev.vdev_path); 949 return (DCMD_ERR); 950 } 951 } else if (vdev.vdev_ops != NULL) { 952 vdev_ops_t ops; 953 if (mdb_vread(&ops, sizeof (ops), 954 (uintptr_t)vdev.vdev_ops) == -1) { 955 mdb_warn("failed to read vdev_ops at %p\n", 956 vdev.vdev_ops); 957 return (DCMD_ERR); 958 } 959 (void) strcpy(desc, ops.vdev_op_type); 960 } else { 961 (void) strcpy(desc, "<unknown>"); 962 } 963 964 if (depth == 0 && DCMD_HDRSPEC(flags)) 965 mdb_printf("%<u>%-?s %-9s %-12s %-*s%</u>\n", 966 "ADDR", "STATE", "AUX", 967 sizeof (uintptr_t) == 4 ? 43 : 35, 968 "DESCRIPTION"); 969 970 mdb_printf("%0?p ", addr); 971 972 switch (vdev.vdev_state) { 973 case VDEV_STATE_CLOSED: 974 state = "CLOSED"; 975 break; 976 case VDEV_STATE_OFFLINE: 977 state = "OFFLINE"; 978 break; 979 case VDEV_STATE_CANT_OPEN: 980 state = "CANT_OPEN"; 981 break; 982 case VDEV_STATE_DEGRADED: 983 state = "DEGRADED"; 984 break; 985 case VDEV_STATE_HEALTHY: 986 state = "HEALTHY"; 987 break; 988 default: 989 state = "UNKNOWN"; 990 break; 991 } 992 993 switch (vdev.vdev_stat.vs_aux) { 994 case VDEV_AUX_NONE: 995 aux = "-"; 996 break; 997 case VDEV_AUX_OPEN_FAILED: 998 aux = "OPEN_FAILED"; 999 break; 1000 case VDEV_AUX_CORRUPT_DATA: 1001 aux = "CORRUPT_DATA"; 1002 break; 1003 case VDEV_AUX_NO_REPLICAS: 1004 aux = "NO_REPLICAS"; 1005 break; 1006 case VDEV_AUX_BAD_GUID_SUM: 1007 aux = "BAD_GUID_SUM"; 1008 break; 1009 case VDEV_AUX_TOO_SMALL: 1010 aux = "TOO_SMALL"; 1011 break; 1012 case VDEV_AUX_BAD_LABEL: 1013 aux = "BAD_LABEL"; 1014 break; 1015 default: 1016 aux = "UNKNOWN"; 1017 break; 1018 } 1019 1020 mdb_printf("%-9s %-12s %*s%s\n", state, aux, depth, "", desc); 1021 1022 if (queue) { 1023 mdb_inc_indent(4); 1024 mdb_printf("\n"); 1025 mdb_printf("%p min pending 0x%llx\n", 1026 (uintptr_t)(addr + offsetof(vdev_t, 1027 vdev_queue.vq_min_pending)), 1028 vdev.vdev_queue.vq_min_pending); 1029 mdb_printf("%p max pending 0x%llx\n", 1030 (uintptr_t)(addr + offsetof(vdev_t, 1031 vdev_queue.vq_max_pending)), 1032 vdev.vdev_queue.vq_max_pending); 1033 mdb_printf("%p agg limit 0x%llx\n", 1034 (uintptr_t)(addr + offsetof(vdev_t, 1035 vdev_queue.vq_agg_limit)), 1036 vdev.vdev_queue.vq_agg_limit); 1037 mdb_printf("%p time shift 0x%llx\n", 1038 (uintptr_t)(addr + offsetof(vdev_t, 1039 vdev_queue.vq_time_shift)), 1040 vdev.vdev_queue.vq_time_shift); 1041 mdb_printf("%p ramp rate 0x%llx\n", 1042 (uintptr_t)(addr + offsetof(vdev_t, 1043 vdev_queue.vq_ramp_rate)), 1044 vdev.vdev_queue.vq_ramp_rate); 1045 mdb_dec_indent(4); 1046 } 1047 1048 if (stats) { 1049 vdev_stat_t *vs = &vdev.vdev_stat; 1050 int i; 1051 1052 mdb_inc_indent(4); 1053 mdb_printf("\n"); 1054 mdb_printf("%<u> %12s %12s %12s %12s " 1055 "%12s%</u>\n", "READ", "WRITE", "FREE", "CLAIM", 1056 "IOCTL"); 1057 mdb_printf("OPS "); 1058 for (i = 1; i < ZIO_TYPES; i++) 1059 mdb_printf("%11#llx%s", vs->vs_ops[i], 1060 i == ZIO_TYPES - 1 ? "" : " "); 1061 mdb_printf("\n"); 1062 mdb_printf("BYTES "); 1063 for (i = 1; i < ZIO_TYPES; i++) 1064 mdb_printf("%11#llx%s", vs->vs_bytes[i], 1065 i == ZIO_TYPES - 1 ? "" : " "); 1066 1067 1068 mdb_printf("\n"); 1069 mdb_printf("EREAD %10#llx\n", vs->vs_read_errors); 1070 mdb_printf("EWRITE %10#llx\n", vs->vs_write_errors); 1071 mdb_printf("ECKSUM %10#llx\n", 1072 vs->vs_checksum_errors); 1073 mdb_dec_indent(4); 1074 } 1075 1076 if (queue || stats) 1077 mdb_printf("\n"); 1078 } 1079 1080 children = vdev.vdev_children; 1081 1082 if (children == 0 || !recursive) 1083 return (DCMD_OK); 1084 1085 child = mdb_alloc(children * sizeof (void *), UM_SLEEP | UM_GC); 1086 if (mdb_vread(child, children * sizeof (void *), 1087 (uintptr_t)vdev.vdev_child) == -1) { 1088 mdb_warn("failed to read vdev children at %p", vdev.vdev_child); 1089 return (DCMD_ERR); 1090 } 1091 1092 for (c = 0; c < children; c++) { 1093 if (do_print_vdev(child[c], flags, depth + 2, queue, stats, 1094 recursive)) 1095 return (DCMD_ERR); 1096 } 1097 1098 return (DCMD_OK); 1099 } 1100 1101 static int 1102 vdev_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1103 { 1104 int print_queue = FALSE; 1105 int recursive = FALSE; 1106 int stats = FALSE; 1107 1108 if (mdb_getopts(argc, argv, 1109 'q', MDB_OPT_SETBITS, TRUE, &print_queue, 1110 'r', MDB_OPT_SETBITS, TRUE, &recursive, 1111 'e', MDB_OPT_SETBITS, TRUE, &stats, 1112 NULL) != argc) 1113 return (DCMD_USAGE); 1114 1115 if (!(flags & DCMD_ADDRSPEC)) { 1116 mdb_warn("no vdev_t address given\n"); 1117 return (DCMD_ERR); 1118 } 1119 1120 return (do_print_vdev(addr, flags, 0, print_queue, stats, recursive)); 1121 } 1122 1123 typedef struct mdb_spa { 1124 uintptr_t spa_dsl_pool; 1125 uintptr_t spa_root_vdev; 1126 } mdb_spa_t; 1127 1128 typedef struct mdb_dsl_dir { 1129 uintptr_t dd_phys; 1130 uint64_t dd_used_bytes; 1131 int64_t dd_space_towrite[TXG_SIZE]; 1132 } mdb_dsl_dir_t; 1133 1134 typedef struct mdb_dsl_dir_phys { 1135 uint64_t dd_used_bytes; 1136 uint64_t dd_compressed_bytes; 1137 uint64_t dd_uncompressed_bytes; 1138 } mdb_dsl_dir_phys_t; 1139 1140 typedef struct mdb_vdev { 1141 uintptr_t vdev_parent; 1142 uintptr_t vdev_ms; 1143 uint64_t vdev_ms_count; 1144 vdev_stat_t vdev_stat; 1145 } mdb_vdev_t; 1146 1147 typedef struct mdb_metaslab { 1148 space_map_t ms_allocmap[TXG_SIZE]; 1149 space_map_t ms_freemap[TXG_SIZE]; 1150 space_map_t ms_map; 1151 space_map_obj_t ms_smo; 1152 } mdb_metaslab_t; 1153 1154 /* 1155 * ::spa_space [-b] 1156 * 1157 * Given a spa_t, print out it's on-disk space usage and in-core 1158 * estimates of future usage. If -b is given, print space in bytes. 1159 * Otherwise print in megabytes. 1160 */ 1161 /* ARGSUSED */ 1162 static int 1163 spa_space(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1164 { 1165 mdb_spa_t spa; 1166 uintptr_t dp_root_dir; 1167 mdb_dsl_dir_t dd; 1168 mdb_dsl_dir_phys_t dsp; 1169 uint64_t children; 1170 uintptr_t childaddr; 1171 uintptr_t *child; 1172 uint64_t ms_allocmap[TXG_SIZE] = {0, 0, 0, 0}; 1173 uint64_t ms_freemap[TXG_SIZE] = {0, 0, 0, 0}; 1174 uint64_t ms_map = 0; 1175 uint64_t avail = 0; 1176 int i, j; 1177 int havecompressed = TRUE; 1178 int shift = 20; 1179 char *suffix = "M"; 1180 int bits = FALSE; 1181 1182 if (mdb_getopts(argc, argv, 'b', MDB_OPT_SETBITS, TRUE, &bits, NULL) != 1183 argc) 1184 return (DCMD_USAGE); 1185 if (!(flags & DCMD_ADDRSPEC)) 1186 return (DCMD_USAGE); 1187 1188 if (bits) { 1189 shift = 0; 1190 suffix = ""; 1191 } 1192 1193 if (GETMEMB(addr, struct spa, spa_dsl_pool, spa.spa_dsl_pool) || 1194 GETMEMB(addr, struct spa, spa_root_vdev, spa.spa_root_vdev) || 1195 GETMEMB(spa.spa_root_vdev, struct vdev, vdev_children, children) || 1196 GETMEMB(spa.spa_root_vdev, struct vdev, vdev_child, childaddr) || 1197 GETMEMB(spa.spa_dsl_pool, struct dsl_pool, 1198 dp_root_dir, dp_root_dir) || 1199 GETMEMB(dp_root_dir, struct dsl_dir, dd_phys, dd.dd_phys) || 1200 GETMEMB(dp_root_dir, struct dsl_dir, 1201 dd_used_bytes, dd.dd_used_bytes) || 1202 GETMEMB(dp_root_dir, struct dsl_dir, 1203 dd_space_towrite, dd.dd_space_towrite) || 1204 GETMEMB(dd.dd_phys, struct dsl_dir_phys, 1205 dd_used_bytes, dsp.dd_used_bytes)) { 1206 return (DCMD_ERR); 1207 } 1208 1209 if (GETMEMB(dd.dd_phys, struct dsl_dir_phys, 1210 dd_compressed_bytes, dsp.dd_compressed_bytes) || 1211 GETMEMB(dd.dd_phys, struct dsl_dir_phys, 1212 dd_uncompressed_bytes, dsp.dd_uncompressed_bytes)) { 1213 havecompressed = FALSE; 1214 } 1215 1216 child = mdb_alloc(children * sizeof (void *), UM_SLEEP | UM_GC); 1217 if (mdb_vread(child, children * sizeof (void *), childaddr) == -1) { 1218 mdb_warn("failed to read root vdev children at %p", childaddr); 1219 return (DCMD_ERR); 1220 } 1221 1222 mdb_printf("dd_space_towrite = %llu%s %llu%s %llu%s %llu%s\n", 1223 dd.dd_space_towrite[0] >> shift, suffix, 1224 dd.dd_space_towrite[1] >> shift, suffix, 1225 dd.dd_space_towrite[2] >> shift, suffix, 1226 dd.dd_space_towrite[3] >> shift, suffix); 1227 mdb_printf("dd_used_bytes = %llu%s\n", 1228 dd.dd_used_bytes >> shift, suffix); 1229 1230 mdb_printf("dd_phys.dd_used_bytes = %llu%s\n", 1231 dsp.dd_used_bytes >> shift, suffix); 1232 if (havecompressed) { 1233 mdb_printf("dd_phys.dd_compressed_bytes = %llu%s\n", 1234 dsp.dd_compressed_bytes >> shift, suffix); 1235 mdb_printf("dd_phys.dd_uncompressed_bytes = %llu%s\n", 1236 dsp.dd_uncompressed_bytes >> shift, suffix); 1237 } 1238 1239 for (i = 0; i < children; i++) { 1240 mdb_vdev_t vd; 1241 uintptr_t *vdev_ms; 1242 1243 if (GETMEMB(child[i], struct vdev, 1244 vdev_parent, vd.vdev_parent) || 1245 GETMEMB(child[i], struct vdev, 1246 vdev_stat, vd.vdev_stat) || 1247 GETMEMB(child[i], struct vdev, vdev_ms, vd.vdev_ms) || 1248 GETMEMB(child[i], struct vdev, 1249 vdev_ms_count, vd.vdev_ms_count)) { 1250 return (DCMD_ERR); 1251 } 1252 1253 /* 1254 * If this is the root vdev, its stats are the pool-wide stats. 1255 */ 1256 if (vd.vdev_parent == NULL) { 1257 mdb_printf("pool_alloc = %llu%s\n", 1258 vd.vdev_stat.vs_alloc >> shift, suffix); 1259 mdb_printf("pool_space = %llu%s\n", 1260 vd.vdev_stat.vs_space >> shift, suffix); 1261 } 1262 1263 /* 1264 * If this is not a top-level vdev, it doesn't have space. 1265 */ 1266 if (vd.vdev_parent != spa.spa_root_vdev) 1267 continue; 1268 1269 vdev_ms = mdb_alloc(vd.vdev_ms_count * sizeof (void*), 1270 UM_SLEEP | UM_GC); 1271 if (mdb_vread(vdev_ms, vd.vdev_ms_count * sizeof (void*), 1272 (uintptr_t)vd.vdev_ms) == -1) { 1273 mdb_warn("failed to read vdev_ms at %p", vd.vdev_ms); 1274 return (DCMD_ERR); 1275 } 1276 1277 for (j = 0; j < vd.vdev_ms_count; j++) { 1278 mdb_metaslab_t ms; 1279 1280 if (GETMEMB(vdev_ms[j], struct metaslab, 1281 ms_allocmap, ms.ms_allocmap) || 1282 GETMEMB(vdev_ms[j], struct metaslab, 1283 ms_freemap, ms.ms_freemap) || 1284 GETMEMB(vdev_ms[j], struct metaslab, 1285 ms_map, ms.ms_map) || 1286 GETMEMB(vdev_ms[j], struct metaslab, 1287 ms_smo, ms.ms_smo)) { 1288 return (DCMD_ERR); 1289 } 1290 1291 ms_allocmap[0] += ms.ms_allocmap[0].sm_space; 1292 ms_allocmap[1] += ms.ms_allocmap[1].sm_space; 1293 ms_allocmap[2] += ms.ms_allocmap[2].sm_space; 1294 ms_allocmap[3] += ms.ms_allocmap[3].sm_space; 1295 ms_freemap[0] += ms.ms_freemap[0].sm_space; 1296 ms_freemap[1] += ms.ms_freemap[1].sm_space; 1297 ms_freemap[2] += ms.ms_freemap[2].sm_space; 1298 ms_freemap[3] += ms.ms_freemap[3].sm_space; 1299 ms_map += ms.ms_map.sm_space; 1300 avail += ms.ms_map.sm_size - ms.ms_smo.smo_alloc; 1301 } 1302 } 1303 1304 mdb_printf("ms_allocmap = %llu%s %llu%s %llu%s %llu%s\n", 1305 ms_allocmap[0] >> shift, suffix, 1306 ms_allocmap[1] >> shift, suffix, 1307 ms_allocmap[2] >> shift, suffix, 1308 ms_allocmap[3] >> shift, suffix); 1309 mdb_printf("ms_freemap = %llu%s %llu%s %llu%s %llu%s\n", 1310 ms_freemap[0] >> shift, suffix, 1311 ms_freemap[1] >> shift, suffix, 1312 ms_freemap[2] >> shift, suffix, 1313 ms_freemap[3] >> shift, suffix); 1314 mdb_printf("ms_map = %llu%s\n", ms_map >> shift, suffix); 1315 mdb_printf("avail = %llu%s\n", avail >> shift, suffix); 1316 1317 return (DCMD_OK); 1318 } 1319 1320 /* 1321 * ::spa_verify 1322 * 1323 * Given a spa_t, verify that that the pool is self-consistent. 1324 * Currently, it only checks to make sure that the vdev tree exists. 1325 */ 1326 /* ARGSUSED */ 1327 static int 1328 spa_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1329 { 1330 spa_t spa; 1331 1332 if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 1333 return (DCMD_USAGE); 1334 1335 if (mdb_vread(&spa, sizeof (spa), addr) == -1) { 1336 mdb_warn("failed to read spa_t at %p", addr); 1337 return (DCMD_ERR); 1338 } 1339 1340 if (spa.spa_root_vdev == NULL) { 1341 mdb_printf("no vdev tree present\n"); 1342 return (DCMD_OK); 1343 } 1344 1345 return (DCMD_OK); 1346 } 1347 1348 /* 1349 * ::spa_vdevs 1350 * 1351 * -e Include error stats 1352 * 1353 * Print out a summarized list of vdevs for the given spa_t. 1354 * This is accomplished by invoking "::vdev -re" on the root vdev. 1355 */ 1356 /* ARGSUSED */ 1357 static int 1358 spa_vdevs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 1359 { 1360 spa_t spa; 1361 mdb_arg_t v; 1362 int errors = FALSE; 1363 1364 if (mdb_getopts(argc, argv, 1365 'e', MDB_OPT_SETBITS, TRUE, &errors, 1366 NULL) != argc) 1367 return (DCMD_USAGE); 1368 1369 if (!(flags & DCMD_ADDRSPEC)) 1370 return (DCMD_USAGE); 1371 1372 if (mdb_vread(&spa, sizeof (spa), addr) == -1) { 1373 mdb_warn("failed to read spa_t at %p", addr); 1374 return (DCMD_ERR); 1375 } 1376 1377 /* 1378 * Unitialized spa_t structures can have a NULL root vdev. 1379 */ 1380 if (spa.spa_root_vdev == NULL) { 1381 mdb_printf("no associated vdevs\n"); 1382 return (DCMD_OK); 1383 } 1384 1385 v.a_type = MDB_TYPE_STRING; 1386 v.a_un.a_str = errors ? "-re" : "-r"; 1387 1388 return (mdb_call_dcmd("vdev", (uintptr_t)spa.spa_root_vdev, 1389 flags, 1, &v)); 1390 } 1391 1392 typedef struct txg_list_walk_data { 1393 uintptr_t lw_head[TXG_SIZE]; 1394 int lw_txgoff; 1395 int lw_maxoff; 1396 size_t lw_offset; 1397 void *lw_obj; 1398 } txg_list_walk_data_t; 1399 1400 static int 1401 txg_list_walk_init_common(mdb_walk_state_t *wsp, int txg, int maxoff) 1402 { 1403 txg_list_walk_data_t *lwd; 1404 txg_list_t list; 1405 int i; 1406 1407 lwd = mdb_alloc(sizeof (txg_list_walk_data_t), UM_SLEEP | UM_GC); 1408 if (mdb_vread(&list, sizeof (txg_list_t), wsp->walk_addr) == -1) { 1409 mdb_warn("failed to read txg_list_t at %#lx", wsp->walk_addr); 1410 return (WALK_ERR); 1411 } 1412 1413 for (i = 0; i < TXG_SIZE; i++) 1414 lwd->lw_head[i] = (uintptr_t)list.tl_head[i]; 1415 lwd->lw_offset = list.tl_offset; 1416 lwd->lw_obj = mdb_alloc(lwd->lw_offset + sizeof (txg_node_t), 1417 UM_SLEEP | UM_GC); 1418 lwd->lw_txgoff = txg; 1419 lwd->lw_maxoff = maxoff; 1420 1421 wsp->walk_addr = lwd->lw_head[lwd->lw_txgoff]; 1422 wsp->walk_data = lwd; 1423 1424 return (WALK_NEXT); 1425 } 1426 1427 static int 1428 txg_list_walk_init(mdb_walk_state_t *wsp) 1429 { 1430 return (txg_list_walk_init_common(wsp, 0, TXG_SIZE-1)); 1431 } 1432 1433 static int 1434 txg_list0_walk_init(mdb_walk_state_t *wsp) 1435 { 1436 return (txg_list_walk_init_common(wsp, 0, 0)); 1437 } 1438 1439 static int 1440 txg_list1_walk_init(mdb_walk_state_t *wsp) 1441 { 1442 return (txg_list_walk_init_common(wsp, 1, 1)); 1443 } 1444 1445 static int 1446 txg_list2_walk_init(mdb_walk_state_t *wsp) 1447 { 1448 return (txg_list_walk_init_common(wsp, 2, 2)); 1449 } 1450 1451 static int 1452 txg_list3_walk_init(mdb_walk_state_t *wsp) 1453 { 1454 return (txg_list_walk_init_common(wsp, 3, 3)); 1455 } 1456 1457 static int 1458 txg_list_walk_step(mdb_walk_state_t *wsp) 1459 { 1460 txg_list_walk_data_t *lwd = wsp->walk_data; 1461 uintptr_t addr; 1462 txg_node_t *node; 1463 int status; 1464 1465 while (wsp->walk_addr == NULL && lwd->lw_txgoff < lwd->lw_maxoff) { 1466 lwd->lw_txgoff++; 1467 wsp->walk_addr = lwd->lw_head[lwd->lw_txgoff]; 1468 } 1469 1470 if (wsp->walk_addr == NULL) 1471 return (WALK_DONE); 1472 1473 addr = wsp->walk_addr - lwd->lw_offset; 1474 1475 if (mdb_vread(lwd->lw_obj, 1476 lwd->lw_offset + sizeof (txg_node_t), addr) == -1) { 1477 mdb_warn("failed to read list element at %#lx", addr); 1478 return (WALK_ERR); 1479 } 1480 1481 status = wsp->walk_callback(addr, lwd->lw_obj, wsp->walk_cbdata); 1482 node = (txg_node_t *)((uintptr_t)lwd->lw_obj + lwd->lw_offset); 1483 wsp->walk_addr = (uintptr_t)node->tn_next[lwd->lw_txgoff]; 1484 1485 return (status); 1486 } 1487 1488 /* ARGSUSED */ 1489 static void 1490 txg_list_walk_fini(mdb_walk_state_t *wsp) 1491 { 1492 } 1493 1494 /* 1495 * ::walk spa 1496 * 1497 * Walk all named spa_t structures in the namespace. This is nothing more than 1498 * a layered avl walk. 1499 */ 1500 static int 1501 spa_walk_init(mdb_walk_state_t *wsp) 1502 { 1503 GElf_Sym sym; 1504 1505 if (wsp->walk_addr != NULL) { 1506 mdb_warn("spa walk only supports global walks\n"); 1507 return (WALK_ERR); 1508 } 1509 1510 if (mdb_lookup_by_obj(ZFS_OBJ_NAME, "spa_namespace_avl", &sym) == -1) { 1511 mdb_warn("failed to find symbol 'spa_namespace_avl'"); 1512 return (WALK_ERR); 1513 } 1514 1515 wsp->walk_addr = (uintptr_t)sym.st_value; 1516 1517 if (mdb_layered_walk("avl", wsp) == -1) { 1518 mdb_warn("failed to walk 'avl'\n"); 1519 return (WALK_ERR); 1520 } 1521 1522 return (WALK_NEXT); 1523 } 1524 1525 static int 1526 spa_walk_step(mdb_walk_state_t *wsp) 1527 { 1528 spa_t spa; 1529 1530 if (mdb_vread(&spa, sizeof (spa), wsp->walk_addr) == -1) { 1531 mdb_warn("failed to read spa_t at %p", wsp->walk_addr); 1532 return (WALK_ERR); 1533 } 1534 1535 return (wsp->walk_callback(wsp->walk_addr, &spa, wsp->walk_cbdata)); 1536 } 1537 1538 /* 1539 * MDB module linkage information: 1540 * 1541 * We declare a list of structures describing our dcmds, and a function 1542 * named _mdb_init to return a pointer to our module information. 1543 */ 1544 1545 static const mdb_dcmd_t dcmds[] = { 1546 { "blkptr", ":", "print blkptr_t", blkptr }, 1547 { "dbuf", ":", "print dmu_buf_impl_t", dbuf }, 1548 { "dbuf_stats", ":", "dbuf stats", dbuf_stats }, 1549 { "dbufs", 1550 "\t[-O objset_t*] [-n objset_name | \"mos\"] [-o object | \"mdn\"] \n" 1551 "\t[-l level] [-b blkid | \"bonus\"]", 1552 "find dmu_buf_impl_t's that meet criterion", dbufs }, 1553 { "abuf_find", "dva_word[0] dva_word[1]", 1554 "find arc_buf_hdr_t of a specified DVA", 1555 abuf_find }, 1556 { "spa", "?[-cv]", "spa_t summary", spa_print }, 1557 { "spa_config", ":", "print spa_t configuration", spa_print_config }, 1558 { "spa_verify", ":", "verify spa_t consistency", spa_verify }, 1559 { "spa_space", ":[-b]", "print spa_t on-disk space usage", spa_space }, 1560 { "spa_vdevs", ":", "given a spa_t, print vdev summary", spa_vdevs }, 1561 { "vdev", ":[-qre]", "vdev_t summary", vdev_print }, 1562 { "zio_pipeline", ":", "decode a zio pipeline", zio_pipeline }, 1563 { NULL } 1564 }; 1565 1566 static const mdb_walker_t walkers[] = { 1567 /* 1568 * In userland, there is no generic provider of list_t walkers, so we 1569 * need to add it. 1570 */ 1571 #ifndef _KERNEL 1572 { LIST_WALK_NAME, LIST_WALK_DESC, 1573 list_walk_init, list_walk_step, list_walk_fini }, 1574 #endif 1575 { "dbufs", "walk cached ZFS dbufs", 1576 dbuf_walk_init, dbuf_walk_step, dbuf_walk_fini }, 1577 { "zms_freelist", "walk ZFS metaslab freelist", 1578 freelist_walk_init, freelist_walk_step, freelist_walk_fini }, 1579 { "txg_list", "given any txg_list_t *, walk all entries in all txgs", 1580 txg_list_walk_init, txg_list_walk_step, txg_list_walk_fini }, 1581 { "txg_list0", "given any txg_list_t *, walk all entries in txg 0", 1582 txg_list0_walk_init, txg_list_walk_step, txg_list_walk_fini }, 1583 { "txg_list1", "given any txg_list_t *, walk all entries in txg 1", 1584 txg_list1_walk_init, txg_list_walk_step, txg_list_walk_fini }, 1585 { "txg_list2", "given any txg_list_t *, walk all entries in txg 2", 1586 txg_list2_walk_init, txg_list_walk_step, txg_list_walk_fini }, 1587 { "txg_list3", "given any txg_list_t *, walk all entries in txg 3", 1588 txg_list3_walk_init, txg_list_walk_step, txg_list_walk_fini }, 1589 { "spa", "walk all spa_t entries in the namespace", 1590 spa_walk_init, spa_walk_step, NULL }, 1591 { NULL } 1592 }; 1593 1594 static const mdb_modinfo_t modinfo = { 1595 MDB_API_VERSION, dcmds, walkers 1596 }; 1597 1598 const mdb_modinfo_t * 1599 _mdb_init(void) 1600 { 1601 return (&modinfo); 1602 } 1603