├── .travis.yml ├── COPYING ├── Makefile ├── README.md ├── bash-completion └── eventstat ├── debian ├── changelog ├── control ├── copyright ├── rules ├── source │ └── format └── watch ├── eventstat.8 └── eventstat.c /.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) 2011-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.06.00 21 | 22 | CFLAGS += -Wall -Wextra -DVERSION='"$(VERSION)"' -O2 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 -fanalyzer 32 | endif 33 | 34 | BINDIR=/usr/bin 35 | MANDIR=/usr/share/man/man8 36 | BASHDIR=/usr/share/bash-completion/completions 37 | 38 | eventstat: eventstat.o 39 | $(CC) $(CFLAGS) $< -lm -lncurses -o $@ $(LDFLAGS) 40 | 41 | eventstat.8.gz: eventstat.8 42 | gzip -c $< > $@ 43 | 44 | dist: 45 | rm -rf eventstat-$(VERSION) 46 | mkdir eventstat-$(VERSION) 47 | cp -rp Makefile eventstat.c eventstat.8 COPYING .travis.yml \ 48 | bash-completion README.md eventstat-$(VERSION) 49 | tar -Jcf eventstat-$(VERSION).tar.xz eventstat-$(VERSION) 50 | rm -rf eventstat-$(VERSION) 51 | 52 | clean: 53 | rm -f eventstat eventstat.o eventstat.8.gz 54 | rm -f eventstat-$(VERSION).tar.xz 55 | 56 | install: eventstat eventstat.8.gz 57 | mkdir -p ${DESTDIR}${BINDIR} 58 | cp eventstat ${DESTDIR}${BINDIR} 59 | mkdir -p ${DESTDIR}${MANDIR} 60 | cp eventstat.8.gz ${DESTDIR}${MANDIR} 61 | mkdir -p ${DESTDIR}${BASHDIR} 62 | cp bash-completion/eventstat ${DESTDIR}${BASHDIR} 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eventstat 2 | 3 | Eventstat periodically dumps out the current kernel event state. 4 | It keeps track of current events and outputs the change in events 5 | on each output update. 6 | 7 | # Eventstat command line options: 8 | 9 | * -c report cumulative events rather than events per second. 10 | * -C report event count rather than event per second in CSV output. 11 | * -d remove pathname from long process name in CSV output. 12 | * -h print this help. 13 | * -l use long cmdline text from /proc/pid/cmdline in CSV output. 14 | * -n events - specifies number of events to display. 15 | * -q run quietly, useful with option -r. 16 | * -r filename - specifies a comma separated values (CSV) output file to dump samples into. 17 | * -s use short process name from /proc/pid/cmdline in CSV output. 18 | * -S calculate min, max, average and standard deviation in CSV output. 19 | * -t threshold - samples less than the specified threshold are ignored. 20 | * -T 'top' mode 21 | 22 | # Example Output: 23 | ``` 24 | sudo eventstat 10 1 25 | Evnt/s PID Task Init Function Callback 26 | 123.30 2253 alsa-sink hrtimer_start_range_ns hrtimer_wakeup 27 | 55.20 2252 alsa-source hrtimer_start_range_ns hrtimer_wakeup 28 | 41.20 0 swapper/0 hrtimer_start_range_ns tick_sched_timer 29 | 24.60 0 swapper/1 hrtimer_start_range_ns tick_sched_timer 30 | 22.70 0 swapper/0 hrtimer_start tick_sched_timer 31 | 18.20 2186 compiz hrtimer_start_range_ns hrtimer_wakeup 32 | 8.00 0 swapper/1 usb_hcd_poll_rh_status rh_timer_func 33 | 5.00 2245 syndaemon hrtimer_start_range_ns hrtimer_wakeup 34 | 2.20 0 swapper/1 hrtimer_start tick_sched_timer 35 | 2.10 3116 xchat-gnome hrtimer_start_range_ns hrtimer_wakeup 36 | 2.00 3088 mumble hrtimer_start_range_ns hrtimer_wakeup 37 | 1.10 2232 gvfs-afc-volume hrtimer_start_range_ns hrtimer_wakeup 38 | 1.00 5 kworker/u:0 queue_delayed_work delayed_work_timer_fn 39 | 1.00 2641 ubuntuone-syncd hrtimer_start_range_ns hrtimer_wakeup 40 | 1.00 0 swapper/1 add_timer tg3_timer 41 | 1.00 1 swapper/0 start_bandwidth_timer sched_rt_period_timer 42 | 0.60 12547 firefox hrtimer_start_range_ns hrtimer_wakeup 43 | 0.60 3097 threaded-ml hrtimer_start_range_ns hrtimer_wakeup 44 | 0.50 1218 Xorg intel_mark_busy intel_gpu_idle_timer 45 | 0.20 2178 gnome-settings- hrtimer_start_range_ns hrtimer_wakeup 46 | 0.20 3123 mumble hrtimer_start_range_ns hrtimer_wakeup 47 | 0.20 1218 Xorg hrtimer_start_range_ns hrtimer_wakeup 48 | 0.20 3123 mumble sk_reset_timer tcp_write_timer 49 | 0.10 2465 gnome-terminal hrtimer_start_range_ns hrtimer_wakeup 50 | N 0.00 13143 kworker/0:2 queue_delayed_work delayed_work_timer_fn 51 | N 0.00 1706 upowerd schedule_timeout_interruptible process_timeout 52 | N 0.00 12546 firefox hrtimer_start_range_ns hrtimer_wakeup 53 | N 0.00 2415 unity-applicati hrtimer_start_range_ns hrtimer_wakeup 54 | N 0.00 2456 zeitgeist-datah hrtimer_start_range_ns hrtimer_wakeup 55 | N 0.00 12893 thunderbird-bin hrtimer_start_range_ns hrtimer_wakeup 56 | N 0.00 1218 Xorg i915_add_request i915_hangcheck_elapsed 57 | N 0.00 2200 nautilus hrtimer_start_range_ns hrtimer_wakeup 58 | N 0.00 3091 threaded-ml laptop_io_completion laptop_mode_timer_fn 59 | N 0.00 1218 Xorg intel_mark_busy intel_crtc_idle_timer 60 | N 0.00 1190 irqbalance hrtimer_start_range_ns hrtimer_wakeup 61 | N 0.00 1959 rtkit-daemon hrtimer_start_range_ns hrtimer_wakeup 62 | N 0.00 0 swapper/0 dev_watchdog dev_watchdog 63 | N 0.00 1706 upowerd acpi_ec_transaction_unlocked process_timeout 64 | N 0.00 1706 upowerd schedule_timeout_uninterruptible process_timeout 65 | N 0.00 12 watchdog/1 hrtimer_start watchdog_timer_fn 66 | N 0.00 2153 ssh-agent hrtimer_start_range_ns hrtimer_wakeup 67 | N 0.00 1706 upowerd hrtimer_start_range_ns hrtimer_wakeup 68 | N 0.00 1570 accounts-daemon hrtimer_start_range_ns hrtimer_wakeup 69 | N 0.00 2282 unity-panel-ser hrtimer_start_range_ns hrtimer_wakeup 70 | N 0.00 1218 Xorg drm_vblank_put vblank_disable_fn 71 | N 0.00 12896 thunderbird-bin hrtimer_start_range_ns hrtimer_wakeup 72 | N 0.00 2189 gconfd-2 hrtimer_start_range_ns hrtimer_wakeup 73 | N 0.00 7 watchdog/0 hrtimer_start watchdog_timer_fn 74 | N 0.00 13361 kworker/0:0 schedule_timeout_uninterruptible process_timeout 75 | 3122 Total events, 312.20 events/sec 76 | ``` 77 | -------------------------------------------------------------------------------- /bash-completion/eventstat: -------------------------------------------------------------------------------- 1 | # eventstat 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 | _eventstat() 20 | { 21 | local cur prev words cword 22 | _init_completion || return 23 | 24 | case "$prev" in 25 | '-n') 26 | COMPREPLY=( $(compgen -W "maxevents" -- $cur) ) 27 | return 0 28 | ;; 29 | '-r') 30 | _filedir 31 | return 0 32 | ;; 33 | '-t') 34 | COMPREPLY=( $(compgen -W "threshold" -- $cur) ) 35 | return 0 36 | ;; 37 | esac 38 | 39 | case "$cur" in 40 | -*) 41 | OPTS="-c -C -d -h -l -q -s -S -T -w -n -r -t" 42 | COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) 43 | return 0 44 | ;; 45 | esac 46 | return 0 47 | } 48 | 49 | # load the completion 50 | complete -F _eventstat eventstat 51 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | eventstat (0.06.00-1) unstable; urgency=medium 2 | 3 | * Makefile: bump version 4 | * Makefile: remove .xz tarball and not .gz 5 | * eventstat: the Init Function should be annotated as "Kernel Init Function" 6 | * eventstat: report all cumulative events 7 | 8 | -- Colin Ian King Tue, 16 Jan 2024 13:17:26 +0000 9 | 10 | eventstat (0.05.02-1) unstable; urgency=medium 11 | 12 | * Makefile: bump version 13 | * Makefile: remove snap from make dist rule 14 | * debian/rules: debian/rules: add missing hardening settings 15 | * Update copyright to 2024 16 | * Check for array bounds limit while parsing pid length string 17 | * Remove snapcraft 18 | 19 | -- Colin Ian King Fri, 12 Jan 2024 10:05:41 +0000 20 | 21 | eventstat (0.05.01-1) unstable; urgency=medium 22 | 23 | * Makefile: bump version 24 | * Makefile: use xz compression on make dist rule 25 | * debian/control: use libncurses-dev instead of libncurses5-dev 26 | * debian/control: update standards version to 4.6.2 27 | * Update copyright year and email address 28 | 29 | -- Colin Ian King Tue, 7 Feb 2023 14:23:28 +0000 30 | 31 | eventstat (0.05.00-1) unstable; urgency=medium 32 | 33 | * Makefile: bump to version 5.0 34 | * Minor code cleanup, no functional changes 35 | * debian/control: update standards version to 4.6.1 36 | * Fix potential division by zero when duration is 0.0 37 | * Limit sscanf string field sizes to match size of buffers being used 38 | * Update copyright 39 | 40 | -- Colin Ian King Tue, 6 Dec 2022 11:47:22 +0000 41 | 42 | eventstat (0.04.13-1) unstable; urgency=medium 43 | 44 | * Makefile: bump version 45 | * Makefile: add README.md to dist rule 46 | * manual: update date, author email and copyright 47 | * debian/watch: update watch path for tarball, move to version 4 48 | * debian/copyright: Update copyright and git URL 49 | * debian/control: update author email address and project URL 50 | * Add README.md file 51 | * snapcraft: update to core20 52 | 53 | -- Colin Ian King Wed, 10 Nov 2021 08:32:15 +0000 54 | 55 | eventstat (0.04.12-1) unstable; urgency=medium 56 | 57 | * Makefile: bump version 58 | * Debian/control: remove uploaders field 59 | * Rename clock_tick_rate to g_clock_tick_rate 60 | * Make get_events_buf local 61 | * Copyright: update to 2021 62 | 63 | -- Colin King Thu, 29 Apr 2021 09:21:55 +0100 64 | 65 | eventstat (0.04.11-1) unstable; urgency=medium 66 | 67 | * Makefile: bump version 68 | * handle case where comm field contains ) and spaces 69 | * snapcraft: replace version-script with an override 70 | 71 | -- Colin King Sat, 7 Nov 2020 17:17:12 +0000 72 | 73 | eventstat (0.04.10-1) unstable; urgency=medium 74 | 75 | * Makefile: bump version 76 | * Debian/control: update compat to 13, remove compat file, 77 | add Rules-Requires-Root no rule 78 | * zero ws struct to clear static analysis warnings 79 | 80 | -- Colin King Sat, 4 Jul 2020 10:46:24 +0000 81 | 82 | eventstat (0.04.09-1) unstable; urgency=medium 83 | 84 | * Makefile: bump version 85 | * Add bash completion script 86 | * Update copyright to 2020 87 | 88 | -- Colin King Tue, 25 Feb 2020 23:53:22 +0000 89 | 90 | eventstat (0.04.08-1) unstable; urgency=medium 91 | 92 | * Add UNLIKELY hinting on memory allocation failure checks 93 | * check for failed allocation of comm field 94 | * Add null check on failed allocation of comm field 95 | * Add null check on return of ctime 96 | * Don't strdup basename return but strdup base string 97 | * Fix inverted null check logic on comm field 98 | * Add null check on localtime return 99 | * check that basename returns a non-null string 100 | * fix out of memory allocation failure check 101 | * eventstat: fix '\n' check on string (null ptr dereference) 102 | * eventstat: use comm field for task name rather than task info field 103 | 104 | -- Colin King Mon, 16 Dec 2019 11:11:11 +0000 105 | 106 | eventstat (0.04.07-1) unstable; urgency=medium 107 | 108 | * Makefile: bump version 109 | * Update compat to level 12 110 | * snap: clean up Makefile 111 | * snap: makefile: remove versioning magic 112 | * snapcraft: make versioning automatic 113 | 114 | -- Colin King Mon, 12 Aug 2019 16:30:11 +0100 115 | 116 | eventstat (0.04.06-1) unstable; urgency=medium 117 | 118 | * Makefile: bump version 119 | * Reduce scope of variable ticks 120 | * Remove unused assignments 121 | * Update copyright year 122 | 123 | -- Colin King Tue, 9 Jul 2019 23:43:24 +0100 124 | 125 | eventstat (0.04.05-1) unstable; urgency=medium 126 | 127 | * Makefile: bump version 128 | * Makefile: add .travis.yml file to dist rule 129 | * Makefile: rename snapcraft to snap in make dist rule 130 | * Add in some missing voidifications on function returns 131 | * Make task_mangled large enough to avoid truncation 132 | * Re-order some fields in timer_info to pack more efficiently 133 | * Rename snapcraft directory to snap 134 | * Add travis build yaml file 135 | 136 | -- Colin King Wed, 24 Oct 2018 09:40:01 +0100 137 | 138 | eventstat (0.04.04-1) unstable; urgency=medium 139 | 140 | * Makefile: bump version 141 | * debian/copyright: use secure url https:// etc 142 | * debian/control: remove trailing empty line 143 | * Fix -b brief mode headings 144 | * Ensure we have at least 1 task accounted for 145 | * Add CPU load, real time priority and niceness fields 146 | * Add fast check if we are inside a container 147 | 148 | -- Colin King Wed, 13 Jun 2018 16:13:12 +0100 149 | 150 | eventstat (0.04.03-1) unstable; urgency=medium 151 | 152 | * Makefile: bump version 153 | * debian/control: update debhelper version 154 | * Update debian/compat to 10 155 | * debian/rules: remove trailing whitespace, cleans up lintian warning 156 | * debian/control: update Standards-Version to 4.1.2, remove some whitespace 157 | * re-format some overly wide source lines 158 | * Don't force wide display unless long mode is used 159 | * eventstat: add some more UNLIKELY/LIKELY annotations 160 | * Update copyright dates 161 | 162 | -- Colin King Thu, 1 Feb 2018 10:03:05 +0000 163 | 164 | eventstat (0.04.02-1) unstable; urgency=medium 165 | 166 | * Makefile: bump version 167 | * Iterate over arrays using size of array rather than using 168 | sentinels at end of array 169 | * Remove empty lines 170 | * Voidify returns from various functions 171 | * Add some UNLIKELY/LIKELY branch hinting 172 | * debian/control: update Standards-Version to 4.1.1 173 | * Prefix all globals with g_ to notify scope 174 | * Use sizeof object rather than size of type 175 | * Remove whitespace from source 176 | 177 | -- Colin King Wed, 18 Oct 2017 22:04:12 +0100 178 | 179 | eventstat (0.04.01-1) unstable; urgency=medium 180 | 181 | * Makefile: bump version 182 | * Remove unused variable fields 183 | * Fix -c cumulative stats 184 | * Make column sizing more intelligent 185 | * snapcraft: Makefile: set LC_ALL=C.UTF-8 LANG=C.UTF-8 186 | * Add back ref_count reference counting which was accidentally removed 187 | * Compute column width correctly with -i mode being used 188 | * Make source more 80 column friendly 189 | * Use double_to_timeval instead of time coversion 190 | * Minor code clean up, no functional changes 191 | * Fix non-root failure message 192 | * Break out of trace reading loop on SIGINT 193 | * Add -i timer ID information mode 194 | * Add libncurses5-dev to snapcraft build dependencies 195 | 196 | -- Colin King Wed, 16 Aug 2017 18:48:43 +0100 197 | 198 | eventstat (0.04.00-1) unstable; urgency=medium 199 | 200 | * Makefile: bump version 201 | * Debian/control: update Standards-Version to 4.0.0 202 | * Update manual 203 | * Fix reversed order sample list, put data on tail instead of head 204 | * Add back functioning -S option now that stats look sane 205 | * Fake initial previous time event on first event. 206 | * Remove whence debug info 207 | * Eventstat: use event tracing rather than the older deprecated 208 | /proc/timer_stats interface (LP: #1705472) 209 | * Make symlink to snapcraft file 210 | * Snapcraft: add default type and grade keys to yaml 211 | 212 | -- Colin King Mon, 14 Aug 2017 19:47:14 +0100 213 | 214 | eventstat (0.03.04-1) unstable; urgency=medium 215 | 216 | * Makefile: bump version 217 | * Add GNU C printf attribute to err_abort 218 | * Makefile: add snapcraft files to make dist rule 219 | * Add variable pid size handling. 220 | * Add snapcraft files 221 | * Update copyright year 222 | * Fix non-ANSI declaration of functions eventstat_winsize 223 | and eventstat_refresh 224 | 225 | -- Colin King Fri, 14 Apr 2017 16:38:32 +0100 226 | 227 | eventstat (0.03.03-1) unstable; urgency=medium 228 | 229 | * Makefile: bump version 230 | * Fix FLOAT_CMP macro - need fabs on result 231 | * Remove sdl declarations to fix sdl shadowing 232 | * constify info1, info2 233 | * Move scope of duration, removes shadowing of this variable 234 | * Allow floating point comparisons a little bit of tiny slop 235 | * Add stdarg.h header 236 | * use gnu printf style attributes for es_printf 237 | * Makefile: add PEDANTIC flags 238 | 239 | -- Colin King Wed, 2 Nov 2016 09:12:11 +0100 240 | 241 | eventstat (0.03.02-1) unstable; urgency=medium 242 | 243 | * Makefile: bump version 244 | * debian/control: update Standards-Version to 3.9.8 245 | 246 | -- Colin King Thu, 5 May 2016 13:19:39 +0100 247 | 248 | eventstat (0.03.01-1) unstable; urgency=medium 249 | 250 | * Makefile: bump version 251 | * Update manual to reflect -l -s mode on tty output 252 | * Show long or short command line info in tty output and not just in CSV 253 | * Add smart column resizing based on variable tty width 254 | * Add some small ncurses helpers for top only mode 255 | * Clean up -h info, make it 80 column friendly 256 | * Remove a few empty lines in source 257 | * Re-align global vars so there is less wasted padding 258 | * Make source 80 column friendly 259 | * es_printf: emit curses output if curses_init is true 260 | * Reformat overly long function declarations 261 | * Minor code improvement in set_timer_stat 262 | * check for failed sigaction on SIGWINCH 263 | * Improve error handling with some error message and clean up helpers 264 | * Call endwin() only in top mode 265 | 266 | -- Colin King Thu, 11 Feb 2016 11:09:11 +0000 267 | 268 | eventstat (0.03.00-1) unstable; urgency=medium 269 | 270 | * Makefile: bump version 271 | * reduce scope of buf in es_printf 272 | * Fix spacing in cumulative mode, missing space after count and before pid 273 | * Add 'T' option, 'top' mode 274 | * Update copyright year to 2016 275 | 276 | -- Colin King Thu, 11 Feb 2016 01:12:34 +0000 277 | 278 | eventstat (0.02.02-1) unstable; urgency=medium 279 | 280 | * fix segfault when removing stale timer stats (LP: #1496900) 281 | * Makefile: bump version 282 | * Debian: update compat to 9 283 | * Debian: control, update debhelper dependency on version 9 284 | * remove duplicate initialisation to ts from timer_stats hash 285 | * Don't initialise duration, it is set later on 286 | 287 | -- Colin King Thu, 17 Sep 2015 16:14:17 +0100 288 | 289 | eventstat (0.02.01-1) unstable; urgency=medium 290 | 291 | * Makefile: bump version 292 | * Makefile: Use -O2 optimisation level 293 | * Reduce scope of variable i 294 | * Shorten hash ident string, we only require so much to hash on 295 | * Signed-off-by: Colin Ian King 296 | * Minor code clean up 297 | * Replace generic lists with type specific lists 298 | * Move __attribute__ ((noreturn)) 299 | 300 | -- Colin King Fri, 31 Jul 2015 00:00:09 +0100 301 | 302 | eventstat (0.02.00-1) unstable; urgency=medium 303 | 304 | * Makefile: bump version to 0.02.00 305 | * Re-order man page options into alphabetical order 306 | * Add -w "whence" timestamp row option 307 | * Add time column to CSV output 308 | 309 | -- Colin King Thu, 9 Jul 2015 21:33:00 +0100 310 | 311 | eventstat (0.01.37-1) unstable; urgency=medium 312 | 313 | * Makefile: bump version 314 | * Fix repeaped prev pointer not advancing 315 | * Fix resource leak on file descriptor on error exit path 316 | * Add auto-purge of old state timer stats to stop memory growth 317 | * Speed up reading and parsing of the timer stat data 318 | * Add some performance optimisations, better hashing and heap management 319 | * Make hash ident a few bytes smaller for marginal compare speedup 320 | * Replace hash function with one that is 3.3x faster 321 | * Fix hash chain growth by comparing with ident rather than an NULL field 322 | * Free cmdline on mask check continue path 323 | * Remove SIGSEGV from trapped signals list 324 | * constify a few more variables 325 | * Fix memory leak on cmdline, found by CoverityScan 326 | * Improve kernel thread detection on unmodified task name (LP: #1467932) 327 | * Compare process hashes based on original process name 328 | 329 | -- Colin King Thu, 25 Jun 2015 16:51:00 +0100 330 | 331 | eventstat (0.01.36-1) unstable; urgency=medium 332 | 333 | * Makefile: bump version 334 | * Manual: update date 335 | * Remove signal handlers for SIGBUS, SIGABRT, SIGILL 336 | * Makefile: add copyright 337 | 338 | -- Colin King Mon, 18 May 2015 18:22:00 +0100 339 | 340 | eventstat (0.01.35-1) unstable; urgency=medium 341 | 342 | * Makefile: bump version again 343 | * Fix the per sample time delta calculations 344 | 345 | -- Colin King Thu, 12 Feb 2015 20:24:00 +0000 346 | 347 | eventstat (0.01.34-1) unstable; urgency=medium 348 | 349 | * Makefile: bump version 350 | * Update copyright to manual 351 | * Update debian/copyright 352 | * Update copyright in source 353 | * Clarify calculation precedence for '&' and '?' 354 | * Handle time deltas more exactly, this is required of eventstat is suspended. 355 | 356 | -- Colin King Thu, 12 Feb 2015 19:36:00 +0000 357 | 358 | eventstat (0.01.33-1) unstable; urgency=medium 359 | 360 | * Makefile: bump version 361 | * Update standards version 362 | * Use suseconds_t for nsec conversions 363 | * Using stdint int types 364 | * Change TIMER_STATS to proc_timer_stats static const string 365 | * Add better signal handling; clear timer stat setting and bail out 366 | * Add whitespace in hash function 367 | * Make APP_NAME a const string and rename to app_name 368 | * Use strncmp where possible 369 | * Add more error checking on gettimeofday 370 | * Fix up some overly long lines 371 | * Check for scanf failure 372 | * Ignore returns from close, fclose 373 | 374 | -- Colin King Fri, 19 Dec 2014 12:16:00 +0000 375 | 376 | eventstat (0.01.32-1) unstable; urgency=low 377 | 378 | * Makefile: bump version 379 | * Handle overflow/underflow invalid values for count arg 380 | * Handle overflow/underflow invalid values for -n option 381 | * Handle bad options with help and exit failure 382 | 383 | -- Colin King Sun, 18 May 2014 23:59:49 +0100 384 | 385 | eventstat (0.01.31-1) unstable; urgency=low 386 | 387 | * Makefile: bump version 388 | * Makefile: Fix make dist, use plain old tar to omit debian contents 389 | * Fix copyright 390 | * Add COPYING file 391 | 392 | -- Colin King Thu, 20 Mar 2014 13:22:31 +0000 393 | 394 | eventstat (0.01.30-1) unstable; urgency=low 395 | 396 | * Makefile: bump version 397 | * Avoid some potential event overflows 398 | * Reduce scope on some variables 399 | 400 | -- Colin King Fri, 14 Mar 2014 09:08:12 +0000 401 | 402 | eventstat (0.01.29-1) unstable; urgency=low 403 | 404 | * Makefile: bump version 405 | * debian control: update standards version 406 | * Add -u and -k options 407 | * Add the -b brief output option 408 | 409 | -- Colin King Tue, 21 Jan 2014 11:01:00 +0000 410 | 411 | eventstat (0.01.28-1) unstable; urgency=low 412 | 413 | * Makefile: bump version 414 | * Update copyright year 415 | * Makefile: add -Wextra 416 | * Simplify kernel thread detection for the common path 417 | 418 | -- Colin King Tue, 7 Jan 2014 17:10:00 +0000 419 | 420 | eventstat (0.01.27-1) unstable; urgency=low 421 | 422 | * Makefile: bump version 423 | * eventstat: fix kernel thread detection issue (LP: #1226787) 424 | 425 | -- Colin King Thu, 19 Sep 2013 16:05:06 +0100 426 | 427 | eventstat (0.01.26-1) unstable; urgency=low 428 | 429 | * Makefile: bump version 430 | * eventstat: fix comment typo 431 | 432 | -- Colin King Thu, 29 Aug 2013 11:37:03 +0100 433 | 434 | eventstat (0.01.25-1) unstable; urgency=low 435 | 436 | * eventstat: work around proc info containerisation restrictions 437 | * eventstat.c: Add -d to short help info 438 | 439 | -- Colin King Wed, 26 Jun 2013 18:06:12 +0100 440 | 441 | eventstat (0.01.24-1) unstable; urgency=low 442 | 443 | * Makefile: bump version 444 | * eventstat.c: make hash table much larger 445 | * eventstat.c: make more functions static 446 | * eventstat.c: ensure initial info total is set to the count and not zero 447 | 448 | -- Colin King Tue, 25 Jun 2013 17:12:48 +0100 449 | 450 | eventstat (0.01.23-1) unstable; urgency=low 451 | 452 | * Makefile: bump version 453 | * eventstat.c: cater for -ve time if somebody suspended the process 454 | * eventstat.c: handle zero duration corner case 455 | * eventstat.c: remove 'N' new field, it's not helpful 456 | * eventstat.c: remove debug 457 | * eventstat.c: fix stats on SIGINT and missing first stats issue 458 | 459 | -- Colin King Mon, 24 Jun 2013 18:13:22 +0100 460 | 461 | eventstat (0.01.22-1) unstable; urgency=low 462 | 463 | * Makefile: bump version 464 | * eventstat: fix a fd leak on read fail error path 465 | 466 | -- Colin King Tue, 7 May 2013 15:36:22 +0100 467 | 468 | eventstat (0.01.21-1) unstable; urgency=low 469 | 470 | * Makefile: bump version 471 | * eventstat: handle_sigint: cast to void unused dummy parameter 472 | * eventstat: samples_dump - make i size_t since we compare it to n 473 | * eventstat: put inline at start of sample_find declaration 474 | * eventstat: update copyright year 475 | * eventstat: fix order of calloc() args. 476 | * eventstat: more code clean ups: 477 | * eventstat: line up #defines 478 | * eventstat: fix some whitespacing in for loops 479 | * eventstat: update -h help information 480 | * evenstat.8: update man page with new -S -C options 481 | * Makefile: Add math linking now that we are using sqrt() 482 | * eventstat: extra features for the CSV output. 483 | * eventstat.8: add more description of the CSV output format 484 | * eventstat.8: update man page with new -d option 485 | * eventstat: add -d option to strip off directory name from task name 486 | * eventstat: truncate short -s proc name when we find a space 487 | 488 | -- Colin King Fri, 5 Apr 2013 10:49:32 +0100 489 | 490 | eventstat (0.01.20-1) unstable; urgency=low 491 | 492 | * Makefile: bump version 493 | * eventstat: ensure we don't fall off end of buffer 494 | * evenstat.8: add -s -l options to man page 495 | * eventstat: Add /proc/pid/cmdline process info in CSV output 496 | * eventstat.8: ensure - sign is escaped in manual 497 | 498 | -- Colin King Tue, 2 Apr 2013 15:13:00 +0100 499 | 500 | eventstat (0.01.19-1) unstable; urgency=low 501 | 502 | * Makefile: bump version 503 | * eventstat: make set_timer_stat conditionally complain if it fails 504 | * eventstat: remove need for PATH_MAX, replace with suitable size 505 | * debian: control: set Architecture to just linux-any 506 | * eventstat: ensure all exits tidy up by setting the timer stat to 0 507 | * eventstat: exit with EXIT_FAILURE rather than 1 508 | * eventstat: handle failed calloc on timer stat tables 509 | 510 | -- Colin King Tue, 19 Feb 2013 22:04:06 +0000 511 | 512 | eventstat (0.01.18-1) unstable; urgency=low 513 | 514 | * Makefile: bump version 515 | * Makefile: include CFLAGS and LDFLAGS into CC line 516 | * debian: rules: force dh_ to apply flags during make phase 517 | * Remove unwanted debian/powerstat.install 518 | 519 | -- Colin King Tue, 19 Feb 2013 12:04:06 +0000 520 | 521 | eventstat (0.01.17-1) unstable; urgency=low 522 | 523 | * Update version in Makefile 524 | * debian: control: update Standards-Version to 3.9.4 525 | 526 | -- Colin King Mon, 18 Feb 2013 16:55:24 +0000 527 | 528 | eventstat (0.01.16-1) unstable; urgency=low 529 | 530 | * Bump version, add version number to eventstat 531 | * Update and tidyup show_usage 532 | * Add cumulative events reporting instead of events per second 533 | * Increase the hash table size, reduce the collisions. 534 | * Ignore zero events 535 | * Show kernel threads names in [ ] brackets 536 | 537 | -- Colin King Mon, 18 Feb 2013 16:40:21 +0000 538 | 539 | eventstat (0.01.15-1) unstable; urgency=low 540 | 541 | * Initial Debian release (Closes: #678277) 542 | 543 | -- Colin King Wed, 20 Jun 2012 15:43:28 +0100 544 | 545 | eventstat (0.01.14-1) unstable; urgency=low 546 | 547 | * Update version 548 | * Fix debian/control Description lintian warning 549 | * Remove -lm from Makefile - not needed 550 | 551 | -- Colin King Wed, 20 Jun 2012 14:10:15 +0100 552 | 553 | eventstat (0.01.13-1) unstable; urgency=low 554 | 555 | * Update version number in Makefile 556 | * Fix debian copyright lintian errors 557 | * Add some more comments, fix trailing whitespaces 558 | * Update source copyright year 559 | * Update copyright year 560 | * Update debian/copyright 561 | 562 | -- Colin King Thu, 14 Jun 2012 10:40:23 +0100 563 | 564 | eventstat (0.01.12-1) unstable; urgency=low 565 | 566 | * Updated version 567 | * Add in project homepage 568 | * Update manpage section and date 569 | * Update copyright URL 570 | * Make description more litian friendly 571 | * fix typo on debian/watch 572 | 573 | -- Colin King Wed, 13 Jun 2012 19:18:31 +0100 574 | 575 | eventstat (0.01.11-1) unstable; urgency=low 576 | 577 | * Update version in Makefile 578 | * Add debian/watch file 579 | * Update Maintainer field and add Kamal Mostafa as an Uploader 580 | 581 | -- Colin King Tue, 12 Jun 2012 18:51:34 +0100 582 | 583 | eventstat (0.01.10-1) unstable; urgency=low 584 | 585 | * Add dist rule to Makefile 586 | * Add debian/source/format 587 | * debian: control: update Standards-Version to 3.9.3 588 | * debian: control: fix Section 589 | * debian: control: fix Description 590 | * Remove more timer_stat_dump cruft 591 | 592 | -- Colin King Tue, 12 Jun 2012 16:56:23 +0100 593 | 594 | eventstat (0.01.09) precise; urgency=low 595 | 596 | * Remove timer_stat_dump cruft 597 | * Name callback correctly, update field headings 598 | 599 | -- Colin King Wed, 28 Dec 2011 13:21:00 +0000 600 | 601 | eventstat (0.01.08) precise; urgency=low 602 | 603 | * Tidy up lists 604 | 605 | -- Colin King Tue, 27 Dec 2011 11:54:00 +0000 606 | 607 | eventstat (0.01.07) precise; urgency=low 608 | 609 | * Add in list init calls 610 | 611 | -- Colin King Mon, 26 Dec 2011 10:16:00 +0000 612 | 613 | eventstat (0.01.06) precise; urgency=low 614 | 615 | * fix cut and paste error in man page 616 | 617 | -- Colin King Fri, 23 Dec 2011 20:36:00 +0000 618 | 619 | eventstat (0.01.05) precise; urgency=low 620 | 621 | * Add -q quiet option 622 | 623 | -- Colin King Tue, 20 Dec 2011 00:43:00 +0000 624 | 625 | eventstat (0.01.04) precise; urgency=low 626 | 627 | * Output events per second in .csv results 628 | * Add -t threshold option 629 | * Convert to use generic lists, phase 2 630 | * Introduce generic lists 631 | 632 | -- Colin King Mon, 19 Dec 2011 10:40:00 +0000 633 | 634 | eventstat (0.01.03) precise; urgency=low 635 | 636 | * Stop on SIGINT, handle timing more accurately 637 | 638 | -- Colin King Sun, 18 Dec 2011 17:35:00 +0000 639 | 640 | eventstat (0.01.02) precise; urgency=low 641 | 642 | * Dump out samples into a CSV file with -r option 643 | * Add timer info cache, more memory efficient 644 | * Add help option 645 | * Add -n option 646 | * Fix count error message. 647 | 648 | -- Colin King Sun, 18 Dec 2011 17:11:00 +0000 649 | 650 | eventstat (0.01.01) precise; urgency=low 651 | 652 | * Remove trailing ws 653 | * Ensure we write to timer_stats correctly 654 | 655 | -- Colin King Sat, 17 Dec 2011 21:29:00 +0000 656 | 657 | eventstat (0.01.00) precise; urgency=low 658 | 659 | * Initial version 660 | 661 | -- Colin King Sat, 17 Dec 2011 20:44:00 +0000 662 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: eventstat 2 | Rules-Requires-Root: no 3 | Section: admin 4 | Priority: optional 5 | Maintainer: Colin Ian King 6 | Standards-Version: 4.6.2 7 | Build-Depends: debhelper (>= 13), 8 | debhelper-compat (=13), 9 | libncurses-dev 10 | Homepage: https://github.com/ColinIanKing/eventstat 11 | 12 | Package: eventstat 13 | Architecture: linux-any 14 | Depends: ${shlibs:Depends}, ${misc:Depends} 15 | Description: kernel event states monitoring tool 16 | Eventstat periodically dumps out the current kernel event state. 17 | It keeps track of current events and outputs the change in events 18 | on each output update. The tool requires sudo to run since it 19 | needs to write to /proc/timer_stats to start and stop the event 20 | monitoring. 21 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: eventstat 3 | Upstream-Contact: Colin Ian King 4 | Source: https://github.com/ColinIanKing/eventstat 5 | 6 | Files: * 7 | Copyright: 2012-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/eventstat/tags \ 4 | (?:.*?/)?V?(\d[\d.]*)\.tar\.gz debian uupdate 5 | 6 | -------------------------------------------------------------------------------- /eventstat.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 EVENTSTAT 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 | .SH NAME 19 | eventstat \- a tool to measure system events. 20 | .br 21 | 22 | .SH SYNOPSIS 23 | .B eventstat 24 | .RI [options] " [delay " [count]] 25 | .br 26 | 27 | .SH DESCRIPTION 28 | eventstat is a program that dumps the current active system events that are 29 | added to the kernel timer list. 30 | 31 | .SH OPTIONS 32 | eventstat options are as follow: 33 | .TP 34 | .B \-b 35 | just report events, PID and process name. By default the short task name from 36 | the kernel comm field will be displayed, however the \-s and \-l options 37 | will report more process name information. 38 | .TP 39 | .B \-c 40 | report cumulative events rather than events per sample period. 41 | .TP 42 | .B \-C 43 | report the sample event count in the CSV output rather than the default 44 | events per second rate. 45 | .TP 46 | .B \-d 47 | strip full directory path off the process name in the CSV output. 48 | .TP 49 | .B \-h 50 | show help. 51 | .TP 52 | .B \-i 53 | show timer ID information. 54 | .TP 55 | .B \-k 56 | report just kernel threads. 57 | .TP 58 | .B \-l 59 | report long process name from /proc/pid/cmdline. This reports 60 | the process name and all the command line arguments. 61 | .TP 62 | .B \-n event_count 63 | only display the first event_count number of top events. 64 | .TP 65 | .B \-q 66 | run quietly, only really makes sense with \-r option. 67 | .TP 68 | .B \-r csv_file 69 | output gathered data in a comma separated values file. This 70 | can be then imported and graphed using your favourite open 71 | source spread sheet. 72 | .TP 73 | .B \-s 74 | report short process name from /proc/pid/cmdline. This reports 75 | just the process name. 76 | .TP 77 | .B \-S 78 | report the minimum, maximum, average and population standard deviation 79 | at the end of the CSV output. 80 | .TP 81 | .B \-t threshold 82 | ignore samples where the event delta per second less than the given threshold. 83 | .TP 84 | .B \-T 85 | enable 'top' mode, refresh display on each update. 86 | .TP 87 | .B \-u 88 | report just user space processes. 89 | .TP 90 | .B \-w 91 | add timestamp (the "whence" info) to the output. 92 | .SH EXAMPLES 93 | .LP 94 | Dump events every second until stopped. 95 | .RS 8 96 | sudo eventstat 97 | .RE 98 | .LP 99 | Dump the top 20 events every 60 seconds until stopped. 100 | .RS 8 101 | sudo eventstat \-n 20 60 102 | .RE 103 | .LP 104 | Dump events every 10 seconds just 5 times. 105 | .RS 8 106 | sudo eventstat 10 5 107 | .RE 108 | .LP 109 | Quietly dump events every 10 seconds just 5 times into a CSV file with short process name. 110 | .RS 8 111 | sudo eventstat 10 5 \-q \-s \-r results.csv 112 | .RE 113 | .SH CSV OUTPUT 114 | .LP 115 | The \-r option generates a comma separated file report that can be imported into 116 | spreadsheets or parsed using text processing tools. Column 1 of the data is the 117 | label for each row, columns 2 onwards contain the data for each task that generated 118 | a wakeup event. 119 | .LP 120 | The first row lists the task name of the thread or process. Task names in [ ] brackets are 121 | kernel threads, other tasks are the names of user space processes. By default these 122 | names are derived from the task names from kernel trace events but the \-s \-l options 123 | fetch more complete task names from /proc/pid/cmdline instead. 124 | .LP 125 | The second and third rows list the names of the internal Linux kernel timer init function. 126 | .LP 127 | The fourth row lists the total number of wakeup events for each task during the entire run of eventstat. 128 | .LP 129 | The subsequent rows list the average number of wakeups per second measured during the sample interval for each 130 | task in column two onwards. The first column indicates the sample time (in seconds) since the start of the measuring. 131 | .SH NOTES 132 | Version 4.0 of eventstat gathers event timer data from the kernel trace event timers and hence is 133 | not compatible with previous versions of eventstat. The move to using kernel trace events was 134 | necessary as the Linux 4.12 kernel dropped support for the /proc/timer_stats interface. 135 | .SH SEE ALSO 136 | .BR powertop (8), 137 | .BR top (1) 138 | .SH AUTHOR 139 | eventstat was written by Colin King 140 | .PP 141 | This manual page was written by Colin King , 142 | for the Ubuntu project (but may be used by others). 143 | .SH COPYRIGHT 144 | Copyright \(co 2011-2021 Canonical Ltd, Copyright \(co 2021-2025 Colin Ian King. 145 | .br 146 | This is free software; see the source for copying conditions. There is NO 147 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 148 | -------------------------------------------------------------------------------- /eventstat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-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 | * eventstat by Colin Ian King 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 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 TABLE_SIZE (1009) /* Should be a prime */ 47 | 48 | #define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(a[0])) 49 | 50 | #define OPT_QUIET (0x00000001) 51 | #define OPT_CUMULATIVE (0x00000002) 52 | #define OPT_CMD_SHORT (0x00000004) 53 | #define OPT_CMD_LONG (0x00000008) 54 | #define OPT_DIRNAME_STRIP (0x00000010) 55 | #define OPT_SAMPLE_COUNT (0x00000020) 56 | #define OPT_RESULT_STATS (0x00000040) 57 | #define OPT_BRIEF (0x00000080) 58 | #define OPT_KERNEL (0x00000100) 59 | #define OPT_USER (0x00000200) 60 | #define OPT_SHOW_WHENCE (0x00000400) 61 | #define OPT_TOP (0x00000800) 62 | #define OPT_TIMER_ID (0x00001000) 63 | #define OPT_CMD (OPT_CMD_SHORT | OPT_CMD_LONG) 64 | 65 | #define EVENT_BUF_SIZE (64 * 1024) 66 | #define TIMER_REAP_AGE (600) /* Age of timer before it is reaped */ 67 | #define TIMER_REAP_THRESHOLD (30) 68 | #define EVENTS_WIDTH (8) 69 | #define TASK_WIDTH (15) 70 | #define TIMER_ID_WIDTH (16) 71 | #define FUNC_WIDTH (24) 72 | #define FUNC_WIDTH_MAX (30) 73 | 74 | #define _VER_(major, minor, patchlevel) \ 75 | ((major * 10000) + (minor * 100) + patchlevel) 76 | 77 | #if defined(__GNUC__) && defined(__GNUC_MINOR__) 78 | #if defined(__GNUC_PATCHLEVEL__) 79 | #define NEED_GNUC(major, minor, patchlevel) \ 80 | _VER_(major, minor, patchlevel) <= \ 81 | _VER_(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) 82 | #else 83 | #define NEED_GNUC(major, minor, patchlevel) \ 84 | _VER_(major, minor, patchlevel) <= _VER_(__GNUC__, __GNUC_MINOR__, 0) 85 | #endif 86 | #else 87 | #define NEED_GNUC(major, minor, patchlevel) (0) 88 | #endif 89 | 90 | #if defined(__GNUC__) && NEED_GNUC(4,6,0) 91 | #define HOT __attribute__ ((hot)) 92 | #else 93 | #define HOT 94 | #endif 95 | 96 | #if defined(__GNUC__) && !defined(__clang__) && NEED_GNUC(4,6,0) 97 | #define OPTIMIZE3 __attribute__((optimize("-O3"))) 98 | #else 99 | #define OPTIMIZE3 100 | #endif 101 | 102 | #if defined(__GNUC__) 103 | #define LIKELY(x) __builtin_expect((x),1) 104 | #define UNLIKELY(x) __builtin_expect((x),0) 105 | #else 106 | #define LIKELY(x) (x) 107 | #define UNLIKELY(x) (x) 108 | #endif 109 | 110 | #define FLOAT_TINY (0.0000001) 111 | #define FLOAT_CMP(a, b) (fabs(a - b) < FLOAT_TINY) 112 | 113 | /* 114 | * timer_info_t contains per task timer infos. 115 | */ 116 | typedef struct timer_info { 117 | struct timer_info *next; /* Next in list */ 118 | struct timer_info *hash_next; /* Next in hash list */ 119 | pid_t pid; 120 | uint32_t ref_count; /* Timer stat reference count */ 121 | char *cmdline; /* From /proc/$pid/cmdline */ 122 | char *comm; /* from /proc/$pid/comm */ 123 | char *func; /* Kernel waiting func */ 124 | char *ident; /* Unique identity */ 125 | uint64_t timer; /* Timer ID */ 126 | uint64_t total_events; /* Total number of events */ 127 | uint64_t delta_events; /* Events in one time period */ 128 | uint16_t cpu_rt_prio; /* process priority level */ 129 | int16_t cpu_nice; /* process nice level */ 130 | uint32_t cpu_tasks; /* Number of tasks sharing ticks */ 131 | uint64_t cpu_ticks; /* CPU utilization ticks */ 132 | double cpu_ticks_time; /* CPU utilization ticks, last read */ 133 | double time_total; /* Total time */ 134 | double last_used; /* Last referenced */ 135 | double prev_used; /* Previous time used */ 136 | bool kernel_thread; /* True if task is a kernel thread */ 137 | bool just_added; /* True if recently added */ 138 | } timer_info_t; 139 | 140 | typedef struct timer_stat { 141 | struct timer_stat *next; /* Next timer stat in hash table */ 142 | struct timer_stat *sorted_freq_next; /* Next timer stat in event */ 143 | /* frequency sorted list */ 144 | timer_info_t *info; /* Timer info */ 145 | } timer_stat_t; 146 | 147 | /* sample delta item as an element of the sample_delta_list_t */ 148 | typedef struct sample_delta_item { 149 | struct sample_delta_item *next; /* next in list */ 150 | int64_t delta_events; /* delta in events */ 151 | double time_delta; /* difference in time between old */ 152 | /* and new */ 153 | timer_info_t *info; /* timer this refers to */ 154 | } sample_delta_item_t; 155 | 156 | /* list of sample_delta_items */ 157 | typedef struct sample_delta_list { 158 | struct sample_delta_list *next; /* next in list */ 159 | struct sample_delta_item *list; /* list of sample delta items */ 160 | double whence; /* when the sample was taken */ 161 | } sample_delta_list_t; 162 | 163 | typedef struct { 164 | char *task; /* Name of kernel task */ 165 | size_t len; /* Length */ 166 | } kernel_task_info; 167 | 168 | #define KERN_TASK_INFO(str) { str, sizeof(str) - 1 } 169 | 170 | static const char * const g_app_name = "eventstat"; 171 | static const char * const g_sys_tracing_enable = 172 | "/sys/kernel/debug/tracing/events/timer/hrtimer_start/enable"; 173 | static const char * const g_sys_tracing_pipe = 174 | "/sys/kernel/debug/tracing/trace_pipe"; 175 | static const char * const g_sys_tracing_set_event = 176 | "/sys/kernel/debug/tracing/set_event"; 177 | static const char * const g_sys_tracing_filter = 178 | "/sys/kernel/debug/tracing/events/timer/filter"; 179 | 180 | static void es_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 181 | 182 | static timer_stat_t *g_timer_stat_free_list; /* free list of timer stats */ 183 | static timer_info_t *g_timer_info_list; /* cache list of timer_info */ 184 | static timer_info_t *g_timer_info_hash[TABLE_SIZE]; /* hash of timer_info */ 185 | 186 | /* head of list of samples, sorted in sample time order */ 187 | static sample_delta_list_t *g_sample_delta_list_head; 188 | 189 | /* tail of list of samples, sorted in sample time order */ 190 | static sample_delta_list_t *g_sample_delta_list_tail; 191 | 192 | /* ignore samples with event delta less than this */ 193 | static double g_opt_threshold; 194 | 195 | static char *g_csv_results; /* results in comma separated values */ 196 | static uint32_t g_timer_info_list_length; /* length of timer_info_list */ 197 | static uint32_t g_opt_flags; /* option flags */ 198 | static volatile bool g_stop_eventstat = false; /* set by sighandler */ 199 | static bool g_sane_procs; /* false if we are in a container */ 200 | static bool g_resized; /* window resized */ 201 | static bool g_curses_init; /* curses initialised */ 202 | static int g_rows = 25; /* tty size, rows */ 203 | static int g_cols = 80; /* tty size, columns */ 204 | static uint64_t g_clock_tick_rate; /* system clock tick rate */ 205 | 206 | /* 207 | * Attempt to catch a range of signals so 208 | * we can clean 209 | */ 210 | static const int g_signals[] = { 211 | /* POSIX.1-1990 */ 212 | #ifdef SIGHUP 213 | SIGHUP, 214 | #endif 215 | #ifdef SIGINT 216 | SIGINT, 217 | #endif 218 | #ifdef SIGQUIT 219 | SIGQUIT, 220 | #endif 221 | #ifdef SIGFPE 222 | SIGFPE, 223 | #endif 224 | #ifdef SIGTERM 225 | SIGTERM, 226 | #endif 227 | #ifdef SIGUSR1 228 | SIGUSR1, 229 | #endif 230 | #ifdef SIGUSR2 231 | SIGUSR2, 232 | /* POSIX.1-2001 */ 233 | #endif 234 | #ifdef SIGXCPU 235 | SIGXCPU, 236 | #endif 237 | #ifdef SIGXFSZ 238 | SIGXFSZ, 239 | #endif 240 | /* Linux various */ 241 | #ifdef SIGIOT 242 | SIGIOT, 243 | #endif 244 | #ifdef SIGSTKFLT 245 | SIGSTKFLT, 246 | #endif 247 | #ifdef SIGPWR 248 | SIGPWR, 249 | #endif 250 | #ifdef SIGINFO 251 | SIGINFO, 252 | #endif 253 | #ifdef SIGVTALRM 254 | SIGVTALRM, 255 | #endif 256 | }; 257 | 258 | /* 259 | * pid_max_digits() 260 | * determine (or guess) maximum digits of pids 261 | */ 262 | static int pid_max_digits(void) 263 | { 264 | static int max_digits; 265 | ssize_t n; 266 | int fd; 267 | const int default_digits = 6; 268 | const int min_digits = 5; 269 | char buf[32]; 270 | 271 | if (LIKELY(max_digits)) 272 | goto ret; 273 | 274 | max_digits = default_digits; 275 | fd = open("/proc/sys/kernel/pid_max", O_RDONLY); 276 | if (fd < 0) 277 | goto ret; 278 | n = read(fd, buf, sizeof(buf) - 1); 279 | (void)close(fd); 280 | if (n < 0) 281 | goto ret; 282 | 283 | buf[n] = '\0'; 284 | max_digits = 0; 285 | while ((max_digits < n) && (buf[max_digits] >= '0') && (buf[max_digits] <= '9')) 286 | max_digits++; 287 | if (max_digits < min_digits) 288 | max_digits = min_digits; 289 | ret: 290 | return max_digits; 291 | } 292 | 293 | /* 294 | * hash_djb2a() 295 | * Hash a string, from Dan Bernstein comp.lang.c (xor version) 296 | */ 297 | static HOT OPTIMIZE3 uint32_t hash_djb2a(const char *str) 298 | { 299 | register uint32_t hash = 5381; 300 | register int c; 301 | 302 | while ((c = *str++)) { 303 | /* (hash * 33) ^ c */ 304 | hash = ((hash << 5) + hash) ^ c; 305 | hash = (hash * 33) ^ c; 306 | } 307 | return hash % TABLE_SIZE; 308 | } 309 | 310 | /* 311 | * eventstat_winsize() 312 | * get tty size 313 | */ 314 | static void eventstat_winsize(void) 315 | { 316 | struct winsize ws; 317 | 318 | (void)memset(&ws, 0, sizeof(ws)); 319 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) { 320 | g_rows = ws.ws_row; 321 | g_cols = ws.ws_col; 322 | } 323 | } 324 | 325 | /* 326 | * eventstat_clear() 327 | * clear screen if in top mode 328 | */ 329 | static inline void eventstat_clear(void) 330 | { 331 | if (g_curses_init) 332 | (void)clear(); 333 | } 334 | 335 | /* 336 | * eventstat_refresh() 337 | * refresh screen if in top mode 338 | */ 339 | static inline void eventstat_refresh(void) 340 | { 341 | if (g_curses_init) 342 | (void)refresh(); 343 | } 344 | 345 | /* 346 | * eventstat_move() 347 | * move cursor if in top mode 348 | */ 349 | static inline void eventstat_move(const int y, const int x) 350 | { 351 | if (g_curses_init) 352 | (void)move(y, x); 353 | } 354 | 355 | /* 356 | * eventstat_endwin() 357 | * call endwin if in top mode 358 | */ 359 | static void eventstat_endwin(void) 360 | { 361 | if (g_curses_init) { 362 | (void)clear(); 363 | (void)endwin(); 364 | } 365 | } 366 | 367 | /* 368 | * err_abort() 369 | * print an error and exit 370 | */ 371 | static void __attribute__ ((noreturn)) __attribute__((format(printf, 1, 2))) 372 | err_abort(const char *fmt, ...) 373 | { 374 | va_list ap; 375 | 376 | va_start(ap, fmt); 377 | eventstat_endwin(); 378 | (void)vfprintf(stderr,fmt, ap); 379 | va_end(ap); 380 | 381 | exit(EXIT_FAILURE); 382 | } 383 | 384 | /* 385 | * set_tracing() 386 | * enable/disable timer stat 387 | */ 388 | static void set_tracing(const char *path, const char *str, const bool carp) 389 | { 390 | int fd; 391 | const ssize_t len = (ssize_t)strlen(str); 392 | 393 | if ((fd = open(path, O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { 394 | if (carp) { 395 | err_abort("Cannot open %s, errno=%d (%s)\n", 396 | path, errno, strerror(errno)); 397 | } 398 | return; 399 | } 400 | if (write(fd, str, len) != len) { 401 | (void)close(fd); 402 | if (carp) { 403 | err_abort("Cannot write to %s, errno=%d (%s)\n", 404 | path, errno, strerror(errno)); 405 | } 406 | return; 407 | } 408 | (void)close(fd); 409 | } 410 | 411 | /* 412 | * set_tracing_enable() 413 | * enable/disable timer stat 414 | */ 415 | static inline void set_tracing_enable(const char *str, const bool carp) 416 | { 417 | set_tracing(g_sys_tracing_enable, str, carp); 418 | } 419 | 420 | static void set_tracing_event(void) 421 | { 422 | char buffer[64]; 423 | 424 | set_tracing(g_sys_tracing_set_event, "\n", true); 425 | set_tracing(g_sys_tracing_set_event, "hrtimer_start", true); 426 | set_tracing(g_sys_tracing_filter, "0", true); 427 | 428 | /* Ignore event stat and idle events */ 429 | (void)snprintf(buffer, sizeof(buffer), 430 | "common_pid != %d && common_pid != 0", getpid()); 431 | set_tracing(g_sys_tracing_filter, buffer, true); 432 | } 433 | 434 | 435 | /* 436 | * eventstat_exit() 437 | * exit and set timer stat to 0 438 | */ 439 | static void __attribute__ ((noreturn)) eventstat_exit(const int status) 440 | { 441 | set_tracing_enable("0\n", false); 442 | exit(status); 443 | } 444 | 445 | 446 | /* 447 | * timeval_to_double 448 | * timeval to a double (in seconds) 449 | */ 450 | static inline double timeval_to_double(const struct timeval *const tv) 451 | { 452 | return (double)tv->tv_sec + ((double)tv->tv_usec / 1000000.0); 453 | } 454 | 455 | /* 456 | * double_to_timeval 457 | * seconds in double to timeval 458 | */ 459 | static inline struct timeval double_to_timeval(const double val) 460 | { 461 | struct timeval tv; 462 | 463 | tv.tv_sec = val; 464 | tv.tv_usec = (val - (time_t)val) * 1000000.0; 465 | return tv; 466 | } 467 | 468 | /* 469 | * gettime_to_double() 470 | * get time as a double 471 | */ 472 | static double gettime_to_double(void) 473 | { 474 | struct timeval tv; 475 | 476 | if (UNLIKELY(gettimeofday(&tv, NULL) < 0)) 477 | err_abort("gettimeofday failed: errno=%d (%s)\n", 478 | errno, strerror(errno)); 479 | return timeval_to_double(&tv); 480 | } 481 | 482 | /* 483 | * sane_proc_pid_info() 484 | * detect if proc info mapping from /proc/timer_stats 485 | * maps to proc pids OK. If we are in a container or 486 | * we can't tell, return false. 487 | */ 488 | static bool sane_proc_pid_info(void) 489 | { 490 | FILE *fp; 491 | static const char pattern[] = "container="; 492 | const char *ptr = pattern; 493 | bool ret = true; 494 | 495 | /* Fast check */ 496 | if (access("/run/systemd/container", R_OK) == 0) 497 | return false; 498 | 499 | fp = fopen("/proc/1/environ", "r"); 500 | if (!fp) 501 | return false; 502 | 503 | while (!feof(fp)) { 504 | int ch = getc(fp); 505 | 506 | if (*ptr == ch) { 507 | ptr++; 508 | /* Match? So we're inside a container */ 509 | if (*ptr == '\0') { 510 | ret = false; 511 | break; 512 | } 513 | } else { 514 | /* No match, skip to end of var and restart scan */ 515 | do { 516 | ch = getc(fp); 517 | } while ((ch != EOF) && (ch != '\0')); 518 | ptr = pattern; 519 | } 520 | } 521 | (void)fclose(fp); 522 | 523 | return ret; 524 | } 525 | 526 | /* 527 | * handle_sig() 528 | * catch signal, flag a stop and restore timer stat 529 | */ 530 | static void handle_sig(int dummy) 531 | { 532 | (void)dummy; /* Stop unused parameter warning with -Wextra */ 533 | 534 | g_stop_eventstat = true; 535 | set_tracing_enable("0\n", false); 536 | } 537 | 538 | static uint32_t get_proc_cpu_tasks(const pid_t pid) 539 | { 540 | char path[PATH_MAX]; 541 | DIR *dir; 542 | struct dirent *d; 543 | uint32_t n = 0; 544 | 545 | (void)snprintf(path, sizeof(path), "/proc/%d/task", pid); 546 | dir = opendir(path); 547 | 548 | if (!dir) 549 | return 1; 550 | 551 | while ((d = readdir(dir)) != NULL) { 552 | if (d->d_name[0] != '.') 553 | n++; 554 | } 555 | 556 | (void)closedir(dir); 557 | 558 | /* We should always have at least 1 task! */ 559 | return (n < 1) ? 1 : n; 560 | } 561 | 562 | #define SKIP_FIELDS(n) \ 563 | skip = n; \ 564 | while (skip > 0 && *ptr) { \ 565 | if (*ptr == ' ') \ 566 | skip--; \ 567 | ptr++; \ 568 | } \ 569 | if (UNLIKELY(*ptr == '\0')) \ 570 | return -1; \ 571 | 572 | /* 573 | * get_proc_cpu_ticks() 574 | * get cpu ticks for a given pid 575 | * unique tasks 576 | */ 577 | static int get_proc_cpu_ticks( 578 | const pid_t pid, 579 | uint64_t *ticks, 580 | uint16_t *rt_prio, 581 | int16_t *niceness) 582 | { 583 | char buffer[4096], path[PATH_MAX], *ptr = buffer, *endptr, *tmp; 584 | int fd, skip; 585 | uint64_t utime, stime; 586 | ssize_t len; 587 | 588 | (void)snprintf(path, sizeof(path), "/proc/%d/stat", pid); 589 | 590 | *ticks = 0; 591 | 592 | if ((fd = open(path, O_RDONLY)) < 0) 593 | return -1; 594 | 595 | len = read(fd, buffer, sizeof(buffer) - 1); 596 | (void)close(fd); 597 | if (UNLIKELY(len <= 1)) 598 | return -1; 599 | 600 | buffer[len] = '\0'; 601 | 602 | /* 3173 (a.out) R 3093 3173 3093 34818 3173 4202496 165 0 0 0 3194 0 */ 603 | 604 | /* 605 | * We used to use scanf but this is really expensive and it 606 | * is far faster to parse the data via a more tedious means 607 | * of scanning down the buffer manually.. 608 | */ 609 | if ((pid_t)strtoul(ptr, &endptr, 10) != pid) 610 | return -1; 611 | 612 | if (endptr == ptr) 613 | return -1; 614 | 615 | ptr = endptr; 616 | if (UNLIKELY(*ptr != ' ')) 617 | return -1; 618 | ptr++; 619 | if (UNLIKELY((*ptr != '('))) 620 | return -1; 621 | ptr++; 622 | /* parse comm field */ 623 | for (tmp = ptr; *tmp; tmp++) { 624 | if (*tmp == ')') 625 | ptr = tmp; 626 | } 627 | ptr++; 628 | if (UNLIKELY(*ptr != ' ')) 629 | return -1; 630 | /* skip over state field */ 631 | ptr+=2 ; 632 | 633 | /* Skip over fields to the 14th field (utime) */ 634 | SKIP_FIELDS(11) 635 | 636 | /* Field 14, utime */ 637 | utime = strtoull(ptr, &endptr, 10); 638 | if (UNLIKELY(endptr == ptr)) 639 | return -1; 640 | ptr = endptr; 641 | if (UNLIKELY(*ptr != ' ')) 642 | return -1; 643 | ptr++; 644 | /* Field 15, stime */ 645 | stime = strtoull(ptr, &endptr, 10); 646 | if (UNLIKELY(endptr == ptr)) 647 | return -1; 648 | 649 | /* Skip over fields to the 19th field (utime) */ 650 | SKIP_FIELDS(4) 651 | 652 | /* Field 19, niceness */ 653 | *niceness = strtoul(ptr, &endptr, 10); 654 | if (UNLIKELY(endptr == ptr)) 655 | return -1; 656 | 657 | SKIP_FIELDS(21) 658 | /* Field 40, rt_priority */ 659 | *rt_prio = strtol(ptr, &endptr, 10); 660 | if (UNLIKELY(endptr == ptr)) 661 | return -1; 662 | 663 | *ticks = utime + stime; 664 | return 0; 665 | } 666 | 667 | /* 668 | * samples_free() 669 | * free collected samples 670 | */ 671 | static inline void samples_free(void) 672 | { 673 | sample_delta_list_t *sdl = g_sample_delta_list_head; 674 | 675 | while (sdl) { 676 | sample_delta_list_t *sdl_next = sdl->next; 677 | sample_delta_item_t *sdi = sdl->list; 678 | 679 | while (sdi) { 680 | sample_delta_item_t *sdi_next = sdi->next; 681 | free(sdi); 682 | sdi = sdi_next; 683 | } 684 | free(sdl); 685 | sdl = sdl_next; 686 | } 687 | } 688 | 689 | /* 690 | * sample_add() 691 | * add a timer_stat's delta and info field to a 692 | * list at time position whence 693 | */ 694 | static void sample_add(timer_stat_t *timer_stat, const double whence) 695 | { 696 | bool found = false; 697 | sample_delta_list_t *sdl; 698 | sample_delta_item_t *sdi; 699 | 700 | if (g_csv_results == NULL) /* No need if not enabled */ 701 | return; 702 | 703 | for (sdl = g_sample_delta_list_head; sdl; sdl = sdl->next) { 704 | if (FLOAT_CMP(sdl->whence, whence)) { 705 | found = true; 706 | break; 707 | } 708 | } 709 | 710 | /* 711 | * New time period, need new sdl, we assume it goes at the end of the 712 | * list since time is assumed to be increasing 713 | */ 714 | if (!found) { 715 | sdl = calloc(1, sizeof(*sdl)); 716 | if (UNLIKELY(!sdl)) 717 | err_abort("Cannot allocate sample delta list\n"); 718 | sdl->whence = whence; 719 | 720 | if (g_sample_delta_list_tail) { 721 | g_sample_delta_list_tail->next = sdl; 722 | g_sample_delta_list_tail = sdl; 723 | } else { 724 | g_sample_delta_list_head = sdl; 725 | g_sample_delta_list_tail = sdl; 726 | } 727 | } 728 | 729 | /* Now append the sdi onto the list */ 730 | sdi = calloc(1, sizeof(*sdi)); 731 | if (UNLIKELY(!sdi)) 732 | err_abort("Cannot allocate sample delta item\n"); 733 | sdi->delta_events = timer_stat->info->delta_events; 734 | sdi->time_delta = timer_stat->info->last_used - 735 | timer_stat->info->prev_used; 736 | sdi->info = timer_stat->info; 737 | sdi->next = sdl->list; 738 | sdl->list = sdi; 739 | } 740 | 741 | /* 742 | * sample_find() 743 | * scan through a sample_delta_list for timer info, 744 | * return NULL if not found 745 | */ 746 | inline HOT static sample_delta_item_t *sample_find( 747 | sample_delta_list_t *sdl, 748 | const timer_info_t *info) 749 | { 750 | sample_delta_item_t *sdi; 751 | 752 | for (sdi = sdl->list; sdi; sdi = sdi->next) { 753 | if (sdi->info == info) 754 | return sdi; 755 | } 756 | return NULL; 757 | } 758 | 759 | /* 760 | * info_compare_total() 761 | * used by qsort to sort array in sample event total order 762 | */ 763 | static int info_compare_total(const void *item1, const void *item2) 764 | { 765 | timer_info_t *const *info1 = (timer_info_t *const *)item1; 766 | timer_info_t *const *info2 = (timer_info_t *const *)item2; 767 | 768 | if (UNLIKELY((*info2)->total_events == (*info1)->total_events)) 769 | return 0; 770 | 771 | return ((*info2)->total_events > (*info1)->total_events) ? 1 : -1; 772 | } 773 | 774 | static bool pid_a_kernel_thread_guess(const char *task) 775 | { 776 | /* 777 | * This is not exactly accurate, but if we can't look up 778 | * a process then try and infer something from the comm field. 779 | * Until we have better kernel support to map /proc/timer_stats 780 | * pids to containerised pids this is the best we can do. 781 | */ 782 | static const kernel_task_info kernel_tasks[] = { 783 | KERN_TASK_INFO("swapper/"), 784 | KERN_TASK_INFO("kworker/"), 785 | KERN_TASK_INFO("ksoftirqd/"), 786 | KERN_TASK_INFO("watchdog/"), 787 | KERN_TASK_INFO("migration/"), 788 | KERN_TASK_INFO("irq/"), 789 | KERN_TASK_INFO("mmcqd/"), 790 | KERN_TASK_INFO("jbd2/"), 791 | KERN_TASK_INFO("kthreadd"), 792 | KERN_TASK_INFO("kthrotld"), 793 | KERN_TASK_INFO("kswapd"), 794 | KERN_TASK_INFO("ecryptfs-kthrea"), 795 | KERN_TASK_INFO("kauditd"), 796 | KERN_TASK_INFO("kblockd"), 797 | KERN_TASK_INFO("kcryptd"), 798 | KERN_TASK_INFO("kdevtmpfs"), 799 | KERN_TASK_INFO("khelper"), 800 | KERN_TASK_INFO("khubd"), 801 | KERN_TASK_INFO("khugepaged"), 802 | KERN_TASK_INFO("khungtaskd"), 803 | KERN_TASK_INFO("flush-"), 804 | KERN_TASK_INFO("bdi-default-"), 805 | { NULL, 0 } 806 | }; 807 | 808 | size_t i; 809 | 810 | for (i = 0; i < SIZEOF_ARRAY(kernel_tasks); i++) { 811 | if (!strncmp(task, kernel_tasks[i].task, kernel_tasks[i].len)) 812 | return true; 813 | } 814 | return false; 815 | } 816 | 817 | /* 818 | * pid_a_kernel_thread 819 | * 820 | */ 821 | static bool pid_a_kernel_thread(const char *task, const pid_t id) 822 | { 823 | const pid_t pgid = (id == 0) ? 0 : getpgid(id); 824 | 825 | /* We are either in a container, or with a task with a NULL cmdline */ 826 | if (LIKELY(g_sane_procs || (id >= 0))) 827 | return (pgid == 0); 828 | 829 | /* Can't get pgid on that pid, so make a guess */ 830 | return pid_a_kernel_thread_guess(task); 831 | } 832 | 833 | /* 834 | * unknown_comm() 835 | * return an unknonw comm field replacement 836 | */ 837 | static char *unknown_comm(void) 838 | { 839 | return strdup(""); 840 | } 841 | 842 | /* 843 | * get_pid_comm 844 | * get process /proc/pid/comm field info 845 | */ 846 | static char *get_pid_comm(const pid_t id, bool kernel_thread) 847 | { 848 | char buffer[256]; 849 | ssize_t ret; 850 | char *comm, *ptr; 851 | int fd; 852 | 853 | (void)snprintf(buffer, sizeof(buffer), "/proc/%d/comm", id); 854 | if ((fd = open(buffer, O_RDONLY)) < 0) 855 | return unknown_comm(); 856 | 857 | ret = read(fd, buffer, sizeof(buffer)); 858 | (void)close(fd); 859 | if (ret <= 0) 860 | return unknown_comm(); 861 | 862 | buffer[sizeof(buffer)-1] = '\0'; 863 | for (ptr = buffer; *ptr; ptr++) { 864 | if (*ptr == '\n') { 865 | *ptr = '\0'; 866 | break; 867 | } 868 | } 869 | 870 | if (kernel_thread) { 871 | size_t len = strlen(buffer) + 3; 872 | comm = malloc(len); 873 | if (UNLIKELY(!comm)) 874 | return unknown_comm(); 875 | snprintf(comm, len, "[%s]", buffer); 876 | } else { 877 | comm = strdup(buffer); 878 | if (!comm) 879 | return unknown_comm(); 880 | } 881 | return comm; 882 | } 883 | 884 | /* 885 | * get_pid_cmdline 886 | * get process /proc/pid/cmdline 887 | */ 888 | static char *get_pid_cmdline(const pid_t id) 889 | { 890 | char buffer[4096]; 891 | char *ptr; 892 | int fd; 893 | ssize_t ret; 894 | 895 | (void)snprintf(buffer, sizeof(buffer), "/proc/%d/cmdline", id); 896 | 897 | if ((fd = open(buffer, O_RDONLY)) < 0) 898 | return NULL; 899 | 900 | ret = read(fd, buffer, sizeof(buffer)); 901 | (void)close(fd); 902 | if (ret < 0) 903 | return NULL; 904 | if (ret == 0) 905 | return strdup(""); 906 | 907 | buffer[sizeof(buffer)-1] = '\0'; 908 | 909 | /* 910 | * OPT_CMD_LONG option we get the full cmdline args 911 | */ 912 | if (g_opt_flags & OPT_CMD_LONG) { 913 | for (ptr = buffer; ptr < buffer + ret - 1; ptr++) { 914 | if (*ptr == '\0') 915 | *ptr = ' '; 916 | } 917 | *ptr = '\0'; 918 | } 919 | /* 920 | * OPT_CMD_SHORT option we discard anything after a space 921 | */ 922 | if (g_opt_flags & OPT_CMD_SHORT) { 923 | for (ptr = buffer; *ptr && (ptr < buffer + ret); ptr++) { 924 | if (*ptr == ' ') 925 | *ptr = '\0'; 926 | } 927 | } 928 | 929 | if (g_opt_flags & OPT_DIRNAME_STRIP) { 930 | char *base = basename(buffer); 931 | 932 | /* Should always be true */ 933 | if (base) 934 | return strdup(base); 935 | } 936 | 937 | return strdup(buffer); 938 | } 939 | 940 | static inline double duration_round(const double duration) 941 | { 942 | return floor((duration * 100.0) + 0.5) / 100.0; 943 | } 944 | 945 | /* 946 | * samples_dump() 947 | * dump out collected sample information 948 | */ 949 | static void samples_dump(const char *filename) 950 | { 951 | timer_info_t **sorted_timer_infos; 952 | size_t i, n; 953 | FILE *fp; 954 | uint64_t count = 0; 955 | double first_time = -1.0; 956 | timer_info_t *info; 957 | sample_delta_list_t *sdl; 958 | 959 | if (filename == NULL) 960 | return; 961 | 962 | if ((fp = fopen(filename, "w")) == NULL) { 963 | (void)fprintf(stderr, "Cannot write to file %s\n", filename); 964 | return; 965 | } 966 | 967 | sorted_timer_infos = calloc(g_timer_info_list_length, 968 | sizeof(*sorted_timer_infos)); 969 | if (UNLIKELY(!sorted_timer_infos)) 970 | err_abort("Cannot allocate buffer for sorting timer_infos\n"); 971 | 972 | /* Just want the timers with some non-zero total */ 973 | for (n = 0, info = g_timer_info_list; info; info = info->next) { 974 | if (info->total_events > 0) 975 | sorted_timer_infos[n++] = info; 976 | } 977 | 978 | qsort(sorted_timer_infos, n, 979 | sizeof(timer_info_t *), info_compare_total); 980 | 981 | fprintf(fp, "Time:,Task:"); 982 | for (i = 0; i < n; i++) { 983 | char *task; 984 | 985 | if (g_opt_flags & OPT_CMD) 986 | task = sorted_timer_infos[i]->cmdline; 987 | else 988 | task = sorted_timer_infos[i]->comm; 989 | 990 | (void)fprintf(fp, ",%s", task); 991 | } 992 | (void)fprintf(fp, "\n"); 993 | 994 | (void)fprintf(fp, ",Kernel Init Function:"); 995 | for (i = 0; i < n; i++) 996 | (void)fprintf(fp, ",%s", sorted_timer_infos[i]->func); 997 | (void)fprintf(fp, "\n"); 998 | 999 | (void)fprintf(fp, ",Total:"); 1000 | for (i = 0; i < n; i++) 1001 | (void)fprintf(fp, ",%" PRIu64, 1002 | sorted_timer_infos[i]->total_events); 1003 | (void)fprintf(fp, "\n"); 1004 | 1005 | for (sdl = g_sample_delta_list_head; sdl; sdl = sdl->next) { 1006 | time_t t = (time_t)sdl->whence; 1007 | struct tm *tm; 1008 | 1009 | count++; 1010 | tm = localtime(&t); 1011 | if (tm) { 1012 | (void)fprintf(fp, "%2.2d:%2.2d:%2.2d", 1013 | tm->tm_hour, tm->tm_min, tm->tm_sec); 1014 | } else { 1015 | (void)fprintf(fp, "--:--:--"); 1016 | } 1017 | 1018 | if (first_time < 0) 1019 | first_time = sdl->whence; 1020 | (void)fprintf(fp, ",%f", 1021 | duration_round(sdl->whence - first_time)); 1022 | 1023 | /* 1024 | * Scan in timer info order to be consistent for all sdl rows 1025 | */ 1026 | for (i = 0; i < n; i++) { 1027 | sample_delta_item_t *sdi = 1028 | sample_find(sdl, sorted_timer_infos[i]); 1029 | /* 1030 | * duration - if -C option is used then don't scale 1031 | * by the per sample duration time, instead give the 1032 | * raw sample count by scaling by 1.0 (i.e. no scaling) 1033 | */ 1034 | if (sdi) { 1035 | double duration = duration_round((g_opt_flags & OPT_SAMPLE_COUNT) ? 1.0 : sdi->time_delta); 1036 | (void)fprintf(fp, ",%f", 1037 | FLOAT_CMP(duration, 0.0) ? -99.99 : 1038 | (double)sdi->delta_events / duration); 1039 | } else 1040 | (void)fprintf(fp, ",%f", 0.0); 1041 | } 1042 | (void)fprintf(fp, "\n"); 1043 | } 1044 | 1045 | /* 1046 | * -S option - some statistics, min, max, average, std.dev. 1047 | */ 1048 | if (g_opt_flags & OPT_RESULT_STATS) { 1049 | (void)fprintf(fp, ",Min:"); 1050 | for (i = 0; i < n; i++) { 1051 | double min = DBL_MAX; 1052 | 1053 | for (sdl = g_sample_delta_list_head; sdl; sdl = sdl->next) { 1054 | sample_delta_item_t *sdi = 1055 | sample_find(sdl, sorted_timer_infos[i]); 1056 | 1057 | if (sdi) { 1058 | double duration = duration_round((g_opt_flags & OPT_SAMPLE_COUNT) ? 1.0 : sdi->time_delta); 1059 | double val = FLOAT_CMP(duration, 0.0) ? 1060 | 0.0 : sdi->delta_events / duration; 1061 | if (min > val) 1062 | min = val; 1063 | } 1064 | } 1065 | (void)fprintf(fp, ",%f", min); 1066 | } 1067 | (void)fprintf(fp, "\n"); 1068 | 1069 | (void)fprintf(fp, ",Max:"); 1070 | for (i = 0; i < n; i++) { 1071 | double max = DBL_MIN; 1072 | 1073 | for (sdl = g_sample_delta_list_head; sdl; sdl = sdl->next) { 1074 | sample_delta_item_t *sdi = 1075 | sample_find(sdl, sorted_timer_infos[i]); 1076 | 1077 | if (sdi) { 1078 | double duration = duration_round((g_opt_flags & OPT_SAMPLE_COUNT) ? 1.0 : sdi->time_delta); 1079 | double val = FLOAT_CMP(duration, 0.0) ? 1080 | 0.0 : sdi->delta_events / duration; 1081 | if (max < val) 1082 | max = val; 1083 | } 1084 | } 1085 | (void)fprintf(fp, ",%f", max); 1086 | } 1087 | (void)fprintf(fp, "\n"); 1088 | 1089 | (void)fprintf(fp, ",Average:"); 1090 | for (i = 0; i < n; i++) 1091 | (void)fprintf(fp, ",%f", count == 0 ? 0.0 : 1092 | (double)sorted_timer_infos[i]->total_events / count); 1093 | (void)fprintf(fp, "\n"); 1094 | 1095 | /* 1096 | * population standard deviation 1097 | */ 1098 | (void)fprintf(fp, ",Std.Dev.:"); 1099 | for (i = 0; i < n; i++) { 1100 | double average = (double) 1101 | sorted_timer_infos[i]->total_events / (double)count; 1102 | double sum = 0.0; 1103 | 1104 | for (sdl = g_sample_delta_list_head; sdl; sdl = sdl->next) { 1105 | sample_delta_item_t *sdi = 1106 | sample_find(sdl, sorted_timer_infos[i]); 1107 | if (sdi) { 1108 | double duration = duration_round((g_opt_flags & OPT_SAMPLE_COUNT) ? 1.0 : sdi->time_delta); 1109 | double diff = FLOAT_CMP(duration, 0.0) ? 0.0 : 1110 | ((double)sdi->delta_events - average) / duration; 1111 | diff = diff * diff; 1112 | sum += diff; 1113 | } 1114 | } 1115 | sum = sum / (double)count; 1116 | (void)fprintf(fp, ",%f", sqrt(sum)); 1117 | } 1118 | (void)fprintf(fp, "\n"); 1119 | } 1120 | free(sorted_timer_infos); 1121 | (void)fclose(fp); 1122 | } 1123 | 1124 | /* 1125 | * timer_info_find() 1126 | * try to find existing timer info in cache, and to the cache 1127 | * if it is new. 1128 | */ 1129 | static HOT timer_info_t *timer_info_find( 1130 | const timer_info_t *new_info, 1131 | const char *ident, 1132 | const double time_now, 1133 | const double duration) 1134 | { 1135 | timer_info_t *info; 1136 | const uint32_t h = hash_djb2a(ident); 1137 | 1138 | for (info = g_timer_info_hash[h]; info; info = info->hash_next) { 1139 | if (UNLIKELY(strcmp(ident, info->ident) == 0)) { 1140 | info->prev_used = info->last_used; 1141 | info->last_used = time_now; 1142 | return info; 1143 | } 1144 | } 1145 | info = calloc(1, sizeof(*info)); 1146 | if (UNLIKELY(!info)) 1147 | err_abort("Cannot allocate timer info\n"); 1148 | 1149 | info->pid = new_info->pid; 1150 | info->comm = new_info->comm ? strdup(new_info->comm) : unknown_comm(); 1151 | info->cmdline = strdup(new_info->cmdline); 1152 | info->func = strdup(new_info->func); 1153 | info->ident = strdup(ident); 1154 | info->kernel_thread = new_info->kernel_thread; 1155 | info->time_total = new_info->time_total; 1156 | info->timer = new_info->timer; 1157 | info->ref_count = 0; 1158 | info->prev_used = time_now - duration; /* Fake previous time */ 1159 | info->last_used = time_now; 1160 | info->total_events = 1; 1161 | info->delta_events = 1; 1162 | get_proc_cpu_ticks(info->pid, &info->cpu_ticks, &info->cpu_rt_prio, &info->cpu_nice); 1163 | info->cpu_ticks_time = time_now; 1164 | 1165 | if (UNLIKELY(info->comm == NULL || 1166 | info->cmdline == NULL || 1167 | info->func == NULL || 1168 | info->ident == NULL)) { 1169 | err_abort("Out of memory allocating a timer stat fields\n"); 1170 | } 1171 | 1172 | /* Does not exist in list, append it */ 1173 | info->next = g_timer_info_list; 1174 | g_timer_info_list = info; 1175 | g_timer_info_list_length++; 1176 | info->hash_next = g_timer_info_hash[h]; 1177 | g_timer_info_hash[h] = info; 1178 | 1179 | return info; 1180 | } 1181 | 1182 | /* 1183 | * timer_info_free() 1184 | * free up timer_info 1185 | */ 1186 | static void timer_info_free(void *data) 1187 | { 1188 | timer_info_t *info = (timer_info_t*)data; 1189 | 1190 | free(info->comm); 1191 | free(info->cmdline); 1192 | free(info->func); 1193 | free(info->ident); 1194 | free(info); 1195 | } 1196 | 1197 | /* 1198 | * timer_info_purge_old_from_timer_list() 1199 | * remove old timer info from the timer list 1200 | */ 1201 | static void timer_info_purge_old_from_timer_list( 1202 | timer_info_t **list, 1203 | const double time_now) 1204 | { 1205 | timer_info_t *prev = NULL, *info = *list; 1206 | 1207 | while (info) { 1208 | timer_info_t *next = info->next; 1209 | 1210 | /* 1211 | * Only remove from list once all timer 1212 | * stats no longer reference it 1213 | */ 1214 | if ((info->ref_count == 0) && 1215 | (info->last_used + TIMER_REAP_AGE < time_now)) { 1216 | if (prev == NULL) 1217 | *list = next; 1218 | else 1219 | prev->next = next; 1220 | g_timer_info_list_length--; 1221 | } else { 1222 | prev = info; 1223 | } 1224 | info = next; 1225 | } 1226 | } 1227 | 1228 | /* 1229 | * timer_info_purge_old_from_hash_list() 1230 | * remove old timer info from a hash list 1231 | */ 1232 | static void timer_info_purge_old_from_hash_list( 1233 | timer_info_t **list, 1234 | const double time_now) 1235 | { 1236 | timer_info_t *prev = NULL, *info = *list; 1237 | 1238 | while (info) { 1239 | timer_info_t *next = info->hash_next; 1240 | 1241 | /* 1242 | * Only remove and free once all timer stats no 1243 | * longer reference it 1244 | */ 1245 | if ((info->ref_count == 0) && 1246 | (info->last_used + TIMER_REAP_AGE < time_now)) { 1247 | if (prev == NULL) 1248 | *list = next; 1249 | else 1250 | prev->hash_next = next; 1251 | 1252 | timer_info_free(info); 1253 | } else { 1254 | prev = info; 1255 | } 1256 | info = next; 1257 | } 1258 | } 1259 | 1260 | /* 1261 | * timer_info_purge_old() 1262 | * clean out old timer infos 1263 | */ 1264 | static inline void timer_info_purge_old(const double time_now) 1265 | { 1266 | static uint16_t count = 0; 1267 | 1268 | count++; 1269 | if (count > TIMER_REAP_THRESHOLD) { 1270 | size_t i; 1271 | 1272 | count = 0; 1273 | timer_info_purge_old_from_timer_list(&g_timer_info_list, 1274 | time_now); 1275 | for (i = 0; i < TABLE_SIZE; i++) 1276 | timer_info_purge_old_from_hash_list(&g_timer_info_hash[i], time_now); 1277 | } 1278 | } 1279 | 1280 | /* 1281 | * timer_info_list_free() 1282 | * free up all unique timer infos 1283 | */ 1284 | static inline void timer_info_list_free(void) 1285 | { 1286 | timer_info_t *info = g_timer_info_list; 1287 | 1288 | /* Free list and timers on list */ 1289 | while (info) { 1290 | timer_info_t *next = info->next; 1291 | 1292 | timer_info_free(info); 1293 | info = next; 1294 | } 1295 | } 1296 | 1297 | /* 1298 | * make_hash_ident() 1299 | */ 1300 | static char *make_hash_ident(const timer_info_t *info) 1301 | { 1302 | static char ident[128]; 1303 | 1304 | if (g_opt_flags & OPT_TIMER_ID) { 1305 | (void)snprintf(ident, sizeof(ident), "%x%s%8.8s%" PRIx64, 1306 | info->pid, info->comm, info->func, info->timer); 1307 | } else { 1308 | (void)snprintf(ident, sizeof(ident), "%x%s%8.8s", 1309 | info->pid, info->comm, info->func); 1310 | } 1311 | return ident; 1312 | } 1313 | 1314 | /* 1315 | * timer_stat_free_list_free() 1316 | * free up the timer stat free list 1317 | */ 1318 | static void timer_stat_free_list_free(void) 1319 | { 1320 | timer_stat_t *ts = g_timer_stat_free_list; 1321 | 1322 | while (ts) { 1323 | timer_stat_t *next = ts->next; 1324 | 1325 | free(ts); 1326 | ts = next; 1327 | } 1328 | g_timer_stat_free_list = NULL; 1329 | } 1330 | 1331 | /* 1332 | * timer_stat_free_contents() 1333 | * Free timers from a hash table 1334 | */ 1335 | static void timer_stat_free_contents(timer_stat_t *timer_stats[]) 1336 | { 1337 | size_t i; 1338 | 1339 | for (i = 0; i < TABLE_SIZE; i++) { 1340 | timer_stat_t *ts = timer_stats[i]; 1341 | 1342 | while (ts) { 1343 | timer_stat_t *next = ts->next; 1344 | 1345 | /* Decrement info ref count */ 1346 | ts->info->ref_count--; 1347 | /* Add it onto the timer stat free list */ 1348 | ts->next = g_timer_stat_free_list; 1349 | g_timer_stat_free_list = ts; 1350 | 1351 | ts = next; 1352 | } 1353 | timer_stats[i] = NULL; 1354 | } 1355 | } 1356 | 1357 | /* 1358 | * timer_stat_add() 1359 | * add timer stats to a hash table if it is new, otherwise just 1360 | * accumulate the event count. 1361 | */ 1362 | static void timer_stat_add( 1363 | timer_stat_t *timer_stats[], /* timer stat hash table */ 1364 | timer_info_t *info, /* timer info to be added */ 1365 | const double time_now, /* time sample was taken */ 1366 | const double duration) /* duration of a sample */ 1367 | { 1368 | const char *ident = make_hash_ident(info); 1369 | const uint32_t h = hash_djb2a(ident); 1370 | timer_stat_t *ts, *ts_new; 1371 | 1372 | for (ts = timer_stats[h]; ts; ts = ts->next) { 1373 | if (UNLIKELY(strcmp(ts->info->ident, ident) == 0)) { 1374 | ts->info->total_events++; 1375 | ts->info->delta_events++; 1376 | sample_add(ts, time_now); 1377 | return; 1378 | } 1379 | } 1380 | /* Not found, it is new */ 1381 | if (g_timer_stat_free_list) { 1382 | /* Get new one from free list */ 1383 | ts_new = g_timer_stat_free_list; 1384 | g_timer_stat_free_list = g_timer_stat_free_list->next; 1385 | } else { 1386 | /* Get one from heap */ 1387 | ts_new = malloc(sizeof(*ts_new)); 1388 | if (UNLIKELY(!ts_new)) 1389 | err_abort("Out of memory allocating a timer stat\n"); 1390 | } 1391 | 1392 | ts_new->info = timer_info_find(info, ident, time_now, duration); 1393 | ts_new->info->ref_count++; 1394 | ts_new->next = timer_stats[h]; 1395 | ts_new->sorted_freq_next = NULL; 1396 | 1397 | timer_stats[h] = ts_new; 1398 | sample_add(ts_new, time_now); 1399 | } 1400 | 1401 | /* 1402 | * timer_stat_sort_freq_add() 1403 | * add a timer stat to a sorted list of timer stats 1404 | */ 1405 | static void timer_stat_sort_freq_add( 1406 | timer_stat_t **sorted, /* timer stat sorted list */ 1407 | timer_stat_t *new) /* timer stat to add */ 1408 | { 1409 | while (*sorted) { 1410 | if (UNLIKELY(g_opt_flags & OPT_CUMULATIVE)) { 1411 | if ((*sorted)->info->total_events < new->info->total_events) { 1412 | new->sorted_freq_next = *(sorted); 1413 | break; 1414 | } 1415 | } else { 1416 | if ((*sorted)->info->delta_events < new->info->delta_events) { 1417 | new->sorted_freq_next = *(sorted); 1418 | break; 1419 | } 1420 | } 1421 | sorted = &(*sorted)->sorted_freq_next; 1422 | } 1423 | *sorted = new; 1424 | } 1425 | 1426 | /* 1427 | * es_printf() 1428 | * eventstat printf - print to stdout or ncurses 1429 | * print depending on the mode 1430 | */ 1431 | static void es_printf(const char *fmt, ...) 1432 | { 1433 | va_list ap; 1434 | static int col = 0; 1435 | int n; 1436 | char buf[256], *eol; 1437 | 1438 | va_start(ap, fmt); 1439 | n = vsnprintf(buf, sizeof(buf), fmt, ap); 1440 | va_end(ap); 1441 | eol = strchr(buf, '\n'); 1442 | 1443 | if (n + col >= g_cols) { 1444 | n = g_cols - col; 1445 | buf[n > 0 ? n : 0] = '\0'; 1446 | } 1447 | 1448 | (void)(g_curses_init ? printw : printf)("%s", buf); 1449 | col = eol ? 0 : col + n; 1450 | } 1451 | 1452 | /* 1453 | * timer_stat_dump() 1454 | */ 1455 | static OPTIMIZE3 void timer_stat_dump( 1456 | const double duration, /* time between each sample */ 1457 | const double time_delta, /* how long been running sofar */ 1458 | const int32_t n_lines, /* number of lines to output */ 1459 | const double whence, /* nth sample */ 1460 | timer_stat_t *timer_stats[]) /* timer stats samples */ 1461 | { 1462 | size_t i; 1463 | timer_stat_t *sorted = NULL; 1464 | double now = gettime_to_double(); 1465 | 1466 | for (i = 0; i < TABLE_SIZE; i++) { 1467 | timer_stat_t *ts; 1468 | 1469 | for (ts = timer_stats[i]; ts; ts = ts->next) 1470 | timer_stat_sort_freq_add(&sorted, ts); 1471 | } 1472 | 1473 | if (!(g_opt_flags & OPT_QUIET)) { 1474 | uint64_t total = 0UL, kt_total = 0UL; 1475 | int32_t j = 0; 1476 | const int pid_size = pid_max_digits(); 1477 | int cols, sz, ta_size, fn_size = 0; 1478 | int min_width; 1479 | 1480 | eventstat_winsize(); 1481 | if (UNLIKELY(g_resized && g_curses_init)) { 1482 | (void)resizeterm(g_rows, g_cols); 1483 | (void)refresh(); 1484 | g_resized = false; 1485 | } 1486 | 1487 | /* Minimum width w/o task or func info */ 1488 | min_width = EVENTS_WIDTH + 1 + \ 1489 | 1 + \ 1490 | pid_size + 1 + 6 + 4 + 4; 1491 | if (!(g_opt_flags & OPT_BRIEF)) { 1492 | if (g_opt_flags & OPT_TIMER_ID) 1493 | min_width += TIMER_ID_WIDTH + 1; 1494 | fn_size = FUNC_WIDTH; 1495 | } 1496 | if (!(g_opt_flags & OPT_CMD_LONG) && g_cols > 80) 1497 | cols = 80; 1498 | else 1499 | cols = g_cols; 1500 | 1501 | sz = cols - min_width; 1502 | sz = (sz < 0) ? 0 : sz; 1503 | 1504 | if (fn_size) { 1505 | fn_size += (sz >> 1); 1506 | if (fn_size > FUNC_WIDTH_MAX) 1507 | fn_size = FUNC_WIDTH_MAX; 1508 | 1509 | min_width += fn_size; 1510 | sz = cols - min_width; 1511 | sz = (sz < 0) ? 0 : sz; 1512 | } 1513 | 1514 | ta_size = sz; 1515 | if (ta_size < TASK_WIDTH) 1516 | ta_size = TASK_WIDTH; 1517 | 1518 | if (g_opt_flags & OPT_BRIEF) { 1519 | es_printf("%*.*s %-*.*s %-*.*s", 1520 | EVENTS_WIDTH, EVENTS_WIDTH, 1521 | (g_opt_flags & OPT_CUMULATIVE) ? 1522 | "Events" : "Event/s", 1523 | pid_size, pid_size, "PID", 1524 | ta_size, ta_size, "Task"); 1525 | } else { 1526 | es_printf("%*.*s %-*.*s %5s %3.3s %3.3s %-*.*s", 1527 | EVENTS_WIDTH, EVENTS_WIDTH, 1528 | (g_opt_flags & OPT_CUMULATIVE) ? 1529 | "Events" : "Event/s", 1530 | pid_size, pid_size, "PID", 1531 | "%CPU", "PR", "NI", 1532 | ta_size, ta_size, "Task"); 1533 | 1534 | if (g_opt_flags & OPT_TIMER_ID) { 1535 | es_printf(" %16.16s", "Timer ID"); 1536 | } 1537 | es_printf("%-*.*s", fn_size, fn_size, 1538 | " Kernel Init Function"); 1539 | } 1540 | es_printf("\n"); 1541 | 1542 | while (sorted) { 1543 | uint64_t events; 1544 | 1545 | if (g_opt_flags & OPT_CUMULATIVE) 1546 | events = sorted->info->total_events; 1547 | else 1548 | events = sorted->info->delta_events; 1549 | 1550 | if (((n_lines == -1) || (j < n_lines)) && (events != 0)) { 1551 | char *task = (g_opt_flags & OPT_CMD) ? 1552 | sorted->info->cmdline : 1553 | sorted->info->comm; 1554 | j++; 1555 | if (g_opt_flags & OPT_CUMULATIVE) { 1556 | es_printf("%*" PRIu64 " ", 1557 | EVENTS_WIDTH, events); 1558 | } else { 1559 | es_printf("%*.2f ", 1560 | EVENTS_WIDTH, 1561 | (duration > 0.0) ? (double)events / duration : 0.0); 1562 | } 1563 | 1564 | if (g_opt_flags & OPT_BRIEF) { 1565 | es_printf("%*d %s\n", 1566 | pid_size, sorted->info->pid, 1567 | task); 1568 | } else { 1569 | double tick_time = now - sorted->info->cpu_ticks_time; 1570 | uint32_t tasks = get_proc_cpu_tasks(sorted->info->pid); 1571 | uint64_t cpu_ticks; 1572 | uint16_t cpu_rt_prio; 1573 | int16_t cpu_nice; 1574 | double cpu; 1575 | 1576 | get_proc_cpu_ticks(sorted->info->pid, &cpu_ticks, 1577 | &cpu_rt_prio, &cpu_nice); 1578 | 1579 | if (cpu_ticks && sorted->info->cpu_ticks) { 1580 | uint64_t ticks = cpu_ticks - sorted->info->cpu_ticks; 1581 | 1582 | cpu = (100.0 * (double)ticks) / (tick_time * (double)g_clock_tick_rate); 1583 | } else { 1584 | cpu = 0.0; 1585 | } 1586 | sorted->info->cpu_ticks = cpu_ticks; 1587 | sorted->info->cpu_ticks_time = now; 1588 | 1589 | es_printf("%*d %5.1f %3d %3d %-*.*s", 1590 | pid_size, sorted->info->pid, 1591 | cpu / (double)tasks, 1592 | sorted->info->cpu_rt_prio, 1593 | sorted->info->cpu_nice, 1594 | ta_size, ta_size, task); 1595 | if (g_opt_flags & OPT_TIMER_ID) { 1596 | es_printf(" %16" PRIx64, 1597 | sorted->info->timer); 1598 | } 1599 | es_printf(" %-*.*s\n", 1600 | fn_size - 1, fn_size - 1, 1601 | sorted->info->func); 1602 | } 1603 | } 1604 | total += sorted->info->delta_events; 1605 | if (sorted->info->kernel_thread) 1606 | kt_total += sorted->info->delta_events; 1607 | sorted->info->delta_events = 0; 1608 | sorted = sorted->sorted_freq_next; 1609 | } 1610 | eventstat_move(LINES - 1, 0); 1611 | es_printf("%" PRIu64 " Total events, %5.2f events/sec " 1612 | "(kernel: %5.2f, userspace: %5.2f)\n", 1613 | total, 1614 | (duration > 0.0) ? (double)total / duration : 0.0, 1615 | (duration > 0.0) ? (double)kt_total / duration : 0.0, 1616 | (duration > 0.0) ? (double)(total - kt_total) / duration : 0.0); 1617 | if ((g_opt_flags & OPT_SHOW_WHENCE) && !g_curses_init) { 1618 | time_t t = (time_t)whence; 1619 | char *timestr = ctime(&t); 1620 | char *pos; 1621 | 1622 | if (timestr) { 1623 | pos = strchr(timestr, '\n'); 1624 | } else { 1625 | pos = ""; 1626 | } 1627 | 1628 | if (pos) 1629 | *pos = '\0'; 1630 | es_printf("Timestamp: %s, Total Run Duration: " 1631 | "%.1f secs\n", timestr, time_delta); 1632 | } 1633 | 1634 | if (!g_sane_procs) 1635 | es_printf("Note: this was run inside a container, " 1636 | "kernel tasks were guessed.\n"); 1637 | es_printf("\n"); 1638 | } 1639 | } 1640 | 1641 | /* 1642 | * read_events() 1643 | * read in events data into a global read buffer. 1644 | * the buffer is auto-expanded where necessary and 1645 | * only free'd at exit time. This way we can parse 1646 | * the data a little faster. 1647 | */ 1648 | static char *read_events(const double time_end, char **get_events_buf) 1649 | { 1650 | int fd; 1651 | static size_t get_events_size; 1652 | size_t size; 1653 | 1654 | if (UNLIKELY(*get_events_buf == NULL)) { 1655 | *get_events_buf = calloc(EVENT_BUF_SIZE << 1, sizeof(char)); 1656 | if (UNLIKELY(!*get_events_buf)) { 1657 | err_abort("Cannot read %s, out of memory\n", 1658 | g_sys_tracing_pipe); 1659 | } 1660 | 1661 | get_events_size = (EVENT_BUF_SIZE << 1); 1662 | } 1663 | 1664 | if ((fd = open(g_sys_tracing_pipe, O_RDONLY)) < 0) 1665 | err_abort("Cannot open %s\n", g_sys_tracing_pipe); 1666 | 1667 | size = 0; 1668 | while (!g_stop_eventstat) { 1669 | ssize_t ret; 1670 | int rc; 1671 | static char buffer[EVENT_BUF_SIZE]; 1672 | const double duration = time_end - gettime_to_double(); 1673 | struct timeval tv; 1674 | fd_set rfds; 1675 | 1676 | if (UNLIKELY(duration < 0.0)) 1677 | break; 1678 | 1679 | tv = double_to_timeval(duration); 1680 | FD_ZERO(&rfds); 1681 | FD_SET(fd, &rfds); 1682 | 1683 | errno = 0; 1684 | rc = select(fd + 1, &rfds, NULL, NULL, &tv); 1685 | if (UNLIKELY(rc <= 0)) { 1686 | if (errno == EINTR) 1687 | continue; 1688 | break; 1689 | } 1690 | if (!FD_ISSET(fd, &rfds)) 1691 | continue; 1692 | ret = read(fd, buffer, sizeof(buffer)); 1693 | if (ret == 0) 1694 | continue; 1695 | if (UNLIKELY(ret < 0)) { 1696 | if (!g_stop_eventstat && 1697 | ((errno == EINTR) || 1698 | (errno != EAGAIN))) { 1699 | continue; 1700 | } 1701 | break; 1702 | } 1703 | /* Do we need to expand the global buffer? */ 1704 | if (size + ret >= get_events_size) { 1705 | char *tmpptr; 1706 | 1707 | get_events_size += (EVENT_BUF_SIZE << 1); 1708 | tmpptr = realloc(*get_events_buf, get_events_size + 1); 1709 | if (UNLIKELY(!tmpptr)) { 1710 | (void)close(fd); 1711 | err_abort("Cannot read %s, out of memory\n", 1712 | g_sys_tracing_pipe); 1713 | } 1714 | *get_events_buf = tmpptr; 1715 | } 1716 | (void)memcpy((*get_events_buf) + size, buffer, ret); 1717 | size += ret; 1718 | *(*get_events_buf + size) = '\0'; 1719 | } 1720 | (void)close(fd); 1721 | 1722 | return *get_events_buf; 1723 | } 1724 | 1725 | /* 1726 | * get_events() 1727 | * parse /sys/kernel/debug/tracing/trace_pipe and populate 1728 | * a timer stat hash table with unique events 1729 | */ 1730 | static void get_events( 1731 | timer_stat_t *timer_stats[], 1732 | char **get_events_buf, 1733 | const double time_now, 1734 | const double duration) 1735 | { 1736 | const size_t app_name_len = strlen(g_app_name); 1737 | const double time_end = time_now + duration - 0.05; 1738 | char *tmpptr; 1739 | timer_info_t *pinfo; 1740 | 1741 | tmpptr = read_events(time_end, get_events_buf); 1742 | if (!tmpptr) 1743 | return; 1744 | 1745 | if (g_opt_flags & OPT_CUMULATIVE) { 1746 | for (pinfo = g_timer_info_list; pinfo; pinfo = pinfo->next) 1747 | pinfo->just_added = false; 1748 | } 1749 | 1750 | while (*tmpptr) { 1751 | char *ptr, *eol = tmpptr; 1752 | char func[64], task[64]; 1753 | char *cmdline, *comm; 1754 | int mask; 1755 | timer_info_t info; 1756 | 1757 | /* Find the end of a line */ 1758 | while (*eol) { 1759 | if (UNLIKELY(*eol == '\n')) { 1760 | *eol = '\0'; 1761 | eol++; 1762 | break; 1763 | } 1764 | eol++; 1765 | } 1766 | if (strstr(tmpptr, "hrtimer_start")) { 1767 | (void)memset(task, 0, sizeof(task)); 1768 | (void)memset(&info, 0, sizeof(info)); 1769 | (void)memset(func, 0, sizeof(func)); 1770 | 1771 | /* 1772 | * Parse something like the following: 1773 | * gnome-shell-3515 [003] d.h. 101499.108349: hrtimer_start: hrtimer=ffff99979e2d4600 function=tick_sched_timer expires=101497144000000 softexpires=101497144000000 1774 | */ 1775 | if (sscanf(tmpptr, "%63s %*s %*s %*f: hrtimer_start: hrtimer=%" PRIx64 " function=%63s", task, &info.timer, func) != 3) 1776 | goto next; 1777 | } else { 1778 | goto next; 1779 | } 1780 | 1781 | /* 1782 | * task name in form: gnome-shell-3515, scan to end of 1783 | * string, then scan back to find start of PID 1784 | */ 1785 | ptr = task; 1786 | while (*ptr) 1787 | ptr++; 1788 | ptr--; 1789 | while (ptr >= task && (*ptr >= '0' && *ptr <= '9')) 1790 | ptr--; 1791 | 1792 | *ptr = '\0'; 1793 | ptr++; 1794 | 1795 | if (sscanf(ptr, "%10d\n", &info.pid) != 1) 1796 | goto next; 1797 | if (UNLIKELY(info.pid == 0)) 1798 | goto next; 1799 | 1800 | /* Processes without a command line are kernel threads */ 1801 | cmdline = get_pid_cmdline(info.pid); 1802 | info.kernel_thread = pid_a_kernel_thread(task, info.pid); 1803 | comm = get_pid_comm(info.pid, info.kernel_thread); 1804 | if (UNLIKELY(!comm)) 1805 | goto free_next; 1806 | 1807 | /* Swapper is special, like all corner cases */ 1808 | if (UNLIKELY(strncmp(task, "swapper", 6) == 0)) 1809 | info.kernel_thread = true; 1810 | 1811 | mask = info.kernel_thread ? OPT_KERNEL : OPT_USER; 1812 | if (!(g_opt_flags & mask)) 1813 | goto free_next; 1814 | 1815 | if (strncmp(task, g_app_name, app_name_len)) { 1816 | info.cmdline = cmdline ? cmdline : comm; 1817 | info.comm = comm; 1818 | info.func = func; 1819 | info.time_total = 0.0; 1820 | info.total_events = 1; 1821 | info.ident = make_hash_ident(&info); 1822 | info.just_added = true; 1823 | timer_stat_add(timer_stats, &info, time_now, duration); 1824 | } 1825 | free_next: 1826 | free(cmdline); 1827 | free(comm); 1828 | next: 1829 | tmpptr = eol; 1830 | } 1831 | 1832 | if (g_opt_flags & OPT_CUMULATIVE) { 1833 | for (pinfo = g_timer_info_list; pinfo; pinfo = pinfo->next) { 1834 | if (!pinfo->just_added) 1835 | timer_stat_add(timer_stats, pinfo, time_now, duration); 1836 | } 1837 | } 1838 | } 1839 | 1840 | /* 1841 | * show_usage() 1842 | * show how to use 1843 | */ 1844 | static void show_usage(void) 1845 | { 1846 | (void)printf("%s, version %s\n\n", g_app_name, VERSION); 1847 | (void)printf("Usage: %s [options] [duration] [count]\n", g_app_name); 1848 | (void)printf("Options are:\n" 1849 | " -c\t\treport cumulative events rather than events per second.\n" 1850 | " -C\t\treport event count rather than event per second in CSV output.\n" 1851 | " -d\t\tremove pathname from long process name in CSV output.\n" 1852 | " -h\t\tprint this help.\n" 1853 | " -l\t\tuse long cmdline text from /proc/pid/cmdline for process name.\n" 1854 | " -n events\tspecifies number of events to display.\n" 1855 | " -q\t\trun quietly, useful with option -r.\n" 1856 | " -r filename\tspecifies a comma separated values (CSV) output file\n" 1857 | "\t\tto dump samples into.\n" 1858 | " -s\t\tuse short process name from /proc/pid/cmdline for process name.\n" 1859 | " -S\t\tcalculate min, max, average and standard deviation in CSV\n" 1860 | "\t\toutput.\n" 1861 | " -t threshold\tsamples less than the specified threshold are ignored.\n" 1862 | " -T\t\tenable \'top\' mode rather than a scrolling output.\n" 1863 | " -w\t\tadd time stamp (when events occurred) to output.\n"); 1864 | } 1865 | 1866 | /* 1867 | * handle_sigwinch() 1868 | * flag window resize on SIGWINCH 1869 | */ 1870 | static void handle_sigwinch(int sig) 1871 | { 1872 | (void)sig; 1873 | 1874 | eventstat_winsize(); 1875 | 1876 | g_resized = true; 1877 | } 1878 | 1879 | int main(int argc, char **argv) 1880 | { 1881 | timer_stat_t **timer_stats; 1882 | char *get_events_buf = NULL; 1883 | double duration_secs = 1.0, time_start, time_now; 1884 | int64_t count = 1, t = 1; 1885 | int32_t n_lines = -1; 1886 | bool forever = true; 1887 | bool redo = false; 1888 | struct sigaction new_action; 1889 | size_t i; 1890 | 1891 | for (;;) { 1892 | int c = getopt(argc, argv, "bcCdksSlhin:qr:t:Tuw"); 1893 | if (c == -1) 1894 | break; 1895 | switch (c) { 1896 | case 'b': 1897 | g_opt_flags |= OPT_BRIEF; 1898 | break; 1899 | case 'c': 1900 | g_opt_flags |= OPT_CUMULATIVE; 1901 | break; 1902 | case 'C': 1903 | g_opt_flags |= OPT_SAMPLE_COUNT; 1904 | break; 1905 | case 'd': 1906 | g_opt_flags |= OPT_DIRNAME_STRIP; 1907 | break; 1908 | case 'h': 1909 | show_usage(); 1910 | eventstat_exit(EXIT_SUCCESS); 1911 | case 'i': 1912 | g_opt_flags |= OPT_TIMER_ID; 1913 | break; 1914 | case 'n': 1915 | errno = 0; 1916 | n_lines = (int32_t)strtol(optarg, NULL, 10); 1917 | if (errno) 1918 | err_abort("Invalid value for number " 1919 | "of events to display\n"); 1920 | if (n_lines < 1) 1921 | err_abort("-n option must be greater than 0\n"); 1922 | break; 1923 | case 'S': 1924 | g_opt_flags |= OPT_RESULT_STATS; 1925 | break; 1926 | case 't': 1927 | g_opt_threshold = strtoull(optarg, NULL, 10); 1928 | if (g_opt_threshold < 1) 1929 | err_abort("-t threshold must be 1 or more.\n"); 1930 | break; 1931 | case 'T': 1932 | g_opt_flags |= OPT_TOP; 1933 | break; 1934 | case 'q': 1935 | g_opt_flags |= OPT_QUIET; 1936 | break; 1937 | case 'r': 1938 | g_csv_results = optarg; 1939 | break; 1940 | case 's': 1941 | g_opt_flags |= OPT_CMD_SHORT; 1942 | break; 1943 | case 'l': 1944 | g_opt_flags |= OPT_CMD_LONG; 1945 | break; 1946 | case 'k': 1947 | g_opt_flags |= OPT_KERNEL; 1948 | break; 1949 | case 'u': 1950 | g_opt_flags |= OPT_USER; 1951 | break; 1952 | case 'w': 1953 | g_opt_flags |= OPT_SHOW_WHENCE; 1954 | break; 1955 | default: 1956 | show_usage(); 1957 | eventstat_exit(EXIT_FAILURE); 1958 | } 1959 | } 1960 | 1961 | if (!(g_opt_flags & (OPT_KERNEL | OPT_USER))) 1962 | g_opt_flags |= (OPT_KERNEL | OPT_USER); 1963 | 1964 | if (optind < argc) { 1965 | duration_secs = atof(argv[optind++]); 1966 | if (duration_secs < 0.5) 1967 | err_abort("Duration must 0.5 or more.\n"); 1968 | } 1969 | 1970 | if (optind < argc) { 1971 | forever = false; 1972 | errno = 0; 1973 | count = (int64_t)strtoll(argv[optind++], NULL, 10); 1974 | if (errno) 1975 | err_abort("Invalid count value\n"); 1976 | if (count < 1) 1977 | err_abort("Count must be > 0\n"); 1978 | } 1979 | 1980 | g_opt_threshold *= duration_secs; 1981 | 1982 | if (geteuid() != 0) 1983 | err_abort("%s requires root privileges to gather " 1984 | "trace event data\n", g_app_name); 1985 | 1986 | g_sane_procs = sane_proc_pid_info(); 1987 | if (!g_sane_procs) 1988 | g_opt_flags &= ~(OPT_CMD_SHORT | OPT_CMD_LONG); 1989 | 1990 | (void)memset(&new_action, 0, sizeof(new_action)); 1991 | for (i = 0; i < SIZEOF_ARRAY(g_signals); i++) { 1992 | new_action.sa_handler = handle_sig; 1993 | (void)sigemptyset(&new_action.sa_mask); 1994 | new_action.sa_flags = 0; 1995 | 1996 | if (sigaction(g_signals[i], &new_action, NULL) < 0) 1997 | err_abort("sigaction failed: errno=%d (%s)\n", 1998 | errno, strerror(errno)); 1999 | } 2000 | 2001 | timer_stats = calloc(TABLE_SIZE, sizeof(*timer_stats)); 2002 | if (UNLIKELY(!timer_stats)) 2003 | err_abort("Cannot allocate timer stats table\n"); 2004 | 2005 | /* Should really catch signals and set back to zero before we die */ 2006 | set_tracing_enable("1\n", true); 2007 | set_tracing_event(); 2008 | 2009 | g_clock_tick_rate = (uint64_t)sysconf(_SC_CLK_TCK); 2010 | time_now = time_start = gettime_to_double(); 2011 | 2012 | if (g_opt_flags & OPT_TOP) { 2013 | struct sigaction sa; 2014 | 2015 | (void)memset(&sa, 0, sizeof(sa)); 2016 | sa.sa_handler = handle_sigwinch; 2017 | if (sigaction(SIGWINCH, &sa, NULL) < 0) 2018 | err_abort("sigaction failed: errno=%d (%s)\n", 2019 | errno, strerror(errno)); 2020 | (void)initscr(); 2021 | (void)cbreak(); 2022 | (void)noecho(); 2023 | (void)nodelay(stdscr, 1); 2024 | (void)keypad(stdscr, 1); 2025 | (void)curs_set(0); 2026 | g_curses_init = true; 2027 | } 2028 | 2029 | while (!g_stop_eventstat && (forever || count--)) { 2030 | double secs, duration, time_delta; 2031 | 2032 | /* Timeout to wait for in the future for this sample */ 2033 | secs = time_start + ((double)t * duration_secs) - time_now; 2034 | /* Play catch-up, probably been asleep */ 2035 | if (secs < 0.0) { 2036 | t = ceil((time_now - time_start) / duration_secs); 2037 | secs = time_start + 2038 | ((double)t * duration_secs) - time_now; 2039 | /* Really, it's impossible, but just in case.. */ 2040 | if (secs < 0.0) 2041 | secs = 0.0; 2042 | } else { 2043 | if (!redo) 2044 | t++; 2045 | } 2046 | 2047 | redo = false; 2048 | 2049 | if (g_curses_init) { 2050 | fd_set rfds; 2051 | int ch, ret; 2052 | struct timeval tv; 2053 | 2054 | (void)memset(&tv, 0, sizeof(tv)); 2055 | 2056 | FD_ZERO(&rfds); 2057 | FD_SET(fileno(stdin), &rfds); 2058 | 2059 | ret = select(fileno(stdin) + 1, &rfds, NULL, NULL, &tv); 2060 | ch = getch(); 2061 | if ((ch == 27) || (ch == 'q')) 2062 | break; 2063 | if (ret > 0) 2064 | redo = true; 2065 | if (ret < 0) { 2066 | if (errno != EINTR) { 2067 | eventstat_endwin(); 2068 | 2069 | (void)fprintf(stderr, "select() failed: " 2070 | "errno=%d (%s)\n", 2071 | errno, strerror(errno)); 2072 | goto abort; 2073 | } 2074 | redo = true; 2075 | } 2076 | } 2077 | 2078 | get_events(timer_stats, &get_events_buf, time_now, secs); 2079 | 2080 | duration = gettime_to_double() - time_now; 2081 | duration = floor((duration * 1000.0) + 0.5) / 1000.0; 2082 | time_now = gettime_to_double(); 2083 | time_delta = time_now - time_start; 2084 | 2085 | eventstat_clear(); 2086 | timer_stat_dump(duration, time_delta, n_lines, 2087 | time_now, timer_stats); 2088 | eventstat_refresh(); 2089 | timer_stat_free_contents(timer_stats); 2090 | timer_info_purge_old(time_now); 2091 | } 2092 | eventstat_endwin(); 2093 | abort: 2094 | samples_dump(g_csv_results); 2095 | 2096 | timer_stat_free_contents(timer_stats); 2097 | free(timer_stats); 2098 | samples_free(); 2099 | timer_info_list_free(); 2100 | timer_stat_free_list_free(); 2101 | free(get_events_buf); 2102 | 2103 | eventstat_exit(EXIT_SUCCESS); 2104 | } 2105 | --------------------------------------------------------------------------------