From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ... --- .../x86/intel_pstate_tracer/intel_pstate_tracer.py | 613 +++++++++++++++++++++ 1 file changed, 613 insertions(+) create mode 100755 tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py (limited to 'tools/power/x86/intel_pstate_tracer') 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 000000000..ec3323100 --- /dev/null +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py @@ -0,0 +1,613 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-only +# -*- 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 or higher + gnuplot 5.0 or higher + gnuplot-py 1.8 or higher + (Most of the distributions have these required packages. They may be called + gnuplot-py, phython-gnuplot or phython3-gnuplot, gnuplot-nox, ... ) + + HWP (Hardware P-States are disabled) + Kernel config for Linux trace is enabled + + see print_help(): for Usage and Output details + +""" +from __future__ import print_function +from datetime import datetime +import subprocess +import os +import time +import re +import signal +import sys +import getopt +import Gnuplot +from numpy import * +from decimal import * + +__author__ = "Srinivas Pandruvada" +__copyright__ = " Copyright (c) 2017, Intel Corporation. " +__license__ = "GPL version 2" + + +MAX_CPUS = 256 + +# Define the csv file columns +C_COMM = 18 +C_GHZ = 17 +C_ELAPSED = 16 +C_SAMPLE = 15 +C_DURATION = 14 +C_LOAD = 13 +C_BOOST = 12 +C_FREQ = 11 +C_TSC = 10 +C_APERF = 9 +C_MPERF = 8 +C_TO = 7 +C_FROM = 6 +C_SCALED = 5 +C_CORE = 4 +C_USEC = 3 +C_SEC = 2 +C_CPU = 1 + +global sample_num, last_sec_cpu, last_usec_cpu, start_time, testname, trace_file + +# 11 digits covers uptime to 115 days +getcontext().prec = 11 + +sample_num =0 +last_sec_cpu = [0] * MAX_CPUS +last_usec_cpu = [0] * MAX_CPUS + +def print_help(driver_name): + print('%s_tracer.py:'%driver_name) + print(' Usage:') + print(' If the trace file is available, then to simply parse and plot, use (sudo not required):') + print(' ./%s_tracer.py [-c cpus] -t -n '%driver_name) + print(' Or') + print(' ./%s_tracer.py [--cpu cpus] ---trace_file --name '%driver_name) + print(' To generate trace file, parse and plot, use (sudo required):') + print(' sudo ./%s_tracer.py [-c cpus] -i -n -m '%driver_name) + print(' Or') + print(' sudo ./%s_tracer.py [--cpu cpus] --interval --name --memory '%driver_name) + print(' Optional argument:') + print(' cpus: comma separated list of CPUs') + print(' kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240') + print(' Output:') + print(' If not already present, creates a "results/test_name" folder in the current working directory with:') + print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') + print(' cpu???.csv - comma seperated values file for CPU number ???.') + print(' *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.') + print(' Notes:') + print(' Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.') + print(' Maximum number of CPUs is {0:d}. If there are more the script will abort with an error.'.format(MAX_CPUS)) + 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.') + print(' Empty y range warnings for autoscaled plots can occur and can be ignored.') + +def plot_perf_busy_with_sample(cpu_index): + """ Plot method to per cpu information """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y1 range + g_plot('set y2range [0:200]') + g_plot('set y2tics 0, 10') + g_plot('set title "{} : cpu perf busy vs. sample : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) +# Override common + g_plot('set xlabel "Samples"') + g_plot('set ylabel "P-State"') + g_plot('set y2label "Scaled Busy/performance/io-busy(%)"') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_SAMPLE, C_CORE)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE, C_SCALED)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO)) + +def plot_perf_busy(cpu_index): + """ Plot some per cpu information """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_perf_busy.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y1 range + g_plot('set y2range [0:200]') + g_plot('set y2tics 0, 10') + g_plot('set title "{} : perf busy : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) + g_plot('set ylabel "P-State"') + g_plot('set y2label "Scaled Busy/performance/io-busy(%)"') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_ELAPSED, C_CORE)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED, C_SCALED)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED, C_BOOST)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO)) + +def plot_durations(cpu_index): + """ Plot per cpu durations """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_durations.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set title "{} : durations : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) + g_plot('set ylabel "Timer Duration (MilliSeconds)"') +# override common + g_plot('set key off') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION)) + +def plot_loads(cpu_index): + """ Plot per cpu loads """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_loads.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:100]') + g_plot('set ytics 0, 10') + g_plot('set title "{} : loads : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) + g_plot('set ylabel "CPU load (percent)"') +# override common + g_plot('set key off') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD)) + +def plot_pstate_cpu_with_sample(): + """ Plot all cpu information """ + + if os.path.exists('cpu.csv'): + output_png = 'all_cpu_pstates_vs_samples.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range +# override common + g_plot('set xlabel "Samples"') + g_plot('set ylabel "P-State"') + g_plot('set title "{} : cpu pstate vs. sample : {:%F %H:%M}"'.format(testname, datetime.now())) + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_pstate_cpu(): + """ Plot all cpu information from csv files """ + + output_png = 'all_cpu_pstates.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "P-State"') + g_plot('set title "{} : cpu pstates : {:%F %H:%M}"'.format(testname, datetime.now())) + +# the following command is really cool, but doesn't work with the CPU masking option because it aborts on the first missing file. +# 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' +# + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_TO) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_load_cpu(): + """ Plot all cpu loads """ + + output_png = 'all_cpu_loads.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:100]') + g_plot('set ylabel "CPU load (percent)"') + g_plot('set title "{} : cpu loads : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_frequency_cpu(): + """ Plot all cpu frequencies """ + + output_png = 'all_cpu_frequencies.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "CPU Frequency (GHz)"') + g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_duration_cpu(): + """ Plot all cpu durations """ + + output_png = 'all_cpu_durations.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "Timer Duration (MilliSeconds)"') + g_plot('set title "{} : cpu durations : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_scaled_cpu(): + """ Plot all cpu scaled busy """ + + output_png = 'all_cpu_scaled.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "Scaled Busy (Unitless)"') + g_plot('set title "{} : cpu scaled busy : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_boost_cpu(): + """ Plot all cpu IO Boosts """ + + output_png = 'all_cpu_boost.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:100]') + g_plot('set ylabel "CPU IO Boost (percent)"') + g_plot('set title "{} : cpu io boost : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_ghz_cpu(): + """ Plot all cpu tsc ghz """ + + output_png = 'all_cpu_ghz.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "TSC Frequency (GHz)"') + g_plot('set title "{} : cpu TSC Frequencies (Sanity check calculation) : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).decode('utf-8').replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def common_all_gnuplot_settings(output_png): + """ common gnuplot settings for multiple CPUs one one graph. """ + + g_plot = common_gnuplot_settings() + g_plot('set output "' + output_png + '"') + return(g_plot) + +def common_gnuplot_settings(): + """ common gnuplot settings. """ + + g_plot = Gnuplot.Gnuplot(persist=1) +# The following line is for rigor only. It seems to be assumed for .csv files + g_plot('set datafile separator \",\"') + g_plot('set ytics nomirror') + g_plot('set xtics nomirror') + g_plot('set xtics font ", 10"') + g_plot('set ytics font ", 10"') + g_plot('set tics out scale 1.0') + g_plot('set grid') + g_plot('set key out horiz') + g_plot('set key bot center') + g_plot('set key samplen 2 spacing .8 font ", 9"') + g_plot('set term png size 1200, 600') + g_plot('set title font ", 11"') + g_plot('set ylabel font ", 10"') + g_plot('set xlabel font ", 10"') + g_plot('set xlabel offset 0, 0.5') + g_plot('set xlabel "Elapsed Time (Seconds)"') + return(g_plot) + +def set_4_plot_linestyles(g_plot): + """ set the linestyles used for 4 plots in 1 graphs. """ + + g_plot('set style line 1 linetype 1 linecolor rgb "green" pointtype -1') + g_plot('set style line 2 linetype 1 linecolor rgb "red" pointtype -1') + g_plot('set style line 3 linetype 1 linecolor rgb "purple" pointtype -1') + g_plot('set style line 4 linetype 1 linecolor rgb "blue" pointtype -1') + +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, cpu_mask): + """ Store master csv file information """ + + global graph_data_present + + if cpu_mask[cpu_int] == 0: + return + + try: + f_handle = open('cpu.csv', 'a') + 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) + f_handle.write(string_buffer); + f_handle.close() + except: + print('IO error cpu.csv') + return + + graph_data_present = True; + +def split_csv(current_max_cpu, cpu_mask): + """ seperate the all csv file into per CPU csv files. """ + + if os.path.exists('cpu.csv'): + for index in range(0, current_max_cpu + 1): + if cpu_mask[int(index)] != 0: + os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index)) + os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index)) + +def fix_ownership(path): + """Change the owner of the file to SUDO_UID, if required""" + + uid = os.environ.get('SUDO_UID') + gid = os.environ.get('SUDO_GID') + if uid is not None: + os.chown(path, int(uid), int(gid)) + +def cleanup_data_files(): + """ clean up existing data files """ + + if os.path.exists('cpu.csv'): + os.remove('cpu.csv') + f_handle = open('cpu.csv', 'a') + 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') + f_handle.write('\n') + f_handle.close() + +def clear_trace_file(): + """ Clear trace file """ + + try: + f_handle = open('/sys/kernel/tracing/trace', 'w') + f_handle.close() + except: + print('IO error clearing trace file ') + sys.exit(2) + +def enable_trace(trace_file): + """ Enable trace """ + + try: + open(trace_file,'w').write("1") + except: + print('IO error enabling trace ') + sys.exit(2) + +def disable_trace(trace_file): + """ Disable trace """ + + try: + open(trace_file, 'w').write("0") + except: + print('IO error disabling trace ') + sys.exit(2) + +def set_trace_buffer_size(memory): + """ Set trace buffer size """ + + try: + with open('/sys/kernel/tracing/buffer_size_kb', 'w') as fp: + fp.write(memory) + except: + print('IO error setting trace buffer size ') + sys.exit(2) + +def free_trace_buffer(): + """ Free the trace buffer memory """ + + try: + open('/sys/kernel/tracing/buffer_size_kb' + , 'w').write("1") + except: + print('IO error freeing trace buffer ') + sys.exit(2) + +def read_trace_data(filename, cpu_mask): + """ Read and parse trace data """ + + global current_max_cpu + global sample_num, last_sec_cpu, last_usec_cpu, start_time + + try: + data = open(filename, 'r').read() + except: + print('Error opening ', filename) + sys.exit(2) + + for line in data.splitlines(): + search_obj = \ + re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\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) + + time_pre_dec = search_obj.group(6) + time_post_dec = search_obj.group(8) + core_busy = search_obj.group(10) + scaled = search_obj.group(12) + _from = search_obj.group(14) + _to = search_obj.group(16) + mperf = search_obj.group(18) + aperf = search_obj.group(20) + tsc = search_obj.group(22) + freq = search_obj.group(24) + common_comm = search_obj.group(2).replace(' ', '') + + # Not all kernel versions have io_boost field + io_boost = '0' + search_obj = re.search(r'.*?io_boost=(\d+)', line) + if search_obj: + io_boost = search_obj.group(1) + + if sample_num == 0 : + start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) + sample_num += 1 + + if last_sec_cpu[cpu_int] == 0 : + last_sec_cpu[cpu_int] = time_pre_dec + last_usec_cpu[cpu_int] = time_post_dec + else : + duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int])) + duration_ms = Decimal(duration_us) / Decimal(1000) + last_sec_cpu[cpu_int] = time_pre_dec + last_usec_cpu[cpu_int] = time_post_dec + elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time + load = Decimal(int(mperf)*100)/ Decimal(tsc) + freq_ghz = Decimal(freq)/Decimal(1000000) +# Sanity check calculation, typically anomalies indicate missed samples +# However, check for 0 (should never occur) + tsc_ghz = Decimal(0) + if duration_ms != Decimal(0) : + tsc_ghz = Decimal(tsc)/duration_ms/Decimal(1000000) + 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, cpu_mask) + + if cpu_int > current_max_cpu: + current_max_cpu = cpu_int +# End of for each trace line loop +# Now seperate the main overall csv file into per CPU csv files. + split_csv(current_max_cpu, cpu_mask) + +def signal_handler(signal, frame): + print(' SIGINT: Forcing cleanup before exit.') + if interval: + disable_trace(trace_file) + clear_trace_file() + # Free the memory + free_trace_buffer() + sys.exit(0) + +if __name__ == "__main__": + trace_file = "/sys/kernel/tracing/events/power/pstate_sample/enable" + signal.signal(signal.SIGINT, signal_handler) + + interval = "" + filename = "" + cpu_list = "" + testname = "" + memory = "10240" + graph_data_present = False; + + valid1 = False + valid2 = False + + cpu_mask = zeros((MAX_CPUS,), dtype=int) + + try: + opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="]) + except getopt.GetoptError: + print_help('intel_pstate') + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + print_help('intel_pstate') + sys.exit() + elif opt in ("-t", "--trace_file"): + valid1 = True + location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + filename = os.path.join(location, arg) + elif opt in ("-i", "--interval"): + valid1 = True + interval = arg + elif opt in ("-c", "--cpu"): + cpu_list = arg + elif opt in ("-n", "--name"): + valid2 = True + testname = arg + elif opt in ("-m", "--memory"): + memory = arg + + if not (valid1 and valid2): + print_help('intel_pstate') + sys.exit() + + if cpu_list: + for p in re.split("[,]", cpu_list): + if int(p) < MAX_CPUS : + cpu_mask[int(p)] = 1 + else: + for i in range (0, MAX_CPUS): + cpu_mask[i] = 1 + + if not os.path.exists('results'): + os.mkdir('results') + # The regular user needs to own the directory, not root. + fix_ownership('results') + + os.chdir('results') + if os.path.exists(testname): + print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.') + sys.exit() + os.mkdir(testname) + # The regular user needs to own the directory, not root. + fix_ownership(testname) + os.chdir(testname) + + # Temporary (or perhaps not) + cur_version = sys.version_info + print('python version (should be >= 2.7):') + print(cur_version) + + # Left as "cleanup" for potential future re-run ability. + cleanup_data_files() + + if interval: + filename = "/sys/kernel/tracing/trace" + clear_trace_file() + set_trace_buffer_size(memory) + enable_trace(trace_file) + print('Sleeping for ', interval, 'seconds') + time.sleep(int(interval)) + disable_trace(trace_file) + + current_max_cpu = 0 + + read_trace_data(filename, cpu_mask) + + if interval: + clear_trace_file() + # Free the memory + free_trace_buffer() + + if graph_data_present == False: + print('No valid data to plot') + sys.exit(2) + + for cpu_no in range(0, current_max_cpu + 1): + plot_perf_busy_with_sample(cpu_no) + plot_perf_busy(cpu_no) + plot_durations(cpu_no) + plot_loads(cpu_no) + + plot_pstate_cpu_with_sample() + plot_pstate_cpu() + plot_load_cpu() + plot_frequency_cpu() + plot_duration_cpu() + plot_scaled_cpu() + plot_boost_cpu() + plot_ghz_cpu() + + # It is preferrable, but not necessary, that the regular user owns the files, not root. + for root, dirs, files in os.walk('.'): + for f in files: + fix_ownership(f) + + os.chdir('../../') -- cgit v1.2.3