diff --git a/CHANGELOG.md b/CHANGELOG.md index bc7827ca..d7a76826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,17 @@ Changelog ========= -## 2.23.8 (TBC) +## 2.23.8 (2020-05-20) + +## Bug fixes * Fix missing comma in podspec headers list [#456](https://github.com/bugsnag/bugsnag-react-native/pull/456) +* (iOS) Fix an issue where an app could deadlock during a crash if unfavorable + timing caused DYLD lock contention. + [#580](https://github.com/bugsnag/bugsnag-cocoa/pull/580) + ## 2.23.7 (2020-04-07) ### Bug fixes @@ -47,7 +53,6 @@ Changelog * `BugsnagConfiguration.autoCaptureSessions` is now named `BugsnagConfiguration.autoDetectSessions` - ## 2.23.5 (2020-01-13) ### Bug fixes @@ -55,6 +60,7 @@ Changelog * (iOS) Fix a packaging issue introduced in 2.23.3 which prevented apps using React Native 0.59 and below from building successfully [#441](https://github.com/bugsnag/bugsnag-react-native/pull/441) + ## 2.23.4 (2020-01-06) ### Bug fixes diff --git a/README.md b/README.md index facd6bff..25bfc196 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Bugsnag error monitoring & exception reporter for React Native -[![Documentation](https://img.shields.io/badge/documentation-2.23.7-blue.svg)](http://docs.bugsnag.com/platforms/react-native/) +[![Documentation](https://img.shields.io/badge/documentation-2.23.8-blue.svg)](http://docs.bugsnag.com/platforms/react-native/) Automatic [React Native crash reporting](https://www.bugsnag.com/platforms/react-native-error-reporting/) with Bugsnag helps you detect both native OS and JavaScript errors in your React Native apps. diff --git a/android/build.gradle b/android/build.gradle index 4f65dfc4..1c4d7fc5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -23,7 +23,7 @@ android { minSdkVersion safeExtGet('minSdkVersion', 16) targetSdkVersion safeExtGet('targetSdkVersion', 28) versionCode 4 - versionName '2.23.7' + versionName '2.23.8' consumerProguardFiles 'proguard-rules.pro' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BSGOutOfMemoryWatchdog.m b/cocoa/vendor/bugsnag-cocoa/Source/BSGOutOfMemoryWatchdog.m index ac71776d..f203b569 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BSGOutOfMemoryWatchdog.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BSGOutOfMemoryWatchdog.m @@ -243,6 +243,8 @@ - (NSMutableDictionary *)generateCacheInfoWithConfig:(BugsnagConfiguration *)con app[@"releaseStage"] = config.releaseStage; app[@"version"] = systemInfo[@BSG_KSSystemField_BundleShortVersion] ?: @""; app[@"bundleVersion"] = systemInfo[@BSG_KSSystemField_BundleVersion] ?: @""; + // 'codeBundleId' only (optionally) exists for React Native clients and defaults otherwise to nil + app[@"codeBundleId"] = [config codeBundleId]; #if BSGOOMAvailable UIApplicationState state = [BSG_KSSystemInfo currentAppState]; app[@"inForeground"] = @([BSG_KSSystemInfo isInForeground:state]); @@ -262,8 +264,12 @@ - (NSMutableDictionary *)generateCacheInfoWithConfig:(BugsnagConfiguration *)con // device[@"lowMemory"] is initially unset device[@"osBuild"] = systemInfo[@BSG_KSSystemField_OSVersion]; device[@"osVersion"] = systemInfo[@BSG_KSSystemField_SystemVersion]; + device[@"osName"] = systemInfo[@BSG_KSSystemField_SystemName]; + // Translated from 'iDeviceMaj,Min' into human-readable "iPhone X" description on the server device[@"model"] = systemInfo[@BSG_KSSystemField_Machine]; + device[@"modelNumber"] = systemInfo[@ BSG_KSSystemField_Model]; device[@"wordSize"] = @(PLATFORM_WORD_SIZE); + device[@"locale"] = [[NSLocale currentLocale] localeIdentifier]; #if TARGET_OS_SIMULATOR device[@"simulator"] = @YES; #else diff --git a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m index af1c28aa..a74ab801 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/BugsnagNotifier.m @@ -47,7 +47,7 @@ #import #endif -NSString *const NOTIFIER_VERSION = @"5.23.0"; +NSString *const NOTIFIER_VERSION = @"5.23.2"; NSString *const NOTIFIER_URL = @"https://github.com/bugsnag/bugsnag-cocoa"; NSString *const BSTabCrash = @"crash"; NSString *const BSAttributeDepth = @"depth"; @@ -471,7 +471,7 @@ - (void)watchLifecycleEvents:(NSNotificationCenter *)center { #if TARGET_OS_TV || TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE foregroundName = UIApplicationWillEnterForegroundNotification; - backgroundName = UIApplicationWillEnterForegroundNotification; + backgroundName = UIApplicationDidEnterBackgroundNotification; #elif TARGET_OS_MAC foregroundName = NSApplicationWillBecomeActiveNotification; backgroundName = NSApplicationDidFinishLaunchingNotification; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m index c703c0c9..12294f1e 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m @@ -24,6 +24,7 @@ // THE SOFTWARE. // +#import #import "BSG_KSCrashAdvanced.h" #import "BSG_KSCrashC.h" @@ -31,6 +32,7 @@ #import "BSG_KSJSONCodecObjC.h" #import "BSG_KSSingleton.h" #import "BSG_KSSystemCapabilities.h" +#import "BSG_KSMachHeaders.h" #import "NSError+BSG_SimpleConstructor.h" //#define BSG_KSLogger_LocalLevel TRACE @@ -49,6 +51,10 @@ #define BSG_KSCRASH_DefaultReportFilesDirectory @"KSCrashReports" #endif +#ifndef BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE +#define BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE 400 +#endif + // ============================================================================ #pragma mark - Constants - // ============================================================================ @@ -219,13 +225,16 @@ - (NSString *)stateFilePath { } - (BOOL)install { + // Maintain a cache of info about dynamically loaded binary images + [self listenForLoadedBinaries]; + _handlingCrashTypes = bsg_kscrash_install( [self.crashReportPath UTF8String], [self.recrashReportPath UTF8String], [self.stateFilePath UTF8String], [self.nextCrashID UTF8String]); if (self.handlingCrashTypes == 0) { return false; } - + #if BSG_KSCRASH_HAS_UIKIT NSNotificationCenter *nCenter = [NSNotificationCenter defaultCenter]; [nCenter addObserver:self @@ -253,6 +262,21 @@ - (BOOL)install { return true; } +/** + * Set up listeners for un/loaded frameworks. Maintaining our own list of framework Mach + * headers means that we avoid potential deadlock situations where we try and suspend + * lock-holding threads prior to loading mach headers as part of our normal event handling + * behaviour. + */ +- (void)listenForLoadedBinaries { + bsg_initialise_mach_binary_headers(BSG_INITIAL_MACH_BINARY_IMAGE_ARRAY_SIZE); + + // Note: Internally, access to DYLD's binary image store is guarded by an OSSpinLock. We therefore don't need to + // add additional guards around our access. + _dyld_register_func_for_remove_image(&bsg_mach_binary_image_removed); + _dyld_register_func_for_add_image(&bsg_mach_binary_image_added); +} + - (void)sendAllReportsWithCompletion: (BSG_KSCrashReportFilterCompletion)onCompletion { [self.crashReportStore pruneFilesLeaving:self.maxStoredReports]; diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index a9ea67e1..616de183 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -36,6 +36,7 @@ #include "BSG_KSObjC.h" #include "BSG_KSSignalInfo.h" #include "BSG_KSString.h" +#include "BSG_KSMachHeaders.h" //#define BSG_kSLogger_LocalLevel TRACE #include "BSG_KSLogger.h" @@ -1084,70 +1085,23 @@ int bsg_kscrw_i_threadIndex(const thread_t thread) { * * @param key The object key, if needed. * - * @param index Which image to write about. + * @param index Cached info about the binary image. */ void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer, - const char *const key, const uint32_t index) { - const struct mach_header *header = _dyld_get_image_header(index); - if (header == NULL) { - return; - } - - uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); - if (cmdPtr == 0) { - return; - } - - // Look for the TEXT segment to get the image size. - // Also look for a UUID command. - uint64_t imageSize = 0; - uint64_t imageVmAddr = 0; - uint8_t *uuid = NULL; - - for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { - struct load_command *loadCmd = (struct load_command *)cmdPtr; - switch (loadCmd->cmd) { - case LC_SEGMENT: { - struct segment_command *segCmd = (struct segment_command *)cmdPtr; - if (strcmp(segCmd->segname, SEG_TEXT) == 0) { - imageSize = segCmd->vmsize; - imageVmAddr = segCmd->vmaddr; - } - break; - } - case LC_SEGMENT_64: { - struct segment_command_64 *segCmd = - (struct segment_command_64 *)cmdPtr; - if (strcmp(segCmd->segname, SEG_TEXT) == 0) { - imageSize = segCmd->vmsize; - imageVmAddr = segCmd->vmaddr; - } - break; - } - case LC_UUID: { - struct uuid_command *uuidCmd = (struct uuid_command *)cmdPtr; - uuid = uuidCmd->uuid; - break; - } - } - cmdPtr += loadCmd->cmdsize; - } - + const char *const key, + const uint32_t index) +{ + BSG_Mach_Binary_Image_Info info = *bsg_dyld_get_image_info(index); + writer->beginObject(writer, key); { - writer->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, - (uintptr_t)header); - writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, - imageVmAddr); - writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, - imageSize); - writer->addStringElement(writer, BSG_KSCrashField_Name, - _dyld_get_image_name(index)); - writer->addUUIDElement(writer, BSG_KSCrashField_UUID, uuid); - writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, - header->cputype); - writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, - header->cpusubtype); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageAddress, (uintptr_t)info.header); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageVmAddress, info.imageVmAddr); + writer->addUIntegerElement(writer, BSG_KSCrashField_ImageSize, info.imageSize); + writer->addStringElement(writer, BSG_KSCrashField_Name, info.name); + writer->addUUIDElement(writer, BSG_KSCrashField_UUID, info.uuid); + writer->addIntegerElement(writer, BSG_KSCrashField_CPUType, info.header->cputype); + writer->addIntegerElement(writer, BSG_KSCrashField_CPUSubType, info.header->cpusubtype); } writer->endContainer(writer); } @@ -1159,12 +1113,14 @@ void bsg_kscrw_i_writeBinaryImage(const BSG_KSCrashReportWriter *const writer, * @param key The object key, if needed. */ void bsg_kscrw_i_writeBinaryImages(const BSG_KSCrashReportWriter *const writer, - const char *const key) { - const uint32_t imageCount = _dyld_image_count(); + const char *const key) +{ + const uint32_t imageCount = bsg_dyld_image_count(); writer->beginArray(writer, key); { for (uint32_t iImg = 0; iImg < imageCount; iImg++) { + // Threads are suspended at this point; no need to synchronise/lock bsg_kscrw_i_writeBinaryImage(writer, NULL, iImg); } } @@ -1658,9 +1614,6 @@ void bsg_kscrashreport_writeStandardReport( bsg_ksjsonendEncode(bsg_getJsonContext(writer)); - if (!bsg_ksfuflushWriteBuffer(fd)) { - BSG_KSLOG_ERROR("Failed to flush write buffer"); - } close(fd); } diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m index 7c47457d..d2008efa 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m @@ -263,9 +263,6 @@ bool bsg_kscrashstate_i_saveState(const BSG_KSCrash_State *const state, goto done; } result = bsg_ksjsonendEncode(&JSONContext); - if (!bsg_ksfuflushWriteBuffer(fd)) { - BSG_KSLOG_ERROR(@"Failed to flush write buffer"); - } done: close(fd); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c index 7f79499a..14864793 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSDynamicLinker.c @@ -26,6 +26,7 @@ #include "BSG_KSDynamicLinker.h" #include "BSG_KSArchSpecific.h" +#include "BSG_KSMachHeaders.h" #include #include @@ -33,10 +34,10 @@ uint32_t bsg_ksdlimageNamed(const char *const imageName, bool exactMatch) { if (imageName != NULL) { - const uint32_t imageCount = _dyld_image_count(); - + const uint32_t imageCount = bsg_dyld_image_count(); + for (uint32_t iImg = 0; iImg < imageCount; iImg++) { - const char *name = _dyld_get_image_name(iImg); + const char *name = bsg_dyld_get_image_name(iImg); if (name == NULL) { continue; // name is null if the index is out of range per dyld(3) } else if (exactMatch) { @@ -57,7 +58,7 @@ const uint8_t *bsg_ksdlimageUUID(const char *const imageName, bool exactMatch) { if (imageName != NULL) { const uint32_t iImg = bsg_ksdlimageNamed(imageName, exactMatch); if (iImg != UINT32_MAX) { - const struct mach_header *header = _dyld_get_image_header(iImg); + const struct mach_header *header = bsg_dyld_get_image_header(iImg); if (header != NULL) { uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); if (cmdPtr != 0) { @@ -96,15 +97,15 @@ uintptr_t bsg_ksdlfirstCmdAfterHeader(const struct mach_header *const header) { } uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address) { - const uint32_t imageCount = _dyld_image_count(); + const uint32_t imageCount = bsg_dyld_image_count(); const struct mach_header *header = 0; for (uint32_t iImg = 0; iImg < imageCount; iImg++) { - header = _dyld_get_image_header(iImg); + header = bsg_dyld_get_image_header(iImg); if (header != NULL) { // Look for a segment command with this address within its range. uintptr_t addressWSlide = - address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg); + address - bsg_dyld_get_image_vmaddr_slide(iImg); uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); if (cmdPtr == 0) { continue; @@ -135,7 +136,7 @@ uint32_t bsg_ksdlimageIndexContainingAddress(const uintptr_t address) { } uintptr_t bsg_ksdlsegmentBaseOfImageIndex(const uint32_t idx) { - const struct mach_header *header = _dyld_get_image_header(idx); + const struct mach_header *header = bsg_dyld_get_image_header(idx); if (header == NULL) { return 0; } @@ -176,12 +177,12 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { if (idx == UINT_MAX) { return false; } - const struct mach_header *header = _dyld_get_image_header(idx); + const struct mach_header *header = bsg_dyld_get_image_header(idx); if (header == NULL) { return false; } const uintptr_t imageVMAddrSlide = - (uintptr_t)_dyld_get_image_vmaddr_slide(idx); + bsg_dyld_get_image_vmaddr_slide(idx); const uintptr_t addressWithSlide = address - imageVMAddrSlide; const uintptr_t segmentBase = bsg_ksdlsegmentBaseOfImageIndex(idx) + imageVMAddrSlide; @@ -189,7 +190,7 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { return false; } - info->dli_fname = _dyld_get_image_name(idx); + info->dli_fname = bsg_dyld_get_image_name(idx); info->dli_fbase = (void *)header; // Find symbol tables and get whichever symbol is closest to the address. @@ -244,12 +245,12 @@ bool bsg_ksdldladdr(const uintptr_t address, Dl_info *const info) { const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx, const char *symbolName) { - const struct mach_header *header = _dyld_get_image_header(imageIdx); + const struct mach_header *header = bsg_dyld_get_image_header(imageIdx); if (header == NULL) { return NULL; } const uintptr_t imageVMAddrSlide = - (uintptr_t)_dyld_get_image_vmaddr_slide(imageIdx); + bsg_dyld_get_image_vmaddr_slide(imageIdx); const uintptr_t segmentBase = bsg_ksdlsegmentBaseOfImageIndex(imageIdx) + imageVMAddrSlide; if (segmentBase == 0) { @@ -290,7 +291,7 @@ const void *bsg_ksdlgetSymbolAddrInImage(uint32_t imageIdx, } const void *bsg_ksdlgetSymbolAddrInAnyImage(const char *symbolName) { - const uint32_t imageCount = _dyld_image_count(); + const uint32_t imageCount = bsg_dyld_image_count(); for (uint32_t iImg = 0; iImg < imageCount; iImg++) { const void *symbolAddr = bsg_ksdlgetSymbolAddrInImage(iImg, symbolName); diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c index 02959004..fcbeec96 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.c @@ -30,24 +30,9 @@ #include "BSG_KSLogger.h" #include -#include -#include #include -#include #include -/** Buffer size to use in the "writeFmt" functions. - * If the formatted output length would exceed this value, it is truncated. - */ -#ifndef BSG_KSFU_WriteFmtBufferSize -#define BSG_KSFU_WriteFmtBufferSize 1024 -#endif - -#define BUFFER_SIZE 65536 - -char charBuffer[BUFFER_SIZE]; -ssize_t bufferLen = 0; - const char *bsg_ksfulastPathEntry(const char *const path) { if (path == NULL) { return NULL; @@ -57,106 +42,42 @@ const char *bsg_ksfulastPathEntry(const char *const path) { return lastFile == NULL ? path : lastFile + 1; } -bool bsg_ksfuflushWriteBuffer(const int fd) { - const char *pos = charBuffer; - while (bufferLen > 0) { - ssize_t bytesWritten = write(fd, pos, (size_t)bufferLen); - if (bytesWritten == -1) { - BSG_KSLOG_ERROR("Could not write to fd %d: %s", fd, - strerror(errno)); - return false; - } - bufferLen -= bytesWritten; - pos += bytesWritten; - } - return true; -} - bool bsg_ksfuwriteBytesToFD(const int fd, const char *const bytes, ssize_t length) { - - for (ssize_t k = 0; k < length; k++) { - if (bufferLen >= BUFFER_SIZE) { - if (!bsg_ksfuflushWriteBuffer(fd)) { - return false; - } + ssize_t bytesRemaining = length; + ssize_t bytesWritten = 0; + const char *unwrittenBytes = (const char *)bytes; + + // write(2) attempts to write the entire length but may write less than + // that, the exact number of bytes are specified in the return value. In + // the (unlikely) event that the bytes written are less than length, this + // function retries with the remaining bytes until all bytes are written, + // handling potential error cases as needed. + while (bytesRemaining > 0) { + bytesWritten = write(fd, unwrittenBytes, bytesRemaining); + if (bytesWritten == -1) { + // Retry as-is if a signal interrupt occurred, as its a recoverable + // error. Otherwise exit early as the file descriptor cannot be written + // (due to lack of disk space, invalid fd, invalid file offset etc). + // + // write(2): Upon successful completion the number of bytes which were + // written is returned. Otherwise, a -1 is returned and the global + // variable errno is set to indicate the error. + // + // ERRORS + // The write(), writev(), and pwrite() system calls will fail and + // the file pointer will remain unchanged if: + // + // ... + // [EINTR] A signal interrupts the write before it could be completed. + if (errno != EINTR) { + return false; // Unrecoverable } - charBuffer[bufferLen] = bytes[k]; - bufferLen++; + } else { + bytesRemaining -= bytesWritten; + unwrittenBytes += bytesWritten; + } } - return true; -} -bool bsg_ksfureadBytesFromFD(const int fd, char *const bytes, ssize_t length) { - char *pos = bytes; - while (length > 0) { - ssize_t bytesRead = read(fd, pos, (size_t)length); - if (bytesRead == -1) { - BSG_KSLOG_ERROR("Could not write to fd %d: %s", fd, - strerror(errno)); - return false; - } - length -= bytesRead; - pos += bytesRead; - } return true; } - -bool bsg_ksfuwriteStringToFD(const int fd, const char *const string) { - if (*string != 0) { - size_t bytesToWrite = strlen(string); - const char *pos = string; - while (bytesToWrite > 0) { - ssize_t bytesWritten = write(fd, pos, bytesToWrite); - if (bytesWritten == -1) { - BSG_KSLOG_ERROR("Could not write to fd %d: %s", fd, - strerror(errno)); - return false; - } - bytesToWrite -= (size_t)bytesWritten; - pos += bytesWritten; - } - return true; - } - return false; -} - -bool bsg_ksfuwriteFmtToFD(const int fd, const char *const fmt, ...) { - if (*fmt != 0) { - va_list args; - va_start(args, fmt); - bool result = bsg_ksfuwriteFmtArgsToFD(fd, fmt, args); - va_end(args); - return result; - } - return false; -} - -bool bsg_ksfuwriteFmtArgsToFD(const int fd, const char *const fmt, - va_list args) { - if (*fmt != 0) { - char buffer[BSG_KSFU_WriteFmtBufferSize]; - vsnprintf(buffer, sizeof(buffer), fmt, args); - return bsg_ksfuwriteStringToFD(fd, buffer); - } - return false; -} - -ssize_t bsg_ksfureadLineFromFD(const int fd, char *const buffer, - const int maxLength) { - char *end = buffer + maxLength - 1; - *end = 0; - char *ch; - for (ch = buffer; ch < end; ch++) { - ssize_t bytesRead = read(fd, ch, 1); - if (bytesRead < 0) { - BSG_KSLOG_ERROR("Could not read from fd %d: %s", fd, - strerror(errno)); - return -1; - } else if (bytesRead == 0 || *ch == '\n') { - break; - } - } - *ch = 0; - return ch - buffer; -} diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h index 7b89277e..2c64c0a3 100644 --- a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h @@ -34,9 +34,7 @@ extern "C" { #endif -#include #include -#include #include /** Get the last entry in a file path. Assumes UNIX style separators. @@ -66,62 +64,6 @@ bool bsg_ksfuwriteBytesToFD(const int fd, const char *bytes, ssize_t length); */ bool bsg_ksfuflushWriteBuffer(const int fd); -/** Read bytes from a file descriptor. - * - * @param fd The file descriptor. - * - * @param bytes Buffer to store the bytes in. - * - * @param length The number of bytes to read. - * - * @return true if the operation was successful. - */ -bool bsg_ksfureadBytesFromFD(const int fd, char *bytes, ssize_t length); - -/** Write a string to a file. - * - * @param fd The file descriptor. - * - * @param string The string to write. - * - * @return true if successful. - */ -bool bsg_ksfuwriteStringToFD(const int fd, const char *string); - -/** Write a formatted string to a file. - * - * @param fd The file descriptor. - * - * @param fmt The format specifier, followed by its arguments. - * - * @return true if successful. - */ -bool bsg_ksfuwriteFmtToFD(const int fd, const char *fmt, ...); - -/** Write a formatted string to a file. - * - * @param fd The file descriptor. - * - * @param fmt The format specifier. - * - * @param args The arguments list. - * - * @return true if successful. - */ -bool bsg_ksfuwriteFmtArgsToFD(const int fd, const char *fmt, va_list args); - -/** Read a single line from a file. - * - * @param fd The file descriptor. - * - * @param buffer The buffer to read into. - * - * @param maxLength The maximum length to read. - * - * @return The number of bytes read. - */ -ssize_t bsg_ksfureadLineFromFD(const int fd, char *buffer, int maxLength); - #ifdef __cplusplus } #endif diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h new file mode 100644 index 00000000..339d67d1 --- /dev/null +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h @@ -0,0 +1,129 @@ +// +// BSG_KSMachHeaders.h +// Bugsnag +// +// Created by Robin Macharg on 04/05/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#ifndef BSG_KSMachHeaders_h +#define BSG_KSMachHeaders_h + +#import + +/** + * An encapsulation of the Mach header - either 64 or 32 bit, along with some additional information required for + * detailing a crash report's binary images. + */ +typedef struct { + const struct mach_header *header; /* The mach_header - 32 or 64 bit */ + uint64_t imageVmAddr; + uint64_t imageSize; + uint8_t *uuid; + const char* name; + intptr_t slide; +} BSG_Mach_Binary_Image_Info; + +/** + * MARK: - A Dynamic array container + * See: https://stackoverflow.com/a/3536261/2431627 + */ +typedef struct { + uint32_t used; + uint32_t size; + BSG_Mach_Binary_Image_Info *contents; +} BSG_Mach_Binary_Images; + +static BSG_Mach_Binary_Images bsg_mach_binary_images; + +/** + * An OS-version-specific lock, used to synchronise access to the array of binary image info. + * + * os_unfair_lock is available from specific OS versions onwards: + * https://developer.apple.com/documentation/os/os_unfair_lock + * + * It deprecates OSSpinLock: + * https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/spinlock.3.html + * + * The #defined BSG_DYLD_CACHE_LOCK/UNLOCK avoid spurious warnings on later OSs. + */ + +#if defined(__IPHONE_10_0) || defined(__MAC_10_12) || defined(__TVOS_10_0) || defined(__WATCHOS_3_0) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunguarded-availability" + + #import + static os_unfair_lock bsg_mach_binary_images_access_lock = OS_UNFAIR_LOCK_INIT; + + #ifndef BSG_DYLD_CACHE_LOCK + #define BSG_DYLD_CACHE_LOCK \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \ + os_unfair_lock_lock(&bsg_mach_binary_images_access_lock); \ + _Pragma("clang diagnostic pop") + #endif + + #ifndef BSG_DYLD_CACHE_UNLOCK + #define BSG_DYLD_CACHE_UNLOCK \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wunguarded-availability\"") \ + os_unfair_lock_unlock(&bsg_mach_binary_images_access_lock); \ + _Pragma("clang diagnostic pop") + #endif + + #pragma clang diagnostic pop +#else + #import + static OSSpinLock bsg_mach_binary_images_access_lock = OS_SPINLOCK_INIT; + + #ifndef BSG_DYLD_CACHE_LOCK + #define BSG_DYLD_CACHE_LOCK OSSpinLockLock(&bsg_mach_binary_images_access_lock); + #endif + + #ifndef BSG_DYLD_CACHE_UNLOCK + #define BSG_DYLD_CACHE_UNLOCK OSSpinLockUnlock(&bsg_mach_binary_images_access_lock); + #endif +#endif + +// MARK: - Replicate the DYLD API + +/** + * Returns the current number of images mapped in by dyld + */ +uint32_t bsg_dyld_image_count(void); + +/** + * Returns a pointer to the mach header of the image indexed by image_index. If imageIndex + * is out of range, NULL is returned. + */ +const struct mach_header* bsg_dyld_get_image_header(uint32_t imageIndex); + +/** + * Returns the virtural memory address slide amount of the image indexed by imageIndex. + * If image_index is out of range zero is returned. + */ +intptr_t bsg_dyld_get_image_vmaddr_slide(uint32_t imageIndex); + +/** + * Returns the name of the image indexed by imageIndex. + */ +const char* bsg_dyld_get_image_name(uint32_t imageIndex); + +BSG_Mach_Binary_Image_Info *bsg_dyld_get_image_info(uint32_t imageIndex); // An additional convenience function + +/** + * Called when a binary image is loaded. + */ +void bsg_mach_binary_image_added(const struct mach_header *mh, intptr_t slide); + +/** + * Called when a binary image is unloaded. + */ +void bsg_mach_binary_image_removed(const struct mach_header *mh, intptr_t slide); + +/** + * Create an empty array with initial capacity to hold Mach header info. + */ +BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize); + +#endif /* BSG_KSMachHeaders_h */ diff --git a/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m new file mode 100644 index 00000000..c9a86754 --- /dev/null +++ b/cocoa/vendor/bugsnag-cocoa/Source/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.m @@ -0,0 +1,216 @@ +// +// BSG_KSMachHeaders.m +// Bugsnag +// +// Created by Robin Macharg on 04/05/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import +#import +#import +#import "BSG_KSDynamicLinker.h" +#import "BSG_KSMachHeaders.h" + +// MARK: - Replicate the DYLD API + +uint32_t bsg_dyld_image_count(void) { + return bsg_mach_binary_images.used; +} + +const struct mach_header* bsg_dyld_get_image_header(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return bsg_mach_binary_images.contents[imageIndex].header; + } + return NULL; +} + +intptr_t bsg_dyld_get_image_vmaddr_slide(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return bsg_mach_binary_images.contents[imageIndex].slide; + } + return 0; +} + +const char* bsg_dyld_get_image_name(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return bsg_mach_binary_images.contents[imageIndex].name; + } + return NULL; +} + +BSG_Mach_Binary_Image_Info *bsg_dyld_get_image_info(uint32_t imageIndex) { + if (imageIndex < bsg_mach_binary_images.used) { + return &bsg_mach_binary_images.contents[imageIndex]; + } + return NULL; +} + +/** + * Store a Mach binary image-excapsulating struct in a dynamic array. + * The array doubles on filling-up. Typical sizes is expected to be in < 1000 (i.e. 2-3 doublings, at app start-up) + * This should be called in a threadsafe way; we lock against a simultaneous add and remove. + */ +void bsg_add_mach_binary_image(BSG_Mach_Binary_Image_Info element) { + + BSG_DYLD_CACHE_LOCK + + // Expand array if necessary. We're slightly paranoid here. An OOM is likely to be indicative of bigger problems + // but we should still do *our* best not to crash the app. + if (bsg_mach_binary_images.used == bsg_mach_binary_images.size) { + uint32_t newSize = bsg_mach_binary_images.size *= 2; + uint32_t newAllocationSize = newSize * sizeof(BSG_Mach_Binary_Image_Info); + errno = 0; + BSG_Mach_Binary_Image_Info *newAllocation = (BSG_Mach_Binary_Image_Info *)realloc(bsg_mach_binary_images.contents, newAllocationSize); + + if (newAllocation != NULL && errno != ENOMEM) { + bsg_mach_binary_images.size = newSize; + bsg_mach_binary_images.contents = newAllocation; + } + else { + // Exit early, don't expand the array, don't store the header info and unlock + BSG_DYLD_CACHE_UNLOCK + return; + } + } + + // Store the value, increment the number of used elements + bsg_mach_binary_images.contents[bsg_mach_binary_images.used++] = element; + + BSG_DYLD_CACHE_UNLOCK +} + +/** + * Binary images can only be loaded at most once. We can use the VMAddress as a key, without needing to compare the + * other fields. Element order is not important; deletion is accomplished by copying the last item into the deleted + * position. + */ +void bsg_remove_mach_binary_image(uint64_t imageVmAddr) { + + BSG_DYLD_CACHE_LOCK + + for (uint32_t i=0; ilast. + if (bsg_mach_binary_images.used >= 2) { + bsg_mach_binary_images.contents[i] = bsg_mach_binary_images.contents[--bsg_mach_binary_images.used]; + } + else { + bsg_mach_binary_images.used = 0; + } + break; // an image can only be loaded singularly; exit loop once found + } + } + + BSG_DYLD_CACHE_UNLOCK +} + +BSG_Mach_Binary_Images *bsg_initialise_mach_binary_headers(uint32_t initialSize) { + bsg_mach_binary_images.contents = (BSG_Mach_Binary_Image_Info *)malloc(initialSize * sizeof(BSG_Mach_Binary_Image_Info)); + bsg_mach_binary_images.used = 0; + bsg_mach_binary_images.size = initialSize; + return &bsg_mach_binary_images; +} + +/** + * Populate a Mach binary image info structure + * + * @param header The Mach binary image header + * + * @param info Encapsulated Binary Image info + * + * @returns a boolean indicating success + */ +bool bsg_populate_mach_image_info(const struct mach_header *header, intptr_t slide, BSG_Mach_Binary_Image_Info *info) { + + // Early exit conditions; this is not a valid/useful binary image + // 1. We can't find a sensible Mach command + uintptr_t cmdPtr = bsg_ksdlfirstCmdAfterHeader(header); + if (cmdPtr == 0) { + return false; + } + + // 2. The image doesn't have a name. Note: running with a debugger attached causes this condition to match. + Dl_info DlInfo = (const Dl_info) { 0 }; + dladdr(header, &DlInfo); + const char *imageName = DlInfo.dli_fname; + if (!imageName) { + return false; + } + + // Look for the TEXT segment to get the image size. + // Also look for a UUID command. + uint64_t imageSize = 0; + uint64_t imageVmAddr = 0; + uint8_t *uuid = NULL; + + for (uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) { + struct load_command *loadCmd = (struct load_command *)cmdPtr; + switch (loadCmd->cmd) { + case LC_SEGMENT: { + struct segment_command *segCmd = (struct segment_command *)cmdPtr; + if (strcmp(segCmd->segname, SEG_TEXT) == 0) { + imageSize = segCmd->vmsize; + imageVmAddr = segCmd->vmaddr; + } + break; + } + case LC_SEGMENT_64: { + struct segment_command_64 *segCmd = + (struct segment_command_64 *)cmdPtr; + if (strcmp(segCmd->segname, SEG_TEXT) == 0) { + imageSize = segCmd->vmsize; + imageVmAddr = segCmd->vmaddr; + } + break; + } + case LC_UUID: { + struct uuid_command *uuidCmd = (struct uuid_command *)cmdPtr; + uuid = uuidCmd->uuid; + break; + } + } + cmdPtr += loadCmd->cmdsize; + } + + // Save these values + info->header = header; + info->imageSize = imageSize; + info->imageVmAddr = imageVmAddr; + info->uuid = uuid; + info->name = imageName; + info->slide = slide; + + return true; +} + +/** + * A callback invoked when dyld loads binary images. It stores enough relevant info about the + * image to populate a crash report later. + * + * @param header A mach_header structure + * + * @param slide A virtual memory slide amount. The virtual memory slide amount specifies the difference between the + * address at which the image was linked and the address at which the image is loaded. + */ +void bsg_mach_binary_image_added(const struct mach_header *header, intptr_t slide) +{ + BSG_Mach_Binary_Image_Info info = { 0 }; + if (bsg_populate_mach_image_info(header, slide, &info)) { + bsg_add_mach_binary_image(info); + } +} + +/** + * Called when a binary image is unloaded. + */ +void bsg_mach_binary_image_removed(const struct mach_header *header, intptr_t slide) +{ + // Convert header into an info struct + BSG_Mach_Binary_Image_Info info = { 0 }; + if (bsg_populate_mach_image_info(header, slide, &info)) { + bsg_remove_mach_binary_image(info.imageVmAddr); + } +} diff --git a/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj b/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj index 014d1dcb..5da86599 100644 --- a/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/cocoa/vendor/bugsnag-cocoa/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 0003F3C52461B29A00AE0C93 /* BSG_KSMachHeaders.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */; }; + 0071CB4B2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */; }; + 0071CB4C2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */; }; + 0071CB4F246025AF00F562B1 /* KSMachHeader_Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0071CB4E246025AF00F562B1 /* KSMachHeader_Tests.m */; }; + 0071CB502460705D00F562B1 /* BSG_KSMachHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */; }; 4B47970A22A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47970922A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m */; }; 4B775FCF22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; 4BE6C42622CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE6C42522CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m */; }; @@ -323,6 +328,7 @@ dstPath = "include/${PRODUCT_NAME}"; dstSubfolderSpec = 16; files = ( + 0003F3C52461B29A00AE0C93 /* BSG_KSMachHeaders.h in CopyFiles */, 8A3C590923965D9400B344AA /* BugsnagPlugin.h in CopyFiles */, E79148251FD828E6003EFEBF /* BugsnagKeys.h in CopyFiles */, E79148261FD828E6003EFEBF /* BugsnagSessionTracker.h in CopyFiles */, @@ -397,6 +403,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSG_KSMachHeaders.m; sourceTree = ""; }; + 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSG_KSMachHeaders.h; sourceTree = ""; }; + 0071CB4E246025AF00F562B1 /* KSMachHeader_Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSMachHeader_Tests.m; sourceTree = ""; }; 4B3B193422CA7B0900475354 /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictSetSafeObjectTest.m; path = ../../Tests/BugsnagCollectionsBSGDictSetSafeObjectTest.m; sourceTree = ""; }; 4B47970922A9AE1F00FF9C2E /* BugsnagCrashReportFromKSCrashReportTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagCrashReportFromKSCrashReportTest.m; sourceTree = ""; }; 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictInsertIfNotNilTest.m; path = ../../Tests/BugsnagCollectionsBSGDictInsertIfNotNilTest.m; sourceTree = ""; }; @@ -771,6 +780,7 @@ E7B970321FD7031500590C27 /* XCTestCase+KSCrash.h */, E7B970331FD7031500590C27 /* XCTestCase+KSCrash.m */, E7B970301FD702DA00590C27 /* KSLogger_Tests.m */, + 0071CB4E246025AF00F562B1 /* KSMachHeader_Tests.m */, ); name = KSCrash; path = ../../Tests/KSCrash; @@ -883,6 +893,8 @@ E7107C011F4C97F100BB3F98 /* BSG_KSMach_x86_32.c */, E7107C021F4C97F100BB3F98 /* BSG_KSMach_x86_64.c */, E7107C031F4C97F100BB3F98 /* BSG_KSMachApple.h */, + 0071CB4A2460213200F562B1 /* BSG_KSMachHeaders.m */, + 0071CB4D2460216200F562B1 /* BSG_KSMachHeaders.h */, E7107C041F4C97F100BB3F98 /* BSG_KSObjC.c */, E7107C051F4C97F100BB3F98 /* BSG_KSObjC.h */, E7107C061F4C97F100BB3F98 /* BSG_KSObjCApple.h */, @@ -965,6 +977,7 @@ E7107C451F4C97F100BB3F98 /* BSG_KSCrashType.h in Headers */, E7107C431F4C97F100BB3F98 /* BSG_KSCrashState.h in Headers */, E7107C471F4C97F100BB3F98 /* BSG_KSSystemInfo.h in Headers */, + 0071CB502460705D00F562B1 /* BSG_KSMachHeaders.h in Headers */, E72962D61F4BBA8B00CEA15D /* BugsnagCrashSentry.h in Headers */, E7107C3C1F4C97F100BB3F98 /* BSG_KSCrashReport.h in Headers */, 8A381D4B1EAA49A700AF8429 /* BugsnagLogger.h in Headers */, @@ -1170,6 +1183,7 @@ E72BF7801FC86A7A004BE82F /* BugsnagUser.m in Sources */, 8A2C8F541C6BBE3C00846019 /* BugsnagCollections.m in Sources */, E7107C501F4C97F100BB3F98 /* BSG_KSCrashSentry_MachException.c in Sources */, + 0071CB4B2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */, E7107C5F1F4C97F100BB3F98 /* BSG_KSDynamicLinker.c in Sources */, E737DEA31F73AD7400BC7C80 /* BugsnagHandledState.m in Sources */, E72962D71F4BBA8B00CEA15D /* BugsnagCrashSentry.m in Sources */, @@ -1218,6 +1232,7 @@ E784D25A1FD70C25004B01E1 /* KSJSONCodec_Tests.m in Sources */, E70EE0881FD7047800FA745C /* KSSystemInfo_Tests.m in Sources */, 4BE6C42622CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */, + 0071CB4F246025AF00F562B1 /* KSMachHeader_Tests.m in Sources */, E70EE0871FD7047800FA745C /* KSSysCtl_Tests.m in Sources */, E78C1EF31FCC615400B976D3 /* BugsnagSessionTrackingPayloadTest.m in Sources */, E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */, @@ -1277,6 +1292,7 @@ E72BF7811FC86A7A004BE82F /* BugsnagUser.m in Sources */, E7397E291F83BC2A0034242A /* Bugsnag.m in Sources */, E7397E2A1F83BC2A0034242A /* BugsnagBreadcrumb.m in Sources */, + 0071CB4C2460213200F562B1 /* BSG_KSMachHeaders.m in Sources */, E7397E2B1F83BC2A0034242A /* BugsnagCollections.m in Sources */, E7397E2C1F83BC2A0034242A /* BugsnagConfiguration.m in Sources */, E7397E2D1F83BC2A0034242A /* BugsnagCrashReport.m in Sources */, diff --git a/cocoa/vendor/bugsnag-cocoa/iOS/BugsnagTests/BSGOutOfMemoryWatchdogTests.m b/cocoa/vendor/bugsnag-cocoa/iOS/BugsnagTests/BSGOutOfMemoryWatchdogTests.m index 7f4784be..4136f493 100644 --- a/cocoa/vendor/bugsnag-cocoa/iOS/BugsnagTests/BSGOutOfMemoryWatchdogTests.m +++ b/cocoa/vendor/bugsnag-cocoa/iOS/BugsnagTests/BSGOutOfMemoryWatchdogTests.m @@ -1,19 +1,73 @@ - +#import #import "BSGOutOfMemoryWatchdog.h" #import "BSG_KSSystemInfo.h" #import "BugsnagConfiguration.h" -#import +#import "Bugsnag.h" +#import "BugsnagNotifier.h" -@interface BSGOutOfMemoryWatchdogTests : XCTestCase +// Expose private identifiers for testing + +@interface Bugsnag (Testing) ++ (BugsnagNotifier *)notifier; +@end + +@interface BugsnagNotifier (Testing) +@property (nonatomic, strong) BSGOutOfMemoryWatchdog *oomWatchdog; +@end +@interface BSGOutOfMemoryWatchdog (Testing) +- (NSMutableDictionary *)generateCacheInfoWithConfig:(BugsnagConfiguration *)config; +@property(nonatomic, strong, readwrite) NSMutableDictionary *cachedFileInfo; +@end + +@interface BSGOutOfMemoryWatchdogTests : XCTestCase @end @implementation BSGOutOfMemoryWatchdogTests +- (void)setUp { + [super setUp]; + BugsnagConfiguration *config = [BugsnagConfiguration new]; + config.autoDetectErrors = NO; + config.apiKey = @"apiKeyHere"; + config.codeBundleId = @"codeBundleIdHere"; + config.releaseStage = @"MagicalTestingTime"; + + [Bugsnag startBugsnagWithConfiguration:config]; +} + - (void)testNilPathDoesNotCreateWatchdog { XCTAssertNil([[BSGOutOfMemoryWatchdog alloc] init]); XCTAssertNil([[BSGOutOfMemoryWatchdog alloc] initWithSentinelPath:nil configuration:nil]); } +/** + * Test that the generated OOM report values exist and are correct (where that can be tested) + */ +- (void)testOOMFieldsSetCorrectly { + NSMutableDictionary *cachedFileInfo = [[[Bugsnag notifier] oomWatchdog] cachedFileInfo]; + XCTAssertNotNil([cachedFileInfo objectForKey:@"app"]); + XCTAssertNotNil([cachedFileInfo objectForKey:@"device"]); + + NSMutableDictionary *app = [cachedFileInfo objectForKey:@"app"]; + XCTAssertNotNil([app objectForKey:@"bundleVersion"]); + XCTAssertNotNil([app objectForKey:@"id"]); + XCTAssertNotNil([app objectForKey:@"inForeground"]); + XCTAssertNotNil([app objectForKey:@"version"]); + XCTAssertNotNil([app objectForKey:@"name"]); + XCTAssertEqual([app valueForKey:@"codeBundleId"], @"codeBundleIdHere"); + XCTAssertEqual([app valueForKey:@"releaseStage"], @"MagicalTestingTime"); + + NSMutableDictionary *device = [cachedFileInfo objectForKey:@"device"]; + XCTAssertNotNil([device objectForKey:@"osName"]); + XCTAssertNotNil([device objectForKey:@"osBuild"]); + XCTAssertNotNil([device objectForKey:@"osVersion"]); + XCTAssertNotNil([device objectForKey:@"id"]); + XCTAssertNotNil([device objectForKey:@"model"]); + XCTAssertNotNil([device objectForKey:@"simulator"]); + XCTAssertNotNil([device objectForKey:@"wordSize"]); + XCTAssertEqual([device valueForKey:@"locale"], [[NSLocale currentLocale] localeIdentifier]); +} + @end diff --git a/package.json b/package.json index ccf7c214..a6e46101 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "homepage": "https://www.bugsnag.com/platforms/react-native-error-reporting/", "repository": "https://github.com/bugsnag/bugsnag-react-native.git", "bugs": "https://github.com/bugsnag/bugsnag-react-native/issues", - "version": "2.23.7", + "version": "2.23.8", "license": "MIT", "main": "index.js", "types": "index.d.ts",