[关闭]
@joshsulin 2017-07-28T12:34:22.000000Z 字数 11333 阅读 2372

Byteman教程

byteman


英文原文: https://developer.jboss.org/wiki/ABytemanTutorial#top

我们只是在这里入门。 。

本文是一个简单的教程,向您展示如何开始使用Byteman。它解释了如何安装Byteman并将其用于将简单的Java程序进行注入。本教程的重点是从命令行驱动Byteman(这就是为什么需要下载zip版本)。第二个教程说明如何使用Byteman在单元和集成测试中执行故障注入测试。它描述了如何从ant和maven驱动Byteman(n.b.maven集成避免了下载Byteman版本的需要)。建议您在尝试第2部分之前完成本教程,因为它将基于您将从本教程中获得的理解

如果您需要有关如何使用Byteman的完整信息,“Byteman程序员指南”提供了Byteman的概述,以及Byteman如何操作以及如何定义规定您要注入的副作用的规则的详细说明。它还包括如何安装Byteman以及您可以运行Byteman的各种方式的综合说明。

本教程的结构是一个FAQ,包括以下问题的答案。如果你是Byteman的新人,你应该从头开始读完这些例子。如果您以前使用过Byteman,并希望刷新您的记忆或回答具体问题,您可以使用以下链接之一跳过。然而,在后一种情况下,您可能会发现有必要对前面的部分进行简要说明,以便充分掌握正在呈现的内容。

  • 为什么要用Byteman?
  • 如何下载并安装Byteman?
  • 使用Byteman如何运行程序?
  • 如何将代码注入JVM类?
  • 有更简单的方法来运行Byteman吗?
  • 如何将规则加载到正在运行的程序中?
  • 如何查看哪些规则被加载和编译?
  • 如何卸载规则?
  • 如何将代理安装到正在运行的程序中?
  • 如何使用Byteman运行JBoss AS?
  • 我如何知道我的规则是否正确?
  • 如何判断我的规则是否正在运行?
  • 如何让我的规则运行得很快?
  • 哪里可以下载教程来源

为什么要用Byteman?

您可以使用Byteman来更改Java程序的运行方式,而无需编辑源代码并重新编译它。实际上,您甚至可以使用Byteman修改正在运行的应用程序,而无需停止并重新启动它。 Byteman将乐意重新定义应用程序类和JVM运行时类(如String,Thread等)的行为。

Byteman最简单的使用是将print语句插入到代码中,以便您可以看到程序正在做什么。 Byteman可以读取和打印公共,私人和受保护的字段或局部变量。甚至可以调用应用程序的方法来计算要显示的值。 Byteman做出非常具体的,高度本地化的变化,并且在这样做时引起了很少的开销。调试与时序相关的代码非常有用,特别是多线程应用程序,同时在多个线程中可能会发生有趣的事件。它还允许您调试或监视已部署的应用程序,因为使用调试器停止程序是不可接受的。

Byteman也可以改变程序控制流程。您可以调用应用程序或JVM方法来修改应用程序或运行时状态。您还可以重新分配静态和实例字段,方法参数和局部变量,强制返回或抛出异常。通常在测试过程中使用此功能来模拟错误情况。使用仪器代码或使用虚拟类来实现错误行为,而不是用您的应用程序来混淆应用程序,您只需在准确的位置将代码注入到应用程序中即可。

请注意,Byteman要求您在Java 6或更高版本的JVM上运行应用程序。如果您的代码是使用较早版本的Java版本编译的,那么您不需要担心它仍然可以工作。但是,Byteman推出了仅在JDK6中完全支持的JVM功能。如果您真的需要运行Java 5 JVM,您可以下载旧版1.0.3 Byteman版本。它提供了这里描述的所有基本功能,但不支持动态加载Byteman代理和Byteman规则(有关详细信息,请参阅1.0.3用户指南)。

如何下载并安装Byteman?

在运行Byteman之前,您将需要安装Java 6或更高版本的JVM。为了执行本教程中的程序,您将需要编译源代码,以便您需要安装包含javac编译器和Java工具jar的完整JDK。但是,可以在仅安装JRE的系统上使用Byteman。需要安装完整JDK的唯一功能是Byteman代理的动态加载。当然,如果您正在开发Java软件,那么您将已经安装了JDK。
 
