AutorÃa | Ultima modificación | Ver Log |
// Copyright 2019 Google//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.#include "Crashlytics/Crashlytics/Components/FIRCLSContext.h"#include <stdlib.h>#include <string.h>#import "Crashlytics/Shared/FIRCLSConstants.h"#import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"#import "Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h"#import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"#import "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"#include "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"#include "Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h"#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"#include "Crashlytics/Crashlytics/Components/FIRCLSProcess.h"#include "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"// The writable size is our handler stack plus whatever scratch we need. We have to use this space// extremely carefully, however, because thread stacks always needs to be page-aligned. Only the// first allocation is gauranteed to be page-aligned.//// CLS_SIGNAL_HANDLER_STACK_SIZE and CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE are platform dependant,// defined as 0 for tv/watch.#define CLS_MINIMUM_READWRITE_SIZE \(CLS_SIGNAL_HANDLER_STACK_SIZE + CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE + \sizeof(FIRCLSReadWriteContext))// We need enough space here for the context, plus storage for strings.#define CLS_MINIMUM_READABLE_SIZE (sizeof(FIRCLSReadOnlyContext) + 4096 * 4)static const int64_t FIRCLSContextInitWaitTime = 5LL * NSEC_PER_SEC;static bool FIRCLSContextRecordMetadata(const char* path, const FIRCLSContextInitData* initData);static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component);static void FIRCLSContextAllocate(FIRCLSContext* context);FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report,FIRCLSSettings* settings,FIRCLSFileManager* fileManager) {// Because we need to start the crash reporter right away,// it starts up either with default settings, or cached settings// from the last time they were fetchedFIRCLSContextInitData initData;memset(&initData, 0, sizeof(FIRCLSContextInitData));initData.customBundleId = nil;initData.sessionId = [[report identifier] UTF8String];initData.rootPath = [[report path] UTF8String];initData.previouslyCrashedFileRootPath = [[fileManager rootPath] UTF8String];initData.errorsEnabled = [settings errorReportingEnabled];initData.customExceptionsEnabled = [settings customExceptionsEnabled];initData.maxCustomExceptions = [settings maxCustomExceptions];initData.maxErrorLogSize = [settings errorLogBufferSize];initData.maxLogSize = [settings logBufferSize];initData.maxKeyValues = [settings maxCustomKeys];initData.betaToken = "";return initData;}bool FIRCLSContextInitialize(FIRCLSInternalReport* report,FIRCLSSettings* settings,FIRCLSFileManager* fileManager) {FIRCLSContextInitData initDataObj = FIRCLSContextBuildInitData(report, settings, fileManager);FIRCLSContextInitData* initData = &initDataObj;if (!initData) {return false;}FIRCLSContextBaseInit();dispatch_group_t group = dispatch_group_create();dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);if (!FIRCLSIsValidPointer(initData->rootPath)) {return false;}NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath];// setup our SDK log file synchronously, because other calls may depend on it_firclsContext.readonly->logPath = FIRCLSContextAppendToRoot(rootPath, @"sdk.log");_firclsContext.readonly->initialReportPath = FIRCLSDupString([report.path UTF8String]);if (!FIRCLSUnlinkIfExists(_firclsContext.readonly->logPath)) {FIRCLSErrorLog(@"Unable to write initialize SDK write paths %s", strerror(errno));}// some values that aren't tied to particular subsystem_firclsContext.readonly->debuggerAttached = FIRCLSProcessDebuggerAttached();dispatch_group_async(group, queue, ^{FIRCLSHostInitialize(&_firclsContext.readonly->host);});dispatch_group_async(group, queue, ^{_firclsContext.readonly->logging.errorStorage.maxSize = 0;_firclsContext.readonly->logging.errorStorage.maxEntries =initData->errorsEnabled ? initData->maxCustomExceptions : 0;_firclsContext.readonly->logging.errorStorage.restrictBySize = false;_firclsContext.readonly->logging.errorStorage.entryCount =&_firclsContext.writable->logging.errorsCount;_firclsContext.readonly->logging.errorStorage.aPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportErrorAFile);_firclsContext.readonly->logging.errorStorage.bPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportErrorBFile);_firclsContext.readonly->logging.logStorage.maxSize = initData->maxLogSize;_firclsContext.readonly->logging.logStorage.maxEntries = 0;_firclsContext.readonly->logging.logStorage.restrictBySize = true;_firclsContext.readonly->logging.logStorage.entryCount = NULL;_firclsContext.readonly->logging.logStorage.aPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportLogAFile);_firclsContext.readonly->logging.logStorage.bPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportLogBFile);_firclsContext.readonly->logging.customExceptionStorage.aPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportCustomExceptionAFile);_firclsContext.readonly->logging.customExceptionStorage.bPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportCustomExceptionBFile);_firclsContext.readonly->logging.customExceptionStorage.maxSize = 0;_firclsContext.readonly->logging.customExceptionStorage.restrictBySize = false;_firclsContext.readonly->logging.customExceptionStorage.maxEntries =initData->maxCustomExceptions;_firclsContext.readonly->logging.customExceptionStorage.entryCount =&_firclsContext.writable->exception.customExceptionCount;_firclsContext.readonly->logging.userKVStorage.maxCount = initData->maxKeyValues;_firclsContext.readonly->logging.userKVStorage.incrementalPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportUserIncrementalKVFile);_firclsContext.readonly->logging.userKVStorage.compactedPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportUserCompactedKVFile);_firclsContext.readonly->logging.internalKVStorage.maxCount = 32; // Hardcode = bad_firclsContext.readonly->logging.internalKVStorage.incrementalPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportInternalIncrementalKVFile);_firclsContext.readonly->logging.internalKVStorage.compactedPath =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportInternalCompactedKVFile);FIRCLSUserLoggingInit(&_firclsContext.readonly->logging, &_firclsContext.writable->logging);});dispatch_group_async(group, queue, ^{_firclsContext.readonly->binaryimage.path =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportBinaryImageFile);FIRCLSBinaryImageInit(&_firclsContext.readonly->binaryimage,&_firclsContext.writable->binaryImage);});dispatch_group_async(group, queue, ^{NSString* rootPath = [NSString stringWithUTF8String:initData->previouslyCrashedFileRootPath];NSString* fileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName];_firclsContext.readonly->previouslyCrashedFileFullPath =FIRCLSContextAppendToRoot(rootPath, fileName);});// To initialize Crashlytics handlers even if the Xcode debugger is attached, replace this check// with YES. Note that this is only possible to do on an actual device as it will cause the// simulator to crash.if (!_firclsContext.readonly->debuggerAttached) {#if CLS_SIGNAL_SUPPORTEDdispatch_group_async(group, queue, ^{_firclsContext.readonly->signal.path =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportSignalFile);FIRCLSSignalInitialize(&_firclsContext.readonly->signal);});#endif#if CLS_MACH_EXCEPTION_SUPPORTEDdispatch_group_async(group, queue, ^{_firclsContext.readonly->machException.path =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile);FIRCLSMachExceptionInit(&_firclsContext.readonly->machException);});#endifdispatch_group_async(group, queue, ^{_firclsContext.readonly->exception.path =FIRCLSContextAppendToRoot(rootPath, FIRCLSReportExceptionFile);_firclsContext.readonly->exception.maxCustomExceptions =initData->customExceptionsEnabled ? initData->maxCustomExceptions : 0;FIRCLSExceptionInitialize(&_firclsContext.readonly->exception,&_firclsContext.writable->exception);});} else {FIRCLSSDKLog("Debugger present - not installing handlers\n");}dispatch_group_async(group, queue, ^{const char* metaDataPath = [[rootPath stringByAppendingPathComponent:FIRCLSReportMetadataFile]fileSystemRepresentation];if (!FIRCLSContextRecordMetadata(metaDataPath, initData)) {FIRCLSSDKLog("Unable to record context metadata\n");}});// At this point we need to do two things. First, we need to do our memory protection *only* after// all of these initialization steps are really done. But, we also want to wait as long as// possible for these to be complete. If we do not, there's a chance that we will not be able to// correctly report a crash shortly after start.// Note at this will retain the group, so its totally fine to release the group here.dispatch_group_notify(group, queue, ^{_firclsContext.readonly->initialized = true;__sync_synchronize();if (!FIRCLSAllocatorProtect(_firclsContext.allocator)) {FIRCLSSDKLog("Error: Memory protection failed\n");}});if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, FIRCLSContextInitWaitTime)) !=0) {FIRCLSSDKLog("Error: Delayed initialization\n");}return true;}void FIRCLSContextBaseInit(void) {NSString* sdkBundleID = FIRCLSApplicationGetSDKBundleID();NSString* loggingQueueName = [sdkBundleID stringByAppendingString:@".logging"];NSString* binaryImagesQueueName = [sdkBundleID stringByAppendingString:@".binary-images"];NSString* exceptionQueueName = [sdkBundleID stringByAppendingString:@".exception"];_firclsLoggingQueue = dispatch_queue_create([loggingQueueName UTF8String], DISPATCH_QUEUE_SERIAL);_firclsBinaryImageQueue =dispatch_queue_create([binaryImagesQueueName UTF8String], DISPATCH_QUEUE_SERIAL);_firclsExceptionQueue =dispatch_queue_create([exceptionQueueName UTF8String], DISPATCH_QUEUE_SERIAL);FIRCLSContextAllocate(&_firclsContext);_firclsContext.writable->internalLogging.logFd = -1;_firclsContext.writable->internalLogging.logLevel = FIRCLSInternalLogLevelDebug;_firclsContext.writable->crashOccurred = false;_firclsContext.readonly->initialized = false;__sync_synchronize();}static void FIRCLSContextAllocate(FIRCLSContext* context) {// create the allocator, and the contexts// The ordering here is really important, because the "stack" variable must be// page-aligned. There's no mechanism to ask the allocator to do alignment, but we// do know the very first allocation in a region is aligned to a page boundary.context->allocator = FIRCLSAllocatorCreate(CLS_MINIMUM_READWRITE_SIZE, CLS_MINIMUM_READABLE_SIZE);context->readonly =FIRCLSAllocatorSafeAllocate(context->allocator, sizeof(FIRCLSReadOnlyContext), CLS_READONLY);memset(context->readonly, 0, sizeof(FIRCLSReadOnlyContext));#if CLS_MEMORY_PROTECTION_ENABLED#if CLS_MACH_EXCEPTION_SUPPORTEDcontext->readonly->machStack = FIRCLSAllocatorSafeAllocate(context->allocator, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE, CLS_READWRITE);#endif#if CLS_USE_SIGALTSTACKcontext->readonly->signalStack =FIRCLSAllocatorSafeAllocate(context->allocator, CLS_SIGNAL_HANDLER_STACK_SIZE, CLS_READWRITE);#endif#else#if CLS_MACH_EXCEPTION_SUPPORTEDcontext->readonly->machStack = valloc(CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE);#endif#if CLS_USE_SIGALTSTACKcontext->readonly->signalStack = valloc(CLS_SIGNAL_HANDLER_STACK_SIZE);#endif#endif#if CLS_MACH_EXCEPTION_SUPPORTEDmemset(_firclsContext.readonly->machStack, 0, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE);#endif#if CLS_USE_SIGALTSTACKmemset(_firclsContext.readonly->signalStack, 0, CLS_SIGNAL_HANDLER_STACK_SIZE);#endifcontext->writable = FIRCLSAllocatorSafeAllocate(context->allocator,sizeof(FIRCLSReadWriteContext), CLS_READWRITE);memset(context->writable, 0, sizeof(FIRCLSReadWriteContext));}void FIRCLSContextBaseDeinit(void) {_firclsContext.readonly->initialized = false;FIRCLSAllocatorDestroy(_firclsContext.allocator);}bool FIRCLSContextIsInitialized(void) {__sync_synchronize();if (!FIRCLSIsValidPointer(_firclsContext.readonly)) {return false;}return _firclsContext.readonly->initialized;}bool FIRCLSContextHasCrashed(void) {if (!FIRCLSContextIsInitialized()) {return false;}// we've already run a full barrier above, so this read is okreturn _firclsContext.writable->crashOccurred;}void FIRCLSContextMarkHasCrashed(void) {if (!FIRCLSContextIsInitialized()) {return;}_firclsContext.writable->crashOccurred = true;__sync_synchronize();}bool FIRCLSContextMarkAndCheckIfCrashed(void) {if (!FIRCLSContextIsInitialized()) {return false;}if (_firclsContext.writable->crashOccurred) {return true;}_firclsContext.writable->crashOccurred = true;__sync_synchronize();return false;}static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component) {return FIRCLSDupString([[root stringByAppendingPathComponent:component] fileSystemRepresentation]);}static bool FIRCLSContextRecordIdentity(FIRCLSFile* file, const FIRCLSContextInitData* initData) {FIRCLSFileWriteSectionStart(file, "identity");FIRCLSFileWriteHashStart(file);FIRCLSFileWriteHashEntryString(file, "generator", FIRCLSSDKGeneratorName().UTF8String);FIRCLSFileWriteHashEntryString(file, "display_version", FIRCLSSDKVersion().UTF8String);FIRCLSFileWriteHashEntryString(file, "build_version", FIRCLSSDKVersion().UTF8String);FIRCLSFileWriteHashEntryUint64(file, "started_at", time(NULL));FIRCLSFileWriteHashEntryString(file, "session_id", initData->sessionId);// install_id is written into the proto directly. This is only left here to// support Apple Report Converter.FIRCLSFileWriteHashEntryString(file, "install_id", "");FIRCLSFileWriteHashEntryString(file, "beta_token", initData->betaToken);FIRCLSFileWriteHashEntryBoolean(file, "absolute_log_timestamps", true);FIRCLSFileWriteHashEnd(file);FIRCLSFileWriteSectionEnd(file);return true;}static bool FIRCLSContextRecordApplication(FIRCLSFile* file, const char* customBundleId) {FIRCLSFileWriteSectionStart(file, "application");FIRCLSFileWriteHashStart(file);FIRCLSFileWriteHashEntryString(file, "bundle_id",[FIRCLSApplicationGetBundleIdentifier() UTF8String]);FIRCLSFileWriteHashEntryString(file, "custom_bundle_id", customBundleId);FIRCLSFileWriteHashEntryString(file, "build_version",[FIRCLSApplicationGetBundleVersion() UTF8String]);FIRCLSFileWriteHashEntryString(file, "display_version",[FIRCLSApplicationGetShortBundleVersion() UTF8String]);FIRCLSFileWriteHashEntryString(file, "extension_id",[FIRCLSApplicationExtensionPointIdentifier() UTF8String]);FIRCLSFileWriteHashEnd(file);FIRCLSFileWriteSectionEnd(file);return true;}static bool FIRCLSContextRecordMetadata(const char* path, const FIRCLSContextInitData* initData) {if (!FIRCLSUnlinkIfExists(path)) {FIRCLSSDKLog("Unable to unlink existing metadata file %s\n", strerror(errno));}FIRCLSFile file;if (!FIRCLSFileInitWithPath(&file, path, false)) {FIRCLSSDKLog("Unable to open metadata file %s\n", strerror(errno));return false;}if (!FIRCLSContextRecordIdentity(&file, initData)) {FIRCLSSDKLog("Unable to write out identity metadata\n");}if (!FIRCLSHostRecord(&file)) {FIRCLSSDKLog("Unable to write out host metadata\n");}if (!FIRCLSContextRecordApplication(&file, initData->customBundleId)) {FIRCLSSDKLog("Unable to write out application metadata\n");}if (!FIRCLSBinaryImageRecordMainExecutable(&file)) {FIRCLSSDKLog("Unable to write out executable metadata\n");}FIRCLSFileClose(&file);return true;}