From d60f70a124a225af7e59a79ac26e64921a9d8ac9 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Wed, 5 Apr 2023 10:27:42 +0200 Subject: [PATCH 01/42] chore: Add repo link to all KSCrash files (#2845) --- .../SentryCrash/Installations/SentryCrashInstallation+Private.h | 1 + Sources/SentryCrash/Installations/SentryCrashInstallation.h | 1 + Sources/SentryCrash/Installations/SentryCrashInstallation.m | 1 + Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c | 1 + Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h | 1 + Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c | 1 + Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h | 1 + .../Recording/Monitors/SentryCrashMonitor_CPPException.cpp | 1 + .../Recording/Monitors/SentryCrashMonitor_CPPException.h | 1 + .../Recording/Monitors/SentryCrashMonitor_MachException.c | 1 + .../Recording/Monitors/SentryCrashMonitor_MachException.h | 1 + .../Recording/Monitors/SentryCrashMonitor_NSException.h | 1 + .../Recording/Monitors/SentryCrashMonitor_NSException.m | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h | 1 + .../SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m | 1 + Sources/SentryCrash/Recording/SentryCrash.h | 1 + Sources/SentryCrash/Recording/SentryCrash.m | 1 + Sources/SentryCrash/Recording/SentryCrashC.c | 1 + Sources/SentryCrash/Recording/SentryCrashC.h | 1 + Sources/SentryCrash/Recording/SentryCrashCachedData.c | 1 + Sources/SentryCrash/Recording/SentryCrashCachedData.h | 1 + Sources/SentryCrash/Recording/SentryCrashDoctor.h | 1 + Sources/SentryCrash/Recording/SentryCrashDoctor.m | 1 + Sources/SentryCrash/Recording/SentryCrashReport.c | 1 + Sources/SentryCrash/Recording/SentryCrashReport.h | 1 + Sources/SentryCrash/Recording/SentryCrashReportFields.h | 1 + Sources/SentryCrash/Recording/SentryCrashReportFixer.c | 1 + Sources/SentryCrash/Recording/SentryCrashReportFixer.h | 1 + Sources/SentryCrash/Recording/SentryCrashReportStore.c | 1 + Sources/SentryCrash/Recording/SentryCrashReportStore.h | 1 + Sources/SentryCrash/Recording/SentryCrashReportVersion.h | 1 + Sources/SentryCrash/Recording/SentryCrashReportWriter.h | 1 + Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h | 1 + .../Recording/Tools/NSError+SentrySimpleConstructor.h | 1 + .../Recording/Tools/NSError+SentrySimpleConstructor.m | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashID.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashID.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h | 1 + .../Recording/Tools/SentryCrashMachineContext_Apple.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h | 1 + .../Recording/Tools/SentryCrashStackCursor_Backtrace.c | 1 + .../Recording/Tools/SentryCrashStackCursor_Backtrace.h | 1 + .../Recording/Tools/SentryCrashStackCursor_MachineContext.c | 1 + .../Recording/Tools/SentryCrashStackCursor_MachineContext.h | 1 + .../Recording/Tools/SentryCrashStackCursor_SelfThread.c | 1 + .../Recording/Tools/SentryCrashStackCursor_SelfThread.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashString.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashString.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashThread.c | 1 + Sources/SentryCrash/Recording/Tools/SentryCrashThread.h | 1 + Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h | 1 + .../SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h | 1 + .../SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m | 1 + .../Reporting/Filters/Tools/Container+SentryDeepSearch.h | 1 + .../Reporting/Filters/Tools/Container+SentryDeepSearch.m | 1 + Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h | 1 + Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h | 1 + Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m | 1 + Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m | 1 + Tests/SentryTests/SentryCrash/FileBasedTestCase.h | 1 + Tests/SentryTests/SentryCrash/FileBasedTestCase.m | 1 + Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m | 1 + Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m | 1 + .../SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m | 1 + .../SentryCrash/SentryCrashMonitor_NSException_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m | 1 + Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m | 1 + Tests/SentryTests/SentryCrash/TestThread.h | 1 + Tests/SentryTests/SentryCrash/TestThread.m | 1 + Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h | 1 + Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m | 1 + 122 files changed, 122 insertions(+) diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h b/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h index ccb0605db57..2522f1809ec 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFieldProperties.h // diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation.h b/Sources/SentryCrash/Installations/SentryCrashInstallation.h index 4eba18e1f67..cfddcfe4d6c 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation.h +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashInstallation.h // diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation.m b/Sources/SentryCrash/Installations/SentryCrashInstallation.m index 763613f371c..7d644167b1b 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation.m +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashInstallation.m // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c index 055b19fcf31..080720bf20d 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h index 0c8e3d17680..8e4c9259a71 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h index 7de0b426370..08eec503539 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorContext.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitorContext.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c index 86481cc3b18..b14aead36d9 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitorType.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h index 7a388a42098..e38dd9b097f 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitorType.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitorType.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c index 19c6a830392..0ed2e122597 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_AppState.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h index fe1235586dd..e00da4e74d0 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_AppState.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_AppState.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp index f24617fcf7d..e7d13d427ce 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_CPPException.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h index 7b4d71d3a45..39908fb7cb4 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_CPPException.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c index ad9fbf88392..6176dd84a9d 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_MachException.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h index 013db661ef8..e72d29422c8 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_MachException.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h index c6a3f2974cf..76280ba508a 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_NSException.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m index 91573c5057d..4d1a658c796 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_NSException.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_NSException.m // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c index 382976344a9..a73c73668cd 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Signal.c // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h index 5c3802f38ff..f03f3fdb329 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Signal.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h index 9b0bb7ddd8a..8fbe326b0ef 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_System.h // diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m index aecb5680acf..84f225539fe 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_System.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_System.m // diff --git a/Sources/SentryCrash/Recording/SentryCrash.h b/Sources/SentryCrash/Recording/SentryCrash.h index 1ba6b775956..c7188e94e15 100644 --- a/Sources/SentryCrash/Recording/SentryCrash.h +++ b/Sources/SentryCrash/Recording/SentryCrash.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrash.h // diff --git a/Sources/SentryCrash/Recording/SentryCrash.m b/Sources/SentryCrash/Recording/SentryCrash.m index 368a2873ead..de82c959fec 100644 --- a/Sources/SentryCrash/Recording/SentryCrash.m +++ b/Sources/SentryCrash/Recording/SentryCrash.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrash.m // diff --git a/Sources/SentryCrash/Recording/SentryCrashC.c b/Sources/SentryCrash/Recording/SentryCrashC.c index b058957dfc3..ff21ab1ffcf 100644 --- a/Sources/SentryCrash/Recording/SentryCrashC.c +++ b/Sources/SentryCrash/Recording/SentryCrashC.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashC.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashC.h b/Sources/SentryCrash/Recording/SentryCrashC.h index eeba4f817e0..9601289b7a6 100644 --- a/Sources/SentryCrash/Recording/SentryCrashC.h +++ b/Sources/SentryCrash/Recording/SentryCrashC.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashC.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashCachedData.c b/Sources/SentryCrash/Recording/SentryCrashCachedData.c index 66e21255679..5302eb41b51 100644 --- a/Sources/SentryCrash/Recording/SentryCrashCachedData.c +++ b/Sources/SentryCrash/Recording/SentryCrashCachedData.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCachedData.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashCachedData.h b/Sources/SentryCrash/Recording/SentryCrashCachedData.h index d0f35cbaedf..f6a89f95d82 100644 --- a/Sources/SentryCrash/Recording/SentryCrashCachedData.h +++ b/Sources/SentryCrash/Recording/SentryCrashCachedData.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCachedData.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashDoctor.h b/Sources/SentryCrash/Recording/SentryCrashDoctor.h index af7aab96a09..09734fbadbf 100644 --- a/Sources/SentryCrash/Recording/SentryCrashDoctor.h +++ b/Sources/SentryCrash/Recording/SentryCrashDoctor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDoctor.h // SentryCrash diff --git a/Sources/SentryCrash/Recording/SentryCrashDoctor.m b/Sources/SentryCrash/Recording/SentryCrashDoctor.m index e9a1ea785ab..759d856ddc3 100644 --- a/Sources/SentryCrash/Recording/SentryCrashDoctor.m +++ b/Sources/SentryCrash/Recording/SentryCrashDoctor.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDoctor.m // SentryCrash diff --git a/Sources/SentryCrash/Recording/SentryCrashReport.c b/Sources/SentryCrash/Recording/SentryCrashReport.c index 41c73794032..07500c2fcaa 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReport.c +++ b/Sources/SentryCrash/Recording/SentryCrashReport.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReport.m // diff --git a/Sources/SentryCrash/Recording/SentryCrashReport.h b/Sources/SentryCrash/Recording/SentryCrashReport.h index fa06ae182aa..1eb0aeb8965 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReport.h +++ b/Sources/SentryCrash/Recording/SentryCrashReport.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReport.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportFields.h b/Sources/SentryCrash/Recording/SentryCrashReportFields.h index cef8cb36d00..8736d2b6ea0 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportFields.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportFields.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFields.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportFixer.c b/Sources/SentryCrash/Recording/SentryCrashReportFixer.c index 0a1fe65494a..feccd2b3e1c 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportFixer.c +++ b/Sources/SentryCrash/Recording/SentryCrashReportFixer.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFixer.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportFixer.h b/Sources/SentryCrash/Recording/SentryCrashReportFixer.h index bc259043078..ad2e77b09d4 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportFixer.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportFixer.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFixer.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportStore.c b/Sources/SentryCrash/Recording/SentryCrashReportStore.c index 997064a6fed..f7bba6bb23b 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportStore.c +++ b/Sources/SentryCrash/Recording/SentryCrashReportStore.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportStore.c // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportStore.h b/Sources/SentryCrash/Recording/SentryCrashReportStore.h index 24ced7d9811..7b101368b4b 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportStore.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportStore.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportStore.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportVersion.h b/Sources/SentryCrash/Recording/SentryCrashReportVersion.h index 651bfd863f7..4f7e8fe4b8d 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportVersion.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportVersion.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportVersion.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashReportWriter.h b/Sources/SentryCrash/Recording/SentryCrashReportWriter.h index 4febb0bf91f..2cacc25add5 100644 --- a/Sources/SentryCrash/Recording/SentryCrashReportWriter.h +++ b/Sources/SentryCrash/Recording/SentryCrashReportWriter.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportWriter.h // diff --git a/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h b/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h index 16bf1d83e43..aeae47c3c19 100644 --- a/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h +++ b/Sources/SentryCrash/Recording/SentryCrashSystemCapabilities.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSystemCapabilities.h // diff --git a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h index bd3c5c5b180..ff6ac7808c5 100644 --- a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h +++ b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // NSError+SimpleConstructor.h // diff --git a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m index 37feed23691..e9b6c6144d3 100644 --- a/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m +++ b/Sources/SentryCrash/Recording/Tools/NSError+SentrySimpleConstructor.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // NSError+SimpleConstructor.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c index cd4d147ffc5..52201766d04 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h index 982006f3b50..1ec4a85d3b6 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h index 42cd748a15f..dddb2911635 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_Apple.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_Apple.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c index 90bb24551b7..3a4a71878e5 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_arm.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c index 4cd9939b248..d244494ddfb 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_arm64_Apple.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c index 36acd112d89..c95ac6275fa 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_x86_32.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c index 27f1e62cba5..aaca4b24b07 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_x86_64.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c index ef956a4a4c6..649f4e350cc 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDebug.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h index c4aeaa26e15..f29c6e634cb 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDebug.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDebug.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c index 573d22a8e9f..4706b6de389 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDynamicLinker.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h index 185d92e144b..7f7bfda8432 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashDynamicLinker.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDynamicLinker.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c index 500b7300d29..0380419b773 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashFileUtils.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h index 1218e645c45..596c48b0c15 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashFileUtils.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashFileUtils.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashID.c b/Sources/SentryCrash/Recording/Tools/SentryCrashID.c index be519cb5f60..5f98467c500 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashID.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashID.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashID.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashID.h b/Sources/SentryCrash/Recording/Tools/SentryCrashID.h index bce2d5cd2ce..3ca996f9f76 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashID.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashID.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashID.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c index 450047c8a26..53d4c38be5e 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodec.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h index 6cda5ad513a..035fad33454 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodec.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h index ebfa2d4929f..c8fb49ae598 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodecObjC.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m index 0e81e382995..e9d935bc7b1 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodecObjC.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodecObjC.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c index b6edb9b5496..0b921437107 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashLogger.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h index 5d0427cb173..61bfbb4b48e 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashLogger.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashLogger.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c index 447f4194199..7fb4063bf5f 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMachineContext.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h index fb494eeff25..6e3cb9e1f98 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMachineContext.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h index 15c2d057512..fcf1cde44a2 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMachineContext_Apple.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMachineContext_Apple.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c index 5a64d1ce3c2..763a9b1fe29 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMemory.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h index b9c9a5d6fed..06cb2a9870d 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashMemory.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMemory.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c index f5017a9951b..ee1ef1ffdd6 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashObjC.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h index 70d5f58218b..2374029db38 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashObjC.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashObjC.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c index c8c5069fce6..8d27ed32597 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSignalInfo.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h index 5965a1b0cb0..63ac46678e7 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSignalInfo.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSignalInfo.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c index 409ab9ed575..b4d79d572a3 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h index 3a05a64ad62..f2bac8c3d87 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c index b8b349ccacd..34057a64432 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_Backtrace.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h index 1110e9e47df..0b60ebb76d9 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_Backtrace.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_Backtrace.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c index 768c5225eb1..77bb3749564 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_MachineContext.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h index abd597c55d7..a76013b360f 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_MachineContext.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_MachineContext.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c index 3afd3fd7e93..68ae0a401fe 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_SelfThread.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h index c5cc261eeae..46b4d679bca 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashStackCursor_SelfThread.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashStackCursor_SelfThread.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashString.c b/Sources/SentryCrash/Recording/Tools/SentryCrashString.c index d46e3355b83..3252ffe4ed0 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashString.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashString.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashString.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashString.h b/Sources/SentryCrash/Recording/Tools/SentryCrashString.h index 426c9cbdf8e..cd5b2731459 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashString.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashString.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashString.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c index d57237551f3..0f151a13158 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSymbolicator.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h index a57d54f1ed8..915cdddbe86 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSymbolicator.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSymbolicator.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c index 989ec2728bd..7a6265837f4 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSysCtl.m // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h index 6e699de6be1..faf3398845c 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashSysCtl.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSysCtl.h // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c index 3472673f28d..db829421433 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.c @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashThread.c // diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h index 656d12c378e..7244aeb297c 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashThread.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashThread.h // diff --git a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h index 9934ff4fb16..de106c9f054 100644 --- a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h +++ b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilter.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilter.h // diff --git a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h index c3626342b6a..e9ccd5aaf9d 100644 --- a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h +++ b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilterBasic.h // diff --git a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m index 17c7c66a625..37f738aa560 100644 --- a/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m +++ b/Sources/SentryCrash/Reporting/Filters/SentryCrashReportFilterBasic.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilterBasic.m // diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h index c7948fbdcf4..f822d1e877b 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h +++ b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // Container+DeepSearch // diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m index 881a0dc9b55..1f025de48ce 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m +++ b/Sources/SentryCrash/Reporting/Filters/Tools/Container+SentryDeepSearch.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // Container+DeepSearch // diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h b/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h index cb6e5f7fd23..f2758879830 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h +++ b/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashVarArgs.h // diff --git a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h index e5d8da87e92..dec6c52f429 100644 --- a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h +++ b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCString.h // diff --git a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m index af11161cffa..d41aec28810 100644 --- a/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m +++ b/Sources/SentryCrash/Reporting/Tools/SentryCrashCString.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCString.m // diff --git a/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m b/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m index b2126776931..1636cd1d2bf 100644 --- a/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m +++ b/Tests/SentryTests/SentryCrash/Container+DeepSearch_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // Container+DeepSearch_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/FileBasedTestCase.h b/Tests/SentryTests/SentryCrash/FileBasedTestCase.h index 50c9b82ad37..7c60f3bc4be 100644 --- a/Tests/SentryTests/SentryCrash/FileBasedTestCase.h +++ b/Tests/SentryTests/SentryCrash/FileBasedTestCase.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // FileBasedTestCase.h // diff --git a/Tests/SentryTests/SentryCrash/FileBasedTestCase.m b/Tests/SentryTests/SentryCrash/FileBasedTestCase.m index cfc70abde2d..cbdd20caf80 100644 --- a/Tests/SentryTests/SentryCrash/FileBasedTestCase.m +++ b/Tests/SentryTests/SentryCrash/FileBasedTestCase.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // FileBasedTestCase.m // diff --git a/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m b/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m index 57a1b7f6817..49b47e7b329 100644 --- a/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m +++ b/Tests/SentryTests/SentryCrash/NSError+SimpleConstructor_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // NSError+SimpleConstructor_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m b/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m index 1797edd76da..f0bd6f32683 100644 --- a/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m +++ b/Tests/SentryTests/SentryCrash/RFC3339UTFString_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // RFC3339DateTool_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m index 9541d30b493..ac777d231fb 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCPU_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m index 7c582c52762..eb7042b6fc0 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCString_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCString_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m index 0e8d2610470..0999a63393f 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCachedData_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashCachedData_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m index 6cff4b6796f..0ba13504d64 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashDynamicLinker_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashDynamicLinker_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m index 419ff6c953f..fb5f5197b4e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashFileUtils_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashFileUtils_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m index bdccc7a5386..0bc90ad5b1b 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashJSONCodec_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m index f3277efd71e..9c846fbb5c6 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashLogger_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashLogger_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m index aa2b0b602ad..f55c2981c2d 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMach_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMach_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m index 6bc8d249988..9f082ae37f9 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMemory_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // sentrycrashmemory_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m index 584634bec2b..7e541b6fd4e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_AppState_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_AppState_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m index b6d52eb380a..1746654b5bd 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_NSException_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_NSException_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m index b730beac311..63a12832136 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Signal_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Signal_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m index e474911b12a..81301ed7a62 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashMonitor_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashMonitor_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m index cb941d53d4b..b15fb0114f3 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashObjC_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashObjC_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m index f590179ec84..9294cf81f2e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashReportFilter_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportFilter_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m index 8cb62e67fac..c5010c675d7 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashReportStore_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashReportStore_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m index a628e4596b3..fc0a0ca344c 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashSignalInfo_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSignalInfo_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m index 3bd95891e6f..bd604fe2345 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashString_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashString_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m index b864e262967..2c498a55f5e 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashSysCtl_Tests.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SentryCrashSysCtl_Tests.m // diff --git a/Tests/SentryTests/SentryCrash/TestThread.h b/Tests/SentryTests/SentryCrash/TestThread.h index 6252d668090..e3242cba13b 100644 --- a/Tests/SentryTests/SentryCrash/TestThread.h +++ b/Tests/SentryTests/SentryCrash/TestThread.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // TestThread.h // diff --git a/Tests/SentryTests/SentryCrash/TestThread.m b/Tests/SentryTests/SentryCrash/TestThread.m index 348008697fa..8151051e973 100644 --- a/Tests/SentryTests/SentryCrash/TestThread.m +++ b/Tests/SentryTests/SentryCrash/TestThread.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // TestThread.m // diff --git a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h index 64e91823427..b54bff692bd 100644 --- a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h +++ b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.h @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SenTestCase+SentryCrash.h // diff --git a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m index 50065d1e3ff..9973e1bc555 100644 --- a/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m +++ b/Tests/SentryTests/SentryCrash/XCTestCase+SentryCrash.m @@ -1,3 +1,4 @@ +// Adapted from: https://github.com/kstenerud/KSCrash // // SenTestCase+SentryCrash.m // From d1a3794200f4ec58ed09954014c2d2eb332de3c8 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 5 Apr 2023 15:59:51 +0200 Subject: [PATCH 02/42] fix: test compilation (#2857) Updating tests to work with Xcode 14.3 --- Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift | 2 +- Tests/SentryTests/Protocol/SentrySdkInfoTests.swift | 2 +- Tests/SentryTests/Protocol/TestData.swift | 6 +++--- Tests/SentryTests/SentryScopeSwiftTests.swift | 4 ++-- Tests/SentryTests/Transaction/SentryTracerTests.swift | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift b/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift index 08e824e1e79..be90f2b3a16 100644 --- a/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift +++ b/Tests/SentryTests/Protocol/SentryBreadcrumbTests.swift @@ -19,7 +19,7 @@ class SentryBreadcrumbTests: XCTestCase { breadcrumb.category = category breadcrumb.type = "user" breadcrumb.message = "Click something" - breadcrumb.data = ["some": ["data": "data", "date": date]] + breadcrumb.data = ["some": ["data": "data", "date": date] as [String: Any]] } var dateAs8601String: String { diff --git a/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift b/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift index e124c7ab2c0..6080e19fd3e 100644 --- a/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift +++ b/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift @@ -100,7 +100,7 @@ class SentrySdkInfoTests: XCTestCase { } func testInitWithDict_AllNil() { - let dict = ["sdk": [ "name": nil, "version": nil]] + let dict = ["sdk": [ "name": nil, "version": nil] as [String: Any?]] assertEmptySdkInfo(actual: SentrySdkInfo(dict: dict)) } diff --git a/Tests/SentryTests/Protocol/TestData.swift b/Tests/SentryTests/Protocol/TestData.swift index c2a6b0eba11..3209afc21f1 100644 --- a/Tests/SentryTests/Protocol/TestData.swift +++ b/Tests/SentryTests/Protocol/TestData.swift @@ -14,7 +14,7 @@ class TestData { } } static let sdk = ["name": SentryMeta.sdkName, "version": SentryMeta.versionString] - static let context = ["context": ["c": "a", "date": timestamp]] + static let context: [String: [String: Any]] = ["context": ["c": "a", "date": timestamp]] static var crumb: Breadcrumb { let crumb = Breadcrumb() @@ -22,7 +22,7 @@ class TestData { crumb.timestamp = timestamp crumb.type = "user" crumb.message = "Clicked something" - crumb.data = ["some": ["data": "data", "date": timestamp]] + crumb.data = ["some": ["data": "data", "date": timestamp] as [String: Any]] return crumb } @@ -65,7 +65,7 @@ class TestData { user.segment = "segmentA" user.name = "User" user.geo = geo - user.data = ["some": ["data": "data", "date": timestamp]] + user.data = ["some": ["data": "data", "date": timestamp] as [String: Any]] return user } diff --git a/Tests/SentryTests/SentryScopeSwiftTests.swift b/Tests/SentryTests/SentryScopeSwiftTests.swift index 89ef6fc1296..74975f08951 100644 --- a/Tests/SentryTests/SentryScopeSwiftTests.swift +++ b/Tests/SentryTests/SentryScopeSwiftTests.swift @@ -29,14 +29,14 @@ class SentryScopeSwiftTests: XCTestCase { user.email = "user@sentry.io" user.username = "user123" user.ipAddress = ipAddress - user.data = ["some": ["data": "data", "date": date]] + user.data = ["some": ["data": "data", "date": date] as [String: Any]] breadcrumb = Breadcrumb() breadcrumb.level = SentryLevel.info breadcrumb.timestamp = date breadcrumb.type = "user" breadcrumb.message = "Clicked something" - breadcrumb.data = ["some": ["data": "data", "date": date]] + breadcrumb.data = ["some": ["data": "data", "date": date] as [String: Any]] scope = Scope(maxBreadcrumbs: maxBreadcrumbs) scope.setUser(user) diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index 10094c91636..27ba0b07e99 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -918,7 +918,7 @@ class SentryTracerTests: XCTestCase { group.enter() queue.async { let child = sut.startChild(operation: self.fixture.transactionOperation) - Dynamic(child).frames = [] + Dynamic(child).frames = [] as [Frame] child.finish() group.leave() } From 2c8dc83a3aae9b1342560a97746aeffba77f3d1f Mon Sep 17 00:00:00 2001 From: Stefan Jandl Date: Wed, 5 Apr 2023 16:20:16 +0200 Subject: [PATCH 03/42] added 'strong' attribute (#2860) --- Sources/Sentry/include/SentryTraceContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Sentry/include/SentryTraceContext.h b/Sources/Sentry/include/SentryTraceContext.h index 3fd1b491928..607471c0083 100644 --- a/Sources/Sentry/include/SentryTraceContext.h +++ b/Sources/Sentry/include/SentryTraceContext.h @@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Sample rate used for this trace. */ -@property (nullable, nonatomic) NSString *sampleRate; +@property (nullable, nonatomic, strong) NSString *sampleRate; /** * Initializes a SentryTraceContext with given properties. From a5c946bb1a2453d604864443277424df99c2076d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= <31292499+krystofwoldrich@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:29:14 +0200 Subject: [PATCH 04/42] chore(hybrid): Move extra context to standalone class (#2855) Co-authored-by: Dhiogo Brustolin --- Sentry.xcodeproj/project.pbxproj | 10 +++ SentryTestUtils/TestClient.swift | 7 +- Sources/Sentry/PrivateSentrySDKOnly.m | 6 ++ Sources/Sentry/SentryClient.m | 55 ++++--------- Sources/Sentry/SentryExtraContextProvider.h | 23 ++++++ Sources/Sentry/SentryExtraContextProvider.m | 79 +++++++++++++++++++ .../HybridPublic/PrivateSentrySDKOnly.h | 5 ++ .../SentryExtraContextProviderTests.swift | 67 ++++++++++++++++ Tests/SentryTests/SentryClient+TestInit.h | 6 +- Tests/SentryTests/SentryClientTests.swift | 15 ++-- .../SentryTests/SentryTests-Bridging-Header.h | 1 + 11 files changed, 219 insertions(+), 55 deletions(-) create mode 100644 Sources/Sentry/SentryExtraContextProvider.h create mode 100644 Sources/Sentry/SentryExtraContextProvider.m create mode 100644 Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 175c4555124..f77a3013ee3 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -77,6 +77,8 @@ 15E0A8ED240F2CB000F044E3 /* SentrySerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8EC240F2CB000F044E3 /* SentrySerialization.m */; }; 15E0A8F0240F638200F044E3 /* SentrySerializationNilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8EF240F638200F044E3 /* SentrySerializationNilTests.m */; }; 15E0A8F22411A45A00F044E3 /* SentrySession.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8F12411A45A00F044E3 /* SentrySession.m */; }; + 33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */; }; + 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */; }; 627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 627E7588299F6FE40085504D /* SentryInternalDefines.h */; }; 62F226B729A37C120038080D /* SentryBooleanSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F226B629A37C120038080D /* SentryBooleanSerialization.m */; }; 630435FE1EBCA9D900C4D3FA /* SentryNSURLRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */; }; @@ -920,6 +922,9 @@ 15E0A8EC240F2CB000F044E3 /* SentrySerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentrySerialization.m; sourceTree = ""; }; 15E0A8EF240F638200F044E3 /* SentrySerializationNilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySerializationNilTests.m; sourceTree = ""; }; 15E0A8F12411A45A00F044E3 /* SentrySession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentrySession.m; sourceTree = ""; }; + 33042A0B29DAF5F400C60085 /* SentryExtraContextProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryExtraContextProvider.h; sourceTree = ""; }; + 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryExtraContextProvider.m; sourceTree = ""; }; + 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraContextProviderTests.swift; sourceTree = ""; }; 627E7588299F6FE40085504D /* SentryInternalDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalDefines.h; path = include/SentryInternalDefines.h; sourceTree = ""; }; 62F226B629A37C120038080D /* SentryBooleanSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBooleanSerialization.m; sourceTree = ""; }; 62F226B829A37C270038080D /* SentryBooleanSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryBooleanSerialization.h; sourceTree = ""; }; @@ -2104,6 +2109,8 @@ 844EDC75294144DB00C86F34 /* SentrySystemWrapper.mm */, 844EDCE32947DC3100C86F34 /* SentryNSTimerWrapper.h */, 844EDCE42947DC3100C86F34 /* SentryNSTimerWrapper.m */, + 33042A0B29DAF5F400C60085 /* SentryExtraContextProvider.h */, + 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */, ); name = Helper; sourceTree = ""; @@ -2709,6 +2716,7 @@ 84A8892028DBD8D600C51DFD /* SentryDeviceTests.mm */, D85790282976A69F00C6AC1F /* TestDebugImageProvider.swift */, 8431EE5A29ADB8EA00D8DC56 /* SentryTimeTests.m */, + 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */, ); path = Helper; sourceTree = ""; @@ -3885,6 +3893,7 @@ 630435FF1EBCA9D900C4D3FA /* SentryNSURLRequest.m in Sources */, 7B5CAF7727F5A68C00ED0DB6 /* SentryNSURLRequestBuilder.m in Sources */, 639FCFA11EBC804600778193 /* SentryException.m in Sources */, + 33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */, 7BA61CAD247BAA0B00C130A8 /* SentryDebugImageProvider.m in Sources */, 63FE70E720DA4C1000CDBAE8 /* SentryCrashMonitor.c in Sources */, 84354E1229BF944900CDBB8B /* SentryProfileTimeseries.mm in Sources */, @@ -4103,6 +4112,7 @@ 7B6D1263265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift in Sources */, 7BB42EF124F3B7B700D7B39A /* SentrySession+Equality.m in Sources */, 7BF9EF8B2722D58700B5BBEF /* SentryInitializeForGettingSubclassesNotCalled.m in Sources */, + 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */, D8137D54272B53070082656C /* TestSentrySpan.m in Sources */, 7BECF432261463E600D9826E /* SentryMechanismMetaTests.swift in Sources */, 7BE8E8462593313500C4DA1F /* SentryAttachment+Equality.m in Sources */, diff --git a/SentryTestUtils/TestClient.swift b/SentryTestUtils/TestClient.swift index fb037f8ca9d..f6d885c91e7 100644 --- a/SentryTestUtils/TestClient.swift +++ b/SentryTestUtils/TestClient.swift @@ -15,7 +15,7 @@ public class TestClient: SentryClient { // Without this override we get a fatal error: use of unimplemented initializer // see https://stackoverflow.com/questions/28187261/ios-swift-fatal-error-use-of-unimplemented-initializer-init - public override init(options: Options, transportAdapter: SentryTransportAdapter, fileManager: SentryFileManager, deleteOldEnvelopeItems: Bool, threadInspector: SentryThreadInspector, random: SentryRandomProtocol, crashWrapper: SentryCrashWrapper, deviceWrapper: SentryUIDeviceWrapper, locale: Locale, timezone: TimeZone) { + public override init(options: Options, transportAdapter: SentryTransportAdapter, fileManager: SentryFileManager, deleteOldEnvelopeItems: Bool, threadInspector: SentryThreadInspector, random: SentryRandomProtocol, locale: Locale, timezone: TimeZone, extraContextProvider: SentryExtraContextProvider) { super.init( options: options, transportAdapter: transportAdapter, @@ -23,10 +23,9 @@ public class TestClient: SentryClient { deleteOldEnvelopeItems: false, threadInspector: threadInspector, random: random, - crashWrapper: crashWrapper, - deviceWrapper: deviceWrapper, locale: locale, - timezone: timezone + timezone: timezone, + extraContextProvider: extraContextProvider ) } diff --git a/Sources/Sentry/PrivateSentrySDKOnly.m b/Sources/Sentry/PrivateSentrySDKOnly.m index c60549fe3df..1dcbac8ca64 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.m +++ b/Sources/Sentry/PrivateSentrySDKOnly.m @@ -1,6 +1,7 @@ #import "PrivateSentrySDKOnly.h" #import "SentryClient.h" #import "SentryDebugImageProvider.h" +#import "SentryExtraContextProvider.h" #import "SentryHub+Private.h" #import "SentryInstallation.h" #import "SentryMeta.h" @@ -100,6 +101,11 @@ + (NSString *)getSdkVersionString return SentryMeta.versionString; } ++ (NSDictionary *)getExtraContext +{ + return [[SentryExtraContextProvider sharedInstance] getExtraContext]; +} + #if SENTRY_HAS_UIKIT + (BOOL)framesTrackingMeasurementHybridSDKMode diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index c9c962a3d17..239124673ca 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -8,7 +8,6 @@ #import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashIntegration.h" #import "SentryCrashStackEntryMapper.h" -#import "SentryCrashWrapper.h" #import "SentryDebugImageProvider.h" #import "SentryDefaultCurrentDateProvider.h" #import "SentryDependencyContainer.h" @@ -18,6 +17,7 @@ #import "SentryEnvelopeItemType.h" #import "SentryEvent.h" #import "SentryException.h" +#import "SentryExtraContextProvider.h" #import "SentryFileManager.h" #import "SentryGlobalEventProcessor.h" #import "SentryHub+Private.h" @@ -31,7 +31,6 @@ #import "SentryMessage.h" #import "SentryMeta.h" #import "SentryNSError.h" -#import "SentryNSProcessInfoWrapper.h" #import "SentryOptions+Private.h" #import "SentrySDK+Private.h" #import "SentryScope+Private.h" @@ -43,7 +42,6 @@ #import "SentryTransport.h" #import "SentryTransportAdapter.h" #import "SentryTransportFactory.h" -#import "SentryUIDeviceWrapper.h" #import "SentryUser.h" #import "SentryUserFeedback.h" #import "SentryWatchdogTerminationTracker.h" @@ -60,11 +58,9 @@ @property (nonatomic, strong) SentryTransportAdapter *transportAdapter; @property (nonatomic, strong) SentryDebugImageProvider *debugImageProvider; @property (nonatomic, strong) id random; -@property (nonatomic, strong) SentryCrashWrapper *crashWrapper; -@property (nonatomic, strong) SentryUIDeviceWrapper *deviceWrapper; @property (nonatomic, strong) NSLocale *locale; @property (nonatomic, strong) NSTimeZone *timezone; -@property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; +@property (nonatomic, strong) SentryExtraContextProvider *extraContextProvider; @end @@ -135,7 +131,7 @@ - (instancetype)initWithOptions:(SentryOptions *)options SentryThreadInspector *threadInspector = [[SentryThreadInspector alloc] initWithStacktraceBuilder:stacktraceBuilder andMachineContextWrapper:machineContextWrapper]; - SentryUIDeviceWrapper *deviceWrapper = [[SentryUIDeviceWrapper alloc] init]; + SentryExtraContextProvider *extraContextProvider = [SentryExtraContextProvider sharedInstance]; return [self initWithOptions:options transportAdapter:transportAdapter @@ -143,10 +139,9 @@ - (instancetype)initWithOptions:(SentryOptions *)options deleteOldEnvelopeItems:deleteOldEnvelopeItems threadInspector:threadInspector random:[SentryDependencyContainer sharedInstance].random - crashWrapper:[SentryCrashWrapper sharedInstance] - deviceWrapper:deviceWrapper locale:[NSLocale autoupdatingCurrentLocale] - timezone:[NSCalendar autoupdatingCurrentCalendar].timeZone]; + timezone:[NSCalendar autoupdatingCurrentCalendar].timeZone + extraContextProvider:extraContextProvider]; } - (instancetype)initWithOptions:(SentryOptions *)options @@ -155,10 +150,9 @@ - (instancetype)initWithOptions:(SentryOptions *)options deleteOldEnvelopeItems:(BOOL)deleteOldEnvelopeItems threadInspector:(SentryThreadInspector *)threadInspector random:(id)random - crashWrapper:(SentryCrashWrapper *)crashWrapper - deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper locale:(NSLocale *)locale timezone:(NSTimeZone *)timezone + extraContextProvider:(SentryExtraContextProvider *)extraContentProvider { if (self = [super init]) { _isEnabled = YES; @@ -167,13 +161,11 @@ - (instancetype)initWithOptions:(SentryOptions *)options self.fileManager = fileManager; self.threadInspector = threadInspector; self.random = random; - self.crashWrapper = crashWrapper; self.debugImageProvider = [SentryDependencyContainer sharedInstance].debugImageProvider; self.locale = locale; self.timezone = timezone; self.attachmentProcessors = [[NSMutableArray alloc] init]; - self.deviceWrapper = deviceWrapper; - self.processInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + self.extraContextProvider = extraContentProvider; if (deleteOldEnvelopeItems) { [fileManager deleteOldEnvelopeItems]; @@ -791,36 +783,17 @@ - (void)applyCultureContextToEvent:(SentryEvent *)event - (void)applyExtraDeviceContextToEvent:(SentryEvent *)event { - [self - modifyContext:event - key:@"device" - block:^(NSMutableDictionary *device) { - device[SentryDeviceContextFreeMemoryKey] = @(self.crashWrapper.freeMemorySize); - device[@"free_storage"] = @(self.crashWrapper.freeStorageSize); - device[@"processor_count"] = @([self.processInfoWrapper processorCount]); - -#if TARGET_OS_IOS - if (self.deviceWrapper.orientation != UIDeviceOrientationUnknown) { - device[@"orientation"] - = UIDeviceOrientationIsPortrait(self.deviceWrapper.orientation) - ? @"portrait" - : @"landscape"; - } - - if (self.deviceWrapper.isBatteryMonitoringEnabled) { - device[@"charging"] - = self.deviceWrapper.batteryState == UIDeviceBatteryStateCharging - ? @(YES) - : @(NO); - device[@"battery_level"] = @((int)(self.deviceWrapper.batteryLevel * 100)); - } -#endif - }]; + NSDictionary *extraContext = [[self extraContextProvider] getExtraContext]; + [self modifyContext:event + key:@"device" + block:^(NSMutableDictionary *device) { + [device addEntriesFromDictionary:extraContext[@"device"]]; + }]; [self modifyContext:event key:@"app" block:^(NSMutableDictionary *app) { - app[SentryDeviceContextAppMemoryKey] = @(self.crashWrapper.appMemorySize); + [app addEntriesFromDictionary:extraContext[@"app"]]; }]; } diff --git a/Sources/Sentry/SentryExtraContextProvider.h b/Sources/Sentry/SentryExtraContextProvider.h new file mode 100644 index 00000000000..fbf1d3f1b3e --- /dev/null +++ b/Sources/Sentry/SentryExtraContextProvider.h @@ -0,0 +1,23 @@ +#import "SentryCrashWrapper.h" +#import "SentryNSProcessInfoWrapper.h" +#import "SentryUIDeviceWrapper.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Provider of dynamic context data that we need to read at the time of an exception. + */ +@interface SentryExtraContextProvider : NSObject + ++ (instancetype)sharedInstance; + +- (instancetype)initWithCrashWrapper:(SentryCrashWrapper *)crashWrapper + deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper + processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper; + +- (NSDictionary *)getExtraContext; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentryExtraContextProvider.m b/Sources/Sentry/SentryExtraContextProvider.m new file mode 100644 index 00000000000..ef34501854f --- /dev/null +++ b/Sources/Sentry/SentryExtraContextProvider.m @@ -0,0 +1,79 @@ +#import "SentryExtraContextProvider.h" +#import "SentryCrashIntegration.h" +#import "SentryCrashWrapper.h" +#import "SentryNSProcessInfoWrapper.h" +#import "SentryUIDeviceWrapper.h" + +@interface +SentryExtraContextProvider () + +@property (nonatomic, strong) SentryCrashWrapper *crashWrapper; +@property (nonatomic, strong) SentryUIDeviceWrapper *deviceWrapper; +@property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; + +@end + +@implementation SentryExtraContextProvider + ++ (instancetype)sharedInstance +{ + static SentryExtraContextProvider *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); + return instance; +} + +- (instancetype)init +{ + return [self initWithCrashWrapper:[SentryCrashWrapper sharedInstance] + deviceWrapper:[[SentryUIDeviceWrapper alloc] init] + processInfoWrapper:[[SentryNSProcessInfoWrapper alloc] init]]; +} + +- (instancetype)initWithCrashWrapper:(id)crashWrapper + deviceWrapper:(id)deviceWrapper + processInfoWrapper:(id)processInfoWrapper +{ + if (self = [super init]) { + self.crashWrapper = crashWrapper; + self.deviceWrapper = deviceWrapper; + self.processInfoWrapper = processInfoWrapper; + } + return self; +} + +- (NSDictionary *)getExtraContext +{ + return @{ @"device" : [self getExtraDeviceContext], @"app" : [self getExtraAppContext] }; +} + +- (NSDictionary *)getExtraDeviceContext +{ + NSMutableDictionary *extraDeviceContext = [[NSMutableDictionary alloc] init]; + + extraDeviceContext[SentryDeviceContextFreeMemoryKey] = @(self.crashWrapper.freeMemorySize); + extraDeviceContext[@"free_storage"] = @(self.crashWrapper.freeStorageSize); + extraDeviceContext[@"processor_count"] = @([self.processInfoWrapper processorCount]); + +#if TARGET_OS_IOS + if (self.deviceWrapper.orientation != UIDeviceOrientationUnknown) { + extraDeviceContext[@"orientation"] + = UIDeviceOrientationIsPortrait(self.deviceWrapper.orientation) ? @"portrait" + : @"landscape"; + } + + if (self.deviceWrapper.isBatteryMonitoringEnabled) { + extraDeviceContext[@"charging"] + = self.deviceWrapper.batteryState == UIDeviceBatteryStateCharging ? @(YES) : @(NO); + extraDeviceContext[@"battery_level"] = @((int)(self.deviceWrapper.batteryLevel * 100)); + } +#endif + return extraDeviceContext; +} + +- (NSDictionary *)getExtraAppContext +{ + return @{ SentryDeviceContextAppMemoryKey : @(self.crashWrapper.appMemorySize) }; +} + +@end diff --git a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h index d6a4001914e..3d5c93c0668 100644 --- a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h +++ b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h @@ -63,6 +63,11 @@ typedef void (^SentryOnAppStartMeasurementAvailable)( */ + (NSString *)getSdkVersionString; +/** + * Retrieves extra context + */ ++ (NSDictionary *)getExtraContext; + @property (class, nullable, nonatomic, copy) SentryOnAppStartMeasurementAvailable onAppStartMeasurementAvailable; diff --git a/Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift b/Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift new file mode 100644 index 00000000000..8c8267bd3a6 --- /dev/null +++ b/Tests/SentryTests/Helper/SentryExtraContextProviderTests.swift @@ -0,0 +1,67 @@ +import SentryTestUtils +import XCTest + +final class SentryExtraContextProviderTests: XCTestCase { + + private class Fixture { + let crashWrapper = TestSentryCrashWrapper.sharedInstance() + let deviceWrapper = TestSentryUIDeviceWrapper() + let processWrapper = TestSentryNSProcessInfoWrapper() + + func getSut() -> SentryExtraContextProvider { + return SentryExtraContextProvider( + crashWrapper: crashWrapper, + deviceWrapper: deviceWrapper, + processInfoWrapper: processWrapper) + } + } + + private var fixture: Fixture! + + override func setUp() { + super.setUp() + fixture = Fixture() + } + + func testExtraCrashInfo() throws { + let sut = fixture.getSut() + fixture.crashWrapper.internalFreeMemorySize = 123_456 + fixture.crashWrapper.internalAppMemorySize = 234_567 + fixture.crashWrapper.internalFreeStorageSize = 345_678 + + let actualContext = sut.getExtraContext() + let device = actualContext["device"] as? [String: Any] + let app = actualContext["app"] as? [String: Any] + + XCTAssertEqual(device?["free_memory"] as? UInt64, fixture.crashWrapper.internalFreeMemorySize) + XCTAssertEqual(app?["app_memory"] as? UInt64, fixture.crashWrapper.internalAppMemorySize) + XCTAssertEqual(device?["free_storage"] as? UInt64, fixture.crashWrapper.internalFreeStorageSize) + } + + func testExtraDeviceInfo() { +#if os(iOS) + let sut = fixture.getSut() + fixture.deviceWrapper.internalOrientation = .landscapeLeft + fixture.deviceWrapper.internalBatteryState = .full + fixture.deviceWrapper.internalBatteryLevel = 0.44 + + let actualContext = sut.getExtraContext() + let device = actualContext["device"] as? [String: Any] + + XCTAssertEqual(device?["orientation"] as? String, "landscape") + XCTAssertEqual(device?["charging"] as? Bool, false) + XCTAssertEqual(device?["battery_level"] as? UInt, 44) +#endif + } + + func testExtraProcessInfo() { + let sut = fixture.getSut() + fixture.processWrapper.overrides.processorCount = 12 + + let actualContext = sut.getExtraContext() + let device = actualContext["device"] as? [String: Any] + + XCTAssertEqual(device?["processor_count"] as? UInt, fixture.processWrapper.overrides.processorCount) + } + +} diff --git a/Tests/SentryTests/SentryClient+TestInit.h b/Tests/SentryTests/SentryClient+TestInit.h index 61ad03da9e7..27e1213ab18 100644 --- a/Tests/SentryTests/SentryClient+TestInit.h +++ b/Tests/SentryTests/SentryClient+TestInit.h @@ -1,3 +1,4 @@ +#import "SentryExtraContextProvider.h" #import "SentryRandom.h" #import "SentryTransport.h" #import @@ -28,10 +29,9 @@ SentryClient () deleteOldEnvelopeItems:(BOOL)deleteOldEnvelopeItems threadInspector:(SentryThreadInspector *)threadInspector random:(id)random - crashWrapper:(SentryCrashWrapper *)crashWrapper - deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper locale:(NSLocale *)locale - timezone:(NSTimeZone *)timezone; + timezone:(NSTimeZone *)timezone + extraContextProvider:(SentryExtraContextProvider *)extraContextProvider; @end diff --git a/Tests/SentryTests/SentryClientTests.swift b/Tests/SentryTests/SentryClientTests.swift index 4d91e2d19c1..26098db0d12 100644 --- a/Tests/SentryTests/SentryClientTests.swift +++ b/Tests/SentryTests/SentryClientTests.swift @@ -30,6 +30,8 @@ class SentryClientTest: XCTestCase { let transaction: Transaction let crashWrapper = TestSentryCrashWrapper.sharedInstance() let deviceWrapper = TestSentryUIDeviceWrapper() + let processWrapper = TestSentryNSProcessInfoWrapper() + let extraContentProvider: SentryExtraContextProvider let locale = Locale(identifier: "en_US") let timezone = TimeZone(identifier: "Europe/Vienna")! let queue = DispatchQueue(label: "SentryHubTests", qos: .utility, attributes: [.concurrent]) @@ -60,6 +62,8 @@ class SentryClientTest: XCTestCase { crashWrapper.internalFreeMemorySize = 123_456 crashWrapper.internalAppMemorySize = 234_567 crashWrapper.internalFreeStorageSize = 345_678 + + extraContentProvider = SentryExtraContextProvider(crashWrapper: crashWrapper, deviceWrapper: deviceWrapper, processInfoWrapper: processWrapper) } func getSut(configureOptions: (Options) -> Void = { _ in }) -> SentryClient { @@ -77,10 +81,9 @@ class SentryClientTest: XCTestCase { deleteOldEnvelopeItems: false, threadInspector: threadInspector, random: random, - crashWrapper: crashWrapper, - deviceWrapper: deviceWrapper, locale: locale, - timezone: timezone + timezone: timezone, + extraContextProvider: extraContentProvider ) } catch { XCTFail("Options could not be created") @@ -610,9 +613,7 @@ class SentryClientTest: XCTestCase { func testCaptureEvent_AddCurrentMemoryStorageAndCPUCoreCount() { let sut = fixture.getSut() - let testProcessWrapper = TestSentryNSProcessInfoWrapper() - testProcessWrapper.overrides.processorCount = 12 - Dynamic(sut).processInfoWrapper = testProcessWrapper + fixture.processWrapper.overrides.processorCount = 12 sut.capture(event: TestData.event) @@ -627,7 +628,7 @@ class SentryClientTest: XCTestCase { XCTAssertEqual(eventFreeStorage, 345_678) let cpuCoreCount = actual.context?["device"]?["processor_count"] as? UInt - XCTAssertEqual(testProcessWrapper.processorCount, cpuCoreCount) + XCTAssertEqual(fixture.processWrapper.processorCount, cpuCoreCount) } } diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 9b42fc83605..45bea05f484 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -185,6 +185,7 @@ #import "URLSessionTaskMock.h" @import SentryPrivate; #import "SentryEnvelopeAttachmentHeader.h" +#import "SentryExtraContextProvider.h" #import "SentryMeasurementValue.h" #import "SentryNSProcessInfoWrapper.h" #import "SentryPerformanceTracker+Testing.h" From bcd991b5a570ab45bd2dfd16d85ff5d679935fe9 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 5 Apr 2023 17:02:06 +0200 Subject: [PATCH 05/42] fix: Serialization with invalid objects (#2858) Serialization may fail and crash if there is an invalid objects in the data. Co-authored-by: Philipp Hofmann --- CHANGELOG.md | 8 ++- Sources/Sentry/SentryEnvelope.m | 7 ++- Sources/Sentry/SentryFileManager.m | 12 ++--- Sources/Sentry/SentryNSURLRequest.m | 9 +--- Sources/Sentry/SentryProfiler.mm | 5 +- Sources/Sentry/SentrySerialization.m | 50 ++++--------------- Sources/Sentry/include/SentrySerialization.h | 6 +-- .../Helper/SentrySerializationTests.swift | 25 +++------- .../Protocol/SentryEnvelopeTests.swift | 4 +- 9 files changed, 41 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 399776acefa..18037da91e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Crash when serializing invalid objects (#2858) + ## 8.4.0 ### Features @@ -11,7 +17,7 @@ ### Fixes - Correctly track and send GPU frame render data in profiles (#2823) -- Xcode 14.3 compiling issue regarding functions declaration with no prototype (#2852) +- Xcode 14.3 compiling issue regarding functions declaration with no prototype (#2852) ## 8.3.3 diff --git a/Sources/Sentry/SentryEnvelope.m b/Sources/Sentry/SentryEnvelope.m index ba290b064fe..5018dc9bb47 100644 --- a/Sources/Sentry/SentryEnvelope.m +++ b/Sources/Sentry/SentryEnvelope.m @@ -63,10 +63,9 @@ - (instancetype)initWithHeader:(SentryEnvelopeItemHeader *)header data:(NSData * - (instancetype)initWithEvent:(SentryEvent *)event { - NSError *error; - NSData *json = [SentrySerialization dataWithJSONObject:[event serialize] error:&error]; + NSData *json = [SentrySerialization dataWithJSONObject:[event serialize]]; - if (nil != error) { + if (nil == json) { // We don't know what caused the serialization to fail. SentryEvent *errorEvent = [[SentryEvent alloc] initWithLevel:kSentryLevelWarning]; @@ -83,7 +82,7 @@ - (instancetype)initWithEvent:(SentryEvent *)event // We accept the risk that this simple serialization fails. Therefore we ignore the // error on purpose. - json = [SentrySerialization dataWithJSONObject:[errorEvent serialize] error:nil]; + json = [SentrySerialization dataWithJSONObject:[errorEvent serialize]]; } // event.type can be nil and the server infers error if there's a stack trace, otherwise diff --git a/Sources/Sentry/SentryFileManager.m b/Sources/Sentry/SentryFileManager.m index a49104b897a..128d8b980d7 100644 --- a/Sources/Sentry/SentryFileManager.m +++ b/Sources/Sentry/SentryFileManager.m @@ -318,7 +318,7 @@ - (void)storeCrashedSession:(SentrySession *)session - (void)storeSession:(SentrySession *)session sessionFilePath:(NSString *)sessionFilePath { - NSData *sessionData = [SentrySerialization dataWithSession:session error:nil]; + NSData *sessionData = [SentrySerialization dataWithSession:session]; SENTRY_LOG_DEBUG(@"Writing session: %@", sessionFilePath); @synchronized(self.currentSessionFilePath) { if (![self writeData:sessionData toPath:sessionFilePath]) { @@ -444,7 +444,7 @@ - (BOOL)writeData:(NSData *)data toPath:(NSString *)path - (NSString *)storeDictionary:(NSDictionary *)dictionary toPath:(NSString *)path { - NSData *saveData = [SentrySerialization dataWithJSONObject:dictionary error:nil]; + NSData *saveData = [SentrySerialization dataWithJSONObject:dictionary]; return nil != saveData ? [self storeData:saveData toUniqueJSONPath:path] : path; // TODO: Should we return null instead? Whoever is using this // return value is being tricked. @@ -452,12 +452,10 @@ - (NSString *)storeDictionary:(NSDictionary *)dictionary toPath:(NSString *)path - (void)storeAppState:(SentryAppState *)appState { - NSError *error = nil; - NSData *data = [SentrySerialization dataWithJSONObject:[appState serialize] error:&error]; + NSData *data = [SentrySerialization dataWithJSONObject:[appState serialize]]; - if (error != nil) { - SENTRY_LOG_ERROR( - @"Failed to store app state, because of an error in serialization: %@", error); + if (data == nil) { + SENTRY_LOG_ERROR(@"Failed to store app state, because of an error in serialization"); return; } diff --git a/Sources/Sentry/SentryNSURLRequest.m b/Sources/Sentry/SentryNSURLRequest.m index 7cb40d722a1..a02c06d3ff8 100644 --- a/Sources/Sentry/SentryNSURLRequest.m +++ b/Sources/Sentry/SentryNSURLRequest.m @@ -29,14 +29,9 @@ - (_Nullable instancetype)initStoreRequestWithDsn:(SentryDsn *)dsn didFailWithError:(NSError *_Nullable *_Nullable)error { NSDictionary *serialized = [event serialize]; - NSData *jsonData = [SentrySerialization dataWithJSONObject:serialized error:error]; + NSData *jsonData = [SentrySerialization dataWithJSONObject:serialized]; if (nil == jsonData) { - if (error) { - // TODO: We're possibly overriding an error set by the actual - // code that failed ^ - *error = NSErrorFromSentryError( - kSentryErrorJsonConversionError, @"Event cannot be converted to JSON"); - } + SENTRY_LOG_ERROR(@"Event cannot be converted to JSON"); return nil; } diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index aefc844d118..32bcf20df94 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -742,10 +742,9 @@ + (NSDictionary *)serializeInfoForTransaction:(SentryTransaction *)transaction + (SentryEnvelopeItem *)envelopeItemForProfileData:(NSMutableDictionary *)profile profileID:(SentryId *)profileID { - NSError *error = nil; - const auto JSONData = [SentrySerialization dataWithJSONObject:profile error:&error]; + const auto JSONData = [SentrySerialization dataWithJSONObject:profile]; if (JSONData == nil) { - SENTRY_LOG_DEBUG(@"Failed to encode profile to JSON: %@", error); + SENTRY_LOG_DEBUG(@"Failed to encode profile to JSON."); return nil; } diff --git a/Sources/Sentry/SentrySerialization.m b/Sources/Sentry/SentrySerialization.m index 2a423b53c15..850c63a69a7 100644 --- a/Sources/Sentry/SentrySerialization.m +++ b/Sources/Sentry/SentrySerialization.m @@ -16,35 +16,17 @@ @implementation SentrySerialization + (NSData *_Nullable)dataWithJSONObject:(NSDictionary *)dictionary - error:(NSError *_Nullable *_Nullable)error { -// We'll do this whether we're handling an exception or library error -#define SENTRY_HANDLE_ERROR(__sentry_error) \ - SENTRY_LOG_ERROR(@"Invalid JSON: %@", __sentry_error); \ - *error = __sentry_error; \ - return nil; - - NSData *data = nil; - -#if defined(DEBUG) || defined(TEST) || defined(TESTCI) - @try { -#else - if ([NSJSONSerialization isValidJSONObject:dictionary]) { -#endif - data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:error]; -#if defined(DEBUG) || defined(TEST) || defined(TESTCI) - } @catch (NSException *exception) { - if (error) { - SENTRY_HANDLE_ERROR(NSErrorFromSentryErrorWithException( - kSentryErrorJsonConversionError, @"Event cannot be converted to JSON", exception)); - } + if (![NSJSONSerialization isValidJSONObject:dictionary]) { + SENTRY_LOG_ERROR(@"Dictionary is not a valid JSON object."); + return nil; } -#else - } else if (error) { - SENTRY_HANDLE_ERROR(NSErrorFromSentryErrorWithUnderlyingError( - kSentryErrorJsonConversionError, @"Event cannot be converted to JSON", *error)); + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error]; + if (error) { + SENTRY_LOG_ERROR(@"Internal error while serializing JSON: %@", error); } -#endif return data; } @@ -69,13 +51,9 @@ + (NSData *_Nullable)dataWithEnvelope:(SentryEnvelope *)envelope [serializedData setValue:[traceContext serialize] forKey:@"trace"]; } - NSData *header = [SentrySerialization dataWithJSONObject:serializedData error:error]; + NSData *header = [SentrySerialization dataWithJSONObject:serializedData]; if (nil == header) { SENTRY_LOG_ERROR(@"Envelope header cannot be converted to JSON."); - if (error) { - *error = NSErrorFromSentryError( - kSentryErrorJsonConversionError, @"Envelope header cannot be converted to JSON"); - } return nil; } [envelopeData appendData:header]; @@ -84,14 +62,9 @@ + (NSData *_Nullable)dataWithEnvelope:(SentryEnvelope *)envelope [envelopeData appendData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]]; NSDictionary *serializedItemHeaderData = [envelope.items[i].header serialize]; - NSData *itemHeader = [SentrySerialization dataWithJSONObject:serializedItemHeaderData - error:error]; + NSData *itemHeader = [SentrySerialization dataWithJSONObject:serializedItemHeaderData]; if (nil == itemHeader) { SENTRY_LOG_ERROR(@"Envelope item header cannot be converted to JSON."); - if (error) { - *error = NSErrorFromSentryError(kSentryErrorJsonConversionError, - @"Envelope item header cannot be converted to JSON"); - } return nil; } [envelopeData appendData:itemHeader]; @@ -305,9 +278,8 @@ + (SentryEnvelope *_Nullable)envelopeWithData:(NSData *)data } + (NSData *_Nullable)dataWithSession:(SentrySession *)session - error:(NSError *_Nullable *_Nullable)error { - return [self dataWithJSONObject:[session serialize] error:error]; + return [self dataWithJSONObject:[session serialize]]; } + (SentrySession *_Nullable)sessionWithData:(NSData *)sessionData diff --git a/Sources/Sentry/include/SentrySerialization.h b/Sources/Sentry/include/SentrySerialization.h index 4946fadddb5..562e7904eec 100644 --- a/Sources/Sentry/include/SentrySerialization.h +++ b/Sources/Sentry/include/SentrySerialization.h @@ -8,11 +8,9 @@ static int const SENTRY_BAGGAGE_MAX_SIZE = 8192; @interface SentrySerialization : NSObject -+ (NSData *_Nullable)dataWithJSONObject:(NSDictionary *)dictionary - error:(NSError *_Nullable *_Nullable)error; ++ (NSData *_Nullable)dataWithJSONObject:(NSDictionary *)dictionary; -+ (NSData *_Nullable)dataWithSession:(SentrySession *)session - error:(NSError *_Nullable *_Nullable)error; ++ (NSData *_Nullable)dataWithSession:(SentrySession *)session; + (NSDictionary *)decodeBaggage:(NSString *)baggage; + (NSString *)baggageEncodedDictionary:(NSDictionary *)dictionary; diff --git a/Tests/SentryTests/Helper/SentrySerializationTests.swift b/Tests/SentryTests/Helper/SentrySerializationTests.swift index edba69ed866..85b02eab7db 100644 --- a/Tests/SentryTests/Helper/SentrySerializationTests.swift +++ b/Tests/SentryTests/Helper/SentrySerializationTests.swift @@ -12,18 +12,7 @@ class SentrySerializationTests: XCTestCase { "valid object": "hi, i'm a valid object", "invalid object": NSDate() ] - - var data: Data? - let exp = expectation(description: "Should throw error") - do { - data = try SentrySerialization.data(withJSONObject: json) - } catch { - exp.fulfill() - //Depending of the iOS version, the underlying type of NSDate may change. - //Knowing that we have an error is enough. - XCTAssertTrue(error.localizedDescription.starts(with: "Event cannot be converted to JSON (Invalid type in JSON write")) - } - waitForExpectations(timeout: 1) + let data = SentrySerialization.data(withJSONObject: json) XCTAssertNil(data) } @@ -198,9 +187,9 @@ class SentrySerializationTests: XCTestCase { let dict = SentrySession(releaseName: "1.0.0").serialize() let session = SentrySession(jsonObject: dict)! - let data = try SentrySerialization.data(with: session) + let data = SentrySerialization.data(with: session) - XCTAssertNotNil(SentrySerialization.session(with: data)) + XCTAssertNotNil(SentrySerialization.session(with: data!)) } func testSerializeSessionWithNoReleaseName() throws { @@ -208,7 +197,7 @@ class SentrySerializationTests: XCTestCase { dict["attrs"] = nil // Remove release name let session = SentrySession(jsonObject: dict)! - let data = try SentrySerialization.data(with: session) + let data = SentrySerialization.data(with: session)! XCTAssertNil(SentrySerialization.session(with: data)) } @@ -217,7 +206,7 @@ class SentrySerializationTests: XCTestCase { let dict = SentrySession(releaseName: "").serialize() let session = SentrySession(jsonObject: dict)! - let data = try SentrySerialization.data(with: session) + let data = SentrySerialization.data(with: session)! XCTAssertNil(SentrySerialization.session(with: data)) } @@ -225,7 +214,7 @@ class SentrySerializationTests: XCTestCase { func testSerializeSessionWithGarbageInDict() throws { var dict = SentrySession(releaseName: "").serialize() dict["started"] = "20" - let data = try SentrySerialization.data(withJSONObject: dict) + let data = SentrySerialization.data(withJSONObject: dict)! XCTAssertNil(SentrySerialization.session(with: data)) } @@ -252,7 +241,7 @@ class SentrySerializationTests: XCTestCase { func testAppStateWithValidData_ReturnsValidAppState() throws { let appState = TestData.appState - let appStateData = try SentrySerialization.data(withJSONObject: appState.serialize()) + let appStateData = SentrySerialization.data(withJSONObject: appState.serialize())! let actual = SentrySerialization.appState(with: appStateData) diff --git a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift index de1809034c4..00c1889f110 100644 --- a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift +++ b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift @@ -177,7 +177,7 @@ class SentryEnvelopeTests: XCTestCase { let event = fixture.event let envelope = SentryEnvelope(event: event) - let expectedData = try SentrySerialization.data(withJSONObject: event.serialize()) + let expectedData = SentrySerialization.data(withJSONObject: event.serialize())! XCTAssertEqual(event.eventId, envelope.header.eventId) XCTAssertEqual(1, envelope.items.count) @@ -222,7 +222,7 @@ class SentryEnvelopeTests: XCTestCase { XCTAssertEqual("user_report", item?.header.type) XCTAssertNotNil(item?.data) - let expectedData = try SentrySerialization.data(withJSONObject: userFeedback.serialize()) + let expectedData = SentrySerialization.data(withJSONObject: userFeedback.serialize())! let actual = String(data: item?.data ?? Data(), encoding: .utf8)?.sorted() let expected = String(data: expectedData, encoding: .utf8)?.sorted() From 33b2f5140a89fa9ba5de0dd0d3a23a1c5a2645ce Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 5 Apr 2023 17:02:36 +0200 Subject: [PATCH 06/42] ref: Use SentryTracerConfiguration to initialize SentryTracer (#2821) Instead of having multiple functions to initialize SentryTracer with different configurations we should we one complex object that contains all the configurations. --- Sentry.xcodeproj/project.pbxproj | 8 + Sources/Sentry/SentryHub.m | 43 +---- Sources/Sentry/SentryPerformanceTracker.m | 14 +- Sources/Sentry/SentryTracer.m | 170 ++++++------------ Sources/Sentry/SentryTracerConfiguration.m | 28 +++ Sources/Sentry/SentryUIEventTracker.m | 17 +- Sources/Sentry/include/SentryHub+Private.h | 12 +- Sources/Sentry/include/SentryTracer.h | 49 +---- .../include/SentryTracerConfiguration.h | 50 ++++++ .../Network/SentryNetworkTrackerTests.swift | 5 +- .../SentryPerformanceTrackerTests.swift | 2 +- .../SentryTests/SentryTests-Bridging-Header.h | 1 + .../Transaction/SentryTracerObjCTests.m | 41 +++-- .../Transaction/SentryTracerTests.swift | 15 +- 14 files changed, 219 insertions(+), 236 deletions(-) create mode 100644 Sources/Sentry/SentryTracerConfiguration.m create mode 100644 Sources/Sentry/include/SentryTracerConfiguration.h diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index f77a3013ee3..10409b81ad1 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -765,6 +765,8 @@ D8ACE3CD2762187D00F5A213 /* SentryNSDataSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CA2762187D00F5A213 /* SentryNSDataSwizzling.h */; }; D8ACE3CE2762187D00F5A213 /* SentryNSDataTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CB2762187D00F5A213 /* SentryNSDataTracker.h */; }; D8ACE3CF2762187D00F5A213 /* SentryFileIOTrackingIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8ACE3CC2762187D00F5A213 /* SentryFileIOTrackingIntegration.h */; }; + D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */; }; + D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */; }; D8B76B062808066D000A58C4 /* SentryScreenshotIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B76B042808060E000A58C4 /* SentryScreenshotIntegrationTests.swift */; }; D8B76B0828081461000A58C4 /* TestSentryScreenShot.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B76B0728081461000A58C4 /* TestSentryScreenShot.swift */; }; D8BBD32728FD9FC00011F850 /* SentrySwift.h in Headers */ = {isa = PBXBuildFile; fileRef = D8BBD32628FD9FBF0011F850 /* SentrySwift.h */; }; @@ -1678,6 +1680,8 @@ D8ACE3CA2762187D00F5A213 /* SentryNSDataSwizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSDataSwizzling.h; path = include/SentryNSDataSwizzling.h; sourceTree = ""; }; D8ACE3CB2762187D00F5A213 /* SentryNSDataTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSDataTracker.h; path = include/SentryNSDataTracker.h; sourceTree = ""; }; D8ACE3CC2762187D00F5A213 /* SentryFileIOTrackingIntegration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryFileIOTrackingIntegration.h; path = include/SentryFileIOTrackingIntegration.h; sourceTree = ""; }; + D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryTracerConfiguration.h; path = include/SentryTracerConfiguration.h; sourceTree = ""; }; + D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTracerConfiguration.m; sourceTree = ""; }; D8B76B042808060E000A58C4 /* SentryScreenshotIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryScreenshotIntegrationTests.swift; sourceTree = ""; }; D8B76B0728081461000A58C4 /* TestSentryScreenShot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSentryScreenShot.swift; sourceTree = ""; }; D8BBD32628FD9FBF0011F850 /* SentrySwift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentrySwift.h; path = include/SentrySwift.h; sourceTree = ""; }; @@ -3140,6 +3144,8 @@ 8E133FA025E72DEF00ABD0BF /* SentrySamplingContext.m */, 8E4E7C7B25DAB287006AB9E2 /* SentryTracer.h */, 8E4E7C8125DAB2A5006AB9E2 /* SentryTracer.m */, + D8B088B429C9E3FF00213258 /* SentryTracerConfiguration.h */, + D8B088B529C9E3FF00213258 /* SentryTracerConfiguration.m */, 84AF45A429A7FFA500FBB177 /* SentryTracerConcurrency.h */, 84AF45A529A7FFA500FBB177 /* SentryTracerConcurrency.mm */, 8E8C57A525EEFC42001CEEFA /* SentryTracesSampler.h */, @@ -3490,6 +3496,7 @@ 63BE85701ECEC6DE00DC44F5 /* NSDate+SentryExtras.h in Headers */, 63FE709520DA4C1000CDBAE8 /* SentryCrashReportFilterBasic.h in Headers */, 844EDCE52947DC3100C86F34 /* SentryNSTimerWrapper.h in Headers */, + D8B088B629C9E3FF00213258 /* SentryTracerConfiguration.h in Headers */, 63FE70A120DA4C1000CDBAE8 /* SentryCrashCString.h in Headers */, 7B63459D280EBA6300CFA05A /* SentryUIEventTracker.h in Headers */, 7B7D873424864C6600D2ECFF /* SentryCrashDefaultMachineContextWrapper.h in Headers */, @@ -3843,6 +3850,7 @@ 7B3B473825D6CC7E00D01640 /* SentryNSError.m in Sources */, D8ACE3C82762187200F5A213 /* SentryNSDataTracker.m in Sources */, 7BE3C77D2446112C00A38442 /* SentryRateLimitParser.m in Sources */, + D8B088B729C9E3FF00213258 /* SentryTracerConfiguration.m in Sources */, 8ECC674A25C23A20000E2BF6 /* SentryTransactionContext.mm in Sources */, 03BCC38C27E1C01A003232C7 /* SentryTime.mm in Sources */, A8F17B342902870300990B25 /* SentryHttpStatusCodeRange.m in Sources */, diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index 92a1f389e99..de8f8de3b6a 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -369,9 +369,8 @@ - (SentryId *)captureEvent:(SentryEvent *)event { return [self startTransactionWithContext:transactionContext bindToScope:bindToScope - waitForChildren:NO customSamplingContext:customSamplingContext - timerWrapper:nil]; + configuration:[SentryTracerConfiguration defaultConfiguration]]; } - (SentryTransactionContext *)transactionContext:(SentryTransactionContext *)context @@ -388,41 +387,10 @@ - (SentryTransactionContext *)transactionContext:(SentryTransactionContext *)con parentSampled:context.parentSampled]; } -- (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext - bindToScope:(BOOL)bindToScope - waitForChildren:(BOOL)waitForChildren - customSamplingContext:(NSDictionary *)customSamplingContext - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - SentrySamplingContext *samplingContext = - [[SentrySamplingContext alloc] initWithTransactionContext:transactionContext - customSamplingContext:customSamplingContext]; - - SentryTracesSamplerDecision *samplerDecision = [_tracesSampler sample:samplingContext]; - transactionContext = [self transactionContext:transactionContext - withSampled:samplerDecision.decision]; - transactionContext.sampleRate = samplerDecision.sampleRate; - - SentryProfilesSamplerDecision *profilesSamplerDecision = - [_profilesSampler sample:samplingContext tracesSamplerDecision:samplerDecision]; - - id tracer = [[SentryTracer alloc] initWithTransactionContext:transactionContext - hub:self - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:waitForChildren - timerWrapper:timerWrapper]; - - if (bindToScope) - self.scope.span = tracer; - - return tracer; -} - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper + configuration:(SentryTracerConfiguration *)configuration { SentrySamplingContext *samplingContext = [[SentrySamplingContext alloc] initWithTransactionContext:transactionContext @@ -436,11 +404,12 @@ - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transa SentryProfilesSamplerDecision *profilesSamplerDecision = [_profilesSampler sample:samplingContext tracesSamplerDecision:samplerDecision]; + configuration.profilesSamplerDecision = profilesSamplerDecision; + SentryTracer *tracer = [[SentryTracer alloc] initWithTransactionContext:transactionContext hub:self - profilesSamplerDecision:profilesSamplerDecision - idleTimeout:idleTimeout - dispatchQueueWrapper:dispatchQueueWrapper]; + configuration:configuration]; + if (bindToScope) self.scope.span = tracer; diff --git a/Sources/Sentry/SentryPerformanceTracker.m b/Sources/Sentry/SentryPerformanceTracker.m index 9751eb47aa8..671e91a3486 100644 --- a/Sources/Sentry/SentryPerformanceTracker.m +++ b/Sources/Sentry/SentryPerformanceTracker.m @@ -79,11 +79,15 @@ - (SentrySpanId *)startSpanWithName:(NSString *)name } SENTRY_LOG_DEBUG(@"Creating new transaction bound to scope: %d", bindToScope); - newSpan = [SentrySDK.currentHub startTransactionWithContext:context - bindToScope:bindToScope - waitForChildren:YES - customSamplingContext:@{} - timerWrapper:nil]; + + newSpan = [SentrySDK.currentHub + startTransactionWithContext:context + bindToScope:bindToScope + customSamplingContext:@{} + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.waitForChildren = YES; + }]]; [(SentryTracer *)newSpan setDelegate:self]; }]; diff --git a/Sources/Sentry/SentryTracer.m b/Sources/Sentry/SentryTracer.m index ff73c082018..31da399df8f 100644 --- a/Sources/Sentry/SentryTracer.m +++ b/Sources/Sentry/SentryTracer.m @@ -54,10 +54,9 @@ * @c -[finish] doesn't necessarily lead to finishing the tracer, because it could still wait for * child spans to finish if @c waitForChildren is @c YES . */ @property (nonatomic) BOOL wasFinishCalled; -@property (nonatomic) NSTimeInterval idleTimeout; -@property (nonatomic, nullable, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper; -@property (nonatomic, nullable, strong) SentryNSTimerWrapper *timerWrapper; @property (nonatomic, nullable, strong) NSTimer *deadlineTimer; +@property (nonnull, strong) SentryTracerConfiguration *configuration; + #if SENTRY_TARGET_PROFILING_SUPPORTED @property (nonatomic) BOOL isProfiling; @property (nonatomic) uint64_t startSystemTime; @@ -67,7 +66,6 @@ @implementation SentryTracer { /** Wether the tracer should wait for child spans to finish before finishing itself. */ - BOOL _waitForChildren; SentryTraceContext *_traceContext; SentryAppStartMeasurement *appStartMeasurement; NSMutableDictionary *_measurements; @@ -77,7 +75,6 @@ @implementation SentryTracer { NSDate *_originalStartTimestamp; #if SENTRY_HAS_UIKIT - NSUInteger initTotalFrames; NSUInteger initSlowFrames; NSUInteger initFrozenFrames; @@ -100,113 +97,60 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti { return [self initWithTransactionContext:transactionContext hub:hub - profilesSamplerDecision:nil - waitForChildren:NO - timerWrapper:nil]; + configuration:SentryTracerConfiguration.defaultConfiguration]; } - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext hub:(nullable SentryHub *)hub - waitForChildren:(BOOL)waitForChildren + configuration:(SentryTracerConfiguration *)configuration; { - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:nil - waitForChildren:waitForChildren - idleTimeout:0.0 - dispatchQueueWrapper:nil - timerWrapper:nil]; -} + if (!(self = [super initWithContext:transactionContext])) { + return nil; + } -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:waitForChildren - idleTimeout:0.0 - dispatchQueueWrapper:nil - timerWrapper:timerWrapper]; -} + _configuration = configuration; -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper -{ - return [self initWithTransactionContext:transactionContext - hub:hub - profilesSamplerDecision:profilesSamplerDecision - waitForChildren:YES - idleTimeout:idleTimeout - dispatchQueueWrapper:dispatchQueueWrapper - timerWrapper:nil]; -} - -- (instancetype) - initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision:(nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(nullable SentryDispatchQueueWrapper *)dispatchQueueWrapper - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper -{ - if (self = [super initWithContext:transactionContext]) { - self.transactionContext = transactionContext; - _children = [[NSMutableArray alloc] init]; - self.hub = hub; - self.wasFinishCalled = NO; - _waitForChildren = waitForChildren; - _measurements = [[NSMutableDictionary alloc] init]; - self.finishStatus = kSentrySpanStatusUndefined; - self.idleTimeout = idleTimeout; - self.dispatchQueueWrapper = dispatchQueueWrapper; - - if (timerWrapper == nil) { - self.timerWrapper = [[SentryNSTimerWrapper alloc] init]; - } else { - self.timerWrapper = timerWrapper; - } + self.transactionContext = transactionContext; + _children = [[NSMutableArray alloc] init]; + self.hub = hub; + self.wasFinishCalled = NO; + _measurements = [[NSMutableDictionary alloc] init]; + self.finishStatus = kSentrySpanStatusUndefined; - appStartMeasurement = [self getAppStartMeasurement]; + if (_configuration.timerWrapper == nil) { + _configuration.timerWrapper = [[SentryNSTimerWrapper alloc] init]; + } - if ([self hasIdleTimeout]) { - [self dispatchIdleTimeout]; - } + appStartMeasurement = [self getAppStartMeasurement]; - if ([self isAutoGeneratedTransaction]) { - [self startDeadlineTimer]; - } + if ([self hasIdleTimeout]) { + [self dispatchIdleTimeout]; + } + + if ([self isAutoGeneratedTransaction]) { + [self startDeadlineTimer]; + } #if SENTRY_HAS_UIKIT - // Store current amount of frames at the beginning to be able to calculate the amount of - // frames at the end of the transaction. - SentryFramesTracker *framesTracker = [SentryFramesTracker sharedInstance]; - if (framesTracker.isRunning) { - SentryScreenFrames *currentFrames = framesTracker.currentFrames; - initTotalFrames = currentFrames.total; - initSlowFrames = currentFrames.slow; - initFrozenFrames = currentFrames.frozen; - } + // Store current amount of frames at the beginning to be able to calculate the amount of + // frames at the end of the transaction. + SentryFramesTracker *framesTracker = [SentryFramesTracker sharedInstance]; + if (framesTracker.isRunning) { + SentryScreenFrames *currentFrames = framesTracker.currentFrames; + initTotalFrames = currentFrames.total; + initSlowFrames = currentFrames.slow; + initFrozenFrames = currentFrames.frozen; + } #endif // SENTRY_HAS_UIKIT #if SENTRY_TARGET_PROFILING_SUPPORTED - if (profilesSamplerDecision.decision == kSentrySampleDecisionYes) { - _isProfiling = YES; - _startSystemTime = getAbsoluteTime(); - [SentryProfiler startWithHub:hub]; - trackTracerWithID(self.traceId); - } -#endif // SENTRY_TARGET_PROFILING_SUPPORTED + if (_configuration.profilesSamplerDecision.decision == kSentrySampleDecisionYes) { + _isProfiling = YES; + _startSystemTime = getAbsoluteTime(); + [SentryProfiler startWithHub:hub]; + trackTracerWithID(self.traceId); } +#endif // SENTRY_TARGET_PROFILING_SUPPORTED return self; } @@ -219,40 +163,42 @@ - (nullable SentryTracer *)tracer - (void)dispatchIdleTimeout { if (_idleTimeoutBlock != nil) { - [self.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; } __weak SentryTracer *weakSelf = self; - _idleTimeoutBlock = [self.dispatchQueueWrapper createDispatchBlock:^{ + _idleTimeoutBlock = [_configuration.dispatchQueueWrapper createDispatchBlock:^{ if (weakSelf == nil) { SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything."); return; } [weakSelf finishInternal]; }]; + if (_idleTimeoutBlock == NULL) { SENTRY_LOG_WARN(@"Couln't create idle time out block. Can't schedule idle timeout. " @"Finishing transaction"); // If the transaction has no children, the SDK will discard it. [self finishInternal]; } else { - [self.dispatchQueueWrapper dispatchAfter:self.idleTimeout block:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchAfter:_configuration.idleTimeout + block:_idleTimeoutBlock]; } } - (BOOL)hasIdleTimeout { - return self.idleTimeout > 0 && self.dispatchQueueWrapper != nil; + return _configuration.idleTimeout > 0 && _configuration.dispatchQueueWrapper != nil; } - (BOOL)isAutoGeneratedTransaction { - return self.waitForChildren || [self hasIdleTimeout]; + return _configuration.waitForChildren || [self hasIdleTimeout]; } - (void)cancelIdleTimeout { if ([self hasIdleTimeout]) { - [self.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; + [_configuration.dispatchQueueWrapper dispatchCancel:_idleTimeoutBlock]; } } @@ -260,14 +206,14 @@ - (void)startDeadlineTimer { __weak SentryTracer *weakSelf = self; self.deadlineTimer = - [self.timerWrapper scheduledTimerWithTimeInterval:SENTRY_AUTO_TRANSACTION_DEADLINE - repeats:NO - block:^(NSTimer *_Nonnull timer) { - if (weakSelf == nil) { - return; - } - [weakSelf deadlineTimerFired]; - }]; + [_configuration.timerWrapper scheduledTimerWithTimeInterval:SENTRY_AUTO_TRANSACTION_DEADLINE + repeats:NO + block:^(NSTimer *_Nonnull timer) { + if (weakSelf == nil) { + return; + } + [weakSelf deadlineTimerFired]; + }]; } - (void)deadlineTimerFired @@ -456,7 +402,7 @@ - (void)canBeFinished - (BOOL)hasUnfinishedChildSpansToWaitFor { - if (!_waitForChildren) { + if (!self.configuration.waitForChildren) { return NO; } @@ -524,7 +470,7 @@ - (void)finishInternal }]; @synchronized(_children) { - if (self.idleTimeout > 0.0 && _children.count == 0) { + if (_configuration.idleTimeout > 0.0 && _children.count == 0) { SENTRY_LOG_DEBUG(@"Was waiting for timeout for UI event trace but it had no children, " @"will not keep transaction."); return; diff --git a/Sources/Sentry/SentryTracerConfiguration.m b/Sources/Sentry/SentryTracerConfiguration.m new file mode 100644 index 00000000000..c14ec504f4b --- /dev/null +++ b/Sources/Sentry/SentryTracerConfiguration.m @@ -0,0 +1,28 @@ +#import "SentryTracerConfiguration.h" + +@implementation SentryTracerConfiguration + ++ (SentryTracerConfiguration *)defaultConfiguration +{ + return [[SentryTracerConfiguration alloc] init]; +} + ++ (SentryTracerConfiguration *)configurationWithBlock:(void (^)(SentryTracerConfiguration *))block +{ + SentryTracerConfiguration *result = [[SentryTracerConfiguration alloc] init]; + + block(result); + + return result; +} + +- (instancetype)init +{ + if (self = [super init]) { + self.idleTimeout = 0; + self.waitForChildren = NO; + } + return self; +} + +@end diff --git a/Sources/Sentry/SentryUIEventTracker.m b/Sources/Sentry/SentryUIEventTracker.m index f15cbf752eb..d99bdf06523 100644 --- a/Sources/Sentry/SentryUIEventTracker.m +++ b/Sources/Sentry/SentryUIEventTracker.m @@ -121,12 +121,17 @@ - (void)start && ![span.operation containsString:SentrySpanOperationUIAction]; BOOL bindToScope = !ongoingScreenLoadTransaction && !ongoingManualTransaction; - transaction = - [SentrySDK.currentHub startTransactionWithContext:context - bindToScope:bindToScope - customSamplingContext:@{} - idleTimeout:self.idleTimeout - dispatchQueueWrapper:self.dispatchQueueWrapper]; + + transaction = [SentrySDK.currentHub + startTransactionWithContext:context + bindToScope:bindToScope + customSamplingContext:@{} + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *config) { + config.idleTimeout = self.idleTimeout; + config.waitForChildren = YES; + config.dispatchQueueWrapper = self.dispatchQueueWrapper; + }]]; SENTRY_LOG_DEBUG(@"SentryUIEventTracker automatically started a new transaction " @"with name: %@, bindToScope: %@", diff --git a/Sources/Sentry/include/SentryHub+Private.h b/Sources/Sentry/include/SentryHub+Private.h index 21692e7d22c..57f9ecf6b8d 100644 --- a/Sources/Sentry/include/SentryHub+Private.h +++ b/Sources/Sentry/include/SentryHub+Private.h @@ -1,7 +1,8 @@ #import "SentryHub.h" +#import "SentryTracer.h" @class SentryEnvelopeItem, SentryId, SentryScope, SentryTransaction, SentryDispatchQueueWrapper, - SentryEnvelope, SentryTracer, SentryNSTimerWrapper; + SentryEnvelope, SentryNSTimerWrapper; NS_ASSUME_NONNULL_BEGIN @@ -33,17 +34,10 @@ SentryHub (Private) operation:(NSString *)operation bindToScope:(BOOL)bindToScope; -- (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext - bindToScope:(BOOL)bindToScope - waitForChildren:(BOOL)waitForChildren - customSamplingContext:(NSDictionary *)customSamplingContext - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper; - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + configuration:(SentryTracerConfiguration *)configuration; - (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope diff --git a/Sources/Sentry/include/SentryTracer.h b/Sources/Sentry/include/SentryTracer.h index 300ea72a447..1a6fab6a8f8 100644 --- a/Sources/Sentry/include/SentryTracer.h +++ b/Sources/Sentry/include/SentryTracer.h @@ -1,5 +1,6 @@ #import "SentrySpan.h" #import "SentrySpanProtocol.h" +#import "SentryTracerConfiguration.h" #import NS_ASSUME_NONNULL_BEGIN @@ -31,13 +32,6 @@ static NSTimeInterval const SentryTracerDefaultTimeout = 3.0; @property (nullable, nonatomic, copy) void (^finishCallback)(SentryTracer *); -/** - * Indicates whether this tracer will be finished only if all children have been finished. - * If this property is @c YES and the finish function is called before all children are finished - * the tracer will automatically finish when the last child finishes. - */ -@property (readonly) BOOL waitForChildren; - /** * Retrieves a trace context from this tracer. */ @@ -72,46 +66,17 @@ static NSTimeInterval const SentryTracerDefaultTimeout = 3.0; hub:(nullable SentryHub *)hub; /** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. - * @param transactionContext Transaction context - * @param hub A hub to bind this transaction - * @param waitForChildren Whether this tracer should wait all children to finish. - */ -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - waitForChildren:(BOOL)waitForChildren; - -/** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. - * @param transactionContext Transaction context - * @param hub A hub to bind this transaction. - * @param profilesSamplerDecision Whether to sample a profile corresponding to this transaction. - * @param waitForChildren Whether this tracer should wait all children to finish. - * @param timerWrapper A wrapper around @c NSTimer, to make it testable. - */ -- (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext - hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - waitForChildren:(BOOL)waitForChildren - timerWrapper:(nullable SentryNSTimerWrapper *)timerWrapper; - -/** - * Init a @c SentryTracer with given transaction context, hub and whether the tracer should wait - * for all children to finish before it finishes. + * Init a SentryTracer with given transaction context and hub and set other fields by default + * * @param transactionContext Transaction context * @param hub A hub to bind this transaction - * @param profilesSamplerDecision Whether to sample a profile corresponding to this transaction - * @param idleTimeout The idle time to wait until to finish the transaction. + * @param configuration Configuration on how SentryTracer will behave + * + * @return SentryTracer */ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transactionContext hub:(nullable SentryHub *)hub - profilesSamplerDecision: - (nullable SentryProfilesSamplerDecision *)profilesSamplerDecision - idleTimeout:(NSTimeInterval)idleTimeout - dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper; + configuration:(SentryTracerConfiguration *)configuration; - (id)startChildWithParentId:(SentrySpanId *)parentId operation:(NSString *)operation diff --git a/Sources/Sentry/include/SentryTracerConfiguration.h b/Sources/Sentry/include/SentryTracerConfiguration.h new file mode 100644 index 00000000000..25070ff20d3 --- /dev/null +++ b/Sources/Sentry/include/SentryTracerConfiguration.h @@ -0,0 +1,50 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@class SentryNSTimerWrapper, SentryDispatchQueueWrapper, SentryProfilesSamplerDecision; + +@interface SentryTracerConfiguration : NSObject + +/** + * Return an instance of SentryTracerConfiguration with default values. + */ +@property (class, readonly) SentryTracerConfiguration *defaultConfiguration; + +/** + * Indicates whether the tracer will be finished only if all children have been finished. + * If this property is YES and the finish function is called before all children are finished + * the tracer will automatically finish when the last child finishes. + * + * Default is NO. + */ +@property (nonatomic) BOOL waitForChildren; + +/** + * A dispatch queue wrapper to intermediate between the tracer and dispatch calls. + */ +@property (nonatomic, strong, nullable) SentryDispatchQueueWrapper *dispatchQueueWrapper; + +/** + * Whether to sample a profile corresponding to this transaction + */ +@property (nonatomic, strong, nullable) SentryProfilesSamplerDecision *profilesSamplerDecision; + +/** + * The idle time to wait until to finish the transaction + * + * Default is 0 seconds + */ +@property (nonatomic) NSTimeInterval idleTimeout; + +/** + * A writer around NSTimer, to make it testable + */ +@property (nonatomic, strong, nullable) SentryNSTimerWrapper *timerWrapper; + ++ (SentryTracerConfiguration *)configurationWithBlock: + (void (^)(SentryTracerConfiguration *configuration))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift index 8b24de0ada8..7641face2c3 100644 --- a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift @@ -155,7 +155,7 @@ class SentryNetworkTrackerTests: XCTestCase { let tracer = SentryTracer(transactionContext: TransactionContext(name: SentryNetworkTrackerTests.transactionName, operation: SentryNetworkTrackerTests.transactionOperation), hub: nil, - waitForChildren: true) + configuration: SentryTracerConfiguration(block: { $0.waitForChildren = true })) tracer.finish() @@ -172,8 +172,7 @@ class SentryNetworkTrackerTests: XCTestCase { let task = createDataTask() let tracer = SentryTracer(transactionContext: TransactionContext(name: SentryNetworkTrackerTests.transactionName, operation: SentryNetworkTrackerTests.transactionOperation), - hub: nil, - waitForChildren: true) + hub: nil, configuration: SentryTracerConfiguration(block: { $0.waitForChildren = true })) fixture.scope.span = tracer sut.urlSessionTaskResume(task) diff --git a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift index b4bda6d7347..5df7d37db68 100644 --- a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift @@ -47,7 +47,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let scopeSpan = fixture.scope.span XCTAssert(scopeSpan === transaction) - XCTAssertTrue(transaction.waitForChildren) + XCTAssertTrue(Dynamic(transaction).configuration.waitForChildren.asBool ?? false) XCTAssertEqual(transaction.transactionContext.name, fixture.someTransaction) XCTAssertEqual(transaction.transactionContext.nameSource, .custom) } diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 45bea05f484..8e32aee1441 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -191,6 +191,7 @@ #import "SentryPerformanceTracker+Testing.h" #import "SentrySpanOperations.h" #import "SentryTimeToDisplayTracker.h" +#import "SentryTracerConfiguration.h" #import "TestSentryViewHierarchy.h" #if SENTRY_HAS_UIKIT # import "MockUIScene.h" diff --git a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m index 495fc004e9c..8378315cfb6 100644 --- a/Tests/SentryTests/Transaction/SentryTracerObjCTests.m +++ b/Tests/SentryTests/Transaction/SentryTracerObjCTests.m @@ -27,11 +27,14 @@ - (void)testSpanFinishesAfterTracerReleased_NoCrash_TracerIsNil SentryHub *hub = [[SentryHub alloc] initWithClient:nil andScope:nil]; SentryTransactionContext *context = [[SentryTransactionContext alloc] initWithOperation:@""]; - SentryTracer *tracer = [[SentryTracer alloc] initWithTransactionContext:context - hub:hub - profilesSamplerDecision:nil - waitForChildren:YES - timerWrapper:nil]; + SentryTracer *tracer = [[SentryTracer alloc] + initWithTransactionContext:context + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.waitForChildren = YES; + }]]; + [tracer finish]; child = [tracer startChildWithOperation:@"child"]; } @@ -55,17 +58,23 @@ - (void)testConcurrentTracerProfiling [[SentryProfilesSamplerDecision alloc] initWithDecision:kSentrySampleDecisionYes forSampleRate:@1]; - SentryTracer *tracer1 = [[SentryTracer alloc] initWithTransactionContext:context1 - hub:hub - profilesSamplerDecision:decision - waitForChildren:YES - timerWrapper:nil]; - - SentryTracer *tracer2 = [[SentryTracer alloc] initWithTransactionContext:context2 - hub:hub - profilesSamplerDecision:decision - waitForChildren:YES - timerWrapper:nil]; + SentryTracer *tracer1 = [[SentryTracer alloc] + initWithTransactionContext:context1 + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.profilesSamplerDecision = decision; + configuration.waitForChildren = YES; + }]]; + + SentryTracer *tracer2 = [[SentryTracer alloc] + initWithTransactionContext:context2 + hub:hub + configuration:[SentryTracerConfiguration configurationWithBlock:^( + SentryTracerConfiguration *configuration) { + configuration.profilesSamplerDecision = decision; + configuration.waitForChildren = YES; + }]]; // force some samples to be taken by the profiler NSMutableString *string = [NSMutableString string]; diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index 27ba0b07e99..c67dec01722 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -88,9 +88,11 @@ class SentryTracerTests: XCTestCase { let tracer = hub.startTransaction( with: transactionContext, bindToScope: false, - waitForChildren: waitForChildren, customSamplingContext: [:], - timerWrapper: timerWrapper) as! SentryTracer + configuration: SentryTracerConfiguration(block: { + $0.waitForChildren = waitForChildren + $0.timerWrapper = self.timerWrapper + })) return tracer } @@ -99,8 +101,11 @@ class SentryTracerTests: XCTestCase { with: transactionContext, bindToScope: false, customSamplingContext: [:], - idleTimeout: idleTimeout, - dispatchQueueWrapper: dispatchQueueWrapper + configuration: SentryTracerConfiguration(block: { + $0.idleTimeout = idleTimeout + $0.dispatchQueueWrapper = dispatchQueueWrapper + $0.waitForChildren = true + }) ) return tracer } @@ -276,7 +281,7 @@ class SentryTracerTests: XCTestCase { } func testFinish_WithoutHub_DoesntCaptureTransaction() { - let sut = SentryTracer(transactionContext: fixture.transactionContext, hub: nil, waitForChildren: false) + let sut = SentryTracer(transactionContext: fixture.transactionContext, hub: nil) sut.finish() From 4654f668feee8e05221a56f0e08178b8c9824068 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Wed, 5 Apr 2023 22:48:40 -0800 Subject: [PATCH 07/42] fix: use new API to fix test compilation error (#2867) --- .../UIViewController/SentryTimeToDisplayTrackerTest.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift index 4c1d3673975..4999a5f0a38 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift @@ -238,7 +238,9 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) let hub = TestHub(client: SentryClient(options: Options()), andScope: nil) - let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Test Operation"), hub: hub, waitForChildren: true) + let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Test Operation"), hub: hub, configuration: SentryTracerConfiguration(block: { config in + config.waitForChildren = true + })) let sut = fixture.getSut(for: UIViewController(), waitForFullDisplay: true) sut.start(for: tracer) From 90d17d3377ae86c2e4701219719ae696fd19a4ac Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Thu, 6 Apr 2023 14:58:51 +0200 Subject: [PATCH 08/42] test: FileManager remove perf test (#2869) CI can't compare the performance test with measure against anything, as Xcode needs baseline files for the same computer. In CI, the computer specifications might change frequently, so it's a lot of effort to create those baseline files. This test only slows down CI, and therefore, we can remove it. For more context see https://github.com/getsentry/sentry-cocoa/issues/1482. --- .../Helper/SentryFileManagerTests.swift | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Tests/SentryTests/Helper/SentryFileManagerTests.swift b/Tests/SentryTests/Helper/SentryFileManagerTests.swift index 74c05d913ad..8f35cb14a72 100644 --- a/Tests/SentryTests/Helper/SentryFileManagerTests.swift +++ b/Tests/SentryTests/Helper/SentryFileManagerTests.swift @@ -375,21 +375,6 @@ class SentryFileManagerTests: XCTestCase { XCTAssertEqual(0, fixture.delegate.envelopeItemsDeleted.count) } - /** - * We need to deserialize every envelope and check if it contains a session. - */ - func testMigrateSessionInit_WorstCasePerformance() { - sut.store(fixture.sessionEnvelope) - sut.store(fixture.sessionUpdateEnvelope) - for _ in 0...(fixture.maxCacheItems - 3) { - sut.store(TestConstants.envelope) - } - - measure { - sut.store(TestConstants.envelope) - } - } - func testGetAllEnvelopesAreSortedByDateAscending() { givenMaximumEnvelopes() From e1eed6bebb8ebe9b3747c87620a5f6e126b1c88b Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Thu, 6 Apr 2023 15:13:04 +0200 Subject: [PATCH 09/42] test: Replace fatalError with XCTUnwrap (#2870) Replace fatalError with XCTUnwrap in SentryTracerTests. --- .../Transaction/SentryTracerTests.swift | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index c67dec01722..6fd41429f49 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -123,7 +123,7 @@ class SentryTracerTests: XCTestCase { clearTestState() } - func testFinish_WithChildren_WaitsForAllChildren() { + func testFinish_WithChildren_WaitsForAllChildren() throws { let sut = fixture.getSut() let child = sut.startChild(operation: fixture.transactionOperation) sut.finish() @@ -144,7 +144,7 @@ class SentryTracerTests: XCTestCase { assertOneTransactionCaptured(sut) - let serialization = getSerializedTransaction() + let serialization = try getSerializedTransaction() let spans = serialization["spans"]! as! [[String: Any]] let tracerTimestamp: NSDate = sut.timestamp! as NSDate @@ -839,7 +839,7 @@ class SentryTracerTests: XCTestCase { XCTAssertTrue(sutOnScope === fixture.hub.scope.span) } - func testFinishAsync() { + func testFinishAsync() throws { let sut = fixture.getSut() let child = sut.startChild(operation: fixture.transactionOperation) sut.finish() @@ -871,7 +871,7 @@ class SentryTracerTests: XCTestCase { assertOneTransactionCaptured(sut) - let spans = getSerializedTransaction()["spans"]! as! [[String: Any]] + let spans = try getSerializedTransaction()["spans"]! as! [[String: Any]] XCTAssertEqual(spans.count, children * (grandchildren + 1) + 1) } @@ -906,7 +906,7 @@ class SentryTracerTests: XCTestCase { XCTAssertEqual(1, transactionsWithAppStartMeasurement.count) } - func testAddingSpansOnDifferentThread_WhileFinishing_DoesNotCrash() { + func testAddingSpansOnDifferentThread_WhileFinishing_DoesNotCrash() throws { let sut = fixture.getSut(waitForChildren: false) let children = 1_000 @@ -943,7 +943,7 @@ class SentryTracerTests: XCTestCase { queue.activate() group.wait() - let spans = getSerializedTransaction()["spans"]! as! [[String: Any]] + let spans = try getSerializedTransaction()["spans"]! as! [[String: Any]] XCTAssertGreaterThanOrEqual(spans.count, children) } @@ -1013,10 +1013,9 @@ class SentryTracerTests: XCTestCase { fixture.currentDateProvider.internalDispatchNow = newNow } - private func getSerializedTransaction() -> [String: Any] { - guard let transaction = fixture.hub.capturedEventsWithScopes.first?.event else { - fatalError("Event must not be nil.") - } + private func getSerializedTransaction() throws -> [String: Any] { + let transaction = try XCTUnwrap( fixture.hub.capturedEventsWithScopes.first?.event) + return transaction.serialize() } From e6812159bdb630ef47abaa1d6def1b790918e146 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Thu, 6 Apr 2023 22:04:41 -0800 Subject: [PATCH 10/42] ref: remove some dead code (#2864) --- .../SentryCrashInstallation+Private.h | 39 ------------------- .../Installations/SentryCrashInstallation.m | 25 ------------ .../Filters/Tools/SentryCrashVarArgs.h | 23 ----------- 3 files changed, 87 deletions(-) diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h b/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h index 2522f1809ec..196dcc5916d 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation+Private.h @@ -27,31 +27,6 @@ #import "SentryCrashInstallation.h" -/** Implement a property to be used as a "key". */ -#define IMPLEMENT_REPORT_KEY_PROPERTY(NAME, NAMEUPPER) \ - @synthesize NAME##Key = _##NAME##Key; \ - -(void)set##NAMEUPPER##Key : (NSString *)value \ - { \ - _##NAME##Key; \ - _##NAME##Key = value; \ - [self reportFieldForProperty:@ #NAME setKey:value]; \ - } - -/** Implement a property to be used as a "value". */ -#define IMPLEMENT_REPORT_VALUE_PROPERTY(NAME, NAMEUPPER, TYPE) \ - @synthesize NAME = _##NAME; \ - -(void)set##NAMEUPPER : (TYPE)value \ - { \ - _##NAME; \ - _##NAME = value; \ - [self reportFieldForProperty:@ #NAME setValue:value]; \ - } - -/** Implement a standard report property (with key and value properties) */ -#define IMPLEMENT_REPORT_PROPERTY(NAME, NAMEUPPER, TYPE) \ - IMPLEMENT_REPORT_VALUE_PROPERTY(NAME, NAMEUPPER, TYPE) \ - IMPLEMENT_REPORT_KEY_PROPERTY(NAME, NAMEUPPER) - typedef struct { const char *key; const char *value; @@ -72,20 +47,6 @@ SentryCrashInstallation () */ - (id)initWithRequiredProperties:(NSArray *)requiredProperties; -/** Set the key to be used for the specified report property. - * - * @param propertyName The name of the property. - * @param key The key to use. - */ -- (void)reportFieldForProperty:(NSString *)propertyName setKey:(id)key; - -/** Set the value of the specified report property. - * - * @param propertyName The name of the property. - * @param value The value to set. - */ -- (void)reportFieldForProperty:(NSString *)propertyName setValue:(id)value; - /** Create a new sink. Subclasses must implement this. */ - (id)sink; diff --git a/Sources/SentryCrash/Installations/SentryCrashInstallation.m b/Sources/SentryCrash/Installations/SentryCrashInstallation.m index 7d644167b1b..31f33665703 100644 --- a/Sources/SentryCrash/Installations/SentryCrashInstallation.m +++ b/Sources/SentryCrash/Installations/SentryCrashInstallation.m @@ -194,31 +194,6 @@ - (CrashHandlerData *)g_crashHandlerData return g_crashHandlerData; } -- (SentryCrashInstReportField *)reportFieldForProperty:(NSString *)propertyName -{ - SentryCrashInstReportField *field = [self.fields objectForKey:propertyName]; - if (field == nil) { - field = [SentryCrashInstReportField fieldWithIndex:self.nextFieldIndex]; - self.nextFieldIndex++; - self.crashHandlerData->reportFieldsCount = self.nextFieldIndex; - self.crashHandlerData->reportFields[field.index] = field.field; - [self.fields setObject:field forKey:propertyName]; - } - return field; -} - -- (void)reportFieldForProperty:(NSString *)propertyName setKey:(id)key -{ - SentryCrashInstReportField *field = [self reportFieldForProperty:propertyName]; - field.key = key; -} - -- (void)reportFieldForProperty:(NSString *)propertyName setValue:(id)value -{ - SentryCrashInstReportField *field = [self reportFieldForProperty:propertyName]; - field.value = value; -} - - (NSError *)validateProperties { NSMutableString *errors = [NSMutableString string]; diff --git a/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h b/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h index f2758879830..722d9c5de67 100644 --- a/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h +++ b/Sources/SentryCrash/Reporting/Filters/Tools/SentryCrashVarArgs.h @@ -71,26 +71,3 @@ typedef void (^SentryCrashVA_Block)(id entry); #define sentrycrashva_list_to_nsarray(FIRST_ARG_NAME, ARRAY_NAME) \ NSMutableArray *ARRAY_NAME = [NSMutableArray array]; \ sentrycrashva_iterate_list(FIRST_ARG_NAME, ^(id entry) { [ARRAY_NAME addObject:entry]; }) - -/** - * Convert a variable argument list into a dictionary, interpreting the vararg - * list as object, key, object, key, ... - * An autoreleased NSMutableDictionary will be created in the current scope with - * the specified name. - * - * @param FIRST_ARG_NAME The name of the first argument in the vararg list. - * @param DICT_NAME The name of the dictionary to create in the current scope. - */ -#define sentrycrashva_list_to_nsdictionary(FIRST_ARG_NAME, DICT_NAME) \ - NSMutableDictionary *DICT_NAME = [NSMutableDictionary dictionary]; \ - { \ - __block id sentrycrashva_object = nil; \ - sentrycrashva_iterate_list(FIRST_ARG_NAME, ^(id entry) { \ - if (sentrycrashva_object == nil) { \ - sentrycrashva_object = entry; \ - } else { \ - [DICT_NAME setObject:sentrycrashva_object forKey:entry]; \ - sentrycrashva_object = nil; \ - } \ - }); \ - } From 2cf1b84604c9c8e3a16b9707ef825e4d94abfb48 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 7 Apr 2023 09:25:16 +0200 Subject: [PATCH 11/42] test: Use Invocation in TestHub (#2871) Use thread safe Invocation in TestHub as running the tests locally sometimes lead to crashes in capturedTransactionsWithScope. Furthermore, remove DispatchGroup so we can remove plenty of calls to fixture.hub.group.wait(). --- .../TestSentryNSTimerWrapper.swift | 2 +- .../Network/SentryNetworkTrackerTests.swift | 6 ----- Tests/SentryTests/State/TestHub.swift | 25 ++++++++----------- .../Transaction/SentryTracerTests.swift | 22 ++-------------- 4 files changed, 13 insertions(+), 42 deletions(-) diff --git a/SentryTestUtils/TestSentryNSTimerWrapper.swift b/SentryTestUtils/TestSentryNSTimerWrapper.swift index 34b10dfb8cb..5def3be65f8 100644 --- a/SentryTestUtils/TestSentryNSTimerWrapper.swift +++ b/SentryTestUtils/TestSentryNSTimerWrapper.swift @@ -16,7 +16,7 @@ public class TestSentryNSTimerWrapper: SentryNSTimerWrapper { var block: ((Timer) -> Void)? } - public lazy var overrides = Overrides() + public var overrides = Overrides() public override func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void) -> Timer { let timer = TestTimer() diff --git a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift index 7641face2c3..d567a7a42e1 100644 --- a/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/Network/SentryNetworkTrackerTests.swift @@ -625,8 +625,6 @@ class SentryNetworkTrackerTests: XCTestCase { sut.urlSessionTask(task, setState: .completed) - fixture.hub.group.wait() - guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else { XCTFail("Expected to capture 1 event") return @@ -656,8 +654,6 @@ class SentryNetworkTrackerTests: XCTestCase { sut.urlSessionTask(task, setState: .completed) - fixture.hub.group.wait() - guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else { XCTFail("Expected to capture 1 event") return @@ -677,8 +673,6 @@ class SentryNetworkTrackerTests: XCTestCase { sut.urlSessionTask(task, setState: .completed) - fixture.hub.group.wait() - guard let envelope = self.fixture.hub.capturedEventsWithScopes.first else { XCTFail("Expected to capture 1 event") return diff --git a/Tests/SentryTests/State/TestHub.swift b/Tests/SentryTests/State/TestHub.swift index a4bc4ea5695..c66c452063f 100644 --- a/Tests/SentryTests/State/TestHub.swift +++ b/Tests/SentryTests/State/TestHub.swift @@ -1,9 +1,7 @@ import Foundation +import SentryTestUtils class TestHub: SentryHub { - - let group = DispatchGroup() - let queue = DispatchQueue(label: "TestHub", attributes: .concurrent) var startSessionInvocations: Int = 0 var closeCachedSessionInvocations: Int = 0 @@ -23,30 +21,27 @@ class TestHub: SentryHub { endSessionTimestamp = timestamp } - var sentCrashEvents: [Event] = [] + var sentCrashEvents = Invocations() override func captureCrash(_ event: Event) { - sentCrashEvents.append(event) + sentCrashEvents.record(event) } - var sentCrashEventsWithScope: [(event: Event, scope: Scope)] = [] + var sentCrashEventsWithScope = Invocations<(event: Event, scope: Scope)>() override func captureCrash(_ event: Event, with scope: Scope) { - sentCrashEventsWithScope.append((event, scope)) + sentCrashEventsWithScope.record((event, scope)) } - var capturedEventsWithScopes: [(event: Event, scope: Scope, additionalEnvelopeItems: [SentryEnvelopeItem])] = [] + var capturedEventsWithScopes = Invocations<(event: Event, scope: Scope, additionalEnvelopeItems: [SentryEnvelopeItem])>() override func capture(event: Event, scope: Scope, additionalEnvelopeItems: [SentryEnvelopeItem]) -> SentryId { - group.enter() - queue.async(flags: .barrier) { - self.capturedEventsWithScopes.append((event, scope, additionalEnvelopeItems)) - self.group.leave() - } + + self.capturedEventsWithScopes.record((event, scope, additionalEnvelopeItems)) return event.eventId } - var capturedTransactionsWithScope: [(transaction: [String: Any], scope: Scope)] = [] + var capturedTransactionsWithScope = Invocations<(transaction: [String: Any], scope: Scope)>() override func capture(_ transaction: Transaction, with scope: Scope) -> SentryId { - capturedTransactionsWithScope.append((transaction.serialize(), scope)) + capturedTransactionsWithScope.record((transaction.serialize(), scope)) return super.capture(transaction, with: scope) } } diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index 6fd41429f49..f27f3da33b3 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -603,7 +603,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() advanceTime(bySeconds: 1) sut.finish() - fixture.hub.group.wait() XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) @@ -623,16 +622,14 @@ class SentryTracerTests: XCTestCase { advanceTime(bySeconds: 0.5) secondTransaction.finish() - fixture.hub.group.wait() XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedSecondTransaction = fixture.hub.capturedEventsWithScopes.first!.event.serialize() XCTAssertNil(serializedSecondTransaction["measurements"]) firstTransaction.finish() - fixture.hub.group.wait() XCTAssertEqual(2, fixture.hub.capturedEventsWithScopes.count) - assertAppStartMeasurementOn(transaction: fixture.hub.capturedEventsWithScopes[1].event as! Transaction, appStartMeasurement: appStartMeasurement) + assertAppStartMeasurementOn(transaction: fixture.hub.capturedEventsWithScopes.invocations[1].event as! Transaction, appStartMeasurement: appStartMeasurement) } func testAddUnknownAppStartMeasurement_NotPutOnNextTransaction() { @@ -647,7 +644,6 @@ class SentryTracerTests: XCTestCase { )) fixture.getSut().finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -694,7 +690,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.hub.startTransaction(transactionContext: TransactionContext(name: "custom", operation: "custom")) as! SentryTracer sut.finish() - fixture.hub.group.wait() XCTAssertNotNil(SentrySDK.getAppStartMeasurement()) @@ -718,7 +713,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() advanceTime(bySeconds: 1.0) sut.finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -732,7 +726,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() advanceTime(bySeconds: 1.0) sut.finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -744,7 +737,6 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.finish() - fixture.hub.group.wait() assertAppStartMeasurementNotPutOnTransaction() } @@ -760,7 +752,6 @@ class SentryTracerTests: XCTestCase { childSpan.setMeasurement(name: name, value: value, unit: unit) childSpan.finish() sut.finish() - fixture.hub.group.wait() XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedTransaction = fixture.hub.capturedEventsWithScopes.first?.event.serialize() @@ -893,11 +884,9 @@ class SentryTracerTests: XCTestCase { queue.activate() group.wait() - fixture.hub.group.wait() - XCTAssertEqual(transactions, fixture.hub.capturedEventsWithScopes.count) - let transactionsWithAppStartMeasurement = fixture.hub.capturedEventsWithScopes.filter { pair in + let transactionsWithAppStartMeasurement = fixture.hub.capturedEventsWithScopes.invocations.filter { pair in let serializedTransaction = pair.event.serialize() let measurements = serializedTransaction["measurements"] as? [String: [String: Int]] return measurements == ["app_start_warm": ["value": 500]] @@ -970,8 +959,6 @@ class SentryTracerTests: XCTestCase { sut.finish() - fixture.hub.group.wait() - XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedTransaction = fixture.hub.capturedEventsWithScopes.first!.event.serialize() let measurements = serializedTransaction["measurements"] as? [String: [String: Int]] @@ -1023,16 +1010,13 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.updateStartTime(fixture.appStartEnd.addingTimeInterval(startTimestamp)) sut.finish() - fixture.hub.group.wait() } private func assertTransactionNotCaptured(_ tracer: SentryTracer) { - fixture.hub.group.wait() XCTAssertEqual(0, fixture.hub.capturedEventsWithScopes.count) } private func assertOneTransactionCaptured(_ tracer: SentryTracer) { - fixture.hub.group.wait() XCTAssertTrue(tracer.isFinished) XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) } @@ -1115,8 +1099,6 @@ class SentryTracerTests: XCTestCase { } private func assertNoMeasurementsAdded() { - fixture.hub.group.wait() - XCTAssertEqual(1, fixture.hub.capturedEventsWithScopes.count) let serializedTransaction = fixture.hub.capturedEventsWithScopes.first?.event.serialize() XCTAssertNil(serializedTransaction?["measurements"]) From afb69821393bee473130c0f1698b555dc7de0052 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 7 Apr 2023 16:43:38 -0800 Subject: [PATCH 12/42] fix: avoid screenshots with either dimension of 0 (#2876) --- CHANGELOG.md | 1 + Sources/Sentry/SentryScreenshot.m | 14 +++++++-- Tests/SentryTests/SentryScreenShotTests.swift | 30 +++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18037da91e9..bf6219bee20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Crash when serializing invalid objects (#2858) +- Don't send screenshots with either width or height of 0 (#2876)) ## 8.4.0 diff --git a/Sources/Sentry/SentryScreenshot.m b/Sources/Sentry/SentryScreenshot.m index d4b1cb21f0e..91bb2e90e2b 100644 --- a/Sources/Sentry/SentryScreenshot.m +++ b/Sources/Sentry/SentryScreenshot.m @@ -1,4 +1,5 @@ #import "SentryScreenshot.h" +#import "SentryCompiler.h" #import "SentryDependencyContainer.h" #import "SentryDispatchQueueWrapper.h" #import "SentryUIApplication.h" @@ -43,11 +44,20 @@ - (void)saveScreenShots:(NSString *)imagesDirectoryPath NSMutableArray *result = [NSMutableArray arrayWithCapacity:windows.count]; for (UIWindow *window in windows) { - UIGraphicsBeginImageContext(window.frame.size); + CGSize size = window.frame.size; + if (size.width == 0 || size.height == 0) { + // avoid API errors reported as e.g.: + // [Graphics] Invalid size provided to UIGraphicsBeginImageContext(): size={0, 0}, + // scale=1.000000 + continue; + } + UIGraphicsBeginImageContext(size); if ([window drawViewHierarchyInRect:window.bounds afterScreenUpdates:false]) { UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); - if (img.size.width > 0 || img.size.height > 0) { + // this shouldn't happen now that we discard windows with either 0 height or 0 width, + // but still, we shouldn't send any images with either one. + if (LIKELY(img.size.width > 0 && img.size.height > 0)) { NSData *bytes = UIImagePNGRepresentation(img); if (bytes && bytes.length > 0) { [result addObject:bytes]; diff --git a/Tests/SentryTests/SentryScreenShotTests.swift b/Tests/SentryTests/SentryScreenShotTests.swift index f9cd101f613..eaacf748623 100644 --- a/Tests/SentryTests/SentryScreenShotTests.swift +++ b/Tests/SentryTests/SentryScreenShotTests.swift @@ -82,18 +82,42 @@ class SentryScreenShotTests: XCTestCase { XCTAssertEqual(image?.size.width, 10) XCTAssertEqual(image?.size.height, 10) } - + func test_ZeroSizeScreenShot_GetsDiscarded() { let testWindow = TestWindow(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) fixture.uiApplication.windows = [testWindow] - + guard let data = self.fixture.sut.appScreenshots() else { XCTFail("Could not make window screenshot") return } - + XCTAssertEqual(0, data.count, "No screenshot should be taken, cause the image has zero size.") } + + func test_ZeroWidthScreenShot_GetsDiscarded() { + let testWindow = TestWindow(frame: CGRect(x: 0, y: 0, width: 0, height: 1_000)) + fixture.uiApplication.windows = [testWindow] + + guard let data = self.fixture.sut.appScreenshots() else { + XCTFail("Could not make window screenshot") + return + } + + XCTAssertEqual(0, data.count, "No screenshot should be taken, cause the image has zero width.") + } + + func test_ZeroHeightScreenShot_GetsDiscarded() { + let testWindow = TestWindow(frame: CGRect(x: 0, y: 0, width: 1_000, height: 0)) + fixture.uiApplication.windows = [testWindow] + + guard let data = self.fixture.sut.appScreenshots() else { + XCTFail("Could not make window screenshot") + return + } + + XCTAssertEqual(0, data.count, "No screenshot should be taken, cause the image has zero height.") + } class TestSentryUIApplication: SentryUIApplication { private var _windows: [UIWindow]? From c319795584164ee5c447c35622a3c20950b12362 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 7 Apr 2023 16:44:18 -0800 Subject: [PATCH 13/42] test: various fixes (#2875) --- Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift | 8 ++++++++ .../SentryAppStartTrackingIntegrationTests.swift | 5 +++++ .../SentryTimeToDisplayTrackerTest.swift | 5 +++-- .../SentryCrash/SentryCrashIntegrationTests.swift | 2 +- Tests/SentryTests/SentryClientTests.swift | 2 +- Tests/SentryTests/SentryHubTests.swift | 4 ++-- Tests/SentryTests/SentrySDKTests.swift | 10 +++++----- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift b/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift index dc71b34cfc3..5c4aabf2b75 100644 --- a/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift +++ b/Tests/SentryTests/Helper/UrlSessionDelegateSpy.swift @@ -5,5 +5,13 @@ class UrlSessionDelegateSpy: NSObject, URLSessionDelegate { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { delegateCallback() + + /* + Fixes error in tests: + + 2023-04-06 23:47:38.040259-0800 xctest[76215:8787183] [API] API MISUSE: NSURLSession delegate SentryTests.UrlSessionDelegateSpy: (0x12b124d90) + 2023-04-06 23:47:38.040521-0800 xctest[76215:8787183] [API] API MISUSE: didReceiveChallenge:completionHandler: completion handler not called + */ + completionHandler(.performDefaultHandling, nil) } } diff --git a/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift index 5a6f8f74883..4a327ab40dc 100644 --- a/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift @@ -19,6 +19,11 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { private var fixture: Fixture! private var sut: SentryAppStartTrackingIntegration! + + override class func setUp() { + super.setUp() + SentryLog.configure(true, diagnosticLevel: .debug) + } override func setUp() { super.setUp() diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift index 4999a5f0a38..c7f8260d907 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryTimeToDisplayTrackerTest.swift @@ -234,10 +234,11 @@ class SentryTimeToDisplayTrackerTest: XCTestCase { XCTAssertEqual(sut.fullDisplaySpan?.timestamp, sut.initialDisplaySpan?.timestamp) } - func testReportFullyDisplayed_afterFinishingTracer_withWaitForChildren() { + func testReportFullyDisplayed_afterFinishingTracer_withWaitForChildren() throws { fixture.dateProvider.setDate(date: Date(timeIntervalSince1970: 9)) - let hub = TestHub(client: SentryClient(options: Options()), andScope: nil) + let options = Options() + let hub = TestHub(client: SentryClient(options: options, fileManager: try TestFileManager(options: options), deleteOldEnvelopeItems: false), andScope: nil) let tracer = SentryTracer(transactionContext: TransactionContext(operation: "Test Operation"), hub: hub, configuration: SentryTracerConfiguration(block: { config in config.waitForChildren = true })) diff --git a/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift b/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift index dc1f6a4f347..1ef469ae058 100644 --- a/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/SentryCrash/SentryCrashIntegrationTests.swift @@ -272,7 +272,7 @@ class SentryCrashIntegrationTests: NotificationCenterTestCase { api?.pointee.setEnabled(true) let transport = TestTransport() - let client = SentryClient(options: fixture.options) + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) Dynamic(client).transportAdapter = TestTransportAdapter(transport: transport, options: fixture.options) hub.bindClient(client) diff --git a/Tests/SentryTests/SentryClientTests.swift b/Tests/SentryTests/SentryClientTests.swift index 26098db0d12..bfd35cc227b 100644 --- a/Tests/SentryTests/SentryClientTests.swift +++ b/Tests/SentryTests/SentryClientTests.swift @@ -52,7 +52,7 @@ class SentryClientTest: XCTestCase { let options = Options() options.dsn = SentryClientTest.dsn - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: TestCurrentDateProvider()) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: TestCurrentDateProvider(), dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) transaction = Transaction(trace: trace, children: []) diff --git a/Tests/SentryTests/SentryHubTests.swift b/Tests/SentryTests/SentryHubTests.swift index fb354d61e19..f78b2a1092f 100644 --- a/Tests/SentryTests/SentryHubTests.swift +++ b/Tests/SentryTests/SentryHubTests.swift @@ -176,8 +176,8 @@ class SentryHubTests: XCTestCase { XCTAssertEqual(crumbMessage, scopeBreadcrumbs?.first?["message"] as? String) } - func testAddUserToTheScope() { - let client = SentryClient(options: fixture.options) + func testAddUserToTheScope() throws { + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) let hub = SentryHub(client: client, andScope: Scope()) let user = User() diff --git a/Tests/SentryTests/SentrySDKTests.swift b/Tests/SentryTests/SentrySDKTests.swift index eb615f4e929..d191594cb12 100644 --- a/Tests/SentryTests/SentrySDKTests.swift +++ b/Tests/SentryTests/SentrySDKTests.swift @@ -561,13 +561,13 @@ class SentrySDKTests: XCTestCase { XCTAssertFalse(client?.isEnabled ?? true) } - func testClose_CallsFlushCorrectlyOnTransport() { + func testClose_CallsFlushCorrectlyOnTransport() throws { SentrySDK.start { options in options.dsn = SentrySDKTests.dsnAsString } let transport = TestTransport() - let client = SentryClient(options: fixture.options) + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) Dynamic(client).transportAdapter = TestTransportAdapter(transport: transport, options: fixture.options) SentrySDK.currentHub().bindClient(client) SentrySDK.close() @@ -575,13 +575,13 @@ class SentrySDKTests: XCTestCase { XCTAssertEqual(Options().shutdownTimeInterval, transport.flushInvocations.first) } - func testFlush_CallsFlushCorrectlyOnTransport() { + func testFlush_CallsFlushCorrectlyOnTransport() throws { SentrySDK.start { options in options.dsn = SentrySDKTests.dsnAsString } let transport = TestTransport() - let client = SentryClient(options: fixture.options) + let client = SentryClient(options: fixture.options, fileManager: try TestFileManager(options: fixture.options), deleteOldEnvelopeItems: false) Dynamic(client).transportAdapter = TestTransportAdapter(transport: transport, options: fixture.options) SentrySDK.currentHub().bindClient(client) @@ -591,7 +591,7 @@ class SentrySDKTests: XCTestCase { XCTAssertEqual(flushTimeout, transport.flushInvocations.first) } - func testSetpAppStartMeasurementConcurrently_() { + func testSetAppStartMeasurementConcurrently() { func setAppStartMeasurement(_ queue: DispatchQueue, _ i: Int) { group.enter() queue.async { From 4b08ceb3beb715627b1c9068fcf56a2e9ec8f43a Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Tue, 11 Apr 2023 09:13:13 +0200 Subject: [PATCH 14/42] ref: Remove not needed TransactionContext (#2884) --- Sources/Sentry/include/SentryTransaction.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Sentry/include/SentryTransaction.h b/Sources/Sentry/include/SentryTransaction.h index d12447d740b..5b9eadaae35 100644 --- a/Sources/Sentry/include/SentryTransaction.h +++ b/Sources/Sentry/include/SentryTransaction.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN -@class SentryTracer, SentryTransactionContext; +@class SentryTracer; NS_SWIFT_NAME(Transaction) @interface SentryTransaction : SentryEvent From c2e829a5a9383d4a65370adad1df00b11566b855 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 09:59:18 +0200 Subject: [PATCH 15/42] build(deps): bump github/codeql-action from 2.2.9 to 2.2.11 (#2881) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.2.9 to 2.2.11. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/04df1262e6247151b5ac09cd2c303ac36ad3f62b...d186a2a36cc67bfa1b860e6170d37fb9634742c7) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7d72210a275..b2fdeac6ec1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@04df1262e6247151b5ac09cd2c303ac36ad3f62b # pin@v2 + uses: github/codeql-action/init@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # pin@v2 with: languages: ${{ matrix.language }} @@ -35,4 +35,4 @@ jobs: -destination platform="iOS Simulator,OS=latest,name=iPhone 11 Pro" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@04df1262e6247151b5ac09cd2c303ac36ad3f62b # pin@v2 + uses: github/codeql-action/analyze@d186a2a36cc67bfa1b860e6170d37fb9634742c7 # pin@v2 From d09c79712ab5a6b42f1930218c01febd606ee6fe Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Tue, 11 Apr 2023 11:46:47 +0200 Subject: [PATCH 16/42] ref: Remove unused start transaction methods (#2886) Remove two non public start transaction methods that are not used anywhere in the SDK or by HybridSDKs. --- Sources/Sentry/SentrySDK.m | 24 ++-------------------- Sources/Sentry/include/SentrySDK+Private.h | 12 ----------- Tests/SentryTests/SentrySDKTests.swift | 14 +++++++++++++ 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/Sources/Sentry/SentrySDK.m b/Sources/Sentry/SentrySDK.m index 474c7d42003..f521df137d5 100644 --- a/Sources/Sentry/SentrySDK.m +++ b/Sources/Sentry/SentrySDK.m @@ -181,38 +181,18 @@ + (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope } + (id)startTransactionWithName:(NSString *)name operation:(NSString *)operation -{ - return [self startTransactionWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation]; -} - -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation { return [SentrySDK.currentHub startTransactionWithName:name - nameSource:source + nameSource:kSentryTransactionNameSourceCustom operation:operation]; } + (id)startTransactionWithName:(NSString *)name operation:(NSString *)operation bindToScope:(BOOL)bindToScope -{ - return [self startTransactionWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation - bindToScope:bindToScope]; -} - -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope { return [SentrySDK.currentHub startTransactionWithName:name - nameSource:source + nameSource:kSentryTransactionNameSourceCustom operation:operation bindToScope:bindToScope]; } diff --git a/Sources/Sentry/include/SentrySDK+Private.h b/Sources/Sentry/include/SentrySDK+Private.h index c50cc50ee4e..f4fbaeb2a4b 100644 --- a/Sources/Sentry/include/SentrySDK+Private.h +++ b/Sources/Sentry/include/SentrySDK+Private.h @@ -36,18 +36,6 @@ SentrySDK (Private) */ + (void)captureEnvelope:(SentryEnvelope *)envelope; -/** - * Start a transaction with a name and a name source. - */ -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation; - -+ (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope; - @end NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/SentrySDKTests.swift b/Tests/SentryTests/SentrySDKTests.swift index d191594cb12..ff89b51a6dd 100644 --- a/Tests/SentryTests/SentrySDKTests.swift +++ b/Tests/SentryTests/SentrySDKTests.swift @@ -344,6 +344,20 @@ class SentrySDKTests: XCTestCase { func testStartTransaction() { givenSdkWithHub() + let operation = "ui.load" + let name = "Load Main Screen" + let transaction = SentrySDK.startTransaction(name: name, operation: operation) + + XCTAssertEqual(operation, transaction.operation) + let tracer = transaction as! SentryTracer + XCTAssertEqual(name, tracer.traceContext.transaction) + + XCTAssertNil(SentrySDK.span) + } + + func testStartTransaction_WithBindToScope() { + givenSdkWithHub() + let span = SentrySDK.startTransaction(name: "Some Transaction", operation: "Operations", bindToScope: true) let newSpan = SentrySDK.span From 11ccc162b484e1b4f607166c42a5dce2a4b81ca9 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Tue, 11 Apr 2023 13:06:44 +0200 Subject: [PATCH 17/42] ref: Remove unused start transaction methods in hub (#2887) Remove not required unused start transaction methods in the SentryHub. --- Sources/Sentry/SentryHub.m | 22 ------------------ Sources/Sentry/SentrySDK.m | 5 +---- Sources/Sentry/include/SentryHub+Private.h | 9 -------- Tests/SentryTests/SentrySDKTests.swift | 26 ++++++++++++++-------- 4 files changed, 18 insertions(+), 44 deletions(-) diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index de8f8de3b6a..39dde8ab0e2 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -309,16 +309,6 @@ - (SentryId *)captureEvent:(SentryEvent *)event operation:operation]]; } -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation -{ - return [self - startTransactionWithContext:[[SentryTransactionContext alloc] initWithName:name - nameSource:source - operation:operation]]; -} - - (id)startTransactionWithName:(NSString *)name operation:(NSString *)operation bindToScope:(BOOL)bindToScope @@ -330,18 +320,6 @@ - (SentryId *)captureEvent:(SentryEvent *)event bindToScope:bindToScope]; } -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope -{ - return - [self startTransactionWithContext:[[SentryTransactionContext alloc] initWithName:name - nameSource:source - operation:operation] - bindToScope:bindToScope]; -} - - (id)startTransactionWithContext:(SentryTransactionContext *)transactionContext { return [self startTransactionWithContext:transactionContext customSamplingContext:@{}]; diff --git a/Sources/Sentry/SentrySDK.m b/Sources/Sentry/SentrySDK.m index f521df137d5..3c56c17d734 100644 --- a/Sources/Sentry/SentrySDK.m +++ b/Sources/Sentry/SentrySDK.m @@ -182,9 +182,7 @@ + (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope + (id)startTransactionWithName:(NSString *)name operation:(NSString *)operation { - return [SentrySDK.currentHub startTransactionWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation]; + return [SentrySDK.currentHub startTransactionWithName:name operation:operation]; } + (id)startTransactionWithName:(NSString *)name @@ -192,7 +190,6 @@ + (SentryId *)captureEvent:(SentryEvent *)event withScope:(SentryScope *)scope bindToScope:(BOOL)bindToScope { return [SentrySDK.currentHub startTransactionWithName:name - nameSource:kSentryTransactionNameSourceCustom operation:operation bindToScope:bindToScope]; } diff --git a/Sources/Sentry/include/SentryHub+Private.h b/Sources/Sentry/include/SentryHub+Private.h index 57f9ecf6b8d..622911d03cc 100644 --- a/Sources/Sentry/include/SentryHub+Private.h +++ b/Sources/Sentry/include/SentryHub+Private.h @@ -25,15 +25,6 @@ SentryHub (Private) - (void)closeCachedSessionWithTimestamp:(NSDate *_Nullable)timestamp; -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation; - -- (id)startTransactionWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - bindToScope:(BOOL)bindToScope; - - (SentryTracer *)startTransactionWithContext:(SentryTransactionContext *)transactionContext bindToScope:(BOOL)bindToScope customSamplingContext:(NSDictionary *)customSamplingContext diff --git a/Tests/SentryTests/SentrySDKTests.swift b/Tests/SentryTests/SentrySDKTests.swift index ff89b51a6dd..18dbe41dc41 100644 --- a/Tests/SentryTests/SentrySDKTests.swift +++ b/Tests/SentryTests/SentrySDKTests.swift @@ -31,6 +31,8 @@ class SentrySDKTests: XCTestCase { } let message = "message" + let operation = "ui.load" + let transactionName = "Load Main Screen" init() { CurrentDate.setCurrentDateProvider(currentDate) @@ -344,24 +346,23 @@ class SentrySDKTests: XCTestCase { func testStartTransaction() { givenSdkWithHub() - let operation = "ui.load" - let name = "Load Main Screen" - let transaction = SentrySDK.startTransaction(name: name, operation: operation) - - XCTAssertEqual(operation, transaction.operation) - let tracer = transaction as! SentryTracer - XCTAssertEqual(name, tracer.traceContext.transaction) + let transaction = SentrySDK.startTransaction(name: fixture.transactionName, operation: fixture.operation) + assertTransaction(transaction: transaction) + XCTAssertNil(SentrySDK.span) } func testStartTransaction_WithBindToScope() { givenSdkWithHub() - let span = SentrySDK.startTransaction(name: "Some Transaction", operation: "Operations", bindToScope: true) + let transaction = SentrySDK.startTransaction(name: fixture.transactionName, operation: fixture.operation, bindToScope: true) + + assertTransaction(transaction: transaction) + let newSpan = SentrySDK.span - XCTAssert(span === newSpan) + XCTAssert(transaction === newSpan) } func testInstallIntegrations() { @@ -709,6 +710,13 @@ class SentrySDKTests: XCTestCase { XCTAssertEqual(fixture.scope, hubScope) } + private func assertTransaction(transaction: Span) { + XCTAssertEqual(fixture.operation, transaction.operation) + let tracer = transaction as! SentryTracer + XCTAssertEqual(fixture.transactionName, tracer.traceContext.transaction) + XCTAssertEqual(.custom, tracer.transactionContext.nameSource) + } + private func advanceTime(bySeconds: TimeInterval) { fixture.currentDate.setDate(date: fixture.currentDate.date().addingTimeInterval(bySeconds)) } From 6f4d20cfd596b993e8ef9c1ebdeb5c835cb612be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 10:10:48 +0200 Subject: [PATCH 18/42] build(deps): bump nokogiri from 1.14.2 to 1.14.3 (#2890) Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.14.2 to 1.14.3. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.14.2...v1.14.3) --- updated-dependencies: - dependency-name: nokogiri dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index df2e4186902..2f16633abb0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -224,7 +224,7 @@ GEM nap (1.1.0) naturally (2.2.1) netrc (0.11.0) - nokogiri (1.14.2) + nokogiri (1.14.3) mini_portile2 (~> 2.8.0) racc (~> 1.4) optparse (0.1.1) From 7192d9ecad406e1fec33e2efb3d8627bad722ba8 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Wed, 12 Apr 2023 11:53:28 +0200 Subject: [PATCH 19/42] chore: Fix decision log headings (#2892) All headings in the decision log should be h3 and not h2. --- develop-docs/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/develop-docs/README.md b/develop-docs/README.md index 1307f1a278c..d4aca7010c3 100644 --- a/develop-docs/README.md +++ b/develop-docs/README.md @@ -177,33 +177,33 @@ platform-specific framework bundles only works with Xcode 12. Carthage has encouraged its users [to use XCFrameworks](https://github.com/Carthage/Carthage/tree/a91d086ceaffef65c4a4a761108f3f32c519940c#getting-started) since version 0.37.0, released in January 2021. Therefore, it's acceptable to use XCFrameworks for Carthage users. -## Remove the permissions feature +### Remove the permissions feature Date: December 14, 2022 We [removed](https://github.com/getsentry/sentry-cocoa/pull/2529) the permissions feature that we added in [7.24.0](https://github.com/getsentry/sentry-cocoa/releases/tag/7.24.0). Multiple people reported getting denied in app review because of permission access without the corresponding Info.plist entry: see [#2528](https://github.com/getsentry/sentry-cocoa/issues/2528) and [2065](https://github.com/getsentry/sentry-cocoa/issues/2065). -## Rename master to main +### Rename master to main Date: January 16th, 2023 Contributors: @kahest, @brustolin and @philipphofmann With 8.0.0, we rename the default branch from `master` to `main`. We will keep the `master` branch for backwards compatibility for package managers pointing to the `master` branch. -## SentrySwiftUI version +### SentrySwiftUI version Date: January 18th, 2023 Contributors: @brustolin and @philipphofmann We release experimental SentrySwiftUI cocoa package with the version 8.0.0 because all podspecs file in a repo need to have the same version. -## Tracking package managers +### Tracking package managers To be able to identify the package manager(PM) being used by the user, we need that the PM identify itself. Luckily all of the 3 PMs we support do this in some way, mostly by exposing a compiler directive (SPM, COCOA) or a build setting (CARTHAGE). With this information we can create a conditional compilation that injects the name of the PM. You can find this in `SentrySDKInfo.m`. -## Usage of `__has_include` +### Usage of `__has_include` Some private headers add a dependency of a public header, when those private headers are used in a sample project, or referenced from a hybrid SDK, it is treated as part of the project using it, therefore, if it points to a header that is not part of said project, a compilation error will occur. To solve this we make use of `__has_include` to try to point to the SDK version of the header, or to fallback to the direct reference when compiling the SDK. From 89d72e756356031d3d3cb41c939774c13a1ebfc0 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 12 Apr 2023 14:25:32 +0200 Subject: [PATCH 20/42] Chore : Fixing CI (#2889) Fixing CI Co-authored-by: Philipp Hofmann --- .github/workflows/test.yml | 32 ---------- Sources/Sentry/SentryAppStartTracker.m | 11 +++- .../Sentry/include/SentryAppStartTracker.h | 2 + ...ntryAppStartTrackingIntegrationTests.swift | 64 +++++++++++++------ .../SentryViewHierarchyTests.swift | 19 +++++- 5 files changed, 71 insertions(+), 57 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eadba7c2c9a..d9659a7766c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -326,38 +326,6 @@ jobs: ~/Library/Logs/scan/*.log ./fastlane/test_output/** - # macos-11 doesn't have a simulator for iOS 12 - ui-tests-swift-ios-12: - name: UI Tests on iOS 12 Simulator - runs-on: macos-11 - strategy: - matrix: - target: ['ios_swift', 'ios_objc', 'tvos_swift'] - - steps: - - uses: actions/checkout@v3 - - # GH action images don't have an iOS 12.4 simulator. Therefore we have to download and install the simulator manually. - - name: Install iOS 12.4 simulator - run: | - gem install xcode-install - xcversion simulators --install='iOS 12.4' - xcrun simctl create custom-test-device "iPhone 8" "com.apple.CoreSimulator.SimRuntime.iOS-12-4" - - # GitHub Actions sometimes fail to launch the UI tests. Therefore we retry - - name: Run Fastlane - run: for i in {1..2}; do fastlane ui_tests_${{matrix.target}} device:"iPhone 8 (12.4)" address_sanitizer:false && break ; done - shell: sh - - - name: Archiving Raw Test Logs - uses: actions/upload-artifact@v3 - if: ${{ failure() || cancelled() }} - with: - name: raw-uitest-output-${{matrix.target}}-ios-12 - path: | - ~/Library/Logs/scan/*.log - ./fastlane/test_output/** - ui-tests-address-sanitizer: name: UI Tests with Address Sanitizer runs-on: macos-12 diff --git a/Sources/Sentry/SentryAppStartTracker.m b/Sources/Sentry/SentryAppStartTracker.m index 801c067d188..3b351d1aabb 100644 --- a/Sources/Sentry/SentryAppStartTracker.m +++ b/Sources/Sentry/SentryAppStartTracker.m @@ -67,6 +67,7 @@ - (instancetype)initWithCurrentDateProvider:(id)curre self.wasInBackground = NO; self.didFinishLaunchingTimestamp = [currentDateProvider date]; self.enablePreWarmedAppStartTracing = enablePreWarmedAppStartTracing; + self.isRunning = NO; } return self; } @@ -116,6 +117,8 @@ - (void)start # if SENTRY_HAS_UIKIT [self.appStateManager start]; # endif + + self.isRunning = YES; } - (void)buildAppStartMeasurement @@ -208,8 +211,8 @@ - (void)buildAppStartMeasurement SentrySDK.appStartMeasurement = appStartMeasurement; }; - // With only running this once we know that the process is a new one when the following - // code is executed. +// With only running this once we know that the process is a new one when the following +// code is executed. // We need to make sure the block runs on each test instead of only once # if TEST block(); @@ -285,6 +288,10 @@ - (void)stop [NSNotificationCenter.defaultCenter removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; + +# if TEST + self.isRunning = NO; +# endif } - (void)dealloc diff --git a/Sources/Sentry/include/SentryAppStartTracker.h b/Sources/Sentry/include/SentryAppStartTracker.h index 82a4f1d802e..461fe5d58ec 100644 --- a/Sources/Sentry/include/SentryAppStartTracker.h +++ b/Sources/Sentry/include/SentryAppStartTracker.h @@ -16,6 +16,8 @@ NS_ASSUME_NONNULL_BEGIN @interface SentryAppStartTracker : NSObject SENTRY_NO_INIT +@property (nonatomic) BOOL isRunning; + - (instancetype)initWithCurrentDateProvider:(id)currentDateProvider dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper appStateManager:(SentryAppStateManager *)appStateManager diff --git a/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift index 4a327ab40dc..04146f54c97 100644 --- a/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Performance/AppStartTracking/SentryAppStartTrackingIntegrationTests.swift @@ -40,26 +40,35 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { sut.stop() } - func testAppStartMeasuringEnabledAndSampleRate_DoesUpdatesAppState() { + func testAppStartMeasuringEnabledAndSampleRate_properlySetupTracker() throws { sut.install(with: fixture.options) + + let tracker = try XCTUnwrap(Dynamic(sut).tracker.asObject as? SentryAppStartTracker, "SentryAppStartTrackingIntegration should have a tracker") + try assertTrackerSetupAndRunning(tracker) + } + + func testUnistall_stopsTracker() throws { + sut.install(with: fixture.options) + + let tracker = try XCTUnwrap(Dynamic(sut).tracker.asObject as? SentryAppStartTracker, "SentryAppStartTrackingIntegration should have a tracker") + try assertTrackerSetupAndRunning(tracker) + sut.uninstall() - uiWindowDidBecomeVisible() - - XCTAssertNotNil(SentrySDK.getAppStartMeasurement()) + let isRunning = Dynamic(tracker).isRunning.asBool ?? true + XCTAssertFalse(isRunning, "AppStartTracking should not be running") } - func testNoSampleRate_DoesNotUpdatesAppState() { + func testNoSampleRate_noTracker() { let options = fixture.options options.tracesSampleRate = 0.0 options.tracesSampler = nil sut.install(with: options) - - uiWindowDidBecomeVisible() - - XCTAssertNil(SentrySDK.getAppStartMeasurement()) + + let tracker = Dynamic(sut).tracker.asAnyObject as? SentryAppStartTracker + XCTAssertNil(tracker) } - func testHybridSDKModeEnabled_DoesUpdatesAppState() { + func testHybridSDKModeEnabled_properlySetupTracker() throws { PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true let options = fixture.options @@ -67,30 +76,27 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { options.tracesSampler = nil sut.install(with: options) - uiWindowDidBecomeVisible() - - XCTAssertNotNil(SentrySDK.getAppStartMeasurement()) + let tracker = try XCTUnwrap(Dynamic(sut).tracker.asObject as? SentryAppStartTracker, "SentryAppStartTrackingIntegration should have a tracker") + try assertTrackerSetupAndRunning(tracker) } - func testOnlyAppStartMeasuringEnabled_DoesNotUpdatesAppState() { + func testOnlyAppStartMeasuringEnabled_noTracker() { let options = fixture.options options.tracesSampleRate = 0.0 options.tracesSampler = nil sut.install(with: options) - uiWindowDidBecomeVisible() - - XCTAssertNil(SentrySDK.getAppStartMeasurement()) + let tracker = Dynamic(sut).tracker.asAnyObject as? SentryAppStartTracker + XCTAssertNil(tracker) } - func testAutoPerformanceTrackingDisabled_DoesNotUpdatesAppState() { + func testAutoPerformanceTrackingDisabled_noTracker() { let options = fixture.options options.enableAutoPerformanceTracing = false sut.install(with: options) - uiWindowDidBecomeVisible() - - XCTAssertNil(SentrySDK.getAppStartMeasurement()) + let tracker = Dynamic(sut).tracker.asAnyObject as? SentryAppStartTracker + XCTAssertNil(tracker) } func test_PerformanceTrackingDisabled() { @@ -100,6 +106,22 @@ class SentryAppStartTrackingIntegrationTests: NotificationCenterTestCase { XCTAssertFalse(result) } + + func assertTrackerSetupAndRunning(_ tracker: SentryAppStartTracker) throws { + let dateProvider = Dynamic(tracker).currentDate.asObject as? DefaultCurrentDateProvider + + XCTAssertEqual(dateProvider, DefaultCurrentDateProvider.sharedInstance()) + + _ = try XCTUnwrap(Dynamic(tracker).dispatchQueue.asAnyObject as? SentryDispatchQueueWrapper, "Tracker does not have a dispatch queue.") + + let appStateManager = Dynamic(tracker).appStateManager.asObject as? SentryAppStateManager + + XCTAssertEqual(appStateManager, SentryDependencyContainer.sharedInstance().appStateManager) + + _ = try XCTUnwrap(Dynamic(tracker).sysctl.asObject as? SentrySysctl, "Tracker does not have a Sysctl") + + XCTAssertTrue(tracker.isRunning, "AppStartTracking should be running") + } } #endif diff --git a/Tests/SentryTests/SentryViewHierarchyTests.swift b/Tests/SentryTests/SentryViewHierarchyTests.swift index f55f1318ce9..8dfef6bb3b6 100644 --- a/Tests/SentryTests/SentryViewHierarchyTests.swift +++ b/Tests/SentryTests/SentryViewHierarchyTests.swift @@ -4,7 +4,6 @@ import XCTest #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) class SentryViewHierarchyTests: XCTestCase { private class Fixture { - let uiApplication = TestSentryUIApplication() var sut: SentryViewHierarchy { @@ -16,10 +15,26 @@ class SentryViewHierarchyTests: XCTestCase { override func setUp() { super.setUp() + fixture = Fixture() SentryDependencyContainer.sharedInstance().application = fixture.uiApplication } + override func setUpWithError() throws { + try super.setUpWithError() + + /** + * This test is making iOS 13 simulator hang in GH workflow, + * thats why we need to check for iOS 13 or later. + * By testing this in the other versions of iOS we guarantee the behavior + * mean while, running an iOS 12 sample with Saucelabs ensures this feature + * is not crashing the app. + */ + guard #available(iOS 13, *) else { + throw XCTSkip("Skipping for iOS < 13") + } + } + override func tearDown() { super.tearDown() clearTestState() @@ -164,7 +179,7 @@ class SentryViewHierarchyTests: XCTestCase { ex.fulfill() XCTAssertTrue(Thread.isMainThread) } - + let dispatch = DispatchQueue(label: "background") dispatch.async { let _ = sut.fetch() From a6f8b189b77aff74ef52802779e0b0de9026ddb0 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Wed, 12 Apr 2023 15:28:58 +0200 Subject: [PATCH 21/42] ci: Run UI tests on iOS 16 (#2893) --- .github/workflows/saucelabs-UI-tests.yml | 5 ++++- .sauce/config.yml | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/saucelabs-UI-tests.yml b/.github/workflows/saucelabs-UI-tests.yml index aa42e663feb..8cd6904d274 100644 --- a/.github/workflows/saucelabs-UI-tests.yml +++ b/.github/workflows/saucelabs-UI-tests.yml @@ -33,7 +33,7 @@ jobs: xcode: '13.4.1' - runs-on: macos-12 - xcode: '14.1' + xcode: '14.2' steps: - uses: actions/checkout@v3 @@ -93,6 +93,9 @@ jobs: fail-fast: false matrix: include: + - xcode: '14.2' + suite: 'iOS-16' + - xcode: '13.4.1' suite: 'iOS-15' diff --git a/.sauce/config.yml b/.sauce/config.yml index c1b73083197..b3374e976f6 100644 --- a/.sauce/config.yml +++ b/.sauce/config.yml @@ -12,6 +12,11 @@ xcuitest: testApp: ./DerivedData/Build/Products/Test-iphoneos/iOS-SwiftUITests-Runner.app suites: + + - name: "iOS-16" + devices: + - name: "iPhone.*" + platformVersion: "16.4" - name: "iOS-15" devices: From 4d682295564134a1b3a8e4b4579bb067b4dbf9b7 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Thu, 13 Apr 2023 08:34:30 +0200 Subject: [PATCH 22/42] feat: Core data operation in the main thread (#2879) Adding extra information to a core data span when running in the main thread --- CHANGELOG.md | 4 + Sources/Sentry/SentryClient.m | 13 +-- Sources/Sentry/SentryCoreDataTracker.m | 30 ++++++- .../SentryCoreDataTrackingIntegration.m | 7 +- Sources/Sentry/SentryDependencyContainer.m | 13 +++ Sources/Sentry/SentryExtraContextProvider.m | 8 +- Sources/Sentry/SentryNSDataSwizzling.m | 20 +---- Sources/Sentry/SentryNSDataTracker.m | 5 +- Sources/Sentry/SentryNSProcessInfoWrapper.mm | 8 ++ .../SentryPerformanceTrackingIntegration.m | 3 +- Sources/Sentry/SentryProfiler.mm | 2 +- Sources/Sentry/SentryThreadInspector.m | 19 +++++ .../Sentry/include/SentryCoreDataTracker.h | 6 ++ .../include/SentryDependencyContainer.h | 3 +- .../Sentry/include/SentryInternalDefines.h | 2 + .../Sentry/include/SentryThreadInspector.h | 4 +- .../CoreData/SentryCoreDataTrackerTest.swift | 85 ++++++++++++++++--- 17 files changed, 180 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf6219bee20..95fb3c4d277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- feat: Core data operation in the main thread (#2879) + ### Fixes - Crash when serializing invalid objects (#2858) diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index 239124673ca..663f4c22920 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -119,18 +119,9 @@ - (instancetype)initWithOptions:(SentryOptions *)options transportAdapter:(SentryTransportAdapter *)transportAdapter { - SentryInAppLogic *inAppLogic = - [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes - inAppExcludes:options.inAppExcludes]; - SentryCrashStackEntryMapper *crashStackEntryMapper = - [[SentryCrashStackEntryMapper alloc] initWithInAppLogic:inAppLogic]; - SentryStacktraceBuilder *stacktraceBuilder = - [[SentryStacktraceBuilder alloc] initWithCrashStackEntryMapper:crashStackEntryMapper]; - id machineContextWrapper = - [[SentryCrashDefaultMachineContextWrapper alloc] init]; SentryThreadInspector *threadInspector = - [[SentryThreadInspector alloc] initWithStacktraceBuilder:stacktraceBuilder - andMachineContextWrapper:machineContextWrapper]; + [[SentryThreadInspector alloc] initWithOptions:options]; + SentryExtraContextProvider *extraContextProvider = [SentryExtraContextProvider sharedInstance]; return [self initWithOptions:options diff --git a/Sources/Sentry/SentryCoreDataTracker.m b/Sources/Sentry/SentryCoreDataTracker.m index 98567e4a56d..0d3ee19f749 100644 --- a/Sources/Sentry/SentryCoreDataTracker.m +++ b/Sources/Sentry/SentryCoreDataTracker.m @@ -1,20 +1,30 @@ #import "SentryCoreDataTracker.h" +#import "SentryFrame.h" #import "SentryHub+Private.h" +#import "SentryInternalDefines.h" #import "SentryLog.h" +#import "SentryNSProcessInfoWrapper.h" #import "SentryPredicateDescriptor.h" #import "SentrySDK+Private.h" #import "SentryScope+Private.h" #import "SentrySpanProtocol.h" +#import "SentryStacktrace.h" +#import "SentryThreadInspector.h" @implementation SentryCoreDataTracker { SentryPredicateDescriptor *predicateDescriptor; + SentryThreadInspector *_threadInspector; + SentryNSProcessInfoWrapper *_processInfoWrapper; } -- (instancetype)init +- (instancetype)initWithThreadInspector:(SentryThreadInspector *)threadInspector + processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper; { if (self = [super init]) { predicateDescriptor = [[SentryPredicateDescriptor alloc] init]; + _threadInspector = threadInspector; + _processInfoWrapper = processInfoWrapper; } return self; } @@ -47,8 +57,9 @@ - (NSArray *)managedObjectContext:(NSManagedObjectContext *)context NSArray *result = original(request, error); if (fetchSpan) { - [fetchSpan setDataValue:[NSNumber numberWithInteger:result.count] forKey:@"read_count"]; + [self mainThreadExtraInfo:fetchSpan]; + [fetchSpan setDataValue:[NSNumber numberWithInteger:result.count] forKey:@"read_count"]; [fetchSpan finishWithStatus:result == nil ? kSentrySpanStatusInternalError : kSentrySpanStatusOk]; @@ -91,6 +102,7 @@ - (BOOL)managedObjectContext:(NSManagedObjectContext *)context BOOL result = original(error); if (fetchSpan) { + [self mainThreadExtraInfo:fetchSpan]; [fetchSpan finishWithStatus:result ? kSentrySpanStatusOk : kSentrySpanStatusInternalError]; SENTRY_LOG_DEBUG(@"SentryCoreDataTracker automatically finished span with status: %@", @@ -100,6 +112,20 @@ - (BOOL)managedObjectContext:(NSManagedObjectContext *)context return result; } +- (void)mainThreadExtraInfo:(SentrySpan *)span +{ + BOOL isMainThread = [NSThread isMainThread]; + + [span setDataValue:@(isMainThread) forKey:BLOCKED_MAIN_THREAD]; + + if (!isMainThread) { + return; + } + + SentryStacktrace *stackTrace = [_threadInspector stacktraceForCurrentThreadAsyncUnsafe]; + [span setFrames:stackTrace.frames]; +} + - (NSString *)descriptionForOperations: (NSDictionary *> *)operations inContext:(NSManagedObjectContext *)context diff --git a/Sources/Sentry/SentryCoreDataTrackingIntegration.m b/Sources/Sentry/SentryCoreDataTrackingIntegration.m index 5691c672229..937e2d6bb39 100644 --- a/Sources/Sentry/SentryCoreDataTrackingIntegration.m +++ b/Sources/Sentry/SentryCoreDataTrackingIntegration.m @@ -1,9 +1,12 @@ #import "SentryCoreDataTrackingIntegration.h" #import "SentryCoreDataSwizzling.h" #import "SentryCoreDataTracker.h" +#import "SentryDependencyContainer.h" #import "SentryLog.h" #import "SentryNSDataSwizzling.h" +#import "SentryNSProcessInfoWrapper.h" #import "SentryOptions.h" +#import "SentryThreadInspector.h" @interface SentryCoreDataTrackingIntegration () @@ -20,7 +23,9 @@ - (BOOL)installWithOptions:(SentryOptions *)options return NO; } - self.tracker = [[SentryCoreDataTracker alloc] init]; + self.tracker = [[SentryCoreDataTracker alloc] + initWithThreadInspector:[[SentryThreadInspector alloc] initWithOptions:options] + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; [SentryCoreDataSwizzling.sharedInstance startWithMiddleware:self.tracker]; return YES; diff --git a/Sources/Sentry/SentryDependencyContainer.m b/Sources/Sentry/SentryDependencyContainer.m index 77c0f6f051a..1aa3991f5ba 100644 --- a/Sources/Sentry/SentryDependencyContainer.m +++ b/Sources/Sentry/SentryDependencyContainer.m @@ -1,6 +1,7 @@ #import "SentryANRTracker.h" #import "SentryDefaultCurrentDateProvider.h" #import "SentryDispatchQueueWrapper.h" +#import "SentryNSProcessInfoWrapper.h" #import "SentryUIApplication.h" #import #import @@ -229,6 +230,18 @@ - (SentryMXManager *)metricKitManager return _metricKitManager; } +- (SentryNSProcessInfoWrapper *)processInfoWrapper +{ + if (_processInfoWrapper == nil) { + @synchronized(sentryDependencyContainerLock) { + if (_processInfoWrapper == nil) { + _processInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + } + } + } + return _processInfoWrapper; +} + #endif @end diff --git a/Sources/Sentry/SentryExtraContextProvider.m b/Sources/Sentry/SentryExtraContextProvider.m index ef34501854f..1ed102cf6dd 100644 --- a/Sources/Sentry/SentryExtraContextProvider.m +++ b/Sources/Sentry/SentryExtraContextProvider.m @@ -1,6 +1,7 @@ #import "SentryExtraContextProvider.h" #import "SentryCrashIntegration.h" #import "SentryCrashWrapper.h" +#import "SentryDependencyContainer.h" #import "SentryNSProcessInfoWrapper.h" #import "SentryUIDeviceWrapper.h" @@ -25,9 +26,10 @@ + (instancetype)sharedInstance - (instancetype)init { - return [self initWithCrashWrapper:[SentryCrashWrapper sharedInstance] - deviceWrapper:[[SentryUIDeviceWrapper alloc] init] - processInfoWrapper:[[SentryNSProcessInfoWrapper alloc] init]]; + return + [self initWithCrashWrapper:[SentryCrashWrapper sharedInstance] + deviceWrapper:[[SentryUIDeviceWrapper alloc] init] + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; } - (instancetype)initWithCrashWrapper:(id)crashWrapper diff --git a/Sources/Sentry/SentryNSDataSwizzling.m b/Sources/Sentry/SentryNSDataSwizzling.m index a54d6459364..c61c4fd7993 100644 --- a/Sources/Sentry/SentryNSDataSwizzling.m +++ b/Sources/Sentry/SentryNSDataSwizzling.m @@ -2,6 +2,7 @@ #import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashMachineContextWrapper.h" #import "SentryCrashStackEntryMapper.h" +#import "SentryDependencyContainer.h" #import "SentryInAppLogic.h" #import "SentryNSDataTracker.h" #import "SentryNSProcessInfoWrapper.h" @@ -32,8 +33,8 @@ + (SentryNSDataSwizzling *)shared - (void)startWithOptions:(SentryOptions *)options { self.dataTracker = [[SentryNSDataTracker alloc] - initWithThreadInspector:[self buildThreadInspectorForOptions:options] - processInfoWrapper:[[SentryNSProcessInfoWrapper alloc] init]]; + initWithThreadInspector:[[SentryThreadInspector alloc] initWithOptions:options] + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; [self.dataTracker enable]; [SentryNSDataSwizzling swizzleNSData]; } @@ -43,21 +44,6 @@ - (void)stop [self.dataTracker disable]; } -- (SentryThreadInspector *)buildThreadInspectorForOptions:(SentryOptions *)options -{ - SentryInAppLogic *inAppLogic = - [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes - inAppExcludes:options.inAppExcludes]; - SentryCrashStackEntryMapper *crashStackEntryMapper = - [[SentryCrashStackEntryMapper alloc] initWithInAppLogic:inAppLogic]; - SentryStacktraceBuilder *stacktraceBuilder = - [[SentryStacktraceBuilder alloc] initWithCrashStackEntryMapper:crashStackEntryMapper]; - id machineContextWrapper = - [[SentryCrashDefaultMachineContextWrapper alloc] init]; - return [[SentryThreadInspector alloc] initWithStacktraceBuilder:stacktraceBuilder - andMachineContextWrapper:machineContextWrapper]; -} - // SentrySwizzleInstanceMethod declaration shadows a local variable. The swizzling is working // fine and we accept this warning. #pragma clang diagnostic push diff --git a/Sources/Sentry/SentryNSDataTracker.m b/Sources/Sentry/SentryNSDataTracker.m index 100179076ba..0880e159e5f 100644 --- a/Sources/Sentry/SentryNSDataTracker.m +++ b/Sources/Sentry/SentryNSDataTracker.m @@ -5,6 +5,7 @@ #import "SentryFileManager.h" #import "SentryFrame.h" #import "SentryHub+Private.h" +#import "SentryInternalDefines.h" #import "SentryLog.h" #import "SentryNSProcessInfoWrapper.h" #import "SentryOptions.h" @@ -187,7 +188,7 @@ - (void)mainThreadExtraInfo:(id)span { BOOL isMainThread = [NSThread isMainThread]; - [span setDataValue:@(isMainThread) forKey:@"blocked_main_thread"]; + [span setDataValue:@(isMainThread) forKey:BLOCKED_MAIN_THREAD]; if (!isMainThread) { return; @@ -207,7 +208,7 @@ - (void)mainThreadExtraInfo:(id)span // and only the 'main' frame remains in the stack // therefore, there is nothing to do about it // and we should not report it as an issue. - [span setDataValue:@(NO) forKey:@"blocked_main_thread"]; + [span setDataValue:@(NO) forKey:BLOCKED_MAIN_THREAD]; } else { [((SentrySpan *)span) setFrames:frames]; } diff --git a/Sources/Sentry/SentryNSProcessInfoWrapper.mm b/Sources/Sentry/SentryNSProcessInfoWrapper.mm index 5d672f6c7e3..d92bc3bace4 100644 --- a/Sources/Sentry/SentryNSProcessInfoWrapper.mm +++ b/Sources/Sentry/SentryNSProcessInfoWrapper.mm @@ -2,6 +2,14 @@ @implementation SentryNSProcessInfoWrapper ++ (SentryNSProcessInfoWrapper *)shared +{ + static SentryNSProcessInfoWrapper *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); + return instance; +} + - (NSString *)processDirectoryPath { return NSBundle.mainBundle.bundlePath; diff --git a/Sources/Sentry/SentryPerformanceTrackingIntegration.m b/Sources/Sentry/SentryPerformanceTrackingIntegration.m index 632fdb4a527..d525f2a60cb 100644 --- a/Sources/Sentry/SentryPerformanceTrackingIntegration.m +++ b/Sources/Sentry/SentryPerformanceTrackingIntegration.m @@ -1,5 +1,6 @@ #import "SentryPerformanceTrackingIntegration.h" #import "SentryDefaultObjCRuntimeWrapper.h" +#import "SentryDependencyContainer.h" #import "SentryDispatchQueueWrapper.h" #import "SentryLog.h" #import "SentryNSProcessInfoWrapper.h" @@ -40,7 +41,7 @@ - (BOOL)installWithOptions:(SentryOptions *)options dispatchQueue:dispatchQueue objcRuntimeWrapper:[SentryDefaultObjCRuntimeWrapper sharedInstance] subClassFinder:subClassFinder - processInfoWrapper:[[SentryNSProcessInfoWrapper alloc] init]]; + processInfoWrapper:[SentryDependencyContainer.sharedInstance processInfoWrapper]]; [self.swizzling start]; SentryUIViewControllerPerformanceTracker.shared.enableWaitForFullDisplay diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index 32bcf20df94..0a9301761ec 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -543,7 +543,7 @@ - (void)startMetricProfiler _gCurrentSystemWrapper = [[SentrySystemWrapper alloc] init]; } if (_gCurrentProcessInfoWrapper == nil) { - _gCurrentProcessInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + _gCurrentProcessInfoWrapper = [SentryDependencyContainer.sharedInstance processInfoWrapper]; } if (_gCurrentTimerWrapper == nil) { _gCurrentTimerWrapper = [[SentryNSTimerWrapper alloc] init]; diff --git a/Sources/Sentry/SentryThreadInspector.m b/Sources/Sentry/SentryThreadInspector.m index e6829a24673..8e1b66dc235 100644 --- a/Sources/Sentry/SentryThreadInspector.m +++ b/Sources/Sentry/SentryThreadInspector.m @@ -1,8 +1,12 @@ #import "SentryThreadInspector.h" +#import "SentryCrashDefaultMachineContextWrapper.h" #import "SentryCrashStackCursor.h" #include "SentryCrashStackCursor_MachineContext.h" +#import "SentryCrashStackEntryMapper.h" #include "SentryCrashSymbolicator.h" #import "SentryFrame.h" +#import "SentryInAppLogic.h" +#import "SentryOptions.h" #import "SentryStacktrace.h" #import "SentryStacktraceBuilder.h" #import "SentryThread.h" @@ -59,6 +63,21 @@ - (id)initWithStacktraceBuilder:(SentryStacktraceBuilder *)stacktraceBuilder return self; } +- (instancetype)initWithOptions:(SentryOptions *)options +{ + SentryInAppLogic *inAppLogic = + [[SentryInAppLogic alloc] initWithInAppIncludes:options.inAppIncludes + inAppExcludes:options.inAppExcludes]; + SentryCrashStackEntryMapper *crashStackEntryMapper = + [[SentryCrashStackEntryMapper alloc] initWithInAppLogic:inAppLogic]; + SentryStacktraceBuilder *stacktraceBuilder = + [[SentryStacktraceBuilder alloc] initWithCrashStackEntryMapper:crashStackEntryMapper]; + id machineContextWrapper = + [[SentryCrashDefaultMachineContextWrapper alloc] init]; + return [self initWithStacktraceBuilder:stacktraceBuilder + andMachineContextWrapper:machineContextWrapper]; +} + - (SentryStacktrace *)stacktraceForCurrentThreadAsyncUnsafe { return [self.stacktraceBuilder buildStacktraceForCurrentThreadAsyncUnsafe]; diff --git a/Sources/Sentry/include/SentryCoreDataTracker.h b/Sources/Sentry/include/SentryCoreDataTracker.h index a6967e1ae96..773e56aeffb 100644 --- a/Sources/Sentry/include/SentryCoreDataTracker.h +++ b/Sources/Sentry/include/SentryCoreDataTracker.h @@ -7,7 +7,13 @@ NS_ASSUME_NONNULL_BEGIN static NSString *const SENTRY_COREDATA_FETCH_OPERATION = @"db.sql.query"; static NSString *const SENTRY_COREDATA_SAVE_OPERATION = @"db.sql.transaction"; +@class SentryThreadInspector, SentryNSProcessInfoWrapper; + @interface SentryCoreDataTracker : NSObject +SENTRY_NO_INIT + +- (instancetype)initWithThreadInspector:(SentryThreadInspector *)threadInspector + processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper; @end diff --git a/Sources/Sentry/include/SentryDependencyContainer.h b/Sources/Sentry/include/SentryDependencyContainer.h index ad5905fd98a..49d34bd8f9a 100644 --- a/Sources/Sentry/include/SentryDependencyContainer.h +++ b/Sources/Sentry/include/SentryDependencyContainer.h @@ -4,7 +4,7 @@ @class SentryAppStateManager, SentryCrashWrapper, SentryThreadWrapper, SentrySwizzleWrapper, SentryDispatchQueueWrapper, SentryDebugImageProvider, SentryANRTracker, - SentryNSNotificationCenterWrapper, SentryMXManager; + SentryNSNotificationCenterWrapper, SentryMXManager, SentryNSProcessInfoWrapper; #if SENTRY_HAS_UIKIT @class SentryScreenshot, SentryUIApplication, SentryViewHierarchy; @@ -32,6 +32,7 @@ SENTRY_NO_INIT @property (nonatomic, strong) SentryNSNotificationCenterWrapper *notificationCenterWrapper; @property (nonatomic, strong) SentryDebugImageProvider *debugImageProvider; @property (nonatomic, strong) SentryANRTracker *anrTracker; +@property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; #if SENTRY_HAS_UIKIT @property (nonatomic, strong) SentryScreenshot *screenshot; diff --git a/Sources/Sentry/include/SentryInternalDefines.h b/Sources/Sentry/include/SentryInternalDefines.h index f18f232a38f..5b8ae123250 100644 --- a/Sources/Sentry/include/SentryInternalDefines.h +++ b/Sources/Sentry/include/SentryInternalDefines.h @@ -31,3 +31,5 @@ static NSString *const SentryDebugImageType = @"macho"; } \ (__cond_result); \ }) + +#define BLOCKED_MAIN_THREAD @"blocked_main_thread" diff --git a/Sources/Sentry/include/SentryThreadInspector.h b/Sources/Sentry/include/SentryThreadInspector.h index 77da2bf208f..216aac1ba46 100644 --- a/Sources/Sentry/include/SentryThreadInspector.h +++ b/Sources/Sentry/include/SentryThreadInspector.h @@ -2,7 +2,7 @@ #import "SentryDefines.h" #import -@class SentryThread, SentryStacktraceBuilder, SentryStacktrace; +@class SentryThread, SentryStacktraceBuilder, SentryStacktrace, SentryOptions; NS_ASSUME_NONNULL_BEGIN @@ -12,6 +12,8 @@ SENTRY_NO_INIT - (id)initWithStacktraceBuilder:(SentryStacktraceBuilder *)stacktraceBuilder andMachineContextWrapper:(id)machineContextWrapper; +- (instancetype)initWithOptions:(SentryOptions *)options; + - (nullable SentryStacktrace *)stacktraceForCurrentThreadAsyncUnsafe; /** diff --git a/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift b/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift index f63ac81b84c..2236b2d310d 100644 --- a/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift +++ b/Tests/SentryTests/Integrations/Performance/CoreData/SentryCoreDataTrackerTest.swift @@ -6,9 +6,18 @@ class SentryCoreDataTrackerTests: XCTestCase { private class Fixture { let context = TestNSManagedObjectContext() + let threadInspector = TestThreadInspector.instance + let imageProvider = TestDebugImageProvider() func getSut() -> SentryCoreDataTracker { - return SentryCoreDataTracker() + imageProvider.debugImages = [TestData.debugImage] + SentryDependencyContainer.sharedInstance().debugImageProvider = imageProvider + + threadInspector.allThreads = [TestData.thread2] + let processInfoWrapper = TestSentryNSProcessInfoWrapper() + processInfoWrapper.overrides.processDirectoryPath = "sentrytest" + + return SentryCoreDataTracker(threadInspector: threadInspector, processInfoWrapper: processInfoWrapper) } func testEntity() -> TestEntity { @@ -46,6 +55,17 @@ class SentryCoreDataTrackerTests: XCTestCase { let fetch = NSFetchRequest(entityName: "TestEntity") assertRequest(fetch, expectedDescription: "SELECT 'TestEntity'") } + + func testFetchRequestBackgroundThread() { + let expect = expectation(description: "Operation in background thread") + DispatchQueue.global(qos: .default).async { + let fetch = NSFetchRequest(entityName: "TestEntity") + self.assertRequest(fetch, expectedDescription: "SELECT 'TestEntity'", mainThread: false) + expect.fulfill() + } + + wait(for: [expect], timeout: 0.1) + } func test_FetchRequest_WithPredicate() { let fetch = NSFetchRequest(entityName: "TestEntity") @@ -82,6 +102,17 @@ class SentryCoreDataTrackerTests: XCTestCase { fixture.context.inserted = [fixture.testEntity()] assertSave("INSERTED 1 'TestEntity'") } + + func testSaveBackgroundThread() { + let expect = expectation(description: "Operation in background thread") + DispatchQueue.global(qos: .default).async { + self.fixture.context.inserted = [self.fixture.testEntity()] + self.assertSave("INSERTED 1 'TestEntity'", mainThread: false) + expect.fulfill() + } + + wait(for: [expect], timeout: 0.1) + } func test_Save_2Insert_1Entity() { fixture.context.inserted = [fixture.testEntity(), fixture.testEntity()] @@ -229,7 +260,7 @@ class SentryCoreDataTrackerTests: XCTestCase { XCTAssertEqual(transaction.children.count, 0) } - func assertSave(_ expectedDescription: String) { + func assertSave(_ expectedDescription: String, mainThread: Bool = true) { let sut = fixture.getSut() let transaction = startTransaction() @@ -237,13 +268,27 @@ class SentryCoreDataTrackerTests: XCTestCase { try? sut.saveManagedObjectContext(fixture.context) { _ in return true } - - XCTAssertEqual(transaction.children.count, 1) - XCTAssertEqual(transaction.children[0].operation, SENTRY_COREDATA_SAVE_OPERATION) - XCTAssertEqual(transaction.children[0].spanDescription, expectedDescription) + + guard let dbSpan = try? XCTUnwrap(transaction.children.first) else { + XCTFail("Span for DB operation don't exist.") + return + } + + XCTAssertEqual(dbSpan.operation, SENTRY_COREDATA_SAVE_OPERATION) + XCTAssertEqual(dbSpan.spanDescription, expectedDescription) + XCTAssertEqual(dbSpan.data["blocked_main_thread"] as? Bool ?? false, mainThread) + + if mainThread { + guard let frames = (dbSpan as? SentrySpan)?.frames else { + XCTFail("File IO Span in the main thread has no frames") + return + } + XCTAssertEqual(frames.first, TestData.mainFrame) + XCTAssertEqual(frames.last, TestData.testFrame) + } } - func assertRequest(_ fetch: NSFetchRequest, expectedDescription: String) { + func assertRequest(_ fetch: NSFetchRequest, expectedDescription: String, mainThread: Bool = true) { let transaction = startTransaction() let sut = fixture.getSut() @@ -254,12 +299,28 @@ class SentryCoreDataTrackerTests: XCTestCase { let result = try? sut.fetchManagedObjectContext(context, request: fetch) { _, _ in return [someEntity] } - + + guard let dbSpan = try? XCTUnwrap(transaction.children.first) else { + XCTFail("Span for DB operation don't exist.") + return + } + XCTAssertEqual(result?.count, 1) - XCTAssertEqual(transaction.children.count, 1) - XCTAssertEqual(transaction.children[0].operation, SENTRY_COREDATA_FETCH_OPERATION) - XCTAssertEqual(transaction.children[0].spanDescription, expectedDescription) - XCTAssertEqual(transaction.children[0].data["read_count"] as? Int, 1) + XCTAssertEqual(dbSpan.operation, SENTRY_COREDATA_FETCH_OPERATION) + XCTAssertEqual(dbSpan.spanDescription, expectedDescription) + XCTAssertEqual(dbSpan.data["read_count"] as? Int, 1) + XCTAssertEqual(dbSpan.data["blocked_main_thread"] as? Bool ?? false, mainThread) + + if mainThread { + guard let frames = (dbSpan as? SentrySpan)?.frames else { + XCTFail("File IO Span in the main thread has no frames") + return + } + XCTAssertEqual(frames.first, TestData.mainFrame) + XCTAssertEqual(frames.last, TestData.testFrame) + } else { + XCTAssertNil((dbSpan as? SentrySpan)?.frames) + } } private func startTransaction() -> SentryTracer { From bd054788823ac0162b74774f8b4f9ab17754880c Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Thu, 13 Apr 2023 08:42:41 +0200 Subject: [PATCH 23/42] ci: Remove iOS 12 simulator (#2891) Remove running unit tests on iOS 12 simulator as the xcode-install tool is being sunset. Instead, we now run UI tests in saucelabs on iOS 12. Fixes GH-2862 --- .github/workflows/saucelabs-UI-tests.yml | 3 +++ .github/workflows/test.yml | 15 --------------- .sauce/config.yml | 2 +- Sources/Sentry/SentryNetworkTracker.m | 9 +++++++++ develop-docs/README.md | 20 +++++++++++++++++++- scripts/no-changes-in-high-risk-files.sh | 7 ++++--- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/.github/workflows/saucelabs-UI-tests.yml b/.github/workflows/saucelabs-UI-tests.yml index 8cd6904d274..3ae2a310a2a 100644 --- a/.github/workflows/saucelabs-UI-tests.yml +++ b/.github/workflows/saucelabs-UI-tests.yml @@ -109,6 +109,9 @@ jobs: - xcode: '13.4.1' suite: 'iOS-13' + - xcode: '13.4.1' + suite: 'iOS-12' + steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d9659a7766c..4577fe4fc0b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,13 +81,6 @@ jobs: # we get the error 'XCTest/XCTest.h' not found. Setting ENABLE_TESTING_SEARCH_PATH=YES # doesn't work. include: - # iOS 12.4 - - runs-on: macos-11 - platform: 'iOS' - xcode: '13.2.1' - test-destination-os: '12.4' - # This job needs to install the simulator which can take a couple of minutes - timeout-minutes: 25 # iOS 13.7 - runs-on: macos-11 @@ -167,14 +160,6 @@ jobs: - run: ./scripts/ci-select-xcode.sh ${{matrix.xcode}} - # GH action images don't have an iOS 12.4 simulator. Therefore we have to download and install the simulator manually. - - name: Install iOS 12.4 simulator - if: ${{ matrix.platform == 'iOS' && matrix.test-destination-os == '12.4'}} - run: | - gem install xcode-install - xcversion simulators --install='iOS 12.4' - xcrun simctl create custom-test-device "iPhone 8" "com.apple.CoreSimulator.SimRuntime.iOS-12-4" - # Workaround with a symlink pointed out in: https://github.com/actions/virtual-environments/issues/551#issuecomment-637344435 - name: Prepare iOS 13.7 simulator if: ${{ matrix.platform == 'iOS' && matrix.test-destination-os == '13.7'}} diff --git a/.sauce/config.yml b/.sauce/config.yml index b3374e976f6..0271736022f 100644 --- a/.sauce/config.yml +++ b/.sauce/config.yml @@ -41,7 +41,7 @@ suites: - name: "iOS-12" devices: - name: "iPhone.*" - platformVersion: "12.5.5" + platformVersion: "12.5.7" - name: "iOS-11" devices: diff --git a/Sources/Sentry/SentryNetworkTracker.m b/Sources/Sentry/SentryNetworkTracker.m index d0b0967c300..2df26e0683b 100644 --- a/Sources/Sentry/SentryNetworkTracker.m +++ b/Sources/Sentry/SentryNetworkTracker.m @@ -22,6 +22,15 @@ #import "SentryTracer.h" #import +/** + * WARNING: We had issues in the past with this code on older iOS versions. We don't run unit tests + * on all the iOS versions our SDK supports. When adding this comment on April 12th, 2023, we + * decided to remove running unit tests on iOS 12 simulators. Check the develop-docs decision log + * for more information https://github.com/getsentry/sentry-cocoa/blob/main/develop-docs/README.md. + * Back then, the code worked correctly on all iOS versions. Please evaluate if your changes could + * break on specific iOS versions to ensure it works properly when modifying this file. If they + * could, please add UI tests and run them on older iOS versions. + */ @interface SentryNetworkTracker () diff --git a/develop-docs/README.md b/develop-docs/README.md index d4aca7010c3..4782dd8afd7 100644 --- a/develop-docs/README.md +++ b/develop-docs/README.md @@ -130,11 +130,13 @@ Contributors: @marandaneto, @brustolin and @philipphofmann We decided not to use the `NSRange` type for the `failedRequestStatusCodes` property of the `SentryNetworkTracker` class because it's not compatible with the specification, which requires the type to be a range of `from` -> `to` integers. The `NSRange` type is a range of `location` -> `length` integers. We decided to use a custom type instead of `NSRange` to avoid confusion. The custom type is called `SentryHttpStatusCodeRange`. -### Manually installing iOS 12 simulators +### Manually installing iOS 12 simulators Date: October 21st 2022 Contributors: @philipphofmann +We reverted this decision with [remove running unit tests on iOS 12 simulators](#remove-ios-12-simulators). + GH actions will remove the macOS-10.15 image, which contains an iOS 12 simulator on 12/1/22; see https://github.com/actions/runner-images/issues/5583. Neither the [macOS-11](https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md#installed-sdks) nor the [macOS-12](https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#installed-sdks) image contains an iOS 12 simulator. GH @@ -207,3 +209,19 @@ the PM. You can find this in `SentrySDKInfo.m`. ### Usage of `__has_include` Some private headers add a dependency of a public header, when those private headers are used in a sample project, or referenced from a hybrid SDK, it is treated as part of the project using it, therefore, if it points to a header that is not part of said project, a compilation error will occur. To solve this we make use of `__has_include` to try to point to the SDK version of the header, or to fallback to the direct reference when compiling the SDK. + +### Remove running unit tests on iOS 12 simulators + +Date: April 12th 2023 +Contributors: @philipphofmann + +We use [`xcode-install`](https://github.com/xcpretty/xcode-install) to install some older iOS simulators for test runs [here](https://github.com/getsentry/sentry-cocoa/blob/ff5c1d83bf601bbcd0f5f1070c3abf05310881bd/.github/workflows/test.yml#L174) and [here](https://github.com/getsentry/sentry-cocoa/blob/ff5c1d83bf601bbcd0f5f1070c3abf05310881bd/.github/workflows/test.yml#L343). That project is being sunset, so we would have to find an alternative. + +Installing the simulator can take up to 15 minutes, so the current solution slows CI and sometimes leads to timeouts. +We want our CI to be fast and reliable. Instead of running the unit tests on iOS 12, we run UI tests on an iPhone with iOS 12, +which reduces the risk of breaking users on iOS 12. Our unit tests should primarily focus on business logic and shouldn't depend +on specific iOS versions. If we have functionality that risks breaking on older iOS versions, we should write UI tests instead. +For the swizzling of UIViewControllers and NSURLSession, we have UI tests running on iOS 12. Therefore, dropping running unit +tests on iOS 12 simulators is acceptable. This decision reverts [manually installing iOS 12 simulators](#ios-12-simulators). + +Related to [GH-2862](https://github.com/getsentry/sentry-cocoa/issues/2862) and diff --git a/scripts/no-changes-in-high-risk-files.sh b/scripts/no-changes-in-high-risk-files.sh index 714f165415a..38718a2a98f 100755 --- a/scripts/no-changes-in-high-risk-files.sh +++ b/scripts/no-changes-in-high-risk-files.sh @@ -1,10 +1,11 @@ #!/bin/bash set -euo pipefail -# To update the sha run shasum -a 256 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m and copy the result in EXPECTED. +# To update the sha run the command in ACTUAL and copy the result in EXPECTED. -ACTUAL=$(shasum -a 256 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m) -EXPECTED="819d5ca5e3db2ac23c859b14c149b7f0754d3ae88bea1dba92c18f49a81da0e1 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m" +ACTUAL=$(shasum -a 256 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m ./Sources/Sentry/SentryNetworkTracker.m) +EXPECTED="819d5ca5e3db2ac23c859b14c149b7f0754d3ae88bea1dba92c18f49a81da0e1 ./Sources/Sentry/SentryNSURLSessionTaskSearch.m +58d5414b4f0a4c821b20fc1a16f88bda3116401e905b7bc1d18af828be75e431 ./Sources/Sentry/SentryNetworkTracker.m" if [ "$ACTUAL" = "$EXPECTED" ]; then echo "No changes in high risk files." From 46f5eb8f90236e0b538f5ff1c237e290c12a25e6 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Thu, 13 Apr 2023 12:32:42 +0200 Subject: [PATCH 24/42] Update SentryDependencyContainer.m (#2895) SentryDependencyContainer processInfoWrapper property was wrapped by #if SENTRY_HAS_METRIC_KIT and did not work for tvOS. --- Sources/Sentry/SentryDependencyContainer.m | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/Sentry/SentryDependencyContainer.m b/Sources/Sentry/SentryDependencyContainer.m index 1aa3991f5ba..58b802dcd7e 100644 --- a/Sources/Sentry/SentryDependencyContainer.m +++ b/Sources/Sentry/SentryDependencyContainer.m @@ -213,6 +213,18 @@ - (SentryANRTracker *)getANRTracker:(NSTimeInterval)timeout return _anrTracker; } +- (SentryNSProcessInfoWrapper *)processInfoWrapper +{ + if (_processInfoWrapper == nil) { + @synchronized(sentryDependencyContainerLock) { + if (_processInfoWrapper == nil) { + _processInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; + } + } + } + return _processInfoWrapper; +} + #if SENTRY_HAS_METRIC_KIT - (SentryMXManager *)metricKitManager { @@ -230,18 +242,6 @@ - (SentryMXManager *)metricKitManager return _metricKitManager; } -- (SentryNSProcessInfoWrapper *)processInfoWrapper -{ - if (_processInfoWrapper == nil) { - @synchronized(sentryDependencyContainerLock) { - if (_processInfoWrapper == nil) { - _processInfoWrapper = [[SentryNSProcessInfoWrapper alloc] init]; - } - } - } - return _processInfoWrapper; -} - #endif @end From 630ddf4ab6bcef91fccf4e5ff0735f4b909e6c1b Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 14 Apr 2023 09:21:42 +0200 Subject: [PATCH 25/42] ci: Run unit tests on tvOS 16 and macOS 13 (#2894) --- .github/workflows/test.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4577fe4fc0b..69600cec962 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -123,6 +123,13 @@ jobs: xcode: '13.4.1' test-destination-os: 'latest' timeout-minutes: 15 + + # macOS 13 + - runs-on: macos-12 + platform: 'macOS' + xcode: '14.2' + test-destination-os: 'latest' + timeout-minutes: 15 # Catalyst. We only test the latest version, as # the risk something breaking on Catalyst and not @@ -149,6 +156,13 @@ jobs: test-destination-os: 'latest' timeout-minutes: 15 + # tvOS 16 + - runs-on: macos-12 + platform: 'tvOS' + xcode: '14.2' + test-destination-os: 'latest' + timeout-minutes: 15 + steps: - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 From f8c8a0a488c5bd44c73f6c49683a667368488005 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 14 Apr 2023 09:21:55 +0200 Subject: [PATCH 26/42] ref: Use sample decision instead of bool (#2896) Use sample decision instead of bool in SentrySpanContext. --- Sources/Sentry/SentrySpanContext.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Sentry/SentrySpanContext.m b/Sources/Sentry/SentrySpanContext.m index 42bb43773b0..dbed024ac2d 100644 --- a/Sources/Sentry/SentrySpanContext.m +++ b/Sources/Sentry/SentrySpanContext.m @@ -9,7 +9,7 @@ @implementation SentrySpanContext - (instancetype)initWithOperation:(NSString *)operation { - return [self initWithOperation:operation sampled:NO]; + return [self initWithOperation:operation sampled:kSentrySampleDecisionUndecided]; } - (instancetype)initWithOperation:(NSString *)operation sampled:(SentrySampleDecision)sampled From 3b782cc23698436ec8cd3a225c7c0c94fa040812 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 14 Apr 2023 09:22:31 +0200 Subject: [PATCH 27/42] ref: Remove SentryPerformanceTracker+Private.h (#2897) There is not need for a SentryPerformanceTracker+Private.h as SentryPerformanceTracker.h is internal anyways. --- Sentry.xcodeproj/project.pbxproj | 4 --- Sources/Sentry/SentryPerformanceTracker.m | 29 ------------------- ...SentryUIViewControllerPerformanceTracker.m | 1 - .../SentryPerformanceTracker+Private.h | 21 -------------- .../Sentry/include/SentryPerformanceTracker.h | 9 +++++- .../SentryPerformanceTrackerTests.swift | 12 ++++---- ...iewControllerPerformanceTrackerTests.swift | 2 +- 7 files changed, 15 insertions(+), 63 deletions(-) delete mode 100644 Sources/Sentry/include/SentryPerformanceTracker+Private.h diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 10409b81ad1..05801c75ce2 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -42,7 +42,6 @@ 0A2D8D9828997887008720F6 /* NSLocale+Sentry.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A2D8D9728997887008720F6 /* NSLocale+Sentry.h */; }; 0A2D8DA8289BC905008720F6 /* SentryViewHierarchy.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A2D8DA6289BC905008720F6 /* SentryViewHierarchy.h */; }; 0A2D8DA9289BC905008720F6 /* SentryViewHierarchy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0A2D8DA7289BC905008720F6 /* SentryViewHierarchy.m */; }; - 0A4EDEA928D3461B00FA67CB /* SentryPerformanceTracker+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A4EDEA828D3461B00FA67CB /* SentryPerformanceTracker+Private.h */; }; 0A5370A128A3EC2400B2DCDE /* SentryViewHierarchyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5370A028A3EC2400B2DCDE /* SentryViewHierarchyTests.swift */; }; 0A56DA5F28ABA01B00C400D5 /* SentryTransactionContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A56DA5E28ABA01B00C400D5 /* SentryTransactionContext+Private.h */; }; 0A6EEADD28A657970076B469 /* UIViewRecursiveDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6EEADC28A657970076B469 /* UIViewRecursiveDescriptionTests.swift */; }; @@ -889,7 +888,6 @@ 0A2D8D9728997887008720F6 /* NSLocale+Sentry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSLocale+Sentry.h"; path = "include/NSLocale+Sentry.h"; sourceTree = ""; }; 0A2D8DA6289BC905008720F6 /* SentryViewHierarchy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryViewHierarchy.h; path = include/SentryViewHierarchy.h; sourceTree = ""; }; 0A2D8DA7289BC905008720F6 /* SentryViewHierarchy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryViewHierarchy.m; sourceTree = ""; }; - 0A4EDEA828D3461B00FA67CB /* SentryPerformanceTracker+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryPerformanceTracker+Private.h"; path = "include/SentryPerformanceTracker+Private.h"; sourceTree = ""; }; 0A5370A028A3EC2400B2DCDE /* SentryViewHierarchyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryViewHierarchyTests.swift; sourceTree = ""; }; 0A56DA5E28ABA01B00C400D5 /* SentryTransactionContext+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryTransactionContext+Private.h"; path = "include/SentryTransactionContext+Private.h"; sourceTree = ""; }; 0A6EEADC28A657970076B469 /* UIViewRecursiveDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewRecursiveDescriptionTests.swift; sourceTree = ""; }; @@ -2834,7 +2832,6 @@ 8EAE9804261E87120073B6B3 /* SentryUIViewControllerPerformanceTracker.m */, 8EAE9809261E9F530073B6B3 /* SentryPerformanceTracker.h */, 8EBF870726140D37001A6853 /* SentryPerformanceTracker.m */, - 0A4EDEA828D3461B00FA67CB /* SentryPerformanceTracker+Private.h */, D8BFE37029A3782F002E73F3 /* SentryTimeToDisplayTracker.h */, D8BFE37129A3782F002E73F3 /* SentryTimeToDisplayTracker.m */, ); @@ -3380,7 +3377,6 @@ 63AA76991EB9C1C200D153DE /* SentryDefines.h in Headers */, D86B6835294348A400B8B1FC /* SentryAttachment+Private.h in Headers */, 84AF45A629A7FFA500FBB177 /* SentryTracerConcurrency.h in Headers */, - 0A4EDEA928D3461B00FA67CB /* SentryPerformanceTracker+Private.h in Headers */, 7B2A70DB27D607CF008B0D15 /* SentryThreadWrapper.h in Headers */, 8EAE980B261E9F530073B6B3 /* SentryPerformanceTracker.h in Headers */, 63FE718520DA4C1100CDBAE8 /* SentryCrashC.h in Headers */, diff --git a/Sources/Sentry/SentryPerformanceTracker.m b/Sources/Sentry/SentryPerformanceTracker.m index 671e91a3486..9c049b6f751 100644 --- a/Sources/Sentry/SentryPerformanceTracker.m +++ b/Sources/Sentry/SentryPerformanceTracker.m @@ -39,13 +39,6 @@ - (instancetype)init return self; } -- (SentrySpanId *)startSpanWithName:(NSString *)name operation:(NSString *)operation -{ - return [self startSpanWithName:name - nameSource:kSentryTransactionNameSourceCustom - operation:operation]; -} - - (SentrySpanId *)startSpanWithName:(NSString *)name nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation @@ -107,16 +100,6 @@ - (SentrySpanId *)startSpanWithName:(NSString *)name return spanId; } -- (void)measureSpanWithDescription:(NSString *)description - operation:(NSString *)operation - inBlock:(void (^)(void))block -{ - [self measureSpanWithDescription:description - nameSource:kSentryTransactionNameSourceCustom - operation:operation - inBlock:block]; -} - - (void)measureSpanWithDescription:(NSString *)description nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation @@ -133,18 +116,6 @@ - (void)measureSpanWithDescription:(NSString *)description [self finishSpan:spanId]; } -- (void)measureSpanWithDescription:(NSString *)description - operation:(NSString *)operation - parentSpanId:(SentrySpanId *)parentSpanId - inBlock:(void (^)(void))block -{ - [self measureSpanWithDescription:description - nameSource:kSentryTransactionNameSourceCustom - operation:operation - parentSpanId:parentSpanId - inBlock:block]; -} - - (void)measureSpanWithDescription:(NSString *)description nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation diff --git a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m index ebc6d09126a..2d2ee94ae39 100644 --- a/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m +++ b/Sources/Sentry/SentryUIViewControllerPerformanceTracker.m @@ -2,7 +2,6 @@ #import "SentryFramesTracker.h" #import "SentryHub.h" #import "SentryLog.h" -#import "SentryPerformanceTracker+Private.h" #import "SentryPerformanceTracker.h" #import "SentrySDK+Private.h" #import "SentryScope.h" diff --git a/Sources/Sentry/include/SentryPerformanceTracker+Private.h b/Sources/Sentry/include/SentryPerformanceTracker+Private.h deleted file mode 100644 index d58ef221026..00000000000 --- a/Sources/Sentry/include/SentryPerformanceTracker+Private.h +++ /dev/null @@ -1,21 +0,0 @@ -#import "SentryPerformanceTracker.h" - -@interface -SentryPerformanceTracker (Private) - -- (SentrySpanId *)startSpanWithName:(NSString *)name - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation; - -- (void)measureSpanWithDescription:(NSString *)description - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - inBlock:(void (^)(void))block; - -- (void)measureSpanWithDescription:(NSString *)description - nameSource:(SentryTransactionNameSource)source - operation:(NSString *)operation - parentSpanId:(SentrySpanId *)parentSpanId - inBlock:(void (^)(void))block; - -@end diff --git a/Sources/Sentry/include/SentryPerformanceTracker.h b/Sources/Sentry/include/SentryPerformanceTracker.h index 6f43d141d0b..e187521e0f3 100644 --- a/Sources/Sentry/include/SentryPerformanceTracker.h +++ b/Sources/Sentry/include/SentryPerformanceTracker.h @@ -21,10 +21,13 @@ NS_ASSUME_NONNULL_BEGIN * Starts a new span if no span is active, then bind it to the scope if no span is bound. * @note If there's an active span, starts a child of the active span. * @param name Span name. + * @param source the transaction name source. * @param operation Span operation. * @return The span id. */ -- (SentrySpanId *)startSpanWithName:(NSString *)name operation:(NSString *)operation; +- (SentrySpanId *)startSpanWithName:(NSString *)name + nameSource:(SentryTransactionNameSource)source + operation:(NSString *)operation; /** * Activate the span with @c spanId to create any call to @c startSpan as a child. @@ -37,10 +40,12 @@ NS_ASSUME_NONNULL_BEGIN /** * Measure the given @c block execution. * @param description The description of the span. + * @param source the transaction name source. * @param operation Span operation. * @param block Block to be measured. */ - (void)measureSpanWithDescription:(NSString *)description + nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation inBlock:(void (^)(void))block; @@ -48,11 +53,13 @@ NS_ASSUME_NONNULL_BEGIN * Measure the given @c block execution adding it as a child of given parent span. * @note If @c parentSpanId does not exist this measurement is not performed. * @param description The description of the span. + * @param source the transaction name source. * @param operation Span operation. * @param parentSpanId Id of the span to use as parent. * @param block Block to be measured. */ - (void)measureSpanWithDescription:(NSString *)description + nameSource:(SentryTransactionNameSource)source operation:(NSString *)operation parentSpanId:(SentrySpanId *)parentSpanId inBlock:(void (^)(void))block; diff --git a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift index 5df7d37db68..f4753aa6b46 100644 --- a/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/SentryPerformanceTrackerTests.swift @@ -155,7 +155,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let expect = expectation(description: "Callback Expectation") - sut.measureSpan(withDescription: fixture.someTransaction, operation: fixture.someOperation) { + sut.measureSpan(withDescription: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) { let spanId = sut.activeSpanId()! span = sut.getSpan(spanId) @@ -175,7 +175,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let expect = expectation(description: "Callback Expectation") - sut.measureSpan(withDescription: fixture.someTransaction, operation: fixture.someOperation, parentSpanId: SpanId()) { + sut.measureSpan(withDescription: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation, parentSpanId: SpanId()) { expect.fulfill() } @@ -186,7 +186,7 @@ class SentryPerformanceTrackerTests: XCTestCase { func testNotSampled() { fixture.client.options.tracesSampleRate = 0 let sut = fixture.getSut() - let spanId = sut.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + let spanId = sut.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) let span = sut.getSpan(spanId) XCTAssertEqual(span!.sampled, .no) @@ -195,7 +195,7 @@ class SentryPerformanceTrackerTests: XCTestCase { func testSampled() { fixture.client.options.tracesSampleRate = 1 let sut = fixture.getSut() - let spanId = sut.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + let spanId = sut.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) let span = sut.getSpan(spanId) XCTAssertEqual(span!.sampled, .yes) @@ -273,7 +273,7 @@ class SentryPerformanceTrackerTests: XCTestCase { let activeSpans = Dynamic(sut).activeSpanStack as NSMutableArray? activeSpans?.add(TestSentrySpan()) - let spanId = sut.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + let spanId = sut.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) XCTAssertEqual(spanId, SpanId.empty) } @@ -342,7 +342,7 @@ class SentryPerformanceTrackerTests: XCTestCase { } private func startSpan(tracker: SentryPerformanceTracker) -> SpanId { - return tracker.startSpan(withName: fixture.someTransaction, operation: fixture.someOperation) + return tracker.startSpan(withName: fixture.someTransaction, nameSource: .custom, operation: fixture.someOperation) } } diff --git a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift index dc62f9ad7d1..919e6b76ed7 100644 --- a/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/UIViewController/SentryUIViewControllerPerformanceTrackerTests.swift @@ -350,7 +350,7 @@ class SentryUIViewControllerPerformanceTrackerTests: XCTestCase { sut.viewControllerLoadView(viewController) { transactionSpan = self.getStack(tracker).first lastSpan = self.getStack(tracker).last - customSpanId = tracker.startSpan(withName: self.spanName, operation: self.spanOperation) + customSpanId = tracker.startSpan(withName: self.spanName, nameSource: .custom, operation: self.spanOperation) } let unwrappedLastSpan = try XCTUnwrap(lastSpan) XCTAssertTrue(unwrappedLastSpan.isFinished) From 211f63be1bcb4696cb4bd4f8300c49262bf897b3 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 14 Apr 2023 11:19:12 +0200 Subject: [PATCH 28/42] test: Make TestLogOutput thread safe (#2901) When running the tests locally, I got a SIGABRT in the TestLogOutput when writing to the loggedMessages. I added a test to reproduce the same error by writing and reading the loggedMessages from different threads. The problem is fixed now by using a dispatch queue. --- Sentry.xcodeproj/project.pbxproj | 4 +++ Tests/SentryTests/SentryScopeSwiftTests.swift | 25 ++++------------ Tests/SentryTests/TestLogOutput.swift | 30 +++++++++++++++++-- .../TestConncurrentModifications.swift | 23 ++++++++++++++ 4 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 Tests/SentryTests/TestUtils/TestConncurrentModifications.swift diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 05801c75ce2..a69faec9dfa 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -79,6 +79,7 @@ 33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */; }; 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */; }; 627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 627E7588299F6FE40085504D /* SentryInternalDefines.h */; }; + 62885DA729E946B100554F38 /* TestConncurrentModifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */; }; 62F226B729A37C120038080D /* SentryBooleanSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F226B629A37C120038080D /* SentryBooleanSerialization.m */; }; 630435FE1EBCA9D900C4D3FA /* SentryNSURLRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */; }; 630435FF1EBCA9D900C4D3FA /* SentryNSURLRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 630435FD1EBCA9D900C4D3FA /* SentryNSURLRequest.m */; }; @@ -926,6 +927,7 @@ 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryExtraContextProvider.m; sourceTree = ""; }; 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraContextProviderTests.swift; sourceTree = ""; }; 627E7588299F6FE40085504D /* SentryInternalDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalDefines.h; path = include/SentryInternalDefines.h; sourceTree = ""; }; + 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConncurrentModifications.swift; sourceTree = ""; }; 62F226B629A37C120038080D /* SentryBooleanSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBooleanSerialization.m; sourceTree = ""; }; 62F226B829A37C270038080D /* SentryBooleanSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryBooleanSerialization.h; sourceTree = ""; }; 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSURLRequest.h; path = include/SentryNSURLRequest.h; sourceTree = ""; }; @@ -2963,6 +2965,7 @@ 7BF536D224BEF240004FA6A2 /* TestUtils */ = { isa = PBXGroup; children = ( + 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */, 7BF536D324BEF255004FA6A2 /* SentryAssertions.swift */, 7B6D98EC24C703F8005502FA /* Async.swift */, 7BF9EF712722A84800B5BBEF /* SentryClassRegistrator.h */, @@ -4123,6 +4126,7 @@ 63FE721F20DA66EC00CDBAE8 /* SentryCrashSignalInfo_Tests.m in Sources */, 0ADC33F128D9BE940078D980 /* TestSentryUIDeviceWrapper.swift in Sources */, 63FE721420DA66EC00CDBAE8 /* SentryCrashMemory_Tests.m in Sources */, + 62885DA729E946B100554F38 /* TestConncurrentModifications.swift in Sources */, 63FE720520DA66EC00CDBAE8 /* FileBasedTestCase.m in Sources */, 0A6EEADD28A657970076B469 /* UIViewRecursiveDescriptionTests.swift in Sources */, 63EED6C32237989300E02400 /* SentryOptionsTest.m in Sources */, diff --git a/Tests/SentryTests/SentryScopeSwiftTests.swift b/Tests/SentryTests/SentryScopeSwiftTests.swift index 74975f08951..80067fce97a 100644 --- a/Tests/SentryTests/SentryScopeSwiftTests.swift +++ b/Tests/SentryTests/SentryScopeSwiftTests.swift @@ -317,28 +317,13 @@ class SentryScopeSwiftTests: XCTestCase { // With this test we test if modifications from multiple threads don't lead to a crash. func testModifyingFromMultipleThreads() { - let queue = DispatchQueue(label: "SentryScopeTests", qos: .userInteractive, attributes: [.concurrent, .initiallyInactive]) - let group = DispatchGroup() - let scope = fixture.scope - for _ in 0...2 { - group.enter() - queue.async { - - // The number is kept small for the CI to not take too long. - // If you really want to test this increase to 100_000 or so. - for _ in 0...10 { - // Simulate some real world modifications of the user - self.modifyScope(scope: scope) - } - - group.leave() - } - } - - queue.activate() - group.waitWithTimeout(timeout: 500) + // The number is kept small for the CI to not take too long. + // If you really want to test this increase to 100_000 or so. + testConcurrentModifications(asyncWorkItems: 2, writeLoopCount: 10, writeWork: { _ in + self.modifyScope(scope: scope) + }) } func testScopeObserver_setUser() { diff --git a/Tests/SentryTests/TestLogOutput.swift b/Tests/SentryTests/TestLogOutput.swift index af9c6e1777c..4e61843805e 100644 --- a/Tests/SentryTests/TestLogOutput.swift +++ b/Tests/SentryTests/TestLogOutput.swift @@ -1,9 +1,35 @@ import Foundation class TestLogOutput: SentryLogOutput { - var loggedMessages: [String] = [] + + private let queue = DispatchQueue(label: "TestLogOutput", attributes: .concurrent) + + private var _loggedMessages: [String] = [] + + var loggedMessages: [String] { + get { + queue.sync { + return _loggedMessages + } + } + } + override func log(_ message: String) { super.log(message) - loggedMessages.append(message) + queue.async(flags: .barrier) { + self._loggedMessages.append(message) + } + } +} + +class TestLogOutPutTests: XCTestCase { + + func testLoggingFromMulitpleThreads() { + let sut = TestLogOutput() + testConcurrentModifications(writeWork: { i in + sut.log("Some message \(i)") + }, readWork: { + XCTAssertNotNil(sut.loggedMessages) + }) } } diff --git a/Tests/SentryTests/TestUtils/TestConncurrentModifications.swift b/Tests/SentryTests/TestUtils/TestConncurrentModifications.swift new file mode 100644 index 00000000000..d0dff19460e --- /dev/null +++ b/Tests/SentryTests/TestUtils/TestConncurrentModifications.swift @@ -0,0 +1,23 @@ +import Foundation + +func testConcurrentModifications(asyncWorkItems: Int = 5, writeLoopCount: Int = 1_000, writeWork: @escaping (Int) -> Void, readWork: @escaping () -> Void = {}) { + let queue = DispatchQueue(label: "testConcurrentModifications", qos: .userInteractive, attributes: [.concurrent, .initiallyInactive]) + let group = DispatchGroup() + + for _ in 0.. Date: Fri, 14 Apr 2023 11:19:38 +0200 Subject: [PATCH 29/42] test: Fix failing FileIOTracking tests (#2902) When running the SentryFileIOTrackingIntegrationObjCTests locally they failed. This is fixed now by using the SentryByteCountFormatter instead of the NSByteCountFormatter. --- .../IO/SentryFileIOTrackingIntegrationObjCTests.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m b/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m index faa185dcd2a..cd88dd47457 100644 --- a/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m +++ b/Tests/SentryTests/Integrations/Performance/IO/SentryFileIOTrackingIntegrationObjCTests.m @@ -1,3 +1,4 @@ +#import "SentryByteCountFormatter.h" #import "SentryNSDataTracker.h" #import "SentryOptions.h" #import "SentrySDK.h" @@ -218,10 +219,9 @@ - (void)assertTransactionForOperation:(NSString *)operation block:(void (^)(void if ([operation isEqualToString:SENTRY_FILE_READ_OPERATION]) { XCTAssertEqualObjects(ioSpan.spanDescription, filename); } else { - NSString *expectedString = [NSString - stringWithFormat:@"%@ (%@)", filename, - [NSByteCountFormatter stringFromByteCount:someData.length - countStyle:NSByteCountFormatterCountStyleBinary]]; + NSString *expectedString = + [NSString stringWithFormat:@"%@ (%@)", filename, + [SentryByteCountFormatter bytesCountDescription:someData.length]]; XCTAssertEqualObjects(ioSpan.spanDescription, expectedString); } From 65b8953bbdb7179aab1d909b2ee7cc3b7c801fad Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 14 Apr 2023 11:19:59 +0200 Subject: [PATCH 30/42] test: Fix failing NSDataTracker tests (#2903) When running the SentryNSDataTrackerTests locally they failed. This is fixed now by using the SentryByteCountFormatter instead of the NSByteCountFormatter. --- .../Integrations/Performance/IO/SentryNSDataTrackerTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift index 892f1ec6ebc..1702e725aad 100644 --- a/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/IO/SentryNSDataTrackerTests.swift @@ -316,7 +316,7 @@ class SentryNSDataTrackerTests: XCTestCase { if operation == SENTRY_FILE_READ_OPERATION { XCTAssertEqual(span?.spanDescription, lastComponent) } else { - let bytesDescription = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .binary) + let bytesDescription = SentryByteCountFormatter.bytesCountDescription( UInt(size)) XCTAssertEqual(span?.spanDescription ?? "", "\(lastComponent) (\(bytesDescription))") } } From 8c50edbfdb94d6de171a52917ed9e69144465cf5 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 14 Apr 2023 11:20:22 +0200 Subject: [PATCH 31/42] test: Add missing test for transaction name (#2900) --- Sources/Sentry/SentryTransaction.m | 2 +- .../SentryTests/Transaction/SentryTransactionTests.swift | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Sources/Sentry/SentryTransaction.m b/Sources/Sentry/SentryTransaction.m index b17eba33d9d..bf1ef01fbed 100644 --- a/Sources/Sentry/SentryTransaction.m +++ b/Sources/Sentry/SentryTransaction.m @@ -85,7 +85,7 @@ - (instancetype)initWithTrace:(SentryTracer *)trace children:(NSArray Date: Fri, 14 Apr 2023 13:32:33 +0200 Subject: [PATCH 32/42] test: Remove subclass of NSTimer (#2904) We subclassed NSTimer, which is not allowed see: https://developer.apple.com/documentation/foundation/nstimer#1770465. This sometimes leads to crashes in TestSentryNSTimerWrapper with EXC_BAD_ACCESS. The subclass is now removed. --- .../TestSentryNSTimerWrapper.swift | 31 ++++++++----------- .../Transaction/SentryTracerTests.swift | 2 +- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/SentryTestUtils/TestSentryNSTimerWrapper.swift b/SentryTestUtils/TestSentryNSTimerWrapper.swift index 5def3be65f8..2969151dd85 100644 --- a/SentryTestUtils/TestSentryNSTimerWrapper.swift +++ b/SentryTestUtils/TestSentryNSTimerWrapper.swift @@ -1,31 +1,26 @@ import Foundation import Sentry -public class TestTimer: Timer { - public var invalidateCount = 0 - - public override func invalidate() { - // no-op as this timer doesn't actually schedule anything on a runloop - invalidateCount += 1 - } -} - +// We must not subclass NSTimer, see https://developer.apple.com/documentation/foundation/nstimer#1770465. +// Therefore we return a NSTimer instance here with TimeInterval.infinity. public class TestSentryNSTimerWrapper: SentryNSTimerWrapper { - public struct Overrides { - public var timer: TestTimer! - var block: ((Timer) -> Void)? - } - public var overrides = Overrides() + private var _timer: Timer? + + public var timer: Timer { + get { + _timer ?? Timer() + } + } public override func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void) -> Timer { - let timer = TestTimer() - overrides.timer = timer - overrides.block = block + let timer = Timer.scheduledTimer(withTimeInterval: TimeInterval.infinity, repeats: repeats, block: block) + _timer = timer + return timer } public func fire() { - overrides.block?(overrides.timer) + _timer?.fire() } } diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index f27f3da33b3..acf4365b92d 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -209,7 +209,7 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.finish() - XCTAssertEqual(fixture.timerWrapper.overrides.timer.invalidateCount, 1) + XCTAssertFalse(fixture.timerWrapper.timer.isValid) } func testFinish_CheckDefaultStatus() { From 3e7aa41e5f1f370d6252b3ffec0ddf359fb684d3 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Mon, 17 Apr 2023 13:04:17 +0200 Subject: [PATCH 33/42] test: Remove noisy logs for TestLogTests (#2909) Don't call super when running testLoggingFromMulitpleThreads to avoid spamming the test logs. --- Tests/SentryTests/TestLogOutput.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tests/SentryTests/TestLogOutput.swift b/Tests/SentryTests/TestLogOutput.swift index 4e61843805e..c5cbc2b8d12 100644 --- a/Tests/SentryTests/TestLogOutput.swift +++ b/Tests/SentryTests/TestLogOutput.swift @@ -6,6 +6,8 @@ class TestLogOutput: SentryLogOutput { private var _loggedMessages: [String] = [] + var callSuperWhenLogging = true + var loggedMessages: [String] { get { queue.sync { @@ -15,7 +17,9 @@ class TestLogOutput: SentryLogOutput { } override func log(_ message: String) { - super.log(message) + if callSuperWhenLogging { + super.log(message) + } queue.async(flags: .barrier) { self._loggedMessages.append(message) } @@ -26,6 +30,7 @@ class TestLogOutPutTests: XCTestCase { func testLoggingFromMulitpleThreads() { let sut = TestLogOutput() + sut.callSuperWhenLogging = false testConcurrentModifications(writeWork: { i in sut.log("Some message \(i)") }, readWork: { From be51b56f07a0289bee9d8145dcdbaa33e4466f34 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Mon, 17 Apr 2023 13:04:31 +0200 Subject: [PATCH 34/42] ref: Simplify Tracer finish logging (#2910) The SentryTracer.finish and SentryTracer.finishWithStatus both logged similar debug messages, which is not required as SentryTracer.finish calls SentryTracer.finishWithStatus. --- Sources/Sentry/SentryTracer.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/Sentry/SentryTracer.m b/Sources/Sentry/SentryTracer.m index 31da399df8f..3abd7708c43 100644 --- a/Sources/Sentry/SentryTracer.m +++ b/Sources/Sentry/SentryTracer.m @@ -359,14 +359,13 @@ - (void)setMeasurement:(NSString *)name value:(NSNumber *)value unit:(SentryMeas - (void)finish { - SENTRY_LOG_DEBUG( - @"-[SentryTracer finish] for trace ID %@", _traceContext.traceId.sentryIdString); [self finishWithStatus:kSentrySpanStatusOk]; } - (void)finishWithStatus:(SentrySpanStatus)status { - SENTRY_LOG_DEBUG(@"Finished trace %@", self.traceContext.traceId.sentryIdString); + SENTRY_LOG_DEBUG(@"Finished trace with traceID: %@ and status: %@", self.traceId.sentryIdString, + nameForSentrySpanStatus(status)); self.wasFinishCalled = YES; _finishStatus = status; [self canBeFinished]; From d889d5661fdfd711439bebb678b4ff1e8da20304 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Mon, 17 Apr 2023 13:04:42 +0200 Subject: [PATCH 35/42] ref: Add some log messages when closing the SDK (#2912) Add some extra log messages when closing the SDK in the client, hub, and SDK. --- Sources/Sentry/SentryClient.m | 1 + Sources/Sentry/SentryHub.m | 1 + Sources/Sentry/SentrySDK.m | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Sentry/SentryClient.m b/Sources/Sentry/SentryClient.m index 663f4c22920..4281f5b9d3a 100644 --- a/Sources/Sentry/SentryClient.m +++ b/Sources/Sentry/SentryClient.m @@ -492,6 +492,7 @@ - (void)close { _isEnabled = NO; [self flush:self.options.shutdownTimeInterval]; + SENTRY_LOG_DEBUG(@"Closed the Client."); } - (SentryEvent *_Nullable)prepareEvent:(SentryEvent *)event diff --git a/Sources/Sentry/SentryHub.m b/Sources/Sentry/SentryHub.m index 39dde8ab0e2..026c223db32 100644 --- a/Sources/Sentry/SentryHub.m +++ b/Sources/Sentry/SentryHub.m @@ -640,6 +640,7 @@ - (void)flush:(NSTimeInterval)timeout - (void)close { [_client close]; + SENTRY_LOG_DEBUG(@"Closed the Hub."); } @end diff --git a/Sources/Sentry/SentrySDK.m b/Sources/Sentry/SentrySDK.m index 3c56c17d734..2be5d6fa68e 100644 --- a/Sources/Sentry/SentrySDK.m +++ b/Sources/Sentry/SentrySDK.m @@ -375,16 +375,18 @@ + (void)flush:(NSTimeInterval)timeout */ + (void)close { + SENTRY_LOG_DEBUG(@"Starting to close SDK."); // pop the hub and unset SentryHub *hub = SentrySDK.currentHub; - // uninstall all the integrations + // Uninstall all the integrations for (NSObject *integration in hub.installedIntegrations) { if ([integration respondsToSelector:@selector(uninstall)]) { [integration uninstall]; } } [hub removeAllIntegrations]; + SENTRY_LOG_DEBUG(@"Uninstalled all integrations."); #if SENTRY_HAS_UIKIT // force the AppStateManager to unsubscribe, see From c50d363109c0b313da20428b38c31e2c740eff77 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Mon, 17 Apr 2023 13:05:10 +0200 Subject: [PATCH 36/42] ref: Rename SentryBreadcrumbDelegate (#2913) Rename SentrySystemEventBreadcrumbsDelegate to SentryBreadcrumbDelegate so we can reuse it for SentryBreadcrumbTracker. --- Sentry.xcodeproj/project.pbxproj | 8 ++++++++ Sources/Sentry/SentrySystemEventBreadcrumbs.m | 7 ++++--- .../SentryAutoBreadcrumbTrackingIntegration.h | 4 ++-- Sources/Sentry/include/SentryBreadcrumbDelegate.h | 11 +++++++++++ .../Sentry/include/SentrySystemEventBreadcrumbs.h | 12 +++--------- ...entryAutoBreadcrumbTrackingIntegrationTests.swift | 4 ++-- .../Breadcrumbs/SentryBreadcrumbTestDelegate.swift | 10 ++++++++++ .../SentrySystemEventBreadcrumbsTest.swift | 10 +--------- Tests/SentryTests/SentryTests-Bridging-Header.h | 1 + 9 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 Sources/Sentry/include/SentryBreadcrumbDelegate.h create mode 100644 Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index a69faec9dfa..5e48b9218e0 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -80,6 +80,8 @@ 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */; }; 627E7589299F6FE40085504D /* SentryInternalDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 627E7588299F6FE40085504D /* SentryInternalDefines.h */; }; 62885DA729E946B100554F38 /* TestConncurrentModifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */; }; + 62E081A929ED4260000F69FC /* SentryBreadcrumbDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E081A829ED4260000F69FC /* SentryBreadcrumbDelegate.h */; }; + 62E081AB29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E081AA29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift */; }; 62F226B729A37C120038080D /* SentryBooleanSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F226B629A37C120038080D /* SentryBooleanSerialization.m */; }; 630435FE1EBCA9D900C4D3FA /* SentryNSURLRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */; }; 630435FF1EBCA9D900C4D3FA /* SentryNSURLRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 630435FD1EBCA9D900C4D3FA /* SentryNSURLRequest.m */; }; @@ -928,6 +930,8 @@ 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraContextProviderTests.swift; sourceTree = ""; }; 627E7588299F6FE40085504D /* SentryInternalDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalDefines.h; path = include/SentryInternalDefines.h; sourceTree = ""; }; 62885DA629E946B100554F38 /* TestConncurrentModifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConncurrentModifications.swift; sourceTree = ""; }; + 62E081A829ED4260000F69FC /* SentryBreadcrumbDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryBreadcrumbDelegate.h; path = include/SentryBreadcrumbDelegate.h; sourceTree = ""; }; + 62E081AA29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryBreadcrumbTestDelegate.swift; sourceTree = ""; }; 62F226B629A37C120038080D /* SentryBooleanSerialization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryBooleanSerialization.m; sourceTree = ""; }; 62F226B829A37C270038080D /* SentryBooleanSerialization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryBooleanSerialization.h; sourceTree = ""; }; 630435FC1EBCA9D900C4D3FA /* SentryNSURLRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryNSURLRequest.h; path = include/SentryNSURLRequest.h; sourceTree = ""; }; @@ -2734,6 +2738,7 @@ A839D89924864BA8003B7AFD /* SentrySystemEventBreadcrumbs.m */, 639889B51EDECFA800EA7442 /* SentryBreadcrumbTracker.h */, 639889B61EDECFA800EA7442 /* SentryBreadcrumbTracker.m */, + 62E081A829ED4260000F69FC /* SentryBreadcrumbDelegate.h */, ); name = Breadcrumbs; sourceTree = ""; @@ -2863,6 +2868,7 @@ 7BE0DC30272ABCEC004FA8B7 /* SentryAutoBreadcrumbTrackingIntegration+Test.h */, 7BE0DC28272A9E1C004FA8B7 /* SentryBreadcrumbTrackerTests.swift */, A811D866248E2770008A41EA /* SentrySystemEventBreadcrumbsTest.swift */, + 62E081AA29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift */, ); path = Breadcrumbs; sourceTree = ""; @@ -3526,6 +3532,7 @@ 7B3B83722833832B0001FDEB /* SentrySpanOperations.h in Headers */, 7BF9EF722722A84800B5BBEF /* SentryClassRegistrator.h in Headers */, 63FE715520DA4C1100CDBAE8 /* SentryCrashStackCursor_MachineContext.h in Headers */, + 62E081A929ED4260000F69FC /* SentryBreadcrumbDelegate.h in Headers */, 15360CF02433A16D00112302 /* SentryInstallation.h in Headers */, 63FE714720DA4C1100CDBAE8 /* SentryCrashMachineContext.h in Headers */, 7BA61CAB247BA98100C130A8 /* SentryDebugImageProvider.h in Headers */, @@ -4139,6 +4146,7 @@ 7BE912AF272166DD00E49E62 /* SentryNoOpSpanTests.swift in Sources */, 7B56D73524616E5600B842DA /* SentryConcurrentRateLimitsDictionaryTests.swift in Sources */, 7B7D8730248648AD00D2ECFF /* SentryStacktraceBuilderTests.swift in Sources */, + 62E081AB29ED4322000F69FC /* SentryBreadcrumbTestDelegate.swift in Sources */, D8751FA5274743710032F4DE /* SentryNSURLSessionTaskSearchTests.swift in Sources */, D86F419827C8FEFA00490520 /* SentryCoreDataMiddleware+Extension.swift in Sources */, 7B944FB32469C02900A10721 /* TestHub.swift in Sources */, diff --git a/Sources/Sentry/SentrySystemEventBreadcrumbs.m b/Sources/Sentry/SentrySystemEventBreadcrumbs.m index e2fac3d4f12..656c1ec881b 100644 --- a/Sources/Sentry/SentrySystemEventBreadcrumbs.m +++ b/Sources/Sentry/SentrySystemEventBreadcrumbs.m @@ -1,5 +1,6 @@ #import "SentrySystemEventBreadcrumbs.h" #import "SentryBreadcrumb.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryCurrentDateProvider.h" #import "SentryDependencyContainer.h" #import "SentryLog.h" @@ -12,7 +13,7 @@ @interface SentrySystemEventBreadcrumbs () -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id delegate; @property (nonatomic, strong) SentryFileManager *fileManager; @property (nonatomic, strong) id currentDateProvider; @property (nonatomic, strong) SentryNSNotificationCenterWrapper *notificationCenterWrapper; @@ -32,7 +33,7 @@ - (instancetype)initWithFileManager:(SentryFileManager *)fileManager return self; } -- (void)startWithDelegate:(id)delegate +- (void)startWithDelegate:(id)delegate { #if TARGET_OS_IOS UIDevice *currentDevice = [UIDevice currentDevice]; @@ -73,7 +74,7 @@ - (void)dealloc /** * Only used for testing, call startWithDelegate instead. */ -- (void)startWithDelegate:(id)delegate +- (void)startWithDelegate:(id)delegate currentDevice:(nullable UIDevice *)currentDevice { _delegate = delegate; diff --git a/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h b/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h index 7c2ca028161..bd8e8819608 100644 --- a/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h +++ b/Sources/Sentry/include/SentryAutoBreadcrumbTrackingIntegration.h @@ -1,6 +1,6 @@ #import "SentryBaseIntegration.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryIntegrationProtocol.h" -#import "SentrySystemEventBreadcrumbs.h" NS_ASSUME_NONNULL_BEGIN @@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN * This automatically adds breadcrumbs for different user actions. */ @interface SentryAutoBreadcrumbTrackingIntegration - : SentryBaseIntegration + : SentryBaseIntegration @end diff --git a/Sources/Sentry/include/SentryBreadcrumbDelegate.h b/Sources/Sentry/include/SentryBreadcrumbDelegate.h new file mode 100644 index 00000000000..b5db2045a03 --- /dev/null +++ b/Sources/Sentry/include/SentryBreadcrumbDelegate.h @@ -0,0 +1,11 @@ +#import "SentryInternalDefines.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol SentryBreadcrumbDelegate + +- (void)addBreadcrumb:(SentryBreadcrumb *)crumb; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h b/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h index 76d14a1ccf2..39f8aecd309 100644 --- a/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h +++ b/Sources/Sentry/include/SentrySystemEventBreadcrumbs.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @class SentryNSNotificationCenterWrapper; -@protocol SentrySystemEventBreadcrumbsDelegate; +@protocol SentryBreadcrumbDelegate; @interface SentrySystemEventBreadcrumbs : NSObject SENTRY_NO_INIT @@ -19,10 +19,10 @@ SENTRY_NO_INIT andCurrentDateProvider:(id)currentDateProvider andNotificationCenterWrapper:(SentryNSNotificationCenterWrapper *)notificationCenterWrapper; -- (void)startWithDelegate:(id)delegate; +- (void)startWithDelegate:(id)delegate; #if TARGET_OS_IOS -- (void)startWithDelegate:(id)delegate +- (void)startWithDelegate:(id)delegate currentDevice:(nullable UIDevice *)currentDevice; - (void)timezoneEventTriggered; #endif @@ -31,10 +31,4 @@ SENTRY_NO_INIT @end -@protocol SentrySystemEventBreadcrumbsDelegate - -- (void)addBreadcrumb:(SentryBreadcrumb *)crumb; - -@end - NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift index 1fd70d82f2f..d54c7700988 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift @@ -107,8 +107,8 @@ private class SentryTestBreadcrumbTracker: SentryBreadcrumbTracker { private class SentryTestSystemEventBreadcrumbs: SentrySystemEventBreadcrumbs { - let startWithdelegateInvocations = Invocations() - override func start(with delegate: SentrySystemEventBreadcrumbsDelegate) { + let startWithdelegateInvocations = Invocations() + override func start(with delegate: SentryBreadcrumbDelegate) { startWithdelegateInvocations.record(delegate) } } diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift new file mode 100644 index 00000000000..50325fa2834 --- /dev/null +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTestDelegate.swift @@ -0,0 +1,10 @@ +import Foundation +import SentryTestUtils + +class SentryBreadcrumbTestDelegate: NSObject, SentryBreadcrumbDelegate { + + var addCrumbInvocations = Invocations() + func add(_ crumb: Breadcrumb) { + addCrumbInvocations.record(crumb) + } +} diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift index 1a185fcc009..99560b62f8e 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift @@ -9,7 +9,7 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase { private class Fixture { let options: Options - let delegate = SentrySystemEventBreadcrumbTestDelegate() + let delegate = SentryBreadcrumbTestDelegate() let fileManager: TestFileManager var currentDateProvider = TestCurrentDateProvider() let notificationCenterWrapper = TestNSNotificationCenterWrapper() @@ -282,11 +282,3 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase { #endif } - -class SentrySystemEventBreadcrumbTestDelegate: NSObject, SentrySystemEventBreadcrumbsDelegate { - - var addCrumbInvocations = Invocations() - func add(_ crumb: Breadcrumb) { - addCrumbInvocations.record(crumb) - } -} diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 8e32aee1441..012cbcaff01 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -22,6 +22,7 @@ #import "SentryAutoSessionTrackingIntegration.h" #import "SentryBaggage.h" #import "SentryBooleanSerialization.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryBreadcrumbTracker.h" #import "SentryByteCountFormatter.h" #import "SentryClassRegistrator.h" From 7d83dc680fcaa0f96e94cd6169796c1dc5675d40 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Mon, 17 Apr 2023 03:07:19 -0800 Subject: [PATCH 37/42] use dispatch queue wrappers for all file managers in tests (#2907) --- .../Integrations/Session/SentrySessionGeneratorTests.swift | 2 +- .../SentryWatchdogTerminationsIntegrationTests.swift | 2 +- .../SentryWatchdogTerminationsScopeObserverTests.swift | 2 +- Tests/SentryTests/SentryHubTests.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift b/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift index a30afb2fb6b..340611c524b 100644 --- a/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift +++ b/Tests/SentryTests/Integrations/Session/SentrySessionGeneratorTests.swift @@ -38,7 +38,7 @@ class SentrySessionGeneratorTests: NotificationCenterTestCase { return name != "SentryAutoSessionTrackingIntegration" } - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: DefaultCurrentDateProvider.sharedInstance()) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: DefaultCurrentDateProvider.sharedInstance(), dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) fileManager.deleteCurrentSession() fileManager.deleteCrashedSession() diff --git a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift index 09cd94d38d5..1bae051838d 100644 --- a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsIntegrationTests.swift @@ -18,7 +18,7 @@ class SentryWatchdogTerminationIntegrationTests: XCTestCase { crashWrapper = TestSentryCrashWrapper.sharedInstance() SentryDependencyContainer.sharedInstance().crashWrapper = crashWrapper - SentryDependencyContainer.sharedInstance().fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate) + SentryDependencyContainer.sharedInstance().fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate, dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) let hub = SentryHub(client: client, andScope: nil, andCrashWrapper: crashWrapper, andCurrentDateProvider: currentDate) SentrySDK.setCurrentHub(hub) diff --git a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift index 003773e57f6..840dbb2b5dc 100644 --- a/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift +++ b/Tests/SentryTests/Integrations/WatchdogTerminations/SentryWatchdogTerminationsScopeObserverTests.swift @@ -18,7 +18,7 @@ class SentryWatchdogTerminationScopeObserverTests: XCTestCase { options = Options() options.dsn = SentryWatchdogTerminationScopeObserverTests.dsn - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDate, dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) } func getSut() -> SentryWatchdogTerminationScopeObserver { diff --git a/Tests/SentryTests/SentryHubTests.swift b/Tests/SentryTests/SentryHubTests.swift index f78b2a1092f..a249d5cbe78 100644 --- a/Tests/SentryTests/SentryHubTests.swift +++ b/Tests/SentryTests/SentryHubTests.swift @@ -33,7 +33,7 @@ class SentryHubTests: XCTestCase { event = Event() event.message = SentryMessage(formatted: message) - fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDateProvider) + fileManager = try! SentryFileManager(options: options, andCurrentDateProvider: currentDateProvider, dispatchQueueWrapper: TestSentryDispatchQueueWrapper()) CurrentDate.setCurrentDateProvider(currentDateProvider) From 43aa39d9b5f97dca2eefc25997662327e87dd009 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Mon, 17 Apr 2023 13:25:17 +0200 Subject: [PATCH 38/42] fix: Start frame check removed (#2898) We stopped by mistake marking the start frame after an async frame marker. Rolling back the code to fix this. --- Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme | 3 --- Sources/Sentry/SentryStacktraceBuilder.m | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme b/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme index 378ddf22970..2dd6a7be9d5 100644 --- a/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme +++ b/Sentry.xcodeproj/xcshareddata/xcschemes/Sentry.xcscheme @@ -76,9 +76,6 @@ - - diff --git a/Sources/Sentry/SentryStacktraceBuilder.m b/Sources/Sentry/SentryStacktraceBuilder.m index 97d40419a52..bac08039954 100644 --- a/Sources/Sentry/SentryStacktraceBuilder.m +++ b/Sources/Sentry/SentryStacktraceBuilder.m @@ -41,7 +41,8 @@ - (SentryStacktrace *)retrieveStacktraceFromCursor:(SentryCrashStackCursor)stack continue; } if (stackCursor.symbolicate(&stackCursor)) { - [frames addObject:[self.crashStackEntryMapper mapStackEntryWithCursor:stackCursor]]; + frame = [self.crashStackEntryMapper mapStackEntryWithCursor:stackCursor]; + [frames addObject:frame]; } } sentrycrash_async_backtrace_decref(stackCursor.async_caller); From 74192858dff8cf729ce015b06fe9d3ff64fe7b74 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Mon, 17 Apr 2023 13:56:35 +0200 Subject: [PATCH 39/42] ref: Add delegate to SentryBreadcrumbTracker (#2914) Replace usages of SentrySDK.addBreadcrumb with delegate except one usage inside of swizzling, which can't be easily replaced cause we can't reference an instance of SentryBreadcrumbTracker inside SwizzleInstanceMethod. To get rid of SentrySDK we need to add swizzling viewDidAppear to swizzleWrapper, which we are going to do in a subsequent PR. --- .../SentryAutoBreadcrumbTrackingIntegration.m | 2 +- Sources/Sentry/SentryBreadcrumbTracker.m | 39 +++++++++---------- .../Sentry/include/SentryBreadcrumbTracker.h | 4 +- ...toBreadcrumbTrackingIntegrationTests.swift | 6 +-- .../SentryBreadcrumbTrackerTests.swift | 24 +++++++----- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m b/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m index 6a054907b46..2171743e317 100644 --- a/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m +++ b/Sources/Sentry/SentryAutoBreadcrumbTrackingIntegration.m @@ -53,7 +53,7 @@ - (void)installWithOptions:(nonnull SentryOptions *)options systemEventBreadcrumbs:(SentrySystemEventBreadcrumbs *)systemEventBreadcrumbs { self.breadcrumbTracker = breadcrumbTracker; - [self.breadcrumbTracker start]; + [self.breadcrumbTracker startWithDelegate:self]; if (options.enableSwizzling) { [self.breadcrumbTracker startSwizzle]; diff --git a/Sources/Sentry/SentryBreadcrumbTracker.m b/Sources/Sentry/SentryBreadcrumbTracker.m index dfc635fb04b..f2cccb57d7d 100644 --- a/Sources/Sentry/SentryBreadcrumbTracker.m +++ b/Sources/Sentry/SentryBreadcrumbTracker.m @@ -1,5 +1,6 @@ #import "SentryBreadcrumbTracker.h" #import "SentryBreadcrumb.h" +#import "SentryBreadcrumbDelegate.h" #import "SentryClient.h" #import "SentryDefines.h" #import "SentryHub.h" @@ -25,6 +26,7 @@ SentryBreadcrumbTracker () @property (nonatomic, strong) SentrySwizzleWrapper *swizzleWrapper; +@property (nonatomic, weak) id delegate; @end @@ -38,8 +40,9 @@ - (instancetype)initWithSwizzleWrapper:(SentrySwizzleWrapper *)swizzleWrapper return self; } -- (void)start +- (void)startWithDelegate:(id)delegate { + _delegate = delegate; [self addEnabledCrumb]; [self trackApplicationUIKitNotifications]; } @@ -57,6 +60,7 @@ - (void)stop #if SENTRY_HAS_UIKIT [self.swizzleWrapper removeSwizzleSendActionForKey:SentryBreadcrumbTrackerSwizzleSendAction]; #endif + _delegate = nil; } - (void)trackApplicationUIKitNotifications @@ -81,15 +85,13 @@ - (void)trackApplicationUIKitNotifications object:nil queue:nil usingBlock:^(NSNotification *notification) { - if (nil != [SentrySDK.currentHub getClient]) { - SentryBreadcrumb *crumb = - [[SentryBreadcrumb alloc] initWithLevel:kSentryLevelWarning - category:@"device.event"]; - crumb.type = @"system"; - crumb.data = @ { @"action" : @"LOW_MEMORY" }; - crumb.message = @"Low memory"; - [SentrySDK addBreadcrumb:crumb]; - } + SentryBreadcrumb *crumb = + [[SentryBreadcrumb alloc] initWithLevel:kSentryLevelWarning + category:@"device.event"]; + crumb.type = @"system"; + crumb.data = @ { @"action" : @"LOW_MEMORY" }; + crumb.message = @"Low memory"; + [self.delegate addBreadcrumb:crumb]; }]; #endif @@ -124,12 +126,10 @@ - (void)addBreadcrumbWithType:(NSString *)type withDataKey:(NSString *)key withDataValue:(NSString *)value { - if (nil != [SentrySDK.currentHub getClient]) { - SentryBreadcrumb *crumb = [[SentryBreadcrumb alloc] initWithLevel:level category:category]; - crumb.type = type; - crumb.data = @{ key : value }; - [SentrySDK addBreadcrumb:crumb]; - } + SentryBreadcrumb *crumb = [[SentryBreadcrumb alloc] initWithLevel:level category:category]; + crumb.type = type; + crumb.data = @{ key : value }; + [self.delegate addBreadcrumb:crumb]; } - (void)addEnabledCrumb @@ -138,7 +138,7 @@ - (void)addEnabledCrumb category:@"started"]; crumb.type = @"debug"; crumb.message = @"Breadcrumb Tracking"; - [SentrySDK addBreadcrumb:crumb]; + [self.delegate addBreadcrumb:crumb]; } #if SENTRY_HAS_UIKIT @@ -165,8 +165,7 @@ - (void)swizzleSendAction #if SENTRY_HAS_UIKIT [self.swizzleWrapper swizzleSendAction:^(NSString *action, id target, id sender, UIEvent *event) { - if ([SentrySDK.currentHub getClient] == nil || - [SentryBreadcrumbTracker avoidSender:sender forTarget:target action:action]) { + if ([SentryBreadcrumbTracker avoidSender:sender forTarget:target action:action]) { return; } @@ -182,7 +181,7 @@ - (void)swizzleSendAction crumb.type = @"user"; crumb.message = action; crumb.data = data; - [SentrySDK addBreadcrumb:crumb]; + [self.delegate addBreadcrumb:crumb]; } forKey:SentryBreadcrumbTrackerSwizzleSendAction]; diff --git a/Sources/Sentry/include/SentryBreadcrumbTracker.h b/Sources/Sentry/include/SentryBreadcrumbTracker.h index 0029bddcacc..8a9b7ffc247 100644 --- a/Sources/Sentry/include/SentryBreadcrumbTracker.h +++ b/Sources/Sentry/include/SentryBreadcrumbTracker.h @@ -4,12 +4,14 @@ NS_ASSUME_NONNULL_BEGIN @class SentrySwizzleWrapper; +@protocol SentryBreadcrumbDelegate; + @interface SentryBreadcrumbTracker : NSObject SENTRY_NO_INIT - (instancetype)initWithSwizzleWrapper:(SentrySwizzleWrapper *)swizzleWrapper; -- (void)start; +- (void)startWithDelegate:(id)delegate; - (void)startSwizzle; - (void)stop; diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift index d54c7700988..e86d79ebd42 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryAutoBreadcrumbTrackingIntegrationTests.swift @@ -93,9 +93,9 @@ class SentryAutoBreadcrumbTrackingIntegrationTests: XCTestCase { private class SentryTestBreadcrumbTracker: SentryBreadcrumbTracker { - let startInvocations = Invocations() - override func start() { - startInvocations.record(Void()) + let startInvocations = Invocations() + override func start(with delegate: SentryBreadcrumbDelegate) { + startInvocations.record(delegate) } let startSwizzleInvocations = Invocations() diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift index 7a383c989f4..ad16b700317 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentryBreadcrumbTrackerTests.swift @@ -3,20 +3,17 @@ import XCTest class SentryBreadcrumbTrackerTests: XCTestCase { - private var scope: Scope! + private var delegate: SentryBreadcrumbTestDelegate! override func setUp() { super.setUp() - scope = Scope() - let client = TestClient(options: Options()) - let hub = TestHub(client: client, andScope: scope) - SentrySDK.setCurrentHub(hub) + delegate = SentryBreadcrumbTestDelegate() } override func tearDown() { super.tearDown() - clearTestState() + delegate = nil } #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) @@ -25,7 +22,7 @@ class SentryBreadcrumbTrackerTests: XCTestCase { let swizzleWrapper = SentrySwizzleWrapper.sharedInstance let sut = SentryBreadcrumbTracker(swizzleWrapper: swizzleWrapper) - sut.start() + sut.start(with: delegate) sut.startSwizzle() sut.stop() @@ -34,8 +31,13 @@ class SentryBreadcrumbTrackerTests: XCTestCase { } func testSwizzlingStarted_ViewControllerAppears_AddsUILifeCycleBreadcrumb() { + let scope = Scope() + let client = TestClient(options: Options()) + let hub = TestHub(client: client, andScope: scope) + SentrySDK.setCurrentHub(hub) + let sut = SentryBreadcrumbTracker(swizzleWrapper: SentrySwizzleWrapper.sharedInstance) - sut.start() + sut.start(with: delegate) sut.startSwizzle() let viewController = UIViewController() @@ -45,9 +47,9 @@ class SentryBreadcrumbTrackerTests: XCTestCase { let crumbs = Dynamic(scope).breadcrumbArray.asArray as? [Breadcrumb] - XCTAssertEqual(2, crumbs?.count) + XCTAssertEqual(1, crumbs?.count) - let lifeCycleCrumb = crumbs?[1] + let lifeCycleCrumb = crumbs?[0] XCTAssertEqual("navigation", lifeCycleCrumb?.type) XCTAssertEqual("ui.lifecycle", lifeCycleCrumb?.category) XCTAssertEqual("false", lifeCycleCrumb?.data?["beingPresented"] as? String) @@ -55,6 +57,8 @@ class SentryBreadcrumbTrackerTests: XCTestCase { XCTAssertEqual("test title", lifeCycleCrumb?.data?["title"] as? String) XCTAssertEqual("false", lifeCycleCrumb?.data?["beingPresented"] as? String) XCTAssertEqual("UINavigationController", lifeCycleCrumb?.data?["parentViewController"] as? String) + + clearTestState() } func testExtractDataFrom_View() { From 869ab7e33d7061bff9b105a09f29da8e6454a229 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Mon, 17 Apr 2023 12:03:50 -0800 Subject: [PATCH 40/42] fix: GPU info collection and testing (#2849) following on with #2842 and #2856, fixes a few small bugs with how we collect GPU frame information, and refactors things so that we can validate that info in tests with a mocked system clock --- Sentry.xcodeproj/project.pbxproj | 4 + SentryTestUtils/ClearTestState.swift | 4 + SentryTestUtils/TestCurrentDateProvider.swift | 27 +- SentryTestUtils/TestDisplayLinkWrapper.swift | 100 ++-- .../TestSentryNSTimerWrapper.swift | 42 +- SentryTestUtils/TimeInterval+Sentry.swift | 11 + Sources/Sentry/SentryCurrentDate.m | 8 + .../Sentry/SentryDefaultCurrentDateProvider.m | 6 + Sources/Sentry/SentryFramesTracker.m | 63 +-- Sources/Sentry/SentryMetricProfiler.mm | 5 +- Sources/Sentry/SentryNSTimerWrapper.m | 13 + Sources/Sentry/SentryProfiler.mm | 119 ++--- Sources/Sentry/SentrySpan.m | 2 +- Sources/Sentry/SentryTracer.m | 16 +- Sources/Sentry/SentryTracerConcurrency.mm | 9 + Sources/Sentry/include/SentryCurrentDate.h | 2 + .../include/SentryCurrentDateProvider.h | 2 + Sources/Sentry/include/SentryNSTimerWrapper.h | 6 + Sources/Sentry/include/SentryProfiler.h | 1 - Sources/Sentry/include/SentryTime.h | 3 +- .../Sentry/include/SentryTracerConcurrency.h | 4 + .../SentryProfilerSwiftTests.swift | 476 +++++++++--------- .../Helper/SentryProfiler+SwiftTest.h | 6 +- .../SentryFramesTracker+TestInit.h | 4 + .../SentryFramesTrackerTests.swift | 29 +- .../TestUtils/TestExtensions.swift | 12 - .../Transaction/SentryTracer+Test.h | 6 +- .../Transaction/SentryTracerTests.swift | 2 +- 28 files changed, 567 insertions(+), 415 deletions(-) create mode 100644 SentryTestUtils/TimeInterval+Sentry.swift diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 5e48b9218e0..3853002daee 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -629,6 +629,7 @@ 8453421228BE855D00C22EEC /* SentrySampleDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 8453421128BE855D00C22EEC /* SentrySampleDecision.m */; }; 8453421628BE8A9500C22EEC /* SentrySpanStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 8453421528BE8A9500C22EEC /* SentrySpanStatus.m */; }; 8454CF8D293EAF9A006AC140 /* SentryMetricProfiler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */; }; + 84A5D75B29D5170700388BFA /* TimeInterval+Sentry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */; }; 84A888FD28D9B11700C51DFD /* SentryProfiler+Test.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */; }; 84A8891C28DBD28900C51DFD /* SentryDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A8891A28DBD28900C51DFD /* SentryDevice.h */; }; 84A8891D28DBD28900C51DFD /* SentryDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84A8891B28DBD28900C51DFD /* SentryDevice.mm */; }; @@ -1541,6 +1542,7 @@ 849472802971C107002603DE /* SentrySystemWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySystemWrapperTests.swift; sourceTree = ""; }; 849472822971C2CD002603DE /* SentryNSProcessInfoWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSProcessInfoWrapperTests.swift; sourceTree = ""; }; 849472842971C41A002603DE /* SentryNSTimerWrapperTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryNSTimerWrapperTest.swift; sourceTree = ""; }; + 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Sentry.swift"; sourceTree = ""; }; 84A888FC28D9B11700C51DFD /* SentryProfiler+Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryProfiler+Test.h"; path = "Sources/Sentry/include/SentryProfiler+Test.h"; sourceTree = SOURCE_ROOT; }; 84A8891A28DBD28900C51DFD /* SentryDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDevice.h; path = include/SentryDevice.h; sourceTree = ""; }; 84A8891B28DBD28900C51DFD /* SentryDevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryDevice.mm; sourceTree = ""; }; @@ -3091,6 +3093,7 @@ isa = PBXGroup; children = ( 7BD47B4C268F0B080076A663 /* ClearTestState.swift */, + 84A5D75A29D5170700388BFA /* TimeInterval+Sentry.swift */, 7B30B68126527C55006B2752 /* TestDisplayLinkWrapper.swift */, 8E25C97425F8511A00DC215B /* TestRandom.swift */, 7BE3C7762445E50A00A38442 /* TestCurrentDateProvider.swift */, @@ -4321,6 +4324,7 @@ 8431F01629B2851500D8DC56 /* TestSentryNSProcessInfoWrapper.swift in Sources */, 84B7FA4229B28CDE00AD93B1 /* TestCurrentDateProvider.swift in Sources */, 84B7FA3F29B28BAD00AD93B1 /* TestTransport.swift in Sources */, + 84A5D75B29D5170700388BFA /* TimeInterval+Sentry.swift in Sources */, 8431F01929B2852D00D8DC56 /* Invocation.swift in Sources */, 84B7FA4629B2935F00AD93B1 /* ClearTestState.swift in Sources */, 8431F01529B2851500D8DC56 /* TestSentryNSTimerWrapper.swift in Sources */, diff --git a/SentryTestUtils/ClearTestState.swift b/SentryTestUtils/ClearTestState.swift index 14f72adc610..6a87a432493 100644 --- a/SentryTestUtils/ClearTestState.swift +++ b/SentryTestUtils/ClearTestState.swift @@ -34,5 +34,9 @@ class TestCleanup: NSObject { SentrySwizzleWrapper.sharedInstance.removeAllCallbacks() SentryTracer.resetAppStartMeasurementRead() + +#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + SentryTracer.resetConcurrencyTracking() +#endif } } diff --git a/SentryTestUtils/TestCurrentDateProvider.swift b/SentryTestUtils/TestCurrentDateProvider.swift index e2f325689ed..f99af35bea2 100644 --- a/SentryTestUtils/TestCurrentDateProvider.swift +++ b/SentryTestUtils/TestCurrentDateProvider.swift @@ -2,9 +2,9 @@ import Foundation @objc public class TestCurrentDateProvider: NSObject, CurrentDateProvider { - - private var internalDate = Date(timeIntervalSinceReferenceDate: 0) - + public static let defaultStartingDate = Date(timeIntervalSinceReferenceDate: 0) + private var internalDate = defaultStartingDate + private var internalSystemTime: UInt64 = 0 public var driftTimeForEveryRead = false public func date() -> Date { @@ -17,10 +17,27 @@ public class TestCurrentDateProvider: NSObject, CurrentDateProvider { return internalDate } + + /// Reset the date provider to its default starting date. + public func reset() { + setDate(date: TestCurrentDateProvider.defaultStartingDate) + internalSystemTime = 0 + } public func setDate(date: Date) { internalDate = date } + + /// Advance the current date by the specified number of seconds. + public func advance(by seconds: TimeInterval) { + setDate(date: date().addingTimeInterval(seconds)) + internalSystemTime += seconds.toNanoSeconds() + } + + public func advanceBy(nanoseconds: UInt64) { + setDate(date: date().addingTimeInterval(TimeInterval(nanoseconds) / 1e9)) + internalSystemTime += nanoseconds + } public var internalDispatchNow = DispatchTime.now() public func dispatchTimeNow() -> dispatch_time_t { @@ -31,4 +48,8 @@ public class TestCurrentDateProvider: NSObject, CurrentDateProvider { public func timezoneOffset() -> Int { return timezoneOffsetValue } + + public func systemTime() -> UInt64 { + return internalSystemTime + } } diff --git a/SentryTestUtils/TestDisplayLinkWrapper.swift b/SentryTestUtils/TestDisplayLinkWrapper.swift index 3ce7f56df14..b2c0636b612 100644 --- a/SentryTestUtils/TestDisplayLinkWrapper.swift +++ b/SentryTestUtils/TestDisplayLinkWrapper.swift @@ -1,77 +1,109 @@ import Foundation #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) +/// The smallest magnitude of time that is significant to how frames are classified as normal/slow/frozen. +let timeEpsilon = 0.001 + +public enum GPUFrame { + case normal + case slow + case frozen +} + +public enum FrameRate: UInt64 { + case low = 60 + case high = 120 + + public var tickDuration: CFTimeInterval { + return 1 / CFTimeInterval(self.rawValue) + } +} + public class TestDisplayLinkWrapper: SentryDisplayLinkWrapper { - public var target: AnyObject! public var selector: Selector! - var internalTimestamp = 0.0 - var internalActualFrameRate = 60.0 + public var currentFrameRate: FrameRate = .low let frozenFrameThreshold = 0.7 - - var frameDuration: Double { - return 1.0 / internalActualFrameRate - } - - private var slowFrameThreshold: CFTimeInterval { - return 1 / (Double(internalActualFrameRate) - 1.0) + public var dateProvider: TestCurrentDateProvider + + public init(dateProvider: TestCurrentDateProvider = TestCurrentDateProvider()) { + self.dateProvider = dateProvider } - + public override func link(withTarget target: Any, selector sel: Selector) { self.target = target as AnyObject self.selector = sel } + + public override var timestamp: CFTimeInterval { + return dateProvider.systemTime().toTimeInterval() + } + + public override var targetTimestamp: CFTimeInterval { + return dateProvider.systemTime().toTimeInterval() + currentFrameRate.tickDuration + } + + public override func invalidate() { + target = nil + selector = nil + } public func call() { _ = target.perform(selector) } - public override var timestamp: CFTimeInterval { - return internalTimestamp - } - - public func changeFrameRate(_ newFrameRate: Double) { - internalActualFrameRate = newFrameRate + public func changeFrameRate(_ newFrameRate: FrameRate) { + currentFrameRate = newFrameRate } public func normalFrame() { - internalTimestamp += frameDuration + dateProvider.advance(by: currentFrameRate.tickDuration) call() } - public func slowFrame() { - internalTimestamp += slowFrameThreshold + 0.001 + public func fastestSlowFrame() -> CFTimeInterval { + let duration: Double = slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon + dateProvider.advance(by: duration) call() + return duration } - - public func almostFrozenFrame() { - internalTimestamp += frozenFrameThreshold + + public func middlingSlowFrame() -> CFTimeInterval { + let duration: Double = (frozenFrameThreshold - (slowFrameThreshold(currentFrameRate.rawValue) + timeEpsilon)) / 2.0 + dateProvider.advance(by: duration) call() + return duration } - public func frozenFrame() { - internalTimestamp += frozenFrameThreshold + 0.001 + public func slowestSlowFrame() -> CFTimeInterval { + dateProvider.advance(by: frozenFrameThreshold) call() + return frozenFrameThreshold } - - public override var targetTimestamp: CFTimeInterval { - return internalTimestamp + frameDuration + + public func fastestFrozenFrame() -> CFTimeInterval { + let duration: Double = frozenFrameThreshold + timeEpsilon + dateProvider.advance(by: duration) + call() + return duration } - - public override func invalidate() { - target = nil - selector = nil + + /// There's no upper bound for a frozen frame, except maybe for the watchdog time limit. + /// - parameter extraTime: the additional time to add to the frozen frame threshold when simulating a frozen frame. + public func slowerFrozenFrame(extraTime: TimeInterval = 0.1) { + dateProvider.advance(by: frozenFrameThreshold + extraTime) + call() } public func givenFrames(_ slow: Int, _ frozen: Int, _ normal: Int) { self.call() for _ in 0.. Void)? + + struct InvocationInfo { + var target: NSObject + var selector: Selector } + var invocationInfo: InvocationInfo? } + public var overrides = Overrides() + public override func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void) -> Timer { let timer = Timer.scheduledTimer(withTimeInterval: TimeInterval.infinity, repeats: repeats, block: block) - _timer = timer - + overrides.timer = timer + overrides.block = block + return timer + } + + public override func scheduledTimer(withTimeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) -> Timer { + let timer = Timer.scheduledTimer(timeInterval: ti, target: aTarget, selector: aSelector, userInfo: userInfo, repeats: yesOrNo) + //swiftlint:disable force_cast + overrides.invocationInfo = Overrides.InvocationInfo(target: aTarget as! NSObject, selector: aSelector) + //swiftlint:enable force_cast return timer } public func fire() { - _timer?.fire() + if let block = overrides.block { + block(overrides.timer) + } else if let invocationInfo = overrides.invocationInfo { + try! Invocation(target: invocationInfo.target, selector: invocationInfo.selector).invoke() + } } } diff --git a/SentryTestUtils/TimeInterval+Sentry.swift b/SentryTestUtils/TimeInterval+Sentry.swift new file mode 100644 index 00000000000..c75691d562e --- /dev/null +++ b/SentryTestUtils/TimeInterval+Sentry.swift @@ -0,0 +1,11 @@ +public extension TimeInterval { + func toNanoSeconds() -> UInt64 { + return UInt64(self * Double(NSEC_PER_SEC)) + } +} + +public extension UInt64 { + func toTimeInterval() -> TimeInterval { + return Double(self) / Double(NSEC_PER_SEC) + } +} diff --git a/Sources/Sentry/SentryCurrentDate.m b/Sources/Sentry/SentryCurrentDate.m index bed5c86e865..21468fe90cb 100644 --- a/Sources/Sentry/SentryCurrentDate.m +++ b/Sources/Sentry/SentryCurrentDate.m @@ -22,6 +22,14 @@ + (NSDate *)date return [currentDateProvider date]; } ++ (uint64_t)systemTime +{ + if (currentDateProvider == nil) { + currentDateProvider = [SentryDefaultCurrentDateProvider sharedInstance]; + } + return [currentDateProvider systemTime]; +} + + (dispatch_time_t)dispatchTimeNow { if (currentDateProvider == nil) { diff --git a/Sources/Sentry/SentryDefaultCurrentDateProvider.m b/Sources/Sentry/SentryDefaultCurrentDateProvider.m index 6e86349bbc1..e56a654be3d 100644 --- a/Sources/Sentry/SentryDefaultCurrentDateProvider.m +++ b/Sources/Sentry/SentryDefaultCurrentDateProvider.m @@ -1,4 +1,5 @@ #import "SentryDefaultCurrentDateProvider.h" +#import "SentryTime.h" NS_ASSUME_NONNULL_BEGIN @@ -32,6 +33,11 @@ - (NSInteger)timezoneOffset return [NSTimeZone localTimeZone].secondsFromGMT; } +- (uint64_t)systemTime +{ + return getAbsoluteTime(); +} + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentryFramesTracker.m b/Sources/Sentry/SentryFramesTracker.m index c57cadc2078..60400e9284f 100644 --- a/Sources/Sentry/SentryFramesTracker.m +++ b/Sources/Sentry/SentryFramesTracker.m @@ -1,6 +1,8 @@ #import "SentryFramesTracker.h" #import "SentryCompiler.h" +#import "SentryCurrentDate.h" #import "SentryDisplayLinkWrapper.h" +#import "SentryLog.h" #import "SentryProfiler.h" #import "SentryProfilingConditionals.h" #import "SentryTime.h" @@ -40,6 +42,14 @@ @end +CFTimeInterval +slowFrameThreshold(uint64_t actualFramesPerSecond) +{ + // Most frames take just a few microseconds longer than the optimal calculated duration. + // Therefore we subtract one, because otherwise almost all frames would be slow. + return 1.0 / (actualFramesPerSecond - 1.0); +} + @implementation SentryFramesTracker { /** @@ -111,7 +121,7 @@ - (void)start - (void)displayLinkCallback { CFTimeInterval thisFrameTimestamp = self.displayLinkWrapper.timestamp; - uint64_t thisFrameSystemTimestamp = getAbsoluteTime(); + uint64_t thisFrameSystemTimestamp = SentryCurrentDate.systemTime; if (self.previousFrameTimestamp == SentryPreviousFrameInitialValue) { self.previousFrameTimestamp = thisFrameTimestamp; @@ -127,12 +137,12 @@ - (void)displayLinkCallback // need to check the frame rate for every callback. // targetTimestamp is only available on iOS 10.0 and tvOS 10.0 and above. We use a fallback of // 60 fps. - double actualFramesPerSecond = 60.0; + uint64_t currentFrameRate = 60; if (UNLIKELY((self.displayLinkWrapper.targetTimestamp == self.displayLinkWrapper.timestamp))) { - actualFramesPerSecond = 60.0; + currentFrameRate = 60; } else { - actualFramesPerSecond - = 1 / (self.displayLinkWrapper.targetTimestamp - self.displayLinkWrapper.timestamp); + currentFrameRate = (uint64_t)round( + (1 / (self.displayLinkWrapper.targetTimestamp - self.displayLinkWrapper.timestamp))); } # if SENTRY_TARGET_PROFILING_SUPPORTED @@ -142,40 +152,37 @@ - (void)displayLinkCallback BOOL shouldRecordFrameRates = [SentryProfiler isRunning]; # endif // defined(TEST) || defined(TESTCI) BOOL hasNoFrameRatesYet = self.frameRateTimestamps.count == 0; - BOOL frameRateSignificantlyChanged - = fabs(self.frameRateTimestamps.lastObject[@"frame_rate"].doubleValue - - actualFramesPerSecond) - > 1e-10f; // these may be a small fraction off of a whole number of frames per second, so - // allow some small epsilon difference + uint64_t previousFrameRate + = self.frameRateTimestamps.lastObject[@"value"].unsignedLongLongValue; + BOOL frameRateChanged = previousFrameRate != currentFrameRate; BOOL shouldRecordNewFrameRate - = shouldRecordFrameRates && (hasNoFrameRatesYet || frameRateSignificantlyChanged); + = shouldRecordFrameRates && (hasNoFrameRatesYet || frameRateChanged); if (shouldRecordNewFrameRate) { - [self.frameRateTimestamps addObject:@{ - @"timestamp" : @(thisFrameSystemTimestamp), - @"frame_rate" : @(actualFramesPerSecond), - }]; + SENTRY_LOG_DEBUG(@"Recording new frame rate at %llu.", thisFrameSystemTimestamp); + [self recordTimestamp:thisFrameSystemTimestamp + value:@(currentFrameRate) + array:self.frameRateTimestamps]; } # endif // SENTRY_TARGET_PROFILING_SUPPORTED - // Most frames take just a few microseconds longer than the optimal calculated duration. - // Therefore we subtract one, because otherwise almost all frames would be slow. - CFTimeInterval slowFrameThreshold = 1 / (actualFramesPerSecond - 1); - CFTimeInterval frameDuration = thisFrameTimestamp - self.previousFrameTimestamp; - if (frameDuration > slowFrameThreshold && frameDuration <= SentryFrozenFrameThreshold) { + if (frameDuration > slowFrameThreshold(currentFrameRate) + && frameDuration <= SentryFrozenFrameThreshold) { atomic_fetch_add_explicit(&_slowFrames, 1, SentryFramesMemoryOrder); # if SENTRY_TARGET_PROFILING_SUPPORTED - [self recordTimestampStart:@(self.previousFrameSystemTimestamp) - end:@(thisFrameSystemTimestamp) - array:self.slowFrameTimestamps]; + SENTRY_LOG_DEBUG(@"Capturing slow frame starting at %llu.", thisFrameSystemTimestamp); + [self recordTimestamp:thisFrameSystemTimestamp + value:@(thisFrameSystemTimestamp - self.previousFrameSystemTimestamp) + array:self.slowFrameTimestamps]; # endif // SENTRY_TARGET_PROFILING_SUPPORTED } else if (frameDuration > SentryFrozenFrameThreshold) { atomic_fetch_add_explicit(&_frozenFrames, 1, SentryFramesMemoryOrder); # if SENTRY_TARGET_PROFILING_SUPPORTED - [self recordTimestampStart:@(self.previousFrameSystemTimestamp) - end:@(thisFrameSystemTimestamp) - array:self.frozenFrameTimestamps]; + SENTRY_LOG_DEBUG(@"Capturing frozen frame starting at %llu.", thisFrameSystemTimestamp); + [self recordTimestamp:thisFrameSystemTimestamp + value:@(thisFrameSystemTimestamp - self.previousFrameSystemTimestamp) + array:self.frozenFrameTimestamps]; # endif // SENTRY_TARGET_PROFILING_SUPPORTED } @@ -198,14 +205,14 @@ - (void)reportNewFrame } # if SENTRY_TARGET_PROFILING_SUPPORTED -- (void)recordTimestampStart:(NSNumber *)start end:(NSNumber *)end array:(NSMutableArray *)array +- (void)recordTimestamp:(uint64_t)timestamp value:(NSNumber *)value array:(NSMutableArray *)array { BOOL shouldRecord = [SentryProfiler isRunning]; # if defined(TEST) || defined(TESTCI) shouldRecord = YES; # endif if (shouldRecord) { - [array addObject:@{ @"start_timestamp" : start, @"end_timestamp" : end }]; + [array addObject:@{ @"timestamp" : @(timestamp), @"value" : value }]; } } # endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Sources/Sentry/SentryMetricProfiler.mm b/Sources/Sentry/SentryMetricProfiler.mm index 294379105ad..678479f94db 100644 --- a/Sources/Sentry/SentryMetricProfiler.mm +++ b/Sources/Sentry/SentryMetricProfiler.mm @@ -2,6 +2,7 @@ #if SENTRY_TARGET_PROFILING_SUPPORTED +# import "SentryCurrentDate.h" # import "SentryEvent+Private.h" # import "SentryLog.h" # import "SentryNSProcessInfoWrapper.h" @@ -47,7 +48,7 @@ @implementation SentryMetricReading [absoluteTimestampValues enumerateObjectsUsingBlock:^( SentryMetricReading *_Nonnull reading, NSUInteger idx, BOOL *_Nonnull stop) { // if the metric reading wasn't recorded until the transaction ended, don't include it - if (orderedChronologically(transaction.endSystemTime, reading.absoluteTimestamp)) { + if (!orderedChronologically(reading.absoluteTimestamp, transaction.endSystemTime)) { return; } @@ -200,7 +201,7 @@ - (SentryMetricReading *)metricReadingForValue:(NSNumber *)value { const auto reading = [[SentryMetricReading alloc] init]; reading.value = value; - reading.absoluteTimestamp = getAbsoluteTime(); + reading.absoluteTimestamp = SentryCurrentDate.systemTime; return reading; } diff --git a/Sources/Sentry/SentryNSTimerWrapper.m b/Sources/Sentry/SentryNSTimerWrapper.m index 5e6af70ad1b..6a9ffef47c5 100644 --- a/Sources/Sentry/SentryNSTimerWrapper.m +++ b/Sources/Sentry/SentryNSTimerWrapper.m @@ -9,4 +9,17 @@ - (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval return [NSTimer scheduledTimerWithTimeInterval:interval repeats:repeats block:block]; } +- (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti + target:(id)aTarget + selector:(SEL)aSelector + userInfo:(nullable id)userInfo + repeats:(BOOL)yesOrNo +{ + return [NSTimer scheduledTimerWithTimeInterval:ti + target:aTarget + selector:aSelector + userInfo:userInfo + repeats:yesOrNo]; +} + @end diff --git a/Sources/Sentry/SentryProfiler.mm b/Sources/Sentry/SentryProfiler.mm index 0a9301761ec..34e465b7130 100644 --- a/Sources/Sentry/SentryProfiler.mm +++ b/Sources/Sentry/SentryProfiler.mm @@ -162,7 +162,8 @@ SentryProfiler *_Nullable _gCurrentProfiler; SentryNSProcessInfoWrapper *_gCurrentProcessInfoWrapper; SentrySystemWrapper *_gCurrentSystemWrapper; -SentryNSTimerWrapper *_gCurrentTimerWrapper; +SentryNSTimerWrapper *_gMetricTimerWrapper; +SentryNSTimerWrapper *_gTimeoutTimerWrapper; # if SENTRY_HAS_UIKIT SentryFramesTracker *_gCurrentFramesTracker; # endif // SENTRY_HAS_UIKIT @@ -193,73 +194,32 @@ * didn't occur within the profile time. */ NSArray * -processFrameRenders(SentryFrameInfoTimeSeries *frameInfo, SentryTransaction *transaction) +sliceGPUData(SentryFrameInfoTimeSeries *frameInfo, SentryTransaction *transaction) { - auto relativeFrameInfo = [NSMutableArray array]; + auto slicedGPUEntries = [NSMutableArray array]; [frameInfo enumerateObjectsUsingBlock:^( - NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - const auto frameRenderStart = obj[@"start_timestamp"].unsignedLongLongValue; - - if (!orderedChronologically(transaction.startSystemTime, frameRenderStart)) { - SENTRY_LOG_DEBUG(@"GPU frame render started before profile start, will not report it."); - return; - } - const auto frameRenderEnd = obj[@"end_timestamp"].unsignedLongLongValue; - if (orderedChronologically(transaction.endSystemTime, frameRenderEnd)) { - SENTRY_LOG_DEBUG(@"Frame render finished after transaction finished, won't record."); - return; - } - const auto relativeFrameRenderStart - = getDurationNs(transaction.startSystemTime, frameRenderStart); - const auto relativeFrameRenderEnd - = getDurationNs(transaction.startSystemTime, frameRenderEnd); - - // this probably won't happen, but doesn't hurt to have one last defensive check before - // calling getDurationNs - if (!orderedChronologically(relativeFrameRenderStart, relativeFrameRenderEnd)) { - SENTRY_LOG_WARN( - @"Computed relative start and end timestamps are not chronologically ordered."); - return; - } - const auto frameRenderDurationNs - = getDurationNs(relativeFrameRenderStart, relativeFrameRenderEnd); - - [relativeFrameInfo addObject:@{ - @"elapsed_since_start_ns" : serializedUnsigned64BitInteger(relativeFrameRenderStart), - @"value" : @(frameRenderDurationNs), - }]; - }]; - return relativeFrameInfo; -} - -/** - * Convert the data structure that records timestamps for GPU frame rate info from - * SentryFramesTracker to the structure expected for profiling metrics. - */ -NSArray * -processFrameRates(SentryFrameInfoTimeSeries *frameRates, SentryTransaction *transaction) -{ - auto relativeFrameRates = [NSMutableArray array]; - [frameRates enumerateObjectsUsingBlock:^( NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { const auto timestamp = obj[@"timestamp"].unsignedLongLongValue; - const auto refreshRate = obj[@"frame_rate"]; if (!orderedChronologically(transaction.startSystemTime, timestamp)) { + SENTRY_LOG_DEBUG(@"GPU info recorded (%llu) before transaction start (%llu), " + @"will not report it.", + timestamp, transaction.startSystemTime); return; } - if (orderedChronologically(transaction.endSystemTime, timestamp)) { + + if (!orderedChronologically(timestamp, transaction.endSystemTime)) { + SENTRY_LOG_DEBUG(@"GPU info recorded after transaction finished, won't record."); return; } - const auto relativeTimestamp = getDurationNs(transaction.startSystemTime, timestamp); - [relativeFrameRates addObject:@ { + [slicedGPUEntries addObject:@ { @"elapsed_since_start_ns" : serializedUnsigned64BitInteger(relativeTimestamp), - @"value" : refreshRate, + @"value" : obj[@"value"], }]; }]; - return relativeFrameRates; + return slicedGPUEntries; } # endif // SENTRY_HAS_UIKIT @@ -341,12 +301,15 @@ + (void)startWithHub:(SentryHub *)hub [_gCurrentProfiler start]; + if (_gTimeoutTimerWrapper == nil) { + _gTimeoutTimerWrapper = [[SentryNSTimerWrapper alloc] init]; + } _gCurrentProfiler->_timeoutTimer = - [NSTimer scheduledTimerWithTimeInterval:kSentryProfilerTimeoutInterval - target:self - selector:@selector(timeoutAbort) - userInfo:nil - repeats:NO]; + [_gTimeoutTimerWrapper scheduledTimerWithTimeInterval:kSentryProfilerTimeoutInterval + target:self + selector:@selector(timeoutAbort) + userInfo:nil + repeats:NO]; # if SENTRY_HAS_UIKIT [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundAbort) @@ -425,21 +388,21 @@ + (SentryEnvelopeItem *)createProfilingEnvelopeItemForTransaction:(SentryTransac const auto metrics = [_gCurrentProfiler->_metricProfiler serializeForTransaction:transaction]; # if SENTRY_HAS_UIKIT - const auto slowFrames = processFrameRenders( - _gCurrentFramesTracker.currentFrames.slowFrameTimestamps, transaction); + const auto slowFrames + = sliceGPUData(_gCurrentFramesTracker.currentFrames.slowFrameTimestamps, transaction); if (slowFrames.count > 0) { metrics[@"slow_frame_renders"] = @{ @"unit" : @"nanosecond", @"values" : slowFrames }; } - const auto frozenFrames = processFrameRenders( - _gCurrentFramesTracker.currentFrames.frozenFrameTimestamps, transaction); + const auto frozenFrames + = sliceGPUData(_gCurrentFramesTracker.currentFrames.frozenFrameTimestamps, transaction); if (frozenFrames.count > 0) { metrics[@"frozen_frame_renders"] = @{ @"unit" : @"nanosecond", @"values" : frozenFrames }; } if (slowFrames.count > 0 || frozenFrames.count > 0) { - const auto frameRates = processFrameRates( - _gCurrentFramesTracker.currentFrames.frameRateTimestamps, transaction); + const auto frameRates + = sliceGPUData(_gCurrentFramesTracker.currentFrames.frameRateTimestamps, transaction); if (frameRates.count > 0) { metrics[@"screen_frame_rates"] = @{ @"unit" : @"hz", @"values" : frameRates }; } @@ -477,10 +440,16 @@ + (void)useProcessInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper _gCurrentProcessInfoWrapper = processInfoWrapper; } -+ (void)useTimerWrapper:(SentryNSTimerWrapper *)timerWrapper ++ (void)useMetricTimerWrapper:(SentryNSTimerWrapper *)timerWrapper +{ + std::lock_guard l(_gProfilerLock); + _gMetricTimerWrapper = timerWrapper; +} + ++ (void)useTimeoutTimerWrapper:(SentryNSTimerWrapper *)timerWrapper { std::lock_guard l(_gProfilerLock); - _gCurrentTimerWrapper = timerWrapper; + _gTimeoutTimerWrapper = timerWrapper; } # if SENTRY_HAS_UIKIT @@ -545,8 +514,8 @@ - (void)startMetricProfiler if (_gCurrentProcessInfoWrapper == nil) { _gCurrentProcessInfoWrapper = [SentryDependencyContainer.sharedInstance processInfoWrapper]; } - if (_gCurrentTimerWrapper == nil) { - _gCurrentTimerWrapper = [[SentryNSTimerWrapper alloc] init]; + if (_gMetricTimerWrapper == nil) { + _gMetricTimerWrapper = [[SentryNSTimerWrapper alloc] init]; } # if SENTRY_HAS_UIKIT if (_gCurrentFramesTracker == nil) { @@ -556,7 +525,7 @@ - (void)startMetricProfiler _metricProfiler = [[SentryMetricProfiler alloc] initWithProcessInfoWrapper:_gCurrentProcessInfoWrapper systemWrapper:_gCurrentSystemWrapper - timerWrapper:_gCurrentTimerWrapper]; + timerWrapper:_gMetricTimerWrapper]; [_metricProfiler start]; } @@ -655,8 +624,20 @@ - (void)start SENTRY_LOG_WARN(@"Profiler instance no longer exists, cannot process next sample."); return; } + + // in test, we'll overwrite the sample's timestamp to one mocked by SentryCurrentDate etal. + // Doing this in a unified way between tests and production required extensive changes to + // the C++ layer, so we opted for this solution to avoid any potential breakages or + // performance hits there. +# if defined(TEST) || defined(TESTCI) + Backtrace backtraceCopy = backtrace; + backtraceCopy.absoluteTimestamp = SentryCurrentDate.systemTime; + processBacktrace(backtraceCopy, threadMetadata, queueMetadata, samples, stacks, frames, + frameIndexLookup, stackIndexLookup); +# else processBacktrace(backtrace, threadMetadata, queueMetadata, samples, stacks, frames, frameIndexLookup, stackIndexLookup); +# endif // defined(TEST) || defined(TESTCI) }, kSentryProfilerFrequencyHz); _profiler->startSampling(); diff --git a/Sources/Sentry/SentrySpan.m b/Sources/Sentry/SentrySpan.m index 4b6f44e76aa..4e3331261bf 100644 --- a/Sources/Sentry/SentrySpan.m +++ b/Sources/Sentry/SentrySpan.m @@ -146,7 +146,7 @@ - (void)finishWithStatus:(SentrySpanStatus)status if (self.timestamp == nil) { self.timestamp = [SentryCurrentDate date]; SENTRY_LOG_DEBUG(@"Setting span timestamp: %@ at system time %llu", self.timestamp, - (unsigned long long)getAbsoluteTime()); + (unsigned long long)SentryCurrentDate.systemTime); } if (self.tracer == nil) { SENTRY_LOG_DEBUG( diff --git a/Sources/Sentry/SentryTracer.m b/Sources/Sentry/SentryTracer.m index 3abd7708c43..b043649af85 100644 --- a/Sources/Sentry/SentryTracer.m +++ b/Sources/Sentry/SentryTracer.m @@ -146,7 +146,7 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti #if SENTRY_TARGET_PROFILING_SUPPORTED if (_configuration.profilesSamplerDecision.decision == kSentrySampleDecisionYes) { _isProfiling = YES; - _startSystemTime = getAbsoluteTime(); + _startSystemTime = SentryCurrentDate.systemTime; [SentryProfiler startWithHub:hub]; trackTracerWithID(self.traceId); } @@ -175,7 +175,7 @@ - (void)dispatchIdleTimeout }]; if (_idleTimeoutBlock == NULL) { - SENTRY_LOG_WARN(@"Couln't create idle time out block. Can't schedule idle timeout. " + SENTRY_LOG_WARN(@"Couldn't create idle time out block. Can't schedule idle timeout. " @"Finishing transaction"); // If the transaction has no children, the SDK will discard it. [self finishInternal]; @@ -561,7 +561,7 @@ - (SentryTransaction *)toTransaction transaction.transaction = self.transactionContext.name; #if SENTRY_TARGET_PROFILING_SUPPORTED transaction.startSystemTime = self.startSystemTime; - transaction.endSystemTime = getAbsoluteTime(); + transaction.endSystemTime = SentryCurrentDate.systemTime; #endif // SENTRY_TARGET_PROFILING_SUPPORTED NSMutableArray *framesOfAllSpans = [NSMutableArray array]; @@ -795,6 +795,16 @@ - (NSDate *)originalStartTimestamp return _startTimeChanged ? _originalStartTimestamp : self.startTimestamp; } +#if SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) +// this just calls through to SentryTracerConcurrency.resetConcurrencyTracking(). we have to do this +// through SentryTracer because SentryTracerConcurrency cannot be included in test targets via ObjC +// bridging headers because it contains C++. ++ (void)resetConcurrencyTracking +{ + resetConcurrencyTracking(); +} +#endif // SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentryTracerConcurrency.mm b/Sources/Sentry/SentryTracerConcurrency.mm index bb3ee5f9c5d..7c4fd052338 100644 --- a/Sources/Sentry/SentryTracerConcurrency.mm +++ b/Sources/Sentry/SentryTracerConcurrency.mm @@ -38,4 +38,13 @@ } } +# if defined(TEST) || defined(TESTCI) +void +resetConcurrencyTracking() +{ + std::lock_guard l(_gStateLock); + [_gInFlightTraceIDs removeAllObjects]; +} +# endif // defined(TEST) || defined(TESTCI) + #endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Sources/Sentry/include/SentryCurrentDate.h b/Sources/Sentry/include/SentryCurrentDate.h index 26c6afea515..839c6960474 100644 --- a/Sources/Sentry/include/SentryCurrentDate.h +++ b/Sources/Sentry/include/SentryCurrentDate.h @@ -12,6 +12,8 @@ NS_SWIFT_NAME(CurrentDate) + (NSDate *)date; ++ (uint64_t)systemTime; + + (dispatch_time_t)dispatchTimeNow; + (void)setCurrentDateProvider:(nullable id)currentDateProvider; diff --git a/Sources/Sentry/include/SentryCurrentDateProvider.h b/Sources/Sentry/include/SentryCurrentDateProvider.h index 74dae8dd2f1..cc7417ef250 100644 --- a/Sources/Sentry/include/SentryCurrentDateProvider.h +++ b/Sources/Sentry/include/SentryCurrentDateProvider.h @@ -11,6 +11,8 @@ NS_SWIFT_NAME(CurrentDateProvider) - (NSInteger)timezoneOffset; +- (uint64_t)systemTime; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryNSTimerWrapper.h b/Sources/Sentry/include/SentryNSTimerWrapper.h index 0529b48e5ba..2e80fa83f84 100644 --- a/Sources/Sentry/include/SentryNSTimerWrapper.h +++ b/Sources/Sentry/include/SentryNSTimerWrapper.h @@ -8,6 +8,12 @@ NS_ASSUME_NONNULL_BEGIN repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block; +- (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti + target:(id)aTarget + selector:(SEL)aSelector + userInfo:(nullable id)userInfo + repeats:(BOOL)yesOrNo; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryProfiler.h b/Sources/Sentry/include/SentryProfiler.h index 1a9116a06e6..2bfa6d16f26 100644 --- a/Sources/Sentry/include/SentryProfiler.h +++ b/Sources/Sentry/include/SentryProfiler.h @@ -22,7 +22,6 @@ NS_ASSUME_NONNULL_BEGIN SENTRY_EXTERN const int kSentryProfilerFrequencyHz; SENTRY_EXTERN NSString *const kTestStringConst; -FOUNDATION_EXPORT NSTimeInterval kSentryProfilerTimeoutInterval; SENTRY_EXTERN NSString *const kSentryProfilerSerializationKeySlowFrameRenders; SENTRY_EXTERN NSString *const kSentryProfilerSerializationKeyFrozenFrameRenders; diff --git a/Sources/Sentry/include/SentryTime.h b/Sources/Sentry/include/SentryTime.h index d236fc38dee..3985b60d12a 100644 --- a/Sources/Sentry/include/SentryTime.h +++ b/Sources/Sentry/include/SentryTime.h @@ -21,7 +21,8 @@ uint64_t getAbsoluteTime(void); * Check whether two timestamps provided as 64 bit unsigned integers are in normal * chronological order, as a convenience runtime check before using @c getDurationNs. * Equal timestamps are considered to be valid chronological order. - * @return @c true if @c b>=a, otherwise return @c false. + * @return @c true if @c a<=b, otherwise return @c false. + * @note Negating the return value implies @c a>b . */ bool orderedChronologically(uint64_t a, uint64_t b); diff --git a/Sources/Sentry/include/SentryTracerConcurrency.h b/Sources/Sentry/include/SentryTracerConcurrency.h index f4123810cb3..9710f5df41d 100644 --- a/Sources/Sentry/include/SentryTracerConcurrency.h +++ b/Sources/Sentry/include/SentryTracerConcurrency.h @@ -22,6 +22,10 @@ void trackTracerWithID(SentryId *traceID); */ void stopTrackingTracerWithID(SentryId *traceID, SentryConcurrentTransactionCleanupBlock cleanup); +# if defined(TEST) || defined(TESTCI) +void resetConcurrencyTracking(void); +# endif // defined(TEST) || defined(TESTCI) + SENTRY_EXTERN_C_END NS_ASSUME_NONNULL_END diff --git a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift index 7c579ceba8b..2144d86211b 100644 --- a/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift +++ b/Tests/SentryProfilerTests/SentryProfilerSwiftTests.swift @@ -27,21 +27,43 @@ class SentryProfilerSwiftTests: XCTestCase { lazy var systemWrapper = TestSentrySystemWrapper() lazy var processInfoWrapper = TestSentryNSProcessInfoWrapper() - lazy var timerWrapper = TestSentryNSTimerWrapper() + lazy var metricTimerWrapper = TestSentryNSTimerWrapper() + lazy var timeoutTimerWrapper = TestSentryNSTimerWrapper() let currentDateProvider = TestCurrentDateProvider() #if !os(macOS) - lazy var displayLinkWrapper = TestDisplayLinkWrapper() + lazy var displayLinkWrapper = TestDisplayLinkWrapper(dateProvider: currentDateProvider) lazy var framesTracker = SentryFramesTracker(displayLinkWrapper: displayLinkWrapper) #endif - func newTransaction(testingAppLaunchSpans: Bool = false) -> Span { - currentDateProvider.setDate(date: currentDateProvider.date().addingTimeInterval(2)) + init() { + CurrentDate.setCurrentDateProvider(currentDateProvider) + options.profilesSampleRate = 1.0 + options.tracesSampleRate = 1.0 + + SentryProfiler.useSystemWrapper(systemWrapper) + SentryProfiler.useProcessInfoWrapper(processInfoWrapper) + SentryProfiler.useMetricTimerWrapper(metricTimerWrapper) + SentryProfiler.useTimeoutTimerWrapper(timeoutTimerWrapper) + + systemWrapper.overrides.cpuUsagePerCore = mockCPUUsages.map { NSNumber(value: $0) } + processInfoWrapper.overrides.processorCount = UInt(mockCPUUsages.count) + systemWrapper.overrides.memoryFootprintBytes = mockMemoryFootprint + +#if !os(macOS) + SentryProfiler.useFramesTracker(framesTracker) + framesTracker.start() + displayLinkWrapper.call() +#endif + } + + /// Advance the mock date provider, start a new transaction and return its handle. + func newTransaction(testingAppLaunchSpans: Bool = false) throws -> SentryTracer { if testingAppLaunchSpans { - return hub.startTransaction(name: transactionName, operation: SentrySpanOperationUILoad) + return try XCTUnwrap(hub.startTransaction(name: transactionName, operation: SentrySpanOperationUILoad) as? SentryTracer) } - return hub.startTransaction(name: transactionName, operation: transactionOperation) + return try XCTUnwrap(hub.startTransaction(name: transactionName, operation: transactionOperation) as? SentryTracer) } // mocking @@ -49,70 +71,115 @@ class SentryProfilerSwiftTests: XCTestCase { let mockCPUUsages = [12.4, 63.5, 1.4, 4.6] let mockMemoryFootprint: SentryRAMBytes = 123_455 let mockUsageReadingsPerBatch = 2 - let mockSlowFramesPerBatch = 2 - let mockFrozenFramesPerBatch = 1 +#if !os(macOS) // SentryFramesTracker starts assuming a frame rate of 60 Hz and will only log an update if it changes, so the first value here needs to be different for it to register. - let mockFrameRateChangesPerBatch: [Double] = [120.0, 60.0, 120.0, 60.0] - - func mockMetricsSubsystems() { - SentryProfiler.useSystemWrapper(systemWrapper) - SentryProfiler.useProcessInfoWrapper(processInfoWrapper) - SentryProfiler.useTimerWrapper(timerWrapper) - #if !os(macOS) - SentryProfiler.useFramesTracker(framesTracker) - #endif - } + let mockFrameRateChangesPerBatch: [FrameRate] = [.high, .low, .high, .low] +#endif - func prepareMetricsMocks() { - systemWrapper.overrides.cpuUsagePerCore = mockCPUUsages.map { NSNumber(value: $0) } - processInfoWrapper.overrides.processorCount = UInt(mockCPUUsages.count) +#if !os(macOS) + // Absolute timestamps must be adjusted per span when asserting + var expectedSlowFrames = [[String: Any]]() + var expectedFrozenFrames = [[String: Any]]() + var expectedFrameRateChanges = [[String: Any]]() - systemWrapper.overrides.memoryFootprintBytes = mockMemoryFootprint + func resetGPUExpectations() { + expectedSlowFrames = [[String: Any]]() + expectedFrozenFrames = [[String: Any]]() + expectedFrameRateChanges = [[String: Any]]() } +#endif - func gatherMockedMetrics() { + func gatherMockedMetrics(span: Span) throws { // clear out any errors that might've been set in previous calls systemWrapper.overrides.cpuUsageError = nil systemWrapper.overrides.memoryFootprintError = nil // gather mock cpu usages and memory footprints for _ in 0.. /// transaction A |---------------------------------------------------| /// profiler A |---------------------------x <- timeout /// transaction B |-------| /// profiler B |-------| <- normal finish /// ``` func testConcurrentSpansWithTimeout() throws { - let originalTimeoutInterval = kSentryProfilerTimeoutInterval - kSentryProfilerTimeoutInterval = 1 - - let spanA = fixture.newTransaction() - + let spanA = try fixture.newTransaction() + fixture.currentDateProvider.advanceBy(nanoseconds: 1.toNanoSeconds()) forceProfilerSample() + fixture.currentDateProvider.advanceBy(nanoseconds: 30.toNanoSeconds()) + fixture.timeoutTimerWrapper.fire() - // cause spanA profiler to time out - let exp = expectation(description: "spanA times out") - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - exp.fulfill() - } - waitForExpectations(timeout: 3) - - let spanB = fixture.newTransaction() + fixture.currentDateProvider.advanceBy(nanoseconds: 0.5.toNanoSeconds()) + let spanB = try fixture.newTransaction() + fixture.currentDateProvider.advanceBy(nanoseconds: 0.5.toNanoSeconds()) forceProfilerSample() spanB.finish() @@ -261,8 +292,6 @@ class SentryProfilerSwiftTests: XCTestCase { spanA.finish() try self.assertValidProfileData() - - kSentryProfilerTimeoutInterval = originalTimeoutInterval } func testProfileTimeoutTimer() throws { @@ -299,86 +328,69 @@ class SentryProfilerSwiftTests: XCTestCase { try performTest(transactionEnvironment: expectedEnvironment) } - func testStartTransaction_NotSamplingProfileUsingEnableProfiling() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_NotSamplingProfileUsingEnableProfiling() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.enableProfiling_DEPRECATED_TEST_ONLY = false } } - func testStartTransaction_SamplingProfileUsingEnableProfiling() { - assertProfilesSampler(expectedDecision: .yes) { options in + func testStartTransaction_SamplingProfileUsingEnableProfiling() throws { + try assertProfilesSampler(expectedDecision: .yes) { options in options.enableProfiling_DEPRECATED_TEST_ONLY = true } } - func testStartTransaction_NotSamplingProfileUsingSampleRate() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_NotSamplingProfileUsingSampleRate() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampleRate = 0.49 } } - func testStartTransaction_SamplingProfileUsingSampleRate() { - assertProfilesSampler(expectedDecision: .yes) { options in + func testStartTransaction_SamplingProfileUsingSampleRate() throws { + try assertProfilesSampler(expectedDecision: .yes) { options in options.profilesSampleRate = 0.5 } } - func testStartTransaction_SamplingProfileUsingProfilesSampler() { - assertProfilesSampler(expectedDecision: .yes) { options in + func testStartTransaction_SamplingProfileUsingProfilesSampler() throws { + try assertProfilesSampler(expectedDecision: .yes) { options in options.profilesSampler = { _ in return 0.51 } } } - func testStartTransaction_WhenProfilesSampleRateAndProfilesSamplerNil() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_WhenProfilesSampleRateAndProfilesSamplerNil() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampleRate = nil options.profilesSampler = { _ in return nil } } } - func testStartTransaction_WhenProfilesSamplerOutOfRange_TooBig() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_WhenProfilesSamplerOutOfRange_TooBig() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampler = { _ in return 1.01 } } } - func testStartTransaction_WhenProfilesSamplersOutOfRange_TooSmall() { - assertProfilesSampler(expectedDecision: .no) { options in + func testStartTransaction_WhenProfilesSamplersOutOfRange_TooSmall() throws { + try assertProfilesSampler(expectedDecision: .no) { options in options.profilesSampler = { _ in return -0.01 } } } } private extension SentryProfilerSwiftTests { - enum TestError: Error { - case unexpectedProfileDeserializationType - case unexpectedMeasurementsDeserializationType - case noEnvelopeCaptured - case noProfileEnvelopeItem - case malformedMetricValueEntry - case noMetricsReported - case noMetricValuesFound - } - func getLatestProfileData() throws -> Data { - guard let envelope = try XCTUnwrap(self.fixture.client).captureEventWithScopeInvocations.last else { - throw(TestError.noEnvelopeCaptured) - } + let envelope = try XCTUnwrap(self.fixture.client?.captureEventWithScopeInvocations.last) XCTAssertEqual(1, envelope.additionalEnvelopeItems.count) - guard let profileItem = envelope.additionalEnvelopeItems.first else { - throw(TestError.noProfileEnvelopeItem) - } + let profileItem = try XCTUnwrap(envelope.additionalEnvelopeItems.first) XCTAssertEqual("profile", profileItem.header.type) return profileItem.data } func getLatestTransaction() throws -> Transaction { - guard let envelope = try XCTUnwrap(self.fixture.client).captureEventWithScopeInvocations.last else { - throw(TestError.noEnvelopeCaptured) - } - + let envelope = try XCTUnwrap(self.fixture.client?.captureEventWithScopeInvocations.last) return try XCTUnwrap(envelope.event as? Transaction) } @@ -399,43 +411,23 @@ private extension SentryProfilerSwiftTests { SentrySDK.setAppStartMeasurement(appStartMeasurement) } - let originalTimeoutInterval = kSentryProfilerTimeoutInterval - if shouldTimeOut { - kSentryProfilerTimeoutInterval = 1 - } - let span = fixture.newTransaction(testingAppLaunchSpans: testingAppLaunchSpans) - + let span = try fixture.newTransaction(testingAppLaunchSpans: testingAppLaunchSpans) forceProfilerSample() - - let exp = expectation(description: "profiler should finish") + fixture.currentDateProvider.advance(by: 31) if shouldTimeOut { - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - span.finish() - exp.fulfill() - } - } else { - span.finish() - exp.fulfill() + fixture.timeoutTimerWrapper.fire() } - waitForExpectations(timeout: 10) + span.finish() try self.assertValidProfileData(transactionEnvironment: transactionEnvironment, shouldTimeout: shouldTimeOut) - - if shouldTimeOut { - kSentryProfilerTimeoutInterval = originalTimeoutInterval - } } func assertMetricsPayload(metricsBatches: Int = 1) throws { let profileData = try self.getLatestProfileData() let transaction = try getLatestTransaction() - guard let profile = try JSONSerialization.jsonObject(with: profileData) as? [String: Any] else { - throw TestError.unexpectedProfileDeserializationType - } - guard let measurements = profile["measurements"] as? [String: Any] else { - throw TestError.unexpectedMeasurementsDeserializationType - } + let profile = try XCTUnwrap(JSONSerialization.jsonObject(with: profileData) as? [String: Any]) + let measurements = try XCTUnwrap(profile["measurements"] as? [String: Any]) let expectedUsageReadings = fixture.mockUsageReadingsPerBatch * metricsBatches @@ -447,30 +439,57 @@ private extension SentryProfilerSwiftTests { try assertMetricValue(measurements: measurements, key: kSentryMetricProfilerSerializationKeyMemoryFootprint, numberOfReadings: expectedUsageReadings, expectedValue: fixture.mockMemoryFootprint, transaction: transaction) #if !os(macOS) - // can't elide the value argument, because assertMetricValue is generic and the parameter type won't be able to be inferred, so we provide a dummy variable/value - let dummyValue: UInt64? = nil - try assertMetricValue(measurements: measurements, key: kSentryProfilerSerializationKeySlowFrameRenders, numberOfReadings: fixture.mockSlowFramesPerBatch * metricsBatches, expectedValue: dummyValue, transaction: transaction) - try assertMetricValue(measurements: measurements, key: kSentryProfilerSerializationKeyFrozenFrameRenders, numberOfReadings: fixture.mockFrozenFramesPerBatch * metricsBatches, expectedValue: dummyValue, transaction: transaction) - - // need to add 1 frame rate reading because there is always an initial one for the frame rate when the frame tracker starts - let expectedFrameRateReadings = 1 + fixture.mockFrameRateChangesPerBatch.count * metricsBatches - try assertMetricValue(measurements: measurements, key: kSentryProfilerSerializationKeyFrameRates, numberOfReadings: expectedFrameRateReadings, expectedValue: dummyValue, transaction: transaction) + try assertMetricEntries(measurements: measurements, key: kSentryProfilerSerializationKeySlowFrameRenders, expectedEntries: fixture.expectedSlowFrames, transaction: transaction) + try assertMetricEntries(measurements: measurements, key: kSentryProfilerSerializationKeyFrozenFrameRenders, expectedEntries: fixture.expectedFrozenFrames, transaction: transaction) + try assertMetricEntries(measurements: measurements, key: kSentryProfilerSerializationKeyFrameRates, expectedEntries: fixture.expectedFrameRateChanges, transaction: transaction) #endif } - func assertMetricValue(measurements: [String: Any], key: String, numberOfReadings: Int, expectedValue: T?, transaction: Transaction) throws { - guard let metricContainer = measurements[key] as? [String: Any] else { - throw TestError.noMetricsReported + func sortedByTimestamps(_ entries: [[String: Any]]) -> [[String: Any]] { + entries.sorted { a, b in + UInt64(a["elapsed_since_start_ns"] as! String)! < UInt64(b["elapsed_since_start_ns"] as! String)! + } + } + + func printTimestamps(entries: [[String: Any]]) -> [NSString] { + entries.reduce(into: [NSString](), { partialResult, entry in + partialResult.append(entry["elapsed_since_start_ns"] as! NSString) + }) + } + + func assertMetricEntries(measurements: [String: Any], key: String, expectedEntries: [[String: Any]], transaction: Transaction) throws { + let metricContainer = try XCTUnwrap(measurements[key] as? [String: Any]) + let actualEntries = try XCTUnwrap(metricContainer["values"] as? [[String: Any]]) + let sortedActualEntries = sortedByTimestamps(actualEntries) + let sortedExpectedEntries = sortedByTimestamps(expectedEntries) + + guard actualEntries.count == expectedEntries.count else { + XCTFail("Wrong number of values under \(key). expected: \(printTimestamps(entries: sortedExpectedEntries)); actual: \(printTimestamps(entries: sortedActualEntries)); transaction start time: \(transaction.startSystemTime)") + return } - guard let values = metricContainer["values"] as? [[String: Any]] else { - throw TestError.malformedMetricValueEntry + + for i in 0..(measurements: [String: Any], key: String, numberOfReadings: Int, expectedValue: T? = nil, transaction: Transaction) throws { + let metricContainer = try XCTUnwrap(measurements[key] as? [String: Any]) + let values = try XCTUnwrap(metricContainer["values"] as? [[String: Any]]) XCTAssertEqual(values.count, numberOfReadings, "Wrong number of values under \(key)") if let expectedValue = expectedValue { - guard let actualValue = values[0]["value"] as? T else { - throw TestError.noMetricValuesFound - } + let actualValue = try XCTUnwrap(values[0]["value"] as? T) XCTAssertEqual(actualValue, expectedValue, "Wrong value for \(key)") let timestamp = try XCTUnwrap(values[0]["elapsed_since_start_ns"] as? NSString) @@ -596,7 +615,7 @@ private extension SentryProfilerSwiftTests { } } - func assertProfilesSampler(expectedDecision: SentrySampleDecision, options: (Options) -> Void) { + func assertProfilesSampler(expectedDecision: SentrySampleDecision, options: (Options) -> Void) throws { let fixtureOptions = fixture.options fixtureOptions.tracesSampleRate = 1.0 fixtureOptions.profilesSampleRate = nil @@ -607,7 +626,8 @@ private extension SentryProfilerSwiftTests { case .yes: return NSNumber(value: 1) @unknown default: - fatalError("Unexpected value for sample decision") + XCTFail("Unexpected value for sample decision") + return NSNumber(value: 0) } } options(fixtureOptions) @@ -615,37 +635,23 @@ private extension SentryProfilerSwiftTests { let hub = fixture.hub Dynamic(hub).tracesSampler.random = TestRandom(value: 1.0) - let span = fixture.newTransaction() - let exp = expectation(description: "Span finishes") - DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) { - span.finish() - - guard let client = self.fixture.client else { - XCTFail("Expected a valid test client to exist") - return - } + let span = try fixture.newTransaction() + forceProfilerSample() + fixture.currentDateProvider.advance(by: 5) + span.finish() - switch expectedDecision { - case .undecided, .no: - guard let event = client.captureEventWithScopeInvocations.first else { - XCTFail("Expected to capture at least 1 event, but without a profile") - return - } - XCTAssertEqual(0, event.additionalEnvelopeItems.count) - case .yes: - guard let event = client.captureEventWithScopeInvocations.first else { - XCTFail("Expected to capture at least 1 event with a profile") - return - } - XCTAssertEqual(1, event.additionalEnvelopeItems.count) - @unknown default: - fatalError("Unexpected value for sample decision") - } + let client = try XCTUnwrap(self.fixture.client) - exp.fulfill() + switch expectedDecision { + case .undecided, .no: + let event = try XCTUnwrap(client.captureEventWithScopeInvocations.first) + XCTAssertEqual(0, event.additionalEnvelopeItems.count) + case .yes: + let event = try XCTUnwrap(client.captureEventWithScopeInvocations.first) + XCTAssertEqual(1, event.additionalEnvelopeItems.count) + @unknown default: + XCTFail("Unexpected value for sample decision") } - - waitForExpectations(timeout: 3) } } #endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) diff --git a/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h b/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h index 35d8aae8b25..9e050c8b14a 100644 --- a/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h +++ b/Tests/SentryTests/Helper/SentryProfiler+SwiftTest.h @@ -21,7 +21,11 @@ SentryProfiler () + (void)useProcessInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper NS_SWIFT_NAME(useProcessInfoWrapper(_:)); -+ (void)useTimerWrapper:(SentryNSTimerWrapper *)timerWrapper NS_SWIFT_NAME(useTimerWrapper(_:)); ++ (void)useMetricTimerWrapper:(SentryNSTimerWrapper *)timerWrapper + NS_SWIFT_NAME(useMetricTimerWrapper(_:)); + ++ (void)useTimeoutTimerWrapper:(SentryNSTimerWrapper *)timerWrapper + NS_SWIFT_NAME(useTimeoutTimerWrapper(_:)); # if SENTRY_HAS_UIKIT + (void)useFramesTracker:(SentryFramesTracker *)framesTracker NS_SWIFT_NAME(useFramesTracker(_:)); diff --git a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h index 73078665b5b..682921307ba 100644 --- a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h +++ b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTracker+TestInit.h @@ -1,8 +1,12 @@ +#import "SentryDefines.h" #import "SentryFramesTracker.h" NS_ASSUME_NONNULL_BEGIN #if SENTRY_HAS_UIKIT +SENTRY_EXTERN double slowFrameThreshold(uint64_t actualFramesPerSecond); +SENTRY_EXTERN CFTimeInterval const SentryFrozenFrameThreshold; + @interface SentryFramesTracker (TestInit) diff --git a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift index 080d75e9746..24d51cf3aa0 100644 --- a/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift +++ b/Tests/SentryTests/Integrations/Performance/FramesTracking/SentryFramesTrackerTests.swift @@ -46,9 +46,9 @@ class SentryFramesTrackerTests: XCTestCase { sut.start() fixture.displayLinkWrapper.call() - fixture.displayLinkWrapper.slowFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() fixture.displayLinkWrapper.normalFrame() - fixture.displayLinkWrapper.almostFrozenFrame() + _ = fixture.displayLinkWrapper.slowestSlowFrame() try assert(slow: 2, frozen: 0, total: 3) } @@ -58,8 +58,8 @@ class SentryFramesTrackerTests: XCTestCase { sut.start() fixture.displayLinkWrapper.call() - fixture.displayLinkWrapper.slowFrame() - fixture.displayLinkWrapper.frozenFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() + _ = fixture.displayLinkWrapper.fastestFrozenFrame() try assert(slow: 1, frozen: 1, total: 2) } @@ -69,9 +69,9 @@ class SentryFramesTrackerTests: XCTestCase { sut.start() fixture.displayLinkWrapper.call() - fixture.displayLinkWrapper.slowFrame() - fixture.displayLinkWrapper.changeFrameRate(120.0) - fixture.displayLinkWrapper.frozenFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() + fixture.displayLinkWrapper.changeFrameRate(.high) + _ = fixture.displayLinkWrapper.fastestFrozenFrame() try assert(slow: 1, frozen: 1, total: 2, frameRates: 2) } @@ -92,8 +92,8 @@ class SentryFramesTrackerTests: XCTestCase { let frames: UInt = 600_000 for _ in 0 ..< frames { fixture.displayLinkWrapper.normalFrame() - fixture.displayLinkWrapper.slowFrame() - fixture.displayLinkWrapper.frozenFrame() + _ = fixture.displayLinkWrapper.fastestSlowFrame() + _ = fixture.displayLinkWrapper.fastestFrozenFrame() } group.wait() @@ -186,10 +186,9 @@ private extension SentryFramesTrackerTests { #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) func assertProfilingData(slow: UInt? = nil, frozen: UInt? = nil, frameRates: UInt? = nil) throws { - func assertStartAndEndOrdering(frame: [String: NSNumber]) throws { - let start = try XCTUnwrap(frame["start_timestamp"], "Expected a start timestamp for the frame.") - let end = try XCTUnwrap(frame["end_timestamp"], "Expected an end timestamp for the frame.") - XCTAssert(start.compare(end) != .orderedDescending) + func assertFrameInfo(frame: [String: NSNumber]) throws { + XCTAssertNotNil(frame["timestamp"], "Expected a timestamp for the frame.") + XCTAssertNotNil(frame["value"], "Expected a duration value for the frame.") } let currentFrames = fixture.sut.currentFrames @@ -197,13 +196,13 @@ private extension SentryFramesTrackerTests { if let slow = slow { XCTAssertEqual(currentFrames.slowFrameTimestamps.count, Int(slow)) for frame in currentFrames.slowFrameTimestamps { - try assertStartAndEndOrdering(frame: frame) + try assertFrameInfo(frame: frame) } } if let frozen = frozen { XCTAssertEqual(currentFrames.frozenFrameTimestamps.count, Int(frozen)) for frame in currentFrames.frozenFrameTimestamps { - try assertStartAndEndOrdering(frame: frame) + try assertFrameInfo(frame: frame) } } if let frameRates = frameRates { diff --git a/Tests/SentryTests/TestUtils/TestExtensions.swift b/Tests/SentryTests/TestUtils/TestExtensions.swift index 52c8cf39476..8be0dd39823 100644 --- a/Tests/SentryTests/TestUtils/TestExtensions.swift +++ b/Tests/SentryTests/TestUtils/TestExtensions.swift @@ -1,18 +1,6 @@ import Foundation import XCTest -extension TimeInterval { - func toNanoSeconds() -> UInt64 { - return UInt64(self * Double(NSEC_PER_SEC)) - } -} - -extension UInt64 { - func toTimeInterval() -> TimeInterval { - return Double(self) / Double(NSEC_PER_SEC) - } -} - extension XCTest { func contentsOfResource(_ resource: String, ofType: String = "json") throws -> Data { let path = Bundle(for: type(of: self)).path(forResource: "Resources/\(resource)", ofType: "json") diff --git a/Tests/SentryTests/Transaction/SentryTracer+Test.h b/Tests/SentryTests/Transaction/SentryTracer+Test.h index 01a4b2e1173..3444b3f8b46 100644 --- a/Tests/SentryTests/Transaction/SentryTracer+Test.h +++ b/Tests/SentryTests/Transaction/SentryTracer+Test.h @@ -1,4 +1,4 @@ - +#import "SentryProfilingConditionals.h" #import "SentryTracer.h" NS_ASSUME_NONNULL_BEGIN @@ -10,6 +10,10 @@ SentryTracer (Test) - (void)updateStartTime:(NSDate *)startTime; +#if SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) ++ (void)resetConcurrencyTracking; +#endif // SENTRY_TARGET_PROFILING_SUPPORTED && (defined(TEST) || defined(TESTCI)) + @end NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/Transaction/SentryTracerTests.swift b/Tests/SentryTests/Transaction/SentryTracerTests.swift index acf4365b92d..ebfd7a99a27 100644 --- a/Tests/SentryTests/Transaction/SentryTracerTests.swift +++ b/Tests/SentryTests/Transaction/SentryTracerTests.swift @@ -209,7 +209,7 @@ class SentryTracerTests: XCTestCase { let sut = fixture.getSut() sut.finish() - XCTAssertFalse(fixture.timerWrapper.timer.isValid) + XCTAssertFalse(fixture.timerWrapper.overrides.timer.isValid) } func testFinish_CheckDefaultStatus() { From c00eafe9f4ce7a9bfc0ca8bea1e1851fe4ab53f2 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Mon, 17 Apr 2023 14:09:19 -0800 Subject: [PATCH 41/42] meta: fix a changelog entry and add one for GPU timestamps (#2923) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95fb3c4d277..350406f2fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ ### Fixes - Crash when serializing invalid objects (#2858) -- Don't send screenshots with either width or height of 0 (#2876)) +- Don't send screenshots with either width or height of 0 (#2876) +- GPU frame alignment with stack traces in profiles (#2856) ## 8.4.0 From 2479b6f7ff69b66bcdea82184d097667e63828ed Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Mon, 17 Apr 2023 23:51:46 +0000 Subject: [PATCH 42/42] release: 8.5.0 --- CHANGELOG.md | 2 +- Sentry.podspec | 4 ++-- SentryPrivate.podspec | 2 +- SentrySwiftUI.podspec | 4 ++-- Sources/Configuration/Sentry.xcconfig | 2 +- Sources/Configuration/SentryPrivate.xcconfig | 2 +- Sources/Sentry/SentryMeta.m | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 350406f2fd1..77a9b8ee8f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 8.5.0 ### Features diff --git a/Sentry.podspec b/Sentry.podspec index 37dcbb00f09..d9e36231188 100644 --- a/Sentry.podspec +++ b/Sentry.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Sentry" - s.version = "8.4.0" + s.version = "8.5.0" s.summary = "Sentry client for cocoa" s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" @@ -27,7 +27,7 @@ Pod::Spec.new do |s| } s.default_subspecs = ['Core'] - s.dependency "SentryPrivate", "8.4.0" + s.dependency "SentryPrivate", "8.5.0" s.subspec 'Core' do |sp| sp.source_files = "Sources/Sentry/**/*.{h,hpp,m,mm,c,cpp}", diff --git a/SentryPrivate.podspec b/SentryPrivate.podspec index 55c5e7998d1..01b0fb283db 100644 --- a/SentryPrivate.podspec +++ b/SentryPrivate.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SentryPrivate" - s.version = "8.4.0" + s.version = "8.5.0" s.summary = "Sentry Private Library." s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" diff --git a/SentrySwiftUI.podspec b/SentrySwiftUI.podspec index 637d7c6ab42..29349fe1ef3 100644 --- a/SentrySwiftUI.podspec +++ b/SentrySwiftUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SentrySwiftUI" - s.version = "8.4.0" + s.version = "8.5.0" s.summary = "Sentry client for SwiftUI" s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.watchos.framework = 'WatchKit' s.source_files = "Sources/SentrySwiftUI/**/*.{swift,h,m}" - s.dependency 'Sentry', "8.4.0" + s.dependency 'Sentry', "8.5.0" end diff --git a/Sources/Configuration/Sentry.xcconfig b/Sources/Configuration/Sentry.xcconfig index 6ca539ff207..61103651a8a 100644 --- a/Sources/Configuration/Sentry.xcconfig +++ b/Sources/Configuration/Sentry.xcconfig @@ -2,6 +2,6 @@ PRODUCT_NAME = Sentry INFOPLIST_FILE = Sources/Sentry/Info.plist PRODUCT_BUNDLE_IDENTIFIER = io.sentry.Sentry -CURRENT_PROJECT_VERSION = 8.4.0 +CURRENT_PROJECT_VERSION = 8.5.0 MODULEMAP_FILE = $(SRCROOT)/Sources/Sentry/Sentry.modulemap diff --git a/Sources/Configuration/SentryPrivate.xcconfig b/Sources/Configuration/SentryPrivate.xcconfig index 4ec32be3fad..e5cf62409c8 100644 --- a/Sources/Configuration/SentryPrivate.xcconfig +++ b/Sources/Configuration/SentryPrivate.xcconfig @@ -1,3 +1,3 @@ PRODUCT_NAME = SentryPrivate MACH_O_TYPE = staticlib -CURRENT_PROJECT_VERSION = 8.4.0 +CURRENT_PROJECT_VERSION = 8.5.0 diff --git a/Sources/Sentry/SentryMeta.m b/Sources/Sentry/SentryMeta.m index 027ff89943f..d5ad6f3231f 100644 --- a/Sources/Sentry/SentryMeta.m +++ b/Sources/Sentry/SentryMeta.m @@ -5,7 +5,7 @@ @implementation SentryMeta // Don't remove the static keyword. If you do the compiler adds the constant name to the global // symbol table and it might clash with other constants. When keeping the static keyword the // compiler replaces all occurrences with the value. -static NSString *versionString = @"8.4.0"; +static NSString *versionString = @"8.5.0"; static NSString *sdkName = @"sentry.cocoa"; + (NSString *)versionString