├── .github └── workflows │ └── pre-integration.yml ├── .gitignore ├── .jcheck └── conf ├── LICENSE ├── README.md ├── images ├── sample-region-popup-screenshot.png ├── sample-screenshot-2.png ├── sample-screenshot.png └── sample-usage-screenshot.png ├── pom.xml └── src ├── license └── gpl_cpe │ └── header.txt ├── main └── java │ └── org │ └── openjdk │ └── shenandoah │ ├── CircularBuffer.java │ ├── Colors.java │ ├── DataConnector.java │ ├── DataLogProvider.java │ ├── DataProvider.java │ ├── EventLog.java │ ├── GraphPanel.java │ ├── LayoutConstants.java │ ├── LegendPanel.java │ ├── Phase.java │ ├── RegionAffiliation.java │ ├── RegionHistory.java │ ├── RegionPanel.java │ ├── RegionPopUp.java │ ├── RegionSelectionParser.java │ ├── RegionStat.java │ ├── RegionState.java │ ├── RenderRunner.java │ ├── ShenandoahVisualizer.java │ ├── Snapshot.java │ ├── StatusPanel.java │ ├── Timed.java │ └── ToolbarPanel.java └── test ├── java └── org │ └── openjdk │ └── shenandoah │ ├── CircularBufferTest.java │ ├── CounterTest.java │ ├── DecoderTest.java │ ├── EventLogTest.java │ ├── PopupSpotlightTest.java │ ├── PopupTimelineTest.java │ ├── ProcessLoggingTagTest.java │ └── RegionSelectionParserTest.java └── resources └── regions-6425.log /.github/workflows/pre-integration.yml: -------------------------------------------------------------------------------- 1 | name: Shenandoah Visualizer Pre-Integration Tests 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, ready_for_review, synchronize] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | test: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | java: [17, 20, 21-ea] 14 | os: [ubuntu-20.04, windows-2019, macos-12] 15 | fail-fast: false 16 | name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up JDK ${{ matrix.java }} 21 | uses: actions/setup-java@v3 22 | with: 23 | java-version: ${{ matrix.java }} 24 | distribution: zulu 25 | - name: Cache Maven packages 26 | uses: actions/cache@v3 27 | with: 28 | path: ~/.m2/repository 29 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml', '.github/workflows/pre-integration.yml') }} 30 | restore-keys: ${{ runner.os }}-maven 31 | - name: Run build with tests 32 | run: mvn clean install -B --file pom.xml 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | .idea/ 3 | *.iml 4 | target/ 5 | -------------------------------------------------------------------------------- /.jcheck/conf: -------------------------------------------------------------------------------- 1 | [general] 2 | project=jdk 3 | jbs=JDK 4 | 5 | [checks] 6 | error=author,committer,reviewers,executable,symlink,message,hg-tag,whitespace,problemlists 7 | 8 | [repository] 9 | tags=(?:jdk-(?:[1-9]([0-9]*)(?:\.(?:0|[1-9][0-9]*)){0,4})(?:\+(?:(?:[0-9]+))|(?:-ga)))|(?:jdk[4-9](?:u\d{1,3})?-(?:(?:b\d{2,3})|(?:ga)))|(?:hs\d\d(?:\.\d{1,2})?-b\d\d) 10 | branches= 11 | 12 | [census] 13 | version=0 14 | domain=openjdk.org 15 | 16 | [checks "whitespace"] 17 | files=.*\.cpp|.*\.hpp|.*\.c|.*\.h|.*\.java|.*\.cc|.*\.hh|.*\.m|.*\.mm 18 | 19 | [checks "reviewers"] 20 | reviewers=1 21 | ignore=duke 22 | 23 | [checks "committer"] 24 | role=committer 25 | 26 | [checks "problemlists"] 27 | dirs=test/jdk|test/langtools|test/lib-test|test/hotspot/jtreg|test/jaxp 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The GNU General Public License (GPL) 2 | 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this license 9 | document, but changing it is not allowed. 10 | 11 | Preamble 12 | 13 | The licenses for most software are designed to take away your freedom to share 14 | and change it. By contrast, the GNU General Public License is intended to 15 | guarantee your freedom to share and change free software--to make sure the 16 | software is free for all its users. This General Public License applies to 17 | most of the Free Software Foundation's software and to any other program whose 18 | authors commit to using it. (Some other Free Software Foundation software is 19 | covered by the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not price. Our 23 | General Public Licenses are designed to make sure that you have the freedom to 24 | distribute copies of free software (and charge for this service if you wish), 25 | that you receive source code or can get it if you want it, that you can change 26 | the software or use pieces of it in new free programs; and that you know you 27 | can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid anyone to deny 30 | you these rights or to ask you to surrender the rights. These restrictions 31 | translate to certain responsibilities for you if you distribute copies of the 32 | software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether gratis or for 35 | a fee, you must give the recipients all the rights that you have. You must 36 | make sure that they, too, receive or can get the source code. And you must 37 | show them these terms so they know their rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and (2) 40 | offer you this license which gives you legal permission to copy, distribute 41 | and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain that 44 | everyone understands that there is no warranty for this free software. If the 45 | software is modified by someone else and passed on, we want its recipients to 46 | know that what they have is not the original, so that any problems introduced 47 | by others will not reflect on the original authors' reputations. 48 | 49 | Finally, any free program is threatened constantly by software patents. We 50 | wish to avoid the danger that redistributors of a free program will 51 | individually obtain patent licenses, in effect making the program proprietary. 52 | To prevent this, we have made it clear that any patent must be licensed for 53 | everyone's free use or not licensed at all. 54 | 55 | The precise terms and conditions for copying, distribution and modification 56 | follow. 57 | 58 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 59 | 60 | 0. This License applies to any program or other work which contains a notice 61 | placed by the copyright holder saying it may be distributed under the terms of 62 | this General Public License. The "Program", below, refers to any such program 63 | or work, and a "work based on the Program" means either the Program or any 64 | derivative work under copyright law: that is to say, a work containing the 65 | Program or a portion of it, either verbatim or with modifications and/or 66 | translated into another language. (Hereinafter, translation is included 67 | without limitation in the term "modification".) Each licensee is addressed as 68 | "you". 69 | 70 | Activities other than copying, distribution and modification are not covered by 71 | this License; they are outside its scope. The act of running the Program is 72 | not restricted, and the output from the Program is covered only if its contents 73 | constitute a work based on the Program (independent of having been made by 74 | running the Program). Whether that is true depends on what the Program does. 75 | 76 | 1. You may copy and distribute verbatim copies of the Program's source code as 77 | you receive it, in any medium, provided that you conspicuously and 78 | appropriately publish on each copy an appropriate copyright notice and 79 | disclaimer of warranty; keep intact all the notices that refer to this License 80 | and to the absence of any warranty; and give any other recipients of the 81 | Program a copy of this License along with the Program. 82 | 83 | You may charge a fee for the physical act of transferring a copy, and you may 84 | at your option offer warranty protection in exchange for a fee. 85 | 86 | 2. You may modify your copy or copies of the Program or any portion of it, thus 87 | forming a work based on the Program, and copy and distribute such modifications 88 | or work under the terms of Section 1 above, provided that you also meet all of 89 | these conditions: 90 | 91 | a) You must cause the modified files to carry prominent notices stating 92 | that you changed the files and the date of any change. 93 | 94 | b) You must cause any work that you distribute or publish, that in whole or 95 | in part contains or is derived from the Program or any part thereof, to be 96 | licensed as a whole at no charge to all third parties under the terms of 97 | this License. 98 | 99 | c) If the modified program normally reads commands interactively when run, 100 | you must cause it, when started running for such interactive use in the 101 | most ordinary way, to print or display an announcement including an 102 | appropriate copyright notice and a notice that there is no warranty (or 103 | else, saying that you provide a warranty) and that users may redistribute 104 | the program under these conditions, and telling the user how to view a copy 105 | of this License. (Exception: if the Program itself is interactive but does 106 | not normally print such an announcement, your work based on the Program is 107 | not required to print an announcement.) 108 | 109 | These requirements apply to the modified work as a whole. If identifiable 110 | sections of that work are not derived from the Program, and can be reasonably 111 | considered independent and separate works in themselves, then this License, and 112 | its terms, do not apply to those sections when you distribute them as separate 113 | works. But when you distribute the same sections as part of a whole which is a 114 | work based on the Program, the distribution of the whole must be on the terms 115 | of this License, whose permissions for other licensees extend to the entire 116 | whole, and thus to each and every part regardless of who wrote it. 117 | 118 | Thus, it is not the intent of this section to claim rights or contest your 119 | rights to work written entirely by you; rather, the intent is to exercise the 120 | right to control the distribution of derivative or collective works based on 121 | the Program. 122 | 123 | In addition, mere aggregation of another work not based on the Program with the 124 | Program (or with a work based on the Program) on a volume of a storage or 125 | distribution medium does not bring the other work under the scope of this 126 | License. 127 | 128 | 3. You may copy and distribute the Program (or a work based on it, under 129 | Section 2) in object code or executable form under the terms of Sections 1 and 130 | 2 above provided that you also do one of the following: 131 | 132 | a) Accompany it with the complete corresponding machine-readable source 133 | code, which must be distributed under the terms of Sections 1 and 2 above 134 | on a medium customarily used for software interchange; or, 135 | 136 | b) Accompany it with a written offer, valid for at least three years, to 137 | give any third party, for a charge no more than your cost of physically 138 | performing source distribution, a complete machine-readable copy of the 139 | corresponding source code, to be distributed under the terms of Sections 1 140 | and 2 above on a medium customarily used for software interchange; or, 141 | 142 | c) Accompany it with the information you received as to the offer to 143 | distribute corresponding source code. (This alternative is allowed only 144 | for noncommercial distribution and only if you received the program in 145 | object code or executable form with such an offer, in accord with 146 | Subsection b above.) 147 | 148 | The source code for a work means the preferred form of the work for making 149 | modifications to it. For an executable work, complete source code means all 150 | the source code for all modules it contains, plus any associated interface 151 | definition files, plus the scripts used to control compilation and installation 152 | of the executable. However, as a special exception, the source code 153 | distributed need not include anything that is normally distributed (in either 154 | source or binary form) with the major components (compiler, kernel, and so on) 155 | of the operating system on which the executable runs, unless that component 156 | itself accompanies the executable. 157 | 158 | If distribution of executable or object code is made by offering access to copy 159 | from a designated place, then offering equivalent access to copy the source 160 | code from the same place counts as distribution of the source code, even though 161 | third parties are not compelled to copy the source along with the object code. 162 | 163 | 4. You may not copy, modify, sublicense, or distribute the Program except as 164 | expressly provided under this License. Any attempt otherwise to copy, modify, 165 | sublicense or distribute the Program is void, and will automatically terminate 166 | your rights under this License. However, parties who have received copies, or 167 | rights, from you under this License will not have their licenses terminated so 168 | long as such parties remain in full compliance. 169 | 170 | 5. You are not required to accept this License, since you have not signed it. 171 | However, nothing else grants you permission to modify or distribute the Program 172 | or its derivative works. These actions are prohibited by law if you do not 173 | accept this License. Therefore, by modifying or distributing the Program (or 174 | any work based on the Program), you indicate your acceptance of this License to 175 | do so, and all its terms and conditions for copying, distributing or modifying 176 | the Program or works based on it. 177 | 178 | 6. Each time you redistribute the Program (or any work based on the Program), 179 | the recipient automatically receives a license from the original licensor to 180 | copy, distribute or modify the Program subject to these terms and conditions. 181 | You may not impose any further restrictions on the recipients' exercise of the 182 | rights granted herein. You are not responsible for enforcing compliance by 183 | third parties to this License. 184 | 185 | 7. If, as a consequence of a court judgment or allegation of patent 186 | infringement or for any other reason (not limited to patent issues), conditions 187 | are imposed on you (whether by court order, agreement or otherwise) that 188 | contradict the conditions of this License, they do not excuse you from the 189 | conditions of this License. If you cannot distribute so as to satisfy 190 | simultaneously your obligations under this License and any other pertinent 191 | obligations, then as a consequence you may not distribute the Program at all. 192 | For example, if a patent license would not permit royalty-free redistribution 193 | of the Program by all those who receive copies directly or indirectly through 194 | you, then the only way you could satisfy both it and this License would be to 195 | refrain entirely from distribution of the Program. 196 | 197 | If any portion of this section is held invalid or unenforceable under any 198 | particular circumstance, the balance of the section is intended to apply and 199 | the section as a whole is intended to apply in other circumstances. 200 | 201 | It is not the purpose of this section to induce you to infringe any patents or 202 | other property right claims or to contest validity of any such claims; this 203 | section has the sole purpose of protecting the integrity of the free software 204 | distribution system, which is implemented by public license practices. Many 205 | people have made generous contributions to the wide range of software 206 | distributed through that system in reliance on consistent application of that 207 | system; it is up to the author/donor to decide if he or she is willing to 208 | distribute software through any other system and a licensee cannot impose that 209 | choice. 210 | 211 | This section is intended to make thoroughly clear what is believed to be a 212 | consequence of the rest of this License. 213 | 214 | 8. If the distribution and/or use of the Program is restricted in certain 215 | countries either by patents or by copyrighted interfaces, the original 216 | copyright holder who places the Program under this License may add an explicit 217 | geographical distribution limitation excluding those countries, so that 218 | distribution is permitted only in or among countries not thus excluded. In 219 | such case, this License incorporates the limitation as if written in the body 220 | of this License. 221 | 222 | 9. The Free Software Foundation may publish revised and/or new versions of the 223 | General Public License from time to time. Such new versions will be similar in 224 | spirit to the present version, but may differ in detail to address new problems 225 | or concerns. 226 | 227 | Each version is given a distinguishing version number. If the Program 228 | specifies a version number of this License which applies to it and "any later 229 | version", you have the option of following the terms and conditions either of 230 | that version or of any later version published by the Free Software Foundation. 231 | If the Program does not specify a version number of this License, you may 232 | choose any version ever published by the Free Software Foundation. 233 | 234 | 10. If you wish to incorporate parts of the Program into other free programs 235 | whose distribution conditions are different, write to the author to ask for 236 | permission. For software which is copyrighted by the Free Software Foundation, 237 | write to the Free Software Foundation; we sometimes make exceptions for this. 238 | Our decision will be guided by the two goals of preserving the free status of 239 | all derivatives of our free software and of promoting the sharing and reuse of 240 | software generally. 241 | 242 | NO WARRANTY 243 | 244 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 245 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 246 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE 247 | PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 248 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 249 | FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND 250 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, 251 | YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 252 | 253 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL 254 | ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE 255 | PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 256 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 257 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 258 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 259 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 260 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 261 | 262 | END OF TERMS AND CONDITIONS 263 | 264 | How to Apply These Terms to Your New Programs 265 | 266 | If you develop a new program, and you want it to be of the greatest possible 267 | use to the public, the best way to achieve this is to make it free software 268 | which everyone can redistribute and change under these terms. 269 | 270 | To do so, attach the following notices to the program. It is safest to attach 271 | them to the start of each source file to most effectively convey the exclusion 272 | of warranty; and each file should have at least the "copyright" line and a 273 | pointer to where the full notice is found. 274 | 275 | One line to give the program's name and a brief idea of what it does. 276 | 277 | Copyright (C) 278 | 279 | This program is free software; you can redistribute it and/or modify it 280 | under the terms of the GNU General Public License as published by the Free 281 | Software Foundation; either version 2 of the License, or (at your option) 282 | any later version. 283 | 284 | This program is distributed in the hope that it will be useful, but WITHOUT 285 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 286 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 287 | more details. 288 | 289 | You should have received a copy of the GNU General Public License along 290 | with this program; if not, write to the Free Software Foundation, Inc., 291 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 292 | 293 | Also add information on how to contact you by electronic and paper mail. 294 | 295 | If the program is interactive, make it output a short notice like this when it 296 | starts in an interactive mode: 297 | 298 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 299 | with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free 300 | software, and you are welcome to redistribute it under certain conditions; 301 | type 'show c' for details. 302 | 303 | The hypothetical commands 'show w' and 'show c' should show the appropriate 304 | parts of the General Public License. Of course, the commands you use may be 305 | called something other than 'show w' and 'show c'; they could even be 306 | mouse-clicks or menu items--whatever suits your program. 307 | 308 | You should also get your employer (if you work as a programmer) or your school, 309 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 310 | is a sample; alter the names: 311 | 312 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 313 | 'Gnomovision' (which makes passes at compilers) written by James Hacker. 314 | 315 | signature of Ty Coon, 1 April 1989 316 | 317 | Ty Coon, President of Vice 318 | 319 | This General Public License does not permit incorporating your program into 320 | proprietary programs. If your program is a subroutine library, you may 321 | consider it more useful to permit linking proprietary applications with the 322 | library. If this is what you want to do, use the GNU Library General Public 323 | License instead of this License. 324 | 325 | 326 | "CLASSPATH" EXCEPTION TO THE GPL 327 | 328 | Certain source files distributed by Oracle America and/or its affiliates are 329 | subject to the following clarification and special exception to the GPL, but 330 | only where Oracle has expressly included in the particular source file's header 331 | the words "Oracle designates this particular file as subject to the "Classpath" 332 | exception as provided by Oracle in the LICENSE file that accompanied this code." 333 | 334 | Linking this library statically or dynamically with other modules is making 335 | a combined work based on this library. Thus, the terms and conditions of 336 | the GNU General Public License cover the whole combination. 337 | 338 | As a special exception, the copyright holders of this library give you 339 | permission to link this library with independent modules to produce an 340 | executable, regardless of the license terms of these independent modules, 341 | and to copy and distribute the resulting executable under terms of your 342 | choice, provided that you also meet, for each linked independent module, 343 | the terms and conditions of the license of that module. An independent 344 | module is a module which is not derived from or based on this library. If 345 | you modify this library, you may extend this exception to your version of 346 | the library, but you are not obligated to do so. If you do not wish to do 347 | so, delete this exception statement from your version. 348 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shenandoah Visualizer 2 | 3 | Shenandoah Visualizer is a low-level tool to visualize the internal state of 4 | [Shenandoah GC](https://wiki.openjdk.java.net/display/Shenandoah). It relies on 5 | jvmstat interface to pull the data from the live JVM. 6 | 7 | ![Sample Shenandoah Visualizer Screenshot](images/sample-screenshot-2.png) 8 | 9 | ## Building 10 | 11 | Build as any Maven-driven Java project: 12 | 13 | $ mvn clean verify 14 | 15 | ...or pick up the binary build [from here](https://builds.shipilev.net/shenandoah-visualizer/). 16 | 17 | ## Usage 18 | 19 | 20 | #### Realtime 21 | *Step 1.* Start target JVM with these additional flags: 22 | 23 | $ java -XX:+UseShenandoahGC -XX:+UsePerfData -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahRegionSampling ... 24 | 25 | *Step 2.* Figure out the target JVM PID: 26 | 27 | $ jps 28 | 29 | *Step 3a.* Run the Visualizer; Visualizer will attempt to detect a JVM running Shenandoah: 30 | 31 | $ java -jar visualizer.jar 32 | 33 | *Step 3b.* Optionally attach the Visualizer using the -vm flag: 34 | If using JDK8: 35 | 36 | $ java -Xbootclasspath/p: -jar visualizer.jar -vm local:// 37 | `tools.jar` can usually be found at `$JAVA_HOME/lib` 38 | 39 | If using JDK17+ 40 | ```bash 41 | $ java --add-exports jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED -jar visualizer.jar 42 | ``` 43 | Note that if you are using Intellij, you can also add these exports to the compiler (settings->javac) so that you can build and debug within the IDE. 44 | 45 | 46 | #### Saving JVM session 47 | Add this additional flag to an active JVM running Shenandoah: 48 | 49 | $ -Xlog:gc+region=debug:::filesize=,filecount= 50 | 51 | 52 | #### Replaying JVM session 53 | *Step 1.* Run the Visualizer using the -logFile flag: 54 | 55 | $ java -jar visualizer.jar -logFile 56 | 57 | #### Visualizer User Interface 58 | * **Graph (Orange box on the top)**: different displacements for different collector phases and a new timescale 59 | * **Region (Blue box in the middle)**: generates popup window with detailed information of chosen region and historical timeline of the states for that region 60 | * **Toolbar (Pink box in the bottom)**: buttons and slider to control the timeline and speed for the replay mode with keyboard shortcut 61 | * `space bar`: play/pause 62 | * `up`: `+5` snapshots 63 | * `down`: `-5` snapshots 64 | * `left`: `-1` snapshot 65 | * `right`: `+1` snapshot 66 | * `enter`: reaches the last snapshot of the replay 67 | * **Summary view (Red box on the right)**: provides the total number of regions in each state 68 | 69 | ![Sample Shenandoah Visualizer Screenshot](images/sample-usage-screenshot.png) 70 | 71 | Sample region popup window view 72 | 73 | -------------------------------------------------------------------------------- /images/sample-region-popup-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openjdk/shenandoah-visualizer/b268e150b74cbf3fed7dba5094e69d1b9de30145/images/sample-region-popup-screenshot.png -------------------------------------------------------------------------------- /images/sample-screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openjdk/shenandoah-visualizer/b268e150b74cbf3fed7dba5094e69d1b9de30145/images/sample-screenshot-2.png -------------------------------------------------------------------------------- /images/sample-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openjdk/shenandoah-visualizer/b268e150b74cbf3fed7dba5094e69d1b9de30145/images/sample-screenshot.png -------------------------------------------------------------------------------- /images/sample-usage-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openjdk/shenandoah-visualizer/b268e150b74cbf3fed7dba5094e69d1b9de30145/images/sample-usage-screenshot.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.openjdk.shenandoah 6 | visualizer 7 | 1.0 8 | jar 9 | 10 | Shenandoah Visualizer 11 | 12 | 13 | 3.0 14 | 15 | 16 | 17 | UTF-8 18 | 17 19 | visualizer 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-compiler-plugin 27 | 3.8.1 28 | 29 | ${javac.target} 30 | ${javac.target} 31 | ${javac.target} 32 | 33 | --add-exports 34 | jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-surefire-plugin 41 | 42 | --add-exports jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED 43 | 44 | 45 | 46 | com.mycila.maven-license-plugin 47 | maven-license-plugin 48 | 1.10.b1 49 | 50 | 51 | 52 | format 53 | 54 | process-sources 55 | 56 |
file:///${project.basedir}/src/license/gpl_cpe/header.txt
57 | true 58 | true 59 | 60 | PHP 61 | 62 |
63 |
64 |
65 |
66 | 67 | org.apache.maven.plugins 68 | maven-shade-plugin 69 | 2.2 70 | 71 | 72 | package 73 | 74 | shade 75 | 76 | 77 | ${uberjar.name} 78 | 79 | 81 | org.openjdk.shenandoah.ShenandoahVisualizer 82 | 83 | 84 | 85 | 86 | 87 | 88 |
89 |
90 | 91 | 92 | 93 | org.hdrhistogram 94 | HdrHistogram 95 | 2.1.11 96 | 97 | 98 | junit 99 | junit 100 | 4.12 101 | test 102 | 103 | 104 | 105 |
106 | -------------------------------------------------------------------------------- /src/license/gpl_cpe/header.txt: -------------------------------------------------------------------------------- 1 | ==== 2 | Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | 5 | This code is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License version 2 only, as 7 | published by the Free Software Foundation. Oracle designates this 8 | particular file as subject to the "Classpath" exception as provided 9 | by Oracle in the LICENSE file that accompanied this code. 10 | 11 | This code is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | version 2 for more details (a copy is included in the LICENSE file that 15 | accompanied this code). 16 | 17 | You should have received a copy of the GNU General Public License version 18 | 2 along with this work; if not, write to the Free Software Foundation, 19 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | or visit www.oracle.com if you need additional information or have any 23 | questions. 24 | ==== 25 | 26 | Copyright (c) 2023, Red Hat, Inc. All rights reserved. 27 | DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 28 | 29 | This code is free software; you can redistribute it and/or modify it 30 | under the terms of the GNU General Public License version 2 only, as 31 | published by the Free Software Foundation. Oracle designates this 32 | particular file as subject to the "Classpath" exception as provided 33 | by Oracle in the LICENSE file that accompanied this code. 34 | 35 | This code is distributed in the hope that it will be useful, but WITHOUT 36 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 37 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 38 | version 2 for more details (a copy is included in the LICENSE file that 39 | accompanied this code). 40 | 41 | You should have received a copy of the GNU General Public License version 42 | 2 along with this work; if not, write to the Free Software Foundation, 43 | Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 44 | 45 | Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 46 | or visit www.oracle.com if you need additional information or have any 47 | questions. 48 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/CircularBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import java.util.*; 28 | 29 | class CircularBuffer { 30 | 31 | static final int DEFAULT_SIZE = 8; 32 | 33 | private final Object[] elements; 34 | private int tail; 35 | private int count; 36 | 37 | CircularBuffer(int size) { 38 | elements = new Object[size]; 39 | count = tail = 0; 40 | } 41 | 42 | CircularBuffer(Collection elements) { 43 | this.elements = elements.toArray(); 44 | tail = 0; 45 | count = this.elements.length; 46 | } 47 | 48 | CircularBuffer() { 49 | this(DEFAULT_SIZE); 50 | } 51 | 52 | boolean isEmpty() { 53 | return count == 0; 54 | } 55 | 56 | void add(T i) { 57 | elements[tail] = i; 58 | tail = (tail + 1) % elements.length; 59 | ++count; 60 | } 61 | 62 | List subList(int include, int exclude) { 63 | if (include == exclude) { 64 | return Collections.emptyList(); 65 | } 66 | 67 | List list = new ArrayList<>(exclude - include); 68 | for (; include < exclude; ++include) { 69 | list.add(get(include)); 70 | } 71 | return list; 72 | } 73 | 74 | T get(int elementAt) { 75 | return (T) elements[index(elementAt)]; 76 | } 77 | 78 | int size() { 79 | return Math.min(count, elements.length); 80 | } 81 | 82 | private int index(int offset) { 83 | if (count <= elements.length) { 84 | return offset; 85 | } 86 | return ((tail + offset) % elements.length); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/Colors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import java.awt.*; 28 | 29 | class Colors { 30 | 31 | static final Color[] YOUNG = createColorFamily(Color.GREEN); 32 | static final Color[] OLD = createColorFamily(new Color(120 , 146, 210)); 33 | static final Color[] GLOBAL = createColorFamily(Color.RED); 34 | 35 | private static Color[] createColorFamily(Color base) { 36 | Color[] colors = new Color[4]; 37 | float[] hsb = Color.RGBtoHSB(base.getRed(), base.getGreen(), base.getBlue(), null); 38 | colors[0] = base; 39 | colors[1] = Color.getHSBColor(hsb[0], 0.5f, 0.5f); 40 | colors[2] = Color.getHSBColor(hsb[0], 0.4f, 0.6f); 41 | colors[3] = Color.getHSBColor(hsb[0], 0.3f, 0.7f); 42 | return colors; 43 | } 44 | 45 | static final Color TIMELINE_IDLE = Color.BLACK; 46 | 47 | static final Color DEGENERATE = Color.ORANGE; 48 | static final Color FULL = Color.RED; 49 | 50 | static final Color SHARED_ALLOC = new Color(0, 150, 250); 51 | static final Color TLAB_ALLOC = new Color(0, 200, 0); 52 | static final Color GCLAB_ALLOC = new Color(185, 0, 250); 53 | static final Color PLAB_ALLOC = new Color(111, 78, 55); 54 | 55 | static final Color USED = new Color(220, 220, 220); 56 | static final Color LIVE_REGULAR = new Color(0, 200, 0); 57 | static final Color LIVE_HUMONGOUS = new Color(250, 100, 0); 58 | static final Color LIVE_PINNED_HUMONGOUS = new Color(255, 0, 0); 59 | static final Color LIVE_CSET = new Color(250, 250, 0); 60 | static final Color LIVE_TRASH = new Color(100, 100, 100); 61 | static final Color LIVE_PINNED = new Color(252, 201, 156); 62 | static final Color LIVE_PINNED_CSET = new Color(59, 88, 124); 63 | static final Color LIVE_EMPTY = new Color(255, 255, 255); 64 | static final Color BORDER = new Color(150, 150, 150); 65 | 66 | static final Color[] AGE_COLORS = {Color.LIGHT_GRAY, Color.YELLOW, Color.ORANGE, Color.RED, Color.BLUE}; 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/DataConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 | * 6 | * This code is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License version 2 only, as 8 | * published by the Free Software Foundation. Oracle designates this 9 | * particular file as subject to the "Classpath" exception as provided 10 | * by Oracle in the LICENSE file that accompanied this code. 11 | * 12 | * This code is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | * version 2 for more details (a copy is included in the LICENSE file that 16 | * accompanied this code). 17 | * 18 | * You should have received a copy of the GNU General Public License version 19 | * 2 along with this work; if not, write to the Free Software Foundation, 20 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 | * 22 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 | * or visit www.oracle.com if you need additional information or have any 24 | * questions. 25 | */ 26 | package org.openjdk.shenandoah; 27 | 28 | import com.sun.management.GarbageCollectionNotificationInfo; 29 | import com.sun.management.GcInfo; 30 | import com.sun.tools.attach.AttachNotSupportedException; 31 | import com.sun.tools.attach.VirtualMachine; 32 | import org.HdrHistogram.Histogram; 33 | import org.HdrHistogram.Recorder; 34 | import sun.jvmstat.monitor.*; 35 | 36 | import javax.management.MBeanServerConnection; 37 | import javax.management.Notification; 38 | import javax.management.NotificationEmitter; 39 | import javax.management.openmbean.CompositeData; 40 | import javax.management.remote.JMXConnectionNotification; 41 | import javax.management.remote.JMXConnector; 42 | import javax.management.remote.JMXConnectorFactory; 43 | import javax.management.remote.JMXServiceURL; 44 | import java.io.IOException; 45 | import java.lang.management.GarbageCollectorMXBean; 46 | import java.lang.management.ManagementFactory; 47 | import java.util.concurrent.Executor; 48 | import java.util.concurrent.Executors; 49 | import java.util.function.Consumer; 50 | 51 | /** 52 | * Purpose of this class is to maintain a JMX connection to a JVM running 53 | * shenandoah region sampling. 54 | */ 55 | class DataConnector { 56 | private static final String LOCAL_CONNECTOR_ADDRESS_PROP = "com.sun.management.jmxremote.localConnectorAddress"; 57 | private static final String SHENANDOAH_PAUSES_BEAN = "java.lang:name=Shenandoah Pauses,type=GarbageCollector"; 58 | 59 | private final Recorder histogramRecorder; 60 | private final Histogram histogram; 61 | 62 | private final Consumer monitoredVmConsumer; 63 | 64 | private volatile State status; 65 | private volatile boolean shouldRun; 66 | 67 | private final Executor executor; 68 | 69 | private volatile String targetVmIdentifier; 70 | private JMXConnector jmxConnector; 71 | 72 | enum State { 73 | SEARCHING, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED 74 | } 75 | 76 | DataConnector(Consumer monitoredVmConsumer) { 77 | this.monitoredVmConsumer = monitoredVmConsumer; 78 | this.histogramRecorder = new Recorder(2); 79 | this.histogram = new Histogram(2); 80 | this.shouldRun = true; 81 | this.status = State.DISCONNECTED; 82 | this.executor = Executors.newSingleThreadExecutor(r -> { 83 | Thread t = new Thread(r); 84 | t.setDaemon(true); 85 | t.setName("JmxConnectionManager"); 86 | return t; 87 | }); 88 | } 89 | 90 | void connectTo(String id) { 91 | targetVmIdentifier = id; 92 | } 93 | 94 | private void searchForShenandoahVm() { 95 | while (shouldRun) { 96 | try { 97 | MonitoredVm vm = findShenandoahVm(); 98 | if (vm != null) { 99 | transitionTo(State.CONNECTING); 100 | MBeanServerConnection server = createServiceConnection(vm); 101 | subscribeToGarbageCollectorNotifications(server); 102 | monitoredVmConsumer.accept(vm); 103 | shouldRun = false; 104 | transitionTo(State.CONNECTED); 105 | } else { 106 | Thread.sleep(250); 107 | } 108 | } catch (InterruptedException e) { 109 | shouldRun = false; 110 | Thread.currentThread().interrupt(); 111 | } catch (Exception e) { 112 | e.printStackTrace(); 113 | } 114 | } 115 | System.out.println("Connection task completed: " + status); 116 | } 117 | 118 | boolean isConnected() { 119 | return status == State.CONNECTED; 120 | } 121 | 122 | private void transitionTo(State newState) { 123 | System.out.println("Change status to: " + newState); 124 | status = newState; 125 | } 126 | 127 | void start() { 128 | if (status == State.DISCONNECTED) { 129 | transitionTo(State.SEARCHING); 130 | shouldRun = true; 131 | executor.execute(this::searchForShenandoahVm); 132 | } 133 | } 134 | 135 | void stop() { 136 | shouldRun = false; 137 | if (status == State.CONNECTED) { 138 | executor.execute(() -> { 139 | try { 140 | transitionTo(State.DISCONNECTING); 141 | jmxConnector.close(); 142 | transitionTo(State.DISCONNECTED); 143 | } catch (IOException e) { 144 | e.printStackTrace(); 145 | throw new RuntimeException(e); 146 | } 147 | }); 148 | } 149 | } 150 | 151 | String status() { 152 | return status.toString(); 153 | } 154 | 155 | Histogram getPauseHistogram() { 156 | Histogram temp = histogramRecorder.getIntervalHistogram(); 157 | histogram.add(temp); 158 | return histogram.copy(); 159 | } 160 | 161 | private MonitoredVm findShenandoahVm() throws Exception { 162 | HostIdentifier hostId = new HostIdentifier((String)null); 163 | MonitoredHost host = MonitoredHost.getMonitoredHost(hostId); 164 | 165 | if (targetVmIdentifier != null) { 166 | try { 167 | MonitoredVm vm = host.getMonitoredVm(new VmIdentifier(targetVmIdentifier)); 168 | String jvmArgs = MonitoredVmUtil.jvmArgs(vm); 169 | if (jvmArgs.contains("ShenandoahRegionSampling")) { 170 | System.out.println("Connecting to given vm: " + targetVmIdentifier); 171 | return vm; 172 | } else { 173 | System.out.println("Given identifier for vm " + targetVmIdentifier + " does not have ShenandoahRegionSampling enabled."); 174 | } 175 | } finally { 176 | targetVmIdentifier = null; 177 | } 178 | } 179 | 180 | for (Integer vmId: host.activeVms()) { 181 | MonitoredVm vm = host.getMonitoredVm(new VmIdentifier(String.valueOf(vmId))); 182 | String jvmArgs = MonitoredVmUtil.jvmArgs(vm); 183 | if (jvmArgs.contains("ShenandoahRegionSampling")) { 184 | System.out.println("Found vm running shenandoah region sampling: " + vm); 185 | return vm; 186 | } 187 | } 188 | System.out.println("Could not find a JVM running -XX:+ShenandoahRegionSampling!"); 189 | return null; 190 | } 191 | 192 | private MBeanServerConnection createServiceConnection(MonitoredVm monitoredVm) throws AttachNotSupportedException, IOException { 193 | String localJmxAddress = getLocalJmxAddress(monitoredVm); 194 | JMXServiceURL url = new JMXServiceURL(localJmxAddress); 195 | jmxConnector = JMXConnectorFactory.connect(url); 196 | MBeanServerConnection server = jmxConnector.getMBeanServerConnection(); 197 | jmxConnector.addConnectionNotificationListener(this::handleConnectionNotification, null, server); 198 | return server; 199 | } 200 | 201 | private String getLocalJmxAddress(MonitoredVm monitoredVm) throws AttachNotSupportedException, IOException { 202 | int pid = monitoredVm.getVmIdentifier().getLocalVmId(); 203 | VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid)); 204 | String localJmxAddress = (String) vm.getAgentProperties().get(LOCAL_CONNECTOR_ADDRESS_PROP); 205 | if (localJmxAddress == null) { 206 | System.out.println("Starting management agent in vm with pid: " + pid + ", patience..."); 207 | vm.startLocalManagementAgent(); 208 | localJmxAddress = (String) vm.getAgentProperties().get(LOCAL_CONNECTOR_ADDRESS_PROP); 209 | } 210 | 211 | if (localJmxAddress == null) { 212 | throw new IllegalStateException("Could not get local Jmx address"); 213 | } 214 | 215 | return localJmxAddress; 216 | } 217 | 218 | private void handleConnectionNotification(Notification notification, Object serverConnection) { 219 | System.out.println(notification.getType() + ": " + notification.getMessage()); 220 | if (JMXConnectionNotification.CLOSED.equals(notification.getType())) { 221 | transitionTo(State.DISCONNECTED); 222 | } 223 | } 224 | 225 | private void subscribeToGarbageCollectorNotifications(MBeanServerConnection server) throws Exception { 226 | GarbageCollectorMXBean collectorBean = ManagementFactory.newPlatformMXBeanProxy(server, SHENANDOAH_PAUSES_BEAN, GarbageCollectorMXBean.class); 227 | NotificationEmitter emitter = (NotificationEmitter)collectorBean; 228 | // do NOT install the filter here, it executes on the remote server 229 | // (and that server won't have access to these classes). 230 | emitter.addNotificationListener(this::handleNotification, null, collectorBean); 231 | } 232 | 233 | private void handleNotification(Notification notification, Object context) { 234 | if (!isGarbageCollectionNotification(notification)) { 235 | return; 236 | } 237 | 238 | GarbageCollectionNotificationInfo info = asGcNotification(notification); 239 | GcInfo gcInfo = info.getGcInfo(); 240 | 241 | System.out.printf("Id=%s, Name=%s, Action=%s, Cause=%s, Duration=%s\n", 242 | gcInfo.getId(), info.getGcName(), info.getGcAction(), info.getGcCause(), gcInfo.getDuration()); 243 | 244 | histogramRecorder.recordValue(gcInfo.getDuration()); 245 | } 246 | 247 | private static boolean isGarbageCollectionNotification(Notification notification) { 248 | return GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION.equals(notification.getType()); 249 | } 250 | 251 | private GarbageCollectionNotificationInfo asGcNotification(Notification notification) { 252 | return GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/DataLogProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import java.io.BufferedReader; 28 | import java.io.FileReader; 29 | import java.io.IOException; 30 | import java.nio.file.Files; 31 | import java.nio.file.Paths; 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | import java.util.concurrent.TimeUnit; 35 | 36 | class DataLogProvider { 37 | private static final long LATEST_VERSION = 2; 38 | 39 | static void loadSnapshots(String filePath, EventLog eventLog) { 40 | if (!isValidPath(filePath)) { 41 | throw new IllegalArgumentException("Invalid file path supplied. Please try again."); 42 | } 43 | 44 | long protocolVersion = LATEST_VERSION; 45 | var events = new ArrayList(); 46 | try (BufferedReader br = new BufferedReader(new FileReader(filePath))) { 47 | // Metadata line: timestamp status numRegions regionSize 48 | String metaDataLine; 49 | while ((metaDataLine = br.readLine()) != null) { 50 | if (metaDataLine.trim().isEmpty()) continue; 51 | 52 | metaDataLine = processLoggingTag(metaDataLine); 53 | long[] metaData = processLongData(metaDataLine); 54 | if (metaData.length != 5 && metaData.length != 4) { 55 | throw new IllegalArgumentException("Metadata line has " + metaData.length + " values. Expected 4 or 5."); 56 | } else if (metaData.length > 4) { 57 | protocolVersion = metaData[4]; 58 | } 59 | 60 | String regionDataLine = br.readLine(); 61 | if (regionDataLine == null) { 62 | throw new NullPointerException("Invalid file format: Missing region data."); 63 | } 64 | regionDataLine = processLoggingTag(regionDataLine); 65 | String[] regionData = regionDataLine.trim().split(" "); 66 | 67 | long tsMilli = TimeUnit.NANOSECONDS.toMillis(metaData[0]); 68 | long regionSize = metaData[3]; 69 | int status = Math.toIntExact(metaData[1]); 70 | events.add(new Snapshot(tsMilli, regionSize, protocolVersion, processRegionStats(regionData), status, null)); 71 | } 72 | } catch (IOException e) { 73 | throw new RuntimeException(e); 74 | } 75 | eventLog.load(TimeUnit.MILLISECONDS, events); 76 | } 77 | 78 | private static boolean isValidPath(String name) { 79 | return name != null && Files.isReadable(Paths.get(name)); 80 | } 81 | 82 | static String processLoggingTag(String data) { 83 | if (data.lastIndexOf("]") != -1) { 84 | int startIndex = data.lastIndexOf("]") + 2; 85 | return data.substring(startIndex); 86 | } else { 87 | return data; 88 | } 89 | } 90 | 91 | private static long[] processLongData(String data) throws NumberFormatException { 92 | String[] dataArray = data.trim().split(" ", 5000); 93 | long[] longArray = new long[dataArray.length]; 94 | 95 | for (int i = 0; i < dataArray.length; i++) { 96 | longArray[i] = Long.parseLong(dataArray[i]); 97 | } 98 | 99 | return longArray; 100 | } 101 | 102 | private static List processRegionStats(String[] regionData) throws NumberFormatException { 103 | List stats = new ArrayList<>(regionData.length); 104 | for (String d : regionData) { 105 | stats.add(new RegionStat(Long.parseLong(d))); 106 | } 107 | return stats; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/DataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 | * 6 | * This code is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU General Public License version 2 only, as 8 | * published by the Free Software Foundation. Oracle designates this 9 | * particular file as subject to the "Classpath" exception as provided 10 | * by Oracle in the LICENSE file that accompanied this code. 11 | * 12 | * This code is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | * version 2 for more details (a copy is included in the LICENSE file that 16 | * accompanied this code). 17 | * 18 | * You should have received a copy of the GNU General Public License version 19 | * 2 along with this work; if not, write to the Free Software Foundation, 20 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 | * 22 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 | * or visit www.oracle.com if you need additional information or have any 24 | * questions. 25 | */ 26 | package org.openjdk.shenandoah; 27 | 28 | import org.HdrHistogram.Histogram; 29 | import sun.jvmstat.monitor.LongMonitor; 30 | import sun.jvmstat.monitor.MonitorException; 31 | import sun.jvmstat.monitor.MonitoredVm; 32 | 33 | import java.util.ArrayList; 34 | import java.util.Collections; 35 | import java.util.List; 36 | 37 | class DataProvider { 38 | private static final long ORIGINAL_VERSION = 1; 39 | private static final long LATEST_VERSION = 2; 40 | static final Snapshot DISCONNECTED = new Snapshot(System.currentTimeMillis(), 1024, LATEST_VERSION, Collections.emptyList(), 0, new Histogram(2)); 41 | private final DataConnector connector; 42 | 43 | private int maxRegions; 44 | private long protocolVersion; 45 | private long maxSize; 46 | private LongMonitor[] data; 47 | private LongMonitor status; 48 | 49 | private MonitoredVm vm; 50 | 51 | DataProvider() { 52 | connector = new DataConnector(this::setMonitoredVm); 53 | } 54 | 55 | private void setMonitoredVm(MonitoredVm vm) { 56 | this.vm = vm; 57 | 58 | LongMonitor max_regions_mon = getMonitor("sun.gc.shenandoah.regions.max_regions"); 59 | maxRegions = (int) max_regions_mon.longValue(); 60 | // Read in the version of the garbage collector 61 | LongMonitor protocol_version_mon = getMonitor("sun.gc.shenandoah.regions.protocol_version"); 62 | if (protocol_version_mon == null) { 63 | protocolVersion = ORIGINAL_VERSION; 64 | } else { 65 | protocolVersion = protocol_version_mon.longValue(); 66 | } 67 | 68 | LongMonitor max_size_mon = getMonitor("sun.gc.shenandoah.regions.region_size"); 69 | maxSize = max_size_mon.longValue(); 70 | status = getMonitor("sun.gc.shenandoah.regions.status"); 71 | 72 | data = new LongMonitor[maxRegions]; 73 | for (int i = 0; i < maxRegions; i++) { 74 | LongMonitor mon = getMonitor("sun.gc.shenandoah.regions.region." + i + ".data"); 75 | if (mon != null) { 76 | data[i] = mon; 77 | } else { 78 | throw new IllegalStateException("Insufficient shared memory for all region counters. " + 79 | "Try -XX:PerfDataMemorySize=512K or higher when running the monitored program."); 80 | } 81 | } 82 | } 83 | 84 | private T getMonitor(String key) { 85 | try { 86 | return (T) vm.findByName(key); 87 | } catch (MonitorException e) { 88 | throw new IllegalStateException(e); 89 | } 90 | } 91 | 92 | boolean isConnected() { 93 | return connector.isConnected(); 94 | } 95 | 96 | Snapshot snapshot() { 97 | if (!connector.isConnected()) { 98 | return null; 99 | } 100 | 101 | List stats = new ArrayList<>(); 102 | for (int c = 0; c < maxRegions; c++) { 103 | stats.add(new RegionStat(data[c].longValue())); 104 | } 105 | 106 | // Cannot use timestamp value from the dataset itself, because statistics 107 | // is not reported continuously 108 | long time = System.currentTimeMillis(); 109 | 110 | // These histograms are not thread safe so we pass a copy here. Also, if 111 | // we ever add a feature to 'replay' sessions, we'll not want these snapshots 112 | // sharing a histogram. 113 | return new Snapshot(time, maxSize, protocolVersion, stats, (int) status.longValue(), connector.getPauseHistogram()); 114 | } 115 | 116 | void stopConnector() { 117 | connector.stop(); 118 | } 119 | 120 | void startConnector() { connector.start(); } 121 | 122 | String status() { 123 | return connector.status(); 124 | } 125 | 126 | void setConnectionTarget(String vmIdentifier) { 127 | connector.connectTo(vmIdentifier); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/EventLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import java.util.Collections; 28 | import java.util.List; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | class EventLog { 32 | private CircularBuffer events; 33 | private TimeUnit eventTimeUnit; 34 | private int cursor; 35 | private long referenceTime; 36 | 37 | EventLog() { 38 | this(TimeUnit.NANOSECONDS); 39 | } 40 | 41 | EventLog(TimeUnit eventTimeUnit) { 42 | this.events = new CircularBuffer<>(); 43 | this.eventTimeUnit = eventTimeUnit; 44 | } 45 | 46 | EventLog(TimeUnit eventTimeUnit, int eventLogSize) { 47 | this.events = new CircularBuffer<>(eventLogSize); 48 | this.eventTimeUnit = eventTimeUnit; 49 | } 50 | 51 | synchronized void add(T t) { 52 | if (!events.isEmpty() && t.time() < events.get(events.size() - 1).time()) { 53 | throw new IllegalArgumentException("Events must be added in chronological order."); 54 | } 55 | 56 | events.add(t); 57 | 58 | if (referenceTime == 0) { 59 | referenceTime = t.time(); 60 | } else if (t.time() < referenceTime) { 61 | cursor++; 62 | } 63 | } 64 | 65 | synchronized List inRange() { 66 | if (events.isEmpty() || cursor == 0) { 67 | return Collections.emptyList(); 68 | } 69 | 70 | assert 1 <= cursor && cursor <= events.size(); 71 | return events.subList(0, cursor); 72 | } 73 | 74 | synchronized void stepBy(int amount) { 75 | stepTo(cursor + amount); 76 | } 77 | 78 | void stepTo(int value) { 79 | if (events.size() > 0) { 80 | cursor = clamp(value, events.size()); 81 | referenceTime = current().time(); 82 | } 83 | } 84 | 85 | synchronized void stepToEnd() { 86 | stepTo(Integer.MAX_VALUE); 87 | } 88 | 89 | synchronized void advanceTo(long pointInTime, TimeUnit timeUnit) { 90 | long eventTime = eventTimeUnit.convert(pointInTime, timeUnit); 91 | advanceTo(eventTime); 92 | } 93 | 94 | void advanceBy(long duration, TimeUnit timeUnit) { 95 | if (referenceTime > 0) { 96 | long eventTime = eventTimeUnit.convert(duration, timeUnit); 97 | long pointInTime = referenceTime + eventTime; 98 | advanceTo(pointInTime); 99 | } 100 | } 101 | 102 | private void advanceTo(long pointInTime) { 103 | for (int i = cursor; i < events.size(); ++i) { 104 | var event = events.get(i); 105 | if (pointInTime < event.time()) { 106 | break; 107 | } 108 | cursor++; 109 | } 110 | referenceTime = pointInTime; 111 | } 112 | 113 | synchronized T current() { 114 | if (cursor == 0) { 115 | return null; 116 | } 117 | return events.get(cursor - 1); 118 | } 119 | 120 | int size() { 121 | return events.size(); 122 | } 123 | 124 | int cursor() { 125 | return cursor; 126 | } 127 | 128 | private static int clamp(int val, int max) { 129 | return Math.min(Math.max(1, val), max); 130 | } 131 | 132 | final void load(TimeUnit eventTimeUnit, List events) { 133 | this.events = new CircularBuffer<>(events); 134 | this.eventTimeUnit = eventTimeUnit; 135 | this.referenceTime = this.events.get(0).time(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/GraphPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.awt.event.ComponentAdapter; 30 | import java.awt.event.ComponentEvent; 31 | import java.util.List; 32 | 33 | class GraphPanel extends JPanel { 34 | private final RenderRunner renderRunner; 35 | 36 | int graphWidth, graphHeight; 37 | 38 | GraphPanel(RenderRunner renderRunner) { 39 | this.renderRunner = renderRunner; 40 | 41 | this.addComponentListener(new ComponentAdapter() { 42 | public void componentResized(ComponentEvent ev) { 43 | graphWidth = ev.getComponent().getWidth(); 44 | graphHeight = ev.getComponent().getHeight(); 45 | } 46 | }); 47 | } 48 | 49 | @Override 50 | public void paint(Graphics g) { 51 | List snapshots = renderRunner.snapshots(); 52 | 53 | int pad = 30; 54 | int bandHeight = (graphHeight - pad) / 2; 55 | int bandWidth = graphWidth; 56 | int phaseHeight = bandHeight / 4; 57 | double stepY = 1D * bandHeight / renderRunner.snapshot().total(); 58 | 59 | int startRaw = graphHeight - bandHeight - pad; 60 | 61 | int snapshotWidth = 1; 62 | int snapshotStartX = bandWidth; 63 | 64 | int oneFourth = bandWidth / 4; 65 | int oneHalf = oneFourth * 2; 66 | int threeFourths = oneFourth * 3; 67 | 68 | int timelineMarkStartY = bandHeight + 5; 69 | int timelineMarkEndY = bandHeight + pad - 5; 70 | int timelineMarkTextOffsetY = bandHeight + 20; 71 | 72 | int phaseLabelOffsetX = bandWidth - 25; 73 | int phaseLabelOffsetY = bandHeight + pad + 20; 74 | 75 | g.setColor(Color.WHITE); 76 | g.fillRect(0, 0, bandWidth, graphHeight); 77 | 78 | g.setColor(Color.BLACK); 79 | g.fillRect(0, 0, bandWidth, bandHeight); 80 | g.fillRect(0, bandHeight + pad, bandWidth, bandHeight); 81 | 82 | for (int i = snapshots.size() - 1; i >= 0 && snapshotStartX >=0; --i) { 83 | Snapshot snapshot = snapshots.get(i); 84 | snapshotStartX -= snapshotWidth; 85 | 86 | if (snapshot.getOldPhase() == Phase.MARKING && snapshot.getGlobalPhase() == Phase.IDLE) { 87 | g.setColor(Colors.OLD[0]); 88 | g.drawRect(snapshotStartX, bandHeight + pad, snapshotWidth, phaseHeight); 89 | } 90 | 91 | if (snapshot.percentageOfOldRegionsInCollectionSet() > 0) { 92 | int height = (int) (bandHeight * snapshot.percentageOfOldRegionsInCollectionSet()); 93 | g.setColor(Colors.OLD[0]); 94 | g.drawRect(snapshotStartX, 2 * bandHeight + pad - height, snapshotWidth, height); 95 | } 96 | 97 | g.setColor(getColor(snapshot)); 98 | if (getPhase(snapshot) == Phase.MARKING) { 99 | g.drawRect(snapshotStartX, bandHeight + pad + phaseHeight, snapshotWidth, phaseHeight); 100 | } 101 | if (getPhase(snapshot) == Phase.EVACUATING) { 102 | g.drawRect(snapshotStartX, bandHeight + pad + 2 * phaseHeight, snapshotWidth, phaseHeight); 103 | } 104 | if (getPhase(snapshot) == Phase.UPDATE_REFS) { 105 | g.drawRect(snapshotStartX, bandHeight + pad + 3 * phaseHeight, snapshotWidth, phaseHeight); 106 | } 107 | 108 | if (snapshot.isFullActive()) { 109 | g.setColor(Colors.FULL); 110 | g.drawRect(snapshotStartX, bandHeight + pad, snapshotWidth, 10); 111 | } else if (snapshot.isDegenActive()) { 112 | g.setColor(Colors.DEGENERATE); 113 | g.drawRect(snapshotStartX, bandHeight + pad, snapshotWidth, 10); 114 | } 115 | 116 | // Draw these in the upper band. 117 | g.setColor(Colors.USED); 118 | g.drawRect(snapshotStartX, (int) Math.round(startRaw - snapshot.used() * stepY), 1, 1); 119 | g.setColor(Colors.LIVE_REGULAR); 120 | g.drawRect(snapshotStartX, (int) Math.round(startRaw - snapshot.live() * stepY), 1, 1); 121 | g.setColor(Colors.LIVE_CSET); 122 | g.drawRect(snapshotStartX, (int) Math.round(startRaw - snapshot.collectionSet() * stepY), 1, 1); 123 | 124 | g.setColor(Color.WHITE); 125 | g.drawString("OM", phaseLabelOffsetX, phaseLabelOffsetY); 126 | g.drawString("M", phaseLabelOffsetX, phaseLabelOffsetY + phaseHeight); 127 | g.drawString("E", phaseLabelOffsetX, phaseLabelOffsetY + 2 * phaseHeight ); 128 | g.drawString("UR", phaseLabelOffsetX, phaseLabelOffsetY + 3 * phaseHeight ); 129 | 130 | Graphics2D g2 = (Graphics2D) g; 131 | g2.setStroke(new BasicStroke(2)); 132 | 133 | if (snapshotStartX == 0) { 134 | g2.drawLine(0, timelineMarkStartY, 0, timelineMarkEndY); 135 | g2.drawString(snapshot.time() + " ms", 0, timelineMarkTextOffsetY); 136 | } else if (oneFourth == snapshotStartX) { 137 | g2.drawLine(oneFourth, timelineMarkStartY, oneFourth, timelineMarkEndY); 138 | g2.drawString(snapshot.time() + " ms", oneFourth + 3, timelineMarkTextOffsetY); 139 | } else if (oneHalf == snapshotStartX) { 140 | g2.drawLine(oneHalf, timelineMarkStartY, oneHalf, timelineMarkEndY); 141 | g2.drawString(snapshot.time() + " ms", oneHalf + 3, timelineMarkTextOffsetY); 142 | } else if (threeFourths == snapshotStartX) { 143 | g2.drawLine(threeFourths, timelineMarkStartY, threeFourths, timelineMarkEndY); 144 | g2.drawString(snapshot.time() + " ms", threeFourths + 3, timelineMarkTextOffsetY); 145 | } 146 | } 147 | } 148 | 149 | protected static Color getColor(Snapshot s) { 150 | if (s.getYoungPhase() != Phase.IDLE) { 151 | return Colors.YOUNG[s.getYoungPhase().ordinal()]; 152 | } 153 | if (s.getGlobalPhase() != Phase.IDLE) { 154 | return Colors.GLOBAL[s.getGlobalPhase().ordinal()]; 155 | } 156 | if (s.getOldPhase() != Phase.IDLE) { 157 | return Colors.OLD[s.getOldPhase().ordinal()]; 158 | } 159 | return Colors.TIMELINE_IDLE; 160 | } 161 | 162 | protected static Phase getPhase(Snapshot s) { 163 | if (s.getYoungPhase() != Phase.IDLE) { 164 | return s.getYoungPhase(); 165 | } 166 | if (s.getGlobalPhase() != Phase.IDLE) { 167 | return s.getGlobalPhase(); 168 | } 169 | if (s.getOldPhase() != Phase.IDLE) { 170 | return s.getOldPhase(); 171 | } 172 | return Phase.UNKNOWN; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/LayoutConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | class LayoutConstants { 28 | static final int LINE = 15; 29 | 30 | static final int INITIAL_WIDTH = 2000; 31 | static final int INITIAL_HEIGHT = 1600; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/LegendPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.util.LinkedHashMap; 30 | import java.util.Map; 31 | 32 | import static org.openjdk.shenandoah.LayoutConstants.LINE; 33 | import static org.openjdk.shenandoah.RegionState.*; 34 | 35 | class LegendPanel extends JPanel { 36 | private final RenderRunner renderRunner; 37 | 38 | LegendPanel(RenderRunner renderRunner) { 39 | this.renderRunner = renderRunner; 40 | } 41 | 42 | @Override 43 | public void paint(Graphics g) { 44 | Snapshot snapshot = renderRunner.snapshot(); 45 | final int sqSize = LINE; 46 | 47 | Map items = new LinkedHashMap<>(); 48 | 49 | items.put("Empty Uncommitted", 50 | new RegionStat(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, EMPTY_UNCOMMITTED)); 51 | 52 | items.put("Empty Committed", 53 | new RegionStat(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, EMPTY_COMMITTED)); 54 | 55 | items.put("Trash", 56 | new RegionStat(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, TRASH)); 57 | 58 | items.put("TLAB Allocs", 59 | new RegionStat(1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, REGULAR)); 60 | 61 | items.put("GCLAB Allocs", 62 | new RegionStat(1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, REGULAR)); 63 | 64 | items.put("PLAB Allocs", 65 | new RegionStat(1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, REGULAR)); 66 | 67 | items.put("Shared Allocs", 68 | new RegionStat(1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, REGULAR)); 69 | 70 | items.put("Humongous", 71 | new RegionStat(1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, HUMONGOUS)); 72 | 73 | items.put("Humongous + Pinned", 74 | new RegionStat(1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, PINNED_HUMONGOUS)); 75 | 76 | items.put("Collection Set", 77 | new RegionStat(1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, CSET)); 78 | 79 | items.put("Pinned", 80 | new RegionStat(1.0f, 1.0f, 0.3f, 0.0f, 0.0f, 0.0f, PINNED)); 81 | 82 | items.put("Pinned CSet", 83 | new RegionStat(1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, PINNED_CSET)); 84 | 85 | items.put("Age [0, 3)", new RegionStat(REGULAR, 0)); 86 | items.put("Age [3, 6)", new RegionStat(REGULAR, 3)); 87 | items.put("Age [6, 9)", new RegionStat(REGULAR, 6)); 88 | items.put("Age [9, 12)", new RegionStat(REGULAR, 9)); 89 | items.put("Age [12, 15)", new RegionStat(REGULAR, 12)); 90 | items.put("Age 15", new RegionStat(REGULAR, 15)); 91 | 92 | Map summaryNumbers = new LinkedHashMap<>(); 93 | 94 | 95 | summaryNumbers.put("Empty Uncommitted", snapshot.getEmptyUncommittedCount()); 96 | 97 | summaryNumbers.put("Empty Committed", snapshot.getEmptyCommittedCount()); 98 | 99 | summaryNumbers.put("Trash", snapshot.getTrashCount()); 100 | 101 | summaryNumbers.put("TLAB Allocs", snapshot.getTlabCount()); 102 | 103 | summaryNumbers.put("GCLAB Allocs", snapshot.getGclabCount()); 104 | 105 | summaryNumbers.put("PLAB Allocs", snapshot.getPlabCount()); 106 | 107 | summaryNumbers.put("Shared Allocs", snapshot.getSharedCount()); 108 | 109 | summaryNumbers.put("Humongous", snapshot.getHumongousCount()); 110 | 111 | summaryNumbers.put("Humongous + Pinned", snapshot.getPinnedHumongousCount()); 112 | 113 | summaryNumbers.put("Collection Set", snapshot.getCSetCount()); 114 | 115 | summaryNumbers.put("Pinned", snapshot.getPinnedCount()); 116 | 117 | 118 | summaryNumbers.put("Pinned CSet", snapshot.getPinnedCSetCount()); 119 | 120 | summaryNumbers.put("Age [0, 3)", snapshot.getAge0Count()); 121 | summaryNumbers.put("Age [3, 6)", snapshot.getAge3Count()); 122 | summaryNumbers.put("Age [6, 9)", snapshot.getAge6Count()); 123 | summaryNumbers.put("Age [9, 12)", snapshot.getAge9Count()); 124 | summaryNumbers.put("Age [12, 15)", snapshot.getAge12Count()); 125 | summaryNumbers.put("Age 15", snapshot.getAge15Count()); 126 | int i = 0; 127 | for (String key : items.keySet()) { 128 | int y = (int) (i * sqSize * 1.5); 129 | items.get(key).render(g, 0, y, sqSize, sqSize); 130 | g.setColor(Color.BLACK); 131 | g.drawString(key + " total: " + summaryNumbers.get(key).toString(), (int) (sqSize * 1.5), y + sqSize); 132 | i++; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/Phase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | enum Phase { 28 | IDLE, 29 | MARKING, 30 | EVACUATING, 31 | UPDATE_REFS, 32 | UNKNOWN 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionAffiliation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. or its affiliates All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | enum RegionAffiliation { 28 | FREE, 29 | YOUNG, 30 | OLD; 31 | 32 | static RegionAffiliation fromOrdinal(int ordinal) { 33 | switch (ordinal) { 34 | case 0: return FREE; 35 | case 1: return YOUNG; 36 | case 2: return OLD; 37 | default: 38 | throw new IllegalStateException("Unhandled ordinal: " + ordinal); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionHistory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package org.openjdk.shenandoah; 27 | 28 | import javax.swing.*; 29 | import javax.swing.event.DocumentEvent; 30 | import javax.swing.event.DocumentListener; 31 | import javax.swing.text.BadLocationException; 32 | import javax.swing.text.Document; 33 | import java.awt.*; 34 | import java.awt.event.KeyAdapter; 35 | import java.util.List; 36 | 37 | class RegionHistory extends JFrame implements DocumentListener { 38 | static final String DEFAULT_REGION_SELECTION = "0, 100 - 130, 2000"; 39 | static final int MIN_REGION_HEIGHT = 5; 40 | private static final int MAX_REGION_HEIGHT = 25; 41 | 42 | private final RenderRunner renderRunner; 43 | 44 | private final List regions; 45 | 46 | private final RegionSelectionParser parser; 47 | 48 | private final JLabel status; 49 | 50 | RegionHistory(RenderRunner renderRunner, KeyAdapter keyListener) { 51 | this.renderRunner = renderRunner; 52 | this.parser = new RegionSelectionParser(); 53 | this.regions = parser.parse(DEFAULT_REGION_SELECTION); 54 | 55 | setSize(600, 500); 56 | setLayout(new BorderLayout()); 57 | setTitle("Region History"); 58 | 59 | Container content = getContentPane(); 60 | var regionSelection = Box.createHorizontalBox(); 61 | regionSelection.add(new JLabel("Regions")); 62 | JTextField regionInput = new JTextField(DEFAULT_REGION_SELECTION, 25); 63 | regionInput.getDocument().addDocumentListener(this); 64 | regionSelection.add(regionInput); 65 | var historyPanel = new RegionHistoryPanel(); 66 | historyPanel.setFocusable(true); 67 | historyPanel.addKeyListener(keyListener); 68 | status = new JLabel(); 69 | 70 | content.add(regionSelection, BorderLayout.NORTH); 71 | content.add(historyPanel, BorderLayout.CENTER); 72 | content.add(status, BorderLayout.SOUTH); 73 | } 74 | 75 | @Override 76 | public void insertUpdate(DocumentEvent e) { 77 | onTextUpdated(e); 78 | } 79 | 80 | @Override 81 | public void removeUpdate(DocumentEvent e) { 82 | onTextUpdated(e); 83 | } 84 | 85 | @Override 86 | public void changedUpdate(DocumentEvent e) { 87 | } 88 | 89 | private void onTextUpdated(DocumentEvent e) { 90 | try { 91 | Document document = e.getDocument(); 92 | updateRegionSection(document.getText(0, document.getLength())); 93 | } catch (BadLocationException ex) { 94 | throw new RuntimeException(ex); 95 | } 96 | } 97 | 98 | private void updateRegionSection(String expression) { 99 | try { 100 | List selection = parser.parse(expression); 101 | regions.clear(); 102 | regions.addAll(selection); 103 | status.setText(""); 104 | } catch (Exception e) { 105 | status.setText(e.getMessage()); 106 | } 107 | } 108 | 109 | private class RegionHistoryPanel extends JPanel { 110 | @Override 111 | public void paint(Graphics g) { 112 | // TODO: Scroll bars? 113 | // TODO: Tooltips for region detail? 114 | if (regions.isEmpty()) { 115 | g.drawString("No regions selected.", 10, 10); 116 | return; 117 | } 118 | 119 | Rectangle viewport = g.getClipBounds(); 120 | int regionSquareSize = clamp(viewport.height / regions.size()); 121 | renderRegionLabels(g, regionSquareSize); 122 | renderRegionHistory(g, viewport, regionSquareSize); 123 | } 124 | 125 | private void renderRegionHistory(Graphics g, Rectangle viewport, int regionSquareSize) { 126 | int x = 21; 127 | List snapshots = renderRunner.snapshots(); 128 | for (int i = snapshots.size() - 1; i >= 0; i--) { 129 | x += regionSquareSize; 130 | Snapshot snapshot = snapshots.get(i); 131 | int y = 1; 132 | for (var region : regions) { 133 | RegionStat r = snapshot.get(region); 134 | r.render(g, x, y, regionSquareSize, regionSquareSize); 135 | y += regionSquareSize; 136 | if (y > (viewport.height - regionSquareSize)) { 137 | // Break a bit early to leave room for a row with timestamps 138 | break; 139 | } 140 | } 141 | 142 | y += regionSquareSize; 143 | if (i % 10 == 0) { 144 | g.setColor(Color.BLACK); 145 | g.drawString(snapshot.time() + "ms", x, y); 146 | } 147 | 148 | if (x > viewport.width) { 149 | break; 150 | } 151 | } 152 | } 153 | 154 | private void renderRegionLabels(Graphics g, int regionSquareSize) { 155 | int x = 1; 156 | int labelStride = regionSquareSize < 10 ? 10 : 1; 157 | for (int i = 0; i < regions.size(); i += labelStride) { 158 | int region = regions.get(i); 159 | g.setColor(Color.BLACK); 160 | g.drawString(String.valueOf(region), x, (i + 1) * regionSquareSize); 161 | } 162 | } 163 | } 164 | 165 | private static int clamp(int value) { 166 | return Math.min(Math.max(MIN_REGION_HEIGHT, value), MAX_REGION_HEIGHT); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.awt.event.*; 30 | 31 | class RegionPanel extends JPanel { 32 | private final RenderRunner renderRunner; 33 | 34 | int regionWidth, regionHeight; 35 | 36 | RegionPanel(RenderRunner renderRunner, KeyAdapter keyboardShortCuts) { 37 | this.renderRunner = renderRunner; 38 | 39 | addComponentListener(new ComponentAdapter() { 40 | public void componentResized(ComponentEvent ev) { 41 | regionWidth = ev.getComponent().getWidth(); 42 | regionHeight = ev.getComponent().getHeight(); 43 | } 44 | }); 45 | 46 | addMouseListener(new MouseAdapter() { 47 | @Override 48 | public void mouseClicked(MouseEvent e) { 49 | Snapshot snapshot = renderRunner.snapshot(); 50 | int area = regionWidth * regionHeight; 51 | int sqSize = Math.max(1, (int) Math.sqrt(1D * area / snapshot.regionCount())); 52 | int cols = regionWidth / sqSize; 53 | int regionNumber = (e.getX() / sqSize) + ((e.getY() / sqSize) * cols) ; 54 | if (regionNumber >= 0 && regionNumber < snapshot.statsSize()) { 55 | RegionPopUp popup = new RegionPopUp(regionNumber, renderRunner); 56 | popup.setSize(450, 450); 57 | popup.setLocation(e.getX(), e.getY()); 58 | popup.setVisible(true); 59 | popup.addKeyListener(keyboardShortCuts); 60 | popup.addWindowListener(new WindowAdapter() { 61 | @Override 62 | public void windowClosing(WindowEvent e) { 63 | super.windowClosing(e); 64 | popup.setVisible(false); 65 | popup.dispose(); 66 | renderRunner.deletePopup(popup); 67 | } 68 | }); 69 | renderRunner.addPopup(popup); 70 | } 71 | } 72 | }); 73 | } 74 | 75 | @Override 76 | public void paint(Graphics g) { 77 | Snapshot snapshot = renderRunner.snapshot(); 78 | int area = regionWidth * regionHeight; 79 | int sqSize = Math.max(1, (int) Math.sqrt(1D * area / snapshot.regionCount())); 80 | int cols = regionWidth / sqSize; 81 | int cellSize = sqSize - 2; 82 | for (int i = 0; i < snapshot.regionCount(); i++) { 83 | int rectx = (i % cols) * sqSize; 84 | int recty = (i / cols) * sqSize; 85 | RegionStat s = snapshot.get(i); 86 | s.render(g, rectx, recty, cellSize, cellSize); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionPopUp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.util.List; 30 | 31 | class RegionPopUp extends JFrame { 32 | private final int regionNumber; 33 | private final RenderRunner renderRunner; 34 | private float spotlightUsedLvl; 35 | private float spotlightLiveLvl; 36 | private float spotlightTlabLvl; 37 | private float spotlightGclabLvl; 38 | private float spotlightPlabLvl; 39 | private float spotlightSharedLvl; 40 | private RegionState spotlightState; 41 | private long spotlightAge; 42 | private RegionAffiliation spotlightAffiliation; 43 | private static final int squareSize = 15; 44 | private static final int spotlightSquareSize = 28; 45 | private static final int initialY = 1; 46 | 47 | RegionStat spotlightRegionData; 48 | 49 | RegionPopUp(int regionNumber, RenderRunner renderRunner) { 50 | this.regionNumber = regionNumber; 51 | this.renderRunner = renderRunner; 52 | 53 | JPanel timelinePanel = new JPanel() { 54 | public void paint(Graphics g) { 55 | timelinePaint(g); 56 | } 57 | }; 58 | JPanel spotlightPanel = new JPanel() { 59 | public void paint(Graphics g) { 60 | spotlightPaint(g); 61 | } 62 | }; 63 | 64 | this.setLayout(new GridBagLayout()); 65 | 66 | Insets pad = new Insets(7, 7, 7, 7); 67 | 68 | { 69 | GridBagConstraints c = new GridBagConstraints(); 70 | c.fill = GridBagConstraints.BOTH; 71 | c.gridx = 0; 72 | c.gridy = 0; 73 | c.weightx = 4; 74 | c.weighty = 5; 75 | c.insets = pad; 76 | this.add(spotlightPanel, c); 77 | } 78 | 79 | { 80 | GridBagConstraints c = new GridBagConstraints(); 81 | c.fill = GridBagConstraints.BOTH; 82 | c.gridx = 1; 83 | c.gridy = 0; 84 | c.weightx = 3; 85 | c.weighty = 7; 86 | c.insets = pad; 87 | c.gridheight = 2; 88 | this.add(timelinePanel, c); 89 | } 90 | } 91 | 92 | synchronized void timelinePaint(Graphics g) { 93 | int y = initialY; 94 | List snapshots = renderRunner.snapshots(); 95 | for (int i = snapshots.size() - 1; i >= 0; i--) { 96 | Snapshot snapshot = snapshots.get(i); 97 | RegionStat r = snapshot.get(regionNumber); 98 | if (y == initialY) { 99 | r.render(g, 1, y, spotlightSquareSize, spotlightSquareSize); 100 | g.setColor(Color.LIGHT_GRAY); 101 | g.drawString(snapshot.time() + " ms", 35, y + spotlightSquareSize); 102 | setSpotlightRegionStat(r); 103 | y += spotlightSquareSize; 104 | } else { 105 | r.render(g, 7, y, squareSize, squareSize); 106 | y += squareSize; 107 | } 108 | if (i % 10 == 0) { 109 | g.setColor(Color.LIGHT_GRAY); 110 | g.drawString(snapshot.time() + " ms", 35, y); 111 | } 112 | } 113 | } 114 | 115 | synchronized void spotlightPaint(Graphics g) { 116 | g.setColor(Color.BLACK); 117 | g.drawString("Spotlight Region Data", 20, 30); 118 | g.drawString("Region index: " + regionNumber, 20, 50); 119 | g.drawString("Used Level: " + spotlightUsedLvl + " %", 20, 70); 120 | g.drawString("Live Level: " + spotlightLiveLvl + " %", 20, 90); 121 | g.drawString("TLAB Level: " + spotlightTlabLvl + " %", 20, 110); 122 | g.drawString("GCLAB Level: " + spotlightGclabLvl + " %", 20, 130); 123 | g.drawString("PLAB Level: " + spotlightPlabLvl + " %", 20, 150); 124 | g.drawString("Shared Level: " + spotlightSharedLvl + " %", 20, 170); 125 | g.drawString("State: " + spotlightState, 20, 190); 126 | g.drawString("Age: " + spotlightAge, 20, 210); 127 | g.drawString("Affiliation: " + spotlightAffiliation, 20, 230); 128 | } 129 | 130 | final void setSpotlightRegionStat(RegionStat r) { 131 | this.spotlightRegionData = r; 132 | spotlightUsedLvl = spotlightRegionData.used() * 100f; 133 | spotlightLiveLvl = spotlightRegionData.live() * 100f; 134 | spotlightTlabLvl = spotlightRegionData.tlabAllocs() * 100f; 135 | spotlightGclabLvl = spotlightRegionData.gclabAllocs() * 100f; 136 | spotlightPlabLvl = spotlightRegionData.plabAllocs() * 100f; 137 | spotlightSharedLvl = spotlightRegionData.sharedAllocs() * 100f; 138 | spotlightState = spotlightRegionData.state(); 139 | spotlightAge = spotlightRegionData.age(); 140 | spotlightAffiliation = spotlightRegionData.affiliation(); 141 | } 142 | } 143 | 144 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionSelectionParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import java.util.ArrayList; 28 | import java.util.Collection; 29 | import java.util.HashSet; 30 | import java.util.List; 31 | 32 | class RegionSelectionParser { 33 | StringBuilder sb = new StringBuilder(4); 34 | int rangeStart = -1; 35 | 36 | List parse(String expression) { 37 | try { 38 | Collection ints = new HashSet<>(); 39 | for (int i = 0, n = expression.length(); i < n; ++i) { 40 | char c = expression.charAt(i); 41 | if (Character.isDigit(c)) { 42 | sb.append(c); 43 | } else if (c == ',') { 44 | consumeExpression(ints); 45 | } else if (c == '-') { 46 | rangeStart = consumeBuffer(); 47 | } 48 | } 49 | if (!sb.isEmpty()) { 50 | consumeExpression(ints); 51 | } 52 | List sorted = new ArrayList<>(ints); 53 | sorted.sort(Integer::compareTo); 54 | return sorted; 55 | } finally { 56 | rangeStart = -1; 57 | sb.setLength(0); 58 | } 59 | } 60 | 61 | private void consumeExpression(Collection ints) { 62 | if (rangeStart == -1) { 63 | ints.add(consumeBuffer()); 64 | } else { 65 | int rangeFinish = consumeBuffer(); 66 | expandRange(ints, rangeFinish); 67 | } 68 | } 69 | 70 | private void expandRange(Collection ints, int rangeFinish) { 71 | if (rangeStart > rangeFinish) { 72 | throw new IllegalArgumentException("Invalid range: " + rangeStart + " - " + rangeFinish); 73 | } 74 | 75 | for (int i = rangeStart; i <= rangeFinish; ++i) { 76 | ints.add(i); 77 | } 78 | rangeStart = -1; 79 | } 80 | 81 | private int consumeBuffer() { 82 | int result = Integer.parseInt(sb.toString()); 83 | sb.setLength(0); 84 | return result; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionStat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import java.awt.*; 28 | import java.util.Arrays; 29 | import java.util.Collections; 30 | 31 | import static org.openjdk.shenandoah.Colors.*; 32 | 33 | class RegionStat { 34 | 35 | private static final int PERCENT_MASK = 0x7f; 36 | private static final int AGE_MASK = 0x0f; 37 | private static final int AFFILIATION_MASK = 0x03; 38 | private static final int STATUS_MASK = 0x3f; 39 | 40 | private static final int USED_SHIFT = 0; 41 | private static final int LIVE_SHIFT = 7; 42 | private static final int TLAB_SHIFT = 14; 43 | private static final int GCLAB_SHIFT = 21; 44 | private static final int SHARED_SHIFT = 28; 45 | private static final int PLAB_SHIFT = 35; 46 | private static final int AGE_SHIFT = 51; 47 | private static final int AFFILIATION_SHIFT = 56; 48 | private static final int FLAGS_SHIFT = 58; 49 | 50 | private final RegionState state; 51 | private final float liveLvl; 52 | private final float usedLvl; 53 | private final float tlabLvl; 54 | private final float gclabLvl; 55 | private final float plabLvl; 56 | private final float sharedLvl; 57 | private final long age; 58 | private final RegionAffiliation affiliation; 59 | private final boolean showLivenessDetail; 60 | 61 | private static final Stroke STROKE = new BasicStroke(2); 62 | 63 | // This constructor is for the legend. 64 | RegionStat(float usedLvl, float liveLvl, float tlabLvl, float gclabLvl, float plabLvl, float sharedLvl, RegionState state) { 65 | this.usedLvl = usedLvl; 66 | this.liveLvl = liveLvl; 67 | this.tlabLvl = tlabLvl; 68 | this.gclabLvl = gclabLvl; 69 | this.plabLvl = plabLvl; 70 | this.sharedLvl = sharedLvl; 71 | this.state = state; 72 | this.age = -1; 73 | this.affiliation = RegionAffiliation.YOUNG; 74 | this.showLivenessDetail = Boolean.getBoolean("show.liveness"); 75 | } 76 | // This constructor is for CounterTest 77 | RegionStat(float usedLvl, float liveLvl, float tlabLvl, float gclabLvl, float plabLvl, float sharedLvl, RegionAffiliation affiliation,RegionState state) { 78 | this.usedLvl = usedLvl; 79 | this.liveLvl = liveLvl; 80 | this.tlabLvl = tlabLvl; 81 | this.gclabLvl = gclabLvl; 82 | this.plabLvl = plabLvl; 83 | this.sharedLvl = sharedLvl; 84 | this.state = state; 85 | this.age = -1; 86 | this.affiliation = affiliation; 87 | this.showLivenessDetail = Boolean.getBoolean("show.liveness"); 88 | } 89 | 90 | // Also only used for the legend. 91 | RegionStat(RegionState state, int age) { 92 | this.usedLvl = 0; 93 | this.liveLvl = 0; 94 | this.tlabLvl = 0; 95 | this.gclabLvl = 0; 96 | this.plabLvl = 0; 97 | this.sharedLvl = 0; 98 | this.state = state; 99 | this.age = age; 100 | this.affiliation = RegionAffiliation.YOUNG; 101 | this.showLivenessDetail = Boolean.getBoolean("show.liveness"); 102 | } 103 | 104 | RegionStat(long data) { 105 | this.showLivenessDetail = Boolean.getBoolean("show.liveness"); 106 | 107 | usedLvl = ((data >>> USED_SHIFT) & PERCENT_MASK) / 100F; 108 | liveLvl = ((data >>> LIVE_SHIFT) & PERCENT_MASK) / 100F; 109 | tlabLvl = ((data >>> TLAB_SHIFT) & PERCENT_MASK) / 100F; 110 | gclabLvl = ((data >>> GCLAB_SHIFT) & PERCENT_MASK) / 100F; 111 | plabLvl = ((data >>> PLAB_SHIFT) & PERCENT_MASK) / 100F; 112 | sharedLvl = ((data >>> SHARED_SHIFT) & PERCENT_MASK) / 100F; 113 | 114 | age = ((data >>> AGE_SHIFT) & AGE_MASK); 115 | affiliation = RegionAffiliation.fromOrdinal((int) (data >>> AFFILIATION_SHIFT) & AFFILIATION_MASK); 116 | state = RegionState.fromOrdinal((int) (data >>> FLAGS_SHIFT) & STATUS_MASK); 117 | } 118 | 119 | private Color selectLive(RegionState s) { 120 | switch (s) { 121 | case CSET: 122 | return LIVE_CSET; 123 | case HUMONGOUS: 124 | return LIVE_HUMONGOUS; 125 | case PINNED_HUMONGOUS: 126 | return LIVE_PINNED_HUMONGOUS; 127 | case REGULAR: 128 | return LIVE_REGULAR; 129 | case TRASH: 130 | return LIVE_TRASH; 131 | case PINNED: 132 | return LIVE_PINNED; 133 | case PINNED_CSET: 134 | return LIVE_PINNED_CSET; 135 | case EMPTY_COMMITTED: 136 | case EMPTY_UNCOMMITTED: 137 | return LIVE_EMPTY; 138 | default: 139 | return Color.WHITE; 140 | } 141 | } 142 | 143 | private Color mixAlpha(Color c, float alpha) { 144 | return new Color(c.getRed(), c.getGreen(), c.getBlue(), (int)(alpha * 100 + 55)); 145 | } 146 | 147 | private void drawShape(Graphics2D g, int x, int y, int width, int height) { 148 | switch (affiliation) { 149 | case FREE: 150 | break; 151 | case YOUNG: 152 | g.drawRect(x, y, width, height); 153 | break; 154 | case OLD: 155 | g.drawOval(x, y, width, height); 156 | break; 157 | } 158 | } 159 | 160 | private void fillShape(Graphics2D g, int x, int y, int width, int height) { 161 | switch (affiliation) { 162 | case FREE: 163 | break; 164 | case YOUNG: 165 | g.fillRect(x, y, width, height); 166 | break; 167 | case OLD: 168 | g.fillOval(x, y, width, height); 169 | break; 170 | } 171 | } 172 | 173 | void render(Graphics graphics, int x, int y, int width, int height) { 174 | Graphics2D g = (Graphics2D) graphics; 175 | g.setColor(Color.WHITE); 176 | fillShape(g, x, y, width, height); 177 | 178 | switch (state) { 179 | case REGULAR: { 180 | if (gclabLvl > 0 || tlabLvl > 0 || sharedLvl > 0 || plabLvl > 0) { 181 | int sharedWidth = (int) (width * sharedLvl); 182 | int tlabWidth = (int) (width * tlabLvl); 183 | int gclabWidth = (int) (width * gclabLvl); 184 | int plabWidth = (int) (width * plabLvl); 185 | 186 | int lx = x; 187 | 188 | if (tlabWidth > 0) { 189 | g.setColor(mixAlpha(TLAB_ALLOC, liveLvl)); 190 | fillShape(g, lx, y, tlabWidth, height); 191 | lx += tlabWidth; 192 | } 193 | 194 | if (gclabWidth > 0) { 195 | g.setColor(mixAlpha(GCLAB_ALLOC, liveLvl)); 196 | fillShape(g, lx, y, gclabWidth, height); 197 | lx += gclabWidth; 198 | } 199 | 200 | if (sharedWidth > 0) { 201 | g.setColor(mixAlpha(SHARED_ALLOC, liveLvl)); 202 | fillShape(g, lx, y, sharedWidth, height); 203 | lx += sharedWidth; 204 | } 205 | 206 | if (plabWidth > 0) { 207 | g.setColor(mixAlpha(PLAB_ALLOC, liveLvl)); 208 | fillShape(g, lx, y, plabWidth, height); 209 | } 210 | } 211 | break; 212 | } 213 | case PINNED: { 214 | int usedWidth = (int) (width * usedLvl); 215 | g.setColor(Colors.LIVE_PINNED); 216 | fillShape(g, x, y, usedWidth, height); 217 | break; 218 | } 219 | case CSET: 220 | case PINNED_CSET: 221 | case HUMONGOUS: 222 | case PINNED_HUMONGOUS: { 223 | int usedWidth = (int) (width * usedLvl); 224 | g.setColor(USED); 225 | fillShape(g, x, y, usedWidth, height); 226 | 227 | int liveWidth = (int) (width * liveLvl); 228 | g.setColor(selectLive(state)); 229 | fillShape(g, x, y, liveWidth, height); 230 | 231 | g.setColor(selectLive(state)); 232 | g.drawLine(x + liveWidth, y, x + liveWidth, y + height); 233 | break; 234 | } 235 | case EMPTY_COMMITTED: 236 | case EMPTY_UNCOMMITTED: 237 | case TRASH: 238 | break; 239 | default: 240 | throw new IllegalStateException("Unhandled region state: " + state); 241 | } 242 | 243 | if (age < 15) { 244 | if (state == RegionState.TRASH) { 245 | g.setColor(Color.BLACK); 246 | g.drawLine(x, y, x + width, y + height); 247 | g.drawLine(x, y + height, x + width, y); 248 | } 249 | 250 | if (state == RegionState.EMPTY_UNCOMMITTED) { 251 | g.setColor(Colors.BORDER); 252 | for (int t = 0; t < 3; t++) { 253 | int off = width * t / 3; 254 | g.drawLine(x, y + off, x + off, y); 255 | g.drawLine(x + off, y + height, x + width, y + off); 256 | } 257 | } 258 | if (state == RegionState.EMPTY_COMMITTED) { 259 | g.setColor(Color.RED); 260 | g.drawLine(x, y, x + width, y + height); 261 | } 262 | } 263 | 264 | if (age > -1) { 265 | g.setColor(getColorForAge()); 266 | g.setStroke(STROKE); 267 | drawShape(g, x, y, width, height); 268 | if (showLivenessDetail) { 269 | g.setColor(Color.BLACK); 270 | g.drawString(String.valueOf(liveLvl), x + 2, y + height - 2); 271 | } 272 | } 273 | } 274 | 275 | private Color getColorForAge() { 276 | final int THRESHOLD = 15; 277 | final int categorySize = THRESHOLD / AGE_COLORS.length; 278 | int category = (int) (age / categorySize); 279 | if (category < AGE_COLORS.length) { 280 | return AGE_COLORS[category]; 281 | } 282 | return Color.BLACK; 283 | } 284 | 285 | @Override 286 | public boolean equals(Object o) { 287 | if (this == o) return true; 288 | if (o == null || getClass() != o.getClass()) return false; 289 | 290 | RegionStat that = (RegionStat) o; 291 | 292 | if (Float.compare(that.liveLvl, liveLvl) != 0) return false; 293 | if (Float.compare(that.usedLvl, usedLvl) != 0) return false; 294 | if (Float.compare(that.tlabLvl, tlabLvl) != 0) return false; 295 | if (Float.compare(that.gclabLvl, gclabLvl) != 0) return false; 296 | return state.equals(that.state); 297 | } 298 | 299 | @Override 300 | public int hashCode() { 301 | int result = state.hashCode(); 302 | result = 31 * result + (liveLvl != 0.0f ? Float.floatToIntBits(liveLvl) : 0); 303 | result = 31 * result + (usedLvl != 0.0f ? Float.floatToIntBits(usedLvl) : 0); 304 | result = 31 * result + (tlabLvl != 0.0f ? Float.floatToIntBits(tlabLvl) : 0); 305 | result = 31 * result + (gclabLvl != 0.0f ? Float.floatToIntBits(gclabLvl) : 0); 306 | return result; 307 | } 308 | 309 | RegionAffiliation affiliation() { 310 | return affiliation; 311 | } 312 | 313 | float live() { 314 | return liveLvl; 315 | } 316 | 317 | float used() { 318 | return usedLvl; 319 | } 320 | 321 | float tlabAllocs() { 322 | return tlabLvl; 323 | } 324 | 325 | float gclabAllocs() { 326 | return gclabLvl; 327 | } 328 | 329 | float plabAllocs() { 330 | return plabLvl; 331 | } 332 | 333 | float maxAllocsYoung() { 334 | return Collections.max(Arrays.asList(tlabLvl, gclabLvl, sharedLvl)); 335 | } 336 | float maxAllocsOld() { 337 | return Collections.max(Arrays.asList(plabLvl, sharedLvl)); 338 | } 339 | 340 | float sharedAllocs() { 341 | return sharedLvl; 342 | } 343 | 344 | RegionState state() { 345 | return state; 346 | } 347 | long age() { return age; } 348 | } 349 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RegionState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | enum RegionState { 28 | EMPTY_UNCOMMITTED, 29 | EMPTY_COMMITTED, 30 | REGULAR, 31 | HUMONGOUS, 32 | CSET, 33 | PINNED, 34 | TRASH, 35 | PINNED_CSET, 36 | PINNED_HUMONGOUS; 37 | 38 | static RegionState fromOrdinal(int idx) { 39 | switch (idx) { 40 | case 0: return EMPTY_UNCOMMITTED; 41 | case 1: return EMPTY_COMMITTED; 42 | case 2: return REGULAR; 43 | case 3: return HUMONGOUS; 44 | case 4: return HUMONGOUS; 45 | case 5: return CSET; 46 | case 6: return PINNED; 47 | case 7: return TRASH; 48 | case 8: return PINNED_CSET; 49 | case 9: return PINNED_HUMONGOUS; 50 | default: 51 | throw new IllegalStateException("Unhandled ordinal: " + idx); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/RenderRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import javax.swing.*; 28 | import java.awt.*; 29 | import java.util.ArrayList; 30 | import java.util.HashSet; 31 | import java.util.List; 32 | import java.util.Set; 33 | import java.util.concurrent.Executors; 34 | import java.util.concurrent.ScheduledExecutorService; 35 | import java.util.concurrent.TimeUnit; 36 | 37 | class RenderRunner implements Runnable { 38 | private final ScheduledExecutorService service; 39 | private long lastUpdateNanos; 40 | private volatile EventLog events; 41 | private boolean isPaused; 42 | private boolean isLive; 43 | private double playbackSpeed; 44 | private String playbackStatus = ""; 45 | 46 | private Runnable recordingLoaded; 47 | 48 | private final DataProvider liveData; 49 | 50 | private final Set frames; 51 | 52 | RenderRunner(JFrame frame) { 53 | this.frames = new HashSet<>(); 54 | this.frames.add(frame); 55 | this.playbackSpeed = 1.0; 56 | this.liveData = new DataProvider(); 57 | this.events = new EventLog<>(TimeUnit.MILLISECONDS, 1); 58 | this.service = Executors.newScheduledThreadPool(2); 59 | service.scheduleAtFixedRate(this, 0, 100, TimeUnit.MILLISECONDS); 60 | } 61 | 62 | void onRecordingLoaded(Runnable runnable) { 63 | this.recordingLoaded = runnable; 64 | } 65 | 66 | synchronized void loadPlayback(String filePath) { 67 | lastUpdateNanos = 0; 68 | liveData.stopConnector(); 69 | playbackStatus = "Loading"; 70 | service.submit(() -> { 71 | DataLogProvider.loadSnapshots(filePath, events); 72 | isLive = false; 73 | playbackStatus = "Recorded"; 74 | if (recordingLoaded != null) { 75 | recordingLoaded.run(); 76 | } 77 | System.out.println("Loaded event log: " + filePath); 78 | }); 79 | } 80 | 81 | synchronized void loadLive(String vmIdentifier) { 82 | if (vmIdentifier != null) { 83 | liveData.setConnectionTarget(vmIdentifier); 84 | } 85 | 86 | lastUpdateNanos = 0; 87 | liveData.startConnector(); 88 | events = new EventLog<>(TimeUnit.MILLISECONDS, 5_000); 89 | isLive = true; 90 | } 91 | 92 | public synchronized void run() { 93 | try { 94 | if (liveData.isConnected()) { 95 | Snapshot snapshot = liveData.snapshot(); 96 | if (snapshot != null) { 97 | events.add(snapshot); 98 | } 99 | } 100 | 101 | long now = System.nanoTime(); 102 | if (lastUpdateNanos != 0) { 103 | if (!isPaused) { 104 | long elapsed = (long)((now - lastUpdateNanos) * playbackSpeed); 105 | events.advanceBy(elapsed, TimeUnit.NANOSECONDS); 106 | } 107 | } 108 | lastUpdateNanos = now; 109 | frames.forEach(JFrame::repaint); 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | } 113 | } 114 | 115 | synchronized Snapshot snapshot() { 116 | Snapshot latest = events.current(); 117 | return latest != null ? latest : DataProvider.DISCONNECTED; 118 | } 119 | 120 | void addPopup(JFrame popup) { 121 | frames.add(popup); 122 | } 123 | 124 | void deletePopup(JFrame popup) { 125 | frames.remove(popup); 126 | } 127 | 128 | List snapshots() { 129 | return new ArrayList<>(events.inRange()); 130 | } 131 | 132 | void setPlaybackSpeed(double speed) { 133 | playbackSpeed = speed; 134 | } 135 | 136 | boolean isPaused() { 137 | return isPaused; 138 | } 139 | 140 | boolean isLive() { return isLive; } 141 | 142 | void togglePlayback() { 143 | isPaused = !isPaused; 144 | } 145 | 146 | void stepBy(int value) { 147 | events.stepBy(value); 148 | } 149 | 150 | void stepToEnd() { 151 | events.stepToEnd(); 152 | } 153 | 154 | double getPlaybackSpeed() { 155 | return playbackSpeed; 156 | } 157 | 158 | int snapshotCount() { 159 | return events.size(); 160 | } 161 | 162 | void stepTo(int value) { 163 | events.stepTo(value); 164 | } 165 | 166 | int cursor() { 167 | return events.cursor(); 168 | } 169 | 170 | String status() { 171 | return isLive ? liveData.status() : playbackStatus; 172 | } 173 | 174 | void shutdown() { 175 | service.shutdown(); 176 | frames.forEach(Window::dispose); 177 | System.exit(0); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/ShenandoahVisualizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, 2023, Amazon.com, Inc. All rights reserved. 3 | * Copyright (c) 2016, 2023, Red Hat, Inc. All rights reserved. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. 8 | * 9 | * This code is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 | * version 2 for more details (a copy is included in the LICENSE file that 13 | * accompanied this code). 14 | * 15 | * You should have received a copy of the GNU General Public License version 16 | * 2 along with this work; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 | * or visit www.oracle.com if you need additional information or have any 21 | * questions. 22 | * 23 | */ 24 | package org.openjdk.shenandoah; 25 | 26 | import javax.swing.*; 27 | import java.awt.*; 28 | import java.awt.event.KeyAdapter; 29 | import java.awt.event.KeyEvent; 30 | import java.awt.event.WindowAdapter; 31 | import java.awt.event.WindowEvent; 32 | 33 | class ShenandoahVisualizer extends JFrame { 34 | 35 | public static void main(String[] args) { 36 | String vmIdentifier = null; 37 | String filePath = null; 38 | 39 | int i = 0; 40 | String arg; 41 | while (i < args.length && args[i].startsWith("-")) { 42 | arg = args[i++]; 43 | if (arg.equals("-vm")) { 44 | if (i < args.length) { 45 | vmIdentifier = args[i++]; 46 | } else { 47 | System.out.println("-vm requires a vm identifier"); 48 | return; 49 | } 50 | } else if (arg.equals("-logFile")) { 51 | if (i < args.length) { 52 | filePath = args[i++]; 53 | } else { 54 | System.out.println("-logFile requires a file path"); 55 | return; 56 | } 57 | } else { 58 | System.out.println("ShenandoahVisualizer: Illegal option " + arg); 59 | System.out.println("Usage: [-vm vmIdentifier] [-logFile filePath]"); 60 | return; 61 | } 62 | } 63 | 64 | ShenandoahVisualizer visualizer = new ShenandoahVisualizer(filePath, vmIdentifier); 65 | visualizer.setVisible(true); 66 | } 67 | 68 | ShenandoahVisualizer(String filePath, String vmIdentifier) { 69 | setLayout(new BorderLayout()); 70 | setTitle("Shenandoah GC Visualizer"); 71 | setSize(LayoutConstants.INITIAL_WIDTH, LayoutConstants.INITIAL_HEIGHT); 72 | 73 | final RenderRunner renderRunner = new RenderRunner(this); 74 | 75 | KeyAdapter keyShortcutAdapter = new KeyboardShortcuts(renderRunner); 76 | 77 | JPanel content = new JPanel(new GridBagLayout()); 78 | JPanel legendPanel = new LegendPanel(renderRunner); 79 | JPanel statusPanel = new StatusPanel(renderRunner); 80 | JPanel graphPanel = new GraphPanel(renderRunner); 81 | JPanel regionsPanel = new RegionPanel(renderRunner, keyShortcutAdapter); 82 | 83 | if (filePath != null) { 84 | renderRunner.loadPlayback(filePath); 85 | } else { 86 | renderRunner.loadLive(vmIdentifier); 87 | } 88 | 89 | Insets pad = new Insets(10, 10, 10, 10); 90 | 91 | { 92 | GridBagConstraints c = new GridBagConstraints(); 93 | c.fill = GridBagConstraints.BOTH; 94 | c.gridx = 0; 95 | c.gridy = 0; 96 | c.weightx = 0.9; 97 | c.weighty = 0.1; 98 | c.insets = pad; 99 | content.add(graphPanel, c); 100 | } 101 | 102 | { 103 | GridBagConstraints c = new GridBagConstraints(); 104 | c.fill = GridBagConstraints.BOTH; 105 | c.gridx = 0; 106 | c.gridy = 1; 107 | c.weightx = 0.9; 108 | c.weighty = 0.9; 109 | c.insets = pad; 110 | c.gridheight = GridBagConstraints.RELATIVE; 111 | content.add(regionsPanel, c); 112 | } 113 | 114 | { 115 | statusPanel.setPreferredSize(new Dimension(25, 175)); 116 | GridBagConstraints c = new GridBagConstraints(); 117 | c.fill = GridBagConstraints.BOTH; 118 | c.gridx = 1; 119 | c.gridy = 0; 120 | c.weightx = 0.1; 121 | c.weighty = 0.5; 122 | c.insets = pad; 123 | content.add(statusPanel, c); 124 | } 125 | 126 | { 127 | GridBagConstraints c = new GridBagConstraints(); 128 | c.fill = GridBagConstraints.BOTH; 129 | c.gridx = 1; 130 | c.gridy = 1; 131 | c.weightx = 0.1; 132 | c.weighty = 0.5; 133 | c.insets = pad; 134 | c.gridheight = GridBagConstraints.REMAINDER; 135 | content.add(legendPanel, c); 136 | } 137 | 138 | var toolbarPanel = new ToolbarPanel(renderRunner, keyShortcutAdapter); 139 | toolbarPanel.setFocusable(true); 140 | toolbarPanel.requestFocusInWindow(); 141 | if (filePath != null) { 142 | toolbarPanel.setFileNameField(filePath); 143 | } 144 | 145 | add(toolbarPanel, BorderLayout.SOUTH); 146 | add(content, BorderLayout.CENTER); 147 | 148 | addWindowListener(new WindowAdapter() { 149 | public void windowClosing(WindowEvent e) { 150 | renderRunner.shutdown(); 151 | } 152 | }); 153 | } 154 | 155 | private static class KeyboardShortcuts extends KeyAdapter { 156 | private final RenderRunner renderRunner; 157 | 158 | KeyboardShortcuts(RenderRunner renderRunner) { 159 | this.renderRunner = renderRunner; 160 | } 161 | 162 | @Override 163 | public void keyPressed(KeyEvent e) { 164 | super.keyPressed(e); 165 | switch (e.getKeyCode()) { 166 | case KeyEvent.VK_LEFT -> renderRunner.stepBy(-1); 167 | case KeyEvent.VK_DOWN -> renderRunner.stepBy(-5); 168 | case KeyEvent.VK_RIGHT -> renderRunner.stepBy(1); 169 | case KeyEvent.VK_UP -> renderRunner.stepBy(5); 170 | case KeyEvent.VK_SPACE -> renderRunner.togglePlayback(); 171 | case KeyEvent.VK_ENTER -> renderRunner.stepToEnd(); 172 | } 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/Snapshot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.HdrHistogram.Histogram; 28 | 29 | import java.util.List; 30 | import java.util.function.Function; 31 | 32 | class Snapshot implements Timed { 33 | protected String collectionMode() { 34 | if (phase() == Phase.IDLE) { 35 | return ""; 36 | } 37 | 38 | if (isFullActive()) { 39 | return "Full"; 40 | } 41 | 42 | if (isDegenActive()) { 43 | return isYoungActive() ? "Degenerate Young" : "Degenerate Global"; 44 | } 45 | 46 | return isYoungActive() ? "Young" : "Global"; 47 | } 48 | 49 | enum Generation { 50 | YOUNG(4), OLD(2), GLOBAL(0); 51 | 52 | final int shift; 53 | 54 | Generation(int shift) { 55 | this.shift = shift; 56 | } 57 | 58 | Phase phase(long status) { 59 | int phase = (int) ((status >> shift) & 0x3); 60 | switch (phase) { 61 | case 0: return Phase.IDLE; 62 | case 1: return Phase.MARKING; 63 | case 2: return Phase.EVACUATING; 64 | case 3: return Phase.UPDATE_REFS; 65 | default: 66 | throw new IllegalArgumentException("Unknown status: " + status); 67 | } 68 | } 69 | 70 | // Decode 3 bits for older versions of shenandoah collector 71 | Phase version1_phase(long status) { 72 | int phase = (int) status; 73 | switch (phase) { 74 | case 0: return Phase.IDLE; 75 | case 1: return Phase.MARKING; 76 | case 2: return Phase.EVACUATING; 77 | case 4: return Phase.UPDATE_REFS; 78 | default: 79 | throw new IllegalArgumentException("Unknown status: " + status); 80 | } 81 | } 82 | } 83 | 84 | private final long time; 85 | private final long regionSize; 86 | private final List stats; 87 | private final Phase globalPhase; 88 | private final Phase oldPhase; 89 | private final Phase youngPhase; 90 | private final boolean degenActive; 91 | private final boolean fullActive; 92 | private final Histogram histogram; 93 | 94 | private int emptyUncommittedCount; 95 | private int emptyCommittedCount; 96 | private int trashCount; 97 | private int tlabCount; 98 | private int gclabCount; 99 | private int plabCount; 100 | private int sharedCount; 101 | private int humongousCount; 102 | private int pinnedHumongousCount; 103 | private int cSetCount; 104 | private int pinnedCount; 105 | private int pinnedCSetCount; 106 | private int age0Count; 107 | private int age3Count; 108 | private int age6Count; 109 | private int age9Count; 110 | private int age12Count; 111 | private int age15Count; 112 | 113 | Snapshot(long time, long regionSize, long protocolVersion, List stats, int status, Histogram histogram) { 114 | this.time = time; 115 | this.regionSize = regionSize; 116 | this.stats = stats; 117 | this.histogram = histogram; 118 | this.degenActive = ((status & 0x40) >> 6) == 1; 119 | this.fullActive = ((status & 0x80) >> 7) == 1; 120 | // Decode differently according to different version value 121 | if (protocolVersion == 1) { 122 | this.globalPhase = Generation.GLOBAL.version1_phase(status); 123 | this.oldPhase = Phase.IDLE; 124 | this.youngPhase = Phase.IDLE; 125 | } else { 126 | this.globalPhase = Generation.GLOBAL.phase(status); 127 | this.oldPhase = Generation.OLD.phase(status); 128 | this.youngPhase = Generation.YOUNG.phase(status); 129 | } 130 | this.stateCounter(); 131 | } 132 | 133 | Phase phase() { 134 | if (oldPhase != Phase.IDLE) { 135 | return oldPhase; 136 | } 137 | if (youngPhase != Phase.IDLE) { 138 | return youngPhase; 139 | } 140 | return globalPhase; 141 | } 142 | 143 | Phase getGlobalPhase() { 144 | return globalPhase; 145 | } 146 | 147 | Phase getYoungPhase() { 148 | return youngPhase; 149 | } 150 | 151 | Phase getOldPhase() { 152 | return oldPhase; 153 | } 154 | 155 | Histogram getSafepointTime() { 156 | return histogram; 157 | } 158 | 159 | boolean isYoungActive() { 160 | return youngPhase != Phase.IDLE; 161 | } 162 | 163 | boolean isDegenActive() { 164 | return degenActive; 165 | } 166 | 167 | boolean isFullActive() { 168 | return fullActive; 169 | } 170 | 171 | RegionStat get(int i) { 172 | return stats.get(i); 173 | } 174 | 175 | @Override 176 | public long time() { 177 | return time; 178 | } 179 | 180 | @Override 181 | public boolean equals(Object o) { 182 | if (this == o) return true; 183 | if (o == null || getClass() != o.getClass()) return false; 184 | 185 | Snapshot snapshot = (Snapshot) o; 186 | 187 | if (time != snapshot.time) return false; 188 | if (!stats.equals(snapshot.stats)) return false; 189 | return youngPhase == snapshot.youngPhase 190 | && globalPhase == snapshot.globalPhase 191 | && oldPhase == snapshot.oldPhase; 192 | } 193 | 194 | @Override 195 | public int hashCode() { 196 | int result = (int) (time ^ (time >>> 32)); 197 | result = 31 * result + stats.hashCode(); 198 | result = 31 * result + youngPhase.hashCode(); 199 | result = 31 * result + oldPhase.hashCode(); 200 | result = 31 * result + globalPhase.hashCode(); 201 | return result; 202 | } 203 | 204 | int regionCount() { 205 | return stats.size(); 206 | } 207 | 208 | long total() { 209 | return regionSize * regionCount(); 210 | } 211 | 212 | long used() { 213 | long used = 0L; 214 | for (RegionStat rs : stats) { 215 | used += regionSize * rs.used(); 216 | } 217 | return used; 218 | } 219 | 220 | long generationStat(RegionAffiliation affiliation, Function stat) { 221 | long used = 0L; 222 | for (RegionStat rs : stats) { 223 | if (rs.affiliation() == affiliation) { 224 | used += regionSize * stat.apply(rs); 225 | } 226 | } 227 | return used; 228 | } 229 | 230 | long collectionSet() { 231 | long used = 0L; 232 | for (RegionStat rs : stats) { 233 | if (rs.state() == RegionState.CSET || rs.state() == RegionState.PINNED_CSET) { 234 | used += regionSize * rs.live(); 235 | } 236 | } 237 | return used; 238 | } 239 | 240 | long live() { 241 | long live = 0L; 242 | for (RegionStat rs : stats) { 243 | live += regionSize * rs.live(); 244 | } 245 | return live; 246 | } 247 | 248 | double percentageOfOldRegionsInCollectionSet() { 249 | long totalInCset = 0, oldInCset = 0; 250 | for (RegionStat rs : stats) { 251 | if (rs.state() == RegionState.CSET || rs.state() == RegionState.PINNED_CSET) { 252 | if (rs.affiliation() == RegionAffiliation.OLD) { 253 | ++oldInCset; 254 | } 255 | ++totalInCset; 256 | } 257 | } 258 | return totalInCset == 0 ? 0 : ((double) (oldInCset)) / totalInCset; 259 | } 260 | 261 | private void stateCounter() { 262 | emptyUncommittedCount = 0; 263 | emptyCommittedCount = 0; 264 | trashCount = 0; 265 | tlabCount = 0; 266 | gclabCount = 0; 267 | plabCount = 0; 268 | sharedCount = 0; 269 | humongousCount = 0; 270 | pinnedHumongousCount = 0; 271 | cSetCount = 0; 272 | pinnedCount = 0; 273 | pinnedCSetCount = 0; 274 | age0Count = 0; 275 | age3Count = 0; 276 | age6Count = 0; 277 | age9Count = 0; 278 | age12Count = 0; 279 | age15Count = 0; 280 | for (RegionStat rs : stats) { 281 | switch (rs.state()) { 282 | case EMPTY_UNCOMMITTED: 283 | emptyUncommittedCount++; 284 | break; 285 | case EMPTY_COMMITTED: 286 | emptyCommittedCount++; 287 | break; 288 | case TRASH: 289 | trashCount++; 290 | break; 291 | case REGULAR: 292 | if (rs.affiliation() == RegionAffiliation.YOUNG) { 293 | if (rs.maxAllocsYoung() == rs.tlabAllocs()) { 294 | tlabCount++; 295 | } 296 | if ((rs.maxAllocsYoung() == rs.gclabAllocs()) && (rs.maxAllocsYoung() > rs.tlabAllocs())) { 297 | gclabCount++; 298 | } 299 | if (((rs.maxAllocsYoung() == rs.sharedAllocs()) && (rs.maxAllocsYoung() > rs.tlabAllocs()) && (rs.maxAllocsYoung() > rs.gclabAllocs()))) { 300 | sharedCount++; 301 | } 302 | } 303 | if (rs.affiliation() == RegionAffiliation.OLD) { 304 | if (rs.maxAllocsOld() == rs.plabAllocs()) { 305 | plabCount++; 306 | } 307 | if ((rs.maxAllocsOld() == rs.sharedAllocs()) && (rs.maxAllocsOld() > rs.plabAllocs())) { 308 | sharedCount++; 309 | } 310 | } 311 | break; 312 | case HUMONGOUS: 313 | humongousCount++; 314 | break; 315 | case PINNED_HUMONGOUS: 316 | pinnedHumongousCount++; 317 | break; 318 | case CSET: 319 | cSetCount++; 320 | break; 321 | case PINNED: 322 | pinnedCount++; 323 | break; 324 | case PINNED_CSET: 325 | pinnedCSetCount++; 326 | break; 327 | } 328 | if (rs.age() >= 0 && rs.age() < 3) { 329 | age0Count++; 330 | } 331 | if (rs.age() >= 3 && rs.age() < 6) { 332 | age3Count++; 333 | } 334 | if (rs.age() >= 6 && rs.age() < 9) { 335 | age6Count++; 336 | } 337 | if (rs.age() >= 9 && rs.age() < 12) { 338 | age9Count++; 339 | } 340 | if (rs.age() >= 12 && rs.age() < 15) { 341 | age12Count++; 342 | } 343 | if (rs.age() >= 15) { 344 | age15Count++; 345 | } 346 | } 347 | } 348 | int getEmptyUncommittedCount() { 349 | return emptyUncommittedCount; 350 | } 351 | int getEmptyCommittedCount() { 352 | return emptyCommittedCount; 353 | } 354 | int getTrashCount() { 355 | return trashCount; 356 | } 357 | int getTlabCount() { 358 | return tlabCount; 359 | } 360 | int getGclabCount() { 361 | return gclabCount; 362 | } 363 | int getPlabCount() { 364 | return plabCount; 365 | } 366 | int getSharedCount() { 367 | return sharedCount; 368 | } 369 | int getHumongousCount() { 370 | return humongousCount; 371 | } 372 | int getPinnedHumongousCount() { 373 | return pinnedHumongousCount; 374 | } 375 | int getCSetCount() { 376 | return cSetCount; 377 | } 378 | int getPinnedCount() { 379 | return pinnedCount; 380 | } 381 | int getPinnedCSetCount() { 382 | return pinnedCSetCount; 383 | } 384 | int getAge0Count() { 385 | return age0Count; 386 | } 387 | int getAge3Count() { 388 | return age3Count; 389 | } 390 | int getAge6Count() { 391 | return age6Count; 392 | } 393 | int getAge9Count() { 394 | return age9Count; 395 | } 396 | int getAge12Count() { 397 | return age12Count; 398 | } 399 | int getAge15Count() { 400 | return age15Count; 401 | } 402 | int statsSize() { 403 | return stats.size(); 404 | } 405 | } 406 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/StatusPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Red Hat, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.HdrHistogram.Histogram; 28 | 29 | import javax.swing.*; 30 | import java.awt.*; 31 | 32 | import static org.openjdk.shenandoah.LayoutConstants.LINE; 33 | 34 | class StatusPanel extends JPanel { 35 | static final int KILO = 1024; 36 | private final RenderRunner renderRunner; 37 | 38 | StatusPanel(RenderRunner renderRunner) { 39 | this.renderRunner = renderRunner; 40 | } 41 | 42 | static void renderTimeLineLegendItem(Graphics g, Color color, int lineNumber, String label) { 43 | g.setColor(color); 44 | int y = (int) (lineNumber * LINE * 1.5); 45 | g.fillRect(0, y, LayoutConstants.LINE, LayoutConstants.LINE); 46 | g.setColor(Color.BLACK); 47 | g.drawString(label, (int) (LayoutConstants.LINE * 1.5), y + LayoutConstants.LINE); 48 | } 49 | 50 | @Override 51 | public void paint(Graphics g) { 52 | Snapshot snapshot = renderRunner.snapshot(); 53 | String mode = snapshot.collectionMode(); 54 | String status = getStatus(snapshot); 55 | 56 | int line = 0; 57 | 58 | g.setColor(Color.BLACK); 59 | g.drawString("GC: " + status, 0, ++line * LINE); 60 | g.drawString("Mode: " + mode, 0, ++line * LINE); 61 | g.drawString("Total: " + (snapshot.total() / KILO) + " MB", 0, ++line * LINE); 62 | g.drawString(usageStatusLine(snapshot), 0, ++line * LINE); 63 | g.drawString(liveStatusLine(snapshot), 0, ++line * LINE); 64 | 65 | Histogram histogram = snapshot.getSafepointTime(); 66 | if (histogram != null) { 67 | String pausesText = String.format("GC Pauses: P100=%d, P95=%d, P90=%d", 68 | histogram.getMaxValue(), histogram.getValueAtPercentile(95), histogram.getValueAtPercentile(90)); 69 | g.drawString(pausesText, 0, ++line * LINE); 70 | } 71 | 72 | renderTimeLineLegendItem(g, Colors.OLD[1], ++line, "Old Marking (OM)"); 73 | renderTimeLineLegendItem(g, Colors.YOUNG[1], ++line, "Young Marking (M)"); 74 | renderTimeLineLegendItem(g, Colors.YOUNG[2], ++line, "Young Evacuation (E)"); 75 | renderTimeLineLegendItem(g, Colors.YOUNG[3], ++line, "Young Update References (UR)"); 76 | 77 | renderTimeLineLegendItem(g, Colors.GLOBAL[1], ++line, "Global Marking (M)"); 78 | renderTimeLineLegendItem(g, Colors.GLOBAL[2], ++line, "Global Evacuation (E)"); 79 | renderTimeLineLegendItem(g, Colors.GLOBAL[3], ++line, "Global Update References (UR)"); 80 | 81 | renderTimeLineLegendItem(g, Colors.DEGENERATE, ++line, "Degenerated Cycle"); 82 | renderTimeLineLegendItem(g, Colors.FULL, ++line, "Full"); 83 | } 84 | 85 | private static String getStatus(Snapshot snapshot) { 86 | switch (snapshot.phase()) { 87 | case IDLE: 88 | return " Idle"; 89 | case EVACUATING: 90 | return " Evacuating"; 91 | case UPDATE_REFS: 92 | return " Updating Refs"; 93 | case UNKNOWN: 94 | return " Unknown"; 95 | case MARKING: 96 | String status = " Marking"; 97 | if (snapshot.getOldPhase() == Phase.MARKING) { 98 | status += " (Old)"; 99 | } 100 | return status; 101 | default: 102 | throw new IllegalArgumentException("Unknown phase: " + snapshot.phase()); 103 | } 104 | } 105 | 106 | protected String liveStatusLine(Snapshot snapshot) { 107 | return "Live (Green): MB: T:" + 108 | snapshot.live() / KILO + " Y:" + 109 | snapshot.generationStat(RegionAffiliation.YOUNG, RegionStat::live) / KILO + " O:" + 110 | snapshot.generationStat(RegionAffiliation.OLD, RegionStat::live) / KILO; 111 | } 112 | 113 | protected String usageStatusLine(Snapshot snapshot) { 114 | return "Used (White): MB: T:" + 115 | snapshot.used() / KILO + " Y:" + 116 | snapshot.generationStat(RegionAffiliation.YOUNG, RegionStat::used) / KILO + " O:" + 117 | snapshot.generationStat(RegionAffiliation.OLD, RegionStat::used) / KILO; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/Timed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | interface Timed { 28 | long time(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/openjdk/shenandoah/ToolbarPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import javax.swing.*; 28 | import javax.swing.event.ChangeEvent; 29 | import java.awt.*; 30 | import java.awt.event.ActionEvent; 31 | import java.awt.event.ActionListener; 32 | import java.awt.event.KeyAdapter; 33 | import java.text.ParseException; 34 | 35 | class ToolbarPanel extends JPanel 36 | implements ActionListener { 37 | 38 | private static final String BACK_1 = "Step back 1"; 39 | private static final String BACK_5 = "Step back 5"; 40 | private static final String PLAY_PAUSE = "Pause"; 41 | private static final String FORWARD_1 = "Step forward 1"; 42 | private static final String FORWARD_5 = "Step forward 5"; 43 | private static final String END_SNAPSHOT = "End snapshot"; 44 | 45 | private static final String REALTIME = "Realtime"; 46 | private static final String CHOOSE_FILE = "choose file"; 47 | 48 | private static final String SPEED_0_5 = "Multiplied speed by 0.5"; 49 | private static final String SPEED_2 = "Multiplied speed by 2"; 50 | private static final String SPEED_RESET = "Reset speed to 1"; 51 | private final RenderRunner renderRunner; 52 | private final JToolBar replayToolbar, statusToolbar, speedToolbar; 53 | private JButton backOneButton, backFiveButton, playPauseButton, forwardOneButton, forwardFiveButton, endSnapshotButton; 54 | private final JButton realtimeModeButton; 55 | private JButton halfSpeedButton, doubleSpeedButton, resetSpeedMultiplierButton; 56 | private JSpinner speedSpinner; 57 | private JSpinner.NumberEditor speedEditor; 58 | private final JTextField fileNameField, lastActionField, modeField, timestampField; 59 | private final JSlider slider; 60 | 61 | boolean speedButtonPressed = false; 62 | 63 | 64 | ToolbarPanel(RenderRunner renderRunner, KeyAdapter keyShortcutAdapter) { 65 | super(new GridBagLayout()); 66 | 67 | this.renderRunner = renderRunner; 68 | 69 | addKeyListener(keyShortcutAdapter); 70 | 71 | JToolBar fileToolbar = new JToolBar(); 72 | fileToolbar.setFloatable(false); 73 | 74 | var history = new RegionHistory(renderRunner, keyShortcutAdapter); 75 | renderRunner.addPopup(history); 76 | 77 | var historyButton = new JButton("Show History"); 78 | historyButton.addActionListener(e -> history.setVisible(true)); 79 | fileToolbar.add(historyButton); 80 | 81 | realtimeModeButton = new JButton("Switch to Live"); 82 | realtimeModeButton.setActionCommand(REALTIME); 83 | realtimeModeButton.addActionListener(this); 84 | realtimeModeButton.setFocusable(false); 85 | fileToolbar.add(realtimeModeButton); 86 | 87 | JButton fileButton = new JButton("Load file"); 88 | fileButton.setActionCommand(CHOOSE_FILE); 89 | fileButton.addActionListener(this); 90 | fileButton.setFocusable(false); 91 | fileToolbar.add(fileButton); 92 | 93 | fileNameField = new JTextField(); 94 | fileNameField.setEditable(false); 95 | fileNameField.setFocusable(false); 96 | fileToolbar.add(fileNameField); 97 | 98 | slider = new JSlider(); 99 | slider.setMinimum(0); 100 | slider.setOrientation(SwingConstants.HORIZONTAL); 101 | slider.setValue(0); 102 | slider.setFocusable(false); 103 | renderRunner.onRecordingLoaded(() -> { 104 | SwingUtilities.invokeLater(() -> { 105 | slider.setMaximum(renderRunner.snapshotCount()); 106 | }); 107 | }); 108 | 109 | replayToolbar = new JToolBar(); 110 | replayToolbar.setFloatable(false); 111 | replayToolbar.setPreferredSize(new Dimension(400, 10)); 112 | 113 | statusToolbar = new JToolBar(); 114 | statusToolbar.setFloatable(false); 115 | 116 | speedToolbar = new JToolBar(); 117 | speedToolbar.setFloatable(false); 118 | 119 | JToolBar timestampToolBar = new JToolBar(); 120 | timestampToolBar.setFloatable(false); 121 | 122 | JLabel lastActionLabel = new JLabel("Last action:"); 123 | statusToolbar.add(lastActionLabel); 124 | 125 | lastActionField = new JTextField(); 126 | lastActionField.setEditable(false); 127 | lastActionField.setFocusable(false); 128 | statusToolbar.add(lastActionField); 129 | 130 | JLabel modeLabel = new JLabel("Mode:"); 131 | statusToolbar.add(modeLabel); 132 | 133 | modeField = new JTextField(); 134 | modeField.setEditable(false); 135 | modeField.setFocusable(false); 136 | statusToolbar.add(modeField); 137 | 138 | JLabel timestampLabel = new JLabel("Timestamp: "); 139 | timestampToolBar.add(timestampLabel); 140 | 141 | timestampField = new JTextField(); 142 | timestampField.setEditable(false); 143 | timestampField.setFocusable(false); 144 | timestampToolBar.add(timestampField); 145 | 146 | addPlaybackButtons(); 147 | addSpeedButtons(); 148 | 149 | setEnabledPlaybackToolbars(); 150 | 151 | { 152 | GridBagConstraints c = new GridBagConstraints(); 153 | c.fill = GridBagConstraints.BOTH; 154 | c.gridx = 0; 155 | c.gridy = 0; 156 | c.weightx = 3; 157 | c.weighty = 1; 158 | add(slider, c); 159 | } 160 | 161 | { 162 | GridBagConstraints c = new GridBagConstraints(); 163 | c.fill = GridBagConstraints.BOTH; 164 | c.gridx = 0; 165 | c.gridy = 1; 166 | c.weightx = 2; 167 | c.weighty = 1; 168 | add(fileToolbar, c); 169 | } 170 | 171 | { 172 | GridBagConstraints c = new GridBagConstraints(); 173 | c.fill = GridBagConstraints.BOTH; 174 | c.gridx = 1; 175 | c.gridy = 0; 176 | c.weightx = 1; 177 | c.weighty = 1; 178 | add(timestampToolBar, c); 179 | } 180 | 181 | { 182 | GridBagConstraints c = new GridBagConstraints(); 183 | c.fill = GridBagConstraints.BOTH; 184 | c.gridx = 1; 185 | c.gridy = 1; 186 | c.weightx = 2; 187 | c.weighty = 1; 188 | add(replayToolbar, c); 189 | } 190 | 191 | { 192 | GridBagConstraints c = new GridBagConstraints(); 193 | c.fill = GridBagConstraints.BOTH; 194 | c.gridx = 0; 195 | c.gridy = 2; 196 | c.weightx = 2; 197 | c.weighty = 1; 198 | add(statusToolbar, c); 199 | } 200 | 201 | { 202 | GridBagConstraints c = new GridBagConstraints(); 203 | c.fill = GridBagConstraints.BOTH; 204 | c.gridx = 1; 205 | c.gridy = 2; 206 | c.weightx = 2; 207 | c.weighty = 1; 208 | add(speedToolbar, c); 209 | } 210 | 211 | realtimeModeButton.addActionListener(event -> { 212 | renderRunner.loadLive(null); 213 | setFileNameField(""); 214 | }); 215 | fileButton.addActionListener(this::onFileButtonEvent); 216 | playPauseButton.addActionListener(this::onPlayPauseEvent); 217 | backOneButton.addActionListener(event -> renderRunner.stepBy(-1)); 218 | backFiveButton.addActionListener(event -> renderRunner.stepBy(-5)); 219 | forwardOneButton.addActionListener(event -> renderRunner.stepBy(1)); 220 | forwardFiveButton.addActionListener(event -> renderRunner.stepBy(5)); 221 | endSnapshotButton.addActionListener(event -> renderRunner.stepToEnd()); 222 | slider.addChangeListener(changeEvent -> renderRunner.stepTo(slider.getValue())); 223 | speedSpinner.addChangeListener(this::onSpeedSpinnerChanged); 224 | halfSpeedButton.addActionListener(event -> { 225 | double speed = Math.max(0.1, renderRunner.getPlaybackSpeed() * 0.5); 226 | changeSpeed(speed); 227 | }); 228 | doubleSpeedButton.addActionListener(event -> { 229 | double speed = Math.max(0.1, renderRunner.getPlaybackSpeed() * 2.0); 230 | changeSpeed(speed); 231 | }); 232 | resetSpeedMultiplierButton.addActionListener(event -> changeSpeed(1.0)); 233 | } 234 | 235 | private void changeSpeed(double speed) { 236 | speedButtonPressed = true; 237 | renderRunner.setPlaybackSpeed(speed); 238 | setSpeedValue(speed); 239 | speedButtonPressed = false; 240 | setLastActionField("Set speed to " + speed); 241 | } 242 | 243 | private void onSpeedSpinnerChanged(ChangeEvent changeEvent) { 244 | if (!speedButtonPressed) { 245 | double speed = getSpeedValue(); 246 | if (speed != renderRunner.getPlaybackSpeed()) { 247 | setLastActionField("Changed playback speed to: " + speed); 248 | renderRunner.setPlaybackSpeed(speed); 249 | } 250 | } 251 | } 252 | 253 | private void setEnabledPlaybackToolbars() { 254 | setEnablePlaybackButtons(); 255 | setEnableSpeedButtons(); 256 | } 257 | 258 | private void setEnablePlaybackButtons() { 259 | backOneButton.setEnabled(true); 260 | backFiveButton.setEnabled(true); 261 | playPauseButton.setEnabled(true); 262 | forwardOneButton.setEnabled(true); 263 | forwardFiveButton.setEnabled(true); 264 | endSnapshotButton.setEnabled(true); 265 | slider.setEnabled(true); 266 | } 267 | 268 | private void setEnableSpeedButtons() { 269 | speedSpinner.setEnabled(true); 270 | halfSpeedButton.setEnabled(true); 271 | doubleSpeedButton.setEnabled(true); 272 | resetSpeedMultiplierButton.setEnabled(true); 273 | } 274 | 275 | private void addPlaybackButtons() { 276 | this.backFiveButton = new JButton("-5"); 277 | backFiveButton.setActionCommand(BACK_5); 278 | backFiveButton.addActionListener(this); 279 | backFiveButton.setFocusable(false); 280 | replayToolbar.add(this.backFiveButton); 281 | 282 | this.backOneButton = new JButton("-1"); 283 | backOneButton.setActionCommand(BACK_1); 284 | backOneButton.addActionListener(this); 285 | backOneButton.setFocusable(false); 286 | replayToolbar.add(this.backOneButton); 287 | 288 | this.playPauseButton = new JButton(PLAY_PAUSE); 289 | playPauseButton.setActionCommand(PLAY_PAUSE); 290 | playPauseButton.addActionListener(this); 291 | playPauseButton.setFocusable(false); 292 | replayToolbar.add(this.playPauseButton); 293 | 294 | this.forwardOneButton = new JButton("+1"); 295 | forwardOneButton.setActionCommand(FORWARD_1); 296 | forwardOneButton.addActionListener(this); 297 | forwardOneButton.setFocusable(false); 298 | replayToolbar.add(this.forwardOneButton); 299 | 300 | this.forwardFiveButton = new JButton("+5"); 301 | forwardFiveButton.setActionCommand(FORWARD_5); 302 | forwardFiveButton.addActionListener(this); 303 | forwardFiveButton.setFocusable(false); 304 | replayToolbar.add(this.forwardFiveButton); 305 | 306 | this.endSnapshotButton = new JButton("End Snapshot"); 307 | endSnapshotButton.setActionCommand(END_SNAPSHOT); 308 | endSnapshotButton.setFocusable(false); 309 | endSnapshotButton.addActionListener(this); 310 | replayToolbar.add(this.endSnapshotButton); 311 | } 312 | 313 | private void addSpeedButtons() { 314 | JLabel speedLabel = new JLabel("Playback Speed: "); 315 | statusToolbar.add(speedLabel); 316 | 317 | this.speedSpinner = new JSpinner(new SpinnerNumberModel(1.0, 0.1, 10.0, 0.1)); 318 | this.speedEditor = new JSpinner.NumberEditor(speedSpinner, "#.#"); 319 | speedSpinner.setEditor(speedEditor); 320 | speedSpinner.setFocusable(false); 321 | speedEditor.setFocusable(false); 322 | statusToolbar.add(speedSpinner); 323 | 324 | halfSpeedButton = new JButton("0.5x"); 325 | halfSpeedButton.setActionCommand(SPEED_0_5); 326 | halfSpeedButton.addActionListener(this); 327 | halfSpeedButton.setFocusable(false); 328 | speedToolbar.add(halfSpeedButton); 329 | 330 | doubleSpeedButton = new JButton("2x"); 331 | doubleSpeedButton.setActionCommand(SPEED_2); 332 | doubleSpeedButton.addActionListener(this); 333 | doubleSpeedButton.setFocusable(false); 334 | speedToolbar.add(doubleSpeedButton); 335 | 336 | resetSpeedMultiplierButton = new JButton("RESET"); 337 | resetSpeedMultiplierButton.setActionCommand(SPEED_RESET); 338 | resetSpeedMultiplierButton.addActionListener(this); 339 | resetSpeedMultiplierButton.setFocusable(false); 340 | speedToolbar.add(resetSpeedMultiplierButton); 341 | } 342 | 343 | double getSpeedValue() { 344 | JFormattedTextField speedField = getTextField(speedSpinner); 345 | if (speedField == null) { 346 | return 0.0; 347 | } 348 | 349 | requestFocusInWindow(); 350 | double speedValue = (double) speedField.getValue(); 351 | try { 352 | speedEditor.commitEdit(); 353 | speedSpinner.commitEdit(); 354 | } catch (ParseException e) { 355 | System.out.println("Speed value must be a double in range (0.0,10.0]. Inputted value: " + speedValue); 356 | } 357 | return speedValue; 358 | } 359 | 360 | void setSpeedValue(double speed) { 361 | JFormattedTextField speedField = getTextField(speedSpinner); 362 | if (speedField == null) { 363 | return; 364 | } 365 | 366 | try { 367 | speedField.setValue(speed); 368 | speedEditor.commitEdit(); 369 | speedSpinner.commitEdit(); 370 | } catch (ParseException e) { 371 | System.out.println("Speed value must be a double in range (0.0,10.0]. Inputted value: " + speed); 372 | } 373 | } 374 | 375 | private JFormattedTextField getTextField(JSpinner spinner) { 376 | JComponent editor = spinner.getEditor(); 377 | if (editor instanceof JSpinner.DefaultEditor) { 378 | return ((JSpinner.DefaultEditor) editor).getTextField(); 379 | } else { 380 | return null; 381 | } 382 | } 383 | 384 | // File Toolbar Listeners 385 | private void onPlayPauseEvent(ActionEvent ae) { 386 | if (renderRunner.isPaused()) { 387 | setLastActionField("Play button pressed."); 388 | } else { 389 | setLastActionField("Pause button pressed."); 390 | } 391 | renderRunner.togglePlayback(); 392 | } 393 | 394 | private void onFileButtonEvent(ActionEvent ae) { 395 | JFileChooser fc = new JFileChooser(); 396 | int returnValue = fc.showOpenDialog(null); 397 | if (returnValue == JFileChooser.APPROVE_OPTION) { 398 | String filePath = fc.getSelectedFile().getAbsolutePath(); 399 | renderRunner.loadPlayback(filePath); 400 | renderRunner.setPlaybackSpeed(1.0); 401 | setSpeedValue(1.0); 402 | setFileNameField(filePath); 403 | setLastActionField("File selected: " + filePath); 404 | } 405 | } 406 | 407 | void setFileNameField(String s) { 408 | fileNameField.setText(s); 409 | } 410 | 411 | void setLastActionField(String s) { 412 | lastActionField.setText(s); 413 | } 414 | 415 | public void actionPerformed(ActionEvent a) { 416 | String cmd = a.getActionCommand(); 417 | if (CHOOSE_FILE.equals(cmd)) { 418 | setLastActionField("Switched to recorded playback mode."); 419 | } else if (REALTIME.equals(cmd)) { 420 | setLastActionField("Switched to live playback mode."); 421 | } else if (!(PLAY_PAUSE.equals(cmd) || SPEED_0_5.equals(cmd) || SPEED_2.equals(cmd) || SPEED_RESET.equals(cmd))) { 422 | setLastActionField(cmd + " button pressed."); 423 | } 424 | } 425 | 426 | @Override 427 | public void paint(Graphics g) { 428 | timestampField.setText(renderRunner.snapshot().time() + " ms"); 429 | slider.setValue(renderRunner.cursor()); 430 | realtimeModeButton.setEnabled(!renderRunner.isLive()); 431 | modeField.setText(renderRunner.status()); 432 | playPauseButton.setText(renderRunner.isPaused() ? "Play" : "Pause"); 433 | super.paint(g); 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/CircularBufferTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.junit.Before; 28 | import org.junit.Test; 29 | 30 | import java.util.Arrays; 31 | import java.util.Collections; 32 | 33 | import static org.junit.Assert.*; 34 | 35 | public class CircularBufferTest { 36 | 37 | private CircularBuffer buffer; 38 | 39 | @Before 40 | public void setUp() { 41 | buffer = new CircularBuffer<>(); 42 | } 43 | 44 | @Test 45 | public void testCreateIsEmpty() { 46 | assertTrue(buffer.isEmpty()); 47 | } 48 | 49 | @Test 50 | public void testAddItem() { 51 | buffer.add('A'); 52 | assertFalse(buffer.isEmpty()); 53 | assertEquals(Character.valueOf('A'), buffer.get(0)); 54 | } 55 | 56 | @Test 57 | public void testWrapAround() { 58 | for (int i = 0; i < CircularBuffer.DEFAULT_SIZE + 1; i++) { 59 | buffer.add((char) ('A' + i)); 60 | assertFalse(buffer.isEmpty()); 61 | } 62 | assertEquals(Character.valueOf('B'), buffer.get(0)); 63 | assertEquals(Character.valueOf('I'), buffer.get(CircularBuffer.DEFAULT_SIZE - 1)); 64 | } 65 | 66 | @Test 67 | public void testSubList() { 68 | for (int i = 0; i < CircularBuffer.DEFAULT_SIZE + 1; i++) { 69 | buffer.add((char) ('A' + i)); 70 | } 71 | 72 | // [I, B, C, D, E, F, G, H] 73 | assertEquals(Collections.emptyList(), buffer.subList(1, 1)); 74 | assertEquals(Arrays.asList('B', 'C', 'D'), buffer.subList(0, 3)); 75 | assertEquals(Arrays.asList('C', 'D', 'E'), buffer.subList(1, 4)); 76 | assertEquals(Arrays.asList('G', 'H', 'I'), buffer.subList(5, 8)); 77 | } 78 | 79 | @Test 80 | public void testGetEnd() { 81 | for (int i = 0; i < CircularBuffer.DEFAULT_SIZE + 1; i++) { 82 | char c = (char) ('A' + i); 83 | buffer.add(c); 84 | assertEquals(String.valueOf(c), String.valueOf(buffer.get(buffer.size() - 1))); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/CounterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.HdrHistogram.Histogram; 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | import static org.openjdk.shenandoah.RegionAffiliation.OLD; 36 | import static org.openjdk.shenandoah.RegionAffiliation.YOUNG; 37 | import static org.openjdk.shenandoah.RegionState.*; 38 | 39 | public class CounterTest { 40 | List emptyUncommittedAge0 = new ArrayList<>(); 41 | List emptyCommittedAge3 = new ArrayList<>(); 42 | List trashAge6 = new ArrayList<>(); 43 | List humongousAge9 = new ArrayList<>(); 44 | List pinnedHumongousAge12 = new ArrayList<>(); 45 | List cSetAge15 = new ArrayList<>(); 46 | List pinned = new ArrayList<>(); 47 | List pinnedCSet = new ArrayList<>(); 48 | List tLab = new ArrayList<>(); 49 | List gcLab = new ArrayList<>(); 50 | List pLab = new ArrayList<>(); 51 | List sharedLabYoung = new ArrayList<>(); 52 | List sharedLabOld = new ArrayList<>(); 53 | 54 | @Before 55 | public void setup() { 56 | for (int i = 0; i < 10; i++) { 57 | emptyUncommittedAge0.add(new RegionStat(EMPTY_UNCOMMITTED, 0)); 58 | emptyCommittedAge3.add(new RegionStat(EMPTY_COMMITTED, 3)); 59 | trashAge6.add(new RegionStat(TRASH, 6)); 60 | humongousAge9.add(new RegionStat(HUMONGOUS, 9)); 61 | pinnedHumongousAge12.add(new RegionStat(PINNED_HUMONGOUS, 12)); 62 | cSetAge15.add(new RegionStat(CSET, 15)); 63 | pinned.add(new RegionStat(PINNED, 0)); 64 | pinnedCSet.add(new RegionStat(PINNED_CSET, 0)); 65 | tLab.add(new RegionStat(1.0f, 1.0f, 0.3f, 0.3f, 0.5f, 0.3f, YOUNG, REGULAR)); 66 | gcLab.add(new RegionStat(1.0f, 1.0f, 0.2f, 0.3f, 0.5f, 0.3f, YOUNG, REGULAR)); 67 | pLab.add(new RegionStat(1.0f, 1.0f, 0.7f, 0.8f, 0.9f, 0.5f, OLD, REGULAR)); 68 | sharedLabYoung.add(new RegionStat(1.0f, 1.0f, 0.2f, 0.3f, 0.5f, 0.7f, YOUNG, REGULAR)); 69 | sharedLabOld.add(new RegionStat(1.0f, 1.0f, 0.2f, 0.9f, 0.5f, 0.7f, OLD, REGULAR)); 70 | } 71 | } 72 | 73 | @Test 74 | public void testEmptyUncommittedCounter() { 75 | Snapshot snapshot = new Snapshot(0, 1024, 1, emptyUncommittedAge0, 0, new Histogram(2)); 76 | Assert.assertEquals(snapshot.getEmptyUncommittedCount(), 10); 77 | } 78 | @Test 79 | public void testEmptyCommittedCounter() { 80 | Snapshot snapshot = new Snapshot(0, 1024, 1, emptyCommittedAge3, 0, new Histogram(2)); 81 | Assert.assertEquals(snapshot.getEmptyCommittedCount(), 10); 82 | } 83 | @Test 84 | public void testTrashCounter() { 85 | Snapshot snapshot = new Snapshot(0, 1024, 1, trashAge6, 0, new Histogram(2)); 86 | Assert.assertEquals(snapshot.getTrashCount(), 10); 87 | } 88 | @Test 89 | public void testHumongousCounter() { 90 | Snapshot snapshot = new Snapshot(0, 1024, 1, humongousAge9, 0, new Histogram(2)); 91 | Assert.assertEquals(snapshot.getHumongousCount(), 10); 92 | } 93 | @Test 94 | public void testPinnedHumongousCounter() { 95 | Snapshot snapshot = new Snapshot(0, 1024, 1, pinnedHumongousAge12, 0, new Histogram(2)); 96 | Assert.assertEquals(snapshot.getPinnedHumongousCount(), 10); 97 | } 98 | @Test 99 | public void testCSetCounter() { 100 | Snapshot snapshot = new Snapshot(0, 1024, 1, cSetAge15, 0, new Histogram(2)); 101 | Assert.assertEquals(snapshot.getCSetCount(), 10); 102 | } 103 | @Test 104 | public void testPinnedCounter() { 105 | Snapshot snapshot = new Snapshot(0, 1024, 1, pinned, 0, new Histogram(2)); 106 | Assert.assertEquals(snapshot.getPinnedCount(), 10); 107 | } 108 | @Test 109 | public void testPinnedCSetCounter() { 110 | Snapshot snapshot = new Snapshot(0, 1024, 1, pinnedCSet, 0, new Histogram(2)); 111 | Assert.assertEquals(snapshot.getPinnedCSetCount(), 10); 112 | } 113 | @Test 114 | public void testAge0Counter() { 115 | Snapshot snapshot = new Snapshot(0, 1024, 1, emptyUncommittedAge0, 0, new Histogram(2)); 116 | Assert.assertEquals(snapshot.getAge0Count(), 10); 117 | } 118 | @Test 119 | public void testAge3Counter() { 120 | Snapshot snapshot = new Snapshot(0, 1024, 1, emptyCommittedAge3, 0, new Histogram(2)); 121 | Assert.assertEquals(snapshot.getAge3Count(), 10); 122 | } 123 | @Test 124 | public void testAge6Counter() { 125 | Snapshot snapshot = new Snapshot(0, 1024, 1, trashAge6, 0, new Histogram(2)); 126 | Assert.assertEquals(snapshot.getAge6Count(), 10); 127 | } 128 | @Test 129 | public void testAge9Counter() { 130 | Snapshot snapshot = new Snapshot(0, 1024, 1, humongousAge9, 0, new Histogram(2)); 131 | Assert.assertEquals(snapshot.getAge9Count(), 10); 132 | } 133 | @Test 134 | public void testAge12Counter() { 135 | Snapshot snapshot = new Snapshot(0, 1024, 1, pinnedHumongousAge12, 0, new Histogram(2)); 136 | Assert.assertEquals(snapshot.getAge12Count(), 10); 137 | } 138 | @Test 139 | public void testAge15Counter() { 140 | Snapshot snapshot = new Snapshot(0, 1024, 1, cSetAge15, 0, new Histogram(2)); 141 | Assert.assertEquals(snapshot.getAge15Count(), 10); 142 | } 143 | @Test 144 | public void testTlabCounter() { 145 | Snapshot snapshot = new Snapshot(0, 1024, 1, tLab, 0, new Histogram(2)); 146 | Assert.assertEquals(snapshot.getTlabCount(), 10); 147 | } 148 | @Test 149 | public void testGClabCounter() { 150 | Snapshot snapshot = new Snapshot(0, 1024, 1, gcLab, 0, new Histogram(2)); 151 | Assert.assertEquals(snapshot.getGclabCount(), 10); 152 | } 153 | @Test 154 | public void testPlabCounter() { 155 | Snapshot snapshot = new Snapshot(0, 1024, 1, pLab, 0, new Histogram(2)); 156 | Assert.assertEquals(snapshot.getPlabCount(), 10); 157 | } 158 | @Test 159 | public void testSharedCounter() { 160 | Snapshot snapshotYoung = new Snapshot(0, 1024, 1, sharedLabYoung, 0, new Histogram(2)); 161 | Snapshot snapshotOld = new Snapshot(0, 1024, 1, sharedLabOld, 0, new Histogram(2)); 162 | Assert.assertEquals(snapshotYoung.getSharedCount(), 10); 163 | Assert.assertEquals(snapshotOld.getSharedCount(), 10); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/DecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | 28 | import org.HdrHistogram.Histogram; 29 | import org.junit.Assert; 30 | import org.junit.Test; 31 | 32 | import java.util.Collections; 33 | 34 | 35 | 36 | public class DecoderTest { 37 | 38 | // Testing different phases for version 1 encoding 39 | @Test 40 | public void testVersion1PhaseIdle() { 41 | var snapshotVersion1Idle = new Snapshot(0, 1024, 1, Collections.emptyList(), 0, new Histogram(2)); 42 | Assert.assertEquals(Phase.IDLE, snapshotVersion1Idle.phase()); 43 | } 44 | @Test 45 | public void testVersion1PhaseMarking() { 46 | var snapshotVersion1Marking = new Snapshot(0, 1024, 1, Collections.emptyList(), 1, new Histogram(2)); 47 | Assert.assertEquals(Phase.MARKING, snapshotVersion1Marking.phase()); 48 | } 49 | @Test 50 | public void testVersion1PhaseEvacuating() { 51 | var snapshotVersion1Evacuating = new Snapshot(0, 1024, 1, Collections.emptyList(), 2, new Histogram(2)); 52 | Assert.assertEquals(Phase.EVACUATING, snapshotVersion1Evacuating.phase()); 53 | } 54 | @Test 55 | public void testVersion1PhaseUpdateRefs() { 56 | var snapshotVersion1UpdateRefs = new Snapshot(0, 1024, 1, Collections.emptyList(), 4, new Histogram(2)); 57 | Assert.assertEquals(Phase.UPDATE_REFS, snapshotVersion1UpdateRefs.phase()); 58 | } 59 | 60 | // Testing different phases for version 2 encoding 61 | @Test 62 | public void testVersion2PhaseIdle() { 63 | var snapshotNewIdle = new Snapshot(0, 1024, 2, Collections.emptyList(), 0, new Histogram(2)); 64 | Assert.assertEquals(Phase.IDLE, snapshotNewIdle.phase()); 65 | } 66 | @Test 67 | public void testVersion2PhaseMarking() { 68 | var snapshotNewMarking = new Snapshot(0, 1024, 2, Collections.emptyList(), 1, new Histogram(2)); 69 | Assert.assertEquals(Phase.MARKING, snapshotNewMarking.phase()); 70 | } 71 | @Test 72 | public void testVersion2PhaseEvacuating() { 73 | var snapshotNewEvacuating = new Snapshot(0, 1024, 2, Collections.emptyList(), 2, new Histogram(2)); 74 | Assert.assertEquals(Phase.EVACUATING, snapshotNewEvacuating.phase()); 75 | } 76 | @Test 77 | public void testVersion2PhaseUpdateRefs() { 78 | var snapshotNewUpdateRefs = new Snapshot(0, 1024, 2, Collections.emptyList(), 3, new Histogram(2)); 79 | Assert.assertEquals(Phase.UPDATE_REFS, snapshotNewUpdateRefs.phase()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/EventLogTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.junit.Assert; 28 | import org.junit.Test; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Collections; 32 | import java.util.List; 33 | import java.util.concurrent.TimeUnit; 34 | 35 | public class EventLogTest { 36 | 37 | private record Event(long time) implements Timed { } 38 | 39 | @Test 40 | public void testThatEmptyLogHasEmptyRange() { 41 | Assert.assertEquals(Collections.emptyList(), new EventLog().inRange()); 42 | } 43 | 44 | @Test 45 | public void testFetchingLatest() { 46 | EventLog log = createEventLog(1, 2, 3); 47 | log.stepBy(1); 48 | Assert.assertEquals(new Event(1), log.current()); 49 | } 50 | @Test 51 | public void testThatSteppingForwardIncreasesRange() { 52 | EventLog log = createEventLog(1, 2, 3); 53 | log.stepBy(1); 54 | Assert.assertEquals(createEvents(1), log.inRange()); 55 | log.stepBy(10); 56 | Assert.assertEquals(createEvents(1, 2, 3), log.inRange()); 57 | } 58 | 59 | @Test 60 | public void testThatSteppingBackwardDecreasesRange() { 61 | EventLog log = createEventLog(1, 2, 3); 62 | log.stepBy(10); 63 | Assert.assertEquals(createEvents(1, 2, 3), log.inRange()); 64 | log.stepBy(-1); 65 | Assert.assertEquals(createEvents(1, 2), log.inRange()); 66 | } 67 | 68 | @Test 69 | public void testAdvancingTimeIncreasesRange() { 70 | EventLog log = createEventLog(100, 200, 300); 71 | log.advanceTo(50, TimeUnit.NANOSECONDS); 72 | Assert.assertEquals(Collections.emptyList(), log.inRange()); 73 | log.advanceTo(150, TimeUnit.NANOSECONDS); 74 | Assert.assertEquals(createEvents(100), log.inRange()); 75 | log.advanceTo(500, TimeUnit.NANOSECONDS); 76 | Assert.assertEquals(createEvents(100, 200, 300), log.inRange()); 77 | } 78 | 79 | @Test 80 | public void testIncrementalAdvanceIncreasesRange() { 81 | // reference time is the time of the first event 82 | EventLog log = createEventLog(100, 200, 300); 83 | log.advanceBy(50, TimeUnit.NANOSECONDS); 84 | Assert.assertEquals(createEvents(100), log.inRange()); // ref time is 150 85 | log.advanceBy(25, TimeUnit.NANOSECONDS); // ref time is 175 86 | Assert.assertEquals(createEvents(100), log.inRange()); 87 | log.advanceBy(25, TimeUnit.NANOSECONDS); // ref time is 200 88 | Assert.assertEquals(createEvents(100, 200), log.inRange()); 89 | } 90 | 91 | @Test 92 | public void testRangeIncludesEventsInsertedBeforeReferenceTime() { 93 | EventLog log = createEventLog(100, 200, 300); 94 | log.advanceTo(500, TimeUnit.NANOSECONDS); 95 | Assert.assertEquals(createEvents(100, 200, 300), log.inRange()); 96 | log.add(new Event(400)); 97 | log.add(new Event(600)); 98 | Assert.assertEquals(createEvents(100, 200, 300, 400), log.inRange()); 99 | } 100 | 101 | private static List createEvents(int... args) { 102 | var list = new ArrayList(args.length); 103 | for (var t : args) { 104 | list.add(new Event(t)); 105 | } 106 | return list; 107 | } 108 | 109 | private static EventLog createEventLog(int... args) { 110 | var log = new EventLog(); 111 | for (var t : args) { 112 | log.add(new Event(t)); 113 | } 114 | return log; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/PopupSpotlightTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.junit.Test; 28 | 29 | import javax.imageio.ImageIO; 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.awt.image.BufferedImage; 33 | import java.io.File; 34 | import java.io.IOException; 35 | 36 | public class PopupSpotlightTest { 37 | @Test 38 | public void test() throws IOException { 39 | if (!GraphicsEnvironment.isHeadless()) { 40 | BufferedImage img = new BufferedImage(300, 700, BufferedImage.TYPE_INT_RGB); 41 | Graphics2D g = img.createGraphics(); 42 | g.setColor(Color.WHITE); 43 | g.fillRect(0, 0, 300, 700); 44 | RenderRunner renderRunner = new RenderRunner(new JFrame()); 45 | renderRunner.loadPlayback("src/test/resources/regions-6425.log"); 46 | renderRunner.stepBy(200); 47 | new RegionPopUp(0, renderRunner).spotlightPaint(g); 48 | ImageIO.write(img, "png", new File("spotlight.png")); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/PopupTimelineTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.junit.Test; 28 | 29 | import javax.imageio.ImageIO; 30 | import javax.swing.*; 31 | import java.awt.*; 32 | import java.awt.image.BufferedImage; 33 | import java.io.File; 34 | import java.io.IOException; 35 | 36 | public class PopupTimelineTest { 37 | @Test 38 | public void test() throws IOException { 39 | if (!GraphicsEnvironment.isHeadless()) { 40 | BufferedImage img = new BufferedImage(300, 700, BufferedImage.TYPE_INT_RGB); 41 | Graphics2D g = img.createGraphics(); 42 | g.setColor(Color.WHITE); 43 | g.fillRect(0, 0, 300, 700); 44 | RenderRunner renderRunner = new RenderRunner(new JFrame()); 45 | renderRunner.loadPlayback("src/test/resources/regions-6425.log"); 46 | renderRunner.stepBy(200); 47 | new RegionPopUp(0, renderRunner).timelinePaint(g); 48 | ImageIO.write(img, "png", new File("timeline.png")); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/ProcessLoggingTagTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.junit.Assert; 28 | 29 | import org.junit.Test; 30 | 31 | public class ProcessLoggingTagTest { 32 | @Test 33 | public void testParseLogMetaDataWithTags() { 34 | String taggedMetaDataLine = "[1.666s][info][gc,region] 1665172926 0 3859 1024 2"; 35 | Assert.assertEquals("1665172926 0 3859 1024 2", DataLogProvider.processLoggingTag(taggedMetaDataLine)); 36 | } 37 | @Test 38 | public void testParseRegionDataWithTags() { 39 | String taggedRegionDataLine = "[1.667s][info][gc,region] 648518346342989924 648518346342989924 648518346342989924"; 40 | Assert.assertEquals("648518346342989924 648518346342989924 648518346342989924", DataLogProvider.processLoggingTag(taggedRegionDataLine)); 41 | } 42 | @Test 43 | public void testParseMetaDataWithoutTags() { 44 | String notTaggedMetaDataLine = "1665172926 0 3859 1024 2"; 45 | Assert.assertEquals("1665172926 0 3859 1024 2", DataLogProvider.processLoggingTag(notTaggedMetaDataLine)); 46 | } 47 | @Test 48 | public void testParseRegionDataWithoutTags() { 49 | String notTaggedRegionDataLine = "648518346342989924 648518346342989924 648518346342989924"; 50 | Assert.assertEquals("648518346342989924 648518346342989924 648518346342989924", DataLogProvider.processLoggingTag(notTaggedRegionDataLine)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/org/openjdk/shenandoah/RegionSelectionParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Amazon.com, Inc. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package org.openjdk.shenandoah; 26 | 27 | import org.junit.Test; 28 | 29 | import java.util.*; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.fail; 33 | 34 | public class RegionSelectionParserTest { 35 | 36 | @Test 37 | public void testParseSingleNumber() { 38 | RegionSelectionParser parser = new RegionSelectionParser(); 39 | Collection regions = parser.parse("42"); 40 | assertEquals(List.of(42), regions); 41 | } 42 | 43 | @Test 44 | public void testParseCommaDelimited() { 45 | RegionSelectionParser parser = new RegionSelectionParser(); 46 | Collection regions = parser.parse("42, 23"); 47 | assertEquals(Arrays.asList(23, 42), regions); 48 | } 49 | 50 | @Test 51 | public void testParseRange() { 52 | RegionSelectionParser parser = new RegionSelectionParser(); 53 | Collection regions = parser.parse("23 - 29"); 54 | assertEquals(Arrays.asList(23, 24, 25, 26, 27, 28, 29), regions); 55 | } 56 | 57 | @Test 58 | public void testNumbersAndRanges() { 59 | RegionSelectionParser parser = new RegionSelectionParser(); 60 | Collection regions = parser.parse("13, 23 - 29, 25, 42"); 61 | assertEquals(Arrays.asList(13, 23, 24, 25, 26, 27, 28, 29, 42), regions); 62 | } 63 | 64 | @Test 65 | public void testInvalidRangeThrows() { 66 | try { 67 | RegionSelectionParser parser = new RegionSelectionParser(); 68 | parser.parse("29 - 23"); 69 | fail("Should not allow reversed ranges"); 70 | } catch (IllegalArgumentException ignore) {} 71 | } 72 | } 73 | --------------------------------------------------------------------------------