既往不戀,縱情向前ios
原文連接git
//Logs an error message to the Apple System Log facility.
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
複製代碼
根據蘋果的文檔介紹,NSLog的做用是輸出信息到標準的Error控制檯和 蘋果的日誌系統(ASL,Apple System Log)裏面(iOS 10以前)。github
iOS10以後,蘋果使用新的統一日誌系統(Unified Logging System)來記錄日誌,全面取代ASL的方式,此種方式,是把日誌集中存放在內存和數據庫裏,並提供單1、高效和高性能的接口去獲取系統全部級別的消息傳遞。objective-c
新的統一日誌系統沒有ASL那樣的接口可讓咱們取出所有日誌。數據庫
NSLog在調試階段,日誌會輸出到到Xcode中,而在iOS真機上,它會輸出到系統的/var/log/syslog
這個文件中。瀏覽器
在平常開發中,不少人喜歡使用NSLog來輸出調試信息,可是都知道NSLog是比較消耗性能呢,NSLog輸出的內容或次數多了以後,甚至會影響App的體驗。緩存
因而乎,比較常見的手段是,線上不使用NSLog,DEBUG下才真正使用NSLog。bash
#if DEBUG
#define MYLOG(fmt, ...) NSLog((@"%s [Line %d] " fmt), PRETTY_FUNCTION, LINE, ##VA_ARGS);
#else
#define MYLOG(fmt,...) {}
#endif
複製代碼
參考CocoaLumberjack中的DDASLLogCapture實現服務器
+ (void)start {
//...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[self captureAslLogs];
});
}
複製代碼
+ (void)captureAslLogs {
//....
}
複製代碼
[DDLog log: message:]
方法將日誌記錄下來。+ (void)aslMessageReceived:(aslmsg)msg {
//...
}
複製代碼
說明:以上方法不會影響Xcode控制檯的輸出,無侵入。app
captureAslLogs
中經過notify_register_dispatch
來註冊監聽進程間的系統通知;notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token)
{
//...
});
複製代碼
/*
* ASL notifications
* Sent by syslogd to advise clients that new log messages have been
* added to the ASL database.
*/
#define kNotifyASLDBUpdate "com.apple.system.logger.message"
複製代碼
#define kNotifyVFSLowDiskSpace "com.apple.system.lowdiskspace"
複製代碼
APP運行時,Mach-O文件被dyld(動態加載器)加載進內存
ASLR(地址空間佈局隨機化)讓Mach-O被加載時內存地址隨機分配
蘋果的PIC位置與代碼獨立技術,讓Mach-O調用系統庫函數時,先在Mach-O表中的_DATA段
創建一個指針指向外部庫函數,dyld加載MachO時知道外部庫函數的調用地址,會動態的把_DATA段的指針指向外部庫函數
fishhook可以替換NSLog等庫函數,這事是由於Mach-O的符號表裏有NSLog等,能夠經過符號表找到NSLog字符串。
說明:具體原理參考iOS逆向工程 - fishhook原理
實現代碼以下:
//申明一個函數指針用於保存原NSLog的真實函數地址
static void (*orig_nslog)(NSString *format, ...);
//NSLog重定向
void redirect_nslog(NSString *format, ...) {
//能夠添加本身的處理,好比輸出到本身的持久化存儲系統中
//繼續執行原來的 NSLog
va_list va;
format = [NSString stringWithFormat:@"[hook success]%@",format];
va_start(va, format);
NSLogv(format, va);
va_end(va);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
struct rebinding nslog_rebinding = {"NSLog",redirect_nslog,(void*)&orig_nslog};
rebind_symbols((struct rebinding[1]){nslog_rebinding}, 1);
NSLog(@"%@, hello word!",@"ss");
}
return
}
//[hook success]ss, hello word!
複製代碼
NSPipe
建立一個管道,pipe有讀端和寫端,而後經過dup2
將標準輸入重定向到pipe的寫端。再經過NSFileHandle
監聽pipe的讀端,最後再處理讀出的信息。- (void)redirectSTD:(int )fd {
NSPipe * pipe = [NSPipe pipe] ;
NSFileHandle *pipeReadHandle = [pipe fileHandleForReading] ;
int pipeFileHandle = [[pipe fileHandleForWriting] fileDescriptor];
dup2(pipeFileHandle, fd) ;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(redirectNotificationHandle:)
name:NSFileHandleReadCompletionNotification
object:pipeReadHandle] ;
[pipeReadHandle readInBackgroundAndNotify];
}
- (void)redirectNotificationHandle:(NSNotification *)nf {
NSData *data = [[nf userInfo] objectForKey:NSFileHandleNotificationDataItem];
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] ;
//能夠添加本身的處理,能夠將內容顯示到View,或者是存放到另外一個文件中等等
//todo
[[nf object] readInBackgroundAndNotify];
}
//使用
[self redirectSTD:STDERR_FILENO];
複製代碼