2016-12-08 08:52:44 -05:00
|
|
|
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2015-03-02 07:52:38 -05:00
|
|
|
#include "CrashDebugHelperMac.h"
|
2014-09-08 09:01:00 -04:00
|
|
|
#include "EngineVersion.h"
|
2014-09-04 12:10:15 -04:00
|
|
|
#include "ApplePlatformSymbolication.h"
|
2015-03-02 07:34:19 -05:00
|
|
|
#include "CrashReporter.h"
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#include "CrashDebugHelperPrivate.h"
|
|
|
|
|
#include "Misc/FileHelper.h"
|
|
|
|
|
#include "Misc/CommandLine.h"
|
|
|
|
|
#include "Misc/Paths.h"
|
2014-09-04 12:10:15 -04:00
|
|
|
#include <cxxabi.h>
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2015-07-03 10:06:17 -04:00
|
|
|
FString ExtractRelativePath( const TCHAR* BaseName, TCHAR const* FullName )
|
|
|
|
|
{
|
|
|
|
|
FString FullPath = FString( FullName ).ToLower();
|
|
|
|
|
FullPath = FullPath.Replace( TEXT( "\\" ), TEXT( "/" ) );
|
|
|
|
|
|
|
|
|
|
TArray<FString> Components;
|
|
|
|
|
int32 Count = FullPath.ParseIntoArray( Components, TEXT( "/" ), true );
|
|
|
|
|
FullPath = TEXT( "" );
|
|
|
|
|
|
|
|
|
|
for( int32 Index = 0; Index < Count; Index++ )
|
|
|
|
|
{
|
|
|
|
|
if( Components[Index] == BaseName )
|
|
|
|
|
{
|
|
|
|
|
if( Index > 0 )
|
|
|
|
|
{
|
|
|
|
|
for( int32 Inner = Index - 1; Inner < Count; Inner++ )
|
|
|
|
|
{
|
|
|
|
|
FullPath += Components[Inner];
|
|
|
|
|
if( Inner < Count - 1 )
|
|
|
|
|
{
|
|
|
|
|
FullPath += TEXT( "/" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FullPath;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 12:10:15 -04:00
|
|
|
static int32 ParseReportVersion(TCHAR const* CrashLog, int32& OutVersion)
|
|
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
|
|
|
|
TCHAR const* VersionLine = FCStringWide::Strstr(CrashLog, TEXT("Report Version:"));
|
|
|
|
|
if (VersionLine)
|
|
|
|
|
{
|
|
|
|
|
Found = swscanf(VersionLine, TEXT("%*ls %*ls %d"), &OutVersion);
|
|
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 ParseVersion(TCHAR const* CrashLog, int32& OutMajor, int32& OutMinor, int32& OutBuild, int32& OutChangeList, FString& OutBranch)
|
|
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
|
|
|
|
TCHAR const* VersionLine = FCStringWide::Strstr(CrashLog, TEXT("Version:"));
|
|
|
|
|
if (VersionLine)
|
|
|
|
|
{
|
|
|
|
|
TCHAR Branch[257] = {0};
|
|
|
|
|
Found = swscanf(VersionLine, TEXT("%*s %d.%d.%d (%*d.%*d.%*d-%d+%256ls)"), &OutMajor, &OutMinor, &OutBuild, &OutChangeList, Branch);
|
|
|
|
|
if(Found == 5)
|
|
|
|
|
{
|
|
|
|
|
TCHAR* BranchEnd = FCStringWide::Strchr(Branch, TEXT(')'));
|
|
|
|
|
if(BranchEnd)
|
|
|
|
|
{
|
|
|
|
|
*BranchEnd = TEXT('\0');
|
|
|
|
|
}
|
|
|
|
|
OutBranch = Branch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
static int32 ParseOS(TCHAR const* CrashLog, uint16& OutMajor, uint16& OutMinor, uint16& OutPatch, uint16& OutBuild)
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
|
|
|
|
TCHAR const* VersionLine = FCStringWide::Strstr(CrashLog, TEXT("OS Version:"));
|
|
|
|
|
if (VersionLine)
|
|
|
|
|
{
|
2014-09-08 09:01:00 -04:00
|
|
|
Found = swscanf(VersionLine, TEXT("%*s %*s Mac OS X %hd.%hd.%hd (%hxd)"), &OutMajor, &OutMinor, &OutPatch, &OutBuild);
|
2014-09-16 10:24:07 -04:00
|
|
|
if ( Found == 2 )
|
|
|
|
|
{
|
|
|
|
|
OutPatch = 0;
|
|
|
|
|
Found += swscanf(VersionLine, TEXT("%*s %*s Mac OS X %*hd.%*hd (%hxd)"), &OutBuild) + 1;
|
|
|
|
|
}
|
2014-09-04 12:10:15 -04:00
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
static bool ParseModel(TCHAR const* CrashLog, FString& OutModelDetails, uint32& OutProcessorNum)
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
bool bFound = false;
|
|
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Model:"));
|
|
|
|
|
if (Line)
|
|
|
|
|
{
|
|
|
|
|
Line += FCStringWide::Strlen(TEXT("Model: "));
|
|
|
|
|
TCHAR const* End = FCStringWide::Strchr(Line, TEXT('\r'));
|
|
|
|
|
if(!End)
|
|
|
|
|
{
|
|
|
|
|
End = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
}
|
|
|
|
|
check(End);
|
|
|
|
|
|
|
|
|
|
int32 Length = FMath::Min(256, (int32)((uintptr_t)(End - Line)));
|
|
|
|
|
OutModelDetails.Append(Line, Length);
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
OutProcessorNum = 1;
|
|
|
|
|
int32 ProcessorPos = OutModelDetails.Find(TEXT(" processors"));
|
|
|
|
|
if( ProcessorPos != INDEX_NONE )
|
|
|
|
|
{
|
|
|
|
|
int32 NumStart = ProcessorPos;
|
|
|
|
|
while(NumStart && OutModelDetails[NumStart] != TEXT(','))
|
|
|
|
|
{
|
|
|
|
|
NumStart--;
|
|
|
|
|
}
|
|
|
|
|
if(NumStart >= 0 && OutModelDetails[NumStart] == TEXT(','))
|
|
|
|
|
{
|
|
|
|
|
NumStart += 2;
|
|
|
|
|
FString NumProc = OutModelDetails.Mid(NumStart, ProcessorPos-NumStart);
|
|
|
|
|
if(NumProc.IsNumeric())
|
|
|
|
|
{
|
|
|
|
|
TTypeFromString<uint32>::FromString(OutProcessorNum, *NumProc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 12:10:15 -04:00
|
|
|
bFound = true;
|
|
|
|
|
}
|
|
|
|
|
return bFound;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 ParseGraphics(TCHAR const* CrashLog, FString& OutGPUDetails)
|
|
|
|
|
{
|
|
|
|
|
bool bFound = false;
|
|
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Graphics:"));
|
|
|
|
|
int32 Output = 0;
|
|
|
|
|
while (Line)
|
|
|
|
|
{
|
|
|
|
|
Line += FCStringWide::Strlen(TEXT("Graphics:"));
|
|
|
|
|
TCHAR const* End = FCStringWide::Strchr(Line, TEXT('\r'));
|
|
|
|
|
if(!End)
|
|
|
|
|
{
|
|
|
|
|
End = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
}
|
|
|
|
|
check(End);
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
OutGPUDetails.Append(TEXT(", "));
|
2014-09-04 12:10:15 -04:00
|
|
|
int32 Length = FMath::Min((256 - Output), (int32)((uintptr_t)(End - Line)));
|
|
|
|
|
OutGPUDetails.Append(Line, Length);
|
|
|
|
|
|
|
|
|
|
Line = FCStringWide::Strstr(Line, TEXT("Graphics:"));
|
|
|
|
|
|
|
|
|
|
bFound = true;
|
|
|
|
|
}
|
|
|
|
|
return bFound;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 ParseError(TCHAR const* CrashLog, FString& OutErrorDetails)
|
|
|
|
|
{
|
|
|
|
|
bool bFound = false;
|
2015-03-02 07:34:19 -05:00
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Exception Codes:"));
|
|
|
|
|
if (Line)
|
|
|
|
|
{
|
|
|
|
|
Line += FCStringWide::Strlen(TEXT("Exception Codes:"));
|
|
|
|
|
check(Line);
|
|
|
|
|
TCHAR const* End = FCStringWide::Strchr(Line, TEXT('\r'));
|
|
|
|
|
if(!End)
|
|
|
|
|
{
|
|
|
|
|
End = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
}
|
|
|
|
|
check(End);
|
|
|
|
|
|
|
|
|
|
int32 Length = FMath::Min(PATH_MAX, (int32)((uintptr_t)(End - Line)));
|
|
|
|
|
OutErrorDetails.Append(Line, Length);
|
|
|
|
|
|
|
|
|
|
bFound = true;
|
|
|
|
|
}
|
|
|
|
|
Line = FCStringWide::Strstr(CrashLog, TEXT("Application Specific Information:"));
|
2014-09-04 12:10:15 -04:00
|
|
|
if (Line)
|
|
|
|
|
{
|
|
|
|
|
Line = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
check(Line);
|
|
|
|
|
Line += 1;
|
|
|
|
|
TCHAR const* End = FCStringWide::Strchr(Line, TEXT('\r'));
|
|
|
|
|
if(!End)
|
|
|
|
|
{
|
|
|
|
|
End = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
}
|
|
|
|
|
check(End);
|
|
|
|
|
|
|
|
|
|
int32 Length = FMath::Min(PATH_MAX, (int32)((uintptr_t)(End - Line)));
|
2015-03-02 07:34:19 -05:00
|
|
|
OutErrorDetails += TEXT(" ");
|
2014-09-04 12:10:15 -04:00
|
|
|
OutErrorDetails.Append(Line, Length);
|
|
|
|
|
|
|
|
|
|
bFound = true;
|
|
|
|
|
}
|
|
|
|
|
return bFound;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
static int32 ParseExceptionCode(TCHAR const* CrashLog, uint32& OutExceptionCode)
|
|
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
|
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Exception Type:"));
|
|
|
|
|
if(Line)
|
|
|
|
|
{
|
|
|
|
|
TCHAR Buffer[257] = {0};
|
2015-03-02 07:34:19 -05:00
|
|
|
Found = swscanf(Line, TEXT("%*s %*s %*s (%256ls)"), Buffer);
|
|
|
|
|
if(!Found)
|
|
|
|
|
{
|
|
|
|
|
Found = swscanf(Line, TEXT("%*s %*s %256ls"), Buffer);
|
|
|
|
|
}
|
2014-09-08 09:01:00 -04:00
|
|
|
if(Found)
|
|
|
|
|
{
|
|
|
|
|
TCHAR* End = FCStringWide::Strchr(Buffer, TEXT(')'));
|
|
|
|
|
if(End)
|
|
|
|
|
{
|
|
|
|
|
*End = TEXT('\0');
|
|
|
|
|
}
|
|
|
|
|
if(FCStringWide::Strcmp(Buffer, TEXT("SIGQUIT")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGQUIT;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGILL")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGILL;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGEMT")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGEMT;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGFPE")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGFPE;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGBUS")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGBUS;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGSEGV")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGSEGV;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGSYS")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGSYS;
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGABRT")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGABRT;
|
|
|
|
|
}
|
2015-08-19 13:40:24 -04:00
|
|
|
else if(FCStringWide::Strcmp(Buffer, TEXT("SIGTRAP")) == 0)
|
|
|
|
|
{
|
|
|
|
|
OutExceptionCode = SIGTRAP;
|
|
|
|
|
}
|
2015-03-02 07:34:19 -05:00
|
|
|
else if(FString(Buffer).IsNumeric())
|
2014-09-08 09:01:00 -04:00
|
|
|
{
|
|
|
|
|
Found = swscanf(Buffer, TEXT("%u"), &OutExceptionCode);
|
|
|
|
|
}
|
2015-03-02 07:34:19 -05:00
|
|
|
else
|
|
|
|
|
{
|
2015-03-02 10:21:50 -05:00
|
|
|
ensure(false);
|
2015-03-02 07:34:19 -05:00
|
|
|
OutExceptionCode = SIGUSR1;
|
|
|
|
|
}
|
2014-09-08 09:01:00 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 ParseCrashedThread(TCHAR const* CrashLog, uint32& OutThreadNumber)
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
|
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Crashed Thread:"));
|
|
|
|
|
if (Line)
|
|
|
|
|
{
|
2014-09-08 09:01:00 -04:00
|
|
|
Found = swscanf(Line, TEXT("%*s %*s %u"), &OutThreadNumber);
|
2014-09-04 12:10:15 -04:00
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
static int32 ParseProcessID(TCHAR const* CrashLog, uint32& OutPID)
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
2014-09-08 09:01:00 -04:00
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Process:"));
|
|
|
|
|
if (Line)
|
|
|
|
|
{
|
|
|
|
|
Found = swscanf(Line, TEXT("%*s %*s [%u]"), &OutPID);
|
|
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static TCHAR const* FindThreadStack(TCHAR const* CrashLog, uint32 const ThreadNumber)
|
|
|
|
|
{
|
|
|
|
|
int32 Found = 0;
|
|
|
|
|
FString Format = FString::Printf(TEXT("Thread %u"), ThreadNumber);
|
2014-09-04 12:10:15 -04:00
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, *Format);
|
|
|
|
|
if (Line)
|
|
|
|
|
{
|
|
|
|
|
Line = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
check(Line);
|
|
|
|
|
Line += 1;
|
|
|
|
|
}
|
|
|
|
|
return Line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static TCHAR const* FindCrashedThreadStack(TCHAR const* CrashLog)
|
|
|
|
|
{
|
|
|
|
|
TCHAR const* Line = nullptr;
|
2014-09-08 09:01:00 -04:00
|
|
|
uint32 ThreadNumber = 0;
|
2014-09-04 12:10:15 -04:00
|
|
|
int32 Found = ParseCrashedThread(CrashLog, ThreadNumber);
|
|
|
|
|
if(Found)
|
|
|
|
|
{
|
|
|
|
|
Line = FindThreadStack(CrashLog, ThreadNumber);
|
|
|
|
|
}
|
|
|
|
|
return Line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 ParseThreadStackLine(TCHAR const* StackLine, FString& OutModuleName, uint64& OutProgramCounter, FString& OutFunctionName, FString& OutFileName, int32& OutLineNumber)
|
|
|
|
|
{
|
|
|
|
|
TCHAR ModuleName[257];
|
|
|
|
|
TCHAR FunctionName[1025];
|
|
|
|
|
TCHAR FileName[257];
|
|
|
|
|
|
2015-03-02 07:34:19 -05:00
|
|
|
int32 Found = swscanf(StackLine, TEXT("%*d %256ls 0x%lx"), ModuleName, &OutProgramCounter);
|
|
|
|
|
if(Found == 2)
|
|
|
|
|
{
|
|
|
|
|
uint64 FunctionAddress = 0;
|
|
|
|
|
uint32 FunctionOffset = 0;
|
|
|
|
|
if(swscanf(StackLine, TEXT("%*d %*ls %*lx 0x%lx + %d"), &FunctionAddress, &FunctionOffset) == 0)
|
|
|
|
|
{
|
|
|
|
|
Found += swscanf(StackLine, TEXT("%*d %*ls %*lx %1024ls + %*d (%256ls:%d)"), FunctionName, FileName, &OutLineNumber);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 12:10:15 -04:00
|
|
|
switch(Found)
|
|
|
|
|
{
|
|
|
|
|
case 5:
|
|
|
|
|
case 4:
|
|
|
|
|
{
|
|
|
|
|
OutFileName = FileName;
|
|
|
|
|
}
|
|
|
|
|
case 3:
|
|
|
|
|
{
|
|
|
|
|
int32 Status = -1;
|
|
|
|
|
ANSICHAR* DemangledName = abi::__cxa_demangle(TCHAR_TO_UTF8(FunctionName), nullptr, nullptr, &Status);
|
|
|
|
|
if(DemangledName && Status == 0)
|
|
|
|
|
{
|
|
|
|
|
// C++ function
|
|
|
|
|
OutFunctionName = FString::Printf(TEXT("%ls "), UTF8_TO_TCHAR(DemangledName));
|
|
|
|
|
}
|
|
|
|
|
else if (FCStringWide::Strlen(FunctionName) > 0 && FCStringWide::Strchr(FunctionName, ']'))
|
|
|
|
|
{
|
|
|
|
|
// ObjC function
|
|
|
|
|
OutFunctionName = FString::Printf(TEXT("%ls "), FunctionName);
|
|
|
|
|
}
|
|
|
|
|
else if(FCStringWide::Strlen(FunctionName) > 0)
|
|
|
|
|
{
|
|
|
|
|
// C function
|
|
|
|
|
OutFunctionName = FString::Printf(TEXT("%ls() "), FunctionName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
case 1:
|
|
|
|
|
{
|
|
|
|
|
OutModuleName = ModuleName;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 10:21:50 -05:00
|
|
|
static int32 SymboliseStackInfo(FPlatformSymbolDatabaseSet& SymbolCache, TArray<FCrashModuleInfo> const& ModuleInfo, FString ModuleName, uint64 const ProgramCounter, FString& OutFunctionName, FString& OutFileName, int32& OutLineNumber)
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
FProgramCounterSymbolInfo Info;
|
|
|
|
|
int32 ValuesSymbolised = 0;
|
|
|
|
|
|
|
|
|
|
FCrashModuleInfo Module;
|
|
|
|
|
for (auto Iterator : ModuleInfo)
|
|
|
|
|
{
|
|
|
|
|
if(Iterator.Name.EndsWith(ModuleName))
|
|
|
|
|
{
|
|
|
|
|
Module = Iterator;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-02 10:21:50 -05:00
|
|
|
|
|
|
|
|
FApplePlatformSymbolDatabase* Db = SymbolCache.Find(Module.Report);
|
|
|
|
|
if(!Db)
|
|
|
|
|
{
|
|
|
|
|
FApplePlatformSymbolDatabase Database;
|
|
|
|
|
if(FPlatformSymbolication::LoadSymbolDatabaseForBinary(TEXT(""), Module.Name, Module.Report, Database))
|
|
|
|
|
{
|
|
|
|
|
SymbolCache.Add(Database);
|
|
|
|
|
Db = SymbolCache.Find(Module.Report);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if((Module.Name.Len() > 0) && Db && FPlatformSymbolication::SymbolInfoForStrippedSymbol(*Db, ProgramCounter, Module.BaseOfImage, Module.Report, Info))
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
if(FCStringAnsi::Strlen(Info.FunctionName) > 0)
|
|
|
|
|
{
|
|
|
|
|
OutFunctionName = Info.FunctionName;
|
|
|
|
|
ValuesSymbolised++;
|
|
|
|
|
}
|
|
|
|
|
if(ValuesSymbolised == 1 && FCStringAnsi::Strlen(Info.Filename) > 0)
|
|
|
|
|
{
|
|
|
|
|
OutFileName = Info.Filename;
|
|
|
|
|
ValuesSymbolised++;
|
|
|
|
|
}
|
|
|
|
|
if(ValuesSymbolised == 2 && Info.LineNumber > 0)
|
|
|
|
|
{
|
|
|
|
|
OutLineNumber = Info.LineNumber;
|
|
|
|
|
ValuesSymbolised++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ValuesSymbolised;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static TCHAR const* FindModules(TCHAR const* CrashLog)
|
|
|
|
|
{
|
|
|
|
|
TCHAR const* Line = FCStringWide::Strstr(CrashLog, TEXT("Binary Images:"));
|
|
|
|
|
if (Line)
|
|
|
|
|
{
|
|
|
|
|
Line = FCStringWide::Strchr(Line, TEXT('\n'));
|
|
|
|
|
check(Line);
|
|
|
|
|
Line += 1;
|
|
|
|
|
}
|
|
|
|
|
return Line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 ParseModuleVersion(TCHAR const* Version, uint16& OutMajor, uint16& OutMinor, uint16& OutPatch, uint16& OutBuild)
|
|
|
|
|
{
|
|
|
|
|
OutMajor = OutMinor = OutPatch = OutBuild = 0;
|
|
|
|
|
int32 Found = swscanf(Version, TEXT("%hu.%hu.%hu"), &OutMajor, &OutMinor, &OutPatch);
|
|
|
|
|
TCHAR const* CurrentStart = FCStringWide::Strchr(Version, TEXT('-'));
|
|
|
|
|
if(CurrentStart)
|
|
|
|
|
{
|
|
|
|
|
int32 Components[3] = {0, 0, 0};
|
|
|
|
|
int32 Result = swscanf(CurrentStart, TEXT("%*ls %d.%d.%d"), &Components[0], &Components[1], &Components[2]);
|
|
|
|
|
OutBuild = (uint16)(Components[0] * 10000) + (Components[1] * 100) + (Components[2]);
|
|
|
|
|
Found = 4;
|
|
|
|
|
}
|
|
|
|
|
return Found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ParseModuleLine(TCHAR const* ModuleLine, FCrashModuleInfo& OutModule)
|
|
|
|
|
{
|
|
|
|
|
bool bOK = false;
|
|
|
|
|
TCHAR ModuleName[257] = {0};
|
|
|
|
|
uint64 ModuleBase = 0;
|
|
|
|
|
uint64 ModuleEnd = 0;
|
|
|
|
|
|
|
|
|
|
int32 Found = swscanf(ModuleLine, TEXT("%lx %*ls %lx %256ls"), &ModuleBase, &ModuleEnd, ModuleName);
|
|
|
|
|
switch (Found)
|
|
|
|
|
{
|
|
|
|
|
case 3:
|
|
|
|
|
{
|
|
|
|
|
TCHAR const* VersionStart = FCStringWide::Strchr(ModuleLine, TEXT('('));
|
|
|
|
|
TCHAR const* VersionEnd = FCStringWide::Strchr(ModuleLine, TEXT(')'));
|
|
|
|
|
if(VersionStart && VersionEnd)
|
|
|
|
|
{
|
|
|
|
|
++VersionStart;
|
|
|
|
|
Found += ParseModuleVersion(VersionStart, OutModule.Major, OutModule.Minor, OutModule.Patch, OutModule.Revision);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TCHAR const* UUIDStart = FCStringWide::Strchr(ModuleLine, TEXT('<'));
|
|
|
|
|
TCHAR const* UUIDEnd = FCStringWide::Strchr(ModuleLine, TEXT('>'));
|
|
|
|
|
if(UUIDStart && UUIDEnd)
|
|
|
|
|
{
|
|
|
|
|
++UUIDStart;
|
|
|
|
|
int32 Length = FMath::Min(64, (int32)((uintptr_t)(UUIDEnd - UUIDStart)));
|
|
|
|
|
OutModule.Report.Append(UUIDStart, Length);
|
2015-03-02 07:34:19 -05:00
|
|
|
if(!OutModule.Report.Contains(TEXT("-")))
|
|
|
|
|
{
|
|
|
|
|
OutModule.Report.InsertAt(8, TEXT('-'));
|
|
|
|
|
OutModule.Report.InsertAt(13, TEXT('-'));
|
|
|
|
|
OutModule.Report.InsertAt(18, TEXT('-'));
|
|
|
|
|
OutModule.Report.InsertAt(23, TEXT('-'));
|
|
|
|
|
}
|
|
|
|
|
OutModule.Report = OutModule.Report.ToUpper();
|
2014-09-04 12:10:15 -04:00
|
|
|
Found++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TCHAR const* Path = FCStringWide::Strchr(ModuleLine, TEXT('/'));
|
|
|
|
|
if(Path)
|
|
|
|
|
{
|
|
|
|
|
TCHAR const* End = FCStringWide::Strchr(Path, TEXT('\r'));
|
|
|
|
|
if(!End)
|
|
|
|
|
{
|
|
|
|
|
End = FCStringWide::Strchr(Path, TEXT('\n'));
|
|
|
|
|
}
|
|
|
|
|
check(End);
|
|
|
|
|
|
|
|
|
|
int32 Length = FMath::Min(PATH_MAX, (int32)((uintptr_t)(End - Path)));
|
|
|
|
|
OutModule.Name.Append(Path, Length);
|
|
|
|
|
Found++;
|
|
|
|
|
bOK = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
|
|
|
|
{
|
|
|
|
|
OutModule.SizeOfImage = (ModuleBase - ModuleEnd);
|
|
|
|
|
}
|
|
|
|
|
case 1:
|
|
|
|
|
{
|
|
|
|
|
OutModule.BaseOfImage = ModuleBase;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return bOK;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
FCrashDebugHelperMac::FCrashDebugHelperMac()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FCrashDebugHelperMac::~FCrashDebugHelperMac()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FCrashDebugHelperMac::ParseCrashDump(const FString& InCrashDumpName, FCrashDebugInfo& OutCrashDebugInfo)
|
|
|
|
|
{
|
2015-03-02 07:34:19 -05:00
|
|
|
SCOPED_AUTORELEASE_POOL;
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
if (bInitialized == false)
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogCrashDebugHelper, Warning, TEXT("ParseCrashDump: CrashDebugHelper not initialized"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-09-04 12:10:15 -04:00
|
|
|
|
|
|
|
|
FString CrashDump;
|
2015-03-02 07:34:19 -05:00
|
|
|
|
|
|
|
|
NSString* CrashDumpPath = InCrashDumpName.GetNSString();
|
|
|
|
|
NSError* Error = nil;
|
|
|
|
|
NSData* Data = [NSData dataWithContentsOfFile: CrashDumpPath options: NSMappedRead error: &Error];
|
|
|
|
|
if(Data && !Error)
|
|
|
|
|
{
|
|
|
|
|
PLCrashReport* CrashLog = [[PLCrashReport alloc] initWithData: Data error: &Error];
|
|
|
|
|
if(CrashLog && !Error)
|
|
|
|
|
{
|
|
|
|
|
NSString* Report = [PLCrashReportTextFormatter stringValueForCrashReport: CrashLog withTextFormat: PLCrashReportTextFormatiOS];
|
|
|
|
|
CrashDump = FString(Report);
|
2015-07-03 10:06:17 -04:00
|
|
|
|
|
|
|
|
// Replace the binary PLCrashReporter file with an easily readable text dump
|
|
|
|
|
FFileHelper::SaveStringToFile(CrashDump, *InCrashDumpName);
|
2015-03-02 07:34:19 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !CrashDump.IsEmpty() || FFileHelper::LoadFileToString( CrashDump, *InCrashDumpName ) )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-09-04 12:10:15 -04:00
|
|
|
// Only supports Apple crash report version 11
|
|
|
|
|
int32 ReportVersion = 0;
|
|
|
|
|
int32 Result = ParseReportVersion(*CrashDump, ReportVersion);
|
2015-03-02 07:34:19 -05:00
|
|
|
if(Result == 1 && (ReportVersion == 11 || ReportVersion == 104))
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
int32 Major = 0;
|
|
|
|
|
int32 Minor = 0;
|
|
|
|
|
int32 Build = 0;
|
|
|
|
|
int32 CLNumber = 0;
|
|
|
|
|
FString Branch;
|
2015-04-22 17:58:53 -04:00
|
|
|
Result = ParseVersion(*CrashDump, Major, Minor, Build, CLNumber, Branch);
|
2014-09-04 12:10:15 -04:00
|
|
|
if(Result >= 3)
|
|
|
|
|
{
|
|
|
|
|
if (Result < 5)
|
|
|
|
|
{
|
|
|
|
|
OutCrashDebugInfo.EngineVersion = Build;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OutCrashDebugInfo.EngineVersion = CLNumber;
|
|
|
|
|
}
|
|
|
|
|
if(Result == 5)
|
|
|
|
|
{
|
|
|
|
|
OutCrashDebugInfo.SourceControlLabel = Branch;
|
|
|
|
|
}
|
|
|
|
|
OutCrashDebugInfo.PlatformName = TEXT("Mac");
|
|
|
|
|
OutCrashDebugInfo.CrashDumpName = InCrashDumpName;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FCrashDebugHelperMac::CreateMinidumpDiagnosticReport( const FString& InCrashDumpName )
|
|
|
|
|
{
|
2014-09-04 12:10:15 -04:00
|
|
|
bool bOK = false;
|
2015-07-03 10:06:17 -04:00
|
|
|
const bool bSyncSymbols = FParse::Param( FCommandLine::Get(), TEXT( "SyncSymbols" ) );
|
|
|
|
|
const bool bAnnotate = FParse::Param( FCommandLine::Get(), TEXT( "Annotate" ) );
|
|
|
|
|
const bool bUseSCC = bSyncSymbols || bAnnotate;
|
|
|
|
|
|
|
|
|
|
if( bUseSCC )
|
|
|
|
|
{
|
|
|
|
|
InitSourceControl( false );
|
|
|
|
|
}
|
2014-09-04 12:10:15 -04:00
|
|
|
|
|
|
|
|
FString CrashDump;
|
2015-03-02 07:34:19 -05:00
|
|
|
|
2015-04-22 17:58:53 -04:00
|
|
|
{
|
|
|
|
|
NSString* CrashDumpPath = InCrashDumpName.GetNSString();
|
|
|
|
|
NSError* Error = nil;
|
|
|
|
|
NSData* Data = [NSData dataWithContentsOfFile: CrashDumpPath options: NSMappedRead error: &Error];
|
|
|
|
|
if(Data && !Error)
|
|
|
|
|
{
|
|
|
|
|
PLCrashReport* CrashLog = [[PLCrashReport alloc] initWithData: Data error: &Error];
|
|
|
|
|
if(CrashLog && !Error)
|
|
|
|
|
{
|
|
|
|
|
NSString* Report = [PLCrashReportTextFormatter stringValueForCrashReport: CrashLog withTextFormat: PLCrashReportTextFormatiOS];
|
2015-07-03 10:06:17 -04:00
|
|
|
CrashDump = FString(Report);
|
|
|
|
|
|
|
|
|
|
// Replace the binary PLCrashReporter file with an easily readable text dump
|
|
|
|
|
FFileHelper::SaveStringToFile(CrashDump, *InCrashDumpName);
|
2015-04-22 17:58:53 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-02 07:34:19 -05:00
|
|
|
|
|
|
|
|
if ( !CrashDump.IsEmpty() || FFileHelper::LoadFileToString( CrashDump, *InCrashDumpName ) )
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
|
|
|
|
int32 ReportVersion = 0;
|
|
|
|
|
int32 Result = ParseReportVersion(*CrashDump, ReportVersion);
|
2015-03-02 07:34:19 -05:00
|
|
|
if(Result == 1 && (ReportVersion == 11 || ReportVersion == 104))
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
2014-09-08 09:01:00 -04:00
|
|
|
FString Error;
|
|
|
|
|
FString ModulePath;
|
|
|
|
|
FString ModuleName;
|
|
|
|
|
FString FunctionName;
|
|
|
|
|
FString FileName;
|
|
|
|
|
FString Branch;
|
|
|
|
|
FString Model;
|
|
|
|
|
FString Gpu;
|
2014-09-04 12:10:15 -04:00
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
uint64 ProgramCounter = 0;
|
|
|
|
|
int32 Major = 0;
|
|
|
|
|
int32 Minor = 0;
|
|
|
|
|
int32 Build = 0;
|
|
|
|
|
int32 CLNumber = 0;
|
|
|
|
|
int32 LineNumber = 0;;
|
|
|
|
|
|
2015-04-22 17:58:53 -04:00
|
|
|
Result = ParseVersion(*CrashDump, Major, Minor, Build, CLNumber, Branch);
|
2014-09-08 09:01:00 -04:00
|
|
|
if(Result >= 3)
|
2014-09-04 12:10:15 -04:00
|
|
|
{
|
2015-03-02 07:52:38 -05:00
|
|
|
CrashInfo.EngineVersion = FEngineVersion(Major, Minor, Build, CLNumber, Branch).ToString();
|
2014-09-04 12:10:15 -04:00
|
|
|
}
|
2014-09-08 09:01:00 -04:00
|
|
|
|
|
|
|
|
if(Result >= 4)
|
|
|
|
|
{
|
2015-01-22 08:03:55 -05:00
|
|
|
CrashInfo.BuiltFromCL = CLNumber;
|
2014-09-08 09:01:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Result == 5 && Branch.Len() > 0)
|
|
|
|
|
{
|
|
|
|
|
CrashInfo.LabelName = Branch;
|
2015-07-03 10:06:17 -04:00
|
|
|
|
|
|
|
|
if( bSyncSymbols )
|
|
|
|
|
{
|
|
|
|
|
FindSymbolsAndBinariesStorage();
|
|
|
|
|
|
2016-09-02 09:58:53 -04:00
|
|
|
bool bPDBCacheEntryValid = false;
|
|
|
|
|
SyncModules(bPDBCacheEntryValid);
|
2015-07-03 10:06:17 -04:00
|
|
|
}
|
2014-09-08 09:01:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result = ParseOS(*CrashDump, CrashInfo.SystemInfo.OSMajor, CrashInfo.SystemInfo.OSMinor, CrashInfo.SystemInfo.OSBuild, CrashInfo.SystemInfo.OSRevision);
|
|
|
|
|
check(Result == 4);
|
|
|
|
|
|
|
|
|
|
CrashInfo.SystemInfo.ProcessorArchitecture = PA_X64;
|
|
|
|
|
|
|
|
|
|
ParseModel(*CrashDump, Model, CrashInfo.SystemInfo.ProcessorCount);
|
|
|
|
|
ParseGraphics(*CrashDump, Gpu);
|
|
|
|
|
CrashInfo.SystemInfo.Report = Model + Gpu;
|
|
|
|
|
|
|
|
|
|
Result = ParseError(*CrashDump, CrashInfo.Exception.ExceptionString);
|
|
|
|
|
check(Result == 1);
|
|
|
|
|
|
|
|
|
|
Result = ParseProcessID(*CrashDump, CrashInfo.Exception.ProcessId);
|
|
|
|
|
check(Result == 1);
|
|
|
|
|
|
|
|
|
|
Result = ParseCrashedThread(*CrashDump, CrashInfo.Exception.ThreadId);
|
|
|
|
|
check(Result == 1);
|
|
|
|
|
|
|
|
|
|
Result = ParseExceptionCode(*CrashDump, CrashInfo.Exception.Code);
|
|
|
|
|
check(Result == 1);
|
|
|
|
|
|
|
|
|
|
FCrashThreadInfo ThreadInfo;
|
|
|
|
|
ThreadInfo.ThreadId = CrashInfo.Exception.ThreadId;
|
|
|
|
|
ThreadInfo.SuspendCount = 0;
|
|
|
|
|
|
|
|
|
|
// Parse modules now for symbolication - if we don't have the running process we need to symbolicate by UUID
|
|
|
|
|
TCHAR const* ModuleLine = FindModules(*CrashDump);
|
|
|
|
|
while(ModuleLine)
|
|
|
|
|
{
|
|
|
|
|
FCrashModuleInfo Module;
|
2015-04-22 17:58:53 -04:00
|
|
|
if (ParseModuleLine(ModuleLine, Module))
|
2014-09-08 09:01:00 -04:00
|
|
|
{
|
|
|
|
|
CrashInfo.Modules.Push(Module);
|
|
|
|
|
CrashInfo.ModuleNames.Push(FPaths::GetBaseFilename(Module.Name));
|
|
|
|
|
|
|
|
|
|
ModuleLine = FCStringWide::Strchr(ModuleLine, TEXT('\n'));
|
|
|
|
|
check(ModuleLine);
|
|
|
|
|
ModuleLine += 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ModuleLine = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 10:21:50 -05:00
|
|
|
|
|
|
|
|
FPlatformSymbolDatabaseSet SymbolCache;
|
2014-09-08 09:25:29 -04:00
|
|
|
|
2015-07-03 10:06:17 -04:00
|
|
|
bool bIsCrashLocation = true;
|
2014-09-08 09:01:00 -04:00
|
|
|
TCHAR const* ThreadStackLine = FindCrashedThreadStack(*CrashDump);
|
2015-12-10 16:56:55 -05:00
|
|
|
uint32 Index = 0;
|
2014-09-08 09:01:00 -04:00
|
|
|
while(ThreadStackLine)
|
|
|
|
|
{
|
2015-12-10 16:56:55 -05:00
|
|
|
if(CrashInfo.Exception.Code == SIGTRAP)
|
|
|
|
|
{
|
|
|
|
|
// For ensures strip the first three lines as they are PLCrashReporter nonsense
|
|
|
|
|
if(Index < 3)
|
|
|
|
|
{
|
|
|
|
|
ThreadStackLine = FCStringWide::Strchr(ThreadStackLine, TEXT('\n'));
|
|
|
|
|
if(ThreadStackLine)
|
|
|
|
|
{
|
|
|
|
|
ThreadStackLine += 1;
|
|
|
|
|
}
|
|
|
|
|
++Index;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Crash location is the 5th entry in the stack.
|
|
|
|
|
bIsCrashLocation = (Index == 5);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
Result = ParseThreadStackLine(ThreadStackLine, ModuleName, ProgramCounter, FunctionName, FileName, LineNumber);
|
|
|
|
|
|
|
|
|
|
// If we got the modulename & program counter but didn't parse the filename & linenumber we can resymbolise
|
|
|
|
|
if(Result > 1 && Result < 4)
|
|
|
|
|
{
|
|
|
|
|
// Attempt to resymbolise using CoreSymbolication
|
2014-09-08 09:25:29 -04:00
|
|
|
Result += SymboliseStackInfo(SymbolCache, CrashInfo.Modules, ModuleName, ProgramCounter, FunctionName, FileName, LineNumber);
|
2014-09-08 09:01:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Output in our format based on the fields we actually have
|
|
|
|
|
switch (Result)
|
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
CrashInfo.Exception.CallStackString.Push( FString::Printf( TEXT( "Unknown() Address = 0x%lx (filename not found) [in %s]" ), ProgramCounter, *ModuleName ) );
|
|
|
|
|
ThreadInfo.CallStack.Push(ProgramCounter);
|
|
|
|
|
|
|
|
|
|
ThreadStackLine = FCStringWide::Strchr(ThreadStackLine, TEXT('\n'));
|
|
|
|
|
check(ThreadStackLine);
|
|
|
|
|
ThreadStackLine += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
case 4:
|
|
|
|
|
CrashInfo.Exception.CallStackString.Push( FString::Printf( TEXT( "%s Address = 0x%lx (filename not found) [in %s]" ), *FunctionName, ProgramCounter, *ModuleName ) );
|
|
|
|
|
ThreadInfo.CallStack.Push(ProgramCounter);
|
|
|
|
|
|
|
|
|
|
ThreadStackLine = FCStringWide::Strchr(ThreadStackLine, TEXT('\n'));
|
|
|
|
|
check(ThreadStackLine);
|
|
|
|
|
ThreadStackLine += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
|
case 6: // Function name might be parsed twice
|
2015-07-03 10:06:17 -04:00
|
|
|
if(bIsCrashLocation)
|
|
|
|
|
{
|
|
|
|
|
if( FileName.Len() > 0 && LineNumber > 0 )
|
|
|
|
|
{
|
|
|
|
|
// Sync the source file where the crash occurred
|
|
|
|
|
CrashInfo.SourceFile = ExtractRelativePath( TEXT( "source" ), *FileName );
|
|
|
|
|
CrashInfo.SourceLineNumber = LineNumber;
|
|
|
|
|
|
|
|
|
|
if( bSyncSymbols && CrashInfo.BuiltFromCL > 0 )
|
|
|
|
|
{
|
|
|
|
|
UE_LOG( LogCrashDebugHelper, Log, TEXT( "Using CL %i to sync crash source file" ), CrashInfo.BuiltFromCL );
|
|
|
|
|
SyncSourceFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to annotate the file if requested
|
|
|
|
|
bool bAnnotationSuccessful = false;
|
|
|
|
|
if( bAnnotate )
|
|
|
|
|
{
|
|
|
|
|
bAnnotationSuccessful = AddAnnotatedSourceToReport();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If annotation is not requested, or failed, add the standard source context
|
|
|
|
|
if( !bAnnotationSuccessful )
|
|
|
|
|
{
|
|
|
|
|
AddSourceToReport();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 09:01:00 -04:00
|
|
|
CrashInfo.Exception.CallStackString.Push( FString::Printf( TEXT( "%s Address = 0x%lx [%s, line %d] [in %s]" ), *FunctionName, ProgramCounter, *FileName, LineNumber, *ModuleName ) );
|
|
|
|
|
ThreadInfo.CallStack.Push(ProgramCounter);
|
|
|
|
|
|
|
|
|
|
ThreadStackLine = FCStringWide::Strchr(ThreadStackLine, TEXT('\n'));
|
|
|
|
|
check(ThreadStackLine);
|
|
|
|
|
ThreadStackLine += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ThreadStackLine = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-07-03 10:06:17 -04:00
|
|
|
|
2015-12-10 16:56:55 -05:00
|
|
|
++Index;
|
2015-07-03 10:06:17 -04:00
|
|
|
bIsCrashLocation = false;
|
2014-09-08 09:01:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CrashInfo.Threads.Push(ThreadInfo);
|
|
|
|
|
|
|
|
|
|
bOK = true;
|
2014-09-04 12:10:15 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-03 10:06:17 -04:00
|
|
|
if( bUseSCC )
|
|
|
|
|
{
|
|
|
|
|
ShutdownSourceControl();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-04 12:10:15 -04:00
|
|
|
return bOK;
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|