RileyHardm 发表于 2023-4-20 09:37

Unity真机调试

近期项目出现了不少性能问题,真机环境下与Editor差异很大,需要对真机环境的程序状态做监控。这些内容不复杂但比较零碎,因此做一些记录。(此章节会持续更新)
一、环境设置

1.1 Building Setting


http://pic1.zhimg.com/v2-94db52388e0aafdcafd02cc9fdfe2fd4_r.jpg

[*]Development Build - 启用此设置后,Unity 会设置宏 DEVELOPMENT_BUILD ,在构建版本中包含脚本调试符号和性能分析器
[*]Autoconnect Profiler - 启动程序时自动连接Unity Profiler
[*]Deep Profiling Support - 详细的Profiler数据
[*]Script Debugging - 真机代码调试



渲染相关设置

需要统一Color Space与图形API,以免出现Unity Editor与真机渲染效果不一致。


推荐使用IL2CPP下的ARM64架构,ARMv7对于内存以及兼容性会有一些问题,容易出现崩溃。当然如果项目需求支持ARMv7就是另外一件事了。
1.2 Andorid Tools



需要下载好JDK、SDK以及NDK,Unity有给到默认的版本,也可以使用自己本地版本。
ADB的全称为Android Debug Bridge,是用来调试Android程序的工具。可以使用SDK Platform Tools安装,或者下载Android Studio来安装。使用adb help命令查看是否安装成功。

二、获取日志

2.1 常用adb接口


[*]卸载|覆盖安装apk:adb uninstall com.xxx.yyy.zzz | adb install -r xx.apk
[*]断开连接 | 启用连接:kill-serve | adb start-server
[*]查看设备分辨率:adb shell - wm size
[*]清理缓存日志: adb logcat -c
[*]保存日志: adb logcat -v *:E time > D:\Logcat\logcat.log
[*]输出崩溃日志: adb shell dumpsys dropbox --print > log-crash.txt

http://pic4.zhimg.com/v2-47d95ca5ddf3100cbe7bd14de7976a43_r.jpg
若出现adb连接失败需要检查:(1)手机是否开启USB调试;(2)USB连接是否稳定;(3)是否存在多个设备连接,若存在可以指定安装设备(adb devices查看设备id,adb -s xxx installl xxx.apk)
2.2 模拟器

在模拟器中即使开启了Profiler的相关设置,可能仍然无法与Unity连接,就需要手动将日志保存下来。不同模拟器处理可能略有不同,一般都会给到处理方式。此处以Mumu模拟器为例



Mumu模拟器教程入口



模拟器adb服务路径:~\emulator\nemu\vmonitor\bin

可以使用bat文件快捷处理,只需替换相关文件路径:
@Echo off
chcp 65001

start "C:\windows\explorer.exe" "D:\快捷工具"
cd /d "D:\Program Files\MuMu\emulator\nemu\vmonitor\bin"
adb_server.exe connect localhost:7555
adb_server.exe logcat -c
adb_server.exe logcat>D:\快捷工具\log.txt

pause
2.3 Android真机

开启Profiler相关选项,可以直接在Unity Console中获取Android日志。但若没有开启相关选项,可以通过上面提到的Adb接口来保存日志。可以配合Unity Profiler、Memory Profiler、Lua Profiler使用。这里给到Bat脚本,清理并保存Android日志。
@Echo off
chcp 65001
start "C:\windows\explorer.exe" "D:\快捷工具\"
adb kill-server
adb start-server
adb logcat -c
adb logcat *:E > log.txt
pause
三、Android堆栈还原

3.1 手动还原

Android的崩溃问题往往只有Native日志,无法直接定位Script。这样就需要将Native日志还原为Script堆栈信息,便于追查问题根源。这里给到某次真机Crash日志,但无法直接根据日志定位问题
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
    x000000071f003b5d0x100000071f003f830x200000071f003b938x300000071f003f830
    x4000000000000000ax500000072776f5718x60000000000000000x70000000080808080
    x800000071f00400d0x900000071f003b930x10 0000000000000000x11 00000071f003b630
    x12 00000071f003b588x13 0000000000000001x14 0000000000000001x15 0000000000000000
    x16 0000007277a2c1d8x17 0000007395125540x18 0000007276828000x19 0000007210005840
    x20 00000071f003f830x21 0000007fec818280x22 00000071a06686f0x23 0000007276d4b1e4
    x24 0000007272a71028x25 00000071a06686f0x26 0000007399069020x27 0000000000000000
    x28 0000007fec818870x29 0000007fec8186b0
    sp0000007fec818250lr0000007276c31fc8pc0000007276b33904


