mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/kdave/btrfs-progs.git
synced 2024-11-15 00:04:23 +08:00
Add debug-tree -e to print all allocated extents, and show-blocks to graph them
This commit is contained in:
parent
6dfdeac7ec
commit
79599947bf
133
debug-tree.c
133
debug-tree.c
@ -18,6 +18,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include "kerncompat.h"
|
||||
#include "radix-tree.h"
|
||||
@ -26,7 +27,79 @@
|
||||
#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",
|
||||
|
306
show-blocks
Executable file
306
show-blocks
Executable file
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user