Byteman项目下载页面提供了最新的Byteman二进制版本。下载二进制zip版本并将其解压缩到本地机器上的目录中。现在设置环境变量BYTEMAN_HOME来引用这个目录。在Linux上,您执行这样的命令
 
export BYTEMAN_HOME = 便使使 {PATH}:$ {BYTEMAN_HOME} / bin
 
如果您在Windows上安装Byteman,那么您将需要使用java命令代替shell脚本(请注意,从2.0.1版本开始,bin目录包含等效的Windows脚本 - 以.bat文件提供 - 它们使用与Linux脚本相同的命令行语法)。
 
无论如何,就是这样,安装完成。

使用Byteman如何运行程序?

使用Byteman有几种不同的方式来运行Java程序。最基本的方法是在java命令行中使用-javaagent选项

这是一个简单的应用程序类,将用于显示Byteman的动作

包org.my;
AppMain类

  1. {
  2.      public static void mainString [] args
  3.      {
  4.          forint i = 0; i <args.length; i ++){
  5.              System.out.println(参数[I]);
  6.          }
  7.      }
  8. }

你通常会编译并运行这个程序如下:

javac org/my/AppMain.java
java org.my.AppMain foo bar baz
foo
bar
baz

我们注入一些代码来跟踪进入和退出方法main。

首先,我们将创建一个Byteman规则脚本文件。只需在名为appmain.btm的工作目录中打开一个文件,并插入以下文本

  1. RULE trace main entry
  2. CLASS AppMain
  3. METHOD main
  4. AT ENTRY
  5. IF true
  6. DO traceln("entering main")
  7. ENDRULE
  8. RULE trace main exit
  9. CLASS AppMain
  10. METHOD main
  11. AT EXIT
  12. IF true
  13. DO traceln("exiting main")
  14. ENDRULE

该脚本包含两个规则,两个规则都指定要注入到CLASS AppMain的METHOD主体中的代码。第一个规则被注入AT ENTRY,即在该方法的开始处。要注入的代码是IF DO部分。在第一个规则中,它调用内建操作traceln(String),将其参数打印到System.out()后跟换行。第二个规则被注入AT EXIT,即控制从方法main返回的点。两个规则都指定条件IF为真,这意味着始终执行DO部分。

如果要计算运行DO之后的操作是否可以提供不同的Java表达式。

所有主要的桌面和服务器JVM都支持用于使用这些Byteman规则运行progarm的-javaagent选项。

语法为-javaagent:path_to_agent_jar = agent_option1,agent_option_2,。 。 。
 
用Byteman和这个规则集运行程序的Linux命令是
 

java -javaagent: {BYTEMAN_HOME} /lib/byteman.jar。我们只提供一个代理选项,跟随= sign脚本:appmain.btm,指定Byteman规则脚本的位置。 Byteman代理读取此选项,然后从文件appmain.btm加载并注入规则。如果我们要加载多个脚本,我们可以提供多个脚本:文件代理选项,用逗号分隔。

如何将代码注入JVM类?

那么我们来试试跟踪一些JVM操作。将以下规则文本插入名为thread.btm的新脚本文件。

  1. RULE trace thread start
  2. CLASS java.lang.Thread
  3. METHOD start()
  4. IF true
  5. DO traceln("*** start for thread: "+ $0.getName())
  6. ENDRULE

该规则被注入到JVM运行时CLASS java.lang.Thread的METHOD start()中。 它使用String +运算符打印粘贴在一起的跟踪消息。 现在,start()是一个实例方法,所以调用它时有一个特定的Thread实例,它是方法调用的目标。 特殊变量使 1,$ 2等引用。

Byteman知道 0.getName(),并验证结果类型是String。注入的代码调用此方法,将结果追加到常量String,并将结果传递给要写入System.out的方法traceln()。

我们将这个规则注入到我们原始类AppMain2的变体中,AppMain2创建一些线程来进行打印

  1. class AppMain2
  2. {
  3. public static void main(String[] args)
  4. {
  5. for (int i = 0; i < args.length; i++) {
  6. final String arg = args[i];
  7. Thread thread = new Thread(arg) {
  8. public void run() {
  9. System.out.println(arg);
  10. }
  11. };
  12. thread.start();
  13. try {
  14. thread.join();
  15. } catch (Exception e) {
  16. }
  17. }
  18. }
  19. }

