byteman_Byteman –用于字节码操纵的瑞士军刀

byteman

我正在与JBoss中的许多社区一起工作,有很多有趣的事情要谈论,以至于我无法将自己的每一分都缠住。 这就是为什么我非常感谢有机会不时地欢迎客座博客的主要原因。 今天是Jochen Mader,他是以代码为中心的书呆子群的一部分。 他目前花费大量的时间在基于Vert.x的中间件解决方案编码,为不同的出版物撰写文章以及在会议上发表演讲。 他的业余时间属于他的家人,山地车和桌面游戏。 您可以在Twitter @codepitbull上关注他。

有些工具通常是您不希望使用的,但在需要时会很高兴知道它们。 至少对我来说,Byteman属于这一类。 这是我个人的瑞士军刀,要处理一个大泥球或其中一个可怕的黑森贝格。 因此,获取当前的Byteman发行版 ,将其解压缩到您计算机上的某个位置,我们将进行一些繁琐的工作。

它是什么

Byteman是字节码操作和注入工具套件。 它使我们能够拦截和替换Java代码的任意部分,以使其表现不同或(故意)破坏它:

  • 将所有线程卡在某个位置,并让它们同时继续(您好,竞争条件)
  • 在意外的位置抛出异常
  • 在执行过程中跟踪代码
  • 更改返回值

还有更多的东西。

一个例子

让我们直接看一些代码来说明Byteman可以为您做些什么。

在这里,我们有一个很棒的Singleton和一个(可悲的)很好的示例代码,您可能在很多地方都可以找到。

public class BrokenSingleton {private static volatile BrokenSingleton instance;private BrokenSingleton() {}public static BrokenSingleton get() {if (instance == null) {instance = new BrokenSingleton();}return instance;}
}

我们假装自己是可怜的人,负责调试一些旧代码,这些旧代码显示了生产中的怪异行为。 一段时间后,我们发现了这颗宝石,我们的直觉表明这里出了点问题。

首先,我们可以尝试如下操作:

public class BrokenSingletonMain {public static void main(String[] args) throws Exception {Thread thread1 = new Thread(new SingletonAccessRunnable());Thread thread2 = new Thread(new SingletonAccessRunnable());thread1.start();thread2.start();thread1.join();thread2.join();}public static class SingletonAccessRunnable implements Runnable {@Overridepublic void run() {System.out.println(BrokenSingleton.get());}}
}

运行此命令,很少有机会看到实际的问题发生。 但是最有可能我们不会看到任何异常情况。 Singleton初始化一次,应用程序按预期执行。 很多时候,人们开始通过增加线程数来进行暴力破解,以期使问题得以解决。 但是我更喜欢一种结构化的方法。

输入Byteman。

DSL

Byteman提供了方便的DSL来修改和跟踪应用程序的行为。 在我的小示例中,我们将从跟踪调用开始。 看一下这段代码。

RULE trace entering
CLASS de.codepitbull.byteman.BrokenSingleton
METHOD get
AT ENTRY
IF true
DO traceln("entered get-Method")
ENDRULERULE trace read stacks
CLASS de.codepitbull.byteman.BrokenSingleton
METHOD get
AFTER READ BrokenSingleton.instance
IF true
DO traceln("READ:\n" + formatStack())
ENDRULE

Byteman脚本的核心构建模块是RULE。