#00 pc 0xde904 libunity.so
#01 pc 0x1dcfc4 libunity.so
#02 pc 0x1dcf80 libunity.so
#03 pc 0x1da29c libunity.so
#04 pc 0x2f6828 libunity.so
#05 pc 0x2f6300 libunity.so
#06 pc 0x2f8224 libunity.so
#07 pc 0x2f85e8 libunity.so
#08 pc 0x3893f0 libunity.so
#09 pc 0x138702c libil2cpp.so
#10 pc 0x138473c libil2cpp.so
#11 pc 0x138529c libil2cpp.so
#12 pc 0x138d704 libil2cpp.so
#13 pc 0xe43054 libil2cpp.so
#14 pc 0xe43054 libil2cpp.so
#15 pc 0x57c738 libil2cpp.so
#16 pc 0x4e4044 libil2cpp.so
#17 pc 0x4e8edc libil2cpp.so
#18 pc 0x4be484 libil2cpp.so
#19 pc 0xb64928 libil2cpp.so
#20 pc 0x129fb40 libil2cpp.so
#21 pc 0x12a2d88 libil2cpp.so
#22 pc 0x58fac4 libil2cpp.so
#23 pc 0x4e4044 libil2cpp.so
#24 pc 0x2df794 libunity.so
#25 pc 0x2e2fc4 libunity.so
#26 pc 0x18a45c libunity.so
#27 pc 0x1bf390 base.odex想要还原日志需要用到:addr2line.exe和相关的so文件
libunity.sym.so是Unity提供的制表符文件,其中包含本地 Unity 库的符号文件。符号文件包含一个表,该表将活动内存地址转换为您可以使用的信息,例如方法名称。其相对安装路径为Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Symbols,根据之前提到的Target Architectures选中需要的版本。
如果使用了IL2CPP,Unity在构建时会生成libil2cpp.sym.so。Unity 2021.3打包时提供了生成symbols.zip的功能,打包时可调用EditorUserBuildSettings.androidCreateSymbols进行设置。





Unity安装目录下的libunity.sym.so



项目生成的libil2cpp.sym.so



打包生成的libil2cpp符号表文件

addr2line.exe是打包apk时,NDK提供的工具,可以通过读取符号表来解析崩溃堆栈。通常使用64位工具处理,相对路径为toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin


准备好之后就可以调用addr2line.exe,根据内存地址还原堆栈,具体格式为:addr2line.exe libunity.sym.so 内存地址1 内存地址2 ...,那么还原上面的Crash日志,调用的Bat代码如下
@Echo off

"aarch64-linux-android-addr2line.exe" -f -C -e "libunity.sym.so" 0xde904 0x1dcfc4 0x1dcf80 0x1da29c 0x2f6828 0x2f6300 0x2f8224 0x2f85e8 0x3893f0
"aarch64-linux-android-addr2line.exe" -f -C -e "libil2cpp.sym.so" 0x138702c 0x138473c 0x138529c 0x138d704 0xe43054 0xe43054 0x57c738 0x4e4044 0x4e8edc 0x4be484 0xb64928 0x129fb40 0x12a2d88 0x58fac4 0x4e4044
"aarch64-linux-android-addr2line.exe" -f -C -e "libunity.sym.so" 0x2df794 0x2e2fc4 0x18a45c

pause
3.2 脚本还原

手动还原日志需要将Crash的内存地址一个一个复制到调用参数中,费时费力。因此编写一个自动解析工具就有些必要了,核心就是提取内存地址。下面是解析日志的关键代码
public string ParseFile(string logPath)
{
    var lines = File.ReadAllLines(logPath);
    StringBuilder sb = new StringBuilder();
    foreach (var line in lines)
    {
      var nline = line.Trim();
      var items = nline.Split(' ').ToList();
      items.RemoveAll(s => string.IsNullOrWhiteSpace(s));

      var t1 = items.ToArray();
      int index1 = Array.FindIndex(t1, 0, t1.Length, x => x.Contains("libunity.so"));
      if (index1 > 0)
      {
            string stack = Exclute(textSOPath.Text, t1);
            sb.Append(stack);
            continue;
      }

      int index2 = Array.FindIndex(t1, 0, t1.Length, x => x.Contains("libil2cpp.so"));
      if (index2 > 0)
      {
            string stack = Exclute(textILPath.Text, t1);
            sb.Append(stack);
            continue;
      }
    }
    return sb.ToString();
}

public string Exclute(string soPath, string address)
{
    string args = $"-f -C -e \"{soPath}\" {address}";
    Console.WriteLine(args);

    ProcessStartInfo info = new ProcessStartInfo(textExePath.Text, args);
    info.CreateNoWindow = false;
    info.UseShellExecute = false;
    info.RedirectStandardError = true;
    info.RedirectStandardOutput = true;
    info.Verb = "runas";

    Process process = new Process
    {
      StartInfo = info
    };
    process.Start();

    string output = process.StandardOutput.ReadToEnd();
    string errors = process.StandardError.ReadToEnd();
    return output;
}


参考


[*]Developing for Android
[*]https://support.unity.com/hc/en-us/articles/115000292166-Symbolicate-Android-crash
页: [1]
查看完整版本: Unity真机调试