├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main └── java └── co └── paralleluniverse └── xst ├── ASMUtil.java ├── ExtendedStackTrace.java ├── ExtendedStackTraceClassContext.java ├── ExtendedStackTraceElement.java ├── ExtendedStackTraceHotSpot.java ├── UtilUnsafe.java └── package-info.java /.gitignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /out/ 3 | _site/ 4 | /docs/javadoc/ 5 | .idea/ 6 | *.iml 7 | /artifacts 8 | /nbproject/private/ 9 | /.nb-gradle 10 | build/ 11 | private/ 12 | .DS_Store 13 | .gradle/ 14 | .nb-gradle-properties 15 | .bdb/ 16 | log4j.xml 17 | /push_docs.sh -------------------------------------------------------------------------------- /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 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 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., 59 291 | Temple Place, Suite 330, Boston, MA 02111-1307 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 | Linking this library statically or dynamically with other modules is making 329 | a combined work based on this library. Thus, the terms and conditions of 330 | the GNU General Public License cover the whole combination. 331 | 332 | As a special exception, the copyright holders of this library give you 333 | permission to link this library with independent modules to produce an 334 | executable, regardless of the license terms of these independent modules, 335 | and to copy and distribute the resulting executable under terms of your 336 | choice, provided that you also meet, for each linked independent module, 337 | the terms and conditions of the license of that module. An independent 338 | module is a module which is not derived from or based on this library. If 339 | you modify this library, you may extend this exception to your version of 340 | the library, but you are not obligated to do so. If you do not wish to do 341 | so, delete this exception statement from your version. 342 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Extended Stack-Trace
Enhanced stack traces for the JVM 3 | 4 | This library allows you to capture stack traces with more information than `Throwable.getStackTrace`. 5 | In addition to the information in the JDK's `StackTraceElement` The captured stack elements contain: 6 | 7 | * The decalaring class -- the actual `Class` object -- not just the name. 8 | * The method -- the actual `Method` object -- not just the method name. 9 | * The bytecode index 10 | 11 | Works best when running on a Java 8 HotSpot (OpenJDK/Oracle JDK) JVM. 12 | 13 | ## Status 14 | 15 | Advanced Beta. 16 | Used in [Quasar](https://github.com/puniverse/quasar). 17 | 18 | ## Usage 19 | 20 | 1. Clone and build the repository with `./gradlew` or use Maven artifact `co.paralleluniverse:extended-stacktrace:0.1.0-SNAPSHOT` 21 | from the Sonatype snapshot repository (`https://oss.sonatype.org/content/repositories/snapshots`) 22 | 23 | 2. Add the JAR file to your dependencies. 24 | 25 | 3. Obtain the extended stack trace information with `ExtendedStackTrace.here()` or `ExtendedStackTrace.of(Throwable)` 26 | 27 | 28 | Please consult the [Javadocs](http://docs.paralleluniverse.co/extended-stacktrace/javadoc/) for detailed information. 29 | 30 | ## Details 31 | 32 | On the HotSpot JVM (OpenJDK/Oracle JDK) for Java 8, the extended information is always available (via some deep hacking), and obtaining it is as efficient as a plain `Throwable.getStackTrace()`. 33 | 34 | On other JVMs/Java versions the extended information may be incomplete. There are (much) better chances for obtaining extended information when capturing the stack with 35 | `ExtendedStackTrace.here()` than when extracting extended information from a `Throwable` with `ExtendedStackTrace.of(Throwable)`. Also, getting the method object carries a significant cost. 36 | 37 | ## License 38 | 39 | This project is free software free software published under the following license: 40 | 41 | ``` 42 | Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 43 | 44 | This program and the accompanying materials are licensed under 45 | GNU General Public License, version 2, with the Classpath Exception (same as OpenJDK) 46 | 47 | http://openjdk.java.net/legal/gplv2+ce.html 48 | ``` 49 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'maven' 4 | id 'signing' 5 | } 6 | 7 | sourceCompatibility = '1.7' 8 | targetCompatibility = '1.7' 9 | 10 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 11 | 12 | group = "co.paralleluniverse" 13 | version = "0.1.0-SNAPSHOT" 14 | status = "integration" 15 | description = "Stack Traces with Extended Information" 16 | ext.url = "http://puniverse.github.com/extended-stacktrace" 17 | ext.vendor = "Parallel Universe Software Co." 18 | ext.licenseName = "GNU General Public License, version 2, with the Classpath Exception" 19 | ext.licenseUrl = "http://openjdk.java.net/legal/gplv2+ce.html" 20 | ext.scmUrl = "https://github.com/puniverse/extended-stacktrace" 21 | ext.scmConnection = "https://github.com/puniverse/extended-stacktrace.git" 22 | 23 | ext.distDir = "$buildDir/dist" 24 | ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 25 | 26 | repositories { 27 | mavenCentral() 28 | } 29 | 30 | configurations { 31 | provided 32 | compileOnly 33 | } 34 | 35 | sourceSets { 36 | main { 37 | compileClasspath += configurations.compileOnly + configurations.provided 38 | } 39 | 40 | test { 41 | compileClasspath += configurations.compileOnly + configurations.provided 42 | runtimeClasspath += configurations.provided 43 | } 44 | } 45 | 46 | dependencies { 47 | compile "org.ow2.asm:asm:5.0.4" 48 | testCompile 'junit:junit:4.10' 49 | } 50 | 51 | jar { 52 | manifest { 53 | attributes( 54 | "Built-By" : System.getProperty("user.name"), 55 | "Implementation-Title" : project.name, 56 | "Implementation-Version" : version, 57 | "Implementation-Vendor" : vendor, 58 | ) 59 | } 60 | } 61 | 62 | javadoc { 63 | options { 64 | links = [ "http://docs.oracle.com/javase/8/docs/api/" ] 65 | noDeprecated = true 66 | addStringOption('public', '-quiet') 67 | } 68 | } 69 | 70 | task sourcesJar(type: Jar, dependsOn: classes) { 71 | classifier = 'sources' 72 | from sourceSets.main.allSource 73 | } 74 | 75 | task javadocJar(type: Jar, dependsOn: javadoc) { 76 | classifier = 'javadoc' 77 | from javadoc.destinationDir 78 | } 79 | 80 | artifacts { 81 | archives jar 82 | archives sourcesJar 83 | archives javadocJar 84 | } 85 | 86 | signing { 87 | required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") } 88 | sign configurations.archives 89 | } 90 | 91 | if (!project.hasProperty("sonatypeUsername") || !project.hasProperty("sonatypePassword")) { 92 | println "sonatype username or password not set" 93 | ext.sonatypeUsername = "" 94 | ext.sonatypePassword = "" 95 | } 96 | 97 | uploadArchives { 98 | repositories { 99 | mavenDeployer { 100 | beforeDeployment { deployment -> signing.signPom(deployment) } 101 | 102 | repository( 103 | url: (isReleaseVersion ? 104 | "https://oss.sonatype.org/service/local/staging/deploy/maven2" : 105 | "https://oss.sonatype.org/content/repositories/snapshots")) { 106 | // User and Password are taken from ~/.gradle/gradle.properties 107 | authentication(userName: project.sonatypeUsername, password: project.sonatypePassword) 108 | } 109 | pom.project { 110 | name project.name 111 | packaging 'jar' 112 | description project.description 113 | url project.url 114 | scm { 115 | url project.scmUrl 116 | connection project.scmConnection 117 | developerConnection project.scmConnection 118 | } 119 | licenses { 120 | license { 121 | name project.licenseName 122 | url project.licenseUrl 123 | distribution 'repo' 124 | } 125 | } 126 | developers { 127 | developer { 128 | id 'pron' 129 | name 'Ron Pressler' 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | install.repositories.mavenInstaller { 138 | pom.whenConfigured { 139 | it.dependencies.clear() 140 | } 141 | } 142 | 143 | uploadArchives.repositories.mavenDeployer { 144 | pom.whenConfigured { 145 | it.dependencies.clear() 146 | } 147 | } 148 | 149 | task wrapper(type: Wrapper) { 150 | gradleVersion = '2.4' 151 | } 152 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puniverse/extended-stacktrace/09f36cfe298659795943b711ae674343999da07f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jun 22 12:26:58 IDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'extended-stacktrace' 2 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/ASMUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 3 | * 4 | * This program and the accompanying materials are licensed under 5 | * GNU General Public License, version 2, with the Classpath Exception 6 | * 7 | * http://openjdk.java.net/legal/gplv2+ce.html 8 | */ 9 | package co.paralleluniverse.xst; 10 | 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import org.objectweb.asm.ClassReader; 14 | import org.objectweb.asm.ClassVisitor; 15 | 16 | final class ASMUtil { 17 | private static final String CLASS_FILE_NAME_EXTENSION = ".class"; 18 | 19 | public static InputStream getClassInputStream(String className, ClassLoader cl) { 20 | return cl.getResourceAsStream(classToResource(className)); 21 | } 22 | 23 | public static InputStream getClassInputStream(Class clazz) { 24 | final InputStream is = getClassInputStream(clazz.getName(), clazz.getClassLoader()); 25 | if (is == null) 26 | throw new UnsupportedOperationException("Class file " + clazz.getName() + " could not be loaded by the class's classloader " + clazz.getClassLoader()); 27 | return is; 28 | } 29 | 30 | public static T accept(InputStream is, int flags, T visitor) throws IOException { 31 | if (is == null) 32 | return null; 33 | try (InputStream is1 = is) { 34 | new ClassReader(is1).accept(visitor, flags); 35 | return visitor; 36 | } 37 | } 38 | 39 | public static T accept(byte[] buffer, int flags, T visitor) throws IOException { 40 | if (buffer == null) 41 | throw new NullPointerException("Buffer is null"); 42 | new ClassReader(buffer).accept(visitor, flags); 43 | return visitor; 44 | } 45 | 46 | public static T accept(String className, ClassLoader cl, int flags, T visitor) throws IOException { 47 | return accept(getClassInputStream(className, cl), flags, visitor); 48 | } 49 | 50 | public static T accept(Class clazz, int flags, T visitor) throws IOException { 51 | return accept(getClassInputStream(clazz), flags, visitor); 52 | } 53 | 54 | public static String classToResource(String className) { 55 | if (className == null) 56 | return null; 57 | return className.replace('.', '/') + CLASS_FILE_NAME_EXTENSION; 58 | } 59 | 60 | public static String classToResource(Class clazz) { 61 | if (clazz == null) 62 | return null; 63 | return classToResource(clazz.getName()); 64 | } 65 | 66 | public static String classToSlashed(String className) { 67 | if (className == null) 68 | return null; 69 | return className.replace('.', '/'); 70 | } 71 | 72 | private ASMUtil() { 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/ExtendedStackTrace.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 3 | * 4 | * This program and the accompanying materials are licensed under 5 | * GNU General Public License, version 2, with the Classpath Exception 6 | * 7 | * http://openjdk.java.net/legal/gplv2+ce.html 8 | */ 9 | package co.paralleluniverse.xst; 10 | 11 | import java.io.PrintStream; 12 | import java.io.PrintWriter; 13 | import java.lang.reflect.Constructor; 14 | //import java.lang.reflect.Executable; 15 | import java.lang.reflect.Member; 16 | import java.lang.reflect.Method; 17 | import java.util.Arrays; 18 | import java.util.Collections; 19 | import java.util.IdentityHashMap; 20 | import java.util.Iterator; 21 | import java.util.Set; 22 | import java.util.concurrent.atomic.AtomicReference; 23 | import org.objectweb.asm.ClassReader; 24 | import org.objectweb.asm.ClassVisitor; 25 | import org.objectweb.asm.Label; 26 | import org.objectweb.asm.MethodVisitor; 27 | import org.objectweb.asm.Opcodes; 28 | import org.objectweb.asm.Type; 29 | 30 | /** 31 | * Represents a captured stack trace which contains more information than that returned by {@link Throwable#getStackTrace()}. 32 | * The extended information is captured on a best-effort basis, and depends on the JVM used. 33 | * 34 | * @author pron 35 | */ 36 | public class ExtendedStackTrace implements Iterable { 37 | /** 38 | * Returns a stack trace with extended information for the given {@code Throwable}. 39 | * @param t 40 | */ 41 | public static ExtendedStackTrace of(Throwable t) { 42 | if (t == null) 43 | return null; 44 | try { 45 | return new ExtendedStackTraceHotSpot(t); 46 | } catch (Throwable e) { 47 | return new ExtendedStackTrace(t); 48 | } 49 | } 50 | 51 | /** 52 | * Returns a stack trace for the current execution point. 53 | */ 54 | public static ExtendedStackTrace here() { 55 | try { 56 | return new ExtendedStackTraceHotSpot(new Exception("Stack trace")); 57 | } catch (Throwable e) { 58 | return new ExtendedStackTraceClassContext(); 59 | } 60 | } 61 | 62 | protected final Throwable t; 63 | private ExtendedStackTraceElement[] est; 64 | // private transient Map, Member[]> methods; // cache 65 | 66 | protected ExtendedStackTrace(Throwable t) { 67 | this.t = t; 68 | } 69 | 70 | @Override 71 | public Iterator iterator() { 72 | return Arrays.asList(get()).iterator(); 73 | } 74 | 75 | /** 76 | * Returns an array of {@link ExtendedStackTraceElement}s representing the captured stack trace. 77 | */ 78 | public ExtendedStackTraceElement[] get() { 79 | synchronized (this) { 80 | if (est == null) { 81 | StackTraceElement[] st = t.getStackTrace(); 82 | if (st != null) { 83 | est = new ExtendedStackTraceElement[st.length]; 84 | for (int i = 0; i < st.length; i++) 85 | est[i] = new BasicExtendedStackTraceElement(st[i]); 86 | } 87 | } 88 | return est; 89 | } 90 | } 91 | 92 | protected /*Executable*/ Member getMethod(final ExtendedStackTraceElement este) { 93 | if (este.getDeclaringClass() == null) 94 | return null; 95 | Member[] ms = getMethods(este.getDeclaringClass()); 96 | Member method = null; 97 | 98 | for (Member m : ms) { 99 | if (este.getMethodName().equals(m.getName())) { 100 | if (method == null) 101 | method = m; 102 | else { 103 | method = null; // more than one match 104 | break; 105 | } 106 | } 107 | } 108 | if (method == null && este.getLineNumber() >= 0) { 109 | try { 110 | final AtomicReference descriptor = new AtomicReference<>(); 111 | ASMUtil.accept(este.getDeclaringClass(), ClassReader.SKIP_FRAMES, new ClassVisitor(Opcodes.ASM5) { 112 | @Override 113 | public MethodVisitor visitMethod(int access, String name, final String desc, String signature, String[] exceptions) { 114 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 115 | if (descriptor.get() == null && este.getMethodName().equals(name)) { 116 | mv = new MethodVisitor(api, mv) { 117 | int minLine = Integer.MAX_VALUE, maxLine = Integer.MIN_VALUE; 118 | 119 | @Override 120 | public void visitLineNumber(int line, Label start) { 121 | if (line < minLine) 122 | minLine = line; 123 | if (line > maxLine) 124 | maxLine = line; 125 | } 126 | 127 | @Override 128 | public void visitEnd() { 129 | if (minLine <= este.getLineNumber() && maxLine >= este.getLineNumber()) 130 | descriptor.set(desc); 131 | super.visitEnd(); 132 | } 133 | }; 134 | } 135 | return mv; 136 | } 137 | }); 138 | 139 | if (descriptor.get() != null) { 140 | final String desc = descriptor.get(); 141 | for (Member m : ms) { 142 | if (este.getMethodName().equals(getName(m)) && desc.equals(getDescriptor(m))) { 143 | method = m; 144 | break; 145 | } 146 | } 147 | } 148 | } catch (Exception e) { 149 | e.printStackTrace(); 150 | } 151 | } 152 | 153 | return method; 154 | } 155 | 156 | protected static final String getName(Member m) { 157 | if (m instanceof Constructor) 158 | return ""; 159 | return ((Method) m).getName(); 160 | } 161 | 162 | protected static final String getDescriptor(Member m) { 163 | if (m instanceof Constructor) 164 | return Type.getConstructorDescriptor((Constructor) m); 165 | return Type.getMethodDescriptor((Method) m); 166 | } 167 | 168 | protected final Member[] getMethods(Class clazz) { 169 | // synchronized (this) { 170 | Member[] es; 171 | // if (methods == null) 172 | // methods = new HashMap<>(); 173 | // es = methods.get(clazz); 174 | // if (es == null) { 175 | Method[] ms = clazz.getDeclaredMethods(); 176 | Constructor[] cs = clazz.getDeclaredConstructors(); 177 | es = new Member[ms.length + cs.length]; 178 | System.arraycopy(cs, 0, es, 0, cs.length); 179 | System.arraycopy(ms, 0, es, cs.length, ms.length); 180 | 181 | // methods.put(clazz, es); 182 | // } 183 | return es; 184 | // } 185 | } 186 | 187 | protected class BasicExtendedStackTraceElement extends ExtendedStackTraceElement { 188 | protected BasicExtendedStackTraceElement(StackTraceElement ste, Class clazz, Method method, int bci) { 189 | super(ste, clazz, method, bci); 190 | } 191 | 192 | protected BasicExtendedStackTraceElement(StackTraceElement ste, Class clazz) { 193 | super(ste, clazz, null, -1); 194 | } 195 | 196 | protected BasicExtendedStackTraceElement(StackTraceElement ste) { 197 | super(ste, null, null, -1); 198 | } 199 | 200 | @Override 201 | public Member getMethod() { 202 | if (method == null) { 203 | method = ExtendedStackTrace.this.getMethod(this); 204 | if (method != null && !getMethodName().equals(getName(method))) { 205 | throw new IllegalStateException("Method name mismatch: " + getMethodName() + ", " + method.getName()); 206 | // method = null; 207 | } 208 | } 209 | return method; 210 | } 211 | 212 | @Override 213 | public Class getDeclaringClass() { 214 | if (clazz == null) { 215 | try { 216 | clazz = Class.forName(getClassName()); 217 | } catch (ClassNotFoundException e) { 218 | } 219 | } 220 | return clazz; 221 | } 222 | } 223 | 224 | // 225 | /////////// Printing /////////////////////////////////// 226 | /* 227 | * Additional copyright for the printing section, 228 | * which is based on OpenJDK code taken from java/lang/Throwable.java: 229 | */ 230 | /* 231 | * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. 232 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 233 | * 234 | * This code is free software; you can redistribute it and/or modify it 235 | * under the terms of the GNU General Public License version 2 only, as 236 | * published by the Free Software Foundation. Oracle designates this 237 | * particular file as subject to the "Classpath" exception as provided 238 | * by Oracle in the LICENSE file that accompanied this code. 239 | * 240 | * This code is distributed in the hope that it will be useful, but WITHOUT 241 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 242 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 243 | * version 2 for more details (a copy is included in the LICENSE file that 244 | * accompanied this code). 245 | * 246 | * You should have received a copy of the GNU General Public License version 247 | * 2 along with this work; if not, write to the Free Software Foundation, 248 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 249 | * 250 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 251 | * or visit www.oracle.com if you need additional information or have any 252 | * questions. 253 | */ 254 | /** 255 | * Prints this stack trace to {@link System#err}. 256 | */ 257 | public void printStackTrace() { 258 | printStackTrace(System.err); 259 | } 260 | 261 | /** 262 | * Prints this stack trace to the given print stream. 263 | */ 264 | public void printStackTrace(PrintStream s) { 265 | printStackTrace(new WrappedPrintStream(s)); 266 | } 267 | 268 | /** 269 | * Prints this stack trace to the given print writer. 270 | */ 271 | public void printStackTrace(PrintWriter s) { 272 | printStackTrace(new WrappedPrintWriter(s)); 273 | } 274 | 275 | private void printStackTrace(PrintStreamOrWriter s) { 276 | synchronized (s.lock()) { 277 | // Guard against malicious overrides of Throwable.equals by using a Set with identity equality semantics. 278 | printStackTrace(s, null, "", "", Collections.newSetFromMap(new IdentityHashMap())); 279 | } 280 | } 281 | 282 | private void printStackTrace(PrintStreamOrWriter s, 283 | ExtendedStackTraceElement[] enclosingTrace, 284 | String caption, 285 | String prefix, 286 | Set dejaVu) { 287 | assert Thread.holdsLock(s.lock()); 288 | if (dejaVu.contains(t)) { 289 | s.println("\t[CIRCULAR REFERENCE:" + this + "]"); 290 | } else { 291 | dejaVu.add(t); 292 | 293 | final ExtendedStackTraceElement[] trace = get(); 294 | final int unique = countUniqueFrames(trace, enclosingTrace); 295 | 296 | // Print our stack trace 297 | s.println(prefix + caption + this); 298 | for (int i = 0; i < unique; i++) 299 | s.println(prefix + "\tat " + trace[i]); 300 | 301 | final int framesInCommon = trace.length - unique; 302 | if (framesInCommon != 0) 303 | s.println(prefix + "\t... " + framesInCommon + " more"); 304 | 305 | // Print suppressed exceptions, if any 306 | for (Throwable se : t.getSuppressed()) 307 | ExtendedStackTrace.of(se).printStackTrace(s, trace, SUPPRESSED_CAPTION, prefix + "\t", dejaVu); 308 | 309 | // Print cause, if any 310 | final ExtendedStackTrace ourCause = ExtendedStackTrace.of(t.getCause()); 311 | if (ourCause != null) 312 | ourCause.printStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu); 313 | } 314 | } 315 | 316 | private static int countUniqueFrames(ExtendedStackTraceElement[] trace, ExtendedStackTraceElement[] enclosingTrace) { 317 | int m = trace.length - 1; 318 | if (enclosingTrace != null) { 319 | int n = enclosingTrace.length - 1; 320 | while (m >= 0 && n >= 0 && trace[m].equals(enclosingTrace[n])) { 321 | m--; 322 | n--; 323 | } 324 | } 325 | return m + 1; 326 | } 327 | 328 | private static final String CAUSE_CAPTION = "Caused by: "; 329 | private static final String SUPPRESSED_CAPTION = "Suppressed: "; 330 | 331 | /** 332 | * Wrapper class for PrintStream and PrintWriter to enable a single implementation of printStackTrace. 333 | */ 334 | private abstract static class PrintStreamOrWriter { 335 | abstract Object lock(); 336 | 337 | abstract void println(Object o); 338 | } 339 | 340 | private static class WrappedPrintStream extends PrintStreamOrWriter { 341 | private final PrintStream printStream; 342 | 343 | WrappedPrintStream(PrintStream printStream) { 344 | this.printStream = printStream; 345 | } 346 | 347 | Object lock() { 348 | return printStream; 349 | } 350 | 351 | void println(Object o) { 352 | printStream.println(o); 353 | } 354 | } 355 | 356 | private static class WrappedPrintWriter extends PrintStreamOrWriter { 357 | private final PrintWriter printWriter; 358 | 359 | WrappedPrintWriter(PrintWriter printWriter) { 360 | this.printWriter = printWriter; 361 | } 362 | 363 | Object lock() { 364 | return printWriter; 365 | } 366 | 367 | void println(Object o) { 368 | printWriter.println(o); 369 | } 370 | } 371 | // 372 | } 373 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/ExtendedStackTraceClassContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 3 | * 4 | * This program and the accompanying materials are licensed under 5 | * GNU General Public License, version 2, with the Classpath Exception 6 | * 7 | * http://openjdk.java.net/legal/gplv2+ce.html 8 | */ 9 | package co.paralleluniverse.xst; 10 | 11 | /** 12 | * @author pron 13 | */ 14 | class ExtendedStackTraceClassContext extends ExtendedStackTrace { 15 | private static final ClassContext classContextGenerator = new ClassContext(); 16 | private ExtendedStackTraceElement[] est; 17 | private final Class[] classContext; 18 | 19 | ExtendedStackTraceClassContext() { 20 | super(new Throwable()); 21 | this.classContext = classContextGenerator.getClassContext(); 22 | 23 | // int i = 0; 24 | // for (Class c : classContext) 25 | // System.out.println("== " + i++ + " " + c.getName()); 26 | // System.out.println(""); 27 | // i = 0; 28 | // for (StackTraceElement e : t.getStackTrace()) 29 | // System.out.println("-- " + i++ + " " + e); 30 | } 31 | 32 | @Override 33 | public ExtendedStackTraceElement[] get() { 34 | synchronized (this) { 35 | if (est == null) { 36 | final StackTraceElement[] st = t.getStackTrace(); 37 | if (st != null) { 38 | est = new ExtendedStackTraceElement[st.length - 1]; 39 | for (int i = 1, k = 2; i < st.length; i++, k++) { 40 | if (skipCTX(classContext[k])) 41 | i--; 42 | else { 43 | final StackTraceElement ste = st[i]; 44 | final Class clazz; 45 | if (skipSTE(st[i])) { 46 | k--; 47 | clazz = null; 48 | } else 49 | clazz = classContext[k]; 50 | est[i - 1] = new BasicExtendedStackTraceElement(ste, clazz); 51 | // System.out.println(">>>> " + k + ": " + (clazz != null ? clazz.getName() : null) + " :: " + i + ": " + ste); 52 | } 53 | } 54 | } 55 | } 56 | return est; 57 | } 58 | } 59 | 60 | static boolean skipSTE(StackTraceElement ste) { 61 | return (ste.getClassName().startsWith("sun.reflect") 62 | || ste.getClassName().equals("java.lang.reflect.Method") 63 | || ste.getClassName().startsWith("java.lang.invoke.")); 64 | } 65 | 66 | private static boolean skipCTX(Class c) { 67 | return c.getName().startsWith("java.lang.invoke."); 68 | } 69 | 70 | private static class ClassContext extends SecurityManager { 71 | @Override 72 | public Class[] getClassContext() { 73 | return super.getClassContext(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/ExtendedStackTraceElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 3 | * 4 | * This program and the accompanying materials are licensed under 5 | * GNU General Public License, version 2, with the Classpath Exception 6 | * 7 | * http://openjdk.java.net/legal/gplv2+ce.html 8 | */ 9 | package co.paralleluniverse.xst; 10 | 11 | // import java.lang.reflect.Executable; 12 | import java.lang.reflect.Constructor; 13 | import java.lang.reflect.Member; 14 | import java.lang.reflect.Method; 15 | import java.util.Objects; 16 | 17 | /** 18 | * An element in a stack trace. Each element represents a single stack frame. 19 | * 20 | * Same as {@link StackTraceElement}, but with additional data. 21 | * 22 | * @author pron 23 | */ 24 | public class ExtendedStackTraceElement { 25 | private final String declaringClassName; 26 | private final String methodName; 27 | private final String fileName; 28 | private final int lineNumber; 29 | private final int bci; 30 | Class clazz; 31 | Member /*Executable*/ method; 32 | 33 | public ExtendedStackTraceElement(StackTraceElement ste) { 34 | this(ste, null, null, -1); 35 | } 36 | 37 | public ExtendedStackTraceElement(StackTraceElement ste, Class clazz, Member method, int bci) { 38 | this(ste.getClassName(), ste.getMethodName(), ste.getFileName(), ste.getLineNumber(), clazz, method, bci); 39 | } 40 | 41 | public ExtendedStackTraceElement(Class clazz, Method method, String fileName, int lineNumber, int bci) { 42 | this(clazz.getName(), method.getName(), fileName, lineNumber, clazz, method, bci); 43 | } 44 | 45 | public ExtendedStackTraceElement(String declaringClassName, String methodName, String fileName, int lineNumber, Class clazz, Member method, int bci) { 46 | Objects.requireNonNull(declaringClassName, "Declaring class is null"); 47 | Objects.requireNonNull(methodName, "Method name is null"); 48 | if (clazz != null && !declaringClassName.equals(clazz.getName())) 49 | throw new IllegalArgumentException("Class name mismatch: " + declaringClassName + ", " + clazz.getName()); 50 | if (method != null && !methodName.equals(method.getName())) 51 | throw new IllegalArgumentException("Method name mismatch: " + methodName + ", " + method.getName()); 52 | this.declaringClassName = declaringClassName; 53 | this.methodName = methodName; 54 | this.fileName = fileName; 55 | this.lineNumber = lineNumber; 56 | this.clazz = clazz; 57 | this.bci = bci; 58 | this.method = method; 59 | } 60 | 61 | /** 62 | * Returns the name of the source file containing the execution point 63 | * represented by this stack trace element. Generally, this corresponds 64 | * to the {@code SourceFile} attribute of the relevant {@code class} 65 | * file (as per The Java Virtual Machine Specification, Section 66 | * 4.7.7). In some systems, the name may refer to some source code unit 67 | * other than a file, such as an entry in source repository. 68 | * 69 | * @return the name of the file containing the execution point 70 | * represented by this stack trace element, or {@code null} if 71 | * this information is unavailable. 72 | */ 73 | public String getFileName() { 74 | return fileName; 75 | } 76 | 77 | /** 78 | * Returns the line number of the source line containing the execution 79 | * point represented by this stack trace element. Generally, this is 80 | * derived from the {@code LineNumberTable} attribute of the relevant 81 | * {@code class} file (as per The Java Virtual Machine 82 | * Specification, Section 4.7.8). 83 | * 84 | * @return the line number of the source line containing the execution 85 | * point represented by this stack trace element, or a negative 86 | * number if this information is unavailable. 87 | */ 88 | public int getLineNumber() { 89 | return lineNumber; 90 | } 91 | 92 | /** 93 | * Returns the fully qualified name of the class containing the 94 | * execution point represented by this stack trace element. 95 | * 96 | * @return the fully qualified name of the {@code Class} containing 97 | * the execution point represented by this stack trace element. 98 | */ 99 | public String getClassName() { 100 | return declaringClassName; 101 | } 102 | 103 | /** 104 | * Returns the name of the method containing the execution point 105 | * represented by this stack trace element. If the execution point is 106 | * contained in an instance or class initializer, this method will return 107 | * the appropriate special method name, {@code } or 108 | * {@code }, as per Section 3.9 of The Java Virtual 109 | * Machine Specification. 110 | * 111 | * @return the name of the method containing the execution point 112 | * represented by this stack trace element. 113 | */ 114 | public String getMethodName() { 115 | return methodName; 116 | } 117 | 118 | /** 119 | * Returns true if the method containing the execution point 120 | * represented by this stack trace element is a native method. 121 | * 122 | * @return {@code true} if the method containing the execution point 123 | * represented by this stack trace element is a native method. 124 | */ 125 | public boolean isNativeMethod() { 126 | return lineNumber == -2; 127 | } 128 | 129 | /** 130 | * Returns the class containing the execution point represented by this stack trace element. 131 | */ 132 | public Class getDeclaringClass() { 133 | return clazz; 134 | } 135 | 136 | /** 137 | * Returns the bytecode index within the method of the execution point represented by this stack trace element. 138 | */ 139 | public int getBytecodeIndex() { 140 | return bci; 141 | } 142 | 143 | /** 144 | * Returns the method containing the execution point represented by this stack trace element. 145 | */ 146 | public Member getMethod() { 147 | return method; 148 | } 149 | 150 | /** 151 | * Converts this extended stack element into a plain {@link StackTraceElement} (obviously with no extended information). 152 | */ 153 | public StackTraceElement getStackTraceElement() { 154 | return new StackTraceElement(declaringClassName, methodName, fileName, lineNumber); 155 | } 156 | 157 | @Override 158 | public boolean equals(Object obj) { 159 | if (obj == this) 160 | return true; 161 | if (!(obj instanceof ExtendedStackTraceElement)) 162 | return false; 163 | ExtendedStackTraceElement e = (ExtendedStackTraceElement) obj; 164 | return e.declaringClassName.equals(declaringClassName) 165 | && e.lineNumber == lineNumber 166 | && Objects.equals(methodName, e.methodName) 167 | && Objects.equals(fileName, e.fileName); 168 | } 169 | 170 | @Override 171 | public int hashCode() { 172 | int result = 31 * declaringClassName.hashCode() + methodName.hashCode(); 173 | result = 31 * result + Objects.hashCode(fileName); 174 | result = 31 * result + lineNumber; 175 | return result; 176 | } 177 | 178 | @Override 179 | public String toString() { 180 | final StringBuilder sb = new StringBuilder(); 181 | if (method != null) 182 | sb.append(toString(method)); 183 | else 184 | sb.append(getClassName()).append('.').append(methodName); 185 | sb.append(' '); 186 | if (isNativeMethod()) 187 | sb.append("(Native Method)"); 188 | else { 189 | sb.append('('); 190 | if (fileName != null) { 191 | sb.append(fileName); 192 | if (lineNumber >= 0) 193 | sb.append(':').append(lineNumber); 194 | } else 195 | sb.append("Unknown Source"); 196 | if (bci >= 0) 197 | sb.append(" bci: ").append(bci); 198 | sb.append(')'); 199 | } 200 | return sb.toString(); 201 | } 202 | 203 | private static String toString(Member method) { 204 | final StringBuilder sb = new StringBuilder(); 205 | sb.append(getTypeName(method.getDeclaringClass())) // .getTypeName() 206 | .append('.') 207 | .append(method.getName()); 208 | sb.append('('); 209 | for (Class type : getParameterTypes(method)) 210 | sb.append(getTypeName(type)).append(','); //.getTypeName() 211 | sb.delete(sb.length() - 1, sb.length()); 212 | sb.append(')'); 213 | 214 | return sb.toString(); 215 | } 216 | 217 | private static Class[] getParameterTypes(Member m) { 218 | if (m instanceof Constructor) 219 | return ((Constructor) m).getParameterTypes(); 220 | else 221 | return ((Method) m).getParameterTypes(); 222 | } 223 | 224 | // In Java 8, replaced by Class.getTypeName() 225 | private static String getTypeName(Class type) { 226 | if (type.isArray()) { 227 | try { 228 | Class cl = type; 229 | int dimensions = 0; 230 | while (cl.isArray()) { 231 | dimensions++; 232 | cl = cl.getComponentType(); 233 | } 234 | StringBuilder sb = new StringBuilder(); 235 | sb.append(cl.getName()); 236 | for (int i = 0; i < dimensions; i++) { 237 | sb.append("[]"); 238 | } 239 | return sb.toString(); 240 | } catch (Throwable e) { /*FALLTHRU*/ } 241 | } 242 | return type.getName(); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/ExtendedStackTraceHotSpot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 3 | * 4 | * This program and the accompanying materials are licensed under 5 | * GNU General Public License, version 2, with the Classpath Exception 6 | * 7 | * http://openjdk.java.net/legal/gplv2+ce.html 8 | */ 9 | package co.paralleluniverse.xst; 10 | 11 | import java.lang.reflect.AccessibleObject; 12 | import java.lang.reflect.Constructor; 13 | // import java.lang.reflect.Executable; 14 | import java.lang.reflect.Field; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.lang.reflect.Member; 17 | import java.lang.reflect.Method; 18 | import java.util.Iterator; 19 | 20 | /** 21 | * This classes uses internal HotSpot data to retrieve a more detailed stacktrace from a {@link Throwable}. 22 | * 23 | * Works only on HotSpot, versions 8 and 9. 24 | * 25 | * @author pron 26 | */ 27 | class ExtendedStackTraceHotSpot extends ExtendedStackTrace { 28 | /* 29 | * hotspot/src/share/vm/classfile/javaClasses.hpp 30 | * hotspot/src/share/vm/classfile/javaClasses.cpp 31 | */ 32 | private ExtendedStackTraceElement[] est; 33 | 34 | ExtendedStackTraceHotSpot(Throwable t) { 35 | super(t); 36 | } 37 | 38 | @Override 39 | public Iterator iterator() { 40 | return new Iterator() { 41 | private Object chunk = getBacktrace(t); 42 | private int j = -1; 43 | private int i = -1; 44 | 45 | @Override 46 | public boolean hasNext() { 47 | if (j + 1 >= TRACE_CHUNK_SIZE) { 48 | j = -1; 49 | chunk = getNext(chunk); 50 | if (chunk == null) 51 | return false; 52 | } 53 | if (getDeclaringClass(chunk, j + 1) == null) 54 | return false; 55 | return true; 56 | } 57 | 58 | @Override 59 | public ExtendedStackTraceElement next() { 60 | return getStackTraceElement(getStackTraceElement0(++i), chunk, ++j); 61 | } 62 | 63 | @Override 64 | public void remove() { 65 | throw new UnsupportedOperationException(); 66 | } 67 | }; 68 | } 69 | 70 | @Override 71 | public ExtendedStackTraceElement[] get() { 72 | synchronized (this) { 73 | if (est == null) { 74 | est = new ExtendedStackTraceElement[getStackTraceDepth()]; 75 | int i = 0; 76 | for (ExtendedStackTraceElement e : this) 77 | est[i++] = e; 78 | } 79 | return est; 80 | } 81 | } 82 | 83 | private int getStackTraceDepth() { 84 | Object chunk = getBacktrace(t); 85 | int depth = 0; 86 | if (chunk != null) { 87 | // Iterate over chunks and count full ones 88 | while (true) { 89 | Object next = getNext(chunk); 90 | if (next == null) 91 | break; 92 | depth += TRACE_CHUNK_SIZE; 93 | chunk = next; 94 | } 95 | // Count element in remaining partial chunk. NULL value for mirror 96 | // marks the end of the stack trace elements that are saved. 97 | for (int j = 0; j < TRACE_CHUNK_SIZE; j++) { 98 | Class c = getDeclaringClass(chunk, j); 99 | if (c == null) 100 | break; 101 | depth++; 102 | } 103 | } 104 | assert depth == getStackTraceDepth0(); 105 | return depth; 106 | } 107 | 108 | private ExtendedStackTraceElement getStackTraceElement(int i) { 109 | int skipChunks = i / TRACE_CHUNK_SIZE; 110 | int j = i % TRACE_CHUNK_SIZE; 111 | Object chunk = getBacktrace(t); 112 | while (chunk != null && skipChunks > 0) { 113 | chunk = getNext(chunk); 114 | skipChunks--; 115 | } 116 | return getStackTraceElement(getStackTraceElement0(i), chunk, j); 117 | } 118 | 119 | private ExtendedStackTraceElement getStackTraceElement(StackTraceElement ste, Object chunk, int j) { 120 | return new HotSpotExtendedStackTraceElement(ste, getDeclaringClass(chunk, j), getMethod(chunk, j), getBci(chunk, j)); 121 | } 122 | 123 | @Override 124 | protected Member getMethod(ExtendedStackTraceElement este) { 125 | final HotSpotExtendedStackTraceElement heste = (HotSpotExtendedStackTraceElement) este; 126 | Member[] ms = getMethods(heste.getDeclaringClass()); 127 | for (Member m : ms) { 128 | if (heste.methodSlot == getSlot(m)) 129 | return m; 130 | } 131 | return null; 132 | } 133 | 134 | private class HotSpotExtendedStackTraceElement extends BasicExtendedStackTraceElement { 135 | private final int methodSlot; 136 | 137 | HotSpotExtendedStackTraceElement(StackTraceElement ste, Class clazz, int methodSlot, int bci) { 138 | super(ste, clazz, null, bci); 139 | this.methodSlot = methodSlot; 140 | } 141 | } 142 | 143 | private int getStackTraceDepth0() { 144 | try { 145 | return (Integer) getStackTraceDepth.invoke(t); 146 | } catch (IllegalAccessException e) { 147 | throw new AssertionError(e); 148 | } catch (InvocationTargetException e) { 149 | throw rethrow(e); 150 | } 151 | } 152 | 153 | private StackTraceElement getStackTraceElement0(int i) { 154 | try { 155 | return (StackTraceElement) getStackTraceElement.invoke(t, i); 156 | } catch (IllegalAccessException e) { 157 | throw new AssertionError(e); 158 | } catch (InvocationTargetException e) { 159 | throw rethrow(e); 160 | } 161 | } 162 | 163 | private static int getSlot(/*Executable*/Member m) { 164 | try { 165 | if (m instanceof Constructor) 166 | return ctorSlot.getInt((Constructor) m); 167 | else if (m instanceof Field) 168 | return fieldSlot.getInt((Field) m); 169 | return methodSlot.getInt((Method) m); 170 | } catch (IllegalAccessException e) { 171 | throw new AssertionError(e); 172 | } 173 | } 174 | 175 | private static Object getBacktrace(Throwable t) { 176 | // the JVM blocks access to Throwable.backtrace via reflection 177 | return (Object[]) UNSAFE.getObject(t, BACKTRACE_FIELD_OFFSET); 178 | // try { 179 | // return (Object[]) backtrace.get(t); 180 | // } catch (IllegalAccessException e) { 181 | // throw new AssertionError(e); 182 | // } 183 | } 184 | 185 | private static Class getDeclaringClass(Object chunk, int j) { 186 | return (Class) ((Object[]) ((Object[]) chunk)[TRACE_CLASSES_OFFSET])[j]; 187 | } 188 | 189 | private static short getMethod(Object chunk, int j) { 190 | return ((short[]) ((Object[]) chunk)[TRACE_METHOD_OFFSET])[j]; 191 | } 192 | 193 | private static short getBci(Object chunk, int j) { 194 | int bciAndVersion = ((int[]) ((Object[]) chunk)[TRACE_BCIS_OFFSET])[j]; 195 | short bci = (short) (bciAndVersion >>> 16); 196 | short version = (short) (bciAndVersion & 0xffff); 197 | return bci; 198 | } 199 | 200 | private static Object getNext(Object chunk) { 201 | return (Object[]) ((Object[]) chunk)[((Object[]) chunk).length - 1]; 202 | } 203 | 204 | // array of arrays; each content array contains trace_chunck_size elements. trace_next_offset points to next array of arrays 205 | private static final long BACKTRACE_FIELD_OFFSET; // 206 | private static final int TRACE_METHOD_OFFSET = 0; // shorts -- index into class's methods; should be equal to Method.slot 207 | private static final int TRACE_BCIS_OFFSET = 1; // ints 208 | private static final int TRACE_CLASSES_OFFSET = 2; // object array containing classes 209 | // private static final int TRACE_CPREFS_OFFSET = 3; // short -- index into constant pool: method name if method is null 210 | // private static final int TRACE_NEXT_OFFSET = 4; // this elements points to next array of arrays 211 | private static final int TRACE_CHUNK_SIZE = 32; // maximum num of elements in each array 212 | 213 | // private static final Field backtrace; // the JVM blocks access to Throwable.backtrace via reflection 214 | private static final Method getStackTraceDepth; 215 | private static final Method getStackTraceElement; 216 | private static final Field methodSlot; 217 | private static final Field ctorSlot; 218 | private static final Field fieldSlot; 219 | private static final sun.misc.Unsafe UNSAFE = UtilUnsafe.getUnsafe(); 220 | 221 | static { 222 | try { 223 | final String javaVersion = System.getProperty("java.version"); 224 | if (!javaVersion.startsWith("1.8") && !javaVersion.startsWith("8.") && !javaVersion.startsWith("1.9") && !javaVersion.startsWith("9.")) 225 | throw new IllegalStateException("UnsupportedJavaVersion"); 226 | if (!System.getProperty("java.vm.name").toLowerCase().contains("hotspot")) 227 | throw new IllegalStateException("Not HotSpot"); 228 | // the JVM blocks access to Throwable.backtrace via reflection 229 | // backtrace = ReflectionUtil.accessible(Throwable.class.getDeclaredField("backtrace")); 230 | getStackTraceDepth = accessible(Throwable.class.getDeclaredMethod("getStackTraceDepth")); 231 | getStackTraceElement = accessible(Throwable.class.getDeclaredMethod("getStackTraceElement", int.class)); 232 | methodSlot = accessible(Method.class.getDeclaredField("slot")); 233 | ctorSlot = accessible(Constructor.class.getDeclaredField("slot")); 234 | fieldSlot = accessible(Field.class.getDeclaredField("slot")); 235 | 236 | BACKTRACE_FIELD_OFFSET = guessBacktraceFieldOffset(); 237 | 238 | sanityCheck(); 239 | } catch (Exception e) { 240 | throw new AssertionError(e); 241 | } 242 | } 243 | 244 | private static long guessBacktraceFieldOffset() { 245 | Field[] fs = Throwable.class.getDeclaredFields(); 246 | Field second = null; 247 | for (Field f : fs) { 248 | if (getSlot(f) == 2) { 249 | second = f; 250 | break; 251 | } 252 | } 253 | if (second == null) 254 | throw new IllegalStateException(); 255 | long secondOffest = UNSAFE.objectFieldOffset(second); 256 | if (secondOffest == 16) 257 | return 12; // compressed oops 258 | if (secondOffest == 24) 259 | return 16; // no compressed oops 260 | else 261 | throw new IllegalStateException("secondOffset: " + secondOffest); // unfamiliar 262 | } 263 | 264 | private static void sanityCheck() { 265 | Throwable t = new Throwable(); 266 | Object[] chunk = (Object[]) getBacktrace(t); 267 | if (((Object[]) chunk[TRACE_CLASSES_OFFSET]).length != TRACE_CHUNK_SIZE) 268 | throw new IllegalStateException(); 269 | if (((short[]) chunk[TRACE_METHOD_OFFSET]).length != TRACE_CHUNK_SIZE) 270 | throw new IllegalStateException(); 271 | if (((int[]) chunk[TRACE_BCIS_OFFSET]).length != TRACE_CHUNK_SIZE) 272 | throw new IllegalStateException(); 273 | final Object[] nextChunk = (Object[]) getNext(chunk); 274 | if (nextChunk != null && nextChunk.length != chunk.length) 275 | throw new IllegalStateException(); 276 | } 277 | 278 | ////////////////////////////////////////////////////////////////// 279 | private static T accessible(T obj) { 280 | if (obj == null) 281 | return null; 282 | obj.setAccessible(true); 283 | return obj; 284 | } 285 | 286 | private static RuntimeException rethrow(Throwable t) { 287 | if (t instanceof RuntimeException) 288 | throw ((RuntimeException) t); 289 | if (t instanceof Error) 290 | throw ((Error) t); 291 | else 292 | throw new RuntimeException(t); 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/UtilUnsafe.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.xst; 2 | 3 | import java.lang.reflect.Field; 4 | import sun.misc.Unsafe; 5 | 6 | /** 7 | * Simple class to obtain access to the {@link Unsafe} object. {@link Unsafe} 8 | * is required to allow efficient CAS operations on arrays. Note that the 9 | * versions in {@code java.util.concurrent.atomic}, such as {@link 10 | * java.util.concurrent.atomic.AtomicLongArray}, require extra memory ordering 11 | * guarantees which are generally not needed in these algorithms and are also 12 | * expensive on most processors. 13 | */ 14 | class UtilUnsafe { 15 | private UtilUnsafe() { 16 | } 17 | 18 | public static Unsafe getUnsafe1() { 19 | // Not on bootclasspath 20 | if (UtilUnsafe.class.getClassLoader() == null) 21 | return Unsafe.getUnsafe(); 22 | try { 23 | Field f = Unsafe.class.getDeclaredField("theUnsafe"); 24 | f.setAccessible(true); 25 | return (Unsafe) f.get(UtilUnsafe.class); 26 | } catch (Exception e) { 27 | throw new RuntimeException("Could not obtain access to sun.misc.Unsafe", e); 28 | } 29 | } 30 | 31 | public static Unsafe getUnsafe() { 32 | try { 33 | return Unsafe.getUnsafe(); 34 | } catch (SecurityException se) { 35 | try { 36 | return java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() { 37 | @Override 38 | public Unsafe run() throws Exception { 39 | final Class k = sun.misc.Unsafe.class; 40 | if (true) { 41 | final Field f = k.getDeclaredField("theUnsafe"); 42 | f.setAccessible(true); 43 | final Object x = f.get(null); 44 | return k.cast(x); 45 | } else { 46 | for (Field f : k.getDeclaredFields()) { 47 | f.setAccessible(true); 48 | final Object x = f.get(null); 49 | if (k.isInstance(x)) 50 | return k.cast(x); 51 | } 52 | throw new NoSuchFieldError("the Unsafe"); 53 | } 54 | } 55 | }); 56 | } catch (java.security.PrivilegedActionException e) { 57 | throw new RuntimeException("Could not initialize intrinsics", e.getCause()); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/xst/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved. 3 | * 4 | * This program and the accompanying materials are licensed under 5 | * GNU General Public License, version 2, with the Classpath Exception 6 | * 7 | * http://openjdk.java.net/legal/gplv2+ce.html 8 | */ 9 | /** 10 | *

