From b5e02cebad27a761dcf9ee33ec0e51abf6e0431e Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Fri, 24 Feb 2023 09:00:17 -0800 Subject: [PATCH 01/15] adding SPR support --- .gitignore | 4 + CODE_OF_CONDUCT.md | 131 +++ CONTRIBUTING.md | 57 ++ Makefile | 73 +- Makefile_non_container | 95 --- README.md | 157 ++-- _version.txt | 2 +- build.sh | 52 -- builder/Dockerfile | 68 -- builder/build | 11 - builder/build_docker_image | 9 - builder/scripts/entrypoint | 10 - builder/shell | 12 - builder/test | 9 - events/bdx.txt | 2 +- events/clx.txt | 2 +- events/clx_aws.txt | 5 + events/icx.txt | 9 +- events/icx_aws.txt | 2 +- events/icx_oci.txt | 5 + events/metric_bdx.json | 662 ++++++++------- events/metric_icx.json | 363 +++++---- events/metric_icx_aws.json | 311 +++++++ events/metric_skx_clx.json | 764 +++++++++--------- events/metric_spr.json | 311 +++++++ events/skx.txt | 2 +- events/skx_aws.txt | 5 + events/skx_oci.txt | 5 + events/spr.txt | 149 ++++ non_container_build.sh | 18 - perf-collect.py | 24 +- perf-postprocess.py | 38 +- pmu-checker/Makefile | 9 +- pmu-checker/README.md | 26 +- pmu-checker/cmd/main.go | 149 ---- pmu-checker/go.mod | 13 +- pmu-checker/go.sum | 16 +- pmu-checker/msr/msr.go | 154 ---- pmu-checker/msr/result.go | 27 - pmu-checker/msr/testdata/dev/cpu/1/msr | 0 pmu-checker/msr/validate.go | 25 - pmu-checker/msr/validate_test.go | 22 - pmu-checker/pmu-checker.go | 311 +++++++ release_notes | 36 +- requirements.txt | 15 +- security.md | 5 + similarity-analyzer/README.md | 2 - similarity-analyzer/Reference/ICX/500.csv | 2 +- similarity-analyzer/Reference/ICX/502.csv | 2 +- similarity-analyzer/Reference/ICX/505.csv | 2 +- similarity-analyzer/Reference/ICX/520.csv | 2 +- similarity-analyzer/Reference/ICX/523.csv | 2 +- similarity-analyzer/Reference/ICX/525.csv | 2 +- similarity-analyzer/Reference/ICX/531.csv | 2 +- similarity-analyzer/Reference/ICX/541.csv | 2 +- similarity-analyzer/Reference/ICX/548.csv | 2 +- similarity-analyzer/Reference/ICX/557.csv | 2 +- similarity-analyzer/SimilarityAnalysis.xlsx | Bin 100376 -> 0 bytes similarity-analyzer/_version.txt | 1 - similarity-analyzer/data_formatter/README.md | 13 + similarity-analyzer/data_formatter/main.py | 42 +- .../data_formatter/requirements.txt | 2 + similarity-analyzer/dopca.py | 258 +++--- similarity-analyzer/requirements.txt | 11 +- src/__init__.py | 2 +- src/basic_stats.py | 5 + src/calibrate.c | 7 +- src/icicle.py | 8 +- src/perf_helpers.py | 31 +- src/prepare_perf_events.py | 85 +- src/report.py | 7 +- test/test_application.py | 218 ++++- 72 files changed, 2841 insertions(+), 2046 deletions(-) create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md delete mode 100644 Makefile_non_container delete mode 100755 build.sh delete mode 100644 builder/Dockerfile delete mode 100755 builder/build delete mode 100755 builder/build_docker_image delete mode 100644 builder/scripts/entrypoint delete mode 100755 builder/shell delete mode 100755 builder/test create mode 100644 events/metric_icx_aws.json create mode 100644 events/metric_spr.json create mode 100644 events/spr.txt delete mode 100755 non_container_build.sh delete mode 100644 pmu-checker/cmd/main.go delete mode 100644 pmu-checker/msr/msr.go delete mode 100644 pmu-checker/msr/result.go delete mode 100644 pmu-checker/msr/testdata/dev/cpu/1/msr delete mode 100644 pmu-checker/msr/validate.go delete mode 100644 pmu-checker/msr/validate_test.go create mode 100644 pmu-checker/pmu-checker.go create mode 100644 security.md delete mode 100644 similarity-analyzer/SimilarityAnalysis.xlsx delete mode 100644 similarity-analyzer/_version.txt create mode 100644 similarity-analyzer/data_formatter/README.md create mode 100644 similarity-analyzer/data_formatter/requirements.txt mode change 100755 => 100644 test/test_application.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f0a1a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/* +dist/* +pmu-checker/pmu-checker +src/libtsc.so diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1735498 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,131 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +CommunityCodeOfConduct AT intel DOT com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..12059c8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,57 @@ +# Contributing + +### License + +PerfSpect is licensed under the terms in [LICENSE](./LICENSE). By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. + +### Sign your work + +Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. \ No newline at end of file diff --git a/Makefile b/Makefile index 9262c90..ee9c55d 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,25 @@ +COMMIT_ID := $(shell git rev-parse --short=8 HEAD) +COMMIT_DATE := $(shell git show -s --format=%cd --date=short HEAD) VERSION_FILE := _version.txt +VERSION_BASE := $(COMMIT_DATE)_$(COMMIT_ID) VERSION_NUMBER := $(shell cat ${VERSION_FILE}) VERSION_PUBLIC := $(VERSION_NUMBER) -PACKAGE := perfspect_$(VERSION_NUMBER).tgz +PACKAGE_EXTERNAL := perfspect_$(VERSION_NUMBER).tgz BINARY_FINAL := perfspect BINARY_COLLECT := perf-collect BINARY_POSTPROCESS := perf-postprocess - default: all -.PHONY: all test default dist clean format format_check security_scan flakes mypy pytype source_check checkmake dist/$(PACKAGE) +.PHONY: all test default dist clean format format_check security_scan flakes source_check checkmake dist/version_file dist/$(SOURCE_PACKAGE) clean_dir: rm -rf build/* rm -rf dist/* - sudo rm -rf test/perf* + #sudo rm -rf test/$(BINARY_FINAL) rm -rf src/__pycache__ build_dir: clean_dir mkdir -p build - mkdir -p dist build/pmu-checker: cd pmu-checker && make @@ -26,76 +27,76 @@ build/pmu-checker: strip -s -p --strip-unneeded build/pmu-checker build/libtsc: - cd src && gcc -shared -o libtsc.so -fPIC calibrate.c + gcc -fno-strict-overflow -fno-delete-null-pointer-checks -fwrapv -fPIC -shared -o src/libtsc.so src/calibrate.c -build/collect: +build-public/collect: $(eval TMPDIR := $(shell mktemp -d build.XXXXXX)) mkdir -p $(TMPDIR)/src mkdir -p $(TMPDIR)/events cp src/* $(TMPDIR)/src && cp events/* $(TMPDIR)/events && cp *.py $(TMPDIR) - sed -i 's/PerfSpect_DEV_VERSION/$(VERSION_PUBLIC)/g' $(TMPDIR)/src/perf_helpers.py + sed -i 's/PerfSpect_DEV_VERSION/$(VERSION_PUBLIC)/g' $(TMPDIR)/src/perf_helpers.py cd $(TMPDIR) && pyinstaller -F perf-collect.py -n $(BINARY_COLLECT) \ --add-data "./src/libtsc.so:." \ --add-data "./events/bdx.txt:." \ --add-data "./events/skx.txt:." \ - --add-data "./events/skx_aws.txt:." \ - --add-data "./events/skx_oci.txt:." \ --add-data "./events/clx.txt:." \ - --add-data "./events/clx_aws.txt:." \ --add-data "./events/icx.txt:." \ + --add-data "./events/spr.txt:." \ --add-data "./events/icx_aws.txt:." \ - --add-data "./events/icx_oci.txt:." \ + --add-data "./events/clx_aws.txt:." \ + --add-data "./events/skx_aws.txt:." \ --add-binary "../build/pmu-checker:." \ - --runtime-tmpdir . + --runtime-tmpdir . \ + --exclude-module readline + cp $(TMPDIR)/dist/$(BINARY_COLLECT) build/ rm -rf $(TMPDIR) -build/postprocess: +build-public/postprocess: $(eval TMPDIR := $(shell mktemp -d build.XXXXXX)) - git clone https://github.com/danthedeckie/simpleeval.git - cp simpleeval/simpleeval.py . mkdir -p $(TMPDIR)/src mkdir -p $(TMPDIR)/events cp src/* $(TMPDIR)/src && cp events/* $(TMPDIR)/events && cp *.py $(TMPDIR) - sed -i 's/PerfSpect_DEV_VERSION/$(VERSION_PUBLIC)/g' $(TMPDIR)/src/perf_helpers.py + sed -i 's/PerfSpect_DEV_VERSION/$(VERSION_PUBLIC)/g' $(TMPDIR)/src/perf_helpers.py cd $(TMPDIR) && pyinstaller -F perf-postprocess.py -n perf-postprocess \ --add-data "./events/metric_skx_clx.json:." \ --add-data "./events/metric_bdx.json:." \ --add-data "./events/metric_icx.json:." \ - --add-data="simpleeval.py:." \ - --runtime-tmpdir . + --add-data "./events/metric_spr.json:." \ + --add-data "./events/metric_icx_aws.json:." \ + --runtime-tmpdir . \ + --exclude-module readline cp $(TMPDIR)/dist/perf-postprocess build/ - rm -rf simpleeval && rm -f simpleeval.py rm -rf $(TMPDIR) -dist/$(PACKAGE): build_dir build/pmu-checker build/libtsc build/collect build/postprocess - rm -rf dist/* - cp build/$(BINARY_COLLECT) dist/$(BINARY_COLLECT) - cp build/$(BINARY_POSTPROCESS) dist/$(BINARY_POSTPROCESS) - rm -rf build - +dist/$(PACKAGE_EXTERNAL): build_dir build/pmu-checker build/libtsc build-public/collect build-public/postprocess + rm -rf dist/$(BINARY_FINAL)/ + mkdir -p dist/$(BINARY_FINAL) + cp build/$(BINARY_COLLECT) dist/$(BINARY_FINAL)/$(BINARY_COLLECT) + cp build/$(BINARY_POSTPROCESS) dist/$(BINARY_FINAL)/$(BINARY_POSTPROCESS) + cp LICENSE dist/$(BINARY_FINAL)/ + cp README.md dist/$(BINARY_FINAL)/README.md + cd dist && tar -czf $(PACKAGE_EXTERNAL) $(BINARY_FINAL) + cd dist && cp -r $(BINARY_FINAL) ../build/ + rm -rf dist/$(BINARY_FINAL)/ + cd dist && md5sum $(PACKAGE_EXTERNAL) > $(PACKAGE_EXTERNAL).md5 test: - cp dist/$(BINARY_COLLECT) dist/$(BINARY_POSTPROCESS) test/ + cd dist && tar -xvf perfspect_$(VERSION_PUBLIC).tgz && cp -r $(BINARY_FINAL) ../test/. cd test && pytest -security_scan: src/*.py - bandit src - bandit *.py - format: black src black *.py format_check: black --check src - black --check *.py + black --check perf-collect.py perf-postprocess.py error_check: # ignore false positives - flake8 --ignore=E501,W503,F403,F405 src - flake8 --ignore=E203,E501,E722,W503,F403,F405 *.py + flake8 --ignore=E501,W503,F403,F405,E741 src + flake8 --ignore=E203,E501,E722,W503,F403,F405 *.py --exclude simpleeval.py,perfmon.py,average.py source_check: security_scan format_check error_check -dist: source_check dist/$(PACKAGE) - +dist: source_check dist/$(PACKAGE_EXTERNAL) diff --git a/Makefile_non_container b/Makefile_non_container deleted file mode 100644 index 6f17725..0000000 --- a/Makefile_non_container +++ /dev/null @@ -1,95 +0,0 @@ -VERSION_FILE := _version.txt -VERSION_NUMBER := $(shell cat ${VERSION_FILE}) -VERSION_PUBLIC := $(VERSION_NUMBER) -PACKAGE := perfspect_$(VERSION_NUMBER).tgz -BINARY_FINAL := perfspect -BINARY_COLLECT := perf-collect -BINARY_POSTPROCESS := perf-postprocess - -default: all - -.PHONY: all test default dist clean format format_check security_scan flakes mypy pytype source_check checkmake dist/$(PACKAGE) - -clean_dir: - sudo rm -rf build/* - rm -rf dist/* - sudo rm -rf test/perf* - rm -rf src/__pycache__ - -build_dir: clean_dir - mkdir -p build - mkdir -p dist - -build/pmu-checker: - cd pmu-checker && make - cp pmu-checker/pmu-checker build/ - strip -s -p --strip-unneeded build/pmu-checker - -build/libtsc: - cd src && gcc -shared -o libtsc.so -fPIC calibrate.c - -build/collect: - sed -i 's/PerfSpect_DEV_VERSION/$(VERSION_PUBLIC)/g' src/perf_helpers.py - pyinstaller -F perf-collect.py -n $(BINARY_COLLECT) \ - --add-data "./src/libtsc.so:." \ - --add-data "./events/bdx.txt:." \ - --add-data "./events/skx.txt:." \ - --add-data "./events/skx_aws.txt:." \ - --add-data "./events/skx_oci.txt:." \ - --add-data "./events/clx.txt:." \ - --add-data "./events/clx_aws.txt:." \ - --add-data "./events/icx.txt:." \ - --add-data "./events/icx_aws.txt:." \ - --add-data "./events/icx_oci.txt:." \ - --add-binary "./build/pmu-checker:." \ - --runtime-tmpdir . - rm -rf build/$(BINARY_COLLECT) - cp dist/$(BINARY_COLLECT) build/ - rm -rf $(TMPDIR) - -build/postprocess: - git clone https://github.com/danthedeckie/simpleeval.git - cp simpleeval/simpleeval.py . - sed -i 's/PerfSpect_DEV_VERSION/$(VERSION_PUBLIC)/g' src/perf_helpers.py - pyinstaller -F perf-postprocess.py -n perf-postprocess \ - --add-data "./events/metric_skx_clx.json:." \ - --add-data "./events/metric_bdx.json:." \ - --add-data "./events/metric_icx.json:." \ - --add-data="simpleeval.py:." \ - --runtime-tmpdir . - rm -rf build/$(BINARY_POSTPROCESS) - cp dist/$(BINARY_POSTPROCESS) build/ - rm -rf simpleeval && rm -f simpleeval.py - rm -rf $(TMPDIR) - -dist/$(PACKAGE): build_dir build/pmu-checker build/libtsc build/collect build/postprocess - rm -rf dist/* - cp build/$(BINARY_COLLECT) dist/$(BINARY_COLLECT) - cp build/$(BINARY_POSTPROCESS) dist/$(BINARY_POSTPROCESS) - rm -rf build - - -test: - cp dist/$(BINARY_COLLECT) dist/$(BINARY_POSTPROCESS) test/ - cd test && pytest - -security_scan: src/*.py - bandit src - bandit *.py - -format: - black src - black perf*.py - -format_check: - black --check src - black --check perf*.py - -error_check: # ignore false positives - flake8 --ignore=E501,W503,F403,F405 src - flake8 --ignore=E203,E501,E722,W503,F403,F405 perf*.py - -source_check: format_check error_check - -dist: source_check dist/$(PACKAGE) - diff --git a/README.md b/README.md index bc3921d..b1db6cf 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ 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. @@ -16,123 +17,125 @@ The tool has two parts ### Prerequisites 1. Linux perf -2. Python3+ +2. Linux cgroup-tools ## Building binaries from source code -### Containerized build -#### pre-requisites - 1. Install docker - 2. Ensure docker commands execute without sudo (for example - `docker run hello-world` runs successfully) +Requires recent python and golang. -execute build.sh -`./build.sh` -### Non-containerized build -`./non_container_build.sh` +``` +pip3 install -r requirements.txt +make dist +``` On successful build, binaries will be created in "dist" folder ### 1. Perf collection: -`(sudo) ./perf-collect (options) -- Some options can be used only with root privileges ` - ``` -Options: - -h, --help (show this help message and exit) - - -v, --version display version info - - -e EVENTFILE, --eventfile EVENTFILE (Event file containing events to collect, default=events/) +(sudo) ./perf-collect (options) -- Some options can be used only with root privileges - -i INTERVAL, --interval INTERVAL (interval in seconds for time series dump, default=1) - - -m MUXINTERVAL, --muxinterval MUXINTERVAL (event mux interval for events in ms, default=0 i.e., will use the system default. Requires root privileges) - - -o OUTCSV, --outcsv OUTCSV (perf stat output in csv format, default=results/perfstat.csv) - - -a APP, --app APP (Application to run with perf-collect, perf collection ends after workload completion) - - -p PID, --pid PID perf-collect on selected PID(s) - - -c CID, --cid CID perf-collect on selected container id(s) - - -t TIMEOUT, --timeout TIMEOUT ( perf event collection time) - - --percore (Enable per core event collection) - - --nogroups (Disable perf event grouping, events are grouped by default as in the event file) - - --dryrun (Test if Performance Monitoring Counters are in-use, and collect stats for 10sec) - - --metadata (collect system info only, does not run perf) - - -csp CLOUD, --cloud CLOUD (Name of the Cloud Service Provider(ex- AWS), if collecting on cloud instances) - - -ct CLOUDTYPE, --cloudtype CLOUDTYPE (Instance type: Options include - VM/BM depending on the instance if it's baremetal or virtual system) +Options: + -h, --help show this help message and exit + -v, --version display version info + -e EVENTFILE, --eventfile EVENTFILE + Event file containing events to collect, + default=events/ + -i INTERVAL, --interval INTERVAL + interval in seconds for time series dump, default=1 + -m MUXINTERVAL, --muxinterval MUXINTERVAL + event mux interval in milli seconds, default=0 i.e. + will use the system default + -o OUTCSV, --outcsv OUTCSV + perf stat output in csv format, + default=results/perfstat.csv + -a APP, --app APP Application to run with perf-collect, perf collection + ends after workload completion + -p PID, --pid PID perf-collect on selected PID(s) + -c CID, --cid CID perf-collect on selected container ids + -t TIMEOUT, --timeout TIMEOUT + perf event collection time + --percore Enable per core event collection + --nogroups Disable perf event grouping, events are grouped by + default as in the event file + --dryrun Test if Performance Monitoring Counters are in-use, + and collect stats for 10sec to validate event file + correctness + --metadata collect system info only, does not run perf + --tma collect additional TMA events on supported + architectures + -csp CLOUD, --cloud CLOUD + Name of the Cloud Service Provider(AWS), if collecting + on cloud instances. Currently supporting AWS and OCI + -ct CLOUDTYPE, --cloudtype CLOUDTYPE + Instance type: Options include - VM,BM +``` - ``` #### 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) +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) 3. sudo ./perf-collect -a "myapp.sh myparameter" (collect perf for myapp.sh) 4. sudo ./perf-collect --dryrun (checks PMU usage, and collects PMU counters for 10 seconds using default architecture specific event file) 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 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 Postprocessing: - -`./perf-postprocess (options)` +### 2. Perf Post Processing: ``` -Options: - - -h, --help (show this help message and exit) - - -v, --version display version info - - -m METRICFILE, --metricfile METRICFILE (formula file, default=events/metric.json) - - -o OUTFILE, --outcsv OUTFILE (perf stat output file, csv or xlsx format is supported, default=results/metric_out.csv) - - --keepall (keep all intermediate csv files) - - -html HTML, --html HTML generate static HTML report - - --persocket (generate persocket metrics) +./perf-postprocess (options) - --percore (generate percore metrics) - - --epoch (time series in epoch format, default is sample count) +Options: + -h, --help show this help message and exit + --version, -v display version information + -m METRICFILE, --metricfile METRICFILE + formula file, default metric file for the architecture + -o OUTFILE, --outfile OUTFILE + perf stat outputs in csv format, + default=results/metric_out.csv + --persocket generate per socket metrics + --percore generate per core metrics + --keepall keep all intermediate csv files, use it for debug + purpose only + --epoch time series in epoch format, default is sample count + -csp CLOUD, --cloud CLOUD + Name of Cloud Service Provider(AWS), if you're + intending to postprocess on cloud instances + -html HTML, --html HTML + Static HTML report required arguments: - - -r RAWFILE, --rawfile RAWFILE (Raw CSV output from perf-collect) -``` + -r RAWFILE, --rawfile RAWFILE + Raw CSV output from perf-collect +``` #### 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 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 -3. metric_out.raw.csv: csv file with raw events normalized per second -4. Socket/core level metrics: Additonal csv files .socket.csv/.core.csv will be generated. Socket/core level data will be in added as new sheets if excel output is chosen - -## Things to note +3. metric_out.raw.csv: csv file with raw events normalized per second +4. Socket/core level metrics: Additonal csv files outputfile.socket.csv/outputfile.core.csv will be generated. Socket/core level data will be added as new sheets if excel output is chosen -1. The tool can collect only the counters supported by underlying linux perf version. -2. Current version supports Intel Icelake, Cascadelake, Skylake and Broadwell microarchitectures only. -3. Perf collection overhead will increase with increase in number of counters and/or dump interval. Using the right perf multiplexing (check perf-collection.py Notes for more details) interval to reduce overhead -4. If you run into locale issues - `UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 4519: ordinal not in range(128)`, more likely the locales needs to be set appropriately. You could also try running post-process step with `LC_ALL=C.UTF-8 LANG=C.UTF-8 ./perf-postprocess -r result.csv` +## Caveats -Special thanks to Vaishali Karanth for her fantastic contributions to the project. +1. The tool can collect only the counters supported by underlying linux perf version. +2. Current version supports Intel Sapphire Rapids, Icelake, Cascadelake, Skylake and Broadwell microarchitectures only. +3. Perf collection overhead will increase with increase in number of counters and/or dump interval. Using the right perf multiplexing (check perf-collection.py Notes for more details) interval to reduce overhead +4. If you run into locale issues - `UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 4519: ordinal not in range(128)`, more than likely the locales needs to be set appropriately. You could also try running post-process step with `LC_ALL=C.UTF-8 LANG=C.UTF-8 ./perf-postprocess -r result.csv` +5. The percore option is not supported while using cid. +6. The html report creation is not yet supported for cid collection. ## How to contribute + Create a pull request on github.com/intel/PerfSpect with your patch. Please make sure your patch is building without errors. A maintainer will contact you if there are questions or concerns. diff --git a/_version.txt b/_version.txt index 781dcb0..26aaba0 100644 --- a/_version.txt +++ b/_version.txt @@ -1 +1 @@ -1.1.3 +1.2.0 diff --git a/build.sh b/build.sh deleted file mode 100755 index 9720109..0000000 --- a/build.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -user_in_group() -{ - groups $1 | grep -q "\b$2\b" -} - -validate_user() -{ - if user_in_group $USER docker; then - echo "The user $USER is part of docker group; continue building....." - else - printf "The user $USER isn't part of the docker group, please add, verify the group membership is re-evaluated and re-run build.sh; exiting...\n" - exit 1 - fi -} - -#check if docker is installed; exit if otherwise -if [ -x "$(command -v docker)" ]; then - #check docker engine is running - if ! docker info > /dev/null 2>&1; then - echo "This build script uses docker, and it isn't running - please start docker and try again!" - exit 1 - fi - if grep -q docker /etc/group; - then - validate_user - else - printf "The docker group doesn't exist, please create docker group, add $USER to the docker group, verify the group membership is re-evaluated and re-run build.sh; exiting...\n" - exit 1 - fi - -else - echo "please install docker on your system and re-run build.sh; exiting....\n" - exit 1 -fi - - -printf "\n ***** If you are behind proxies, please ensure proxy settings are configured at builder/Dockerfile *****\n " - -#build docker image -if ! builder/build_docker_image ; then - echo "docker image build failed" - exit 1 -fi -#build binaries -if ! builder/build ; then - echo "build failed" - exit 1 -fi - -printf "\n ***** Build successful, the binaries are located in dist folder *****\n " diff --git a/builder/Dockerfile b/builder/Dockerfile deleted file mode 100644 index a2fd19e..0000000 --- a/builder/Dockerfile +++ /dev/null @@ -1,68 +0,0 @@ -FROM ubuntu:18.04 -# if using proxy please uncomment and edit proxy config below -# ENV http_proxy -# ENV https_proxy - -ENV LANG en_US.UTF-8 -RUN rm -rf /etc/apt/sources.list.d/ubuntu-esm-infra-trusty.list -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update && apt-get install -y sudo software-properties-common locales -RUN locale-gen en_US.UTF-8 -RUN echo "LANG=en_US.UTF-8" > /etc/default/locale - -ARG USERNAME -ARG USERID -RUN adduser --disabled-password --uid ${USERID} --gecos '' ${USERNAME} \ - && adduser ${USERNAME} sudo \ - && echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - -# set up volumes -VOLUME /scripts -VOLUME /workdir - - -# USER root -RUN apt-get update --fix-missing -RUN apt-get install -y software-properties-common curl git wget build-essential autotools-dev automake gawk zlib1g-dev libtool libaio-dev libaio1 pandoc pkgconf libcap-dev -RUN add-apt-repository -y ppa:deadsnakes/ppa -RUN apt-get update && apt-get install -y python3.7 python3.7-dev python3-distutils -RUN apt-get install -y netcat-openbsd -RUN wget https://golang.org/dl/go1.13.4.linux-amd64.tar.gz -RUN tar -C /usr/local -xzf go1.13.4.linux-amd64.tar.gz -ENV PATH="$PATH:/usr/local/go/bin" -RUN wget https://storage.googleapis.com/shellcheck/shellcheck-v0.7.0.linux.x86_64.tar.xz -RUN tar -xf shellcheck-v0.7.0.linux.x86_64.tar.xz -RUN cp shellcheck-v0.7.0/shellcheck /usr/bin/ -RUN go get github.com/mrtazz/checkmake -ENV BUILDER_NAME="builder" -ENV BUILDER_EMAIL="builder@company.com" -RUN cd /root/go/src/github.com/mrtazz/checkmake && make && cp checkmake /usr/local/bin/ -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.7 -RUN rm -rf /usr/lib/python3/dist-packages/yaml -RUN rm -rf /usr/lib/python3/dist-packages/PyYAML-* -RUN pip3 install PyYaml>=5.1.2 -RUN pip3 install pandas -RUN pip3 install pyinstaller==4.5.1 -RUN pip3 install black -RUN pip3 install bandit -RUN pip3 install flake8 -RUN pip3 install mypy -RUN pip3 install pytype -RUN pip3 install pytest -RUN pip3 install xlsxwriter -RUN pip3 install python-dateutil -COPY requirements.txt / -RUN pip3 install -r requirements.txt -RUN pip3 install --upgrade 'setuptools<45.0.0' -RUN apt-get update && apt-get install -y linux-tools-generic -RUN go get github.com/markbates/pkger/cmd/pkger -RUN cd /root/go/bin && cp pkger /usr/local/bin/ -ENV GOCACHE=/tmp -ENV GOPATH=/tmp - -# Run container as non-root user from here onwards -# so that build output files have the correct owner -USER ${USERNAME} - -# run bash script and process the input command -ENTRYPOINT [ "/bin/bash", "/scripts/entrypoint"] diff --git a/builder/build b/builder/build deleted file mode 100755 index 981de1e..0000000 --- a/builder/build +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -docker container run \ - --volume "$(pwd)"/builder/scripts:/scripts \ - --volume "$(pwd)":/workdir \ - --volume $HOME:$HOME:ro \ - --user $(id -u):$(id -g) \ - --rm \ - --name build_perfspect \ - perfspect_builder:v1 \ - build diff --git a/builder/build_docker_image b/builder/build_docker_image deleted file mode 100755 index 4637bd5..0000000 --- a/builder/build_docker_image +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# command to build: provide architecture(x86/arm) as a parameter to build - -docker image build \ - --build-arg USERNAME="${USER}" \ - --build-arg USERID="$(id -u ${USER})" \ - --file builder/Dockerfile \ - --tag perfspect_builder:v1 \ - . diff --git a/builder/scripts/entrypoint b/builder/scripts/entrypoint deleted file mode 100644 index 1703fec..0000000 --- a/builder/scripts/entrypoint +++ /dev/null @@ -1,10 +0,0 @@ -if [ "$1" = "shell" ]; then - echo "Starting Bash Shell" - /bin/bash -elif [ "$1" = "build" ]; then - echo "Starting Build" - cd workdir && make dist -elif [ "$1" = "test" ]; then - echo "Starting Tests" - cd workdir && make test -fi diff --git a/builder/shell b/builder/shell deleted file mode 100755 index 993229f..0000000 --- a/builder/shell +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -docker container run \ - --volume "$(pwd)"/builder/scripts:/scripts \ - --volume "$(pwd)":/workdir \ - --volume $HOME:$HOME:ro \ - --user $(id -u):$(id -g) \ - --rm \ - -it \ - --name build_perfspect \ - perfspect_builder:v1 \ - shell \ No newline at end of file diff --git a/builder/test b/builder/test deleted file mode 100755 index 800f261..0000000 --- a/builder/test +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -docker container run \ - --volume "$(pwd)"/builder/scripts:/scripts \ - --volume "$(pwd)":/workdir \ - --rm \ - --name build_perfspect \ - perfspect_builder:v1 \ - test \ No newline at end of file diff --git a/events/bdx.txt b/events/bdx.txt index 01fe10c..8ce30bc 100644 --- a/events/bdx.txt +++ b/events/bdx.txt @@ -1,5 +1,5 @@ ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### diff --git a/events/clx.txt b/events/clx.txt index 069ed7e..e0b18ee 100644 --- a/events/clx.txt +++ b/events/clx.txt @@ -1,5 +1,5 @@ ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### diff --git a/events/clx_aws.txt b/events/clx_aws.txt index a527208..d963db1 100644 --- a/events/clx_aws.txt +++ b/events/clx_aws.txt @@ -1,3 +1,8 @@ +########################################################################################################### +# Copyright (C) 2021-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + # Cascadelake event list for AWS instances, includes events needed for TMAM metrics cpu/event=0x51,umask=0x01,period=2000003,name='L1D.REPLACEMENT'/, diff --git a/events/icx.txt b/events/icx.txt index ff4e094..fdbb182 100644 --- a/events/icx.txt +++ b/events/icx.txt @@ -1,5 +1,5 @@ ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### @@ -118,8 +118,11 @@ cpu/event=0xb7,umask=0x01,offcore_rsp=0x8003C0001,name='OCR.DEMAND_DATA_RD.L3_HI cpu-cycles, ref-cycles; +# OCR group 1 (ICX PMU supports a maximum of two OCR counters per group) cpu/event=0xb7,umask=0x01,offcore_rsp=0x104000477,name='OCR.READS_TO_CORE.LOCAL_DRAM'/, -cpu/event=0xb7,umask=0x01,offcore_rsp=0x730000477,name='OCR.READS_TO_CORE.REMOTE_DRAM'/, +cpu/event=0xb7,umask=0x01,offcore_rsp=0x730000477,name='OCR.READS_TO_CORE.REMOTE_DRAM'/; + +# OCR group 2 (ICX PMU supports a maximum of two OCR counters per group) cpu/event=0xb7,umask=0x01,offcore_rsp=0x84002380,name='OCR.HWPF_L3.L3_MISS_LOCAL'/, cpu/event=0xb7,umask=0x01,offcore_rsp=0x90002380,name='OCR.HWPF_L3.REMOTE'/; @@ -157,4 +160,4 @@ cha/event=0x00,umask=0x00,name='UNC_CHA_CLOCKTICKS'/; #memory read/writes imc/event=0x04,umask=0x0f,name='UNC_M_CAS_COUNT.RD'/, -imc/event=0x04,umask=0x30,name='UNC_M_CAS_COUNT.WR'/; +imc/event=0x04,umask=0x30,name='UNC_M_CAS_COUNT.WR'/; \ No newline at end of file diff --git a/events/icx_aws.txt b/events/icx_aws.txt index a846a97..043383f 100644 --- a/events/icx_aws.txt +++ b/events/icx_aws.txt @@ -1,5 +1,5 @@ ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### diff --git a/events/icx_oci.txt b/events/icx_oci.txt index 125c505..b4ab72f 100644 --- a/events/icx_oci.txt +++ b/events/icx_oci.txt @@ -1,3 +1,8 @@ +########################################################################################################### +# Copyright (C) 2021-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + # Icelake event list for OCI instances cpu/event=0x51,umask=0x01,period=100003,name='L1D.REPLACEMENT'/, diff --git a/events/metric_bdx.json b/events/metric_bdx.json index 0415866..a606a23 100644 --- a/events/metric_bdx.json +++ b/events/metric_bdx.json @@ -1,333 +1,331 @@ [ - { - "name" : "metric_CPU operating frequency (in GHz)", - "expression" : "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" - }, - { - "name" : "metric_CPU utilization %", - "expression" : "100 * [ref-cycles] / [const_TSC]" - }, - { - "name" : "metric_CPU utilization% in kernel mode", - "expression" : "100 * [ref-cycles:k] / [const_TSC]" - }, - { - "name" : "metric_CPI", - "expression" : "[cpu-cycles] / [instructions]" - }, - { - "name" : "metric_kernel_CPI", - "expression" : "[cpu-cycles:k] / [instructions:k]" - }, - { - "name" : "metric_L1D MPI (includes data+rfo w/ prefetches)", - "tags" : "transaction", - "expression" : "[L1D.REPLACEMENT] / [instructions]" - }, - { - "name" : "metric_L1D demand data read hits per instr", - "expression" : "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" - }, - { - "name" : "metric_L1-I code read misses (w/ prefetches) per instr", - "expression" : "[L2_RQSTS.ALL_CODE_RD] / [instructions]" - }, - { - "name" : "metric_L2 demand data read hits per instr", - "expression" : "[MEM_LOAD_UOPS_RETIRED.L2_HIT] / [instructions]" - }, - { - "name" : "metric_L2 MPI (includes code+data+rfo w/ prefetches)", - "expression" : "[L2_LINES_IN.ALL] / [instructions]" - }, - { - "name" : "metric_L2 demand data read MPI", - "expression" : "[MEM_LOAD_UOPS_RETIRED.L2_MISS] / [instructions]" - }, - { - "name" : "metric_L2 demand code MPI", - "expression" : "[L2_RQSTS.CODE_RD_MISS] / [instructions]" - }, - { - "name" : "metric_LLC MPI", - "expression" : "([UNC_C_TOR_INSERTS.MISS_OPCODE.0x180] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x181] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x190] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x191] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x192] - [UNC_C_TOR_INSERTS.MISS_OPCODE.tid.0x180]) / [instructions]" - }, - { - "name" : "metric_LLC code read MPI (demand+prefetch)", - "expression" : "([UNC_C_TOR_INSERTS.MISS_OPCODE.0x181] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x191]) / [instructions]" - }, - { - "name" : "metric_LLC data read MPI (demand+prefetch)", - "expression" : "([UNC_C_TOR_INSERTS.MISS_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x192]) / [instructions]" - }, - { - "name" : "metric_LLC total HITM (per instr)", - "expression" : "[OCR.ALL_READS.L3_MISS.REMOTE_HITM] / [instructions]" - }, - { - "name" : "metric_LLC total HIT clean line forwards (per instr)", - "expression" : "[OCR.ALL_READS.L3_MISS.REMOTE_HIT_FORWARD] / [instructions]" - }, - { - "name" : "metric_Average LLC data read miss latency (in clks)", - "expression" : "[UNC_C_TOR_OCCUPANCY.MISS_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_OPCODE.0x182]" - }, - { - "name" : "metric_Average LLC data read miss latency (in ns)", - "expression" : "(1000000000 * [UNC_C_TOR_OCCUPANCY.MISS_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_OPCODE.0x182]) / ([UNC_C_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" - }, - { - "name" : "metric_Average LLC data read miss latency for LOCAL requests (in ns)", - "expression" : "(1000000000 * [UNC_C_TOR_OCCUPANCY.MISS_LOCAL_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182]) / ([UNC_C_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" - }, - { - "name" : "metric_Average LLC data read miss latency for REMOTE requests (in ns)", - "expression" : "(1000000000 * [UNC_C_TOR_OCCUPANCY.MISS_REMOTE_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182]) / ([UNC_C_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" - }, - { - "name" : "metric_ITLB MPI", - "expression" : "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" - }, - { - "name" : "metric_ITLB large page MPI", - "expression" : "[ITLB_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" - }, - { - "name" : "metric_DTLB load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" - }, - { - "name" : "metric_DTLB 2MB large page load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" - }, - { - "name" : "metric_DTLB store MPI", - "expression" : "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" - }, - { - "name" : "metric_DTLB load miss latency (in core clks)", - "expression" : "[DTLB_LOAD_MISSES.WALK_DURATION] / [DTLB_LOAD_MISSES.WALK_COMPLETED]" - }, - { - "name" : "metric_DTLB store miss latency (in core clks)", - "expression" : "[DTLB_STORE_MISSES.WALK_DURATION] / [DTLB_STORE_MISSES.WALK_COMPLETED]" - }, - { - "name" : "metric_NUMA %_Reads addressed to local DRAM", - "expression" : "100 * [UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182] / ([UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182])" - }, - { - "name" : "metric_NUMA %_Reads addressed to remote DRAM", - "expression" : "100 * [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182] / ([UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182])" - }, - { - "name" : "metric_uncore frequency GHz", - "expression" : "[UNC_C_CLOCKTICKS] / ([const_core_count] * [const_socket_count]) / 1000000000" - }, - { - "name" : "metric_package power (watts)", - "expression" : "[power/energy-pkg/]" - }, - { - "name" : "metric_DRAM power (watts)", - "expression" : "[power/energy-ram/]" - }, - { - "name" : "metric_core c6 residency %", - "expression" : "100 * [cstate_core/c6-residency/] / [const_TSC]" - }, - { - "name" : "metric_package c6 residency %", - "expression" : "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" - }, - { - "name" : "metric_memory bandwidth read (MB/sec)", - "expression" : "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" - }, - { - "name" : "metric_memory bandwidth write (MB/sec)", - "expression" : "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" - }, - { - "name" : "metric_memory bandwidth total (MB/sec)", - "expression" : "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" - }, - { - "name" : "metric_UPI Data transmit BW (MB/sec) (only data)", - "expression" : "([UNC_Q_TxL_FLITS_G0.DATA]) * 8 / 1000000" - }, - { - "name" : "metric_UPI Data transmit BW (MB/sec) (includes control)", - "expression" : "([UNC_Q_TxL_FLITS_G0.DATA] + [UNC_Q_TxL_FLITS_G0.NON_DATA]) * 8 / 1000000" - }, - { - "name" : "metric_UPI Transmit utilization_% (includes control)", - "expression" : "([UNC_Q_TxL_FLITS_G0.DATA] + [UNC_Q_TxL_FLITS_G0.NON_DATA]) * 100 / [UNC_Q_CLOCKTICKS]" - }, - { - "name" : "metric_IO_bandwidth_disk_or_network_writes (MB/sec)", - "expression" : "[UNC_C_TOR_INSERTS.OPCODE.0x19e] * 64 / 1000000" - }, - { - "name" : "metric_IO_bandwidth_disk_or_network_reads (MB/sec)", - "expression" : "([UNC_C_TOR_INSERTS.OPCODE.0x1c8] + [UNC_C_TOR_INSERTS.OPCODE.0x180]) * 64 / 1000000" - }, - { - "name" : "metric_TMAM_Info_cycles_both_threads_active(%)", - "expression" : "100 * ( (1 - ([CPU_CLK_THREAD_UNHALTED.ONE_THREAD_ACTIVE] / ([CPU_CLK_THREAD_UNHALTED.REF_XCLK_ANY] / 2)) ) if [const_thread_count] > 1 else 0)" - }, - { - "name" : "metric_TMAM_Info_CoreIPC", - "expression" : "[instructions] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_Frontend_Bound(%)", - "expression" : "100 * [IDQ_UOPS_NOT_DELIVERED.CORE] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" - }, - { - "name" : "metric_TMAM_..Frontend_Latency(%)", - "expression" : "100 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_....ICache_Misses(%)", - "expression" : "100 * [ICACHE.IFDATA_STALL] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....ITLB_Misses(%)", - "expression" : "100 * ((14 * [ITLB_MISSES.STLB_HIT]) + [ITLB_MISSES.WALK_DURATION_c1] + (7 * [ITLB_MISSES.WALK_COMPLETED] )) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....Branch_Resteers(%)", - "expression" : "100 * (([RS_EVENTS.EMPTY_CYCLES] - [ICACHE.IFDATA_STALL] - (14 * [ITLB_MISSES.STLB_HIT] + [ITLB_MISSES.WALK_DURATION_c1] + 7 * [ITLB_MISSES.WALK_COMPLETED])) / [RS_EVENTS.EMPTY_END]) * ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT] + [BACLEARS.ANY]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....DSB_Switches(%)", - "expression" : "100 * 2 * [DSB2MITE_SWITCHES.PENALTY_CYCLES] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....MS_Switches(%)", - "expression" : "100 * 2 * [IDQ.MS_SWITCHES] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_..Frontend_Bandwidth(%)", - "expression" : "100 * ([IDQ_UOPS_NOT_DELIVERED.CORE] - (4 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_Bad_Speculation(%)", - "expression" : "100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + ((4 * [INT_MISC.RECOVERY_CYCLES_ANY]) / [const_thread_count])) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " - }, - { - "name" : "metric_TMAM_..Branch_Mispredicts(%)", - "expression" : "([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * 100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_..Machine_Clears(%)", - "expression" : "([MACHINE_CLEARS.COUNT] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * 100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_Backend_bound(%)", - "expression" : "100 - (100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " - }, - { - "name" : "metric_TMAM_..Memory_Bound(%)", - "expression" : "100 * (1 - (([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [RESOURCE_STALLS.SB]) / ([CYCLE_ACTIVITY.STALLS_TOTAL] + [UOPS_EXECUTED.CYCLES_GE_1_UOPS_EXEC] - ( [UOPS_EXECUTED.CYCLES_GE_3_UOPS_EXEC] if ([instructions] / [cpu-cycles]) > 1.8 else [UOPS_EXECUTED.CYCLES_GE_2_UOPS_EXEC]) - ( [RS_EVENTS.EMPTY_CYCLES] if ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [CPU_CLK_UNHALTED.THREAD_ANY]) > 0.1 else 0) + [RESOURCE_STALLS.SB])" - }, - { - "name" : "metric_TMAM_....L1_Bound(%)", - "expression" : "100 * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......DTLB_Load(%)", - "expression" : "100 * ([DTLB_LOAD_MISSES.STLB_HIT] * 8 + [DTLB_LOAD_MISSES.WALK_DURATION_c1] + 7 * [DTLB_LOAD_MISSES.WALK_COMPLETED]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......Store_Fwd_Blk(%)", - "expression" : "100 * (13 * [LD_BLOCKS.STORE_FORWARD]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....L2_Bound(%)", - "expression" : "100 * ([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....L3_Bound(%)", - "expression" : "100 * [MEM_LOAD_UOPS_RETIRED.L3_HIT] / ([MEM_LOAD_UOPS_RETIRED.L3_HIT] + 7 * [MEM_LOAD_UOPS_RETIRED.L3_MISS]) * ([CYCLE_ACTIVITY.STALLS_L2_MISS] / [cpu-cycles])" - }, - { - "name" : "metric_TMAM_......L3_Latency(%)", - "expression" : "100 * 41 * [MEM_LOAD_UOPS_RETIRED.L3_HIT] * ( 1 + [MEM_LOAD_UOPS_RETIRED.HIT_LFB] / ( [MEM_LOAD_UOPS_RETIRED.L2_HIT] + [MEM_LOAD_UOPS_RETIRED.L3_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_HITM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD] ) ) / [cpu-cycles] " - }, - { - "name" : "metric_TMAM_......Contested_Accesses(%)", - "expression" : "100 * 60 * ([MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS]) * ( 1 + [MEM_LOAD_UOPS_RETIRED.HIT_LFB] / ( [MEM_LOAD_UOPS_RETIRED.L2_HIT] + [MEM_LOAD_UOPS_RETIRED.L3_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_HITM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD] ) ) / [cpu-cycles] " - }, - { - "name" : "metric_TMAM_......Data_Sharing(%)", - "expression" : "100 * 43 * [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] * ( 1 + [MEM_LOAD_UOPS_RETIRED.HIT_LFB] / ( [MEM_LOAD_UOPS_RETIRED.L2_HIT] + [MEM_LOAD_UOPS_RETIRED.L3_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_HITM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD] ) ) / [cpu-cycles] " - }, - { - "name" : "metric_TMAM_......SQ_Full(%)", - "expression" : "100 * ([OFFCORE_REQUESTS_BUFFER.SQ_FULL] / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_....MEM_Bound(%)", - "expression" : "100 * (1 - ( [MEM_LOAD_UOPS_RETIRED.L3_HIT] / ([MEM_LOAD_UOPS_RETIRED.L3_HIT] + 7 * [MEM_LOAD_UOPS_RETIRED.L3_MISS])) ) * ([CYCLE_ACTIVITY.STALLS_L2_MISS] / [cpu-cycles])" - }, - { - "name" : "metric_TMAM_......MEM_Bandwidth(%)", - "expression" : "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4], [cpu-cycles])) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......MEM_Latency(%)", - "expression" : "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4], [cpu-cycles])) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....Stores_Bound(%)", - "expression" : "100 * [RESOURCE_STALLS.SB] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......DTLB_Store(%)", - "expression" : "100 * (7 * [DTLB_STORE_MISSES.STLB_HIT] + [DTLB_STORE_MISSES.WALK_DURATION_c1]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_..Core_Bound(%)", - "expression" : "100 * ( 1 - (( [UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS] ) / ( 4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) * (1 - (([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [RESOURCE_STALLS.SB]) / ([CYCLE_ACTIVITY.STALLS_TOTAL] + [UOPS_EXECUTED.CYCLES_GE_1_UOPS_EXEC] - ( [UOPS_EXECUTED.CYCLES_GE_3_UOPS_EXEC] if ([instructions] / [cpu-cycles]) > 1.8 else [UOPS_EXECUTED.CYCLES_GE_2_UOPS_EXEC]) - ([RS_EVENTS.EMPTY_CYCLES] if ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [CPU_CLK_UNHALTED.THREAD_ANY]) > 0.1 else 0) + [RESOURCE_STALLS.SB])))" - }, - { - "name" : "metric_TMAM_....Divider(%)", - "expression" : "100 * [ARITH.FPU_DIV_ACTIVE] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_....Ports_Utilization(%)", - "expression" : "100 * (( [CYCLE_ACTIVITY.STALLS_TOTAL] + [UOPS_EXECUTED.CYCLES_GE_1_UOPS_EXEC] - ([UOPS_EXECUTED.CYCLES_GE_3_UOPS_EXEC] if ([instructions] / [cpu-cycles]) > 1.8 else [UOPS_EXECUTED.CYCLES_GE_2_UOPS_EXEC]) - ([RS_EVENTS.EMPTY_CYCLES] if ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [CPU_CLK_UNHALTED.THREAD_ANY]) > 0.1 else 0) + [RESOURCE_STALLS.SB]) - [RESOURCE_STALLS.SB] - [CYCLE_ACTIVITY.STALLS_MEM_ANY] ) /[cpu-cycles]" - }, - { - "name" : "metric_TMAM_......0_Port_Utilized(%)", - "expression" : "100 * (([UOPS_EXECUTED.CORE_i1_c1] / [const_thread_count]) if ([const_thread_count] > 1) else ([RS_EVENTS.EMPTY_CYCLES] if ([CYCLE_ACTIVITY.STALLS_TOTAL] - ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) ) > 0.1 else 0)) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]) " - }, - { - "name" : "metric_TMAM_......1_Port_Utilized(%)", - "expression" : "100 * (([UOPS_EXECUTED.CORE_c1] - [UOPS_EXECUTED.CORE_c2]) / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_......2_Port_Utilized(%)", - "expression" : "100 * (([UOPS_EXECUTED.CORE_c2] - [UOPS_EXECUTED.CORE_c3]) / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_......3m_Ports_Utilized(%)", - "expression" : "100 * ([UOPS_EXECUTED.CORE_c3] / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_Retiring(%)", - "expression" : "100 * [UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" - }, - { - "name" : "metric_TMAM_..Base(%)", - "expression" : "100 *(([UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) - (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))))" - }, - { - "name" : "metric_TMAM_..Microcode_Sequencer(%)", - "expression" : "100 * (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] )/ (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" - } - -] - + { + "name": "metric_CPU operating frequency (in GHz)", + "expression": "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" + }, + { + "name": "metric_CPU utilization %", + "expression": "100 * [ref-cycles] / [const_TSC]" + }, + { + "name": "metric_CPU utilization% in kernel mode", + "expression": "100 * [ref-cycles:k] / [const_TSC]" + }, + { + "name": "metric_CPI", + "expression": "[cpu-cycles] / [instructions]" + }, + { + "name": "metric_kernel_CPI", + "expression": "[cpu-cycles:k] / [instructions:k]" + }, + { + "name": "metric_L1D MPI (includes data+rfo w/ prefetches)", + "tags": "transaction", + "expression": "[L1D.REPLACEMENT] / [instructions]" + }, + { + "name": "metric_L1D demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" + }, + { + "name": "metric_L1-I code read misses (w/ prefetches) per instr", + "expression": "[L2_RQSTS.ALL_CODE_RD] / [instructions]" + }, + { + "name": "metric_L2 demand data read hits per instr", + "expression": "[MEM_LOAD_UOPS_RETIRED.L2_HIT] / [instructions]" + }, + { + "name": "metric_L2 MPI (includes code+data+rfo w/ prefetches)", + "expression": "[L2_LINES_IN.ALL] / [instructions]" + }, + { + "name": "metric_L2 demand data read MPI", + "expression": "[MEM_LOAD_UOPS_RETIRED.L2_MISS] / [instructions]" + }, + { + "name": "metric_L2 demand code MPI", + "expression": "[L2_RQSTS.CODE_RD_MISS] / [instructions]" + }, + { + "name": "metric_LLC MPI", + "expression": "([UNC_C_TOR_INSERTS.MISS_OPCODE.0x180] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x181] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x190] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x191] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x192] - [UNC_C_TOR_INSERTS.MISS_OPCODE.tid.0x180]) / [instructions]" + }, + { + "name": "metric_LLC code read MPI (demand+prefetch)", + "expression": "([UNC_C_TOR_INSERTS.MISS_OPCODE.0x181] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x191]) / [instructions]" + }, + { + "name": "metric_LLC data read MPI (demand+prefetch)", + "expression": "([UNC_C_TOR_INSERTS.MISS_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_OPCODE.0x192]) / [instructions]" + }, + { + "name": "metric_LLC total HITM (per instr)", + "expression": "[OCR.ALL_READS.L3_MISS.REMOTE_HITM] / [instructions]" + }, + { + "name": "metric_LLC total HIT clean line forwards (per instr)", + "expression": "[OCR.ALL_READS.L3_MISS.REMOTE_HIT_FORWARD] / [instructions]" + }, + { + "name": "metric_Average LLC data read miss latency (in clks)", + "expression": "[UNC_C_TOR_OCCUPANCY.MISS_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_OPCODE.0x182]" + }, + { + "name": "metric_Average LLC data read miss latency (in ns)", + "expression": "(1000000000 * [UNC_C_TOR_OCCUPANCY.MISS_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_OPCODE.0x182]) / ([UNC_C_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" + }, + { + "name": "metric_Average LLC data read miss latency for LOCAL requests (in ns)", + "expression": "(1000000000 * [UNC_C_TOR_OCCUPANCY.MISS_LOCAL_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182]) / ([UNC_C_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_Average LLC data read miss latency for REMOTE requests (in ns)", + "expression": "(1000000000 * [UNC_C_TOR_OCCUPANCY.MISS_REMOTE_OPCODE.0x182] / [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182]) / ([UNC_C_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_ITLB MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_ITLB large page MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB 2MB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB store MPI", + "expression": "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB load miss latency (in core clks)", + "expression": "[DTLB_LOAD_MISSES.WALK_DURATION] / [DTLB_LOAD_MISSES.WALK_COMPLETED]" + }, + { + "name": "metric_DTLB store miss latency (in core clks)", + "expression": "[DTLB_STORE_MISSES.WALK_DURATION] / [DTLB_STORE_MISSES.WALK_COMPLETED]" + }, + { + "name": "metric_NUMA %_Reads addressed to local DRAM", + "expression": "100 * [UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182] / ([UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182])" + }, + { + "name": "metric_NUMA %_Reads addressed to remote DRAM", + "expression": "100 * [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182] / ([UNC_C_TOR_INSERTS.MISS_LOCAL_OPCODE.0x182] + [UNC_C_TOR_INSERTS.MISS_REMOTE_OPCODE.0x182])" + }, + { + "name": "metric_uncore frequency GHz", + "expression": "[UNC_C_CLOCKTICKS] / ([const_core_count] * [const_socket_count]) / 1000000000" + }, + { + "name": "metric_package power (watts)", + "expression": "[power/energy-pkg/]" + }, + { + "name": "metric_DRAM power (watts)", + "expression": "[power/energy-ram/]" + }, + { + "name": "metric_core c6 residency %", + "expression": "100 * [cstate_core/c6-residency/] / [const_TSC]" + }, + { + "name": "metric_package c6 residency %", + "expression": "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" + }, + { + "name": "metric_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" + }, + { + "name": "metric_UPI Data transmit BW (MB/sec) (only data)", + "expression": "([UNC_Q_TxL_FLITS_G0.DATA]) * 8 / 1000000" + }, + { + "name": "metric_UPI Data transmit BW (MB/sec) (includes control)", + "expression": "([UNC_Q_TxL_FLITS_G0.DATA] + [UNC_Q_TxL_FLITS_G0.NON_DATA]) * 8 / 1000000" + }, + { + "name": "metric_UPI Transmit utilization_% (includes control)", + "expression": "([UNC_Q_TxL_FLITS_G0.DATA] + [UNC_Q_TxL_FLITS_G0.NON_DATA]) * 100 / [UNC_Q_CLOCKTICKS]" + }, + { + "name": "metric_IO_bandwidth_disk_or_network_writes (MB/sec)", + "expression": "[UNC_C_TOR_INSERTS.OPCODE.0x19e] * 64 / 1000000" + }, + { + "name": "metric_IO_bandwidth_disk_or_network_reads (MB/sec)", + "expression": "([UNC_C_TOR_INSERTS.OPCODE.0x1c8] + [UNC_C_TOR_INSERTS.OPCODE.0x180]) * 64 / 1000000" + }, + { + "name": "metric_TMAM_Info_cycles_both_threads_active(%)", + "expression": "100 * ( (1 - ([CPU_CLK_THREAD_UNHALTED.ONE_THREAD_ACTIVE] / ([CPU_CLK_THREAD_UNHALTED.REF_XCLK_ANY] / 2)) ) if [const_thread_count] > 1 else 0)" + }, + { + "name": "metric_TMAM_Info_CoreIPC", + "expression": "[instructions] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_Frontend_Bound(%)", + "expression": "100 * [IDQ_UOPS_NOT_DELIVERED.CORE] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" + }, + { + "name": "metric_TMAM_..Frontend_Latency(%)", + "expression": "100 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_....ICache_Misses(%)", + "expression": "100 * [ICACHE.IFDATA_STALL] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....ITLB_Misses(%)", + "expression": "100 * ((14 * [ITLB_MISSES.STLB_HIT]) + [ITLB_MISSES.WALK_DURATION_c1] + (7 * [ITLB_MISSES.WALK_COMPLETED] )) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....Branch_Resteers(%)", + "expression": "100 * (([RS_EVENTS.EMPTY_CYCLES] - [ICACHE.IFDATA_STALL] - (14 * [ITLB_MISSES.STLB_HIT] + [ITLB_MISSES.WALK_DURATION_c1] + 7 * [ITLB_MISSES.WALK_COMPLETED])) / [RS_EVENTS.EMPTY_END]) * ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT] + [BACLEARS.ANY]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....DSB_Switches(%)", + "expression": "100 * 2 * [DSB2MITE_SWITCHES.PENALTY_CYCLES] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....MS_Switches(%)", + "expression": "100 * 2 * [IDQ.MS_SWITCHES] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_..Frontend_Bandwidth(%)", + "expression": "100 * ([IDQ_UOPS_NOT_DELIVERED.CORE] - (4 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_Bad_Speculation(%)", + "expression": "100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + ((4 * [INT_MISC.RECOVERY_CYCLES_ANY]) / [const_thread_count])) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " + }, + { + "name": "metric_TMAM_..Branch_Mispredicts(%)", + "expression": "([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * 100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_..Machine_Clears(%)", + "expression": "([MACHINE_CLEARS.COUNT] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * 100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_Backend_bound(%)", + "expression": "100 - (100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " + }, + { + "name": "metric_TMAM_..Memory_Bound(%)", + "expression": "100 * (1 - (([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [RESOURCE_STALLS.SB]) / ([CYCLE_ACTIVITY.STALLS_TOTAL] + [UOPS_EXECUTED.CYCLES_GE_1_UOPS_EXEC] - ( [UOPS_EXECUTED.CYCLES_GE_3_UOPS_EXEC] if ([instructions] / [cpu-cycles]) > 1.8 else [UOPS_EXECUTED.CYCLES_GE_2_UOPS_EXEC]) - ( [RS_EVENTS.EMPTY_CYCLES] if ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [CPU_CLK_UNHALTED.THREAD_ANY]) > 0.1 else 0) + [RESOURCE_STALLS.SB])" + }, + { + "name": "metric_TMAM_....L1_Bound(%)", + "expression": "100 * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......DTLB_Load(%)", + "expression": "100 * ([DTLB_LOAD_MISSES.STLB_HIT] * 8 + [DTLB_LOAD_MISSES.WALK_DURATION_c1] + 7 * [DTLB_LOAD_MISSES.WALK_COMPLETED]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......Store_Fwd_Blk(%)", + "expression": "100 * (13 * [LD_BLOCKS.STORE_FORWARD]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....L2_Bound(%)", + "expression": "100 * ([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....L3_Bound(%)", + "expression": "100 * [MEM_LOAD_UOPS_RETIRED.L3_HIT] / ([MEM_LOAD_UOPS_RETIRED.L3_HIT] + 7 * [MEM_LOAD_UOPS_RETIRED.L3_MISS]) * ([CYCLE_ACTIVITY.STALLS_L2_MISS] / [cpu-cycles])" + }, + { + "name": "metric_TMAM_......L3_Latency(%)", + "expression": "100 * 41 * [MEM_LOAD_UOPS_RETIRED.L3_HIT] * ( 1 + [MEM_LOAD_UOPS_RETIRED.HIT_LFB] / ( [MEM_LOAD_UOPS_RETIRED.L2_HIT] + [MEM_LOAD_UOPS_RETIRED.L3_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_HITM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD] ) ) / [cpu-cycles] " + }, + { + "name": "metric_TMAM_......Contested_Accesses(%)", + "expression": "100 * 60 * ([MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS]) * ( 1 + [MEM_LOAD_UOPS_RETIRED.HIT_LFB] / ( [MEM_LOAD_UOPS_RETIRED.L2_HIT] + [MEM_LOAD_UOPS_RETIRED.L3_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_HITM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD] ) ) / [cpu-cycles] " + }, + { + "name": "metric_TMAM_......Data_Sharing(%)", + "expression": "100 * 43 * [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] * ( 1 + [MEM_LOAD_UOPS_RETIRED.HIT_LFB] / ( [MEM_LOAD_UOPS_RETIRED.L2_HIT] + [MEM_LOAD_UOPS_RETIRED.L3_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HIT] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_HITM] + [MEM_LOAD_UOPS_L3_HIT_RETIRED.XSNP_MISS] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.LOCAL_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_DRAM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_HITM] + [MEM_LOAD_UOPS_L3_MISS_RETIRED.REMOTE_FWD] ) ) / [cpu-cycles] " + }, + { + "name": "metric_TMAM_......SQ_Full(%)", + "expression": "100 * ([OFFCORE_REQUESTS_BUFFER.SQ_FULL] / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_....MEM_Bound(%)", + "expression": "100 * (1 - ( [MEM_LOAD_UOPS_RETIRED.L3_HIT] / ([MEM_LOAD_UOPS_RETIRED.L3_HIT] + 7 * [MEM_LOAD_UOPS_RETIRED.L3_MISS])) ) * ([CYCLE_ACTIVITY.STALLS_L2_MISS] / [cpu-cycles])" + }, + { + "name": "metric_TMAM_......MEM_Bandwidth(%)", + "expression": "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4], [cpu-cycles])) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......MEM_Latency(%)", + "expression": "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4], [cpu-cycles])) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....Stores_Bound(%)", + "expression": "100 * [RESOURCE_STALLS.SB] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......DTLB_Store(%)", + "expression": "100 * (7 * [DTLB_STORE_MISSES.STLB_HIT] + [DTLB_STORE_MISSES.WALK_DURATION_c1]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_..Core_Bound(%)", + "expression": "100 * ( 1 - (( [UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS] ) / ( 4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) * (1 - (([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [RESOURCE_STALLS.SB]) / ([CYCLE_ACTIVITY.STALLS_TOTAL] + [UOPS_EXECUTED.CYCLES_GE_1_UOPS_EXEC] - ( [UOPS_EXECUTED.CYCLES_GE_3_UOPS_EXEC] if ([instructions] / [cpu-cycles]) > 1.8 else [UOPS_EXECUTED.CYCLES_GE_2_UOPS_EXEC]) - ([RS_EVENTS.EMPTY_CYCLES] if ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [CPU_CLK_UNHALTED.THREAD_ANY]) > 0.1 else 0) + [RESOURCE_STALLS.SB])))" + }, + { + "name": "metric_TMAM_....Divider(%)", + "expression": "100 * [ARITH.FPU_DIV_ACTIVE] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_....Ports_Utilization(%)", + "expression": "100 * (( [CYCLE_ACTIVITY.STALLS_TOTAL] + [UOPS_EXECUTED.CYCLES_GE_1_UOPS_EXEC] - ([UOPS_EXECUTED.CYCLES_GE_3_UOPS_EXEC] if ([instructions] / [cpu-cycles]) > 1.8 else [UOPS_EXECUTED.CYCLES_GE_2_UOPS_EXEC]) - ([RS_EVENTS.EMPTY_CYCLES] if ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [CPU_CLK_UNHALTED.THREAD_ANY]) > 0.1 else 0) + [RESOURCE_STALLS.SB]) - [RESOURCE_STALLS.SB] - [CYCLE_ACTIVITY.STALLS_MEM_ANY] ) /[cpu-cycles]" + }, + { + "name": "metric_TMAM_......0_Port_Utilized(%)", + "expression": "100 * (([UOPS_EXECUTED.CORE_i1_c1] / [const_thread_count]) if ([const_thread_count] > 1) else ([RS_EVENTS.EMPTY_CYCLES] if ([CYCLE_ACTIVITY.STALLS_TOTAL] - ([IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) ) > 0.1 else 0)) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]) " + }, + { + "name": "metric_TMAM_......1_Port_Utilized(%)", + "expression": "100 * (([UOPS_EXECUTED.CORE_c1] - [UOPS_EXECUTED.CORE_c2]) / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_......2_Port_Utilized(%)", + "expression": "100 * (([UOPS_EXECUTED.CORE_c2] - [UOPS_EXECUTED.CORE_c3]) / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_......3m_Ports_Utilized(%)", + "expression": "100 * ([UOPS_EXECUTED.CORE_c3] / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_Retiring(%)", + "expression": "100 * [UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" + }, + { + "name": "metric_TMAM_..Base(%)", + "expression": "100 *(([UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) - (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))))" + }, + { + "name": "metric_TMAM_..Microcode_Sequencer(%)", + "expression": "100 * (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] )/ (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" + } +] \ No newline at end of file diff --git a/events/metric_icx.json b/events/metric_icx.json index 99e4c44..e3d36e0 100644 --- a/events/metric_icx.json +++ b/events/metric_icx.json @@ -1,344 +1,343 @@ [ { - "name" : "metric_CPU operating frequency (in GHz)", - "expression" : "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" + "name": "metric_CPU operating frequency (in GHz)", + "expression": "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" }, { - "name" : "metric_CPU utilization %", - "expression" : "100 * [ref-cycles] / [const_TSC]" + "name": "metric_CPU utilization %", + "expression": "100 * [ref-cycles] / [const_TSC]" }, { - "name" : "metric_CPU utilization% in kernel mode", - "expression" : "100 * [ref-cycles:k] / [const_TSC]" + "name": "metric_CPU utilization% in kernel mode", + "expression": "100 * [ref-cycles:k] / [const_TSC]" }, { - "name" : "metric_CPI", - "expression" : "[cpu-cycles] / [instructions]" + "name": "metric_CPI", + "expression": "[cpu-cycles] / [instructions]" }, { - "name" : "metric_kernel_CPI", - "expression" : "[cpu-cycles:k] / [instructions:k]" + "name": "metric_kernel_CPI", + "expression": "[cpu-cycles:k] / [instructions:k]" }, { - "name" : "metric_IPC", - "expression" : "[instructions] / [cpu-cycles]" + "name": "metric_IPC", + "expression": "[instructions] / [cpu-cycles]" }, { - "name" : "metric_giga_instructions_per_sec", - "expression" : "[instructions] / 1000000000" + "name": "metric_giga_instructions_per_sec", + "expression": "[instructions] / 1000000000" }, { - "name" : "metric_L1D MPI (includes data+rfo w/ prefetches)", - "tags" : "transaction", - "expression" : "[L1D.REPLACEMENT] / [instructions]" + "name": "metric_L1D MPI (includes data+rfo w/ prefetches)", + "tags": "transaction", + "expression": "[L1D.REPLACEMENT] / [instructions]" }, { - "name" : "metric_L1D demand data read hits per instr", - "expression" : "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" + "name": "metric_L1D demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" }, { - "name" : "metric_L1-I code read misses (w/ prefetches) per instr", - "expression" : "[L2_RQSTS.ALL_CODE_RD] / [instructions]" + "name": "metric_L1-I code read misses (w/ prefetches) per instr", + "expression": "[L2_RQSTS.ALL_CODE_RD] / [instructions]" }, { - "name" : "metric_L2 demand data read hits per instr", - "expression" : "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" + "name": "metric_L2 demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" }, { - "name" : "metric_L2 MPI (includes code+data+rfo w/ prefetches)", - "expression" : "[L2_LINES_IN.ALL] / [instructions]" - }, + "name": "metric_L2 MPI (includes code+data+rfo w/ prefetches)", + "expression": "[L2_LINES_IN.ALL] / [instructions]" + }, { - "name" : "metric_L2 demand data read MPI", - "expression" : "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" + "name": "metric_L2 demand data read MPI", + "expression": "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" }, { - "name" : "metric_L2 demand code MPI", - "expression" : "[L2_RQSTS.CODE_RD_MISS] / [instructions]" + "name": "metric_L2 demand code MPI", + "expression": "[L2_RQSTS.CODE_RD_MISS] / [instructions]" }, { - "name" : "metric_Average LLC data read miss latency (in clks)", - "expression" : "[OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD] / [OFFCORE_REQUESTS.L3_MISS_DEMAND_DATA_RD]" + "name": "metric_Average LLC data read miss latency (in clks)", + "expression": "[OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD] / [OFFCORE_REQUESTS.L3_MISS_DEMAND_DATA_RD]" }, { - "name" : "metric_UPI Data transmit BW (MB/sec) (only data)", - "expression" : "[UNC_UPI_TxL_FLITS.ALL_DATA] * (64 / 9) / 1000000" + "name": "metric_UPI Data transmit BW (MB/sec) (only data)", + "expression": "[UNC_UPI_TxL_FLITS.ALL_DATA] * (64 / 9) / 1000000" }, - { - "name" : "metric_package power (watts)", - "expression" : "[power/energy-pkg/]" + { + "name": "metric_package power (watts)", + "expression": "[power/energy-pkg/]" }, - { - "name" : "metric_DRAM power (watts)", - "expression" : "[power/energy-ram/]" + { + "name": "metric_DRAM power (watts)", + "expression": "[power/energy-ram/]" }, { - "name" : "metric_core c6 residency %", - "expression" : "100 * [cstate_core/c6-residency/] / [const_TSC]" + "name": "metric_core c6 residency %", + "expression": "100 * [cstate_core/c6-residency/] / [const_TSC]" }, { - "name" : "metric_package c6 residency %", - "expression" : "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" + "name": "metric_package c6 residency %", + "expression": "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" }, - { - "name" : "metric_core % cycles in non AVX license", - "expression" : "(100 * [CORE_POWER.LVL0_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + { + "name": "metric_core % cycles in non AVX license", + "expression": "(100 * [CORE_POWER.LVL0_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" }, - { - "name" : "metric_core % cycles in AVX2 license", - "expression" : "(100 * [CORE_POWER.LVL1_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + { + "name": "metric_core % cycles in AVX2 license", + "expression": "(100 * [CORE_POWER.LVL1_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" }, - { - "name" : "metric_core % cycles in AVX-512 license", - "expression" : "(100 * [CORE_POWER.LVL2_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + { + "name": "metric_core % cycles in AVX-512 license", + "expression": "(100 * [CORE_POWER.LVL2_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" }, { - "name" : "metric_core initiated local dram read bandwidth(MB/sec)", - "expression" : "([OCR.READS_TO_CORE.LOCAL_DRAM] + [OCR.HWPF_L3.L3_MISS_LOCAL]) * 64 / 1000000" + "name": "metric_core initiated local dram read bandwidth (MB/sec)", + "expression": "([OCR.READS_TO_CORE.LOCAL_DRAM] + [OCR.HWPF_L3.L3_MISS_LOCAL]) * 64 / 1000000" }, { - "name" : "metric_core initiated remote dram read bandwidth(MB/sec)", - "expression" : "([OCR.READS_TO_CORE.REMOTE_DRAM] + [OCR.HWPF_L3.REMOTE]) * 64 / 1000000" + "name": "metric_core initiated remote dram read bandwidth (MB/sec)", + "expression": "([OCR.READS_TO_CORE.REMOTE_DRAM] + [OCR.HWPF_L3.REMOTE]) * 64 / 1000000" }, - { - "name" : "metric_memory bandwidth read (MB/sec)", - "expression" : "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" + { + "name": "metric_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" }, - { - "name" : "metric_memory bandwidth write (MB/sec)", - "expression" : "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" + { + "name": "metric_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" }, - { - "name" : "metric_memory bandwidth total (MB/sec)", - "expression" : "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" + { + "name": "metric_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" }, { - "name" : "metric_DCPMEM_memory_mode near memory cache read miss rate%", - "expression" : "100 * ([UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY]) / ([UNC_M_TAGCHK.HIT] + [UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY])" + "name": "metric_DCPMEM_memory_mode near memory cache read miss rate%", + "expression": "100 * ([UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY]) / ([UNC_M_TAGCHK.HIT] + [UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY])" }, { - "name" : "metric_3DXP_memory bandwidth read (MB/sec)", - "expression" : "[UNC_M_PMM_RPQ_INSERTS] * 64 / 1000000" + "name": "metric_3DXP_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_PMM_RPQ_INSERTS] * 64 / 1000000" }, { - "name" : "metric_3DXP_memory bandwidth write (MB/sec)", - "expression" : "[UNC_M_PMM_WPQ_INSERTS] * 64 / 1000000" + "name": "metric_3DXP_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_PMM_WPQ_INSERTS] * 64 / 1000000" }, { - "name" : "metric_3DXP_memory bandwidth total (MB/sec)", - "expression" : "([UNC_M_PMM_RPQ_INSERTS] + [UNC_M_PMM_WPQ_INSERTS]) * 64 / 1000000" + "name": "metric_3DXP_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_PMM_RPQ_INSERTS] + [UNC_M_PMM_WPQ_INSERTS]) * 64 / 1000000" }, { - "name" : "metric_LLC code read MPI (demand+prefetch)", - "expression" : "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFCODE] + [UNC_CHA_TOR_INSERTS.IA_MISS_CRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF]) / [instructions]" + "name": "metric_LLC code read MPI (demand+prefetch)", + "expression": "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFCODE] + [UNC_CHA_TOR_INSERTS.IA_MISS_CRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF]) / [instructions]" }, { - "name" : "metric_LLC data read MPI (demand+prefetch)", - "expression" : "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF]) / [instructions]" + "name": "metric_LLC data read MPI (demand+prefetch)", + "expression": "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF]) / [instructions]" }, { - "name" : "metric_LLC total HITM (per instr) (excludes LLC prefetches)", - "expression" : "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HITM] / [instructions]" + "name": "metric_LLC total HITM (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HITM] / [instructions]" }, { - "name" : "metric_LLC total HIT clean line forwards (per instr) (excludes LLC prefetches)", - "expression" : "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HIT_WITH_FWD] / [instructions]" + "name": "metric_LLC total HIT clean line forwards (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HIT_WITH_FWD] / [instructions]" }, { - "name" : "metric_Average LLC demand data read miss latency (in ns)", - "expression" : "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + "name": "metric_Average LLC demand data read miss latency (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" }, { - "name" : "metric_Average LLC demand data read miss latency for LOCAL requests (in ns)", - "expression" : "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [socket_count]))" + "name": "metric_Average LLC demand data read miss latency for LOCAL requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" }, { - "name" : "metric_Average LLC demand data read miss latency for REMOTE requests (in ns)", - "expression" : "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [socket_count]))" + "name": "metric_Average LLC demand data read miss latency for REMOTE requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" }, { - "name" : "metric_ITLB (2nd level) MPI", - "expression" : "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" + "name": "metric_ITLB (2nd level) MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" }, { - "name" : "metric_DTLB (2nd level) load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" + "name": "metric_DTLB (2nd level) load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" }, { - "name" : "metric_DTLB (2nd level) 2MB large page load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + "name": "metric_DTLB (2nd level) 2MB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" }, { - "name" : "metric_DTLB (2nd level) store MPI", - "expression" : "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" + "name": "metric_DTLB (2nd level) store MPI", + "expression": "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" }, { - "name" : "metric_NUMA %_Reads addressed to local DRAM", - "expression" : "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" + "name": "metric_NUMA %_Reads addressed to local DRAM", + "expression": "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" }, { - "name" : "metric_NUMA %_Reads addressed to remote DRAM", - "expression" : "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" + "name": "metric_NUMA %_Reads addressed to remote DRAM", + "expression": "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" }, { - "name" : "metric_uncore frequency GHz", - "expression" : "[UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) / 1000000000" + "name": "metric_uncore frequency GHz", + "expression": "[UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) / 1000000000" }, { - "name" : "metric_TMA_Frontend_Bound(%)", - "expression" : "100 * ([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([slots]))" + "name": "metric_TMA_Frontend_Bound(%)", + "expression": "100 * ([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([slots]))" }, { - "name" : "metric_TMA_..Fetch_Latency(%)", - "expression" : "100 * (5 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [slots])" + "name": "metric_TMA_..Fetch_Latency(%)", + "expression": "100 * (5 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [slots])" }, { - "name" : "metric_TMA_....ICache_Misses(%)", - "expression" : "100 * ([ICACHE_16B.IFDATA_STALL] / [cpu-cycles])" + "name": "metric_TMA_....ICache_Misses(%)", + "expression": "100 * ([ICACHE_16B.IFDATA_STALL] / [cpu-cycles])" }, { - "name" : "metric_TMA_....ITLB_Misses(%)", - "expression" : "100 * ([ICACHE_64B.IFTAG_STALL] / [cpu-cycles])" + "name": "metric_TMA_....ITLB_Misses(%)", + "expression": "100 * ([ICACHE_64B.IFTAG_STALL] / [cpu-cycles])" }, { - "name" : "metric_TMA_....Branch_Resteers(%)", - "expression" : "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles] + (10 * [BACLEARS.ANY] / [cpu-cycles]))" + "name": "metric_TMA_....Branch_Resteers(%)", + "expression": "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles] + (10 * [BACLEARS.ANY] / [cpu-cycles]))" }, { - "name" : "metric_TMA_......Mispredicts_Resteers(%)", - "expression" : "100 * (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + "name": "metric_TMA_......Mispredicts_Resteers(%)", + "expression": "100 * (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" }, { - "name" : "metric_TMA_......Clears_Resteers(%)", - "expression" : "100 * ((1 - ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT]))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + "name": "metric_TMA_......Clears_Resteers(%)", + "expression": "100 * ((1 - ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT]))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" }, { - "name" : "metric_TMA_......Unknown_Branches(%)", - "expression" : "100 * (10 * [BACLEARS.ANY] / [cpu-cycles])" + "name": "metric_TMA_......Unknown_Branches(%)", + "expression": "100 * (10 * [BACLEARS.ANY] / [cpu-cycles])" }, { - "name" : "metric_TMA_..Fetch_Bandwidth(%)", - "expression" : "100 * max(0, (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) - (5 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [slots])))" + "name": "metric_TMA_..Fetch_Bandwidth(%)", + "expression": "100 * max(0, (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) - (5 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [slots])))" }, { - "name" : "metric_TMA_Bad_Speculation(%)", - "expression" : "100 * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0))" + "name": "metric_TMA_Bad_Speculation(%)", + "expression": "100 * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0))" }, { - "name" : "metric_TMA_..Branch_Mispredicts(%)", - "expression" : "100 * (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0)))" + "name": "metric_TMA_..Branch_Mispredicts(%)", + "expression": "100 * (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0)))" }, { - "name" : "metric_TMA_..Machine_Clears(%)", - "expression" : "100 * (max(0, ((max((1 - (([topdown-fe-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0)) - (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))), 0))))))" + "name": "metric_TMA_..Machine_Clears(%)", + "expression": "100 * (max(0, ((max((1 - (([topdown-fe-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0)) - (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) + ([topdown-retiring] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))), 0))))))" }, { - "name" : "metric_TMA_Backend_Bound(%)", - "expression" : "100 * ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) + ( 5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots])" + "name": "metric_TMA_Backend_Bound(%)", + "expression": "100 * ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) + ( 5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots])" }, { - "name" : "metric_TMA_..Memory_Bound(%)", - "expression" : "100 * ((([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([CYCLE_ACTIVITY.STALLS_Total] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) + [EXE_ACTIVITY.BOUND_ON_STORES])) * ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]))" + "name": "metric_TMA_..Memory_Bound(%)", + "expression": "100 * ((([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([CYCLE_ACTIVITY.STALLS_Total] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) + [EXE_ACTIVITY.BOUND_ON_STORES])) * ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]))" }, { - "name" : "metric_TMA_....L1_Bound(%)", - "expression" : "100 * max((([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]), 0)" + "name": "metric_TMA_....L1_Bound(%)", + "expression": "100 * max((([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]), 0)" }, { - "name" : "metric_TMA_......DTLB_Load(%)", - "expression" : "100 * ((min((7 * [DTLB_LOAD_MISSES.STLB_HIT] + [DTLB_LOAD_MISSES.WALK_ACTIVE]), (max(([CYCLE_ACTIVITY.CYCLES_MEM_ANY] - [CYCLE_ACTIVITY.CYCLES_L1D_MISS]), 0))) / [cpu-cycles]) - ([DTLB_LOAD_MISSES.WALK_ACTIVE] / [cpu-cycles]))" + "name": "metric_TMA_......DTLB_Load(%)", + "expression": "100 * ((min((7 * [DTLB_LOAD_MISSES.STLB_HIT] + [DTLB_LOAD_MISSES.WALK_ACTIVE]), (max(([CYCLE_ACTIVITY.CYCLES_MEM_ANY] - [CYCLE_ACTIVITY.CYCLES_L1D_MISS]), 0))) / [cpu-cycles]) - ([DTLB_LOAD_MISSES.WALK_ACTIVE] / [cpu-cycles]))" }, { - "name" : "metric_TMA_......Store_Fwd_Blk(%)", - "expression" : "100 * (13 * [LD_BLOCKS.STORE_FORWARD] / [cpu-cycles])" + "name": "metric_TMA_......Store_Fwd_Blk(%)", + "expression": "100 * (13 * [LD_BLOCKS.STORE_FORWARD] / [cpu-cycles])" }, { - "name" : "metric_TMA_....L2_Bound(%)", - "expression" : "100 * ((([MEM_LOAD_RETIRED.L2_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]))) / (([MEM_LOAD_RETIRED.L2_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]))) + [L1D_PEND_MISS.FB_FULL_PERIODS])) * (([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]))" + "name": "metric_TMA_....L2_Bound(%)", + "expression": "100 * ((([MEM_LOAD_RETIRED.L2_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]))) / (([MEM_LOAD_RETIRED.L2_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]))) + [L1D_PEND_MISS.FB_FULL_PERIODS])) * (([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]))" }, { - "name" : "metric_TMA_....L3_Bound(%)", - "expression" : "100 * (([CYCLE_ACTIVITY.STALLS_L2_MISS] - [CYCLE_ACTIVITY.STALLS_L3_Miss]) / [cpu-cycles])" + "name": "metric_TMA_....L3_Bound(%)", + "expression": "100 * (([CYCLE_ACTIVITY.STALLS_L2_MISS] - [CYCLE_ACTIVITY.STALLS_L3_Miss]) / [cpu-cycles])" }, { - "name" : "metric_TMA_......Contested_Accesses(%)", - "expression" : "100 * ((((48 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) -(4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * ([MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD] * ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] / ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] + [OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD]))) + ((47.5 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * [MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS]) * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" + "name": "metric_TMA_......Contested_Accesses(%)", + "expression": "100 * ((((48 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) -(4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * ([MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD] * ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] / ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] + [OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD]))) + ((47.5 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * [MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS]) * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" }, { - "name" : "metric_TMA_......Data_Sharing(%)", - "expression" : "100 * (((47.5 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * ([MEM_LOAD_L3_HIT_RETIRED.XSNP_NO_FWD] + [MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD] * (1 - ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] / ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] + [OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD])))) * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" + "name": "metric_TMA_......Data_Sharing(%)", + "expression": "100 * (((47.5 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * ([MEM_LOAD_L3_HIT_RETIRED.XSNP_NO_FWD] + [MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD] * (1 - ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] / ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] + [OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD])))) * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" }, { - "name" : "metric_TMA_......L3_Hit_Latency(%)", - "expression" : "100 * (((23 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * [MEM_LOAD_RETIRED.L3_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" + "name": "metric_TMA_......L3_Hit_Latency(%)", + "expression": "100 * (((23 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * [MEM_LOAD_RETIRED.L3_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" }, { - "name" : "metric_TMA_......SQ_Full(%)", - "expression" : "100 * ([L1D_PEND_MISS.L2_STALL] / [cpu-cycles])" + "name": "metric_TMA_......SQ_Full(%)", + "expression": "100 * ([L1D_PEND_MISS.L2_STALL] / [cpu-cycles])" }, { - "name" : "metric_TMA_......MEM_Bandwidth(%)", - "expression" : "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4] - 0))) / [cpu-cycles])" + "name": "metric_TMA_......MEM_Bandwidth(%)", + "expression": "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4] - 0))) / [cpu-cycles])" }, { - "name" : "metric_TMA_......Mem_Latency(%)", - "expression" : "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD] - 0))) / [cpu-cycles] - ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4] - 0))) / [cpu-cycles]))" + "name": "metric_TMA_......MEM_Latency(%)", + "expression": "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD] - 0))) / [cpu-cycles] - ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4] - 0))) / [cpu-cycles]))" }, { - "name" : "metric_TMA_....Store_Bound(%)", - "expression" : "100 * ([EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles])" + "name": "metric_TMA_....Store_Bound(%)", + "expression": "100 * ([EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles])" }, { - "name" : "metric_TMA_..Core_Bound(%)", - "expression" : "100 * (max(0, (([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) - ((([CYCLE_ACTIVITY.CYCLES_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([CYCLE_ACTIVITY.STALLS_Total] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) + [EXE_ACTIVITY.BOUND_ON_STORES])) * ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots])))))" + "name": "metric_TMA_..Core_Bound(%)", + "expression": "100 * (max(0, (([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots]) - ((([CYCLE_ACTIVITY.CYCLES_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([CYCLE_ACTIVITY.STALLS_Total] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) + [EXE_ACTIVITY.BOUND_ON_STORES])) * ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [slots])))))" }, { - "name" : "metric_TMA_....Divider(%)", - "expression" : "100 * ([ARITH.DIVIDER_ACTIVE] / [cpu-cycles])" + "name": "metric_TMA_....Divider(%)", + "expression": "100 * ([ARITH.DIVIDER_ACTIVE] / [cpu-cycles])" }, { - "name" : "metric_TMA_....Ports_Utilization(%)", - "expression" : "100 * ((([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL])) / [cpu-cycles]) if ([ARITH.DIVIDER_ACTIVE] - 0) < ([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY]) else (([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) / [cpu-cycles]))" + "name": "metric_TMA_....Ports_Utilization(%)", + "expression": "100 * ((([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL])) / [cpu-cycles]) if ([ARITH.DIVIDER_ACTIVE] - 0) < ([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY]) else (([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) / [cpu-cycles]))" }, { - "name" : "metric_TMA_......Ports_Utilized_0(%)", - "expression" : "100 * max(0, (([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY]) / [cpu-cycles]))" + "name": "metric_TMA_......Ports_Utilized_0(%)", + "expression": "100 * max(0, (([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY]) / [cpu-cycles]))" }, { - "name" : "metric_TMA_......Ports_Utilized_1(%)", - "expression" : "100 * ([EXE_ACTIVITY.1_PORTS_UTIL] / [cpu-cycles])" + "name": "metric_TMA_......Ports_Utilized_1(%)", + "expression": "100 * ([EXE_ACTIVITY.1_PORTS_UTIL] / [cpu-cycles])" }, { - "name" : "metric_TMA_......Ports_Utilized_2(%)", - "expression" : "100 * ([EXE_ACTIVITY.2_PORTS_UTIL] / [cpu-cycles])" + "name": "metric_TMA_......Ports_Utilized_2(%)", + "expression": "100 * ([EXE_ACTIVITY.2_PORTS_UTIL] / [cpu-cycles])" }, { - "name" : "metric_TMA_......Ports_Utilized_3m(%)", - "expression" : "100 * [UOPS_EXECUTED.CYCLES_GE_3] / [cpu-cycles]" + "name": "metric_TMA_......Ports_Utilized_3m(%)", + "expression": "100 * [UOPS_EXECUTED.CYCLES_GE_3] / [cpu-cycles]" }, { - "name" : "metric_TMA_Retiring(%)", - "expression" : "100 * ([topdown-retiring] / ([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))" + "name": "metric_TMA_Retiring(%)", + "expression": "100 * ([topdown-retiring] / ([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))" }, { - "name" : "metric_TMA_..Light_Operations(%)", - "expression" : "100 * (max(0, (([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) - ((((([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) * [slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [slots])))))" + "name": "metric_TMA_..Light_Operations(%)", + "expression": "100 * (max(0, (([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) - ((((([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) * [slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [slots])))))" }, { - "name" : "metric_TMA_..Heavy_Operations(%)", - "expression" : "100 * ((((([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) * [slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [slots]))" + "name": "metric_TMA_..Heavy_Operations(%)", + "expression": "100 * ((((([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) * [slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [slots]))" }, { - "name" : "metric_TMA_....Microcode_Sequencer(%)", - "expression" : "100 * (((([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-found]))) * [slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [slots])" + "name": "metric_TMA_....Microcode_Sequencer(%)", + "expression": "100 * (((([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-found]))) * [slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [slots])" }, { - "name" : "metric_TMA_Info_CoreIPC", - "expression" : "[instructions] / [CPU_CLK_UNHALTED.DISTRIBUTED]" + "name": "metric_TMA_Info_CoreIPC", + "expression": "[instructions] / [CPU_CLK_UNHALTED.DISTRIBUTED]" }, { - "name" : "metric_TMA_Info_System_SMT_2T_Utilization", - "expression" : "(1 - [CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE] / [CPU_CLK_UNHALTED.REF_DISTRIBUTED]) if [const_socket_count] > 1 else 0" + "name": "metric_TMA_Info_System_SMT_2T_Utilization", + "expression": "(1 - [CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE] / [CPU_CLK_UNHALTED.REF_DISTRIBUTED]) if [const_socket_count] > 1 else 0" } - -] +] \ No newline at end of file diff --git a/events/metric_icx_aws.json b/events/metric_icx_aws.json new file mode 100644 index 0000000..0538672 --- /dev/null +++ b/events/metric_icx_aws.json @@ -0,0 +1,311 @@ +[ + { + "name": "metric_CPU operating frequency (in GHz)", + "expression": "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" + }, + { + "name": "metric_CPU utilization %", + "expression": "100 * [ref-cycles] / [const_TSC]" + }, + { + "name": "metric_CPU utilization% in kernel mode", + "expression": "100 * [ref-cycles:k] / [const_TSC]" + }, + { + "name": "metric_CPI", + "expression": "[cpu-cycles] / [instructions]" + }, + { + "name": "metric_kernel_CPI", + "expression": "[cpu-cycles:k] / [instructions:k]" + }, + { + "name": "metric_IPC", + "expression": "[instructions] / [cpu-cycles]" + }, + { + "name": "metric_giga_instructions_per_sec", + "expression": "[instructions] / 1000000000" + }, + { + "name": "metric_L1D MPI (includes data+rfo w/ prefetches)", + "tags": "transaction", + "expression": "[L1D.REPLACEMENT] / [instructions]" + }, + { + "name": "metric_L1D demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" + }, + { + "name": "metric_L1-I code read misses (w/ prefetches) per instr", + "expression": "[L2_RQSTS.ALL_CODE_RD] / [instructions]" + }, + { + "name": "metric_L2 demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" + }, + { + "name": "metric_L2 MPI (includes code+data+rfo w/ prefetches)", + "expression": "[L2_LINES_IN.ALL] / [instructions]" + }, + { + "name": "metric_L2 demand data read MPI", + "expression": "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" + }, + { + "name": "metric_L2 demand code MPI", + "expression": "[L2_RQSTS.CODE_RD_MISS] / [instructions]" + }, + { + "name": "metric_UPI Data transmit BW (MB/sec) (only data)", + "expression": "[UNC_UPI_TxL_FLITS.ALL_DATA] * (64 / 9) / 1000000" + }, + { + "name": "metric_package power (watts)", + "expression": "[power/energy-pkg/]" + }, + { + "name": "metric_DRAM power (watts)", + "expression": "[power/energy-ram/]" + }, + { + "name": "metric_core % cycles in non AVX license", + "expression": "(100 * [CORE_POWER.LVL0_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + }, + { + "name": "metric_core % cycles in AVX2 license", + "expression": "(100 * [CORE_POWER.LVL1_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + }, + { + "name": "metric_core % cycles in AVX-512 license", + "expression": "(100 * [CORE_POWER.LVL2_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + }, + { + "name": "metric_DCPMEM_memory_mode near memory cache read miss rate%", + "expression": "100 * ([UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY]) / ([UNC_M_TAGCHK.HIT] + [UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY])" + }, + { + "name": "metric_3DXP_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_PMM_RPQ_INSERTS] * 64 / 1000000" + }, + { + "name": "metric_3DXP_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_PMM_WPQ_INSERTS] * 64 / 1000000" + }, + { + "name": "metric_3DXP_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_PMM_RPQ_INSERTS] + [UNC_M_PMM_WPQ_INSERTS]) * 64 / 1000000" + }, + { + "name": "metric_LLC code read MPI (demand+prefetch)", + "expression": "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFCODE] + [UNC_CHA_TOR_INSERTS.IA_MISS_CRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_CRD_PREF]) / [instructions]" + }, + { + "name": "metric_LLC data read MPI (demand+prefetch)", + "expression": "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF]) / [instructions]" + }, + { + "name": "metric_LLC total HITM (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HITM] / [instructions]" + }, + { + "name": "metric_LLC total HIT clean line forwards (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HIT_WITH_FWD] / [instructions]" + }, + { + "name": "metric_Average LLC demand data read miss latency (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_Average LLC demand data read miss latency for LOCAL requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_Average LLC demand data read miss latency for REMOTE requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_ITLB (2nd level) MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) 2MB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) store MPI", + "expression": "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_NUMA %_Reads addressed to local DRAM", + "expression": "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" + }, + { + "name": "metric_NUMA %_Reads addressed to remote DRAM", + "expression": "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" + }, + { + "name": "metric_uncore frequency GHz", + "expression": "[UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) / 1000000000" + }, + { + "name": "metric_TMA_Frontend_Bound(%)", + "expression": "100 * ([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([topdown.slots]))" + }, + { + "name": "metric_TMA_..Fetch_Latency(%)", + "expression": "100 * (5 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [topdown.slots])" + }, + { + "name": "metric_TMA_....ICache_Misses(%)", + "expression": "100 * ([ICACHE_16B.IFDATA_STALL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....ITLB_Misses(%)", + "expression": "100 * ([ICACHE_64B.IFTAG_STALL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....Branch_Resteers(%)", + "expression": "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles] + (10 * [BACLEARS.ANY] / [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Mispredicts_Resteers(%)", + "expression": "100 * (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Clears_Resteers(%)", + "expression": "100 * ((1 - ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT]))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Unknown_Branches(%)", + "expression": "100 * (10 * [BACLEARS.ANY] / [cpu-cycles])" + }, + { + "name": "metric_TMA_..Fetch_Bandwidth(%)", + "expression": "100 * max(0, (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) - (5 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / [topdown.slots])))" + }, + { + "name": "metric_TMA_Bad_Speculation(%)", + "expression": "100 * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0))" + }, + { + "name": "metric_TMA_..Branch_Mispredicts(%)", + "expression": "100 * (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0)))" + }, + { + "name": "metric_TMA_..Machine_Clears(%)", + "expression": "100 * (max(0, ((max((1 - (([topdown-fe-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots]) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0)) - (([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots]) + ([topdown-retiring] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))), 0))))))" + }, + { + "name": "metric_TMA_Backend_Bound(%)", + "expression": "100 * ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) + ( 5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots])" + }, + { + "name": "metric_TMA_..Memory_Bound(%)", + "expression": "100 * ((([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([CYCLE_ACTIVITY.STALLS_Total] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) + [EXE_ACTIVITY.BOUND_ON_STORES])) * ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots]))" + }, + { + "name": "metric_TMA_....L1_Bound(%)", + "expression": "100 * max((([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]), 0)" + }, + { + "name": "metric_TMA_......DTLB_Load(%)", + "expression": "100 * ((min((7 * [DTLB_LOAD_MISSES.STLB_HIT] + [DTLB_LOAD_MISSES.WALK_ACTIVE]), (max(([CYCLE_ACTIVITY.CYCLES_MEM_ANY] - [CYCLE_ACTIVITY.CYCLES_L1D_MISS]), 0))) / [cpu-cycles]) - ([DTLB_LOAD_MISSES.WALK_ACTIVE] / [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Store_Fwd_Blk(%)", + "expression": "100 * (13 * [LD_BLOCKS.STORE_FORWARD] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....L2_Bound(%)", + "expression": "100 * ((([MEM_LOAD_RETIRED.L2_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]))) / (([MEM_LOAD_RETIRED.L2_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]))) + [L1D_PEND_MISS.FB_FULL_PERIODS])) * (([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]))" + }, + { + "name": "metric_TMA_....L3_Bound(%)", + "expression": "100 * (([CYCLE_ACTIVITY.STALLS_L2_MISS] - [CYCLE_ACTIVITY.STALLS_L3_Miss]) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Contested_Accesses(%)", + "expression": "100 * ((((48 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) -(4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * ([MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD] * ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] / ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] + [OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD]))) + ((47.5 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * [MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS]) * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Data_Sharing(%)", + "expression": "100 * (((47.5 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * ([MEM_LOAD_L3_HIT_RETIRED.XSNP_NO_FWD] + [MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD] * (1 - ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] / ([OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM] + [OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD])))) * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......L3_Hit_Latency(%)", + "expression": "100 * (((23 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000)) - (4 * (([cpu-cycles] / [ref-cycles]) * [const_tsc_freq] / 1000000000))) * [MEM_LOAD_RETIRED.L3_HIT] * (1 + ([MEM_LOAD_RETIRED.FB_HIT] / [MEM_LOAD_RETIRED.L1_MISS]) / 2) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......SQ_Full(%)", + "expression": "100 * ([L1D_PEND_MISS.L2_STALL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......MEM_Bandwidth(%)", + "expression": "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4] - 0))) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......MEM_Latency(%)", + "expression": "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD] - 0))) / [cpu-cycles] - ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD_c4] - 0))) / [cpu-cycles]))" + }, + { + "name": "metric_TMA_....Store_Bound(%)", + "expression": "100 * ([EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_..Core_Bound(%)", + "expression": "100 * (max(0, (([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots]) - ((([CYCLE_ACTIVITY.CYCLES_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([CYCLE_ACTIVITY.STALLS_Total] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) + [EXE_ACTIVITY.BOUND_ON_STORES])) * ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) + (5 * [INT_MISC.RECOVERY_CYCLES_c1_e1]) / [topdown.slots])))))" + }, + { + "name": "metric_TMA_....Divider(%)", + "expression": "100 * ([ARITH.DIVIDER_ACTIVE] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....Ports_Utilization(%)", + "expression": "100 * ((([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY] + ([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL])) / [cpu-cycles]) if ([ARITH.DIVIDER_ACTIVE] - 0) < ([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY]) else (([EXE_ACTIVITY.1_PORTS_UTIL] + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) * [EXE_ACTIVITY.2_PORTS_UTIL]) / [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Ports_Utilized_0(%)", + "expression": "100 * max(0, (([CYCLE_ACTIVITY.STALLS_Total] - [CYCLE_ACTIVITY.CYCLES_MEM_ANY]) / [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Ports_Utilized_1(%)", + "expression": "100 * ([EXE_ACTIVITY.1_PORTS_UTIL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Ports_Utilized_2(%)", + "expression": "100 * ([EXE_ACTIVITY.2_PORTS_UTIL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Ports_Utilized_3m(%)", + "expression": "100 * [UOPS_EXECUTED.CYCLES_GE_3] / [cpu-cycles]" + }, + { + "name": "metric_TMA_Retiring(%)", + "expression": "100 * ([topdown-retiring] / ([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))" + }, + { + "name": "metric_TMA_..Light_Operations(%)", + "expression": "100 * (max(0, (([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) - ((((([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) * [topdown.slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [topdown.slots])))))" + }, + { + "name": "metric_TMA_..Heavy_Operations(%)", + "expression": "100 * ((((([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) * [topdown.slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [topdown.slots]))" + }, + { + "name": "metric_TMA_....Microcode_Sequencer(%)", + "expression": "100 * (((([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-found]))) * [topdown.slots]) / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / [topdown.slots])" + }, + { + "name": "metric_TMA_Info_CoreIPC", + "expression": "[instructions] / [CPU_CLK_UNHALTED.DISTRIBUTED]" + }, + { + "name": "metric_TMA_Info_System_SMT_2T_Utilization", + "expression": "(1 - [CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE] / [CPU_CLK_UNHALTED.REF_DISTRIBUTED]) if [const_socket_count] > 1 else 0" + } +] \ No newline at end of file diff --git a/events/metric_skx_clx.json b/events/metric_skx_clx.json index 2847130..85189bd 100644 --- a/events/metric_skx_clx.json +++ b/events/metric_skx_clx.json @@ -1,383 +1,383 @@ [ - { - "name" : "metric_CPU operating frequency (in GHz)", - "expression" : "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" - }, - { - "name" : "metric_CPU utilization %", - "expression" : "100 * [ref-cycles] / [const_TSC]" - }, - { - "name" : "metric_CPU utilization% in kernel mode", - "expression" : "100 * [ref-cycles:k] / [const_TSC]" - }, - { - "name" : "metric_CPI", - "expression" : "[cpu-cycles] / [instructions]" - }, - { - "name" : "metric_kernel_CPI", - "expression" : "[cpu-cycles:k] / [instructions:k]" - }, - { - "name" : "metric_L1D MPI (includes data+rfo w/ prefetches)", - "tags" : "transaction", - "expression" : "[L1D.REPLACEMENT] / [instructions]" - }, - { - "name" : "metric_L1D demand data read hits per instr", - "expression" : "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" - }, - { - "name" : "metric_L1-I code read misses (w/ prefetches) per instr", - "expression" : "[L2_RQSTS.ALL_CODE_RD] / [instructions]" - }, - { - "name" : "metric_L2 demand data read hits per instr", - "expression" : "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" - }, - { - "name" : "metric_L2 MPI (includes code+data+rfo w/ prefetches)", - "expression" : "[L2_LINES_IN.ALL] / [instructions]" - }, - { - "name" : "metric_L2 demand data read MPI", - "expression" : "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" - }, - { - "name" : "metric_L2 demand code MPI", - "expression" : "[L2_RQSTS.CODE_RD_MISS] / [instructions]" - }, - { - "name" : "metric_LLC MPI (includes code+data+rfo w/ prefetches)", - "expression" : "([UNC_CHA_TOR_INSERTS.IA_MISS.0x12CC0233] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x12D40433] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x12C40033]) / [instructions]" - }, - { - "name" : "metric_LLC code read MPI (demand+prefetch)", - "expression" : "[UNC_CHA_TOR_INSERTS.IA_MISS.0x12CC0233] / [instructions]" - }, - { - "name" : "metric_LLC data read MPI (demand+prefetch)", - "expression" : "[UNC_CHA_TOR_INSERTS.IA_MISS.0x12D40433] / [instructions]" - }, - { - "name" : "metric_LLC total HITM (per instr)", - "expression" : "[OCR.ALL_READS.L3_MISS.REMOTE_HITM] / [instructions]" - }, - { - "name" : "metric_LLC total HIT clean line forwards (per instr)", - "expression" : "[OCR.ALL_READS.L3_MISS.REMOTE_HIT_FORWARD] / [instructions]" - }, - { - "name" : "metric_Average LLC data read miss latency (in clks)", - "expression" : "[OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD] / [OFFCORE_REQUESTS.L3_MISS_DEMAND_DATA_RD]" - }, - { - "name" : "metric_Average LLC data read miss latency (in ns)", - "expression" : "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS.0x40433] / [UNC_CHA_TOR_INSERTS.IA_MISS.0x40433]) / ( [UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" - }, - { - "name" : "metric_Average LLC data read miss latency for LOCAL requests (in ns)", - "expression" : "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS.0x40432] / [UNC_CHA_TOR_INSERTS.IA_MISS.0x40432]) / ( [UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" - }, - { - "name" : "metric_Average LLC data read miss latency for REMOTE requests (in ns)", - "expression" : "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS.0x40431] / [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431]) / ( [UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" - }, - { - "name" : "metric_ITLB MPI", - "expression" : "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" - }, - { - "name" : "metric_ITLB large page MPI", - "expression" : "[ITLB_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" - }, - { - "name" : "metric_DTLB load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" - }, - { - "name" : "metric_DTLB 4KB page load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED_4K] / [instructions]" - }, - { - "name" : "metric_DTLB 2MB large page load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" - }, - { - "name" : "metric_DTLB 1GB large page load MPI", - "expression" : "[DTLB_LOAD_MISSES.WALK_COMPLETED_1G] / [instructions]" - }, - { - "name" : "metric_DTLB store MPI", - "expression" : "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" - }, - { - "name" : "metric_DTLB load miss latency (in core clks)", - "expression" : "[DTLB_LOAD_MISSES.WALK_ACTIVE] / [DTLB_LOAD_MISSES.WALK_COMPLETED]" - }, - { - "name" : "metric_DTLB store miss latency (in core clks)", - "expression" : "[DTLB_STORE_MISSES.WALK_ACTIVE] / [DTLB_STORE_MISSES.WALK_COMPLETED]" - }, - { - "name" : "metric_ITLB miss latency (in core clks)", - "expression" : "[ITLB_MISSES.WALK_ACTIVE] / [ITLB_MISSES.WALK_COMPLETED]" - }, - { - "name" : "metric_NUMA %_Reads addressed to local DRAM", - "expression" : "100 * [UNC_CHA_TOR_INSERTS.IA_MISS.0x40432] / ([UNC_CHA_TOR_INSERTS.IA_MISS.0x40432] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431])" - }, - { - "name" : "metric_NUMA %_Reads addressed to remote DRAM", - "expression" : "100 * [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431] / ([UNC_CHA_TOR_INSERTS.IA_MISS.0x40432] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431])" - }, - { - "name" : "metric_UPI Data transmit BW (MB/sec) (only data)", - "expression" : "[UNC_UPI_TxL_FLITS.ALL_DATA] * (64 / 9) / 1000000" - }, - { - "name" : "metric_UPI Transmit utilization_% (includes control)", - "expression" : "100 * (([UNC_UPI_TxL_FLITS.ALL_DATA] + [UNC_UPI_TxL_FLITS.NON_DATA]) / 3) / ((((([const_tsc_freq] / ([const_cha_count] * [const_thread_count])) / (([const_tsc_freq] / ([const_cha_count] * [const_thread_count])) - [FREERUN_CORE_C6_RESIDENCY])) * ([UNC_UPI_CLOCKTICKS] - [UNC_UPI_L1_POWER_CYCLES])) * 5 / 6))" - }, - { - "name" : "metric_uncore frequency GHz", - "expression" : "[UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) / 1000000000" - }, - { - "name" : "metric_package power (watts)", - "expression" : "[power/energy-pkg/]" - }, - { - "name" : "metric_DRAM power (watts)", - "expression" : "[power/energy-ram/]" - }, - { - "name" : "metric_core c6 residency %", - "expression" : "100 * [cstate_core/c6-residency/] / [const_TSC]" - }, - { - "name" : "metric_package c6 residency %", - "expression" : "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" - }, - { - "name" : "metric_core % cycles in non AVX license", - "expression" : "(100 * [CORE_POWER.LVL0_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" - }, - { - "name" : "metric_core % cycles in AVX2 license", - "expression" : "(100 * [CORE_POWER.LVL1_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" - }, - { - "name" : "metric_core % cycles in AVX-512 license", - "expression" : "(100 * [CORE_POWER.LVL2_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" - }, - { - "name" : "metric_core initiated local dram read bandwidth(MB/sec)", - "expression" : "[OCR.ALL_READS.L3_MISS_LOCAL_DRAM.ANY_SNOOP] * 64 / 1000000" - }, - { - "name" : "metric_core initiated remote dram read bandwidth(MB/sec)", - "expression" : "[OCR.ALL_READS.L3_MISS_LOCAL_DRAM.ANY_SNOOP_ocr_msr_3fB80007f7] * 64 / 1000000" - }, - { - "name" : "metric_memory bandwidth read (MB/sec)", - "expression" : "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" - }, - { - "name" : "metric_memory bandwidth write (MB/sec)", - "expression" : "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" - }, - { - "name" : "metric_memory bandwidth total (MB/sec)", - "expression" : "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" - }, - { - "name" : "metric_DCPMEM_memory_mode near memory cache read miss rate%", - "expression" : "100 * ([UNC_M_PMM_RPQ_INSERTS] / ([UNC_M2M_TAG_HIT.NM_RD_HIT_CLEAN] + [UNC_M2M_TAG_HIT.NM_RD_HIT_DIRTY] + [UNC_M_PMM_RPQ_INSERTS]))" - }, - { - "name" : "metric_3DXP_memory bandwidth read (MB/sec)", - "expression" : "[UNC_M_PMM_RPQ_INSERTS] * 64 / 1000000" - }, - { - "name" : "metric_3DXP_memory bandwidth write (MB/sec)", - "expression" : "[UNC_M_PMM_WPQ_INSERTS] * 64 / 1000000" - }, - { - "name" : "metric_3DXP_memory bandwidth total (MB/sec)", - "expression" : "([UNC_M_PMM_RPQ_INSERTS] + [UNC_M_PMM_WPQ_INSERTS]) * 64 / 1000000" - }, - { - "name" : "metric_3DXP memory RPQ read latency (ns)", - "expression" : "(([UNC_M_PMM_RPQ_OCCUPANCY.ALL] / [UNC_M_PMM_RPQ_INSERTS]) / ([UNC_M_CLOCKTICKS] / ([const_socket_count] * 6))) * 1000000000" - }, - { - "name" : "metric_3DXP memory WPQ write latency (ns)", - "expression" : "(([UNC_M_PMM_WPQ_OCCUPANCY.ALL] / [UNC_M_PMM_WPQ_INSERTS]) / ([UNC_M_CLOCKTICKS] / ([const_socket_count] * 6))) * 1000000000" - }, - { - "name" : "metric_IO_bandwidth_disk_or_network_writes (MB/sec)", - "expression" : "([UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART0] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART1] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART2] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART3]) * 4 / 1000000" - }, - { - "name" : "metric_IO_bandwidth_disk_or_network_reads (MB/sec)", - "expression" : "([UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART1] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART2] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART3]) * 4 / 1000000" - }, - { - "name" : "metric_TMAM_Info_cycles_both_threads_active(%)", - "expression" : "100 * ( (1 - ([CPU_CLK_THREAD_UNHALTED.ONE_THREAD_ACTIVE] / ([CPU_CLK_THREAD_UNHALTED.REF_XCLK_ANY] / 2)) ) if [const_thread_count] > 1 else 0)" - }, - { - "name" : "metric_TMAM_Info_CoreIPC", - "expression" : "[instructions] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_Frontend_Bound(%)", - "expression" : "100 * [IDQ_UOPS_NOT_DELIVERED.CORE] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" - }, - { - "name" : "metric_TMAM_..Frontend_Latency(%)", - "expression" : "100 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / ([CPU_CLK_UNHALTED.THREAD_ANY] /[const_thread_count])" - }, - { - "name" : "metric_TMAM_....ICache_Misses(%)", - "expression" : "100 * ([ICACHE_16B.IFDATA_STALL] + 2 * [ICACHE_16B.c1_e1_IFDATA_STALL]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....ITLB_Misses(%)", - "expression" : "100 * [ICACHE_64B.IFTAG_STALL] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....Branch_Resteers(%)", - "expression" : "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] + 9 * [BACLEARS.ANY]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......Mispredicts_Resteers(%)", - "expression" : "100 * [INT_MISC.CLEAR_RESTEER_CYCLES] * ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......Clears_Resteers(%)", - "expression" : "100 * [INT_MISC.CLEAR_RESTEER_CYCLES] * (1 - ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT]))) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......Unknown_Branches_Resteers(%)", - "expression" : "100 * (9 * [BACLEARS.ANY]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_..Frontend_Bandwidth(%)", - "expression" : "100 * ([IDQ_UOPS_NOT_DELIVERED.CORE] - 4 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE]) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" - }, - { - "name" : "metric_TMAM_Bad_Speculation(%)", - "expression" : "100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + ((4 * [INT_MISC.RECOVERY_CYCLES_ANY]) / [const_thread_count])) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " - }, - { - "name" : "metric_TMAM_..Branch_Mispredicts(%)", - "expression" : "100 * ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]) " - }, - { - "name" : "metric_TMAM_..Machine_Clears(%)", - "expression" : "100 * ([MACHINE_CLEARS.COUNT] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_Backend_bound(%)", - "expression" : "100 - (100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " - }, - { - "name" : "metric_TMAM_..Memory_Bound(%)", - "expression" : "100 * (1 - (([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([EXE_ACTIVITY.EXE_BOUND_0_PORTS] + [EXE_ACTIVITY.1_PORTS_UTIL] + ([EXE_ACTIVITY.2_PORTS_UTIL] if ([instructions] / [cpu-cycles]) > 1.8 else 0) + [CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES])" - }, - { "name" : "metric_TMAM_....L1_Bound(%)", - "expression" : "100 * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]" - }, - { "name" : "metric_TMAM_......DTLB_Load(%)", - "expression" : "100 * (7 * [DTLB_LOAD_MISSES.STLB_HIT] + [DTLB_LOAD_MISSES.WALK_ACTIVE]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......Store_Fwd_Blk(%)", - "expression" : "100 * (13 * [LD_BLOCKS.STORE_FORWARD]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....L2_Bound(%)", - "expression" : "100 * ([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....L3_Bound(%)", - "expression" : "100 * ([CYCLE_ACTIVITY.STALLS_L2_MISS] - [CYCLE_ACTIVITY.STALLS_L3_MISS]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......L3_Latency(%)", - "expression" : "100 * (((min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_DATA_RD], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_L3_MISS_DEMAND_DATA_RD], [cpu-cycles])) / [cpu-cycles]) - ((min([OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD_GE_6], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6] , [cpu-cycles])) / [cpu-cycles]))" - }, - { - "name" : "metric_TMAM_......L3_Bandwidth(%)", - "expression" : "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD_GE_6], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6], [cpu-cycles])) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......SQ_Full(%)", - "expression" : "100 * ([OFFCORE_REQUESTS_BUFFER.SQ_FULL] / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_....MEM_Bound(%)", - "expression" : "100 * [CYCLE_ACTIVITY.STALLS_L3_MISS] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......MEM_Bandwidth(%)", - "expression" : "100 * min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6] , [cpu-cycles]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......MEM_Latency(%)", - "expression" : "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_L3_MISS_DEMAND_DATA_RD] , [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6] , [cpu-cycles]))/ [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....Stores_Bound(%)", - "expression" : "100 * [EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......DTLB_Store(%)", - "expression" : "100 * (7 * [DTLB_STORE_MISSES.STLB_HIT] + [DTLB_STORE_MISSES.WALK_ACTIVE]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_..Core_Bound(%)", - "expression" : "100 * (1 - (([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])))) * (1 - (([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([EXE_ACTIVITY.EXE_BOUND_0_PORTS] + [EXE_ACTIVITY.1_PORTS_UTIL] + ([EXE_ACTIVITY.2_PORTS_UTIL] if ([instructions] / [cpu-cycles]) > 1.8 else 0) + [CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES])))" - }, - { - "name" : "metric_TMAM_....Divider(%)", - "expression" : "100 * [ARITH.DIVIDER_ACTIVE] / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_....Ports_Utilization(%)", - "expression" : "100 * (([EXE_ACTIVITY.EXE_BOUND_0_PORTS] + [EXE_ACTIVITY.1_PORTS_UTIL] + ([EXE_ACTIVITY.2_PORTS_UTIL] if ([instructions] / [cpu-cycles]) > 1.8 else 0) + [CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) - [CYCLE_ACTIVITY.STALLS_MEM_ANY] - [EXE_ACTIVITY.BOUND_ON_STORES]) / [cpu-cycles]" - }, - { - "name" : "metric_TMAM_......0_Port_Utilized(%)", - "expression" : "100 * (([UOPS_EXECUTED.CORE_CYCLES_NONE] / 2) if ([const_thread_count] > 1) else [EXE_ACTIVITY.EXE_BOUND_0_PORTS]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_......1_Port_Utilized(%)", - "expression" : "100 * ((([UOPS_EXECUTED.CORE_CYCLES_GE_1] - [UOPS_EXECUTED.CORE_CYCLES_GE_2]) / 2) if ([const_thread_count] > 1) else [EXE_ACTIVITY.1_PORTS_UTIL]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_......2_Port_Utilized(%)", - "expression" : "100 * ((([UOPS_EXECUTED.CORE_CYCLES_GE_2] - [UOPS_EXECUTED.CORE_CYCLES_GE_3]) / 2) if ([const_thread_count] > 1) else [EXE_ACTIVITY.2_PORTS_UTIL]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" - }, - { - "name" : "metric_TMAM_......3m_Ports_Utilized(%)", - "expression" : "100 * [UOPS_EXECUTED.CORE_CYCLES_GE_3] / [CPU_CLK_UNHALTED.THREAD_ANY]" - }, - { - "name" : "metric_TMAM_Retiring(%)", - "expression" : "100 * [UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" - }, - { - "name" : "metric_TMAM_..Base(%)", - "expression" : "100 * (([UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) - (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))))" - }, - { - "name" : "metric_TMAM_..Microcode_Sequencer(%)", - "expression" : "100 * (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])))" - } -] - - + { + "name": "metric_CPU operating frequency (in GHz)", + "expression": "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" + }, + { + "name": "metric_CPU utilization %", + "expression": "100 * [ref-cycles] / [const_TSC]" + }, + { + "name": "metric_CPU utilization% in kernel mode", + "expression": "100 * [ref-cycles:k] / [const_TSC]" + }, + { + "name": "metric_CPI", + "expression": "[cpu-cycles] / [instructions]" + }, + { + "name": "metric_kernel_CPI", + "expression": "[cpu-cycles:k] / [instructions:k]" + }, + { + "name": "metric_L1D MPI (includes data+rfo w/ prefetches)", + "tags": "transaction", + "expression": "[L1D.REPLACEMENT] / [instructions]" + }, + { + "name": "metric_L1D demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" + }, + { + "name": "metric_L1-I code read misses (w/ prefetches) per instr", + "expression": "[L2_RQSTS.ALL_CODE_RD] / [instructions]" + }, + { + "name": "metric_L2 demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" + }, + { + "name": "metric_L2 MPI (includes code+data+rfo w/ prefetches)", + "expression": "[L2_LINES_IN.ALL] / [instructions]" + }, + { + "name": "metric_L2 demand data read MPI", + "expression": "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" + }, + { + "name": "metric_L2 demand code MPI", + "expression": "[L2_RQSTS.CODE_RD_MISS] / [instructions]" + }, + { + "name": "metric_LLC MPI (includes code+data+rfo w/ prefetches)", + "expression": "([UNC_CHA_TOR_INSERTS.IA_MISS.0x12CC0233] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x12D40433] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x12C40033]) / [instructions]" + }, + { + "name": "metric_LLC code read MPI (demand+prefetch)", + "expression": "[UNC_CHA_TOR_INSERTS.IA_MISS.0x12CC0233] / [instructions]" + }, + { + "name": "metric_LLC data read MPI (demand+prefetch)", + "expression": "[UNC_CHA_TOR_INSERTS.IA_MISS.0x12D40433] / [instructions]" + }, + { + "name": "metric_LLC total HITM (per instr)", + "expression": "[OCR.ALL_READS.L3_MISS.REMOTE_HITM] / [instructions]" + }, + { + "name": "metric_LLC total HIT clean line forwards (per instr)", + "expression": "[OCR.ALL_READS.L3_MISS.REMOTE_HIT_FORWARD] / [instructions]" + }, + { + "name": "metric_Average LLC data read miss latency (in clks)", + "expression": "[OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD] / [OFFCORE_REQUESTS.L3_MISS_DEMAND_DATA_RD]" + }, + { + "name": "metric_Average LLC data read miss latency (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS.0x40433] / [UNC_CHA_TOR_INSERTS.IA_MISS.0x40433]) / ( [UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" + }, + { + "name": "metric_Average LLC data read miss latency for LOCAL requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS.0x40432] / [UNC_CHA_TOR_INSERTS.IA_MISS.0x40432]) / ( [UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" + }, + { + "name": "metric_Average LLC data read miss latency for REMOTE requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS.0x40431] / [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431]) / ( [UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) )" + }, + { + "name": "metric_ITLB MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_ITLB large page MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB 4KB page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_4K] / [instructions]" + }, + { + "name": "metric_DTLB 2MB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB 1GB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_1G] / [instructions]" + }, + { + "name": "metric_DTLB store MPI", + "expression": "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB load miss latency (in core clks)", + "expression": "[DTLB_LOAD_MISSES.WALK_ACTIVE] / [DTLB_LOAD_MISSES.WALK_COMPLETED]" + }, + { + "name": "metric_DTLB store miss latency (in core clks)", + "expression": "[DTLB_STORE_MISSES.WALK_ACTIVE] / [DTLB_STORE_MISSES.WALK_COMPLETED]" + }, + { + "name": "metric_ITLB miss latency (in core clks)", + "expression": "[ITLB_MISSES.WALK_ACTIVE] / [ITLB_MISSES.WALK_COMPLETED]" + }, + { + "name": "metric_NUMA %_Reads addressed to local DRAM", + "expression": "100 * [UNC_CHA_TOR_INSERTS.IA_MISS.0x40432] / ([UNC_CHA_TOR_INSERTS.IA_MISS.0x40432] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431])" + }, + { + "name": "metric_NUMA %_Reads addressed to remote DRAM", + "expression": "100 * [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431] / ([UNC_CHA_TOR_INSERTS.IA_MISS.0x40432] + [UNC_CHA_TOR_INSERTS.IA_MISS.0x40431])" + }, + { + "name": "metric_UPI Data transmit BW (MB/sec) (only data)", + "expression": "[UNC_UPI_TxL_FLITS.ALL_DATA] * (64 / 9) / 1000000" + }, + { + "name": "metric_UPI Transmit utilization_% (includes control)", + "expression": "100 * (([UNC_UPI_TxL_FLITS.ALL_DATA] + [UNC_UPI_TxL_FLITS.NON_DATA]) / 3) / ((((([const_tsc_freq] / ([const_cha_count] * [const_thread_count])) / (([const_tsc_freq] / ([const_cha_count] * [const_thread_count])) - [FREERUN_CORE_C6_RESIDENCY])) * ([UNC_UPI_CLOCKTICKS] - [UNC_UPI_L1_POWER_CYCLES])) * 5 / 6))" + }, + { + "name": "metric_uncore frequency GHz", + "expression": "[UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) / 1000000000" + }, + { + "name": "metric_package power (watts)", + "expression": "[power/energy-pkg/]" + }, + { + "name": "metric_DRAM power (watts)", + "expression": "[power/energy-ram/]" + }, + { + "name": "metric_core c6 residency %", + "expression": "100 * [cstate_core/c6-residency/] / [const_TSC]" + }, + { + "name": "metric_package c6 residency %", + "expression": "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" + }, + { + "name": "metric_core % cycles in non AVX license", + "expression": "(100 * [CORE_POWER.LVL0_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + }, + { + "name": "metric_core % cycles in AVX2 license", + "expression": "(100 * [CORE_POWER.LVL1_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + }, + { + "name": "metric_core % cycles in AVX-512 license", + "expression": "(100 * [CORE_POWER.LVL2_TURBO_LICENSE]) / ([CORE_POWER.LVL0_TURBO_LICENSE] + [CORE_POWER.LVL1_TURBO_LICENSE] + [CORE_POWER.LVL2_TURBO_LICENSE])" + }, + { + "name": "metric_core initiated local dram read bandwidth (MB/sec)", + "expression": "[OCR.ALL_READS.L3_MISS_LOCAL_DRAM.ANY_SNOOP] * 64 / 1000000" + }, + { + "name": "metric_core initiated remote dram read bandwidth (MB/sec)", + "expression": "[OCR.ALL_READS.L3_MISS_LOCAL_DRAM.ANY_SNOOP_ocr_msr_3fB80007f7] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" + }, + { + "name": "metric_DCPMEM_memory_mode near memory cache read miss rate%", + "expression": "100 * ([UNC_M_PMM_RPQ_INSERTS] / ([UNC_M2M_TAG_HIT.NM_RD_HIT_CLEAN] + [UNC_M2M_TAG_HIT.NM_RD_HIT_DIRTY] + [UNC_M_PMM_RPQ_INSERTS]))" + }, + { + "name": "metric_3DXP_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_PMM_RPQ_INSERTS] * 64 / 1000000" + }, + { + "name": "metric_3DXP_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_PMM_WPQ_INSERTS] * 64 / 1000000" + }, + { + "name": "metric_3DXP_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_PMM_RPQ_INSERTS] + [UNC_M_PMM_WPQ_INSERTS]) * 64 / 1000000" + }, + { + "name": "metric_3DXP memory RPQ read latency (ns)", + "expression": "(([UNC_M_PMM_RPQ_OCCUPANCY.ALL] / [UNC_M_PMM_RPQ_INSERTS]) / ([UNC_M_CLOCKTICKS] / ([const_socket_count] * 6))) * 1000000000" + }, + { + "name": "metric_3DXP memory WPQ write latency (ns)", + "expression": "(([UNC_M_PMM_WPQ_OCCUPANCY.ALL] / [UNC_M_PMM_WPQ_INSERTS]) / ([UNC_M_CLOCKTICKS] / ([const_socket_count] * 6))) * 1000000000" + }, + { + "name": "metric_IO_bandwidth_disk_or_network_writes (MB/sec)", + "expression": "([UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART0] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART1] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART2] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_READ.PART3]) * 4 / 1000000" + }, + { + "name": "metric_IO_bandwidth_disk_or_network_reads (MB/sec)", + "expression": "([UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART1] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART2] + [UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART3]) * 4 / 1000000" + }, + { + "name": "metric_TMAM_Info_cycles_both_threads_active(%)", + "expression": "100 * ( (1 - ([CPU_CLK_THREAD_UNHALTED.ONE_THREAD_ACTIVE] / ([CPU_CLK_THREAD_UNHALTED.REF_XCLK_ANY] / 2)) ) if [const_thread_count] > 1 else 0)" + }, + { + "name": "metric_TMAM_Info_CoreIPC", + "expression": "[instructions] / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_Frontend_Bound(%)", + "expression": "100 * [IDQ_UOPS_NOT_DELIVERED.CORE] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" + }, + { + "name": "metric_TMAM_..Frontend_Latency(%)", + "expression": "100 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE] / ([CPU_CLK_UNHALTED.THREAD_ANY] /[const_thread_count])" + }, + { + "name": "metric_TMAM_....ICache_Misses(%)", + "expression": "100 * ([ICACHE_16B.IFDATA_STALL] + 2 * [ICACHE_16B.c1_e1_IFDATA_STALL]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....ITLB_Misses(%)", + "expression": "100 * [ICACHE_64B.IFTAG_STALL] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....Branch_Resteers(%)", + "expression": "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] + 9 * [BACLEARS.ANY]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......Mispredicts_Resteers(%)", + "expression": "100 * [INT_MISC.CLEAR_RESTEER_CYCLES] * ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......Clears_Resteers(%)", + "expression": "100 * [INT_MISC.CLEAR_RESTEER_CYCLES] * (1 - ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT]))) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......Unknown_Branches_Resteers(%)", + "expression": "100 * (9 * [BACLEARS.ANY]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_..Frontend_Bandwidth(%)", + "expression": "100 * ([IDQ_UOPS_NOT_DELIVERED.CORE] - 4 * [IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE]) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" + }, + { + "name": "metric_TMAM_Bad_Speculation(%)", + "expression": "100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + ((4 * [INT_MISC.RECOVERY_CYCLES_ANY]) / [const_thread_count])) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " + }, + { + "name": "metric_TMAM_..Branch_Mispredicts(%)", + "expression": "100 * ([BR_MISP_RETIRED.ALL_BRANCHES] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]) " + }, + { + "name": "metric_TMAM_..Machine_Clears(%)", + "expression": "100 * ([MACHINE_CLEARS.COUNT] / ([BR_MISP_RETIRED.ALL_BRANCHES] + [MACHINE_CLEARS.COUNT])) * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * [INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_Backend_bound(%)", + "expression": "100 - (100 * ([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])) " + }, + { + "name": "metric_TMAM_..Memory_Bound(%)", + "expression": "100 * (1 - (([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + 4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count]) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * [CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([EXE_ACTIVITY.EXE_BOUND_0_PORTS] + [EXE_ACTIVITY.1_PORTS_UTIL] + ([EXE_ACTIVITY.2_PORTS_UTIL] if ([instructions] / [cpu-cycles]) > 1.8 else 0) + [CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES])" + }, + { + "name": "metric_TMAM_....L1_Bound(%)", + "expression": "100 * ([CYCLE_ACTIVITY.STALLS_MEM_ANY] - [CYCLE_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......DTLB_Load(%)", + "expression": "100 * (7 * [DTLB_LOAD_MISSES.STLB_HIT] + [DTLB_LOAD_MISSES.WALK_ACTIVE]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......Store_Fwd_Blk(%)", + "expression": "100 * (13 * [LD_BLOCKS.STORE_FORWARD]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....L2_Bound(%)", + "expression": "100 * ([CYCLE_ACTIVITY.STALLS_L1D_MISS] - [CYCLE_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....L3_Bound(%)", + "expression": "100 * ([CYCLE_ACTIVITY.STALLS_L2_MISS] - [CYCLE_ACTIVITY.STALLS_L3_MISS]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......L3_Latency(%)", + "expression": "100 * (((min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_DATA_RD], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_L3_MISS_DEMAND_DATA_RD], [cpu-cycles])) / [cpu-cycles]) - ((min([OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD_GE_6], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6] , [cpu-cycles])) / [cpu-cycles]))" + }, + { + "name": "metric_TMAM_......L3_Bandwidth(%)", + "expression": "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD_GE_6], [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6], [cpu-cycles])) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......SQ_Full(%)", + "expression": "100 * ([OFFCORE_REQUESTS_BUFFER.SQ_FULL] / [const_thread_count]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_....MEM_Bound(%)", + "expression": "100 * [CYCLE_ACTIVITY.STALLS_L3_MISS] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......MEM_Bandwidth(%)", + "expression": "100 * min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6] , [cpu-cycles]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......MEM_Latency(%)", + "expression": "100 * (min([OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_L3_MISS_DEMAND_DATA_RD] , [cpu-cycles]) - min([OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6] , [cpu-cycles]))/ [cpu-cycles]" + }, + { + "name": "metric_TMAM_....Stores_Bound(%)", + "expression": "100 * [EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......DTLB_Store(%)", + "expression": "100 * (7 * [DTLB_STORE_MISSES.STLB_HIT] + [DTLB_STORE_MISSES.WALK_ACTIVE]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_..Core_Bound(%)", + "expression": "100 * (1 - (([UOPS_ISSUED.ANY] - [UOPS_RETIRED.RETIRE_SLOTS] + (4 * ([INT_MISC.RECOVERY_CYCLES_ANY] / [const_thread_count])) + [IDQ_UOPS_NOT_DELIVERED.CORE] + [UOPS_RETIRED.RETIRE_SLOTS]) / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])))) * (1 - (([CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) / ([EXE_ACTIVITY.EXE_BOUND_0_PORTS] + [EXE_ACTIVITY.1_PORTS_UTIL] + ([EXE_ACTIVITY.2_PORTS_UTIL] if ([instructions] / [cpu-cycles]) > 1.8 else 0) + [CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES])))" + }, + { + "name": "metric_TMAM_....Divider(%)", + "expression": "100 * [ARITH.DIVIDER_ACTIVE] / [cpu-cycles]" + }, + { + "name": "metric_TMAM_....Ports_Utilization(%)", + "expression": "100 * (([EXE_ACTIVITY.EXE_BOUND_0_PORTS] + [EXE_ACTIVITY.1_PORTS_UTIL] + ([EXE_ACTIVITY.2_PORTS_UTIL] if ([instructions] / [cpu-cycles]) > 1.8 else 0) + [CYCLE_ACTIVITY.STALLS_MEM_ANY] + [EXE_ACTIVITY.BOUND_ON_STORES]) - [CYCLE_ACTIVITY.STALLS_MEM_ANY] - [EXE_ACTIVITY.BOUND_ON_STORES]) / [cpu-cycles]" + }, + { + "name": "metric_TMAM_......0_Port_Utilized(%)", + "expression": "100 * (([UOPS_EXECUTED.CORE_CYCLES_NONE] / 2) if ([const_thread_count] > 1) else [EXE_ACTIVITY.EXE_BOUND_0_PORTS]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_......1_Port_Utilized(%)", + "expression": "100 * ((([UOPS_EXECUTED.CORE_CYCLES_GE_1] - [UOPS_EXECUTED.CORE_CYCLES_GE_2]) / 2) if ([const_thread_count] > 1) else [EXE_ACTIVITY.1_PORTS_UTIL]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_......2_Port_Utilized(%)", + "expression": "100 * ((([UOPS_EXECUTED.CORE_CYCLES_GE_2] - [UOPS_EXECUTED.CORE_CYCLES_GE_3]) / 2) if ([const_thread_count] > 1) else [EXE_ACTIVITY.2_PORTS_UTIL]) / ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])" + }, + { + "name": "metric_TMAM_......3m_Ports_Utilized(%)", + "expression": "100 * [UOPS_EXECUTED.CORE_CYCLES_GE_3] / [CPU_CLK_UNHALTED.THREAD_ANY]" + }, + { + "name": "metric_TMAM_Retiring(%)", + "expression": "100 * [UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))" + }, + { + "name": "metric_TMAM_..Base(%)", + "expression": "100 * (([UOPS_RETIRED.RETIRE_SLOTS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))) - (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count]))))" + }, + { + "name": "metric_TMAM_..Microcode_Sequencer(%)", + "expression": "100 * (([UOPS_RETIRED.RETIRE_SLOTS] / [UOPS_ISSUED.ANY]) * [IDQ.MS_UOPS] / (4 * ([CPU_CLK_UNHALTED.THREAD_ANY] / [const_thread_count])))" + } +] \ No newline at end of file diff --git a/events/metric_spr.json b/events/metric_spr.json new file mode 100644 index 0000000..718872e --- /dev/null +++ b/events/metric_spr.json @@ -0,0 +1,311 @@ +[ + { + "name": "metric_CPU operating frequency (in GHz)", + "expression": "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" + }, + { + "name": "metric_CPU utilization %", + "expression": "100 * [ref-cycles] / [const_TSC]" + }, + { + "name": "metric_CPU utilization% in kernel mode", + "expression": "100 * [ref-cycles:k] / [const_TSC]" + }, + { + "name": "metric_CPI", + "expression": "[cpu-cycles] / [instructions]" + }, + { + "name": "metric_kernel_CPI", + "expression": "[cpu-cycles:k] / [instructions:k]" + }, + { + "name": "metric_IPC", + "expression": "[instructions] / [cpu-cycles]" + }, + { + "name": "metric_giga_instructions_per_sec", + "expression": "[instructions] / 1000000000" + }, + { + "name": "metric_L1D MPI (includes data+rfo w/ prefetches)", + "tags": "transaction", + "expression": "[L1D.REPLACEMENT] / [instructions]" + }, + { + "name": "metric_L1D demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" + }, + { + "name": "metric_L1-I code read misses (w/ prefetches) per instr", + "expression": "[L2_RQSTS.ALL_CODE_RD] / [instructions]" + }, + { + "name": "metric_L2 demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" + }, + { + "name": "metric_L2 MPI (includes code+data+rfo w/ prefetches)", + "expression": "[L2_LINES_IN.ALL] / [instructions]" + }, + { + "name": "metric_L2 demand data read MPI", + "expression": "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" + }, + { + "name": "metric_L2 demand code MPI", + "expression": "[L2_RQSTS.CODE_RD_MISS] / [instructions]" + }, + { + "name": "metric_UPI Data transmit BW (MB/sec) (only data)", + "expression": "[UNC_UPI_TxL_FLITS.ALL_DATA] * (64 / 9) / 1000000" + }, + { + "name": "metric_package power (watts)", + "expression": "[power/energy-pkg/]" + }, + { + "name": "metric_DRAM power (watts)", + "expression": "[power/energy-ram/]" + }, + { + "name": "metric_core c6 residency %", + "expression": "100 * [cstate_core/c6-residency/] / [const_TSC]" + }, + { + "name": "metric_package c6 residency %", + "expression": "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" + }, + { + "name": "metric_core initiated local dram read bandwidth (MB/sec)", + "expression": "([OCR.READS_TO_CORE.LOCAL_DRAM] + [OCR.HWPF_L3.L3_MISS_LOCAL]) * 64 / 1000000" + }, + { + "name": "metric_core initiated remote dram read bandwidth (MB/sec)", + "expression": "([OCR.READS_TO_CORE.REMOTE_DRAM] + [OCR.HWPF_L3.REMOTE]) * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth read (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.RD] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth write (MB/sec)", + "expression": "[UNC_M_CAS_COUNT.WR] * 64 / 1000000" + }, + { + "name": "metric_memory bandwidth total (MB/sec)", + "expression": "([UNC_M_CAS_COUNT.RD] + [UNC_M_CAS_COUNT.WR]) * 64 / 1000000" + }, + { + "name": "metric_DCPMEM_memory_mode near memory cache read miss rate%", + "expression": "100 * ([UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY]) / ([UNC_M_TAGCHK.HIT] + [UNC_M_TAGCHK.MISS_CLEAN] + [UNC_M_TAGCHK.MISS_DIRTY])" + }, + { + "name": "metric_LLC code read MPI (demand+prefetch)", + "expression": "[UNC_CHA_TOR_INSERTS.IA_MISS_CRD] / [instructions]" + }, + { + "name": "metric_LLC data read MPI (demand+prefetch)", + "expression": "([UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF]) / [instructions]" + }, + { + "name": "metric_LLC total HITM (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HITM] / [instructions]" + }, + { + "name": "metric_LLC total HIT clean line forwards (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HIT_WITH_FWD] / [instructions]" + }, + { + "name": "metric_Average LLC demand data read miss latency (in ns)", + "expression": "1000000000 * ([UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_Average LLC demand data read miss latency for LOCAL requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_Average LLC demand data read miss latency for REMOTE requests (in ns)", + "expression": "(1000000000 * [UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE] / [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE]) / ([UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]))" + }, + { + "name": "metric_ITLB (2nd level) MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) 2MB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) store MPI", + "expression": "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_NUMA %_Reads addressed to local DRAM", + "expression": "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" + }, + { + "name": "metric_NUMA %_Reads addressed to remote DRAM", + "expression": "100 * ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE]) / ([UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE] + [UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE])" + }, + { + "name": "metric_uncore frequency GHz", + "expression": "[UNC_CHA_CLOCKTICKS] / ([const_cha_count] * [const_socket_count]) / 1000000000" + }, + { + "name": "metric_IO_bandwidth_disk_or_network_writes (MB/sec)", + "expression": "[UNC_CHA_TOR_INSERTS.IO_PCIRDCUR] * 64 / 1000000" + }, + { + "name": "metric_IO_bandwidth_disk_or_network_reads (MB/sec)", + "expression": "([UNC_CHA_TOR_INSERTS.IO_ITOM] + [UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR]) * 64 / 1000000" + }, + { + "name": "metric_TMA_Frontend_Bound(%)", + "expression": "100 * ([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([slots]))" + }, + { + "name": "metric_TMA_..Fetch_Latency(%)", + "expression": "100 * (([topdown-fetch-lat] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([slots])))" + }, + { + "name": "metric_TMA_....ICache_Misses(%)", + "expression": "100 * ([ICACHE_DATA.STALLS] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....ITLB_Misses(%)", + "expression": "100 * ([ICACHE_TAG.STALLS] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....Branch_Resteers(%)", + "expression": "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles] + ([INT_MISC.UNKNOWN_BRANCH_CYCLES] / [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Mispredicts_Resteers(%)", + "expression": "100 * ((([topdown-br-mispredict] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) / (max(0, (1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])))))))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Clears_Resteers(%)", + "expression": "100 * ((1 - (([topdown-br-mispredict] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) / (max(0, (1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))))))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Unknown_Branches(%)", + "expression": "100 * ([INT_MISC.UNKNOWN_BRANCH_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_..Fetch_Bandwidth(%)", + "expression": "100 * (max(0, (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) - (([topdown-fetch-lat] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots])))))" + }, + { + "name": "metric_TMA_Bad_Speculation(%)", + "expression": "100 * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0))" + }, + { + "name": "metric_TMA_..Branch_Mispredicts(%)", + "expression": "100 * ([topdown-br-mispredict] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])))" + }, + { + "name": "metric_TMA_..Machine_Clears(%)", + "expression": "100 * (max(0, ((max(0, (1 - (([topdown-fe-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) - [INT_MISC.UOP_DROPPING] / [slots]) + ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))) + ([topdown-retiring] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))))) - ([topdown-br-mispredict] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))))" + }, + { + "name": "metric_TMA_Backend_Bound(%)", + "expression": "100 * ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])))" + }, + { + "name": "metric_TMA_..Memory_Bound(%)", + "expression": "100 * ([topdown-mem-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])))" + }, + { + "name": "metric_TMA_....L1_Bound(%)", + "expression": "100 * (max(0, (([EXE_ACTIVITY.BOUND_ON_LOADS] - [MEMORY_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles])))" + }, + { + "name": "metric_TMA_......DTLB_Load(%)", + "expression": "100 * (min((7) * [DTLB_LOAD_MISSES.STLB_HIT:c1] + [DTLB_LOAD_MISSES.WALK_ACTIVE], max([CYCLE_ACTIVITY.CYCLES_MEM_ANY] - [CYCLE_ACTIVITY.CYCLES_L1D_MISS], 0)) / ( [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Split_Loads(%)", + "expression": "100 * (min(1, ((([L1D_PEND_MISS.PENDING] / ([MEM_LOAD_COMPLETED.L1_MISS_ANY])) * [LD_BLOCKS.NO_SR] / [cpu-cycles]))))" + }, + { + "name": "metric_TMA_....L2_Bound(%)", + "expression": "100 * (([MEMORY_ACTIVITY.STALLS_L1D_MISS] - [MEMORY_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles])" + }, + { + "name": "metric_TMA_....L3_Bound(%)", + "expression": "100 * (([MEMORY_ACTIVITY.STALLS_L2_MISS] - [MEMORY_ACTIVITY.STALLS_L3_MISS]) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......MEM_Bandwidth(%)", + "expression": "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.DATA_RD:c4] - 0))) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......MEM_Latency(%)", + "expression": "100 * ( ( min( [cpu-cycles], [OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD] ) ) / ( [cpu-cycles] ) - ( ( min([cpu-cycles], [OFFCORE_REQUESTS_OUTSTANDING.DATA_RD:c4] ) ) / ( [cpu-cycles] ) ) )" + }, + { + "name": "metric_TMA_....Store_Bound(%)", + "expression": "100 * ([EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_..Core_Bound(%)", + "expression": "100 * (max(0, (([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) - ([topdown-mem-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))))" + }, + { + "name": "metric_TMA_....Divider(%)", + "expression": "100 * ([ARITH.DIV_ACTIVE] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....Ports_Utilization(%)", + "expression": "100 * ( ( [EXE_ACTIVITY.3_PORTS_UTIL:u0x80] + ( [RESOURCE_STALLS.SCOREBOARD] / ( [cpu-cycles] ) ) * ( [CYCLE_ACTIVITY.STALLS_TOTAL] - [EXE_ACTIVITY.BOUND_ON_LOADS] ) + ( [EXE_ACTIVITY.1_PORTS_UTIL] + ( [topdown-retiring] / ( [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound] ) ) * [EXE_ACTIVITY.2_PORTS_UTIL:u0xc] ) ) / ( [cpu-cycles] ) if ( [ARITH.DIV_ACTIVE] < ( [CYCLE_ACTIVITY.STALLS_TOTAL] - [EXE_ACTIVITY.BOUND_ON_LOADS] ) ) else ( [EXE_ACTIVITY.1_PORTS_UTIL] + ( [topdown-retiring] / ( [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound] ) ) * [EXE_ACTIVITY.2_PORTS_UTIL:u0xc] ) / ( [cpu-cycles] ) )" + }, + { + "name": "metric_TMA_......Ports_Utilized_0(%)", + "expression": "100 * ( [EXE_ACTIVITY.3_PORTS_UTIL:u0x80] / ( [cpu-cycles] ) + ( [RESOURCE_STALLS.SCOREBOARD] / ( [cpu-cycles] ) ) * ( [CYCLE_ACTIVITY.STALLS_TOTAL] - [EXE_ACTIVITY.BOUND_ON_LOADS] ) / ( [cpu-cycles] ) )" + }, + { + "name": "metric_TMA_......Ports_Utilized_1(%)", + "expression": "100 * ([EXE_ACTIVITY.1_PORTS_UTIL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Ports_Utilized_2(%)", + "expression": "100 * ([EXE_ACTIVITY.2_PORTS_UTIL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Ports_Utilized_3m(%)", + "expression": "100 * [UOPS_EXECUTED.CYCLES_GE_3] / [cpu-cycles]" + }, + { + "name": "metric_TMA_Retiring(%)", + "expression": "100 * ([topdown-retiring] / ([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))" + }, + { + "name": "metric_TMA_..Light_Operations(%)", + "expression": "100 * (max(0, (([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) - ([topdown-heavy-ops] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))))))" + }, + { + "name": "metric_TMA_..Heavy_Operations(%)", + "expression": "100 * ([topdown-heavy-ops] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound])))" + }, + { + "name": "metric_TMA_....Microcode_Sequencer(%)", + "expression": "100 * ([UOPS_RETIRED.MS] / [slots])" + }, + { + "name": "metric_TMA_Info_Thread_IPC", + "expression": "[instructions] / [cpu-cycles]" + }, + { + "name": "metric_TMA_Info_Core_ILP", + "expression": "[instructions] / [CPU_CLK_UNHALTED.DISTRIBUTED]" + }, + { + "name": "metric_TMA_Info_System_SMT_2T_Utilization", + "expression": "(1 - [CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE] / [CPU_CLK_UNHALTED.REF_DISTRIBUTED]) if [const_socket_count] > 1 else 0" + } +] \ No newline at end of file diff --git a/events/skx.txt b/events/skx.txt index b561a0a..32806c2 100644 --- a/events/skx.txt +++ b/events/skx.txt @@ -1,5 +1,5 @@ ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### diff --git a/events/skx_aws.txt b/events/skx_aws.txt index 36bd767..a3c5d4d 100644 --- a/events/skx_aws.txt +++ b/events/skx_aws.txt @@ -1,3 +1,8 @@ +########################################################################################################### +# Copyright (C) 2021-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + # Skylake event list for AWS instances cpu/event=0x51,umask=0x01,period=2000003,name='L1D.REPLACEMENT'/, diff --git a/events/skx_oci.txt b/events/skx_oci.txt index fcc5db3..79aaee1 100644 --- a/events/skx_oci.txt +++ b/events/skx_oci.txt @@ -1,3 +1,8 @@ +########################################################################################################### +# Copyright (C) 2021-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + # Skylake event list for OCI instances cpu/event=0x51,umask=0x01,period=2000003,name='L1D.REPLACEMENT'/, diff --git a/events/spr.txt b/events/spr.txt new file mode 100644 index 0000000..c0d02a6 --- /dev/null +++ b/events/spr.txt @@ -0,0 +1,149 @@ +########################################################################################################### +# Copyright (C) 2021-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + +# SapphireRapids event list (default) + +cpu/event=0x51,umask=0x01,period=100003,name='L1D.REPLACEMENT'/, +cpu/event=0x24,umask=0xe4,period=200003,name='L2_RQSTS.ALL_CODE_RD'/, +cpu/event=0xd1,umask=0x01,period=1000003,name='MEM_LOAD_RETIRED.L1_HIT'/, +cpu/event=0xa3,umask=0x04,cmask=0x04,period=1000003,name='CYCLE_ACTIVITY.STALLS_TOTAL'/, +cpu-cycles, +ref-cycles, +instructions; + +cpu/event=0x80,umask=0x04,period=500009,name='ICACHE_DATA.STALLS'/, +cpu/event=0x83,umask=0x04,period=200003,name='ICACHE_TAG.STALLS'/, +cpu/event=0xa3,umask=0x08,cmask=0x08,period=1000003,name='CYCLE_ACTIVITY.CYCLES_L1D_MISS'/, +cpu/event=0xa3,umask=0x10,cmask=0x10,period=1000003,name='CYCLE_ACTIVITY.CYCLES_MEM_ANY'/, +cpu-cycles; + + +cpu/event=0x25,umask=0x1f,period=100003,name='L2_LINES_IN.ALL'/, +cpu/event=0xd1,umask=0x10,period=100021,name='MEM_LOAD_RETIRED.L2_MISS'/, +cpu/event=0x24,umask=0x24,period=200003,name='L2_RQSTS.CODE_RD_MISS'/, +cpu/event=0x11,umask=0x0e,period=100003,name='ITLB_MISSES.WALK_COMPLETED'/, +cpu-cycles, +ref-cycles, +instructions; + +cpu/event=0x12,umask=0x0e,period=100003,name='DTLB_LOAD_MISSES.WALK_COMPLETED'/, +cpu/event=0x12,umask=0x04,period=100003,name='DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M'/, +cpu/event=0x13,umask=0x0e,period=100003,name='DTLB_STORE_MISSES.WALK_COMPLETED'/, +cpu/event=0xd1,umask=0x02,period=200003,name='MEM_LOAD_RETIRED.L2_HIT'/, +cpu-cycles:k, +ref-cycles:k, +instructions:k; + +#C6 +cstate_core/c6-residency/; +cstate_pkg/c6-residency/; + +cpu/event=0x20,umask=0x08,cmask=0x01,period=1000003,name='OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD'/, +cpu/event=0x20,umask=0x08,cmask=0x04,period=1000003,name='OFFCORE_REQUESTS_OUTSTANDING.DATA_RD:c4'/, +cpu-cycles, +ref-cycles, +instructions; + +#TMA related +slots, +topdown-bad-spec, +topdown-be-bound, +topdown-fe-bound, +topdown-retiring, +topdown-fetch-lat, +topdown-mem-bound, +topdown-br-mispredict, +topdown-heavy-ops; + +cpu/event=0xad,umask=0x10,period=1000003,name='INT_MISC.UOP_DROPPING'/, +cpu/event=0xad,umask=0x40,frontend=0x7,period=1000003,name='INT_MISC.UNKNOWN_BRANCH_CYCLES'/, +cpu/event=0xa6,umask=0x21,cmask=0x05,period=2000003,name='EXE_ACTIVITY.BOUND_ON_LOADS'/, +cpu-cycles; + +cpu/event=0x47,umask=0x03,cmask=0x03,period=1000003,name='MEMORY_ACTIVITY.STALLS_L1D_MISS'/, +cpu/event=0x12,umask=0x20,cmask=0x01,period=100003,name='DTLB_LOAD_MISSES.STLB_HIT:c1'/, +cpu/event=0x12,umask=0x10,cmask=0x01,period=100003,name='DTLB_LOAD_MISSES.WALK_ACTIVE'/, +cpu/event=0x47,umask=0x05,cmask=0x05,period=1000003,name='MEMORY_ACTIVITY.STALLS_L2_MISS'/, +cpu-cycles, +instructions; + +cpu/event=0x47,umask=0x09,cmask=0x09,period=1000003,name='MEMORY_ACTIVITY.STALLS_L3_MISS'/, +cpu/event=0xa6,umask=0x40,cmask=0x02,period=1000003,name='EXE_ACTIVITY.BOUND_ON_STORES'/, +cpu/event=0xa6,umask=0x02,period=2000003,name='EXE_ACTIVITY.1_PORTS_UTIL'/, +cpu/event=0xa6,umask=0x04,period=2000003,name='EXE_ACTIVITY.2_PORTS_UTIL'/, +cpu-cycles, +instructions; + +cpu/event=0x43,umask=0xfd,period=2000003,name='MEM_LOAD_COMPLETED.L1_MISS_ANY'/, +cpu/event=0xa2,umask=0x02,period=2000003,name='RESOURCE_STALLS.SCOREBOARD'/, +cpu/event=0xa6,umask=0x80,period=2000003,name='EXE_ACTIVITY.3_PORTS_UTIL:u0x80'/, +cpu/event=0xa6,umask=0xc,period=2000003,name='EXE_ACTIVITY.2_PORTS_UTIL:u0xc'/, +cpu-cycles, +instructions; + +cpu/event=0xad,umask=0x80,period=500009,name='INT_MISC.CLEAR_RESTEER_CYCLES'/, +cpu/event=0xb1,umask=0x01,cmask=0x03,period=2000003,name='UOPS_EXECUTED.CYCLES_GE_3'/, +cpu/event=0x48,umask=0x01,period=1000003,name='L1D_PEND_MISS.PENDING'/, +cpu/event=0x03,umask=0x88,period=100003,name='LD_BLOCKS.NO_SR'/, +cpu-cycles; + +cpu/event=0xc2,umask=0x04,frontend=0x8,period=2000003,name='UOPS_RETIRED.MS'/, +cpu/event=0xec,umask=0x02,period=2000003,name='CPU_CLK_UNHALTED.DISTRIBUTED'/, +cpu-cycles; + +cpu/event=0x3c,umask=0x02,period=25003,name='CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE'/, +cpu/event=0x3c,umask=0x08,period=2000003,name='CPU_CLK_UNHALTED.REF_DISTRIBUTED'/, +cpu/event=0xb0,umask=0x09,cmask=0x01,period=1000003,name='ARITH.DIV_ACTIVE'/; + +#offcore response +cpu/event=0x2a,umask=0x01,offcore_rsp=0x104004477,name='OCR.READS_TO_CORE.LOCAL_DRAM'/, +cpu/event=0x2a,umask=0x01,offcore_rsp=0x730004477,name='OCR.READS_TO_CORE.REMOTE_DRAM'/; + +cpu/event=0x2a,umask=0x01,offcore_rsp=0x1030004477,name='OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HITM'/, +cpu/event=0x2a,umask=0x01,offcore_rsp=0x830004477,name='OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HIT_WITH_FWD'/; + +cpu/event=0x2a,umask=0x01,offcore_rsp=0x84002380,name='OCR.HWPF_L3.L3_MISS_LOCAL'/, +cpu/event=0x2a,umask=0x01,offcore_rsp=0x90002380,name='OCR.HWPF_L3.REMOTE'/; + +#power related +power/energy-pkg/, +power/energy-ram/; + +#UPI related +upi/event=0x02,umask=0x0f,name='UNC_UPI_TxL_FLITS.ALL_DATA'/, +upi/event=0x02,umask=0x97,name='UNC_UPI_TxL_FLITS.NON_DATA'/, +upi/event=0x1,umask=0x0,name='UNC_UPI_CLOCKTICKS'/; + +cha/event=0x35,umask=0xc80ffe01,name='UNC_CHA_TOR_INSERTS.IA_MISS_CRD'/; + +cha/event=0x35,umask=0xc8177e01,name='UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE'/, +cha/event=0x36,umask=0xc8177e01,name='UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE'/; + +cha/event=0x35,umask=0xC816FE01,name='UNC_CHA_TOR_INSERTS.IA_MISS_DRD_LOCAL'/, +cha/event=0x36,umask=0xc816fe01,name='UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_LOCAL'/; + +cha/event=0x35,umask=0xC896FE01,name='UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_LOCAL'/, +cha/event=0x35,umask=0xC8977E01,name='UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_REMOTE'/; + +cha/event=0x35,umask=0xccd7fe01,name='UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA'/, +cha/event=0x35,umask=0xc817fe01,name='UNC_CHA_TOR_INSERTS.IA_MISS_DRD'/, +cha/event=0x35,umask=0xc897fe01,name='UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF'/, +cha/event=0x36,umask=0xC817fe01,name='UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD'/; + + +#IO Bandwidth +cha/event=0x35,umask=0xc8f3ff04,name='UNC_CHA_TOR_INSERTS.IO_PCIRDCUR'/, +cha/event=0x35,umask=0xCC43FF04,name='UNC_CHA_TOR_INSERTS.IO_ITOM'/, +cha/event=0x35,umask=0xCD43FF04,name='UNC_CHA_TOR_INSERTS.IO_ITOMCACHENEAR'/, +cha/event=0x01,umask=0x00,name='UNC_CHA_CLOCKTICKS'/; + +#PMEM +imc/event=0xd3,umask=0x01,name='UNC_M_TAGCHK.HIT'/, +imc/event=0xd3,umask=0x02,name='UNC_M_TAGCHK.MISS_CLEAN'/, +imc/event=0xd3,umask=0x04,name='UNC_M_TAGCHK.MISS_DIRTY'/; + +#memory read/writes +imc/event=0x05,umask=0xcf,name='UNC_M_CAS_COUNT.RD'/, +imc/event=0x05,umask=0xf0,name='UNC_M_CAS_COUNT.WR'/; \ No newline at end of file diff --git a/non_container_build.sh b/non_container_build.sh deleted file mode 100755 index 4c2decb..0000000 --- a/non_container_build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -### non container build script -### Assumes the following are installed - #python3.6 or above - #golang1.13 or above -rm -rf build -rm -rf dist -pip3 install -r requirements.txt -pip3 install pyinstaller==4.5.1 -pip3 install flake8 -pip3 install black -pip3 install bandit -go get github.com/markbates/pkger/cmd/pkger -make dist -f Makefile_non_container -rm -rf __pycache* -rm -f *.spec - diff --git a/perf-collect.py b/perf-collect.py index 432812f..db7bc17 100644 --- a/perf-collect.py +++ b/perf-collect.py @@ -1,7 +1,7 @@ -#!/usr/bin/env python3 +#! /usr/bin/python ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### @@ -100,11 +100,11 @@ def write_metadata( modified.write("### PERF DATA ###" + ",\n") if time_stamp: zone = subprocess.check_output( # nosec - ["date"], universal_newlines=True # nosec + ["date", "+%Z"], universal_newlines=True # nosec ).split() # nosec epoch = str(perf_helpers.get_epoch(time_stamp)) modified.write( - time_stamp.rstrip() + " " + zone[4] + " EPOCH " + epoch + "\n" + time_stamp.rstrip() + " " + zone[0] + " EPOCH " + epoch + "\n" ) modified.write(data) @@ -134,7 +134,6 @@ def is_safe_file(fname, substr): if __name__ == "__main__": - script_path = os.path.dirname(os.path.realpath(__file__)) # fix the pyinstaller path if "_MEI" in script_path: @@ -218,7 +217,7 @@ def is_safe_file(fname, substr): "--cloud", type=str, default=None, - help="Name of the Cloud Service Provider(AWS), if collecting on cloud instances", + help="Name of the Cloud Service Provider(AWS), if collecting on cloud instances. Currently supporting AWS and OCI", ) parser.add_argument( "-ct", @@ -227,7 +226,6 @@ def is_safe_file(fname, substr): default="VM", help="Instance type: Options include - VM,BM", ) - args = parser.parse_args() if args.version: @@ -237,9 +235,7 @@ def is_safe_file(fname, substr): interval = int(args.interval * 1000) if args.app and args.timeout: - raise SystemExit( - "Please provide either time duration or application parameter but" - ) + raise SystemExit("Please provide time duration or application parameter") if args.cid and args.pid: raise SystemExit("Cannot combine cgroup with pid in same collection") @@ -289,9 +285,11 @@ def is_safe_file(fname, substr): eventfile = "icx_aws.txt" elif is_oci_vm: eventfile = "icx_oci.txt" + elif arch == "sapphirerapids": + eventfile = "spr.txt" else: raise SystemExit( - "Unsupported architecture (currently supports Broadwell, Skylake, CascadeLake and Icelake Intel Xeon processors)" + "Unsupported architecture (currently supports IA -> Broadwell, Skylake, CascadeLake Icelake and SapphireRapids)" ) # Convert path of event file to relative path if being packaged by pyInstaller into a binary @@ -352,7 +350,7 @@ def is_safe_file(fname, substr): print("Warning: nmi_watchdog enabled, perf grouping will be disabled") args.nogroups = True - # disable grouping if more than 1 cgroups are being monitored + # disable grouping if more than 1 cgroups are being monitored -- not relevant anymore cgroups = [] if args.cid is not None: cgroups = perf_helpers.get_cgroups_from_cids(args.cid.split(",")) @@ -418,7 +416,6 @@ def is_safe_file(fname, substr): events, args.outcsv, ) - elif args.cid and args.timeout: print("Info: Only CPU/core events will be enabled with cid option") perf_format = prep_events.get_cgroup_events_format( @@ -485,6 +482,7 @@ def is_safe_file(fname, substr): validate_perfargs(perfargs) try: print("Collecting perf stat for events in : %s" % eventfilename) + # PerfSpect isn't aware of the actual instance cloudtype if args.cloud and args.cloudtype != "BM": print( "If you're on baremetal cloud instance, consider using cloudtype flag (options:VM/BM, default is VM)" diff --git a/perf-postprocess.py b/perf-postprocess.py index 8765897..3a6b3d7 100644 --- a/perf-postprocess.py +++ b/perf-postprocess.py @@ -1,7 +1,7 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### @@ -21,16 +21,14 @@ # fix the pyinstaller path if "_MEI" in script_path: script_path = script_path.rsplit("/", 1)[0] - # temporary output :time series dump of raw events output_file = script_path + "/_tmp_perf_/tmp_perf_out.csv" -output_files = [] +output_files = [] # For per cgroup tmp_perf_out files # temporary output :time series dump of raw events at socket level tmp_socket_file = script_path + "/_tmp_perf_/tmp_socket_out.csv" - # temporary output:trasposed view of perf-collect output time_dump_file = script_path + "/_tmp_perf_/time_dump.csv" -time_dump_files = [] +time_dump_files = [] # For per cgroup time-dump file # final output of post-process out_metric_file = script_path + "/results/metric_out.csv" out_metric_files = [] # For per cgroup metrics @@ -38,6 +36,8 @@ html_input = "metric_out.average.csv" +# globals +# excel output related class workbook: def __init__(self): self.book = None @@ -267,6 +267,7 @@ def evaluate_expression( "{:.8f}".format(simple_eval(formula, functions={"min": min, "max": max})) ) except ZeroDivisionError: + # ignore the systems with no PMEM if "UNC_M_PMM" not in temp_formula and "UNC_M_TAGC" not in temp_formula: zero_division_errcount += 1 result = "0" @@ -300,7 +301,7 @@ def validate_file(fname): def is_safe_file(fname, substr): if not fname.endswith(substr): - raise SystemExit(str(fname) + " not a valid file") + raise SystemExit(str(fname) + " not a valid file, expecting " + str(substr)) return 1 @@ -374,6 +375,7 @@ def get_extra_out_file(out_file, t, excelsheet=False): # level: 0-> system, 1->socket, 2->thread def load_metrics(infile, outfile, level=0): global CGROUPS + event_list, event_dict = get_perf_events(level) metrics = {} validate_file(metric_file) @@ -439,12 +441,14 @@ def load_metrics(infile, outfile, level=0): OUT_WORKBOOK.writerow(0, metric_row, sheet_type) f_pmu = open(input_file, "r") pmucsv = csv.reader(f_pmu, delimiter=",") + if CGROUPS == "enabled": const_TSC = CONST_TSC_FREQ * CPUSETS[infile.rsplit("_", 1)[1].split(".")[0]] else: const_TSC = ( CONST_TSC_FREQ * CONST_CORE_COUNT * CONST_HT_COUNT * CONST_SOCKET_COUNT ) + const_dict = { "const_tsc_freq": CONST_TSC_FREQ, "const_core_count": CONST_CORE_COUNT, @@ -543,7 +547,7 @@ def write_summary(level=0): for h in reader.fieldnames: metrics.append(h) first_row = False - for (k, v) in row.items(): + for k, v in row.items(): columns[k].append(float(v)) sheet_type = "" @@ -587,6 +591,7 @@ def write_summary(level=0): if EXCEL_OUT: OUT_WORKBOOK.writerow(i, [h, avg, p95, minval, maxval], sheet_type) elif level == 1: + # [metric, S0.avg, S1.avg, S0.p95, S1.p95] socket_id = (i - 1) % int( CONST_SOCKET_COUNT ) # -1 for first column in metrics - time @@ -646,6 +651,7 @@ def get_metadata(): raise SystemExit( "The perf raw file doesn't contain metadata, please re-collect perf raw data" ) + f_dat = open(dat_file, "r") for line in f_dat: if start_events: @@ -699,6 +705,7 @@ def get_metadata(): int(docker_SET.split("-")[1]) - int(docker_SET.split("-")[0]) + 1 ) CPUSETS[docker_HASH[i]] = docker_SET + elif line.startswith("Percore mode"): PERCORE_MODE = True if (str(line.split(",")[1]) == "enabled") else False elif line.startswith("# started on"): @@ -1312,7 +1319,6 @@ def is_safe_path(base_dir, path, follow_symlinks=True): if __name__ == "__main__": - from argparse import ArgumentParser parser = ArgumentParser(description="perf-postprocess: perf post process") @@ -1408,10 +1414,9 @@ def is_safe_path(base_dir, path, follow_symlinks=True): html_input = out_metric_file.split("/")[-1] if "/" in out_metric_file: res_dir = out_metric_file.rpartition("/")[0] - # print(res_dir) - # exit() else: res_dir = script_path + if args.metricfile: metric_file = args.metricfile if dat_file and not os.path.isfile(dat_file): @@ -1437,6 +1442,7 @@ def is_safe_path(base_dir, path, follow_symlinks=True): raise SystemExit( args.html + " isn't a valid html file name, .html files are accepted" ) + # parse header get_metadata() zero_division_errcount = 0 @@ -1450,6 +1456,8 @@ def is_safe_path(base_dir, path, follow_symlinks=True): metric_file = "metric_icx_aws.json" elif CONST_ARCH == "icelake": metric_file = "metric_icx.json" + elif CONST_ARCH == "sapphirerapids": + metric_file = "metric_spr.json" else: raise SystemExit("Suitable metric file not found") @@ -1485,7 +1493,6 @@ def is_safe_path(base_dir, path, follow_symlinks=True): OUT_WORKBOOK.initialize(args.outfile, persocket_output, percore_output) samples = write_perf_tmp_output(args.epoch) - # levels: 0->system 1->socket 2->core if percore_output or persocket_output: write_socket_view(1, samples) @@ -1514,6 +1521,7 @@ def is_safe_path(base_dir, path, follow_symlinks=True): infile = time_dump_file outfile = output_file write_system_view(infile, outfile) + # Load metrics from raw data and summarize if CGROUPS == "enabled": for infile in output_files: @@ -1530,8 +1538,11 @@ def is_safe_path(base_dir, path, follow_symlinks=True): cleanup() if EXCEL_OUT: OUT_WORKBOOK.close() - if "res_dir" in locals(): + try: + res_dir perf_helpers.fix_path_ownership(res_dir, True) + except NameError: + pass if zero_division_errcount > 0: print( "Warning:" @@ -1541,6 +1552,7 @@ def is_safe_path(base_dir, path, follow_symlinks=True): + " samples were used" ) print("Post processing done, result file:%s" % args.outfile) + if args.html: from src import report diff --git a/pmu-checker/Makefile b/pmu-checker/Makefile index d6fd981..1ec7052 100644 --- a/pmu-checker/Makefile +++ b/pmu-checker/Makefile @@ -15,11 +15,8 @@ fmt: do $(GO_FMT) -w -s "$$file"; \ done -pmu-checker: - $(GO_BIN) build -o $(BINARY_NAME) -ldflags '-X main.gVersion=$(VERSION)' ./cmd/ - -pmu-checker_arm64: - GOOS=linux GOARCH=arm64 $(GO_BIN) build -o $(BINARY_NAME)_arm64 -ldflags '-X main.gVersion=$(VERSION)' ./cmd/ +pmu-checker: pmu-checker.go + $(GO_BIN) build -o $(BINARY_NAME) -ldflags '-X main.gVersion=$(VERSION)' . .PHONY: lint lint: @@ -27,6 +24,6 @@ lint: .PHONY: test test: pmu-checker - sudo ./$(BINARY_NAME) --no-stdout | jq + sudo ./$(BINARY_NAME) | jq .DEFAULT_GOAL := $(BINARY_NAME) diff --git a/pmu-checker/README.md b/pmu-checker/README.md index 8ae9729..76a9cff 100644 --- a/pmu-checker/README.md +++ b/pmu-checker/README.md @@ -1,19 +1,25 @@ # pmu-checker -Allows us to verify if the system is running any drivers/daemons that may be programming the PMU. +Allows us to verify if the system is running any drivers/daemons that may be programming the PMU. Superuseful for customers who have such instrumentations, don’t know it. + +pmu-checker specifically checks if the following MSRs are actively being programmed/used: -pmu-checker specifically checks if the following MSRs are actively being programmed/used : 1. 0x309 2. 0x30a -3. 0x30b -4. 0xc1 -5. 0xc2 -6. 0xc3 -7. 0xc4 +3. 0x30b +4. 0x30c (SPR) +5. 0xc1 +6. 0xc2 +7. 0xc3 +8. 0xc4 +9. 0xc5 (SPR) +10. 0xc6 (SPR) +11. 0xc7 (SPR) +12. 0xc8 (SPR) ## Usage -Usage: sudo ./pmu-checker [OPTION ...] +Usage: sudo ./pmu-checker [OPTION ...] Options: @@ -22,3 +28,7 @@ Options: -logfile, --logfile (string) Specify the log filename to be used for logging, Default is "pmu-checker.log" -debug, --debug Set the loglevel to debug, Default is info -no-stdout, --no-stdout Set the logwriter to write to log file only + +## Contribution + +If you are interested in contributing, feel free to fork this repo and create MR(Merge Requests). diff --git a/pmu-checker/cmd/main.go b/pmu-checker/cmd/main.go deleted file mode 100644 index 4b3ea20..0000000 --- a/pmu-checker/cmd/main.go +++ /dev/null @@ -1,149 +0,0 @@ -//########################################################################################################### -//# Copyright (C) 2021 Intel Corporation -//# SPDX-License-Identifier: BSD-3-Clause -//########################################################################################################### - -package main - -import ( - "flag" - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "sync" - - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" - - "github.com/intel/perfspect/pmu-checker/msr" -) - -const ( - description = `pmu-checker - -Allows us to verify if the system is running any drivers/daemons that may be programming the PMU. - -Options: -` - iterationCompleted = "-------------All Iteration checks completed-------------" - iterations = 6 -) - -var ( - loglevel = flag.Bool("debug", false, "set the loglevel to debug, default is info") - multiLogWriter = flag.Bool("no-stdout", false, "set the logwriter to write to logfile only, default is false") - cpu = flag.Int("cpu", 0, "Read MSRs on respective CPU, default is 0") - logfile = flag.String("logfile", "pmu-checker.log", "set the logfile name, default is pmu-checker.log") - help = flag.Bool("help", false, "Shows the usage of pmu-checker application") - logFileRegexp = regexp.MustCompile(`([a-zA-Z0-9\s_\\.\-():])+(.log|.txt)$`) -) - -func initialize() error { - if !logFileRegexp.MatchString(*logfile) { - return errors.New("the file name isn't valid for logging, the valid extensions are .log and .txt") - } - - log.SetFormatter(&log.TextFormatter{ - ForceColors: true, - FullTimestamp: true, - DisableLevelTruncation: true, - }) - // programDir, err := filepath.Abs(filepath.Dir(os.Args[0])) - ex, err := os.Executable() - if err != nil { - return errors.Wrap(err, "failed to get absolute path of the program") - } - exPath := filepath.Dir(ex) - file, err := os.OpenFile(filepath.Join(exPath, *logfile), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - return errors.Wrap(err, "failed to open the log file") - } - - //write to logfile and stdout at same time - mw := io.MultiWriter(os.Stdout, file) - - if *multiLogWriter == true { - log.SetOutput(file) - } else { - log.SetOutput(mw) - } - - log.SetLevel(log.InfoLevel) - if *loglevel == true { - log.SetLevel(log.DebugLevel) - } - - err = msr.Initialize() - if err != nil { - return errors.Wrap(err, "couldn't initialize msr module") - } - - return nil -} - -func main() { - if os.Geteuid() != 0 { - println("You need a root privileges to run.") - os.Exit(2) - } - - flag.Parse() - - if *help == true { - println(description) - flag.PrintDefaults() - os.Exit(0) - } - - err := initialize() - if err != nil { - log.Error(errors.Wrap(err, "couldn't initialize PMU Checker")) - os.Exit(2) - } - - err = msr.ValidateMSRModule(*cpu) - if err != nil { - log.Error(errors.Wrap(err, "couldn't validate MSR module")) - os.Exit(2) - } - - log.Info("Starting the PMU Checker application...") - runIterations() - log.Info(iterationCompleted) - - res, err := msr.GetActivePMUs() - if err != nil { - log.Error(errors.Wrap(err, "couldn't obtain active PMUs")) - os.Exit(2) - } - - fmt.Println(res) -} - -func runIterations() { - for i := 1; i <= iterations; i++ { - var wg sync.WaitGroup - if len(msr.UsedPMUs) == 7 { - // if all the PMUs are being used, break the loop - log.Infof("Aborting iteration check #%d", i) - break - } - - log.Debugf("Iteration check #%d started\n", i) - msr.Values.Range(func(key, value interface{}) bool { - _, ok := value.(uint64) - if !ok { - return false - } - - wg.Add(1) - go msr.ReadMSR(key.(string), &wg, i, *cpu) - return true - }) - - wg.Wait() - log.Infof("Iteration check #%d completed\n", i) - } -} diff --git a/pmu-checker/go.mod b/pmu-checker/go.mod index dbcc2a9..7923acb 100644 --- a/pmu-checker/go.mod +++ b/pmu-checker/go.mod @@ -1,13 +1,8 @@ -module github.com/intel/perfspect/pmu-checker +module pmu-checker -go 1.17 +go 1.18 require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pkg/errors v0.9.1 - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.0 - golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + github.com/sirupsen/logrus v1.9.0 + golang.org/x/sys v0.5.0 // indirect ) diff --git a/pmu-checker/go.sum b/pmu-checker/go.sum index c1dd677..2919c20 100644 --- a/pmu-checker/go.sum +++ b/pmu-checker/go.sum @@ -1,20 +1,18 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/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= -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 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 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= diff --git a/pmu-checker/msr/msr.go b/pmu-checker/msr/msr.go deleted file mode 100644 index 041458c..0000000 --- a/pmu-checker/msr/msr.go +++ /dev/null @@ -1,154 +0,0 @@ -//########################################################################################################### -//# Copyright (C) 2021 Intel Corporation -//# SPDX-License-Identifier: BSD-3-Clause -//########################################################################################################### - -package msr - -import ( - "encoding/binary" - "fmt" - "strconv" - "strings" - "sync" - "syscall" - - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" -) - -const ( - msrPath = "/dev/cpu/%d/msr" - generalPurposePMU = "General_purpose_programmable_PMU" -) - -var ( - Values = sync.Map{} - UsedPMUs []string - pmuPurpose = map[string]string{ - "0x309": "instructions", - "0x30a": "cpu_cycles", - "0x30b": "ref_cycles", - "0xc1": generalPurposePMU, - "0xc2": generalPurposePMU, - "0xc3": generalPurposePMU, - "0xc4": generalPurposePMU, - } -) - -type retMSR struct { - fd int -} - -func (dpt retMSR) read(msr int64) (uint64, error) { - // Reads a given MSR on the respective CPU - - buf := make([]byte, 8) - rc, err := syscall.Pread(dpt.fd, buf, msr) - if err != nil { - log.Fatal(err) - panic(err) - } - - if rc != 8 { - log.Errorf("wrong byte count %d", rc) - return 0, fmt.Errorf("wrong byte count %d", rc) - } - - //assuming all x86 uses little endian format - msrVal := binary.LittleEndian.Uint64(buf) - log.Tracef("MSR %d was read successfully as %d", msr, msrVal) - return msrVal, err - -} - -func openMSRInterface(cpu int) (*retMSR, error) { - // Open connection to MSR Interface with given cpu - - msrDir := fmt.Sprintf(msrPath, cpu) - fd, err := syscall.Open(msrDir, syscall.O_RDONLY, 777) - if err != nil { - return nil, errors.New("Couldn't open the msr interface") - } - - return &retMSR{fd: fd}, nil - -} - -func closeMSRInterface(dpt retMSR) error { - // Close connection to MSR Interface - return syscall.Close(dpt.fd) -} - -func ReadMSR(reg string, wg *sync.WaitGroup, thread int, cpu int) { - // Read MSR value, update map as needed - - defer wg.Done() - log.Debugf("Worker %d starting %s", thread, reg) - hexreg := strings.Replace(reg, "0x", "", -1) - hexreg = strings.Replace(hexreg, "0X", "", -1) - regInt64, err := strconv.ParseInt(hexreg, 16, 64) - if err != nil { - log.Panicf("The Hex to int64 type covertion failed\nError: ", err) - } - - msr, err := openMSRInterface(cpu) - if err != nil { - log.Panic(err) - } - - msrVal, err := msr.read(regInt64) - if err != nil { - log.Panic(err) - } - - err = closeMSRInterface(*msr) - if err != nil { - log.Panic(err) - } - - log.Debugf("New value of thread %d for %s is %d", thread, reg, msrVal) - currentVal, found := Values.Load(reg) - Values.Store(reg, msrVal) - if found == false { - // The key has been deleted, meaning PMU was active - } - - log.Debugf("Old value of thread %d for %s is %d", thread, reg, currentVal) - - if found == true && currentVal != uint64(0) && msrVal != currentVal { - // The key exists but value has changed, delete it - - UsedPMUs = append(UsedPMUs, reg) - log.Debugf("Deleting %s in the thread %d", reg, thread) - Values.Delete(reg) - - } - log.Debugf("Worker %d done for %s\n", thread, reg) - -} - -func Initialize() error { - for regPMU, _ := range pmuPurpose { - Values.Store(regPMU, uint64(0)) - } - - return nil -} - -func GetActivePMUs() (Result, error) { - var res Result - res.PMUDetails = make(map[string]string) - log.Info("Following PMU(s) are actively being used:") - for _, pmu := range UsedPMUs { - purpose, ok := pmuPurpose[pmu] - if !ok { - return Result{}, errors.New("Report this to the Developers.") - } - res.PMUDetails[pmu] = purpose - res.PMUActive++ - log.Infof("%s: might be using: %s", pmu, purpose) - } - - return res, nil -} diff --git a/pmu-checker/msr/result.go b/pmu-checker/msr/result.go deleted file mode 100644 index d62ab5c..0000000 --- a/pmu-checker/msr/result.go +++ /dev/null @@ -1,27 +0,0 @@ -//########################################################################################################### -//# Copyright (C) 2021 Intel Corporation -//# SPDX-License-Identifier: BSD-3-Clause -//########################################################################################################### - -package msr - -import ( - "encoding/json" - - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" -) - -type Result struct { - PMUActive int `json:"active_pmus"` - PMUDetails map[string]string `json:"details"` -} - -func (r Result) String() string { - js, err := json.MarshalIndent(r, "", "\t") - if err != nil { - log.Error(errors.Wrap(err, "result could not be converted to json")) - return "" - } - return string(js) -} diff --git a/pmu-checker/msr/testdata/dev/cpu/1/msr b/pmu-checker/msr/testdata/dev/cpu/1/msr deleted file mode 100644 index e69de29..0000000 diff --git a/pmu-checker/msr/validate.go b/pmu-checker/msr/validate.go deleted file mode 100644 index 00efdd2..0000000 --- a/pmu-checker/msr/validate.go +++ /dev/null @@ -1,25 +0,0 @@ -//########################################################################################################### -//# Copyright (C) 2021 Intel Corporation -//# SPDX-License-Identifier: BSD-3-Clause -//########################################################################################################### - -package msr - -import ( - "fmt" - "os" - - "github.com/pkg/errors" -) - -func validate(path string) error { - if _, err := os.Stat(path); err != nil { - return errors.Wrap(err, fmt.Sprintf("MSR modules aren't loaded at %s, please load them using modprobe msr command", path)) - } - return nil -} - -func ValidateMSRModule(cpu int) error { - msrDir := fmt.Sprintf(msrPath, cpu) - return validate(msrDir) -} diff --git a/pmu-checker/msr/validate_test.go b/pmu-checker/msr/validate_test.go deleted file mode 100644 index 066f49b..0000000 --- a/pmu-checker/msr/validate_test.go +++ /dev/null @@ -1,22 +0,0 @@ -//########################################################################################################### -//# Copyright (C) 2021 Intel Corporation -//# SPDX-License-Identifier: BSD-3-Clause -//########################################################################################################### - -package msr - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestValidate(t *testing.T) { - msrExists := "./testdata/dev/cpu/1/msr" - err := validate(msrExists) - require.NoError(t, err) - - noExistingMSR := "./testdata/dev/cpu/2/msr" - err = validate(noExistingMSR) - require.EqualError(t, err, "MSR modules aren't loaded at ./testdata/dev/cpu/2/msr, please load them using modprobe msr command: stat ./testdata/dev/cpu/2/msr: no such file or directory") -} diff --git a/pmu-checker/pmu-checker.go b/pmu-checker/pmu-checker.go new file mode 100644 index 0000000..54d4f2e --- /dev/null +++ b/pmu-checker/pmu-checker.go @@ -0,0 +1,311 @@ +package main + +import ( + "encoding/binary" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "sync" + "syscall" + "time" + + log "github.com/sirupsen/logrus" +) + +//globals +var msrValues = sync.Map{} +var msrRegs = []string{ + "0x309", + "0x30a", + "0x30b", + "0xc1", + "0xc2", + "0xc3", + "0xc4", +} + +var msrDel = []string{} +var CPU int + +type retMSR struct { + fd int +} +type Result struct { + Pmu_active_count int `json:"PMU(s)_active"` + Pmu_details map[string]string `json:"Details"` +} + +const msrPath = "/dev/cpu/%d/msr" + +func (dpt retMSR) Read(msr int64) (uint64, error) { + // Reads a given MSR on the respective CPU + + buf := make([]byte, 8) + rc, err := syscall.Pread(dpt.fd, buf, msr) + if err != nil { + log.Fatal(err) + panic(err) + } + + if rc != 8 { + log.Errorf("wrong byte count %d", rc) + return 0, fmt.Errorf("wrong byte count %d", rc) + } + + //assuming all x86 uses little endian format + msrVal := binary.LittleEndian.Uint64(buf) + log.Tracef("MSR %d was read successfully as %d", msr, msrVal) + return msrVal, err + +} + +func openMSRInterface(cpu int) (*retMSR, error) { + // Open connection to MSR Interface with given cpu + + msrDir := fmt.Sprintf(msrPath, cpu) + fd, err := syscall.Open(msrDir, syscall.O_RDONLY, 777) + if err != nil { + log.Errorf("Couln't open the msr interface, Error: ", err) + return nil, errors.New("couldn't open the msr interface") + } + + return &retMSR{fd: fd}, nil + +} + +func closeMSRInterface(dpt retMSR) { + // Close connection to MSR Interface + + syscall.Close(dpt.fd) +} + +func validateMSRModule(cpu int) { + + msrDir := fmt.Sprintf(msrPath, cpu) + if _, err := os.Stat(msrDir); os.IsNotExist(err) { + // if msr modules aren't loaded + + log.Panicf("MSR modules aren't loaded at %s, please load them using modprobe msr command\n", msrDir) + } + +} + +func readMSR(reg string, wg *sync.WaitGroup, thread int, cpu int) { + // Read MSR value, update map as needed + + defer wg.Done() + log.Debugf("Worker %d starting %s", thread, reg) + hexreg := strings.Replace(reg, "0x", "", -1) + hexreg = strings.Replace(hexreg, "0X", "", -1) + regInt64, err := strconv.ParseInt(hexreg, 16, 64) + if err != nil { + log.Panicf("The Hex to int64 type covertion failed\nError: ", err) + } + + msr, err := openMSRInterface(cpu) + if err != nil { + log.Panic(err) + } + + msrVal, err := msr.Read(regInt64) + if err != nil { + log.Panic(err) + } + + closeMSRInterface(*msr) + log.Debugf("New value of thread %d for %s is %d", thread, reg, msrVal) + currentVal, found := msrValues.Load(reg) + msrValues.Store(reg, msrVal) + + log.Debugf("Old value of thread %d for %s is %d", thread, reg, currentVal) + + if found && currentVal != uint64(0) && msrVal != currentVal { + // The key exists but value has changed, delete it + + msrDel = append(msrDel, reg) + log.Debugf("Deleting %s in the thread %d", reg, thread) + msrValues.Delete(reg) + + } + log.Debugf("Worker %d done for %s\n", thread, reg) + +} + +func validateLogFileName(file string) { + regexString := `([a-zA-Z0-9\s_\\.\-\(\):])+(.log|.txt)$` + reg, err := regexp.Compile(regexString) + if err != nil { + log.Fatal(err) + os.Exit(0) + } + + if reg.MatchString(file) { + return + } else { + log.Panic("The file name isn't valid for logging, The valid extensions are .log and .txt") + } + +} + +func showUsage() { + fmt.Println("pmu-checker needs to be run with sudo previlages") + fmt.Println("Usage:") + fmt.Println(" sudo ./pmu-checker [OPTION...]") + fmt.Println("Options:") + flag.PrintDefaults() + fmt.Println(" logfile[*.log], cpu[int], debug, no-stdout") + +} + +func init() { + //parse the commandline arguments + loglevel := flag.Bool("debug", false, "set the loglevel to debug, default is info") + multiLogWriter := flag.Bool("no-stdout", false, "set the logwriter to write to logfile only, default is false") + cpu := flag.Int("cpu", 0, "Read MSRs on respective CPU, default is 0") + logfile := flag.String("logfile", "pmu-checker.log", "set the logfile name, default is pmu-checker.log") + help := flag.Bool("help", false, "Shows the usage of pmu-checker application") + + flag.Parse() + + if *help { + showUsage() + os.Exit(0) + } + + //don't allow non-sudo runs + if os.Geteuid() != 0 { + log.Fatalf("You need root privileges to run pmu-checker, please run again with sudo") + os.Exit(0) + } + + validateLogFileName(*logfile) + + log.SetFormatter(&log.TextFormatter{ + ForceColors: true, + FullTimestamp: true, + DisableLevelTruncation: true, + }) + // programDir, err := filepath.Abs(filepath.Dir(os.Args[0])) + ex, err := os.Executable() + if err != nil { + log.Errorf("Failed to get absolute path of the program\nError:", err) + } + exPath := filepath.Dir(ex) + file, err := os.OpenFile(filepath.Join(exPath, *logfile), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatalf("Failed to open log file\nError:", err) + } + + //write to logfile and stdout at same time + mw := io.MultiWriter(os.Stdout, file) + + if *multiLogWriter { + log.SetOutput(file) + } else { + log.SetOutput(mw) + } + + log.SetLevel(log.InfoLevel) + if *loglevel { + log.SetLevel(log.DebugLevel) + } + + CPU = *cpu + +} + +func main() { + + log.Info("Starting the PMU Checker application...") + validateMSRModule(CPU) + + // initialize the map + for i := 0; i < len(msrRegs); i++ { + msrValues.Store(msrRegs[i], uint64(0)) + } + + var wg sync.WaitGroup + + for i := 1; i <= 6; i++ { + + if len(msrDel) == 7 { + // if all the PMUs are being used, break the loop + + log.Infof("Aborting iteration check #%d", i) + break + } + + log.Debugf("Iteration check #%d started\n", i) + msrValues.Range(func(key, value interface{}) bool { + _, ok := value.(uint64) + if !ok { + return false + } + + wg.Add(1) + go readMSR(key.(string), &wg, i, CPU) + return true + }) + + wg.Wait() + log.Infof("Iteration check #%d completed\n", i) + //intentional sleep + time.Sleep(time.Second) + + } + + res := new(Result) + + log.Infof(strings.Repeat("-", 12) + "All Iteration checks completed" + strings.Repeat("-", 12)) + res.Pmu_details = make(map[string]string) + if len(msrDel) == 0 { + log.Infof("None of the PMU(s) are actively being used\n") + res.Pmu_active_count = 0 + } + + if len(msrDel) > 0 { + + log.Info("Following PMU(s) are actively being used:") + for i := 0; i < len(msrDel); i++ { + pmu := msrDel[i] + switch pmu { + + case "0x309": + res.Pmu_details[pmu] = "instructions" + log.Infof("%s: might be using instructions", pmu) + case "0x30a": + res.Pmu_details[pmu] = "cpu_cycles" + log.Infof("%s: might be using cpu_cycles (check if nmi_watchdog is running)", pmu) + case "0x30b": + res.Pmu_details[pmu] = "ref_cycles" + log.Infof("%s: might be using ref_cycles", pmu) + case "0xc1", "0xc2", "0xc3", "0xc4": + res.Pmu_details[pmu] = "General_purpose_programmable_PMU" + log.Infof("%s: might be using general programmable PMU", pmu) + default: + // mustn't enter default case + log.Infof("Report this to the Developers") + os.Exit(0) + + } + + } + res.Pmu_active_count = len(msrDel) + } + + var js []byte + js, err := json.Marshal(res) + if err != nil { + log.Errorf("Result could not be converted to json\nError: ", err) + os.Exit(3) + } + fmt.Println(string(js)) + +} diff --git a/release_notes b/release_notes index 04fd920..bddeb5c 100644 --- a/release_notes +++ b/release_notes @@ -1,26 +1,24 @@ RELEASE NOTES PerfSpect -Support structure(Tested internally): +Xeon Micro-Architectures: +- Broadwell +- Skylake +- Cascadelake +- Icelake +- Sapphire Rapids +Operating Systems: +- Ubuntu 16.04 and newer +- centos 7 and newer +- Amazon Linux 2 +- RHEL 9 +- Debian 11 -| BigCores (server) | - -------------------------------------------------------------- -| Arch | OS | Kernel | -------------------------------------------------------------- -| Broadwell | >= Ubuntu 16.04 | >=4.15 | -| Broadwell | >= centos 7 | >=3.10 | -| Skylake | >= centos 7 | >=3.10 | -| Skylake | >= Ubuntu 16.04 | >=4.15 | -| Cascadelake | >= Ubuntu 16.04 | >=4.15 | -| Cascadelake | >= centos 7 | >=3.10 | -| Icelake | >= Ubuntu 16.04 | >=5.9 | -| Icelake | >= centos 7 | >=5.9 | -------------------------------------------------------------- - - -* v1.1.0 -PerfSpect supports BDX, SKX, CLX and ICX +* v1.2.0 +PerfSpect supports BDX, SKX, CLX, ICX and SPR * v1.1.1 Added support for AWS instances using cloud and cloudtype flag + +* v1.1.0 +PerfSpect supports BDX, SKX, CLX and ICX \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 966766c..d4ee382 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,11 @@ -simpleeval==0.9.11 -python-dateutil -xlsxwriter +black +flake8 +simpleeval +pandas plotly -kaleido -scipy -yattag psutil +pyinstaller +pytest +python-dateutil +XlsxWriter +yattag \ No newline at end of file diff --git a/security.md b/security.md new file mode 100644 index 0000000..f14ba1e --- /dev/null +++ b/security.md @@ -0,0 +1,5 @@ +# Security Policy +Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. + +## Reporting a Vulnerability +Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). \ No newline at end of file diff --git a/similarity-analyzer/README.md b/similarity-analyzer/README.md index 04abbfd..52d09ec 100644 --- a/similarity-analyzer/README.md +++ b/similarity-analyzer/README.md @@ -54,5 +54,3 @@ It also produces a combined CSV file comprising: * Kindly use "--debug" flag if you wish to log PCA components used for plotting. * Due to mathematical limitation with underlying PCA library, one can perform similarity analysis for 42 workload profiles at the same time. - -Authors: Karan Kamatgi, Harshad Sane diff --git a/similarity-analyzer/Reference/ICX/500.csv b/similarity-analyzer/Reference/ICX/500.csv index 0565e03..90afa61 100644 --- a/similarity-analyzer/Reference/ICX/500.csv +++ b/similarity-analyzer/Reference/ICX/500.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.004423982068965517,0.005649421999999999,0.001 metric_TMA_......L3_Hit_Latency(%),0.17783907310344826,0.6775015679999997,0.02283305,0.8332183 metric_TMA_......SQ_Full(%),0.13579130551724136,0.19015179399999993,0.05768861,0.22856939 metric_TMA_......MEM_Bandwidth(%),0.6546436393103446,2.581028779999998,0.0757437,3.4958891 -metric_TMA_......Mem_Latency(%),4.377492249310345,13.773500661999991,1.68238782,15.58695667 +metric_TMA_......MEM_Latency(%),4.377492249310345,13.773500661999991,1.68238782,15.58695667 metric_TMA_....Store_Bound(%),1.3670219672413795,5.152926585999998,0.5561879,6.8985576 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.06122536034482759,0.12383227,0.03009665,0.14975621 diff --git a/similarity-analyzer/Reference/ICX/502.csv b/similarity-analyzer/Reference/ICX/502.csv index 0e43c60..9175527 100644 --- a/similarity-analyzer/Reference/ICX/502.csv +++ b/similarity-analyzer/Reference/ICX/502.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.02604648827586207,0.04833127199999998,0.00974 metric_TMA_......L3_Hit_Latency(%),3.4066045358620687,3.915886632,2.35309752,6.34434381 metric_TMA_......SQ_Full(%),1.244770408965517,1.841267276,0.73991051,2.10920923 metric_TMA_......MEM_Bandwidth(%),30.542444001034486,36.07468290999999,13.23755847,37.14292175 -metric_TMA_......Mem_Latency(%),45.32318453551724,48.595298434,35.1794058,48.98968563 +metric_TMA_......MEM_Latency(%),45.32318453551724,48.595298434,35.1794058,48.98968563 metric_TMA_....Store_Bound(%),4.1001735551724146,6.646127149999994,3.08095423,15.31000799 metric_TMA_..Core_Bound(%),0.13471021,0.0,0.0,3.90659609 metric_TMA_....Divider(%),0.11673475758620691,0.18485510599999985,0.05909057,0.25240928 diff --git a/similarity-analyzer/Reference/ICX/505.csv b/similarity-analyzer/Reference/ICX/505.csv index b1b3ba6..53612ac 100644 --- a/similarity-analyzer/Reference/ICX/505.csv +++ b/similarity-analyzer/Reference/ICX/505.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.0021323586206896555,0.0026338259999999997,0.0 metric_TMA_......L3_Hit_Latency(%),13.804415213448275,16.110765256,6.66674729,16.86034053 metric_TMA_......SQ_Full(%),5.746837990344828,7.2978420559999995,3.35180605,8.07614195 metric_TMA_......MEM_Bandwidth(%),55.24995918344828,64.33875130999999,42.84893133,65.06153742 -metric_TMA_......Mem_Latency(%),23.334137649655176,24.257483082,21.21271285,24.33349113 +metric_TMA_......MEM_Latency(%),23.334137649655176,24.257483082,21.21271285,24.33349113 metric_TMA_....Store_Bound(%),2.714802168965517,4.465818585999999,0.94244553,4.72990458 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.07134564724137932,0.101767134,0.03375385,0.11868516 diff --git a/similarity-analyzer/Reference/ICX/520.csv b/similarity-analyzer/Reference/ICX/520.csv index 39c56cc..7d81411 100644 --- a/similarity-analyzer/Reference/ICX/520.csv +++ b/similarity-analyzer/Reference/ICX/520.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.001862514482758621,0.002404974,0.00144132,0.0 metric_TMA_......L3_Hit_Latency(%),3.4175575886206895,3.4458402579999996,3.32120746,3.4464033 metric_TMA_......SQ_Full(%),0.05413051275862069,0.05560140599999999,0.04956643,0.05597805 metric_TMA_......MEM_Bandwidth(%),21.75205649586207,21.830844182,21.41846617,21.84911648 -metric_TMA_......Mem_Latency(%),58.97937940344828,59.1235348,58.80365823,59.48694834 +metric_TMA_......MEM_Latency(%),58.97937940344828,59.1235348,58.80365823,59.48694834 metric_TMA_....Store_Bound(%),10.64102896586207,10.838106419999999,10.14949775,10.87884375 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.005735969999999998,0.005781396,0.0057065,0.00579553 diff --git a/similarity-analyzer/Reference/ICX/523.csv b/similarity-analyzer/Reference/ICX/523.csv index e8f3570..044ec7d 100644 --- a/similarity-analyzer/Reference/ICX/523.csv +++ b/similarity-analyzer/Reference/ICX/523.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.004410422105263157,0.005510369999999999,0.003 metric_TMA_......L3_Hit_Latency(%),0.8846972984210526,0.948886217,0.79388984,0.96475472 metric_TMA_......SQ_Full(%),0.3537605010526316,0.378839263,0.32216732,0.38193817 metric_TMA_......MEM_Bandwidth(%),2.1597949236842107,2.639450936,1.9670796,2.84048831 -metric_TMA_......Mem_Latency(%),13.538248633684208,14.074509026999998,12.93828235,15.2470623 +metric_TMA_......MEM_Latency(%),13.538248633684208,14.074509026999998,12.93828235,15.2470623 metric_TMA_....Store_Bound(%),1.5689664915789472,1.611220796,1.51165253,1.61138996 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.04397925578947368,0.1211737639999998,0.0218285,0.2034671 diff --git a/similarity-analyzer/Reference/ICX/525.csv b/similarity-analyzer/Reference/ICX/525.csv index 67d8a86..8ff6ae5 100644 --- a/similarity-analyzer/Reference/ICX/525.csv +++ b/similarity-analyzer/Reference/ICX/525.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.002560914736842105,0.00391123,0.00160419,0.00 metric_TMA_......L3_Hit_Latency(%),0.41934530368421047,0.5256358649999999,0.33197021,0.5427954 metric_TMA_......SQ_Full(%),0.18615972578947368,0.24927978499999998,0.15936838,0.25991099 metric_TMA_......MEM_Bandwidth(%),2.50889254,2.7365203379999996,2.22620022,2.80423884 -metric_TMA_......Mem_Latency(%),8.343931622631578,9.064821177999999,7.5212437,9.09604882 +metric_TMA_......MEM_Latency(%),8.343931622631578,9.064821177999999,7.5212437,9.09604882 metric_TMA_....Store_Bound(%),0.8195709184210527,1.1313420639999998,0.71449247,1.18916863 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.08459899368421052,0.10682484,0.06760873,0.10702203 diff --git a/similarity-analyzer/Reference/ICX/531.csv b/similarity-analyzer/Reference/ICX/531.csv index a552397..582c01c 100644 --- a/similarity-analyzer/Reference/ICX/531.csv +++ b/similarity-analyzer/Reference/ICX/531.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.000991595,0.001294612,0.0006534,0.00164725 metric_TMA_......L3_Hit_Latency(%),0.101113219,0.117515922,0.08245768,0.12270369 metric_TMA_......SQ_Full(%),0.040799977,0.180390682,0.01019106,0.21058348 metric_TMA_......MEM_Bandwidth(%),0.104621263,0.399264732,0.02614526,0.58967582 -metric_TMA_......Mem_Latency(%),11.07007529,14.12880043,8.08128263,14.26036389 +metric_TMA_......MEM_Latency(%),11.07007529,14.12880043,8.08128263,14.26036389 metric_TMA_....Store_Bound(%),0.242391785,1.062604758,0.03371587,1.26593562 metric_TMA_..Core_Bound(%),0,0,0,0 metric_TMA_....Divider(%),0.467286231,0.499833534,0.40820648,0.5076326 diff --git a/similarity-analyzer/Reference/ICX/541.csv b/similarity-analyzer/Reference/ICX/541.csv index 3153c6d..77b5f2b 100644 --- a/similarity-analyzer/Reference/ICX/541.csv +++ b/similarity-analyzer/Reference/ICX/541.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.0024918885,0.0029596615000000003,0.00193797,0 metric_TMA_......L3_Hit_Latency(%),0.6816311425,0.9247876685,0.2599712,1.10517383 metric_TMA_......SQ_Full(%),0.22741931600000004,0.2829461435,0.13929831,0.37201973 metric_TMA_......MEM_Bandwidth(%),0.6157485285000001,0.8310658635,0.33944481,0.89255221 -metric_TMA_......Mem_Latency(%),2.7987915935000003,3.2468984885000003,2.13469038,3.36966848 +metric_TMA_......MEM_Latency(%),2.7987915935000003,3.2468984885000003,2.13469038,3.36966848 metric_TMA_....Store_Bound(%),0.1899499405,0.2417374145,0.12135474,0.25696144 metric_TMA_..Core_Bound(%),0.0163698,0.016369800000000233,0.0,0.327396 metric_TMA_....Divider(%),1.1819574750000001,1.2611423765,1.09573466,1.33649384 diff --git a/similarity-analyzer/Reference/ICX/548.csv b/similarity-analyzer/Reference/ICX/548.csv index 422e876..80d784e 100644 --- a/similarity-analyzer/Reference/ICX/548.csv +++ b/similarity-analyzer/Reference/ICX/548.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.0013530644827586206,0.0017697239999999999,0.0 metric_TMA_......L3_Hit_Latency(%),0.009592303793103451,0.019207871999999987,0.0077605,0.02532769 metric_TMA_......SQ_Full(%),0.00031174103448275864,0.0006027259999999999,0.0001749,0.00066019 metric_TMA_......MEM_Bandwidth(%),22.582366972413794,26.69991116,15.82866801,27.8154729 -metric_TMA_......Mem_Latency(%),34.41457712275862,35.646741385999995,32.75673371,37.24999391 +metric_TMA_......MEM_Latency(%),34.41457712275862,35.646741385999995,32.75673371,37.24999391 metric_TMA_....Store_Bound(%),0.5726192951724137,0.88938586,0.42424125,0.98933402 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.009902247241379312,0.021105585999999992,0.00612324,0.0262728 diff --git a/similarity-analyzer/Reference/ICX/557.csv b/similarity-analyzer/Reference/ICX/557.csv index c244cc5..2904284 100644 --- a/similarity-analyzer/Reference/ICX/557.csv +++ b/similarity-analyzer/Reference/ICX/557.csv @@ -54,7 +54,7 @@ metric_TMA_......Data_Sharing(%),0.0011725957894736846,0.0016735469999999996,0.0 metric_TMA_......L3_Hit_Latency(%),4.03971422,5.192479599,2.61800292,5.23195458 metric_TMA_......SQ_Full(%),0.05883785473684212,0.06699107699999998,0.04567624,0.07047999 metric_TMA_......MEM_Bandwidth(%),2.441540811052632,2.592008976,2.27400335,2.64467838 -metric_TMA_......Mem_Latency(%),35.57819142368421,36.383872172,34.86833026,36.4072874 +metric_TMA_......MEM_Latency(%),35.57819142368421,36.383872172,34.86833026,36.4072874 metric_TMA_....Store_Bound(%),0.4187662873684211,0.490900434,0.31080914,0.51047268 metric_TMA_..Core_Bound(%),0.0,0.0,0.0,0.0 metric_TMA_....Divider(%),0.005642168947368421,0.0056625059999999994,0.00561741,0.00567732 diff --git a/similarity-analyzer/SimilarityAnalysis.xlsx b/similarity-analyzer/SimilarityAnalysis.xlsx deleted file mode 100644 index c6a9f35ac1c3323cdb5e9c24d4b28b4a2682d4c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100376 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 diff --git a/similarity-analyzer/_version.txt b/similarity-analyzer/_version.txt deleted file mode 100644 index 1cc5f65..0000000 --- a/similarity-analyzer/_version.txt +++ /dev/null @@ -1 +0,0 @@ -1.1.0 \ No newline at end of file diff --git a/similarity-analyzer/data_formatter/README.md b/similarity-analyzer/data_formatter/README.md new file mode 100644 index 0000000..93ad61e --- /dev/null +++ b/similarity-analyzer/data_formatter/README.md @@ -0,0 +1,13 @@ +# data_formatter + +Formats csv/xlsx data into json and helps comparing metrics from multiple csv's. + +## Example usage + +1. To compare TMA and basic metrics + +`python3 main.py -f "summary_resnet.xlsx,summary_specjbb.xlsx" -m d ` + +2. To compare all compatible metrics + +`python3 main.py -f "summary_resnet.xlsx,summary_specjbb.xlsx" -o comp1.csv` diff --git a/similarity-analyzer/data_formatter/main.py b/similarity-analyzer/data_formatter/main.py index a61f55d..e545562 100644 --- a/similarity-analyzer/data_formatter/main.py +++ b/similarity-analyzer/data_formatter/main.py @@ -1,18 +1,20 @@ #!/usr/bin/env python3 ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### +import xlrd import csv import os -import sys -import xlrd def invalid_filename(filename): - if not filename.endswith(".csv"): + # check if filename ends with .csv + if filename.endswith(".csv"): + return 0 + else: raise SystemError(f"{filename} isn't a csv format!") @@ -21,7 +23,9 @@ def excel2json(filename, sheet="system view"): try: if os.access(filename, os.R_OK): wb = xlrd.open_workbook(filename) + # sh = wb.sheet_by_index(sheet) sh = wb.sheet_by_name(sheet) + for rownum in range(1, sh.nrows): row_values = sh.row_values(rownum) metric = row_values[0] @@ -29,30 +33,31 @@ def excel2json(filename, sheet="system view"): data[metric] = val else: raise SystemExit(f"{filename} not accessible") - except xlrd.XLRDError as e: + except Exception as e: print(e) - sys.exit(1) + exit() return data -def csv2json(filename): +def csv2json(filename, args): data = {} try: if os.access(filename, os.R_OK): - with open(filename, encoding="utf-8") as csvf: - csvReader = csv.DictReader(csvf) - for rows in csvReader: - key = rows["metrics"] - data[key] = float(rows["avg"]) + if args.perfspect: + with open(filename, encoding="utf-8") as csvf: + csvReader = csv.DictReader(csvf) + for rows in csvReader: + key = rows["metrics"] + data[key] = float(rows["avg"]) else: raise SystemExit(f"{filename} not accessible") - except KeyError as invalid_csv_key: - raise SystemExit() from invalid_csv_key + except KeyError as e: + raise SystemError(e) return data def compare_metrics(metriclist, datalist, fields, out): - if not metriclist: + if not len(metriclist): metriclist = [ m for m in datalist[0].keys() @@ -96,7 +101,8 @@ def compare_metrics_all(datalist, fields, out): allkeys = [] for data in datalist: for k in data: - allkeys.append(k) + if k.startswith("metric_"): + allkeys.append(k) uniquekeys = list(set(allkeys)) for metric in uniquekeys: rowlines = [] @@ -154,8 +160,8 @@ def main(): jsondata = [] fields = ["Metric"] for filename in filenames: - if args.perfspect: - jsondata.append(csv2json(filename)) + if args.perfspect or filenames[0].endswith(".csv"): + jsondata.append(csv2json(filename, args)) else: jsondata.append(excel2json(filename)) fields.append(filename) diff --git a/similarity-analyzer/data_formatter/requirements.txt b/similarity-analyzer/data_formatter/requirements.txt new file mode 100644 index 0000000..75a0ba0 --- /dev/null +++ b/similarity-analyzer/data_formatter/requirements.txt @@ -0,0 +1,2 @@ +xlrd +simplejson diff --git a/similarity-analyzer/dopca.py b/similarity-analyzer/dopca.py index d638185..138463b 100644 --- a/similarity-analyzer/dopca.py +++ b/similarity-analyzer/dopca.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2021-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### @@ -14,56 +14,31 @@ import numpy as np -def verify_args(args): - if not args.files: +def verify_args(arg): + if not arg.files: parser.print_help() logger.error("files is a required field") - sys.exit(1) + exit(1) basepath = os.getcwd() - outfilecsv = os.path.join(basepath, args.out + ".csv") + outfilecsv = os.path.join(basepath, arg.out + ".csv") + if os.path.exists(outfilecsv): logger.warning(f"The {outfilecsv} exists already!") - sys.exit(1) - if args.march and args.march not in ("CLX", "ICX"): - logger.warning(f"The current released version doesn't support {args.march}") - parser.print_help() - sys.exit(1) - try: - files = args.files.split(",") - if "" in files: - logger.error("File name cannot be null/empty string") - sys.exit(1) - component_size = len(files) - if component_size in (0, 1) and not args.march: - logger.error( - f"The number of components requested is {component_size}, a minimum of 2 is required..." - ) - raise Exception - except Exception as invalid_comp_size: - raise SystemExit( - 'Minimum of 2 input files required and must contain "," delimiter between them' - ) from invalid_comp_size - if args.label: - if "" in args.label: - logger.error("label cannot be null/empty string") - parser.print_help() - sys.exit(1) - if component_size != len(args.label): - logger.warning(f"The size of labels {args.label} don't match with input files {args.files}") - parser.print_help() - sys.exit(1) - return component_size + raise SystemExit() + def get_version(): basepath = os.getcwd() version_file = os.path.join(basepath, "_version.txt") if os.access(version_file, os.R_OK): - with open(version_file) as vfile: - version = vfile.readline() + with open(version_file) as f: + version = f.readline() else: raise SystemError("version file isn't accessible") + return version + def setup_custom_logger(name, debug): formatter = logging.Formatter( fmt="%(asctime)s %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S" @@ -72,76 +47,35 @@ def setup_custom_logger(name, debug): handler.setFormatter(formatter) screen_handler = logging.StreamHandler(stream=sys.stdout) screen_handler.setFormatter(formatter) - custom_logger = logging.getLogger(name) + logger = logging.getLogger(name) if debug: - custom_logger.setLevel(logging.DEBUG) - else: - custom_logger.setLevel(logging.INFO) - custom_logger.addHandler(handler) - custom_logger.addHandler(screen_handler) - return custom_logger - -def handle_nan(data, comp_size): - logger.debug("Checking for NaN in telemetry input files") - df = pd.DataFrame(data) - deleted_workload_profiles = [] - if not df.isnull().values.any(): - logger.debug("No NaN found in telemetry input files") + logger.setLevel(logging.DEBUG) else: - logger.warning("NaN found in the input telemetry files, attempting to fix them") - df_thresh_nan = df.dropna(thresh=0.8*len(df.columns)) - diff_df = pd.merge(df, df_thresh_nan, how='outer', indicator='Exist') - diff_df = diff_df.loc[diff_df['Exist'] != 'both'] - deleted_row_indices = diff_df.index.tolist() - if deleted_row_indices: - if len(deleted_row_indices) in (comp_size, comp_size-1): - #too many workload profiles have NaN greater than threshold, must quit similarity analysis - logger.error("Attempted dropping of NaNs resulted in fewer #input profiles without NaN....quiting similarity analysis") - sys.exit(1) - logger.warning("The following input files contain NaN and will no longer be considered for similarity analysis") - inp_files = args.files.split(",") - for row in deleted_row_indices: - for index, filename in enumerate(inp_files): - if row == index: - comp_size = comp_size - 1 - logger.warning(f"{filename}") - if args.label: - deleted_workload_profiles.append(args.label[index]) - else: - deleted_workload_profiles.append(filename) - df = data = df_thresh_nan - if df.isnull().values.any(): - logger.debug(f"A total of {df.isnull().sum().sum()} NaN found in your telemetry files and these will be replaced with large negative number") - data = df.fillna(-99999) - return data, df.shape[0], deleted_workload_profiles + logger.setLevel(logging.INFO) + logger.addHandler(handler) + logger.addHandler(screen_handler) + return logger + def dopca(dataset, colnames, n_components, cols): # lazy loading from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA + logger.info("starting PCA") - # cleaning and separating dimensions + # Preprocessing and separating dimensions logger.debug(f"deleting colnames {colnames[0]}") del colnames[0] num_val = dataset.loc[:, colnames].values - num_val, n_components, del_rows = handle_nan(num_val, n_components) - if del_rows: - for profiles in del_rows: - try: - cols.remove(profiles) - except ValueError as e: - logger.error(e) - sys.exit(1) - # normalizing the metrics + + # Normalizing the metrics num_val = StandardScaler().fit_transform(num_val) logger.debug(f"Post normalizing metrics, num_val: {num_val}") - # PCA analysis, Create PCA model - #pca = PCA(n_components=n_components) #Limitation: If the n_components(no of workloads) are greater than num_val(the number of features), it will throw error. - n_components = len(num_val[0]) #Solution: To scale it for any number of workloads, generate PCAs equivalent to number of features/performance matrics (instead of number of workloads) that we have for each workload. + # PCA analysis, Create PCA model pca = PCA(n_components=n_components) - # transform + # Fit transform function principal_components = pca.fit_transform(num_val) principal_df = pd.DataFrame( data=principal_components, @@ -149,18 +83,47 @@ def dopca(dataset, colnames, n_components, cols): ) logger.debug(f"explained variance ratio: {pca.explained_variance_ratio_}") metric_df = pd.DataFrame(cols, columns=["Metric"]) - # concatenating the dataframe along axis = 1 + + # Concatenating the dataframe along axis = 1 final_dataframe = pd.concat([principal_df, metric_df], axis=1) logger.debug(f"principalDF:\n\n {principal_df}") logger.debug(f"finalDF:\n\n {final_dataframe}") + + # Get benchmarks = TMA(max) values from raw data + df1 = dataset[["metric_TMA_Frontend_Bound(%)", "metric_TMA_Backend_Bound(%)"]] + df1 = df1 / df1.max() + # print(df1) + fe_max = df1.loc[df1["metric_TMA_Frontend_Bound(%)"] == 1].index.tolist()[0] + be_max = df1.loc[df1["metric_TMA_Backend_Bound(%)"] == 1].index.tolist()[0] + # print(fe_max,be_max) + # Get benchmarks = PC(max) values from PCA data + # print(final_dataframe) + pc1_max = final_dataframe.iloc[final_dataframe["PC1"].idxmax()]["Metric"] + pc2_max = final_dataframe.iloc[final_dataframe["PC2"].idxmax()]["Metric"] + # Compare benchmarks from TMA (max) and PC(max) + if (fe_max == pc1_max) and (be_max == pc2_max): + # print(fe_max, "is along +ve X axis") + # print(be_max, "is along +ve Y axis") + xlabel = "FrontEnd Bound -->" + ylabel = "BackEnd Bound -->" + elif (be_max == pc1_max) and (fe_max == pc2_max): + # print(be_max, "is along +ve X axis") + # print(fe_max, "is along +ve Y axis") + xlabel = "BackEnd Bound -->" + ylabel = "FrontEnd Bound -->" + else: + # print("Axis max's not found") + xlabel = "PC1" + ylabel = "PC2" logger.info("PCA completed") - return final_dataframe + return final_dataframe, xlabel, ylabel -# plot along PCs -def plotpca(rownames, dataframe): + +# Plotting along PCs +def plotpca(rownames, dataframe, xlabel, ylabel): # lazy loading from matplotlib import pyplot as plt - from matplotlib import cm as colmgr + from matplotlib import cm as cm logger.info("PCA plot initiated") fig = plt.figure(figsize=(8, 8)) @@ -168,22 +131,27 @@ def plotpca(rownames, dataframe): plot.set_xlabel("Principal Component 1", fontsize=15) plot.set_ylabel("Principal Component 2", fontsize=15) plot.set_title("Similarity Analyzer", fontsize=20) + xs = np.arange(len(rownames)) ys = [i + xs + (i * xs) ** 2 for i in range(len(rownames))] - colors = colmgr.rainbow(np.linspace(0, 1, len(ys))) + colors = cm.rainbow(np.linspace(0, 1, len(ys))) for target, color in zip(rownames, colors): - indices_to_keep = dataframe["Metric"] == target - pc1 = dataframe.loc[indices_to_keep, "PC1"] - pc2 = dataframe.loc[indices_to_keep, "PC2"] + indicesToKeep = dataframe["Metric"] == target + pc1 = dataframe.loc[indicesToKeep, "PC1"] + pc2 = dataframe.loc[indicesToKeep, "PC2"] plot.scatter(pc1, pc2, c=color.reshape(1, -1), s=50) + # plot.text(pc1 - 0.03, pc2 - 0.03, target, fontsize=9) plot.annotate(target, (pc1, pc2)) - plt.xlabel("PC1", fontsize=8) - plt.ylabel("PC2", fontsize=8) + # plt.xlabel("PC1", fontsize=8) + # plt.ylabel("PC2", fontsize=8) + plt.xlabel(xlabel, fontsize=12, labelpad=10) + plt.ylabel(ylabel, fontsize=12, labelpad=10) plot.grid() plt.savefig(outfile) logger.info(f"PCA plot saved at {outfile}") +# To Do: plot PC weights and variation along other PCs if __name__ == "__main__": parser = ArgumentParser(description="Similarity Analyzer") required_arg = parser.add_argument_group("required arguments") @@ -209,33 +177,44 @@ def plotpca(rownames, dataframe): parser.add_argument( "-m", "--march", - help="plot pca against reference SPECcpu2017 (int_rate) components based on architecture specified. Expected values: ICX/CLX", - ) - parser.add_argument( - "-l", - "--label", - type=str, - help='label each workload profiles which will be used to plot for similarity analysis; This must map to corresponding input files delimited by ","', + help="plot pca against reference SPECcpu2017 (int_rate) components based on architecture specified", ) + args = parser.parse_args() + logger = setup_custom_logger("similarity_analyzer", args.debug) if args.version: print(get_version()) sys.exit(0) - if args.label: - args.label = args.label.split(",") - logger.info(f"starting similarity analyzer {get_version()}") - comp_size = verify_args(args) + logger.info("starting similarity analyzer " + get_version()) + verify_args(args) + if args.march: import glob + if args.postprocessType == "perfspect": spec_profiles = glob.glob("Reference/" + args.march + "/*.csv") else: - logger.error("Similarity Analyzer supports perfspect telemetry data only") - sys.exit(1) + spec_profiles = glob.glob("Reference/" + args.march + "/*.xlsx") + for spec in spec_profiles: args.files += "," + spec - logger.debug(f"The files being compared are: {args.files}") + logger.debug("The files being compared are: " + args.files) + + try: + component_size = len(args.files.split(",")) + if component_size == 0 or component_size == 1: + logger.error( + f"The number of components requested is {component_size}, a minimum of 2 is required... Exiting" + ) + raise Exception + except Exception as e: + print(e) + raise SystemExit( + 'Minimum of 2 input files required and must contain "," delimiter between them' + ) + + # Integrated data_formatter cmd = [] cmd.append("python3") cmd.append("data_formatter/main.py") @@ -247,40 +226,35 @@ def plotpca(rownames, dataframe): cmd.append(args.out + ".csv") if args.postprocessType == "perfspect": cmd.append("-p") + logger.debug(f"The command used by data formatter: {cmd}") logger.info( f"Initiating data_formatter with {args.postprocessType} pmu postprocessor" ) - with subprocess.Popen( # nosec + process = subprocess.Popen( # nosec cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) as process: - out, err = process.communicate() - if err: - logger.error(err.decode()) - sys.exit(1) - if "Data compared and stored" in str(out): - logger.info(f"data formatter collated pmu metrics at {args.out}.csv file") - else: - logger.error( - "data formatter wasn't able to collate all the pmu metrics from input files" - ) + ) + out, err = process.communicate() + if err: + logger.error(err.decode()) + exit(1) + if "Data compared and stored" in str(out): + logger.info(f"data formatter collated pmu metrics at {args.out}.csv file") + else: + logger.error( + "data formatter wasn't able to collate all the pmu metrics from input files" + ) + + # Ingest data_formatted output into pandas for PCA data = args.out + ".csv" outfile = args.out + ".png" pd_data = pd.read_csv(data) pd_data = pd_data.rename( columns={i: i[14:] for i in pd_data.columns if i.startswith("Reference")} ) - if not args.label and args.postprocessType == "perfspect": - pd_data = pd_data.rename( - columns={i: i[:-4] for i in pd_data.columns if i != "Metric"} - ) - elif args.label and args.postprocessType == "perfspect": - pd_data = pd_data.rename( - columns={i: str(j) for i,j in zip(pd_data.drop(columns="Metric").columns, args.label)} - ) - else: - logger.error("Similarity Analyzer supports perfspect telemetry data only") - sys.exit(1) + pd_data = pd_data.rename( + columns={i: i[:-4] for i in pd_data.columns if i != "Metric"} + ) logger.debug(f"dataset before transpose:\n {pd_data}") columns = list(pd_data.columns) columns.remove("Metric") @@ -289,6 +263,6 @@ def plotpca(rownames, dataframe): pd_data.insert(loc=0, column="metric", value=columns) logger.debug(f"dataset post transpose:\n {pd_data}") column_names = pd_data.columns.tolist() - final_df = dopca(pd_data, column_names, comp_size, columns) - row_names = final_df["Metric"].values - plotpca(row_names, final_df) \ No newline at end of file + row_names = pd_data.iloc[:, 0].values.tolist() + final_df, xlabel, ylabel = dopca(pd_data, column_names, component_size, columns) + plotpca(row_names, final_df, xlabel, ylabel) diff --git a/similarity-analyzer/requirements.txt b/similarity-analyzer/requirements.txt index 66174e4..2a2e4d5 100644 --- a/similarity-analyzer/requirements.txt +++ b/similarity-analyzer/requirements.txt @@ -1,6 +1,5 @@ -numpy>=1.16 -matplotlib==3.3.4 -pandas==1.1.5 -scikit_learn==0.24.2 -simplejson==3.17.2 -xlrd==1.2.0 +matplotlib +pandas +scikit_learn +simplejson +xlrd diff --git a/src/__init__.py b/src/__init__.py index e43414e..74c46d3 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,5 +1,5 @@ ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### diff --git a/src/basic_stats.py b/src/basic_stats.py index 771f7fb..c80b7cb 100644 --- a/src/basic_stats.py +++ b/src/basic_stats.py @@ -1,3 +1,8 @@ +########################################################################################################### +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + import plotly.graph_objects as go import plotly import pandas as pd diff --git a/src/calibrate.c b/src/calibrate.c index 035faf9..e4de248 100644 --- a/src/calibrate.c +++ b/src/calibrate.c @@ -1,6 +1,6 @@ -//########################################################################################################### -//# Copyright (C) 2021 Intel Corporation -//# SPDX-License-Identifier: BSD-3-Clause +//########################################################################################################## +// Copyright (C) 2020-2023 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause //########################################################################################################### #include @@ -38,4 +38,3 @@ unsigned Calibrate(void){ } - diff --git a/src/icicle.py b/src/icicle.py index a6345da..3d43060 100644 --- a/src/icicle.py +++ b/src/icicle.py @@ -1,11 +1,13 @@ +########################################################################################################### +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + from yattag import Doc import plotly.graph_objects as go import pandas as pd import numpy as np -import warnings -from pandas.core.common import SettingWithCopyWarning -warnings.simplefilter(action="ignore", category=SettingWithCopyWarning) doc, tag, text = Doc().tagtext() metric_parent = {} diff --git a/src/perf_helpers.py b/src/perf_helpers.py index a802928..97ddb76 100644 --- a/src/perf_helpers.py +++ b/src/perf_helpers.py @@ -1,5 +1,7 @@ +#!/usr/bin/env python + ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### @@ -178,7 +180,6 @@ def parse_hex(s): # detect if PMU counters are in use def pmu_contention_detect(iterations=6): - interval = 10 msrregs = ["0x309", "0x30a", "0x30b", "0xc1", "0xc2", "0xc3", "0xc4"] values = [0] * len(msrregs) @@ -272,14 +273,7 @@ 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 +# Check if arch is broadwell/skyalke/cascadelake/icelake def check_architecture(procinfo): try: model = int(procinfo[0]["model"].strip()) @@ -304,11 +298,19 @@ def check_architecture(procinfo): arch = "broadwell" elif model == 106 and cpufamily == 6 and stepping >= 4: arch = "icelake" + elif model == 143 and cpufamily == 6 and stepping >= 3: + arch = "sapphirerapids" else: - arch = "unknown" - not_suported() + print( + "Current architecture not supported!\nThis version only suports Broadwell/Skylake/Cascadelake/Icelake/SPR architectures. Exiting!" + ) + sys.exit() + else: - not_suported() + print( + "Current architecture not supported!\nThis version only suports Broadwell/Skylake/Cascadelake/Icelake/SapphireRapids . Exiting!" + ) + sys.exit() return arch, modelname @@ -323,7 +325,6 @@ def get_cpuid_info(procinfo): if vendor == "GenuineIntel": key = proc["physical id"] else: - # assuming single socket (ARM) key = 0 val = proc["processor"] if socketinfo.get(key) is None: @@ -340,7 +341,7 @@ def validate_outfile(filename, xlsx=False): outfile = os.path.basename(filename) if resdir and not os.path.exists(resdir): return False - regx = r"[@!#$%^&*()<>?/\|}{~:]" + regx = r"[@!#$%^&*()<>?\|}{~:]" # regex = re.compile("[@!#$%^&*()<>?/\|}{~:]") regex = re.compile(regx) if regex.search(outfile) is None: diff --git a/src/prepare_perf_events.py b/src/prepare_perf_events.py index 8b31bf2..1edf4d9 100644 --- a/src/prepare_perf_events.py +++ b/src/prepare_perf_events.py @@ -1,5 +1,7 @@ +#!/usr/bin/env python + ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### @@ -58,51 +60,11 @@ def check_cpu_event(line): line = line.strip() tmp_list = line.split("/") # assumes event name without a PMU qualifier is a core event - if len(tmp_list) == 1 or tmp_list[0] == "cpu": + if len(tmp_list) == 1 or tmp_list[0] == "cpu" or tmp_list[0].startswith("cstate"): return True return False -# expand event names - deprecated -def expand_event_name(line, grouping): - line = line.strip() - loop_imc = False - loop_cha = False - loop_cbox = False - loop_upi = False - if grouping and line.startswith("imc"): - line = line.replace("imc", "uncore_imc_0") - if "name=" in line: - name = (line.split("'"))[1] - line = line.replace(name, name + ".0") - loop_imc = True - if grouping and line.startswith("cha"): - if "name=" in line: - name = (line.split("'"))[1] - line = line.replace(name, name + ".0") - line = line.replace("cha", "uncore_cha_0") - loop_cha = True - if grouping and line.startswith("cbox"): - if "name=" in line: - name = (line.split("'"))[1] - line = line.replace(name, name + ".0") - line = line.replace("cbox", "uncore_cbox_0") - loop_cbox = True - if grouping and line.startswith("upi"): - if "name=" in line: - name = (line.split("'"))[1] - line = line.replace(name, name + ".0") - line = line.replace("upi", "uncore_upi_0") - loop_upi = True - if grouping and line.startswith("qpi"): - if "name=" in line: - name = (line.split("'"))[1] - line = line.replace(name, name + ".0") - line = line.replace("qpi", "uncore_qpi_0") - loop_upi = True - return (line, loop_imc, loop_cha, loop_cbox, loop_upi) - - # save the last group names in a list when it is cha or imc # test for cha or imc event. append with count value # once reaches new group, start looping through all imc/cha counts to finish up @@ -131,11 +93,6 @@ def enumerate_uncore(group, pattern, n, default_range=True): return uncore_group -# get number of cgroups in the list -def get_num_cgroups(cgroups): - return len(cgroups.split(",")) - - # fix events that aren't compatible with older kernels def fix_events_for_older_kernels(eventfile, kernel_version): reg = r".*?\-(\d*)(\.|\-).*" @@ -186,7 +143,6 @@ def get_cgroup_events_format(cgroups, events, num_events): def prepare_perf_events(event_file, grouping, cpu_only): - if not os.path.isfile(event_file): raise SystemExit("event file not found") @@ -210,6 +166,10 @@ def prepare_perf_events(event_file, grouping, cpu_only): ) except FileNotFoundError: raise SystemExit("perf not found; please install linux perf utility") + + except subprocess.CalledProcessError: + raise SystemExit("perf not found; please install linux perf utility") + unsupported_events = [] for line in fin: if (line != "\n") and (not line.startswith("#")): @@ -223,9 +183,13 @@ def prepare_perf_events(event_file, grouping, cpu_only): collection_events[-1] = end_event[:-1] + ";" else: collection_events.append(line) + if any("cpu-cycles" in event for event in unsupported_events): + raise SystemExit( + "Fixed counters not supported, unable to collect PMUs on this platform" + ) if len(unsupported_events) > 0: print( - "These events are not supported with current version of perf, will not be collected!" + "The following events are not supported with current version of perf, will not be collected!" ) for e in unsupported_events: print("%s" % e) @@ -238,11 +202,22 @@ def prepare_perf_events(event_file, grouping, cpu_only): templist.append(e) collection_events = templist + core_event = [] + uncore_event = [] event_names = [] for line in collection_events: - event = line + ":c" if check_cpu_event(line) else line + ":u" + if cpu_only: + if check_cpu_event(line): + event = line + ":c" + core_event.append(event) + else: + if check_cpu_event(line): + event = line + ":c" + core_event.append(event) + else: + event = line + ":u" + uncore_event.append(event) event_names.append(event) - # line, loop_imc, loop_cha, loop_cbox, loop_upi = expand_event_name(line, grouping) line, unc_count = expand_unc(line, grouping) if new_group: group += start_group @@ -264,5 +239,11 @@ def prepare_perf_events(event_file, grouping, cpu_only): fin.close() group = group[:-1] - + if len(event_names) == 0: + raise SystemExit("No supported events found on this platform.") + # being conservative not letting the collection to proceed if fixed counters aren't suported on the platform + if len(unsupported_events) >= len(core_event): + raise SystemExit( + "Most core counters aren't supported on this platform, unable to collect PMUs" + ) return group, event_names diff --git a/src/report.py b/src/report.py index 005cb04..103426a 100644 --- a/src/report.py +++ b/src/report.py @@ -1,3 +1,8 @@ +########################################################################################################### +# Copyright (C) 2020-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + from src import basic_stats from src import icicle import os @@ -51,8 +56,6 @@ def write_html(res_dir, base_input_file, arch, html_report_out, type="both"): doc.stag("br") doc.asis(fig2) result = indent(doc.getvalue()) - if "/" in html_report_out: - html_report_out = html_report_out.rpartition("/")[-1] out_html = os.path.join(res_dir, html_report_out) with open(out_html, "w") as file: file.write(result) diff --git a/test/test_application.py b/test/test_application.py old mode 100755 new mode 100644 index 52d3b87..8a13aae --- a/test/test_application.py +++ b/test/test_application.py @@ -1,30 +1,32 @@ - -#! /usr/bin/python - +#!/usr/bin/env python3 ########################################################################################################### -# Copyright (C) 2021 Intel Corporation +# Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### import os import re +import sys +import glob import tarfile +import pytest import subprocess #nosec def _run_command(command, cwd=""): - proc = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=cwd) #nosec + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd) #nosec try: stdout, stderr = proc.communicate(timeout=10) except: proc.kill() stdout, stderr = proc.communicate() retcode = proc.returncode + return stdout, stderr, retcode def _test_run_regex( - command, expected_retcode, capsys, stdout_regex=None, stderr_regex=None, regex_flags=0, cwd="." + command, expected_retcode, capsys, stdout_regex=None, stderr_regex=None, regex_flags=0, cwd="perfspect" ): stdout, stderr, retcode = _run_command(command, cwd) @@ -39,44 +41,47 @@ def _test_run_regex( assert re.search(stdout_regex, stdout, regex_flags) -def _test_run_output(command, expected_output_extensions): - proc = subprocess.Popen(command, stdout=subprocess.PIPE) #nosec +def _test_run_output(command, cwd, expected_output_extensions,collect=True): + proc = subprocess.Popen(command, cwd=cwd,stdout=subprocess.PIPE) #nosec try: - stdout, stderr = proc.communicate(timeout=10) + stdout, stderr = proc.communicate(timeout=20) except: proc.kill() stdout, stderr = proc.communicate() - retcode = proc.returncode + retcode = proc.returncode assert retcode == 0 # get output dir from stdout - matches = re.findall(r"Output archive: (.*)", stdout) + if collect: + matches = re.findall(r"perf stat dumped to (.*)", stdout.decode()) + else: + matches = re.findall(r"Post processing done, result file:(.*)", stdout.decode()) assert matches - relative_output_tar = matches[0] - assert os.path.exists(relative_output_tar) + relative_output_path = matches[0].split("/")[:-1] + relative_output_path = '/'.join(relative_output_path) + assert os.path.exists(relative_output_path) # make sure all expected files are present - tar = tarfile.open(relative_output_tar, "r") + # tar = tarfile.open(relative_output_tar, "r") extension_present = {k: False for k in expected_output_extensions} - for filename in tar.getnames(): + for filename in glob.iglob(f'{relative_output_path}/*'): ext = os.path.splitext(filename)[1] if ext in extension_present.keys(): extension_present[ext] = True for ext in extension_present.keys(): assert extension_present[ext] - -def test_version(capsys): - _test_run_regex( - ["./perf-collect", "--version"], - 0, - capsys, - stdout_regex=r"^[0-9]*.[0-9]*.[0-9]*$", - ) - _test_run_regex( - ["./perf-postprocess", "--version"], - 0, - capsys, - stdout_regex=r"^[0-9]*.[0-9]*.[0-9]*$", - ) +# def test_version(capsys): +# _test_run_regex( +# ["./perf-collect", "--version"], +# 0, +# capsys, +# stdout_regex=r"^[0-9]*.[0-9]*.[0-9]*$", +# ) +# _test_run_regex( +# ["./perf-postprocess", "--version"], +# 0, +# capsys, +# stdout_regex=r"^[0-9]*.[0-9]*.[0-9]*$", +# ) def test_help_collect(capsys): @@ -92,3 +97,158 @@ def test_help_postprocess(capsys): def test_help2_postprocess(capsys): _test_run_regex(["./perf-postprocess", "-h"], 0, capsys, stdout_regex=r"optional arguments:") +def test_run_no_options(): + # no arguments passed + stdout,_,_ = _run_command(["sudo", "./perf-postprocess"],"perfspect") + matches = re.findall(r"usage: (.*)", stdout.decode()) + assert matches + + # no HTML filename provided + _,_,retcode = _run_command(["sudo", "./perf-postprocess" ,"--html"],"perfspect") + assert retcode == 2 + + #no options to pid + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"--pid"],"perfspect") + assert retcode == 2 + + #no options to cid + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"--cid"],"perfspect") + assert retcode == 2 + + #no options to app + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"--app"],"perfspect") + assert retcode == 2 + + #no options to timeout + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"-t"],"perfspect") + assert retcode == 2 + + #no options to csp + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"-csp"],"perfspect") + assert retcode == 2 + + #no options to eventfile + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"-e"],"perfspect") + assert retcode == 2 + + #no options to outcsv + _,_,retcode = _run_command(["sudo", "./perf-collect" ,"-o"],"perfspect") + assert retcode == 2 + + + + +def test_invalid_arguments(): + #perf-collect + #invalid csp name + stdout,_,retcode = _run_command(["sudo", "./perf-collect","-csp", "bad_cspname"],"perfspect") + hit = re.search(r"Invalid csp/cloud", stdout.decode()) + assert hit + assert retcode == 1 + + #invalid eventfile + stdout,_,retcode = _run_command(["sudo", "./perf-collect","-e", "bad_eventfile"],"perfspect") + hit = re.search(r"event file not found", stdout.decode()) + assert hit + assert retcode == 1 + + #invalid interval + stdout,_,retcode = _run_command(["sudo", "./perf-collect","-i", "invalid_interval"],"perfspect") + hit = re.search(r"invalid float value", stdout.decode()) + assert hit + assert retcode == 2 + + #invalid interval range + stdout,_,retcode = _run_command(["sudo", "./perf-collect","-i", "-.007"],"perfspect") + hit = re.search(r"dump interval is too large or too small", stdout.decode()) + assert hit + assert retcode == 1 + + #invalid interval range + stdout,_,retcode = _run_command(["sudo", "./perf-collect","-i", "1001"],"perfspect") + hit = re.search(r"dump interval is too large or too small", stdout.decode()) + assert hit + assert retcode == 1 + + #invalid outcsv + stdout,_,retcode = _run_command(["sudo", "./perf-collect","-o", "bad_outcsv"],"perfspect") + hit = re.search(r"Output filename not accepted", stdout.decode()) + assert hit + assert retcode == 1 + + #uncomment if testing outside of containers + #invalid app argument + # stdout,_,retcode = _run_command(["sudo", "./perf-collect","-a", "bad_app_argument", "-m", "0"],"perfspect") + # hit = re.search(r"Workload failed", stdout.decode()) + # print("--app",stdout) + # assert hit + + #invalid pid argument + # stdout,_,retcode = _run_command(["sudo", "./perf-collect","-p", "bad_pid"],"perfspect") + # hit = re.search(r"Problems finding threads of monitor", stdout.decode()) + # assert hit + + #invalid cid argument + # stdout,_,retcode = _run_command(["sudo", "./perf-collect","--cid", "bad_cid"],"perfspect") + # hit = re.search(r"invalid container ID", stdout.decode()) + # # print("cid",stdout) + # assert hit + # assert retcode == 1 + + #invalid timeout + stdout,_,retcode = _run_command(["sudo", "./perf-collect","--timeout", "bad_timeout"],"perfspect") + # print(stdout) + hit = re.search(r"invalid int value", stdout.decode()) + assert hit + assert retcode == 2 + + #perf-postprocess + #invalid HTML filename provided + stdout,_,retcode = _run_command(["sudo", "./perf-postprocess","-r","../data/perfstat.csv","--html", "bad.filename"],"perfspect") + matches = re.findall(r"isn't a valid html file (.*)", stdout.decode()) + # print("html_matches",stdout) + assert matches + assert retcode == 1 + + #invalid metricfile + stdout,_,retcode = _run_command(["sudo", "./perf-postprocess","-r","../data/perfstat.csv", "-m", "bad_metricfile.name"],"perfspect") + matches = re.findall(r"metric file not found (.*)", stdout.decode()) + assert matches + assert retcode == 1 + + + #invalid raw file + stdout,_,retcode = _run_command(["sudo", "./perf-postprocess","-r", "bad_raw_file"],"perfspect") + hit = re.search(r"perf raw data file not found", stdout.decode()) + assert hit + assert retcode == 1 + + +def test_no_raw_perf_data(): + #invalid perf raw file provided + stdout,_,_ = _run_command(["sudo", "./perf-postprocess" ,"-r", "perfstat.badfilename"],"perfspect") + matches = re.findall(r"usage: (.*)", stdout.decode()) + assert matches + + #empty perf raw file provided + stdout,_,retcode = _run_command(["sudo", "./perf-postprocess" ,"-r", "../data/perfstat_empty.csv"],"perfspect") + hit = re.search(r"The perf raw file doesn't contain metadata", stdout.decode()) + assert hit + assert retcode == 1 + +def test_perf_data_without_metadata(): + #empty perf raw file provided + stdout,_,retcode = _run_command(["sudo", "./perf-postprocess" ,"-r", "../data/perfstat_without_metadata.csv"],"perfspect") + hit = re.search(r"The perf raw file doesn't contain metadata", stdout.decode()) + assert hit + assert retcode == 1 + +def test_valid_perf_postprocess(): + #empty perf raw file provided + _test_run_output(["sudo", "./perf-postprocess" ,"-r", "../data/perfstat.csv"],"perfspect",[".csv"], False) + _test_run_output(["sudo", "./perf-postprocess" ,"-r", "../data/perfstat.csv", "--html", "abc.html"],"perfspect",[".csv", ".html"], False) + +# uncomment if testing outside of container +# def test_valid_perf_collect(): +# #empty perf raw file provided +# _test_run_output(["sudo", "./perf-collect", "-m", "0", "-t", "5"],"perfspect",[".csv"]) From 568f05f50a2fb8967fda8e491f52406a59b58a45 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 10:00:33 -0800 Subject: [PATCH 02/15] add aws vm support --- Makefile | 2 + events/metric_spr_aws.json | 247 +++++++++++++++++++++++++++++++++++++ events/spr_aws.txt | 94 ++++++++++++++ perf-collect.py | 2 + perf-postprocess.py | 2 + 5 files changed, 347 insertions(+) create mode 100644 events/metric_spr_aws.json create mode 100644 events/spr_aws.txt diff --git a/Makefile b/Makefile index ee9c55d..e292b1f 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ build-public/collect: --add-data "./events/icx.txt:." \ --add-data "./events/spr.txt:." \ --add-data "./events/icx_aws.txt:." \ + --add-data "./events/spr_aws.txt:." \ --add-data "./events/clx_aws.txt:." \ --add-data "./events/skx_aws.txt:." \ --add-binary "../build/pmu-checker:." \ @@ -64,6 +65,7 @@ build-public/postprocess: --add-data "./events/metric_icx.json:." \ --add-data "./events/metric_spr.json:." \ --add-data "./events/metric_icx_aws.json:." \ + --add-data "./events/metric_spr_aws.json:." \ --runtime-tmpdir . \ --exclude-module readline cp $(TMPDIR)/dist/perf-postprocess build/ diff --git a/events/metric_spr_aws.json b/events/metric_spr_aws.json new file mode 100644 index 0000000..331cc26 --- /dev/null +++ b/events/metric_spr_aws.json @@ -0,0 +1,247 @@ +[ + { + "name": "metric_CPU operating frequency (in GHz)", + "expression": "([cpu-cycles] / [ref-cycles]) * ([const_tsc_freq] / 1000000000)" + }, + { + "name": "metric_CPU utilization %", + "expression": "100 * [ref-cycles] / [const_TSC]" + }, + { + "name": "metric_CPU utilization% in kernel mode", + "expression": "100 * [ref-cycles:k] / [const_TSC]" + }, + { + "name": "metric_CPI", + "expression": "[cpu-cycles] / [instructions]" + }, + { + "name": "metric_kernel_CPI", + "expression": "[cpu-cycles:k] / [instructions:k]" + }, + { + "name": "metric_IPC", + "expression": "[instructions] / [cpu-cycles]" + }, + { + "name": "metric_giga_instructions_per_sec", + "expression": "[instructions] / 1000000000" + }, + { + "name": "metric_L1D MPI (includes data+rfo w/ prefetches)", + "tags": "transaction", + "expression": "[L1D.REPLACEMENT] / [instructions]" + }, + { + "name": "metric_L1D demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L1_HIT] / [instructions]" + }, + { + "name": "metric_L1-I code read misses (w/ prefetches) per instr", + "expression": "[L2_RQSTS.ALL_CODE_RD] / [instructions]" + }, + { + "name": "metric_L2 demand data read hits per instr", + "expression": "[MEM_LOAD_RETIRED.L2_HIT] / [instructions]" + }, + { + "name": "metric_L2 MPI (includes code+data+rfo w/ prefetches)", + "expression": "[L2_LINES_IN.ALL] / [instructions]" + }, + { + "name": "metric_L2 demand data read MPI", + "expression": "[MEM_LOAD_RETIRED.L2_MISS] / [instructions]" + }, + { + "name": "metric_L2 demand code MPI", + "expression": "[L2_RQSTS.CODE_RD_MISS] / [instructions]" + }, + { + "name": "metric_package power (watts)", + "expression": "[power/energy-pkg/]" + }, + { + "name": "metric_DRAM power (watts)", + "expression": "[power/energy-ram/]" + }, + { + "name": "metric_core c6 residency %", + "expression": "100 * [cstate_core/c6-residency/] / [const_TSC]" + }, + { + "name": "metric_package c6 residency %", + "expression": "100 * [cstate_pkg/c6-residency/] * [const_core_count] / [const_TSC]" + }, + { + "name": "metric_core initiated local dram read bandwidth (MB/sec)", + "expression": "([OCR.READS_TO_CORE.LOCAL_DRAM] + [OCR.HWPF_L3.L3_MISS_LOCAL]) * 64 / 1000000" + }, + { + "name": "metric_core initiated remote dram read bandwidth (MB/sec)", + "expression": "([OCR.READS_TO_CORE.REMOTE_DRAM] + [OCR.HWPF_L3.REMOTE]) * 64 / 1000000" + }, + { + "name": "metric_LLC total HITM (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HITM] / [instructions]" + }, + { + "name": "metric_LLC total HIT clean line forwards (per instr) (excludes LLC prefetches)", + "expression": "[OCR.READS_TO_CORE.REMOTE_CACHE.SNOOP_HIT_WITH_FWD] / [instructions]" + }, + { + "name": "metric_ITLB (2nd level) MPI", + "expression": "[ITLB_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) 2MB large page load MPI", + "expression": "[DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M] / [instructions]" + }, + { + "name": "metric_DTLB (2nd level) store MPI", + "expression": "[DTLB_STORE_MISSES.WALK_COMPLETED] / [instructions]" + }, + { + "name": "metric_TMA_Frontend_Bound(%)", + "expression": "100 * ([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([topdown.slots]))" + }, + { + "name": "metric_TMA_..Fetch_Latency(%)", + "expression": "100 * (([topdown-fetch-lat] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / ([topdown.slots])))" + }, + { + "name": "metric_TMA_....ICache_Misses(%)", + "expression": "100 * ([ICACHE_DATA.STALLS] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....ITLB_Misses(%)", + "expression": "100 * ([ICACHE_TAG.STALLS] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....Branch_Resteers(%)", + "expression": "100 * ([INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles] + ([INT_MISC.UNKNOWN_BRANCH_CYCLES] / [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Mispredicts_Resteers(%)", + "expression": "100 * ((([topdown-br-mispredict] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) / (max(0, (1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])))))))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Clears_Resteers(%)", + "expression": "100 * ((1 - (([topdown-br-mispredict] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) / (max(0, (1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))))))) * [INT_MISC.CLEAR_RESTEER_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Unknown_Branches(%)", + "expression": "100 * ([INT_MISC.UNKNOWN_BRANCH_CYCLES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_..Fetch_Bandwidth(%)", + "expression": "100 * (max(0, (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) - (([topdown-fetch-lat] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots])))))" + }, + { + "name": "metric_TMA_Bad_Speculation(%)", + "expression": "100 * (max((1 - (([topdown-fe-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) + ([topdown-retiring] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))))), 0))" + }, + { + "name": "metric_TMA_..Branch_Mispredicts(%)", + "expression": "100 * ([topdown-br-mispredict] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound])))" + }, + { + "name": "metric_TMA_..Machine_Clears(%)", + "expression": "100 * (max(0, ((max(0, (1 - (([topdown-fe-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])) - [INT_MISC.UOP_DROPPING] / [topdown.slots]) + ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))) + ([topdown-retiring] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))))) - ([topdown-br-mispredict] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))))" + }, + { + "name": "metric_TMA_Backend_Bound(%)", + "expression": "100 * ([topdown-be-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])))" + }, + { + "name": "metric_TMA_..Memory_Bound(%)", + "expression": "100 * ([topdown-mem-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring])))" + }, + { + "name": "metric_TMA_....L1_Bound(%)", + "expression": "100 * (max(0, (([EXE_ACTIVITY.BOUND_ON_LOADS] - [MEMORY_ACTIVITY.STALLS_L1D_MISS]) / [cpu-cycles])))" + }, + { + "name": "metric_TMA_......DTLB_Load(%)", + "expression": "100 * (min((7) * [DTLB_LOAD_MISSES.STLB_HIT:c1] + [DTLB_LOAD_MISSES.WALK_ACTIVE], max([CYCLE_ACTIVITY.CYCLES_MEM_ANY] - [CYCLE_ACTIVITY.CYCLES_L1D_MISS], 0)) / ( [cpu-cycles]))" + }, + { + "name": "metric_TMA_......Split_Loads(%)", + "expression": "100 * (min(1, ((([L1D_PEND_MISS.PENDING] / ([MEM_LOAD_COMPLETED.L1_MISS_ANY])) * [LD_BLOCKS.NO_SR] / [cpu-cycles]))))" + }, + { + "name": "metric_TMA_....L2_Bound(%)", + "expression": "100 * (([MEMORY_ACTIVITY.STALLS_L1D_MISS] - [MEMORY_ACTIVITY.STALLS_L2_MISS]) / [cpu-cycles])" + }, + { + "name": "metric_TMA_....L3_Bound(%)", + "expression": "100 * (([MEMORY_ACTIVITY.STALLS_L2_MISS] - [MEMORY_ACTIVITY.STALLS_L3_MISS]) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......MEM_Bandwidth(%)", + "expression": "100 * ((min(([cpu-cycles] - 0), ([OFFCORE_REQUESTS_OUTSTANDING.DATA_RD:c4] - 0))) / [cpu-cycles])" + }, + { + "name": "metric_TMA_......MEM_Latency(%)", + "expression": "100 * ( ( min( [cpu-cycles], [OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD] ) ) / ( [cpu-cycles] ) - ( ( min([cpu-cycles], [OFFCORE_REQUESTS_OUTSTANDING.DATA_RD:c4] ) ) / ( [cpu-cycles] ) ) )" + }, + { + "name": "metric_TMA_....Store_Bound(%)", + "expression": "100 * ([EXE_ACTIVITY.BOUND_ON_STORES] / [cpu-cycles])" + }, + { + "name": "metric_TMA_..Core_Bound(%)", + "expression": "100 * (max(0, (([topdown-be-bound] / (([topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound]))) - ([topdown-mem-bound] / (([topdown-be-bound] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring]))))))" + }, + { + "name": "metric_TMA_....Divider(%)", + "expression": "100 * ([ARITH.DIV_ACTIVE] / [cpu-cycles])" + }, + { + "name": "metric_TMA_....Ports_Utilization(%)", + "expression": "100 * ( ( [EXE_ACTIVITY.3_PORTS_UTIL:u0x80] + ( [RESOURCE_STALLS.SCOREBOARD] / ( [cpu-cycles] ) ) * ( [CYCLE_ACTIVITY.STALLS_TOTAL] - [EXE_ACTIVITY.BOUND_ON_LOADS] ) + ( [EXE_ACTIVITY.1_PORTS_UTIL] + ( [topdown-retiring] / ( [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound] ) ) * [EXE_ACTIVITY.2_PORTS_UTIL:u0xc] ) ) / ( [cpu-cycles] ) if ( [ARITH.DIV_ACTIVE] < ( [CYCLE_ACTIVITY.STALLS_TOTAL] - [EXE_ACTIVITY.BOUND_ON_LOADS] ) ) else ( [EXE_ACTIVITY.1_PORTS_UTIL] + ( [topdown-retiring] / ( [topdown-fe-bound] + [topdown-bad-spec] + [topdown-retiring] + [topdown-be-bound] ) ) * [EXE_ACTIVITY.2_PORTS_UTIL:u0xc] ) / ( [cpu-cycles] ) )" + }, + { + "name": "metric_TMA_......Ports_Utilized_0(%)", + "expression": "100 * ( [EXE_ACTIVITY.3_PORTS_UTIL:u0x80] / ( [cpu-cycles] ) + ( [RESOURCE_STALLS.SCOREBOARD] / ( [cpu-cycles] ) ) * ( [CYCLE_ACTIVITY.STALLS_TOTAL] - [EXE_ACTIVITY.BOUND_ON_LOADS] ) / ( [cpu-cycles] ) )" + }, + { + "name": "metric_TMA_......Ports_Utilized_1(%)", + "expression": "100 * ([EXE_ACTIVITY.1_PORTS_UTIL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Ports_Utilized_2(%)", + "expression": "100 * ([EXE_ACTIVITY.2_PORTS_UTIL] / [cpu-cycles])" + }, + { + "name": "metric_TMA_......Ports_Utilized_3m(%)", + "expression": "100 * [UOPS_EXECUTED.CYCLES_GE_3] / [cpu-cycles]" + }, + { + "name": "metric_TMA_Retiring(%)", + "expression": "100 * ([topdown-retiring] / ([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))" + }, + { + "name": "metric_TMA_..Light_Operations(%)", + "expression": "100 * (max(0, (([topdown-retiring] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))) - ([topdown-heavy-ops] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound]))))))" + }, + { + "name": "metric_TMA_..Heavy_Operations(%)", + "expression": "100 * ([topdown-heavy-ops] / (([topdown-retiring] + [topdown-fe-bound] + [topdown-bad-spec] + [topdown-be-bound])))" + }, + { + "name": "metric_TMA_Info_Thread_IPC", + "expression": "[instructions] / [cpu-cycles]" + }, + { + "name": "metric_TMA_Info_Core_ILP", + "expression": "[instructions] / [CPU_CLK_UNHALTED.DISTRIBUTED]" + }, + { + "name": "metric_TMA_Info_System_SMT_2T_Utilization", + "expression": "(1 - [CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE] / [CPU_CLK_UNHALTED.REF_DISTRIBUTED]) if [const_socket_count] > 1 else 0" + } +] \ No newline at end of file diff --git a/events/spr_aws.txt b/events/spr_aws.txt new file mode 100644 index 0000000..83f48a6 --- /dev/null +++ b/events/spr_aws.txt @@ -0,0 +1,94 @@ +########################################################################################################### +# Copyright (C) 2021-2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +########################################################################################################### + +# SapphireRapids event list for AWS instances (default) + +cpu/event=0x51,umask=0x01,period=100003,name='L1D.REPLACEMENT'/, +cpu/event=0x24,umask=0xe4,period=200003,name='L2_RQSTS.ALL_CODE_RD'/, +cpu/event=0xd1,umask=0x01,period=1000003,name='MEM_LOAD_RETIRED.L1_HIT'/, +cpu/event=0xa3,umask=0x04,cmask=0x04,period=1000003,name='CYCLE_ACTIVITY.STALLS_TOTAL'/, +cpu-cycles, +ref-cycles, +instructions; + +cpu/event=0x80,umask=0x04,period=500009,name='ICACHE_DATA.STALLS'/, +cpu/event=0x83,umask=0x04,period=200003,name='ICACHE_TAG.STALLS'/, +cpu/event=0xa3,umask=0x08,cmask=0x08,period=1000003,name='CYCLE_ACTIVITY.CYCLES_L1D_MISS'/, +cpu/event=0xa3,umask=0x10,cmask=0x10,period=1000003,name='CYCLE_ACTIVITY.CYCLES_MEM_ANY'/, +cpu-cycles; + + +cpu/event=0x25,umask=0x1f,period=100003,name='L2_LINES_IN.ALL'/, +cpu/event=0xd1,umask=0x10,period=100021,name='MEM_LOAD_RETIRED.L2_MISS'/, +cpu/event=0x24,umask=0x24,period=200003,name='L2_RQSTS.CODE_RD_MISS'/, +cpu/event=0x11,umask=0x0e,period=100003,name='ITLB_MISSES.WALK_COMPLETED'/, +cpu-cycles, +ref-cycles, +instructions; + +cpu/event=0x12,umask=0x0e,period=100003,name='DTLB_LOAD_MISSES.WALK_COMPLETED'/, +cpu/event=0x12,umask=0x04,period=100003,name='DTLB_LOAD_MISSES.WALK_COMPLETED_2M_4M'/, +cpu/event=0x13,umask=0x0e,period=100003,name='DTLB_STORE_MISSES.WALK_COMPLETED'/, +cpu/event=0xd1,umask=0x02,period=200003,name='MEM_LOAD_RETIRED.L2_HIT'/, +cpu-cycles:k, +ref-cycles:k, +instructions:k; + +cpu/event=0x20,umask=0x08,cmask=0x01,period=1000003,name='OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD'/, +cpu/event=0x20,umask=0x08,cmask=0x04,period=1000003,name='OFFCORE_REQUESTS_OUTSTANDING.DATA_RD:c4'/, +cpu-cycles, +ref-cycles, +instructions; + +#TMA related +cycles, +slots, +topdown-bad-spec, +topdown-be-bound, +topdown-fe-bound, +topdown-retiring, +topdown-fetch-lat, +topdown-mem-bound, +topdown-br-mispredict, +topdown-heavy-ops; + +cpu/event=0xad,umask=0x10,period=1000003,name='INT_MISC.UOP_DROPPING'/, +cpu/event=0xad,umask=0x40,frontend=0x7,period=1000003,name='INT_MISC.UNKNOWN_BRANCH_CYCLES'/, +cpu/event=0xa6,umask=0x21,cmask=0x05,period=2000003,name='EXE_ACTIVITY.BOUND_ON_LOADS'/, +cpu-cycles; + +cpu/event=0x47,umask=0x03,cmask=0x03,period=1000003,name='MEMORY_ACTIVITY.STALLS_L1D_MISS'/, +cpu/event=0x12,umask=0x20,cmask=0x01,period=100003,name='DTLB_LOAD_MISSES.STLB_HIT:c1'/, +cpu/event=0x12,umask=0x10,cmask=0x01,period=100003,name='DTLB_LOAD_MISSES.WALK_ACTIVE'/, +cpu/event=0x47,umask=0x05,cmask=0x05,period=1000003,name='MEMORY_ACTIVITY.STALLS_L2_MISS'/, +cpu-cycles, +instructions; + +cpu/event=0x47,umask=0x09,cmask=0x09,period=1000003,name='MEMORY_ACTIVITY.STALLS_L3_MISS'/, +cpu/event=0xa6,umask=0x40,cmask=0x02,period=1000003,name='EXE_ACTIVITY.BOUND_ON_STORES'/, +cpu/event=0xa6,umask=0x02,period=2000003,name='EXE_ACTIVITY.1_PORTS_UTIL'/, +cpu/event=0xa6,umask=0x04,period=2000003,name='EXE_ACTIVITY.2_PORTS_UTIL'/, +cpu-cycles, +instructions; + +cpu/event=0x43,umask=0xfd,period=2000003,name='MEM_LOAD_COMPLETED.L1_MISS_ANY'/, +cpu/event=0xa2,umask=0x02,period=2000003,name='RESOURCE_STALLS.SCOREBOARD'/, +cpu/event=0xa6,umask=0x80,period=2000003,name='EXE_ACTIVITY.3_PORTS_UTIL:u0x80'/, +cpu/event=0xa6,umask=0xc,period=2000003,name='EXE_ACTIVITY.2_PORTS_UTIL:u0xc'/, +cpu-cycles, +instructions; + +cpu/event=0xad,umask=0x80,period=500009,name='INT_MISC.CLEAR_RESTEER_CYCLES'/, +cpu/event=0xb1,umask=0x01,cmask=0x03,period=2000003,name='UOPS_EXECUTED.CYCLES_GE_3'/, +cpu/event=0x48,umask=0x01,period=1000003,name='L1D_PEND_MISS.PENDING'/, +cpu/event=0x03,umask=0x88,period=100003,name='LD_BLOCKS.NO_SR'/, +cpu-cycles; + +cpu/event=0xec,umask=0x02,period=2000003,name='CPU_CLK_UNHALTED.DISTRIBUTED'/, +cpu-cycles; + +cpu/event=0x3c,umask=0x02,period=25003,name='CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE'/, +cpu/event=0x3c,umask=0x08,period=2000003,name='CPU_CLK_UNHALTED.REF_DISTRIBUTED'/, +cpu/event=0xb0,umask=0x09,cmask=0x01,period=1000003,name='ARITH.DIV_ACTIVE'/; diff --git a/perf-collect.py b/perf-collect.py index db7bc17..563e1d4 100644 --- a/perf-collect.py +++ b/perf-collect.py @@ -287,6 +287,8 @@ def is_safe_file(fname, substr): eventfile = "icx_oci.txt" elif arch == "sapphirerapids": 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)" diff --git a/perf-postprocess.py b/perf-postprocess.py index 3a6b3d7..9183012 100644 --- a/perf-postprocess.py +++ b/perf-postprocess.py @@ -1456,6 +1456,8 @@ def is_safe_path(base_dir, path, follow_symlinks=True): metric_file = "metric_icx_aws.json" elif CONST_ARCH == "icelake": metric_file = "metric_icx.json" + elif CONST_ARCH == "sapphirerapids" and args.cloud == "aws": + metric_file = "metric_spr_aws.json" elif CONST_ARCH == "sapphirerapids": metric_file = "metric_spr.json" else: From 031a4e5b6f0053396abc2e4b1d2f01d861141190 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 10:09:56 -0800 Subject: [PATCH 03/15] updated build workflow --- .github/workflows/build.yml | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d2464f..fc5a697 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,31 +1,22 @@ name: Build & Test -on: - [pull_request,push] +on: [pull_request, push] jobs: build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: [3.9] - + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies + - uses: actions/checkout@v3 + - name: install dependencies run: | - python -m pip install --upgrade pip - pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - name: Edit memory config - run: sed -i '10 i \ --memory-swap -1 \\' builder/build - - name: Build binaries - run: ./build.sh - - name: Validate with pytest + sudo apt update + sudo apt install -y python3 golang + pip3 install -r requirements.txt + - name: build run: | - builder/test + make dist + - name: upload artifact + uses: actions/upload-artifact@v3 + with: + name: perfspect + path: dist/perfspect*.tgz From 26a09359f268ea1f1e5864421cfdf56fb963521e Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 10:14:14 -0800 Subject: [PATCH 04/15] install newer golang --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc5a697..4435c80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,9 @@ jobs: - name: install dependencies run: | sudo apt update - sudo apt install -y python3 golang + sudo apt install -y python3 + rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz + export PATH=$PATH:/usr/local/go/bin pip3 install -r requirements.txt - name: build run: | From 8a2e19478bfda858f0882db75f195c33c43de584 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 10:17:31 -0800 Subject: [PATCH 05/15] use snap to install golang --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4435c80..abd94c0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,8 +11,7 @@ jobs: run: | sudo apt update sudo apt install -y python3 - rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz - export PATH=$PATH:/usr/local/go/bin + sudo snap install go --classic pip3 install -r requirements.txt - name: build run: | From 879ddfbd9035cd7b9571de5bb267d9eabf461598 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 13:27:04 -0800 Subject: [PATCH 06/15] revert pmu-checker and add spr support to pmu-checker --- pmu-checker/Makefile | 9 +- pmu-checker/README.md | 6 +- pmu-checker/cmd/main.go | 149 ++++++++++++ pmu-checker/go.mod | 4 +- pmu-checker/go.sum | 20 +- pmu-checker/msr/msr.go | 159 +++++++++++++ pmu-checker/msr/result.go | 27 +++ pmu-checker/msr/testdata/dev/cpu/1/msr | 0 pmu-checker/msr/validate.go | 25 ++ pmu-checker/msr/validate_test.go | 22 ++ pmu-checker/pmu-checker.go | 311 ------------------------- 11 files changed, 410 insertions(+), 322 deletions(-) create mode 100644 pmu-checker/cmd/main.go create mode 100644 pmu-checker/msr/msr.go create mode 100644 pmu-checker/msr/result.go create mode 100644 pmu-checker/msr/testdata/dev/cpu/1/msr create mode 100644 pmu-checker/msr/validate.go create mode 100644 pmu-checker/msr/validate_test.go delete mode 100644 pmu-checker/pmu-checker.go diff --git a/pmu-checker/Makefile b/pmu-checker/Makefile index 1ec7052..d6fd981 100644 --- a/pmu-checker/Makefile +++ b/pmu-checker/Makefile @@ -15,8 +15,11 @@ fmt: do $(GO_FMT) -w -s "$$file"; \ done -pmu-checker: pmu-checker.go - $(GO_BIN) build -o $(BINARY_NAME) -ldflags '-X main.gVersion=$(VERSION)' . +pmu-checker: + $(GO_BIN) build -o $(BINARY_NAME) -ldflags '-X main.gVersion=$(VERSION)' ./cmd/ + +pmu-checker_arm64: + GOOS=linux GOARCH=arm64 $(GO_BIN) build -o $(BINARY_NAME)_arm64 -ldflags '-X main.gVersion=$(VERSION)' ./cmd/ .PHONY: lint lint: @@ -24,6 +27,6 @@ lint: .PHONY: test test: pmu-checker - sudo ./$(BINARY_NAME) | jq + sudo ./$(BINARY_NAME) --no-stdout | jq .DEFAULT_GOAL := $(BINARY_NAME) diff --git a/pmu-checker/README.md b/pmu-checker/README.md index 76a9cff..5da4ba4 100644 --- a/pmu-checker/README.md +++ b/pmu-checker/README.md @@ -1,6 +1,6 @@ # pmu-checker -Allows us to verify if the system is running any drivers/daemons that may be programming the PMU. Superuseful for customers who have such instrumentations, don’t know it. +Allows us to verify if the system is running any drivers/daemons that may be programming the PMU. pmu-checker specifically checks if the following MSRs are actively being programmed/used: @@ -28,7 +28,3 @@ Options: -logfile, --logfile (string) Specify the log filename to be used for logging, Default is "pmu-checker.log" -debug, --debug Set the loglevel to debug, Default is info -no-stdout, --no-stdout Set the logwriter to write to log file only - -## Contribution - -If you are interested in contributing, feel free to fork this repo and create MR(Merge Requests). diff --git a/pmu-checker/cmd/main.go b/pmu-checker/cmd/main.go new file mode 100644 index 0000000..4b3ea20 --- /dev/null +++ b/pmu-checker/cmd/main.go @@ -0,0 +1,149 @@ +//########################################################################################################### +//# Copyright (C) 2021 Intel Corporation +//# SPDX-License-Identifier: BSD-3-Clause +//########################################################################################################### + +package main + +import ( + "flag" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "sync" + + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + + "github.com/intel/perfspect/pmu-checker/msr" +) + +const ( + description = `pmu-checker + +Allows us to verify if the system is running any drivers/daemons that may be programming the PMU. + +Options: +` + iterationCompleted = "-------------All Iteration checks completed-------------" + iterations = 6 +) + +var ( + loglevel = flag.Bool("debug", false, "set the loglevel to debug, default is info") + multiLogWriter = flag.Bool("no-stdout", false, "set the logwriter to write to logfile only, default is false") + cpu = flag.Int("cpu", 0, "Read MSRs on respective CPU, default is 0") + logfile = flag.String("logfile", "pmu-checker.log", "set the logfile name, default is pmu-checker.log") + help = flag.Bool("help", false, "Shows the usage of pmu-checker application") + logFileRegexp = regexp.MustCompile(`([a-zA-Z0-9\s_\\.\-():])+(.log|.txt)$`) +) + +func initialize() error { + if !logFileRegexp.MatchString(*logfile) { + return errors.New("the file name isn't valid for logging, the valid extensions are .log and .txt") + } + + log.SetFormatter(&log.TextFormatter{ + ForceColors: true, + FullTimestamp: true, + DisableLevelTruncation: true, + }) + // programDir, err := filepath.Abs(filepath.Dir(os.Args[0])) + ex, err := os.Executable() + if err != nil { + return errors.Wrap(err, "failed to get absolute path of the program") + } + exPath := filepath.Dir(ex) + file, err := os.OpenFile(filepath.Join(exPath, *logfile), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + return errors.Wrap(err, "failed to open the log file") + } + + //write to logfile and stdout at same time + mw := io.MultiWriter(os.Stdout, file) + + if *multiLogWriter == true { + log.SetOutput(file) + } else { + log.SetOutput(mw) + } + + log.SetLevel(log.InfoLevel) + if *loglevel == true { + log.SetLevel(log.DebugLevel) + } + + err = msr.Initialize() + if err != nil { + return errors.Wrap(err, "couldn't initialize msr module") + } + + return nil +} + +func main() { + if os.Geteuid() != 0 { + println("You need a root privileges to run.") + os.Exit(2) + } + + flag.Parse() + + if *help == true { + println(description) + flag.PrintDefaults() + os.Exit(0) + } + + err := initialize() + if err != nil { + log.Error(errors.Wrap(err, "couldn't initialize PMU Checker")) + os.Exit(2) + } + + err = msr.ValidateMSRModule(*cpu) + if err != nil { + log.Error(errors.Wrap(err, "couldn't validate MSR module")) + os.Exit(2) + } + + log.Info("Starting the PMU Checker application...") + runIterations() + log.Info(iterationCompleted) + + res, err := msr.GetActivePMUs() + if err != nil { + log.Error(errors.Wrap(err, "couldn't obtain active PMUs")) + os.Exit(2) + } + + fmt.Println(res) +} + +func runIterations() { + for i := 1; i <= iterations; i++ { + var wg sync.WaitGroup + if len(msr.UsedPMUs) == 7 { + // if all the PMUs are being used, break the loop + log.Infof("Aborting iteration check #%d", i) + break + } + + log.Debugf("Iteration check #%d started\n", i) + msr.Values.Range(func(key, value interface{}) bool { + _, ok := value.(uint64) + if !ok { + return false + } + + wg.Add(1) + go msr.ReadMSR(key.(string), &wg, i, *cpu) + return true + }) + + wg.Wait() + log.Infof("Iteration check #%d completed\n", i) + } +} diff --git a/pmu-checker/go.mod b/pmu-checker/go.mod index 7923acb..fb53138 100644 --- a/pmu-checker/go.mod +++ b/pmu-checker/go.mod @@ -1,8 +1,10 @@ -module pmu-checker +module github.com/intel/perfspect/pmu-checker go 1.18 require ( + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.0 + github.com/stretchr/testify v1.8.2 golang.org/x/sys v0.5.0 // indirect ) diff --git a/pmu-checker/go.sum b/pmu-checker/go.sum index 2919c20..de09d3f 100644 --- a/pmu-checker/go.sum +++ b/pmu-checker/go.sum @@ -1,18 +1,34 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 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= diff --git a/pmu-checker/msr/msr.go b/pmu-checker/msr/msr.go new file mode 100644 index 0000000..07f8831 --- /dev/null +++ b/pmu-checker/msr/msr.go @@ -0,0 +1,159 @@ +//########################################################################################################### +//# Copyright (C) 2021 Intel Corporation +//# SPDX-License-Identifier: BSD-3-Clause +//########################################################################################################### + +package msr + +import ( + "encoding/binary" + "fmt" + "strconv" + "strings" + "sync" + "syscall" + + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +const ( + msrPath = "/dev/cpu/%d/msr" + generalPurposePMU = "General_purpose_programmable_PMU" +) + +var ( + Values = sync.Map{} + UsedPMUs []string + pmuPurpose = map[string]string{ + "0x309": "instructions", + "0x30a": "cpu_cycles", + "0x30b": "ref_cycles", + "0x30c": "", + "0xc1": generalPurposePMU, + "0xc2": generalPurposePMU, + "0xc3": generalPurposePMU, + "0xc4": generalPurposePMU, + "0xc5": generalPurposePMU, + "0xc6": generalPurposePMU, + "0xc7": generalPurposePMU, + "0xc8": generalPurposePMU, + } +) + +type retMSR struct { + fd int +} + +func (dpt retMSR) read(msr int64) (uint64, error) { + // Reads a given MSR on the respective CPU + + buf := make([]byte, 8) + rc, err := syscall.Pread(dpt.fd, buf, msr) + if err != nil { + log.Fatal(err) + panic(err) + } + + if rc != 8 { + log.Errorf("wrong byte count %d", rc) + return 0, fmt.Errorf("wrong byte count %d", rc) + } + + //assuming all x86 uses little endian format + msrVal := binary.LittleEndian.Uint64(buf) + log.Tracef("MSR %d was read successfully as %d", msr, msrVal) + return msrVal, err + +} + +func openMSRInterface(cpu int) (*retMSR, error) { + // Open connection to MSR Interface with given cpu + + msrDir := fmt.Sprintf(msrPath, cpu) + fd, err := syscall.Open(msrDir, syscall.O_RDONLY, 777) + if err != nil { + return nil, errors.New("Couldn't open the msr interface") + } + + return &retMSR{fd: fd}, nil + +} + +func closeMSRInterface(dpt retMSR) error { + // Close connection to MSR Interface + return syscall.Close(dpt.fd) +} + +func ReadMSR(reg string, wg *sync.WaitGroup, thread int, cpu int) { + // Read MSR value, update map as needed + + defer wg.Done() + log.Debugf("Worker %d starting %s", thread, reg) + hexreg := strings.Replace(reg, "0x", "", -1) + hexreg = strings.Replace(hexreg, "0X", "", -1) + regInt64, err := strconv.ParseInt(hexreg, 16, 64) + if err != nil { + log.Panicf("The Hex to int64 type covertion failed\nError: ", err) + } + + msr, err := openMSRInterface(cpu) + if err != nil { + log.Panic(err) + } + + msrVal, err := msr.read(regInt64) + if err != nil { + log.Panic(err) + } + + err = closeMSRInterface(*msr) + if err != nil { + log.Panic(err) + } + + log.Debugf("New value of thread %d for %s is %d", thread, reg, msrVal) + currentVal, found := Values.Load(reg) + Values.Store(reg, msrVal) + if found == false { + // The key has been deleted, meaning PMU was active + } + + log.Debugf("Old value of thread %d for %s is %d", thread, reg, currentVal) + + if found == true && currentVal != uint64(0) && msrVal != currentVal { + // The key exists but value has changed, delete it + + UsedPMUs = append(UsedPMUs, reg) + log.Debugf("Deleting %s in the thread %d", reg, thread) + Values.Delete(reg) + + } + log.Debugf("Worker %d done for %s\n", thread, reg) + +} + +func Initialize() error { + for regPMU, _ := range pmuPurpose { + Values.Store(regPMU, uint64(0)) + } + + return nil +} + +func GetActivePMUs() (Result, error) { + var res Result + res.PMUDetails = make(map[string]string) + log.Info("Following PMU(s) are actively being used:") + for _, pmu := range UsedPMUs { + purpose, ok := pmuPurpose[pmu] + if !ok { + return Result{}, errors.New("Report this to the Developers.") + } + res.PMUDetails[pmu] = purpose + res.PMUActive++ + log.Infof("%s: might be using: %s", pmu, purpose) + } + + return res, nil +} diff --git a/pmu-checker/msr/result.go b/pmu-checker/msr/result.go new file mode 100644 index 0000000..d62ab5c --- /dev/null +++ b/pmu-checker/msr/result.go @@ -0,0 +1,27 @@ +//########################################################################################################### +//# Copyright (C) 2021 Intel Corporation +//# SPDX-License-Identifier: BSD-3-Clause +//########################################################################################################### + +package msr + +import ( + "encoding/json" + + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +type Result struct { + PMUActive int `json:"active_pmus"` + PMUDetails map[string]string `json:"details"` +} + +func (r Result) String() string { + js, err := json.MarshalIndent(r, "", "\t") + if err != nil { + log.Error(errors.Wrap(err, "result could not be converted to json")) + return "" + } + return string(js) +} diff --git a/pmu-checker/msr/testdata/dev/cpu/1/msr b/pmu-checker/msr/testdata/dev/cpu/1/msr new file mode 100644 index 0000000..e69de29 diff --git a/pmu-checker/msr/validate.go b/pmu-checker/msr/validate.go new file mode 100644 index 0000000..00efdd2 --- /dev/null +++ b/pmu-checker/msr/validate.go @@ -0,0 +1,25 @@ +//########################################################################################################### +//# Copyright (C) 2021 Intel Corporation +//# SPDX-License-Identifier: BSD-3-Clause +//########################################################################################################### + +package msr + +import ( + "fmt" + "os" + + "github.com/pkg/errors" +) + +func validate(path string) error { + if _, err := os.Stat(path); err != nil { + return errors.Wrap(err, fmt.Sprintf("MSR modules aren't loaded at %s, please load them using modprobe msr command", path)) + } + return nil +} + +func ValidateMSRModule(cpu int) error { + msrDir := fmt.Sprintf(msrPath, cpu) + return validate(msrDir) +} diff --git a/pmu-checker/msr/validate_test.go b/pmu-checker/msr/validate_test.go new file mode 100644 index 0000000..066f49b --- /dev/null +++ b/pmu-checker/msr/validate_test.go @@ -0,0 +1,22 @@ +//########################################################################################################### +//# Copyright (C) 2021 Intel Corporation +//# SPDX-License-Identifier: BSD-3-Clause +//########################################################################################################### + +package msr + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestValidate(t *testing.T) { + msrExists := "./testdata/dev/cpu/1/msr" + err := validate(msrExists) + require.NoError(t, err) + + noExistingMSR := "./testdata/dev/cpu/2/msr" + err = validate(noExistingMSR) + require.EqualError(t, err, "MSR modules aren't loaded at ./testdata/dev/cpu/2/msr, please load them using modprobe msr command: stat ./testdata/dev/cpu/2/msr: no such file or directory") +} diff --git a/pmu-checker/pmu-checker.go b/pmu-checker/pmu-checker.go deleted file mode 100644 index 54d4f2e..0000000 --- a/pmu-checker/pmu-checker.go +++ /dev/null @@ -1,311 +0,0 @@ -package main - -import ( - "encoding/binary" - "encoding/json" - "errors" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "strconv" - "strings" - "sync" - "syscall" - "time" - - log "github.com/sirupsen/logrus" -) - -//globals -var msrValues = sync.Map{} -var msrRegs = []string{ - "0x309", - "0x30a", - "0x30b", - "0xc1", - "0xc2", - "0xc3", - "0xc4", -} - -var msrDel = []string{} -var CPU int - -type retMSR struct { - fd int -} -type Result struct { - Pmu_active_count int `json:"PMU(s)_active"` - Pmu_details map[string]string `json:"Details"` -} - -const msrPath = "/dev/cpu/%d/msr" - -func (dpt retMSR) Read(msr int64) (uint64, error) { - // Reads a given MSR on the respective CPU - - buf := make([]byte, 8) - rc, err := syscall.Pread(dpt.fd, buf, msr) - if err != nil { - log.Fatal(err) - panic(err) - } - - if rc != 8 { - log.Errorf("wrong byte count %d", rc) - return 0, fmt.Errorf("wrong byte count %d", rc) - } - - //assuming all x86 uses little endian format - msrVal := binary.LittleEndian.Uint64(buf) - log.Tracef("MSR %d was read successfully as %d", msr, msrVal) - return msrVal, err - -} - -func openMSRInterface(cpu int) (*retMSR, error) { - // Open connection to MSR Interface with given cpu - - msrDir := fmt.Sprintf(msrPath, cpu) - fd, err := syscall.Open(msrDir, syscall.O_RDONLY, 777) - if err != nil { - log.Errorf("Couln't open the msr interface, Error: ", err) - return nil, errors.New("couldn't open the msr interface") - } - - return &retMSR{fd: fd}, nil - -} - -func closeMSRInterface(dpt retMSR) { - // Close connection to MSR Interface - - syscall.Close(dpt.fd) -} - -func validateMSRModule(cpu int) { - - msrDir := fmt.Sprintf(msrPath, cpu) - if _, err := os.Stat(msrDir); os.IsNotExist(err) { - // if msr modules aren't loaded - - log.Panicf("MSR modules aren't loaded at %s, please load them using modprobe msr command\n", msrDir) - } - -} - -func readMSR(reg string, wg *sync.WaitGroup, thread int, cpu int) { - // Read MSR value, update map as needed - - defer wg.Done() - log.Debugf("Worker %d starting %s", thread, reg) - hexreg := strings.Replace(reg, "0x", "", -1) - hexreg = strings.Replace(hexreg, "0X", "", -1) - regInt64, err := strconv.ParseInt(hexreg, 16, 64) - if err != nil { - log.Panicf("The Hex to int64 type covertion failed\nError: ", err) - } - - msr, err := openMSRInterface(cpu) - if err != nil { - log.Panic(err) - } - - msrVal, err := msr.Read(regInt64) - if err != nil { - log.Panic(err) - } - - closeMSRInterface(*msr) - log.Debugf("New value of thread %d for %s is %d", thread, reg, msrVal) - currentVal, found := msrValues.Load(reg) - msrValues.Store(reg, msrVal) - - log.Debugf("Old value of thread %d for %s is %d", thread, reg, currentVal) - - if found && currentVal != uint64(0) && msrVal != currentVal { - // The key exists but value has changed, delete it - - msrDel = append(msrDel, reg) - log.Debugf("Deleting %s in the thread %d", reg, thread) - msrValues.Delete(reg) - - } - log.Debugf("Worker %d done for %s\n", thread, reg) - -} - -func validateLogFileName(file string) { - regexString := `([a-zA-Z0-9\s_\\.\-\(\):])+(.log|.txt)$` - reg, err := regexp.Compile(regexString) - if err != nil { - log.Fatal(err) - os.Exit(0) - } - - if reg.MatchString(file) { - return - } else { - log.Panic("The file name isn't valid for logging, The valid extensions are .log and .txt") - } - -} - -func showUsage() { - fmt.Println("pmu-checker needs to be run with sudo previlages") - fmt.Println("Usage:") - fmt.Println(" sudo ./pmu-checker [OPTION...]") - fmt.Println("Options:") - flag.PrintDefaults() - fmt.Println(" logfile[*.log], cpu[int], debug, no-stdout") - -} - -func init() { - //parse the commandline arguments - loglevel := flag.Bool("debug", false, "set the loglevel to debug, default is info") - multiLogWriter := flag.Bool("no-stdout", false, "set the logwriter to write to logfile only, default is false") - cpu := flag.Int("cpu", 0, "Read MSRs on respective CPU, default is 0") - logfile := flag.String("logfile", "pmu-checker.log", "set the logfile name, default is pmu-checker.log") - help := flag.Bool("help", false, "Shows the usage of pmu-checker application") - - flag.Parse() - - if *help { - showUsage() - os.Exit(0) - } - - //don't allow non-sudo runs - if os.Geteuid() != 0 { - log.Fatalf("You need root privileges to run pmu-checker, please run again with sudo") - os.Exit(0) - } - - validateLogFileName(*logfile) - - log.SetFormatter(&log.TextFormatter{ - ForceColors: true, - FullTimestamp: true, - DisableLevelTruncation: true, - }) - // programDir, err := filepath.Abs(filepath.Dir(os.Args[0])) - ex, err := os.Executable() - if err != nil { - log.Errorf("Failed to get absolute path of the program\nError:", err) - } - exPath := filepath.Dir(ex) - file, err := os.OpenFile(filepath.Join(exPath, *logfile), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - log.Fatalf("Failed to open log file\nError:", err) - } - - //write to logfile and stdout at same time - mw := io.MultiWriter(os.Stdout, file) - - if *multiLogWriter { - log.SetOutput(file) - } else { - log.SetOutput(mw) - } - - log.SetLevel(log.InfoLevel) - if *loglevel { - log.SetLevel(log.DebugLevel) - } - - CPU = *cpu - -} - -func main() { - - log.Info("Starting the PMU Checker application...") - validateMSRModule(CPU) - - // initialize the map - for i := 0; i < len(msrRegs); i++ { - msrValues.Store(msrRegs[i], uint64(0)) - } - - var wg sync.WaitGroup - - for i := 1; i <= 6; i++ { - - if len(msrDel) == 7 { - // if all the PMUs are being used, break the loop - - log.Infof("Aborting iteration check #%d", i) - break - } - - log.Debugf("Iteration check #%d started\n", i) - msrValues.Range(func(key, value interface{}) bool { - _, ok := value.(uint64) - if !ok { - return false - } - - wg.Add(1) - go readMSR(key.(string), &wg, i, CPU) - return true - }) - - wg.Wait() - log.Infof("Iteration check #%d completed\n", i) - //intentional sleep - time.Sleep(time.Second) - - } - - res := new(Result) - - log.Infof(strings.Repeat("-", 12) + "All Iteration checks completed" + strings.Repeat("-", 12)) - res.Pmu_details = make(map[string]string) - if len(msrDel) == 0 { - log.Infof("None of the PMU(s) are actively being used\n") - res.Pmu_active_count = 0 - } - - if len(msrDel) > 0 { - - log.Info("Following PMU(s) are actively being used:") - for i := 0; i < len(msrDel); i++ { - pmu := msrDel[i] - switch pmu { - - case "0x309": - res.Pmu_details[pmu] = "instructions" - log.Infof("%s: might be using instructions", pmu) - case "0x30a": - res.Pmu_details[pmu] = "cpu_cycles" - log.Infof("%s: might be using cpu_cycles (check if nmi_watchdog is running)", pmu) - case "0x30b": - res.Pmu_details[pmu] = "ref_cycles" - log.Infof("%s: might be using ref_cycles", pmu) - case "0xc1", "0xc2", "0xc3", "0xc4": - res.Pmu_details[pmu] = "General_purpose_programmable_PMU" - log.Infof("%s: might be using general programmable PMU", pmu) - default: - // mustn't enter default case - log.Infof("Report this to the Developers") - os.Exit(0) - - } - - } - res.Pmu_active_count = len(msrDel) - } - - var js []byte - js, err := json.Marshal(res) - if err != nil { - log.Errorf("Result could not be converted to json\nError: ", err) - os.Exit(3) - } - fmt.Println(string(js)) - -} From 29470bfcb382560bee93ac32551ad7f81d07537a Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 13:37:40 -0800 Subject: [PATCH 07/15] include minimum kernel --- release_notes | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/release_notes b/release_notes index bddeb5c..ae47d72 100644 --- a/release_notes +++ b/release_notes @@ -2,11 +2,12 @@ RELEASE NOTES PerfSpect Xeon Micro-Architectures: -- Broadwell -- Skylake -- Cascadelake -- Icelake -- Sapphire Rapids +- 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 @@ -14,6 +15,8 @@ Operating Systems: - RHEL 9 - Debian 11 +Note: PerfSpect may work on other micro-architectures and Linux distributions, but has not been thoroughly tested + * v1.2.0 PerfSpect supports BDX, SKX, CLX, ICX and SPR From 1c5b3b00549b43361e5009ab9addfbc16f1aea8f Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 13:44:33 -0800 Subject: [PATCH 08/15] fix wording and add spr msrs to busy check --- release_notes | 2 +- src/perf_helpers.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/release_notes b/release_notes index ae47d72..8454414 100644 --- a/release_notes +++ b/release_notes @@ -15,7 +15,7 @@ Operating Systems: - RHEL 9 - Debian 11 -Note: PerfSpect may work on other micro-architectures and Linux distributions, but has not been thoroughly tested +Note: PerfSpect may work on other Linux distributions, but has not been thoroughly tested * v1.2.0 PerfSpect supports BDX, SKX, CLX, ICX and SPR diff --git a/src/perf_helpers.py b/src/perf_helpers.py index 97ddb76..d6d8b5e 100644 --- a/src/perf_helpers.py +++ b/src/perf_helpers.py @@ -181,7 +181,20 @@ def parse_hex(s): # detect if PMU counters are in use def pmu_contention_detect(iterations=6): interval = 10 - msrregs = ["0x309", "0x30a", "0x30b", "0xc1", "0xc2", "0xc3", "0xc4"] + msrregs = [ + "0x309", + "0x30a", + "0x30b", + "0x30c", + "0xc1", + "0xc2", + "0xc3", + "0xc4", + "0xc5", + "0xc6", + "0xc7", + "0xc8", + ] values = [0] * len(msrregs) prev_values = [0] * len(msrregs) From 8460fd29e7e8868615e20af46c971ee2e542f9b1 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Mon, 27 Feb 2023 14:45:31 -0800 Subject: [PATCH 09/15] revert similarity analyzer --- similarity-analyzer/SimilarityAnalysis.xlsx | Bin 0 -> 100376 bytes similarity-analyzer/data_formatter/main.py | 40 ++- similarity-analyzer/dopca.py | 256 +++++++++++--------- 3 files changed, 158 insertions(+), 138 deletions(-) create mode 100644 similarity-analyzer/SimilarityAnalysis.xlsx diff --git a/similarity-analyzer/SimilarityAnalysis.xlsx b/similarity-analyzer/SimilarityAnalysis.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c6a9f35ac1c3323cdb5e9c24d4b28b4a2682d4c0 GIT binary patch literal 100376 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 literal 0 HcmV?d00001 diff --git a/similarity-analyzer/data_formatter/main.py b/similarity-analyzer/data_formatter/main.py index e545562..a8d3b16 100644 --- a/similarity-analyzer/data_formatter/main.py +++ b/similarity-analyzer/data_formatter/main.py @@ -5,16 +5,14 @@ # SPDX-License-Identifier: BSD-3-Clause ########################################################################################################### -import xlrd import csv import os +import sys +import xlrd def invalid_filename(filename): - # check if filename ends with .csv - if filename.endswith(".csv"): - return 0 - else: + if not filename.endswith(".csv"): raise SystemError(f"{filename} isn't a csv format!") @@ -23,9 +21,7 @@ def excel2json(filename, sheet="system view"): try: if os.access(filename, os.R_OK): wb = xlrd.open_workbook(filename) - # sh = wb.sheet_by_index(sheet) sh = wb.sheet_by_name(sheet) - for rownum in range(1, sh.nrows): row_values = sh.row_values(rownum) metric = row_values[0] @@ -33,31 +29,30 @@ def excel2json(filename, sheet="system view"): data[metric] = val else: raise SystemExit(f"{filename} not accessible") - except Exception as e: + except xlrd.XLRDError as e: print(e) - exit() + sys.exit(1) return data -def csv2json(filename, args): +def csv2json(filename): data = {} try: if os.access(filename, os.R_OK): - if args.perfspect: - with open(filename, encoding="utf-8") as csvf: - csvReader = csv.DictReader(csvf) - for rows in csvReader: - key = rows["metrics"] - data[key] = float(rows["avg"]) + with open(filename, encoding="utf-8") as csvf: + csvReader = csv.DictReader(csvf) + for rows in csvReader: + key = rows["metrics"] + data[key] = float(rows["avg"]) else: raise SystemExit(f"{filename} not accessible") - except KeyError as e: - raise SystemError(e) + except KeyError as invalid_csv_key: + raise SystemExit() from invalid_csv_key return data def compare_metrics(metriclist, datalist, fields, out): - if not len(metriclist): + if not metriclist: metriclist = [ m for m in datalist[0].keys() @@ -101,8 +96,7 @@ def compare_metrics_all(datalist, fields, out): allkeys = [] for data in datalist: for k in data: - if k.startswith("metric_"): - allkeys.append(k) + allkeys.append(k) uniquekeys = list(set(allkeys)) for metric in uniquekeys: rowlines = [] @@ -160,8 +154,8 @@ def main(): jsondata = [] fields = ["Metric"] for filename in filenames: - if args.perfspect or filenames[0].endswith(".csv"): - jsondata.append(csv2json(filename, args)) + if args.perfspect: + jsondata.append(csv2json(filename)) else: jsondata.append(excel2json(filename)) fields.append(filename) diff --git a/similarity-analyzer/dopca.py b/similarity-analyzer/dopca.py index 138463b..8bbdf0c 100644 --- a/similarity-analyzer/dopca.py +++ b/similarity-analyzer/dopca.py @@ -14,31 +14,56 @@ import numpy as np -def verify_args(arg): - if not arg.files: +def verify_args(args): + if not args.files: parser.print_help() logger.error("files is a required field") - exit(1) + sys.exit(1) basepath = os.getcwd() - outfilecsv = os.path.join(basepath, arg.out + ".csv") - + outfilecsv = os.path.join(basepath, args.out + ".csv") if os.path.exists(outfilecsv): logger.warning(f"The {outfilecsv} exists already!") - raise SystemExit() - + sys.exit(1) + if args.march and args.march not in ("CLX", "ICX"): + logger.warning(f"The current released version doesn't support {args.march}") + parser.print_help() + sys.exit(1) + try: + files = args.files.split(",") + if "" in files: + logger.error("File name cannot be null/empty string") + sys.exit(1) + component_size = len(files) + if component_size in (0, 1) and not args.march: + logger.error( + f"The number of components requested is {component_size}, a minimum of 2 is required..." + ) + raise Exception + except Exception as invalid_comp_size: + raise SystemExit( + 'Minimum of 2 input files required and must contain "," delimiter between them' + ) from invalid_comp_size + if args.label: + if "" in args.label: + logger.error("label cannot be null/empty string") + parser.print_help() + sys.exit(1) + if component_size != len(args.label): + logger.warning(f"The size of labels {args.label} don't match with input files {args.files}") + parser.print_help() + sys.exit(1) + return component_size def get_version(): basepath = os.getcwd() version_file = os.path.join(basepath, "_version.txt") if os.access(version_file, os.R_OK): - with open(version_file) as f: - version = f.readline() + with open(version_file) as vfile: + version = vfile.readline() else: raise SystemError("version file isn't accessible") - return version - def setup_custom_logger(name, debug): formatter = logging.Formatter( fmt="%(asctime)s %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S" @@ -47,35 +72,76 @@ def setup_custom_logger(name, debug): handler.setFormatter(formatter) screen_handler = logging.StreamHandler(stream=sys.stdout) screen_handler.setFormatter(formatter) - logger = logging.getLogger(name) + custom_logger = logging.getLogger(name) if debug: - logger.setLevel(logging.DEBUG) + custom_logger.setLevel(logging.DEBUG) else: - logger.setLevel(logging.INFO) - logger.addHandler(handler) - logger.addHandler(screen_handler) - return logger - + custom_logger.setLevel(logging.INFO) + custom_logger.addHandler(handler) + custom_logger.addHandler(screen_handler) + return custom_logger + +def handle_nan(data, comp_size): + logger.debug("Checking for NaN in telemetry input files") + df = pd.DataFrame(data) + deleted_workload_profiles = [] + if not df.isnull().values.any(): + logger.debug("No NaN found in telemetry input files") + else: + logger.warning("NaN found in the input telemetry files, attempting to fix them") + df_thresh_nan = df.dropna(thresh=0.8*len(df.columns)) + diff_df = pd.merge(df, df_thresh_nan, how='outer', indicator='Exist') + diff_df = diff_df.loc[diff_df['Exist'] != 'both'] + deleted_row_indices = diff_df.index.tolist() + if deleted_row_indices: + if len(deleted_row_indices) in (comp_size, comp_size-1): + #too many workload profiles have NaN greater than threshold, must quit similarity analysis + logger.error("Attempted dropping of NaNs resulted in fewer #input profiles without NaN....quiting similarity analysis") + sys.exit(1) + logger.warning("The following input files contain NaN and will no longer be considered for similarity analysis") + inp_files = args.files.split(",") + for row in deleted_row_indices: + for index, filename in enumerate(inp_files): + if row == index: + comp_size = comp_size - 1 + logger.warning(f"{filename}") + if args.label: + deleted_workload_profiles.append(args.label[index]) + else: + deleted_workload_profiles.append(filename) + df = data = df_thresh_nan + if df.isnull().values.any(): + logger.debug(f"A total of {df.isnull().sum().sum()} NaN found in your telemetry files and these will be replaced with large negative number") + data = df.fillna(-99999) + return data, df.shape[0], deleted_workload_profiles def dopca(dataset, colnames, n_components, cols): # lazy loading from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA - logger.info("starting PCA") - # Preprocessing and separating dimensions + # cleaning and separating dimensions logger.debug(f"deleting colnames {colnames[0]}") del colnames[0] num_val = dataset.loc[:, colnames].values - - # Normalizing the metrics + num_val, n_components, del_rows = handle_nan(num_val, n_components) + if del_rows: + for profiles in del_rows: + try: + cols.remove(profiles) + except ValueError as e: + logger.error(e) + sys.exit(1) + # normalizing the metrics num_val = StandardScaler().fit_transform(num_val) logger.debug(f"Post normalizing metrics, num_val: {num_val}") - # PCA analysis, Create PCA model + #pca = PCA(n_components=n_components) #Limitation: If the n_components(no of workloads) are greater than num_val(the number of features), it will throw error. + + n_components = len(num_val[0]) #Solution: To scale it for any number of workloads, generate PCAs equivalent to number of features/performance matrics (instead of number of workloads) that we have for each workload. pca = PCA(n_components=n_components) - # Fit transform function + # transform principal_components = pca.fit_transform(num_val) principal_df = pd.DataFrame( data=principal_components, @@ -83,47 +149,18 @@ def dopca(dataset, colnames, n_components, cols): ) logger.debug(f"explained variance ratio: {pca.explained_variance_ratio_}") metric_df = pd.DataFrame(cols, columns=["Metric"]) - - # Concatenating the dataframe along axis = 1 + # concatenating the dataframe along axis = 1 final_dataframe = pd.concat([principal_df, metric_df], axis=1) logger.debug(f"principalDF:\n\n {principal_df}") logger.debug(f"finalDF:\n\n {final_dataframe}") - - # Get benchmarks = TMA(max) values from raw data - df1 = dataset[["metric_TMA_Frontend_Bound(%)", "metric_TMA_Backend_Bound(%)"]] - df1 = df1 / df1.max() - # print(df1) - fe_max = df1.loc[df1["metric_TMA_Frontend_Bound(%)"] == 1].index.tolist()[0] - be_max = df1.loc[df1["metric_TMA_Backend_Bound(%)"] == 1].index.tolist()[0] - # print(fe_max,be_max) - # Get benchmarks = PC(max) values from PCA data - # print(final_dataframe) - pc1_max = final_dataframe.iloc[final_dataframe["PC1"].idxmax()]["Metric"] - pc2_max = final_dataframe.iloc[final_dataframe["PC2"].idxmax()]["Metric"] - # Compare benchmarks from TMA (max) and PC(max) - if (fe_max == pc1_max) and (be_max == pc2_max): - # print(fe_max, "is along +ve X axis") - # print(be_max, "is along +ve Y axis") - xlabel = "FrontEnd Bound -->" - ylabel = "BackEnd Bound -->" - elif (be_max == pc1_max) and (fe_max == pc2_max): - # print(be_max, "is along +ve X axis") - # print(fe_max, "is along +ve Y axis") - xlabel = "BackEnd Bound -->" - ylabel = "FrontEnd Bound -->" - else: - # print("Axis max's not found") - xlabel = "PC1" - ylabel = "PC2" logger.info("PCA completed") - return final_dataframe, xlabel, ylabel + return final_dataframe - -# Plotting along PCs -def plotpca(rownames, dataframe, xlabel, ylabel): +# plot along PCs +def plotpca(rownames, dataframe): # lazy loading from matplotlib import pyplot as plt - from matplotlib import cm as cm + from matplotlib import cm as colmgr logger.info("PCA plot initiated") fig = plt.figure(figsize=(8, 8)) @@ -131,27 +168,22 @@ def plotpca(rownames, dataframe, xlabel, ylabel): plot.set_xlabel("Principal Component 1", fontsize=15) plot.set_ylabel("Principal Component 2", fontsize=15) plot.set_title("Similarity Analyzer", fontsize=20) - xs = np.arange(len(rownames)) ys = [i + xs + (i * xs) ** 2 for i in range(len(rownames))] - colors = cm.rainbow(np.linspace(0, 1, len(ys))) + colors = colmgr.rainbow(np.linspace(0, 1, len(ys))) for target, color in zip(rownames, colors): - indicesToKeep = dataframe["Metric"] == target - pc1 = dataframe.loc[indicesToKeep, "PC1"] - pc2 = dataframe.loc[indicesToKeep, "PC2"] + indices_to_keep = dataframe["Metric"] == target + pc1 = dataframe.loc[indices_to_keep, "PC1"] + pc2 = dataframe.loc[indices_to_keep, "PC2"] plot.scatter(pc1, pc2, c=color.reshape(1, -1), s=50) - # plot.text(pc1 - 0.03, pc2 - 0.03, target, fontsize=9) plot.annotate(target, (pc1, pc2)) - # plt.xlabel("PC1", fontsize=8) - # plt.ylabel("PC2", fontsize=8) - plt.xlabel(xlabel, fontsize=12, labelpad=10) - plt.ylabel(ylabel, fontsize=12, labelpad=10) + plt.xlabel("PC1", fontsize=8) + plt.ylabel("PC2", fontsize=8) plot.grid() plt.savefig(outfile) logger.info(f"PCA plot saved at {outfile}") -# To Do: plot PC weights and variation along other PCs if __name__ == "__main__": parser = ArgumentParser(description="Similarity Analyzer") required_arg = parser.add_argument_group("required arguments") @@ -177,44 +209,33 @@ def plotpca(rownames, dataframe, xlabel, ylabel): parser.add_argument( "-m", "--march", - help="plot pca against reference SPECcpu2017 (int_rate) components based on architecture specified", + help="plot pca against reference SPECcpu2017 (int_rate) components based on architecture specified. Expected values: ICX/CLX", + ) + parser.add_argument( + "-l", + "--label", + type=str, + help='label each workload profiles which will be used to plot for similarity analysis; This must map to corresponding input files delimited by ","', ) - args = parser.parse_args() - logger = setup_custom_logger("similarity_analyzer", args.debug) if args.version: print(get_version()) sys.exit(0) - logger.info("starting similarity analyzer " + get_version()) - verify_args(args) - + if args.label: + args.label = args.label.split(",") + logger.info(f"starting similarity analyzer {get_version()}") + comp_size = verify_args(args) if args.march: import glob - if args.postprocessType == "perfspect": spec_profiles = glob.glob("Reference/" + args.march + "/*.csv") else: - spec_profiles = glob.glob("Reference/" + args.march + "/*.xlsx") - + logger.error("Similarity Analyzer supports perfspect telemetry data only") + sys.exit(1) for spec in spec_profiles: args.files += "," + spec - logger.debug("The files being compared are: " + args.files) - - try: - component_size = len(args.files.split(",")) - if component_size == 0 or component_size == 1: - logger.error( - f"The number of components requested is {component_size}, a minimum of 2 is required... Exiting" - ) - raise Exception - except Exception as e: - print(e) - raise SystemExit( - 'Minimum of 2 input files required and must contain "," delimiter between them' - ) - - # Integrated data_formatter + logger.debug(f"The files being compared are: {args.files}") cmd = [] cmd.append("python3") cmd.append("data_formatter/main.py") @@ -226,35 +247,40 @@ def plotpca(rownames, dataframe, xlabel, ylabel): cmd.append(args.out + ".csv") if args.postprocessType == "perfspect": cmd.append("-p") - logger.debug(f"The command used by data formatter: {cmd}") logger.info( f"Initiating data_formatter with {args.postprocessType} pmu postprocessor" ) - process = subprocess.Popen( # nosec + with subprocess.Popen( # nosec cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - out, err = process.communicate() - if err: - logger.error(err.decode()) - exit(1) - if "Data compared and stored" in str(out): - logger.info(f"data formatter collated pmu metrics at {args.out}.csv file") - else: - logger.error( - "data formatter wasn't able to collate all the pmu metrics from input files" - ) - - # Ingest data_formatted output into pandas for PCA + ) as process: + out, err = process.communicate() + if err: + logger.error(err.decode()) + sys.exit(1) + if "Data compared and stored" in str(out): + logger.info(f"data formatter collated pmu metrics at {args.out}.csv file") + else: + logger.error( + "data formatter wasn't able to collate all the pmu metrics from input files" + ) data = args.out + ".csv" outfile = args.out + ".png" pd_data = pd.read_csv(data) pd_data = pd_data.rename( columns={i: i[14:] for i in pd_data.columns if i.startswith("Reference")} ) - pd_data = pd_data.rename( - columns={i: i[:-4] for i in pd_data.columns if i != "Metric"} - ) + if not args.label and args.postprocessType == "perfspect": + pd_data = pd_data.rename( + columns={i: i[:-4] for i in pd_data.columns if i != "Metric"} + ) + elif args.label and args.postprocessType == "perfspect": + pd_data = pd_data.rename( + columns={i: str(j) for i,j in zip(pd_data.drop(columns="Metric").columns, args.label)} + ) + else: + logger.error("Similarity Analyzer supports perfspect telemetry data only") + sys.exit(1) logger.debug(f"dataset before transpose:\n {pd_data}") columns = list(pd_data.columns) columns.remove("Metric") @@ -263,6 +289,6 @@ def plotpca(rownames, dataframe, xlabel, ylabel): pd_data.insert(loc=0, column="metric", value=columns) logger.debug(f"dataset post transpose:\n {pd_data}") column_names = pd_data.columns.tolist() - row_names = pd_data.iloc[:, 0].values.tolist() - final_df, xlabel, ylabel = dopca(pd_data, column_names, component_size, columns) - plotpca(row_names, final_df, xlabel, ylabel) + final_df = dopca(pd_data, column_names, comp_size, columns) + row_names = final_df["Metric"].values + plotpca(row_names, final_df) \ No newline at end of file From 6d374731c4e462c513c7b46f0ff7be7224858baf Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Tue, 28 Feb 2023 14:42:18 -0800 Subject: [PATCH 10/15] fixing name of pmu --- pmu-checker/msr/msr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmu-checker/msr/msr.go b/pmu-checker/msr/msr.go index 07f8831..591ddf6 100644 --- a/pmu-checker/msr/msr.go +++ b/pmu-checker/msr/msr.go @@ -29,7 +29,7 @@ var ( "0x309": "instructions", "0x30a": "cpu_cycles", "0x30b": "ref_cycles", - "0x30c": "", + "0x30c": "topdown_slots", "0xc1": generalPurposePMU, "0xc2": generalPurposePMU, "0xc3": generalPurposePMU, From 0ae4e95639a489494a285ab2e35e0d7722c35cb0 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Tue, 28 Feb 2023 14:51:35 -0800 Subject: [PATCH 11/15] more tiny tweaks to remove noise --- perf-collect.py | 2 +- perf-postprocess.py | 2 +- src/perf_helpers.py | 19 ++++++++++--------- src/prepare_perf_events.py | 2 +- src/report.py | 2 ++ 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/perf-collect.py b/perf-collect.py index 563e1d4..a8a456f 100644 --- a/perf-collect.py +++ b/perf-collect.py @@ -1,4 +1,4 @@ -#! /usr/bin/python +#!/usr/bin/env python3 ########################################################################################################### # Copyright (C) 2021-2023 Intel Corporation diff --git a/perf-postprocess.py b/perf-postprocess.py index 9183012..7faffab 100644 --- a/perf-postprocess.py +++ b/perf-postprocess.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ########################################################################################################### # Copyright (C) 2021-2023 Intel Corporation diff --git a/src/perf_helpers.py b/src/perf_helpers.py index d6d8b5e..fcf5dd7 100644 --- a/src/perf_helpers.py +++ b/src/perf_helpers.py @@ -286,7 +286,14 @@ def get_lscpu(): return cpuinfo -# Check if arch is broadwell/skyalke/cascadelake/icelake +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): try: model = int(procinfo[0]["model"].strip()) @@ -314,16 +321,10 @@ def check_architecture(procinfo): elif model == 143 and cpufamily == 6 and stepping >= 3: arch = "sapphirerapids" else: - print( - "Current architecture not supported!\nThis version only suports Broadwell/Skylake/Cascadelake/Icelake/SPR architectures. Exiting!" - ) - sys.exit() + not_suported() else: - print( - "Current architecture not supported!\nThis version only suports Broadwell/Skylake/Cascadelake/Icelake/SapphireRapids . Exiting!" - ) - sys.exit() + not_suported() return arch, modelname diff --git a/src/prepare_perf_events.py b/src/prepare_perf_events.py index 1edf4d9..24cca95 100644 --- a/src/prepare_perf_events.py +++ b/src/prepare_perf_events.py @@ -189,7 +189,7 @@ def prepare_perf_events(event_file, grouping, cpu_only): ) if len(unsupported_events) > 0: print( - "The following events are not supported with current version of perf, will not be collected!" + "These events are not supported with current version of perf, will not be collected!" ) for e in unsupported_events: print("%s" % e) diff --git a/src/report.py b/src/report.py index 103426a..bd9066e 100644 --- a/src/report.py +++ b/src/report.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + ########################################################################################################### # Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause From 3f25f255d7308461b42162b3aeffaee96513c77b Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Tue, 28 Feb 2023 15:53:13 -0800 Subject: [PATCH 12/15] update readme flags --- README.md | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index b1db6cf..6c2a24a 100644 --- a/README.md +++ b/README.md @@ -44,29 +44,26 @@ Options: -i INTERVAL, --interval INTERVAL interval in seconds for time series dump, default=1 -m MUXINTERVAL, --muxinterval MUXINTERVAL - event mux interval in milli seconds, default=0 i.e. - will use the system default + event mux interval in milli seconds, default=0 i.e. will + use the system default -o OUTCSV, --outcsv OUTCSV perf stat output in csv format, default=results/perfstat.csv - -a APP, --app APP Application to run with perf-collect, perf collection - ends after workload completion + -a APP, --app APP Application to run with perf-collect, perf collection ends + after workload completion -p PID, --pid PID perf-collect on selected PID(s) -c CID, --cid CID perf-collect on selected container ids -t TIMEOUT, --timeout TIMEOUT perf event collection time --percore Enable per core event collection - --nogroups Disable perf event grouping, events are grouped by - default as in the event file - --dryrun Test if Performance Monitoring Counters are in-use, - and collect stats for 10sec to validate event file - correctness + --nogroups Disable perf event grouping, events are grouped by default + as in the event file + --dryrun Test if Performance Monitoring Counters are in-use, and + collect stats for 10sec to validate event file correctness --metadata collect system info only, does not run perf - --tma collect additional TMA events on supported - architectures -csp CLOUD, --cloud CLOUD - Name of the Cloud Service Provider(AWS), if collecting - on cloud instances. Currently supporting AWS and OCI + Name of the Cloud Service Provider(AWS), if collecting on + cloud instances. Currently supporting AWS and OCI -ct CLOUDTYPE, --cloudtype CLOUDTYPE Instance type: Options include - VM,BM ``` @@ -100,12 +97,12 @@ Options: default=results/metric_out.csv --persocket generate per socket metrics --percore generate per core metrics - --keepall keep all intermediate csv files, use it for debug - purpose only + --keepall keep all intermediate csv files, use it for debug purpose + only --epoch time series in epoch format, default is sample count -csp CLOUD, --cloud CLOUD - Name of Cloud Service Provider(AWS), if you're - intending to postprocess on cloud instances + Name of Cloud Service Provider(AWS), if you're intending + to postprocess on cloud instances -html HTML, --html HTML Static HTML report From 140e97084e2362959e80e0f438ea98e08bff7843 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Tue, 28 Feb 2023 15:57:50 -0800 Subject: [PATCH 13/15] add python3 header to all python files --- src/__init__.py | 2 ++ src/basic_stats.py | 2 ++ src/icicle.py | 2 ++ src/perf_helpers.py | 2 +- src/prepare_perf_events.py | 2 +- 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index 74c46d3..38bbf18 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + ########################################################################################################### # Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/basic_stats.py b/src/basic_stats.py index c80b7cb..6bbe4ea 100644 --- a/src/basic_stats.py +++ b/src/basic_stats.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + ########################################################################################################### # Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/icicle.py b/src/icicle.py index 3d43060..840f24b 100644 --- a/src/icicle.py +++ b/src/icicle.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + ########################################################################################################### # Copyright (C) 2020-2023 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause diff --git a/src/perf_helpers.py b/src/perf_helpers.py index fcf5dd7..1b6b2a9 100644 --- a/src/perf_helpers.py +++ b/src/perf_helpers.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ########################################################################################################### # Copyright (C) 2020-2023 Intel Corporation diff --git a/src/prepare_perf_events.py b/src/prepare_perf_events.py index 24cca95..edbedb3 100644 --- a/src/prepare_perf_events.py +++ b/src/prepare_perf_events.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 ########################################################################################################### # Copyright (C) 2020-2023 Intel Corporation From 0744d6e349319c2a211558b892b2e22b705180af Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Tue, 28 Feb 2023 16:15:03 -0800 Subject: [PATCH 14/15] remove depreciated pmu contention detection --- src/perf_helpers.py | 76 --------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/src/perf_helpers.py b/src/perf_helpers.py index 1b6b2a9..2070344 100644 --- a/src/perf_helpers.py +++ b/src/perf_helpers.py @@ -161,82 +161,6 @@ def enumerate_uncore(event, n): return event_list -# read the MSR register and return the value in dec format -def readmsr(msr, cpu=0): - f = os.open("/dev/cpu/%d/msr" % (cpu,), os.O_RDONLY) - os.lseek(f, msr, os.SEEK_SET) - val = struct.unpack("Q", os.read(f, 8))[0] - os.close(f) - return val - - -# parse hex to int -def parse_hex(s): - try: - return int(s, 16) - except ValueError: - raise argparse.ArgumentError("Bad hex number %s" % (s)) - - -# detect if PMU counters are in use -def pmu_contention_detect(iterations=6): - interval = 10 - msrregs = [ - "0x309", - "0x30a", - "0x30b", - "0x30c", - "0xc1", - "0xc2", - "0xc3", - "0xc4", - "0xc5", - "0xc6", - "0xc7", - "0xc8", - ] - values = [0] * len(msrregs) - prev_values = [0] * len(msrregs) - - for count in range(iterations): - for i, reg in enumerate(msrregs): - msrreg = parse_hex(reg) - values[i] = readmsr(msrreg) - - in_use = 0 - if count > 0: - for j, val in enumerate(values): - if val != prev_values[j]: - in_use = 1 - if msrregs[j] == "0x309": - print("PMU in use, hint: instructions") - if msrregs[j] == "0x30a": - print( - "PMU in use, hint: cpu-cycles or Check NMI watchdog. Try: echo 0 > /proc/sys/kernel/nmi_watchdog as sudo" - ) - if msrregs[j] == "0x30b": - print("PMU in use, hint: ref-cycles") - if ( - msrregs[j] == "0xc1" - or msrregs[j] == "0xc2" - or msrregs[j] == "0xc3" - or msrregs[j] == "0xc4" - ): - print("Some PMUs in use") - if in_use != 0: - print("FAIL: PMUs in use") - return True - - print( - "checking iteration= %d waiting for %d seconds " % ((count + 1), interval) - ) - time.sleep(interval) - prev_values = values[:] - - print("PASS: PMUs not in use") - return False - - # get linux kernel version def get_version(): version = "" From 6339198815b1cdc527047f654a18659206336933 Mon Sep 17 00:00:00 2001 From: Daniel Hill Date: Tue, 28 Feb 2023 16:35:10 -0800 Subject: [PATCH 15/15] remove struct --- src/perf_helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/perf_helpers.py b/src/perf_helpers.py index 2070344..86607ed 100644 --- a/src/perf_helpers.py +++ b/src/perf_helpers.py @@ -10,7 +10,6 @@ import re import fnmatch import time -import struct import math import collections import psutil