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