要使用新脚本运行此程序,我们修改脚本:代理选项以引用文件thread.btm。但是,如果我们要将代码注入到JVM类中,这还不够。我们需要提供一些额外的选项/参数。在Linux上

  1. > java -javaagent:${BYTEMAN_HOME}/lib/byteman.jar=script:thread.btm,boot:${BYTEMAN_HOME}/lib/byteman.jar -Dorg.jboss.byteman.transform.all org.my.AppMain2 foo bar baz

or on Windows

  1. > java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:thread.btm,boot:%BYTEMAN_HOME%\lib\byteman.jar -Dorg.jboss.byteman.transform.all org.my.AppMain2 foo bar baz

Class Thread是一个JVM类,它意味着它被引导类加载器加载。如果Byteman代理类也由引导类加载器加载,Byteman只能将代码注入此类。额外的代理选项boot:$ {BYTEMAN_HOME} /lib/byteman.jar附加在脚本选项之后(注意用作分隔符的逗号)。这样可以确保将byteman代理jar安装到引导类路径中。

Class Thread也恰好在java.lang包中。 通常,Byteman是非常谨慎的,不会将代码注入到这个包中,以防止它挂起JVM。 如果你真的想改变这个包中的类的方法,你需要定义系统属性org.jboss.byteman.transform.all。

当我们运行该程序时,我们得到这个输出

有比较简单的方式来运行Byteman吗?

在Linux上,您可以使用命令脚本bmjava.sh来包装-javaagent选项。 它看起来非常像java命令,但它在命令行上接受Byteman规则脚本,并将其捆绑为-javaagent script:options。 它也使用boot:agent选项自动捆绑在byteman jar中。 所以,使用bmjava.sh,以前的命令行简化为

  • bmjava.sh -l thread.btm org.my.AppMain2 foo bar baz

请注意,使用的标志是-l用于加载。

在Windows上,如果您使用的是byteman 2.0.1或更新版本,则有一个名为bmjava.bat的等效脚本,您可以使用命令bjava执行该脚本。 所以以前的命令就简化了

  • bmjava -l thread.btm org.my.AppMain2 foo bar baz

请注意,您需要将安装的bin目录$ {BYTEMAN_HOME} / bin添加到路径中,以便能够按名称执行脚本(在Windows上使用%BYETMAN_HOME%/ bin)

如何将规则加载到正在运行的程序中?

如果您有一个长时间运行的程序,您可能希望在程序开始运行后加载规则,甚至卸载规则并加载新的规则。 如果你告诉Byteman来启动代理监听器,你可以这样做。 监听器还允许您检查加载的规则的状态。 为了显示在这里使用的听众是我们的程序AppMain3的另一个变体。

  1. package org.my;
  2. import java.io.DataInputStream;
  3. class AppMain3
  4. {
  5. public static void main(String[] args)
  6. {
  7. try {
  8. DataInputStream in = new DataInputStream(System.in);
  9. String next = in.readLine();
  10. while (next != null && next.length() > 0 && !next.contains("end")) {
  11. final String arg = next;
  12. Thread thread = new Thread(arg) {
  13. public void run() {
  14. System.out.println(arg);
  15. }
  16. };
  17. thread.start();
  18. try {
  19. thread.join();
  20. } catch (Exception e){
  21. }
  22. next = in.readLine();
  23. }
  24. } catch (Exception e) {
  25. }
  26. }
  27. }

监听器打开服务器套接字,然后等待传入命令。 请注意,没有将规则脚本指定为代理选项,所以最初在输入时只是回显

在Linux上,我们使用命令脚本bmsubmit.sh与代理监听器通信。 调用它没有参数列出所有安装的规则的状态

如何查看哪些规则被加载和编译?

再次运行bmsubmit.sh,我们可以看到规则已经成功编译。

  1. > bmsubmit.sh
  2. # File thread.btm line 4
  3. RULE trace thread start
  4. CLASS java.lang.Thread
  5. METHOD start()
  6. AT ENTRY
  7. IF true
  8. DO traceln("*** start for thread: "+ $0.getName())
  9. ENDRULE
  10. Transformed in:
  11. loader: sun.misc.Launcher$AppClassLoader@5acac268
  12. trigger method: java.lang.Thread.start() void
  13. compiled successfully
  14. >

这一次,输出结束处有一个额外的线,编译成功。 这意味着规则已被类型检查并执行。 类型检查仅在规则首次触发时发生 - 在这种情况下,当在包含单词baz的行中键入后,调用Thread.start()。 如果类型检查失败,则执行注入的代码将被禁止,并且列表将包括类型错误的详细信息。 注释代码只有在卸载规则时才会被删除。

