644 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			644 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright (c) 2022 Sentry. All Rights Reserved.
 | 
						|
 | 
						|
#include "SentrySubsystemDesktop.h"
 | 
						|
#include "SentryEventDesktop.h"
 | 
						|
#include "SentryBreadcrumbDesktop.h"
 | 
						|
#include "SentryUserDesktop.h"
 | 
						|
#include "SentryUserFeedbackDesktop.h"
 | 
						|
#include "SentryScopeDesktop.h"
 | 
						|
#include "SentryTransactionDesktop.h"
 | 
						|
#include "SentryTransactionContextDesktop.h"
 | 
						|
#include "SentryIdDesktop.h"
 | 
						|
 | 
						|
#include "SentryDefines.h"
 | 
						|
#include "SentrySettings.h"
 | 
						|
#include "SentryEvent.h"
 | 
						|
#include "SentryModule.h"
 | 
						|
#include "SentryBeforeSendHandler.h"
 | 
						|
 | 
						|
#include "SentryTraceSampler.h"
 | 
						|
 | 
						|
#include "Utils/SentryFileUtils.h"
 | 
						|
#include "Utils/SentryLogUtils.h"
 | 
						|
#include "Utils/SentryScreenshotUtils.h"
 | 
						|
 | 
						|
#include "Infrastructure/SentryConvertorsDesktop.h"
 | 
						|
 | 
						|
#include "CrashReporter/SentryCrashReporter.h"
 | 
						|
#include "CrashReporter/SentryCrashContext.h"
 | 
						|
 | 
						|
#include "Transport/SentryTransport.h"
 | 
						|
 | 
						|
#include "Misc/Paths.h"
 | 
						|
#include "Misc/ScopeLock.h"
 | 
						|
#include "Misc/CoreDelegates.h"
 | 
						|
#include "HAL/FileManager.h"
 | 
						|
#include "Misc/EngineVersionComparison.h"
 | 
						|
#include "GenericPlatform/GenericPlatformOutputDevices.h"
 | 
						|
#include "GenericPlatform/GenericPlatformCrashContext.h"
 | 
						|
#include "HAL/ExceptionHandling.h"
 | 
						|
#include "UObject/GarbageCollection.h"
 | 
						|
#include "UObject/UObjectThreadContext.h"
 | 
						|
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
#include "Windows/WindowsPlatformMisc.h"
 | 
						|
#include "Windows/WindowsPlatformCrashContext.h"
 | 
						|
#endif
 | 
						|
 | 
						|
extern CORE_API bool GIsGPUCrashed;
 | 
						|
 | 
						|
#if USE_SENTRY_NATIVE
 | 
						|
 | 
						|
void PrintVerboseLog(sentry_level_t level, const char *message, va_list args, void *userdata)
 | 
						|
{
 | 
						|
	char buffer[512];
 | 
						|
	vsnprintf(buffer, 512, message, args);
 | 
						|
 | 
						|
	FString MessageBuf = FString(buffer);
 | 
						|
 | 
						|
	// The WER (Windows Error Reporting) module (crashpad_wer.dll) can't be distributed along with other Sentry binaries
 | 
						|
	// within the plugin package due to some UE Marketplace restrictions. Its absence doesn't affect crash capturing
 | 
						|
	// and the corresponding warning can be disregarded
 | 
						|
	if(MessageBuf.Equals(TEXT("crashpad WER handler module not found")))
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
#if !NO_LOGGING
 | 
						|
	const FName SentryCategoryName(LogSentrySdk.GetCategoryName());
 | 
						|
#else
 | 
						|
	const FName SentryCategoryName(TEXT("LogSentrySdk"));
 | 
						|
#endif
 | 
						|
 | 
						|
	GLog->CategorizedLogf(SentryCategoryName, SentryConvertorsDesktop::SentryLevelToLogVerbosity(level), TEXT("%s"), *MessageBuf);
 | 
						|
}
 | 
						|
 | 
						|
