Add debug-tree -e to print all allocated extents, and show-blocks to graph them
authorChris Mason <chris.mason@oracle.com>
Tue, 4 Mar 2008 00:55:16 +0000 (19:55 -0500)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Tue, 4 Mar 2008 00:55:16 +0000 (19:55 -0500)
debug-tree.c
show-blocks [new file with mode: 0755]

index 368b8fb3a1e4efd4ad10fcc0f4b1dc434bf3fa6e..7a2d0d06535003c4c6b40237d7f4df46a57e59a1 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <uuid/uuid.h>
 #include "kerncompat.h"
 #include "radix-tree.h"
 #include "print-tree.h"
 #include "transaction.h"
 
-int main(int ac, char **av) {
+static int print_usage(void)
+{
+       fprintf(stderr, "usage: debug-tree [ -e ] device\n");
+       exit(1);
+}
+
+static void print_extent_leaf(struct btrfs_root *root, struct extent_buffer *l)
+{
+       int i;
+       struct btrfs_item *item;
+       struct btrfs_extent_ref *ref;
+       struct btrfs_key key;
+       static u64 last = 0;
+       static u64 last_len = 0;
+       u32 nr = btrfs_header_nritems(l);
+       u32 type;
+
+       for (i = 0 ; i < nr ; i++) {
+               item = btrfs_item_nr(l, i);
+               btrfs_item_key_to_cpu(l, &key, i);
+               type = btrfs_key_type(&key);
+               switch (type) {
+               case BTRFS_EXTENT_ITEM_KEY:
+                       last_len = key.offset;
+                       last = key.objectid;
+                       break;
+               case BTRFS_EXTENT_REF_KEY:
+                       ref = btrfs_item_ptr(l, i, struct btrfs_extent_ref);
+                       printf("%llu %llu extent back ref root %llu gen %llu "
+                              "owner %llu offset %llu\n",
+                              (unsigned long long)last,
+                              (unsigned long long)last_len,
+                              (unsigned long long)btrfs_ref_root(l, ref),
+                              (unsigned long long)btrfs_ref_generation(l, ref),
+                              (unsigned long long)btrfs_ref_objectid(l, ref),
+                              (unsigned long long)btrfs_ref_offset(l, ref));
+                       break;
+               };
+               fflush(stdout);
+       }
+}
+
+static void print_extents(struct btrfs_root *root, struct extent_buffer *eb)
+{
+       int i;
+       u32 nr;
+       u32 size;
+
+       if (!eb)
+               return;
+       if (btrfs_is_leaf(eb)) {
+               print_extent_leaf(root, eb);
+               return;
+       }
+       size = btrfs_level_size(root, btrfs_header_level(eb) - 1);
+       nr = btrfs_header_nritems(eb);
+       for (i = 0; i < nr; i++) {
+               struct extent_buffer *next = read_tree_block(root,
+                                            btrfs_node_blockptr(eb, i),
+                                            size);
+               if (btrfs_is_leaf(next) &&
+                   btrfs_header_level(eb) != 1)
+                       BUG();
+               if (btrfs_header_level(next) !=
+                       btrfs_header_level(eb) - 1)
+                       BUG();
+               print_extents(root, next);
+               free_extent_buffer(next);
+       }
+}
+
+int main(int ac, char **av)
+{
        struct btrfs_root *root;
        struct btrfs_path path;
        struct btrfs_key key;
@@ -36,20 +109,37 @@ int main(int ac, char **av) {
        char uuidbuf[37];
        int ret;
        int slot;
+       int extent_only = 0;
 
-       if (ac != 2) {
-               fprintf(stderr, "usage: %s device\n", av[0]);
-               exit(1);
-       }
        radix_tree_init();
-       root = open_ctree(av[1], 0);
+
+       while(1) {
+               int c;
+               c = getopt(ac, av, "e");
+               if (c < 0)
+                       break;
+               switch(c) {
+                       case 'e':
+                               extent_only = 1;
+                               break;
+                       default:
+                               print_usage();
+               }
+       }
+       ac = ac - optind;
+       if (ac != 1)
+               print_usage();
+
+       root = open_ctree(av[optind], 0);
        if (!root) {
-               fprintf(stderr, "unable to open %s\n", av[1]);
+               fprintf(stderr, "unable to open %s\n", av[optind]);
                exit(1);
        }
-       printf("root tree\n");
-       btrfs_print_tree(root->fs_info->tree_root,
-                        root->fs_info->tree_root->node);
+       if (!extent_only) {
+               printf("root tree\n");
+               btrfs_print_tree(root->fs_info->tree_root,
+                                root->fs_info->tree_root->node);
+       }
        btrfs_init_path(&path);
        key.offset = 0;
        key.objectid = 0;
@@ -71,6 +161,8 @@ int main(int ac, char **av) {
                if (btrfs_key_type(&found_key) == BTRFS_ROOT_ITEM_KEY) {
                        unsigned long offset;
                        struct extent_buffer *buf;
+                       int skip = extent_only;
+
                        offset = btrfs_item_ptr_offset(leaf, slot);
                        read_extent_buffer(leaf, &ri, offset, sizeof(ri));
                        buf = read_tree_block(root->fs_info->tree_root,
@@ -81,18 +173,27 @@ int main(int ac, char **av) {
                                printf("root ");
                                break;
                        case BTRFS_EXTENT_TREE_OBJECTID:
-                               printf("extent tree ");
+                               skip = 0;
+                               if (!extent_only)
+                                       printf("extent tree ");
                                break;
                        }
-                       printf("tree %llu %u %llu\n",
-                              (unsigned long long)found_key.objectid,
-                              found_key.type,
-                              (unsigned long long)found_key.offset);
-                       btrfs_print_tree(root, buf);
+                       if (!skip && !extent_only) {
+                               printf("tree %llu %u %llu\n",
+                                      (unsigned long long)found_key.objectid,
+                                      found_key.type,
+                                      (unsigned long long)found_key.offset);
+                               btrfs_print_tree(root, buf);
+                       } else if (extent_only && !skip) {
+                               print_extents(root, buf);
+                       }
                }
                path.slots[0]++;
        }
        btrfs_release_path(root, &path);
+       if (extent_only)
+               return 0;
+
        printf("total bytes %llu\n",
               (unsigned long long)btrfs_super_total_bytes(&root->fs_info->super_copy));
        printf("bytes used %llu\n",
diff --git a/show-blocks b/show-blocks
new file mode 100755 (executable)
index 0000000..8db4c0b
--- /dev/null
@@ -0,0 +1,306 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 Oracle.  All rights reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public
+# License v2 as published by the Free Software Foundation.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 021110-1307, USA.
+#
+import sys, os, signal, time, commands, tempfile
+from optparse import OptionParser
+from matplotlib import rcParams
+from matplotlib.font_manager import fontManager, FontProperties
+import numpy
+
+rcParams['numerix'] = 'numpy'
+rcParams['backend'] = 'Agg'
+rcParams['interactive'] = 'False'
+from pylab import *
+
+class AnnoteFinder:
+  """
+  callback for matplotlib to display an annotation when points are clicked on.  The
+  point which is closest to the click and within xtol and ytol is identified.
+    
+  Register this function like this:
+    
+  scatter(xdata, ydata)
+  af = AnnoteFinder(xdata, ydata, annotes)
+  connect('button_press_event', af)
+  """
+
+  def __init__(self, axis=None):
+    if axis is None:
+      self.axis = gca()
+    else:
+      self.axis= axis
+    self.drawnAnnotations = {}
+    self.links = []
+    
+  def clear(self):
+    for k in self.drawnAnnotations.keys():
+        self.drawnAnnotations[k].set_visible(False)
+
+  def __call__(self, event):
+    if event.inaxes:
+      if event.button != 1:
+        self.clear()
+        draw()
+        return
+      clickX = event.xdata
+      clickY = event.ydata
+      if (self.axis is None) or (self.axis==event.inaxes):
+        self.drawAnnote(event.inaxes, clickX, clickY)
+    
+  def drawAnnote(self, axis, x, y):
+    """
+    Draw the annotation on the plot
+    """
+    if self.drawnAnnotations.has_key((x,y)):
+      markers = self.drawnAnnotations[(x,y)]
+      markers.set_visible(not markers.get_visible())
+      draw()
+    else:
+      t = axis.text(x,y, "(%3.2f, %3.2f)"%(x,y), bbox=dict(facecolor='red',
+                    alpha=0.8))
+      self.drawnAnnotations[(x,y)] = t
+      draw()
+
+def loaddata(fh,delimiter=None, converters=None):
+
+    def iter(fh, delimiter, converters):
+        global total_data
+        global total_metadata
+        for i,line in enumerate(fh):
+            line = line.split(' ')
+            start = float(line[0])
+            len = float(line[1])
+            owner = float(line[10])
+            if owner <= 255:
+                total_metadata += int(len)
+            else:
+                total_data += int(len)
+            if start < zoommin or (zoommax != 0 and start > zoommax):
+                continue
+            yield start
+            yield len
+            yield owner
+    X = numpy.fromiter(iter(fh, delimiter, converters), dtype=float)
+    return X
+
+def run_debug_tree(device):
+    p = os.popen('debug-tree -e ' + device)
+    data = loaddata(p)
+    return data
+
+def shapeit(X):
+    lines = len(X) / 3
+    X.shape = (lines, 3)
+
+def line_picker(line, mouseevent):
+    if mouseevent.xdata is None: return False, dict()
+    print "%d %d\n", mouseevent.xdata, mouseevent.ydata
+    return False, dict()
+
+def xycalc(byte):
+    byte = byte / bytes_per_cell
+    yval = floor(byte / num_cells)
+    xval = byte % num_cells
+    return (xval, yval + 1)
+
+def plotone(a, xvals, yvals, owner):
+    global data_lines
+    global meta_lines
+
+    if owner:
+        if options.meta_only:
+            return
+        color = "blue"
+        label = "Data"
+    else:
+        if options.data_only:
+            return
+        color = "green"
+        label = "Metadata"
+
+    lines = a.plot(xvals, yvals, 's', color=color, mfc=color, mec=color,
+           markersize=.23, label=label)
+    if owner and not data_lines:
+        data_lines = lines
+    elif not owner and not meta_lines:
+        meta_lines = lines
+
+
+def parse_zoom():
+    def parse_num(s):
+        mult = 1
+        c = s.lower()[-1]
+        if c == 't':
+            mult = 1024 * 1024 * 1024 * 1024
+        elif c == 'g':
+            mult = 1024 * 1024 * 1024
+        elif c == 'm':
+            mult = 1024 * 1024
+        elif c == 'k':
+            mult = 1024
+        else:
+            c = None
+        if c:
+            num = int(s[:-1]) * mult
+        else:
+            num = int(s)
+        return num
+        
+    if not options.zoom:
+        return (0, 0)
+
+    vals = options.zoom.split(':')
+    if len(vals) != 2:
+        sys.stderr.write("warning: unable to parse zoom %s\n" % options.zoom)
+        return (0, 0)
+    zoommin = parse_num(vals[0])
+    zoommax = parse_num(vals[1])
+    return (zoommin, zoommax)
+
+usage = "usage: %prog [options]"
+parser = OptionParser(usage=usage)
+parser.add_option("-d", "--device", help="Btrfs device", default="")
+parser.add_option("-i", "--input-file", help="debug-tree data", default="")
+parser.add_option("-o", "--output", help="Output file", default="blocks.png")
+parser.add_option("-z", "--zoom", help="Zoom", default=None)
+parser.add_option("", "--data-only", help="Only print data blocks",
+                  default=False, action="store_true")
+parser.add_option("", "--meta-only", help="Only print metadata blocks",
+                  default=False, action="store_true")
+
+(options,args) = parser.parse_args()
+
+if not options.device and not options.input_file:
+    parser.print_help()
+    sys.exit(1)
+
+zoommin, zoommax = parse_zoom()
+total_data = 0
+total_metadata = 0
+data_lines = []
+meta_lines = []
+
+if options.device:
+    data = run_debug_tree(options.device)
+elif options.input_file:
+    data = loaddata(file(options.input_file))
+shapeit(data)
+
+# try to drop out the least common data points by creating
+# a historgram of the sectors seen.
+sectors = data[:,0]
+sizes = data[:,1]
+datalen = len(data)
+sectormax = numpy.max(sectors)
+sectormin = 0
+num_cells = 800
+total_cells = num_cells * num_cells
+byte_range = sectormax - sectormin
+bytes_per_cell = byte_range / total_cells
+
+f = figure(figsize=(8,6))
+
+# Throughput goes at the botoom
+a = subplot(1, 1, 1)
+datai = 0
+xvals = []
+yvals = []
+last = 0
+while datai < datalen:
+    row = data[datai]
+    datai += 1
+    byte = row[0]
+    size = row[1]
+    owner = row[2]
+
+    if owner <= 255:
+        owner = 0
+    else:
+        owner = 1
+
+    if len(xvals) and owner != last:
+        plotone(a, xvals, yvals, last)
+        xvals = []
+        yvals = []
+    cell = 0
+    while cell < size:
+        xy = xycalc(byte)
+        byte += bytes_per_cell
+        cell += bytes_per_cell
+        if xy:
+            xvals.append(xy[0])
+            yvals.append(xy[1])
+    last = owner
+
+if xvals:
+    plotone(a, xvals, yvals, last)
+
+# make sure the final second goes on the x axes
+ticks = []
+a.set_xticks(ticks)
+ticks = a.get_yticks()
+
+first_tick = ticks[1] * bytes_per_cell * num_cells
+if first_tick > 1024 * 1024 * 1024 * 1024:
+    scale = 1024 * 1024 * 1024 * 1024;
+    scalestr = "TB"
+elif first_tick > 1024 * 1024 * 1024:
+    scale = 1024 * 1024 * 1024;
+    scalestr = "GB"
+elif first_tick > 1024 * 1024:
+    scale = 1024 * 1024;
+    scalestr = "MB"
+elif first_tick > 1024:
+    scale = 1024;
+    scalestr = "KB"
+else:
+    scalestr = "Bytes"
+    scale = 1
+
+ylabels = [ str(int((x * bytes_per_cell * num_cells) / scale)) for x in ticks ]
+a.set_yticklabels(ylabels)
+a.set_ylabel('Disk offset (%s)' % scalestr)
+a.set_xlim(0, num_cells)
+a.set_title('Blocks')
+
+lines = []
+labels = []
+if data_lines:
+    lines += data_lines
+    labels += ["Data"]
+if meta_lines:
+    lines += meta_lines
+    labels += ["Metadata"]
+
+a.legend(lines, labels, loc=(.9, 1.02), shadow=True, pad=0.5, numpoints=1,
+              handletextsep = 0.005,
+              labelsep = 0.01,
+              markerscale=10,
+              prop=FontProperties(size='x-small') )
+
+if total_data == 0:
+    percent_meta = 100
+else:
+    percent_meta = (float(total_metadata) / float(total_data)) * 100
+
+print "Total metadata bytes %d data %d ratio %.3f" % (total_metadata,
+                                                    total_data, percent_meta)
+print "saving graph to %s" % options.output
+savefig(options.output, orientation='landscape')
+show()
+