From patchwork Wed Jan 11 20:50:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: srinivas pandruvada X-Patchwork-Id: 9511349 X-Patchwork-Delegate: rjw@sisk.pl Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E152B6075C for ; Wed, 11 Jan 2017 20:53:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D0AAC2868C for ; Wed, 11 Jan 2017 20:53:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C3BB528694; Wed, 11 Jan 2017 20:53:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0A7D62868C for ; Wed, 11 Jan 2017 20:53:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758018AbdAKUvp (ORCPT ); Wed, 11 Jan 2017 15:51:45 -0500 Received: from mga04.intel.com ([192.55.52.120]:52936 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754977AbdAKUuT (ORCPT ); Wed, 11 Jan 2017 15:50:19 -0500 Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga104.fm.intel.com with ESMTP; 11 Jan 2017 12:50:17 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.33,346,1477983600"; d="scan'208";a="29197131" Received: from spandruv-desk.jf.intel.com ([10.54.75.13]) by orsmga002.jf.intel.com with ESMTP; 11 Jan 2017 12:50:17 -0800 From: Srinivas Pandruvada To: rjw@rjwysocki.net, len.brown@intel.com Cc: linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, 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> X-Mailer: git-send-email 2.7.4 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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('../')