├── .travis.yml ├── COPYING ├── Makefile ├── README.md ├── bash-completion └── power-calibrate ├── debian ├── changelog ├── control ├── copyright ├── rules ├── source │ └── format └── watch ├── perf.c ├── perf.h ├── power-calibrate.8 ├── power-calibrate.c └── scripts └── power-calibrate-json-parse.py /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | sudo: required 3 | 4 | matrix: 5 | include: 6 | - env: PEDANTIC=1 7 | 8 | before_install: 9 | - sudo apt-get update -q 10 | - sudo apt-get install build-essential 11 | 12 | language: c 13 | 14 | script: 15 | - make -j2 PEDANTIC=$PEDANTIC 16 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2014-2021 Canonical, Ltd. 3 | # Copyright (C) 2021-2025 Colin Ian King. 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | # 19 | 20 | VERSION=0.01.37 21 | 22 | CFLAGS += -Wall -Wextra -DVERSION='"$(VERSION)"' -O2 -g 23 | 24 | # 25 | # Pedantic flags 26 | # 27 | ifeq ($(PEDANTIC),1) 28 | CFLAGS += -Wabi -Wcast-qual -Wfloat-equal -Wmissing-declarations \ 29 | -Wmissing-format-attribute -Wno-long-long -Wpacked \ 30 | -Wredundant-decls -Wshadow -Wno-missing-field-initializers \ 31 | -Wno-missing-braces -Wno-sign-compare -Wno-multichar 32 | endif 33 | 34 | BINDIR=/usr/sbin 35 | MANDIR=/usr/share/man/man8 36 | BASHDIR=/usr/share/bash-completion/completions 37 | 38 | SRC = power-calibrate.c perf.c 39 | OBJS = $(SRC:.c=.o) 40 | 41 | power-calibrate: $(OBJS) Makefile perf.h 42 | $(CC) $(CPPFLAGS) $(CFLAGS) $(OBJS) -lm -o $@ $(LDFLAGS) 43 | 44 | power-calibrate.o: power-calibrate.c Makefile perf.h 45 | $(CC) $(CPPFLAGS) $(CFLAGS) -c power-calibrate.c -o $@ 46 | 47 | perf.o: perf.c Makefile perf.h 48 | $(CC) $(CPPFLAGS) $(CFLAGS) -c perf.c -o $@ 49 | 50 | power-calibrate.8.gz: power-calibrate.8 51 | gzip -c $< > $@ 52 | 53 | dist: 54 | rm -rf power-calibrate-$(VERSION) 55 | mkdir power-calibrate-$(VERSION) 56 | cp -rp Makefile scripts power-calibrate.c power-calibrate.8 \ 57 | perf.c perf.h COPYING .travis.yml bash-completion README.md \ 58 | power-calibrate-$(VERSION) 59 | tar -Jcf power-calibrate-$(VERSION).tar.xz power-calibrate-$(VERSION) 60 | rm -rf power-calibrate-$(VERSION) 61 | 62 | clean: 63 | rm -f power-calibrate $(OBJS) power-calibrate.8.gz 64 | rm -f power-calibrate-$(VERSION).tar.xz 65 | 66 | install: power-calibrate power-calibrate.8.gz 67 | mkdir -p ${DESTDIR}${BINDIR} 68 | cp power-calibrate ${DESTDIR}${BINDIR} 69 | mkdir -p ${DESTDIR}${MANDIR} 70 | cp power-calibrate.8.gz ${DESTDIR}${MANDIR} 71 | mkdir -p ${DESTDIR}${BASHDIR} 72 | cp bash-completion/power-calibrate ${DESTDIR}${BASHDIR} 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Power Calibrate 2 | 3 | Power-calibrate calibrates the power consumption of a mobile device that has a battery power source. It will attempt to calculate the power usage of 1% of CPU utiltisation and 1 context switch. 4 | 5 | # Example: 6 | 7 | ``` 8 | $ power-calibrate -p -s 11 -d 20 -r 60 -R 9 | CPU load User Sys Idle Run Ctxt/s IRQ/s Ops/s Cycl/s Inst/s Watts 10 | 0% x 1 0.5 1.4 96.3 1.1 372.7 108.8 0.0 2.1K 370.4 3.039 11 | 0% x 2 0.2 0.0 99.7 1.0 104.2 36.9 0.0 5.3K 0.7K 2.230 12 | 0% x 3 0.1 0.0 99.9 1.0 96.2 29.7 0.0 7.7K 1.3K 2.196 13 | 0% x 4 0.2 0.0 99.8 1.0 113.3 35.9 0.0 11.9K 1.6K 2.237 14 | 10% x 1 2.4 0.0 97.6 1.0 135.9 72.4 21.0M 121.4M 294.2M 2.455 15 | 10% x 2 5.0 0.1 94.9 1.2 170.1 121.4 32.3M 240.9M 452.4M 2.518 16 | 10% x 3 7.5 0.1 92.4 1.3 197.7 158.9 53.1M 362.7M 0.7B 2.731 17 | 10% x 4 10.1 0.1 89.9 1.4 235.2 203.1 69.8M 485.5M 1.0B 2.874 18 | 20% x 1 5.0 0.1 94.9 1.3 221.5 148.2 66.0M 381.5M 0.9B 2.988 19 | 20% x 2 10.1 0.3 89.6 1.4 365.9 273.9 135.1M 1.1B 1.9B 3.688 20 | 20% x 3 14.8 0.2 85.0 1.4 569.8 419.1 237.5M 1.7B 3.3B 4.944 21 | 20% x 4 20.2 0.2 79.6 1.9 666.1 524.8 286.3M 2.3B 4.0B 4.972 22 | 30% x 1 7.4 0.0 92.6 1.3 314.1 215.9 109.4M 0.6B 1.5B 3.477 23 | 30% x 2 14.5 0.2 85.3 1.9 507.7 389.6 204.8M 1.7B 2.9B 4.343 24 | 30% x 3 22.6 0.3 77.2 2.0 822.2 627.2 363.3M 2.6B 5.1B 6.357 25 | 30% x 4 29.8 0.4 69.8 1.9 972.4 774.9 440.9M 3.4B 6.2B 6.338 26 | 40% x 1 9.9 0.2 89.9 1.4 459.4 314.8 179.5M 1.0B 2.5B 4.563 27 | 40% x 2 20.2 0.3 79.6 1.9 645.8 516.2 275.9M 2.3B 3.9B 5.134 28 | 40% x 3 29.8 0.3 69.9 1.9 1059.4 817.9 480.6M 3.5B 6.7B 7.577 29 | 40% x 4 40.1 0.5 59.4 2.8 1207.6 1007.1 0.6B 4.6B 7.8B 7.015 30 | 50% x 1 12.2 0.1 87.7 1.4 483.2 352.2 194.0M 1.1B 2.7B 4.553 31 | 50% x 2 24.7 0.3 75.1 2.1 797.0 641.4 351.6M 2.9B 4.9B 6.106 32 | 50% x 3 36.4 0.4 63.2 2.3 1260.1 987.2 0.6B 4.2B 8.1B 8.462 33 | 50% x 4 49.5 0.7 49.8 2.3 1473.9 1231.5 0.7B 5.7B 9.7B 8.083 34 | 60% x 1 14.7 0.2 85.0 1.6 696.0 483.1 297.2M 1.7B 4.2B 6.698 35 | 60% x 2 29.4 0.4 70.3 2.3 936.1 758.8 420.0M 3.5B 5.9B 6.871 36 | 60% x 3 44.2 0.3 55.5 2.9 1513.2 1198.4 0.7B 5.1B 10.0B 9.687 37 | 60% x 4 58.6 0.7 40.6 3.3 1740.3 1452.9 0.8B 6.7B 11.5B 9.192 38 | 70% x 1 16.8 0.3 83.0 1.7 788.0 552.2 345.8M 2.0B 4.8B 7.504 39 | 70% x 2 33.8 0.4 65.8 2.5 1002.5 837.1 456.2M 3.8B 6.4B 6.904 40 | 70% x 3 49.1 0.4 50.5 3.1 1723.0 1333.5 0.8B 5.9B 11.4B 10.545 41 | 70% x 4 68.3 0.5 31.2 3.4 1971.1 1656.7 0.9B 7.4B 13.2B 10.050 42 | 80% x 1 19.4 0.3 80.3 1.9 888.0 632.4 393.6M 2.3B 5.5B 8.243 43 | 80% x 2 39.2 0.4 60.4 2.7 1205.3 996.3 0.6B 4.6B 7.8B 8.452 44 | 80% x 3 59.1 0.4 40.5 3.5 2012.2 1603.4 0.9B 6.8B 13.3B 11.767 45 | 80% x 4 78.5 0.6 20.9 4.2 2426.5 2004.6 1.2B 9.0B 16.2B 12.467 46 | 90% x 1 21.9 0.2 77.9 2.0 994.8 706.9 446.5M 2.6B 6.3B 8.944 47 | 90% x 2 44.3 0.2 55.5 2.8 1355.9 1121.2 0.6B 5.2B 8.8B 9.226 48 | 90% x 3 66.1 0.6 32.7 3.8 2297.8 1807.9 1.1B 7.6B 14.9B 12.690 49 | 90% x 4 87.5 1.3 11.0 4.6 2814.5 2262.2 1.3B 10.0B 17.8B 13.465 50 | 100% x 1 25.0 0.0 75.0 2.0 101.9 293.6 0.5B 3.1B 7.5B 10.222 51 | 100% x 2 50.0 0.0 50.0 3.0 93.9 550.4 0.7B 6.2B 10.4B 10.397 52 | 100% x 3 75.1 0.0 24.9 4.0 82.2 793.5 1.2B 8.7B 16.8B 12.889 53 | 100% x 4 100.0 0.0 0.0 5.0 77.8 1038.9 1.4B 11.6B 19.5B 13.132 54 | 55 | For 4 CPUs (of a 4 CPU system): 56 | Power (Watts) = (% CPU load * 1.226129e-01) + 3.187491 57 | 1% CPU load is about 122.61 mW 58 | Coefficient of determination R^2 = 0.849442 (good) 59 | 60 | Energy (Watt-seconds) = (bogo op * 8.666398e-09) + 2.972347 61 | 1 bogo op is about 8.67 nWs 62 | Coefficient of determination R^2 = 0.930446 (strong) 63 | 64 | Energy (Watt-seconds) = (CPU cycle * 1.061961e-09) + 3.281099 65 | 1 CPU cycle is about 1.06 nWs 66 | Coefficient of determination R^2 = 0.871018 (good) 67 | 68 | Energy (Watt-seconds) = (CPU instruction * 6.188774e-10) + 2.972191 69 | 1 CPU instruction is about 0.62 nWs 70 | Coefficient of determination R^2 = 0.930441 (strong) 71 | ``` 72 | -------------------------------------------------------------------------------- /bash-completion/power-calibrate: -------------------------------------------------------------------------------- 1 | # power-calibrate tab completion for bash. 2 | # 3 | # Copyright (C) 2020-2021 Canonical 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | _power-calibrate() 20 | { 21 | local cur prev words cword 22 | _init_completion || return 23 | 24 | case "$prev" in 25 | '-d') COMPREPLY=( $(compgen -W "seconds" -- $cur) ) 26 | return 0 27 | ;; 28 | '-n') COMPREPLY=( $(compgen -W "cpus" -- $cur) ) 29 | return 0 30 | ;; 31 | '-o') _filedir 32 | return 0 33 | ;; 34 | '-r') COMPREPLY=( $(compgen -W "seconds" -- $cur) ) 35 | return 0 36 | ;; 37 | '-s') COMPREPLY=( $(compgen -W "samples" -- $cur) ) 38 | return 0 39 | ;; 40 | esac 41 | 42 | case "$cur" in 43 | -*) 44 | OPTS="-d -h -n -o -p -r -R -s" 45 | COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) 46 | return 0 47 | ;; 48 | esac 49 | return 0 50 | } 51 | 52 | # load the completion 53 | complete -F _power-calibrate power-calibrate 54 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | power-calibrate (0.01.37-1) unstable; urgency=medium 2 | 3 | * debian/control: update Standards-Version to 4.7.0 4 | * Makefile: bump version 5 | * power-calibrate: re-work code to reduce number of exit calls 6 | 7 | -- Colin Ian King Wed, 17 Jul 2024 17:20:23 +0100 8 | 9 | power-calibrate (0.01.36-1) unstable; urgency=medium 10 | 11 | * Makefile: bump version 12 | * add init_values to ensure all data is initialized 13 | 14 | -- Colin Ian King Fri, 2 Feb 2024 19:23:48 +0000 15 | 16 | power-calibrate (0.01.35-1) unstable; urgency=medium 17 | 18 | * Makefile: bump version 19 | * Update copyright for Makefile and manual 20 | * debian/control: update standards version to 4.6.2 21 | * Makefile: use xz compression on tarball for make dist rule 22 | * debian/rules: add missing hardening rules 23 | * power-calibrate: improve error messages, hint to run as root 24 | 25 | -- Colin Ian King Fri, 12 Jan 2024 11:50:29 +0000 26 | 27 | power-calibrate (0.01.34-1) unstable; urgency=medium 28 | 29 | * Makefile: bump version 30 | * Makefile: add README.md to dist rule 31 | * manual: update date, author email and copyright 32 | * debian/watch: update watch path for tarball, move to version 4 33 | * debian/copyright: Update copyright and git URL 34 | * debian/control: update author email address and project URL 35 | * Add README.md 36 | 37 | -- Colin Ian King Wed, 10 Nov 2021 18:59:23 +0000 38 | 39 | power-calibrate (0.01.33-1) unstable; urgency=medium 40 | 41 | * Makefile: bump version 42 | * Ensure outputs args of monitor() are zero'd 43 | 44 | -- Colin King Wed, 28 Apr 2021 22:19:17 +0100 45 | 46 | power-calibrate (0.01.32-1) unstable; urgency=medium 47 | 48 | * Makefile: bump version 49 | * Remove deprecated siginterrupt call, replace with SA_RESTART flag 50 | * update copyright to 2021 51 | 52 | -- Colin King Fri, 19 Feb 2021 11:47:59 +0100 53 | 54 | power-calibrate (0.01.31-1) unstable; urgency=medium 55 | 56 | * Makefile: bump version 57 | * Debian/control: update compat to 13, remove compat file, 58 | add Rules-Requires-Root rule 59 | 60 | -- Colin King Sat, 4 Jul 2020 21:20:19 +0100 61 | 62 | power-calibrate (0.01.30-1) unstable; urgency=medium 63 | 64 | * Makefile: bump version 65 | * Add bash completion script 66 | * Update copyright to 2020 67 | 68 | -- Colin King Wed, 26 Feb 2020 17:11:31 +0000 69 | 70 | power-calibrate (0.01.29-1) unstable; urgency=medium 71 | 72 | * Makefile: bump version 73 | * perf: remove redundant variable rc 74 | * check for correct number of fields scanned with sscanf 75 | 76 | -- Colin King Mon, 16 Dec 2019 14:09:14 +0000 77 | 78 | power-calibrate (0.01.28-1) unstable; urgency=medium 79 | 80 | * Makefile: bump version 81 | * Debian: Update to compat level 12 82 | 83 | -- Colin King Mon, 12 Aug 2019 19:11:12 +0100 84 | 85 | power-calibrate (0.01.27-1) unstable; urgency=medium 86 | 87 | * Makefile: bump version 88 | * No need for < comparison on unsigned int 89 | * Reduce scope of cpu_cycles and cpu_instr 90 | * Use correct format specifiers 91 | * Remove a redundant duplicated if statement 92 | * Update copyright year (#2) 93 | * Update copyright year 94 | * Add travis yaml file 95 | 96 | -- Colin King Tue, 9 Jul 2019 11:29:11 +0100 97 | 98 | power-calibrate (0.01.26-1) unstable; urgency=medium 99 | 100 | * Makefile: bump version 101 | * voidify unused function returns 102 | * constify path 103 | * debian/copyright: use secure URI 104 | * debian/control: update debhelper to >= 11 105 | * debian/control: remove trailing empty line 106 | * debian/compat: update to 11 107 | 108 | -- Colin King Sun, 17 Jun 2018 13:34:42 +0100 109 | 110 | power-calibrate (0.01.25-1) unstable; urgency=medium 111 | 112 | * Makefile: bump version 113 | * debian/control: uopdate Standards-Version to 4.1.2 114 | * Fix build issue with perf-less ia64 arch 115 | * Update copyright year 116 | 117 | -- Colin King Thu, 11 Jan 2018 10:16:49 +0000 118 | 119 | power-calibrate (0.01.24-1) unstable; urgency=medium 120 | 121 | * Makefile: bump version 122 | * Manual: update copyright year and manual date 123 | * Update copyright year 124 | * Fix inconsistent indenting 125 | 126 | -- Colin King Sat, 15 Apr 2017 16:40:11 +0100 127 | 128 | power-calibrate (0.01.23-1) unstable; urgency=medium 129 | 130 | * Makefile: bump version 131 | * Move scope of variable discharging 132 | * Allow float comparisons some slop 133 | * Makefile: add PEDANTIC flags 134 | 135 | -- Colin King Wed, 2 Nov 2016 13:27:21 -0600 136 | 137 | power-calibrate (0.01.22-1) unstable; urgency=medium 138 | 139 | * Makefile: bump version 140 | * debian/control: update Standards-Version to 3.9.8 141 | * Update man page to mention perf mode restrictions 142 | * perf: detect when perf counters can't be used (LP: #1627516) 143 | 144 | -- Colin King Sun, 25 Sep 2016 09:05:53 +0100 145 | 146 | power-calibrate (0.01.21-1) unstable; urgency=medium 147 | 148 | * Makefile: bump version 149 | * Fix indentation, as spotted by gcc-6 150 | * Update and correct copyright years 151 | 152 | -- Colin King Wed, 13 Jan 2016 17:53:23 +0000 153 | 154 | power-calibrate (0.01.20-1) unstable; urgency=medium 155 | 156 | * Clear pids after each round so perf failures don't occur (LP:#1494226) 157 | * Makefile: bump version 158 | * Handle NaN values for R^2 gracefully 159 | * Only update stats if perf counter is valid 160 | * Add SI scale 'atto' prefix 161 | 162 | -- Colin King Thu, 10 Sep 2015 11:58:00 +0100 163 | 164 | power-calibrate (0.01.19-1) unstable; urgency=medium 165 | 166 | * debian/control: update Build-Depends to version 9 or above 167 | * Update debian/compat to 9 168 | * Makefile: bump version 169 | * Check for zero CPUs 170 | * move scaled onto one line 171 | * perf: make pid arg const 172 | 173 | -- Colin King Mon, 7 Sep 2015 23:44:00 +0100 174 | 175 | power-calibrate (0.01.18-1) unstable; urgency=medium 176 | 177 | * Makefile: bump version 178 | * Debian: control: fix '..' typo, make lines wider 179 | 180 | -- Colin King Tue, 4 Aug 2015 16:13:12 +0100 181 | 182 | power-calibrate (0.01.17-1) unstable; urgency=medium 183 | 184 | * Makefile: bump version 185 | * Fix units (Watts, Watt-Seconds) 186 | 187 | -- Colin King Tue, 4 Aug 2015 13:47:12 +0100 188 | 189 | power-calibrate (0.01.16-1) unstable; urgency=medium 190 | 191 | * Update debian control 192 | * Add perf.c perf.h to make dist rule 193 | * Make RAPL help hint available if RAPL is built-in 194 | * Fix build issue on non-RAPL enabled H/W 195 | * Makefile: bump version, remove -g flag 196 | * Ensure YAML output is correct for all metrics 197 | * perf: initialize i in case perf is not opened 198 | * Use strtok instead of strtok_r 199 | * perf: don't read if ioctl() failed 200 | * clear cpu_list, found by valgrind 201 | * Add more help on -n option 202 | * Add example to help 203 | * Update man page 204 | * Makefile: improve rules 205 | * Make more perf specific code build dependent 206 | * Make start delay 0 seconds for RAPL 207 | * Remove "Each " from stats 208 | * Make perf stats build time dependent 209 | * Makefile: make build depend on this 210 | * Cater for builds with perf disabled 211 | * Make monitor inline, it is called just once 212 | * Make next pointers in list start of structs for better performance 213 | * Constify func arg 214 | * Remove rapl_list global 215 | * Remove global max_cpus 216 | * Remove global num_cpus 217 | * Remove global sample_delay 218 | * Remove power_domains, it is not used 219 | * Remove global samples_cpu 220 | * Clean up of code, remove cpu_list global 221 | * Add perf statistics 222 | * Switch output from JSON to YAML as it is easier to parse for humans 223 | * Make source 80 char wide tty friendly 224 | * Remove unused struct measurement_t 225 | * Reduce scope of amps[] and volts[] 226 | * Don't intialise cpu to -1 as it gets re-assigned a little later on 227 | * Clean up func args 228 | * mwc() should be 32 bit and not 64 229 | * Fix spelling in struct comment 230 | * Remove -C, -S context switch option as this is not accurate 231 | 232 | -- Colin King Tue, 4 Aug 2015 10:45:00 +0100 233 | 234 | power-calibrate (0.01.15-1) unstable; urgency=medium 235 | 236 | * Makefile: bump version 237 | * Remove SIGILL, SIGABRT signal handling 238 | * Manual: add copyright, update date 239 | * Makefile: Add copyright 240 | 241 | -- Colin King Mon, 18 May 2015 17:38:00 +0100 242 | 243 | power-calibrate (0.01.14-1) unstable; urgency=medium 244 | 245 | * Prepare for initial upload to Debian (Closes: #782734) 246 | * Makefile: bump version 247 | 248 | -- Colin King Thu, 16 Apr 2015 16:29:00 +0500 249 | 250 | power-calibrate (0.01.13-1) unstable; urgency=medium 251 | 252 | * Makefile: bump version 253 | * Make a larger field for load x number of processors. 254 | 255 | -- Colin King Thu, 16 Apr 2015 16:20:00 +0500 256 | 257 | power-calibrate (0.01.12-1) unstable; urgency=medium 258 | 259 | * Makefile: bump version 260 | * Fix non-x86 build, remove some redundant rapl code 261 | 262 | -- Colin King Sun, 29 Mar 2015 17:15:00 +0100 263 | 264 | power-calibrate (0.01.11-1) unstable; urgency=medium 265 | 266 | * Makefile: bump version 267 | * Add -R RAPL per CPU package power measurement 268 | 269 | -- Colin King Sun, 29 Mar 2015 16:40:00 +0100 270 | 271 | power-calibrate (0.01.10-1) unstable; urgency=medium 272 | 273 | * Makefile: bump version 274 | * Add more clarification about the -c mode. 275 | * Print the gradient in %e as this generally small for context switches 276 | * Add more clarifications in the manual. 277 | 278 | -- Colin King Sun, 1 Feb 2015 17:27:11 +0000 279 | 280 | power-calibrate (0.01.09-1) unstable; urgency=medium 281 | 282 | * Makefile: Bump version 283 | * Add more comments 284 | * Guard against division by zero for 0 samples 285 | * Update the manual, add more examples 286 | * Make Ctxt/s field 1 char wider 287 | * Remove the "calibrating" message, in fact, it's warming up 288 | * Remove Voltage + Current Info, not really helpful or necessary 289 | * Add -e option, re-work output 290 | * Change the meaning of the -n option, now list the CPUs to run on. 291 | 292 | -- Colin King Wed, 21 Jan 2015 19:22:23 +0000 293 | 294 | power-calibrate (0.01.08-1) unstable; urgency=medium 295 | 296 | * Bump makefile version 297 | * Update copyright information 298 | * Add scripts to make dist 299 | * Remove scripts/foo, this was not meant to be checked in 300 | 301 | -- Colin King Sun, 18 Jan 2015 20:43:12 +0000 302 | 303 | power-calibrate (0.01.07-1) unstable; urgency=medium 304 | 305 | * Makefile: bump version 306 | * Clean up error handling messages. 307 | * Fix some issues found by Coverity Scan 308 | * power-calibrate should be installed to /usr/sbin 309 | * Minor adjustments to keep code and manual in sync and up to date 310 | * Remove unwanted capacity calculation cruft 311 | 312 | -- Colin King Sun, 18 Jan 2015 20:28:00 +0000 313 | 314 | power-calibrate (0.01.06) vivid; urgency=medium 315 | 316 | * Makefile: bump version 317 | * Update manual 318 | * Update help info 319 | * Make bogo op counters more cache friendly 320 | * Add bogo ops calculation 321 | * Correct context switch related messages 322 | * Fix context switch stats 323 | * Remove use of set_proc_name 324 | * Handle signal abort more responsively 325 | * Improved reading and error handling for stats 326 | * Check for sysconf failures 327 | * Handle unknown optarg 328 | * Replace APP_NAME with static string app_name 329 | * Improve time keeping accuracy and handle stats reading errors 330 | * Add -r option for run durations 331 | * Add -s. -S samples option, tweaks to improve cpu loading 332 | * Make context samples increment 25 instead of 10 333 | * Add -p progress option 334 | * Don't trap SIGSEGV or SIGBUS 335 | 336 | -- Colin King Sun, 18 Jan 2015 15:55:55 +0000 337 | 338 | power-calibrate (0.01.05) vivid; urgency=medium 339 | 340 | * Debian: update standards version 341 | * Makefile: bump version 342 | * Add better signal handling 343 | * Ignore errors on close, fclose, closedir failures 344 | * Handle error on time() failing 345 | * Handle gettimeofday error returns 346 | 347 | -- Colin King Tue, 23 Dec 2014 00:14:00 +0000 348 | 349 | power-calibrate (0.01.04) vivid; urgency=medium 350 | 351 | * Makefile: bump version 352 | * Forgot to replace all rand() calls with mwc() 353 | * Remove rand function, use mcw() as it is far faster 354 | * Handle zero power consumption 355 | * Add example power calibration script 356 | 357 | -- Colin King Tue, 2 Dec 2014 11:54:00 +0000 358 | 359 | power-calibrate (0.01.03) trusty; urgency=low 360 | 361 | * Makefile: bump version 362 | * Add some more const args 363 | * Add some more comments 364 | * Add author name into header 365 | * unlink json file if tests fail to run 366 | * Make max_readings non-configurable 367 | * Add -o option to documentation and internal help 368 | * Add example json parser script 369 | * Add json output 370 | 371 | -- Colin King Wed, 9 Apr 2014 14:50:00 +0100 372 | 373 | power-calibrate (0.01.02) trusty; urgency=low 374 | 375 | * Makefile: bump version 376 | * Fix incorrect current calculation, which is off by a multiple of the CPUs 377 | * Fix up stupid multiplication of 1000 for Ctxt Switches 378 | 379 | -- Colin King Tue, 8 Apr 2014 21:27:00 +0100 380 | 381 | power-calibrate (0.01.01) trusty; urgency=low 382 | 383 | * Makefile: bump version 384 | * Calculating power from charge is inaccurate, so remove it 385 | 386 | -- Colin King Tue, 8 Apr 2014 18:30:00 +0100 387 | 388 | power-calibrate (0.01.00) trusty; urgency=low 389 | 390 | * Initial version 391 | 392 | -- Colin King Tue, 8 Apr 2014 10:51:00 +0100 393 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: power-calibrate 2 | Rules-Requires-Root: no 3 | Section: admin 4 | Priority: optional 5 | Maintainer: Colin Ian King 6 | Standards-Version: 4.7.0 7 | Build-Depends: debhelper (>= 13), 8 | debhelper-compat (=13) 9 | Homepage: https://github.com/ColinIanKing/power-calibrate 10 | 11 | Package: power-calibrate 12 | Architecture: linux-any 13 | Depends: ${shlibs:Depends}, ${misc:Depends} 14 | Description: processor power calibration tool 15 | Power-calibrate calibrates the power consumption of a mobile device that 16 | has a battery power source or of an modern Intel device has RAPL support. 17 | It will attempt to calculate the power usage of 1% of CPU utilisation 18 | (and 1 instruction and 1 cpu cycle if perf is available). 19 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: power-calibrate 3 | Upstream-Contact: Colin Ian King 4 | Source: https://github.com/ColinIanKing/power-calibrate 5 | 6 | Files: * 7 | Copyright: 2014-2021, Canonical Ltd 8 | 2021-2022, Colin Ian King 9 | License: GPL-2+ 10 | On Debian systems, the complete text of the GNU General Public 11 | License can be found in `/usr/share/common-licenses/GPL-2'. 12 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 4 | DPKG_EXPORT_BUILDFLAGS = 1 5 | include /usr/share/dpkg/buildflags.mk 6 | 7 | override_dh_auto_build: 8 | $(shell dpkg-buildflags --export=sh); dh_auto_build 9 | 10 | %: 11 | dh $@ 12 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | opts="filenamemangle=s%(?:.*?)?V?(\d[\d.]*)\.tar\.gz%@PACKAGE@-$1.tar.gz%" \ 3 | https://github.com/ColinIanKing/power-calibrate/tags \ 4 | (?:.*?/)?V?(\d[\d.]*)\.tar\.gz debian uupdate 5 | 6 | -------------------------------------------------------------------------------- /perf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2021 Canonical, Ltd. 3 | * Copyright (C) 2021-2025 Colin Ian King 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | * 19 | * This code is a complete clean re-write of the stress tool by 20 | * Colin Ian King and attempts to be 21 | * backwardly compatible with the stress tool by Amos Waterland 22 | * but has more stress tests and more 23 | * functionality. 24 | * 25 | */ 26 | #define _GNU_SOURCE 27 | 28 | #include "perf.h" 29 | 30 | #if defined(PERF_ENABLED) 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #define THOUSAND (1000.0) 47 | #define MILLION (THOUSAND * THOUSAND) 48 | #define BILLION (THOUSAND * MILLION) 49 | #define TRILLION (THOUSAND * BILLION) 50 | #define QUADRILLION (THOUSAND * TRILLION) 51 | #define QUINTILLION (THOUSAND * QUADRILLION) 52 | 53 | #define PERF_INVALID (~0ULL) 54 | 55 | #define PERF_INFO(type, config) \ 56 | { PERF_ ## config, PERF_TYPE_ ## type, PERF_COUNT_ ## config } 57 | 58 | /* perf counters to be read */ 59 | static const perf_info_t perf_info[PERF_MAX] = { 60 | PERF_INFO(HARDWARE, HW_CPU_CYCLES), 61 | PERF_INFO(HARDWARE, HW_INSTRUCTIONS), 62 | }; 63 | 64 | int perf_start(perf_t *p, const pid_t pid) 65 | { 66 | int i; 67 | 68 | memset(p, 0, sizeof(perf_t)); 69 | p->perf_opened = 0; 70 | 71 | for (i = 0; i < PERF_MAX; i++) { 72 | p->perf_stat[i].fd = -1; 73 | p->perf_stat[i].counter = 0; 74 | } 75 | 76 | if (pid <= 0) 77 | return 0; 78 | 79 | for (i = 0; i < PERF_MAX; i++) { 80 | struct perf_event_attr attr; 81 | 82 | memset(&attr, 0, sizeof(attr)); 83 | attr.type = perf_info[i].type; 84 | attr.config = perf_info[i].config; 85 | attr.disabled = 1; 86 | attr.inherit = 1; 87 | attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | 88 | PERF_FORMAT_TOTAL_TIME_RUNNING; 89 | attr.size = sizeof(attr); 90 | p->perf_stat[i].fd = syscall(__NR_perf_event_open, &attr, pid, -1, -1, 0); 91 | if (p->perf_stat[i].fd > -1) 92 | p->perf_opened++; 93 | else { 94 | fprintf(stderr, "perf fail: %d %s\n", errno, strerror(errno)); 95 | } 96 | } 97 | if (!p->perf_opened) { 98 | munmap(p, sizeof(perf_t)); 99 | return -1; 100 | } 101 | 102 | for (i = 0; i < PERF_MAX; i++) { 103 | int fd = p->perf_stat[i].fd; 104 | 105 | if (fd > -1) { 106 | if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) < 0) { 107 | (void)close(fd); 108 | p->perf_stat[i].fd = -1; 109 | continue; 110 | } 111 | if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) < 0) { 112 | (void)close(fd); 113 | p->perf_stat[i].fd = -1; 114 | } 115 | } 116 | } 117 | return 0; 118 | } 119 | 120 | /* 121 | * perf_stop() 122 | * stop and read counters 123 | */ 124 | int perf_stop(perf_t *p) 125 | { 126 | /* perf data */ 127 | typedef struct { 128 | uint64_t counter; /* perf counter */ 129 | uint64_t time_enabled; /* perf time enabled */ 130 | uint64_t time_running; /* perf time running */ 131 | } perf_data_t; 132 | 133 | size_t i = 0; 134 | perf_data_t data; 135 | ssize_t ret; 136 | double scale; 137 | 138 | if (!p) 139 | return -1; 140 | if (!p->perf_opened) 141 | goto out_ok; 142 | 143 | for (i = 0; i < PERF_MAX; i++) { 144 | int fd = p->perf_stat[i].fd; 145 | 146 | if (fd < 0) { 147 | p->perf_stat[i].counter = PERF_INVALID; 148 | continue; 149 | } 150 | if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) < 0) { 151 | p->perf_stat[i].counter = 0; 152 | } else { 153 | memset(&data, 0, sizeof(data)); 154 | ret = read(fd, &data, sizeof(data)); 155 | if (ret != sizeof(data)) 156 | p->perf_stat[i].counter = PERF_INVALID; 157 | else { 158 | /* Ensure we don't get division by zero */ 159 | if (data.time_running == 0) { 160 | scale = (data.time_enabled == 0) ? 1.0 : 0.0; 161 | } else { 162 | scale = (double)data.time_enabled / (double)data.time_running; 163 | } 164 | p->perf_stat[i].counter = (uint64_t)((double)data.counter * scale); 165 | } 166 | } 167 | (void)close(fd); 168 | p->perf_stat[i].fd = -1; 169 | } 170 | out_ok: 171 | for (; i < PERF_MAX; i++) 172 | p->perf_stat[i].counter = PERF_INVALID; 173 | 174 | return 0; 175 | } 176 | 177 | /* 178 | * perf_counter 179 | * fetch counter and index via perf ID 180 | */ 181 | void perf_counter( 182 | const perf_t *p, 183 | const int id, 184 | double *counter) 185 | { 186 | int i; 187 | 188 | for (i = 0; i < PERF_MAX; i++) { 189 | if (perf_info[i].id == id) { 190 | if (p->perf_stat[i].counter == PERF_INVALID) { 191 | *counter = 0; 192 | } else { 193 | *counter = (double)p->perf_stat[i].counter; 194 | } 195 | } 196 | } 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /perf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2021 Canonical, Ltd. 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | * 18 | * This code is a complete clean re-write of the stress tool by 19 | * Colin Ian King and attempts to be 20 | * backwardly compatible with the stress tool by Amos Waterland 21 | * but has more stress tests and more 22 | * functionality. 23 | * 24 | */ 25 | #ifndef __PERF_POWER_CALIBRATE_H__ 26 | #define __PERF_POWER_CALIBRATE_H__ 27 | 28 | #define _GNU_SOURCE 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #if defined(__linux__) && defined(__NR_perf_event_open) 36 | #define PERF_ENABLED 37 | #endif 38 | 39 | enum { 40 | PERF_HW_CPU_CYCLES = 0, 41 | PERF_HW_INSTRUCTIONS, 42 | PERF_MAX 43 | }; 44 | 45 | /* per perf counter info */ 46 | typedef struct { 47 | uint64_t counter; /* perf counter */ 48 | int fd; /* perf per counter fd */ 49 | } perf_stat_t; 50 | 51 | typedef struct { 52 | perf_stat_t perf_stat[PERF_MAX]; /* perf counters */ 53 | int perf_opened; /* count of opened counters */ 54 | } perf_t; 55 | 56 | /* used for table of perf events to gather */ 57 | typedef struct { 58 | int id; /* stress-ng perf ID */ 59 | unsigned long type; /* perf types */ 60 | unsigned long config; /* perf type specific config */ 61 | } perf_info_t; 62 | 63 | extern int perf_start(perf_t *p, const pid_t pid); 64 | extern int perf_stop(perf_t *p); 65 | extern void perf_counter(const perf_t *p, const int id, double *counter); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /power-calibrate.8: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH POWER-CALIBRATE 8 "January 12, 2024" 6 | .\" Please adjust this date whenever revising the manpage. 7 | .\" 8 | .\" Some roff macros, for reference: 9 | .\" .nh disable hyphenation 10 | .\" .hy enable hyphenation 11 | .\" .ad l left justify 12 | .\" .ad b justify to both left and right margins 13 | .\" .nf disable filling 14 | .\" .fi enable filling 15 | .\" .br insert line break 16 | .\" .sp insert n+1 empty lines 17 | .\" for manpage-specific macros, see man(7) 18 | .nr SZ ((\n[.l] - \n[.i]) / 1n - 17) 19 | .SH NAME 20 | power-calibrate \- a tool to calibrate power consumption. 21 | .br 22 | 23 | .SH SYNOPSIS 24 | .B power-calibrate 25 | .RI [ options ] 26 | .br 27 | 28 | .SH DESCRIPTION 29 | Power-calibrate calibrates the power consumption of a mobile device that has a battery power source or a recent Intel processor using the RAPL (Running Average Power Limit) interface. It will attempt to calculate the power usage of 1% of CPU utiltisation. If perf is available, power-calibrate 30 | will also attempt to estimate the power consumption of 1 CPU cycle and 1 CPU instruction (one may 31 | need to run as root or set /proc/sys/kernel/perf_event_paranoid to below 2 for this to work). 32 | 33 | .SH OPTIONS 34 | power-calibrate options are as follow: 35 | .TP 36 | .B \-d 37 | specify the delay in seconds from starting a new test configuration and before starting the sampling. The default is 20 seconds, which is normally enough time to allow the battery statistics to settle down during the current test. 38 | .TP 39 | .B \-e 40 | Calibrate for each CPU run, rather than for all the CPUs. By default, power-calibrate will run tests on all the CPUs and produce one set of results for all the runs. While this provides a good average result, it may be misleading for processors where power utilisation or performance is not uniform across the processors, for example, with hyperthreading. 41 | .TP 42 | .B \-h 43 | show help. 44 | .TP 45 | .B \-n 46 | specify a list CPU numbers to run on. By default, the number of CPUs is determined automatically, but this option allows one to override this by listing the CPUs (range 0..number of CPUs-1) using a comma separated list. 47 | .TP 48 | .B \-o file 49 | output results into a YAML formatted file. 50 | .TP 51 | .B \-p 52 | show progress. This will display the progress in terms of % completion of a test run and also % completion of the total run. The progress is based on work done rather than on an accurate time estimate. 53 | .TP 54 | .B \-r secs 55 | Set run duration. Normally, the default of 120 seconds is suitable for most laptop devices where discharge rates from the battery can be fairly accurately determined over this duration. Setting this to a shorter duration will complete the calibration tests in less time but may be less accurate. 56 | .TP 57 | .B \-R 58 | read power statistics from the RAPL (Running Average Power Limit) domains. This is supported 59 | by recent Linux kernels and Sandybridge and later Intel processors. This option just measures 60 | the per processor package power utilization so the readings do not cover the entire machine. 61 | .TP 62 | .B \-s samples 63 | specify the number of samples for the CPU (\-c) test. The CPU test will measure 0% to 100% CPU loading across 1..number of CPUs. The number of test rounds to be done per CPU is controlled by the samples value. If samples is low then less data points are gathered for the calculation of the power utilisation and this leads to less accurate results. 64 | .SH OUTPUT 65 | During the testing/data sampling phase, power-calibrate will show the following information: 66 | .TS 67 | expand; 68 | lB2 lBw(\n[SZ]n) 69 | l l. 70 | Column Description 71 | CPU load T{ 72 | The loading of each CPU in terms of % utilisation for each CPU and the number of CPUs being loaded. For example, 80% x 2 means 2 CPUs each at 80% utilisation and for an 8 CPU machine this is effectivly 20% total utilisation since 6 other CPUs are idle. 73 | T} 74 | User T{ 75 | % load of the CPU consumed by user space programs. 76 | T} 77 | Sys T{ 78 | % load of the CPU consumed by the kernel. 79 | T} 80 | Idle T{ 81 | % idle time of the CPU. 82 | T} 83 | Ctxt/s T{ 84 | Context switches per second. 85 | T} 86 | IRQ/s T{ 87 | Interrupts per second. 88 | T} 89 | Ops/s T{ 90 | Bogo operations per second. This is the number of compute operations per second (computation of a random number using a 32 bit multiple-with-carry). 91 | T} 92 | Watts T{ 93 | Power used in Watts. 94 | T} 95 | .TE 96 | .br 97 | 98 | The following is an example of the output for the \-c option: 99 | .br 100 | 101 | .nf 102 | Power (Watts) = (% CPU load * 0.424631) + 10.131925 103 | Each 1% CPU load is about 424.63 mW (about 37.87 mA @ 11.21 V) 104 | Coefficient of determination R^2 = 0.999015 (very strong) 105 | 106 | Power (Watts) = (bogo op * 4.267444e-08) + 10.666399 107 | 1 bogo ops is about 42.67 nW (about 3.81 nA @ 11.21 V) 108 | Coefficient of determination R^2 = 0.999460 (very strong) 109 | .fi 110 | .br 111 | 112 | Power-calibrate will determine a suitable way of calculating the power consumed based on the CPU load and an estimation of the amount of power consumed for each 1% of CPU loading. 113 | .br 114 | 115 | The Coefficient of determination shows how close the results are to the measured data; a perfect match results in R^2 = 1.0 and this will drop towards zero as the accuracy drops. Power-calibrate will annotate R^2 to provide some hint on how strong the relationship between the raw data and the linear estimation of power consumption. 116 | .br 117 | 118 | An estimate of the power used per bogo-op is estimated, that is, the power consumed to perform 1 computation of a random number using a 64 bit multiple-with-carry. This can be used a very na\[:i]ve compute benchmarking metric when comparing different processors or power configurations. 119 | .SH EXAMPLES 120 | .LP 121 | power-calibrate -d 60 -s 5 -n 0,1 -r 200 -p 122 | .RS 123 | Measure Watts per 1% CPU (and bogo compute ops per Watt) with a 60 second warm-up delay per test round, 5 tests in the CPU load level (0%, 25%, 50%, 75%, 100%) on CPUs 0 and 1 with a 200 second run time per test round while showing progress. 124 | .RE 125 | .LP 126 | power-calibrate -n 0,2,1,3 127 | .RS 128 | Measure Watts per 1% CPU (and bogo compute oper per Watt) on 4 CPUs. 4 rounds of tests will be run for each CPU load interval, measuring: 129 | .RE 130 | .TS 131 | center; 132 | l l. 133 | 1 CPU: CPU 0 134 | 2 CPUs: CPUs 0,2 135 | 3 CPUs: CPUs 0,2,1 136 | 4 CPUs: CPUs 0,2,1,3 137 | .TE 138 | .LP 139 | power-calibrate -R -r 10 -d 5 -s 21 -n 0 -p 140 | .RS 141 | Measure per-CPU package Watts consumed using the Intel RAPL interface. This example has a 5 second warm-up delay per test round, 21 tests in the CPU load level (0%, 5%, 10%, .., 95%, 100%) on CPUs 0 with a 10 second run time per test round while showing progress. 142 | 143 | .SH BUGS 144 | Power-calibrate attempts to find a linear relationship between power consumed and the CPU loading, bogo operations per second and the context switching. This is not necessarily the case for all processors. If the R^2 coefficient of determination is not close to 1.0 then this indicates there may not be a linear relationship. 145 | .SH NOTES 146 | Power-calibrate relies on the battery interface to provide timely stats on battery power consumption and this will vary from device to device. Software or firmware may adjust the battery readings from gas-gauges and even re-calibrate the values during battery drain hence skewing the final results from power-calibrate. Batteries also show a non-linear discharge characteristic, so running power-calibrate on low battery charge is not advised. 147 | .br 148 | 149 | Processors with hyperthreading or non-uniform processing performance will skew the bogo-ops calculations since different logical CPUs will vary in processing throughput. 150 | .br 151 | 152 | In general, the more samples gathered, the more accurate the final results will be, however, the calibration will take longer to run. With more samples, the tendency to get a few random outliers in the samples may increase, and that may affect the final R^2 coefficient of determination. 153 | .br 154 | 155 | Power-calibrate is a power estimation tool, so results will vary between different runs. It is not meant to be an accurate substitute for power measurements using a high precision multimeter. 156 | .SH SEE ALSO 157 | .BR powerstat (8), 158 | .BR eventstat (8) 159 | .SH AUTHOR 160 | power-calibrate was written by Colin Ian King 161 | .PP 162 | This manual page was written by Colin Ian King , for the Ubuntu project (but may be used by others). 163 | .SH COPYRIGHT 164 | Copyright \(co 2014-2021 Canonical Ltd, Copyright \(co 2021-2025 Colin Ian King. 165 | .br 166 | This is free software; see the source for copying conditions. There is NO 167 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 168 | -------------------------------------------------------------------------------- /power-calibrate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2021 Canonical 3 | * Copyright (C) 2021-2025 Colin Ian King 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | * 19 | * Author: Colin Ian King 20 | * 21 | * Much of this code was derived from other GPL-2+ projects by the author 22 | * such as powerstat, fnotifystat and stress-ng. 23 | */ 24 | #define _GNU_SOURCE 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include "perf.h" 57 | 58 | #define MIN_RUN_DURATION (10) /* Minimum run duration */ 59 | #define DEFAULT_RUN_DURATION (120) /* Default duration */ 60 | #define SAMPLE_DELAY (1) /* Delay between samples in seconds */ 61 | #define START_DELAY_BATTERY (20) /* Delay to wait before sampling */ 62 | #define START_DELAY_RAPL (0) /* Delay to wait before sampling */ 63 | #define RATE_ZERO_LIMIT (0.001) /* Less than this is a 0 power rate */ 64 | #define MAX_CPU_LOAD (100) /* Maximum CPU load */ 65 | #define DEFAULT_TIMEOUT (10) /* Zero load sleep duration */ 66 | #define CPU_ANY (-1) 67 | 68 | #define DETECT_DISCHARGING (1) 69 | 70 | #define OPT_DELAY (0x00000001) 71 | #define OPT_PROGRESS (0x00000002) 72 | #define OPT_CALIBRATE_EACH_CPU (0x00000004) 73 | #define OPT_RAPL (0x00000008) 74 | 75 | #define MAX_POWER_DOMAINS (16) 76 | #define MAX_POWER_VALUES (MAX_POWER_DOMAINS + 1) 77 | 78 | #define CPU_USER (0) 79 | #define CPU_NICE (1) 80 | #define CPU_SYS (2) 81 | #define CPU_IDLE (3) 82 | #define CPU_IOWAIT (4) 83 | #define CPU_IRQ (5) 84 | #define CPU_SOFTIRQ (6) 85 | #define CPU_INTR (7) 86 | #define CPU_CTXT (8) 87 | #define CPU_PROCS_RUN (9) 88 | #define CPU_PROCS_BLK (10) 89 | #define VOLTAGE_NOW (11) 90 | #define CURRENT_NOW (12) 91 | #define PROC_FORK (13) 92 | #define PROC_EXEC (14) 93 | #define PROC_EXIT (15) 94 | #define BOGO_OPS (16) 95 | #define CPU_CYCLES (17) 96 | #define CPU_INSTRUCTIONS (18) 97 | #define POWER_NOW (19) 98 | #define POWER_DOMAIN_0 (20) 99 | #define MAX_VALUES (POWER_DOMAIN_0 + MAX_POWER_VALUES) 100 | 101 | #define MWC_SEED_Z (362436069UL) 102 | #define MWC_SEED_W (521288629UL) 103 | 104 | #define SYS_CLASS_POWER_SUPPLY "/sys/class/power_supply" 105 | #define PROC_ACPI_BATTERY "/proc/acpi/battery" 106 | 107 | #define FLOAT_TINY (0.0000001) 108 | #define FLOAT_CMP(a, b) (fabs(a - b) < FLOAT_TINY) 109 | 110 | #define SYS_FIELD_VOLTAGE_NOW "POWER_SUPPLY_VOLTAGE_NOW=" 111 | #define SYS_FIELD_POWER_NOW "POWER_SUPPLY_POWER_NOW=" 112 | #define SYS_FIELD_ENERGY_NOW "POWER_SUPPLY_ENERGY_NOW=" 113 | #define SYS_FIELD_CURRENT_NOW "POWER_SUPPLY_CURRENT_NOW=" 114 | #define SYS_FIELD_CHARGE_NOW "POWER_SUPPLY_CHARGE_NOW=" 115 | #define SYS_FIELD_STATUS_DISCHARGING "POWER_SUPPLY_STATUS=Discharging" 116 | 117 | 118 | #if defined(__x86_64__) || defined(__x86_64) || \ 119 | defined(__i386__) || defined(__i386) 120 | #define RAPL_X86 121 | #endif 122 | 123 | /* Statistics entry */ 124 | typedef struct { 125 | double value[MAX_VALUES]; 126 | bool inaccurate[MAX_VALUES]; 127 | } stats_t; 128 | 129 | /* x,y data pair, for trend analysis */ 130 | typedef struct { 131 | double x; 132 | double y; 133 | 134 | double voltage; 135 | int cpu_id; 136 | int cpus_used; 137 | } value_t; 138 | 139 | /* Bogo operation stats */ 140 | typedef struct { 141 | double ops; 142 | uint8_t padding[64]; /* Make ops not align on cache boundary */ 143 | } bogo_ops_t; 144 | 145 | /* CPUs to use from -n option */ 146 | typedef struct cpu_info { 147 | struct cpu_info *next; 148 | int cpu_id; /* CPU number, 0 = first CPU */ 149 | pid_t pid; 150 | perf_t perf; 151 | } cpu_info_t; 152 | 153 | /* CPU list */ 154 | typedef struct cpu_list { 155 | cpu_info_t *head; 156 | cpu_info_t *tail; 157 | uint32_t count; 158 | } cpu_list_t; 159 | 160 | /* RAPL domain info */ 161 | typedef struct rapl_info { 162 | struct rapl_info *next; 163 | char *name; 164 | char *domain_name; 165 | double max_energy_uj; 166 | double last_energy_uj; 167 | double t_last; 168 | bool is_package; 169 | } rapl_info_t; 170 | 171 | typedef void (*func)( 172 | uint64_t param, const int instance, bogo_ops_t *bogo_ops); 173 | 174 | static volatile bool stop_flag; /* sighandler stop flag */ 175 | static int32_t opt_flags; /* command options */ 176 | static char *app_name = "power-calibrate"; /* application name */ 177 | static bool perf_enabled = false; /* true if we can access perf */ 178 | 179 | /* 180 | * Attempt to catch a range of signals so 181 | * we can clean 182 | */ 183 | static const int signals[] = { 184 | /* POSIX.1-1990 */ 185 | #ifdef SIGHUP 186 | SIGHUP, 187 | #endif 188 | #ifdef SIGINT 189 | SIGINT, 190 | #endif 191 | #ifdef SIGQUIT 192 | SIGQUIT, 193 | #endif 194 | #ifdef SIGFPE 195 | SIGFPE, 196 | #endif 197 | #ifdef SIGTERM 198 | SIGTERM, 199 | #endif 200 | #ifdef SIGUSR1 201 | SIGUSR1, 202 | #endif 203 | #ifdef SIGUSR2 204 | SIGUSR2, 205 | /* POSIX.1-2001 */ 206 | #endif 207 | #ifdef SIGXCPU 208 | SIGXCPU, 209 | #endif 210 | #ifdef SIGXFSZ 211 | SIGXFSZ, 212 | #endif 213 | /* Linux various */ 214 | #ifdef SIGIOT 215 | SIGIOT, 216 | #endif 217 | #ifdef SIGSTKFLT 218 | SIGSTKFLT, 219 | #endif 220 | #ifdef SIGPWR 221 | SIGPWR, 222 | #endif 223 | #ifdef SIGINFO 224 | SIGINFO, 225 | #endif 226 | #ifdef SIGVTALRM 227 | SIGVTALRM, 228 | #endif 229 | -1, 230 | }; 231 | 232 | /* 233 | * perf_possible() 234 | * check if perf can be run 235 | */ 236 | static bool perf_possible(void) 237 | { 238 | #if defined(PERF_ENABLED) 239 | static const char *path = "/proc/sys/kernel/perf_event_paranoid"; 240 | FILE *fp; 241 | int level; 242 | 243 | if (geteuid() == 0) 244 | return true; 245 | 246 | if ((fp = fopen(path, "r")) == NULL) 247 | return false; 248 | if (fscanf(fp, "%5d", &level) < 1) { 249 | (void)fclose(fp); 250 | return false; 251 | } 252 | (void)fclose(fp); 253 | 254 | return (level < 2); 255 | #else 256 | return false; 257 | #endif 258 | } 259 | 260 | /* 261 | * units_to_str() 262 | * units to strings 263 | */ 264 | static char *units_to_str( 265 | const double val, 266 | char *units, 267 | char *const buf, 268 | const size_t buflen) 269 | { 270 | double v = (double)val; 271 | size_t i; 272 | static const char *scales[] = { "", "m", "µ", "n", "p", "f", "a", NULL }; 273 | 274 | for (i = 0; i < 6; i++, v *= 1000) { 275 | if (v > 0.5) 276 | break; 277 | } 278 | (void)snprintf(buf, buflen, "%.2f %s%s", v, scales[i], units); 279 | return buf; 280 | } 281 | /* 282 | * value_to_str() 283 | * values to strings 284 | */ 285 | static char *value_to_str( 286 | const double val, 287 | const bool inaccurate, 288 | char *const buf, 289 | const size_t buflen) 290 | { 291 | if (inaccurate) { 292 | (void)snprintf(buf, buflen, "-N/A-"); 293 | } else { 294 | double v = (double)val; 295 | size_t i; 296 | static const char scales[] = " KMBTPE"; 297 | 298 | for (i = 0; i < sizeof(scales) - 1; i++, v /= 1000) { 299 | if (v <= 500) 300 | break; 301 | } 302 | (void)snprintf(buf, buflen, "%5.1f%c", v, scales[i]); 303 | } 304 | return buf; 305 | } 306 | 307 | /* 308 | * timeval_to_double 309 | * timeval to a double (in seconds) 310 | */ 311 | static inline double timeval_to_double(const struct timeval *const tv) 312 | { 313 | return (double)tv->tv_sec + ((double)tv->tv_usec / 1000000.0); 314 | } 315 | 316 | /* 317 | * double_to_timeval 318 | * seconds in double to timeval 319 | */ 320 | static inline struct timeval double_to_timeval(const double val) 321 | { 322 | struct timeval tv; 323 | 324 | tv.tv_sec = val; 325 | tv.tv_usec = (val - (time_t)val) * 1000000.0; 326 | 327 | return tv; 328 | } 329 | 330 | /* 331 | * gettime_to_double() 332 | * get time as a double 333 | */ 334 | static double gettime_to_double(void) 335 | { 336 | struct timeval tv; 337 | 338 | if (gettimeofday(&tv, NULL) < 0) { 339 | (void)fprintf(stderr, "gettimeofday failed: errno=%d (%s).\n", 340 | errno, strerror(errno)); 341 | return -1.0; 342 | } 343 | return timeval_to_double(&tv); 344 | } 345 | 346 | /* 347 | * mwc() 348 | * fast pseudo random number generator, see 349 | * http://www.cse.yorku.ca/~oz/marsaglia-rng.html 350 | */ 351 | static inline uint32_t mwc(void) 352 | { 353 | static uint32_t w = MWC_SEED_W, z = MWC_SEED_Z; 354 | 355 | z = 36969 * (z & 65535) + (z >> 16); 356 | w = 18000 * (w & 65535) + (w >> 16); 357 | return (z << 16) + w; 358 | } 359 | 360 | /* 361 | * handle_sig() 362 | * catch signal and flag a stop 363 | */ 364 | static void handle_sig(int dummy) 365 | { 366 | (void)dummy; 367 | stop_flag = true; 368 | } 369 | 370 | /* 371 | * set_affinity() 372 | * set cpu affinity 373 | */ 374 | static int set_affinity(const int cpu) 375 | { 376 | cpu_set_t mask; 377 | int ret; 378 | 379 | CPU_ZERO(&mask); 380 | CPU_SET(cpu, &mask); 381 | ret = sched_setaffinity(0, sizeof(mask), &mask); 382 | if (ret < 0) { 383 | (void)fprintf(stderr, "sched_setffinity failed: errno=%d (%s).\n", 384 | errno, strerror(errno)); 385 | return -1; 386 | } 387 | return 0; 388 | } 389 | 390 | /* 391 | * stress_cpu() 392 | * stress CPU 393 | */ 394 | static void stress_cpu( 395 | const uint64_t cpu_load, 396 | const int instance, 397 | bogo_ops_t *bogo_ops) 398 | { 399 | /* 400 | * Normal use case, 100% load, simple spinning on CPU 401 | */ 402 | if (cpu_load == 100) { 403 | uint64_t i; 404 | 405 | for (;;) { 406 | for (i = 0; i < 1000000; i++) { 407 | #if __GNUC__ 408 | /* Stop optimising out */ 409 | __asm__ __volatile__(""); 410 | #endif 411 | (void)mwc(); 412 | if (stop_flag) { 413 | bogo_ops[instance].ops += i; 414 | goto finish; 415 | } 416 | } 417 | bogo_ops[instance].ops += i; 418 | } 419 | } else if (cpu_load == 0) { 420 | for (;;) { 421 | (void)sleep(DEFAULT_TIMEOUT); 422 | if (stop_flag) 423 | goto finish; 424 | } 425 | } else { 426 | /* 427 | * More complex percentage CPU utilisation. This is 428 | * not intended to be 100% accurate timing, it is good 429 | * enough for most purposes. 430 | */ 431 | for (;;) { 432 | uint64_t i; 433 | double time_start, delay; 434 | struct timeval tv; 435 | 436 | time_start = gettime_to_double(); 437 | for (i = 0; i < 1000000; i++) { 438 | #if __GNUC__ 439 | /* Stop optimising out */ 440 | __asm__ __volatile__(""); 441 | #endif 442 | (void)mwc(); 443 | if (stop_flag) { 444 | bogo_ops[instance].ops += i; 445 | exit(EXIT_SUCCESS); 446 | } 447 | } 448 | bogo_ops[instance].ops += i; 449 | delay = gettime_to_double() - time_start; 450 | /* Must not calculate this with zero % load */ 451 | delay *= (((100.0 / (double) cpu_load)) - 1.0); 452 | tv = double_to_timeval(delay); 453 | (void)select(0, NULL, NULL, NULL, &tv); 454 | } 455 | } 456 | finish: 457 | exit(EXIT_SUCCESS); 458 | } 459 | 460 | /* 461 | * stop_load() 462 | * kill load child processes 463 | */ 464 | static void stop_load(cpu_list_t *cpu_list, const int total_procs) 465 | { 466 | int i; 467 | cpu_info_t *c; 468 | 469 | /* Kill.. */ 470 | for (c = cpu_list->head, i = 0; c && i < total_procs; c = c->next, i++) { 471 | if (c->pid > -1) 472 | (void)kill(c->pid, SIGKILL); 473 | } 474 | /* And ensure we don't get zombies */ 475 | for (c = cpu_list->head, i = 0; c && i < total_procs; c = c->next, i++) { 476 | if (c->pid > -1) { 477 | int status; 478 | 479 | (void)waitpid(c->pid, &status, 0); 480 | } 481 | c->pid = -1; 482 | } 483 | } 484 | 485 | /* 486 | * start_load() 487 | * load system with some stress processes 488 | */ 489 | static void start_load( 490 | cpu_list_t *cpu_list, 491 | const int total_procs, 492 | const func load_func, 493 | const uint64_t param, 494 | bogo_ops_t *bogo_ops) 495 | { 496 | int i; 497 | struct sigaction new_action; 498 | cpu_info_t *c; 499 | 500 | memset(&new_action, 0, sizeof(new_action)); 501 | for (i = 0; signals[i] != -1; i++) { 502 | new_action.sa_handler = handle_sig; 503 | (void)sigemptyset(&new_action.sa_mask); 504 | new_action.sa_flags = SA_RESTART; 505 | 506 | (void)sigaction(signals[i], &new_action, NULL); 507 | } 508 | 509 | for (c = cpu_list->head, i = 0; 510 | c && i < total_procs; c = c->next, i++) { 511 | c->pid = fork(); 512 | 513 | switch (c->pid) { 514 | case -1: 515 | (void)fprintf(stderr, "Cannot fork, errno=%d (%s)\n", 516 | errno, strerror(errno)); 517 | stop_load(cpu_list, i); 518 | exit(EXIT_FAILURE); 519 | case 0: 520 | /* Child */ 521 | if (set_affinity(c->cpu_id) < 0) 522 | exit(0); 523 | load_func(param, i, bogo_ops); 524 | exit(0); 525 | default: 526 | break; 527 | } 528 | } 529 | } 530 | 531 | /* 532 | * file_get() 533 | * read a line from a /sys file 534 | */ 535 | static char *file_get(const char *const file) 536 | { 537 | FILE *fp; 538 | char buffer[4096]; 539 | 540 | if ((fp = fopen(file, "r")) == NULL) 541 | return NULL; 542 | 543 | if (fgets(buffer, sizeof(buffer), fp) == NULL) { 544 | (void)fclose(fp); 545 | return NULL; 546 | } 547 | (void)fclose(fp); 548 | 549 | return strdup(buffer); 550 | } 551 | 552 | /* 553 | * get_time() 554 | * Gather current time in buffer 555 | */ 556 | static void get_time(char *const buffer, const size_t buflen) 557 | { 558 | struct tm tm; 559 | time_t now; 560 | 561 | now = time(NULL); 562 | if (now == ((time_t) -1)) { 563 | (void)snprintf(buffer, buflen, "--:--:-- "); 564 | } else { 565 | (void)localtime_r(&now, &tm); 566 | (void)snprintf(buffer, buflen, "%2.2d:%2.2d:%2.2d ", 567 | tm.tm_hour, tm.tm_min, tm.tm_sec); 568 | } 569 | } 570 | 571 | /* 572 | * stats_clear() 573 | * clear stats 574 | */ 575 | static void stats_clear(stats_t *const stats) 576 | { 577 | int i; 578 | 579 | for (i = 0; i < MAX_VALUES; i++) { 580 | stats->value[i] = 0.0; 581 | stats->inaccurate[i] = false; 582 | } 583 | } 584 | 585 | /* 586 | * stats_clear_all() 587 | * zero stats data 588 | */ 589 | static void stats_clear_all(stats_t *const stats, const int n) 590 | { 591 | int i; 592 | 593 | for (i = 0; i < n; i++) 594 | stats_clear(&stats[i]); 595 | } 596 | 597 | 598 | /* 599 | * stats_read() 600 | * gather pertinent /proc/stat data 601 | */ 602 | static int stats_read( 603 | const int32_t num_cpus, 604 | stats_t *const stats, 605 | bogo_ops_t *bogo_ops) 606 | { 607 | FILE *fp; 608 | char buf[4096]; 609 | int i, j; 610 | 611 | static const int indices[] = { 612 | CPU_USER, CPU_NICE, CPU_SYS, CPU_IDLE, 613 | CPU_IOWAIT, CPU_IRQ, CPU_SOFTIRQ, CPU_CTXT, 614 | CPU_INTR, CPU_PROCS_RUN, CPU_PROCS_BLK, -1 615 | }; 616 | 617 | for (i = 0; (j = indices[i]) != -1; i++) { 618 | stats->value[j] = 0.0; 619 | stats->inaccurate[j] = true; 620 | } 621 | 622 | if ((fp = fopen("/proc/stat", "r")) == NULL) { 623 | (void)fprintf(stderr, "Cannot read /proc/stat, errno=%d (%s).\n", 624 | errno, strerror(errno)); 625 | return -1; 626 | } 627 | 628 | while (fgets(buf, sizeof(buf), fp) != NULL) { 629 | if (strncmp(buf, "cpu ", 4) == 0) 630 | if (sscanf(buf, "%*s %15lf %15lf %15lf %15lf %15lf %15lf %15lf", 631 | &(stats->value[CPU_USER]), 632 | &(stats->value[CPU_NICE]), 633 | &(stats->value[CPU_SYS]), 634 | &(stats->value[CPU_IDLE]), 635 | &(stats->value[CPU_IOWAIT]), 636 | &(stats->value[CPU_IRQ]), 637 | &(stats->value[CPU_SOFTIRQ])) == 7) { 638 | stats->inaccurate[CPU_USER] = false; 639 | stats->inaccurate[CPU_NICE] = false; 640 | stats->inaccurate[CPU_SYS] = false; 641 | stats->inaccurate[CPU_IDLE] = false; 642 | stats->inaccurate[CPU_IOWAIT] = false; 643 | stats->inaccurate[CPU_IRQ] = false; 644 | stats->inaccurate[CPU_SOFTIRQ] = false; 645 | } 646 | if (strncmp(buf, "ctxt ", 5) == 0) 647 | if (sscanf(buf, "%*s %15lf", &(stats->value[CPU_CTXT])) == 1) 648 | stats->inaccurate[CPU_CTXT] = false; 649 | if (strncmp(buf, "intr ", 5) == 0) 650 | if (sscanf(buf, "%*s %15lf", &(stats->value[CPU_INTR])) == 1) 651 | stats->inaccurate[CPU_INTR] = false; 652 | if (strncmp(buf, "procs_running ", 14) == 0) 653 | if (sscanf(buf, "%*s %15lf", 654 | &(stats->value[CPU_PROCS_RUN])) == 1) 655 | stats->inaccurate[CPU_PROCS_RUN] = false; 656 | if (strncmp(buf, "procs_blocked ", 14) == 0) 657 | if (sscanf(buf, "%*s %15lf", 658 | &(stats->value[CPU_PROCS_BLK])) == 1) 659 | stats->inaccurate[CPU_PROCS_BLK] = false; 660 | } 661 | (void)fclose(fp); 662 | 663 | stats->value[BOGO_OPS] = 0; 664 | stats->inaccurate[BOGO_OPS] = false; 665 | for (i = 0; i < num_cpus; i++) { 666 | stats->value[BOGO_OPS] += bogo_ops[i].ops; 667 | } 668 | 669 | return 0; 670 | } 671 | 672 | /* 673 | * stats_sane() 674 | * check if stats are accurate and calculate a 675 | * sane -ve delta 676 | */ 677 | static double stats_sane( 678 | const stats_t *const s1, 679 | const stats_t *const s2, 680 | const int index) 681 | { 682 | double ret; 683 | 684 | /* Discard inaccurate or empty stats */ 685 | if (s1->inaccurate[index] || s2->inaccurate[index]) 686 | return 0.0; 687 | 688 | /* 689 | * On Nexus 4 we occasionally get idle time going backwards so 690 | * work around this by ensuring we don't get -ve deltas. 691 | */ 692 | ret = s2->value[index] - s1->value[index]; 693 | return ret < 0.0 ? 0.0 : ret; 694 | } 695 | 696 | #define INACCURATE(s1, s2, index) \ 697 | (s1->inaccurate[index] | s2->inaccurate[index]) 698 | 699 | /* 700 | * stats_gather() 701 | * gather up delta between last stats and current to get 702 | * some form of per sample accounting calculated. 703 | */ 704 | static bool stats_gather( 705 | cpu_list_t *cpu_list, 706 | const uint32_t sample_delay, 707 | const stats_t *const s1, 708 | const stats_t *const s2, 709 | stats_t *const res) 710 | { 711 | double total; 712 | int i, j; 713 | bool inaccurate = false; 714 | 715 | static const int indices[] = { 716 | CPU_USER, CPU_NICE, CPU_SYS, CPU_IDLE, 717 | CPU_IOWAIT, -1 718 | }; 719 | 720 | res->value[CPU_USER] = stats_sane(s1, s2, CPU_USER); 721 | res->value[CPU_NICE] = stats_sane(s1, s2, CPU_NICE); 722 | res->value[CPU_SYS] = stats_sane(s1, s2, CPU_SYS); 723 | res->value[CPU_IDLE] = stats_sane(s1, s2, CPU_IDLE); 724 | res->value[CPU_IOWAIT] = stats_sane(s1, s2, CPU_IOWAIT); 725 | res->value[CPU_IRQ] = stats_sane(s1, s2, CPU_IRQ); 726 | res->value[CPU_SOFTIRQ] = stats_sane(s1, s2, CPU_SOFTIRQ); 727 | res->value[CPU_CTXT] = stats_sane(s1, s2, CPU_CTXT); 728 | res->value[CPU_INTR] = stats_sane(s1, s2, CPU_INTR); 729 | res->value[BOGO_OPS] = stats_sane(s1, s2, BOGO_OPS); 730 | 731 | res->value[CPU_CYCLES] = 0.0; 732 | res->inaccurate[CPU_CYCLES] = false; 733 | res->value[CPU_INSTRUCTIONS] = 0.0; 734 | res->inaccurate[CPU_INSTRUCTIONS] = false; 735 | 736 | #if defined(PERF_ENABLED) 737 | if (perf_enabled) { 738 | cpu_info_t *c; 739 | 740 | for (c = cpu_list->head; c; c = c->next) { 741 | double value; 742 | 743 | perf_counter(&c->perf, PERF_HW_CPU_CYCLES, &value); 744 | if (value > 0.0) 745 | res->value[CPU_CYCLES] += value; 746 | perf_counter(&c->perf, PERF_HW_INSTRUCTIONS, &value); 747 | if (value > 0.0) 748 | res->value[CPU_INSTRUCTIONS] += value; 749 | } 750 | } 751 | #else 752 | (void)cpu_list; 753 | #endif 754 | 755 | for (i = 0; (j = indices[i]) != -1; i++) 756 | inaccurate |= (s1->inaccurate[j] | s2->inaccurate[j]); 757 | 758 | total = res->value[CPU_USER] + res->value[CPU_NICE] + 759 | res->value[CPU_SYS] + res->value[CPU_IDLE] + 760 | res->value[CPU_IOWAIT]; 761 | 762 | /* 763 | * This should not happen, but we need to avoid division 764 | * by zero or weird results if the data is deemed valid 765 | */ 766 | if (!inaccurate && total <= 0.0) 767 | return false; 768 | 769 | for (i = 0; (j = indices[i]) != -1; i++) { 770 | res->value[j] = (INACCURATE(s1, s2, j) || (total <= 0.0)) ? 771 | NAN : (100.0 * res->value[j]) / total; 772 | } 773 | res->value[CPU_CTXT] = (INACCURATE(s1, s2, CPU_CTXT) || 774 | (sample_delay <= 0.0)) ? 775 | NAN : res->value[CPU_CTXT] / sample_delay; 776 | res->value[CPU_INTR] = (INACCURATE(s1, s2, CPU_INTR) || 777 | (sample_delay <= 0.0)) ? 778 | NAN : res->value[CPU_INTR] / sample_delay; 779 | res->value[BOGO_OPS] = (INACCURATE(s1, s2, BOGO_OPS) || 780 | (sample_delay <= 0.0)) ? 781 | NAN : res->value[BOGO_OPS] / sample_delay; 782 | res->value[CPU_PROCS_RUN] = s2->inaccurate[CPU_PROCS_RUN] ? 783 | NAN : s2->value[CPU_PROCS_RUN]; 784 | res->value[CPU_PROCS_BLK] = s2->inaccurate[CPU_PROCS_BLK] ? 785 | NAN : s2->value[CPU_PROCS_BLK]; 786 | 787 | return true; 788 | } 789 | 790 | /* 791 | * stats_headings() 792 | * dump heading columns 793 | */ 794 | static void stats_headings(const char *test) 795 | { 796 | #if defined(PERF_ENABLED) 797 | if (perf_enabled) { 798 | (void)printf("%10.10s User Sys Idle Run Ctxt/s IRQ/s Ops/s " 799 | "Cycl/s Inst/s Watts\n", test); 800 | } else 801 | #endif 802 | { 803 | (void)printf("%10.10s User Sys Idle Run Ctxt/s IRQ/s Ops/s " 804 | " Watts\n", test); 805 | } 806 | } 807 | 808 | /* 809 | * stats_print() 810 | * print out statistics with accuracy depending if it's a summary or not 811 | */ 812 | static void stats_print( 813 | const char *const prefix, 814 | const bool summary, 815 | const stats_t *const s) 816 | { 817 | char buf[10], bogo_ops[10]; 818 | char *fmt; 819 | 820 | if (summary) { 821 | if (s->inaccurate[POWER_NOW]) 822 | (void)snprintf(buf, sizeof(buf), "-N/A-"); 823 | else 824 | (void)snprintf(buf, sizeof(buf), "%6.3f", s->value[POWER_NOW]); 825 | } else { 826 | (void)snprintf(buf, sizeof(buf), "%6.3f%s", s->value[POWER_NOW], 827 | s->inaccurate[POWER_NOW] ? "E" : ""); 828 | } 829 | 830 | value_to_str(s->value[BOGO_OPS], s->inaccurate[BOGO_OPS], 831 | bogo_ops, sizeof(bogo_ops)); 832 | if (perf_enabled) { 833 | char cpu_cycles[10], cpu_instr[10]; 834 | 835 | value_to_str(s->value[CPU_CYCLES], s->inaccurate[CPU_CYCLES], 836 | cpu_cycles, sizeof(cpu_cycles)); 837 | value_to_str(s->value[CPU_INSTRUCTIONS], s->inaccurate[CPU_INSTRUCTIONS], 838 | cpu_instr, sizeof(cpu_instr)); 839 | fmt = summary ? 840 | "%10.10s %5.1f %5.1f %5.1f %4.1f %7.1f %6.1f %6s %6s %6s %s\n" : 841 | "%10.10s %5.1f %5.1f %5.1f %4.0f %7.0f %6.0f %6s %6s %6s %s\n"; 842 | (void)printf(fmt, 843 | prefix, 844 | s->value[CPU_USER], s->value[CPU_SYS], s->value[CPU_IDLE], 845 | s->value[CPU_PROCS_RUN], s->value[CPU_CTXT], 846 | s->value[CPU_INTR], bogo_ops, cpu_cycles, cpu_instr, buf); 847 | } else { 848 | fmt = summary ? 849 | "%10.10s %5.1f %5.1f %5.1f %4.1f %7.1f %6.1f %6s %s\n" : 850 | "%10.10s %5.1f %5.1f %5.1f %4.0f %7.0f %6.0f %6s %s\n"; 851 | (void)printf(fmt, 852 | prefix, 853 | s->value[CPU_USER], s->value[CPU_SYS], s->value[CPU_IDLE], 854 | s->value[CPU_PROCS_RUN], s->value[CPU_CTXT], 855 | s->value[CPU_INTR], bogo_ops, buf); 856 | } 857 | } 858 | 859 | /* 860 | * stats_average_stddev_min_max() 861 | * calculate average, std deviation, min and max 862 | */ 863 | static void stats_average_stddev_min_max( 864 | const stats_t *const stats, 865 | const int num, 866 | stats_t *const average, 867 | stats_t *const stddev) 868 | { 869 | int i, j, valid; 870 | 871 | for (j = 0; j < MAX_VALUES; j++) { 872 | double total = 0.0; 873 | 874 | for (valid = 0, i = 0; i < num; i++) { 875 | if (!stats[i].inaccurate[j]) { 876 | total += stats[i].value[j]; 877 | valid++; 878 | } 879 | } 880 | 881 | if (valid) { 882 | average->value[j] = total / (double)valid; 883 | total = 0.0; 884 | for (i = 0; i < num; i++) { 885 | if (!stats[i].inaccurate[j]) { 886 | double diff = (double)stats[i].value[j] 887 | - average->value[j]; 888 | diff = diff * diff; 889 | total += diff; 890 | } 891 | } 892 | stddev->value[j] = total / (double)num; 893 | stddev->value[j] = sqrt(stddev->value[j]); 894 | } else { 895 | average->inaccurate[j] = true; 896 | stddev->inaccurate[j] = true; 897 | 898 | average->value[j] = 0.0; 899 | stddev->value[j] = 0.0; 900 | } 901 | } 902 | } 903 | 904 | /* 905 | * power_get_sys_fs() 906 | * get power discharge rate from battery via /sys interface 907 | */ 908 | static int power_get_sys_fs( 909 | stats_t *stats, 910 | bool *const discharging, 911 | bool *const inaccurate) 912 | { 913 | DIR *dir; 914 | struct dirent *dirent; 915 | double total_watts = 0.0; 916 | double average_voltage = 0.0; 917 | int n = 0; 918 | 919 | stats->value[POWER_NOW] = 0.0; 920 | stats->value[VOLTAGE_NOW] = 0.0; 921 | stats->value[CURRENT_NOW] = 0.0; 922 | *discharging = false; 923 | *inaccurate = true; 924 | 925 | if ((dir = opendir(SYS_CLASS_POWER_SUPPLY)) == NULL) { 926 | (void)fprintf(stderr, "Machine does not have %s, cannot run the test.\n", 927 | SYS_CLASS_POWER_SUPPLY); 928 | return -1; 929 | } 930 | 931 | do { 932 | dirent = readdir(dir); 933 | if (dirent && strlen(dirent->d_name) > 2) { 934 | char path[PATH_MAX]; 935 | char *data; 936 | int val; 937 | FILE *fp; 938 | 939 | /* Check that type field matches the expected type */ 940 | (void)snprintf(path, sizeof(path), "%s/%s/type", 941 | SYS_CLASS_POWER_SUPPLY, dirent->d_name); 942 | if ((data = file_get(path)) != NULL) { 943 | bool mismatch = (strstr(data, "Battery") == NULL); 944 | free(data); 945 | if (mismatch) 946 | continue; /* type don't match, skip this entry */ 947 | } else 948 | continue; /* can't check type, skip this entry */ 949 | 950 | snprintf(path, sizeof(path), "%s/%s/uevent", 951 | SYS_CLASS_POWER_SUPPLY, dirent->d_name); 952 | if ((fp = fopen(path, "r")) == NULL) { 953 | (void)fprintf(stderr, "Battery %s present but under supported - " 954 | "no state present.", dirent->d_name); 955 | (void)closedir(dir); 956 | return -1; 957 | } else { 958 | char buffer[4096]; 959 | double voltage = 0.0; 960 | double amps_rate = 0.0; 961 | double watts_rate = 0.0; 962 | 963 | while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) { 964 | int rc; 965 | 966 | if (strstr(buffer, SYS_FIELD_STATUS_DISCHARGING)) 967 | *discharging = true; 968 | 969 | if (strstr(buffer, SYS_FIELD_CURRENT_NOW) && 970 | strlen(buffer) > sizeof(SYS_FIELD_CURRENT_NOW) - 1) { 971 | rc = sscanf(buffer + sizeof(SYS_FIELD_CURRENT_NOW) - 1, "%12d", &val); 972 | if (rc == 1) 973 | amps_rate = (double)val / 1000000.0; 974 | } 975 | 976 | if (strstr(buffer, SYS_FIELD_POWER_NOW) && 977 | strlen(buffer) > sizeof(SYS_FIELD_POWER_NOW) - 1) { 978 | rc = sscanf(buffer + sizeof(SYS_FIELD_POWER_NOW) - 1, "%12d", &val); 979 | if (rc == 1) 980 | watts_rate = (double)val / 1000000.0; 981 | } 982 | 983 | if (strstr(buffer, SYS_FIELD_VOLTAGE_NOW) && 984 | strlen(buffer) > sizeof(SYS_FIELD_VOLTAGE_NOW) - 1) { 985 | rc = sscanf(buffer + sizeof(SYS_FIELD_VOLTAGE_NOW) - 1, "%12d", &val); 986 | if (rc == 1) 987 | voltage = (double)val / 1000000.0; 988 | } 989 | } 990 | average_voltage += voltage; 991 | total_watts += watts_rate + voltage * amps_rate; 992 | n++; 993 | (void)fclose(fp); 994 | } 995 | } 996 | } while (dirent); 997 | 998 | (void)closedir(dir); 999 | 1000 | #if DETECT_DISCHARGING 1001 | if (! *discharging) { 1002 | (void)printf("Machine is not discharging, cannot measure power usage.\n"); 1003 | #if defined(RAPL_X86) 1004 | (void)printf("Alternatively, use the RAPL power measuring option '-R'.\n"); 1005 | #endif 1006 | return -1; 1007 | } 1008 | #else 1009 | *discharging = true; 1010 | #endif 1011 | 1012 | /* 1013 | * If the battery is helpful it supplies the rate already, in which case 1014 | * we know the results from the battery are as good as we can and we don't 1015 | * have to figure out anything from capacity change over time. 1016 | */ 1017 | if (total_watts > RATE_ZERO_LIMIT) { 1018 | stats->value[POWER_NOW] = total_watts; 1019 | stats->value[VOLTAGE_NOW] = average_voltage / (double)n; 1020 | stats->value[CURRENT_NOW] = total_watts / average_voltage; 1021 | *inaccurate = (total_watts < 0.0); 1022 | return 0; 1023 | } 1024 | 1025 | /* 1026 | * Rate not known, battery is less than useful. We could 1027 | * calculate it from delta in charge, but that is not accurate 1028 | * for this kind of use case, so error out instead. 1029 | */ 1030 | (void)fprintf(stderr, "The battery just provided charge data which is not accurate enough.\n"); 1031 | return -1; 1032 | } 1033 | 1034 | /* 1035 | * power_get_proc_acpi() 1036 | * get power discharge rate from battery via /proc/acpi interface 1037 | */ 1038 | static int power_get_proc_acpi( 1039 | stats_t *stats, 1040 | bool *const discharging, 1041 | bool *const inaccurate) 1042 | { 1043 | DIR *dir; 1044 | FILE *file; 1045 | struct dirent *dirent; 1046 | char filename[PATH_MAX]; 1047 | double total_watts = 0.0; 1048 | double average_voltage = 0.0; 1049 | int n = 0; 1050 | 1051 | stats->value[POWER_NOW] = 0.0; 1052 | stats->value[VOLTAGE_NOW] = 0.0; 1053 | stats->value[CURRENT_NOW] = 0.0; 1054 | *discharging = false; 1055 | *inaccurate = true; 1056 | 1057 | if ((dir = opendir(PROC_ACPI_BATTERY)) == NULL) { 1058 | (void)fprintf(stderr, "Machine does not have %s, cannot run the test.\n", 1059 | PROC_ACPI_BATTERY); 1060 | return -1; 1061 | } 1062 | 1063 | while ((dirent = readdir(dir))) { 1064 | double voltage = 0.0; 1065 | double amps_rate = 0.0; 1066 | double watts_rate = 0.0; 1067 | char buffer[4096]; 1068 | char *ptr; 1069 | 1070 | if (strlen(dirent->d_name) < 3) 1071 | continue; 1072 | 1073 | (void)sprintf(filename, "/proc/acpi/battery/%s/state", dirent->d_name); 1074 | if ((file = fopen(filename, "r")) == NULL) 1075 | continue; 1076 | 1077 | (void)memset(buffer, 0, sizeof(buffer)); 1078 | while (fgets(buffer, sizeof(buffer), file) != NULL) { 1079 | if (strstr(buffer, "present:") && 1080 | strstr(buffer, "no")) 1081 | break; 1082 | 1083 | if (strstr(buffer, "charging state:") && 1084 | (strstr(buffer, "discharging") || strstr(buffer, "critical"))) 1085 | *discharging = true; 1086 | 1087 | ptr = strchr(buffer, ':'); 1088 | if (ptr) { 1089 | ptr++; 1090 | if (strstr(buffer, "present voltage")) 1091 | voltage = strtoull(ptr, NULL, 10) / 1000.0; 1092 | 1093 | if (strstr(buffer, "present rate")) { 1094 | if (strstr(ptr, "mW")) 1095 | watts_rate = strtoull(ptr, NULL, 10) / 1000.0 ; 1096 | if (strstr(ptr, "mA")) 1097 | amps_rate = strtoull(ptr, NULL, 10) / 1000.0; 1098 | } 1099 | } 1100 | } 1101 | (void)fclose(file); 1102 | 1103 | /* 1104 | * Some HP firmware is broken and has an undefined 1105 | * 'present voltage' field and instead returns this in 1106 | * the design_voltage field, so work around this. 1107 | */ 1108 | if (FLOAT_CMP(voltage, 0.0)) { 1109 | sprintf(filename, "/proc/acpi/battery/%s/info", 1110 | dirent->d_name); 1111 | if ((file = fopen(filename, "r")) != NULL) { 1112 | while (fgets(buffer, sizeof(buffer), file) != NULL) { 1113 | ptr = strchr(buffer, ':'); 1114 | if (ptr) { 1115 | ptr++; 1116 | if (strstr(buffer, "design voltage:")) { 1117 | voltage = strtoull(ptr, NULL, 10) / 1000.0; 1118 | break; 1119 | } 1120 | } 1121 | } 1122 | (void)fclose(file); 1123 | } 1124 | } 1125 | 1126 | average_voltage += voltage; 1127 | total_watts += watts_rate + voltage * amps_rate; 1128 | n++; 1129 | } 1130 | (void)closedir(dir); 1131 | 1132 | #if DETECT_DISCHARGING 1133 | if (! *discharging) { 1134 | printf("Machine is indicating it is not discharging and hence " 1135 | "we cannot measure power usage.\n"); 1136 | return -1; 1137 | } 1138 | #else 1139 | *discharging = true; 1140 | #endif 1141 | 1142 | /* 1143 | * If the battery is helpful it supplies the rate already, in which 1144 | * case we know the results from the battery are as good as we can 1145 | * and we don't have to figure out anything from capacity change over 1146 | * time. 1147 | */ 1148 | if (total_watts > RATE_ZERO_LIMIT) { 1149 | stats->value[POWER_NOW] = total_watts; 1150 | stats->value[VOLTAGE_NOW] = average_voltage / (double)n; 1151 | stats->value[CURRENT_NOW] = total_watts / average_voltage; 1152 | *inaccurate = (total_watts < 0.0); 1153 | return 0; 1154 | } 1155 | 1156 | /* 1157 | * Rate not known, battery is less than useful. We could 1158 | * calculate it from delta in charge, but that is not accurate 1159 | * for this kind of use case, so error out instead. 1160 | */ 1161 | (void)fprintf(stderr, "The battery just provided charge data which is not accurate enough.\n"); 1162 | return -1; 1163 | } 1164 | 1165 | #if defined(RAPL_X86) 1166 | 1167 | /* 1168 | * rapl_free_list() 1169 | * free RAPL list 1170 | */ 1171 | static void rapl_free_list(rapl_info_t *rapl_list) 1172 | { 1173 | rapl_info_t *rapl = rapl_list; 1174 | 1175 | while (rapl) { 1176 | rapl_info_t *next = rapl->next; 1177 | 1178 | free(rapl->name); 1179 | free(rapl->domain_name); 1180 | free(rapl); 1181 | rapl = next; 1182 | } 1183 | } 1184 | 1185 | /* 1186 | * rapl_get_domains() 1187 | */ 1188 | static int rapl_get_domains(rapl_info_t **rapl_list) 1189 | { 1190 | DIR *dir; 1191 | struct dirent *entry; 1192 | int n = 0; 1193 | 1194 | dir = opendir("/sys/class/powercap"); 1195 | if (dir == NULL) { 1196 | printf("Cannot open /sys/class/powercap, cannot measure power usage, try running as root.\n"); 1197 | return -1; 1198 | } 1199 | 1200 | while ((entry = readdir(dir)) != NULL) { 1201 | char path[PATH_MAX]; 1202 | FILE *fp; 1203 | rapl_info_t *rapl; 1204 | 1205 | /* Ignore non Intel RAPL interfaces */ 1206 | if (strncmp(entry->d_name, "intel-rapl", 10)) 1207 | continue; 1208 | 1209 | if ((rapl = calloc(1, sizeof(*rapl))) == NULL) { 1210 | fprintf(stderr, "Cannot allocate RAPL information.\n"); 1211 | closedir(dir); 1212 | return -1; 1213 | } 1214 | if ((rapl->name = strdup(entry->d_name)) == NULL) { 1215 | fprintf(stderr, "Cannot allocate RAPL name information.\n"); 1216 | closedir(dir); 1217 | free(rapl); 1218 | return -1; 1219 | } 1220 | (void)snprintf(path, sizeof(path), 1221 | "/sys/class/powercap/%s/max_energy_range_uj", 1222 | entry->d_name); 1223 | 1224 | rapl->max_energy_uj = 0.0; 1225 | if ((fp = fopen(path, "r")) != NULL) { 1226 | if (fscanf(fp, "%lf\n", &rapl->max_energy_uj) != 1) 1227 | rapl->max_energy_uj = 0.0; 1228 | (void)fclose(fp); 1229 | } 1230 | (void)snprintf(path, sizeof(path), 1231 | "/sys/class/powercap/%s/name", 1232 | entry->d_name); 1233 | 1234 | rapl->domain_name = NULL; 1235 | if ((fp = fopen(path, "r")) != NULL) { 1236 | char domain_name[128]; 1237 | 1238 | if (fgets(domain_name, sizeof(domain_name), fp) != NULL) { 1239 | domain_name[strcspn(domain_name, "\n")] = '\0'; 1240 | rapl->domain_name = strdup(domain_name); 1241 | } 1242 | (void)fclose(fp); 1243 | } 1244 | if (rapl->domain_name == NULL) { 1245 | free(rapl->name); 1246 | free(rapl); 1247 | continue; 1248 | } 1249 | 1250 | rapl->is_package = (strncmp(rapl->domain_name, "package-", 8) == 0); 1251 | rapl->next = *rapl_list; 1252 | *rapl_list = rapl; 1253 | n++; 1254 | } 1255 | (void)closedir(dir); 1256 | 1257 | if (!n) 1258 | (void)printf("Cannot detect any RAPL domains, cannot power measure power usage, try running as root.\n"); 1259 | return n; 1260 | } 1261 | 1262 | /* 1263 | * power_get_rapl() 1264 | * get power discharge rate from battery via the RAPL interface 1265 | */ 1266 | static int power_get_rapl( 1267 | rapl_info_t *rapl_list, 1268 | stats_t *stats, 1269 | bool *const discharging) 1270 | { 1271 | double t_now; 1272 | static bool first = true; 1273 | rapl_info_t *rapl; 1274 | int n = 0; 1275 | 1276 | /* Assume OK until found otherwise */ 1277 | stats->inaccurate[POWER_NOW] = false; 1278 | stats->value[POWER_NOW] = 0.0; 1279 | *discharging = false; 1280 | 1281 | t_now = gettime_to_double(); 1282 | 1283 | for (rapl = rapl_list; rapl; rapl = rapl->next) { 1284 | char path[PATH_MAX]; 1285 | FILE *fp; 1286 | double ujoules; 1287 | 1288 | (void)snprintf(path, sizeof(path), 1289 | "/sys/class/powercap/%s/energy_uj", 1290 | rapl->name); 1291 | 1292 | if ((fp = fopen(path, "r")) == NULL) 1293 | continue; 1294 | 1295 | if (fscanf(fp, "%lf\n", &ujoules) == 1) { 1296 | double t_delta = t_now - rapl->t_last; 1297 | double last_energy_uj = rapl->last_energy_uj; 1298 | 1299 | rapl->t_last = t_now; 1300 | 1301 | /* Wrapped around since last time? */ 1302 | if (ujoules - rapl->last_energy_uj < 0.0) { 1303 | rapl->last_energy_uj = ujoules; 1304 | ujoules += rapl->max_energy_uj; 1305 | } else { 1306 | rapl->last_energy_uj = ujoules; 1307 | } 1308 | 1309 | if (first || (t_delta <= 0.0)) { 1310 | stats->value[POWER_DOMAIN_0 + n] = 0.0; 1311 | stats->inaccurate[POWER_NOW] = true; 1312 | } else { 1313 | stats->value[POWER_DOMAIN_0 + n] = 1314 | (ujoules - last_energy_uj) / 1315 | (t_delta * 1000000.0); 1316 | } 1317 | if (rapl->is_package) 1318 | stats->value[POWER_NOW] += stats->value[POWER_DOMAIN_0 + n]; 1319 | n++; 1320 | *discharging = true; 1321 | } 1322 | (void)fclose(fp); 1323 | } 1324 | 1325 | if (first) { 1326 | stats->inaccurate[POWER_NOW] = true; 1327 | first = false; 1328 | } 1329 | 1330 | if (!n) { 1331 | (void)printf("Cannot detect any RAPL domains, cannot power measure power usage, try running as root\n"); 1332 | return -1; 1333 | } 1334 | return 0; 1335 | } 1336 | #endif 1337 | 1338 | /* 1339 | * power_get() 1340 | * get consumption rate 1341 | */ 1342 | static int power_get( 1343 | rapl_info_t *rapl_list, 1344 | stats_t *stats, 1345 | bool *const discharging, 1346 | bool *const inaccurate) 1347 | { 1348 | struct stat buf; 1349 | int i; 1350 | 1351 | for (i = POWER_NOW; i < MAX_VALUES; i++) { 1352 | stats->value[i] = i; 1353 | stats->inaccurate[i] = 0.0; 1354 | } 1355 | #if defined(RAPL_X86) 1356 | if (opt_flags & OPT_RAPL) 1357 | return power_get_rapl(rapl_list, stats, discharging); 1358 | #else 1359 | (void)rapl_list; 1360 | #endif 1361 | 1362 | if ((stat(SYS_CLASS_POWER_SUPPLY, &buf) != -1) && 1363 | S_ISDIR(buf.st_mode)) 1364 | return power_get_sys_fs(stats, discharging, inaccurate); 1365 | 1366 | if ((stat(PROC_ACPI_BATTERY, &buf) != -1) && 1367 | S_ISDIR(buf.st_mode)) 1368 | return power_get_proc_acpi(stats, discharging, inaccurate); 1369 | 1370 | (void)fprintf(stderr, "Machine does not seem to have a battery, cannot measure power.\n"); 1371 | return -1; 1372 | } 1373 | 1374 | /* 1375 | * not_discharging() 1376 | * returns true if battery is not discharging 1377 | */ 1378 | static inline bool not_discharging(rapl_info_t *rapl_list) 1379 | { 1380 | stats_t dummy; 1381 | bool discharging, inaccurate; 1382 | 1383 | return power_get(rapl_list, &dummy, &discharging, &inaccurate) < 0; 1384 | } 1385 | 1386 | /* 1387 | * monitor() 1388 | * monitor system activity and power consumption 1389 | */ 1390 | static inline int monitor( 1391 | const int num_cpus, 1392 | cpu_list_t *cpu_list, 1393 | rapl_info_t *rapl_list, 1394 | const int start_delay, 1395 | const int sample_delay, 1396 | const int max_readings, 1397 | const char *test, 1398 | const double percent_each, 1399 | const double percent, 1400 | bogo_ops_t *bogo_ops, 1401 | double *busy, 1402 | double *power, 1403 | double *voltage, 1404 | double *ops, 1405 | double *cpu_cycles, 1406 | double *cpu_instr) 1407 | { 1408 | int readings = 0, i; 1409 | int64_t t = 1; 1410 | stats_t *stats, s1, s2, average, stddev; 1411 | bool dummy_inaccurate; 1412 | double time_start; 1413 | 1414 | *busy = 0.0; 1415 | *power = 0.0; 1416 | *voltage = 0.0; 1417 | *ops = 0.0; 1418 | *cpu_cycles = 0.0; 1419 | *cpu_instr = 0.0; 1420 | 1421 | if (start_delay > 0) { 1422 | stats_t dummy; 1423 | bool discharging; 1424 | 1425 | /* Gather up initial data */ 1426 | for (i = 0; i < start_delay; i++) { 1427 | if (opt_flags & OPT_PROGRESS) { 1428 | (void)fprintf(stdout, "%10.10s: test warming up %5.1f%%..\r", 1429 | test, 100.0 * i / start_delay); 1430 | (void)fflush(stdout); 1431 | } 1432 | if (power_get(rapl_list, &dummy, &discharging, &dummy_inaccurate) < 0) 1433 | return -1; 1434 | if (sleep(1) || stop_flag) 1435 | return -1; 1436 | if (!discharging) 1437 | return -1; 1438 | } 1439 | } 1440 | 1441 | if (not_discharging(rapl_list)) 1442 | return -1; 1443 | 1444 | if ((stats = calloc(max_readings, sizeof(stats_t))) == NULL) { 1445 | (void)fprintf(stderr, "Cannot allocate statistics table.\n"); 1446 | return -1; 1447 | } 1448 | 1449 | stats_clear_all(stats, max_readings); 1450 | stats_clear(&average); 1451 | stats_clear(&stddev); 1452 | 1453 | if ((time_start = gettime_to_double()) < 0.0) { 1454 | free(stats); 1455 | return -1; 1456 | } 1457 | 1458 | if (stats_read(num_cpus, &s1, bogo_ops) < 0) { 1459 | free(stats); 1460 | return -1; 1461 | } 1462 | 1463 | while (!stop_flag && (readings < max_readings)) { 1464 | int ret = 0; 1465 | double secs, time_now; 1466 | struct timeval tv; 1467 | #if defined(PERF_ENABLED) 1468 | cpu_info_t *c; 1469 | #endif 1470 | 1471 | if ((time_now = gettime_to_double()) < 0.0) { 1472 | free(stats); 1473 | return -1; 1474 | } 1475 | 1476 | #if defined(PERF_ENABLED) 1477 | if (perf_enabled) { 1478 | for (c = cpu_list->head; c; c = c->next) 1479 | perf_start(&c->perf, c->pid); 1480 | } 1481 | #endif 1482 | 1483 | if (opt_flags & OPT_PROGRESS) { 1484 | double progress = readings * 100.0 / max_readings; 1485 | (void)fprintf(stdout, "%10.10s: test progress %5.1f%% (total progress %6.2f%%)\r", 1486 | test, progress, 1487 | (progress * percent_each / 100.0) + percent); 1488 | (void)fflush(stdout); 1489 | } 1490 | 1491 | secs = time_start + ((double)t * sample_delay) - time_now; 1492 | if (secs < 0.0) 1493 | goto sample_now; 1494 | tv = double_to_timeval(secs); 1495 | ret = select(0, NULL, NULL, NULL, &tv); 1496 | #if defined(PERF_ENABLED) 1497 | if ((ret < 0) && (perf_enabled)) { 1498 | for (c = cpu_list->head; c; c = c->next) 1499 | perf_stop(&c->perf); 1500 | 1501 | if (errno == EINTR) 1502 | break; 1503 | (void)fprintf(stderr,"select failed: errno=%d (%s).\n", 1504 | errno, strerror(errno)); 1505 | free(stats); 1506 | return -1; 1507 | } 1508 | #endif 1509 | sample_now: 1510 | if (stop_flag) 1511 | break; 1512 | /* Time out, so measure some more samples */ 1513 | if (ret == 0) { 1514 | char tmbuffer[10]; 1515 | bool discharging; 1516 | 1517 | #if defined(PERF_ENABLED) 1518 | if (perf_enabled) { 1519 | for (c = cpu_list->head; c; c = c->next) 1520 | perf_stop(&c->perf); 1521 | } 1522 | #endif 1523 | 1524 | get_time(tmbuffer, sizeof(tmbuffer)); 1525 | if (stats_read(num_cpus, &s2, bogo_ops) < 0) 1526 | goto tidy_exit; 1527 | 1528 | /* 1529 | * Total ticks was zero, something is broken, 1530 | * so re-sample 1531 | */ 1532 | if (!stats_gather(cpu_list, sample_delay, &s1, &s2, &stats[readings])) { 1533 | stats_clear(&stats[readings]); 1534 | if (stats_read(num_cpus, &s1, bogo_ops) < 0) 1535 | goto tidy_exit; 1536 | continue; 1537 | } 1538 | 1539 | if (power_get(rapl_list, &stats[readings], &discharging, 1540 | &stats[readings].inaccurate[POWER_NOW]) < 0) 1541 | goto tidy_exit; 1542 | 1543 | if (!discharging) 1544 | goto tidy_exit; /* No longer discharging! */ 1545 | 1546 | readings++; 1547 | s1 = s2; 1548 | t++; 1549 | } 1550 | } 1551 | 1552 | /* 1553 | * Stats now gathered, calculate averages, stddev, min 1554 | * and max and display 1555 | */ 1556 | stats_average_stddev_min_max(stats, readings, &average, &stddev); 1557 | if (readings > 0) { 1558 | stats_print(test, true, &average); 1559 | } 1560 | *busy = 100.0 - average.value[CPU_IDLE]; 1561 | *power = average.value[POWER_NOW]; 1562 | *voltage = average.value[VOLTAGE_NOW]; 1563 | *ops = average.value[BOGO_OPS]; 1564 | *cpu_cycles = average.value[CPU_CYCLES]; 1565 | *cpu_instr = average.value[CPU_INSTRUCTIONS]; 1566 | 1567 | free(stats); 1568 | return 0; 1569 | 1570 | tidy_exit: 1571 | free(stats); 1572 | return -1; 1573 | 1574 | } 1575 | 1576 | /* 1577 | * calc_trend() 1578 | * calculate linear trendline - compute gradient, y intercept and 1579 | * coefficient of determination. 1580 | */ 1581 | static int calc_trend( 1582 | const char *heading, 1583 | const int cpus_used, 1584 | const value_t *values, 1585 | const int num_values, 1586 | double *gradient, 1587 | double *intercept, 1588 | double *r2) 1589 | { 1590 | int i, n = 0; 1591 | double a = 0.0, b, c = 0.0, d, e, f; 1592 | double sum_x = 0.0, sum_y = 0.0; 1593 | double sum_x2 = 0.0, sum_y2 = 0.0; 1594 | double sum_xy = 0.0, r; 1595 | double n1, n2; 1596 | 1597 | for (i = 0; i < num_values; i++) { 1598 | if (cpus_used == CPU_ANY || cpus_used >= values[i].cpus_used) { 1599 | a += values[i].x * values[i].y; 1600 | sum_x += values[i].x; 1601 | sum_y += values[i].y; 1602 | sum_xy += (values[i].x * values[i].y); 1603 | sum_x2 += (values[i].x * values[i].x); 1604 | sum_y2 += (values[i].y * values[i].y); 1605 | n++; 1606 | } 1607 | } 1608 | 1609 | if (!n) { 1610 | (void)printf("%s: Cannot perform trend analysis, zero samples.\n", heading); 1611 | return -1; 1612 | } 1613 | 1614 | /* 1615 | * Coefficient of determination R^2, 1616 | * http://mathbits.com/MathBits/TISection/Statistics2/correlation.htm 1617 | */ 1618 | n1 = sqrt(((double)n * sum_x2) - (sum_x * sum_x)); 1619 | n2 = sqrt(((double)n * sum_y2) - (sum_y * sum_y)); 1620 | d = n1 * n2; 1621 | if (d <= 0.0) { 1622 | (void)printf("%s: Cannot perform trend analysis\n" 1623 | "(the coefficient of determination is not invalid).\n", heading); 1624 | return -1; 1625 | } 1626 | r = (((double)n * sum_xy) - (sum_x * sum_y)) / d; 1627 | *r2 = r * r; 1628 | 1629 | /* 1630 | * Regression Equation(y) = a + bx 1631 | * Slope = (NΣXY - (ΣX)(ΣY)) / (NΣX2 - (ΣX)2) 1632 | * Intercept = (ΣY - b(ΣX)) / N 1633 | */ 1634 | a *= (double)n; 1635 | b = sum_x * sum_y; 1636 | c = sum_x2 * (double)n; 1637 | d = sum_x * sum_x; 1638 | e = sum_y; 1639 | *gradient = (a - b) / (c - d); 1640 | f = (*gradient) * sum_x; 1641 | *intercept = (e - f) / (double)n; 1642 | 1643 | return 0; 1644 | } 1645 | 1646 | 1647 | /* 1648 | * show_help() 1649 | * simple help 1650 | */ 1651 | static void show_help(char *const argv[]) 1652 | { 1653 | (void)printf("%s, version %s\n\n", app_name, VERSION); 1654 | (void)printf("usage: %s [options]\n", argv[0]); 1655 | (void)printf(" -d secs specify delay before starting\n"); 1656 | (void)printf(" -h show help\n"); 1657 | (void)printf(" -n cpus specify number of CPUs to exercise\n"); 1658 | (void)printf(" -o file output results into YAML formatted file\n"); 1659 | (void)printf(" -p show progress\n"); 1660 | (void)printf(" -r secs specify run duration in seconds of each test cycle\n"); 1661 | #if defined(RAPL_X86) 1662 | (void)printf(" -R use Intel RAPL per CPU package data to measure Watts\n"); 1663 | #endif 1664 | (void)printf(" -s num number of samples (tests) per CPU for CPU calibration\n"); 1665 | (void)printf("\nExample: power-calibrate -R -r 20 -d 5 -s 21 -n 0 -p\n"); 1666 | } 1667 | 1668 | /* 1669 | * coefficient_r2() 1670 | * give some idea of r2 1671 | */ 1672 | static const char *coefficient_r2(const double r2) 1673 | { 1674 | if (r2 < 0.4) 1675 | return "very weak"; 1676 | if (r2 < 0.75) 1677 | return "weak"; 1678 | if (r2 < 0.80) 1679 | return "fair"; 1680 | if (r2 < 0.90) 1681 | return "good"; 1682 | if (r2 < 0.95) 1683 | return "strong"; 1684 | if (r2 < 1.0) 1685 | return "very strong"; 1686 | return "perfect"; 1687 | } 1688 | 1689 | /* 1690 | * dump_yaml_values() 1691 | * output yaml formatted data 1692 | */ 1693 | static void dump_yaml_values( 1694 | FILE *yaml, 1695 | const char *heading, 1696 | const char *field, 1697 | const double value, 1698 | const double r2) 1699 | { 1700 | if (!yaml) 1701 | return; 1702 | 1703 | (void)fprintf(yaml, " %s:\n", heading); 1704 | (void)fprintf(yaml, " %s: %e\n", field, value); 1705 | (void)fprintf(yaml, " r-squared: %f\n", r2); 1706 | } 1707 | 1708 | /* 1709 | * dump_yaml_misc() 1710 | * output json misc test data 1711 | */ 1712 | static void dump_yaml_misc(FILE *yaml) 1713 | { 1714 | time_t now; 1715 | struct tm tm; 1716 | struct utsname buf; 1717 | 1718 | now = time(NULL); 1719 | if (now == ((time_t) -1)) { 1720 | (void)memset(&tm, 0, sizeof(tm)); 1721 | } else { 1722 | localtime_r(&now, &tm); 1723 | } 1724 | 1725 | (void)memset(&buf, 0, sizeof(struct utsname)); 1726 | (void)uname(&buf); 1727 | 1728 | (void)fprintf(yaml, " test-run:\n"); 1729 | (void)fprintf(yaml, " date: %2.2d/%2.2d/%-2.2d\n", 1730 | tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); 1731 | (void)fprintf(yaml, " time: %-2.2d:%-2.2d:%-2.2d\n", 1732 | tm.tm_hour, tm.tm_min, tm.tm_sec); 1733 | (void)fprintf(yaml, " sysname: %s\n", buf.sysname); 1734 | (void)fprintf(yaml, " nodename: %s\n", buf.nodename); 1735 | (void)fprintf(yaml, " release: %s\n", buf.release); 1736 | (void)fprintf(yaml, " machine: %s\n", buf.machine); 1737 | } 1738 | 1739 | /* 1740 | * calc_average_voltage() 1741 | * calculatate average voltage on CPU ids 1742 | */ 1743 | static double calc_average_voltage( 1744 | const int cpus_used, 1745 | value_t *values, 1746 | const int num_values) 1747 | { 1748 | int i, n = 0; 1749 | double average = 0.0; 1750 | 1751 | for (i = 0; i < num_values; i++) { 1752 | if (cpus_used == CPU_ANY || cpus_used >= values[i].cpus_used) { 1753 | average += values[i].voltage; 1754 | n++; 1755 | } 1756 | } 1757 | average = (n == 0) ? 0.0 : average / n; 1758 | 1759 | if (FLOAT_CMP(average, 0.0)) 1760 | return -1; 1761 | 1762 | return average; 1763 | } 1764 | 1765 | /* 1766 | * show_trend() 1767 | * Show power trend 1768 | */ 1769 | static void show_trend( 1770 | FILE *yaml, 1771 | const int cpus_used, 1772 | value_t *values, 1773 | const int num_values, 1774 | const char *unit, 1775 | const char *each, 1776 | const char *heading, 1777 | const char *field, 1778 | bool power) 1779 | { 1780 | char watts[16]; 1781 | double gradient, intercept, r2; 1782 | double average_voltage = 1783 | calc_average_voltage(cpus_used, values, num_values); 1784 | 1785 | if (calc_trend(heading, cpus_used, values, num_values, &gradient, &intercept, &r2) < 0) 1786 | return; 1787 | 1788 | units_to_str(gradient, "W", watts, sizeof(watts)); 1789 | 1790 | (void)printf(" %s (%s) = (%s * %e) + %f\n", 1791 | power ? "Power" : "Energy", 1792 | power ? "Watts" : "Watt-seconds", 1793 | unit, gradient, intercept); 1794 | if (average_voltage > 0) { 1795 | char amps[16], volts[16]; 1796 | units_to_str(average_voltage, "V", volts, sizeof(volts)); 1797 | units_to_str(gradient / average_voltage, "A", amps, sizeof(amps)); 1798 | printf(" %s is about %s (about %s @ %s)\n", 1799 | each, watts, amps, volts); 1800 | } else { 1801 | (void)printf(" %s is about %s%s\n", each, watts, 1802 | power ? "" : "s"); 1803 | } 1804 | (void)printf(" Coefficient of determination R^2 = %f (%s)\n", 1805 | r2, coefficient_r2(r2)); 1806 | 1807 | dump_yaml_values(yaml, heading, field, gradient, r2); 1808 | } 1809 | 1810 | static void init_values(value_t *values, const size_t n) 1811 | { 1812 | register size_t i; 1813 | 1814 | for (i = 0; i < n; i++) { 1815 | values[i].x = 0.0; 1816 | values[i].y = 0.0; 1817 | values[i].voltage = 0.0; 1818 | values[i].cpu_id = 0; 1819 | values[i].cpus_used = 0; 1820 | } 1821 | } 1822 | 1823 | /* 1824 | * monitor_cpu_load() 1825 | * load CPU(s) and gather stats 1826 | */ 1827 | static int monitor_cpu_load( 1828 | FILE *fp, 1829 | const int32_t num_cpus, 1830 | const int32_t max_cpus, 1831 | const int32_t samples_cpu, 1832 | const int32_t sample_delay, 1833 | cpu_list_t *cpu_list, 1834 | rapl_info_t *rapl_list, 1835 | const int start_delay, 1836 | const int max_readings, 1837 | bogo_ops_t *bogo_ops) 1838 | { 1839 | uint32_t i, n = 0; 1840 | const size_t n_values = num_cpus * samples_cpu; 1841 | value_t values_load[n_values], *value_load = values_load; 1842 | value_t values_ops[n_values], *value_ops = values_ops; 1843 | value_t values_cpu_cycles[n_values], *value_cpu_cycles = values_cpu_cycles; 1844 | value_t values_cpu_instr[n_values], *value_cpu_instr = values_cpu_instr; 1845 | double scale = (double)MAX_CPU_LOAD / (samples_cpu - 1); 1846 | 1847 | init_values(values_load, n_values); 1848 | init_values(values_ops, n_values); 1849 | init_values(values_cpu_cycles, n_values); 1850 | init_values(values_cpu_instr, n_values); 1851 | 1852 | stats_headings("CPU load"); 1853 | for (i = 0; i < (uint32_t)samples_cpu; i++) { 1854 | cpu_info_t *c; 1855 | uint32_t n_cpus; 1856 | 1857 | for (n_cpus = 1, c = cpu_list->head; c; n_cpus++, c = c->next) { 1858 | char buffer[1024]; 1859 | int cpu_load = scale * i; 1860 | int ret; 1861 | double percent_each = 100.0 / (samples_cpu * num_cpus); 1862 | double percent = n * percent_each; 1863 | 1864 | (void)snprintf(buffer, sizeof(buffer), "%d%% x %u", 1865 | cpu_load, n_cpus); 1866 | start_load(cpu_list, n_cpus, stress_cpu, 1867 | (uint64_t)cpu_load, bogo_ops); 1868 | 1869 | ret = monitor(num_cpus, cpu_list, rapl_list, 1870 | start_delay, sample_delay, 1871 | max_readings, buffer, 1872 | percent_each, percent, bogo_ops, 1873 | &value_load->x, 1874 | &value_load->y, 1875 | &value_load->voltage, 1876 | &value_ops->x, 1877 | &value_cpu_cycles->x, 1878 | &value_cpu_instr->x); 1879 | value_ops->y = value_load->y; 1880 | value_ops->voltage = value_load->voltage; 1881 | value_ops->cpu_id = value_load->cpu_id = c->cpu_id; 1882 | value_ops->cpus_used = value_load->cpus_used = n_cpus; 1883 | 1884 | value_cpu_cycles->y = value_load->y; 1885 | value_cpu_cycles->voltage = value_load->voltage; 1886 | value_cpu_cycles->cpu_id = value_load->cpu_id; 1887 | value_cpu_cycles->cpus_used = value_load->cpus_used; 1888 | 1889 | value_cpu_instr->y = value_load->y; 1890 | value_cpu_instr->voltage = value_load->voltage; 1891 | value_cpu_instr->cpu_id = value_load->cpu_id; 1892 | value_cpu_instr->cpus_used = value_load->cpus_used; 1893 | 1894 | stop_load(cpu_list, n_cpus); 1895 | if (stop_flag || (ret < 0)) 1896 | return -1; 1897 | value_load++; 1898 | value_ops++; 1899 | value_cpu_cycles++; 1900 | value_cpu_instr++; 1901 | n++; 1902 | } 1903 | } 1904 | /* Keep static analysis happy */ 1905 | if (n == 0) { 1906 | printf("\nZero samples, cannot compute statistics.\n"); 1907 | return -1; 1908 | } 1909 | 1910 | if (opt_flags & OPT_CALIBRATE_EACH_CPU) { 1911 | cpu_info_t *c; 1912 | int cpus_used = 0; 1913 | 1914 | for (c = cpu_list->head, cpus_used = 1; c; c = c->next, cpus_used++) { 1915 | (void)printf("\nFor %d CPU%s (of a %d CPU system):\n", 1916 | cpus_used, cpus_used > 1 ? "s" : "", max_cpus); 1917 | show_trend(NULL, cpus_used, values_load, n, 1918 | "% CPU load", "1% CPU load", 1919 | "cpu-load", "one-percent-cpu-load-watts", true); 1920 | (void)printf("\n"); 1921 | show_trend(NULL, cpus_used, values_ops, n, 1922 | "bogo op", "1 bogo op", 1923 | "bogo-op", "one-bogo-op-power-watt-seconds", false); 1924 | 1925 | if (perf_enabled) { 1926 | (void)printf("\n"); 1927 | show_trend(NULL, cpus_used, values_cpu_cycles, n, 1928 | "CPU cycle", "1 CPU cycle", 1929 | "cpu-cycle", "one-cpu-cycle-watt-seconds", false); 1930 | (void)printf("\n"); 1931 | show_trend(NULL, cpus_used, values_cpu_instr, n, 1932 | "CPU instruction", "1 CPU instruction", 1933 | "cpu-instruction", "on-cpu-instruction-watt-seconds", false); 1934 | } 1935 | } 1936 | } else { 1937 | (void)printf("\nFor %u CPU%s (of a %d CPU system):\n", 1938 | cpu_list->count, cpu_list->count > 1 ? "s" : "", max_cpus); 1939 | show_trend(fp, CPU_ANY, values_load, n, 1940 | "% CPU load", "1% CPU load", 1941 | "cpu-load", "one-percent-cpu-load-watts", true); 1942 | (void)printf("\n"); 1943 | show_trend(fp, CPU_ANY, values_ops, n, 1944 | "bogo op", "1 bogo op", 1945 | "bogo-op", "one-bogo-op-watts-seconds", false); 1946 | if (perf_enabled) { 1947 | (void)printf("\n"); 1948 | show_trend(fp, CPU_ANY, values_cpu_cycles, n, 1949 | "CPU cycle", "1 CPU cycle", 1950 | "cpu-cycle", "one-cpu-cycle-watt-seconds", false); 1951 | (void)printf("\n"); 1952 | show_trend(fp, CPU_ANY, values_cpu_instr, n, 1953 | "CPU instruction", "1 CPU instruction", 1954 | "cpu-instruction", "on-cpu-instruction-watt-seconds", false); 1955 | } 1956 | } 1957 | return 0; 1958 | } 1959 | 1960 | /* 1961 | * add_cpu_info() 1962 | * add cpu # to cpu_info list 1963 | */ 1964 | static int add_cpu_info(cpu_list_t *cpu_list, const int cpu) 1965 | { 1966 | cpu_info_t *c; 1967 | 1968 | c = calloc(1, sizeof(cpu_info_t)); 1969 | if (!c) { 1970 | (void)fprintf(stderr, "Out of memory allocating CPU info.\n"); 1971 | return -1; 1972 | } 1973 | if (cpu_list->head) 1974 | cpu_list->tail->next = c; 1975 | else 1976 | cpu_list->head = c; 1977 | 1978 | c->cpu_id = cpu; 1979 | cpu_list->tail = c; 1980 | cpu_list->count++; 1981 | 1982 | return 0; 1983 | } 1984 | 1985 | 1986 | /* 1987 | * parse_cpu_info() 1988 | * parse cpu info 1989 | */ 1990 | static int parse_cpu_info( 1991 | int32_t *num_cpus, 1992 | const int32_t max_cpus, 1993 | cpu_list_t *cpu_list, 1994 | char *arg) 1995 | { 1996 | char *str, *token; 1997 | int n = 0; 1998 | 1999 | for (str = arg; (token = strtok(str, ",")) != NULL; str = NULL) { 2000 | int cpu; 2001 | char *endptr; 2002 | 2003 | errno = 0; 2004 | cpu = strtol(token, &endptr, 10); 2005 | if (errno || endptr == token) { 2006 | (void)fprintf(stderr, "Invalid CPU specified.\n"); 2007 | return -1; 2008 | } 2009 | if (cpu < 0 || cpu > max_cpus - 1) { 2010 | (void)fprintf(stderr, "CPU number out of range.\n"); 2011 | return -1; 2012 | } 2013 | if (add_cpu_info(cpu_list, cpu) < 0) 2014 | return -1; 2015 | n++; 2016 | } 2017 | if (!cpu_list->head) { 2018 | (void)fprintf(stderr, "No valid CPU numbers given.\n"); 2019 | return -1; 2020 | } 2021 | 2022 | *num_cpus = n; 2023 | 2024 | return 0; 2025 | } 2026 | 2027 | /* 2028 | * populate_cpu_info() 2029 | * if user has not supplied cpu info then 2030 | * we need to populate the list 2031 | */ 2032 | static inline int populate_cpu_info(const int32_t num_cpus, cpu_list_t *cpu_list) 2033 | { 2034 | int cpu; 2035 | 2036 | if (cpu_list->head) 2037 | return 0; 2038 | 2039 | for (cpu = 0; cpu < num_cpus; cpu++) 2040 | if (add_cpu_info(cpu_list, cpu) < 0) 2041 | return -1; 2042 | 2043 | return 0; 2044 | } 2045 | 2046 | /* 2047 | * free_cpu_info() 2048 | * free CPU info list 2049 | */ 2050 | static void free_cpu_info(cpu_list_t *cpu_list) 2051 | { 2052 | cpu_info_t *c = cpu_list->head; 2053 | 2054 | while (c) { 2055 | cpu_info_t *next = c->next; 2056 | free(c); 2057 | c = next; 2058 | } 2059 | cpu_list->head = NULL; 2060 | cpu_list->tail = NULL; 2061 | cpu_list->count = 0; 2062 | } 2063 | 2064 | int main(int argc, char * const argv[]) 2065 | { 2066 | int max_readings, run_duration, start_delay = START_DELAY_BATTERY; 2067 | int opt_run_duration = DEFAULT_RUN_DURATION; 2068 | char *filename = NULL; 2069 | FILE *yaml = NULL; 2070 | int ret = EXIT_FAILURE, i; 2071 | struct sigaction new_action; 2072 | bogo_ops_t *bogo_ops = NULL; 2073 | rapl_info_t *rapl_list = NULL; /* RAPL domain info list */ 2074 | cpu_list_t cpu_list; 2075 | int32_t samples_cpu = 11.0; /* samples per run */ 2076 | int32_t sample_delay = SAMPLE_DELAY; /* time between each sampl */ 2077 | int32_t num_cpus; /* number of CPUs */ 2078 | int32_t max_cpus; /* number of CPUs in system */ 2079 | 2080 | (void)memset(&cpu_list, 0, sizeof(cpu_list)); 2081 | 2082 | max_cpus = num_cpus = sysconf(_SC_NPROCESSORS_CONF); 2083 | if (num_cpus < 1) { 2084 | /* Zero CPUs makes no sense, -ve is an error */ 2085 | (void)fprintf(stderr, "Cannot determine number of CPUs, errno=%d (%s).\n", 2086 | errno, strerror(errno)); 2087 | goto out; 2088 | } 2089 | 2090 | for (;;) { 2091 | int c = getopt(argc, argv, "d:ehn:o:ps:r:R"); 2092 | if (c == -1) 2093 | break; 2094 | switch (c) { 2095 | case 'd': 2096 | opt_flags |= OPT_DELAY; 2097 | start_delay = atoi(optarg); 2098 | if (start_delay < 0) { 2099 | (void)fprintf(stderr, "Start delay must be 0 or more seconds.\n"); 2100 | goto out; 2101 | } 2102 | break; 2103 | case 'e': 2104 | opt_flags |= OPT_CALIBRATE_EACH_CPU; 2105 | break; 2106 | case 'h': 2107 | show_help(argv); 2108 | goto out; 2109 | case 'n': 2110 | if (parse_cpu_info(&num_cpus, max_cpus, &cpu_list, optarg) < 0) 2111 | goto out; 2112 | break; 2113 | case 'o': 2114 | filename = optarg; 2115 | break; 2116 | case 'p': 2117 | opt_flags |= OPT_PROGRESS; 2118 | break; 2119 | case 'r': 2120 | opt_run_duration = atoi(optarg); 2121 | if (opt_run_duration < MIN_RUN_DURATION) { 2122 | (void)fprintf(stderr, "Minimum run duration must be %d seconds or more\n", 2123 | MIN_RUN_DURATION); 2124 | goto out; 2125 | } 2126 | break; 2127 | #if defined(RAPL_X86) 2128 | case 'R': 2129 | opt_flags |= OPT_RAPL; 2130 | break; 2131 | #endif 2132 | case 's': 2133 | samples_cpu = atoi(optarg); 2134 | if ((samples_cpu < 3.0) || 2135 | (samples_cpu > MAX_CPU_LOAD)) { 2136 | (void)fprintf(stderr, "Samples for CPU measurements out of range.\n"); 2137 | goto out; 2138 | } 2139 | break; 2140 | default: 2141 | show_help(argv); 2142 | goto out; 2143 | } 2144 | } 2145 | 2146 | if ((opt_flags & (OPT_RAPL | OPT_DELAY)) == OPT_RAPL) 2147 | start_delay = START_DELAY_RAPL; 2148 | 2149 | perf_enabled = perf_possible(); 2150 | populate_cpu_info(num_cpus, &cpu_list); 2151 | 2152 | #if defined(RAPL_X86) 2153 | if ((opt_flags & OPT_RAPL) && (rapl_get_domains(&rapl_list) < 1)) 2154 | exit(EXIT_FAILURE); 2155 | #endif 2156 | 2157 | if (optind < argc) { 2158 | sample_delay = atoi(argv[optind++]); 2159 | if (sample_delay < 1) { 2160 | (void)fprintf(stderr, "Sample delay must be >= 1.\n"); 2161 | goto out; 2162 | } 2163 | } 2164 | 2165 | if (filename) { 2166 | if ((yaml = fopen(filename, "w")) == NULL) { 2167 | (void)fprintf(stderr, "Cannot open json output file '%s', " 2168 | "errno=%d (%s).\n", 2169 | filename, errno, strerror(errno)); 2170 | goto out; 2171 | } 2172 | (void)fprintf(yaml, "---\n%s:\n", app_name); 2173 | } 2174 | 2175 | (void)memset(&new_action, 0, sizeof(new_action)); 2176 | for (i = 0; signals[i] != -1; i++) { 2177 | new_action.sa_handler = handle_sig; 2178 | (void)sigemptyset(&new_action.sa_mask); 2179 | new_action.sa_flags = SA_RESTART; 2180 | 2181 | if (sigaction(signals[i], &new_action, NULL) < 0) { 2182 | (void)fprintf(stderr, "sigaction failed: errno=%d (%s).\n", 2183 | errno, strerror(errno)); 2184 | goto out; 2185 | } 2186 | } 2187 | 2188 | run_duration = opt_run_duration; 2189 | max_readings = run_duration / sample_delay; 2190 | 2191 | bogo_ops = mmap(NULL, sizeof(bogo_ops_t) * num_cpus, 2192 | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); 2193 | if (bogo_ops == MAP_FAILED) { 2194 | (void)fprintf(stderr, "mmap failed: errno=%d (%s).\n", 2195 | errno, strerror(errno)); 2196 | goto out; 2197 | } 2198 | 2199 | if (not_discharging(rapl_list)) 2200 | goto out; 2201 | 2202 | if (monitor_cpu_load(yaml, num_cpus, max_cpus, samples_cpu, sample_delay, 2203 | &cpu_list, rapl_list, start_delay, max_readings, bogo_ops) < 0) 2204 | goto out; 2205 | 2206 | ret = EXIT_SUCCESS; 2207 | out: 2208 | if (bogo_ops) 2209 | (void)munmap(bogo_ops, sizeof(bogo_ops_t) * num_cpus); 2210 | if (yaml) { 2211 | dump_yaml_misc(yaml); 2212 | 2213 | (void)fprintf(yaml, "...\n"); 2214 | (void)fclose(yaml); 2215 | if (ret != EXIT_SUCCESS) 2216 | unlink(filename); 2217 | } 2218 | if (cpu_list.head) 2219 | free_cpu_info(&cpu_list); 2220 | 2221 | #if defined(RAPL_X86) 2222 | rapl_free_list(rapl_list); 2223 | #endif 2224 | 2225 | exit(ret); 2226 | } 2227 | -------------------------------------------------------------------------------- /scripts/power-calibrate-json-parse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright (C) 2014 Canonical 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | # 19 | # This script takes json output from power-calibrate and health-check 20 | # to calculate an esimate of power consumption 21 | # 22 | # 23 | import sys, os, json 24 | 25 | def r2tostr(r2): 26 | if r2 < 0.4: 27 | return "very poor" 28 | if r2 < 0.75: 29 | return "poor" 30 | if r2 < 0.80: 31 | return "fair" 32 | if r2 < 0.90: 33 | return "good" 34 | if r2 < 0.95: 35 | return "very good" 36 | if r2 < 1.0: 37 | return "excellent" 38 | return "perfect" 39 | 40 | if len(sys.argv) != 3: 41 | sys.stderr.write("Usage: " + sys.argv[0] + " power-calibrate.json health-check.json\n") 42 | os._exit(1) 43 | 44 | try: 45 | file = sys.argv[1] 46 | f = open(file, 'r') 47 | data = json.load(f) 48 | f.close() 49 | except: 50 | sys.stderr.write("Failed to open and parse JSON file " + file + "\n"); 51 | os._exit(1) 52 | 53 | if not "power-calibrate" in data: 54 | sys.stderr.write("Failed to find power calibration data in file " + file + "\n"); 55 | os._exit(1) 56 | 57 | pc = data["power-calibrate"] 58 | if "cpu-load" in pc: 59 | cpu_1pc = pc["cpu-load"]["one-percent-cpu-load"] 60 | cpu_r2 = pc["cpu-load"]["r-squared"] 61 | else: 62 | cpu_1pc = 0 63 | cpu_r2 = 0 64 | 65 | if "context-switches" in pc: 66 | ctxt_sw = pc["context-switches"]["one-context-switch"] 67 | ctxt_r2 = pc["context-switches"]["r-squared"] 68 | else: 69 | ctxt_sw = 0 70 | ctxt_r2 = 0 71 | 72 | if not "test-run" in pc: 73 | sys.stderr.write("Failed to find power calibration machine information in file " + file + "\n"); 74 | os._exit(1) 75 | 76 | test_date = pc["test-run"]["date"] 77 | test_time = pc["test-run"]["time"] 78 | test_sys = pc["test-run"]["sysname"] 79 | test_node = pc["test-run"]["nodename"] 80 | test_rel = pc["test-run"]["release"] 81 | test_arch = pc["test-run"]["machine"] 82 | 83 | try: 84 | file = sys.argv[2] 85 | f = open(file, 'r') 86 | data = json.load(f) 87 | f.close() 88 | except: 89 | sys.stderr.write("Failed to open and parse JSON file " + file + "\n"); 90 | os._exit(1) 91 | 92 | if not "health-check" in data: 93 | sys.stderr.write("Failed to find health-check data in file " + file + "\n"); 94 | os._exit(1) 95 | 96 | hc = data["health-check"] 97 | if not "cpu-load" in hc: 98 | sys.stderr.write("Failed to find cpu-load stats in health-check file " + file + "\n"); 99 | os._exit(1) 100 | 101 | total_cpu = hc["cpu-load"]["cpu-load-total"]["total-cpu-percent"] 102 | 103 | if not "context-switches" in hc: 104 | sys.stderr.write("Failed to find context-switch stats in health-check file " + file + "\n"); 105 | os._exit(1) 106 | 107 | ctxt_switches = hc["context-switches"]["context-switches-total"]["context-switch-total-rate"] 108 | 109 | cpu_total_watts = cpu_1pc * total_cpu 110 | ctxt_total_watts = ctxt_sw * ctxt_switches 111 | total_watts = cpu_total_watts + ctxt_total_watts 112 | 113 | if total_watts == 0.0: 114 | print "CPU : %8.3f Watts (%.2f%% CPU)" % \ 115 | (cpu_total_watts, total_cpu) 116 | print "Context Switches : %8.3f Watts (%.2f switches)" % \ 117 | (ctxt_sw * ctxt_switches, ctxt_switches) 118 | print "Total : %8.3f Watts" % total_watts 119 | else: 120 | rel = ((cpu_total_watts * cpu_r2) + (ctxt_total_watts * ctxt_r2)) / total_watts 121 | cpu_pc = cpu_total_watts / total_watts * 100 122 | ctxt_pc = ctxt_total_watts / total_watts * 100 123 | print "CPU : %8.3f Watts (%.2f%% CPU, %.2f%% of total power)" % \ 124 | (cpu_total_watts, total_cpu, cpu_pc) 125 | print "Context Switches : %8.3f Watts (%.2f switches, %.2f%% of total power)" % \ 126 | (ctxt_sw * ctxt_switches, ctxt_switches, ctxt_pc) 127 | print "Total : %8.3f Watts (%s estimate)" % (total_watts, r2tostr(rel)) 128 | 129 | 130 | print " " 131 | 132 | print "Note, estimate is based on calibration data:" 133 | print " 1%% CPU : %10.5f Watts, R^2 %f (%s)" % (cpu_1pc, cpu_r2, r2tostr(cpu_r2)) 134 | print " 1 Context Switch : %10.5f Watts, R^2 %f (%s)" % (ctxt_sw, ctxt_r2, r2tostr(ctxt_r2)) 135 | print " " 136 | 137 | print "Tested on " + test_date + " at " + test_time + " on " + test_node + " (" + \ 138 | test_sys + " " + test_rel + " " + test_arch + ")" 139 | --------------------------------------------------------------------------------