2 # SPDX-License-Identifier: GPL-2.0-only
3 # -*- coding: utf-8 -*-
5 """ This utility can be used to debug and tune the performance of the
6 AMD P-State driver. It imports intel_pstate_tracer to analyze AMD P-State
10 Python version 2.7.x or higher
12 gnuplot-py 1.8 or higher
13 (Most of the distributions have these required packages. They may be called
14 gnuplot-py, phython-gnuplot or phython3-gnuplot, gnuplot-nox, ... )
16 Kernel config for Linux trace is enabled
18 see print_help(): for Usage and Output details
21 from __future__
import print_function
22 from datetime
import datetime
33 sys
.path
.append(os
.path
.join(os
.path
.dirname(__file__
), "..", "intel_pstate_tracer"))
34 import intel_pstate_tracer
as ipt
36 __license__
= "GPL version 2"
39 # Define the csv file columns
56 global sample_num
, last_sec_cpu
, last_usec_cpu
, start_time
, test_name
, trace_file
58 getcontext().prec
= 11
61 last_sec_cpu
= [0] * MAX_CPUS
62 last_usec_cpu
= [0] * MAX_CPUS
64 def plot_per_cpu_freq(cpu_index
):
65 """ Plot per cpu frequency """
67 file_name
= 'cpu{:0>3}.csv'.format(cpu_index
)
68 if os
.path
.exists(file_name
):
69 output_png
= "cpu%03d_frequency.png" % cpu_index
70 g_plot
= ipt
.common_gnuplot_settings()
71 g_plot('set output "' + output_png
+ '"')
72 g_plot('set yrange [0:7]')
73 g_plot('set ytics 0, 1')
74 g_plot('set ylabel "CPU Frequency (GHz)"')
75 g_plot('set title "{} : frequency : CPU {:0>3} : {:%F %H:%M}"'.format(test_name
, cpu_index
, datetime
.now()))
76 g_plot('set ylabel "CPU frequency"')
78 ipt
.set_4_plot_linestyles(g_plot
)
79 g_plot('plot "' + file_name
+ '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED
, C_FREQ
))
81 def plot_per_cpu_des_perf(cpu_index
):
82 """ Plot per cpu desired perf """
84 file_name
= 'cpu{:0>3}.csv'.format(cpu_index
)
85 if os
.path
.exists(file_name
):
86 output_png
= "cpu%03d_des_perf.png" % cpu_index
87 g_plot
= ipt
.common_gnuplot_settings()
88 g_plot('set output "' + output_png
+ '"')
89 g_plot('set yrange [0:255]')
90 g_plot('set ylabel "des perf"')
91 g_plot('set title "{} : cpu des perf : CPU {:0>3} : {:%F %H:%M}"'.format(test_name
, cpu_index
, datetime
.now()))
93 ipt
.set_4_plot_linestyles(g_plot
)
94 g_plot('plot "' + file_name
+ '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED
, C_DES_PERF
))
96 def plot_per_cpu_load(cpu_index
):
97 """ Plot per cpu load """
99 file_name
= 'cpu{:0>3}.csv'.format(cpu_index
)
100 if os
.path
.exists(file_name
):
101 output_png
= "cpu%03d_load.png" % cpu_index
102 g_plot
= ipt
.common_gnuplot_settings()
103 g_plot('set output "' + output_png
+ '"')
104 g_plot('set yrange [0:100]')
105 g_plot('set ytics 0, 10')
106 g_plot('set ylabel "CPU load (percent)"')
107 g_plot('set title "{} : cpu load : CPU {:0>3} : {:%F %H:%M}"'.format(test_name
, cpu_index
, datetime
.now()))
108 g_plot('set key off')
109 ipt
.set_4_plot_linestyles(g_plot
)
110 g_plot('plot "' + file_name
+ '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED
, C_LOAD
))
112 def plot_all_cpu_frequency():
113 """ Plot all cpu frequencies """
115 output_png
= 'all_cpu_frequencies.png'
116 g_plot
= ipt
.common_gnuplot_settings()
117 g_plot('set output "' + output_png
+ '"')
118 g_plot('set ylabel "CPU Frequency (GHz)"')
119 g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(test_name
, datetime
.now()))
121 title_list
= subprocess
.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell
=True).decode('utf-8').replace('\n', ' ')
122 plot_str
= "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED
, C_FREQ
)
123 g_plot('title_list = "{}"'.format(title_list
))
126 def plot_all_cpu_des_perf():
127 """ Plot all cpu desired perf """
129 output_png
= 'all_cpu_des_perf.png'
130 g_plot
= ipt
.common_gnuplot_settings()
131 g_plot('set output "' + output_png
+ '"')
132 g_plot('set ylabel "des perf"')
133 g_plot('set title "{} : cpu des perf : {:%F %H:%M}"'.format(test_name
, datetime
.now()))
135 title_list
= subprocess
.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell
=True).decode('utf-8').replace('\n', ' ')
136 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
)
137 g_plot('title_list = "{}"'.format(title_list
))
140 def plot_all_cpu_load():
141 """ Plot all cpu load """
143 output_png
= 'all_cpu_load.png'
144 g_plot
= ipt
.common_gnuplot_settings()
145 g_plot('set output "' + output_png
+ '"')
146 g_plot('set yrange [0:100]')
147 g_plot('set ylabel "CPU load (percent)"')
148 g_plot('set title "{} : cpu load : {:%F %H:%M}"'.format(test_name
, datetime
.now()))
150 title_list
= subprocess
.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell
=True).decode('utf-8').replace('\n', ' ')
151 plot_str
= "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 255 ps 1 title i".format(C_ELAPSED
, C_LOAD
)
152 g_plot('title_list = "{}"'.format(title_list
))
155 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
):
156 """ Store master csv file information """
158 global graph_data_present
160 if cpu_mask
[cpu_int
] == 0:
164 f_handle
= open('cpu.csv', 'a')
165 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
)
166 f_handle
.write(string_buffer
)
169 print('IO error cpu.csv')
172 graph_data_present
= True;
175 def cleanup_data_files():
176 """ clean up existing data files """
178 if os
.path
.exists('cpu.csv'):
180 f_handle
= open('cpu.csv', 'a')
181 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')
185 def read_trace_data(file_name
, cpu_mask
):
186 """ Read and parse trace data """
188 global current_max_cpu
189 global sample_num
, last_sec_cpu
, last_usec_cpu
, start_time
192 data
= open(file_name
, 'r').read()
194 print('Error opening ', file_name
)
197 for line
in data
.splitlines():
199 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+)'
203 cpu
= search_obj
.group(3)
207 time_pre_dec
= search_obj
.group(6)
208 time_post_dec
= search_obj
.group(8)
209 min_perf
= search_obj
.group(10)
210 des_perf
= search_obj
.group(12)
211 max_perf
= search_obj
.group(14)
212 freq
= search_obj
.group(16)
213 mperf
= search_obj
.group(18)
214 aperf
= search_obj
.group(20)
215 tsc
= search_obj
.group(22)
217 common_comm
= search_obj
.group(2).replace(' ', '')
220 start_time
= Decimal(time_pre_dec
) + Decimal(time_post_dec
) / Decimal(1000000)
223 if last_sec_cpu
[cpu_int
] == 0 :
224 last_sec_cpu
[cpu_int
] = time_pre_dec
225 last_usec_cpu
[cpu_int
] = time_post_dec
227 duration_us
= (int(time_pre_dec
) - int(last_sec_cpu
[cpu_int
])) * 1000000 + (int(time_post_dec
) - int(last_usec_cpu
[cpu_int
]))
228 duration_ms
= Decimal(duration_us
) / Decimal(1000)
229 last_sec_cpu
[cpu_int
] = time_pre_dec
230 last_usec_cpu
[cpu_int
] = time_post_dec
231 elapsed_time
= Decimal(time_pre_dec
) + Decimal(time_post_dec
) / Decimal(1000000) - start_time
232 load
= Decimal(int(mperf
)*100)/ Decimal(tsc
)
233 freq_ghz
= Decimal(freq
)/Decimal(1000000)
234 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
)
236 if cpu_int
> current_max_cpu
:
237 current_max_cpu
= cpu_int
238 # Now separate the main overall csv file into per CPU csv files.
239 ipt
.split_csv(current_max_cpu
, cpu_mask
)
242 def signal_handler(signal
, frame
):
243 print(' SIGINT: Forcing cleanup before exit.')
245 ipt
.disable_trace(trace_file
)
246 ipt
.clear_trace_file()
247 ipt
.free_trace_buffer()
250 trace_file
= "/sys/kernel/tracing/events/amd_cpu/enable"
251 signal
.signal(signal
.SIGINT
, signal_handler
)
258 graph_data_present
= False;
263 cpu_mask
= zeros((MAX_CPUS
,), dtype
=int)
267 opts
, args
= getopt
.getopt(sys
.argv
[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="])
268 except getopt
.GetoptError
:
269 ipt
.print_help('amd_pstate')
271 for opt
, arg
in opts
:
275 elif opt
in ("-t", "--trace_file"):
277 location
= os
.path
.realpath(os
.path
.join(os
.getcwd(), os
.path
.dirname(__file__
)))
278 file_name
= os
.path
.join(location
, arg
)
279 elif opt
in ("-i", "--interval"):
282 elif opt
in ("-c", "--cpu"):
284 elif opt
in ("-n", "--name"):
287 elif opt
in ("-m", "--memory"):
290 if not (valid1
and valid2
):
291 ipt
.print_help('amd_pstate')
295 for p
in re
.split("[,]", cpu_list
):
296 if int(p
) < MAX_CPUS
:
299 for i
in range (0, MAX_CPUS
):
302 if not os
.path
.exists('results'):
304 ipt
.fix_ownership('results')
307 if os
.path
.exists(test_name
):
308 print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.')
311 ipt
.fix_ownership(test_name
)
314 cur_version
= sys
.version_info
315 print('python version (should be >= 2.7):')
321 file_name
= "/sys/kernel/tracing/trace"
322 ipt
.clear_trace_file()
323 ipt
.set_trace_buffer_size(memory
)
324 ipt
.enable_trace(trace_file
)
325 time
.sleep(int(interval
))
326 ipt
.disable_trace(trace_file
)
330 read_trace_data(file_name
, cpu_mask
)
333 ipt
.clear_trace_file()
334 ipt
.free_trace_buffer()
336 if graph_data_present
== False:
337 print('No valid data to plot')
340 for cpu_no
in range(0, current_max_cpu
+ 1):
341 plot_per_cpu_freq(cpu_no
)
342 plot_per_cpu_des_perf(cpu_no
)
343 plot_per_cpu_load(cpu_no
)
345 plot_all_cpu_des_perf()
346 plot_all_cpu_frequency()
349 for root
, dirs
, files
in os
.walk('.'):