前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Byteman 使用指南(九)

Byteman 使用指南(九)

作者头像
FunTester
发布2025-02-19 12:57:37
发布2025-02-19 12:57:37
1400
代码可运行
举报
文章被收录于专栏:FunTester
运行总次数:0
代码可运行

本文重点讲解 Byteman 规则状态管理操作,以下是具体的内容。

1. 链接映射(LinkMaps)

规则引擎提供了 LinkMaps,用于在规则触发时记录信息,供其他规则或测试运行结束时检索。链接映射本质上是一个命名的 Map,它将一个 Object 与另一个 Object 关联。帮助器类定义的 API 如下:

代码语言:javascript
代码运行次数:0
复制
boolean createLinkMap(Object mapName)
boolean deleteLinkMap(Object mapName)
Object link(Object mapName, Object name, Object value)
Object linked(Object mapName, Object name)
Object unlink(Object mapName, Object name)
List<Object> linkNames(Object mapName)
List<Object> linkValues(Object mapName)
boolean clearLinks(Object mapName)
Object link(Object name, Object value)
Object linked(Object name)
Object unlink(Object name)
List<Object> linkNames()
List<Object> linkValues()
boolean clearLinks()

API 方法中省略了 mapName 参数的版本会在默认映射上操作,该映射是预先定义的映射,标签为全局 String 名称 "default"。然而,您可以拥有任意数量的映射,使用任何方便的对象作为规则触发时的标签。当运行多线程程序时,通常使用当前线程来命名 LinkMap 是有用的,因为这确保了一个线程保存的值不会被其他线程覆盖。

  • createLinkMap:用于在使用之前创建一个 LinkMap。然而,这并不总是必要的,因为其他 API 函数会在需要时自动创建映射。如果映射不存在,它返回 true;如果映射已存在,则返回 false
  • deleteLinkMap:用于删除 LinkMap。这很有用,因为它确保了清除了映射中链接的所有对象的所有引用。如果找到了标签为 mapName 的映射并已删除,它返回 true;否则返回 false
  • link:用于向映射中添加从 namevalue 的链接。没有 mapName 参数的版本将链接添加到默认映射中(标签为 "default" 的映射)。调用的返回值是之前与 name 链接的任何先前值,如果没有先前的链接存在于映射中,则为 null
  • linked:用于从映射中检索通过 name 链接的值。没有 mapName 参数的版本从默认映射中检索值。调用的返回值是从 name 链接的值,如果没有链接存在于映射中,或者找不到标签为 mapName 的映射,则为 null
  • unlink:用于从映射中移除任何从 name 的链接。没有 mapName 参数的版本将默认映射中的链接移除。调用的返回值是从 name 解除链接的值,如果没有链接存在于映射中,则为 null
  • linkNames:用于检索映射中用作链接名称的所有对象的列表。没有 mapName 参数的版本检索默认映射中所有链接的名称。调用的返回值将是一个可能为空的列表,如果在调用时找到了映射。如果找不到标签为 mapName 的映射,它将为 null
  • linkValues:用于检索映射中作为链接值出现的所有对象的列表。没有 mapName 参数的版本检索默认映射中所有链接的值。调用的返回值将是一个可能为空的列表,如果在调用时找到了映射。如果找不到标签为 mapName 的映射,它将为 null
  • clearLinks:用于原子地从映射中清除所有链接。没有 mapName 参数的版本清除了标签为 "default" 的默认映射中的所有链接。如果在调用时找到了一个非空的标签为 mapName 的映射并清除了它,返回值将是 true。如果没有找到映射,或者找到了一个空映射,它将返回 false

2. 倒计时(CountDowns)

规则引擎提供了 CountDowns,用于确保在触发其他规则或一定次数后,某些规则才会触发。帮助器类定义的 API 如下:

代码语言:javascript
代码运行次数:0
复制
public boolean createCountDown(Object identifier, int count)
public boolean getCountDown(Object identifier)
public boolean countDown(Object identifier)

