找回密码
 立即注册
查看: 444|回复: 0

Unity真机调试

[复制链接]
发表于 2023-4-20 09:37 | 显示全部楼层 |阅读模式
近期项目出现了不少性能问题,真机环境下与Editor差异很大,需要对真机环境的程序状态做监控。这些内容不复杂但比较零碎,因此做一些记录。(此章节会持续更新)
一、环境设置

1.1 Building Setting




  • 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


若出现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
    x0  00000071f003b5d0  x1  00000071f003f830  x2  00000071f003b938  x3  00000071f003f830
    x4  000000000000000a  x5  00000072776f5718  x6  0000000000000000  x7  0000000080808080
    x8  00000071f00400d0  x9  00000071f003b930  x10 0000000000000000  x11 00000071f003b630
    x12 00000071f003b588  x13 0000000000000001  x14 0000000000000001  x15 0000000000000000
    x16 0000007277a2c1d8  x17 0000007395125540  x18 0000007276828000  x19 0000007210005840
    x20 00000071f003f830  x21 0000007fec818280  x22 00000071a06686f0  x23 0000007276d4b1e4
    x24 0000007272a71028  x25 00000071a06686f0  x26 0000007399069020  x27 0000000000000000
    x28 0000007fec818870  x29 0000007fec8186b0
    sp  0000007fec818250  lr  0000007276c31fc8  pc  0000007276b33904


#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[index1 - 1]);
            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[index2 - 1]);
            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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-29 14:49 , Processed in 0.420462 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表