Skip to content

Commit eb0b5a5

Browse files
authored
chore: fatal log example (#120)
1 parent 9924106 commit eb0b5a5

File tree

4 files changed

+199
-6
lines changed

4 files changed

+199
-6
lines changed

Content/UI/W_BugSplatDemo.uasset

4.81 KB
Binary file not shown.

README.md

Lines changed: 191 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,207 @@ To create a `.dSYM` file for a macOS build invoke `RunUAT.command` with the `-En
7070

7171
Before attempting to use the BugSplat plugin to capture crashes on Mobile, please ensure you've completed the [iOS](https://docs.unrealengine.com/5.0/en-US/setting-up-an-unreal-engine-project-for-ios/) and [Android](https://docs.unrealengine.com/5.0/en-US/android-support-for-unreal-engine/) quickstart guides.
7272

73-
In order to get function names and line numbers in your iOS crash reports, please make the following changes in the `iOS` section of `Project Settings`.
73+
To enable crash reporting, ensure the `Enable iOS Crash Reporting` and `Enable Android Crash Reporting` options are selected. Also, ensure that `Enable Automatic Symbol Uploads` is checked so that your crash reports contain function names and line numbers.
74+
75+
#### iOS
76+
77+
> [!NOTE]
78+
> The Unreal iOS project's build process includes a `Build Phase called Generate dSYM for archive, and strip`, which executes after the Unreal PostBuildSteps. However, this Build Phase must complete before the `dSYM` file (debug symbols) is generated. Due to this timing, BugSplat cannot upload the `dSYM` immediately during the initial build. Instead, BugSplat will upload the `dSYM` during the next incremental build in Xcode. Alternatively, you can follow the [example](https://github.com/BugSplat-Git/bugsplat-apple/blob/main/Symbol_Upload_Examples/Build-Phase-symbol-upload.sh) in our bugsplat-apple repo to configure a custom Build Phase for symbol uploads.
79+
80+
To get function names and line numbers in your iOS crash reports, please make the following changes in the `iOS` section of `Project Settings`.
7481

7582
| Option | Value |
7683
|--------|-------|
7784
| Generate dSYMs for code debugging and profiling| true |
7885
| Generate dSYMs as a bundle for third-party crash tools | true |
7986
| Support bitcode in shipping | false |
8087

81-
To enable crash reporting, ensure the `Enable iOS Crash Reporting` and `Enable Android Crash Reporting` options are selected. Also ensure that `Enable Automatic Symbol Uploads` is checked so that your crash reports contain function names and line numbers.
82-
8388
Note that sometimes iOS applications won't crash while the USB cable is connected. If this happens, disconnect the USB cable and re-run the application to trigger a crash.
8489

85-
> [!NOTE]
86-
> The Unreal iOS project's build process includes a `Build Phase called Generate dSYM for archive, and strip`, which executes after the Unreal PostBuildSteps. However, this Build Phase must complete before the `dSYM` file (debug symbols) is generated. Due to this timing, BugSplat cannot upload the `dSYM` immediately during the initial build. Instead, BugSplat will upload the `dSYM` during the next incremental build in Xcode. Alternatively, you can follow the [example](https://github.com/BugSplat-Git/bugsplat-apple/blob/main/Symbol_Upload_Examples/Build-Phase-symbol-upload.sh) in our bugsplat-apple repo to configure your a custom Build Phase for symbol uploads.
90+
#### Android
91+
92+
> ![Note]
93+
> Code is aggressively optimized when building for Android. Oftentimes, Unreal's build process optimizes away code that generates simple errors used in testing. To test a null pointer dereference, you can add the `volatile` keyword to work around compiler optimizations.
94+
95+
96+
Fatal Errors on Android raise a `SIGTRAP` and require extra configuration so that they can be reported to BugSplat.
97+
98+
[MyUnrealCrasherErrorOutputDevice.h](https://github.com/BugSplat-Git/my-unreal-crasher/blob/b0a805505a661d6729657bcae724e64dea31484b/Source/MyUnrealCrasher/MyUnrealCrasherAndroidErrorOutputDevice.h)
99+
```cpp
100+
#pragma once
101+
102+
#include "CoreMinimal.h"
103+
#include "Misc/OutputDeviceError.h"
104+
105+
#if PLATFORM_ANDROID
106+
class FMyUnrealCrasherAndroidErrorOutputDevice : public FOutputDeviceError
107+
{
108+
public:
109+
virtual ~FMyUnrealCrasherAndroidErrorOutputDevice() {}
110+
111+
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;
112+
virtual void HandleError() override;
113+
114+
static FOutputDeviceError* GetErrorOutputDevice();
115+
116+
private:
117+
void RequestExit(bool Force, const TCHAR* CallSite);
118+
};
119+
#endif
120+
```
121+
122+
[MyUnrealCrasherErrorOutputDevice.cpp](https://github.com/BugSplat-Git/my-unreal-crasher/blob/b0a805505a661d6729657bcae724e64dea31484b/Source/MyUnrealCrasher/MyUnrealCrasherAndroidErrorOutputDevice.cpp)
123+
```cpp
124+
// Copyright © BugSplat. All rights reserved.
125+
#include "MyUnrealCrasherAndroidErrorOutputDevice.h"
126+
127+
#include "CoreMinimal.h"
128+
#include "CoreGlobals.h"
129+
#include "Misc/OutputDevice.h"
130+
#include "Misc/OutputDeviceHelper.h"
131+
#include "Misc/App.h"
132+
#include "Misc/CoreDelegates.h"
133+
#include "Misc/FeedbackContext.h"
134+
#include "HAL/PlatformMisc.h"
135+
#include "HAL/PlatformCrt.h"
136+
137+
#if PLATFORM_ANDROID
138+
#include "Android/AndroidPlatform.h" // For LogAndroid
139+
140+
FOutputDeviceError* FMyUnrealCrasherAndroidErrorOutputDevice::GetErrorOutputDevice()
141+
{
142+
static FMyUnrealCrasherAndroidErrorOutputDevice ErrorOutputDevice;
143+
return &ErrorOutputDevice;
144+
}
145+
146+
void FMyUnrealCrasherAndroidErrorOutputDevice::Serialize( const TCHAR* Msg, ELogVerbosity::Type Verbosity, const class FName& Category )
147+
{
148+
FPlatformMisc::LowLevelOutputDebugString(*FOutputDeviceHelper::FormatLogLine(Verbosity, Category, Msg, GPrintLogTimes));
149+
150+
static int32 CallCount = 0;
151+
int32 NewCallCount = FPlatformAtomics::InterlockedIncrement(&CallCount);
152+
if(GIsCriticalError == 0 && NewCallCount == 1)
153+
{
154+
// First appError.
155+
GIsCriticalError = 1;
156+
157+
FCString::Strncpy(GErrorExceptionDescription, Msg, UE_ARRAY_COUNT(GErrorExceptionDescription));
158+
}
159+
else
160+
{
161+
UE_LOG(LogAndroid, Error, TEXT("Error reentered: %s"), Msg);
162+
}
163+
164+
if (GIsGuarded)
165+
{
166+
UE_DEBUG_BREAK();
167+
}
168+
else
169+
{
170+
HandleError();
171+
RequestExit(true, TEXT("MyUnrealCrasherAndroidErrorOutputDevice::Serialize.!GIsGuarded"));
172+
}
173+
}
174+
175+
void FMyUnrealCrasherAndroidErrorOutputDevice::HandleError()
176+
{
177+
static int32 CallCount = 0;
178+
int32 NewCallCount = FPlatformAtomics::InterlockedIncrement(&CallCount);
179+
180+
if (NewCallCount != 1)
181+
{
182+
UE_LOG(LogAndroid, Error, TEXT("HandleError re-entered."));
183+
return;
184+
}
185+
186+
GIsGuarded = 0;
187+
GIsRunning = 0;
188+
GIsCriticalError = 1;
189+
GLogConsole = NULL;
190+
GErrorHist[UE_ARRAY_COUNT(GErrorHist) - 1] = 0;
191+
192+
// Dump the error and flush the log.
193+
#if !NO_LOGGING
194+
FDebug::LogFormattedMessageWithCallstack(LogAndroid.GetCategoryName(), __FILE__, __LINE__, TEXT("=== Critical error: ==="), GErrorHist, ELogVerbosity::Error);
195+
#endif
196+
197+
GLog->Panic();
198+
199+
FCoreDelegates::OnHandleSystemError.Broadcast();
200+
FCoreDelegates::OnShutdownAfterError.Broadcast();
201+
}
202+
203+
204+
void FMyUnrealCrasherAndroidErrorOutputDevice::RequestExit( bool Force, const TCHAR* CallSite)
205+
{
206+
207+
#if PLATFORM_COMPILER_OPTIMIZATION_PG_PROFILING
208+
// Write the PGO profiling file on a clean shutdown.
209+
extern void PGO_WriteFile();
210+
if (!GIsCriticalError)
211+
{
212+
PGO_WriteFile();
213+
// exit now to avoid a possible second PGO write when AndroidMain exits.
214+
Force = true;
215+
}
216+
#endif
217+
218+
UE_LOG(LogAndroid, Log, TEXT("FMyUnrealCrasherAndroidErrorOutputDevice::RequestExit(%i, %s)"), Force,
219+
CallSite ? CallSite : TEXT("<NoCallSiteInfo>"));
220+
if (GLog)
221+
{
222+
GLog->Flush();
223+
}
224+
225+
if (Force)
226+
{
227+
abort(); // Abort to trigger a crash report
228+
}
229+
else
230+
{
231+
RequestEngineExit(TEXT("Android RequestExitWithCrashReporting")); // Called regardless in our version to set up the crash context
232+
}
233+
}
234+
#endif
235+
```
236+
[MyUnrealCrasherGameInstance.h](https://github.com/BugSplat-Git/my-unreal-crasher/blob/b0a805505a661d6729657bcae724e64dea31484b/Source/MyUnrealCrasher/MyUnrealCrasherGameInstance.h)
237+
```cpp
238+
#pragma once
239+
240+
#include "CoreMinimal.h"
241+
#include "Engine/GameInstance.h"
242+
#include "MyUnrealCrasherGameInstance.generated.h"
87243
244+
UCLASS()
245+
class MYUNREALCRASHER_API UMyUnrealCrasherGameInstance : public UGameInstance
246+
{
247+
GENERATED_BODY()
248+
249+
public:
250+
virtual void Init() override;
251+
};
252+
```
253+
254+
[MyUnrealCrasherGameInstance.cpp](https://github.com/BugSplat-Git/my-unreal-crasher/blob/b0a805505a661d6729657bcae724e64dea31484b/Source/MyUnrealCrasher/MyUnrealCrasherGameInstance.cpp)
255+
```cpp
256+
// Copyright © BugSplat. All rights reserved.
257+
#include "MyUnrealCrasherGameInstance.h"
258+
#include "MyUnrealCrasherAndroidErrorOutputDevice.h"
259+
260+
#if PLATFORM_ANDROID
261+
#include <android/log.h>
262+
#endif
263+
264+
void UMyUnrealCrasherGameInstance::Init()
265+
{
266+
Super::Init();
267+
UE_LOG(LogTemp, Log, TEXT("MyUnrealCrasherGameInstance::Init - Setting custom error output device"));
268+
269+
#if PLATFORM_ANDROID
270+
GError = FMyUnrealCrasherAndroidErrorOutputDevice::GetErrorOutputDevice();
271+
#endif
272+
}
273+
```
88274

89275
### Xbox and PlayStation
90276

Source/BugSplatRuntime/Private/BugSplatUtils.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// Copyright BugSplat. All Rights Reserved.
2-
32
#include "BugSplatUtils.h"
43

54
void UBugSplatUtils::GenerateCrash()
@@ -21,4 +20,9 @@ void UBugSplatUtils::GenerateEnsure()
2120
UE_LOG(LogTemp, Log, TEXT("BugSplat: GenerateEnsure"));
2221
char* ptr = nullptr;
2322
ensure(ptr != nullptr);
23+
}
24+
25+
void UBugSplatUtils::GenerateFatalLog()
26+
{
27+
UE_LOG(LogTemp, Fatal, TEXT("BugSplat: GenerateFatalLog"));
2428
}

Source/BugSplatRuntime/Public/BugSplatUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@ class BUGSPLATRUNTIME_API UBugSplatUtils : public UBlueprintFunctionLibrary
2020

2121
UFUNCTION(BlueprintCallable, Category = "BugSplat")
2222
static void GenerateEnsure();
23+
24+
UFUNCTION(BlueprintCallable, Category = "BugSplat")
25+
static void GenerateFatalLog();
2326
};

0 commit comments

Comments
 (0)