[CHORE] Update react-native-firebase (#2336)
* Remove firebase * Install firebase/app * Install analytics * Crashlytics * Android * Fix mocks * Edit scheme to Debug build configuration Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
This commit is contained in:
parent
a00e3c7769
commit
c91cd0b963
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
crashlytics: null
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
apply plugin: "com.android.application"
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: "io.fabric"
|
||||
apply plugin: "com.google.firebase.firebase-perf"
|
||||
apply plugin: 'com.bugsnag.android.gradle'
|
||||
|
||||
import com.android.build.OutputFile
|
||||
|
@ -168,6 +168,9 @@ android {
|
|||
minifyEnabled enableProguardInReleaseBuilds
|
||||
setProguardFiles([getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'])
|
||||
signingConfig signingConfigs.release
|
||||
firebaseCrashlytics {
|
||||
nativeSymbolUploadEnabled true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,11 +218,6 @@ dependencies {
|
|||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
implementation "com.google.firebase:firebase-messaging:18.0.0"
|
||||
implementation "com.google.firebase:firebase-core:16.0.9"
|
||||
implementation "com.google.firebase:firebase-perf:17.0.2"
|
||||
implementation('com.crashlytics.sdk.android:crashlytics:2.9.9@aar') {
|
||||
transitive = true
|
||||
}
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
|
@ -251,5 +249,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
|||
into 'libs'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
|
@ -29,10 +29,6 @@ import com.wix.reactnativenotifications.core.notification.INotificationsApplicat
|
|||
import com.wix.reactnativenotifications.core.notification.IPushNotification;
|
||||
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
|
||||
|
||||
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
|
||||
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;
|
||||
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
|
||||
|
||||
import com.nozbe.watermelondb.WatermelonDBPackage;
|
||||
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
|
||||
|
||||
|
@ -53,9 +49,6 @@ public class MainApplication extends Application implements ReactApplication, IN
|
|||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
packages.add(new RNFirebaseCrashlyticsPackage());
|
||||
packages.add(new RNFirebaseAnalyticsPackage());
|
||||
packages.add(new RNFirebasePerformancePackage());
|
||||
packages.add(new KeyboardInputPackage(MainApplication.this));
|
||||
packages.add(new RNNotificationsPackage(MainApplication.this));
|
||||
packages.add(new WatermelonDBPackage());
|
||||
|
|
|
@ -20,8 +20,7 @@ buildscript {
|
|||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.google.gms:google-services:4.2.0'
|
||||
classpath 'io.fabric.tools:gradle:1.28.1'
|
||||
classpath 'com.google.firebase:perf-plugin:1.2.1'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { Client } from 'bugsnag-react-native';
|
||||
import firebase from 'react-native-firebase';
|
||||
import analytics from '@react-native-firebase/analytics';
|
||||
import crashlytics from '@react-native-firebase/crashlytics';
|
||||
import config from '../../../config';
|
||||
import events from './events';
|
||||
|
||||
const bugsnag = new Client(config.BUGSNAG_API_KEY);
|
||||
|
||||
export const { analytics } = firebase;
|
||||
export { analytics };
|
||||
export const loggerConfig = bugsnag.config;
|
||||
export const { leaveBreadcrumb } = bugsnag;
|
||||
export { events };
|
||||
|
@ -37,6 +38,7 @@ export default (e) => {
|
|||
}
|
||||
};
|
||||
});
|
||||
crashlytics().recordError(e);
|
||||
} else {
|
||||
console.log(e);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ PODS:
|
|||
- React
|
||||
- CocoaAsyncSocket (7.6.4)
|
||||
- CocoaLibEvent (1.0.0)
|
||||
- Crashlytics (3.14.0):
|
||||
- Fabric (~> 1.10.2)
|
||||
- DoubleConversion (1.1.6)
|
||||
- EXAppleAuthentication (2.2.1):
|
||||
- UMCore
|
||||
|
@ -41,7 +39,6 @@ PODS:
|
|||
- UMFileSystemInterface
|
||||
- EXWebBrowser (8.3.1):
|
||||
- UMCore
|
||||
- Fabric (1.10.2)
|
||||
- FBLazyVector (0.63.1)
|
||||
- FBReactNativeSpec (0.63.1):
|
||||
- Folly (= 2020.01.13.00)
|
||||
|
@ -50,11 +47,16 @@ PODS:
|
|||
- React-Core (= 0.63.1)
|
||||
- React-jsi (= 0.63.1)
|
||||
- ReactCommon/turbomodule/core (= 0.63.1)
|
||||
- Firebase/Core (6.28.1):
|
||||
- Firebase/Analytics (6.27.1):
|
||||
- Firebase/Core
|
||||
- Firebase/Core (6.27.1):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseAnalytics (= 6.6.2)
|
||||
- Firebase/CoreOnly (6.28.1):
|
||||
- FirebaseCore (= 6.9.1)
|
||||
- Firebase/CoreOnly (6.27.1):
|
||||
- FirebaseCore (= 6.8.1)
|
||||
- Firebase/Crashlytics (6.27.1):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseCrashlytics (~> 4.2.0)
|
||||
- FirebaseAnalytics (6.6.2):
|
||||
- FirebaseCore (~> 6.8)
|
||||
- FirebaseInstallations (~> 1.4)
|
||||
|
@ -64,15 +66,22 @@ PODS:
|
|||
- GoogleUtilities/Network (~> 6.0)
|
||||
- "GoogleUtilities/NSData+zlib (~> 6.0)"
|
||||
- nanopb (~> 1.30905.0)
|
||||
- FirebaseCore (6.9.1):
|
||||
- FirebaseCore (6.8.1):
|
||||
- FirebaseCoreDiagnostics (~> 1.3)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- FirebaseCoreDiagnostics (1.5.0):
|
||||
- GoogleDataTransport (~> 7.0)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- GoogleUtilities/Environment (~> 6.5)
|
||||
- GoogleUtilities/Logger (~> 6.5)
|
||||
- FirebaseCoreDiagnostics (1.4.0):
|
||||
- GoogleDataTransportCCTSupport (~> 3.1)
|
||||
- GoogleUtilities/Environment (~> 6.5)
|
||||
- GoogleUtilities/Logger (~> 6.5)
|
||||
- nanopb (~> 1.30905.0)
|
||||
- FirebaseCrashlytics (4.2.0):
|
||||
- FirebaseCore (~> 6.8)
|
||||
- FirebaseInstallations (~> 1.1)
|
||||
- GoogleDataTransport (~> 6.1)
|
||||
- GoogleDataTransportCCTSupport (~> 3.1)
|
||||
- nanopb (~> 1.30905.0)
|
||||
- PromisesObjC (~> 1.2)
|
||||
- FirebaseInstallations (1.5.0):
|
||||
- FirebaseCore (~> 6.8)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
|
@ -140,7 +149,9 @@ PODS:
|
|||
- GoogleUtilities/Network (~> 6.0)
|
||||
- "GoogleUtilities/NSData+zlib (~> 6.0)"
|
||||
- nanopb (~> 1.30905.0)
|
||||
- GoogleDataTransport (7.0.0):
|
||||
- GoogleDataTransport (6.2.1)
|
||||
- GoogleDataTransportCCTSupport (3.2.0):
|
||||
- GoogleDataTransport (~> 6.1)
|
||||
- nanopb (~> 1.30905.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (6.7.1):
|
||||
- GoogleUtilities/Environment
|
||||
|
@ -453,15 +464,18 @@ PODS:
|
|||
- React
|
||||
- SDWebImage (~> 5.0)
|
||||
- SDWebImageWebPCoder (~> 0.4.1)
|
||||
- RNFirebase (5.6.0):
|
||||
- Firebase/Core
|
||||
- RNFBAnalytics (7.3.1):
|
||||
- Firebase/Analytics (~> 6.27.0)
|
||||
- React
|
||||
- RNFirebase/Crashlytics (= 5.6.0)
|
||||
- RNFirebase/Crashlytics (5.6.0):
|
||||
- Crashlytics
|
||||
- Fabric
|
||||
- Firebase/Core
|
||||
- RNFBApp
|
||||
- RNFBApp (8.2.0):
|
||||
- Firebase/CoreOnly (~> 6.27.0)
|
||||
- React
|
||||
- RNFBCrashlytics (8.1.2):
|
||||
- Firebase/Core (~> 6.27.0)
|
||||
- Firebase/Crashlytics (~> 6.27.0)
|
||||
- React
|
||||
- RNFBApp
|
||||
- RNGestureHandler (1.6.1):
|
||||
- React
|
||||
- RNImageCropPicker (0.31.1):
|
||||
|
@ -594,7 +608,9 @@ DEPENDENCIES:
|
|||
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
|
||||
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
|
||||
- "RNFastImage (from `../node_modules/@rocket.chat/react-native-fast-image`)"
|
||||
- RNFirebase (from `../node_modules/react-native-firebase/ios`)
|
||||
- "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)"
|
||||
- "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
|
||||
- "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)"
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
|
||||
- RNLocalize (from `../node_modules/react-native-localize`)
|
||||
|
@ -623,12 +639,11 @@ SPEC REPOS:
|
|||
- boost-for-react-native
|
||||
- CocoaAsyncSocket
|
||||
- CocoaLibEvent
|
||||
- Crashlytics
|
||||
- Fabric
|
||||
- Firebase
|
||||
- FirebaseAnalytics
|
||||
- FirebaseCore
|
||||
- FirebaseCoreDiagnostics
|
||||
- FirebaseCrashlytics
|
||||
- FirebaseInstallations
|
||||
- Flipper
|
||||
- Flipper-DoubleConversion
|
||||
|
@ -639,6 +654,7 @@ SPEC REPOS:
|
|||
- FlipperKit
|
||||
- GoogleAppMeasurement
|
||||
- GoogleDataTransport
|
||||
- GoogleDataTransportCCTSupport
|
||||
- GoogleUtilities
|
||||
- JitsiMeetSDK
|
||||
- libwebp
|
||||
|
@ -769,8 +785,12 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-device-info"
|
||||
RNFastImage:
|
||||
:path: "../node_modules/@rocket.chat/react-native-fast-image"
|
||||
RNFirebase:
|
||||
:path: "../node_modules/react-native-firebase/ios"
|
||||
RNFBAnalytics:
|
||||
:path: "../node_modules/@react-native-firebase/analytics"
|
||||
RNFBApp:
|
||||
:path: "../node_modules/@react-native-firebase/app"
|
||||
RNFBCrashlytics:
|
||||
:path: "../node_modules/@react-native-firebase/crashlytics"
|
||||
RNGestureHandler:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNImageCropPicker:
|
||||
|
@ -821,7 +841,6 @@ SPEC CHECKSUMS:
|
|||
BugsnagReactNative: 98fb350df4bb0c94cce903023531a1a5cc11fa51
|
||||
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
|
||||
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
|
||||
Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df
|
||||
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||
EXAppleAuthentication: 5b3da71bada29e2423d8ea27e5538ef0d75aba62
|
||||
EXAV: 86344030966e0da7e00556fbb97269d9ad16071d
|
||||
|
@ -834,13 +853,13 @@ SPEC CHECKSUMS:
|
|||
EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8
|
||||
EXVideoThumbnails: f70bdc5511749f3181028f5000bcb7be203c631d
|
||||
EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4
|
||||
Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74
|
||||
FBLazyVector: a50434c875bd42f2b1c99c712bda892a1dc659c7
|
||||
FBReactNativeSpec: 393853a536428e05a9da00b6290042f09809b15b
|
||||
Firebase: ed042590caa0029392257529a8003c25ee82bc18
|
||||
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
||||
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
||||
FirebaseCore: 687b8e6a0a4337b898a6326d68254c2f80c143af
|
||||
FirebaseCoreDiagnostics: 7535fe695737f8c5b350584292a70b7f8ff0357b
|
||||
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
||||
FirebaseCoreDiagnostics: 4505e4d4009b1d93f605088ee7d7764d5f0d1c84
|
||||
FirebaseCrashlytics: 7d0fa02ea8842cc4b2ab07b0735edafde1ad77d6
|
||||
FirebaseInstallations: 3c520c951305cbf9ca54eb891ff9e6d1fd384881
|
||||
Flipper: 33585e2d9810fe5528346be33bcf71b37bb7ae13
|
||||
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
|
||||
|
@ -852,7 +871,8 @@ SPEC CHECKSUMS:
|
|||
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
|
||||
GoogleAppMeasurement: 8cd1f289d60e629cf16ab03363b9e89c776b9651
|
||||
GoogleDataTransport: 8a40cb194ad242b6f6dfe72c14fe40fc67c4dcd7
|
||||
GoogleDataTransport: 9a8a16f79feffc7f42096743de2a7c4815e84020
|
||||
GoogleDataTransportCCTSupport: 489c1265d2c85b68187a83a911913d190012158d
|
||||
GoogleUtilities: e121a3867449ce16b0e35ddf1797ea7a389ffdf2
|
||||
JitsiMeetSDK: 2984eac1343690bf1c0c72bde75b48b0148d0f79
|
||||
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
|
||||
|
@ -901,7 +921,9 @@ SPEC CHECKSUMS:
|
|||
RNDateTimePicker: e386ff4ef3300964ed0cad97ce6f206e0effbfdb
|
||||
RNDeviceInfo: ed8557a8bd6443cbc0ab5d375e6808a38a279744
|
||||
RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9
|
||||
RNFirebase: 37daa9a346d070f9f6ee1f3b4aaf4c8e3b1d5d1c
|
||||
RNFBAnalytics: dae6d7b280ba61c96e1bbdd34aca3154388f025e
|
||||
RNFBApp: 6fd8a7e757135d4168bf033a8812c241af7363a0
|
||||
RNFBCrashlytics: 88de72c2476b5868a892d9523b89b86c527c540e
|
||||
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
|
||||
RNImageCropPicker: 38865ab4af1b0b2146ad66061196bc0184946855
|
||||
RNLocalize: b6df30cc25ae736d37874f9bce13351db2f56796
|
||||
|
@ -931,4 +953,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: 55c04243097892160d63f79f3a23157165b7ac68
|
||||
|
||||
COCOAPODS: 1.8.4
|
||||
COCOAPODS: 1.9.3
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
We've now combined all our supported platforms into a single podspec. As a result, we moved our submit script to a new location for Cocoapods projects: ${PODS_ROOT}/Crashlytics/submit. To avoid breaking functionality that references the old location of the submit, we've placed this dummy script that calls to the correct location, while providing a helpful warning if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning!
|
|
@ -1,6 +0,0 @@
|
|||
if [[ -z $PODS_ROOT ]]; then
|
||||
echo "error: The submit binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Crashlytics/submit'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the submit binary to fix this issue."
|
||||
else
|
||||
echo "warning: The submit script is now located at '$"{"PODS_ROOT"}"/Crashlytics/submit'. To remove this warning, update your path to point to this new location."
|
||||
sh "${PODS_ROOT}/Crashlytics/submit" "$@"
|
||||
fi
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
# Crashlytics
|
||||
|
||||
## Overview
|
||||
|
||||
[Crashlytics](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) offers the most powerful, yet lightest weight crash reporting solution for iOS.
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
To start using Crashlytics, there are two options:
|
||||
|
||||
1) The recommended way is to go to the [Firebase Crashlytics Docs](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) and follow the directions there.
|
||||
|
||||
2) If you aren't using Firebase yet, go to [Fabric Kits](https://fabric.io/kits), and follow the directions for Crashlytics.
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
* [API Reference](https://firebase.google.com/docs/reference/ios/crashlytics/api/reference/Classes)
|
||||
* [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
|
||||
* [Website](https://firebase.google.com/docs/crashlytics)
|
||||
* Follow us on Twitter: [@crashlytics](https://twitter.com/crashlytics)
|
Binary file not shown.
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// ANSCompatibility.h
|
||||
// AnswersKit
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_generics)
|
||||
#define ANS_GENERIC_NSARRAY(type) NSArray<type>
|
||||
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||
#else
|
||||
#define ANS_GENERIC_NSARRAY(type) NSArray
|
||||
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||
#endif
|
|
@ -1,210 +0,0 @@
|
|||
//
|
||||
// Answers.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ANSCompatibility.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* This class exposes the Answers Events API, allowing you to track key
|
||||
* user user actions and metrics in your app.
|
||||
*/
|
||||
@interface Answers : NSObject
|
||||
|
||||
/**
|
||||
* Log a Sign Up event to see users signing up for your app in real-time, understand how
|
||||
* many users are signing up with different methods and their success rate signing up.
|
||||
*
|
||||
* @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits.
|
||||
* @param signUpSucceededOrNil The ultimate success or failure of the login
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil
|
||||
success:(nullable NSNumber *)signUpSucceededOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Log In event to see users logging into your app in real-time, understand how many
|
||||
* users are logging in with different methods and their success rate logging into your app.
|
||||
*
|
||||
* @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits.
|
||||
* @param loginSucceededOrNil The ultimate success or failure of the login
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil
|
||||
success:(nullable NSNumber *)loginSucceededOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Share event to see users sharing from your app in real-time, letting you
|
||||
* understand what content they're sharing from the type or genre down to the specific id.
|
||||
*
|
||||
* @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS.
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil
|
||||
contentName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Invite Event to track how users are inviting other users into
|
||||
* your application.
|
||||
*
|
||||
* @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which
|
||||
* items are most popular, and track plenty of other important purchase-related metrics.
|
||||
*
|
||||
* @param itemPriceOrNil The purchased item's price.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param purchaseSucceededOrNil Was the purchase successful or unsuccessful
|
||||
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase.
|
||||
*/
|
||||
+ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
success:(nullable NSNumber *)purchaseSucceededOrNil
|
||||
itemName:(nullable NSString *)itemNameOrNil
|
||||
itemType:(nullable NSString *)itemTypeOrNil
|
||||
itemId:(nullable NSString *)itemIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Level Start Event to track where users are in your game.
|
||||
*
|
||||
* @param levelNameOrNil The level name
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event.
|
||||
*/
|
||||
+ (void)logLevelStart:(nullable NSString *)levelNameOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Level End event to track how users are completing levels in your game.
|
||||
*
|
||||
* @param levelNameOrNil The name of the level completed, E.G. "1" or "Training"
|
||||
* @param scoreOrNil The score the user completed the level with.
|
||||
* @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed successfully.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logLevelEnd:(nullable NSString *)levelNameOrNil
|
||||
score:(nullable NSNumber *)scoreOrNil
|
||||
success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how
|
||||
* many users start the purchase flow, see which items are most popular, and track plenty of other important
|
||||
* purchase-related metrics.
|
||||
*
|
||||
* @param itemPriceOrNil The purchased item's price.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
itemName:(nullable NSString *)itemNameOrNil
|
||||
itemType:(nullable NSString *)itemTypeOrNil
|
||||
itemId:(nullable NSString *)itemIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many
|
||||
* users are doing this and how much they're spending per checkout, and see how it related to other important
|
||||
* purchase-related metrics.
|
||||
*
|
||||
* @param totalPriceOrNil The total price of the cart.
|
||||
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||
* @param itemCountOrNil The number of items in the cart.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil
|
||||
currency:(nullable NSString *)currencyOrNil
|
||||
itemCount:(nullable NSNumber *)itemCountOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Rating event to see users rating content within your app in real-time and understand what
|
||||
* content is most engaging, from the type or genre down to the specific id.
|
||||
*
|
||||
* @param ratingOrNil The integer rating given by the user.
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logRating:(nullable NSNumber *)ratingOrNil
|
||||
contentName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Content View event to see users viewing content within your app in real-time and
|
||||
* understand what content is most engaging, from the type or genre down to the specific id.
|
||||
*
|
||||
* @param contentNameOrNil The human readable name for this piece of content.
|
||||
* @param contentTypeOrNil The type of content shared.
|
||||
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil
|
||||
contentType:(nullable NSString *)contentTypeOrNil
|
||||
contentId:(nullable NSString *)contentIdOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Search event allows you to see users searching within your app in real-time and understand
|
||||
* exactly what they're searching for.
|
||||
*
|
||||
* @param queryOrNil The user's query.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||
*/
|
||||
+ (void)logSearchWithQuery:(nullable NSString *)queryOrNil
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
/**
|
||||
* Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often
|
||||
* they're performing these actions with breakdowns by different categories you add. Use a human-readable name for
|
||||
* the name of the event, since this is how the event will appear in Answers.
|
||||
*
|
||||
* @param eventName The human-readable name for the event.
|
||||
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys
|
||||
* must be <code>NSString</code> and values must be <code>NSNumber</code> or <code>NSString</code>.
|
||||
* @discussion How we treat <code>NSNumbers</code>:
|
||||
* We will provide information about the distribution of values over time.
|
||||
*
|
||||
* How we treat <code>NSStrings</code>:
|
||||
* NSStrings are used as categorical data, allowing comparison across different category values.
|
||||
* Strings are limited to a maximum length of 100 characters, attributes over this length will be
|
||||
* truncated.
|
||||
*
|
||||
* When tracking the Tweet views to better understand user engagement, sending the tweet's length
|
||||
* and the type of media present in the tweet allows you to track how tweet length and the type of media influence
|
||||
* engagement.
|
||||
*/
|
||||
+ (void)logCustomEventWithName:(NSString *)eventName
|
||||
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,33 +0,0 @@
|
|||
//
|
||||
// CLSAttributes.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x)))
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
#if __has_feature(objc_generics)
|
||||
#define CLS_GENERIC_NSARRAY(type) NSArray<type>
|
||||
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||
#else
|
||||
#define CLS_GENERIC_NSARRAY(type) NSArray
|
||||
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// CLSLogging.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
#ifdef __OBJC__
|
||||
#import "CLSAttributes.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
|
||||
*
|
||||
* Example output:
|
||||
* -[AppDelegate login:] line 134 $ login start
|
||||
*
|
||||
* If you would like to change this macro, create a new header file, unset our define and then define
|
||||
* your own version. Make sure this new header file is imported after the Crashlytics header file.
|
||||
*
|
||||
* #undef CLS_LOG
|
||||
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
|
||||
*
|
||||
**/
|
||||
#ifdef __OBJC__
|
||||
#ifdef DEBUG
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
|
||||
* and will only be visible in your Crashlytics dashboard.
|
||||
*
|
||||
**/
|
||||
|
||||
#ifdef __OBJC__
|
||||
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will show up in the system.log
|
||||
* and your Crashlytics dashboard. It is not recommended for Release builds.
|
||||
*
|
||||
**/
|
||||
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
|
@ -1,103 +0,0 @@
|
|||
//
|
||||
// CLSReport.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CLSAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details.
|
||||
**/
|
||||
@protocol CLSCrashReport <NSObject>
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||
@property (nonatomic, readonly, nullable) NSDate *crashedOnDate;
|
||||
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can
|
||||
* use this class to get information about the event, and can also set some values after the
|
||||
* event has occurred.
|
||||
**/
|
||||
@interface CLSReport : NSObject <CLSCrashReport>
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
* Returns the custom key value data for the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleVersion of the application that generated the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleShortVersionString of the application that generated the report.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||
|
||||
/**
|
||||
* Returns the date that the report was created.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSDate *dateCreated;
|
||||
|
||||
/**
|
||||
* Returns the os version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||
|
||||
/**
|
||||
* Returns the os build version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||
|
||||
/**
|
||||
* Returns YES if the report contains any crash information, otherwise returns NO.
|
||||
**/
|
||||
@property (nonatomic, assign, readonly) BOOL isCrash;
|
||||
|
||||
/**
|
||||
* You can use this method to set, after the event, additional custom keys. The rules
|
||||
* and semantics for this method are the same as those documented in Crashlytics.h. Be aware
|
||||
* that the maximum size and count of custom keys is still enforced, and you can overwrite keys
|
||||
* and/or cause excess keys to be deleted by using this method.
|
||||
**/
|
||||
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Record an application-specific user identifier. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userIdentifier;
|
||||
|
||||
/**
|
||||
* Record a user name. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userName;
|
||||
|
||||
/**
|
||||
* Record a user email. See Crashlytics.h for details.
|
||||
**/
|
||||
@property (nonatomic, copy, nullable) NSString * userEmail;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,38 +0,0 @@
|
|||
//
|
||||
// CLSStackFrame.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CLSAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
*
|
||||
* This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to
|
||||
* record information about non-ObjC/C++ exceptions. All information included here will be displayed
|
||||
* in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the
|
||||
* address property. If set, Crashlytics will attempt symbolication and could overwrite other properities
|
||||
* in the process.
|
||||
*
|
||||
**/
|
||||
@interface CLSStackFrame : NSObject
|
||||
|
||||
+ (instancetype)stackFrame;
|
||||
+ (instancetype)stackFrameWithAddress:(NSUInteger)address;
|
||||
+ (instancetype)stackFrameWithSymbol:(NSString *)symbol;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSString *symbol;
|
||||
@property (nonatomic, copy, nullable) NSString *rawSymbol;
|
||||
@property (nonatomic, copy, nullable) NSString *library;
|
||||
@property (nonatomic, copy, nullable) NSString *fileName;
|
||||
@property (nonatomic, assign) uint32_t lineNumber;
|
||||
@property (nonatomic, assign) uint64_t offset;
|
||||
@property (nonatomic, assign) uint64_t address;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,288 +0,0 @@
|
|||
//
|
||||
// Crashlytics.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "CLSAttributes.h"
|
||||
#import "CLSLogging.h"
|
||||
#import "CLSReport.h"
|
||||
#import "CLSStackFrame.h"
|
||||
#import "Answers.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol CrashlyticsDelegate;
|
||||
|
||||
/**
|
||||
* Crashlytics. Handles configuration and initialization of Crashlytics.
|
||||
*
|
||||
* Note: The Crashlytics class cannot be subclassed. If this is causing you pain for
|
||||
* testing, we suggest using either a wrapper class or a protocol extension.
|
||||
*/
|
||||
@interface Crashlytics : NSObject
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *APIKey;
|
||||
@property (nonatomic, readonly, copy) NSString *version;
|
||||
@property (nonatomic, assign) BOOL debugMode;
|
||||
|
||||
/**
|
||||
*
|
||||
* The delegate can be used to influence decisions on reporting and behavior, as well as reacting
|
||||
* to previous crashes.
|
||||
*
|
||||
* Make certain that the delegate is setup before starting Crashlytics with startWithAPIKey:... or
|
||||
* via +[Fabric with:...]. Failure to do will result in missing any delegate callbacks that occur
|
||||
* synchronously during start.
|
||||
*
|
||||
**/
|
||||
@property (nonatomic, assign, nullable) id <CrashlyticsDelegate> delegate;
|
||||
|
||||
/**
|
||||
* The recommended way to install Crashlytics into your application is to place a call to +startWithAPIKey:
|
||||
* in your -application:didFinishLaunchingWithOptions: or -applicationDidFinishLaunching:
|
||||
* method.
|
||||
*
|
||||
* Note: Starting with 3.0, the submission process has been significantly improved. The delay parameter
|
||||
* is no longer required to throttle submissions on launch, performance will be great without it.
|
||||
*
|
||||
* @param apiKey The Crashlytics API Key for this app
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey: instead.");
|
||||
|
||||
/**
|
||||
* If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
|
||||
* these convenience methods to activate the framework and set the delegate in one call.
|
||||
*
|
||||
* @param apiKey The Crashlytics API Key for this app
|
||||
* @param delegate A delegate object which conforms to CrashlyticsDelegate.
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)delegate;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id<CrashlyticsDelegate>)delegate afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey:delegate: instead.");
|
||||
|
||||
/**
|
||||
* Access the singleton Crashlytics instance.
|
||||
*
|
||||
* @return The singleton Crashlytics instance
|
||||
*/
|
||||
+ (Crashlytics *)sharedInstance;
|
||||
|
||||
/**
|
||||
* The easiest way to cause a crash - great for testing!
|
||||
*/
|
||||
- (void)crash;
|
||||
|
||||
/**
|
||||
* The easiest way to cause a crash with an exception - great for testing.
|
||||
*/
|
||||
- (void)throwException;
|
||||
|
||||
/**
|
||||
* Specify a user identifier which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* Many of our customers have requested the ability to tie crashes to specific end-users of their
|
||||
* application in order to facilitate responses to support requests or permit the ability to reach
|
||||
* out for more information. We allow you to specify up to three separate values for display within
|
||||
* the Crashlytics UI - but please be mindful of your end-user's privacy.
|
||||
*
|
||||
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
|
||||
* in your system. This could be a database id, hash, or other value that is meaningless to a
|
||||
* third-party observer but can be indexed and queried by you.
|
||||
*
|
||||
* Optionally, you may also specify the end-user's name or username, as well as email address if you
|
||||
* do not have a system that works well with obscured identifiers.
|
||||
*
|
||||
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
|
||||
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
|
||||
* contact information, we strongly recommend that you disclose this in your application's privacy
|
||||
* policy. Data privacy is of our utmost concern.
|
||||
*
|
||||
* @param identifier An arbitrary user identifier string which ties an end-user to a record in your system.
|
||||
*/
|
||||
- (void)setUserIdentifier:(nullable NSString *)identifier;
|
||||
|
||||
/**
|
||||
* Specify a user name which will be visible in the Crashlytics UI.
|
||||
* Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
|
||||
* @see setUserIdentifier:
|
||||
*
|
||||
* @param name An end user's name.
|
||||
*/
|
||||
- (void)setUserName:(nullable NSString *)name;
|
||||
|
||||
/**
|
||||
* Specify a user email which will be visible in the Crashlytics UI.
|
||||
* Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
|
||||
*
|
||||
* @see setUserIdentifier:
|
||||
*
|
||||
* @param email An end user's email address.
|
||||
*/
|
||||
- (void)setUserEmail:(nullable NSString *)email;
|
||||
|
||||
+ (void)setUserIdentifier:(nullable NSString *)identifier CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setUserName:(nullable NSString *)name CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setUserEmail:(nullable NSString *)email CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
|
||||
/**
|
||||
* Set a value for a for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
* When setting an object value, the object is converted to a string. This is typically done by calling
|
||||
* -[NSObject description].
|
||||
*
|
||||
* @param value The object to be associated with the key
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an int value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The integer value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setIntValue:(int)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an BOOL value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The BOOL value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
|
||||
|
||||
/**
|
||||
* Set an float value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
|
||||
*
|
||||
* @param value The float value to be set
|
||||
* @param key The key with which to associate the value
|
||||
*/
|
||||
- (void)setFloatValue:(float)value forKey:(NSString *)key;
|
||||
|
||||
+ (void)setObjectValue:(nullable id)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setIntValue:(int)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
+ (void)setFloatValue:(float)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
|
||||
|
||||
/**
|
||||
* This method can be used to record a single exception structure in a report. This is particularly useful
|
||||
* when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be
|
||||
* expensive and should only be used shortly before process termination. This API is not intended be to used
|
||||
* to log NSException objects. All safely-reportable NSExceptions are automatically captured by
|
||||
* Crashlytics.
|
||||
*
|
||||
* @param name The name of the custom exception
|
||||
* @param reason The reason this exception occurred
|
||||
* @param frameArray An array of CLSStackFrame objects
|
||||
*/
|
||||
- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray;
|
||||
|
||||
/**
|
||||
*
|
||||
* This allows you to record a non-fatal event, described by an NSError object. These events will be grouped and
|
||||
* displayed similarly to crashes. Keep in mind that this method can be expensive. Also, the total number of
|
||||
* NSErrors that can be recorded during your app's life-cycle is limited by a fixed-size circular buffer. If the
|
||||
* buffer is overrun, the oldest data is dropped. Errors are relayed to Crashlytics on a subsequent launch
|
||||
* of your application.
|
||||
*
|
||||
* You can also use the -recordError:withAdditionalUserInfo: to include additional context not represented
|
||||
* by the NSError instance itself.
|
||||
*
|
||||
**/
|
||||
- (void)recordError:(NSError *)error;
|
||||
- (void)recordError:(NSError *)error withAdditionalUserInfo:(nullable CLS_GENERIC_NSDICTIONARY(NSString *, id) *)userInfo;
|
||||
|
||||
- (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
- (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
+ (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
+ (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
*
|
||||
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
|
||||
* action on events that occur in the Crashlytics crash reporting system. You can make
|
||||
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
|
||||
* or through the convenience +startWithAPIKey:delegate: method.
|
||||
*
|
||||
*/
|
||||
@protocol CrashlyticsDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
|
||||
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
|
||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when a Crashlytics instance has determined that the last execution of the
|
||||
* application resulted in a saved report. This is called synchronously on Crashlytics
|
||||
* initialization. Your delegate must invoke the completionHandler, but does not need to do so
|
||||
* synchronously, or even on the main thread. Invoking completionHandler with NO will cause the
|
||||
* detected report to be deleted and not submitted to Crashlytics. This is useful for
|
||||
* implementing permission prompts, or other more-complex forms of logic around submitting crashes.
|
||||
*
|
||||
* Instead of using this method, you should try to make use of -crashlyticsDidDetectReportForLastExecution:
|
||||
* if you can.
|
||||
*
|
||||
* @warning Failure to invoke the completionHandler will prevent submissions from being reported. Watch out.
|
||||
*
|
||||
* @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can
|
||||
* impact the reliability of reporting crashes very early in application launch.
|
||||
*
|
||||
* @param report The CLSReport object representing the last detected report
|
||||
* @param completionHandler The completion handler to call when your logic has completed.
|
||||
*
|
||||
*/
|
||||
- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when a Crashlytics instance has determined that the last execution of the
|
||||
* application resulted in a saved report. This method differs from
|
||||
* -crashlyticsDidDetectReportForLastExecution:completionHandler: in three important ways:
|
||||
*
|
||||
* - it is not called synchronously during initialization
|
||||
* - it does not give you the ability to prevent the report from being submitted
|
||||
* - the report object itself is immutable
|
||||
*
|
||||
* Thanks to these limitations, making use of this method does not impact reporting
|
||||
* reliabilty in any way.
|
||||
*
|
||||
* @param report The read-only CLSReport object representing the last detected report
|
||||
*
|
||||
*/
|
||||
|
||||
- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report;
|
||||
|
||||
/**
|
||||
* If your app is running on an OS that supports it (OS X 10.9+, iOS 7.0+), Crashlytics will submit
|
||||
* most reports using out-of-process background networking operations. This results in a significant
|
||||
* improvement in reliability of reporting, as well as power and performance wins for your users.
|
||||
* If you don't want this functionality, you can disable by returning NO from this method.
|
||||
*
|
||||
* @warning Background submission is not supported for extensions on iOS or OS X.
|
||||
*
|
||||
* @param crashlytics The Crashlytics singleton instance
|
||||
*
|
||||
* @return Return NO if you don't want out-of-process background network operations.
|
||||
*
|
||||
*/
|
||||
- (BOOL)crashlyticsCanUseBackgroundSessions:(Crashlytics *)crashlytics;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, use Crashlytics.sharedInstance()
|
||||
*/
|
||||
#define CrashlyticsKit [Crashlytics sharedInstance]
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Binary file not shown.
|
@ -1,14 +0,0 @@
|
|||
framework module Crashlytics {
|
||||
header "Crashlytics.h"
|
||||
header "Answers.h"
|
||||
header "ANSCompatibility.h"
|
||||
header "CLSLogging.h"
|
||||
header "CLSReport.h"
|
||||
header "CLSStackFrame.h"
|
||||
header "CLSAttributes.h"
|
||||
|
||||
export *
|
||||
|
||||
link "z"
|
||||
link "c++"
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
#
|
||||
#
|
||||
# This script is meant to be run as a Run Script in the "Build Phases" section
|
||||
# of your Xcode project. It sends debug symbols to symbolicate stacktraces,
|
||||
# sends build events to track versions, and onboard apps for Crashlytics.
|
||||
#
|
||||
# This script calls upload-symbols twice:
|
||||
#
|
||||
# 1) First it calls upload-symbols synchronously in "validation" mode. If the
|
||||
# script finds issues with the build environment, it will report errors to Xcode.
|
||||
# In validation mode it exits before doing any time consuming work.
|
||||
#
|
||||
# 2) Then it calls upload-symbols in the background to actually send the build
|
||||
# event and upload symbols. It does this in the background so that it doesn't
|
||||
# slow down your builds. If an error happens here, you won't see it in Xcode.
|
||||
#
|
||||
# You can find the output for the background execution in Console.app, by
|
||||
# searching for "upload-symbols".
|
||||
#
|
||||
# If you want verbose output, you can pass the --debug flag to this script
|
||||
#
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# If the first argument is specified without a dash, treat it as the Fabric API
|
||||
# Key and add it as an argument
|
||||
if [ -z "$1" ] || [[ $1 == -* ]]; then
|
||||
API_KEY_ARG=""
|
||||
else
|
||||
API_KEY_ARG="-a $1"; shift
|
||||
fi
|
||||
|
||||
# If a second argument is specified without a dash, treat it as the Build Secret
|
||||
# and add it as an argument
|
||||
if [ -z "$1" ] || [[ $1 == -* ]]; then
|
||||
BUILD_SECRET_ARG=""
|
||||
else
|
||||
BUILD_SECRET_ARG="-bs $1"; shift
|
||||
fi
|
||||
|
||||
# Build up the arguments list, passing through any flags added after the
|
||||
# API Key and Build Secret
|
||||
ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@"
|
||||
VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate"
|
||||
UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase"
|
||||
|
||||
# Quote the path to handle folders with special characters
|
||||
COMMAND_PATH="\"$DIR/upload-symbols\" "
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate,
|
||||
# and cause a build error if validation fails
|
||||
eval $COMMAND_PATH$VALIDATE_ARGUMENTS
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, convert and upload cSYMs in the background to prevent
|
||||
# build delays
|
||||
#
|
||||
# Note: Validation is performed again at this step before upload
|
||||
#
|
||||
# Note: Output can still be found in Console.app, by searching for
|
||||
# "upload-symbols"
|
||||
#
|
||||
eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 &
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1 +0,0 @@
|
|||
We've now combined all our supported platforms into a single podspec. As a result, we moved our run script to a new location for Cocoapods projects: ${PODS_ROOT}/Fabric/run. To avoid breaking builds that reference the old location of the run script, we've placed this dummy script that calls to the correct location, while providing a helpful warning in Xcode if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning!
|
|
@ -1,6 +0,0 @@
|
|||
if [[ -z $PODS_ROOT ]]; then
|
||||
echo "error: The run binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Fabric/run'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the run binary to fix this issue."
|
||||
else
|
||||
echo "warning: The run script is now located at '$"{"PODS_ROOT"}"/Fabric/run'. To remove this warning, update your Run Script Build Phase to point to this new location."
|
||||
sh "${PODS_ROOT}/Fabric/run" "$@"
|
||||
fi
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
# Fabric
|
||||
|
||||
## Overview
|
||||
|
||||
[Fabric](https://get.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Google and the team that built Crashlytics.
|
||||
|
||||
For a full list of SDKs provided through Fabric visit [https://fabric.io/kits](https://fabric.io/kits).
|
||||
|
||||
To follow the migration to Firebase, check out the [Fabric Roadmap](https://get.fabric.io/roadmap).
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
Fabric is a dependency for the Crashlytics SDK. To start using Crashlytics, there are two options:
|
||||
|
||||
1) The recommended way is to go to the [Firebase Crashlytics Docs](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) and follow the directions there.
|
||||
|
||||
2) If you aren't using Firebase yet, go to [Fabric Kits](https://fabric.io/kits), and follow the directions for Crashlytics.
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
* [Documentation](https://docs.fabric.io/)
|
||||
* [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
|
||||
* [Website](https://get.fabric.io)
|
||||
* Follow us on Twitter: [@fabric](https://twitter.com/fabric)
|
Binary file not shown.
|
@ -1,51 +0,0 @@
|
|||
//
|
||||
// FABAttributes.h
|
||||
// Fabric
|
||||
//
|
||||
// Copyright (C) 2015 Twitter, Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x)))
|
||||
|
||||
#if !__has_feature(nullability)
|
||||
#define nonnull
|
||||
#define nullable
|
||||
#define _Nullable
|
||||
#define _Nonnull
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||
#define NS_ASSUME_NONNULL_BEGIN
|
||||
#endif
|
||||
|
||||
#ifndef NS_ASSUME_NONNULL_END
|
||||
#define NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* The following macros are defined here to provide
|
||||
* backwards compatability. If you are still using
|
||||
* them you should migrate to the native nullability
|
||||
* macros.
|
||||
*/
|
||||
#define fab_nullable nullable
|
||||
#define fab_nonnull nonnull
|
||||
#define FAB_NONNULL __fab_nonnull
|
||||
#define FAB_NULLABLE __fab_nullable
|
||||
#define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN
|
||||
#define FAB_END_NONNULL NS_ASSUME_NONNULL_END
|
|
@ -1,82 +0,0 @@
|
|||
//
|
||||
// Fabric.h
|
||||
// Fabric
|
||||
//
|
||||
// Copyright (C) 2015 Twitter, Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "FABAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
|
||||
#error "Fabric's minimum iOS version is 6.0"
|
||||
#endif
|
||||
#else
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
|
||||
#error "Fabric's minimum OS X version is 10.7"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fabric Base. Coordinates configuration and starts all provided kits.
|
||||
*/
|
||||
@interface Fabric : NSObject
|
||||
|
||||
/**
|
||||
* Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use.
|
||||
*
|
||||
* For example, in Objective-C:
|
||||
*
|
||||
* `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];`
|
||||
*
|
||||
* Swift:
|
||||
*
|
||||
* `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])`
|
||||
*
|
||||
* Only the first call to this method is honored. Subsequent calls are no-ops.
|
||||
*
|
||||
* @param kitClasses An array of kit Class objects
|
||||
*
|
||||
* @return Returns the shared Fabric instance. In most cases this can be ignored.
|
||||
*/
|
||||
+ (instancetype)with:(NSArray *)kitClasses;
|
||||
|
||||
/**
|
||||
* Returns the Fabric singleton object.
|
||||
*/
|
||||
+ (instancetype)sharedSDK;
|
||||
|
||||
/**
|
||||
* This BOOL enables or disables debug logging, such as kit version information. The default value is NO.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL debug;
|
||||
|
||||
/**
|
||||
* Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance.
|
||||
*/
|
||||
- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance.");
|
||||
|
||||
/**
|
||||
* Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance.
|
||||
*/
|
||||
+ (instancetype)new FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance.");
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
Binary file not shown.
|
@ -1,6 +0,0 @@
|
|||
framework module Fabric {
|
||||
umbrella header "Fabric.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
#
|
||||
#
|
||||
# This script is meant to be run as a Run Script in the "Build Phases" section
|
||||
# of your Xcode project. It sends debug symbols to symbolicate stacktraces,
|
||||
# sends build events to track versions, and onboard apps for Crashlytics.
|
||||
#
|
||||
# This script calls upload-symbols twice:
|
||||
#
|
||||
# 1) First it calls upload-symbols synchronously in "validation" mode. If the
|
||||
# script finds issues with the build environment, it will report errors to Xcode.
|
||||
# In validation mode it exits before doing any time consuming work.
|
||||
#
|
||||
# 2) Then it calls upload-symbols in the background to actually send the build
|
||||
# event and upload symbols. It does this in the background so that it doesn't
|
||||
# slow down your builds. If an error happens here, you won't see it in Xcode.
|
||||
#
|
||||
# You can find the output for the background execution in Console.app, by
|
||||
# searching for "upload-symbols".
|
||||
#
|
||||
# If you want verbose output, you can pass the --debug flag to this script
|
||||
#
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# If the first argument is specified without a dash, treat it as the Fabric API
|
||||
# Key and add it as an argument
|
||||
if [ -z "$1" ] || [[ $1 == -* ]]; then
|
||||
API_KEY_ARG=""
|
||||
else
|
||||
API_KEY_ARG="-a $1"; shift
|
||||
fi
|
||||
|
||||
# If a second argument is specified without a dash, treat it as the Build Secret
|
||||
# and add it as an argument
|
||||
if [ -z "$1" ] || [[ $1 == -* ]]; then
|
||||
BUILD_SECRET_ARG=""
|
||||
else
|
||||
BUILD_SECRET_ARG="-bs $1"; shift
|
||||
fi
|
||||
|
||||
# Build up the arguments list, passing through any flags added after the
|
||||
# API Key and Build Secret
|
||||
ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@"
|
||||
VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate"
|
||||
UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase"
|
||||
|
||||
# Quote the path to handle folders with special characters
|
||||
COMMAND_PATH="\"$DIR/upload-symbols\" "
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate,
|
||||
# and cause a build error if validation fails
|
||||
eval $COMMAND_PATH$VALIDATE_ARGUMENTS
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, convert and upload cSYMs in the background to prevent
|
||||
# build delays
|
||||
#
|
||||
# Note: Validation is performed again at this step before upload
|
||||
#
|
||||
# Note: Output can still be found in Console.app, by searching for
|
||||
# "upload-symbols"
|
||||
#
|
||||
eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 &
|
Binary file not shown.
|
@ -1,73 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# run
|
||||
#
|
||||
# Copyright (c) 2015 Crashlytics. All rights reserved.
|
||||
#
|
||||
#
|
||||
# This script is meant to be run as a Run Script in the "Build Phases" section
|
||||
# of your Xcode project. It sends debug symbols to symbolicate stacktraces,
|
||||
# sends build events to track versions, and onboard apps for Crashlytics.
|
||||
#
|
||||
# This script calls upload-symbols twice:
|
||||
#
|
||||
# 1) First it calls upload-symbols synchronously in "validation" mode. If the
|
||||
# script finds issues with the build environment, it will report errors to Xcode.
|
||||
# In validation mode it exits before doing any time consuming work.
|
||||
#
|
||||
# 2) Then it calls upload-symbols in the background to actually send the build
|
||||
# event and upload symbols. It does this in the background so that it doesn't
|
||||
# slow down your builds. If an error happens here, you won't see it in Xcode.
|
||||
#
|
||||
# You can find the output for the background execution in Console.app, by
|
||||
# searching for "upload-symbols".
|
||||
#
|
||||
# If you want verbose output, you can pass the --debug flag to this script
|
||||
#
|
||||
|
||||
# Figure out where we're being called from
|
||||
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
|
||||
# If the first argument is specified without a dash, treat it as the Fabric API
|
||||
# Key and add it as an argument
|
||||
if [ -z "$1" ] || [[ $1 == -* ]]; then
|
||||
API_KEY_ARG=""
|
||||
else
|
||||
API_KEY_ARG="-a $1"; shift
|
||||
fi
|
||||
|
||||
# If a second argument is specified without a dash, treat it as the Build Secret
|
||||
# and add it as an argument
|
||||
if [ -z "$1" ] || [[ $1 == -* ]]; then
|
||||
BUILD_SECRET_ARG=""
|
||||
else
|
||||
BUILD_SECRET_ARG="-bs $1"; shift
|
||||
fi
|
||||
|
||||
# Build up the arguments list, passing through any flags added after the
|
||||
# API Key and Build Secret
|
||||
ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@"
|
||||
VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate"
|
||||
UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase"
|
||||
|
||||
# Quote the path to handle folders with special characters
|
||||
COMMAND_PATH="\"$DIR/upload-symbols\" "
|
||||
|
||||
# Ensure params are as expected, run in sync mode to validate,
|
||||
# and cause a build error if validation fails
|
||||
eval $COMMAND_PATH$VALIDATE_ARGUMENTS
|
||||
return_code=$?
|
||||
|
||||
if [[ $return_code != 0 ]]; then
|
||||
exit $return_code
|
||||
fi
|
||||
|
||||
# Verification passed, convert and upload cSYMs in the background to prevent
|
||||
# build delays
|
||||
#
|
||||
# Note: Validation is performed again at this step before upload
|
||||
#
|
||||
# Note: Output can still be found in Console.app, by searching for
|
||||
# "upload-symbols"
|
||||
#
|
||||
eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 &
|
Binary file not shown.
|
@ -12,39 +12,26 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The module qualified imports are for CocoaPods and the simple file names
|
||||
// for Swift Package Manager.
|
||||
#import <FirebaseCore/FirebaseCore.h>
|
||||
|
||||
#if !defined(__has_include)
|
||||
#error "Firebase.h won't import anything if your compiler doesn't support __has_include. Please \
|
||||
import the headers individually."
|
||||
#else
|
||||
#if __has_include(<FirebaseCore/FirebaseCore.h>)
|
||||
#import <FirebaseCore/FirebaseCore.h>
|
||||
#elif __has_include("FirebaseCore.h")
|
||||
#import "FirebaseCore.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
|
||||
#import <FirebaseAnalytics/FirebaseAnalytics.h>
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseAuth/FirebaseAuth.h>)
|
||||
#import <FirebaseAuth/FirebaseAuth.h>
|
||||
#elif __has_include("FirebaseAuth.h")
|
||||
#import "FirebaseAuth.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseCrashlytics/FirebaseCrashlytics.h>)
|
||||
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
|
||||
#elif __has_include("FirebaseCrashlytics.h")
|
||||
#import "FirebaseCrashlytics.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseDatabase/FirebaseDatabase.h>)
|
||||
#import <FirebaseDatabase/FirebaseDatabase.h>
|
||||
#elif __has_include("FirebaseDatabase.h")
|
||||
#import "FirebaseDatabase.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
|
||||
|
@ -56,20 +43,14 @@
|
|||
Firebase Dynamic Links works as intended."
|
||||
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
|
||||
#endif
|
||||
#elif __has_include("FirebaseDynamicLinks.h")
|
||||
#import "FirebaseDynamicLinks.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||
#import <FirebaseFirestore/FirebaseFirestore.h>
|
||||
#elif __has_include("FirebaseFirestore.h")
|
||||
#import "FirebaseFirestore.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseFunctions/FirebaseFunctions.h>)
|
||||
#import <FirebaseFunctions/FirebaseFunctions.h>
|
||||
#elif __has_include("FirebaseFunctions.h")
|
||||
#import "FirebaseFunctions.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseInAppMessaging/FirebaseInAppMessaging.h>)
|
||||
|
@ -81,8 +62,6 @@ Firebase Dynamic Links works as intended."
|
|||
Firebase In App Messaging works as intended."
|
||||
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
|
||||
#endif
|
||||
#elif __has_include("FirebaseInAppMessaging.h")
|
||||
#import "FirebaseInAppMessaging.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseInstanceID/FirebaseInstanceID.h>)
|
||||
|
@ -98,8 +77,6 @@ Firebase In App Messaging works as intended."
|
|||
Firebase Messaging works as intended."
|
||||
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
|
||||
#endif
|
||||
#elif __has_include("FirebaseMessaging.h")
|
||||
#import "FirebaseMessaging.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseMLCommon/FirebaseMLCommon.h>)
|
||||
|
@ -174,14 +151,10 @@ Firebase Performance works as intended."
|
|||
Firebase Remote Config works as intended."
|
||||
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
|
||||
#endif
|
||||
#elif __has_include("FirebaseRemoteConfig.h")
|
||||
#import "FirebaseRemoteConfig.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<FirebaseStorage/FirebaseStorage.h>)
|
||||
#import <FirebaseStorage/FirebaseStorage.h>
|
||||
#elif __has_include("FirebaseStorage.h")
|
||||
#import "FirebaseStorage.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(<GoogleMobileAds/GoogleMobileAds.h>)
|
||||
|
|
|
@ -140,13 +140,21 @@ To ensure that the code is formatted consistently, run the script
|
|||
before creating a PR.
|
||||
|
||||
Travis will verify that any code changes are done in a style compliant way. Install
|
||||
`clang-format` and `swiftformat`:
|
||||
`clang-format` and `swiftformat`.
|
||||
These commands will get the right versions:
|
||||
|
||||
```
|
||||
brew install clang-format
|
||||
brew install swiftformat
|
||||
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb
|
||||
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb
|
||||
```
|
||||
|
||||
Note: if you already have a newer version of these installed you may need to
|
||||
`brew switch` to this version.
|
||||
|
||||
To update this section, find the versions of clang-format and swiftformat.rb to
|
||||
match the versions in the CI failure logs
|
||||
[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||
|
@ -169,7 +177,12 @@ files without real values, but can be replaced with real plist files. To get you
|
|||
2. Create a new Firebase project, if you don't already have one
|
||||
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
|
||||
identifier (e.g. `com.google.Database-Example`)
|
||||
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
|
||||
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
|
||||
(e.g. in [Example/Database/App/](Example/Database/App/));
|
||||
|
||||
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
|
||||
special Apple capabilities, and you will have to change the sample app to use a unique bundle
|
||||
identifier that you can control in your own Apple Developer account.
|
||||
|
||||
## Specific Component Instructions
|
||||
See the sections below for any special instructions for those components.
|
||||
|
@ -189,7 +202,7 @@ To run against a local emulator instance, invoke `./scripts/run_database_emulato
|
|||
running the integration test.
|
||||
|
||||
To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to
|
||||
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
|
||||
running.
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#import "FirebaseCore/Sources/Private/FIRLogger.h"
|
||||
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
|
||||
|
||||
#import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h"
|
||||
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#import "FirebaseCore/Sources/FIRBundleUtil.h"
|
||||
|
||||
#import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h"
|
||||
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
|
||||
|
||||
@implementation FIRBundleUtil
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "FirebaseCore/Sources/Public/FIRConfiguration.h"
|
||||
#import "FIRConfiguration.h"
|
||||
|
||||
@class FIRAnalyticsConfiguration;
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
// limitations under the License.
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h"
|
||||
#import "GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h"
|
||||
#import "GoogleUtilities/Logger/Private/GULLogger.h"
|
||||
#import <GoogleUtilities/GULHeartbeatDateStorage.h>
|
||||
#import <GoogleUtilities/GULLogger.h>
|
||||
|
||||
const static long secondsInDay = 86400;
|
||||
@implementation FIRHeartbeatInfo : NSObject
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
|
||||
#import "FirebaseCore/Sources/Private/FIRLogger.h"
|
||||
|
||||
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
|
||||
#import <GoogleUtilities/GULLogger.h>
|
||||
#import "FirebaseCore/Sources/Public/FIRLoggerLevel.h"
|
||||
#import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h"
|
||||
#import "GoogleUtilities/Logger/Private/GULLogger.h"
|
||||
|
||||
#import "FirebaseCore/Sources/FIRVersion.h"
|
||||
|
||||
|
|
|
@ -90,30 +90,32 @@ NSString *const kFIRExceptionBadModification =
|
|||
|
||||
static FIROptions *sDefaultOptions = nil;
|
||||
static NSDictionary *sDefaultOptionsDictionary = nil;
|
||||
static dispatch_once_t sDefaultOptionsOnceToken;
|
||||
static dispatch_once_t sDefaultOptionsDictionaryOnceToken;
|
||||
|
||||
#pragma mark - Public only for internal class methods
|
||||
|
||||
+ (FIROptions *)defaultOptions {
|
||||
dispatch_once(&sDefaultOptionsOnceToken, ^{
|
||||
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
|
||||
if (defaultOptionsDictionary != nil) {
|
||||
sDefaultOptions =
|
||||
[[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
|
||||
if (sDefaultOptions != nil) {
|
||||
return sDefaultOptions;
|
||||
}
|
||||
});
|
||||
|
||||
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
|
||||
if (defaultOptionsDictionary == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
sDefaultOptions = [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
|
||||
return sDefaultOptions;
|
||||
}
|
||||
|
||||
#pragma mark - Private class methods
|
||||
|
||||
+ (NSDictionary *)defaultOptionsDictionary {
|
||||
dispatch_once(&sDefaultOptionsDictionaryOnceToken, ^{
|
||||
if (sDefaultOptionsDictionary != nil) {
|
||||
return sDefaultOptionsDictionary;
|
||||
}
|
||||
NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
|
||||
if (plistFilePath == nil) {
|
||||
return;
|
||||
return nil;
|
||||
}
|
||||
sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
|
||||
if (sDefaultOptionsDictionary == nil) {
|
||||
|
@ -122,8 +124,6 @@ static dispatch_once_t sDefaultOptionsDictionaryOnceToken;
|
|||
@"'%@.%@'.",
|
||||
kServiceInfoFileName, kServiceInfoFileType);
|
||||
}
|
||||
});
|
||||
|
||||
return sDefaultOptionsDictionary;
|
||||
}
|
||||
|
||||
|
@ -144,8 +144,6 @@ static dispatch_once_t sDefaultOptionsDictionaryOnceToken;
|
|||
+ (void)resetDefaultOptions {
|
||||
sDefaultOptions = nil;
|
||||
sDefaultOptionsDictionary = nil;
|
||||
sDefaultOptionsOnceToken = 0;
|
||||
sDefaultOptionsDictionaryOnceToken = 0;
|
||||
}
|
||||
|
||||
#pragma mark - Private instance methods
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GULAppEnvironmentUtil : NSObject
|
||||
|
||||
/// Indicates whether the app is from Apple Store or not. Returns NO if the app is on simulator,
|
||||
/// development environment or sideloaded.
|
||||
+ (BOOL)isFromAppStore;
|
||||
|
||||
/// Indicates whether the app is a Testflight app. Returns YES if the app has sandbox receipt.
|
||||
/// Returns NO otherwise.
|
||||
+ (BOOL)isAppStoreReceiptSandbox;
|
||||
|
||||
/// Indicates whether the app is on simulator or not at runtime depending on the device
|
||||
/// architecture.
|
||||
+ (BOOL)isSimulator;
|
||||
|
||||
/// The current device model. Returns an empty string if device model cannot be retrieved.
|
||||
+ (NSString *)deviceModel;
|
||||
|
||||
/// The current operating system version. Returns an empty string if the system version cannot be
|
||||
/// retrieved.
|
||||
+ (NSString *)systemVersion;
|
||||
|
||||
/// Indicates whether it is running inside an extension or an app.
|
||||
+ (BOOL)isAppExtension;
|
||||
|
||||
/// @return Returns @YES when is run on iOS version greater or equal to 7.0
|
||||
+ (BOOL)isIOS7OrHigher DEPRECATED_MSG_ATTRIBUTE(
|
||||
"Always `YES` because only iOS 8 and higher supported. The method will be removed.");
|
||||
|
||||
@end
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Stores either a date or a dictionary to a specified file.
|
||||
@interface GULHeartbeatDateStorage : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@property(nonatomic, readonly) NSURL *fileURL;
|
||||
|
||||
/**
|
||||
* Default initializer.
|
||||
* @param fileName The name of the file to store the date information.
|
||||
* exist, it will be created if needed.
|
||||
*/
|
||||
- (instancetype)initWithFileName:(NSString *)fileName;
|
||||
|
||||
/**
|
||||
* Reads the date from the specified file for the given tag.
|
||||
* @return Returns date if exists, otherwise `nil`.
|
||||
*/
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag;
|
||||
|
||||
/**
|
||||
* Saves the date for the specified tag in the specified file.
|
||||
* @return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FBLPromise<ValueType>;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The class provides a convenient abstraction on top of the iOS Keychain API to save data.
|
||||
@interface GULKeychainStorage : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/** Initializes the keychain storage with Keychain Service name.
|
||||
* @param service A Keychain Service name that will be used to store and retrieve objects. See also
|
||||
* `kSecAttrService`.
|
||||
*/
|
||||
- (instancetype)initWithService:(NSString *)service;
|
||||
|
||||
/**
|
||||
* Get an object by key.
|
||||
* @param key The key.
|
||||
* @param objectClass The expected object class required by `NSSecureCoding`.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns a promise. It is resolved with an object stored by key if exists. It is resolved
|
||||
* with `nil` when the object not found. It fails on a Keychain error.
|
||||
*/
|
||||
- (FBLPromise<id<NSSecureCoding>> *)getObjectForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
|
||||
/**
|
||||
* Saves the given object by the given key.
|
||||
* @param object The object to store.
|
||||
* @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
* overridden.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns which is resolved with `[NSNull null]` on success.
|
||||
*/
|
||||
- (FBLPromise<NSNull *> *)setObject:(id<NSSecureCoding>)object
|
||||
forKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
|
||||
/**
|
||||
* Removes the object by the given key.
|
||||
* @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
* overridden.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns which is resolved with `[NSNull null]` on success.
|
||||
*/
|
||||
- (FBLPromise<NSNull *> *)removeObjectForKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
/// If not `nil`, then only this keychain will be used to save and read data (see
|
||||
/// `kSecMatchSearchList` and `kSecUseKeychain`. It is mostly intended to be used by unit tests.
|
||||
@property(nonatomic, nullable) SecKeychainRef keychainRef;
|
||||
#endif // TARGET_OSX
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kGULKeychainUtilsErrorDomain;
|
||||
|
||||
/// Helper functions to access Keychain.
|
||||
@interface GULKeychainUtils : NSObject
|
||||
|
||||
/** Fetches a keychain item data matching to the provided query.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemCopyMatching` for
|
||||
* details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns Data for the first Keychain Item matching the provided query or `nil` if there is not
|
||||
* such an item (`outError` will be `nil` in this case) or an error occurred.
|
||||
*/
|
||||
+ (nullable NSData *)getItemWithQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
/** Stores data to a Keychain Item matching to the provided query. An existing Keychain Item
|
||||
* matching the query parameters will be updated or a new will be created.
|
||||
* @param item A Keychain Item data to store.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemAdd` and
|
||||
* `SecItemUpdate` for details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns `YES` when data was successfully stored, `NO` otherwise.
|
||||
*/
|
||||
+ (BOOL)setItem:(NSData *)item
|
||||
withQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
/** Removes a Keychain Item matching to the provided query.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemDelete` for
|
||||
* details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns `YES` if the item was removed successfully or doesn't exist, `NO` otherwise.
|
||||
*/
|
||||
+ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -140,13 +140,21 @@ To ensure that the code is formatted consistently, run the script
|
|||
before creating a PR.
|
||||
|
||||
Travis will verify that any code changes are done in a style compliant way. Install
|
||||
`clang-format` and `swiftformat`:
|
||||
`clang-format` and `swiftformat`.
|
||||
These commands will get the right versions:
|
||||
|
||||
```
|
||||
brew install clang-format
|
||||
brew install swiftformat
|
||||
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb
|
||||
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb
|
||||
```
|
||||
|
||||
Note: if you already have a newer version of these installed you may need to
|
||||
`brew switch` to this version.
|
||||
|
||||
To update this section, find the versions of clang-format and swiftformat.rb to
|
||||
match the versions in the CI failure logs
|
||||
[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||
|
@ -169,7 +177,12 @@ files without real values, but can be replaced with real plist files. To get you
|
|||
2. Create a new Firebase project, if you don't already have one
|
||||
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
|
||||
identifier (e.g. `com.google.Database-Example`)
|
||||
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
|
||||
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
|
||||
(e.g. in [Example/Database/App/](Example/Database/App/));
|
||||
|
||||
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
|
||||
special Apple capabilities, and you will have to change the sample app to use a unique bundle
|
||||
identifier that you can control in your own Apple Developer account.
|
||||
|
||||
## Specific Component Instructions
|
||||
See the sections below for any special instructions for those components.
|
||||
|
@ -189,7 +202,7 @@ To run against a local emulator instance, invoke `./scripts/run_database_emulato
|
|||
running the integration test.
|
||||
|
||||
To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to
|
||||
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
|
||||
running.
|
||||
|
||||
|
|
|
@ -17,11 +17,14 @@
|
|||
#import <objc/runtime.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#import "GoogleDataTransport/GDTCORLibrary/Internal/GoogleDataTransportInternal.h"
|
||||
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
|
||||
#import <GoogleDataTransport/GDTCOREvent.h>
|
||||
#import <GoogleDataTransport/GDTCORTargets.h>
|
||||
#import <GoogleDataTransport/GDTCORTransport.h>
|
||||
|
||||
#import "GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h"
|
||||
#import "GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h"
|
||||
#import "GoogleUtilities/Logger/Private/GULLogger.h"
|
||||
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
|
||||
#import <GoogleUtilities/GULHeartbeatDateStorage.h>
|
||||
#import <GoogleUtilities/GULLogger.h>
|
||||
|
||||
#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h"
|
||||
#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h"
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GULAppEnvironmentUtil : NSObject
|
||||
|
||||
/// Indicates whether the app is from Apple Store or not. Returns NO if the app is on simulator,
|
||||
/// development environment or sideloaded.
|
||||
+ (BOOL)isFromAppStore;
|
||||
|
||||
/// Indicates whether the app is a Testflight app. Returns YES if the app has sandbox receipt.
|
||||
/// Returns NO otherwise.
|
||||
+ (BOOL)isAppStoreReceiptSandbox;
|
||||
|
||||
/// Indicates whether the app is on simulator or not at runtime depending on the device
|
||||
/// architecture.
|
||||
+ (BOOL)isSimulator;
|
||||
|
||||
/// The current device model. Returns an empty string if device model cannot be retrieved.
|
||||
+ (NSString *)deviceModel;
|
||||
|
||||
/// The current operating system version. Returns an empty string if the system version cannot be
|
||||
/// retrieved.
|
||||
+ (NSString *)systemVersion;
|
||||
|
||||
/// Indicates whether it is running inside an extension or an app.
|
||||
+ (BOOL)isAppExtension;
|
||||
|
||||
/// @return Returns @YES when is run on iOS version greater or equal to 7.0
|
||||
+ (BOOL)isIOS7OrHigher DEPRECATED_MSG_ATTRIBUTE(
|
||||
"Always `YES` because only iOS 8 and higher supported. The method will be removed.");
|
||||
|
||||
@end
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Stores either a date or a dictionary to a specified file.
|
||||
@interface GULHeartbeatDateStorage : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@property(nonatomic, readonly) NSURL *fileURL;
|
||||
|
||||
/**
|
||||
* Default initializer.
|
||||
* @param fileName The name of the file to store the date information.
|
||||
* exist, it will be created if needed.
|
||||
*/
|
||||
- (instancetype)initWithFileName:(NSString *)fileName;
|
||||
|
||||
/**
|
||||
* Reads the date from the specified file for the given tag.
|
||||
* @return Returns date if exists, otherwise `nil`.
|
||||
*/
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag;
|
||||
|
||||
/**
|
||||
* Saves the date for the specified tag in the specified file.
|
||||
* @return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FBLPromise<ValueType>;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The class provides a convenient abstraction on top of the iOS Keychain API to save data.
|
||||
@interface GULKeychainStorage : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/** Initializes the keychain storage with Keychain Service name.
|
||||
* @param service A Keychain Service name that will be used to store and retrieve objects. See also
|
||||
* `kSecAttrService`.
|
||||
*/
|
||||
- (instancetype)initWithService:(NSString *)service;
|
||||
|
||||
/**
|
||||
* Get an object by key.
|
||||
* @param key The key.
|
||||
* @param objectClass The expected object class required by `NSSecureCoding`.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns a promise. It is resolved with an object stored by key if exists. It is resolved
|
||||
* with `nil` when the object not found. It fails on a Keychain error.
|
||||
*/
|
||||
- (FBLPromise<id<NSSecureCoding>> *)getObjectForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
|
||||
/**
|
||||
* Saves the given object by the given key.
|
||||
* @param object The object to store.
|
||||
* @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
* overridden.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns which is resolved with `[NSNull null]` on success.
|
||||
*/
|
||||
- (FBLPromise<NSNull *> *)setObject:(id<NSSecureCoding>)object
|
||||
forKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
|
||||
/**
|
||||
* Removes the object by the given key.
|
||||
* @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
* overridden.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns which is resolved with `[NSNull null]` on success.
|
||||
*/
|
||||
- (FBLPromise<NSNull *> *)removeObjectForKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
/// If not `nil`, then only this keychain will be used to save and read data (see
|
||||
/// `kSecMatchSearchList` and `kSecUseKeychain`. It is mostly intended to be used by unit tests.
|
||||
@property(nonatomic, nullable) SecKeychainRef keychainRef;
|
||||
#endif // TARGET_OSX
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kGULKeychainUtilsErrorDomain;
|
||||
|
||||
/// Helper functions to access Keychain.
|
||||
@interface GULKeychainUtils : NSObject
|
||||
|
||||
/** Fetches a keychain item data matching to the provided query.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemCopyMatching` for
|
||||
* details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns Data for the first Keychain Item matching the provided query or `nil` if there is not
|
||||
* such an item (`outError` will be `nil` in this case) or an error occurred.
|
||||
*/
|
||||
+ (nullable NSData *)getItemWithQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
/** Stores data to a Keychain Item matching to the provided query. An existing Keychain Item
|
||||
* matching the query parameters will be updated or a new will be created.
|
||||
* @param item A Keychain Item data to store.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemAdd` and
|
||||
* `SecItemUpdate` for details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns `YES` when data was successfully stored, `NO` otherwise.
|
||||
*/
|
||||
+ (BOOL)setItem:(NSData *)item
|
||||
withQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
/** Removes a Keychain Item matching to the provided query.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemDelete` for
|
||||
* details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns `YES` if the item was removed successfully or doesn't exist, `NO` otherwise.
|
||||
*/
|
||||
+ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
@import GoogleUtilities_Logger;
|
||||
#else
|
||||
#import <GoogleUtilities/GULLoggerLevel.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The services used in the logger.
|
||||
*/
|
||||
typedef NSString *const GULLoggerService;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
* Initialize GULLogger.
|
||||
*/
|
||||
extern void GULLoggerInitializeASL(void);
|
||||
|
||||
/**
|
||||
* Override log level to Debug.
|
||||
*/
|
||||
void GULLoggerForceDebug(void);
|
||||
|
||||
/**
|
||||
* Turn on logging to STDERR.
|
||||
*/
|
||||
extern void GULLoggerEnableSTDERR(void);
|
||||
|
||||
/**
|
||||
* Changes the default logging level of GULLoggerLevelNotice to a user-specified level.
|
||||
* The default level cannot be set above GULLoggerLevelNotice if the app is running from App Store.
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
*/
|
||||
extern void GULSetLoggerLevel(GULLoggerLevel loggerLevel);
|
||||
|
||||
/**
|
||||
* Checks if the specified logger level is loggable given the current settings.
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
*/
|
||||
extern BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel);
|
||||
|
||||
/**
|
||||
* Register version to include in logs.
|
||||
* (required) version
|
||||
*/
|
||||
extern void GULLoggerRegisterVersion(const char *version);
|
||||
|
||||
/**
|
||||
* Logs a message to the Xcode console and the device log. If running from AppStore, will
|
||||
* not log any messages with a level higher than GULLoggerLevelNotice to avoid log spamming.
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
* (required) service name of type GULLoggerService.
|
||||
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||
* string.
|
||||
*/
|
||||
extern void GULLogBasic(GULLoggerLevel level,
|
||||
GULLoggerService service,
|
||||
BOOL forceLog,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
|
||||
// See: http://stackoverflow.com/q/29095469
|
||||
#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
|
||||
va_list args_ptr
|
||||
#else
|
||||
va_list _Nullable args_ptr
|
||||
#endif
|
||||
);
|
||||
|
||||
/**
|
||||
* The following functions accept the following parameters in order:
|
||||
* (required) service name of type GULLoggerService.
|
||||
* (required) message code starting from "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* See go/firebase-log-proposal for details.
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) the list of arguments to substitute into the format string.
|
||||
* Example usage:
|
||||
* GULLogError(kGULLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||
*/
|
||||
extern void GULLogError(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogWarning(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogNotice(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogInfo(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogDebug(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
@interface GULLoggerWrapper : NSObject
|
||||
|
||||
/**
|
||||
* Objective-C wrapper for GULLogBasic to allow weak linking to GULLogger
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
* (required) service name of type GULLoggerService.
|
||||
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||
* string.
|
||||
*/
|
||||
|
||||
+ (void)logWithLevel:(GULLoggerLevel)level
|
||||
withService:(GULLoggerService)service
|
||||
withCode:(NSString *)messageCode
|
||||
withMessage:(NSString *)message
|
||||
withArgs:(va_list)args;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -140,13 +140,21 @@ To ensure that the code is formatted consistently, run the script
|
|||
before creating a PR.
|
||||
|
||||
Travis will verify that any code changes are done in a style compliant way. Install
|
||||
`clang-format` and `swiftformat`:
|
||||
`clang-format` and `swiftformat`.
|
||||
These commands will get the right versions:
|
||||
|
||||
```
|
||||
brew install clang-format
|
||||
brew install swiftformat
|
||||
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb
|
||||
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb
|
||||
```
|
||||
|
||||
Note: if you already have a newer version of these installed you may need to
|
||||
`brew switch` to this version.
|
||||
|
||||
To update this section, find the versions of clang-format and swiftformat.rb to
|
||||
match the versions in the CI failure logs
|
||||
[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||
|
@ -169,7 +177,12 @@ files without real values, but can be replaced with real plist files. To get you
|
|||
2. Create a new Firebase project, if you don't already have one
|
||||
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
|
||||
identifier (e.g. `com.google.Database-Example`)
|
||||
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
|
||||
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
|
||||
(e.g. in [Example/Database/App/](Example/Database/App/));
|
||||
|
||||
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
|
||||
special Apple capabilities, and you will have to change the sample app to use a unique bundle
|
||||
identifier that you can control in your own Apple Developer account.
|
||||
|
||||
## Specific Component Instructions
|
||||
See the sections below for any special instructions for those components.
|
||||
|
@ -189,7 +202,7 @@ To run against a local emulator instance, invoke `./scripts/run_database_emulato
|
|||
running the integration test.
|
||||
|
||||
To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to
|
||||
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to
|
||||
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
|
||||
running.
|
||||
|
||||
|
|
88
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h
generated
Normal file
88
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h
generated
Normal file
|
@ -0,0 +1,88 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if CLS_TARGET_OS_HAS_UIKIT
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define FIRCLSApplicationActivityDefault \
|
||||
(NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled)
|
||||
|
||||
/**
|
||||
* Type to indicate application installation source
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, FIRCLSApplicationInstallationSourceType) {
|
||||
FIRCLSApplicationInstallationSourceTypeDeveloperInstall = 1,
|
||||
// 2 and 3 are reserved for legacy values.
|
||||
FIRCLSApplicationInstallationSourceTypeAppStore = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the application bundle identifier with occurences of "/" replaced by "_"
|
||||
*/
|
||||
NSString* FIRCLSApplicationGetBundleIdentifier(void);
|
||||
|
||||
/**
|
||||
* Returns the SDK's bundle ID
|
||||
*/
|
||||
NSString* FIRCLSApplicationGetSDKBundleID(void);
|
||||
|
||||
/**
|
||||
* Returns the platform identifier, either: ios, mac, or tvos.
|
||||
* Catalyst apps are treated as mac.
|
||||
*/
|
||||
NSString* FIRCLSApplicationGetPlatform(void);
|
||||
|
||||
/**
|
||||
* Returns the user-facing app name
|
||||
*/
|
||||
NSString* FIRCLSApplicationGetName(void);
|
||||
|
||||
/**
|
||||
* Returns the build number
|
||||
*/
|
||||
NSString* FIRCLSApplicationGetBundleVersion(void);
|
||||
|
||||
/**
|
||||
* Returns the human-readable build version
|
||||
*/
|
||||
NSString* FIRCLSApplicationGetShortBundleVersion(void);
|
||||
|
||||
/**
|
||||
* Returns a number to indicate how the app has been installed: Developer / App Store
|
||||
*/
|
||||
FIRCLSApplicationInstallationSourceType FIRCLSApplicationInstallationSource(void);
|
||||
|
||||
BOOL FIRCLSApplicationIsExtension(void);
|
||||
NSString* FIRCLSApplicationExtensionPointIdentifier(void);
|
||||
|
||||
#if CLS_TARGET_OS_HAS_UIKIT
|
||||
UIApplication* FIRCLSApplicationSharedInstance(void);
|
||||
#else
|
||||
id FIRCLSApplicationSharedInstance(void);
|
||||
#endif
|
||||
|
||||
void FIRCLSApplicationOpenURL(NSURL* url,
|
||||
NSExtensionContext* extensionContext,
|
||||
void (^completionBlock)(BOOL success));
|
||||
|
||||
id<NSObject> FIRCLSApplicationBeginActivity(NSActivityOptions options, NSString* reason);
|
||||
void FIRCLSApplicationEndActivity(id<NSObject> activity);
|
||||
|
||||
void FIRCLSApplicationActivity(NSActivityOptions options, NSString* reason, void (^block)(void));
|
||||
|
||||
__END_DECLS
|
211
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m
generated
Normal file
211
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m
generated
Normal file
|
@ -0,0 +1,211 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSApplication.h"
|
||||
|
||||
#import "FIRCLSHost.h"
|
||||
#import "FIRCLSUtility.h"
|
||||
|
||||
#if CLS_TARGET_OS_OSX
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
#if CLS_TARGET_OS_HAS_UIKIT
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
NSString* FIRCLSApplicationGetBundleIdentifier(void) {
|
||||
return [[[NSBundle mainBundle] bundleIdentifier] stringByReplacingOccurrencesOfString:@"/"
|
||||
withString:@"_"];
|
||||
}
|
||||
|
||||
NSString* FIRCLSApplicationGetSDKBundleID(void) {
|
||||
return
|
||||
[@"com.google.firebase.crashlytics." stringByAppendingString:FIRCLSApplicationGetPlatform()];
|
||||
}
|
||||
|
||||
NSString* FIRCLSApplicationGetPlatform(void) {
|
||||
#if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
|
||||
return @"mac";
|
||||
#elif TARGET_OS_IOS
|
||||
return @"ios";
|
||||
#elif TARGET_OS_OSX
|
||||
return @"mac";
|
||||
#elif TARGET_OS_TV
|
||||
return @"tvos";
|
||||
#endif
|
||||
}
|
||||
|
||||
// these defaults match the FIRCLSInfoPlist helper in FIRCLSIDEFoundation
|
||||
NSString* FIRCLSApplicationGetBundleVersion(void) {
|
||||
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
NSString* FIRCLSApplicationGetShortBundleVersion(void) {
|
||||
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
}
|
||||
|
||||
NSString* FIRCLSApplicationGetName(void) {
|
||||
NSString* name;
|
||||
NSBundle* mainBundle;
|
||||
|
||||
mainBundle = [NSBundle mainBundle];
|
||||
|
||||
name = [mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
name = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"];
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return FIRCLSApplicationGetBundleVersion();
|
||||
}
|
||||
|
||||
BOOL FIRCLSApplicationHasAppStoreReceipt(void) {
|
||||
NSURL* url = NSBundle.mainBundle.appStoreReceiptURL;
|
||||
return [NSFileManager.defaultManager fileExistsAtPath:[url path]];
|
||||
}
|
||||
|
||||
FIRCLSApplicationInstallationSourceType FIRCLSApplicationInstallationSource(void) {
|
||||
if (FIRCLSApplicationHasAppStoreReceipt()) {
|
||||
return FIRCLSApplicationInstallationSourceTypeAppStore;
|
||||
}
|
||||
|
||||
return FIRCLSApplicationInstallationSourceTypeDeveloperInstall;
|
||||
}
|
||||
|
||||
BOOL FIRCLSApplicationIsExtension(void) {
|
||||
return FIRCLSApplicationExtensionPointIdentifier() != nil;
|
||||
}
|
||||
|
||||
NSString* FIRCLSApplicationExtensionPointIdentifier(void) {
|
||||
id extensionDict = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"NSExtension"];
|
||||
|
||||
if (!extensionDict) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![extensionDict isKindOfClass:[NSDictionary class]]) {
|
||||
FIRCLSSDKLog("Error: NSExtension Info.plist entry is mal-formed\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
id typeValue = [(NSDictionary*)extensionDict objectForKey:@"NSExtensionPointIdentifier"];
|
||||
|
||||
if (![typeValue isKindOfClass:[NSString class]]) {
|
||||
FIRCLSSDKLog("Error: NSExtensionPointIdentifier Info.plist entry is mal-formed\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return typeValue;
|
||||
}
|
||||
|
||||
#if CLS_TARGET_OS_HAS_UIKIT
|
||||
UIApplication* FIRCLSApplicationSharedInstance(void) {
|
||||
if (FIRCLSApplicationIsExtension()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [[UIApplication class] performSelector:@selector(sharedApplication)];
|
||||
}
|
||||
#elif CLS_TARGET_OS_OSX
|
||||
id FIRCLSApplicationSharedInstance(void) {
|
||||
return [NSClassFromString(@"NSApplication") sharedApplication];
|
||||
}
|
||||
#else
|
||||
id FIRCLSApplicationSharedInstance(void) {
|
||||
return nil; // FIXME: what do we actually return for watch?
|
||||
}
|
||||
#endif
|
||||
|
||||
void FIRCLSApplicationOpenURL(NSURL* url,
|
||||
NSExtensionContext* extensionContext,
|
||||
void (^completionBlock)(BOOL success)) {
|
||||
if (extensionContext) {
|
||||
[extensionContext openURL:url completionHandler:completionBlock];
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL result = NO;
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
// What's going on here is the value returned is a scalar, but we really need an object to
|
||||
// call this dynamically. Hoops must be jumped.
|
||||
NSInvocationOperation* op =
|
||||
[[NSInvocationOperation alloc] initWithTarget:FIRCLSApplicationSharedInstance()
|
||||
selector:@selector(openURL:)
|
||||
object:url];
|
||||
[op start];
|
||||
[op.result getValue:&result];
|
||||
#elif CLS_TARGET_OS_OSX
|
||||
result = [[NSClassFromString(@"NSWorkspace") sharedWorkspace] openURL:url];
|
||||
#endif
|
||||
|
||||
completionBlock(result);
|
||||
}
|
||||
|
||||
id<NSObject> FIRCLSApplicationBeginActivity(NSActivityOptions options, NSString* reason) {
|
||||
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(beginActivityWithOptions:
|
||||
reason:)]) {
|
||||
return [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:reason];
|
||||
}
|
||||
|
||||
#if CLS_TARGET_OS_OSX
|
||||
if (options & NSActivitySuddenTerminationDisabled) {
|
||||
[[NSProcessInfo processInfo] disableSuddenTermination];
|
||||
}
|
||||
|
||||
if (options & NSActivityAutomaticTerminationDisabled) {
|
||||
[[NSProcessInfo processInfo] disableAutomaticTermination:reason];
|
||||
}
|
||||
#endif
|
||||
|
||||
// encode the options, so we can undo our work later
|
||||
return @{@"options" : @(options), @"reason" : reason};
|
||||
}
|
||||
|
||||
void FIRCLSApplicationEndActivity(id<NSObject> activity) {
|
||||
if (!activity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([[NSProcessInfo processInfo] respondsToSelector:@selector(endActivity:)]) {
|
||||
[[NSProcessInfo processInfo] endActivity:activity];
|
||||
return;
|
||||
}
|
||||
|
||||
#if CLS_TARGET_OS_OSX
|
||||
NSInteger options = [[(NSDictionary*)activity objectForKey:@"options"] integerValue];
|
||||
|
||||
if (options & NSActivitySuddenTerminationDisabled) {
|
||||
[[NSProcessInfo processInfo] enableSuddenTermination];
|
||||
}
|
||||
|
||||
if (options & NSActivityAutomaticTerminationDisabled) {
|
||||
[[NSProcessInfo processInfo]
|
||||
enableAutomaticTermination:[(NSDictionary*)activity objectForKey:@"reason"]];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FIRCLSApplicationActivity(NSActivityOptions options, NSString* reason, void (^block)(void)) {
|
||||
id<NSObject> activity = FIRCLSApplicationBeginActivity(options, reason);
|
||||
|
||||
block();
|
||||
|
||||
FIRCLSApplicationEndActivity(activity);
|
||||
}
|
81
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h
generated
Normal file
81
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h
generated
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "FIRCLSFeatures.h"
|
||||
#include "FIRCLSFile.h"
|
||||
#include "FIRCLSMachO.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
// Typically, apps seem to have ~300 binary images loaded
|
||||
#define CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT (512)
|
||||
#define CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE (32)
|
||||
#define CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME 0
|
||||
|
||||
#define FIRCLSUUIDStringLength (33)
|
||||
|
||||
typedef struct {
|
||||
_Atomic(void*) volatile baseAddress;
|
||||
uint64_t size;
|
||||
#if CLS_DWARF_UNWINDING_SUPPORTED
|
||||
const void* ehFrame;
|
||||
#endif
|
||||
#if CLS_COMPACT_UNWINDING_SUPPORTED
|
||||
const void* unwindInfo;
|
||||
#endif
|
||||
const void* crashInfo;
|
||||
#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
|
||||
char name[CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE];
|
||||
#endif
|
||||
} FIRCLSBinaryImageRuntimeNode;
|
||||
|
||||
typedef struct {
|
||||
char uuidString[FIRCLSUUIDStringLength];
|
||||
bool encrypted;
|
||||
FIRCLSMachOVersion builtSDK;
|
||||
FIRCLSMachOVersion minSDK;
|
||||
FIRCLSBinaryImageRuntimeNode node;
|
||||
struct FIRCLSMachOSlice slice;
|
||||
intptr_t vmaddr_slide;
|
||||
} FIRCLSBinaryImageDetails;
|
||||
|
||||
typedef struct {
|
||||
const char* path;
|
||||
} FIRCLSBinaryImageReadOnlyContext;
|
||||
|
||||
typedef struct {
|
||||
FIRCLSFile file;
|
||||
FIRCLSBinaryImageRuntimeNode nodes[CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT];
|
||||
} FIRCLSBinaryImageReadWriteContext;
|
||||
|
||||
void FIRCLSBinaryImageInit(FIRCLSBinaryImageReadOnlyContext* roContext,
|
||||
FIRCLSBinaryImageReadWriteContext* rwContext);
|
||||
|
||||
#if CLS_COMPACT_UNWINDING_SUPPORTED
|
||||
bool FIRCLSBinaryImageSafeFindImageForAddress(uintptr_t address,
|
||||
FIRCLSBinaryImageRuntimeNode* image);
|
||||
bool FIRCLSBinaryImageSafeHasUnwindInfo(FIRCLSBinaryImageRuntimeNode* image);
|
||||
#endif
|
||||
|
||||
bool FIRCLSBinaryImageFindImageForUUID(const char* uuidString,
|
||||
FIRCLSBinaryImageDetails* imageDetails);
|
||||
|
||||
bool FIRCLSBinaryImageRecordMainExecutable(FIRCLSFile* file);
|
||||
|
||||
__END_DECLS
|
571
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m
generated
Normal file
571
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m
generated
Normal file
|
@ -0,0 +1,571 @@
|
|||
// 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 "FIRCLSBinaryImage.h"
|
||||
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
#include <mach-o/getsect.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "FIRCLSByteUtility.h"
|
||||
#include "FIRCLSFeatures.h"
|
||||
#include "FIRCLSFile.h"
|
||||
#include "FIRCLSGlobals.h"
|
||||
#include "FIRCLSHost.h"
|
||||
#include "FIRCLSMachO.h"
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
// this is defined only if __OPEN_SOURCE__ is *not* defined in the TVOS SDK's mach-o/loader.h
|
||||
// also, it has not yet made it back to the OSX SDKs, for example
|
||||
#ifndef LC_VERSION_MIN_TVOS
|
||||
#define LC_VERSION_MIN_TVOS 0x2F
|
||||
#endif
|
||||
|
||||
#pragma mark Prototypes
|
||||
static bool FIRCLSBinaryImageOpenIfNeeded(bool* needsClosing);
|
||||
|
||||
static void FIRCLSBinaryImageAddedCallback(const struct mach_header* mh, intptr_t vmaddr_slide);
|
||||
static void FIRCLSBinaryImageRemovedCallback(const struct mach_header* mh, intptr_t vmaddr_slide);
|
||||
static void FIRCLSBinaryImageChanged(bool added,
|
||||
const struct mach_header* mh,
|
||||
intptr_t vmaddr_slide);
|
||||
static bool FIRCLSBinaryImageFillInImageDetails(FIRCLSBinaryImageDetails* details);
|
||||
|
||||
static void FIRCLSBinaryImageStoreNode(bool added, FIRCLSBinaryImageDetails imageDetails);
|
||||
static void FIRCLSBinaryImageRecordSlice(bool added, const FIRCLSBinaryImageDetails imageDetails);
|
||||
|
||||
#pragma mark - Core API
|
||||
void FIRCLSBinaryImageInit(FIRCLSBinaryImageReadOnlyContext* roContext,
|
||||
FIRCLSBinaryImageReadWriteContext* rwContext) {
|
||||
// initialize our node array to all zeros
|
||||
memset(&_firclsContext.writable->binaryImage, 0, sizeof(_firclsContext.writable->binaryImage));
|
||||
_firclsContext.writable->binaryImage.file.fd = -1;
|
||||
|
||||
dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
|
||||
if (!FIRCLSUnlinkIfExists(_firclsContext.readonly->binaryimage.path)) {
|
||||
FIRCLSSDKLog("Unable to reset the binary image log file %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
bool needsClosing; // unneeded
|
||||
if (!FIRCLSBinaryImageOpenIfNeeded(&needsClosing)) {
|
||||
FIRCLSSDKLog("Error: Unable to open the binary image log file during init\n");
|
||||
}
|
||||
});
|
||||
|
||||
_dyld_register_func_for_add_image(FIRCLSBinaryImageAddedCallback);
|
||||
_dyld_register_func_for_remove_image(FIRCLSBinaryImageRemovedCallback);
|
||||
|
||||
dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
|
||||
FIRCLSFileClose(&_firclsContext.writable->binaryImage.file);
|
||||
});
|
||||
}
|
||||
|
||||
static bool FIRCLSBinaryImageOpenIfNeeded(bool* needsClosing) {
|
||||
if (!FIRCLSIsValidPointer(_firclsContext.writable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FIRCLSIsValidPointer(_firclsContext.readonly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FIRCLSIsValidPointer(needsClosing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*needsClosing = false;
|
||||
|
||||
if (FIRCLSFileIsOpen(&_firclsContext.writable->binaryImage.file)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!FIRCLSFileInitWithPath(&_firclsContext.writable->binaryImage.file,
|
||||
_firclsContext.readonly->binaryimage.path, false)) {
|
||||
FIRCLSSDKLog("Error: unable to open binary image log file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*needsClosing = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if CLS_COMPACT_UNWINDING_SUPPORTED
|
||||
bool FIRCLSBinaryImageSafeFindImageForAddress(uintptr_t address,
|
||||
FIRCLSBinaryImageRuntimeNode* image) {
|
||||
if (!FIRCLSContextIsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FIRCLSIsValidPointer(image)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FIRCLSBinaryImageRuntimeNode* nodes = _firclsContext.writable->binaryImage.nodes;
|
||||
if (!nodes) {
|
||||
FIRCLSSDKLogError("The node structure is NULL\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) {
|
||||
FIRCLSBinaryImageRuntimeNode* node = &nodes[i];
|
||||
if (!FIRCLSIsValidPointer(node)) {
|
||||
FIRCLSSDKLog(
|
||||
"Invalid node pointer encountered in context's writable binary image at index %i", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((address >= (uintptr_t)node->baseAddress) &&
|
||||
(address < (uintptr_t)node->baseAddress + node->size)) {
|
||||
*image = *node; // copy the image
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FIRCLSBinaryImageSafeHasUnwindInfo(FIRCLSBinaryImageRuntimeNode* image) {
|
||||
return FIRCLSIsValidPointer(image->unwindInfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool FIRCLSBinaryImageFindImageForUUID(const char* uuidString,
|
||||
FIRCLSBinaryImageDetails* imageDetails) {
|
||||
if (!imageDetails || !uuidString) {
|
||||
FIRCLSSDKLog("null input\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t imageCount = _dyld_image_count();
|
||||
|
||||
for (uint32_t i = 0; i < imageCount; ++i) {
|
||||
const struct mach_header* mh = _dyld_get_image_header(i);
|
||||
|
||||
FIRCLSBinaryImageDetails image;
|
||||
|
||||
image.slice = FIRCLSMachOSliceWithHeader((void*)mh);
|
||||
FIRCLSBinaryImageFillInImageDetails(&image);
|
||||
|
||||
if (strncmp(uuidString, image.uuidString, FIRCLSUUIDStringLength) == 0) {
|
||||
*imageDetails = image;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma mark - DYLD callback handlers
|
||||
static void FIRCLSBinaryImageAddedCallback(const struct mach_header* mh, intptr_t vmaddr_slide) {
|
||||
FIRCLSBinaryImageChanged(true, mh, vmaddr_slide);
|
||||
}
|
||||
|
||||
static void FIRCLSBinaryImageRemovedCallback(const struct mach_header* mh, intptr_t vmaddr_slide) {
|
||||
FIRCLSBinaryImageChanged(false, mh, vmaddr_slide);
|
||||
}
|
||||
|
||||
#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
|
||||
static bool FIRCLSBinaryImagePopulateRuntimeNodeName(FIRCLSBinaryImageDetails* details) {
|
||||
if (!FIRCLSIsValidPointer(details)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(details->node.name, 0, CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE);
|
||||
|
||||
// We have limited storage space for the name. And, we really want to store
|
||||
// "CoreFoundation", not "/System/Library/Fram", so we have to play tricks
|
||||
// to make sure we get the right side of the string.
|
||||
const char* imageName = FIRCLSMachOSliceGetExecutablePath(&details->slice);
|
||||
if (!imageName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t imageNameLength = strlen(imageName);
|
||||
|
||||
// Remember to leave one character for null-termination.
|
||||
if (imageNameLength > CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1) {
|
||||
imageName = imageName + (imageNameLength - (CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1));
|
||||
}
|
||||
|
||||
// subtract one to make sure the string is always null-terminated
|
||||
strncpy(details->node.name, imageName, CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// There were plans later to replace this with FIRCLSMachO
|
||||
static FIRCLSMachOSegmentCommand FIRCLSBinaryImageMachOGetSegmentCommand(
|
||||
const struct load_command* cmd) {
|
||||
FIRCLSMachOSegmentCommand segmentCommand;
|
||||
|
||||
memset(&segmentCommand, 0, sizeof(FIRCLSMachOSegmentCommand));
|
||||
|
||||
if (!cmd) {
|
||||
return segmentCommand;
|
||||
}
|
||||
|
||||
if (cmd->cmd == LC_SEGMENT) {
|
||||
struct segment_command* segCmd = (struct segment_command*)cmd;
|
||||
|
||||
memcpy(segmentCommand.segname, segCmd->segname, 16);
|
||||
segmentCommand.vmaddr = segCmd->vmaddr;
|
||||
segmentCommand.vmsize = segCmd->vmsize;
|
||||
} else if (cmd->cmd == LC_SEGMENT_64) {
|
||||
struct segment_command_64* segCmd = (struct segment_command_64*)cmd;
|
||||
|
||||
memcpy(segmentCommand.segname, segCmd->segname, 16);
|
||||
segmentCommand.vmaddr = segCmd->vmaddr;
|
||||
segmentCommand.vmsize = segCmd->vmsize;
|
||||
}
|
||||
|
||||
return segmentCommand;
|
||||
}
|
||||
|
||||
static bool FIRCLSBinaryImageMachOSliceInitSectionByName(FIRCLSMachOSliceRef slice,
|
||||
const char* segName,
|
||||
const char* sectionName,
|
||||
FIRCLSMachOSection* section) {
|
||||
if (!FIRCLSIsValidPointer(slice)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!section) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(section, 0, sizeof(FIRCLSMachOSection));
|
||||
|
||||
if (FIRCLSMachOSliceIs64Bit(slice)) {
|
||||
const struct section_64* sect =
|
||||
getsectbynamefromheader_64(slice->startAddress, segName, sectionName);
|
||||
if (!sect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
section->addr = sect->addr;
|
||||
section->size = sect->size;
|
||||
section->offset = sect->offset;
|
||||
} else {
|
||||
const struct section* sect = getsectbynamefromheader(slice->startAddress, segName, sectionName);
|
||||
if (!sect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
section->addr = sect->addr;
|
||||
section->size = sect->size;
|
||||
section->offset = sect->offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FIRCLSBinaryImageFillInImageDetails(FIRCLSBinaryImageDetails* details) {
|
||||
if (!FIRCLSIsValidPointer(details)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FIRCLSIsValidPointer(details->slice.startAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
|
||||
// this is done for debugging purposes, so if it fails, its ok to continue
|
||||
FIRCLSBinaryImagePopulateRuntimeNodeName(details);
|
||||
#endif
|
||||
|
||||
// This cast might look a little dubious, but its just because we're using the same
|
||||
// struct types in a few different places.
|
||||
details->node.baseAddress = (void* volatile)details->slice.startAddress;
|
||||
|
||||
FIRCLSMachOSliceEnumerateLoadCommands(
|
||||
&details->slice, ^(uint32_t type, uint32_t size, const struct load_command* cmd) {
|
||||
switch (type) {
|
||||
case LC_UUID: {
|
||||
const uint8_t* uuid = FIRCLSMachOGetUUID(cmd);
|
||||
FIRCLSSafeHexToString(uuid, 16, details->uuidString);
|
||||
} break;
|
||||
case LC_ENCRYPTION_INFO:
|
||||
details->encrypted = FIRCLSMachOGetEncrypted(cmd);
|
||||
break;
|
||||
case LC_SEGMENT:
|
||||
case LC_SEGMENT_64: {
|
||||
FIRCLSMachOSegmentCommand segmentCommand = FIRCLSBinaryImageMachOGetSegmentCommand(cmd);
|
||||
|
||||
if (strncmp(segmentCommand.segname, SEG_TEXT, sizeof(SEG_TEXT)) == 0) {
|
||||
details->node.size = segmentCommand.vmsize;
|
||||
}
|
||||
} break;
|
||||
case LC_VERSION_MIN_MACOSX:
|
||||
case LC_VERSION_MIN_IPHONEOS:
|
||||
case LC_VERSION_MIN_TVOS:
|
||||
case LC_VERSION_MIN_WATCHOS:
|
||||
details->minSDK = FIRCLSMachOGetMinimumOSVersion(cmd);
|
||||
details->builtSDK = FIRCLSMachOGetLinkedSDKVersion(cmd);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// We look up the section we want, and we *should* be able to use:
|
||||
//
|
||||
// address of data we want = start address + section.offset
|
||||
//
|
||||
// However, the offset value is coming back funky in iOS 9. So, instead we look up the address
|
||||
// the section should be loaded at, and compute the offset by looking up the address of the
|
||||
// segment itself.
|
||||
|
||||
FIRCLSMachOSection section;
|
||||
|
||||
#if CLS_COMPACT_UNWINDING_SUPPORTED
|
||||
if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_TEXT, "__unwind_info",
|
||||
§ion)) {
|
||||
details->node.unwindInfo = (void*)(section.addr + details->vmaddr_slide);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CLS_DWARF_UNWINDING_SUPPORTED
|
||||
if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_TEXT, "__eh_frame",
|
||||
§ion)) {
|
||||
details->node.ehFrame = (void*)(section.addr + details->vmaddr_slide);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_DATA, "__crash_info",
|
||||
§ion)) {
|
||||
details->node.crashInfo = (void*)(section.addr + details->vmaddr_slide);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FIRCLSBinaryImageChanged(bool added,
|
||||
const struct mach_header* mh,
|
||||
intptr_t vmaddr_slide) {
|
||||
// FIRCLSSDKLog("Binary image %s %p\n", added ? "loaded" : "unloaded", mh);
|
||||
|
||||
FIRCLSBinaryImageDetails imageDetails;
|
||||
|
||||
memset(&imageDetails, 0, sizeof(FIRCLSBinaryImageDetails));
|
||||
|
||||
imageDetails.slice = FIRCLSMachOSliceWithHeader((void*)mh);
|
||||
imageDetails.vmaddr_slide = vmaddr_slide;
|
||||
FIRCLSBinaryImageFillInImageDetails(&imageDetails);
|
||||
|
||||
// this is an atomic operation
|
||||
FIRCLSBinaryImageStoreNode(added, imageDetails);
|
||||
|
||||
// this isn't, so do it on a serial queue
|
||||
dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
|
||||
FIRCLSBinaryImageRecordSlice(added, imageDetails);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - In-Memory Storage
|
||||
static void FIRCLSBinaryImageStoreNode(bool added, FIRCLSBinaryImageDetails imageDetails) {
|
||||
// This function is mutating a structure that needs to be accessed at crash time. We
|
||||
// need to make sure the structure is always in as valid a state as possible.
|
||||
// FIRCLSSDKLog("Storing %s node %p\n", added ? "loaded" : "unloaded",
|
||||
// (void*)imageDetails.node.baseAddress);
|
||||
|
||||
if (!_firclsContext.writable) {
|
||||
FIRCLSSDKLog("Error: Writable context is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void* searchAddress = NULL;
|
||||
bool success = false;
|
||||
FIRCLSBinaryImageRuntimeNode* nodes = _firclsContext.writable->binaryImage.nodes;
|
||||
|
||||
if (!added) {
|
||||
// capture the search address first
|
||||
searchAddress = imageDetails.node.baseAddress;
|
||||
|
||||
// If we are removing a node, we need to set its entries to zero. By clearing all of
|
||||
// these values, we can just copy in imageDetails.node. Using memset here is slightly
|
||||
// weird, since we have to restore one field. But, this way, if/when the structure changes,
|
||||
// we still do the right thing.
|
||||
memset(&imageDetails.node, 0, sizeof(FIRCLSBinaryImageRuntimeNode));
|
||||
|
||||
// restore the baseAddress, which just got zeroed, and is used for indexing
|
||||
imageDetails.node.baseAddress = searchAddress;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) {
|
||||
FIRCLSBinaryImageRuntimeNode* node = &nodes[i];
|
||||
|
||||
if (!node) {
|
||||
FIRCLSSDKLog("Error: Binary image storage is NULL\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// navigate through the array, looking for our matching address
|
||||
if (node->baseAddress != searchAddress) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempt to swap the base address with whatever we are searching for. Success means that
|
||||
// entry has been claims/cleared. Failure means some other thread beat us to it.
|
||||
if (atomic_compare_exchange_strong(&node->baseAddress, &searchAddress,
|
||||
imageDetails.node.baseAddress)) {
|
||||
*node = imageDetails.node;
|
||||
success = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is an unload, getting here means two threads unloaded at the same time. I think
|
||||
// that's highly unlikely, and possibly even impossible. So, I'm choosing to abort the process
|
||||
// at this point.
|
||||
if (!added) {
|
||||
FIRCLSSDKLog("Error: Failed to swap during image unload\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
FIRCLSSDKLog("Error: Unable to track a %s node %p\n", added ? "loaded" : "unloaded",
|
||||
(void*)imageDetails.node.baseAddress);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - On-Disk Storage
|
||||
static void FIRCLSBinaryImageRecordDetails(FIRCLSFile* file,
|
||||
const FIRCLSBinaryImageDetails imageDetails) {
|
||||
if (!file) {
|
||||
FIRCLSSDKLog("Error: file is invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteHashEntryString(file, "uuid", imageDetails.uuidString);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "base", (uintptr_t)imageDetails.slice.startAddress);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "size", imageDetails.node.size);
|
||||
}
|
||||
|
||||
static void FIRCLSBinaryImageRecordLibraryFrameworkInfo(FIRCLSFile* file, const char* path) {
|
||||
if (!file) {
|
||||
FIRCLSSDKLog("Error: file is invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Because this function is so expensive, we've decided to omit this info for all Apple-supplied
|
||||
// frameworks. This really isn't that bad, because we can know their info ahead of time (within a
|
||||
// small margin of error). With this implemenation, we will still record this info for any
|
||||
// user-built framework, which in the end is the most important thing.
|
||||
if (strncmp(path, "/System", 7) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check to see if this is a potential framework bundle
|
||||
if (!strstr(path, ".framework")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// My.framework/Versions/A/My for OS X
|
||||
// My.framework/My for iOS
|
||||
|
||||
NSString* frameworkPath = [NSString stringWithUTF8String:path];
|
||||
#if TARGET_OS_IPHONE
|
||||
frameworkPath = [frameworkPath stringByDeletingLastPathComponent];
|
||||
#else
|
||||
frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework/Versions/A
|
||||
frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework/Versions
|
||||
frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework
|
||||
#endif
|
||||
|
||||
NSBundle* const bundle = [NSBundle bundleWithPath:frameworkPath];
|
||||
|
||||
if (!bundle) {
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(file, "bundle_id", [bundle bundleIdentifier]);
|
||||
FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(
|
||||
file, "build_version", [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]);
|
||||
FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(
|
||||
file, "display_version", [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]);
|
||||
}
|
||||
|
||||
static void FIRCLSBinaryImageRecordSlice(bool added, const FIRCLSBinaryImageDetails imageDetails) {
|
||||
bool needsClosing = false;
|
||||
if (!FIRCLSBinaryImageOpenIfNeeded(&needsClosing)) {
|
||||
FIRCLSSDKLog("Error: unable to open binary image log file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFile* file = &_firclsContext.writable->binaryImage.file;
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, added ? "load" : "unload");
|
||||
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
const char* path = FIRCLSMachOSliceGetExecutablePath((FIRCLSMachOSliceRef)&imageDetails.slice);
|
||||
|
||||
FIRCLSFileWriteHashEntryString(file, "path", path);
|
||||
|
||||
if (added) {
|
||||
// this won't work if the binary has been unloaded
|
||||
FIRCLSBinaryImageRecordLibraryFrameworkInfo(file, path);
|
||||
}
|
||||
|
||||
FIRCLSBinaryImageRecordDetails(file, imageDetails);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
|
||||
if (needsClosing) {
|
||||
FIRCLSFileClose(file);
|
||||
}
|
||||
}
|
||||
|
||||
bool FIRCLSBinaryImageRecordMainExecutable(FIRCLSFile* file) {
|
||||
FIRCLSBinaryImageDetails imageDetails;
|
||||
|
||||
memset(&imageDetails, 0, sizeof(FIRCLSBinaryImageDetails));
|
||||
|
||||
imageDetails.slice = FIRCLSMachOSliceGetCurrent();
|
||||
FIRCLSBinaryImageFillInImageDetails(&imageDetails);
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "executable");
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
FIRCLSFileWriteHashEntryString(file, "architecture",
|
||||
FIRCLSMachOSliceGetArchitectureName(&imageDetails.slice));
|
||||
|
||||
FIRCLSBinaryImageRecordDetails(file, imageDetails);
|
||||
FIRCLSFileWriteHashEntryBoolean(file, "encrypted", imageDetails.encrypted);
|
||||
FIRCLSFileWriteHashEntryString(file, "minimum_sdk_version",
|
||||
[FIRCLSMachOFormatVersion(&imageDetails.minSDK) UTF8String]);
|
||||
FIRCLSFileWriteHashEntryString(file, "built_sdk_version",
|
||||
[FIRCLSMachOFormatVersion(&imageDetails.builtSDK) UTF8String]);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
|
||||
return true;
|
||||
}
|
121
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h
generated
Normal file
121
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h
generated
Normal file
|
@ -0,0 +1,121 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FIRCLSAllocate.h"
|
||||
#include "FIRCLSBinaryImage.h"
|
||||
#include "FIRCLSException.h"
|
||||
#include "FIRCLSFeatures.h"
|
||||
#include "FIRCLSHost.h"
|
||||
#include "FIRCLSInternalLogging.h"
|
||||
#include "FIRCLSMachException.h"
|
||||
#include "FIRCLSSignal.h"
|
||||
#include "FIRCLSUserLogging.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// The purpose of the crash context is to hold values that absolutely must be read and/or written at
|
||||
// crash time. For robustness against memory corruption, they are protected with guard pages.
|
||||
// Further, the context is seperated into read-only and read-write sections.
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class FIRCLSInternalReport;
|
||||
@class FIRCLSSettings;
|
||||
@class FIRCLSInstallIdentifierModel;
|
||||
@class FIRCLSFileManager;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
volatile bool initialized;
|
||||
volatile bool debuggerAttached;
|
||||
const char* previouslyCrashedFileFullPath;
|
||||
const char* logPath;
|
||||
#if CLS_USE_SIGALTSTACK
|
||||
void* signalStack;
|
||||
#endif
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
void* machStack;
|
||||
#endif
|
||||
void* delegate;
|
||||
void* callbackDelegate;
|
||||
|
||||
FIRCLSBinaryImageReadOnlyContext binaryimage;
|
||||
FIRCLSExceptionReadOnlyContext exception;
|
||||
FIRCLSHostReadOnlyContext host;
|
||||
FIRCLSSignalReadContext signal;
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
FIRCLSMachExceptionReadContext machException;
|
||||
#endif
|
||||
FIRCLSUserLoggingReadOnlyContext logging;
|
||||
} FIRCLSReadOnlyContext;
|
||||
|
||||
typedef struct {
|
||||
FIRCLSInternalLoggingWritableContext internalLogging;
|
||||
volatile bool crashOccurred;
|
||||
FIRCLSBinaryImageReadWriteContext binaryImage;
|
||||
FIRCLSUserLoggingWritableContext logging;
|
||||
FIRCLSExceptionWritableContext exception;
|
||||
} FIRCLSReadWriteContext;
|
||||
|
||||
typedef struct {
|
||||
FIRCLSReadOnlyContext* readonly;
|
||||
FIRCLSReadWriteContext* writable;
|
||||
FIRCLSAllocatorRef allocator;
|
||||
} FIRCLSContext;
|
||||
|
||||
typedef struct {
|
||||
void* delegate;
|
||||
const char* customBundleId;
|
||||
const char* rootPath;
|
||||
const char* previouslyCrashedFileRootPath;
|
||||
const char* sessionId;
|
||||
const char* installId;
|
||||
const char* betaToken;
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
exception_mask_t machExceptionMask;
|
||||
#endif
|
||||
bool errorsEnabled;
|
||||
bool customExceptionsEnabled;
|
||||
uint32_t maxCustomExceptions;
|
||||
uint32_t maxErrorLogSize;
|
||||
uint32_t maxLogSize;
|
||||
uint32_t maxKeyValues;
|
||||
} FIRCLSContextInitData;
|
||||
|
||||
#ifdef __OBJC__
|
||||
bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
|
||||
FIRCLSSettings* settings,
|
||||
FIRCLSInstallIdentifierModel* installIDModel,
|
||||
FIRCLSFileManager* fileManager);
|
||||
|
||||
// Re-writes the metadata file on the current thread
|
||||
void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report,
|
||||
FIRCLSSettings* settings,
|
||||
FIRCLSInstallIdentifierModel* installIDModel,
|
||||
FIRCLSFileManager* fileManager);
|
||||
#endif
|
||||
|
||||
void FIRCLSContextBaseInit(void);
|
||||
void FIRCLSContextBaseDeinit(void);
|
||||
|
||||
bool FIRCLSContextIsInitialized(void);
|
||||
bool FIRCLSContextHasCrashed(void);
|
||||
void FIRCLSContextMarkHasCrashed(void);
|
||||
bool FIRCLSContextMarkAndCheckIfCrashed(void);
|
||||
|
||||
__END_DECLS
|
468
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m
generated
Normal file
468
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m
generated
Normal file
|
@ -0,0 +1,468 @@
|
|||
// 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 "FIRCLSContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#import "FIRCLSFileManager.h"
|
||||
#import "FIRCLSInstallIdentifierModel.h"
|
||||
#import "FIRCLSInternalReport.h"
|
||||
#import "FIRCLSSettings.h"
|
||||
|
||||
#include "FIRCLSApplication.h"
|
||||
#include "FIRCLSCrashedMarkerFile.h"
|
||||
#include "FIRCLSDefines.h"
|
||||
#include "FIRCLSFeatures.h"
|
||||
#include "FIRCLSGlobals.h"
|
||||
#include "FIRCLSProcess.h"
|
||||
#include "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,
|
||||
FIRCLSInstallIdentifierModel* installIDModel,
|
||||
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 fetched
|
||||
|
||||
FIRCLSContextInitData initData;
|
||||
|
||||
memset(&initData, 0, sizeof(FIRCLSContextInitData));
|
||||
|
||||
initData.customBundleId = nil;
|
||||
initData.installId = [installIDModel.installID UTF8String];
|
||||
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];
|
||||
|
||||
// If this is set, then we could attempt to do a synchronous submission for certain kinds of
|
||||
// events (exceptions). This is a very cool feature, but adds complexity to the backend. For now,
|
||||
// we're going to leave this disabled. It does work in the exception case, but will ultimtely
|
||||
// result in the following crash to be discared. Usually that crash isn't interesting. But, if it
|
||||
// was, we'd never have a chance to see it.
|
||||
initData.delegate = nil;
|
||||
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
__block exception_mask_t mask = 0;
|
||||
|
||||
// TODO(b/141241224) This if statement was hardcoded to no, so this block was never run
|
||||
// FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
|
||||
// if ([self.delegate ensureDeliveryOfUnixSignal:signal]) {
|
||||
// mask |= FIRCLSMachExceptionMaskForSignal(signal);
|
||||
// }
|
||||
// });
|
||||
|
||||
initData.machExceptionMask = mask;
|
||||
#endif
|
||||
|
||||
return initData;
|
||||
}
|
||||
|
||||
bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
|
||||
FIRCLSSettings* settings,
|
||||
FIRCLSInstallIdentifierModel* installIDModel,
|
||||
FIRCLSFileManager* fileManager) {
|
||||
FIRCLSContextInitData initDataObj =
|
||||
FIRCLSContextBuildInitData(report, settings, installIDModel, 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");
|
||||
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();
|
||||
_firclsContext.readonly->delegate = initData->delegate;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
if (!_firclsContext.readonly->debuggerAttached) {
|
||||
dispatch_group_async(group, queue, ^{
|
||||
_firclsContext.readonly->signal.path =
|
||||
FIRCLSContextAppendToRoot(rootPath, FIRCLSReportSignalFile);
|
||||
|
||||
FIRCLSSignalInitialize(&_firclsContext.readonly->signal);
|
||||
});
|
||||
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
dispatch_group_async(group, queue, ^{
|
||||
_firclsContext.readonly->machException.path =
|
||||
FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile);
|
||||
|
||||
FIRCLSMachExceptionInit(&_firclsContext.readonly->machException, initData->machExceptionMask);
|
||||
});
|
||||
#endif
|
||||
|
||||
dispatch_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, initData->delegate);
|
||||
});
|
||||
} 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 FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report,
|
||||
FIRCLSSettings* settings,
|
||||
FIRCLSInstallIdentifierModel* installIDModel,
|
||||
FIRCLSFileManager* fileManager) {
|
||||
FIRCLSContextInitData initDataObj =
|
||||
FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager);
|
||||
FIRCLSContextInitData* initData = &initDataObj;
|
||||
|
||||
NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath];
|
||||
|
||||
const char* metaDataPath =
|
||||
[[rootPath stringByAppendingPathComponent:FIRCLSReportMetadataFile] fileSystemRepresentation];
|
||||
|
||||
if (!FIRCLSContextRecordMetadata(metaDataPath, initData)) {
|
||||
FIRCLSErrorLog(@"Unable to update context metadata");
|
||||
}
|
||||
}
|
||||
|
||||
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_SUPPORTED
|
||||
context->readonly->machStack = FIRCLSAllocatorSafeAllocate(
|
||||
context->allocator, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE, CLS_READWRITE);
|
||||
#endif
|
||||
#if CLS_USE_SIGALTSTACK
|
||||
context->readonly->signalStack =
|
||||
FIRCLSAllocatorSafeAllocate(context->allocator, CLS_SIGNAL_HANDLER_STACK_SIZE, CLS_READWRITE);
|
||||
#endif
|
||||
#else
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
context->readonly->machStack = valloc(CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE);
|
||||
#endif
|
||||
#if CLS_USE_SIGALTSTACK
|
||||
context->readonly->signalStack = valloc(CLS_SIGNAL_HANDLER_STACK_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
memset(_firclsContext.readonly->machStack, 0, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE);
|
||||
#endif
|
||||
#if CLS_USE_SIGALTSTACK
|
||||
memset(_firclsContext.readonly->signalStack, 0, CLS_SIGNAL_HANDLER_STACK_SIZE);
|
||||
#endif
|
||||
|
||||
context->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 ok
|
||||
return _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", CLS_SDK_GENERATOR_NAME);
|
||||
FIRCLSFileWriteHashEntryString(file, "display_version", CLS_SDK_DISPLAY_VERSION);
|
||||
FIRCLSFileWriteHashEntryString(file, "build_version", CLS_SDK_DISPLAY_VERSION);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "started_at", time(NULL));
|
||||
|
||||
FIRCLSFileWriteHashEntryString(file, "session_id", initData->sessionId);
|
||||
FIRCLSFileWriteHashEntryString(file, "install_id", initData->installId);
|
||||
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;
|
||||
}
|
31
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c
generated
Normal file
31
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c
generated
Normal file
|
@ -0,0 +1,31 @@
|
|||
// 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 "FIRCLSCrashedMarkerFile.h"
|
||||
#include "FIRCLSFile.h"
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
const char *FIRCLSCrashedMarkerFileName = "previously-crashed";
|
||||
|
||||
void FIRCLSCreateCrashedMarkerFile() {
|
||||
FIRCLSFile file;
|
||||
|
||||
if (!FIRCLSFileInitWithPath(&file, _firclsContext.readonly->previouslyCrashedFileFullPath, false)) {
|
||||
FIRCLSSDKLog("Unable to create the crashed marker file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileClose(&file);
|
||||
FIRCLSSDKLog("Created the crashed marker file\n");
|
||||
}
|
19
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h
generated
Normal file
19
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h
generated
Normal file
|
@ -0,0 +1,19 @@
|
|||
// 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 <stdio.h>
|
||||
|
||||
extern const char *FIRCLSCrashedMarkerFileName;
|
||||
|
||||
void FIRCLSCreateCrashedMarkerFile(void);
|
28
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h
generated
Normal file
28
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h
generated
Normal file
|
@ -0,0 +1,28 @@
|
|||
// 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 "FIRCLSContext.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
extern FIRCLSContext _firclsContext;
|
||||
extern dispatch_queue_t _firclsLoggingQueue;
|
||||
extern dispatch_queue_t _firclsBinaryImageQueue;
|
||||
extern dispatch_queue_t _firclsExceptionQueue;
|
||||
|
||||
#define FIRCLSGetLoggingQueue() (_firclsLoggingQueue)
|
||||
#define FIRCLSGetBinaryImageQueue() (_firclsBinaryImageQueue)
|
||||
#define FIRCLSGetExceptionQueue() (_firclsExceptionQueue)
|
||||
|
||||
__END_DECLS
|
37
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h
generated
Normal file
37
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h
generated
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mach/vm_types.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include "FIRCLSFile.h"
|
||||
|
||||
typedef struct {
|
||||
const char* documentDirectoryPath;
|
||||
vm_size_t pageSize;
|
||||
} FIRCLSHostReadOnlyContext;
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
void FIRCLSHostInitialize(FIRCLSHostReadOnlyContext* roContext);
|
||||
|
||||
vm_size_t FIRCLSHostGetPageSize(void);
|
||||
|
||||
bool FIRCLSHostRecord(FIRCLSFile* file);
|
||||
|
||||
void FIRCLSHostWriteDiskUsage(FIRCLSFile* file);
|
||||
|
||||
__END_DECLS
|
161
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m
generated
Normal file
161
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m
generated
Normal file
|
@ -0,0 +1,161 @@
|
|||
// 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 "FIRCLSHost.h"
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#import "FIRCLSApplication.h"
|
||||
#include "FIRCLSDefines.h"
|
||||
#import "FIRCLSFABHost.h"
|
||||
#include "FIRCLSFile.h"
|
||||
#include "FIRCLSGlobals.h"
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
#define CLS_HOST_SYSCTL_BUFFER_SIZE (128)
|
||||
|
||||
#if CLS_CPU_ARM64
|
||||
#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 16)
|
||||
#else
|
||||
// return 4K, which is correct for all platforms except arm64, currently
|
||||
#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 4)
|
||||
#endif
|
||||
#define CLS_MIN_NATIVE_PAGE_SIZE (1024 * 4)
|
||||
|
||||
#pragma mark Prototypes
|
||||
static void FIRCLSHostWriteSysctlEntry(
|
||||
FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize);
|
||||
static void FIRCLSHostWriteModelInfo(FIRCLSFile* file);
|
||||
static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file);
|
||||
|
||||
#pragma mark - API
|
||||
void FIRCLSHostInitialize(FIRCLSHostReadOnlyContext* roContext) {
|
||||
_firclsContext.readonly->host.pageSize = FIRCLSHostGetPageSize();
|
||||
_firclsContext.readonly->host.documentDirectoryPath = NULL;
|
||||
|
||||
// determine where the document directory is mounted, so we can get file system statistics later
|
||||
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
if ([paths count]) {
|
||||
_firclsContext.readonly->host.documentDirectoryPath =
|
||||
FIRCLSDupString([[paths objectAtIndex:0] fileSystemRepresentation]);
|
||||
}
|
||||
}
|
||||
|
||||
vm_size_t FIRCLSHostGetPageSize(void) {
|
||||
size_t size;
|
||||
int pageSize;
|
||||
|
||||
// hw.pagesize is defined as HW_PAGESIZE, which is an int. It's important to match
|
||||
// these types. Turns out that sysctl will not init the data to zero, but it appears
|
||||
// that sysctlbyname does. This API is nicer, but that's important to keep in mind.
|
||||
|
||||
pageSize = 0;
|
||||
size = sizeof(pageSize);
|
||||
if (sysctlbyname("hw.pagesize", &pageSize, &size, NULL, 0) != 0) {
|
||||
FIRCLSSDKLog("sysctlbyname failed while trying to get hw.pagesize\n");
|
||||
|
||||
return CLS_MAX_NATIVE_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// if the returned size is not the expected value, abort
|
||||
if (size != sizeof(pageSize)) {
|
||||
return CLS_MAX_NATIVE_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// put in some guards to make sure our size is reasonable
|
||||
if (pageSize > CLS_MAX_NATIVE_PAGE_SIZE) {
|
||||
return CLS_MAX_NATIVE_PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (pageSize < CLS_MIN_NATIVE_PAGE_SIZE) {
|
||||
return CLS_MIN_NATIVE_PAGE_SIZE;
|
||||
}
|
||||
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
static void FIRCLSHostWriteSysctlEntry(
|
||||
FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize) {
|
||||
if (sysctlbyname(sysctlKey, buffer, &bufferSize, NULL, 0) != 0) {
|
||||
FIRCLSFileWriteHashEntryString(file, key, "(failed)");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteHashEntryString(file, key, buffer);
|
||||
}
|
||||
|
||||
static void FIRCLSHostWriteModelInfo(FIRCLSFile* file) {
|
||||
FIRCLSFileWriteHashEntryString(file, "model", [FIRCLSHostModelInfo() UTF8String]);
|
||||
|
||||
// allocate a static buffer for the sysctl values, which are typically
|
||||
// quite short
|
||||
char buffer[CLS_HOST_SYSCTL_BUFFER_SIZE];
|
||||
|
||||
#if TARGET_OS_EMBEDDED
|
||||
FIRCLSHostWriteSysctlEntry(file, "machine", "hw.model", buffer, CLS_HOST_SYSCTL_BUFFER_SIZE);
|
||||
#else
|
||||
FIRCLSHostWriteSysctlEntry(file, "machine", "hw.machine", buffer, CLS_HOST_SYSCTL_BUFFER_SIZE);
|
||||
FIRCLSHostWriteSysctlEntry(file, "cpu", "machdep.cpu.brand_string", buffer,
|
||||
CLS_HOST_SYSCTL_BUFFER_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file) {
|
||||
FIRCLSFileWriteHashEntryString(file, "os_build_version", [FIRCLSHostOSBuildVersion() UTF8String]);
|
||||
FIRCLSFileWriteHashEntryString(file, "os_display_version",
|
||||
[FIRCLSHostOSDisplayVersion() UTF8String]);
|
||||
FIRCLSFileWriteHashEntryString(file, "platform", [FIRCLSApplicationGetPlatform() UTF8String]);
|
||||
}
|
||||
|
||||
bool FIRCLSHostRecord(FIRCLSFile* file) {
|
||||
FIRCLSFileWriteSectionStart(file, "host");
|
||||
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
FIRCLSHostWriteModelInfo(file);
|
||||
FIRCLSHostWriteOSVersionInfo(file);
|
||||
FIRCLSFileWriteHashEntryString(file, "locale",
|
||||
[[[NSLocale currentLocale] localeIdentifier] UTF8String]);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FIRCLSHostWriteDiskUsage(FIRCLSFile* file) {
|
||||
struct statfs tStats;
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "storage");
|
||||
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
if (statfs(_firclsContext.readonly->host.documentDirectoryPath, &tStats) == 0) {
|
||||
FIRCLSFileWriteHashEntryUint64(file, "free", tStats.f_bavail * tStats.f_bsize);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "total", tStats.f_blocks * tStats.f_bsize);
|
||||
}
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
824
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c
generated
Normal file
824
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c
generated
Normal file
|
@ -0,0 +1,824 @@
|
|||
// 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 "FIRCLSProcess.h"
|
||||
#include "FIRCLSDefines.h"
|
||||
#include "FIRCLSFeatures.h"
|
||||
#include "FIRCLSGlobals.h"
|
||||
#include "FIRCLSProfiling.h"
|
||||
#include "FIRCLSThreadState.h"
|
||||
#include "FIRCLSUnwind.h"
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <objc/message.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#define THREAD_NAME_BUFFER_SIZE (64)
|
||||
|
||||
#pragma mark Prototypes
|
||||
static bool FIRCLSProcessGetThreadName(FIRCLSProcess *process,
|
||||
thread_t thread,
|
||||
char *buffer,
|
||||
size_t length);
|
||||
static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *process, thread_t thread);
|
||||
|
||||
#pragma mark - API
|
||||
bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid) {
|
||||
if (!process) {
|
||||
return false;
|
||||
}
|
||||
|
||||
process->task = mach_task_self();
|
||||
process->thisThread = mach_thread_self();
|
||||
process->crashedThread = crashedThread;
|
||||
process->uapVoid = uapVoid;
|
||||
|
||||
if (task_threads(process->task, &process->threads, &process->threadCount) != KERN_SUCCESS) {
|
||||
// failed to get all threads
|
||||
process->threadCount = 0;
|
||||
FIRCLSSDKLog("Error: unable to get task threads\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FIRCLSProcessDestroy(FIRCLSProcess *process) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
|
||||
bool FIRCLSProcessDebuggerAttached(void) {
|
||||
int junk;
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
|
||||
// Initialize the flags so that, if sysctl fails for some bizarre
|
||||
// reason, we get a predictable result.
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
// Initialize mib, which tells sysctl the info we want, in this case
|
||||
// we're looking for information about a specific process ID.
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
|
||||
// Call sysctl.
|
||||
size = sizeof(info);
|
||||
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
|
||||
if (junk != 0) {
|
||||
FIRCLSSDKLog("sysctl failed while trying to get kinfo_proc\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're being debugged if the P_TRACED flag is set.
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
|
||||
#pragma mark - Thread Support
|
||||
static bool FIRCLSProcessIsCurrentThread(FIRCLSProcess *process, thread_t thread) {
|
||||
return MACH_PORT_INDEX(process->thisThread) == MACH_PORT_INDEX(thread);
|
||||
}
|
||||
|
||||
static bool FIRCLSProcessIsCrashedThread(FIRCLSProcess *process, thread_t thread) {
|
||||
return MACH_PORT_INDEX(process->crashedThread) == MACH_PORT_INDEX(thread);
|
||||
}
|
||||
|
||||
static uint32_t FIRCLSProcessGetThreadCount(FIRCLSProcess *process) {
|
||||
return process->threadCount;
|
||||
}
|
||||
|
||||
static thread_t FIRCLSProcessGetThread(FIRCLSProcess *process, uint32_t index) {
|
||||
if (index >= process->threadCount) {
|
||||
return MACH_PORT_NULL;
|
||||
}
|
||||
|
||||
return process->threads[index];
|
||||
}
|
||||
|
||||
bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process) {
|
||||
mach_msg_type_number_t i;
|
||||
bool success;
|
||||
|
||||
success = true;
|
||||
for (i = 0; i < process->threadCount; ++i) {
|
||||
thread_t thread;
|
||||
|
||||
thread = FIRCLSProcessGetThread(process, i);
|
||||
|
||||
if (FIRCLSProcessIsCurrentThread(process, thread)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: workaround to get this building on watch, but we need to suspend/resume threads!
|
||||
#if CLS_CAN_SUSPEND_THREADS
|
||||
success = success && (thread_suspend(thread) == KERN_SUCCESS);
|
||||
#endif
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool FIRCLSProcessResumeAllOtherThreads(FIRCLSProcess *process) {
|
||||
mach_msg_type_number_t i;
|
||||
bool success;
|
||||
|
||||
success = true;
|
||||
for (i = 0; i < process->threadCount; ++i) {
|
||||
thread_t thread;
|
||||
|
||||
thread = FIRCLSProcessGetThread(process, i);
|
||||
|
||||
if (FIRCLSProcessIsCurrentThread(process, thread)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: workaround to get this building on watch, but we need to suspend/resume threads!
|
||||
#if CLS_CAN_SUSPEND_THREADS
|
||||
success = success && (thread_resume(thread) == KERN_SUCCESS);
|
||||
#endif
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
#pragma mark - Thread Properties
|
||||
void *FIRCLSThreadGetCurrentPC(void) {
|
||||
return __builtin_return_address(0);
|
||||
}
|
||||
|
||||
static bool FIRCLSProcessGetThreadState(FIRCLSProcess *process,
|
||||
thread_t thread,
|
||||
FIRCLSThreadContext *context) {
|
||||
if (!FIRCLSIsValidPointer(context)) {
|
||||
FIRCLSSDKLogError("invalid context supplied");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the thread context we should use is non-NULL, then just assign it here. Otherwise,
|
||||
// query the thread state
|
||||
if (FIRCLSProcessIsCrashedThread(process, thread) && FIRCLSIsValidPointer(process->uapVoid)) {
|
||||
*context = *((_STRUCT_UCONTEXT *)process->uapVoid)->uc_mcontext;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Here's a wild trick: emulate what thread_get_state would do. It apppears that
|
||||
// we cannot reliably unwind out of thread_get_state. So, instead of trying, setup
|
||||
// a thread context that resembles what the real thing would look like
|
||||
if (FIRCLSProcessIsCurrentThread(process, thread)) {
|
||||
FIRCLSSDKLog("Faking current thread\n");
|
||||
memset(context, 0, sizeof(FIRCLSThreadContext));
|
||||
|
||||
// Compute the frame address, and then base the stack value off of that. A frame pushes
|
||||
// two pointers onto the stack, so we have to offset.
|
||||
const uintptr_t frameAddress = (uintptr_t)__builtin_frame_address(0);
|
||||
const uintptr_t stackAddress = FIRCLSUnwindStackPointerFromFramePointer(frameAddress);
|
||||
|
||||
#if CLS_CPU_X86_64
|
||||
context->__ss.__rip = (uintptr_t)FIRCLSThreadGetCurrentPC();
|
||||
context->__ss.__rbp = frameAddress;
|
||||
context->__ss.__rsp = stackAddress;
|
||||
#elif CLS_CPU_I386
|
||||
context->__ss.__eip = (uintptr_t)FIRCLSThreadGetCurrentPC();
|
||||
context->__ss.__ebp = frameAddress;
|
||||
context->__ss.__esp = stackAddress;
|
||||
#elif CLS_CPU_ARM64
|
||||
FIRCLSThreadContextSetPC(context, (uintptr_t)FIRCLSThreadGetCurrentPC());
|
||||
FIRCLSThreadContextSetFramePointer(context, frameAddress);
|
||||
FIRCLSThreadContextSetLinkRegister(context, (uintptr_t)__builtin_return_address(0));
|
||||
FIRCLSThreadContextSetStackPointer(context, stackAddress);
|
||||
#elif CLS_CPU_ARM
|
||||
context->__ss.__pc = (uintptr_t)FIRCLSThreadGetCurrentPC();
|
||||
context->__ss.__r[7] = frameAddress;
|
||||
context->__ss.__lr = (uintptr_t)__builtin_return_address(0);
|
||||
context->__ss.__sp = stackAddress;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
// try to get the value by querying the thread state
|
||||
mach_msg_type_number_t stateCount = FIRCLSThreadStateCount;
|
||||
if (thread_get_state(thread, FIRCLSThreadState, (thread_state_t)(&(context->__ss)),
|
||||
&stateCount) != KERN_SUCCESS) {
|
||||
FIRCLSSDKLogError("failed to get thread state\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool FIRCLSProcessGetThreadName(FIRCLSProcess *process,
|
||||
thread_t thread,
|
||||
char *buffer,
|
||||
size_t length) {
|
||||
pthread_t pthread;
|
||||
|
||||
if (!buffer || length <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread = pthread_from_mach_thread_np(thread);
|
||||
|
||||
return pthread_getname_np(pthread, buffer, length) == 0;
|
||||
}
|
||||
|
||||
static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *process,
|
||||
thread_t thread) {
|
||||
thread_identifier_info_data_t info;
|
||||
mach_msg_type_number_t infoCount;
|
||||
dispatch_queue_t *queueAddress;
|
||||
dispatch_queue_t queue;
|
||||
const char *string;
|
||||
|
||||
infoCount = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
if (thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&info, &infoCount) !=
|
||||
KERN_SUCCESS) {
|
||||
FIRCLSSDKLog("unable to get thread info\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
queueAddress = (dispatch_queue_t *)info.dispatch_qaddr;
|
||||
if (queueAddress == NULL) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Sometimes a queue address is invalid. I cannot explain why this is, but
|
||||
// it can cause a crash.
|
||||
if (!FIRCLSReadMemory((vm_address_t)queueAddress, &queue, sizeof(void *))) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// here, we know it is safe to de-reference this address, so attempt to get the queue name
|
||||
if (!queue) {
|
||||
return "";
|
||||
}
|
||||
|
||||
string = dispatch_queue_get_label(queue);
|
||||
|
||||
// but, we still don't if the entire string is valid, so check that too
|
||||
if (!FIRCLSReadString((vm_address_t)string, (char **)&string, 128)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
#pragma mark - Data Recording
|
||||
static bool FIRCLSProcessRecordThreadRegisters(FIRCLSThreadContext context, FIRCLSFile *file) {
|
||||
#if CLS_CPU_ARM
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r0", context.__ss.__r[0]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r1", context.__ss.__r[1]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r2", context.__ss.__r[2]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r3", context.__ss.__r[3]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r4", context.__ss.__r[4]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r5", context.__ss.__r[5]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r6", context.__ss.__r[6]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r7", context.__ss.__r[7]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r8", context.__ss.__r[8]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r9", context.__ss.__r[9]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r10", context.__ss.__r[10]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r11", context.__ss.__r[11]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "ip", context.__ss.__r[12]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "sp", context.__ss.__sp);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "lr", context.__ss.__lr);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "pc", context.__ss.__pc);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "cpsr", context.__ss.__cpsr);
|
||||
#elif CLS_CPU_ARM64
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x0", context.__ss.__x[0]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x1", context.__ss.__x[1]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x2", context.__ss.__x[2]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x3", context.__ss.__x[3]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x4", context.__ss.__x[4]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x5", context.__ss.__x[5]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x6", context.__ss.__x[6]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x7", context.__ss.__x[7]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x8", context.__ss.__x[8]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x9", context.__ss.__x[9]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x10", context.__ss.__x[10]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x11", context.__ss.__x[11]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x12", context.__ss.__x[12]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x13", context.__ss.__x[13]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x14", context.__ss.__x[14]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x15", context.__ss.__x[15]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x16", context.__ss.__x[16]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x17", context.__ss.__x[17]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x18", context.__ss.__x[18]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x19", context.__ss.__x[19]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x20", context.__ss.__x[20]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x21", context.__ss.__x[21]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x22", context.__ss.__x[22]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x23", context.__ss.__x[23]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x24", context.__ss.__x[24]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x25", context.__ss.__x[25]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x26", context.__ss.__x[26]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x27", context.__ss.__x[27]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "x28", context.__ss.__x[28]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "fp", FIRCLSThreadContextGetFramePointer(&context));
|
||||
FIRCLSFileWriteHashEntryUint64(file, "sp", FIRCLSThreadContextGetStackPointer(&context));
|
||||
FIRCLSFileWriteHashEntryUint64(file, "lr", FIRCLSThreadContextGetLinkRegister(&context));
|
||||
FIRCLSFileWriteHashEntryUint64(file, "pc", FIRCLSThreadContextGetPC(&context));
|
||||
FIRCLSFileWriteHashEntryUint64(file, "cpsr", context.__ss.__cpsr);
|
||||
#elif CLS_CPU_I386
|
||||
FIRCLSFileWriteHashEntryUint64(file, "eax", context.__ss.__eax);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "ebx", context.__ss.__ebx);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "ecx", context.__ss.__ecx);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "edx", context.__ss.__edx);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "edi", context.__ss.__edi);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "esi", context.__ss.__esi);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "ebp", context.__ss.__ebp);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "esp", context.__ss.__esp);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "ss", context.__ss.__ss);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "eflags", context.__ss.__eflags);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "eip", context.__ss.__eip);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "cs", context.__ss.__cs);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "ds", context.__ss.__ds);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "es", context.__ss.__es);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "fs", context.__ss.__fs);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "gs", context.__ss.__gs);
|
||||
|
||||
// how do we get the cr2 register?
|
||||
#elif CLS_CPU_X86_64
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rax", context.__ss.__rax);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rbx", context.__ss.__rbx);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rcx", context.__ss.__rcx);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rdx", context.__ss.__rdx);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rdi", context.__ss.__rdi);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rsi", context.__ss.__rsi);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rbp", context.__ss.__rbp);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rsp", context.__ss.__rsp);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r8", context.__ss.__r8);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r9", context.__ss.__r9);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r10", context.__ss.__r10);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r11", context.__ss.__r11);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r12", context.__ss.__r12);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r13", context.__ss.__r13);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r14", context.__ss.__r14);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "r15", context.__ss.__r15);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rip", context.__ss.__rip);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "rflags", context.__ss.__rflags);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "cs", context.__ss.__cs);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "fs", context.__ss.__fs);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "gs", context.__ss.__gs);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FIRCLSProcessRecordThread(FIRCLSProcess *process, thread_t thread, FIRCLSFile *file) {
|
||||
FIRCLSUnwindContext unwindContext;
|
||||
FIRCLSThreadContext context;
|
||||
|
||||
if (!FIRCLSProcessGetThreadState(process, thread, &context)) {
|
||||
FIRCLSSDKLogError("unable to get thread state");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FIRCLSUnwindInit(&unwindContext, context)) {
|
||||
FIRCLSSDKLog("unable to init unwind context\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
// registers
|
||||
FIRCLSFileWriteHashKey(file, "registers");
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
FIRCLSProcessRecordThreadRegisters(context, file);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
// stacktrace
|
||||
FIRCLSFileWriteHashKey(file, "stacktrace");
|
||||
|
||||
// stacktrace is an array of integers
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
|
||||
uint32_t repeatedPCCount = 0;
|
||||
uint64_t repeatedPC = 0;
|
||||
const FIRCLSInternalLogLevel level = _firclsContext.writable->internalLogging.logLevel;
|
||||
|
||||
while (FIRCLSUnwindNextFrame(&unwindContext)) {
|
||||
const uintptr_t pc = FIRCLSUnwindGetPC(&unwindContext);
|
||||
const uint32_t frameCount = FIRCLSUnwindGetFrameRepeatCount(&unwindContext);
|
||||
|
||||
if (repeatedPC == pc && repeatedPC != 0) {
|
||||
// actively counting a recursion
|
||||
repeatedPCCount = frameCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frameCount >= FIRCLSUnwindInfiniteRecursionCountThreshold && repeatedPC == 0) {
|
||||
repeatedPC = pc;
|
||||
FIRCLSSDKLogWarn("Possible infinite recursion - suppressing logging\n");
|
||||
_firclsContext.writable->internalLogging.logLevel = FIRCLSInternalLogLevelWarn;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (repeatedPC != 0) {
|
||||
// at this point, we've recorded a repeated PC, but it is now no longer
|
||||
// repeating, so we can restore the logging
|
||||
_firclsContext.writable->internalLogging.logLevel = level;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEntryUint64(file, pc);
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
|
||||
// crashed?
|
||||
if (FIRCLSProcessIsCrashedThread(process, thread)) {
|
||||
FIRCLSFileWriteHashEntryBoolean(file, "crashed", true);
|
||||
}
|
||||
|
||||
if (repeatedPC != 0) {
|
||||
FIRCLSFileWriteHashEntryUint64(file, "repeated_pc", repeatedPC);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "repeat_count", repeatedPCCount);
|
||||
}
|
||||
|
||||
// Just for extra safety, restore the logging level again. The logic
|
||||
// above is fairly tricky, this is cheap, and no logging is a real pain.
|
||||
_firclsContext.writable->internalLogging.logLevel = level;
|
||||
|
||||
// end thread info
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file) {
|
||||
uint32_t threadCount;
|
||||
uint32_t i;
|
||||
|
||||
threadCount = FIRCLSProcessGetThreadCount(process);
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "threads");
|
||||
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
|
||||
for (i = 0; i < threadCount; ++i) {
|
||||
thread_t thread;
|
||||
|
||||
thread = FIRCLSProcessGetThread(process, i);
|
||||
|
||||
FIRCLSSDKLogInfo("recording thread %d data\n", i);
|
||||
if (!FIRCLSProcessRecordThread(process, thread, file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
|
||||
FIRCLSSDKLogInfo("completed recording all thread data\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FIRCLSProcessRecordThreadNames(FIRCLSProcess *process, FIRCLSFile *file) {
|
||||
uint32_t threadCount;
|
||||
uint32_t i;
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "thread_names");
|
||||
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
|
||||
threadCount = FIRCLSProcessGetThreadCount(process);
|
||||
for (i = 0; i < threadCount; ++i) {
|
||||
thread_t thread;
|
||||
char name[THREAD_NAME_BUFFER_SIZE];
|
||||
|
||||
thread = FIRCLSProcessGetThread(process, i);
|
||||
|
||||
name[0] = 0; // null-terminate, just in case nothing is written
|
||||
|
||||
FIRCLSProcessGetThreadName(process, thread, name, THREAD_NAME_BUFFER_SIZE);
|
||||
|
||||
FIRCLSFileWriteArrayEntryString(file, name);
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
||||
|
||||
void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *file) {
|
||||
uint32_t threadCount;
|
||||
uint32_t i;
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "dispatch_queue_names");
|
||||
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
|
||||
threadCount = FIRCLSProcessGetThreadCount(process);
|
||||
for (i = 0; i < threadCount; ++i) {
|
||||
thread_t thread;
|
||||
const char *name;
|
||||
|
||||
thread = FIRCLSProcessGetThread(process, i);
|
||||
|
||||
name = FIRCLSProcessGetThreadDispatchQueueName(process, thread);
|
||||
|
||||
FIRCLSFileWriteArrayEntryString(file, name);
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
||||
|
||||
#pragma mark - Othe Process Info
|
||||
bool FIRCLSProcessGetMemoryUsage(uint64_t *active,
|
||||
uint64_t *inactive,
|
||||
uint64_t *wired,
|
||||
uint64_t *freeMem) {
|
||||
mach_port_t hostPort;
|
||||
mach_msg_type_number_t hostSize;
|
||||
vm_size_t pageSize;
|
||||
vm_statistics_data_t vmStat;
|
||||
|
||||
hostPort = mach_host_self();
|
||||
|
||||
hostSize = sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
||||
|
||||
pageSize = _firclsContext.readonly->host.pageSize;
|
||||
|
||||
if (host_statistics(hostPort, HOST_VM_INFO, (host_info_t)&vmStat, &hostSize) != KERN_SUCCESS) {
|
||||
FIRCLSSDKLog("Failed to get vm statistics\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(active && inactive && wired && freeMem)) {
|
||||
FIRCLSSDKLog("Invalid pointers\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute the sizes in bytes and return the values
|
||||
*active = vmStat.active_count * pageSize;
|
||||
*inactive = vmStat.inactive_count * pageSize;
|
||||
*wired = vmStat.wire_count * pageSize;
|
||||
*freeMem = vmStat.free_count * pageSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FIRCLSProcessGetInfo(FIRCLSProcess *process,
|
||||
uint64_t *virtualSize,
|
||||
uint64_t *residentSize,
|
||||
time_value_t *userTime,
|
||||
time_value_t *systemTime) {
|
||||
struct task_basic_info_64 taskInfo;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
count = TASK_BASIC_INFO_64_COUNT;
|
||||
if (task_info(process->task, TASK_BASIC_INFO_64, (task_info_t)&taskInfo, &count) !=
|
||||
KERN_SUCCESS) {
|
||||
FIRCLSSDKLog("Failed to get task info\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(virtualSize && residentSize && userTime && systemTime)) {
|
||||
FIRCLSSDKLog("Invalid pointers\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*virtualSize = taskInfo.virtual_size;
|
||||
*residentSize = taskInfo.resident_size;
|
||||
*userTime = taskInfo.user_time;
|
||||
*systemTime = taskInfo.system_time;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FIRCLSProcessRecordStats(FIRCLSProcess *process, FIRCLSFile *file) {
|
||||
uint64_t active;
|
||||
uint64_t inactive;
|
||||
uint64_t virtualSize;
|
||||
uint64_t residentSize;
|
||||
uint64_t wired;
|
||||
uint64_t freeMem;
|
||||
time_value_t userTime;
|
||||
time_value_t systemTime;
|
||||
|
||||
if (!FIRCLSProcessGetMemoryUsage(&active, &inactive, &wired, &freeMem)) {
|
||||
FIRCLSSDKLog("Unable to get process memory usage\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FIRCLSProcessGetInfo(process, &virtualSize, &residentSize, &userTime, &systemTime)) {
|
||||
FIRCLSSDKLog("Unable to get process stats\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "process_stats");
|
||||
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
FIRCLSFileWriteHashEntryUint64(file, "active", active);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "inactive", inactive);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "wired", wired);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "freeMem", freeMem); // Intentionally left in, for now. Arg.
|
||||
FIRCLSFileWriteHashEntryUint64(file, "free_mem", freeMem);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "virtual", virtualSize);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "resident", active);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "user_time",
|
||||
(userTime.seconds * 1000 * 1000) + userTime.microseconds);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "sys_time",
|
||||
(systemTime.seconds * 1000 * 1000) + systemTime.microseconds);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
||||
|
||||
#pragma mark - Runtime Info
|
||||
#define OBJC_MSG_SEND_START ((vm_address_t)objc_msgSend)
|
||||
#define OBJC_MSG_SEND_SUPER_START ((vm_address_t)objc_msgSendSuper)
|
||||
#define OBJC_MSG_SEND_END (OBJC_MSG_SEND_START + 66)
|
||||
#define OBJC_MSG_SEND_SUPER_END (OBJC_MSG_SEND_SUPER_START + 66)
|
||||
|
||||
#if !CLS_CPU_ARM64
|
||||
#define OBJC_MSG_SEND_STRET_START ((vm_address_t)objc_msgSend_stret)
|
||||
#define OBJC_MSG_SEND_SUPER_STRET_START ((vm_address_t)objc_msgSendSuper_stret)
|
||||
#define OBJC_MSG_SEND_STRET_END (OBJC_MSG_SEND_STRET_START + 66)
|
||||
#define OBJC_MSG_SEND_SUPER_STRET_END (OBJC_MSG_SEND_SUPER_STRET_START + 66)
|
||||
#endif
|
||||
|
||||
#if CLS_CPU_X86
|
||||
#define OBJC_MSG_SEND_FPRET_START ((vm_address_t)objc_msgSend_fpret)
|
||||
#define OBJC_MSG_SEND_FPRET_END (OBJC_MSG_SEND_FPRET_START + 66)
|
||||
#endif
|
||||
|
||||
static const char *FIRCLSProcessGetObjCSelectorName(FIRCLSThreadContext registers) {
|
||||
void *selectorAddress;
|
||||
void *selRegister;
|
||||
#if !CLS_CPU_ARM64
|
||||
void *stretSelRegister;
|
||||
#endif
|
||||
vm_address_t pc;
|
||||
|
||||
// First, did we crash in objc_msgSend? The two ways I can think
|
||||
// of doing this are to use dladdr, and then comparing the strings to
|
||||
// objc_msg*, or looking up the symbols, and guessing if we are "close enough".
|
||||
|
||||
selectorAddress = NULL;
|
||||
|
||||
#if CLS_CPU_ARM
|
||||
pc = registers.__ss.__pc;
|
||||
selRegister = (void *)registers.__ss.__r[1];
|
||||
stretSelRegister = (void *)registers.__ss.__r[2];
|
||||
#elif CLS_CPU_ARM64
|
||||
pc = FIRCLSThreadContextGetPC(®isters);
|
||||
selRegister = (void *)registers.__ss.__x[1];
|
||||
#elif CLS_CPU_I386
|
||||
pc = registers.__ss.__eip;
|
||||
selRegister = (void *)registers.__ss.__ecx;
|
||||
stretSelRegister = (void *)registers.__ss.__ecx;
|
||||
#elif CLS_CPU_X86_64
|
||||
pc = registers.__ss.__rip;
|
||||
selRegister = (void *)registers.__ss.__rsi;
|
||||
stretSelRegister = (void *)registers.__ss.__rdx;
|
||||
#endif
|
||||
|
||||
if ((pc >= OBJC_MSG_SEND_START) && (pc <= OBJC_MSG_SEND_END)) {
|
||||
selectorAddress = selRegister;
|
||||
}
|
||||
|
||||
#if !CLS_CPU_ARM64
|
||||
if ((pc >= OBJC_MSG_SEND_SUPER_START) && (pc <= OBJC_MSG_SEND_SUPER_END)) {
|
||||
selectorAddress = selRegister;
|
||||
}
|
||||
|
||||
if ((pc >= OBJC_MSG_SEND_STRET_START) && (pc <= OBJC_MSG_SEND_STRET_END)) {
|
||||
selectorAddress = stretSelRegister;
|
||||
}
|
||||
|
||||
if ((pc >= OBJC_MSG_SEND_SUPER_STRET_START) && (pc <= OBJC_MSG_SEND_SUPER_STRET_END)) {
|
||||
selectorAddress = stretSelRegister;
|
||||
}
|
||||
|
||||
#if CLS_CPU_X86
|
||||
if ((pc >= OBJC_MSG_SEND_FPRET_START) && (pc <= OBJC_MSG_SEND_FPRET_END)) {
|
||||
selectorAddress = selRegister;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!selectorAddress) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!FIRCLSReadString((vm_address_t)selectorAddress, (char **)&selectorAddress, 128)) {
|
||||
FIRCLSSDKLog("Unable to read the selector string\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
return selectorAddress;
|
||||
}
|
||||
|
||||
#define CRASH_ALIGN __attribute__((aligned(8)))
|
||||
typedef struct {
|
||||
unsigned version CRASH_ALIGN;
|
||||
const char *message CRASH_ALIGN;
|
||||
const char *signature CRASH_ALIGN;
|
||||
const char *backtrace CRASH_ALIGN;
|
||||
const char *message2 CRASH_ALIGN;
|
||||
void *reserved CRASH_ALIGN;
|
||||
void *reserved2 CRASH_ALIGN;
|
||||
} crash_info_t;
|
||||
|
||||
static void FIRCLSProcessRecordCrashInfo(FIRCLSFile *file) {
|
||||
// TODO: this should be abstracted into binary images, if possible
|
||||
FIRCLSBinaryImageRuntimeNode *nodes = _firclsContext.writable->binaryImage.nodes;
|
||||
if (!nodes) {
|
||||
FIRCLSSDKLogError("The node structure is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) {
|
||||
FIRCLSBinaryImageRuntimeNode *node = &nodes[i];
|
||||
|
||||
if (!node->crashInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
crash_info_t info;
|
||||
|
||||
if (!FIRCLSReadMemory((vm_address_t)node->crashInfo, &info, sizeof(crash_info_t))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FIRCLSSDKLogDebug("Found crash info with version %d\n", info.version);
|
||||
|
||||
// Currently support versions 0 through 5.
|
||||
// 4 was in use for a long time, but it appears that with iOS 9 / swift 2.0, the verison has
|
||||
// been bumped.
|
||||
if (info.version > 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!info.message) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
|
||||
FIRCLSSDKLogInfo("Found crash info for %s\n", node->name);
|
||||
#endif
|
||||
|
||||
FIRCLSSDKLogDebug("attempting to read crash info string\n");
|
||||
|
||||
char *string = NULL;
|
||||
|
||||
if (!FIRCLSReadString((vm_address_t)info.message, &string, 256)) {
|
||||
FIRCLSSDKLogError("Failed to copy crash info string\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEntryHexEncodedString(file, string);
|
||||
}
|
||||
}
|
||||
|
||||
void FIRCLSProcessRecordRuntimeInfo(FIRCLSProcess *process, FIRCLSFile *file) {
|
||||
FIRCLSThreadContext mcontext;
|
||||
|
||||
if (!FIRCLSProcessGetThreadState(process, process->crashedThread, &mcontext)) {
|
||||
FIRCLSSDKLogError("unable to get crashed thread state");
|
||||
}
|
||||
|
||||
FIRCLSFileWriteSectionStart(file, "runtime");
|
||||
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
|
||||
FIRCLSFileWriteHashEntryString(file, "objc_selector", FIRCLSProcessGetObjCSelectorName(mcontext));
|
||||
|
||||
FIRCLSFileWriteHashKey(file, "crash_info_entries");
|
||||
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
FIRCLSProcessRecordCrashInfo(file);
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
45
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h
generated
Normal file
45
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h
generated
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "FIRCLSFile.h"
|
||||
|
||||
typedef struct {
|
||||
// task info
|
||||
mach_port_t task;
|
||||
|
||||
// thread stuff
|
||||
thread_t thisThread;
|
||||
thread_t crashedThread;
|
||||
thread_act_array_t threads;
|
||||
mach_msg_type_number_t threadCount;
|
||||
void *uapVoid; // current thread state
|
||||
} FIRCLSProcess;
|
||||
|
||||
bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid);
|
||||
bool FIRCLSProcessDestroy(FIRCLSProcess *process);
|
||||
bool FIRCLSProcessDebuggerAttached(void);
|
||||
|
||||
bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process);
|
||||
bool FIRCLSProcessResumeAllOtherThreads(FIRCLSProcess *process);
|
||||
|
||||
void FIRCLSProcessRecordThreadNames(FIRCLSProcess *process, FIRCLSFile *file);
|
||||
void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *file);
|
||||
bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file);
|
||||
void FIRCLSProcessRecordStats(FIRCLSProcess *process, FIRCLSFile *file);
|
||||
void FIRCLSProcessRecordRuntimeInfo(FIRCLSProcess *process, FIRCLSFile *file);
|
101
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h
generated
Normal file
101
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h
generated
Normal file
|
@ -0,0 +1,101 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FIRCLSFile.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#ifdef __OBJC__
|
||||
extern NSString* const FIRCLSStartTimeKey;
|
||||
extern NSString* const FIRCLSFirstRunloopTurnTimeKey;
|
||||
extern NSString* const FIRCLSInBackgroundKey;
|
||||
#if TARGET_OS_IPHONE
|
||||
extern NSString* const FIRCLSDeviceOrientationKey;
|
||||
extern NSString* const FIRCLSUIOrientationKey;
|
||||
#endif
|
||||
extern NSString* const FIRCLSUserIdentifierKey;
|
||||
extern NSString* const FIRCLSUserNameKey;
|
||||
extern NSString* const FIRCLSUserEmailKey;
|
||||
extern NSString* const FIRCLSDevelopmentPlatformNameKey;
|
||||
extern NSString* const FIRCLSDevelopmentPlatformVersionKey;
|
||||
#endif
|
||||
|
||||
extern const uint32_t FIRCLSUserLoggingMaxKVEntries;
|
||||
|
||||
typedef struct {
|
||||
const char* incrementalPath;
|
||||
const char* compactedPath;
|
||||
|
||||
uint32_t maxIncrementalCount;
|
||||
uint32_t maxCount;
|
||||
} FIRCLSUserLoggingKVStorage;
|
||||
|
||||
typedef struct {
|
||||
const char* aPath;
|
||||
const char* bPath;
|
||||
uint32_t maxSize;
|
||||
uint32_t maxEntries;
|
||||
bool restrictBySize;
|
||||
uint32_t* entryCount;
|
||||
} FIRCLSUserLoggingABStorage;
|
||||
|
||||
typedef struct {
|
||||
FIRCLSUserLoggingKVStorage userKVStorage;
|
||||
FIRCLSUserLoggingKVStorage internalKVStorage;
|
||||
|
||||
FIRCLSUserLoggingABStorage logStorage;
|
||||
FIRCLSUserLoggingABStorage errorStorage;
|
||||
FIRCLSUserLoggingABStorage customExceptionStorage;
|
||||
} FIRCLSUserLoggingReadOnlyContext;
|
||||
|
||||
typedef struct {
|
||||
const char* activeUserLogPath;
|
||||
const char* activeErrorLogPath;
|
||||
const char* activeCustomExceptionPath;
|
||||
uint32_t userKVCount;
|
||||
uint32_t internalKVCount;
|
||||
uint32_t errorsCount;
|
||||
} FIRCLSUserLoggingWritableContext;
|
||||
|
||||
void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext* roContext,
|
||||
FIRCLSUserLoggingWritableContext* rwContext);
|
||||
|
||||
#ifdef __OBJC__
|
||||
void FIRCLSUserLoggingRecordUserKeyValue(NSString* key, id value);
|
||||
void FIRCLSUserLoggingRecordInternalKeyValue(NSString* key, id value);
|
||||
void FIRCLSUserLoggingWriteInternalKeyValue(NSString* key, NSString* value);
|
||||
|
||||
void FIRCLSUserLoggingRecordError(NSError* error, NSDictionary<NSString*, id>* additionalUserInfo);
|
||||
|
||||
NSDictionary* FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage* storage,
|
||||
bool decodeHex);
|
||||
void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage* storage);
|
||||
|
||||
void FIRCLSUserLoggingRecordKeyValue(NSString* key,
|
||||
id value,
|
||||
FIRCLSUserLoggingKVStorage* storage,
|
||||
uint32_t* counter);
|
||||
|
||||
void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage* storage,
|
||||
const char** activePath,
|
||||
void (^openedFileBlock)(FIRCLSFile* file));
|
||||
|
||||
NSArray* FIRCLSUserLoggingStoredKeyValues(const char* path);
|
||||
|
||||
OBJC_EXTERN void FIRCLSLog(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
523
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m
generated
Normal file
523
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m
generated
Normal file
|
@ -0,0 +1,523 @@
|
|||
// 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 "FIRCLSUserLogging.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "FIRCLSGlobals.h"
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
#import "FIRCLSReportManager_Private.h"
|
||||
|
||||
NSString *const FIRCLSStartTimeKey = @"com.crashlytics.kit-start-time";
|
||||
NSString *const FIRCLSFirstRunloopTurnTimeKey = @"com.crashlytics.first-run-loop-time";
|
||||
NSString *const FIRCLSInBackgroundKey = @"com.crashlytics.in-background";
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString *const FIRCLSDeviceOrientationKey = @"com.crashlytics.device-orientation";
|
||||
NSString *const FIRCLSUIOrientationKey = @"com.crashlytics.ui-orientation";
|
||||
#endif
|
||||
NSString *const FIRCLSUserIdentifierKey = @"com.crashlytics.user-id";
|
||||
NSString *const FIRCLSDevelopmentPlatformNameKey = @"com.crashlytics.development-platform-name";
|
||||
NSString *const FIRCLSDevelopmentPlatformVersionKey =
|
||||
@"com.crashlytics.development-platform-version";
|
||||
|
||||
const uint32_t FIRCLSUserLoggingMaxKVEntries = 64;
|
||||
|
||||
#pragma mark - Prototypes
|
||||
static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
|
||||
NSString *value,
|
||||
FIRCLSUserLoggingKVStorage *storage,
|
||||
uint32_t *counter);
|
||||
static void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage,
|
||||
const char **activePath,
|
||||
off_t fileSize);
|
||||
void FIRCLSLogInternal(NSString *message);
|
||||
|
||||
#pragma mark - Setup
|
||||
void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext *roContext,
|
||||
FIRCLSUserLoggingWritableContext *rwContext) {
|
||||
rwContext->activeUserLogPath = roContext->logStorage.aPath;
|
||||
rwContext->activeErrorLogPath = roContext->errorStorage.aPath;
|
||||
rwContext->activeCustomExceptionPath = roContext->customExceptionStorage.aPath;
|
||||
|
||||
rwContext->userKVCount = 0;
|
||||
rwContext->internalKVCount = 0;
|
||||
rwContext->errorsCount = 0;
|
||||
|
||||
roContext->userKVStorage.maxIncrementalCount = FIRCLSUserLoggingMaxKVEntries;
|
||||
roContext->internalKVStorage.maxIncrementalCount = roContext->userKVStorage.maxIncrementalCount;
|
||||
}
|
||||
|
||||
#pragma mark - KV Logging
|
||||
void FIRCLSUserLoggingRecordInternalKeyValue(NSString *key, id value) {
|
||||
FIRCLSUserLoggingRecordKeyValue(key, value, &_firclsContext.readonly->logging.internalKVStorage,
|
||||
&_firclsContext.writable->logging.internalKVCount);
|
||||
}
|
||||
|
||||
void FIRCLSUserLoggingWriteInternalKeyValue(NSString *key, NSString *value) {
|
||||
// Unsynchronized - must be run on the correct queue
|
||||
FIRCLSUserLoggingWriteKeyValue(key, value, &_firclsContext.readonly->logging.internalKVStorage,
|
||||
&_firclsContext.writable->logging.internalKVCount);
|
||||
}
|
||||
|
||||
void FIRCLSUserLoggingRecordUserKeyValue(NSString *key, id value) {
|
||||
FIRCLSUserLoggingRecordKeyValue(key, value, &_firclsContext.readonly->logging.userKVStorage,
|
||||
&_firclsContext.writable->logging.userKVCount);
|
||||
}
|
||||
|
||||
static id FIRCLSUserLoggingGetComponent(NSDictionary *entry,
|
||||
NSString *componentName,
|
||||
bool decodeHex) {
|
||||
id value = [entry objectForKey:componentName];
|
||||
|
||||
return (decodeHex && value != [NSNull null]) ? FIRCLSFileHexDecodeString([value UTF8String])
|
||||
: value;
|
||||
}
|
||||
|
||||
static NSString *FIRCLSUserLoggingGetKey(NSDictionary *entry, bool decodeHex) {
|
||||
return FIRCLSUserLoggingGetComponent(entry, @"key", decodeHex);
|
||||
}
|
||||
|
||||
static id FIRCLSUserLoggingGetValue(NSDictionary *entry, bool decodeHex) {
|
||||
return FIRCLSUserLoggingGetComponent(entry, @"value", decodeHex);
|
||||
}
|
||||
|
||||
NSDictionary *FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage *storage,
|
||||
bool decodeHex) {
|
||||
if (!FIRCLSIsValidPointer(storage)) {
|
||||
FIRCLSSDKLogError("storage invalid\n");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray *incrementalKVs = FIRCLSUserLoggingStoredKeyValues(storage->incrementalPath);
|
||||
NSArray *compactedKVs = FIRCLSUserLoggingStoredKeyValues(storage->compactedPath);
|
||||
|
||||
NSMutableDictionary *finalKVSet = [NSMutableDictionary new];
|
||||
|
||||
// These should all be unique, so there might be a more efficient way to
|
||||
// do this
|
||||
for (NSDictionary *entry in compactedKVs) {
|
||||
NSString *key = FIRCLSUserLoggingGetKey(entry, decodeHex);
|
||||
NSString *value = FIRCLSUserLoggingGetValue(entry, decodeHex);
|
||||
|
||||
if (!key || !value) {
|
||||
FIRCLSSDKLogError("compacted key/value contains a nil and must be dropped\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
[finalKVSet setObject:value forKey:key];
|
||||
}
|
||||
|
||||
// Now, assign the incremental values, in file order, so we overwrite any older values.
|
||||
for (NSDictionary *entry in incrementalKVs) {
|
||||
NSString *key = FIRCLSUserLoggingGetKey(entry, decodeHex);
|
||||
NSString *value = FIRCLSUserLoggingGetValue(entry, decodeHex);
|
||||
|
||||
if (!key || !value) {
|
||||
FIRCLSSDKLogError("incremental key/value contains a nil and must be dropped\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([value isEqual:[NSNull null]]) {
|
||||
[finalKVSet removeObjectForKey:key];
|
||||
} else {
|
||||
[finalKVSet setObject:value forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
return finalKVSet;
|
||||
}
|
||||
|
||||
void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage *storage) {
|
||||
if (!FIRCLSIsValidPointer(storage)) {
|
||||
FIRCLSSDKLogError("Error: storage invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *finalKVs = FIRCLSUserLoggingGetCompactedKVEntries(storage, false);
|
||||
|
||||
if (unlink(storage->compactedPath) != 0) {
|
||||
FIRCLSSDKLog("Error: Unable to remove compacted KV store before compaction %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
FIRCLSFile file;
|
||||
|
||||
if (!FIRCLSFileInitWithPath(&file, storage->compactedPath, true)) {
|
||||
FIRCLSSDKLog("Error: Unable to open compacted k-v file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t maxCount = storage->maxCount;
|
||||
if ([finalKVs count] > maxCount) {
|
||||
// We need to remove keys, to avoid going over the max.
|
||||
// This is just about the worst way to go about doing this. There are lots of smarter ways,
|
||||
// but it's very uncommon to go down this path.
|
||||
NSArray *keys = [finalKVs allKeys];
|
||||
|
||||
FIRCLSSDKLogInfo("Truncating KV set, which is above max %d\n", maxCount);
|
||||
|
||||
finalKVs =
|
||||
[finalKVs dictionaryWithValuesForKeys:[keys subarrayWithRange:NSMakeRange(0, maxCount)]];
|
||||
}
|
||||
|
||||
for (NSString *key in finalKVs) {
|
||||
NSString *value = [finalKVs objectForKey:key];
|
||||
|
||||
FIRCLSFileWriteSectionStart(&file, "kv");
|
||||
FIRCLSFileWriteHashStart(&file);
|
||||
// tricky - the values stored incrementally have already been hex-encoded
|
||||
FIRCLSFileWriteHashEntryString(&file, "key", [key UTF8String]);
|
||||
FIRCLSFileWriteHashEntryString(&file, "value", [value UTF8String]);
|
||||
FIRCLSFileWriteHashEnd(&file);
|
||||
FIRCLSFileWriteSectionEnd(&file);
|
||||
}
|
||||
|
||||
FIRCLSFileClose(&file);
|
||||
|
||||
if (unlink(storage->incrementalPath) != 0) {
|
||||
FIRCLSSDKLog("Error: Unable to remove incremental KV store after compaction %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
void FIRCLSUserLoggingRecordKeyValue(NSString *key,
|
||||
id value,
|
||||
FIRCLSUserLoggingKVStorage *storage,
|
||||
uint32_t *counter) {
|
||||
if (!FIRCLSIsValidPointer(key)) {
|
||||
FIRCLSSDKLogWarn("User provided bad key\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure that any invalid pointer is actually set to nil
|
||||
if (!FIRCLSIsValidPointer(value) && value != nil) {
|
||||
FIRCLSSDKLogWarn("Bad value pointer being clamped to nil\n");
|
||||
value = nil;
|
||||
}
|
||||
|
||||
if (!FIRCLSContextIsInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([value respondsToSelector:@selector(description)]) {
|
||||
value = [value description];
|
||||
} else {
|
||||
// passing nil will result in a JSON null being written, which is deserialized as [NSNull null],
|
||||
// signaling to remove the key during compaction
|
||||
value = nil;
|
||||
}
|
||||
|
||||
dispatch_sync(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSUserLoggingWriteKeyValue(key, value, storage, counter);
|
||||
});
|
||||
}
|
||||
|
||||
static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
|
||||
NSString *value,
|
||||
FIRCLSUserLoggingKVStorage *storage,
|
||||
uint32_t *counter) {
|
||||
FIRCLSFile file;
|
||||
|
||||
if (!FIRCLSIsValidPointer(storage) || !FIRCLSIsValidPointer(counter)) {
|
||||
FIRCLSSDKLogError("Bad parameters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FIRCLSFileInitWithPath(&file, storage->incrementalPath, true)) {
|
||||
FIRCLSSDKLogError("Unable to open k-v file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteSectionStart(&file, "kv");
|
||||
FIRCLSFileWriteHashStart(&file);
|
||||
FIRCLSFileWriteHashEntryHexEncodedString(&file, "key", [key UTF8String]);
|
||||
FIRCLSFileWriteHashEntryHexEncodedString(&file, "value", [value UTF8String]);
|
||||
FIRCLSFileWriteHashEnd(&file);
|
||||
FIRCLSFileWriteSectionEnd(&file);
|
||||
|
||||
FIRCLSFileClose(&file);
|
||||
|
||||
*counter += 1;
|
||||
if (*counter >= storage->maxIncrementalCount) {
|
||||
dispatch_async(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSUserLoggingCompactKVEntries(storage);
|
||||
*counter = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *FIRCLSUserLoggingStoredKeyValues(const char *path) {
|
||||
if (!FIRCLSContextIsInitialized()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return FIRCLSFileReadSections(path, true, ^NSObject *(id obj) {
|
||||
return [obj objectForKey:@"kv"];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - NSError Logging
|
||||
static void FIRCLSUserLoggingRecordErrorUserInfo(FIRCLSFile *file,
|
||||
const char *fileKey,
|
||||
NSDictionary<NSString *, id> *userInfo) {
|
||||
if ([userInfo count] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteHashKey(file, fileKey);
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
|
||||
for (id key in userInfo) {
|
||||
id value = [userInfo objectForKey:key];
|
||||
if (![value respondsToSelector:@selector(description)]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
FIRCLSFileWriteArrayEntryHexEncodedString(file, [key UTF8String]);
|
||||
FIRCLSFileWriteArrayEntryHexEncodedString(file, [[value description] UTF8String]);
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
}
|
||||
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
}
|
||||
|
||||
static void FIRCLSUserLoggingWriteError(FIRCLSFile *file,
|
||||
NSError *error,
|
||||
NSDictionary<NSString *, id> *additionalUserInfo,
|
||||
NSArray *addresses,
|
||||
uint64_t timestamp) {
|
||||
FIRCLSFileWriteSectionStart(file, "error");
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
FIRCLSFileWriteHashEntryHexEncodedString(file, "domain", [[error domain] UTF8String]);
|
||||
FIRCLSFileWriteHashEntryInt64(file, "code", [error code]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "time", timestamp);
|
||||
|
||||
// addresses
|
||||
FIRCLSFileWriteHashKey(file, "stacktrace");
|
||||
FIRCLSFileWriteArrayStart(file);
|
||||
for (NSNumber *address in addresses) {
|
||||
FIRCLSFileWriteArrayEntryUint64(file, [address unsignedLongLongValue]);
|
||||
}
|
||||
FIRCLSFileWriteArrayEnd(file);
|
||||
|
||||
// user-info
|
||||
FIRCLSUserLoggingRecordErrorUserInfo(file, "info", [error userInfo]);
|
||||
FIRCLSUserLoggingRecordErrorUserInfo(file, "extra_info", additionalUserInfo);
|
||||
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
||||
|
||||
void FIRCLSUserLoggingRecordError(NSError *error,
|
||||
NSDictionary<NSString *, id> *additionalUserInfo) {
|
||||
if (!error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FIRCLSContextIsInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// record the stacktrace and timestamp here, so we
|
||||
// are as close as possible to the user's log statement
|
||||
NSArray *addresses = [NSThread callStackReturnAddresses];
|
||||
uint64_t timestamp = time(NULL);
|
||||
|
||||
FIRCLSUserLoggingWriteAndCheckABFiles(
|
||||
&_firclsContext.readonly->logging.errorStorage,
|
||||
&_firclsContext.writable->logging.activeErrorLogPath, ^(FIRCLSFile *file) {
|
||||
FIRCLSUserLoggingWriteError(file, error, additionalUserInfo, addresses, timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - CLSLog Support
|
||||
void FIRCLSLog(NSString *format, ...) {
|
||||
// If the format is nil do nothing just like NSLog.
|
||||
if (!format) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
FIRCLSLogInternal(msg);
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
uint32_t FIRCLSUserLoggingMaxLogSize(void) {
|
||||
// don't forget that the message encoding overhead is 2x, and we
|
||||
// wrap everything in a json structure with time. So, there is
|
||||
// quite a penalty
|
||||
|
||||
uint32_t size = 1024 * 64;
|
||||
|
||||
return size * 2;
|
||||
}
|
||||
|
||||
uint32_t FIRCLSUserLoggingMaxErrorSize(void) {
|
||||
return FIRCLSUserLoggingMaxLogSize();
|
||||
}
|
||||
|
||||
#pragma mark - AB Logging
|
||||
void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage,
|
||||
const char **activePath,
|
||||
off_t fileSize) {
|
||||
if (!activePath || !storage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*activePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (storage->restrictBySize) {
|
||||
if (fileSize <= storage->maxSize) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!FIRCLSIsValidPointer(storage->entryCount)) {
|
||||
FIRCLSSDKLogError("Error: storage has invalid pointer, but is restricted by entry count\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*storage->entryCount < storage->maxEntries) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we have rolled over, so we have to reset our counter.
|
||||
*storage->entryCount = 0;
|
||||
}
|
||||
|
||||
// if it is too big:
|
||||
// - reset the other log
|
||||
// - make it active
|
||||
const char *otherPath = NULL;
|
||||
|
||||
if (*activePath == storage->aPath) {
|
||||
otherPath = storage->bPath;
|
||||
} else {
|
||||
// take this path if the pointer is invalid as well, to reset
|
||||
otherPath = storage->aPath;
|
||||
}
|
||||
|
||||
// guard here against path being nil or empty
|
||||
NSString *pathString = [NSString stringWithUTF8String:otherPath];
|
||||
|
||||
if ([pathString length] > 0) {
|
||||
// ignore the error, because there is nothing we can do to recover here, and its likely
|
||||
// any failures would be intermittent
|
||||
|
||||
[[NSFileManager defaultManager] removeItemAtPath:pathString error:nil];
|
||||
}
|
||||
|
||||
*activePath = otherPath;
|
||||
}
|
||||
|
||||
void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage *storage,
|
||||
const char **activePath,
|
||||
void (^openedFileBlock)(FIRCLSFile *file)) {
|
||||
if (!storage || !activePath || !openedFileBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*activePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (storage->restrictBySize) {
|
||||
if (storage->maxSize == 0) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (storage->maxEntries == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_sync(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSFile file;
|
||||
|
||||
if (!FIRCLSFileInitWithPath(&file, *activePath, true)) {
|
||||
FIRCLSSDKLog("Unable to open log file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
openedFileBlock(&file);
|
||||
|
||||
off_t fileSize = 0;
|
||||
FIRCLSFileCloseWithOffset(&file, &fileSize);
|
||||
|
||||
// increment the count before calling FIRCLSUserLoggingCheckAndSwapABFiles, so the value
|
||||
// reflects the actual amount of stuff written
|
||||
if (!storage->restrictBySize && FIRCLSIsValidPointer(storage->entryCount)) {
|
||||
*storage->entryCount += 1;
|
||||
}
|
||||
|
||||
dispatch_async(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSUserLoggingCheckAndSwapABFiles(storage, activePath, fileSize);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void FIRCLSLogInternalWrite(FIRCLSFile *file, NSString *message, uint64_t time) {
|
||||
FIRCLSFileWriteSectionStart(file, "log");
|
||||
FIRCLSFileWriteHashStart(file);
|
||||
FIRCLSFileWriteHashEntryHexEncodedString(file, "msg", [message UTF8String]);
|
||||
FIRCLSFileWriteHashEntryUint64(file, "time", time);
|
||||
FIRCLSFileWriteHashEnd(file);
|
||||
FIRCLSFileWriteSectionEnd(file);
|
||||
}
|
||||
|
||||
void FIRCLSLogInternal(NSString *message) {
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FIRCLSContextIsInitialized()) {
|
||||
FIRCLSWarningLog(@"WARNING: FIRCLSLog has been used before (or concurrently with) "
|
||||
@"Crashlytics initialization and cannot be recorded. The message was: \n%@",
|
||||
message);
|
||||
return;
|
||||
}
|
||||
struct timeval te;
|
||||
|
||||
NSUInteger messageLength = [message length];
|
||||
int maxLogSize = _firclsContext.readonly->logging.logStorage.maxSize;
|
||||
|
||||
if (messageLength > maxLogSize) {
|
||||
FIRCLSWarningLog(
|
||||
@"WARNING: Attempted to write %zd bytes, but %d is the maximum size of the log. "
|
||||
@"Truncating to %d bytes.\n",
|
||||
messageLength, maxLogSize, maxLogSize);
|
||||
message = [message substringToIndex:maxLogSize];
|
||||
}
|
||||
|
||||
// unable to get time - abort
|
||||
if (gettimeofday(&te, NULL) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t time = te.tv_sec * 1000LL + te.tv_usec / 1000;
|
||||
|
||||
FIRCLSUserLoggingWriteAndCheckABFiles(&_firclsContext.readonly->logging.logStorage,
|
||||
&_firclsContext.writable->logging.activeUserLogPath,
|
||||
^(FIRCLSFile *file) {
|
||||
FIRCLSLogInternalWrite(file, message, time);
|
||||
});
|
||||
}
|
61
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h
generated
Normal file
61
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h
generated
Normal file
|
@ -0,0 +1,61 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, FIRCLSNetworkClientErrorType) {
|
||||
FIRCLSNetworkClientErrorTypeUnknown = -1,
|
||||
FIRCLSNetworkClientErrorTypeFileUnreadable = -2
|
||||
};
|
||||
|
||||
extern NSString *const FIRCLSNetworkClientErrorDomain;
|
||||
|
||||
@protocol FIRCLSNetworkClientDelegate;
|
||||
@class FIRCLSDataCollectionToken;
|
||||
@class FIRCLSFileManager;
|
||||
|
||||
@interface FIRCLSNetworkClient : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)initWithQueue:(NSOperationQueue *)queue
|
||||
fileManager:(FIRCLSFileManager *)fileManager
|
||||
delegate:(id<FIRCLSNetworkClientDelegate>)delegate;
|
||||
|
||||
@property(nonatomic, weak) id<FIRCLSNetworkClientDelegate> delegate;
|
||||
|
||||
@property(nonatomic, readonly) NSOperationQueue *operationQueue;
|
||||
@property(nonatomic, readonly) BOOL supportsBackgroundRequests;
|
||||
|
||||
- (void)startUploadRequest:(NSURLRequest *)request
|
||||
filePath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
immediately:(BOOL)immediate;
|
||||
|
||||
- (void)attemptToReconnectBackgroundSessionWithCompletionBlock:(void (^)(void))completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
@class FIRCLSNetworkClient;
|
||||
|
||||
@protocol FIRCLSNetworkClientDelegate <NSObject>
|
||||
@required
|
||||
- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client;
|
||||
|
||||
@optional
|
||||
- (void)networkClient:(FIRCLSNetworkClient *)client
|
||||
didFinishUploadWithPath:(NSString *)path
|
||||
error:(NSError *)error;
|
||||
|
||||
@end
|
366
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m
generated
Normal file
366
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m
generated
Normal file
|
@ -0,0 +1,366 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSNetworkClient.h"
|
||||
|
||||
#import "FIRCLSApplication.h"
|
||||
#import "FIRCLSByteUtility.h"
|
||||
#import "FIRCLSDataCollectionToken.h"
|
||||
#import "FIRCLSDefines.h"
|
||||
#import "FIRCLSFileManager.h"
|
||||
#import "FIRCLSNetworkResponseHandler.h"
|
||||
#import "FIRCLSURLSession.h"
|
||||
#import "FIRCLSURLSessionConfiguration.h"
|
||||
|
||||
#import "FIRCLSUtility.h"
|
||||
|
||||
NSString *const FIRCLSNetworkClientErrorDomain = @"FIRCLSNetworkError";
|
||||
|
||||
NSString *const FIRCLSNetworkClientBackgroundIdentifierSuffix = @".crash.background-session";
|
||||
|
||||
@interface FIRCLSNetworkClient () <NSURLSessionDelegate> {
|
||||
NSURLSession *_session;
|
||||
}
|
||||
|
||||
@property(nonatomic, strong) void (^backgroundCompletionHandler)(void);
|
||||
@property(nonatomic, strong, readonly) NSURLSession *session;
|
||||
@property(nonatomic, assign) BOOL canUseBackgroundSession;
|
||||
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRCLSNetworkClient
|
||||
|
||||
- (instancetype)initWithQueue:(NSOperationQueue *)queue
|
||||
fileManager:(FIRCLSFileManager *)fileManager
|
||||
delegate:(id<FIRCLSNetworkClientDelegate>)delegate {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_operationQueue = queue;
|
||||
_delegate = delegate;
|
||||
_fileManager = fileManager;
|
||||
|
||||
self.canUseBackgroundSession = [_delegate networkClientCanUseBackgroundSessions:self];
|
||||
|
||||
if (!self.supportsBackgroundRequests) {
|
||||
FIRCLSDeveloperLog(
|
||||
"Crashlytics:Crash:Client",
|
||||
@"Background session uploading not supported, asynchronous uploading will be used");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Background Support
|
||||
- (NSURLSession *)session {
|
||||
// Creating a NSURLSession takes some time. Doing it lazily saves us time in the normal case.
|
||||
if (_session) {
|
||||
return _session;
|
||||
}
|
||||
|
||||
NSURLSessionConfiguration *config = nil;
|
||||
|
||||
Class urlSessionClass;
|
||||
Class urlSessionConfigurationClass;
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
urlSessionClass = [FIRCLSURLSession class];
|
||||
urlSessionConfigurationClass = [FIRCLSURLSessionConfiguration class];
|
||||
#else
|
||||
urlSessionClass = [NSURLSession class];
|
||||
urlSessionConfigurationClass = [NSURLSessionConfiguration class];
|
||||
#endif
|
||||
|
||||
if (self.supportsBackgroundRequests) {
|
||||
NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID();
|
||||
NSString *backgroundConfigName =
|
||||
[sdkBundleID stringByAppendingString:FIRCLSNetworkClientBackgroundIdentifierSuffix];
|
||||
|
||||
config = [urlSessionConfigurationClass backgroundSessionConfiguration:backgroundConfigName];
|
||||
#if TARGET_OS_IPHONE
|
||||
[config setSessionSendsLaunchEvents:NO];
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
// take this code path if we don't support background requests OR if we failed to create a
|
||||
// background configuration
|
||||
config = [urlSessionConfigurationClass defaultSessionConfiguration];
|
||||
}
|
||||
|
||||
_session = [urlSessionClass sessionWithConfiguration:config
|
||||
delegate:self
|
||||
delegateQueue:self.operationQueue];
|
||||
|
||||
if (!_session || !config) {
|
||||
FIRCLSErrorLog(@"Failed to initialize");
|
||||
}
|
||||
|
||||
return _session;
|
||||
}
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
- (BOOL)NSURLSessionAvailable {
|
||||
if ([[FIRCLSURLSession class] respondsToSelector:@selector(NSURLSessionShouldBeUsed)]) {
|
||||
return [FIRCLSURLSession NSURLSessionShouldBeUsed];
|
||||
}
|
||||
|
||||
return NSClassFromString(@"NSURLSession") != nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (BOOL)supportsBackgroundRequests {
|
||||
return !FIRCLSApplicationIsExtension()
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
&& [self NSURLSessionAvailable]
|
||||
#endif
|
||||
&& self.canUseBackgroundSession;
|
||||
}
|
||||
|
||||
- (void)attemptToReconnectBackgroundSessionWithCompletionBlock:(void (^)(void))completionBlock {
|
||||
if (!self.supportsBackgroundRequests) {
|
||||
if (completionBlock) {
|
||||
completionBlock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the absolute minimum necessary. Perhaps we can do better?
|
||||
if (completionBlock) {
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:completionBlock];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - API
|
||||
- (void)startUploadRequest:(NSURLRequest *)request
|
||||
filePath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
immediately:(BOOL)immediate {
|
||||
if (![dataCollectionToken isValid]) {
|
||||
FIRCLSErrorLog(@"An upload was requested with an invalid data collection token.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
[self startImmediateUploadRequest:request filePath:path];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *description = [self relativeTaskPathForAbsolutePath:path];
|
||||
[self checkForExistingTaskMatchingDescription:description
|
||||
completionBlock:^(BOOL found) {
|
||||
if (found) {
|
||||
FIRCLSDeveloperLog(
|
||||
"Crashlytics:Crash:Client",
|
||||
@"A task currently exists for this upload, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
[self startNewUploadRequest:request filePath:path];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Support API
|
||||
- (void)startImmediateUploadRequest:(NSURLRequest *)request filePath:(NSString *)path {
|
||||
// check the ivar directly, to avoid going back to the delegate
|
||||
if (self.supportsBackgroundRequests) {
|
||||
// this can be done here, because the request will be started synchronously.
|
||||
[self startNewUploadRequest:request filePath:path];
|
||||
return;
|
||||
}
|
||||
|
||||
if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) {
|
||||
FIRCLSSDKLog("Error: file unreadable\n");
|
||||
// Following the same logic as below, do not try to inform the delegate
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
|
||||
[mutableRequest setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:path]];
|
||||
|
||||
NSURLResponse *requestResponse = nil;
|
||||
|
||||
[[NSURLSession sharedSession]
|
||||
dataTaskWithRequest:mutableRequest
|
||||
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
|
||||
NSError *_Nullable error) {
|
||||
[FIRCLSNetworkResponseHandler
|
||||
clientResponseType:requestResponse
|
||||
handler:^(FIRCLSNetworkClientResponseType type, NSInteger statusCode) {
|
||||
if (type != FIRCLSNetworkClientResponseSuccess) {
|
||||
// don't even inform the delegate of a failure here, because we don't
|
||||
// want any action to be taken synchronously
|
||||
return;
|
||||
}
|
||||
|
||||
[[self delegate] networkClient:self
|
||||
didFinishUploadWithPath:path
|
||||
error:error];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startNewUploadRequest:(NSURLRequest *)request filePath:(NSString *)path {
|
||||
if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) {
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
[self
|
||||
handleTaskDescription:path
|
||||
completedWithError:[NSError errorWithDomain:FIRCLSNetworkClientErrorDomain
|
||||
code:FIRCLSNetworkClientErrorTypeFileUnreadable
|
||||
userInfo:@{@"path" : path}]];
|
||||
}];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NSURLSessionUploadTask *task = [self.session uploadTaskWithRequest:request
|
||||
fromFile:[NSURL fileURLWithPath:path]];
|
||||
|
||||
// set the description, so we can determine what file was successfully uploaded later on
|
||||
[task setTaskDescription:[self relativeTaskPathForAbsolutePath:path]];
|
||||
|
||||
[task resume];
|
||||
}
|
||||
|
||||
- (NSString *)rootPath {
|
||||
return self.fileManager.rootPath;
|
||||
}
|
||||
|
||||
- (NSString *)absolutePathForRelativeTaskPath:(NSString *)path {
|
||||
return [self.rootPath stringByAppendingPathComponent:path];
|
||||
}
|
||||
|
||||
- (NSString *)relativeTaskPathForAbsolutePath:(NSString *)path {
|
||||
// make sure this has a tailing slash, so the path looks relative
|
||||
NSString *root = [self.rootPath stringByAppendingString:@"/"];
|
||||
|
||||
if (![path hasPrefix:root]) {
|
||||
FIRCLSSDKLog("Error: path '%s' is not at the root '%s'", [path UTF8String], [root UTF8String]);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [path stringByReplacingOccurrencesOfString:root withString:@""];
|
||||
}
|
||||
|
||||
#pragma mark - Task Management
|
||||
- (BOOL)taskArray:(NSArray *)array hasTaskMatchingDescription:(NSString *)description {
|
||||
NSUInteger idx = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger arrayIdx, BOOL *stop) {
|
||||
return [[obj taskDescription] isEqualToString:description];
|
||||
}];
|
||||
|
||||
return idx != NSNotFound;
|
||||
}
|
||||
|
||||
- (void)checkSession:(NSURLSession *)session
|
||||
forTasksMatchingDescription:(NSString *)description
|
||||
completionBlock:(void (^)(BOOL found))block {
|
||||
if (!session) {
|
||||
block(NO);
|
||||
return;
|
||||
}
|
||||
|
||||
[session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks,
|
||||
NSArray *downloadTasks) {
|
||||
if ([self taskArray:uploadTasks hasTaskMatchingDescription:description]) {
|
||||
block(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self taskArray:dataTasks hasTaskMatchingDescription:description]) {
|
||||
block(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self taskArray:downloadTasks hasTaskMatchingDescription:description]) {
|
||||
block(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
block(NO);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)checkForExistingTaskMatchingDescription:(NSString *)description
|
||||
completionBlock:(void (^)(BOOL found))block {
|
||||
// Do not instantiate the normal session, because if it doesn't exist yet, it cannot possibly have
|
||||
// existing tasks
|
||||
[_operationQueue addOperationWithBlock:^{
|
||||
[self checkSession:self.session
|
||||
forTasksMatchingDescription:description
|
||||
completionBlock:^(BOOL found) {
|
||||
block(found);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Result Handling
|
||||
// This method is duplicated from FIRCLSFABNetworkClient. Sharing it is a little weird - I didn't
|
||||
// feel like it fit into FIRCLSNetworkResponseHandler.
|
||||
- (void)runAfterRetryValueFromResponse:(NSURLResponse *)response block:(void (^)(void))block {
|
||||
NSTimeInterval delay = [FIRCLSNetworkResponseHandler retryValueForResponse:response];
|
||||
|
||||
// FIRCLSDeveloperLog("Network", @"Restarting request after %f", delay);
|
||||
|
||||
FIRCLSAddOperationAfter(delay, _operationQueue, block);
|
||||
}
|
||||
|
||||
- (void)restartTask:(NSURLSessionTask *)task {
|
||||
NSURLRequest *request = [task originalRequest];
|
||||
|
||||
[self runAfterRetryValueFromResponse:[task response]
|
||||
block:^{
|
||||
NSString *path = [self
|
||||
absolutePathForRelativeTaskPath:[task taskDescription]];
|
||||
|
||||
[self startNewUploadRequest:request filePath:path];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleTask:(NSURLSessionTask *)task completedWithError:(NSError *)error {
|
||||
[self handleTaskDescription:[task taskDescription] completedWithError:error];
|
||||
}
|
||||
|
||||
- (void)handleTaskDescription:(NSString *)taskDescription completedWithError:(NSError *)error {
|
||||
NSString *path = [self absolutePathForRelativeTaskPath:taskDescription];
|
||||
|
||||
[[self delegate] networkClient:self didFinishUploadWithPath:path error:error];
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDelegate
|
||||
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Client", @"session became invalid: %@", error);
|
||||
}
|
||||
|
||||
// Careful! Not implementing this method appears to cause a crash when using a background task
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error {
|
||||
[FIRCLSNetworkResponseHandler handleCompletedResponse:task.response
|
||||
forOriginalRequest:task.originalRequest
|
||||
error:error
|
||||
block:^(BOOL restart, NSError *taskError) {
|
||||
if (restart) {
|
||||
[self restartTask:task];
|
||||
return;
|
||||
}
|
||||
|
||||
[self handleTask:task
|
||||
completedWithError:taskError];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
57
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h
generated
Normal file
57
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h
generated
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "FIRCLSApplicationIdentifierModel.h"
|
||||
#include "FIRCLSProfiling.h"
|
||||
#include "FIRCrashlytics.h"
|
||||
|
||||
@class FBLPromise<T>;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FIRCLSDataCollectionArbiter;
|
||||
@class FIRCLSFileManager;
|
||||
@class FIRCLSInternalReport;
|
||||
@class FIRCLSSettings;
|
||||
@class GDTCORTransport;
|
||||
@class FIRInstallations;
|
||||
@protocol FIRAnalyticsInterop;
|
||||
|
||||
@interface FIRCLSReportManager : NSObject
|
||||
|
||||
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
|
||||
installations:(FIRInstallations *)installations
|
||||
analytics:(nullable id<FIRAnalyticsInterop>)analytics
|
||||
googleAppID:(NSString *)googleAppID
|
||||
dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
|
||||
googleTransport:(GDTCORTransport *)googleTransport
|
||||
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
|
||||
settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
- (FBLPromise<NSNumber *> *)startWithProfilingMark:(FIRCLSProfileMark)mark;
|
||||
|
||||
- (FBLPromise<NSNumber *> *)checkForUnsentReports;
|
||||
- (FBLPromise *)sendUnsentReports;
|
||||
- (FBLPromise *)deleteUnsentReports;
|
||||
|
||||
@end
|
||||
|
||||
extern NSString *const FIRCLSConfigSubmitReportsKey;
|
||||
extern NSString *const FIRCLSConfigPackageReportsKey;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
909
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
generated
Normal file
909
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
generated
Normal file
|
@ -0,0 +1,909 @@
|
|||
// 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.
|
||||
|
||||
//
|
||||
// The report manager has the ability to send to two different endpoints.
|
||||
//
|
||||
// The old legacy flow for a report goes through the following states/folders:
|
||||
// 1. active - .clsrecords optimized for crash time persistence
|
||||
// 2. processing - .clsrecords with attempted symbolication
|
||||
// 3. prepared-legacy - .multipartmime of compressed .clsrecords
|
||||
//
|
||||
// The new flow for a report goes through the following states/folders:
|
||||
// 1. active - .clsrecords optimized for crash time persistence
|
||||
// 2. processing - .clsrecords with attempted symbolication
|
||||
// 3. prepared - .clsrecords moved from processing with no changes
|
||||
//
|
||||
// The code was designed so the report processing workflows are not dramatically different from one
|
||||
// another. The design will help avoid having a lot of conditional code blocks throughout the
|
||||
// codebase.
|
||||
//
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#if __has_include(<FBLPromises/FBLPromises.h>)
|
||||
#import <FBLPromises/FBLPromises.h>
|
||||
#else
|
||||
#import "FBLPromises.h"
|
||||
#endif
|
||||
|
||||
#import "FIRCLSApplication.h"
|
||||
#import "FIRCLSDataCollectionArbiter.h"
|
||||
#import "FIRCLSDataCollectionToken.h"
|
||||
#import "FIRCLSDefines.h"
|
||||
#import "FIRCLSFeatures.h"
|
||||
#import "FIRCLSFileManager.h"
|
||||
#import "FIRCLSInternalReport.h"
|
||||
#import "FIRCLSLogger.h"
|
||||
#import "FIRCLSNetworkClient.h"
|
||||
#import "FIRCLSPackageReportOperation.h"
|
||||
#import "FIRCLSProcessReportOperation.h"
|
||||
#import "FIRCLSReportUploader.h"
|
||||
#import "FIRCLSSettings.h"
|
||||
#import "FIRCLSSymbolResolver.h"
|
||||
#import "FIRCLSUserLogging.h"
|
||||
|
||||
#include "FIRCLSGlobals.h"
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
#import "FIRCLSConstants.h"
|
||||
#import "FIRCLSExecutionIdentifierModel.h"
|
||||
#import "FIRCLSInstallIdentifierModel.h"
|
||||
#import "FIRCLSSettingsOnboardingManager.h"
|
||||
|
||||
#import "FIRCLSReportManager_Private.h"
|
||||
|
||||
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
|
||||
#import "Interop/Analytics/Public/FIRAnalyticsInteropListener.h"
|
||||
|
||||
#include "FIRAEvent+Internal.h"
|
||||
#include "FIRCLSFCRAnalytics.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
static NSTimeInterval const CLSReportRetryInterval = 10 * 60;
|
||||
|
||||
static NSString *FIRCLSFirebaseAnalyticsEventLogFormat = @"$A$:%@";
|
||||
|
||||
@interface FIRCLSAnalyticsInteropListener : NSObject <FIRAnalyticsInteropListener> {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FIRCLSAnalyticsInteropListener
|
||||
|
||||
- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters {
|
||||
NSDictionary *event = @{
|
||||
@"name" : name,
|
||||
@"parameters" : parameters,
|
||||
};
|
||||
NSString *json = FIRCLSFIRAEventDictionaryToJSON(event);
|
||||
if (json != nil) {
|
||||
FIRCLSLog(FIRCLSFirebaseAnalyticsEventLogFormat, json);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* A FIRReportAction is used to indicate how to handle unsent reports.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, FIRCLSReportAction) {
|
||||
/** Upload the reports to Crashlytics. */
|
||||
FIRCLSReportActionSend,
|
||||
/** Delete the reports without uploading them. */
|
||||
FIRCLSReportActionDelete,
|
||||
};
|
||||
|
||||
/**
|
||||
* This is just a helper to make code using FIRReportAction more readable.
|
||||
*/
|
||||
typedef NSNumber FIRCLSWrappedReportAction;
|
||||
@implementation NSNumber (FIRCLSWrappedReportAction)
|
||||
- (FIRCLSReportAction)reportActionValue {
|
||||
return [self intValue];
|
||||
}
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a helper to make code using NSNumber for bools more readable.
|
||||
*/
|
||||
typedef NSNumber FIRCLSWrappedBool;
|
||||
|
||||
@interface FIRCLSReportManager () <FIRCLSNetworkClientDelegate,
|
||||
FIRCLSReportUploaderDelegate,
|
||||
FIRCLSReportUploaderDataSource> {
|
||||
FIRCLSFileManager *_fileManager;
|
||||
FIRCLSNetworkClient *_networkClient;
|
||||
FIRCLSReportUploader *_uploader;
|
||||
dispatch_queue_t _dispatchQueue;
|
||||
NSOperationQueue *_operationQueue;
|
||||
id<FIRAnalyticsInterop> _analytics;
|
||||
|
||||
// A promise that will be resolved when unsent reports are found on the device, and
|
||||
// processReports: can be called to decide how to deal with them.
|
||||
FBLPromise<FIRCLSWrappedBool *> *_unsentReportsAvailable;
|
||||
|
||||
// A promise that will be resolved when the user has provided an action that they want to perform
|
||||
// for all the unsent reports.
|
||||
FBLPromise<FIRCLSWrappedReportAction *> *_reportActionProvided;
|
||||
|
||||
// A promise that will be resolved when all unsent reports have been "handled". They won't
|
||||
// necessarily have been uploaded, but we will know whether they should be sent or deleted, and
|
||||
// the initial work to make that happen will have been processed on the work queue.
|
||||
//
|
||||
// Currently only used for testing
|
||||
FBLPromise *_unsentReportsHandled;
|
||||
|
||||
// A token to make sure that checkForUnsentReports only gets called once.
|
||||
atomic_bool _checkForUnsentReportsCalled;
|
||||
|
||||
BOOL _registeredAnalyticsEventListener;
|
||||
}
|
||||
|
||||
@property(nonatomic, readonly) NSString *googleAppID;
|
||||
|
||||
@property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter;
|
||||
|
||||
// Uniquely identifies a build / binary of the app
|
||||
@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
|
||||
|
||||
// Uniquely identifies an install of the app
|
||||
@property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel;
|
||||
|
||||
// Uniquely identifies a run of the app
|
||||
@property(nonatomic, strong) FIRCLSExecutionIdentifierModel *executionIDModel;
|
||||
|
||||
// Settings fetched from the server
|
||||
@property(nonatomic, strong) FIRCLSSettings *settings;
|
||||
|
||||
// Runs the operations that fetch settings and call onboarding endpoints
|
||||
@property(nonatomic, strong) FIRCLSSettingsOnboardingManager *settingsAndOnboardingManager;
|
||||
|
||||
@property(nonatomic, strong) GDTCORTransport *googleTransport;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRCLSReportManager
|
||||
|
||||
// Used only for internal data collection E2E testing
|
||||
static void (^reportSentCallback)(void);
|
||||
|
||||
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
|
||||
installations:(FIRInstallations *)installations
|
||||
analytics:(id<FIRAnalyticsInterop>)analytics
|
||||
googleAppID:(NSString *)googleAppID
|
||||
dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
|
||||
googleTransport:(GDTCORTransport *)googleTransport
|
||||
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
|
||||
settings:(FIRCLSSettings *)settings {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_fileManager = fileManager;
|
||||
_analytics = analytics;
|
||||
_googleAppID = [googleAppID copy];
|
||||
_dataArbiter = dataArbiter;
|
||||
|
||||
_googleTransport = googleTransport;
|
||||
|
||||
NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID();
|
||||
|
||||
_operationQueue = [NSOperationQueue new];
|
||||
[_operationQueue setMaxConcurrentOperationCount:1];
|
||||
[_operationQueue setName:[sdkBundleID stringByAppendingString:@".work-queue"]];
|
||||
|
||||
_dispatchQueue = dispatch_queue_create("com.google.firebase.crashlytics.startup", 0);
|
||||
_operationQueue.underlyingQueue = _dispatchQueue;
|
||||
|
||||
_networkClient = [self clientWithOperationQueue:_operationQueue];
|
||||
|
||||
_unsentReportsAvailable = [FBLPromise pendingPromise];
|
||||
_reportActionProvided = [FBLPromise pendingPromise];
|
||||
_unsentReportsHandled = [FBLPromise pendingPromise];
|
||||
|
||||
_checkForUnsentReportsCalled = NO;
|
||||
|
||||
_installIDModel = [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:installations];
|
||||
_executionIDModel = [[FIRCLSExecutionIdentifierModel alloc] init];
|
||||
|
||||
_settings = settings;
|
||||
_appIDModel = appIDModel;
|
||||
|
||||
_settingsAndOnboardingManager =
|
||||
[[FIRCLSSettingsOnboardingManager alloc] initWithAppIDModel:appIDModel
|
||||
installIDModel:self.installIDModel
|
||||
settings:self.settings
|
||||
fileManager:self.fileManager
|
||||
googleAppID:self.googleAppID];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FIRCLSNetworkClient *)clientWithOperationQueue:(NSOperationQueue *)queue {
|
||||
return [[FIRCLSNetworkClient alloc] initWithQueue:queue fileManager:_fileManager delegate:self];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of unsent reports on the device, including the ones passed in.
|
||||
*/
|
||||
- (int)unsentReportsCountWithPreexisting:(NSArray<NSString *> *)paths {
|
||||
int count = [self countSubmittableAndDeleteUnsubmittableReportPaths:paths];
|
||||
|
||||
count += _fileManager.processingPathContents.count;
|
||||
|
||||
if (self.settings.shouldUseNewReportEndpoint) {
|
||||
count += _fileManager.preparedPathContents.count;
|
||||
} else {
|
||||
count += _fileManager.legacyPreparedPathContents.count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// This method returns a promise that is resolved with a wrapped FIRReportAction once the user has
|
||||
// indicated whether they want to upload currently cached reports.
|
||||
// This method should only be called when we have determined there is at least 1 unsent report.
|
||||
// This method waits until either:
|
||||
// 1. Data collection becomes enabled, in which case, the promise will be resolved with Send.
|
||||
// 2. The developer uses the processCrashReports API to indicate whether the report
|
||||
// should be sent or deleted, at which point the promise will be resolved with the action.
|
||||
- (FBLPromise<FIRCLSWrappedReportAction *> *)waitForReportAction {
|
||||
FIRCLSDebugLog(@"[Crashlytics:Crash] Notifying that unsent reports are available.");
|
||||
[_unsentReportsAvailable fulfill:@YES];
|
||||
|
||||
// If data collection gets enabled while we are waiting for an action, go ahead and send the
|
||||
// reports, and any subsequent explicit response will be ignored.
|
||||
FBLPromise<FIRCLSWrappedReportAction *> *collectionEnabled =
|
||||
[[self.dataArbiter waitForCrashlyticsCollectionEnabled]
|
||||
then:^id _Nullable(NSNumber *_Nullable value) {
|
||||
return @(FIRCLSReportActionSend);
|
||||
}];
|
||||
|
||||
FIRCLSDebugLog(@"[Crashlytics:Crash] Waiting for send/deleteUnsentReports to be called.");
|
||||
// Wait for either the processReports callback to be called, or data collection to be enabled.
|
||||
return [FBLPromise race:@[ collectionEnabled, _reportActionProvided ]];
|
||||
}
|
||||
|
||||
- (FBLPromise<FIRCLSWrappedBool *> *)checkForUnsentReports {
|
||||
bool expectedCalled = NO;
|
||||
if (!atomic_compare_exchange_strong(&_checkForUnsentReportsCalled, &expectedCalled, YES)) {
|
||||
FIRCLSErrorLog(@"checkForUnsentReports should only be called once per execution.");
|
||||
return [FBLPromise resolvedWith:@NO];
|
||||
}
|
||||
return _unsentReportsAvailable;
|
||||
}
|
||||
|
||||
- (FBLPromise *)sendUnsentReports {
|
||||
[_reportActionProvided fulfill:@(FIRCLSReportActionSend)];
|
||||
return _unsentReportsHandled;
|
||||
}
|
||||
|
||||
- (FBLPromise *)deleteUnsentReports {
|
||||
[_reportActionProvided fulfill:@(FIRCLSReportActionDelete)];
|
||||
return _unsentReportsHandled;
|
||||
}
|
||||
|
||||
- (FBLPromise<NSNumber *> *)startWithProfilingMark:(FIRCLSProfileMark)mark {
|
||||
NSString *executionIdentifier = self.executionIDModel.executionID;
|
||||
|
||||
// This needs to be called before any values are read from settings
|
||||
NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
|
||||
[self.settings reloadFromCacheWithGoogleAppID:self.googleAppID currentTimestamp:currentTimestamp];
|
||||
|
||||
if (![self validateAppIdentifiers]) {
|
||||
return [FBLPromise resolvedWith:@NO];
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
FIRCLSDebugLog(@"Root: %@", [_fileManager rootPath]);
|
||||
#endif
|
||||
|
||||
if ([self.dataArbiter isLegacyDataCollectionKeyInPlist]) {
|
||||
FIRCLSErrorLog(@"Found legacy data collection key in app's Info.plist: "
|
||||
@"firebase_crashlytics_collection_enabled");
|
||||
FIRCLSErrorLog(@"Please update your Info.plist to use the new data collection key: "
|
||||
@"FirebaseCrashlyticsCollectionEnabled");
|
||||
FIRCLSErrorLog(@"The legacy data collection Info.plist value could be overridden by "
|
||||
@"calling: [Fabric with:...]");
|
||||
FIRCLSErrorLog(@"The new value can be overridden by calling: [[FIRCrashlytics "
|
||||
@"crashlytics] setCrashlyticsCollectionEnabled:<isEnabled>]");
|
||||
|
||||
return [FBLPromise resolvedWith:@NO];
|
||||
}
|
||||
|
||||
if (![_fileManager createReportDirectories]) {
|
||||
return [FBLPromise resolvedWith:@NO];
|
||||
}
|
||||
|
||||
// Grab existing reports
|
||||
BOOL launchFailure = [self checkForAndCreateLaunchMarker];
|
||||
NSArray *preexistingReportPaths = _fileManager.activePathContents;
|
||||
|
||||
FIRCLSInternalReport *report = [self setupCurrentReport:executionIdentifier];
|
||||
if (!report) {
|
||||
FIRCLSErrorLog(@"Unable to setup a new report");
|
||||
}
|
||||
|
||||
if (![self startCrashReporterWithProfilingMark:mark report:report]) {
|
||||
FIRCLSErrorLog(@"Unable to start crash reporter");
|
||||
report = nil;
|
||||
}
|
||||
|
||||
// Regenerate the Install ID on a background thread if it needs to rotate because
|
||||
// fetching the Firebase Install ID can be slow on some devices. This should happen after we
|
||||
// create the session on disk so that we can update the Install ID in the written crash report
|
||||
// metadata.
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
[self checkAndRotateInstallUUIDIfNeededWithReport:report];
|
||||
});
|
||||
|
||||
FBLPromise<NSNumber *> *promise = [FBLPromise resolvedWith:@(report != nil)];
|
||||
|
||||
if ([self.dataArbiter isCrashlyticsCollectionEnabled]) {
|
||||
FIRCLSDebugLog(@"Automatic data collection is enabled.");
|
||||
FIRCLSDebugLog(@"Unsent reports will be uploaded at startup");
|
||||
FIRCLSDataCollectionToken *dataCollectionToken = [FIRCLSDataCollectionToken validToken];
|
||||
|
||||
[self beginSettingsAndOnboardingWithToken:dataCollectionToken waitForSettingsRequest:NO];
|
||||
|
||||
[self beginReportUploadsWithToken:dataCollectionToken
|
||||
preexistingReportPaths:preexistingReportPaths
|
||||
blockingSend:launchFailure
|
||||
report:report];
|
||||
|
||||
// If data collection is enabled, the SDK will not notify the user
|
||||
// when unsent reports are available, or respect Send / DeleteUnsentReports
|
||||
[_unsentReportsAvailable fulfill:@NO];
|
||||
|
||||
} else {
|
||||
FIRCLSDebugLog(@"Automatic data collection is disabled.");
|
||||
|
||||
// TODO: This counting of the file system happens on the main thread. Now that some of the other
|
||||
// work below has been made async and moved to the dispatch queue, maybe we can move this code
|
||||
// to the dispatch queue as well.
|
||||
int unsentReportsCount = [self unsentReportsCountWithPreexisting:preexistingReportPaths];
|
||||
if (unsentReportsCount > 0) {
|
||||
FIRCLSDebugLog(
|
||||
@"[Crashlytics:Crash] %d unsent reports are available. Checking for upload permission.",
|
||||
unsentReportsCount);
|
||||
// Wait for an action to get sent, either from processReports: or automatic data collection.
|
||||
promise = [[self waitForReportAction]
|
||||
onQueue:_dispatchQueue
|
||||
then:^id _Nullable(FIRCLSWrappedReportAction *_Nullable wrappedAction) {
|
||||
// Process the actions for the reports on disk.
|
||||
FIRCLSReportAction action = [wrappedAction reportActionValue];
|
||||
if (action == FIRCLSReportActionSend) {
|
||||
FIRCLSDebugLog(@"Sending unsent reports.");
|
||||
FIRCLSDataCollectionToken *dataCollectionToken =
|
||||
[FIRCLSDataCollectionToken validToken];
|
||||
|
||||
// For the new report endpoint, the orgID is not needed.
|
||||
// For the legacy report endpoint, wait on settings if orgID is not available.
|
||||
BOOL waitForSetting =
|
||||
!self.settings.shouldUseNewReportEndpoint && !self.settings.orgID;
|
||||
|
||||
[self beginSettingsAndOnboardingWithToken:dataCollectionToken
|
||||
waitForSettingsRequest:waitForSetting];
|
||||
|
||||
[self beginReportUploadsWithToken:dataCollectionToken
|
||||
preexistingReportPaths:preexistingReportPaths
|
||||
blockingSend:NO
|
||||
report:report];
|
||||
|
||||
} else if (action == FIRCLSReportActionDelete) {
|
||||
FIRCLSDebugLog(@"Deleting unsent reports.");
|
||||
[self deleteUnsentReportsWithPreexisting:preexistingReportPaths];
|
||||
} else {
|
||||
FIRCLSErrorLog(@"Unknown report action: %d", action);
|
||||
}
|
||||
return @(report != nil);
|
||||
}];
|
||||
} else {
|
||||
FIRCLSDebugLog(@"[Crashlytics:Crash] There are no unsent reports.");
|
||||
[_unsentReportsAvailable fulfill:@NO];
|
||||
}
|
||||
}
|
||||
|
||||
if (report != nil) {
|
||||
// capture the start-up time here, but record it asynchronously
|
||||
double endMark = FIRCLSProfileEnd(mark);
|
||||
|
||||
dispatch_async(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSStartTimeKey, [@(endMark) description]);
|
||||
});
|
||||
}
|
||||
|
||||
// To make the code more predictable and therefore testable, don't resolve the startup promise
|
||||
// until the operations that got queued up for processing reports have been processed through the
|
||||
// work queue.
|
||||
NSOperationQueue *__weak queue = _operationQueue;
|
||||
FBLPromise *__weak unsentReportsHandled = _unsentReportsHandled;
|
||||
promise = [promise then:^id _Nullable(NSNumber *_Nullable value) {
|
||||
[queue waitUntilAllOperationsAreFinished];
|
||||
// Signal that to callers of processReports that everything is finished.
|
||||
[unsentReportsHandled fulfill:nil];
|
||||
return value;
|
||||
}];
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
- (void)checkAndRotateInstallUUIDIfNeededWithReport:(FIRCLSInternalReport *)report {
|
||||
[self.installIDModel regenerateInstallIDIfNeededWithBlock:^(BOOL didRotate) {
|
||||
if (!didRotate) {
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSContextUpdateMetadata(report, self.settings, self.installIDModel, self->_fileManager);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)beginSettingsAndOnboardingWithToken:(FIRCLSDataCollectionToken *)token
|
||||
waitForSettingsRequest:(BOOL)waitForSettings {
|
||||
if (self.settings.isCacheExpired) {
|
||||
// This method can be called more than once if the user calls
|
||||
// SendUnsentReports again, so don't repeat the settings fetch
|
||||
static dispatch_once_t settingsFetchOnceToken;
|
||||
dispatch_once(&settingsFetchOnceToken, ^{
|
||||
[self.settingsAndOnboardingManager beginSettingsAndOnboardingWithGoogleAppId:self.googleAppID
|
||||
token:token
|
||||
waitForCompletion:waitForSettings];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)beginReportUploadsWithToken:(FIRCLSDataCollectionToken *)token
|
||||
preexistingReportPaths:(NSArray *)preexistingReportPaths
|
||||
blockingSend:(BOOL)blockingSend
|
||||
report:(FIRCLSInternalReport *)report {
|
||||
if (self.settings.collectReportsEnabled) {
|
||||
[self processExistingReportPaths:preexistingReportPaths
|
||||
dataCollectionToken:token
|
||||
asUrgent:blockingSend];
|
||||
[self handleContentsInOtherReportingDirectoriesWithToken:token];
|
||||
|
||||
} else {
|
||||
FIRCLSInfoLog(@"Collect crash reports is disabled");
|
||||
[self deleteUnsentReportsWithPreexisting:preexistingReportPaths];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)startCrashReporterWithProfilingMark:(FIRCLSProfileMark)mark
|
||||
report:(FIRCLSInternalReport *)report {
|
||||
if (!report) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!FIRCLSContextInitialize(report, self.settings, self.installIDModel, _fileManager)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self setupStateNotifications];
|
||||
|
||||
[self registerAnalyticsEventListener];
|
||||
|
||||
[self crashReportingSetupCompleted:mark];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)crashReportingSetupCompleted:(FIRCLSProfileMark)mark {
|
||||
// check our handlers
|
||||
FIRCLSDispatchAfter(2.0, dispatch_get_main_queue(), ^{
|
||||
FIRCLSExceptionCheckHandlers((__bridge void *)(self));
|
||||
FIRCLSSignalCheckHandlers();
|
||||
#if CLS_MACH_EXCEPTION_SUPPORTED
|
||||
FIRCLSMachExceptionCheckHandlers();
|
||||
#endif
|
||||
});
|
||||
|
||||
// remove the launch failure marker and record the startup time
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self removeLaunchFailureMarker];
|
||||
dispatch_async(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSFirstRunloopTurnTimeKey,
|
||||
[@(FIRCLSProfileEnd(mark)) description]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)validateAppIdentifiers {
|
||||
// When the ApplicationIdentifierModel fails to initialize, it is usually due to
|
||||
// failing computeExecutableInfo. This can happen if the user sets the
|
||||
// Exported Symbols File in Build Settings, and leaves off the one symbol
|
||||
// that Crashlytics needs, "__mh_execute_header" (wich is defined in mach-o/ldsyms.h as
|
||||
// _MH_EXECUTE_SYM). From https://github.com/firebase/firebase-ios-sdk/issues/5020
|
||||
if (!self.appIDModel) {
|
||||
FIRCLSErrorLog(
|
||||
@"Crashlytics could not find the symbol for the app's main function and cannot "
|
||||
@"start up. This can happen when Exported Symbols File is set in Build Settings. To "
|
||||
@"resolve this, add \"__mh_execute_header\" as a newline to your Exported Symbols File.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (self.appIDModel.bundleID.length == 0) {
|
||||
FIRCLSErrorLog(@"An application must have a valid bundle identifier in its Info.plist");
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (FIRCLSReportUploader *)uploader {
|
||||
if (!_uploader) {
|
||||
_uploader = [[FIRCLSReportUploader alloc] initWithQueue:self.operationQueue
|
||||
delegate:self
|
||||
dataSource:self
|
||||
client:self.networkClient
|
||||
fileManager:_fileManager
|
||||
analytics:_analytics];
|
||||
}
|
||||
|
||||
return _uploader;
|
||||
}
|
||||
|
||||
#pragma mark - Reporting Lifecycle
|
||||
|
||||
- (FIRCLSInternalReport *)setupCurrentReport:(NSString *)executionIdentifier {
|
||||
[self createLaunchFailureMarker];
|
||||
|
||||
NSString *reportPath = [_fileManager setupNewPathForExecutionIdentifier:executionIdentifier];
|
||||
|
||||
return [[FIRCLSInternalReport alloc] initWithPath:reportPath
|
||||
executionIdentifier:executionIdentifier];
|
||||
}
|
||||
|
||||
- (int)countSubmittableAndDeleteUnsubmittableReportPaths:(NSArray *)reportPaths {
|
||||
int count = 0;
|
||||
for (NSString *path in reportPaths) {
|
||||
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
|
||||
if ([report needsToBeSubmitted]) {
|
||||
count++;
|
||||
} else {
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
[self->_fileManager removeItemAtPath:path];
|
||||
}];
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void)processExistingReportPaths:(NSArray *)reportPaths
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
asUrgent:(BOOL)urgent {
|
||||
for (NSString *path in reportPaths) {
|
||||
[self processExistingActiveReportPath:path
|
||||
dataCollectionToken:dataCollectionToken
|
||||
asUrgent:urgent];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processExistingActiveReportPath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
asUrgent:(BOOL)urgent {
|
||||
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
|
||||
|
||||
// TODO: needsToBeSubmitted should really be called on the background queue.
|
||||
if (![report needsToBeSubmitted]) {
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
[self->_fileManager removeItemAtPath:path];
|
||||
}];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (urgent && [dataCollectionToken isValid]) {
|
||||
// We can proceed without the delegate.
|
||||
[[self uploader] prepareAndSubmitReport:report
|
||||
dataCollectionToken:dataCollectionToken
|
||||
asUrgent:urgent
|
||||
withProcessing:YES];
|
||||
return;
|
||||
}
|
||||
|
||||
[self submitReport:report dataCollectionToken:dataCollectionToken];
|
||||
}
|
||||
|
||||
- (void)submitReport:(FIRCLSInternalReport *)report
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken {
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
[[self uploader] prepareAndSubmitReport:report
|
||||
dataCollectionToken:dataCollectionToken
|
||||
asUrgent:NO
|
||||
withProcessing:YES];
|
||||
}];
|
||||
|
||||
[self didSubmitReport];
|
||||
}
|
||||
|
||||
// This is the side-effect of calling deleteUnsentReports, or collect_reports setting
|
||||
// being false
|
||||
- (void)deleteUnsentReportsWithPreexisting:(NSArray *)preexistingReportPaths {
|
||||
[self removeExistingReportPaths:preexistingReportPaths];
|
||||
|
||||
[self removeExistingReportPaths:self.fileManager.processingPathContents];
|
||||
if (self.settings.shouldUseNewReportEndpoint) {
|
||||
[self removeExistingReportPaths:self.fileManager.preparedPathContents];
|
||||
} else {
|
||||
[self removeExistingReportPaths:self.fileManager.legacyPreparedPathContents];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeExistingReportPaths:(NSArray *)reportPaths {
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
for (NSString *path in reportPaths) {
|
||||
[self.fileManager removeItemAtPath:path];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleContentsInOtherReportingDirectoriesWithToken:(FIRCLSDataCollectionToken *)token {
|
||||
[self handleExistingFilesInProcessingWithToken:token];
|
||||
[self handleExistingFilesInPreparedWithToken:token];
|
||||
}
|
||||
|
||||
- (void)handleExistingFilesInProcessingWithToken:(FIRCLSDataCollectionToken *)token {
|
||||
NSArray *processingPaths = _fileManager.processingPathContents;
|
||||
|
||||
// deal with stuff in processing more carefully - do not process again
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
for (NSString *path in processingPaths) {
|
||||
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
|
||||
[[self uploader] prepareAndSubmitReport:report
|
||||
dataCollectionToken:token
|
||||
asUrgent:NO
|
||||
withProcessing:NO];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)handleExistingFilesInPreparedWithToken:(FIRCLSDataCollectionToken *)token {
|
||||
NSArray *preparedPaths = self.settings.shouldUseNewReportEndpoint
|
||||
? _fileManager.preparedPathContents
|
||||
: _fileManager.legacyPreparedPathContents;
|
||||
|
||||
// Give our network client a chance to reconnect here, if needed. This attempts to avoid
|
||||
// trying to re-submit a prepared file that is already in flight.
|
||||
[self.networkClient attemptToReconnectBackgroundSessionWithCompletionBlock:^{
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
[self uploadPreexistingFiles:preparedPaths withToken:token];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)uploadPreexistingFiles:(NSArray *)files withToken:(FIRCLSDataCollectionToken *)token {
|
||||
// Because this could happen quite a bit after the inital set of files was
|
||||
// captured, some could be completed (deleted). So, just double-check to make sure
|
||||
// the file still exists.
|
||||
|
||||
for (NSString *path in files) {
|
||||
if (![[_fileManager underlyingFileManager] fileExistsAtPath:path]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[[self uploader] uploadPackagedReportAtPath:path dataCollectionToken:token asUrgent:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)retryUploadForReportAtPath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)token {
|
||||
FIRCLSAddOperationAfter(CLSReportRetryInterval, self.operationQueue, ^{
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash", @"re-attempting report submission");
|
||||
[[self uploader] uploadPackagedReportAtPath:path dataCollectionToken:token asUrgent:NO];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Launch Failure Detection
|
||||
- (NSString *)launchFailureMarkerPath {
|
||||
return [[_fileManager structurePath] stringByAppendingPathComponent:@"launchmarker"];
|
||||
}
|
||||
|
||||
- (BOOL)createLaunchFailureMarker {
|
||||
// It's tempting to use - [NSFileManger createFileAtPath:contents:attributes:] here. But that
|
||||
// operation, even with empty/nil contents does a ton of work to write out nothing via a
|
||||
// temporarly file. This is a much faster implemenation.
|
||||
const char *path = [[self launchFailureMarkerPath] fileSystemRepresentation];
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
/*
|
||||
* data-protected non-portable open(2) :
|
||||
* int open_dprotected_np(user_addr_t path, int flags, int class, int dpflags, int mode)
|
||||
*/
|
||||
int fd = open_dprotected_np(path, O_WRONLY | O_CREAT | O_TRUNC, 4, 0, 0644);
|
||||
#else
|
||||
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
#endif
|
||||
if (fd == -1) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return close(fd) == 0;
|
||||
}
|
||||
|
||||
- (BOOL)launchFailureMarkerPresent {
|
||||
return [[_fileManager underlyingFileManager] fileExistsAtPath:[self launchFailureMarkerPath]];
|
||||
}
|
||||
|
||||
- (BOOL)removeLaunchFailureMarker {
|
||||
return [_fileManager removeItemAtPath:[self launchFailureMarkerPath]];
|
||||
}
|
||||
|
||||
- (BOOL)checkForAndCreateLaunchMarker {
|
||||
BOOL launchFailure = [self launchFailureMarkerPresent];
|
||||
if (launchFailure) {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash",
|
||||
@"Last launch failed: this may indicate a crash shortly after app launch.");
|
||||
} else {
|
||||
[self createLaunchFailureMarker];
|
||||
}
|
||||
|
||||
return launchFailure;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)registerAnalyticsEventListener {
|
||||
if (_registeredAnalyticsEventListener) {
|
||||
return;
|
||||
}
|
||||
FIRCLSAnalyticsInteropListener *listener = [[FIRCLSAnalyticsInteropListener alloc] init];
|
||||
[FIRCLSFCRAnalytics registerEventListener:listener toAnalytics:_analytics];
|
||||
_registeredAnalyticsEventListener = YES;
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
- (void)setupStateNotifications {
|
||||
[self captureInitialNotificationStates];
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(willBecomeActive:)
|
||||
name:UIApplicationWillEnterForegroundNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didBecomeInactive:)
|
||||
name:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didChangeOrientation:)
|
||||
name:UIDeviceOrientationDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(didChangeUIOrientation:)
|
||||
name:UIApplicationDidChangeStatusBarOrientationNotification
|
||||
object:nil];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#elif CLS_TARGET_OS_OSX
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(willBecomeActive:)
|
||||
name:@"NSApplicationWillBecomeActiveNotification"
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didBecomeInactive:)
|
||||
name:@"NSApplicationDidResignActiveNotification"
|
||||
object:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)captureInitialNotificationStates {
|
||||
#if TARGET_OS_IOS
|
||||
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
|
||||
UIInterfaceOrientation statusBarOrientation =
|
||||
[FIRCLSApplicationSharedInstance() statusBarOrientation];
|
||||
#endif
|
||||
|
||||
// It's nice to do this async, so we don't hold up the main thread while doing three
|
||||
// consecutive IOs here.
|
||||
dispatch_async(FIRCLSGetLoggingQueue(), ^{
|
||||
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSInBackgroundKey, @"0");
|
||||
#if TARGET_OS_IOS
|
||||
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSDeviceOrientationKey,
|
||||
[@(orientation) description]);
|
||||
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSUIOrientationKey,
|
||||
[@(statusBarOrientation) description]);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
- (void)willBecomeActive:(NSNotification *)notification {
|
||||
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @NO);
|
||||
}
|
||||
|
||||
- (void)didBecomeInactive:(NSNotification *)notification {
|
||||
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @YES);
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
- (void)didChangeOrientation:(NSNotification *)notification {
|
||||
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
|
||||
|
||||
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDeviceOrientationKey, @(orientation));
|
||||
}
|
||||
|
||||
- (void)didChangeUIOrientation:(NSNotification *)notification {
|
||||
UIInterfaceOrientation statusBarOrientation =
|
||||
[FIRCLSApplicationSharedInstance() statusBarOrientation];
|
||||
|
||||
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUIOrientationKey, @(statusBarOrientation));
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - FIRCLSNetworkClientDelegate
|
||||
- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client {
|
||||
return !FIRCLSApplicationIsExtension();
|
||||
}
|
||||
|
||||
- (void)networkClient:(FIRCLSNetworkClient *)client
|
||||
didFinishUploadWithPath:(NSString *)path
|
||||
error:(NSError *)error {
|
||||
// Route this through to the reports uploader.
|
||||
// Since this callback happens after an upload finished, then we can assume that the original data
|
||||
// collection was authorized. This isn't ideal, but it's better than trying to plumb the data
|
||||
// collection token through all the system networking callbacks.
|
||||
FIRCLSDataCollectionToken *token = [FIRCLSDataCollectionToken validToken];
|
||||
[[self uploader] reportUploadAtPath:path dataCollectionToken:token completedWithError:error];
|
||||
}
|
||||
|
||||
#pragma mark - FIRCLSReportUploaderDelegate
|
||||
|
||||
- (void)didCompletePackageSubmission:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)token
|
||||
error:(NSError *)error {
|
||||
if (!error) {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission successful");
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission failed with error %@", error);
|
||||
FIRCLSSDKLog("Error: failed to submit report '%s'\n", error.description.UTF8String);
|
||||
|
||||
[self retryUploadForReportAtPath:path dataCollectionToken:token];
|
||||
}
|
||||
|
||||
- (void)didCompleteAllSubmissions {
|
||||
[self.operationQueue addOperationWithBlock:^{
|
||||
// Dealloc the reports uploader. If we need it again (if we re-enqueued submissions from
|
||||
// didCompletePackageSubmission:, we can just create it again
|
||||
self->_uploader = nil;
|
||||
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission complete");
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITest Helpers
|
||||
|
||||
// Used only for internal data collection E2E testing
|
||||
- (void)didSubmitReport {
|
||||
if (reportSentCallback) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
reportSentCallback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)setReportSentCallback:(void (^)(void))callback {
|
||||
reportSentCallback = callback;
|
||||
}
|
||||
|
||||
@end
|
36
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
generated
Normal file
36
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
generated
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSReportManager.h"
|
||||
#import "FIRCLSReportUploader.h"
|
||||
|
||||
@class FIRCLSInstallIdentifierModel;
|
||||
|
||||
@interface FIRCLSReportManager () <FIRCLSReportUploaderDelegate, FIRCLSReportUploaderDataSource>
|
||||
|
||||
@property(nonatomic, strong) NSOperationQueue *operationQueue;
|
||||
@property(nonatomic, strong) FIRCLSNetworkClient *networkClient;
|
||||
@property(nonatomic, readonly) FIRCLSReportUploader *uploader;
|
||||
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
|
||||
|
||||
@end
|
||||
|
||||
@interface FIRCLSReportManager (PrivateMethods)
|
||||
|
||||
- (BOOL)createLaunchFailureMarker;
|
||||
- (BOOL)launchFailureMarkerPresent;
|
||||
|
||||
- (BOOL)potentiallySubmittableCrashOccurred;
|
||||
|
||||
@end
|
80
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h
generated
Normal file
80
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h
generated
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <GoogleDataTransport/GDTCORTransport.h>
|
||||
|
||||
@class FIRCLSDataCollectionToken;
|
||||
@class FIRCLSInternalReport;
|
||||
@class FIRCLSSettings;
|
||||
@class FIRCLSFileManager;
|
||||
@class FIRCLSNetworkClient;
|
||||
@class FIRCLSReportUploader;
|
||||
|
||||
@protocol FIRCLSReportUploaderDelegate;
|
||||
@protocol FIRCLSReportUploaderDataSource;
|
||||
@protocol FIRAnalyticsInterop;
|
||||
|
||||
@interface FIRCLSReportUploader : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)initWithQueue:(NSOperationQueue *)queue
|
||||
delegate:(id<FIRCLSReportUploaderDelegate>)delegate
|
||||
dataSource:(id<FIRCLSReportUploaderDataSource>)dataSource
|
||||
client:(FIRCLSNetworkClient *)client
|
||||
fileManager:(FIRCLSFileManager *)fileManager
|
||||
analytics:(id<FIRAnalyticsInterop>)analytics NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property(nonatomic, weak) id<FIRCLSReportUploaderDelegate> delegate;
|
||||
@property(nonatomic, weak) id<FIRCLSReportUploaderDataSource> dataSource;
|
||||
|
||||
@property(nonatomic, readonly) NSOperationQueue *operationQueue;
|
||||
@property(nonatomic, readonly) FIRCLSNetworkClient *networkClient;
|
||||
@property(nonatomic, readonly) FIRCLSFileManager *fileManager;
|
||||
|
||||
- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
asUrgent:(BOOL)urgent
|
||||
withProcessing:(BOOL)shouldProcess;
|
||||
|
||||
- (BOOL)uploadPackagedReportAtPath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
asUrgent:(BOOL)urgent;
|
||||
|
||||
- (void)reportUploadAtPath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
completedWithError:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FIRCLSReportUploaderDelegate <NSObject>
|
||||
@required
|
||||
|
||||
- (void)didCompletePackageSubmission:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)token
|
||||
error:(NSError *)error;
|
||||
- (void)didCompleteAllSubmissions;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FIRCLSReportUploaderDataSource <NSObject>
|
||||
@required
|
||||
|
||||
- (NSString *)googleAppID;
|
||||
- (FIRCLSSettings *)settings;
|
||||
- (GDTCORTransport *)googleTransport;
|
||||
|
||||
@end
|
356
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m
generated
Normal file
356
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m
generated
Normal file
|
@ -0,0 +1,356 @@
|
|||
// 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.
|
||||
|
||||
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
|
||||
|
||||
#import "FIRCLSApplication.h"
|
||||
#import "FIRCLSDataCollectionArbiter.h"
|
||||
#import "FIRCLSDataCollectionToken.h"
|
||||
#import "FIRCLSDefines.h"
|
||||
#import "FIRCLSFCRAnalytics.h"
|
||||
#import "FIRCLSFileManager.h"
|
||||
#import "FIRCLSInstallIdentifierModel.h"
|
||||
#import "FIRCLSInternalReport.h"
|
||||
#import "FIRCLSNetworkClient.h"
|
||||
#import "FIRCLSPackageReportOperation.h"
|
||||
#import "FIRCLSProcessReportOperation.h"
|
||||
#import "FIRCLSReportAdapter.h"
|
||||
#import "FIRCLSReportUploader_Private.h"
|
||||
#import "FIRCLSSettings.h"
|
||||
#import "FIRCLSSymbolResolver.h"
|
||||
|
||||
#include "FIRCLSUtility.h"
|
||||
|
||||
#import "FIRCLSConstants.h"
|
||||
#import "FIRCLSMultipartMimeStreamEncoder.h"
|
||||
#import "FIRCLSURLBuilder.h"
|
||||
|
||||
#import <GoogleDataTransport/GDTCOREvent.h>
|
||||
#import <GoogleDataTransport/GDTCORTransport.h>
|
||||
|
||||
@interface FIRCLSReportUploader () {
|
||||
id<FIRAnalyticsInterop> _analytics;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FIRCLSReportUploader
|
||||
|
||||
- (instancetype)initWithQueue:(NSOperationQueue *)queue
|
||||
delegate:(id<FIRCLSReportUploaderDelegate>)delegate
|
||||
dataSource:(id<FIRCLSReportUploaderDataSource>)dataSource
|
||||
client:(FIRCLSNetworkClient *)client
|
||||
fileManager:(FIRCLSFileManager *)fileManager
|
||||
analytics:(id<FIRAnalyticsInterop>)analytics {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_operationQueue = queue;
|
||||
_delegate = delegate;
|
||||
_dataSource = dataSource;
|
||||
_networkClient = client;
|
||||
_fileManager = fileManager;
|
||||
_analytics = analytics;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Packaging and Submission
|
||||
- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
asUrgent:(BOOL)urgent
|
||||
withProcessing:(BOOL)shouldProcess {
|
||||
__block BOOL success = NO;
|
||||
|
||||
if (![dataCollectionToken isValid]) {
|
||||
FIRCLSErrorLog(@"Data collection disabled and report will not be submitted");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!self.dataSource.settings.orgID && !self.dataSource.settings.shouldUseNewReportEndpoint) {
|
||||
FIRCLSDebugLog(
|
||||
@"Skipping report with id '%@' this run of the app because Organization ID was "
|
||||
@"nil. Report via the legacy endpoint will upload once settings are download successfully",
|
||||
report.identifier);
|
||||
return YES;
|
||||
}
|
||||
|
||||
FIRCLSApplicationActivity(
|
||||
FIRCLSApplicationActivityDefault, @"Crashlytics Crash Report Processing", ^{
|
||||
if (shouldProcess) {
|
||||
if (![self.fileManager moveItemAtPath:report.path
|
||||
toDirectory:self.fileManager.processingPath]) {
|
||||
FIRCLSErrorLog(@"Unable to move report for processing");
|
||||
return;
|
||||
}
|
||||
|
||||
// adjust the report's path, and process it
|
||||
[report setPath:[self.fileManager.processingPath
|
||||
stringByAppendingPathComponent:report.directoryName]];
|
||||
|
||||
FIRCLSSymbolResolver *resolver = [[FIRCLSSymbolResolver alloc] init];
|
||||
|
||||
FIRCLSProcessReportOperation *processOperation =
|
||||
[[FIRCLSProcessReportOperation alloc] initWithReport:report resolver:resolver];
|
||||
|
||||
[processOperation start];
|
||||
}
|
||||
|
||||
NSString *packagedPath;
|
||||
|
||||
FIRCLSDebugLog(@"Preparing the report for the new endpoint: %d",
|
||||
self.dataSource.settings.shouldUseNewReportEndpoint);
|
||||
|
||||
// With the new report endpoint, the report is deleted once it is written to GDT
|
||||
// Check if the report has a crash file before the report is moved or deleted
|
||||
BOOL isCrash = report.isCrash;
|
||||
|
||||
if (self.dataSource.settings.shouldUseNewReportEndpoint) {
|
||||
// For the new endpoint, just move the .clsrecords from "processing" -> "prepared"
|
||||
if (![self.fileManager moveItemAtPath:report.path
|
||||
toDirectory:self.fileManager.preparedPath]) {
|
||||
FIRCLSErrorLog(@"Unable to move report to prepared");
|
||||
return;
|
||||
}
|
||||
|
||||
packagedPath = [self.fileManager.preparedPath
|
||||
stringByAppendingPathComponent:report.path.lastPathComponent];
|
||||
} else {
|
||||
// For the legacy endpoint, continue generate the multipartmime file in "prepared-legacy"
|
||||
FIRCLSPackageReportOperation *packageOperation =
|
||||
[[FIRCLSPackageReportOperation alloc] initWithReport:report
|
||||
fileManager:self.fileManager
|
||||
settings:self.dataSource.settings];
|
||||
|
||||
[packageOperation start];
|
||||
packagedPath = packageOperation.finalPath;
|
||||
if (!packagedPath) {
|
||||
FIRCLSErrorLog(@"Unable to package report");
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self.fileManager removeItemAtPath:report.path]) {
|
||||
FIRCLSErrorLog(@"Unable to remove a processing item");
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"[Firebase/Crashlytics] Packaged report with id '%@' for submission",
|
||||
report.identifier);
|
||||
|
||||
success = [self uploadPackagedReportAtPath:packagedPath
|
||||
dataCollectionToken:dataCollectionToken
|
||||
asUrgent:urgent];
|
||||
|
||||
// If the upload was successful and the report contained a crash forward it to Google
|
||||
// Analytics.
|
||||
if (success && isCrash) {
|
||||
[FIRCLSFCRAnalytics logCrashWithTimeStamp:report.crashedOnDate.timeIntervalSince1970
|
||||
toAnalytics:self->_analytics];
|
||||
}
|
||||
});
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)submitPackageMultipartMimeAtPath:(NSString *)multipartmimePath
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
synchronously:(BOOL)synchronous {
|
||||
FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports", "Submitting %@ %@",
|
||||
synchronous ? @"sync" : @"async", multipartmimePath);
|
||||
|
||||
if ([[[self fileManager] fileSizeAtPath:multipartmimePath] unsignedIntegerValue] == 0) {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Already-submitted report being ignored");
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSTimeInterval timeout = 10.0;
|
||||
|
||||
// If we are submitting synchronously, be more aggressive with the timeout. However,
|
||||
// we only need this if the client does not support background requests.
|
||||
if (synchronous && ![[self networkClient] supportsBackgroundRequests]) {
|
||||
timeout = 2.0;
|
||||
}
|
||||
|
||||
NSMutableURLRequest *request = [self mutableRequestWithURL:[self reportURL] timeout:timeout];
|
||||
|
||||
[request setHTTPMethod:@"POST"];
|
||||
|
||||
if (![self fillInRequest:request forMultipartMimeDataAtPath:multipartmimePath]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
[[self networkClient] startUploadRequest:request
|
||||
filePath:multipartmimePath
|
||||
dataCollectionToken:dataCollectionToken
|
||||
immediately:synchronous];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)uploadPackagedReportAtPath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
asUrgent:(BOOL)urgent {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Submitting report%@",
|
||||
urgent ? @" as urgent" : @"");
|
||||
|
||||
// Check with the legacy path as the new path will always be contained in the legacy path
|
||||
BOOL isNewPreparedPath = ![path containsString:self.fileManager.legacyPreparedPath];
|
||||
|
||||
if (isNewPreparedPath && self.dataSource.settings.shouldUseNewReportEndpoint) {
|
||||
if (![dataCollectionToken isValid]) {
|
||||
FIRCLSErrorLog(@"A report upload was requested with an invalid data collection token.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
FIRCLSReportAdapter *adapter =
|
||||
[[FIRCLSReportAdapter alloc] initWithPath:path googleAppId:self.dataSource.googleAppID];
|
||||
|
||||
GDTCOREvent *event = [self.dataSource.googleTransport eventForTransport];
|
||||
event.dataObject = adapter;
|
||||
event.qosTier = GDTCOREventQoSFast; // Bypass batching, send immediately
|
||||
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
|
||||
__block BOOL success = YES;
|
||||
|
||||
[self.dataSource.googleTransport
|
||||
sendDataEvent:event
|
||||
onComplete:^(BOOL wasWritten, NSError *error) {
|
||||
if (!wasWritten) {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Reports",
|
||||
@"Failed to send crash report due to gdt write failure.");
|
||||
success = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Reports",
|
||||
@"Failed to send crash report due to gdt error: %@",
|
||||
error.localizedDescription);
|
||||
success = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Reports",
|
||||
@"Completed report submission with id: %@", path.lastPathComponent);
|
||||
|
||||
if (urgent) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}
|
||||
|
||||
[self cleanUpSubmittedReportAtPath:path];
|
||||
}];
|
||||
|
||||
if (urgent) {
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
} else if (!isNewPreparedPath && !self.dataSource.settings.shouldUseNewReportEndpoint) {
|
||||
return [self submitPackageMultipartMimeAtPath:path
|
||||
dataCollectionToken:dataCollectionToken
|
||||
synchronously:urgent];
|
||||
}
|
||||
|
||||
// Unsupported state
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)cleanUpSubmittedReportAtPath:(NSString *)path {
|
||||
if (![[self fileManager] removeItemAtPath:path]) {
|
||||
FIRCLSErrorLog(@"Unable to remove packaged submission");
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)reportUploadAtPath:(NSString *)path
|
||||
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
|
||||
completedWithError:(NSError *)error {
|
||||
FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"completed submission of %@", path);
|
||||
|
||||
if (!error) {
|
||||
[self cleanUpSubmittedReportAtPath:path];
|
||||
}
|
||||
|
||||
[[self delegate] didCompletePackageSubmission:path
|
||||
dataCollectionToken:dataCollectionToken
|
||||
error:error];
|
||||
}
|
||||
|
||||
#pragma mark - Properties (TODO: Can delete once the experiment is over)
|
||||
|
||||
- (NSURL *)reportURL {
|
||||
FIRCLSURLBuilder *url = [FIRCLSURLBuilder URLWithBase:FIRCLSReportsEndpoint];
|
||||
|
||||
[url appendComponent:@"/sdk-api/v1/platforms/"];
|
||||
[url appendComponent:FIRCLSApplicationGetPlatform()];
|
||||
[url appendComponent:@"/apps/"];
|
||||
[url appendComponent:self.dataSource.settings.fetchedBundleID];
|
||||
[url appendComponent:@"/reports"];
|
||||
|
||||
return [url URL];
|
||||
}
|
||||
|
||||
- (NSString *)localeIdentifier {
|
||||
return [[NSLocale currentLocale] localeIdentifier];
|
||||
}
|
||||
|
||||
#pragma mark - URL Requests
|
||||
- (NSMutableURLRequest *)mutableRequestWithURL:(NSURL *)url timeout:(NSTimeInterval)timeout {
|
||||
NSMutableURLRequest *request =
|
||||
[NSMutableURLRequest requestWithURL:url
|
||||
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval:timeout];
|
||||
|
||||
NSString *localeId = [self localeIdentifier];
|
||||
|
||||
[request setValue:@CLS_SDK_GENERATOR_NAME forHTTPHeaderField:FIRCLSNetworkUserAgent];
|
||||
[request setValue:FIRCLSNetworkApplicationJson forHTTPHeaderField:FIRCLSNetworkAccept];
|
||||
[request setValue:FIRCLSNetworkUTF8 forHTTPHeaderField:FIRCLSNetworkAcceptCharset];
|
||||
[request setValue:localeId forHTTPHeaderField:FIRCLSNetworkAcceptLanguage];
|
||||
[request setValue:localeId forHTTPHeaderField:FIRCLSNetworkContentLanguage];
|
||||
[request setValue:FIRCLSDeveloperToken forHTTPHeaderField:FIRCLSNetworkCrashlyticsDeveloperToken];
|
||||
[request setValue:FIRCLSApplicationGetSDKBundleID()
|
||||
forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientId];
|
||||
[request setValue:@CLS_SDK_DISPLAY_VERSION
|
||||
forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientDisplayVersion];
|
||||
[request setValue:[[self dataSource] googleAppID]
|
||||
forHTTPHeaderField:FIRCLSNetworkCrashlyticsGoogleAppId];
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
- (BOOL)fillInRequest:(NSMutableURLRequest *)request forMultipartMimeDataAtPath:(NSString *)path {
|
||||
NSString *boundary = [[path lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
[request setValue:[FIRCLSMultipartMimeStreamEncoder
|
||||
contentTypeHTTPHeaderValueWithBoundary:boundary]
|
||||
forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
NSNumber *fileSize = [[self fileManager] fileSizeAtPath:path];
|
||||
if (fileSize == nil) {
|
||||
FIRCLSErrorLog(@"Could not determine size of multipart mime file");
|
||||
return NO;
|
||||
}
|
||||
|
||||
[request setValue:[fileSize stringValue] forHTTPHeaderField:@"Content-Length"];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
23
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h
generated
Normal file
23
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h
generated
Normal file
|
@ -0,0 +1,23 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSReportUploader.h"
|
||||
|
||||
@interface FIRCLSReportUploader (PrivateMethods)
|
||||
|
||||
@property(nonatomic, readonly) NSURL *reportURL;
|
||||
|
||||
- (NSMutableURLRequest *)mutableRequestWithURL:(NSURL *)url timeout:(NSTimeInterval)timeout;
|
||||
|
||||
@end
|
39
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h
generated
Normal file
39
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h
generated
Normal file
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FIRApp;
|
||||
@class FBLPromise<T>;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FIRCLSDataCollectionArbiter : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict;
|
||||
|
||||
- (BOOL)isLegacyDataCollectionKeyInPlist;
|
||||
|
||||
- (BOOL)isCrashlyticsCollectionEnabled;
|
||||
|
||||
- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled;
|
||||
|
||||
// Returns a promise that is fulfilled once data collection is enabled.
|
||||
- (FBLPromise<NSNumber *> *)waitForCrashlyticsCollectionEnabled;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
148
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m
generated
Normal file
148
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m
generated
Normal file
|
@ -0,0 +1,148 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSDataCollectionArbiter.h"
|
||||
|
||||
#if __has_include(<FBLPromises/FBLPromises.h>)
|
||||
#import <FBLPromises/FBLPromises.h>
|
||||
#else
|
||||
#import "FBLPromises.h"
|
||||
#endif
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
|
||||
#import "FIRCLSUserDefaults.h"
|
||||
|
||||
// The legacy data collection setting allows Fabric customers to turn off auto-
|
||||
// initialization, but can be overridden by calling [Fabric with:].
|
||||
//
|
||||
// While we support Fabric, we must have two different versions, because
|
||||
// they require these slightly different semantics.
|
||||
NSString *const FIRCLSLegacyCrashlyticsCollectionKey = @"firebase_crashlytics_collection_enabled";
|
||||
|
||||
// The new data collection setting can be set by an API that is stored in FIRCLSUserDefaults
|
||||
NSString *const FIRCLSDataCollectionEnabledKey = @"com.crashlytics.data_collection";
|
||||
|
||||
// The new data collection setting also allows Firebase customers to turn off data
|
||||
// collection in their Info.plist, and can be overridden by setting it to true using
|
||||
// the setCrashlyticsCollectionEnabled API.
|
||||
NSString *const FIRCLSCrashlyticsCollectionKey = @"FirebaseCrashlyticsCollectionEnabled";
|
||||
|
||||
typedef NS_ENUM(NSInteger, FIRCLSDataCollectionSetting) {
|
||||
FIRCLSDataCollectionSettingNotSet = 0,
|
||||
FIRCLSDataCollectionSettingEnabled = 1,
|
||||
FIRCLSDataCollectionSettingDisabled = 2,
|
||||
};
|
||||
|
||||
@interface FIRCLSDataCollectionArbiter () {
|
||||
NSLock *_mutex;
|
||||
FBLPromise *_dataCollectionEnabled;
|
||||
BOOL _promiseResolved;
|
||||
FIRApp *_app;
|
||||
NSDictionary *_appInfo;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FIRCLSDataCollectionArbiter
|
||||
|
||||
- (instancetype)initWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_mutex = [[NSLock alloc] init];
|
||||
_appInfo = dict;
|
||||
_app = app;
|
||||
if ([FIRCLSDataCollectionArbiter isCrashlyticsCollectionEnabledWithApp:app withAppInfo:dict]) {
|
||||
_dataCollectionEnabled = [FBLPromise resolvedWith:nil];
|
||||
_promiseResolved = YES;
|
||||
} else {
|
||||
_dataCollectionEnabled = [FBLPromise pendingPromise];
|
||||
_promiseResolved = NO;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* Legacy collection key that we provide for customers to disable Crash reporting.
|
||||
* Customers can later turn on Crashlytics using Fabric.with if they choose to do so.
|
||||
*
|
||||
* This flag is unsupported for the "New SDK"
|
||||
*/
|
||||
- (BOOL)isLegacyDataCollectionKeyInPlist {
|
||||
if ([_appInfo objectForKey:FIRCLSLegacyCrashlyticsCollectionKey]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This functionality is called in the initializer before self is fully initialized,
|
||||
// so a class method is used. The instance method below allows for a consistent clean API.
|
||||
+ (BOOL)isCrashlyticsCollectionEnabledWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict {
|
||||
FIRCLSDataCollectionSetting stickySetting = [FIRCLSDataCollectionArbiter stickySetting];
|
||||
if (stickySetting != FIRCLSDataCollectionSettingNotSet) {
|
||||
return stickySetting == FIRCLSDataCollectionSettingEnabled;
|
||||
}
|
||||
|
||||
id firebaseCrashlyticsCollectionEnabled = [dict objectForKey:FIRCLSCrashlyticsCollectionKey];
|
||||
if ([firebaseCrashlyticsCollectionEnabled isKindOfClass:[NSString class]] ||
|
||||
[firebaseCrashlyticsCollectionEnabled isKindOfClass:[NSNumber class]]) {
|
||||
return [firebaseCrashlyticsCollectionEnabled boolValue];
|
||||
}
|
||||
|
||||
return [app isDataCollectionDefaultEnabled];
|
||||
}
|
||||
|
||||
- (BOOL)isCrashlyticsCollectionEnabled {
|
||||
return [FIRCLSDataCollectionArbiter isCrashlyticsCollectionEnabledWithApp:_app
|
||||
withAppInfo:_appInfo];
|
||||
}
|
||||
|
||||
- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled {
|
||||
FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults];
|
||||
FIRCLSDataCollectionSetting setting =
|
||||
enabled ? FIRCLSDataCollectionSettingEnabled : FIRCLSDataCollectionSettingDisabled;
|
||||
[userDefaults setInteger:setting forKey:FIRCLSDataCollectionEnabledKey];
|
||||
[userDefaults synchronize];
|
||||
|
||||
[_mutex lock];
|
||||
if (enabled) {
|
||||
if (!_promiseResolved) {
|
||||
[_dataCollectionEnabled fulfill:nil];
|
||||
_promiseResolved = YES;
|
||||
}
|
||||
} else {
|
||||
if (_promiseResolved) {
|
||||
_dataCollectionEnabled = [FBLPromise pendingPromise];
|
||||
_promiseResolved = NO;
|
||||
}
|
||||
}
|
||||
[_mutex unlock];
|
||||
}
|
||||
|
||||
+ (FIRCLSDataCollectionSetting)stickySetting {
|
||||
FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults];
|
||||
return [userDefaults integerForKey:FIRCLSDataCollectionEnabledKey];
|
||||
}
|
||||
|
||||
- (FBLPromise *)waitForCrashlyticsCollectionEnabled {
|
||||
FBLPromise *result = nil;
|
||||
[_mutex lock];
|
||||
result = _dataCollectionEnabled;
|
||||
[_mutex unlock];
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
45
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h
generated
Normal file
45
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h
generated
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* A FIRCLSDataCollectionToken represents having permission to upload data. A data collection token
|
||||
* is either valid or nil. Every function that directly initiates a network operation that will
|
||||
* result in data collection must check to make sure it has been passed a valid token. Tokens should
|
||||
* only be created when either (1) automatic data collection is enabled, or (2) the user has
|
||||
* explicitly given permission to collect data for a particular purpose, using the API. For all the
|
||||
* functions in between, the data collection token getting passed as an argument helps to document
|
||||
* and enforce the flow of data collection permission through the SDK.
|
||||
*/
|
||||
@interface FIRCLSDataCollectionToken : NSObject
|
||||
|
||||
/**
|
||||
* Creates a valid token. Only call this method when either (1) automatic data collection is
|
||||
* enabled, or (2) the user has explicitly given permission to collect data for a particular
|
||||
* purpose, using the API.
|
||||
*/
|
||||
+ (instancetype)validToken;
|
||||
|
||||
/**
|
||||
* Use this to verify that a token is valid. If this is called on a nil instance, it will return NO.
|
||||
* @return YES.
|
||||
*/
|
||||
- (BOOL)isValid;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
27
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m
generated
Normal file
27
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m
generated
Normal file
|
@ -0,0 +1,27 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSDataCollectionToken.h"
|
||||
|
||||
@implementation FIRCLSDataCollectionToken
|
||||
|
||||
+ (instancetype)validToken {
|
||||
return [[FIRCLSDataCollectionToken alloc] init];
|
||||
}
|
||||
|
||||
- (BOOL)isValid {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
84
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h
generated
Normal file
84
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h
generated
Normal file
|
@ -0,0 +1,84 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import "FIRCLSURLSessionConfiguration.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FIRCLSURLSession : NSObject {
|
||||
id<NSURLSessionDelegate> _delegate;
|
||||
NSOperationQueue *_delegateQueue;
|
||||
NSURLSessionConfiguration *_configuration;
|
||||
NSMutableSet *_taskSet;
|
||||
dispatch_queue_t _queue;
|
||||
|
||||
NSString *_sessionDescription;
|
||||
}
|
||||
|
||||
+ (BOOL)NSURLSessionShouldBeUsed;
|
||||
|
||||
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
|
||||
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
|
||||
delegate:(nullable id<NSURLSessionDelegate>)delegate
|
||||
delegateQueue:(nullable NSOperationQueue *)queue;
|
||||
|
||||
@property(nonatomic, readonly, retain) NSOperationQueue *delegateQueue;
|
||||
@property(nonatomic, readonly, retain) id<NSURLSessionDelegate> delegate;
|
||||
@property(nonatomic, readonly, copy) NSURLSessionConfiguration *configuration;
|
||||
|
||||
@property(nonatomic, copy) NSString *sessionDescription;
|
||||
|
||||
- (void)getTasksWithCompletionHandler:
|
||||
(void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler;
|
||||
|
||||
// task creation - suitable for background operations
|
||||
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
|
||||
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
|
||||
|
||||
// convenience methods (that are not available for background sessions
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
|
||||
completionHandler:(nullable void (^)(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error))completionHandler;
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
||||
completionHandler:
|
||||
(nullable void (^)(NSURL *targetPath,
|
||||
NSURLResponse *response,
|
||||
NSError *error))completionHandler;
|
||||
|
||||
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
|
||||
fromFile:(NSURL *)fileURL
|
||||
completionHandler:
|
||||
(nullable void (^)(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error))completionHandler;
|
||||
|
||||
- (void)invalidateAndCancel;
|
||||
- (void)finishTasksAndInvalidate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif
|
346
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m
generated
Normal file
346
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m
generated
Normal file
|
@ -0,0 +1,346 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
#import "FIRCLSURLSession.h"
|
||||
|
||||
#import "FIRCLSURLSessionDataTask.h"
|
||||
#import "FIRCLSURLSessionDataTask_PrivateMethods.h"
|
||||
#import "FIRCLSURLSessionDownloadTask.h"
|
||||
#import "FIRCLSURLSessionDownloadTask_PrivateMethods.h"
|
||||
#import "FIRCLSURLSessionTask_PrivateMethods.h"
|
||||
#import "FIRCLSURLSessionUploadTask.h"
|
||||
|
||||
#define DELEGATE ((id<NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>)self->_delegate)
|
||||
|
||||
@interface FIRCLSURLSession () <FIRCLSURLSessionDownloadDelegate>
|
||||
|
||||
@property(nonatomic, retain) NSOperationQueue *delegateQueue;
|
||||
@property(nonatomic, retain) id<NSURLSessionDelegate> delegate;
|
||||
@property(nonatomic, copy) NSURLSessionConfiguration *configuration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRCLSURLSession
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
@synthesize delegateQueue = _delegateQueue;
|
||||
@synthesize configuration = _configuration;
|
||||
@synthesize sessionDescription = _sessionDescription;
|
||||
|
||||
+ (BOOL)NSURLSessionShouldBeUsed {
|
||||
if (!NSClassFromString(@"NSURLSession")) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// We use this as a proxy to verify that we are on at least iOS 8 or 10.10. The first OSes that
|
||||
// has NSURLSession were fairly unstable.
|
||||
return [[NSURLSessionConfiguration class]
|
||||
respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)];
|
||||
}
|
||||
|
||||
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration {
|
||||
return [self sessionWithConfiguration:configuration delegate:nil delegateQueue:nil];
|
||||
}
|
||||
|
||||
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
|
||||
delegate:(nullable id<NSURLSessionDelegate>)delegate
|
||||
delegateQueue:(nullable NSOperationQueue *)queue {
|
||||
if ([self NSURLSessionShouldBeUsed]) {
|
||||
return [NSURLSession sessionWithConfiguration:configuration
|
||||
delegate:delegate
|
||||
delegateQueue:queue];
|
||||
}
|
||||
|
||||
if (!configuration) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
FIRCLSURLSession *session = [self new];
|
||||
#else
|
||||
FIRCLSURLSession *session = [[self new] autorelease];
|
||||
#endif
|
||||
[session setDelegate:delegate];
|
||||
// When delegate exists, but delegateQueue is nil, create a serial queue like NSURLSession
|
||||
// documents.
|
||||
if (delegate && !queue) {
|
||||
queue = [self newDefaultDelegateQueue];
|
||||
}
|
||||
session.delegateQueue = queue;
|
||||
session.configuration = configuration;
|
||||
return (NSURLSession *)session;
|
||||
}
|
||||
|
||||
+ (NSOperationQueue *)newDefaultDelegateQueue {
|
||||
NSOperationQueue *delegateQueue = [[NSOperationQueue alloc] init];
|
||||
delegateQueue.name = [NSString stringWithFormat:@"%@ %p", NSStringFromClass(self), self];
|
||||
delegateQueue.maxConcurrentOperationCount = 1;
|
||||
return delegateQueue;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_queue = dispatch_queue_create("com.crashlytics.URLSession", 0);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
- (void)dealloc {
|
||||
[_taskSet release];
|
||||
[_delegate release];
|
||||
[_delegateQueue release];
|
||||
[_configuration release];
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(_queue);
|
||||
#endif
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Managing the Session
|
||||
|
||||
- (void)invalidateAndCancel {
|
||||
dispatch_sync(_queue, ^{
|
||||
for (FIRCLSURLSessionTask *task in self->_taskSet) {
|
||||
[task cancel];
|
||||
}
|
||||
});
|
||||
|
||||
self.delegate = nil;
|
||||
}
|
||||
|
||||
- (void)finishTasksAndInvalidate {
|
||||
self.delegate = nil;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)getTasksWithCompletionHandler:
|
||||
(void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler {
|
||||
[[self delegateQueue] addOperationWithBlock:^{
|
||||
// TODO - this is totally wrong, but better than not calling back at all
|
||||
completionHandler(@[], @[], @[]);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)removeTaskFromSet:(FIRCLSURLSessionTask *)task {
|
||||
dispatch_async(_queue, ^{
|
||||
[self->_taskSet removeObject:task];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)configureTask:(FIRCLSURLSessionTask *)task
|
||||
withRequest:(NSURLRequest *)request
|
||||
block:(void (^)(NSMutableURLRequest *mutableRequest))block {
|
||||
NSMutableURLRequest *modifiedRequest = [request mutableCopy];
|
||||
|
||||
dispatch_sync(_queue, ^{
|
||||
[self->_taskSet addObject:task];
|
||||
|
||||
// TODO: this isn't allowed to overwrite existing headers
|
||||
for (NSString *key in [self->_configuration HTTPAdditionalHeaders]) {
|
||||
[modifiedRequest addValue:[[self->_configuration HTTPAdditionalHeaders] objectForKey:key]
|
||||
forHTTPHeaderField:key];
|
||||
}
|
||||
});
|
||||
|
||||
if (block) {
|
||||
block(modifiedRequest);
|
||||
}
|
||||
|
||||
[task setOriginalRequest:modifiedRequest];
|
||||
[task setDelegate:self];
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
[modifiedRequest release];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)shouldInvokeDelegateSelector:(SEL)selector forTask:(FIRCLSURLSessionTask *)task {
|
||||
return [task invokesDelegate] && [_delegate respondsToSelector:selector];
|
||||
}
|
||||
|
||||
#pragma mark Task Creation
|
||||
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
|
||||
fromFile:(NSURL *)fileURL {
|
||||
return [self uploadTaskWithRequest:request fromFile:fileURL completionHandler:nil];
|
||||
}
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request {
|
||||
return [self downloadTaskWithRequest:request completionHandler:nil];
|
||||
}
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url {
|
||||
return [self downloadTaskWithRequest:[NSURLRequest requestWithURL:url]];
|
||||
}
|
||||
|
||||
#pragma mark Async Convenience Methods
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
|
||||
completionHandler:(nullable void (^)(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error))completionHandler {
|
||||
FIRCLSURLSessionDataTask *task = [FIRCLSURLSessionDataTask task];
|
||||
|
||||
if (completionHandler) {
|
||||
[task setCompletionHandler:completionHandler];
|
||||
[task setInvokesDelegate:NO];
|
||||
}
|
||||
|
||||
[self configureTask:task withRequest:request block:nil];
|
||||
|
||||
return (NSURLSessionDataTask *)task;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request {
|
||||
return [self dataTaskWithRequest:request completionHandler:nil];
|
||||
}
|
||||
|
||||
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
|
||||
fromFile:(NSURL *)fileURL
|
||||
completionHandler:
|
||||
(nullable void (^)(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error))completionHandler {
|
||||
FIRCLSURLSessionUploadTask *task = [FIRCLSURLSessionUploadTask task];
|
||||
|
||||
if (completionHandler) {
|
||||
[task setCompletionHandler:completionHandler];
|
||||
[task setInvokesDelegate:NO];
|
||||
}
|
||||
|
||||
[self configureTask:task
|
||||
withRequest:request
|
||||
block:^(NSMutableURLRequest *mutableRequest) {
|
||||
// you cannot set up both of these, and we'll be using the stream here
|
||||
[mutableRequest setHTTPBody:nil];
|
||||
[mutableRequest setHTTPBodyStream:[NSInputStream inputStreamWithURL:fileURL]];
|
||||
}];
|
||||
|
||||
return (NSURLSessionUploadTask *)task;
|
||||
}
|
||||
|
||||
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
|
||||
completionHandler:
|
||||
(nullable void (^)(NSURL *targetPath,
|
||||
NSURLResponse *response,
|
||||
NSError *error))completionHandler {
|
||||
FIRCLSURLSessionDownloadTask *task = [FIRCLSURLSessionDownloadTask task];
|
||||
|
||||
if (completionHandler) {
|
||||
[task setDownloadCompletionHandler:completionHandler];
|
||||
[task setInvokesDelegate:NO];
|
||||
}
|
||||
|
||||
[self configureTask:task withRequest:request block:nil];
|
||||
|
||||
return (NSURLSessionDownloadTask *)task;
|
||||
}
|
||||
|
||||
#pragma mark FIRCLSURLSessionTaskDelegate
|
||||
- (NSURLRequest *)task:(FIRCLSURLSessionTask *)task
|
||||
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request {
|
||||
// just accept the proposed redirection
|
||||
return request;
|
||||
}
|
||||
|
||||
- (void)task:(FIRCLSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
if (![self shouldInvokeDelegateSelector:@selector(URLSession:task:didCompleteWithError:)
|
||||
forTask:task]) {
|
||||
[self removeTaskFromSet:task];
|
||||
return;
|
||||
}
|
||||
|
||||
[_delegateQueue addOperationWithBlock:^{
|
||||
[DELEGATE URLSession:(NSURLSession *)self
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:error];
|
||||
|
||||
// Note that you *cannot* clean up here, because this method could be run asynchronously with
|
||||
// the delegate methods that care about the state of the task
|
||||
[self removeTaskFromSet:task];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark FIRCLSURLSessionDataTask
|
||||
- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response {
|
||||
if (![self shouldInvokeDelegateSelector:@selector
|
||||
(URLSession:dataTask:didReceiveResponse:completionHandler:)
|
||||
forTask:task]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_delegateQueue addOperationWithBlock:^{
|
||||
[DELEGATE URLSession:(NSURLSession *)self
|
||||
dataTask:(NSURLSessionDataTask *)task
|
||||
didReceiveResponse:response
|
||||
completionHandler:^(NSURLSessionResponseDisposition disposition){
|
||||
// nothing to do here
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveData:(NSData *)data {
|
||||
if (![self shouldInvokeDelegateSelector:@selector(URLSession:dataTask:didReceiveData:)
|
||||
forTask:task]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_delegateQueue addOperationWithBlock:^{
|
||||
[DELEGATE URLSession:(NSURLSession *)self
|
||||
dataTask:(NSURLSessionDataTask *)task
|
||||
didReceiveData:data];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark FIRCLSURLSessionDownloadDelegate
|
||||
- (void)downloadTask:(FIRCLSURLSessionDownloadTask *)task didFinishDownloadingToURL:(NSURL *)url {
|
||||
if (![self shouldInvokeDelegateSelector:@selector(URLSession:
|
||||
downloadTask:didFinishDownloadingToURL:)
|
||||
forTask:task]) {
|
||||
// We have to be certain that we cleanup only once the delegate no longer cares about the state
|
||||
// of the task being changed. In the case of download, this is either after the delegate method
|
||||
// has been invoked, or here, if the delegate doesn't care.
|
||||
[task cleanup];
|
||||
return;
|
||||
}
|
||||
|
||||
[_delegateQueue addOperationWithBlock:^{
|
||||
[DELEGATE URLSession:(NSURLSession *)self
|
||||
downloadTask:(NSURLSessionDownloadTask *)task
|
||||
didFinishDownloadingToURL:url];
|
||||
|
||||
// Cleanup for the download tasks is a little complex. As long as we do it only after
|
||||
// the delegate has been informed of the completed download, we are ok.
|
||||
[task cleanup];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
INJECT_STRIP_SYMBOL(clsurlsession)
|
||||
|
||||
#endif
|
28
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h
generated
Normal file
28
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h
generated
Normal file
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#define FIRCLSURLSESSION_REQUIRED (!TARGET_OS_WATCH && !TARGET_OS_TV)
|
||||
|
||||
// These macros generate a function to force a symbol for the containing .o, to work around an issue
|
||||
// where strip will not strip debug information without a symbol to strip.
|
||||
#define CONCAT_EXPANDED(a, b) a##b
|
||||
#define CONCAT(a, b) CONCAT_EXPANDED(a, b)
|
||||
#define DUMMY_FUNCTION_NAME(x) CONCAT(fircls_strip_this_, x)
|
||||
#define INJECT_STRIP_SYMBOL(x) \
|
||||
void DUMMY_FUNCTION_NAME(x)(void) { \
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FIRCLSURLSessionConfiguration : NSObject <NSCopying> {
|
||||
NSDictionary *_additionalHeaders;
|
||||
NSURLCache *_URLCache;
|
||||
NSHTTPCookieAcceptPolicy _cookiePolicy;
|
||||
}
|
||||
|
||||
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
|
||||
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
|
||||
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
|
||||
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;
|
||||
|
||||
@property(nonatomic, copy) NSDictionary *HTTPAdditionalHeaders;
|
||||
@property(nonatomic, retain) NSURLCache *URLCache;
|
||||
@property(nonatomic, assign) NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;
|
||||
@property(nonatomic, assign) BOOL sessionSendsLaunchEvents;
|
||||
@property(nonatomic, assign) NSTimeInterval timeoutIntervalForRequest;
|
||||
@property(nonatomic, assign) NSTimeInterval timeoutIntervalForResource;
|
||||
@property(nonatomic, assign) BOOL allowsCellularAccess;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -0,0 +1,92 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#import "FIRCLSURLSession.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
#import "FIRCLSURLSessionConfiguration.h"
|
||||
|
||||
@implementation FIRCLSURLSessionConfiguration
|
||||
|
||||
@synthesize URLCache = _URLCache;
|
||||
@synthesize HTTPAdditionalHeaders = _additionalHeaders;
|
||||
@synthesize HTTPCookieAcceptPolicy = _cookiePolicy;
|
||||
|
||||
+ (NSURLSessionConfiguration *)defaultSessionConfiguration {
|
||||
if ([FIRCLSURLSession NSURLSessionShouldBeUsed]) {
|
||||
return [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
}
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
return [self new];
|
||||
#else
|
||||
return [[self new] autorelease];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration {
|
||||
if ([FIRCLSURLSession NSURLSessionShouldBeUsed]) {
|
||||
return [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
}
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
return [self new];
|
||||
#else
|
||||
return [[self new] autorelease];
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier {
|
||||
return [self backgroundSessionConfigurationWithIdentifier:identifier];
|
||||
}
|
||||
|
||||
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier {
|
||||
if (![FIRCLSURLSession NSURLSessionShouldBeUsed]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([[NSURLSessionConfiguration class]
|
||||
respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) {
|
||||
return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
|
||||
}
|
||||
|
||||
return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
FIRCLSURLSessionConfiguration *configuration;
|
||||
|
||||
configuration = [FIRCLSURLSessionConfiguration new];
|
||||
[configuration setHTTPAdditionalHeaders:[self HTTPAdditionalHeaders]];
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
// This functionality is not supported by the wrapper, so we just stub it out
|
||||
- (BOOL)sessionSendsLaunchEvents {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)setSessionSendsLaunchEvents:(BOOL)sessionSendsLaunchEvents {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
INJECT_STRIP_SYMBOL(clsurlsessionconfiguration)
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Google LLC
|
||||
// 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.
|
||||
|
@ -12,11 +12,16 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An umbrella header, for any other libraries in this repo to access Firebase Public and Private
|
||||
// headers. Any package manager complexity should be handled here.
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FIRCLSURLSession (PrivateMethods)
|
||||
|
||||
- (void)runOnDelegateQueue:(void (^)(void))block;
|
||||
|
||||
@end
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
@import GoogleDataTransport;
|
||||
#else
|
||||
#import <GoogleDataTransport/GoogleDataTransport.h>
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import "FIRCLSURLSessionTask.h"
|
||||
|
||||
@interface FIRCLSURLSessionDataTask : FIRCLSURLSessionTask {
|
||||
void (^_completionHandler)(NSData *data, NSURLResponse *response, NSError *error);
|
||||
NSURLConnection *_connection;
|
||||
NSMutableData *_data;
|
||||
NSString *_taskDescription;
|
||||
}
|
||||
|
||||
@property(nonatomic, copy) NSString *taskDescription;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
124
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m
generated
Normal file
124
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m
generated
Normal file
|
@ -0,0 +1,124 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
#import "FIRCLSURLSessionDataTask.h"
|
||||
|
||||
#import "FIRCLSURLSessionDataTask_PrivateMethods.h"
|
||||
|
||||
#define DELEGATE ((id<FIRCLSURLSessionDataDelegate>)[self delegate])
|
||||
|
||||
@interface FIRCLSURLSessionDataTask () <NSURLConnectionDelegate>
|
||||
@end
|
||||
|
||||
@implementation FIRCLSURLSessionDataTask
|
||||
|
||||
@synthesize connection = _connection;
|
||||
@synthesize completionHandler = _completionHandler;
|
||||
@synthesize taskDescription = _taskDescription;
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
- (void)dealloc {
|
||||
[_connection release];
|
||||
[_completionHandler release];
|
||||
[_taskDescription release];
|
||||
[_data release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)resume {
|
||||
dispatch_async([self queue], ^{
|
||||
NSURLConnection *connection;
|
||||
|
||||
if ([self connection]) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
connection = [[NSURLConnection alloc] initWithRequest:[self originalRequest]
|
||||
delegate:self
|
||||
startImmediately:NO];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
[self setConnection:connection];
|
||||
|
||||
// bummer we have to do this on a runloop, but other mechanisms require iOS 5 or 10.7
|
||||
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
#if !__has_feature(objc_arc)
|
||||
[connection release];
|
||||
#endif
|
||||
[connection start];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)complete {
|
||||
// call completion handler first
|
||||
if (_completionHandler) {
|
||||
// this should go to another queue
|
||||
_completionHandler(_data, [self response], [self error]);
|
||||
}
|
||||
|
||||
// and then finally, call the session delegate completion
|
||||
[DELEGATE task:self didCompleteWithError:[self error]];
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
[self.connection cancel];
|
||||
}
|
||||
|
||||
#pragma mark NSURLConnectionDelegate
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
dispatch_async([self queue], ^{
|
||||
[DELEGATE task:self didReceiveResponse:response];
|
||||
|
||||
[self setResponse:response];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
dispatch_async([self queue], ^{
|
||||
if (!self->_data) {
|
||||
self->_data = [NSMutableData new];
|
||||
}
|
||||
|
||||
[self->_data appendData:data];
|
||||
[DELEGATE task:self didReceiveData:data];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
dispatch_async([self queue], ^{
|
||||
[self setError:error];
|
||||
[self complete];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
dispatch_async([self queue], ^{
|
||||
[self complete];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
INJECT_STRIP_SYMBOL(clsurlsessiondatatask)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "FIRCLSURLSessionTask_PrivateMethods.h"
|
||||
|
||||
@protocol FIRCLSURLSessionDataDelegate;
|
||||
|
||||
@interface FIRCLSURLSessionDataTask ()
|
||||
|
||||
@property(nonatomic, retain) NSURLConnection *connection;
|
||||
@property(nonatomic, copy) void (^completionHandler)
|
||||
(NSData *data, NSURLResponse *response, NSError *error);
|
||||
|
||||
- (void)complete;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FIRCLSURLSessionDataDelegate <FIRCLSURLSessionTaskDelegate>
|
||||
@required
|
||||
|
||||
- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response;
|
||||
- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveData:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import "FIRCLSURLSessionDataTask.h"
|
||||
|
||||
@protocol FIRCLSURLSessionDownloadDelegate;
|
||||
|
||||
@interface FIRCLSURLSessionDownloadTask : FIRCLSURLSessionDataTask {
|
||||
void (^_downloadCompletionHandler)(NSURL *targetPath, NSURLResponse *response, NSError *error);
|
||||
NSOutputStream *_outputStream;
|
||||
NSURL *_targetURL;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -0,0 +1,157 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
#import "FIRCLSURLSessionDownloadTask.h"
|
||||
|
||||
#import "FIRCLSURLSessionDownloadTask_PrivateMethods.h"
|
||||
|
||||
#define DELEGATE ((id<FIRCLSURLSessionDownloadDelegate>)[self delegate])
|
||||
|
||||
@interface FIRCLSURLSessionDownloadTask () <NSStreamDelegate>
|
||||
@end
|
||||
|
||||
@implementation FIRCLSURLSessionDownloadTask
|
||||
|
||||
@synthesize downloadCompletionHandler = _downloadCompletionHandler;
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
_targetURL = [self temporaryFileURL];
|
||||
_outputStream = [NSOutputStream outputStreamWithURL:_targetURL append:NO];
|
||||
#else
|
||||
_targetURL = [[self temporaryFileURL] retain];
|
||||
_outputStream = [[NSOutputStream outputStreamWithURL:_targetURL append:NO] retain];
|
||||
#endif
|
||||
|
||||
[_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
[_outputStream setDelegate:self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
- (void)dealloc {
|
||||
[_downloadCompletionHandler release];
|
||||
[_targetURL release];
|
||||
[_outputStream release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
#else
|
||||
- (void)dealloc {
|
||||
[_outputStream close];
|
||||
_outputStream.delegate = nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (NSURL *)temporaryFileURL {
|
||||
NSString *tmpPath;
|
||||
|
||||
tmpPath = [NSTemporaryDirectory()
|
||||
stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
|
||||
// TODO: make this actually unique
|
||||
return [NSURL fileURLWithPath:tmpPath isDirectory:NO];
|
||||
}
|
||||
|
||||
- (void)cleanup {
|
||||
// now, remove the temporary file
|
||||
[[NSFileManager defaultManager] removeItemAtURL:_targetURL error:nil];
|
||||
}
|
||||
|
||||
- (void)complete {
|
||||
// This is an override of FIRCLSURLSessionDataTask's cleanup method
|
||||
|
||||
// call completion handler first
|
||||
if (_downloadCompletionHandler) {
|
||||
_downloadCompletionHandler(_targetURL, [self response], [self error]);
|
||||
}
|
||||
|
||||
// followed by the session delegate, if there was no error
|
||||
if (![self error]) {
|
||||
[DELEGATE downloadTask:self didFinishDownloadingToURL:_targetURL];
|
||||
}
|
||||
|
||||
// and then finally, call the session delegate completion
|
||||
[DELEGATE task:self didCompleteWithError:[self error]];
|
||||
}
|
||||
|
||||
- (void)writeDataToStream:(NSData *)data {
|
||||
// open the stream first
|
||||
if ([_outputStream streamStatus] == NSStreamStatusNotOpen) {
|
||||
[_outputStream open];
|
||||
}
|
||||
|
||||
if ([data respondsToSelector:@selector(enumerateByteRangesUsingBlock:)]) {
|
||||
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
|
||||
[self->_outputStream write:bytes maxLength:byteRange.length];
|
||||
}];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// fall back to the less-efficient mechanism for older OSes
|
||||
[_outputStream write:[data bytes] maxLength:[data length]];
|
||||
}
|
||||
|
||||
#pragma mark NSURLConnectionDelegate
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
dispatch_async([self queue], ^{
|
||||
[self writeDataToStream:data];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)completeForError {
|
||||
dispatch_async([self queue], ^{
|
||||
[self->_outputStream close];
|
||||
[self->_connection cancel];
|
||||
if (![self error]) {
|
||||
[self setError:[NSError errorWithDomain:@"FIRCLSURLSessionDownloadTaskError"
|
||||
code:-1
|
||||
userInfo:nil]];
|
||||
}
|
||||
[self complete];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark NSStreamDelegate
|
||||
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
|
||||
switch (eventCode) {
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
break;
|
||||
case NSStreamEventErrorOccurred:
|
||||
[self completeForError];
|
||||
break;
|
||||
case NSStreamEventEndEncountered:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
INJECT_STRIP_SYMBOL(clsurlsessiondownloadtask)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "FIRCLSURLSessionDataTask_PrivateMethods.h"
|
||||
|
||||
@protocol FIRCLSURLSessionDownloadDelegate;
|
||||
|
||||
@interface FIRCLSURLSessionDownloadTask ()
|
||||
|
||||
@property(nonatomic, copy) void (^downloadCompletionHandler)
|
||||
(NSURL *targetPath, NSURLResponse *response, NSError *error);
|
||||
|
||||
@end
|
||||
|
||||
@protocol FIRCLSURLSessionDownloadDelegate <FIRCLSURLSessionDataDelegate>
|
||||
@required
|
||||
|
||||
- (void)downloadTask:(FIRCLSURLSessionDownloadTask *)task didFinishDownloadingToURL:(NSURL *)url;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
38
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h
generated
Normal file
38
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h
generated
Normal file
|
@ -0,0 +1,38 @@
|
|||
// 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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FIRCLSURLSessionTaskDelegate;
|
||||
|
||||
@interface FIRCLSURLSessionTask : NSObject {
|
||||
__unsafe_unretained id<FIRCLSURLSessionTaskDelegate> _delegate;
|
||||
|
||||
NSURLRequest* _originalRequest;
|
||||
NSURLRequest* _currentRequest;
|
||||
NSURLResponse* _response;
|
||||
NSError* _error;
|
||||
dispatch_queue_t _queue;
|
||||
BOOL _invokesDelegate;
|
||||
}
|
||||
|
||||
@property(nonatomic, readonly, copy) NSURLRequest* originalRequest;
|
||||
@property(nonatomic, readonly, copy) NSURLRequest* currentRequest;
|
||||
@property(nonatomic, readonly, copy) NSURLResponse* response;
|
||||
|
||||
@property(nonatomic, readonly, copy) NSError* error;
|
||||
|
||||
- (void)resume;
|
||||
|
||||
@end
|
95
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m
generated
Normal file
95
ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m
generated
Normal file
|
@ -0,0 +1,95 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
#import "FIRCLSURLSession.h"
|
||||
|
||||
#import "FIRCLSURLSessionTask.h"
|
||||
|
||||
#import "FIRCLSURLSessionTask_PrivateMethods.h"
|
||||
#import "FIRCLSURLSession_PrivateMethods.h"
|
||||
|
||||
@implementation FIRCLSURLSessionTask
|
||||
|
||||
+ (instancetype)task {
|
||||
#if __has_feature(objc_arc)
|
||||
return [[self class] new];
|
||||
|
||||
#else
|
||||
return [[[self class] new] autorelease];
|
||||
#endif
|
||||
}
|
||||
|
||||
@synthesize currentRequest = _currentRequest;
|
||||
@synthesize originalRequest = _originalRequest;
|
||||
@synthesize response = _response;
|
||||
@synthesize error = _error;
|
||||
@synthesize queue = _queue;
|
||||
@synthesize invokesDelegate = _invokesDelegate;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_queue = dispatch_queue_create("com.crashlytics.URLSessionTask", 0);
|
||||
|
||||
_invokesDelegate = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
- (void)dealloc {
|
||||
[_originalRequest release];
|
||||
[_currentRequest release];
|
||||
[_response release];
|
||||
[_error release];
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(_queue);
|
||||
#endif
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)start {
|
||||
#if DEBUG
|
||||
assert(0 && "Must be implemented by FIRCLSURLSessionTask subclasses");
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
#if DEBUG
|
||||
assert(0 && "Must be implemented by FIRCLSURLSessionTask subclasses");
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)resume {
|
||||
}
|
||||
|
||||
- (void)cleanup {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
INJECT_STRIP_SYMBOL(clsurlsessiontask)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FIRCLSURLSessionTaskDelegate;
|
||||
|
||||
@interface FIRCLSURLSessionTask ()
|
||||
|
||||
+ (instancetype)task;
|
||||
|
||||
@property(nonatomic, assign) id<FIRCLSURLSessionTaskDelegate> delegate;
|
||||
|
||||
@property(nonatomic, copy) NSURLRequest *originalRequest;
|
||||
@property(nonatomic, copy) NSURLRequest *currentRequest;
|
||||
@property(nonatomic, copy) NSURLResponse *response;
|
||||
|
||||
@property(nonatomic, readonly) dispatch_queue_t queue;
|
||||
@property(nonatomic, assign) BOOL invokesDelegate;
|
||||
|
||||
- (void)cancel;
|
||||
|
||||
@property(nonatomic, copy) NSError *error;
|
||||
|
||||
- (void)cleanup;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FIRCLSURLSessionTaskDelegate <NSObject>
|
||||
@required
|
||||
|
||||
- (NSURLRequest *)task:(FIRCLSURLSessionTask *)task
|
||||
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request;
|
||||
|
||||
- (void)task:(FIRCLSURLSessionTask *)task didCompleteWithError:(NSError *)error;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
// 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.
|
||||
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
|
||||
#import "FIRCLSURLSessionDataTask.h"
|
||||
|
||||
@interface FIRCLSURLSessionUploadTask : FIRCLSURLSessionDataTask
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 Google LLC
|
||||
// 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.
|
||||
|
@ -12,11 +12,17 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// An umbrella header, for any other libraries in this repo to access Firebase Public and Private
|
||||
// headers. Any package manager complexity should be handled here.
|
||||
#import "FIRCLSURLSessionAvailability.h"
|
||||
|
||||
#if FIRCLSURLSESSION_REQUIRED
|
||||
#import "FIRCLSURLSessionUploadTask.h"
|
||||
|
||||
@implementation FIRCLSURLSessionUploadTask
|
||||
|
||||
@end
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
@import GoogleDataTransport;
|
||||
#else
|
||||
#import <GoogleDataTransport/GoogleDataTransport.h>
|
||||
|
||||
INJECT_STRIP_SYMBOL(clsurlsessionuploadtask)
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue