UH4L15——调试相关

本章代码关键字

1
2
appDomain.DebugService.StartDebugService()    //注册调试服务
appDomain.DebugService.IsDebuggerAttached //判断调试器链接

ILRuntime调试相关

注:笔者实际测试下来出现了无法命中断点的问题,原因不明且无法解决

  1. 进行ILRuntime调试需要安装插件
  2. 想要进行断点调试,我们需要注册调试服务,想要及时进断点,那么可以配合协同程序等待调试器链接
  3. 可以利用IP和端口调试特点来调试移动设备

准备ILRuntime调试相关插件

  1. ILRuntime 2.1.0以下的版本 需要前往ILRuntime的Github页面获取插件
    地址:Releases · Ourpalm/ILRuntime (github.com)
    打开页面后,点击右侧的Releases,在对应版本处下载调试插件

    image

  2. ILRuntime 2.1.0及其以上的版本
    在VS上方的:拓展-管理拓展-搜索ILRuntime后安装

    image

    当VS的菜单栏的调试处多了一个Attach to ILRuntime时,就说明安装成功

    image

断点调试的必备工作

  1. 在初始化处 注册调试服务 appDomain.DebugService.StartDebugService(端口号)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //初始化ILRuntime相关的方法
    private unsafe void InitILRuntime()
    {
    //注册委托和委托转换器
    //注册跨域继承类
    appDomain.RegisterCrossBindingAdaptor(new ILRuntimeAdapter.Lesson11_TestAdapter());
    appDomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
    appDomain.RegisterCrossBindingAdaptor(new IAsyncStateMachineClassInheritanceAdaptor());
    //初始化LitJson相关内容
    LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appDomain);
    //注册值类型
    appDomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
    appDomain.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
    appDomain.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());


    //CLR重定向内容,必须要写到CLR绑定之前!!!
    System.Type debugType = typeof(Debug);
    MethodInfo methodInfo = debugType.GetMethod("Log", new System.Type[] { typeof(object) });
    appDomain.RegisterCLRMethodRedirection(methodInfo, MyLog);

    //注册 CLR绑定相关信息
    ILRuntime.Runtime.Generated.CLRBindings.Initialize(appDomain);
    //初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
    appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;

    //启动调试服务
    appDomain.DebugService.StartDebugService(56000);
    }
  2. 通过协同程序,等待调试器链接(否则我们无法对一开始的逻辑 进行断点)
    判断调试器链接的API为 appDomain.DebugService.IsDebuggerAttached

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public class ILRuntimeMgr : MonoBehaviour
    {
    private IEnumerator LoadHotUpdateInfo(UnityAction callBack)
    {
    //异步加载DLL文件
    #if UNITY_ANDROID
    UnityWebRequest reqDll = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
    #else
    UnityWebRequest reqDll = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
    #endif
    yield return reqDll.SendWebRequest();
    if (reqDll.result != UnityWebRequest.Result.Success)
    Debug.Log("加载DLL文件失败" + reqDll.responseCode + reqDll.result);
    //读取加载的DLL数据
    byte[] dll = reqDll.downloadHandler.data;
    reqDll.Dispose();
    //编辑器环境下,我们可以加载调试用的pdb文件
    UnityWebRequest reqpdb = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
    yield return reqpdb.SendWebRequest();
    //如果加载失败
    if (reqpdb.result != UnityWebRequest.Result.Success)
    {
    Debug.LogError("加载DLL文件失败" + reqpdb.responseCode + reqpdb.result);
    StopILRuntime();
    yield break;
    }
    //读取加载的DLL数据
    byte[] pdb = reqpdb.downloadHandler.data;
    reqpdb.Dispose();
    pdbStream = new MemoryStream(pdb);
    dllStream = new MemoryStream(dll);
    //将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
    appDomain.LoadAssembly(dllStream, pdbStream, new PdbReaderProvider());
    InitILRuntime();

    //如果想要调试我们的项目,那么就加一个协同程序去等待,等待调试器链接进入过后,再去这些我们后续的逻辑
    StartCoroutine(WaitDebugger(callBack));
    }

    IEnumerator WaitDebugger(UnityAction callBack)
    {
    while (!appDomain.DebugService.IsDebuggerAttached)
    {
    yield return null;
    }
    ILRuntimeLoadOverDo();
    callBack?.Invoke();
    }
    }

注意:一般只有在开发周期 才会这样去处理,如果最终发布了 就不用启动我们的调试服务 也不用去通过协程延迟执行后面的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
private bool isDebug = true;