CountDowns 由任意对象标识,允许连续对 countdown API 的调用应用于相同或不同的情况。这种识别可以跨不同的规则和帮助器实例进行。例如,一个规则可能包括动作 createCountDown(0, 1),另一个规则可能包括条件 countDown(0)。由第一个规则创建的 CountDown 只有在第二个规则被触发的方法调用具有相同的值时才会减少。由不同值的调用创建的 CountDown 将相应地匹配。然而,如果 CountDown 是用一个共同的 String 文字(即动作和条件都是 createCountDown("counter", 1) 和 countDown("counter"))标识的,那么第一个规则创建的 CountDown 将被下一个触发的第二个规则减少,无论触发方法调用是否在相关实例上。

  • createCountDown:用于创建 CountDown。count 参数指定 CountDown 将被减少的次数,然后减少操作失败,即如果 count 是 1,那么 CountDown 将减少一次,然后在下一次减少时失败。如果 count 以小于 1 的值提供,它将被替换为值 1。createCountDown 通常在规则动作中使用。然而,它被定义为如果创建了新的 CountDown 则返回 true,如果已经与标识符关联了 CountDown 则返回 false。这允许它在规则条件中使用,其中几个规则可能在竞争创建 CountDown。
  • getCountDown:用于在规则条件中使用,以测试是否存在与给定标识符关联的 CountDown,如果存在则返回 true,否则返回 false
  • countDown:用于在规则条件中使用,以减少 CountDown。如果减少成功,或者没有与标识符关联的 CountDown,它返回 false。如果 CountDown 失败,即它的计数为 0,它返回 true。在后一种情况下,标识符和 CountDown 之间的关联被删除,允许使用相同的标识符开始新的 CountDown。请注意,这种行为确保了多个线程尝试从规则条件减少计数器之间的竞争只有一个赢家。

3. 标志(Flags)

规则引擎提供了一个简单的机制来设置、测试和清除全局标志。帮助器类定义的 API 如下:

代码语言:javascript
代码运行次数:0
复制
public boolean flag(Object identifier)
public boolean flagged(Object identifier)
public boolean clear(Object identifier)

与以前一样,标志由任意对象标识。所有三个方法都旨在在规则条件或动作中使用。

  • flag:可以被调用以确保标识为 identifier 的标志被设置。如果标志以前是清除的,它返回 true,否则返回 false。请注意,API 设计旨在确保多个线程尝试从规则条件设置标志之间的竞争只有一个赢家。
  • flagged:测试标识为 identifier 的标志是否被设置。如果标志被设置,它返回 true,否则返回 false
  • clear:可以被调用以确保标识为 identifier 的标志被清除。如果标志以前是设置的,它返回 true,否则返回 false。请注意,API 设计旨在确保多个线程尝试从规则条件清除标志之间的竞争只有一个赢家。

4. 计数器(Counters)

规则引擎提供了 Counters,用于维护独立规则触发之间的全局计数。它们可以被创建和初始化,读取,递增和递减,以跟踪和响应各种触发或触发发生的次数。请注意,与 CountDowns 不同,递减计数器到零没有特殊语义。它们甚至可能有负值。帮助器类定义的 API 如下:

代码语言:javascript
代码运行次数:0
复制
public boolean createCounter(Object o)
public boolean createCounter(Object o, int count)
public boolean deleteCounter(Object o)
public int incrementCounter(Object o, int amount)
public int incrementCounter(Object o)
public int decrementCounter(Object o)
public int readCounter(Object o)
public int readCounter(Object o, boolean zero)

与以前一样,计数器由任意对象标识。所有方法都旨在在规则条件或动作中使用。

  • createCounter:可以被调用以创建一个新的与 o 相关联的 Counter。如果未提供参数 count,则新 Counter 的值默认为 0createCounter 返回 true 如果创建了一个新的 Counter,如果已经与 o 关联了 Counter,则返回 false。请注意,API 设计旨在确保多个线程尝试从规则条件创建 Counter 之间的竞争只有一个赢家。
  • deleteCounter:可以被调用以删除与 o 相关联的任何现有 Counter。它返回 true 如果已删除 Counter,如果与 o 没有关联的 Counter,则返回 false。请注意,API 设计旨在确保多个线程尝试从规则条件删除 Counter 之间的竞争只有一个赢家。
  • incrementCounter:可以被调用以递增与 o 相关联的 Counter。如果不存在这样的 Counter,它将创建一个初始值为 0 的 Counter,然后递增它。incrementCounter 返回新 Counter 的值。如果省略了 amount,则默认为 1。
  • decrementCounter:等同于调用 incrementCounter(o, -1),即它将计数器的值加上 -1。
  • readCounter:可以被调用以读取与 o 相关联的 Counter 的值。如果不存在这样的 Counter,它将创建一个初始值为 0 的 Counter。如果省略了可选标志参数 zero,则默认为 false

