From mboxrd@z Thu Jan 1 00:00:00 1970 From: Srinivas Pandruvada Subject: [PATCH] tools/power/x86: Debug utility for intel_pstate driver Date: Wed, 11 Jan 2017 12:50:00 -0800 Message-ID: <1484167800-96170-1-git-send-email-srinivas.pandruvada@linux.intel.com> Return-path: Sender: linux-kernel-owner@vger.kernel.org To: rjw@rjwysocki.net, len.brown@intel.com Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Srinivas Pandruvada List-Id: linux-pm@vger.kernel.org This utility can be used to debug and tune the performance of the intel_pstate driver. This utility can be used in two ways: - If there is Linux trace file with pstate_sample events enabled, then this utility can parse the trace file and generate performance plots. - If user has not specified a trace file as input via command line parameters, then this utility enables and collects trace data for a user specified interval and generates performance plots. Signed-off-by: Srinivas Pandruvada --- .../x86/intel_pstate_tracer/intel_pstate_tracer.py | 287 +++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100755 tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py new file mode 100755 index 0000000..00ed159 --- /dev/null +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py @@ -0,0 +1,287 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" This utility can be used to debug and tune the performance of the +intel_pstate driver. This utility can be used in two ways: +- If there is Linux trace file with pstate_sample events enabled, then +this utility can parse the trace file and generate performance plots. +- If user has not specified a trace file as input via command line parameters, +then this utility enables and collects trace data for a user specified interval +and generates performance plots. + +Prerequisites: + Python version 2.7.x + gnuplot 5.0 or higher + gnuplot-py 1.8 + (Most of the distributions have these required packages) + + HWP (Hardware P-States are disabled) + Kernel config for Linux trace is enabled + + Usage: + If the trace file is available, then to simply parse and plot, use + sudo ./intel_pstate_tracer.py -t + To generate trace file, parse and plot, use + sudo ./intel_pstate_tracer.py -i + Output: + Creates a "data" folder in the current working directory and copies: + cpu*.dat - Raw performance data + cpu_perf_busy*.png - Plots of P-States, Performance, busy + and io_busy + all_cpu_per.png - Plot of P-State transition on each CPU + +""" +from __future__ import print_function +import os +import time +import re +import sys +import getopt +import Gnuplot + +__author__ = "Srinivas Pandruvada" +__copyright__ = " Copyright (c) 2017, Intel Corporation. " +__license__ = "GPL version 2" + + +MAX_CPUS = 256 + +def plot_perf_busy(cpu_index): + """ Plot method to per cpu information """ + + file_name = 'cpu' + str(cpu_index) + '.dat' + if os.path.exists(file_name): + + output_png = "cpu_perf_busy%d.png" % cpu_index + g_plot = Gnuplot.Gnuplot(persist=1) + g_plot('set yrange [0:40]') + g_plot('set y2range [0:100]') + g_plot('set y2tics 0, 10') + g_plot('set ytics nomirror') + g_plot('set xlabel "Samples"') + g_plot('set ylabel "P-State"') + g_plot('set y2label "Scaled Busy"') + g_plot('set term png size 1280, 720') + g_plot('set output "' + output_png + '"') + + g_plot.plot(Gnuplot.File(file_name, using='1', + with_="lines linecolor 'green' axis x1y2", + title='performance'), + Gnuplot.File(file_name, + using='2', with_="lines linecolor 'red' axis x1y2", + title='scaled-busy'), + Gnuplot.File(file_name, + using='4', + with_="lines linecolor 'purple' axis x1y2", + title='io-busy'), + Gnuplot.File(file_name, using='3', + with_="lines linecolor 'blue' axis x1y1", + title='P-State')) + + +def plot_perf_cpu(): + """ Plot all cpu information """ + + if os.path.exists('cpu.dat'): + output_png = 'all_cpu_perf.png' + g_plot = Gnuplot.Gnuplot(persist=1) + g_plot('set yrange [0:30]') + g_plot('set xlabel "Samples"') + g_plot('set ylabel "P-State"') + g_plot('set term png size 1280, 720') + g_plot('set output "' + output_png + '"') + + plot_str = 'plot for [col=1:' + str(current_max_cpu + 1) \ + + "] 'cpu.dat' using 0:col with lines title columnheader" + + g_plot.__call__(plot_str) + +def store_per_cpu_information(cup_index, perf, busy, pstate, io_boost_pct): + """ Store information for each CPU """ + + try: + f_handle = open('cpu' + cup_index + '.dat', 'a') + sep = "\t"; + f_handle.write(sep.join((perf, busy, pstate, io_boost_pct)) + '\n'); + f_handle.close() + except: + print('IO error cpu*.dat') + return + + try: + f_handle = open('cpu.dat', 'a') + for index in range(0, int(cup_index)): + f_handle.write('0\t') + + f_handle.write(pstate + '\t') + for index in range(int(cup_index) + 1, MAX_CPUS): + f_handle.write('0\t') + f_handle.write('\n') + f_handle.close() + except: + print('IO error cpu.dat') + return + +def cleanup_data_files(): + """ clean up existing data files """ + + for index in range(0, MAX_CPUS): + filename = 'cpu' + str(index) + '.dat' + if os.path.exists(filename): + os.remove(filename) + + if os.path.exists('cpu.dat'): + os.remove('cpu.dat') + f_handle = open('cpu.dat', 'a') + for index in range(0, MAX_CPUS): + f_handle.write('cpu' + str(index) + '\t') + f_handle.close() + +def clear_trace_file(): + """ Clear trace file """ + try: + f_handle = open('/sys/kernel/debug/tracing/trace', 'w') + f_handle.close() + except: + print('IO error clearing trace file ') + quit() + +def enable_trace(): + """ Enable trace """ + try: + open('/sys/kernel/debug/tracing/events/power/pstate_sample/enable' + , 'w').write("1") + except: + print('IO error enabling trace ') + quit() + +def disable_trace(): + """ Enable trace """ + try: + open('/sys/kernel/debug/tracing/events/power/pstate_sample/enable' + , 'w').write("0") + except: + print('IO error disabling trace ') + quit() + +def set_trace_buffer_size(): + """ Set trace buffer size """ + try: + open('/sys/kernel/debug/tracing/buffer_size_kb' + , 'w').write("10240") + except: + print('IO error setting trace buffer size ') + quit() + +def read_and_plot_trace_data(filename): + """ Read trace data and call for plot """ + + global current_max_cpu + try: + data = open(filename, 'r').read() + except: + print('Error opening ', filename) + quit() + + for line in data.splitlines(): + search_obj = \ + re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)' + , line) + + if search_obj: + cpu = search_obj.group(3) + cpu_int = int(cpu) + cpu = str(cpu_int) + + print('[\ncpu : ', cpu) + + core_busy = search_obj.group(6) + print('core_busy : ', core_busy) + + scaled = search_obj.group(8) + print('scaled : ', scaled) + + _from = search_obj.group(10) + print('from : ', _from) + + _to = search_obj.group(12) + print('to : ', _to) + + mperf = search_obj.group(14) + print('mperf : ', mperf) + + aperf = search_obj.group(16) + print('aperf : ', aperf) + + tsc = search_obj.group(18) + print('tsc : ', tsc) + + freq = search_obj.group(20) + print('freq : ', freq) + + # Not all kernel version has io_boost field + + io_boost = '0' + search_obj = re.search(r'.*?io_boost=(\d+)', line) + if search_obj: + print('io_boost : ', search_obj.group(1)) + + store_per_cpu_information(cpu, core_busy, scaled, _to, io_boost) + + if int(cpu) > current_max_cpu: + current_max_cpu = int(cpu) + +interval = "" +filename = "" +valid = False + +try: + opts, args = getopt.getopt(sys.argv[1:],"ht:i:",["trace_file=","interval="]) +except getopt.GetoptError: + print('intel_pstate_tracer.py -t ') + print('intel_pstate_tracer.py -i ') + sys.exit(2) +for opt, arg in opts: + if opt == '-h': + print('intel_pstate_tracer.py -t ') + print('intel_pstate_tracer.py -i ') + sys.exit() + elif opt in ("-t", "--trace_file"): + valid = True + filename = arg + elif opt in ("-i", "--interval"): + valid = True + interval = arg + +if not valid: + print('intel_pstate_tracer.py -t ') + print('intel_pstate_tracer.py -i ') + sys.exit() + +if not os.path.exists('data'): + os.mkdir('data') + +os.chdir('data') + +cleanup_data_files() + +if interval: + filename = "/sys/kernel/debug/tracing/trace" + clear_trace_file() + set_trace_buffer_size() + enable_trace() + print('Sleeping for ', interval, 'seconds') + time.sleep(int(interval)) + +current_max_cpu = 0 + +read_and_plot_trace_data(filename) + +if interval: + disable_trace() + +for cpu_no in range(0, current_max_cpu + 1): + plot_perf_busy(cpu_no) + +plot_perf_cpu() + +os.chdir('../') -- 2.7.4