美文网首页
2020-12-03 Unity Crash定位

2020-12-03 Unity Crash定位

作者: VECTOR_Y | 来源:发表于2020-12-03 14:18 被阅读0次

    转自:https://zhuanlan.zhihu.com/p/77984555

    最近做的项目上线之后崩溃率比较高,测试中没发现问题

    后台收到的崩溃日志大概这样,我是懵逼的,网上查询看到了上面知乎上大神的方式,可以通过符号表来编译这些错误信息,就可以定位到Crash的位置


    image.png

    大神列举的两个官方文章
    https://support.unity3d.com/hc/en-us/articles/115000292166-Symbolicate-Android-crash
    https://support.unity3d.com/hc/en-us/articles/115000292166-Symbolicate-Android-crash

    下面是我的尝试
    我们再打包的时候需要勾选下图Shmbols选项,这样打包的时候会同时生成符号表压缩包,我理解的打包中会做混淆,符号表相当于我们的密码本,我们用这个才能反射错误信息中的内容


    image.png

    下图为生成的压缩包


    image.png

    我们先找到arm-linux-androideabi-addr2line,找到自己的Ndk位置就好

    image.png
    或者通过下面的链接下载
    https://developer.android.com/ndk/downloads?hl=en

    我们首先尝试用cmd来尝试一下

    image.png
    命令行,需要根据自己的 路径修改
    "C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\NDK\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin\aarch64-linux-android-addr2line.exe" -f -C -e "D:\git\greedysnake\Snake3D\build\snake-0.1-v1.symbols\arm64-v8a\libil2cpp.sym.so" 0x4d3190
    反射的结果
    RuntimeInvoker_FalseIntPtr_t_RuntimeObject_IntPtr_t_IntPtr_t(void ()(), MethodInfo const, void, void*)

    下面贴上大神的代码,做了一点点修改

    
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.TextField("名称", name, GUILayout.MaxWidth(400));
            GUILayout.Space(10);
            if (GUILayout.Button("解析", GUILayout.Width(50)))
            {
                if (!JudgePath(PathType.addr2Line,addr2linePath))
                {
                    Debug.LogError("Ndk解析路径出错");
                    return;
                }
                if (!JudgePath(PathType.unitySoPath, unitydebugsoPath) && !JudgePath(PathType.il2cppSoPath, il2cppdebugsoPath))
                {
                    Debug.LogError("unity与il2cppSoPanth符合表路径出错");
                    return;
                }
                if (!JudgePath(PathType.il2cppSoPath, il2cppdebugsoPath))
                {
                    Debug.LogError("il2cppSoPanth符合表路径出错");
                }
                OutCrash(name,path);
            } 
            EditorGUILayout.EndHorizontal();
        }
    
        /// <summary>
        /// 根据获取Crash文件的文件创建Button与显示框
        /// </summary>
        /// <param name="path"></param>
        void GetCrashByPath(string path)
        {
            if (Directory.Exists(path))
            {
                var dirctory = new DirectoryInfo(path);
                var files = dirctory.GetFiles("*", SearchOption.AllDirectories);
                foreach (var fi in files)
                {
                    CreatorButton(fi.Name, path);
                }
            }
        }
    
        /// <summary>
        /// 打开Crash
        /// </summary>
        void OutCrash(string filename,string path)
        {
            isAnalysis = false;
            string filePath = string.Join("/",path,filename);
            using (StreamReader sr =new StreamReader(filePath))
            {
                while (!sr.EndOfStream)
                {
                    OutCmd(sr.ReadLine());
                }
            }
            if (!isAnalysis)
            {
                Debug.LogError("无法解析当前cash文件,请检查文件是否为设备崩溃日志");
            }
        }
    
        /// <summary>
        /// 解析Crash
        /// </summary>
        void OutCmd(string log)
        {
            if (log==null)
            {
                return;
            }       
            if (log.EndsWith(crashEndFlag))//找以libunity.so结尾的崩溃日志
            {
                if (log.Contains("pc"))
                {
                    int startIndex = log.IndexOf("pc") + 3;
                    if (log.Contains("/data/"))
                    {
                        int endIndex = log.IndexOf("/data/");
                        string addStr = log.Substring(startIndex, endIndex - startIndex - 1);
                        string tempUnitySoPath = string.Format("\"{0}\"", unitydebugsoPath);
                        ExecuteCmd(tempUnitySoPath, addStr);
                    }     
                } 
            }
            else//找 il2cpp和libunity 崩溃日志
            {
                if (log.Contains(il2cppflag) && JudgePath(PathType.il2cppSoPath,il2cppdebugsoPath))
                {
                    string tempill2cppSoPath = string.Format("\"{0}\"", il2cppdebugsoPath);
                    FindMiddleCrash(log, il2cppflag, tempill2cppSoPath);
                } else if(log.Contains(unityflag))
                {
                    string tempUnitySoPath = string.Format("\"{0}\"", unitydebugsoPath);
                    FindMiddleCrash(log,unityflag, tempUnitySoPath);
                }
            }
        }
    
        /// <summary>
        /// 找 il2cpp和libunity 崩溃日志
        /// </summary>
        /// <param name="log"></param>
        /// <param name="debugFlag">标志元素</param>
        /// <param name="SoPath">符号表路径</param>
        void FindMiddleCrash(string log,string debugFlag,string SoPath)
        {
            if (!string.IsNullOrEmpty(SoPath))
            {
                int startIndex = log.IndexOf(debugFlag);
                startIndex = startIndex + debugFlag.Length + 1;
                if (log.Contains("("))
                {
                    int endIndex = log.IndexOf("(");
                    if (endIndex > 0)
                    {
                        string addStr = log.Substring(startIndex, endIndex - startIndex);
                        ExecuteCmd(SoPath, addStr);
                    }
                }
            }
            else
            {
                Debug.LogErrorFormat("{0}的符号表路径为空",debugFlag);
            }
            
        }
    
        
        /// <summary>
        /// 执行CMD命令
        /// </summary>
        /// <param name="SoPath">符号表路径</param>
        /// <param name="addStr">崩溃代码地址</param>
        void ExecuteCmd(string soPath, string addStr)
        {
            string cmdStr = string.Join(" ", addr2linePath, "-f", "-C", "-e", soPath, addStr);
            CmdHandler.RunCmd(cmdStr, (str) =>
            {
               Debug.Log(string.Format("解析后{0}", ResultStr(str, addStr)));
                isAnalysis = true;
            });
    
        }
        /// <summary>
        /// 对解析结果进行分析
        /// </summary>
        /// <param name="str"></param>
        /// <param name="addStr"></param>
        /// <returns></returns>
        string ResultStr(string str,string addStr)
        {
            string tempStr = string.Empty;
            if (!string.IsNullOrEmpty(str))
            {
                if (str.Contains("exit"))
                {
                    int startIndex = str.IndexOf("exit");
                    if (startIndex < str.Length)
                    {
                        tempStr = str.Substring(startIndex);
                        if (tempStr.Contains(")"))
                        {
                            startIndex = tempStr.IndexOf("t") + 1;
                            int endIndex = tempStr.LastIndexOf(")");
                            tempStr = tempStr.Substring(startIndex, endIndex - startIndex + 1);
                            tempStr = string.Format("<color=red>[{0}]</color> :<color=yellow>{1}</color>", addStr, tempStr);
                        }
                        else
                        {
                            startIndex = tempStr.IndexOf("t") + 1;
                            tempStr = tempStr.Substring(startIndex);
                            tempStr = string.Format("<color=red>[{0}]</color> :<color=yellow>{1}</color>", addStr, tempStr);
                        }
                        
                    }
                }
                else
                {
                    Debug.LogErrorFormat("当前结果未执行cmd命令", str);
                }
            }
            else
            {
                Debug.LogErrorFormat("执行cmd:{0}命令,返回值为空", str);
            }
            return tempStr;     
        }
    
        private void OnDestroy()
        {
            EditorPrefs.SetString("addr2linePath", addr2linePath);
            EditorPrefs.SetString("il2cppdebugsoPath", il2cppdebugsoPath);
            EditorPrefs.SetString("unitydebugsoPath", unitydebugsoPath);
            EditorPrefs.SetString("MyCashPath", MyCashPath);
        }
    
    
    }
    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    public class CmdHandler
    {
        private static string CmdPath = "cmd.exe";
        //C:\Windows\System32\cmd.exe
        /// <summary>
        /// 执行cmd命令 返回cmd窗口显示的信息
        /// 多命令请使用批处理命令连接符:
        /// <![CDATA[
        /// &:同时执行两个命令
        /// |:将上一个命令的输出,作为下一个命令的输入
        /// &&:当&&前的命令成功时,才执行&&后的命令
        /// ||:当||前的命令失败时,才执行||后的命令]]>
        /// </summary>
        /// <param name="cmd">执行的命令</param>
        public static string RunCmd(string cmd,Action <string>act=null)
        {
            cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态
            using (Process p = new Process())
            {
                p.StartInfo.FileName = CmdPath;
                p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动
                p.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息
                p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息
                p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出
                p.StartInfo.CreateNoWindow = true;          //不显示程序窗口
                p.Start();//启动程序
    
                //向cmd窗口写入命令
                p.StandardInput.WriteLine(cmd);
                p.StandardInput.AutoFlush = true;
    
                //获取cmd窗口的输出信息
                string output = p.StandardOutput.ReadToEnd();
                p.WaitForExit();//等待程序执行完退出进程
                p.Close();
                if (act!=null)
                {
                    act(output);
                }
                return output;
            }
        }
    
        /// <summary>
        /// 执行多个cmd命令
        /// </summary>
        /// <param name="cmdList"></param>
        /// <param name="act"></param>
        public static void RunCmd(List<string> cmd, Action<string> act = null)
        {
            //cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态
            using (Process p = new Process())
            {
                p.StartInfo.FileName = CmdPath;
                p.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动
                p.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息
                p.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息
                p.StartInfo.RedirectStandardError = true;   //重定向标准错误输出
                p.StartInfo.CreateNoWindow = true;          //不显示程序窗口
                p.Start();//启动程序
    
                //向cmd窗口写入命令
                foreach (var cm in cmd)
                {
                    p.StandardInput.WriteLine(cm);
                    p.StandardInput.WriteLine("exit");
                    p.StandardInput.AutoFlush = true;
                    //获取cmd窗口的输出信息
                    string output = p.StandardOutput.ReadToEnd();
                    if (act != null)
                    {
                        act(output);
                    }
                    p.Start();
                }
    
                p.WaitForExit();//等待程序执行完退出进程
                p.Close();
            }
        }
    }
    
    

    效果如下


    image.png image.png image.png

    确实定位到了代码中的问题,真的是非常感谢,这里抄一下以防以后忘掉

    相关文章

      网友评论

          本文标题:2020-12-03 Unity Crash定位

          本文链接:https://www.haomeiwen.com/subject/rgemwktx.html