FLEXOSLogController.m 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //
  2. // FLEXOSLogController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 12/19/18.
  6. // Copyright © 2018 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXOSLogController.h"
  9. #include <dlfcn.h>
  10. #include "ActivityStreamAPI.h"
  11. NSString * const kFLEXiOSPersistentOSLogKey = @"com.flex.enablePersistentOSLogLogging";
  12. static os_activity_stream_for_pid_t OSActivityStreamForPID;
  13. static os_activity_stream_resume_t OSActivityStreamResume;
  14. static os_activity_stream_cancel_t OSActivityStreamCancel;
  15. static os_log_copy_formatted_message_t OSLogCopyFormattedMessage;
  16. static os_activity_stream_set_event_handler_t OSActivityStreamSetEventHandler;
  17. static int (*proc_name)(int, char *, unsigned int);
  18. static int (*proc_listpids)(uint32_t, uint32_t, void*, int);
  19. static uint8_t (*OSLogGetType)(void *);
  20. @interface FLEXOSLogController ()
  21. + (FLEXOSLogController *)sharedLogController;
  22. @property (nonatomic) void (^updateHandler)(NSArray<FLEXSystemLogMessage *> *);
  23. @property (nonatomic) BOOL canPrint;
  24. @property (nonatomic) int filterPid;
  25. @property (nonatomic) BOOL levelInfo;
  26. @property (nonatomic) BOOL subsystemInfo;
  27. @property (nonatomic) os_activity_stream_t stream;
  28. @end
  29. @implementation FLEXOSLogController
  30. + (void)load {
  31. // Persist logs when the app launches on iOS 10 if we have persistent logs turned on
  32. if (FLEXOSLogAvailable()) {
  33. BOOL persistent = [[NSUserDefaults standardUserDefaults] boolForKey:kFLEXiOSPersistentOSLogKey];
  34. if (persistent) {
  35. [self sharedLogController].persistent = YES;
  36. [[self sharedLogController] startMonitoring];
  37. }
  38. }
  39. }
  40. + (instancetype)sharedLogController {
  41. static FLEXOSLogController *shared = nil;
  42. static dispatch_once_t onceToken;
  43. dispatch_once(&onceToken, ^{
  44. shared = [self new];
  45. });
  46. return shared;
  47. }
  48. + (instancetype)withUpdateHandler:(void(^)(NSArray<FLEXSystemLogMessage *> *newMessages))newMessagesHandler {
  49. FLEXOSLogController *shared = [self sharedLogController];
  50. shared.updateHandler = newMessagesHandler;
  51. return shared;
  52. }
  53. - (id)init {
  54. NSAssert(FLEXOSLogAvailable(), @"os_log is only available on iOS 10 and up");
  55. self = [super init];
  56. if (self) {
  57. _filterPid = NSProcessInfo.processInfo.processIdentifier;
  58. _levelInfo = NO;
  59. _subsystemInfo = NO;
  60. }
  61. return self;
  62. }
  63. - (void)dealloc {
  64. OSActivityStreamCancel(self.stream);
  65. _stream = nil;
  66. }
  67. - (void)setPersistent:(BOOL)persistent {
  68. if (_persistent == persistent) return;
  69. _persistent = persistent;
  70. self.messages = persistent ? [NSMutableArray array] : nil;
  71. }
  72. - (BOOL)startMonitoring {
  73. if (![self lookupSPICalls]) {
  74. // >= iOS 10 is required
  75. return NO;
  76. }
  77. // Are we already monitoring?
  78. if (self.stream) {
  79. // Should we send out the "persisted" messages?
  80. if (self.updateHandler && self.messages.count) {
  81. dispatch_async(dispatch_get_main_queue(), ^{
  82. self.updateHandler(self.messages);
  83. });
  84. }
  85. return YES;
  86. }
  87. // Stream entry handler
  88. os_activity_stream_block_t block = ^bool(os_activity_stream_entry_t entry, int error) {
  89. return [self handleStreamEntry:entry error:error];
  90. };
  91. // Controls which types of messages we see
  92. // 'Historical' appears to just show NSLog stuff
  93. uint32_t activity_stream_flags = OS_ACTIVITY_STREAM_HISTORICAL;
  94. activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY;
  95. // activity_stream_flags |= OS_ACTIVITY_STREAM_PROCESS_ONLY;
  96. self.stream = OSActivityStreamForPID(self.filterPid, activity_stream_flags, block);
  97. // Specify the stream-related event handler
  98. OSActivityStreamSetEventHandler(self.stream, [self streamEventHandlerBlock]);
  99. // Start the stream
  100. OSActivityStreamResume(self.stream);
  101. return YES;
  102. }
  103. - (BOOL)lookupSPICalls {
  104. static BOOL hasSPI = NO;
  105. static dispatch_once_t onceToken;
  106. dispatch_once(&onceToken, ^{
  107. void *handle = dlopen("/System/Library/PrivateFrameworks/LoggingSupport.framework/LoggingSupport", RTLD_NOW);
  108. OSActivityStreamForPID = (os_activity_stream_for_pid_t)dlsym(handle, "os_activity_stream_for_pid");
  109. OSActivityStreamResume = (os_activity_stream_resume_t)dlsym(handle, "os_activity_stream_resume");
  110. OSActivityStreamCancel = (os_activity_stream_cancel_t)dlsym(handle, "os_activity_stream_cancel");
  111. OSLogCopyFormattedMessage = (os_log_copy_formatted_message_t)dlsym(handle, "os_log_copy_formatted_message");
  112. OSActivityStreamSetEventHandler = (os_activity_stream_set_event_handler_t)dlsym(handle, "os_activity_stream_set_event_handler");
  113. proc_name = (int(*)(int, char *, unsigned int))dlsym(handle, "proc_name");
  114. proc_listpids = (int(*)(uint32_t, uint32_t, void*, int))dlsym(handle, "proc_listpids");
  115. OSLogGetType = (uint8_t(*)(void *))dlsym(handle, "os_log_get_type");
  116. hasSPI = (OSActivityStreamForPID != NULL) &&
  117. (OSActivityStreamResume != NULL) &&
  118. (OSActivityStreamCancel != NULL) &&
  119. (OSLogCopyFormattedMessage != NULL) &&
  120. (OSActivityStreamSetEventHandler != NULL) &&
  121. (OSLogGetType != NULL) &&
  122. (proc_name != NULL);
  123. });
  124. return hasSPI;
  125. }
  126. - (BOOL)handleStreamEntry:(os_activity_stream_entry_t)entry error:(int)error {
  127. if (!self.canPrint || (self.filterPid != -1 && entry->pid != self.filterPid)) {
  128. return YES;
  129. }
  130. if (!error && entry) {
  131. if (entry->type == OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE ||
  132. entry->type == OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE) {
  133. os_log_message_t log_message = &entry->log_message;
  134. // Get date
  135. NSDate *date = [NSDate dateWithTimeIntervalSince1970:log_message->tv_gmt.tv_sec];
  136. // Get log message text
  137. const char *messageText = OSLogCopyFormattedMessage(log_message);
  138. // https://github.com/limneos/oslog/issues/1
  139. if (entry->log_message.format && !(strcmp(entry->log_message.format, messageText))) {
  140. messageText = (char *)entry->log_message.format;
  141. }
  142. // move messageText from stack to heap
  143. NSString *msg = [NSString stringWithUTF8String:messageText];
  144. dispatch_async(dispatch_get_main_queue(), ^{
  145. FLEXSystemLogMessage *message = [FLEXSystemLogMessage logMessageFromDate:date text:msg];
  146. if (self.persistent) {
  147. [self.messages addObject:message];
  148. }
  149. if (self.updateHandler) {
  150. self.updateHandler(@[message]);
  151. }
  152. });
  153. }
  154. }
  155. return YES;
  156. }
  157. - (os_activity_stream_event_block_t)streamEventHandlerBlock {
  158. return [^void(os_activity_stream_t stream, os_activity_stream_event_t event) {
  159. switch (event) {
  160. case OS_ACTIVITY_STREAM_EVENT_STARTED:
  161. self.canPrint = YES;
  162. break;
  163. case OS_ACTIVITY_STREAM_EVENT_STOPPED:
  164. break;
  165. case OS_ACTIVITY_STREAM_EVENT_FAILED:
  166. break;
  167. case OS_ACTIVITY_STREAM_EVENT_CHUNK_STARTED:
  168. break;
  169. case OS_ACTIVITY_STREAM_EVENT_CHUNK_FINISHED:
  170. break;
  171. default:
  172. printf("=== Unhandled case ===\n");
  173. break;
  174. }
  175. } copy];
  176. }
  177. @end