Skip to content

Commit

Permalink
Removing FSEvents code from FBCrashLogNotifier
Browse files Browse the repository at this point in the history
Summary:
## Context
As mentioned [here](https://fb.workplace.com/groups/1042353022615812/permalink/2767866986731065/) by RE prod eng, they freqently kill fseventsd when it starts using too many resources. They don't currently do anything to ensure that it restarts correctly, and it doesn't appear as though they have it in their priorities.

What this means for us, is CrashLog collection is extremely unreliable (seems like ~30% of the time FSEvents is down), which means we can't observe the diagnostics folders.

## This diff
- Strips FBCrashLogNotifier of FSEvents logic, and funnels everything into a `resolveUntil` block which is effectively polling the known diagnostics locations.
- Fixes a bug(?) where the polling implementation wasn't handling the `onlyNew` flag. I don't think this is an issue in practice as I'm not seeing anywhere in the code where we actually want to collect older crashlogs.
- Updates the FBSimulatorControlTests to target MacOSX, and replaces the assertion for iPhone8 to iPhone16 (which is something we actually have).

Differential Revision: D68713530

fbshipit-source-id: 1d9907af3dbf2da51de345d1c58cb0d2457062d0
  • Loading branch information
cpepin authored and facebook-github-bot committed Jan 27, 2025
1 parent 83f6ee7 commit b9f96bc
Show file tree
Hide file tree
Showing 3 changed files with 4 additions and 140 deletions.
140 changes: 2 additions & 138 deletions FBControlCore/Crashes/FBCrashLogNotifier.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,133 +13,9 @@
#import "FBControlCoreLogger.h"
#import "FBControlCoreError.h"

#if defined(__apple_build_version__)

#import <CoreServices/CoreServices.h>
#include <sys/stat.h>

@interface FBCrashLogNotifier_FSEvents : NSObject

@property (nonatomic, copy, readonly) NSArray<NSString *> *directories;
@property (nonatomic, strong, readonly) FBCrashLogStore *store;
@property (nonatomic, strong, readonly) id<FBControlCoreLogger> logger;
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
@property (nonatomic, assign, readwrite) FSEventStreamRef eventStream;

@end

typedef NS_ENUM(NSUInteger, FBCrashLogNotifierFileEvent) {
FBCrashLogNotifierFileEventUnknown = 0,
FBCrashLogNotifierFileEventAdded = 1,
FBCrashLogNotifierFileEventRemoved = 2,
};

static FBCrashLogNotifierFileEvent GetEventType(FSEventStreamEventFlags flag, NSString *filePath) {
if (flag & kFSEventStreamEventFlagItemRemoved) {
return FBCrashLogNotifierFileEventRemoved;
} else if (flag & kFSEventStreamEventFlagItemCreated) {
return FBCrashLogNotifierFileEventAdded;
} else if (flag & kFSEventStreamEventFlagItemRenamed) {
struct stat buffer;
int value = stat(filePath.UTF8String, &buffer);
return value == 0 ? FBCrashLogNotifierFileEventAdded : FBCrashLogNotifierFileEventRemoved;
}
return FBCrashLogNotifierFileEventUnknown;
}

static void EventStreamCallback(
ConstFSEventStreamRef streamRef,
FBCrashLogNotifier_FSEvents *notifier,
size_t numEvents,
NSArray<NSString *> *eventPaths,
const FSEventStreamEventFlags *eventFlags,
const FSEventStreamEventId *eventIds
){
for (size_t index = 0; index < numEvents; index++) {
NSString *path = eventPaths[index];
FSEventStreamEventFlags flag = eventFlags[index];
switch (GetEventType(flag, path)) {
case FBCrashLogNotifierFileEventAdded:
[notifier.store ingestCrashLogAtPath:path];
continue;
case FBCrashLogNotifierFileEventRemoved:
[notifier.store removeCrashLogAtPath:path];
continue;
default:
continue;
}
}
}

@implementation FBCrashLogNotifier_FSEvents

- (instancetype)initWithDirectories:(NSArray<NSString *> *)directories store:(FBCrashLogStore *)store logger:(id<FBControlCoreLogger>)logger
{
self = [super init];
if (!self) {
return nil;
}

_directories = directories;
_store = store;
_logger = logger;
_queue = dispatch_queue_create("com.facebook.fbcontrolcore.crash_logs.fsevents", DISPATCH_QUEUE_SERIAL);

return self;
}

- (BOOL)startListening:(BOOL)onlyNew
{
if (self.eventStream) {
return YES;
}

FSEventStreamContext context = {
.version = 0,
.info = (void *) CFBridgingRetain(self),
.retain = CFRetain,
.release = CFRelease,
.copyDescription = NULL,
};

NSMutableArray<NSString *> *pathsToWatch = NSMutableArray.array;
for (NSString *reportPath in self.directories) {
if ([[NSFileManager defaultManager] fileExistsAtPath:reportPath]) {
[pathsToWatch addObject:reportPath];
}
}

FSEventStreamRef eventStream = FSEventStreamCreate(
NULL, // Allocator
(FSEventStreamCallback) EventStreamCallback, // Callback
&context, // Context
CFBridgingRetain(pathsToWatch), // Paths to watch
onlyNew ? kFSEventStreamEventIdSinceNow : 0, // Since When
0, // Latency
kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer
);
FSEventStreamSetDispatchQueue(eventStream, self.queue);
Boolean started = FSEventStreamStart(eventStream);
if (!started) {
NSLog(@"FS Event Stream could not be started");
return NO;
}

self.eventStream = eventStream;
return YES;
}

@end

#endif

@interface FBCrashLogNotifier ()

#if defined(__apple_build_version__)
@property (nonatomic, strong, readonly) FBCrashLogNotifier_FSEvents *fsEvents;
#else
@property (nonatomic, copy, readwrite) NSDate *sinceDate;
#endif

@end

Expand All @@ -155,12 +31,7 @@ - (instancetype)initWithLogger:(id<FBControlCoreLogger>)logger
}