如何卸载规则?

我们也可以使用bmsubmit.sh(或Windows上的Submit Submit)来卸载规则,恢复Thread.start()的原始行为。

  1. > bmsubmit.sh -u thread.btm
  2. uninstall RULE trace thread start
  3. > bmsubmit.sh
  4. no rules installed

-u标志后跟规则脚本文件的名称。 文件中提到的所有规则都将被卸载,从他们注入的任何方法中删除其IF DO代码。 如果我们再次切换到该程序,它现在将恢复为仅回显输入文本

  1. . . .
  2. mumble
  3. *** start for thread: mumble
  4. mumble
  5. grumble
  6. grumble
  7. bletch
  8. bletch
  9. end
  10. >

注: 如果要更改注入的规则代码,则不必卸载,然后重新加载规则。 如果您提交修改版本的规则脚本,Byteman将删除任何现有注入的代码并重新注入新代码。

如何将代理安装到正在运行的程序中?

您有时会发现,您启动您的程序,而不加载Byteman代理选项,只能意识到您希望使用Byteman来检查其行为。

您有时会发现,您启动您的程序,而不加载Byteman代理选项,只能意识到您希望使用Byteman来检查其行为。 这通常发生在长时间运行的程序,如JBoss应用程序服务器,当您注意到一个日志消息指示某些事情已经开始行为不正。 您不必重新启动程序,以便能够使用Byteman,至少不是如果您在Hotspot,JRockit或OpenJDK JVM上运行代码。 这些JVM都允许您将代理程序安装到正在运行的程序中。

如果您在Linux上运行,则可以使用命令脚本bminstall.sh安装代理程序

  1. > bminstall.sh 13101

n.b.不是所有JVM都允许代理被动态加载。 在使用Oracle JVM,OpenJDK和JRockit JVM以及在使用Oracle JVM和OpenJDK JVM时在OSX上,已知可在Linux上运行。 尚未发现在IBM JVM或Windows上的任何JVM上都可以工作。

在Windows上,如果您使用2.0.1或更高版本,您可以使用等效的脚本bminstall.bat。否则,您需要执行Install.main()传递相关参数。

数字参数是要安装代理程序的进程的id - 显然,您需要提供一个适合您要检查的Java程序的值。或者,您可以提供程序的Java主类的名称(如果您使用java -jar myapp.jar启动它的话,也可以提供该名称)。如果要将Byteman代理安装到正在运行的JBoss Application Server实例中,则此选项非常有用。让我们再次运行AppMain3,但没有-javaagent选项,然后加载代理和一些规则,它已经开始运行。

现在在另一个命令shell中,我们将安装代理。在Linux上。

bminstall.sh不加载任何规则脚本。但是,它会自动启用代理侦听器,允许您使用bmsubmit.sh提交规则。如果您尝试使用上述示例中给出的命令提交和取消提交在thread.btm中定义的规则,您将看到它们修改AppMain3的行为。

如何使用Byteman运行JBoss AS?

如果要在启动时将Byteman安装到JBoss应用服务器中,则需要使用-javaagent选项。 但是JBoss AS是通过在命令脚本中执行java命令来运行的,对于AS 4,5或6执行run.sh,对于AS 7,standalone.sh或domain.sh。那么,如何确保这些脚本通过必要的 -javaagent选项到JVM?

With AS 4, 5 and 6 you can pass the required argument to the scripts by setting the environment variable JAVA_OPTS. On Linux you might use the following command

  1. set JAVA_OPTS="${JAVA_OPTS} -Dorg.jboss.byteman.transform.all -javaagent:${BYTEMAN_HOME}/lib/byteman.jar=script:thread.btm,boot:${BYTEMAN_HOME}/lib/byteman.jar,listener:true"

or on Windows

  1. set JAVA_OPTS="%JAVA_OPTS% -Dorg.jboss.byteman.transform.all -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:thread.btm,boot:%BYTEMAN_HOME%\lib\byteman.jar,listener:true"

请注意,该命令插入JAVA_OPTS的当前值,保留可能已设置的任何其他java选项。

