文章目录
InjectFix介绍
插件地址:https://github.com/Tencent/InjectFix
作用: 用于修复线上C# bug
流程: InjectFix包括Inject和Fix两个部分,发包的时候对需要修复的类进行插桩(Inject)处理,线上版本要写好加载热更补丁包的逻辑,通过生成Patch补丁进行资源热更的形式进行修复线上bug。
原理: InjectFix实现bug修复主要靠两部分:虚拟机负责新逻辑的解析执行;注入代码负责把调用重定向到虚拟机。
作者对Inject的介绍:https://segmentfault.com/a/1190000020375313
优势: 代码都以原生的方式执行,只有需要修复的代码会重定向到虚拟机执行,内部使用反射,不如xlua的静态warp,但考虑到只有少量函数需要修复,不会有大量性能损耗。
InjectFix使用说明
- InjectFix通过标签标注的形式,进行对代码插桩和生成补丁包生成处理。
- 标签分为注入和补丁。注入标签在发包前做。补丁标签在发包后修复阶段使用。
标签 | 使用阶段 | 用途 | 用法 |
---|---|---|---|
[Patch] | 补丁 | 修复函数 | 只能放在函数上 |
[Interpret] | 补丁 | 新增属性,函数,类型 | 放在属性,函数,类型上 |
[CustomBridge] | 注入 | interface和delegate桥接 | 只能放在单独写一个静态类上,存储虚拟机的类适配到原生interface或者虚拟机的函数适配到原生delegate,该类不能放Editor目录 |
[Configure] | 注入 | 配置类 | 只能放在单独写一个存放在Editor目录下的类上 |
[IFix] | 注入 | 可能需要修复函数的类的集合 | 只能放在[Configure]类的一个静态属性上 |
[Filter] | 注入 | 不想发生注入的函数 | 只能放在[Configure]类的一个静态函数上 |
[IFix.Patch]
修复某个函数,该标签只能用在方法上,直接在方法上面标注一下[IFix.Patch]即可
[IFix.Patch]
private int Add(int a,int b)
{
return a * b;
}
[IFix.Interpret]
新增个函数或者类,在属性,方法,类型上,直接在要新增的代码上面标注一下这个标签即可。
[IFix.Patch]和[IFix.Interpret]属于比较常用的修复Bug
[IFix.CustomBridge]
在注入阶段使用; 把一个虚拟机的类适配到原生interface或者把一个虚拟机的函数适配到原生delegate。
- 修复代码赋值一个闭包到一个delegate变量;
- 修复代码的Unity协程用了yield return;
- 新增一个函数,赋值到一个delegate变量;
- 新增一个类,赋值到一个原生interface变量;
- 新增函数,用了yield return;
该标签只能用在类上,写上一个静态类,里面有一个静态字段,值就是interface和delegate的类型集合,该配置类不能放到Editor目录,且不能内嵌到另外一个类里头。
[IFix.CustomBridge]
public static class AdditionalBridge
{
static List<Type> bridge = new List<Type>()
{
typeof(ISubSystem),
typeof(IEnumerator), //如果之前使用过协程,可不做处理
typeof(Test.MyDelegate)
};
}
[IFix.Filter]
在注入阶段使用,过滤某些方法。在注入阶段,凡是在[IFix]标签下的属性里面的值,都会被注入适配代码,但如果不想对某个函数进行注入,可以用该标签进行过滤。该标签只能用在方法上,Configure类中的一个静态方法。
[Filter]
static bool Filter(System.Reflection.MethodInfo methodInfo)
{
return methodInfo.DeclaringType.FullName == "Test"
&& (methodInfo.Name == "Test2" || methodInfo.Name == "Test1");
}
项目中使用
【注入(发包前)】
- 可以单独对一个类型或者一个dll进行插桩配置,打包的时候会自动注入到程序集中去。
- 要修复Assets下dll的类,需要在InjectFixAssemblys中配置对应dll的路径信息
使用PatchManager.Load(stream)加载补丁:
string[] fixFileNames = Directory.GetFiles(fullFixFilePath, "*.bytes");
foreach (var fileName in fixFileNames)
{
FileStream stream = new FileStream(fileName, FileMode.Open);
if (stream != null)
{
try
{
PatchManager.Load(stream);
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
Debug.LogFormat("InjectFix load fix file {0} success and start pathch", fileName);
stream.Dispose();
}
}
【Fix补丁包(发包后需要修复)】
在需要修复的代码上面加上Patch标签,生成对应平台补丁包,走热更流程,完成修复。
PS:一个程序集生成一个补丁包,多个补丁包会自动卸载上一个,即时多次Load,只有最后一个会生效。
【接入】
具体接入参考官方文档
接入遇到的问题记录
1. 注入失败,提示下面报错
System.Exception: assembly may be not injected yet, cat find IFix.ILFixInterfaceBridge, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
解决:
- 确保出包之前注入成功,官方监听场景打包之后进行自动注入 ([UnityEditor.Callbacks.PostProcessScene]),确保注入注入之后不会触发编译,若触发编译(比如改变dll或者路径),则注入失效,需要手动调用IFixEditor.InjectAllAssemblys重新注入
- 如果需要注入自定义程序集,需要改官方源码,官方会限定注入project\Library\ScriptAssemblies 路径下的程序集,若要注入Assets/Plugins下的程序集,需要去改对应路径和ScriptAssemblies限制,另外注入第三方dll会触发编译,需要重新注入ScriptAssemblies下的Assembly-CSharp.dll
2. 泛型Patch报错
Unhandled Exception:System.Exception: Utils/d__0 is CompilerGenerated
Unhandled Exception:System.InvalidProgramException: try to use a generic type definition: !0
InjectFix对于泛型支持有限制,不支持一个不确定的泛型方法,支持确定的泛型方法
后期有坑持续记录
赞赏与支持
【收藏与点赞】
觉得写的不错的可以收藏点赞转发噢~
【请作者喝杯咖啡】
觉得写的不错的可以请作者喝杯咖啡噢~
您的支持是我创作和分享的动力!
(完)