Byteman basics

Ondra Chaloupka / ochaloup@redhat.com / @_chalda

Motivation

  • Would you like trace the code during execution without enabling trace?

  • Would you like to change program without need to re-compile?

  • Would you like to change how threads interleaves?

  • Have you got troubles to create reliable reproducer?

  • Would you like to simulate OOM or JVM crash?

  • …​

Agenda

  • what is the Byteman and what is good for

  • how to start

  • Byteman DSL (Byteman Rule Language)

  • handling Byteman issues

  • helper classes

  • thread activity tracing

What’s not included

  • Java 9
    (Byteman 4.0.0+ has full support of Java 9 features)

  • testing with WildFly

Workshop structure

Byteman!

byteman icon

Byteman DSL

RULE <rule name>        <-- name
CLASS <class name>      <-- where class/method
METHOD <method name>
AT <location>           <-- location inside the method (entry/exit/line...)
HELPER <helper class>   <-- implementation of actions used in DO
BIND <bindings>         <-- gathering variables values to be used in DO
IF <condition>          <-- conditions to execute the DO action
DO <actions>            <-- what should be done
ENDRULE

Java agent

  • JVM "plugin" (special .jar file) in use of Instrumentation API

  • the jar introduced to Java by command line parameter -javaagent

  • the agent makes execute its method premain before app main

  • Byteman intitialization class: org.jboss.byteman.agent.Main

Byteman agent setup

  • Byteman agent specified at the start

# attaching the Byteman agent to the program
java -javaagent:./byteman.jar=script:./file.btm  -cp ... org.jboss.MainClass

#
# -- OR --
#

export JAVA_TOOL_OPTIONS="-javaagent:./byteman.jar=script:./file.btm"
java -cp ... org.jboss.MainClass

Byteman agent setup #2

  • Byteman aggent attached to the running java process

# receive <pid>
jps -l
# to attach agent to the <pid>
$BYTEMAN_HOME/bin/bminstall.sh <pid>
# to install rules
$BYTEMAN_HOME/bin/bmsubmit.sh ./file.btm

Byteman shell script tooling

  • bminstall : attaching agent to the <pid>

    • bminstall.sh <pid>, see help bminstall.sh -h

  • bmsubmit : injecting rule (connecting to agent, by default to 9091)

    • bmsubmit.sh ./file.btm

  • bmcheck : verifying syntax of btm script

    • bmcheck.sh ./file.btm

  • bmjava : adding Byteman -javaagent to java program startup

  • bmjava.sh -l ./file.btm -cp program.jar org.jboss.MainClass

Task #1

byteman icon

Byteman DSL: example

RULE dump at ActiveMQRAManagedConnection
CLASS ActiveMQRAManagedConnection
METHOD getXAResource
AT INVOKE org.apache.activemq.artemis.service.extensions.ServiceUtils.wrapXAResource
BIND
  c:ClientSessionInternal = $csi;
  xa:java.util.Map = $xaResourceProperties;
  n:String = c.getNodeId();
  u:String = $this.userName;
  p:String = $this.password;
IF true
DO
  debug("Class " + $0.getClass().getName() + ", props: " + xa + ", node: " + n);
  Thread.dumpStack();
ENDRULE

Byteman DSL

RULE <rule name>        <-- name
CLASS <class name>      <-- where class/method
METHOD <method name>
AT <location>           <-- location inside the method (entry/exit/line...)
HELPER <helper class>   <-- implementation of actions used in DO
BIND <bindings>         <-- gathering variables values to be used in DO
IF <condition>          <-- conditions to execute the DO action
DO <actions>            <-- what should be done
ENDRULE

Byteman DSL: tricky points

  • RULE name has to be unique, otherwise JVM won’t start

  • be sure on CLASS and INTERFACE otherwise the rule could not be applied

  • CLASS can be defined with or without package (CLASS String vs. CLASS java.lang.String)

  • remember you need to use ^ sometimes (CLASS ^MyAbstractClass)

  • METHOD can be defined with or without argument list (METHOD valueOf vs METHOD valueOf(int))

  • METHOD names <init> or <clinit> specify binding at constructor/class init method

  • DO actions have to be delimited with , or ;