使用AS 7,您通常会在位于AS7安装的bin子目录中的配置文件中插入诸如JAVA_OPTS之类的变量的配置设置。 在Linux上,您需要编辑standalone.conf或domain.conf文件。 在Windows上,您可以编辑standalone.conf.bat或domain.conf.bat文件。 如果编辑这些文件,您将看到有一个明显的地方可以将-javaagent选项插入到JAVA_OPTS中。 您将使用一个设置,就像上面给出的示例一样。

我如何知道我的规则是否正确?

加载规则后,重新运行bmsubmit.sh并没有参数是有用的,看看他们是否遇到了解析或类型错误。不过,提前确定这些问题通常更好。 Byteman允许您在将其安装到Java程序之前解析并键入检查规则。在Linux上运行命令脚本bmcheck.sh

  1. > bmcheck.sh thread.btm
  2. checking rule trace thread start
  3. parsed rule "trace thread start" for class java.lang.Thread
  4. type checked rule "trace thread start"
  5. TestScript: no errors
  6. >

如果规则脚本引用应用程序类,那么您需要使用-cp标志来告诉bmcheck.sh找到它们。 所以,我们需要使用这个参数来检查我们的第一个脚本appmain.btm。 但这还不够。 这是第一条规则

如何判断我的规则是否正在运行?

有时您可能不确定您的规则是否被正确注入。 也许目标方法从不执行。 也许规则条件总是假的。 或者可能是解析或类型错误正在阻止它们被执行。 如果您以详细模式运行Byteman,您将看到跟踪输出告诉您Byteman正在做什么。 通过设置系统属性启用详细模式

  1. > java -Dorg.jboss.byteman.verbose -javaagent:${BYTEMAN_HOME}/lib/byteman.jar=script:appmain.btm org.my.AppMain foo bar baz
  2. org.jboss.byteman.agent.Transformer : possible trigger for rule trace main entry in class org.my.AppMain
  3. RuleTriggerMethodAdapter.injectTriggerPoint : inserting trigger into org.my.AppMain.main(java.lang.String[]) void for rule trace main entry
  4. org.jboss.byteman.agent.Transformer : inserted trigger for trace main entry in class org.my.AppMain
  5. org.jboss.byteman.agent.Transformer : possible trigger for rule trace main exit in class org.my.AppMain
  6. RuleTriggerMethodAdapter.injectTriggerPoint : inserting trigger into org.my.AppMain.main(java.lang.String[]) void for rule trace main exit
  7. org.jboss.byteman.agent.Transformer : inserted trigger for trace main exit in class org.my.AppMain
  8. Rule.execute called for trace main entry_0
  9. HelperManager.install for helper classorg.jboss.byteman.rule.helper.Helper
  10. calling activated() for helper classorg.jboss.byteman.rule.helper.Helper
  11. Default helper activated
  12. calling installed(trace main entry) for helper classorg.jboss.byteman.rule.helper.Helper
  13. Installed rule using default helper : trace main entry
  14. trace main entry execute
  15. entering main
  16. foo
  17. bar
  18. baz
  19. Rule.execute called for trace main exit_1
  20. HelperManager.install for helper classorg.jboss.byteman.rule.helper.Helper
  21. calling installed(trace main exit) for helper classorg.jboss.byteman.rule.helper.Helper
  22. Installed rule using default helper : trace main exit
  23. trace main exit execute
  24. exiting main
  25. >

跟踪是相当冗长的,但是您需要的所有信息都在那里,如果你寻找它。

当加载类AppMain时,会识别每个规则的触发器位置,并注入规则代码Next主方法被调用,您可以看到条目规则的执行消息(您可以立即忽略有关助手激活和规则安装的消息 或者如果要解释,请阅读Ptrogrammer指南)执行消息后面是输入规则的跟踪消息。
主要方法打印它的所有ouptut
在它返回之前,还有一个执行消息的退出规则(再次,只是忽略有关规则安装的消息)
第二个执行消息后面是退出规则的跟踪消息。

如何让我的规则运行得很快?

通常,Byteman通过解释解析的规则代码来执行规则。 这对于大多数使用来说足够快,但是如果您将代码注入紧密循环或被称为频繁的方法,那么这可能会减慢您的程序。 您可以通过要求Byteman将规则代码转换为JIT编译器可以优化的字节码来加快速度。 这使得规则的第一次执行需要更长的时间,但是后续的执行应该更快。

通过在安装代理程序时设置系统属性org.jboss.byteman.compile.to.bytecode来启用规则编译。 您可以在java命令行或使用bminstall.sh安装代理程序时执行此操作

哪里可以下载教程来源?

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注