Back to home page

OSCL-LXR

 
 

    


0001 #!/usr/bin/env python3
0002 # SPDX-License-Identifier: GPL-2.0-only
0003 # -*- coding: utf-8 -*-
0004 #
0005 """ This utility can be used to debug and tune the performance of the
0006 AMD P-State driver. It imports intel_pstate_tracer to analyze AMD P-State
0007 trace event.
0008 
0009 Prerequisites:
0010     Python version 2.7.x or higher
0011     gnuplot 5.0 or higher
0012     gnuplot-py 1.8 or higher
0013     (Most of the distributions have these required packages. They may be called
0014      gnuplot-py, phython-gnuplot or phython3-gnuplot, gnuplot-nox, ... )
0015 
0016     Kernel config for Linux trace is enabled
0017 
0018     see print_help(): for Usage and Output details
0019 
0020 """
0021 from __future__ import print_function
0022 from datetime import datetime
0023 import subprocess
0024 import os
0025 import time
0026 import re
0027 import signal
0028 import sys
0029 import getopt
0030 import Gnuplot
0031 from numpy import *
0032 from decimal import *
0033 sys.path.append('../intel_pstate_tracer')
0034 #import intel_pstate_tracer
0035 import intel_pstate_tracer as ipt
0036 
0037 __license__ = "GPL version 2"
0038 
0039 MAX_CPUS = 256
0040 # Define the csv file columns
0041 C_COMM = 15
0042 C_ELAPSED = 14
0043 C_SAMPLE = 13
0044 C_DURATION = 12
0045 C_LOAD = 11
0046 C_TSC = 10
0047 C_APERF = 9
0048 C_MPERF = 8
0049 C_FREQ = 7
0050 C_MAX_PERF = 6
0051 C_DES_PERF = 5
0052 C_MIN_PERF = 4
0053 C_USEC = 3
0054 C_SEC = 2
0055 C_CPU = 1
0056 
0057 global sample_num, last_sec_cpu, last_usec_cpu, start_time, test_name, trace_file
0058 
0059 getcontext().prec = 11
0060 
0061 sample_num =0
0062 last_sec_cpu = [0] * MAX_CPUS
0063 last_usec_cpu = [0] * MAX_CPUS
0064 
0065 def plot_per_cpu_freq(cpu_index):
0066     """ Plot per cpu frequency """
0067 
0068     file_name = 'cpu{:0>3}.csv'.format(cpu_index)
0069     if os.path.exists(file_name):
0070         output_png = "cpu%03d_frequency.png" % cpu_index
0071         g_plot = ipt.common_gnuplot_settings()
0072         g_plot('set output "' + output_png + '"')
0073         g_plot('set yrange [0:7]')
0074         g_plot('set ytics 0, 1')
0075         g_plot('set ylabel "CPU Frequency (GHz)"')
0076         g_plot('set title "{} : frequency : CPU {:0>3} : {:%F %H:%M}"'.format(test_name, cpu_index, datetime.now()))
0077         g_plot('set ylabel "CPU frequency"')
0078         g_plot('set key off')
0079         ipt.set_4_plot_linestyles(g_plot)
0080         g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_FREQ))
0081 
0082 def plot_per_cpu_des_perf(cpu_index):
0083     """ Plot per cpu desired perf """
0084 
0085     file_name = 'cpu{:0>3}.csv'.format(cpu_index)
0086     if os.path.exists(file_name):
0087         output_png = "cpu%03d_des_perf.png" % cpu_index
0088         g_plot = ipt.common_gnuplot_settings()
0089         g_plot('set output "' + output_png + '"')
0090         g_plot('set yrange [0:255]')
0091         g_plot('set ylabel "des perf"')
0092         g_plot('set title "{} : cpu des perf : CPU {:0>3} : {:%F %H:%M}"'.format(test_name, cpu_index, datetime.now()))
0093         g_plot('set key off')
0094         ipt.set_4_plot_linestyles(g_plot)
0095         g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DES_PERF))
0096 
0097 def plot_per_cpu_load(cpu_index):
0098     """ Plot per cpu load """
0099 
0100     file_name = 'cpu{:0>3}.csv'.format(cpu_index)
0101     if os.path.exists(file_name):
0102         output_png = "cpu%03d_load.png" % cpu_index
0103         g_plot = ipt.common_gnuplot_settings()
0104         g_plot('set output "' + output_png + '"')
0105         g_plot('set yrange [0:100]')
0106         g_plot('set ytics 0, 10')
0107         g_plot('set ylabel "CPU load (percent)"')
0108         g_plot('set title "{} : cpu load : CPU {:0>3} : {:%F %H:%M}"'.format(test_name, cpu_index, datetime.now()))
0109         g_plot('set key off')
0110         ipt.set_4_plot_linestyles(g_plot)
0111         g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD))
0112 
0113 def plot_all_cpu_frequency():
0114     """ Plot all cpu frequencies """
0115 
0116     output_png = 'all_cpu_frequencies.png'
0117     g_plot = ipt.common_gnuplot_settings()
0118     g_plot('set output "' + output_png + '"')
0119     g_plot('set ylabel "CPU Frequency (GHz)"')
0120     g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(test_name, datetime.now()))
0121 
0122     title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
0123     plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ)
0124     g_plot('title_list = "{}"'.format(title_list))
0125     g_plot(plot_str)
0126 
0127 def plot_all_cpu_des_perf():
0128     """ Plot all cpu desired perf """
0129 
0130     output_png = 'all_cpu_des_perf.png'
0131     g_plot = ipt.common_gnuplot_settings()
0132     g_plot('set output "' + output_png + '"')
0133     g_plot('set ylabel "des perf"')
0134     g_plot('set title "{} : cpu des perf : {:%F %H:%M}"'.format(test_name, datetime.now()))
0135 
0136     title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
0137     plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 255 ps 1 title i".format(C_ELAPSED, C_DES_PERF)
0138     g_plot('title_list = "{}"'.format(title_list))
0139     g_plot(plot_str)
0140 
0141 def plot_all_cpu_load():
0142     """ Plot all cpu load  """
0143 
0144     output_png = 'all_cpu_load.png'
0145     g_plot = ipt.common_gnuplot_settings()
0146     g_plot('set output "' + output_png + '"')
0147     g_plot('set yrange [0:100]')
0148     g_plot('set ylabel "CPU load (percent)"')
0149     g_plot('set title "{} : cpu load : {:%F %H:%M}"'.format(test_name, datetime.now()))
0150 
0151     title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ')
0152     plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 255 ps 1 title i".format(C_ELAPSED, C_LOAD)
0153     g_plot('title_list = "{}"'.format(title_list))
0154     g_plot(plot_str)
0155 
0156 def store_csv(cpu_int, time_pre_dec, time_post_dec, min_perf, des_perf, max_perf, freq_ghz, mperf, aperf, tsc, common_comm, load, duration_ms, sample_num, elapsed_time, cpu_mask):
0157     """ Store master csv file information """
0158 
0159     global graph_data_present
0160 
0161     if cpu_mask[cpu_int] == 0:
0162         return
0163 
0164     try:
0165         f_handle = open('cpu.csv', 'a')
0166         string_buffer = "CPU_%03u, %05u, %06u, %u, %u, %u, %.4f, %u, %u, %u, %.2f, %.3f, %u, %.3f, %s\n" % (cpu_int, int(time_pre_dec), int(time_post_dec), int(min_perf), int(des_perf), int(max_perf), freq_ghz, int(mperf), int(aperf), int(tsc), load, duration_ms, sample_num, elapsed_time, common_comm)
0167         f_handle.write(string_buffer)
0168         f_handle.close()
0169     except:
0170         print('IO error cpu.csv')
0171         return
0172 
0173     graph_data_present = True;
0174 
0175 
0176 def cleanup_data_files():
0177     """ clean up existing data files """
0178 
0179     if os.path.exists('cpu.csv'):
0180         os.remove('cpu.csv')
0181     f_handle = open('cpu.csv', 'a')
0182     f_handle.write('common_cpu, common_secs, common_usecs, min_perf, des_perf, max_perf, freq, mperf, aperf, tsc, load, duration_ms, sample_num, elapsed_time, common_comm')
0183     f_handle.write('\n')
0184     f_handle.close()
0185 
0186 def read_trace_data(file_name, cpu_mask):
0187     """ Read and parse trace data """
0188 
0189     global current_max_cpu
0190     global sample_num, last_sec_cpu, last_usec_cpu, start_time
0191 
0192     try:
0193         data = open(file_name, 'r').read()
0194     except:
0195         print('Error opening ', file_name)
0196         sys.exit(2)
0197 
0198     for line in data.splitlines():
0199         search_obj = \
0200             re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?amd_min_perf=)(\d+)(.*?amd_des_perf=)(\d+)(.*?amd_max_perf=)(\d+)(.*?freq=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)'
0201                       , line)
0202 
0203         if search_obj:
0204             cpu = search_obj.group(3)
0205             cpu_int = int(cpu)
0206             cpu = str(cpu_int)
0207 
0208             time_pre_dec = search_obj.group(6)
0209             time_post_dec = search_obj.group(8)
0210             min_perf = search_obj.group(10)
0211             des_perf = search_obj.group(12)
0212             max_perf = search_obj.group(14)
0213             freq = search_obj.group(16)
0214             mperf = search_obj.group(18)
0215             aperf = search_obj.group(20)
0216             tsc = search_obj.group(22)
0217 
0218             common_comm = search_obj.group(2).replace(' ', '')
0219 
0220             if sample_num == 0 :
0221                 start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000)
0222             sample_num += 1
0223 
0224             if last_sec_cpu[cpu_int] == 0 :
0225                 last_sec_cpu[cpu_int] = time_pre_dec
0226                 last_usec_cpu[cpu_int] = time_post_dec
0227             else :
0228                 duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int]))
0229                 duration_ms = Decimal(duration_us) / Decimal(1000)
0230                 last_sec_cpu[cpu_int] = time_pre_dec
0231                 last_usec_cpu[cpu_int] = time_post_dec
0232                 elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time
0233                 load = Decimal(int(mperf)*100)/ Decimal(tsc)
0234                 freq_ghz = Decimal(freq)/Decimal(1000000)
0235                 store_csv(cpu_int, time_pre_dec, time_post_dec, min_perf, des_perf, max_perf, freq_ghz, mperf, aperf, tsc, common_comm, load, duration_ms, sample_num, elapsed_time, cpu_mask)
0236 
0237             if cpu_int > current_max_cpu:
0238                 current_max_cpu = cpu_int
0239 # Now separate the main overall csv file into per CPU csv files.
0240     ipt.split_csv(current_max_cpu, cpu_mask)
0241 
0242 
0243 def signal_handler(signal, frame):
0244     print(' SIGINT: Forcing cleanup before exit.')
0245     if interval:
0246         ipt.disable_trace(trace_file)
0247         ipt.clear_trace_file()
0248         ipt.free_trace_buffer()
0249         sys.exit(0)
0250 
0251 trace_file = "/sys/kernel/debug/tracing/events/amd_cpu/enable"
0252 signal.signal(signal.SIGINT, signal_handler)
0253 
0254 interval = ""
0255 file_name = ""
0256 cpu_list = ""
0257 test_name = ""
0258 memory = "10240"
0259 graph_data_present = False;
0260 
0261 valid1 = False
0262 valid2 = False
0263 
0264 cpu_mask = zeros((MAX_CPUS,), dtype=int)
0265 
0266 
0267 try:
0268     opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="])
0269 except getopt.GetoptError:
0270     ipt.print_help('amd_pstate')
0271     sys.exit(2)
0272 for opt, arg in opts:
0273     if opt == '-h':
0274         print()
0275         sys.exit()
0276     elif opt in ("-t", "--trace_file"):
0277         valid1 = True
0278         location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
0279         file_name = os.path.join(location, arg)
0280     elif opt in ("-i", "--interval"):
0281         valid1 = True
0282         interval = arg
0283     elif opt in ("-c", "--cpu"):
0284         cpu_list = arg
0285     elif opt in ("-n", "--name"):
0286         valid2 = True
0287         test_name = arg
0288     elif opt in ("-m", "--memory"):
0289         memory = arg
0290 
0291 if not (valid1 and valid2):
0292     ipt.print_help('amd_pstate')
0293     sys.exit()
0294 
0295 if cpu_list:
0296     for p in re.split("[,]", cpu_list):
0297         if int(p) < MAX_CPUS :
0298             cpu_mask[int(p)] = 1
0299 else:
0300     for i in range (0, MAX_CPUS):
0301         cpu_mask[i] = 1
0302 
0303 if not os.path.exists('results'):
0304     os.mkdir('results')
0305     ipt.fix_ownership('results')
0306 
0307 os.chdir('results')
0308 if os.path.exists(test_name):
0309     print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
0310     sys.exit()
0311 os.mkdir(test_name)
0312 ipt.fix_ownership(test_name)
0313 os.chdir(test_name)
0314 
0315 cur_version = sys.version_info
0316 print('python version (should be >= 2.7):')
0317 print(cur_version)
0318 
0319 cleanup_data_files()
0320 
0321 if interval:
0322     file_name = "/sys/kernel/debug/tracing/trace"
0323     ipt.clear_trace_file()
0324     ipt.set_trace_buffer_size(memory)
0325     ipt.enable_trace(trace_file)
0326     time.sleep(int(interval))
0327     ipt.disable_trace(trace_file)
0328 
0329 current_max_cpu = 0
0330 
0331 read_trace_data(file_name, cpu_mask)
0332 
0333 if interval:
0334     ipt.clear_trace_file()
0335     ipt.free_trace_buffer()
0336 
0337 if graph_data_present == False:
0338     print('No valid data to plot')
0339     sys.exit(2)
0340 
0341 for cpu_no in range(0, current_max_cpu + 1):
0342     plot_per_cpu_freq(cpu_no)
0343     plot_per_cpu_des_perf(cpu_no)
0344     plot_per_cpu_load(cpu_no)
0345 
0346 plot_all_cpu_des_perf()
0347 plot_all_cpu_frequency()
0348 plot_all_cpu_load()
0349 
0350 for root, dirs, files in os.walk('.'):
0351     for f in files:
0352         ipt.fix_ownership(f)
0353 
0354 os.chdir('../../')