From 41588a6e6c4f1b9bc681908c3c56a7226459c35b Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Thu, 7 Dec 2023 15:02:07 +0200 Subject: [PATCH 01/29] Add tracing settings --- .../Source/Sentry/Private/SentrySettings.cpp | 3 +++ .../Source/Sentry/Public/SentrySettings.h | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/SentrySettings.cpp b/plugin-dev/Source/Sentry/Private/SentrySettings.cpp index 1b9b7c30..7a94bb0f 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySettings.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySettings.cpp @@ -28,6 +28,9 @@ USentrySettings::USentrySettings(const FObjectInitializer& ObjectInitializer) , ProxyUrl() , BeforeSendHandler(USentryBeforeSendHandler::StaticClass()) , EnableForPromotedBuildsOnly(false) + , EnableTracing(false) + , SamplingType(ESentryTracesSamplingType::UniformSampleRate) + , TracesSampleRate(1.0f) , UploadSymbolsAutomatically(false) , IncludeSources(false) , CrashReporterUrl() diff --git a/plugin-dev/Source/Sentry/Public/SentrySettings.h b/plugin-dev/Source/Sentry/Public/SentrySettings.h index b9ed2ca4..fa23ce16 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySettings.h +++ b/plugin-dev/Source/Sentry/Public/SentrySettings.h @@ -9,6 +9,15 @@ class USentryBeforeSendHandler; +UENUM(BlueprintType) +enum class ESentryTracesSamplingType : uint8 +{ + // Use uniform sample rate for all transactions + UniformSampleRate, + // Control the sample rate based on the transaction itself and the context in which it's captured + TracesSampler +}; + USTRUCT(BlueprintType) struct FAutomaticBreadcrumbs { @@ -255,6 +264,24 @@ class SENTRY_API USentrySettings : public UObject Meta = (DisplayName = "Enable for promoted builds only", ToolTip = "Flag indicating whether to enable for promoted builds only.")) bool EnableForPromotedBuildsOnly; + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Enable tracing", ToolTip = "Flag indicating whether to enable tracing for performance monitoring.")) + bool EnableTracing; + + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Sampling type", ToolTip = "Method of controlling the sample rate for transactions.", EditCondition = "EnableTracing")) + ESentryTracesSamplingType SamplingType; + + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Traces sample rate", ToolTip = "Setting a uniform sample rate for all transactions to a number between 0.0 and 1.0. (For example, to send 20% of transactions, set TracesSampleRate to 0.2).", + EditCondition = "EnableTracing && SamplingType == ESentryTracesSamplingType::UniformSampleRate", EditConditionHides)) + float TracesSampleRate; + + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Traces sampler", ToolTip = "Custom hanler for determining traces sample rate based on the sampling context.", + EditCondition = "EnableTracing && SamplingType == ESentryTracesSamplingType::TracesSampler", EditConditionHides)) + TSubclassOf TracesSampler; + UPROPERTY(Config, EditAnywhere, Category = "Debug Symbols", Meta = (DisplayName = "Upload debug symbols automatically", ToolTip = "Flag indicating whether to automatically upload debug symbols to Sentry when packaging the app.")) bool UploadSymbolsAutomatically; From 390dd0eb0ab8de3239760b92058fd066068b08a7 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Thu, 7 Dec 2023 15:02:27 +0200 Subject: [PATCH 02/29] Update tracing init for Apple --- .../Private/Apple/SentrySubsystemApple.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp index 8deee399..b992276a 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp @@ -15,6 +15,7 @@ #include "SentryUserFeedback.h" #include "SentryUser.h" #include "SentryBeforeSendHandler.h" +#include "SentryDefines.h" #include "Infrastructure/SentryConvertorsApple.h" @@ -65,6 +66,21 @@ void SentrySubsystemApple::InitWithSettings(const USentrySettings* settings, USe { [options addInAppExclude:it->GetNSString()]; } + if (settings->EnableTracing) + { + options.enableTracing = true; + switch (settings->SamplingType) + { + case ESentryTracesSamplingType::UniformSampleRate: + options.tracesSampleRate = [NSNumber numberWithFloat:settings->TracesSampleRate]; + break; + case ESentryTracesSamplingType::TracesSampler: + options.tracesSampler = nil; + break; + default: + UE_LOG(LogSentrySdk, Warning, TEXT("Unknown sampling type value used.")); + } + } }]; } From ec0c501699fc34a833199ec82a9f07b13ced6a73 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 8 Dec 2023 09:42:40 +0200 Subject: [PATCH 03/29] Add tracing init for Android --- .../Private/Android/Java/SentryBridgeJava.java | 15 +++++++++++++++ .../Private/Android/Jni/SentryJniAndroid.cpp | 5 +++++ .../Private/Android/SentrySubsystemAndroid.cpp | 9 +++++++++ 3 files changed, 29 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java b/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java index 6d4f8cff..82639da3 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java +++ b/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java @@ -17,6 +17,7 @@ import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IHub; +import io.sentry.SamplingContext; import io.sentry.Scope; import io.sentry.ScopeCallback; import io.sentry.Sentry; @@ -30,6 +31,7 @@ public class SentryBridgeJava { public static native void onConfigureScope(long callbackAddr, Scope scope); public static native SentryEvent onBeforeSend(long handlerAddr, SentryEvent event, Hint hint); + public static native Double onTracesSampler(long samplerAddr, SamplingContext samplingContext); public static void init(Activity activity, final String settingsJsonStr, final long beforeSendHandler) { SentryAndroid.init(activity, new Sentry.OptionsConfiguration() { @@ -62,6 +64,19 @@ public SentryEvent execute(SentryEvent event, Hint hint) { for (int i = 0; i < Excludes.length(); i++) { options.addInAppExclude(Excludes.getString(i)); } + options.setEnableTracing(settingJson.getBoolean("enableTracing")); + if(settingJson.has("tracesSampleRate")) { + options.setTracesSampleRate(settingJson.getDouble("tracesSampleRate")); + } + if(settingJson.has("tracesSampler")) { + final long samplerAddr = settingJson.getLong("tracesSampler"); + options.setTracesSampler(new SentryOptions.TracesSamplerCallback() { + @Override + public Double sample(SamplingContext samplingContext) { + return onTracesSampler(samplerAddr, samplingContext); + } + }); + } } catch (JSONException e) { throw new RuntimeException(e); } diff --git a/plugin-dev/Source/Sentry/Private/Android/Jni/SentryJniAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/Jni/SentryJniAndroid.cpp index cb7369ae..cd93899d 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Jni/SentryJniAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/Jni/SentryJniAndroid.cpp @@ -31,4 +31,9 @@ JNI_METHOD jobject Java_io_sentry_unreal_SentryBridgeJava_onBeforeSend(JNIEnv* e HintToProcess->InitWithNativeImpl(MakeShareable(new SentryHintAndroid(hint))); return handler->HandleBeforeSend(EventToProcess, HintToProcess) ? event : nullptr; +} + +JNI_METHOD jobject Java_io_sentry_unreal_SentryBridgeJava_onTracesSampler(JNIEnv* env, jclass clazz, jlong objAddr, jobject samplingContext) +{ + return nullptr; } \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp index 54fcfe26..fde1fdcc 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp @@ -44,6 +44,15 @@ void SentrySubsystemAndroid::InitWithSettings(const USentrySettings* settings, U SettingsJson->SetArrayField(TEXT("inAppInclude"), SentryConvertorsAndroid::StrinArrayToJsonArray(settings->InAppInclude)); SettingsJson->SetArrayField(TEXT("inAppExclude"), SentryConvertorsAndroid::StrinArrayToJsonArray(settings->InAppExclude)); SettingsJson->SetBoolField(TEXT("sendDefaultPii"), settings->SendDefaultPii); + SettingsJson->SetBoolField(TEXT("enableTracing"), settings->EnableTracing); + if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::UniformSampleRate) + { + SettingsJson->SetNumberField(TEXT("tracesSampleRate"), settings->TracesSampleRate); + } + if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler) + { + SettingsJson->SetNumberField(TEXT("tracesSampler"), (jlong)0); + } FString SettingsJsonStr; TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&SettingsJsonStr); From 79bf971fce287c68ddb4f14ac2a57251b2fa11d1 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 8 Dec 2023 09:43:08 +0200 Subject: [PATCH 04/29] Replace switch in Apple tracing init --- .../Private/Apple/SentrySubsystemApple.cpp | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp index b992276a..23f78ccc 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp @@ -66,20 +66,14 @@ void SentrySubsystemApple::InitWithSettings(const USentrySettings* settings, USe { [options addInAppExclude:it->GetNSString()]; } - if (settings->EnableTracing) + options.enableTracing = settings->EnableTracing; + if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::UniformSampleRate) { - options.enableTracing = true; - switch (settings->SamplingType) - { - case ESentryTracesSamplingType::UniformSampleRate: - options.tracesSampleRate = [NSNumber numberWithFloat:settings->TracesSampleRate]; - break; - case ESentryTracesSamplingType::TracesSampler: - options.tracesSampler = nil; - break; - default: - UE_LOG(LogSentrySdk, Warning, TEXT("Unknown sampling type value used.")); - } + options.tracesSampleRate = [NSNumber numberWithFloat:settings->TracesSampleRate]; + } + if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler) + { + options.tracesSampler = nil; } }]; } From 5424cbc0cdc573b65e23548d44185ebdca9fc707 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 8 Dec 2023 10:26:17 +0200 Subject: [PATCH 05/29] Add tracing init for desktop --- .../Sentry/Private/Desktop/SentrySubsystemDesktop.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp index 40f67be8..799e448d 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp @@ -103,6 +103,15 @@ void SentrySubsystemDesktop::InitWithSettings(const USentrySettings* settings, U 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 sentry_options_set_handler_pathw(options, *FPaths::ConvertRelativePathToFull(HandlerPath)); sentry_options_set_database_pathw(options, *FPaths::ConvertRelativePathToFull(DatabasePath)); From d6e8f385066267eac10300c5736e78b96f8817d6 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 8 Dec 2023 15:27:26 +0200 Subject: [PATCH 06/29] Add placeholder classes for performance monitoring API --- .../Infrastructure/SentryJavaClasses.cpp | 2 ++ .../Infrastructure/SentryJavaClasses.h | 2 ++ .../Private/Android/SentrySpanAndroid.cpp | 11 +++++++++++ .../Private/Android/SentrySpanAndroid.h | 13 +++++++++++++ .../Android/SentryTransactionAndroid.cpp | 11 +++++++++++ .../Android/SentryTransactionAndroid.h | 13 +++++++++++++ .../Sentry/Private/Apple/SentrySpanApple.cpp | 3 +++ .../Sentry/Private/Apple/SentrySpanApple.h | 11 +++++++++++ .../Private/Apple/SentryTransactionApple.cpp | 3 +++ .../Private/Apple/SentryTransactionApple.h | 11 +++++++++++ .../Private/Desktop/SentrySpanDesktop.cpp | 7 +++++++ .../Private/Desktop/SentrySpanDesktop.h | 17 +++++++++++++++++ .../Desktop/SentryTransactionDesktop.cpp | 7 +++++++ .../Desktop/SentryTransactionDesktop.h | 17 +++++++++++++++++ .../Private/Interface/SentrySpanInterface.h | 11 +++++++++++ .../Interface/SentryTransactionInterface.h | 11 +++++++++++ .../Source/Sentry/Private/SentrySpan.cpp | 7 +++++++ .../Sentry/Private/SentryTransaction.cpp | 7 +++++++ plugin-dev/Source/Sentry/Public/SentrySpan.h | 19 +++++++++++++++++++ .../Source/Sentry/Public/SentryTransaction.h | 19 +++++++++++++++++++ 20 files changed, 202 insertions(+) create mode 100644 plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp create mode 100644 plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h create mode 100644 plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp create mode 100644 plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h create mode 100644 plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp create mode 100644 plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h create mode 100644 plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp create mode 100644 plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h create mode 100644 plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp create mode 100644 plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h create mode 100644 plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp create mode 100644 plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h create mode 100644 plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h create mode 100644 plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h create mode 100644 plugin-dev/Source/Sentry/Private/SentrySpan.cpp create mode 100644 plugin-dev/Source/Sentry/Private/SentryTransaction.cpp create mode 100644 plugin-dev/Source/Sentry/Public/SentrySpan.h create mode 100644 plugin-dev/Source/Sentry/Public/SentryTransaction.h diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.cpp b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.cpp index 2ca85e2c..bb2b332e 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.cpp @@ -15,6 +15,8 @@ const FSentryJavaClass SentryJavaClasses::UserFeedback = FSentryJavaClass { "io const FSentryJavaClass SentryJavaClasses::Message = FSentryJavaClass { "io/sentry/protocol/Message", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::SentryLevel = FSentryJavaClass { "io/sentry/SentryLevel", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::SentryHint = FSentryJavaClass { "io/sentry/Hint", ESentryJavaClassType::External }; +const FSentryJavaClass SentryJavaClasses::Transaction = FSentryJavaClass { "io/sentry/ITransaction", ESentryJavaClassType::External }; +const FSentryJavaClass SentryJavaClasses::Span = FSentryJavaClass { "io/sentry/ISpan", ESentryJavaClassType::External }; // System Java classes definitions const FSentryJavaClass SentryJavaClasses::ArrayList = FSentryJavaClass { "java/util/ArrayList", ESentryJavaClassType::System }; diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.h b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.h index 04703260..c37e773a 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.h +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryJavaClasses.h @@ -19,6 +19,8 @@ struct SentryJavaClasses const static FSentryJavaClass Message; const static FSentryJavaClass SentryLevel; const static FSentryJavaClass SentryHint; + const static FSentryJavaClass Transaction; + const static FSentryJavaClass Span; // System Java classes const static FSentryJavaClass ArrayList; diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp new file mode 100644 index 00000000..f450dd03 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp @@ -0,0 +1,11 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentrySpanAndroid.h" + +#include "Infrastructure/SentryConvertorsAndroid.h" +#include "Infrastructure/SentryJavaClasses.h" + +SentrySpanAndroid::SentrySpanAndroid(jobject span) + : FSentryJavaObjectWrapper(SentryJavaClasses::Span, span) +{ +} diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h new file mode 100644 index 00000000..972563d0 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentrySpanInterface.h" + +#include "Infrastructure/SentryJavaObjectWrapper.h" + +class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper +{ +public: + SentrySpanAndroid(jobject span); +}; diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp new file mode 100644 index 00000000..8d68e911 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -0,0 +1,11 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentryTransactionAndroid.h" + +#include "Infrastructure/SentryConvertorsAndroid.h" +#include "Infrastructure/SentryJavaClasses.h" + +SentryTransactionAndroid::SentryTransactionAndroid(jobject transaction) + : FSentryJavaObjectWrapper(SentryJavaClasses::Transaction, transaction) +{ +} diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h new file mode 100644 index 00000000..fc32d508 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h @@ -0,0 +1,13 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentryTransactionInterface.h" + +#include "Infrastructure/SentryJavaObjectWrapper.h" + +class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaObjectWrapper +{ +public: + SentryTransactionAndroid(jobject transaction); +}; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp new file mode 100644 index 00000000..f66067ba --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp @@ -0,0 +1,3 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentrySpanApple.h" diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h new file mode 100644 index 00000000..7b968c00 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h @@ -0,0 +1,11 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentrySpanInterface.h" + +class SentrySpanApple : public ISentrySpan +{ +public: + +}; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp new file mode 100644 index 00000000..e90004ed --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -0,0 +1,3 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentryTransactionApple.h" diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h new file mode 100644 index 00000000..901790c5 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h @@ -0,0 +1,11 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentryTransactionInterface.h" + +class SentryTransactionApple : public ISentryTransaction +{ +public: + +}; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp new file mode 100644 index 00000000..15860705 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentrySpanDesktop.h" + +#if USE_SENTRY_NATIVE + +#endif \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h new file mode 100644 index 00000000..25e8dd57 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h @@ -0,0 +1,17 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "Convenience/SentryInclude.h" + +#include "Interface/SentrySpanInterface.h" + +#if USE_SENTRY_NATIVE + +class SentrySpanDesktop : public ISentrySpan +{ +public: + +}; + +#endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp new file mode 100644 index 00000000..26dd015d --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentryTransactionDesktop.h" + +#if USE_SENTRY_NATIVE + +#endif \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h new file mode 100644 index 00000000..4eff960a --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h @@ -0,0 +1,17 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "Convenience/SentryInclude.h" + +#include "Interface/SentryTransactionInterface.h" + +#if USE_SENTRY_NATIVE + +class SentryTransactionDesktop : public ISentryTransaction +{ +public: + +}; + +#endif diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h new file mode 100644 index 00000000..27737898 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h @@ -0,0 +1,11 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +class ISentrySpan +{ +public: + virtual ~ISentrySpan() = default; +}; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h new file mode 100644 index 00000000..d1793118 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h @@ -0,0 +1,11 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +class ISentryTransaction +{ +public: + virtual ~ISentryTransaction() = default; +}; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp new file mode 100644 index 00000000..61f4838f --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentrySpan.h" + +USentrySpan::USentrySpan() +{ +} diff --git a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp new file mode 100644 index 00000000..ebd20bee --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#include "SentryTransaction.h" + +USentryTransaction::USentryTransaction() +{ +} diff --git a/plugin-dev/Source/Sentry/Public/SentrySpan.h b/plugin-dev/Source/Sentry/Public/SentrySpan.h new file mode 100644 index 00000000..0365a80e --- /dev/null +++ b/plugin-dev/Source/Sentry/Public/SentrySpan.h @@ -0,0 +1,19 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "SentrySpan.generated.h" + +/** + * Unit of work within a transaction. + */ +UCLASS(BlueprintType) +class SENTRY_API USentrySpan : public UObject +{ + GENERATED_BODY() + +public: + USentrySpan(); +}; diff --git a/plugin-dev/Source/Sentry/Public/SentryTransaction.h b/plugin-dev/Source/Sentry/Public/SentryTransaction.h new file mode 100644 index 00000000..46eef77d --- /dev/null +++ b/plugin-dev/Source/Sentry/Public/SentryTransaction.h @@ -0,0 +1,19 @@ +// Copyright (c) 2023 Sentry. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "SentryTransaction.generated.h" + +/** + * Representation of an activity to measure or track. + */ +UCLASS(BlueprintType) +class SENTRY_API USentryTransaction : public UObject +{ + GENERATED_BODY() + +public: + USentryTransaction(); +}; From 17384d939f7d8f8fb08046e868c94b1b1d8a01c7 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 8 Dec 2023 15:32:13 +0200 Subject: [PATCH 07/29] Update plugin snapshot --- scripts/packaging/package-github.snapshot | 18 ++++++++++++++++++ scripts/packaging/package-marketplace.snapshot | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/scripts/packaging/package-github.snapshot b/scripts/packaging/package-github.snapshot index 046b4d2c..35c0cde4 100644 --- a/scripts/packaging/package-github.snapshot +++ b/scripts/packaging/package-github.snapshot @@ -42,8 +42,12 @@ Source/Sentry/Private/Android/SentryMessageAndroid.cpp Source/Sentry/Private/Android/SentryMessageAndroid.h Source/Sentry/Private/Android/SentryScopeAndroid.cpp Source/Sentry/Private/Android/SentryScopeAndroid.h +Source/Sentry/Private/Android/SentrySpanAndroid.cpp +Source/Sentry/Private/Android/SentrySpanAndroid.h Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp Source/Sentry/Private/Android/SentrySubsystemAndroid.h +Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +Source/Sentry/Private/Android/SentryTransactionAndroid.h Source/Sentry/Private/Android/SentryUserAndroid.cpp Source/Sentry/Private/Android/SentryUserAndroid.h Source/Sentry/Private/Android/SentryUserFeedbackAndroid.cpp @@ -65,8 +69,12 @@ Source/Sentry/Private/Apple/SentryIdApple.cpp Source/Sentry/Private/Apple/SentryIdApple.h Source/Sentry/Private/Apple/SentryScopeApple.cpp Source/Sentry/Private/Apple/SentryScopeApple.h +Source/Sentry/Private/Apple/SentrySpanApple.cpp +Source/Sentry/Private/Apple/SentrySpanApple.h Source/Sentry/Private/Apple/SentrySubsystemApple.cpp Source/Sentry/Private/Apple/SentrySubsystemApple.h +Source/Sentry/Private/Apple/SentryTransactionApple.cpp +Source/Sentry/Private/Apple/SentryTransactionApple.h Source/Sentry/Private/Apple/SentryUserApple.cpp Source/Sentry/Private/Apple/SentryUserApple.h Source/Sentry/Private/Apple/SentryUserFeedbackApple.cpp @@ -90,8 +98,12 @@ Source/Sentry/Private/Desktop/SentryIdDesktop.cpp Source/Sentry/Private/Desktop/SentryIdDesktop.h Source/Sentry/Private/Desktop/SentryScopeDesktop.cpp Source/Sentry/Private/Desktop/SentryScopeDesktop.h +Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp +Source/Sentry/Private/Desktop/SentrySpanDesktop.h Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp Source/Sentry/Private/Desktop/SentrySubsystemDesktop.h +Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +Source/Sentry/Private/Desktop/SentryTransactionDesktop.h Source/Sentry/Private/Desktop/SentryUserDesktop.cpp Source/Sentry/Private/Desktop/SentryUserDesktop.h Source/Sentry/Private/Desktop/Transport/ @@ -106,7 +118,9 @@ Source/Sentry/Private/Interface/SentryEventInterface.h Source/Sentry/Private/Interface/SentryHintInterface.h Source/Sentry/Private/Interface/SentryIdInterface.h Source/Sentry/Private/Interface/SentryScopeInterface.h +Source/Sentry/Private/Interface/SentrySpanInterface.h Source/Sentry/Private/Interface/SentrySubsystemInterface.h +Source/Sentry/Private/Interface/SentryTransactionInterface.h Source/Sentry/Private/Interface/SentryUserFeedbackInterface.h Source/Sentry/Private/Interface/SentryUserInterface.h Source/Sentry/Private/SentryAttachment.cpp @@ -120,7 +134,9 @@ Source/Sentry/Private/SentryLibrary.cpp Source/Sentry/Private/SentryModule.cpp Source/Sentry/Private/SentryScope.cpp Source/Sentry/Private/SentrySettings.cpp +Source/Sentry/Private/SentrySpan.cpp Source/Sentry/Private/SentrySubsystem.cpp +Source/Sentry/Private/SentryTransaction.cpp Source/Sentry/Private/SentryUser.cpp Source/Sentry/Private/SentryUserFeedback.cpp Source/Sentry/Private/Tests/ @@ -144,7 +160,9 @@ Source/Sentry/Public/SentryLibrary.h Source/Sentry/Public/SentryModule.h Source/Sentry/Public/SentryScope.h Source/Sentry/Public/SentrySettings.h +Source/Sentry/Public/SentrySpan.h Source/Sentry/Public/SentrySubsystem.h +Source/Sentry/Public/SentryTransaction.h Source/Sentry/Public/SentryUser.h Source/Sentry/Public/SentryUserFeedback.h Source/Sentry/Sentry_Android_UPL.xml diff --git a/scripts/packaging/package-marketplace.snapshot b/scripts/packaging/package-marketplace.snapshot index 576852e9..19485b35 100644 --- a/scripts/packaging/package-marketplace.snapshot +++ b/scripts/packaging/package-marketplace.snapshot @@ -40,8 +40,12 @@ Source/Sentry/Private/Android/SentryMessageAndroid.cpp Source/Sentry/Private/Android/SentryMessageAndroid.h Source/Sentry/Private/Android/SentryScopeAndroid.cpp Source/Sentry/Private/Android/SentryScopeAndroid.h +Source/Sentry/Private/Android/SentrySpanAndroid.cpp +Source/Sentry/Private/Android/SentrySpanAndroid.h Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp Source/Sentry/Private/Android/SentrySubsystemAndroid.h +Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +Source/Sentry/Private/Android/SentryTransactionAndroid.h Source/Sentry/Private/Android/SentryUserAndroid.cpp Source/Sentry/Private/Android/SentryUserAndroid.h Source/Sentry/Private/Android/SentryUserFeedbackAndroid.cpp @@ -63,8 +67,12 @@ Source/Sentry/Private/Apple/SentryIdApple.cpp Source/Sentry/Private/Apple/SentryIdApple.h Source/Sentry/Private/Apple/SentryScopeApple.cpp Source/Sentry/Private/Apple/SentryScopeApple.h +Source/Sentry/Private/Apple/SentrySpanApple.cpp +Source/Sentry/Private/Apple/SentrySpanApple.h Source/Sentry/Private/Apple/SentrySubsystemApple.cpp Source/Sentry/Private/Apple/SentrySubsystemApple.h +Source/Sentry/Private/Apple/SentryTransactionApple.cpp +Source/Sentry/Private/Apple/SentryTransactionApple.h Source/Sentry/Private/Apple/SentryUserApple.cpp Source/Sentry/Private/Apple/SentryUserApple.h Source/Sentry/Private/Apple/SentryUserFeedbackApple.cpp @@ -88,8 +96,12 @@ Source/Sentry/Private/Desktop/SentryIdDesktop.cpp Source/Sentry/Private/Desktop/SentryIdDesktop.h Source/Sentry/Private/Desktop/SentryScopeDesktop.cpp Source/Sentry/Private/Desktop/SentryScopeDesktop.h +Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp +Source/Sentry/Private/Desktop/SentrySpanDesktop.h Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp Source/Sentry/Private/Desktop/SentrySubsystemDesktop.h +Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +Source/Sentry/Private/Desktop/SentryTransactionDesktop.h Source/Sentry/Private/Desktop/SentryUserDesktop.cpp Source/Sentry/Private/Desktop/SentryUserDesktop.h Source/Sentry/Private/Desktop/Transport/ @@ -104,7 +116,9 @@ Source/Sentry/Private/Interface/SentryEventInterface.h Source/Sentry/Private/Interface/SentryHintInterface.h Source/Sentry/Private/Interface/SentryIdInterface.h Source/Sentry/Private/Interface/SentryScopeInterface.h +Source/Sentry/Private/Interface/SentrySpanInterface.h Source/Sentry/Private/Interface/SentrySubsystemInterface.h +Source/Sentry/Private/Interface/SentryTransactionInterface.h Source/Sentry/Private/Interface/SentryUserFeedbackInterface.h Source/Sentry/Private/Interface/SentryUserInterface.h Source/Sentry/Private/SentryAttachment.cpp @@ -118,7 +132,9 @@ Source/Sentry/Private/SentryLibrary.cpp Source/Sentry/Private/SentryModule.cpp Source/Sentry/Private/SentryScope.cpp Source/Sentry/Private/SentrySettings.cpp +Source/Sentry/Private/SentrySpan.cpp Source/Sentry/Private/SentrySubsystem.cpp +Source/Sentry/Private/SentryTransaction.cpp Source/Sentry/Private/SentryUser.cpp Source/Sentry/Private/SentryUserFeedback.cpp Source/Sentry/Private/Tests/ @@ -142,7 +158,9 @@ Source/Sentry/Public/SentryLibrary.h Source/Sentry/Public/SentryModule.h Source/Sentry/Public/SentryScope.h Source/Sentry/Public/SentrySettings.h +Source/Sentry/Public/SentrySpan.h Source/Sentry/Public/SentrySubsystem.h +Source/Sentry/Public/SentryTransaction.h Source/Sentry/Public/SentryUser.h Source/Sentry/Public/SentryUserFeedback.h Source/Sentry/Sentry_Android_UPL.xml From cf91b7f080f10397fbc82059b7ccbabd7ae431a6 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Mon, 11 Dec 2023 11:01:06 +0200 Subject: [PATCH 08/29] Add get/set methods for Span and Trancaction native impl --- .../Source/Sentry/Private/SentrySpan.cpp | 20 +++++++++++++++++++ .../Sentry/Private/SentryTransaction.cpp | 20 +++++++++++++++++++ plugin-dev/Source/Sentry/Public/SentrySpan.h | 8 ++++++++ .../Source/Sentry/Public/SentryTransaction.h | 8 ++++++++ 4 files changed, 56 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp index 61f4838f..02afbd7b 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp @@ -2,6 +2,26 @@ #include "SentrySpan.h" +#include "Interface/SentrySpanInterface.h" + +#if PLATFORM_ANDROID +#include "Android/SentrySpanAndroid.h" +#elif PLATFORM_IOS || PLATFORM_MAC +#include "Apple/SentrySpanApple.h" +#elif PLATFORM_WINDOWS || PLATFORM_LINUX +#include "Desktop/SentrySpanDesktop.h" +#endif + USentrySpan::USentrySpan() { } + +void USentrySpan::InitWithNativeImpl(TSharedPtr spanImpl) +{ + SentrySpanNativeImpl = spanImpl; +} + +TSharedPtr USentrySpan::GetNativeImpl() +{ + return SentrySpanNativeImpl; +} diff --git a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp index ebd20bee..7211faf9 100644 --- a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp +++ b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp @@ -2,6 +2,26 @@ #include "SentryTransaction.h" +#include "Interface/SentryTransactionInterface.h" + +#if PLATFORM_ANDROID +#include "Android/SentryTransactionAndroid.h" +#elif PLATFORM_IOS || PLATFORM_MAC +#include "Apple/SentryTransactionApple.h" +#elif PLATFORM_WINDOWS || PLATFORM_LINUX +#include "Desktop/SentryTransactionDesktop.h" +#endif + USentryTransaction::USentryTransaction() { } + +void USentryTransaction::InitWithNativeImpl(TSharedPtr transactionImpl) +{ + SentryTransactionNativeImpl = transactionImpl; +} + +TSharedPtr USentryTransaction::GetNativeImpl() +{ + return SentryTransactionNativeImpl; +} diff --git a/plugin-dev/Source/Sentry/Public/SentrySpan.h b/plugin-dev/Source/Sentry/Public/SentrySpan.h index 0365a80e..2fa3c68d 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySpan.h +++ b/plugin-dev/Source/Sentry/Public/SentrySpan.h @@ -6,6 +6,8 @@ #include "UObject/Object.h" #include "SentrySpan.generated.h" +class ISentrySpan; + /** * Unit of work within a transaction. */ @@ -16,4 +18,10 @@ class SENTRY_API USentrySpan : public UObject public: USentrySpan(); + + void InitWithNativeImpl(TSharedPtr spanImpl); + TSharedPtr GetNativeImpl(); + +private: + TSharedPtr SentrySpanNativeImpl; }; diff --git a/plugin-dev/Source/Sentry/Public/SentryTransaction.h b/plugin-dev/Source/Sentry/Public/SentryTransaction.h index 46eef77d..d7f14cbf 100644 --- a/plugin-dev/Source/Sentry/Public/SentryTransaction.h +++ b/plugin-dev/Source/Sentry/Public/SentryTransaction.h @@ -6,6 +6,8 @@ #include "UObject/Object.h" #include "SentryTransaction.generated.h" +class ISentryTransaction; + /** * Representation of an activity to measure or track. */ @@ -16,4 +18,10 @@ class SENTRY_API USentryTransaction : public UObject public: USentryTransaction(); + + void InitWithNativeImpl(TSharedPtr transactionImpl); + TSharedPtr GetNativeImpl(); + +private: + TSharedPtr SentryTransactionNativeImpl; }; From b1cd1ab2f9b9f3afb0f925598a948f100caf43b9 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Mon, 11 Dec 2023 17:19:19 +0200 Subject: [PATCH 09/29] Add minimap API for starting/finishing transactions --- .../SentryConvertorsAndroid.cpp | 10 ++++++++ .../Infrastructure/SentryConvertorsAndroid.h | 2 ++ .../Android/SentrySubsystemAndroid.cpp | 8 ++++++ .../Private/Android/SentrySubsystemAndroid.h | 1 + .../Android/SentryTransactionAndroid.cpp | 10 ++++++++ .../Android/SentryTransactionAndroid.h | 7 ++++++ .../Infrastructure/SentryConvertorsApple.cpp | 10 ++++++++ .../Infrastructure/SentryConvertorsApple.h | 2 ++ .../Private/Apple/SentrySubsystemApple.cpp | 9 +++++++ .../Private/Apple/SentrySubsystemApple.h | 1 + .../Private/Apple/SentryTransactionApple.cpp | 25 +++++++++++++++++++ .../Private/Apple/SentryTransactionApple.h | 12 ++++++++- .../Desktop/SentrySubsystemDesktop.cpp | 20 ++++++++++++++- .../Private/Desktop/SentrySubsystemDesktop.h | 1 + .../Desktop/SentryTransactionDesktop.cpp | 21 +++++++++++++++- .../Desktop/SentryTransactionDesktop.h | 10 +++++++- .../Interface/SentrySubsystemInterface.h | 2 ++ .../Interface/SentryTransactionInterface.h | 2 ++ .../Source/Sentry/Private/SentrySubsystem.cpp | 8 ++++++ .../Source/Sentry/Public/SentrySubsystem.h | 12 ++++++++- 20 files changed, 168 insertions(+), 5 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp index 9c5caaa8..0615eca7 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp @@ -6,10 +6,12 @@ #include "SentryScope.h" #include "SentryId.h" +#include "SentryTransaction.h" #include "SentryDefines.h" #include "Android/SentryScopeAndroid.h" #include "Android/SentryIdAndroid.h" +#include "Android/SentryTransactionAndroid.h" #include "Android/AndroidApplication.h" #include "Android/AndroidJavaEnv.h" @@ -149,6 +151,14 @@ USentryId* SentryConvertorsAndroid::SentryIdToUnreal(jobject id) return unrealId; } +USentryTransaction* SentryConvertorsAndroid::SentryTransactionToUnreal(jobject transaction) +{ + TSharedPtr transactionNativeImpl = MakeShareable(new SentryTransactionAndroid(transaction)); + USentryTransaction* unrealTransaction = NewObject(); + unrealTransaction->InitWithNativeImpl(transactionNativeImpl); + return unrealTransaction; +} + TMap SentryConvertorsAndroid::StringMapToUnreal(jobject map) { TMap result; diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h index 0c91f567..55df4565 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h @@ -8,6 +8,7 @@ class USentryScope; class USentryId; +class USentryTransaction; class FSentryJavaObjectWrapper; class FJsonValue; @@ -24,6 +25,7 @@ class SentryConvertorsAndroid static ESentryLevel SentryLevelToUnreal(jobject level); static USentryScope* SentryScopeToUnreal(jobject scope); static USentryId* SentryIdToUnreal(jobject id); + static USentryTransaction* SentryTransactionToUnreal(jobject transaction); static TMap StringMapToUnreal(jobject stringMap); static TArray StringListToUnreal(jobject stringList); static TArray ByteArrayToUnreal(jbyteArray byteArray); diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp index fde1fdcc..bb54b45e 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp @@ -193,3 +193,11 @@ void SentrySubsystemAndroid::EndSession() { FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "endSession", "()V", nullptr); } + +USentryTransaction* SentrySubsystemAndroid::StartTransaction(const FString& name, const FString& operation) +{ + auto transaction = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "startTransaction", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction;", + *FSentryJavaObjectWrapper::GetJString(name), *FSentryJavaObjectWrapper::GetJString(operation)); + + return SentryConvertorsAndroid::SentryTransactionToUnreal(transaction); +} diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.h index 7c146173..b3dd7a8b 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.h @@ -26,4 +26,5 @@ class SentrySubsystemAndroid : public ISentrySubsystem virtual void SetLevel(ESentryLevel level) override; virtual void StartSession() override; virtual void EndSession() override; + virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override; }; diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp index 8d68e911..98ee516f 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -9,3 +9,13 @@ SentryTransactionAndroid::SentryTransactionAndroid(jobject transaction) : FSentryJavaObjectWrapper(SentryJavaClasses::Transaction, transaction) { } + +void SentryTransactionAndroid::SetupClassMethods() +{ + FinishMethod = GetMethod("finish", "()V"); +} + +void SentryTransactionAndroid::Finish() +{ + CallMethod(FinishMethod); +} diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h index fc32d508..58540974 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h @@ -10,4 +10,11 @@ class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaOb { public: SentryTransactionAndroid(jobject transaction); + + void SetupClassMethods(); + + virtual void Finish() override; + +private: + FSentryJavaMethod FinishMethod; }; diff --git a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp index 80814997..3d13edf3 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp @@ -3,10 +3,12 @@ #include "SentryConvertorsApple.h" #include "SentryScope.h" #include "SentryId.h" +#include "SentryTransaction.h" #include "SentryDefines.h" #include "Apple/SentryScopeApple.h" #include "Apple/SentryIdApple.h" +#include "Apple/SentryTransactionApple.h" SentryLevel SentryConvertorsApple::SentryLevelToNative(ESentryLevel level) { @@ -147,6 +149,14 @@ USentryId* SentryConvertorsApple::SentryIdToUnreal(SentryId* id) return unrealId; } +USentryTransaction* SentryConvertorsApple::SentryTransactionToUnreal(id transaction) +{ + TSharedPtr transactionNativeImpl = MakeShareable(new SentryTransactionApple(transaction)); + USentryTransaction* unrealTransaction = NewObject(); + unrealTransaction->InitWithNativeImpl(transactionNativeImpl); + return unrealTransaction; +} + SentryLevel SentryConvertorsApple::StringToSentryLevel(NSString* string) { SentryLevel nativeLevel = kSentryLevelDebug; diff --git a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h index 1cbce542..a9d90a68 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h @@ -8,6 +8,7 @@ class USentryScope; class USentryId; +class USentryTransaction; class SentryConvertorsApple { @@ -25,6 +26,7 @@ class SentryConvertorsApple static TArray ByteDataToUnreal(NSData* data); static USentryScope* SentryScopeToUnreal(SentryScope* scope); static USentryId* SentryIdToUnreal(SentryId* id); + static USentryTransaction* SentryTransactionToUnreal(id transaction); /** Other conversions */ static SentryLevel StringToSentryLevel(NSString* string); diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp index 23f78ccc..70f55866 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp @@ -7,6 +7,7 @@ #include "SentryScopeApple.h" #include "SentryUserApple.h" #include "SentryUserFeedbackApple.h" +#include "SentryTransactionApple.h" #include "SentryEvent.h" #include "SentryBreadcrumb.h" @@ -14,6 +15,7 @@ #include "SentrySettings.h" #include "SentryUserFeedback.h" #include "SentryUser.h" +#include "SentryTransaction.h" #include "SentryBeforeSendHandler.h" #include "SentryDefines.h" @@ -203,3 +205,10 @@ void SentrySubsystemApple::EndSession() { [SENTRY_APPLE_CLASS(SentrySDK) endSession]; } + +USentryTransaction* SentrySubsystemApple::StartTransaction(const FString& name, const FString& operation) +{ + id transaction = [SENTRY_APPLE_CLASS(SentrySDK) startTransactionWithName:name.GetNSString() operation:operation.GetNSString()]; + + return SentryConvertorsApple::SentryTransactionToUnreal(transaction); +} diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.h index d82b9c8e..90a69a38 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.h @@ -26,4 +26,5 @@ class SentrySubsystemApple : public ISentrySubsystem virtual void SetLevel(ESentryLevel level) override; virtual void StartSession() override; virtual void EndSession() override; + virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override; }; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp index e90004ed..b7ace0d4 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -1,3 +1,28 @@ // Copyright (c) 2023 Sentry. All Rights Reserved. #include "SentryTransactionApple.h" + +#include "Infrastructure/SentryConvertorsApple.h" + +#include "Convenience/SentryInclude.h" +#include "Convenience/SentryMacro.h" + +SentryTransactionApple::SentryTransactionApple(id transaction) +{ + TransactionApple = transaction; +} + +SentryTransactionApple::~SentryTransactionApple() +{ + // Put custom destructor logic here if needed +} + +id SentryTransactionApple::GetNativeObject() +{ + return TransactionApple; +} + +void SentryTransactionApple::Finish() +{ + [TransactionApple finish]; +} diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h index 901790c5..008ea9cd 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h @@ -4,8 +4,18 @@ #include "Interface/SentryTransactionInterface.h" +@protocol SentrySpan; + class SentryTransactionApple : public ISentryTransaction { public: - + SentryTransactionApple(id transaction); + virtual ~SentryTransactionApple() override; + + id GetNativeObject(); + + virtual void Finish() override; + +private: + id TransactionApple; }; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp index 799e448d..b885ea1d 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp @@ -5,6 +5,7 @@ #include "SentryBreadcrumbDesktop.h" #include "SentryUserDesktop.h" #include "SentryScopeDesktop.h" +#include "SentryTransactionDesktop.h" #include "SentryDefines.h" #include "SentrySettings.h" @@ -12,12 +13,15 @@ #include "SentryBreadcrumb.h" #include "SentryUserFeedback.h" #include "SentryUser.h" +#include "SentryTransaction.h" #include "SentryModule.h" #include "SentryBeforeSendHandler.h" -#include "CrashReporter/SentryCrashContext.h" #include "Infrastructure/SentryConvertorsDesktop.h" + #include "CrashReporter/SentryCrashReporter.h" +#include "CrashReporter/SentryCrashContext.h" + #include "Transport/SentryTransport.h" #include "Misc/Paths.h" @@ -309,6 +313,20 @@ void SentrySubsystemDesktop::EndSession() sentry_end_session(); } +USentryTransaction* 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()); + + TSharedPtr TransactionDesktop = MakeShareable(new SentryTransactionDesktop(nativeTransaction)); + + USentryTransaction* Transaction = NewObject(); + Transaction->InitWithNativeImpl(TransactionDesktop); + + return Transaction; +} + USentryBeforeSendHandler* SentrySubsystemDesktop::GetBeforeSendHandler() { return beforeSend; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.h index 7f1c1b17..c1b3deff 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.h @@ -35,6 +35,7 @@ class SentrySubsystemDesktop : public ISentrySubsystem virtual void SetLevel(ESentryLevel level) override; virtual void StartSession() override; virtual void EndSession() override; + virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override; USentryBeforeSendHandler* GetBeforeSendHandler(); diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp index 26dd015d..a0620656 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -4,4 +4,23 @@ #if USE_SENTRY_NATIVE -#endif \ No newline at end of file +SentryTransactionDesktop::SentryTransactionDesktop(sentry_transaction_t* transaction) + : TransactionDesktop(transaction) +{ +} + +SentryTransactionDesktop::~SentryTransactionDesktop() +{ +} + +sentry_transaction_t* SentryTransactionDesktop::GetNativeObject() +{ + return TransactionDesktop; +} + +void SentryTransactionDesktop::Finish() +{ + sentry_transaction_finish(TransactionDesktop); +} + +#endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h index 4eff960a..81b25d4e 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h @@ -11,7 +11,15 @@ class SentryTransactionDesktop : public ISentryTransaction { public: - + SentryTransactionDesktop(sentry_transaction_t* transaction); + virtual ~SentryTransactionDesktop() override; + + sentry_transaction_t* GetNativeObject(); + + virtual void Finish() override; + +private: + sentry_transaction_t* TransactionDesktop; }; #endif diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h index e9a52b45..c1d6e112 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h @@ -14,6 +14,7 @@ class USentryId; class USentryUserFeedback; class USentryUser; class USentryBeforeSendHandler; +class USentryTransaction; class ISentrySubsystem { @@ -39,4 +40,5 @@ class ISentrySubsystem virtual void SetLevel(ESentryLevel level) = 0; virtual void StartSession() = 0; virtual void EndSession() = 0; + virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) = 0; }; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h index d1793118..cabaa303 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h @@ -8,4 +8,6 @@ class ISentryTransaction { public: virtual ~ISentryTransaction() = default; + + virtual void Finish() = 0; }; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp b/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp index 83f3a998..c1cf7a3f 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp @@ -285,6 +285,14 @@ void USentrySubsystem::EndSession() SubsystemNativeImpl->EndSession(); } +USentryTransaction* USentrySubsystem::StartTransaction(const FString& Name, const FString& Operation) +{ + if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled()) + return nullptr; + + return SubsystemNativeImpl->StartTransaction(Name, Operation); +} + void USentrySubsystem::AddDefaultContext() { if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled()) diff --git a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h index 8f670ac3..dd31480c 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h @@ -16,8 +16,10 @@ class USentryEvent; class USentryId; class USentryUserFeedback; class USentryUser; -class ISentrySubsystem; class USentryBeforeSendHandler; +class USentryTransaction; + +class ISentrySubsystem; DECLARE_DYNAMIC_DELEGATE_OneParam(FConfigureSettingsDelegate, USentrySettings*, Settings); @@ -230,6 +232,14 @@ class SENTRY_API USentrySubsystem : public UEngineSubsystem UFUNCTION(BlueprintCallable, Category = "Sentry") void EndSession(); + /** + * Starts a new transaction based on the provided context. + * + * @param Name Transaction name. + * @param Operation Short code identifying the type of operation the span is measuring. + */ + USentryTransaction* StartTransaction(const FString& Name, const FString& Operation); + private: /** Adds default context data for all events captured by Sentry SDK. */ void AddDefaultContext(); From 36c15259eaa5b470eadc993fe12c33ca7a0cec74 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Tue, 12 Dec 2023 14:18:10 +0200 Subject: [PATCH 10/29] Add basic spans implementation --- .../SentryConvertorsAndroid.cpp | 10 +++++++++ .../Infrastructure/SentryConvertorsAndroid.h | 2 ++ .../Private/Android/SentrySpanAndroid.cpp | 11 ++++++++++ .../Private/Android/SentrySpanAndroid.h | 7 ++++++ .../Android/SentrySubsystemAndroid.cpp | 2 +- .../Android/SentryTransactionAndroid.cpp | 8 +++++++ .../Android/SentryTransactionAndroid.h | 2 ++ .../Infrastructure/SentryConvertorsApple.cpp | 10 +++++++++ .../Infrastructure/SentryConvertorsApple.h | 1 + .../Sentry/Private/Apple/SentrySpanApple.cpp | 20 +++++++++++++++++ .../Sentry/Private/Apple/SentrySpanApple.h | 12 +++++++++- .../Private/Apple/SentryTransactionApple.cpp | 6 +++++ .../Private/Apple/SentryTransactionApple.h | 1 + .../Private/Desktop/SentrySpanDesktop.cpp | 22 ++++++++++++++++++- .../Private/Desktop/SentrySpanDesktop.h | 10 ++++++++- .../Desktop/SentryTransactionDesktop.cpp | 10 +++++++++ .../Desktop/SentryTransactionDesktop.h | 1 + .../Private/Interface/SentrySpanInterface.h | 2 ++ .../Interface/SentryTransactionInterface.h | 3 +++ .../Sentry/Private/SentryTransaction.cpp | 17 ++++++++++++++ .../Source/Sentry/Public/SentrySubsystem.h | 2 +- .../Source/Sentry/Public/SentryTransaction.h | 9 ++++++++ 22 files changed, 163 insertions(+), 5 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp index 0615eca7..27e80a5a 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.cpp @@ -7,11 +7,13 @@ #include "SentryScope.h" #include "SentryId.h" #include "SentryTransaction.h" +#include "SentrySpan.h" #include "SentryDefines.h" #include "Android/SentryScopeAndroid.h" #include "Android/SentryIdAndroid.h" #include "Android/SentryTransactionAndroid.h" +#include "Android/SentrySpanAndroid.h" #include "Android/AndroidApplication.h" #include "Android/AndroidJavaEnv.h" @@ -159,6 +161,14 @@ USentryTransaction* SentryConvertorsAndroid::SentryTransactionToUnreal(jobject t return unrealTransaction; } +USentrySpan* SentryConvertorsAndroid::SentrySpanToUnreal(jobject span) +{ + TSharedPtr spanNativeImpl = MakeShareable(new SentrySpanAndroid(span)); + USentrySpan* unrealSpan = NewObject(); + unrealSpan->InitWithNativeImpl(spanNativeImpl); + return unrealSpan; +} + TMap SentryConvertorsAndroid::StringMapToUnreal(jobject map) { TMap result; diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h index 55df4565..c93f40dd 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/SentryConvertorsAndroid.h @@ -9,6 +9,7 @@ class USentryScope; class USentryId; class USentryTransaction; +class USentrySpan; class FSentryJavaObjectWrapper; class FJsonValue; @@ -26,6 +27,7 @@ class SentryConvertorsAndroid static USentryScope* SentryScopeToUnreal(jobject scope); static USentryId* SentryIdToUnreal(jobject id); static USentryTransaction* SentryTransactionToUnreal(jobject transaction); + static USentrySpan* SentrySpanToUnreal(jobject span); static TMap StringMapToUnreal(jobject stringMap); static TArray StringListToUnreal(jobject stringList); static TArray ByteArrayToUnreal(jbyteArray byteArray); diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp index f450dd03..323db5a8 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp @@ -8,4 +8,15 @@ SentrySpanAndroid::SentrySpanAndroid(jobject span) : FSentryJavaObjectWrapper(SentryJavaClasses::Span, span) { + SetupClassMethods(); +} + +void SentrySpanAndroid::SetupClassMethods() +{ + FinishMethod = GetMethod("finish", "()V"); +} + +void SentrySpanAndroid::Finish() +{ + CallMethod(FinishMethod); } diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h index 972563d0..6fad4bed 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h @@ -10,4 +10,11 @@ class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper { public: SentrySpanAndroid(jobject span); + + void SetupClassMethods(); + + virtual void Finish() override; + +private: + FSentryJavaMethod FinishMethod; }; diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp index bb54b45e..18153ea1 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp @@ -199,5 +199,5 @@ USentryTransaction* SentrySubsystemAndroid::StartTransaction(const FString& name auto transaction = FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "startTransaction", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction;", *FSentryJavaObjectWrapper::GetJString(name), *FSentryJavaObjectWrapper::GetJString(operation)); - return SentryConvertorsAndroid::SentryTransactionToUnreal(transaction); + return SentryConvertorsAndroid::SentryTransactionToUnreal(*transaction); } diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp index 98ee516f..657fd8c1 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -8,13 +8,21 @@ SentryTransactionAndroid::SentryTransactionAndroid(jobject transaction) : FSentryJavaObjectWrapper(SentryJavaClasses::Transaction, transaction) { + SetupClassMethods(); } void SentryTransactionAndroid::SetupClassMethods() { + StartChildMethod = GetMethod("startChild", "(Ljava/lang/String;Ljava/lang/String;)Lsentry/io/ISpan;"); FinishMethod = GetMethod("finish", "()V"); } +USentrySpan* SentryTransactionAndroid::StartChild(const FString& operation, const FString& desctiption) +{ + auto span = CallObjectMethod(StartChildMethod, *GetJString(operation), *GetJString(desctiption)); + return SentryConvertorsAndroid::SentrySpanToUnreal(*span); +} + void SentryTransactionAndroid::Finish() { CallMethod(FinishMethod); diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h index 58540974..f387c500 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h @@ -13,8 +13,10 @@ class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaOb void SetupClassMethods(); + virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; private: + FSentryJavaMethod StartChildMethod; FSentryJavaMethod FinishMethod; }; diff --git a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp index 3d13edf3..fb28ce4a 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.cpp @@ -4,11 +4,13 @@ #include "SentryScope.h" #include "SentryId.h" #include "SentryTransaction.h" +#include "SentrySpan.h" #include "SentryDefines.h" #include "Apple/SentryScopeApple.h" #include "Apple/SentryIdApple.h" #include "Apple/SentryTransactionApple.h" +#include "Apple/SentrySpanApple.h" SentryLevel SentryConvertorsApple::SentryLevelToNative(ESentryLevel level) { @@ -157,6 +159,14 @@ USentryTransaction* SentryConvertorsApple::SentryTransactionToUnreal(id span) +{ + TSharedPtr spanNativeImpl = MakeShareable(new SentrySpanApple(span)); + USentrySpan* unrealSpan = NewObject(); + unrealSpan->InitWithNativeImpl(spanNativeImpl); + return unrealSpan; +} + SentryLevel SentryConvertorsApple::StringToSentryLevel(NSString* string) { SentryLevel nativeLevel = kSentryLevelDebug; diff --git a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h index a9d90a68..2b7d649b 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/Infrastructure/SentryConvertorsApple.h @@ -27,6 +27,7 @@ class SentryConvertorsApple static USentryScope* SentryScopeToUnreal(SentryScope* scope); static USentryId* SentryIdToUnreal(SentryId* id); static USentryTransaction* SentryTransactionToUnreal(id transaction); + static USentrySpan* SentrySpanToUnreal(id span); /** Other conversions */ static SentryLevel StringToSentryLevel(NSString* string); diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp index f66067ba..15880379 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp @@ -1,3 +1,23 @@ // Copyright (c) 2023 Sentry. All Rights Reserved. #include "SentrySpanApple.h" + +SentrySpanApple::SentrySpanApple(id span) +{ + SpanApple = span; +} + +SentrySpanApple::~SentrySpanApple() +{ + // Put custom destructor logic here if needed +} + +id SentrySpanApple::GetNativeObject() +{ + return SpanApple; +} + +void SentrySpanApple::Finish() +{ + [SpanApple finish]; +} diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h index 7b968c00..403077f5 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h @@ -4,8 +4,18 @@ #include "Interface/SentrySpanInterface.h" +@protocol SentrySpan; + class SentrySpanApple : public ISentrySpan { public: - + SentrySpanApple(id span); + virtual ~SentrySpanApple() override; + + id GetNativeObject(); + + virtual void Finish() override; + +private: + id SpanApple; }; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp index b7ace0d4..958cdf27 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -22,6 +22,12 @@ id SentryTransactionApple::GetNativeObject() return TransactionApple; } +USentrySpan* SentryTransactionApple::StartChild(const FString& operation, const FString& desctiption) +{ + id span = [TransactionApple startChildWithOperation:operation.GetNSString() description:desctiption.GetNSString()]; + return SentryConvertorsApple::SentrySpanToUnreal(span); +} + void SentryTransactionApple::Finish() { [TransactionApple finish]; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h index 008ea9cd..ad2cbda1 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h @@ -14,6 +14,7 @@ class SentryTransactionApple : public ISentryTransaction id GetNativeObject(); + virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; private: diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp index 15860705..c46c6399 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp @@ -4,4 +4,24 @@ #if USE_SENTRY_NATIVE -#endif \ No newline at end of file +SentrySpanDesktop::SentrySpanDesktop(sentry_span_t* span) + : SpanDesktop(span) +{ +} + +SentrySpanDesktop::~SentrySpanDesktop() +{ + // Put custom destructor logic here if needed +} + +sentry_span_t* SentrySpanDesktop::GetNativeObject() +{ + return SpanDesktop; +} + +void SentrySpanDesktop::Finish() +{ + +} + +#endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h index 25e8dd57..18c2abf3 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h @@ -11,7 +11,15 @@ class SentrySpanDesktop : public ISentrySpan { public: - + SentrySpanDesktop(sentry_span_t* span); + virtual ~SentrySpanDesktop() override; + + sentry_span_t* GetNativeObject(); + + virtual void Finish() override; + +private: + sentry_span_t* SpanDesktop; }; #endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp index a0620656..847de00c 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -2,6 +2,10 @@ #include "SentryTransactionDesktop.h" +#include "SentrySpan.h" + +#include "Infrastructure/SentryConvertorsDesktop.h" + #if USE_SENTRY_NATIVE SentryTransactionDesktop::SentryTransactionDesktop(sentry_transaction_t* transaction) @@ -18,6 +22,12 @@ sentry_transaction_t* SentryTransactionDesktop::GetNativeObject() return TransactionDesktop; } +USentrySpan* SentryTransactionDesktop::StartChild(const FString& operation, const FString& desctiption) +{ + sentry_span_t* span = sentry_transaction_start_child(TransactionDesktop, TCHAR_TO_ANSI(*operation), TCHAR_TO_ANSI(*desctiption)); + return nullptr; +} + void SentryTransactionDesktop::Finish() { sentry_transaction_finish(TransactionDesktop); diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h index 81b25d4e..049e2d67 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h @@ -16,6 +16,7 @@ class SentryTransactionDesktop : public ISentryTransaction sentry_transaction_t* GetNativeObject(); + virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; private: diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h index 27737898..ce10b02a 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h @@ -8,4 +8,6 @@ class ISentrySpan { public: virtual ~ISentrySpan() = default; + + virtual void Finish() = 0; }; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h index cabaa303..60144dc5 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h @@ -4,10 +4,13 @@ #include "CoreMinimal.h" +class USentrySpan; + class ISentryTransaction { public: virtual ~ISentryTransaction() = default; + virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) = 0; virtual void Finish() = 0; }; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp index 7211faf9..5c00ebb9 100644 --- a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp +++ b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2023 Sentry. All Rights Reserved. #include "SentryTransaction.h" +#include "SentrySpan.h" #include "Interface/SentryTransactionInterface.h" @@ -16,6 +17,22 @@ USentryTransaction::USentryTransaction() { } +USentrySpan* USentryTransaction::StartChild(const FString& Operation, const FString& Description) +{ + if (!SentryTransactionNativeImpl) + return nullptr; + + return SentryTransactionNativeImpl->StartChild(Operation, Description); +} + +void USentryTransaction::Finish() +{ + if (!SentryTransactionNativeImpl) + return; + + SentryTransactionNativeImpl->Finish(); +} + void USentryTransaction::InitWithNativeImpl(TSharedPtr transactionImpl) { SentryTransactionNativeImpl = transactionImpl; diff --git a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h index dd31480c..11828059 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h @@ -233,7 +233,7 @@ class SENTRY_API USentrySubsystem : public UEngineSubsystem void EndSession(); /** - * Starts a new transaction based on the provided context. + * Starts a new transaction. * * @param Name Transaction name. * @param Operation Short code identifying the type of operation the span is measuring. diff --git a/plugin-dev/Source/Sentry/Public/SentryTransaction.h b/plugin-dev/Source/Sentry/Public/SentryTransaction.h index d7f14cbf..081a9b99 100644 --- a/plugin-dev/Source/Sentry/Public/SentryTransaction.h +++ b/plugin-dev/Source/Sentry/Public/SentryTransaction.h @@ -7,6 +7,7 @@ #include "SentryTransaction.generated.h" class ISentryTransaction; +class USentrySpan; /** * Representation of an activity to measure or track. @@ -19,6 +20,14 @@ class SENTRY_API USentryTransaction : public UObject public: USentryTransaction(); + /** . */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + USentrySpan* StartChild(const FString& Operation, const FString& Description); + + /** Finishes and sends a transaction to Sentry. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void Finish(); + void InitWithNativeImpl(TSharedPtr transactionImpl); TSharedPtr GetNativeImpl(); From be52bb751a948393e7ecacac4431bc529144e726 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 13 Dec 2023 16:33:35 +0200 Subject: [PATCH 11/29] Add missing implementation parts --- .../Sentry/Private/Desktop/SentrySpanDesktop.cpp | 2 +- .../Private/Desktop/SentryTransactionDesktop.cpp | 11 +++++++++-- plugin-dev/Source/Sentry/Private/SentrySpan.cpp | 8 ++++++++ plugin-dev/Source/Sentry/Public/SentrySpan.h | 4 ++++ plugin-dev/Source/Sentry/Public/SentrySubsystem.h | 1 + 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp index c46c6399..3485c64f 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp @@ -21,7 +21,7 @@ sentry_span_t* SentrySpanDesktop::GetNativeObject() void SentrySpanDesktop::Finish() { - + sentry_span_finish(SpanDesktop); } #endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp index 847de00c..3318ba72 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2023 Sentry. All Rights Reserved. #include "SentryTransactionDesktop.h" +#include "SentrySpanDesktop.h" #include "SentrySpan.h" @@ -24,8 +25,14 @@ sentry_transaction_t* SentryTransactionDesktop::GetNativeObject() USentrySpan* SentryTransactionDesktop::StartChild(const FString& operation, const FString& desctiption) { - sentry_span_t* span = sentry_transaction_start_child(TransactionDesktop, TCHAR_TO_ANSI(*operation), TCHAR_TO_ANSI(*desctiption)); - return nullptr; + sentry_span_t* nativeSpan = sentry_transaction_start_child(TransactionDesktop, TCHAR_TO_ANSI(*operation), TCHAR_TO_ANSI(*desctiption)); + + TSharedPtr SpanDesktop = MakeShareable(new SentrySpanDesktop(nativeSpan)); + + USentrySpan* Span = NewObject(); + Span->InitWithNativeImpl(SpanDesktop); + + return Span; } void SentryTransactionDesktop::Finish() diff --git a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp index 02afbd7b..30835c15 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp @@ -16,6 +16,14 @@ USentrySpan::USentrySpan() { } +void USentrySpan::Finish() +{ + if (!SentrySpanNativeImpl) + return; + + SentrySpanNativeImpl->Finish(); +} + void USentrySpan::InitWithNativeImpl(TSharedPtr spanImpl) { SentrySpanNativeImpl = spanImpl; diff --git a/plugin-dev/Source/Sentry/Public/SentrySpan.h b/plugin-dev/Source/Sentry/Public/SentrySpan.h index 2fa3c68d..8475cd9b 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySpan.h +++ b/plugin-dev/Source/Sentry/Public/SentrySpan.h @@ -19,6 +19,10 @@ class SENTRY_API USentrySpan : public UObject public: USentrySpan(); + /** Finishes and sends a span to Sentry. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void Finish(); + void InitWithNativeImpl(TSharedPtr spanImpl); TSharedPtr GetNativeImpl(); diff --git a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h index 11828059..ce39a167 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h @@ -238,6 +238,7 @@ class SENTRY_API USentrySubsystem : public UEngineSubsystem * @param Name Transaction name. * @param Operation Short code identifying the type of operation the span is measuring. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") USentryTransaction* StartTransaction(const FString& Name, const FString& Operation); private: From 0b4c3285dc7b32fbec7239245fae32e610b13b91 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Thu, 14 Dec 2023 14:09:39 +0200 Subject: [PATCH 12/29] Add performance test workflow to demo --- sample/Content/UI/W_SentryDemo.uasset | Bin 179245 -> 258770 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/sample/Content/UI/W_SentryDemo.uasset b/sample/Content/UI/W_SentryDemo.uasset index f63e72b36c8c30c1a7ae459c8689c99eabed6bc4..66291249cfe4fe262def44f84753d969ede06b2f 100644 GIT binary patch literal 258770 zcmeEP2VfLM_ur$1Djg9)2puUQkN}~RN@xj?0D=-O$t6URT*zI301}$mD1sCf1w;iz z5R_*5G=L2&C?YCqC@O*=2uLUSzu(Tj>|XA2nS}fF(?1u6+nt?#GxPd;GrPBI*4-;E z=jG*%DXl0KODW1(`o^a`mF~MncfQv5r*da!E8C`xzq@%Es+&`{!I;?*@f&{JUuXT- zAD*ldNp&+0JdpHqpNL;h4eqzT<@O!#jG(&42cn*NC!+bfP%rPr`(|!6x1qY9I()Oa zO2i`#cTC&6VRgS7ohnjYFZ=bq(}On$<ADRyW@p|W zpVNx!PLJLE(}}>v7b<<{ySmR?q3v2yU45@oN*((42{Wfye4@jBhQ|!Gq}tOngDojm zsxPBVGAYV2D)di@Z=opO^c~W%Pe=bgodW#2`1J`1>f{&f9~2l86c7*;+^1teXlH+F zB0k&c%hp*@66kvt-MnRr0F?cZqI^gtdaa@yIq_2ZJMUHKw;;XmxKMN2yT4@THary_ z9=&40)^B4kjctdH0S5yqOT#cJpKVVz)u5Wzt)oNyTMw}$Tg)~~TmN>x>UBj)KNwTU zb-GFYG&x$M91h;1{#TsS=p`;GM9Dm$r8)JtTDw*_in^70`L5gO&OY(+ee8B~+}IRC zpR(abo1nU)HZa{{j*m;vNQoVuWFH%8PB*96ltH<#x2>sOpI}anv&2Uxr3NS2(vr=Y z%Cy_hcvZrscm)ofQx zZ4b6_x-HC-8gEHgDsHV?rFub-;e?0o-#yqJl;UBjeMM!aS(HcaO*$gRmTu0BOt+?4 z((Rc__~v6tUTAN#(Yf;djwQM3#C6OzYPmZh>hz*&PW~RBvAy2^fI2@ypCvujoE$O%3^i`pjuxPAY`87d8kb=+W8~j|-fmW9Q5l($ zZVAmujZ?mPFXBOU5CbfU=D5s37E64Tm98dQhK{o&53wZJlm#2_?beuX3nXqxOQ&HG z&eE)@)D6&?XtrAdlTzc8QWKT3mhBxtr2Lj)8AqQlwp>18%Qz3 zUZJ(aO)9-BS6`Gf3OA+3hfJ`?!~|y8?bg&7LFkzN9R^wBEiw563AARQ9Std_Q%ppv zXdIMG2(oC^F+A3xgnYA|rmuu8+5e^4xr-ok8ZHZHZaV5H-BII5nt9*vz9Y!`&^Y31 zcT@9|?y+xnj~#J^_BhaDvzZg!cGX_Gt0$)HZkSwDS5njKAl+wJUu%VtF8WM2+s3*> zpuc<%L*^?fh8~P&j(IWClAd5qPk};q*Qu{`rwiAWeW@&U8f&#CEA1-9pM(Sl(n-F) zt5#<7qtJJOR+5GkC2z;%7oaX(om{8f^=rpZeAIdNxwRf^c!qS2+Y3n<AainZtT}F6l$o@rUAa{E$ETq-Q4LiaW1Miz5DO`pRAFI< z-T0_a>%v;*kKEywuWB5v+0?}?EZRo!@hd8LwJp4@>kcN*PMg}o;U;V9gqWdrVyDC? zYZOu0VLaaDX3ueMj%HC-ag#&y&5OK{~fK46lJ+?3UisZeKX8Tx&d)FQ6@?3N0F^hhq$^UR@-QuZTz3LlVheB02 zA}us-^_y_2^Eo8UVh>48OiHy3&4{(BM$!SM>AmtR;dvO_M<$!?&_ZzF92)<6wexm| z;SM!VurU2Xlad`>ll+(#@$ha~xEZL+it~2cCAo1P?Tj1kKhboqRrr_T?(R^flzrC{oIF|25~J z9*mXw%&jc_I(UJQQq{b()>vSvw+{NvQJLtsl|DDF$6fk$&^eTRB*-}$Y5sZbChJ;O zP+Qq8iB?jJm!>U#znY<9s5RXV52;r5yFP~w3>s^R8)wZhB1E~?V&N%78F9&Gn@zD# zwC%!Kkajj{A^dmET~$85<3B4^1PJ#v$eL_TS0?T3KFd%UY)P|^Rel{h=|y#DqNPoF zxc4VR;Zx>2PwW|KUYt#t`^b-G6_M=cv8q#}gudT-4@Vdo6fr0`W)S&-%GOIATHUQe zOif5iB;QH(WEgCW3)gE_{EI0n3~Q>*o}LkBS0-9lJR>GZCWIW==LU8<3InE^6tZB5 z0T$ctIR$e^brWKYx<%YS>mG zJm^PYCD@W+&PcW^GxDrG(TR2*mTpcP3r|?tdi0kGAiZ`$*p+G@T<8d_5G5#3y!6G1 z-@yp+n$xaay7u$}41x(2F_EGYF}|?Xd)tYAgoDovpm@g~>Wa9D@8JvT(C3f8zX@h) z^dCy!eWZdqFcGc}Fvn6&&uhTQ%GNHdeUTaVa&;Wk||2Sol4$;$Hu7u2P7p>=$K+5<|Ei}js8<6eh<(a zl0xyG;yV&C$RY6E zBCQB9H;t&&M2tHlg{i3wKKN{J5GW*dLQ=X_VB)Q5rq3X=A?fMXbQRh52JNq^2v9># z-i$Fl!|KDwQB^$+2oqXD`Pr)mxDZ6CRyT?sVA@3!+3C# zLG(#l_S1T3_YiR&9cxKXgiOU4{e9*6PBXTvs8?GN=~-Zv#y@WI1=GTw5|>(((oGvC zg5aS^sY&El4&7VsPGBfOO^MXXeD+hg#cF~iAJ^B`{&)ie3?$QP-%D`Aiz%21X z(C3sN5d}L%o`=zsP=1|UtpkXyl2MJ~SI_)p0*0xc1VvsQi<$IQ1tlhks7VQpJ}K3* zVccBF(b`>qbVA;Yn85zVwFDq7JT)!DPJ*kvb#>SlQ6bE}QlrVL>%c;IDzv;ZM)n#Wm$8>0TNtbP6KCqVn37%Ix|3qN?x02+5{mturk z&bjvI%Z7oX0nM9?V2tn)Ir*hpfFJvH3`Y{Uza>-I+GE6Ga1$LjYOj3ifA|YN^Hrv_ zd~iE(NY^G>)8lPQP}@OUL5u-ro83_x+LX$7mD&e2Fn}0A(~gI&LxMzACb=bQ*JIkh zRv8o@U`enmK64)a4ZW%UxN?7+g^NLv0pzQi(}nj@E65fK*`m5}lwDBHzjyE`Mh=|3 zuxL*j+S7491^pM#m;oXWq;QFt;6iS6E!;lPJSiz9BSraOP)-~MG|-YlQmJy?#UbO> zRHvv?Wnpf^E!!a=qKdpRnvXK=!~@;I)S^{=%scc@*m2cS?Ow+$`P#~?T~~T z2SM6_*7%HM3uTMR98IvOmLje3V`gAP?K+@Amv3>BO5&#kGzeLZao|e+3}>Dz-%qWy zOytnoro6D`k1aq2OaOaxW17aC2k}s+&h(27WnRYQQPJ0ulsJ}B#6ve-lB&Q}FS4R(@?Lz7ItEiVVQdBv#DIx8; zyaIYyRi|IE^k0?-g`pWr&banDwB;ml7?F@*L#}ygsaKw9iF3)Mn|W`=y686|RZGt) zpB`$~6n2?vtVaJ)8P%^(4jh1pR8=rlxy)~(gfIGIwz#dph4nX*#;ZM0idc9O<-4C< z3v3D<5JS3735!p8TAiJvTBw||XrOwpS`FRwL6z?tATbe14nJ}A0Bf8%Icc&A7qqse znaO_DuxCCDq(qwI)jX)*{aNFIN{t`8{kDHDNG?u=;;s9`i=#jcm9MIA-**h8qz+Y} z%1b*gH&QFK=uSBk*xd_ZifXeh=}}ggDpfg$(gJ}+iCW$LX|)Tado(Tj-4}V|F-PLZ zIO2E;KPae6RX)r$m4ohPHVsWSC)$)l*1KLa)`AUf%9|6*?!hQYmnJL2K6j!)eMU<}lMH{<&9{{bWS}4a?e@$F~IZy>hnLpg~^dc|_RYd{YWB>F0%Rq!U z7iJ-)*07(oHi$Bn&DKt>(D%@!$rk0c#E!qINFau=z*A6N+M|!XctUNGY_%(cy_Y_r zj+{$o>Rw`AGIs<{%rleikSidx-#u_y$oQ6A~b}q%{TV1 z4ZSG1iyV&p&|Fy@vnNBHjy;{ZO6j&My{UFL;=9G(OGl{VQ7v75tM*D(VXD^X9F z!f|4U(ccx&qlS7#ob*nDT*4g?*2b&TLz|&nQIikty{`5hDuA)%Fwc1P%jxK`fD7p} z^*{=^*s*#DEeVoK|6`Ajr-BAilyr?sN>jF-^m+$2EXtauT#Z?_O&t(I(dnV-M*+Nm z7~Yk%qzgPny;=+Hlz_aWKl2T$iy2AU#HQ41vbv*MO}~;@k`aK(c!_$)-c zHt6TR%GMeiJ`v}_($4mMZ#w3|W=prB=N}8MqP2voQgWx3^VQDPm>{ghfQlIGFiIR* z(>nw!(e0BffOCg85;1Wjb)|SwLe9$|pM#(M@FbsRU``?UQIwrl#+WQz$NnvYmJ^U)Wb;K3^=4owY)|HGGqtm&3$?bgfTZF>S|d`%0_=Jb9r z+*mCJtSoHb@C!I}+*(wVyPTPrj&&#H_J#IKV1I59Jupn^bl4p{2QII)xf>~Q(EMKNaJ%BUR z2@QFAS%o{H2@MxiAe)0~-+}2Amj6*ct7}if&`T0tY;Z`|>$Z8PMRd01$=a4UWf@nycF{gScpE3#E*8>wOr7b`EC^ ztD0)+;pq*vx&nxHO&BOzX;NO_^<)*ue@H6%$-~}#&yLP(_yu`PdO3zF3``dd(;nNuArR1V@Ott&f@$Q5}KN z6;G(K07o*)Pt&(P0bQca*wX*B32di%v5GD1;o2aucAkIRvu$3msJ@KmYbh1BZQl>% zv-72DkiLy3WvG?vDxrv7mHxG7Ka8;nHq_T)lq=OY)KZ-T3eD0p1^IS7`SNVFAGMBh z0)Kper@9HiQIyX-cHkXE0HU^_1m}IUZJ-WVIBXko2CrBB8x^o>3BOwO@?EM&D5`j6 zMXCDzZ%+Zu0=QviLdl$TxU&vKwZZm@u|aBI`m%^JqSN^A!AALuHTm+-s%U8Py3Opi z1SVT)yT> zf%<7eo3ijJpbT4R)zRXr@-pq=>Z8?trq$ugRl9J>cKsyV^_OfnK(gIH$##Pz+eJvW zi@F4=B`M7x)$-;R>)dP%kmlWZ3y*)CeLU6Mq*bu{16 zlI>z7+l`TIXO?UiC)qAuvYkb;U4mr0M2U853D2<-?bc8`bwe!aGIx3n?R+HRwU=zy zL9*Q_$#(Zjw!2@l-FV4%>5}d2lI81c`PpP`lo4+V#|~L4NPk?4K)s z2{g0|as!Wb?e7F{xf^)Kc4H;rohEoG67bOOm_)l%)Xq<$U$iqz!doiQ?l*$hSEAn& z)DHVN@o_~D#vj^U63yJUJ5KH5CEHmf+a*Z0`$Mu_X$idiLi5m?xt%ZC{VCBe+Fg`v zcUiLC6^V8~Q@@KO+WkcB=t&Xvpe_!QMHRy1?l?puO4+C{sT+F015GOoBC z=n*0T5AB9Ywma&gU&bl>;IR8$T)^XY>|3K zx$(Q=&*oK24nN1B^Pq;2E5JAO+j%PZ?Di0RjM5eSZPh$8e(XYY1%F+2&%noi0ax&s z*YFH{to*rxKfk7D;A7W-EBG^Nc?Ld~rChj-EdIml=tGF8f9ra5N|3`!Vg#YQQ z`Lf{Er>Wpmxq)ZktM=Um{53ZL{|CePvA+YkUDp4}T3@~ID*5=0@Ug(>3jT(MC5M03 zF#fFsA8RteJw6dI2=uAzU2^zm4Dh!QtY8f%=!4Oo*tF#E_Z#3hCYZ?Oq6@(PjS4=q zn|l^MpaK2~0H=?&g=gUd?SOw4!09uhrDx&84g>z508XF2tvm}Kb{Oz4062Ym-0fNT zu)~1Q>uJ8NJqsUp81Uz5yt<%`XW;|35q|x4C5M0B!2ginMpW>@(0i&!it>_==iqy5 zFpcn!dk}tO7x0_4_uTlKxPTw$LHJEwz<*(F zM*bOflkl}6D}cYiF#bi@HbkGNJP7}Q0IAB?Q`&Ls@SDSr21XtK*Dm0{cN6e)4f+AH z6;6l;?xBCw?aK6hU%!%%-xYj71N_HfXy|kQ5YNJgpAPtULQ&J_>M+m3ho27kvo!yw z_Gr(-ho27k?`!a%Gpcq}drSe}PlNyIX3xUM)?&c_OoOlN@GN|6k_UX&KcBtrS@?MS4d90$P@+%gk39*S4*~e4HU8Xqz_ainR)9ZIldsL+dlo*PfdTwY8vGVVJPW^(27jgo z|LdPU3*TFVf2YPjBTjl2eq#;(P>sH}GoFRtM1#-v<%5f!h2K!W;bR>G@Goonp?mX^!@ptRDab9Jt>E~si3ZlMwP)c& znlOHbKfIl1;RBix{$yXz!Ux&`zqbbVSwGLhXZdCLKXmpie3oCve}itGh0pTK@O$?x zIsEGd_!H0cFn=!9z{d6QEPUoqhJPivmP<6H_Ef{S^qHnJ6lEc4LKz{M8BI}1y5H$K+iuEjfmS2WH#Nt`_oF5L-#=kMqv+y}T z%<#`9c@{qBhZ%myWY5Cq{4m3xkm_0ZoF8WRZ=`t^KIexS{`GXv!Ur_y2jmD z`k&!HJ<+r9SwA!UKQlcGpY=22KXR&P;j?~b_)kppEPU3_4FButo`ui)nc?3ttK{%6 z7SPXlMx6EYCz}6u?}MI&&-(dG4StJ9JqsVuKw}Quugotw{67oeXNUnmH#GfUW07a! zGkzF;)g_*V&-h{Zm7nx1eAYhDCVE%t!%m0*q&a?2D zz6^iQ8qdN9H1I#epSPjp@Xs0WLm-urKn{H{hk8(S^cnJ+4j-4bCh9fge*`K&mgD;G z4RzR``MY)jh`*Q#886$KyrGhM;|@eDuqxM2J@sNi$TgYd^`%mMg~wc`_SmmEI@$Ip9W0Uvvb z@u`&UIru4t@$-8_y6*HG{8R&cet${kF3-Ye{nK6RYr}5O!M7U5-$*-F!B%dgDd_I_`oyc2Ygrj-|0io!FR>~BldU>zAOHJ=p)a;cg6o7 zeC%2H1^NHVC!T}vivRsT^BjCv{2%>!$>D!fK>yne@&$gyXU<;Fzy~~6@VoCTIsAi$ z@vkEIcoz(&0R3NIo69*5!)N~C=kIRgjX>0=yN@gV<7)iuKQnyOe$V2E{bz=M!Nc%5 zKfv&hf92WnbAEu~e|NyM@WDGM4F9vQJqw@n0}OxXLC?T{j>hbY|F?YOS@;F{fA6=R zg{68b7aE;;@9R#(|q`4l%*P&^go>8GfZPoMZpIx7(RH2;g>(>4BrTs zk6posu0g(k9TglSH~)fmjD6N?v}5dveqKASN#8GO$JlGURy)SN-IuiEI`q9+JH~$4 z4camGuD+rjW1s2E+A;QoZq$yke{+*|jJ=kxYRACYLhTr1U8Egj_TWX-LANc|j-iK^ zXvZKm@P#&zZ@e1?$G{_=1HmzP0?(}C7&&0P-wnrDkHWh!aE!Ghy!QmhSm#-x9b-*q zrFPtqzUOPljp!TkKH6hn=PKm|WKpqEu~W&Q5=`%vKsQ|a} zRC-eZo@P=hO$B%Z{y-PB1Ac%j%pdpyj?fNtA4BC?5^$UYhp*5v@K}Zl+HKIz0Rwfw z8E^?)0e7ea{%{T)Zq#6bpUhMM8*Ok6?SNafDM#PEsbCJlR8~;kbovJ0p*{Kl{m|bm zDwszg74UK>m6g;UyoqrKQ32csso-9~>rEwz3V0ZGAyjZ5_+|nX^pA7E#5M30_#FI< zzJNR65xfSzkD~%O;4R=0d^Lv(>cX{ij2Cl5T|X+g2mCyl3dRZf7)1rVj5d&!`>24g zW2vAG@Wck2!K3-~d;E zIfV*f0Y~6{;0x#AU7UB(&Ve(W1An*_!<^jIPIlfVczHvT*3giTH!a0s{FY0kVi3(^5oG zuV{-pe4`%skE8-vXpj5BGdPES=uHK3jdOgXE?7IqJOZg;PQb@>D!r-T7_x&o<9H?& zrr|6)hJ0+L0+_R@bT^zoNXJ2j^Eq@3J;3cBqGQMu&M^<@23&{Saorxu$X!j@; z^wWb1+TmlOGGF^%K;IbaTq+H;<9YOb7ZtQyt$kP3zN=~9)wS;$+ILOuyO#D{Tl-$G zeb>>xZ`Z!-YTtKg-}SWbJGJln^j(1p`hT9vi&S2qvWCh_RMt{iN2MVZv_n5HQ`tym z1C>{(yh>#gmCaNdQNcV{QF+{O3>GIZa9qW3T;6ben|2HuKBMWwZFJn23hF?g?Nq#}bfn@(r7e{%nPfi4!8o27E=LEmQcYQ7Eu9So}hxcFQWq7ETsax zETjV7U|wRp03Xj#!Tg`40-QWe1vpqv1@l}<1$bCN1vq$$io*vT&^XhH_<7&t9agIK z3k?rd@Jdn>I?p>qqExAT`HJPeDpjmdu}bAiRjSvoQN3!_>W%NHSF3)jrmgR8*|bHA zwjFwRZri>``xY&_3<&5E6w)`mZ|hDG(St&xdWZE50T5H=Dpjgit?pf;hIdH27VScc z{mI*@R4w+Nu5QYE4?JrsYf?WvEmNt|Wy+TGDqo>uC2Fv(no`PCx^$^B zrOTErL)bSxK-ZNr)yvlK_U%)y=3ui|Oxta*!;t?u^o@6@?VK-Zw)kkGKc;r)h2MGqT3;+~Ol@s@=_d#PRg7- zYxaY49(wqZM;AS@c*)XbPd@eZ>gQj0an0IwFRg!V%hqk%cf9__o4enA@BI%x-1E`L zU+(|vz}E-A`S!abN00sV^DoDLJ#q4nbLTJodGXTaD_1cu%#%N2W(DR|o#s`lOqtSU zyf80QsfkodS1(i6+qYbeK7+l?<7+nVFr$2}z=hAfx~oEyjziAYj!plfVx6XbN1FYC zsi`xQo7l_}%`AUn`n)No z8}*(UUOKc-#X+WBKb>E(;-P)}%4O8PKc!u6PR|GCeL5=gjhvd5+x6btWKG}WKW@w# z^Ld+%zmGin?ZBOtAK&u$&;3*O9P?VS^NIQ6d*5Lnc4|etLCWhpS*q`u7d6S z2OU0|x$5@|$GpDY^1FTclm4CeA1n3C&t2kol+8_!pa0CleaF6>-fHm3+uv+(WAXXW zug~?}{QA6~AE~=4wp_XK2N%8{(rL-MQK4T(#Mx@J>)dho$K?i<3vcil{chu$H@3dM zC2iTTb31=Zt#o(orjyS{?lXndigYga+&j#S%6t2~Od%8Vl(REWWS{@>OzyRq+~x}|_p#?(IYYG%Y|2w=_O4&H zc21tMyV9CGW$%>iYp1g=Ki%s{+5K6^-_E&lG5h*JC9T%*tURTByXARGkKgl@Gu!f% zqhs@w*|!hOQ&wEcy7XP1@`s;k=ivMDl7*v?)9tB+BdUGMvl87)_wki%57J8$1nV4;*zPW*FSg9yNd&cj9=2A z{ON^|1qVPR-AO9=eDxh4-d%+ zAJuGYZ2Igcd|NGf;LwutS4JEwGi&aISC;42zOnT5TW2Otwh*LM`*NzDTG+qikL$e3 zECRA(tJYmU<)wtpTc_As@AYll>0*sNzOOe}Wu8)bnt$JMo9B%_d11t<`M=khKQ?Yz z{moY%eP-$#-H&;F`R0tXSNAL`9aF=1;gVxLdd*puTl>#tmo~k5Z2g}Xu5C3fupLW$ zecr)S)9&ezS~h;@l4jwGN?D zbE{RVmNxoQz@Y<^uRicy)pxHos=Ms%-8X(8zF^ZQz3VUCwqf3g-A#71|M1Sfmyu={?gPYrC{r6TFh=fAezAx6|4XXD`dTT&LIN^~#xFUnzaf=lzxE zzoZG3`Y=za9dQg*(Yr@TM-#BtPv)7TaX~5tU`Ryd`%sNzCC9Bu(o|XRm&}>Z$BHS$q3u zU$4~gdUV6$jiE>LZoBUKy8166_pMrbZ2#J0{Vu)#LfOc>s~%~%AnSww9CSDQ9?g8Z zZ+?A28>P(gndQcR(BnjlH*3DPKJMLi0cElt%*-}Ub%Ak&%H*+Mz41%Cx2L7P^Xz)R z|3uZTzT%CB?d#s?b@j&szc`|>z}!tIW<^Gq9agQ@`Acu?E#2$!bw8dM-8`bTa?GpA z+{%-)+6-Twr#Pa=5xtRKSs`hT2rAe^&KLK36+ZHb7lxZ0N%XuUhI%)+QF~_r5*2?8 z2Rk0ybfwh0c}h$rQWV?b909JMztOVs(8!lh&I_3SdZk|m)lc1Z`iVn+ru~<1&+WY> z>)^XtzqY>5KlAekk7k`3)ndoB7SFyJcgeXj&$E=;!s%`Tm}H{L2@{ zp01oc)NI~a_MMfdvsU&ypY3K^@9dagZuT9W%I%%7tLG0{dpC9uv(9)uwAb5@be#2J z=@l!F`ybD7)Ae(;wpyCi?o(yf(hu)A+0**Rnp!(T~@Ie*4A`OmXmZ3nMk z9lpXiNz$JNFum`Z(`eh3X_LM=+`80_Yb#d2{C(5@wGw)~u<(4f%)Q5Nw4O~$ku>mg z*;g9oDX%`CTQg6&Iww#0DJ%Db>D{`vp8NM*{$*-Lp3-PU)^A6$F4S2cW5lfgQ?FcJ zdF}p>+U9=r)9J^1p6NItt^5b=dyFj|w`*ygWg~<4jvrU%^A?T#7S3suGCJUuhpsgw z*J0HIYj0eMIs3|#UCS;meKYu@@fl^>B{prdsBF`e(V71H@|5JH>khs7ky`%v3}%?xnufGpBAN@KeC*-y4{3;3+F;7CtiDJ zaf>-^;s@^D{YhP~8+|WajjFI|MDtE3ht2Yuc=e~>uI&Ht;SKM`-#=mc{3n<6@V&my z^zOS$%T0LfRGle{arxRb7KQO1(@;e)CoZER(x7>!u{mVSve0lx3 z*<-dO*N^Z0dTtas7k!hrpU&OC^~3s^FCAJtdG2prZN4oAZ~Z8s*E=zPtSXcH;r^^& z8z$u`(}rJf>3H%DUGb(XS<9}E%{87Vj_n*E7~B10Z)&~bk7plz@s))&4}G_*(o;Lz z);AC8w4?s;pc}7}YrijWe{P=QbNOJ8W8JRx-`Ke6jyGo})|@#kpmhC|)JcA(1of9{ZwC`uy>0|&pYtN?ec!XS{-&!jqrU0WZ1&vphjvV_RX?%e5+(D@^+pZ< ztU-Fa;f*B?&rZ+!sz0f*tc%gVR$b@Yrq-LUd|9q)z_Fa~7RNNMW2t=4YnjKqO7Gj} zwX*QJ-8k^&fIX4#)H+@!>(4)~4*l+82isFUB1#|sZP)R&$9vy3#fS)Ui}O35T=mfI z_%**+hac;H+{^35Zl}LE)Ar5Co}-lurw_iZWF9v(HIDPgkn|=q4)kxm^n2g^SMrp3 zgRH9$Z3|jIr;O68X3oK$TTRDT>a&V64tw~jx6cOno(YVe@nQWZzFxP_>yE;PHo+eG zmt4H(=`(YBy_&q}*y(8#PLxVHckx)GXO6e{^6h$WtZb0Ar%uDGmt$^}U03?aFRdAG zjT<(wZDRY|oBikY((}G)aCS=UrmXuXZOP4~(Dra6a+=2_I(YNzN>9)IioEdi@WT60 z==;Tum&gl0PG0!*Srm-ElKZG~xYxGsWMiQuDALm zsm@nzUNg_H`q})$jasIi?)%9KQPLb~TL2F^1;?=Nt^zwW6 z&d>g-Qq>z%eR5}hWm<3~yT-_|SEnw$RQ>bMzg$+%zf7DmvB{U)dmZUY`IHB9Zk)}& z9-VkOB541gJ=)!~sqOWT>cp-(WB%Ykj|FdR%()zVA?C_CQdFCA{Y?FHYi{hEJBcF1 z9Eu*^C+%9{Yl@4yQvi8}%~sA})=$HUP`!@S{37f4CwWTa?92Pg`dl8g zHRs9^;{2&T2v@JS7-_wJmgM4{+bJA@Jdcq~up8FQs}p7Ozh|M8h_T084P=AD%+pLhK9 zbKg8=K~(nHSxL*6WNo<;H@(bhqQ;bZ3gOz_jK8GIt@~BTK2<-i*KfHvz)+$m8fnweNbOR~9C?A_3nQ0B zJMdtZf!wPq#u#~Ffg^_v&oc1e7mmc9-pY{|{+@|$$f_}E(f$$pkL}noKOo$3{=Qg5~VU`cOSlSv6ksMN7v`TFC!!7{Ql=- z!(QL9sBB33?7r{SKYZ8Ds|U_p?vvqjGUV*clQHLi+@E{Rs^)h+J&0ci?}=+Vy>9iHRRizN+VQbTX;gRTvETnZ zapmB0%Gnh*y~*L(%PQB74&U4QnYmYO@Aqjlug!ypUixkHlz+sWJ%M;z&#vS;x9Rog z+3c&8hIMwNDq3J*E zdEuGEx4kzx`po4)+kCDZAzqu>nUuW7YZ*k0N)gwNWM5uSf=0ad;MyB!bFN2gyjJ0B z;nUzz8)SgJZoqhKH znDe{$=U%qv-c5SI(}Vc+;+(RH9WOK)d&M^Q()GJ{E;=;#$*W^>&#aBGp8svM8OqPy z98Q)RH@Jm&#b=+Ned4$4y^epqKBDnQTgHyAm3aS*&oU2^_WOHhr*hSQbr64kdil59 z-kbB3gJg(*Z95{zNUwD#e{A~J*KLRWc(hCH+V9S)_qc6lwFo;i zqi%1&KqJk`7Q5XNM13Y&(=(|?Y&nQZiW?W2ZW*6pNsY@?H#+pmuv^su;ZkI}B|a(6 zo@7l`H!MV?(xl9(@d|diI9?xOG25sWwnPk|P6!v+DHEA)O|zuiGXs-SgEL5qem%>xhtm%Zn{3|wf zTSm)P3U=Jk)nrQwjlvQiPXDDR(5*K0UhK2T2ODX&k9Ec@wv-egP$X3ukX;bXj;&kx zgwb$)qQiZR1Z!tJi3w_6JTcw_Yq`Gw?s$8-fxMsw^OgS;44JxcF{IV zx*&85b+5@Fvps2o<>p~wZ_=QQlp&V1;+qFD*J&u@WHm1 z{EH5p26oB65}ag9OEzbMaJWm|$*s;YG}%lXf{ii0egS@6I{J4|uZ7!!&31D{YI3I7 zSZYqTS=8+@p-F+bqHG+3ZIak+Vxnze{nc*_Sk#FBE99Tm)sDhwOQw~!81|$8gJ}ET z1lpE2p0>83HiNdZ#nN{yZT(H4Iuj8~z!^+uY19t28Pqm`Ht!X6l}B>3V<@MMA%e!0 zNtwoC)OD`{ zBXo`D@>;7j+KxGn;Kx%hCgNSsV3o%hX@DL~*o`Hg1XmVyopH!mc0GqXwb8aGzKAF6 zfN~k63ZiH}kQCs{$Kg!ppNbxuDUY-t%`1?4w9y^f@=_ZYrS8*PZ%Lo8_`NQjV4XeGTL_i$J?B2+oXeGJ+CeRZ_{gK5(=cr=;r zpFkt zfw%R!7PKdhlN(&}LFG^d_rpj=tp=%Nu9!xx+2%8^71q{79zTy#$I~!+m_WBzu6Ymk zg~v6F=nwuML0oE4J*EtLY$I5ufL*l^6V!2<$ek0txw>|{c1_1s$@o(5gDoLr#@N5} zHSUeIgN{Yx5o6RyKcgI)lsh~$54N4mC4GtBz^{q6j0-F}U#X{EaX$yv%K`L|Mi`YK z+7#cJ>}nYO`ID{*qKDT4sV0DE*pW(Cs_RPUo#@z)S_ILbKiy+e*f+RYnz5aPhrxPK zr<(h#^`+*_dN`CYsOt}7e`DyeppFEKweQW&i8VuC(%O^BXG$X2uIlcR8b3>iK6cFH zev&SDtUd-yFh-?jCP8heLB83Uu(j7`5=H$^rg z{Wm)+<|=(w&`K#PK6Q=k`YKD1dpYybM%s#e$DTwLA9@f)|7%Np=&ClzWo@8~TG+NG z9>NA=6F5j!y%Km8x`$n`zNv@$Wsgb3VX!LBHO;iX++&-sMykH+c+G=__W?c~OFk1K zJQF<>?HT@IOMo5{3A(e_UUZ8VQZr+H5C6h(E-j_*XN)G$!+!AZP4w`!zybeXw1>i) zUhu(q;u3gQ;E^CYh9_ci&_U6vZo+6ys--|rQ^}4v&cHF7qIv1^7q3NVh%Fed-kME3d0OiISYE%s*Ir##;Wn~1k%C@lr<7DhsYc09+K?5Z_XcM z8)qD0H}d6>-$0fJkzIo7L9p(_HE*Y@z$E_Mh|i zkWkW}9mq;|q%(iY^#u|~1=1Njn66Y%6F_ayGK5MOwJwWWX6vg;tB~8SsvL)^c<89c8J!6~ zA*#G~pnJmT*pL1=yJ9a%A-BU&^gFjs-a5Px5RRY8=$Z-R2dK|-^t?{b2 z;X0xZ{JGb=tmVN2ItRF-zn>I5j!AS_CY2+L9tm3j`TU--k51a?beuDOu=WB=h41l( zED1;U9QPVEF8fG)ueWx$i8xONtDIkQlmdn(MuOEP8CcBy!6e&=s}VJW`#Td?`_ms{ zfG~muTh@g%10rk0)Q|yie-NE_p?gdu88ZEG#-T9ocbtXJ^NYYgks5LzyN#A4Dc)O=mf6Re; z#$EK;s~CM^T?<(-lhRwN&w_J=2I^C2f9%17LCt(713 z+K~$KTCmKp*YG$Yzgqp2wqKVevi_;%^gSG^%kgLRupjY8lL5~I!WIy`u zLj825>(HneA9{uFWzzNt=<%YiO;~S*5;u<}X+xB2qRgNepR@KUqg@$B@J*z9L_7Zf zR+Le6TwPnknY$>%=z;R8!#^vg9;D6Ey?$Vcg8qSb96+24&D2Ge6OpTj7XjW6p}(%` zA9ND57h3wMH73%h%!A_l7AF?XiJXa-KXw1lQ_HJI_@1J1)pWjk4D#(Mh8m| zTjEcWht(}uPe>E27h+LZkN|4cNtH_EmpZA%MA?0r`EdRa^U-Ic&&5>)7o|bgeJ{38 zM!!ejzW^9|_8#l#X=>JQ3|S`ZHpp9ISg#DA8mv}^Q9)!gT&h1_x9X?z6n47!IE{RS z6eK+YHRgcrbniV<_w&pHXcq82ed&MrP?!_6eHa~eR`C!@N8nCeM~*3!>VxTj;iH=9 z^?t>{G<+wl|N7DY;B)XhWI$LSU^q|%83nac1dw?V8Fx$i5FRlHeXlbtu8-54*ruDvw#)Rz>m_2phA%;ctwXi~vlRO^P>b34QGvhM zmS*K@7e`_HHL!oHXe_g>bd)RDhNACf3JHH9K-H2$^T9XoNLXSj!GjX?!4W(u;X!~) z1F5IPV)X}&3y%O^0z7_y!Vz$XED*BV@LG_Sf#)KmLwGFMWd<2CQNBfHeyo9b*P*w% zD;w+Dam2$|<2I=_uxL!x()xpJCD6Sh6KUMn?oD2+&}%00pJhhJ*oPMddlXD_M?s7M z+k{*hB6WCOuwTLSColpjLG&S_eG@$|Ak$wB7jrPqKp#KwQZ!Cj--J>e5leGUp?gs9 z=bZgwT>;D?y6B@~OMfaLUX1}v1WTrG&ZYHJ_b6Bk?UghBoI^yG7Py2jnkLl~`|6Ax zE6r6rQ8T{KUilv6ol>cv3Hh9nF4bpctyMo-)AA;Mu+Sg)PCTn&E3{wZY`c*5fd&$~ z$wc?c;I*9ADn#Y+SUQ81#vjwq^^OG0_UD z%t%@4Iuc%Cc@P&OkB(80j8HtvW6t$i5bByCi1ijyMv+A%TVbLvO1;6wtdyM7|B$vt$TVhmJlyP=ODI5 z3<*ok863otVI)bw6>I}+w#ePXf(y-UqL;!Khbq7=B2+}?u>J6k&>tdL*a>)2xB{9W z69X$E@}6kZiTavWtp2bE2-z8o9r_mk!#9H!45mKdkD(r%06u|r??Qbd%0>prL@(@@ znP2hOzxCMHq)eCUgK36wgD#zEwAg2YGprHnv zAHjq-fInC>$O`Ni=mV}1-mah|c*B>jiQQQy`Vjyb9P<2NFR@OJOay1Mkp;n=kzYiu zSh<4N35i8s1T+S}22o$Lr21nm4#~t5PP(R4tSpA2k`eN;;Q{xe-`-!1BY6J}T8C z(;reO_98;kpvj<1v6l%~#NID>#PIc??I6F%jw4@(sL7-}Ce;maiC?`-o{uh^+>oa)&@Hz0Q&ulhaUAW;5s4WfHH%iOGIRTdUHMxIwR zjRjJ}lr_;0O~^FO|BJXACO>%?(|Sd=0D^^f!D6cnk0hvFa-J zYyuz1fTLbmR}=jRhzzZ`KX@ZzF9vXk-Huq#g(U;-;VHpig?EY^JoE?p0@e|y2UA~9 zO7&MyYvqVxBX1bBp}rDjq8}b9&U}Pd1>XcXf(;kDU%@}H(qd+?)9|lEo))~x3Z!5;#ra_$Yg1>g zs8>osDXX;-yZB7XD(&1^PqV%pMLH0B!c6o-K{9w@$v2K|b&IQoKPUn2#JebIt(4!z0l;i*q=qDlhM z##_zBATJ_t>q8k2AA+<_YLv_s0|^!^0xX?Hr0ka@44D)HdG^-{g?eQ`7|__`+g z5iS{SYM`}p?PYp1JX@Id-ze3G&dIP<&`hw8$hJWHLn9&H zhcy#f1gs>(pMW2X$W8dm(EhJT^~e6D|cKLFN%Q`k(zo$w$K5hH8Cc0jB(iG2}>&=I{OpJSpQI+Ni| zmT#lp$7r`m-OUmq>;>-IDs><0SH=={UQVPK4VJ*FY69^j>NY88j5Bba@J+GG0Iexv za^wwwC)h*yR?w=5-{G4eb0V_z$N_Da>W}rS7=2-5-ysEy?QUUEUe};Fb28_a$E#Sg zP|Sii4e^^e-jM2@^UO>KqtW^b%#CKNuQDG6YLFe1ZiXiU_&1{VFTB_Ct&*-sb_>WXij*@J|%-h=i z&NN{=6h>Aog9_}>J5s%|_7-ad-h@H1K4l@Q+K9HPlw~pMZpUapTdEJH6`s$6rvV=l zbb;RiIe@;w5&RTT2l-h0!HVQr?s5PuHAyPXv64 z_Y3WZb@T`t8`fhm)3Mb01F4?aA9J_j8Ax*mzv4|CsoHD`avS_Llk%ZdpUi29n4t*} zRf}hJ!E4AG!-8S;6MICl?+;oS+Cc1q1`oo@nUp4K9;(VISfx1LL2GpkDqAwJM(*8?Fz3y^1ZI+<3pp#q+(J&l^Tzpi-9Hu&*Oi* z|BE^DvqHzkUKF0y!yBsboH?)|tWSH=&9FttJhxLbTtb(n61>l)#^$ZHa_=!0MPZY& zSE{$#TB|6U18f8$f4t*P$QdG)G^%6Vo0NT0P<0tV^d_=q(6K_>LdU{i1J8?{o?`b5 z)~Mi*AcumyiZAu`g;akv3e3VdhiICCKK?JI;IVJekNCn7BPM0P2EmzQn`>9136IEa z;!a&VA_wbA(?Cv>GX5szYpMRKYOTcBU61^r6eRWuUBmcB3I@*}*-BV@_;pz8 zz}lAZ+rYu#P55tOhnsjx4l5p5DZ~n15cTzKvHBCaSnR(?PvV&ZU=sN(_#wbP`onsp zSR+RM7Hj|5Wd!g2JE{I^YOP%12yY-^Uc8&y9g>2`7z0kg2N(_3VSzcwwiq8`4)GKl zvZKf`i#-ZrKNjLPlk$DB`opR#D1_)Rgs_I5VL8zc)*KP(hzvIP6P`A5N$}9I8r6~d z`k`3;g%DI|CRlUmF6{C{ehR)LGFGtX;0@#|q36V!7Us=r!VtHLP6`4_wT zroS91n7Zfatl5LKd=9cw#y5=~mV(UGN2~)LMVtv*BWEl2f+07H9KKk$Mm^|)9TLzk zkQebh=@F^^7&`^^yFLr!9FA%*oi#N_QpMBMI_{yHk4cT#6CS9(kLf3=KA9@mzXAyq z&n;jN1$<9X2hxIPdSRKtyNGa*Re>Cd>?zskeD(D#lZ}Mc| z3*zmvxDy#`L`T@0%{d4=)xq1DK>jdVARf)2>+ooV_sia~Nja&F$C=8UKM19JkPEX> z%ipBt*i36>jDQMT^>fUhigj_|#F*naCDki)4rql)4p}#05g=XQBk&LG1hNU(7X!J& zE+V1x@QgO-ZcGhqt$4yx^Wx8nM1#-lUwB z!ZBx}9G@ZROJf1wBPxm~sLay|4n3QcKcxC#zCk`4b`WddymuP923i^V7}@}PZm>cQ zj~lsd_y~+zjN=+-Eb8xjbH0YR@9MbZT^`t@SV|j#4%hiTvqe2& z!?CzytfX_cNjb0eQayjZ+^;?Y{avH_NKMLxV%-@}(L6ZMPIvt&btm&n!B`zQ1eZ9z zS=grlzg(2+gY{x0$qMoc(2JL(?&SPBFbnI>s;6kwn3>FGL zhKTfGN8#hayA!?ybRYbElX69>KaS(Xs0)tyss^L@`pZtTE8futeJ?WNh_+42H4Ub` z9Nnee0nLE7nHSs#h__jbcV_GFf(cW30#6yBPv&I-;lC?k+?P?poX2cZu4}X6_D1Q_ zCGJkGm9vh_AU@FFBI2rt8&W+m$01HY+>87vVk=lx@E^Q0Jb5Ovr-(e^BZ2co{3l{V zcs{vO{lS~ZpS$5<829v|*+q|013M zf%G9y01JUw6p{(7zBG(^$CG7GNbVc+CPe{axpsa|c5vjmWhA+%-T|g!sK2s^p_Q>@I4TW*T zj~7vo;34o1tSt7CqYts#ES~g3R0NDdzu{S+;>-^oJ*We?z#;s4L=?atcKX9pL^OtD z%n*AA;1{Aj;tBi`O>z1Io{$|vABge9>MwFf;@NrZ%f;-0b;RGmJFtv1&=k*Li~i(w zAnO6xx>U{c7uil%Z$SMwzAv;E;SGO|&jt@xZM5*3q3uQN^iO?CxLmRL5cx>NHOLC< zJ_5KOGKM`f;8L-t3(rJ~CuHzc2lAPSYQ2ipA0VMG#9#1|kpTpEi&X^FBj163MQo3( zBMS5vWCM|kjGWa}>1bOfB6A3DNn|M@!LTLbeIU@SSh0q8 zg}rlVjnxy(8x{|mqM}rP`U(lwIx&O7etV{p6g>9$BT4e%lftXv{TG!r2+lqlk3!tT zGSZGbGwg@ce~Ab0+;h}N#s9SqZw6$koqtURxkEA9c=GhS(Oc=^!wpt_{|qhK zO*OnBfN9U3TEUjaH=5oig*&D_XbEo`{D}-TXpi+`SRQaF^fj))LWn1upxe#AP0uJW6&$@zqfd=o zu2=(RE^~F&(GGEhWNe95U>#C%w1dppYD(dWIRv?J=tjuBSPv5WQ$P`TYghq-w<4bG zgBC<i9Nsh5358~co<=55MAGo2 zA(_zW_(PQ3OsYSonm%sFc{MMRYT^kPoo9-sS_`R>v45hEjCVj`&pa}^h;U7`_g3(L zGgs?218W-z)V7sW&wO7T@g07Z$wce>GSbJ{bLSD`%?e`)PHQPRERC$Q#G5Ah*P+@J zI$vksl6^&d>%JxOA$+B{?+|J$H3PP~kRo1J!tYGTV38$KHjfVp#flJg0;~t)$w#U; zw(%jvA&!w#`$F-|_jDwWobiZn{9=U644J>g+7m)Uk z#xzp=Au5Cqja5QqGvRk5Y7{;aA_nA8#S@X(?Sxnn>(PiOVgKqWuZc}W(u7>uMf+uTl z95Wo8%RsEa+OsXmgY%p05Uuo*8VN@a1?hpPui)=y_tqdXSCv%PGIv2k2dV41@VR*p zRWu#L;&U&JDZ=KKAynT-YF5hKIx&yq~U+NC#lsHxErI4J7*d4Y- zWM~FR!D0Hu(Hl~*;{&#MJaHXz(C{sUr5dQgD!%>U{HD>m==O&*$$y{y86<@zIr}4K zgI{2fk&MPfYoT4>pCGr4XcMc=$a5nHA)YS4IeZ)BGO@aioCTs&WEhdxFwtHbnf^E` z5I!y537}=)ZY-T zzv6S!-{;A&PmFAiac!osb)%uh!YSfGJXZqWLS!1S_ZZxObvYF5lEm5`p0vQ9pa8zH zQjWDa8TuD@7SSc9)ZZub#hv3YDa6xv`d1bNF5#q1CK245IE zYQ(Ao=7@DytcXFHvCjt2_aHkao@hZFCH7?E_YPz@k@1OjJ*@5_+lFUFfG6?94D=wp za*P4GPVA~fMjN|9u~!3YANaik(H~oN{nwn>^MftJbBM@_+Ubr_h2Ft=P{%u9xA5*B zcX`lzi#2QD4SQh_oBFCV6uVy#Z2+UfU&lKUF;Cw2gv=u{i^y}_SFHZPv(VUxd$ETH z+=-Y5JcJR5I6yqbhW8@i`4#adC-II1ye+>lEHloKbJMXbgR_`o%>g?p#Qq$N3|s*Y zghj*}8}>9{uK;8PJ1ntA2(Cnq7As?jpvM$C2hJyn6<&^sfM>II&p*jlu|MaEB6`#v zTP*C0=l#T!F}!{PyCj~Q!Ro7cKP=h_t0d$U-U@UH-uNz~7v#nsUt+5KeRfH1W)>+d zvGx@92D^+v3!$x{DY3H%mI%58nF`QA>_kGXc$NpNyd~gvo7+}NhiJeShHwe6DSX=at9fPP9 z^BXJGALnIUdvjRz5YI5<-SWl37gp>AJ+Mv$FMufn?TdY+zz+1Uc;B*E7l0Ljo(8_| zm+Ft72f-?buvKDjw=tiNRp^Nn&lOxd07$qnJJAxHx{10o5nZiDS(}d>= zyA53|_9tLhDf|}`z2RC0i?XwFWvwx*6scZ#R`6`V-|)h*TLZH~jDsB{SkdFiOFY8~ z?t}Hgj8Fqwr539{P#7KzV8arNb$!H07=efc@W1f;!Aa1pLXN>ZCZe>!qO<42HW}*= zV&&WwPaxtd_!qMBWs8y0D{9>}P@BhJ6h1 z{7jT*kl_TT5i|g(13U;B7b~vVp$#tpai@qi;RA?$LC}n1?H_S+dNFY3imzPZYzk!- z+f(UEo+XZb)#JuQ$Hqz?(!$wWPi3d2|Hp}ls- zeL#0KrZ=*;ap)QQri<4%yijq6Cwj*W|Eihar(p!xEJy)p95E8`Ag+Ttm?1{jp%AJ8 zx8gc*i+f;SxDI#{bv#S>&0NR*Arr!N++RnnKX3&10dHKigO4iufL;)FOcU@uY8?3s znu+gV9^yNg$Do%*8~1cA$UzJTGVC}O9Wu|WD9HCY@u#1P2}c|wt5p!k@btOv7LL1; z7vfqcJV5cFGXzQxDDQt@!C>i`|cER~2-8SlDYaL32k_0JM5EK2cp z1+1&+)jwa#{@MD&(Y7Hs!Z<2SH!%hHI=oJCPw{mX{210jOk?r;p?PLkY_FkyHh@Y6%TrQC+P6hoS`$#ZXP;QP`75t zZ;2jn9y%ZhZ-=pttZYf4J8N=883LNF$+72!>zD)K7jvCE4(yyyH&1gQ<5b5p*D#hD zFN`U9$#m2L4EMj$Q^_fSU46GGaPzdj849r0aPQ6Bl7Zs-Ix21|`4~IK zxQ?|o*6z5@9geY|Otf?FSz@i6+i@REMLid)k4~3G<60fxQFuN9d#6M{_;!aIN2}>c z$j+RNbwSuN$F&q!ct=ZJ)BJUqH;(Y%g?m|B0}RON?wheT=!LbMj%U{#!#t_`&V_M| zF4KQ4$FSbeXY$XJR!2Pm`mpBHwG3)m&k4V>G3imRDW0YQEY?xR9#Ahd#Lf4By^*YK zF&nX>E_NY62YI3gor{e|&rSqRe2QgL|yLI#O zAm7)`=_R(rx9H$n9{wsi$oVS&TlPU7H~$=YVE^fsJa}Fnuv44;$D*Qfeb=yiN_aMN zh7q!d_-~5Fjr*-R4r5QKaGy-<%`U#Z0~Xo7|5ZL9`ebjWXx@^udYt=Y1 zb#!rfOW%cGRL;TCR{a&3EPZuGpAnu>VNJvmTJl^Oo)!N;-e?;Dz zl+;^99)dM{;dJ2^JRWc(`x-?_yfZJ$XB5P$8*HR|KjnYRbC<`BBd@VE{O{30fBxeZ z9b9|v@-%mr2mBfnXJ(7i?fNcv_muFoZr4`^Zh2QF_pbhG-L5}fRJcx&d#=-S>r*!? zDex79MSuq<{1~=R`g3xSDZLg)f0Y+xe0J*pw-==As-k=ZM>`1G>9L&dbLdZwvA?Ld zD{c?^6KA?)z!PFUT>Ktf>27z~mcpXEA-pk&U3>kvcuD-; zq~LA+X#vy%1MtUg@l#3p$+e}`pM7I~x>@hnHJ8b1Ed431zcz-`pZog%&S6-mW_wj! zo#i@))1Q@N9+R_w&QIiH_pqyVLcGmT=m$^qzlh z(UoGRTh5kJU#!z1GvS?(UyEH|{XPtg%w5&%R_F#2TFSNJ%N7kAop`jTo2n zQ!es7IZL;mA<$`HjPe;fpaIX&Jq>Q=IcW64_D#1l&Z9;zo?VgcW}>Fw{#M?pg3=4me)z}uVnmYJ|PRm(f{&sf${k}jG?d{hqr8SacwYG(U^yz za|CYn+_1jy6Sj=)<9|;hp%>0A=#l@ewLX2%SxLQYXg9VfMjfKZ$gHJ|dXOa@@f!PJ z#g}wYRi4XOqx|={jK`vL7)Hq2?cZegT&=xfuFMHc^OD;QS6qb^ImRaUaI;)h5?_tS zpmVYAf0c}j**DU4C9nxtA|3!|3G1Zq#&^%hx?KGqd(QzE#qsrNY{6b)?3}&CLRVsg zqjwdsw*wA2g*${p>|*b|_ud=!ie0frG@7C@u|$o=X!LK4o$tNfox8ndcDXwb(0tDQ z_Gb4@d+*JgH*aQVW~JvN4DFO{OZ>Skz8v#+WwN~g^yf&i7b3laL3iOSG^t@CWf+rK zN~G2iMp}G!wVvBjW?UX(ij=S;H-bOGozO^VCgJALM;p)bceP4e0ET_2(lTxB@hpFL z&woXxao&(fszIi+t82c@n=8xozJS#oC~-u-Oz#V5(!}4sl9pxI9V)Gr3^S^hkCo_F0DtsP z>}t~M61&K>{Y+Wcnvs0OtvtgBGCP~@Lv0EEzS;k8(nu^rgESguDU79&)R?q1nyURx zT}i;V&;P%Vat!l@*|iAc5j3tOAoVQX-b^3i80U`ifUU-Oe2!3JFA!Z}X$h8&Mu@~_ z$1R%v;IG9ZBQ~*?VeP(*Kl_C=h|Ovlb}rNPw`qJcy;f*^;_rzje8RW1MBey1#IV;^ zT8HrI$N6=n-|(L0L%uwZn2`~Yp(P-cNHCFAgc148PJV>#!IXWCAC^CnI0Fqt)(QR} zR;&Eokfc`W(r?(mC|#$f_6x`t@=is1PM5#ykEPmpPS*k6;t?qn=aZ@Rko66BZnQ4b z-$ORj%e8gt!PNelzq*K43q&r(Yru3n&$ylCT+WuxLK2M3)fe>s)3B4%{5EaODQpR3 zRBEUPgc3dSp(U8s7Kn}|V+n#&cKo9CJN(^`(i+OJUom%EhG8!sy>}+kVyF=UA+_=G zwG3R_LS)8(H;zDwrX{$W-v0K&5{b%Ag0hxD`u3bGgUGjW8H9g? zQ#fiTJsxQp2qW(X%%crcVtX*Q#t|y4A%qc$!+SnHmhlzWlVzpZurG#R0^xsl)Mah^ zh$|^Yden+B3Ab~z?W3(qYBaaoK9)@+#~^p4o((lX_>vv}jav?KlqOyDIwqxBDqXa? zH|#7h)IHKn$})^M5Q_9>KVywCiNqtWQX>xj9t|v$KQ_zlx+XMRzn-(!F)52k5aAcm zSfoXoCr>0-aSALgryHjMag`j_60X+8dS>xspRhN>FtS#82$zOk^PV-$5I7lx)vHAI zgt-dC*_~AeDC%HZz9d&R95H3(*pQOtyLfrXC#js-@J1l#sGHETz}A5$RvF4*>r%W7 zHXQW}YX{)iA6p(fyUSut=eD5?Hk@5yiE}J?ZYJ*QZOX%gUq^--GvvXBBMvyPg_<2l zdN^7%)DUUMV$_guVcnyQNO6*c8`ktTH&2vnZ%72!{V1 z8^W?#6jSYpZd9F1MiqRWOScUT?=7WkI%jom_V&-mHwR#_z)iQ>40Vvx_Mzz|ed@gu zD;Y$G;z|oCyM;)(m_0@meC~EC%B_E6c`f=Gfb)Ay6*KEE!sLWCWCbO&elL*+AB< zX~dAs2>gE^nVVkI!dj-SZ@}>dne$*rF1Xjq)X~4N1lsxrEP>SBhnA4r>l+Yv@&?h+ zGQ`IRhOvND+xR>_V8=-0{eodMNO&yXYmzYLrGt^FVu}#NUfg>KkO7H+k(VxHdeo77mjan@O=`VHG_Q&YCc05roK(WaSyIA#??aDw(&g;?L%ScR0F4Y6vc)qlA1R zs~ruxivC8))UOritggz*cHiw2f1mWZJxZ-+{1n=w>9g2|Z+J^v4%W6C4H)LO-I$ZM zghAt3-| zZm81_!J2s|uW@A#8J`(sgnlv#p15I< z9VvdBstd3jvZe%Qm039VwpnIcNF`DZ~7T79;ryC4j^r;W#EC+QIx*W3Fx?e6dcu`ETkL zA|=-0k!dTyQF4f+eAX?d_anj`gvu;66%g!4cX1>`mGi!6Q_80ckGFeHu zI`jxtX>H3<*$Y1}~_)k~#Q#3rfY6Vw`=k4mFTQ0i1s z9AL|I{oT>-A(q_}pf_*iu$2DM4h#;X!^YE_~vKZM!Iiot&yWdan#rgDI1Qig{% zK@}bn(OVU%Ys`e!B_UXPc%mwS#h|XND5MI~MkU3nf|YS9S#fye<1QH7y!y-W#9_tK-NHmLDV}|*(*`@C1h>yAJ#!WxX`_4Jth_27U7;VIG>4#3El|;3Xllv;N~mt zp&kKpem+?ucUOQb5c$G52Js}FPOItRo(MeG$u5u}@J&Li6hst7YZU~VjuuK3sWB*{3FDq71+1tgm^v1$v6M90WeAkg&584^>5gHR8p zrk^sgjZ&kE?GdC@YmDqdlQ5G@VKy~~Fxw9Kq9dcvf7nME+JIDdiGz{^3hr5SxGFK| zl*uFpod(MqjWcwlNI&*bM&R6|!P`$FaD!o-Oh;FpduE@q{QupW1^wNc*!08~9rjj* z6r%FE3<60GnDb{|BNtd_0k5k;N>N@fhd@5BJ9Fir0zp>;hpym=3^DK8NF)OM3y1XJ zs<2+x|1KQp1m}}CO0cAdb$!Jme9r~U6N^A5+z1h(MLBUfSYA{uE{AN3a{1t*>X=%T z!v_~t!_=beKe(vsrWWP=!9~?JwJ5s}E=n#aiiih-pK)PX=Kb)c>p3ovw6*@6nkA88 ziK0vv1sBRR-bcVS94-{$1h@vkH3F{gaA6CNgbUllK)6s?d%~rJ%N4Fxa3NFN;7WmO z2wX$q8U|M)TspXt;PQma3odWC`oWb9mk(V2a0S2>2v;y%A#k;U3uQeVuJ&+sfU7@T zb>Rwv3#VjJk~_lH39dnKq3%$?6%AKYxMJW^!=-_%4_vWu#lh7JuC8!t;c5ogw{WT8 zY7Un#T-celg=-vKBjIv~YdlBhh@NxaY;j(F{-j{IE8?WDCE(Bc?B^wqVqw7JT72!$n)g z`2|R_vmhL2#R3O|l$;j}{JWq`c3Ld3Clqjs3ncH}i*wJ+3Igt(Y@#fkgPKg{io0VL zeLK*{Y47&bpU+&Jv5rqB<|RsMrD8IfWu%rWB0*|Ag;X5uF|IKDGjH%MAhfl8`RDsj z2XENcbJO=CI-y@#O6cGDB?wIcX%NdkQ4z|>KIT{KwR1~U z)#eoRIJBerZJ$XG4w+1^T_Fj2Q@@aq;b~BJqB2aQ_iI9LS-5f6>6DQ%aj-r^UN2&R zPL){Se4_uns<_m^-@C}#V!F|<`d@r*GSL+!D;ek#ZMc#|$5Se~JRurun@@DKI#y)? z&Gqa?I#&$w^;vmmL|FC5-?kIdT#hR*t4>7ij&2p}$$(7O!Rn$+(T2ZYO2@VMQi4@& zs!GU5Dl`@%|5;H*0YR5FMA(L)B3XxhL0E!Plcr=0%F+m#nc`kOk>o(AGx>g3UPevB#OkKEl7g z&i8Vl^_1<6d*8<;GfD+Rj;VKRyCf#10b z)eSNpvtpgTt_TOQ*B2%c;UGz3LFbAynCMJkI=>lW=ZemRWZ1cG2jj{GtQH6Yg!e2Nx zmF{(v(OD^A;t#j1-*vl|kpMk99sNTzWugKua4=oF^h^w*- zA~7-es3Fmv3+aHl;0#B<9!vg->O!AK5A$a2vK7Z zCw(}E8jvON5m^*geyl1u8^u7~AOszz<(EdPC5Flf5*o&&W&f(|CVBEp%33`gw8_=w z9b%L^xsoGP9-~Z@_fAUG$+eM@u-h!Ey395VY89t)0V$M5mQY`=IED=up*T7AabJUQ zvt;wbYw=M_hiFE8PQK7bfY?CvNzDJyh^e{HWKZOk-p~eA{q{1h6*5A;d~CJ zKiKRbpx1C4Yx~R_z!i72V8#x;*9~|5C^6>J|HFoiYw2=YlY$9^zrzBnDay^_^H{1*zcf2 z<6D9B@BvJT8<}u6Yuh%k(;D|3%9v7%b3yvVd_b1OwF)qah}o-8#0O+a!~pojFnJKA zqR)d5$dZRMhbD_Ou@m%Z`G72Gt6^HE%rAw+@&Q@W;Tw0xF=>l(5Bf6sfGlZi!G?aU zigjEneWmiN!hy-yQy7I0h{4y}_4C;dBDFR|9vP#G?4wQ6$!wdsJKH%sIw+hxy&avJ zc{qAHIX820c6apfa#c7ucs0eXx+{-uAy}!5HqU{ z7Z%BnRb?m1k6(%{yr4iaY^0|LwIO5OhC&AD^wupuH=;8A*f>n7SuIJ@wE3}EE8Flk zL}p9w#SLw~#m^AN&rok@x98rlWXZ>3L;J${K%Svp5@@YDcdq|6BSYI@2O}$x`^b4) zFHJACMkTWi_mZ_R=tyj#7=^DR4b?|NT_6T=RSrP4*@h-XBo0W_sp3Ert95E1bfAhc zv+)25B4F#eG*|!{o`t3WCfz$55G3Xd+90-_v`O+nWm2yg?pCW!1a!mHeU$L8g#Q*i z8w7L4Y|G*e=c*hc0)+*Wyp2p3jETvNjGZG$5R9^-l9<<%#{EA@LDUdFv&K#VASe#H zFR;ezLe~LaXV5p`ZH6a!hmtQP1tT+#jG4$Y`dBL&9N>SPk1%xTBlBnB1LO2L;J~y2 zfUr)$ijBgkYvCex9Fvyh=L7-d<&Ikc!;1zzHTrBJB5_tm46{X^8zF{@n;>=<*hb!f z=nJe(>M@eDNxHg9C01w?oV(&@|JPPHFSgzXV^#ms}g-K_!jW2 zSm3gEnVDn60+F5RTp*#Q6!*NC^Rsv(X1d+ zkze^2o3g#`r1M4Jx1zzO@N&<@dteCT%O-On{M>nw4Qw$2X7j*gjYOl1f@^*CUeo%$ z5ALyQ>EpxS)P8vz;TTTh;Iq&n9qQ{h5;ofUC^kNwF!k{tKhCDXit#crqAg#})8+;^ zo-MW8<9<8O)Cq+*|F~&E=xLEA=9i$WV7E{~U<*L~Gk9mhpp8rN%(mqrWCiSlLr?d)$Vz zcM`sLzJ<|EA-vt|5+w#zc5AjhU+3_Le=6T2;X>G9%#3qJl`6~6RktV!q5&j7@XNzR z@4tAFWJWwQg|ZQ=g6;My+yW+N+^Dm(?{P<@ObEl(mc-=%Whf-ixgOS|ey`nm2w+J!L2n8pmj8PiV2IiyKlvP6#{Ivtb=8knnQ zrUv{p(ORY~eR<>w2_{4n81b!V4B+IRs4?&_S015Q)EJlsmba)ekOG#c+5LTQyXW&B z*oJv7k6-?B$AvZL2nvW1u|cpxY?oE6*nLE}$3n+tMUT6sAEUwYvcHc89A_TbcOunZ z9yooIQ^AzGD;ad8Y8PolI*L4K6I0>TK*hWt^R3;Zndv}-dfM?8+Nv*H;NE`%NC;bN?VvQxF9 z`jwX>Og6$+3U2lgUx*k@)bk@6u}z9N>**uO!3i_#0SJ-`6xByZK;?+)BRLGO%+^Qh z({0y{T_g+K@Xwc1O1JV zweff*j|GF<{k34OYzhrV)k+2mb73qp9x&a|wcG1o3K#XA^`>$}a@qP!otr{op;Ef! ztEzP&v#N#7>C<`ZS)@=1Gha8=hg{fEiVgRc$6#}_r-aJesiI*n9nZOw<@}%p%??Wj zEy#^hS6;7yA2zuKUwtXs}}tk2t*&vjqsAacUE)gELoq+1T)x!D}F(F-+WG z9f1>-a?t=b9rfQx{P8W}seP)iw}tQUP0Lpy;JZb~zQ$Ch2uI^5?~jdX*xP$etyA@u z6y3ZDNf*L?#LPISC%ruYLda@!J^i6E+5{cEJ&97u)lu+@PaQn~moear6djzhD%VEy zmZ&2%F&F_9w}Z6abxdCM+P3wNt^Ns2Vv;87G0XFiQ{Qd|}DbWbz4{46ZKX8hd+a&5Q$XZBr`uDKv7d?PMB^+BGsc zOlJNiDv@6-7dKnasCIio^pd?AjdV!~37PlPvguLx%3_<9tX&L8I<|J5+j05@MgRGZ ztJ^twSJ%;C)J_pI$ieOqvqvW-p%{+%K7RS9;^NUWqHE9x09U^U3COcVrscn|n5YQvI=1<6OvPOQ;lWobeD* zv~kMeO{)2JYm(iRe#37BE}PJP>%NXYeFzGOQGO0BTuaogr;Wp=r_cO`Z%JD-pk3F7 zG?@97&$D@apmUFpRSuAc>R^vmWTHHa0?#5P$WsnQ*5e~@6hL{{Vy={69!PbUQkRQd zEH%q_bn&-WuJ2k}j0U6jTE`)=aj(VMI4qD|9RW*fuzyEW2m5yH(P23jp4=`6?l21u z8p?HAIoA`*qtwxiO{kMcsA81;)G%C&P$t5%DpDLSY% zQQG9hhVmd~WJ7tNT9eejq1-Pdw4vNx6P2JPb~LzAeDRY4q;F0yg`#^Z$*mux2}eB(+o}i~uet8}TElMyzDZ*PErHjTpC!iZA86Be`^-gO@!J9EVbP`ySvLOl`=J+ncgOu}7xVSQFdEGKM%)l`<_0bS z>clveP9CP}uLEg{NKhsWFrghITRG;j9v^?B0LsHAOWH9KzOs+{)yvtHLPl+C>$RiA z(Q-7{N?w5sicnZ)c>wxp7$R*LN@35pJV6x=%B%=>C*X_-;9yw;7zq2vkHZ5qw@kq# z0yd4P%{?A9E;KA;=9U>NFEpH57Gq&JiG$C?k?C!uPB^5Y!Kj)G*O*Z?mxor<4?dZY zD38@f;*ths1T03>VeNy8Vn!p3I?D~6K^tNs6T}#UVb<&3aV#Nfdy|SGQX#4t>|l%~ zsu?VrmOAUuulLXyuf3NpQ8>2Pzk54D0jVd`QZ3;A%IR}Ws@~Y-w{-c-_$FJn9HPOf zwzpx@CKslq+yT=s>yKXFq4avsgJ~Upxiq~{C{iL-#~6;(l-pfp&qRE~WxYawOKG75r|KG^gv6&t{ zJeF52<>{3?L50K#VF~)|d9N?A2k}%TGM_$z)!p3WlaWXSFLG{MXW8)cy?LsTlWX?EFzE&YcOz9X7xY*fb>fh*8Hz$+FBM z;jS@06~=wPQHc11Q5TOoKy!9`>G$3ReKy_sHR52n!+A_9B?8v#$^Me+$?k9@kZwe@ zCvz(&(UaNP7$$zO+eSz1lws^6?4jarhHyT%dUPb>aQ2Zsj12G19{N_VcV$7TA6f-W zZkMmrs>Svv2~n6CV8}SMe9on}sdH>hvTw?XRoh%g_lcpw^1g3Gso5~C(U6JfeLUt( z8o1!d10Po;K}u>c9H}|f-eq^p>HxoG@nr(`$K~HggXMkS*cveHyWOQkiy5avrewZc zdUWdk@koIw6xQr%cQXBpH~uSbS9AC^wRw4@PzW=>Z)^;?7>|5H*)_hVkA1MYArX4V zmqI;IQ#3HbiPgdZL9m0sIg0sNK~mbHVQ3F3Mn-+y!G%LtXNf} z_ykp?IuR`R`Yew*cFLo;=<<5%Xt`cu*_Mp&io>8G)o&~h$82DKUN7c*p>j=9Tm&@i zWOZyTOm8J7#p+<*7<8U83Zvodjy_+V8m6nG6SQ&cgtb-^t;YAx_c{D$^5`(SCDm&L z6g+$h&kkH=F(-!VA4o*Ac|ZAWwGz+&x7cm+O}n)f*Iu8FycfbYk=()8^S&QE1gHks zFiR9{{G#PbP&PBEANIe1Vt8IebJ}CKkYJr;M@;*21nPRG-WZm#b-mFVvdW z8)-JQxTz-Z3w3l1&ZhUqsk{K?1} zA#Q0KDh!yYOqof8P2u9Br8w{Wz`azQ{fkQ;tg(GT3PAxeLQ5E3Yq*5NK^lzO1Tm9bTTO}Gi*Seyplll=QuE!PXFL2; z<8HvFm+J~%x)`mZ!T1f4ZQ0c!rMGJ{XN8@MtCLqVR~LI{MKk-R3K!>QUS5t~UQTe! z%yD>VTW zshZ<6$Gdz+hT_n0`|uN&aByY3FDzfiHo58HJk+xuRd zEpzNuwPEOfH%KRhncp2^=~>?EqKGpAa7s2e)yne!A{i zXLa$DWoE8#5kxNJnxPahrUTPkpymVqug4j*5new^_S0d`E_%z z=A~$`We|_J9H0qbcNvcCBL$&vHZ`3FYBxBI3aEPE?&&gfkFTHO2AK$9Uy~{&1%NoP z1zBZnA!;)Hse^E@cV-jQtNyjGA6;_aJ}##0!}8ZQgt$RgLfC43_QD~)Rfu7$Mt^Xk zurTs0)_6#`8{7=uI)0nyU+xZRi;hgyc&`eh!PF!H_}L3u?r!Z)0h~f3X080yM{#M) z$ZM5T?`>eZMKRjRt711u*Rtjy-whW%;>NZcJX2P-%Q_m2x>f+vW_{^@o5(AMqBBSN zu_-wAl9n(8pozUk|GIg3;De3P?|%Nl&pjCvzx8w37u(;zB`6>|WGc=b?j3fin-^-*+vDJ#>rLH5-6Lr* zs=bn{^8jrDQ{i|XDUqsU3`c5&%id@(s?}nkFc*gMxB^GGln^N}g~A(uwpx7E{-*cb zVz*k{YgKU>QYeI(Z@bijT#T2mQ5X4SwQsDyu4g2m5eEIBbU`4MfWqkVVJe6FY2BS7{%qSPurgn!?-rk?NwPXh``w4|~r&mwA{5zJM zo#-PS`!{v$akkJ|@BJ6G+UpsSVKf-EaWe%>#(!{p&an{gUo^ldmhl7A_apA;yy2Su zq|3er$st3w)$03v+}qR}2seec$!Vod{1Y7!vhSC$$A2$)HVkPK!pv{hSbCNr$p;mMX{Fqmut-mEOqKUib}Mn;rhVSc3S05gHd}41BE$g0wR{^Ord1qq|C0rM3oKR@$bIaDxQ~r zLrR1&^ZNu`qh)OB<@HX5(ST8>mrK^#)SVl=y_Zw_!QpHF29P9=tkVt$&W^ypFS<{nA3w;=ph9-O82kXLOEE0TyNI45=rC31>~I&7H|(GM5vf zo_jQSq5*~|S?-LKxf=uvXPPlZcFhCn2uKQT)cAL|Vv9!ZFEqztMhp!`ZRkt^IY%32 zkIobts`ge*db7eebzF~tGTlmM6hIn;F!P%@Q@n8Th4s(t#7Qtqj=n)6cN~l^xYkFJ zy4kJB_jmgholb+5M-Fn1ks7c)89$3jYyF9)p-^t!lrBxGuMbRLbZS~!c=_4O5f!OQ z%_W`GhGPk7eBwH(yo64|gNMg(Ij!RS0-VoKY4#9xWzBp|o{vy??YyBnHsjd1vjjiP z6tpAbyx?DZ?!v%ncLP`M-gbGA>cG3HG#FJU89L}(FhRTl(_OV9G%?>S?Xd`xWc zoD)cblmK8jQd7~UrtDwupq&%1%QC{gt2LemqiQAtg&%^g07Cn698jowMPVH^K z`^}`hT2V?wdKHu&!a*sCCC_u<;O&IK3is z1PJq~d%0f}Ihn~Xin6Z`mw_{1S%Qd-vkvF+Lu4d##^<*?qX3BqCI=L4bp7Cx$E7qJ z6yo>Y+>ygp{nEH ziCKxp$q5Xx7TvX>G?Tt(Mes!5NU%(){ud)R1x|0_b#~FR@lRJS9N~b>5W z%Ve*`KHskSS21hgA@BYKKSZ}6hcot|!s$>NYzh}2J-s{_dcs`j>v4)pIJu12WGC@R zjnGD*!Sb%JCn_D=q0sI!mBN~;!RhUUsw}YqkQ=w{ zZq7U2A#l*nn}yrD1{T{xgN;RYb51YiD0qwwKnwuZAQ0{nWw$A@HUR9WOjK#2@Z=cw zI7iTl!l9n^CtVW(QO5z}`qq&LY+J1;+sY@+Zz~dGN(-4%Zoos=;=vF0)hd4O)$7G8 zkUK&cbwU~UmeS52pjb&+*B)5kEW(8bPfNF}9$nKj@<7nm84k)p%+d)d9^_It5d^5A zBWhT*n~0a{CQ`nz_K+|!ZvFX&l>;kIeK%(J`O|i_2!4obA^Zd91DEjla~h1=O)xo3 zX8sM-bB_j(U#tNteaEy>qCW8rjqn(GOfFUR7>;x-EL$ib?q2ZSEycaJ+<7_aAPq+C zATWcEU>Jz_*6lXA^Nq@$>9g&&B>yoaGKvN>zY9PDv*<4CPgBJ~DVAt|dUFaFprr|=2+7@Xq>tIaUjhB>vOXBch9c9|G4DU3qB3xhDB9@AIyR)E=C}!y-u>#Ae4yH~9hL2G`h8u??b%<=>U?8kk6WX`sGWm> z!W`@(NK+jLn0oh`ej}mAu)uNYm&WIBQ-&FsN=Y%HP)sQ|REP-SQzMj4u zKXHvvXuT8HkT&6oYcv?O_dz?8HsYZ!#?18-*Tw;MKONZ=``zUyzFRc4pH*m4vmSsX zMZkKUndBzyJBSB3(vygGW<9@@=*&Kn6W5xRndjhecbeyl)EyI+&UbVoL?MAT_y^7p zF5!u5G+5quk0>>Lz2zq>{JYm}S;u@kU&`K?WTGN9!tLNRSl)M!xL{ql#U4PC?E=G* z8sX_`G?@9_V`Ipfd%?z?HT?;kxQ3()Ph6wHKK-59NR{w(H5zOM#3N4MC!)&D{S(*V z12XC;IPfS-M~Q~$2b{QuWD8GRqrn;yK7g-h1+EDcoYQ2<;bQE$LLb-pV#ovGiEA_% z8p6cs`Qix>JzxGSC$1s&!qe4gu+Jy1!AS}AiA=DKg*=H3*zlz|I^?JDbTt}`dSt{J zNNmE95%^mF^NDN7ZPxL5FFaihc`t-*A_>9Qw|HE#@%h9xhBF`j5u$rAdnvlj^A*Q_ zVw9iZo>y^x0nV36zG}&UXz5%`f-n(;#Uuxz?@%l}z4J`(l(9p;nV!D+-*N;6j7MI& zW0&}>RH(x_XSb5${hKPbq`35=!KmW`rohR}zhNlFJsLcIu?DF09V4h@IteFsZN7Y) zyxc8d|N1jUhAMWKz;A41YcC@m+S6ezizEdkrxvgM&9RjYzo5aWBLb#?oTsfblr!nk z!mgzV-|ncP~{!{g4nO(Tn*E;kxyGfb+~Z|@%ukJNgN8hgB<^T4H@S|i~? z7**|I0oGX-hV7@bDnZNSNdAmiB-VG5$cVH{z@wk%t#h9@zR=9^e^$N95F&(C;)#J? ze>0g_WqlhG;;BrwaK_Gh(>0RYDuOwt5vdbZqWE#Ovs~mX8rJ&DnkKIGy4$*az?3y* zWV$h#-unrDhyf0C&;`KtEaHc|TU|W&4NtcFu0o5Rwlr8?YS=D-q@2io>{f>ubM_2T!zqt6zP33;uaGC}?%&?LD%K-&c z6LRjP98g{E3m;*I^Q+-#U*?crjVhTO-P^cvV;&Bi8A~9h)bEjI3%hP_?Kf#+>9{L@ z+5fKwk|~7kBpHHlOeWJW0hF|Ei}u^1o@LHY)8CNtn8L_|Db%PRJzubPsrSTDjV}G! zdrj#DNR1HoC&>_eBWkjIY2{OWSuNRNgmC1ODzmJ^z@4!8o(^4>-UMsPY?C~yL*aX; zdU&|**?jOw!?2U<2!2>XVf+J!uS(s&nY;PSw}H*~)Uk>8e|hXD8Z0k63|y=cT%)LK zeS5`-$u48N27gf^3~|p+e36b6#rA=hm-@_~``y*2=Sr^`PJ?~S9R}iCbyfR}R~Hlu z4xc-4{P*WK3(#O6ZHLhZ2(YBXKqBu&?do29lJ~%+>64;t?)^K820IM#h%?eX)-k;+ zLf)Z~+IaXHD>(+9?KRYz>ZDl4H%qZ1Ar<%6d^Ki=d#7Dn%D#2mU*#qZc7<1jF>%-r zqvN+}{TR+FE1V)A_fcCCR;7;$zC5ayZ@WP&&lkrMO=)#S9xLV)ZW26nv|~)4ml9*9QkG0S zy{pgp~3R9m&5nY zi;EP?s9Qbo_?FARw5U4lK8}>KZEr}&!s6H8+`Z@j_uMAUdbtG^xI}|}%)K1qTWQ;EjT*OPqF;)tm+$Gt_=r}NtKts=qWq9 zp92@}UwY^3PoAM*-b=oC7r$>)%-%P~JLO)}KT`<`hzU_U!EpcL z;@5w*zM#0d?OoM9mmb>Bp~3P}?O->*rBmWhEWueD?Ee$mqZ1Vft;mKSP=^hP)^bGUicuGOuOX6rh%Y8$@dl}W7lJ$s-FkS~8Q%-*V@`~(UgQ~`T4YkCI+tFOUg$4h=bg=4 zUt17b^EMJLgcT+nhA?AwM4Kkg-4kZ9h6|TgFI{rUXZzB$t1k|2s5KryqO=!*uv}7C z{~#XVh^`P-SEXPsKy+r=B6am-O0QF6BHZ52TQxcT=`8>TbZDpFOq*KUqsX{wDN|Pdy8Sr~ zmY3?P4dD3TpMz7HwD_b0xhFp0tm+f?;C4+;q!@p6*w z)(0^gkU}BMd=*z4auFQ&gVTLkj=IUZ8Pxit$N_+h#{RK=`KF#Pw{4CJ{-^Vn{QxqF zJJlCe4({&jUuF7vN5>QQYi~jB2w^)hGtTMtU8G>R2vD^h`Th*eo^zVa?(HB_WtBQqi?gP zUv90pkD!3~AoI}C*74I|d9TI%09)bS2&5o8Er!$xkJhKb_*#s~R8}2QI@o(Qb$7LM zb@FglIKwCN6bcV-7Y~Jti>J53!P)WS`Ba`eA}jdT0f0{^$y89bKg{_ z5#MIV9P2f4$+b_xhzB^*zC^8N`PbHN;uFTKu5~Ke)nixM`=5(9s#cPqfanmbITY>> zm2gyyy|m1IN$>wn-1Yc%M;a{enmA&MF1x0TSQni*DtX77UG9sc{jXH|E&o#*Ebp2! zLxzcz94zJg-N+60A*)Nf)m2TY{1s9ngqh!D>q9PrCYuzII3Q7{iZh|zT7R)@Z@|Zr zwNJ>6{vj1-+l@QyHsM@myZJTi`O{#BAs%shwT%-R+=pb04sIpOm$)WoH1h$u3n!5&sfSiwv>0wb(4%YpM)R#+qmTVMP4gU_MZ^2e#Su>jJn_m zCu3!0k3S-XGwYkrFn8=h|WSa*Nx%SVUDq|^GRFyNE0f1<%Y z{{9KE7WPjx*vH>LA=bjahz5Jk$T%!)L?72v2RH<4qg2c-+Y9k;VOdV*$JRf^1JaiC zPsj~n|3rg*RQ(ezh)fu{QxaO7)+<)^@ zh}VQm#XF`y^eaSzQTrz*hsn&pVSvaz8a#fn2B`EM)0EJOEXt3v8dei$bCc#B68L2xIaSp_KB)|wL^00(UY?Omo zaAcmU zIIXujR^_hKDI;U>n;(sJ{dK%@xsnOQ6w>DPj_7r_y2tB!fE7f zsgAWKAk{(`wa>&dvh1U@{wi|WTV{ooDpALM5=5@l z_a!h8i^W~|8Xa~l1O>)7OTQvfaRqjHJWBK2zPG4sXRRKK9?)Q)V26oh2yF-&OuXyJ zc{>LipwJe>N|aJA*6TVXHwclQi|B39u45T2gA-lvYSDpMrQ@sY8t3B{HgRC1uj>0& z?nzKU&fpRVbTspx2FuH?18Y|}OhVkHY8S(i4&lB;8Z0lm4#W?gDKrT8B_a(%n3cK+ z>#r#756D`wFA<3pT7xuLDTqg$an~UnB3j>KMZpwW(1?pvrCLwzNLs94aC(Qdc~P~~ z6O2evwIjD=R>vgfbRtJgc*|Npcci$;?uNl;{U$f_HLujxZ zwpeka3Vwl;VzEXH(5djVr#xqtsfyIRJbHKCnu#G^J4S{^=09_K0}Uo_&o$+W<~M92 zSWFNG<-6YLUL+)A*?@78T|8&_&|tYV`d2^?VXf(M zB;)PF`Bk3G_rEf;_29?7`q>oDcVP-mXYWqan7SG zms35?CtlV)zP=>L6K+Cdv`JBdJ-)E%msxEmhXsO>D!Eppk|!$@aW8P9N+(Z>hgBJB zU7|b^-oP-MsLg(xJtnQ;gdy$XJeJuOciHprT313nB1ZUzv*5z>uW7Kn?4JDrN8vf* zNQ0DMVK{Aup9agz?inX8g>Om_cT;F{@7%4@g!pcOyBh6Yy7BS4IY^rjW~J`g`qMCj z0NF-sp0_*gy3lW4ozbpcLl!e*aZ?EYW^Bgr@pGC69PrWn7}2g$Q6yXl+X*n@jP<{; z+qQTEuUA5XHi4AE_)I|rB1K7GF476kCmpwp8T0~B%LQc7b2s5-J#z>eM97lbkxMm! z^FLgzu7To2SL$5miXn=n0{5I%ImU1ySun221&9tzZUP(4)_i4d&Kb30vrqk$fz!7Q z?)$5TAc%$K=SoB-o^R8lmH(BX_OoKt5iP?jJfOjT=HjDgBuM!*TnImRePonBLYy9G z!i~uygn1#p!gIrEu)Nf80f3|M{B^`#s(D~|4mA-M%p2UN(?sg92#b&18`@aVM0yWA z{CIAUZk~r;K55tVS*f>tAtp_vE0jFPHF>D!8!yi-WAAxn`mMC5!9Jjg5aKu*P4diY zB8czYR=viDTJJ2iAn=>`2>(_neoM4ADP`yUQVPcJ^gf9bwz4x=l+Z;qVEqbA~n zWrO>4nn*1cVeuw{r-zx{i`=R_HNATEz;swzA&#TbB+slSg7^v#6Q{xQvWWx&j>1FPkrR2;MC`F_aGy>Ssm>xS zejaEWsog#|fX`;PpUV>;wTtT#GGxyD8&UIX-q=p-V!8ub%{L9J(&)FqM%!cRFFu-@ zuOAKe0quuS&N09w&#d-?_`bbAb8E>CUiK3T=}xbncKLT2EHB%S0&ra4<;!Ph11ow> zDdW>;=c(lK$ca2^KMq(nxKF43)L;>|u>DN2r2nZTTUlycnt#jvuj~74Z?W3wfJ)N; z^Z>L5cMi&&ogB1bQX|F8h@W?tqrpC){SeAI2AJfT)qW7)DZd=)UZtLA)cEF|Zw+ic zRYrs5W&4Q+9Dn+wTHE~FezKjZgHcZJ)om`>-EOIoa>!9Jjg5aKx6O!CZX zB8YFvUX4b&q=bab`)S$qsC#AG(_neoL|g&KvA2iT%sAlIHl=c(LLlxiO3mzrJ4v#6Pj&>4SK$EmqRP}NxR4FI&^uMO@3MvQ9{YjOU`y&{MM_%!C}*V z{709(MuUAo6CuQLG@9g@)kF~Azb=j~G4-fVfx}b2UuP3qGlT}q%O=taaP*rP{$%8g z5Vy1q6$VUHrp!c69r#aE-(;v9lqA#?TK?G5iYtgaUHm3?dt~Sl*=d@Gx(p$f) z)|W3&VC66_0{(>`%AvtNK%NNYLUcF`s2(Fv$ZK>sq7;5`2;te)1=1n>IsgrpmvR;i zI10bAU@C3(U!HIBSF-QxHG3oa_Uu3OG14Z4{SPza9K%$yg)qymEbG5G!Z$8oJ!*V% z@UwtF_Dy{!D}U~vlZc8`yCs?lH{&?*V#LUb5*i2NC~N*{zUX_j{UnT%DRZ zxqErIIC(mID7-veT|GSA9bLVfI(K7E%x>oHZ0GFgpm6f^c64gy;ppk)+|0$<-O^)l^mv@*318R;i2DCd3VqN5-fk`)HGN`VzB~UF1j9<{pn47aA5abIXjC7aC42 zOM^Ys=P2M&z8v9^Q5TH!S}XggU%i}NDP+{Pwq83*97VFTm0hGmK5s(I_}NOgw0F(_ zH@d=_?KBt>U(D0E{;jey6SL(zBig=&kS?Vz7r9t!mhb4|Z?9b6wUlXMQuQX<%x)H! zXlB!(S(vTDSJWx^P34D21IPXFcU+fMD~Iysy&y*vZ6=Zi%gYseJpfnXj4q-t#byjg zI%Xc&cOunZ9yooIQ^AzGE2q$4d8yiQ`IzmpiWR$$2=`d%xUA@LxAbF(yD79?%skk! z(7)XktA=C@cyn=Mcce`S3&+ejXWTr(I4F^dr_kGyn2b*XjDR8!54gleIhaC2Sl@ER zj<2{GFwy>X?3J(lGmr)$%=Bf*1Pwiw=FmVAF*Fbp2kXaf(z*Moai`tK%-#^&@Xh!4 zF(NAIWYwzju}^(_&t1K7tM`&_ZU{GpX8V@ix;Bk0tk^SZ)7O1>o(Mvkg|O$C8F82? z)1$zL7Q9_u9PFIj9USZ(9qhe5?L8b7t^jT7XxGfai#akQBuS@AkU@p8l_9i+Kd7Dw zMkd#DFD^xvm4|p3tf(%OJZHoX>c(tcxHGVE$8V~Iyxsi9rdyHy&U5*K!LS$bL|lcZ z_0eFa%cJ#Ay!8ScTMUaHzHyDmwNwqnq3FFPQDps zM|sp(-a6fNNR35C{Sm1HoCOiq0S%Uyb%5sh(nyztmc9HBj2YQ5wDz{%8vrEP_ARS} z_(BqOAhV|}wsRJ4;FYCY?aIC2s zoZkM5+nBL?Z`9bCbb`TRf zFtfp|pMytqnHyHEpMwLf9cU?aK!fFF9pK{sLZx)cS5@mmW>pKD)2H)RhEXU-M3%5l z_|7uh^d_)bv<@t?i@H{N_;!A6|B!t{?0(AdX+MBg2l0ToupQ7~d07WIvtq7YBsvs6 zA|>kB*PAWs{dP8Lxum^cyKQwAbidUqp#-1JP#EKIyHv#6ITK&o27kTnrsidtiB6kn zu;pBQ^q4sT(P;;ZUX1f{XKtAyh#2Fm``$yh9v*ha_rm&^6XUBFdDaeNiHUj!^tLm*5OM^;{JFs%ggMwAk5RVYJgkK^@xEReC zCMsIfAeCqfwn((P>?os9$~{&s%J=und~dEBSrddb+KLMrY&qi2IV1MVE^<&JxYie~ z2q;0gN(BoQV>1ygnn==Mc_~`7W{k82!}ZePai`0k9+O67z6jT>I-Y`XF`6+mR+Me! zljgUT1~Xl>tWO&bgl^m2oOirK;Gms13%7L*EVf6AHVhLLp_GzRB4kO&mpm}?$geGe z6IR|Cxvl=?R1ca6(dPAOu)Gu@d|x1ZM*%UoyT-e@4oV{e|JjJhGF6&B&mfg5uxqnk8im0%J0U^F}8i)>^^Dq zNe`lhXe7{Jc_~7)YL{X&5h0}G#-z9o*IoRQ7X<%4ZKU^tPBd6vijWWB*z{6~1^Msz z6y5#0WRX`%O`l28hGC*2RI0p$7P4rxKc-{NE*r196(89zu7{@0p!GB@)DKXqt%yHz z>UPlLMem!|c-_d`j|MYcg#I9}1Cd&E{9;*1_WwracQVBH?!4XW{p^5l)4U#(SUp9l z#w@by$dC?sTuQ@1A%5S@9XV{(FRlFTXfV@7$ojP5Teh?x>;G|fxo_r(!n%fKqt+dl zq7B1DMW{^~Ym3lOtz7p*Ou&oD#d;n)=ofZ?UzbD+Vf8#M-L86cP0z>!L0e}yCCo6ewlCk*^X0b9QNjOo-m;$t z%L@@A{BHv|PS~cZ-+53-;E0!#Y_~p$*?_bS#!Jw&5iX{AF-%mnWThoU%OdTxuYli( z$Y((f4?2Alyx?(cW14mv2q+2n2+&}8DO!<$qi|mZvQmuAM6{5O`BNshR`u|>vu@MK zqNmG^Zc2k)!^}8mG)I;TQHXui8ejf9 zhbCGy#Y=jsA7>^1ufhqu>4fgP(gc4Q9Fs-M0GVfE(c0vgRP)4HrG) z#R(>4_8_%Y)|Kts3yi(az2d(w5Lfh(-bpmX{(# zt9B_i6A?l>>ZWvQQhj}3`l3_Q(!$HnUQUDMr3iTfjyn!U7hLP3NZssKPW2xT@b9GSB_3mwL^bsqx(|xJJ=n zn;{-?MzqVaBh#emf2@iitu~4j!T3x$1Y!yy?T@e9xIyjxeBIlBHtx9HtWF9f7Q!kM zjzL%s5Q6PGRF$CCCSj{FIJ0Sq2r3qQ+{RPAYvoK2Wx}+hDUg>Cw#ATtPSApt5~$Vo z;g!Pij6h7Ol;-Unu16ON?y~BX!>xCJ*w0CU#6nn=oUIfj#K&UkvQU*lnWEMfe!b7^ zuF~ds@00(^JmU_i67nVPs@8|EGmt`b!{I7iTD^41C7?`+=s+JexUw~@B&gbnFjw0`V!yC*)5(;xUO-x+XGO@rmIDzgDJI%$*S zfy$&_F^IR`nWX~|bHy)9C)DW}RBCPMq23SbAGl3}<*+`}Q=`=;s`QlLjoH+^N;{)g zuL-C)W01$?fTX2=AvHo+E@-qcbsy!25z^zu#p~x>{eRoIw@=8_>6;!QAwpO#XtXe; zrG$vuU()PT%O(@U-SfHsb3FZ(KS#_ywjUd;OE=JUD_uZPlLIjpfzs=~eC z&)`f}Rn@S%z>&|-jA9?fzv^|6i*SPT$*@jVk;~}-7eI1krf&eYn1g5W-q&ApJ$7@3 zV)B`Cn#bqQ3~0i|s3(Y-Pj6R?w&t59BwBL@Xm+zrkh*OX_YRL4?me>GL+?gg>KEs? z=KLIjBf*azQR%xGnmF${shz@4cB|Qd1|uB8WK0F=M^hKMoJj6>tv+Yy4kg_9v(@6O z_BXxf7Q5BrUaN}Bu+Z#UE7Eaqp{jh}FD3?#YnQ)Jbj0dWG}v>@3>+g4*tP;3g0)d9 z=G&rS%7k7j9siaPo@KQ;vmBAhi2EZP;lr}KH%a)BaO(+wD2J2T!e4sMu|a(nr}@r* zwD)%L`~}{Afbh40mL}SjStR^lJk2-dcE_}URp)Hn{_fPuj$au>_)%eecc$Cz4&n2I z*JoZIv9<1?Ei@S65Hdn~AdgLnM9FSd&3<=?QjvJ-x! zqfnoBu}KaE6!SF2t4FE*)il^=;YV)fsqkkMln{Q4H2?4^Yxf;HuRf4?Uwi+c5%HOGI;PHn(&VWsuP~C-+rgDujlTd%zaaxMtn3DUF5M?ybq(A);(jaT%!Nk#_@J9%+&K96 z?lt{JLXBa8@XIE1A^fJri)@7*G^qK@?X&N5JM~?_+DYGT z8XePp8{JYm}S;u@kU&`KeqQUa=TuVQ|@mzYFI>*K&`=*>&was;O zpBPhV`{~H0*zYbs@!g`S{j5TZn)Q%2A&f{i=4o7SIX_Y13uQ*#z2K9!>HCm{^LJLe z(!qZ%;$cd?)%$kq4=uv)1}T?J7BT>d@$b5_fKTh2UQ51O_P?Ok zjeF2wKVf9f=_?OUvTCQ&C8$;XREct3j7r{58Jh%OvXAB-64c6wSXD!Lq*5bKN>s_! znnayS8HEv)@R9p?Wr9+tlKa5d?lm~AlVu6C^?EOYyHth$wnpq-Cqml${2K{WCP^~=~#aD$IT7fzi?YHw&1L=H_tcHV0o#;QPEvFeXdE>8=L%= zE`J%{WXqOAh`TAYMf454bju|sV8oH8hitAL8;-OIVWPvXER(L*pK!x^D-cj@M~R`8 zyholp8@{&~5X!@KHs7IP;bU`(%|k64SWHo|qr|$vvmI z>~{aDcbD!oSl%dc>j?r@CORo$ww@9vgDkTxaVri(Q>8Z3Sfs=!?p6A1Em19Kluz^D zB1Y~WK~v%rfX9gkTkR}2^g+PR0c$2K-O^T0gXN_X$JuvFEODe`!?Z?2CZ6~4m^W$Q zf+G)nTxqbpRN^fGM@uYmq(gIdd+GPy1${Q%`8DETxWjoGj7T;_&vi>2>&+5N9O)?A zz~OC|iBA=~s;sGYwtkr>G}tGr#E}@=@7<^FS$xHFV%w+zy?4}XLWAXv63<;roUfEB z17CK+g-YKRE_7&Z>JpMv%%^}bfUK?52Vg7=*orqihp?mEGv?eTDI!)K*~CK|7q8W* zG`hqliX@#*t7#m=M6w`y?w6Q_D-M7=a-uCm1AI6Z{P09og4|C7-x-KjMyh1(xuEhu zVUq?8Y`&B?kb5X&V^vXdWi&)$25?E5C{;r20JWxd_%E-6;0j*nHzb?P`6P)5N}Qm2(GqnMnPnkadKDo)!Ei-}Iq#xaG&s8tEdgvgiy zjhU?dqU8f%&7ekwbjVRexV#%_HIb@D$uTNTqsRmm&=SRB(2yCIhG_gWYMojcs~!mS zU`|c+l2a5orBK0=9yxsWXMkRKssWf29tDhf;}g^xT@z$-2Zl!=KP6Fkz~uu!H&~lP zY_$qYSw_JBQRWW5jRn5-+n$-Z=YCT)6TzH~p8Mp}N~FPZj_%KD@v~aAN^fHwwU`go z;B`(VRtG=3uhhxjbCFAMei6>k;C#mD!ExDF9H1Tv;QV~DMDDHt77h-En5@G*LzU60 z&R_}k2vVvw`lXQ=Zc@opA)?hwzJbMqBQzFZA7!#haMOc($P!o;0{C{MXfBhba=~t% zpUamV(~5GwiYuP=YJz9tmj<1@T^to&?hc-wPOhGwP3@X`*?ZX8HEU+);^6M;>h2(e zb$74>N~22{AP@BnkVk>(Bb&{YMpnVr=APejeirWy2U*4Wde)(dNa%XU1!iI43SK0P zBP6~nmyq?$ARa!1<>Kn;?CkF9q;PU}v1{h!pip?bH}h=fYVYji*o;}#=^h&D)h^76 zG-Ff9mgbTWN!UV&MB}MBvM^PDog!8n*{6qlBA9zReU``tA%`LnsCQ;?$f=o~vx|qq zp{awD!b_oWwfBM#QhGOQ=IQR~<>=_%jQK#QhkKi_@OEBuul8QSxy~q*Pa&hws$<;O z@NZz0hcaH5l%VoLlV6fuy}9J9X9l}mvE|s=H+6P)YU=6X1)u8#iE?k|plE9E)zr(* zl~GEBU7;Q!ZM;15hHEH6Lax;!RLkLp&b1C|T})`CHeMykK8ux+yQ_nvqn(GBm#0%R zZ|`PK_6l#irgjP!M>{Vshh`qEG9v5?@(K-g_wmZ@hKb#jkaMWx83(I%vT`m+m6)jP zCCxJYFwOt9cdemuR8e@uM6EOuOk4bs(oL<8s)?Iqv)M(&%)St9HBCxI6vMKnBuE=c zVyzFPprD{))N0jgO)wReD#4;C{?I}N5q$kn5!xapmI~I#AAbq@eRs~8?9R??lig5B zcMqJsckVqi=bkfX&OP_;ncX>9fW<}HQ;B$2A{w_N9dKrm?ygQ7M%bOsWHO0(XQ#zS zKvgCd_djW_2_x}pPWKGVLafNG?* zGl}k4S9>fRN!sQ=5tDs(|BYwb$X58Mifsny@$ytPnjIS|sh_QKo>MG9KX?$M$!HahB|2ZjotqSSV4y-~k zr>b6TI^pkL?V4X5%m4#N9YUv$Widy61lJ|#KHS63q4lwpMa4mB z*)3(=9L1#FD0_*%oxhbjbA72!odjMNqL`EyNco_Y);_5ml~OIn9pofB27)>#(4nkI zv!rxWp2$>8m5Iz3v#U(x`sx$;-0Bngyy_E~(@m8<%jYj;kt?6Tryzdd1pd-e)=l69 zUQ%S{be(-qKuM98RiDVqt54($t54*H>JynOxGHmB%uUJ{`CPmg zbLW##ydA(jc!IabviLOyp}0q{%H9SU(qlXT=#ZXt4xMx*)-Mu5O~B%8WPnl1hU_@A zB-RN{(OUci&>VBfi+9|9!jD`rV~H^B6otPSzCtcbbrN)p%EqLRtwqP@rW<3G@>|Bq z0M)m}rGgS^<6a?ZHKaZtYlQt_=~MRZ&-|?x0=;6R5WePfG1KJz)q{}d0%ClWe8gO*;@YuG&@&4S< z;I^!}MXAB9S>(nS)PIrFR*J-~F6+%rw5(r+ph(#&>PaE+ZVWvn?be-A`AkYRhAuhQ zyJQZPsGt!&u6S~iTWIQqDy`1#K}E+HXfgdlyiR;`E0H$tm6LW#SyVhAEqa1F$bX9{^BVr} zoTM{oiwrrB)T!QnBKKqJwfteWikQz(4Cjp(^S-SR6!q=M?u%$s_exNv0= zuhr6`66xRqr}xoM{+Ye$RPxZ{M{`#kZ~J;i`SUt~A<~EF+##IwcIxb?12%JH3y7LT zFWTWs(uvL!a+Qyy?3GfDLgg^cjEH81!0Rn^w(?Fs`kOf3FeYD426YS(BfwVRHNY_N zYG6B%v+=b+rgU?%<-nH%b#NLgpJRp4>bw@&$Vw>0Pk_s960B4aWI^C*)Dgb{7-s{3 zT6Nx&)FkLlrX4{r%G(ByD#?-Id}IoBJ7%r%v@Qm(Wif*;p4X!qs1ctUI7=*OOxqwGy;Wu>x4 zDJZ~?SrgiYS$=Y2r7V9p4Ea~wGx+0mWo*C$&Ycg@>4(R$B&`~g*x+Ol^=W2wLSwR@S(ia)ErlOm(=Mg z)=8bN_6Moco-dbrJIavM>6$N-dKhJk)akmpx+b&&zc)3 w0k{vy+y64~E1<%mN3Ytq=kNWy+O9kN9i`I1PWyKpi*2g=^3(OaPXyBc2fD82)c^nh literal 179245 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 Date: Thu, 14 Dec 2023 14:48:10 +0200 Subject: [PATCH 13/29] Fix Android crash --- .../Source/Sentry/Private/Android/SentryTransactionAndroid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp index 657fd8c1..b30306e0 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -13,7 +13,7 @@ SentryTransactionAndroid::SentryTransactionAndroid(jobject transaction) void SentryTransactionAndroid::SetupClassMethods() { - StartChildMethod = GetMethod("startChild", "(Ljava/lang/String;Ljava/lang/String;)Lsentry/io/ISpan;"); + StartChildMethod = GetMethod("startChild", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan;"); FinishMethod = GetMethod("finish", "()V"); } From 6c7664fffb20445dcff526a3df84c6b174247f65 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 15 Dec 2023 14:03:09 +0200 Subject: [PATCH 14/29] Add transaction and span convertors for desktop --- .../SentryConvertorsDesktop.cpp | 20 +++++++++++++++++++ .../Infrastructure/SentryConvertorsDesktop.h | 4 ++++ .../Desktop/SentrySubsystemDesktop.cpp | 7 +------ .../Desktop/SentryTransactionDesktop.cpp | 8 +------- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.cpp index d083f4a1..e9ec2139 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.cpp @@ -2,9 +2,13 @@ #include "SentryConvertorsDesktop.h" #include "SentryId.h" +#include "SentryTransaction.h" +#include "SentrySpan.h" #include "SentryDefines.h" #include "Desktop/SentryIdDesktop.h" +#include "Desktop/SentryTransactionDesktop.h" +#include "Desktop/SentrySpanDesktop.h" #include "UObject/Package.h" #include "UObject/UObjectGlobals.h" @@ -116,6 +120,22 @@ USentryId* SentryConvertorsDesktop::SentryIdToUnreal(sentry_uuid_t id) return unrealId; } +USentryTransaction* SentryConvertorsDesktop::SentryTransactionToUnreal(sentry_transaction_t* transaction) +{ + TSharedPtr transactionNativeImpl = MakeShareable(new SentryTransactionDesktop(transaction)); + USentryTransaction* unrealTransaction = NewObject(); + unrealTransaction->InitWithNativeImpl(transactionNativeImpl); + return unrealTransaction; +} + +USentrySpan* SentryConvertorsDesktop::SentrySpanToUnreal(sentry_span_t* span) +{ + TSharedPtr spanNativeImpl = MakeShareable(new SentrySpanDesktop(span)); + USentrySpan* unrealSpan = NewObject(); + unrealSpan->InitWithNativeImpl(spanNativeImpl); + return unrealSpan; +} + TMap SentryConvertorsDesktop::StringMapToUnreal(sentry_value_t map) { TMap unrealMap; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.h index 1e6ffe37..b10c2060 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/Infrastructure/SentryConvertorsDesktop.h @@ -9,6 +9,8 @@ #if USE_SENTRY_NATIVE class USentryId; +class USentryTransaction; +class USentrySpan; class SentryConvertorsDesktop { @@ -22,6 +24,8 @@ class SentryConvertorsDesktop static ESentryLevel SentryLevelToUnreal(sentry_value_t level); static ESentryLevel SentryLevelToUnreal(sentry_level_t level); static USentryId* SentryIdToUnreal(sentry_uuid_t id); + static USentryTransaction* SentryTransactionToUnreal(sentry_transaction_t* transaction); + static USentrySpan* SentrySpanToUnreal(sentry_span_t* span); static TMap StringMapToUnreal(sentry_value_t map); static TArray StringArrayToUnreal(sentry_value_t array); diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp index b885ea1d..96a94c5f 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySubsystemDesktop.cpp @@ -319,12 +319,7 @@ USentryTransaction* SentrySubsystemDesktop::StartTransaction(const FString& name sentry_transaction_t* nativeTransaction = sentry_transaction_start(transactionContext, sentry_value_new_null()); - TSharedPtr TransactionDesktop = MakeShareable(new SentryTransactionDesktop(nativeTransaction)); - - USentryTransaction* Transaction = NewObject(); - Transaction->InitWithNativeImpl(TransactionDesktop); - - return Transaction; + return SentryConvertorsDesktop::SentryTransactionToUnreal(nativeTransaction); } USentryBeforeSendHandler* SentrySubsystemDesktop::GetBeforeSendHandler() diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp index 3318ba72..780b40a0 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -26,13 +26,7 @@ sentry_transaction_t* SentryTransactionDesktop::GetNativeObject() USentrySpan* SentryTransactionDesktop::StartChild(const FString& operation, const FString& desctiption) { sentry_span_t* nativeSpan = sentry_transaction_start_child(TransactionDesktop, TCHAR_TO_ANSI(*operation), TCHAR_TO_ANSI(*desctiption)); - - TSharedPtr SpanDesktop = MakeShareable(new SentrySpanDesktop(nativeSpan)); - - USentrySpan* Span = NewObject(); - Span->InitWithNativeImpl(SpanDesktop); - - return Span; + return SentryConvertorsDesktop::SentrySpanToUnreal(nativeSpan); } void SentryTransactionDesktop::Finish() From 1f06047b47551593330c07c06e021a1253038a49 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Tue, 19 Dec 2023 16:35:10 +0200 Subject: [PATCH 15/29] Add more performance monitoring API methods --- .../Private/Android/SentrySpanAndroid.cpp | 21 ++++++++ .../Private/Android/SentrySpanAndroid.h | 6 +++ .../Android/SentryTransactionAndroid.cpp | 34 ++++++++++++ .../Android/SentryTransactionAndroid.h | 10 ++++ .../Sentry/Private/Apple/SentrySpanApple.cpp | 25 +++++++++ .../Sentry/Private/Apple/SentrySpanApple.h | 6 +++ .../Private/Apple/SentryTransactionApple.cpp | 30 +++++++++++ .../Private/Apple/SentryTransactionApple.h | 6 +++ .../Private/Desktop/SentrySpanDesktop.cpp | 38 ++++++++++++++ .../Private/Desktop/SentrySpanDesktop.h | 11 ++++ .../Desktop/SentryTransactionDesktop.cpp | 43 +++++++++++++++ .../Desktop/SentryTransactionDesktop.h | 12 +++++ .../Private/Interface/SentrySpanInterface.h | 5 ++ .../Interface/SentryTransactionInterface.h | 6 +++ .../Source/Sentry/Private/SentrySpan.cpp | 40 ++++++++++++++ .../Sentry/Private/SentryTransaction.cpp | 52 ++++++++++++++++++- plugin-dev/Source/Sentry/Public/SentrySpan.h | 20 +++++++ .../Source/Sentry/Public/SentryTransaction.h | 24 +++++++++ 18 files changed, 387 insertions(+), 2 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp index 323db5a8..6fa8e4f7 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp @@ -20,3 +20,24 @@ void SentrySpanAndroid::Finish() { CallMethod(FinishMethod); } + +bool SentrySpanAndroid::IsFinished() +{ + return false; +} + +void SentrySpanAndroid::SetTag(const FString& key, const FString& value) +{ +} + +void SentrySpanAndroid::RemoveTag(const FString& key) +{ +} + +void SentrySpanAndroid::SetData(const FString& key, const TMap& values) +{ +} + +void SentrySpanAndroid::RemoveData(const FString& key) +{ +} diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h index 6fad4bed..53f4bce7 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h @@ -14,6 +14,12 @@ class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper void SetupClassMethods(); virtual void Finish() override; + virtual bool IsFinished() override; + virtual void SetTag(const FString& key, const FString& value) override; + virtual void RemoveTag(const FString& key) override; + virtual void SetData(const FString& key, const TMap& values) override; + virtual void RemoveData(const FString& key) override; + private: FSentryJavaMethod FinishMethod; diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp index b30306e0..ee6cdc3b 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -15,6 +15,10 @@ void SentryTransactionAndroid::SetupClassMethods() { StartChildMethod = GetMethod("startChild", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan;"); FinishMethod = GetMethod("finish", "()V"); + IsFinishedMethod = GetMethod("isFinished", "()Z"); + SetNameMethod = GetMethod("setName", "(Ljava/lang/String;)V"); + SetTagMethod = GetMethod("setTag", "(Ljava/lang/String;Ljava/lang/String;)V"); + SetDataMethod = GetMethod("setData", "(Ljava/lang/String;Ljava/lang/Object;)V"); } USentrySpan* SentryTransactionAndroid::StartChild(const FString& operation, const FString& desctiption) @@ -27,3 +31,33 @@ void SentryTransactionAndroid::Finish() { CallMethod(FinishMethod); } + +bool SentryTransactionAndroid::IsFinished() +{ + return CallMethod(IsFinishedMethod);; +} + +void SentryTransactionAndroid::SetName(const FString& name) +{ + CallMethod(SetNameMethod, *GetJString(name)); +} + +void SentryTransactionAndroid::SetTag(const FString& key, const FString& value) +{ + CallMethod(SetTagMethod, *GetJString(key), *GetJString(value)); +} + +void SentryTransactionAndroid::RemoveTag(const FString& key) +{ + SetTag(key, TEXT("")); +} + +void SentryTransactionAndroid::SetData(const FString& key, const TMap& values) +{ + CallMethod(SetTagMethod, *GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject()); +} + +void SentryTransactionAndroid::RemoveData(const FString& key) +{ + SetData(key, TMap()); +} diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h index f387c500..bc976b8d 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h @@ -15,8 +15,18 @@ class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaOb virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; + virtual bool IsFinished() override; + virtual void SetName(const FString& name) override; + virtual void SetTag(const FString& key, const FString& value) override; + virtual void RemoveTag(const FString& key) override; + virtual void SetData(const FString& key, const TMap& values) override; + virtual void RemoveData(const FString& key) override; private: FSentryJavaMethod StartChildMethod; FSentryJavaMethod FinishMethod; + FSentryJavaMethod IsFinishedMethod; + FSentryJavaMethod SetNameMethod; + FSentryJavaMethod SetTagMethod; + FSentryJavaMethod SetDataMethod; }; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp index 15880379..70b6cb29 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp @@ -21,3 +21,28 @@ void SentrySpanApple::Finish() { [SpanApple finish]; } + +bool SentrySpanApple::IsFinished() +{ + return SpanApple.isFinished; +} + +void SentrySpanApple::SetTag(const FString& key, const FString& value) +{ + [SpanApple setTagValue:value.GetNSString() forKey:key.GetNSString()]; +} + +void SentrySpanApple::RemoveTag(const FString& key) +{ + [SpanApple removeTagForKey:key.GetNSString()]; +} + +void SentrySpanApple::SetData(const FString& key, const TMap& values) +{ + [SpanApple setDataValue:SentryConvertorsApple::StringMapToNative(values) forKey:key.GetNSString()]; +} + +void SentrySpanApple::RemoveData(const FString& key) +{ + [SpanApple removeDataForKey:key.GetNSString()]; +} diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h index 403077f5..ab9fec06 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h @@ -15,6 +15,12 @@ class SentrySpanApple : public ISentrySpan id GetNativeObject(); virtual void Finish() override; + virtual bool IsFinished() override; + virtual void SetTag(const FString& key, const FString& value) override; + virtual void RemoveTag(const FString& key) override; + virtual void SetData(const FString& key, const TMap& values) override; + virtual void RemoveData(const FString& key) override; + private: id SpanApple; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp index 958cdf27..44241f59 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -32,3 +32,33 @@ void SentryTransactionApple::Finish() { [TransactionApple finish]; } + +bool SentryTransactionApple::IsFinished() +{ + return TransactionApple.isFinished; +} + +void SentryTransactionApple::SetName(const FString& name) +{ + [TransactionApple setDataValue:name.GetNSString() forKey:@"name"]; +} + +void SentryTransactionApple::SetTag(const FString& key, const FString& value) +{ + [TransactionApple setTagValue:value.GetNSString() forKey:key.GetNSString()]; +} + +void SentryTransactionApple::RemoveTag(const FString& key) +{ + [TransactionApple removeTagForKey:key.GetNSString()]; +} + +void SentryTransactionApple::SetData(const FString& key, const TMap& values) +{ + [TransactionApple setDataValue:SentryConvertorsApple::StringMapToNative(values) forKey:key.GetNSString()]; +} + +void SentryTransactionApple::RemoveData(const FString& key) +{ + [TransactionApple removeDataForKey:key.GetNSString()]; +} diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h index ad2cbda1..e5b532f8 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h @@ -16,6 +16,12 @@ class SentryTransactionApple : public ISentryTransaction virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; + virtual bool IsFinished() override; + virtual void SetName(const FString& name) override; + virtual void SetTag(const FString& key, const FString& value) override; + virtual void RemoveTag(const FString& key) override; + virtual void SetData(const FString& key, const TMap& values) override; + virtual void RemoveData(const FString& key) override; private: id TransactionApple; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp index 3485c64f..8001ac6d 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp @@ -2,10 +2,13 @@ #include "SentrySpanDesktop.h" +#include "Infrastructure/SentryConvertorsDesktop.h" + #if USE_SENTRY_NATIVE SentrySpanDesktop::SentrySpanDesktop(sentry_span_t* span) : SpanDesktop(span) + , isFinished(false) { } @@ -22,6 +25,41 @@ sentry_span_t* SentrySpanDesktop::GetNativeObject() void SentrySpanDesktop::Finish() { sentry_span_finish(SpanDesktop); + + isFinished = true; +} + +bool SentrySpanDesktop::IsFinished() +{ + return isFinished; +} + +void SentrySpanDesktop::SetTag(const FString& key, const FString& value) +{ + FScopeLock Lock(&CriticalSection); + + sentry_span_set_tag(SpanDesktop, TCHAR_TO_ANSI(*key), TCHAR_TO_ANSI(*value)); +} + +void SentrySpanDesktop::RemoveTag(const FString& key) +{ + FScopeLock Lock(&CriticalSection); + + sentry_span_remove_tag(SpanDesktop, TCHAR_TO_ANSI(*key)); +} + +void SentrySpanDesktop::SetData(const FString& key, const TMap& values) +{ + FScopeLock Lock(&CriticalSection); + + sentry_span_set_data(SpanDesktop, TCHAR_TO_ANSI(*key), SentryConvertorsDesktop::StringMapToNative(values)); +} + +void SentrySpanDesktop::RemoveData(const FString& key) +{ + FScopeLock Lock(&CriticalSection); + + sentry_span_remove_data(SpanDesktop, TCHAR_TO_ANSI(*key)); } #endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h index 18c2abf3..32cd1475 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h @@ -4,6 +4,8 @@ #include "Convenience/SentryInclude.h" +#include "HAL/CriticalSection.h" + #include "Interface/SentrySpanInterface.h" #if USE_SENTRY_NATIVE @@ -17,9 +19,18 @@ class SentrySpanDesktop : public ISentrySpan sentry_span_t* GetNativeObject(); virtual void Finish() override; + virtual bool IsFinished() override; + virtual void SetTag(const FString& key, const FString& value) override; + virtual void RemoveTag(const FString& key) override; + virtual void SetData(const FString& key, const TMap& values) override; + virtual void RemoveData(const FString& key) override; private: sentry_span_t* SpanDesktop; + + FCriticalSection CriticalSection; + + bool isFinished; }; #endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp index 780b40a0..a48a4d82 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -11,6 +11,7 @@ SentryTransactionDesktop::SentryTransactionDesktop(sentry_transaction_t* transaction) : TransactionDesktop(transaction) + , isFinished(false) { } @@ -32,6 +33,48 @@ USentrySpan* SentryTransactionDesktop::StartChild(const FString& operation, cons void SentryTransactionDesktop::Finish() { sentry_transaction_finish(TransactionDesktop); + + isFinished = true; +} + +bool SentryTransactionDesktop::IsFinished() +{ + return isFinished; +} + +void SentryTransactionDesktop::SetName(const FString& name) +{ + FScopeLock Lock(&CriticalSection); + + sentry_transaction_set_name(TransactionDesktop, TCHAR_TO_ANSI(*name)); +} + +void SentryTransactionDesktop::SetTag(const FString& key, const FString& value) +{ + FScopeLock Lock(&CriticalSection); + + sentry_transaction_set_tag(TransactionDesktop, TCHAR_TO_ANSI(*key), TCHAR_TO_ANSI(*value)); +} + +void SentryTransactionDesktop::RemoveTag(const FString& key) +{ + FScopeLock Lock(&CriticalSection); + + sentry_transaction_remove_tag(TransactionDesktop, TCHAR_TO_ANSI(*key)); +} + +void SentryTransactionDesktop::SetData(const FString& key, const TMap& values) +{ + FScopeLock Lock(&CriticalSection); + + sentry_transaction_set_data(TransactionDesktop, TCHAR_TO_ANSI(*key), SentryConvertorsDesktop::StringMapToNative(values)); +} + +void SentryTransactionDesktop::RemoveData(const FString& key) +{ + FScopeLock Lock(&CriticalSection); + + sentry_transaction_remove_data(TransactionDesktop, TCHAR_TO_ANSI(*key)); } #endif diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h index 049e2d67..8713ffd8 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h @@ -4,6 +4,8 @@ #include "Convenience/SentryInclude.h" +#include "HAL/CriticalSection.h" + #include "Interface/SentryTransactionInterface.h" #if USE_SENTRY_NATIVE @@ -18,9 +20,19 @@ class SentryTransactionDesktop : public ISentryTransaction virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; + virtual bool IsFinished() override; + virtual void SetName(const FString& name) override; + virtual void SetTag(const FString& key, const FString& value) override; + virtual void RemoveTag(const FString& key) override; + virtual void SetData(const FString& key, const TMap& values) override; + virtual void RemoveData(const FString& key) override; private: sentry_transaction_t* TransactionDesktop; + + FCriticalSection CriticalSection; + + bool isFinished; }; #endif diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h index ce10b02a..f006ad13 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h @@ -10,4 +10,9 @@ class ISentrySpan virtual ~ISentrySpan() = default; virtual void Finish() = 0; + virtual bool IsFinished() = 0; + virtual void SetTag(const FString& key, const FString& value) = 0; + virtual void RemoveTag(const FString& key) = 0; + virtual void SetData(const FString& key, const TMap& values) = 0; + virtual void RemoveData(const FString& key) = 0; }; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h index 60144dc5..bf5493c3 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h @@ -13,4 +13,10 @@ class ISentryTransaction virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) = 0; virtual void Finish() = 0; + virtual bool IsFinished() = 0; + virtual void SetName(const FString& name) = 0; + virtual void SetTag(const FString& key, const FString& value) = 0; + virtual void RemoveTag(const FString& key) = 0; + virtual void SetData(const FString& key, const TMap& values) = 0; + virtual void RemoveData(const FString& key) = 0; }; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp index 30835c15..38cdfac3 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp @@ -24,6 +24,46 @@ void USentrySpan::Finish() SentrySpanNativeImpl->Finish(); } +bool USentrySpan::IsFinished() +{ + if (!SentrySpanNativeImpl) + return false; + + return SentrySpanNativeImpl->IsFinished(); +} + +void USentrySpan::SetTag(const FString& key, const FString& value) +{ + if (!SentrySpanNativeImpl || SentrySpanNativeImpl->IsFinished()) + return; + + SentrySpanNativeImpl->SetTag(key, value); +} + +void USentrySpan::RemoveTag(const FString& key) +{ + if (!SentrySpanNativeImpl || SentrySpanNativeImpl->IsFinished()) + return; + + SentrySpanNativeImpl->RemoveTag(key); +} + +void USentrySpan::SetData(const FString& key, const TMap& values) +{ + if (!SentrySpanNativeImpl || SentrySpanNativeImpl->IsFinished()) + return; + + SentrySpanNativeImpl->SetData(key, values); +} + +void USentrySpan::RemoveData(const FString& key) +{ + if (!SentrySpanNativeImpl || SentrySpanNativeImpl->IsFinished()) + return; + + SentrySpanNativeImpl->RemoveData(key); +} + void USentrySpan::InitWithNativeImpl(TSharedPtr spanImpl) { SentrySpanNativeImpl = spanImpl; diff --git a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp index 5c00ebb9..8149d0ee 100644 --- a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp +++ b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp @@ -19,7 +19,7 @@ USentryTransaction::USentryTransaction() USentrySpan* USentryTransaction::StartChild(const FString& Operation, const FString& Description) { - if (!SentryTransactionNativeImpl) + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) return nullptr; return SentryTransactionNativeImpl->StartChild(Operation, Description); @@ -27,12 +27,60 @@ USentrySpan* USentryTransaction::StartChild(const FString& Operation, const FStr void USentryTransaction::Finish() { - if (!SentryTransactionNativeImpl) + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) return; SentryTransactionNativeImpl->Finish(); } +bool USentryTransaction::IsFinished() +{ + if (!SentryTransactionNativeImpl) + return false; + + return SentryTransactionNativeImpl->IsFinished(); +} + +void USentryTransaction::SetName(const FString& name) +{ + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) + return; + + SentryTransactionNativeImpl->SetName(name); +} + +void USentryTransaction::SetTag(const FString& key, const FString& value) +{ + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) + return; + + SentryTransactionNativeImpl->SetTag(key, value); +} + +void USentryTransaction::RemoveTag(const FString& key) +{ + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) + return; + + SentryTransactionNativeImpl->RemoveTag(key); +} + +void USentryTransaction::SetData(const FString& key, const TMap& values) +{ + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) + return; + + SentryTransactionNativeImpl->SetData(key, values); +} + +void USentryTransaction::RemoveData(const FString& key) +{ + if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished()) + return; + + SentryTransactionNativeImpl->RemoveData(key); +} + void USentryTransaction::InitWithNativeImpl(TSharedPtr transactionImpl) { SentryTransactionNativeImpl = transactionImpl; diff --git a/plugin-dev/Source/Sentry/Public/SentrySpan.h b/plugin-dev/Source/Sentry/Public/SentrySpan.h index 8475cd9b..a0c8f5ba 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySpan.h +++ b/plugin-dev/Source/Sentry/Public/SentrySpan.h @@ -23,6 +23,26 @@ class SENTRY_API USentrySpan : public UObject UFUNCTION(BlueprintCallable, Category = "Sentry") void Finish(); + /** Checks whether the span finished. */ + UFUNCTION(BlueprintPure, Category = "Sentry") + bool IsFinished(); + + /** Sets tag associated with the span. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void SetTag(const FString& key, const FString& value); + + /** Removes tag associated with the span. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void RemoveTag(const FString& key); + + /** Sets data associated with the span. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void SetData(const FString& key, const TMap& values); + + /** Removes data associated with the span. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void RemoveData(const FString& key); + void InitWithNativeImpl(TSharedPtr spanImpl); TSharedPtr GetNativeImpl(); diff --git a/plugin-dev/Source/Sentry/Public/SentryTransaction.h b/plugin-dev/Source/Sentry/Public/SentryTransaction.h index 081a9b99..cbb410fa 100644 --- a/plugin-dev/Source/Sentry/Public/SentryTransaction.h +++ b/plugin-dev/Source/Sentry/Public/SentryTransaction.h @@ -28,6 +28,30 @@ class SENTRY_API USentryTransaction : public UObject UFUNCTION(BlueprintCallable, Category = "Sentry") void Finish(); + /** Checks whether the transaction finished. */ + UFUNCTION(BlueprintPure, Category = "Sentry") + bool IsFinished(); + + /** Sets transactions name. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void SetName(const FString& name); + + /** Sets tag associated with the transaction. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void SetTag(const FString& key, const FString& value); + + /** Removes tag associated with the transaction. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void RemoveTag(const FString& key); + + /** Sets data associated with the transaction. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void SetData(const FString& key, const TMap& values); + + /** Removes data associated with the transaction. */ + UFUNCTION(BlueprintCallable, Category = "Sentry") + void RemoveData(const FString& key); + void InitWithNativeImpl(TSharedPtr transactionImpl); TSharedPtr GetNativeImpl(); From a6b8710e13acf96db1c09a10b5e6d9b9f2ab80f4 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 10:31:36 +0200 Subject: [PATCH 16/29] Fix comment --- plugin-dev/Source/Sentry/Public/SentrySettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Public/SentrySettings.h b/plugin-dev/Source/Sentry/Public/SentrySettings.h index fa23ce16..5549b6ff 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySettings.h +++ b/plugin-dev/Source/Sentry/Public/SentrySettings.h @@ -14,7 +14,7 @@ enum class ESentryTracesSamplingType : uint8 { // Use uniform sample rate for all transactions UniformSampleRate, - // Control the sample rate based on the transaction itself and the context in which it's captured + // Control the sample rate based on the transaction itself and the context in which it's captured (not implemented) TracesSampler }; From 54c49224e97ea463afc3f8122878d66041cd8f04 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 14:16:03 +0200 Subject: [PATCH 17/29] Fix method signature --- .../Source/Sentry/Private/Android/SentryTransactionAndroid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp index ee6cdc3b..7bba1cda 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -54,7 +54,7 @@ void SentryTransactionAndroid::RemoveTag(const FString& key) void SentryTransactionAndroid::SetData(const FString& key, const TMap& values) { - CallMethod(SetTagMethod, *GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject()); + CallMethod(SetDataMethod, *GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject()); } void SentryTransactionAndroid::RemoveData(const FString& key) From b040fca9b4fbd2599358eae7fe70185135c441c2 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 14:16:19 +0200 Subject: [PATCH 18/29] Add missing Android implmentations --- .../Source/Sentry/Private/Android/SentrySpanAndroid.cpp | 7 +++++++ .../Source/Sentry/Private/Android/SentrySpanAndroid.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp index 6fa8e4f7..5cf0dea6 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp @@ -14,6 +14,9 @@ SentrySpanAndroid::SentrySpanAndroid(jobject span) void SentrySpanAndroid::SetupClassMethods() { FinishMethod = GetMethod("finish", "()V"); + IsFinishedMethod = GetMethod("isFinished", "()Z"); + SetTagMethod = GetMethod("setTag", "(Ljava/lang/String;Ljava/lang/String;)V"); + SetDataMethod = GetMethod("setData", "(Ljava/lang/String;Ljava/lang/Object;)V"); } void SentrySpanAndroid::Finish() @@ -28,16 +31,20 @@ bool SentrySpanAndroid::IsFinished() void SentrySpanAndroid::SetTag(const FString& key, const FString& value) { + CallMethod(SetTagMethod, *GetJString(key), *GetJString(value)); } void SentrySpanAndroid::RemoveTag(const FString& key) { + SetTag(key, TEXT("")); } void SentrySpanAndroid::SetData(const FString& key, const TMap& values) { + CallMethod(SetDataMethod, *GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject()); } void SentrySpanAndroid::RemoveData(const FString& key) { + SetData(key, TMap()); } diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h index 53f4bce7..f8ca0505 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h @@ -23,4 +23,7 @@ class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper private: FSentryJavaMethod FinishMethod; + FSentryJavaMethod IsFinishedMethod; + FSentryJavaMethod SetTagMethod; + FSentryJavaMethod SetDataMethod; }; From fb8b5ced54e6a1b0845e5350569352d368ba558c Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 14:16:36 +0200 Subject: [PATCH 19/29] Update sample --- sample/Content/UI/W_SentryDemo.uasset | Bin 258770 -> 290207 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/sample/Content/UI/W_SentryDemo.uasset b/sample/Content/UI/W_SentryDemo.uasset index 66291249cfe4fe262def44f84753d969ede06b2f..b61fc4f0e054dc5d42889c84f455f25fcc9faca1 100644 GIT binary patch literal 290207 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 L=Zo!xgz5haqvH0k literal 258770 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 From 4151f3cf0150dc6ac7b2d48fde0afde25abc6da8 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 14:48:31 +0200 Subject: [PATCH 20/29] Remove transaction SetName implementation --- .../Source/Sentry/Private/Apple/SentryTransactionApple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp index 44241f59..84e7bc5a 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -40,7 +40,7 @@ bool SentryTransactionApple::IsFinished() void SentryTransactionApple::SetName(const FString& name) { - [TransactionApple setDataValue:name.GetNSString() forKey:@"name"]; + // no corresponding implementation in sentry-cocoa } void SentryTransactionApple::SetTag(const FString& key, const FString& value) From 6f2d793f36f3c3c121583970711467e1adc99d55 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 14:56:48 +0200 Subject: [PATCH 21/29] Update settings order --- .../Source/Sentry/Public/SentrySettings.h | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugin-dev/Source/Sentry/Public/SentrySettings.h b/plugin-dev/Source/Sentry/Public/SentrySettings.h index 5549b6ff..5b6840c6 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySettings.h +++ b/plugin-dev/Source/Sentry/Public/SentrySettings.h @@ -244,6 +244,24 @@ class SENTRY_API USentrySettings : public UObject Meta = (DisplayName = "In-app exludes (for Android/Apple only)", Tooltip = "A list of string prefixes of module names that don't belong to the app.")) TArray InAppExclude; + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Enable tracing", ToolTip = "Flag indicating whether to enable tracing for performance monitoring.")) + bool EnableTracing; + + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Sampling type", ToolTip = "Method of controlling the sample rate for transactions.", EditCondition = "EnableTracing")) + ESentryTracesSamplingType SamplingType; + + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Traces sample rate", ToolTip = "Setting a uniform sample rate for all transactions to a number between 0.0 and 1.0. (For example, to send 20% of transactions, set TracesSampleRate to 0.2).", + EditCondition = "EnableTracing && SamplingType == ESentryTracesSamplingType::UniformSampleRate", EditConditionHides)) + float TracesSampleRate; + + UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", + Meta = (DisplayName = "Traces sampler", ToolTip = "Custom hanler for determining traces sample rate based on the sampling context.", + EditCondition = "EnableTracing && SamplingType == ESentryTracesSamplingType::TracesSampler", EditConditionHides)) + TSubclassOf TracesSampler; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "General|Misc", Meta = (DisplayName = "Promote values to tags")) FTagsPromotion TagsPromotion; @@ -264,24 +282,6 @@ class SENTRY_API USentrySettings : public UObject Meta = (DisplayName = "Enable for promoted builds only", ToolTip = "Flag indicating whether to enable for promoted builds only.")) bool EnableForPromotedBuildsOnly; - UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", - Meta = (DisplayName = "Enable tracing", ToolTip = "Flag indicating whether to enable tracing for performance monitoring.")) - bool EnableTracing; - - UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", - Meta = (DisplayName = "Sampling type", ToolTip = "Method of controlling the sample rate for transactions.", EditCondition = "EnableTracing")) - ESentryTracesSamplingType SamplingType; - - UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", - Meta = (DisplayName = "Traces sample rate", ToolTip = "Setting a uniform sample rate for all transactions to a number between 0.0 and 1.0. (For example, to send 20% of transactions, set TracesSampleRate to 0.2).", - EditCondition = "EnableTracing && SamplingType == ESentryTracesSamplingType::UniformSampleRate", EditConditionHides)) - float TracesSampleRate; - - UPROPERTY(Config, EditAnywhere, Category = "General|Performance Monitoring", - Meta = (DisplayName = "Traces sampler", ToolTip = "Custom hanler for determining traces sample rate based on the sampling context.", - EditCondition = "EnableTracing && SamplingType == ESentryTracesSamplingType::TracesSampler", EditConditionHides)) - TSubclassOf TracesSampler; - UPROPERTY(Config, EditAnywhere, Category = "Debug Symbols", Meta = (DisplayName = "Upload debug symbols automatically", ToolTip = "Flag indicating whether to automatically upload debug symbols to Sentry when packaging the app.")) bool UploadSymbolsAutomatically; From 69bd7cbc8814cc2e9c95361b9eccf261a4f3095f Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 15:00:47 +0200 Subject: [PATCH 22/29] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7767cc0..be428116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Add performance monitoring API ([#470](https://github.com/getsentry/sentry-unreal/pull/470)) + ### Fixes - Fix Linux intermediates paths in `FilterPlugin.ini` ([#468](https://github.com/getsentry/sentry-unreal/pull/468)) From 2d9df06131c5bb9c41dc09a94fd8a99a3e0c65c2 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 20 Dec 2023 15:14:11 +0200 Subject: [PATCH 23/29] Fix members init order --- plugin-dev/Source/Sentry/Private/SentrySettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Private/SentrySettings.cpp b/plugin-dev/Source/Sentry/Private/SentrySettings.cpp index 7a94bb0f..07e85bb5 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySettings.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySettings.cpp @@ -27,10 +27,10 @@ USentrySettings::USentrySettings(const FObjectInitializer& ObjectInitializer) , UseProxy(false) , ProxyUrl() , BeforeSendHandler(USentryBeforeSendHandler::StaticClass()) - , EnableForPromotedBuildsOnly(false) , EnableTracing(false) , SamplingType(ESentryTracesSamplingType::UniformSampleRate) , TracesSampleRate(1.0f) + , EnableForPromotedBuildsOnly(false) , UploadSymbolsAutomatically(false) , IncludeSources(false) , CrashReporterUrl() From e9b8fd88c9f71b91fc4f17df307b9cc03b0c587a Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Thu, 21 Dec 2023 10:49:38 +0200 Subject: [PATCH 24/29] Add const specifier to IsFinished method --- plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp | 2 +- plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h | 2 +- .../Source/Sentry/Private/Android/SentryTransactionAndroid.cpp | 2 +- .../Source/Sentry/Private/Android/SentryTransactionAndroid.h | 2 +- plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp | 2 +- plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h | 2 +- .../Source/Sentry/Private/Apple/SentryTransactionApple.cpp | 2 +- plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h | 2 +- plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp | 2 +- plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h | 2 +- .../Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp | 2 +- .../Source/Sentry/Private/Desktop/SentryTransactionDesktop.h | 2 +- .../Source/Sentry/Private/Interface/SentrySpanInterface.h | 2 +- .../Sentry/Private/Interface/SentryTransactionInterface.h | 2 +- plugin-dev/Source/Sentry/Private/SentrySpan.cpp | 2 +- plugin-dev/Source/Sentry/Private/SentryTransaction.cpp | 2 +- plugin-dev/Source/Sentry/Public/SentrySpan.h | 2 +- plugin-dev/Source/Sentry/Public/SentryTransaction.h | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp index 5cf0dea6..7e03e0a5 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp @@ -24,7 +24,7 @@ void SentrySpanAndroid::Finish() CallMethod(FinishMethod); } -bool SentrySpanAndroid::IsFinished() +bool SentrySpanAndroid::IsFinished() const { return false; } diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h index f8ca0505..2ee130e2 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h @@ -14,7 +14,7 @@ class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper void SetupClassMethods(); virtual void Finish() override; - virtual bool IsFinished() override; + virtual bool IsFinished() const override; virtual void SetTag(const FString& key, const FString& value) override; virtual void RemoveTag(const FString& key) override; virtual void SetData(const FString& key, const TMap& values) override; diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp index 7bba1cda..f67bffba 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.cpp @@ -32,7 +32,7 @@ void SentryTransactionAndroid::Finish() CallMethod(FinishMethod); } -bool SentryTransactionAndroid::IsFinished() +bool SentryTransactionAndroid::IsFinished() const { return CallMethod(IsFinishedMethod);; } diff --git a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h index bc976b8d..aa51c851 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h +++ b/plugin-dev/Source/Sentry/Private/Android/SentryTransactionAndroid.h @@ -15,7 +15,7 @@ class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaOb virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; - virtual bool IsFinished() override; + virtual bool IsFinished() const override; virtual void SetName(const FString& name) override; virtual void SetTag(const FString& key, const FString& value) override; virtual void RemoveTag(const FString& key) override; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp index 70b6cb29..e6830ade 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp @@ -22,7 +22,7 @@ void SentrySpanApple::Finish() [SpanApple finish]; } -bool SentrySpanApple::IsFinished() +bool SentrySpanApple::IsFinished() const { return SpanApple.isFinished; } diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h index ab9fec06..d508e651 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h @@ -15,7 +15,7 @@ class SentrySpanApple : public ISentrySpan id GetNativeObject(); virtual void Finish() override; - virtual bool IsFinished() override; + virtual bool IsFinished() const override; virtual void SetTag(const FString& key, const FString& value) override; virtual void RemoveTag(const FString& key) override; virtual void SetData(const FString& key, const TMap& values) override; diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp index 84e7bc5a..05eabb20 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -33,7 +33,7 @@ void SentryTransactionApple::Finish() [TransactionApple finish]; } -bool SentryTransactionApple::IsFinished() +bool SentryTransactionApple::IsFinished() const { return TransactionApple.isFinished; } diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h index e5b532f8..f6b08db7 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.h @@ -16,7 +16,7 @@ class SentryTransactionApple : public ISentryTransaction virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; - virtual bool IsFinished() override; + virtual bool IsFinished() const override; virtual void SetName(const FString& name) override; virtual void SetTag(const FString& key, const FString& value) override; virtual void RemoveTag(const FString& key) override; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp index 8001ac6d..d3f65cbe 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp @@ -29,7 +29,7 @@ void SentrySpanDesktop::Finish() isFinished = true; } -bool SentrySpanDesktop::IsFinished() +bool SentrySpanDesktop::IsFinished() const { return isFinished; } diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h index 32cd1475..f8c00552 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.h @@ -19,7 +19,7 @@ class SentrySpanDesktop : public ISentrySpan sentry_span_t* GetNativeObject(); virtual void Finish() override; - virtual bool IsFinished() override; + virtual bool IsFinished() const override; virtual void SetTag(const FString& key, const FString& value) override; virtual void RemoveTag(const FString& key) override; virtual void SetData(const FString& key, const TMap& values) override; diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp index a48a4d82..aceef7ab 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.cpp @@ -37,7 +37,7 @@ void SentryTransactionDesktop::Finish() isFinished = true; } -bool SentryTransactionDesktop::IsFinished() +bool SentryTransactionDesktop::IsFinished() const { return isFinished; } diff --git a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h index 8713ffd8..5a6f59ea 100644 --- a/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h +++ b/plugin-dev/Source/Sentry/Private/Desktop/SentryTransactionDesktop.h @@ -20,7 +20,7 @@ class SentryTransactionDesktop : public ISentryTransaction virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override; virtual void Finish() override; - virtual bool IsFinished() override; + virtual bool IsFinished() const override; virtual void SetName(const FString& name) override; virtual void SetTag(const FString& key, const FString& value) override; virtual void RemoveTag(const FString& key) override; diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h index f006ad13..08ec7a1c 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentrySpanInterface.h @@ -10,7 +10,7 @@ class ISentrySpan virtual ~ISentrySpan() = default; virtual void Finish() = 0; - virtual bool IsFinished() = 0; + virtual bool IsFinished() const = 0; virtual void SetTag(const FString& key, const FString& value) = 0; virtual void RemoveTag(const FString& key) = 0; virtual void SetData(const FString& key, const TMap& values) = 0; diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h index bf5493c3..1cdbff55 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentryTransactionInterface.h @@ -13,7 +13,7 @@ class ISentryTransaction virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) = 0; virtual void Finish() = 0; - virtual bool IsFinished() = 0; + virtual bool IsFinished() const = 0; virtual void SetName(const FString& name) = 0; virtual void SetTag(const FString& key, const FString& value) = 0; virtual void RemoveTag(const FString& key) = 0; diff --git a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp index 38cdfac3..7b8c6000 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySpan.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySpan.cpp @@ -24,7 +24,7 @@ void USentrySpan::Finish() SentrySpanNativeImpl->Finish(); } -bool USentrySpan::IsFinished() +bool USentrySpan::IsFinished() const { if (!SentrySpanNativeImpl) return false; diff --git a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp index 8149d0ee..42159ac1 100644 --- a/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp +++ b/plugin-dev/Source/Sentry/Private/SentryTransaction.cpp @@ -33,7 +33,7 @@ void USentryTransaction::Finish() SentryTransactionNativeImpl->Finish(); } -bool USentryTransaction::IsFinished() +bool USentryTransaction::IsFinished() const { if (!SentryTransactionNativeImpl) return false; diff --git a/plugin-dev/Source/Sentry/Public/SentrySpan.h b/plugin-dev/Source/Sentry/Public/SentrySpan.h index a0c8f5ba..5347a109 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySpan.h +++ b/plugin-dev/Source/Sentry/Public/SentrySpan.h @@ -25,7 +25,7 @@ class SENTRY_API USentrySpan : public UObject /** Checks whether the span finished. */ UFUNCTION(BlueprintPure, Category = "Sentry") - bool IsFinished(); + bool IsFinished() const; /** Sets tag associated with the span. */ UFUNCTION(BlueprintCallable, Category = "Sentry") diff --git a/plugin-dev/Source/Sentry/Public/SentryTransaction.h b/plugin-dev/Source/Sentry/Public/SentryTransaction.h index cbb410fa..f17aab57 100644 --- a/plugin-dev/Source/Sentry/Public/SentryTransaction.h +++ b/plugin-dev/Source/Sentry/Public/SentryTransaction.h @@ -30,7 +30,7 @@ class SENTRY_API USentryTransaction : public UObject /** Checks whether the transaction finished. */ UFUNCTION(BlueprintPure, Category = "Sentry") - bool IsFinished(); + bool IsFinished() const; /** Sets transactions name. */ UFUNCTION(BlueprintCallable, Category = "Sentry") From c523eeff51be1ff8589ec73367e0de618af7c9a6 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Thu, 21 Dec 2023 10:49:53 +0200 Subject: [PATCH 25/29] Add automation test for transaction and span --- .../Private/Tests/SentrySubsystem.spec.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Tests/SentrySubsystem.spec.cpp b/plugin-dev/Source/Sentry/Private/Tests/SentrySubsystem.spec.cpp index ba542367..1fac3287 100644 --- a/plugin-dev/Source/Sentry/Private/Tests/SentrySubsystem.spec.cpp +++ b/plugin-dev/Source/Sentry/Private/Tests/SentrySubsystem.spec.cpp @@ -2,6 +2,8 @@ #include "SentrySubsystem.h" #include "SentryEvent.h" +#include "SentryTransaction.h" +#include "SentrySpan.h" #include "UObject/UObjectGlobals.h" #include "Misc/AutomationTest.h" @@ -64,6 +66,26 @@ void SentrySubsystemSpec::Define() }); }); + Describe("Transaction", [this]() + { + It("should be started and finished", [this]() + { + USentryTransaction* transaction = SentrySubsystem->StartTransaction(TEXT("Automation transaction"), TEXT("Automation operation")); + TestNotNull("Transaction is non-null", transaction); + TestFalse("Transaction is not finished", transaction->IsFinished()); + + USentrySpan* span = transaction->StartChild(TEXT("Automation span"), TEXT("Description text")); + TestNotNull("Span is non-null", span); + TestFalse("Span is not finished", span->IsFinished()); + + span->Finish(); + TestTrue("Span is finished", span->IsFinished()); + + transaction->Finish(); + TestTrue("Transaction is finished", transaction->IsFinished()); + }); + }); + AfterEach([this] { SentrySubsystem->Close(); From 5a9993436549101023aff4783f45a04b5110463e Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Fri, 12 Jan 2024 13:45:30 +0200 Subject: [PATCH 26/29] Update plugin-dev/Source/Sentry/Private/SentrySettings.cpp Co-authored-by: Stefan Jandl --- plugin-dev/Source/Sentry/Private/SentrySettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Private/SentrySettings.cpp b/plugin-dev/Source/Sentry/Private/SentrySettings.cpp index 07e85bb5..56ee7ae2 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySettings.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySettings.cpp @@ -29,7 +29,7 @@ USentrySettings::USentrySettings(const FObjectInitializer& ObjectInitializer) , BeforeSendHandler(USentryBeforeSendHandler::StaticClass()) , EnableTracing(false) , SamplingType(ESentryTracesSamplingType::UniformSampleRate) - , TracesSampleRate(1.0f) + , TracesSampleRate(0.0f) , EnableForPromotedBuildsOnly(false) , UploadSymbolsAutomatically(false) , IncludeSources(false) From c4eeadb95c7c85946c94eeaefeff39ddcf8c00a3 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Tue, 16 Jan 2024 16:22:33 +0200 Subject: [PATCH 27/29] Add missing implementation for Android span --- plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp index 7e03e0a5..5c401a7e 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp @@ -26,7 +26,7 @@ void SentrySpanAndroid::Finish() bool SentrySpanAndroid::IsFinished() const { - return false; + return CallMethod(IsFinishedMethod);; } void SentrySpanAndroid::SetTag(const FString& key, const FString& value) From 337b2a141fe533939535b9723dd34a22d3afc730 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Tue, 16 Jan 2024 16:34:06 +0200 Subject: [PATCH 28/29] Add log messages about sampling functions not being implemented --- .../Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp | 1 + plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp index 18153ea1..56dccfb0 100644 --- a/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/SentrySubsystemAndroid.cpp @@ -51,6 +51,7 @@ void SentrySubsystemAndroid::InitWithSettings(const USentrySettings* settings, U } if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler) { + UE_LOG(LogSentrySdk, Warning, TEXT("Currently sampling functions are not supported")); SettingsJson->SetNumberField(TEXT("tracesSampler"), (jlong)0); } diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp index 70f55866..50fb55df 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentrySubsystemApple.cpp @@ -75,6 +75,7 @@ void SentrySubsystemApple::InitWithSettings(const USentrySettings* settings, USe } if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler) { + UE_LOG(LogSentrySdk, Warning, TEXT("Currently sampling functions are not supported")); options.tracesSampler = nil; } }]; From 60af3e3b96e94217f41cafba91f00a3bba9c93c5 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Tue, 16 Jan 2024 16:38:53 +0200 Subject: [PATCH 29/29] Add log message about missing transaction SetName implementation for Apple --- .../Source/Sentry/Private/Apple/SentryTransactionApple.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp index 05eabb20..78891754 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/SentryTransactionApple.cpp @@ -7,6 +7,8 @@ #include "Convenience/SentryInclude.h" #include "Convenience/SentryMacro.h" +#include "SentryDefines.h" + SentryTransactionApple::SentryTransactionApple(id transaction) { TransactionApple = transaction; @@ -41,6 +43,7 @@ bool SentryTransactionApple::IsFinished() const void SentryTransactionApple::SetName(const FString& name) { // no corresponding implementation in sentry-cocoa + UE_LOG(LogSentrySdk, Warning, TEXT("The Cocoa SDK doesn't currently support SetName function")); } void SentryTransactionApple::SetTag(const FString& key, const FString& value)