在公司内部定制引擎或者开发编辑器,时常遇到崩溃的情况, 希望可以帮助开发人员快速定位问题,需要将崩溃报告收集上传到服务器 。
虚幻自带的crash reporter 看起来满足了这个需求:
https://docs.unrealengine.com/4.26/en-US/TestingAndOptimization/CrashReporter/
但是在最新的UE5分支中并沒有看到crashreporterserver对应的配置项和工程代码, 不过有crashreporterclient工程, 因此需要分析代码做一些修改以支持。
主要思路:
- 查看引擎调用CrashReporter的时机和相关参数
- 查看CrashReporterClient的行为和参数
- 决定Hook修改的地方以定义自己的收集行为
CrashReporter 调用
搜索CrashReportClient 发现代码片段 , 在文件WindowsPlatformCrashContext.cpp 中
/**
* Finds the crash reporter binary path. Returns true if the file exists.
*/
bool CreateCrashReportClientPath(TCHAR* OutClientPath, int32 MaxLength)
{
auto CreateCrashReportClientPathImpl = [&OutClientPath, MaxLength](const TCHAR* CrashReportClientExeName) -> bool
{
const TCHAR* EngineDir = FPlatformMisc::EngineDir();
const TCHAR* BinariesDir = FPlatformProcess::GetBinariesSubdirectory();
// Find the path to crash reporter binary. Avoid creating FStrings.
*OutClientPath = 0;
FCString::Strncat(OutClientPath, EngineDir, MaxLength);
FCString::Strncat(OutClientPath, TEXT("Binaries/"), MaxLength);
FCString::Strncat(OutClientPath, BinariesDir, MaxLength);
FCString::Strncat(OutClientPath, TEXT("/"), MaxLength);
FCString::Strncat(OutClientPath, CrashReportClientExeName, MaxLength);
const DWORD Results = GetFileAttributesW(OutClientPath);
return Results != INVALID_FILE_ATTRIBUTES;
};
#if WITH_EDITOR
const TCHAR CrashReportClientShippingName[] = TEXT("CrashReportClientEditor.exe");
const TCHAR CrashReportClientDevelopmentName[] = TEXT("CrashReportClientEditor-Win64-Development.exe");
const TCHAR CrashReportClientDebugName[] = TEXT("CrashReportClientEditor-Win64-Debug.exe");
#else
const TCHAR CrashReportClientShippingName[] = TEXT("CrashReportClient.exe");
const TCHAR CrashReportClientDevelopmentName[] = TEXT("CrashReportClient-Win64-Development.exe");
const TCHAR CrashReportClientDebugName[] = TEXT("CrashReportClient-Win64-Debug.exe");
#endif
调用有两处, 分别是 LaunchCrashReportClient 和 ReportCrashUsingCrashReportClient
在此处下断点查看调用情况 , 启动后发现立即调用到, 因为启动后立即创建了一个线程 FCrashReportingThread 。
再下一次断点在参数传递(可能是管道)的地方 。
断点调试
下断点, 启动Editor 后, 创建一个Demo工程, 制造一个崩溃 (C++代码空指针), 查看堆栈调用 。
Editor 启动后 CrashReport 会有一个单独的线程监控CrashEvent , 也就是启动的 CrashReporter进程
while (StopTaskCounter.GetValue() == 0)
{
if (WaitForSingleObject(CrashEvent, 500) == WAIT_OBJECT_0)
{
ResetEvent(CrashHandledEvent);
HandleCrashInternal();
ResetEvent(CrashEvent);
// Let the thread that crashed know we're done.
SetEvent(CrashHandledEvent);
break;
}
在制造崩溃时,也Attach 到 CrashReporter 进程,崩溃后在CrashReportEditor 中点击Send And Close 则可以在 CrashReportClient 进程中捕获到断点输入 。
FReply FCrashReportClient::Submit()
{
bSendData = true;
StoreCommentAndUpload();
bShouldWindowBeHidden = true;
return FReply::Handled();
}其中DataRouter默认的URL是L"https://datarouter.ol.epicgames.com/datarouter/api/v1/public/data"
上传堆栈:
[内联框架] CrashReportClientEditor-Win64-Development.exe!FCrashUploadBase::SetCurrentState(FCrashUploadBase::EUploadState::Type) 行 312 C++
> CrashReportClientEditor-Win64-Development.exe!FCrashUploadToDataRouter::BeginUpload(const FWindowsErrorReport & PlatformErrorReport) 行 744 C++
CrashReportClientEditor-Win64-Development.exe!FCrashReportClient::Tick(float UnusedDeltaTime) 行 296 C++
[内联框架] CrashReportClientEditor-Win64-Development.exe!TDelegate<bool __cdecl(float),FDefaultDelegateUserPolicy>::Execute(float) 行 580 C++
[内联框架] CrashReportClientEditor-Win64-Development.exe!FTicker::FElement::Fire(float) 行 127 C++
CrashReportClientEditor-Win64-Development.exe!FTicker::Tick(float DeltaTime) 行 92 C++
CrashReportClientEditor-Win64-Development.exe!FMainLoopTiming::Tick() 行 32 C++
CrashReportClientEditor-Win64-Development.exe!RunWithUI(FWindowsErrorReport ErrorReport) 行 355 C++
CrashReportClientEditor-Win64-Development.exe!SendErrorReport(FWindowsErrorReport & ErrorReport, TOptional<bool> bNoDialogOpt, TOptional<bool> bImplicitSendOpt) 行 608 C++
CrashReportClientEditor-Win64-Development.exe!RunCrashReportClient(const wchar_t * CommandLine) 行 1074 C++
CrashReportClientEditor-Win64-Development.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) 行 150 C++
CrashReporter 上报参数
CrashReporterEditor 监测管道信息, 如果Engine崩溃, 那么Editor收到崩溃信息, IsCrashReportAvailable 检查, 获取到进程模块等相关信息,
然后Reporter 同步到 UI显示 ,当用户点击Send 时, 调用 Uploader, DataRouter 相关请求接口收集 。
DataRouter 和 CrashUploader 相关, 查看相应的代码即可 。
自定义流程
修改引擎或者修改CrashReporterClient
显然改Client更简单,而且作为公司内部使用分发也问题不大 。
主要修改的地方可以类似 CrashReportUpload , 新增, 通过Http服务上报ErrorReport即可 。