void PrintCrashLog(const sentry_ucontext_t *uctx)
 | 
						|
{
 | 
						|
#if PLATFORM_WINDOWS && !UE_VERSION_OLDER_THAN(5, 0, 0)
 | 
						|
 | 
						|
	SentryConvertorsDesktop::SentryCrashContextToString(uctx, GErrorExceptionDescription, UE_ARRAY_COUNT(GErrorExceptionDescription));
 | 
						|
 | 
						|
	const SIZE_T StackTraceSize = 65535;
 | 
						|
	ANSICHAR* StackTrace = (ANSICHAR*)GMalloc->Malloc(StackTraceSize);
 | 
						|
	StackTrace[0] = 0;
 | 
						|
 | 
						|
	// Currently raw crash data stored in `uctx` can be utilized for stalk walking on Windows only
 | 
						|
	void* ProgramCounter = uctx->exception_ptrs.ExceptionRecord->ExceptionAddress;
 | 
						|
 | 
						|
	FPlatformStackWalk::StackWalkAndDump(StackTrace, StackTraceSize, ProgramCounter);
 | 
						|
 | 
						|
	FCString::Strncat(GErrorHist, GErrorExceptionDescription, UE_ARRAY_COUNT(GErrorHist));
 | 
						|
	FCString::Strncat(GErrorHist, TEXT("\r\n\r\n"), UE_ARRAY_COUNT(GErrorHist));
 | 
						|
	FCString::Strncat(GErrorHist, ANSI_TO_TCHAR(StackTrace), UE_ARRAY_COUNT(GErrorHist));
 | 
						|
 | 
						|
#if !NO_LOGGING
 | 
						|
	FDebug::LogFormattedMessageWithCallstack(LogSentrySdk.GetCategoryName(), __FILE__, __LINE__, TEXT("=== Critical error: ==="), GErrorHist, ELogVerbosity::Error);
 | 
						|
#endif
 | 
						|
 | 
						|
#if !UE_VERSION_OLDER_THAN(5, 1, 0)
 | 
						|
	GLog->Panic();
 | 
						|
#endif
 | 
						|
 | 
						|
	GMalloc->Free(StackTrace);
 | 
						|
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
sentry_value_t HandleBeforeSend(sentry_value_t event, void *hint, void *closure)
 | 
						|
{
 | 
						|
	SentrySubsystemDesktop* SentrySubsystem = static_cast<SentrySubsystemDesktop*>(closure);
 | 
						|
 | 
						|
	TSharedPtr<SentryEventDesktop> eventDesktop = MakeShareable(new SentryEventDesktop(event));
 | 
						|
 | 
						|
	SentrySubsystem->GetCurrentScope()->Apply(eventDesktop);
 | 
						|
 | 
						|
	FGCScopeGuard GCScopeGuard;
 | 
						|
 | 
						|
	USentryEvent* EventToProcess = NewObject<USentryEvent>();
 | 
						|
	EventToProcess->InitWithNativeImpl(eventDesktop);
 | 
						|
 | 
						|
	USentryEvent* ProcessedEvent = EventToProcess;
 | 
						|
	if(!FUObjectThreadContext::Get().IsRoutingPostLoad)
 | 
						|
	{
 | 
						|
		// Executing UFUNCTION is allowed only when not post-loading
 | 
						|
		ProcessedEvent = SentrySubsystem->GetBeforeSendHandler()->HandleBeforeSend(EventToProcess, nullptr);
 | 
						|
	}
 | 
						|
 | 
						|
	return ProcessedEvent ? event : sentry_value_new_null();
 | 
						|
}
 | 
						|
 | 
						|
sentry_value_t HandleBeforeCrash(const sentry_ucontext_t *uctx, sentry_value_t event, void *closure)
 | 
						|
{
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
	// Ensures that error message and corresponding callstack flushed to a log file (if available)
 | 
						|
	// before it's attached to the captured crash event and uploaded to Sentry.
 | 
						|
	PrintCrashLog(uctx);
 | 
						|
#endif
 | 
						|
 | 
						|
	SentrySubsystemDesktop* SentrySubsystem = static_cast<SentrySubsystemDesktop*>(closure);
 | 
						|
	SentrySubsystem->TryCaptureScreenshot();
 | 
						|
 | 
						|
	if (GIsGPUCrashed)
 | 
						|
	{
 | 
						|
		IFileManager::Get().Copy(*SentrySubsystem->GetGpuDumpBackupPath(), *SentryFileUtils::GetGpuDumpPath());
 | 
						|
	}
 | 
						|
 | 
						|
	FSentryCrashContext::Get()->Apply(SentrySubsystem->GetCurrentScope());
 | 
						|
 | 
						|
	TSharedPtr<SentryEventDesktop> eventDesktop = MakeShareable(new SentryEventDesktop(event, true));
 | 
						|
 | 
						|
	SentrySubsystem->GetCurrentScope()->Apply(eventDesktop);
 | 
						|
 | 
						|
	if(!IsGarbageCollecting())
 | 
						|
	{
 | 
						|
		USentryEvent* EventToProcess = NewObject<USentryEvent>();
 | 
						|
		EventToProcess->InitWithNativeImpl(eventDesktop);
 | 
						|
 | 
						|
		USentryEvent* ProcessedEvent = EventToProcess;
 | 
						|
		if(!FUObjectThreadContext::Get().IsRoutingPostLoad)
 | 
						|
		{
 | 
						|
			// Executing UFUNCTION is allowed only when not post-loading
 | 
						|
			ProcessedEvent = SentrySubsystem->GetBeforeSendHandler()->HandleBeforeSend(EventToProcess, nullptr);
 | 
						|
		}
 | 
						|
 | 
						|
		return ProcessedEvent ? event : sentry_value_new_null();
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		// If crash occured during garbage collection we can't just obtain a GC lock like with normal events
 | 
						|
		// since there is no guarantee it will be ever freed. In this case crash event will be reported
 | 
						|
		// without calling a `beforeSend` handler.
 | 
						|
		return event;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
SentrySubsystemDesktop::SentrySubsystemDesktop()
 | 
						|
	: beforeSend(nullptr)
 | 
						|
	, crashReporter(MakeShareable(new SentryCrashReporter))
 | 
						|
	, isEnabled(false)
 | 
						|
	, isStackTraceEnabled(true)
 | 
						|
	, isScreenshotAttachmentEnabled(false)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::InitWithSettings(const USentrySettings* settings, USentryBeforeSendHandler* beforeSendHandler, USentryTraceSampler* traceSampler)
 | 
						|
{
 | 
						|
	beforeSend = beforeSendHandler;
 | 
						|
 | 
						|
	scopeStack.Push(MakeShareable(new SentryScopeDesktop()));
 | 
						|
 | 
						|
	sentry_options_t* options = sentry_options_new();
 | 
						|
 | 
						|
	if(settings->EnableAutoLogAttachment)
 | 
						|
	{
 | 
						|
		const FString LogFilePath = FGenericPlatformOutputDevices::GetAbsoluteLogFilename();
 | 
						|
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
		sentry_options_add_attachmentw(options, *FPaths::ConvertRelativePathToFull(LogFilePath));
 | 
						|
#elif PLATFORM_LINUX
 | 
						|
		sentry_options_add_attachment(options, TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(LogFilePath)));
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	switch (settings->DatabaseLocation)
 | 
						|
	{
 | 
						|
	case ESentryDatabaseLocation::ProjectDirectory:
 | 
						|
		databaseParentPath = FPaths::ProjectDir();
 | 
						|
		break;
 | 
						|
	case ESentryDatabaseLocation::ProjectUserDirectory:
 | 
						|
		databaseParentPath = FPaths::ProjectUserDir();
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if(databaseParentPath.IsEmpty())
 | 
						|
	{
 | 
						|
		UE_LOG(LogSentrySdk, Warning, TEXT("Unknown Sentry database location. Falling back to FPaths::ProjectUserDir()."));
 | 
						|
		databaseParentPath = FPaths::ProjectUserDir();
 | 
						|
	}
 | 
						|
 | 
						|
	if(settings->AttachScreenshot)
 | 
						|
	{
 | 
						|
		isScreenshotAttachmentEnabled = true;
 | 
						|
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
		sentry_options_add_attachmentw(options, *GetScreenshotPath());
 | 
						|
#elif PLATFORM_LINUX
 | 
						|
		sentry_options_add_attachment(options, TCHAR_TO_UTF8(*GetScreenshotPath()));
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	if (settings->AttachGpuDump)
 | 
						|
	{
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
		sentry_options_add_attachmentw(options, *GetGpuDumpBackupPath());
 | 
						|
#elif PLATFORM_LINUX
 | 
						|
		sentry_options_add_attachment(options, TCHAR_TO_UTF8(*GetGpuDumpBackupPath()));
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	if(settings->UseProxy)
 | 
						|
	{
 | 
						|
		sentry_options_set_http_proxy(options, TCHAR_TO_ANSI(*settings->ProxyUrl));
 | 
						|
	}
 | 
						|
 | 
						|
	if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::UniformSampleRate)
 | 
						|
	{
 | 
						|
		sentry_options_set_traces_sample_rate(options, settings->TracesSampleRate);
 | 
						|
	}
 | 
						|
	if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler)
 | 
						|
	{
 | 
						|
		UE_LOG(LogSentrySdk, Warning, TEXT("The Native SDK doesn't currently support sampling functions"));
 | 
						|
	}
 | 
						|
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
	if(!FSentryModule::Get().IsMarketplaceVersion())
 | 
						|
	{
 | 
						|
		const FString HandlerPath = GetHandlerPath();
 | 
						|
 | 
						|
		if(!FPaths::FileExists(HandlerPath))
 | 
						|
		{
 | 
						|
			UE_LOG(LogSentrySdk, Log, TEXT("Crashpad executable couldn't be found so Breakpad will be used instead. "
 | 
						|
				"Please make sure that the plugin was rebuilt to avoid initialization failure."));
 | 
						|
		}
 | 
						|
 | 
						|
		sentry_options_set_handler_pathw(options, *HandlerPath);
 | 
						|
	}
 | 
						|
#elif PLATFORM_LINUX
 | 
						|
	sentry_options_set_handler_path(options, TCHAR_TO_UTF8(*GetHandlerPath()));
 | 
						|
#endif
 | 
						|
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
	sentry_options_set_database_pathw(options, *GetDatabasePath());
 | 
						|
#elif PLATFORM_LINUX
 | 
						|
	sentry_options_set_database_path(options, TCHAR_TO_UTF8(*GetDatabasePath()));
 | 
						|
#endif
 | 
						|
 | 
						|
	sentry_options_set_release(options, TCHAR_TO_ANSI(settings->OverrideReleaseName
 | 
						|
		? *settings->Release
 | 
						|
		: *settings->GetFormattedReleaseName()));
 | 
						|
 | 
						|
	sentry_options_set_dsn(options, TCHAR_TO_ANSI(*settings->Dsn));
 | 
						|
	sentry_options_set_environment(options, TCHAR_TO_ANSI(*settings->Environment));
 | 
						|
	sentry_options_set_logger(options, PrintVerboseLog, nullptr);
 | 
						|
	sentry_options_set_debug(options, settings->Debug);
 | 
						|
	sentry_options_set_auto_session_tracking(options, settings->EnableAutoSessionTracking);
 | 
						|
	sentry_options_set_sample_rate(options, settings->SampleRate);
 | 
						|
	sentry_options_set_max_breadcrumbs(options, settings->MaxBreadcrumbs);
 | 
						|
	sentry_options_set_before_send(options, HandleBeforeSend, this);
 | 
						|
	sentry_options_set_on_crash(options, HandleBeforeCrash, this);
 | 
						|
	sentry_options_set_shutdown_timeout(options, 3000);
 | 
						|
 | 
						|
#if PLATFORM_LINUX
 | 
						|
	sentry_options_set_transport(options, FSentryTransport::Create());
 | 
						|
#endif
 | 
						|
 | 
						|
	int initResult = sentry_init(options);
 | 
						|
 | 
						|
	UE_LOG(LogSentrySdk, Log, TEXT("Sentry initialization completed with result %d (0 on success)."), initResult);
 | 
						|
 | 
						|
	isEnabled = initResult == 0 ? true : false;
 | 
						|
 | 
						|
	sentry_clear_crashed_last_run();
 | 
						|
 | 
						|
#if PLATFORM_WINDOWS && !UE_VERSION_OLDER_THAN(5, 2, 0)
 | 
						|
	FPlatformMisc::SetCrashHandlingType(settings->EnableAutoCrashCapturing
 | 
						|
		? ECrashHandlingType::Disabled
 | 
						|
		: ECrashHandlingType::Default);
 | 
						|
#endif
 | 
						|
 | 
						|
	isStackTraceEnabled = settings->AttachStacktrace;
 | 
						|
 | 
						|
	crashReporter->SetRelease(settings->Release);
 | 
						|
	crashReporter->SetEnvironment(settings->Environment);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::Close()
 | 
						|
{
 | 
						|
	isEnabled = false;
 | 
						|
 | 
						|
	sentry_close();
 | 
						|
 | 
						|
	scopeStack.Empty();
 | 
						|
}
 | 
						|
 | 
						|
bool SentrySubsystemDesktop::IsEnabled()
 | 
						|
{
 | 
						|
	return isEnabled;
 | 
						|
}
 | 
						|
 | 
						|
ESentryCrashedLastRun SentrySubsystemDesktop::IsCrashedLastRun()
 | 
						|
{
 | 
						|
	ESentryCrashedLastRun unrealIsCrashed = ESentryCrashedLastRun::NotEvaluated;
 | 
						|
 | 
						|
	switch (sentry_get_crashed_last_run())
 | 
						|
	{
 | 
						|
	case -1:
 | 
						|
		unrealIsCrashed = ESentryCrashedLastRun::NotEvaluated;
 | 
						|
		break;
 | 
						|
	case 0:
 | 
						|
		unrealIsCrashed = ESentryCrashedLastRun::NotCrashed;
 | 
						|
		break;
 | 
						|
	case 1:
 | 
						|
		unrealIsCrashed = ESentryCrashedLastRun::Crashed;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		UE_LOG(LogSentrySdk, Warning, TEXT("Unknown IsCrashedLastRun result. NotEvaluated will be returned."));
 | 
						|
	}
 | 
						|
 | 
						|
	return unrealIsCrashed;
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::AddBreadcrumb(TSharedPtr<ISentryBreadcrumb> breadcrumb)
 | 
						|
{
 | 
						|
	GetCurrentScope()->AddBreadcrumb(breadcrumb);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::AddBreadcrumbWithParams(const FString& Message, const FString& Category, const FString& Type, const TMap<FString, FString>& Data, ESentryLevel Level)
 | 
						|
{
 | 
						|
	TSharedPtr<SentryBreadcrumbDesktop> breadcrumbDesktop = MakeShareable(new SentryBreadcrumbDesktop());
 | 
						|
	breadcrumbDesktop->SetMessage(Message);
 | 
						|
	breadcrumbDesktop->SetCategory(Category);
 | 
						|
	breadcrumbDesktop->SetType(Type);
 | 
						|
	breadcrumbDesktop->SetData(Data);
 | 
						|
	breadcrumbDesktop->SetLevel(Level);
 | 
						|
 | 
						|
	GetCurrentScope()->AddBreadcrumb(breadcrumbDesktop);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::ClearBreadcrumbs()
 | 
						|
{
 | 
						|
	GetCurrentScope()->ClearBreadcrumbs();
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureMessage(const FString& message, ESentryLevel level)
 | 
						|
{
 | 
						|
	sentry_value_t sentryEvent = sentry_value_new_message_event(SentryConvertorsDesktop::SentryLevelToNative(level), nullptr, TCHAR_TO_UTF8(*message));
 | 
						|
 | 
						|
	if(isStackTraceEnabled)
 | 
						|
	{
 | 
						|
		sentry_value_set_stacktrace(sentryEvent, nullptr, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	sentry_uuid_t id = sentry_capture_event(sentryEvent);
 | 
						|
	return MakeShareable(new SentryIdDesktop(id));
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureMessageWithScope(const FString& message, const FSentryScopeDelegate& onScopeConfigure, ESentryLevel level)
 | 
						|
{
 | 
						|
	FScopeLock Lock(&CriticalSection);
 | 
						|
 | 
						|
	TSharedPtr<SentryScopeDesktop> NewLocalScope = MakeShareable(new SentryScopeDesktop(*GetCurrentScope()));
 | 
						|
 | 
						|
	onScopeConfigure.ExecuteIfBound(NewLocalScope);
 | 
						|
 | 
						|
	scopeStack.Push(NewLocalScope);
 | 
						|
	TSharedPtr<ISentryId> Id = CaptureMessage(message, level);
 | 
						|
	scopeStack.Pop();
 | 
						|
 | 
						|
	return Id;
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureEvent(TSharedPtr<ISentryEvent> event)
 | 
						|
{
 | 
						|
	TSharedPtr<SentryEventDesktop> eventDesktop = StaticCastSharedPtr<SentryEventDesktop>(event);
 | 
						|
 | 
						|
	sentry_value_t nativeEvent = eventDesktop->GetNativeObject();
 | 
						|
 | 
						|
	if(isStackTraceEnabled)
 | 
						|
	{
 | 
						|
		sentry_value_set_stacktrace(nativeEvent, nullptr, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	sentry_uuid_t id = sentry_capture_event(nativeEvent);
 | 
						|
	return MakeShareable(new SentryIdDesktop(id));
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureEventWithScope(TSharedPtr<ISentryEvent> event, const FSentryScopeDelegate& onScopeConfigure)
 | 
						|
{
 | 
						|
	FScopeLock Lock(&CriticalSection);
 | 
						|
 | 
						|
	TSharedPtr<SentryScopeDesktop> NewLocalScope = MakeShareable(new SentryScopeDesktop(*GetCurrentScope()));
 | 
						|
 | 
						|
	onScopeConfigure.ExecuteIfBound(NewLocalScope);
 | 
						|
 | 
						|
	scopeStack.Push(NewLocalScope);
 | 
						|
	TSharedPtr<ISentryId> Id = CaptureEvent(event);
 | 
						|
	scopeStack.Pop();
 | 
						|
 | 
						|
	return Id;
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureException(const FString& type, const FString& message, int32 framesToSkip)
 | 
						|
{
 | 
						|
	sentry_value_t exceptionEvent = sentry_value_new_event();
 | 
						|
 | 
						|
	auto StackFrames = FGenericPlatformStackWalk::GetStack(framesToSkip);
 | 
						|
	sentry_value_set_by_key(exceptionEvent, "stacktrace", SentryConvertorsDesktop::CallstackToNative(StackFrames));
 | 
						|
 | 
						|
	sentry_value_t nativeException = sentry_value_new_exception(TCHAR_TO_ANSI(*type), TCHAR_TO_ANSI(*message));
 | 
						|
	sentry_event_add_exception(exceptionEvent, nativeException);
 | 
						|
 | 
						|
	sentry_uuid_t id = sentry_capture_event(exceptionEvent);
 | 
						|
	return MakeShareable(new SentryIdDesktop(id));
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureAssertion(const FString& type, const FString& message)
 | 
						|
{
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
	int32 framesToSkip = 7;
 | 
						|
#else
 | 
						|
	int32 framesToSkip = 5;
 | 
						|
#endif
 | 
						|
 | 
						|
	SentryLogUtils::LogStackTrace(*message, ELogVerbosity::Error, framesToSkip);
 | 
						|
 | 
						|
	return CaptureException(type, message, framesToSkip);
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryId> SentrySubsystemDesktop::CaptureEnsure(const FString& type, const FString& message)
 | 
						|
{
 | 
						|
#if PLATFORM_WINDOWS && !UE_VERSION_OLDER_THAN(5, 3, 0)
 | 
						|
	int32 framesToSkip = 8;
 | 
						|
#else
 | 
						|
	int32 framesToSkip = 7;
 | 
						|
#endif
 | 
						|
	return CaptureException(type, message, framesToSkip);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::CaptureUserFeedback(TSharedPtr<ISentryUserFeedback> userFeedback)
 | 
						|
{
 | 
						|
	TSharedPtr<SentryUserFeedbackDesktop> userFeedbackDesktop = StaticCastSharedPtr<SentryUserFeedbackDesktop>(userFeedback);
 | 
						|
	sentry_capture_user_feedback(userFeedbackDesktop->GetNativeObject());
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::SetUser(TSharedPtr<ISentryUser> user)
 | 
						|
{
 | 
						|
	TSharedPtr<SentryUserDesktop> userDesktop = StaticCastSharedPtr<SentryUserDesktop>(user);
 | 
						|
	sentry_set_user(userDesktop->GetNativeObject());
 | 
						|
 | 
						|
	crashReporter->SetUser(userDesktop);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::RemoveUser()
 | 
						|
{
 | 
						|
	sentry_remove_user();
 | 
						|
 | 
						|
	crashReporter->RemoveUser();
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::ConfigureScope(const FSentryScopeDelegate& onConfigureScope)
 | 
						|
{
 | 
						|
	onConfigureScope.ExecuteIfBound(GetCurrentScope());
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::SetContext(const FString& key, const TMap<FString, FString>& values)
 | 
						|
{
 | 
						|
	GetCurrentScope()->SetContext(key, values);
 | 
						|
 | 
						|
	crashReporter->SetContext(key, values);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::SetTag(const FString& key, const FString& value)
 | 
						|
{
 | 
						|
	GetCurrentScope()->SetTagValue(key, value);
 | 
						|
 | 
						|
	crashReporter->SetTag(key, value);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::RemoveTag(const FString& key)
 | 
						|
{
 | 
						|
	GetCurrentScope()->RemoveTag(key);
 | 
						|
 | 
						|
	crashReporter->RemoveTag(key);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::SetLevel(ESentryLevel level)
 | 
						|
{
 | 
						|
	GetCurrentScope()->SetLevel(level);
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::StartSession()
 | 
						|
{
 | 
						|
	sentry_start_session();
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::EndSession()
 | 
						|
{
 | 
						|
	sentry_end_session();
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryTransaction> SentrySubsystemDesktop::StartTransaction(const FString& name, const FString& operation)
 | 
						|
{
 | 
						|
	sentry_transaction_context_t* transactionContext = sentry_transaction_context_new(TCHAR_TO_ANSI(*name), TCHAR_TO_ANSI(*operation));
 | 
						|
 | 
						|
	sentry_transaction_t* nativeTransaction = sentry_transaction_start(transactionContext, sentry_value_new_null());
 | 
						|
 | 
						|
	return MakeShareable(new SentryTransactionDesktop(nativeTransaction));
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryTransaction> SentrySubsystemDesktop::StartTransactionWithContext(TSharedPtr<ISentryTransactionContext> context)
 | 
						|
{
 | 
						|
	TSharedPtr<SentryTransactionContextDesktop> transactionContextDesktop = StaticCastSharedPtr<SentryTransactionContextDesktop>(context);
 | 
						|
 | 
						|
	sentry_transaction_t* nativeTransaction = sentry_transaction_start(transactionContextDesktop->GetNativeObject(), sentry_value_new_null());
 | 
						|
 | 
						|
	return MakeShareable(new SentryTransactionDesktop(nativeTransaction));
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryTransaction> SentrySubsystemDesktop::StartTransactionWithContextAndTimestamp(TSharedPtr<ISentryTransactionContext> context, int64 timestamp)
 | 
						|
{
 | 
						|
	TSharedPtr<SentryTransactionContextDesktop> transactionContextDesktop = StaticCastSharedPtr<SentryTransactionContextDesktop>(context);
 | 
						|
 | 
						|
	sentry_transaction_t* nativeTransaction = sentry_transaction_start_ts(transactionContextDesktop->GetNativeObject(), sentry_value_new_null(), timestamp);
 | 
						|
 | 
						|
	return MakeShareable(new SentryTransactionDesktop(nativeTransaction));
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryTransaction> SentrySubsystemDesktop::StartTransactionWithContextAndOptions(TSharedPtr<ISentryTransactionContext> context, const TMap<FString, FString>& options)
 | 
						|
{
 | 
						|
	UE_LOG(LogSentrySdk, Log, TEXT("Transaction options currently not supported on desktop."));
 | 
						|
	return StartTransactionWithContext(context);
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<ISentryTransactionContext> SentrySubsystemDesktop::ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders)
 | 
						|
{
 | 
						|
	sentry_transaction_context_t* nativeTransactionContext = sentry_transaction_context_new("<unlabeled transaction>", "default");
 | 
						|
	sentry_transaction_context_update_from_header(nativeTransactionContext, "sentry-trace", TCHAR_TO_ANSI(*sentryTrace));
 | 
						|
 | 
						|
	// currently `sentry-native` doesn't have API for `sentry_transaction_context_t` to set `baggageHeaders`
 | 
						|
 | 
						|
	TSharedPtr<SentryTransactionContextDesktop> transactionContextDesktop = MakeShareable(new SentryTransactionContextDesktop(nativeTransactionContext));
 | 
						|
 | 
						|
	return transactionContextDesktop;
 | 
						|
}
 | 
						|
 | 
						|
USentryBeforeSendHandler* SentrySubsystemDesktop::GetBeforeSendHandler()
 | 
						|
{
 | 
						|
	return beforeSend;
 | 
						|
}
 | 
						|
 | 
						|
void SentrySubsystemDesktop::TryCaptureScreenshot() const
 | 
						|
{
 | 
						|
	if(!isScreenshotAttachmentEnabled)
 | 
						|
	{
 | 
						|
		UE_LOG(LogSentrySdk, Log, TEXT("Screenshot attachment is disabled in plugin settings."));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	SentryScreenshotUtils::CaptureScreenshot(GetScreenshotPath());
 | 
						|
}
 | 
						|
 | 
						|
FString SentrySubsystemDesktop::GetGpuDumpBackupPath() const
 | 
						|
{
 | 
						|
	static const FString DateTimeString = FDateTime::Now().ToString();
 | 
						|
 | 
						|
	const FString GpuDumpPath = FPaths::Combine(GetDatabasePath(), TEXT("gpudumps"), *FString::Printf(TEXT("UEAftermath-%s.nv-gpudmp"), *DateTimeString));;
 | 
						|
	const FString GpuDumpFullPath = FPaths::ConvertRelativePathToFull(GpuDumpPath);
 | 
						|
 | 
						|
	return GpuDumpFullPath;
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<SentryScopeDesktop> SentrySubsystemDesktop::GetCurrentScope()
 | 
						|
{
 | 
						|
	if(scopeStack.Num() == 0)
 | 
						|
	{
 | 
						|
		UE_LOG(LogSentrySdk, Warning, TEXT("Scope stack is empty."));
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	return scopeStack.Top();
 | 
						|
}
 | 
						|
 | 
						|
FString SentrySubsystemDesktop::GetHandlerPath() const
 | 
						|
{
 | 
						|
#if PLATFORM_WINDOWS
 | 
						|
	const FString HandlerExecutableName = TEXT("crashpad_handler.exe");
 | 
						|
#elif PLATFORM_LINUX
 | 
						|
	const FString HandlerExecutableName = TEXT("crashpad_handler");
 | 
						|
#endif
 | 
						|
 | 
						|
	const FString HandlerPath = FPaths::Combine(FSentryModule::Get().GetBinariesPath(), HandlerExecutableName);
 | 
						|
	const FString HandlerFullPath = FPaths::ConvertRelativePathToFull(HandlerPath);
 | 
						|
 | 
						|
	return HandlerFullPath;
 | 
						|
}
 | 
						|
 | 
						|
FString SentrySubsystemDesktop::GetDatabasePath() const
 | 
						|
{
 | 
						|
	const FString DatabasePath = FPaths::Combine(databaseParentPath, TEXT(".sentry-native"));
 | 
						|
	const FString DatabaseFullPath = FPaths::ConvertRelativePathToFull(DatabasePath);
 | 
						|
 | 
						|
	return DatabaseFullPath;
 | 
						|
}
 | 
						|
 | 
						|
FString SentrySubsystemDesktop::GetScreenshotPath() const
 | 
						|
{
 | 
						|
	const FString ScreenshotPath = FPaths::Combine(GetDatabasePath(), TEXT("screenshots"), TEXT("crash_screenshot.png"));
 | 
						|
	const FString ScreenshotFullPath = FPaths::ConvertRelativePathToFull(ScreenshotPath);
 | 
						|
 | 
						|
	return ScreenshotFullPath;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |