Merge remote branch 'crater/master' into net80211-update
[dragonfly.git] / sbin / hammer / cmd_show.c
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         if (node_offset == (hammer_off_t)-1) {
75                 volume = get_volume(RootVolNo);
76                 node_offset = volume->ondisk->vol0_btree_root;
77                 if (QuietOpt < 3) {
78                         printf("Volume header\trecords=%jd next_tid=%016jx\n",
79                                (intmax_t)volume->ondisk->vol0_stat_records,
80                                (uintmax_t)volume->ondisk->vol0_next_tid);
81                         printf("\t\tbufoffset=%016jx\n",
82                                (uintmax_t)volume->ondisk->vol_buf_beg);
83                         for (zone = 0; zone < HAMMER_MAX_ZONES; ++zone) {
84                                 printf("\t\tzone %d\tnext_offset=%016jx\n",
85                                         zone,
86                                         (uintmax_t)volume->ondisk->vol0_blockmap[zone].next_offset
87                                 );
88                         }
89                 }
90                 rel_volume(volume);
91         }
92
93         if (lo == 0 && obj_id == (int64_t)HAMMER_MIN_OBJID) {
94                 searchp = NULL;
95                 printf("show %016jx depth %d\n", (uintmax_t)node_offset, depth);
96         } else {
97                 search.lo = lo;
98                 search.obj_id = obj_id;
99                 searchp = &search;
100                 printf("show %016jx lo %08x obj_id %016jx depth %d\n",
101                         (uintmax_t)node_offset, lo, (uintmax_t)obj_id, depth);
102         }
103         print_btree_node(node_offset, searchp, depth, 0, HAMMER_MAX_TID,
104                          left_bound, right_bound);
105         print_btree_node(node_offset, searchp, depth, 1, HAMMER_MAX_TID,
106                          left_bound, right_bound);
107 }
108
109 static void
110 print_btree_node(hammer_off_t node_offset, btree_search_t search,
111                 int depth, int spike, hammer_tid_t mirror_tid,
112                 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
113 {
114         struct buffer_info *buffer = NULL;
115         hammer_node_ondisk_t node;
116         hammer_btree_elm_t elm;
117         int i;
118         int flags;
119         int maxcount;
120         char badc;
121         char badm;
122         const char *ext;
123
124         node = get_node(node_offset, &buffer);
125
126         if (crc32(&node->crc + 1, HAMMER_BTREE_CRCSIZE) == node->crc)
127                 badc = ' ';
128         else
129                 badc = 'B';
130
131         if (node->mirror_tid <= mirror_tid) {
132                 badm = ' ';
133         } else {
134                 badm = 'M';
135                 badc = 'B';
136         }
137
138         if (spike == 0) {
139                 printf("%c%c   NODE %016jx cnt=%02d p=%016jx "
140                        "type=%c depth=%d",
141                        badc,
142                        badm,
143                        (uintmax_t)node_offset, node->count,
144                        (uintmax_t)node->parent,
145                        (node->type ? node->type : '?'), depth);
146                 printf(" mirror %016jx", (uintmax_t)node->mirror_tid);
147                 if (QuietOpt < 3) {
148                         printf(" fill=");
149                         print_bigblock_fill(node_offset);
150                 }
151                 printf(" {\n");
152
153                 maxcount = (node->type == HAMMER_BTREE_TYPE_INTERNAL) ?
154                            HAMMER_BTREE_INT_ELMS : HAMMER_BTREE_LEAF_ELMS;
155
156                 for (i = 0; i < node->count && i < maxcount; ++i) {
157                         elm = &node->elms[i];
158
159                         if (node->type != HAMMER_BTREE_TYPE_INTERNAL) {
160                                 ext = NULL;
161                                 if (search &&
162                                     elm->base.localization == search->lo &&
163                                      elm->base.obj_id == search->obj_id) {
164                                         ext = " *";
165                                 }
166                         } else if (search) {
167                                 ext = " *";
168                                 if (elm->base.localization > search->lo ||
169                                     (elm->base.localization == search->lo &&
170                                      elm->base.obj_id > search->obj_id)) {
171                                         ext = NULL;
172                                 }
173                                 if (elm[1].base.localization < search->lo ||
174                                     (elm[1].base.localization == search->lo &&
175                                      elm[1].base.obj_id < search->obj_id)) {
176                                         ext = NULL;
177                                 }
178                         } else {
179                                 ext = NULL;
180                         }
181
182                         flags = print_elm_flags(node, node_offset,
183                                                 elm, elm->base.btype,
184                                                 left_bound, right_bound);
185                         print_btree_elm(elm, i, node->type, flags, "ELM", ext);
186                 }
187                 if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
188                         elm = &node->elms[i];
189
190                         flags = print_elm_flags(node, node_offset,
191                                                 elm, 'I',
192                                                 left_bound, right_bound);
193                         print_btree_elm(elm, i, node->type, flags, "RBN", NULL);
194                 }
195                 printf("    }\n");
196         }
197
198         for (i = 0; i < node->count; ++i) {
199                 elm = &node->elms[i];
200
201                 switch(node->type) {
202                 case HAMMER_BTREE_TYPE_INTERNAL:
203                         if (search) {
204                                 if (elm->base.localization > search->lo ||
205                                     (elm->base.localization == search->lo &&
206                                      elm->base.obj_id > search->obj_id)) {
207                                         break;
208                                 }
209                                 if (elm[1].base.localization < search->lo ||
210                                     (elm[1].base.localization == search->lo &&
211                                      elm[1].base.obj_id < search->obj_id)) {
212                                         break;
213                                 }
214                         }
215                         if (elm->internal.subtree_offset) {
216                                 print_btree_node(elm->internal.subtree_offset,
217                                                  search, depth + 1, spike,
218                                                  elm->internal.mirror_tid,
219                                                  &elm[0].base, &elm[1].base);
220                                 /*
221                                  * Cause show to iterate after seeking to
222                                  * the lo:objid
223                                  */
224                                 search = NULL;
225                         }
226                         break;
227                 default:
228                         break;
229                 }
230         }
231         rel_buffer(buffer);
232 }
233
234 static
235 void
236 print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
237                 int flags, const char *label, const char *ext)
238 {
239         char flagstr[8] = { 0, '-', '-', '-', '-', '-', '-', 0 };
240
241         flagstr[0] = flags ? 'B' : 'G';
242         if (flags & FLAG_TOOFARLEFT)
243                 flagstr[2] = 'L';
244         if (flags & FLAG_TOOFARRIGHT)
245                 flagstr[3] = 'R';
246         if (flags & FLAG_BADTYPE)
247                 flagstr[4] = 'T';
248         if (flags & FLAG_BADCHILDPARENT)
249                 flagstr[5] = 'C';
250         if (flags & FLAG_BADMIRRORTID)
251                 flagstr[6] = 'M';
252
253         printf("%s\t%s %2d %c ",
254                flagstr, label, i,
255                (elm->base.btype ? elm->base.btype : '?'));
256         printf("obj=%016jx key=%016jx lo=%08x rt=%02x ot=%02x\n",
257                (uintmax_t)elm->base.obj_id,
258                (uintmax_t)elm->base.key,
259                elm->base.localization,
260                elm->base.rec_type,
261                elm->base.obj_type);
262         printf("\t       %c tids %016jx:%016jx ",
263                 (elm->base.delete_tid ? 'd' : ' '),
264                (uintmax_t)elm->base.create_tid,
265                (uintmax_t)elm->base.delete_tid);
266
267         switch(type) {
268         case HAMMER_BTREE_TYPE_INTERNAL:
269                 printf("suboff=%016jx",
270                        (uintmax_t)elm->internal.subtree_offset);
271                 if (QuietOpt < 3) {
272                         printf(" mirror %016jx",
273                                (uintmax_t)elm->internal.mirror_tid);
274                 }
275                 if (ext)
276                         printf(" %s", ext);
277                 break;
278         case HAMMER_BTREE_TYPE_LEAF:
279                 if (ext)
280                         printf(" %s", ext);
281                 switch(elm->base.btype) {
282                 case HAMMER_BTREE_TYPE_RECORD:
283                         if (QuietOpt < 3)
284                                 printf("\n%s\t         ", check_data_crc(elm));
285                         else
286                                 printf("\n\t         ");
287                         printf("dataoff=%016jx/%d",
288                                (uintmax_t)elm->leaf.data_offset,
289                                elm->leaf.data_len);
290                         if (QuietOpt < 3) {
291                                 printf(" crc=%04x", elm->leaf.data_crc);
292                                 printf("\n\t         fills=");
293                                 print_bigblock_fill(elm->leaf.data_offset);
294                         }
295                         if (QuietOpt < 2)
296                                 print_record(elm);
297                         break;
298                 }
299                 break;
300         default:
301                 break;
302         }
303         printf("\n");
304 }
305
306 static
307 int
308 print_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset,
309                 hammer_btree_elm_t elm, u_int8_t btype,
310                 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
311 {
312         int flags = 0;
313
314         switch(node->type) {
315         case HAMMER_BTREE_TYPE_INTERNAL:
316                 if (elm->internal.subtree_offset) {
317                         struct buffer_info *buffer = NULL;
318                         hammer_node_ondisk_t subnode;
319
320                         subnode = get_node(elm->internal.subtree_offset,
321                                            &buffer);
322                         if (subnode->parent != node_offset)
323                                 flags |= FLAG_BADCHILDPARENT;
324                         rel_buffer(buffer);
325                 }
326                 if (elm->internal.mirror_tid > node->mirror_tid)
327                         flags |= FLAG_BADMIRRORTID;
328
329                 switch(btype) {
330                 case HAMMER_BTREE_TYPE_INTERNAL:
331                         if (left_bound == NULL || right_bound == NULL)
332                                 break;
333                         if (hammer_btree_cmp(&elm->base, left_bound) < 0)
334                                 flags |= FLAG_TOOFARLEFT;
335                         if (hammer_btree_cmp(&elm->base, right_bound) > 0)
336                                 flags |= FLAG_TOOFARRIGHT;
337                         break;
338                 case HAMMER_BTREE_TYPE_LEAF:
339                         if (left_bound == NULL || right_bound == NULL)
340                                 break;
341                         if (hammer_btree_cmp(&elm->base, left_bound) < 0)
342                                 flags |= FLAG_TOOFARLEFT;
343                         if (hammer_btree_cmp(&elm->base, right_bound) >= 0)
344                                 flags |= FLAG_TOOFARRIGHT;
345                         break;
346                 default:
347                         flags |= FLAG_BADTYPE;
348                         break;
349                 }
350                 break;
351         case HAMMER_BTREE_TYPE_LEAF:
352                 if (elm->base.create_tid &&
353                     elm->base.create_tid > node->mirror_tid) {
354                         flags |= FLAG_BADMIRRORTID;
355                 }
356                 if (elm->base.delete_tid &&
357                     elm->base.delete_tid > node->mirror_tid) {
358                         flags |= FLAG_BADMIRRORTID;
359                 }
360                 switch(btype) {
361                 case HAMMER_BTREE_TYPE_RECORD:
362                         if (left_bound == NULL || right_bound == NULL)
363                                 break;
364                         if (hammer_btree_cmp(&elm->base, left_bound) < 0)
365                                 flags |= FLAG_TOOFARLEFT;
366                         if (hammer_btree_cmp(&elm->base, right_bound) >= 0)
367                                 flags |= FLAG_TOOFARRIGHT;
368                         break;
369                 default:
370                         flags |= FLAG_BADTYPE;
371                         break;
372                 }
373                 break;
374         default:
375                 flags |= FLAG_BADTYPE;
376                 break;
377         }
378         return(flags);
379 }
380
381 static
382 void
383 print_bigblock_fill(hammer_off_t offset)
384 {
385         struct hammer_blockmap_layer1 layer1;
386         struct hammer_blockmap_layer2 layer2;
387         int fill;
388
389         blockmap_lookup(offset, &layer1, &layer2);
390         fill = layer2.bytes_free * 100 / HAMMER_LARGEBLOCK_SIZE;
391         fill = 100 - fill;
392
393         printf("z%d:%lld=%d%%",
394                 HAMMER_ZONE_DECODE(offset),
395                 (offset & ~HAMMER_OFF_ZONE_MASK) / HAMMER_LARGEBLOCK_SIZE,
396                 fill
397         );
398 }
399
400 /*
401  * Check the generic crc on a data element.  Inodes record types are
402  * special in that some of their fields are not CRCed.
403  */
404 static
405 const char *
406 check_data_crc(hammer_btree_elm_t elm)
407 {
408         struct buffer_info *data_buffer;
409         hammer_off_t data_offset;
410         int32_t data_len;
411         int32_t len;
412         u_int32_t crc;
413         char *ptr;
414
415         data_offset = elm->leaf.data_offset;
416         data_len = elm->leaf.data_len;
417         data_buffer = NULL;
418         if (data_offset == 0 || data_len == 0)
419                 return("Z");
420
421         crc = 0;
422         while (data_len) {
423                 ptr = get_buffer_data(data_offset, &data_buffer, 0);
424                 len = HAMMER_BUFSIZE - ((int)data_offset & HAMMER_BUFMASK);
425                 if (len > data_len)
426                         len = (int)data_len;
427                 if (elm->leaf.base.rec_type == HAMMER_RECTYPE_INODE &&
428                     data_len == sizeof(struct hammer_inode_data)) {
429                         crc = crc32_ext(ptr, HAMMER_INODE_CRCSIZE, crc);
430                 } else {
431                         crc = crc32_ext(ptr, len, crc);
432                 }
433                 data_len -= len;
434                 data_offset += len;
435         }
436         if (data_buffer)
437                 rel_buffer(data_buffer);
438         if (crc == elm->leaf.data_crc)
439                 return("");
440         return("B");
441 }
442
443 static
444 void
445 print_record(hammer_btree_elm_t elm)
446 {
447         struct buffer_info *data_buffer;
448         hammer_off_t data_offset;
449         int32_t data_len;
450         hammer_data_ondisk_t data;
451
452         data_offset = elm->leaf.data_offset;
453         data_len = elm->leaf.data_len;
454         data_buffer = NULL;
455
456         if (data_offset)
457                 data = get_buffer_data(data_offset, &data_buffer, 0);
458         else
459                 data = NULL;
460
461         switch(elm->leaf.base.rec_type) {
462         case HAMMER_RECTYPE_INODE:
463                 printf("\n%17s", "");
464                 printf("size=%jd nlinks=%jd",
465                        (intmax_t)data->inode.size,
466                        (intmax_t)data->inode.nlinks);
467                 if (QuietOpt < 1) {
468                         printf(" mode=%05o uflags=%08x\n",
469                                 data->inode.mode,
470                                 data->inode.uflags);
471                         printf("%17s", "");
472                         printf("ctime=%016jx pobjid=%016jx obj_type=%d\n",
473                                 (uintmax_t)data->inode.ctime,
474                                 (uintmax_t)data->inode.parent_obj_id,
475                                 data->inode.obj_type);
476                         printf("%17s", "");
477                         printf("mtime=%016jx", (uintmax_t)data->inode.mtime);
478                         printf(" caps=%02x", data->inode.cap_flags);
479                 }
480                 break;
481         case HAMMER_RECTYPE_DIRENTRY:
482                 printf("\n%17s", "");
483                 data_len -= HAMMER_ENTRY_NAME_OFF;
484                 printf("dir-entry ino=%016jx lo=%08x name=\"%*.*s\"",
485                        (uintmax_t)data->entry.obj_id,
486                        data->entry.localization,
487                        data_len, data_len, data->entry.name);
488                 break;
489         case HAMMER_RECTYPE_FIX:
490                 switch(elm->leaf.base.key) {
491                 case HAMMER_FIXKEY_SYMLINK:
492                         data_len -= HAMMER_SYMLINK_NAME_OFF;
493                         printf("\n%17s", "");
494                         printf("symlink=\"%*.*s\"", data_len, data_len,
495                                 data->symlink.name);
496                         break;
497                 default:
498                         break;
499                 }
500                 break;
501         default:
502                 break;
503         }
504         if (data_buffer)
505                 rel_buffer(data_buffer);
506 }
507
508 /*
509  * Dump the UNDO FIFO
510  */
511 void
512 hammer_cmd_show_undo(void)
513 {
514         struct volume_info *volume;
515         hammer_blockmap_t rootmap;
516         hammer_off_t scan_offset;
517         hammer_fifo_any_t head;
518         struct buffer_info *data_buffer = NULL;
519
520         volume = get_volume(RootVolNo);
521         rootmap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
522         printf("Volume header UNDO %016jx-%016jx/%016jx\n",
523                 (intmax_t)rootmap->first_offset,
524                 (intmax_t)rootmap->next_offset,
525                 (intmax_t)rootmap->alloc_offset);
526         printf("Undo map is %jdMB\n",
527                 (intmax_t)((rootmap->alloc_offset & HAMMER_OFF_LONG_MASK) /
528                            (1024 * 1024)));
529         scan_offset = HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0);
530         while (scan_offset < rootmap->alloc_offset) {
531                 head = get_buffer_data(scan_offset, &data_buffer, 0);
532                 printf("%016jx ", scan_offset);
533
534                 switch(head->head.hdr_type) {
535                 case HAMMER_HEAD_TYPE_PAD:
536                         printf("PAD(%04x)\n", head->head.hdr_size);
537                         break;
538                 case HAMMER_HEAD_TYPE_DUMMY:
539                         printf("DUMMY(%04x) seq=%08x\n",
540                                 head->head.hdr_size, head->head.hdr_seq);
541                         break;
542                 case HAMMER_HEAD_TYPE_UNDO:
543                         printf("UNDO(%04x) seq=%08x "
544                                "dataoff=%016jx bytes=%d\n",
545                                 head->head.hdr_size, head->head.hdr_seq,
546                                 (intmax_t)head->undo.undo_offset,
547                                 head->undo.undo_data_bytes);
548                         break;
549                 case HAMMER_HEAD_TYPE_REDO:
550                         printf("REDO(%04x) seq=%08x flags=%08x "
551                                "objid=%016jx logoff=%016jx bytes=%d\n",
552                                 head->head.hdr_size, head->head.hdr_seq,
553                                 head->redo.redo_flags,
554                                 (intmax_t)head->redo.redo_objid,
555                                 (intmax_t)head->redo.redo_offset,
556                                 head->redo.redo_data_bytes);
557                         break;
558                 default:
559                         printf("UNKNOWN(%04x,%04x) seq=%08x\n",
560                                 head->head.hdr_type,
561                                 head->head.hdr_size,
562                                 head->head.hdr_seq);
563                         break;
564                 }
565                 if ((head->head.hdr_size & HAMMER_HEAD_ALIGN_MASK) ||
566                     head->head.hdr_size == 0 ||
567                     head->head.hdr_size > HAMMER_UNDO_ALIGN -
568                                     ((u_int)scan_offset & HAMMER_UNDO_MASK)) {
569                         printf("Illegal size field, skipping to "
570                                "next boundary\n");
571                         scan_offset = (scan_offset + HAMMER_UNDO_MASK) &
572                                         ~HAMMER_UNDO_MASK64;
573                 } else {
574                         scan_offset += head->head.hdr_size;
575                 }
576         }
577         if (data_buffer)
578                 rel_buffer(data_buffer);
579 }