FLEXOSLogController.m 7.2 KB

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