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
|