使用Byteman COMPILE选项触发VerifyError,求排查规则问题
问题描述
在Felix Server中运行应用时,需要监控RestMonitoringJobRegistry.updateMonitoredState方法的执行时间,编写了两个带COMPILE选项的Byteman规则:入口记录开始时间,出口计算耗时。规则提交无报错,但运行时触发VerifyError,移除COMPILE选项后规则运行正常。
应用代码
package com.mypackage.monitoring.client; public class RestMonitoringJobRegistry extends BaseMonitoringJobRegistry { public synchronized <T> void updateMonitoredState( MonitoringJob<T> aInMonitoringJob, T aInState) { if (aInMonitoringJob == null || aInState == null) { TPAResources.debug(logger, "Cannot send: missing required information."); return; } // 省略其他逻辑 } }
入口规则
RULE record_startTime_RestMonitoringJobRegistry CLASS com.mypackage.monitoring.client.RestMonitoringJobRegistry METHOD updateMonitoredState COMPILE AT ENTRY BIND LOGGER:Logger = com.mypackage.monitoring.client.RestMonitoringJobRegistry.logger; startTime = System.currentTimeMillis() IF true DO LOGGER.info("[JA] AT ENTRY"); link(Thread.currentThread(), "startTime", startTime); ENDRULE
出口规则
RULE check_duration_RestMonitoringJobRegistry CLASS com.mypackage.monitoring.client.RestMonitoringJobRegistry METHOD updateMonitoredState COMPILE AT EXIT BIND LOGGER:Logger = com.mypackage.monitoring.client.RestMonitoringJobRegistry.logger; startTime:int = unlink(Thread.currentThread(), "startTime"); endTime = System.currentTimeMillis() IF true DO LOGGER.info("[JA] AT EXIT: updateMonitoredState took elapsedTime [" + (endTime - startTime) + "] $aInMonitoringJob.getKey() [" + $aInMonitoringJob.getKey() +"]"); ENDRULE
错误日志
{"host":"host","level":"ERROR","log":{"classname":"com.pkg.as.service.log.impl.asrlog.Stdout2Log4jOutputStream$StdoutRedirectorThread:104","message":"check_duration_RestMonitoringJobRegistry : java.lang.VerifyError: (class: org/jboss/byteman/rule/helper/Helper_HelperAdapter_Compiled_53, method: execute0 signature: ()V) Expecting to find integer on stack","stacktrace":"","threadname":"StdoutThread"},"process":"run","service":"","time":"2024-03-22T00:20:50.725Z","timezone":"Asia/Kolkata","type":"log"}
移除COMPILE后的正常日志
{"host":"host","level":"INFO","log":{"classname":"org.jboss.byteman.rule.helper.Helper_HelperAdapter_Compiled_51:11","message":"[JA] AT ENTRY","stacktrace":"","threadname":"MonitoringJobRegistry-4"},"process":"execute0","service":"","time":"2024-03-22T00:20:30.714Z","timezone":"Asia/Kolkata","type":"log"}
问题分析与解决
问题出在类型不匹配:
- 入口规则中
System.currentTimeMillis()返回long类型,通过link绑定到线程的startTime是long值。 - 出口规则错误地将
startTime声明为int类型,unlink取出的long值被强制转换为int,在COMPILE模式下,Byteman生成的字节码严格遵循类型校验,栈上实际是long类型却被当作int处理,触发VerifyError。 - 移除
COMPILE选项后,Byteman使用解释模式执行规则,对类型校验相对宽松,隐式处理了类型转换,因此无报错。
修正后的规则
将startTime的类型统一为long,同时建议入口规则也显式声明类型保持一致:
修正后的入口规则
RULE record_startTime_RestMonitoringJobRegistry CLASS com.mypackage.monitoring.client.RestMonitoringJobRegistry METHOD updateMonitoredState COMPILE AT ENTRY BIND LOGGER:Logger = com.mypackage.monitoring.client.RestMonitoringJobRegistry.logger; startTime:long = System.currentTimeMillis() IF true DO LOGGER.info("[JA] AT ENTRY"); link(Thread.currentThread(), "startTime", startTime); ENDRULE
修正后的出口规则
RULE check_duration_RestMonitoringJobRegistry CLASS com.mypackage.monitoring.client.RestMonitoringJobRegistry METHOD updateMonitoredState COMPILE AT EXIT BIND LOGGER:Logger = com.mypackage.monitoring.client.RestMonitoringJobRegistry.logger; startTime:long = unlink(Thread.currentThread(), "startTime"); endTime:long = System.currentTimeMillis() IF true DO LOGGER.info("[JA] AT EXIT: updateMonitoredState took elapsedTime [" + (endTime - startTime) + "] $aInMonitoringJob.getKey() [" + $aInMonitoringJob.getKey() +"]"); ENDRULE
内容的提问来源于stack exchange,提问作者Asif Ansari
相关产品推荐
相关产品推荐