//初始化ILRuntime相关的方法
private unsafe void InitILRuntime()
{
//注册委托和委托转换器
appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel1>((action) =>
{
return new MyUnityDel1(() =>
{
((System.Action)action)();
});
});
appDomain.DelegateManager.RegisterMethodDelegate<int>();
appDomain.DelegateManager.RegisterFunctionDelegate<int, int, int>();
appDomain.DelegateManager.RegisterDelegateConvertor<MyUnityDel2>((func) =>
{
return new MyUnityDel2((i, j) =>
{
return ((System.Func<int, int, int>)func)(i, j);
});
});
//注册跨域继承类
appDomain.RegisterCrossBindingAdaptor(new ILRuntimeAdapter.Lesson11_TestAdapter());
appDomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
appDomain.RegisterCrossBindingAdaptor(new IAsyncStateMachineClassInheritanceAdaptor());
//初始化LitJson相关内容
LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appDomain);
//注册值类型
appDomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
appDomain.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
appDomain.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());


//CLR重定向内容,必须要写到CLR绑定之前!!!
System.Type debugType = typeof(Debug);
MethodInfo methodInfo = debugType.GetMethod("Log", new System.Type[] { typeof(object) });
appDomain.RegisterCLRMethodRedirection(methodInfo, MyLog);

//注册 CLR绑定相关信息
ILRuntime.Runtime.Generated.CLRBindings.Initialize(appDomain);
//初始化ILRuntime相关信息(目前只需要告诉ILRuntime主线程的线程ID,主要目的是能够在Unity的Profiler剖析器窗口中分析问题)
appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;

if (isDebug)
{
//启动调试服务
appDomain.DebugService.StartDebugService(56000);
}
}

private IEnumerator LoadHotUpdateInfo(UnityAction callBack)
{
//异步加载DLL文件
#if UNITY_ANDROID
UnityWebRequest reqDll = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.dll");
#else
UnityWebRequest reqDll = UnityWebRequest.Get("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
#endif
yield return reqDll.SendWebRequest();
if (reqDll.result != UnityWebRequest.Result.Success)
Debug.Log("加载DLL文件失败" + reqDll.responseCode + reqDll.result);
//读取加载的DLL数据
byte[] dll = reqDll.downloadHandler.data;
reqDll.Dispose();
//编辑器环境下,我们可以加载调试用的pdb文件
UnityWebRequest reqpdb = UnityWebRequest.Get(Application.streamingAssetsPath + "/HotFix_Project.pdb");
yield return reqpdb.SendWebRequest();
//如果加载失败
if (reqpdb.result != UnityWebRequest.Result.Success)
{
Debug.LogError("加载DLL文件失败" + reqpdb.responseCode + reqpdb.result);
StopILRuntime();
yield break;
}
//读取加载的DLL数据
byte[] pdb = reqpdb.downloadHandler.data;
reqpdb.Dispose();
pdbStream = new MemoryStream(pdb);
dllStream = new MemoryStream(dll);
//将我们两个文件的内存流用于初始化 appDomain 我们之后就可以通过该对象来执行我们对应的热更代码了
appDomain.LoadAssembly(dllStream, pdbStream, new PdbReaderProvider());
InitILRuntime();
if (isDebug)
{
//如果想要调试我们的项目,那么就加一个协同程序去等待,等待调试器链接进入过后,再去这些我们后续的逻辑
StartCoroutine(WaitDebugger(callBack));
}
else
{
ILRuntimeLoadOverDo();
callBack?.Invoke();
}
}

IEnumerator WaitDebugger(UnityAction callBack)
{
print("等待调试器接入");
while (!appDomain.DebugService.IsDebuggerAttached)
{
yield return null;
}
print("调试器接入成功");
ILRuntimeLoadOverDo();
callBack?.Invoke();
}

进行断点调试

在热更工程的 调试页签中 选择 Attach to ILRuntime,即可开始断点调试

先运行Unity程序,让协同程序开始等待调试器的接入

输出:image

然后 Attach to ILRuntime 输入调试设备IP地址(本机使用127.0.0.1​即可),端口号填入在主工程内启动调试服务时传入的端口号

image

image

注意:​

  1. 附加到ILRuntime后,弹出窗口中填写的内容为IP地址和端口号,意味着我们可以调试各种设备,只要保证IP地址和端口号正确即可
  2. 如果ILRuntime相关的dll和pdb文件没有加载成功,调试会失败