Skywalking流程分析_5(字节码增强)
SkyWalkingAgent.Transformer#transform
此方法就是进行字节码增强的过程
private static class Transformer implements AgentBuilder.Transformer {private PluginFinder pluginFinder;Transformer(PluginFinder pluginFinder) {this.pluginFinder = pluginFinder;}@Overridepublic DynamicType.Builder<?> transform(//当前拦截到的类的字节码final DynamicType.Builder<?> builder,//可以理解成class 包括类的描述信息 final TypeDescription typeDescription,//当前拦截到的类的类加载器final ClassLoader classLoader,final JavaModule javaModule,final ProtectionDomain protectionDomain) {//如果当前拦截的类的类加载器是URLClassLoader类型,则进行收集LoadedLibraryCollector.registerURLClassLoader(classLoader);//找到对此类匹配的所有插件List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);if (pluginDefines.size() > 0) {DynamicType.Builder<?> newBuilder = builder;//增强上下文EnhanceContext context = new EnhanceContext();for (AbstractClassEnhancePluginDefine define : pluginDefines) {//调用每个插件的define()方法去做字节码增强DynamicType.Builder<?> possibleNewBuilder = define.define(typeDescription, newBuilder, classLoader, context);if (possibleNewBuilder != null) {newBuilder = possibleNewBuilder;}}if (context.isEnhanced()) {LOGGER.debug("Finish the prepare stage for {}.", typeDescription.getName());}//被所有匹配的插件修改完后最终字节码return newBuilder;}LOGGER.debug("Matched class {}, but ignore by finding mechanism.", typeDescription.getTypeName());//原始字节码return builder;}
}
总结:
- 找到对此类匹配的所有插件
- 如果找到有匹配上的插件,调用每个插件的define()方法去做字节码增强,然后增强后的字节码
- 如果没有找到匹配的插件,则返回原始字节码
找到对此类匹配的所有插件
pluginFinder.find(typeDescription)
public class PluginFinder {/*** Map的泛型为<String,List>的原因是对于同一个类,可能会有多个插件都要对这个类进行字节码增加* * key -> 目标类* value -> 所有可以对这个目标类生效的插件* */private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhancePluginDefine>>();private final List<AbstractClassEnhancePluginDefine> signatureMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();private final List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();private static boolean IS_PLUGIN_INIT_COMPLETED = false;/**** 得到对指定类型生效的所有匹配插件,包括命名查找、间接匹配查找*/public List<AbstractClassEnhancePluginDefine> find(TypeDescription typeDescription) {List<AbstractClassEnhancePluginDefine> matchedPlugins = new LinkedList<AbstractClassEnhancePluginDefine>();String typeName = typeDescription.getTypeName();if (nameMatchDefine.containsKey(typeName)) {matchedPlugins.addAll(nameMatchDefine.get(typeName));}for (AbstractClassEnhancePluginDefine pluginDefine : signatureMatchDefine) {IndirectMatch match = (IndirectMatch) pluginDefine.enhanceClass();if (match.isMatch(typeDescription)) {matchedPlugins.add(pluginDefine);}}return matchedPlugins;}}
EnhanceContext context = new EnhanceContext()
此类的作用是用作是否增强的标识
/*** The <code>EnhanceContext</code> represents the context or status for processing a class.* <p>* Based on this context, the plugin core {@link ClassEnhancePluginDefine} knows how to process the specific steps for* every particular plugin.* * 此类的作用是记录当前被拦截的类是否被修改了字节码和是否新增了新的字段或接口*/
public class EnhanceContext {private boolean isEnhanced = false;/*** The object has already been enhanced or extended. e.g. added the new field, or implemented the new interface*/private boolean objectExtended = false;public boolean isEnhanced() {return isEnhanced;}public void initializationStageCompleted() {isEnhanced = true;}public boolean isObjectExtended() {return objectExtended;}public void extendObjectCompleted() {objectExtended = true;}
}
字节码增强
define.define(typeDescription, newBuilder, classLoader, context)
这里调用的其实就调用了插件的顶级接口AbstractClassEnhancePluginDefine的方法
public DynamicType.Builder<?> define(TypeDescription typeDescription, DynamicType.Builder<?> builder,ClassLoader classLoader, EnhanceContext context) throws PluginException {// 当前的插件的全类名String interceptorDefineClassName = this.getClass().getName();// 当前要被增加的类全类名String transformClassName = typeDescription.getTypeName();if (StringUtil.isEmpty(transformClassName)) {LOGGER.warn("classname of being intercepted is not defined by {}.", interceptorDefineClassName);return null;}LOGGER.debug("prepare to enhance class {} by {}.", transformClassName, interceptorDefineClassName);WitnessFinder finder = WitnessFinder.INSTANCE;/*** find witness classes for enhance class*///版本查找//通过类识别String[] witnessClasses = witnessClasses();if (witnessClasses != null) {for (String witnessClass : witnessClasses) {//判断类加载器中存不存在这个标识类if (!finder.exist(witnessClass, classLoader)) {LOGGER.warn("enhance class {} by plugin {} is not activated. Witness class {} does not exist.", transformClassName, interceptorDefineClassName, witnessClass);return null;}}}//通过方法识别(和通过类识别的逻辑相同)List<WitnessMethod> witnessMethods = witnessMethods();if (!CollectionUtil.isEmpty(witnessMethods)) {for (WitnessMethod witnessMethod : witnessMethods) {if (!finder.exist(witnessMethod, classLoader)) {LOGGER.warn("enhance class {} by plugin {} is not activated. Witness method {} does not exist.", transformClassName, interceptorDefineClassName, witnessMethod);return null;}}}/*** find origin class source code for interceptor* 进行真正的字节码增强修改*/DynamicType.Builder<?> newClassBuilder = this.enhance(typeDescription, builder, classLoader, context);//将isEnhanced的标识位设置为truecontext.initializationStageCompleted();LOGGER.debug("enhance class {} by {} completely.", transformClassName, interceptorDefineClassName);return newClassBuilder;
}
总结
- witnessClasses()和witnessMethods()检查插件是否生效,在之前的文章详细分析过
- enhance方法进行字节码增强
- 将EnhanceContext标识设置为已增强
版本识别的具体判断插件是否生效
witnessClasses()和witnessMethods()返回的类和方法都是要调用finder.exist来判断当前应用是否存在
public enum WitnessFinder {INSTANCE;/*** TypePool为类加载器的所有能加载的类型* */private final Map<ClassLoader, TypePool> poolMap = new HashMap<ClassLoader, TypePool>();/*** @param classLoader for finding the witnessClass* @return true, if the given witnessClass exists, through the given classLoader.*/public boolean exist(String witnessClass, ClassLoader classLoader) {return getResolution(witnessClass, classLoader).isResolved();}private TypePool.Resolution getResolution(String witnessClass, ClassLoader classLoader) {ClassLoader mappingKey = classLoader == null ? NullClassLoader.INSTANCE : classLoader;if (!poolMap.containsKey(mappingKey)) {synchronized (poolMap) {if (!poolMap.containsKey(mappingKey)) {TypePool classTypePool = classLoader == null ? TypePool.Default.ofBootLoader() : TypePool.Default.of(classLoader);poolMap.put(mappingKey, classTypePool);}}}TypePool typePool = poolMap.get(mappingKey);//从这个类型池中查找这个类存不存在return typePool.describe(witnessClass);}public boolean exist(WitnessMethod witnessMethod, ClassLoader classLoader) {//方法所在的类是否在这个ClassLoader中TypePool.Resolution resolution = getResolution(witnessMethod.getDeclaringClassName(), classLoader);if (!resolution.isResolved()) {return false;}//判断该方法是否存在return !resolution.resolve().getDeclaredMethods().filter(witnessMethod.getElementMatcher()).isEmpty();}}final class NullClassLoader extends ClassLoader {static NullClassLoader INSTANCE = new NullClassLoader();
}
witnessClass(),会基于传入的classLoader构造TypePool来判断witnessClass是否存在,TypePool最终会存储到Map中witnessMethod(),先判断此方法所在的类是否在这个ClassLoader中,也就是先执行witnessClass(),再判断该方法是否存在
真正的字节码增强
this.enhance(typeDescription, builder, classLoader, context)
protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,ClassLoader classLoader, EnhanceContext context) throws PluginException {//静态方法的增强newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);//构造、实例方法的增强newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);return newClassBuilder;
}
下一篇会详细介绍真正字节码增强的流程
相关文章:
Skywalking流程分析_5(字节码增强)
SkyWalkingAgent.Transformer#transform 此方法就是进行字节码增强的过程 private static class Transformer implements AgentBuilder.Transformer {private PluginFinder pluginFinder;Transformer(PluginFinder pluginFinder) {this.pluginFinder pluginFinder;}Override…...
Windows conan环境搭建
Windows conan环境搭建 1 安装conan1.1 安装依赖软件1.1.1 python安装1.1.2 git bash安装1.1.3 安装Visual Studio Community 20191.1.3.1 选择安装的组件1.1.3.2 选择要支持的工具以及对应的SDK 1.1.4 vscode安装 1.3 验证conan功能1.4 查看conancenter是否包含poco包1.5 查看…...
如何使用Cpolar+Tipask,在ubuntu系统上搭建一个私人问答网站
文章目录 前言2.Tipask网站搭建2.1 Tipask网站下载和安装2.2 Tipask网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道(云端设置)3.3 Cpolar稳定隧道(本地设置) 4. 公网访问测试5. 结语 前…...
怎么在uni-app中使用Vuex(第一篇)
Vuex简介 vuex的官方网址如下 https://vuex.vuejs.org/zh/ 阅读官网请带着几个问题去阅读: vuex用于什么场景?vuex能给我们带来什么好处?我们为什么要用vuex?vuex如何实现状态集中管理? Vuex用于哪些场景? 组件之…...
【MySQL】库的相关操作 + 库的备份和还原
库的操作 前言正式开始创建数据库删除数据库编码集查看系统默认字符集以及校验规则字符集校验规则 所有支持的字符集和校验规则所有字符集所有校验规则 指明字符集和校验规则创建数据库相同的字符集用不同的校验规则读取会出现什么情况 alter修改数据库show create databasealt…...
网络安全基础之php开发文件上传的实现
前言 php是网络安全学习里必不可少的一环,简单理解php的开发环节能更好的帮助我们去学习php以及其他语言的web漏洞原理 正文 在正常的开发中,文件的功能是必不可少,比如我们在论坛的头像想更改时就涉及到文件的上传等等文件功能。但也会出…...
[文件读取]cuberite 文件读取 (CVE-2019-15516)
1.1漏洞描述 漏洞编号CVE-2019-15516漏洞类型文件上传漏洞等级⭐⭐⭐漏洞环境VULFOCUS攻击方式 描述: Cuberite是一款使用C语言编写的、轻量级、可扩展的多人游戏服务器。 Cuberite 2019-06-11之前版本中存在路径遍历漏洞。该漏洞源于网络系统或产品未能正确地过滤资源或文件路…...
SpringBoot 自定义参数校验(5)
文章目录 前言方式一 @Pattern方式二 自定义参数校验Controller层请求示例前言 本文基于SpringBoot 3.1.2,使用自定义参数规则来处理参数校验。 方式一 @Pattern 使用@Pattern,自定义正则表达式,以下是一个校验IP地址的示例: import jakarta.validation.constraints.Not…...
Win Docker Desktop + WSL2 部署PyTorch-CUDA服务至k8s算力集群
Win Docker Desktop WSL2 部署PyTorch-CUDA服务至k8s算力集群 Win Docker Desktop WSL2 安装安装WSL-Ubuntu拉取镜像并测试挂载数据并开放端口导出镜像或导入镜像在k8s集群部署 Win Docker Desktop WSL2 安装 首先根据你的操作系统版本 安装WSL ,记得切换WSL2&a…...
JLMR Micro Super Resolution Algorithm国产微超分算法DEMO
一、简介 目前,做超分算法基本还是以AI训练为主,但是AI基本上都是基于既定场景的训练。而传统的算法基本上都是利用上下文的纹理预测、插值等方案,在图像放大过程中会出现模糊,或马赛克等现象。 我们基于加权概率模型,…...
Docker的安装配置与使用
1、docker安装与启动 首先你要保证虚拟机所在的盘要有至少20G的空间,因为docker开容器很吃空间的,其次是已经安装了yum依赖 yum install -y epel-release yum install docker-io # 安装docker配置文件 /etc/sysconfig/docker chkconfig docker on # 加…...
macOS文本编辑器 BBEdit 最新 for mac
BBEdit是一款功能强大的文本编辑器,适用于Mac操作系统。它由Bare Bones Software开发,旨在为开发者和写作人员提供专业级的文本编辑工具。 以下是BBEdit的一些主要特点和功能: 多语言支持:BBEdit支持多种编程语言和标记语言&…...
Android Audio实战——音量设置Hal(二十)
本来上一篇分析音量设置中对于 setCurrentGainIndex 方法我们分析到了 native 层就没有往下分析,但这这里还有准备再看看下面的流程。 一、源码分析 1、android_media_AudioSystem.cpp 源码位置:frameworks/base/core/jni/android_media_AudioSystem.cpp static jint and…...
jetson配置笔记
typora-root-url: /home/msj/ubuntu笔记本台式机环境配置说明/images Ubuntu18.04 配置 说明:我们所有文档配置都是按照ubuntu18.04,保证x86架构(笔记本台式机)和 ARM架构(jetson Nano只能安装18.04)的一致性 1. 更换各类源 我们所有源都更换清华源&a…...
使用select实现定时任务
selectOutOfTime.c里边的代码如下: #include<stdio.h> #include<sys/time.h> #include<sys/types.h> #include<unistd.h> #include <string.h>#define BUF_SIZE 100int main(void){fd_set reads;struct timeval tv;int errorNum;cha…...
uniapp的实战总结大全
🙂博主:冰海恋雨 🙂文章核心:uniapp部分总结 目录 编辑 目录 前言: 解决方案 1. 跨平台开发 2. Vue.js生态 3. 组件库 4. 自定义组件 5. Native能力 6. 插件生态 7. 性能优化 写法 1. 模板…...
No205.精选前端面试题,享受每天的挑战和学习
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…...
保序回归:拯救你的校准曲线(APP)
保序回归:拯救你的校准曲线(APP) 校准曲线之所以是评价模型效能的重要指标是因为,校准曲线衡量模型预测概率与实际发生概率之间的一致性,它可以帮助我们了解模型的预测结果是否可信。一个理想的模型应该能够准确地预测…...
清华镜像源地址,适用于pip下载速度过慢从而导致下载失败的问题
清华地址 https://pypi.tuna.tsinghua.edu.cn/simple下载各种各样的包的指令模板 pip install XXX -i https://pypi.tuna.tsinghua.edu.cn/simple这样就行了,XXX代表的是你将要下载的包名称。 比如: pip install opencv-python -i https://pypi.tuna.…...
arcgis--NoData数据处理
方法一:利用【栅格计算器】可以对NoData的值进行修改。【Spatial Analyst工具】-【地图代数】-【栅格计算器】,将NoData修改为某一个值。 方法二:先对原始数据进行重分类,分成1类,将NoData赋值为2,。然后,将…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