5. 计时器(Timers)

规则引擎提供了 Timers,允许测量触发之间的经过时间。可以通过以下 API 创建、读取、重置和删除计时器:

代码语言:javascript
代码运行次数:0
复制
public boolean createTimer(Object o)
public long getElapsedTimeFromTimer(Object o)
public long resetTimer(Object o)
public boolean deleteTimer(Object o)

与以前一样,计时器由任意对象标识。所有方法都旨在在规则条件或动作中使用。

  • createTimer:可以被调用以创建一个新的与 o 相关联的计时器。createTimer 返回 true 如果创建了一个新的计时器,如果已经与 o 关联了计时器,则返回 false
  • getElapsedTimeFromTimer:可以被调用以获得自与 o 相关联的计时器创建或上次调用 resetTimer 以来的经过毫秒数。如果没有与 o 相关联的计时器存在,将创建一个新的计时器,然后返回经过时间。
  • resetTimer:可以被调用以将与 o 相关联的计时器归零。它返回自计时器创建或上次调用 resetTimer 以来的秒数。如果没有与 o 相关联的计时器存在,将创建一个新的计时器,然后返回经过时间。
  • deleteTimer:可以被调用以删除与 o 相关联的计时器。deleteTimer 返回 true 如果已删除计时器,如果没有与 o 相关联的计时器存在,则返回 false

6. 递归触发

当规则被触发时,它执行事件、条件和动作中的 Java 代码,这可能包括对帮助器方法或应用程序在测试或 JVM 运行时定义的方法的调用。如果这些方法中的任何一个匹配 Byteman 规则,这可能导致规则执行引擎的递归条目。在某些情况下,这可能是可取的。然而,在其他情况下,这种递归条目可能导致无限触发链,并且有必要在规则执行时禁用触发。例如,以下规则将因这个问题而失败:

代码语言:javascript
代码运行次数:0
复制
RULE infinite triggering chain
CLASS java.io.FileOutputStream
METHOD open(String, int)
AT EXIT
BIND filename = $1
IF TRUE
DO traceln("openlog", "Opened " + $1 + " for write")
ENDRULE

问题是,在对内置方法 traceln(Object, String) 的第一次调用中,默认帮助器类尝试打开一个跟踪文件,并将 "openlog" 与其关联。这样做时,它调用了 FileOutputStream.open 并重新触发了规则。

解决这个问题的一个方法是指定一个条件,该条件将打破链。跟踪文件将有一个名为 "trace_NNN_.txt" 的名称,因此以下版本的规则按预期工作:

代码语言:javascript
代码运行次数:0
复制
RULE infinite triggering chain broken using IF test
CLASS java.io.FileOutputStream
METHOD open(String, int)
AT EXIT
BIND filename = $1
IF !filename.matches("trace.*")
DO traceln("openlog", "Opened " + $1 + " for write")
ENDRULE

这个版本在 traceln 调用下递归触发规则,但条件阻止了它被触发,打破了递归。

当然,在其他情况下,可能没有那么简单就能想出一个避免递归触发的条件。所以,默认帮助器提供了以下方法,允许在规则执行时禁用或重新启用触发。

代码语言:javascript
代码运行次数:0
复制
public boolean setTriggering(boolean enabled)

如果 enabledfalse,那么在规则正文中随后表达式的执行期间禁用触发。如果它是 true,则重新启用触发。

这可以用来实现上面示例中显示的行为,而无需识别合适的条件。

setTriggering 总是返回布尔值 true,允许将其 AND 到 IF 子句的条件中,或用于初始化在 BIND 子句中声明的规则变量。有时这是必要的,以确保触发在 IFBIND 子句中评估其他表达式之前尽早被禁用。

FunTesterFunTester 原创精华 【连载】从 Java 开始性能测试

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-02-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 链接映射(LinkMaps)
  • 2. 倒计时(CountDowns)
  • 3. 标志(Flags)
  • 4. 计数器(Counters)
  • 5. 计时器(Timers)
  • 6. 递归触发
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档