summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/wt_nvd3_util.py46
-rw-r--r--tools/wtperf_graph.py35
-rw-r--r--tools/wtperf_stats.py161
-rw-r--r--tools/wtstats.py42
4 files changed, 250 insertions, 34 deletions
diff --git a/tools/wt_nvd3_util.py b/tools/wt_nvd3_util.py
new file mode 100644
index 00000000000..6bf1396b0ff
--- /dev/null
+++ b/tools/wt_nvd3_util.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Public Domain 2008-2014 WiredTiger, Inc.
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+from datetime import datetime
+from nvd3 import lineChart
+
+# Add a multiChart type so we can overlay line graphs
+class multiChart(lineChart):
+ def __init__(self, **kwargs):
+ lineChart.__init__(self, **kwargs)
+
+ # Fix the axes
+ del self.axislist['yAxis']
+ self.create_y_axis('yAxis1', format=kwargs.get('y_axis_format', '.02f'))
+ self.create_y_axis('yAxis2', format=kwargs.get('y_axis_format', '.02f'))
+
+TIMEFMT = "%b %d %H:%M:%S"
+
+thisyear = datetime.today().year
+def parsetime(s):
+ return datetime.strptime(s, TIMEFMT).replace(year=thisyear)
+
diff --git a/tools/wtperf_graph.py b/tools/wtperf_graph.py
index fb925ef28b9..1f50b6bafa1 100644
--- a/tools/wtperf_graph.py
+++ b/tools/wtperf_graph.py
@@ -33,11 +33,13 @@ from subprocess import call
TIMEFMT = "%b %d %H:%M:%S"
-def process_monitor(fname, ckptlist, opdict):
+def process_monitor(fname, sfx, ckptlist, opdict):
# Read the monitor file and figure out when a checkpoint was running.
in_ckpt = 'N'
ckptlist=[]
+
+ ofname = 'monitor%s.png' % (sfx)
# Monitor output format currently is:
# time,totalsec,read,insert,update,ckpt,...latencies...
ops = ('read', 'insert', 'update')
@@ -45,6 +47,8 @@ def process_monitor(fname, ckptlist, opdict):
with open(fname, 'r') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
+ if row[0].lstrip().startswith('#'):
+ continue
# Look for checkpoints and operations.
if row[5] != in_ckpt:
ckptlist.append(row[0])
@@ -77,13 +81,14 @@ set yrange [0:]\n''' % {
})
it = iter(ckptlist)
for start, stop in zip(it, it):
- of.write('set object rectangle from first \'' + start +\
- '\', graph 0 ' + ' to first \'' + stop +\
- '\', graph 1 fc rgb "gray" back\n')
- of.write('set output "' + fname + '.png"\n')
- of.write('plot "' + fname + '" using 1:($3/1000) title "Reads", "' +\
- fname + '" using 1:($4/1000) title "Inserts", "' +\
- fname + '" using 1:($5/1000) title "Updates"\n')
+ of.write("set object rectangle from first '%i',\
+ graph 0 to first '%i',\
+ graph 1 fc rgb \"gray\" back\n" % (start, stop))
+ of.write('set output "%s"\n' % (ofname))
+ of.write("""plot "{name}" every ::1 using 1:($3/1000) title "Reads", \\
+ "{name}" every ::1 using 1:($4/1000) title "Inserts",\\
+ "{name}" every ::1 using 1:($5/1000) title "Updates"
+ """.format(name=fname))
of.close()
call(["gnuplot", gcmd])
os.remove(gcmd)
@@ -117,10 +122,12 @@ set yrange [1:]\n''' % {
'\', graph 1 fc rgb "gray" back\n')
ofname = name + sfx + '.latency1.png'
of.write('set output "' + ofname + '"\n')
- of.write('plot "' + fname + '" using 1:($' + repr(col_avg) +\
- ') title "Average Latency", "' + fname +'" using 1:($' +\
+ of.write('plot "' +\
+ fname + '" every ::1 using 1:($' + repr(col_avg) +\
+ ') title "Average Latency", "' + fname +'" every ::1 using 1:($' +\
repr(col_min) + ') title "Minimum Latency", "' +\
- fname + '" using 1:($' + repr(col_max) + ') title "Maximum Latency"\n')
+ fname + '" every ::1 using 1:($' + repr(col_max) +\
+ ') title "Maximum Latency"\n')
of.close()
call(["gnuplot", gcmd])
os.remove(gcmd)
@@ -150,7 +157,7 @@ set yrange [0:]\n''')
ofname = name + sfx + '.latency2.png'
of.write('set output "' + ofname + '"\n')
of.write('plot "' + lfile + sfx +\
- '" using (($2 * 100)/$4) title "' + name + '"\n')
+ '" every ::1 using (($2 * 100)/$4) title "' + name + '"\n')
of.close()
call(["gnuplot", gcmd])
os.remove(gcmd)
@@ -182,7 +189,7 @@ set yrange [0:]\n''' % {
ofname = name + sfx + '.latency3.png'
of.write('set output "' + ofname + '"\n')
of.write('plot "' + lfile + sfx +\
- '" using 1:(($3 * 100)/$4) title "' + name + '"\n')
+ '" every ::1 using 1:(($3 * 100)/$4) title "' + name + '"\n')
of.close()
call(["gnuplot", gcmd])
os.remove(gcmd)
@@ -192,13 +199,13 @@ def process_file(fname):
# NOTE: The operations below must be in this exact order to match
# the operation latency output in the monitor file.
opdict={'read':0, 'insert':0, 'update':0}
- process_monitor(fname, ckptlist, opdict)
# This assumes the monitor file has the string "monitor"
# and any other (optional) characters in the filename are a suffix.
sfx = os.path.basename(fname).replace('monitor','')
dirname = os.path.dirname(fname)
+ process_monitor(fname, sfx, ckptlist, opdict)
column = 7 # average, minimum, maximum start in column 7
for k, v in opdict.items():
if v != 0:
diff --git a/tools/wtperf_stats.py b/tools/wtperf_stats.py
new file mode 100644
index 00000000000..e18632c27ff
--- /dev/null
+++ b/tools/wtperf_stats.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#
+# Public Domain 2008-2014 WiredTiger, Inc.
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import os, csv, operator
+from time import mktime
+
+try:
+ from wt_nvd3_util import multiChart, parsetime
+except ImportError:
+ print >>sys.stderr, "Could not import wt_nvd3_util.py, it should be\
+ in the same directory as %s" % sys.argv[0]
+ sys.exit(-1)
+
+try:
+ from stat_data import no_scale_per_second_list
+except ImportError:
+ print >>sys.stderr, "Could not import stat_data.py, it should be in\
+ the same directory as %s" % sys.argv[0]
+ sys.exit(-1)
+
+# Fixup the names and values in a dictionary read in from a csv file. One
+# field must be "#time" - which is used to calculate the interval.
+# Input is a dictionary, output is a list of dictionaries with a single entry.
+def munge_dict(values_dict, abstime):
+ sorted_values = sorted(values_dict, key=operator.itemgetter('#time'))
+ start_time = parsetime(sorted_values[0]['#time'])
+ seconds = (parsetime(sorted_values[1]['#time']) - start_time).seconds
+ if seconds == 0:
+ seconds = 1
+
+ ret = []
+ for v in sorted_values:
+ if abstime:
+ # Build the time series, milliseconds since the epoch
+ v['#time'] = int(mktime(parsetime(v['#time']).timetuple())) * 1000
+ else:
+ # Build the time series as seconds since the start of the data
+ v['#time'] = (parsetime(v['#time']) - start_time).seconds
+ next_val = {}
+ for title, value in v.items():
+ if title.find('NS') != -1:
+ title = title.replace('NS', u'\u03BCs')
+ value = float(value) / 1000
+ if title == 'checkpoints' and value == 'N':
+ value = 0
+ elif title.find('time') != -1:
+ title = 'time'
+ elif title.find('latency') == -1 and \
+ title.find('checkpoints') == -1:
+ title = title + ' per second'
+ value = float(value) / seconds
+ next_val[title] = value
+ ret.append(next_val)
+
+ return ret
+
+def addPlotsToChart(chart, graph_data, wtstat_chart = False):
+ # Extract the times - they are the same for all lines.
+ times = []
+ for v in graph_data:
+ times.append(v['time'])
+
+ # Add a line to the graph for each field in the CSV file in alphabetical
+ # order, so the key is sorted.
+ for field in sorted(graph_data[0].keys()):
+ if field == 'time':
+ continue
+ # Split the latency and non-latency measurements onto different scales
+ axis = "1"
+ if not wtstat_chart and field.find('latency') == -1:
+ axis="2"
+ ydata = []
+ for v in graph_data:
+ ydata.append(v[field])
+ chart.add_serie(x=times, y=ydata, name=field, type="line", yaxis=axis)
+
+# Input parameters are a chart populated with WiredTiger statistics and
+# the directory where the wtperf monitor file can be found.
+def addPlotsToStatsChart(chart, dirname, abstime):
+ fname = os.path.join(dirname, 'monitor')
+ try:
+ with open(fname, 'rb') as csvfile:
+ reader = csv.DictReader(csvfile)
+ # Transform the data into something NVD3 can digest
+ graph_data = munge_dict(reader, abstime)
+ except IOError:
+ print >>sys.stderr, "Could not open wtperf monitor file."
+ sys.exit(-1)
+ addPlotsToChart(chart, graph_data, 1)
+
+def main():
+ # Parse the command line
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Create graphs from WiredTiger statistics.')
+ parser.add_argument('--abstime', action='store_true',
+ help='use absolute time on the x axis')
+ parser.add_argument('--output', '-o', metavar='file',
+ default='wtperf_stats.html', help='HTML output file')
+ parser.add_argument('files', metavar='file', nargs='+',
+ help='input monitor file generated by WiredTiger wtperf application')
+ args = parser.parse_args()
+
+ output_file = open(args.output, 'w')
+
+ if len(args.files) != 1:
+ print 'Script currently only supports a single monitor file'
+ exit (1)
+
+ chart_extra = {}
+ # Add in the x axis if the user wants time.
+ if args.abstime:
+ chart_extra['x_axis_format'] = '%H:%M:%S'
+
+ for f in args.files:
+ with open(f, 'rb') as csvfile:
+ reader = csv.DictReader(csvfile)
+ # Transform the data into something NVD3 can digest
+ graph_data = munge_dict(reader, args.abstime)
+
+ chart = multiChart(name='wtperf',
+ height=450 + 10*len(graph_data[0].keys()),
+ resize=True,
+ x_is_date=args.abstime,
+ assets_directory='http://source.wiredtiger.com/graphs/',
+ **chart_extra)
+
+ addPlotsToChart(chart, graph_data)
+
+ chart.buildhtml()
+ output_file.write(chart.htmlcontent)
+ output_file.close()
+
+if __name__ == '__main__':
+ main()
+
diff --git a/tools/wtstats.py b/tools/wtstats.py
index 766c49989b6..37b28e6c13a 100644
--- a/tools/wtstats.py
+++ b/tools/wtstats.py
@@ -28,38 +28,35 @@
import fileinput, os, re, shutil, sys, textwrap
from collections import defaultdict
-from datetime import datetime
from time import mktime
from subprocess import call
try:
from stat_data import no_scale_per_second_list
except ImportError:
- print >>sys.stderr, "Could not import stat_data.py, it should be in the same directory as %s" % sys.argv[0]
+ print >>sys.stderr, "Could not import stat_data.py, it should be\
+ in the same directory as %s" % sys.argv[0]
sys.exit(-1)
try:
- from nvd3 import lineChart, lineWithFocusChart
+ from wtperf_stats import addPlotsToStatsChart
except ImportError:
- print >>sys.stderr, "Could not import nvd3. Please install it *from source* (other versions may be missing features that we rely on). Run these commands: git clone https://github.com/areski/python-nvd3.git ; cd python-nvd3 ; sudo python setup.py install"
+ print >>sys.stderr, "Could not import wtperf_stats.py, it should be\
+ in the same directory as %s" % sys.argv[0]
sys.exit(-1)
+try:
+ from wt_nvd3_util import multiChart, parsetime
+except ImportError:
+ print >>sys.stderr, "Could not import wt_nvd3_util.py, it should be\
+ in the same directory as %s" % sys.argv[0]
+ sys.exit(-1)
-# Add a multiChart type so we can overlay line graphs
-class multiChart(lineChart):
- def __init__(self, **kwargs):
- lineChart.__init__(self, **kwargs)
-
- # Fix the axes
- del self.axislist['yAxis']
- self.create_y_axis('yAxis1', format=kwargs.get('y_axis_format', '.02f'))
- self.create_y_axis('yAxis2', format=kwargs.get('y_axis_format', '.02f'))
-
-TIMEFMT = "%b %d %H:%M:%S"
-
-thisyear = datetime.today().year
-def parsetime(s):
- return datetime.strptime(s, TIMEFMT).replace(year=thisyear)
+try:
+ from nvd3 import lineChart, lineWithFocusChart
+except ImportError:
+ print >>sys.stderr, "Could not import nvd3. Please install it *from source* (other versions may be missing features that we rely on). Run these commands: git clone https://github.com/areski/python-nvd3.git ; cd python-nvd3 ; sudo python setup.py install"
+ sys.exit(-1)
# Plot a set of entries for a title.
def munge(title, values):
@@ -93,7 +90,7 @@ def munge(title, values):
# Parse the command line
import argparse
-parser = argparse.ArgumentParser(description='Create graphs from WiredTIger statistics.')
+parser = argparse.ArgumentParser(description='Create graphs from WiredTiger statistics.')
parser.add_argument('--abstime', action='store_true',
help='use absolute time on the x axis')
parser.add_argument('--focus', action='store_true',
@@ -108,6 +105,8 @@ parser.add_argument('--output', '-o', metavar='file', default='wtstats.html',
parser.add_argument('--right', '-R', metavar='regexp',
type=re.compile, action='append',
help='use the right axis for series with titles matching the specifed regexp')
+parser.add_argument('--wtperf', '-w', action='store_true',
+ help='Plot wtperf statistics on the same graph')
parser.add_argument('files', metavar='file', nargs='+',
help='input files generated by WiredTiger statistics logging')
args = parser.parse_args()
@@ -211,6 +210,9 @@ for title, yaxis, ydata in results:
chart.add_serie(x=xdata, y=(ydata.get(x, 0) for x in xdata), name=title,
type="line", yaxis="2" if yaxis else "1")
+if args.wtperf:
+ addPlotsToStatsChart(chart, os.path.dirname(args.files[0]), args.abstime)
+
chart.buildhtml()
output_file.write(chart.htmlcontent)