diff options
author | Eugeni Dodonov <eugeni.dodonov@intel.com> | 2011-11-05 10:50:20 -0200 |
---|---|---|
committer | Eugeni Dodonov <eugeni.dodonov@intel.com> | 2011-11-09 11:44:32 -0200 |
commit | 79c6987116cb9ded646b8bf775f458436f20ff4a (patch) | |
tree | cf83e5d8580b34c708d2809b8c45bebaf97c5a25 | |
parent | 3ae38a52d31014cf7ae3cf9f43f51e50468144c0 (diff) |
intel_gpu_analyser: Now generate some interesting results.
Now parsing command line options, generating summaries and some
interesting charts.
Signed-off-by: Eugeni Dodonov <eugeni.dodonov@intel.com>
-rwxr-xr-x | tools/intel_gpu_analyser.py | 342 |
1 files changed, 326 insertions, 16 deletions
diff --git a/tools/intel_gpu_analyser.py b/tools/intel_gpu_analyser.py index a5b92f1..b1e0bf6 100755 --- a/tools/intel_gpu_analyser.py +++ b/tools/intel_gpu_analyser.py @@ -5,19 +5,76 @@ import sys import os +import getopt +import tempfile -def collect(command): +# for charts +import pylab + +HEADER=""" +<html> +<head> +<title>Analysis for %(title)s</title> +<style> +body { + font-family: arial; +} + +p { + margin-left: 10px; +} +</style> +</head> + +<body> +<h1>Analysis for %(title)s</h1> + +<h2>Summary:</h2> +<p> +<h3>Execution lasted %(duration).2f seconds</h3> +""" + +MINMAXAVG=""" +<p> +<h3>%(descr)s:</h3> +<b>Minimum:</b> %(min)s <br/> +<b>Maximum:</b> %(max)s <br/> +<b>Agerage:</b> %(avg)s <br/> +</p> +""" + +FIGURE=""" +<p> +<hr/> +<b>%(title)s</b> +<br/> +<a href="%(img)s"><img src="%(img)s" width=800 height=600></a> +</p> +""" + +TAIL=""" +</body> +</html> +""" + +def print_header(output): + """Prints the header""" + print >>output, HEADER + +def collect(fd): """Collects statistics for a given command""" - print "Running %s" % command - fd = os.popen("intel_gpu_top -o - -e \"%s\"" % command) results = {'time': [], 'utime': [], 'stime': [], 'power': [], 'render': [], + 'render_ops': [], 'bitstr': [], + 'bitstr_ops': [], 'bitstr2': [], - 'blitter': [] + 'bitstr2_ops': [], + 'blitter': [], + 'blitter_ops': [] } while True: line = fd.readline() @@ -27,29 +84,282 @@ def collect(command): if line[0] == "#": continue data = line.split() + # TODO: detect column names from headers results['time'].append(float(data[0])) results['utime'].append(float(data[1])) results['stime'].append(float(data[2])) results['power'].append(float(data[3])) results['render'].append(float(data[4])) + results['render_ops'].append(int(data[5])) results['bitstr'].append(float(data[6])) + results['bitstr_ops'].append(int(data[7])) results['bitstr2'].append(float(data[8])) + results['bitstr2_ops'].append(int(data[9])) results['blitter'].append(float(data[10])) + results['blitter_ops'].append(int(data[11])) return results -def analyse(results): +def analyse(results, title, out_dir, summary="index.html"): """Analyses intel_gpu_top results""" - minpower = min(results['power']) - maxpower = max(results['power']) - avgpower = sum(results['power']) / len(results['power']) - print "Power: min %f, max %f, avg %f" % (minpower, maxpower, avgpower) - for iter in results: - pass + # calculate min/max/avg values + keys = results.keys() + for iter in keys: + if not results[iter]: + print "ERROR: no results collected for '%s', aborting" % iter + sys.exit(1) + results["%s_min" % iter] = min(results[iter]) + results["%s_max" % iter] = max(results[iter]) + results["%s_avg" % iter] = sum(results[iter]) / len(results[iter]) + # start composing the output + output = open("%s/%s" % (out_dir, summary), "w") + print >>output, HEADER % {'title': title, + 'duration': results['time'][-1] + } + + # print summaries + for iter, descr in [('utime', 'User time % of CPU'), + ('stime', 'System time % of CPU'), + ('power', 'Power consumption (W)'), + ('render', 'Render engine usage % of GPU'), + ('render_ops', 'Render engine ops per interval'), + ('bitstr', 'Bitstream engine usage % of GPU'), + ('bitstr_ops', 'Bitstream engine ops per interval'), + ('bitstr2', 'Bitstream 6 engine usage % of GPU'), + ('bitstr2_ops', 'Bitstream 6 engine ops per interval'), + ('blitter', 'Blitter engine usage % of GPU'), + ('blitter_ops', 'Blitter engine ops per interval'), + ]: + minval = results['%s_min' % iter] + maxval = results['%s_max' % iter] + avgval = results['%s_avg' % iter] + if minval == maxval == avgval: + # No variation in data, skipping + print "No variations in %s, skipping" % iter + continue + minval_s = "%.2f" % minval + maxval_s = "%.2f" % maxval + avgval_s = "%.2f" % avgval + print >>output, MINMAXAVG % { + 'descr': descr, + 'min': minval_s, + 'max': maxval_s, + 'avg': avgval_s, + } + + # graphics + fig = pylab.figure() + ax = pylab.subplot(111) + pylab.title("Summary of CPU/GPU/Power usage") + pylab.ylabel("Usage (%)") + pylab.xlabel("Time (s)") + + ax.plot(results['time'], results['utime'], label="User time") + ax.plot(results['time'], results['stime'], label="System time") + num_axis = 2 + for ring in ["render", "bitstr", "bitstr2", "blitter"]: + if results["%s_avg" % ring] == -1: + print "No data for %s, skipping" % ring + continue + ax.plot(results['time'], results[ring], label=ring) + num_axis += 1 + # Do we have power? + if results["power_avg"] != -1: + # plotting power + ax2 = ax.twinx() + ax2.plot(results['time'], results["power"], label="Power (W)", color='red') + ax2.set_ylabel('Watts') + + pylab.grid() + # Shink current axis's height by 10% on the bottom + box = ax.get_position() + ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9]) + ax.legend(loc = 'upper center', ncol=num_axis, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1)) + pylab.savefig("%s/plot_summary.png" % out_dir, format="png", dpi=200) + print >>output, FIGURE % {'img': 'plot_summary.png', + 'title': 'Summary of the execution, outlining the CPU usage (in user and system mode), per-ring GPU usage, and power usage (if available) ' + } + + # graphics ops/per/second + fig = pylab.figure() + ax = pylab.subplot(111) + pylab.title("GPU rings ops per interval") + pylab.ylabel("Ops (n)") + pylab.xlabel("Time (s)") + + num_axis = 0 + for ring in ["render_ops", "bitstr_ops", "bitstr2_ops", "blitter_ops"]: + if results["%s_avg" % ring] == -1: + print "No data for %s, skipping" % ring + continue + ax.plot(results['time'], results[ring], label=ring) + num_axis += 1 + + pylab.grid() + # Shink current axis's height by 10% on the bottom + box = ax.get_position() + ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9]) + ax.legend(loc = 'upper center', ncol=num_axis, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1)) + pylab.savefig("%s/plot_gpu_ops.png" % out_dir, format="png", dpi=200) + print >>output, FIGURE % {'img': 'plot_gpu_ops.png', + 'title': 'Ops per interval for each GPU ring during the execution' + } + + # power + fig = pylab.figure() + pylab.title("Power usage summary") + pylab.ylabel("Power (W)") + pylab.xlabel("Time (s)") + + pylab.plot(results['power'], label="Power") + pylab.grid() + # Shink current axis's height by 10% on the bottom + pylab.savefig("%s/plot_power.png" % out_dir, format="png", dpi=200) + print >>output, FIGURE % {'img': 'plot_power.png', + 'title': 'Power usage over the course of execution' + } + + # power vs CPU + fig = pylab.figure() + ax = pylab.subplot(111) + pylab.title("CPU vs Power usage") + pylab.ylabel("Usage (%)") + pylab.xlabel("Time (s)") + + ax.plot(results['time'], results['utime'], label="User time") + ax.plot(results['time'], results['stime'], label="System time") + # plotting power + ax2 = ax.twinx() + ax2.plot(results['time'], results["power"], label="Power (W)", color='red') + ax2.set_ylabel('Watts') + + pylab.grid() + # Shink current axis's height by 10% on the bottom + box = ax.get_position() + ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9]) + ax.legend(loc = 'upper center', ncol=3, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1)) + pylab.savefig("%s/plot_power_cpu.png" % out_dir, format="png", dpi=200) + print >>output, FIGURE % {'img': 'plot_power_cpu.png', + 'title': 'Power utilization co-related to CPU' + } + + # power vs GPU + fig = pylab.figure() + ax = pylab.subplot(111) + pylab.title("GPU vs Power usage") + pylab.ylabel("Usage (%)") + pylab.xlabel("Time (s)") + + num_axis = 0 + for ring in ["render", "bitstr", "bitstr2", "blitter"]: + if results["%s_avg" % ring] == -1: + print "No data for %s, skipping" % ring + continue + ax.plot(results['time'], results[ring], label=ring) + num_axis += 1 + # Do we have power? + # plotting power + ax2 = ax.twinx() + ax2.plot(results['time'], results["power"], label="Power (W)", color='red') + ax2.set_ylabel('Watts') + num_axis += 1 + + pylab.grid() + # Shink current axis's height by 10% on the bottom + box = ax.get_position() + ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9]) + ax.legend(loc = 'upper center', ncol=num_axis, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1)) + pylab.savefig("%s/plot_power_gpu.png" % out_dir, format="png", dpi=200) + print >>output, FIGURE % {'img': 'plot_power_gpu.png', + 'title': 'Power utilization co-related to all GPU rings' + } + + # per-ring power + for ring in ["render", "bitstr", "bitstr2", "blitter"]: + if results["%s_avg" % ring] == -1: + print "No data for %s, skipping" % ring + continue + fig = pylab.figure() + ax = pylab.subplot(111) + pylab.title("Power usage vs %s ring" % ring) + pylab.ylabel("Usage (%)") + pylab.xlabel("Time (s)") + + ax.plot(results['time'], results[ring], label=ring) + # Do we have power? + # plotting power + ax2 = ax.twinx() + ax2.plot(results['time'], results["power"], label="Power (W)", color='red') + ax2.set_ylabel('Watts') + + pylab.grid() + # Shink current axis's height by 10% on the bottom + box = ax.get_position() + ax.set_position([box.x0, box.y0 + box.height * 0.1, box.width, box.height * 0.9]) + ax.legend(loc = 'upper center', ncol=2, fancybox=True, shadow=True, bbox_to_anchor = (0.5, -0.1)) + pylab.savefig("%s/plot_power_gpu_%s.png" % (out_dir, ring), format="png", dpi=200) + print >>output, FIGURE % {'img': 'plot_power_gpu_%s.png' % ring, + 'title': 'Power utilization co-related to the %s ring' % ring + } + + pass + + print >>output, TAIL + +def usage(cmd): + """Prints usage message""" + print """Intel-gpu-analyser: intel GPU data analyser + +Usage: %s [parameters]. + +The following arguments are accepted: + -h, --help display help message + -l, --logfile intel-gpu-top log file to analyse + -c, --command command to profile + -t, --title description of the command analysed + -o, --output output directory for the results + +You need to specify either a log file to analyse, or a command to profile. +When you specify both -l and -c, the last one takes predence. + +If you do not give a log file to analyse, or a command to profile, this +tool will become sad, depressed and suicide itself with exit code of -1, and +you will feel bad about it. +""" if __name__ == "__main__": - if len(sys.argv) < 2: - print "Usage: %s <command to profile>" % sys.argv[0] + # parse command line + try: + opt, args = getopt.getopt(sys.argv[1:], 'hl:c:t:o:', ['help', 'logfile=', 'command=', 'title=', 'output=']) + except getopt.error: + usage(sys.argv[0]) + sys.exit(1) + logfile = None + title = None + output = os.curdir + for o in opt: + # help + if o[0] == '-h' or o[0] == '--help': + usage(sys.argv[0]) + sys.exit(0) + # list + elif o[0] == '-l' or o[0] == '--logfile': + logfile = open(o[1], "r") + title = "Log file '%s'" % o[1] + elif o[0] == '-c' or o[0] == '--command': + logfile = os.popen("intel_gpu_top -o - -e \"%s\"" % o[1]) + title = "Execution of '%s'" % o[1] + # force new level + elif o[0] == '-t' or o[0] == '--title': + title = o[1] + elif o[0] == '-o' or o[0] == '--output': + output = o[1] + if not logfile: + usage(sys.argv[0]) + print "Error: no log file and no command to profile, don't know what to do" sys.exit(1) - results = collect(sys.argv[1]) - analyse(results) - print results + try: + os.makedirs(output) + except: + pass + results = collect(logfile) + analyse(results, title, output) |