├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── COPYING ├── LICENSE ├── README.md ├── SECURITY.md ├── build.sh ├── clean.sh ├── deps └── build_deps.sh └── src ├── bpf ├── insn │ ├── build.sh │ ├── insn.bpf.c │ └── insn.h ├── tma │ ├── build.sh │ ├── map_utils.h │ ├── perf_slots.bpf.c │ └── perf_slots.h └── vmlinux_505.h ├── build.sh ├── kerninfo.h ├── process_info.h ├── processwatch.c ├── processwatch.h ├── results.h ├── setup_bpf.h └── ui ├── csv.h ├── histogram.h ├── interactive.h └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | src/processwatch 2 | src/processwatch.o 3 | src/tinyexpr.o 4 | processwatch 5 | 6 | .yed* 7 | 8 | deps/install 9 | deps/build_logs 10 | 11 | src/bpf/tma/maps/ 12 | src/bpf/tma/perf_slots.bpf.o 13 | src/bpf/tma/perf_slots.skel.h 14 | src/bpf/tma/vmlinux.h 15 | 16 | src/bpf/insn/insn.bpf.o 17 | src/bpf/insn/insn.skel.h 18 | src/bpf/insn/vmlinux.h 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/bpftool"] 2 | path = deps/bpftool 3 | url = https://github.com/libbpf/bpftool.git 4 | [submodule "deps/pmu-tools"] 5 | path = deps/pmu-tools 6 | url = https://github.com/andikleen/pmu-tools.git 7 | [submodule "deps/tinyexpr"] 8 | path = deps/tinyexpr 9 | url = https://github.com/codeplea/tinyexpr.git 10 | [submodule "deps/capstone"] 11 | path = deps/capstone 12 | url = https://github.com/capstone-engine/capstone.git 13 | [submodule "deps/zydis"] 14 | path = deps/zydis 15 | url = https://github.com/zyantific/zydis.git 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | CommunityCodeOfConduct AT intel DOT com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ### License 4 | 5 | is licensed under the terms in [LICENSE]. By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. 6 | 7 | ### Sign your work 8 | 9 | Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify 10 | the below (from [developercertificate.org](http://developercertificate.org/)): 11 | 12 | ``` 13 | Developer Certificate of Origin 14 | Version 1.1 15 | 16 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 17 | 660 York Street, Suite 102, 18 | San Francisco, CA 94110 USA 19 | 20 | Everyone is permitted to copy and distribute verbatim copies of this 21 | license document, but changing it is not allowed. 22 | 23 | Developer's Certificate of Origin 1.1 24 | 25 | By making a contribution to this project, I certify that: 26 | 27 | (a) The contribution was created in whole or in part by me and I 28 | have the right to submit it under the open source license 29 | indicated in the file; or 30 | 31 | (b) The contribution is based upon previous work that, to the best 32 | of my knowledge, is covered under an appropriate open source 33 | license and I have the right under that license to submit that 34 | work with modifications, whether created in whole or in part 35 | by me, under the same open source license (unless I am 36 | permitted to submit under a different license), as indicated 37 | in the file; or 38 | 39 | (c) The contribution was provided directly to me by some other 40 | person who certified (a), (b) or (c) and I have not modified 41 | it. 42 | 43 | (d) I understand and agree that this project and the contribution 44 | are public and that a record of the contribution (including all 45 | personal information I submit with it, including my sign-off) is 46 | maintained indefinitely and may be redistributed consistent with 47 | this project or the open source license(s) involved. 48 | ``` 49 | 50 | Then you just add a line to every git commit message: 51 | 52 | Signed-off-by: Joe Smith 53 | 54 | Use your real name (sorry, no pseudonyms or anonymous contributions.) 55 | 56 | If you set your `user.name` and `user.email` git configs, you can sign your 57 | commit automatically with `git commit -s`. 58 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Process Watch is provided under: 2 | 3 | SPDX-License-Identifier: GPL-2.0 4 | 5 | Being under the terms of the GNU General Public License version 2 only, 6 | according with: 7 | 8 | LICENSE 9 | 10 | All contributions to Process Watch are subject to this COPYING file. 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Process Watch 2 | ============= 3 | 4 | This is an example of Process Watch running on an Intel® NUC running the [LULESH](https://github.com/LLNL/LULESH) workload. Note the presence of SSE, AVX, and AVX2 instructions. 5 | ![image](https://github.com/user-attachments/assets/647b199d-a4f2-4d34-b5e7-3c0f41249bd3) 6 | 7 | Overview 8 | -------- 9 | 10 | Process Watch displays per-process instruction mix in real-time, organizing these 11 | instructions into categories. It offers a simple, tabular output in addition to 12 | CSV. 13 | 14 | Examples 15 | -------- 16 | 17 | To display the default categories in a table every 2 seconds: 18 | ``` 19 | $ ./processwatch 20 | ``` 21 | 22 | To show mnemonics instead of instruction categories: 23 | ``` 24 | $ ./processwatch -m 25 | ``` 26 | 27 | To list available categories/mnemonics/extensions, add `-l`: 28 | ``` 29 | $ ./processwatch -l 30 | ``` 31 | 32 | To show specific categories/mnemonics/extensions, specify them with multiple `-f` arguments: 33 | ``` 34 | $ ./processwatch -f PUSH -f POP 35 | ``` 36 | 37 | To show *all* categories/mnemonics/extensions, to be parsed by some script: 38 | ``` 39 | $ ./processwatch -a 40 | ``` 41 | 42 | To show all available switches: 43 | ``` 44 | $ ./processwatch -h 45 | ``` 46 | 47 | Runtime Requirements 48 | -------------------- 49 | 50 | For the binary releases, the only requirement is that you have BTF metadata in 51 | your kernel. If the file `/sys/kernel/btf/vmlinux` exists on your system, then 52 | you have it. If not, then your kernel does not have BTF metadata. 53 | 54 | If your kernel does not have this feature, you can enable it by installing or 55 | compiling a kernel with the `CONFIG_DEBUG_INFO_BTF` configuration option 56 | enabled. Keep in mind that if you're compiling the kernel yourself, you need to 57 | install the `pahole` commandline utility to compile a kernel with this option 58 | enabled. 59 | 60 | There are two ways to compile Process Watch: 61 | 1. `./build.sh`. This executable should work in kernel version `5.8.0` and newer. 62 | 2. `./build.sh --legacy`. This executable is for kernels before `5.8.0`. 63 | 64 | Building 65 | -------- 66 | 67 | First, clone the repository: 68 | ``` 69 | git clone --recursive https://github.com/intel/processwatch.git 70 | ``` 71 | 72 | If you've already cloned without `--recursive`, go into the repository directory and issue: 73 | ``` 74 | git submodule init 75 | git submodule update --init --recursive 76 | ``` 77 | 78 | If you want to compile the tool, there are a few common packages that you'll need to install 79 | on your system: 80 | 1. CMake 81 | 2. Clang 82 | 3. LLVM (e.g. `llvm-strip`) 83 | 5. NCURSES 84 | 6. POSIX Threads 85 | 7. `libelf` 86 | 87 | You can install these on Ubuntu 20.04, 21.10, or 22.04 by issuing the following: 88 | ``` 89 | sudo apt-get update 90 | sudo apt-get install libelf-dev cmake clang llvm llvm-dev 91 | ``` 92 | 93 | Ubuntu 18.04 requires a bit more work; the default LLVM version is too old: 94 | ``` 95 | sudo apt-get update 96 | sudo apt-get install libelf-dev cmake clang-10 llvm-10 llvm-10-dev 97 | ``` 98 | Then edit `build.sh` and append `-10` to the values of `CLANG`, `CLANGXX`, and `LLVM_STRIP`. 99 | 100 | On CentOS 8.4, CentOS 8 Stream, and CentOS 9 Stream: 101 | ``` 102 | sudo yum update 103 | sudo yum install cmake bpftool clang llvm-toolset ncurses-devel 104 | ``` 105 | 106 | On Amazon Linux 2, you can do: 107 | ``` 108 | sudo yum update 109 | sudo yum install bpftool zlib-devel zlib-static \ 110 | elfutils-libelf-devel clang cmake3 llvm \ 111 | glibc-static 112 | ``` 113 | 114 | Please keep in mind that if you're running a custom kernel, you'll need to compile 115 | and install `bpftool` in the `tools/bpf/bpftool` directory of your kernel's source tree. 116 | 117 | Now, check your kernel version: 118 | ``` 119 | uname -a 120 | ``` 121 | 122 | If you have a kernel older than 5.8.0, compile with: 123 | ``` 124 | ./build.sh --legacy 125 | ``` 126 | 127 | If your kernel is 5.8.0 or newer, do: 128 | ``` 129 | ./build.sh 130 | ``` 131 | 132 | Interactive Mode 133 | ---------------- 134 | 135 | This is the default mode, so no command-line arguments are necessary to enable it. 136 | Kill it with CTRL-C or send it SIGTERM. You can also do `-n ` to have it stop 137 | after a number of intervals. 138 | 139 | CSV Mode 140 | ---------- 141 | 142 | To enable this mode, pass `--csv` or `-c` on the command-line. Output will go to 143 | `stdout`. Send `SIGTERM` to kill it. 144 | 145 | Known Build Issues 146 | ------------------ 147 | 148 | # Ubuntu `bpftool` Issue 149 | 150 | _This happens particularly on Ubuntu 20.04, which includes a version of `bpftool` that 151 | is too old to read the kernel's BTF information._ 152 | 153 | Sometimes when you install `linux-tools-common` or `linux-tools-generic`, you get 154 | a version of `bpftool` that is not associated with the kernel that you're currently running. 155 | This happens when there is a kernel package update, but you're still running an older kernel. 156 | Similarly, on Ubuntu 20.04, you may get a `bpftool` version that fails to read the BTF 157 | information (which is stored in `/sys/kernel/btf/vmlinux`). 158 | 159 | Resolving this issue is simple, but depends on your situation. 160 | 1. If you're on Ubuntu 20.04, install `linux-tools-*` for a _newer_ kernel. I chose the 161 | `linux-tools-5.8.0-63-generic` package. 162 | 2. If you're running a newer Ubuntu version, you might just want to install `linux-tools-$(uname -r)` 163 | to install the Linux tools for your currently-running kernel. 164 | 3. A longer-term solution is to simply upgrade your system (`sudo apt-get update && sudo apt-get upgrade`), 165 | install the `linux-tools-generic` package, and then reboot the machine into the updated kernel. 166 | 167 | If you need to tell the build system to use a specific version of `bpftool`, simply edit the `BPFTOOL` variable 168 | in `build.sh`. For example, if you're on Ubuntu 20.04 and have installed ``linux-tools-5.8.0-63-generic`, 169 | you might want to edit that variable to become something like: 170 | ``` 171 | export BPFTOOL="/usr/lib/linux-tools/5.8.0-63-generic/bpftool" 172 | ``` 173 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). 6 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | ################################################################### 6 | # BUILD.SH 7 | # This is the main build script for Process Watch. It first uses 8 | # `build_deps.sh` to build the dependencies in the `deps` directory, 9 | # then builds the BPF and userspace programs. 10 | ################################################################### 11 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 12 | ARCH=$(uname -m) 13 | export ARCH 14 | 15 | # USER-CHANGEABLE OPTIONS 16 | export CLANG="${CLANG:-clang}" 17 | export CLANGXX="${CLANGXX:-clang++}" 18 | export LLVM_STRIP="${LLVM_STRIP:-llvm-strip}" 19 | export LLVM_CONFIG="${LLVM_CONFIG:-llvm-config}" 20 | export PW_CC="${CLANG}" 21 | export PW_CXX="${CLANGXX}" 22 | export BPFTOOL="${BPFTOOL:-bpftool}" 23 | export CMAKE="${CMAKE:-cmake}" 24 | 25 | if ! command -v ${CMAKE} &> /dev/null; then 26 | export CMAKE="cmake3" 27 | if ! command -v ${CMAKE} &> /dev/null; then 28 | echo "Could not find CMake. I tried 'cmake' and 'cmake3'." 29 | exit 1 30 | fi 31 | fi 32 | 33 | die () { 34 | echo "ERROR: $*. Aborting." >&2 35 | exit 1 36 | } 37 | 38 | # Command-line arguments 39 | export DEBUG=false 40 | export LEGACY=false 41 | export BUILD_DEPS=true 42 | usage() { echo "Usage: $0 [-l] [-t] [-b] [-d]" 1>&2; exit 1; } 43 | while getopts ":ltbd" arg; do 44 | case $arg in 45 | l) [ "$ARCH" == "aarch64" ] && die "Legacy unsupported on Arm" 46 | LEGACY=true 47 | ;; 48 | b) 49 | BUILD_DEPS=false 50 | ;; 51 | d) 52 | DEBUG=true 53 | ;; 54 | esac 55 | done 56 | 57 | # These are used to compile the dependencies 58 | DEPS_DIR="${DIR}/deps" 59 | 60 | # We export these because they're used by src/build.sh 61 | export PREFIX="${DEPS_DIR}/install" 62 | 63 | export PW_LDFLAGS="-Wl,-z,now" 64 | export PW_CFLAGS="-O2 -Wall -D_FORTIFY_SOURCE=2" 65 | export CFLAGS="-O2 -Wall" 66 | export BPF_CFLAGS="-O2 -Wall -g" 67 | 68 | if [ "${DEBUG}" = true ]; then 69 | export PW_CFLAGS="${PW_CFLAGS} -g -fsanitize=address -static-libsan" 70 | export PW_LDFLAGS="${PW_LDFLAGS} -g -fsanitize=address -static-libsan" 71 | export CFLAGS="${CFLAGS} -g" 72 | fi 73 | 74 | if [ "${LEGACY}" = true ]; then 75 | export BPF_CFLAGS="${BPF_CFLAGS} -DINSNPROF_LEGACY_PERF_BUFFER" 76 | export PW_CFLAGS="${PW_CFLAGS} -DINSNPROF_LEGACY_PERF_BUFFER" 77 | fi 78 | 79 | if [ "${ARCH}" == "x86_64" ]; then 80 | export BPF_CFLAGS="${BPF_CFLAGS} -D__TARGET_ARCH_x86" 81 | export PW_CFLAGS="${PW_CFLAGS}" 82 | else 83 | export BPF_CFLAGS="${BPF_CFLAGS} -D__TARGET_ARCH_arm" 84 | export PW_CFLAGS="${PW_CFLAGS}" 85 | fi 86 | 87 | # Prepare the dependency-building logs 88 | if [ "${BUILD_DEPS}" = true ]; then 89 | BUILD_LOGS=${DEPS_DIR}/build_logs 90 | rm -rf ${BUILD_LOGS} || true 91 | mkdir -p ${BUILD_LOGS} || true 92 | fi 93 | 94 | cd ${DIR} 95 | git submodule update --init --recursive 96 | 97 | ################################################################### 98 | # deps 99 | ################################################################### 100 | if [ "${BUILD_DEPS}" = true ]; then 101 | echo "Compiling dependencies..." 102 | source ${DIR}/deps/build_deps.sh 103 | fi 104 | 105 | export PATH="${PREFIX}/bin:${PATH}" 106 | 107 | # libbpf 108 | export PW_LDFLAGS="${PW_LDFLAGS} ${PREFIX}/lib/libbpf.a" 109 | 110 | if [ "${ARCH}" == "x86_64" ]; then 111 | # Disassembler, Zydis 112 | if [ -f "${PREFIX}/lib/libZydis.a" ]; then 113 | export ZYDIS_STATIC_LIB="${PREFIX}/lib/libZydis.a" 114 | else 115 | export ZYDIS_STATIC_LIB="${PREFIX}/lib64/libZydis.a" 116 | fi 117 | export PW_LDFLAGS="${PW_LDFLAGS} ${ZYDIS_STATIC_LIB}" 118 | else 119 | # Disassembler, Capstone 120 | export PW_LDFLAGS="${PW_LDFLAGS} ${PREFIX}/lib/libcapstone.a" 121 | fi 122 | 123 | ################################################################### 124 | # Process Watch itself 125 | ################################################################### 126 | export CC="${PW_CC}" 127 | export CXX="${PW_CXX}" 128 | ${DIR}/src/build.sh 129 | cp ${DIR}/src/processwatch ${DIR} 130 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | ################################################################### 6 | # CLEAN.SH 7 | # Cleans all dependency installations, build logs, and artifacts 8 | # from the project itself. Throws no errors. 9 | ################################################################### 10 | 11 | echo "Cleaning..." 12 | 13 | rm -rf deps/install &> /dev/null || true 14 | rm -rf deps/build_logs &> /dev/null || true 15 | 16 | rm processwatch &> /dev/null || true 17 | rm src/processwatch &> /dev/null || true 18 | rm src/*.o &> /dev/null || true 19 | rm src/ui/*.o &> /dev/null || true 20 | 21 | rm src/bpf/insn/*.o &> /dev/null || true 22 | rm src/bpf/insn/*.skel.h &> /dev/null || true 23 | rm src/bpf/insn/vmlinux.h &> /dev/null || true 24 | 25 | rm src/bpf/tma/*.o &> /dev/null || true 26 | rm src/bpf/tma/*.skel.h &> /dev/null || true 27 | rm src/bpf/tma/vmlinux.h &> /dev/null || true 28 | 29 | echo "Finished cleaning." 30 | -------------------------------------------------------------------------------- /deps/build_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | ################################################################### 6 | # NOTE 7 | # This is NOT meant to be used standalone. 8 | ################################################################### 9 | BUILD_DEPS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 10 | 11 | # These are used to compile the dependencies 12 | DEPS_DIR="${BUILD_DEPS_DIR}" 13 | BPFTOOL_SRC_DIR="${DEPS_DIR}/bpftool/src" 14 | TINYEXPR_SRC_DIR="${DEPS_DIR}/tinyexpr" 15 | JEVENTS_SRC_DIR="${DEPS_DIR}/pmu-tools/jevents" 16 | 17 | if [ "${ARCH}" == "x86_64" ]; then 18 | ZYDIS_SRC_DIR="${DEPS_DIR}/zydis" 19 | else 20 | CAPSTONE_SRC_DIR="${DEPS_DIR}/capstone" 21 | fi 22 | 23 | # We export these because they're used by src/build.sh 24 | export PREFIX="${DEPS_DIR}/install" 25 | export CFLAGS="-O2 -Wall" 26 | 27 | # Prepare the dependency-building logs 28 | BUILD_LOGS=${DEPS_DIR}/build_logs 29 | rm -rf ${BUILD_LOGS} || true 30 | mkdir -p ${BUILD_LOGS} || true 31 | 32 | git submodule init 33 | git submodule update 34 | 35 | ################################################################### 36 | # bpftool 37 | ################################################################### 38 | # Compile a standalone copy of bpftool. This is so that we no longer 39 | # have to depend on the user installing `linux-tools` packages or 40 | # equivalent, since that can sometimes be a pain, especially if 41 | # they're not running the latest available kernel for their 42 | # distribution. Also builds a static copy of libbpf. 43 | 44 | cd ${BPFTOOL_SRC_DIR} 45 | 46 | make clean &> ${BUILD_LOGS}/bpftool.log 47 | make &>> ${BUILD_LOGS}/bpftool.log 48 | RETVAL=$? 49 | if [ ${RETVAL} -ne 0 ]; then 50 | echo " Building bpftool failed. Please see ${BUILD_LOGS}/bpftool.log for more details." 51 | exit 1 52 | fi 53 | 54 | # Install the bpftool binary and the static libbpf.a 55 | mkdir -p ${PREFIX}/bin 56 | mkdir -p ${PREFIX}/lib 57 | mkdir -p ${PREFIX}/include 58 | cp bpftool ${PREFIX}/bin/bpftool 59 | cp libbpf/libbpf.a ${PREFIX}/lib/libbpf.a 60 | cp -r libbpf/include/* ${PREFIX}/include/ 61 | 62 | if ! command -v ${BPFTOOL} &> /dev/null; then 63 | export PATH="${PREFIX}/bin:${PATH}" 64 | echo " No system bpftool found! Compiling libbpf and bpftool..." 65 | else 66 | echo " Using system bpftool." 67 | fi 68 | 69 | ################################################################### 70 | # capstone 71 | ################################################################### 72 | if [ "${ARCH}" == "aarch64" ]; then 73 | echo " Compiling capstone..." 74 | 75 | cd ${CAPSTONE_SRC_DIR} 76 | 77 | make clean &> ${BUILD_LOGS}/capstone.log 78 | CAPSTONE_ARCHS="arm aarch64 x86" ./make.sh &> ${BUILD_LOGS}/capstone.log 79 | RETVAL=$? 80 | if [ ${RETVAL} -ne 0 ]; then 81 | echo " Building capstone failed. Please see ${BUILD_LOGS}/capstone.log for more details." 82 | exit 1 83 | fi 84 | 85 | # Install the capstone library and headers 86 | cp libcapstone.a ${PREFIX}/lib/. 87 | cp libcapstone.so.5 ${PREFIX}/lib/. 88 | cp -r include/* ${PREFIX}/include/ 89 | fi 90 | 91 | ################################################################### 92 | # zydis 93 | ################################################################### 94 | if [ "${ARCH}" == "x86_64" ]; then 95 | 96 | echo " Compiling zydis..." 97 | 98 | mkdir -p "${PREFIX}" 99 | cd ${ZYDIS_SRC_DIR} 100 | 101 | # Zycore, the only dependency of Zydis 102 | cd dependencies/zycore 103 | rm -rf build \ 104 | &>> ${BUILD_LOGS}/zydis.log 105 | mkdir build \ 106 | &>> ${BUILD_LOGS}/zydis.log 107 | cd build 108 | &>> ${BUILD_LOGS}/zydis.log 109 | ${CMAKE} \ 110 | -DCMAKE_INSTALL_PREFIX=${PREFIX} \ 111 | .. \ 112 | &>> ${BUILD_LOGS}/zydis.log 113 | RETVAL=$? 114 | if [ ${RETVAL} -ne 0 ]; then 115 | echo " Compiling Zycore failed. Please check ${BUILD_LOGS}/zydis.log for more details." 116 | exit 1 117 | fi 118 | 119 | make \ 120 | &>> ${BUILD_LOGS}/zydis.log 121 | RETVAL=$? 122 | if [ ${RETVAL} -ne 0 ]; then 123 | echo " Compiling Zydis failed. Please check ${BUILD_LOGS}/zydis.log for more details." 124 | exit 1 125 | fi 126 | make install \ 127 | &>> ${BUILD_LOGS}/zydis.log 128 | 129 | # Zydis itself 130 | cd ${ZYDIS_SRC_DIR} 131 | rm -rf build && mkdir build && cd build 132 | ${CMAKE} \ 133 | -DCMAKE_INSTALL_PREFIX=${PREFIX} \ 134 | -DZYDIS_FEATURE_ENCODER=OFF \ 135 | -DZYDIS_BUILD_EXAMPLES=OFF \ 136 | -DZYDIS_BUILD_TOOLS=OFF \ 137 | -DZYDIS_FEATURE_AVX512=ON \ 138 | .. \ 139 | &>> ${BUILD_LOGS}/zydis.log 140 | RETVAL=$? 141 | if [ ${RETVAL} -ne 0 ]; then 142 | echo "Compiling Zydis failed. Please check ${BUILD_LOGS}/zydis.log for more details." 143 | exit 1 144 | fi 145 | 146 | make \ 147 | &>> ${BUILD_LOGS}/zydis.log 148 | RETVAL=$? 149 | if [ ${RETVAL} -ne 0 ]; then 150 | echo "Compiling Zydis failed. Please check ${BUILD_LOGS}/zydis.log for more details." 151 | exit 1 152 | fi 153 | make install \ 154 | &>> ${BUILD_LOGS}/zydis.log 155 | 156 | cd ${DEPS_DIR} 157 | fi 158 | -------------------------------------------------------------------------------- /src/bpf/insn/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | # WARNING: ONLY `-O2` OPTIMIZATIONS ARE SUPPORTED 6 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 7 | 8 | echo "Building the 'insn' BPF program:" 9 | 10 | # Get this kernel's `vmlinux.h` 11 | echo " Gathering BTF information for this kernel..." 12 | ${BPFTOOL} btf dump file /sys/kernel/btf/vmlinux format c > ${DIR}/vmlinux.h 13 | RETVAL="$?" 14 | if [ $RETVAL -ne 0 ]; then 15 | echo " Your current kernel does not have BTF information." 16 | echo " This is required for running eBPF programs." 17 | echo " For the purposes of compiling eBPF, though, we'll just use" 18 | echo " a pre-generated vmlinux.h." 19 | cp ${DIR}/../vmlinux_505.h ${DIR}/vmlinux.h 20 | fi 21 | 22 | # Compile the BPF object code 23 | echo " Compiling the BPF program..." 24 | ${CLANG} ${BPF_CFLAGS} -target bpf \ 25 | -I${DIR} -I${PREFIX}/include -c ${DIR}/insn.bpf.c -o ${DIR}/insn.bpf.o 26 | 27 | # Strip the object file (for a smaller filesize) 28 | echo " Stripping the object file..." 29 | ${LLVM_STRIP} -g ${DIR}/insn.bpf.o 30 | 31 | # Compile the object file into the skeleton header 32 | echo " Generating the BPF skeleton header..." 33 | ${BPFTOOL} gen skeleton ${DIR}/insn.bpf.o > ${DIR}/insn.skel.h 34 | -------------------------------------------------------------------------------- /src/bpf/insn/insn.bpf.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "insn.h" 8 | 9 | #ifdef INSNPROF_LEGACY_PERF_BUFFER 10 | 11 | /** 12 | PERFBUFFER INTERFACE: AVAILABLE EARLIER THAN 5.8.0. 13 | **/ 14 | 15 | struct { 16 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 17 | __uint(key_size, sizeof(int)); 18 | __uint(value_size, sizeof(int)); 19 | } pb SEC(".maps"); 20 | 21 | SEC("perf_event") 22 | int insn_collect(struct bpf_perf_event_data *ctx) { 23 | u32 cpu; 24 | struct insn_info insn_info = {}; 25 | long retval; 26 | 27 | /* Construct the insn_info struct */ 28 | u64 pid_tgid = bpf_get_current_pid_tgid(); 29 | u32 pid = pid_tgid >> 32; 30 | insn_info.pid = pid; 31 | 32 | retval = bpf_probe_read_user(insn_info.insn, 15, (void *) ctx->regs.ip); 33 | if(retval < 0) { 34 | return 0; 35 | } 36 | bpf_get_current_comm(insn_info.name, sizeof(insn_info.name)); 37 | 38 | /* Place insn_info in the ringbuf */ 39 | #ifdef BPF_F_CURRENT_CPU 40 | bpf_perf_event_output(ctx, &pb, BPF_F_CURRENT_CPU, &insn_info, sizeof(struct insn_info)); 41 | #else 42 | cpu = bpf_get_smp_processor_id(); 43 | bpf_perf_event_output(ctx, &pb, cpu, &insn_info, sizeof(struct insn_info)); 44 | #endif 45 | 46 | return 0; 47 | } 48 | 49 | #else 50 | 51 | /** 52 | RINGBUFFER INTERFACE: REQUIRES LINUX 5.8.0. 53 | **/ 54 | 55 | struct { 56 | __uint(type, BPF_MAP_TYPE_RINGBUF); 57 | __uint(max_entries, MAX_ENTRIES); 58 | } rb SEC(".maps"); 59 | 60 | SEC("perf_event") 61 | int insn_collect(struct bpf_perf_event_data *ctx) { 62 | struct insn_info *insn_info; 63 | long retval = 0; 64 | 65 | /* Reserve space for this entry */ 66 | insn_info = bpf_ringbuf_reserve(&rb, sizeof(struct insn_info), 0); 67 | if(!insn_info) { 68 | return 1; 69 | } 70 | 71 | /* Construct the insn_info struct */ 72 | u64 pid_tgid = bpf_get_current_pid_tgid(); 73 | u32 pid = pid_tgid >> 32; 74 | insn_info->pid = pid; 75 | 76 | #ifdef __TARGET_ARCH_arm 77 | retval = bpf_probe_read_user(insn_info->insn, 4, (void *) ctx->regs.pc); 78 | #elif __TARGET_ARCH_x86 79 | retval = bpf_probe_read_user(insn_info->insn, 15, (void *) ctx->regs.ip); 80 | #endif 81 | if(retval < 0) { 82 | bpf_ringbuf_discard(insn_info, BPF_RB_NO_WAKEUP); 83 | return 1; 84 | } 85 | bpf_get_current_comm(insn_info->name, sizeof(insn_info->name)); 86 | 87 | /* Place insn_info in the ringbuf */ 88 | bpf_ringbuf_submit(insn_info, BPF_RB_NO_WAKEUP); 89 | 90 | return 0; 91 | } 92 | 93 | #endif 94 | 95 | char LICENSE[] SEC("license") = "GPL"; 96 | -------------------------------------------------------------------------------- /src/bpf/insn/insn.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #ifndef INSN_H 5 | #define INSN_H 6 | 7 | #define TASK_COMM_LEN 16 8 | #define MAX_ENTRIES 512*1024*1024 9 | 10 | struct insn_info { 11 | __u32 pid; 12 | unsigned char insn[15]; 13 | char name[TASK_COMM_LEN]; 14 | }; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/bpf/tma/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | # This script uses `awk` to construct maps.h. 6 | # Instead of using preprocessor trickery, we 7 | # simply loop over a counter (starting at 0) 8 | # with `awk`, and define a map and accompanying 9 | # function using that counter in their name. 10 | # WARNING: ONLY `-O2` OPTIMIZATIONS ARE SUPPORTED 11 | 12 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 13 | MAX_PERF_EVENTS=100 14 | 15 | # Accepts one argument, and constructs a BPF code file 16 | # with one map and one accessor function, named `map${1}.c`. 17 | function make_map() { 18 | awk \ 19 | -v i="${1}" \ 20 | ' 21 | BEGIN{ 22 | printf("#include \n"); 23 | printf("#include \n"); 24 | printf("#include \n"); 25 | printf("#include \n"); 26 | printf("#include \"perf_slots.h\"\n"); 27 | printf("#include \"map_utils.h\"\n"); 28 | printf("DEFINE_MAP(%d);\n", i); 29 | printf("DEFINE_FUNCTION(%d);\n", i); 30 | printf("DEFINE_LICENSE\n"); 31 | } 32 | ' > ${DIR}/maps/map${1}.bpf.c 33 | } 34 | 35 | function compile_bpf() { 36 | ${CLANG} ${BPF_CFLAGS} -target bpf -D__TARGET_ARCH_x86 \ 37 | -I${DIR} -I${PREFIX}/include -c ${DIR}/maps/map${1}.bpf.c -o ${DIR}/maps/map${1}.bpf.o 38 | } 39 | 40 | function strip_bpf() { 41 | ${LLVM_STRIP} -g ${DIR}/maps/map${1}.bpf.o 42 | } 43 | 44 | rm -rf ${DIR}/maps 45 | mkdir -p ${DIR}/maps 46 | let MAX_PERF_EVENTS-- 47 | 48 | # Get this kernel's `vmlinux.h` 49 | echo " Gathering BTF information for this kernel..." 50 | ${BPFTOOL} btf dump file /sys/kernel/btf/vmlinux format c > ${DIR}/vmlinux.h 51 | RETVAL="$?" 52 | if [ $RETVAL -ne 0 ]; then 53 | echo " Your current kernel does not have BTF information." 54 | echo " This is required for running eBPF programs." 55 | echo " For the purposes of compiling eBPF, though, we'll just use" 56 | echo " a pre-generated vmlinux.h." 57 | cp ${DIR}/../vmlinux_505.h ${DIR}/vmlinux.h 58 | fi 59 | 60 | BATCH_SIZE=$(nproc) 61 | for ITER in $(seq 0 ${MAX_PERF_EVENTS}); do 62 | ((i=i%BATCH_SIZE)); ((i++==0)) && wait 63 | make_map "${ITER}" & 64 | done 65 | wait 66 | 67 | # Now, build the BPF object files 68 | BATCH_SIZE=$(nproc) 69 | for ITER in $(seq 0 ${MAX_PERF_EVENTS}); do 70 | ((i=i%BATCH_SIZE)); ((i++==0)) && wait 71 | compile_bpf "${ITER}" & 72 | done 73 | wait 74 | 75 | BATCH_SIZE=$(nproc) 76 | for ITER in $(seq 0 ${MAX_PERF_EVENTS}); do 77 | ((i=i%BATCH_SIZE)); ((i++==0)) && wait 78 | strip_bpf "${ITER}" & 79 | done 80 | wait 81 | 82 | # Construct a list of BPF programs that we need to link together 83 | FILES="" 84 | for ITER in $(seq 0 ${MAX_PERF_EVENTS}); do 85 | FILES+="${DIR}/maps/map${ITER}.bpf.o " 86 | done 87 | 88 | # Try to link them. 89 | ${BPFTOOL} gen object ${DIR}/perf_slots.bpf.o ${FILES} # Only because I have multiple object files 90 | 91 | ${BPFTOOL} gen skeleton ${DIR}/perf_slots.bpf.o > ${DIR}/perf_slots.skel.h 92 | -------------------------------------------------------------------------------- /src/bpf/tma/map_utils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #define MAX_ENTRIES 8192 7 | 8 | #define STRUCT_NAME(FIRST, SECOND) \ 9 | FIRST##SECOND 10 | #define SECTION_NAME(FIRST, SECOND) \ 11 | SEC(FIRST # SECOND) 12 | #define FUNC_NAME(FIRST, SECOND) \ 13 | FIRST##SECOND 14 | 15 | /** 16 | The `DEFINE_MAP` macro simply defines a BPF map titled 17 | `perf_slot_map_[num]`, which stores information about 18 | a single perf event counter. 19 | **/ 20 | #define DEFINE_MAP(_VAL_) \ 21 | struct { \ 22 | __uint(type, BPF_MAP_TYPE_HASH); \ 23 | __uint(max_entries, MAX_ENTRIES); \ 24 | __type(key, __u64); \ 25 | __type(value, __u64); \ 26 | } STRUCT_NAME(perf_slot_map_, _VAL_) SEC(".maps"); 27 | 28 | /** 29 | Next, define a macro which takes an index as an argument, and which 30 | stores the `perf` counters' values in the BPF map associated with that index. 31 | **/ 32 | #define INC_TASK_INFO(sample_period, index) \ 33 | __u64 pid, key, val, *val_ptr; \ 34 | __u32 cpu; \ 35 | int err; \ 36 | unsigned int task_flags; \ 37 | struct task_struct *task; \ 38 | \ 39 | cpu = bpf_get_smp_processor_id(); \ 40 | pid = bpf_get_current_pid_tgid(); \ 41 | key = (pid & 0xFFFFFFFF00000000) | cpu ; \ 42 | val = 0; \ 43 | \ 44 | val_ptr = bpf_map_lookup_elem(&( perf_slot_map_##index ), &key); \ 45 | if(!val_ptr) { \ 46 | val_ptr = &val; \ 47 | task = (struct task_struct *) bpf_get_current_task(); \ 48 | err = bpf_core_read(&task_flags, sizeof(unsigned int), &task->flags); \ 49 | if(!err) { \ 50 | if(task_flags & 0x00200000) { \ 51 | return 0; \ 52 | } \ 53 | } else { \ 54 | return 0; \ 55 | } \ 56 | } \ 57 | \ 58 | __sync_fetch_and_add(val_ptr, sample_period); \ 59 | \ 60 | if(val_ptr == &val) { \ 61 | bpf_map_update_elem(&perf_slot_map_ ## index, &key, val_ptr, 0); \ 62 | } \ 63 | \ 64 | return 0; 65 | 66 | /** 67 | This defines a macro which defines a `perf_event_[num]` function for us. 68 | Each of these functions is associated with one of the above BPF maps. 69 | When invoked, they store their counters in `perf_slot_map_[num]`. 70 | They use the above INC_TASK_INFO macro to accomplish this. 71 | **/ 72 | #define DEFINE_FUNCTION(index) \ 73 | SECTION_NAME("perf_event/", index) \ 74 | int FUNC_NAME(perf_event_, index) (struct bpf_perf_event_data *ctx) { \ 75 | INC_TASK_INFO(ctx->sample_period, index); \ 76 | } 77 | 78 | #define DEFINE_LICENSE \ 79 | char __license[] __attribute__((section("license"), used)) = "GPL"; 80 | -------------------------------------------------------------------------------- /src/bpf/tma/perf_slots.bpf.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "perf_slots.h" 8 | #include "maps.h" 9 | -------------------------------------------------------------------------------- /src/bpf/tma/perf_slots.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #ifndef PERFSCOPE_H 5 | #define PERFSCOPE_H 6 | 7 | /** 8 | This is the maximum number of perf events that the eBPF program 9 | can currently handle. Of course, change this value you modify 10 | the associated eBPF code. 11 | **/ 12 | #define MAX_PERF_EVENTS 100 13 | #define MAX_GROUPS 50 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2022 Intel Corporation 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | 7 | # Get the git commit hash 8 | cd ${DIR} 9 | GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 10 | GIT_COMMIT_HASH=$(git rev-parse HEAD) 11 | 12 | # Compile the appropriate BPF program 13 | if [ "${TMA}" = true ]; then 14 | ${DIR}/bpf/tma/build.sh 15 | ${CLANG} \ 16 | ${PW_CFLAGS} -I${DIR}/bpf/tma -I${PREFIX}/include \ 17 | -DGIT_COMMIT_HASH="\"${GIT_COMMIT_HASH}\"" \ 18 | -c ${DIR}/processwatch.c -o ${DIR}/processwatch.o 19 | else 20 | ${DIR}/bpf/insn/build.sh 21 | ${CLANG} \ 22 | ${PW_CFLAGS} -I${DIR}/bpf/insn -I${PREFIX}/include \ 23 | -DGIT_COMMIT_HASH="\"${GIT_COMMIT_HASH}\"" \ 24 | -c ${DIR}/processwatch.c -o ${DIR}/processwatch.o 25 | fi 26 | 27 | ################################################################### 28 | # USERSPACE PROGRAM 29 | ################################################################### 30 | echo "Linking the main Process Watch binary..." 31 | ${CLANG} ${PW_CFLAGS} ${DIR}/processwatch.o \ 32 | -o ${DIR}/processwatch -lrt -lpthread -lm ${PW_LDFLAGS} -lelf -lz 33 | -------------------------------------------------------------------------------- /src/kerninfo.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #ifndef INSNPROF_KERNINFO 5 | #define INSNPROF_KERNINFO 6 | 7 | #include 8 | 9 | #ifdef __x86_64__ 10 | 11 | /* Return value: >0: Valid, -1: Error */ 12 | static int get_ibs_op_type(void) { 13 | static int type = -1; /* -1 : Unknown, 0: Failed to read first time, >0: Valid */ 14 | FILE *fp; 15 | int ret; 16 | 17 | if (type != -1) { 18 | if (!type) 19 | return -1; 20 | return type; 21 | } 22 | 23 | fp = fopen("/sys/bus/event_source/devices/ibs_op/type", "r"); 24 | if (!fp) { 25 | fprintf(stderr, "Failed to find ibs_op// pmu sysfs. [%m]\n"); 26 | type = 0; 27 | return -1; 28 | } 29 | 30 | ret = fscanf(fp, "%d", &type); 31 | fclose(fp); 32 | if (ret != 1) { 33 | fprintf(stderr, "Failed to read ibs_op// type. [%m]\n"); 34 | type = 0; 35 | return -1; 36 | } 37 | return type; 38 | } 39 | 40 | void get_vendor(char *vendor) { 41 | unsigned int a[4]; 42 | 43 | asm ( 44 | /* %rbx must be preserved. */ 45 | "mov %%rbx, %%rdi\n" 46 | "cpuid\n" 47 | "xchg %%rdi, %%rbx\n" 48 | : "=a"(a[0]), "=D"(a[1]), "=c"(a[2]), "=d"(a[3]) 49 | : "a"(0) 50 | ); 51 | strncpy(&vendor[0], (char *)&a[1], 4); 52 | strncpy(&vendor[4], (char *)&a[3], 4); 53 | strncpy(&vendor[8], (char *)&a[2], 4); 54 | } 55 | 56 | int is_amd_arch(void) { 57 | static int amd = -1; /* -1: Unknown, 1: Yes, 0: No */ 58 | char vendor[13] = {0}; 59 | 60 | if (amd != -1) 61 | return amd; 62 | 63 | get_vendor(vendor); 64 | amd = strcmp(vendor, "AuthenticAMD") ? 0 : 1; 65 | return amd; 66 | } 67 | 68 | void get_pmu_string(char *pmu_name) { 69 | FILE *f; 70 | size_t retval; 71 | 72 | if (is_amd_arch()) { 73 | f = fopen("/sys/bus/event_source/devices/ibs_op", "r"); 74 | if (!f) { 75 | fprintf(stderr, "WARNING: Unable to open '/sys/bus/event_source/devices/ibs_op'. " 76 | "Using software events.\n"); 77 | strcpy(pmu_name, "invalid"); 78 | return; 79 | } 80 | fclose(f); 81 | strcpy(pmu_name, "ibs_op"); 82 | return; 83 | } 84 | 85 | f = fopen("/sys/devices/cpu/caps/pmu_name", "r"); 86 | if(!f) { 87 | fprintf(stderr, "WARNING: Unable to open '/sys/devices/cpu/caps/pmu_name'. Using software events.\n"); 88 | strcpy(pmu_name, "invalid"); 89 | return; 90 | } 91 | retval = fread(pmu_name, sizeof(char), 31, f); 92 | if(retval == 0) { 93 | fprintf(stderr, "WARNING: Unable to read '/sys/devices/cpu/caps/pmu_name'. Using software events.\n"); 94 | strcpy(pmu_name, "invalid"); 95 | return; 96 | } 97 | fclose(f); 98 | 99 | } 100 | 101 | #endif 102 | 103 | #define MINIMUM_ISA_SUPPORT \ 104 | fprintf(stderr, "Failed to parse '/proc/cpuinfo'! Assuming minimum ISA support.\n"); \ 105 | return 0; 106 | 107 | /* It's either this, or I call `lscpu` and parse its output. */ 108 | char supports_amx_tile() { 109 | FILE *cpu_info = fopen("/proc/cpuinfo", "r"); 110 | char *buff, *buff2, *buff_orig, found; 111 | int chars_read; 112 | size_t n; 113 | ssize_t nread; 114 | 115 | if(cpu_info == NULL) { 116 | MINIMUM_ISA_SUPPORT; 117 | } 118 | 119 | buff = NULL; 120 | found = 0; 121 | while(!found && !feof(cpu_info)) { 122 | if(fscanf(cpu_info, "%*[^f]") == EOF) { 123 | MINIMUM_ISA_SUPPORT; 124 | } 125 | found = fscanf(cpu_info, "flags%m[^:]", &buff) == 1; 126 | } 127 | fseek(cpu_info, 1, SEEK_CUR); 128 | free(buff); 129 | 130 | buff = NULL; 131 | nread = getline(&buff, &n, cpu_info); 132 | fclose(cpu_info); 133 | if(nread <= 0) { 134 | MINIMUM_ISA_SUPPORT; 135 | } 136 | buff_orig = buff; 137 | 138 | found = 0; 139 | while(!found) { 140 | nread = sscanf(buff, "%ms%n", &buff2, &chars_read); 141 | if((nread == EOF) || (nread == 0)) { 142 | break; 143 | } 144 | buff += chars_read; 145 | if(!strcmp(buff2, "amx_tile")) { 146 | found = 1; 147 | } 148 | free(buff2); 149 | } 150 | free(buff_orig); 151 | 152 | if(found) { 153 | return 1; 154 | } 155 | return 0; 156 | } 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /src/process_info.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #define resize_array(ptr, old_size, new_size, datatype, new_value, iterator) \ 9 | ptr = realloc(ptr, new_size * sizeof(datatype)); \ 10 | if(!ptr) { \ 11 | fprintf(stderr, "Failed to allocate more memory! Aborting.\n"); \ 12 | exit(1); \ 13 | } \ 14 | for(iterator = old_size; iterator < new_size; iterator++) { \ 15 | ptr[iterator] = new_value; \ 16 | } \ 17 | 18 | /** 19 | grow_interval_proc_arrs: This grows the per-process arrays in 20 | the `interval_results_t` struct. It ensures that the per-process 21 | array can store up to `pid_ctr + 1` values. 22 | **/ 23 | #define INITIAL_SIZE 64 24 | static void grow_interval_proc_arrs() { 25 | int old_size, new_size, i, n; 26 | 27 | /* We don't need to allocate anything */ 28 | if((results->interval->pid_ctr <= results->interval->proc_arr_size - 1) && 29 | (results->interval->proc_arr_size != 0)) { 30 | return; 31 | } 32 | 33 | /* Figure out the old size and new size */ 34 | old_size = results->interval->proc_arr_size; 35 | if(old_size == 0) { 36 | new_size = INITIAL_SIZE; 37 | } else { 38 | new_size = (results->interval->proc_arr_size * 2); 39 | } 40 | 41 | /* Per-process totals */ 42 | resize_array(results->interval->proc_percent, old_size, new_size, double, 0, n); 43 | resize_array(results->interval->proc_failed_percent, old_size, new_size, double, 0, n); 44 | resize_array(results->interval->proc_num_samples, old_size, new_size, uint64_t, 0, n); 45 | resize_array(results->interval->proc_num_failed, old_size, new_size, uint64_t, 0, n); 46 | resize_array(results->interval->pids, old_size, new_size, uint32_t, 0, n); 47 | 48 | /* CATEGORIES */ 49 | for(i = 0; i <= CATEGORY_MAX_VALUE; i++) { 50 | resize_array(results->interval->proc_cat_count[i], old_size, new_size, uint64_t, 0, n); 51 | resize_array(results->interval->proc_cat_percent[i], old_size, new_size, double, 0, n); 52 | } 53 | 54 | /* MNEMONICS */ 55 | for(i = 0; i <= MNEMONIC_MAX_VALUE; i++) { 56 | resize_array(results->interval->proc_insn_count[i], old_size, new_size, uint64_t, 0, n); 57 | resize_array(results->interval->proc_insn_percent[i], old_size, new_size, double, 0, n); 58 | } 59 | 60 | #ifdef __x86_64__ 61 | for(i = 0; i <= EXTENSION_MAX_VALUE; i++) { 62 | resize_array(results->interval->proc_ext_count[i], old_size, new_size, uint64_t, 0, n); 63 | resize_array(results->interval->proc_ext_percent[i], old_size, new_size, double, 0, n); 64 | } 65 | #endif 66 | 67 | results->interval->proc_arr_size = new_size; 68 | 69 | return; 70 | } 71 | 72 | static uint32_t djb2(char *name) { 73 | uint32_t hash = 5381; 74 | int c; 75 | 76 | while((c = *name++)) { 77 | hash = ((hash << 5) + hash) + c; 78 | } 79 | 80 | return hash; 81 | } 82 | 83 | /** 84 | get_num_process_info 85 | ** 86 | Get the number of processes that have been associated with this PID thus far. 87 | **/ 88 | static int get_num_process_info(uint32_t pid) { 89 | int num_procs; 90 | process_t **ptr; 91 | process_t **proc_arr; 92 | 93 | proc_arr = results->process_info.arr[pid]; 94 | if(!proc_arr) { 95 | return 0; 96 | } 97 | 98 | num_procs = 0; 99 | ptr = &(proc_arr[0]); 100 | while(*ptr) { 101 | num_procs++; 102 | ptr++; 103 | } 104 | 105 | return num_procs; 106 | } 107 | 108 | /** 109 | get_interval_process_info 110 | ** 111 | Gets the process_t pointer for the latest process that's using this PID. 112 | **/ 113 | static process_t *get_interval_process_info(uint32_t pid) { 114 | int num_procs; 115 | 116 | num_procs = get_num_process_info(pid); 117 | if(!num_procs) return NULL; 118 | return results->process_info.arr[pid][num_procs - 1]; 119 | } 120 | 121 | /** 122 | get_process_info 123 | ** 124 | Returns a process_t to the process with the given PID, hash of the name. 125 | If not found, returns NULL. 126 | **/ 127 | static process_t *get_process_info(uint32_t pid, uint32_t hash) { 128 | process_t *process; 129 | process_t **proc_arr; 130 | int i; 131 | 132 | proc_arr = results->process_info.arr[pid]; 133 | if(!proc_arr) { 134 | return NULL; 135 | } 136 | 137 | /* Iterate over the processes and grab the one whose 138 | hash matches */ 139 | i = 0; 140 | process = proc_arr[i]; 141 | while(process) { 142 | if(process->name_hash == hash) { 143 | return process; 144 | } 145 | process = proc_arr[++i]; 146 | } 147 | 148 | return NULL; 149 | } 150 | 151 | /** 152 | update_one_process_info 153 | ** 154 | We've seen this PID before, but now the process name is different, 155 | so the OS is reusing PIDs. Add this PID to the array in `process_info`. 156 | **/ 157 | static void update_one_process_info(uint32_t pid, char *name, uint32_t hash, int num_procs) { 158 | process_t *process; 159 | 160 | /* Here, we're adding 2 additional elements because 161 | we need one for the NULL terminator, and another 162 | for the new process. */ 163 | results->process_info.arr[pid] = realloc(results->process_info.arr[pid], 164 | (num_procs + 2) * sizeof(process_t *)); 165 | process = malloc(sizeof(process_t)); 166 | if(!process) { 167 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 168 | exit(1); 169 | } 170 | process->name = strdup(name); 171 | process->name_hash = hash; 172 | process->index = results->pid_ctr++; 173 | results->process_info.arr[pid][num_procs] = process; 174 | results->process_info.arr[pid][num_procs + 1] = NULL; 175 | } 176 | 177 | /** 178 | add_process_info 179 | ** 180 | If we haven't seen this PID at all, create a new array of process_t pointers. 181 | **/ 182 | static void add_process_info(uint32_t pid, char *name, uint32_t hash) { 183 | process_t *process; 184 | 185 | /* Allocate the new process_t */ 186 | process = malloc(sizeof(process_t)); 187 | if(!process) { 188 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 189 | exit(1); 190 | } 191 | process->name = strdup(name); 192 | process->name_hash = hash; 193 | process->index = results->pid_ctr++; 194 | 195 | /* Now add that process_t to the process_arr_t struct */ 196 | results->process_info.arr[pid] = (process_t **) malloc(sizeof(process_t *) * 2); 197 | results->process_info.arr[pid][0] = process; 198 | results->process_info.arr[pid][1] = NULL; 199 | } 200 | 201 | static void update_process_info(uint32_t pid, char *name, uint32_t hash) { 202 | int num_procs; 203 | process_t *process; 204 | 205 | /* First, get the array of process_t pointers. */ 206 | num_procs = get_num_process_info(pid); 207 | if(num_procs > 0) { 208 | process = get_process_info(pid, hash); 209 | if(!process) { 210 | update_one_process_info(pid, name, hash, num_procs); 211 | } 212 | } else { 213 | add_process_info(pid, name, hash); 214 | } 215 | 216 | /* Update the maximum PID we've seen */ 217 | if(pid > results->process_info.max_pid) { 218 | results->process_info.max_pid = pid; 219 | } 220 | } 221 | 222 | static int get_interval_proc_arr_index(uint32_t pid) { 223 | int i; 224 | 225 | /* Have we seen this PID this interval? */ 226 | for(i = 0; i < results->interval->pid_ctr; i++) { 227 | if(results->interval->pids[i] == pid) { 228 | return i; 229 | } 230 | } 231 | 232 | /* Increment the counter, thus choosing an index for this process 233 | in the proc_* arrays. */ 234 | i = results->interval->pid_ctr++; 235 | grow_interval_proc_arrs(); 236 | 237 | return i; 238 | } 239 | -------------------------------------------------------------------------------- /src/processwatch.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #ifdef __aarch64__ 13 | #include 14 | #elif __x86_64__ 15 | #include 16 | #endif 17 | 18 | #include "processwatch.h" 19 | 20 | /* This is where results are stored */ 21 | results_t *results = NULL; 22 | bpf_info_t *bpf_info = NULL; 23 | pthread_rwlock_t results_lock = PTHREAD_RWLOCK_INITIALIZER; 24 | 25 | #ifndef GIT_COMMIT_HASH 26 | #define GIT_COMMIT_HASH "?" 27 | #endif 28 | 29 | /******************************************************************************* 30 | * OPTIONS 31 | *******************************************************************************/ 32 | 33 | static struct option long_options[] = { 34 | {"help", no_argument, 0, 'h'}, 35 | {"version", no_argument, 0, 'v'}, 36 | {"debug", no_argument, 0, 'd'}, 37 | {"interval", required_argument, 0, 'i'}, 38 | {"num-intervals", required_argument, 0, 'n'}, 39 | {"csv", no_argument, 0, 'c'}, 40 | {"pid", required_argument, 0, 'p'}, 41 | {"mnemonics", no_argument, 0, 'm'}, 42 | {"extensions", no_argument, 0, 'e'}, 43 | {"sample-period", required_argument, 0, 's'}, 44 | {"filter", required_argument, 0, 'f'}, 45 | {"list", no_argument, 0, 'l'}, 46 | {"btf", required_argument, 0, 'b'}, 47 | {"all", no_argument, 0, 'a'}, 48 | {0, 0, 0, 0} 49 | }; 50 | 51 | struct pw_opts_t pw_opts; 52 | 53 | #ifdef __aarch64__ 54 | 55 | #define NUM_DEFAULT_COL_STRS 4 56 | #define NUM_DEFAULT_MNEM_COL_STRS 0 57 | static int num_default_col_strs = NUM_DEFAULT_COL_STRS; 58 | static int num_default_mnem_col_strs = NUM_DEFAULT_MNEM_COL_STRS; 59 | static char *default_col_strs[NUM_DEFAULT_COL_STRS] = { 60 | "HasFPARMv8", 61 | "HasNEON", 62 | "HasSVE", 63 | "HasSVE2" 64 | }; 65 | static char *default_mnem_col_strs[NUM_DEFAULT_MNEM_COL_STRS] = { 66 | }; 67 | static char *default_ext_col_strs[1]; 68 | 69 | #elif __x86_64__ 70 | 71 | #define NUM_DEFAULT_COL_STRS 5 72 | #define NUM_DEFAULT_MNEM_COL_STRS 0 73 | #define NUM_DEFAULT_EXT_COL_STRS 6 74 | static int num_default_col_strs = NUM_DEFAULT_COL_STRS; 75 | static int num_default_mnem_col_strs = NUM_DEFAULT_MNEM_COL_STRS; 76 | static int num_default_ext_col_strs = NUM_DEFAULT_EXT_COL_STRS; 77 | static char *default_col_strs[NUM_DEFAULT_COL_STRS] = { 78 | "SSE", 79 | "AVX", 80 | "AVX2", 81 | "AVX512", 82 | "AMX_TILE" 83 | }; 84 | static char *default_mnem_col_strs[NUM_DEFAULT_MNEM_COL_STRS] = { 85 | }; 86 | static char *default_ext_col_strs[NUM_DEFAULT_EXT_COL_STRS] = { 87 | "BASE", 88 | "AVX512EVEX", 89 | "AVX512VEX", 90 | "AMX_BF16", 91 | "AMX_INT8", 92 | "AMX_TILE" 93 | }; 94 | 95 | #endif 96 | 97 | void list_opt() { 98 | int i; 99 | 100 | if(pw_opts.show_mnemonics) { 101 | printf("Listing all available mnemonics:\n"); 102 | for(i = 0; i <= MNEMONIC_MAX_VALUE; i++) { 103 | #ifdef __aarch64__ 104 | printf("%s\n", cs_insn_name(handle, i)); 105 | #elif __x86_64__ 106 | printf("%s\n", ZydisMnemonicGetString(i)); 107 | #endif 108 | } 109 | #ifdef __x86_64__ 110 | } else if(pw_opts.show_extensions) { 111 | printf("Listing all available extensions:\n"); 112 | for(i = 0; i <= EXTENSION_MAX_VALUE; i++) { 113 | printf("%s\n", ZydisISAExtGetString(i)); 114 | } 115 | #endif 116 | } else { 117 | printf("Listing all available categories:\n"); 118 | for(i = 0; i <= CATEGORY_MAX_VALUE; i++) { 119 | #ifdef __aarch64__ 120 | /* Capstone aarch64 groups aren't consecutive :( */ 121 | if (cs_group_name(handle, i) != NULL) printf("%s\n", cs_group_name(handle, i)); 122 | #elif __x86_64__ 123 | printf("%s\n", ZydisCategoryGetString(i)); 124 | #endif 125 | } 126 | } 127 | } 128 | 129 | void convert_col_strs() { 130 | int i, n, found, max_value; 131 | const char *name; 132 | 133 | /* Convert col_strs to an array of the ZydisInstructionCategory or ZydisMnemonic enum. */ 134 | if(pw_opts.show_mnemonics) { 135 | max_value = MNEMONIC_MAX_VALUE; 136 | #ifdef __x86_64__ 137 | } else if(pw_opts.show_extensions) { 138 | max_value = EXTENSION_MAX_VALUE; 139 | #endif 140 | } else { 141 | max_value = CATEGORY_MAX_VALUE; 142 | } 143 | 144 | for(i = 0; i < pw_opts.col_strs_len; i++) { 145 | for(n = 0; n <= max_value; n++) { 146 | found = 0; 147 | if(pw_opts.show_mnemonics) { 148 | 149 | #ifdef __aarch64__ 150 | name = cs_insn_name(handle, n); 151 | #elif __x86_64__ 152 | name = ZydisMnemonicGetString(n); 153 | } else if(pw_opts.show_extensions) { 154 | name = ZydisISAExtGetString(n); 155 | #endif 156 | } else { 157 | #ifdef __aarch64__ 158 | name = cs_group_name(handle, n); 159 | #elif __x86_64__ 160 | name = ZydisCategoryGetString(n); 161 | #endif 162 | } 163 | if(name && strncasecmp(pw_opts.col_strs[i], name, strlen(pw_opts.col_strs[i])) == 0) { 164 | found = 1; 165 | pw_opts.cols_len++; 166 | pw_opts.cols = realloc(pw_opts.cols, sizeof(int) * pw_opts.cols_len); 167 | pw_opts.cols[pw_opts.cols_len - 1] = n; 168 | break; 169 | } 170 | } 171 | if(!found) { 172 | fprintf(stderr, "WARNING: Didn't recognize instruction mnemonic/category/extension: %s\n", pw_opts.col_strs[i]); 173 | } 174 | } 175 | } 176 | 177 | #ifdef __aarch64__ 178 | int aarch_get_num_cols() { 179 | int i, retval; 180 | 181 | /* Needed because Capstone's enums aren't consecutive, and their 182 | final value also isn't the final value (it's the final value + 1). */ 183 | retval = 0; 184 | if(pw_opts.show_mnemonics) { 185 | for(i = 0; i <= MNEMONIC_MAX_VALUE; i++) { 186 | if(cs_insn_name(handle, i) == NULL) continue; 187 | retval++; 188 | } 189 | } else { 190 | for(i = 0; i <= CATEGORY_MAX_VALUE; i++) { 191 | if(cs_group_name(handle, i) == NULL) continue; 192 | retval++; 193 | } 194 | } 195 | 196 | return retval; 197 | } 198 | #endif 199 | 200 | #ifdef __x86_64__ 201 | int x86_get_num_cols() { 202 | int retval; 203 | 204 | if(pw_opts.show_mnemonics) { 205 | retval = MNEMONIC_MAX_VALUE + 1; 206 | } else if(pw_opts.show_extensions) { 207 | retval = EXTENSION_MAX_VALUE + 1; 208 | } else { 209 | retval = CATEGORY_MAX_VALUE + 1; 210 | } 211 | 212 | return retval; 213 | } 214 | #endif 215 | 216 | void free_opts() { 217 | int i; 218 | 219 | if((pw_opts.col_strs != default_col_strs) && 220 | (pw_opts.col_strs != default_mnem_col_strs) && 221 | (pw_opts.col_strs != default_ext_col_strs)) { 222 | for(i = 0; i < pw_opts.col_strs_len; i++) { 223 | free(pw_opts.col_strs[i]); 224 | } 225 | free(pw_opts.col_strs); 226 | } 227 | } 228 | 229 | int read_opts(int argc, char **argv) { 230 | int option_index, index, elem; 231 | size_t size; 232 | int c; 233 | 234 | pw_opts.interval_time = 2; 235 | pw_opts.num_intervals = 0; 236 | pw_opts.pid = -1; 237 | pw_opts.show_mnemonics = 0; 238 | pw_opts.show_extensions = 0; 239 | pw_opts.csv = 0; 240 | pw_opts.btf_custom_path = NULL; 241 | pw_opts.debug = 0; 242 | pw_opts.sample_period = 100000; 243 | 244 | /* Column filters */ 245 | pw_opts.col_strs = NULL; 246 | pw_opts.col_strs_len = 0; 247 | pw_opts.cols = NULL; 248 | pw_opts.cols_len = 0; 249 | pw_opts.list = 0; 250 | 251 | while(1) { 252 | option_index = 0; 253 | c = getopt_long(argc, argv, "hvdi:cp:ms:f:ln:b:ea", 254 | long_options, &option_index); 255 | if(c == -1) { 256 | break; 257 | } 258 | 259 | switch(c) { 260 | case 0: 261 | printf("option %s\n", long_options[option_index].name); 262 | break; 263 | case 'v': 264 | printf("Version: %s\n", GIT_COMMIT_HASH); 265 | return -1; 266 | break; 267 | case 'h': 268 | printf("usage: processwatch [options]\n"); 269 | printf("\n"); 270 | printf("options:\n"); 271 | printf(" -h Displays this help message.\n"); 272 | printf(" -v Displays the version.\n"); 273 | printf(" -i Prints results every seconds.\n"); 274 | printf(" -n Prints results for intervals.\n"); 275 | printf(" -c Prints all results in CSV format to stdout.\n"); 276 | printf(" -p Only profiles .\n"); 277 | printf(" -m Displays instruction mnemonics, instead of categories.\n"); 278 | #ifdef __x86_64__ 279 | printf(" -e Displays instruction extensions, instead of categories. Only for x86.\n"); 280 | #endif 281 | printf(" -s Profiles instructions with a sampling period of . Defaults to 100000 instructions (1 in 100000 instructions).\n"); 282 | #ifdef __aarch64__ 283 | printf(" -f Can be used multiple times. Defines filters for columns. Defaults to 'FPARMv8', 'NEON', 'SVE' and 'SVE2'.\n"); 284 | #elif __x86_64__ 285 | printf(" -f Can be used multiple times. Defines filters for columns. Defaults to 'AVX', 'AVX2', and 'AVX512'.\n"); 286 | #endif 287 | printf(" -a Displays a column for each category, mnemonic, or extension. This is a lot of output!\n"); 288 | printf(" -l Prints a list of all available categories, mnemonics, or extensions.\n"); 289 | printf(" -d Prints only debug information.\n"); 290 | return -1; 291 | break; 292 | case 'b': 293 | if(pw_opts.btf_custom_path) { 294 | fprintf(stderr, "Multiple custom BTF files specified! Aborting.\n"); 295 | exit(1); 296 | } 297 | size = strlen(optarg); 298 | pw_opts.btf_custom_path = calloc(size + 1, sizeof(char)); 299 | strncpy(pw_opts.btf_custom_path, optarg, size); 300 | break; 301 | case 'i': 302 | /* Length in seconds of an interval */ 303 | pw_opts.interval_time = strtoul(optarg, NULL, 10); 304 | break; 305 | case 'n': 306 | /* Number of intervals */ 307 | pw_opts.num_intervals = strtoul(optarg, NULL, 10); 308 | break; 309 | case 'c': 310 | pw_opts.csv = 1; 311 | break; 312 | case 'p': 313 | pw_opts.pid = (int) strtoul(optarg, NULL, 10); 314 | break; 315 | case 'm': 316 | pw_opts.show_mnemonics = 1; 317 | break; 318 | #ifdef __x86_64__ 319 | case 'e': 320 | pw_opts.show_extensions = 1; 321 | break; 322 | #endif 323 | case 's': 324 | pw_opts.sample_period = strtoul(optarg, NULL, 10); 325 | break; 326 | case 'f': 327 | pw_opts.col_strs_len++; 328 | pw_opts.col_strs = (char **) realloc(pw_opts.col_strs, sizeof(char *) * 329 | pw_opts.col_strs_len); 330 | pw_opts.col_strs[pw_opts.col_strs_len - 1] = strdup(optarg); 331 | break; 332 | case 'a': 333 | pw_opts.all = 1; 334 | break; 335 | case 'l': 336 | pw_opts.list = 1; 337 | break; 338 | case 'd': 339 | pw_opts.debug = 1; 340 | break; 341 | case '?': 342 | return -1; 343 | default: 344 | return -1; 345 | } 346 | } 347 | 348 | if(pw_opts.list) { 349 | list_opt(); 350 | exit(0); 351 | } 352 | 353 | if(pw_opts.all) { 354 | 355 | /* Set the number of columns */ 356 | #ifdef __x86_64__ 357 | pw_opts.cols_len = x86_get_num_cols(); 358 | #elif __aarch64__ 359 | pw_opts.cols_len = aarch_get_num_cols(); 360 | #else 361 | fprintf(stderr, "Invalid architecture! Aborting.\n"); 362 | exit(1); 363 | #endif 364 | 365 | /* Allocate room for the columns */ 366 | pw_opts.cols = realloc(pw_opts.cols, sizeof(int) * pw_opts.cols_len); 367 | 368 | /* Loop over all categories/mnemonics/extensions and add them to 369 | the columns array */ 370 | index = 0; 371 | elem = 0; 372 | while(index < pw_opts.cols_len) { 373 | #ifdef __aarch64__ 374 | /* Capstone aarch64 groups aren't consecutive :( */ 375 | if (cs_group_name(handle, elem) == NULL) { 376 | elem++; 377 | continue; 378 | } 379 | #endif 380 | pw_opts.cols[index] = elem; 381 | index++; 382 | elem++; 383 | } 384 | 385 | } else if(pw_opts.col_strs == NULL) { 386 | /* User didn't specify -f or -a */ 387 | if(pw_opts.show_mnemonics) { 388 | pw_opts.col_strs = default_mnem_col_strs; 389 | pw_opts.col_strs_len = num_default_mnem_col_strs; 390 | #ifdef __x86_64__ 391 | } else if(pw_opts.show_extensions) { 392 | pw_opts.col_strs = default_ext_col_strs; 393 | pw_opts.col_strs_len = num_default_ext_col_strs; 394 | #endif 395 | } else { 396 | pw_opts.col_strs = default_col_strs; 397 | pw_opts.col_strs_len = num_default_col_strs; 398 | } 399 | convert_col_strs(); 400 | } else { 401 | convert_col_strs(); 402 | } 403 | 404 | 405 | return 0; 406 | } 407 | 408 | /******************************************************************************* 409 | * THREAD AND SIGNALS 410 | *******************************************************************************/ 411 | 412 | pthread_t ui_thread_id; 413 | 414 | int interval_signal; 415 | static int stopping = 0; 416 | timer_t interval_timer; 417 | double interval_target_time; 418 | struct timespec interval_start, 419 | interval_end; 420 | 421 | void ui_thread_stop(int s) { 422 | timer_delete(interval_timer); 423 | stopping = 1; 424 | } 425 | 426 | void ui_thread_interval(int s) { 427 | if(pthread_rwlock_wrlock(&results_lock) != 0) { 428 | fprintf(stderr, "Failed to grab the write lock! Aborting.\n"); 429 | exit(1); 430 | } 431 | 432 | calculate_interval_percentages(); 433 | 434 | if(pw_opts.debug) { 435 | results->interval->ringbuf_used = get_ringbuf_used(); 436 | } 437 | 438 | if(sorted_interval) { 439 | free_sorted_interval(); 440 | } 441 | 442 | /* Display the results */ 443 | if(pw_opts.csv) { 444 | print_csv_interval(stdout); 445 | } else { 446 | update_screen(&sorted_interval); 447 | } 448 | 449 | /* Clear out the events to start another interval */ 450 | clear_interval_results(); 451 | 452 | if(pthread_rwlock_unlock(&results_lock) != 0) { 453 | fprintf(stderr, "Failed to release the lock! Aborting.\n"); 454 | exit(1); 455 | } 456 | 457 | /* If the user specified a number of intervals to run */ 458 | if(results->interval_num == pw_opts.num_intervals) { 459 | ui_thread_stop(SIGTERM); 460 | } 461 | } 462 | 463 | /** 464 | init_interval_signal: Sets up the signal handler to trigger an interval. 465 | */ 466 | int init_interval_signal() { 467 | struct sigevent sev; 468 | struct itimerspec its; 469 | struct sigaction sa; 470 | sigset_t interval_mask; 471 | pid_t tid; 472 | 473 | interval_signal = SIGRTMIN; 474 | 475 | /* Set up a signal handler for the master. 476 | The call to sigaddset here blocks the stop signal until an interval is completed. */ 477 | sa.sa_flags = 0; 478 | sa.sa_handler = ui_thread_interval; 479 | sigemptyset(&sa.sa_mask); 480 | if(sigaction(interval_signal, &sa, NULL) == -1) { 481 | fprintf(stderr, "Error creating interval signal handler. Aborting.\n"); 482 | exit(1); 483 | } 484 | 485 | /* Block the interval signal */ 486 | sigemptyset(&interval_mask); 487 | sigaddset(&interval_mask, interval_signal); 488 | if(sigprocmask(SIG_SETMASK, &interval_mask, NULL) == -1) { 489 | fprintf(stderr, "Error blocking signal. Aborting.\n"); 490 | exit(1); 491 | } 492 | 493 | /* Create the interval timer */ 494 | tid = syscall(SYS_gettid); 495 | sev.sigev_notify = SIGEV_THREAD_ID; 496 | sev.sigev_signo = interval_signal; 497 | sev.sigev_value.sival_ptr = &interval_timer; 498 | sev._sigev_un._tid = tid; 499 | if(timer_create(CLOCK_REALTIME, &sev, &interval_timer) == -1) { 500 | fprintf(stderr, "Error creating timer. Aborting.\n"); 501 | exit(1); 502 | } 503 | 504 | /* Set the interval timer */ 505 | its.it_value.tv_sec = pw_opts.interval_time; 506 | its.it_value.tv_nsec = 0; 507 | its.it_interval.tv_sec = its.it_value.tv_sec; 508 | its.it_interval.tv_nsec = its.it_value.tv_nsec; 509 | if(timer_settime(interval_timer, 0, &its, NULL) == -1) { 510 | fprintf(stderr, "Error setting the timer. Aborting.\n"); 511 | exit(1); 512 | } 513 | 514 | /* Initialize the current time so that our first interval 515 | knows how long it took */ 516 | clock_gettime(CLOCK_MONOTONIC, &(interval_end)); 517 | 518 | /* Unblock the interval signal */ 519 | if(sigprocmask(SIG_UNBLOCK, &interval_mask, NULL) == -1) { 520 | fprintf(stderr, "Error unblocking signal. Aborting.\n"); 521 | exit(1); 522 | } 523 | 524 | return 0; 525 | } 526 | 527 | /** 528 | ui_thread_main: This is the function for the profiling thread. 529 | It does some setup, then triggers a profiling interval at a given 530 | rate. 531 | */ 532 | void *ui_thread_main(void *a) { 533 | int sig; 534 | sigset_t mask; 535 | 536 | init_interval_signal(); 537 | 538 | /* Wait for the user to exit */ 539 | sigemptyset(&mask); 540 | sigaddset(&mask, SIGTERM); 541 | if(sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { 542 | fprintf(stderr, "Error blocking SIGTERM. Aborting.\n"); 543 | exit(1); 544 | } 545 | while(sigwait(&mask, &sig) == 0) { 546 | if(sig == SIGTERM) { 547 | break; 548 | } 549 | } 550 | 551 | ui_thread_stop(SIGTERM); 552 | 553 | return NULL; 554 | } 555 | 556 | /** 557 | start_ui_thread: Sets up the stop signal and creates the profiling thread. 558 | */ 559 | int start_ui_thread() { 560 | int retval; 561 | sigset_t mask; 562 | 563 | /* The main thread should block SIGTERM, so that all 564 | SIGTERMs go to the UI thread. */ 565 | sigemptyset(&mask); 566 | sigaddset(&mask, SIGTERM); 567 | if(sigprocmask(SIG_SETMASK, &mask, NULL) == -1) { 568 | fprintf(stderr, "Error blocking signal. Aborting.\n"); 569 | exit(1); 570 | } 571 | 572 | retval = pthread_create(&ui_thread_id, NULL, &ui_thread_main, NULL); 573 | if(retval != 0) { 574 | fprintf(stderr, "Failed to call pthread_create. Something is very wrong. Aborting.\n"); 575 | return -1; 576 | } 577 | 578 | return 0; 579 | } 580 | 581 | /******************************************************************************* 582 | * MAIN 583 | *******************************************************************************/ 584 | 585 | int main(int argc, char **argv) { 586 | int retval; 587 | 588 | #ifdef __aarch64__ 589 | enum cs_err cap_err; 590 | /* Initialise Capstone, which we use to disassemble the instruction */ 591 | cap_err = cs_open(CS_ARCH_AARCH64, CS_MODE_ARM, &handle); 592 | if (cap_err != CS_ERR_OK) { 593 | fprintf(stderr, "Failed to initialise Capstone! Aborting.\n"); 594 | exit(1); 595 | } 596 | cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON); 597 | cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON); 598 | #endif 599 | 600 | /* Read options */ 601 | retval = read_opts(argc, argv); 602 | if(retval != 0) { 603 | return 1; 604 | } 605 | 606 | /* Open perf events and start gathering */ 607 | bpf_info = calloc(1, sizeof(bpf_info_t)); 608 | if(!bpf_info) { 609 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 610 | exit(1); 611 | } 612 | if(program_events(pw_opts.pid) == -1) { 613 | retval = 1; 614 | goto cleanup; 615 | } 616 | 617 | /* Initialize the results struct. */ 618 | init_results(); 619 | 620 | /* Initialize the UI */ 621 | if(pw_opts.csv) { 622 | print_csv_header(stdout); 623 | } 624 | 625 | /* Start the ui thread, which will collect results 626 | and, for each interval, render/print the UI. */ 627 | retval = start_ui_thread(); 628 | if(retval != 0) { 629 | retval = 1; 630 | goto cleanup; 631 | } 632 | 633 | /* Poll for some new samples */ 634 | #ifdef INSNPROF_LEGACY_PERF_BUFFER 635 | int err; 636 | while(stopping == 0) { 637 | err = perf_buffer__poll(bpf_info->pb, 100); 638 | if(err < 0) { 639 | fprintf(stderr, "Failed to poll perf buffer: %d\n", err); 640 | break; 641 | } 642 | } 643 | #else 644 | struct timespec time; 645 | time.tv_sec = 0; 646 | time.tv_nsec = 100000000; 647 | while(stopping == 0) { 648 | ring_buffer__consume(bpf_info->rb); 649 | nanosleep(&time, NULL); 650 | } 651 | #endif 652 | 653 | /* Send the stop signal to the profiling thread, 654 | then wait for it to close successfully. */ 655 | pthread_kill(ui_thread_id, SIGTERM); 656 | pthread_join(ui_thread_id, NULL); 657 | 658 | cleanup: 659 | deinit_bpf_info(); 660 | deinit_results(); 661 | free_opts(); 662 | return retval; 663 | } 664 | -------------------------------------------------------------------------------- /src/processwatch.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #ifdef __x86_64__ 10 | #include 11 | #elif __aarch64__ 12 | /* Include Capstone, then libbpf. 13 | This fixes a double-define conflict with the bpf_insn identifier. */ 14 | #include 15 | #endif 16 | 17 | #ifdef __aarch64__ 18 | #define bpf_insn cs_bpf_insn 19 | #endif 20 | 21 | #include 22 | #include 23 | 24 | #ifdef __aarch64__ 25 | #undef cs_bpf_insn 26 | csh handle; 27 | #endif 28 | 29 | /* Maximums */ 30 | #ifdef __x86_64__ 31 | #define MNEMONIC_MAX_VALUE ZYDIS_MNEMONIC_MAX_VALUE 32 | #define CATEGORY_MAX_VALUE ZYDIS_CATEGORY_MAX_VALUE 33 | #define EXTENSION_MAX_VALUE ZYDIS_ISA_EXT_MAX_VALUE 34 | #elif __aarch64__ 35 | #define MNEMONIC_MAX_VALUE (AArch64_INS_ALIAS_END-1) 36 | #define CATEGORY_MAX_VALUE (AArch64_GRP_ENDING-1) 37 | #endif 38 | 39 | /** 40 | pw_opts_t 41 | ** 42 | Stores preprocessed command-line options. 43 | */ 44 | struct pw_opts_t { 45 | char csv; 46 | unsigned int interval_time, num_intervals; 47 | int pid; 48 | unsigned char show_mnemonics : 1; 49 | unsigned char show_extensions : 1; 50 | unsigned int sample_period; 51 | 52 | char *btf_custom_path; 53 | 54 | char **col_strs; 55 | int col_strs_len; 56 | int *cols; 57 | int cols_len; 58 | 59 | char list; 60 | char debug; 61 | char all; 62 | }; 63 | 64 | /** 65 | bpf_info_t 66 | ** 67 | Stores information needed to attach to the BPF program. 68 | Also includes the ring_buffer, which we'll use to collect 69 | data from the BPF program. 70 | **/ 71 | typedef struct { 72 | struct insn_bpf *obj; 73 | 74 | /* The BPF programs and links */ 75 | struct bpf_program **prog; 76 | struct bpf_map **map; 77 | struct bpf_link **links; 78 | size_t num_links; 79 | int nr_cpus; 80 | char pmu_name[32]; 81 | 82 | struct ring_buffer *rb; 83 | struct perf_buffer *pb; 84 | 85 | } bpf_info_t; 86 | 87 | 88 | /** 89 | process_t 90 | ** 91 | Stores information about a process. We store pointers to 92 | these in the `process_info` array: one for each 93 | process that we've seen. 94 | **/ 95 | typedef struct { 96 | int index; 97 | char *name; 98 | uint32_t name_hash; 99 | } process_t; 100 | 101 | #define MAX_PROCESSES 4194304 102 | 103 | /** 104 | process_arr_t 105 | ** 106 | This is an array of lists of processes. 107 | The first dimension is keyed on the PID of the process, 108 | and the value is a pointer to an array of process_t pointers. 109 | This array is NULL-terminated. 110 | ** 111 | The header process_info.h initializes, grows, and accesses this array. 112 | **/ 113 | typedef struct { 114 | process_t **arr[MAX_PROCESSES]; 115 | int max_pid; 116 | } process_arr_t; 117 | 118 | 119 | /** 120 | interval_results_t 121 | ** 122 | Stores all profiling information. Most of this is cleared 123 | each interval. We categorize each instruction that we see, 124 | and each array has one value per instruction category. 125 | We also have per-process arrays, which dynamically grow 126 | as we see more processes. Gets updated by `results.h`. 127 | **/ 128 | typedef struct { 129 | /* 130 | TOTALS 131 | insn = instruction (mnemonic) 132 | cat = category 133 | ext = extension 134 | */ 135 | 136 | uint64_t cat_count[CATEGORY_MAX_VALUE+1]; 137 | uint64_t insn_count[MNEMONIC_MAX_VALUE+1]; 138 | double cat_percent[CATEGORY_MAX_VALUE+1]; 139 | double insn_percent[MNEMONIC_MAX_VALUE+1]; 140 | double failed_percent; 141 | 142 | /* 143 | PER PROCESS 144 | insn = instruction (mnemonic) 145 | cat = category 146 | ext = extension 147 | proc = process 148 | */ 149 | uint64_t *proc_cat_count[CATEGORY_MAX_VALUE+1]; 150 | uint64_t *proc_insn_count[MNEMONIC_MAX_VALUE+1]; 151 | double *proc_cat_percent[CATEGORY_MAX_VALUE+1]; 152 | double *proc_insn_percent[MNEMONIC_MAX_VALUE+1]; 153 | double *proc_percent; 154 | double *proc_failed_percent; 155 | 156 | /* Only for x86, also include ISA extensions */ 157 | #ifdef __x86_64__ 158 | uint64_t ext_count[EXTENSION_MAX_VALUE+1]; 159 | double ext_percent[EXTENSION_MAX_VALUE+1]; 160 | uint64_t *proc_ext_count[EXTENSION_MAX_VALUE+1]; 161 | double *proc_ext_percent[EXTENSION_MAX_VALUE+1]; 162 | #endif 163 | 164 | /* Per-interval counts */ 165 | uint64_t num_samples; 166 | uint64_t num_failed; 167 | 168 | /* Per-interval per-process counts */ 169 | uint64_t *proc_num_samples; 170 | uint64_t *proc_num_failed; 171 | 172 | /* Keep track of PIDs */ 173 | int proc_arr_size; 174 | int pid_ctr; 175 | uint32_t *pids; 176 | 177 | /* Ringbuffer stats */ 178 | double ringbuf_used; 179 | } interval_results_t; 180 | 181 | 182 | /** 183 | results_t 184 | ** 185 | Stores overall profiling information. Includes an interval_results_t, 186 | which is cleared each interval. 187 | **/ 188 | typedef struct { 189 | /* Bookkeeping */ 190 | int pid_ctr; 191 | uint64_t interval_num; 192 | uint64_t num_samples; 193 | uint64_t num_failed; 194 | double failed_percent; 195 | 196 | process_arr_t process_info; 197 | 198 | #ifdef __x86_64__ 199 | ZydisDecoder decoder; 200 | ZydisFormatter formatter; 201 | ZydisDecodedInstruction decoded_insn; 202 | #endif 203 | 204 | /* The interval */ 205 | interval_results_t *interval; 206 | } results_t; 207 | 208 | /* Need these globals outside of insnprof.c */ 209 | extern results_t *results; 210 | extern bpf_info_t *bpf_info; 211 | extern pthread_rwlock_t results_lock; 212 | extern struct pw_opts_t pw_opts; 213 | 214 | /* Reading from BPF and storing the results */ 215 | #include "results.h" 216 | #include "kerninfo.h" 217 | #include "setup_bpf.h" 218 | #include "process_info.h" 219 | 220 | /* The UI */ 221 | #include "ui/utils.h" 222 | #include "ui/interactive.h" 223 | #include "ui/csv.h" 224 | -------------------------------------------------------------------------------- /src/results.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #include 7 | #include "process_info.h" 8 | #include "bpf/insn/insn.h" 9 | 10 | /* Only the function signature differs between the perf_buffer and ringbuffer versions */ 11 | #ifdef INSNPROF_LEGACY_PERF_BUFFER 12 | static void handle_sample(void *ctx, int cpu, void *data, unsigned int data_sz) { 13 | #else 14 | static int handle_sample(void *ctx, void *data, size_t data_sz) { 15 | #endif 16 | 17 | struct insn_info *insn_info; 18 | int category, mnemonic, success; 19 | int interval_index; 20 | #ifdef __x86_64__ 21 | int extension; 22 | #endif 23 | uint32_t hash; 24 | 25 | insn_info = data; 26 | success = 0; 27 | 28 | category = -1; 29 | mnemonic = -1; 30 | #ifdef __x86_64__ 31 | extension = -1; 32 | #endif 33 | 34 | #ifdef __x86_64__ 35 | ZyanStatus status; 36 | status = ZydisDecoderDecodeInstruction(&results->decoder, 37 | ZYAN_NULL, 38 | insn_info->insn, 15, 39 | &results->decoded_insn); 40 | if(ZYAN_SUCCESS(status)) { 41 | success = 1; 42 | mnemonic = results->decoded_insn.mnemonic; 43 | category = results->decoded_insn.meta.category; 44 | #ifdef __x86_64__ 45 | extension = results->decoded_insn.meta.isa_ext; 46 | #endif 47 | } 48 | #elif __aarch64__ 49 | int count; 50 | cs_insn *insn; 51 | count = cs_disasm(handle, insn_info->insn, 4, 0, 0, &insn); 52 | if(count && insn[0].detail) { 53 | success = 1; 54 | mnemonic = insn[0].id; 55 | } 56 | #endif 57 | 58 | if(pthread_rwlock_wrlock(&results_lock) != 0) { 59 | fprintf(stderr, "Failed to grab write lock! Aborting.\n"); 60 | exit(1); 61 | } 62 | 63 | hash = djb2(insn_info->name); 64 | update_process_info(insn_info->pid, insn_info->name, hash); 65 | 66 | /* Store this result in the per-process array */ 67 | interval_index = get_interval_proc_arr_index(insn_info->pid); 68 | 69 | if(success) { 70 | results->interval->insn_count[mnemonic]++; 71 | results->interval->proc_insn_count[mnemonic][interval_index]++; 72 | 73 | #ifdef __x86_64__ 74 | results->interval->cat_count[results->decoded_insn.meta.category]++; 75 | results->interval->proc_cat_count[category][interval_index]++; 76 | results->interval->ext_count[results->decoded_insn.meta.isa_ext]++; 77 | results->interval->proc_ext_count[extension][interval_index]++; 78 | #elif __aarch64__ 79 | int i; 80 | // Capstone (LLVM) puts some instructions in 0, 1 or more groups 81 | for (i = 0; i < insn[0].detail->groups_count; i++) { 82 | category = insn[0].detail->groups[i]; 83 | results->interval->cat_count[category]++; 84 | results->interval->proc_cat_count[category][interval_index]++; 85 | } 86 | cs_free(insn, count); 87 | #endif 88 | 89 | } else { 90 | results->interval->num_failed++; 91 | results->interval->proc_num_failed[interval_index]++; 92 | results->num_failed++; 93 | } 94 | 95 | results->interval->num_samples++; 96 | results->interval->proc_num_samples[interval_index]++; 97 | results->interval->pids[interval_index] = insn_info->pid; 98 | results->num_samples++; 99 | 100 | if(pthread_rwlock_unlock(&results_lock) != 0) { 101 | fprintf(stderr, "Failed to unlock the lock! Aborting.\n"); 102 | exit(1); 103 | } 104 | 105 | #ifndef INSNPROF_LEGACY_PERF_BUFFER 106 | return 0; 107 | #endif 108 | } 109 | 110 | static void init_results() { 111 | results = calloc(1, sizeof(results_t)); 112 | if(!results) { 113 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 114 | exit(1); 115 | } 116 | results->interval = calloc(1, sizeof(interval_results_t)); 117 | 118 | #ifdef __x86_64__ 119 | ZydisDecoderInit(&results->decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); 120 | ZydisFormatterInit(&results->formatter, ZYDIS_FORMATTER_STYLE_INTEL); 121 | #endif 122 | 123 | /* Grow the per-process arrays to the first size class */ 124 | grow_interval_proc_arrs(); 125 | } 126 | 127 | static int clear_interval_results() { 128 | int i; 129 | 130 | memset(results->interval->proc_num_samples, 0, results->interval->proc_arr_size * sizeof(uint64_t)); 131 | memset(results->interval->proc_num_failed, 0, results->interval->proc_arr_size * sizeof(uint64_t)); 132 | memset(results->interval->pids, 0, results->interval->proc_arr_size * sizeof(uint32_t)); 133 | results->interval->num_samples = 0; 134 | results->interval->num_failed = 0; 135 | 136 | for(i = 0; i <= CATEGORY_MAX_VALUE; i++) { 137 | memset(results->interval->proc_cat_count[i], 0, results->interval->proc_arr_size * sizeof(uint64_t)); 138 | } 139 | for(i = 0; i <= MNEMONIC_MAX_VALUE; i++) { 140 | memset(results->interval->proc_insn_count[i], 0, results->interval->proc_arr_size * sizeof(uint64_t)); 141 | } 142 | 143 | /* Per-category or per-instruction arrays */ 144 | memset(results->interval->cat_count, 0, (CATEGORY_MAX_VALUE + 1) * sizeof(uint64_t)); 145 | memset(results->interval->insn_count, 0, (MNEMONIC_MAX_VALUE + 1) * sizeof(uint64_t)); 146 | 147 | #ifdef __x86_64__ 148 | for(i = 0; i <= EXTENSION_MAX_VALUE; i++) { 149 | memset(results->interval->proc_ext_count[i], 0, results->interval->proc_arr_size * sizeof(uint64_t)); 150 | } 151 | memset(results->interval->ext_count, 0, (EXTENSION_MAX_VALUE + 1) * sizeof(uint64_t)); 152 | #endif 153 | 154 | results->interval->pid_ctr = 0; 155 | results->interval_num++; 156 | 157 | return 0; 158 | } 159 | 160 | static void deinit_results() { 161 | int i; 162 | process_t **proc_arr; 163 | 164 | if(!results) { 165 | return; 166 | } 167 | 168 | for(i = 0; i <= results->process_info.max_pid; i++) { 169 | proc_arr = results->process_info.arr[i]; 170 | if(!proc_arr) continue; 171 | while(*proc_arr) { 172 | free((*proc_arr)->name); 173 | free(*proc_arr); 174 | proc_arr++; 175 | } 176 | free(results->process_info.arr[i]); 177 | } 178 | 179 | free(results->interval->pids); 180 | free(results->interval->proc_num_samples); 181 | free(results->interval->proc_num_failed); 182 | free(results->interval->proc_percent); 183 | free(results->interval->proc_failed_percent); 184 | for(i = 0; i <= CATEGORY_MAX_VALUE; i++) { 185 | free(results->interval->proc_cat_count[i]); 186 | free(results->interval->proc_cat_percent[i]); 187 | } 188 | for(i = 0; i <= MNEMONIC_MAX_VALUE; i++) { 189 | free(results->interval->proc_insn_count[i]); 190 | free(results->interval->proc_insn_percent[i]); 191 | } 192 | #ifdef __x86_64__ 193 | for(i = 0; i <= EXTENSION_MAX_VALUE; i++) { 194 | free(results->interval->proc_ext_count[i]); 195 | free(results->interval->proc_ext_percent[i]); 196 | } 197 | #endif 198 | free(results->interval); 199 | free(results); 200 | } 201 | 202 | static double get_ringbuf_used() { 203 | uint64_t size, avail; 204 | 205 | avail = ring__avail_data_size(ring_buffer__ring(bpf_info->rb, 0)); 206 | size = ring__size(ring_buffer__ring(bpf_info->rb, 0)); 207 | return ((double) avail) / size; 208 | } 209 | -------------------------------------------------------------------------------- /src/setup_bpf.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "bpf/insn/insn.h" 16 | #include "bpf/insn/insn.skel.h" 17 | 18 | /******************************************************************************* 19 | * PERF_EVENT_OPEN WRAPPER 20 | *******************************************************************************/ 21 | 22 | /** 23 | Loose wrapper around perf_event_open. Opens a perf_event_attr 24 | on each CPU, for all processes, and then attaches that event 25 | to the given BPF program and link. 26 | **/ 27 | static int open_and_attach_perf_event(struct perf_event_attr *attr, int cpu, int pid, int group_fd) { 28 | int fd; 29 | 30 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, 0); 31 | if(fd < 0) { 32 | /* Ignore CPU that is offline */ 33 | if(errno == ENODEV) { 34 | return -2; 35 | } 36 | fprintf(stderr, "Failed to initialize perf sampling: %s\n", 37 | strerror(errno)); 38 | return -1; 39 | } 40 | 41 | /* Add a link to the array. Note that bpf_info->prog gets incremented 42 | per perf event. */ 43 | bpf_info->num_links++; 44 | bpf_info->links = realloc(bpf_info->links, sizeof(struct bpf_link *) * bpf_info->num_links); 45 | if(!bpf_info->links) { 46 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 47 | exit(1); 48 | } 49 | bpf_info->links[bpf_info->num_links - 1] = bpf_program__attach_perf_event(*(bpf_info->prog), fd); 50 | if(libbpf_get_error(bpf_info->links[bpf_info->num_links - 1])) { 51 | fprintf(stderr, "failed to attach perf event on cpu: " 52 | "%d\n", cpu); 53 | /* Set this pointer to NULL, since it's undefined what it will be */ 54 | bpf_info->links[bpf_info->num_links - 1] = NULL; 55 | close(fd); 56 | return -1; 57 | } 58 | 59 | return fd; 60 | } 61 | 62 | /** 63 | single_insn_event - Handles a single CPU, PMU, socket event. 64 | Returns: 65 | >0 if successful. 66 | -1 if there was an issue with perf. 67 | -2 if the CPU was offline. 68 | **/ 69 | static int single_insn_event(int cpu, int pid) { 70 | int retval; 71 | 72 | /* Architecture-independent settings */ 73 | struct perf_event_attr attr = { 74 | .sample_period = pw_opts.sample_period, 75 | .sample_type = PERF_SAMPLE_IDENTIFIER, 76 | .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING, 77 | .exclude_guest = 1, 78 | .inherit = 1, 79 | .size = sizeof(struct perf_event_attr), 80 | }; 81 | 82 | #ifdef __aarch64__ 83 | attr.type = PERF_TYPE_RAW; 84 | attr.config = 0x08; 85 | #elif __x86_64__ 86 | get_pmu_string(bpf_info->pmu_name); 87 | /* Program INST_RETIRED.ANY (or equivalent) depending on PMU version */ 88 | if(strncmp(bpf_info->pmu_name, "skylake", 7) == 0) { 89 | attr.type = PERF_TYPE_RAW; 90 | attr.config = 0x00c0; 91 | } else if(strncmp(bpf_info->pmu_name, "icelake", 7) == 0) { 92 | attr.type = PERF_TYPE_RAW; 93 | attr.config = 0x00c0; 94 | } else if(strncmp(bpf_info->pmu_name, "sapphire_rapids", 7) == 0) { 95 | attr.type = PERF_TYPE_RAW; 96 | attr.config = 0x00c0; 97 | } else if(strncmp(bpf_info->pmu_name, "ibs_op", 6) == 0) { 98 | attr.type = get_ibs_op_type(); 99 | if (attr.type < 0) 100 | return -1; 101 | attr.config = 0x80000; 102 | attr.exclude_guest = 0; 103 | } else { 104 | attr.type = PERF_TYPE_SOFTWARE; 105 | attr.config = PERF_COUNT_SW_CPU_CLOCK; 106 | } 107 | #endif 108 | 109 | /* Attach the event, and handle the BPF linkages. */ 110 | retval = open_and_attach_perf_event(&attr, cpu, pid, -1); 111 | if(retval == -1) { 112 | fprintf(stderr, "Failed to open perf event.\n"); 113 | return -1; 114 | } else if(retval == -2) { 115 | fprintf(stderr, "WARNING: CPU %d is offline.\n", cpu); 116 | return -2; 117 | } 118 | 119 | return retval; 120 | } 121 | 122 | static int init_insn_bpf_info() { 123 | int err; 124 | struct bpf_object_open_opts opts = {0}; 125 | 126 | opts.sz = sizeof(struct bpf_object_open_opts); 127 | if(pw_opts.btf_custom_path) { 128 | opts.btf_custom_path = pw_opts.btf_custom_path; 129 | } 130 | 131 | bpf_info->obj = insn_bpf__open_opts(&opts); 132 | if(!bpf_info->obj) { 133 | fprintf(stderr, "ERROR: Failed to get BPF object.\n"); 134 | fprintf(stderr, " Most likely, one of two things are true:\n"); 135 | fprintf(stderr, " 1. You're not root.\n"); 136 | fprintf(stderr, " 2. You don't have a kernel that supports BTF type information.\n"); 137 | return -1; 138 | } 139 | err = insn_bpf__load(bpf_info->obj); 140 | if(err) { 141 | fprintf(stderr, "Failed to load BPF object!\n"); 142 | return -1; 143 | } 144 | 145 | bpf_info->prog = (struct bpf_program **) &(bpf_info->obj->progs.insn_collect); 146 | bpf_info->links = NULL; 147 | bpf_info->num_links = 0; 148 | 149 | /* Construct the ringbuffer or perfbuffer */ 150 | #ifdef INSNPROF_LEGACY_PERF_BUFFER 151 | struct perf_buffer_opts pb_opts = {}; 152 | pb_opts.sz = sizeof(struct perf_buffer_opts); 153 | bpf_info->pb = perf_buffer__new(bpf_map__fd(bpf_info->obj->maps.pb), 154 | MAX_ENTRIES / (4096 * 1024), 155 | handle_sample, 156 | NULL, 157 | NULL, 158 | &pb_opts); 159 | if(!(bpf_info->pb)) { 160 | fprintf(stderr, "Failed to create a new perf buffer. You're most likely not root.\n"); 161 | return -1; 162 | } 163 | #else 164 | bpf_info->rb = ring_buffer__new(bpf_map__fd(bpf_info->obj->maps.rb), handle_sample, NULL, NULL); 165 | if(!(bpf_info->rb)) { 166 | fprintf(stderr, "Failed to create a new ring buffer. You're most likely not root.\n"); 167 | return -1; 168 | } 169 | #endif 170 | 171 | bpf_info->nr_cpus = libbpf_num_possible_cpus(); 172 | return 0; 173 | } 174 | 175 | 176 | /** 177 | deinit_bpf_info: Deinitializes and frees everything in the structure. 178 | */ 179 | static void deinit_bpf_info() { 180 | int i; 181 | 182 | if(!bpf_info) { 183 | return; 184 | } 185 | 186 | if(bpf_info->obj) { 187 | insn_bpf__destroy(bpf_info->obj); 188 | } 189 | #ifdef INSNPROF_LEGACY_PERF_BUFFER 190 | if(bpf_info->pb) { 191 | perf_buffer__free(bpf_info->pb); 192 | } 193 | #else 194 | if(bpf_info->rb) { 195 | ring_buffer__free(bpf_info->rb); 196 | } 197 | #endif 198 | 199 | if(bpf_info->links) { 200 | for(i = 0; i < bpf_info->num_links; i++) { 201 | bpf_link__destroy(bpf_info->links[i]); 202 | } 203 | free(bpf_info->links); 204 | } 205 | free(bpf_info); 206 | } 207 | 208 | static int program_events(int pid) { 209 | int retval, cpu; 210 | 211 | if(init_insn_bpf_info() == -1) { 212 | return -1; 213 | } 214 | 215 | retval = 0; 216 | if(pid == -1) { 217 | for(cpu = 0; cpu < bpf_info->nr_cpus; cpu++) { 218 | retval = single_insn_event(cpu, pid); 219 | if(retval == -2) { // cpu is offline 220 | continue; 221 | } 222 | if(retval < 0) { 223 | return -1; 224 | } 225 | } 226 | } else { 227 | retval = single_insn_event(-1, pid); 228 | if(retval < 0) { 229 | return -1; 230 | } 231 | } 232 | 233 | return retval; 234 | } 235 | -------------------------------------------------------------------------------- /src/ui/csv.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | static void print_csv_header(FILE *csv_file) { 10 | int i; 11 | 12 | if(!csv_file) return; 13 | 14 | fprintf(csv_file, "interval,pid,name,"); 15 | for(i = 0; i < pw_opts.cols_len; i++) { 16 | fprintf(csv_file, "%s", get_name(pw_opts.cols[i])); 17 | if(i != (pw_opts.cols_len - 1)) { 18 | fprintf(csv_file, ","); 19 | } 20 | } 21 | fprintf(csv_file, "\n"); 22 | } 23 | 24 | static void print_csv_interval(FILE *csv_file) { 25 | int i, n, counter; 26 | process_t *process; 27 | 28 | if(!csv_file) return; 29 | 30 | /* Print overall first */ 31 | fprintf(csv_file, "%" PRIu64 ",", results->interval_num); 32 | fprintf(csv_file, "%s,", "ALL"); 33 | fprintf(csv_file, "%s,", "ALL"); 34 | for(i = 0; i < pw_opts.cols_len; i++) { 35 | fprintf(csv_file, "%lf", get_interval_percent(pw_opts.cols[i])); 36 | if(i != (pw_opts.cols_len - 1)) { 37 | fprintf(csv_file, ","); 38 | } 39 | } 40 | fprintf(csv_file, "\n"); 41 | 42 | /* Now one line per process */ 43 | counter = 0; 44 | for(i = 0; i < results->interval->pid_ctr; i++) { 45 | process = get_interval_process_info(results->interval->pids[i]); 46 | if(!process) continue; 47 | if(!get_interval_proc_num_samples(i)) continue; 48 | counter++; 49 | fprintf(csv_file, "%" PRIu64 ",", results->interval_num); 50 | fprintf(csv_file, "%d,", results->interval->pids[i]); 51 | fprintf(csv_file, "%s,", process->name); 52 | for(n = 0; n < pw_opts.cols_len; n++) { 53 | fprintf(csv_file, "%lf", get_interval_proc_percent(process->index, pw_opts.cols[n])); 54 | if(n != (pw_opts.cols_len - 1)) { 55 | fprintf(csv_file, ","); 56 | } 57 | } 58 | fprintf(csv_file, "\n"); 59 | } 60 | if(counter) { 61 | fprintf(csv_file, "\n"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/ui/histogram.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | /****************************************** 5 | * histogram.h 6 | * This is a small library which, given data 7 | * representing a histogram, produces a two- 8 | * dimensional array of strings which 9 | * represent a little ASCII histogram. 10 | ******************************************/ 11 | 12 | typedef struct { 13 | size_t width, height; 14 | char ***str; 15 | } histogram; 16 | 17 | typedef struct { 18 | size_t width, height; 19 | char ***str; 20 | } histogram_legend; 21 | 22 | static histogram *generate_histogram() { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/ui/interactive.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | #include "utils.h" 7 | 8 | static int pid_col_width = 8; 9 | static int name_col_width = 16; 10 | static int col_width = 8; 11 | static int cols_fit = 0; 12 | static int rows_fit = 0; 13 | static int tot_screen_width = 0; 14 | static int tot_screen_height = 0; 15 | static char tmp_str[8]; 16 | 17 | /* The sorted_interval struct stores the sorted 18 | indices, PIDs, and process names of all 19 | processes that we're displaying this interval. 20 | We store this information so that, on successive 21 | intervals, we can recall the exact order in which 22 | we displayed the processes (in the case of the user 23 | pausing the interface). We regenerate this struct when 24 | we want to display a regular (non-paused) interval. */ 25 | struct sorted_interval { 26 | int *pid_indices, 27 | *pids, num_pids; 28 | char **proc_names; 29 | }; 30 | static struct sorted_interval *sorted_interval = NULL; 31 | 32 | char *truncate_uint64(uint64_t val, char *str, int size) { 33 | memset(str, 0, size); 34 | if(val < 10000) { 35 | /* Less than 10k, just print the whole number */ 36 | snprintf(str, size, "%-*.*" PRIu64, col_width, col_width, val); 37 | } else if((val >= 10000) && (val < 1000000)) { 38 | /* More than 10k and less than 1m, add "k" suffix */ 39 | snprintf(str, size, "%" PRIu64 "k", val / 1000); 40 | } else { 41 | /* More than 1m, add "m" suffix */ 42 | snprintf(str, size, "%" PRIu64 "m", val / 1000000); 43 | } 44 | return str; 45 | } 46 | 47 | void update_screen(struct sorted_interval **sortint_arg) { 48 | int i, n, index; 49 | process_t *process; 50 | char *column_name; 51 | struct sorted_interval *sortint = *sortint_arg; 52 | 53 | /* If the user passes in NULL, initialize and sort. 54 | If it's a valid pointer instead, leave it alone */ 55 | if(!sortint) { 56 | sortint = malloc(sizeof(struct sorted_interval)); 57 | if(!sortint) { 58 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 59 | exit(1); 60 | } 61 | 62 | /* Sort the PIDs and store them in 'sortint'. */ 63 | sortint->pid_indices = sort_interval_pids(&(sortint->num_pids)); 64 | sortint->pids = calloc(sortint->num_pids, sizeof(int)); 65 | if(!(sortint->pids)) { 66 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 67 | exit(1); 68 | } 69 | sortint->proc_names = calloc(sortint->num_pids, sizeof(char *)); 70 | if(!(sortint->proc_names)) { 71 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 72 | exit(1); 73 | } 74 | for(i = 0; i < sortint->num_pids; i++) { 75 | index = sortint->pid_indices[i]; 76 | process = get_interval_process_info(results->interval->pids[index]); 77 | if(!process) continue; 78 | if(!get_interval_proc_num_samples(index)) continue; 79 | sortint->pids[i] = results->interval->pids[index]; 80 | sortint->proc_names[i] = realloc(sortint->proc_names[i], 81 | sizeof(char) * (strlen(process->name) + 1)); 82 | strcpy(sortint->proc_names[i], process->name); 83 | } 84 | 85 | 86 | *sortint_arg = sortint; 87 | } 88 | 89 | /**************************************************************************** 90 | HEADER 91 | ****************************************************************************/ 92 | printf("\n"); 93 | printf("%-*s %-*s", pid_col_width, "PID", name_col_width, "NAME"); 94 | if(pw_opts.debug) { 95 | /* Only print debug stuff */ 96 | printf(" %-*.*s", col_width, col_width, "%ERROR"); 97 | printf(" %-*.*s", col_width, col_width, "%RINGBUF"); 98 | } else { 99 | /* Print chosen instruction groups */ 100 | for(i = 0; i < pw_opts.cols_len; i++) { 101 | printf(" "); 102 | column_name = (char*)get_name(pw_opts.cols[i]); 103 | if (!strncmp(column_name, "Has", 3)) column_name += 3; 104 | printf("%-*.*s", col_width, col_width, column_name); 105 | } 106 | } 107 | printf(" %-*.*s", col_width, col_width, "%TOTAL"); 108 | printf(" %-*.*s", col_width, col_width, "TOTAL"); 109 | printf("\n"); 110 | 111 | /**************************************************************************** 112 | ALL PIDS 113 | ****************************************************************************/ 114 | printf("%-*s ", pid_col_width, "ALL"); 115 | printf("%-*s", name_col_width, "ALL"); 116 | if(pw_opts.debug) { 117 | printf(" %-*.*lf", col_width, 2, get_interval_failed_percent()); 118 | printf(" %-*.*lf", col_width, 2, get_interval_ringbuf_used()); 119 | } else { 120 | for(i = 0; i < pw_opts.cols_len; i++) { 121 | printf(" "); 122 | printf("%-*.*lf", 123 | col_width, 2, /* Two digits of precision */ 124 | get_interval_percent(pw_opts.cols[i])); 125 | } 126 | } 127 | printf(" %-*.*lf", col_width, 2, 100.0); 128 | printf(" %-*.*" PRIu64, col_width, 2, get_interval_num_samples()); 129 | printf("\n"); 130 | 131 | /**************************************************************************** 132 | PER-PID 133 | ****************************************************************************/ 134 | for(i = 0; i < sortint->num_pids; i++) { 135 | printf("%-*d ", pid_col_width, sortint->pids[i]); 136 | printf("%-*.*s", name_col_width, name_col_width, sortint->proc_names[i]); 137 | if(pw_opts.debug) { 138 | printf(" %-*.*lf", col_width, 2, get_interval_proc_percent_failed(sortint->pid_indices[i])); 139 | printf(" %-*.*s", col_width, col_width, "N/A"); 140 | } else { 141 | for(n = 0; n < pw_opts.cols_len; n++) { 142 | printf(" "); 143 | printf("%-*.*lf", 144 | col_width, 2, 145 | get_interval_proc_percent(sortint->pid_indices[i], pw_opts.cols[n])); 146 | } 147 | } 148 | printf(" %-*.*lf", col_width, 2, get_interval_proc_percent_samples(sortint->pid_indices[i])); 149 | printf(" %-*.*" PRIu64, col_width, 2, get_interval_proc_num_samples(sortint->pid_indices[i])); 150 | printf("\n"); 151 | } 152 | } 153 | 154 | void free_sorted_interval() { 155 | int i; 156 | 157 | free(sorted_interval->pids); 158 | for(i = 0; i < sorted_interval->num_pids; i++) { 159 | free(sorted_interval->proc_names[i]); 160 | } 161 | free(sorted_interval->proc_names); 162 | free(sorted_interval->pid_indices); 163 | free(sorted_interval); 164 | sorted_interval = NULL; 165 | } 166 | 167 | void deinit_screen() { 168 | int i; 169 | 170 | if(sorted_interval) { 171 | free(sorted_interval->pids); 172 | for(i = 0; i < sorted_interval->num_pids; i++) { 173 | free(sorted_interval->proc_names[i]); 174 | } 175 | free(sorted_interval->proc_names); 176 | free(sorted_interval->pid_indices); 177 | free(sorted_interval); 178 | sorted_interval = NULL; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/ui/utils.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2022 Intel Corporation */ 2 | /* SPDX-License-Identifier: GPL-2.0-only */ 3 | 4 | #pragma once 5 | 6 | double get_interval_ringbuf_used() { 7 | return results->interval->ringbuf_used; 8 | } 9 | 10 | double get_interval_proc_percent_samples(int proc_index) { 11 | return results->interval->proc_percent[proc_index]; 12 | } 13 | 14 | double get_interval_proc_percent_failed(int proc_index) { 15 | return results->interval->proc_failed_percent[proc_index]; 16 | } 17 | 18 | uint64_t get_interval_proc_num_samples(int proc_index) { 19 | return results->interval->proc_num_samples[proc_index]; 20 | } 21 | 22 | uint64_t get_interval_num_samples() { 23 | return results->interval->num_samples; 24 | } 25 | 26 | const char *get_name(int index) { 27 | 28 | #ifdef __x86_64__ 29 | if(pw_opts.show_mnemonics) { 30 | return ZydisMnemonicGetString(index); 31 | #ifdef __x86_64__ 32 | } else if(pw_opts.show_extensions) { 33 | return ZydisISAExtGetString(index); 34 | #endif 35 | } else { 36 | return ZydisCategoryGetString(index); 37 | } 38 | #elif __aarch64__ 39 | if(pw_opts.show_mnemonics) { 40 | return cs_insn_name(handle, index); 41 | } else { 42 | return cs_group_name(handle, index); 43 | } 44 | #endif 45 | 46 | } 47 | 48 | double get_interval_percent(int index) { 49 | if(pw_opts.show_mnemonics) { 50 | return results->interval->insn_percent[index]; 51 | #ifdef __x86_64__ 52 | } else if(pw_opts.show_extensions) { 53 | return results->interval->ext_percent[index]; 54 | #endif 55 | } else { 56 | return results->interval->cat_percent[index]; 57 | } 58 | } 59 | 60 | double get_interval_failed_percent() { 61 | return results->interval->failed_percent; 62 | } 63 | 64 | double get_interval_proc_percent(int proc_index, int index) { 65 | if(pw_opts.show_mnemonics) { 66 | return results->interval->proc_insn_percent[index][proc_index]; 67 | #ifdef __x86_64__ 68 | } else if(pw_opts.show_extensions) { 69 | return results->interval->proc_ext_percent[index][proc_index]; 70 | #endif 71 | } else { 72 | return results->interval->proc_cat_percent[index][proc_index]; 73 | } 74 | } 75 | 76 | enum qsort_val_type { 77 | QSORT_INTERVAL_PID, 78 | QSORT_INTERVAL_CAT_COUNT, 79 | QSORT_INTERVAL_CAT_PERCENT, 80 | QSORT_INTERVAL_EXT_COUNT, 81 | QSORT_INTERVAL_EXT_PERCENT, 82 | QSORT_INTERVAL_INSN_COUNT, 83 | QSORT_INTERVAL_INSN_PERCENT, 84 | QSORT_PID, 85 | QSORT_CAT_PERCENT, 86 | QSORT_EXT_PERCENT, 87 | QSORT_INSN_PERCENT 88 | }; 89 | 90 | #define swap(a, b, t) \ 91 | t = *a; \ 92 | *a = *b; \ 93 | *b = t; 94 | 95 | #define get_value(val, val_type, set) \ 96 | switch(val_type) { \ 97 | case QSORT_INTERVAL_PID: \ 98 | set = results->interval->proc_num_samples[val]; \ 99 | break; \ 100 | case QSORT_INTERVAL_CAT_COUNT: \ 101 | set = results->interval->cat_count[val]; \ 102 | break; \ 103 | case QSORT_INTERVAL_CAT_PERCENT: \ 104 | set = results->interval->cat_percent[val]; \ 105 | break; \ 106 | case QSORT_INTERVAL_INSN_COUNT: \ 107 | set = results->interval->insn_count[val]; \ 108 | break; \ 109 | case QSORT_INTERVAL_INSN_PERCENT: \ 110 | set = results->interval->insn_percent[val]; \ 111 | break; \ 112 | default: \ 113 | fprintf(stderr, "Invalid val_type! Aborting.\n"); \ 114 | exit(1); \ 115 | break; \ 116 | } 117 | 118 | #ifdef __x86_64__ 119 | #undef get_value 120 | #define get_value(val, val_type, set) \ 121 | switch(val_type) { \ 122 | case QSORT_INTERVAL_PID: \ 123 | set = results->interval->proc_num_samples[val]; \ 124 | break; \ 125 | case QSORT_INTERVAL_CAT_COUNT: \ 126 | set = results->interval->cat_count[val]; \ 127 | break; \ 128 | case QSORT_INTERVAL_CAT_PERCENT: \ 129 | set = results->interval->cat_percent[val]; \ 130 | break; \ 131 | case QSORT_INTERVAL_EXT_COUNT: \ 132 | set = results->interval->ext_count[val]; \ 133 | break; \ 134 | case QSORT_INTERVAL_EXT_PERCENT: \ 135 | set = results->interval->ext_percent[val]; \ 136 | break; \ 137 | case QSORT_INTERVAL_INSN_COUNT: \ 138 | set = results->interval->insn_count[val]; \ 139 | break; \ 140 | case QSORT_INTERVAL_INSN_PERCENT: \ 141 | set = results->interval->insn_percent[val]; \ 142 | break; \ 143 | default: \ 144 | fprintf(stderr, "Invalid val_type! Aborting.\n"); \ 145 | exit(1); \ 146 | break; \ 147 | } 148 | #endif 149 | 150 | #define partition(vals, val_type, low, high, partition_index) \ 151 | switch(val_type) { \ 152 | case QSORT_INTERVAL_PID: \ 153 | partition_index = int_partition(vals, val_type, low, high); \ 154 | break; \ 155 | case QSORT_INTERVAL_CAT_COUNT: \ 156 | partition_index = int_partition(vals, val_type, low, high); \ 157 | break; \ 158 | case QSORT_INTERVAL_CAT_PERCENT: \ 159 | partition_index = double_partition(vals, val_type, low, high); \ 160 | break; \ 161 | case QSORT_INTERVAL_EXT_COUNT: \ 162 | partition_index = int_partition(vals, val_type, low, high); \ 163 | break; \ 164 | case QSORT_INTERVAL_EXT_PERCENT: \ 165 | partition_index = double_partition(vals, val_type, low, high); \ 166 | break; \ 167 | case QSORT_INTERVAL_INSN_COUNT: \ 168 | partition_index = int_partition(vals, val_type, low, high); \ 169 | break; \ 170 | case QSORT_INTERVAL_INSN_PERCENT: \ 171 | partition_index = double_partition(vals, val_type, low, high); \ 172 | break; \ 173 | case QSORT_PID: \ 174 | partition_index = int_partition(vals, val_type, low, high); \ 175 | break; \ 176 | case QSORT_CAT_PERCENT: \ 177 | partition_index = double_partition(vals, val_type, low, high); \ 178 | break; \ 179 | case QSORT_EXT_PERCENT: \ 180 | partition_index = double_partition(vals, val_type, low, high); \ 181 | break; \ 182 | case QSORT_INSN_PERCENT: \ 183 | partition_index = double_partition(vals, val_type, low, high); \ 184 | break; \ 185 | default: \ 186 | fprintf(stderr, "Invalid partition_index! Aborting.\n"); \ 187 | exit(1); \ 188 | break; \ 189 | } 190 | 191 | int int_partition(int *vals, int val_type, int low, int high) { 192 | int pivot, i, j, j_val, pivot_val, tmp; 193 | 194 | pivot = high; 195 | i = low - 1; 196 | get_value(vals[pivot], val_type, pivot_val); 197 | for(j = low; j < high; j++) { 198 | get_value(vals[j], val_type, j_val); 199 | if(j_val > pivot_val) { 200 | i++; 201 | swap(&vals[i], &vals[j], tmp); 202 | } 203 | } 204 | swap(&vals[i + 1], &vals[high], tmp); 205 | 206 | return i + 1; 207 | } 208 | 209 | int double_partition(int *vals, int val_type, int low, int high) { 210 | int pivot, i, j; 211 | double j_val, pivot_val, 212 | tmp; 213 | 214 | pivot = high; 215 | i = low - 1; 216 | get_value(vals[pivot], val_type, pivot_val); 217 | for(j = low; j < high; j++) { 218 | get_value(vals[j], val_type, j_val); 219 | if(j_val > pivot_val) { 220 | i++; 221 | swap(&vals[i], &vals[j], tmp); 222 | } 223 | } 224 | swap(&vals[i + 1], &vals[high], tmp); 225 | 226 | return i + 1; 227 | } 228 | 229 | void quicksort(int *vals, int val_type, int low, int high) { 230 | int partition_index; 231 | 232 | if(low >= high) return; 233 | 234 | partition(vals, val_type, low, high, partition_index); 235 | quicksort(vals, val_type, low, partition_index - 1); 236 | quicksort(vals, val_type, partition_index + 1, high); 237 | } 238 | 239 | int *sort_interval_pids(int *num_pids) { 240 | int *pids, i, num_procs; 241 | 242 | num_procs = results->interval->pid_ctr; 243 | 244 | /* Copy all the PIDs into an array, unsorted. */ 245 | pids = calloc(num_procs, sizeof(int)); 246 | if(!pids) { 247 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 248 | exit(1); 249 | } 250 | for(i = 0; i < num_procs; i++) { 251 | pids[i] = i; 252 | } 253 | 254 | quicksort(pids, QSORT_INTERVAL_PID, 0, num_procs - 1); 255 | 256 | *num_pids = num_procs; 257 | return pids; 258 | } 259 | 260 | int *sort_pids(int *num_pids) { 261 | int *pids, i, num_procs; 262 | 263 | num_procs = results->pid_ctr; 264 | 265 | /* Copy all the PIDs into an array, unsorted. */ 266 | pids = calloc(num_procs, sizeof(int)); 267 | if(!pids) { 268 | fprintf(stderr, "Failed to allocate memory! Aborting.\n"); 269 | exit(1); 270 | } 271 | for(i = 0; i < num_procs; i++) { 272 | pids[i] = i; 273 | } 274 | 275 | quicksort(pids, QSORT_PID, 0, num_procs - 1); 276 | 277 | *num_pids = num_procs; 278 | return pids; 279 | } 280 | 281 | /** 282 | calculate_interval_percentages 283 | ** 284 | For each instruction category and mnemonic, calculate: 285 | 1. Systemwide percentages. 286 | 2. Per-process percentages. 287 | **/ 288 | void calculate_interval_percentages() { 289 | int i, n; 290 | 291 | if(!(results->interval->num_samples)) { 292 | return; 293 | } 294 | 295 | results->interval->failed_percent = ((double) results->interval->num_failed) / 296 | results->interval->num_samples * 100; 297 | 298 | for(i = 0; i < results->interval->proc_arr_size; i++) { 299 | results->interval->proc_percent[i] = ((double) results->interval->proc_num_samples[i]) / 300 | results->interval->num_samples * 100; 301 | results->interval->proc_failed_percent[i] = ((double) results->interval->proc_num_failed[i]) / 302 | results->interval->num_samples * 100; 303 | } 304 | 305 | for(i = 0; i <= CATEGORY_MAX_VALUE; i++) { 306 | results->interval->cat_percent[i] = ((double) results->interval->cat_count[i]) / 307 | results->interval->num_samples * 100; 308 | for(n = 0; n < results->interval->proc_arr_size; n++) { 309 | if(!(results->interval->proc_num_samples[n])) continue; 310 | results->interval->proc_cat_percent[i][n] = ((double) results->interval->proc_cat_count[i][n]) / 311 | results->interval->proc_num_samples[n] * 100; 312 | } 313 | } 314 | 315 | for(i = 0; i <= MNEMONIC_MAX_VALUE; i++) { 316 | results->interval->insn_percent[i] = ((double) results->interval->insn_count[i]) / 317 | results->interval->num_samples * 100; 318 | for(n = 0; n < results->interval->proc_arr_size; n++) { 319 | if(!(results->interval->proc_num_samples[n])) continue; 320 | results->interval->proc_insn_percent[i][n] = ((double) results->interval->proc_insn_count[i][n]) / 321 | results->interval->proc_num_samples[n] * 100; 322 | } 323 | } 324 | 325 | #ifdef __x86_64__ 326 | for(i = 0; i <= EXTENSION_MAX_VALUE; i++) { 327 | results->interval->ext_percent[i] = ((double) results->interval->ext_count[i]) / 328 | results->interval->num_samples * 100; 329 | for(n = 0; n < results->interval->proc_arr_size; n++) { 330 | if(!(results->interval->proc_num_samples[n])) continue; 331 | results->interval->proc_ext_percent[i][n] = ((double) results->interval->proc_ext_count[i][n]) / 332 | results->interval->proc_num_samples[n] * 100; 333 | } 334 | } 335 | #endif 336 | } 337 | --------------------------------------------------------------------------------