diff options
author | Nicholas D Steeves <nsteeves@gmail.com> | 2016-04-23 00:41:30 +0100 |
---|---|---|
committer | Nicholas D Steeves <nsteeves@gmail.com> | 2016-04-23 00:41:30 +0100 |
commit | cec572daccafa1e912cbed363df6f84687778c6f (patch) | |
tree | 7d99ab9f73d25c1ed8eaf6393f6374edf5316b03 /show-blocks |
btrfs-progs (4.4.1-1.1) unstable; urgency=medium
* Non-maintainer upload.
* New upstream release.
* Rename package to btrfs-progs (Closes: #780081)
* Update standards version to 3.9.7 (no changes needed).
* debian/control: Add "Breaks" per Gianfranco Costamagna's suggestion
* Change lintian override to reflect package rename
* Switch from using postinst and postrm to using triggers
per Christian Seiler's recommendation.
# imported from the archive
Diffstat (limited to 'show-blocks')
-rwxr-xr-x | show-blocks | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/show-blocks b/show-blocks new file mode 100755 index 00000000..0164be96 --- /dev/null +++ b/show-blocks @@ -0,0 +1,325 @@ +#!/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, random + +# numpy seems to override random() with something else. Instantiate our +# own here +randgen = random.Random() +randgen.seed(50) + +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): + + #14413824 8192 extent back ref root 5 gen 10 owner 282 num_refs 1 + 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]) + root = float(line[6]) + 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 + yield root + X = numpy.fromiter(iter(fh, delimiter, converters), dtype=float) + return X + +def run_debug_tree(device): + p = os.popen('btrfs-debug-tree -e ' + device) + data = loaddata(p) + return data + +def shapeit(X): + lines = len(X) / 4 + X.shape = (lines, 4) + +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) + +# record the color used for each root the first time we find it +root_colors = {} +# there are lots of good colormaps to choose from +# http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps +# +meta_cmap = get_cmap("gist_ncar") +data_done = False + +def plotone(a, xvals, yvals, owner, root, lines, labels): + global data_done + add_label = False + + if owner: + if options.meta_only: + return + color = "blue" + label = "Data" + if not data_done: + add_label = True + data_done = True + else: + if options.data_only: + return + if root not in root_colors: + color = meta_cmap(randgen.random()) + label = "Meta %d" % int(root) + root_colors[root] = (color, label) + add_label = True + else: + color, label = root_colors[root] + + plotlines = a.plot(xvals, yvals, 's', color=color, mfc=color, mec=color, + markersize=.23, label=label) + if add_label: + lines += plotlines + labels.append(label) + print "add label %s" % label + +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 + +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) +subplots_adjust(right=0.7) +datai = 0 +xvals = [] +yvals = [] +last_owner = 0 +last_root = 0 +lines = [] +labels = [] +while datai < datalen: + row = data[datai] + datai += 1 + byte = row[0] + size = row[1] + owner = row[2] + root = row[3] + + if owner <= 255: + owner = 0 + else: + owner = 1 + + if len(xvals) and (owner != last_owner or last_root != root): + plotone(a, xvals, yvals, last_owner, last_root, lines, labels) + 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 = owner + last_root = root + +if xvals: + plotone(a, xvals, yvals, last_owner, last_root, lines, labels) + +# 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') + +a.legend(lines, labels, loc=(1.05, 0.8), shadow=True, pad=0.1, 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() + |