3 # Copyright (C) 2007 Oracle. All rights reserved.
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public
7 # License v2 as published by the Free Software Foundation.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public
15 # License along with this program; if not, write to the
16 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 # Boston, MA 021110-1307, USA.
19 import sys
, os
, signal
, time
, commands
, tempfile
20 from optparse
import OptionParser
21 from matplotlib
import rcParams
22 from matplotlib
.font_manager
import fontManager
, FontProperties
25 rcParams
['numerix'] = 'numpy'
26 rcParams
['backend'] = 'Agg'
27 rcParams
['interactive'] = 'False'
32 callback for matplotlib to display an annotation when points are clicked on. The
33 point which is closest to the click and within xtol and ytol is identified.
35 Register this function like this:
38 af = AnnoteFinder(xdata, ydata, annotes)
39 connect('button_press_event', af)
42 def __init__(self
, axis
=None):
47 self
.drawnAnnotations
= {}
51 for k
in self
.drawnAnnotations
.keys():
52 self
.drawnAnnotations
[k
].set_visible(False)
54 def __call__(self
, event
):
62 if (self
.axis
is None) or (self
.axis
==event
.inaxes
):
63 self
.drawAnnote(event
.inaxes
, clickX
, clickY
)
65 def drawAnnote(self
, axis
, x
, y
):
67 Draw the annotation on the plot
69 if self
.drawnAnnotations
.has_key((x
,y
)):
70 markers
= self
.drawnAnnotations
[(x
,y
)]
71 markers
.set_visible(not markers
.get_visible())
74 t
= axis
.text(x
,y
, "(%3.2f, %3.2f)"%(x
,y
), bbox
=dict(facecolor
='red',
76 self
.drawnAnnotations
[(x
,y
)] = t
79 def loaddata(fh
,delimiter
=None, converters
=None):
81 def iter(fh
, delimiter
, converters
):
84 for i
,line
in enumerate(fh
):
85 line
= line
.split(' ')
86 start
= float(line
[0])
88 owner
= float(line
[10])
90 total_metadata
+= int(len)
92 total_data
+= int(len)
93 if start
< zoommin
or (zoommax
!= 0 and start
> zoommax
):
98 X
= numpy
.fromiter(iter(fh
, delimiter
, converters
), dtype
=float)
101 def run_debug_tree(device
):
102 p
= os
.popen('debug-tree -e ' + device
)
110 def line_picker(line
, mouseevent
):
111 if mouseevent
.xdata
is None: return False, dict()
112 print "%d %d\n", mouseevent
.xdata
, mouseevent
.ydata
116 byte
= byte
/ bytes_per_cell
117 yval
= floor(byte
/ num_cells
)
118 xval
= byte
% num_cells
119 return (xval
, yval
+ 1)
121 def plotone(a
, xvals
, yvals
, owner
):
126 if options
.meta_only
:
131 if options
.data_only
:
136 lines
= a
.plot(xvals
, yvals
, 's', color
=color
, mfc
=color
, mec
=color
,
137 markersize
=.23, label
=label
)
138 if owner
and not data_lines
:
140 elif not owner
and not meta_lines
:
149 mult
= 1024 * 1024 * 1024 * 1024
151 mult
= 1024 * 1024 * 1024
159 num
= int(s
[:-1]) * mult
167 vals
= options
.zoom
.split(':')
169 sys
.stderr
.write("warning: unable to parse zoom %s\n" % options
.zoom
)
171 zoommin
= parse_num(vals
[0])
172 zoommax
= parse_num(vals
[1])
173 return (zoommin
, zoommax
)
175 usage
= "usage: %prog [options]"
176 parser
= OptionParser(usage
=usage
)
177 parser
.add_option("-d", "--device", help="Btrfs device", default
="")
178 parser
.add_option("-i", "--input-file", help="debug-tree data", default
="")
179 parser
.add_option("-o", "--output", help="Output file", default
="blocks.png")
180 parser
.add_option("-z", "--zoom", help="Zoom", default
=None)
181 parser
.add_option("", "--data-only", help="Only print data blocks",
182 default
=False, action
="store_true")
183 parser
.add_option("", "--meta-only", help="Only print metadata blocks",
184 default
=False, action
="store_true")
186 (options
,args
) = parser
.parse_args()
188 if not options
.device
and not options
.input_file
:
192 zoommin
, zoommax
= parse_zoom()
199 data
= run_debug_tree(options
.device
)
200 elif options
.input_file
:
201 data
= loaddata(file(options
.input_file
))
204 # try to drop out the least common data points by creating
205 # a historgram of the sectors seen.
209 sectormax
= numpy
.max(sectors
)
212 total_cells
= num_cells
* num_cells
213 byte_range
= sectormax
- sectormin
214 bytes_per_cell
= byte_range
/ total_cells
216 f
= figure(figsize
=(8,6))
218 # Throughput goes at the botoom
224 while datai
< datalen
:
236 if len(xvals
) and owner
!= last
:
237 plotone(a
, xvals
, yvals
, last
)
243 byte
+= bytes_per_cell
244 cell
+= bytes_per_cell
251 plotone(a
, xvals
, yvals
, last
)
253 # make sure the final second goes on the x axes
256 ticks
= a
.get_yticks()
258 first_tick
= ticks
[1] * bytes_per_cell
* num_cells
259 if first_tick
> 1024 * 1024 * 1024 * 1024:
260 scale
= 1024 * 1024 * 1024 * 1024;
262 elif first_tick
> 1024 * 1024 * 1024:
263 scale
= 1024 * 1024 * 1024;
265 elif first_tick
> 1024 * 1024:
268 elif first_tick
> 1024:
275 ylabels
= [ str(int((x
* bytes_per_cell
* num_cells
) / scale
)) for x
in ticks
]
276 a
.set_yticklabels(ylabels
)
277 a
.set_ylabel('Disk offset (%s)' % scalestr
)
278 a
.set_xlim(0, num_cells
)
279 a
.set_title('Blocks')
288 labels
+= ["Metadata"]
290 a
.legend(lines
, labels
, loc
=(.9, 1.02), shadow
=True, pad
=0.5, numpoints
=1,
291 handletextsep
= 0.005,
294 prop
=FontProperties(size
='x-small') )
299 percent_meta
= (float(total_metadata
) / float(total_data
)) * 100
301 print "Total metadata bytes %d data %d ratio %.3f" % (total_metadata
,
302 total_data
, percent_meta
)
303 print "saving graph to %s" % options
.output
304 savefig(options
.output
, orientation
='landscape')