intel_pstate_tracer.py 23 KB

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