它由几个组件组成(例如从Byteman-Docs中毫不客气地示例:

# rule skeletonRULE <rule name>CLASS <class name>METHOD <method name>BIND <bindings>IF <condition>DO <actions>ENDRULE

每个规则都必须具有唯一的__规则名称__。 CLASS和METHOD的组合定义了我们希望将修改应用到的位置。 BIND允许我们将变量绑定到可以在IF和DO中使用的名称。 使用IF,我们可以添加触发规则的条件。 在DO中,实际的魔术发生了。

ENDRULE,它结束规则。

知道这一点,我的第一条规则很容易转换为:

当有人调用_de.codepitbull.byteman.BrokenSingleton.get()_时,我想在调用方法主体之前(即__AT ENTRY__转换为)打印字符串“ entered get-Method”。

我的第二条规则可以转换为:

读取(__AFTER READ__)之后,我想查看当前的调用堆栈。

抓取代码并将其放入名为_check.btm_的文件中。 Byteman提供了一个不错的工具来验证您的脚本。 使用__ <bytemanhome> /bin/bmcheck.sh -cp文件夹/包含/已编译/类/至/测试check.btm__来查看脚本是否可以编译。 每次更改时都要执行此操作,很容易弄错细节并花很长时间弄清楚它。

现在,脚本已保存并经过测试,现在可以在我们的应用程序中使用它了。

中介

脚本通过代理应用于运行代码。 打开__BrokenSingletonMain-class__的运行配置并添加

__-javaagent:<BYTEMAN_HOME>/lib/byteman.jar=script:check.btm__

到您的JVM参数。 这将注册代理并告诉它运行_check.btm_。

而当我们在这里时,还有更多选择:

如果您需要操纵一些核心Java东西,请使用

__-javaagent:<BYTEMAN_HOME>/lib/byteman.jar=script:appmain.btm,boot:<BYTEMAN_HOME>/lib/byteman.jar__

这会将Byteman添加到引导类路径中,并允许我们操纵_Thread _,_ String_之类的类……我的意思是,如果您想处理这种讨厌的事情……

也可以将代理附加到正在运行的进程。 我们__jps__查找您要附加并运行的进程ID

__<bytemanhome>/bin/bminstall.sh <pid>__

安装代理。 之后运行

__<bytemanhome>/bin/bmsubmit.sh check.btm__

回到我们眼前的问题。

使用修改后的run-Configuration运行我们的应用程序,应得到如下输出

entered get-Method
entered get-Method
READ:
Stack trace for thread Thread-0
de.codepitbull.byteman.BrokenSingleton.get(BrokenSingleton.java:14)
de.codepitbull.byteman.BrokenSingletonMain$SingletonAccessRunnable.run(BrokenSingletonMain.java:20)
java.lang.Thread.run(Thread.java:745)READ:
Stack trace for thread Thread-1
de.codepitbull.byteman.BrokenSingleton.get(BrokenSingleton.java:14)
de.codepitbull.byteman.BrokenSingletonMain$SingletonAccessRunnable.run(BrokenSingletonMain.java:20)
java.lang.Thread.run(Thread.java:745)

恭喜,您刚刚操作了字节码。 输出还不是很有帮助,但这是我们要更改的东西。

线程混乱

现在,随着我们的基础架构的建立,我们可以开始更深入地挖掘。 我们非常确定我们的问题与某些多线程问题有关。 为了检验我们的假设,我们必须同时将多个线程放入关键部分。 使用纯Java,这几乎是不可能的,至少在不对我们要调试的代码进行大量修改的情况下。

使用Byteman可以轻松实现。

RULE define rendezvous
CLASS de.codepitbull.byteman.BrokenSingleton
METHOD get
AT ENTRY
IF NOT isRendezvous("rendezvous", 2)
DO createRendezvous("rendezvous", 2, true);
traceln("rendezvous created");
ENDRULE

该规则定义了一个所谓的集合点。 它允许我们指定多个线程必须到达的位置,直到允许它们继续前进(也称为aa障碍)。

这是规则的翻译:

当调用_BrokenSingleton.get()_时,创建一个新的集合点,当2个线程到达时将允许进度。 使集合点可重用,并仅在不存在时才创建它(IF NOT部分至关重要),否则,我们将在每次对_BrokenSingleton.get()_的调用上创建一个障碍。

定义此障碍后,我们仍然需要显式使用它。

RULE catch threads
CLASS de.codepitbull.byteman.BrokenSingleton
METHOD get
AFTER READ BrokenSingleton.instance
IF isRendezvous("rendezvous", 2)
DO rendezvous("rendezvous");
ENDRULE

翻译:读取_BrokenSingleton.get()_中的_instance_-member后,在集合点等待,直到第二个线程到达并继续。

现在,在实例空检查之后,我们停止来自同一花边中_BrokenSingletonMain_的两个线程。 这就是使比赛条件可再现的方法。 两个线程将继续认为_instance_为null,从而导致构造函数触发两次。

我将这个问题的解决方案留给您……

单元测试

我在撰写此博客文章时发现,有可能在我的单元测试中运行Byteman脚本。 它们的JUNit和TestNG集成很容易集成。

将以下依赖项添加到_pom.xml_

<dependency><groupId>org.jboss.byteman</groupId>   <artifactId>byteman-submit</artifactId><scope>test</scope><version>${byteman.version}</version>
</dependency>

现在,Byteman脚本可以在您的单元测试中执行,如下所示:

@RunWith(BMUnitRunner.class)
public class BrokenSingletonTest
{@Test@BMScript("check.btm")public void testForRaceCondition() {...}
}

将此类测试添加到您的西装中会大大提高Byteman的有用性。 没有更好的办法阻止其他人重复这些错误,因为这些脚本是构建过程的一部分。

结束语

博客文章中只有这么多空间,我也不想开始重写他们的文档。 写这篇文章是一件很有趣的事情,因为我已经有一段时间没有使用Byteman了。 我不知道我如何忽略了单元测试的集成。 这将使我将来更多地使用它。

现在,我建议浏览他们的文档并开始进行注入,还有很多事情要做。

翻译自: https://www.javacodegeeks.com/2015/02/byteman-swiss-army-knife-byte-code-manipulation.html

byteman

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338132.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html 怎么置顶表格,表格(Table)表头固定,内容上滚【5个实例】

当表格往上滚动的时候&#xff0c;表头固定不动&#xff0c;这样可以让用户时刻看清每一列的数据含义&#xff0c;这是人性化的设计&#xff0c;充分考虑了用户使用体验。本文将通过5个实例&#xff0c;来介绍这种表格设计。用户可通过下载源码&#xff0c;直接应用于自己的项目…

C语言变量的定义包括变量存储类型和变量的什么?

C语言变量的定义包括变量存储类型和变量的名称。C语言定义变量的格式&#xff1a;“数据类型 变量名;”&#xff0c;“数据类型”表示想要存储什么类型的数据&#xff0c;“变量名”就是你想给这个变量起个什么名字&#xff0c;通常都是用字母。变量的定义定义变量的格式非常简…

java 调用祖父方法_在Java中调用祖父母方法:您不能

java 调用祖父方法在文章保护的重点中&#xff0c;我详细介绍了“受保护”如何扩展“包私有”访问。 我在那儿写道&#xff1a; 你能做的是 覆盖子类中的方法或 使用关键字super调用parent方法。 通常&#xff0c;这实际上是您可以使用受保护的方法完成的所有操作。 &…

C语言代码注释必须用/**/ , 你没看错~

事情是这样的&#xff0c;有人离职&#xff0c;公司调我补缺。那个系统一直有个工程师在维护&#xff0c;参与该系统的新人来了又走&#xff0c;他始终泰然自若。刚过去一个礼拜&#xff0c;我就心下窃吼&#xff1a;“坑爹啊!”&#xff0c;也彻底体会到什么叫---绝对的权威、…

java8 streams_另一个Java 8 Lamdbas和Streams示例

java8 streams我一直落后于Java 8所关注的功能&#xff0c;因此在这篇文章中&#xff0c;我将简要介绍我对lambda和stream的初步经验。 和往常一样&#xff0c;我将专注于Podcast课程&#xff1a; package org.codingpedia.learning.java.core;import java.util.Comparator;p…

html文档的基本类型,HTML(网页的文档类型介绍)

一个html文件的第一行代码通常就是用于声明网页文档类型&#xff0c;其格式是:这一行不是属于标签文档类型:可以理解为不同的html版本&#xff01;html4.0 或4.01版本基本固定&#xff0c;但又有分化:严格性:了用的标签和属性相对较少&#xff0c;但能兼容更多的浏览器。宽松型…

C语言源代码展示:常用转换函数实现原理

编程时经常用到进制转换、字符转换。比如软件界面输入的数字字符串&#xff0c;如何将字符串处理成数字呢&#xff1f;和大家分享一下。01字符串转十六进制代码实现&#xff1a;void StrToHex(char *pbDest, char *pbSrc, int nLen) {char h1,h2;char s1,s2;int i;for (i0; i …

jax-rs jax-ws_在JAX-RS中处理异步请求中的超时

jax-rs jax-wsJAX-RS 2.0在客户端和服务器端都支持异步编程范例。 这篇文章重点介绍了使用JAX-RS&#xff08;2.0&#xff09;API在服务器端执行异步REST请求时的超时功能 无需过多介绍&#xff0c;这里是一个快速概述。 为了以异步方式执行方法&#xff0c;您只需 需要指定A…

html5 移动 优化,第四天:HTML5移动站优化技巧 摘自《10天学会移动站SEO》

现在大家基本上做手机网站都是做成HTML5的&#xff0c;因为现在智能手机等移动设备越来越多&#xff0c;几乎全部支持HTML5&#xff0c;那么给网站适配上HTML5的网站就很是必要了。以前的WML网站已经淘汰&#xff0c;而最新的方式就这种最好。我们这一节就重点讲一讲HTML5移动网…

c语言中switch的用法是什么?

c语言中switch的用法是&#xff1a;功能&#xff1a;switch语句是多分支选择语句.用来实现多分支选择结构.if语句只有两个分支可供选择,而实际问题中常常要用到多分支的选择.例如,学生成绩分类(90为"A"等,80-89分为B等,70-90分为C等......).当然这些都可以用嵌套的if…

简述垃圾对象产生_使用零垃圾创建数百万个对象

简述垃圾对象产生如性能优化第一规则中所述&#xff0c;垃圾是快速代码的敌人。 通过使用垃圾收集器的服务&#xff0c;它不仅会破坏任何确定性的性能&#xff0c;而且我们开始用垃圾填充CPU高速缓存&#xff0c;这将导致程序的高速缓存未命中。 那么&#xff0c;我们可以在不…

光学模拟 Android,基于Android平台的光学字符识别应用的设计与实现

摘要&#xff1a;随着数字化时代的蓬勃发展,信息量以指数级的速度增长,然而手工录入并存储信息的速度远不及信息的产生速度.光学字符识别(OCR)技术能够自动化地检测信息并识别出来,有效地解决了信息录入速度和正确率的问题.目前,基于PC设备的光学字符识别已经被广泛的应用于办公…

C语言“fread”函数的用法?

C语言“fread”函数的用法为“size_tf read(void *buffer,size_t size,size_t count,FILE *stream)”&#xff0c;其作用是从一个文件流中读数据&#xff0c;读取count个元素&#xff0c;每个元素size字节。示例1#include #include #include int main(){ FILE *stream; c…

html怎么设置数据条的颜色,jQuery EasyUI 数据网格 – 条件设置行背景颜色 | 菜鸟教程...

jQuery EasyUI 数据网格 - 条件设置行背景颜色本教程将向您展示如何根据一些条件改变数据网格(datagrid)组件的行样式。当 listprice 值大于 50 时&#xff0c;我们将为该行设置不同的颜色。数据网格(datagrid)的 rowStyler 函数的设计目的是允许您自定义行样式。以下代码展示如…

C语言中for语句的执行过程是什么?

C语言中for语句的执行过程是&#xff1a;for语句的一般形式为&#xff1a;for&#xff08;单次表达式;条件表达式;末尾循环体&#xff09;{中间循环体&#xff1b;}。for循环执行时&#xff0c;会先判断条件表达式是否成立&#xff0c;如果条件成立则执行中间循环体&#xff0c…

解耦,未解耦的区别_幂等与时间解耦之旅

解耦,未解耦的区别HTTP中的幂等性意味着相同的请求可以执行多次&#xff0c;效果与仅执行一次一样。 如果用新资源替换某个资源的当前状态&#xff0c;则无论您执行多少次&#xff0c;最终状态都将与您仅执行一次相同。 举一个更具体的例子&#xff1a;删除用户是幂等的&#x…

c语言getch()的用法是什么?

C语言中getch()函数功 能&#xff1a; 从stdio流中读字符&#xff0c;即从控制台读取一个字符&#xff0c;但不显示在屏幕上用 法:int getchar(void);这个函数是一个不回显函数&#xff0c;当用户按下某个字符时&#xff0c;函数自动读取&#xff0c;无需按回车&#xff0c;有的…

rx.observable_在Spring MVC流中使用rx-java Observable

rx.observableSpring MVC现在已经支持异步请求处理流程了一段时间&#xff0c;该支持内部利用了Tomcat / Jetty等容器的Servlet 3异步支持。 Spring Web Async支持 考虑一下需要花一点时间处理的服务呼叫&#xff0c;该服务呼叫具有延迟&#xff1a; public CompletableFutur…

淮安中专学计算机哪个学校好,2021淮安初中十强排名 哪些初中比较好

对于淮安的学生来说&#xff0c;了解淮安初中排名会更有利于选择初中。那么&#xff0c;2021淮安初中十强有哪些学校呢?下面小编整理了一些相关信息&#xff0c;供大家参考!2021淮安十大初中排名1、淮安兴隆中学2、淮安郑梁梅中学华禹分校3、淮安高堰九年制学校4、淮安长江路中…

C 隐式类型转换是什么?

C 隐式类类型转换《C Primer》中提到&#xff1a;“可以用 单个形参来调用 的构造函数定义了从 形参类型 到 该类类型 的一个隐式转换。”这里应该注意的是&#xff0c; “可以用单个形参进行调用” 并不是指构造函数只能有一个形参&#xff0c;而是它可以有多个形参&#xff0…