Extended Stack-Trace

11 | *

Enhanced stack traces for the JVM

12 | * 13 | * This library allows you to capture stack traces with more information than {@code Throwable.getStackTrace}. 14 | * In addition to the information in the JDK's {@link java.lang.StackTraceElement} The captured stack elements contain: 15 | *

16 | *

    17 | *
  • The decalaring class -- the actual {@link java.lang.Class} object -- not just the name.
  • 18 | *
  • The method -- the actual {@link java.lang.reflect.Method Method} object -- not just the method name.
  • 19 | *
  • The bytecode index
  • 20 | *
21 | *

22 | * Works best when running on a Java 8 HotSpot (OpenJDK/Oracle JDK) JVM. 23 | * 24 | *

Usage

25 | * Obtain the extended stack trace information with {@link co.paralleluniverse.xst.ExtendedStackTrace#here()} or {@link co.paralleluniverse.xst.ExtendedStackTrace#of(Throwable)} 26 | * 27 | *

Details

28 | * On the HotSpot JVM (OpenJDK/Oracle JDK) for Java 8, the extended information is always available, and obtaining it is as efficient as a plain {@link Throwable#getStackTrace()}. 29 | * 30 | * On other JVMs/Java versions the extended information may be incomplete. There are (much) better chances for obtaining extended information when capturing the stack with 31 | * {@link co.paralleluniverse.xst.ExtendedStackTrace#here()} than when extracting extended information from a {@code Throwable} with {@link co.paralleluniverse.xst.ExtendedStackTrace#of(Throwable)}. 32 | * Also, getting the method object carries a significant cost. 33 | * 34 | *

License

35 | * This project is free software free software published under the following license: 36 | *

37 | *

38 |  * Copyright (c) 2015, Parallel Universe Software Co. All rights reserved.
39 |  *
40 |  * This program and the accompanying materials are licensed under
41 |  * GNU General Public License, version 2, with the Classpath Exception (same as OpenJDK)
42 |  * 
43 |  * http://openjdk.java.net/legal/gplv2+ce.html
44 |  * 
45 | */ 46 | package co.paralleluniverse.xst; 47 | --------------------------------------------------------------------------------