Re: [PATCH v2] tools/power/x86: Debug utility for intel_pstate driver

From: Srinivas Pandruvada
Date: Mon Feb 13 2017 - 23:19:30 EST


On Mon, 2017-02-13 at 15:19 -0800, Doug Smythies wrote:
> This utility can be used to debug and tune the performance of the
> intel_pstate driver.
> This utility can be used in two ways:
> - If there is Linux trace file with pstate_sample events enabled,
> then
> this utility can parse the trace file and generate performance plots.
> - If user has not specified a trace file as input via command line
> parameters, then this utility enables and collects trace data for a
> user
> specified interval and generates performance plots.
>
> Signed-off-by: Doug Smythies <dsmythies@xxxxxxxxx>

I am fine with this version. But since Rafael may have already prepared
for pull request, so it is up to him. Otherwise we have to send delta
patch. Since this is tool and a first version only, it is fine in my
opinion to have some backward compatibility issue with version 1 of the
patch.

Thanks,
Srinivas

> ---
> Â.../x86/intel_pstate_tracer/intel_pstate_tracer.py | 569
> +++++++++++++++++++++
> Â1 file changed, 569 insertions(+)
> Âcreate mode 100755
> tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
>
> diff --git
> a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
> b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
> new file mode 100755
> index 0000000..fd706ac
> --- /dev/null
> +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py
> @@ -0,0 +1,569 @@
> +#!/usr/bin/python
> +# -*- coding: utf-8 -*-
> +#
> +""" This utility can be used to debug and tune the performance of
> the
> +intel_pstate driver. This utility can be used in two ways:
> +- If there is Linux trace file with pstate_sample events enabled,
> then
> +this utility can parse the trace file and generate performance
> plots.
> +- If user has not specified a trace file as input via command line
> parameters,
> +then this utility enables and collects trace data for a user
> specified interval
> +and generates performance plots.
> +
> +Prerequisites:
> +ÂÂÂÂPython version 2.7.x
> +ÂÂÂÂgnuplot 5.0 or higher
> +ÂÂÂÂgnuplot-py 1.8
> +ÂÂÂÂ(Most of the distributions have these required packages. They
> may be called
> +ÂÂÂÂÂgnuplot-py, phython-gnuplot. )
> +
> +ÂÂÂÂHWP (Hardware P-States are disabled)
> +ÂÂÂÂKernel config for Linux trace is enabled
> +
> +ÂÂÂÂsee print_help(): for Usage and Output details
> +
> +"""
> +from __future__ import print_function
> +from datetime import datetime
> +import subprocess
> +import os
> +import time
> +import re
> +import sys
> +import getopt
> +import Gnuplot
> +from numpy import *
> +from decimal import *
> +
> +__author__ = "Srinivas Pandruvada"
> +__copyright__ = " Copyright (c) 2017, Intel Corporation. "
> +__license__ = "GPL version 2"
> +
> +
> +MAX_CPUS = 256
> +
> +# Define the csv file columns
> +C_COMM = 18
> +C_GHZ = 17
> +C_ELAPSED = 16
> +C_SAMPLE = 15
> +C_DURATION = 14
> +C_LOAD = 13
> +C_BOOST = 12
> +C_FREQ = 11
> +C_TSC = 10
> +C_APERF = 9
> +C_MPERF = 8
> +C_TO = 7
> +C_FROM = 6
> +C_SCALED = 5
> +C_CORE = 4
> +C_USEC = 3
> +C_SEC = 2
> +C_CPU = 1
> +
> +global sample_num, last_sec_cpu, last_usec_cpu, start_time, testname
> +
> +# 11 digits covers uptime to 115 days
> +getcontext().prec = 11
> +
> +sample_num =0
> +last_sec_cpu = [0] * MAX_CPUS
> +last_usec_cpu = [0] * MAX_CPUS
> +
> +def print_help():
> +ÂÂÂÂprint('intel_pstate_tracer.py:')
> +ÂÂÂÂprint('ÂÂUsage:')
> +ÂÂÂÂprint('ÂÂÂÂIf the trace file is available, then to simply parse
> and plot, use (sudo not required):')
> +ÂÂÂÂprint('ÂÂÂÂÂÂ./intel_pstate_tracer.py [-c cpus] -t <trace_file>
> -n <test_name>')
> +ÂÂÂÂprint('ÂÂÂÂOr')
> +ÂÂÂÂprint('ÂÂÂÂÂÂ./intel_pstate_tracer.py [--cpu cpus] ---trace_file
> <trace_file> --name <test_name>')
> +ÂÂÂÂprint('ÂÂÂÂTo generate trace file, parse and plot, use (sudo
> required):')
> +ÂÂÂÂprint('ÂÂÂÂÂÂsudo ./intel_pstate_tracer.py [-c cpus] -i
> <interval> -n <test_name>')
> +ÂÂÂÂprint('ÂÂÂÂOr')
> +ÂÂÂÂprint('ÂÂÂÂÂÂsudo ./intel_pstate_tracer.py [--cpu cpus] --
> interval <interval> --name <test_name>')
> +ÂÂÂÂprint('ÂÂÂÂOptional argument:')
> +ÂÂÂÂprint('ÂÂÂÂÂÂcpus:ÂÂcomma separated list of CPUs')
> +ÂÂÂÂprint('ÂÂOutput:')
> +ÂÂÂÂprint('ÂÂÂÂIf not already present, creates a "results/test_name"
> folder in the current working directory with:')
> +ÂÂÂÂprint('ÂÂÂÂÂÂcpu.csv - comma seperated values file with trace
> contents and some additional calculations.')
> +ÂÂÂÂprint('ÂÂÂÂÂÂcpu???.csv - comma seperated values file for CPU
> number ???.')
> +ÂÂÂÂprint('ÂÂÂÂÂÂ*.png - a variety of PNG format plot files created
> from the trace contents and the additional calculations.')
> +ÂÂÂÂprint('ÂÂNotes:')
> +ÂÂÂÂprint('ÂÂÂÂAvoid the use of _ (underscore) in test names,
> because in gnuplot it is a subscript directive.')
> +ÂÂÂÂprint('ÂÂÂÂMaximum number of CPUs is {0:d}. If there are more
> the script will abort with an error.'.format(MAX_CPUS))
> +ÂÂÂÂprint('ÂÂÂÂOff-line CPUs cause the script to list some warnings,
> and create some empty files. Use the CPU mask feature for a clean
> run.')
> +ÂÂÂÂprint('ÂÂÂÂEmpty y range warnings for autoscaled plots can occur
> and can be ignored.')
> +
> +def plot_perf_busy_with_sample(cpu_index):
> +ÂÂÂÂ""" Plot method to per cpu information """
> +
> +ÂÂÂÂfile_name = 'cpu{:0>3}.csv'.format(cpu_index)
> +ÂÂÂÂif os.path.exists(file_name):
> +ÂÂÂÂÂÂÂÂoutput_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index
> +ÂÂÂÂÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂÂÂÂÂg_plot('set yrange [0:40]')
> +ÂÂÂÂÂÂÂÂg_plot('set y2range [0:200]')
> +ÂÂÂÂÂÂÂÂg_plot('set y2tics 0, 10')
> +ÂÂÂÂÂÂÂÂg_plot('set title "{} : cpu perf busy vs. sample : CPU
> {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
> +#ÂÂÂÂÂÂÂOverride common
> +ÂÂÂÂÂÂÂÂg_plot('set xlabel "Samples"')
> +ÂÂÂÂÂÂÂÂg_plot('set ylabel "P-State"')
> +ÂÂÂÂÂÂÂÂg_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
> +ÂÂÂÂÂÂÂÂset_4_plot_linestyles(g_plot)
> +ÂÂÂÂÂÂÂÂg_plot('plot "' + file_name + '" using {:d}:{:d} with
> linespoints linestyle 1 axis x1y2 title
> "performance",\\'.format(C_SAMPLE, C_CORE))
> +ÂÂÂÂÂÂÂÂg_plot('"' + file_name + '" using {:d}:{:d} with linespoints
> linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE,
> C_SCALED))
> +ÂÂÂÂÂÂÂÂg_plot('"' + file_name + '" using {:d}:{:d} with linespoints
> linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST))
> +ÂÂÂÂÂÂÂÂg_plot('"' + file_name + '" using {:d}:{:d} with linespoints
> linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO))
> +
> +def plot_perf_busy(cpu_index):
> +ÂÂÂÂ""" Plot some per cpu information """
> +
> +ÂÂÂÂfile_name = 'cpu{:0>3}.csv'.format(cpu_index)
> +ÂÂÂÂif os.path.exists(file_name):
> +ÂÂÂÂÂÂÂÂoutput_png = "cpu%03d_perf_busy.png" % cpu_index
> +ÂÂÂÂÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂÂÂÂÂg_plot('set yrange [0:40]')
> +ÂÂÂÂÂÂÂÂg_plot('set y2range [0:200]')
> +ÂÂÂÂÂÂÂÂg_plot('set y2tics 0, 10')
> +ÂÂÂÂÂÂÂÂg_plot('set title "{} : perf busy : CPU {:0>3} : {:%F
> %H:%M}"'.format(testname, cpu_index, datetime.now()))
> +ÂÂÂÂÂÂÂÂg_plot('set ylabel "P-State"')
> +ÂÂÂÂÂÂÂÂg_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
> +ÂÂÂÂÂÂÂÂset_4_plot_linestyles(g_plot)
> +ÂÂÂÂÂÂÂÂg_plot('plot "' + file_name + '" using {:d}:{:d} with
> linespoints linestyle 1 axis x1y2 title
> "performance",\\'.format(C_ELAPSED, C_CORE))
> +ÂÂÂÂÂÂÂÂg_plot('"' + file_name + '" using {:d}:{:d} with linespoints
> linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED,
> C_SCALED))
> +ÂÂÂÂÂÂÂÂg_plot('"' + file_name + '" using {:d}:{:d} with linespoints
> linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED,
> C_BOOST))
> +ÂÂÂÂÂÂÂÂg_plot('"' + file_name + '" using {:d}:{:d} with linespoints
> linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO))
> +
> +def plot_durations(cpu_index):
> +ÂÂÂÂ""" Plot per cpu durations """
> +
> +ÂÂÂÂfile_name = 'cpu{:0>3}.csv'.format(cpu_index)
> +ÂÂÂÂif os.path.exists(file_name):
> +ÂÂÂÂÂÂÂÂoutput_png = "cpu%03d_durations.png" % cpu_index
> +ÂÂÂÂÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +#ÂÂÂÂÂÂÂShould autoscale be used here? Should seconds be used here?
> +ÂÂÂÂÂÂÂÂg_plot('set yrange [0:5000]')
> +ÂÂÂÂÂÂÂÂg_plot('set ytics 0, 500')
> +ÂÂÂÂÂÂÂÂg_plot('set title "{} : durations : CPU {:0>3} : {:%F
> %H:%M}"'.format(testname, cpu_index, datetime.now()))
> +ÂÂÂÂÂÂÂÂg_plot('set ylabel "Timer Duration (MilliSeconds)"')
> +#ÂÂÂÂÂÂÂoverride common
> +ÂÂÂÂÂÂÂÂg_plot('set key off')
> +ÂÂÂÂÂÂÂÂset_4_plot_linestyles(g_plot)
> +ÂÂÂÂÂÂÂÂg_plot('plot "' + file_name + '" using {:d}:{:d} with
> linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION))
> +
> +def plot_loads(cpu_index):
> +ÂÂÂÂ""" Plot per cpu loads """
> +
> +ÂÂÂÂfile_name = 'cpu{:0>3}.csv'.format(cpu_index)
> +ÂÂÂÂif os.path.exists(file_name):
> +ÂÂÂÂÂÂÂÂoutput_png = "cpu%03d_loads.png" % cpu_index
> +ÂÂÂÂÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂÂÂÂÂg_plot('set yrange [0:100]')
> +ÂÂÂÂÂÂÂÂg_plot('set ytics 0, 10')
> +ÂÂÂÂÂÂÂÂg_plot('set title "{} : loads : CPU {:0>3} : {:%F
> %H:%M}"'.format(testname, cpu_index, datetime.now()))
> +ÂÂÂÂÂÂÂÂg_plot('set ylabel "CPU load (percent)"')
> +#ÂÂÂÂÂÂÂoverride common
> +ÂÂÂÂÂÂÂÂg_plot('set key off')
> +ÂÂÂÂÂÂÂÂset_4_plot_linestyles(g_plot)
> +ÂÂÂÂÂÂÂÂg_plot('plot "' + file_name + '" using {:d}:{:d} with
> linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD))
> +
> +def plot_pstate_cpu_with_sample():
> +ÂÂÂÂ""" Plot all cpu information """
> +
> +ÂÂÂÂif os.path.exists('cpu.csv'):
> +ÂÂÂÂÂÂÂÂoutput_png = 'all_cpu_pstates_vs_samples.png'
> +ÂÂÂÂÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂÂÂÂÂg_plot('set yrange [0:40]')
> +#ÂÂÂÂÂÂÂoverride common
> +ÂÂÂÂÂÂÂÂg_plot('set xlabel "Samples"')
> +ÂÂÂÂÂÂÂÂg_plot('set ylabel "P-State"')
> +ÂÂÂÂÂÂÂÂg_plot('set title "{} : cpu pstate vs. sample : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +ÂÂÂÂÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using
> {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO)
> +ÂÂÂÂÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂÂÂÂÂg_plot(plot_str)
> +
> +def plot_pstate_cpu():
> +ÂÂÂÂ""" Plot all cpu information from csv files """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_pstates.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂg_plot('set yrange [0:40]')
> +ÂÂÂÂg_plot('set ylabel "P-State"')
> +ÂÂÂÂg_plot('set title "{} : cpu pstates : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +
> +#ÂÂÂÂthe following command is really cool, but doesn't work with the
> CPU masking option because it aborts on the first missing file.
> +#ÂÂÂÂplot_str = 'plot for [i=0:*] file=sprintf("cpu%03d.csv",i)
> title_s=sprintf("cpu%03d",i) file using 16:7 pt 7 ps 1 title title_s'
> +#
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_TO)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def plot_load_cpu():
> +ÂÂÂÂ""" Plot all cpu loads """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_loads.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂg_plot('set yrange [0:100]')
> +ÂÂÂÂg_plot('set ylabel "CPU load (percent)"')
> +ÂÂÂÂg_plot('set title "{} : cpu loads : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def plot_frequency_cpu():
> +ÂÂÂÂ""" Plot all cpu frequencies """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_frequencies.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂg_plot('set yrange [0:4]')
> +ÂÂÂÂg_plot('set ylabel "CPU Frequency (GHz)"')
> +ÂÂÂÂg_plot('set title "{} : cpu frequencies : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def plot_duration_cpu():
> +ÂÂÂÂ""" Plot all cpu durations """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_durations.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂg_plot('set yrange [0:5000]')
> +ÂÂÂÂg_plot('set ytics 0, 500')
> +ÂÂÂÂg_plot('set ylabel "Timer Duration (MilliSeconds)"')
> +ÂÂÂÂg_plot('set title "{} : cpu durations : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def plot_scaled_cpu():
> +ÂÂÂÂ""" Plot all cpu scaled busy """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_scaled.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +#ÂÂÂautoscale this one, no set y range
> +ÂÂÂÂg_plot('set ylabel "Scaled Busy (Unitless)"')
> +ÂÂÂÂg_plot('set title "{} : cpu scaled busy : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def plot_boost_cpu():
> +ÂÂÂÂ""" Plot all cpu IO Boosts """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_boost.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +ÂÂÂÂg_plot('set yrange [0:100]')
> +ÂÂÂÂg_plot('set ylabel "CPU IO Boost (percent)"')
> +ÂÂÂÂg_plot('set title "{} : cpu io boost : {:%F
> %H:%M}"'.format(testname, datetime.now()))
> +
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def plot_ghz_cpu():
> +ÂÂÂÂ""" Plot all cpu tsc ghz """
> +
> +ÂÂÂÂoutput_png = 'all_cpu_ghz.png'
> +ÂÂÂÂg_plot = common_all_gnuplot_settings(output_png)
> +#ÂÂÂautoscale this one, no set y range
> +ÂÂÂÂg_plot('set ylabel "TSC Frequency (GHz)"')
> +ÂÂÂÂg_plot('set title "{} : cpu TSC Frequencies (Sanity check
> calculation) : {:%F %H:%M}"'.format(testname, datetime.now()))
> +
> +ÂÂÂÂtitle_list = subprocess.check_output('ls cpu???.csv | sed -e
> \'s/.csv//\'',shell=True).replace('\n', ' ')
> +ÂÂÂÂplot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d}
> pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ)
> +ÂÂÂÂg_plot('title_list = "{}"'.format(title_list))
> +ÂÂÂÂg_plot(plot_str)
> +
> +def common_all_gnuplot_settings(output_png):
> +ÂÂÂÂ""" common gnuplot settings for multiple CPUs one one graph. """
> +
> +ÂÂÂÂg_plot = common_gnuplot_settings()
> +ÂÂÂÂg_plot('set output "' + output_png + '"')
> +ÂÂÂÂreturn(g_plot)
> +
> +def common_gnuplot_settings():
> +ÂÂÂÂ""" common gnuplot settings. """
> +
> +ÂÂÂÂg_plot = Gnuplot.Gnuplot(persist=1)
> +#ÂÂÂThe following line is for rigor only. It seems to be assumed for
> .csv files
> +ÂÂÂÂg_plot('set datafile separator \",\"')
> +ÂÂÂÂg_plot('set ytics nomirror')
> +ÂÂÂÂg_plot('set xtics nomirror')
> +ÂÂÂÂg_plot('set xtics font ", 10"')
> +ÂÂÂÂg_plot('set ytics font ", 10"')
> +ÂÂÂÂg_plot('set tics out scale 1.0')
> +ÂÂÂÂg_plot('set grid')
> +ÂÂÂÂg_plot('set key out horiz')
> +ÂÂÂÂg_plot('set key bot center')
> +ÂÂÂÂg_plot('set key samplen 2 spacing .8 font ", 9"')
> +ÂÂÂÂg_plot('set term png size 1200, 600')
> +ÂÂÂÂg_plot('set title font ", 11"')
> +ÂÂÂÂg_plot('set ylabel font ", 10"')
> +ÂÂÂÂg_plot('set xlabel font ", 10"')
> +ÂÂÂÂg_plot('set xlabel offset 0, 0.5')
> +ÂÂÂÂg_plot('set xlabel "Elapsed Time (Seconds)"')
> +ÂÂÂÂreturn(g_plot)
> +
> +def set_4_plot_linestyles(g_plot):
> +ÂÂÂÂ""" set the linestyles used for 4 plots in 1 graphs. """
> +
> +ÂÂÂÂg_plot('set style line 1 linetype 1 linecolor rgb "green"
> pointtype -1')
> +ÂÂÂÂg_plot('set style line 2 linetype 1 linecolor rgb "red"
> pointtype -1')
> +ÂÂÂÂg_plot('set style line 3 linetype 1 linecolor rgb "purple"
> pointtype -1')
> +ÂÂÂÂg_plot('set style line 4 linetype 1 linecolor rgb "blue"
> pointtype -1')
> +
> +def store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy,
> scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost,
> common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz):
> +ÂÂÂÂ""" Store master csv file information """
> +
> +ÂÂÂÂglobal graph_data_present
> +
> +ÂÂÂÂif cpu_mask[cpu_int] == 0:
> +ÂÂÂÂÂÂÂÂreturn
> +
> +ÂÂÂÂtry:
> +ÂÂÂÂÂÂÂÂf_handle = open('cpu.csv', 'a')
> +ÂÂÂÂÂÂÂÂstring_buffer = "CPU_%03u, %05u, %06u, %u, %u, %u, %u, %u,
> %u, %u, %.4f, %u, %.2f, %.3f, %u, %.3f, %.3f, %s\n" % (cpu_int,
> int(time_pre_dec), int(time_post_dec), int(core_busy), int(scaled),
> int(_from), int(_to), int(mperf), int(aperf), int(tsc), freq_ghz,
> int(io_boost), load, duration_ms, sample_num, elapsed_time, tsc_ghz,
> common_comm)
> +ÂÂÂÂÂÂÂÂf_handle.write(string_buffer);
> +ÂÂÂÂÂÂÂÂf_handle.close()
> +ÂÂÂÂexcept:
> +ÂÂÂÂÂÂÂÂprint('IO error cpu.csv')
> +ÂÂÂÂÂÂÂÂreturn
> +
> +ÂÂÂÂgraph_data_present = True;
> +
> +def split_csv():
> +ÂÂÂÂ""" seperate the all csv file into per CPU csv files. """
> +
> +ÂÂÂÂglobal current_max_cpu
> +
> +ÂÂÂÂif os.path.exists('cpu.csv'):
> +ÂÂÂÂÂÂÂÂfor index in range(0, current_max_cpu + 1):
> +ÂÂÂÂÂÂÂÂÂÂÂÂif cpu_mask[int(index)] != 0:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂos.system('grep -m 1 common_cpu cpu.csv >
> cpu{:0>3}.csv'.format(index))
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂos.system('grep CPU_{:0>3} cpu.csv >>
> cpu{:0>3}.csv'.format(index, index))
> +
> +def cleanup_data_files():
> +ÂÂÂÂ""" clean up existing data files """
> +
> +ÂÂÂÂif os.path.exists('cpu.csv'):
> +ÂÂÂÂÂÂÂÂos.remove('cpu.csv')
> +ÂÂÂÂf_handle = open('cpu.csv', 'a')
> +ÂÂÂÂf_handle.write('common_cpu, common_secs, common_usecs,
> core_busy, scaled_busy, from, to, mperf, aperf, tsc, freq, boost,
> load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm')
> +ÂÂÂÂf_handle.write('\n')
> +ÂÂÂÂf_handle.close()
> +
> +def clear_trace_file():
> +ÂÂÂÂ""" Clear trace file """
> +
> +ÂÂÂÂtry:
> +ÂÂÂÂÂÂÂÂf_handle = open('/sys/kernel/debug/tracing/trace', 'w')
> +ÂÂÂÂÂÂÂÂf_handle.close()
> +ÂÂÂÂexcept:
> +ÂÂÂÂÂÂÂÂprint('IO error clearing trace file ')
> +ÂÂÂÂÂÂÂÂquit()
> +
> +def enable_trace():
> +ÂÂÂÂ""" Enable trace """
> +
> +ÂÂÂÂtry:
> +ÂÂÂÂÂÂÂopen('/sys/kernel/debug/tracing/events/power/pstate_sample/en
> able'
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ, 'w').write("1")
> +ÂÂÂÂexcept:
> +ÂÂÂÂÂÂÂÂprint('IO error enabling trace ')
> +ÂÂÂÂÂÂÂÂquit()
> +
> +def disable_trace():
> +ÂÂÂÂ""" Disable trace """
> +
> +ÂÂÂÂtry:
> +ÂÂÂÂÂÂÂopen('/sys/kernel/debug/tracing/events/power/pstate_sample/en
> able'
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ, 'w').write("0")
> +ÂÂÂÂexcept:
> +ÂÂÂÂÂÂÂÂprint('IO error disabling trace ')
> +ÂÂÂÂÂÂÂÂquit()
> +
> +def set_trace_buffer_size():
> +ÂÂÂÂ""" Set trace buffer size """
> +
> +ÂÂÂÂtry:
> +ÂÂÂÂÂÂÂopen('/sys/kernel/debug/tracing/buffer_size_kb'
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ, 'w').write("10240")
> +ÂÂÂÂexcept:
> +ÂÂÂÂÂÂÂÂprint('IO error setting trace buffer size ')
> +ÂÂÂÂÂÂÂÂquit()
> +
> +def read_trace_data(filename):
> +ÂÂÂÂ""" Read and parse trace data """
> +
> +ÂÂÂÂglobal current_max_cpu
> +ÂÂÂÂglobal sample_num, last_sec_cpu, last_usec_cpu, start_time
> +
> +ÂÂÂÂtry:
> +ÂÂÂÂÂÂÂÂdata = open(filename, 'r').read()
> +ÂÂÂÂexcept:
> +ÂÂÂÂÂÂÂÂprint('Error opening ', filename)
> +ÂÂÂÂÂÂÂÂquit()
> +
> +ÂÂÂÂfor line in data.splitlines():
> +ÂÂÂÂÂÂÂÂsearch_obj = \
> +ÂÂÂÂÂÂÂÂÂÂÂÂre.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.
> *?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mp
> erf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)'
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ, line)
> +
> +ÂÂÂÂÂÂÂÂif search_obj:
> +ÂÂÂÂÂÂÂÂÂÂÂÂcpu = search_obj.group(3)
> +ÂÂÂÂÂÂÂÂÂÂÂÂcpu_int = int(cpu)
> +ÂÂÂÂÂÂÂÂÂÂÂÂcpu = str(cpu_int)
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂtime_pre_dec = search_obj.group(6)
> +ÂÂÂÂÂÂÂÂÂÂÂÂtime_post_dec = search_obj.group(8)
> +ÂÂÂÂÂÂÂÂÂÂÂÂcore_busy = search_obj.group(10)
> +ÂÂÂÂÂÂÂÂÂÂÂÂscaled = search_obj.group(12)
> +ÂÂÂÂÂÂÂÂÂÂÂÂ_from = search_obj.group(14)
> +ÂÂÂÂÂÂÂÂÂÂÂÂ_to = search_obj.group(16)
> +ÂÂÂÂÂÂÂÂÂÂÂÂmperf = search_obj.group(18)
> +ÂÂÂÂÂÂÂÂÂÂÂÂaperf = search_obj.group(20)
> +ÂÂÂÂÂÂÂÂÂÂÂÂtsc = search_obj.group(22)
> +ÂÂÂÂÂÂÂÂÂÂÂÂfreq = search_obj.group(24)
> +ÂÂÂÂÂÂÂÂÂÂÂÂcommon_comm = search_obj.group(2).replace(' ', '')
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂ# Not all kernel versions have io_boost field
> +ÂÂÂÂÂÂÂÂÂÂÂÂio_boost = '0'
> +ÂÂÂÂÂÂÂÂÂÂÂÂsearch_obj = re.search(r'.*?io_boost=(\d+)', line)
> +ÂÂÂÂÂÂÂÂÂÂÂÂif search_obj:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂio_boost = search_obj.group(1)
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂif sample_num == 0 :
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂstart_time = Decimal(time_pre_dec) +
> Decimal(time_post_dec) / Decimal(1000000)
> +ÂÂÂÂÂÂÂÂÂÂÂÂsample_num += 1
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂif last_sec_cpu[cpu_int] == 0 :
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂlast_sec_cpu[cpu_int] = time_pre_dec
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂlast_usec_cpu[cpu_int] = time_post_dec
> +ÂÂÂÂÂÂÂÂÂÂÂÂelse :
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂduration_us = (int(time_pre_dec) -
> int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) -
> int(last_usec_cpu[cpu_int]))
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂduration_ms = Decimal(duration_us) / Decimal(1000)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂlast_sec_cpu[cpu_int] = time_pre_dec
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂlast_usec_cpu[cpu_int] = time_post_dec
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂelapsed_time = Decimal(time_pre_dec) +
> Decimal(time_post_dec) / Decimal(1000000) - start_time
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂload = Decimal(int(mperf)*100)/ Decimal(tsc)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂfreq_ghz = Decimal(freq)/Decimal(1000000)
> +#ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂSanity check calculation, typically anomalies
> indicate missed samples
> +#ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂHowever, check for 0 (should never occur)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂtsc_ghz = Decimal(0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif duration_ms != Decimal(0) :
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂtsc_ghz =
> Decimal(tsc)/duration_ms/Decimal(1000000)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂstore_csv(cpu_int, time_pre_dec, time_post_dec,
> core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost,
> common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz)
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂif cpu_int > current_max_cpu:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcurrent_max_cpu = cpu_int
> +# End of for each trace line loop
> +# Now seperate the main overall csv file into per CPU csv files.
> +ÂÂÂÂsplit_csv()
> +
> +interval = ""
> +filename = ""
> +cpu_list = ""
> +testname = ""
> +graph_data_present = False;
> +
> +valid1 = False
> +valid2 = False
> +
> +cpu_mask = zeros((MAX_CPUS,), dtype=int)
> +
> +try:
> +ÂÂÂÂopts, args =
> getopt.getopt(sys.argv[1:],"ht:i:c:n:",["help","trace_file=","interva
> l=","cpu=","name="])
> +except getopt.GetoptError:
> +ÂÂÂÂprint_help()
> +ÂÂÂÂsys.exit(2)
> +for opt, arg in opts:
> +ÂÂÂÂif opt == '-h':
> +ÂÂÂÂÂÂÂÂprint()
> +ÂÂÂÂÂÂÂÂsys.exit()
> +ÂÂÂÂelif opt in ("-t", "--trace_file"):
> +ÂÂÂÂÂÂÂÂvalid1 = True
> +ÂÂÂÂÂÂÂÂlocation = os.path.realpath(os.path.join(os.getcwd(),
> os.path.dirname(__file__)))
> +ÂÂÂÂÂÂÂÂfilename = os.path.join(location, arg)
> +ÂÂÂÂelif opt in ("-i", "--interval"):
> +ÂÂÂÂÂÂÂÂvalid1 = True
> +ÂÂÂÂÂÂÂÂinterval = arg
> +ÂÂÂÂelif opt in ("-c", "--cpu"):
> +ÂÂÂÂÂÂÂÂcpu_list = arg
> +ÂÂÂÂelif opt in ("-n", "--name"):
> +ÂÂÂÂÂÂÂÂvalid2 = True
> +ÂÂÂÂÂÂÂÂtestname = arg
> +
> +if not (valid1 and valid2):
> +ÂÂÂÂprint_help()
> +ÂÂÂÂsys.exit()
> +
> +if cpu_list:
> +ÂÂÂÂfor p in re.split("[,]", cpu_list):
> +ÂÂÂÂÂÂÂÂif int(p) < MAX_CPUS :
> +ÂÂÂÂÂÂÂÂÂÂÂÂcpu_mask[int(p)] = 1
> +else:
> +ÂÂÂÂfor i in range (0, MAX_CPUS):
> +ÂÂÂÂÂÂÂÂcpu_mask[i] = 1
> +
> +if not os.path.exists('results'):
> +ÂÂÂÂos.mkdir('results')
> +
> +os.chdir('results')
> +if os.path.exists(testname):
> +ÂÂÂÂprint('The test name directory already exists. Please provide a
> unique test name. Test re-run not supported, yet.')
> +ÂÂÂÂsys.exit()
> +os.mkdir(testname)
> +os.chdir(testname)
> +
> +# Temporary (or perhaps not)
> +cur_version = sys.version_info
> +print('python version (should be >= 2.7):')
> +print(cur_version)
> +
> +# Left as "cleanup" for potential future re-run ability.
> +cleanup_data_files()
> +
> +if interval:
> +ÂÂÂÂfilename = "/sys/kernel/debug/tracing/trace"
> +ÂÂÂÂclear_trace_file()
> +ÂÂÂÂset_trace_buffer_size()
> +ÂÂÂÂenable_trace()
> +ÂÂÂÂprint('Sleeping for ', interval, 'seconds')
> +ÂÂÂÂtime.sleep(int(interval))
> +ÂÂÂÂdisable_trace()
> +
> +current_max_cpu = 0
> +
> +read_trace_data(filename)
> +
> +if graph_data_present == False:
> +ÂÂÂÂprint('No valid data to plot')
> +ÂÂÂÂsys.exit(2)
> +
> +for cpu_no in range(0, current_max_cpu + 1):
> +ÂÂÂÂplot_perf_busy_with_sample(cpu_no)
> +ÂÂÂÂplot_perf_busy(cpu_no)
> +ÂÂÂÂplot_durations(cpu_no)
> +ÂÂÂÂplot_loads(cpu_no)
> +
> +plot_pstate_cpu_with_sample()
> +plot_pstate_cpu()
> +plot_load_cpu()
> +plot_frequency_cpu()
> +plot_duration_cpu()
> +plot_scaled_cpu()
> +plot_boost_cpu()
> +plot_ghz_cpu()
> +
> +os.chdir('../../')