1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sbin/hammer/cmd_show.c,v 1.18 2008/10/09 04:20:59 dillon Exp $ 35 */ 36 37 #include "hammer.h" 38 39 #define FLAG_TOOFARLEFT 0x0001 40 #define FLAG_TOOFARRIGHT 0x0002 41 #define FLAG_BADTYPE 0x0004 42 #define FLAG_BADCHILDPARENT 0x0008 43 #define FLAG_BADMIRRORTID 0x0010 44 45 typedef struct btree_search { 46 u_int32_t lo; 47 int64_t obj_id; 48 } *btree_search_t; 49 50 static void print_btree_node(hammer_off_t node_offset, btree_search_t search, 51 int depth, int spike, hammer_tid_t mirror_tid, 52 hammer_base_elm_t left_bound, 53 hammer_base_elm_t right_bound); 54 static const char *check_data_crc(hammer_btree_elm_t elm); 55 static void print_record(hammer_btree_elm_t elm); 56 static void print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type, 57 int flags, const char *label, const char *ext); 58 static int print_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset, 59 hammer_btree_elm_t elm, u_int8_t btype, 60 hammer_base_elm_t left_bound, 61 hammer_base_elm_t right_bound); 62 static void print_bigblock_fill(hammer_off_t offset); 63 64 void 65 hammer_cmd_show(hammer_off_t node_offset, u_int32_t lo, int64_t obj_id, 66 int depth, 67 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound) 68 { 69 struct volume_info *volume; 70 struct btree_search search; 71 btree_search_t searchp; 72 int zone; 73 74 AssertOnFailure = 0; 75 76 if (node_offset == (hammer_off_t)-1) { 77 volume = get_volume(RootVolNo); 78 node_offset = volume->ondisk->vol0_btree_root; 79 if (QuietOpt < 3) { 80 printf("Volume header\trecords=%jd next_tid=%016jx\n", 81 (intmax_t)volume->ondisk->vol0_stat_records, 82 (uintmax_t)volume->ondisk->vol0_next_tid); 83 printf("\t\tbufoffset=%016jx\n", 84 (uintmax_t)volume->ondisk->vol_buf_beg); 85 for (zone = 0; zone < HAMMER_MAX_ZONES; ++zone) { 86 printf("\t\tzone %d\tnext_offset=%016jx\n", 87 zone, 88 (uintmax_t)volume->ondisk->vol0_blockmap[zone].next_offset 89 ); 90 } 91 } 92 rel_volume(volume); 93 } 94 95 if (lo == 0 && obj_id == (int64_t)HAMMER_MIN_OBJID) { 96 searchp = NULL; 97 printf("show %016jx depth %d\n", (uintmax_t)node_offset, depth); 98 } else { 99 search.lo = lo; 100 search.obj_id = obj_id; 101 searchp = &search; 102 printf("show %016jx lo %08x obj_id %016jx depth %d\n", 103 (uintmax_t)node_offset, lo, (uintmax_t)obj_id, depth); 104 } 105 print_btree_node(node_offset, searchp, depth, 0, HAMMER_MAX_TID, 106 left_bound, right_bound); 107 print_btree_node(node_offset, searchp, depth, 1, HAMMER_MAX_TID, 108 left_bound, right_bound); 109 110 AssertOnFailure = 1; 111 } 112 113 static void 114 print_btree_node(hammer_off_t node_offset, btree_search_t search, 115 int depth, int spike, hammer_tid_t mirror_tid, 116 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound) 117 { 118 struct buffer_info *buffer = NULL; 119 hammer_node_ondisk_t node; 120 hammer_btree_elm_t elm; 121 int i; 122 int flags; 123 int maxcount; 124 char badc; 125 char badm; 126 const char *ext; 127 128 node = get_node(node_offset, &buffer); 129 130 if (node == NULL) { 131 printf("BI NODE %016jx (IO ERROR)\n", 132 (uintmax_t)node_offset); 133 return; 134 } 135 136 if (crc32(&node->crc + 1, HAMMER_BTREE_CRCSIZE) == node->crc) 137 badc = ' '; 138 else 139 badc = 'B'; 140 141 if (node->mirror_tid <= mirror_tid) { 142 badm = ' '; 143 } else { 144 badm = 'M'; 145 badc = 'B'; 146 } 147 148 if (spike == 0) { 149 printf("%c%c NODE %016jx cnt=%02d p=%016jx " 150 "type=%c depth=%d", 151 badc, 152 badm, 153 (uintmax_t)node_offset, node->count, 154 (uintmax_t)node->parent, 155 (node->type ? node->type : '?'), depth); 156 printf(" mirror %016jx", (uintmax_t)node->mirror_tid); 157 if (QuietOpt < 3) { 158 printf(" fill="); 159 print_bigblock_fill(node_offset); 160 } 161 printf(" {\n"); 162 163 maxcount = (node->type == HAMMER_BTREE_TYPE_INTERNAL) ? 164 HAMMER_BTREE_INT_ELMS : HAMMER_BTREE_LEAF_ELMS; 165 166 for (i = 0; i < node->count && i < maxcount; ++i) { 167 elm = &node->elms[i]; 168 169 if (node->type != HAMMER_BTREE_TYPE_INTERNAL) { 170 ext = NULL; 171 if (search && 172 elm->base.localization == search->lo && 173 elm->base.obj_id == search->obj_id) { 174 ext = " *"; 175 } 176 } else if (search) { 177 ext = " *"; 178 if (elm->base.localization > search->lo || 179 (elm->base.localization == search->lo && 180 elm->base.obj_id > search->obj_id)) { 181 ext = NULL; 182 } 183 if (elm[1].base.localization < search->lo || 184 (elm[1].base.localization == search->lo && 185 elm[1].base.obj_id < search->obj_id)) { 186 ext = NULL; 187 } 188 } else { 189 ext = NULL; 190 } 191 192 flags = print_elm_flags(node, node_offset, 193 elm, elm->base.btype, 194 left_bound, right_bound); 195 print_btree_elm(elm, i, node->type, flags, "ELM", ext); 196 } 197 if (node->type == HAMMER_BTREE_TYPE_INTERNAL) { 198 elm = &node->elms[i]; 199 200 flags = print_elm_flags(node, node_offset, 201 elm, 'I', 202 left_bound, right_bound); 203 print_btree_elm(elm, i, node->type, flags, "RBN", NULL); 204 } 205 printf(" }\n"); 206 } 207 208 for (i = 0; i < node->count; ++i) { 209 elm = &node->elms[i]; 210 211 switch(node->type) { 212 case HAMMER_BTREE_TYPE_INTERNAL: 213 if (search) { 214 if (elm->base.localization > search->lo || 215 (elm->base.localization == search->lo && 216 elm->base.obj_id > search->obj_id)) { 217 break; 218 } 219 if (elm[1].base.localization < search->lo || 220 (elm[1].base.localization == search->lo && 221 elm[1].base.obj_id < search->obj_id)) { 222 break; 223 } 224 } 225 if (elm->internal.subtree_offset) { 226 print_btree_node(elm->internal.subtree_offset, 227 search, depth + 1, spike, 228 elm->internal.mirror_tid, 229 &elm[0].base, &elm[1].base); 230 /* 231 * Cause show to iterate after seeking to 232 * the lo:objid 233 */ 234 search = NULL; 235 } 236 break; 237 default: 238 break; 239 } 240 } 241 rel_buffer(buffer); 242 } 243 244 static 245 void 246 print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type, 247 int flags, const char *label, const char *ext) 248 { 249 char flagstr[8] = { 0, '-', '-', '-', '-', '-', '-', 0 }; 250 251 flagstr[0] = flags ? 'B' : 'G'; 252 if (flags & FLAG_TOOFARLEFT) 253 flagstr[2] = 'L'; 254 if (flags & FLAG_TOOFARRIGHT) 255 flagstr[3] = 'R'; 256 if (flags & FLAG_BADTYPE) 257 flagstr[4] = 'T'; 258 if (flags & FLAG_BADCHILDPARENT) 259 flagstr[5] = 'C'; 260 if (flags & FLAG_BADMIRRORTID) 261 flagstr[6] = 'M'; 262 263 printf("%s\t%s %2d %c ", 264 flagstr, label, i, 265 (elm->base.btype ? elm->base.btype : '?')); 266 printf("obj=%016jx key=%016jx lo=%08x rt=%02x ot=%02x\n", 267 (uintmax_t)elm->base.obj_id, 268 (uintmax_t)elm->base.key, 269 elm->base.localization, 270 elm->base.rec_type, 271 elm->base.obj_type); 272 printf("\t %c tids %016jx:%016jx ", 273 (elm->base.delete_tid ? 'd' : ' '), 274 (uintmax_t)elm->base.create_tid, 275 (uintmax_t)elm->base.delete_tid); 276 277 switch(type) { 278 case HAMMER_BTREE_TYPE_INTERNAL: 279 printf("suboff=%016jx", 280 (uintmax_t)elm->internal.subtree_offset); 281 if (QuietOpt < 3) { 282 printf(" mirror %016jx", 283 (uintmax_t)elm->internal.mirror_tid); 284 } 285 if (ext) 286 printf(" %s", ext); 287 break; 288 case HAMMER_BTREE_TYPE_LEAF: 289 if (ext) 290 printf(" %s", ext); 291 switch(elm->base.btype) { 292 case HAMMER_BTREE_TYPE_RECORD: 293 if (QuietOpt < 3) 294 printf("\n%s\t ", check_data_crc(elm)); 295 else 296 printf("\n\t "); 297 printf("dataoff=%016jx/%d", 298 (uintmax_t)elm->leaf.data_offset, 299 elm->leaf.data_len); 300 if (QuietOpt < 3) { 301 printf(" crc=%04x", elm->leaf.data_crc); 302 printf("\n\t fills="); 303 print_bigblock_fill(elm->leaf.data_offset); 304 } 305 if (QuietOpt < 2) 306 print_record(elm); 307 break; 308 } 309 break; 310 default: 311 break; 312 } 313 printf("\n"); 314 } 315 316 static 317 int 318 print_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset, 319 hammer_btree_elm_t elm, u_int8_t btype, 320 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound) 321 { 322 int flags = 0; 323 324 switch(node->type) { 325 case HAMMER_BTREE_TYPE_INTERNAL: 326 if (elm->internal.subtree_offset) { 327 struct buffer_info *buffer = NULL; 328 hammer_node_ondisk_t subnode; 329 330 subnode = get_node(elm->internal.subtree_offset, 331 &buffer); 332 if (subnode == NULL) 333 flags |= FLAG_BADCHILDPARENT; 334 else if (subnode->parent != node_offset) 335 flags |= FLAG_BADCHILDPARENT; 336 rel_buffer(buffer); 337 } 338 if (elm->internal.mirror_tid > node->mirror_tid) 339 flags |= FLAG_BADMIRRORTID; 340 341 switch(btype) { 342 case HAMMER_BTREE_TYPE_INTERNAL: 343 if (left_bound == NULL || right_bound == NULL) 344 break; 345 if (hammer_btree_cmp(&elm->base, left_bound) < 0) 346 flags |= FLAG_TOOFARLEFT; 347 if (hammer_btree_cmp(&elm->base, right_bound) > 0) 348 flags |= FLAG_TOOFARRIGHT; 349 break; 350 case HAMMER_BTREE_TYPE_LEAF: 351 if (left_bound == NULL || right_bound == NULL) 352 break; 353 if (hammer_btree_cmp(&elm->base, left_bound) < 0) 354 flags |= FLAG_TOOFARLEFT; 355 if (hammer_btree_cmp(&elm->base, right_bound) >= 0) 356 flags |= FLAG_TOOFARRIGHT; 357 break; 358 default: 359 flags |= FLAG_BADTYPE; 360 break; 361 } 362 break; 363 case HAMMER_BTREE_TYPE_LEAF: 364 if (elm->base.create_tid && 365 elm->base.create_tid > node->mirror_tid) { 366 flags |= FLAG_BADMIRRORTID; 367 } 368 if (elm->base.delete_tid && 369 elm->base.delete_tid > node->mirror_tid) { 370 flags |= FLAG_BADMIRRORTID; 371 } 372 switch(btype) { 373 case HAMMER_BTREE_TYPE_RECORD: 374 if (left_bound == NULL || right_bound == NULL) 375 break; 376 if (hammer_btree_cmp(&elm->base, left_bound) < 0) 377 flags |= FLAG_TOOFARLEFT; 378 if (hammer_btree_cmp(&elm->base, right_bound) >= 0) 379 flags |= FLAG_TOOFARRIGHT; 380 break; 381 default: 382 flags |= FLAG_BADTYPE; 383 break; 384 } 385 break; 386 default: 387 flags |= FLAG_BADTYPE; 388 break; 389 } 390 return(flags); 391 } 392 393 static 394 void 395 print_bigblock_fill(hammer_off_t offset) 396 { 397 struct hammer_blockmap_layer1 layer1; 398 struct hammer_blockmap_layer2 layer2; 399 int fill; 400 int error; 401 402 blockmap_lookup(offset, &layer1, &layer2, &error); 403 if (error) { 404 printf("z%d:%lld=BADZ", 405 HAMMER_ZONE_DECODE(offset), 406 (offset & ~HAMMER_OFF_ZONE_MASK) / 407 HAMMER_LARGEBLOCK_SIZE 408 ); 409 } else { 410 fill = layer2.bytes_free * 100 / HAMMER_LARGEBLOCK_SIZE; 411 fill = 100 - fill; 412 413 printf("z%d:%lld=%d%%", 414 HAMMER_ZONE_DECODE(offset), 415 (offset & ~HAMMER_OFF_ZONE_MASK) / 416 HAMMER_LARGEBLOCK_SIZE, 417 fill 418 ); 419 } 420 } 421 422 /* 423 * Check the generic crc on a data element. Inodes record types are 424 * special in that some of their fields are not CRCed. 425 * 426 * Also check that the zone is valid. 427 */ 428 static 429 const char * 430 check_data_crc(hammer_btree_elm_t elm) 431 { 432 struct buffer_info *data_buffer; 433 hammer_off_t data_offset; 434 int32_t data_len; 435 int32_t len; 436 u_int32_t crc; 437 int error; 438 char *ptr; 439 440 data_offset = elm->leaf.data_offset; 441 data_len = elm->leaf.data_len; 442 data_buffer = NULL; 443 if (data_offset == 0 || data_len == 0) 444 return("Z"); 445 446 crc = 0; 447 error = 0; 448 while (data_len) { 449 blockmap_lookup(data_offset, NULL, NULL, &error); 450 if (error) 451 break; 452 453 ptr = get_buffer_data(data_offset, &data_buffer, 0); 454 len = HAMMER_BUFSIZE - ((int)data_offset & HAMMER_BUFMASK); 455 if (len > data_len) 456 len = (int)data_len; 457 if (elm->leaf.base.rec_type == HAMMER_RECTYPE_INODE && 458 data_len == sizeof(struct hammer_inode_data)) { 459 crc = crc32_ext(ptr, HAMMER_INODE_CRCSIZE, crc); 460 } else { 461 crc = crc32_ext(ptr, len, crc); 462 } 463 data_len -= len; 464 data_offset += len; 465 } 466 if (data_buffer) 467 rel_buffer(data_buffer); 468 if (error) 469 return("BO"); /* bad offset */ 470 if (crc == elm->leaf.data_crc) 471 return(""); 472 return("BX"); /* bad crc */ 473 } 474 475 static 476 void 477 print_record(hammer_btree_elm_t elm) 478 { 479 struct buffer_info *data_buffer; 480 hammer_off_t data_offset; 481 int32_t data_len; 482 hammer_data_ondisk_t data; 483 484 data_offset = elm->leaf.data_offset; 485 data_len = elm->leaf.data_len; 486 data_buffer = NULL; 487 488 if (data_offset) 489 data = get_buffer_data(data_offset, &data_buffer, 0); 490 else 491 data = NULL; 492 493 switch(elm->leaf.base.rec_type) { 494 case HAMMER_RECTYPE_INODE: 495 printf("\n%17s", ""); 496 printf("size=%jd nlinks=%jd", 497 (intmax_t)data->inode.size, 498 (intmax_t)data->inode.nlinks); 499 if (QuietOpt < 1) { 500 printf(" mode=%05o uflags=%08x\n", 501 data->inode.mode, 502 data->inode.uflags); 503 printf("%17s", ""); 504 printf("ctime=%016jx pobjid=%016jx obj_type=%d\n", 505 (uintmax_t)data->inode.ctime, 506 (uintmax_t)data->inode.parent_obj_id, 507 data->inode.obj_type); 508 printf("%17s", ""); 509 printf("mtime=%016jx", (uintmax_t)data->inode.mtime); 510 printf(" caps=%02x", data->inode.cap_flags); 511 } 512 break; 513 case HAMMER_RECTYPE_DIRENTRY: 514 printf("\n%17s", ""); 515 data_len -= HAMMER_ENTRY_NAME_OFF; 516 printf("dir-entry ino=%016jx lo=%08x name=\"%*.*s\"", 517 (uintmax_t)data->entry.obj_id, 518 data->entry.localization, 519 data_len, data_len, data->entry.name); 520 break; 521 case HAMMER_RECTYPE_FIX: 522 switch(elm->leaf.base.key) { 523 case HAMMER_FIXKEY_SYMLINK: 524 data_len -= HAMMER_SYMLINK_NAME_OFF; 525 printf("\n%17s", ""); 526 printf("symlink=\"%*.*s\"", data_len, data_len, 527 data->symlink.name); 528 break; 529 default: 530 break; 531 } 532 break; 533 default: 534 break; 535 } 536 if (data_buffer) 537 rel_buffer(data_buffer); 538 } 539 540 /* 541 * Dump the UNDO FIFO 542 */ 543 void 544 hammer_cmd_show_undo(void) 545 { 546 struct volume_info *volume; 547 hammer_blockmap_t rootmap; 548 hammer_off_t scan_offset; 549 hammer_fifo_any_t head; 550 struct buffer_info *data_buffer = NULL; 551 552 volume = get_volume(RootVolNo); 553 rootmap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX]; 554 printf("Volume header UNDO %016jx-%016jx/%016jx\n", 555 (intmax_t)rootmap->first_offset, 556 (intmax_t)rootmap->next_offset, 557 (intmax_t)rootmap->alloc_offset); 558 printf("Undo map is %jdMB\n", 559 (intmax_t)((rootmap->alloc_offset & HAMMER_OFF_LONG_MASK) / 560 (1024 * 1024))); 561 scan_offset = HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0); 562 while (scan_offset < rootmap->alloc_offset) { 563 head = get_buffer_data(scan_offset, &data_buffer, 0); 564 printf("%016jx ", scan_offset); 565 566 switch(head->head.hdr_type) { 567 case HAMMER_HEAD_TYPE_PAD: 568 printf("PAD(%04x)\n", head->head.hdr_size); 569 break; 570 case HAMMER_HEAD_TYPE_DUMMY: 571 printf("DUMMY(%04x) seq=%08x\n", 572 head->head.hdr_size, head->head.hdr_seq); 573 break; 574 case HAMMER_HEAD_TYPE_UNDO: 575 printf("UNDO(%04x) seq=%08x " 576 "dataoff=%016jx bytes=%d\n", 577 head->head.hdr_size, head->head.hdr_seq, 578 (intmax_t)head->undo.undo_offset, 579 head->undo.undo_data_bytes); 580 break; 581 case HAMMER_HEAD_TYPE_REDO: 582 printf("REDO(%04x) seq=%08x flags=%08x " 583 "objid=%016jx logoff=%016jx bytes=%d\n", 584 head->head.hdr_size, head->head.hdr_seq, 585 head->redo.redo_flags, 586 (intmax_t)head->redo.redo_objid, 587 (intmax_t)head->redo.redo_offset, 588 head->redo.redo_data_bytes); 589 break; 590 default: 591 printf("UNKNOWN(%04x,%04x) seq=%08x\n", 592 head->head.hdr_type, 593 head->head.hdr_size, 594 head->head.hdr_seq); 595 break; 596 } 597 if ((head->head.hdr_size & HAMMER_HEAD_ALIGN_MASK) || 598 head->head.hdr_size == 0 || 599 head->head.hdr_size > HAMMER_UNDO_ALIGN - 600 ((u_int)scan_offset & HAMMER_UNDO_MASK)) { 601 printf("Illegal size field, skipping to " 602 "next boundary\n"); 603 scan_offset = (scan_offset + HAMMER_UNDO_MASK) & 604 ~HAMMER_UNDO_MASK64; 605 } else { 606 scan_offset += head->head.hdr_size; 607 } 608 } 609 if (data_buffer) 610 rel_buffer(data_buffer); 611 } 612