Add -[FBCrashLogCommands pruneCrashes:]
Summary: Instead of exposing the details of the Crash Log Store, it makes some sense to be able to control the availability of crash logs for a given target. For Simulators this now means we have to specialize since we need to have knowledge of the UDID being present in (some) logs but not others. For Devices we need to be able to prune crashes by talking to the device Reviewed By: zeyadsalloum Differential Revision: D8595775 fbshipit-source-id: 5fc559191d881c591842c425e1dd026f51f78cbd
This commit is contained in:
Родитель
37cfbd0b90
Коммит
de2db61465
|
@ -16,6 +16,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FBCrashLogInfo;
|
||||
@class FBCrashLogStore;
|
||||
|
||||
/**
|
||||
Commands for obtaining crash logs.
|
||||
|
@ -26,9 +27,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
Obtains all of the crash logs matching a given predicate.
|
||||
|
||||
@param predicate the predicate to match against.
|
||||
@param useCache YES to use the cached crash logs, NO to re-fetch. Pass YES when significant events have happened.
|
||||
@return a Future that resolves with crash logs.
|
||||
*/
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate;
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate useCache:(BOOL)useCache;
|
||||
|
||||
/**
|
||||
Notifies when a Crash Log becomes available for a given predicate.
|
||||
|
@ -38,12 +40,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (FBFuture<FBCrashLogInfo *> *)notifyOfCrash:(NSPredicate *)predicate;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
An implementation of FBCrashLogCommands, that looks for crash logs on the host.
|
||||
Prunes all of the crashes that may be cached that match the given predicate.
|
||||
|
||||
@param predicate the predicate to match against.
|
||||
@return a Future that will resolve with the pruned crash logs.
|
||||
*/
|
||||
@interface FBHostCrashLogCommands : NSObject <FBCrashLogCommands>
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)pruneCrashes:(NSPredicate *)predicate;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBCrashLogCommands.h"
|
||||
|
||||
#import "FBCrashLogNotifier.h"
|
||||
#import "FBCrashLogStore.h"
|
||||
|
||||
@interface FBHostCrashLogCommands ()
|
||||
|
||||
@property (nonatomic, strong, readonly) FBCrashLogNotifier *notifier;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBHostCrashLogCommands
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)commandsWithTarget:(id<FBiOSTarget>)target
|
||||
{
|
||||
return [[self alloc] initWithNotifier:FBCrashLogNotifier.sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNotifier:(FBCrashLogNotifier *)notifier
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_notifier = notifier;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark id<FBiOSTarget>
|
||||
|
||||
- (FBFuture<FBCrashLogInfo *> *)notifyOfCrash:(NSPredicate *)predicate
|
||||
{
|
||||
return [self.notifier nextCrashLogForPredicate:predicate];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate
|
||||
{
|
||||
return [FBFuture futureWithResult:[self.notifier.store ingestedCrashLogsMatchingPredicate:predicate]];
|
||||
}
|
||||
|
||||
@end
|
|
@ -139,11 +139,19 @@ typedef NS_OPTIONS(NSUInteger, FBCrashLogInfoProcessType) {
|
|||
/**
|
||||
A Predicate for FBCrashLogInfo that passes for all Crash Logs that are newer than the given date.
|
||||
|
||||
@param date the start date.
|
||||
@param date the date.
|
||||
@return a NSPredicate.
|
||||
*/
|
||||
+ (NSPredicate *)predicateNewerThanDate:(NSDate *)date;
|
||||
|
||||
/**
|
||||
A Predicate for FBCrashLogInfo that passes for all Crash Logs that are older than the given date.
|
||||
|
||||
@param date the date.
|
||||
@return a NSPredicate.
|
||||
*/
|
||||
+ (NSPredicate *)predicateOlderThanDate:(NSDate *)date;
|
||||
|
||||
/**
|
||||
A Predicate for FBCrashLogInfo that matches a identifier.
|
||||
|
||||
|
@ -160,6 +168,14 @@ typedef NS_OPTIONS(NSUInteger, FBCrashLogInfoProcessType) {
|
|||
*/
|
||||
+ (NSPredicate *)predicateForName:(NSString *)name;
|
||||
|
||||
/**
|
||||
A Predicate that searches for a substring in the executable path.
|
||||
|
||||
@param contains the substring to search for.
|
||||
@return an NSPredicate
|
||||
*/
|
||||
+ (NSPredicate *)predicateForExecutablePathContains:(NSString *)contains;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -177,6 +177,11 @@
|
|||
}];
|
||||
}
|
||||
|
||||
+ (NSPredicate *)predicateOlderThanDate:(NSDate *)date
|
||||
{
|
||||
return [NSCompoundPredicate notPredicateWithSubpredicate:[self predicateNewerThanDate:date]];
|
||||
}
|
||||
|
||||
+ (NSPredicate *)predicateForIdentifier:(NSString *)identifier
|
||||
{
|
||||
return [NSPredicate predicateWithBlock:^ BOOL (FBCrashLogInfo *crashLog, id _) {
|
||||
|
@ -191,6 +196,13 @@
|
|||
}];
|
||||
}
|
||||
|
||||
+ (NSPredicate *)predicateForExecutablePathContains:(NSString *)contains
|
||||
{
|
||||
return [NSPredicate predicateWithBlock:^ BOOL (FBCrashLogInfo *crashLog, id _) {
|
||||
return [crashLog.executablePath containsString:contains];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark Helpers
|
||||
|
||||
+ (NSArray<NSString *> *)diagnosticReportsPaths
|
||||
|
|
|
@ -370,7 +370,7 @@ static NSString *const FBDiagnosticQueryCrashesSystem = @"system";
|
|||
[FBCrashLogInfo predicateNewerThanDate:self.date],
|
||||
]];
|
||||
return [[target
|
||||
crashes:predicate]
|
||||
crashes:predicate useCache:NO]
|
||||
onQueue:target.asyncQueue map:^(NSArray<FBCrashLogInfo *> *crashes) {
|
||||
NSMutableArray<FBDiagnostic *> *diagnostics = [NSMutableArray array];
|
||||
for (FBCrashLogInfo *crash in crashes) {
|
||||
|
|
|
@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
+ (instancetype)storeForDirectories:(NSArray<NSString *> *)directories logger:(id<FBControlCoreLogger>)logger;
|
||||
|
||||
#pragma mark Public Methods
|
||||
#pragma mark Ingestion
|
||||
|
||||
/**
|
||||
Ingests all of the crash logs in the directory.
|
||||
|
@ -58,13 +58,22 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (nullable FBCrashLogInfo *)ingestCrashLogData:(NSData *)data name:(NSString *)name;
|
||||
|
||||
#pragma mark Fetching
|
||||
|
||||
/**
|
||||
Checks whether the crash log has already been ingested.
|
||||
Returns the ingested crash log for a given name
|
||||
|
||||
@param name the name of the crash log.
|
||||
@return YES if ingested, NO otherwise.
|
||||
@return the Crash Log Info, if present.
|
||||
*/
|
||||
- (BOOL)hasIngestedCrashLogWithName:(NSString *)name;
|
||||
- (nullable FBCrashLogInfo *)ingestedCrashLogWithName:(NSString *)name;
|
||||
|
||||
/**
|
||||
Returns all of the ingested crash logs.
|
||||
|
||||
@return all of the ingested crash logs.
|
||||
*/
|
||||
- (NSArray<FBCrashLogInfo *> *)allIngestedCrashLogs;
|
||||
|
||||
/**
|
||||
A future that resolves the next time a crash log becomes available that matches the given predicate.
|
||||
|
@ -82,6 +91,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (NSArray<FBCrashLogInfo *> *)ingestedCrashLogsMatchingPredicate:(NSPredicate *)predicate;
|
||||
|
||||
/**
|
||||
Prunes all of the ingested logs that match the given predicate.
|
||||
|
||||
@param predicate the predicate to use.
|
||||
@return an array of all the pruned crash logs.
|
||||
*/
|
||||
- (NSArray<FBCrashLogInfo *> *)pruneCrashLogsMatchingPredicate:(NSPredicate *)predicate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -20,7 +20,7 @@ FBCrashLogNotificationName const FBCrashLogAppeared = @"FBCrashLogAppeared";
|
|||
|
||||
@property (nonatomic, copy, readonly) NSArray<NSString *> *directories;
|
||||
@property (nonatomic, strong, readonly) id<FBControlCoreLogger> logger;
|
||||
@property (nonatomic, strong, readonly) NSMutableArray<FBCrashLogInfo *> *ingestedCrashLogs;
|
||||
@property (nonatomic, strong, readonly) NSMutableDictionary<NSString *, FBCrashLogInfo *> *ingestedCrashLogs;
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
|
||||
|
||||
@end
|
||||
|
@ -43,13 +43,13 @@ FBCrashLogNotificationName const FBCrashLogAppeared = @"FBCrashLogAppeared";
|
|||
|
||||
_directories = directories;
|
||||
_logger = logger;
|
||||
_ingestedCrashLogs = NSMutableArray.array;
|
||||
_ingestedCrashLogs = NSMutableDictionary.dictionary;
|
||||
_queue = dispatch_queue_create("com.facebook.fbcontrolcore.crash_store", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Public Methods
|
||||
#pragma mark Ingestion
|
||||
|
||||
- (NSArray<FBCrashLogInfo *> *)ingestAllExistingInDirectory
|
||||
{
|
||||
|
@ -67,15 +67,12 @@ FBCrashLogNotificationName const FBCrashLogAppeared = @"FBCrashLogAppeared";
|
|||
if ([self hasIngestedCrashLogWithName:path.lastPathComponent]) {
|
||||
return nil;
|
||||
}
|
||||
FBCrashLogInfo *crashLogInfo = [FBCrashLogInfo fromCrashLogAtPath:path];
|
||||
if (!crashLogInfo) {
|
||||
FBCrashLogInfo *crashLog = [FBCrashLogInfo fromCrashLogAtPath:path];
|
||||
if (!crashLog) {
|
||||
[self.logger logFormat:@"Could not obtain crash info for %@", path];
|
||||
return nil;
|
||||
}
|
||||
[self.logger logFormat:@"Ingesting Crash Log %@", crashLogInfo];
|
||||
[self.ingestedCrashLogs addObject:crashLogInfo];
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:FBCrashLogAppeared object:crashLogInfo];
|
||||
return crashLogInfo;
|
||||
return [self ingestCrashLog:crashLog];
|
||||
}
|
||||
|
||||
- (nullable FBCrashLogInfo *)ingestCrashLogData:(NSData *)data name:(NSString *)name
|
||||
|
@ -102,9 +99,16 @@ FBCrashLogNotificationName const FBCrashLogAppeared = @"FBCrashLogAppeared";
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasIngestedCrashLogWithName:(NSString *)key
|
||||
#pragma mark Fetching
|
||||
|
||||
- (FBCrashLogInfo *)ingestedCrashLogWithName:(NSString *)name
|
||||
{
|
||||
return [self.ingestedNames containsObject:key];
|
||||
return self.ingestedCrashLogs[name];
|
||||
}
|
||||
|
||||
- (NSArray<FBCrashLogInfo *> *)allIngestedCrashLogs
|
||||
{
|
||||
return self.ingestedCrashLogs.allValues;
|
||||
}
|
||||
|
||||
- (FBFuture<FBCrashLogInfo *> *)nextCrashLogForMatchingPredicate:(NSPredicate *)predicate
|
||||
|
@ -117,11 +121,39 @@ FBCrashLogNotificationName const FBCrashLogAppeared = @"FBCrashLogAppeared";
|
|||
|
||||
- (NSArray<FBCrashLogInfo *> *)ingestedCrashLogsMatchingPredicate:(NSPredicate *)predicate
|
||||
{
|
||||
return [self.ingestedCrashLogs filteredArrayUsingPredicate:predicate];
|
||||
return [self.ingestedCrashLogs.allValues filteredArrayUsingPredicate:predicate];
|
||||
}
|
||||
|
||||
- (NSArray<FBCrashLogInfo *> *)pruneCrashLogsMatchingPredicate:(NSPredicate *)predicate
|
||||
{
|
||||
NSMutableArray<NSString *> *keys = NSMutableArray.array;
|
||||
NSMutableArray<FBCrashLogInfo *> *crashLogs = NSMutableArray.array;
|
||||
for (FBCrashLogInfo *crashLog in self.ingestedCrashLogs.allValues) {
|
||||
if (![predicate evaluateWithObject:crashLog]) {
|
||||
continue;
|
||||
}
|
||||
[keys addObject:crashLog.name];
|
||||
[crashLogs addObject:crashLog];
|
||||
}
|
||||
[self.ingestedCrashLogs removeObjectsForKeys:keys];
|
||||
return crashLogs;
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (BOOL)hasIngestedCrashLogWithName:(NSString *)key
|
||||
{
|
||||
return self.ingestedCrashLogs[key] != nil;
|
||||
}
|
||||
|
||||
- (FBCrashLogInfo *)ingestCrashLog:(FBCrashLogInfo *)crashLog
|
||||
{
|
||||
[self.logger logFormat:@"Ingesting Crash Log %@", crashLog];
|
||||
self.ingestedCrashLogs[crashLog.name] = crashLog;
|
||||
[NSNotificationCenter.defaultCenter postNotificationName:FBCrashLogAppeared object:crashLog];
|
||||
return crashLog;
|
||||
}
|
||||
|
||||
+ (FBFuture<FBCrashLogInfo *> *)oneshotCrashLogNotificationForPredicate:(NSPredicate *)predicate queue:(dispatch_queue_t)queue
|
||||
{
|
||||
__weak NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
|
@ -164,9 +196,4 @@ FBCrashLogNotificationName const FBCrashLogAppeared = @"FBCrashLogAppeared";
|
|||
return [ingested copy];
|
||||
}
|
||||
|
||||
- (NSSet<NSString *> *)ingestedNames
|
||||
{
|
||||
return [NSSet setWithArray:[self.ingestedCrashLogs valueForKeyPath:@"name"]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
return [FBFuture futureWithError:[[FBControlCoreError describe:@"Unimplemented"] build]];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate useCache:(BOOL)useCache
|
||||
{
|
||||
return [FBFuture futureWithError:[[FBControlCoreError describe:@"Unimplemented"] build]];
|
||||
}
|
||||
|
@ -157,4 +157,9 @@
|
|||
return [FBFuture futureWithError:[[FBControlCoreError describe:@"Unimplemented"] build]];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)pruneCrashes:(NSPredicate *)predicate
|
||||
{
|
||||
return [FBFuture futureWithError:[[FBControlCoreError describe:@"Unimplemented"] build]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -58,26 +58,114 @@ static NSString *const PingSuccess = @"ping";
|
|||
|
||||
- (FBFuture<FBCrashLogInfo *> *)notifyOfCrash:(NSPredicate *)predicate
|
||||
{
|
||||
[self ingestAllCrashLogs];
|
||||
[self ingestAllCrashLogs:NO];
|
||||
return [self.store nextCrashLogForMatchingPredicate:predicate];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate useCache:(BOOL)useCache
|
||||
{
|
||||
return [[self
|
||||
ingestAllCrashLogs]
|
||||
ingestAllCrashLogs:useCache]
|
||||
onQueue:self.device.workQueue map:^(NSArray<FBCrashLogInfo *> *_) {
|
||||
return [self.store ingestedCrashLogsMatchingPredicate:predicate];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)pruneCrashes:(NSPredicate *)predicate
|
||||
{
|
||||
id<FBControlCoreLogger> logger = [self.device.logger withName:@"crash_remove"];
|
||||
return [[self
|
||||
ingestAllCrashLogs:YES]
|
||||
onQueue:self.device.workQueue fmap:^(NSArray<FBCrashLogInfo *> *_) {
|
||||
NSArray<FBCrashLogInfo *> *pruned = [self.store pruneCrashLogsMatchingPredicate:predicate];
|
||||
[logger logFormat:@"Pruned %@ logs from local cache", [FBCollectionInformation oneLineDescriptionFromArray:[pruned valueForKeyPath:@"name"]]];
|
||||
return [self removeCrashLogsFromDevice:pruned logger:logger];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)ingestAllCrashLogs
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)ingestAllCrashLogs:(BOOL)useCache
|
||||
{
|
||||
return [[[self.device.amDevice
|
||||
if (self.hasPerformedInitialIngestion && useCache) {
|
||||
return [FBFuture futureWithResult:@[]];
|
||||
}
|
||||
|
||||
id<FBControlCoreLogger> logger = self.device.logger;
|
||||
return [[self
|
||||
copyCrashReportsAndGetFileConnection]
|
||||
onQueue:self.device.workQueue fmap:^(FBAFCConnection *afc) {
|
||||
if (!self.hasPerformedInitialIngestion) {
|
||||
[self.store ingestAllExistingInDirectory];
|
||||
self.hasPerformedInitialIngestion = YES;
|
||||
}
|
||||
NSError *error = nil;
|
||||
NSArray<NSString *> *paths = [afc contentsOfDirectory:@"." error:&error];
|
||||
if (!paths) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
NSMutableArray<FBCrashLogInfo *> *crashes = [NSMutableArray array];
|
||||
for (NSString *path in paths) {
|
||||
FBCrashLogInfo *crash = [self crashLogInfo:afc path:path error:&error];
|
||||
if (!crash) {
|
||||
[logger logFormat:@"Failed to ingest crash log %@: %@", path, error];
|
||||
continue;
|
||||
}
|
||||
[crashes addObject:crash];
|
||||
}
|
||||
return [FBFuture futureWithResult:crashes];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)removeCrashLogsFromDevice:(NSArray<FBCrashLogInfo *> *)crashesToRemove logger:(id<FBControlCoreLogger>)logger
|
||||
{
|
||||
return [[self
|
||||
crashReportFileConnection]
|
||||
onQueue:self.device.workQueue fmap:^(FBAFCConnection *afc) {
|
||||
NSMutableArray<FBCrashLogInfo *> *removed = NSMutableArray.array;
|
||||
for (FBCrashLogInfo *crash in crashesToRemove) {
|
||||
NSError *error = nil;
|
||||
if ([afc removePath:crash.name recursively:NO error:&error]) {
|
||||
[logger logFormat:@"Crash %@ removed from device", crash.name];
|
||||
[removed addObject:crash];
|
||||
} else {
|
||||
[logger logFormat:@"Crash %@ could not be removed from device: %@", crash.name, error];
|
||||
}
|
||||
}
|
||||
return [FBFuture futureWithResult:removed];
|
||||
}];
|
||||
}
|
||||
|
||||
- (nullable FBCrashLogInfo *)crashLogInfo:(FBAFCConnection *)afc path:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
NSString *name = path;
|
||||
FBCrashLogInfo *existing = [self.store ingestedCrashLogWithName:path];
|
||||
if (existing) {
|
||||
[self.device.logger logFormat:@"No need to re-ingest %@", path];
|
||||
return existing;
|
||||
}
|
||||
NSData *data = [afc contentsOfPath:path error:error];
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
return [self.store ingestCrashLogData:data name:name];
|
||||
}
|
||||
|
||||
- (FBFutureContext<FBAFCConnection *> *)copyCrashReportsAndGetFileConnection
|
||||
{
|
||||
return [[self
|
||||
moveCrashReports]
|
||||
onQueue:self.device.workQueue pushTeardown:^(NSString *_) {
|
||||
return [self crashReportFileConnection];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSString *> *)moveCrashReports
|
||||
{
|
||||
return [[self.device.amDevice
|
||||
startService:CrashReportMoverService]
|
||||
onQueue:self.device.asyncQueue fmap:^ FBFuture<FBAMDServiceConnection *> * (FBAMDServiceConnection *connection) {
|
||||
// The mover is used first and can be discarded when done.
|
||||
onQueue:self.device.asyncQueue fmap:^ FBFuture<NSString *> * (FBAMDServiceConnection *connection) {
|
||||
NSError *error = nil;
|
||||
NSData *data = [connection receive:4 error:&error];
|
||||
if (!data) {
|
||||
|
@ -93,49 +181,23 @@ static NSString *const PingSuccess = @"ping";
|
|||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
if (!self.hasPerformedInitialIngestion) {
|
||||
[self.store ingestAllExistingInDirectory];
|
||||
self.hasPerformedInitialIngestion = YES;
|
||||
}
|
||||
return [FBFuture futureWithResult:response];
|
||||
}]
|
||||
onQueue:self.device.workQueue fmap:^(id _) {
|
||||
return [[self.device.amDevice
|
||||
startService:CrashReportCopyService]
|
||||
onQueue:self.device.asyncQueue fmap:^ FBFuture<NSArray<FBCrashLogInfo *> *> * (FBAMDServiceConnection *connection) {
|
||||
NSError *error = nil;
|
||||
FBAFCConnection *afc = [FBAFCConnection afcFromServiceConnection:connection calls:FBAFCConnection.defaultCalls logger:connection.logger error:&error];
|
||||
if (!afc) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
NSArray<NSString *> *paths = [afc contentsOfDirectory:@"." error:&error];
|
||||
if (!paths) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
NSMutableArray<FBCrashLogInfo *> *crashes = [NSMutableArray array];
|
||||
for (NSString *path in paths) {
|
||||
FBCrashLogInfo *crash = [self crashLogInfo:afc path:path error:nil];
|
||||
if (!crash) {
|
||||
continue;
|
||||
}
|
||||
[crashes addObject:crash];
|
||||
}
|
||||
return [FBFuture futureWithResult:crashes];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (nullable FBCrashLogInfo *)crashLogInfo:(FBAFCConnection *)afc path:(NSString *)path error:(NSError **)error
|
||||
- (FBFutureContext<FBAFCConnection *> *)crashReportFileConnection
|
||||
{
|
||||
NSString *name = path;
|
||||
if ([self.store hasIngestedCrashLogWithName:name]) {
|
||||
return nil;
|
||||
}
|
||||
NSData *data = [afc contentsOfPath:path error:error];
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
return [self.store ingestCrashLogData:data name:name];
|
||||
return [[self.device.amDevice
|
||||
startService:CrashReportCopyService]
|
||||
// Re-map this into a AFC Connection.
|
||||
onQueue:self.device.workQueue pend:^(FBAMDServiceConnection *connection) {
|
||||
NSError *error = nil;
|
||||
FBAFCConnection *afc = [FBAFCConnection afcFromServiceConnection:connection calls:FBAFCConnection.defaultCalls logger:self.device.logger error:&error];
|
||||
if (!afc) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
return [FBFuture futureWithResult:afc];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
AA08487E1F3F49D600A4BA60 /* FBFutureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AA08487D1F3F49D600A4BA60 /* FBFutureTests.m */; };
|
||||
AA0949F31F8F4A8A00841A73 /* FBEventReporterIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0949F21F8F4A8A00841A73 /* FBEventReporterIntegrationTests.m */; };
|
||||
AA0CA38720643CCF00347424 /* FBCrashLogCommands.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0CA38620643C6800347424 /* FBCrashLogCommands.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA0CA3892064F44D00347424 /* FBCrashLogCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0CA3882064F44D00347424 /* FBCrashLogCommands.m */; };
|
||||
AA0DC7561CE3A29F0037A8A7 /* FBTestDaemonConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0DC7541CE3A29F0037A8A7 /* FBTestDaemonConnection.h */; };
|
||||
AA0DC7571CE3A29F0037A8A7 /* FBTestDaemonConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0DC7551CE3A29F0037A8A7 /* FBTestDaemonConnection.m */; };
|
||||
AA0EB2831F16905400ABBD7E /* FBApplicationBundle+Simulator.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0EB2811F16905400ABBD7E /* FBApplicationBundle+Simulator.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -408,6 +407,8 @@
|
|||
AAB207C01C2099A9007C7908 /* FBSimulatorLoggingEventSink.h in Headers */ = {isa = PBXBuildFile; fileRef = AAB207BE1C2099A9007C7908 /* FBSimulatorLoggingEventSink.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AAB207C11C2099A9007C7908 /* FBSimulatorLoggingEventSink.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB207BF1C2099A9007C7908 /* FBSimulatorLoggingEventSink.m */; };
|
||||
AAB475F420C80F7D00B37634 /* FBiOSTargetCommandForwarderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB475F320C80F7D00B37634 /* FBiOSTargetCommandForwarderTests.m */; };
|
||||
AAB475F720C8217F00B37634 /* FBSimulatorCrashLogCommands.h in Headers */ = {isa = PBXBuildFile; fileRef = AAB475F520C8217F00B37634 /* FBSimulatorCrashLogCommands.h */; };
|
||||
AAB475F820C8217F00B37634 /* FBSimulatorCrashLogCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB475F620C8217F00B37634 /* FBSimulatorCrashLogCommands.m */; };
|
||||
AAB4AC1E1BB586930046F6A1 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAB4AC1D1BB586930046F6A1 /* AVFoundation.framework */; };
|
||||
AAB4AC271BBBC6880046F6A1 /* FBSimulatorControlTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB4AC261BBBC6880046F6A1 /* FBSimulatorControlTestCase.m */; };
|
||||
AAB52AD120C699E20057F947 /* FBSimulatorCrashLogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AAB52AD020C699E20057F947 /* FBSimulatorCrashLogTests.m */; };
|
||||
|
@ -831,7 +832,6 @@
|
|||
AA08487D1F3F49D600A4BA60 /* FBFutureTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFutureTests.m; sourceTree = "<group>"; };
|
||||
AA0949F21F8F4A8A00841A73 /* FBEventReporterIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBEventReporterIntegrationTests.m; sourceTree = "<group>"; };
|
||||
AA0CA38620643C6800347424 /* FBCrashLogCommands.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBCrashLogCommands.h; sourceTree = "<group>"; };
|
||||
AA0CA3882064F44D00347424 /* FBCrashLogCommands.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBCrashLogCommands.m; sourceTree = "<group>"; };
|
||||
AA0DC7541CE3A29F0037A8A7 /* FBTestDaemonConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBTestDaemonConnection.h; sourceTree = "<group>"; };
|
||||
AA0DC7551CE3A29F0037A8A7 /* FBTestDaemonConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBTestDaemonConnection.m; sourceTree = "<group>"; };
|
||||
AA0EB2811F16905400ABBD7E /* FBApplicationBundle+Simulator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FBApplicationBundle+Simulator.h"; sourceTree = "<group>"; };
|
||||
|
@ -1359,6 +1359,8 @@
|
|||
AAB207BE1C2099A9007C7908 /* FBSimulatorLoggingEventSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSimulatorLoggingEventSink.h; sourceTree = "<group>"; };
|
||||
AAB207BF1C2099A9007C7908 /* FBSimulatorLoggingEventSink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSimulatorLoggingEventSink.m; sourceTree = "<group>"; };
|
||||
AAB475F320C80F7D00B37634 /* FBiOSTargetCommandForwarderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBiOSTargetCommandForwarderTests.m; sourceTree = "<group>"; };
|
||||
AAB475F520C8217F00B37634 /* FBSimulatorCrashLogCommands.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBSimulatorCrashLogCommands.h; sourceTree = "<group>"; };
|
||||
AAB475F620C8217F00B37634 /* FBSimulatorCrashLogCommands.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBSimulatorCrashLogCommands.m; sourceTree = "<group>"; };
|
||||
AAB4AC1D1BB586930046F6A1 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
AAB4AC251BBBC6880046F6A1 /* FBSimulatorControlTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSimulatorControlTestCase.h; sourceTree = "<group>"; };
|
||||
AAB4AC261BBBC6880046F6A1 /* FBSimulatorControlTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSimulatorControlTestCase.m; sourceTree = "<group>"; };
|
||||
|
@ -1799,7 +1801,6 @@
|
|||
AA3EA84F1F31B0DA003FBDC1 /* FBApplicationDataCommands.h */,
|
||||
AA4D30721E799C1900A9FBD0 /* FBBitmapStreamingCommands.h */,
|
||||
AA0CA38620643C6800347424 /* FBCrashLogCommands.h */,
|
||||
AA0CA3882064F44D00347424 /* FBCrashLogCommands.m */,
|
||||
AA4424CA1F4C11A9006B5E5D /* FBiOSTargetCommandForwarder.h */,
|
||||
AA4424CB1F4C11A9006B5E5D /* FBiOSTargetCommandForwarder.m */,
|
||||
AA805F7E1F0D0E0000AB31DE /* FBLogCommands.h */,
|
||||
|
@ -1840,6 +1841,8 @@
|
|||
AA14B55A1DF7401700085855 /* FBSimulatorVideoRecordingCommands.m */,
|
||||
AA861B6B1E5F8F270080C86B /* FBSimulatorXCTestCommands.h */,
|
||||
AA861B6C1E5F8F270080C86B /* FBSimulatorXCTestCommands.m */,
|
||||
AAB475F520C8217F00B37634 /* FBSimulatorCrashLogCommands.h */,
|
||||
AAB475F620C8217F00B37634 /* FBSimulatorCrashLogCommands.m */,
|
||||
);
|
||||
path = Commands;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3334,6 +3337,7 @@
|
|||
AA25770A1DF16B1300789490 /* FBDefaultsModificationStrategy.h in Headers */,
|
||||
AAFE93B61CE4954500A50F76 /* FBSimulatorEraseStrategy.h in Headers */,
|
||||
AA19DA881C77450A009BB89B /* FBSimulatorPool+Private.h in Headers */,
|
||||
AAB475F720C8217F00B37634 /* FBSimulatorCrashLogCommands.h in Headers */,
|
||||
AAFE1C121FD68A7D00ADDE66 /* FBSimulatorNotificationUpdateStrategy.h in Headers */,
|
||||
AA0FF9091E6DE3EC0052634B /* FBVideoEncoderConfiguration.h in Headers */,
|
||||
AA14B55B1DF7401700085855 /* FBSimulatorVideoRecordingCommands.h in Headers */,
|
||||
|
@ -3871,6 +3875,7 @@
|
|||
AA5CB00D1E09B3E500F77765 /* FBUploadMediaStrategy.m in Sources */,
|
||||
AA3EA8541F31B20D003FBDC1 /* FBSimulatorApplicationDataCommands.m in Sources */,
|
||||
AADDED301D6D81F80011EE15 /* FBSimulatorProcessFetcher.m in Sources */,
|
||||
AAB475F820C8217F00B37634 /* FBSimulatorCrashLogCommands.m in Sources */,
|
||||
AA4242EE1C528338008ABD80 /* FBFramebuffer.m in Sources */,
|
||||
AAE42AA91D2D77D800DCD0EA /* FBSimulatorBridge.m in Sources */,
|
||||
AA14B55C1DF7401700085855 /* FBSimulatorVideoRecordingCommands.m in Sources */,
|
||||
|
@ -4168,7 +4173,6 @@
|
|||
AA308FF720E37F9A00503C90 /* FBFutureContextManager.m in Sources */,
|
||||
D76C2AEF1F13F61E000EF13D /* FBEventReporterSubject.m in Sources */,
|
||||
AA14B5601DF8017900085855 /* FBiOSTargetDiagnostics.m in Sources */,
|
||||
AA0CA3892064F44D00347424 /* FBCrashLogCommands.m in Sources */,
|
||||
EEBD60951C908F8500298A07 /* FBCollectionInformation.m in Sources */,
|
||||
AAF026F11F25ED1A0091FDAB /* FBSocketServer.m in Sources */,
|
||||
AA4B4B211F3DAADD005BD475 /* FBApplicationInstallConfiguration.m in Sources */,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBControlCore/FBControlCore.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FBSimulatorCrashLogCommands : NSObject <FBCrashLogCommands>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBSimulatorCrashLogCommands.h"
|
||||
|
||||
#import "FBSimulator.h"
|
||||
|
||||
@interface FBSimulatorCrashLogCommands ()
|
||||
|
||||
@property (nonatomic, weak, readonly) FBSimulator *simulator;
|
||||
@property (nonatomic, strong, readonly) FBCrashLogNotifier *notifier;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBSimulatorCrashLogCommands
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)commandsWithTarget:(id<FBiOSTarget>)target
|
||||
{
|
||||
NSParameterAssert([target isKindOfClass:FBSimulator.class]);
|
||||
return [[self alloc] initWithSimulator:(FBSimulator *)target notifier:FBCrashLogNotifier.sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)initWithSimulator:(FBSimulator *)simulator notifier:(FBCrashLogNotifier *)notifier
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_simulator = simulator;
|
||||
_notifier = notifier;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark id<FBiOSTarget>
|
||||
|
||||
- (FBFuture<FBCrashLogInfo *> *)notifyOfCrash:(NSPredicate *)predicate
|
||||
{
|
||||
return [self.notifier nextCrashLogForPredicate:predicate];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate useCache:(BOOL)useCache
|
||||
{
|
||||
return [FBFuture futureWithResult:[self.notifier.store ingestedCrashLogsMatchingPredicate:predicate]];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)pruneCrashes:(NSPredicate *)predicate
|
||||
{
|
||||
// Unfortunately, the Crash Logs that are created for Simulators may not contain the UDID of the Simulator.
|
||||
// Crashes will not contain a UDID if they are launching System Apps that are present in the RuntimeRoot, not the Simulator Data Directory.
|
||||
// If they are Applications installed by the User, the UDID will appear in the launch path, as the Application is installed relative to the Simulator's Simulator Data Directory.
|
||||
// For this reason, we need to be conservative about which Crash Logs to prune, otherwise we may end up deleting the crash logs of another Simulator, or something running on the host.
|
||||
// Deleting these behind the back of the API is not something that makes sense.
|
||||
// We ensure that *any* crash logs that are to be deleted *must* contain the UDID of the Simulator.
|
||||
NSPredicate *simulatorPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[
|
||||
[FBCrashLogInfo predicateForExecutablePathContains:self.simulator.udid],
|
||||
predicate,
|
||||
]];
|
||||
return [FBFuture futureWithResult:[self.notifier.store pruneCrashLogsMatchingPredicate:simulatorPredicate]];
|
||||
}
|
||||
|
||||
@end
|
|
@ -23,19 +23,21 @@
|
|||
#import "FBMutableSimulatorEventSink.h"
|
||||
#import "FBSimulatorAgentCommands.h"
|
||||
#import "FBSimulatorApplicationCommands.h"
|
||||
#import "FBSimulatorApplicationDataCommands.h"
|
||||
#import "FBSimulatorBridgeCommands.h"
|
||||
#import "FBSimulatorConfiguration+CoreSimulator.h"
|
||||
#import "FBSimulatorConfiguration.h"
|
||||
#import "FBSimulatorControlConfiguration.h"
|
||||
#import "FBSimulatorControlOperator.h"
|
||||
#import "FBSimulatorCrashLogCommands.h"
|
||||
#import "FBSimulatorDiagnostics.h"
|
||||
#import "FBSimulatorError.h"
|
||||
#import "FBSimulatorMutableState.h"
|
||||
#import "FBSimulatorEventSink.h"
|
||||
#import "FBSimulatorHIDEvent.h"
|
||||
#import "FBSimulatorLifecycleCommands.h"
|
||||
#import "FBSimulatorLogCommands.h"
|
||||
#import "FBSimulatorLoggingEventSink.h"
|
||||
#import "FBSimulatorMutableState.h"
|
||||
#import "FBSimulatorNotificationEventSink.h"
|
||||
#import "FBSimulatorPool.h"
|
||||
#import "FBSimulatorScreenshotCommands.h"
|
||||
|
@ -43,7 +45,6 @@
|
|||
#import "FBSimulatorSettingsCommands.h"
|
||||
#import "FBSimulatorVideoRecordingCommands.h"
|
||||
#import "FBSimulatorXCTestCommands.h"
|
||||
#import "FBSimulatorApplicationDataCommands.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wprotocol"
|
||||
|
@ -305,16 +306,16 @@
|
|||
static NSArray<Class> *commandClasses;
|
||||
dispatch_once(&onceToken, ^{
|
||||
commandClasses = @[
|
||||
FBHostCrashLogCommands.class,
|
||||
FBSimulatorScreenshotCommands.class,
|
||||
FBSimulatorAgentCommands.class,
|
||||
FBSimulatorApplicationCommands.class,
|
||||
FBSimulatorApplicationDataCommands.class,
|
||||
FBSimulatorBridgeCommands.class,
|
||||
FBSimulatorCrashLogCommands.class,
|
||||
FBSimulatorKeychainCommands.class,
|
||||
FBSimulatorLaunchCtlCommands.class,
|
||||
FBSimulatorLifecycleCommands.class,
|
||||
FBSimulatorLogCommands.class,
|
||||
FBSimulatorScreenshotCommands.class,
|
||||
FBSimulatorSettingsCommands.class,
|
||||
FBSimulatorVideoRecordingCommands.class,
|
||||
FBSimulatorXCTestCommands.class,
|
||||
|
@ -339,6 +340,7 @@
|
|||
static NSSet<NSString *> *statefulCommands;
|
||||
dispatch_once(&onceToken, ^{
|
||||
statefulCommands = [NSSet setWithArray:@[
|
||||
FBSimulatorCrashLogCommands.class,
|
||||
FBSimulatorVideoRecordingCommands.class,
|
||||
]];
|
||||
});
|
||||
|
|
|
@ -443,7 +443,13 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)crashes:(NSPredicate *)predicate useCache:(BOOL)useCache
|
||||
{
|
||||
NSAssert(NO, @"-[%@ %@] is not yet supported", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<FBCrashLogInfo *> *> *)pruneCrashes:(NSPredicate *)predicate
|
||||
{
|
||||
NSAssert(NO, @"-[%@ %@] is not yet supported", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
|
||||
return nil;
|
||||
|
|
Загрузка…
Ссылка в новой задаче