|
近期项目出现了不少性能问题,真机环境下与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
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|