Byteman DSL: tricky points #2

  • the rule parts which could be ommitted

    • HELPER : default org.jboss.byteman.rule.helper.Helper

    • AT : AT ENTRY will be used

    • BINDING: used BINDING NOTHING, no binding is specified

  • the rule has to define!

    • IF: if you want to use rule in whatever case use IF true

Byteman DSL: minimalistic

RULE <rule name>
CLASS <class name>
METHOD <method name>
IF true
DO <actions>
ENDRULE

Verify rule syntax

  • bmcheck.sh -p org.jboss.btm.workshop -cp target/program.jar ./task2.btm

  • maven rulecheck plugin

<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-rulecheck-maven-plugin</artifactId>

Classloading and additions

  • to define classes loaded by specific classloader you can use Byteman agent properties

    • sys:<jar-file>

    • boot:<jar-file>

  • by default byteman does not inject rules under package java.lang, if you want to allow this, use -Dorg.jboss.byteman.transform.all

  • to get information about Byteman processing use -Dorg.jboss.byteman.verbose -Dorg.jboss.byteman.debug

Task #2

byteman icon

Language standard build-ins

Helper to usage

  • declare non-standard helper in rule

  • put helper to classpath

...
HELPER org.jboss.MyBytemanHelper
...
java -javaagent:byteman.jar=script:./file.btm,sys:myhelper.jar \
  -cp application.jar org.jboss.MainClass

Task #3

byteman icon

BMUnit

  • a library helping to introduce rules for the tests

  • set of annotations

  • ensures loading the Byteman agent to the JVM

  • could be used with JUnit and TestNG

BMUnit: maven coordinates

<dependency>
    <groupId>org.jboss.byteman</groupId>
    <artifactId>byteman-bmunit</artifactId>
    <version>4.0.0</version>
    <scope>test</scope>
</dependency>

BMUnit: example

@RunWith( BMUnitRunner.class )
@BMScript( dir="test/scripts" )
@BMUnitConfig( debug = true, verbose = true )
public class BMUnitTest {

  @Test
  @BMRule( name="disallow reading from file",
           targetClass = "FileInputStream",
           targetMethod = "<init>(String)",
           condition="$1.contains(\"andrew\")",
           action="throw new FileNotFoundException(\"ha ha\")")
  public void test1() {
  ...

  @Test
  @BMScript("file.btm")
  public void test2() {
  ...

dtest library

  • java API on rule creation and rule installation

  • not installing agent to the JVM itself

  • tooling for program workflow verification

dtest: example

// new Instrumentor(String address, int port, int rmiPort)
// default to connect at localhost:9091, rmi at 1099
org.jboss.byteman.contrib.dtest.Instrumentor instrumentor = new Instrumentor();

instrumentor.injectOnMethod(FileInputStream.class.getName(), "<init>(String)",
  "$1.contains(\"andrew\")", "throw new FileNotFoundException(\"ha ha\")", "ENTRY");

RuleConstructor rule = RuleConstructor
  .createRule("disallow reading from file")
  .onInterface(FileInputStream.class)
  .inMethod("<init>(String)")
  .atEntry()
  .helper(org.jboss.byteman.rule.helper.Helper.class)
  .ifCondition("$1.contains(\"andrew\")")
  .doAction("throw new FileNotFoundException(\"ha ha\")");
instrumentor.installRule(rule);

dtest: verify being called

org.jboss.byteman.contrib.dtest.Instrumentor instrumentor = new Instrumentor();
InstrumentedClass instrumentedClass = intrumentor.instrumentClass(FileInputStream.class);

// verification if there was an instance and how much times it was called
instrumentedClass.assertKnownInstances(1);
instrumentedClass.assertMethodCalled("<init>");
// each 'known' instance had to be called once
instrumentedClass.assertMethodCallCount("<init>", 2);

Tracing capabilities

Task #5

byteman icon

More tasks

Other tools for working with bytecode

References

References #2

References #3

cajk