_store = [FBCrashLogStore storeForDirectories:FBCrashLogInfo.diagnosticReportsPaths logger:logger];

#if defined(__apple_build_version__)
_fsEvents = [[FBCrashLogNotifier_FSEvents alloc] initWithDirectories:FBCrashLogInfo.diagnosticReportsPaths store:_store logger:logger];
#else
_sinceDate = NSDate.date;
#endif

return self;
}
Expand All @@ -179,12 +50,8 @@ + (instancetype)sharedInstance

- (BOOL)startListening:(BOOL)onlyNew
{
#if defined(__apple_build_version__)
return [self.fsEvents startListening:onlyNew];
#else
self.sinceDate = NSDate.date;
self.sinceDate = onlyNew ? NSDate.date : [NSDate distantPast];
return YES;
#endif
}

- (FBFuture<FBCrashLogInfo *> *)nextCrashLogForPredicate:(NSPredicate *)predicate
Expand All @@ -195,9 +62,6 @@ - (BOOL)startListening:(BOOL)onlyNew
failFuture];
}

#if defined(__apple_build_version__)
return [FBCrashLogNotifier.sharedInstance.fsEvents.store nextCrashLogForMatchingPredicate:predicate];
#else
dispatch_queue_t queue = dispatch_queue_create("com.facebook.fbcontrolcore.crashlogfetch", DISPATCH_QUEUE_SERIAL);
return [FBFuture
onQueue:queue resolveUntil:^{
Expand All @@ -210,9 +74,9 @@ - (BOOL)startListening:(BOOL)onlyNew
describeFormat:@"Crash Log Info for %@ could not be obtained", predicate]
failFuture];
}
[self.store ingestCrashLogAtPath:crashInfo.crashPath];
return [FBFuture futureWithResult:crashInfo];
}];
#endif
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ - (void)testAppCrashLogIsFetched
XCTAssertNil(error);
XCTAssertNotNil(crashLog);
XCTAssertEqualObjects(crashLog.identifier, @"TableSearch");
XCTAssertTrue([[NSString stringWithContentsOfFile:crashLog.crashPath encoding:NSUTF8StringEncoding error:nil] containsString:@"NSFileManager stringWithFormat"]);
XCTAssertTrue([[NSString stringWithContentsOfFile:crashLog.crashPath encoding:NSUTF8StringEncoding error:&error] containsString:@"\"app_name\":\"TableSearch\""]);
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ + (FBSimulatorBootConfiguration *)defaultBootConfiguration
- (void)setUp
{
self.continueAfterFailure = NO;
self.simulatorConfiguration = [FBSimulatorConfiguration.defaultConfiguration withDeviceModel:FBDeviceModeliPhone8];
self.simulatorConfiguration = [FBSimulatorConfiguration.defaultConfiguration withDeviceModel:FBDeviceModeliPhone16];
self.bootConfiguration = [[FBSimulatorBootConfiguration alloc] initWithOptions:FBSimulatorControlTestCase.bootOptions environment:@{}];
self.deviceSetPath = FBSimulatorControlTestCase.defaultDeviceSetPath;
}
Expand Down

0 comments on commit b9f96bc

Please sign in to comment.