美文网首页
UE4收集崩溃日志

UE4收集崩溃日志

作者: eca3ef0d3d2d | 来源:发表于2017-09-26 10:54 被阅读718次

摘要

项目中接入了Crasheye帮助收集崩溃信息,可最近测试总是反馈说刚刚崩溃的信息在crasheye上差不到,而本地却打印出了崩溃日志,这个比较郁闷,最终定位到,空指针的崩溃能收集到,check(UE_LOG)的崩溃收集不到,why?请教各位大神后,发现是信号的原因,在check(false)后,是不会触发crash sig的,而UE4则从call stack中拿信息写到本地日志中!哎,隐藏的好深啊,本篇文章就简单的记录一下UE4处理崩溃信息的大概思路。

一. 信号机制

信号处理

函数运行在用户态,当遇到系统调用、中断或是异常的情况时,程序会进入内核态。信号涉及到了这两种状态之间的转换,过程可以先看一下下面的示意图:

1.信号的接收

接收信号的任务是由内核代理的,当内核接收到信号后,会将其放到对应进程的信号队列中,同时向进程发送一个中断,使其陷入内核态。
注意,此时信号还只是在队列中,对进程来说暂时是不知道有信号到来的。

2.信号的检测

进程陷入内核态后,有两种场景会对信号进行检测:
进程从内核态返回到用户态前进行信号检测
进程在内核态中,从睡眠状态被唤醒的时候进行信号检测

当发现有新信号时,便会进入下一步,信号的处理。

3.信号的处理

信号处理函数是运行在用户态的,调用处理函数前,内核会将当前内核栈的内容备份拷贝到用户栈上,并且修改指令寄存器(eip)将其指向信号处理函数。

接下来进程返回到用户态中,执行相应的信号处理函数。

信号处理函数执行完成后,还需要返回内核态,检查是否还有其它信号未处理。如果所有信号都处理完成,就会将内核栈恢复(从用户栈的备份拷贝回来),同时恢复指令寄存器(eip)将其指向中断前的运行位置,最后回到用户态继续执行进程。

至此,一个完整的信号处理流程便结束了,如果同时有多个信号到达,上面的处理流程会在第2步和第3步骤间重复进行。
所以第一步就是要用信号处理函数捕获到native crash(SIGSEGV, SIGBUS等)。

二. UE4信号处理

//UE4中,使用SetCrashHandler注册信号处理器
void FAndroidMisc::SetCrashHandler(void(*CrashHandler)(const FGenericCrashContext& Context))
{
    GCrashHandlerPointer = CrashHandler;
    ...
    struct sigaction Action;
    FMemory::Memzero(&Action, sizeof(struct sigaction));
    Action.sa_sigaction = PlatformCrashHandler;
    sigemptyset(&Action.sa_mask);
    Action.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK;

    for (int32 i = 0; i < NumTargetSignals; ++i)
    {
        //sigaction回调函数的第三个参数是一个指向ucontext_t的指针,
        //ucontext_t收集了寄存器数值(还有各种处理器特定的信息)。
        sigaction(TargetSignals[i], &Action, &PrevActions[i]);
    }
    ...
}

//当发生回调时,执行PlatformCrashHandler
void PlatformCrashHandler(int32 Signal, siginfo* Info, void* Context)
{
    ...
    if (GCrashHandlerPointer)
    {
        //这里的函数指针指向崩溃处理函数,即下面的EngineCrashHandler
        GCrashHandlerPointer(CrashContext);
    }
    else
    {
        // call default one
        DefaultCrashHandler(CrashContext);
    }
    ..
}

//注册崩溃信号处理函数EngineCrashHandler
JNIEXPORT jint JNI_OnLoad(JavaVM* InJavaVM, void* InReserved)
{
    ...
    // hook signals
    if (!FPlatformMisc::IsDebuggerPresent() || GAlwaysReportCrash)
    {
#if !UE_BUILD_SHIPPING
        FPlatformMisc::SetCrashHandler(EngineCrashHandler);
#endif
    }
    ....
}


至此,UE4完成了崩溃信息的收集及处理流程,EngineCrashHandler中,会将崩溃信息写到本地log中。然而,对于手游项目而言,客户端本地的崩溃信息无法满足调试优化的需求(shipping版本还没有崩溃信息)。所以,我们需要将本地的崩溃信息上传到服务器,以供项目组调优。目前,很多第三方崩溃收集工具提供了这样的功能,我们使用的Crasheye(集成了google breakpad,通过注册crash sig来处理崩溃信息)可以帮助我们将崩溃信息上传的服务器并解析成可读信息。

三. UE4的特殊“崩溃”

特殊指的是check 或者 UE_LOG中的Error和Fatal,当触发这种逻辑的时候也会崩溃,准确的说,应该是退出游戏FGenericPlatformMisc::RequestExit(true),所以,这种情况是不会触发信号的。在非shipping下,它也会写本地log,但是,我们同样希望将这些日志发送到服务器,所以,就需要将写日志的逻辑替换为发送崩溃信息到crasheye了,最简单的方法就是在写日志的后面加一句调用发送日志的API,但是,我们不希望改动引擎的代码!
方法是,替换引擎自带的AndroidOutputDevice,自己写一个CrasheyeOutputDevice将崩溃信息发送到crasheye服务器
GError = CrasheyeOutputDevice.GetError();
需要特殊注意的是,上传日志的API是个比较耗时的函数,不要调用完毕后直接退出游戏,这样是上传不成功的。

相关文章

  • UE4收集崩溃日志

    摘要 项目中接入了Crasheye帮助收集崩溃信息,可最近测试总是反馈说刚刚崩溃的信息在crasheye上差不到,...

  • 代码:android崩溃日志收集和处理

    用来处理android崩溃日志收集的代码,详情的使用请转:android崩溃日志收集和处理 第一个类 /** ...

  • 【iOS】腾讯Bugly使用记录

    由于项目中以前没有收集崩溃日志,对崩溃问题都是测试发给我们崩溃日志,然后我们自己去进行符号化,再进行分析,这样的话...

  • Cordova崩溃日志收集-Android篇

    一、需求 收集移动端app闪退日志 支持离线收集 开始准备使用腾讯的bugly来统一收集崩溃日志,但是存在一下特殊...

  • 使用NSSetUncaughtExceptionHandler收

    1. 在代码中添加手机崩溃日志代码 2. 收集到的崩溃日志如下 dSYM符号表获取 xcode->window->...

  • 线上崩溃的解决办法

    开发过程中对于线上项目崩溃我使用的是友盟统计,它会收集到线上的崩溃日志,根据崩溃日志我们可以快速定位到代码中崩溃位...

  • Android 崩溃日志收集

    原理:应用出现异常后,会由默认的异常处理器来处理异常,我们要做的就是把这个任务接管过来,自己处理异常,包括收集日志...

  • IOS 崩溃日志收集

    通过崩溃捕获和收集,可以收集到已发布应用(游戏)的异常,以便开发人员发现和修改bug,对于提高软件质量有着极大的帮...

  • iOS使用 Metrickit 收集崩溃日志

    iOS使用 Metrickit 收集崩溃日志 Metrickit 是什么 MetricKit 是 苹果在 iOS...

  • 解析IOS崩溃日志(crash-Log)-抓取线上bug

    写在前言 基本上我们每个app发布之后都要拿到线上的崩溃日志来收集起来,线下来根据崩溃日志来进行修改代码,或者直接...

网友评论

      本文标题:UE4收集崩溃日志

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