Skip to content

Commit

Permalink
Merge pull request #19 from ashrafMahgoub/master
Browse files Browse the repository at this point in the history
Fix -pid bug + Update README
  • Loading branch information
ashrafMahgoub authored Mar 20, 2023
2 parents 31c0b0e + 5b941a8 commit d54d178
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 84 deletions.
46 changes: 35 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[![Build](https://github.com/intel/PerfSpect/actions/workflows/build.yml/badge.svg)](https://github.com/intel/PerfSpect/actions/workflows/build.yml)
[![License](https://img.shields.io/badge/License-BSD--3-blue)](https://github.com/intel/PerfSpect/blob/master/LICENSE)
# PerfSpect · [![Build](https://github.com/intel/PerfSpect/actions/workflows/build.yml/badge.svg)](https://github.com/intel/PerfSpect/actions/workflows/build.yml)[![License](https://img.shields.io/badge/License-BSD--3-blue)](https://github.com/intel/PerfSpect/blob/master/LICENSE)

# PerfSpect
[Quick Start](#quick-start-requires-perf-installed) | [Requirements](#requirements) | [Build from source](#build-from-source) | [Collection](#collection) | [Post-processing](#post-processing) | [Caveats](#caveats) | [How to contribute](#how-to-contribute)

PerfSpect is a system performance characterization tool based on linux perf targeting Intel microarchitectures.
The tool has two parts

1. perf collection to collect underlying PMU (Performance Monitoring Unit) counters
2. post processing that generates csv output of performance metrics.
### Quick start (requires perf installed)

## Quick start (requires perf installed)
```
wget -qO- https://github.com/intel/PerfSpect/releases/latest/download/perfspect.tgz | tar xvz
cd perfspect
Expand All @@ -19,7 +19,31 @@ sudo ./perf-postprocess -r results/perfstat.csv --html perfstat.html
![PerfSpect BS](images/basic_stats.JPG "perfspect-bs")
![perfspect-demo1](https://user-images.githubusercontent.com/5321018/205159259-3654fa12-74d6-4cb5-8194-ea1b66aadb25.gif)

## Building binaries from source code
## Requirements
### Packages:
- **perf** - PerfSpect uses the Linux perf tool to collect PMU counters
- **lscgroup** - Perfspect needs lscgroup from the cgroup-tools (libcgroup on RHEL/CentOS) package when collecting data for containers

### Supported kernels

| Xeon Generation | Minimum Kernel |
| - | - |
| Broadwell | kernel 4.15 |
| Skylake | kernel 4.15 |
| Cascadelake | kernel 4.15 |
| Icelake | kernel 5.9 |
| Sapphire Rapids | kernel 5.12 |

### Supported Operating Systems:
- Ubuntu 16.04 and newer
- centos 7 and newer
- Amazon Linux 2
- RHEL 9
- Debian 11

*Note: PerfSpect may work on other Linux distributions, but has not been thoroughly tested*

## Build from source

Requires recent python and golang.

Expand All @@ -30,7 +54,7 @@ make

On successful build, binaries will be created in "dist" folder

### 1. Perf collection:
## Collection:

```
(sudo) ./perf-collect (options) -- Some options can be used only with root privileges
Expand Down Expand Up @@ -68,7 +92,7 @@ Options:
Instance type: Options include - VM,BM
```

#### Examples
### Examples

1. sudo ./perf-collect (collect PMU counters using predefined architecture specific event file until collection is terminated)
2. sudo ./perf-collect -m 10 -t 30 (sets event multiplexing interval to 10ms and collects PMU counters for 30 seconds using default architecture specific event file)
Expand All @@ -77,12 +101,12 @@ Options:
5. sudo ./perf-collect --metadata (collect system info and PMU event info without running perf, uses default outputfile if -o option is not used)
6. sudo ./perf-collect --cid "one or more container IDs from docker or kubernetes seperated by semicolon"

#### Notes
### Notes

1. Intel CPUs (until Cascadelake) have 3 fixed PMUs (cpu-cycles, ref-cycles, instructions) and 4 programmable PMUs. The events are grouped in event files with this assumption. However, some of the counters may not be available on some CPUs. You can check the correctness of the event file with dryrun and check the output for anamolies. Typically output will have "not counted", "unsuppported" or zero values for cpu-cycles if number of available counters are less than events in a group.
2. Globally pinned events can limit the number of counters available for perf event groups. On X86 systems NMI watchdog pins a fixed counter by default. NMI watchdog is disabled during perf collection if run as a sudo user. If NMI watchdog can't be disabled, event grouping will be forcefully disabled to let perf driver handle event multiplexing.

### 2. Perf Post Processing:
## Post-processing:

```
./perf-postprocess (options)
Expand Down Expand Up @@ -111,13 +135,13 @@ required arguments:
Raw CSV output from perf-collect
```

#### Examples
### Examples

./perf-postprocess -r results/perfstat.csv (post processes perfstat.csv and creates metric_out.csv, metric_out.average.csv, metric_out.raw.csv)

./perf-postprocess -r results/perfstat.csv --html perfstat.html (creates a report for TMA analysis and system level metric charts.)

#### Notes
### Notes

1. metric_out.csv : Time series dump of the metrics. The metrics are defined in events/metric.json
2. metric_out.averags.csv: Average of metrics over the collection period
Expand Down
40 changes: 25 additions & 15 deletions perf-collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from __future__ import print_function
import os
import platform
import sys
import subprocess # nosec
import shlex # nosec
Expand All @@ -16,6 +17,14 @@

from subprocess import PIPE, run # nosec

SUPPORTED_ARCHITECTURES = [
"Broadwell",
"Skylake",
"Cascadelake",
"Icelake",
"SapphireRapids",
]


# meta data gathering
def write_metadata(
Expand Down Expand Up @@ -46,10 +55,7 @@ def write_metadata(
modified.write("TSC Frequency(MHz)," + tsc_freq + ",\n")
modified.write("CPU count," + str(perf_helpers.get_cpu_count()) + ",\n")
modified.write("SOCKET count," + str(perf_helpers.get_socket_count()) + ",\n")
if args.pid or args.cid:
modified.write("HT count," + str(1) + ",\n")
else:
modified.write("HT count," + str(perf_helpers.get_ht_count()) + ",\n")
modified.write("HT count," + str(perf_helpers.get_ht_count()) + ",\n")
imc, cha, upi = perf_helpers.get_imc_cacheagent_count()
modified.write("IMC count," + str(imc) + ",\n")
modified.write("CHA count," + str(cha) + ",\n")
Expand Down Expand Up @@ -134,6 +140,9 @@ def is_safe_file(fname, substr):


if __name__ == "__main__":
if platform.system() != "Linux":
raise SystemExit("PerfSpect currently supports Linux only")

script_path = os.path.dirname(os.path.realpath(__file__))
# fix the pyinstaller path
if "_MEI" in script_path:
Expand Down Expand Up @@ -257,17 +266,19 @@ def is_safe_file(fname, substr):
raise SystemExit(
"Input argument dump interval is too large or too small, range is [0.1 to 300s]!"
)
if args.cloud and args.cloud not in ("AWS", "aws", "OCI", "oci", "oracle"):
parser.print_help()
raise SystemExit("Invalid csp/cloud")

# select architecture default event file if not supplied
procinfo = perf_helpers.get_cpuinfo()
arch, cpuname = perf_helpers.check_architecture(procinfo)
arch, cpuname = perf_helpers.get_arch_and_name(procinfo)
if not arch:
raise SystemExit(
f"Unrecognized CPU architecture. Supported architectures: {', '.join(SUPPORTED_ARCHITECTURES)}"
)
eventfile = args.eventfile
eventfilename = eventfile
perf_helpers.check_os()
if args.cloud and args.cloud not in ("AWS", "aws", "OCI", "oci", "oracle"):
parser.print_help()
raise SystemExit("Invalid csp/cloud")

if not eventfile:
is_vm = args.cloudtype in ("VM", "vm")
is_aws_vm = args.cloud in ("aws", "AWS", "amazon") and is_vm
Expand Down Expand Up @@ -295,10 +306,9 @@ def is_safe_file(fname, substr):
eventfile = "spr.txt"
if is_aws_vm:
eventfile = "spr_aws.txt"
else:
raise SystemExit(
"Unsupported architecture (currently supports IA -> Broadwell, Skylake, CascadeLake Icelake and SapphireRapids)"
)

if not eventfile:
raise SystemExit(f"Event file for arch ({arch}) not found.")

# Convert path of event file to relative path if being packaged by pyInstaller into a binary
if getattr(sys, "frozen", False):
Expand All @@ -312,7 +322,7 @@ def is_safe_file(fname, substr):
eventfile = script_path + "/events/" + eventfile
eventfilename = eventfile
else:
raise SystemExit("Unknow application type")
raise SystemExit("Unknown application type")

if not os.path.isfile(eventfile):
parser.print_usage()
Expand Down
6 changes: 6 additions & 0 deletions pmu-checker/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,11 @@ require (
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
9 changes: 0 additions & 9 deletions pmu-checker/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,21 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
17 changes: 1 addition & 16 deletions release_notes
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
RELEASE NOTES
PerfSpect

Xeon Micro-Architectures:
- Broadwell (minimum kernel 4.15)
- Skylake (minimum kernel 4.15)
- Cascadelake (minimum kernel 4.15)
- Icelake (minimum kernel 5.9)
- Sapphire Rapids (minimum kernel 5.12)

Operating Systems:
- Ubuntu 16.04 and newer
- centos 7 and newer
- Amazon Linux 2
- RHEL 9
- Debian 11

Note: PerfSpect may work on other Linux distributions, but has not been thoroughly tested
PerfSpect

* v1.2.0
PerfSpect supports BDX, SKX, CLX, ICX and SPR
Expand Down
42 changes: 12 additions & 30 deletions src/perf_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
###########################################################################################################

import os
import sys
import re
import fnmatch
import time
Expand Down Expand Up @@ -210,29 +209,20 @@ def get_lscpu():
return cpuinfo


def not_suported():
print(
"Current architecture not supported!\nThis version only suports Broadwell/Skylake/Cascadelake/Icelake. Exiting!"
)
sys.exit()


# Check if arch is broadwell/skyalke/cascadelake/icelake/sapphirerapids
def check_architecture(procinfo):
def get_arch_and_name(procinfo):
arch = modelname = ""
try:
model = int(procinfo[0]["model"].strip())
cpufamily = int(procinfo[0]["cpu family"].strip())
stepping = int(procinfo[0]["stepping"].strip())
vendor = str(procinfo[0]["vendor_id"].strip())
modelname = procinfo[0]["model name"].strip()

except KeyError:
# for non-Intel architectures
cpuinfo = get_lscpu()
modelname = str(cpuinfo["Model name"])
stepping = str(cpuinfo["Stepping"])
vendor = str(cpuinfo["Vendor ID"])

if vendor == "GenuineIntel":
if model == 85 and cpufamily == 6 and stepping == 4:
arch = "skylake"
Expand All @@ -244,11 +234,6 @@ def check_architecture(procinfo):
arch = "icelake"
elif model == 143 and cpufamily == 6 and stepping >= 3:
arch = "sapphirerapids"
else:
not_suported()

else:
not_suported()
return arch, modelname


Expand Down Expand Up @@ -343,12 +328,17 @@ def get_epoch(start_time):
# Requires cgroup-tools/libgroup-tools for ubuntu/centos
def get_cgroups_from_cids(cids):
cgroups = []
p = subprocess.Popen( # nosec
["lscgroup"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
) # nosec
try:
p = subprocess.Popen( # nosec
["lscgroup"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
) # nosec
except FileNotFoundError:
raise SystemExit(
"lscgroup not found; please install the lscgroup utility for container support"
)
out, err = p.communicate()
if err:
raise SystemExit("please install prerequisites(lscgroup)")
raise SystemExit(f"error calling lscgroup: {err}")
cgevent = "perf_event:"
for cid in cids:
match = [cid, cgevent]
Expand All @@ -357,7 +347,7 @@ def get_cgroups_from_cids(cids):
if all(x in s.decode() for x in match):
cgroups.append((s.decode().lstrip(cgevent)))
if len(cgroups) == 0:
raise SystemExit("invalid container ID " + cid)
raise SystemExit(f"invalid container ID: {cid}")
return cgroups


Expand Down Expand Up @@ -391,11 +381,3 @@ def fix_path_ownership(path, recursive=False):
fix_path_ownership(dirpath)
for filename in filenames:
fix_path_ownership(os.path.join(dirpath, filename))


def check_os():
import platform

curr_os = platform.system()
if curr_os != "Linux":
raise SystemExit("PerfSpect currently supports Linux-based OS only")
6 changes: 3 additions & 3 deletions src/prepare_perf_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ def prepare_perf_events(event_file, grouping, cpu_only):
["perf", "list"], universal_newlines=True
)
except FileNotFoundError:
raise SystemExit("perf not found; please install linux perf utility")
raise SystemExit("perf not found; please install the Linux perf utility")

except subprocess.CalledProcessError:
raise SystemExit("perf not found; please install linux perf utility")
except subprocess.CalledProcessError as e:
raise SystemExit(f"error calling Linux perf, error code: {e.returncode}")

unsupported_events = []
for line in fin:
Expand Down

0 comments on commit d54d178

Please sign in to comment.