pax_global_header00006660000000000000000000000064131105701640014510gustar00rootroot0000000000000052 comment=a477fe226cbe3652a5d30a191fdf84ebbf744d25 rt-app-1.0/000077500000000000000000000000001311057016400125535ustar00rootroot00000000000000rt-app-1.0/.gitignore000066400000000000000000000005561311057016400145510ustar00rootroot00000000000000COPYING Makefile Makefile.in README aclocal.m4 *.txt *.swp *.patch cscope.out ID TAGS.LST autom4te.cache/ build-aux/ config.log config.status configure libdl/.deps/ libdl/Makefile libdl/Makefile.in libdl/dl_syscalls.o libdl/libdl.a libtool m4/ src/.deps/ src/Makefile src/Makefile.in src/config.h src/config.h.in src/rt-app src/*.o src/*.txt src/*.swp src/stamp-h1 rt-app-1.0/COPYING.in000066400000000000000000000431021311057016400142130ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. 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 Appendix: 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) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General Public License instead of this License. rt-app-1.0/Makefile.am000066400000000000000000000000751311057016400146110ustar00rootroot00000000000000if SET_DLSCHED SUBDIRS = libdl src else SUBDIRS = src endif rt-app-1.0/README.in000066400000000000000000000022561311057016400140450ustar00rootroot00000000000000README for rt-app @VERSION@ ============== INTRODUCTION ============== rt-app is a test application that starts multiple periodic threads in order to simulate a real-time periodic load. Code is currently maintained on GitHub: http://github.com/scheduler-tools/rt-app ============== REQUIREMENTS ============== rt-app runs on GNU/Linux. It needs autoconf, automake, libtool , libjson-c and a recent compiler (mainly: gcc) for basic features. ============= COMPILATION ============= $ autoreconf --install $ ./configure $ make $ make install Last step is optional, rt-app is built in the src/ directory. Typical usage: $ ./configure --prefix= installs the compiled program in the given directory $ ./configure --with-deadline builds rt-app with support for SCHED_DEADLINE See ./configure --help for additional options. For cross-compilation, you may have to set the CC environment variable to your cross-compiler, and provide the --host option (e.g., --host=arm). ======= USAGE ======= $ rt-app where config file is a full/relative path to a json file (look under doc/ for examples config file) or "-" (without quotes) to read JSON data from stdin. rt-app-1.0/TODO000066400000000000000000000006011311057016400132400ustar00rootroot00000000000000List of things to do for enhancing the synthetic workload generato * Use a .json file to create a file viewable on kernelshark * Use a record from trace-cmd and/or perf to generate a json file * simplify the grammar to descibe a pattern of a task * Add more details of the grammarin documentation * create different kind of busy loop : cpu intensive loop, memeory intensive * loop ... rt-app-1.0/autogen.sh000077500000000000000000000004671311057016400145630ustar00rootroot00000000000000#!/bin/bash autoreconf --install --symlink echo echo "----------------------------------------------------------------" echo "Initialized build system. For a common configuration please run:" echo "----------------------------------------------------------------" echo echo "./configure --with-deadline" echo rt-app-1.0/configure.ac000066400000000000000000000024411311057016400150420ustar00rootroot00000000000000AC_INIT([rt-app], [1.0], [juri.lelli@arm.com, vincent.guittot@linaro.org]) AC_COPYRIGHT([Copyright (C) 2009 Giacomo Bagnoli Copyright (C) 2016 Juri Lelli Copyright (C) 2016 Vincent Guittot ]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2]) m4_pattern_allow([AM_PROG_AR]) AM_PROG_AR AC_PROG_CC AC_PROG_LIBTOOL AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([src/rt-app.c]) AC_HEADER_STDC AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([m], [round]) AC_CHECK_LIB([rt], [clock_gettime]) AC_CHECK_LIB([json-c], [json_object_from_file]) AC_CHECK_FUNCS(sched_setattr, [], []) AC_ARG_WITH([deadline], [AS_HELP_STRING([--with-deadline], [Add support for SCHED_DEADLINE])], [AC_DEFINE([DLSCHED], [1], [Define if you have SCHED_DEADLINE support])], [with_deadline=no]) AM_CONDITIONAL([SET_DLSCHED], [test x$with_deadline != xno && test !HAVE_SCHED_SETATTR]) AC_ARG_VAR([LOGLVL], [verbosity level, from 0 to 100. 100 is very verbose]) if test -z "${LOGLVL}";then AC_DEFINE([LOG_LEVEL], [50], [Define log message verbosity]) else AC_DEFINE_UNQUOTED([LOG_LEVEL], [${LOGLVL}]) fi AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_FILES([Makefile src/Makefile libdl/Makefile README COPYING]) AC_OUTPUT rt-app-1.0/doc/000077500000000000000000000000001311057016400133205ustar00rootroot00000000000000rt-app-1.0/doc/examples/000077500000000000000000000000001311057016400151365ustar00rootroot00000000000000rt-app-1.0/doc/examples/browser-long.json000066400000000000000000000047551311057016400204640ustar00rootroot00000000000000{ "tasks" : { "BrowserMain" : { "loop" : 3, "phases" : { "start" : { "loop" : 1, "sleep" : 400000, "run" : 15000, "resume" : "Browser", "run" : 7000, "sleep" : 8000 }, "render1" : { "loop" : 50, "resume" : "BrowserSub", "run" : 3000 }, "render2" : { "loop" : 1, "suspend" : "Browser", "run" : 10000, "resume" : "Browser", "run" : 5000 }, "render3" : { "loop" : 20, "resume" : "BrowserSub", "run" : 3000 }, "stop" : { "loop" : 1, "run" : 2000, "sleep" : 200000, "suspend" : "Browser", "sleep" : 600000 }, "scroll" : { "loop" : 4, "resume" : "Browser", "suspend" : "BrowserNext", "run" : 1000 }, "stop2" : { "loop" : 1, "suspend" : "Browser", "run" : 200, "sleep" : 800000 } } }, "BrowserSub1" : { "priority" : -6, "loop" : -1, "suspend" : "BrowserSub", "run" : 100 }, "BrowserSub2" : { "priority" : -6, "loop" : -1, "suspend" : "BrowserSub", "run" : 100 }, "BrowserDisplay" : { "priority" : -6, "loop" : -1, "suspend" : "Browser", "run" : 300, "resume" : "BrowserNext", "run" : 12000, "lock" : "mutex11", "sync" : { "ref" : "queue11", "mutex": "mutex11" }, "unlock" : "mutex11", "run" : 300, "resume" : "Binder-display", "run" : 400 }, "Binder-dummy" : { "priority" : -6, "loop" : -1, "lock" : "mutex11", "wait" : { "ref" : "queue11", "mutex": "mutex11" }, "unlock" : "mutex11", "run" : 200, "lock" : "mutex11", "signal" : "queue11", "unlock" : "mutex11", "run" : 100 }, "Binder-display" : { "priority" : -6, "loop" : -1, "suspend" : "Binder-display", "run" : 300, "resume" : "Event-Browser", "resume" : "Event-Display" }, "Event-Browser" : { "priority" : -9, "loop" : -1, "suspend" : "Event-Browser", "run" : 50, "sleep" : 16000, "run" : 50, "resume" : "Browser" }, "Event-Display" : { "priority" : -9, "loop" : -1, "suspend" : "Event-Display", "run" : 50, "sleep" : 16000, "run" : 50, "resume" : "Display" }, "Display" : { "priority" : -8, "loop" : -1, "suspend" : "Display", "run" : 16000 }, }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 600, "ftrace" : false, "gnuplot" : false, "logdir" : "./", "log_basename" : "web", "lock_pages" : true, "frag" : 1, "calibration" : "CPU0" } } rt-app-1.0/doc/examples/browser-short.json000066400000000000000000000047531311057016400206620ustar00rootroot00000000000000{ "tasks" : { "BrowserMain" : { "loop" : 3, "phases" : { "start" : { "loop" : 1, "sleep" : 400000, "run" : 15000, "resume" : "Browser", "run" : 7000, "sleep" : 8000 }, "render1" : { "loop" : 50, "resume" : "BrowserSub", "run" : 3000 }, "render2" : { "loop" : 1, "suspend" : "Browser", "run" : 10000, "resume" : "Browser", "run" : 5000 }, "render3" : { "loop" : 20, "resume" : "BrowserSub", "run" : 3000 }, "stop" : { "loop" : 1, "run" : 2000, "sleep" : 200000, "suspend" : "Browser", "sleep" : 600000 }, "scroll" : { "loop" : 4, "resume" : "Browser", "suspend" : "BrowserNext", "run" : 1000 }, "stop2" : { "loop" : 1, "suspend" : "Browser", "run" : 200, "sleep" : 800000 } } }, "BrowserSub1" : { "priority" : -6, "loop" : -1, "suspend" : "BrowserSub", "run" : 100 }, "BrowserSub2" : { "priority" : -6, "loop" : -1, "suspend" : "BrowserSub", "run" : 100 }, "BrowserDisplay" : { "priority" : -6, "loop" : -1, "suspend" : "Browser", "run" : 300, "resume" : "BrowserNext", "run" : 12000, "lock" : "mutex11", "sync" : { "ref" : "queue11", "mutex": "mutex11" }, "unlock" : "mutex11", "run" : 300, "resume" : "Binder-display", "run" : 400 }, "Binder-dummy" : { "priority" : -6, "loop" : -1, "lock" : "mutex11", "wait" : { "ref" : "queue11", "mutex": "mutex11" }, "unlock" : "mutex11", "run" : 200, "lock" : "mutex11", "signal" : "queue11", "unlock" : "mutex11", "run" : 100 }, "Binder-display" : { "priority" : -6, "loop" : -1, "suspend" : "Binder-display", "run" : 300, "resume" : "Event-Browser", "resume" : "Event-Display" }, "Event-Browser" : { "priority" : -9, "loop" : -1, "suspend" : "Event-Browser", "run" : 50, "sleep" : 16000, "run" : 50, "resume" : "Browser" }, "Event-Display" : { "priority" : -9, "loop" : -1, "suspend" : "Event-Display", "run" : 50, "sleep" : 16000, "run" : 50, "resume" : "Display" }, "Display" : { "priority" : -8, "loop" : -1, "suspend" : "Display", "run" : 16000 }, }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 6, "ftrace" : false, "gnuplot" : false, "logdir" : "./", "log_basename" : "web", "lock_pages" : true, "frag" : 1, "calibration" : "CPU0" } } rt-app-1.0/doc/examples/cpufreq_governor_efficiency/000077500000000000000000000000001311057016400227105ustar00rootroot00000000000000rt-app-1.0/doc/examples/cpufreq_governor_efficiency/README000077500000000000000000000051531311057016400235770ustar00rootroot00000000000000Measure the efficiency of cpufreq governors using rt-app BACKGROUND: DVFS adds a latency in the execution of task because of the time to decide to move at max freq. We need to measure this latency and check that the governor stays in an acceptable range. When rt-app runs a json file, a log file is created for each thread. This log file records the number of loop that has been executed and the duration for executing these loops (per phase). We can use these figures to evaluate to latency that is added by a cpufreq governor and its "performance efficiency". We use the run+sleep pattern to do the measurement, for the run time per loop, the performance governor should run the expected duration as the CPU stays a max freq. At the opposite, the powersave governor will give use the longest duration (as it stays at lowest OPP). Other governor will be somewhere between the 2 previous duration as they will use several OPP and will go back to max frequency after a defined duration which depends on its monitoring period. The formula: duration of powersave gov - duration of the gov -------------------------------------------------------- x 100% duration of powersave gov - duration of performance gov will give the efficiency of the governor. 100% means as efficient as the perf governor and 0% means as efficient as the powersave governor. This test offers json files and shell scripts to do the measurement. Usage: ./calibration.sh cpu: cpu number on which you want to run the test ./test.sh [] governor: target CPUFreq governor you want to test cpu: cpu number on which you want to run the test. Be the same as the one passing to "calibration.sh". runtime: running time in ms per loop of the workload pattern sleeptime: sleeping time in ms per loop of the workload pattern loops: repeat times of the workload pattern. default: 10 Example: "./calibration.sh 0" means to calculate the computing capacity of CPU0 which will be used in the following test. "./test.sh ondemand 0 100 100 20" means to test "ondemand" on CPU0 with workload pattern "run 100ms + sleep 100ms"(20 loops). NOTE: - Make sure there are "sed", "cut", "grep", "rt-app", etc tools on your test machine, and run the scripts under root privilege. - Run the test while the system is idle. - You can change the target governor's parameters after running "calibration.sh", but before "test.sh". rt-app-1.0/doc/examples/cpufreq_governor_efficiency/calibration.json000077500000000000000000000005411311057016400260750ustar00rootroot00000000000000{ "tasks" : { "thread" : { "instance" : 1, "loop" : 1, "phases" : { "run" : { "loop" : 1, "run" : 2000, }, "sleep" : { "loop" : 1, "sleep" : 2000, } } } }, "global" : { "default_policy" : "SCHED_FIFO", "calibration" : "CPU0", "lock_pages" : true, "ftrace" : false, "logdir" : "./", } } rt-app-1.0/doc/examples/cpufreq_governor_efficiency/calibration.sh000077500000000000000000000006421311057016400255400ustar00rootroot00000000000000#!/bin/sh set -e if [ ! $1 ] ; then echo "Please input one cpu" exit fi echo performance > /sys/devices/system/cpu/cpu$1/cpufreq/scaling_governor sleep 1 sed 's/"calibration" : "CPU.*",/"calibration" : "CPU'$1'",/' -i calibration.json pLoad=$(rt-app calibration.json 2>&1 |grep pLoad |sed 's/.*= \(.*\)ns.*/\1/') sed 's/"calibration" : .*,/"calibration" : '$pLoad',/' -i dvfs.json echo CPU$1\'s pLoad is $pLoad rt-app-1.0/doc/examples/cpufreq_governor_efficiency/dvfs.json000077500000000000000000000007271311057016400245560ustar00rootroot00000000000000{ "tasks" : { "thread" : { "instance" : 1, "policy" : "SCHED_FIFO", "cpus" : [1], "loop" : 10, "phases" : { "sleeping" : { "loop" : 1, "timer" : { "ref" : "tick", "period" : 1200000, } }, "running" : { "loop" : 1, "run" : 900000, } } } }, "global" : { "default_policy" : "SCHED_OTHER", "calibration" : 128, "lock_pages" : true, "ftrace" : false, "logdir" : "./", "log_size" : 2, } } rt-app-1.0/doc/examples/cpufreq_governor_efficiency/dvfs.sh000077500000000000000000000017001311057016400242070ustar00rootroot00000000000000#!/bin/sh # $1 $2 $3 $4 $5: governor cpu run sleep loops set -e echo $1 > /sys/devices/system/cpu/cpu$2/cpufreq/scaling_governor #echo $1 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor sed 's/"cpus" : \[.*\],/"cpus" : \['$2'\],/' -i dvfs.json if [ $3 ] ; then sed 's/"run" : .*,/"run" : '$3',/' -i dvfs.json fi if [ $4 ] ; then sed 's/"period" : .*,/"period" : '$4',/' -i dvfs.json fi if [ $5 ] ; then sed '0,/"loop"/s/"loop" : .*,/"loop" : '$5',/' -i dvfs.json fi sync sleep 1 rt-app dvfs.json 2> /dev/null if [ $1 ] ; then mv -f rt-app-thread-0.log rt-app_$1_run$3us_sleep$4us.log sum=0 loop=0 overrun=0 for i in $(cat rt-app_$1_run$3us_sleep$4us.log | sed '1d;n;d' | sed '1d' | awk '{print $3}'); do loop=$(expr $loop + 1) sum=$(expr $sum + $i) if [ $4 -le $i ] ; then #echo $i"vs"$4 overrun=$(expr $overrun + 1) fi done sum=$(expr $sum / $loop) echo $sum" "$overrun #rm -f rt-app_$1_run$3us_sleep$4us.log fi rt-app-1.0/doc/examples/cpufreq_governor_efficiency/test.sh000077500000000000000000000054731311057016400242370ustar00rootroot00000000000000#!/bin/sh set -e test_efficiency() { FILENAME="results_$RANDOM$$.txt" if [ -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors ]; then for i in $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors); do if [ $i = $1 ] ; then gov_target=$i fi export gov_$i=$(echo $i) done else echo "cpufreq sysfs is not available!" exit fi if [ ! $gov_target ] ; then echo " Can't find $1 governor!" exit fi if [ ! $gov_performance ] ; then echo "Can't find performance governor!" exit fi if [ ! $gov_powersave ] ; then echo "Can't find powersave governor!" exit fi if [ $gov_target = $gov_performance ] || [ $gov_target = $gov_powersave ] ; then echo "Please input a governor other than \"performance\" or \"powersave\"" exit fi # Get target gov data first dvfs.sh $1 $2 $3 $4 $5 > $FILENAME target=$(cat $FILENAME |sed -n '$p' | cut -d " " -f 1) over_target=$(cat $FILENAME |sed -n '$p' | cut -d " " -f 2) # Get performance data dvfs.sh performance $2 $3 $4 $5 > $FILENAME performance=$(cat $FILENAME |sed -n '$p' | cut -d " " -f 1) over_perf=$(cat $FILENAME |sed -n '$p' | cut -d " " -f 2) # Get powersave data dvfs.sh powersave $2 $3 $4 $5 > $FILENAME powersave=$(cat $FILENAME |sed -n '$p' | cut -d " " -f 1) over_save=$(cat $FILENAME |sed -n '$p' | cut -d " " -f 2) if [ $performance -ge $powersave ] ; then echo "powersave: $powersave" echo "performance: $performance" echo "Error! performance spent more time than powersave!" exit fi echo "\"powersave\" efficiency: 0% overrun: "$over_save echo "\"performance\" efficiency: 100% overrun: "$over_perf denominator=$(expr $powersave - $performance) if [ $powersave -le $target ]; then target=0 else numerator=$(expr $powersave - $target) numerator=$(expr $numerator \* 100) target=$(expr $numerator / $denominator) if [ $target -gt 100 ]; then target=100 fi fi echo "\"$gov_target\" efficiency: $target% overrun: "$over_target rm -f $FILENAME } if [ $# -lt 4 ]; then echo "Usage: ./test.sh []" echo "governor: target CPUFreq governor you want to test" echo "cpu: cpu number on which you want to run the test" echo "runtime: running time in ms per loop of the workload pattern" echo "sleeptime: sleeping time in ms per loop of the workload pattern" echo "loops: repeat times of the workload pattern. default: 10" echo "\nExample:\n\"./test.sh ondemand 0 100 100 20\" means\nTest \"ondemand\" on CPU0 with workload pattern \"run 100ms + sleep 100ms\"(20 loops).\n" exit fi if [ $# = 4 ]; then loops=10 else loops=$5 fi echo "Test \"$1\" on CPU$2 with workload pattern \"run $3ms + sleep $4ms\"($loops loops)." sleep 1 PATH=$PATH:. test_efficiency $1 $2 $(expr $3 \* 1000) $(expr $4 \* 1000) $loops rt-app-1.0/doc/examples/merge/000077500000000000000000000000001311057016400162355ustar00rootroot00000000000000rt-app-1.0/doc/examples/merge/global.json000066400000000000000000000003111311057016400203630ustar00rootroot00000000000000{ "global" : { "default_policy" : "SCHED_OTHER", "duration" : 5, "ftrace" : true, "gnuplot" : true, "logdir" : "/tmp/", "log_basename" : "rt-app", "lock_pages" : true, "frag" : 1 } } rt-app-1.0/doc/examples/merge/resources.json000066400000000000000000000004531311057016400211440ustar00rootroot00000000000000{ "resources" : { "m0" : { "type" : "mutex" }, "sync_mutex" : { "type" : "mutex" }, "wait" : { "type" : "wait" }, "trig" : { "type" : "signal", "target" : "wait" }, "m1" : { "type" : "mutex" }, "wake" : { "type" : "wait" }, "broad" : { "type" : "broadcast", "target" : "wake" } } } rt-app-1.0/doc/examples/merge/thread0.json000066400000000000000000000005531311057016400204620ustar00rootroot00000000000000{ "tasks" : { "thread0" : { "exec" : 5000, "period" : 5000, "deadline" : 5000, "lock_order" : ["wait", "m0", "broad"], "resources" : { "m0" : { "duration" : 1000 }, "wait" : { "duration" : 0, "access": ["sync_mutex"] }, "broad" : { "duration" : 0, "access": ["m1"] } } } } } rt-app-1.0/doc/examples/merge/thread1.json000066400000000000000000000004461311057016400204640ustar00rootroot00000000000000{ "tasks" : { "thread1" : { "exec" : 1000, "period" : 10000, "deadline" : 8000, "lock_order" : ["m0", "trig"], "resources" : { "m0" : { "duration" : 500 }, "trig" : { "duration" : 0, "access": ["sync_mutex"] } } } } } rt-app-1.0/doc/examples/merge/thread2.json000066400000000000000000000003641311057016400204640ustar00rootroot00000000000000{ "tasks" : { "thread2" : { "exec" : 1000, "period" : 1000, "deadline" : 1000, "lock_order" : ["wake"], "resources" : { "wake" : { "duration" : 0, "access": ["m1"] } } } } } rt-app-1.0/doc/examples/merge/thread3.json000066400000000000000000000003641311057016400204650ustar00rootroot00000000000000{ "tasks" : { "thread3" : { "exec" : 1000, "period" : 1000, "deadline" : 1000, "lock_order" : ["wake"], "resources" : { "wake" : { "duration" : 0, "access": ["m1"] } } } } } rt-app-1.0/doc/examples/mp3-long.json000066400000000000000000000024411311057016400174660ustar00rootroot00000000000000{ "tasks" : { "AudioTick" : { "priority" : -19, "loop" : -1, "cpus" : [0], "phases" : { "p1" : { "loop" : 1, "resume" : "AudioOut", "timer" : { "ref" : "tick", "period": 6000 } }, "p2" : { "loop" : 4, "timer" : { "ref" : "tick", "period": 6000 } } } }, "AudioOut" : { "priority" : -19, "loop" : -1, "run" : 275, "resume" : "AudioTrack", "run" : 4725, "suspend" : "AudioOut" }, "AudioTrack" : { "priority" : -16, "loop" : -1, "suspend" : "AudioTrack", "run" : 300, "resume" : "mp3.decoder" }, "mp3.decoder" : { "priority" : -2, "loop" : -1, "suspend" : "mp3.decoder", "run" : 1000, "lock" : "mutex", "signal" : "queue", "wait" : { "ref" : "queue", "mutex": "mutex" }, "unlock" : "mutex", "run" : 150 }, "OMXCall" : { "priority" : -2, "loop" : -1, "lock" : "mutex", "wait" : { "ref" : "queue", "mutex": "mutex" }, "unlock" : "mutex", "run" : 300, "lock" : "mutex", "signal" : "queue", "unlock" : "mutex" } }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 600, "ftrace" : false, "gnuplot" : false, "logdir" : "./", "log_basename" : "mp3", "lock_pages" : true, "frag" : 1, "calibration" : "CPU0" } } rt-app-1.0/doc/examples/mp3-short.json000066400000000000000000000024371311057016400176730ustar00rootroot00000000000000{ "tasks" : { "AudioTick" : { "priority" : -19, "loop" : -1, "cpus" : [0], "phases" : { "p1" : { "loop" : 1, "resume" : "AudioOut", "timer" : { "ref" : "tick", "period": 6000 } }, "p2" : { "loop" : 4, "timer" : { "ref" : "tick", "period": 6000 } } } }, "AudioOut" : { "priority" : -19, "loop" : -1, "run" : 275, "resume" : "AudioTrack", "run" : 4725, "suspend" : "AudioOut" }, "AudioTrack" : { "priority" : -16, "loop" : -1, "suspend" : "AudioTrack", "run" : 300, "resume" : "mp3.decoder" }, "mp3.decoder" : { "priority" : -2, "loop" : -1, "suspend" : "mp3.decoder", "run" : 1000, "lock" : "mutex", "signal" : "queue", "wait" : { "ref" : "queue", "mutex": "mutex" }, "unlock" : "mutex", "run" : 150 }, "OMXCall" : { "priority" : -2, "loop" : -1, "lock" : "mutex", "wait" : { "ref" : "queue", "mutex": "mutex" }, "unlock" : "mutex", "run" : 300, "lock" : "mutex", "signal" : "queue", "unlock" : "mutex" } }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 6, "ftrace" : false, "gnuplot" : false, "logdir" : "./", "log_basename" : "mp3", "lock_pages" : true, "frag" : 1, "calibration" : "CPU0" } } rt-app-1.0/doc/examples/spreading-tasks.json000066400000000000000000000024071311057016400211330ustar00rootroot00000000000000{ "tasks" : { "thread1" : { "instance" : 1, "loop" : -1, "phases" : { "light" : { "loop" : 300, "run" : 1000, "timer" : { "ref" : "unique", "period" : 10000 } }, "heavy" : { "loop" : 300, "run" : 7000, "timer" : { "ref" : "unique", "period" : 10000 } } } }, "thread2" : { "instance" : 1, "loop" : -1, "phases" : { "light1" : { "loop" : 900, "run" : 1000, "timer" : { "ref" : "unique", "period" : 10000 } }, "heavy1" : { "loop" : 600, "run" : 7000, "timer" : { "ref" : "unique", "period" : 10000 } }, "light2" : { "loop" : 300, "run" : 1000, "timer" : { "ref" : "unique", "period" : 10000 } }, "heavy1" : { "loop" : 600, "run" : 7000, "timer" : { "ref" : "unique", "period" : 10000 } }, } } }, "global" : { "duration" : 60, "default_policy" : "SCHED_OTHER", "calibration" : "CPU0" } } rt-app-1.0/doc/examples/template.json000066400000000000000000000011721311057016400176450ustar00rootroot00000000000000{ /* * Simple use case which creates 10% load * for 6 seconds. * A "sleep" : 0 has been added so the file can be used by tune_json.py to * use a sleep event instead of the timer. In this latter case, you need * to set the timer's period to 0 */ "tasks" : { "thread0" : { "instance" : 1, "loop" : -1, "run" : 10000, "sleep" : 0, "timer" : { "ref" : "unique", "period" : 100000 } } }, "global" : { "duration" : 6, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_basename" : "rt-app2", "gnuplot" : true } } rt-app-1.0/doc/examples/tutorial/000077500000000000000000000000001311057016400170015ustar00rootroot00000000000000rt-app-1.0/doc/examples/tutorial/example1.json000066400000000000000000000007071311057016400214140ustar00rootroot00000000000000{ /* * Simple use case which creates a thread that run 2ms then sleep 8ms * until the use case is stopped with Ctrl+C */ "tasks" : { "thread0" : { "loop" : -1, "run" : 20000, "sleep" : 80000 } }, "global" : { "duration" : 2, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_basename" : "rt-app1", "ftrace" : true, "gnuplot" : true, } } rt-app-1.0/doc/examples/tutorial/example2.json000066400000000000000000000007371311057016400214200ustar00rootroot00000000000000{ /* * Simple use case which creates 10% load * until the use case is stopped with Ctrl+C */ "tasks" : { "thread0" : { "instance" : 1, "loop" : -1, "run" : 10000, "timer" : { "ref" : "unique", "period" : 100000 } } }, "global" : { "duration" : 2, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_basename" : "rt-app2", "ftrace" : true, "gnuplot" : true } } rt-app-1.0/doc/examples/tutorial/example3.json000066400000000000000000000007241311057016400214150ustar00rootroot00000000000000{ /* * Simple use case which starts with a 10% load during 1sec (100 loops) * and then increase to 90% for the next sec (100 loops) */ "tasks" : { "thread0" : { "instance" : 12, "loop" : 1, "phases" : { "light" : { "loop" : 10, "run" : 3000, "timer" : { "ref" : "unique", "period" : 30000 } }, "heavy" : { "loop" : 10, "run" : 27000, "timer" : { "ref" : "unique", "period" : 30000 } } } } } } rt-app-1.0/doc/examples/tutorial/example4.json000066400000000000000000000005621311057016400214160ustar00rootroot00000000000000{ /* * Simple use case with 2 threads that runs for 10 ms and wake up each * other until the use case is stopped with Ctrl+C */ "tasks" : { "thread0" : { "loop" : -1, "run" : 10000, "resume" : "thread1", "suspend" : "thread0" }, "thread1" : { "loop" : -1, "run" : 10000, "resume" : "thread0", "suspend" : "thread1" } } } rt-app-1.0/doc/examples/tutorial/example5.json000066400000000000000000000020461311057016400214160ustar00rootroot00000000000000{ /* This example doesn't care of unique id constraint imposed for json * file and multiplies the used of the same key id at same object's level. * Removing this constraint makes the json file more readable and easier * to create. This file can't be used directly with rt-app but can be used * with workgen which parses and modifies it to match with json requirement */ "tasks": { "thread0": { "instance" : 1, "priority": -19, "cpus": [ 0 ], "loop" : 1, "phases" : { "p0" : { "sleep" : 10000, }, "p1" : { "loop" : 8, "lock": "mutex", "run" : 10000, "signal": "queue", "run" : 10000, "unlock": "mutex", "run": 100000, "resume" : "thread1", "timer": { "ref" : "tick", "period" : 200000} } } }, "thread1": { "priority": -19, "cpus": [ 1 ], "loop" : 3, "lock": "mutex", "wait": { "ref": "queue", "mutex": "mutex" }, "unlock": "mutex", "run": 10000, "suspend" : "thread1", "run": 10000, "suspend" : "thread1", "run": 10000 } } } rt-app-1.0/doc/examples/tutorial/example6.json000066400000000000000000000010421311057016400214120ustar00rootroot00000000000000{ /* * Simple use case which creates CPU-, memory-, * and IO-bouned load for 2 seconds duration. */ "tasks" : { "thread0" : { "instance" : 1, "loop" : -1, "run" : 1000, "mem" : 1000, "sleep" : 5000, "iorun" : 100000 } }, "global" : { "duration" : 2, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_basename" : "rt-app2", "ftrace" : true, "gnuplot" : true, "io_device" : "/dev/null", "mem_buffer_size" : 1048576 } } rt-app-1.0/doc/examples/tutorial/example7.json000066400000000000000000000037751311057016400214320ustar00rootroot00000000000000{ /* Simple use case with two tasks driving each other in turn. This demonstrates that the barrier event causes tasks to be synchronized no matter which task reaches the barrier first. The sequence which should be seen is: time task0 task1 0000: run run 1000: sleep run 2000: sleep wait FIRST 3000: wait FIRST, then run wait FIRST, then run 4000: run sleep 5000: wait SECOND sleep 6000: wait SECOND, then run wait SECOND, then run (These times will not be exactly reflected when you run the example as we don't account for any scheduling effects or run/sleep time variation in the explanation) At 2000, task1 is waiting for the FIRST barrier however task0 doesn't enter this barrier until the 2000-usec sleep event completes at 3000. In this first barrier, task1 waits for task0 to reach the synchronization point. At 5000, task0 is waiting for the SECOND barrier, however task1 does not enter this barrier until the 2000-usec sleep event completes at 6000. In this second barrier, task0 waits for task1 to reach the synchronization point. */ "tasks" : { "task0" : { "loop" : -1, "runtime1" : 1000, "sleep1" : 2000, "barrier1" : "FIRST", "runtime2" : 2000, "barrier2" : "SECOND", "runtime3" : 1000, "sleep3" : 2000, "barrier3" : "THIRD", }, "task1" : { "loop" : -1, "runtime1" : 2000, "barrier1" : "FIRST", "runtime2" : 1000, "sleep2" : 2000, "barrier2" : "SECOND", "runtime3" : 2000, "barrier3" : "THIRD", }, }, "global" : { "duration" : 5, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_basename" : "rt-app1", "ftrace" : true, "gnuplot" : false, } } rt-app-1.0/doc/examples/tutorial/example8.json000066400000000000000000000014431311057016400214210ustar00rootroot00000000000000{ /* * Simple use case which creates a thread with 3 phases each running for * 1.5ms. In phase 1 the thread will be affined to CPU 0, in phase 2 it * will be affined to CPU 1 and in the third phase it will be affined to * CPUs 2 (inherit from "cpus" at task level). */ "tasks" : { "thread0" : { "cpus" : [2], "phases" : { "phase1" : { "cpus" : [0], "loop" : 1, "run" : 1500 }, "phase2" : { "cpus" : [1], "loop" : 1, "run" : 1500 }, "phase3" : { "loop" : 1, "run" : 1500 } } } }, "global" : { "duration" : 2, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_basename" : "rt-app1", "ftrace" : true, "gnuplot" : true } } rt-app-1.0/doc/examples/video-long.json000066400000000000000000000107141311057016400200770ustar00rootroot00000000000000{ "tasks" : { "surfaceflinger" : { "priority" : -7, "loop" : -1, "suspend", "run" : 1500 }, "DispSync" : { "priority" : -7, "loop" : -1, "phases" : { "p1" : { "suspend", "run" : 35, "resume" : "EventThread", "run" : 40, }, "p2" : { "loop" : 2, "suspend", "run" : 30 } }, }, "hwc_eventmon" : { "priority" : -19, "loop" : -1, "resume" : "DispSync", "run" : 115, "timer" : { "ref" : "timerA", "period" : 16667 } }, "EventThread1" : { "priority" : -8, "loop" : -1, "phases" : { "p1" : { "suspend" : "EventThread", "run" : 25, "resume" : "DispSync", "sleep" : 9650, "run" : 70, "resume" : "DispSync", "run" : 80 }, "p2" : { "suspend" : "EventThread", "run" : 90, "resume" : "DispSync" } } }, "EventThread2" : { "priority" : -8, "loop" : -1, "phases" : { "p1" : { "suspend" : "EventThread", "run" : 30, "resume" : "surfaceflinger" }, "p2" : { "suspend" : "EventThread", "run" : 35, "sleep" : 2000, "run" : 110, "resume" : "DispSync", "run" : 60 } } }, "waker" : { "priority" : -19, "loop" : -1, "resume" : "NuPlayerRenderer", "timer" : { "ref" : "timerB", "period" : 33333 } }, "NuPlayerRenderer" : { "priority" : -15, "loop" : -1, "phases" : { "p1" : { "loop" : 3, "suspend" : "NuPlayerRenderer", "run" : 140, "resume" : "NuPlayerDriver1", "run" : 95 }, "p2" : { "sleep" : 27000, "run" : 580, "resume" : "NPDecoder", "resume" : "NPDecoder-CL", "resume" : "gle.aac.decoder" } } }, "NuPlayerDriver1" : { "priority" : -15, "loop" : -1, "suspend", "run" : 100, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 50, "suspend" : "NuPlayerDriver", "run" : 80, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 370, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 135, "resume" : "NuPlayerDriver" }, "NuPlayerDriver2" : { "priority" : -15, "loop" : -1, "suspend" : "NuPlayerDriver", "run" : 110, "resume" : "NuPlayerDriver", "resume" : "CodecLooper1", "sleep" : 2500, "run" : 80, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 50, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 70, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 35 }, "CodecLooper1" : { "priority" : -15, "loop" : -1, "suspend", "run" : 230, "sleep" : 80, "run" : 150, "sleep" : 210, "run" : 330, "resume" : "CodecLooper2", "sleep" : 900, "run" : 170, "sleep" : 670, "run" : 125, "resume" : "CodecLooper2" }, "CodecLooper2" : { "priority" : -1, "loop" : -1, "suspend", "run" : 160, "resume" : "CodecLooper3", "sleep" : 590, "resume" : "OMXCallbackDisp2", "run" : 75, "suspend", "run" : 260 }, "OMXCallbackDisp2" : { "priority" : -1, "loop" : -1, "suspend", "run" : 180 }, "CodecLooper3" : { "priority" : -1, "loop" : -1, "suspend", "run" : 1000 }, "NPDecoder" : { "priority" : -15, "loop" : -1, "suspend", "run" : 500, "sleep" : 680, "resume" : "OMXCallbackDisp1", "run" : 2000 }, "NPDecoder-CL" : { "priority" : -15, "loop" : -1, "suspend", "run" : 570, "sleep" : 570, "run" : 2100 }, "gle.aac.decoder" : { "priority" : -1, "loop" : -1, "suspend", "run" : 2400, "sleep" : 430, "run" : 45 }, "OMXCallbackDisp1" : { "priority" : -1, "loop" : -1, "suspend", "run" : 135, "sleep" : 230, "run" : 140, "sleep" : 330, "run" : 190, "sleep" : 550, "run" : 160 } }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 600, "ftrace" : false, "gnuplot" : false, "logdir" : "./", "log_basename" : "video", "lock_pages" : true, "frag" : 1, "calibration" : "CPU0" } } rt-app-1.0/doc/examples/video-short.json000066400000000000000000000107121311057016400202750ustar00rootroot00000000000000{ "tasks" : { "surfaceflinger" : { "priority" : -7, "loop" : -1, "suspend", "run" : 1500 }, "DispSync" : { "priority" : -7, "loop" : -1, "phases" : { "p1" : { "suspend", "run" : 35, "resume" : "EventThread", "run" : 40, }, "p2" : { "loop" : 2, "suspend", "run" : 30 } }, }, "hwc_eventmon" : { "priority" : -19, "loop" : -1, "resume" : "DispSync", "run" : 115, "timer" : { "ref" : "timerA", "period" : 16667 } }, "EventThread1" : { "priority" : -8, "loop" : -1, "phases" : { "p1" : { "suspend" : "EventThread", "run" : 25, "resume" : "DispSync", "sleep" : 9650, "run" : 70, "resume" : "DispSync", "run" : 80 }, "p2" : { "suspend" : "EventThread", "run" : 90, "resume" : "DispSync" } } }, "EventThread2" : { "priority" : -8, "loop" : -1, "phases" : { "p1" : { "suspend" : "EventThread", "run" : 30, "resume" : "surfaceflinger" }, "p2" : { "suspend" : "EventThread", "run" : 35, "sleep" : 2000, "run" : 110, "resume" : "DispSync", "run" : 60 } } }, "waker" : { "priority" : -19, "loop" : -1, "resume" : "NuPlayerRenderer", "timer" : { "ref" : "timerB", "period" : 33333 } }, "NuPlayerRenderer" : { "priority" : -15, "loop" : -1, "phases" : { "p1" : { "loop" : 3, "suspend" : "NuPlayerRenderer", "run" : 140, "resume" : "NuPlayerDriver1", "run" : 95 }, "p2" : { "sleep" : 27000, "run" : 580, "resume" : "NPDecoder", "resume" : "NPDecoder-CL", "resume" : "gle.aac.decoder" } } }, "NuPlayerDriver1" : { "priority" : -15, "loop" : -1, "suspend", "run" : 100, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 50, "suspend" : "NuPlayerDriver", "run" : 80, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 370, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 135, "resume" : "NuPlayerDriver" }, "NuPlayerDriver2" : { "priority" : -15, "loop" : -1, "suspend" : "NuPlayerDriver", "run" : 110, "resume" : "NuPlayerDriver", "resume" : "CodecLooper1", "sleep" : 2500, "run" : 80, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 50, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 70, "lock" : "NuPlayerDriver", "sync" : { "ref" : "NuPlayerDriver", "mutex" : "NuPlayerDriver" }, "unlock" : "NuPlayerDriver", "run" : 35 }, "CodecLooper1" : { "priority" : -15, "loop" : -1, "suspend", "run" : 230, "sleep" : 80, "run" : 150, "sleep" : 210, "run" : 330, "resume" : "CodecLooper2", "sleep" : 900, "run" : 170, "sleep" : 670, "run" : 125, "resume" : "CodecLooper2" }, "CodecLooper2" : { "priority" : -1, "loop" : -1, "suspend", "run" : 160, "resume" : "CodecLooper3", "sleep" : 590, "resume" : "OMXCallbackDisp2", "run" : 75, "suspend", "run" : 260 }, "OMXCallbackDisp2" : { "priority" : -1, "loop" : -1, "suspend", "run" : 180 }, "CodecLooper3" : { "priority" : -1, "loop" : -1, "suspend", "run" : 1000 }, "NPDecoder" : { "priority" : -15, "loop" : -1, "suspend", "run" : 500, "sleep" : 680, "resume" : "OMXCallbackDisp1", "run" : 2000 }, "NPDecoder-CL" : { "priority" : -15, "loop" : -1, "suspend", "run" : 570, "sleep" : 570, "run" : 2100 }, "gle.aac.decoder" : { "priority" : -1, "loop" : -1, "suspend", "run" : 2400, "sleep" : 430, "run" : 45 }, "OMXCallbackDisp1" : { "priority" : -1, "loop" : -1, "suspend", "run" : 135, "sleep" : 230, "run" : 140, "sleep" : 330, "run" : 190, "sleep" : 550, "run" : 160 } }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 6, "ftrace" : false, "gnuplot" : false, "logdir" : "./", "log_basename" : "video", "lock_pages" : true, "frag" : 1, "calibration" : "CPU0" } } rt-app-1.0/doc/merge.py000077500000000000000000000014671311057016400150040ustar00rootroot00000000000000#!/usr/bin/env python import os import sys import getopt import json outfile = "merged.json" try: opts, args = getopt.getopt(sys.argv[1:], "o:") except getopt.GetoptError as err: print str(err) # will print something like "option -a not recognized" sys.exit(2) for o, a in opts: if o == "-o": outfile = a merged = dict() for f in args: if not os.path.exists(f): print "WARN: %s does not exist", f fp = open(f, "r") d = json.load(fp) fp.close() for key in d: print key, if merged.has_key(key): print "WARNING: merged already has key", key merged[key].update(d[key]) else: merged[key] = d[key] print merged print fp = open(outfile, "w") json.dump(merged, fp, indent=4, sort_keys=True) fp.close() rt-app-1.0/doc/taskset.json000066400000000000000000000047651311057016400157050ustar00rootroot00000000000000{ "tasks" : { "ThreadA" : { "exec" : 5000, "period" : 24000, "priority" : -19, "cpus": [0], "lock_order" : ["r0", "trig1"], "resources" : { "r0" : { "duration" : 1000 }, "trig1" : { "duration" : 0 }, } }, "ThreadB" : { "priority" : -16, "phases" : { "phase1" : { "exec" : 300, "period" : 24000, "sleep" : false, "loop" : 1, "lock_order" : ["wait1", "r0", "trig2"], "resources" : { "wait1" : { "duration" : 0, "access": ["trig1_mutex"] }, "r0" : { "duration" : 300 }, "trig2" : { "duration" : 0 }, } }, "phase2" : { "exec" : 4000, "period" : 24000, "loop" : 2, "sleep" : false, "lock_order" : ["wait1", "r0", "trig2"], "resources" : { "wait1" : { "duration" : 0, "access": ["trig1_mutex"] }, "r0" : { "duration" : 300 }, "trig2" : { "duration" : 0 }, } }, } }, "ThreadC" : { "exec" : 1150, "period" : 24000, "priority" : -2, "sleep" : false, "lock_order" : ["wait2", "r0", "sync3"], "resources" : { "wait2" : { "duration" : 0, "access": ["trig2_mutex"] }, "r0" : { "duration" : 1000 }, "sync3" : { "duration" : 0, "access": ["trig3_mutex"] }, } }, "ThreadD" : { "exec" : 300, "period" : 24000, "deadline" : 24000, "priority" : -2, "sleep" : false, "lock_order" : ["wait3", "r0", "trig3"], "resources" : { "wait3" : { "duration" : 0, "access": ["trig3_mutex"] }, "r0" : { "duration" : 300 }, "trig3" : { "duration" : 0, "access": ["trig3_mutex"] }, } }, }, "resources" : { "trig1_mutex" : { "type" : "mutex" }, "wait1" : { "type" : "wait" }, "trig1" : { "type" : "signal", "target" : "wait1" }, "trig2_mutex" : { "type" : "mutex" }, "wait2" : { "type" : "wait" }, "trig2" : { "type" : "signal", "target" : "wait2" }, "trig3_mutex" : { "type" : "mutex" }, "wait3" : { "type" : "wait" }, "trig3" : { "type" : "signal", "target" : "wait3" }, "sync3" : { "type" : "sync", "target" : "wait3" }, "r0" : { "type" : "run" }, }, "global" : { "default_policy" : "SCHED_OTHER", "duration" : 5, "ftrace" : false, "gnuplot" : false, "logdir" : "/tmp/", "log_basename" : "rt-app", "lock_pages" : true, "frag" : 1, "calibration" : "CPU1", } } rt-app-1.0/doc/tune_json.py000077500000000000000000000101311311057016400156750ustar00rootroot00000000000000#!/usr/bin/env python import argparse import collections import json import os import re import shutil import sys import tempfile def find_dict_by_key(doc, key): if key in doc and type(doc[key]) is collections.OrderedDict: return doc[key] for k in doc: if type(doc[k]) is collections.OrderedDict: return find_dict_by_key(doc[k], key) def dict_find_and_replace_value(dic, key, val): for k in dic: if type(dic[k]) is collections.OrderedDict: dict_find_and_replace_value(dic[k], key, val) if k == key: dic[k] = val def dict_of_loading(dic): if not 'run' in dic: return False, None for k in dic: if 'timer' in k and 'period' in dic[k]: return True, k else: return False, None def calculate_and_update_loading(dic, loading): of_loading, timer_id = dict_of_loading(dic) if of_loading: period = dic[timer_id]['period'] run = period * loading / 100 dic['run'] = run for k in dic: if type(dic[k]) is collections.OrderedDict: calculate_and_update_loading(dic[k], loading) # strip comments in json file and load the file as a dict def load_json_file(filename): try: f = open(filename, 'r') except: print 'ERROR: Unable to open %s' %filename sys.exit(2) comment_re = re.compile( '(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?', re.DOTALL | re.MULTILINE) content = ''.join(f.readlines()) f.close() match = comment_re.search(content) while match: content = content[:match.start()] + content[match.end():] match = comment_re.search(content) return json.JSONDecoder(object_pairs_hook=collections.OrderedDict).decode(content) def dump_json_file(doc, outfile): tmp = tempfile.NamedTemporaryFile(delete=False) json.dump(doc, tmp, indent=4, sort_keys=False) tmp.close() shutil.move(tmp.name, outfile) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-f', '--file', dest='infile', default='', help='input json filename') parser.add_argument('-o', '--out', dest='outfile', default='workload.json', help='output json filename'); parser.add_argument('--instance', default=0, type=int, help='number of thread instance') parser.add_argument('--period', default=0, type=int, help='period of each thread/phase (us)') parser.add_argument('--run', default=0, type=int, help='run time of each thread/phase (us)') parser.add_argument('--sleep', default=0, type=int, help='sleep time of each thread/phase (us)') parser.add_argument('--loop', default=0,type=int, help='loop count of each thread/phase (-1 as infinite loop)') parser.add_argument('--loading', default=0, type=int, help='loading of each thread (%%)') parser.add_argument('--key', type=str, help='the key id of thread/phase in which the parameters will be changed') parser.add_argument('--duration', default=0, type=int, help='max duration of the use case (s)') args = parser.parse_args() if not os.path.isfile(args.infile): print 'ERROR: input file %s does not exist\n' %args.infile parser.print_help() sys.exit(2) doc = target = load_json_file(args.infile) if args.key: target = find_dict_by_key(doc, args.key) if not target: print 'ERROR: key id %s is not found' %args.key sys.exit(2) if args.instance > 0: dict_find_and_replace_value(target, 'instance', args.instance) if args.period > 0: dict_find_and_replace_value(target, 'period', args.period) if args.duration > 0: dict_find_and_replace_value(target, 'duration', args.duration) if args.run > 0: dict_find_and_replace_value(target, 'run', args.run) if args.sleep > 0: dict_find_and_replace_value(target, 'sleep', args.sleep) if args.loop > 0 or args.loop == -1: dict_find_and_replace_value(target, 'loop', args.loop) if args.loading > 0: calculate_and_update_loading(target, args.loading); dump_json_file(doc, args.outfile) rt-app-1.0/doc/tutorial.txt000066400000000000000000000717351311057016400157410ustar00rootroot00000000000000HOW-TO use rt-app and workgen to emulate a workload ? **** rt-app and workgen **** rt-app/workgen is a tool that can be used to emulate a use case. Not only the sleep and run pattern can be emulated but also the dependency between tasks like accessing same critical resources, creating sequencial wake up or syncing the wake up of threads. The use case is described in a json like file which is first parsed by workgen then rt-app. workgen is a python script that will parse, check and update the json like file in order to strictly match the json grammar before calling rt-app that will execute the sequence described in it. Even if the json protocol is used in rt-app, workgen enables some freedom compared to stricter json grammar rules. For examples, - You don't have to ensure uniqueness of key in json object, workgen will check the file and ensure that each key is unique by appending an index if necessary. The feature is quite useful when you describe a sequence made of same kind of events e.g. a sequence of multiple sleep and run events. - Some values can be omitted and workgen will add them when it parses the file before starting the use case. **** Compiling and cross-compiling rt-app **** rt-app uses libjson-c to parse the .json file You can either install the libjson-c2 and libjson-c-dev deb packages or compile from source code available here: https://github.com/json-c/json-c The following sequence can be used to cross compile rt-app with static linkage for aarch64: For libjson-c: export ac_cv_func_malloc_0_nonnull=yes export ac_cv_func_realloc_0_nonnull=yes ./autogen.sh ./configure --host=aarch64-linux-gnu --disable-shared --enable-static make For rt-app: export ac_cv_lib_json_c_json_object_from_file=yes ./autogen.sh ./configure --host=aarch64-linux-gnu LDFLAGS=" --static -L/json-c/." CFLAGS="-I" --with-deadline make e.g, with a directory structure like the following: $ tree -d . ├── autom4te.cache ├── build ├── build-aux ├── doc │   └── examples │   ├── cpufreq_governor_efficiency │   ├── merge │   └── tutorial ├── json-c │   ├── autom4te.cache │   ├── doc │   │   └── html │   └── tests ├── libdl ├── m4 └── src the configure step becomes ./configure --host=aarch64-linux-gnu LDFLAGS="--static -L./../json-c/." CFLAGS="-I./.." --with-deadline **** json file skeleton **** The json file that describes a workload is made on 3 main objects: tasks, resources and global objects. Only tasks object is mandatory; default value will be used if global object is not defined and resources object is kept for backward compatibility with old version of rt-app but it doesn't give any benefit to declare it as the resources are now dynamically created by rt-app when it parses the file. **** global object **** The global object defines parameters for the whole use case: * duration : Integer. Duration of the use case in seconds. All the threads will be killed once the duration has elapsed. if -1 has been set, the use case will run indefinitly until all threads kill themselves (as an example if a finite number of loop has been defined in their running pattern) or if a signal is received to stop the use case. * calibration : String or Integer: A String defines the CPU that will be used to calibrate the ns per loop value. "CPU0" is the default value (see run event section for details about ns per loop value). A integer skips the calibration step and uses the integer value as ns per loop. * default_policy : String. Default scheduling policy of threads. Default value is SCHED_OTHER. * pi_enabled: Boolean. Enable the prority inheritance of mutex. Default value is False. * lock_pages : Boolean. Lock the mem page in RAM. Locking the page in RAM ensures that your RT thread will not be stalled until a page is moved from swap to RAM. The lock of the page is only possible for non CFS tasks. Default value is False. * logdir : String. Path to store the various log files. The default path is the current directory (./). * log_basename : String. Prefix used for all log files of the use case. "rt-app-" is used by default. * log_size : String or Integer. A Integer defines a fix size in MB of the temporary buffer (size per thread) that will be used to store the log data before saving them in a file. This temporary buffer is used as a cicular buffer so the oldest data will be lost in case of overflow. A string is used to set a predifined behavior: - "file" will be used to store the log data directly in the file without using a temporary buffer. - "Disable" will disable the log mecahnism. - "Auto" will let rt-app compute the buffer size to not overflow the latter during the use case. The use of a temporary buffer prevents the threads of unexpected wait during io access. The "Auto" mode is not implemented yet and fallback to "file" mode for the moment. * ftrace: Boolean. If enable, rt-app logs in ftrace the main events of the use case. Default value is False. * gnuplot : Boolean. if True, it will create a gnu plot compatible file for each threads (see gnuplot section for more details). Default value is False. "io_device" : Text. Path to the file which will be written to create IO-bounded busy loop. Specify it carefully since it might damage the specified file. Default value is "/dev/null". "mem_buffer_size" : Integer. The size of per-thread memory buffer in byte being used to create IO-bounded and memory-bounded busy loop. Default value is 4194304(4MB). * cumulative_slack : Boolean. Accumulate slack (see below) measured during successive timer events in a phase. Default value is False (time between the end of last event and the end of the phase). *** default global object: "global" : { "duration" : -1, "calibration" : "CPU0", "default_policy" : "SCHED_OTHER", "pi_enabled" : false, "lock_pages" : false, "logdir" : "./", "log_size" : "file", "log_basename" : "rt-app", "ftrace" : false, "gnuplot" : false, "io_device" : "/dev/null" "mem_buffer_size" : 4194304, "cumulative_slack" : false } **** tasks object **** The tasks object is made of sub-objects that describe threads. No other kind of object than thread object is allowed. The key value of each object will be used as thread's name. "tasks" : { "thread_name1" : { ... }, "thread_name2" : { ... }, "thread_name3" : { ... }, ... } *** thread object *** Thread object describes the behavior of one kind of thread which means that several threads can be created with the same object (see instance parameter below). * instance : Integer. Define the number of threads that will be created with the properties of this thread object. Default value is 1. *** policy : String: Define the scheduling policy of the thread. default_policy is used if not defined * priority : Integer. Define the scheduling priority of the thread. The value must be aligned with the range allowed by the policy. The default priority is 0 for sched_other class and 10 for other classes * dl-runtime : Integer: Define the runtime budget for deadline scheduling class. Default value is 0. The unit is usec. * dl-period : Integer. Define the period duration for deadline scheduling class. Default value is runtime. The unit is usec. * dl-deadline : Integer. Define the deadline parameter for deadline scheduling class. Default value is period. The unit is usec. * cpus: Array of Integer. Define the CPU affinity of the thread. Default value is all CPUs of the system. An example : "cpus" : [0, 2, 3] * delay: Integer. Initial delay before a thread starts execution. The unit is usec. * phases: Object. The phases object described TBF If there is only 1 phase, the sequence of events can be directly put in the thread object instead of creating a phases object. As an example, the 2 objects below are equals: "task" : { "thread1" : { "phases" : { "phase1" : { "run" : 10, "sleep" : 10 } } } } "task" : { "thread1" : { "run" : 10, "sleep" : 10 } } "cpus" can be specified at task level or phase level. As an example, below creates two threads. One thread will run with affinity of CPU 2 and 3. The second task will run first phase with affinity to CPU 0 and 1, second phase with affinity to CPU 2, and the third phase with affinity to CPU 4, 5, and 6 (it will inherit the affinity from the task level): "task" : { "thread1" : { "cpus" : [2,3], "phases" : { "phase1" : { "run" : 10, "sleep" : 10 } } } "thread2" : { "cpus" : [4,5,6], "phases" : { "phase1" : { "cpus" : [0,1], "run" : 10, "sleep" : 10 } "phase2" : { "cpus" : [2], "run" : 10, "sleep" : 10 } "phase3" : { "run" : 10, "sleep" : 10 } } } } * loop: Integer. Define the number of times the parent object must be run. The parent object can be a phase or a thread. For phase object, the loop defines the number of time the phase will be executed before starting the next phase. For thread object, the loop defines the number of times that the complete "phases" object will be executed. The default value is -1. *** events *** events are simple action that will be performed by the thread or on the thread. They have to be listed by execution order. * run : Integer. Emulate the execution of a load. The duration is defined in usec but the run event will effectively run a number of time a loop that waste cpu cycles. When the run event is executed, the duration is transformed in a number of loop thanks to the ns per loop value (see calibration). This way of working enables to emulate a load with a duration that will vary with the frequency or the compute capacity of the CPU. * runtime : Integer. The duration is define in usec. Similar to the run event, it emulates the execution of a load. Unlike run, runtime runs for a specific amount of time irrespective of the compute capacity of the CPU or the frequency. * sleep : Integer. Emulate the sleep of a task. The duration is defined in usec. * mem : Integer. Emulate the memory write operation. The value defines the size in byte to be written into the memory buffer. The size of the memory buffer is defined by "mem_buffer_size" in "global" object. * iorun : Integer. Emulate the IO write operation. The value defined the size in byte to be write into the IO device specified by "io_device" in "global" object. * timer : Object. Emulate the wake up of the thread by a timer. Timer differs from sleep event by the start time of the timer duration. Sleep duration starts at the beginning of the sleep event whereas timer duration starts at the end of the last use of the timer. So Timer event are immunized against preemption, frequency scaling and computing capacity of a CPU. The initial starting time of the timer is set during the 1st use of the latter. |<------19---------->|<------19---------->|<------19---------->| task A ...|run 5|timer 19 |------run 5|timer 19|run 5|timer 19 | task B |run 10 | As an example to point out the difference between sleep and timer, let consider a task A that run 5 and then sleep 10. The period of task A should be 15. Let's add a task B that runs 5 and use a timer to wakes up every 19. |<------15------>|<------15------>|<------19---------->|<------15------>|<------16------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->| taskA ...|run 5|sleep 10 |run 5|sleep 10 |----run 5|sleep 10 |run 5|sleep 10 |-run 5|sleep 10 |----run 5|sleep 10 |----run 5|sleep 10 |----run 5|sleep 10 |----run 5|sleep 10 |----run 5|sleep 10 | taskB ...|------run 5|timer 19|--run 5|timer 19 |run 5|timer 19 |run 5|timer 19 |run 5|timer 19 |run 5|timer 19 |run 5|timer 19 |run 5|timer 19 |run 5|timer 19 | |<------19---------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->|<------19---------->| We can see that task B period stays to 19 even if the run event is delayed because of scheduling preemption whereas the period of task A starts at 15 but increases to 19 because of the scheduling delay. "unique" timer device: When a thread that uses a timer is instanciated, the timer will be shared across the thread instance which disturbs the original sequence. In order to have 1 timer per instance, you can use the "unique" prefix so a new timer will be created for each instance. Uniqueness applies to the thread boundary which means that using the same unique name in the sequence of a thread will refer to the same timer like the example below: "phases" : { "light" : { "loop" : 10, "run" : 1000, "timer" : { "ref" : "unique", "period" : 30000 } }, "heavy" : { "loop" : 10, "run" : 4000, "timer" : { "ref" : "unique", "period" : 30000 } } } If you want to refer to different timer you must use different name like below: "phases" : { "light" : { "loop" : 10, "run" : 1000, "timer" : { "ref" : "uniqueA", "period" : 30000 } }, "heavy" : { "loop" : 10, "run" : 4000, "timer" : { "ref" : "uniqueB", "period" : 400000 } } } Timers can work with a "relative" or an "absolute" reference. By default they work in "relative" mode, but this mode can also be explicity specified as the following: "phases" : { "phase0" : { "loop" : 10, "run0" : 10000, "timer0" : { "ref" : "unique", "period" : 20000, "mode" : "relative" }, } "relative" mode means that the reference for setting the next timer event is relative to the end of the current phase. This in turn means that if, for some reason (i.e., clock frequency was too low), events in a certain phase took too long to execute and the timer of that phase couldn't actually fire at all, the next phase won't be affected. For example: +---- + +-----+ +-------------------+-----+ +--- |r0 | |r0 | |r0 |r0 | |r0 | | | | | | | | o-----------o-----------o-------------------o-----------o-------> 0 10 20 30 40 50 60 70 80 100 120 ^ ^ ^ ^ | | | MISS! | + + + + Timer0 Timer0 Timer0 Timer0 In this example character "o" denotes when phases finish/start. Third activation of Timer0 is missed, since r0 executed for more that 20ms. However the next phase is not affected as Timer0 was set considering the instant of time when the misbehaving r0 finished executing. "absolute" mode is specified as the following: "phases" : { "phase0" : { "loop" : 10, "run0" : 10000, "timer0" : { "ref" : "unique", "period" : 20000, "mode" : "absolute" }, } "absolute" mode means that the reference for setting the next timer event is fixed and always consider the starting time of the first phase. This means that if, for some reason (i.e., clock frequency was too low), events in a certain phase took too long to execute and the timer of that phase couldn't actually fire at all, the next phase (and potentially other subsequent phases) _will_ be affected. For example, considering again the example above: +---- + +-----+ +-------------------+-----+-----+ +--- |r0 | |r0 | |r0 |r0 |r0 | |r0 | | | | | | | | | o-----------o-----------o-------------------o-----o---------o----> 0 10 20 30 40 50 60 70 80 100 120 ^ ^ ^ ^ ^ | | | MISS! | MISS! | + + + + + Timer0 Timer0 Timer0 Timer0 Timer0 Third activation of Timer0 is missed, since r0 executed for more that 20ms. Even if 4th activation of r0 executes for 10ms (as specified in the configuration), 4th Timer0 is still missed because the reference didn't change. In this example 5th activation of r0 then managed to recover, but in general it depends on how badly a certain phase misbehaves. * lock : String. Lock the mutex defined by the string value. * unlock : String. Unlock the mutex defined by the string value. * wait : Object {"ref" : String, "mutex" : String }. Block the calling thread until another thread sends a wake up signal to the resource defined by "ref". The mutex defined by "mutex" is used during the block sequence: See pthread_cond_wait() for more details about the sequence (especially regarding the use of the mutex). * signal : String. Send a wake up signal to the resource defined by the string. The 1st thread in the wait list will be wokn up. See pthread_cond_signal() for more details. * broad : String. Send a wake up signal to the resource defined by the string. All threads that are blocked on the resource wil wake up. See pthread_cond_broadcast() for more details. * sync : Object {"ref" : String, "mutex" : String }. Atomically wakes up a blocked thread and then blocks the calling thread on the condition. taskA ...|run 5|wait|------------|run 5| wait |---------------- taskB ...-------------------|sync|-------- The sync event "sync" : {"ref" : "CondA", "mutex" : "mutexA" } generates the following sequence: { "lock" : "mutexA", "signal" : "CondA", "wait" : { "ref" : "condA", "mutex" : "mutexA" }, "unlock" : "mutexA" } * barrier : String. Used as at least a pair where the name must match. Any number of matching uses will cause all threads hitting the barrier event to wait for a signal. The number of users is recorded, so that when the last user hits the barrier event, that thread will broadcast and continue to the next step. This is conceptually exactly the same as a pthread_barrier_wait operation however, using a pthread_barrier would impose some strict conditions on usage around thread cleanups - primarily that you cannot cancel an in-progress barrier operation which would mean that we have to restrict cleanup to only be possible at the end of a loop cycle (i.e. all phases are complete). This would be too restrictive for most uses so here we use an alternative. In this implementation, the barrier event manages its own mutex and uses a variable shared between users to track waiting tasks, protected by the mutex. You must use a unique name for each sync event since the number of users is taken from the number of references to the name in the input json. i.e. each name must represent a single sync point, and be shared amongst all threads which wish to syncronise at that point. The barrier event "barrier" : "SyncPointA" generates the following sequence: { "lock" : "SyncPointA" (internal mutex), If the shared variable is 0: "signal" : "SyncPointA" (internal condvar), Else: decrement the shared variable, "wait" : { "ref" : "SyncPointA" (internal condvar), "mutex" : "SyncPointA" (internal mutex) }, increment the shared variable, "unlock" : "SyncPointA" (internal mutex) } * suspend : String. Block the calling thread until another thread wakes it up with resume. The String can be let empty as it will be filled by workgen with the right thread's name before starting the use case. * resume : String. Wake up the thread defined by the string. taskA ...|run 5|suspend |----------------|run 5|suspend |---------------- taskB ...-------------------|resume taskA|run 10 |-------------------- * yield: String. Calls pthread_yield(), freeing the CPU for other tasks. This has a special meaning for SCHED_DEADLINE tasks. String can be empty. **** Trace and Log **** Some traces and log hooks have been added to ease the debug and monitor various metrics of the use cases *** Trace *** A trace can be inserted into ftrace buffer for each event in order to sync kernel events like sched_switch with the use case's sequence. A simple example of ftrace log: thread0-23345 [003] 115591.560884: tracing_mark_write: [0] starts thread0-23345 [003] 115591.560890: tracing_mark_write: [0] begins loop 0 phase 0 step 0 thread0-23345 [003] 115591.560894: tracing_mark_write: [0] executing 0 thread0-23345 [003] 115591.580212: tracing_mark_write: [0] executing 1 thread0-23345 [003] 115591.580217: sched_switch: prev_comm=thread0 prev_pid=23345 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 -0 [003] 115591.670198: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=thread0 next_pid=23345 next_prio=120 thread0-23345 [003] 115591.670243: tracing_mark_write: [0] end loop 0 phase 0 step 0 thread0-23345 [003] 115591.670251: tracing_mark_write: [0] begins loop 0 phase 0 step 1 thread0-23345 [003] 115591.670254: tracing_mark_write: [0] executing 0 thread0-23345 [003] 115591.688081: tracing_mark_write: [0] executing 1 thread0-23345 [003] 115591.688085: sched_switch: prev_comm=thread0 prev_pid=23345 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 -0 [003] 115591.778063: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=thread0 next_pid=23345 next_prio=120 thread0-23345 [003] 115591.778108: tracing_mark_write: [0] end loop 0 phase 0 step 1 thread0-23345 [003] 115591.778116: tracing_mark_write: [0] begins loop 0 phase 0 step 2 thread0-23345 [003] 115591.778119: tracing_mark_write: [0] executing 0 thread0-23345 [003] 115591.794619: tracing_mark_write: [0] executing 1 thread0-23345 [003] 115591.794623: sched_switch: prev_comm=thread0 prev_pid=23345 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 -0 [003] 115591.884607: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=thread0 next_pid=23345 next_prio=120 thread0-23345 [003] 115591.884652: tracing_mark_write: [0] end loop 0 phase 0 step 2 ... thread0-23345 [003] 115593.394022: tracing_mark_write: [0] begins loop 0 phase 0 step 17 thread0-23345 [003] 115593.394025: tracing_mark_write: [0] executing 0 thread0-23345 [003] 115593.410583: tracing_mark_write: [0] executing 1 thread0-23345 [003] 115593.410594: sched_switch: prev_comm=thread0 prev_pid=23345 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 -0 [003] 115593.500567: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=thread0 next_pid=23345 next_prio=120 thread0-23345 [003] 115593.500612: tracing_mark_write: [0] end loop 0 phase 0 step 17 thread0-23345 [003] 115593.500620: tracing_mark_write: [0] begins loop 0 phase 0 step 18 thread0-23345 [003] 115593.500623: tracing_mark_write: [0] executing 0 thread0-23345 [003] 115593.520198: tracing_mark_write: [0] executing 1 thread0-23345 [003] 115593.520202: sched_switch: prev_comm=thread0 prev_pid=23345 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 -0 [003] 115593.610185: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=thread0 next_pid=23345 next_prio=120 thread0-23345 [003] 115593.610230: tracing_mark_write: [0] end loop 0 phase 0 step 18 thread0-23345 [003] 115593.610258: tracing_mark_write: [0] exiting The main events are : * "tracing_mark_write: [0] starts" indicates the start of the thread with index 0 * "tracing_mark_write: [0] begins loop A phase B loop C" indicates that the thread with index 0 starts to execute the loop C of the phase B of the main loop A * "tracing_mark_write: [0] executing X": indicates that the thread with index 0 execute the event X of the current phase -"tracing_mark_write: [0] end loop A phase B step C" indicates that the thread with index 0 ends to execute the event C of the phase B of the loop A *"tracing_mark_write: [0] exiting": indiscates that the trhead with index 0 has finished to execute its events. *** Log and gnuplot *** You can log some metrics for each phase that is executed by a thread. These metrics are: - perf: number of loop that has been executed by run events - run: time spent by the thread to execute the run events - period: duration to execute the complte phase - start/end : absolute start and end time of a phase. Same time base is used in ftrace - rel_st: start time of a phase relatively to the beg of the use case - slack: if global option "cumulative_slack" (see above) is false, time between the end of last event and the end of the phase, e.g. taskA ...|-- run5 --|- sleep5 -|-- run5--|..timer20.|-- run5 --|- sleep5 -|-- run6 --|.timer20.| <--------------- period 20 --------------> <--------------- period 20 --------------> <-slack5-> it can also be negative if the execution of a phases' events overshoots the current period, e.g. taskA ...|-- run5 --|- sleep5 -|------- run30 ------xxxxxxxxxx| <--------------- period 20 --------------> if global option "cumulative_slack" is true, all the intermediate slacks of a phase with multiple timers are accumulated and reported when the phase completes - c_duration: sum of the configured duration of run/runtime events - c_period: sum of the timer(s) period(s) - wu_lat: sum of wakeup latencies after timer events Below is an extract of a log: # Policy : SCHED_OTHER priority : 0 #idx perf run period start end rel_st slack c_duration c_period wu_lat 0 92164 19935 98965 504549567051 504549666016 2443 78701 20000 100000 266 0 92164 19408 99952 504549666063 504549766015 101455 80217 20000 100000 265 0 92164 19428 99952 504549766062 504549866014 201454 80199 20000 100000 264 0 92164 19438 99955 504549866060 504549966015 301452 80190 20000 100000 265 0 92164 19446 99952 504549966061 504550066013 401453 80093 20000 100000 264 0 92164 19415 99953 504550066060 504550166013 501452 80215 20000 100000 263 0 92164 19388 99954 504550166059 504550266013 601451 80242 20000 100000 264 0 92164 19444 99956 504550266060 504550366015 701452 80185 20000 100000 265 Some gnuplot files are also created to generate charts based on the log files for each thread and for each kind of metrics. The format of the chart that will be generated by gnuplot is eps (text art has been used for the chart below) Measured thread0 Loop stats Load [nb loop] 120000 ++--+----+---+----+---+---+----+---+----+--++ 506000 load ****** + + + + + + + + + + + run ###### |$$ : : : : $$$ : : : : | period $$$$$$ 110000 ++.$$$........$$$$...$...$..........$$$.$$$$+ | : $$$$$$$$ $$ $: $ $$$$$$$$ $: $ | : : : : $ : :$ : : : ++ 504000 | : : : : : : : : : | 100000 ++.........................................++ | : : : : : : : : : | | : : : : : : : : : | 90000 ++.........................................++ | : : : : : : : : : ++ 502000 | : : : : : : : : : | 80000 ++.........................................++ | : : : : : : : : : | | : : : : : : : : : | 70000 +******************************************** 500000 | : : : : : : : : : | | : : : : : : : : : | | : : : : : : : : : | 60000 ++.........................................++ | : : : : : : : : : | | : : : : : : : : : ++ 498000 50000 ++.........................................++ | : : : : : : : : : | | : : : : : : : : : | 40000 ++.........................................++ | : : : : : : : : : ++ 496000 |# : : : : : : : : : | | # : : : : ### : : : : | 30000 ++.###.###....####...#...#..........###.####+ | : # :#### ### : # ######## #: # + + + + + + +# + + + + 20000 ++--+----+---+----+---+---+----+---+----+--++ 494000 17560556057560556057560656065606756065606756075607 Loop start time [msec] rt-app-1.0/doc/workgen000077500000000000000000000076601311057016400147330ustar00rootroot00000000000000#!/usr/bin/env python import os import sys import getopt import subprocess import signal def check_unikid_json(infile, outfile, verbose=0): if not os.path.exists(infile): print "WARN: %s does not exist", infile try: fi = open(infile, "r") except IOError: print "WARN: Unable to open %s", infile sys.exit(2) lines = fi.readlines() fi.close() try: fo = open(outfile, "w+") except IOError: print "WARN: Unable to open %s", f sys.exit(2) curid = 1 refcount = 0 idlist = {} myid = [] for myline in lines: if "{" in myline: refcount +=1 myid.append(curid) curid = 1 idlist[refcount] = {} if "}" in myline: del idlist[refcount] curid = myid.pop() refcount -=1 try: key_id, value = myline.split(":", 1) except ValueError: fo.write(myline) continue key_id = key_id.strip('\"\t\n\r ') value = value.strip(',\"\t\n\r ') if key_id in idlist[refcount]: newkey_id = key_id + str(curid) while newkey_id in idlist[refcount]: curid +=1 newkey_id = key_id + str(curid) if verbose: print "level ", refcount, " : key ", key_id, " changed into ", newkey_id myline = myline.replace(key_id, newkey_id, 1) key_id = newkey_id idlist[refcount][key_id] = value fo.write(myline) fo.close() return def check_suspend_json(infile, outfile, verbose=0): if not os.path.exists(infile): print "WARN: %s does not exist", infile try: fi = open(infile, "r") except IOError: print "WARN: Unable to open %s", infile sys.exit(2) lines = fi.readlines() fi.close() try: fo = open(outfile, "w+") except IOError: print "WARN: Unable to open %s", f sys.exit(2) taskobj = 0 curid = "" for myline in lines: exception = 0 key_id = "exception" try: key_id, value = myline.split(":", 1) except ValueError: if "suspend" in myline: key_id = "suspend" exception = 1 key_id = key_id.strip('\"\t\n\r ') if not "tasks" in key_id and \ taskobj == 0: fo.write(myline) continue if "{" in myline: taskobj += 1 if taskobj == 2: curid = key_id if "}" in myline: taskobj -= 1 if "suspend" in key_id and \ exception == 1: if verbose: print "value ", curid, " added to suspend key" if "," in myline: myline = myline.replace(",", " : " + "\"" + curid + "\",", 1) else: myline = myline.replace("\n", " : " + "\"" + curid + "\"\n", 1) fo.write(myline) fo.close() return if __name__ == '__main__': def handleSigTERM(signum, frame): sys.exit signal.signal(signal.SIGTERM, handleSigTERM) signal.signal(signal.SIGINT, handleSigTERM) outfile = "unikid.json" selfupdate = 0 verbose = 0 dry_run = False try: opts, args = getopt.getopt(sys.argv[1:], "o:avd") except getopt.GetoptError as err: print str(err) # will print something like "option -a not recognized" sys.exit(2) for o, a in opts: if o == "-o": outfile = a if o == "-a": selfupate = 1 if o == "-v": verbose = 1 if o == "-d": dry_run = True for f in args: if selfupdate: outfile = f check_suspend_json(f, outfile) check_unikid_json(outfile, outfile) if not dry_run: subprocess.call(["rt-app", outfile]) rt-app-1.0/libdl/000077500000000000000000000000001311057016400136415ustar00rootroot00000000000000rt-app-1.0/libdl/COPYING000066400000000000000000000433351311057016400147040ustar00rootroot00000000000000The following is the original GNU GPL v2 under which schedtool is released. schedtool is (c) by Freek, 2004, 2005, 2006, 2007 -- -- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. 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) 19yy 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy 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 Library General Public License instead of this License. rt-app-1.0/libdl/Makefile.am000066400000000000000000000001101311057016400156650ustar00rootroot00000000000000noinst_LIBRARIES = libdl.a libdl_a_SOURCES= dl_syscalls.c dl_syscalls.h rt-app-1.0/libdl/Makefile.orig000066400000000000000000000021661311057016400162450ustar00rootroot00000000000000# go on and adjust here if you don't like those flags CFLAGS=-Os -s -pipe -DDEBUG #CFLAGS=-Wall -Os -fomit-frame-pointer -s -pipe -DDEBUG CC=$(CROSS_COMPILE)gcc # likewise, if you want to change the destination prefix DESTPREFIX=/usr/local DESTDIR=lib TARGET=libdl DOCS=COPYING README RELEASE=$(shell basename `pwd`) all: static shared clean: rm -f *.o $(TARGET).so* $(TARGET).a distclean: clean rm -f *~ *.s install: all install -d $(DESTPREFIX)/$(DESTDIR) install -c $(TARGET) $(DESTPREFIX)/$(DESTDIR) install-doc: install -d $(DESTPREFIX)/share/doc/libdl install -c $(DOCS) $(DESTPREFIX)/share/doc/libdl release: distclean release_gz release_bz2 @echo --- $(RELEASE) released --- release_gz: distclean @echo Building tar.gz ( cd .. ; tar czf $(RELEASE).tar.gz $(RELEASE) ) release_bz2: distclean @echo Building tar.bz2 ( cd .. ; tar cjf $(RELEASE).tar.bz2 $(RELEASE) ) static: $(TARGET).o $(AR) rcs $(TARGET).a $(TARGET).o shared: $(TARGET).o $(CC) -shared -Wl,-soname,$(TARGET).so.1 -o $(TARGET).so.1.0.0 $(TARGET).o $(TARGET).o: dl_syscalls.h dl_syscalls.c $(CC) -fPIC $(CFLAGS) -c dl_syscalls.c -o $(TARGET).o rt-app-1.0/libdl/README000066400000000000000000000024511311057016400145230ustar00rootroot00000000000000Libdl Copyright (C) 2009-2010 Dario Faggioli Release under GPL, version 2 (see COPYING) Use at your own risk. ABOUT ----- Libdl provides what is needed to use the SCHED_DEADLINE scheduling policy and all its features from an userspace program. This is needed since the new system calls, flags, etc. that make it possible are not (yet? :-P) included in the standard library. PREREQUISITES ------------- The new interface is only implemented by kernels patched to support the SCHED_DEADLINE schedulign policy. Instructions on how to download and compile such a kernel are available on the development Website of the project: http://gitorious.org/sched_deadline/pages/Home USAGE ----- To compile the library as a static or shared object: $> make static or $> make shared or just $> make To install in the default path (/usr/local/lib): #> make install To customize the install path change DESTPREFIX and DESTDIR in the Makefile. To statically include the files in your project: - copy dl_syscalls.h and dl_syscalls.c in your header and source folder; - edit your Makefile so that targets where the new interface is used depends on dl_syscall.c; - compile and install it as usual. CONTACT ------- For bug reporting, as well as for any other comment, feel free to contact me at raistlin@linux.it. rt-app-1.0/libdl/dl_syscalls.c000066400000000000000000000005541311057016400163250ustar00rootroot00000000000000#include "dl_syscalls.h" int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags) { return syscall(__NR_sched_setattr, pid, attr, flags); } int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags) { return syscall(__NR_sched_getattr, pid, attr, size, flags); } rt-app-1.0/libdl/dl_syscalls.h000066400000000000000000000040471311057016400163330ustar00rootroot00000000000000/* * Libdl * (C) Dario Faggioli , 2009, 2010 * * 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 version 2 of the License. * * 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 (COPYING file) for more details. * */ #ifndef __DL_SYSCALLS__ #define __DL_SYSCALLS__ #include #define _GNU_SOURCE #include #include #include #include #define SCHED_DEADLINE 6 /* XXX use the proper syscall numbers */ /* __NR_sched_setattr number */ #ifndef __NR_sched_setattr #ifdef __x86_64__ #define __NR_sched_setattr 314 #endif #ifdef __i386__ #define __NR_sched_setattr 351 #endif #ifdef __arm__ #define __NR_sched_setattr 380 #endif #ifdef __aarch64__ #define __NR_sched_setattr 274 #endif #endif /* __NR_sched_getattr number */ #ifndef __NR_sched_getattr #ifdef __x86_64__ #define __NR_sched_getattr 315 #endif #ifdef __i386__ #define __NR_sched_getattr 352 #endif #ifdef __arm__ #define __NR_sched_getattr 381 #endif #ifdef __aarch64__ #define __NR_sched_getattr 275 #endif #endif #define SF_SIG_RORUN 2 #define SF_SIG_DMISS 4 #define SF_BWRECL_DL 8 #define SF_BWRECL_RT 16 #define SF_BWRECL_OTH 32 #define RLIMIT_DLDLINE 16 #define RLIMIT_DLRTIME 17 struct sched_attr { __u32 size; __u32 sched_policy; __u64 sched_flags; /* SCHED_NORMAL, SCHED_BATCH */ __s32 sched_nice; /* SCHED_FIFO, SCHED_RR */ __u32 sched_priority; /* SCHED_DEADLINE */ __u64 sched_runtime; __u64 sched_deadline; __u64 sched_period; }; int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags); int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags); #endif /* __DL_SYSCALLS__ */ rt-app-1.0/src/000077500000000000000000000000001311057016400133425ustar00rootroot00000000000000rt-app-1.0/src/Makefile.am000066400000000000000000000005711311057016400154010ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = -I$(srcdir)/../libdl/ bin_PROGRAMS = rt-app rt_app_SOURCES= rt-app_types.h rt-app_args.h rt-app_utils.h rt-app_utils.c rt-app_args.c rt-app.h rt-app.c rt_app_SOURCES += rt-app_parse_config.h rt-app_parse_config.c rt_app_LDADD = $(QRESLIB) if SET_DLSCHED rt_app_LDADD += ../libdl/libdl.a endif dist_bin_SCRIPTS = $(srcdir)/../doc/workgen rt-app-1.0/src/rt-app.c000066400000000000000000000664521311057016400147260ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ /* for CPU_SET macro */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include /* for memlock */ #include "config.h" #include "rt-app_utils.h" #include "rt-app_args.h" static int errno; static volatile sig_atomic_t continue_running; static pthread_t *threads; static int nthreads; static volatile sig_atomic_t running_threads; static int p_load; rtapp_options_t opts; static struct timespec t_zero; static pthread_barrier_t threads_barrier; static ftrace_data_t ft_data = { .debugfs = "/sys/kernel/debug", .marker_fd = -1, }; /* * Function: to do some useless operation. * TODO: improve the waste loop with more heavy functions */ void waste_cpu_cycles(unsigned long long load_loops) { double param, result; double n; unsigned long long i; param = 0.95; n = 4; for (i = 0 ; i < load_loops ; i++) { result = ldexp(param , (ldexp(param , ldexp(param , n)))); result = ldexp(param , (ldexp(param , ldexp(param , n)))); result = ldexp(param , (ldexp(param , ldexp(param , n)))); result = ldexp(param , (ldexp(param , ldexp(param , n)))); } return; } /* * calibrate_cpu_cycles_1() * 1st method to calibrate the ns per loop value * We alternate idle period and run period in order to not trig some hw * protection mechanism like thermal mitgation */ int calibrate_cpu_cycles_1(int clock) { struct timespec start, stop, sleep; int max_load_loop = 10000; unsigned int diff; int nsec_per_loop, avg_per_loop = 0; int ret, cal_trial = 1000; while (cal_trial) { cal_trial--; sleep.tv_sec = 1; sleep.tv_nsec = 0; clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, NULL); clock_gettime(clock, &start); waste_cpu_cycles(max_load_loop); clock_gettime(clock, &stop); diff = (int)timespec_sub_to_ns(&stop, &start); nsec_per_loop = diff / max_load_loop; avg_per_loop = (avg_per_loop + nsec_per_loop) >> 1; /* collect a critical mass of samples.*/ if ((abs(nsec_per_loop - avg_per_loop) * 50) < avg_per_loop) return avg_per_loop; /* * use several loop duration in order to be sure to not * fall into a specific platform loop duration *(like the cpufreq period) */ /*randomize the number of loops and recheck 1000 times*/ max_load_loop += 33333; max_load_loop %= 1000000; } return 0; } /* * calibrate_cpu_cycles_2() * 2nd method to calibrate the ns per loop value * We continously runs something to ensure that CPU is set to max freq by the * governor */ int calibrate_cpu_cycles_2(int clock) { struct timespec start, stop, sleep; int max_load_loop = 10000; unsigned int diff; int nsec_per_loop, avg_per_loop = 0; int ret, cal_trial = 1000; while (cal_trial) { cal_trial--; clock_gettime(clock, &start); waste_cpu_cycles(max_load_loop); clock_gettime(clock, &stop); diff = (int)timespec_sub_to_ns(&stop, &start); nsec_per_loop = diff / max_load_loop; avg_per_loop = (avg_per_loop + nsec_per_loop) >> 1; /* collect a critical mass of samples.*/ if ((abs(nsec_per_loop - avg_per_loop) * 50) < avg_per_loop) return avg_per_loop; /* * use several loop duration in order to be sure to not * fall into a specific platform loop duration *(like the cpufreq period) */ /*randomize the number of loops and recheck 1000 times*/ max_load_loop += 33333; max_load_loop %= 1000000; } return 0; } /* * calibrate_cpu_cycles() * Use several methods to calibrate the ns per loop and get the min value which * correspond to the highest achievable compute capacity. */ int calibrate_cpu_cycles(int clock) { int calib1, calib2; /* Run 1st method */ calib1 = calibrate_cpu_cycles_1(clock); /* Run 2nd method */ calib2 = calibrate_cpu_cycles_2(clock); if (calib1 < calib2) return calib1; else return calib2; } static inline unsigned long loadwait(unsigned long exec) { unsigned long load_count; unsigned long secs; int i; /* * If exec is still too big, let's run it in bursts * so that we don't overflow load_count. */ secs = exec / 1000000; for (i = 0; i < secs; i++) { load_count = 1000000000/p_load; waste_cpu_cycles(load_count); exec -= 1000000; } /* * Run for the remainig exec (if any). */ load_count = (exec * 1000)/p_load; waste_cpu_cycles(load_count); return load_count; } static void ioload(unsigned long count, struct _rtapp_iomem_buf *iomem, int io_fd) { ssize_t ret; while (count != 0) { unsigned long size; if (count > iomem->size) size = iomem->size; else size = count; ret = write(io_fd, iomem->ptr, size); if (ret == -1) { perror("write"); return; } count -= ret; } } static void memload(unsigned long count, struct _rtapp_iomem_buf *iomem) { while (count > 0) { unsigned long size; if (count > iomem->size) size = iomem->size; else size = count; memset(iomem->ptr, 0, size); count -= size; } } static int run_event(event_data_t *event, int dry_run, unsigned long *perf, rtapp_resource_t *resources, struct timespec *t_first, log_data_t *ldata) { rtapp_resource_t *rdata = &(resources[event->res]); rtapp_resource_t *ddata = &(resources[event->dep]); unsigned long lock = 0; switch(event->type) { case rtapp_lock: log_debug("lock %s ", rdata->name); pthread_mutex_lock(&(rdata->res.mtx.obj)); lock = 1; break; case rtapp_unlock: log_debug("unlock %s ", rdata->name); pthread_mutex_unlock(&(rdata->res.mtx.obj)); lock = -1; break; } if (dry_run) return lock; switch(event->type) { case rtapp_wait: log_debug("wait %s ", rdata->name); pthread_cond_wait(&(rdata->res.cond.obj), &(ddata->res.mtx.obj)); break; case rtapp_signal: log_debug("signal %s ", rdata->name); pthread_cond_signal(&(rdata->res.cond.obj)); break; case rtapp_barrier: log_debug("barrier %s ", rdata->name); pthread_mutex_lock(&(rdata->res.barrier.m_obj)); if (rdata->res.barrier.waiting == 0) { /* everyone is already waiting, signal */ pthread_cond_broadcast(&(rdata->res.barrier.c_obj)); } else { /* not everyone is waiting, mark then wait */ rdata->res.barrier.waiting -= 1; pthread_cond_wait(&(rdata->res.barrier.c_obj), &(rdata->res.barrier.m_obj)); rdata->res.barrier.waiting += 1; } pthread_mutex_unlock(&(rdata->res.barrier.m_obj)); break; case rtapp_sig_and_wait: log_debug("signal and wait %s", rdata->name); pthread_cond_signal(&(rdata->res.cond.obj)); pthread_cond_wait(&(rdata->res.cond.obj), &(ddata->res.mtx.obj)); break; case rtapp_broadcast: pthread_cond_broadcast(&(rdata->res.cond.obj)); break; case rtapp_sleep: { struct timespec sleep = usec_to_timespec(event->duration); log_debug("sleep %d ", event->duration); nanosleep(&sleep, NULL); } break; case rtapp_run: { struct timespec t_start, t_end; log_debug("run %d ", event->duration); ldata->c_duration += event->duration; clock_gettime(CLOCK_MONOTONIC, &t_start); *perf += loadwait(event->duration); clock_gettime(CLOCK_MONOTONIC, &t_end); t_end = timespec_sub(&t_end, &t_start); ldata->duration += timespec_to_usec(&t_end); } break; case rtapp_runtime: { struct timespec t_start, t_end; int64_t diff_ns; log_debug("runtime %d ", event->duration); ldata->c_duration += event->duration; clock_gettime(CLOCK_MONOTONIC, &t_start); do { /* Do work for 32usec */ *perf += loadwait(32); clock_gettime(CLOCK_MONOTONIC, &t_end); diff_ns = timespec_sub_to_ns(&t_end, &t_start); } while ((diff_ns / 1000) < event->duration); t_end = timespec_sub(&t_end, &t_start); ldata->duration += timespec_to_usec(&t_end); } break; case rtapp_timer: { struct timespec t_period, t_now, t_wu, t_slack; log_debug("timer %d ", event->duration); t_period = usec_to_timespec(event->duration); ldata->c_period += event->duration; if (rdata->res.timer.init == 0) { rdata->res.timer.init = 1; rdata->res.timer.t_next = *t_first; } rdata->res.timer.t_next = timespec_add(&rdata->res.timer.t_next, &t_period); clock_gettime(CLOCK_MONOTONIC, &t_now); t_slack = timespec_sub(&rdata->res.timer.t_next, &t_now); if (opts.cumulative_slack) ldata->slack += timespec_to_usec_long(&t_slack); else ldata->slack = timespec_to_usec_long(&t_slack); if (timespec_lower(&t_now, &rdata->res.timer.t_next)) { clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rdata->res.timer.t_next, NULL); clock_gettime(CLOCK_MONOTONIC, &t_now); t_wu = timespec_sub(&t_now, &rdata->res.timer.t_next); ldata->wu_latency += timespec_to_usec(&t_wu); } else { if (rdata->res.timer.relative) clock_gettime(CLOCK_MONOTONIC, &rdata->res.timer.t_next); ldata->wu_latency = 0UL; } } break; case rtapp_suspend: { log_debug("suspend %s ", rdata->name); pthread_mutex_lock(&(ddata->res.mtx.obj)); pthread_cond_wait(&(rdata->res.cond.obj), &(ddata->res.mtx.obj)); pthread_mutex_unlock(&(ddata->res.mtx.obj)); break; } case rtapp_resume: { log_debug("resume %s ", rdata->name); pthread_mutex_lock(&(ddata->res.mtx.obj)); pthread_cond_broadcast(&(rdata->res.cond.obj)); pthread_mutex_unlock(&(ddata->res.mtx.obj)); break; } case rtapp_mem: { log_debug("mem %d", event->count); memload(event->count, &rdata->res.buf); } break; case rtapp_iorun: { log_debug("iorun %d", event->count); ioload(event->count, &rdata->res.buf, ddata->res.dev.fd); } break; case rtapp_yield: { log_debug("yield %d", event->count); pthread_yield(); } break; } return lock; } int run(int ind, phase_data_t *pdata, rtapp_resource_t *resources, struct timespec *t_first, log_data_t *ldata) { event_data_t *events = pdata->events; int nbevents = pdata->nbevents; int i, lock = 0; unsigned long perf = 0; for (i = 0; i < nbevents; i++) { if (!continue_running && !lock) return perf; log_debug("[%d] runs events %d type %d ", ind, i, events[i].type); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] executing %d", ind, i); lock += run_event(&events[i], !continue_running, &perf, resources, t_first, ldata); } return perf; } static void shutdown(int sig) { int i; if(!continue_running) return; /* notify threads, join them, then exit */ continue_running = 0; /* Force wake up of all waiting threads */ for (i = 0; i < opts.nresources; i++) { if (opts.resources[i].type == rtapp_wait) { pthread_cond_broadcast(&opts.resources[i].res.cond.obj); } if (opts.resources[i].type == rtapp_barrier) { pthread_cond_broadcast(&opts.resources[i].res.barrier.c_obj); } } /* wait up all waiting threads */ for (i = 0; i < running_threads; i++) { pthread_join(threads[i], NULL); } if (opts.ftrace) { log_ftrace(ft_data.marker_fd, "main ends\n"); log_notice("deconfiguring ftrace"); close(ft_data.marker_fd); } exit(EXIT_SUCCESS); } static void create_cpuset_str(cpuset_data_t *cpu_data) { unsigned int cpu_count = CPU_COUNT_S(cpu_data->cpusetsize, cpu_data->cpuset); unsigned int i; unsigned int idx = 0; /* * Assume we can go up to 9999 cpus. Each cpu would take up to 4 + 2 * bytes (4 for the number and 2 for the comma and space). 2 bytes * for beginning bracket + space and 2 bytes for end bracket and space * and finally null-terminator. */ unsigned int size_needed = cpu_count * 6 + 2 + 2 + 1; if (cpu_count > 9999) { log_error("Too many cpus specified. Up to 9999 cpus supported"); exit(EXIT_FAILURE); } cpu_data->cpuset_str = malloc(size_needed); strcpy(cpu_data->cpuset_str, "[ "); idx += 2; for (i = 0; i < 10000 && cpu_count; ++i) { unsigned int n; if (CPU_ISSET(i, cpu_data->cpuset)) { --cpu_count; if (size_needed <= (idx + 1)) { log_error("Not enough memory for array"); exit(EXIT_FAILURE); } n = snprintf(&cpu_data->cpuset_str[idx], size_needed - idx - 1, "%d", i); if (n > 0) { idx += n; } else { log_error("Error creating array"); exit(EXIT_FAILURE); } if (size_needed <= (idx + 1)) { log_error("Not enough memory for array"); exit(EXIT_FAILURE); } if (cpu_count) { strncat(cpu_data->cpuset_str, ", ", size_needed - idx - 1); idx += 2; } } } strncat(cpu_data->cpuset_str, " ]", size_needed - idx - 1); } static void set_thread_affinity(thread_data_t *data, cpuset_data_t *cpu_data) { int ret; cpuset_data_t *actual_cpu_data = &data->cpu_data; if (data->def_cpu_data.cpuset == NULL) { /* Get default affinity */ cpu_set_t cpuset; unsigned int cpu_count; unsigned int cpu = 0; ret = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); if (ret != 0) { errno = ret; perror("pthread_get_affinity"); exit(EXIT_FAILURE); } cpu_count = CPU_COUNT(&cpuset); data->def_cpu_data.cpusetsize = CPU_ALLOC_SIZE(cpu_count); data->def_cpu_data.cpuset = CPU_ALLOC(cpu_count); memcpy(data->def_cpu_data.cpuset, &cpuset, data->def_cpu_data.cpusetsize); create_cpuset_str(&data->def_cpu_data); data->curr_cpu_data = &data->def_cpu_data; } /* * Order of preference: * 1. Phase cpuset * 2. Task level cpuset * 3. Default cpuset */ if (cpu_data->cpuset != NULL) actual_cpu_data = cpu_data; if (actual_cpu_data->cpuset == NULL) actual_cpu_data = &data->def_cpu_data; if (!CPU_EQUAL(actual_cpu_data->cpuset, data->curr_cpu_data->cpuset)) { log_debug("[%d] setting cpu affinity to CPU(s) %s", data->ind, actual_cpu_data->cpuset_str); ret = pthread_setaffinity_np(pthread_self(), actual_cpu_data->cpusetsize, actual_cpu_data->cpuset); if (ret != 0) { errno = ret; perror("pthread_setaffinity_np"); exit(EXIT_FAILURE); } data->curr_cpu_data = actual_cpu_data; } } void *thread_body(void *arg) { thread_data_t *data = (thread_data_t*) arg; phase_data_t *pdata; log_data_t ldata; struct sched_param param; struct timespec t_start, t_end, t_first; unsigned long t_start_usec; long slack; timing_point_t *curr_timing; timing_point_t *timings; timing_point_t tmp_timing; unsigned int timings_size, timing_loop; pid_t tid; struct sched_attr attr; unsigned int flags = 0; int ret, i, j, loop, idx; /* Set thread name */ ret = pthread_setname_np(pthread_self(), data->name); if (ret != 0) { perror("pthread_setname_np thread name over 16 characters"); } /* Get the 1st phase's data */ pdata = &data->phases[0]; /* Set scheduling policy and print pretty info on stdout */ log_notice("[%d] Using %s policy with priority %d", data->ind, data->sched_policy_descr, data->sched_prio); switch (data->sched_policy) { case rr: case fifo: fprintf(data->log_handler, "# Policy : %s priority : %d\n", (data->sched_policy == rr ? "SCHED_RR" : "SCHED_FIFO"), data->sched_prio); param.sched_priority = data->sched_prio; ret = pthread_setschedparam(pthread_self(), data->sched_policy, ¶m); if (ret != 0) { errno = ret; perror("pthread_setschedparam"); exit(EXIT_FAILURE); } break; case other: fprintf(data->log_handler, "# Policy : SCHED_OTHER priority : %d\n", data->sched_prio); if (data->sched_prio > 19 || data->sched_prio < -20) { log_critical("[%d] setpriority " "%d nice invalid. " "Valid between -20 and 19", data->ind, data->sched_prio); exit(EXIT_FAILURE); } if (data->sched_prio) { ret = setpriority(PRIO_PROCESS, 0, data->sched_prio); if (ret != 0) { log_critical("[%d] setpriority" "returned %d", data->ind, ret); errno = ret; perror("setpriority"); exit(EXIT_FAILURE); } } data->lock_pages = 0; /* forced off for SCHED_OTHER */ break; #ifdef DLSCHED case deadline: fprintf(data->log_handler, "# Policy : SCHED_DEADLINE\n"); tid = gettid(); attr.size = sizeof(attr); attr.sched_flags = 0; attr.sched_policy = SCHED_DEADLINE; attr.sched_priority = 0; attr.sched_runtime = data->runtime; attr.sched_deadline = data->deadline; attr.sched_period = data->period; log_notice("[%d] period: %lu, exec: %lu, deadline: %lu", data->ind, data->period, data->runtime, data->deadline); break; #endif default: log_error("Unknown scheduling policy %d", data->sched_policy); exit(EXIT_FAILURE); } if (opts.logsize > 0) { timings = malloc(opts.logsize); timings_size = opts.logsize / sizeof(timing_point_t); } else { timings = NULL; timings_size = 0; } timing_loop = 0; /* Lock pages */ if (data->lock_pages == 1) { log_notice("[%d] Locking pages in memory", data->ind); ret = mlockall(MCL_CURRENT | MCL_FUTURE); if (ret < 0) { errno = ret; perror("mlockall"); exit(EXIT_FAILURE); } } if (data->ind == 0) { /* * Only first thread sets t_zero. Other threads sync with this * timestamp. */ clock_gettime(CLOCK_MONOTONIC, &t_zero); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] sets zero time", data->ind); } pthread_barrier_wait(&threads_barrier); t_first = t_zero; log_notice("[%d] starting thread ...\n", data->ind); fprintf(data->log_handler, "%s %8s %8s %8s %15s %15s %15s %10s %10s %10s %10s\n", "#idx", "perf", "run", "period", "start", "end", "rel_st", "slack", "c_duration", "c_period", "wu_lat"); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] starts", data->ind); #ifdef DLSCHED /* TODO find a better way to handle that constraint */ /* * Set the task to SCHED_DEADLINE as far as possible touching its * budget as little as possible for the first iteration. */ if (data->sched_policy == SCHED_DEADLINE) { ret = sched_setattr(tid, &attr, flags); if (ret != 0) { log_critical("[%d] sched_setattr " "returned %d", data->ind, ret); errno = ret; perror("sched_setattr"); exit(EXIT_FAILURE); } } #endif if (data->delay > 0) { struct timespec delay = usec_to_timespec(data->delay); log_debug("initial delay %lu ", data->delay); t_first = timespec_add(&t_first, &delay); clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_first, NULL); } i = j = loop = idx = 0; while (continue_running && (i != data->loop)) { struct timespec t_diff, t_rel_start; set_thread_affinity(data, &pdata->cpu_data); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] begins loop %d phase %d step %d", data->ind, i, j, loop); log_debug("[%d] begins loop %d phase %d step %d", data->ind, i, j, loop);; memset(&ldata, 0, sizeof(ldata)); clock_gettime(CLOCK_MONOTONIC, &t_start); ldata.perf = run(data->ind, pdata, *(data->resources), &t_first, &ldata); clock_gettime(CLOCK_MONOTONIC, &t_end); if (timings) curr_timing = &timings[idx]; else curr_timing = &tmp_timing; t_diff = timespec_sub(&t_end, &t_start); t_rel_start = timespec_sub(&t_start, &data->main_app_start); curr_timing->ind = data->ind; curr_timing->rel_start_time = timespec_to_usec_ull(&t_rel_start); curr_timing->start_time = timespec_to_usec_ull(&t_start); curr_timing->end_time = timespec_to_usec_ull(&t_end); curr_timing->period = timespec_to_usec(&t_diff); curr_timing->duration = ldata.duration; curr_timing->perf = ldata.perf; curr_timing->wu_latency = ldata.wu_latency; curr_timing->slack = ldata.slack; curr_timing->c_period = ldata.c_period; curr_timing->c_duration = ldata.c_duration; if (opts.logsize && !timings && continue_running) log_timing(data->log_handler, curr_timing); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] end loop %d phase %d step %d", data->ind, i, j, loop); loop++; if (loop == pdata->loop) { loop = 0; j++; if (j == data->nphases) { j = 0; i++; } pdata = &data->phases[j]; } idx++; if (idx >= timings_size) { timing_loop = 1; idx = 0; } } param.sched_priority = 0; ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m); if (ret != 0) { errno = ret; perror("pthread_setschedparam"); exit(EXIT_FAILURE); } if (timings) { for (j = idx; timing_loop && (j < timings_size); j++) log_timing(data->log_handler, &timings[j]); for (j = 0; j < idx; j++) log_timing(data->log_handler, &timings[j]); } if (opts.ftrace) log_ftrace(ft_data.marker_fd, "[%d] exiting", data->ind); log_notice("[%d] Exiting.", data->ind); fclose(data->log_handler); pthread_exit(NULL); } int main(int argc, char* argv[]) { struct timespec t_start; FILE *gnuplot_script = NULL; int i, res, nresources; thread_data_t *tdata; rtapp_resource_t *rdata; char tmp[PATH_LENGTH]; static cpu_set_t orig_set; parse_command_line(argc, argv, &opts); /* allocated threads */ nthreads = opts.nthreads; threads = malloc(nthreads * sizeof(pthread_t)); pthread_barrier_init(&threads_barrier, NULL, nthreads); /* install a signal handler for proper shutdown */ signal(SIGQUIT, shutdown); signal(SIGTERM, shutdown); signal(SIGHUP, shutdown); signal(SIGINT, shutdown); /* If using ftrace, open trace and marker fds */ if (opts.ftrace) { log_notice("configuring ftrace"); strcpy(tmp, ft_data.debugfs); strcat(tmp, "/tracing/tracing_on"); strcpy(tmp, ft_data.debugfs); strcat(tmp, "/tracing/trace_marker"); ft_data.marker_fd = open(tmp, O_WRONLY); if (ft_data.marker_fd < 0) { log_error("Cannot open trace_marker file %s", tmp); exit(EXIT_FAILURE); } log_ftrace(ft_data.marker_fd, "main creates threads\n"); } /* Init global running_variable */ continue_running = 1; /* Needs to calibrate 'calib_cpu' core */ if (opts.calib_ns_per_loop == 0) { log_notice("Calibrate ns per loop"); cpu_set_t calib_set; CPU_ZERO(&calib_set); CPU_SET(opts.calib_cpu, &calib_set); sched_getaffinity(0, sizeof(cpu_set_t), &orig_set); sched_setaffinity(0, sizeof(cpu_set_t), &calib_set); p_load = calibrate_cpu_cycles(CLOCK_MONOTONIC); sched_setaffinity(0, sizeof(cpu_set_t), &orig_set); log_notice("pLoad = %dns : calib_cpu %d", p_load, opts.calib_cpu); } else { p_load = opts.calib_ns_per_loop; log_notice("pLoad = %dns", p_load); } /* Take the beginning time for everything */ clock_gettime(CLOCK_MONOTONIC, &t_start); /* Prepare log file of each thread before starting the use case */ for (i = 0; i < nthreads; i++) { tdata = &opts.threads_data[i]; tdata->duration = opts.duration; tdata->main_app_start = t_start; tdata->lock_pages = opts.lock_pages; if (opts.logdir) { snprintf(tmp, PATH_LENGTH, "%s/%s-%s-%d.log", opts.logdir, opts.logbasename, tdata->name, tdata->ind); tdata->log_handler = fopen(tmp, "w"); if (!tdata->log_handler) { log_error("Cannot open logfile %s", tmp); exit(EXIT_FAILURE); } } else { tdata->log_handler = stdout; } } /* Prepare gnuplot files before starting the use case */ if (opts.logdir && opts.gnuplot) { /* gnuplot plot of the period */ snprintf(tmp, PATH_LENGTH, "%s/%s-period.plot", opts.logdir, opts.logbasename); gnuplot_script = fopen(tmp, "w+"); snprintf(tmp, PATH_LENGTH, "%s-period.eps", opts.logbasename); fprintf(gnuplot_script, "set terminal postscript enhanced color\n" "set output '%s'\n" "set grid\n" "set key outside right\n" "set title \"Measured time per loop\"\n" "set xlabel \"Loop start time [usec]\"\n" "set ylabel \"Period Time [usec]\"\n" "plot ", tmp); for (i=0; i 0) { sleep(opts.duration); if (opts.ftrace) log_ftrace(ft_data.marker_fd, "main shutdown\n"); shutdown(SIGTERM); } for (i = 0; i < nthreads; i++) { pthread_join(threads[i], NULL); } if (opts.ftrace) { log_ftrace(ft_data.marker_fd, "main ends\n"); close(ft_data.marker_fd); } exit(EXIT_SUCCESS); exit_err: exit(EXIT_FAILURE); } rt-app-1.0/src/rt-app.h000066400000000000000000000020001311057016400147060ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #ifndef _RT_APP_H_ #define _RT_APP_H_ void *thread_body(void *arg); #endif /* _RT_APP_H_ */ rt-app-1.0/src/rt-app_args.c000066400000000000000000000030231311057016400157230ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #include #include #include #include #include "rt-app_parse_config.h" void usage (const char* msg, int ex_code) { printf("usage:\n" "rt-app \n"); if (msg != NULL) printf("\n%s\n", msg); exit(ex_code); } void parse_command_line(int argc, char **argv, rtapp_options_t *opts) { struct stat config_file_stat; if (argc < 2) usage(NULL, EXIT_SUCCESS); if (stat(argv[1], &config_file_stat) == 0) { parse_config(argv[1], opts); return; } else if (strcmp(argv[1], "-") == 0) { parse_config_stdin(opts); return; } usage(NULL, EXIT_SUCCESS); } rt-app-1.0/src/rt-app_args.h000066400000000000000000000021701311057016400157320ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #ifndef _RTAPP_ARGS_H_ #define _RTAPP_ARGS_H_ #include "rt-app_types.h" void usage (const char* msg, int ex_code); void parse_command_line(int argc, char **argv, rtapp_options_t *opts); #endif // _RTAPP_ARGS_H_ rt-app-1.0/src/rt-app_parse_config.c000066400000000000000000000666061311057016400174460ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ /* for CPU_SET macro */ #define _GNU_SOURCE #include #include #include #include #include "rt-app_utils.h" #include "rt-app_parse_config.h" #define PFX "[json] " #define PFL " "PFX #define PIN PFX" " #define PIN2 PIN" " #define PIN3 PIN2" " #define JSON_FILE_BUF_SIZE 4096 #define DEFAULT_MEM_BUF_SIZE (4 * 1024 * 1024) /* redefine foreach as in but to be ANSI * compatible */ #define foreach(obj, entry, key, val, idx) \ for ( ({ idx = 0; entry = json_object_get_object(obj)->head;}); \ ({ if (entry) { key = (char*)entry->k; \ val = (struct json_object*)entry->v; \ }; \ entry; \ } \ ); \ ({ entry = entry->next; idx++; }) \ ) /* this macro set a default if key not present, or give an error and exit * if key is present but does not have a default */ #define set_default_if_needed(key, value, have_def, def_value) do { \ if (!value) { \ if (have_def) { \ log_info(PIN "key: %s %d", key, def_value);\ return def_value; \ } else { \ log_critical(PFX "Key %s not found", key); \ exit(EXIT_INV_CONFIG); \ } \ } \ } while(0) /* same as before, but for string, for which we need to strdup in the * default value so it can be a literal */ #define set_default_if_needed_str(key, value, have_def, def_value) do { \ if (!value) { \ if (have_def) { \ if (!def_value) { \ log_info(PIN "key: %s NULL", key);\ return NULL; \ } \ log_info(PIN "key: %s %s", \ key, def_value); \ return strdup(def_value); \ } else { \ log_critical(PFX "Key %s not found", key); \ exit(EXIT_INV_CONFIG); \ } \ } \ }while (0) /* get an object obj and check if its type is . If not, print a message * (this is what parent and key are used for) and exit */ static inline void assure_type_is(struct json_object *obj, struct json_object *parent, const char *key, enum json_type type) { if (!json_object_is_type(obj, type)) { log_critical("Invalid type for key %s", key); log_critical("%s", json_object_to_json_string(parent)); exit(EXIT_INV_CONFIG); } } /* search a key (what) in object "where", and return a pointer to its * associated object. If nullable is false, exit if key is not found */ static inline struct json_object* get_in_object(struct json_object *where, const char *what, int nullable) { struct json_object *to; json_bool ret; ret = json_object_object_get_ex(where, what, &to); if (!nullable && !ret) { log_critical(PFX "Error while parsing config\n" PFL); exit(EXIT_INV_CONFIG); } if (!nullable && strcmp(json_object_to_json_string(to), "null") == 0) { log_critical(PFX "Cannot find key %s", what); exit(EXIT_INV_CONFIG); } return to; } static inline int get_int_value_from(struct json_object *where, const char *key, int have_def, int def_value) { struct json_object *value; int i_value; value = get_in_object(where, key, have_def); set_default_if_needed(key, value, have_def, def_value); assure_type_is(value, where, key, json_type_int); i_value = json_object_get_int(value); log_info(PIN "key: %s, value: %d, type ", key, i_value); return i_value; } static inline int get_bool_value_from(struct json_object *where, const char *key, int have_def, int def_value) { struct json_object *value; int b_value; value = get_in_object(where, key, have_def); set_default_if_needed(key, value, have_def, def_value); assure_type_is(value, where, key, json_type_boolean); b_value = json_object_get_boolean(value); log_info(PIN "key: %s, value: %d, type ", key, b_value); return b_value; } static inline char* get_string_value_from(struct json_object *where, const char *key, int have_def, const char *def_value) { struct json_object *value; char *s_value; value = get_in_object(where, key, have_def); set_default_if_needed_str(key, value, have_def, def_value); if (json_object_is_type(value, json_type_null)) { log_info(PIN "key: %s, value: NULL, type ", key); return NULL; } assure_type_is(value, where, key, json_type_string); s_value = strdup(json_object_get_string(value)); log_info(PIN "key: %s, value: %s, type ", key, s_value); return s_value; } static int init_mutex_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { log_info(PIN3 "Init: %s mutex", data->name); pthread_mutexattr_init(&data->res.mtx.attr); if (opts->pi_enabled) { pthread_mutexattr_setprotocol( &data->res.mtx.attr, PTHREAD_PRIO_INHERIT); } pthread_mutex_init(&data->res.mtx.obj, &data->res.mtx.attr); } static int init_timer_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { log_info(PIN3 "Init: %s timer", data->name); data->res.timer.init = 0; data->res.timer.relative = 1; } static int init_cond_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { log_info(PIN3 "Init: %s wait", data->name); pthread_condattr_init(&data->res.cond.attr); pthread_cond_init(&data->res.cond.obj, &data->res.cond.attr); } static int init_membuf_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { log_info(PIN3 "Init: %s membuf", data->name); data->res.buf.ptr = malloc(opts->mem_buffer_size); data->res.buf.size = opts->mem_buffer_size; } static int init_iodev_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { log_info(PIN3 "Init: %s io device", data->name); data->res.dev.fd = open(opts->io_device, O_CREAT | O_WRONLY, 0644); } static int init_barrier_resource(rtapp_resource_t *data, const rtapp_options_t *opts) { log_info(PIN3 "Init: %s barrier", data->name); /* each task waiting for this resource will increment this counter. * start at -1 so that when we see this is zero we are the last man * to enter the sync point and should wake everyone else. */ data->res.barrier.waiting = -1; pthread_mutexattr_init(&data->res.barrier.m_attr); if (opts->pi_enabled) { pthread_mutexattr_setprotocol( &data->res.barrier.m_attr, PTHREAD_PRIO_INHERIT); } pthread_mutex_init(&data->res.barrier.m_obj, &data->res.barrier.m_attr); pthread_cond_init(&data->res.barrier.c_obj, NULL); } static void init_resource_data(const char *name, int type, int idx, const rtapp_options_t *opts) { rtapp_resource_t *data = &(opts->resources[idx]); /* common and defaults */ data->index = idx; data->name = strdup(name); data->type = type; switch (data->type) { case rtapp_mutex: init_mutex_resource(data, opts); break; case rtapp_timer: init_timer_resource(data, opts); break; case rtapp_wait: init_cond_resource(data, opts); break; case rtapp_mem: init_membuf_resource(data, opts); break; case rtapp_iorun: init_iodev_resource(data, opts); break; case rtapp_barrier: init_barrier_resource(data, opts); break; } } static void parse_resource_data(const char *name, struct json_object *obj, int idx, rtapp_resource_t *data, const rtapp_options_t *opts) { char *type; char def_type[RTAPP_RESOURCE_DESCR_LENGTH]; log_info(PFX "Parsing resources %s [%d]", name, idx); /* resource type */ resource_to_string(0, def_type); type = get_string_value_from(obj, "type", TRUE, def_type); if (string_to_resource(type, &data->type) != 0) { log_critical(PIN2 "Invalid type of resource %s", type); exit(EXIT_INV_CONFIG); } /* * get_string_value_from allocate the string so with have to free it * once useless */ free(type); init_resource_data(name, data->type, idx, opts); } static int add_resource_data(const char *name, int type, rtapp_options_t *opts) { int idx; idx = opts->nresources; log_info(PIN2 "Add new resource %s [%d] type %d", name, idx, type); opts->nresources++; opts->resources = realloc(opts->resources, sizeof(rtapp_resource_t) * opts->nresources); init_resource_data(name, type, idx, opts); return idx; } static void parse_resources(struct json_object *resources, rtapp_options_t *opts) { int i; struct lh_entry *entry; char *key; struct json_object *val; int idx; log_info(PFX "Parsing resource section"); if (!resources) { log_info(PFX "No resource section Found"); return; } if (json_object_is_type(resources, json_type_object)) { opts->nresources = 0; foreach(resources, entry, key, val, idx) { opts->nresources++; } log_info(PFX "Found %d Resources", opts->nresources); opts->resources = malloc(sizeof(rtapp_resource_t) * opts->nresources); foreach (resources, entry, key, val, idx) { parse_resource_data(key, val, idx, &opts->resources[idx], opts); } } } static int get_resource_index(const char *name, int type, rtapp_options_t *opts) { rtapp_resource_t *resources = opts->resources; int nresources = opts->nresources; int i = 0; while ((i < nresources) && ((strcmp(resources[i].name, name) != 0) || (resources[i].type != type))) i++; if (i >= nresources) i = add_resource_data(name, type, opts); return i; } static char* create_unique_name(char *tmp, int size, const char* ref, long tag) { snprintf(tmp, size, "%s%lx", ref, (long)(tag)); return tmp; } static void parse_thread_event_data(char *name, struct json_object *obj, event_data_t *data, rtapp_options_t *opts, long tag) { rtapp_resource_t *rdata, *ddata; char unique_name[22]; const char *ref; char *tmp; int i; if (!strncmp(name, "run", strlen("run")) || !strncmp(name, "sleep", strlen("sleep"))) { if (!json_object_is_type(obj, json_type_int)) goto unknown_event; data->duration = json_object_get_int(obj); if (!strncmp(name, "sleep", strlen("sleep"))) data->type = rtapp_sleep; else if (!strncmp(name, "runtime", strlen("runtime"))) data->type = rtapp_runtime; else data->type = rtapp_run; log_info(PIN2 "type %d duration %d", data->type, data->duration); return; } if (!strncmp(name, "mem", strlen("mem")) || !strncmp(name, "iorun", strlen("iorun"))) { if (!json_object_is_type(obj, json_type_int)) goto unknown_event; /* create an unique name for per-thread buffer */ ref = create_unique_name(unique_name, sizeof(unique_name), "mem", tag); i = get_resource_index(ref, rtapp_mem, opts); data->res = i; data->count = json_object_get_int(obj); /* A single IO devices for all threads */ if (strncmp(name, "iorun", strlen("iorun")) == 0) { i = get_resource_index("io_device", rtapp_iorun, opts); data->dep = i; }; if (!strncmp(name, "mem", strlen("mem"))) data->type = rtapp_mem; else data->type = rtapp_iorun; log_info(PIN2 "type %d count %d", data->type, data->count); return; } if (!strncmp(name, "lock", strlen("lock")) || !strncmp(name, "unlock", strlen("unlock"))) { if (!json_object_is_type(obj, json_type_string)) goto unknown_event; ref = json_object_get_string(obj); i = get_resource_index(ref, rtapp_mutex, opts); data->res = i; if (!strncmp(name, "lock", strlen("lock"))) data->type = rtapp_lock; else data->type = rtapp_unlock; rdata = &(opts->resources[data->res]); ddata = &(opts->resources[data->dep]); log_info(PIN2 "type %d target %s [%d]", data->type, rdata->name, rdata->index); return; } if (!strncmp(name, "signal", strlen("signal")) || !strncmp(name, "broad", strlen("broad"))) { if (!strncmp(name, "signal", strlen("signal"))) data->type = rtapp_signal; else data->type = rtapp_broadcast; if (!json_object_is_type(obj, json_type_string)) goto unknown_event; ref = json_object_get_string(obj); i = get_resource_index(ref, rtapp_wait, opts); data->res = i; rdata = &(opts->resources[data->res]); ddata = &(opts->resources[data->dep]); log_info(PIN2 "type %d target %s [%d]", data->type, rdata->name, rdata->index); return; } if (!strncmp(name, "wait", strlen("wait")) || !strncmp(name, "sync", strlen("sync"))) { if (!strncmp(name, "wait", strlen("wait"))) data->type = rtapp_wait; else data->type = rtapp_sig_and_wait; tmp = get_string_value_from(obj, "ref", TRUE, "unknown"); i = get_resource_index(tmp, rtapp_wait, opts); /* * get_string_value_from allocate the string so with have to free it * once useless */ free(tmp); data->res = i; tmp = get_string_value_from(obj, "mutex", TRUE, "unknown"); i = get_resource_index(tmp, rtapp_mutex, opts); /* * get_string_value_from allocate the string so with have to free it * once useless */ free(tmp); data->dep = i; rdata = &(opts->resources[data->res]); ddata = &(opts->resources[data->dep]); log_info(PIN2 "type %d target %s [%d] mutex %s [%d]", data->type, rdata->name, rdata->index, ddata->name, ddata->index); return; } if (!strncmp(name, "barrier", strlen("barrier"))) { if (!json_object_is_type(obj, json_type_string)) goto unknown_event; data->type = rtapp_barrier; ref = json_object_get_string(obj); i = get_resource_index(ref, rtapp_barrier, opts); data->res = i; rdata = &(opts->resources[data->res]); rdata->res.barrier.waiting += 1; log_info(PIN2 "type %d target %s [%d] %d users so far", data->type, rdata->name, rdata->index, rdata->res.barrier.waiting); return; } if (!strncmp(name, "timer", strlen("timer"))) { tmp = get_string_value_from(obj, "ref", TRUE, "unknown"); if (!strncmp(tmp, "unique", strlen("unique"))) ref = create_unique_name(unique_name, sizeof(unique_name), tmp, tag); else ref = tmp; i = get_resource_index(ref, rtapp_timer, opts); /* * get_string_value_from allocate the string so with have to free it * once useless */ free(tmp); data->res = i; data->duration = get_int_value_from(obj, "period", TRUE, 0); data->type = rtapp_timer; rdata = &(opts->resources[data->res]); ddata = &(opts->resources[data->dep]); tmp = get_string_value_from(obj, "mode", TRUE, "relative"); if (!strncmp(tmp, "absolute", strlen("absolute"))) rdata->res.timer.relative = 0; free(tmp); log_info(PIN2 "type %d target %s [%d] period %d", data->type, rdata->name, rdata->index, data->duration); return; } if (!strncmp(name, "resume", strlen("resume"))) { data->type = rtapp_resume; if (!json_object_is_type(obj, json_type_string)) goto unknown_event; ref = json_object_get_string(obj); i = get_resource_index(ref, rtapp_wait, opts); data->res = i; i = get_resource_index(ref, rtapp_mutex, opts); data->dep = i; rdata = &(opts->resources[data->res]); ddata = &(opts->resources[data->dep]); log_info(PIN2 "type %d target %s [%d] mutex %s [%d]", data->type, rdata->name, rdata->index, ddata->name, ddata->index); return; } if (!strncmp(name, "suspend", strlen("suspend"))) { data->type = rtapp_suspend; if (!json_object_is_type(obj, json_type_string)) goto unknown_event; ref = json_object_get_string(obj); i = get_resource_index(ref, rtapp_wait, opts); data->res = i; i = get_resource_index(ref, rtapp_mutex, opts); data->dep = i; rdata = &(opts->resources[data->res]); ddata = &(opts->resources[data->dep]); log_info(PIN2 "type %d target %s [%d] mutex %s [%d]", data->type, rdata->name, rdata->index, ddata->name, ddata->index); return; } if (!strncmp(name, "yield", strlen("yield"))) { data->type = rtapp_yield; log_info(PIN2 "type %d", data->type); return; } unknown_resource: log_error(PIN2 "Resource %s not found in the resource section !!!", ref); log_error(PIN2 "Please check the resource name or the resource section"); unknown_event: data->duration = 0; data->type = rtapp_run; log_error(PIN2 "Unknown or mismatch %s event type !!!", name); } static char *events[] = { "lock", "unlock", "wait", "signal", "broad", "sync", "sleep", "runtime", "run", "timer", "suspend", "resume", "mem", "iorun", "yield", "barrier", NULL }; static int obj_is_event(char *name) { char **pos; for (pos = events; *pos; pos++) { char *event = *pos; if (!strncmp(name, event, strlen(event))) return 1; } return 0; } static void parse_cpuset_data(struct json_object *obj, cpuset_data_t *data) { struct json_object *cpuset_obj, *cpu; unsigned int max_cpu = sysconf(_SC_NPROCESSORS_CONF) - 1; /* cpuset */ cpuset_obj = get_in_object(obj, "cpus", TRUE); if (cpuset_obj) { unsigned int i; unsigned int cpu_idx; assure_type_is(cpuset_obj, obj, "cpus", json_type_array); data->cpuset_str = strdup(json_object_to_json_string(cpuset_obj)); data->cpusetsize = sizeof(cpu_set_t); data->cpuset = malloc(data->cpusetsize); CPU_ZERO(data->cpuset); for (i = 0; i < json_object_array_length(cpuset_obj); i++) { cpu = json_object_array_get_idx(cpuset_obj, i); cpu_idx = json_object_get_int(cpu); if (cpu_idx > max_cpu) { log_critical(PIN2 "Invalid cpu %d in cpuset %s", cpu_idx, data->cpuset_str); free(data->cpuset); free(data->cpuset_str); exit(EXIT_INV_CONFIG); } CPU_SET(cpu_idx, data->cpuset); } } else { data->cpuset_str = strdup("-"); data->cpuset = NULL; data->cpusetsize = 0; } log_info(PIN "key: cpus %s", data->cpuset_str); } static void parse_thread_phase_data(struct json_object *obj, phase_data_t *data, rtapp_options_t *opts, long tag) { /* used in the foreach macro */ struct lh_entry *entry; char *key; struct json_object *val; int idx; int i; /* loop */ data->loop = get_int_value_from(obj, "loop", TRUE, 1); /* Count number of events */ data->nbevents = 0; foreach(obj, entry, key, val, idx) { if (obj_is_event(key)) data->nbevents++; } log_info(PIN "Found %d events", data->nbevents); if (data->nbevents == 0) return; data->events = malloc(data->nbevents * sizeof(event_data_t)); /* Parse events */ i = 0; foreach(obj, entry, key, val, idx) { if (obj_is_event(key)) { log_info(PIN "Parsing event %s", key); parse_thread_event_data(key, val, &data->events[i], opts, tag); i++; } } parse_cpuset_data(obj, &data->cpu_data); } static void parse_thread_data(char *name, struct json_object *obj, int index, thread_data_t *data, rtapp_options_t *opts) { char *policy; char def_policy[RTAPP_POLICY_DESCR_LENGTH]; struct json_object *phases_obj, *resources; int prior_def; log_info(PFX "Parsing thread %s [%d]", name, index); /* common and defaults */ data->resources = &opts->resources; data->ind = index; data->name = strdup(name); data->lock_pages = opts->lock_pages; data->cpu_data.cpuset = NULL; data->cpu_data.cpuset_str = NULL; data->curr_cpu_data = NULL; data->def_cpu_data.cpuset = NULL; data->def_cpu_data.cpuset_str = NULL; /* policy */ policy_to_string(opts->policy, def_policy); policy = get_string_value_from(obj, "policy", TRUE, def_policy); if (policy) { if (string_to_policy(policy, &data->sched_policy) != 0) { log_critical(PIN2 "Invalid policy %s", policy); exit(EXIT_INV_CONFIG); } } policy_to_string(data->sched_policy, data->sched_policy_descr); /* priority */ if (data->sched_policy == other) prior_def = DEFAULT_THREAD_NICE; else prior_def = DEFAULT_THREAD_PRIORITY; data->sched_prio = get_int_value_from(obj, "priority", TRUE, prior_def); /* deadline params */ data->runtime = get_int_value_from(obj, "dl-runtime", TRUE, 0) * 1000; if (!data->runtime) data->runtime = get_int_value_from(obj, "runtime", TRUE, 0); data->period = get_int_value_from(obj, "dl-period", TRUE, 0) * 1000; if (!data->period) data->period = get_int_value_from(obj, "period", TRUE, data->runtime); data->deadline = get_int_value_from(obj, "dl-deadline", TRUE, 0) * 1000; if (!data->period) data->deadline = get_int_value_from(obj, "deadline", TRUE, data->period); /* cpuset */ parse_cpuset_data(obj, &data->cpu_data); /* initial delay */ data->delay = get_int_value_from(obj, "delay", TRUE, 0); if (data->delay < 0) data->delay = 0; /* Get phases */ phases_obj = get_in_object(obj, "phases", TRUE); if (phases_obj) { /* used in the foreach macro */ struct lh_entry *entry; char *key; struct json_object *val; int idx; assure_type_is(phases_obj, obj, "phases", json_type_object); log_info(PIN "Parsing phases section"); data->nphases = 0; foreach(phases_obj, entry, key, val, idx) { data->nphases++; } log_info(PIN "Found %d phases", data->nphases); data->phases = malloc(sizeof(phase_data_t) * data->nphases); foreach(phases_obj, entry, key, val, idx) { log_info(PIN "Parsing phase %s", key); parse_thread_phase_data(val, &data->phases[idx], opts, (long)data); } /* Get loop number */ data->loop = get_int_value_from(obj, "loop", TRUE, -1); } else { data->nphases = 1; data->phases = malloc(sizeof(phase_data_t) * data->nphases); parse_thread_phase_data(obj, &data->phases[0], opts, (long)data); /* Get loop number */ data->loop = -1; } } static void parse_tasks(struct json_object *tasks, rtapp_options_t *opts) { /* used in the foreach macro */ struct lh_entry *entry; char *key; struct json_object *val; int idx; int i, instance; log_info(PFX "Parsing tasks section"); opts->nthreads = 0; foreach(tasks, entry, key, val, idx) { instance = get_int_value_from(val, "instance", TRUE, 1); opts->nthreads += instance; } log_info(PFX "Found %d tasks", opts->nthreads); opts->threads_data = malloc(sizeof(thread_data_t) * opts->nthreads); i = instance = 0; foreach (tasks, entry, key, val, idx) { instance += get_int_value_from(val, "instance", TRUE, 1); for (; i < instance; i++) parse_thread_data(key, val, i, &opts->threads_data[i], opts); } } static void parse_global(struct json_object *global, rtapp_options_t *opts) { char *policy, *tmp_str; struct json_object *tmp_obj; int scan_cnt; log_info(PFX "Parsing global section"); if (!global) { log_info(PFX " No global section Found: Use default value"); opts->duration = -1; opts->gnuplot = 0; opts->policy = other; opts->calib_cpu = 0; opts->calib_ns_per_loop = 0; opts->logdir = strdup("./"); opts->logbasename = strdup("rt-app"); opts->logsize = 0; opts->ftrace = 0; opts->lock_pages = 1; opts->pi_enabled = 0; opts->io_device = strdup("/dev/null"); opts->mem_buffer_size = DEFAULT_MEM_BUF_SIZE; opts->cumulative_slack = 0; return; } opts->duration = get_int_value_from(global, "duration", TRUE, -1); opts->gnuplot = get_bool_value_from(global, "gnuplot", TRUE, 0); policy = get_string_value_from(global, "default_policy", TRUE, "SCHED_OTHER"); if (string_to_policy(policy, &opts->policy) != 0) { log_critical(PFX "Invalid policy %s", policy); exit(EXIT_INV_CONFIG); } /* * get_string_value_from allocate the string so with have to free it * once useless */ free(policy); tmp_obj = get_in_object(global, "calibration", TRUE); if (tmp_obj == NULL) { /* no setting ? Calibrate CPU0 */ opts->calib_cpu = 0; opts->calib_ns_per_loop = 0; log_error("missing calibration setting force CPU0"); } else { if (json_object_is_type(tmp_obj, json_type_int)) { /* integer (no " ") detected. */ opts->calib_ns_per_loop = json_object_get_int(tmp_obj); log_debug("ns_per_loop %d", opts->calib_ns_per_loop); } else { /* Get CPU number */ tmp_str = get_string_value_from(global, "calibration", TRUE, "CPU0"); scan_cnt = sscanf(tmp_str, "CPU%d", &opts->calib_cpu); /* * get_string_value_from allocate the string so with have to free it * once useless */ free(tmp_str); if (!scan_cnt) { log_critical(PFX "Invalid calibration CPU%d", opts->calib_cpu); exit(EXIT_INV_CONFIG); } log_debug("calibrating CPU%d", opts->calib_cpu); } } tmp_obj = get_in_object(global, "log_size", TRUE); if (tmp_obj == NULL) { /* no size ? use file system */ opts->logsize = -2; } else { if (json_object_is_type(tmp_obj, json_type_int)) { /* integer (no " ") detected. */ /* buffer size is set in MB */ opts->logsize = json_object_get_int(tmp_obj) << 20; log_notice("Log buffer size fixed to %dMB per threads", (opts->logsize >> 20)); } else { /* Get CPU number */ tmp_str = get_string_value_from(global, "log_size", TRUE, "disable"); if (strcmp(tmp_str, "disable")) opts->logsize = 0; else if (strcmp(tmp_str, "file")) opts->logsize = -2; else if (strcmp(tmp_str, "auto")) opts->logsize = -2; /* Automatic buffer size computation is not supported yet so we fall back on file system mode */ log_debug("Log buffer set to %s mode", tmp_str); /* * get_string_value_from allocate the string so with have to free it * once useless */ free(tmp_str); } } opts->logdir = get_string_value_from(global, "logdir", TRUE, "./"); opts->logbasename = get_string_value_from(global, "log_basename", TRUE, "rt-app"); opts->ftrace = get_bool_value_from(global, "ftrace", TRUE, 0); opts->lock_pages = get_bool_value_from(global, "lock_pages", TRUE, 1); opts->pi_enabled = get_bool_value_from(global, "pi_enabled", TRUE, 0); opts->io_device = get_string_value_from(global, "io_device", TRUE, "/dev/null"); opts->mem_buffer_size = get_int_value_from(global, "mem_buffer_size", TRUE, DEFAULT_MEM_BUF_SIZE); opts->cumulative_slack = get_bool_value_from(global, "cumulative_slack", TRUE, 0); } static void get_opts_from_json_object(struct json_object *root, rtapp_options_t *opts) { struct json_object *global, *tasks, *resources; if (root == NULL) { log_error(PFX "Error while parsing input JSON"); exit(EXIT_INV_CONFIG); } log_info(PFX "Successfully parsed input JSON"); log_info(PFX "root : %s", json_object_to_json_string(root)); global = get_in_object(root, "global", TRUE); if (global) log_info(PFX "global : %s", json_object_to_json_string(global)); tasks = get_in_object(root, "tasks", FALSE); log_info(PFX "tasks : %s", json_object_to_json_string(tasks)); resources = get_in_object(root, "resources", TRUE); if (resources) log_info(PFX "resources: %s", json_object_to_json_string(resources)); log_info(PFX "Parsing global"); parse_global(global, opts); json_object_put(global); log_info(PFX "Parsing resources"); parse_resources(resources, opts); json_object_put(resources); log_info(PFX "Parsing tasks"); parse_tasks(tasks, opts); json_object_put(tasks); log_info(PFX "Free json objects"); } void parse_config_stdin(rtapp_options_t *opts) { /* * Read from stdin until EOF, write to temp file and parse * as a "normal" config file */ size_t in_length; char buf[JSON_FILE_BUF_SIZE]; struct json_object *js; log_info(PFX "Reading JSON config from stdin..."); in_length = fread(buf, sizeof(char), JSON_FILE_BUF_SIZE, stdin); buf[in_length] = '\0'; js = json_tokener_parse(buf); get_opts_from_json_object(js, opts); return; } void parse_config(const char *filename, rtapp_options_t *opts) { int done; char *fn = strdup(filename); struct json_object *js; log_info(PFX "Reading JSON config from %s", fn); js = json_object_from_file(fn); get_opts_from_json_object(js, opts); return; } rt-app-1.0/src/rt-app_parse_config.h000066400000000000000000000022131311057016400174330ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #ifndef _RTAPP_PARSE_CONFIG_H #define _RTAPP_PARSE_CONFIG_H #include "rt-app_types.h" void parse_config(const char *filename, rtapp_options_t *opts); void parse_config_stdin(rtapp_options_t *opts); #endif // _RTAPP_PARSE_CONFIG_H rt-app-1.0/src/rt-app_types.h000066400000000000000000000116071311057016400161470ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #ifndef _RTAPP_TYPES_H_ #define _RTAPP_TYPES_H_ #include #include "config.h" #include "dl_syscalls.h" #define RTAPP_POLICY_DESCR_LENGTH 16 #define RTAPP_RESOURCE_DESCR_LENGTH 16 #define RTAPP_FTRACE_PATH_LENGTH 256 #define DEFAULT_THREAD_PRIORITY 10 #define DEFAULT_THREAD_NICE 0 #define PATH_LENGTH 256 /* exit codes */ #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 #define EXIT_INV_CONFIG 2 #define EXIT_INV_COMMANDLINE 3 typedef enum policy_t { other = SCHED_OTHER, rr = SCHED_RR, fifo = SCHED_FIFO #ifdef DLSCHED , deadline = SCHED_DEADLINE #endif } policy_t; typedef enum resource_t { rtapp_unknown = 0, rtapp_mutex, rtapp_wait, rtapp_signal, rtapp_broadcast, rtapp_sleep, rtapp_run, rtapp_sig_and_wait, rtapp_lock, rtapp_unlock, rtapp_timer, rtapp_suspend, rtapp_resume, rtapp_mem, rtapp_iorun, rtapp_runtime, rtapp_yield, rtapp_barrier } resource_t; struct _rtapp_mutex { pthread_mutex_t obj; pthread_mutexattr_t attr; } ; struct _rtapp_cond { pthread_cond_t obj; pthread_condattr_t attr; }; struct _rtapp_barrier_like { /* sync operation which works without ordering - everyone waits * until the last task arrives at the sync point. Conceptually * just like pthread_barrier except we don't have any cleanup * issues which barrier would impose */ /* mutex to guard read/write of the flag */ pthread_mutex_t m_obj; pthread_mutexattr_t m_attr; /* flag to indicate how many are waiting */ int waiting; /* condvar to wait/signal on */ pthread_cond_t c_obj; }; struct _rtapp_signal { pthread_cond_t *target; }; struct _rtapp_timer { struct timespec t_next; int init; int relative; }; struct _rtapp_iomem_buf { char *ptr; int size; }; struct _rtapp_iodev { int fd; }; /* Shared resources */ typedef struct _rtapp_resource_t { union { struct _rtapp_mutex mtx; struct _rtapp_cond cond; struct _rtapp_signal signal; struct _rtapp_timer timer; struct _rtapp_iomem_buf buf; struct _rtapp_iodev dev; struct _rtapp_barrier_like barrier; } res; int index; resource_t type; char *name; } rtapp_resource_t; typedef struct _event_data_t { resource_t type; int res; int dep; int duration; int count; } event_data_t; typedef struct _cpuset_data_t { cpu_set_t *cpuset; char *cpuset_str; size_t cpusetsize; } cpuset_data_t; typedef struct _phase_data_t { int loop; event_data_t *events; int nbevents; cpuset_data_t cpu_data; } phase_data_t; typedef struct _thread_data_t { int ind; char *name; int lock_pages; int duration; rtapp_resource_t **resources; cpuset_data_t cpu_data; /* cpu set information */ cpuset_data_t *curr_cpu_data; /* Current cpu set being used */ cpuset_data_t def_cpu_data; /* Default cpu set for task */ unsigned long runtime, deadline, period; int loop; int nphases; phase_data_t *phases; struct timespec main_app_start; FILE *log_handler; policy_t sched_policy; char sched_policy_descr[RTAPP_POLICY_DESCR_LENGTH]; int sched_prio; unsigned long delay; #ifdef DLSCHED struct sched_attr dl_params; #endif } thread_data_t; typedef struct _ftrace_data_t { char *debugfs; int trace_fd; int marker_fd; } ftrace_data_t; typedef struct _log_data_t { unsigned long perf; unsigned long duration; unsigned long wu_latency; unsigned long c_duration; unsigned long c_period; long slack; } log_data_t; typedef struct _rtapp_options_t { int lock_pages; thread_data_t *threads_data; int nthreads; policy_t policy; int duration; char *logdir; char *logbasename; int logsize; int gnuplot; int calib_cpu; int calib_ns_per_loop; rtapp_resource_t *resources; int nresources; int pi_enabled; int ftrace; int die_on_dmiss; int mem_buffer_size; char *io_device; int cumulative_slack; } rtapp_options_t; typedef struct _timing_point_t { int ind; unsigned long perf; unsigned long duration; unsigned long period; unsigned long c_duration; unsigned long c_period; unsigned long wu_latency; long slack; __u64 start_time; __u64 end_time; __u64 rel_start_time; } timing_point_t; #endif // _RTAPP_TYPES_H_ rt-app-1.0/src/rt-app_utils.c000066400000000000000000000150021311057016400161270ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #include #include #include #include #include #include #include "rt-app_utils.h" unsigned long timespec_to_usec(struct timespec *ts) { return lround((ts->tv_sec * 1E9 + ts->tv_nsec) / 1000.0); } unsigned long long timespec_to_usec_ull(struct timespec *ts) { return llround((ts->tv_sec * 1E9 + ts->tv_nsec) / 1000.0); } long timespec_to_usec_long(struct timespec *ts) { return round((ts->tv_sec * 1E9 + ts->tv_nsec) / 1000.0); } #ifdef DLSCHED __u64 timespec_to_nsec(struct timespec *ts) { return round(ts->tv_sec * 1E9 + ts->tv_nsec); } #endif struct timespec usec_to_timespec(unsigned long usec) { struct timespec ts; ts.tv_sec = usec / 1000000; ts.tv_nsec = (usec % 1000000) * 1000; return ts; } struct timespec msec_to_timespec(unsigned int msec) { struct timespec ts; ts.tv_sec = msec / 1000; ts.tv_nsec = (msec % 1000) * 1000000; return ts; } struct timespec timespec_add(struct timespec *t1, struct timespec *t2) { struct timespec ts; ts.tv_sec = t1->tv_sec + t2->tv_sec; ts.tv_nsec = t1->tv_nsec + t2->tv_nsec; while (ts.tv_nsec >= 1E9) { ts.tv_nsec -= 1E9; ts.tv_sec++; } return ts; } struct timespec timespec_sub(struct timespec *t1, struct timespec *t2) { struct timespec ts; if (t1->tv_nsec < t2->tv_nsec) { ts.tv_sec = t1->tv_sec - t2->tv_sec -1; ts.tv_nsec = t1->tv_nsec + 1000000000 - t2->tv_nsec; } else { ts.tv_sec = t1->tv_sec - t2->tv_sec; ts.tv_nsec = t1->tv_nsec - t2->tv_nsec; } return ts; } int timespec_lower(struct timespec *what, struct timespec *than) { if (what->tv_sec > than->tv_sec) return 0; if (what->tv_sec < than->tv_sec) return 1; if (what->tv_nsec < than->tv_nsec) return 1; return 0; } int64_t timespec_sub_to_ns(struct timespec *t1, struct timespec *t2) { int64_t diff; if (t1->tv_nsec < t2->tv_nsec) { diff = 1E9 * (int64_t)((int) t1->tv_sec - (int) t2->tv_sec - 1); diff += ((int) t1->tv_nsec + 1E9 - (int) t2->tv_nsec); } else { diff = 1E9 * (int64_t)((int) t1->tv_sec - (int) t2->tv_sec); diff += ((int) t1->tv_nsec - (int) t2->tv_nsec); } return diff; } void log_timing(FILE *handler, timing_point_t *t) { fprintf(handler, "%4d %8lu %8lu %8lu %15llu %15llu %15llu %10ld %10lu %10lu %10lu", t->ind, t->perf, t->duration, t->period, t->start_time, t->end_time, t->rel_start_time, t->slack, t->c_duration, t->c_period, t->wu_latency ); fprintf(handler, "\n"); } pid_t gettid(void) { return syscall(__NR_gettid); } int string_to_policy(const char *policy_name, policy_t *policy) { if (strcmp(policy_name, "SCHED_OTHER") == 0) *policy = other; else if (strcmp(policy_name, "SCHED_RR") == 0) *policy = rr; else if (strcmp(policy_name, "SCHED_FIFO") == 0) *policy = fifo; #ifdef DLSCHED else if (strcmp(policy_name, "SCHED_DEADLINE") == 0) *policy = deadline; #endif else return 1; return 0; } int policy_to_string(policy_t policy, char *policy_name) { switch (policy) { case other: strcpy(policy_name, "SCHED_OTHER"); break; case rr: strcpy(policy_name, "SCHED_RR"); break; case fifo: strcpy(policy_name, "SCHED_FIFO"); break; #ifdef DLSCHED case deadline: strcpy(policy_name, "SCHED_DEADLINE"); break; #endif default: return 1; } return 0; } int string_to_resource(const char *name, resource_t *resource) { if (strcmp(name, "mutex") == 0) *resource = rtapp_mutex; else if (strcmp(name, "signal") == 0) *resource = rtapp_signal; else if (strcmp(name, "wait") == 0) *resource = rtapp_wait; else if (strcmp(name, "broadcast") == 0) *resource = rtapp_broadcast; else if (strcmp(name, "sync") == 0) *resource = rtapp_sig_and_wait; else if (strcmp(name, "sleep") == 0) *resource = rtapp_sleep; else if (strcmp(name, "run") == 0) *resource = rtapp_run; else if (strcmp(name, "timer") == 0) *resource = rtapp_timer; else return 1; return 0; } int resource_to_string(resource_t resource, char *resource_name) { switch (resource) { case rtapp_mutex: strcpy(resource_name, "mutex"); break; case rtapp_wait: strcpy(resource_name, "wait"); break; case rtapp_signal: strcpy(resource_name, "signal"); break; case rtapp_broadcast: strcpy(resource_name, "broadcast"); break; case rtapp_sig_and_wait: strcpy(resource_name, "sync"); break; case rtapp_sleep: strcpy(resource_name, "sleep"); break; case rtapp_run: strcpy(resource_name, "run"); break; case rtapp_timer: strcpy(resource_name, "timer"); break; case rtapp_barrier: strcpy(resource_name, "barrier"); break; default: return 1; } return 0; } void ftrace_write(int mark_fd, const char *fmt, ...) { va_list ap; int n, size = BUF_SIZE, ret; char *tmp, *ntmp; if (mark_fd < 0) { log_error("invalid mark_fd"); exit(EXIT_FAILURE); } if ((tmp = malloc(size)) == NULL) { log_error("Cannot allocate ftrace buffer"); exit(EXIT_FAILURE); } while(1) { /* Try to print in the allocated space */ va_start(ap, fmt); n = vsnprintf(tmp, BUF_SIZE, fmt, ap); va_end(ap); /* If it worked return success */ if (n > -1 && n < size) { ret = write(mark_fd, tmp, n); free(tmp); if (ret < 0) { log_error("Cannot write mark_fd: %s\n", strerror(errno)); exit(EXIT_FAILURE); } else if (ret < n) { log_debug("Cannot write all bytes at once into mark_fd\n"); } return; } /* Else try again with more space */ if (n > -1) /* glibc 2.1 */ size = n+1; else /* glibc 2.0 */ size *= 2; if ((ntmp = realloc(tmp, size)) == NULL) { free(tmp); log_error("Cannot reallocate ftrace buffer"); exit(EXIT_FAILURE); } else { tmp = ntmp; } } } rt-app-1.0/src/rt-app_utils.h000066400000000000000000000067351311057016400161510ustar00rootroot00000000000000/* This file is part of rt-app - https://launchpad.net/rt-app Copyright (C) 2010 Giacomo Bagnoli Copyright (C) 2014 Juri Lelli Copyright (C) 2014 Vincent Guittot 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. */ #ifndef _TIMESPEC_UTILS_H_ #define _TIMESPEC_UTILS_H_ #include #include #include "config.h" #include "rt-app_types.h" #ifndef LOG_PREFIX #define LOG_PREFIX "[rt-app] " #endif #ifndef LOG_LEVEL #define LOG_LEVEL 50 #endif #define LOG_LEVEL_DEBUG 100 #define LOG_LEVEL_INFO 75 #define LOG_LEVEL_NOTICE 50 #define LOG_LEVEL_ERROR 10 #define LOG_LEVEL_CRITICAL 10 #define BUF_SIZE 100 /* This prepend a string to a message */ #define rtapp_log_to(where, level, level_pfx, msg, args...) \ do { \ if (level <= LOG_LEVEL) { \ fprintf(where, LOG_PREFIX level_pfx msg "\n", ##args); \ } \ } while (0); #define log_ftrace(mark_fd, msg, args...) \ do { \ ftrace_write(mark_fd, msg, ##args); \ } while (0); #define log_notice(msg, args...) \ do { \ rtapp_log_to(stderr, LOG_LEVEL_NOTICE, " ", msg, ##args); \ } while (0); #define log_info(msg, args...) \ do { \ rtapp_log_to(stderr, LOG_LEVEL_INFO, " ", msg, ##args); \ } while (0); #define log_error(msg, args...) \ do { \ rtapp_log_to(stderr, LOG_LEVEL_ERROR, " ", msg, ##args); \ } while (0); #define log_debug(msg, args...) \ do { \ rtapp_log_to(stderr, LOG_LEVEL_DEBUG, " ", msg, ##args); \ } while (0); #define log_critical(msg, args...) \ do { \ rtapp_log_to(stderr, LOG_LEVEL_CRITICAL, " ", msg, ##args); \ } while (0); unsigned long timespec_to_usec(struct timespec *ts); unsigned long long timespec_to_usec_ull(struct timespec *ts); long timespec_to_usec_long(struct timespec *ts); struct timespec usec_to_timespec(unsigned long usec); struct timespec usec_to_timespec(unsigned long usec); struct timespec msec_to_timespec(unsigned int msec); struct timespec timespec_add(struct timespec *t1, struct timespec *t2); struct timespec timespec_sub(struct timespec *t1, struct timespec *t2); int timespec_lower(struct timespec *what, struct timespec *than); int64_t timespec_sub_to_ns(struct timespec *t1, struct timespec *t2); void log_timing(FILE *handler, timing_point_t *t); #ifdef DLSCHED pid_t gettid(void); unsigned long long timespec_to_nsec(struct timespec *ts); #endif int policy_to_string(policy_t policy, char *policy_name); int string_to_policy(const char *policy_name, policy_t *policy); int string_to_resource(const char *name, resource_t *resource); int resource_to_string(resource_t resource, char *name); void ftrace_write(int mark_fd, const char *fmt, ...); #endif // _TIMESPEC_UTILS_H_ /* vim: set ts=8 noexpandtab shiftwidth=8: */