Using Byteman to troubleshoot Java issues
Environment
- Red Hat JBoss Enterprise Application Platform (EAP)
- Java
- Byteman
- Automated testing or production runtime issue
Issue
- Troubleshoot runtime issues or perform automated testing without modifying code
- An exception is printed in
server.log; however, there is no stacktrace - Find out if a method is executed
Resolution
Byteman is a tool to inject Java code into your running application with no need to recompile. Byteman hooks in as a JVMTI agent and has often a very low overhead. 1
Download Byteman and resources
Download Byteman from Content from byteman.jboss.org is not included.the Downloads page release. It is recommended to use the latest 4.x or 3.x releases. Especially, some early releases (< 4.0.3, < 3.0.13) had a bug that led to failure in routing traceStack() output to JBoss EAP server.log, and the bug has been fixed as This content is not included.BYTEMAN-370.
Once downloaded, please unzip the binary zip file to your local file system, for example /path/to/byteman-download-4.0.21/ and declare BYTEMAN_HOME system environment variable, for example, on Linux:
export BYTEMAN_HOME=/path/to/byteman-download-4.0.21/
Documentation and other resources can be found in Content from byteman.jboss.org is not included.the Docs page as well as Content from github.com is not included.Byteman Github project page. Example Byteman scripts are also included in the downloaded binary release byteman-download-<version>/sample folder.
Byteman versions vs. JDK versions
- See What JDK version does byteman require for details.
- In short, we recommend using the latest Byteman 4.x release for JDK 8+ users, and the latest Byteman 3.x release for old JDK 6/7 users. (Note that Byteman 4.0.0+ won't work with JDK 6, and Byteman 4.0.19+ won't work with JDK 7.)
- You might also want to know which Byteman version added support for newer JDK versions. See "Table of Which Byteman versions added support for newer JDK versions" in the solution for details.
Activate Byteman and enable the rules
Enable Byteman for JBoss server persistently
Byteman rules can be enabled in a persistent way so that these configurations live across Java/JBoss restart. This is often the simplest option for configuration.
- Create a text file and put Byteman rules in, rename the file with
.btmextension for example,/my/path/examplescript.btm - Add JVM parameters below to enable Byteman and restart Java/JBoss server instance
-
For JBoss EAP 5 and JBoss EAP 6/7 in standalone mode:
Enable the Byteman instrumentation by adding the following Java VM option to
JAVA_OPTSinrun.conf/run.conf.bat(JBoss EAP 5) orstandalone.conf/standalone.conf.bat(JBoss EAP 6/7). For example, here is example of configuration snippet from JBoss EAP 7standalone.conf:... ... if [ "x$JAVA_OPTS" = "x" ]; then JAVA_OPTS="-Xms1303m -Xmx1303m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true" ... ... JAVA_OPTS="$JAVA_OPTS -javaagent:/path/to/byteman/lib/byteman.jar=script:/my/path/examplescript.btm,sys:/path/to/byteman/lib/byteman.jar -Dorg.jboss.byteman.transform.all=true" ... ... -
For JBoss EAP 6/7 in domain mode:
Add the javaagent configuration in the
<jvm>options for the<server>or<server-group>in domain.xml or host.xml:<jvm name="default"> <java-agent value="/path/to/byteman/lib/byteman.jar=script:/my/path/examplescript.btm,sys:/path/to/byteman/lib/byteman.jar"/> </jvm> <--- optional jvm flags that can help ---> <jvm-options> <option value="-Dorg.jboss.byteman.transform.all=true"/> <option value="-Dorg.jboss.byteman.verbose"/> <option value="-Dorg.jboss.byteman.debug=true"/> </jvm-options>
Adding a custom Byteman Helper to the classpath
JBoss Modules by default will expose classes in the package org.jboss.byteman , the simplest way is to have a custom byteman helper package be org.jboss.byteman, otherwise the JBOSS_MODULES_SYSTEM_PKGS will need to include the package name where the custom byteman helper is located. JBOSS_MODULES_SYSTEM_PKGS is a comma separated list of packages.
$JBOSS_HOME/bin/standalone.conf or standalone.conf.bat or standalone.conf.ps1
if [ "x$JBOSS_MODULES_SYSTEM_PKGS" = "x" ]; then
JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman"
fi
Next include the custom byteman helper(s) inside of a jar which gets added to the system classpath similar to the byteman.jar, for example:
JAVA_OPTS="$JAVA_OPTS -javaagent:/path/to/byteman/lib/byteman.jar=script:/my/path/examplescript.btm,sys:/path/to/byteman/lib/byteman.jar,sys:/path/to/byteman/lib/custom-byteman-helpers.jar -Dorg.jboss.byteman.transform.all=true"
Install Byteman and submit rules dynamically
Activating Byteman rules dynamically can be useful for troubleshooting issues in production environment against running Java/JBoss processes. Rules can be activated and deactivated dynamically without requiring to restart Java/JBoss process. This may require greater effort for successful deployment than the persistent approach mentioned above.
-
Create a text file and put Byteman rules in, rename the file with
.btmextension for example,/my/path/examplescript.btm -
Identify the process ID (PID) of the Java application process or JBoss EAP server process,
<pid> -
Install Byteman on this specific process (Run Byteman using the same OS user as you run Java application/JBoss server)
$ $BYTEMAN_HOME/bin/bminstall.sh -Dorg.jboss.byteman.verbose=true -Dorg.jboss.byteman.debug=true -Dorg.jboss.byteman.transform.all=true <pid> -
Activate the rules:
$ $BYTEMAN_HOME/bin/bmsubmit.sh /my/path/examplescript.btm -
Deactivate all rules after troubleshooting:
$ $BYTEMAN_HOME/bin/bmsubmit.sh -u
Additional Notes:
- The above example uses the
"sys"- System class loader. This is sufficient for instrumenting end-user application code. If you wish to instrument JVM classes, such asjava.net.Socket, then you should use the"boot"- Bootstrap class loader. If you are gettingNoClassDefFoundErrorwhen using"sys", switch to"boot"and see if theNoClassDefFoundErrorerrors persist. - If you are running JBoss EAP 6 or another program based on JBoss Modules, ensure that
"-Djboss.modules.system.pkgs=org.jboss.byteman"is added to your Java VM options instandalone.confordomain.conf. - Please refer to Using Byteman to troubleshoot Java issues in JBoss Fuse or A-MQ for Byteman troubleshooting with JBoss Fuse or A-MQ.
Example Byteman Rules
-
Generate a stacktrace (send to standard out
stdout) whenever theorg.jboss.security.identity.plugins.SimpleRole(String roleName, Role parent)constructor is called.-
This rule uses method
traceStack(String prefix, int maxFrames)to print stacktrace on exiting constructor method -
"prefix" is a message printed in front of the stacktrace and "maxFrames" limits the max stack size getting printed.
RULE trace SimpleRole constructor CLASS org.jboss.security.identity.plugins.SimpleRole METHOD <init>(String,Role) AT EXIT IF TRUE DO traceStack("-----Byteman Rule is executed: printing stacktrace", 500) ENDRULE
-
-
Generate a stacktrace to view the calling method whenever the
getParameterorgetInputStreamis called.-
This Byteman script was designed specifically to capture offending code that calls the
getInputStreamand related methods prematurely in a POST request which will cause future calls togetParameterto fail. The effect here is that parameters will be seen asnullin this situation. See the following knowledge solution for a further discussion on this: Why is the POST body unavailable to my servlet when the RequestDumperValve is enabled?RULE check getParameter CLASS org.apache.catalina.connector.Request METHOD getInputStream AT ENTRY IF $0.parametersParsed DO System.err.println("-----Byteman Rule is executed: attempting to get inputstream when parameters already parsed") ENDRULE RULE check parseParameters CLASS org.apache.catalina.connector.Request METHOD parseParameters AT ENTRY IF $0.usingInputStream DO System.err.println("-----Byteman Rule is executed: attempting to get parameters when input stream already read") ENDRULE
-
-
Create a timer on entering a method to track the method execution time, print the time spent when exiting method.
-
The symbol
^in front of the class name requests that the rule is injected down the class hierarchy into any implementation defined by a subclass ofcom.abc.BusinessService. -
BINDis used to create variables and bind/assign their values. -
this:Objectis to specify the variable being created with namethisand typeObject. -
timerCreated:booleanis to specify the variable being created with nametimerCreatedand typeboolean. -
$0is a special value and used by Byteman to reference a Thread object which triggered this rule (current thread). -
createTimermethod can be called to create a new Timer associated with input parameter (A String object in this example).createTimerreturns true if a new Timer was created and false if a Timer associated with o already exists. -
deleteTimermethod can be called to delete the Timer associated with input parameter.RULE com.abc.BusinessService#processRequest - START CLASS ^com.abc.BusinessService METHOD processRequest AT ENTRY BIND this:Object = $0; timerCreated:boolean = createTimer(this.toString()+"processRequest"); IF TRUE DO System.out.println("[BYTEMAN] " + this.toString() + " processRequest called"); ENDRULE RULE com.abc.BusinessService#processRequest - END CLASS ^com.abc.BusinessService METHOD processRequest AT EXIT BIND this:Object = $0; timeSpent:long = getElapsedTimeFromTimer(this.toString()+"processRequest"); IF TRUE DO System.out.println("[BYTEMAN] " + this.toString() + " processRequest took " + timeSpent + " ms");deleteTimer(this.toString()+"processRequest"); ENDRULE
-
-
Print stacktrace of the unhandled exception in
org.jboss.seam.Component.forName()method-
An
AT EXCEPTION EXITspecifier identifies the point where a method returns control back to its caller via unhandled exceptional control flow. -
Special variable
$^is valid in an AT EXCEPTION EXIT rule and is bound to the throwable being returned from the method via exceptional control flow. Its type is Throwable.RULE printStackTrace CLASS org.jboss.seam.Component AT EXCEPTION EXIT METHOD forName IF TRUE DO System.out.println(formatStack("---------[BYTEMAN-1] Exception caught:\n")); $^.printStackTrace(); ENDRULE
-
-
Print stacktrace and extra caller (Thread) information
-
This set of rules was created to troubleshoot SSL certificate related issues by printing more details (caller stacktrace and thread information)
-
Rules can be attached to interfaces using
INTERFACEkeyword -
^special character in front ofjavax.net.ssl.X509KeyManagermake the rule not only effective to classes directly implement thisX509KeyManagerinterface, but also effective to those subclasses which extends the parent implementation class.RULE log key manager - chooseClientAlias INTERFACE ^javax.net.ssl.X509KeyManager METHOD chooseClientAlias AT ENTRY IF TRUE DO System.out.println("Printing stacktrace for chooseClientAlias - " + Thread.currentThread().getName()); traceStack(); ENDRULE RULE log key manager - chooseServerAlias INTERFACE ^javax.net.ssl.X509KeyManager METHOD chooseServerAlias AT ENTRY IF TRUE DO System.out.println("Printing stacktrace for chooseServerAlias - " + Thread.currentThread().getName()); traceStack(); ENDRULE
-
Note: Any agent will have an overhead, Byteman makes very specific, Content from developer.jboss.org is not included.highly localized changes and incurs little overhead in doing so, however the overhead is completely dependent upon the rules in play, what the rules do, and the resulting number of invocations on that rule with the given application load and behaviour.
Root Cause
Byteman is a free and open-source tool that can be used to modify bytecode in a running application for use with testing and debugging without changing the source. Red Hat Support sometimes uses Byteman for troubleshooting deployed customer applications, but it is not a shipped or supported product. If you need help or to report an issue, see the Content from byteman.jboss.org is not included.The Byteman Community.
Diagnostic Steps
Note: If encountering an error like the below when starting a Java process with Byteman, verify that the Byteman folder, jar and input script exist in the path and the user running the process has permission to access them:
Error opening zip file or JAR manifest missing : /opt/byteman-download-3.0.10/lib/byteman.jar
Error occurred during initialization of VM
agent library failed to init: instrument
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.