1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0-only 3# -*- coding: utf-8 -*- 4# 5""" This utility can be used to debug and tune the performance of the 6intel_pstate driver. This utility can be used in two ways: 7- If there is Linux trace file with pstate_sample events enabled, then 8this utility can parse the trace file and generate performance plots. 9- If user has not specified a trace file as input via command line parameters, 10then this utility enables and collects trace data for a user specified interval 11and generates performance plots. 12 13Prerequisites: 14 Python version 3.6.x or higher 15 gnuplot 5.0 or higher 16 python3-gnuplot 1.8 or higher 17 (Most of the distributions have these required packages. They may be called 18 gnuplot-py, python-gnuplot or python3-gnuplot, gnuplot-nox, ... ) 19 20 HWP (Hardware P-States are disabled) 21 Kernel config for Linux trace is enabled 22 23 see print_help(): for Usage and Output details 24 25""" 26 27from datetime import datetime 28import subprocess 29import os 30import time 31import re 32import signal 33import sys 34import getopt 35import Gnuplot 36from numpy import * 37from decimal import * 38 39__author__ = "Srinivas Pandruvada" 40__copyright__ = " Copyright (c) 2017, Intel Corporation. " 41__license__ = "GPL version 2" 42 43 44MAX_CPUS = 256 45 46# Define the csv file columns 47C_COMM = 18 48C_GHZ = 17 49C_ELAPSED = 16 50C_SAMPLE = 15 51C_DURATION = 14 52C_LOAD = 13 53C_BOOST = 12 54C_FREQ = 11 55C_TSC = 10 56C_APERF = 9 57C_MPERF = 8 58C_TO = 7 59C_FROM = 6 60C_SCALED = 5 61C_CORE = 4 62C_USEC = 3 63C_SEC = 2 64C_CPU = 1 65 66global sample_num, last_sec_cpu, last_usec_cpu, start_time, testname, trace_file 67 68# 11 digits covers uptime to 115 days 69getcontext().prec = 11 70 71sample_num =0 72last_sec_cpu = [0] * MAX_CPUS 73last_usec_cpu = [0] * MAX_CPUS 74 75def print_help(driver_name): 76 print('%s_tracer.py:'%driver_name) 77 print(' Usage:') 78 print(' If the trace file is available, then to simply parse and plot, use (sudo not required):') 79 print(' ./%s_tracer.py [-c cpus] -t <trace_file> -n <test_name>'%driver_name) 80 print(' Or') 81 print(' ./%s_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>'%driver_name) 82 print(' To generate trace file, parse and plot, use (sudo required):') 83 print(' sudo ./%s_tracer.py [-c cpus] -i <interval> -n <test_name> -m <kbytes>'%driver_name) 84 print(' Or') 85 print(' sudo ./%s_tracer.py [--cpu cpus] --interval <interval> --name <test_name> --memory <kbytes>'%driver_name) 86 print(' Optional argument:') 87 print(' cpus: comma separated list of CPUs') 88 print(' kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240') 89 print(' Output:') 90 print(' If not already present, creates a "results/test_name" folder in the current working directory with:') 91 print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') 92 print(' cpu???.csv - comma seperated values file for CPU number ???.') 93 print(' *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.') 94 print(' Notes:') 95 print(' Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.') 96 print(' Maximum number of CPUs is {0:d}. If there are more the script will abort with an error.'.format(MAX_CPUS)) 97 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.') 98 print(' Empty y range warnings for autoscaled plots can occur and can be ignored.') 99 100def plot_perf_busy_with_sample(cpu_index): 101 """ Plot method to per cpu information """ 102 103 file_name = 'cpu{:0>3}.csv'.format(cpu_index) 104 if os.path.exists(file_name): 105 output_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index 106 g_plot = common_all_gnuplot_settings(output_png) 107# autoscale this one, no set y1 range 108 g_plot('set y2range [0:200]') 109 g_plot('set y2tics 0, 10') 110 g_plot('set title "{} : cpu perf busy vs. sample : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) 111# Override common 112 g_plot('set xlabel "Samples"') 113 g_plot('set ylabel "P-State"') 114 g_plot('set y2label "Scaled Busy/performance/io-busy(%)"') 115 set_4_plot_linestyles(g_plot) 116 g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_SAMPLE, C_CORE)) 117 g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE, C_SCALED)) 118 g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST)) 119 g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO)) 120 121def plot_perf_busy(cpu_index): 122 """ Plot some per cpu information """ 123 124 file_name = 'cpu{:0>3}.csv'.format(cpu_index) 125 if os.path.exists(file_name): 126 output_png = "cpu%03d_perf_busy.png" % cpu_index 127 g_plot = common_all_gnuplot_settings(output_png) 128# autoscale this one, no set y1 range 129 g_plot('set y2range [0:200]') 130 g_plot('set y2tics 0, 10') 131 g_plot('set title "{} : perf busy : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) 132 g_plot('set ylabel "P-State"') 133 g_plot('set y2label "Scaled Busy/performance/io-busy(%)"') 134 set_4_plot_linestyles(g_plot) 135 g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_ELAPSED, C_CORE)) 136 g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED, C_SCALED)) 137 g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED, C_BOOST)) 138 g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO)) 139 140def plot_durations(cpu_index): 141 """ Plot per cpu durations """ 142 143 file_name = 'cpu{:0>3}.csv'.format(cpu_index) 144 if os.path.exists(file_name): 145 output_png = "cpu%03d_durations.png" % cpu_index 146 g_plot = common_all_gnuplot_settings(output_png) 147# autoscale this one, no set y range 148 g_plot('set title "{} : durations : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) 149 g_plot('set ylabel "Timer Duration (MilliSeconds)"') 150# override common 151 g_plot('set key off') 152 set_4_plot_linestyles(g_plot) 153 g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION)) 154 155def plot_loads(cpu_index): 156 """ Plot per cpu loads """ 157 158 file_name = 'cpu{:0>3}.csv'.format(cpu_index) 159 if os.path.exists(file_name): 160 output_png = "cpu%03d_loads.png" % cpu_index 161 g_plot = common_all_gnuplot_settings(output_png) 162 g_plot('set yrange [0:100]') 163 g_plot('set ytics 0, 10') 164 g_plot('set title "{} : loads : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) 165 g_plot('set ylabel "CPU load (percent)"') 166# override common 167 g_plot('set key off') 168 set_4_plot_linestyles(g_plot) 169 g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD)) 170 171def plot_pstate_cpu_with_sample(): 172 """ Plot all cpu information """ 173 174 if os.path.exists('cpu.csv'): 175 output_png = 'all_cpu_pstates_vs_samples.png' 176 g_plot = common_all_gnuplot_settings(output_png) 177# autoscale this one, no set y range 178# override common 179 g_plot('set xlabel "Samples"') 180 g_plot('set ylabel "P-State"') 181 g_plot('set title "{} : cpu pstate vs. sample : {:%F %H:%M}"'.format(testname, datetime.now())) 182 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 183 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO) 184 g_plot('title_list = "{}"'.format(title_list)) 185 g_plot(plot_str) 186 187def plot_pstate_cpu(): 188 """ Plot all cpu information from csv files """ 189 190 output_png = 'all_cpu_pstates.png' 191 g_plot = common_all_gnuplot_settings(output_png) 192# autoscale this one, no set y range 193 g_plot('set ylabel "P-State"') 194 g_plot('set title "{} : cpu pstates : {:%F %H:%M}"'.format(testname, datetime.now())) 195 196# the following command is really cool, but doesn't work with the CPU masking option because it aborts on the first missing file. 197# 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' 198# 199 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 200 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_TO) 201 g_plot('title_list = "{}"'.format(title_list)) 202 g_plot(plot_str) 203 204def plot_load_cpu(): 205 """ Plot all cpu loads """ 206 207 output_png = 'all_cpu_loads.png' 208 g_plot = common_all_gnuplot_settings(output_png) 209 g_plot('set yrange [0:100]') 210 g_plot('set ylabel "CPU load (percent)"') 211 g_plot('set title "{} : cpu loads : {:%F %H:%M}"'.format(testname, datetime.now())) 212 213 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 214 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD) 215 g_plot('title_list = "{}"'.format(title_list)) 216 g_plot(plot_str) 217 218def plot_frequency_cpu(): 219 """ Plot all cpu frequencies """ 220 221 output_png = 'all_cpu_frequencies.png' 222 g_plot = common_all_gnuplot_settings(output_png) 223# autoscale this one, no set y range 224 g_plot('set ylabel "CPU Frequency (GHz)"') 225 g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(testname, datetime.now())) 226 227 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 228 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ) 229 g_plot('title_list = "{}"'.format(title_list)) 230 g_plot(plot_str) 231 232def plot_duration_cpu(): 233 """ Plot all cpu durations """ 234 235 output_png = 'all_cpu_durations.png' 236 g_plot = common_all_gnuplot_settings(output_png) 237# autoscale this one, no set y range 238 g_plot('set ylabel "Timer Duration (MilliSeconds)"') 239 g_plot('set title "{} : cpu durations : {:%F %H:%M}"'.format(testname, datetime.now())) 240 241 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 242 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION) 243 g_plot('title_list = "{}"'.format(title_list)) 244 g_plot(plot_str) 245 246def plot_scaled_cpu(): 247 """ Plot all cpu scaled busy """ 248 249 output_png = 'all_cpu_scaled.png' 250 g_plot = common_all_gnuplot_settings(output_png) 251# autoscale this one, no set y range 252 g_plot('set ylabel "Scaled Busy (Unitless)"') 253 g_plot('set title "{} : cpu scaled busy : {:%F %H:%M}"'.format(testname, datetime.now())) 254 255 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 256 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED) 257 g_plot('title_list = "{}"'.format(title_list)) 258 g_plot(plot_str) 259 260def plot_boost_cpu(): 261 """ Plot all cpu IO Boosts """ 262 263 output_png = 'all_cpu_boost.png' 264 g_plot = common_all_gnuplot_settings(output_png) 265 g_plot('set yrange [0:100]') 266 g_plot('set ylabel "CPU IO Boost (percent)"') 267 g_plot('set title "{} : cpu io boost : {:%F %H:%M}"'.format(testname, datetime.now())) 268 269 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 270 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST) 271 g_plot('title_list = "{}"'.format(title_list)) 272 g_plot(plot_str) 273 274def plot_ghz_cpu(): 275 """ Plot all cpu tsc ghz """ 276 277 output_png = 'all_cpu_ghz.png' 278 g_plot = common_all_gnuplot_settings(output_png) 279# autoscale this one, no set y range 280 g_plot('set ylabel "TSC Frequency (GHz)"') 281 g_plot('set title "{} : cpu TSC Frequencies (Sanity check calculation) : {:%F %H:%M}"'.format(testname, datetime.now())) 282 283 title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') 284 plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ) 285 g_plot('title_list = "{}"'.format(title_list)) 286 g_plot(plot_str) 287 288def common_all_gnuplot_settings(output_png): 289 """ common gnuplot settings for multiple CPUs one one graph. """ 290 291 g_plot = common_gnuplot_settings() 292 g_plot('set output "' + output_png + '"') 293 return(g_plot) 294 295def common_gnuplot_settings(): 296 """ common gnuplot settings. """ 297 298 g_plot = Gnuplot.Gnuplot(persist=1) 299# The following line is for rigor only. It seems to be assumed for .csv files 300 g_plot('set datafile separator \",\"') 301 g_plot('set ytics nomirror') 302 g_plot('set xtics nomirror') 303 g_plot('set xtics font ", 10"') 304 g_plot('set ytics font ", 10"') 305 g_plot('set tics out scale 1.0') 306 g_plot('set grid') 307 g_plot('set key out horiz') 308 g_plot('set key bot center') 309 g_plot('set key samplen 2 spacing .8 font ", 9"') 310 g_plot('set term png size 1200, 600') 311 g_plot('set title font ", 11"') 312 g_plot('set ylabel font ", 10"') 313 g_plot('set xlabel font ", 10"') 314 g_plot('set xlabel offset 0, 0.5') 315 g_plot('set xlabel "Elapsed Time (Seconds)"') 316 return(g_plot) 317 318def set_4_plot_linestyles(g_plot): 319 """ set the linestyles used for 4 plots in 1 graphs. """ 320 321 g_plot('set style line 1 linetype 1 linecolor rgb "green" pointtype -1') 322 g_plot('set style line 2 linetype 1 linecolor rgb "red" pointtype -1') 323 g_plot('set style line 3 linetype 1 linecolor rgb "purple" pointtype -1') 324 g_plot('set style line 4 linetype 1 linecolor rgb "blue" pointtype -1') 325 326def 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): 327 """ Store master csv file information """ 328 329 global graph_data_present 330 331 if cpu_mask[cpu_int] == 0: 332 return 333 334 try: 335 f_handle = open('cpu.csv', 'a') 336 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) 337 f_handle.write(string_buffer); 338 f_handle.close() 339 except: 340 print('IO error cpu.csv') 341 return 342 343 graph_data_present = True; 344 345def split_csv(current_max_cpu, cpu_mask): 346 """ seperate the all csv file into per CPU csv files. """ 347 348 if os.path.exists('cpu.csv'): 349 for index in range(0, current_max_cpu + 1): 350 if cpu_mask[int(index)] != 0: 351 os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index)) 352 os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index)) 353 354def fix_ownership(path): 355 """Change the owner of the file to SUDO_UID, if required""" 356 357 uid = os.environ.get('SUDO_UID') 358 gid = os.environ.get('SUDO_GID') 359 if uid is not None: 360 os.chown(path, int(uid), int(gid)) 361 362def cleanup_data_files(): 363 """ clean up existing data files """ 364 365 if os.path.exists('cpu.csv'): 366 os.remove('cpu.csv') 367 f_handle = open('cpu.csv', 'a') 368 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') 369 f_handle.write('\n') 370 f_handle.close() 371 372def clear_trace_file(): 373 """ Clear trace file """ 374 375 try: 376 f_handle = open('/sys/kernel/tracing/trace', 'w') 377 f_handle.close() 378 except: 379 print('IO error clearing trace file ') 380 sys.exit(2) 381 382def enable_trace(trace_file): 383 """ Enable trace """ 384 385 try: 386 open(trace_file,'w').write("1") 387 except: 388 print('IO error enabling trace ') 389 sys.exit(2) 390 391def disable_trace(trace_file): 392 """ Disable trace """ 393 394 try: 395 open(trace_file, 'w').write("0") 396 except: 397 print('IO error disabling trace ') 398 sys.exit(2) 399 400def set_trace_buffer_size(memory): 401 """ Set trace buffer size """ 402 403 try: 404 with open('/sys/kernel/tracing/buffer_size_kb', 'w') as fp: 405 fp.write(memory) 406 except: 407 print('IO error setting trace buffer size ') 408 sys.exit(2) 409 410def free_trace_buffer(): 411 """ Free the trace buffer memory """ 412 413 try: 414 open('/sys/kernel/tracing/buffer_size_kb' 415 , 'w').write("1") 416 except: 417 print('IO error freeing trace buffer ') 418 sys.exit(2) 419 420def read_trace_data(filename, cpu_mask): 421 """ Read and parse trace data """ 422 423 global current_max_cpu 424 global sample_num, last_sec_cpu, last_usec_cpu, start_time 425 426 try: 427 data = open(filename, 'r').read() 428 except: 429 print('Error opening ', filename) 430 sys.exit(2) 431 432 for line in data.splitlines(): 433 search_obj = \ 434 re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)' 435 , line) 436 437 if search_obj: 438 cpu = search_obj.group(3) 439 cpu_int = int(cpu) 440 cpu = str(cpu_int) 441 442 time_pre_dec = search_obj.group(6) 443 time_post_dec = search_obj.group(8) 444 core_busy = search_obj.group(10) 445 scaled = search_obj.group(12) 446 _from = search_obj.group(14) 447 _to = search_obj.group(16) 448 mperf = search_obj.group(18) 449 aperf = search_obj.group(20) 450 tsc = search_obj.group(22) 451 freq = search_obj.group(24) 452 common_comm = search_obj.group(2).replace(' ', '') 453 454 # Not all kernel versions have io_boost field 455 io_boost = '0' 456 search_obj = re.search(r'.*?io_boost=(\d+)', line) 457 if search_obj: 458 io_boost = search_obj.group(1) 459 460 if sample_num == 0 : 461 start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) 462 sample_num += 1 463 464 if last_sec_cpu[cpu_int] == 0 : 465 last_sec_cpu[cpu_int] = time_pre_dec 466 last_usec_cpu[cpu_int] = time_post_dec 467 else : 468 duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int])) 469 duration_ms = Decimal(duration_us) / Decimal(1000) 470 last_sec_cpu[cpu_int] = time_pre_dec 471 last_usec_cpu[cpu_int] = time_post_dec 472 elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time 473 load = Decimal(int(mperf)*100)/ Decimal(tsc) 474 freq_ghz = Decimal(freq)/Decimal(1000000) 475# Sanity check calculation, typically anomalies indicate missed samples 476# However, check for 0 (should never occur) 477 tsc_ghz = Decimal(0) 478 if duration_ms != Decimal(0) : 479 tsc_ghz = Decimal(tsc)/duration_ms/Decimal(1000000) 480 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) 481 482 if cpu_int > current_max_cpu: 483 current_max_cpu = cpu_int 484# End of for each trace line loop 485# Now seperate the main overall csv file into per CPU csv files. 486 split_csv(current_max_cpu, cpu_mask) 487 488def signal_handler(signal, frame): 489 print(' SIGINT: Forcing cleanup before exit.') 490 if interval: 491 disable_trace(trace_file) 492 clear_trace_file() 493 # Free the memory 494 free_trace_buffer() 495 sys.exit(0) 496 497if __name__ == "__main__": 498 trace_file = "/sys/kernel/tracing/events/power/pstate_sample/enable" 499 signal.signal(signal.SIGINT, signal_handler) 500 501 interval = "" 502 filename = "" 503 cpu_list = "" 504 testname = "" 505 memory = "10240" 506 graph_data_present = False; 507 508 valid1 = False 509 valid2 = False 510 511 cpu_mask = zeros((MAX_CPUS,), dtype=int) 512 513 try: 514 opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="]) 515 except getopt.GetoptError: 516 print_help('intel_pstate') 517 sys.exit(2) 518 for opt, arg in opts: 519 if opt == '-h': 520 print_help('intel_pstate') 521 sys.exit() 522 elif opt in ("-t", "--trace_file"): 523 valid1 = True 524 location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) 525 filename = os.path.join(location, arg) 526 elif opt in ("-i", "--interval"): 527 valid1 = True 528 interval = arg 529 elif opt in ("-c", "--cpu"): 530 cpu_list = arg 531 elif opt in ("-n", "--name"): 532 valid2 = True 533 testname = arg 534 elif opt in ("-m", "--memory"): 535 memory = arg 536 537 if not (valid1 and valid2): 538 print_help('intel_pstate') 539 sys.exit() 540 541 if cpu_list: 542 for p in re.split("[,]", cpu_list): 543 if int(p) < MAX_CPUS : 544 cpu_mask[int(p)] = 1 545 else: 546 for i in range (0, MAX_CPUS): 547 cpu_mask[i] = 1 548 549 if not os.path.exists('results'): 550 os.mkdir('results') 551 # The regular user needs to own the directory, not root. 552 fix_ownership('results') 553 554 os.chdir('results') 555 if os.path.exists(testname): 556 print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.') 557 sys.exit() 558 os.mkdir(testname) 559 # The regular user needs to own the directory, not root. 560 fix_ownership(testname) 561 os.chdir(testname) 562 563 # Temporary (or perhaps not) 564 cur_version = sys.version_info 565 print('python version (should be >= 3.6):') 566 print(cur_version) 567 568 # Left as "cleanup" for potential future re-run ability. 569 cleanup_data_files() 570 571 if interval: 572 filename = "/sys/kernel/tracing/trace" 573 clear_trace_file() 574 set_trace_buffer_size(memory) 575 enable_trace(trace_file) 576 print('Sleeping for ', interval, 'seconds') 577 time.sleep(int(interval)) 578 disable_trace(trace_file) 579 580 current_max_cpu = 0 581 582 read_trace_data(filename, cpu_mask) 583 584 if interval: 585 clear_trace_file() 586 # Free the memory 587 free_trace_buffer() 588 589 if graph_data_present == False: 590 print('No valid data to plot') 591 sys.exit(2) 592 593 for cpu_no in range(0, current_max_cpu + 1): 594 plot_perf_busy_with_sample(cpu_no) 595 plot_perf_busy(cpu_no) 596 plot_durations(cpu_no) 597 plot_loads(cpu_no) 598 599 plot_pstate_cpu_with_sample() 600 plot_pstate_cpu() 601 plot_load_cpu() 602 plot_frequency_cpu() 603 plot_duration_cpu() 604 plot_scaled_cpu() 605 plot_boost_cpu() 606 plot_ghz_cpu() 607 608 # It is preferrable, but not necessary, that the regular user owns the files, not root. 609 for root, dirs, files in os.walk('.'): 610 for f in files: 611 fix_ownership(f) 612 613 os.chdir('../../') 614