重定向 NSLog 输出到文件

来源:互联网 发布:java中构造方法重载 编辑:程序博客网 时间:2024/04/27 12:15

问题:

在 iOS 的开发过程中总是离不开 Debug,调试的时候都是依靠 XCode log 输出来追踪确定问题。

但如果离开了 XCode 的时候仍然想看到日志的输出,比如在调试 App 与硬件的交互的时候,这时候应该怎么办?


解决思路:


方法一:

第一反应是,写个类似于 NSLog 的输出函数,把想要的查看的信息输出到 TextView 上就好了。但很快就否决了,原因有2个:

  • 每一个你想要测试的地方都要添加自定打印输出,代码侵入性大,而且 bug 的出现往往在你意想不到的地方
  • 内核代码一般都是 C/C++ 写的,用不了 Cocoa 的框架,想要在 TextView 上打印出日志,意味着你要把 printf 的 log 一层一层往上传递到 Cocoa 层,麻烦!


方法二:

Hook NSLog 和 print 等输出函数,考虑到 Method Swizzing 方式只能动态替换 OC 方法,对于 C 函数却无能为力。Google 了一下, 发现 facebook 开源的 fishhook 可以 hook C API 。那么这样一来的话就只需要 Hook 常用的几个输出就可以了。似乎可以做到.....

参考链接:Log message send


方法三

深究 NSLog 的调用关系

NSlog->_CFLogvEx->0x32262f20

_CFLogvEX 调用 

CFStringCreateWithFormatAndArgumentsAuxCFStringGetLengthCFStringGetMaximumSizeForEncoding

进行字符串格式化,然后 malloc 一块内存通过 CFStringGetCString 写入函数 0x32262f20 , 再调用 CFAbsoluteTimeGetCurrent 获得系统时间,CFGetProgname 获得 mach-o 文件名,getpid 获得进程 pid

然后,重点来了:

0x32263250 <<redacted>+816>:    movs    r0, #20x32263252 <<redacted>+818>:    blx     0x322b541c <dyld_stub_writev>

没错,写入文件句柄 2,也就是 stderr 。(0 是标准输入,1 是标准输出,2 是错误输出)

writev 执行完后,可以在 stderr 上看到格式化好的字符串。

结论:要获得 NSLog 输出的信息只需要重定向标准输出 stdout 和错误输出 stderr 即可。其他的输出函数也是一样,这样一来,只需要重定向 stdout 和 stderr 就可以做到把所有的打印日志输出到任意地方了,bingo !


解决办法:

以上 3 中方法中,最有意思的最精简的为第三种方法,那么现在就来实现第三种方法。

关于重定向,需要用到 dup2 函数。

int dup2(int oldfd, int newfd);

现在,只需要创建一个文件,然后将其句柄通过函数 dup2 传入,替换到默认的句柄就好了。

int substrateInit(){    int fd;    char pathBuffer[1000];    char timeBuff[20];    time_t now = time(NULL);    strftime(timeBuff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));        fflush(stdout);    fflush(stderr);    snprintf(pathBuffer, sizeof(pathBuffer), "%s/Library/stdout-%s.log", getenv("HOME"), timeBuff);    setvbuf(stdout,NULL,_IONBF,0);    fd = open(pathBuffer, (O_RDWR | O_CREAT), 0644);    if(-1 == fd)    {        printf("\n open() failed with error [%s]\n",strerror(errno));        return -1;    } else {        dup2(fd, STDOUT_FILENO);        dup2(fd, STDERR_FILENO);        printf("Here is dll init, redirect stdout and stderr to logfile\n");    }        return 0;}

打开 Library/ 目录下的日志文件,日志全都写到里面去了,好好耍去吧~


参考链接:ios NSLog 日志重定向到文件中保存 ,这个只能重定向 NSLog 的,printf 的输出是捕获不到的。

1 0
原创粉丝点击