diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9e0a0ef..3168215 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,8 +41,7 @@ jobs: - name: Build c if: matrix.language == 'cpp' run: | - cd src/calcfreq/ - make + make calcfreq - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@1500a131381b66de0c52ac28abb13cd79f4b7ecc # v2.22.12 with: diff --git a/.gitignore b/.gitignore index 469e859..260a28c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,54 +1,12 @@ -# make output -/dist -# apps -src/orchestrator/orchestrator* -src/orchestrator/resources/collector -src/orchestrator/resources/collector_arm64 -src/orchestrator/resources/sshpass -src/orchestrator/resources/reporter -src/reporter/reporter* -src/collector/collector -src/collector/collector.exe -src/collector/collector_arm64 -src/rdmsr/rdmsrx -src/wrmsr/wrmsrx -src/msrbusy/msrbusy -src/pmu2metrics/pmu2metrics -src/pmu2metrics/pmu2metrics-with-perf -src/pmu2metrics/resources/perf -src/calcfreq/calcfreq -# src -async-profiler -cpuid -dmidecode -ethtool -fio -flamegraph -ipmitool -linux -lshw -lspci -mlc -pmu-checker -spectre-meltdown-checker -sshpass -stress-ng -sysstat -src/async-profiler-* -src/linux-* -src/cpuid-* -src/libcrypt* -src/glibc* -src/zlib* -# build output in config -collector_deps_a* -# run/debug output -src/collector/collector.log -src/collector/collector.pid -src/orchestrator/orchestrator_20* -src/reporter/debug_out/* -__debug_bin*.log -# other -src/orchestrator/targets -src/pmu2metrics/sys -src/pmu2metrics/scripts +dist +bin +build.* +cmd/orchestrator/targets +cmd/orchestrator/orchestrator_20* +cmd/orchestrator/resources/collector_deps_* +cmd/orchestrator/resources/collector +cmd/orchestrator/resources/collector_arm64 +cmd/orchestrator/resources/sshpass +cmd/orchestrator/resources/reporter +cmd/pmu2metrics/scripts +cmd/reporter/debug_out/* diff --git a/Dockerfile b/Dockerfile index 88355cf..0f3f3de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ RUN cp /usr/local/lib/libz.a /usr/lib/x86_64-linux-gnu/libz.a RUN curl -s https://gitlab.com/akihe/radamsa/uploads/a2228910d0d3c68d19c09cee3943d7e5/radamsa-0.6.c.gz | gzip -d | cc -O2 -x c -o /usr/local/bin/radamsa - # Install Go -ARG GO_VERSION="1.21.4" +ARG GO_VERSION="1.21.6" RUN wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz RUN tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz RUN rm go${GO_VERSION}.linux-amd64.tar.gz @@ -50,18 +50,8 @@ RUN if [ ! -z "${LOCALBUILD}" ] ; then \ USER ${USERNAME} # Build third-party components -COPY src/Makefile prebuilt/ -RUN cd prebuilt && make -j4 prebuilt_tools -# intent here is to fill out the go package cache -RUN mkdir prebuilt/x -COPY src prebuilt/x -RUN cd prebuilt/x/orchestrator && go get -d ./... -RUN cd prebuilt/x/reporter && go get -d ./... -RUN cd prebuilt/x/collector && go get -d ./... -RUN cd prebuilt/x/rdmsr && go get -d ./... -RUN cd prebuilt/x/wrmsr && go get -d ./... -RUN cd prebuilt/x/msrbusy && go get -d ./... -RUN cd prebuilt/x/pmu2metrics && go get -d ./... +COPY third_party/Makefile prebuilt/ +RUN cd prebuilt && make -j4 tools && make oss-source # run bash script and process the input command ENTRYPOINT [ "/bin/bash", "/scripts/entrypoint"] diff --git a/Makefile b/Makefile index 91f5650..ec984b4 100644 --- a/Makefile +++ b/Makefile @@ -3,15 +3,76 @@ # Copyright (C) 2023 Intel Corporation # SPDX-License-Identifier: MIT # +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_NUMBER := $(shell cat ${VERSION_FILE}) +VERSION := $(VERSION_NUMBER)_$(COMMIT_DATE)_$(COMMIT_ID) + TARBALL := svr-info.tgz default: dist .PHONY: clean default dist dist-amd64 test tools -tools: - cd src && make +bin: + mkdir -p bin + +orchestrator: bin reporter collector collector-deps + cp /prebuilt/bin/sshpass cmd/orchestrator/resources/ + cp bin/reporter cmd/orchestrator/resources/ + cp bin/collector cmd/orchestrator/resources/ + cp bin/collector_arm64 cmd/orchestrator/resources/ + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o orchestrator ../cmd/orchestrator + +collector: bin + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o collector ../cmd/collector + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o collector_arm64 ../cmd/collector + +reporter: bin + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o reporter ../cmd/reporter + +msrread: bin + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o msrread ../cmd/msrread + +msrwrite: bin + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o msrwrite ../cmd/msrread + +msrbusy: bin + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o msrbusy ../cmd/msrbusy + +pmu2metrics: bin + rm -f cmd/pmu2metrics/resources/perf + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o pmu2metrics_noperf ../cmd/pmu2metrics + -cp /prebuilt/bin/perf cmd/pmu2metrics/resources + cd bin && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '-s -w -X main.gVersion=$(VERSION)' -o pmu2metrics ../cmd/pmu2metrics + rm -f cmd/pmu2metrics/resources/perf + +calcfreq: bin + cd bin && gcc -D VERSION=\"$(VERSION)\" ../tools/calcfreq/calcfreq.c -lpthread -o calcfreq -static -dist-amd64: tools +collector-deps-amd64: third_party calcfreq msrbusy msrread msrwrite pmu2metrics + $(eval TMPDIR := $(shell mktemp -d build.XXXXXX)) + cp -R /prebuilt/bin/* $(TMPDIR) + cp bin/calcfreq $(TMPDIR) + cp bin/msrbusy $(TMPDIR) + cp bin/msrread $(TMPDIR) + cp bin/msrwrite $(TMPDIR) + cp bin/pmu2metrics_noperf $(TMPDIR)/pmu2metrics + cd $(TMPDIR) && tar -czf ../cmd/orchestrator/resources/collector_deps_amd64.tgz . + rm -rf $(TMPDIR) + +collector-deps-arm64: third_party + $(eval TMPDIR := $(shell mktemp -d build.XXXXXX)) + cp /prebuilt/bin/spectre-meltdown-checker.sh $(TMPDIR) + cd $(TMPDIR) && tar -czf ../cmd/orchestrator/resources/collector_deps_arm64.tgz . + rm -rf $(TMPDIR) + +collector-deps: collector-deps-amd64 collector-deps-arm64 + +third_party: + cd third_party && make + +dist-amd64: orchestrator rm -rf dist/svr-info mkdir -p dist/svr-info cp LICENSE dist/svr-info @@ -19,30 +80,56 @@ dist-amd64: tools cp RELEASE_NOTES dist/svr-info cp THIRD_PARTY_PROGRAMS dist/svr-info cp docs/guide/SvrInfoUserGuide.pdf dist/svr-info/USER_GUIDE.pdf - cp src/orchestrator/orchestrator dist/svr-info/svr-info + cp bin/orchestrator dist/svr-info/svr-info + mkdir -p dist/svr-info/tools + cp bin/collector dist/svr-info/tools + cp bin/collector_arm64 dist/svr-info/tools + cp bin/reporter dist/svr-info/tools + cp bin/pmu2metrics dist/svr-info/tools cd dist && tar -czf $(TARBALL) svr-info cd dist && md5sum $(TARBALL) > $(TARBALL).md5 rm -rf dist/svr-info - rm -rf dist/tools - mkdir -p dist/tools - cp src/orchestrator/resources/* dist/tools - cp src/pmu2metrics/pmu2metrics-with-perf dist/tools - cd dist/tools && tar -xf collector_deps_amd64.tgz && rm collector_deps_*.tgz *.yaml.tmpl - -dist: dist-amd64 -oss: - cd src && make oss-source - mv src/oss_source* dist/ +dist: dist-amd64 third_party + cp /prebuilt/oss_source.* dist clean: - cd src && make clean rm -rf dist + rm -rf bin test: - cd src && make test + # test packages + cd internal/commandfile && go test -v -vet=all . + cd internal/core && go test -v -vet=all . + cd internal/cpu && go test -v -vet=all . + # these tests require access to MSRs which we don't have on WSL2 and may not have on build machine + # cd internal/msr && go test -v -vet=all . + cd internal/progress && go test -v -vet=all . + cd internal/target && go test -v -vet=all . + + # test apps + go test -v -vet=all ./cmd/orchestrator + go test -v -vet=all ./cmd/collector + go test -v -vet=all ./cmd/reporter + go test -v -vet=all ./cmd/msrread + go test -v -vet=all ./cmd/msrwrite + go test -v -vet=all ./cmd/msrbusy + go test -v -vet=all ./cmd/pmu2metrics + + # test svr-info rm -rf test/svr-info cd test && tar -xf ../dist/$(TARBALL) cd test && ./functional cd test && ./fuzz rm -rf test/svr-info + +format_check: + @echo "Running gofmt -l to check for code formatting issues..." + @test -z $(shell gofmt -l -s internal/commandfile/ internal/core/ internal/cpu/ internal/progress/ internal/target/ cmd/orchestrator/ cmd/collector/ cmd/reporter/ cmd/pmu2metrics/ cmd/msrread/ cmd/msrwrite/) || { echo "[WARN] Formatting issues detected. Resolve with 'make format'"; exit 1; } + @echo "gofmt detected no issues" + +check: format_check + +format: + gofmt -l -w -s internal/commandfile/ internal/core/ internal/cpu/ internal/progress/ internal/target/ orchestrator/ collector/ reporter/ pmu2metrics/ rdmsr/ wrmsr/ + diff --git a/RELEASE_NOTES b/RELEASE_NOTES index bcdab8a..13a9745 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -3,10 +3,26 @@ Intel® System Health Inspector (AKA svr-info) Fully Supported Platforms - Xeon Micro-Architectures: EMR,SPR,CPX,ICX,CLX,SKX,BDX,HSX -- Core Micro-Architectures: TGL,RKL,CFL,KBL,SKL,BDW,HSW - Operating Systems: Ubuntu 16.04, 18.04, 20.04, 22.04, CentOS 7, Amazon Linux 2, Debian 11, RHEL 9, Rocky Linux 8 Note: svr-info may work on other micro-architectures and Linux distributions, but has not been thoroughly tested +2.9.0 +Features Added +- Report CPU temperature when CPUs have been active when '-benchmark turbo' is run +- Memory population chart for Dell SPR platform +- Extend prefetchers field to include all known prefetchers + +Bugs Fixed +- Slow report generation when an extremely deep call stack is captured with the -analyze option +- LLC size mis-calculated on EMR MCC SKUs +- Micro-architecture mis-identified on AWS m7i instances + +Known Issues +- Frequency measurement benchmark not yet supported on GNR and SRF +- The storage micro-benchmark may not run on CentOS due to OS locale settings. +- CPU cache sizes are reported in aggregate on Ubuntu 20.04 and newer. +- DRAM population and CPU frequencies may not be accurate on some public cloud IaaS VMs. + 2.8.0 Bugs Fixed - Turbo on/off incorrectly reported on Excel Brief Report summary field diff --git a/THIRD_PARTY_PROGRAMS b/THIRD_PARTY_PROGRAMS index eea3ab6..4b4b98f 100644 --- a/THIRD_PARTY_PROGRAMS +++ b/THIRD_PARTY_PROGRAMS @@ -2,43 +2,30 @@ Intel® System Health Inspector Third Party Programs File This file is the "third-party-programs.txt" file specified in the associated Intel end user license agreement for the Intel software you are licensing. Third party programs and their corresponding required notices and/or license terms are listed below. ------------------------------------------------------------- -DMidecode -Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. -------------------------------------------------------------- -stress-ng -Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -------------------------------------------------------------- -sysstat -License: GPLv2+ -Copyright: - * (C) 1998-2022 by Sebastien GODARD (sysstat orange.fr) +async-profiler + * Copyright 2017 Andrei Pangin * - *************************************************************************** - * This program is free software; you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the * - * Free Software Foundation; either version 2 of the License, or (at your * - * option) any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, write to the Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA * - *************************************************************************** -------------------------------------------------------------- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ------------------------------------------------------------ cpuid ** Copyright 2003,2004,2005,2006,2010,2011,2012,2013,2014,2015,2016,2017,2018, ** 2020 by Todd Allen. -Linux Kernel (turbostat) -Copyright (c) 2013 Intel Corporation. - * Len Brown +------------------------------------------------------------- +dmidecode +Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. ------------------------------------------------------------- ethtool * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) @@ -71,6 +58,57 @@ Copyright (C) 1989, 1991 Free Software Foundation, Inc., Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ------------------------------------------------------------- +iostat +* iostat: report CPU and I/O statistics + * (C) 1998-2023 by Sebastien GODARD (sysstat orange.fr) + * + *************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA * + *************************************************************************** +------------------------------------------------------------- +ipmitool +Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistribution of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +Redistribution in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +Neither the name of Sun Microsystems, Inc. or the names of +contributors may be used to endorse or promote products derived +from this software without specific prior written permission. + +This software is provided "AS IS," without a warranty of any kind. +ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +------------------------------------------------------------- lshw Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -171,105 +209,92 @@ NO WARRANTY END OF TERMS AND CONDITIONS ------------------------------------------------------------- -sshpass -Copyright – not available/not listed -shpass was written by Shachar Shemesh for Lingnu Open Source Consulting Ltd. - -GNU General Public License v2.0 or later - -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 - -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. - -Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and modification follow. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. - -1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - -a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. - -b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. - -c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - -a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - -b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - -c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - -If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - -4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - -5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - -6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - -7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. +lspci + * Copyright (c) 1997--2020 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL v2+. + * + * SPDX-License-Identifier: GPL-2.0-or-later +------------------------------------------------------------- +mpstat + * (C) 2000-2023 by Sebastien GODARD (sysstat orange.fr) + * Copyright (C) 2022 Oracle and/or its affiliates. + * + *************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA * + *************************************************************************** +------------------------------------------------------------- +perf +The Linux Kernel is provided under: -8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note -9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Being under the terms of the GNU General Public License version 2 only, +according with: -Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + LICENSES/preferred/GPL-2.0 -10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. +With an explicit syscall exception, as stated at: -NO WARRANTY + LICENSES/exceptions/Linux-syscall-note -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +In addition, other licenses may also apply. Please see: -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + Documentation/process/license-rules.rst -END OF TERMS AND CONDITIONS +for more details. +All contributions to the Linux Kernel are subject to this COPYING file. +------------------------------------------------------------- +sadc + * (C) 1999-2023 by Sebastien GODARD (sysstat orange.fr) + * + *************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA * + *************************************************************************** +------------------------------------------------------------- +sar + * (C) 1999-2023 by Sebastien GODARD (sysstat orange.fr) + * + *************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA * + *************************************************************************** ------------------------------------------------------------- spectre-meltdown-checker Copyright not available/not identified @@ -388,113 +413,156 @@ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY C If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS ------------------------------------------------------------- -ipmitool -Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. +sshpass +Copyright – not available/not listed +shpass was written by Shachar Shemesh for Lingnu Open Source Consulting Ltd. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +GNU General Public License v2.0 or later -Redistribution of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 -Redistribution in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Neither the name of Sun Microsystems, Inc. or the names of -contributors may be used to endorse or promote products derived -from this software without specific prior written permission. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. -This software is provided "AS IS," without a warranty of any kind. -ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, -INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. -SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE -FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING -OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL -SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, -OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR -PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF -LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, -EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -------------------------------------------------------------- -Intel® Memory Latency Checker -Copyright: Not available/not listed +Preamble -idzla-Software License for Intel® Memory Latency Checker (Intel® MLC) -SOFTWARE TOOLS LICENSE AGREEMENT +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. -DO NOT DOWNLOAD, INSTALL, ACCESS, COPY, OR USE ANY PORTION OF THE MATERIALS (DEFINED BELOW) UNTIL YOU HAVE READ AND ACCEPTED THE TERMS AND CONDITIONS OF THIS AGREEMENT. BY INSTALLING, COPYING, ACCESSING, OR USING THE MATERIALS, YOU AGREE TO BE LEGALLY BOUND BY THE TERMS AND CONDITIONS OF THIS AGREEMENT. If You do not agree to be bound by, or the entity for whose benefit You act has not authorized You to accept, these terms and conditions, do not install, access, copy, or use the Software and destroy all copies of the Software in Your possession. +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. -This DEVELOPMENT TOOLS LICENSE AGREEMENT (this “Agreement”) is entered into between Intel Corporation, a Delaware corporation (“Intel”) and You. “You” refers to you or your employer or other entity for whose benefit you act, as applicable. If you are agreeing to the terms and conditions of this Agreement on behalf of a company or other legal entity, you represent and warrant that you have the legal authority to bind that legal entity to the Agreement, in which case, "You" or "Your" shall be in reference to such entity. Intel and You are referred to herein individually as a “Party” or, together, as the “Parties”. +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. -The Parties, in consideration of the mutual covenants contained in this Agreement, and for other good and valuable consideration, the receipt and sufficiency of which they acknowledge, and intending to be legally bound, agree as follows: +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. -1. DEFINITIONS. The following definitions are used throughout this Agreement: +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. -“Affiliate” means any entity controlling, controlled by or under common control with a Party hereto, where “control” means the direct or indirect ownership of more than fifty percent (50%) of such entity’s capital or equivalent voting rights. An entity will be deemed an “Affiliate” only as long as such control exists during the term of this Agreement. +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. -“Contractor” means a third party consultant or subcontractor who requires access to or use of the Materials to perform work on Your behalf or at Your behest. +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. -“Development Tools” means the development, evaluation, production, or test tool software, and associated documentation or other collateral, identified in the “development_tools.txt” text files, if any, included in the Materials. +The precise terms and conditions for copying, distribution and modification follow. -“Derivatives” means derivative works as defined in 17 U.S.C § 101 et seq. +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -“Intel-based Device” means a device designed, manufactured, or configured by You or Your Affiliates to include or operate Intel hardware, software, or services. +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". -"Materials" means the software, documentation, the software product serial number and license key codes (if applicable), Development Tools, Redistributables, and other materials or collateral, including any updates and upgrades thereto, in source code or object code form where applicable, that are provided or otherwise made available by Intel to You under this Agreement. “Materials” do not include Open Source Software or any computer programming code that is subject to an agreement, obligation or license (whether or not accompanying the Materials) intended to supersede this Agreement. +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. -"Redistributables" means the software, documentation, or other collateral identified in the “redist.txt” text files, if any, included in the Materials. +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. -2. LIMITED LICENSE. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. -(A) Subject to the terms and conditions of this Agreement, Intel grants You and Your Affiliates, a limited, nonexclusive, nontransferable, revocable, worldwide, fully paid-up license during the term of this Agreement, without the right to sublicense, under Intel’s copyrights (subject to any third party licensing requirements), unless expressly stated otherwise, to: +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: -(1) internally reproduce and install a reasonable number of copies of the Materials for Your internal use solely for the purposes of designing, developing, manufacturing and testing Intel-based Devices; +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. -(2) internally reproduce the source code of the Development Tools, if provided to You by Intel, and to internally create and reproduce Derivatives of the Development Tools, and to internally reproduce the binary code of the Development Tools, or any Derivatives created by You, in each case solely for the purpose of designing, developing, manufacturing and testing the Intel-based Device, solely as necessary for the integration of any Intel software and the output generated by the Development Tools, with and into Intel-based Devices; +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) -(3) create Derivatives of the Redistributables, or any portions thereof, provided to You by Intel in source code form solely for the purposes of designing, developing, debugging, modifying, distributing and testing software containing significantly more functionality and features than the Redistributables in the form provided to You by Intel; +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. -(4) distribute (or otherwise make available) on a royalty-free basis, subject to any other terms and conditions which may appear in the Redistributables text files, the Redistributables, including any Derivatives of the Redistributables pursuant to Section 2(A)(3), or any portions thereof, only as integrated or embedded in software (and not on a stand-alone basis) solely for use on an Intel-based Device; and +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. -(5) have the tasks set forth in Section 2(A)(1) and (2) above performed by a Contractor on the conditions that You enter into a written confidentiality agreement with any such Contractor, subject to Section 7 (Confidentiality), and You remain fully liable to Intel for the actions and inactions of Your Contractors. +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. -(B) You will be liable for Your Affiliate’s breach of these terms. In addition, You acknowledge that Your Affiliates are beneficiaries of the licenses granted by Intel under Section 2. +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. -(C) Intel hereby grants You the right to sub-license (without rights to further sublicense) the Development Tools, including any accompanying documentation, to Your manufacturing partners, in the code format provided to You by Intel, solely for designing, developing, manufacturing and testing the Intel-based Devices solely as necessary for the integration of any Intel software and the output generated by the Development Tools, with and into Intel-based Devices. The sublicense is subject to a written sublicensing agreement that contains confidentiality obligations and license restrictions that are no less protective of Intel than those provided in this Agreement. You will be fully responsible and liable towards Intel for Your sub-licensees’ compliance with all such confidentiality obligations and license restrictions. You may grant Your manufacturing partners the right to further distribute Redistributables solely as integrated or embedded in software for Your Intel-based Devices. +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. -3. LICENSE RESTRICTIONS. All right, title and interest in and to the Materials and associated documentation are and will remain the exclusive property of Intel and its suppliers. Unless expressly permitted under the Agreement, You will not, and will not allow any third party to (i) use, copy, distribute, sell or offer to sell the Materials or associated documentation; (ii) modify, adapt, enhance, disassemble, decompile, reverse engineer, change or create derivative works from the Materials except and only to the extent as specifically required by mandatory applicable laws or any applicable third party license terms accompanying the Materials; (iii) use or make the Materials available for the use or benefit of third parties; or (iv) use the Materials on Your products other than those that include the Intel product(s), platform(s), or software identified in the Materials; or (v) publish or provide any Materials benchmark or comparison test results. +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. -If You received the Materials solely for evaluation purposes, You have no distribution rights to the Materials or any portion thereof. +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. -Distribution of the Redistributables is also subject to the following conditions: You shall: (i) be solely responsible to Your customers and end users for any update or support obligation or other liability which may arise from the distribution, (ii) not make any statement that Your software is "certified", or that its performance is guaranteed, by Intel, (iii) not use Intel's name or trademarks to promote Your software without prior written permission, (iv) use a license agreement that contains provisions that are at least as restrictive as this Agreement and which prohibits disassembly and reverse engineering of the Materials provided in object code form, and (v) indemnify, hold harmless, and defend Intel, Intel’s Affiliates, and its licensors from and against any claims or lawsuits, including attorney's fees, that arise or result from Your Derivatives or Your distribution of Your software. +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. -The consideration under this Agreement is only for the licenses Intel expressly grants above. Any other rights including, but not limited to, additional patent rights, will require an additional license and additional consideration. Nothing in this Agreement requires or will be treated to require Intel to grant any additional license. You acknowledge that an essential basis of the bargain in this Agreement is that Intel grants You no licenses or other rights including, but not limited to, patent, copyright, trade secret, trademark, trade name, service mark or other intellectual property licenses or rights with respect to the Materials and associated documentation, by implication, estoppel or otherwise, except for the licenses expressly granted above. You acknowledge there are significant uses of the Materials in their original, unmodified and uncombined form. The consideration for the licenses in this Agreement reflects Intel’s continuing right to assert patent claims against any modifications or derivative works (including, without limitation, error corrections and bug fixes) of, or combinations with, the Materials that You, Your Affiliates or third parties make that infringe any Intel patent claim. +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. -4. LICENSE TO FEEDBACK. This Agreement does not obligate You to provide Intel with materials, information, comments, suggestions, Your Derivatives or other communication regarding the features, functions, performance or use of the Materials (“Feedback”). If any software included in the Materials is provided or otherwise made available by Intel in source code form, to the extent You provide Intel with Feedback in a tangible form, You grant to Intel and its affiliates a non-exclusive, perpetual, sublicenseable, irrevocable, worldwide, royalty-free, fully paid-up and transferable license, to and under all of Your intellectual property rights, whether perfected or not, to publicly perform, publicly display, reproduce, use, make, have made, sell, offer for sale, distribute, import, create derivative works of and otherwise exploit any comments, suggestions, descriptions, ideas, Your Derivatives or other feedback regarding the Materials provided by You or on Your behalf. +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. -5. OPEN SOURCE STATEMENT. The Materials may include Open Source Software (OSS) licensed pursuant to OSS license agreement(s) identified in the OSS comments in the applicable source code file(s) and/or file header(s) provided with or otherwise associated with the Materials. Neither You nor any Original Equipment Manufacturer (OEM), Original Device Manufacturer (ODM), customer, or distributor may subject any proprietary portion of the Materials to any OSS license obligations including, without limitation, combining or distributing the Materials with OSS in a manner that subjects Intel, the Materials or any portion thereof to any OSS license obligation. Nothing in this Agreement limits any rights under, or grants rights that supersede, the terms of any applicable OSS license. +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -6. THIRD PARTY SOFTWARE. Certain third party software provided with or within the Materials may only be used (a) upon securing a license directly from the owner of the software or (b) in combination with hardware components purchased from such third party and (c) subject to further license limitations by the software owner. A listing of any such third party limitations is in one or more text files accompanying the Materials. You acknowledge Intel is not providing You with a license to such third party software and further that it is Your responsibility to obtain appropriate licenses from such third parties directly. +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. -7. CONFIDENTIALITY. The terms and conditions of this Agreement, exchanged confidential information, as well as the Materials are subject to the terms and conditions of the Non-Disclosure Agreement(s) or Intel Pre-Release Loan Agreement(s) (referred to herein collectively or individually as “NDA”) entered into by and in force between Intel and You, and in any case no less confidentiality protection than You apply to Your information of similar sensitivity. If You would like to have a Contractor perform work on Your behalf that requires any access to or use of Materials You must obtain a written confidentiality agreement from the Contractor which contains terms and conditions with respect to access to or use of Materials no less restrictive than those set forth in this Agreement, excluding any distribution rights and use for any other purpose, and You will remain fully liable to Intel for the actions and inactions of those Contractors. You may not use Intel's name in any publications, advertisements, or other announcements without Intel's prior written consent. +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. -8. NO OBLIGATION; NO AGENCY. Intel may make changes to the Software, or items referenced therein, at any time without notice. Intel is not obligated to support, update, provide training for, or develop any further version of the Software or to grant any license thereto. No agency, franchise, partnership, joint- venture, or employee-employer relationship is intended or created by this Agreement. +NO WARRANTY -9. EXCLUSION OF WARRANTIES. THE MATERIALS ARE PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTY OF ANY KIND INCLUDING WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE. Intel does not warrant or assume responsibility for the accuracy or completeness of any information, text, graphics, links or other items within the Materials. +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -10. LIMITATION OF LIABILITY. IN NO EVENT WILL INTEL OR ITS AFFILIATES, LICENSORS OR SUPPLIERS (INCLUDING THEIR RESPECTIVE DIRECTORS, OFFICERS, EMPLOYEES, AND AGENTS) BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, LOST PROFITS, BUSINESS INTERRUPTION, OR LOST DATA) ARISING OUT OF OR IN RELATION TO THIS AGREEMENT, INCLUDING THE USE OF OR INABILITY TO USE THE MATERIALS, EVEN IF INTEL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS PROHIBIT EXCLUSION OR LIMITATION OF LIABILITY FOR IMPLIED WARRANTIES OR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE LIMITATION MAY IN PART NOT APPLY TO YOU. YOU MAY ALSO HAVE OTHER LEGAL RIGHTS THAT VARY FROM JURISDICTION TO JURISDICTION. THE MATERIALS LICENSED HEREUNDER ARE NOT DESIGNED OR INTENDED FOR USE IN ANY MEDICAL, LIFE SAVING OR LIFE SUSTAINING SYSTEMS, TRANSPORTATION SYSTEMS, NUCLEAR SYSTEMS, OR FOR ANY OTHER MISSION CRITICAL APPLICATION IN WHICH THE FAILURE OF THE DEVELOPMENT TOOLS COULD LEAD TO PERSONAL INJURY OR DEATH. YOU WILL INDEMNIFY AND HOLD INTEL AND ITS AFFILIATES, LICENSORS AND SUPPLIERS (INCLUDING THEIR RESPECTIVE DIRECTORS, OFFICERS, EMPLOYEES, AND AGENTS) HARMLESS AGAINST ALL CLAIMS, LIABILITIES, LOSSES, COSTS, DAMAGES, AND EXPENSES (INCLUDING REASONABLE ATTORNEY FEES), ARISING OUT OF, DIRECTLY OR INDIRECTLY, THE DISTRIBUTION OF THE MATERIALS AND ANY CLAIM OF PRODUCT LIABILITY, PERSONAL INJURY OR DEATH ASSOCIATED WITH ANY UNINTENDED USE, EVEN IF SUCH CLAIM ALLEGES THAT INTEL OR AN INTEL AFFILIATE, LICENSOR OR SUPPLIER WAS NEGLIGENT REGARDING THE DESIGN OR MANUFACTURE OF THE MATERIALS. THE LIMITED REMEDIES, WARRANTY DISCLAIMER AND LIMITED LIABILITY ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN INTEL AND YOU AND INTEL WOULD NOT BE ABLE TO PROVIDE THE MATERIALS WITHOUT SUCH LIMITATIONS. +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -11. TERMINATION AND SURVIVAL. Intel may terminate this Agreement for any reason with thirty (30) days’ notice and immediately if You or someone acting on Your behalf or at Your behest violates any of its terms or conditions. Upon termination You will immediately destroy and ensure the destruction of the Materials (including providing certification of such destruction or return back to Intel). Upon termination of this Agreement, all licenses granted to You hereunder terminate immediately. All Sections of this Agreement, except Section 2, will survive termination. In the event of termination of this Agreement, the license grant to any Redistributables, including Your Derivatives of the Redistributables, distributed by You prior to the effective date of such termination and in accordance with the terms and conditions of this Agreement shall survive any such termination of this Agreement. -12. GOVERNING LAW AND JURISDICTION. This Agreement and any dispute arising out of or relating to it will be governed by the laws of the U.S.A. and Delaware, without regard to conflict of laws principles. The Parties exclude the application of the United Nations Convention on Contracts for the International Sale of Goods (1980). The state and federal courts sitting in Delaware, U.S.A. will have exclusive jurisdiction over any dispute arising out of or relating to this Agreement. The Parties consent to personal jurisdiction and venue in those courts. A Party that obtains a judgment against the other Party in the courts identified in this section may enforce that judgment in any court that has jurisdiction over the Parties. -13. EXPORT REGULATIONS/EXPORT CONTROL. You agree that neither You nor Your subsidiaries or Affiliates will export/re-export the Materials, directly or indirectly, to any country for which the U.S. Department of Commerce or any other agency or department of the U.S. Government or the foreign government from where it is shipping requires an export license, or other governmental approval, without first obtaining any such required license or approval. In the event the Materials are exported from the U.S.A. or re-exported from a foreign destination by You, Your subsidiaries, or Your Affiliates, You will ensure that the distribution and export/re-export or import of the Materials complies with all laws, regulations, orders, or other restrictions of the U.S. Export Administration Regulations and the appropriate foreign government. -14. GOVERNMENT RESTRICTED RIGHTS. The Materials are a commercial item (as defined in 48 C.F.R. 2.101) consisting of commercial computer software and commercial computer software documentation (as those terms are used in 48 C.F.R. 12.212). Consistent with 48 C.F.R. 12.212 and 48 C.F.R 227.7202- 1 through 227.7202-4, You will not provide the Materials to the U.S. Government. Contractor or Manufacturer is Intel Corporation, 2200 Mission College Blvd., Santa Clara, CA 95054. -15. TRADEMARKS. Third party trademarks, trade names, product names and logos (the “Trademarks”) contained in or used by the Materials are the trademarks or registered trademarks of their respective owners, and the use of such Trademarks shall inure to the benefit of the trademark owner. The reference to such Trademarks (if any) by Intel in any of the Materials does not constitute: (i) an affiliation by Intel and its licensors with such company, or (ii) an endorsement or approval of such company of Intel and its licensors and its products or services. -16. ASSIGNMENT. You may not delegate, assign or transfer this Agreement, the license(s) granted or any of Your rights or duties hereunder, expressly, by implication, by operation of law, or otherwise and any attempt to do so, without Intel’s express prior written consent, will be null and void. Intel may assign, delegate and transfer this Agreement, and its rights and obligations hereunder, in its sole discretion. -17. ENTIRE AGREEMENT; SEVERABILITY. The terms and conditions of this Agreement and any NDA with Intel constitute the entire agreement between the Parties with respect to the subject matter hereof, and merge and supersede all prior or contemporaneous agreements, understandings, negotiations and discussions. Neither Party will be bound by any terms, conditions, definitions, warranties, understandings, or representations with respect to the subject matter hereof other than as expressly provided herein. In the event any provision of this Agreement is unenforceable or invalid under any applicable law or applicable court decision, such unenforceability or invalidity will not render this Agreement unenforceable or invalid as a whole, instead such provision will be changed and interpreted so as to best accomplish the objectives of such provision within legal limits. -18. WAIVER. The failure of a Party to require performance by the other Party of any provision hereof will not affect the full right to require such performance at any time thereafter; nor will waiver by a Party of a breach of any provision hereof constitute a waiver of the provision itself. -19. PRIVACY. YOUR PRIVACY RIGHTS ARE SET FORTH IN INTEL’S PRIVACY NOTICE, WHICH FORMS A PART OF THIS AGREEMENT. PLEASE REVIEW THE PRIVACY NOTICE AT HTTP://WWW.INTEL.COM/PRIVACY TO LEARN HOW INTEL COLLECTS, USES AND SHARES INFORMATION ABOUT YOU. +END OF TERMS AND CONDITIONS ------------------------------------------------------------- -Other names and brands may be claimed as the property of others. +stackcollapse-perf.pl +# Copyright 2012 Joyent, Inc. All rights reserved. +# Copyright 2012 Brendan Gregg. All rights reserved. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at docs/cddl1.txt or +# http://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at docs/cddl1.txt. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +------------------------------------------------------------- +stress-ng +Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +------------------------------------------------------------- +sysstat +License: GPLv2+ +Copyright: + * (C) 1998-2022 by Sebastien GODARD (sysstat orange.fr) + * + *************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 2 of the License, or (at your * + * option) any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA * + *************************************************************************** +------------------------------------------------------------- +turbostat +Copyright (c) 2023 Intel Corporation. + * Len Brown +------------------------------------------------------------- + +Other names and brands may be claimed as the property of others. \ No newline at end of file diff --git a/bin/.README b/bin/.README deleted file mode 100644 index bd0523f..0000000 --- a/bin/.README +++ /dev/null @@ -1 +0,0 @@ -The build will add files placed in this directory to the collector dependencies tarball...for use when the collector is running on target. \ No newline at end of file diff --git a/src/collector/collector_linux.go b/cmd/collector/collector_linux.go similarity index 98% rename from src/collector/collector_linux.go rename to cmd/collector/collector_linux.go index c84f231..3060b57 100644 --- a/src/collector/collector_linux.go +++ b/cmd/collector/collector_linux.go @@ -12,7 +12,7 @@ import ( "path/filepath" "strings" - "intel.com/svr-info/pkg/target" + "github.com/intel/svr-info/internal/target" ) func getUserPath() string { diff --git a/src/collector/collector_windows.go b/cmd/collector/collector_windows.go similarity index 96% rename from src/collector/collector_windows.go rename to cmd/collector/collector_windows.go index 2cffc4d..922c707 100644 --- a/src/collector/collector_windows.go +++ b/cmd/collector/collector_windows.go @@ -9,7 +9,7 @@ import ( "os/exec" "strings" - "intel.com/svr-info/pkg/target" + "github.com/intel/svr-info/internal/target" ) func runCommand(label string, command string, superuser bool, sudoPassword string, binPath string, timeout int) (stdout string, stderr string, exitCode int, err error) { diff --git a/src/collector/main.go b/cmd/collector/main.go similarity index 98% rename from src/collector/main.go rename to cmd/collector/main.go index 37e9f72..53b944d 100644 --- a/src/collector/main.go +++ b/cmd/collector/main.go @@ -14,9 +14,9 @@ import ( "path/filepath" "strings" + "github.com/intel/svr-info/internal/commandfile" + "github.com/intel/svr-info/internal/util" "gopkg.in/yaml.v2" - "intel.com/svr-info/pkg/commandfile" - "intel.com/svr-info/pkg/core" ) // globals @@ -223,7 +223,7 @@ func mainReturnWithCode() int { return 1 } } else if flag.NArg() == 1 { - absFilename, err := core.AbsPath(flag.Arg(0)) + absFilename, err := util.AbsPath(flag.Arg(0)) if err != nil { log.Printf("Error: %v", err) return 1 diff --git a/src/collector/test/test_linux.yaml b/cmd/collector/test/test_linux.yaml similarity index 100% rename from src/collector/test/test_linux.yaml rename to cmd/collector/test/test_linux.yaml diff --git a/src/collector/test/test_windows.yaml b/cmd/collector/test/test_windows.yaml similarity index 100% rename from src/collector/test/test_windows.yaml rename to cmd/collector/test/test_windows.yaml diff --git a/src/msrbusy/main.go b/cmd/msrbusy/main.go similarity index 98% rename from src/msrbusy/main.go rename to cmd/msrbusy/main.go index 85b987c..283b330 100644 --- a/src/msrbusy/main.go +++ b/cmd/msrbusy/main.go @@ -12,7 +12,7 @@ import ( "strconv" "strings" - "intel.com/svr-info/pkg/msr" + "github.com/intel/svr-info/internal/msr" ) type CmdLineArgs struct { diff --git a/src/rdmsr/main.go b/cmd/msrread/main.go similarity index 98% rename from src/rdmsr/main.go rename to cmd/msrread/main.go index 51cdd5e..5d55861 100644 --- a/src/rdmsr/main.go +++ b/cmd/msrread/main.go @@ -12,7 +12,7 @@ import ( "strconv" "strings" - "intel.com/svr-info/pkg/msr" + "github.com/intel/svr-info/internal/msr" ) type CmdLineArgs struct { diff --git a/src/wrmsr/main.go b/cmd/msrwrite/main.go similarity index 98% rename from src/wrmsr/main.go rename to cmd/msrwrite/main.go index 1048208..90cfbed 100644 --- a/src/wrmsr/main.go +++ b/cmd/msrwrite/main.go @@ -11,7 +11,7 @@ import ( "path/filepath" "strconv" - "intel.com/svr-info/pkg/msr" + "github.com/intel/svr-info/internal/msr" ) type CmdLineArgs struct { diff --git a/src/orchestrator/collection.go b/cmd/orchestrator/collection.go similarity index 96% rename from src/orchestrator/collection.go rename to cmd/orchestrator/collection.go index 9586cae..558bf94 100644 --- a/src/orchestrator/collection.go +++ b/cmd/orchestrator/collection.go @@ -15,10 +15,10 @@ import ( "strings" "text/template" + "github.com/intel/svr-info/internal/commandfile" + "github.com/intel/svr-info/internal/target" + "github.com/intel/svr-info/internal/util" "gopkg.in/yaml.v2" - "intel.com/svr-info/pkg/commandfile" - "intel.com/svr-info/pkg/core" - "intel.com/svr-info/pkg/target" ) type Collection struct { @@ -261,8 +261,13 @@ func (c *Collection) getExtrasFiles() (extras []string, err error) { log.Printf("Failed to get path to extra files: %v", err) return } - err2 := core.DirectoryExists(extrasDir) - if err2 != nil { + var exists bool + exists, err = util.DirectoryExists(extrasDir) + if err != nil { + err = fmt.Errorf("failed to determine if extras directory exists: %v", err) + return + } + if !exists { log.Printf("Extra collection files dir (%s) not found.", extrasDir) return } @@ -377,8 +382,13 @@ func (c *Collection) Collect() (err error) { log.Printf("Failed to get extras dir: %v", err) return } - err2 := core.DirectoryExists(extrasDir) - if err2 == nil { + var exists bool + exists, err = util.DirectoryExists(extrasDir) + if err != nil { + err = fmt.Errorf("failed to determine if extras directory exists: %v", err) + return + } + if exists { var extraFilenames []string extraFilenames, err = c.getExtrasFiles() if err != nil { diff --git a/src/orchestrator/command_line_args.go b/cmd/orchestrator/command_line_args.go similarity index 93% rename from src/orchestrator/command_line_args.go rename to cmd/orchestrator/command_line_args.go index ed7351b..74444e6 100644 --- a/src/orchestrator/command_line_args.go +++ b/cmd/orchestrator/command_line_args.go @@ -12,7 +12,8 @@ import ( "path/filepath" "strings" - "intel.com/svr-info/pkg/core" + "github.com/intel/svr-info/internal/core" + "github.com/intel/svr-info/internal/util" ) type CmdLineArgs struct { @@ -37,7 +38,7 @@ type CmdLineArgs struct { output string targetTemp string temp string - dumpConfig bool + printConfig bool noConfig bool cmdTimeout int reporter string @@ -57,7 +58,7 @@ func showUsage() { fmt.Fprintf(os.Stderr, " [-analyze SELECT] [-analyze_duration SECONDS] [-analyze_frequency N]\n") fmt.Fprintf(os.Stderr, " [-megadata]\n") fmt.Fprintf(os.Stderr, " [-ip IP] [-port PORT] [-user USER] [-key KEY] [-targets TARGETS]\n") - fmt.Fprintf(os.Stderr, " [-output OUTPUT] [-temp TEMP] [-targettemp TEMP] [-dumpconfig] [-cmd_timeout]\n") + fmt.Fprintf(os.Stderr, " [-output OUTPUT] [-temp TEMP] [-targettemp TEMP] [-printconfig] [-noconfig] [-cmd_timeout]\n") fmt.Fprintf(os.Stderr, " [-reporter \"args\"] [-collector \"args\"] [-debug]\n") longHelp := ` @@ -89,7 +90,7 @@ analyze arguments: -analyze_frequency N the number of samples taken per second (default: 11) additional data collection arguments: - -megadata collect additional data specified in megadata template file (default: False) + -megadata collect additional data in megadata directory (default: False) remote target arguments: -ip IP ip address or hostname (default: Nil) @@ -106,7 +107,7 @@ advanced arguments: -output DIR path to output directory. Directory must exist. (default: $PWD/orchestrator_timestamp) -temp DIR path to temporary directory on localhost. Directory must exist. (default: system default) -targettemp DIR path to temporary directory on target. Directory must exist. (default: system default) - -dumpconfig dump the collector configuration file and exit (default: False) + -printconfig print the collector configuration file and exit (default: False) -noconfig do not collect system configuration data. (default: False) -cmd_timeout the maximum number of seconds to wait for each data collection command (default: 300) -reporter run the the reporter sub-component with args @@ -147,7 +148,7 @@ func (cmdLineArgs *CmdLineArgs) parse(name string, arguments []string) (err erro flagSet.StringVar(&cmdLineArgs.output, "output", "", "") flagSet.StringVar(&cmdLineArgs.temp, "temp", "", "") flagSet.StringVar(&cmdLineArgs.targetTemp, "targettemp", "", "") - flagSet.BoolVar(&cmdLineArgs.dumpConfig, "dumpconfig", false, "") + flagSet.BoolVar(&cmdLineArgs.printConfig, "printconfig", false, "") flagSet.BoolVar(&cmdLineArgs.noConfig, "noconfig", false, "") flagSet.IntVar(&cmdLineArgs.cmdTimeout, "cmd_timeout", 300, "") flagSet.StringVar(&cmdLineArgs.format, "format", "html,xlsx,json", "") @@ -183,7 +184,7 @@ func (cmdLineArgs *CmdLineArgs) parse(name string, arguments []string) (err erro func argDirExists(dir string, label string) (err error) { if dir != "" { var path string - path, err = core.AbsPath(dir) + path, err = util.AbsPath(dir) if err != nil { return } @@ -314,15 +315,20 @@ func (cmdLineArgs *CmdLineArgs) validate() (err error) { // -key if cmdLineArgs.key != "" { var path string - path, err = core.AbsPath(cmdLineArgs.key) + path, err = util.AbsPath(cmdLineArgs.key) if err != nil { return } - err = core.FileExists(path) + var exists bool + exists, err = util.FileExists(path) if err != nil { err = fmt.Errorf("-key %s : %s", path, err.Error()) return } + if !exists { + err = fmt.Errorf("-key %s : file does not exist", path) + return + } if cmdLineArgs.ipAddress == "" || cmdLineArgs.user == "" { err = fmt.Errorf("-key %s : user and ip required when key provided", cmdLineArgs.key) return @@ -331,15 +337,20 @@ func (cmdLineArgs *CmdLineArgs) validate() (err error) { // -targets if cmdLineArgs.targets != "" { var path string - path, err = core.AbsPath(cmdLineArgs.targets) + path, err = util.AbsPath(cmdLineArgs.targets) if err != nil { return } - err = core.FileExists(path) + var exists bool + exists, err = util.FileExists(path) if err != nil { err = fmt.Errorf("-targets %s : %s", path, err.Error()) return } + if !exists { + err = fmt.Errorf("-targets %s : file does not exist", path) + return + } } // -collector and -reporter are mutually exclusive if cmdLineArgs.collector != "" && cmdLineArgs.reporter != "" { diff --git a/src/orchestrator/command_line_args_test.go b/cmd/orchestrator/command_line_args_test.go similarity index 97% rename from src/orchestrator/command_line_args_test.go rename to cmd/orchestrator/command_line_args_test.go index ad5d386..6d4c3a1 100644 --- a/src/orchestrator/command_line_args_test.go +++ b/cmd/orchestrator/command_line_args_test.go @@ -86,11 +86,11 @@ func TestAllExceptTargetsFile(t *testing.T) { "-megadata", "-debug", "-cmd_timeout", "150", - "-dumpconfig", + "-printconfig", "-ip", "192.168.1.1", "-port", "20", "-user", "foo", - "-key", "go.mod", // any file + "-key", "targets.example", // any file } if !isValid(args) { t.Fail() diff --git a/src/orchestrator/main.go b/cmd/orchestrator/main.go similarity index 97% rename from src/orchestrator/main.go rename to cmd/orchestrator/main.go index 389bf5e..3f53be9 100644 --- a/src/orchestrator/main.go +++ b/cmd/orchestrator/main.go @@ -19,11 +19,11 @@ import ( "strings" "time" + "github.com/intel/svr-info/internal/progress" + "github.com/intel/svr-info/internal/target" + "github.com/intel/svr-info/internal/util" "golang.org/x/exp/slices" "golang.org/x/term" - "intel.com/svr-info/pkg/core" - "intel.com/svr-info/pkg/progress" - "intel.com/svr-info/pkg/target" ) //go:embed resources @@ -290,7 +290,7 @@ func cleanupOutputDir(outputDir string, collections []*Collection, reportFilePat } func (app *App) doWork() (err error) { - if app.args.dumpConfig { + if app.args.printConfig { var bytes []byte bytes, err = resources.ReadFile("resources/collector_reports.yaml.tmpl") if err != nil { @@ -398,8 +398,8 @@ func (app *App) runSubComponent() (exitCode int, err error) { if err != nil { return } - fmt.Fprintf(os.Stdout, stdout) - fmt.Fprintf(os.Stderr, stderr) + fmt.Fprint(os.Stdout, stdout) + fmt.Fprint(os.Stderr, stderr) return } @@ -435,7 +435,7 @@ func mainReturnWithCode() int { var outputDir string if cmdLineArgs.output != "" { var err error - outputDir, err = core.AbsPath(cmdLineArgs.output) + outputDir, err = util.AbsPath(cmdLineArgs.output) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) return retError @@ -444,7 +444,7 @@ func mainReturnWithCode() int { outputDirName := filepath.Base(os.Args[0]) + "_" + time.Now().Local().Format("2006-01-02_15-04-05") var err error // outputDir will be created in current working directory - outputDir, err = core.AbsPath(outputDirName) + outputDir, err = util.AbsPath(outputDirName) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) return retError diff --git a/src/orchestrator/resources/collector_megadata.yaml.tmpl b/cmd/orchestrator/resources/collector_megadata.yaml.tmpl similarity index 100% rename from src/orchestrator/resources/collector_megadata.yaml.tmpl rename to cmd/orchestrator/resources/collector_megadata.yaml.tmpl diff --git a/src/orchestrator/resources/collector_reports.yaml.tmpl b/cmd/orchestrator/resources/collector_reports.yaml.tmpl similarity index 91% rename from src/orchestrator/resources/collector_reports.yaml.tmpl rename to cmd/orchestrator/resources/collector_reports.yaml.tmpl index b12c66f..f916c3e 100644 --- a/src/orchestrator/resources/collector_reports.yaml.tmpl +++ b/cmd/orchestrator/resources/collector_reports.yaml.tmpl @@ -127,81 +127,81 @@ commands: superuser: true parallel: true - label: rdmsr 0x1a4 - command: rdmsrx -f 3:0 0x1a4 # MSR_MISC_FEATURE_CONTROL: L2 and DCU Prefetcher enabled/disabled + command: msrread -f 7:0 0x1a4 # MSR_PREFETCH_CONTROL: L2, DCU, and AMP Prefetchers enabled/disabled superuser: true modprobe: msr parallel: true - label: rdmsr 0x1b0 - command: rdmsrx -f 3:0 0x1b0 # IA32_ENERGY_PERF_BIAS: Performance Energy Bias Hint (0 is highest perf, 15 is highest energy saving) + command: msrread -f 3:0 0x1b0 # IA32_ENERGY_PERF_BIAS: Performance Energy Bias Hint (0 is highest perf, 15 is highest energy saving) superuser: true modprobe: msr parallel: true - label: rdmsr 0x1ad - command: rdmsrx 0x1ad # MSR_TURBO_RATIO_LIMIT: Maximum Ratio Limit of Turbo Mode + command: msrread 0x1ad # MSR_TURBO_RATIO_LIMIT: Maximum Ratio Limit of Turbo Mode superuser: true modprobe: msr parallel: true - label: rdmsr 0x1ae - command: rdmsrx 0x1ae # MSR_TURBO_GROUP_CORE_CNT: Group Size of Active Cores for Turbo Mode Operation + command: msrread 0x1ae # MSR_TURBO_GROUP_CORE_CNT: Group Size of Active Cores for Turbo Mode Operation superuser: true modprobe: msr parallel: true - label: rdmsr 0x4f - command: rdmsrx -a 0x4f # MSR_PPIN: Protected Processor Inventory Number + command: msrread -a 0x4f # MSR_PPIN: Protected Processor Inventory Number superuser: true modprobe: msr parallel: true - label: rdmsr 0x610 - command: rdmsrx -f 14:0 0x610 # MSR_PKG_POWER_LIMIT: Package limit in bits 14:0 + command: msrread -f 14:0 0x610 # MSR_PKG_POWER_LIMIT: Package limit in bits 14:0 superuser: true modprobe: msr parallel: true - label: rdmsr 0x6d - command: rdmsrx 0x6d # TODO: what is the name/ID of this MSR? SPR Features + command: msrread 0x6d # TODO: what is the name/ID of this MSR? SPR Features superuser: true modprobe: msr parallel: true - label: rdmsr 0xc90 - command: rdmsrx 0xc90 + command: msrread 0xc90 superuser: true modprobe: msr parallel: true - label: uncore cha count - command: rdmsrx 0x702 + command: msrread 0x702 superuser: true modprobe: msr parallel: true - label: uncore client cha count - command: rdmsrx 0x396 + command: msrread 0x396 superuser: true modprobe: msr parallel: true - label: uncore cha count spr - command: rdmsrx 0x2FFE + command: msrread 0x2FFE superuser: true modprobe: msr parallel: true - label: uncore max frequency - command: rdmsrx -f 6:0 0x620 # MSR_UNCORE_RATIO_LIMIT: MAX_RATIO in bits 6:0 + command: msrread -f 6:0 0x620 # MSR_UNCORE_RATIO_LIMIT: MAX_RATIO in bits 6:0 superuser: true modprobe: msr parallel: true - label: uncore min frequency - command: rdmsrx -f 14:8 0x620 # MSR_UNCORE_RATIO_LIMIT: MIN_RATIO in bits 14:8 + command: msrread -f 14:8 0x620 # MSR_UNCORE_RATIO_LIMIT: MIN_RATIO in bits 14:8 superuser: true modprobe: msr parallel: true - label: active idle utilization point command: |- - wrmsrx 0xb0 0x80000694 # must write this value to this MSR before reading 0xb1 - rdmsrx -f 15:8 0xb1 # ACTIVE IDLE - UTILIZATION POINT + msrwrite 0xb0 0x80000694 # must write this value to this MSR before reading 0xb1 + msrread -f 15:8 0xb1 # ACTIVE IDLE - UTILIZATION POINT superuser: true modprobe: msr parallel: true - label: active idle mesh frequency command: |- - wrmsrx 0xb0 0x80000694 # must write this value to this MSR before reading 0xb1 - rdmsrx -f 7:0 0xb1 # ACTIVE IDLE - MESH FREQUENCY + msrwrite 0xb0 0x80000694 # must write this value to this MSR before reading 0xb1 + msrread -f 7:0 0xb1 # ACTIVE IDLE - MESH FREQUENCY superuser: true modprobe: msr parallel: true @@ -306,7 +306,7 @@ commands: sar -n DEV "$interval" "$samples" > sar-network.out & fi if {{.ProfilePMU}}; then - pmu2metrics -v -csv -t $duration 1>pmu2metrics.out & + pmu2metrics -v --output csv -t $duration 1>pmu2metrics.out & fi if {{.ProfilePower}}; then turbostat -S -s PkgWatt,RAMWatt -q -i "$interval" -n "$samples" -o turbostat.out & @@ -450,7 +450,7 @@ commands: - label: CPU Turbo Test command: |- # measure tdp and all-core turbo frequency - ((turbostat -i 2 2>/dev/null &) ; stress-ng --cpu 1 -t 20s 2>&1 ; stress-ng --cpu 0 -t 20s 2>&1 ; pkill -9 -f turbostat) | awk '$0~"stress" {print $0} $1=="Package" || $1=="CPU" || $1=="Core" || $1=="Node" {if(f!=1) print $0;f=1} $1=="-" {print $0}' + ((turbostat -i 2 2>/dev/null &) ; stress-ng --cpu 1 -t 20s 2>&1 ; stress-ng --cpu 0 -t 60s 2>&1 ; pkill -9 -f turbostat) | awk '$0~"stress" {print $0} $1=="Package" || $1=="CPU" || $1=="Core" || $1=="Node" {if(f!=1) print $0;f=1} $1=="-" {print $0}' superuser: true modprobe: msr - label: CPU Idle diff --git a/src/orchestrator/targets.example b/cmd/orchestrator/targets.example similarity index 100% rename from src/orchestrator/targets.example rename to cmd/orchestrator/targets.example diff --git a/src/orchestrator/targets_file.go b/cmd/orchestrator/targets_file.go similarity index 89% rename from src/orchestrator/targets_file.go rename to cmd/orchestrator/targets_file.go index 6e59bad..3534b2d 100644 --- a/src/orchestrator/targets_file.go +++ b/cmd/orchestrator/targets_file.go @@ -12,7 +12,7 @@ import ( "strconv" "strings" - "intel.com/svr-info/pkg/core" + "github.com/intel/svr-info/internal/util" ) type targetFromFile struct { @@ -87,10 +87,12 @@ func (tf *TargetsFile) parseContent(content []byte) (targets []targetFromFile, e // key, pwd, and sudo are all optional t.key = tokens[i+3] if t.key != "" { - err = core.FileExists(t.key) + var exists bool + exists, err = util.FileExists(t.key) if err != nil { + fileErrors = append(fileErrors, fmt.Sprintf("-targets %s : failed to determine if key file (%s) is a file, line %d: %v\n", tf.path, t.key, lineNo, err)) + } else if !exists { fileErrors = append(fileErrors, fmt.Sprintf("-targets %s : key file (%s) not a file, line %d\n", tf.path, t.key, lineNo)) - return } } t.pwd = tokens[i+4] diff --git a/src/orchestrator/targets_file_test.go b/cmd/orchestrator/targets_file_test.go similarity index 100% rename from src/orchestrator/targets_file_test.go rename to cmd/orchestrator/targets_file_test.go diff --git a/src/pmu2metrics/event_defs.go b/cmd/pmu2metrics/event_defs.go similarity index 75% rename from src/pmu2metrics/event_defs.go rename to cmd/pmu2metrics/event_defs.go index 6f3dcb6..961cf38 100644 --- a/src/pmu2metrics/event_defs.go +++ b/cmd/pmu2metrics/event_defs.go @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: MIT + */ +// +// helper functions for parsing and interpreting the architecture-specific perf event definition files +// package main import ( @@ -13,16 +20,90 @@ import ( mapset "github.com/deckarep/golang-set/v2" ) +// EventDefinition represents a single perf event type EventDefinition struct { Raw string Name string Device string } -type GroupDefinition []EventDefinition // AKA a "group", ordered list of event definitions +// GroupDefinition represents a group of perf events +type GroupDefinition []EventDefinition + +// LoadEventGroups reads the events defined in the architecture specific event definition file, then +// expands them to include the per-device uncore events +func LoadEventGroups(eventDefinitionOverridePath string, metadata Metadata) (groups []GroupDefinition, err error) { + var file fs.File + if eventDefinitionOverridePath != "" { + if file, err = os.Open(eventDefinitionOverridePath); err != nil { + return + } + } else { + if file, err = resources.Open(filepath.Join("resources", fmt.Sprintf("%s_events.txt", metadata.Microarchitecture))); err != nil { + return + } + } + defer file.Close() + scanner := bufio.NewScanner(file) + uncollectableEvents := mapset.NewSet[string]() + var group GroupDefinition + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if len(line) == 0 || line[0] == '#' { + continue + } + var event EventDefinition + if event, err = parseEventDefinition(line[:len(line)-1]); err != nil { + return + } + var collectable bool + if collectable, err = isCollectableEvent(event, metadata); err != nil { + return + } + if collectable { + group = append(group, event) + } else { + uncollectableEvents.Add(event.Name) + } + if line[len(line)-1] == ';' { + // end of group detected + if len(group) > 0 { + groups = append(groups, group) + } else if gCmdLineArgs.verbose { + log.Printf("No collectable events in group ending with %s", line) + } + group = GroupDefinition{} // clear the list + } + } + if err = scanner.Err(); err != nil { + return + } + // expand uncore groups for all uncore devices + groups, err = expandUncoreGroups(groups, metadata) + // "fixed" PMU counters are not supported on (most) IaaS VMs, so we add a separate group + if !isUncoreSupported(metadata) { + group = GroupDefinition{EventDefinition{Raw: "cpu-cycles"}, EventDefinition{Raw: "instructions"}} + if metadata.RefCyclesSupported { + group = append(group, EventDefinition{Raw: "ref-cycles"}) + } + groups = append(groups, group) + group = GroupDefinition{EventDefinition{Raw: "cpu-cycles:k"}, EventDefinition{Raw: "instructions"}} + if metadata.RefCyclesSupported { + group = append(group, EventDefinition{Raw: "ref-cycles:k"}) + } + groups = append(groups, group) + + } + if uncollectableEvents.Cardinality() != 0 && gCmdLineArgs.verbose { + log.Printf("Uncollectable events: %s", uncollectableEvents) + } + return +} + +// isUncoreSupported confirms if platform has exposed uncore devices func isUncoreSupported(metadata Metadata) (supported bool) { supported = false - for uncoreDeviceName := range metadata.DeviceCounts { + for uncoreDeviceName := range metadata.DeviceIDs { if uncoreDeviceName == "cha" { // could be any uncore device supported = true break @@ -31,6 +112,7 @@ func isUncoreSupported(metadata Metadata) (supported bool) { return } +// isCollectableEvent confirms if given event can be collected on the platform func isCollectableEvent(event EventDefinition, metadata Metadata) (collectable bool, err error) { collectable = true // TMA @@ -43,13 +125,23 @@ func isCollectableEvent(event EventDefinition, metadata Metadata) (collectable b return } // short-circuit off-core response events - if event.Device == "cpu" && strings.HasPrefix(event.Name, "OCR") && isUncoreSupported(metadata) { + if event.Device == "cpu" && + strings.HasPrefix(event.Name, "OCR") && + isUncoreSupported(metadata) && + !(gCmdLineArgs.scope == ScopeProcess) && + !(gCmdLineArgs.scope == ScopeCgroup) { return } - // exclude uncore events when their corresponding device is not found + // exclude uncore events when + // - their corresponding device is not found + // - not in system-wide collection scope if event.Device != "cpu" && event.Device != "" { + if gCmdLineArgs.scope == ScopeProcess || gCmdLineArgs.scope == ScopeCgroup { + collectable = false + return + } deviceExists := false - for uncoreDeviceName := range metadata.DeviceCounts { + for uncoreDeviceName := range metadata.DeviceIDs { if event.Device == uncoreDeviceName { deviceExists = true break @@ -69,7 +161,13 @@ func isCollectableEvent(event EventDefinition, metadata Metadata) (collectable b return } // no uncore means we're on a VM where cpu fixed cycles are likely not supported - if !isUncoreSupported(metadata) && strings.Contains(event.Name, "cpu-cycles") { + if strings.Contains(event.Name, "cpu-cycles") && !isUncoreSupported(metadata) { + collectable = false + return + } + // no cstate and power events when collecting at process or cgroup scope + if (gCmdLineArgs.scope == ScopeProcess || gCmdLineArgs.scope == ScopeCgroup) && + (strings.Contains(event.Name, "cstate_") || strings.Contains(event.Name, "power/energy")) { collectable = false return } @@ -79,6 +177,7 @@ func isCollectableEvent(event EventDefinition, metadata Metadata) (collectable b return } +// parseEventDefinition parses one line from the event definition file into a representative structure func parseEventDefinition(line string) (eventDef EventDefinition, err error) { eventDef.Raw = line fields := strings.Split(line, ",") @@ -99,6 +198,8 @@ func parseEventDefinition(line string) (eventDef EventDefinition, err error) { return } +// expandUncoreGroup expands a perf event group into a list of groups where each group is +// associated with an uncore device func expandUncoreGroup(group GroupDefinition, ids []int, re *regexp.Regexp) (groups []GroupDefinition, err error) { for _, deviceID := range ids { var newGroup GroupDefinition @@ -119,7 +220,7 @@ func expandUncoreGroup(group GroupDefinition, ids []int, re *regexp.Regexp) (gro return } -// expands groups with uncore events to include events for all uncore devices +// expandUncoreGroups expands groups with uncore events to include events for all uncore devices // assumes that uncore device events are in their own groups, not mixed with other device types func expandUncoreGroups(groups []GroupDefinition, metadata Metadata) (expandedGroups []GroupDefinition, err error) { // example 1: cha/event=0x35,umask=0xc80ffe01,name='UNC_CHA_TOR_INSERTS.IA_MISS_CRD'/, @@ -131,22 +232,13 @@ func expandUncoreGroups(groups []GroupDefinition, metadata Metadata) (expandedGr device := group[0].Device if device == "cha" || device == "upi" || device == "imc" || device == "iio" { var newGroups []GroupDefinition - // unlike other device types, imc ids may not be consecutive - var ids []int - if device == "imc" { - ids = metadata.IMCDeviceIDs - } else { - for i := 0; i < metadata.DeviceCounts[device]; i++ { - ids = append(ids, i) - } - } - if len(ids) == 0 { + if len(metadata.DeviceIDs[device]) == 0 { if gCmdLineArgs.verbose { log.Printf("No uncore devices found for %s", device) } continue } - if newGroups, err = expandUncoreGroup(group, ids, re); err != nil { + if newGroups, err = expandUncoreGroup(group, metadata.DeviceIDs[device], re); err != nil { return } expandedGroups = append(expandedGroups, newGroups...) @@ -156,73 +248,3 @@ func expandUncoreGroups(groups []GroupDefinition, metadata Metadata) (expandedGr } return } - -// reads the events defined in the architecture specific event definition file, then -// expands them to include the per-device uncore events -func loadEventDefinitions(eventDefinitionOverridePath string, metadata Metadata) (groups []GroupDefinition, err error) { - var file fs.File - if eventDefinitionOverridePath != "" { - if file, err = os.Open(eventDefinitionOverridePath); err != nil { - return - } - } else { - if file, err = resources.Open(filepath.Join("resources", fmt.Sprintf("%s_events.txt", metadata.Microarchitecture))); err != nil { - return - } - } - defer file.Close() - scanner := bufio.NewScanner(file) - uncollectableEvents := mapset.NewSet[string]() - var group GroupDefinition - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if len(line) == 0 || line[0] == '#' { - continue - } - var event EventDefinition - if event, err = parseEventDefinition(line[:len(line)-1]); err != nil { - return - } - var collectable bool - if collectable, err = isCollectableEvent(event, metadata); err != nil { - return - } - if collectable { - group = append(group, event) - } else { - uncollectableEvents.Add(event.Name) - } - if line[len(line)-1] == ';' { - // end of group detected - if len(group) > 0 { - groups = append(groups, group) - } else if gCmdLineArgs.verbose { - log.Printf("No collectable events in group ending with %s", line) - } - group = GroupDefinition{} // clear the list - } - } - if err = scanner.Err(); err != nil { - return - } - // expand uncore groups for all uncore devices - groups, err = expandUncoreGroups(groups, metadata) - // "fixed" PMU counters are not supported on (most) IaaS VMs, so we add a separate group - if !isUncoreSupported(metadata) { - group = GroupDefinition{EventDefinition{Raw: "cpu-cycles"}, EventDefinition{Raw: "instructions"}} - if metadata.RefCyclesSupported { - group = append(group, EventDefinition{Raw: "ref-cycles"}) - } - groups = append(groups, group) - group = GroupDefinition{EventDefinition{Raw: "cpu-cycles:k"}, EventDefinition{Raw: "instructions"}} - if metadata.RefCyclesSupported { - group = append(group, EventDefinition{Raw: "ref-cycles:k"}) - } - groups = append(groups, group) - - } - if uncollectableEvents.Cardinality() != 0 && gCmdLineArgs.verbose { - log.Printf("Uncollectable events: %s", uncollectableEvents) - } - return -} diff --git a/cmd/pmu2metrics/event_frame.go b/cmd/pmu2metrics/event_frame.go new file mode 100644 index 0000000..9255e90 --- /dev/null +++ b/cmd/pmu2metrics/event_frame.go @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: MIT + */ +// +// Linux perf event output, i.e., from 'perf stat' parsing and processing helper functions +// +package main + +import ( + "encoding/json" + "fmt" + "log" + "math" + "strconv" + "strings" + + "github.com/intel/svr-info/internal/util" + "golang.org/x/exp/slices" +) + +// EventGroup represents a group of perf events and their values +type EventGroup struct { + EventValues map[string]float64 // event name -> event value + GroupID int + Percentage float64 +} + +// EventFrame represents the list of EventGroups collected with a specific timestamp +// and sometimes present cgroup +type EventFrame struct { + EventGroups []EventGroup + Timestamp float64 + Socket string + CPU string + Cgroup string +} + +// Event represents the structure of an event output by perf stat...with +// a few exceptions +type Event struct { + Interval float64 `json:"interval"` + CPU string `json:"cpu"` + CounterValue string `json:"counter-value"` + Unit string `json:"unit"` + Cgroup string `json:"cgroup"` + Event string `json:"event"` + EventRuntime int `json:"event-runtime"` + PcntRunning float64 `json:"pcnt-running"` + Value float64 // parsed value + Group int // event group index + Socket string // only relevant if granularity is socket +} + +// GetEventFrames organizes raw events received from perf into one or more frames (groups of events) that +// will be used for calculating metrics. +// +// The raw events received from perf will differ based on the scope of collection. Current options +// are system-wide, process, cgroup(s). Cgroup scoped data is received intermixed, i.e., multiple +// cgroups' data is represented in the rawEvents list. Process scoped data is received for only +// one process at a time. +// +// The frames produced will differ based on the intended metric granularity. Current options are +// system, socket, cpu (thread/logical CPU), but only when in system scope. Process and cgroup scope +// only support system-level granularity. +func GetEventFrames(rawEvents [][]byte, eventGroupDefinitions []GroupDefinition, scope Scope, granularity Granularity, metadata Metadata) (eventFrames []EventFrame, err error) { + // parse raw events into list of Event + var allEvents []Event + if allEvents, err = parseEvents(rawEvents, eventGroupDefinitions); err != nil { + return + } + // coalesce events to one or more lists based on scope and granularity + var coalescedEvents [][]Event + if coalescedEvents, err = coalesceEvents(allEvents, scope, granularity, metadata); err != nil { + return + } + // create one EventFrame per list of Events + for _, events := range coalescedEvents { + // organize events into groups + group := EventGroup{EventValues: make(map[string]float64)} + var lastGroupID int + var eventFrame EventFrame + for eventIdx, event := range events { + if eventIdx == 0 { + lastGroupID = event.Group + eventFrame.Timestamp = event.Interval + if gCmdLineArgs.granularity == GranularityCPU { + eventFrame.CPU = event.CPU + } else if gCmdLineArgs.granularity == GranularitySocket { + eventFrame.Socket = event.Socket + } + if gCmdLineArgs.scope == ScopeCgroup { + eventFrame.Cgroup = event.Cgroup + } + } + if event.Group != lastGroupID { + eventFrame.EventGroups = append(eventFrame.EventGroups, group) + group = EventGroup{EventValues: make(map[string]float64)} + lastGroupID = event.Group + } + group.GroupID = event.Group + group.Percentage = event.PcntRunning + group.EventValues[event.Event] = event.Value + } + // add the last group + eventFrame.EventGroups = append(eventFrame.EventGroups, group) + // TODO: can we collapse uncore groups as we're parsing (above)? + if eventFrame, err = collapseUncoreGroupsInFrame(eventFrame); err != nil { + return + } + eventFrames = append(eventFrames, eventFrame) + } + return +} + +// parseEvents parses the raw event data into a list of Event +func parseEvents(rawEvents [][]byte, eventGroupDefinitions []GroupDefinition) (events []Event, err error) { + events = make([]Event, 0, len(rawEvents)) + groupIdx := 0 + eventIdx := -1 + previousEvent := "" + for _, rawEvent := range rawEvents { + var event Event + if event, err = parseEventJSON(rawEvent); err != nil { + err = fmt.Errorf("failed to parse perf event: %v", err) + return + } + if event.Event != previousEvent { + eventIdx++ + previousEvent = event.Event + } + if eventIdx == len(eventGroupDefinitions[groupIdx]) { // last event in group + groupIdx++ + if groupIdx == len(eventGroupDefinitions) { + if gCmdLineArgs.scope == ScopeCgroup { + groupIdx = 0 + } else { + err = fmt.Errorf("event group definitions not aligning with raw events") + return + } + } + eventIdx = 0 + } + event.Group = groupIdx + events = append(events, event) + } + return +} + +// coalesceEvents separates the events into a number of event lists by granularity and scope +func coalesceEvents(allEvents []Event, scope Scope, granularity Granularity, metadata Metadata) (coalescedEvents [][]Event, err error) { + if scope == ScopeSystem { + if granularity == GranularitySystem { + coalescedEvents = append(coalescedEvents, allEvents) + return + } else if granularity == GranularitySocket { + // one list of Events per Socket + newEvents := make([][]Event, metadata.SocketCount) + for i := 0; i < metadata.SocketCount; i++ { + newEvents[i] = make([]Event, 0, len(allEvents)/metadata.SocketCount) + } + // merge + prevSocket := -1 + var socket int + var newEvent Event + for i, event := range allEvents { + var cpu int + if cpu, err = strconv.Atoi(event.CPU); err != nil { + return + } + socket = metadata.CPUSocketMap[cpu] + if socket != prevSocket { + if i != 0 { + newEvents[prevSocket] = append(newEvents[prevSocket], newEvent) + } + prevSocket = socket + newEvent = event + newEvent.Socket = fmt.Sprintf("%d", socket) + continue + } + newEvent.Value += event.Value + } + newEvents[socket] = append(newEvents[socket], newEvent) + coalescedEvents = append(coalescedEvents, newEvents...) + return + } else if granularity == GranularityCPU { + // create one list of Events per CPU + numCPUs := metadata.SocketCount * metadata.CoresPerSocket * metadata.ThreadsPerCore + newEvents := make([][]Event, numCPUs) + for i := 0; i < numCPUs; i++ { + newEvents[i] = make([]Event, 0, len(allEvents)/numCPUs) + } + for _, event := range allEvents { + var cpu int + if cpu, err = strconv.Atoi(event.CPU); err != nil { + return + } + newEvents[cpu] = append(newEvents[cpu], event) + } + coalescedEvents = append(coalescedEvents, newEvents...) + } else { + err = fmt.Errorf("unsupported granularity: %d", granularity) + return + } + } else if scope == ScopeProcess { + coalescedEvents = append(coalescedEvents, allEvents) + return + } else if scope == ScopeCgroup { + // expand events list to one list per cgroup + var allCgroupEvents [][]Event + var cgroups []string + for _, event := range allEvents { + var cgroupIdx int + if cgroupIdx, err = util.StringIndexInList(event.Cgroup, cgroups); err != nil { + cgroups = append(cgroups, event.Cgroup) + cgroupIdx = len(cgroups) - 1 + allCgroupEvents = append(allCgroupEvents, []Event{}) + } + allCgroupEvents[cgroupIdx] = append(allCgroupEvents[cgroupIdx], event) + } + coalescedEvents = append(coalescedEvents, allCgroupEvents...) + } else { + err = fmt.Errorf("unsupported scope: %d", scope) + return + } + return +} + +// collapseUncoreGroupsInFrame merges repeated (per-device) uncore groups into a single +// group by summing the values for events that only differ by device ID. +// +// uncore events are received in repeated perf groups like this: +// group: +// 5.005032332,49,,UNC_CHA_TOR_INSERTS.IA_MISS_CRD.0,2806917160,25.00,, +// 5.005032332,2720,,UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE.0,2806917160,25.00,, +// 5.005032332,1061494,,UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE.0,2806917160,25.00,, +// group: +// 5.005032332,49,,UNC_CHA_TOR_INSERTS.IA_MISS_CRD.1,2806585867,25.00,, +// 5.005032332,2990,,UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE.1,2806585867,25.00,, +// 5.005032332,1200063,,UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE.1,2806585867,25.00,, +// +// For the example above, we will have this: +// 5.005032332,98,,UNC_CHA_TOR_INSERTS.IA_MISS_CRD,2806585867,25.00,, +// 5.005032332,5710,,UNC_CHA_TOR_INSERTS.IA_MISS_DRD_REMOTE,2806585867,25.00,, +// 5.005032332,2261557,,UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD_REMOTE,2806585867,25.00,, +// Note: uncore event names start with "UNC" +// Note: we assume that uncore events are not mixed into groups that have other event types, e.g., cpu events +func collapseUncoreGroupsInFrame(inFrame EventFrame) (outFrame EventFrame, err error) { + outFrame = inFrame + outFrame.EventGroups = []EventGroup{} + var idxUncoreMatches []int + for inGroupIdx, inGroup := range inFrame.EventGroups { + // skip groups that have been collapsed + if slices.Contains(idxUncoreMatches, inGroupIdx) { + continue + } + idxUncoreMatches = []int{} + foundUncore := false + for eventName := range inGroup.EventValues { + // only check the first entry + if strings.HasPrefix(eventName, "UNC") { + foundUncore = true + } + break + } + if foundUncore { + // we need to know how many of the following groups (if any) match the current group + // so they can be merged together into a single group + for i := inGroupIdx + 1; i < len(inFrame.EventGroups); i++ { + if isMatchingGroup(inGroup, inFrame.EventGroups[i]) { + // keep track of the groups that match so we can skip processing them since + // they will be merged into a single group + idxUncoreMatches = append(idxUncoreMatches, i) + } else { + break + } + } + var outGroup EventGroup + if outGroup, err = collapseUncoreGroups(inFrame.EventGroups, inGroupIdx, len(idxUncoreMatches)); err != nil { + return + } + outFrame.EventGroups = append(outFrame.EventGroups, outGroup) + } else { + outFrame.EventGroups = append(outFrame.EventGroups, inGroup) + } + } + return +} + +// isMatchingGroup - groups are considered matching if they include the same event names (ignoring .ID suffix) +func isMatchingGroup(groupA, groupB EventGroup) bool { + if len(groupA.EventValues) != len(groupB.EventValues) { + return false + } + aNames := make([]string, 0, len(groupA.EventValues)) + bNames := make([]string, 0, len(groupB.EventValues)) + for eventAName := range groupA.EventValues { + parts := strings.Split(eventAName, ".") + newName := strings.Join(parts[:len(parts)-1], ".") + aNames = append(aNames, newName) + } + for eventBName := range groupB.EventValues { + parts := strings.Split(eventBName, ".") + newName := strings.Join(parts[:len(parts)-1], ".") + bNames = append(bNames, newName) + } + slices.Sort(aNames) + slices.Sort(bNames) + for nameIdx, name := range aNames { + if name != bNames[nameIdx] { + return false + } + } + return true +} + +// collapseUncoreGroups collapses a list of groups into a single group +func collapseUncoreGroups(inGroups []EventGroup, firstIdx int, count int) (outGroup EventGroup, err error) { + outGroup.GroupID = inGroups[firstIdx].GroupID + outGroup.Percentage = inGroups[firstIdx].Percentage + outGroup.EventValues = make(map[string]float64) + for i := firstIdx; i <= firstIdx+count; i++ { + for name, value := range inGroups[i].EventValues { + parts := strings.Split(name, ".") + newName := strings.Join(parts[:len(parts)-1], ".") + if _, ok := outGroup.EventValues[newName]; !ok { + outGroup.EventValues[newName] = 0 + } + outGroup.EventValues[newName] += value + } + } + return +} + +// parseEventJSON parses JSON formatted event into struct +// example: {"interval" : 5.005113019, "cpu": "0", "counter-value" : "22901873.000000", "unit" : "", "cgroup" : "...1cb2de.scope", "event" : "L1D.REPLACEMENT", "event-runtime" : 80081151765, "pcnt-running" : 6.00, "metric-value" : 0.000000, "metric-unit" : "(null)"} +func parseEventJSON(rawEvent []byte) (event Event, err error) { + if err = json.Unmarshal(rawEvent, &event); err != nil { + err = fmt.Errorf("unrecognized event format [%s]: %v", rawEvent, err) + return + } + if event.Value, err = strconv.ParseFloat(event.CounterValue, 64); err != nil { + event.Value = math.NaN() + err = nil + if gCmdLineArgs.verbose { + log.Printf("failed to parse event value: %s", rawEvent) + } + } + return +} diff --git a/cmd/pmu2metrics/main.go b/cmd/pmu2metrics/main.go new file mode 100644 index 0000000..db00e89 --- /dev/null +++ b/cmd/pmu2metrics/main.go @@ -0,0 +1,1058 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: MIT + */ +// +// Command line interface and program logic +// +package main + +import ( + "bufio" + "embed" + "encoding/binary" + "flag" + "fmt" + "log" + "log/syslog" + "os" + "os/exec" + "os/signal" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "github.com/intel/svr-info/internal/util" +) + +// globals +var ( + gVersion string = "dev" + gCmdLineArgs CmdLineArgs + gCollectionStartTime time.Time +) + +// Granularity represents the requested granularity level for produced metrics +type Granularity int + +const ( + GranularitySystem Granularity = iota + GranularitySocket + GranularityCPU +) + +var GranularityOptions = []string{"system", "socket", "cpu"} + +// Scope represents the requested scope of event collection +type Scope int + +const ( + ScopeSystem Scope = iota + ScopeProcess + ScopeCgroup +) + +var ScopeOptions = []string{"system", "process", "cgroup"} + +// Format represents the format of the metric output +type Format int + +const ( + FormatHuman Format = iota + FormatCSV + FormatWide +) + +var FormatOptions = []string{"human", "csv", "wide"} + +// Summary represents the format of the post-processed summary report +type Summary int + +const ( + SummaryCSV Summary = iota + SummaryHTML +) + +var SummaryOptions = []string{"csv", "html"} + +// CmdLineArgs represents the program arguments provided by the user +type CmdLineArgs struct { + showHelp bool + showVersion bool + // collection options + timeout int // seconds + // collection options + scope Scope + pidList string + cidList string + filter string + count int + refresh int // seconds + // post-processing options + inputCSVFilePath string + summaryFormat Summary + // output format options + granularity Granularity + metricsList string + outputFormat Format + verbose bool + veryVerbose bool + // advanced options + showMetricNames bool + syslog bool + eventFilePath string + metricFilePath string + perfPrintInterval int // milliseconds + perfMuxInterval int // milliseconds + // debugging options + metadataFilePath string + perfStatFilePath string +} + +//go:embed resources +var resources embed.FS + +// extractExecutableResources extracts executables from embedded resources to temporary directory +func extractExecutableResources(tempDir string) (err error) { + toolNames := []string{"perf"} + for _, toolName := range toolNames { + // get the exe from our embedded resources + var toolBytes []byte + toolBytes, err = resources.ReadFile("resources/" + toolName) + if err != nil { + return + } + toolPath := filepath.Join(tempDir, toolName) + var f *os.File + f, err = os.OpenFile(toolPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0744) + if err != nil { + return + } + defer f.Close() + err = binary.Write(f, binary.LittleEndian, toolBytes) + if err != nil { + return + } + } + return +} + +// resourceExists confirms that file of provided filename exists in the embedded +// resources +func resourceExists(filename string) (exists bool) { + f, err := resources.Open(filepath.Join("resources", filename)) + if err != nil { + exists = false + return + } + f.Close() + exists = true + return +} + +// printMetrics prints one frame of metrics to stdout in the format requested by the user. The +// frameCount argument is used to control when the headers are printed, e.g., on the first frame +// only. +func printMetrics(metricFrame MetricFrame, frameCount int) { + if gCmdLineArgs.outputFormat == FormatCSV { + if frameCount == 1 { + fmt.Print("TS,SKT,CPU,PID,CMD,CID,") + names := make([]string, 0, len(metricFrame.Metrics)) + for _, metric := range metricFrame.Metrics { + names = append(names, metric.Name) + } + fmt.Printf("%s\n", strings.Join(names, ",")) + } + fmt.Printf("%d,%s,%s,%s,%s,%s,", gCollectionStartTime.Unix()+int64(metricFrame.Timestamp), metricFrame.Socket, metricFrame.CPU, metricFrame.PID, metricFrame.Cmd, metricFrame.Cgroup) + values := make([]string, 0, len(metricFrame.Metrics)) + for _, metric := range metricFrame.Metrics { + values = append(values, strconv.FormatFloat(metric.Value, 'g', 8, 64)) + } + fmt.Printf("%s\n", strings.ReplaceAll(strings.Join(values, ","), "NaN", "")) + } else { + if gCmdLineArgs.outputFormat == FormatHuman { + fmt.Println("--------------------------------------------------------------------------------------") + fmt.Printf("- Metrics captured at %s\n", gCollectionStartTime.Add(time.Second*time.Duration(int(metricFrame.Timestamp))).UTC()) + if metricFrame.PID != "" { + fmt.Printf("- PID: %s\n", metricFrame.PID) + fmt.Printf("- CMD: %s\n", metricFrame.Cmd) + } else if metricFrame.Cgroup != "" { + fmt.Printf("- CID: %s\n", metricFrame.Cgroup) + } + if metricFrame.CPU != "" { + fmt.Printf("- CPU: %s\n", metricFrame.CPU) + } else if metricFrame.Socket != "" { + fmt.Printf("- Socket: %s\n", metricFrame.Socket) + } + fmt.Println("--------------------------------------------------------------------------------------") + fmt.Printf("%-70s %15s\n", "metric", "value") + fmt.Printf("%-70s %15s\n", "------------------------", "----------") + for _, metric := range metricFrame.Metrics { + fmt.Printf("%-70s %15s\n", metric.Name, strconv.FormatFloat(metric.Value, 'g', 4, 64)) + } + } else { // wide format + var names []string + var values []float64 + for _, metric := range metricFrame.Metrics { + names = append(names, metric.Name) + values = append(values, metric.Value) + } + minColWidth := 6 + colSpacing := 3 + if frameCount == 1 { // print headers + header := "Timestamp " // 10 + 3 + if metricFrame.PID != "" { + header += "PID " // 7 + 3 + header += "Command " // 15 + 3 + } else if metricFrame.Cgroup != "" { + header += "CID " + } + if metricFrame.CPU != "" { + header += "CPU " // 3 + 3 + } else if metricFrame.Socket != "" { + header += "SKT " // 3 + 3 + } + for _, name := range names { + extend := 0 + if len(name) < minColWidth { + extend = minColWidth - len(name) + } + header += fmt.Sprintf("%s%*s%*s", name, extend, "", colSpacing, "") + } + fmt.Println(header) + } + // handle values + TimestampColWidth := 10 + formattedTimestamp := fmt.Sprintf("%d", gCollectionStartTime.Unix()+int64(metricFrame.Timestamp)) + row := fmt.Sprintf("%s%*s%*s", formattedTimestamp, TimestampColWidth-len(formattedTimestamp), "", colSpacing, "") + if metricFrame.PID != "" { + PIDColWidth := 7 + commandColWidth := 15 + row += fmt.Sprintf("%s%*s%*s", metricFrame.PID, PIDColWidth-len(metricFrame.PID), "", colSpacing, "") + var command string + if len(metricFrame.Cmd) <= commandColWidth { + command = metricFrame.Cmd + } else { + command = metricFrame.Cmd[:commandColWidth] + } + row += fmt.Sprintf("%s%*s%*s", command, commandColWidth-len(command), "", colSpacing, "") + } else if metricFrame.Cgroup != "" { + CIDColWidth := 7 + row += fmt.Sprintf("%s%*s%*s", metricFrame.Cgroup, CIDColWidth-len(metricFrame.Cgroup), "", colSpacing, "") + } + if metricFrame.CPU != "" { + CPUColWidth := 3 + row += fmt.Sprintf("%s%*s%*s", metricFrame.CPU, CPUColWidth-len(metricFrame.CPU), "", colSpacing, "") + } else if metricFrame.Socket != "" { + SKTColWidth := 3 + row += fmt.Sprintf("%s%*s%*s", metricFrame.Socket, SKTColWidth-len(metricFrame.Socket), "", colSpacing, "") + } + // handle the metric values + for i, value := range values { + colWidth := max(len(names[i]), minColWidth) + formattedVal := fmt.Sprintf("%.2f", value) + row += fmt.Sprintf("%s%*s%*s", formattedVal, colWidth-len(formattedVal), "", colSpacing, "") + } + fmt.Println(row) + } + } +} + +// getPerfPath returns the path to the perf executable that will be used to collect +// events. If the perf binary is included in the embedded resources, it will be extracted +// to a temporary directory and run from there, otherwise the system-installed perf will +// be used. +func getPerfPath() (path string, tempDir string, err error) { + if resourceExists("perf") { + if tempDir, err = os.MkdirTemp("", fmt.Sprintf("%s.tmp.", filepath.Base(os.Args[0]))); err != nil { + log.Printf("failed to create temporary directory: %v", err) + return + } + if err = extractExecutableResources(tempDir); err != nil { + log.Printf("failed to extract executable resources to %s: %v", "", err) + return + } + path = filepath.Join(tempDir, "perf") + } else { + path, err = exec.LookPath("perf") + } + return +} + +// getPerfCommandArgs assembles the arguments that will be passed to Linux perf +func getPerfCommandArgs(pid string, cgroups []string, timeout int, eventGroups []GroupDefinition, metadata Metadata) (args []string, err error) { + // -I: print interval in ms + // -j: json formatted event output + args = append(args, "stat", "-I", fmt.Sprintf("%d", gCmdLineArgs.perfPrintInterval), "-j") + if gCmdLineArgs.scope == ScopeSystem { + args = append(args, "-a") // system-wide collection + if gCmdLineArgs.granularity == GranularityCPU || gCmdLineArgs.granularity == GranularitySocket { + args = append(args, "-A") // no aggregation + } + } else if gCmdLineArgs.scope == ScopeProcess { + args = append(args, "-p", pid) // collect only for this process + } else if gCmdLineArgs.scope == ScopeCgroup { + args = append(args, "--for-each-cgroup", strings.Join(cgroups, ",")) // collect only for these cgroups + } + // -i: event groups to collect + args = append(args, "-e") + var groups []string + for _, group := range eventGroups { + var events []string + for _, event := range group { + events = append(events, event.Raw) + } + groups = append(groups, fmt.Sprintf("{%s}", strings.Join(events, ","))) + } + args = append(args, fmt.Sprintf("'%s'", strings.Join(groups, ","))) + // add timeout, if applicable + if gCmdLineArgs.scope != ScopeCgroup && timeout != 0 { + args = append(args, "sleep", fmt.Sprintf("%d", timeout)) + } + return +} + +// getPerfCommands is responsible for assembling the command(s) that will be +// executed to collect event data +func getPerfCommands(perfPath string, eventGroups []GroupDefinition, metadata Metadata) (processes []Process, perfCommands []*exec.Cmd, err error) { + if gCmdLineArgs.scope == ScopeSystem { + var args []string + if args, err = getPerfCommandArgs("", []string{}, gCmdLineArgs.timeout, eventGroups, metadata); err != nil { + err = fmt.Errorf("failed to assemble perf args: %v", err) + return + } + cmd := exec.Command(perfPath, args...) + perfCommands = append(perfCommands, cmd) + } else if gCmdLineArgs.scope == ScopeProcess { + if gCmdLineArgs.pidList != "" { + if processes, err = GetProcesses(gCmdLineArgs.pidList); err != nil { + return + } + } else { + if processes, err = GetHotProcesses(gCmdLineArgs.count, gCmdLineArgs.filter); err != nil { + return + } + } + if len(processes) == 0 { + err = fmt.Errorf("no PIDs selected") + return + } + var timeout int + if gCmdLineArgs.timeout > 0 && gCmdLineArgs.timeout < gCmdLineArgs.refresh { + timeout = gCmdLineArgs.timeout + } else { + timeout = gCmdLineArgs.refresh + } + for _, process := range processes { + var args []string + if args, err = getPerfCommandArgs(process.pid, []string{}, timeout, eventGroups, metadata); err != nil { + err = fmt.Errorf("failed to assemble perf args: %v", err) + return + } + cmd := exec.Command(perfPath, args...) + perfCommands = append(perfCommands, cmd) + } + } else if gCmdLineArgs.scope == ScopeCgroup { + var cgroups []string + if gCmdLineArgs.cidList != "" { + if cgroups, err = GetCgroups(gCmdLineArgs.cidList); err != nil { + return + } + } else { + if cgroups, err = GetHotCgroups(gCmdLineArgs.count, gCmdLineArgs.filter); err != nil { + return + } + } + if len(cgroups) == 0 { + err = fmt.Errorf("no CIDs selected") + return + } + var args []string + if args, err = getPerfCommandArgs("", cgroups, -1, eventGroups, metadata); err != nil { + err = fmt.Errorf("failed to assemble perf args: %v", err) + return + } + cmd := exec.Command(perfPath, args...) + perfCommands = append(perfCommands, cmd) + } + return +} + +// runPerf starts Linux perf using the provided command, then reads perf's output +// until perf stops. When collecting for cgroups, perf will be manually terminated if/when the +// run duration exceeds the collection time or the time when the cgroup list needs +// to be refreshed. +func runPerf(process Process, cmd *exec.Cmd, eventGroupDefinitions []GroupDefinition, metricDefinitions []MetricDefinition, metadata Metadata, frameChannel chan MetricFrame, errorChannel chan error) { + var err error + defer func() { errorChannel <- err }() + reader, _ := cmd.StderrPipe() + if gCmdLineArgs.veryVerbose { + log.Printf("perf command: %s", cmd) + } + scanner := bufio.NewScanner(reader) + cpuCount := metadata.SocketCount * metadata.CoresPerSocket * metadata.ThreadsPerCore + outputLines := make([][]byte, 0, cpuCount*150) // a rough approximation of expected number of events + // start perf + if err = cmd.Start(); err != nil { + err = fmt.Errorf("failed to run perf: %v", err) + log.Printf("%v", err) + return + } + // must manually terminate perf in cgroup scope when a timeout is specified and/or need to refresh cgroups + startPerfTimestamp := time.Now() + var timeout int + if gCmdLineArgs.scope == ScopeCgroup && (gCmdLineArgs.timeout != 0 || gCmdLineArgs.cidList == "") { + if gCmdLineArgs.timeout > 0 && gCmdLineArgs.timeout < gCmdLineArgs.refresh { + timeout = gCmdLineArgs.timeout + } else { + timeout = gCmdLineArgs.refresh + } + } + // Use a timer to determine when we received an entire frame of events from perf + // The timer will expire when no lines (events) have been received from perf for more than 100ms. This + // works because perf writes the events to stderr in a burst every collection interval, e.g., 5 seconds. + // When the timer expires, this code assumes that perf is done writing events to stderr. + // The first duration needs to be longer than the time it takes for perf to print its first line of output. + t1 := time.NewTimer(time.Duration(2 * gCmdLineArgs.perfPrintInterval)) + var frameTimestamp float64 + frameCount := 0 + go func() { + for { + <-t1.C // waits for timer to expire + if len(outputLines) != 0 { + var metricFrames []MetricFrame + if metricFrames, frameTimestamp, err = ProcessEvents(outputLines, eventGroupDefinitions, metricDefinitions, process, frameTimestamp, metadata); err != nil { + log.Printf("%v", err) + return + } + for _, metricFrame := range metricFrames { + frameCount += 1 + metricFrame.FrameCount = frameCount + frameChannel <- metricFrame + outputLines = [][]byte{} // empty it + } + } + if timeout != 0 && int(time.Since(startPerfTimestamp).Seconds()) > timeout { + cmd.Process.Signal(os.Interrupt) + } + } + }() + // read perf output + for scanner.Scan() { // blocks waiting for next token (line), loop terminated (Scan returns false) when file empty/closed + line := scanner.Text() + if gCmdLineArgs.veryVerbose { + log.Print(line) + } + t1.Stop() + t1.Reset(100 * time.Millisecond) // 100ms is somewhat arbitrary, but seems to work + outputLines = append(outputLines, []byte(line)) + } + t1.Stop() + if len(outputLines) != 0 { + var metricFrames []MetricFrame + if metricFrames, frameTimestamp, err = ProcessEvents(outputLines, eventGroupDefinitions, metricDefinitions, process, frameTimestamp, metadata); err != nil { + log.Printf("%v", err) + return + } + for _, metricFrame := range metricFrames { + frameCount += 1 + metricFrame.FrameCount = frameCount + frameChannel <- metricFrame + } + } + // wait for perf stat to exit + if err = cmd.Wait(); err != nil { + if strings.Contains(err.Error(), "signal") { // perf received kill signal, ignore + err = nil + } else { + err = fmt.Errorf("error from perf on exit: %v", err) + log.Printf("%v", err) + } + return + } +} + +// receiveMetrics prints metrics that it receives over the provided channel +func receiveMetrics(frameChannel chan MetricFrame) { + totalFrameCount := 0 + // block until next frame of metrics arrives, will exit loop when channel is closed + for frame := range frameChannel { + totalFrameCount++ + printMetrics(frame, totalFrameCount) + } +} + +// doWork is the primary application event loop. It sets up the goroutines and +// communication channels, runs perf, restarts perf (if necessary), etc. +func doWork(perfPath string, eventGroupDefinitions []GroupDefinition, metricDefinitions []MetricDefinition, metadata Metadata) (err error) { + // refresh if collecting per-process/cgroup and list of PIDs/CIDs not specified + refresh := (gCmdLineArgs.scope == ScopeProcess && gCmdLineArgs.pidList == "") || + (gCmdLineArgs.scope == ScopeCgroup && gCmdLineArgs.cidList == "") + errorChannel := make(chan error) + frameChannel := make(chan MetricFrame) + totalRuntimeSeconds := 0 // only relevant in process scope + go receiveMetrics(frameChannel) + for { + // get current time for use in setting timestamps on output + gCollectionStartTime = time.Now() + var perfCommands []*exec.Cmd + var processes []Process + // One perf command when in system or cgroup scope and one or more perf commands when in process scope. + if processes, perfCommands, err = getPerfCommands(perfPath, eventGroupDefinitions, metadata); err != nil { + break + } + beginTimestamp := time.Now() + for i, cmd := range perfCommands { + var process Process + if len(processes) > i { + process = processes[i] + } + go runPerf(process, cmd, eventGroupDefinitions, metricDefinitions, metadata, frameChannel, errorChannel) + } + // wait for all runPerf goroutines to finish + var perfErrors []error + for range perfCommands { + perfErr := <-errorChannel // capture and return all errors + if perfErr != nil { + perfErrors = append(perfErrors, perfErr) + } + } + endTimestamp := time.Now() + if len(perfErrors) > 0 { + var errStrings []string + for _, perfErr := range perfErrors { + errStrings = append(errStrings, fmt.Sprintf("%v", perfErr)) + } + err = fmt.Errorf("error(s) from perf commands: %s", strings.Join(errStrings, ", ")) + break + } + // no perf errors, continue + totalRuntimeSeconds += int(endTimestamp.Sub(beginTimestamp).Seconds()) + if !refresh || (gCmdLineArgs.timeout != 0 && totalRuntimeSeconds >= gCmdLineArgs.timeout) { + break + } + } + close(frameChannel) // trigger receiveMetrics to end + return +} + +// doWorkDebug is used for testing and debugging +// Plays back events present in a file that contains perf stat output +func doWorkDebug(perfStatFilePath string, eventGroupDefinitions []GroupDefinition, metricDefinitions []MetricDefinition, metadata Metadata) (err error) { + gCollectionStartTime = time.Now() + // var perfCommands []*exec.Cmd + // var processes []Process + // if processes, perfCommands, err = getPerfCommands("perf", nil /*eventGroups*/, metadata); err != nil { + // return + // } + // for _, cmd := range perfCommands { + // log.Print(cmd) + // } + // log.Print(processes) + file, err := os.Open(perfStatFilePath) + if err != nil { + return + } + defer file.Close() + scanner := bufio.NewScanner(file) + frameCount := 0 + eventCount := 0 + frameTimestamp := 0.0 + prevEventTimestamp := 0.0 + var outputLines [][]byte + for scanner.Scan() { + line := scanner.Text() + var event Event + if event, err = parseEventJSON([]byte(line)); err != nil { + return + } + if eventCount == 0 { + prevEventTimestamp = event.Interval + } + if event.Interval != prevEventTimestamp { + if len(outputLines) > 0 { + var metricFrames []MetricFrame + if metricFrames, frameTimestamp, err = ProcessEvents(outputLines, eventGroupDefinitions, metricDefinitions, Process{}, frameTimestamp, metadata); err != nil { + log.Printf("%v", err) + return + } + for _, metricFrame := range metricFrames { + frameCount++ + printMetrics(metricFrame, frameCount) + outputLines = [][]byte{} // empty it + } + } + } + outputLines = append(outputLines, []byte(line)) + prevEventTimestamp = event.Interval + eventCount++ + } + if len(outputLines) != 0 { + var metricFrames []MetricFrame + if metricFrames, _, err = ProcessEvents(outputLines, eventGroupDefinitions, metricDefinitions, Process{}, frameTimestamp, metadata); err != nil { + log.Printf("%v", err) + return + } + for _, metricFrame := range metricFrames { + frameCount += 1 + printMetrics(metricFrame, frameCount) + } + } + err = scanner.Err() + return +} + +// flagUsage is called when a flag parsing error occurs or undefined flag is passed to the program +func flagUsage() { + fmt.Fprintln(os.Stderr) + fmt.Fprintf(os.Stderr, "See '%s -h' for options.\n", filepath.Base(os.Args[0])) +} + +// showArgumentError prints error found while validating arguments +func showArgumentError(err error) { + out := fmt.Sprintf("Argument validation error: %v", err) + fmt.Fprintln(os.Stderr, out) + flagUsage() +} + +// showUsage prints program usage and options to stdout +func showUsage() { + fmt.Printf("Usage: sudo %s [OPTIONS]\n", filepath.Base(os.Args[0])) + fmt.Println() + fmt.Println("Prints system metrics at 5 second intervals until interrupted by user.") + fmt.Println("Note: Metrics are printed to stdout. Log messages are printed to stderr or, optionally, sent to syslog.") + fmt.Println() + args := `Options + -h, --help + Show this usage message and exit. + -V, --version + Show program version and exit. + +Collection Options + -t, --timeout + Number of seconds to run (default: indefinitely). + -s, --scope