本文作为byteman的第一篇文章,初步介绍了byteman、部署和验证以及两个入门案例(程序运行中读取其局部变量值和程序未运行时统计某个方法耗时)。更多的关于byteman参考该系列的其他文章。
一、部署安装与验证
1、介绍
The simplest use of Byteman is to insert print statements into code so you can see what your program is doing. Byteman can read and print public, private and protected fields or local variables. It can even call application methods to compute values to be displayed. Byteman makes very specific, highly localized changes and incurs little overhead in doing so. This is extremely useful when debugging timing dependent code, especially multi-threaded applications where interesting events may happen in several threads at the same time. It also allows you to debug or monitor a deployed application where it is not acceptable to stop the program using a debugger. Byteman can also change program control flow. You can inject a call to application or JVM methods which modify application or runtime state. You can also reassign static and instance fields, method parameters and local variables, force a return or throw an exception. This capability is normally used during testing to simulate error situations. Instead of cluttering your application with instrumentation code or using dummy classes to implement faulty behaviour you simply inject code into the application at the precise point where you want it to misbehave.
2、下载
下载完成后,解压即可。 解压后必须的文件或文件夹如下: The directory identified by BYTEMAN_HOME should include a file called README and subdirectories sample, lib, docs, contrib and bin.
3、配置系统环境变量
4、测试
在cmd中输入bminstall命令,出现如下信息则表示成功
二、入门示例
1、读取方法的参数(局部变量,程序运行中)
1)、创建示例类
该示例是通过线程运行程序,通过控制台输入并且实时显示出来,输入end则程序自动退出。
package com.win.byteman;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author alanchan 2018年12月27日
*/
public class ReadlocalVarDemo {
public static void main(String[] args) {
new ReadlocalVarDemo().start();
}
private void start() {
new Thread(() -> {
DataInputStream in = new DataInputStream(System.in);
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
try {
String next = buf.readLine();
while (next != null && !next.contains("end")) {
consume(next);
next = buf.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
public void consume(String text) {
// 这是个局部变量,将会在byteman中追踪她
final String arg = text;
Thread thread = new Thread(() -> System.out.println("输入参数 :" + arg));
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return;
}
}
测试该示例结果:
2)、建立监控
- 获取运行程序进程号 在cmd中输入jps,结果如下图:
- 建立监控 在cmd中执行如下命令:
bminstall -b -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.verbose -p 55000 14048
执行完命令后,控制台显示如下图所示
输出信息中可以看到,byteman监听55000端口,并在该端口接收请求。
3)、编制脚本
脚本名称定义为:ReadlocalVarDemo.btm。文件名称随意,但扩展名不能改变。
RULE trace_lin_ local_var
CLASS ReadlocalVarDemo
METHOD consume(String)
AFTER WRITE $arg
IF TRUE
DO traceln("----input value is : [" + $arg + "]----")
ENDRULE
脚本的编制具体说明参考本系列文章的关于Byteman脚本介绍部分。
4)、 提交脚本
由于本脚本文件存储在D:\application\byteman-download-4.0.5\sample\testing,所以需要在cmd中先定位到该目录下。在该目录下执行命令:
bmsubmit -p 55000 -l ReadlocalVarDemo.btm
命令执行成功截图如下:
如果执行上述命令后,只有下面 TransformListener() : handling connection on port 55000 但程序运行控制台没有下面信息 retransforming com.win.byteman.ReadlocalVarDemo org.jboss.byteman.agent.Transformer : possible trigger for rule trace_lin_ local_var in class com.win.byteman.ReadlocalVarDemo RuleTriggerMethodAdapter.injectTriggerPoint : inserting trigger into com.win.byteman.ReadlocalVarDemo.consume(java.lang.String) void for rule trace_lin_ local_var org.jboss.byteman.agent.Transformer : inserted trigger for trace_lin_ local_var in class com.win.byteman.ReadlocalVarDemo 则表示脚本没有正常的生效。可以通过bmcheck命令检查脚本。
以上则完成了第一个示例的配置,下面进行测试。
5)、验证
在运行程序控制台,随意输入一串字符,查看控制台输出。
6)、卸载脚本
在cmd中输入命令
bmsubmit -p 55000 -u ReadlocalVarDemo.btm
再在程序运行控制台进行测试,则发现没有监控程序的内容输出了。 以上6步完成了本示例。
本被监控程序是在开发工具中运行的,开发工具会自动编译源码,只需要运行即可。 如果使用命令行自己编译的Java源码,很可能无法追踪到局部变量的值,这是因为自己编译的话,需要指定-g参数,也就是像这样javac -g ReadlocalVarDemo.java编译出来的class文件才能够被追踪到局部变量。
2、统计方法耗时(程序未启动)
该示例是针对程序未在运行的情况下进行方法的统计。 该类是实时显示控制台输入的结果以及输入的是end时退出。 程序未运行则是通过bmjava命令进行程序启动。
1)、创建示例类
package com.win.byteman;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author alan 2018年12月26日
*/
public class WorkerDemo {
public static void main(String[] args) {
WorkerDemo workerDemo = new WorkerDemo();
DataInputStream dataInputStream = new DataInputStream(System.in);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(dataInputStream));
try {
String next;
while ((next = bufferedReader.readLine()) != null) {
System.out.println(
"==========================================开始调用==========================================");
workerDemo.working(next);
System.out.println(
"==========================================结束调用==========================================");
if (next.contains("end")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void working(String input) {
System.out.println("输入内容:" + input);
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
2)、编制脚本
WorkerDemo.btm 文件存储在:D:\application\byteman-download-4.0.5\sample\testing 规则如下:
RULE trace worker time start
CLASS WorkerDemo
METHOD working(String)
AT ENTRY
IF TRUE
DO createTimer($0)
ENDRULE
RULE trace worker time stop
CLASS WorkerDemo
METHOD working(String)
AT EXIT
IF TRUE
DO traceln(" working method spent [" + getElapsedTimeFromTimer($0) + "] ms ");
deleteTimer($0);
ENDRULE
3)、检测脚本
参考下篇文章例子中的检测脚本方法
4)、 提交脚本
本示例是通过bmjava命令启动应用程序,bmjava类似java的命令,具体参考bmjava的语法说明。 cd转java的class所在包的目录,本示例即:D:\workspace\springbootrestful\target\classes。 在cmd中执行命令:
bmjava -l D:\application\byteman-download-4.0.5\sample\testing\WorkerDemo.btm com.win.byteman.WorkerDemo
如果没有异常则说明程序启动成功。 另外,该种方式不存在卸载脚本,直接关闭运行程序即可。
5)、验证
详见下图