pax_global_header00006660000000000000000000000064150307672540014523gustar00rootroot0000000000000052 comment=9166c3f0bb9c5d3e31dc80aff165a5073b5ac5be rt-tests-2.9/000077500000000000000000000000001503076725400131625ustar00rootroot00000000000000rt-tests-2.9/COPYING000066400000000000000000000431031503076725400142160ustar00rootroot00000000000000 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 Lesser 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. GNU GENERAL PUBLIC LICENSE 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. 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. 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. 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. 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. NO WARRANTY 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. 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. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 even 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-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. rt-tests-2.9/Dockerfile000066400000000000000000000011211503076725400151470ustar00rootroot00000000000000# Use CentOS Stream 9 as base image FROM centos:stream9 # Install required packages for RT-Tests RUN dnf -y update && \ dnf install -y \ gcc \ make \ numactl-devel \ util-linux \ git && \ git clone https://git.kernel.org/pub/scm/utils/rt-tests/rt-tests.git /opt/rt-tests && \ cd /opt/rt-tests && \ make && \ make install && \ dnf remove -y \ git \ make && \ dnf clean all # Set the working directory to the RT-Tests directory WORKDIR /opt/rt-tests # Set the entrypoint to a shell ENTRYPOINT ["/bin/bash"] rt-tests-2.9/MAINTAINERS000066400000000000000000000002371503076725400146610ustar00rootroot00000000000000Please send your patches to Clark Williams John Kacur and cc rt-users Thank You rt-tests-2.9/Makefile000066400000000000000000000206121503076725400146230ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later include feature/test-feature.mak VERSION = 2.8 CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar OBJDIR = bld sources = cyclictest.c \ hackbench.c \ pip_stress.c \ pi_stress.c \ pmqtest.c \ ptsematest.c \ rt-migrate-test.c \ signaltest.c \ sigwaittest.c \ svsematest.c \ cyclicdeadline.c \ deadline_test.c \ queuelat.c \ ssdd.c \ oslat.c TARGETS = $(sources:.c=) LIBS = -lrt -lpthread RTTESTLIB = -lrttest -L$(OBJDIR) EXTRA_LIBS ?= -ldl # for get_cpu RTTESTNUMA = -lrttestnuma -lnuma DESTDIR ?= prefix ?= /usr/local bindir ?= $(prefix)/bin mandir ?= $(prefix)/share/man CFLAGS ?= -Wall -Werror -Wno-nonnull -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-error=format-truncation= -Wno-error=unused-result CPPFLAGS += -D_GNU_SOURCE -Isrc/include LDFLAGS ?= PYLIB ?= $(shell python3 -m get_pylib) # Check for optional libcpupower dependency ifneq ($(no_libcpupower), 1) ifeq ($(call test-feature,libcpupower), 0) CPPFLAGS += -DHAVE_LIBCPUPOWER_SUPPORT LIBCPUPOWER += -lcpupower else $(warning libcpupower is missing, building without --deepest-idle-state support.) $(warning Please install libcpupower-dev/kernel-tools-libs-devel) endif else $(warning libcpupower disabled, building without --deepest-idle-state support.) endif # Check for errors, such as python3 not available ifeq (${PYLIB},) undefine PYLIB endif MANPAGES = src/cyclictest/cyclictest.8 \ src/pi_tests/pi_stress.8 \ src/ptsematest/ptsematest.8 \ src/rt-migrate-test/rt-migrate-test.8 \ src/sigwaittest/sigwaittest.8 \ src/svsematest/svsematest.8 \ src/pmqtest/pmqtest.8 \ src/hackbench/hackbench.8 \ src/signaltest/signaltest.8 \ src/pi_tests/pip_stress.8 \ src/queuelat/queuelat.8 \ src/queuelat/determine_maximum_mpps.8 \ src/sched_deadline/deadline_test.8 \ src/ssdd/ssdd.8 \ src/sched_deadline/cyclicdeadline.8 \ src/oslat/oslat.8 ifdef PYLIB MANPAGES += src/cyclictest/get_cyclictest_snapshot.8 \ src/hwlatdetect/hwlatdetect.8 endif ifeq ($(MAN_COMPRESSION),gzip) MANPAGES := $(MANPAGES:.8=.8.gz) else ifeq ($(MAN_COMPRESSION),bzip2) MANPAGES := $(MANPAGES:.8=.8.bz2) endif ifndef DEBUG CFLAGS += -O2 -g else CFLAGS += -O0 -g endif # We make some gueses on how to compile rt-tests based on the machine type # and the ostype. These can often be overridden. dumpmachine := $(shell $(CC) -dumpmachine) # The ostype is typically something like linux or android ostype := $(lastword $(subst -, ,$(dumpmachine))) machinetype := $(shell echo $(dumpmachine)| \ sed -e 's/-.*//' -e 's/i.86/i386/' -e 's/mips.*/mips/' -e 's/ppc.*/powerpc/') include src/arch/android/Makefile VPATH = src/cyclictest: VPATH += src/signaltest: VPATH += src/pi_tests: VPATH += src/rt-migrate-test: VPATH += src/ptsematest: VPATH += src/sigwaittest: VPATH += src/svsematest: VPATH += src/pmqtest: VPATH += src/lib: VPATH += src/hackbench: VPATH += src/sched_deadline: VPATH += src/queuelat: VPATH += src/ssdd: VPATH += src/oslat: $(OBJDIR)/%.o: %.c | $(OBJDIR) $(CC) -D VERSION=$(VERSION) -c $< $(CFLAGS) $(CPPFLAGS) -o $@ # Pattern rule to generate dependency files from .c files $(OBJDIR)/%.d: %.c | $(OBJDIR) @$(CC) -MM $(CFLAGS) $(CPPFLAGS) $< | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' > $@ || rm -f $@ .PHONY: all all: $(TARGETS) hwlatdetect get_cyclictest_snapshot | $(OBJDIR) $(OBJDIR): mkdir $(OBJDIR) # Include dependency files, automatically generate them if needed. -include $(addprefix $(OBJDIR)/,$(sources:.c=.d)) cyclictest: $(OBJDIR)/cyclictest.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA) $(LIBCPUPOWER) cyclicdeadline: $(OBJDIR)/cyclicdeadline.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) deadline_test: $(OBJDIR)/deadline_test.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) signaltest: $(OBJDIR)/signaltest.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA) pi_stress: $(OBJDIR)/pi_stress.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) hwlatdetect: src/hwlatdetect/hwlatdetect.py chmod +x src/hwlatdetect/hwlatdetect.py ln -s src/hwlatdetect/hwlatdetect.py hwlatdetect get_cyclictest_snapshot: src/cyclictest/get_cyclictest_snapshot.py chmod +x src/cyclictest/get_cyclictest_snapshot.py ln -s src/cyclictest/get_cyclictest_snapshot.py get_cyclictest_snapshot rt-migrate-test: $(OBJDIR)/rt-migrate-test.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) ptsematest: $(OBJDIR)/ptsematest.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS) sigwaittest: $(OBJDIR)/sigwaittest.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS) svsematest: $(OBJDIR)/svsematest.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS) pmqtest: $(OBJDIR)/pmqtest.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(EXTRA_LIBS) pip_stress: $(OBJDIR)/pip_stress.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) hackbench: $(OBJDIR)/hackbench.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) queuelat: $(OBJDIR)/queuelat.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) ssdd: $(OBJDIR)/ssdd.o $(OBJDIR)/librttest.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) oslat: $(OBJDIR)/oslat.o $(OBJDIR)/librttest.a $(OBJDIR)/librttestnuma.a $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(RTTESTLIB) $(RTTESTNUMA) %.8.gz: %.8 gzip -nc $< > $@ %.8.bz2: %.8 bzip2 -c $< > $@ LIBOBJS =$(addprefix $(OBJDIR)/,rt-error.o rt-get_cpu.o rt-sched.o rt-utils.o \ histogram.o) $(OBJDIR)/librttest.a: $(LIBOBJS) $(AR) rcs $@ $^ LIBNUMAOBJS =$(addprefix $(OBJDIR)/,rt-numa.o) $(OBJDIR)/librttestnuma.a: $(LIBNUMAOBJS) $(AR) rcs $@ $^ CLEANUP = $(TARGETS) *.o .depend *.*~ *.orig *.rej *.d *.a *.8.gz *.8.bz2 CLEANUP += $(if $(wildcard .git), ChangeLog) .PHONY: clean clean: for F in $(CLEANUP); do find -type f -name $$F | xargs rm -f; done rm -f rt-tests-*.tar rm -f hwlatdetect rm -f get_cyclictest_snapshot rm -f tags RPMDIRS = BUILD BUILDROOT RPMS SRPMS SPECS .PHONY: distclean distclean: clean rm -rf $(RPMDIRS) releases *.tar.gz *.tar.asc tmp .PHONY: rebuild rebuild: $(MAKE) clean $(MAKE) all .PHONY: install install: all install_manpages install_hwlatdetect install_get_cyclictest_snapshot mkdir -p "$(DESTDIR)$(bindir)" cp $(TARGETS) "$(DESTDIR)$(bindir)" install src/queuelat/determine_maximum_mpps.sh "${DESTDIR}${bindir}" .PHONY: install_hwlatdetect install_hwlatdetect: hwlatdetect if test -n "$(PYLIB)" ; then \ mkdir -p "$(DESTDIR)$(bindir)" ; \ install -D -m 755 src/hwlatdetect/hwlatdetect.py $(DESTDIR)$(PYLIB)/hwlatdetect.py ; \ rm -f "$(DESTDIR)$(bindir)/hwlatdetect" ; \ ln -s $(PYLIB)/hwlatdetect.py "$(DESTDIR)$(bindir)/hwlatdetect" ; \ fi .PHONY: install_get_cyclictest_snapshot install_get_cyclictest_snapshot: get_cyclictest_snapshot if test -n "${PYLIB}" ; then \ mkdir -p "${DESTDIR}${bindir}" ; \ install -D -m 755 src/cyclictest/get_cyclictest_snapshot.py ${DESTDIR}${PYLIB}/get_cyclictest_snapshot.py ; \ rm -f "${DESTDIR}${bindir}/get_cyclictest_snapshot" ; \ ln -s ${PYLIB}/get_cyclictest_snapshot.py "${DESTDIR}${bindir}/get_cyclictest_snapshot" ; \ fi .PHONY: install_manpages install_manpages: $(MANPAGES) mkdir -p "$(DESTDIR)$(mandir)/man8" cp $(MANPAGES) "$(DESTDIR)$(mandir)/man8" .PHONY: tarball tarball: git archive --worktree-attributes --prefix=rt-tests-${VERSION}/ -o rt-tests-${VERSION}.tar v${VERSION} .PHONY: help help: @echo "" @echo " rt-tests useful Makefile targets:" @echo "" @echo " all : build all tests (default" @echo " install : install tests to local filesystem" @echo " clean : remove object files" @echo " distclean : remove all generated files" @echo " tarball : make a rt-tests tarball suitable for release" @echo " help : print this message" # Universal Ctags warns about the backward compatible option '--extra' and # wants it to be called '--extras'. CTAGS_BIN = ctags CTAGS_EXTRA := $(shell $(CTAGS_BIN) --version 2>&1 | grep -iq universal && echo extras || echo extra) .PHONY: tags tags: $(CTAGS_BIN) -R --$(CTAGS_EXTRA)=+f --c-kinds=+p --exclude=tmp --exclude=BUILD * rt-tests-2.9/README-Dockerfile000066400000000000000000000053511503076725400161130ustar00rootroot000000000000001 # README-Dockerfile-rt-tests ## Introduction This README provides detailed instructions for setting up an rt-tests container using Docker or Podman. This Dockerfile will pull a current copy of your code (plus all of your changes) and compile it into a container image for you to test with. This will give you an imbiguous way to test your changes, assuming you are running on a semi-modern host OS. The rt-tests container can be used to test any of the subpackages found under the rt-tests project, but is mainly intended for hwlatdetect and cyclictest. ## Prerequisites - Docker or Podman installed on your system. ## Installation ### Prepare Your Environment - For EPEL systems (Fedora, CentOS, RHEL): - Install Podman: `sudo dnf install podman -y` - For Debian-based systems (Ubuntu): - Install Docker: `sudo apt-get update && sudo apt-get install docker.io -y` - Alternatively, install Podman: `sudo apt-get update && sudo apt-get install podman -y` ### Build the Container - Navigate to the root directory of the 'rteval' - Build the container image named 'rteval-upstream': - For Podman: `sudo podman build -t rt-tests-upstream .` - For Docker: `sudo docker build -t rt-tests-upstream .` ## Usage ### Run the Container - Start the container in privileged mode: - For Podman: `sudo podman run -it --privileged rt-tests-upstream` - For Docker: `sudo docker run -it --privileged rt-tests-upstream` - This will create a long lasting container. In order to use it again (say after a reboot), you need to start and exec into the container to get yourself back into the shell: - For Podman: - `sudo podman start rt-tests-upstream` - `sudo podman exec -it rt-tests-upstream /bin/bash` - For Docker: - `sudo docker start rt-tests-upstream` - `sudo docker exec -it rt-tests-upstream /bin/bash` - And to clean up and remove your container so you can test a new one: - For Podman: `sudo podman rm -f rt-tests-upstream` - For Docker: `sudo docker rm -f rt-tests-upstream` - Please note that the above command does not cleanup the container when you are done. It can be useful to run a single use container when trying to only test a quick change and remove the container afterwards. To run a a single use container that removes itself immediately after you exit the shell, run the following commands with the `--rm` option: - For Podman: `sudo podman run -it --rm --privileged rt-tests-upstream` - For Docker: `sudo docker run -it --rm --privileged rt-tests-upstream` ### Test rteval Build - Inside the container, test one of the rt-tests packages you just built: - Command: `cyclictest -D 1s` - Command: `hwlatdetect --duration 1` ## Conclusion Follow these detailed steps for a successful rt-tests container setup using Docker or Podman. rt-tests-2.9/README.markdown000066400000000000000000000076551503076725400157000ustar00rootroot00000000000000# RT-Tests This repository contains some programs that test various rt-linux features. ## Usage ### Compile sudo apt-get install build-essential libnuma-dev make ### Run tests To run one test thread per CPU or per CPU core, each thread on a separate processor, type sudo ./cyclictest -a -t -n -p99 On a non-realtime system, you may see something like T: 0 ( 3431) P:99 I:1000 C: 100000 Min: 5 Act: 10 Avg: 14 Max: 39242 T: 1 ( 3432) P:98 I:1500 C: 66934 Min: 4 Act: 10 Avg: 17 Max: 39661 The rightmost column contains the most important result, i.e. the worst-case latency of 39.242 milliseconds. On a realtime-enabled system, the result may look like T: 0 ( 3407) P:99 I:1000 C: 100000 Min: 7 Act: 10 Avg: 10 Max: 18 T: 1 ( 3408) P:98 I:1500 C: 67043 Min: 7 Act: 8 Avg: 10 Max: 22 and, thus, indicate an apparent short-term worst-case latency of 18 microseconds. Running cyclictest only over a short period of time and without creating appropriate real-time stress conditions is rather meaningless, since the execution of an asynchronous event from idle state is normally always quite fast, and every - even non-RT system - can do that. The challenge is to minimize the latency when reacting to an asynchronuous event, irrespective of what code path is executed at the time when the external event arrives. Therefore, specific stress conditions must be present while cyclictest is running to reliably determine the worst-case latency of a given system. ### Latency fighting If - as in the above example - a low worst-case latency is measured, and this is the case even under a system load that is equivalent to the load expected under production conditions, everything is alright. Of course, the measurement must last suffciently long, preferably 24 hours or more to run several hundred million test threads. If possible, the `-i` command line option (thread interval) should be used to increase the number of test threads over time. As a role of thumb, the thread interval should be set to a value twice as long as the expected worst-case latency. If at the end of such a test period the worst-cae latency still did not exceed the value that is assumed critical for a given system, the particular kernel in combination with the hardware in use can then probably be regarded as real-time capable. What, however, if the latency is higher than acceptable? Then, the famous "*latency fighting*" begins. For this purpose, the cyclictest tool provides the `-b` option that causes a function tracing to be written to `/sys/kernel/debug/tracing/trace`, if a specified latency threshold was exceeded, for example: ./cyclictest -a -t -n -p99 -f -b100 This causes the program to abort execution, if the latency value exceeds 100 microseconds; the culprit can then be found in the trace output at `/sys/kernel/debug/tracing/trace`. The kernel function that was executed just before a latency of more than 100 microseconds was detected is marked with an exclamation mark such as qemu-30047 2D.h3 742805us : __activate_task+0x42/0x68 (199 1) qemu-30047 2D.h3 742806us : __trace_start_sched_wakeup+0x40/0x161 (0 -1) qemu-30047 2DNh3 742806us!: try_to_wake_up+0x422/0x460 (199 -5) qemu-30047 2DN.1 742939us : __sched_text_start+0xf3/0xdcd (c064e442 0) The first column indicates the calling process responsible for triggering the latency. If the trace output is not obvious, it can be submitted to the OSADL Latency Fight Support Service at [latency-fighters@osadl.org](mailto:latency-fighters@osadl.org). In addition to the output of `cat /sys/kernel/debug/tracing/trace`, the output of `lspci` and the `.config` file that was used to build the kernel in question must be submitted. We are sure you understand that OSADL members will be served first, but we promise to do our best to help everybody to successfully fight against kernel and driver latencies. rt-tests-2.9/bld/000077500000000000000000000000001503076725400137235ustar00rootroot00000000000000rt-tests-2.9/feature/000077500000000000000000000000001503076725400146155ustar00rootroot00000000000000rt-tests-2.9/feature/Makefile000066400000000000000000000004401503076725400162530ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later all: feature-libcpupower feature-libcpupower: $(OBJDIR)/test-libcpupower.o $(OBJDIR)/test-libcpupower.o: feature/test-libcpupower.c @$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -lcpupower -o $@ .PHONY: clean clean: rm -f $(OBJDIR)/test-*.o rt-tests-2.9/feature/test-feature.mak000066400000000000000000000003611503076725400177170ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-or-later define test-feature $(shell $(MAKE) OBJDIR="$(OBJDIR)" CFLAGS="$(CFLAGS)" CPPFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS)" \ -f feature/Makefile feature-$1 clean >/dev/null 2>/dev/null; echo $$?) endef rt-tests-2.9/feature/test-libcpupower.c000066400000000000000000000002041503076725400202650ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later #include int main(void) { int rv = cpuidle_state_count(0); return rv; } rt-tests-2.9/get_pylib.py000077500000000000000000000006511503076725400155170ustar00rootroot00000000000000#!/usr/bin/python3 # This file is used during the make process import sysconfig # Older versions of python don't have this scheme # but produce the answer we expect with 'posix_prefix' # Newer versions of python insert 'local' in the path unless we use rpm_prefix SCHEME = 'rpm_prefix' if not SCHEME in sysconfig.get_scheme_names(): SCHEME = 'posix_prefix' PYLIB = sysconfig.get_path('purelib', SCHEME) print(PYLIB) rt-tests-2.9/src/000077500000000000000000000000001503076725400137515ustar00rootroot00000000000000rt-tests-2.9/src/arch/000077500000000000000000000000001503076725400146665ustar00rootroot00000000000000rt-tests-2.9/src/arch/android/000077500000000000000000000000001503076725400163065ustar00rootroot00000000000000rt-tests-2.9/src/arch/android/Makefile000066400000000000000000000013161503076725400177470ustar00rootroot00000000000000 # Android use Bionic for libc, and this does not have # - pthread barriers # - pthread_[gs]etaffinity # # This is all handled by bionic.h based on flags we set here. # Typically see something like "aarch64-linux-android". However, in some # buildsystems, it will be a variation of -androidabe. ost=$(findstring android, $(ostype)) ifeq (android,$(ost)) USE_BIONIC := 1 CFLAGS += -DPTHREAD_BIONIC LDFLAGS += -pie # -lrt and -lpthread is in standard bionic library, no standalone library LIBS := $(filter-out -lrt,$(LIBS)) LIBS := $(filter-out -lpthread,$(LIBS)) # Currently, only these binaries will compile and link properly for android # - cyclictest # - hackbench sources := cyclictest.c hackbench.c endif rt-tests-2.9/src/backfire/000077500000000000000000000000001503076725400155175ustar00rootroot00000000000000rt-tests-2.9/src/backfire/Makefile000066400000000000000000000006531503076725400171630ustar00rootroot00000000000000# If KERNELRELEASE is defined, we've been invoked from the # kernel build system and can use its language ifneq ($(KERNELRELEASE),) obj-m := backfire.o # otherwise we were called directly from the command # line; invoke the kernel build system. else KERNELDIR ?= /lib/modules/$(shell uname -r)/build modules modules_install clean:: make -C $(KERNELDIR) M=$(CURDIR) $@ clean:: rm -f *.o Module.markers modules.order endif rt-tests-2.9/src/backfire/Makefile.module000066400000000000000000000002121503076725400204360ustar00rootroot00000000000000obj-m := backfire.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules rt-tests-2.9/src/backfire/backfire.4000066400000000000000000000020761503076725400173570ustar00rootroot00000000000000.TH "backfire" "4" "0.1" "" "Driver" # SPDX-License-Identifier: GPL-2.0-or-later .SH "NAME" .LP backfire \- send a signal from driver to user .SH "DESCRIPTION" .LP The \fBbackfire\fR driver reads a numerical string that is sent to the \fB/dev/backfire\fR device and sends the corresponding signal to the calling user program. Reading from \fB/dev/backfire\fR returns the time of the day when the most recent sent request was serviced or 0, if a sent request was not yet received. The time of the day is displayed in seconds since 1970-01-01 00:00:00 UTC followed by the fraction of the second in microseconds separated by a comma. .SH "PURPOSE" .LP The \fBbackfire\fR driver is normally used in combination with the program \fBsendme\fR to benchmark the performance of the kernel's signal sending capabilities. .SH "EXAMPLES" .LP .nf head -1 /dev/backfire 0,0 trap "echo Got signal 7" 7 echo 7 >/dev/backfire Got signal 7 head -1 /dev/backfire 1234567890,123456 .fi .LP .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP sendme(8) rt-tests-2.9/src/backfire/backfire.c000066400000000000000000000061201503076725400174300ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * backfire - send signal back to caller * Copyright (C) 2007 Carsten Emde */ #include #include #include #include #include #include #include #include #include #include #include #include #define BACKFIRE_MINOR MISC_DYNAMIC_MINOR static spinlock_t backfire_state_lock = SPIN_LOCK_UNLOCKED; static int backfire_open_cnt; /* #times opened */ static int backfire_open_mode; /* special open modes */ static struct timeval sendtime; /* when the most recent signal was sent */ #define BACKFIRE_WRITE 1 /* opened for writing (exclusive) */ #define BACKFIRE_EXCL 2 /* opened with O_EXCL */ /* * These are the file operation function for user access to /dev/backfire */ static ssize_t backfire_read(struct file *file, char *buf, size_t count, loff_t *ppos) { return snprintf(buf, count, "%d,%d\n", (int) sendtime.tv_sec, (int) sendtime.tv_usec); } static ssize_t backfire_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { int signo; struct pid *pid; if (sscanf(buf, "%d", &signo) >= 1) { if (signo > 0 && signo < 32) { pid = get_pid(task_pid(current)); do_gettimeofday(&sendtime); kill_pid(pid, signo, 1); } else printk(KERN_ERR "Invalid signal no. %d\n", signo); } return strlen(buf); } static int backfire_open(struct inode *inode, struct file *file) { spin_lock(&backfire_state_lock); if ((backfire_open_cnt && (file->f_flags & O_EXCL)) || (backfire_open_mode & BACKFIRE_EXCL)) { spin_unlock(&backfire_state_lock); return -EBUSY; } if (file->f_flags & O_EXCL) backfire_open_mode |= BACKFIRE_EXCL; if (file->f_mode & 2) backfire_open_mode |= BACKFIRE_WRITE; backfire_open_cnt++; spin_unlock(&backfire_state_lock); return 0; } static int backfire_release(struct inode *inode, struct file *file) { spin_lock(&backfire_state_lock); backfire_open_cnt--; if (backfire_open_cnt == 1 && backfire_open_mode & BACKFIRE_EXCL) backfire_open_mode &= ~BACKFIRE_EXCL; if (file->f_mode & 2) backfire_open_mode &= ~BACKFIRE_WRITE; spin_unlock(&backfire_state_lock); return 0; } static struct file_operations backfire_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = backfire_open, .read = backfire_read, .write = backfire_write, .release = backfire_release, }; static struct miscdevice backfire_dev = { BACKFIRE_MINOR, "backfire", &backfire_fops }; static int __init backfire_init(void) { int ret; ret = misc_register(&backfire_dev); if (ret) printk(KERN_ERR "backfire: can't register dynamic misc device\n"); else printk(KERN_INFO "backfire driver misc device %d\n", backfire_dev.minor); return ret; } static void __exit backfire_exit(void) { misc_deregister(&backfire_dev); } module_init(backfire_init); module_exit(backfire_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Carsten Emde "); MODULE_DESCRIPTION("Send signal back to caller"); rt-tests-2.9/src/backfire/sendme.8000066400000000000000000000027001503076725400170620ustar00rootroot00000000000000.TH "sendme" "8" "0.2" "" "" # SPDX-License-Identifier: GPL-2.0-only .SH "NAME" .LP \fBsendme\fR \- Send a signal from driver to user and measure time intervals .SH "SYNTAX" .LP sendme [-a|-a PROC] [-b USEC] [-l loops] [-p PRIO] .br .SH "DESCRIPTION" .LP The program \fBsendme\fR uses the \fBbackfire\fR driver to send a signal from driver to user. It then reads the timestamp from the driver and calculates the time intervals to call the driver and to receive the signal from the driver. .SH "OPTIONS" .TP .B \-a, \-\-affinity[=PROC] Run on processor number PROC. If PROC is not specified, run on current processor. .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies on a system. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. Sendme is stopped once the number of timer intervals has been reached. .TP .B \-p, \-\-prio=PRIO Set the priority of the process. .SH "FILES" backfire.ko .SH "EXAMPLES" .LP .nf # modprobe backfire # sendme -a -p99 -l1000000 Samples: 1000000 To: Min 0, Cur 0, Avg 1, Max 11 From: Min 2, Cur 3, Avg 3, Max 43 .fi .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP backfire(4) rt-tests-2.9/src/backfire/sendme.c000066400000000000000000000156041503076725400171440ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * sendme.c * * Copyright (C) 2009 Carsten Emde * */ #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "rt-get_cpu.h" #include #include #include #include #include #define SIGTEST SIGHUP enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USECURRENT }; static int setaffinity = AFFINITY_UNSPECIFIED; static int affinity; static int tracelimit; static int priority; static int shutdown; static int max_cycles; static volatile struct timeval after; static int interval = 1000; static int kernvar(int mode, const char *name, char *value, size_t sizeofvalue) { char filename[128]; char *fileprefix = get_debugfileprefix(); int retval = 1; int path; size_t len_prefix = strlen(fileprefix), len_name = strlen(name); if (len_prefix + len_name + 1 > sizeof(filename)) { errno = ENOMEM; return 1; } memcpy(filename, fileprefix, len_prefix); memcpy(filename + len_prefix, name, len_name + 1); path = open(filename, mode); if (path >= 0) { if (mode == O_RDONLY) { int got; if ((got = read(path, value, sizeofvalue)) > 0) { retval = 0; value[got-1] = '\0'; } } else if (mode == O_WRONLY) { if (write(path, value, sizeofvalue) == sizeofvalue) retval = 0; } close(path); } return retval; } void signalhandler(int signo) { struct timeval tv; gettimeofday(&tv, NULL); after = tv; if (signo == SIGINT || signo == SIGTERM) shutdown = 1; } void stop_tracing(void) { kernvar(O_WRONLY, "tracing_enabled", "0", 1); } static void display_help(void) { printf("sendme V %1.2f\n", VERSION); puts("Usage: sendme "); puts("Function: send a signal from driver to userspace"); puts( "Options:\n" "-a [NUM] --affinity pin to current processor\n" " with NUM pin to processor NUM\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority\n"); exit(1); } static void process_options (int argc, char *argv[]) { int error = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"affinity", optional_argument, NULL, 'a'}, {"breaktrace", required_argument, NULL, 'b'}, {"interval", required_argument, NULL, 'i'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "a::b:i:l:p:", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (optarg != NULL) { affinity = atoi(optarg); setaffinity = AFFINITY_SPECIFIED; } else if (optind < argc && atoi(argv[optind])) { affinity = atoi(argv[optind]); setaffinity = AFFINITY_SPECIFIED; } else setaffinity = AFFINITY_USECURRENT; break; case 'b': tracelimit = atoi(optarg); break; case 'i': interval = atoi(optarg); break; case 'l': max_cycles = atoi(optarg); break; case 'p': priority = atoi(optarg); break; case '?': error = 1; break; } } if (setaffinity == AFFINITY_SPECIFIED) { if (affinity < 0) error = 1; if (affinity >= max_cpus) { fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n", affinity, max_cpus); error = 1; } } if (priority < 0 || priority > 99) error = 1; if (error) display_help (); } int main(int argc, char *argv[]) { int path; cpu_set_t mask; int policy = SCHED_FIFO; struct sched_param schedp; struct flock fl; int retval = 0; process_options(argc, argv); if (check_privs()) return 1; if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return 1; } memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = priority; sched_setscheduler(0, policy, &schedp); if (setaffinity != AFFINITY_UNSPECIFIED) { CPU_ZERO(&mask); if (setaffinity == AFFINITY_USECURRENT) { get_cpu_setup(); affinity = get_cpu(); } CPU_SET(affinity, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) == -1) fprintf(stderr, "WARNING: Could not set CPU affinity " "to CPU #%d\n", affinity); } path = open("/dev/backfire", O_RDWR); if (path < 0) { fprintf(stderr, "ERROR: Could not access backfire device, " "try 'modprobe backfire'\n" "If the module backfire can't be loaded, " "it may need to be built first.\n" "Execute 'cd src/backfire; make' in the " "rt-tests directory (requires rt-tests\n" "sources and kernel-devel package).\n"); return 1; } fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; if (fcntl(path, F_SETLK, &fl) == -1) { fprintf(stderr, "ERRROR: backfire device locked\n"); retval = 1; } else { char sigtest[8]; char timestamp[32]; struct timeval before, sendtime, diff; unsigned int diffno = 0; unsigned int mindiff1 = UINT_MAX, maxdiff1 = 0; unsigned int mindiff2 = UINT_MAX, maxdiff2 = 0; double sumdiff1 = 0.0, sumdiff2 = 0.0; if (tracelimit) kernvar(O_WRONLY, "tracing_enabled", "1", 1); sprintf(sigtest, "%d", SIGTEST); signal(SIGTEST, signalhandler); signal(SIGINT, signalhandler); signal(SIGTERM, signalhandler); while (1) { struct timespec ts; ts.tv_sec = interval / USEC_PER_SEC; ts.tv_nsec = (interval % USEC_PER_SEC) * 1000; gettimeofday(&before, NULL); write(path, sigtest, strlen(sigtest)); while (after.tv_sec == 0); read(path, timestamp, sizeof(timestamp)); if (sscanf(timestamp, "%lu,%lu\n", &sendtime.tv_sec, &sendtime.tv_usec) != 2) break; diffno++; if(max_cycles && diffno >= max_cycles) shutdown = 1; printf("Samples: %8d\n", diffno); timersub(&sendtime, &before, &diff); if (diff.tv_usec < mindiff1) mindiff1 = diff.tv_usec; if (diff.tv_usec > maxdiff1) maxdiff1 = diff.tv_usec; sumdiff1 += (double) diff.tv_usec; printf("To: Min %4d, Cur %4d, Avg %4d, Max %4d\n", mindiff1, (int) diff.tv_usec, (int) ((sumdiff1 / diffno) + 0.5), maxdiff1); timersub(&after, &sendtime, &diff); if (diff.tv_usec < mindiff2) mindiff2 = diff.tv_usec; if (diff.tv_usec > maxdiff2) maxdiff2 = diff.tv_usec; sumdiff2 += (double) diff.tv_usec; printf("From: Min %4d, Cur %4d, Avg %4d, Max %4d\n", mindiff2, (int) diff.tv_usec, (int) ((sumdiff2 / diffno) + 0.5), maxdiff2); after.tv_sec = 0; if ((tracelimit && diff.tv_usec > tracelimit) || shutdown) { if (tracelimit) stop_tracing(); break; } nanosleep(&ts, NULL); printf("\033[3A"); } } close(path); return retval; } rt-tests-2.9/src/cyclictest/000077500000000000000000000000001503076725400161175ustar00rootroot00000000000000rt-tests-2.9/src/cyclictest/cyclictest.8000066400000000000000000000171551503076725400203670ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .TH CYCLICTEST 8 "April 22, 2016" # SPDX-License-Identifier: GPL-2.0-only .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME cyclictest \- High resolution test program .SH SYNOPSIS .SY cyclictest .RI "[ \-hfmnqrsvMS ] [\-a " proc " ] [\-A " align " ] [\-b " usec " ] [\-c " clock " ] [\-d " dist " ] \ [\-h " histogram " ] [\-i " intv " ] [\-\-json " filename " ] [\-l " loop " ] [\-o " red " ] \ [\-p " prio " ] [\-t " num " ] [\-D " time "] [\-w] [\-W] [\-y " policy " ] [ \-S | \-U ]" .\" .SH DESCRIPTION .\" This manual page documents briefly the .\" .B cyclictest commands. .\" .PP .\" \fI\fP escape sequences to invode bold face and italics, respectively. .\" \fBcyclictest\fP is a program that... .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes ('\-\-'). .br A summary of options is included below. .\" For a complete description, see the Info files. .TP .B \-a, \-\-affinity[=PROC-SET] Run threads on the set of processors given by PROC-SET. If PROC-SET is not specified, all processors will be used. Threads will be assigned to processors in the set in numeric order, in a round\-robin fashion. .br The set of processors can be specified as A,B,C, or A-C, or A-B,D-F, and so on*. The ! character can be used to negate a set. For example, !B-D means to use all available CPUs except B through D. The cpu numbers are the same as shown in the .I processor field in /proc/cpuinfo. See numa(3) for more information on specifying CPU sets. * Support for CPU sets requires libnuma version >= 2. For libnuma v1, PROC-SET, if specified, must be a single CPU number. .TP .B \-A, \-\-align=USEC Align thread wakeups to a specific offset in microseconds .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC .TP .B \-c, \-\-clock=CLOCK select clock .br 0 = CLOCK_MONOTONIC (default) .br 1 = CLOCK_REALTIME .TP .B \-\-deepest\-idle\-state=n Reduce exit from idle latency by limiting idle state up to n on used cpus (-1 disables all idle states). Power management is not suppresed on other cpus. .TP .B \-\-default\-system Don't attempt to tune the system from cyclictest. Power management is not suppressed. This might give poorer results, but will allow you to discover if you need to tune the system. .TP .B \-d, \-\-distance=DIST Distance of thread intervals in us, default = 500 .TP .B \-D, \-\-duration=TIME Specify a length for the test run. .br Append 'm', 'h', or 'd' to specify minutes, hours or days. .TP .B \-F, \-\-fifo= Create a named pipe at path and write stats to it .TP .B \-h, \-\-histogram=US Dump latency histogram to stdout after the run. US is the max latency time to be be tracked in microseconds. This option runs all threads at the same priority. .TP .B \-H, \-\-histofall=MAXLATENCYINUS Same as -h except that an additional histogram column is displayed at the right that contains summary data of all thread histograms. If cyclictest runs a single thread only, the -H option is equivalent to -h. .TP .B \-\-histfile= Dump the latency histogram to instead of stdout. .TP .B \-i, \-\-interval=INTV Set the base interval of the thread(s) in microseconds (default is 1000us). This sets the interval of the first thread. See also \-d. .TP .B \-\-json=FILENAME Write final results into FILENAME, JSON formatted. .TP .B \-\-laptop Save battery when running cyclictest. This will give you poorer realtime results, but will not drain your battery so quickly. .TP .B \-\-latency=PM_Q0S power management latency target value. This value is written to /dev/cpu_dma_latency and affects c-states. The default is 0 .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. Cyclictest is stopped once the number of timer intervals has been reached. .TP .B \-\-mainaffinity=CPUSET Run the main thread on CPU #N. This only affects the main thread and not the measurement threads .TP .B \-m, \-\-mlockall Lock current and future memory allocations to prevent being paged out .TP .B \\-M, \-\-refresh_on_max Delay updating the screen until a new max latency is hit. (useful for running cyclictest on low-bandwidth connections) .TP .B \-N, \-\-nsecs Show results in nanoseconds instead of microseconds, which is the default unit. .TP .B \-o, \-\-oscope=RED Oscilloscope mode, reduce verbose output by RED. .TP .B \-p, \-\-prio=PRIO Set the priority of the first thread. The given priority is set to the first test thread. Each further thread gets a lower priority: Priority(Thread N) = max(Priority(Thread N\-1) \- 1, 0) .TP .B \-\-policy=NAME set the scheduler policy of the measurement threads where NAME is one of: other, normal, batch, idle, fifo, rr .TP .B \-\-priospread spread priority levels starting at a specified value .TP .B \-q, \-\-quiet Print a summary only on exit. Useful for automated tests, where only the summary output needs to be captured. .TP .B \-r, \-\-relative Use relative timers instead of absolute. The default behaviour of the tests is to use absolute timers. This option is there for completeness and should not be used for reproducible tests. .TP .B \-R, \-\-resolution Check clock resolution, calling clock_gettime() many times. List of lock_gettime() values will be reported with -X .TP .B \-\-secaligned [USEC] align thread wakeups to the next full second and apply the optional offset. .TP .B \-s, \-\-system Use sys_nanosleep and sys_setitimer instead of posix timers. Note, that \-s can only be used with one thread because itimers are per process and not per thread. \-s uses the nanosleep syscall and is not restricted to one thread. .TP .B \\-S, \-\-smp Set options for standard testing on SMP systems. Equivalent to using the options: "\-t \-a" as well keeping any specified priority equal across all threads .TP .B \-\-spike= record all spikes > trigger .TP .B \-\-spike-nodes=[num of nodes] These are the maximum number of spikes we can record. .br The default is 1024 if not specified. .TP .B \\-\-smi Enable SMI count/detection on processors with SMI count support. .TP .B \-t, \-\-threads[=NUM] Set the number of test threads (default is 1). Create NUM test threads. If NUM is not specified, NUM is set to the number of available CPUs. See \-d, \-i and \-p for further information. .TP .B \-\-tracemark write a trace mark when \-b latency is exceeded. .TP .B \-u, \-\-unbuffered force unbuffered output for live processing .TP .B \-v, \-\-verbose Output values on stdout for statistics. This option is used to gather statistical information about the latency distribution. The output is sent to stdout. The output format is: n:c:v where n=task number c=count v=latency value in us. .TP .B \-\-dbg_cyclictest Print info userful for debugging cyclictest .TP .B \-x, \-\-posix_timers Use POSIX timers instead of clock_nanosleep. .SH SEE ALSO .BR numa (3), .BR numactl (8), .\" .br .\" The programs are documented fully by .\" .IR "The Rise and Fall of a Fooish Bar" , .\" available via the Info system. .SH AUTHOR cyclictest was written by Thomas Gleixner . .PP This manual page was written by Alessio Igor Bogani , for the Debian project (but may be used by others). .br Updated by John Kacur rt-tests-2.9/src/cyclictest/cyclictest.c000066400000000000000000001710501503076725400204350ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * High resolution timer test software * * (C) 2013 Clark Williams * (C) 2013 John Kacur * (C) 2008-2012 Clark Williams * (C) 2005-2007 Thomas Gleixner * */ #ifdef HAVE_LIBCPUPOWER_SUPPORT #include #endif /* HAVE_LIBCPUPOWER_SUPPORT */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt_numa.h" #include "rt-utils.h" #include "rt-numa.h" #include "rt-error.h" #include "histogram.h" #include #define DEFAULT_INTERVAL 1000 #define DEFAULT_DISTANCE 500 #ifndef SCHED_IDLE #define SCHED_IDLE 5 #endif #ifndef SCHED_NORMAL #define SCHED_NORMAL SCHED_OTHER #endif #define sigev_notify_thread_id _sigev_un._tid #ifdef __UCLIBC__ #define MAKE_PROCESS_CPUCLOCK(pid, clock) \ ((~(clockid_t) (pid) << 3) | (clockid_t) (clock)) #define CPUCLOCK_SCHED 2 static int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *req, struct timespec *rem) { if (clock_id == CLOCK_THREAD_CPUTIME_ID) return -EINVAL; if (clock_id == CLOCK_PROCESS_CPUTIME_ID) clock_id = MAKE_PROCESS_CPUCLOCK(0, CPUCLOCK_SCHED); return syscall(__NR_clock_nanosleep, clock_id, flags, req, rem); } int sched_setaffinity(__pid_t __pid, size_t __cpusetsize, __const cpu_set_t *__cpuset) { return -EINVAL; } #undef CPU_SET #undef CPU_ZERO #define CPU_SET(cpu, cpusetp) #define CPU_ZERO(cpusetp) #else extern int clock_nanosleep(clockid_t __clock_id, int __flags, __const struct timespec *__req, struct timespec *__rem); #endif /* __UCLIBC__ */ #define HIST_MAX 1000000 #define MODE_CYCLIC 0 #define MODE_CLOCK_NANOSLEEP 1 #define MODE_SYS_ITIMER 2 #define MODE_SYS_NANOSLEEP 3 #define MODE_SYS_OFFSET 2 #define TIMER_RELTIME 0 /* Must be power of 2 ! */ #define VALBUF_SIZE 16384 #if (defined(__i386__) || defined(__x86_64__)) #define ARCH_HAS_SMI_COUNTER #endif #define MSR_SMI_COUNT 0x00000034 #define MSR_SMI_COUNT_MASK 0xFFFFFFFF static char *policyname(int policy); /* Struct to transfer parameters to the thread */ struct thread_param { int prio; int policy; int mode; int timermode; int signal; int clock; unsigned long max_cycles; struct thread_stat *stats; int bufmsk; unsigned long interval; int cpu; int node; int tnum; int msr_fd; }; /* Struct for statistics */ struct thread_stat { unsigned long cycles; unsigned long cyclesread; long min; long max; long act; double avg; long *values; long *smis; struct histogram *hist; pthread_t thread; int threadstarted; int tid; long reduce; long redmax; long cycleofmax; unsigned long smi_count; }; static pthread_mutex_t trigger_lock = PTHREAD_MUTEX_INITIALIZER; static int trigger = 0; /* Record spikes > trigger, 0 means don't record */ static int trigger_list_size = 1024; /* Number of list nodes */ /* Info to store when the diff is greater than the trigger */ struct thread_trigger { int cpu; int tnum; /* thread number */ int64_t ts; /* time-stamp */ int diff; struct thread_trigger *next; }; struct thread_trigger *head = NULL; struct thread_trigger *tail = NULL; struct thread_trigger *current = NULL; static int spikes; /* count of the number of spikes */ static int trigger_init(); static void trigger_print(); static void trigger_update(struct thread_param *par, int diff, int64_t ts); static int shutdown; static int tracelimit = 0; static int trace_marker = 0; static int verbose = 0; static int oscope_reduction = 1; static int lockall = 0; static int histogram = 0; static int histofall = 0; static int duration = 0; static int use_nsecs = 0; static int refresh_on_max; static int force_sched_other; static int priospread = 0; static int check_clock_resolution; static int ct_debug; static int use_fifo = 0; static pthread_t fifo_threadid; static int laptop = 0; static int power_management = 0; static int use_histfile = 0; #ifdef ARCH_HAS_SMI_COUNTER static int smi = 0; #else #define smi 0 #endif static pthread_cond_t refresh_on_max_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t refresh_on_max_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t break_thread_id_lock = PTHREAD_MUTEX_INITIALIZER; static pid_t break_thread_id = 0; static uint64_t break_thread_value = 0; static int aligned = 0; static int secaligned = 0; static int offset = 0; static pthread_barrier_t align_barr; static pthread_barrier_t globalt_barr; static struct timespec globalt; static char fifopath[MAX_PATH]; static char histfile[MAX_PATH]; static char jsonfile[MAX_PATH]; static struct thread_param **parameters; static struct thread_stat **statistics; static struct histoset hset; static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet); static void rstat_print_stat(struct thread_param *par, int index, int verbose, int quiet); static void rstat_setup(void); static int latency_target_fd = -1; static int32_t latency_target_value = 0; static int deepest_idle_state = -2; static int rstat_ftruncate(int fd, off_t len); static int rstat_fd = -1; /* strlen("/cyclictest") + digits in max pid len + '\0' */ #define SHM_BUF_SIZE 19 static char shm_name[SHM_BUF_SIZE]; /* Latency trick * if the file /dev/cpu_dma_latency exists, * open it and write a zero into it. This will tell * the power management system not to transition to * a high cstate (in fact, the system acts like idle=poll) * When the fd to /dev/cpu_dma_latency is closed, the behavior * goes back to the system default. * * Documentation/power/pm_qos_interface.txt */ static void set_latency_target(void) { struct stat s; int err; if (laptop) { warn("not setting cpu_dma_latency to save battery power\n"); return; } if (power_management) { warn("not setting cpu_dma_latency from cyclictest\n"); return; } if (deepest_idle_state >= -1) { warn("not setting cpu_dma_latency, --deepest-idle-state is set instead\n"); return; } errno = 0; err = stat("/dev/cpu_dma_latency", &s); if (err == -1) { err_msg_n(errno, "WARN: stat /dev/cpu_dma_latency failed"); return; } errno = 0; latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR); if (latency_target_fd == -1) { err_msg_n(errno, "WARN: open /dev/cpu_dma_latency"); return; } errno = 0; err = write(latency_target_fd, &latency_target_value, 4); if (err < 1) { err_msg_n(errno, "# error setting cpu_dma_latency to %d!", latency_target_value); close(latency_target_fd); return; } printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value); } #ifdef HAVE_LIBCPUPOWER_SUPPORT static unsigned int **saved_cpu_idle_disable_state; static size_t saved_cpu_idle_disable_state_alloc_ctr; /* * save_cpu_idle_state_disable - save disable for all idle states of a cpu * * Saves the current disable of all idle states of a cpu, to be subsequently * restored via restore_cpu_idle_disable_state. * * Return: idle state count on success, negative on error */ static int save_cpu_idle_disable_state(unsigned int cpu) { unsigned int nr_states; unsigned int state; int disabled; int nr_cpus; nr_states = cpuidle_state_count(cpu); if (nr_states == 0) return 0; if (saved_cpu_idle_disable_state == NULL) { nr_cpus = sysconf(_SC_NPROCESSORS_CONF); saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsigned int *)); if (!saved_cpu_idle_disable_state) return -1; } saved_cpu_idle_disable_state[cpu] = calloc(nr_states, sizeof(unsigned int)); if (!saved_cpu_idle_disable_state[cpu]) return -1; saved_cpu_idle_disable_state_alloc_ctr++; for (state = 0; state < nr_states; state++) { disabled = cpuidle_is_state_disabled(cpu, state); if (disabled < 0) return disabled; saved_cpu_idle_disable_state[cpu][state] = disabled; } return nr_states; } /* * restore_cpu_idle_disable_state - restore disable for all idle states of a cpu * * Restores the current disable state of all idle states of a cpu that was * previously saved by save_cpu_idle_disable_state. * * Return: idle state count on success, negative on error */ static int restore_cpu_idle_disable_state(unsigned int cpu) { unsigned int nr_states; unsigned int state; int disabled; int result; nr_states = cpuidle_state_count(cpu); if (nr_states == 0) return 0; if (!saved_cpu_idle_disable_state) return -1; for (state = 0; state < nr_states; state++) { if (!saved_cpu_idle_disable_state[cpu]) return -1; disabled = saved_cpu_idle_disable_state[cpu][state]; result = cpuidle_state_disable(cpu, state, disabled); if (result < 0) return result; } free(saved_cpu_idle_disable_state[cpu]); saved_cpu_idle_disable_state[cpu] = NULL; saved_cpu_idle_disable_state_alloc_ctr--; if (saved_cpu_idle_disable_state_alloc_ctr == 0) { free(saved_cpu_idle_disable_state); saved_cpu_idle_disable_state = NULL; } return nr_states; } /* * free_cpu_idle_disable_states - free saved idle state disable for all cpus * * Frees the memory used for storing cpu idle state disable for all cpus * and states. * * Normally, the memory is freed automatically in * restore_cpu_idle_disable_state; this is mostly for cleaning up after an * error. */ static void free_cpu_idle_disable_states(void) { int cpu; int nr_cpus; if (!saved_cpu_idle_disable_state) return; nr_cpus = sysconf(_SC_NPROCESSORS_CONF); for (cpu = 0; cpu < nr_cpus; cpu++) { free(saved_cpu_idle_disable_state[cpu]); saved_cpu_idle_disable_state[cpu] = NULL; } free(saved_cpu_idle_disable_state); saved_cpu_idle_disable_state = NULL; } /* * set_deepest_cpu_idle_state - limit idle state of cpu * * Disables all idle states deeper than the one given in * deepest_state (assuming states with higher number are deeper). * * This is used to reduce the exit from idle latency. Unlike * set_cpu_dma_latency, it can disable idle states per cpu. * * Return: idle state count on success, negative on error */ static int set_deepest_cpu_idle_state(unsigned int cpu, unsigned int deepest_state) { unsigned int nr_states; unsigned int state; int result; nr_states = cpuidle_state_count(cpu); for (state = deepest_state + 1; state < nr_states; state++) { result = cpuidle_state_disable(cpu, state, 1); if (result < 0) return result; } return nr_states; } static inline int have_libcpupower_support(void) { return 1; } #else static inline int save_cpu_idle_disable_state(unsigned int cpu) { return -1; } static inline int restore_cpu_idle_disable_state(unsigned int cpu) { return -1; } static inline void free_cpu_idle_disable_states(void) { } static inline int set_deepest_cpu_idle_state(unsigned int cpu, unsigned int state) { return -1; } static inline int have_libcpupower_support(void) { return 0; } #endif /* HAVE_LIBCPUPOWER_SUPPORT */ enum { ERROR_GENERAL = -1, ERROR_NOTFOUND = -2, }; /* * Raise the soft priority limit up to prio, if that is less than or equal * to the hard limit * if a call fails, return the error * if successful return 0 * if fails, return -1 */ static int raise_soft_prio(int policy, const struct sched_param *param) { int err; int policy_max; /* max for scheduling policy such as SCHED_FIFO */ int soft_max; int hard_max; int prio; struct rlimit rlim; prio = param->sched_priority; policy_max = sched_get_priority_max(policy); if (policy_max == -1) { err = errno; err_msg("WARN: no such policy\n"); return err; } err = getrlimit(RLIMIT_RTPRIO, &rlim); if (err) { err = errno; err_msg_n(err, "WARN: getrlimit failed"); return err; } soft_max = (rlim.rlim_cur == RLIM_INFINITY) ? (unsigned int)policy_max : rlim.rlim_cur; hard_max = (rlim.rlim_max == RLIM_INFINITY) ? (unsigned int)policy_max : rlim.rlim_max; if (prio > soft_max && prio <= hard_max) { rlim.rlim_cur = prio; err = setrlimit(RLIMIT_RTPRIO, &rlim); if (err) { err = errno; err_msg_n(err, "WARN: setrlimit failed"); /* return err; */ } } else { err = -1; } return err; } /* * Check the error status of sched_setscheduler * If an error can be corrected by raising the soft limit priority to * a priority less than or equal to the hard limit, then do so. */ static int setscheduler(pid_t pid, int policy, const struct sched_param *param) { int err = 0; try_again: err = sched_setscheduler(pid, policy, param); if (err) { err = errno; if (err == EPERM) { int err1; err1 = raise_soft_prio(policy, param); if (!err1) goto try_again; } } return err; } #ifdef ARCH_HAS_SMI_COUNTER static int open_msr_file(int cpu) { int fd; char pathname[32]; /* SMI needs thread affinity */ sprintf(pathname, "/dev/cpu/%d/msr", cpu); fd = open(pathname, O_RDONLY); if (fd < 0) warn("%s open failed, try chown or chmod +r " "/dev/cpu/*/msr, or run as root\n", pathname); return fd; } static int get_msr(int fd, off_t offset, unsigned long long *msr) { ssize_t retval; retval = pread(fd, msr, sizeof *msr, offset); if (retval != sizeof *msr) return 1; return 0; } static int get_smi_counter(int fd, unsigned long *counter) { int retval; unsigned long long msr; retval = get_msr(fd, MSR_SMI_COUNT, &msr); if (retval) return retval; *counter = (unsigned long) (msr & MSR_SMI_COUNT_MASK); return 0; } #include /* Guess if the CPU has an SMI counter in the model-specific registers (MSR). * * This is true for Intel x86 CPUs after Nehalem (2008). However, it's not * possible to detect the feature directly, (or at least, turbostat.c doesn't * know how to do it either) so we assume it's true for all x86 CPUs. * Previously, this function had an explicit allowlist, which required updates * every time a new CPU generation was released. */ static int has_smi_counter(void) { unsigned int ebx, ecx, edx, max_level; unsigned int fms, family, model; fms = family = model = ebx = ecx = edx = 0; __get_cpuid(0, &max_level, &ebx, &ecx, &edx); /* check genuine intel */ if (!(ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e)) return 0; __get_cpuid(1, &fms, &ebx, &ecx, &edx); family = (fms >> 8) & 0xf; if (family != 6) return 0; /* no MSR */ if (!(edx & (1 << 5))) return 0; return 1; } #else static int open_msr_file(int cpu) { return -1; } static int get_smi_counter(int fd, unsigned long *counter) { return 1; } static int has_smi_counter(void) { return 0; } #endif /* * timer thread * * Modes: * - clock_nanosleep based * - cyclic timer based * * Clock: * - CLOCK_MONOTONIC * - CLOCK_REALTIME * */ static void *timerthread(void *param) { struct thread_param *par = param; struct sched_param schedp; struct sigevent sigev; sigset_t sigset; timer_t timer; struct timespec now, next, interval, stop = { 0 }; struct itimerval itimer; struct itimerspec tspec; struct thread_stat *stat = par->stats; int stopped = 0; cpu_set_t mask; pthread_t thread; unsigned long smi_now, smi_old = 0; /* if we're running in numa mode, set our memory node */ if (par->node != -1) rt_numa_set_numa_run_on_node(par->node, par->cpu); if (par->cpu != -1) { CPU_ZERO(&mask); CPU_SET(par->cpu, &mask); thread = pthread_self(); if (pthread_setaffinity_np(thread, sizeof(mask), &mask) != 0) warn("Could not set CPU affinity to CPU #%d\n", par->cpu); } interval.tv_sec = par->interval / USEC_PER_SEC; interval.tv_nsec = (par->interval % USEC_PER_SEC) * 1000; stat->tid = gettid(); sigemptyset(&sigset); sigaddset(&sigset, par->signal); sigprocmask(SIG_BLOCK, &sigset, NULL); if (par->mode == MODE_CYCLIC) { sigev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL; sigev.sigev_signo = par->signal; sigev.sigev_notify_thread_id = stat->tid; timer_create(par->clock, &sigev, &timer); tspec.it_interval = interval; } memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->prio; if (setscheduler(0, par->policy, &schedp)) fatal("timerthread%d: failed to set priority to %d\n", par->cpu, par->prio); if (smi) { par->msr_fd = open_msr_file(par->cpu); if (par->msr_fd < 0) fatal("Could not open MSR interface, errno: %d\n", errno); /* get current smi count to use as base value */ if (get_smi_counter(par->msr_fd, &smi_old)) fatal("Could not read SMI counter, errno: %d\n", par->cpu, errno); } /* Get current time */ if (aligned || secaligned) { pthread_barrier_wait(&globalt_barr); if (par->tnum == 0) { clock_gettime(par->clock, &globalt); if (secaligned) { /* Ensure that the thread start timestamp is not * in the past */ if (globalt.tv_nsec > 900000000) globalt.tv_sec += 2; else globalt.tv_sec++; globalt.tv_nsec = 0; } } pthread_barrier_wait(&align_barr); now = globalt; if (offset) { if (aligned) now.tv_nsec += offset * par->tnum; else now.tv_nsec += offset; tsnorm(&now); } } else clock_gettime(par->clock, &now); next = now; next.tv_sec += interval.tv_sec; next.tv_nsec += interval.tv_nsec; tsnorm(&next); if (duration) { stop = now; stop.tv_sec += duration; } if (par->mode == MODE_CYCLIC) { if (par->timermode == TIMER_ABSTIME) tspec.it_value = next; else tspec.it_value = interval; timer_settime(timer, par->timermode, &tspec, NULL); } if (par->mode == MODE_SYS_ITIMER) { itimer.it_interval.tv_sec = interval.tv_sec; itimer.it_interval.tv_usec = interval.tv_nsec / 1000; itimer.it_value = itimer.it_interval; setitimer(ITIMER_REAL, &itimer, NULL); } stat->threadstarted++; while (!shutdown) { uint64_t diff; unsigned long diff_smi = 0; int sigs, ret; /* Wait for next period */ switch (par->mode) { case MODE_CYCLIC: case MODE_SYS_ITIMER: if (sigwait(&sigset, &sigs) < 0) goto out; break; case MODE_CLOCK_NANOSLEEP: if (par->timermode == TIMER_ABSTIME) { ret = clock_nanosleep(par->clock, TIMER_ABSTIME, &next, NULL); if (ret != 0) { if (ret != EINTR) warn("clock_nanosleep failed. errno: %d\n", errno); goto out; } } else { ret = clock_gettime(par->clock, &now); if (ret != 0) { if (ret != EINTR) warn("clock_gettime() failed: %s", strerror(errno)); goto out; } ret = clock_nanosleep(par->clock, TIMER_RELTIME, &interval, NULL); if (ret != 0) { if (ret != EINTR) warn("clock_nanosleep() failed. errno: %d\n", errno); goto out; } next.tv_sec = now.tv_sec + interval.tv_sec; next.tv_nsec = now.tv_nsec + interval.tv_nsec; tsnorm(&next); } break; case MODE_SYS_NANOSLEEP: ret = clock_gettime(par->clock, &now); if (ret != 0) { if (ret != EINTR) warn("clock_gettime() failed: errno %d\n", errno); goto out; } if (nanosleep(&interval, NULL)) { if (errno != EINTR) warn("nanosleep failed. errno: %d\n", errno); goto out; } next.tv_sec = now.tv_sec + interval.tv_sec; next.tv_nsec = now.tv_nsec + interval.tv_nsec; tsnorm(&next); break; } ret = clock_gettime(par->clock, &now); if (ret != 0) { if (ret != EINTR) warn("clock_gettime() failed. errno: %d\n", errno); goto out; } if (smi) { if (get_smi_counter(par->msr_fd, &smi_now)) { warn("Could not read SMI counter, errno: %d\n", par->cpu, errno); goto out; } diff_smi = smi_now - smi_old; stat->smi_count += diff_smi; smi_old = smi_now; } if (use_nsecs) diff = calcdiff_ns(now, next); else diff = calcdiff(now, next); if (diff < stat->min) stat->min = diff; if (diff > stat->max) { stat->max = diff; if (refresh_on_max) pthread_cond_signal(&refresh_on_max_cond); } stat->avg += (double) diff; if (trigger && (diff > trigger)) trigger_update(par, diff, calctime(now)); if (duration && (calcdiff(now, stop) >= 0)) shutdown++; if (!stopped && tracelimit && (diff > tracelimit)) { stopped++; shutdown++; pthread_mutex_lock(&break_thread_id_lock); if (break_thread_id == 0) { break_thread_id = stat->tid; tracemark("hit latency threshold (%llu > %d)", (unsigned long long) diff, tracelimit); break_thread_value = diff; } pthread_mutex_unlock(&break_thread_id_lock); } stat->act = diff; if (par->bufmsk) { stat->values[stat->cycles & par->bufmsk] = diff; if (smi) stat->smis[stat->cycles & par->bufmsk] = diff_smi; } /* Update the histogram */ if (histogram) hist_sample(stat->hist, diff); stat->cycles++; next.tv_sec += interval.tv_sec; next.tv_nsec += interval.tv_nsec; if (par->mode == MODE_CYCLIC) { int overrun_count = timer_getoverrun(timer); next.tv_sec += overrun_count * interval.tv_sec; next.tv_nsec += overrun_count * interval.tv_nsec; } tsnorm(&next); while (tsgreater(&now, &next)) { next.tv_sec += interval.tv_sec; next.tv_nsec += interval.tv_nsec; tsnorm(&next); } if (par->max_cycles && par->max_cycles == stat->cycles) break; } out: if (refresh_on_max) { pthread_mutex_lock(&refresh_on_max_lock); /* We could reach here with both shutdown and allstopped unset (0). * Set shutdown with synchronization to notify the main * thread not to be blocked when it should exit. */ shutdown++; pthread_cond_signal(&refresh_on_max_cond); pthread_mutex_unlock(&refresh_on_max_lock); } if (par->mode == MODE_CYCLIC) timer_delete(timer); if (par->mode == MODE_SYS_ITIMER) { itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, NULL); } /* close msr file */ if (smi) close(par->msr_fd); /* switch to normal */ schedp.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &schedp); stat->threadstarted = -1; return NULL; } /* Print usage information */ static void display_help(int error) { printf("cyclictest V %1.2f\n", VERSION); printf("Usage:\n" "cyclictest \n\n" "-a [CPUSET] --affinity Run thread #N on processor #N, if possible,\n" " or if CPUSET given, pin threads to that set of\n" " processors in round-robin order.\n" " E.g. -a 2 pins all threads to CPU 2, but\n" " -a 3-5,0 -t 5 will run the first and fifth threads\n" " on CPU 0, the second thread on CPU 3,\n" " the third thread on CPU 4,\n" " and the fourth thread on CPU 5.\n" "-A USEC --aligned=USEC align thread wakeups to a specific offset\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-c CLOCK --clock=CLOCK select clock\n" " 0 = CLOCK_MONOTONIC (default)\n" " 1 = CLOCK_REALTIME\n" " --deepest-idle-state=n\n" " Reduce exit from idle latency by limiting idle state\n" " up to n on used cpus (-1 disables all idle states).\n" " Power management is not suppresed on other cpus.\n" " --default-system Don't attempt to tune the system from cyclictest.\n" " Power management is not suppressed.\n" " This might give poorer results, but will allow you\n" " to discover if you need to tune the system\n" "-d DIST --distance=DIST distance of thread intervals in us, default=500\n" "-D --duration=TIME specify a length for the test run.\n" " Append 'm', 'h', or 'd' to specify minutes, hours or days.\n" "-F --fifo= create a named pipe at path and write stats to it\n" "-h --histogram=US dump a latency histogram to stdout after the run\n" " US is the max latency time to be tracked in microseconds\n" " This option runs all threads at the same priority.\n" "-H --histofall=US same as -h except with an additional summary column\n" " --histfile= dump the latency histogram to instead of stdout\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" " --json=FILENAME write final results into FILENAME, JSON formatted\n" " --laptop Save battery when running cyclictest\n" " This will give you poorer realtime results\n" " but will not drain your battery so quickly\n" " --latency=PM_QOS power management latency target value\n" " This value is written to /dev/cpu_dma_latency\n" " and affects c-states. The default is 0\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" " --mainaffinity=CPUSET\n" " Run the main thread on CPU #N. This only affects\n" " the main thread and not the measurement threads\n" "-m --mlockall lock current and future memory allocations\n" "-M --refresh_on_max delay updating the screen until a new max\n" " latency is hit. Useful for low bandwidth.\n" "-N --nsecs print results in ns instead of us (default us)\n" "-o RED --oscope=RED oscilloscope mode, reduce verbose output by RED\n" "-p PRIO --priority=PRIO priority of highest prio thread\n" " --policy=NAME policy of measurement thread, where NAME may be one\n" " of: other, normal, batch, idle, fifo or rr.\n" " --priospread spread priority levels starting at specified value\n" "-q --quiet print a summary only on exit\n" "-r --relative use relative timer instead of absolute\n" "-R --resolution check clock resolution, calling clock_gettime() many\n" " times. List of clock_gettime() values will be\n" " reported with -X\n" " --secaligned [USEC] align thread wakeups to the next full second\n" " and apply the optional offset\n" "-s --system use sys_nanosleep and sys_setitimer\n" "-S --smp Standard SMP testing: options -a -t and same priority\n" " of all threads\n" " --spike= record all spikes > trigger\n" " --spike-nodes=[num of nodes]\n" " These are the maximum number of spikes we can record.\n" " The default is 1024 if not specified\n" #ifdef ARCH_HAS_SMI_COUNTER " --smi Enable SMI counting\n" #endif "-t --threads one thread per available processor\n" "-t [NUM] --threads=NUM number of threads:\n" " without NUM, threads = max_cpus\n" " without -t default = 1\n" " --tracemark write a trace mark when -b latency is exceeded\n" "-u --unbuffered force unbuffered output for live processing\n" "-v --verbose output values on stdout for statistics\n" " format: n:c:v n=tasknum c=count v=value in us\n" " --dbg_cyclictest print info useful for debugging cyclictest\n" "-x --posix_timers use POSIX timers instead of clock_nanosleep.\n" ); if (error) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } static int use_nanosleep = MODE_CLOCK_NANOSLEEP; static int timermode = TIMER_ABSTIME; static int use_system; static int priority; static int policy = SCHED_OTHER; /* default policy if not specified */ static int num_threads = 1; static int max_cycles; static int clocksel = 0; static int quiet; static int interval = DEFAULT_INTERVAL; static int distance = -1; static struct bitmask *affinity_mask = NULL; static struct bitmask *main_affinity_mask = NULL; static int smp = 0; static int setaffinity = AFFINITY_UNSPECIFIED; static int clocksources[] = { CLOCK_MONOTONIC, CLOCK_REALTIME, }; static void handlepolicy(char *polname) { if (strncasecmp(polname, "other", 5) == 0) policy = SCHED_OTHER; else if (strncasecmp(polname, "batch", 5) == 0) policy = SCHED_BATCH; else if (strncasecmp(polname, "idle", 4) == 0) policy = SCHED_IDLE; else if (strncasecmp(polname, "fifo", 4) == 0) policy = SCHED_FIFO; else if (strncasecmp(polname, "rr", 2) == 0) policy = SCHED_RR; else /* default policy if we don't recognize the request */ policy = SCHED_OTHER; } static char *policyname(int policy) { char *policystr = ""; switch(policy) { case SCHED_OTHER: policystr = "other"; break; case SCHED_FIFO: policystr = "fifo"; break; case SCHED_RR: policystr = "rr"; break; case SCHED_BATCH: policystr = "batch"; break; case SCHED_IDLE: policystr = "idle"; break; } return policystr; } enum option_values { OPT_AFFINITY=1, OPT_BREAKTRACE, OPT_CLOCK, OPT_DEFAULT_SYSTEM, OPT_DISTANCE, OPT_DURATION, OPT_LATENCY, OPT_FIFO, OPT_HISTOGRAM, OPT_HISTOFALL, OPT_HISTFILE, OPT_INTERVAL, OPT_JSON, OPT_MAINAFFINITY, OPT_LOOPS, OPT_MLOCKALL, OPT_REFRESH, OPT_NANOSLEEP, OPT_NSECS, OPT_OSCOPE, OPT_PRIORITY, OPT_QUIET, OPT_PRIOSPREAD, OPT_RELATIVE, OPT_RESOLUTION, OPT_SYSTEM, OPT_SMP, OPT_THREADS, OPT_TRIGGER, OPT_TRIGGER_NODES, OPT_UNBUFFERED, OPT_NUMA, OPT_VERBOSE, OPT_DBGCYCLIC, OPT_POLICY, OPT_HELP, OPT_NUMOPTS, OPT_ALIGNED, OPT_SECALIGNED, OPT_LAPTOP, OPT_SMI, OPT_TRACEMARK, OPT_POSIX_TIMERS, OPT_DEEPEST_IDLE_STATE, }; /* Process commandline options */ static void process_options(int argc, char *argv[], int max_cpus) { int error = 0; int option_affinity = 0; for (;;) { int option_index = 0; /* * Options for getopt * Ordered alphabetically by single letter name */ static struct option long_options[] = { {"affinity", optional_argument, NULL, OPT_AFFINITY}, {"aligned", optional_argument, NULL, OPT_ALIGNED }, {"breaktrace", required_argument, NULL, OPT_BREAKTRACE }, {"clock", required_argument, NULL, OPT_CLOCK }, {"default-system", no_argument, NULL, OPT_DEFAULT_SYSTEM }, {"distance", required_argument, NULL, OPT_DISTANCE }, {"duration", required_argument, NULL, OPT_DURATION }, {"latency", required_argument, NULL, OPT_LATENCY }, {"fifo", required_argument, NULL, OPT_FIFO }, {"histogram", required_argument, NULL, OPT_HISTOGRAM }, {"histofall", required_argument, NULL, OPT_HISTOFALL }, {"histfile", required_argument, NULL, OPT_HISTFILE }, {"interval", required_argument, NULL, OPT_INTERVAL }, {"json", required_argument, NULL, OPT_JSON }, {"laptop", no_argument, NULL, OPT_LAPTOP }, {"loops", required_argument, NULL, OPT_LOOPS }, {"mainaffinity", required_argument, NULL, OPT_MAINAFFINITY}, {"mlockall", no_argument, NULL, OPT_MLOCKALL }, {"refresh_on_max", no_argument, NULL, OPT_REFRESH }, {"nsecs", no_argument, NULL, OPT_NSECS }, {"oscope", required_argument, NULL, OPT_OSCOPE }, {"priority", required_argument, NULL, OPT_PRIORITY }, {"quiet", no_argument, NULL, OPT_QUIET }, {"priospread", no_argument, NULL, OPT_PRIOSPREAD }, {"relative", no_argument, NULL, OPT_RELATIVE }, {"resolution", no_argument, NULL, OPT_RESOLUTION }, {"secaligned", optional_argument, NULL, OPT_SECALIGNED }, {"system", no_argument, NULL, OPT_SYSTEM }, {"smi", no_argument, NULL, OPT_SMI }, {"smp", no_argument, NULL, OPT_SMP }, {"spike", required_argument, NULL, OPT_TRIGGER }, {"spike-nodes", required_argument, NULL, OPT_TRIGGER_NODES }, {"threads", optional_argument, NULL, OPT_THREADS }, {"tracemark", no_argument, NULL, OPT_TRACEMARK }, {"unbuffered", no_argument, NULL, OPT_UNBUFFERED }, {"verbose", no_argument, NULL, OPT_VERBOSE }, {"dbg_cyclictest", no_argument, NULL, OPT_DBGCYCLIC }, {"policy", required_argument, NULL, OPT_POLICY }, {"help", no_argument, NULL, OPT_HELP }, {"posix_timers", no_argument, NULL, OPT_POSIX_TIMERS }, {"deepest-idle-state", required_argument, NULL, OPT_DEEPEST_IDLE_STATE }, {NULL, 0, NULL, 0 }, }; int c = getopt_long(argc, argv, "a::A::b:c:d:D:F:h:H:i:l:MNo:p:mqrRsSt::uvD:x", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': case OPT_AFFINITY: option_affinity = 1; /* smp sets AFFINITY_USEALL in OPT_SMP */ if (smp) break; numa = numa_initialize(); if (optarg) { parse_cpumask(optarg, max_cpus, &affinity_mask); setaffinity = AFFINITY_SPECIFIED; } else if (optind < argc && (atoi(argv[optind]) || argv[optind][0] == '0' || argv[optind][0] == '!' || argv[optind][0] == '+' || argv[optind][0] == 'a')) { parse_cpumask(argv[optind], max_cpus, &affinity_mask); setaffinity = AFFINITY_SPECIFIED; } else { setaffinity = AFFINITY_USEALL; } if (setaffinity == AFFINITY_SPECIFIED && !affinity_mask) display_help(1); if (verbose && affinity_mask) printf("Using %u cpus.\n", numa_bitmask_weight(affinity_mask)); break; case 'A': case OPT_ALIGNED: aligned = 1; if (optarg != NULL) offset = atoi(optarg) * 1000; else if (optind < argc && atoi(argv[optind])) offset = atoi(argv[optind]) * 1000; else offset = 0; break; case 'b': case OPT_BREAKTRACE: tracelimit = atoi(optarg); break; case 'c': case OPT_CLOCK: clocksel = atoi(optarg); break; case OPT_DEFAULT_SYSTEM: power_management = 1; break; case 'd': case OPT_DISTANCE: distance = atoi(optarg); break; case 'D': case OPT_DURATION: duration = parse_time_string(optarg); break; case 'F': case OPT_FIFO: use_fifo = 1; strncpy(fifopath, optarg, strnlen(optarg, MAX_PATH-1)); break; case 'H': case OPT_HISTOFALL: histofall = 1; /* fall through */ case 'h': case OPT_HISTOGRAM: histogram = atoi(optarg); if (!histogram) display_help(1); break; case OPT_HISTFILE: use_histfile = 1; strncpy(histfile, optarg, strnlen(optarg, MAX_PATH-1)); break; case 'i': case OPT_INTERVAL: interval = atoi(optarg); break; case OPT_JSON: strncpy(jsonfile, optarg, strnlen(optarg, MAX_PATH-1)); break; case 'l': case OPT_LOOPS: max_cycles = atoi(optarg); break; case OPT_MAINAFFINITY: if (optarg) { parse_cpumask(optarg, max_cpus, &main_affinity_mask); } else if (optind < argc && (atoi(argv[optind]) || argv[optind][0] == '0' || argv[optind][0] == '!')) { parse_cpumask(argv[optind], max_cpus, &main_affinity_mask); } break; case 'm': case OPT_MLOCKALL: lockall = 1; break; case 'M': case OPT_REFRESH: refresh_on_max = 1; break; case 'N': case OPT_NSECS: use_nsecs = 1; break; case 'o': case OPT_OSCOPE: oscope_reduction = atoi(optarg); break; case 'p': case OPT_PRIORITY: priority = atoi(optarg); if (policy != SCHED_FIFO && policy != SCHED_RR) policy = SCHED_FIFO; break; case 'q': case OPT_QUIET: quiet = 1; break; case 'r': case OPT_RELATIVE: timermode = TIMER_RELTIME; break; case 'R': case OPT_RESOLUTION: check_clock_resolution = 1; break; case OPT_SECALIGNED: secaligned = 1; if (optarg != NULL) offset = atoi(optarg) * 1000; else if (optind < argc && atoi(argv[optind])) offset = atoi(argv[optind]) * 1000; else offset = 0; break; case 's': case OPT_SYSTEM: use_system = MODE_SYS_OFFSET; break; case 'S': case OPT_SMP: /* SMP testing */ if (numa) fatal("numa and smp options are mutually exclusive\n"); smp = 1; num_threads = -1; /* update after parsing */ setaffinity = AFFINITY_USEALL; break; case 't': case OPT_THREADS: if (smp) { warn("-t ignored due to smp mode\n"); break; } if (optarg != NULL) num_threads = atoi(optarg); else if (optind < argc && atoi(argv[optind])) num_threads = atoi(argv[optind]); else num_threads = -1; /* update after parsing */ break; case OPT_TRIGGER: trigger = atoi(optarg); break; case OPT_TRIGGER_NODES: if (trigger) trigger_list_size = atoi(optarg); break; case 'u': case OPT_UNBUFFERED: setvbuf(stdout, NULL, _IONBF, 0); break; case 'v': case OPT_VERBOSE: verbose = 1; break; case 'x': case OPT_POSIX_TIMERS: use_nanosleep = MODE_CYCLIC; break; case '?': case OPT_HELP: display_help(0); break; /* long only options */ case OPT_PRIOSPREAD: priospread = 1; break; case OPT_LATENCY: /* power management latency target value */ /* note: default is 0 (zero) */ latency_target_value = atoi(optarg); if (latency_target_value < 0) latency_target_value = 0; break; case OPT_POLICY: handlepolicy(optarg); break; case OPT_DBGCYCLIC: ct_debug = 1; break; case OPT_LAPTOP: laptop = 1; break; case OPT_SMI: #ifdef ARCH_HAS_SMI_COUNTER smi = 1; #else fatal("--smi is not available on your arch\n"); #endif break; case OPT_TRACEMARK: trace_marker = 1; break; case OPT_DEEPEST_IDLE_STATE: deepest_idle_state = atoi(optarg); break; } } if ((use_system == MODE_SYS_OFFSET) && (use_nanosleep == MODE_CYCLIC)) { warn("The system option requires clock_nanosleep\n"); warn("and is not compatible with posix_timers\n"); warn("Using clock_nanosleep\n"); use_nanosleep = MODE_CLOCK_NANOSLEEP; } /* if smp wasn't requested, test for numa automatically */ if (!smp) { numa = numa_initialize(); } if (option_affinity) { if (smp) warn("-a ignored due to smp mode\n"); } if (smi) { if (setaffinity == AFFINITY_UNSPECIFIED) fatal("SMI counter relies on thread affinity\n"); if (!has_smi_counter()) fatal("SMI counter is not supported " "on this processor\n"); } if (clocksel < 0 || clocksel > ARRAY_SIZE(clocksources)) error = 1; if (oscope_reduction < 1) error = 1; if (oscope_reduction > 1 && !verbose) { warn("-o option only meaningful, if verbose\n"); error = 1; } if (histogram < 0) error = 1; if (histogram > HIST_MAX) histogram = HIST_MAX; if (histogram && distance != -1) warn("distance is ignored and set to 0, if histogram enabled\n"); if (distance == -1) distance = DEFAULT_DISTANCE; if (priority < 0 || priority > 99) error = 1; if (num_threads == -1) num_threads = get_available_cpus(affinity_mask); if (priospread && priority == 0) { fprintf(stderr, "defaulting realtime priority to %d\n", num_threads+1); priority = num_threads+1; } if (priority && (policy != SCHED_FIFO && policy != SCHED_RR)) { fprintf(stderr, "policy and priority don't match: setting policy to SCHED_FIFO\n"); policy = SCHED_FIFO; } if ((policy == SCHED_FIFO || policy == SCHED_RR) && priority == 0) { fprintf(stderr, "defaulting realtime priority to %d\n", num_threads+1); priority = num_threads+1; } if (num_threads < 1) error = 1; if (aligned && secaligned) error = 1; if (aligned || secaligned) { pthread_barrier_init(&globalt_barr, NULL, num_threads); pthread_barrier_init(&align_barr, NULL, num_threads); } if (error) { if (affinity_mask) rt_bitmask_free(affinity_mask); display_help(1); } } static int check_timer(void) { struct timespec ts; if (clock_getres(CLOCK_MONOTONIC, &ts)) return 1; return (ts.tv_sec != 0 || ts.tv_nsec != 1); } static void sighand(int sig) { if (sig == SIGUSR1) { int i; int oldquiet = quiet; quiet = 0; fprintf(stderr, "#---------------------------\n"); fprintf(stderr, "# cyclictest current status:\n"); for (i = 0; i < num_threads; i++) print_stat(stderr, parameters[i], i, 0, 0); fprintf(stderr, "#---------------------------\n"); quiet = oldquiet; return; } else if (sig == SIGUSR2) { int i; int oldquiet = quiet; if (rstat_fd == -1) { fprintf(stderr, "ERROR: rstat_fd not valid\n"); return; } rstat_ftruncate(rstat_fd, 0); quiet = 0; dprintf(rstat_fd, "#---------------------------\n"); dprintf(rstat_fd, "# cyclictest current status:\n"); for (i = 0; i < num_threads; i++) rstat_print_stat(parameters[i], i, 0, 0); dprintf(rstat_fd, "#---------------------------\n"); quiet = oldquiet; return; } shutdown = 1; if (refresh_on_max) pthread_cond_signal(&refresh_on_max_cond); } static void print_tids(struct thread_param *par[], int nthreads) { int i; printf("# Thread Ids:"); for (i = 0; i < nthreads; i++) printf(" %05d", par[i]->stats->tid); printf("\n"); } static void print_hist(struct thread_param *par[], int nthreads) { int i, j; unsigned long maxmax, alloverflows; FILE *fd; if (use_histfile) { fd = fopen(histfile, "w"); if (!fd) { perror("opening histogram file:"); return; } } else { fd = stdout; } fprintf(fd, "# Histogram\n"); for (i = 0; i < histogram; i++) { unsigned long flags = 0; char buf[64]; snprintf(buf, sizeof(buf), "%06d ", i); if (histofall) flags |= HSET_PRINT_SUM; hset_print_bucket(&hset, fd, buf, i, flags); } fprintf(fd, "# Min Latencies:"); for (j = 0; j < nthreads; j++) fprintf(fd, " %05lu", par[j]->stats->min); fprintf(fd, "\n"); fprintf(fd, "# Avg Latencies:"); for (j = 0; j < nthreads; j++) fprintf(fd, " %05lu", par[j]->stats->cycles ? (long)(par[j]->stats->avg/par[j]->stats->cycles) : 0); fprintf(fd, "\n"); fprintf(fd, "# Max Latencies:"); maxmax = 0; for (j = 0; j < nthreads; j++) { fprintf(fd, " %05lu", par[j]->stats->max); if (par[j]->stats->max > maxmax) maxmax = par[j]->stats->max; } if (histofall && nthreads > 1) fprintf(fd, " %05lu", maxmax); fprintf(fd, "\n"); fprintf(fd, "# Histogram Overflows:"); alloverflows = 0; for (j = 0; j < nthreads; j++) { fprintf(fd, " %05lu", par[j]->stats->hist->oflow_count); alloverflows += par[j]->stats->hist->oflow_count; } if (histofall && nthreads > 1) fprintf(fd, " %05lu", alloverflows); fprintf(fd, "\n"); fprintf(fd, "# Histogram Overflow at cycle number:\n"); for (i = 0; i < nthreads; i++) { fprintf(fd, "# Thread %d: ", i); hist_print_oflows(par[i]->stats->hist, fd); fprintf(fd, "\n"); } if (smi) { fprintf(fd, "# SMIs:"); for (i = 0; i < nthreads; i++) fprintf(fd, " %05lu", par[i]->stats->smi_count); fprintf(fd, "\n"); } fprintf(fd, "\n"); if (use_histfile) fclose(fd); } static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet) { struct thread_stat *stat = par->stats; if (!verbose) { if (quiet != 1) { char *fmt; if (use_nsecs) fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu " "Min:%7ld Act:%8ld Avg:%8ld Max:%8ld"; else fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu " "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld"; fprintf(fp, fmt, index, stat->tid, par->prio, par->interval, stat->cycles, stat->min, stat->act, stat->cycles ? (long)(stat->avg/stat->cycles) : 0, stat->max); if (smi) fprintf(fp, " SMI:%8ld", stat->smi_count); fprintf(fp, "\n"); } } else { while (stat->cycles != stat->cyclesread) { unsigned long diff_smi; long diff = stat->values [stat->cyclesread & par->bufmsk]; if (smi) diff_smi = stat->smis [stat->cyclesread & par->bufmsk]; if (diff > stat->redmax) { stat->redmax = diff; stat->cycleofmax = stat->cyclesread; } if (++stat->reduce == oscope_reduction) { if (!smi) fprintf(fp, "%8d:%8lu:%8ld\n", index, stat->cycleofmax, stat->redmax); else fprintf(fp, "%8d:%8lu:%8ld%8ld\n", index, stat->cycleofmax, stat->redmax, diff_smi); stat->reduce = 0; stat->redmax = 0; } stat->cyclesread++; } } } static void rstat_print_stat(struct thread_param *par, int index, int verbose, int quiet) { struct thread_stat *stat = par->stats; int fd = rstat_fd; if (!verbose) { if (quiet != 1) { char *fmt; if (use_nsecs) fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu " "Min:%7ld Act:%8ld Avg:%8ld Max:%8ld"; else fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu " "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld"; dprintf(fd, fmt, index, stat->tid, par->prio, par->interval, stat->cycles, stat->min, stat->act, stat->cycles ? (long)(stat->avg/stat->cycles) : 0, stat->max); if (smi) dprintf(fd, " SMI:%8ld", stat->smi_count); dprintf(fd, "\n"); } } else { while (stat->cycles != stat->cyclesread) { unsigned long diff_smi; long diff = stat->values [stat->cyclesread & par->bufmsk]; if (smi) diff_smi = stat->smis [stat->cyclesread & par->bufmsk]; if (diff > stat->redmax) { stat->redmax = diff; stat->cycleofmax = stat->cyclesread; } if (++stat->reduce == oscope_reduction) { if (!smi) dprintf(fd, "%8d:%8lu:%8ld\n", index, stat->cycleofmax, stat->redmax); else dprintf(fd, "%8d:%8lu:%8ld%8ld\n", index, stat->cycleofmax, stat->redmax, diff_smi); stat->reduce = 0; stat->redmax = 0; } stat->cyclesread++; } } } /* * thread that creates a named fifo and hands out run stats when someone * reads from the fifo. */ static void *fifothread(void *param) { int ret; int fd; FILE *fp; int i; unlink(fifopath); ret = mkfifo(fifopath, 0666); if (ret) { fprintf(stderr, "Error creating fifo %s: %s\n", fifopath, strerror(errno)); return NULL; } while (!shutdown) { fd = open(fifopath, O_WRONLY|O_NONBLOCK); if (fd < 0) { usleep(500000); continue; } fp = fdopen(fd, "w"); for (i=0; i < num_threads; i++) print_stat(fp, parameters[i], i, 0, 0); fclose(fp); usleep(250); } unlink(fifopath); return NULL; } static int trigger_init() { int i; int size = trigger_list_size; struct thread_trigger *trig = NULL; for(i=0; inext = trig; tail = trig; } trig->tnum = i; trig->next = NULL; } else { return -1; } } current = head; return 0; } static void trigger_print() { struct thread_trigger *trig = head; char *fmt = "T:%2d Spike:%8ld: TS: %12ld\n"; if (current == head) return; printf("\n"); while (trig->next != current) { fprintf(stdout, fmt, trig->tnum, trig->diff, trig->ts); trig = trig->next; } fprintf(stdout, fmt, trig->tnum, trig->diff, trig->ts); printf("spikes = %d\n\n", spikes); } static void trigger_update(struct thread_param *par, int diff, int64_t ts) { pthread_mutex_lock(&trigger_lock); if (current != NULL) { current->tnum = par->tnum; current->ts = ts; current->diff = diff; current = current->next; } spikes++; pthread_mutex_unlock(&trigger_lock); } /* Running status shared memory open */ static int rstat_shm_open(void) { int fd; pid_t pid; pid = getpid(); snprintf(shm_name, SHM_BUF_SIZE, "%s%d", "/cyclictest", pid); errno = 0; fd = shm_unlink(shm_name); if ((fd == -1) && (errno != ENOENT)) { fprintf(stderr, "ERROR: shm_unlink %s\n", strerror(errno)); return fd; } errno = 9; fd = shm_open(shm_name, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if (fd == -1) fprintf(stderr, "ERROR: shm_open %s\n", strerror(errno)); rstat_fd = fd; return fd; } static int rstat_ftruncate(int fd, off_t len) { int err; errno = 0; err = ftruncate(fd, len); if (err) fprintf(stderr, "ftruncate error %s\n", strerror(errno)); return err; } static void *rstat_mmap(int fd) { void *mptr; errno = 0; mptr = mmap(0, _SC_PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mptr == (void*)-1) fprintf(stderr, "ERROR: mmap, %s\n", strerror(errno)); return mptr; } static int rstat_mlock(void *mptr) { int err; errno = 0; err = mlock(mptr, _SC_PAGE_SIZE); if (err == -1) fprintf(stderr, "ERROR, mlock %s\n", strerror(errno)); return err; } static void rstat_setup(void) { int res; void *mptr = NULL; int sfd = rstat_shm_open(); if (sfd < 0) goto rstat_err; res = rstat_ftruncate(sfd, _SC_PAGE_SIZE); if (res) goto rstat_err1; mptr = rstat_mmap(sfd); if (mptr == MAP_FAILED) goto rstat_err1; res = rstat_mlock(mptr); if (res) goto rstat_err2; return; rstat_err2: munmap(mptr, _SC_PAGE_SIZE); rstat_err1: close(sfd); shm_unlink(shm_name); rstat_err: rstat_fd = -1; return; } static void write_stats(FILE *f, void *data) { struct thread_param **par = parameters; int i; struct thread_stat *s; fprintf(f, " \"num_threads\": %d,\n", num_threads); fprintf(f, " \"resolution_in_ns\": %u,\n", use_nsecs); fprintf(f, " \"thread\": {\n"); for (i = 0; i < num_threads; i++) { s = par[i]->stats; fprintf(f, " \"%u\": {\n", i); if (s->hist) { fprintf(f, " \"histogram\": {"); hist_print_json(s->hist, f); fprintf(f, " },\n"); } fprintf(f, " \"cycles\": %ld,\n", s->cycles); fprintf(f, " \"min\": %ld,\n", s->min); fprintf(f, " \"max\": %ld,\n", s->max); fprintf(f, " \"avg\": %.2f,\n", s->avg/s->cycles); fprintf(f, " \"cpu\": %d,\n", par[i]->cpu); fprintf(f, " \"node\": %d\n", par[i]->node); fprintf(f, " }%s\n", i == num_threads - 1 ? "" : ","); } fprintf(f, " }\n"); } static void set_main_thread_affinity(struct bitmask *cpumask) { int res; errno = 0; res = numa_sched_setaffinity(getpid(), cpumask); if (res != 0) warn("Couldn't setaffinity in main thread: %s\n", strerror(errno)); } int main(int argc, char **argv) { sigset_t sigset; int signum = SIGALRM; int mode; int cpu; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int online_cpus = sysconf(_SC_NPROCESSORS_ONLN); int i, ret = -1; int status; rt_init(argc, argv); process_options(argc, argv, max_cpus); if (check_privs()) exit(EXIT_FAILURE); if (verbose) { printf("Max CPUs = %d\n", max_cpus); printf("Online CPUs = %d\n", online_cpus); } if (affinity_mask != NULL) { set_main_thread_affinity(affinity_mask); if (verbose) printf("Using %u cpus.\n", numa_bitmask_weight(affinity_mask)); } if (trigger) { int retval; retval = trigger_init(); if (retval != 0) { fprintf(stderr, "trigger_init() failed\n"); exit(EXIT_FAILURE); } } /* lock all memory (prevent swapping) */ if (lockall) if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); goto out; } /* use the /dev/cpu_dma_latency trick if it's there */ set_latency_target(); if (deepest_idle_state >= -1) { if (!have_libcpupower_support()) { fprintf(stderr, "cyclictest built without libcpupower, --deepest-idle-state is not supported\n"); goto out; } for (i = 0; i < max_cpus; i++) { if (affinity_mask && !numa_bitmask_isbitset(affinity_mask, i)) continue; if (save_cpu_idle_disable_state(i) < 0) { fprintf(stderr, "Could not save cpu idle state.\n"); goto out; } if (set_deepest_cpu_idle_state(i, deepest_idle_state) < 0) { fprintf(stderr, "Could not set deepest cpu idle state.\n"); goto out; } } } if (tracelimit && trace_marker) enable_trace_mark(); if (check_timer()) warn("High resolution timers not available\n"); if (check_clock_resolution) { int clock; uint64_t diff; int k; uint64_t min_non_zero_diff = UINT64_MAX; struct timespec now; struct timespec prev; uint64_t reported_resolution = UINT64_MAX; struct timespec res; struct timespec *time; int times; clock = clocksources[clocksel]; if (clock_getres(clock, &res)) warn("clock_getres failed"); else reported_resolution = (NSEC_PER_SEC * res.tv_sec) + res.tv_nsec; /* * Calculate how many calls to clock_gettime are needed. * Then call it that many times. * Goal is to collect timestamps for ~ 0.001 sec. * This will reliably capture resolution <= 500 usec. */ times = 1000; clock_gettime(clock, &prev); for (k=0; k < times; k++) clock_gettime(clock, &now); diff = calcdiff_ns(now, prev); if (diff == 0) { /* * No clock rollover occurred. * Use the default value for times. */ times = -1; } else { int call_time; call_time = diff / times; /* duration 1 call */ times = NSEC_PER_SEC / call_time; /* calls per second */ times /= 1000; /* calls per msec */ if (times < 1000) times = 1000; } /* sanity check */ if ((times <= 0) || (times > 100000)) times = 100000; time = calloc(times, sizeof(*time)); for (k=0; k < times; k++) clock_gettime(clock, &time[k]); info(ct_debug, "For %d consecutive calls to clock_gettime():\n", times); info(ct_debug, "time, delta time (nsec)\n"); prev = time[0]; for (k=1; k < times; k++) { diff = calcdiff_ns(time[k], prev); prev = time[k]; if (diff && (diff < min_non_zero_diff)) min_non_zero_diff = diff; info(ct_debug, "%ld.%06ld %5llu\n", time[k].tv_sec, time[k].tv_nsec, (unsigned long long)diff); } free(time); if (verbose || (min_non_zero_diff && (min_non_zero_diff > reported_resolution))) { /* * Measured clock resolution includes the time to call * clock_gettime(), so it will be slightly larger than * actual resolution. */ warn("reported clock resolution: %llu nsec\n", (unsigned long long)reported_resolution); warn("measured clock resolution approximately: %llu nsec\n", (unsigned long long)min_non_zero_diff); } } mode = use_nanosleep + use_system; sigemptyset(&sigset); sigaddset(&sigset, signum); sigprocmask(SIG_BLOCK, &sigset, NULL); signal(SIGINT, sighand); signal(SIGTERM, sighand); signal(SIGUSR1, sighand); signal(SIGUSR2, sighand); /* Set-up shm */ rstat_setup(); if (histogram && hset_init(&hset, num_threads, 1, histogram, histogram)) fatal("failed to allocate histogram of size %d for %d threads\n", histogram, num_threads); parameters = calloc(num_threads, sizeof(struct thread_param *)); if (!parameters) goto out; statistics = calloc(num_threads, sizeof(struct thread_stat *)); if (!statistics) goto outpar; for (i = 0; i < num_threads; i++) { pthread_attr_t attr; int node; struct thread_param *par; struct thread_stat *stat; status = pthread_attr_init(&attr); if (status != 0) fatal("error from pthread_attr_init for thread %d: %s\n", i, strerror(status)); switch (setaffinity) { case AFFINITY_UNSPECIFIED: cpu = -1; break; case AFFINITY_SPECIFIED: cpu = cpu_for_thread_sp(i, max_cpus, affinity_mask); if (verbose) printf("Thread %d using cpu %d.\n", i, cpu); break; case AFFINITY_USEALL: cpu = cpu_for_thread_ua(i, max_cpus); break; default: cpu = -1; } node = -1; if (numa) { void *stack; void *currstk; size_t stksize; int node_cpu = cpu; if (node_cpu == -1) node_cpu = cpu_for_thread_ua(i, max_cpus); /* find the memory node associated with the cpu i */ node = rt_numa_numa_node_of_cpu(node_cpu); /* get the stack size set for this thread */ if (pthread_attr_getstack(&attr, &currstk, &stksize)) fatal("failed to get stack size for thread %d\n", i); /* if the stack size is zero, set a default */ if (stksize == 0) stksize = PTHREAD_STACK_MIN * 2; /* allocate memory for a stack on appropriate node */ stack = rt_numa_numa_alloc_onnode(stksize, node, node_cpu); /* touch the stack pages to pre-fault them in */ memset(stack, 0, stksize); /* set the thread's stack */ if (pthread_attr_setstack(&attr, stack, stksize)) fatal("failed to set stack addr for thread %d to 0x%x\n", i, stack+stksize); } /* allocate the thread's parameter block */ parameters[i] = par = threadalloc(sizeof(struct thread_param), node); if (par == NULL) fatal("error allocating thread_param struct for thread %d\n", i); memset(par, 0, sizeof(struct thread_param)); /* allocate the thread's statistics block */ statistics[i] = stat = threadalloc(sizeof(struct thread_stat), node); if (stat == NULL) fatal("error allocating thread status struct for thread %d\n", i); memset(stat, 0, sizeof(struct thread_stat)); if (histogram) stat->hist = &hset.histos[i]; if (verbose) { int bufsize = VALBUF_SIZE * sizeof(long); stat->values = threadalloc(bufsize, node); if (!stat->values) goto outall; memset(stat->values, 0, bufsize); par->bufmsk = VALBUF_SIZE - 1; if (smi) { stat->smis = threadalloc(bufsize, node); if (!stat->smis) goto outall; memset(stat->smis, 0, bufsize); } } par->prio = priority; if (priority && (policy == SCHED_FIFO || policy == SCHED_RR)) par->policy = policy; else { par->policy = SCHED_OTHER; force_sched_other = 1; } if (priospread) priority--; par->clock = clocksources[clocksel]; par->mode = mode; par->timermode = timermode; par->signal = signum; par->interval = interval; if (!histogram) /* same interval on CPUs */ interval += distance; if (verbose) printf("Thread %d Interval: %d\n", i, interval); par->max_cycles = max_cycles; par->stats = stat; par->node = node; par->tnum = i; par->cpu = cpu; stat->min = 1000000; stat->max = 0; stat->avg = 0.0; stat->threadstarted = 1; stat->smi_count = 0; status = pthread_create(&stat->thread, &attr, timerthread, par); if (status) fatal("failed to create thread %d: %s\n", i, strerror(status)); } /* Restrict the main pid to the affinity specified by the user */ if (main_affinity_mask != NULL) set_main_thread_affinity(main_affinity_mask); if (use_fifo) { status = pthread_create(&fifo_threadid, NULL, fifothread, NULL); if (status) fatal("failed to create fifo thread: %s\n", strerror(status)); } while (!shutdown) { char lavg[256]; int fd, len, allstopped = 0; static char *policystr = NULL; static char *slash = NULL; static char *policystr2; if (!policystr) policystr = policyname(policy); if (!slash) { if (force_sched_other) { slash = "/"; policystr2 = policyname(SCHED_OTHER); } else slash = policystr2 = ""; } if (!verbose && !quiet) { fd = open("/proc/loadavg", O_RDONLY, 0666); len = read(fd, &lavg, 255); close(fd); lavg[len-1] = 0x0; printf("policy: %s%s%s: loadavg: %s \n\n", policystr, slash, policystr2, lavg); } for (i = 0; i < num_threads; i++) { print_stat(stdout, parameters[i], i, verbose, quiet); if (max_cycles && statistics[i]->cycles >= max_cycles) allstopped++; } usleep(10000); if (shutdown || allstopped) break; if (!verbose && !quiet) printf("\033[%dA", num_threads + 2); if (refresh_on_max) { pthread_mutex_lock(&refresh_on_max_lock); if (!shutdown) pthread_cond_wait(&refresh_on_max_cond, &refresh_on_max_lock); pthread_mutex_unlock(&refresh_on_max_lock); } } ret = EXIT_SUCCESS; outall: shutdown = 1; usleep(50000); if (!verbose && !quiet && refresh_on_max) printf("\033[%dB", num_threads + 2); if (strlen(jsonfile) != 0) rt_write_json(jsonfile, ret, write_stats, NULL); if (quiet) quiet = 2; for (i = 0; i < num_threads; i++) { if (statistics[i]->threadstarted > 0) pthread_kill(statistics[i]->thread, SIGTERM); if (statistics[i]->threadstarted) { pthread_join(statistics[i]->thread, NULL); if (quiet && !histogram) print_stat(stdout, parameters[i], i, 0, 0); } if (statistics[i]->values) threadfree(statistics[i]->values, VALBUF_SIZE*sizeof(long), parameters[i]->node); } if (trigger) trigger_print(); if (histogram) print_hist(parameters, num_threads); if (tracelimit) { print_tids(parameters, num_threads); if (break_thread_id) { printf("# Break thread: %d\n", break_thread_id); printf("# Break value: %llu\n", (unsigned long long)break_thread_value); } } for (i=0; i < num_threads; i++) { if (!statistics[i]) continue; threadfree(statistics[i], sizeof(struct thread_stat), parameters[i]->node); } outpar: for (i = 0; i < num_threads; i++) { if (!parameters[i]) continue; threadfree(parameters[i], sizeof(struct thread_param), parameters[i]->node); } out: /* close any tracer file descriptors */ disable_trace_mark(); /* unlock everything */ if (lockall) munlockall(); /* close the latency_target_fd if it's open */ if (latency_target_fd >= 0) close(latency_target_fd); /* restore and free cpu idle disable states */ if (deepest_idle_state >= -1) { for (i = 0; i < max_cpus; i++) { if (affinity_mask && !numa_bitmask_isbitset(affinity_mask, i)) continue; restore_cpu_idle_disable_state(i); } } free_cpu_idle_disable_states(); if (affinity_mask) rt_bitmask_free(affinity_mask); /* Remove running status shared memory file if it exists */ if (rstat_fd >= 0) shm_unlink(shm_name); hset_destroy(&hset); exit(ret); } rt-tests-2.9/src/cyclictest/get_cyclictest_snapshot.8000066400000000000000000000025461503076725400231430ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .TH GET_CYCLICTEST_SNAPSHOT 8 "July 6, 2020" # SPDX-License-Identifier: GPL-2.0-or-later .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME get_cyclictest_snapshot \- Get a snapshot of running instances of cyclictest .SH SYNOPSIS .SY get_cyclictest_snapshot .OP [\-h]\ [\-l]\ [\-s\ [pid [pid ...]]] .br .OP [\-p\ [pid\ [pid ...]]] .SH OPTIONS Without options, this program will send USR2 to cyclictest to create a snapshot and then print it out. .br .TP Optional arguments: .TP .B -h, --help show this help message and exit .TP .B -l, --list list the main pid(s) of running instances of cyclictest .TP .B -s [pid [pid ...]], --snapshot [pid [pid ...]] take a snapshot of running instances of cyclictest .br by sending USR2 to cyclictest .TP .B -p [pid [pid ...]], --print [pid [pid ...]] print the snapshots .SH SEE ALSO .BR cyclictest (8), .SH AUTHOR get_cyclictest_snapshot was written by John Kacur rt-tests-2.9/src/cyclictest/get_cyclictest_snapshot.py000077500000000000000000000062511503076725400234240ustar00rootroot00000000000000#!/usr/bin/env python3 """ Program to get a snapshot of a running instance of cyclictest """ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2020 John Kacur import subprocess import argparse import re import glob import sys parser = argparse.ArgumentParser(description='Get a snapshot of running instances of cyclictest') parser.add_argument('-l', '--list', action='store_true', help='list the main pid(s) of running instances of cyclictest') parser.add_argument('-s', '--snapshot', nargs='*', metavar='pid', help='take a snapshot of running instances of cyclictest') parser.add_argument('-p', '--print', nargs='*', metavar='pid', help='print the snapshots') args = parser.parse_args() class Snapshot: """ Class for getting a snapshot of a running cyclictest instance """ warned = False @classmethod def print_warning(cls): """ print a warning one time only even if called multiple times """ if not cls.warned: cls.warned = True print("No cyclictest instance found") def __init__(self): self.pids = [] self.shm_files = [] self.refresh() def refresh(self): """ Create a list of running cyclictest instances. """ self.pids = [] self.shm_files = glob.glob('/dev/shm/cyclictest*') self.shm_files.sort() for shm_file in self.shm_files: pid = re.search('[0-9]*$', shm_file).group() self.pids += [pid] def take_snapshot(self, spids=None): """ Send USR2 to all running instances of cyclictest, or just to a specific pid (spids) if specified. """ if spids is None: if not self.pids: Snapshot.print_warning() for pid in self.pids: subprocess.run(["kill", "-s", "USR2", pid]) else: for pid in spids: subprocess.run(["kill", "-s", "USR2", pid]) def print_pids(self): """ Print the list of pids of running cyclictest instances. """ if not self.pids: Snapshot.print_warning() for pid in self.pids: print(pid) def print(self, spids=None): """ Print the data in /dev/shm/cyclictest* """ if spids is None: if not self.shm_files: Snapshot.print_warning() for shm_file in self.shm_files: with open(shm_file, 'r') as f: data = f.read() print(data) else: for spid in spids: if spid in self.pids: shm_file = '/dev/shm/cyclictest' + spid with open(shm_file, 'r') as f: data = f.read() print(data) else: Snapshot.print_warning() snapshot = Snapshot() if args.list: snapshot.print_pids() if args.snapshot is not None: if args.snapshot: snapshot.take_snapshot(args.snapshot) else: snapshot.take_snapshot() if args.print is not None: if args.print: snapshot.print(args.print) else: snapshot.print() if len(sys.argv) == 1: snapshot.take_snapshot() snapshot.print() rt-tests-2.9/src/cyclictest/rt_numa.h000066400000000000000000000031361503076725400177400ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * A numa library for cyclictest. * * (C) 2010 John Kacur * (C) 2010 Clark Williams * */ #ifndef _RT_NUMA_H #define _RT_NUMA_H #include "rt-utils.h" #include "rt-error.h" static int numa = 0; #include static void * threadalloc(size_t size, int node) { if (node == -1) return malloc(size); return numa_alloc_onnode(size, node); } static void threadfree(void *ptr, size_t size, int node) { if (node == -1) free(ptr); else numa_free(ptr, size); } static void rt_numa_set_numa_run_on_node(int node, int cpu) { int res; res = numa_run_on_node(node); if (res) warn("Could not set NUMA node %d for thread %d: %s\n", node, cpu, strerror(errno)); return; } static void *rt_numa_numa_alloc_onnode(size_t size, int node, int cpu) { void *stack; stack = numa_alloc_onnode(size, node); if (stack == NULL) fatal("failed to allocate %d bytes on node %d for cpu %d\n", size, node, cpu); return stack; } /* * Use new bit mask CPU affinity behavior */ static int rt_numa_numa_node_of_cpu(int cpu) { int node; node = numa_node_of_cpu(cpu); if (node == -1) fatal("invalid cpu passed to numa_node_of_cpu(%d)\n", cpu); return node; } static inline unsigned int rt_numa_bitmask_isbitset( const struct bitmask *mask, unsigned long i) { return numa_bitmask_isbitset(mask,i); } static inline struct bitmask* rt_numa_parse_cpustring(const char* s) { return numa_parse_cpustring_all(s); } static inline void rt_bitmask_free(struct bitmask *mask) { numa_bitmask_free(mask); } #endif /* _RT_NUMA_H */ rt-tests-2.9/src/hackbench/000077500000000000000000000000001503076725400156575ustar00rootroot00000000000000rt-tests-2.9/src/hackbench/hackbench.8000066400000000000000000000075131503076725400176640ustar00rootroot00000000000000.TH "hackbench" "8" "September 19, 2020" "" "" # SPDX-License-Identifier: GPL-2.0-or-later .SH "NAME" hackbench \- scheduler benchmark/stress test .SH "SYNOPSIS" .B hackbench .RI "[\-f|\-\-fds NUM] " .RI "[\-F|\-\-fifo] " .RI "[\-g|\-\-groups NUM] " .RI "[\-h|\-\-help] " .RI "[\-l|\-\-loops LOOPS] " .RI "[\-p|\-\-pipe] " .RI "[\-s|\-\-datasize SIZE] " .RI "[\-T|\-\-threads]" .RI "[\-P|\-\-process]" .SH "DESCRIPTION" Hackbench is both a benchmark and a stress test for the Linux kernel scheduler. It's main job is to create a specified number of pairs of schedulable entities (either threads or traditional processes) which communicate via either sockets or pipes and time how long it takes for each pair to send data back and forth. .SH "OPTIONS" These programs follow the usual GNU command line syntax, with long options starting with two dashes ("\-\-"). .br A summary of options is included below. .TP .B \-f, \-\-fds=NUM Defines how many file descriptors each child should use. Note that the effective number will be twice the amount you set here, as the sender and receiver children will each open the given amount of file descriptors. .TP .B \-F,\-\-fifo Change the main thread to SCHED_FIFO after creating workers. This allows the management thread to run after many workers are created. .TP .B \-g, \-\-groups=NUM Defines how many groups of senders and receivers should be started .TP .B \-h, \-\-help .TP .B \-l, \-\-loops=LOOPS How many messages each sender/receiver pair should send .TP .B \-p, \-\-pipe Sends the data via a pipe instead of the socket (default) .TP .B \-s, \-\-datasize=SIZE Sets the amount of data to send in each message .TP .B \-T, \-\-threads Each sender/receiver child will be a POSIX thread of the parent. .TP .B \-P, \-\-process Hackbench will use fork() on all children (default behaviour) .br Shows a simple help screen .SH "EXAMPLES" .LP Running hackbench without any options will give default behaviour, using fork() and sending data between senders and receivers via sockets. .LP user@host: ~ $ hackbench .br Running in process mode with 10 groups using 40 file descriptors each (== 400 tasks) .br Each sender will pass 100 messages of 100 bytes .br Time: 0.890 .LP To use pipes between senders and receivers and using threads instead of fork(), run .LP user@host: ~ $ hackbench \-\-pipe \-\-threads (or hackbench \-p \-T) .br Running in threaded mode with 10 groups using 40 file descriptors each (== 400 tasks) .br Each sender will pass 100 messages of 100 bytes .br Time: 0.497 .LP Set the datasize to 512 bytes, do 200 messages per sender/receiver pairs and use 15 groups using 25 file descriptors per child, in process mode. .LP user@host: ~ $ hackbench \-s 512 \-l 200 \-g 15 \-f 25 \-P .br Running in process mode with 15 groups using 50 file descriptors each (== 750 tasks) .br Each sender will pass 200 messages of 512 bytes .br Time: 4.497 .SH "AUTHORS" .LP hackbench was written by Rusty Russell with contributions from Yanmin Zhang , Ingo Molnar and David Sommerseth This manual page was written by Clark Williams and David Sommerseth .SH "HISTORY" This version of hackbench is based on the code downloaded from http://people.redhat.com/mingo/cfs\-scheduler/tools/hackbench.c. Yanmin Zhang merged the original hackbench code from .br http://devresources.linuxfoundation.org/craiger/hackbench/src/hackbench.c which uses fork() and a modified version from .br http://www.bullopensource.org/posix/pi\-futex/hackbench_pth.c which uses pthread only and gave the possibility to change behaviour at run time. Hackbench have since then gone through some more rewriting to improve error handling and proper tracking of fork()ed children, to avoid leaving zombies on the system if hackbench stops unexpectedly. rt-tests-2.9/src/hackbench/hackbench.c000066400000000000000000000314271503076725400177400ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /* * This is the latest version of hackbench.c, that tests scheduler and * unix-socket (or pipe) performance. * * Usage: hackbench [-pipe] [process|thread] [loops] * * Build it with: * gcc -g -Wall -O2 -o hackbench hackbench.c -lpthread * * Downloaded from http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c * February 19 2010. * */ /* Test groups of 20 processes spraying to 20 receivers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static unsigned int datasize = 100; static unsigned int loops = 100; static unsigned int num_groups = 10; static unsigned int num_fds = 20; static unsigned int fifo = 0; /* * 0 means thread mode and others mean process (default) */ #define THREAD_MODE 0 #define PROCESS_MODE 1 static unsigned int process_mode = PROCESS_MODE; static int use_pipes = 0; static int use_inet = 0; struct sender_context { unsigned int num_fds; int ready_out; int wakefd; int out_fds[0]; }; struct receiver_context { unsigned int num_packets; int in_fds[2]; int ready_out; int wakefd; }; typedef union { pthread_t threadid; pid_t pid; } childinfo_t; childinfo_t *child_tab = NULL; unsigned int total_children = 0; unsigned int signal_caught = 0; static jmp_buf jmpbuf; inline static void sneeze(const char *msg) { /* Avoid calling these functions when called from a code path * which involves sigcatcher(), as they are not reentrant safe. */ if( !signal_caught ) { fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno)); } } static void barf(const char *msg) { sneeze(msg); exit(1); } static void print_usage_exit(int error) { printf("hackbench V %1.2f\n", VERSION); printf("Usage:\n" "hackbench \n\n" "-f --fds=NUM number of fds\n" "-F --fifo use SCHED_FIFO for main thread\n" "-g --groups=NUM number of groups to be used\n" "-h --help print this message\n" "-l --loops=LOOPS how many message should be send\n" "-p --pipe send data via a pipe\n" "-i --inet send data via a inet tcp connection\n" "-s --datasize=SIZE message size\n" "-T --threads use POSIX threads\n" "-P --process use fork (default)\n" ); exit(error); } static int inet_socketpair(int fds[2]) { int s1, s2; struct sockaddr_in sin; unsigned long ul = 1; if ((s1 = socket(AF_INET, SOCK_STREAM, 0)) < 0) barf("socket"); if ((s2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) barf("socket"); socklen_t len = sizeof(sin); bzero(&sin, len); sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); if (bind(s1, &sin, len) < 0) barf("bind"); if (getsockname(s1, &sin, &len) < 0) barf("getsockname"); if (listen(s1, 10) < 0) barf("listen"); if (ioctl(s2, FIONBIO, &ul) < 0) barf("ioctl"); if (ioctl(s1, FIONBIO, &ul) < 0) barf("ioctl"); if (connect(s2, &sin, len) < 0) barf("connect"); if ((fds[0] = accept(s1, &sin, &len)) < 0) barf("accept"); ul = 0; if (ioctl(s2, FIONBIO, &ul) < 0) barf("ioctl"); fds[1] = s2; close(s1); return 0; } static void fdpair(int fds[2]) { if (use_pipes) { if (pipe(fds) == 0) return; } else if (use_inet) { if (inet_socketpair(fds) == 0) return; } else { if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) return; } barf("Creating fdpair"); } /* Block until we're ready to go */ static void ready(int ready_out, int wakefd) { char dummy = '*'; struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; /* Tell them we're ready. */ if (write(ready_out, &dummy, 1) != 1) barf("CLIENT: ready write"); /* Wait for "GO" signal */ if (poll(&pollfd, 1, -1) != 1) barf("poll"); } static void reset_worker_signals(void) { signal(SIGTERM, SIG_DFL); signal(SIGINT, SIG_DFL); } /* Sender sprays loops messages down each file descriptor */ static void *sender(struct sender_context *ctx) { char data[datasize]; unsigned int i, j; reset_worker_signals(); ready(ctx->ready_out, ctx->wakefd); memset(&data, '-', datasize); /* Now pump to every receiver. */ for (i = 0; i < loops; i++) { for (j = 0; j < ctx->num_fds; j++) { int ret; size_t done = 0; again: ret = write(ctx->out_fds[j], data + done, sizeof(data)-done); if (ret < 0) barf("SENDER: write"); done += ret; if (done < sizeof(data)) goto again; } } return NULL; } /* One receiver per fd */ static void *receiver(struct receiver_context* ctx) { unsigned int i; reset_worker_signals(); if (process_mode == PROCESS_MODE) close(ctx->in_fds[1]); /* Wait for start... */ ready(ctx->ready_out, ctx->wakefd); /* Receive them all */ for (i = 0; i < ctx->num_packets; i++) { char data[datasize]; int ret; size_t done = 0; again: ret = read(ctx->in_fds[0], data + done, datasize - done); if (ret < 0) barf("SERVER: read"); done += ret; if (done < datasize) goto again; } if (ctx) { free(ctx); } return NULL; } static int create_worker(childinfo_t *child, void *ctx, void *(*func)(void *)) { pthread_attr_t attr; int err; switch (process_mode) { case PROCESS_MODE: /* process mode */ /* Fork the sender/receiver child. */ switch ((child->pid = fork())) { case -1: sneeze("fork()"); return -1; case 0: (*func) (ctx); exit(0); } break; case THREAD_MODE: /* threaded mode */ if (pthread_attr_init(&attr) != 0) { sneeze("pthread_attr_init()"); return -1; } #ifndef __ia64__ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) { sneeze("pthread_attr_setstacksize()"); return -1; } #endif if ((err=pthread_create(&child->threadid, &attr, func, ctx)) != 0) { sneeze("pthread_create failed()"); return -1; } break; } return 0; } void signal_workers(childinfo_t *children, unsigned int num_children) { unsigned int i; printf("signaling %d worker threads to terminate\n", num_children); for (i=0; i < num_children; i++) { kill(children[i].pid, SIGTERM); } } unsigned int reap_workers(childinfo_t *child, unsigned int totchld, unsigned int dokill) { unsigned int i, rc = 0; int status, err; void *thr_status; if (dokill) { fprintf(stderr, "sending SIGTERM to all child processes\n"); signal(SIGTERM, SIG_IGN); signal_workers(child, totchld); } for( i = 0; i < totchld; i++ ) { int pid; switch( process_mode ) { case PROCESS_MODE: /* process mode */ fflush(stdout); pid = wait(&status); if (pid == -1 && errno == ECHILD) break; if (!WIFEXITED(status)) rc++; break; case THREAD_MODE: /* threaded mode */ err = pthread_join(child[i].threadid, &thr_status); if( err != 0 ) { sneeze("pthread_join()"); rc++; } break; } } return rc; } /* One group of senders and receivers */ static unsigned int group(childinfo_t *child, unsigned int tab_offset, unsigned int num_fds, int ready_out, int wakefd) { unsigned int i; struct sender_context* snd_ctx = malloc (sizeof(struct sender_context) +num_fds*sizeof(int)); int err; if (!snd_ctx) { sneeze("malloc() [sender ctx]"); return 0; } for (i = 0; i < num_fds; i++) { int fds[2]; struct receiver_context* ctx = malloc (sizeof(*ctx)); if (!ctx) { sneeze("malloc() [receiver ctx]"); return (i > 0 ? i-1 : 0); } /* Create the pipe between client and server */ fdpair(fds); ctx->num_packets = num_fds*loops; ctx->in_fds[0] = fds[0]; ctx->in_fds[1] = fds[1]; ctx->ready_out = ready_out; ctx->wakefd = wakefd; err = create_worker(&child[tab_offset+i], ctx, (void *)(void *)receiver); if(err) { return (i > 0 ? i-1 : 0); } snd_ctx->out_fds[i] = fds[1]; if (process_mode == PROCESS_MODE) close(fds[0]); } snd_ctx->ready_out = ready_out; snd_ctx->wakefd = wakefd; snd_ctx->num_fds = num_fds; /* Now we have all the fds, fork the senders */ for (i = 0; i < num_fds; i++) { err = create_worker(&child[tab_offset+num_fds+i], snd_ctx, (void *)(void *)sender); if(err) { return (num_fds+i)-1; } } /* Close the fds we have left */ if (process_mode == PROCESS_MODE) for (i = 0; i < num_fds; i++) close(snd_ctx->out_fds[i]); /* Return number of children to reap */ return num_fds * 2; } static void process_options(int argc, char *argv[]) { for(;;) { static struct option longopts[] = { {"fds", required_argument, NULL, 'f'}, {"fifo", no_argument, NULL, 'F'}, {"groups", required_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, {"loops", required_argument, NULL, 'l'}, {"pipe", no_argument, NULL, 'p'}, {"inet", no_argument, NULL, 'i'}, {"datasize", required_argument, NULL, 's'}, {"threads", no_argument, NULL, 'T'}, {"processes", no_argument, NULL, 'P'}, {NULL, 0, NULL, 0} }; int c = getopt_long(argc, argv, "f:Fg:hl:pis:TP", longopts, NULL); if (c == -1) { break; } switch (c) { case 'f': num_fds = atoi(optarg); if (atoi(optarg) <= 0) { fprintf(stderr, "%s: --fds|-f requires an integer > 0\n", argv[0]); print_usage_exit(1); } break; case 'F': fifo = 1; break; case 'g': num_groups = atoi(optarg); if (atoi(optarg) <= 0) { fprintf(stderr, "%s: --groups|-g requires an integer > 0\n", argv[0]); print_usage_exit(1); } break; case 'h': print_usage_exit(0); break; case 'l': loops = atoi(optarg); if (atoi(optarg) <= 0) { fprintf(stderr, "%s: --loops|-l requires an integer > 0\n", argv[0]); print_usage_exit(1); } break; case 'p': use_pipes = 1; break; case 'i': use_inet = 1; break; case 's': datasize = atoi(optarg); if (atoi(optarg) <= 0) { fprintf(stderr, "%s: --datasize|-s requires an integer > 0\n", argv[0]); print_usage_exit(1); } break; case 'T': process_mode = THREAD_MODE; break; case 'P': process_mode = PROCESS_MODE; break; default: print_usage_exit(1); } } if (use_pipes && use_inet) { fprintf(stderr, "%s: --pipe|-p and --inet|-i cannot be used together\n", argv[0]); print_usage_exit(1); } } void sigcatcher(int sig) { /* All caught signals will cause the program to exit */ signal_caught = 1; fprintf(stderr, "Signal %d caught, longjmp'ing out!\n", sig); signal(sig, SIG_IGN); longjmp(jmpbuf, 1); } int main(int argc, char *argv[]) { unsigned int i; struct timeval start, stop, diff; int readyfds[2], wakefds[2]; char dummy; volatile int timer_started = 0; struct sched_param sp; process_options (argc, argv); printf("Running in %s mode with %d groups using %d file descriptors each (== %d tasks)\n", (process_mode == THREAD_MODE ? "threaded" : "process"), num_groups, 2*num_fds, num_groups*(num_fds*2)); printf("Each sender will pass %d messages of %d bytes\n", loops, datasize); fflush(NULL); child_tab = calloc(num_fds * 2 * num_groups, sizeof(childinfo_t)); if (!child_tab) barf("main:malloc()"); fdpair(readyfds); fdpair(wakefds); /* Catch some signals */ signal(SIGINT, sigcatcher); signal(SIGTERM, sigcatcher); signal(SIGHUP, SIG_IGN); if (setjmp(jmpbuf) == 0) { total_children = 0; for (i = 0; i < num_groups; i++) { unsigned int c = group(child_tab, total_children, num_fds, readyfds[1], wakefds[0]); if( c != (num_fds*2) ) { fprintf(stderr, "%i children started. Expected %i\n", c, num_fds*2); reap_workers(child_tab, total_children + c, 1); barf("Creating workers"); } total_children += c; } if (fifo) { /* make main a realtime task so that we can manage the workers */ sp.sched_priority = 1; if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) barf("can't change to fifo in main"); } /* Wait for everyone to be ready */ for (i = 0; i < total_children; i++) if (read(readyfds[0], &dummy, 1) != 1) { reap_workers(child_tab, total_children, 1); barf("Reading for readyfds"); } gettimeofday(&start, NULL); timer_started = 1; /* Kick them off */ if (write(wakefds[1], &dummy, 1) != 1) { reap_workers(child_tab, total_children, 1); barf("Writing to start senders"); } } else { fprintf(stderr, "longjmp'ed out, reaping children\n"); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); } /* Reap them all */ reap_workers(child_tab, total_children, signal_caught); gettimeofday(&stop, NULL); /* Print time... */ if (timer_started) { timersub(&stop, &start, &diff); printf("Time: %lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000); } else fprintf(stderr, "No measurements available\n"); free(child_tab); exit(0); } rt-tests-2.9/src/hwlatdetect/000077500000000000000000000000001503076725400162615ustar00rootroot00000000000000rt-tests-2.9/src/hwlatdetect/hwlat.txt000066400000000000000000000112201503076725400201350ustar00rootroot00000000000000Introduction: ------------- The module hwlat is a special purpose kernel module that is used to detect if System Management Interrupts (SMIs) are causing event latencies in the Linux RT kernel. SMIs are usually not serviced by the Linux kernel. They are set up by BIOS code and are serviced by BIOS code, usually for critical events such as management of thermal sensors and fans. Sometimes though, SMIs are used for other tasks and those tasks can spend an inordinate amount of time in the handler (sometimes measured in milliseconds). Obviously if you are trying to keep event service latencies down in the microsecond range, this is a problem. The SMI detector works by hogging the cpu for configurable amounts of time (by calling stop_machine()), polling the Time Stamp Counter (TSC) register for some period, then looking for gaps in the TSC data. Any gap indicates a time when the polling was interrupted and since the machine is stopped and interrupts turned off the only thing that could do that would be an SMI. Note that the SMI detector should *NEVER* be used in a production environment. It is intended to be run manually to determine if the hardware platform has a problem with long SMI service routines. Usage: ------ Loading the module hwlat passing the parameter "enabled=1" is the only step required to start the hwlat. It is possible to define a threshold in microseconds (us) above which latency spikes will be taken in account (parameter "threshold="). Example: # insmod ./hwlat.ko enabled=1 threshold=100 After the module is loaded, it creates a directory named "hwlat" under the debugfs mountpoint, "/debugfs/hwlat" for this text. It is necessary to have debugfs mounted. avg_smi_interval_us - average interval (usecs) between SMI latency spikes latency_threshold_us - minimum latency value to be considered (usecs) max_sample_us - maximum SMI latency spike observed (usecs) ms_between_samples - interval between samples (ms) ms_per_sample - sampling time (ms) sample_us - last sample (usecs). continuously updated, may be used to plot graphs or to create histograms smi_count - how many latency spikes have been observed # cd /debugfs/hwlat # cat * 0 (avg_smi_interval_us) 100 (latency_threshold_us) 0 (latency_threshold_us) 5000 (ms_between_samples) 1 (ms_per_sample) 3 (sample_us) 0 (smi_count) The default values for ms_between_samples and ms_per_sample define that every 5000ms (5s) samples will be collected during 1ms. It is possible to change the sampling time or the sampling interval writing to the related files. To collect samples during 5ms every 50ms: # echo 5 > ms_per_sample # echo 50 > ms_between_samples # cat ms_between_samples 50 # cat ms_per_sample 5 After a while, data may be verified to test the existence of SMI induced latencies: # cat smi_count 2 # cat max_sample_us 468 # cat avg_smi_interval_us 1356306050 Depending on the latencies observed it is important to better adjust the sampling intervals to obtain more accurate measurements. Care must be taken to not create a continuous sampling situation, that might be perceived by the kernel as a deadlock. Python Script ------------- A python script is provided in in the kernel scripts directory named 'hwlatdetect'. This script handles mounting/unmounting the debugfs, loading/unloading the hwlat module, setting various parameters for the SMI detector and then starting and stopping the detection. The hwlatdetect script handles the following arguments: Usage: hwlatdetect [options] Options: -h, --help show this help message and exit --duration=DURATION total time to test for SMIs ({smdw}) --threshold=THRESHOLD value above which is considered an SMI (microseconds --interval=INTERVAL time between samples (milliseconds) --sample_width=SAMPLE_WIDTH time to actually measure (milliseconds) --report=REPORT filename for sample data --cleanup force unload of module and umount of debugfs --debug turn on debugging prints --quiet turn off all screen output --watch print sample data to stdout as it arrives An example run looks like this: $ sudo hwlatdetect --duration=1m --report=smi.txt hwlatdetect: test duration 60 seconds parameters: Latency threshold: 100us Non-sampling gap: 5ms Sample length: 20ms Output File: smi.txt Starting test test finished Max Latency: 427us Samples recorded: 2201 Samples exceeding threshold: 7 sample data written to smi.txt $ The above run generated 2201 samples of which 27 exceeded the 100 microsecond threshold. rt-tests-2.9/src/hwlatdetect/hwlatdetect.8000066400000000000000000000104061503076725400206630ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .TH HWLATDETECT 8 "May 12, 2009" # SPDX-License-Identifier: GPL-2.0-only .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME hwlatdetect \- program to control the ftrace kernel hardware latency detection .SH SYNOPSIS .B hwlatdetect .RI "[ \-\-duration=