diff --git a/Signal/src/ViewControllers/AppSettingsViewController.m b/Signal/src/ViewControllers/AppSettingsViewController.m index 8774344da..b7fee9fa2 100644 --- a/Signal/src/ViewControllers/AppSettingsViewController.m +++ b/Signal/src/ViewControllers/AppSettingsViewController.m @@ -108,10 +108,16 @@ - (void)updateTableContents { OWSTableContents *contents = [OWSTableContents new]; - OWSTableSection *section = [OWSTableSection new]; __weak AppSettingsViewController *weakSelf = self; +#ifdef INTERNAL + OWSTableSection *internalSection = [OWSTableSection new]; + [section addItem:[OWSTableItem softCenterLabelItemWithText:@"Internal Build"]]; + [contents addSection:internalSection]; +#endif + + OWSTableSection *section = [OWSTableSection new]; [section addItem:[OWSTableItem itemWithCustomCellBlock:^{ return [weakSelf profileHeaderCell]; } diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 4ffc5f843..bec4f2a96 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -2834,23 +2834,26 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { // We need to reload any modified interactions _before_ we call // reloadViewItems. BOOL hasDeletions = NO; + BOOL hasMalformedRowChange = NO; for (YapDatabaseViewRowChange *rowChange in rowChanges) { switch (rowChange.type) { case YapDatabaseViewChangeUpdate: { YapCollectionKey *collectionKey = rowChange.collectionKey; - OWSAssert(collectionKey.key.length > 0); if (collectionKey.key) { ConversationViewItem *viewItem = self.viewItemCache[collectionKey.key]; [self reloadInteractionForViewItem:viewItem]; + } else { + hasMalformedRowChange = YES; } break; } case YapDatabaseViewChangeDelete: { // Discard cached view items after deletes. YapCollectionKey *collectionKey = rowChange.collectionKey; - OWSAssert(collectionKey.key.length > 0); if (collectionKey.key) { [self.viewItemCache removeObjectForKey:collectionKey.key]; + } else { + hasMalformedRowChange = YES; } hasDeletions = YES; break; @@ -2858,6 +2861,19 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) { default: break; } + if (hasMalformedRowChange) { + break; + } + } + + if (hasMalformedRowChange) { + // These errors seems to be very rare; they can only be reproduced + // using the more extreme actions in the debug UI. + DDLogError(@"%@ hasMalformedRowChange", self.logTag); + [self.collectionView reloadData]; + [self updateLastVisibleTimestamp]; + [self cleanUpUnreadIndicatorIfNecessary]; + return; } NSUInteger oldViewItemCount = self.viewItems.count; diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 99657f5c6..3f53fbe08 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "DebugUIMessages.h" @@ -124,6 +124,14 @@ NS_ASSUME_NONNULL_BEGIN actionBlock:^{ [DebugUIMessages sendFakeMessages:10 * 1000 thread:thread]; }], + [OWSTableItem itemWithTitle:@"Create 100k fake messages" + actionBlock:^{ + [DebugUIMessages sendFakeMessages:100 * 1000 thread:thread]; + }], + [OWSTableItem itemWithTitle:@"Create 100k fake text messages" + actionBlock:^{ + [DebugUIMessages sendFakeMessages:100 * 1000 thread:thread isTextOnly:YES]; + }], [OWSTableItem itemWithTitle:@"Create 1 fake unread messages" actionBlock:^{ [DebugUIMessages createFakeUnreadMessages:1 thread:thread]; @@ -282,9 +290,24 @@ NS_ASSUME_NONNULL_BEGIN [DebugUIMessages createNewGroups:1000 recipientId:recipientId]; }]]; } + if ([thread isKindOfClass:[TSGroupThread class]]) { + TSGroupThread *groupThread = (TSGroupThread *)thread; + [items addObject:[OWSTableItem itemWithTitle:@"Send message to all members" + actionBlock:^{ + [DebugUIMessages sendMessages:1 toAllMembersOfGroup:groupThread]; + }]]; + } return [OWSTableSection sectionWithTitle:self.name items:items]; } ++ (void)sendMessages:(int)counter toAllMembersOfGroup:(TSGroupThread *)groupThread +{ + for (NSString *recipientId in groupThread.groupModel.groupMemberIds) { + TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:recipientId]; + [DebugUIMessages sendTextMessages:counter thread:contactThread]; + } +} + + (void)sendTextMessageInThread:(TSThread *)thread counter:(int)counter { DDLogInfo(@"%@ sendTextMessageInThread: %d", self.logTag, counter); @@ -964,20 +987,42 @@ NS_ASSUME_NONNULL_BEGIN + (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread { - [TSStorageManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [self sendFakeMessages:counter thread:thread transaction:transaction]; - }]; + [self sendFakeMessages:counter thread:thread isTextOnly:NO]; +} + ++ (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread isTextOnly:(BOOL)isTextOnly +{ + const NSUInteger kMaxBatchSize = 2500; + if (counter < kMaxBatchSize) { + [TSStorageManager.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self sendFakeMessages:counter thread:thread isTextOnly:isTextOnly transaction:transaction]; + }]; + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSUInteger remainder = counter; + while (remainder > 0) { + NSUInteger batchSize = MIN(kMaxBatchSize, remainder); + [TSStorageManager.dbReadWriteConnection + readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self sendFakeMessages:batchSize thread:thread isTextOnly:isTextOnly transaction:transaction]; + }]; + remainder -= batchSize; + DDLogInfo(@"%@ sendFakeMessages %zd / %zd", self.logTag, counter - remainder, counter); + } + }); + } } + (void)sendFakeMessages:(NSUInteger)counter thread:(TSThread *)thread + isTextOnly:(BOOL)isTextOnly transaction:(YapDatabaseReadWriteTransaction *)transaction { DDLogInfo(@"%@ sendFakeMessages: %zd", self.logTag, counter); for (NSUInteger i = 0; i < counter; i++) { NSString *randomText = [self randomText]; - switch (arc4random_uniform(4)) { + switch (arc4random_uniform(isTextOnly ? 2 : 4)) { case 0: { TSIncomingMessage *message = [[TSIncomingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] @@ -985,7 +1030,6 @@ NS_ASSUME_NONNULL_BEGIN authorId:@"+19174054215" sourceDeviceId:0 messageBody:randomText]; - DDLogError(@"%@ sendFakeMessages incoming timestamp: %llu.", self.logTag, message.timestamp); [message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO]; break; } @@ -994,8 +1038,8 @@ NS_ASSUME_NONNULL_BEGIN [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:randomText]; - DDLogError(@"%@ sendFakeMessages outgoing timestamp: %llu.", self.logTag, message.timestamp); [message saveWithTransaction:transaction]; + [message updateWithMessageState:TSOutgoingMessageStateUnsent transaction:transaction]; break; } case 2: { @@ -1009,6 +1053,7 @@ NS_ASSUME_NONNULL_BEGIN relay:@"" sourceFilename:@"test.mp3" attachmentType:TSAttachmentTypeDefault]; + pointer.state = TSAttachmentPointerStateFailed; [pointer saveWithTransaction:transaction]; TSIncomingMessage *message = [[TSIncomingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] @@ -1020,7 +1065,6 @@ NS_ASSUME_NONNULL_BEGIN pointer.uniqueId, ] expiresInSeconds:0]; - DDLogError(@"%@ sendFakeMessages incoming attachment timestamp: %llu.", self.logTag, message.timestamp); [message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO]; break; } @@ -1031,7 +1075,6 @@ NS_ASSUME_NONNULL_BEGIN messageBody:nil isVoiceMessage:NO expiresInSeconds:0]; - DDLogError(@"%@ sendFakeMessages outgoing attachment timestamp: %llu.", self.logTag, message.timestamp); NSString *filename = @"test.mp3"; UInt32 filesize = 16; @@ -1050,6 +1093,7 @@ NS_ASSUME_NONNULL_BEGIN message.attachmentFilenameMap[attachmentStream.uniqueId] = filename; } [message saveWithTransaction:transaction]; + [message updateWithMessageState:TSOutgoingMessageStateUnsent transaction:transaction]; break; } } @@ -1248,7 +1292,7 @@ NS_ASSUME_NONNULL_BEGIN }, ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); - [self sendFakeMessages:messageCount thread:thread transaction:transaction]; + [self sendFakeMessages:messageCount thread:thread isTextOnly:NO transaction:transaction]; }, ^(YapDatabaseReadWriteTransaction *transaction) { NSUInteger messageCount = (NSUInteger)(1 + arc4random_uniform(4)); diff --git a/SignalServiceKit/src/Storage/TSStorageManager.m b/SignalServiceKit/src/Storage/TSStorageManager.m index f77ebec86..23b1c3295 100644 --- a/SignalServiceKit/src/Storage/TSStorageManager.m +++ b/SignalServiceKit/src/Storage/TSStorageManager.m @@ -141,6 +141,10 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage) + (void)protectFiles { + DDLogInfo(@"%@ Database file size: %@", self.logTag, [OWSFileSystem fileSizeOfPath:self.legacyDatabaseFilePath]); + DDLogInfo(@"%@ \t SHM file size: %@", self.logTag, [OWSFileSystem fileSizeOfPath:self.legacyDatabaseFilePath_SHM]); + DDLogInfo(@"%@ \t WAL file size: %@", self.logTag, [OWSFileSystem fileSizeOfPath:self.legacyDatabaseFilePath_WAL]); + // The old database location was in the Document directory, // so protect the database files individually. [OWSFileSystem protectFileOrFolderAtPath:self.legacyDatabaseFilePath]; diff --git a/SignalServiceKit/src/Util/OWSFileSystem.h b/SignalServiceKit/src/Util/OWSFileSystem.h index cf6f091ce..d81d94fb3 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.h +++ b/SignalServiceKit/src/Util/OWSFileSystem.h @@ -35,6 +35,8 @@ NS_ASSUME_NONNULL_BEGIN // Returns nil on failure. + (nullable NSString *)writeDataToTemporaryFile:(NSData *)data fileExtension:(NSString *_Nullable)fileExtension; ++ (nullable NSNumber *)fileSizeOfPath:(NSString *)filePath; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index bd089cca6..a95ae4395 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -200,6 +200,20 @@ NS_ASSUME_NONNULL_BEGIN return tempFilePath; } ++ (nullable NSNumber *)fileSizeOfPath:(NSString *)filePath +{ + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSError *_Nullable error; + unsigned long long fileSize = + [[fileManager attributesOfItemAtPath:filePath error:&error][NSFileSize] unsignedLongLongValue]; + if (error) { + DDLogError(@"%@ Couldn't fetch file size[%@]: %@", self.logTag, filePath, error); + return nil; + } else { + return @(fileSize); + } +} + @end NS_ASSUME_NONNULL_END