xref: /linux/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1*e68cb15bSDoug Smythies#!/usr/bin/env python3
26c8f392cSThomas Gleixner# SPDX-License-Identifier: GPL-2.0-only
348385dd7SDoug Smythies# -*- coding: utf-8 -*-
448385dd7SDoug Smythies#
548385dd7SDoug Smythies""" This utility can be used to debug and tune the performance of the
648385dd7SDoug Smythiesintel_pstate driver. This utility can be used in two ways:
748385dd7SDoug Smythies- If there is Linux trace file with pstate_sample events enabled, then
848385dd7SDoug Smythiesthis utility can parse the trace file and generate performance plots.
948385dd7SDoug Smythies- If user has not specified a trace file as input via command line parameters,
1048385dd7SDoug Smythiesthen this utility enables and collects trace data for a user specified interval
1148385dd7SDoug Smythiesand generates performance plots.
1248385dd7SDoug Smythies
1348385dd7SDoug SmythiesPrerequisites:
14*e68cb15bSDoug Smythies    Python version 3.6.x or higher
1548385dd7SDoug Smythies    gnuplot 5.0 or higher
16*e68cb15bSDoug Smythies    python3-gnuplot 1.8 or higher
1748385dd7SDoug Smythies    (Most of the distributions have these required packages. They may be called
18*e68cb15bSDoug Smythies     gnuplot-py, python-gnuplot or python3-gnuplot, gnuplot-nox, ... )
1948385dd7SDoug Smythies
2048385dd7SDoug Smythies    HWP (Hardware P-States are disabled)
2148385dd7SDoug Smythies    Kernel config for Linux trace is enabled
2248385dd7SDoug Smythies
2348385dd7SDoug Smythies    see print_help(): for Usage and Output details
2448385dd7SDoug Smythies
2548385dd7SDoug Smythies"""
26*e68cb15bSDoug Smythies
2748385dd7SDoug Smythiesfrom datetime import datetime
2848385dd7SDoug Smythiesimport subprocess
2948385dd7SDoug Smythiesimport os
3048385dd7SDoug Smythiesimport time
3148385dd7SDoug Smythiesimport re
3235459105SDoug Smythiesimport signal
3348385dd7SDoug Smythiesimport sys
3448385dd7SDoug Smythiesimport getopt
3548385dd7SDoug Smythiesimport Gnuplot
3648385dd7SDoug Smythiesfrom numpy import *
3748385dd7SDoug Smythiesfrom decimal import *
3848385dd7SDoug Smythies
3948385dd7SDoug Smythies__author__ = "Srinivas Pandruvada"
4048385dd7SDoug Smythies__copyright__ = " Copyright (c) 2017, Intel Corporation. "
4148385dd7SDoug Smythies__license__ = "GPL version 2"
4248385dd7SDoug Smythies
4348385dd7SDoug Smythies
4448385dd7SDoug SmythiesMAX_CPUS = 256
4548385dd7SDoug Smythies
4648385dd7SDoug Smythies# Define the csv file columns
4748385dd7SDoug SmythiesC_COMM = 18
4848385dd7SDoug SmythiesC_GHZ = 17
4948385dd7SDoug SmythiesC_ELAPSED = 16
5048385dd7SDoug SmythiesC_SAMPLE = 15
5148385dd7SDoug SmythiesC_DURATION = 14
5248385dd7SDoug SmythiesC_LOAD = 13
5348385dd7SDoug SmythiesC_BOOST = 12
5448385dd7SDoug SmythiesC_FREQ = 11
5548385dd7SDoug SmythiesC_TSC = 10
5648385dd7SDoug SmythiesC_APERF = 9
5748385dd7SDoug SmythiesC_MPERF = 8
5848385dd7SDoug SmythiesC_TO = 7
5948385dd7SDoug SmythiesC_FROM = 6
6048385dd7SDoug SmythiesC_SCALED = 5
6148385dd7SDoug SmythiesC_CORE = 4
6248385dd7SDoug SmythiesC_USEC = 3
6348385dd7SDoug SmythiesC_SEC = 2
6448385dd7SDoug SmythiesC_CPU = 1
6548385dd7SDoug Smythies
66ab3ff9f1SJinzhou Suglobal sample_num, last_sec_cpu, last_usec_cpu, start_time, testname, trace_file
6748385dd7SDoug Smythies
6848385dd7SDoug Smythies# 11 digits covers uptime to 115 days
6948385dd7SDoug Smythiesgetcontext().prec = 11
7048385dd7SDoug Smythies
7148385dd7SDoug Smythiessample_num =0
7248385dd7SDoug Smythieslast_sec_cpu = [0] * MAX_CPUS
7348385dd7SDoug Smythieslast_usec_cpu = [0] * MAX_CPUS
7448385dd7SDoug Smythies
75ab3ff9f1SJinzhou Sudef print_help(driver_name):
76ab3ff9f1SJinzhou Su    print('%s_tracer.py:'%driver_name)
7748385dd7SDoug Smythies    print('  Usage:')
7848385dd7SDoug Smythies    print('    If the trace file is available, then to simply parse and plot, use (sudo not required):')
79ab3ff9f1SJinzhou Su    print('      ./%s_tracer.py [-c cpus] -t <trace_file> -n <test_name>'%driver_name)
8048385dd7SDoug Smythies    print('    Or')
81ab3ff9f1SJinzhou Su    print('      ./%s_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>'%driver_name)
8248385dd7SDoug Smythies    print('    To generate trace file, parse and plot, use (sudo required):')
83ab3ff9f1SJinzhou Su    print('      sudo ./%s_tracer.py [-c cpus] -i <interval> -n <test_name> -m <kbytes>'%driver_name)
8448385dd7SDoug Smythies    print('    Or')
85ab3ff9f1SJinzhou Su    print('      sudo ./%s_tracer.py [--cpu cpus] --interval <interval> --name <test_name> --memory <kbytes>'%driver_name)
8648385dd7SDoug Smythies    print('    Optional argument:')
8748385dd7SDoug Smythies    print('      cpus:   comma separated list of CPUs')
8835459105SDoug Smythies    print('      kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240')
8948385dd7SDoug Smythies    print('  Output:')
9048385dd7SDoug Smythies    print('    If not already present, creates a "results/test_name" folder in the current working directory with:')
9148385dd7SDoug Smythies    print('      cpu.csv - comma seperated values file with trace contents and some additional calculations.')
9248385dd7SDoug Smythies    print('      cpu???.csv - comma seperated values file for CPU number ???.')
9348385dd7SDoug Smythies    print('      *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.')
9448385dd7SDoug Smythies    print('  Notes:')
9548385dd7SDoug Smythies    print('    Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.')
9648385dd7SDoug Smythies    print('    Maximum number of CPUs is {0:d}. If there are more the script will abort with an error.'.format(MAX_CPUS))
9748385dd7SDoug Smythies    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.')
9848385dd7SDoug Smythies    print('    Empty y range warnings for autoscaled plots can occur and can be ignored.')
9948385dd7SDoug Smythies
10048385dd7SDoug Smythiesdef plot_perf_busy_with_sample(cpu_index):
10148385dd7SDoug Smythies    """ Plot method to per cpu information """
10248385dd7SDoug Smythies
10348385dd7SDoug Smythies    file_name = 'cpu{:0>3}.csv'.format(cpu_index)
10448385dd7SDoug Smythies    if os.path.exists(file_name):
10548385dd7SDoug Smythies        output_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index
10648385dd7SDoug Smythies        g_plot = common_all_gnuplot_settings(output_png)
107709bd70dSDoug Smythies#   autoscale this one, no set y1 range
10848385dd7SDoug Smythies        g_plot('set y2range [0:200]')
10948385dd7SDoug Smythies        g_plot('set y2tics 0, 10')
11048385dd7SDoug Smythies        g_plot('set title "{} : cpu perf busy vs. sample : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
11148385dd7SDoug Smythies#       Override common
11248385dd7SDoug Smythies        g_plot('set xlabel "Samples"')
11348385dd7SDoug Smythies        g_plot('set ylabel "P-State"')
11448385dd7SDoug Smythies        g_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
11548385dd7SDoug Smythies        set_4_plot_linestyles(g_plot)
11648385dd7SDoug Smythies        g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_SAMPLE, C_CORE))
11748385dd7SDoug Smythies        g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE, C_SCALED))
11848385dd7SDoug Smythies        g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST))
11948385dd7SDoug Smythies        g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO))
12048385dd7SDoug Smythies
12148385dd7SDoug Smythiesdef plot_perf_busy(cpu_index):
12248385dd7SDoug Smythies    """ Plot some per cpu information """
12348385dd7SDoug Smythies
12448385dd7SDoug Smythies    file_name = 'cpu{:0>3}.csv'.format(cpu_index)
12548385dd7SDoug Smythies    if os.path.exists(file_name):
12648385dd7SDoug Smythies        output_png = "cpu%03d_perf_busy.png" % cpu_index
12748385dd7SDoug Smythies        g_plot = common_all_gnuplot_settings(output_png)
128709bd70dSDoug Smythies#   autoscale this one, no set y1 range
12948385dd7SDoug Smythies        g_plot('set y2range [0:200]')
13048385dd7SDoug Smythies        g_plot('set y2tics 0, 10')
13148385dd7SDoug Smythies        g_plot('set title "{} : perf busy : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
13248385dd7SDoug Smythies        g_plot('set ylabel "P-State"')
13348385dd7SDoug Smythies        g_plot('set y2label "Scaled Busy/performance/io-busy(%)"')
13448385dd7SDoug Smythies        set_4_plot_linestyles(g_plot)
13548385dd7SDoug Smythies        g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_ELAPSED, C_CORE))
13648385dd7SDoug Smythies        g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED, C_SCALED))
13748385dd7SDoug Smythies        g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED, C_BOOST))
13848385dd7SDoug Smythies        g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO))
13948385dd7SDoug Smythies
14048385dd7SDoug Smythiesdef plot_durations(cpu_index):
14148385dd7SDoug Smythies    """ Plot per cpu durations """
14248385dd7SDoug Smythies
14348385dd7SDoug Smythies    file_name = 'cpu{:0>3}.csv'.format(cpu_index)
14448385dd7SDoug Smythies    if os.path.exists(file_name):
14548385dd7SDoug Smythies        output_png = "cpu%03d_durations.png" % cpu_index
14648385dd7SDoug Smythies        g_plot = common_all_gnuplot_settings(output_png)
147709bd70dSDoug Smythies#       autoscale this one, no set y range
14848385dd7SDoug Smythies        g_plot('set title "{} : durations : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
14948385dd7SDoug Smythies        g_plot('set ylabel "Timer Duration (MilliSeconds)"')
15048385dd7SDoug Smythies#       override common
15148385dd7SDoug Smythies        g_plot('set key off')
15248385dd7SDoug Smythies        set_4_plot_linestyles(g_plot)
15348385dd7SDoug Smythies        g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION))
15448385dd7SDoug Smythies
15548385dd7SDoug Smythiesdef plot_loads(cpu_index):
15648385dd7SDoug Smythies    """ Plot per cpu loads """
15748385dd7SDoug Smythies
15848385dd7SDoug Smythies    file_name = 'cpu{:0>3}.csv'.format(cpu_index)
15948385dd7SDoug Smythies    if os.path.exists(file_name):
16048385dd7SDoug Smythies        output_png = "cpu%03d_loads.png" % cpu_index
16148385dd7SDoug Smythies        g_plot = common_all_gnuplot_settings(output_png)
16248385dd7SDoug Smythies        g_plot('set yrange [0:100]')
16348385dd7SDoug Smythies        g_plot('set ytics 0, 10')
16448385dd7SDoug Smythies        g_plot('set title "{} : loads : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now()))
16548385dd7SDoug Smythies        g_plot('set ylabel "CPU load (percent)"')
16648385dd7SDoug Smythies#       override common
16748385dd7SDoug Smythies        g_plot('set key off')
16848385dd7SDoug Smythies        set_4_plot_linestyles(g_plot)
16948385dd7SDoug Smythies        g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD))
17048385dd7SDoug Smythies
17148385dd7SDoug Smythiesdef plot_pstate_cpu_with_sample():
17248385dd7SDoug Smythies    """ Plot all cpu information """
17348385dd7SDoug Smythies
17448385dd7SDoug Smythies    if os.path.exists('cpu.csv'):
17548385dd7SDoug Smythies        output_png = 'all_cpu_pstates_vs_samples.png'
17648385dd7SDoug Smythies        g_plot = common_all_gnuplot_settings(output_png)
177709bd70dSDoug Smythies#       autoscale this one, no set y range
17848385dd7SDoug Smythies#       override common
17948385dd7SDoug Smythies        g_plot('set xlabel "Samples"')
18048385dd7SDoug Smythies        g_plot('set ylabel "P-State"')
18148385dd7SDoug Smythies        g_plot('set title "{} : cpu pstate vs. sample : {:%F %H:%M}"'.format(testname, datetime.now()))
182e749e09dSDoug Smythies        title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
18348385dd7SDoug Smythies        plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO)
18448385dd7SDoug Smythies        g_plot('title_list = "{}"'.format(title_list))
18548385dd7SDoug Smythies        g_plot(plot_str)
18648385dd7SDoug Smythies
18748385dd7SDoug Smythiesdef plot_pstate_cpu():
18848385dd7SDoug Smythies    """ Plot all cpu information from csv files """
18948385dd7SDoug Smythies
19048385dd7SDoug Smythies    output_png = 'all_cpu_pstates.png'
19148385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
192709bd70dSDoug Smythies#   autoscale this one, no set y range
19348385dd7SDoug Smythies    g_plot('set ylabel "P-State"')
19448385dd7SDoug Smythies    g_plot('set title "{} : cpu pstates : {:%F %H:%M}"'.format(testname, datetime.now()))
19548385dd7SDoug Smythies
19648385dd7SDoug Smythies#    the following command is really cool, but doesn't work with the CPU masking option because it aborts on the first missing file.
19748385dd7SDoug Smythies#    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'
19848385dd7SDoug Smythies#
199e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
20048385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_TO)
20148385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
20248385dd7SDoug Smythies    g_plot(plot_str)
20348385dd7SDoug Smythies
20448385dd7SDoug Smythiesdef plot_load_cpu():
20548385dd7SDoug Smythies    """ Plot all cpu loads """
20648385dd7SDoug Smythies
20748385dd7SDoug Smythies    output_png = 'all_cpu_loads.png'
20848385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
20948385dd7SDoug Smythies    g_plot('set yrange [0:100]')
21048385dd7SDoug Smythies    g_plot('set ylabel "CPU load (percent)"')
21148385dd7SDoug Smythies    g_plot('set title "{} : cpu loads : {:%F %H:%M}"'.format(testname, datetime.now()))
21248385dd7SDoug Smythies
213e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
21448385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD)
21548385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
21648385dd7SDoug Smythies    g_plot(plot_str)
21748385dd7SDoug Smythies
21848385dd7SDoug Smythiesdef plot_frequency_cpu():
21948385dd7SDoug Smythies    """ Plot all cpu frequencies """
22048385dd7SDoug Smythies
22148385dd7SDoug Smythies    output_png = 'all_cpu_frequencies.png'
22248385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
223709bd70dSDoug Smythies#   autoscale this one, no set y range
22448385dd7SDoug Smythies    g_plot('set ylabel "CPU Frequency (GHz)"')
22548385dd7SDoug Smythies    g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(testname, datetime.now()))
22648385dd7SDoug Smythies
227e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
22848385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ)
22948385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
23048385dd7SDoug Smythies    g_plot(plot_str)
23148385dd7SDoug Smythies
23248385dd7SDoug Smythiesdef plot_duration_cpu():
23348385dd7SDoug Smythies    """ Plot all cpu durations """
23448385dd7SDoug Smythies
23548385dd7SDoug Smythies    output_png = 'all_cpu_durations.png'
23648385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
237709bd70dSDoug Smythies#   autoscale this one, no set y range
23848385dd7SDoug Smythies    g_plot('set ylabel "Timer Duration (MilliSeconds)"')
23948385dd7SDoug Smythies    g_plot('set title "{} : cpu durations : {:%F %H:%M}"'.format(testname, datetime.now()))
24048385dd7SDoug Smythies
241e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
24248385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION)
24348385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
24448385dd7SDoug Smythies    g_plot(plot_str)
24548385dd7SDoug Smythies
24648385dd7SDoug Smythiesdef plot_scaled_cpu():
24748385dd7SDoug Smythies    """ Plot all cpu scaled busy """
24848385dd7SDoug Smythies
24948385dd7SDoug Smythies    output_png = 'all_cpu_scaled.png'
25048385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
25148385dd7SDoug Smythies#   autoscale this one, no set y range
25248385dd7SDoug Smythies    g_plot('set ylabel "Scaled Busy (Unitless)"')
25348385dd7SDoug Smythies    g_plot('set title "{} : cpu scaled busy : {:%F %H:%M}"'.format(testname, datetime.now()))
25448385dd7SDoug Smythies
255e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
25648385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED)
25748385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
25848385dd7SDoug Smythies    g_plot(plot_str)
25948385dd7SDoug Smythies
26048385dd7SDoug Smythiesdef plot_boost_cpu():
26148385dd7SDoug Smythies    """ Plot all cpu IO Boosts """
26248385dd7SDoug Smythies
26348385dd7SDoug Smythies    output_png = 'all_cpu_boost.png'
26448385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
26548385dd7SDoug Smythies    g_plot('set yrange [0:100]')
26648385dd7SDoug Smythies    g_plot('set ylabel "CPU IO Boost (percent)"')
26748385dd7SDoug Smythies    g_plot('set title "{} : cpu io boost : {:%F %H:%M}"'.format(testname, datetime.now()))
26848385dd7SDoug Smythies
269e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
27048385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST)
27148385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
27248385dd7SDoug Smythies    g_plot(plot_str)
27348385dd7SDoug Smythies
27448385dd7SDoug Smythiesdef plot_ghz_cpu():
27548385dd7SDoug Smythies    """ Plot all cpu tsc ghz """
27648385dd7SDoug Smythies
27748385dd7SDoug Smythies    output_png = 'all_cpu_ghz.png'
27848385dd7SDoug Smythies    g_plot = common_all_gnuplot_settings(output_png)
27948385dd7SDoug Smythies#   autoscale this one, no set y range
28048385dd7SDoug Smythies    g_plot('set ylabel "TSC Frequency (GHz)"')
28148385dd7SDoug Smythies    g_plot('set title "{} : cpu TSC Frequencies (Sanity check calculation) : {:%F %H:%M}"'.format(testname, datetime.now()))
28248385dd7SDoug Smythies
283e749e09dSDoug Smythies    title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
28448385dd7SDoug Smythies    plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ)
28548385dd7SDoug Smythies    g_plot('title_list = "{}"'.format(title_list))
28648385dd7SDoug Smythies    g_plot(plot_str)
28748385dd7SDoug Smythies
28848385dd7SDoug Smythiesdef common_all_gnuplot_settings(output_png):
28948385dd7SDoug Smythies    """ common gnuplot settings for multiple CPUs one one graph. """
29048385dd7SDoug Smythies
29148385dd7SDoug Smythies    g_plot = common_gnuplot_settings()
29248385dd7SDoug Smythies    g_plot('set output "' + output_png + '"')
29348385dd7SDoug Smythies    return(g_plot)
29448385dd7SDoug Smythies
29548385dd7SDoug Smythiesdef common_gnuplot_settings():
29648385dd7SDoug Smythies    """ common gnuplot settings. """
29748385dd7SDoug Smythies
29848385dd7SDoug Smythies    g_plot = Gnuplot.Gnuplot(persist=1)
29948385dd7SDoug Smythies#   The following line is for rigor only. It seems to be assumed for .csv files
30048385dd7SDoug Smythies    g_plot('set datafile separator \",\"')
30148385dd7SDoug Smythies    g_plot('set ytics nomirror')
30248385dd7SDoug Smythies    g_plot('set xtics nomirror')
30348385dd7SDoug Smythies    g_plot('set xtics font ", 10"')
30448385dd7SDoug Smythies    g_plot('set ytics font ", 10"')
30548385dd7SDoug Smythies    g_plot('set tics out scale 1.0')
30648385dd7SDoug Smythies    g_plot('set grid')
30748385dd7SDoug Smythies    g_plot('set key out horiz')
30848385dd7SDoug Smythies    g_plot('set key bot center')
30948385dd7SDoug Smythies    g_plot('set key samplen 2 spacing .8 font ", 9"')
31048385dd7SDoug Smythies    g_plot('set term png size 1200, 600')
31148385dd7SDoug Smythies    g_plot('set title font ", 11"')
31248385dd7SDoug Smythies    g_plot('set ylabel font ", 10"')
31348385dd7SDoug Smythies    g_plot('set xlabel font ", 10"')
31448385dd7SDoug Smythies    g_plot('set xlabel offset 0, 0.5')
31548385dd7SDoug Smythies    g_plot('set xlabel "Elapsed Time (Seconds)"')
31648385dd7SDoug Smythies    return(g_plot)
31748385dd7SDoug Smythies
31848385dd7SDoug Smythiesdef set_4_plot_linestyles(g_plot):
31948385dd7SDoug Smythies    """ set the linestyles used for 4 plots in 1 graphs. """
32048385dd7SDoug Smythies
32148385dd7SDoug Smythies    g_plot('set style line 1 linetype 1 linecolor rgb "green" pointtype -1')
32248385dd7SDoug Smythies    g_plot('set style line 2 linetype 1 linecolor rgb "red" pointtype -1')
32348385dd7SDoug Smythies    g_plot('set style line 3 linetype 1 linecolor rgb "purple" pointtype -1')
32448385dd7SDoug Smythies    g_plot('set style line 4 linetype 1 linecolor rgb "blue" pointtype -1')
32548385dd7SDoug Smythies
326ab3ff9f1SJinzhou Sudef 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, cpu_mask):
32748385dd7SDoug Smythies    """ Store master csv file information """
32848385dd7SDoug Smythies
32948385dd7SDoug Smythies    global graph_data_present
33048385dd7SDoug Smythies
33148385dd7SDoug Smythies    if cpu_mask[cpu_int] == 0:
33248385dd7SDoug Smythies        return
33348385dd7SDoug Smythies
33448385dd7SDoug Smythies    try:
33548385dd7SDoug Smythies        f_handle = open('cpu.csv', 'a')
33648385dd7SDoug Smythies        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)
33748385dd7SDoug Smythies        f_handle.write(string_buffer);
33848385dd7SDoug Smythies        f_handle.close()
33948385dd7SDoug Smythies    except:
34048385dd7SDoug Smythies        print('IO error cpu.csv')
34148385dd7SDoug Smythies        return
34248385dd7SDoug Smythies
34348385dd7SDoug Smythies    graph_data_present = True;
34448385dd7SDoug Smythies
345ab3ff9f1SJinzhou Sudef split_csv(current_max_cpu, cpu_mask):
34648385dd7SDoug Smythies    """ seperate the all csv file into per CPU csv files. """
34748385dd7SDoug Smythies
34848385dd7SDoug Smythies    if os.path.exists('cpu.csv'):
34948385dd7SDoug Smythies        for index in range(0, current_max_cpu + 1):
35048385dd7SDoug Smythies            if cpu_mask[int(index)] != 0:
35148385dd7SDoug Smythies                os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index))
35248385dd7SDoug Smythies                os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index))
35348385dd7SDoug Smythies
354010a522cSDoug Smythiesdef fix_ownership(path):
355010a522cSDoug Smythies    """Change the owner of the file to SUDO_UID, if required"""
356010a522cSDoug Smythies
357010a522cSDoug Smythies    uid = os.environ.get('SUDO_UID')
358010a522cSDoug Smythies    gid = os.environ.get('SUDO_GID')
359010a522cSDoug Smythies    if uid is not None:
360010a522cSDoug Smythies        os.chown(path, int(uid), int(gid))
361010a522cSDoug Smythies
36248385dd7SDoug Smythiesdef cleanup_data_files():
36348385dd7SDoug Smythies    """ clean up existing data files """
36448385dd7SDoug Smythies
36548385dd7SDoug Smythies    if os.path.exists('cpu.csv'):
36648385dd7SDoug Smythies        os.remove('cpu.csv')
36748385dd7SDoug Smythies    f_handle = open('cpu.csv', 'a')
36848385dd7SDoug Smythies    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')
36948385dd7SDoug Smythies    f_handle.write('\n')
37048385dd7SDoug Smythies    f_handle.close()
37148385dd7SDoug Smythies
37248385dd7SDoug Smythiesdef clear_trace_file():
37348385dd7SDoug Smythies    """ Clear trace file """
37448385dd7SDoug Smythies
37548385dd7SDoug Smythies    try:
376cf3e0251SRoss Zwisler        f_handle = open('/sys/kernel/tracing/trace', 'w')
37748385dd7SDoug Smythies        f_handle.close()
37848385dd7SDoug Smythies    except:
37948385dd7SDoug Smythies        print('IO error clearing trace file ')
38035459105SDoug Smythies        sys.exit(2)
38148385dd7SDoug Smythies
382ab3ff9f1SJinzhou Sudef enable_trace(trace_file):
38348385dd7SDoug Smythies    """ Enable trace """
38448385dd7SDoug Smythies
38548385dd7SDoug Smythies    try:
386ab3ff9f1SJinzhou Su        open(trace_file,'w').write("1")
38748385dd7SDoug Smythies    except:
38848385dd7SDoug Smythies        print('IO error enabling trace ')
38935459105SDoug Smythies        sys.exit(2)
39048385dd7SDoug Smythies
391ab3ff9f1SJinzhou Sudef disable_trace(trace_file):
39248385dd7SDoug Smythies    """ Disable trace """
39348385dd7SDoug Smythies
39448385dd7SDoug Smythies    try:
395ab3ff9f1SJinzhou Su       open(trace_file, 'w').write("0")
39648385dd7SDoug Smythies    except:
39748385dd7SDoug Smythies        print('IO error disabling trace ')
39835459105SDoug Smythies        sys.exit(2)
39948385dd7SDoug Smythies
400ab3ff9f1SJinzhou Sudef set_trace_buffer_size(memory):
40148385dd7SDoug Smythies    """ Set trace buffer size """
40248385dd7SDoug Smythies
40348385dd7SDoug Smythies    try:
404cf3e0251SRoss Zwisler       with open('/sys/kernel/tracing/buffer_size_kb', 'w') as fp:
40535459105SDoug Smythies          fp.write(memory)
40648385dd7SDoug Smythies    except:
40748385dd7SDoug Smythies       print('IO error setting trace buffer size ')
40835459105SDoug Smythies       sys.exit(2)
40948385dd7SDoug Smythies
410fbe31388SDoug Smythiesdef free_trace_buffer():
411fbe31388SDoug Smythies    """ Free the trace buffer memory """
412fbe31388SDoug Smythies
413fbe31388SDoug Smythies    try:
414cf3e0251SRoss Zwisler       open('/sys/kernel/tracing/buffer_size_kb'
415fbe31388SDoug Smythies                 , 'w').write("1")
416fbe31388SDoug Smythies    except:
41735459105SDoug Smythies        print('IO error freeing trace buffer ')
41835459105SDoug Smythies        sys.exit(2)
419fbe31388SDoug Smythies
420ab3ff9f1SJinzhou Sudef read_trace_data(filename, cpu_mask):
42148385dd7SDoug Smythies    """ Read and parse trace data """
42248385dd7SDoug Smythies
42348385dd7SDoug Smythies    global current_max_cpu
42448385dd7SDoug Smythies    global sample_num, last_sec_cpu, last_usec_cpu, start_time
42548385dd7SDoug Smythies
42648385dd7SDoug Smythies    try:
42748385dd7SDoug Smythies        data = open(filename, 'r').read()
42848385dd7SDoug Smythies    except:
42948385dd7SDoug Smythies        print('Error opening ', filename)
43035459105SDoug Smythies        sys.exit(2)
43148385dd7SDoug Smythies
43248385dd7SDoug Smythies    for line in data.splitlines():
43348385dd7SDoug Smythies        search_obj = \
43448385dd7SDoug Smythies            re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)'
43548385dd7SDoug Smythies                      , line)
43648385dd7SDoug Smythies
43748385dd7SDoug Smythies        if search_obj:
43848385dd7SDoug Smythies            cpu = search_obj.group(3)
43948385dd7SDoug Smythies            cpu_int = int(cpu)
44048385dd7SDoug Smythies            cpu = str(cpu_int)
44148385dd7SDoug Smythies
44248385dd7SDoug Smythies            time_pre_dec = search_obj.group(6)
44348385dd7SDoug Smythies            time_post_dec = search_obj.group(8)
44448385dd7SDoug Smythies            core_busy = search_obj.group(10)
44548385dd7SDoug Smythies            scaled = search_obj.group(12)
44648385dd7SDoug Smythies            _from = search_obj.group(14)
44748385dd7SDoug Smythies            _to = search_obj.group(16)
44848385dd7SDoug Smythies            mperf = search_obj.group(18)
44948385dd7SDoug Smythies            aperf = search_obj.group(20)
45048385dd7SDoug Smythies            tsc = search_obj.group(22)
45148385dd7SDoug Smythies            freq = search_obj.group(24)
45248385dd7SDoug Smythies            common_comm = search_obj.group(2).replace(' ', '')
45348385dd7SDoug Smythies
45448385dd7SDoug Smythies            # Not all kernel versions have io_boost field
45548385dd7SDoug Smythies            io_boost = '0'
45648385dd7SDoug Smythies            search_obj = re.search(r'.*?io_boost=(\d+)', line)
45748385dd7SDoug Smythies            if search_obj:
45848385dd7SDoug Smythies                io_boost = search_obj.group(1)
45948385dd7SDoug Smythies
46048385dd7SDoug Smythies            if sample_num == 0 :
46148385dd7SDoug Smythies                start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000)
46248385dd7SDoug Smythies            sample_num += 1
46348385dd7SDoug Smythies
46448385dd7SDoug Smythies            if last_sec_cpu[cpu_int] == 0 :
46548385dd7SDoug Smythies                last_sec_cpu[cpu_int] = time_pre_dec
46648385dd7SDoug Smythies                last_usec_cpu[cpu_int] = time_post_dec
46748385dd7SDoug Smythies            else :
46848385dd7SDoug Smythies                duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int]))
46948385dd7SDoug Smythies                duration_ms = Decimal(duration_us) / Decimal(1000)
47048385dd7SDoug Smythies                last_sec_cpu[cpu_int] = time_pre_dec
47148385dd7SDoug Smythies                last_usec_cpu[cpu_int] = time_post_dec
47248385dd7SDoug Smythies                elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time
47348385dd7SDoug Smythies                load = Decimal(int(mperf)*100)/ Decimal(tsc)
47448385dd7SDoug Smythies                freq_ghz = Decimal(freq)/Decimal(1000000)
47548385dd7SDoug Smythies#               Sanity check calculation, typically anomalies indicate missed samples
47648385dd7SDoug Smythies#               However, check for 0 (should never occur)
47748385dd7SDoug Smythies                tsc_ghz = Decimal(0)
47848385dd7SDoug Smythies                if duration_ms != Decimal(0) :
47948385dd7SDoug Smythies                    tsc_ghz = Decimal(tsc)/duration_ms/Decimal(1000000)
480ab3ff9f1SJinzhou Su                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, cpu_mask)
48148385dd7SDoug Smythies
48248385dd7SDoug Smythies            if cpu_int > current_max_cpu:
48348385dd7SDoug Smythies                current_max_cpu = cpu_int
48448385dd7SDoug Smythies# End of for each trace line loop
48548385dd7SDoug Smythies# Now seperate the main overall csv file into per CPU csv files.
486ab3ff9f1SJinzhou Su    split_csv(current_max_cpu, cpu_mask)
48748385dd7SDoug Smythies
48835459105SDoug Smythiesdef signal_handler(signal, frame):
48935459105SDoug Smythies    print(' SIGINT: Forcing cleanup before exit.')
49035459105SDoug Smythies    if interval:
491ab3ff9f1SJinzhou Su        disable_trace(trace_file)
49235459105SDoug Smythies        clear_trace_file()
49335459105SDoug Smythies        # Free the memory
49435459105SDoug Smythies        free_trace_buffer()
49535459105SDoug Smythies        sys.exit(0)
49635459105SDoug Smythies
497ab3ff9f1SJinzhou Suif __name__ == "__main__":
498cf3e0251SRoss Zwisler    trace_file = "/sys/kernel/tracing/events/power/pstate_sample/enable"
49935459105SDoug Smythies    signal.signal(signal.SIGINT, signal_handler)
50035459105SDoug Smythies
50148385dd7SDoug Smythies    interval = ""
50248385dd7SDoug Smythies    filename = ""
50348385dd7SDoug Smythies    cpu_list = ""
50448385dd7SDoug Smythies    testname = ""
50535459105SDoug Smythies    memory = "10240"
50648385dd7SDoug Smythies    graph_data_present = False;
50748385dd7SDoug Smythies
50848385dd7SDoug Smythies    valid1 = False
50948385dd7SDoug Smythies    valid2 = False
51048385dd7SDoug Smythies
51148385dd7SDoug Smythies    cpu_mask = zeros((MAX_CPUS,), dtype=int)
51248385dd7SDoug Smythies
51348385dd7SDoug Smythies    try:
51435459105SDoug Smythies        opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="])
51548385dd7SDoug Smythies    except getopt.GetoptError:
516ab3ff9f1SJinzhou Su        print_help('intel_pstate')
51748385dd7SDoug Smythies        sys.exit(2)
51848385dd7SDoug Smythies    for opt, arg in opts:
51948385dd7SDoug Smythies        if opt == '-h':
520ab3ff9f1SJinzhou Su            print_help('intel_pstate')
52148385dd7SDoug Smythies            sys.exit()
52248385dd7SDoug Smythies        elif opt in ("-t", "--trace_file"):
52348385dd7SDoug Smythies            valid1 = True
52448385dd7SDoug Smythies            location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
52548385dd7SDoug Smythies            filename = os.path.join(location, arg)
52648385dd7SDoug Smythies        elif opt in ("-i", "--interval"):
52748385dd7SDoug Smythies            valid1 = True
52848385dd7SDoug Smythies            interval = arg
52948385dd7SDoug Smythies        elif opt in ("-c", "--cpu"):
53048385dd7SDoug Smythies            cpu_list = arg
53148385dd7SDoug Smythies        elif opt in ("-n", "--name"):
53248385dd7SDoug Smythies            valid2 = True
53348385dd7SDoug Smythies            testname = arg
53435459105SDoug Smythies        elif opt in ("-m", "--memory"):
53535459105SDoug Smythies            memory = arg
53648385dd7SDoug Smythies
53748385dd7SDoug Smythies    if not (valid1 and valid2):
538ab3ff9f1SJinzhou Su        print_help('intel_pstate')
53948385dd7SDoug Smythies        sys.exit()
54048385dd7SDoug Smythies
54148385dd7SDoug Smythies    if cpu_list:
54248385dd7SDoug Smythies        for p in re.split("[,]", cpu_list):
54348385dd7SDoug Smythies            if int(p) < MAX_CPUS :
54448385dd7SDoug Smythies                cpu_mask[int(p)] = 1
54548385dd7SDoug Smythies    else:
54648385dd7SDoug Smythies        for i in range (0, MAX_CPUS):
54748385dd7SDoug Smythies            cpu_mask[i] = 1
54848385dd7SDoug Smythies
54948385dd7SDoug Smythies    if not os.path.exists('results'):
55048385dd7SDoug Smythies        os.mkdir('results')
551010a522cSDoug Smythies        # The regular user needs to own the directory, not root.
552010a522cSDoug Smythies        fix_ownership('results')
55348385dd7SDoug Smythies
55448385dd7SDoug Smythies    os.chdir('results')
55548385dd7SDoug Smythies    if os.path.exists(testname):
55648385dd7SDoug Smythies        print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
55748385dd7SDoug Smythies        sys.exit()
55848385dd7SDoug Smythies    os.mkdir(testname)
559010a522cSDoug Smythies    # The regular user needs to own the directory, not root.
560010a522cSDoug Smythies    fix_ownership(testname)
56148385dd7SDoug Smythies    os.chdir(testname)
56248385dd7SDoug Smythies
56348385dd7SDoug Smythies    # Temporary (or perhaps not)
56448385dd7SDoug Smythies    cur_version = sys.version_info
565*e68cb15bSDoug Smythies    print('python version (should be >= 3.6):')
56648385dd7SDoug Smythies    print(cur_version)
56748385dd7SDoug Smythies
56848385dd7SDoug Smythies    # Left as "cleanup" for potential future re-run ability.
56948385dd7SDoug Smythies    cleanup_data_files()
57048385dd7SDoug Smythies
57148385dd7SDoug Smythies    if interval:
572cf3e0251SRoss Zwisler        filename = "/sys/kernel/tracing/trace"
57348385dd7SDoug Smythies        clear_trace_file()
574ab3ff9f1SJinzhou Su        set_trace_buffer_size(memory)
575ab3ff9f1SJinzhou Su        enable_trace(trace_file)
57648385dd7SDoug Smythies        print('Sleeping for ', interval, 'seconds')
57748385dd7SDoug Smythies        time.sleep(int(interval))
578ab3ff9f1SJinzhou Su        disable_trace(trace_file)
57948385dd7SDoug Smythies
58048385dd7SDoug Smythies    current_max_cpu = 0
58148385dd7SDoug Smythies
582ab3ff9f1SJinzhou Su    read_trace_data(filename, cpu_mask)
58348385dd7SDoug Smythies
58466354690SDoug Smythies    if interval:
58535459105SDoug Smythies        clear_trace_file()
58635459105SDoug Smythies        # Free the memory
58735459105SDoug Smythies        free_trace_buffer()
58835459105SDoug Smythies
58948385dd7SDoug Smythies    if graph_data_present == False:
59048385dd7SDoug Smythies        print('No valid data to plot')
59148385dd7SDoug Smythies        sys.exit(2)
59248385dd7SDoug Smythies
59348385dd7SDoug Smythies    for cpu_no in range(0, current_max_cpu + 1):
59448385dd7SDoug Smythies        plot_perf_busy_with_sample(cpu_no)
59548385dd7SDoug Smythies        plot_perf_busy(cpu_no)
59648385dd7SDoug Smythies        plot_durations(cpu_no)
59748385dd7SDoug Smythies        plot_loads(cpu_no)
59848385dd7SDoug Smythies
59948385dd7SDoug Smythies    plot_pstate_cpu_with_sample()
60048385dd7SDoug Smythies    plot_pstate_cpu()
60148385dd7SDoug Smythies    plot_load_cpu()
60248385dd7SDoug Smythies    plot_frequency_cpu()
60348385dd7SDoug Smythies    plot_duration_cpu()
60448385dd7SDoug Smythies    plot_scaled_cpu()
60548385dd7SDoug Smythies    plot_boost_cpu()
60648385dd7SDoug Smythies    plot_ghz_cpu()
60748385dd7SDoug Smythies
608010a522cSDoug Smythies    # It is preferrable, but not necessary, that the regular user owns the files, not root.
609010a522cSDoug Smythies    for root, dirs, files in os.walk('.'):
610010a522cSDoug Smythies        for f in files:
611010a522cSDoug Smythies            fix_ownership(f)
612010a522cSDoug Smythies
61348385dd7SDoug Smythies    os.chdir('../../')
614