diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 74a3149bd..84cac9e69 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -138,6 +138,7 @@ steps: docker-compose#v3.3.0: run: cocoa-maze-runner command: + - "features/app_hangs.feature" - "features/barebone_tests.feature" - "--app=/app/build/iOSTestApp.ipa" - "--farm=bs" @@ -164,6 +165,7 @@ steps: docker-compose#v3.3.0: run: cocoa-maze-runner command: + - "features/app_hangs.feature" - "features/barebone_tests.feature" - "--app=/app/build/iOSTestApp.ipa" - "--farm=bs" @@ -190,6 +192,7 @@ steps: commands: - bundle install - bundle exec maze-runner + features/app_hangs.feature features/barebone_tests.feature --farm=local --os=macos @@ -198,5 +201,20 @@ steps: --tags='not @skip_macos' --fail-fast + - label: 'macOS 10.15 stress test' + timeout_in_minutes: 3 + agents: + queue: opensource-mac-cocoa + env: + STRESS_TEST: "true" + commands: + - bundle install + - bundle exec maze-runner + features/stress_test.feature + --no-log-requests + artifact_paths: + - features/fixtures/macos-stress-test/BugsnagStressTest.stdout.log + - features/fixtures/macos-stress-test/BugsnagStressTest.stderr.log + - label: 'Conditionally trigger full set of tests' command: sh -c .buildkite/pipeline_trigger.sh diff --git a/Bugsnag.xcodeproj/project.pbxproj b/Bugsnag.xcodeproj/project.pbxproj index c837c0ea3..733334fda 100644 --- a/Bugsnag.xcodeproj/project.pbxproj +++ b/Bugsnag.xcodeproj/project.pbxproj @@ -58,9 +58,6 @@ 0089671E2486D43700DC48C2 /* report.json in Resources */ = {isa = PBXBuildFile; fileRef = 008966B72486D43500DC48C2 /* report.json */; }; 0089671F2486D43700DC48C2 /* report.json in Resources */ = {isa = PBXBuildFile; fileRef = 008966B72486D43500DC48C2 /* report.json */; }; 008967202486D43700DC48C2 /* report.json in Resources */ = {isa = PBXBuildFile; fileRef = 008966B72486D43500DC48C2 /* report.json */; }; - 008967212486D43700DC48C2 /* BugsnagErrorReportSinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966B82486D43500DC48C2 /* BugsnagErrorReportSinkTests.m */; }; - 008967222486D43700DC48C2 /* BugsnagErrorReportSinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966B82486D43500DC48C2 /* BugsnagErrorReportSinkTests.m */; }; - 008967232486D43700DC48C2 /* BugsnagErrorReportSinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966B82486D43500DC48C2 /* BugsnagErrorReportSinkTests.m */; }; 008967272486D43700DC48C2 /* BugsnagStackframeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966BA2486D43500DC48C2 /* BugsnagStackframeTest.m */; }; 008967282486D43700DC48C2 /* BugsnagStackframeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966BA2486D43500DC48C2 /* BugsnagStackframeTest.m */; }; 008967292486D43700DC48C2 /* BugsnagStackframeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966BA2486D43500DC48C2 /* BugsnagStackframeTest.m */; }; @@ -150,12 +147,6 @@ 008967872486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DD2486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m */; }; 008967882486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DD2486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m */; }; 008967892486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DD2486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m */; }; - 0089678A2486D43700DC48C2 /* KSCrashReportStore_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DE2486D43700DC48C2 /* KSCrashReportStore_Tests.m */; }; - 0089678B2486D43700DC48C2 /* KSCrashReportStore_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DE2486D43700DC48C2 /* KSCrashReportStore_Tests.m */; }; - 0089678C2486D43700DC48C2 /* KSCrashReportStore_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DE2486D43700DC48C2 /* KSCrashReportStore_Tests.m */; }; - 0089678D2486D43700DC48C2 /* KSCrashReportConverter_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DF2486D43700DC48C2 /* KSCrashReportConverter_Tests.m */; }; - 0089678E2486D43700DC48C2 /* KSCrashReportConverter_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DF2486D43700DC48C2 /* KSCrashReportConverter_Tests.m */; }; - 0089678F2486D43700DC48C2 /* KSCrashReportConverter_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966DF2486D43700DC48C2 /* KSCrashReportConverter_Tests.m */; }; 008967902486D43700DC48C2 /* KSJSONCodec_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966E02486D43700DC48C2 /* KSJSONCodec_Tests.m */; }; 008967912486D43700DC48C2 /* KSJSONCodec_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966E02486D43700DC48C2 /* KSJSONCodec_Tests.m */; }; 008967922486D43700DC48C2 /* KSJSONCodec_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 008966E02486D43700DC48C2 /* KSJSONCodec_Tests.m */; }; @@ -237,16 +228,9 @@ 008968032486DA4500DC48C2 /* BSGConnectivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F02486DA4500DC48C2 /* BSGConnectivity.m */; }; 008968042486DA4500DC48C2 /* BSGConnectivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F02486DA4500DC48C2 /* BSGConnectivity.m */; }; 008968052486DA4500DC48C2 /* BSGConnectivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F02486DA4500DC48C2 /* BSGConnectivity.m */; }; - 008968062486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F12486DA4500DC48C2 /* BugsnagErrorReportApiClient.m */; }; - 008968072486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F12486DA4500DC48C2 /* BugsnagErrorReportApiClient.m */; }; - 008968082486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F12486DA4500DC48C2 /* BugsnagErrorReportApiClient.m */; }; - 008968092486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 008967F12486DA4500DC48C2 /* BugsnagErrorReportApiClient.m */; }; 0089680A2486DA4500DC48C2 /* BSGConnectivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 008967F22486DA4500DC48C2 /* BSGConnectivity.h */; }; 0089680B2486DA4500DC48C2 /* BSGConnectivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 008967F22486DA4500DC48C2 /* BSGConnectivity.h */; }; 0089680C2486DA4500DC48C2 /* BSGConnectivity.h in Headers */ = {isa = PBXBuildFile; fileRef = 008967F22486DA4500DC48C2 /* BSGConnectivity.h */; }; - 0089680D2486DA4500DC48C2 /* BugsnagErrorReportApiClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 008967F32486DA4500DC48C2 /* BugsnagErrorReportApiClient.h */; }; - 0089680E2486DA4500DC48C2 /* BugsnagErrorReportApiClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 008967F32486DA4500DC48C2 /* BugsnagErrorReportApiClient.h */; }; - 0089680F2486DA4500DC48C2 /* BugsnagErrorReportApiClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 008967F32486DA4500DC48C2 /* BugsnagErrorReportApiClient.h */; }; 008968182486DA5600DC48C2 /* BugsnagCollections.h in Headers */ = {isa = PBXBuildFile; fileRef = 008968102486DA5600DC48C2 /* BugsnagCollections.h */; }; 008968192486DA5600DC48C2 /* BugsnagCollections.h in Headers */ = {isa = PBXBuildFile; fileRef = 008968102486DA5600DC48C2 /* BugsnagCollections.h */; }; 0089681A2486DA5600DC48C2 /* BugsnagCollections.h in Headers */ = {isa = PBXBuildFile; fileRef = 008968102486DA5600DC48C2 /* BugsnagCollections.h */; }; @@ -501,9 +485,6 @@ 008969D52486DAD100DC48C2 /* BSG_KSCrashType.c in Sources */ = {isa = PBXBuildFile; fileRef = 0089692B2486DAD000DC48C2 /* BSG_KSCrashType.c */; }; 008969D62486DAD100DC48C2 /* BSG_KSCrashType.c in Sources */ = {isa = PBXBuildFile; fileRef = 0089692B2486DAD000DC48C2 /* BSG_KSCrashType.c */; }; 008969D72486DAD100DC48C2 /* BSG_KSCrashType.c in Sources */ = {isa = PBXBuildFile; fileRef = 0089692B2486DAD000DC48C2 /* BSG_KSCrashType.c */; }; - 008969D82486DAD100DC48C2 /* BSG_KSCrashReportStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089692C2486DAD000DC48C2 /* BSG_KSCrashReportStore.m */; }; - 008969D92486DAD100DC48C2 /* BSG_KSCrashReportStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089692C2486DAD000DC48C2 /* BSG_KSCrashReportStore.m */; }; - 008969DA2486DAD100DC48C2 /* BSG_KSCrashReportStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089692C2486DAD000DC48C2 /* BSG_KSCrashReportStore.m */; }; 008969DB2486DAD100DC48C2 /* BSG_KSCrash.h in Headers */ = {isa = PBXBuildFile; fileRef = 0089692D2486DAD000DC48C2 /* BSG_KSCrash.h */; }; 008969DC2486DAD100DC48C2 /* BSG_KSCrash.h in Headers */ = {isa = PBXBuildFile; fileRef = 0089692D2486DAD000DC48C2 /* BSG_KSCrash.h */; }; 008969DD2486DAD100DC48C2 /* BSG_KSCrash.h in Headers */ = {isa = PBXBuildFile; fileRef = 0089692D2486DAD000DC48C2 /* BSG_KSCrash.h */; }; @@ -540,9 +521,6 @@ 008969FC2486DAD100DC48C2 /* BSG_KSCrashAdvanced.h in Headers */ = {isa = PBXBuildFile; fileRef = 008969382486DAD000DC48C2 /* BSG_KSCrashAdvanced.h */; }; 008969FD2486DAD100DC48C2 /* BSG_KSCrashAdvanced.h in Headers */ = {isa = PBXBuildFile; fileRef = 008969382486DAD000DC48C2 /* BSG_KSCrashAdvanced.h */; }; 008969FE2486DAD100DC48C2 /* BSG_KSCrashAdvanced.h in Headers */ = {isa = PBXBuildFile; fileRef = 008969382486DAD000DC48C2 /* BSG_KSCrashAdvanced.h */; }; - 008969FF2486DAD100DC48C2 /* BSG_KSCrashReportStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 008969392486DAD000DC48C2 /* BSG_KSCrashReportStore.h */; }; - 00896A002486DAD100DC48C2 /* BSG_KSCrashReportStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 008969392486DAD000DC48C2 /* BSG_KSCrashReportStore.h */; }; - 00896A012486DAD100DC48C2 /* BSG_KSCrashReportStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 008969392486DAD000DC48C2 /* BSG_KSCrashReportStore.h */; }; 00896A022486DAD100DC48C2 /* BSG_KSCrashSentry_NSException.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089693B2486DAD000DC48C2 /* BSG_KSCrashSentry_NSException.m */; }; 00896A032486DAD100DC48C2 /* BSG_KSCrashSentry_NSException.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089693B2486DAD000DC48C2 /* BSG_KSCrashSentry_NSException.m */; }; 00896A042486DAD100DC48C2 /* BSG_KSCrashSentry_NSException.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089693B2486DAD000DC48C2 /* BSG_KSCrashSentry_NSException.m */; }; @@ -609,11 +587,6 @@ 00AD1F102486A17900A27979 /* BugsnagSessionTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */; }; 00AD1F112486A17900A27979 /* BugsnagSessionTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */; }; 00AD1F122486A17900A27979 /* BugsnagSessionTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */; }; - 00AD1F142486A17900A27979 /* BugsnagErrorReportSink.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EF92486A17700A27979 /* BugsnagErrorReportSink.m */; }; - 00AD1F152486A17900A27979 /* BugsnagErrorReportSink.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EF92486A17700A27979 /* BugsnagErrorReportSink.m */; }; - 00AD1F162486A17900A27979 /* BugsnagErrorReportSink.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EF92486A17700A27979 /* BugsnagErrorReportSink.m */; }; - 00AD1F212486A17900A27979 /* BugsnagErrorReportSink.h in Headers */ = {isa = PBXBuildFile; fileRef = 00AD1EFD2486A17800A27979 /* BugsnagErrorReportSink.h */; }; - 00AD1F222486A17900A27979 /* BugsnagErrorReportSink.h in Headers */ = {isa = PBXBuildFile; fileRef = 00AD1EFD2486A17800A27979 /* BugsnagErrorReportSink.h */; }; 00AD1F232486A17900A27979 /* Bugsnag.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EFE2486A17800A27979 /* Bugsnag.m */; }; 00AD1F242486A17900A27979 /* Bugsnag.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EFE2486A17800A27979 /* Bugsnag.m */; }; 00AD1F252486A17900A27979 /* Bugsnag.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EFE2486A17800A27979 /* Bugsnag.m */; }; @@ -630,6 +603,13 @@ 00AD1F302486A17900A27979 /* BugsnagSessionTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1F012486A17900A27979 /* BugsnagSessionTracker.m */; }; 00AD1F312486A17900A27979 /* BugsnagSessionTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1F012486A17900A27979 /* BugsnagSessionTracker.m */; }; 00E636C224878D84006CBF1A /* BSG_RFC3339DateTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 008969142486DAD000DC48C2 /* BSG_RFC3339DateTool.m */; }; + 010FF28425ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */ = {isa = PBXBuildFile; fileRef = 010FF28225ED2A8D00E4F2B0 /* BSGAppHangDetector.h */; }; + 010FF28525ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */ = {isa = PBXBuildFile; fileRef = 010FF28225ED2A8D00E4F2B0 /* BSGAppHangDetector.h */; }; + 010FF28625ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */ = {isa = PBXBuildFile; fileRef = 010FF28225ED2A8D00E4F2B0 /* BSGAppHangDetector.h */; }; + 010FF28725ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */; }; + 010FF28825ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */; }; + 010FF28925ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */; }; + 010FF28A25ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */; }; 01210B8825CD665000D683BB /* BugsnagThread+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = 01210B8725CD665000D683BB /* BugsnagThread+Recording.m */; }; 01210B8925CD665000D683BB /* BugsnagThread+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = 01210B8725CD665000D683BB /* BugsnagThread+Recording.m */; }; 01210B8A25CD665000D683BB /* BugsnagThread+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = 01210B8725CD665000D683BB /* BugsnagThread+Recording.m */; }; @@ -637,6 +617,41 @@ 0126DF1B257A92860031A70C /* BugsnagSession+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126DF1A257A92860031A70C /* BugsnagSession+Private.h */; }; 0126DF1C257A92860031A70C /* BugsnagSession+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126DF1A257A92860031A70C /* BugsnagSession+Private.h */; }; 0126DF1D257A92860031A70C /* BugsnagSession+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126DF1A257A92860031A70C /* BugsnagSession+Private.h */; }; + 0126F78B25DD508C008483C2 /* BSGEventUploadOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F78925DD508C008483C2 /* BSGEventUploadOperation.h */; }; + 0126F78C25DD508C008483C2 /* BSGEventUploadOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F78925DD508C008483C2 /* BSGEventUploadOperation.h */; }; + 0126F78D25DD508C008483C2 /* BSGEventUploadOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F78925DD508C008483C2 /* BSGEventUploadOperation.h */; }; + 0126F78E25DD508C008483C2 /* BSGEventUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F78A25DD508C008483C2 /* BSGEventUploadOperation.m */; }; + 0126F78F25DD508C008483C2 /* BSGEventUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F78A25DD508C008483C2 /* BSGEventUploadOperation.m */; }; + 0126F79025DD508C008483C2 /* BSGEventUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F78A25DD508C008483C2 /* BSGEventUploadOperation.m */; }; + 0126F79125DD508C008483C2 /* BSGEventUploadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F78A25DD508C008483C2 /* BSGEventUploadOperation.m */; }; + 0126F79B25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F79925DD510E008483C2 /* BSGEventUploadObjectOperation.h */; }; + 0126F79C25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F79925DD510E008483C2 /* BSGEventUploadObjectOperation.h */; }; + 0126F79D25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F79925DD510E008483C2 /* BSGEventUploadObjectOperation.h */; }; + 0126F79E25DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F79A25DD510E008483C2 /* BSGEventUploadObjectOperation.m */; }; + 0126F79F25DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F79A25DD510E008483C2 /* BSGEventUploadObjectOperation.m */; }; + 0126F7A025DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F79A25DD510E008483C2 /* BSGEventUploadObjectOperation.m */; }; + 0126F7A125DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F79A25DD510E008483C2 /* BSGEventUploadObjectOperation.m */; }; + 0126F7AB25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F7A925DD5118008483C2 /* BSGEventUploadFileOperation.h */; }; + 0126F7AC25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F7A925DD5118008483C2 /* BSGEventUploadFileOperation.h */; }; + 0126F7AD25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F7A925DD5118008483C2 /* BSGEventUploadFileOperation.h */; }; + 0126F7AE25DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7AA25DD5118008483C2 /* BSGEventUploadFileOperation.m */; }; + 0126F7AF25DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7AA25DD5118008483C2 /* BSGEventUploadFileOperation.m */; }; + 0126F7B025DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7AA25DD5118008483C2 /* BSGEventUploadFileOperation.m */; }; + 0126F7B125DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7AA25DD5118008483C2 /* BSGEventUploadFileOperation.m */; }; + 0126F7BB25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F7B925DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h */; }; + 0126F7BC25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F7B925DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h */; }; + 0126F7BD25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0126F7B925DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h */; }; + 0126F7BE25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; + 0126F7BF25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; + 0126F7C025DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; + 0126F7C125DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; + 0127149225F6171000D3500A /* BugsnagClient+AppHangs.h in Headers */ = {isa = PBXBuildFile; fileRef = 0127149025F6171000D3500A /* BugsnagClient+AppHangs.h */; }; + 0127149325F6171000D3500A /* BugsnagClient+AppHangs.h in Headers */ = {isa = PBXBuildFile; fileRef = 0127149025F6171000D3500A /* BugsnagClient+AppHangs.h */; }; + 0127149425F6171000D3500A /* BugsnagClient+AppHangs.h in Headers */ = {isa = PBXBuildFile; fileRef = 0127149025F6171000D3500A /* BugsnagClient+AppHangs.h */; }; + 0127149525F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0127149125F6171000D3500A /* BugsnagClient+AppHangs.m */; }; + 0127149625F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0127149125F6171000D3500A /* BugsnagClient+AppHangs.m */; }; + 0127149725F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0127149125F6171000D3500A /* BugsnagClient+AppHangs.m */; }; + 0127149825F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0127149125F6171000D3500A /* BugsnagClient+AppHangs.m */; }; 0140D29A25767C9A00FD0306 /* BugsnagApiClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */; }; 01447605256684500018AB94 /* BugsnagApiClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */; }; 01468F5225876DC1002B0519 /* BSGNotificationBreadcrumbs.h in Headers */ = {isa = PBXBuildFile; fileRef = 01468F5025876DC1002B0519 /* BSGNotificationBreadcrumbs.h */; }; @@ -656,11 +671,24 @@ 016875C6258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */; }; 016875C7258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */; }; 016875C8258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */; }; + 01840B6F25DC26E200F95648 /* BSGEventUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 01840B6D25DC26E200F95648 /* BSGEventUploader.h */; }; + 01840B7025DC26E200F95648 /* BSGEventUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 01840B6D25DC26E200F95648 /* BSGEventUploader.h */; }; + 01840B7125DC26E200F95648 /* BSGEventUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 01840B6D25DC26E200F95648 /* BSGEventUploader.h */; }; + 01840B7225DC26E200F95648 /* BSGEventUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 01840B6E25DC26E200F95648 /* BSGEventUploader.m */; }; + 01840B7325DC26E200F95648 /* BSGEventUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 01840B6E25DC26E200F95648 /* BSGEventUploader.m */; }; + 01840B7425DC26E200F95648 /* BSGEventUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 01840B6E25DC26E200F95648 /* BSGEventUploader.m */; }; + 01840B7525DC26E200F95648 /* BSGEventUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 01840B6E25DC26E200F95648 /* BSGEventUploader.m */; }; 0187D464255BD7B800C503D9 /* BugsnagApiClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */; }; 01B14C56251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B14C57251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B14C58251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B6BB7E25D5777F00FC4DE6 /* BugsnagSwiftPublicAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008966B02486D43500DC48C2 /* BugsnagSwiftPublicAPITests.swift */; }; + 01BDB1F525DEBFB200A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */; }; + 01BDB1FD25DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */; }; + 01BDB20525DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */; }; + 01BDB21525DEC02900A91FAF /* Data in Resources */ = {isa = PBXBuildFile; fileRef = 01BDB21425DEC02900A91FAF /* Data */; }; + 01BDB21625DEC02900A91FAF /* Data in Resources */ = {isa = PBXBuildFile; fileRef = 01BDB21425DEC02900A91FAF /* Data */; }; + 01BDB21725DEC02900A91FAF /* Data in Resources */ = {isa = PBXBuildFile; fileRef = 01BDB21425DEC02900A91FAF /* Data */; }; 01C17AE72542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */; }; 01C17AE82542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */; }; 01C17AE92542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */; }; @@ -830,7 +858,6 @@ E7462900248907C100F92D67 /* NSError+BSG_SimpleConstructor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089691E2486DAD000DC48C2 /* NSError+BSG_SimpleConstructor.m */; }; E7462901248907C100F92D67 /* BSG_KSLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 008969262486DAD000DC48C2 /* BSG_KSLogger.m */; }; E7462902248907C100F92D67 /* BSG_KSCrashState.m in Sources */ = {isa = PBXBuildFile; fileRef = 008969292486DAD000DC48C2 /* BSG_KSCrashState.m */; }; - E7462903248907C100F92D67 /* BSG_KSCrashReportStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089692C2486DAD000DC48C2 /* BSG_KSCrashReportStore.m */; }; E7462904248907C100F92D67 /* BSG_KSCrashIdentifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 008969302486DAD000DC48C2 /* BSG_KSCrashIdentifier.m */; }; E7462905248907C100F92D67 /* BSG_KSCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = 008969362486DAD000DC48C2 /* BSG_KSCrash.m */; }; E7462906248907C100F92D67 /* BSG_KSCrashSentry_NSException.m in Sources */ = {isa = PBXBuildFile; fileRef = 0089693B2486DAD000DC48C2 /* BSG_KSCrashSentry_NSException.m */; }; @@ -875,7 +902,6 @@ E746294124890D2F00F92D67 /* BSGConfigurationBuilder.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008967CA2486DA2D00DC48C2 /* BSGConfigurationBuilder.h */; }; E746294524890D2F00F92D67 /* BSGConnectivity.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008967F22486DA4500DC48C2 /* BSGConnectivity.h */; }; E746294624890D2F00F92D67 /* BugsnagApiClient.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008967ED2486DA4400DC48C2 /* BugsnagApiClient.h */; }; - E746294724890D2F00F92D67 /* BugsnagErrorReportApiClient.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008967F32486DA4500DC48C2 /* BugsnagErrorReportApiClient.h */; }; E746294824890D2F00F92D67 /* BugsnagSessionTrackingApiClient.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008967EC2486DA4400DC48C2 /* BugsnagSessionTrackingApiClient.h */; }; E746294924890D2F00F92D67 /* BSGSerialization.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008968112486DA5600DC48C2 /* BSGSerialization.h */; }; E746294A24890D2F00F92D67 /* BugsnagCollections.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008968102486DA5600DC48C2 /* BugsnagCollections.h */; }; @@ -920,7 +946,6 @@ E746298424890D3200F92D67 /* BSG_KSCrashState.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008969352486DAD000DC48C2 /* BSG_KSCrashState.h */; }; E746298524890D3200F92D67 /* BSG_KSCrashReportVersion.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008969372486DAD000DC48C2 /* BSG_KSCrashReportVersion.h */; }; E746298624890D3200F92D67 /* BSG_KSCrashAdvanced.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008969382486DAD000DC48C2 /* BSG_KSCrashAdvanced.h */; }; - E746298724890D3200F92D67 /* BSG_KSCrashReportStore.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 008969392486DAD000DC48C2 /* BSG_KSCrashReportStore.h */; }; E746298824890D3200F92D67 /* BSG_KSCrashSentry_MachException.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0089693C2486DAD000DC48C2 /* BSG_KSCrashSentry_MachException.h */; }; E746298924890D3200F92D67 /* BSG_KSCrashSentry_Private.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0089693D2486DAD000DC48C2 /* BSG_KSCrashSentry_Private.h */; }; E746298A24890D3200F92D67 /* BSG_KSCrashSentry.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0089693F2486DAD000DC48C2 /* BSG_KSCrashSentry.h */; }; @@ -932,9 +957,6 @@ E746299024890D3200F92D67 /* BSG_KSCrashIdentifier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0089694A2486DAD000DC48C2 /* BSG_KSCrashIdentifier.h */; }; E746299524890D3200F92D67 /* BugsnagCrashSentry.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00AD1F002486A17900A27979 /* BugsnagCrashSentry.h */; }; E746299624890D3200F92D67 /* BugsnagSessionTracker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */; }; - E746299724890D3200F92D67 /* BugsnagErrorReportSink.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00AD1EFD2486A17800A27979 /* BugsnagErrorReportSink.h */; }; - E75A5CDB248A5D97005D2C74 /* BugsnagErrorReportSink.h in Headers */ = {isa = PBXBuildFile; fileRef = 00AD1EFD2486A17800A27979 /* BugsnagErrorReportSink.h */; }; - E75A5CDC248A5DA2005D2C74 /* BugsnagErrorReportSink.m in Sources */ = {isa = PBXBuildFile; fileRef = 00AD1EF92486A17700A27979 /* BugsnagErrorReportSink.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -993,7 +1015,6 @@ E746294124890D2F00F92D67 /* BSGConfigurationBuilder.h in CopyFiles */, E746294524890D2F00F92D67 /* BSGConnectivity.h in CopyFiles */, E746294624890D2F00F92D67 /* BugsnagApiClient.h in CopyFiles */, - E746294724890D2F00F92D67 /* BugsnagErrorReportApiClient.h in CopyFiles */, E746294824890D2F00F92D67 /* BugsnagSessionTrackingApiClient.h in CopyFiles */, E746294924890D2F00F92D67 /* BSGSerialization.h in CopyFiles */, E746294A24890D2F00F92D67 /* BugsnagCollections.h in CopyFiles */, @@ -1038,7 +1059,6 @@ E746298424890D3200F92D67 /* BSG_KSCrashState.h in CopyFiles */, E746298524890D3200F92D67 /* BSG_KSCrashReportVersion.h in CopyFiles */, E746298624890D3200F92D67 /* BSG_KSCrashAdvanced.h in CopyFiles */, - E746298724890D3200F92D67 /* BSG_KSCrashReportStore.h in CopyFiles */, E746298824890D3200F92D67 /* BSG_KSCrashSentry_MachException.h in CopyFiles */, E746298924890D3200F92D67 /* BSG_KSCrashSentry_Private.h in CopyFiles */, E746298A24890D3200F92D67 /* BSG_KSCrashSentry.h in CopyFiles */, @@ -1050,7 +1070,6 @@ E746299024890D3200F92D67 /* BSG_KSCrashIdentifier.h in CopyFiles */, E746299524890D3200F92D67 /* BugsnagCrashSentry.h in CopyFiles */, E746299624890D3200F92D67 /* BugsnagSessionTracker.h in CopyFiles */, - E746299724890D3200F92D67 /* BugsnagErrorReportSink.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1075,7 +1094,6 @@ 008966B52486D43500DC48C2 /* BugsnagErrorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagErrorTest.m; sourceTree = ""; }; 008966B62486D43500DC48C2 /* BugsnagSessionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTest.m; sourceTree = ""; }; 008966B72486D43500DC48C2 /* report.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = report.json; sourceTree = ""; }; - 008966B82486D43500DC48C2 /* BugsnagErrorReportSinkTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagErrorReportSinkTests.m; sourceTree = ""; }; 008966B92486D43500DC48C2 /* BugsnagHandledStateTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagHandledStateTest.m; sourceTree = ""; }; 008966BA2486D43500DC48C2 /* BugsnagStackframeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagStackframeTest.m; sourceTree = ""; }; 008966BB2486D43500DC48C2 /* BugsnagStacktraceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagStacktraceTest.m; sourceTree = ""; }; @@ -1109,8 +1127,6 @@ 008966DB2486D43700DC48C2 /* KSSystemInfo_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSSystemInfo_Tests.m; sourceTree = ""; }; 008966DC2486D43700DC48C2 /* KSDynamicLinker_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSDynamicLinker_Tests.m; sourceTree = ""; }; 008966DD2486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSCrashSentry_NSException_Tests.m; sourceTree = ""; }; - 008966DE2486D43700DC48C2 /* KSCrashReportStore_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSCrashReportStore_Tests.m; sourceTree = ""; }; - 008966DF2486D43700DC48C2 /* KSCrashReportConverter_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSCrashReportConverter_Tests.m; sourceTree = ""; }; 008966E02486D43700DC48C2 /* KSJSONCodec_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSJSONCodec_Tests.m; sourceTree = ""; }; 008966E12486D43700DC48C2 /* KSSignalInfo_Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSSignalInfo_Tests.m; sourceTree = ""; }; 008966E22486D43700DC48C2 /* XCTestCase+KSCrash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+KSCrash.h"; sourceTree = ""; }; @@ -1137,9 +1153,7 @@ 008967EE2486DA4400DC48C2 /* BugsnagApiClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagApiClient.m; sourceTree = ""; }; 008967EF2486DA4500DC48C2 /* BugsnagSessionTrackingApiClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagSessionTrackingApiClient.m; sourceTree = ""; }; 008967F02486DA4500DC48C2 /* BSGConnectivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSGConnectivity.m; sourceTree = ""; }; - 008967F12486DA4500DC48C2 /* BugsnagErrorReportApiClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagErrorReportApiClient.m; sourceTree = ""; }; 008967F22486DA4500DC48C2 /* BSGConnectivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSGConnectivity.h; sourceTree = ""; }; - 008967F32486DA4500DC48C2 /* BugsnagErrorReportApiClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagErrorReportApiClient.h; sourceTree = ""; }; 008968102486DA5600DC48C2 /* BugsnagCollections.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagCollections.h; sourceTree = ""; }; 008968112486DA5600DC48C2 /* BSGSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSGSerialization.h; sourceTree = ""; }; 008968122486DA5600DC48C2 /* BugsnagPlatformConditional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagPlatformConditional.h; sourceTree = ""; }; @@ -1218,7 +1232,6 @@ 008969292486DAD000DC48C2 /* BSG_KSCrashState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSCrashState.m; sourceTree = ""; }; 0089692A2486DAD000DC48C2 /* BSG_KSCrashContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashContext.h; sourceTree = ""; }; 0089692B2486DAD000DC48C2 /* BSG_KSCrashType.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BSG_KSCrashType.c; sourceTree = ""; }; - 0089692C2486DAD000DC48C2 /* BSG_KSCrashReportStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSCrashReportStore.m; sourceTree = ""; }; 0089692D2486DAD000DC48C2 /* BSG_KSCrash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrash.h; sourceTree = ""; }; 0089692E2486DAD000DC48C2 /* BSG_KSCrashC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashC.h; sourceTree = ""; }; 0089692F2486DAD000DC48C2 /* BSG_KSSystemInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSSystemInfo.h; sourceTree = ""; }; @@ -1231,7 +1244,6 @@ 008969362486DAD000DC48C2 /* BSG_KSCrash.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSCrash.m; sourceTree = ""; }; 008969372486DAD000DC48C2 /* BSG_KSCrashReportVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashReportVersion.h; sourceTree = ""; }; 008969382486DAD000DC48C2 /* BSG_KSCrashAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashAdvanced.h; sourceTree = ""; }; - 008969392486DAD000DC48C2 /* BSG_KSCrashReportStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashReportStore.h; sourceTree = ""; }; 0089693B2486DAD000DC48C2 /* BSG_KSCrashSentry_NSException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSG_KSCrashSentry_NSException.m; sourceTree = ""; }; 0089693C2486DAD000DC48C2 /* BSG_KSCrashSentry_MachException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashSentry_MachException.h; sourceTree = ""; }; 0089693D2486DAD000DC48C2 /* BSG_KSCrashSentry_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSG_KSCrashSentry_Private.h; sourceTree = ""; }; @@ -1262,8 +1274,6 @@ 00AD1CD124869C2400A27979 /* Bugsnag-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Bugsnag-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 00AD1CE424869C6C00A27979 /* libBugsnagStatic.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBugsnagStatic.a; sourceTree = BUILT_PRODUCTS_DIR; }; 00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagSessionTracker.h; sourceTree = ""; }; - 00AD1EF92486A17700A27979 /* BugsnagErrorReportSink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagErrorReportSink.m; sourceTree = ""; }; - 00AD1EFD2486A17800A27979 /* BugsnagErrorReportSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagErrorReportSink.h; sourceTree = ""; }; 00AD1EFE2486A17800A27979 /* Bugsnag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Bugsnag.m; sourceTree = ""; }; 00AD1EFF2486A17900A27979 /* BugsnagCrashSentry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCrashSentry.m; sourceTree = ""; }; 00AD1F002486A17900A27979 /* BugsnagCrashSentry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagCrashSentry.h; sourceTree = ""; }; @@ -1279,6 +1289,8 @@ 00E636C02487031D006CBF1A /* Bugsnag.podspec.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Bugsnag.podspec.json; sourceTree = SOURCE_ROOT; }; 00E636C12487031D006CBF1A /* docker-compose.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = "docker-compose.yml"; sourceTree = SOURCE_ROOT; }; 00E636C324878FFC006CBF1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 010FF28225ED2A8D00E4F2B0 /* BSGAppHangDetector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGAppHangDetector.h; sourceTree = ""; }; + 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGAppHangDetector.m; sourceTree = ""; }; 01210B7C25CD661800D683BB /* BugsnagThread+Recording.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagThread+Recording.h"; sourceTree = ""; }; 01210B8725CD665000D683BB /* BugsnagThread+Recording.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "BugsnagThread+Recording.m"; sourceTree = ""; }; 012482A225627B51003F7243 /* UIKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIKitTests.m; sourceTree = ""; }; @@ -1289,6 +1301,16 @@ 0126DF1A257A92860031A70C /* BugsnagSession+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BugsnagSession+Private.h"; sourceTree = ""; }; 0126DF2C257A92D70031A70C /* BugsnagStacktrace+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagStacktrace+Private.h"; sourceTree = ""; }; 0126DF34257A94740031A70C /* BugsnagUser+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagUser+Private.h"; sourceTree = ""; }; + 0126F78925DD508C008483C2 /* BSGEventUploadOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGEventUploadOperation.h; sourceTree = ""; }; + 0126F78A25DD508C008483C2 /* BSGEventUploadOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadOperation.m; sourceTree = ""; }; + 0126F79925DD510E008483C2 /* BSGEventUploadObjectOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGEventUploadObjectOperation.h; sourceTree = ""; }; + 0126F79A25DD510E008483C2 /* BSGEventUploadObjectOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadObjectOperation.m; sourceTree = ""; }; + 0126F7A925DD5118008483C2 /* BSGEventUploadFileOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGEventUploadFileOperation.h; sourceTree = ""; }; + 0126F7AA25DD5118008483C2 /* BSGEventUploadFileOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadFileOperation.m; sourceTree = ""; }; + 0126F7B925DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGEventUploadKSCrashReportOperation.h; sourceTree = ""; }; + 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadKSCrashReportOperation.m; sourceTree = ""; }; + 0127149025F6171000D3500A /* BugsnagClient+AppHangs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagClient+AppHangs.h"; sourceTree = ""; }; + 0127149125F6171000D3500A /* BugsnagClient+AppHangs.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "BugsnagClient+AppHangs.m"; sourceTree = ""; }; 0134524A256BCF7C0088C548 /* BugsnagError+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagError+Private.h"; sourceTree = ""; }; 0134524B256BD00A0088C548 /* BugsnagThread+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagThread+Private.h"; sourceTree = ""; }; 0140D24725765F8F00FD0306 /* BSGUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGUIKit.h; sourceTree = ""; }; @@ -1298,15 +1320,20 @@ 0163BF5825823D8D008DC28B /* NotificationBreadcrumbTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationBreadcrumbTests.m; sourceTree = ""; }; 016875C4258D003200DFFF69 /* NSUserDefaultsStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSUserDefaultsStub.h; sourceTree = ""; }; 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsStub.m; sourceTree = ""; }; + 01840B6D25DC26E200F95648 /* BSGEventUploader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGEventUploader.h; sourceTree = ""; }; + 01840B6E25DC26E200F95648 /* BSGEventUploader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploader.m; sourceTree = ""; }; 01937CF9257A7B4C00F2DE31 /* Bugsnag+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bugsnag+Private.h"; sourceTree = ""; }; - 01937D01257A7E0E00F2DE31 /* BugsnagErrorReportSink+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagErrorReportSink+Private.h"; sourceTree = ""; }; 01937D09257A7ED000F2DE31 /* BugsnagSessionTracker+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagSessionTracker+Private.h"; sourceTree = ""; }; 01937D11257A814D00F2DE31 /* BugsnagMetadata+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagMetadata+Private.h"; sourceTree = ""; }; 01937D2E257A83A900F2DE31 /* BugsnagApp+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagApp+Private.h"; sourceTree = ""; }; 0195FC3B256BC81400DE6646 /* BugsnagEvent+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagEvent+Private.h"; sourceTree = ""; }; 0198762E2567D5AB000A7AF3 /* BugsnagStackframe+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagStackframe+Private.h"; sourceTree = ""; }; 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "report-react-native-promise-rejection.json"; sourceTree = ""; }; + 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadKSCrashReportOperationTests.m; sourceTree = ""; }; + 01BDB21425DEC02900A91FAF /* Data */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Data; sourceTree = ""; }; 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSCrashReportWriterTests.m; sourceTree = ""; }; + 01C2769B2601F44D006901EA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; + 01C2769C2601F455006901EA /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = ""; }; 01CCAEE825D414D60057268D /* BugsnagLastRunInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagLastRunInfo.h; sourceTree = ""; }; 01CCAEE925D414D60057268D /* BugsnagLastRunInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagLastRunInfo.m; sourceTree = ""; }; 01CCAEFF25D4151C0057268D /* BugsnagLastRunInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagLastRunInfo+Private.h"; sourceTree = ""; }; @@ -1458,8 +1485,6 @@ 008966D62486D43700DC48C2 /* FileBasedTestCase.h */, 008966E42486D43700DC48C2 /* FileBasedTestCase.m */, 008966E92486D43700DC48C2 /* KSCrashIdentifierTests.m */, - 008966DF2486D43700DC48C2 /* KSCrashReportConverter_Tests.m */, - 008966DE2486D43700DC48C2 /* KSCrashReportStore_Tests.m */, 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */, 008966DD2486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m */, 008966E72486D43700DC48C2 /* KSCrashSentry_Signal_Tests.m */, @@ -1542,8 +1567,6 @@ 008969492486DAD000DC48C2 /* BSG_KSCrashReport.c */, 008969322486DAD000DC48C2 /* BSG_KSCrashReport.h */, 008969342486DAD000DC48C2 /* BSG_KSCrashReportFields.h */, - 008969392486DAD000DC48C2 /* BSG_KSCrashReportStore.h */, - 0089692C2486DAD000DC48C2 /* BSG_KSCrashReportStore.m */, 008969372486DAD000DC48C2 /* BSG_KSCrashReportVersion.h */, 008969352486DAD000DC48C2 /* BSG_KSCrashState.h */, 008969292486DAD000DC48C2 /* BSG_KSCrashState.m */, @@ -1623,6 +1646,8 @@ 00AD1C6824869B0E00A27979 = { isa = PBXGroup; children = ( + 01C2769B2601F44D006901EA /* CHANGELOG.md */, + 01C2769C2601F455006901EA /* CONTRIBUTING.md */, 00E636B2248702A1006CBF1A /* README.md */, 00E636B3248702A1006CBF1A /* LICENSE.txt */, 00AD1C7424869B0E00A27979 /* Bugsnag */, @@ -1654,9 +1679,6 @@ 01937CF9257A7B4C00F2DE31 /* Bugsnag+Private.h */, 00AD1F002486A17900A27979 /* BugsnagCrashSentry.h */, 00AD1EFF2486A17900A27979 /* BugsnagCrashSentry.m */, - 00AD1EFD2486A17800A27979 /* BugsnagErrorReportSink.h */, - 00AD1EF92486A17700A27979 /* BugsnagErrorReportSink.m */, - 01937D01257A7E0E00F2DE31 /* BugsnagErrorReportSink+Private.h */, 01CCAEE925D414D60057268D /* BugsnagLastRunInfo.m */, 01CCAEFF25D4151C0057268D /* BugsnagLastRunInfo+Private.h */, 00AD1EF82486A17700A27979 /* BugsnagSessionTracker.h */, @@ -1684,6 +1706,7 @@ children = ( 00896A3F2486DBDD00DC48C2 /* BSGConfigurationBuilderTests.m */, 008966C62486D43600DC48C2 /* BSGConnectivityTest.m */, + 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */, CBCF77AA250142E0004AF22A /* BSGJSONSerializationTests.m */, 008966C82486D43600DC48C2 /* BSGOutOfMemoryTests.m */, CB6419AA25A73E8C00613D25 /* BSGStorageMigratorTests.m */, @@ -1698,7 +1721,6 @@ 00896A432486DBF000DC48C2 /* BugsnagConfigurationTests.m */, 008966A42486D43400DC48C2 /* BugsnagDeviceTest.m */, 008966CB2486D43600DC48C2 /* BugsnagEnabledBreadcrumbTest.m */, - 008966B82486D43500DC48C2 /* BugsnagErrorReportSinkTests.m */, 008966B52486D43500DC48C2 /* BugsnagErrorTest.m */, 008966C02486D43600DC48C2 /* BugsnagEventFromKSCrashReportTest.m */, 008966AC2486D43500DC48C2 /* BugsnagEventPersistLoadTest.m */, @@ -1727,6 +1749,7 @@ 008966C52486D43600DC48C2 /* BugsnagUserTest.m */, E701FAA62490EF77008D842F /* ClientApiValidationTest.m */, E701FAAE2490EFE8008D842F /* ConfigurationApiValidationTest.m */, + 01BDB21425DEC02900A91FAF /* Data */, E701FAAA2490EFD9008D842F /* EventApiValidationTest.m */, 00E636C324878FFC006CBF1A /* Info.plist */, 008966D32486D43700DC48C2 /* KSCrash */, @@ -1761,6 +1784,8 @@ isa = PBXGroup; children = ( 008967BB2486DA1900DC48C2 /* BugsnagClient.m */, + 0127149025F6171000D3500A /* BugsnagClient+AppHangs.h */, + 0127149125F6171000D3500A /* BugsnagClient+AppHangs.m */, 008967BC2486DA1900DC48C2 /* BugsnagClient+Private.h */, ); path = Client; @@ -1784,10 +1809,18 @@ children = ( 008967F22486DA4500DC48C2 /* BSGConnectivity.h */, 008967F02486DA4500DC48C2 /* BSGConnectivity.m */, + 01840B6D25DC26E200F95648 /* BSGEventUploader.h */, + 01840B6E25DC26E200F95648 /* BSGEventUploader.m */, + 0126F7A925DD5118008483C2 /* BSGEventUploadFileOperation.h */, + 0126F7AA25DD5118008483C2 /* BSGEventUploadFileOperation.m */, + 0126F7B925DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h */, + 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */, + 0126F79925DD510E008483C2 /* BSGEventUploadObjectOperation.h */, + 0126F79A25DD510E008483C2 /* BSGEventUploadObjectOperation.m */, + 0126F78925DD508C008483C2 /* BSGEventUploadOperation.h */, + 0126F78A25DD508C008483C2 /* BSGEventUploadOperation.m */, 008967ED2486DA4400DC48C2 /* BugsnagApiClient.h */, 008967EE2486DA4400DC48C2 /* BugsnagApiClient.m */, - 008967F32486DA4500DC48C2 /* BugsnagErrorReportApiClient.h */, - 008967F12486DA4500DC48C2 /* BugsnagErrorReportApiClient.m */, 008967EC2486DA4400DC48C2 /* BugsnagSessionTrackingApiClient.h */, 008967EF2486DA4500DC48C2 /* BugsnagSessionTrackingApiClient.m */, ); @@ -1799,6 +1832,8 @@ children = ( 008969272486DAD000DC48C2 /* BSG_RFC3339DateTool.h */, 008969142486DAD000DC48C2 /* BSG_RFC3339DateTool.m */, + 010FF28225ED2A8D00E4F2B0 /* BSGAppHangDetector.h */, + 010FF28325ED2A8D00E4F2B0 /* BSGAppHangDetector.m */, CBCF77A125010648004AF22A /* BSGJSONSerialization.h */, CBCF77A225010648004AF22A /* BSGJSONSerialization.m */, 008968112486DA5600DC48C2 /* BSGSerialization.h */, @@ -1981,13 +2016,16 @@ 3A700A9824A63AC60068CD1B /* BugsnagEndpointConfiguration.h in Headers */, CBB092902519F891007698BC /* BugsnagSystemState.h in Headers */, 3A700A9924A63AC60068CD1B /* BugsnagBreadcrumb.h in Headers */, + 0126F7AB25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */, 3A700A9A24A63AC60068CD1B /* BSG_KSCrashReportWriter.h in Headers */, 3A700A9B24A63AC60068CD1B /* BugsnagErrorTypes.h in Headers */, + 01840B6F25DC26E200F95648 /* BSGEventUploader.h in Headers */, 3A700A9C24A63AC60068CD1B /* BugsnagEvent.h in Headers */, 3A700A9D24A63AC60068CD1B /* BugsnagClient.h in Headers */, 3A700A9E24A63AC60068CD1B /* BugsnagUser.h in Headers */, 3A700A9F24A63ADC0068CD1B /* BugsnagAppWithState.h in Headers */, 3A700AA024A63ADC0068CD1B /* Bugsnag.h in Headers */, + 0126F78B25DD508C008483C2 /* BSGEventUploadOperation.h in Headers */, 3A700AA124A63ADC0068CD1B /* BugsnagConfiguration.h in Headers */, 3A700AA224A63ADC0068CD1B /* BugsnagPlugin.h in Headers */, 3A700AA324A63ADC0068CD1B /* BugsnagDevice.h in Headers */, @@ -2001,6 +2039,7 @@ 008968282486DA5600DC48C2 /* BugsnagKeys.h in Headers */, CBCAF6FA25A457F90095771F /* BSGFileLocations.h in Headers */, 008969BD2486DAD100DC48C2 /* BSG_KSMachHeaders.h in Headers */, + 0126F7BB25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h in Headers */, 008968DE2486DAA700DC48C2 /* BugsnagPluginClient.h in Headers */, 008969EA2486DAD100DC48C2 /* BSG_KSCrashReport.h in Headers */, 008969F02486DAD100DC48C2 /* BSG_KSCrashReportFields.h in Headers */, @@ -2014,11 +2053,12 @@ 008969E72486DAD100DC48C2 /* BSG_KSSystemInfoC.h in Headers */, 008968A42486DA9600DC48C2 /* BugsnagSessionTrackingPayload.h in Headers */, CBAA6ABB250BA70E00713376 /* BugsnagKVStore.h in Headers */, - E75A5CDB248A5D97005D2C74 /* BugsnagErrorReportSink.h in Headers */, 00AD1F102486A17900A27979 /* BugsnagSessionTracker.h in Headers */, + 0126F79B25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */, 008968F42486DAB800DC48C2 /* BugsnagSessionFileStore.h in Headers */, 008968882486DA9600DC48C2 /* BugsnagHandledState.h in Headers */, CBCF77A325010648004AF22A /* BSGJSONSerialization.h in Headers */, + 0127149225F6171000D3500A /* BugsnagClient+AppHangs.h in Headers */, 00896A082486DAD100DC48C2 /* BSG_KSCrashSentry_Private.h in Headers */, 008969722486DAD000DC48C2 /* BSG_KSSignalInfo.h in Headers */, 008967C22486DA1900DC48C2 /* BugsnagClient+Private.h in Headers */, @@ -2029,10 +2069,10 @@ 00AD1F2B2486A17900A27979 /* BugsnagCrashSentry.h in Headers */, 00896A1D2486DAD100DC48C2 /* BSG_KSCrashSentry_NSException.h in Headers */, 0089695A2486DAD000DC48C2 /* BSG_KSBacktrace.h in Headers */, - 0089680D2486DA4500DC48C2 /* BugsnagErrorReportApiClient.h in Headers */, 0089688B2486DA9600DC48C2 /* BugsnagStacktrace.h in Headers */, 0089681E2486DA5600DC48C2 /* BugsnagPlatformConditional.h in Headers */, 008969602486DAD000DC48C2 /* BSG_KSSysCtl.h in Headers */, + 010FF28425ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008967F42486DA4500DC48C2 /* BugsnagSessionTrackingApiClient.h in Headers */, 0126DF1B257A92860031A70C /* BugsnagSession+Private.h in Headers */, 008969E12486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, @@ -2060,7 +2100,6 @@ 0089696F2486DAD000DC48C2 /* BSG_KSFileUtils.h in Headers */, 008969512486DAD000DC48C2 /* BSGOnErrorSentBlock.h in Headers */, 00896A052486DAD100DC48C2 /* BSG_KSCrashSentry_MachException.h in Headers */, - 008969FF2486DAD100DC48C2 /* BSG_KSCrashReportStore.h in Headers */, 008968CF2486DA9600DC48C2 /* BugsnagNotifier.h in Headers */, 008969872486DAD100DC48C2 /* BSG_KSMachApple.h in Headers */, 008969C92486DAD100DC48C2 /* BSG_RFC3339DateTool.h in Headers */, @@ -2080,13 +2119,16 @@ 3A700AAC24A63CFD0068CD1B /* BugsnagEndpointConfiguration.h in Headers */, CBB092912519F891007698BC /* BugsnagSystemState.h in Headers */, 3A700AAD24A63CFD0068CD1B /* BugsnagBreadcrumb.h in Headers */, + 0126F7AC25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */, 3A700AAE24A63CFD0068CD1B /* BSG_KSCrashReportWriter.h in Headers */, 3A700AAF24A63CFD0068CD1B /* BugsnagErrorTypes.h in Headers */, + 01840B7025DC26E200F95648 /* BSGEventUploader.h in Headers */, 3A700AB024A63CFD0068CD1B /* BugsnagEvent.h in Headers */, 3A700AB124A63CFD0068CD1B /* BugsnagClient.h in Headers */, 3A700AB224A63CFD0068CD1B /* BugsnagUser.h in Headers */, 3A700AB324A63CFD0068CD1B /* BugsnagAppWithState.h in Headers */, 3A700AB424A63CFD0068CD1B /* Bugsnag.h in Headers */, + 0126F78C25DD508C008483C2 /* BSGEventUploadOperation.h in Headers */, 3A700AB524A63CFD0068CD1B /* BugsnagConfiguration.h in Headers */, 3A700AB624A63CFD0068CD1B /* BugsnagPlugin.h in Headers */, 3A700AB724A63CFD0068CD1B /* BugsnagDevice.h in Headers */, @@ -2100,6 +2142,7 @@ 008968292486DA5600DC48C2 /* BugsnagKeys.h in Headers */, CBCAF6FB25A457F90095771F /* BSGFileLocations.h in Headers */, 008969BE2486DAD100DC48C2 /* BSG_KSMachHeaders.h in Headers */, + 0126F7BC25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h in Headers */, 008968DF2486DAA700DC48C2 /* BugsnagPluginClient.h in Headers */, 008969EB2486DAD100DC48C2 /* BSG_KSCrashReport.h in Headers */, 008969F12486DAD100DC48C2 /* BSG_KSCrashReportFields.h in Headers */, @@ -2115,8 +2158,10 @@ CBAA6ABC250BA70E00713376 /* BugsnagKVStore.h in Headers */, 00AD1F112486A17900A27979 /* BugsnagSessionTracker.h in Headers */, 008968F52486DAB800DC48C2 /* BugsnagSessionFileStore.h in Headers */, + 0126F79C25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */, 008968892486DA9600DC48C2 /* BugsnagHandledState.h in Headers */, 00896A092486DAD100DC48C2 /* BSG_KSCrashSentry_Private.h in Headers */, + 0127149325F6171000D3500A /* BugsnagClient+AppHangs.h in Headers */, CBCF77A425010648004AF22A /* BSGJSONSerialization.h in Headers */, 008969732486DAD000DC48C2 /* BSG_KSSignalInfo.h in Headers */, 008967C32486DA1900DC48C2 /* BugsnagClient+Private.h in Headers */, @@ -2127,11 +2172,11 @@ 00AD1F2C2486A17900A27979 /* BugsnagCrashSentry.h in Headers */, 00896A1E2486DAD100DC48C2 /* BSG_KSCrashSentry_NSException.h in Headers */, 0089695B2486DAD000DC48C2 /* BSG_KSBacktrace.h in Headers */, - 0089680E2486DA4500DC48C2 /* BugsnagErrorReportApiClient.h in Headers */, 0089688C2486DA9600DC48C2 /* BugsnagStacktrace.h in Headers */, 0089681F2486DA5600DC48C2 /* BugsnagPlatformConditional.h in Headers */, 008969612486DAD000DC48C2 /* BSG_KSSysCtl.h in Headers */, 008967F52486DA4500DC48C2 /* BugsnagSessionTrackingApiClient.h in Headers */, + 010FF28525ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008969E22486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, 0126DF1C257A92860031A70C /* BugsnagSession+Private.h in Headers */, 00896A0F2486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, @@ -2158,12 +2203,10 @@ 008969702486DAD000DC48C2 /* BSG_KSFileUtils.h in Headers */, 008969522486DAD000DC48C2 /* BSGOnErrorSentBlock.h in Headers */, 00896A062486DAD100DC48C2 /* BSG_KSCrashSentry_MachException.h in Headers */, - 00896A002486DAD100DC48C2 /* BSG_KSCrashReportStore.h in Headers */, 008968D02486DA9600DC48C2 /* BugsnagNotifier.h in Headers */, 008969882486DAD100DC48C2 /* BSG_KSMachApple.h in Headers */, 008969CA2486DAD100DC48C2 /* BSG_RFC3339DateTool.h in Headers */, 008969F42486DAD100DC48C2 /* BSG_KSCrashState.h in Headers */, - 00AD1F212486A17900A27979 /* BugsnagErrorReportSink.h in Headers */, 008969792486DAD100DC48C2 /* NSError+BSG_SimpleConstructor.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2179,13 +2222,16 @@ 3A700AC024A63D110068CD1B /* BugsnagEndpointConfiguration.h in Headers */, CBB092922519F891007698BC /* BugsnagSystemState.h in Headers */, 3A700AC124A63D110068CD1B /* BugsnagBreadcrumb.h in Headers */, + 0126F7AD25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */, 3A700AC224A63D110068CD1B /* BSG_KSCrashReportWriter.h in Headers */, 3A700AC324A63D110068CD1B /* BugsnagErrorTypes.h in Headers */, + 01840B7125DC26E200F95648 /* BSGEventUploader.h in Headers */, 3A700AC424A63D110068CD1B /* BugsnagEvent.h in Headers */, 3A700AC524A63D110068CD1B /* BugsnagClient.h in Headers */, 3A700AC624A63D110068CD1B /* BugsnagUser.h in Headers */, 3A700AC724A63D110068CD1B /* BugsnagAppWithState.h in Headers */, 3A700AC824A63D110068CD1B /* Bugsnag.h in Headers */, + 0126F78D25DD508C008483C2 /* BSGEventUploadOperation.h in Headers */, 3A700AC924A63D110068CD1B /* BugsnagConfiguration.h in Headers */, 3A700ACA24A63D110068CD1B /* BugsnagPlugin.h in Headers */, 3A700ACB24A63D110068CD1B /* BugsnagDevice.h in Headers */, @@ -2199,6 +2245,7 @@ 0089682A2486DA5600DC48C2 /* BugsnagKeys.h in Headers */, CBCAF6FC25A457F90095771F /* BSGFileLocations.h in Headers */, 008969BF2486DAD100DC48C2 /* BSG_KSMachHeaders.h in Headers */, + 0126F7BD25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.h in Headers */, 008968E02486DAA700DC48C2 /* BugsnagPluginClient.h in Headers */, 008969EC2486DAD100DC48C2 /* BSG_KSCrashReport.h in Headers */, 008969F22486DAD100DC48C2 /* BSG_KSCrashReportFields.h in Headers */, @@ -2214,8 +2261,10 @@ CBAA6ABD250BA70F00713376 /* BugsnagKVStore.h in Headers */, 00AD1F122486A17900A27979 /* BugsnagSessionTracker.h in Headers */, 008968F62486DAB800DC48C2 /* BugsnagSessionFileStore.h in Headers */, + 0126F79D25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */, 0089688A2486DA9600DC48C2 /* BugsnagHandledState.h in Headers */, 00896A0A2486DAD100DC48C2 /* BSG_KSCrashSentry_Private.h in Headers */, + 0127149425F6171000D3500A /* BugsnagClient+AppHangs.h in Headers */, CBCF77A525010648004AF22A /* BSGJSONSerialization.h in Headers */, 008969742486DAD100DC48C2 /* BSG_KSSignalInfo.h in Headers */, 008967C42486DA1900DC48C2 /* BugsnagClient+Private.h in Headers */, @@ -2226,11 +2275,11 @@ 00AD1F2D2486A17900A27979 /* BugsnagCrashSentry.h in Headers */, 00896A1F2486DAD100DC48C2 /* BSG_KSCrashSentry_NSException.h in Headers */, 0089695C2486DAD000DC48C2 /* BSG_KSBacktrace.h in Headers */, - 0089680F2486DA4500DC48C2 /* BugsnagErrorReportApiClient.h in Headers */, 0089688D2486DA9600DC48C2 /* BugsnagStacktrace.h in Headers */, 008968202486DA5600DC48C2 /* BugsnagPlatformConditional.h in Headers */, 008969622486DAD000DC48C2 /* BSG_KSSysCtl.h in Headers */, 008967F62486DA4500DC48C2 /* BugsnagSessionTrackingApiClient.h in Headers */, + 010FF28625ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008969E32486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, 0126DF1D257A92860031A70C /* BugsnagSession+Private.h in Headers */, 00896A102486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, @@ -2257,12 +2306,10 @@ 008969712486DAD000DC48C2 /* BSG_KSFileUtils.h in Headers */, 008969532486DAD000DC48C2 /* BSGOnErrorSentBlock.h in Headers */, 00896A072486DAD100DC48C2 /* BSG_KSCrashSentry_MachException.h in Headers */, - 00896A012486DAD100DC48C2 /* BSG_KSCrashReportStore.h in Headers */, 008968D12486DA9600DC48C2 /* BugsnagNotifier.h in Headers */, 008969892486DAD100DC48C2 /* BSG_KSMachApple.h in Headers */, 008969CB2486DAD100DC48C2 /* BSG_RFC3339DateTool.h in Headers */, 008969F52486DAD100DC48C2 /* BSG_KSCrashState.h in Headers */, - 00AD1F222486A17900A27979 /* BugsnagErrorReportSink.h in Headers */, 0089697A2486DAD100DC48C2 /* NSError+BSG_SimpleConstructor.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2467,6 +2514,7 @@ files = ( 01B14C56251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */, 0089671E2486D43700DC48C2 /* report.json in Resources */, + 01BDB21525DEC02900A91FAF /* Data in Resources */, CB6419B425A7419400613D25 /* v0_files in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2486,6 +2534,7 @@ files = ( 01B14C57251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */, 0089671F2486D43700DC48C2 /* report.json in Resources */, + 01BDB21625DEC02900A91FAF /* Data in Resources */, CB6419B525A7419400613D25 /* v0_files in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2505,6 +2554,7 @@ files = ( 01B14C58251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */, 008967202486D43700DC48C2 /* report.json in Resources */, + 01BDB21725DEC02900A91FAF /* Data in Resources */, CB6419B625A7419400613D25 /* v0_files in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2517,9 +2567,9 @@ buildActionMask = 2147483647; files = ( CBAA6AB5250BA01D00713376 /* BugsnagKVStore.c in Sources */, - E75A5CDC248A5DA2005D2C74 /* BugsnagErrorReportSink.m in Sources */, 008969992486DAD100DC48C2 /* BSG_KSMach_Arm64.c in Sources */, 008967E82486DA2D00DC48C2 /* BugsnagErrorTypes.m in Sources */, + 0126F7AE25DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */, 008968722486DA9500DC48C2 /* BugsnagDevice.m in Sources */, 008969842486DAD100DC48C2 /* BSG_KSMachHeaders.c in Sources */, 00896A322486DAD100DC48C2 /* BSG_KSCrashC.c in Sources */, @@ -2539,6 +2589,7 @@ 008967B42486D9D800DC48C2 /* BugsnagBreadcrumbs.m in Sources */, 00AD1F2E2486A17900A27979 /* BugsnagSessionTracker.m in Sources */, 008968212486DA5600DC48C2 /* BugsnagKeys.m in Sources */, + 0126F7BE25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */, 008969B12486DAD100DC48C2 /* BSG_KSMach_x86_64.c in Sources */, 0089696C2486DAD000DC48C2 /* BSG_KSJSONCodecObjC.m in Sources */, 0089689D2486DA9600DC48C2 /* BugsnagSessionTrackingPayload.m in Sources */, @@ -2565,12 +2616,14 @@ 008969692486DAD000DC48C2 /* BSG_KSMach_Arm.c in Sources */, 008969C62486DAD100DC48C2 /* BSG_KSLogger.m in Sources */, 008969662486DAD000DC48C2 /* BSG_KSDynamicLinker.c in Sources */, + 0127149525F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */, 008969C02486DAD100DC48C2 /* BSG_KSString.c in Sources */, - 008968062486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */, + 0126F79E25DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */, 0089682B2486DA5600DC48C2 /* BSGSerialization.m in Sources */, 008968E92486DAB800DC48C2 /* BugsnagSessionFileStore.m in Sources */, 00896A172486DAD100DC48C2 /* BSG_KSCrashSentry_CPPException.mm in Sources */, 008969CF2486DAD100DC48C2 /* BSG_KSCrashState.m in Sources */, + 01840B7225DC26E200F95648 /* BSGEventUploader.m in Sources */, 008968C32486DA9600DC48C2 /* BugsnagUser.m in Sources */, CBAB4DD82510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, 008968A72486DA9600DC48C2 /* BugsnagSession.m in Sources */, @@ -2583,8 +2636,10 @@ 015F528425C15BB7000D1915 /* MRCCanary.m in Sources */, 008969AE2486DAD100DC48C2 /* NSError+BSG_SimpleConstructor.m in Sources */, 008968CB2486DA9600DC48C2 /* BugsnagThread.m in Sources */, + 0126F78E25DD508C008483C2 /* BSGEventUploadOperation.m in Sources */, 008968022486DA4500DC48C2 /* BSGConnectivity.m in Sources */, CBE9062D25A34DAB0045B965 /* BSGStorageMigratorV0V1.m in Sources */, + 010FF28725ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00896A352486DAD100DC48C2 /* BSG_KSSystemInfo.m in Sources */, 008969E42486DAD100DC48C2 /* BSG_KSCrashIdentifier.m in Sources */, 008967DA2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, @@ -2594,7 +2649,6 @@ 008968B22486DA9600DC48C2 /* BugsnagDeviceWithState.m in Sources */, 00896A0B2486DAD100DC48C2 /* BSG_KSCrashSentry_User.c in Sources */, 0089682F2486DA5600DC48C2 /* BugsnagCollections.m in Sources */, - 008969D82486DAD100DC48C2 /* BSG_KSCrashReportStore.m in Sources */, 0089698D2486DAD100DC48C2 /* BSG_KSMach.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2613,7 +2667,6 @@ 008967A22486D43700DC48C2 /* KSCrashSentry_Signal_Tests.m in Sources */, 008967272486D43700DC48C2 /* BugsnagStackframeTest.m in Sources */, 008967302486D43700DC48C2 /* BugsnagStateEventTest.m in Sources */, - 0089678A2486D43700DC48C2 /* KSCrashReportStore_Tests.m in Sources */, E701FAAB2490EFD9008D842F /* EventApiValidationTest.m in Sources */, 0089677E2486D43700DC48C2 /* KSLogger_Tests.m in Sources */, 012482A325627B51003F7243 /* UIKitTests.m in Sources */, @@ -2644,6 +2697,7 @@ 008967872486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m in Sources */, 0089679C2486D43700DC48C2 /* KSFileUtils_Tests.m in Sources */, 008966EE2486D43700DC48C2 /* BugsnagClientPayloadInfoTest.m in Sources */, + 01BDB1F525DEBFB200A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */, 008967782486D43700DC48C2 /* KSMachHeader_Tests.m in Sources */, 0089673F2486D43700DC48C2 /* BugsnagAppTest.m in Sources */, 0089675A2486D43700DC48C2 /* BugsnagEnabledBreadcrumbTest.m in Sources */, @@ -2658,11 +2712,9 @@ 01E8765E256684E700F4B70A /* URLSessionMock.m in Sources */, 008967AB2486D43700DC48C2 /* KSMach_Tests.m in Sources */, 0089672A2486D43700DC48C2 /* BugsnagStacktraceTest.m in Sources */, - 0089678D2486D43700DC48C2 /* KSCrashReportConverter_Tests.m in Sources */, 0163BF5925823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */, 008967392486D43700DC48C2 /* BugsnagEventFromKSCrashReportTest.m in Sources */, 008967182486D43700DC48C2 /* BugsnagErrorTest.m in Sources */, - 008967212486D43700DC48C2 /* BugsnagErrorReportSinkTests.m in Sources */, 004E35352487AFDC007FBAE4 /* BugsnagHandledStateTest.m in Sources */, 008967932486D43700DC48C2 /* KSSignalInfo_Tests.m in Sources */, 008967662486D43700DC48C2 /* BugsnagNotifierTest.m in Sources */, @@ -2688,6 +2740,7 @@ 0089699A2486DAD100DC48C2 /* BSG_KSMach_Arm64.c in Sources */, 008967E92486DA2D00DC48C2 /* BugsnagErrorTypes.m in Sources */, 008968732486DA9500DC48C2 /* BugsnagDevice.m in Sources */, + 0126F7AF25DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */, 008969852486DAD100DC48C2 /* BSG_KSMachHeaders.c in Sources */, 00896A332486DAD100DC48C2 /* BSG_KSCrashC.c in Sources */, 008969912486DAD100DC48C2 /* BSG_RFC3339DateTool.m in Sources */, @@ -2707,7 +2760,7 @@ 00AD1F2F2486A17900A27979 /* BugsnagSessionTracker.m in Sources */, 008968222486DA5600DC48C2 /* BugsnagKeys.m in Sources */, 008969B22486DAD100DC48C2 /* BSG_KSMach_x86_64.c in Sources */, - 00AD1F142486A17900A27979 /* BugsnagErrorReportSink.m in Sources */, + 0126F7BF25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */, 0089696D2486DAD000DC48C2 /* BSG_KSJSONCodecObjC.m in Sources */, 0089689E2486DA9600DC48C2 /* BugsnagSessionTrackingPayload.m in Sources */, 008969B82486DAD100DC48C2 /* BSG_KSSignalInfo.c in Sources */, @@ -2733,12 +2786,14 @@ 0089696A2486DAD000DC48C2 /* BSG_KSMach_Arm.c in Sources */, 008969C72486DAD100DC48C2 /* BSG_KSLogger.m in Sources */, 008969672486DAD000DC48C2 /* BSG_KSDynamicLinker.c in Sources */, + 0127149625F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */, 008969C12486DAD100DC48C2 /* BSG_KSString.c in Sources */, - 008968072486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */, + 0126F79F25DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */, 0089682C2486DA5600DC48C2 /* BSGSerialization.m in Sources */, 008968EA2486DAB800DC48C2 /* BugsnagSessionFileStore.m in Sources */, 00896A182486DAD100DC48C2 /* BSG_KSCrashSentry_CPPException.mm in Sources */, 008969D02486DAD100DC48C2 /* BSG_KSCrashState.m in Sources */, + 01840B7325DC26E200F95648 /* BSGEventUploader.m in Sources */, 008968C42486DA9600DC48C2 /* BugsnagUser.m in Sources */, CBAB4DD92510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, 008968A82486DA9600DC48C2 /* BugsnagSession.m in Sources */, @@ -2751,8 +2806,10 @@ 015F528525C15BB7000D1915 /* MRCCanary.m in Sources */, 008969AF2486DAD100DC48C2 /* NSError+BSG_SimpleConstructor.m in Sources */, 008968CC2486DA9600DC48C2 /* BugsnagThread.m in Sources */, + 0126F78F25DD508C008483C2 /* BSGEventUploadOperation.m in Sources */, 008968032486DA4500DC48C2 /* BSGConnectivity.m in Sources */, CBE9062E25A34DAB0045B965 /* BSGStorageMigratorV0V1.m in Sources */, + 010FF28825ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00896A362486DAD100DC48C2 /* BSG_KSSystemInfo.m in Sources */, 008969E52486DAD100DC48C2 /* BSG_KSCrashIdentifier.m in Sources */, 008967DB2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, @@ -2762,7 +2819,6 @@ 008968B32486DA9600DC48C2 /* BugsnagDeviceWithState.m in Sources */, 00896A0C2486DAD100DC48C2 /* BSG_KSCrashSentry_User.c in Sources */, 008968302486DA5600DC48C2 /* BugsnagCollections.m in Sources */, - 008969D92486DAD100DC48C2 /* BSG_KSCrashReportStore.m in Sources */, 0089698E2486DAD100DC48C2 /* BSG_KSMach.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2776,7 +2832,6 @@ E701FAA02490EF4A008D842F /* BugsnagApiValidationTest.m in Sources */, 0089677F2486D43700DC48C2 /* KSLogger_Tests.m in Sources */, 008967342486D43700DC48C2 /* BugsnagClientTests.m in Sources */, - 008967222486D43700DC48C2 /* BugsnagErrorReportSinkTests.m in Sources */, CB10E540250BA8DF00AF5824 /* BugsnagKVStoreTest.m in Sources */, CBCF77AC250142E0004AF22A /* BSGJSONSerializationTests.m in Sources */, 0089679A2486D43700DC48C2 /* FileBasedTestCase.m in Sources */, @@ -2810,6 +2865,7 @@ 0089671C2486D43700DC48C2 /* BugsnagSessionTest.m in Sources */, 008967AC2486D43700DC48C2 /* KSMach_Tests.m in Sources */, 0163BF5A25823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */, + 01BDB1FD25DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */, 00896A452486DBF000DC48C2 /* BugsnagConfigurationTests.m in Sources */, 008967492486D43700DC48C2 /* BugsnagUserTest.m in Sources */, 0089673A2486D43700DC48C2 /* BugsnagEventFromKSCrashReportTest.m in Sources */, @@ -2820,7 +2876,6 @@ 008967282486D43700DC48C2 /* BugsnagStackframeTest.m in Sources */, 008967942486D43700DC48C2 /* KSSignalInfo_Tests.m in Sources */, 004E35402487B3BE007FBAE4 /* BugsnagSwiftConfigurationTests.swift in Sources */, - 0089678E2486D43700DC48C2 /* KSCrashReportConverter_Tests.m in Sources */, 008966F52486D43700DC48C2 /* BugsnagThreadTests.m in Sources */, 0089679D2486D43700DC48C2 /* KSFileUtils_Tests.m in Sources */, 008967A32486D43700DC48C2 /* KSCrashSentry_Signal_Tests.m in Sources */, @@ -2835,7 +2890,6 @@ 008967312486D43700DC48C2 /* BugsnagStateEventTest.m in Sources */, 004E35362487AFF2007FBAE4 /* BugsnagHandledStateTest.m in Sources */, 01C17AE82542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */, - 0089678B2486D43700DC48C2 /* KSCrashReportStore_Tests.m in Sources */, 00896A412486DBDD00DC48C2 /* BSGConfigurationBuilderTests.m in Sources */, 008967672486D43700DC48C2 /* BugsnagNotifierTest.m in Sources */, 0089676D2486D43700DC48C2 /* BugsnagTestsDummyClass.m in Sources */, @@ -2854,6 +2908,7 @@ 0089699B2486DAD100DC48C2 /* BSG_KSMach_Arm64.c in Sources */, 008967EA2486DA2D00DC48C2 /* BugsnagErrorTypes.m in Sources */, 008968742486DA9500DC48C2 /* BugsnagDevice.m in Sources */, + 0126F7B025DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */, 008969862486DAD100DC48C2 /* BSG_KSMachHeaders.c in Sources */, 00896A342486DAD100DC48C2 /* BSG_KSCrashC.c in Sources */, 008969922486DAD100DC48C2 /* BSG_RFC3339DateTool.m in Sources */, @@ -2873,7 +2928,7 @@ 00AD1F302486A17900A27979 /* BugsnagSessionTracker.m in Sources */, 008968232486DA5600DC48C2 /* BugsnagKeys.m in Sources */, 008969B32486DAD100DC48C2 /* BSG_KSMach_x86_64.c in Sources */, - 00AD1F152486A17900A27979 /* BugsnagErrorReportSink.m in Sources */, + 0126F7C025DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */, 0089696E2486DAD000DC48C2 /* BSG_KSJSONCodecObjC.m in Sources */, 0089689F2486DA9600DC48C2 /* BugsnagSessionTrackingPayload.m in Sources */, 008969B92486DAD100DC48C2 /* BSG_KSSignalInfo.c in Sources */, @@ -2899,12 +2954,14 @@ 0089696B2486DAD000DC48C2 /* BSG_KSMach_Arm.c in Sources */, 008969C82486DAD100DC48C2 /* BSG_KSLogger.m in Sources */, 008969682486DAD000DC48C2 /* BSG_KSDynamicLinker.c in Sources */, + 0127149725F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */, 008969C22486DAD100DC48C2 /* BSG_KSString.c in Sources */, - 008968082486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */, + 0126F7A025DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */, 0089682D2486DA5600DC48C2 /* BSGSerialization.m in Sources */, 008968EB2486DAB800DC48C2 /* BugsnagSessionFileStore.m in Sources */, 00896A192486DAD100DC48C2 /* BSG_KSCrashSentry_CPPException.mm in Sources */, 008969D12486DAD100DC48C2 /* BSG_KSCrashState.m in Sources */, + 01840B7425DC26E200F95648 /* BSGEventUploader.m in Sources */, 008968C52486DA9600DC48C2 /* BugsnagUser.m in Sources */, CBAB4DDA2510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, 008968A92486DA9600DC48C2 /* BugsnagSession.m in Sources */, @@ -2917,8 +2974,10 @@ 015F528625C15BB7000D1915 /* MRCCanary.m in Sources */, 008969B02486DAD100DC48C2 /* NSError+BSG_SimpleConstructor.m in Sources */, 008968CD2486DA9600DC48C2 /* BugsnagThread.m in Sources */, + 0126F79025DD508C008483C2 /* BSGEventUploadOperation.m in Sources */, 008968042486DA4500DC48C2 /* BSGConnectivity.m in Sources */, CBE9062F25A34DAB0045B965 /* BSGStorageMigratorV0V1.m in Sources */, + 010FF28925ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00896A372486DAD100DC48C2 /* BSG_KSSystemInfo.m in Sources */, 008969E62486DAD100DC48C2 /* BSG_KSCrashIdentifier.m in Sources */, 008967DC2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, @@ -2928,7 +2987,6 @@ 008968B42486DA9600DC48C2 /* BugsnagDeviceWithState.m in Sources */, 00896A0D2486DAD100DC48C2 /* BSG_KSCrashSentry_User.c in Sources */, 008968312486DA5600DC48C2 /* BugsnagCollections.m in Sources */, - 008969DA2486DAD100DC48C2 /* BSG_KSCrashReportStore.m in Sources */, 0089698F2486DAD100DC48C2 /* BSG_KSMach.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2940,7 +2998,6 @@ 008967892486D43700DC48C2 /* KSCrashSentry_NSException_Tests.m in Sources */, 008967802486D43700DC48C2 /* KSLogger_Tests.m in Sources */, 008967352486D43700DC48C2 /* BugsnagClientTests.m in Sources */, - 008967232486D43700DC48C2 /* BugsnagErrorReportSinkTests.m in Sources */, 0089679B2486D43700DC48C2 /* FileBasedTestCase.m in Sources */, CB10E541250BA8E000AF5824 /* BugsnagKVStoreTest.m in Sources */, 008967922486D43700DC48C2 /* KSJSONCodec_Tests.m in Sources */, @@ -2976,13 +3033,13 @@ 00896A462486DBF000DC48C2 /* BugsnagConfigurationTests.m in Sources */, 0089674A2486D43700DC48C2 /* BugsnagUserTest.m in Sources */, 0089673B2486D43700DC48C2 /* BugsnagEventFromKSCrashReportTest.m in Sources */, + 01BDB20525DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */, 0089674D2486D43700DC48C2 /* BSGConnectivityTest.m in Sources */, 01E87660256684E700F4B70A /* URLSessionMock.m in Sources */, 008966F02486D43700DC48C2 /* BugsnagClientPayloadInfoTest.m in Sources */, E701FAA92490EF77008D842F /* ClientApiValidationTest.m in Sources */, 008967292486D43700DC48C2 /* BugsnagStackframeTest.m in Sources */, 008967952486D43700DC48C2 /* KSSignalInfo_Tests.m in Sources */, - 0089678F2486D43700DC48C2 /* KSCrashReportConverter_Tests.m in Sources */, 008966F62486D43700DC48C2 /* BugsnagThreadTests.m in Sources */, 0089679E2486D43700DC48C2 /* KSFileUtils_Tests.m in Sources */, 008967A42486D43700DC48C2 /* KSCrashSentry_Signal_Tests.m in Sources */, @@ -2999,7 +3056,6 @@ CBA2249D251E429C00B87416 /* TestSupport.m in Sources */, 004E35372487AFF2007FBAE4 /* BugsnagHandledStateTest.m in Sources */, 016875C8258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */, - 0089678C2486D43700DC48C2 /* KSCrashReportStore_Tests.m in Sources */, 01C17AE92542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */, 00896A422486DBDD00DC48C2 /* BSGConfigurationBuilderTests.m in Sources */, 008967682486D43700DC48C2 /* BugsnagNotifierTest.m in Sources */, @@ -3020,6 +3076,7 @@ E7462909248907E500F92D67 /* BSG_KSMach_x86_32.c in Sources */, E746290A248907E500F92D67 /* BSG_KSDynamicLinker.c in Sources */, E746290B248907E500F92D67 /* BSG_KSMach_Arm.c in Sources */, + 0126F7B125DD5118008483C2 /* BSGEventUploadFileOperation.m in Sources */, E746290C248907E500F92D67 /* BSG_KSJSONCodec.c in Sources */, E746290D248907E500F92D67 /* BSG_KSMach.c in Sources */, E746290E248907E500F92D67 /* BSG_KSMach_Arm64.c in Sources */, @@ -3039,13 +3096,13 @@ E746291A248907E500F92D67 /* BSG_KSCrashSentry.c in Sources */, E746291B248907E500F92D67 /* BSG_KSCrashReport.c in Sources */, E746291C248907E500F92D67 /* BSG_KSCrashC.c in Sources */, + 0126F7C125DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */, E74628FC248907C100F92D67 /* BSG_KSCrashDoctor.m in Sources */, E74628FD248907C100F92D67 /* BSG_KSJSONCodecObjC.m in Sources */, E74628FF248907C100F92D67 /* BSG_KSMachHeaders.c in Sources */, E7462900248907C100F92D67 /* NSError+BSG_SimpleConstructor.m in Sources */, E7462901248907C100F92D67 /* BSG_KSLogger.m in Sources */, E7462902248907C100F92D67 /* BSG_KSCrashState.m in Sources */, - E7462903248907C100F92D67 /* BSG_KSCrashReportStore.m in Sources */, E7462904248907C100F92D67 /* BSG_KSCrashIdentifier.m in Sources */, CBCAF70025A457F90095771F /* BSGFileLocations.m in Sources */, E7462905248907C100F92D67 /* BSG_KSCrash.m in Sources */, @@ -3055,6 +3112,7 @@ 008968CA2486DA9600DC48C2 /* BugsnagApp.m in Sources */, 008967C12486DA1900DC48C2 /* BugsnagClient.m in Sources */, 008968752486DA9500DC48C2 /* BugsnagDevice.m in Sources */, + 010FF28A25ED2A8D00E4F2B0 /* BSGAppHangDetector.m in Sources */, 00E636C224878D84006CBF1A /* BSG_RFC3339DateTool.m in Sources */, 0089687F2486DA9600DC48C2 /* BugsnagBreadcrumb.m in Sources */, 008968012486DA4500DC48C2 /* BugsnagSessionTrackingApiClient.m in Sources */, @@ -3067,11 +3125,11 @@ CBAB4DDB2510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, 00AD1F2A2486A17900A27979 /* BugsnagCrashSentry.m in Sources */, 008968052486DA4500DC48C2 /* BSGConnectivity.m in Sources */, - 00AD1F162486A17900A27979 /* BugsnagErrorReportSink.m in Sources */, + 0126F7A125DD510E008483C2 /* BSGEventUploadObjectOperation.m in Sources */, 0089689C2486DA9600DC48C2 /* BugsnagStackframe.m in Sources */, 008967DD2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, 008968E42486DAA700DC48C2 /* BugsnagPluginClient.m in Sources */, - 008968092486DA4500DC48C2 /* BugsnagErrorReportApiClient.m in Sources */, + 01840B7525DC26E200F95648 /* BSGEventUploader.m in Sources */, 008968BC2486DA9600DC48C2 /* BugsnagStacktrace.m in Sources */, 008968242486DA5600DC48C2 /* BugsnagKeys.m in Sources */, 008967B72486D9D800DC48C2 /* BugsnagBreadcrumbs.m in Sources */, @@ -3082,9 +3140,11 @@ 015F528725C15BB7000D1915 /* MRCCanary.m in Sources */, 008968A02486DA9600DC48C2 /* BugsnagSessionTrackingPayload.m in Sources */, 0089686E2486DA9500DC48C2 /* BugsnagEvent.m in Sources */, + 0126F79125DD508C008483C2 /* BSGEventUploadOperation.m in Sources */, 008967EB2486DA2D00DC48C2 /* BugsnagErrorTypes.m in Sources */, 008967D62486DA2D00DC48C2 /* BugsnagEndpointConfiguration.m in Sources */, 00AD1F262486A17900A27979 /* Bugsnag.m in Sources */, + 0127149825F6171000D3500A /* BugsnagClient+AppHangs.m in Sources */, 008968CE2486DA9600DC48C2 /* BugsnagThread.m in Sources */, 00AD1F312486A17900A27979 /* BugsnagSessionTracker.m in Sources */, 008968C62486DA9600DC48C2 /* BugsnagUser.m in Sources */, diff --git a/Bugsnag/BugsnagCrashSentry.h b/Bugsnag/BugsnagCrashSentry.h index 9bfc78f12..7a5f20e27 100644 --- a/Bugsnag/BugsnagCrashSentry.h +++ b/Bugsnag/BugsnagCrashSentry.h @@ -11,14 +11,12 @@ #import "BSG_KSCrashReportWriter.h" #import "BSG_KSCrashType.h" #import "BugsnagConfiguration.h" -#import "BugsnagErrorReportApiClient.h" @class BugsnagNotifier; @interface BugsnagCrashSentry : NSObject - (void)install:(BugsnagConfiguration *)config - apiClient:(BugsnagErrorReportApiClient *)apiClient notifier:(BugsnagNotifier *)notifier onCrash:(BSGReportCallback)onCrash; diff --git a/Bugsnag/BugsnagCrashSentry.m b/Bugsnag/BugsnagCrashSentry.m index 9ecef255e..fc23b5ccb 100644 --- a/Bugsnag/BugsnagCrashSentry.m +++ b/Bugsnag/BugsnagCrashSentry.m @@ -9,24 +9,21 @@ #import "BugsnagCrashSentry.h" +#import "BSGFileLocations.h" #import "BSG_KSCrashAdvanced.h" #import "BSG_KSCrashC.h" #import "Bugsnag.h" #import "BugsnagConfiguration.h" -#import "BugsnagErrorReportSink.h" #import "BugsnagErrorTypes.h" #import "BugsnagLogger.h" @implementation BugsnagCrashSentry - (void)install:(BugsnagConfiguration *)config - apiClient:(BugsnagErrorReportApiClient *)apiClient notifier:(BugsnagNotifier *)notifier onCrash:(BSGReportCallback)onCrash { - BugsnagErrorReportSink *sink = [[BugsnagErrorReportSink alloc] initWithApiClient:apiClient configuration:config notifier:notifier]; BSG_KSCrash *ksCrash = [BSG_KSCrash sharedInstance]; - ksCrash.sink = sink; ksCrash.introspectMemory = NO; ksCrash.onCrash = onCrash; ksCrash.maxStoredReports = (int)config.maxPersistedEvents; @@ -47,11 +44,9 @@ - (void)install:(BugsnagConfiguration *)config bsg_kscrash_setHandlingCrashTypes(crashTypes); - if ((![ksCrash install])) { + if ((![ksCrash install:[BSGFileLocations current].kscrashReports])) { bsg_log_err(@"Failed to install crash handler. No exceptions will be reported!"); } - - [sink.apiClient flushPendingData]; } /** diff --git a/Bugsnag/BugsnagErrorReportSink+Private.h b/Bugsnag/BugsnagErrorReportSink+Private.h deleted file mode 100644 index 1a4994be8..000000000 --- a/Bugsnag/BugsnagErrorReportSink+Private.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// BugsnagErrorReportSink+Private.h -// Bugsnag -// -// Created by Nick Dowell on 04/12/2020. -// Copyright © 2020 Bugsnag Inc. All rights reserved. -// - -#import "BugsnagErrorReportSink.h" - -@class BugsnagEvent; - -NS_ASSUME_NONNULL_BEGIN - -@interface BugsnagErrorReportSink () - -#pragma mark Methods - -- (NSDictionary *)prepareEventPayload:(BugsnagEvent *)event; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Bugsnag/BugsnagErrorReportSink.h b/Bugsnag/BugsnagErrorReportSink.h deleted file mode 100644 index 2339e1bb8..000000000 --- a/Bugsnag/BugsnagErrorReportSink.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// BugsnagErrorReportSink.h -// -// Created by Conrad Irwin on 2014-10-01. -// -// Copyright (c) 2014 Bugsnag, Inc. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall remain in place -// in this source code. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import - -#import "BSGOnErrorSentBlock.h" - -@class BugsnagConfiguration; -@class BugsnagErrorReportApiClient; -@class BugsnagNotifier; - -NS_ASSUME_NONNULL_BEGIN - -@interface BugsnagErrorReportSink : NSObject - -- (instancetype)initWithApiClient:(BugsnagErrorReportApiClient *)apiClient - configuration:(BugsnagConfiguration *)configuration - notifier:(BugsnagNotifier *)notifier; - -@property (strong, nonatomic) BugsnagErrorReportApiClient *apiClient; - -@property (strong, nonatomic) BugsnagConfiguration *configuration; - -@property (strong, nonatomic) BugsnagNotifier *notifier; - -/** - * Invoked when reports stored by KSCrash need to be delivered. - * - * @param ksCrashReports a map of KSCrash reports represented as dictionaries, with the filename as the key - * @param block a block that is invoked when delivery of each file concludes - */ -- (void)sendStoredReports:(NSDictionary *)ksCrashReports - withBlock:(BSGOnErrorSentBlock)block; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Bugsnag/BugsnagErrorReportSink.m b/Bugsnag/BugsnagErrorReportSink.m deleted file mode 100644 index b02690713..000000000 --- a/Bugsnag/BugsnagErrorReportSink.m +++ /dev/null @@ -1,169 +0,0 @@ -// -// BugsnagSink.m -// -// Created by Conrad Irwin on 2014-10-01. -// -// Copyright (c) 2014 Bugsnag, Inc. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall remain in place -// in this source code. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import "BugsnagErrorReportSink+Private.h" - -#import "BSG_KSSystemInfo.h" -#import "Bugsnag+Private.h" -#import "BugsnagClient+Private.h" -#import "BugsnagCollections.h" -#import "BugsnagConfiguration+Private.h" -#import "BugsnagErrorReportApiClient.h" -#import "BugsnagEvent+Private.h" -#import "BugsnagKeys.h" -#import "BugsnagLogger.h" -#import "BugsnagNotifier.h" - -@interface BugsnagErrorReportSink () -@property NSMutableSet *activeRequests; -@end - -@implementation BugsnagErrorReportSink - -- (instancetype)initWithApiClient:(BugsnagErrorReportApiClient *)apiClient - configuration:(BugsnagConfiguration *)configuration - notifier:(BugsnagNotifier *)notifier { - if ((self = [super init])) { - _apiClient = apiClient; - _activeRequests = [NSMutableSet new]; - _configuration = configuration; - _notifier = notifier; - } - return self; -} - -/** - * Returns a list of filenames which have no ongoing request and need to be delivered to the error reporting API. - * - * To prevent duplicate reports filenames are recorded in an internal collection when they are - * in the process of being sent in a request. This method adds any unqueued files to this internal collection. - * Once the request has complete the filename should be removed via a separate method. - */ -- (NSArray *)prepareNewRequests:(NSArray *)allRequests { - NSMutableArray *newRequests = [allRequests mutableCopy]; - @synchronized (self.activeRequests) { - NSArray *enqueuedRequests = [self.activeRequests allObjects]; - [newRequests removeObjectsInArray:enqueuedRequests]; - [self.activeRequests addObjectsFromArray:newRequests]; - } - return newRequests; -} - -- (void)finishActiveRequest:(nonnull NSString *)requestId - completed:(BOOL)completed - error:(NSError *)error - block:(nonnull BSGOnErrorSentBlock)block { - block(requestId, completed, error); - @synchronized (self.activeRequests) { - [self.activeRequests removeObject:requestId]; - } -} - -- (void)sendStoredReports:(NSDictionary *)ksCrashReports - withBlock:(BSGOnErrorSentBlock)block { - - // 1. check whether filenames are in the dictionary, if so add them - // 2. If not, add them and enqueue a request - // 3. If so, ignore them and perform no action - // 4. When a request has completed and deleted the file, remove the files from the dictionary - NSArray *keys = [self prepareNewRequests:[ksCrashReports allKeys]]; - NSMutableDictionary* storedEvents = [NSMutableDictionary new]; - - // run user callbacks on events before enqueueing any requests, as - // this way events can be discarded quickly. This frees up disk - // space for any events which are captured in the meantime. - for (NSString *fileKey in keys) { - NSDictionary *report = ksCrashReports[fileKey]; - BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:report]; - event.redactedKeys = self.configuration.redactedKeys; - - NSString *errorClass = event.errors.firstObject.errorClass; - if ([self.configuration shouldDiscardErrorClass:errorClass]) { - bsg_log_info(@"Discarding event because errorClass \"%@\" matched configuration.discardClasses", errorClass); - [self finishActiveRequest:fileKey completed:YES error:nil block:block]; - continue; - } - - if (self.configuration.shouldSendReports && [event shouldBeSent] && [self runOnSendBlocksForEvent:event]) { - storedEvents[fileKey] = event; - } else { // delete the report as the user has discarded it - [self finishActiveRequest:fileKey completed:YES error:nil block:block]; - } - } - [self deliverStoredEvents:storedEvents block:block]; -} - -- (void)deliverStoredEvents:(NSMutableDictionary *)storedEvents block:(BSGOnErrorSentBlock)block { - for (NSString *filename in storedEvents) { - BugsnagEvent *event = storedEvents[filename]; - NSDictionary *requestPayload = [self prepareEventPayload:event]; - - NSMutableDictionary *apiHeaders = [self.configuration.errorApiHeaders mutableCopy]; - apiHeaders[BugsnagHTTPHeaderNameApiKey] = event.apiKey; - apiHeaders[BugsnagHTTPHeaderNameStacktraceTypes] = [event.stacktraceTypes componentsJoinedByString:@","]; - [self.apiClient sendJSONPayload:requestPayload headers:apiHeaders toURL:self.configuration.notifyURL - completionHandler:^(BugsnagApiClientDeliveryStatus status, NSError *error) { - BOOL completed = status == BugsnagApiClientDeliveryStatusDelivered || status == BugsnagApiClientDeliveryStatusUndeliverable; - [self finishActiveRequest:filename completed:completed error:error block:block]; - }]; - } -} - -- (BOOL)runOnSendBlocksForEvent:(BugsnagEvent *)event { - for (BugsnagOnSendErrorBlock onSendErrorBlock in self.configuration.onSendBlocks) { - @try { - if (!onSendErrorBlock(event)) { - return false; - } - } @catch (NSException *exception) { - bsg_log_err(@"Error from onSend callback: %@", exception); - } - } - return true; -} - -/** - * Generates an Error Reporting API payload that can be sent to Bugsnag. - * @param event a single BugsnagEvent - * @return an Error Reporting API payload represented as a serializable dictionary - */ -- (NSDictionary *)prepareEventPayload:(BugsnagEvent *)event { - if (!event.app.type) { - // Use current value for crashes from older notifier versions that didn't persist config.appType - event.app.type = self.configuration.appType; - } - if (!event.apiKey) { - event.apiKey = self.configuration.apiKey; - } - NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; - data[BSGKeyNotifier] = [self.notifier toDict]; - data[BSGKeyApiKey] = event.apiKey; - data[BSGKeyPayloadVersion] = @"4.0"; - data[BSGKeyEvents] = @[[event toJson]]; - return data; -} - -@end diff --git a/Bugsnag/Client/BugsnagClient+AppHangs.h b/Bugsnag/Client/BugsnagClient+AppHangs.h new file mode 100644 index 000000000..dc97ee04f --- /dev/null +++ b/Bugsnag/Client/BugsnagClient+AppHangs.h @@ -0,0 +1,23 @@ +// +// BugsnagClient+AppHangs.h +// Bugsnag +// +// Created by Nick Dowell on 08/03/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BugsnagClient+Private.h" + +#import "BSGAppHangDetector.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface BugsnagClient (AppHangs) + +- (BOOL)lastRunEndedWithAppHang; + +- (void)startAppHangDetector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Client/BugsnagClient+AppHangs.m b/Bugsnag/Client/BugsnagClient+AppHangs.m new file mode 100644 index 000000000..b524dbb1e --- /dev/null +++ b/Bugsnag/Client/BugsnagClient+AppHangs.m @@ -0,0 +1,113 @@ +// +// BugsnagClient+AppHangs.m +// Bugsnag +// +// Created by Nick Dowell on 08/03/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BugsnagClient+AppHangs.h" + +#import "BSGEventUploader.h" +#import "BSGFileLocations.h" +#import "BSGJSONSerialization.h" +#import "BSG_KSSystemInfo.h" +#import "BugsnagBreadcrumbs.h" +#import "BugsnagError+Private.h" +#import "BugsnagEvent+Private.h" +#import "BugsnagHandledState.h" +#import "BugsnagLogger.h" +#import "BugsnagSession+Private.h" +#import "BugsnagSessionTracker.h" +#import "BugsnagThread+Private.h" + +@implementation BugsnagClient (AppHangs) + +- (void)startAppHangDetector { + [NSFileManager.defaultManager removeItemAtPath:BSGFileLocations.current.appHangEvent error:nil]; + + self.appHangDetector = [[BSGAppHangDetector alloc] init]; + [self.appHangDetector startWithDelegate:self]; +} + +- (void)appHangDetectedWithThreads:(nonnull NSArray *)threads { + NSDictionary *systemInfo = [BSG_KSSystemInfo systemInfo]; + + NSString *message = [NSString stringWithFormat:@"The app's main thread failed to respond to an event within %d milliseconds", + (int)self.configuration.appHangThresholdMillis]; + + BugsnagError *error = + [[BugsnagError alloc] initWithErrorClass:@"App Hang" + errorMessage:message + errorType:BSGErrorTypeCocoa + stacktrace:threads.firstObject.stacktrace]; + + BugsnagHandledState *handledState = + [[BugsnagHandledState alloc] initWithSeverityReason:AppHang + severity:BSGSeverityWarning + unhandled:NO + unhandledOverridden:NO + attrValue:nil]; + + self.appHangEvent = + [[BugsnagEvent alloc] initWithApp:[self generateAppWithState:systemInfo] + device:[self generateDeviceWithState:systemInfo] + handledState:handledState + user:self.configuration.user + metadata:[self.metadata deepCopy] + breadcrumbs:self.breadcrumbs.breadcrumbs + errors:@[error] + threads:threads + session:self.sessionTracker.runningSession]; + + NSError *writeError = nil; + NSDictionary *json = [self.appHangEvent toJsonWithRedactedKeys:self.configuration.redactedKeys]; + if (![BSGJSONSerialization writeJSONObject:json toFile:BSGFileLocations.current.appHangEvent options:0 error:&writeError]) { + bsg_log_err(@"Could not write app_hang.json: %@", error); + } +} + +- (void)appHangEnded { + NSError *error = nil; + if (![NSFileManager.defaultManager removeItemAtPath:BSGFileLocations.current.appHangEvent error:&error]) { + bsg_log_err(@"Could not delete app_hang.json: %@", error); + } + + const BOOL fatalOnly = self.configuration.appHangThresholdMillis == BugsnagAppHangThresholdFatalOnly; + if (!fatalOnly && self.appHangEvent) { + [self notifyInternal:self.appHangEvent block:nil]; + } + self.appHangEvent = nil; +} + +- (BOOL)lastRunEndedWithAppHang { + NSError *error = nil; + NSDictionary *json = [BSGJSONSerialization JSONObjectWithContentsOfFile:BSGFileLocations.current.appHangEvent options:0 error:&error]; + if (!json) { + if (!(error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoSuchFileError)) { + bsg_log_err(@"Could not read app_hang.json: %@", error); + } + return NO; + } + + BugsnagEvent *event = [[BugsnagEvent alloc] initWithJson:json]; + if (!event) { + bsg_log_err(@"Could not parse app_hang.json"); + return NO; + } + + // Update event to reflect that the app hang was fatal. + event.errors.firstObject.errorMessage = @"The app was terminated while unresponsive"; + // Cannot set event.severity directly because that sets severityReason.type to "userCallbackSetSeverity" + event.handledState = [[BugsnagHandledState alloc] initWithSeverityReason:AppHang + severity:BSGSeverityError + unhandled:YES + unhandledOverridden:NO + attrValue:nil]; + event.session.unhandledCount++; + + self.appHangEvent = event; + return YES; +} + +@end diff --git a/Bugsnag/Client/BugsnagClient+Private.h b/Bugsnag/Client/BugsnagClient+Private.h index c54b3e930..71e45d44c 100644 --- a/Bugsnag/Client/BugsnagClient+Private.h +++ b/Bugsnag/Client/BugsnagClient+Private.h @@ -10,10 +10,13 @@ #import "BugsnagMetadata+Private.h" // For BugsnagObserverBlock +@class BSGAppHangDetector; +@class BSGEventUploader; +@class BugsnagAppWithState; @class BugsnagBreadcrumbs; @class BugsnagConfiguration; @class BugsnagCrashSentry; -@class BugsnagErrorReportApiClient; +@class BugsnagDeviceWithState; @class BugsnagMetadata; @class BugsnagNotifier; @class BugsnagPluginClient; @@ -28,6 +31,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL appDidCrashLastLaunch; +@property (nonatomic) BSGAppHangDetector *appHangDetector; + +@property (nullable, nonatomic) BugsnagEvent *appHangEvent; + @property (nullable, retain, nonatomic) BugsnagBreadcrumbs *breadcrumbs; @property (nullable, nonatomic) NSString *codeBundleId; @@ -40,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) BugsnagCrashSentry *crashSentry; -@property (strong, nonatomic) BugsnagErrorReportApiClient *errorReportApiClient; +@property (strong, nonatomic) BSGEventUploader *eventUploader; @property NSMutableDictionary *extraRuntimeInfo; @@ -90,7 +97,11 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)collectThreads:(BOOL)unhandled; // Used in BugsnagReactNative -- (void)notifyInternal:(BugsnagEvent *)event block:(BugsnagOnErrorBlock)block; +- (BugsnagAppWithState *)generateAppWithState:(NSDictionary *)systemInfo; + +- (BugsnagDeviceWithState *)generateDeviceWithState:(NSDictionary *)systemInfo; + +- (void)notifyInternal:(BugsnagEvent *)event block:(nullable BugsnagOnErrorBlock)block; - (void)removeObserverWithBlock:(BugsnagObserverBlock)block; // Used in BugsnagReactNative diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index 5f49ffb27..5e86da079 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -29,6 +29,7 @@ #import "BugsnagClient+Private.h" #import "BSGConnectivity.h" +#import "BSGEventUploader.h" #import "BSGFileLocations.h" #import "BSGJSONSerialization.h" #import "BSGNotificationBreadcrumbs.h" @@ -46,6 +47,7 @@ #import "BugsnagAppWithState+Private.h" #import "BugsnagBreadcrumb+Private.h" #import "BugsnagBreadcrumbs.h" +#import "BugsnagClient+AppHangs.h" #import "BugsnagCollections.h" #import "BugsnagConfiguration+Private.h" #import "BugsnagCrashSentry.h" @@ -221,12 +223,15 @@ void BSGWriteSessionCrashData(BugsnagSession *session) { @interface BugsnagClient () @property BSGNotificationBreadcrumbs *notificationBreadcrumbs; + @property (weak) NSTimer *appLaunchTimer; @end + #if __clang_major__ >= 11 // Xcode 10 does not like the following attribute __attribute__((annotate("oclint:suppress[long class]"))) +__attribute__((annotate("oclint:suppress[too many methods]"))) #endif @implementation BugsnagClient @@ -265,7 +270,7 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration { self.stateEventBlocks = [NSMutableArray new]; self.extraRuntimeInfo = [NSMutableDictionary new]; self.crashSentry = [BugsnagCrashSentry new]; - self.errorReportApiClient = [[BugsnagErrorReportApiClient alloc] initWithSession:configuration.session queueName:@"Error API queue"]; + _eventUploader = [[BSGEventUploader alloc] initWithConfiguration:_configuration notifier:_notifier]; bsg_g_bugsnag_data.onCrash = (void (*)(const BSG_KSCrashReportWriter *))self.configuration.onCrashHandler; _notificationBreadcrumbs = [[BSGNotificationBreadcrumbs alloc] initWithConfiguration:configuration breadcrumbSink:self]; @@ -346,7 +351,7 @@ - (void)notifyObservers:(BugsnagStateEvent *)event { - (void)start { [self.configuration validate]; - [self.crashSentry install:self.configuration apiClient:self.errorReportApiClient notifier:self.notifier onCrash:&BSSerializeDataCrashHandler]; + [self.crashSentry install:self.configuration notifier:self.notifier onCrash:&BSSerializeDataCrashHandler]; [self.systemState recordAppUUID]; // Needs to be called after crashSentry installed but before -computeDidCrashLastLaunch [self computeDidCrashLastLaunch]; [self.breadcrumbs removeAllBreadcrumbs]; @@ -417,9 +422,23 @@ - (void)start { userInfo:nil repeats:NO]; } - if (self.lastRunInfo.crashedDuringLaunch && self.configuration.sendLaunchCrashesSynchronously) { + if (self.appHangEvent) { + [self.eventUploader uploadEvent:self.appHangEvent]; + self.appHangEvent = nil; + } else if (self.lastRunInfo.crashedDuringLaunch && self.configuration.sendLaunchCrashesSynchronously) { [self sendLaunchCrashSynchronously]; } + + [self.eventUploader uploadStoredEvents]; + + // App hang detector deliberately started after sendLaunchCrashSynchronously (which by design may itself trigger an app hang) + if (self.configuration.enabledErrorTypes.appHangs) { + [self startAppHangDetector]; + } + + self.configMetadataFromLastLaunch = nil; + self.metadataFromLastLaunch = nil; + self.stateMetadataFromLastLaunch = nil; } - (void)appLaunchTimerFired:(NSTimer *)timer { @@ -440,7 +459,7 @@ - (void)sendLaunchCrashSynchronously { bsg_log_info(@"Sending launch crash synchronously."); dispatch_time_t deadline = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [[BSG_KSCrash sharedInstance] sendLatestReport:^{ + [self.eventUploader uploadLatestStoredEvent:^{ bsg_log_debug(@"Sent launch crash."); dispatch_semaphore_signal(semaphore); }]; @@ -532,12 +551,13 @@ - (void)addTerminationObserver:(NSString *)name { } - (void)computeDidCrashLastLaunch { + BOOL didCrash = NO; + const BSG_KSCrash_State *crashState = bsg_kscrashstate_currentState(); - BOOL didOOMLastLaunch = [self shouldReportOOM]; NSFileManager *manager = [NSFileManager defaultManager]; NSString *didCrashSentinelPath = [NSString stringWithUTF8String:crashSentinelPath]; BOOL appCrashSentinelExists = [manager fileExistsAtPath:didCrashSentinelPath]; - BOOL handledCrashLastLaunch = appCrashSentinelExists || crashState->crashedLastLaunch; + didCrash = appCrashSentinelExists || crashState->crashedLastLaunch; if (appCrashSentinelExists) { NSError *error = nil; if (![manager removeItemAtPath:didCrashSentinelPath error:&error]) { @@ -545,7 +565,19 @@ - (void)computeDidCrashLastLaunch { unlink(crashSentinelPath); } } - BOOL didCrash = handledCrashLastLaunch || didOOMLastLaunch; + + // App hangs take precedence over OOMs. + if (!didCrash) { + // Avoid calling -lastRunEndedWithAppHang if the app crashed, to prevent self.appHangEvent being set and the hang being reported. + didCrash = [self lastRunEndedWithAppHang]; + } + + BOOL shouldReportOOM = NO; + if (!didCrash) { + shouldReportOOM = [self shouldReportOOM]; + didCrash = shouldReportOOM; + } + self.appDidCrashLastLaunch = didCrash; BOOL wasLaunching = [self.stateMetadataFromLastLaunch[BSGKeyApp][BSGKeyIsLaunching] boolValue]; @@ -560,25 +592,9 @@ - (void)computeDidCrashLastLaunch { crashed:didCrash crashedDuringLaunch:didCrashDuringLaunch]; - // Ignore potential false positive OOM if previous session crashed and was - // reported. There are two checks in place: - // - // 1. crashState->crashedLastLaunch: Accurate unless the crash callback crashes - // - // 2. crash sentinel file exists: This file is written in the event of a crash - // and insures against the crash callback crashing - - if (!handledCrashLastLaunch && didOOMLastLaunch) { - void *onCrash = bsg_g_bugsnag_data.onCrash; - // onCrash should not be called for OOMs - bsg_g_bugsnag_data.onCrash = NULL; + if (shouldReportOOM) { [self notifyOutOfMemoryEvent]; - bsg_g_bugsnag_data.onCrash = onCrash; } - - self.configMetadataFromLastLaunch = nil; - self.metadataFromLastLaunch = nil; - self.stateMetadataFromLastLaunch = nil; } - (void)setCodeBundleId:(NSString *)codeBundleId { @@ -652,10 +668,6 @@ - (BOOL)resumeSession { return [self.sessionTracker resumeSession]; } -- (void)flushPendingReports { - [self.errorReportApiClient flushPendingData]; -} - /** * Monitor the Bugsnag endpoint to detect changes in connectivity, * flush pending events when (re)connected and report connectivity @@ -673,7 +685,7 @@ - (void)setupConnectivityListener { usingCallback:^(BOOL connected, NSString *connectionType) { __strong typeof(weakSelf) strongSelf = weakSelf; if (connected) { - [strongSelf flushPendingReports]; + [strongSelf.eventUploader uploadStoredEvents]; } [strongSelf addAutoBreadcrumbOfType:BSGBreadcrumbTypeState @@ -864,6 +876,11 @@ - (void)notifyOutOfMemoryEvent { NSMutableDictionary *appState = [self.stateMetadataFromLastLaunch mutableCopy] ?: [NSMutableDictionary dictionary]; appState[@"didOOM"] = @YES; appState[@"oom"] = lastLaunchInfo; + + // onCrash should not be called for OOMs + void *onCrash = bsg_g_bugsnag_data.onCrash; + bsg_g_bugsnag_data.onCrash = NULL; + [self.crashSentry reportUserException:BSGOutOfMemoryErrorClass reason:message handledState:[handledState toJson] @@ -872,6 +889,8 @@ - (void)notifyOutOfMemoryEvent { eventOverrides:nil metadata:self.metadataFromLastLaunch config:self.configMetadataFromLastLaunch]; + + bsg_g_bugsnag_data.onCrash = onCrash; } - (void)notify:(NSException *)exception @@ -940,10 +959,17 @@ - (void)notify:(NSException *)exception - (void)notifyInternal:(BugsnagEvent *_Nonnull)event block:(BugsnagOnErrorBlock)block { + if ([self shouldNotifyEvent:event withOnErrorBlock:block]) { + [self deliverEvent:event]; + [self addAutoBreadcrumbForEvent:event]; + } +} + +- (BOOL)shouldNotifyEvent:(nonnull BugsnagEvent *)event withOnErrorBlock:(nullable BugsnagOnErrorBlock)block { NSString *errorClass = event.errors.firstObject.errorClass; if ([self.configuration shouldDiscardErrorClass:errorClass]) { bsg_log_info(@"Discarding event because errorClass \"%@\" matched configuration.discardClasses", errorClass); - return; + return NO; } // enhance device information with additional metadata @@ -956,7 +982,7 @@ - (void)notifyInternal:(BugsnagEvent *_Nonnull)event BOOL originalUnhandledValue = event.unhandled; @try { if (block != nil && !block(event)) { // skip notifying if callback false - return; + return NO; } } @catch (NSException *exception) { bsg_log_err(@"Error from onError callback: %@", exception); @@ -965,15 +991,25 @@ - (void)notifyInternal:(BugsnagEvent *_Nonnull)event [event notifyUnhandledOverridden]; } + return YES; +} + +- (void)deliverEvent:(BugsnagEvent *)event { if (event.handledState.unhandled) { [self.sessionTracker handleUnhandledErrorEvent]; } else { [self.sessionTracker handleHandledErrorEvent]; } + // Temporary conditional until all (non-crash) events can be sent via -uploadEvent: + if (event == self.appHangEvent) { + [self.eventUploader uploadEvent:event]; + return; + } + // apiKey not added to event JSON by default, need to add it here // for when it is read next - NSMutableDictionary *eventOverrides = [[event toJson] mutableCopy]; + NSMutableDictionary *eventOverrides = [[event toJsonWithRedactedKeys:self.configuration.redactedKeys] mutableCopy]; eventOverrides[BSGKeyApiKey] = event.apiKey; // handled errors should persist any information edited by the user @@ -988,12 +1024,17 @@ - (void)notifyInternal:(BugsnagEvent *_Nonnull)event metadata:[event.metadata toDictionary] config:self.configuration.dictionaryRepresentation]; + [self.eventUploader uploadStoredEvents]; +} + +// MARK: - Breadcrumbs + +- (void)addAutoBreadcrumbForEvent:(BugsnagEvent *)event { // A basic set of event metadata - NSMutableDictionary *metadata = [@{ - BSGKeyErrorClass : event.errors[0].errorClass, - BSGKeyUnhandled : [[event handledState] unhandled] ? @YES : @NO, - BSGKeySeverity : BSGFormatSeverity(event.severity) - } mutableCopy]; + NSMutableDictionary *metadata = [NSMutableDictionary dictionary]; + metadata[BSGKeyErrorClass] = event.errors[0].errorClass; + metadata[BSGKeyUnhandled] = @(event.handledState.unhandled); + metadata[BSGKeySeverity] = BSGFormatSeverity(event.severity); // Only include the eventMessage if it contains something NSString *eventMessage = event.errors[0].errorMessage; @@ -1004,12 +1045,8 @@ - (void)notifyInternal:(BugsnagEvent *_Nonnull)event [self addAutoBreadcrumbOfType:BSGBreadcrumbTypeError withMessage:event.errors[0].errorClass andMetadata:metadata]; - - [self flushPendingReports]; } -// MARK: - Breadcrumbs - - (void)addBreadcrumbWithBlock:(void (^)(BugsnagBreadcrumb *))block { [self.breadcrumbs addBreadcrumbWithBlock:block]; } diff --git a/Bugsnag/Configuration/BugsnagConfiguration+Private.h b/Bugsnag/Configuration/BugsnagConfiguration+Private.h index ecf30e8ce..4ecd91d96 100644 --- a/Bugsnag/Configuration/BugsnagConfiguration+Private.h +++ b/Bugsnag/Configuration/BugsnagConfiguration+Private.h @@ -23,8 +23,6 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) NSDictionary *dictionaryRepresentation; -@property (readonly) NSDictionary *errorApiHeaders; - @property (readonly, copy, nonatomic) BugsnagMetadata *metadata; @property (readonly, nullable, nonatomic) NSURL *notifyURL; diff --git a/Bugsnag/Configuration/BugsnagConfiguration.m b/Bugsnag/Configuration/BugsnagConfiguration.m index 2df2fec68..58c421fb6 100644 --- a/Bugsnag/Configuration/BugsnagConfiguration.m +++ b/Bugsnag/Configuration/BugsnagConfiguration.m @@ -38,6 +38,8 @@ #import "BugsnagMetadata+Private.h" #import "BugsnagUser+Private.h" +const NSUInteger BugsnagAppHangThresholdFatalOnly = INT_MAX; + static const int BSGApiKeyLength = 32; // User info persistence keys @@ -78,6 +80,7 @@ + (instancetype)loadConfigFromOptions:(NSDictionary *)options { - (nonnull id)copyWithZone:(nullable NSZone *)zone { BugsnagConfiguration *copy = [[BugsnagConfiguration alloc] initWithApiKey:[self.apiKey copy]]; // Omit apiKey - it's set explicitly in the line above + [copy setAppHangThresholdMillis:self.appHangThresholdMillis]; [copy setAppType:self.appType]; [copy setAppVersion:self.appVersion]; [copy setAutoDetectErrors:self.autoDetectErrors]; @@ -168,6 +171,7 @@ - (instancetype)initWithApiKey:(NSString *)apiKey { _endpoints = [BugsnagEndpointConfiguration new]; _sessionURL = [NSURL URLWithString:@"https://sessions.bugsnag.com"]; _autoDetectErrors = YES; + _appHangThresholdMillis = BugsnagAppHangThresholdFatalOnly; _notifyURL = [NSURL URLWithString:BSGDefaultNotifyUrl]; _onSendBlocks = [NSMutableArray new]; _onSessionBlocks = [NSMutableArray new]; @@ -326,13 +330,6 @@ - (void)removeOnBreadcrumbBlock:(BugsnagOnBreadcrumbBlock _Nonnull)block { [(NSMutableArray *)self.onBreadcrumbBlocks removeObject:block]; } -- (NSDictionary *)errorApiHeaders { - return @{BugsnagHTTPHeaderNameApiKey: self.apiKey ?: @"", - BugsnagHTTPHeaderNamePayloadVersion: @"4.0", - BugsnagHTTPHeaderNameSentAt: [BSG_RFC3339DateTool stringFromDate:[NSDate date]] - }; -} - - (NSDictionary *)sessionApiHeaders { return @{BugsnagHTTPHeaderNameApiKey: self.apiKey ?: @"", BugsnagHTTPHeaderNamePayloadVersion: @"1.0", @@ -445,6 +442,16 @@ -(void)deletePersistedUserData { // MARK: - Properties: Getters and Setters // ----------------------------------------------------------------------------- +- (void)setAppHangThresholdMillis:(NSUInteger)appHangThresholdMillis { + if (appHangThresholdMillis >= 250) { + _appHangThresholdMillis = appHangThresholdMillis; + } else { + bsg_log_err(@"Invalid configuration value detected. Option appHangThresholdMillis " + "should be greater than or equal to 250. Supplied value is %lu", + (unsigned long)appHangThresholdMillis); + } +} + - (void)setMaxPersistedEvents:(NSUInteger)maxPersistedEvents { @synchronized (self) { if (maxPersistedEvents >= 1) { diff --git a/Bugsnag/Configuration/BugsnagErrorTypes.m b/Bugsnag/Configuration/BugsnagErrorTypes.m index 04cb5922f..51bddb5bd 100644 --- a/Bugsnag/Configuration/BugsnagErrorTypes.m +++ b/Bugsnag/Configuration/BugsnagErrorTypes.m @@ -12,12 +12,13 @@ @implementation BugsnagErrorTypes - (instancetype)init { if (self = [super init]) { - _unhandledExceptions = true; - _signals = true; - _cppExceptions = true; - _machExceptions = true; - _unhandledRejections = true; - _ooms = true; + _appHangs = YES; + _unhandledExceptions = YES; + _signals = YES; + _cppExceptions = YES; + _machExceptions = YES; + _unhandledRejections = YES; + _ooms = YES; } return self; } diff --git a/Bugsnag/Delivery/BSGEventUploadFileOperation.h b/Bugsnag/Delivery/BSGEventUploadFileOperation.h new file mode 100644 index 000000000..e48842b7b --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadFileOperation.h @@ -0,0 +1,24 @@ +// +// BSGEventUploadFileOperation.h +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * A concrete operation class for uploading an event that is stored on disk. + */ +@interface BSGEventUploadFileOperation : BSGEventUploadOperation + +- (instancetype)initWithFile:(NSString *)file delegate:(id)delegate; + +@property (copy, nonatomic) NSString *file; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Delivery/BSGEventUploadFileOperation.m b/Bugsnag/Delivery/BSGEventUploadFileOperation.m new file mode 100644 index 000000000..39cd67f88 --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadFileOperation.m @@ -0,0 +1,51 @@ +// +// BSGEventUploadFileOperation.m +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadFileOperation.h" + +#import "BSGFileLocations.h" +#import "BSGJSONSerialization.h" +#import "BugsnagEvent+Private.h" +#import "BugsnagLogger.h" + + +@implementation BSGEventUploadFileOperation + +- (instancetype)initWithFile:(NSString *)file delegate:(id)delegate { + if (self = [super initWithDelegate:delegate]) { + _file = [file copy]; + } + return self; +} + +- (BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr { + id json = [BSGJSONSerialization JSONObjectWithContentsOfFile:self.file options:0 error:errorPtr]; + if (![json isKindOfClass:[NSDictionary class]]) { + return nil; + } + return [[BugsnagEvent alloc] initWithJson:json]; +} + +- (void)deleteEvent { + NSError *error = nil; + if ([NSFileManager.defaultManager removeItemAtPath:self.file error:&error]) { + bsg_log_debug(@"Deleted event %@", self.name); + } else { + bsg_log_err(@"%@", error); + } +} + +- (void)storeEventPayload:(NSDictionary *)eventPayload { + // This event was loaded from disk, so nothing needs to be saved. +} + +- (NSString *)name { + return self.file.lastPathComponent; +} + +@end diff --git a/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.h b/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.h new file mode 100644 index 000000000..c83382adf --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.h @@ -0,0 +1,20 @@ +// +// BSGEventUploadKSCrashReportOperation.h +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadFileOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * A concrete operation class for reading a KSCrashReport from disk, converting it into a BugsnagEvent, and uploading. + */ +@interface BSGEventUploadKSCrashReportOperation : BSGEventUploadFileOperation + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m b/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m new file mode 100644 index 000000000..f3566142b --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadKSCrashReportOperation.m @@ -0,0 +1,114 @@ +// +// BSGEventUploadKSCrashReportOperation.m +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadKSCrashReportOperation.h" + +#import "BSGJSONSerialization.h" +#import "BSG_KSCrashDoctor.h" +#import "BSG_KSCrashReportFields.h" +#import "BSG_RFC3339DateTool.h" +#import "BugsnagAppWithState.h" +#import "BugsnagCollections.h" +#import "BugsnagConfiguration.h" +#import "BugsnagEvent+Private.h" +#import "BugsnagLogger.h" + + +@implementation BSGEventUploadKSCrashReportOperation + +- (BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr { + id json = [BSGJSONSerialization JSONObjectWithContentsOfFile:self.file options:0 error:errorPtr]; + if (!json) { + return nil; + } + + json = [self fixupCrashReport:json]; + if (!json) { + return nil; + } + + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:json]; + + if (!event.app.type) { + // Use current value for crashes from older notifier versions that didn't persist config.appType + event.app.type = self.delegate.configuration.appType; + } + + return event; +} + +// Methods below were copied from BSG_KSCrashReportStore.m + +- (NSMutableDictionary *)fixupCrashReport:(NSDictionary *)report { + if (![report isKindOfClass:[NSDictionary class]]) { + bsg_log_err(@"Report should be a dictionary, not %@", [report class]); + return nil; + } + + NSMutableDictionary *mutableReport = [report mutableCopy]; + NSMutableDictionary *mutableInfo = + [report[@BSG_KSCrashField_Report] mutableCopy]; + mutableReport[@BSG_KSCrashField_Report] = mutableInfo; + + // Timestamp gets stored as a unix timestamp. Convert it to rfc3339. + [self convertTimestamp:@BSG_KSCrashField_Timestamp inReport:mutableInfo]; + + [self mergeDictWithKey:@BSG_KSCrashField_SystemAtCrash + intoDictWithKey:@BSG_KSCrashField_System + inReport:mutableReport]; + + [self mergeDictWithKey:@BSG_KSCrashField_UserAtCrash + intoDictWithKey:@BSG_KSCrashField_User + inReport:mutableReport]; + + NSMutableDictionary *crashReport = + [report[@BSG_KSCrashField_Crash] mutableCopy]; + mutableReport[@BSG_KSCrashField_Crash] = crashReport; + BSG_KSCrashDoctor *doctor = [BSG_KSCrashDoctor doctor]; + crashReport[@BSG_KSCrashField_Diagnosis] = [doctor diagnoseCrash:report]; + + return mutableReport; +} + +- (void)mergeDictWithKey:(NSString *)srcKey + intoDictWithKey:(NSString *)dstKey + inReport:(NSMutableDictionary *)report { + NSDictionary *srcDict = report[srcKey]; + if (srcDict == nil) { + // It's OK if the source dict didn't exist. + return; + } + + NSDictionary *dstDict = report[dstKey]; + if (dstDict == nil) { + dstDict = @{}; + } + if (![dstDict isKindOfClass:[NSDictionary class]]) { + bsg_log_err(@"'%@' should be a dictionary, not %@", dstKey, + [dstDict class]); + return; + } + + report[dstKey] = BSGDictMerge(srcDict, dstDict); + [report removeObjectForKey:srcKey]; +} + +- (void)convertTimestamp:(NSString *)key + inReport:(NSMutableDictionary *)report { + NSNumber *timestamp = report[key]; + if (timestamp == nil) { + bsg_log_err(@"entry '%@' not found", key); + return; + } + [report + setValue:[BSG_RFC3339DateTool + stringFromUNIXTimestamp:[timestamp unsignedLongLongValue]] + forKey:key]; +} + +@end diff --git a/Bugsnag/Delivery/BSGEventUploadObjectOperation.h b/Bugsnag/Delivery/BSGEventUploadObjectOperation.h new file mode 100644 index 000000000..9729baaaa --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadObjectOperation.h @@ -0,0 +1,26 @@ +// +// BSGEventUploadObjectOperation.h +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * A concrete operation class for uploading an event object in memory. + * + * If the upload needs to be retried, the event will be persisted to disk. + */ +@interface BSGEventUploadObjectOperation : BSGEventUploadOperation + +- (instancetype)initWithEvent:(BugsnagEvent *)event delegate:(id)delegate; + +@property (nonatomic) BugsnagEvent *event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Delivery/BSGEventUploadObjectOperation.m b/Bugsnag/Delivery/BSGEventUploadObjectOperation.m new file mode 100644 index 000000000..6ed204266 --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadObjectOperation.m @@ -0,0 +1,42 @@ +// +// BSGEventUploadObjectOperation.m +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadObjectOperation.h" + +#import "BSGJSONSerialization.h" +#import "BugsnagEvent.h" +#import "BugsnagLogger.h" + +@implementation BSGEventUploadObjectOperation + +- (instancetype)initWithEvent:(BugsnagEvent *)event delegate:(id)delegate { + if (self = [super initWithDelegate:delegate]) { + _event = event; + } + return self; +} + +- (BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr { + return self.event; +} + +- (void)storeEventPayload:(NSDictionary *)eventPayload inDirectory:(NSString *)directory { + NSString *file = [[directory stringByAppendingPathComponent:[NSUUID UUID].UUIDString] stringByAppendingPathExtension:@"json"]; + NSError *error = nil; + if ([BSGJSONSerialization writeJSONObject:eventPayload toFile:file options:0 error:&error]) { + [self.delegate uploadOperationDidStoreEventPayload:self]; + } else { + bsg_log_err(@"Error encountered while saving event payload for retry: %@", error); + } +} + +- (NSString *)name { + return self.event.description; +} + +@end diff --git a/Bugsnag/Delivery/BSGEventUploadOperation.h b/Bugsnag/Delivery/BSGEventUploadOperation.h new file mode 100644 index 000000000..381884956 --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadOperation.h @@ -0,0 +1,65 @@ +// +// BSGEventUploadOperation.h +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import + +#import "BugsnagApiClient.h" + +@class BugsnagConfiguration; +@class BugsnagEvent; +@class BugsnagNotifier; + +NS_ASSUME_NONNULL_BEGIN + +@protocol BSGEventUploadOperationDelegate; + +/** + * The abstract base class for all event upload operations. + * + * Implements an asynchronous NSOperation and the core logic for checking whether an event should be sent, and uploading it. + */ +@interface BSGEventUploadOperation : NSOperation + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithDelegate:(id)delegate; + +@property (readonly, weak, nonatomic) id delegate; + +// MARK: Subclassing + +/// Must be implemented by all subclasses. +- (nullable BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr; + +/// Called if the event should not be sent or failed to upload in a non-retrable way. +/// +/// To be implemented by subclasses that load their data from a file. +- (void)deleteEvent; + +/// Called if the event upload failed but should be retried later. +/// +/// To be implemented by subclasses that need to persist the payload for a future retry. +- (void)storeEventPayload:(NSDictionary *)eventPayload inDirectory:(NSString *)directory; + +@end + +// MARK: - + +@protocol BSGEventUploadOperationDelegate + +@property (readonly, nonatomic) BugsnagApiClient *apiClient; + +@property (readonly, nonatomic) BugsnagConfiguration *configuration; + +@property (readonly, nonatomic) BugsnagNotifier *notifier; + +- (void)uploadOperationDidStoreEventPayload:(BSGEventUploadOperation *)uploadOperation; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Delivery/BSGEventUploadOperation.m b/Bugsnag/Delivery/BSGEventUploadOperation.m new file mode 100644 index 000000000..df8bf9733 --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploadOperation.m @@ -0,0 +1,195 @@ +// +// BSGEventUploadOperation.m +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploadOperation.h" + +#import "BSGFileLocations.h" +#import "BSG_RFC3339DateTool.h" +#import "BugsnagAppWithState+Private.h" +#import "BugsnagConfiguration+Private.h" +#import "BugsnagError+Private.h" +#import "BugsnagEvent+Private.h" +#import "BugsnagKeys.h" +#import "BugsnagLogger.h" +#import "BugsnagNotifier.h" + + +static NSString * const EventPayloadVersion = @"4.0"; + +typedef NS_ENUM(NSUInteger, BSGEventUploadOperationState) { + BSGEventUploadOperationStateReady, + BSGEventUploadOperationStateExecuting, + BSGEventUploadOperationStateFinished, +}; + +@interface BSGEventUploadOperation () + +@property (nonatomic) BSGEventUploadOperationState state; + +@end + +// MARK: - + +@implementation BSGEventUploadOperation + +- (instancetype)initWithDelegate:(id)delegate { + if (self = [super init]) { + _delegate = delegate; + } + return self; +} + +- (void)runWithDelegate:(id)delegate completionHandler:(nonnull void (^)(void))completionHandler { + bsg_log_debug(@"Preparing event %@", self.name); + + NSError *error = nil; + BugsnagEvent *event = [self loadEventAndReturnError:&error]; + if (!event) { + bsg_log_err(@"Failed to load event %@ due to error %@", self.name, error); + if (!(error.domain == NSCocoaErrorDomain && error.code == NSFileReadNoSuchFileError)) { + [self deleteEvent]; + } + completionHandler(); + return; + } + + BugsnagConfiguration *configuration = delegate.configuration; + + if (!configuration.shouldSendReports || ![event shouldBeSent]) { + bsg_log_info(@"Discarding event %@ because releaseStage not in enabledReleaseStages", self.name); + [self deleteEvent]; + completionHandler(); + return; + } + + NSString *errorClass = event.errors.firstObject.errorClass; + if ([configuration shouldDiscardErrorClass:errorClass]) { + bsg_log_info(@"Discarding event %@ because errorClass \"%@\" matches configuration.discardClasses", self.name, errorClass); + [self deleteEvent]; + completionHandler(); + return; + } + + for (BugsnagOnSendErrorBlock block in configuration.onSendBlocks) { + @try { + if (!block(event)) { + [self deleteEvent]; + completionHandler(); + return; + } + } @catch (NSException *exception) { + bsg_log_err(@"Ignoring exception thrown by onSend callback: %@", exception); + } + } + + NSDictionary *eventPayload = [event toJsonWithRedactedKeys:configuration.redactedKeys]; + + NSString *apiKey = event.apiKey ?: configuration.apiKey; + + NSMutableDictionary *requestPayload = [NSMutableDictionary dictionary]; + requestPayload[BSGKeyApiKey] = apiKey; + requestPayload[BSGKeyEvents] = @[eventPayload]; + requestPayload[BSGKeyNotifier] = [delegate.notifier toDict]; + requestPayload[BSGKeyPayloadVersion] = EventPayloadVersion; + + NSMutableDictionary *requestHeaders = [NSMutableDictionary dictionary]; + requestHeaders[BugsnagHTTPHeaderNameApiKey] = apiKey; + requestHeaders[BugsnagHTTPHeaderNamePayloadVersion] = EventPayloadVersion; + requestHeaders[BugsnagHTTPHeaderNameSentAt] = [BSG_RFC3339DateTool stringFromDate:[NSDate date]]; + requestHeaders[BugsnagHTTPHeaderNameStacktraceTypes] = [event.stacktraceTypes componentsJoinedByString:@","]; + + [delegate.apiClient sendJSONPayload:requestPayload headers:requestHeaders toURL:configuration.notifyURL + completionHandler:^(BugsnagApiClientDeliveryStatus status, NSError *error) { + + switch (status) { + case BugsnagApiClientDeliveryStatusDelivered: + bsg_log_debug(@"Uploaded event %@", self.name); + [self deleteEvent]; + break; + + case BugsnagApiClientDeliveryStatusFailed: + bsg_log_debug(@"Upload failed; will retry event %@", self.name); + [self storeEventPayload:eventPayload inDirectory:[BSGFileLocations current].events]; + break; + + case BugsnagApiClientDeliveryStatusUndeliverable: + bsg_log_debug(@"Upload failed; will discard event %@", self.name); + [self deleteEvent]; + break; + } + + completionHandler(); + }]; +} + +// MARK: Subclassing + +- (BugsnagEvent *)loadEventAndReturnError:(NSError **)errorPtr { + // Must be implemented by all subclasses + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (void)deleteEvent { +} + +- (void)storeEventPayload:(NSDictionary *)eventPayload inDirectory:(NSString *)directory { +} + +// MARK: Asynchronous NSOperation implementation + +- (void)start { + if ([self isCancelled]) { + [self setFinished]; + return; + } + + id delegate = self.delegate; + if (!delegate) { + bsg_log_err(@"Upload operation %@ has no delegate", self); + [self setFinished]; + return; + } + + [self willChangeValueForKey:NSStringFromSelector(@selector(isExecuting))]; + self.state = BSGEventUploadOperationStateExecuting; + [self didChangeValueForKey:NSStringFromSelector(@selector(isExecuting))]; + + [self runWithDelegate:delegate completionHandler:^{ + [self setFinished]; + }]; +} + +- (void)setFinished { + if (self.state == BSGEventUploadOperationStateFinished) { + return; + } + [self willChangeValueForKey:NSStringFromSelector(@selector(isExecuting))]; + [self willChangeValueForKey:NSStringFromSelector(@selector(isFinished))]; + self.state = BSGEventUploadOperationStateFinished; + [self didChangeValueForKey:NSStringFromSelector(@selector(isExecuting))]; + [self didChangeValueForKey:NSStringFromSelector(@selector(isFinished))]; +} + +- (BOOL)isAsynchronous { + return YES; +} + +- (BOOL)isReady { + return self.state == BSGEventUploadOperationStateReady; +} + +- (BOOL)isExecuting { + return self.state == BSGEventUploadOperationStateExecuting; +} + +- (BOOL)isFinished { + return self.state == BSGEventUploadOperationStateFinished; +} + +@end diff --git a/Bugsnag/Delivery/BSGEventUploader.h b/Bugsnag/Delivery/BSGEventUploader.h new file mode 100644 index 000000000..4e69601ae --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploader.h @@ -0,0 +1,30 @@ +// +// BSGEventUploader.h +// Bugsnag +// +// Created by Nick Dowell on 16/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import + +@class BugsnagApiClient; +@class BugsnagConfiguration; +@class BugsnagEvent; +@class BugsnagNotifier; + +NS_ASSUME_NONNULL_BEGIN + +@interface BSGEventUploader : NSObject + +- (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration notifier:(BugsnagNotifier *)notifier; + +- (void)uploadEvent:(BugsnagEvent *)event; + +- (void)uploadStoredEvents; + +- (void)uploadLatestStoredEvent:(void (^)(void))completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Delivery/BSGEventUploader.m b/Bugsnag/Delivery/BSGEventUploader.m new file mode 100644 index 000000000..e077f18ca --- /dev/null +++ b/Bugsnag/Delivery/BSGEventUploader.m @@ -0,0 +1,175 @@ +// +// BSGEventUploader.m +// Bugsnag +// +// Created by Nick Dowell on 17/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGEventUploader.h" + +#import "BSGEventUploadKSCrashReportOperation.h" +#import "BSGEventUploadObjectOperation.h" +#import "BSGFileLocations.h" +#import "BugsnagConfiguration.h" +#import "BugsnagLogger.h" + + +@interface BSGEventUploader () + +@property (readonly, nonatomic) NSString *eventsDirectory; + +@property (readonly, nonatomic) NSString *kscrashReportsDirectory; + +@property (readonly, nonatomic) NSOperationQueue *scanQueue; + +@property (readonly, nonatomic) NSOperationQueue *uploadQueue; + +@end + + +// MARK: - + +@implementation BSGEventUploader + +@synthesize apiClient = _apiClient; +@synthesize configuration = _configuration; +@synthesize notifier = _notifier; + +- (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration notifier:(BugsnagNotifier *)notifier { + if (self = [super init]) { + _apiClient = [[BugsnagApiClient alloc] initWithSession:configuration.session queueName:@""]; + _configuration = configuration; + _eventsDirectory = [BSGFileLocations current].events; + _kscrashReportsDirectory = [BSGFileLocations current].kscrashReports; + _notifier = notifier; + _scanQueue = [[NSOperationQueue alloc] init]; + _scanQueue.maxConcurrentOperationCount = 1; + _scanQueue.name = @"com.bugsnag.event-scanner"; + _uploadQueue = [[NSOperationQueue alloc] init]; + _uploadQueue.maxConcurrentOperationCount = 1; + _uploadQueue.name = @"com.bugsnag.event-uploader"; + } + return self; +} + +- (void)dealloc { + [_scanQueue cancelAllOperations]; + [_uploadQueue cancelAllOperations]; +} + +// MARK: - Public API + +- (void)uploadEvent:(BugsnagEvent *)event { + [self.uploadQueue addOperation:[[BSGEventUploadObjectOperation alloc] initWithEvent:event delegate:self]]; +} + +- (void)uploadStoredEvents { + if (self.scanQueue.operationCount > 1) { + // Prevent too many scan operations being scheduled + return; + } + bsg_log_debug(@"Will scan stored events"); + [self.scanQueue addOperationWithBlock:^{ + NSMutableArray *sortedFiles = [self sortedEventFiles]; + [self deleteExcessFiles:sortedFiles]; + NSArray *operations = [self uploadOperationsWithFiles:sortedFiles]; + bsg_log_debug(@"Uploading %lu stored events", (unsigned long)operations.count); + [self.uploadQueue addOperations:operations waitUntilFinished:NO]; + }]; +} + +- (void)uploadLatestStoredEvent:(void (^)(void))completionHandler { + NSString *latestFile = [self sortedEventFiles].lastObject; + BSGEventUploadFileOperation *operation = latestFile ? [self uploadOperationsWithFiles:@[latestFile]].lastObject : nil; + if (!operation) { + bsg_log_warn(@"Could not find a stored event to upload"); + completionHandler(); + return; + } + operation.completionBlock = completionHandler; + [self.uploadQueue addOperation:operation]; +} + +// MARK: - Implementation + +/// Returns the stored event files sorted from oldest to most recent. +- (NSMutableArray *)sortedEventFiles { + NSMutableArray *files = [NSMutableArray array]; + + NSMutableDictionary *creationDates = [NSMutableDictionary dictionary]; + + for (NSString *directory in @[self.eventsDirectory, self.kscrashReportsDirectory]) { + NSError *error = nil; + NSArray *entries = [NSFileManager.defaultManager contentsOfDirectoryAtPath:directory error:&error]; + if (!entries) { + bsg_log_err(@"%@", error); + continue; + } + + for (NSString *filename in entries) { + if (![filename.pathExtension isEqual:@"json"] || [filename hasSuffix:@"-CrashState.json"]) { + continue; + } + + NSString *file = [directory stringByAppendingPathComponent:filename]; + NSDictionary *attributes = [NSFileManager.defaultManager attributesOfItemAtPath:file error:nil]; + creationDates[file] = attributes.fileCreationDate; + [files addObject:file]; + } + } + + [files sortUsingComparator:^NSComparisonResult(NSString *lhs, NSString *rhs) { + return [creationDates[lhs] compare:creationDates[rhs]]; + }]; + + return files; +} + +/// Deletes the oldest files until no more than `config.maxPersistedEvents` remain and removes them from the array. +- (void)deleteExcessFiles:(NSMutableArray *)sortedEventFiles { + while (sortedEventFiles.count > self.configuration.maxPersistedEvents) { + NSString *file = sortedEventFiles[0]; + NSError *error = nil; + if ([NSFileManager.defaultManager removeItemAtPath:file error:&error]) { + bsg_log_debug(@"Deleted %@ to comply with maxPersistedEvents", file); + } else { + bsg_log_err(@"Error while deleting file: %@", error); + } + [sortedEventFiles removeObject:file]; + } +} + +/// Creates an upload operation for each file that is not currently being uploaded +- (NSArray *)uploadOperationsWithFiles:(NSArray *)files { + NSMutableArray *operations = [NSMutableArray array]; + + NSMutableSet *currentFiles = [NSMutableSet set]; + for (id operation in self.uploadQueue.operations) { + if ([operation isKindOfClass:[BSGEventUploadFileOperation class]]) { + [currentFiles addObject:((BSGEventUploadFileOperation *)operation).file]; + } + } + + for (NSString *file in files) { + if ([currentFiles containsObject:file]) { + continue; + } + NSString *directory = file.stringByDeletingLastPathComponent; + if ([directory isEqualToString:self.kscrashReportsDirectory]) { + [operations addObject:[[BSGEventUploadKSCrashReportOperation alloc] initWithFile:file delegate:self]]; + } else { + [operations addObject:[[BSGEventUploadFileOperation alloc] initWithFile:file delegate:self]]; + } + } + + return operations; +} + +// MARK: - BSGEventUploadOperationDelegate + +- (void)uploadOperationDidStoreEventPayload:(BSGEventUploadOperation *)uploadOperation { + [self deleteExcessFiles:[self sortedEventFiles]]; +} + +@end diff --git a/Bugsnag/Delivery/BugsnagErrorReportApiClient.h b/Bugsnag/Delivery/BugsnagErrorReportApiClient.h deleted file mode 100644 index 5d3b62cb4..000000000 --- a/Bugsnag/Delivery/BugsnagErrorReportApiClient.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// BugsnagErrorReportApiClient.h -// Pods -// -// Created by Jamie Lynch on 11/08/2017. -// -// - -#import - -#import "BSGOnErrorSentBlock.h" -#import "BugsnagEvent.h" -#import "BugsnagConfiguration.h" -#import "BugsnagApiClient.h" - -@interface BugsnagErrorReportApiClient : BugsnagApiClient - -@end diff --git a/Bugsnag/Delivery/BugsnagErrorReportApiClient.m b/Bugsnag/Delivery/BugsnagErrorReportApiClient.m deleted file mode 100644 index 859e6b00a..000000000 --- a/Bugsnag/Delivery/BugsnagErrorReportApiClient.m +++ /dev/null @@ -1,41 +0,0 @@ -// -// BugsnagErrorReportApiClient.m -// Pods -// -// Created by Jamie Lynch on 11/08/2017. -// -// - -#import "BugsnagErrorReportApiClient.h" - -#import "BSG_KSCrash.h" -#import "Bugsnag.h" -#import "BugsnagClient.h" -#import "BugsnagErrorReportSink.h" -#import "BugsnagKeys.h" -#import "BugsnagLogger.h" - - -@interface BSGDeliveryOperation : NSOperation -@end - -@implementation BugsnagErrorReportApiClient - -- (NSOperation *)deliveryOperation { - return [BSGDeliveryOperation new]; -} - -@end - -@implementation BSGDeliveryOperation - -- (void)main { - @autoreleasepool { - @try { - [[BSG_KSCrash sharedInstance] sendAllReports]; - } @catch (NSException *e) { - bsg_log_err(@"Could not send error report: %@", e); - } - } -} -@end diff --git a/Bugsnag/Helpers/BSGAppHangDetector.h b/Bugsnag/Helpers/BSGAppHangDetector.h new file mode 100644 index 000000000..39eaacf3b --- /dev/null +++ b/Bugsnag/Helpers/BSGAppHangDetector.h @@ -0,0 +1,37 @@ +// +// BSGAppHangDetector.h +// Bugsnag +// +// Created by Nick Dowell on 01/03/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import + +@class BugsnagConfiguration; +@class BugsnagEvent; +@class BugsnagThread; + +NS_ASSUME_NONNULL_BEGIN + +@protocol BSGAppHangDetectorDelegate; + + +@interface BSGAppHangDetector : NSObject + +- (void)startWithDelegate:(id)delegate; + +@end + + +@protocol BSGAppHangDetectorDelegate + +@property (readonly) BugsnagConfiguration *configuration; + +- (void)appHangDetectedWithThreads:(NSArray *)threads; + +- (void)appHangEnded; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Helpers/BSGAppHangDetector.m b/Bugsnag/Helpers/BSGAppHangDetector.m new file mode 100644 index 000000000..b12eb0fe1 --- /dev/null +++ b/Bugsnag/Helpers/BSGAppHangDetector.m @@ -0,0 +1,125 @@ +// +// BSGAppHangDetector.m +// Bugsnag +// +// Created by Nick Dowell on 01/03/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "BSGAppHangDetector.h" + +#import +#import + +#import "BugsnagLogger.h" +#import "BugsnagThread+Recording.h" +#import "BugsnagThread+Private.h" + + +@interface BSGAppHangDetector () + +@property (nonatomic) CFRunLoopObserverRef observer; + +@end + + +@implementation BSGAppHangDetector + +- (void)dealloc { + if (_observer) { + CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes); + } +} + +- (void)startWithDelegate:(id)delegate { + if (self.observer) { + bsg_log_err(@"Attempted to call %s more than once", __PRETTY_FUNCTION__); + return; + } + + BugsnagConfiguration *configuration = delegate.configuration; + if (!configuration.enabledErrorTypes.appHangs) { + return; + } + + if (NSProcessInfo.processInfo.environment[@"XCTestConfigurationFilePath"]) { + // Disable functionality during unit testing to avoid crashes that can occur due to there + // being many leaked BugsnagClient instances and BSGAppHangDetectors running while global + // shared data structures are being reinitialized. + return; + } + + const BOOL fatalOnly = configuration.appHangThresholdMillis == BugsnagAppHangThresholdFatalOnly; + const BOOL recordAllThreads = configuration.sendThreads == BSGThreadSendPolicyAlways; + const NSTimeInterval threshold = fatalOnly ? 2 : configuration.appHangThresholdMillis / 1000.0; + + bsg_log_debug(@"Starting App Hang detector with threshold = %g seconds", threshold); + + dispatch_queue_t backgroundQueue; + __block dispatch_semaphore_t semaphore; + __weak typeof(delegate) weakDelegate = delegate; + + backgroundQueue = dispatch_queue_create("com.bugsnag.app-hang-detector", DISPATCH_QUEUE_SERIAL); + + void (^ observerBlock)(CFRunLoopObserverRef, CFRunLoopActivity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + // "Inside the event processing loop after the run loop wakes up, but before processing the event that woke it up" + if (activity == kCFRunLoopAfterWaiting) { + if (!semaphore) { + semaphore = dispatch_semaphore_create(0); + } + dispatch_time_t now = dispatch_time(DISPATCH_TIME_NOW, 0); + // Using dispatch_after prevents our queue showing up in Instruments' Time Profiler until there is a hang. + // Schedule block slightly ahead of time to work around dispatch_after's leeway. + dispatch_time_t after = dispatch_time(now, (int64_t)((threshold * 0.95) * NSEC_PER_SEC)); + dispatch_time_t timeout = dispatch_time(now, (int64_t)(threshold * NSEC_PER_SEC)); + dispatch_after(after, backgroundQueue, ^{ + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + bsg_log_info("App hang detected"); + + NSArray *threads = nil; + if (recordAllThreads) { + threads = [BugsnagThread allThreads:YES callStackReturnAddresses:NSThread.callStackReturnAddresses]; + // By default the calling thread is marked as "Error reported from this thread", which is not correct case for app hangs. + [threads enumerateObjectsUsingBlock:^(BugsnagThread * _Nonnull thread, NSUInteger idx, BOOL * _Nonnull stop) { + thread.errorReportingThread = idx == 0; + }]; + } else { + threads = [NSArray arrayWithObjects:[BugsnagThread mainThread], nil]; //!OCLint + } + + [weakDelegate appHangDetectedWithThreads:threads]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + bsg_log_info("App hang has ended"); + + [weakDelegate appHangEnded]; + } + }); + } + + // "Inside the event processing loop before the run loop sleeps, waiting for a source or timer to fire" + if (activity == kCFRunLoopBeforeWaiting) { + if (semaphore) { + dispatch_semaphore_signal(semaphore); + } + } + }; + + // A high `order` is required to ensure our observer runs after others that may introduce an app hang. + // Once such culprit is -[UITableView tableView:didSelectRowAtIndexPath:] which is run in a + // _afterCACommitHandler, which is invoked via a CFRunLoopObserver. + CFIndex order = INT_MAX; + self.observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopAfterWaiting | kCFRunLoopBeforeWaiting, true, order, observerBlock); + + CFRunLoopMode runLoopMode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()); + // The run loop mode will be NULL if called before the run loop has started; e.g. in a +load method. + if (runLoopMode) { + // If we are already in the run loop (e.g. in app delegate) start monitoring immediately so that app hangs during app launch are detected. + observerBlock(self.observer, kCFRunLoopAfterWaiting); + CFRelease(runLoopMode); + } + + CFRunLoopAddObserver(CFRunLoopGetMain(), self.observer, kCFRunLoopCommonModes); +} + +@end diff --git a/Bugsnag/Helpers/BugsnagCollections.h b/Bugsnag/Helpers/BugsnagCollections.h index da6165093..374c4a115 100644 --- a/Bugsnag/Helpers/BugsnagCollections.h +++ b/Bugsnag/Helpers/BugsnagCollections.h @@ -21,6 +21,11 @@ #import +NS_ASSUME_NONNULL_BEGIN + +/// Returns an array containing the results of mapping the given block over the array's elements +NSArray * BSGArrayMap(NSArray *array, id (^ transform)(id)); + /// Returns a new array containing the elements starting at position `index`, or /// an empty array if `index` is beyond the array's range range of elements. NSArray * BSGArraySubarrayFromIndex(NSArray *array, NSUInteger index); @@ -37,3 +42,5 @@ NSDictionary *BSGDictMerge(NSDictionary *source, NSDictionary *destination); /// Any dictionary keys that are not strings will be ignored. /// Any values that are not valid JSON will be replaced by a string description. NSDictionary * BSGJSONDictionary(NSDictionary *dictionary); + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Helpers/BugsnagCollections.m b/Bugsnag/Helpers/BugsnagCollections.m index 2f8bf668a..7e08d0b11 100644 --- a/Bugsnag/Helpers/BugsnagCollections.m +++ b/Bugsnag/Helpers/BugsnagCollections.m @@ -23,6 +23,17 @@ #import "BSGJSONSerialization.h" +NSArray * BSGArrayMap(NSArray *array, id (^ transform)(id)) { + NSMutableArray *mappedArray = [NSMutableArray array]; + for (id object in array) { + id mapped = transform(object); + if (mapped) { + [mappedArray addObject:mapped]; + } + } + return mappedArray; +} + NSArray * BSGArraySubarrayFromIndex(NSArray *array, NSUInteger index) { if (index >= array.count) { return @[]; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h index cdc8a7d58..224673a55 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h @@ -26,11 +26,8 @@ #import -#import "BugsnagErrorReportSink.h" -#import "BSGOnErrorSentBlock.h" #import "BSG_KSCrashReportWriter.h" #import "BSG_KSCrashType.h" -#import "BugsnagConfiguration.h" /** * Reports any crashes that occur in the application. @@ -72,22 +69,7 @@ * * @return YES if the reporter successfully installed. */ -- (BOOL)install; - -/** Send any outstanding crash reports to the current sink. - * It will only attempt to send the most recent 5 reports. All others will be - * deleted. Once the reports are successfully sent to the server, they may be - * deleted locally. - * - * Note: property "sink" MUST be set or else this method will call the block - * with an error. - */ -- (void)sendAllReports; - -/** - * Send the most recent crash report to the current sink. - */ -- (void)sendLatestReport:(dispatch_block_t)completionHander; +- (BOOL)install:(NSString *)directory; /** Report a custom, user defined exception. * This can be useful when dealing with scripting languages. diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m index 4b5502ea4..ff0ccae75 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m @@ -40,8 +40,8 @@ #import "BSGSerialization.h" #import "Bugsnag.h" #import "BugsnagCollections.h" +#import "BSG_KSCrashIdentifier.h" #import "BSG_KSCrashReportFields.h" -#import "BSGFileLocations.h" #if BSG_HAS_UIKIT #import "BSGUIKit.h" @@ -73,15 +73,9 @@ @interface BSG_KSCrash () @property(nonatomic, readwrite, retain) NSString *bundleName; @property(nonatomic, readwrite, retain) NSString *nextCrashID; -@property(nonatomic, readonly, retain) NSString *crashReportPath; -@property(nonatomic, readonly, retain) NSString *recrashReportPath; -@property(nonatomic, readonly, retain) NSString *stateFilePath; // Mirrored from BSG_KSCrashAdvanced.h to provide ivars -@property(nonatomic, readwrite, retain) BugsnagErrorReportSink *sink; @property(nonatomic, readwrite, retain) NSString *logFilePath; -@property(nonatomic, readwrite, retain) - BSG_KSCrashReportStore *crashReportStore; @property(nonatomic, readwrite, assign) BSGReportCallback onCrash; @property(nonatomic, readwrite, assign) bool printTraceToStdout; @property(nonatomic, readwrite, assign) int maxStoredReports; @@ -94,12 +88,10 @@ @implementation BSG_KSCrash #pragma mark - Properties - // ============================================================================ -@synthesize sink = _sink; @synthesize userInfo = _userInfo; @synthesize handlingCrashTypes = _handlingCrashTypes; @synthesize printTraceToStdout = _printTraceToStdout; @synthesize onCrash = _onCrash; -@synthesize crashReportStore = _crashReportStore; @synthesize bundleName = _bundleName; @synthesize logFilePath = _logFilePath; @synthesize nextCrashID = _nextCrashID; @@ -127,7 +119,6 @@ - (instancetype)init { if ((self = [super init])) { self.bundleName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; self.nextCrashID = [NSUUID UUID].UUIDString; - self.crashReportStore = [BSG_KSCrashReportStore storeWithPath:[BSGFileLocations current].kscrashReports]; self.introspectMemory = YES; self.maxStoredReports = 5; @@ -195,26 +186,20 @@ - (void)setWriteBinaryImagesForUserReported: writeBinaryImagesForUserReported); } -- (NSString *)crashReportPath { - return [self.crashReportStore pathToFileWithId:self.nextCrashID]; -} - -- (NSString *)recrashReportPath { - return [self.crashReportStore pathToRecrashReportWithID:self.nextCrashID]; -} - -- (NSString *)stateFilePath { - NSString *stateFilename = [NSString - stringWithFormat:@"%@" BSG_kCrashStateFilenameSuffix, self.bundleName]; - return [self.crashReportStore.path - stringByAppendingPathComponent:stateFilename]; -} - -- (BOOL)install { - +- (BOOL)install:(NSString *)directory { + bsg_kscrash_generate_report_initialize(directory.fileSystemRepresentation, self.bundleName.UTF8String); + char *crashReportPath = (char *)bsg_kscrash_generate_report_path(self.nextCrashID.UTF8String, false); + char *recrashReportPath = (char *)bsg_kscrash_generate_report_path(self.nextCrashID.UTF8String, true); + NSString *stateFilePath = [directory stringByAppendingPathComponent: + [self.bundleName stringByAppendingString:@BSG_kCrashStateFilenameSuffix]]; + self.handlingCrashTypes = bsg_kscrash_install( - [self.crashReportPath UTF8String], [self.recrashReportPath UTF8String], - [self.stateFilePath UTF8String], [self.nextCrashID UTF8String]); + crashReportPath, recrashReportPath, + [stateFilePath UTF8String], [self.nextCrashID UTF8String]); + + free(crashReportPath); + free(recrashReportPath); + if (self.handlingCrashTypes == 0) { return false; } @@ -260,60 +245,6 @@ - (BOOL)install { return true; } -- (void)sendAllReports { - [self.crashReportStore pruneFilesLeaving:self.maxStoredReports]; - - NSDictionary *reports = [self allReportsByFilename]; - if (!reports.count) { - return; - } - - [self sendReports:reports completionHander:nil]; -} - -- (void)sendLatestReport:(dispatch_block_t)completionHander { - [self.crashReportStore pruneFilesLeaving:self.maxStoredReports]; - - NSString *fileId = [self.crashReportStore fileIds].lastObject; - if (!fileId) { - if (completionHander) { - completionHander(); - } - return; - } - - NSDictionary *contents = [self.crashReportStore fileWithId:fileId]; - if (!contents) { - if (completionHander) { - completionHander(); - } - return; - } - - [self sendReports:@{fileId: contents} completionHander:completionHander]; -} - -- (void)sendReports:(NSDictionary *)reports - completionHander:(dispatch_block_t)completionHander { - BSG_KSLOG_INFO(@"Sending %lu crash reports", (unsigned long)reports.count); - - [self sendReports:reports - withBlock:^(NSString *filename, BOOL completed, - NSError *error) { - BSG_KSLOG_DEBUG(@"Sending finished with completion: %d", completed); - if (error != nil) { - BSG_KSLOG_ERROR(@"Failed to send reports: %@", error); - } - if (completed && filename != nil) { - BSG_KSLOG_DEBUG(@"Deleting KSCrashReport %@", filename); - [self.crashReportStore deleteFileWithId:filename]; - } - if (completionHander) { - completionHander(); - } - }]; -} - - (NSDictionary *)captureAppStats { BSG_KSCrash_State state = crashContext()->state; bsg_kscrashstate_updateDurationStats(&state); @@ -366,39 +297,6 @@ -(TYPE)NAME { \ BSG_SYNTHESIZE_CRASH_STATE_PROPERTY(int, sessionsSinceLaunch) BSG_SYNTHESIZE_CRASH_STATE_PROPERTY(BOOL, crashedLastLaunch) -- (NSUInteger)reportCount { - return [self.crashReportStore fileCount]; -} - -- (NSString *)crashReportsPath { - return self.crashReportStore.path; -} - -- (void)sendReports:(NSDictionary *)reports - withBlock:(BSGOnErrorSentBlock)block { - if ([reports count] == 0) { - if (block) { - block(nil, YES, nil); - } - return; - } - - if (self.sink == nil) { - if (block) { - block(nil, NO, [NSError bsg_errorWithDomain:[[self class] description] - code:0 - description:@"No sink set. Crash reports not sent."]); - } - return; - } - [self.sink sendStoredReports:reports - withBlock:block]; -} - -- (NSDictionary *)allReportsByFilename { - return [self.crashReportStore allFilesByName]; -} - - (BOOL)redirectConsoleLogsToFile:(NSString *)fullPath overwrite:(BOOL)overwrite { if (bsg_kslog_setLogFilename([fullPath UTF8String], overwrite)) { @@ -408,18 +306,6 @@ - (BOOL)redirectConsoleLogsToFile:(NSString *)fullPath return NO; } -- (BOOL)redirectConsoleLogsToDefaultFile { - NSString *logFilename = [NSString - stringWithFormat:@"%@" BSG_kCrashLogFilenameSuffix, self.bundleName]; - NSString *logFilePath = - [self.crashReportStore.path stringByAppendingPathComponent:logFilename]; - if (![self redirectConsoleLogsToFile:logFilePath overwrite:YES]) { - BSG_KSLOG_ERROR(@"Could not redirect logs to %@", logFilePath); - return NO; - } - return YES; -} - // ============================================================================ #pragma mark - Utility - // ============================================================================ diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h index 0589640bf..bfe2fef96 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashAdvanced.h @@ -25,9 +25,6 @@ // #import "BSG_KSCrash.h" -#import "BSG_KSCrashReportStore.h" - -@class BugsnagErrorReportSink; /** * Advanced interface to the BSG_KSCrash system. @@ -67,36 +64,8 @@ * (default 5) */ @property(nonatomic, readwrite, assign) int maxStoredReports; -/** The total number of unsent reports. Note: This is an expensive operation. - */ -- (NSUInteger)reportCount; - -/** Get all reports, with data types corrected, as dictionaries. - */ -- (NSArray *)allReports; - -/** Get all reports as dictionaries, indexed by file name. - */ -- (NSDictionary *)allReportsByFilename; - #pragma mark - Configuration - -/** Init BSG_KSCrash instance with custom report files directory path. */ -- (instancetype)initWithReportFilesDirectory:(NSString *)reportFilesDirectory; - -/** Store containing all crash reports. */ -@property(nonatomic, readwrite, retain) - BSG_KSCrashReportStore *crashReportStore; - -/** The report sink where reports get sent. - * This MUST be set or else the reporter will not send reports (although it will - * still record them). - * - * Note: If you use an installation, it will automatically set this property. - * Do not modify it in such a case. - */ -@property(nonatomic, readwrite, retain) BugsnagErrorReportSink *sink; - /** C Function to call during a crash report to give the callee an opportunity * to add to the report. NULL = ignore. * @@ -124,14 +93,6 @@ */ @property(nonatomic, readwrite, assign) bool printTraceToStdout; -/** Sets logFilePath to the default log file location - * (Library/Caches/KSCrashReports/-CrashLog.txt). - * If the file exists, it will be overwritten. - * - * @return true if the operation was successful. - */ -- (BOOL)redirectConsoleLogsToDefaultFile; - /** Redirect the log of BSG_KSCrash's activities from the console to the * specified log file. * @@ -143,14 +104,4 @@ - (BOOL)redirectConsoleLogsToFile:(NSString *)fullPath overwrite:(BOOL)overwrite; -#pragma mark - Operations - - -/** Send the specified reports to the current sink. - * - * @param reports The reports to send. - * @param block Called when sending is complete (nil = ignore). - */ -- (void)sendReports:(NSDictionary *)reports - withBlock:(BSGOnErrorSentBlock)block; - @end diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.h index efb613787..5db0c7606 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.h @@ -6,6 +6,8 @@ extern "C" { #endif +void bsg_kscrash_generate_report_initialize(const char *directory, const char *bundleName); + /** * Generates a new UUID. Not async signal safe. Caller responsible for * freeing allocated string. diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.m index 5b8805138..0de0172fd 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashIdentifier.m @@ -1,7 +1,19 @@ #import "BSG_KSCrashIdentifier.h" + #import "BSG_KSCrashAdvanced.h" + #import -#import + +#include +#include + +static char *report_directory; +static char *bundle_name; + +void bsg_kscrash_generate_report_initialize(const char *directory, const char *bundleName) { + report_directory = directory ? strdup(directory) : NULL; + bundle_name = bundleName ? strdup(bundleName) : NULL; +} const char *bsg_kscrash_generate_report_identifier(void) { return strdup([[[NSUUID UUID] UUIDString] UTF8String]); @@ -12,12 +24,8 @@ if (identifier == NULL) { return NULL; } - BSG_KSCrashReportStore *store = [[BSG_KSCrash sharedInstance] crashReportStore]; - NSString *reportID = [NSString stringWithUTF8String:identifier]; - - if (is_recrash_report) { - return strdup([[store pathToRecrashReportWithID:reportID] UTF8String]); - } else { - return strdup([[store pathToFileWithId:reportID] UTF8String]); - } + char *type = is_recrash_report ? "RecrashReport" : "CrashReport"; + char *path = NULL; + asprintf(&path, "%s/%s-%s-%s.json", report_directory, bundle_name, type, identifier); + return path; } diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.h deleted file mode 100644 index 4b7dc3969..000000000 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// BSG_KSCrashReportStore.h -// -// Created by Karl Stenerud on 2012-02-05. -// -// Copyright (c) 2012 Karl Stenerud. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall remain in place -// in this source code. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import - -#import "BugsnagFileStore.h" - -/** - * Manages a store of crash reports. - */ -@interface BSG_KSCrashReportStore : BugsnagFileStore - -+ (BSG_KSCrashReportStore *)storeWithPath:(NSString *)path; - - -/** Full path to the recrash report with the specified ID. - * - * @param reportID The report ID - * - * @return The full path. - */ -- (NSString *)pathToRecrashReportWithID:(NSString *)reportID; - -@end diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.m deleted file mode 100644 index 56b47fa72..000000000 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReportStore.m +++ /dev/null @@ -1,177 +0,0 @@ -// -// BSG_KSCrashReportStore.m -// -// Created by Karl Stenerud on 2012-02-05. -// -// Copyright (c) 2012 Karl Stenerud. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall remain in place -// in this source code. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#import "BSG_KSCrashReportStore.h" - -#import "BSG_KSCrashDoctor.h" -#import "BSG_KSCrashReportFields.h" -#import "BSG_RFC3339DateTool.h" -#import "BSG_KSLogger.h" -#import "BugsnagCollections.h" - -static NSString *const kCrashReportSuffix = @"-CrashReport-"; -#define BSG_kRecrashReportSuffix @"-RecrashReport-" - -@implementation BSG_KSCrashReportStore - -#pragma mark Properties - -+ (BSG_KSCrashReportStore *)storeWithPath:(NSString *)path { - return [[self alloc] initWithPath:path - filenameSuffix:kCrashReportSuffix]; -} - -- (NSString *)recrashReportFilenameWithID:(NSString *)reportID { - return [NSString stringWithFormat:@"%@" - BSG_kRecrashReportSuffix - "%@.json", - self.bundleName, reportID]; -} - -- (NSString *)pathToRecrashReportWithID:(NSString *)reportID { - NSString *filename = [self recrashReportFilenameWithID:reportID]; - return [self.path stringByAppendingPathComponent:filename]; -} - -- (NSString *)getReportType:(NSDictionary *)report { - NSDictionary *reportSection = report[@BSG_KSCrashField_Report]; - if (reportSection) { - return reportSection[@BSG_KSCrashField_Type]; - } - BSG_KSLOG_ERROR(@"Expected a report section in the report."); - return nil; -} - - -- (void)deleteFileWithId:(NSString *)fileId { - [super deleteFileWithId:fileId]; - NSError *error = nil; - - // Don't care if this succeeds or not since it may not exist. - [[NSFileManager defaultManager] - removeItemAtPath:[self pathToRecrashReportWithID:fileId] - error:&error]; -} - - -- (NSDictionary *)fileWithId:(NSString *)fileId { - NSDictionary *dict = [super fileWithId:fileId]; - - if (dict != nil) { - return dict; - } else { - NSError *error = nil; - NSMutableDictionary *fileContents = [NSMutableDictionary new]; - NSMutableDictionary *recrashReport = - [self readFile:[self pathToRecrashReportWithID:fileId] error:&error]; - fileContents[@BSG_KSCrashField_RecrashReport] = recrashReport; - return fileContents; - } -} - - -- (NSMutableDictionary *)fixupCrashReport:(NSDictionary *)report { - if (![report isKindOfClass:[NSDictionary class]]) { - BSG_KSLOG_ERROR(@"Report should be a dictionary, not %@", - [report class]); - return nil; - } - - NSMutableDictionary *mutableReport = [report mutableCopy]; - NSMutableDictionary *mutableInfo = - [report[@BSG_KSCrashField_Report] mutableCopy]; - mutableReport[@BSG_KSCrashField_Report] = mutableInfo; - - // Timestamp gets stored as a unix timestamp. Convert it to rfc3339. - [self convertTimestamp:@BSG_KSCrashField_Timestamp inReport:mutableInfo]; - - [self mergeDictWithKey:@BSG_KSCrashField_SystemAtCrash - intoDictWithKey:@BSG_KSCrashField_System - inReport:mutableReport]; - - [self mergeDictWithKey:@BSG_KSCrashField_UserAtCrash - intoDictWithKey:@BSG_KSCrashField_User - inReport:mutableReport]; - - NSMutableDictionary *crashReport = - [report[@BSG_KSCrashField_Crash] mutableCopy]; - mutableReport[@BSG_KSCrashField_Crash] = crashReport; - BSG_KSCrashDoctor *doctor = [BSG_KSCrashDoctor doctor]; - crashReport[@BSG_KSCrashField_Diagnosis] = [doctor diagnoseCrash:report]; - - return mutableReport; -} - -- (void)mergeDictWithKey:(NSString *)srcKey - intoDictWithKey:(NSString *)dstKey - inReport:(NSMutableDictionary *)report { - NSDictionary *srcDict = report[srcKey]; - if (srcDict == nil) { - // It's OK if the source dict didn't exist. - return; - } - - NSDictionary *dstDict = report[dstKey]; - if (dstDict == nil) { - dstDict = @{}; - } - if (![dstDict isKindOfClass:[NSDictionary class]]) { - BSG_KSLOG_ERROR(@"'%@' should be a dictionary, not %@", dstKey, - [dstDict class]); - return; - } - - report[dstKey] = BSGDictMerge(srcDict, dstDict); - [report removeObjectForKey:srcKey]; -} - -- (void)convertTimestamp:(NSString *)key - inReport:(NSMutableDictionary *)report { - NSNumber *timestamp = report[key]; - if (timestamp == nil) { - BSG_KSLOG_ERROR(@"entry '%@' not found", key); - return; - } - [report - setValue:[BSG_RFC3339DateTool - stringFromUNIXTimestamp:[timestamp unsignedLongLongValue]] - forKey:key]; -} - -- (NSMutableDictionary *)readFile:(NSString *)path - error:(NSError *__autoreleasing *)error { - NSMutableDictionary *report = [super readFile:path error:error]; - - NSString *reportType = [self getReportType:report]; - if ([reportType isEqualToString:@BSG_KSCrashReportType_Standard] || - [reportType isEqualToString:@BSG_KSCrashReportType_Minimal]) { - report = [self fixupCrashReport:report]; - } - - return report; -} - -@end diff --git a/Bugsnag/Payload/BugsnagEvent+Private.h b/Bugsnag/Payload/BugsnagEvent+Private.h index 6440c9b40..d13229e88 100644 --- a/Bugsnag/Payload/BugsnagEvent+Private.h +++ b/Bugsnag/Payload/BugsnagEvent+Private.h @@ -30,15 +30,13 @@ NS_ASSUME_NONNULL_BEGIN @property (readwrite, copy, nullable) NSDictionary *error; /// The event state (whether the error is handled/unhandled.) -@property (readonly) BugsnagHandledState *handledState; +@property (readwrite, nonatomic) BugsnagHandledState *handledState; @property (strong, nonatomic) BugsnagMetadata *metadata; /// Property overrides. @property (readonly, nonatomic) NSDictionary *overrides; -@property NSSet *redactedKeys; - /// The release stage of the application @property (readwrite, copy, nullable) NSString *releaseStage; @@ -59,6 +57,8 @@ NS_ASSUME_NONNULL_BEGIN threads:(NSArray *)threads session:(nullable BugsnagSession *)session; +- (instancetype)initWithJson:(NSDictionary *)json; + - (instancetype)initWithKSReport:(NSDictionary *)KSReport; - (instancetype)initWithUserData:(NSDictionary *)event; @@ -70,7 +70,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)setOverrideProperty:(NSString *)key value:(id)value; -- (NSDictionary *)toJson; +- (NSDictionary *)toJsonWithRedactedKeys:(nullable NSSet *)redactedKeys; - (void)notifyUnhandledOverridden; diff --git a/Bugsnag/Payload/BugsnagEvent.m b/Bugsnag/Payload/BugsnagEvent.m index 9d4a709d6..b4756a80a 100644 --- a/Bugsnag/Payload/BugsnagEvent.m +++ b/Bugsnag/Payload/BugsnagEvent.m @@ -182,6 +182,22 @@ - (instancetype)initWithApp:(BugsnagAppWithState *)app return self; } +- (instancetype)initWithJson:(NSDictionary *)json { + if (self = [super init]) { + _app = [BugsnagAppWithState appFromJson:json[BSGKeyApp]]; + _breadcrumbs = BSGArrayMap(json[BSGKeyBreadcrumbs], ^id (NSDictionary *json) { return [BugsnagBreadcrumb breadcrumbFromDict:json]; }); + _device = [BugsnagDeviceWithState deviceFromJson:json[BSGKeyDevice]]; + _error = json[BSGKeyMetadata][BSGKeyError]; + _errors = BSGArrayMap(json[BSGKeyExceptions], ^id (NSDictionary *json) { return [BugsnagError errorFromJson:json]; }); + _handledState = [BugsnagHandledState handledStateFromJson:json]; + _metadata = [[BugsnagMetadata alloc] initWithDictionary:json[BSGKeyMetadata]]; + _session = [BugsnagSession fromJson:json[BSGKeySession]]; + _threads = BSGArrayMap(json[BSGKeyThreads], ^id (NSDictionary *json) { return [BugsnagThread threadFromJson:json]; }); + _user = [[BugsnagUser alloc] initWithDictionary:json[BSGKeyUser]]; + } + return self; +} + /** * Creates a BugsnagEvent from a JSON crash report generated by KSCrash. A KSCrash * report can come in 3 variants, which needs to be deserialized separately: @@ -612,7 +628,7 @@ - (void)notifyUnhandledOverridden { self.handledState.unhandledOverridden = YES; } -- (NSDictionary *)toJson { +- (NSDictionary *)toJsonWithRedactedKeys:(NSSet *)redactedKeys { NSMutableDictionary *event = [NSMutableDictionary dictionary]; event[BSGKeyExceptions] = ({ @@ -635,7 +651,7 @@ - (NSDictionary *)toJson { // add metadata NSMutableDictionary *metadata = [[[self metadata] toDictionary] mutableCopy]; - event[BSGKeyMetadata] = [self sanitiseMetadata:metadata]; + event[BSGKeyMetadata] = [self sanitiseMetadata:metadata redactedKeys:redactedKeys]; event[BSGKeyDevice] = [self.device toDictionary]; event[BSGKeyApp] = [self.app toDict]; @@ -675,28 +691,28 @@ - (NSDictionary *)toJson { return event; } -- (NSMutableDictionary *)sanitiseMetadata:(NSMutableDictionary *)metadata { +- (NSMutableDictionary *)sanitiseMetadata:(NSMutableDictionary *)metadata redactedKeys:(NSSet *)redactedKeys { for (NSString *sectionKey in [metadata allKeys]) { metadata[sectionKey] = [metadata[sectionKey] mutableCopy]; NSMutableDictionary *section = metadata[sectionKey]; if (section != nil) { // redact sensitive metadata values for (NSString *objKey in [section allKeys]) { - section[objKey] = [self sanitiseMetadataValue:section[objKey] key:objKey]; + section[objKey] = [self sanitiseMetadataValue:section[objKey] key:objKey redactedKeys:redactedKeys]; } } } return metadata; } -- (id)sanitiseMetadataValue:(id)value key:(NSString *)key { - if ([self isRedactedKey:key]) { +- (id)sanitiseMetadataValue:(id)value key:(NSString *)key redactedKeys:(NSSet *)redactedKeys { + if ([self isRedactedKey:key redactedKeys:redactedKeys]) { return BSGKeyRedaction; } else if ([value isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *nestedDict = [(NSDictionary *)value mutableCopy]; for (NSString *nestedKey in [nestedDict allKeys]) { - nestedDict[nestedKey] = [self sanitiseMetadataValue:nestedDict[nestedKey] key:nestedKey]; + nestedDict[nestedKey] = [self sanitiseMetadataValue:nestedDict[nestedKey] key:nestedKey redactedKeys:redactedKeys]; } return nestedDict; } else { @@ -704,8 +720,8 @@ - (id)sanitiseMetadataValue:(id)value key:(NSString *)key { } } -- (BOOL)isRedactedKey:(NSString *)key { - for (id obj in self.redactedKeys) { +- (BOOL)isRedactedKey:(NSString *)key redactedKeys:(NSSet *)redactedKeys { + for (id obj in redactedKeys) { if ([obj isKindOfClass:[NSString class]]) { if ([[key lowercaseString] isEqualToString:[obj lowercaseString]]) { return true; @@ -787,7 +803,7 @@ - (void)clearMetadataFromSection:(NSString *_Nonnull)sectionName // The error in self.errors is not always the error that will be sent; this is the case when used in React Native. // Using [self toJson] to ensure this uses the same logic of reading from self.customException instead. - NSDictionary *json = [self toJson]; + NSDictionary *json = [self toJsonWithRedactedKeys:nil]; NSArray *exceptions = json[BSGKeyExceptions]; for (NSDictionary *exception in exceptions) { BugsnagError *error = [BugsnagError errorFromJson:exception]; diff --git a/Bugsnag/Payload/BugsnagHandledState.h b/Bugsnag/Payload/BugsnagHandledState.h index 218007b1f..94b6a025c 100644 --- a/Bugsnag/Payload/BugsnagHandledState.h +++ b/Bugsnag/Payload/BugsnagHandledState.h @@ -19,6 +19,7 @@ typedef NS_ENUM(NSUInteger, SeverityReasonType) { PromiseRejection, LogMessage, LikelyOutOfMemory, + AppHang, }; /** diff --git a/Bugsnag/Payload/BugsnagHandledState.m b/Bugsnag/Payload/BugsnagHandledState.m index 357832070..aaa2b0cc4 100644 --- a/Bugsnag/Payload/BugsnagHandledState.m +++ b/Bugsnag/Payload/BugsnagHandledState.m @@ -36,6 +36,7 @@ BSGSeverity BSGParseSeverity(NSString *severity) { static NSString *const kAttrValue = @"attrValue"; static NSString *const kAttrKey = @"attrKey"; +static NSString *const kAppHang = @"appHang"; static NSString *const kUnhandledException = @"unhandledException"; static NSString *const kSignal = @"signal"; static NSString *const kPromiseRejection = @"unhandledPromiseRejection"; @@ -106,6 +107,10 @@ + (instancetype)handledStateWithSeverityReason: severity = BSGSeverityError; unhandled = YES; break; + case AppHang: + severity = BSGSeverityError; + unhandled = NO; + break; } return [[BugsnagHandledState alloc] initWithSeverityReason:severityReason @@ -176,6 +181,8 @@ + (NSString *)stringFromSeverityReason:(SeverityReasonType)severityReason { return kUnhandledException; case LikelyOutOfMemory: return kLikelyOutOfMemory; + case AppHang: + return kAppHang; } } @@ -198,6 +205,8 @@ + (SeverityReasonType)severityReasonFromString:(NSString *)string { return PromiseRejection; } else if ([kLikelyOutOfMemory isEqualToString:string]) { return LikelyOutOfMemory; + } else if ([kAppHang isEqualToString:string]) { + return AppHang; } else { return UnhandledException; } diff --git a/Bugsnag/Payload/BugsnagThread+Private.h b/Bugsnag/Payload/BugsnagThread+Private.h index b262681cf..42b9481a3 100644 --- a/Bugsnag/Payload/BugsnagThread+Private.h +++ b/Bugsnag/Payload/BugsnagThread+Private.h @@ -24,6 +24,8 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) NSString *crashInfoMessage; +@property (readwrite, nonatomic) BOOL errorReportingThread; + + (NSDictionary *)enhanceThreadInfo:(NSDictionary *)thread depth:(NSUInteger)depth errorType:(nullable NSString *)errorType; diff --git a/Bugsnag/Storage/BSGFileLocations.h b/Bugsnag/Storage/BSGFileLocations.h index 648531369..bfe2a9f22 100644 --- a/Bugsnag/Storage/BSGFileLocations.h +++ b/Bugsnag/Storage/BSGFileLocations.h @@ -14,9 +14,15 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, nonatomic) NSString *kvStore; @property (readonly, nonatomic) NSString *breadcrumbs; +@property (readonly, nonatomic) NSString *events; @property (readonly, nonatomic) NSString *kscrashReports; @property (readonly, nonatomic) NSString *sessions; +/** + * File containing details of the current app hang (if the app is hung) + */ +@property (readonly, nonatomic) NSString *appHangEvent; + /** * File whose presence indicates that the libary at least attempted to handle the last * crash (in case it crashed before writing enough information). diff --git a/Bugsnag/Storage/BSGFileLocations.m b/Bugsnag/Storage/BSGFileLocations.m index 3f29de3c3..22da67349 100644 --- a/Bugsnag/Storage/BSGFileLocations.m +++ b/Bugsnag/Storage/BSGFileLocations.m @@ -81,10 +81,12 @@ + (instancetype) v1 { - (instancetype)initWithVersion1 { if (self = [super init]) { NSString *root = rootDirectory(@"v1"); + _events = getAndCreateSubdir(root, @"events"); _sessions = getAndCreateSubdir(root, @"sessions"); _breadcrumbs = getAndCreateSubdir(root, @"breadcrumbs"); _kscrashReports = getAndCreateSubdir(root, @"KSCrashReports"); _kvStore = getAndCreateSubdir(root, @"kvstore"); + _appHangEvent = [root stringByAppendingPathComponent:@"app_hang.json"]; _flagHandledCrash = [root stringByAppendingPathComponent:@"bugsnag_handled_crash.txt"]; _configuration = [root stringByAppendingPathComponent:@"config.json"]; _metadata = [root stringByAppendingPathComponent:@"metadata.json"]; diff --git a/Bugsnag/Storage/BugsnagFileStore.m b/Bugsnag/Storage/BugsnagFileStore.m index 250586869..599194b3a 100644 --- a/Bugsnag/Storage/BugsnagFileStore.m +++ b/Bugsnag/Storage/BugsnagFileStore.m @@ -18,26 +18,26 @@ * Metadata class to hold name and creation date for a file, with * default comparison based on the creation date (ascending). */ -@interface FileStoreInfo : NSObject +@interface BSGFileStoreInfo : NSObject @property(nonatomic, readonly, retain) NSString *fileId; @property(nonatomic, readonly, retain) NSDate *creationDate; -+ (FileStoreInfo *)fileStoreInfoWithId:(NSString *)fileId ++ (BSGFileStoreInfo *)fileStoreInfoWithId:(NSString *)fileId creationDate:(NSDate *)creationDate; - (instancetype)initWithId:(NSString *)fileId creationDate:(NSDate *)creationDate; -- (NSComparisonResult)compare:(FileStoreInfo *)other; +- (NSComparisonResult)compare:(BSGFileStoreInfo *)other; @end -@implementation FileStoreInfo +@implementation BSGFileStoreInfo @synthesize fileId = _fileId; @synthesize creationDate = _creationDate; -+ (FileStoreInfo *)fileStoreInfoWithId:(NSString *)fileId ++ (BSGFileStoreInfo *)fileStoreInfoWithId:(NSString *)fileId creationDate:(NSDate *)creationDate { return [[self alloc] initWithId:fileId creationDate:creationDate]; } @@ -50,7 +50,7 @@ - (instancetype)initWithId:(NSString *)fileId creationDate:(NSDate *)creationDat return self; } -- (NSComparisonResult)compare:(FileStoreInfo *)other { +- (NSComparisonResult)compare:(BSGFileStoreInfo *)other { return [_creationDate compare:other->_creationDate]; } @@ -110,7 +110,7 @@ - (NSArray *)fileIds { bsg_log_err(@"Could not read file attributes for %@: %@", fullPath, error); } else { - FileStoreInfo *info = [FileStoreInfo fileStoreInfoWithId:fileId + BSGFileStoreInfo *info = [BSGFileStoreInfo fileStoreInfoWithId:fileId creationDate:[fileAttribs valueForKey:NSFileCreationDate]]; [files addObject:info]; } @@ -120,7 +120,7 @@ - (NSArray *)fileIds { NSMutableArray *sortedIDs = [NSMutableArray arrayWithCapacity:[files count]]; - for (FileStoreInfo *info in files) { + for (BSGFileStoreInfo *info in files) { [sortedIDs addObject:info.fileId]; } return sortedIDs; diff --git a/Bugsnag/include/Bugsnag/BugsnagConfiguration.h b/Bugsnag/include/Bugsnag/BugsnagConfiguration.h index 4967162a3..8448efe66 100644 --- a/Bugsnag/include/Bugsnag/BugsnagConfiguration.h +++ b/Bugsnag/include/Bugsnag/BugsnagConfiguration.h @@ -61,6 +61,12 @@ typedef NS_ENUM(NSInteger, BSGThreadSendPolicy) { BSGThreadSendPolicyNever = 2 }; +/** + * Setting `BugsnagConfiguration.appHangThresholdMillis` to this value disables the reporting of + * app hangs that ended before the app was terminated. + */ +extern const NSUInteger BugsnagAppHangThresholdFatalOnly; + /** * A configuration block for modifying an error report * @@ -202,6 +208,18 @@ typedef BOOL (^BugsnagOnSessionBlock)(BugsnagSession *_Nonnull session); */ @property BOOL autoDetectErrors; +/** + * The minimum number of milliseconds of main thread unresponsiveness that will trigger the + * detection and reporting of an app hang. + * + * Set to `BugsnagAppHangThresholdFatalOnly` to disable reporting of app hangs that did not + * end with the app being force quit by the user or terminated by the system watchdog. + * + * By default this is `BugsnagAppHangThresholdFatalOnly`, and can be set to a minimum of 250 + * milliseconds. + */ +@property (nonatomic) NSUInteger appHangThresholdMillis; + /** * Determines whether app sessions should be tracked automatically. By default this value is true. * If this value is updated after +[Bugsnag start] is called, only subsequent automatic sessions diff --git a/Bugsnag/include/Bugsnag/BugsnagErrorTypes.h b/Bugsnag/include/Bugsnag/BugsnagErrorTypes.h index 05035162e..8d9a767dd 100644 --- a/Bugsnag/include/Bugsnag/BugsnagErrorTypes.h +++ b/Bugsnag/include/Bugsnag/BugsnagErrorTypes.h @@ -13,6 +13,13 @@ */ @interface BugsnagErrorTypes : NSObject +/** + * Determines whether App Hang events should be reported to bugsnag. + * + * This flag is true by default. + */ +@property BOOL appHangs; + /** * Determines whether Out of Memory events should be reported to bugsnag. * diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ec8cd27..d7d7b822d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ Changelog ## TBD +### Enhancements + +* Bugsnag now detects app hangs (when the main thread is unresponsive for a period of time), alerting you of issues with your app’s responsiveness. + By default, only fatal app hangs (those that end with termination by the system watchdog or being force-quit by the user) will be reported. + This behaviour can be configured using the new `appHangThresholdMillis` configuration option. + For more information see [the documentation](https://docs.bugsnag.com/platforms/ios/reporting-app-hangs/). + [#1039](https://github.com/bugsnag/bugsnag-cocoa/pull/1039) + ### Bug fixes * Fix parsing of `callStackSymbols` where the image name contains spaces. diff --git a/Gemfile b/Gemfile index 90a3dd9bd..d32da79bf 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'xcpretty' # A reference to Maze Runner is only needed for running tests locally and if committed it must be # portable for CI, e.g. a specific release. However, leaving it commented out would mean quicker CI. -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.11.1' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v4.13.0' # Locally, you can run against Maze Runner branches and uncommitted changes: # gem 'bugsnag-maze-runner', path: '../maze-runner' diff --git a/Gemfile.lock b/Gemfile.lock index e15809786..54ae93bc9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: b5925737b0c3db49ac38a5a68e6bfe38026ef508 - tag: v4.11.1 + revision: 3161e2a7115a2642a1929aa5c037ead65d4ebfba + tag: v4.13.0 specs: - bugsnag-maze-runner (4.11.1) + bugsnag-maze-runner (4.13.0) appium_lib (~> 11.2.0) boring (~> 0.1.0) cucumber (~> 3.1.2) @@ -35,7 +35,7 @@ GEM appium_lib_core (~> 4.1) nokogiri (~> 1.8, >= 1.8.1) tomlrb (~> 1.1) - appium_lib_core (4.4.1) + appium_lib_core (4.5.0) faye-websocket (~> 0.11.0) selenium-webdriver (~> 3.14, >= 3.14.1) atomos (0.1.3) @@ -170,7 +170,7 @@ GEM nap (1.1.0) netrc (0.11.0) no_proxy_fix (0.1.2) - nokogiri (1.11.1) + nokogiri (1.11.2) mini_portile2 (~> 2.5.0) racc (~> 1.4) octokit (4.20.0) diff --git a/Tests/BSGEventUploadKSCrashReportOperationTests.m b/Tests/BSGEventUploadKSCrashReportOperationTests.m new file mode 100644 index 000000000..ddb4cc0a2 --- /dev/null +++ b/Tests/BSGEventUploadKSCrashReportOperationTests.m @@ -0,0 +1,42 @@ +// +// BSGEventUploadKSCrashReportOperationTests.m +// Bugsnag +// +// Created by Nick Dowell on 18/02/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import +#import + +#import "BSGEventUploadKSCrashReportOperation.h" + +@interface BSGEventUploadKSCrashReportOperationTests : XCTestCase + +@end + +@implementation BSGEventUploadKSCrashReportOperationTests + +- (void)testKSCrashReport1 { + NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"KSCrashReport1" ofType:@"json" inDirectory:@"Data"]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + BSGEventUploadKSCrashReportOperation *operation = [[BSGEventUploadKSCrashReportOperation alloc] initWithFile:file delegate:nil]; +#pragma clang diagnostic pop + BugsnagEvent *event = [operation loadEventAndReturnError:nil]; + XCTAssertEqual(event.threads.count, 20); + XCTAssertEqualObjects([event.breadcrumbs valueForKeyPath:NSStringFromSelector(@selector(message))], @[@"Bugsnag loaded"]); + XCTAssertEqualObjects(event.app.bundleVersion, @"5"); + XCTAssertEqualObjects(event.app.id, @"com.bugsnag.macOSTestApp"); + XCTAssertEqualObjects(event.app.releaseStage, @"development"); + XCTAssertEqualObjects(event.app.type, @"macOS"); + XCTAssertEqualObjects(event.app.version, @"1.0.3"); + XCTAssertEqualObjects(event.errors.firstObject.errorClass, @"EXC_BAD_ACCESS"); + XCTAssertEqualObjects(event.errors.firstObject.errorMessage, @"Attempted to dereference null pointer."); + XCTAssertEqualObjects(event.threads.firstObject.stacktrace.firstObject.method, @"-[OverwriteLinkRegisterScenario run]"); + XCTAssertEqualObjects(event.threads.firstObject.stacktrace.firstObject.machoFile, @"/Users/nick/Library/Developer/Xcode/Derived Data/macOSTestApp-ffunpkxyeczwoccascsrmsggolbp/Build/Products/Debug/macOSTestApp.app/Contents/MacOS/macOSTestApp"); + XCTAssertEqualObjects(event.user.id, @"48decb8cf9f410c4c20e6f597070ee60b131a5c4"); + XCTAssertTrue(event.app.inForeground); +} + +@end diff --git a/Tests/BugsnagClientMirrorTest.m b/Tests/BugsnagClientMirrorTest.m index cbac52c5f..c95cc8bd9 100644 --- a/Tests/BugsnagClientMirrorTest.m +++ b/Tests/BugsnagClientMirrorTest.m @@ -35,7 +35,6 @@ - (void)setUp { @"start v16@0:8", @"initWithConfiguration: @24@0:8@16", @"watchLifecycleEvents: v24@0:8@16", - @"flushPendingReports v16@0:8", @"setupConnectivityListener v16@0:8", @"setSessionTracker: v24@0:8@16", @"addTerminationObserver: v24@0:8@16", @@ -45,7 +44,6 @@ - (void)setUp { @"notifyOutOfMemoryEvent v16@0:8", @"willEnterBackground: v24@0:8@16", @"willEnterForeground: v24@0:8@16", - @"errorReportApiClient @16@0:8", @"setConfiguration: v24@0:8@16", @"orientationChanged: v24@0:8@16", @"setMetadataLock: v24@0:8@16", @@ -70,7 +68,6 @@ - (void)setUp { @"sendBreadcrumbForTableViewNotification: v24@0:8@16", @"pluginClient @16@0:8", @"setNotifier: v24@0:8@16", - @"setErrorReportApiClient: v24@0:8@16", @"metadataChanged: v24@0:8@16", @"automaticBreadcrumbMenuItemEvents @16@0:8", @"serializeBreadcrumbs v16@0:8", @@ -124,22 +121,38 @@ - (void)setUp { @"shouldReportOOM c16@0:8", @"systemState @16@0:8", @"setSystemState: v24@0:8@16", + @"addAutoBreadcrumbForEvent: v24@0:8@16", + @"appHangDetectedWithThreads: v24@0:8@16", + @"appHangDetector @16@0:8", + @"appHangEnded v16@0:8", + @"appHangEvent @16@0:8", @"appLaunchTimer @16@0:8", @"appLaunchTimerFired: v24@0:8@16", @"configMetadataFile @16@0:8", @"configMetadataFromLastLaunch @16@0:8", + @"deliverEvent: v24@0:8@16", + @"eventUploader @16@0:8", + @"lastRunEndedWithAppHang B16@0:8", + @"lastRunEndedWithAppHang c16@0:8", + @"leaveBreadcrumbForEvent: v24@0:8@16", @"metadataFile @16@0:8", @"metadataFromLastLaunch @16@0:8", @"notificationBreadcrumbs @16@0:8", @"sendLaunchCrashSynchronously v16@0:8", + @"setAppHangDetector: v24@0:8@16", + @"setAppHangEvent: v24@0:8@16", @"setAppLaunchTimer: v24@0:8@16", @"setConfigMetadataFromLastLaunch: v24@0:8@16", + @"setEventUploader: v24@0:8@16", @"setLastRunInfo: v24@0:8@16", @"setMetadataFromLastLaunch: v24@0:8@16", @"setNotificationBreadcrumbs: v24@0:8@16", @"setStarted: v20@0:8B16", @"setStarted: v20@0:8c16", @"setStateMetadataFromLastLaunch: v24@0:8@16", + @"shouldNotifyEvent:withOnErrorBlock: B32@0:8@16@?24", + @"shouldNotifyEvent:withOnErrorBlock: c32@0:8@16@?24", + @"startAppHangDetector v16@0:8", @"stateMetadataFile @16@0:8", @"stateMetadataFromLastLaunch @16@0:8", ]]; diff --git a/Tests/BugsnagConfigurationTests.m b/Tests/BugsnagConfigurationTests.m index 240deb0b6..72dedc144 100644 --- a/Tests/BugsnagConfigurationTests.m +++ b/Tests/BugsnagConfigurationTests.m @@ -713,14 +713,6 @@ -(void)testUnavailableConvenienceInitializer { XCTAssertThrows(func(config, selector)); } -- (void)testErrorApiHeaders { - BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1]; - NSDictionary *headers = [config errorApiHeaders]; - XCTAssertEqualObjects(config.apiKey, headers[@"Bugsnag-Api-Key"]); - XCTAssertNotNil(headers[@"Bugsnag-Sent-At"]); - XCTAssertNotNil(headers[@"Bugsnag-Payload-Version"]); -} - - (void)testUser { BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1]; diff --git a/Tests/BugsnagErrorReportSinkTests.m b/Tests/BugsnagErrorReportSinkTests.m deleted file mode 100644 index 31a6dc9e2..000000000 --- a/Tests/BugsnagErrorReportSinkTests.m +++ /dev/null @@ -1,500 +0,0 @@ -// -// BugsnagErrorReportSinkTests.m -// Bugsnag -// -// Created by Simon Maynard on 12/1/14. -// -// - -#import "BugsnagPlatformConditional.h" - -#import -#import - -#import "BugsnagErrorReportApiClient.h" -#import "BugsnagErrorReportSink+Private.h" -#import "BugsnagEvent+Private.h" -#import "BugsnagHandledState.h" -#import "BugsnagNotifier.h" -#import "BugsnagTestConstants.h" -#import "URLSessionMock.h" - -@interface BugsnagErrorReportSinkTests : XCTestCase -@property NSDictionary *rawReportData; -@property NSDictionary *processedData; -@end - -@implementation BugsnagErrorReportSinkTests - -- (void)setUp { - [super setUp]; - - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *path = [bundle pathForResource:@"report" ofType:@"json"]; - NSString *contents = [NSString stringWithContentsOfFile:path - encoding:NSUTF8StringEncoding - error:nil]; - NSData *contentData = [contents dataUsingEncoding:NSUTF8StringEncoding]; - self.rawReportData = [NSJSONSerialization JSONObjectWithData:contentData - options:0 - error:nil]; - BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1]; - config.autoDetectErrors = NO; - // This value should not appear in the assertions, as it is not equal to - // the release stage in the serialized report - config.releaseStage = @"MagicalTestingTime"; - - id session = [[URLSessionMock alloc] init]; - - BugsnagErrorReportApiClient *apiClient = [[BugsnagErrorReportApiClient alloc] initWithSession:session queueName:@""]; - - BugsnagNotifier *notifier = [[BugsnagNotifier alloc] init]; - - BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:self.rawReportData]; - - BugsnagErrorReportSink *sink = [[BugsnagErrorReportSink alloc] initWithApiClient:apiClient configuration:config notifier:notifier]; - - self.processedData = [sink prepareEventPayload:event]; -} - -- (void)tearDown { - self.rawReportData = nil; - self.processedData = nil; - [super tearDown]; -} - -- (void)testCorrectTopLevelKeys { - NSArray *expectedKeys = @[@"apiKey", @"events", @"notifier", @"payloadVersion",]; - NSArray *topKeys = [self.processedData allKeys]; - XCTAssertEqualObjects( - [topKeys sortedArrayUsingSelector:@selector(compare:)], expectedKeys); -} - -- (void)testCorrectNotifierKeys { - NSArray *expectedKeys = @[ @"name", @"url", @"version" ]; - NSArray *notifierKeys = [self.processedData[@"notifier"] allKeys]; - XCTAssertEqualObjects( - [notifierKeys sortedArrayUsingSelector:@selector(compare:)], - expectedKeys); -} - -- (void)testNotifierName { - NSString *name = self.processedData[@"notifier"][@"name"]; -#if BSG_PLATFORM_TVOS - XCTAssertEqualObjects(name, @"tvOS Bugsnag Notifier"); -#elif BSG_PLATFORM_IOS - XCTAssertEqualObjects(name, @"iOS Bugsnag Notifier"); -#else - XCTAssertEqualObjects(name, @"OSX Bugsnag Notifier"); -#endif -} - -- (void)testNotifierDefaultURL { - NSString *URLPath = self.processedData[@"notifier"][@"url"]; - XCTAssertEqualObjects(URLPath, @"https://github.com/bugsnag/bugsnag-cocoa"); -} - -- (void)testNotifierVersion { - NSString *version = self.processedData[@"notifier"][@"version"]; - XCTAssert([version isKindOfClass:[NSString class]]); -} - -- (void)testEventCount { - NSArray *events = self.processedData[@"events"]; - XCTAssert(events.count == 1); -} - -- (void)testCorrectEventKeys { - NSArray *actualKeys = [[[self.processedData[@"events"] firstObject] allKeys] - sortedArrayUsingSelector:@selector(compare:)]; - NSArray *eventKeys = @[ - @"app", - @"breadcrumbs", - @"context", - @"device", - @"exceptions", - @"metaData", - @"severity", - @"severityReason", - @"threads", - @"unhandled", - @"user", - ]; - XCTAssertEqualObjects(actualKeys, eventKeys); -} - -- (void)testEventPayloadVersion { - NSString *payloadVersion = - [self.processedData[@"events"] firstObject][@"payloadVersion"]; - XCTAssertNil(payloadVersion); -} - -- (void)testEventSeverity { - NSDictionary *event = [self.processedData[@"events"] firstObject]; - XCTAssertNotNil(event); - - NSString *severity = event[@"severity"]; - XCTAssertFalse([event[@"unhandled"] boolValue]); - XCTAssertEqualObjects(severity, @"info"); -} - -- (void)testEventBreadcrumbs { - NSArray *expected = - [self.rawReportData valueForKeyPath:@"user.state.crash.breadcrumbs"]; - NSArray *breadcrumbs = - [self.processedData[@"events"] firstObject][@"breadcrumbs"]; - XCTAssertEqual(2, breadcrumbs.count); - for (int i = 0; i < breadcrumbs.count; i++) { - XCTAssertEqualObjects(expected[i][@"name"], breadcrumbs[i][@"message"]); - XCTAssertEqualObjects(expected[i][@"type"], breadcrumbs[i][@"type"]); - XCTAssertEqualObjects(expected[i][@"timestamp"], breadcrumbs[i][@"timestamp"]); - XCTAssertEqualObjects(expected[i][@"metadata"], breadcrumbs[i][@"metadata"]); - } -} - -- (void)testEventContext { - NSArray *expected = - [self.rawReportData valueForKeyPath:@"user.config.context"]; - NSArray *context = [self.processedData[@"events"] firstObject][@"context"]; - XCTAssertEqualObjects(context, expected); -} - -- (void)testEventMetadataApp { - NSDictionary *app = [[self.processedData valueForKeyPath:@"events.metaData.app"] firstObject]; - NSDictionary *expected = @{@"name" : self.rawReportData[@"system"][@"CFBundleExecutable"]}; - XCTAssertEqualObjects(app, expected); -} - -- (void)testEventMetadataUser { - NSDictionary *user = - [self.processedData[@"events"] firstObject][@"user"]; - NSDictionary *expected = - @{@"id" : self.rawReportData[@"system"][@"device_app_hash"]}; - XCTAssertEqualObjects(user, expected); -} - -- (void)testEventMetadataCustomTab { - NSDictionary *customTab = - [self.processedData[@"events"] firstObject][@"metaData"][@"tab"]; - NSDictionary *expected = @{@"key" : @"value"}; - XCTAssertEqualObjects(customTab, expected); -} - -- (void)testEventMetadataErrorAddress { - id address = [[self.processedData[@"events"] firstObject] - valueForKeyPath:@"metaData.error.address"]; - XCTAssertEqualObjects(address, @0); -} - -- (void)testEventMetadataErrorType { - id errorType = [[self.processedData[@"events"] firstObject] - valueForKeyPath:@"metaData.error.type"]; - XCTAssertEqualObjects(errorType, @"user"); -} - -- (void)testEventMetadataErrorReason { - id reason = [[self.processedData[@"events"] firstObject] - valueForKeyPath:@"metaData.error.reason"]; - XCTAssertEqualObjects(reason, @"You should've written more tests!"); -} - -- (void)testEventMetadataErrorSignal { - NSDictionary *signal = [[self.processedData[@"events"] firstObject] - valueForKeyPath:@"metaData.error.signal"]; - XCTAssert([signal[@"name"] isEqual:@"SIGABRT"]); - XCTAssert([signal[@"signal"] isEqual:@6]); - XCTAssert([signal[@"code"] isEqual:@0]); -} - -- (void)testEventMetadataErrorMach { - NSDictionary *mach = [[self.processedData[@"events"] firstObject] - valueForKeyPath:@"metaData.error.mach"]; - XCTAssert([mach[@"exception_name"] isEqual:@"EXC_CRASH"]); - XCTAssert([mach[@"subcode"] isEqual:@0]); - XCTAssert([mach[@"code"] isEqual:@0]); - XCTAssert([mach[@"exception"] isEqual:@10]); -} - -- (void)testEventMetadataErrorUserReported { - NSDictionary *reported = [[self.processedData[@"events"] firstObject] - valueForKeyPath:@"metaData.error.user_reported"]; - XCTAssertEqualObjects(reported[@"name"], @"name"); - XCTAssertEqualObjects(reported[@"line_of_code"], @""); -} - -- (void)testBinaryThreadStacktraces { - NSArray *events = self.processedData[@"events"]; - for (NSDictionary *thread in [events firstObject][@"threads"]) { - NSArray *stacktrace = thread[@"stacktrace"]; - - XCTAssertNotNil(stacktrace); - for (NSDictionary *frame in stacktrace) { - XCTAssertNotNil([frame valueForKey:@"machoUUID"]); - XCTAssertNotNil([frame valueForKey:@"machoFile"]); - XCTAssertNotNil([frame valueForKey:@"frameAddress"]); - XCTAssertNotNil([frame valueForKey:@"symbolAddress"]); - XCTAssertNotNil([frame valueForKey:@"machoLoadAddress"]); - XCTAssertNotNil([frame valueForKey:@"machoVMAddress"]); - } - } -} - -- (void)testEventExceptionCount { - NSArray *exceptions = - [self.processedData[@"events"] firstObject][@"exceptions"]; - XCTAssert(exceptions.count == 1); -} - -- (void)testEventExceptionData { - NSArray *exceptions = - [self.processedData[@"events"] firstObject][@"exceptions"]; - NSDictionary *exception = [exceptions firstObject]; - XCTAssertEqualObjects(exception[@"message"], - @"You should've written more tests!"); - XCTAssertEqualObjects(exception[@"errorClass"], @"name"); -} - -- (void)testExceptionStacktrace { - NSArray *exceptions = - [self.processedData[@"events"] firstObject][@"exceptions"]; - NSArray *stacktrace = [exceptions firstObject][@"stacktrace"]; - XCTAssertNotEqual(0, [stacktrace count]); - XCTAssertNotNil(stacktrace); - for (NSDictionary *frame in stacktrace) { - XCTAssertNotNil([frame valueForKey:@"machoUUID"]); - XCTAssertNotNil([frame valueForKey:@"machoFile"]); - XCTAssertNotNil([frame valueForKey:@"frameAddress"]); - XCTAssertNotNil([frame valueForKey:@"symbolAddress"]); - XCTAssertNotNil([frame valueForKey:@"machoLoadAddress"]); - XCTAssertNotNil([frame valueForKey:@"machoVMAddress"]); - } -} - -- (void)testEventThreadCount { - NSArray *threads = [self.processedData[@"events"] firstObject][@"threads"]; - XCTAssertTrue(threads.count == 9); -} - -- (void)testEventDevice { - NSDictionary *event = [self.processedData[@"events"] firstObject]; - NSDictionary *device = event[@"device"]; - XCTAssertNotNil(device); - XCTAssertEqual(14, device.count); - - XCTAssertEqualObjects(device[@"id"], @"f6d519a74213a57f8d052c53febfeee6f856d062"); - XCTAssertEqualObjects(device[@"manufacturer"], @"Apple"); - XCTAssertEqualObjects(device[@"model"], @"x86_64"); - XCTAssertEqualObjects(device[@"modelNumber"], @"MacBookPro11,3"); - XCTAssertEqualObjects(device[@"osName"], @"iPhone OS"); - XCTAssertEqualObjects(device[@"osVersion"], @"8.1"); - XCTAssertEqualObjects(device[@"runtimeVersions"][@"osBuild"], @"14B25"); - XCTAssertEqualObjects(device[@"runtimeVersions"][@"clangVersion"], @"10.0.0 (clang-1000.11.45.5)"); - XCTAssertEqualObjects(device[@"totalMemory"], @15065522176); - XCTAssertNotNil(device[@"freeDisk"]); - XCTAssertEqualObjects(device[@"jailbroken"], @YES); - XCTAssertEqualObjects(device[@"freeMemory"], @742920192); - XCTAssertEqualObjects(device[@"orientation"], @"unknown"); - XCTAssertEqualObjects(device[@"time"], @"2014-12-02T01:56:13.000Z"); -} - -- (void)testEventApp { - NSDictionary *event = [self.processedData[@"events"] firstObject]; - NSDictionary *app = event[@"app"]; - XCTAssertEqualObjects(app[@"id"], @"net.hockeyapp.CrashProbeiOS"); - XCTAssertNotNil(app[@"type"]); - XCTAssertEqualObjects(app[@"version"], @"1.0"); - XCTAssertEqualObjects(app[@"bundleVersion"], @"1"); - XCTAssertEqualObjects(app[@"releaseStage"], @"production"); - XCTAssertEqualObjects(app[@"dsymUUIDs"], @[@"D0A41830-4FD2-3B02-A23B-0741AD4C7F52"]); - XCTAssertEqualObjects(app[@"duration"], @4000); - XCTAssertEqualObjects(app[@"durationInForeground"], @2000); - XCTAssertEqualObjects(app[@"inForeground"], @YES); - XCTAssertEqualObjects(app[@"isLaunching"], @NO); -} - -#pragma mark - handled/unhandled serialisation - -- (NSDictionary *)reportFromHandledState:(BugsnagHandledState *)state { - BugsnagEvent *report = [self generateEvent:state]; - NSDictionary *data = [[BugsnagErrorReportSink new] prepareEventPayload:report]; - return [data[@"events"] firstObject]; -} - -- (BugsnagEvent *)generateEvent:(BugsnagHandledState *)state { - BugsnagEvent *report = [[BugsnagEvent alloc] initWithApp:nil - device:nil - handledState:state - user:nil - metadata:nil - breadcrumbs:@[] - errors:@[] - threads:@[] - session:nil]; - return report; -} - -- (void)testHandledSerialization { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:HandledException]; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertEqualObjects(@"warning", payload[@"severity"]); - XCTAssertFalse([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = - [BugsnagHandledState stringFromSeverityReason:HandledException]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - XCTAssertNil(severityReason[@"attributes"]); -} - -- (void)testHandledOverriddenSerialization { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:HandledException]; - state.unhandled = YES; - state.unhandledOverridden = YES; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertTrue([payload[@"unhandled"] boolValue]); - XCTAssertTrue([payload[@"severityReason"][@"unhandledOverridden"] boolValue]); - - state.unhandled = YES; - state.unhandledOverridden = NO; - payload = [self reportFromHandledState:state]; - - XCTAssertTrue([payload[@"unhandled"] boolValue]); - XCTAssertFalse([payload[@"unhandledOverridden"] boolValue]); - - state.unhandled = NO; - state.unhandledOverridden = YES; - payload = [self reportFromHandledState:state]; - - XCTAssertFalse([payload[@"unhandled"] boolValue]); - XCTAssertTrue([payload[@"severityReason"][@"unhandledOverridden"] boolValue]); - - state.unhandled = NO; - state.unhandledOverridden = NO; - payload = [self reportFromHandledState:state]; - - XCTAssertFalse([payload[@"unhandled"] boolValue]); - XCTAssertFalse([payload[@"unhandledOverridden"] boolValue]); -} - -- (void)testUnhandledSerialization { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:UnhandledException]; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertEqualObjects(@"error", payload[@"severity"]); - XCTAssertTrue([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = - [BugsnagHandledState stringFromSeverityReason:UnhandledException]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - XCTAssertNil(severityReason[@"attributes"]); -} - -- (void)testPromiseRejectionSerialization { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:PromiseRejection]; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertEqualObjects(@"error", payload[@"severity"]); - XCTAssertTrue([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = - [BugsnagHandledState stringFromSeverityReason:PromiseRejection]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - XCTAssertNil(severityReason[@"attributes"]); -} - -- (void)testUserSpecifiedSerialisation { - BugsnagHandledState *state = [BugsnagHandledState - handledStateWithSeverityReason:UserSpecifiedSeverity]; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertEqualObjects(@"warning", payload[@"severity"]); - XCTAssertFalse([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = - [BugsnagHandledState stringFromSeverityReason:UserSpecifiedSeverity]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - XCTAssertNil(severityReason[@"attributes"]); -} - -- (void)testCallbackSpecified { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:HandledException]; - BugsnagEvent *report = [self generateEvent:state]; - report.severity = BSGSeverityInfo; - - NSDictionary *data = [[BugsnagErrorReportSink new] prepareEventPayload:report]; - NSDictionary *payload = [data[@"events"] firstObject]; - - XCTAssertEqualObjects(@"info", payload[@"severity"]); - XCTAssertFalse([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = - [BugsnagHandledState stringFromSeverityReason:UserCallbackSetSeverity]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - XCTAssertNil(severityReason[@"attributes"]); -} - -- (void)testHandledErrorSerialization { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:HandledError - severity:BSGSeverityWarning - attrValue:@"test"]; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertEqualObjects(@"warning", payload[@"severity"]); - XCTAssertFalse([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = - [BugsnagHandledState stringFromSeverityReason:HandledError]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - - NSDictionary *attrs = severityReason[@"attributes"]; - XCTAssertNil(attrs); -} - -- (void)testSignalSerialization { - BugsnagHandledState *state = - [BugsnagHandledState handledStateWithSeverityReason:Signal - severity:BSGSeverityError - attrValue:@"test"]; - NSDictionary *payload = [self reportFromHandledState:state]; - - XCTAssertEqualObjects(@"error", payload[@"severity"]); - XCTAssertTrue([payload[@"unhandled"] boolValue]); - - NSDictionary *severityReason = payload[@"severityReason"]; - XCTAssertNotNil(severityReason); - - NSString *expected = [BugsnagHandledState stringFromSeverityReason:Signal]; - XCTAssertEqualObjects(expected, severityReason[@"type"]); - - NSDictionary *attrs = severityReason[@"attributes"]; - XCTAssertNotNil(attrs); - XCTAssertEqual(1, [attrs count]); - XCTAssertEqualObjects(@"test", attrs[@"signalType"]); -} - -@end diff --git a/Tests/BugsnagEventFromKSCrashReportTest.m b/Tests/BugsnagEventFromKSCrashReportTest.m index fecfbdfc8..eb8b94bd6 100644 --- a/Tests/BugsnagEventFromKSCrashReportTest.m +++ b/Tests/BugsnagEventFromKSCrashReportTest.m @@ -107,7 +107,7 @@ - (void)testAddMetadataRemovesValue { } - (void)testAppVersion { - NSDictionary *dictionary = [self.event toJson]; + NSDictionary *dictionary = [self.event toJsonWithRedactedKeys:nil]; XCTAssertEqualObjects(dictionary[@"app"][@"version"], @"1.0"); XCTAssertEqualObjects(dictionary[@"app"][@"bundleVersion"], @"1"); } diff --git a/Tests/BugsnagEventPersistLoadTest.m b/Tests/BugsnagEventPersistLoadTest.m index 633086b2a..ad199cca8 100644 --- a/Tests/BugsnagEventPersistLoadTest.m +++ b/Tests/BugsnagEventPersistLoadTest.m @@ -356,7 +356,7 @@ - (void)testReactNativePromiseRejection { XCTAssertEqualObjects([userData valueForKeyPath:@"user.event.exceptions.@count"], @(2)); BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:userData]; XCTAssertEqual(event.errors.count, 2); - XCTAssertEqualObjects([[event toJson] valueForKeyPath:@"exceptions.@count"], @(2), + XCTAssertEqualObjects([[event toJsonWithRedactedKeys:nil] valueForKeyPath:@"exceptions.@count"], @(2), @"JSON representation of event should have the same number of errors / exceptions"); } diff --git a/Tests/BugsnagEventTests.m b/Tests/BugsnagEventTests.m index fe487e95d..5f75fc905 100644 --- a/Tests/BugsnagEventTests.m +++ b/Tests/BugsnagEventTests.m @@ -68,7 +68,7 @@ - (void)testSessionJson { BugsnagEvent *event = [self generateEvent:nil]; event.session = bugsnagSession; - NSDictionary *json = [event toJson]; + NSDictionary *json = [event toJsonWithRedactedKeys:nil]; XCTAssertNotNil(json); NSDictionary *session = json[@"session"]; @@ -87,7 +87,7 @@ - (void)testDefaultErrorMessageNilForEmptyThreads { BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ @"threads" : @[] }]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:nil]; XCTAssertEqualObjects(@"Exception", payload[@"exceptions"][0][@"errorClass"]); XCTAssertEqualObjects(@"", payload[@"exceptions"][0][@"message"]); @@ -211,7 +211,7 @@ - (void)testAppVersionOverride { } } }]; - NSDictionary *dictionary = [overrideReport toJson]; + NSDictionary *dictionary = [overrideReport toJsonWithRedactedKeys:nil]; XCTAssertEqualObjects(@"1.2.3", dictionary[@"app"][@"version"]); } @@ -226,7 +226,7 @@ - (void)testBundleVersionOverride { } } }]; - NSDictionary *dictionary = [overrideReport toJson]; + NSDictionary *dictionary = [overrideReport toJsonWithRedactedKeys:nil]; XCTAssertEqualObjects(@"1.2.3", dictionary[@"app"][@"bundleVersion"]); } @@ -340,6 +340,32 @@ - (void)testStacktraceTypes { XCTAssertEqualObjects(sorted(event.stacktraceTypes), (@[@"android", @"c", @"cocoa", @"csharp", @"java"])); } +// MARK: - JSON serialization tests + +- (void)testJsonToEventToJson { + NSString *directory = [[[[NSBundle bundleForClass:[self class]] resourcePath] + stringByAppendingPathComponent:@"Data"] + stringByAppendingPathComponent:@"BugsnagEvents"]; + + NSArray *entries = [NSFileManager.defaultManager contentsOfDirectoryAtPath:directory error:nil]; + + for (NSString *filename in entries) { + if (![filename.pathExtension isEqual:@"json"] || [filename hasSuffix:@"."]) { + continue; + } + + NSString *file = [directory stringByAppendingPathComponent:filename]; + NSData *data = [NSData dataWithContentsOfFile:file]; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + + BugsnagEvent *event = [[BugsnagEvent alloc] initWithJson:json]; + XCTAssertNotNil(event); + + NSDictionary *toJson = [event toJsonWithRedactedKeys:nil]; + XCTAssertEqualObjects(json, toJson, @"Input and output JSON do not match"); + } +} + // MARK: - Metadata interface - (void)testAddMetadataSectionKeyValue { diff --git a/Tests/BugsnagMetadataRedactionTest.m b/Tests/BugsnagMetadataRedactionTest.m index 98ab7a37a..16fa94296 100644 --- a/Tests/BugsnagMetadataRedactionTest.m +++ b/Tests/BugsnagMetadataRedactionTest.m @@ -22,7 +22,7 @@ - (void)testEmptyRedaction { @"some_key": @"2fa0" }]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:nil]; NSDictionary *section = payload[@"metaData"][@"custom"]; XCTAssertNotNil(section); XCTAssertEqualObjects(@"hunter2", section[@"password"]); @@ -34,9 +34,8 @@ - (void)testDefaultRedaction { @"password": @"hunter2", @"some_key": @"2fa0" }]; - event.redactedKeys = [NSSet setWithArray:@[@"password"]]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:[NSSet setWithArray:@[@"password"]]]; NSDictionary *section = payload[@"metaData"][@"custom"]; XCTAssertNotNil(section); XCTAssertEqualObjects(@"[REDACTED]", section[@"password"]); @@ -54,9 +53,8 @@ - (void)testNestedRedaction { }, @"some_key": @"2fa0" }]; - event.redactedKeys = [NSSet setWithArray:@[@"password"]]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:[NSSet setWithArray:@[@"password"]]]; NSDictionary *section = payload[@"metaData"][@"custom"]; XCTAssertNotNil(section); XCTAssertEqualObjects(@"[REDACTED]", section[@"user_auth"][@"meta"][@"password"]); @@ -74,9 +72,8 @@ - (void)testNonDefaultKeys { @"some_key": @"2fa0", @"foo": @"gasdf" }]; - event.redactedKeys = [NSSet setWithArray:@[@"authority", @"some_key"]]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:[NSSet setWithArray:@[@"authority", @"some_key"]]]; NSDictionary *section = payload[@"metaData"][@"custom"]; XCTAssertNotNil(section); XCTAssertEqualObjects(@"123456", section[@"user_auth"][@"password"]); @@ -102,9 +99,8 @@ - (void)testRegexRedaction { }]; // disallow any numeric characters NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[0-9]" options:0 error:nil]; - event.redactedKeys = [NSSet setWithArray:@[@"password", regex]]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:[NSSet setWithArray:@[@"password", regex]]]; NSDictionary *section = payload[@"metaData"][@"custom"]; XCTAssertNotNil(section); XCTAssertEqualObjects(@"[REDACTED]", section[@"password"]); @@ -123,9 +119,8 @@ - (void)testCaseInsensitiveKeys { }]; // Note: this redacts both keys - event.redactedKeys = [NSSet setWithArray:@[@"password", @"Caseinsensitivekey"]]; - NSDictionary *payload = [event toJson]; + NSDictionary *payload = [event toJsonWithRedactedKeys:[NSSet setWithArray:@[@"password", @"Caseinsensitivekey"]]]; NSDictionary *section = payload[@"metaData"][@"custom"]; XCTAssertNotNil(section); XCTAssertEqualObjects(@"[REDACTED]", section[@"password"]); diff --git a/Tests/BugsnagThreadSerializationTest.m b/Tests/BugsnagThreadSerializationTest.m index 94bc76bd3..0a1176dd5 100644 --- a/Tests/BugsnagThreadSerializationTest.m +++ b/Tests/BugsnagThreadSerializationTest.m @@ -14,7 +14,7 @@ @implementation BugsnagThreadSerializationTest - (void)testEmptyThreads { BugsnagEvent *event = [self generateReportWithThreads:@[]]; - NSArray *threads = [event toJson][@"threads"]; + NSArray *threads = [event toJsonWithRedactedKeys:nil][@"threads"]; XCTAssertTrue(threads.count == 0); } @@ -57,7 +57,7 @@ - (void)testThreadSerialisation { ]; BugsnagEvent *event = [self generateReportWithThreads:trace]; - NSArray *threads = [event toJson][@"threads"]; + NSArray *threads = [event toJsonWithRedactedKeys:nil][@"threads"]; XCTAssertTrue(threads.count == 2); // first thread is crashed, should be serialised and contain 'errorReportingThread' flag diff --git a/Tests/Data/BugsnagEvents/BugsnagEvent1.json b/Tests/Data/BugsnagEvents/BugsnagEvent1.json new file mode 100644 index 000000000..97b4b5289 --- /dev/null +++ b/Tests/Data/BugsnagEvents/BugsnagEvent1.json @@ -0,0 +1 @@ +{"device":{"id":"5ed88ab6980274562bcd9106bd03e17810da3e79","orientation":"portrait","osName":"iOS","jailbroken":false,"osVersion":"14.3","time":"2021-01-19T11:16:25.000Z","locale":"en_US","runtimeVersions":{"osBuild":"19H114","clangVersion":"12.0.0 (clang-1200.0.32.28)"},"freeMemory":27904143360,"manufacturer":"Apple","freeDisk":551348178944,"modelNumber":"simulator","model":"iPod9,1","totalMemory":68715134976},"exceptions":[{"message":"-[ViewController someRandomMethod]: unrecognized selector sent to instance 0x7fbd4e51cc60","errorClass":"NSInvalidArgumentException","stacktrace":[{"method":"__exceptionPreprocess","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","isPC":true,"symbolAddress":"0x7fff20420a04","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20420ae6"},{"method":"objc_exception_throw","machoVMAddress":"0x7fff20172000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/libobjc.A.dylib","symbolAddress":"0x7fff20177e48","machoUUID":"0ED2E6A3-D7FC-3A31-A1CA-6BE106521240","machoLoadAddress":"0x7fff20172000","frameAddress":"0x7fff20177e78"},{"method":"-[NSObject(NSObject) doesNotRecognizeSelector:]","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2042f673","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2042f6f7"},{"method":"-[UIResponder doesNotRecognizeSelector:]","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff246c5a33","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff246c5b57"},{"method":"___forwarding___","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20424a65","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20425036"},{"method":"_CF_forwarding_prep_0","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20426ff0","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20427068"},{"method":"__NSThreadPerformPerform","machoVMAddress":"0x7fff2071b000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/Foundation.framework\/Foundation","symbolAddress":"0x7fff208581ee","machoUUID":"5716A8B8-2769-3484-9FD8-196630050F5B","machoLoadAddress":"0x7fff2071b000","frameAddress":"0x7fff208582ba"},{"method":"__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038f379","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038f38a"},{"method":"__CFRunLoopDoSource0","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038f1ce","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038f282"},{"method":"__CFRunLoopDoSources0","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038e66c","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038e764"},{"method":"__CFRunLoopRun","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20388bc1","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20388f2f"},{"method":"CFRunLoopRunSpecific","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038849f","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff203886d6"},{"method":"GSEventRunModal","machoVMAddress":"0x7fff2bedb000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/GraphicsServices.framework\/GraphicsServices","symbolAddress":"0x7fff2beded28","machoUUID":"EFA60C9C-ACAF-3326-BDC5-4B361494A126","machoLoadAddress":"0x7fff2bedb000","frameAddress":"0x7fff2bededb3"},{"method":"-[UIApplication _run]","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff24690a7b","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff24690e0b"},{"method":"UIApplicationMain","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff24695c57","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff24695cbc"},{"method":"_mh_execute_header","machoVMAddress":"0x100000000","machoFile":"\/Users\/nick\/Library\/Developer\/CoreSimulator\/Devices\/5AFE2FCA-EB57-45D2-A705-42F81D4031F3\/data\/Containers\/Bundle\/Application\/B7477EE0-B11B-41B3-9A7A-FFEE9E28AA47\/Bugsnag Test App.app\/Bugsnag Test App","symbolAddress":"0x10d2d0000","machoUUID":"F18A2C56-008C-3CC1-8BFF-4E79683FB1AB","machoLoadAddress":"0x10d2d0000","frameAddress":"0x10d2d1c70"},{"method":"start","machoVMAddress":"0x7fff20258000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libdyld.dylib","symbolAddress":"0x7fff202593e8","machoUUID":"78F65EE7-1659-3B52-9FE5-FDD6C61BDCAA","machoLoadAddress":"0x7fff20258000","frameAddress":"0x7fff202593e9"}],"type":"cocoa"}],"breadcrumbs":[{"timestamp":"2021-01-19T11:16:01.262Z","name":"Bugsnag loaded","type":"state","metaData":{}},{"timestamp":"2021-01-19T11:16:01.276Z","name":"Window Became Visible","type":"state","metaData":{}},{"timestamp":"2021-01-19T11:16:10.779Z","name":"Window Became Visible","type":"state","metaData":{}}],"app":{"bundleVersion":"4","durationInForeground":24000,"dsymUUIDs":["F18A2C56-008C-3CC1-8BFF-4E79683FB1AB"],"id":"bugsnag.Bugsnag-Test-App","inForeground":true,"isLaunching":false,"duration":24000,"version":"1.0","type":"iOS","releaseStage":"development"},"threads":[{"errorReportingThread":true,"id":"0","stacktrace":[{"method":"__exceptionPreprocess","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","isPC":true,"symbolAddress":"0x7fff20420a04","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20420ae6"},{"method":"objc_exception_throw","machoVMAddress":"0x7fff20172000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/libobjc.A.dylib","symbolAddress":"0x7fff20177e48","machoUUID":"0ED2E6A3-D7FC-3A31-A1CA-6BE106521240","machoLoadAddress":"0x7fff20172000","frameAddress":"0x7fff20177e78"},{"method":"-[NSObject(NSObject) doesNotRecognizeSelector:]","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2042f673","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2042f6f7"},{"method":"-[UIResponder doesNotRecognizeSelector:]","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff246c5a33","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff246c5b57"},{"method":"___forwarding___","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20424a65","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20425036"},{"method":"_CF_forwarding_prep_0","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20426ff0","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20427068"},{"method":"__NSThreadPerformPerform","machoVMAddress":"0x7fff2071b000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/Foundation.framework\/Foundation","symbolAddress":"0x7fff208581ee","machoUUID":"5716A8B8-2769-3484-9FD8-196630050F5B","machoLoadAddress":"0x7fff2071b000","frameAddress":"0x7fff208582ba"},{"method":"__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038f379","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038f38a"},{"method":"__CFRunLoopDoSource0","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038f1ce","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038f282"},{"method":"__CFRunLoopDoSources0","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038e66c","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038e764"},{"method":"__CFRunLoopRun","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20388bc1","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff20388f2f"},{"method":"CFRunLoopRunSpecific","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038849f","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff203886d6"},{"method":"GSEventRunModal","machoVMAddress":"0x7fff2bedb000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/GraphicsServices.framework\/GraphicsServices","symbolAddress":"0x7fff2beded28","machoUUID":"EFA60C9C-ACAF-3326-BDC5-4B361494A126","machoLoadAddress":"0x7fff2bedb000","frameAddress":"0x7fff2bededb3"},{"method":"-[UIApplication _run]","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff24690a7b","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff24690e0b"},{"method":"UIApplicationMain","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff24695c57","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff24695cbc"},{"method":"_mh_execute_header","machoVMAddress":"0x100000000","machoFile":"\/Users\/nick\/Library\/Developer\/CoreSimulator\/Devices\/5AFE2FCA-EB57-45D2-A705-42F81D4031F3\/data\/Containers\/Bundle\/Application\/B7477EE0-B11B-41B3-9A7A-FFEE9E28AA47\/Bugsnag Test App.app\/Bugsnag Test App","symbolAddress":"0x10d2d0000","machoUUID":"F18A2C56-008C-3CC1-8BFF-4E79683FB1AB","machoLoadAddress":"0x10d2d0000","frameAddress":"0x10d2d1c70"},{"method":"start","machoVMAddress":"0x7fff20258000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libdyld.dylib","symbolAddress":"0x7fff202593e8","machoUUID":"78F65EE7-1659-3B52-9FE5-FDD6C61BDCAA","machoLoadAddress":"0x7fff20258000","frameAddress":"0x7fff202593e9"}],"type":"cocoa"},{"errorReportingThread":false,"id":"1","stacktrace":[{"method":"__workq_kernreturn","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c534c4","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c534ce"},{"method":"start_wqthread","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c89b68","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c89b77"}],"type":"cocoa"},{"errorReportingThread":false,"id":"2","stacktrace":[{"method":"__workq_kernreturn","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c534c4","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c534ce"},{"method":"start_wqthread","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c89b68","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c89b77"}],"type":"cocoa"},{"errorReportingThread":false,"id":"3","stacktrace":[{"method":"mach_msg_trap","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c51df0","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c51dfa"},{"method":"__CFRunLoopServiceMachPort","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038e880","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038e9bc"},{"method":"__CFRunLoopRun","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20388bc1","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff203890c5"},{"method":"CFRunLoopRunSpecific","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038849f","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff203886d6"},{"method":"-[NSRunLoop(NSRunLoop) runMode:beforeDate:]","machoVMAddress":"0x7fff2071b000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/Foundation.framework\/Foundation","symbolAddress":"0x7fff2082f6e8","machoUUID":"5716A8B8-2769-3484-9FD8-196630050F5B","machoLoadAddress":"0x7fff2071b000","frameAddress":"0x7fff2082f7b9"},{"method":"-[NSRunLoop(NSRunLoop) runUntilDate:]","machoVMAddress":"0x7fff2071b000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/Foundation.framework\/Foundation","symbolAddress":"0x7fff2082f9e0","machoUUID":"5716A8B8-2769-3484-9FD8-196630050F5B","machoLoadAddress":"0x7fff2071b000","frameAddress":"0x7fff2082fa28"},{"method":"-[UIEventFetcher threadMain]","machoVMAddress":"0x7fff23a99000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/PrivateFrameworks\/UIKitCore.framework\/UIKitCore","symbolAddress":"0x7fff24747fa1","machoUUID":"984E55B9-03C9-3D2A-95DC-3A5F434A4A71","machoLoadAddress":"0x7fff23a99000","frameAddress":"0x7fff24748171"},{"method":"__NSThread__start__","machoVMAddress":"0x7fff2071b000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/Foundation.framework\/Foundation","symbolAddress":"0x7fff20857a56","machoUUID":"5716A8B8-2769-3484-9FD8-196630050F5B","machoLoadAddress":"0x7fff2071b000","frameAddress":"0x7fff20857e68"},{"method":"_pthread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c8e075","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c8e109"},{"method":"thread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c89b7c","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c89b8b"}],"type":"cocoa"},{"errorReportingThread":false,"id":"4","stacktrace":[{"method":"mach_msg_trap","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c51df0","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c51dfa"},{"method":"thread_suspend","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c6f7dd","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c6f82d"},{"method":"__cxa_throw","machoVMAddress":"0x100000000","machoFile":"\/Users\/nick\/Library\/Developer\/CoreSimulator\/Devices\/5AFE2FCA-EB57-45D2-A705-42F81D4031F3\/data\/Containers\/Bundle\/Application\/B7477EE0-B11B-41B3-9A7A-FFEE9E28AA47\/Bugsnag Test App.app\/Bugsnag Test App","symbolAddress":"0x10d2de0b0","machoUUID":"F18A2C56-008C-3CC1-8BFF-4E79683FB1AB","machoLoadAddress":"0x10d2d0000","frameAddress":"0x10d2dee10"},{"method":"_pthread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c8e075","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c8e109"},{"method":"thread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c89b7c","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c89b8b"}],"type":"cocoa"},{"errorReportingThread":false,"id":"5","stacktrace":[{"method":"mach_msg_trap","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c51df0","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c51dfa"},{"method":"__cxa_throw","machoVMAddress":"0x100000000","machoFile":"\/Users\/nick\/Library\/Developer\/CoreSimulator\/Devices\/5AFE2FCA-EB57-45D2-A705-42F81D4031F3\/data\/Containers\/Bundle\/Application\/B7477EE0-B11B-41B3-9A7A-FFEE9E28AA47\/Bugsnag Test App.app\/Bugsnag Test App","symbolAddress":"0x10d2de0b0","machoUUID":"F18A2C56-008C-3CC1-8BFF-4E79683FB1AB","machoLoadAddress":"0x10d2d0000","frameAddress":"0x10d2dee40"},{"method":"_pthread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c8e075","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c8e109"},{"method":"thread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c89b7c","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c89b8b"}],"type":"cocoa"},{"errorReportingThread":false,"id":"6","stacktrace":[{"method":"mach_msg_trap","machoVMAddress":"0x7fff60c51000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_kernel.dylib","symbolAddress":"0x7fff60c51df0","machoUUID":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","machoLoadAddress":"0x7fff60c51000","frameAddress":"0x7fff60c51dfa"},{"method":"__CFRunLoopServiceMachPort","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038e880","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff2038e9bc"},{"method":"__CFRunLoopRun","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff20388bc1","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff203890c5"},{"method":"CFRunLoopRunSpecific","machoVMAddress":"0x7fff2030f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CoreFoundation.framework\/CoreFoundation","symbolAddress":"0x7fff2038849f","machoUUID":"8FC68AD0-5128-3700-9E63-F6F358B6321B","machoLoadAddress":"0x7fff2030f000","frameAddress":"0x7fff203886d6"},{"method":"_CFURLStorageSessionCopyCache","machoVMAddress":"0x7fff2350f000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/CFNetwork.framework\/CFNetwork","symbolAddress":"0x7fff2372af3f","machoUUID":"D9DAA7CF-DDC1-361B-A7AD-0D4783893E72","machoLoadAddress":"0x7fff2350f000","frameAddress":"0x7fff23739706"},{"method":"__NSThread__start__","machoVMAddress":"0x7fff2071b000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/System\/Library\/Frameworks\/Foundation.framework\/Foundation","symbolAddress":"0x7fff20857a56","machoUUID":"5716A8B8-2769-3484-9FD8-196630050F5B","machoLoadAddress":"0x7fff2071b000","frameAddress":"0x7fff20857e68"},{"method":"_pthread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c8e075","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c8e109"},{"method":"thread_start","machoVMAddress":"0x7fff60c88000","machoFile":"\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Library\/Developer\/CoreSimulator\/Profiles\/Runtimes\/iOS.simruntime\/Contents\/Resources\/RuntimeRoot\/usr\/lib\/system\/libsystem_pthread.dylib","symbolAddress":"0x7fff60c89b7c","machoUUID":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","machoLoadAddress":"0x7fff60c88000","frameAddress":"0x7fff60c89b8b"}],"type":"cocoa"}],"metaData":{"app":{"name":"Bugsnag Test App"},"device":{"orientation":"portrait","timezone":"GMT","wordSize":64,"simulator":true,"batteryLevel":-1,"charging":false},"error":{"nsexception":{"name":"NSInvalidArgumentException"},"reason":"-[ViewController someRandomMethod]: unrecognized selector sent to instance 0x7fbd4e51cc60","type":"nsexception","address":0},"user":{"id":"5ed88ab6980274562bcd9106bd03e17810da3e79"}},"unhandled":true,"session":{"id":"5C5C6908-726F-4CCE-A081-23BDA1157911","startedAt":"2021-01-19T11:16:01.259Z","events":{"handled":0,"unhandled":1}},"severity":"error","severityReason":{"type":"unhandledException"},"user":{"id":"5ed88ab6980274562bcd9106bd03e17810da3e79"}} diff --git a/Tests/Data/KSCrashReport1.json b/Tests/Data/KSCrashReport1.json new file mode 100644 index 000000000..d54103b36 --- /dev/null +++ b/Tests/Data/KSCrashReport1.json @@ -0,0 +1 @@ +{"report":{"version":"3.1.0","id":"DDED29F4-37A2-4ECC-ABF6-E8483B6FBA2C","process_name":"macOSTestApp","timestamp":1613661883,"type":"standard"},"process":{},"system":{"CFBundleExecutable":"macOSTestApp","CFBundleExecutablePath":"/Users/nick/Library/Developer/Xcode/Derived Data/macOSTestApp-ffunpkxyeczwoccascsrmsggolbp/Build/Products/Debug/macOSTestApp.app/macOSTestApp","CFBundleIdentifier":"com.bugsnag.macOSTestApp","CFBundleName":"macOSTestApp","CFBundleShortVersionString":"1.0.3","CFBundleVersion":"5","app_start_time":"2021-02-18T15:24:43.111Z","app_uuid":"9B407C12-773C-3ED3-9178-9EC67311E5FC","application_stats":{"active_time_since_launch":0.000456487,"application_in_foreground":true,"background_time_since_launch":0},"binary_cpu_subtype":3,"binary_cpu_type":16777223,"boot_time":"2021-02-09T16:59:02.000Z","build_type":"debug","clang_version":"12.0.0 (clang-1200.0.32.29)","cpu_arch":"x86","cpu_subtype":8,"cpu_type":7,"device_app_hash":"48decb8cf9f410c4c20e6f597070ee60b131a5c4","jailbroken":false,"kernel_version":"Darwin Kernel Version 19.6.0: Tue Nov 10 00:10:30 PST 2020; root:xnu-6153.141.10~1/RELEASE_X86_64","machine":"MacBookPro16,1","memory":{"size":68719476736,"usable":67152240640},"os_version":"19H114","parent_process_id":42941,"process_id":60725,"process_name":"macOSTestApp","system_name":"Mac OS","system_version":"10.15.7","time_zone":"GMT"},"system_atcrash":{"memory":{"usable":67151998976,"free":21520228352},"application_stats":{"application_in_foreground":true,"launches_since_last_crash":2,"sessions_since_last_crash":2,"active_time_since_last_crash":0.00493379,"background_time_since_last_crash":0,"sessions_since_launch":1,"active_time_since_launch":0.00493379,"background_time_since_launch":0}},"binary_images":[{"image_addr":4360257536,"image_vmaddr":4294967296,"image_size":311296,"name":"/Users/nick/Library/Developer/Xcode/Derived Data/macOSTestApp-ffunpkxyeczwoccascsrmsggolbp/Build/Products/Debug/macOSTestApp.app/Contents/MacOS/macOSTestApp","uuid":"9B407C12-773C-3ED3-9178-9EC67311E5FC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":4361379840,"image_vmaddr":0,"image_size":475136,"name":"/Users/nick/Library/Developer/Xcode/Derived Data/macOSTestApp-ffunpkxyeczwoccascsrmsggolbp/Build/Products/Debug/Bugsnag.framework/Versions/A/Bugsnag","uuid":"6B328972-B665-3D19-9F53-EC3C2762DE9C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734091161600,"image_vmaddr":140733989814272,"image_size":3956736,"name":"/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation","uuid":"E020DC09-4221-3961-A450-638525789B38","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735006285824,"image_vmaddr":140734904938496,"image_size":212992,"name":"/usr/lib/libobjc.A.dylib","uuid":"6DF81160-5E7F-3E31-AA1E-C875E3B98AF6","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734977482752,"image_vmaddr":140734876135424,"image_size":339968,"name":"/usr/lib/libc++.1.dylib","uuid":"59A8239F-C28A-3B59-B8FA-11340DC85EDC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734974443520,"image_vmaddr":140734873096192,"image_size":8192,"name":"/usr/lib/libSystem.B.dylib","uuid":"8E6AD412-91E7-36FC-A6FD-C13B06A4952A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734003040256,"image_vmaddr":140733901692928,"image_size":14422016,"name":"/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit","uuid":"322D043D-1140-3486-9523-3F56FF4D8349","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734025830400,"image_vmaddr":140733924483072,"image_size":3735552,"name":"/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork","uuid":"07F9CA9C-B954-3EA0-A710-3122BFF9F057","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734050541568,"image_vmaddr":140733949194240,"image_size":4718592,"name":"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation","uuid":"56B0FE5F-D493-3DF1-ABFF-ECC450268B97","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140735014563840,"image_vmaddr":140734913216512,"image_size":3891200,"name":"/usr/lib/swift/libswiftCore.dylib","uuid":"E56CCFCA-99E1-36E5-A6BC-F31F53C79910","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735018541056,"image_vmaddr":140734917193728,"image_size":12288,"name":"/usr/lib/swift/libswiftCoreFoundation.dylib","uuid":"FBA4566B-AD2B-35D7-BC9A-48BE3D88B658","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735018553344,"image_vmaddr":140734917206016,"image_size":57344,"name":"/usr/lib/swift/libswiftCoreGraphics.dylib","uuid":"A8225B5F-F64D-32F8-AD91-D919DF614AA0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735018995712,"image_vmaddr":140734917648384,"image_size":28672,"name":"/usr/lib/swift/libswiftDarwin.dylib","uuid":"F3684684-8258-310F-B05B-BD8A696F0F29","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735019024384,"image_vmaddr":140734917677056,"image_size":98304,"name":"/usr/lib/swift/libswiftDispatch.dylib","uuid":"94D67EF4-42B5-3F54-8D86-B6B2B16827DC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735019122688,"image_vmaddr":140734917775360,"image_size":1597440,"name":"/usr/lib/swift/libswiftFoundation.dylib","uuid":"0173898D-FDA6-378D-87E9-1F2A9BD227D3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735020777472,"image_vmaddr":140734919430144,"image_size":12288,"name":"/usr/lib/swift/libswiftIOKit.dylib","uuid":"FD313708-AE48-3C72-A154-53EC839A8D55","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735021412352,"image_vmaddr":140734920065024,"image_size":16384,"name":"/usr/lib/swift/libswiftObjectiveC.dylib","uuid":"E668BD5D-E1D6-3C21-BA7E-5C3A672A964E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735022477312,"image_vmaddr":140734921129984,"image_size":12288,"name":"/usr/lib/swift/libswiftXPC.dylib","uuid":"3CD547C3-7082-37EA-B289-F6BA6C4D4E26","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735013478400,"image_vmaddr":140734912131072,"image_size":77824,"name":"/usr/lib/libz.1.dylib","uuid":"793D9643-CD83-3AAC-8B96-88D548FAB620","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734279155712,"image_vmaddr":140734177808384,"image_size":503808,"name":"/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration","uuid":"84F9B3BB-F7AF-3B7C-8CD0-D3C22D19619F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734977822720,"image_vmaddr":140734876475392,"image_size":90112,"name":"/usr/lib/libc++abi.dylib","uuid":"E692F14F-C65E-303B-9921-BB7E97D77855","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735022657536,"image_vmaddr":140734921310208,"image_size":24576,"name":"/usr/lib/system/libcache.dylib","uuid":"AF488D13-9E89-35E0-B078-BE37CC5B8586","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735022682112,"image_vmaddr":140734921334784,"image_size":49152,"name":"/usr/lib/system/libcommonCrypto.dylib","uuid":"C7912BE5-993E-3581-B2A0-6AABDC8C5562","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735022731264,"image_vmaddr":140734921383936,"image_size":32768,"name":"/usr/lib/system/libcompiler_rt.dylib","uuid":"49B8F644-5705-3F16-BBE0-6FFF9B17C36E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735022764032,"image_vmaddr":140734921416704,"image_size":40960,"name":"/usr/lib/system/libcopyfile.dylib","uuid":"3C481225-21E7-370A-A30E-0CCFDD64A92C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735022804992,"image_vmaddr":140734921457664,"image_size":602112,"name":"/usr/lib/system/libcorecrypto.dylib","uuid":"60567BF8-80FA-359A-B2F3-A3BAEFB288FD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735024504832,"image_vmaddr":140734923157504,"image_size":266240,"name":"/usr/lib/system/libdispatch.dylib","uuid":"CD9C059C-91D9-30E8-8926-5B9CD0D5D4F5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735024771072,"image_vmaddr":140734923423744,"image_size":225280,"name":"/usr/lib/system/libdyld.dylib","uuid":"789A18C2-8AC7-3C88-813D-CD674376585D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735024996352,"image_vmaddr":140734923649024,"image_size":4096,"name":"/usr/lib/system/libkeymgr.dylib","uuid":"DB3337BE-01CA-3425-BD0C-87774FC0CDC0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025053696,"image_vmaddr":140734923706368,"image_size":4096,"name":"/usr/lib/system/liblaunch.dylib","uuid":"7200E214-9B4D-3B22-9844-4C7892FC890B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025057792,"image_vmaddr":140734923710464,"image_size":24576,"name":"/usr/lib/system/libmacho.dylib","uuid":"AA613A9C-961A-3B67-B696-4622FA59FC4E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025082368,"image_vmaddr":140734923735040,"image_size":12288,"name":"/usr/lib/system/libquarantine.dylib","uuid":"F234E51D-FD0B-3EE4-B679-AE3EE9C536C3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025094656,"image_vmaddr":140734923747328,"image_size":8192,"name":"/usr/lib/system/libremovefile.dylib","uuid":"7C7EFC79-BD24-33EF-B073-06AED234593E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025102848,"image_vmaddr":140734923755520,"image_size":98304,"name":"/usr/lib/system/libsystem_asl.dylib","uuid":"1563EE02-0657-3B78-99BE-A947C24122EF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025201152,"image_vmaddr":140734923853824,"image_size":4096,"name":"/usr/lib/system/libsystem_blocks.dylib","uuid":"0D53847E-AF5F-3ACF-B51F-A15DEA4DEC58","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025205248,"image_vmaddr":140734923857920,"image_size":557056,"name":"/usr/lib/system/libsystem_c.dylib","uuid":"BBDED5E6-A646-3EED-B33A-91E4331EA063","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025762304,"image_vmaddr":140734924414976,"image_size":16384,"name":"/usr/lib/system/libsystem_configuration.dylib","uuid":"0EE84C33-64FD-372B-974A-AF7A136F2068","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025778688,"image_vmaddr":140734924431360,"image_size":16384,"name":"/usr/lib/system/libsystem_coreservices.dylib","uuid":"A199156E-058D-3ABB-BCE9-4B9F20DCED0F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025795072,"image_vmaddr":140734924447744,"image_size":36864,"name":"/usr/lib/system/libsystem_darwin.dylib","uuid":"5B12B5DB-3F30-37C1-8ECC-49A66B1F2864","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025831936,"image_vmaddr":140734924484608,"image_size":32768,"name":"/usr/lib/system/libsystem_dnssd.dylib","uuid":"EBB4C2C2-E031-3094-B40A-E67BF261D295","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025864704,"image_vmaddr":140734924517376,"image_size":8192,"name":"/usr/lib/system/libsystem_featureflags.dylib","uuid":"29FD922A-EC2C-3F25-BCCC-B58D716E60EC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025872896,"image_vmaddr":140734924525568,"image_size":319488,"name":"/usr/lib/system/libsystem_info.dylib","uuid":"8A321605-5480-330B-AF9E-64E65DE61747","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735026376704,"image_vmaddr":140734925029376,"image_size":294912,"name":"/usr/lib/system/libsystem_m.dylib","uuid":"00F331F1-0D09-39B3-8736-1FE90E64E903","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140735026671616,"image_vmaddr":140734925324288,"image_size":163840,"name":"/usr/lib/system/libsystem_malloc.dylib","uuid":"8549294E-4C53-36EB-99F3-584A7393D8D5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735026835456,"image_vmaddr":140734925488128,"image_size":57344,"name":"/usr/lib/system/libsystem_networkextension.dylib","uuid":"F06C65C5-2CBE-313C-96E1-A09240F9FE57","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735026892800,"image_vmaddr":140734925545472,"image_size":40960,"name":"/usr/lib/system/libsystem_notify.dylib","uuid":"FA22F928-D91B-3AA5-96BB-3186AC0FB264","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735027015680,"image_vmaddr":140734925668352,"image_size":20480,"name":"/usr/lib/system/libsystem_sandbox.dylib","uuid":"051C4018-4345-3034-AC98-6DE42FB8273B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735027036160,"image_vmaddr":140734925688832,"image_size":12288,"name":"/usr/lib/system/libsystem_secinit.dylib","uuid":"F80872AA-E1FD-3D7E-8729-467656EC6561","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735026192384,"image_vmaddr":140734924845056,"image_size":184320,"name":"/usr/lib/system/libsystem_kernel.dylib","uuid":"FF092EE8-5BEE-3B9A-8749-F0A067115C7E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735026933760,"image_vmaddr":140734925586432,"image_size":36864,"name":"/usr/lib/system/libsystem_platform.dylib","uuid":"009A7C1F-313A-318E-B9F2-30F4C06FEA5C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735026970624,"image_vmaddr":140734925623296,"image_size":45056,"name":"/usr/lib/system/libsystem_pthread.dylib","uuid":"62CB1A98-0B8F-31E7-A02B-A1139927F61D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735027048448,"image_vmaddr":140734925701120,"image_size":32768,"name":"/usr/lib/system/libsystem_symptoms.dylib","uuid":"702D0910-5C34-3D43-9631-8BD215DE4FE1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735027081216,"image_vmaddr":140734925733888,"image_size":94208,"name":"/usr/lib/system/libsystem_trace.dylib","uuid":"BC141783-66D9-3137-A783-211B38E49ADB","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735027179520,"image_vmaddr":140734925832192,"image_size":24576,"name":"/usr/lib/system/libunwind.dylib","uuid":"42B7B509-BAFE-365B-893A-72414C92F5BF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735027204096,"image_vmaddr":140734925856768,"image_size":221184,"name":"/usr/lib/system/libxpc.dylib","uuid":"54EEF402-42C7-3995-BADE-93C48EFC4452","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734977359872,"image_vmaddr":140734876012544,"image_size":69632,"name":"/usr/lib/libbsm.0.dylib","uuid":"00BFFB9A-2FFE-3C24-896A-251BC61917FD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735000924160,"image_vmaddr":140734899576832,"image_size":4706304,"name":"/usr/lib/libnetwork.dylib","uuid":"BA294A54-F309-398D-B308-F97032AFF555","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734988828672,"image_vmaddr":140734887481344,"image_size":12288,"name":"/usr/lib/libfakelink.dylib","uuid":"36146CB2-E6A5-37BB-9EE8-1B4034D8F3AD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734969372672,"image_vmaddr":140734868025344,"image_size":12288,"name":"/usr/lib/libDiagnosticMessagesClient.dylib","uuid":"C94F3B7B-1854-38EB-9778-834501C53B3F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734989950976,"image_vmaddr":140734888603648,"image_size":2457600,"name":"/usr/lib/libicucore.A.dylib","uuid":"8AC2CB07-E7E0-340D-A849-186FA1F27251","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735006601216,"image_vmaddr":140734905253888,"image_size":225280,"name":"/usr/lib/libpcap.A.dylib","uuid":"A76EC076-A8EA-354C-B95F-7AB1EAFBCC65","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734098735104,"image_vmaddr":140733997387776,"image_size":675840,"name":"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit","uuid":"14223387-6F81-3976-8605-4BC2F253A93E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734981074944,"image_vmaddr":140734879727616,"image_size":94208,"name":"/usr/lib/libcoretls.dylib","uuid":"770A5B96-936E-34E3-B006-B1CEC299B5A5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734981169152,"image_vmaddr":140734879821824,"image_size":8192,"name":"/usr/lib/libcoretls_cfhelpers.dylib","uuid":"940BF370-FD0C-30A8-AA05-FF48DA44FA4C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734261653504,"image_vmaddr":140734160306176,"image_size":3448832,"name":"/System/Library/Frameworks/Security.framework/Versions/A/Security","uuid":"B6F8368F-2395-379B-B768-71C53BB1B903","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734975127552,"image_vmaddr":140734873780224,"image_size":94208,"name":"/usr/lib/libapple_nghttp2.dylib","uuid":"07FEC48A-87CF-32A3-8194-FA70B361713A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734988668928,"image_vmaddr":140734887321600,"image_size":4096,"name":"/usr/lib/libenergytrace.dylib","uuid":"162DFCC0-8F48-3DD0-914F-FA8653E27B26","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735025000448,"image_vmaddr":140734923653120,"image_size":53248,"name":"/usr/lib/system/libkxld.dylib","uuid":"11EABEB3-CD17-3787-86BE-B8B21817E89B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734379040768,"image_vmaddr":140734277693440,"image_size":65536,"name":"/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression","uuid":"466ABD77-2E52-36D1-8E39-77AE2CC61611","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735006572544,"image_vmaddr":140734905225216,"image_size":20480,"name":"/usr/lib/libpam.2.dylib","uuid":"0502F395-8EE6-3D2A-9239-06FD5622E19E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735007838208,"image_vmaddr":140734906490880,"image_size":2011136,"name":"/usr/lib/libsqlite3.dylib","uuid":"35A2BD9F-4E33-30DE-A994-4AB585AC3AFE","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140735012290560,"image_vmaddr":140734910943232,"image_size":57344,"name":"/usr/lib/libxar.1.dylib","uuid":"EE964412-9E25-30B3-BCC0-CCEFBCC8094B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734976548864,"image_vmaddr":140734875201536,"image_size":4096,"name":"/usr/lib/libauto.dylib","uuid":"B6124448-7690-34AE-8939-ED84AAC630CE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734977990656,"image_vmaddr":140734876643328,"image_size":98304,"name":"/usr/lib/libcompression.dylib","uuid":"64C91066-586D-38C0-A2F3-3E60A940F859","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734087749632,"image_vmaddr":140733986402304,"image_size":28672,"name":"/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration","uuid":"0BBBB6A6-604D-368B-9943-50B8CE75D51D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734975434752,"image_vmaddr":140734874087424,"image_size":471040,"name":"/usr/lib/libarchive.2.dylib","uuid":"F9EA2883-5D5C-3FA6-B3F8-C5031BF88ABD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735012368384,"image_vmaddr":140734911021056,"image_size":929792,"name":"/usr/lib/libxml2.2.dylib","uuid":"20567881-7204-3C11-9179-50FE34B3301B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734071414784,"image_vmaddr":140733970067456,"image_size":4096,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices","uuid":"2D1A6C60-DD34-368F-88FF-E39C9FE549A9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734992502784,"image_vmaddr":140734891155456,"image_size":8192,"name":"/usr/lib/liblangid.dylib","uuid":"30CFC08C-EF36-3CF5-8AEA-C1CB070306B7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734968532992,"image_vmaddr":140734867185664,"image_size":217088,"name":"/usr/lib/libCRFSuite.dylib","uuid":"5E5DE3CB-30DD-34DC-AEF8-FE8536A85E96","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734992510976,"image_vmaddr":140734891163648,"image_size":102400,"name":"/usr/lib/liblzma.5.dylib","uuid":"C131EF18-2CDD-3271-8A30-A8760D4FE166","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734977429504,"image_vmaddr":140734876082176,"image_size":53248,"name":"/usr/lib/libbz2.1.0.dylib","uuid":"14CC4988-B6D4-3879-AFC2-9A0DDC6388DE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734988963840,"image_vmaddr":140734887616512,"image_size":987136,"name":"/usr/lib/libiconv.2.dylib","uuid":"18311A67-E4EF-3CC7-95B3-C0EDEE3A282F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734977912832,"image_vmaddr":140734876565504,"image_size":4096,"name":"/usr/lib/libcharset.1.dylib","uuid":"72447768-9244-39AB-8E79-2FA14EC0AD33","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734075310080,"image_vmaddr":140733973962752,"image_size":36864,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents","uuid":"FC84DB48-A3CE-30F7-A918-B3587731ACC7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734071967744,"image_vmaddr":140733970620416,"image_size":3022848,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore","uuid":"BE379206-99FA-30CD-8391-2708473A633F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734077685760,"image_vmaddr":140733976338432,"image_size":626688,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata","uuid":"0973F7E5-D58C-3574-A3CE-4F12CAC2D4C7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734078312448,"image_vmaddr":140733976965120,"image_size":188416,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices","uuid":"0E4F48AD-402C-3E9D-9CA9-6DD9479B28F9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734078500864,"image_vmaddr":140733977153536,"image_size":425984,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit","uuid":"2C5E1D85-E8B1-3DC5-91B9-E3EDB48E9369","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734071418880,"image_vmaddr":140733970071552,"image_size":548864,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE","uuid":"2E5FD5AE-8A7F-353F-9BD1-0241F3586181","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734075346944,"image_vmaddr":140733973999616,"image_size":2338816,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices","uuid":"9A5359D9-9148-3B18-B868-56A9DA5FB60C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734074990592,"image_vmaddr":140733973643264,"image_size":319488,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices","uuid":"26B70C82-25BC-353A-858F-945B14C803A2","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734078926848,"image_vmaddr":140733977579520,"image_size":151552,"name":"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList","uuid":"02DE0D56-E371-3EF5-9BC1-FA435451B412","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734161301504,"image_vmaddr":140734059954176,"image_size":53248,"name":"/System/Library/Frameworks/NetFS.framework/Versions/A/NetFS","uuid":"4415F027-D36D-3B9C-96BA-AD22B44A4722","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734712025088,"image_vmaddr":140734610677760,"image_size":45056,"name":"/System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth","uuid":"B0C03C41-87A3-352B-B130-96E1A6F94B47","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734923313152,"image_vmaddr":140734821965824,"image_size":12288,"name":"/System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport","uuid":"12F77885-27DC-3837-9CE9-A25EBA75F833","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734865690624,"image_vmaddr":140734764343296,"image_size":69632,"name":"/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC","uuid":"C9EB52E3-099B-3597-9623-067C403FA525","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734458937344,"image_vmaddr":140734357590016,"image_size":438272,"name":"/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP","uuid":"E70E2505-8078-324E-BAE1-01A2DA980E2C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734700208128,"image_vmaddr":140734598860800,"image_size":315392,"name":"/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities","uuid":"0237323B-EC78-3FBF-9FC7-5A1FE2B5CE25","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734993395712,"image_vmaddr":140734892048384,"image_size":2502656,"name":"/usr/lib/libmecabra.dylib","uuid":"E31DE74D-1B88-377F-ACD3-D789D29C3AE7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733979791360,"image_vmaddr":140733878444032,"image_size":4096,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate","uuid":"4F9977AE-DBDB-3A16-A536-AC1F9938DCDD","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734992707584,"image_vmaddr":140734891360256,"image_size":688128,"name":"/usr/lib/libmecab.dylib","uuid":"0D5BFD01-D4A7-3C8D-AA69-C329C1A69792","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734988898304,"image_vmaddr":140734887550976,"image_size":24576,"name":"/usr/lib/libgermantok.dylib","uuid":"D2AE5AC0-EDCE-3216-B8C9-CF59292A545F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734975025152,"image_vmaddr":140734873677824,"image_size":8192,"name":"/usr/lib/libThaiTokenizer.dylib","uuid":"4F4ADE99-0D09-3223-B7C0-C407AB6DE8DC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734968758272,"image_vmaddr":140734867410944,"image_size":45056,"name":"/usr/lib/libChineseTokenizer.dylib","uuid":"7F0DA183-1796-315A-B44A-2C234C7C50BE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733979889664,"image_vmaddr":140733878542336,"image_size":6647808,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage","uuid":"EA6F5FF2-7A1B-35D5-A5A3-D2B3386ECB75","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734001295360,"image_vmaddr":140733899948032,"image_size":4096,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib","uuid":"F6C5613D-2284-342B-9160-9731F78B4DE5","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734000496640,"image_vmaddr":140733899149312,"image_size":798720,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib","uuid":"3601FDE3-B142-398D-987D-8151A51F0A96","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733998563328,"image_vmaddr":140733897216000,"image_size":1933312,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib","uuid":"D63DC0A5-B8B4-3562-A574-E73BC3B57407","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733986537472,"image_vmaddr":140733885190144,"image_size":2523136,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib","uuid":"C6C2D42F-7456-3DBF-8BE2-9AA06EFC78FD","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733994123264,"image_vmaddr":140733892775936,"image_size":3784704,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib","uuid":"5E3E3867-50C3-3E6A-9A2E-007CE77A4641","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733997907968,"image_vmaddr":140733896560640,"image_size":90112,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib","uuid":"3D433800-0099-33E0-8C81-15F83247B2C9","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733998485504,"image_vmaddr":140733897138176,"image_size":77824,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib","uuid":"B147FEF6-A0DB-3830-BF06-45BEC58DB576","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733997998080,"image_vmaddr":140733896650752,"image_size":24576,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib","uuid":"371F36A7-B12F-363E-8955-F24F7C2048F6","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733989060608,"image_vmaddr":140733887713280,"image_size":5062656,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib","uuid":"99C61C48-B14C-3DA6-8C31-6BF72DA0A3A9","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733998022656,"image_vmaddr":140733896675328,"image_size":462848,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib","uuid":"B8A10D0C-4577-343D-B310-A3E81265D107","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734678663168,"image_vmaddr":140734577315840,"image_size":847872,"name":"/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling","uuid":"C456087D-5B3A-390E-A665-862FA284C59C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734452318208,"image_vmaddr":140734350970880,"image_size":69632,"name":"/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji","uuid":"7C2B3259-083B-31B8-BCDB-1BA360529936","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734679834624,"image_vmaddr":140734578487296,"image_size":24576,"name":"/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData","uuid":"3B92F249-4602-325F-984B-D2DE61EEE4E1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734679511040,"image_vmaddr":140734578163712,"image_size":299008,"name":"/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon","uuid":"41F208B9-8255-3EC7-9673-FE0925D071D3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734977916928,"image_vmaddr":140734876569600,"image_size":73728,"name":"/usr/lib/libcmph.dylib","uuid":"E72A20DB-2E86-378D-A237-EB9A1370F989","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734207369216,"image_vmaddr":140734106021888,"image_size":118784,"name":"/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory","uuid":"7E6C88EB-3DD9-32B0-81FC-179552834FA9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734207488000,"image_vmaddr":140734106140672,"image_size":49152,"name":"/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory","uuid":"4A92D8D8-A9E5-3A9C-942F-28576F6BCDF5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734346174464,"image_vmaddr":140734244827136,"image_size":811008,"name":"/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS","uuid":"C86A3423-E61C-335A-9D17-0B3CE5BB6467","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734265102336,"image_vmaddr":140734163755008,"image_size":561152,"name":"/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation","uuid":"7C69DF47-4017-3DF2-B55B-712B481654CB","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735012274176,"image_vmaddr":140734910926848,"image_size":16384,"name":"/usr/lib/libutil.dylib","uuid":"F01467F6-23A7-37EE-A170-33CE1577B41D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734474555392,"image_vmaddr":140734373208064,"image_size":192512,"name":"/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore","uuid":"C2D67667-FA0B-3DB6-AA34-6999EE4346A0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734265851904,"image_vmaddr":140734164504576,"image_size":20480,"name":"/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement","uuid":"9B36FD0E-D280-35EB-ABAB-6AC5D7B2118F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734404009984,"image_vmaddr":140734302662656,"image_size":40960,"name":"/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement","uuid":"A6877DAD-8F47-363C-983A-DC8DDE83B7B5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735013310464,"image_vmaddr":140734911963136,"image_size":167936,"name":"/usr/lib/libxslt.1.dylib","uuid":"34A45627-DA5B-37D2-9609-65B425E0010A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734873862144,"image_vmaddr":140734772514816,"image_size":2461696,"name":"/System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation","uuid":"EC55B9E5-7E62-380A-9AB1-FC7BEF663653","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734790582272,"image_vmaddr":140734689234944,"image_size":172032,"name":"/System/Library/PrivateFrameworks/RemoteViewServices.framework/Versions/A/RemoteViewServices","uuid":"D3AAC2BE-3423-3F18-9654-E35F1DD8DDB3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734919327744,"image_vmaddr":140734817980416,"image_size":16384,"name":"/System/Library/PrivateFrameworks/XCTTargetBootstrap.framework/Versions/A/XCTTargetBootstrap","uuid":"D459D628-58C5-39A6-B7E8-B691CBEECEC1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734049284096,"image_vmaddr":140733947936768,"image_size":1257472,"name":"/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay","uuid":"213D7011-8180-3CF4-9BE7-FB8F75DCDB95","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734140157952,"image_vmaddr":140734038810624,"image_size":831488,"name":"/System/Library/Frameworks/Metal.framework/Versions/A/Metal","uuid":"98C944D6-62C8-355E-90F8-C1CA2429EF24","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734488264704,"image_vmaddr":140734386917376,"image_size":1609728,"name":"/System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv","uuid":"C2355E6B-C5F7-3E39-B778-B117BD4652C6","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734474194944,"image_vmaddr":140734372847616,"image_size":147456,"name":"/System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG","uuid":"F38189F9-C8F9-3D62-9D5F-3F520FB81724","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734842339328,"image_vmaddr":140734740992000,"image_size":3108864,"name":"/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight","uuid":"A24929C3-95E6-35A7-9654-46FF3F4D1E80","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734055264256,"image_vmaddr":140733953916928,"image_size":6774784,"name":"/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics","uuid":"4A3CDE7B-4578-3058-966A-3D1DC095A935","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734672433152,"image_vmaddr":140734571085824,"image_size":311296,"name":"/System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices","uuid":"0DADB4C3-46FF-3FDB-8A86-51E2067FC7F4","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734099415040,"image_vmaddr":140733998067712,"image_size":73728,"name":"/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface","uuid":"BCD744D4-E17E-3C2E-B69C-F69C789892E9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734017785856,"image_vmaddr":140733916438528,"image_size":4096,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices","uuid":"20CF59CD-A395-3570-A4DB-589D55C70B3F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734486536192,"image_vmaddr":140734385188864,"image_size":77824,"name":"/System/Library/PrivateFrameworks/DFRFoundation.framework/Versions/A/DFRFoundation","uuid":"8162057E-E856-3451-9160-04AEDDECFFA4","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734021136384,"image_vmaddr":140733919789056,"image_size":925696,"name":"/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox","uuid":"6185BF20-D2E7-3812-8FD8-EC5237672104","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734022066176,"image_vmaddr":140733920718848,"image_size":4096,"name":"/System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit","uuid":"918ED3EB-0232-31F3-9BF5-18D1CB11523F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734045175808,"image_vmaddr":140733943828480,"image_size":4108288,"name":"/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData","uuid":"49AE61CA-C91E-31FE-9BD0-1AACFFB5181E","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734487199744,"image_vmaddr":140734385852416,"image_size":503808,"name":"/System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore","uuid":"B1534796-1000-3520-A641-A97A4AC5D39B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734030090240,"image_vmaddr":140733928742912,"image_size":3100672,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox","uuid":"EAF2DAC3-66B1-30BF-AF10-72DDA90D1044","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734246948864,"image_vmaddr":140734145601536,"image_size":2637824,"name":"/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore","uuid":"FE927B0E-BD49-32CC-8A55-90F553C86C15","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033494016,"image_vmaddr":140733932146688,"image_size":28672,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SpeechRecognition.framework/Versions/A/SpeechRecognition","uuid":"9614A01E-8303-3422-A3BA-6CE27540E09A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734481375232,"image_vmaddr":140734380027904,"image_size":1232896,"name":"/System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI","uuid":"788818B7-7EBC-316D-9464-D12E365E3791","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734039322624,"image_vmaddr":140733937975296,"image_size":5283840,"name":"/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio","uuid":"47923B12-3D14-328A-9C86-27A3A2A73068","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734706585600,"image_vmaddr":140734605238272,"image_size":200704,"name":"/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport","uuid":"6794E1C8-9627-33DF-84F4-FDD02C97F383","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734741901312,"image_vmaddr":140734640553984,"image_size":49152,"name":"/System/Library/PrivateFrameworks/PerformanceAnalysis.framework/Versions/A/PerformanceAnalysis","uuid":"B47C00E5-ECC2-313D-93D4-DBDF562C48EF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734230380544,"image_vmaddr":140734129033216,"image_size":65536,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL","uuid":"D3C57A32-6BD0-3228-B1C4-0F42A6128A6C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734035255296,"image_vmaddr":140733933907968,"image_size":1011712,"name":"/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync","uuid":"A126406C-DA38-3FFE-8B25-BB9859EFD159","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734062092288,"image_vmaddr":140733960744960,"image_size":3522560,"name":"/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage","uuid":"69361069-01AB-342E-862B-73A74271A765","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734082371584,"image_vmaddr":140733981024256,"image_size":1802240,"name":"/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText","uuid":"ADDE38C3-C247-34AD-9903-0276050E242C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734100004864,"image_vmaddr":140733998657536,"image_size":1433600,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO","uuid":"6FC6EE76-F76F-312C-9C67-4B9F0E0B832A","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734972796928,"image_vmaddr":140734871449600,"image_size":135168,"name":"/usr/lib/libMobileGestalt.dylib","uuid":"2BE94E6A-FA61-312F-84A1-F764D71B7E39","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734871150592,"image_vmaddr":140734769803264,"image_size":815104,"name":"/System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO","uuid":"EEDAB753-329A-396A-8119-5BEDF7DB5A56","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734975905792,"image_vmaddr":140734874558464,"image_size":630784,"name":"/usr/lib/libate.dylib","uuid":"76EA60FB-748C-313F-8951-B076540BEA97","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734674567168,"image_vmaddr":140734573219840,"image_size":32768,"name":"/System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport","uuid":"8D8D4A7D-FD35-36B8-A456-7C93030EDAB3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734385803264,"image_vmaddr":140734284455936,"image_size":16384,"name":"/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo","uuid":"67255151-F989-39F0-BC87-0C0BDAE70730","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734102216704,"image_vmaddr":140734000869376,"image_size":147456,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib","uuid":"37FA33BD-1C78-3FA6-8D35-3F3755462597","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734105088000,"image_vmaddr":140734003740672,"image_size":290816,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib","uuid":"1125DCD0-337D-3E01-84F9-106DEEEA5715","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734104969216,"image_vmaddr":140734003621888,"image_size":110592,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib","uuid":"1AD4B940-5B1F-39A4-873C-9C568AA287B7","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734101438464,"image_vmaddr":140734000091136,"image_size":16384,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib","uuid":"F57785E7-6FDA-30A1-B926-CD4D727B56DC","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734101454848,"image_vmaddr":140734000107520,"image_size":761856,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib","uuid":"A6CC541D-37C4-3A90-86B0-2F67B917B4EE","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734105079808,"image_vmaddr":140734003732480,"image_size":8192,"name":"/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib","uuid":"868D2C2C-1909-31B1-A0C2-C971F0BE1EC5","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734988673024,"image_vmaddr":140734887325696,"image_size":102400,"name":"/usr/lib/libexpat.1.dylib","uuid":"FED7C38B-92D8-342D-AED7-871B12D1F7E7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734380462080,"image_vmaddr":140734279114752,"image_size":299008,"name":"/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG","uuid":"6DE30A07-C627-319B-A0DE-EB7A832BEB88","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734528122880,"image_vmaddr":140734426775552,"image_size":1277952,"name":"/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib","uuid":"D93AD0C8-4657-3A3B-87EB-3DF3D522C70C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734906544128,"image_vmaddr":140734805196800,"image_size":8192,"name":"/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient","uuid":"FFA17DA1-F6DD-34D3-A708-1F73C8BA7EA7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734668550144,"image_vmaddr":140734567202816,"image_size":36864,"name":"/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator","uuid":"06E3E70B-C0D0-39A2-96B7-12ED6A0EBEE7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734143938560,"image_vmaddr":140734042591232,"image_size":8192,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders","uuid":"F2921F9A-3041-3495-878D-64134267B847","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734609371136,"image_vmaddr":140734508023808,"image_size":28672,"name":"/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler","uuid":"FA34760B-4E5C-3A18-A6C3-DBA68F9D1220","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734668636160,"image_vmaddr":140734567288832,"image_size":98304,"name":"/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment","uuid":"32F1B3BC-4644-3982-AAB2-8EB5D5FF0161","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734486614016,"image_vmaddr":140734385266688,"image_size":24576,"name":"/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay","uuid":"31ECB5FD-7660-33DB-BC5B-2B2A2AA7C686","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734217400320,"image_vmaddr":140734116052992,"image_size":24576,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib","uuid":"E9A20E72-B17F-33D6-8894-41934A10B822","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734141104128,"image_vmaddr":140734039756800,"image_size":253952,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSCore.framework/Versions/A/MPSCore","uuid":"7EBAC15D-7837-395D-B405-1E29F7DA68FA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734141358080,"image_vmaddr":140734040010752,"image_size":569344,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSImage.framework/Versions/A/MPSImage","uuid":"B424FE0C-6E90-3BFA-A6E7-DD86C735AE90","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734142173184,"image_vmaddr":140734040825856,"image_size":1437696,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork","uuid":"05612E06-50CB-318F-9F8E-EF4D49FAB3B0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734141927424,"image_vmaddr":140734040580096,"image_size":155648,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix","uuid":"02006D92-E2AB-3892-A96B-37F6520C19BA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734143610880,"image_vmaddr":140734042263552,"image_size":327680,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector","uuid":"B0B591F8-6875-351E-867F-8EB3CD38CD52","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734142083072,"image_vmaddr":140734040735744,"image_size":90112,"name":"/System/Library/Frameworks/MetalPerformanceShaders.framework/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray","uuid":"CAA5A368-DB71-34F6-AEF9-27A8BC298F53","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734700523520,"image_vmaddr":140734599176192,"image_size":860160,"name":"/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools","uuid":"99876E08-37D7-3828-8796-56D90C9AFBDB","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734364893184,"image_vmaddr":140734263545856,"image_size":8192,"name":"/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary","uuid":"95A291F5-B69F-3C37-9483-C3B2EBF52AC1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734446321664,"image_vmaddr":140734344974336,"image_size":139264,"name":"/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics","uuid":"2162671C-D49D-3891-AB69-9EEB1FA47DD8","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734384881664,"image_vmaddr":140734283534336,"image_size":143360,"name":"/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce","uuid":"68E0364C-AEA7-3654-A030-136BF3CD92F3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734971625472,"image_vmaddr":140734870278144,"image_size":28672,"name":"/usr/lib/libIOReport.dylib","uuid":"75D177C4-BAD7-3285-B8E1-3019F49B3178","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734084173824,"image_vmaddr":140733982826496,"image_size":282624,"name":"/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo","uuid":"5314E70D-325F-3E98-99FC-00FDF520747E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734630838272,"image_vmaddr":140734529490944,"image_size":61440,"name":"/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer","uuid":"507D5812-9CB4-3C94-938C-59ED2B370818","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734516875264,"image_vmaddr":140734415527936,"image_size":4308992,"name":"/System/Library/PrivateFrameworks/FaceCore.framework/Versions/A/FaceCore","uuid":"5D32F65D-2CD7-3204-975C-F4C9256E505F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734207004672,"image_vmaddr":140734105657344,"image_size":364544,"name":"/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL","uuid":"293FE223-9186-320B-81A4-EC8104C38357","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734969667584,"image_vmaddr":140734868320256,"image_size":1802240,"name":"/usr/lib/libFosl_dynamic.dylib","uuid":"737573B2-190A-3BA1-8220-807AD0A2CE5E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734722600960,"image_vmaddr":140734621253632,"image_size":311296,"name":"/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG","uuid":"AD1D4601-05DE-37A7-AD7E-FCF46BF9981D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734018854912,"image_vmaddr":140733917507584,"image_size":258048,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib","uuid":"F3461C05-0370-359B-9F03-5C1C1F7763EC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734530023424,"image_vmaddr":140734428676096,"image_size":69632,"name":"/System/Library/PrivateFrameworks/FontServices.framework/libhvf.dylib","uuid":"C4B38245-337D-3553-9669-75E5D90071AE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734217445376,"image_vmaddr":140734116098048,"image_size":36864,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib","uuid":"AB47B927-65E3-3924-88BE-0A5BE7906785","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734219403264,"image_vmaddr":140734118055936,"image_size":249856,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib","uuid":"D8B4D804-7323-30BC-871C-B7236FFC2FE3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734217482240,"image_vmaddr":140734116134912,"image_size":45056,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib","uuid":"FB5E6A75-398E-38EF-8CB2-59F5BFE3034C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734217527296,"image_vmaddr":140734116179968,"image_size":217088,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib","uuid":"9A3FE633-61B8-3CC7-8183-62960109401A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734217388032,"image_vmaddr":140734116040704,"image_size":12288,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib","uuid":"2B6C3C16-3F5F-36A8-8070-2A862B90328B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734217424896,"image_vmaddr":140734116077568,"image_size":20480,"name":"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib","uuid":"018A48BA-1326-3847-8FB5-A7C99322EB87","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734999486464,"image_vmaddr":140734898139136,"image_size":196608,"name":"/usr/lib/libncurses.5.4.dylib","uuid":"995DFEEA-40F3-377F-B73D-D02AC59D591F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734017789952,"image_vmaddr":140733916442624,"image_size":442368,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS","uuid":"87EA5DE1-506A-39FD-88BE-D8A3416C9012","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734019678208,"image_vmaddr":140733918330880,"image_size":20480,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy","uuid":"72EE68DB-F069-37F5-AA2A-40D5FCF139F4","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734020325376,"image_vmaddr":140733918978048,"image_size":360448,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices","uuid":"14DF4D42-E24D-3EBD-9A9D-93124D8D6AA1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734020685824,"image_vmaddr":140733919338496,"image_size":61440,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LangAnalysis.framework/Versions/A/LangAnalysis","uuid":"01B8B6B3-E2C3-3607-B34A-8283A7E0E924","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734020747264,"image_vmaddr":140733919399936,"image_size":286720,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore","uuid":"437BCF12-48D2-3770-8BC9-567718FB1BCA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734021033984,"image_vmaddr":140733919686656,"image_size":45056,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD","uuid":"27A36D07-B5E9-32E6-87B6-3127F260F48D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734021079040,"image_vmaddr":140733919731712,"image_size":57344,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis","uuid":"75344F8F-32CA-3558-B4E6-F56D498250E4","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734019481600,"image_vmaddr":140733918134272,"image_size":196608,"name":"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI","uuid":"5F513967-DDD7-3F22-AD14-8A38ABD9F2D0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734987190272,"image_vmaddr":140734885842944,"image_size":393216,"name":"/usr/lib/libcups.2.dylib","uuid":"821ED293-B7B3-3B37-88C9-7B6CA91DA7FA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734127796224,"image_vmaddr":140734026448896,"image_size":77824,"name":"/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos","uuid":"03BB492B-016E-37BF-B020-39C2CF7487FE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734095560704,"image_vmaddr":140733994213376,"image_size":331776,"name":"/System/Library/Frameworks/GSS.framework/Versions/A/GSS","uuid":"2F3A67E6-D42A-3CF0-9041-A42C22D46F95","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735007363072,"image_vmaddr":140734906015744,"image_size":102400,"name":"/usr/lib/libresolv.9.dylib","uuid":"C57EDFEF-D36A-310B-8D14-8C68A625B1E8","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734632595456,"image_vmaddr":140734531248128,"image_size":782336,"name":"/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal","uuid":"B86FE9DB-71BB-3B6E-A4AE-2B0B44570A7F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734127874048,"image_vmaddr":140734026526720,"image_size":4096,"name":"/System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib","uuid":"0A2905EE-9533-3345-AF9B-AAC71513BDFD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734988922880,"image_vmaddr":140734887575552,"image_size":40960,"name":"/usr/lib/libheimdal-asn1.dylib","uuid":"0AC6FB62-2B0F-3E93-A931-E4DC4B1D757A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734434361344,"image_vmaddr":140734333014016,"image_size":69632,"name":"/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth","uuid":"CF67FF34-4238-3ECA-B4A4-EA04F18A0D36","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734386446336,"image_vmaddr":140734285099008,"image_size":65536,"name":"/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices","uuid":"48AD21CA-B81D-380E-A04F-90C48FDA5203","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734398648320,"image_vmaddr":140734297300992,"image_size":2363392,"name":"/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore","uuid":"9DB96FE2-AE60-32AE-B706-546E25BAE53F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734919839744,"image_vmaddr":140734818492416,"image_size":61440,"name":"/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk","uuid":"06D695EA-E2BC-31E4-9816-9C12542BA744","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734404714496,"image_vmaddr":140734303367168,"image_size":577536,"name":"/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard","uuid":"10D0F3BB-E8F3-365E-8528-6AC996A9B0E7","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734792212480,"image_vmaddr":140734690865152,"image_size":245760,"name":"/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices","uuid":"96BB04BD-D6E0-3D70-8F36-89B46DA1DA30","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734741950464,"image_vmaddr":140734640603136,"image_size":167936,"name":"/System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection","uuid":"5B2D87A8-2641-3F6D-ACEA-96B00F85AAB5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734785892352,"image_vmaddr":140734684545024,"image_size":106496,"name":"/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer","uuid":"5A020941-C43C-303E-8DE8-230FC6A84DBC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734434508800,"image_vmaddr":140734333161472,"image_size":98304,"name":"/System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities","uuid":"F4C97244-E5D8-3F7D-8D94-4B6841C5A4EC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734406344704,"image_vmaddr":140734304997376,"image_size":249856,"name":"/System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom","uuid":"79CBE5E7-054F-377B-9566-A86A9F120CF1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734968295424,"image_vmaddr":140734866948096,"image_size":212992,"name":"/usr/lib/libAudioToolboxUtility.dylib","uuid":"E534364D-5E5F-37BA-9FB6-FFFF1416820C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734404050944,"image_vmaddr":140734302703616,"image_size":663552,"name":"/System/Library/PrivateFrameworks/Backup.framework/Versions/A/Backup","uuid":"C9DC35FA-E69F-3C64-9519-494126096337","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734485700608,"image_vmaddr":140734384353280,"image_size":81920,"name":"/System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport","uuid":"ADF138F0-0274-3BA2-A1D2-48B91577FE53","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734820073472,"image_vmaddr":140734718726144,"image_size":1212416,"name":"/System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing","uuid":"CBDA0ADD-F1E7-3B06-9118-C5E183F0D3D6","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734376017920,"image_vmaddr":140734274670592,"image_size":151552,"name":"/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211","uuid":"D94E03E8-4C38-3B2F-8DF4-473ACC5A7D71","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734401024000,"image_vmaddr":140734299676672,"image_size":1167360,"name":"/System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit","uuid":"DC1A27C5-0172-3C72-9B24-06996D0B6207","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734482608128,"image_vmaddr":140734381260800,"image_size":1810432,"name":"/System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils","uuid":"A74A1C65-6695-3F57-B703-0DEDE13E66C1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734084456448,"image_vmaddr":140733983109120,"image_size":581632,"name":"/System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN","uuid":"6223BFD5-D451-3DE9-90F6-F609AC0B0027","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734097186816,"image_vmaddr":140733995839488,"image_size":1134592,"name":"/System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth","uuid":"C7AF719C-51B4-3727-A947-EB01204272D9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734703960064,"image_vmaddr":140734602612736,"image_size":126976,"name":"/System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag","uuid":"D5FA7041-297F-3ADC-8C7A-6EAAAB82EB68","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734463754240,"image_vmaddr":140734362406912,"image_size":36864,"name":"/System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers","uuid":"E4DAD514-0B3B-3E0B-8AEA-39B320FAAF03","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734380146688,"image_vmaddr":140734278799360,"image_size":49152,"name":"/System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport","uuid":"74F6CD9C-27A7-39C7-A7EB-47E60D2517EB","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734161354752,"image_vmaddr":140734060007424,"image_size":1409024,"name":"/System/Library/Frameworks/Network.framework/Versions/A/Network","uuid":"4A0F3B93-4D23-3E74-9A3D-AD19E9C0E59E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734677270528,"image_vmaddr":140734575923200,"image_size":131072,"name":"/System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle","uuid":"6F655A32-F963-3A7E-B475-E460F4AC7D99","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734044942336,"image_vmaddr":140733943595008,"image_size":233472,"name":"/System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth","uuid":"23DBB313-A082-3C08-8E1F-2D31EE4247EF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734854148096,"image_vmaddr":140734752800768,"image_size":61440,"name":"/System/Library/PrivateFrameworks/SpeechRecognitionCore.framework/Versions/A/SpeechRecognitionCore","uuid":"4D6CAC2A-151B-3BBE-BDB7-E2BE72128691","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734036267008,"image_vmaddr":140733934919680,"image_size":987136,"name":"/System/Library/Frameworks/Combine.framework/Versions/A/Combine","uuid":"02C5D48A-E70F-3D68-8555-4211853F9C3B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734474342400,"image_vmaddr":140734372995072,"image_size":212992,"name":"/System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal","uuid":"65F53A22-6B61-382C-AAC2-B2C53F8FFB44","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734089826304,"image_vmaddr":140733988478976,"image_size":1241088,"name":"/System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider","uuid":"E8BB1D4B-05D6-386C-865C-F8C750CEC308","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734001299456,"image_vmaddr":140733899952128,"image_size":393216,"name":"/System/Library/Frameworks/Accounts.framework/Versions/A/Accounts","uuid":"E2438070-30AB-3B89-AE63-1E485B92D108","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734612668416,"image_vmaddr":140734511321088,"image_size":159744,"name":"/System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage","uuid":"54483E50-20BB-3AF8-900F-992320C109B0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734480138240,"image_vmaddr":140734378790912,"image_size":618496,"name":"/System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication","uuid":"0B3BF87A-7F95-3D79-B4F8-421D6FAC4A6C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734862807040,"image_vmaddr":140734761459712,"image_size":40960,"name":"/System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter","uuid":"C3D66901-7E31-3FDB-BD7A-441EB0520D1D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734370791424,"image_vmaddr":140734269444096,"image_size":122880,"name":"/System/Library/PrivateFrameworks/AppContainer.framework/Versions/A/AppContainer","uuid":"87CEE13C-8585-3EFB-92CD-0852DFF0921B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735007469568,"image_vmaddr":140734906122240,"image_size":282624,"name":"/usr/lib/libsandbox.1.dylib","uuid":"E8BA5E84-66AF-3995-8F8E-DDC93B0A88E1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734889385984,"image_vmaddr":140734788038656,"image_size":135168,"name":"/System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement","uuid":"9F00880E-6EA6-3684-B208-455E14EC07C8","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734418649088,"image_vmaddr":140734317301760,"image_size":327680,"name":"/System/Library/PrivateFrameworks/ChunkingLibrary.framework/Versions/A/ChunkingLibrary","uuid":"5B09C30D-FD2B-3E98-8B64-C5EF470FC13C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734488010752,"image_vmaddr":140734386663424,"image_size":253952,"name":"/System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols","uuid":"040AE30B-CF2C-3798-A289-0929B8CAB10D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734371258368,"image_vmaddr":140734269911040,"image_size":61440,"name":"/System/Library/PrivateFrameworks/AppSandbox.framework/Versions/A/AppSandbox","uuid":"0F49AA04-3400-349A-9096-6D4D7ED61027","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734818541568,"image_vmaddr":140734717194240,"image_size":16384,"name":"/System/Library/PrivateFrameworks/SecCodeWrapper.framework/Versions/A/SecCodeWrapper","uuid":"C4BF691D-A09E-37E8-A6CC-1145B79B8722","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734972575744,"image_vmaddr":140734871228416,"image_size":32768,"name":"/usr/lib/libMatch.1.dylib","uuid":"5C6F3971-9D9E-3630-BDB6-60BFC5A665E0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734903853056,"image_vmaddr":140734802505728,"image_size":966656,"name":"/System/Library/PrivateFrameworks/ViewBridge.framework/Versions/A/ViewBridge","uuid":"09E8FFB8-4DA1-3E80-8E19-927A2D987BF9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":4412448768,"image_vmaddr":0,"image_size":16384,"name":"/usr/lib/libobjc-trampolines.dylib","uuid":"88F9B648-C455-36F8-BBB9-7D1A9F57D073","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140733979795456,"image_vmaddr":140733878448128,"image_size":94208,"name":"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib","uuid":"8FD09D09-BB19-36C5-ADE9-4F22DA235AEE","cpu_type":16777223,"cpu_subtype":8},{"image_addr":140734923325440,"image_vmaddr":140734821978112,"image_size":81920,"name":"/System/Library/PrivateFrameworks/login.framework/Versions/A/login","uuid":"C68367BA-2225-31DD-B2D8-16AC0A44421F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734529400832,"image_vmaddr":140734428053504,"image_size":217088,"name":"/System/Library/PrivateFrameworks/FontServices.framework/libTrueTypeScaler.dylib","uuid":"93039A1A-4946-3166-88B6-82EDD1649C3D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734246944768,"image_vmaddr":140734145597440,"image_size":4096,"name":"/System/Library/Frameworks/Quartz.framework/Versions/A/Quartz","uuid":"48F3D0F9-323E-34E5-8F63-CF541F2B7B0E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734035197952,"image_vmaddr":140733933850624,"image_size":4096,"name":"/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa","uuid":"85C8B9B4-6F0B-3BD5-8934-C299FE481A44","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734240714752,"image_vmaddr":140734139367424,"image_size":4980736,"name":"/System/Library/Frameworks/Quartz.framework/Versions/A/Frameworks/QuartzComposer.framework/Versions/A/QuartzComposer","uuid":"9C10ADF7-94B0-3779-AD44-5AF6394A32AF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734232219648,"image_vmaddr":140734130872320,"image_size":999424,"name":"/System/Library/Frameworks/PDFKit.framework/Versions/A/PDFKit","uuid":"A58B3E4C-6B27-32A7-AC6E-35BCB5A45944","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734245695488,"image_vmaddr":140734144348160,"image_size":155648,"name":"/System/Library/Frameworks/Quartz.framework/Versions/A/Frameworks/QuartzFilters.framework/Versions/A/QuartzFilters","uuid":"876E8B28-9500-34C6-B297-EA404D89C903","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734238326784,"image_vmaddr":140734136979456,"image_size":2387968,"name":"/System/Library/Frameworks/Quartz.framework/Versions/A/Frameworks/ImageKit.framework/Versions/A/ImageKit","uuid":"5F086EE2-5745-3E28-B292-1DE5E0652E36","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734245851136,"image_vmaddr":140734144503808,"image_size":1093632,"name":"/System/Library/Frameworks/Quartz.framework/Versions/A/Frameworks/QuickLookUI.framework/Versions/A/QuickLookUI","uuid":"7128FB8C-83B0-3250-BF86-E8A1772CF1F5","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734110269440,"image_vmaddr":140734008922112,"image_size":17436672,"name":"/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/JavaScriptCore","uuid":"18766B97-AB12-331C-984C-F1C7C9363E8B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734462267392,"image_vmaddr":140734360920064,"image_size":561152,"name":"/System/Library/PrivateFrameworks/CorePDF.framework/Versions/A/CorePDF","uuid":"8F94505C-96C2-3694-BEC7-F3B5581A7AB9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734876938240,"image_vmaddr":140734775590912,"image_size":28672,"name":"/System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting","uuid":"7F99D166-86DC-3F77-A34A-2DA7183D7160","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734249586688,"image_vmaddr":140734148239360,"image_size":368640,"name":"/System/Library/Frameworks/QuickLook.framework/Versions/A/QuickLook","uuid":"959CE934-B541-3172-846F-4D1709353526","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734494375936,"image_vmaddr":140734393028608,"image_size":20480,"name":"/System/Library/PrivateFrameworks/DisplayServices.framework/Versions/A/DisplayServices","uuid":"4D71ADB3-B29D-3D20-B6DE-9E94061F86AC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734099607552,"image_vmaddr":140733998260224,"image_size":397312,"name":"/System/Library/Frameworks/ImageCaptureCore.framework/Versions/A/ImageCaptureCore","uuid":"281CE141-B350-30E2-B345-FC7E7DF1AA3A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733975695360,"image_vmaddr":140733874348032,"image_size":2080768,"name":"/System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation","uuid":"0837D912-3783-35D6-A94A-E474E18600CF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734069547008,"image_vmaddr":140733968199680,"image_size":901120,"name":"/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia","uuid":"A3FF3AFC-8C1C-36E5-9179-66D8F075EE35","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734787133440,"image_vmaddr":140734685786112,"image_size":147456,"name":"/System/Library/PrivateFrameworks/QuickLookSupport.framework/Versions/A/QuickLookSupport","uuid":"AFB0DFCC-6580-30E5-8984-831985F37A2C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734493507584,"image_vmaddr":140734392160256,"image_size":868352,"name":"/System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement","uuid":"640DBACE-B6EC-3C72-9F73-F484E891534E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734249955328,"image_vmaddr":140734148608000,"image_size":221184,"name":"/System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing","uuid":"9CCB50D8-AA39-3744-93FB-7B5B65467AB3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733977776128,"image_vmaddr":140733876428800,"image_size":839680,"name":"/System/Library/Frameworks/AVFoundation.framework/Versions/A/Frameworks/AVFAudio.framework/Versions/A/AVFAudio","uuid":"CAC3CA5F-FCF6-37EB-8F1A-090340E2C38E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734691409920,"image_vmaddr":140734590062592,"image_size":569344,"name":"/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience","uuid":"0203AF27-AB5E-32FA-B529-AB7F29EEB887","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734132494336,"image_vmaddr":140734031147008,"image_size":7659520,"name":"/System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox","uuid":"3A848992-9182-382A-BF7D-5CB9707BE27B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734282276864,"image_vmaddr":140734180929536,"image_size":3686400,"name":"/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox","uuid":"6CF18E28-A7A8-3952-8171-7E4FF4FB37FA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734070448128,"image_vmaddr":140733969100800,"image_size":405504,"name":"/System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO","uuid":"6BAFCF2A-9431-39C9-86EE-DC56F861B209","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734968242176,"image_vmaddr":140734866894848,"image_size":53248,"name":"/usr/lib/libAudioStatistics.dylib","uuid":"AE4C2A3B-A0F7-3E15-9B87-3845BE0550B2","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734923608064,"image_vmaddr":140734822260736,"image_size":53248,"name":"/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata","uuid":"21760CFD-62FF-3466-B3AD-191D02411DA0","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735007084544,"image_vmaddr":140734905737216,"image_size":61440,"name":"/usr/lib/libperfcheck.dylib","uuid":"9D9C4879-8A80-34C4-B0D2-BE341FD6D321","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734973452288,"image_vmaddr":140734872104960,"image_size":909312,"name":"/usr/lib/libSMC.dylib","uuid":"5C9C17F2-1E6F-3A19-A440-86F74D82284F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734392025088,"image_vmaddr":140734290677760,"image_size":4177920,"name":"/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration","uuid":"2BD68521-C19C-3D67-B5EB-DE3E9A4DAF0A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735007830016,"image_vmaddr":140734906482688,"image_size":8192,"name":"/usr/lib/libspindump.dylib","uuid":"AE8C1AE9-5CBC-332F-BBE8-370A2A19FED6","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734445993984,"image_vmaddr":140734344646656,"image_size":188416,"name":"/System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD","uuid":"C3CFDC68-C7D9-3C44-9E7C-801D45575C10","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734131617792,"image_vmaddr":140734030270464,"image_size":45056,"name":"/System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility","uuid":"98065EA1-3484-3A5A-B05C-D2FABED8CEA4","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734688870400,"image_vmaddr":140734587523072,"image_size":16384,"name":"/System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove","uuid":"482F300F-9B70-351F-A4DF-B440EEF7368D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734441971712,"image_vmaddr":140734340624384,"image_size":4022272,"name":"/System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC","uuid":"4341271C-D270-3F9F-8464-31A20D15158D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734385819648,"image_vmaddr":140734284472320,"image_size":331776,"name":"/System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA","uuid":"8E18983B-AF92-3853-8251-A6577A55AC15","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734691979264,"image_vmaddr":140734590631936,"image_size":212992,"name":"/System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit","uuid":"09FEE738-41E4-3A9C-AF1E-1DD00C56339D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734086901760,"image_vmaddr":140733985554432,"image_size":700416,"name":"/System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording","uuid":"BCF3AFB0-6E1A-3F28-A575-1FD2D74E7C19","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734968803328,"image_vmaddr":140734867456000,"image_size":561152,"name":"/usr/lib/libCoreStorage.dylib","uuid":"A457B0FE-D77F-30AA-99A4-70F6A98DFE59","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734987112448,"image_vmaddr":140734885765120,"image_size":49152,"name":"/usr/lib/libcsfde.dylib","uuid":"CEE22AEF-4E77-36B1-B007-00303819965F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734495301632,"image_vmaddr":140734393954304,"image_size":20480,"name":"/System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin","uuid":"3BFE697B-469F-38F4-B380-4A4F4A37C836","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734785519616,"image_vmaddr":140734684172288,"image_size":372736,"name":"/System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage","uuid":"6F271388-3817-336D-9B96-08C7AAC4BA39","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734447542272,"image_vmaddr":140734346194944,"image_size":487424,"name":"/System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness","uuid":"8A07EEDB-E52B-3A2B-9B04-3566CE04B46F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734631325696,"image_vmaddr":140734529978368,"image_size":57344,"name":"/System/Library/PrivateFrameworks/HID.framework/Versions/A/HID","uuid":"70CFCF8B-4419-3309-B44D-E44259D6A892","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734255222784,"image_vmaddr":140734153875456,"image_size":110592,"name":"/System/Library/Frameworks/SafariServices.framework/Versions/A/SafariServices","uuid":"BA680F62-FD6F-3FDB-9A59-BEAD76DD1AEA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734787117056,"image_vmaddr":140734685769728,"image_size":16384,"name":"/System/Library/PrivateFrameworks/QuickLookNonBaseSystem.framework/Versions/A/QuickLookNonBaseSystem","uuid":"8563CD18-DCFE-3868-912F-053FC8C34B9C","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734690709504,"image_vmaddr":140734589362176,"image_size":159744,"name":"/System/Library/PrivateFrameworks/MarkupUI.framework/Versions/A/MarkupUI","uuid":"A596E8D7-6DBD-3F01-89AD-B296C9D3B61E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734821773312,"image_vmaddr":140734720425984,"image_size":135168,"name":"/System/Library/PrivateFrameworks/SidecarCore.framework/Versions/A/SidecarCore","uuid":"469E5222-A5C7-3B09-B617-EDB6E9E46B93","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734369431552,"image_vmaddr":140734268084224,"image_size":1359872,"name":"/System/Library/PrivateFrameworks/AnnotationKit.framework/Versions/A/AnnotationKit","uuid":"9BB0EDB6-3808-30FC-B0ED-764DE5AAB893","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734233219072,"image_vmaddr":140734131871744,"image_size":1146880,"name":"/System/Library/Frameworks/PencilKit.framework/Versions/A/PencilKit","uuid":"95301D00-CE35-3F53-BAE1-6E6217706702","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734821908480,"image_vmaddr":140734720561152,"image_size":77824,"name":"/System/Library/PrivateFrameworks/SidecarUI.framework/Versions/A/SidecarUI","uuid":"5CA517E3-4B92-30B1-96EF-77A7A2FBBEE4","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734454177792,"image_vmaddr":140734352830464,"image_size":1368064,"name":"/System/Library/PrivateFrameworks/CoreHandwriting.framework/Versions/A/CoreHandwriting","uuid":"21DAD964-51A7-3F38-9C91-EF46C0CFF12D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734140993536,"image_vmaddr":140734039646208,"image_size":110592,"name":"/System/Library/Frameworks/MetalKit.framework/Versions/A/MetalKit","uuid":"FAACD940-5CF2-300A-83F3-86ABA7FDC531","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734704918528,"image_vmaddr":140734603571200,"image_size":1667072,"name":"/System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal","uuid":"E7881020-FCA6-3DFC-B0EB-9E539F80E821","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734066388992,"image_vmaddr":140733965041664,"image_size":3158016,"name":"/System/Library/Frameworks/CoreML.framework/Versions/A/CoreML","uuid":"FD17F9EB-1930-314C-B6B4-2A2E643AC771","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734494760960,"image_vmaddr":140734393413632,"image_size":151552,"name":"/System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler","uuid":"188C6793-A94C-3B49-A9F4-AF8A348C7E62","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734507155456,"image_vmaddr":140734405808128,"image_size":6729728,"name":"/System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso","uuid":"70B1521B-1B24-3DA4-A41B-E727CF140F1F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734449696768,"image_vmaddr":140734348349440,"image_size":2146304,"name":"/System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet","uuid":"F899F217-FC31-3140-A0EB-92EAD22EEF71","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734451843072,"image_vmaddr":140734350495744,"image_size":319488,"name":"/System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext","uuid":"72341E86-6921-35FE-89CA-7B04725ECC0F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734770253824,"image_vmaddr":140734668906496,"image_size":81920,"name":"/System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog","uuid":"FAD6E2DC-8C9D-38A0-9CB1-75F48473A3E2","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734081552384,"image_vmaddr":140733980205056,"image_size":819200,"name":"/System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony","uuid":"31D93EB1-0A20-3383-9680-090E441F25D8","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734452240384,"image_vmaddr":140734350893056,"image_size":12288,"name":"/System/Library/PrivateFrameworks/CoreDuetDebugLogging.framework/Versions/A/CoreDuetDebugLogging","uuid":"6D0113DB-61D8-3A21-95CB-9E30D8F929F9","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734384701440,"image_vmaddr":140734283354112,"image_size":159744,"name":"/System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService","uuid":"3ED300B6-43E3-31DC-B3C6-6A0FF41A2595","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033522688,"image_vmaddr":140733932175360,"image_size":1675264,"name":"/System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit","uuid":"1B851180-FC00-357F-B6C1-BB0EA7D6D5CA","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734105477120,"image_vmaddr":140734004129792,"image_size":4784128,"name":"/System/Library/Frameworks/Intents.framework/Versions/A/Intents","uuid":"B2884C7F-0D08-3E3C-91A3-7FB755BB5CB1","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734452162560,"image_vmaddr":140734350815232,"image_size":69632,"name":"/System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol","uuid":"5E31B761-4B30-39A8-9084-97ECFD268B6F","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734787567616,"image_vmaddr":140734686220288,"image_size":499712,"name":"/System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport","uuid":"AF01D899-3BF9-3586-860A-D95A837101DF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734974451712,"image_vmaddr":140734873104384,"image_size":573440,"name":"/usr/lib/libTelephonyUtilDynamic.dylib","uuid":"174030B2-E35F-3F17-A9EF-DF8631F30CCF","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734449647616,"image_vmaddr":140734348300288,"image_size":49152,"name":"/System/Library/PrivateFrameworks/CoreDaemon.framework/Versions/B/CoreDaemon","uuid":"BB7D67B1-2102-3D71-9BB6-AEB8C6A6EBB2","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734384861184,"image_vmaddr":140734283513856,"image_size":20480,"name":"/System/Library/PrivateFrameworks/AppleSRP.framework/Versions/A/AppleSRP","uuid":"70C25EA9-F7A7-366C-97C6-EEE7845FFCC3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734407761920,"image_vmaddr":140734306414592,"image_size":229376,"name":"/System/Library/PrivateFrameworks/C2.framework/Versions/A/C2","uuid":"4A7E2A63-E19A-3936-92F5-03F2FD602172","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734065614848,"image_vmaddr":140733964267520,"image_size":434176,"name":"/System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation","uuid":"75966124-2FB7-33C3-BE49-3DD5F327F911","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734237118464,"image_vmaddr":140734135771136,"image_size":28672,"name":"/System/Library/Frameworks/PushKit.framework/Versions/A/PushKit","uuid":"AD547A25-1A0B-3FA6-8676-82C37F267A4A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734162763776,"image_vmaddr":140734061416448,"image_size":2494464,"name":"/System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension","uuid":"3ED35C5A-B170-373E-8277-D4198E408810","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735005630464,"image_vmaddr":140734904283136,"image_size":98304,"name":"/usr/lib/libnetworkextension.dylib","uuid":"D0E8454C-33A9-3F96-B3A0-EDB12C32283A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734495080448,"image_vmaddr":140734393733120,"image_size":221184,"name":"/System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X","uuid":"D3F76E01-2F9F-33E1-B5C9-CAC6E01724C2","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734612926464,"image_vmaddr":140734511579136,"image_size":16687104,"name":"/System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices","uuid":"F735575F-7DEF-3202-9151-589BEB162596","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734680014848,"image_vmaddr":140734578667520,"image_size":151552,"name":"/System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport","uuid":"CA6C86FD-051A-31BB-B3AF-3D02D6FD94B6","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734769881088,"image_vmaddr":140734668533760,"image_size":200704,"name":"/System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit","uuid":"EFBD7FE7-02CC-3E30-999D-B036F252F805","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734674432000,"image_vmaddr":140734573084672,"image_size":61440,"name":"/System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation","uuid":"1BC7D355-E136-391A-8215-6982742645DD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734030069760,"image_vmaddr":140733928722432,"image_size":4096,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon","uuid":"6A5EB506-7A43-3879-A358-E6B9182C3733","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734030073856,"image_vmaddr":140733928726528,"image_size":16384,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/CommonPanels.framework/Versions/A/CommonPanels","uuid":"9F6E13D9-374B-386F-8E15-FDD6CE967859","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033190912,"image_vmaddr":140733931843584,"image_size":16384,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/Help.framework/Versions/A/Help","uuid":"36483951-6F3E-3F7E-8A5B-191C2357EF17","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033207296,"image_vmaddr":140733931859968,"image_size":24576,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/ImageCapture.framework/Versions/A/ImageCapture","uuid":"1A1F320E-3E85-3F3D-8AE0-B238C4E92D40","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033235968,"image_vmaddr":140733931888640,"image_size":110592,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/OpenScripting.framework/Versions/A/OpenScripting","uuid":"B6E28747-5FC7-3461-8A71-864A969ED022","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033231872,"image_vmaddr":140733931884544,"image_size":4096,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/Ink.framework/Versions/A/Ink","uuid":"284507AE-EF47-3ABC-86A4-669243DB1D33","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033477632,"image_vmaddr":140733932130304,"image_size":4096,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/Print.framework/Versions/A/Print","uuid":"0D9FB08F-EA87-3BE7-821B-C61BA5601050","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734033481728,"image_vmaddr":140733932134400,"image_size":12288,"name":"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SecurityHI.framework/Versions/A/SecurityHI","uuid":"390C6FAA-99BF-3924-9180-9EAE41D9C6BE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733840510976,"image_vmaddr":140733739163648,"image_size":2580480,"name":"/System/Library/Extensions/AMDRadeonX6000MTLDriver.bundle/Contents/MacOS/AMDRadeonX6000MTLDriver","uuid":"F9BD9DB4-B0C3-3FC7-ABF5-8AED9E8CB952","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734668509184,"image_vmaddr":140734567161856,"image_size":40960,"name":"/System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo","uuid":"50DDA9C2-BDDF-33D4-9BA9-A161E99F1EAD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733926809600,"image_vmaddr":140733825462272,"image_size":4194304,"name":"/System/Library/Extensions/AppleIntelKBLGraphicsMTLDriver.bundle/Contents/MacOS/AppleIntelKBLGraphicsMTLDriver","uuid":"1384ABF0-8ED1-3E30-828E-FE067DE1368A","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734585536512,"image_vmaddr":140734484189184,"image_size":8192,"name":"/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/3902/Libraries/libmetal_timestamp.dylib","uuid":"C29C7125-A894-3718-8E1D-249C53BCC0B8","cpu_type":16777223,"cpu_subtype":3},{"image_addr":4432867328,"image_vmaddr":0,"image_size":593920,"name":"/System/Library/Extensions/AppleIntelGraphicsShared.bundle/Contents/MacOS/libISAProfilerDyn.dylib","uuid":"CED825B2-54E3-3195-8D95-0C87B90B9B3D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734355140608,"image_vmaddr":140734253793280,"image_size":36864,"name":"/System/Library/PrivateFrameworks/AccessibilityBundles.framework/Versions/A/AccessibilityBundles","uuid":"C616977E-919B-3211-BC56-3803B3B2702E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734863306752,"image_vmaddr":140734761959424,"image_size":176128,"name":"/System/Library/PrivateFrameworks/SystemAdministration.framework/Versions/A/SystemAdministration","uuid":"087553CB-5BF3-3D3D-B620-CA36AD334D92","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734086852608,"image_vmaddr":140733985505280,"image_size":49152,"name":"/System/Library/Frameworks/DirectoryService.framework/Versions/A/DirectoryService","uuid":"6EACB7D0-A013-3D2B-93D2-5113F7C2559D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734492684288,"image_vmaddr":140734391336960,"image_size":823296,"name":"/System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages","uuid":"47CD30B9-887B-3660-B0EA-E8D8258263BC","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734680526848,"image_vmaddr":140734579179520,"image_size":24576,"name":"/System/Library/PrivateFrameworks/LoginUIKit.framework/Versions/A/Frameworks/LoginUICore.framework/Versions/A/LoginUICore","uuid":"6FE011DE-A794-3D82-9E35-7FE1D62DEE7E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735006498816,"image_vmaddr":140734905151488,"image_size":8192,"name":"/usr/lib/libodfde.dylib","uuid":"33F49412-5106-35B8-AA60-5A497C3967E6","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734987587584,"image_vmaddr":140734886240256,"image_size":417792,"name":"/usr/lib/libcurl.4.dylib","uuid":"28809E22-6EF2-3AC7-9501-D77FB6FC5FBD","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734986035200,"image_vmaddr":140734884687872,"image_size":1069056,"name":"/usr/lib/libcrypto.44.dylib","uuid":"CB6B188A-1ADC-3C5D-AFFC-2ACD86E235EE","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735010844672,"image_vmaddr":140734909497344,"image_size":188416,"name":"/usr/lib/libssl.46.dylib","uuid":"06932872-13DA-33E3-8C28-7B49FC582039","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734127878144,"image_vmaddr":140734026530816,"image_size":225280,"name":"/System/Library/Frameworks/LDAP.framework/Versions/A/LDAP","uuid":"47D63DCF-2BDB-3983-B2A2-E9C47AA2401D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734873628672,"image_vmaddr":140734772281344,"image_size":8192,"name":"/System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent","uuid":"10E56F70-E234-31E0-9286-96D93A8ED17E","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735007752192,"image_vmaddr":140734906404864,"image_size":77824,"name":"/usr/lib/libsasl2.2.dylib","uuid":"E7573AC1-83FD-305A-8FB8-03145BDA286B","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140733833428992,"image_vmaddr":140733732081664,"image_size":2416640,"name":"/System/Library/CoreServices/RawCamera.bundle/Contents/MacOS/RawCamera","uuid":"59F81722-039E-33F5-A20E-936E997575A3","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734701764608,"image_vmaddr":140734600417280,"image_size":106496,"name":"/System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset","uuid":"07E116E6-7EBC-39F2-B5B4-31BAB6BAF852","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734861398016,"image_vmaddr":140734760050688,"image_size":270336,"name":"/System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip","uuid":"72CA32F8-4C96-3264-A655-623329EB3A28","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734371049472,"image_vmaddr":140734269702144,"image_size":94208,"name":"/System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore","uuid":"3FBA882A-83BF-3163-916F-0D7FA17D010D","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734375424000,"image_vmaddr":140734274076672,"image_size":417792,"name":"/System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport","uuid":"7408886B-B0CC-3EF0-9F11-9088DEE266ED","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140735011901440,"image_vmaddr":140734910554112,"image_size":372736,"name":"/usr/lib/libusrtcp.dylib","uuid":"05346A91-737C-33D0-B21B-F040950DFB28","cpu_type":16777223,"cpu_subtype":3},{"image_addr":140734976552960,"image_vmaddr":140734875205632,"image_size":806912,"name":"/usr/lib/libboringssl.dylib","uuid":"935DDB00-6514-3D0C-AEA5-C5FE6BCC0B61","cpu_type":16777223,"cpu_subtype":3}],"crash":{"threads":[{"backtrace":{"contents":[{"object_name":"macOSTestApp","object_addr":4360257536,"symbol_name":"-[OverwriteLinkRegisterScenario run]","symbol_addr":4360268640,"instruction_addr":4360268755},{"object_name":"macOSTestApp","object_addr":4360257536,"symbol_name":"__36-[MainWindowController runScenario:]_block_invoke","symbol_addr":4360284160,"instruction_addr":4360284230},{"object_name":"libdispatch.dylib","object_addr":140735024504832,"symbol_name":"_dispatch_call_block_and_release","symbol_addr":140735024510648,"instruction_addr":140735024510660},{"object_name":"libdispatch.dylib","object_addr":140735024504832,"symbol_name":"_dispatch_client_callout","symbol_addr":140735024514640,"instruction_addr":140735024514648},{"object_name":"libdispatch.dylib","object_addr":140735024504832,"symbol_name":"_dispatch_main_queue_callback_4CF","symbol_addr":140735024560387,"instruction_addr":140735024561323},{"object_name":"CoreFoundation","object_addr":140734050541568,"symbol_name":"__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__","symbol_addr":140734051339896,"instruction_addr":140734051339905},{"object_name":"CoreFoundation","object_addr":140734050541568,"symbol_name":"__CFRunLoopRun","symbol_addr":140734051075227,"instruction_addr":140734051077255},{"object_name":"CoreFoundation","object_addr":140734050541568,"symbol_name":"CFRunLoopRunSpecific","symbol_addr":140734051073136,"instruction_addr":140734051073598},{"object_name":"HIToolbox","object_addr":140734030090240,"symbol_name":"RunCurrentEventLoopInMode","symbol_addr":140734030285209,"instruction_addr":140734030285501},{"object_name":"HIToolbox","object_addr":140734030090240,"symbol_name":"ReceiveNextEventCommon","symbol_addr":140734030284173,"instruction_addr":140734030284532},{"object_name":"HIToolbox","object_addr":140734030090240,"symbol_name":"_BlockUntilNextEventMatchingListInModeWithFilter","symbol_addr":140734030284089,"instruction_addr":140734030284153},{"object_name":"AppKit","object_addr":140734003040256,"symbol_name":"_DPSNextEvent","symbol_addr":140734003305670,"instruction_addr":140734003306553},{"object_name":"AppKit","object_addr":140734003040256,"symbol_name":"-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]","symbol_addr":140734003299128,"instruction_addr":140734003300480},{"object_name":"AppKit","object_addr":140734003040256,"symbol_name":"-[NSApplication run]","symbol_addr":140734003241724,"instruction_addr":140734003242382},{"object_name":"AppKit","object_addr":140734003040256,"symbol_name":"NSApplicationMain","symbol_addr":140734003052685,"instruction_addr":140734003053462},{"object_name":"macOSTestApp","object_addr":4360257536,"symbol_name":"main","symbol_addr":4360293776,"instruction_addr":4360294110},{"object_name":"libdyld.dylib","object_addr":140735024771072,"symbol_name":"start","symbol_addr":140735024880840,"instruction_addr":140735024880841}],"skipped":0},"registers":{"basic":{"rax":1,"rbx":105553175461200,"rcx":0,"rdx":281471336899120,"rdi":140735668449560,"rsi":0,"rbp":140732855459504,"rsp":140732855459472,"r8":0,"r9":4294967295,"r10":4294967296,"r11":140735006328106,"r12":0,"r13":0,"r14":0,"r15":276,"rip":4360268755,"rflags":66070,"cs":43,"fs":0,"gs":0},"exception":{"trapno":14,"err":6,"faultvaddr":0}},"index":0,"crashed":true,"current_thread":false,"stack":{"overflow":false}},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":1,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":2,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":3,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":4,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"mach_msg_trap","symbol_addr":140735026195952,"instruction_addr":140735026195962},{"object_name":"CoreFoundation","object_addr":140734050541568,"symbol_name":"__CFRunLoopServiceMachPort","symbol_addr":140734051081726,"instruction_addr":140734051081973},{"object_name":"CoreFoundation","object_addr":140734050541568,"symbol_name":"__CFRunLoopRun","symbol_addr":140734051075227,"instruction_addr":140734051076546},{"object_name":"CoreFoundation","object_addr":140734050541568,"symbol_name":"CFRunLoopRunSpecific","symbol_addr":140734051073136,"instruction_addr":140734051073598},{"object_name":"AppKit","object_addr":140734003040256,"symbol_name":"_NSEventThread","symbol_addr":140734005016784,"instruction_addr":140734005016916},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"_pthread_start","symbol_addr":140735026995317,"instruction_addr":140735026995465},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"thread_start","symbol_addr":140735026977660,"instruction_addr":140735026977675}],"skipped":0},"index":5,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":6,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":7,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":8,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":9,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":10,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":11,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":12,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":13,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":14,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":15,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"__workq_kernreturn","symbol_addr":140735026201796,"instruction_addr":140735026201806},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"start_wqthread","symbol_addr":140735026977640,"instruction_addr":140735026977655}],"skipped":0},"index":16,"crashed":false,"current_thread":false},{"backtrace":{"contents":[],"skipped":0},"index":17,"crashed":false,"current_thread":false},{"backtrace":{"contents":[{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"mach_msg_trap","symbol_addr":140735026195952,"instruction_addr":140735026195962},{"object_name":"libsystem_kernel.dylib","object_addr":140735026192384,"symbol_name":"thread_suspend","symbol_addr":140735026317277,"instruction_addr":140735026317357},{"object_name":"Bugsnag","object_addr":4361379840,"symbol_name":"ksmachexc_i_handleExceptions","symbol_addr":4361710336,"instruction_addr":4361710496},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"_pthread_start","symbol_addr":140735026995317,"instruction_addr":140735026995465},{"object_name":"libsystem_pthread.dylib","object_addr":140735026970624,"symbol_name":"thread_start","symbol_addr":140735026977660,"instruction_addr":140735026977675}],"skipped":0},"index":18,"crashed":false,"current_thread":false},{"index":19,"crashed":false,"current_thread":true}],"error":{"address":0,"mach":{"exception":1,"exception_name":"EXC_BAD_ACCESS","code":"0x1","code_name":"KERN_INVALID_ADDRESS","subcode":"0x0"},"type":"mach"}},"user_atcrash":{"config":{"appType":"macOS","appVersion":"1.0.3","bundleVersion":"5","releaseStage":"development"},"metaData":{"app":{"name":"macOSTestApp"},"user":{"id":"48decb8cf9f410c4c20e6f597070ee60b131a5c4"},"device":{"timezone":"GMT","simulator":false,"wordSize":64}},"state":{"app":{"isLaunching":true}},"breadcrumbs":[{"timestamp":"2021-02-18T15:24:43.113Z","name":"Bugsnag loaded","type":"state","metaData":{}}]}} \ No newline at end of file diff --git a/Tests/KSCrash/KSCrashReportConverter_Tests.m b/Tests/KSCrash/KSCrashReportConverter_Tests.m deleted file mode 100755 index 81c3fccd3..000000000 --- a/Tests/KSCrash/KSCrashReportConverter_Tests.m +++ /dev/null @@ -1,90 +0,0 @@ -// -// KSCrashReportConverter_Tests.m -// -// Created by Karl Stenerud on 2012-02-24. -// -// Copyright (c) 2012 Karl Stenerud. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall remain in place -// in this source code. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - - -#import "FileBasedTestCase.h" -#import "XCTestCase+KSCrash.h" - - -#import "BSG_KSCrashReportStore.h" - -#define REPORT_PREFIX @"CrashReport-KSCrashTest" - -#define REPORT_BADPOINTER @"CrashReport-KSCrashTest-BadPointer.json" -#define REPORT_NSEXCEPTION @"CrashReport-KSCrashTest-NSException.json" - -#define APPLE_BADPOINTER_UNSYMBOLICATED @"AppleReport-KSCrashTest-BadPointer-Unsymbolicated.txt" -#define APPLE_NSEXCEPTION_UNSYMBOLICATED @"AppleReport-KSCrashTest-NSException-Unsymbolicated.txt" - - -@interface KSCrashReportConverter_Tests : FileBasedTestCase @end - -@implementation KSCrashReportConverter_Tests - -- (void) setUp -{ - [super setUp]; - [self createTempReportsAtPath:self.tempPath prefix:REPORT_PREFIX]; -} - -- (BSG_KSCrashReportStore*) store -{ -// return [KSCrashReportStore storeWithPath:self.tempPath filenamePrefix:REPORT_PREFIX]; - return nil; -} - -- (NSString*) resourcePathOfFile:(NSString*) file -{ - return [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:file]; -} - -- (NSString*) loadAppleReportNamed:(NSString*) name -{ - NSString* filename = [[[NSBundle bundleForClass:[self class]] resourcePath] stringByAppendingPathComponent:name]; - NSError* error = nil; - NSString* result = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&error]; - XCTAssertNil(error, @""); - return result; -} - -- (void) testConvertReportsUnsymbolicated -{ - // TODO - return; - -// KSCrashReportStore* store = [self store]; -// NSDictionary* report = [store reportNamed:REPORT_BADPOINTER]; -// NSString* converted = [KSCrashReportConverter toAppleFormat:report -// reportStyle:KSAppleReportStyleUnsymbolicated]; -// XCTAssertNotNil(converted, @""); -// -// NSString* expected = [self loadAppleReportNamed:APPLE_BADPOINTER_UNSYMBOLICATED]; -// XCTAssertNotNil(expected, @""); -// -// XCTAssertTrue([converted isEqualToString:expected], @""); -} - -@end diff --git a/Tests/KSCrash/KSCrashReportStore_Tests.m b/Tests/KSCrash/KSCrashReportStore_Tests.m deleted file mode 100755 index fd0888c2e..000000000 --- a/Tests/KSCrash/KSCrashReportStore_Tests.m +++ /dev/null @@ -1,172 +0,0 @@ -// -// KSCrashReportStore_Tests.m -// -// Created by Karl Stenerud on 2012-02-05. -// -// Copyright (c) 2012 Karl Stenerud. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall remain in place -// in this source code. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - - -#import "FileBasedTestCase.h" -#import "XCTestCase+KSCrash.h" - -#import "BSG_KSCrashReportStore.h" - - -#define REPORT_PREFIX @"CrashReport-KSCrashTest" -// -//#define REPORT_BADPOINTER @"CrashReport-KSCrashTest-BadPointer.json" -//#define REPORT_NSEXCEPTION @"CrashReport-KSCrashTest-NSException.json" - -@interface BugsnagFileStore () - -- (NSString *)fileIdFromFilename:(NSString *)filename; - -@end - -@interface BSG_KSCrashReportStore (Tests) - -- (NSString*) reportIDFromFilename:(NSString*) filename; -- (void) setBundleName:(NSString *) bundleName; - -@end - - -@interface KSCrashReportStore_Tests : FileBasedTestCase @end - -@implementation KSCrashReportStore_Tests - -- (void) setUp -{ - [super setUp]; - [self createTempReportsAtPath:self.tempPath prefix:REPORT_PREFIX]; -} - -- (BSG_KSCrashReportStore*) store -{ - return [BSG_KSCrashReportStore storeWithPath:self.tempPath]; -} - -- (BOOL) reportExists:(NSString*) reportName -{ - NSFileManager* fm = [NSFileManager defaultManager]; - return [fm fileExistsAtPath:[self.tempPath stringByAppendingPathComponent:reportName]]; -} - -- (void) testReportIDFromValidCombinedPath -{ - BSG_KSCrashReportStore* store = [self store]; - - NSString *bundleName = @"ЙогуртЙод"; - [store setBundleName:bundleName]; - - NSString* expectedReportID = @"EEEC2645-5413-48C8-85AD-89638E1BE968"; - NSString* reportFilename = [NSString stringWithFormat:@"%@-CrashReport-%@.json", bundleName, expectedReportID]; - NSString* reportID = [store fileIdFromFilename:reportFilename]; - XCTAssertNotNil(reportID, @""); - XCTAssertEqualObjects(reportID, expectedReportID, @""); -} - -- (void) testReportIDFromFileWithInvalidExtension -{ - BSG_KSCrashReportStore* store = [self store]; - - NSString* reportFilename = @"BundleID-CrashReport-REPORTID.xml"; - NSString* reportID = [store fileIdFromFilename:reportFilename]; - XCTAssertNil(reportID, @""); -} - -- (void) testReportIDFromFileWithExtensionInBundleID -{ - BSG_KSCrashReportStore* store = [self store]; - - NSString *bundleName = @"MyApp.json"; - [store setBundleName:bundleName]; - - NSString* expectedReportID = @"EEEC2645-5413-48C8-85AD-89638E1BE968"; - NSString* reportFilename = [NSString stringWithFormat:@"%@-CrashReport-%@.json", bundleName, expectedReportID]; - NSString* reportID = [store fileIdFromFilename:reportFilename]; - XCTAssertNotNil(reportID, @""); - XCTAssertEqualObjects(reportID, expectedReportID, @""); -} -// -//- (void) testAddAndRetrieveCustomReport -//{ -// BSG_KSCrashReportStore* store = [self store]; -// NSDictionary* report = @{@"A": @"1", @"B": @"2"}; -// -// NSString* reportID = [store addCustomReport:report]; -// XCTAssertNotNil(reportID, @""); -// -// NSDictionary* fetchedReport = [store reportWithID:reportID]; -// NSDictionary* reportSection = fetchedReport[@"report"]; -// XCTAssertNotNil(reportSection, @"Retrieved report is missing report section"); -// -// NSMutableDictionary* mutableReport = [fetchedReport mutableCopy]; -// mutableReport[@"report"] = nil; -// XCTAssertEqualObjects(mutableReport, report, @""); -//} - -/* TODO -- (void) testReportNames -{ - KSCrashReportStore* store = [self store]; - - NSArray* names = [store reportNames]; - XCTAssertEqual([names count], 2u,@""); - XCTAssertTrue([names containsObject:REPORT_BADPOINTER], @""); - XCTAssertTrue([names containsObject:REPORT_NSEXCEPTION], @""); -} - -- (void) testReportLoad -{ - KSCrashReportStore* store = [self store]; - NSDictionary* report = [store reportNamed:REPORT_BADPOINTER]; - XCTAssertNotNil(report, @""); - report = [store reportNamed:REPORT_NSEXCEPTION]; - XCTAssertNotNil(report, @""); -} - -- (void) testReportDelete -{ - KSCrashReportStore* store = [self store]; - - XCTAssertTrue([self reportExists:REPORT_BADPOINTER], @""); - [store deleteReportNamed:REPORT_BADPOINTER]; - XCTAssertFalse([self reportExists:REPORT_BADPOINTER], @""); - - XCTAssertTrue([self reportExists:REPORT_NSEXCEPTION], @""); - [store deleteReportNamed:REPORT_NSEXCEPTION]; - XCTAssertFalse([self reportExists:REPORT_NSEXCEPTION], @""); -} - -- (void) testReportDeleteAll -{ - KSCrashReportStore* store = [self store]; - - XCTAssertTrue([self reportExists:REPORT_BADPOINTER], @""); - XCTAssertTrue([self reportExists:REPORT_NSEXCEPTION], @""); - [store deleteAllReports]; - XCTAssertFalse([self reportExists:REPORT_BADPOINTER], @""); - XCTAssertFalse([self reportExists:REPORT_NSEXCEPTION], @""); -} -*/ -@end diff --git a/features/app_hangs.feature b/features/app_hangs.feature new file mode 100644 index 000000000..d8d06cb54 --- /dev/null +++ b/features/app_hangs.feature @@ -0,0 +1,120 @@ +Feature: App hangs + + Background: + Given I clear all persistent data + + Scenario: Non-fatal app hangs should not be reported by default + When I run "AppHangDefaultConfigScenario" + Then I should receive no errors + + Scenario: App hangs above the threshold should be reported + When I set the app to "2.1" mode + And I run "AppHangScenario" + And I wait to receive an error + + # + # App hang specific values + # + + And the event "severity" equals "warning" + And the event "severityReason.type" equals "appHang" + And the event "threads.0.errorReportingThread" is true + And the event "unhandled" is false + + And the exception "errorClass" equals "App Hang" + And the exception "message" equals "The app's main thread failed to respond to an event within 2000 milliseconds" + And the exception "type" equals "cocoa" + + And the event "session.events.handled" equals 1 + And the event "session.events.unhandled" equals 0 + + # + # Checks copied from app_and_device_attributes.feature + # + + And the error payload field "events.0.device.osName" equals the platform-dependent string: + | ios | iOS | + | macos | Mac OS | + And the error payload field "events.0.device.jailbroken" is false + And the error payload field "events.0.device.osVersion" matches the regex "\d+\.\d+" + And the error payload field "events.0.device.manufacturer" equals "Apple" + And the error payload field "events.0.device.locale" is not null + And the error payload field "events.0.device.id" is not null + And the error payload field "events.0.device.model" matches the test device model + And the error payload field "events.0.device.modelNumber" equals the platform-dependent string: + | ios | @not_null | + | macos | @null | + And the error payload field "events.0.device.runtimeVersions.osBuild" is not null + And the error payload field "events.0.device.runtimeVersions.clangVersion" is not null + And the error payload field "events.0.device.totalMemory" is an integer + + # DeviceWithState + + And the error payload field "events.0.device.freeDisk" is an integer + And the error payload field "events.0.device.freeMemory" is an integer + And the error payload field "events.0.device.orientation" equals the platform-dependent string: + | ios | @not_null | + | macos | @null | + And the error payload field "events.0.device.time" is a date + + # App + + # (codeBundleId is RN only, so omitted) + And the error payload field "events.0.app.bundleVersion" is not null + #And the error payload field "events.0.app.dsymUUIDs" is a non-empty array # Fails, == nil + And the error payload field "events.0.app.id" equals the platform-dependent string: + | ios | com.bugsnag.iOSTestApp | + | macos | com.bugsnag.macOSTestApp | + And the error payload field "events.0.app.isLaunching" is true + And the error payload field "events.0.app.releaseStage" equals "development" + And the error payload field "events.0.app.type" equals the platform-dependent string: + | ios | iOS | + | macos | macOS | + And the error payload field "events.0.app.version" equals "1.0.3" + + # AppWithState + + And the error payload field "events.0.app.duration" is a number + And the error payload field "events.0.app.durationInForeground" is a number + And the error payload field "events.0.app.inForeground" is not null + + Scenario: App hangs below the threshold should not be reported + When I set the app to "1.8" mode + And I run "AppHangScenario" + And I should receive no errors + + Scenario: App hangs should not be reported if enabledErrorTypes.appHangs = false + When I run "AppHangDisabledScenario" + Then I should receive no errors + + Scenario: Fatal app hangs should be reported if appHangThresholdMillis = BugsnagAppHangThresholdFatalOnly + When I run "AppHangFatalOnlyScenario" + And I wait for 3 seconds + And I relaunch the app + And I set the HTTP status code to 500 + And I configure Bugsnag for "AppHangFatalOnlyScenario" + And I wait to receive an error + And I clear the error queue + And I relaunch the app + And I set the HTTP status code to 200 + And I configure Bugsnag for "AppHangFatalOnlyScenario" + And I wait to receive an error + + And the event "severity" equals "error" + And the event "severityReason.type" equals "appHang" + And the event "threads.0.errorReportingThread" is true + And the event "unhandled" is true + + And the exception "errorClass" equals "App Hang" + And the exception "message" equals "The app was terminated while unresponsive" + And the exception "type" equals "cocoa" + + And the event "session.events.handled" equals 0 + And the event "session.events.unhandled" equals 1 + + Scenario: Fatal app hangs should not be reported if enabledErrorTypes.appHangs = false + When I run "AppHangFatalDisabledScenario" + And I wait for 3 seconds + And I relaunch the app + And I configure Bugsnag for "AppHangFatalDisabledScenario" + Then I should receive no errors diff --git a/features/enabled_error_types.feature b/features/enabled_error_types.feature index f9ada71ff..242554cbc 100644 --- a/features/enabled_error_types.feature +++ b/features/enabled_error_types.feature @@ -28,8 +28,7 @@ Feature: Enabled error types And the event "unhandled" is false Scenario: Mach Crash Reporting is disabled - When I run "DisableMachExceptionScenario" - And I relaunch the app + When I run "DisableMachExceptionScenario" and relaunch the app And I configure Bugsnag for "DisableMachExceptionScenario" And I wait to receive an error Then the error is valid for the error reporting API diff --git a/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj index ae5fee28f..dc485f998 100644 --- a/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 01B6BBB625DA82B800FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */; }; 01E0DB0B25E8EBD100A740ED /* AppDurationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */; }; 01E5EAD225B713990066EA8A /* OOMScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E5EAD125B713990066EA8A /* OOMScenario.m */; }; + 01F1474425F282E600C2DC65 /* AppHangScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1474325F282E600C2DC65 /* AppHangScenario.swift */; }; 6526A0D4248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */; }; 8A14F0F62282D4AE00337B05 /* (null) in Sources */ = {isa = PBXBuildFile; }; 8A32DB8222424E3000EDD92F /* NSExceptionShiftScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A32DB8122424E3000EDD92F /* NSExceptionShiftScenario.m */; }; @@ -176,6 +177,7 @@ 01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDurationScenario.swift; sourceTree = ""; }; 01E5EAD025B713990066EA8A /* OOMScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OOMScenario.h; sourceTree = ""; }; 01E5EAD125B713990066EA8A /* OOMScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OOMScenario.m; sourceTree = ""; }; + 01F1474325F282E600C2DC65 /* AppHangScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppHangScenario.swift; sourceTree = ""; }; 4994F05E0421A0B037DD2CC5 /* Pods_iOSTestApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOSTestApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = ""; }; 8A32DB8022424E3000EDD92F /* NSExceptionShiftScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSExceptionShiftScenario.h; sourceTree = ""; }; @@ -567,6 +569,7 @@ F49695AE2445476700105DA9 /* Plugin */, 0037410E2473CF2300BE41AA /* AppAndDeviceAttributesScenario.swift */, 01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */, + 01F1474325F282E600C2DC65 /* AppHangScenario.swift */, 01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */, 0163BFA62583B3CF008DC28B /* DiscardClassesScenarios.swift */, 01E5EAD025B713990066EA8A /* OOMScenario.h */, @@ -983,6 +986,7 @@ E7A324DE247E70E6008B0052 /* SessionCallbackOrderScenario.swift in Sources */, E7A324DA247E70C4008B0052 /* SessionCallbackCrashScenario.swift in Sources */, E7DD40452473D980000EDC14 /* UserDefaultInfoScenario.swift in Sources */, + 01F1474425F282E600C2DC65 /* AppHangScenario.swift in Sources */, E7A324E3247E7C17008B0052 /* SessionCallbackRemovalScenario.m in Sources */, E7767F15221C223C0006648C /* NewSessionScenario.swift in Sources */, F42951BEB2518C610A85FE0D /* BuiltinTrapScenario.m in Sources */, diff --git a/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/project.pbxproj b/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/project.pbxproj new file mode 100644 index 000000000..086659a2b --- /dev/null +++ b/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/project.pbxproj @@ -0,0 +1,337 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 01D124F325F0FCE000082BA4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 01D124F225F0FCE000082BA4 /* main.m */; }; + 4874C18E4E603ED4B989F616 /* libPods-BugsnagStressTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D3BACCAAFA4E7B90A8F6D532 /* libPods-BugsnagStressTest.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 01D124ED25F0FCE000082BA4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 01D124EF25F0FCE000082BA4 /* BugsnagStressTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BugsnagStressTest; sourceTree = BUILT_PRODUCTS_DIR; }; + 01D124F225F0FCE000082BA4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 112D716B312AF37BACF30F9F /* Pods-BugsnagStressTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BugsnagStressTest.debug.xcconfig"; path = "Target Support Files/Pods-BugsnagStressTest/Pods-BugsnagStressTest.debug.xcconfig"; sourceTree = ""; }; + D3BACCAAFA4E7B90A8F6D532 /* libPods-BugsnagStressTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BugsnagStressTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + EAAD88BAEF66A6C78853CF55 /* Pods-BugsnagStressTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BugsnagStressTest.release.xcconfig"; path = "Target Support Files/Pods-BugsnagStressTest/Pods-BugsnagStressTest.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 01D124EC25F0FCE000082BA4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4874C18E4E603ED4B989F616 /* libPods-BugsnagStressTest.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 01D124E625F0FCE000082BA4 = { + isa = PBXGroup; + children = ( + 01D124F125F0FCE000082BA4 /* BugsnagStressTest */, + 01D124F025F0FCE000082BA4 /* Products */, + F78A4B404D94250DB81E281A /* Pods */, + D50F2ACF2C46A92119E76437 /* Frameworks */, + ); + sourceTree = ""; + }; + 01D124F025F0FCE000082BA4 /* Products */ = { + isa = PBXGroup; + children = ( + 01D124EF25F0FCE000082BA4 /* BugsnagStressTest */, + ); + name = Products; + sourceTree = ""; + }; + 01D124F125F0FCE000082BA4 /* BugsnagStressTest */ = { + isa = PBXGroup; + children = ( + 01D124F225F0FCE000082BA4 /* main.m */, + ); + path = BugsnagStressTest; + sourceTree = ""; + }; + D50F2ACF2C46A92119E76437 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D3BACCAAFA4E7B90A8F6D532 /* libPods-BugsnagStressTest.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + F78A4B404D94250DB81E281A /* Pods */ = { + isa = PBXGroup; + children = ( + 112D716B312AF37BACF30F9F /* Pods-BugsnagStressTest.debug.xcconfig */, + EAAD88BAEF66A6C78853CF55 /* Pods-BugsnagStressTest.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 01D124EE25F0FCE000082BA4 /* BugsnagStressTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 01D124F625F0FCE000082BA4 /* Build configuration list for PBXNativeTarget "BugsnagStressTest" */; + buildPhases = ( + 5D15F78FCD5A75517D8F5A2C /* [CP] Check Pods Manifest.lock */, + 01D124EB25F0FCE000082BA4 /* Sources */, + 01D124EC25F0FCE000082BA4 /* Frameworks */, + 01D124ED25F0FCE000082BA4 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BugsnagStressTest; + productName = BugsnagStressTest; + productReference = 01D124EF25F0FCE000082BA4 /* BugsnagStressTest */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 01D124E725F0FCE000082BA4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1240; + TargetAttributes = { + 01D124EE25F0FCE000082BA4 = { + CreatedOnToolsVersion = 12.4; + }; + }; + }; + buildConfigurationList = 01D124EA25F0FCE000082BA4 /* Build configuration list for PBXProject "BugsnagStressTest" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 01D124E625F0FCE000082BA4; + productRefGroup = 01D124F025F0FCE000082BA4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 01D124EE25F0FCE000082BA4 /* BugsnagStressTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 5D15F78FCD5A75517D8F5A2C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-BugsnagStressTest-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 01D124EB25F0FCE000082BA4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01D124F325F0FCE000082BA4 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 01D124F425F0FCE000082BA4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 01D124F525F0FCE000082BA4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 01D124F725F0FCE000082BA4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 112D716B312AF37BACF30F9F /* Pods-BugsnagStressTest.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + }; + name = Debug; + }; + 01D124F825F0FCE000082BA4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EAAD88BAEF66A6C78853CF55 /* Pods-BugsnagStressTest.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 01D124EA25F0FCE000082BA4 /* Build configuration list for PBXProject "BugsnagStressTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01D124F425F0FCE000082BA4 /* Debug */, + 01D124F525F0FCE000082BA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01D124F625F0FCE000082BA4 /* Build configuration list for PBXNativeTarget "BugsnagStressTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01D124F725F0FCE000082BA4 /* Debug */, + 01D124F825F0FCE000082BA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 01D124E725F0FCE000082BA4 /* Project object */; +} diff --git a/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/xcshareddata/xcschemes/BugsnagStressTest.xcscheme b/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/xcshareddata/xcschemes/BugsnagStressTest.xcscheme new file mode 100644 index 000000000..4dd6bb622 --- /dev/null +++ b/features/fixtures/macos-stress-test/BugsnagStressTest.xcodeproj/xcshareddata/xcschemes/BugsnagStressTest.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/fixtures/macos-stress-test/BugsnagStressTest.xcworkspace/contents.xcworkspacedata b/features/fixtures/macos-stress-test/BugsnagStressTest.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..d80480931 --- /dev/null +++ b/features/fixtures/macos-stress-test/BugsnagStressTest.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/features/fixtures/macos-stress-test/BugsnagStressTest/main.m b/features/fixtures/macos-stress-test/BugsnagStressTest/main.m new file mode 100644 index 000000000..e88f083b6 --- /dev/null +++ b/features/fixtures/macos-stress-test/BugsnagStressTest/main.m @@ -0,0 +1,92 @@ +// +// main.m +// BugsnagStressTest +// +// Created by Nick Dowell on 04/03/2021. +// + +#import + +#import +#import +#import + +static NSString * const kNotifyEndpoint = @"http://localhost:9339/notify"; + +static const int kNumberOfIterations = 5000; + +static const NSInteger kMaxConcurrentNotifies = 8; + +// Note: memory usage increases with the number of threads +static const mach_vm_size_t kMemoryLimit = 50 * 1024 * 1024; + +int main(int argc, const char * argv[]) { + if (getenv("QUIET")) { + freopen("BugsnagStressTest.stdout.log", "w", stdout); + freopen("BugsnagStressTest.stderr.log", "w", stderr); + } + + CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); + + NSOperationQueue *notifyQueue = [[NSOperationQueue alloc] init]; + notifyQueue.maxConcurrentOperationCount = kMaxConcurrentNotifies; + + @autoreleasepool { + BugsnagConfiguration *config = [BugsnagConfiguration loadConfig]; + config.apiKey = @"0192837465afbecd0192837465afbecd"; + config.autoDetectErrors = NO; + config.endpoints.notify = kNotifyEndpoint; + [Bugsnag startWithConfiguration:config]; + + // These threads make a deadlock more likely if any of the notify threads are doing something they shouldn't. + + dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ + NSThread.currentThread.name = @"com.bugsnag.stress-test-malloc"; + while (1) { + free(malloc(1024 * 1024)); + } + }); + + dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{ + NSThread.currentThread.name = @"com.bugsnag.stress-test-objc"; + while (1) { + @autoreleasepool { + [[NSArray arrayWithObjects:@0, @1, @3, @4, nil] sortedArrayUsingSelector:@selector(compare:)]; + } + } + }); + + for (int i = 0; i < kNumberOfIterations; i++) { + [notifyQueue addOperationWithBlock:^{ + NSError *error = [NSError errorWithDomain:@"BugsnagStressTest" code:random() userInfo:nil]; + [Bugsnag notifyError:error block:^BOOL(BugsnagEvent *event) { + return YES; + }]; + }]; + } + } + + NSLog(@"Starting main run loop..."); + + mach_vm_size_t maxFootprint = 0; + + while (notifyQueue.operationCount) { + [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + // Memory watchdog that terminates the app if Bugsnag is using too much memory. + // setrlimit() and ulimit are not able to limit memory usage on macOS. + task_vm_info_data_t task_vm_info = {0}; + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&task_vm_info, &count); + maxFootprint = MAX(maxFootprint, task_vm_info.phys_footprint); + if (task_vm_info.phys_footprint > kMemoryLimit) { + NSLog(@"💥 Memory limit (%d MB) exceeded", (int)kMemoryLimit / (1024 * 1024)); + return 1; + } + } + + NSLog(@"Ran in %f seconds", CFAbsoluteTimeGetCurrent() - startTime); + NSLog(@"Maximum memory usage: %.1f MB", maxFootprint / (1024.0 * 1024.0)); + + return 0; +} diff --git a/features/fixtures/macos-stress-test/Makefile b/features/fixtures/macos-stress-test/Makefile new file mode 100644 index 000000000..3c6ad61c0 --- /dev/null +++ b/features/fixtures/macos-stress-test/Makefile @@ -0,0 +1,22 @@ +.PHONY: all build clean run + +all: build + +build: + pod install + xcodebuild \ + -workspace BugsnagStressTest.xcworkspace \ + -scheme BugsnagStressTest \ + -configuration Release \ + -derivedDataPath build/DerivedData \ + -quiet \ + install DSTROOT=build + +clean: + rm -rf build Pods *.log + +run: + rm -rf $(HOME)/Library/Application\ Support/com.bugsnag.Bugsnag + QUIET=true /usr/bin/time -l ./build/usr/local/bin/BugsnagStressTest + rm -rf $(HOME)/Library/Application\ Support/com.bugsnag.Bugsnag + echo "macOS stress-test complete" diff --git a/features/fixtures/macos-stress-test/Podfile b/features/fixtures/macos-stress-test/Podfile new file mode 100644 index 000000000..145574f19 --- /dev/null +++ b/features/fixtures/macos-stress-test/Podfile @@ -0,0 +1,5 @@ +platform :osx, '10.11' + +target 'BugsnagStressTest' do + pod 'Bugsnag', :path => '../../..' +end diff --git a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj index 4cc0bd8d2..121017665 100644 --- a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 01E0DB0625E8E95700A740ED /* AppDurationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */; }; 01ECBCF425A7522000FC0678 /* OnErrorOverwriteUnhandledFalseScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ECBCF225A7522000FC0678 /* OnErrorOverwriteUnhandledFalseScenario.swift */; }; 01ECBCF525A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01ECBCF325A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift */; }; + 01F1473A25F2817100C2DC65 /* AppHangScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F1473925F2817100C2DC65 /* AppHangScenario.swift */; }; 01F47CC4254B1B3100B184AD /* OriginalErrorNSExceptionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47C21254B1B2C00B184AD /* OriginalErrorNSExceptionScenario.swift */; }; 01F47CC5254B1B3100B184AD /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */; }; 01F47CC6254B1B3100B184AD /* HandledExceptionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47C28254B1B2C00B184AD /* HandledExceptionScenario.swift */; }; @@ -174,6 +175,7 @@ 01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDurationScenario.swift; sourceTree = ""; }; 01ECBCF225A7522000FC0678 /* OnErrorOverwriteUnhandledFalseScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnErrorOverwriteUnhandledFalseScenario.swift; sourceTree = ""; }; 01ECBCF325A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnErrorOverwriteUnhandledTrueScenario.swift; sourceTree = ""; }; + 01F1473925F2817100C2DC65 /* AppHangScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHangScenario.swift; sourceTree = ""; }; 01F47C21254B1B2C00B184AD /* OriginalErrorNSExceptionScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OriginalErrorNSExceptionScenario.swift; sourceTree = ""; }; 01F47C22254B1B2C00B184AD /* ThreadScenarios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadScenarios.h; sourceTree = ""; }; 01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = ""; }; @@ -362,6 +364,7 @@ 01F47C5B254B1B2E00B184AD /* AccessNonObjectScenario.m */, 01F47C60254B1B2E00B184AD /* AppAndDeviceAttributesScenario.swift */, 01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */, + 01F1473925F2817100C2DC65 /* AppHangScenario.swift */, 01018BAA25E417EC000312C6 /* AsyncSafeMallocScenario.m */, 01F47C73254B1B2E00B184AD /* AsyncSafeThreadScenario.h */, 01F47C40254B1B2D00B184AD /* AsyncSafeThreadScenario.m */, @@ -717,6 +720,7 @@ 017FBFB8254B09C300809042 /* MainWindowController.m in Sources */, 01F47CF0254B1B3100B184AD /* OnSendOverwriteScenario.swift in Sources */, 01F47D1B254B1B3100B184AD /* SIGSEGVScenario.m in Sources */, + 01F1473A25F2817100C2DC65 /* AppHangScenario.swift in Sources */, 0163BF9B2583AF2A008DC28B /* DiscardClassesScenarios.swift in Sources */, 01F47D09254B1B3100B184AD /* BreadcrumbCallbackOverrideScenario.swift in Sources */, 0176C0B1254AE81B0066E0F3 /* AppDelegate.m in Sources */, diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 8a9033d9f..a3aa4eb57 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -59,10 +59,12 @@ - (IBAction)runScenario:(id)sender { [self.scenario startBugsnag]; } - NSLog(@"Running scenario: %@", self.scenario); + NSLog(@"Will run scenario: %@", self.scenario); // Using dispatch_async to prevent AppleEvents swallowing exceptions. // For more info see https://www.chimehq.com/blog/sad-state-of-exceptions - dispatch_async(dispatch_get_main_queue(), ^{ + // 0.1s delay allows accessibility APIs to finish handling the mouse click and returns control to the tests framework. + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + NSLog(@"Running scenario: %@", self.scenario); [self.scenario run]; }); } diff --git a/features/fixtures/macos/macOSTestApp/main.m b/features/fixtures/macos/macOSTestApp/main.m index 413f1121e..90946e1f8 100644 --- a/features/fixtures/macos/macOSTestApp/main.m +++ b/features/fixtures/macos/macOSTestApp/main.m @@ -8,6 +8,11 @@ #import +void sigterm(int signum) { + NSLog(@"Received SIGTERM"); + exit(0); +} + int main(int argc, const char * argv[]) { [[NSUserDefaults standardUserDefaults] registerDefaults:@{ // Disable state restoration to prevent the following dialog being shown after crashes @@ -19,5 +24,7 @@ int main(int argc, const char * argv[]) { @"NSApplicationCrashOnExceptions": @YES, }]; + sigaction(SIGTERM, &(struct sigaction){ .sa_handler = &sigterm }, NULL); + return NSApplicationMain(argc, argv); } diff --git a/features/fixtures/shared/scenarios/AppHangScenario.swift b/features/fixtures/shared/scenarios/AppHangScenario.swift new file mode 100644 index 000000000..aa513fc76 --- /dev/null +++ b/features/fixtures/shared/scenarios/AppHangScenario.swift @@ -0,0 +1,72 @@ +// +// AppHangScenario.swift +// macOSTestApp +// +// Created by Nick Dowell on 05/03/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +class AppHangScenario: Scenario { + + override func startBugsnag() { + config.appHangThresholdMillis = 2_000 + super.startBugsnag() + } + + override func run() { + let timeInterval = TimeInterval(eventMode!)! + NSLog("Simulating an app hang of \(timeInterval) seconds...") + Thread.sleep(forTimeInterval: timeInterval) + NSLog("Finished sleeping") + } +} + +class AppHangDefaultConfigScenario: Scenario { + + override func run() { + let timeInterval: TimeInterval = 5 + NSLog("Simulating an app hang of \(timeInterval) seconds...") + Thread.sleep(forTimeInterval: timeInterval) + NSLog("Finished sleeping") + } +} + +class AppHangDisabledScenario: Scenario { + + override func startBugsnag() { + config.enabledErrorTypes.appHangs = false + super.startBugsnag() + } + + override func run() { + let timeInterval: TimeInterval = 5 + NSLog("Simulating an app hang of \(timeInterval) seconds...") + Thread.sleep(forTimeInterval: timeInterval) + NSLog("Finished sleeping") + } +} + +class AppHangFatalOnlyScenario: Scenario { + + override func startBugsnag() { + config.appHangThresholdMillis = BugsnagAppHangThresholdFatalOnly + super.startBugsnag() + } + + override func run() { + while true {} + } +} + +class AppHangFatalDisabledScenario: Scenario { + + override func startBugsnag() { + config.enabledErrorTypes.appHangs = false + config.enabledErrorTypes.ooms = false + super.startBugsnag() + } + + override func run() { + while true {} + } +} diff --git a/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.h b/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.h index 501a5e3e1..368cf8a58 100644 --- a/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.h +++ b/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.h @@ -10,4 +10,6 @@ @interface AutoSessionMixedEventsScenario : Scenario +@property (copy, nonatomic) dispatch_block_t onEventDelivery; + @end diff --git a/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m b/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m index 8af8e07ce..d9d2e3960 100644 --- a/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m @@ -22,12 +22,34 @@ @implementation SecondErr @implementation AutoSessionMixedEventsScenario +- (void)startBugsnag { + self.config.session = [self URLSessionWithObserver:^(NSURLRequest *request, NSData *responseData, NSURLResponse *response, NSError *error) { + if (self.onEventDelivery && [request.URL.absoluteString isEqual:self.config.endpoints.notify]) { + self.onEventDelivery(); + } + }]; + [super startBugsnag]; +} + - (void)run { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [Bugsnag notifyError:[FirstErr errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; - [Bugsnag notifyError:[SecondErr errorWithDomain:@"com.bugsnag" code:831 userInfo:nil]]; + [self performBlockAndWaitForEventDelivery:^{ + [Bugsnag notifyError:[FirstErr errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; + }]; + [self performBlockAndWaitForEventDelivery:^{ + [Bugsnag notifyError:[SecondErr errorWithDomain:@"com.bugsnag" code:831 userInfo:nil]]; + }]; @throw [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; }); } +- (void)performBlockAndWaitForEventDelivery:(dispatch_block_t)block { + NSCondition *condition = [[NSCondition alloc] init]; + self.onEventDelivery = ^{ + [condition signal]; + }; + block(); + [condition wait]; +} + @end diff --git a/features/fixtures/shared/scenarios/BareboneTestScenarios.swift b/features/fixtures/shared/scenarios/BareboneTestScenarios.swift index 9abfebf6b..008726dd1 100644 --- a/features/fixtures/shared/scenarios/BareboneTestScenarios.swift +++ b/features/fixtures/shared/scenarios/BareboneTestScenarios.swift @@ -61,13 +61,13 @@ class BareboneTestHandledScenario: Scenario { Bugsnag.leaveBreadcrumb(withMessage: "This is super secret") + self.afterSendErrorBlock = self.afterSendError + Bugsnag.notify(NSException(name: .rangeException, reason: "-[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]")) { $0.addMetadata(["info": "Some error specific information"], section: "Exception") $0.unhandled = true return true } - - self.afterSendErrorBlock = self.afterSendError } func afterSendError() { diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index 879619933..8aa06f43d 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -6,16 +6,17 @@ #import #import -void markErrorHandledCallback(const BSG_KSCrashReportWriter * _Nonnull writer); +NS_ASSUME_NONNULL_BEGIN + +void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @interface Scenario : NSObject @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; -+ (Scenario *_Nonnull)createScenarioNamed:(NSString *_Nonnull)className - withConfig:(BugsnagConfiguration *_Nonnull)config; ++ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config; -- (instancetype _Nonnull)initWithConfig:(BugsnagConfiguration *_Nonnull)config; +- (instancetype)initWithConfig:(BugsnagConfiguration *)config; /** * Blocks the calling thread until network connectivity to the notify endpoint has been verified. @@ -33,4 +34,8 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter * _Nonnull writer); @property (nonatomic, strong, nullable) NSString *eventMode; +- (NSURLSession *)URLSessionWithObserver:(void (^)(NSURLRequest *request, NSData *responseData, NSURLResponse *response, NSError *error))observer; + @end + +NS_ASSUME_NONNULL_END diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index f3c0a0174..a64eb3120 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -10,26 +10,52 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { writer->addBooleanElement(writer, "unhandled", false); } +// MARK: - + +typedef void (^ URLSessionResponseObserver)(NSURLRequest *request, NSData *responseData, NSURLResponse *response, NSError *error); + +@interface ObservableURLSession : NSObject + ++ (instancetype)sessionWithObserver:(URLSessionResponseObserver)observer; + +@property (copy, nonatomic) URLSessionResponseObserver observer; + +@end + + +// MARK: - + @implementation Scenario + (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config { Class clz = NSClassFromString(className); - if (clz == nil) { // swift class #if TARGET_OS_IPHONE - clz = NSClassFromString([NSString stringWithFormat:@"iOSTestApp.%@", className]); -#elif TARGET_OS_MAC - clz = NSClassFromString([NSString stringWithFormat:@"macOSTestApp.%@", className]); + NSString *swiftPrefix = @"iOSTestApp."; +#elif TARGET_OS_OSX + NSString *swiftPrefix = @"macOSTestApp."; #endif - } - NSAssert(clz != nil, @"Failed to find class named '%@'", className); - - BOOL implementsRun = method_getImplementation(class_getInstanceMethod([Scenario class], @selector(run))) != - method_getImplementation(class_getInstanceMethod(clz, @selector(run))); + if (!clz) { // Case-insensitive class lookup because AppiumForMac is a bit unreliable at entering uppercase characters. + unsigned int classCount = 0; + Class *classes = objc_copyClassList(&classCount); + for (unsigned int i = 0; i < classCount; i++) { + NSString *name = NSStringFromClass(classes[i]); + if ([name hasPrefix:swiftPrefix]) { + name = [name substringFromIndex:swiftPrefix.length]; + } + if ([name caseInsensitiveCompare:className] == NSOrderedSame) { + clz = classes[i]; + break; + } + } + free(classes); + } - NSAssert(implementsRun, @"Class '%@' does not implement the run method", className); + if (!clz) { + [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; + } id obj = [clz alloc]; @@ -71,6 +97,8 @@ - (void)waitForNetworkConnectivity { } - (void)run { + // Must be implemented by all subclasses + [self doesNotRecognizeSelector:_cmd]; } - (void)startBugsnag { @@ -82,4 +110,39 @@ - (void)startBugsnag { - (void)didEnterBackgroundNotification { } +- (NSURLSession *)URLSessionWithObserver:(URLSessionResponseObserver)observer { + return (id)[ObservableURLSession sessionWithObserver:observer]; +} + +@end + + +// MARK: - + +@implementation ObservableURLSession + +// NSURLSession does not allow subclassing - calling [ObservableURLSession sessionWithConfiguration:] will return an +// instance of NSURLSession instead of ObservableURLSession, so we have to resort to acting as a proxy object. + ++ (instancetype)sessionWithObserver:(URLSessionResponseObserver)observer { + ObservableURLSession *session = [[ObservableURLSession alloc] init]; + session.observer = observer; + return session; +} + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData + completionHandler:(void (^)(NSData *, NSURLResponse *, NSError *))completionHandler { + return [NSURLSession.sharedSession uploadTaskWithRequest:request fromData:bodyData completionHandler: + ^(NSData *responseData, NSURLResponse *response, NSError *error) { + completionHandler(responseData, response, error); + if (self.observer) { + self.observer(request, responseData, response, error); + } + }]; +} + +- (id)forwardingTargetForSelector:(SEL)aSelector { + return NSURLSession.sharedSession; +} + @end diff --git a/features/last_run_info.feature b/features/last_run_info.feature index a578f6893..e14390ed7 100644 --- a/features/last_run_info.feature +++ b/features/last_run_info.feature @@ -4,8 +4,7 @@ Feature: Launch detection Given I clear all persistent data Scenario: LastRunInfo consecutiveLaunchCrashes increments when isLaunching is true - When I run "LastRunInfoConsecutiveLaunchCrashesScenario" - And I relaunch the app + When I run "LastRunInfoConsecutiveLaunchCrashesScenario" and relaunch the app And I configure Bugsnag for "LastRunInfoConsecutiveLaunchCrashesScenario" And I wait to receive an error And the event "metaData.lastRunInfo.consecutiveLaunchCrashes" equals 1 diff --git a/features/scripts/export_mac_app.sh b/features/scripts/export_mac_app.sh index f4aaa8e3b..055d6099b 100755 --- a/features/scripts/export_mac_app.sh +++ b/features/scripts/export_mac_app.sh @@ -11,7 +11,8 @@ xcrun xcodebuild \ -configuration Debug \ -archivePath archive/macOSTestApp.xcarchive \ -quiet \ - archive + archive \ + GCC_PREPROCESSOR_DEFINITIONS='$(inherited) BSG_LOG_LEVEL=BSG_LOGLEVEL_DEBUG' xcrun xcodebuild \ -exportArchive \ diff --git a/features/stress_test.feature b/features/stress_test.feature new file mode 100644 index 000000000..6126964df --- /dev/null +++ b/features/stress_test.feature @@ -0,0 +1,12 @@ +@stress_test +Feature: Stress test + + Scenario: Triggering error notifications continuously + When I start a new shell + And I input "cd features/fixtures/macos-stress-test" interactively + And I input "make build run" interactively + And I wait for the shell to output "macOS stress-test complete" to stdout + Then the last interactive command exited successfully + + # This is low, but due to network congestion all we can guarantee in the course of a normal test + And I have received at least 1 error diff --git a/features/support/env.rb b/features/support/env.rb index b719c6af3..973f3107e 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -5,6 +5,14 @@ AfterConfiguration do |_config| Maze.config.receive_no_requests_wait = 15 + + # Setup a 3 minute timeout for receiving requests is STRESS_TEST env var is set + Maze.config.receive_requests_wait = 180 unless ENV['STRESS_TEST'].nil? +end + +# Skip stress tests unless STRESS_TEST env var is set +Before('@stress_test') do |_scenario| + skip_this_scenario('Skipping: Run is not configured for stress tests') if ENV['STRESS_TEST'].nil? end # Additional require MacOS configuration diff --git a/features/user_persistence.feature b/features/user_persistence.feature index ef479104b..864ece896 100644 --- a/features/user_persistence.feature +++ b/features/user_persistence.feature @@ -38,6 +38,7 @@ Scenario: User Info is persisted from client across app runs # Session is captured before the user can be set on the Client And I wait to receive a session + And I wait for 1 second And I relaunch the app Then the session is valid for the session reporting API