2015-10-01 16:46:37 WackoSix 阅读数 2789

CSV全称 Comma Separated values,是一种用来存储数据的纯文本文件格式,通常用于电子表格或数据库软件。用Excel或者Numbers都可以导出CSV格式的数据。


CSV文件的规则

   1. 可含或不含列名,含列名则居文件第一行。 

   2. 一行数据不垮行,无空行。

   3. 以半角符号,作分隔符,列为空也要表达其存在。

   4. 列内容如存在,,则用“”包含起来。

   5. 列内容如存在“”则用“”“”包含。

   6. 文件读写时引号,逗号操作规则互逆。

   7. 内码格式不限,可为ASCII、Unicode或者其他。

以上介绍来自于网络!


//保存一个字典

-(void)writeDict:(NSDictionary *)dict forFirst:(BOOL)first{
    
    NSMutableString *str = [NSMutableString  string];

    //第一次写入字典的key值
    if (first) {
        
        for (NSString *key in dict.allKeys) {
            //"," 换列
            [str appendString:[NSString stringWithFormat:@"%@,",key]];
        }
        
    }
    //换行
    [str appendString:@"\n"];
    
    for (NSString *value in dict.allValues) {
        
        [str appendString:[NSString stringWithFormat:@"%@,",value]];
    }
    
    [self writeData:[str dataUsingEncoding:self.encode]];
    
    isFirst = false;
    
}

//保存一个数组

-(void)writeArray:(NSArray *)arr{

    id element = arr[0];
    
    if ([element isKindOfClass:[NSString class]]) {
        
        NSMutableString *str_M = [NSMutableString string];
        
        for (NSString *str in arr) {
            
            [str_M appendString:[NSString stringWithFormat:@"%@,",str]];
        }
        
        [str_M appendString:@"\n"];
        
        [self writeData:[str_M dataUsingEncoding:self.encode]];
    }
    else if ([element isKindOfClass:[NSDictionary class]]){
        
        isFirst = true;
        
        for (NSDictionary *dict in arr) {
            
            [self writeDict:dict forFirst:isFirst];
        }
    
    }
    else if ([element isKindOfClass:[NSArray class]]){
        
        for (NSArray *subArr in arr) {
            
            [self writeArray:subArr];
        }
    
    }
    
}


完整代码:

http://download.csdn.net/detail/wackosix/9151755



2018-07-09 15:25:33 Coco__D 阅读数 1282

符号表文件.dSYM实际上是从Mach-O文件中抽取调试信息而得到的文件目录,实际用于保存调试信息的文件是DWARF,其出身可以从这篇文章了解。

这个是我T9项目导出的文件路径

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/dsymutil /Users/donglizheng/Library/Developer/Xcode/DerivedData/T9-assomxyruqaaigdwvwpkrmuokreu/Build/Products/Debug-iphoneos/T9.app/T9 -o T9.dSYM

这个路径是我在APP中的路径只需要把donglizheng换成自己电脑上的用户,然后把T9换成自己项目名称就可以导出了,如果想导出指定文件夹,那就先cd进入该文件夹,否则就在用户目录下

2016-08-22 19:51:04 xiaofei125145 阅读数 1444


xcode7.3   命令行工具 symbolicatecrash 在如下目录中

/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources

接着需要三个文件

1. 崩溃日志xxx.crash:

Xcode->Window->Devices,选中设备,点击View Device Logs,然后可以看到好多日志,你可以根据时间来判断需要导出哪个日志,选中一个crash日志,右击选择Export log,然后就可以导出日志为aaa.crash,复制到crash文件。

2. 对应的 xxx.app.dSYM文件或者xxx.xcarchive :

打完包之后 注意 备份上述文件中的一个 (xxx.xcarchive  内部是包含 xxx.app.dSYM 的)。


3. symbolicatecrash工具:

/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash 


创建一个 crash文件目录吧,将上述三个文件放入 crash文件目录内。

然后,打开终端,cd到 crash文件目录(可以直接把文件拖到终端,就可以看到crash的路径)


可以用 dwarfdump --uuid xxx.app.dsym 命令先查看一下 dsym 的  uuid如下:

$ dwarfdump --uuid imeituan.app.dSYM
UUID: 846F3B51-91C1-30BD-B725-E77E6544C285 (armv7) imeituan.app.dSYM/Contents/Resources/DWARF/imeituan
UUID: 50232D18-8497-388F-B949-F4128F174C9A (arm64) imeituan.app.dSYM/Contents/Resources/DWARF/imeituan

将上面的 50232D18-8497-388F-B949-F4128F174C9A 去掉 中间横线后 和 

 和 xxx.crash 文件内部 Binary Images: 内对应 imeituan 段的 50232d188497388fb949f4128f174c9a 是一致的 。

Binary Images:
0x100038000 - 0x103607fff imeituan arm64  <50232d188497388fb949f4128f174c9a> /var/containers/Bundle/Application/C58C...


如果不一致,说明 dsym 文件 不匹配。



接着输入

./symbolicatecrash ./xxx.crash ./xxx.app.dSYM > new_symbol.cash

运行完上面这句可能会出现以下问题:Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 60.

解决方法是输入以下命令:export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

然后再重复执行

./symbolicatecrash ./xxx.crash ./xxx.app.dSYM > new_symbol.cash


没问题的话会 在  crash文件目录下 就多了个new_symbol.cash文件,这个就是符号化之后的crash文件了


2016-10-08 16:22:49 Levilly 阅读数 7604

对于那些做后端开发的工程师来说,看LOG解Bug应该是理所当然的事,但我接触到的移动应用开发的工程师里面,很多人并没有这个意识,查Bug时总是一遍一遍的试图重现,试图调试,特别是对一些不太容易重现的Bug经常焦头烂额。而且iOS的异常机制比较复杂,Objective-C的语言驾驭也需要一定的功力,做出来的应用有时候挺容易产生崩溃闪退。一遍一遍的用XCode取应用崩溃记录、解析符号,通常不胜其烦,有时还对着解析出来的调用栈发呆,因为程序当时的内部状态常常难以看明白,只能去猜测。
对于真机,日志没法保存,不好分析问题。所以有必要将日志保存到应用的Docunment目录下,并设置成共享文件,这样才能取出分析。
首先是日志输出,分为c的printf和标准的NSLog输出,printf会向标准输出(sedout)打印,而NSLog则是向标准出错(stderr),我们需要同时让他们都将日志打印到一个文件中。
例子:
freopen(“xx.log”,”a+”,stdout);
freopen(“xx.log”,”a+”,stderr);
具体做法:

#pragma mark - 用户方法,将nslog的输出信息写入到dr.log文件中
// 将NSlog打印信息保存到Document目录下的文件中
//此函数要在- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions中调用,这个函数在AppDelegate.m中实现的。

- (void)redirectNSlogToDocumentFolder
{
    NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
   NSString *documentDirectory = [paths objectAtIndex:0];
   NSString *fileName = [NSStringstringWithFormat:@"dr.log"];//注意不是NSData!
   NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
    //先删除已经存在的文件
   NSFileManager *defaultManager = [NSFileManagerdefaultManager];
    [defaultManagerremoveItemAtPath:logFilePath error:nil];

    // 将log输入到文件
    freopen([logFilePathcStringUsingEncoding:NSASCIIStringEncoding],"a+", stdout);
    freopen([logFilePathcStringUsingEncoding:NSASCIIStringEncoding],"a+", stderr);
}

此函数要在- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions
中调用,这个函数在AppDelegate.m中实现的。
/*********************************************************************/
//当真机连接Mac调试的时候把这些注释掉,否则log只会输入到文件中,而不能从xcode的监视器中看到。
// 如果是真机就保存到Document目录下的dr.log文件中
UIDevice *device = [UIDevicecurrentDevice];
if (![[device model]isEqualToString:@”iPad Simulator”]) {
// 开始保存日志文件
[selfredirectNSlogToDocumentFolder];
}
/*********************************************************************/

最后配置共享文件夹:
在应用程序的Info.plist文件中添加UIFileSharingEnabled键,并将键值设置为YES。将您希望共享的文件放在应用程序的 Documents目录。一旦设备插入到用户计算机,iTunes9.1就会在选中设备的Apps标签中显示一个File Sharing区域。此后,用户就可以向该目录添加文件或者将文件移动到桌面计算机中。如果应用程序支持文件共享,当文件添加到Documents目录后,应用程序应该能够识别并做出适当响应。例如说,应用程序可以将新文件的内容显示界面上。请不要向用户展现目录的文件列表并询问他们希望对文件执行什么操作。
就是说,一旦设备连接上电脑,可以通过iTune查看指定应用程序的共享文件夹,将文件拷贝到你的电脑上看。


iOS中打印日志显示系统详细时间,类名,行号及打印值

Q:如何打印当前的函数和行号?
A:我们可以在打印时使用一些预编译宏作为打印参数,来打印当前的函数和行号。如:

NSLog(@"%s:%d obj=%@", __func__, __LINE__, obj);

其中funcLINE都是预编译的宏,编译时会分别替换为当前函数和当前行号。
下面是一些常用于打印日志的宏。


    宏              说明
__func__             打印当前函数或方法,c字符串
__LINE__             打印当前行号,整数
__FILE__             打印当前文件路径,c字符串
__PRETTY_FUNCTION__  打印当前函数或方法(在C++中会包含参数类型),c字符串

Q:如何打印一个类名,消息名,当前堆栈信息?
A:你可以使用以下方法在运行时动态获取这些信息。


          代码                      说明
NSStringFromSelector(SEL)           获取selector的名字
NSStringFromSelector(_cmd)          获取当前方法名
NSStringFromClass([object class])   获取object的类名
NSThread callStackSymbols]          获取当前线程的栈,是一个NSArry,包含堆栈中所有函数名。

Q:如何将日志打印到一个文件
A:可以使用freopen函数重定向标准输出和标准出错文件。因为printf函数会向标准输出(stdout)打印,而NSLog函数会向标准出错(stderr)打印。重新定向标准输出(stdout)和标准出错(stderr)到一个文件将会使他们打印日志到一个文件中。

freopen("/tmp/log.txt", "a+", stdout);
freopen("/tmp/log.txt", "a+", stderr);
#define NSLog(FORMAT, ...) {\

                            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];\

                            [dateFormatter setDateStyle:NSDateFormatterMediumStyle];\

                            [dateFormatter setTimeStyle:NSDateFormatterShortStyle];\

                            [dateFormatter setDateFormat:@"HH:mm:ss:SSSSSS"]; \

                            NSString *str = [dateFormatter stringFromDate:[NSDate date]];\

                            [dateFormatter release];\

                            fprintf(stderr,"[--%s--]*[--%s--]*[--%s:%d--]\n",[str UTF8String], [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String],[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__);\

                            }
2019-10-14 12:11:18 WangErice 阅读数 65

异常日志符号化

一般情况下如果我们可以通过Xcode来查看异常日志的话,获取到的异常日志都是符号化之后,可以直接查看并定位异常.但是如果在测试阶段需要从手机上导出异常日志,或者集成了第三方异常收集但是未能上传符号表到对应的后台等情况下就只能获取到未符号化的异常日志,这时候就需要对异常日志进行符号化.

生成符号表文件的前提

想要进行异常日志的符号化,前提就是保存了打包时生成的.dSYM文件,如果没有保存的话,符号化是不可能了,能靠经验去找了.

  • 在Generate Debug Symbols --> Apple Clang --> Code Generation中,设置Generate Debug Symbols为YES,模式是打开的;
  • 在Build Settings --> Build Options--> Debug Information Format中,将需要产生符号表的模式对应的值设置为DWARF with dsym File,将不需要产生符号表的模式对应值设置为DWARF.默认Debug模式下不产生符号表,Release模式不产生符号表.

符号表的存储位置

在默认情况下,符号表会存储在跟.xcarchive --> dSYMs文件夹下.

异常日志的符号化

准备

  • 新建一个文件夹CrashAnalysis,用来存储需要的文件;
  • 查找symbolicatecrash,并将该工具保存到新创建的文件夹CrashAnalysis中;

Xcode内置了符号化异常日志的工具symbolicatecrash.打开terminal输入下边的指令,通过find命令查找Mac上的symbolicatecrash位置:

find /Applications/Xcode.app -name symbolicatecrash -type f -print | grep iPhoneSimulator

然后将symbolicatecrash复制到CrashAnalysis中.

  • 将异常日志(.crash),符号表(.dSYM),复制到文件夹CrashAnalysis中;

日志符号化

对于异常日志的符号化,分为两种情况进行讨论:

  • 可以获取到完整的.crash系统异常日志,将全部异常日志一次性进行符号化;
  • 不能获取到完整的.crash系统日志(例如自定义异常获取时,只保留了调用堆栈等部分异常信息),只将需要的部分进行符号化.

(1) 将完整的系统异常日志全部符号化

  • 使用dwarfdump命令查看.dSYM文件的UUID;
dwarfdump --uuid xxx.dSYM
  • 查看.crash中的UUID是否与.dSYM中的UUID一致[在Xcode 11.0上发现每次打包会产生三个.dSYM文件,两个文件名以UUID开头,另外一个文件名以工程名开头];
  • 然后使用以下命令符号化日志
./symbolicatecrash xxx.crash xxx.app.dSYM > result.txt

可能会遇到

Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69.

错误提示,如果遇到了,使用

//如果Xcode.app不是默认的名称,修改为自己Xcode名称即可
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer

然后再次运行开始的命令,即可得到一个result.txt的符号化文件,在该文件中即可看到符号化之后的异常日志.

(2) 将局部异常日志符号化:

有时候我们并不能获取到完整的crash日志,或者只是想对一部分异常日志进行符号化.这时候也可以使用atos命令来进行.

例如自定义异常捕获方法时,我们只获取到了异常调用堆栈的信息:

0   libsystem_kernel.dylib        	0x0000000183d35348 0x183d14000 + 136008
1   libsystem_pthread.dylib       	0x0000000183e49354 0x183e46000 + 13140
2   libsystem_c.dylib             	0x0000000183ca4fd8 0x183c42000 + 405464
3   libc++abi.dylib               	0x0000000183708068 0x183706000 + 8296
4   libc++abi.dylib               	0x0000000183708210 0x183706000 + 8720
5   libobjc.A.dylib               	0x0000000183730810 0x183728000 + 34832
6   libc++abi.dylib               	0x000000018372054c 0x183706000 + 107852
7   libc++abi.dylib               	0x00000001837205b8 0x183706000 + 107960
8   libdispatch.dylib             	0x0000000183ba105c 0x183ba0000 + 4188
9   libdispatch.dylib             	0x0000000183ba86c8 0x183ba0000 + 34504
10  FrontBoardServices            	0x00000001868f1a04 0x1868b1000 + 264708
11  FrontBoardServices            	0x00000001868f16a8 0x1868b1000 + 263848
12  FrontBoardServices            	0x00000001868f1c44 0x1868b1000 + 265284
13  CoreFoundation                	0x00000001841c4358 0x1840da000 + 959320
14  CoreFoundation                	0x00000001841c42d8 0x1840da000 + 959192
15  CoreFoundation                	0x00000001841c3b60 0x1840da000 + 957280
16  CoreFoundation                	0x00000001841c1738 0x1840da000 + 948024
17  CoreFoundation                	0x00000001840e22d8 0x1840da000 + 33496
18  GraphicsServices              	0x0000000185f73f84 0x185f69000 + 44932
19  UIKit                         	0x000000018d68e880 0x18d61b000 + 473216
20  SymblicateCrashDemo             0x0000000100e77770 0x100e70000 + 30576
21  libdyld.dylib                 	0x0000000183c0656c 0x183c05000 + 5484

根据异常堆栈的调用信息可以看到,异常日志调用堆栈包含了四列信息:

  • 第一列是调用顺序序号:只不过时机的执行调用顺序是按照序号的逆序相同(栈);
  • 第二列是对应函数所属的Binary Images:包含了系统的库和自定义的库;
  • 第三列是实际调用的函数栈地址:调用的函数在内存中的栈地址(stack address);
  • 第四列是函数栈地址的偏移量表示法:使用函数所在的的Binary Image起始地址+偏移量来表示函数的栈地址.

目之所及,只有调用序号为20的这一行是自己编写代码中的调用,其余的都是系统Binary Image的调用,没有操作权限,所以不是我们关注的信息.

查看出错信息时,使用

atos -o {.dSYM中可执行文件的路径} -l {函数所在Binary Image起始地址} -arch {对应设备使用的指令集} {实际函数栈地址}

对于用例

20  SymblicateCrashDemo                       	0x0000000100e77770 0x100e70000 + 30576

来说(当前路径是/CrashAnalysis),

  • {.dSYM中可执行文件的路径}:SymblicateCrashDemo.app.dSYM/Contents/Resources/DWARF/SymblicateCrashDemo;
  • {函数所在Binary Image起始地址}:0x100e70000;
  • 所使用测试设备为iPhone XS,所以{对应设备使用的指令集}:arm64(在实际操作中可以获取到当前的设备类型映射到对应的指令集);
  • {实际函数栈地址}:0x0000000100e77770

执行:

atos -o SymblicateCrashDemo.app.dSYM/Contents/Resources/DWARF/SymblicateCrashDemo -l 0x100e70000 -arch arm64 0x0000000100e77770

输出:

__44+[ExceptionHandler registerExceptionHandler]_block_invoke (in SymblicateCrashDemo) (AppDelegate.m:38)

可以知道,出错的代码在Appdelegate实现文件第38行[ExceptionHandler registerExceptionHandler]中.

其他

其实对于局部异常日志符号化,还有一种方法,那就使用 dwarfdump命令,只不过这个命令使用起来比较麻烦.

ASLR (Address space layout randomization):

在iOS中为了防止缓冲区溢出攻击而采用了ASLR技术将可执行文件加载到设备内存.简单来说就是设备每次加载应用可执行文件到内存时,系统都会随机生成一个地址偏移量slide,然后在原来虚拟内存的基础上偏移slide得到得到一个加载可执行文件的起始地址,所以设备每次加载应用的可执行文件到内存的起始地址都是随机的.

在链接时,符号表地址产生了一个实际存储符号的虚拟地址,这个地址存储在应用的可执行文件中,可以使用(mach O文件中load commands中的text段虚拟地址)

otool -arch {设备指令集} -l {应用对应的二进制可执行文件} | grep "segname __TEXT" --after-context=1 | grep "vmaddr"

来进行查看.这个可执行文件在打包时会被存储在符号表的可执行文件中,保存在xxx.app.dSYM/Contents/Resources/DWARF/路径下.

针对用例中:

otool -arch arm64 -l SymblicateCrashDemo.app.dSYM/Contents/Resources/DWARF/SymblicateCrashDemo | grep "segname __TEXT" --after-context=1 | grep "vmaddr"

可以得到:

vmaddr 0x0000000100000000

这个地址就是编译时存储符号表内容的加载地址.

而在设备加载应用可执行文件到内存时会有一个加载起始地址,可以在完整的系统.crash中可以查看(设备加载的第一个Binary Image起始地址):

...
Binary Images:
0x100e70000 - 0x100f53fff SymblicateCrashDemo arm64  <02162b5d77623cd9b937e2633b497b2e> /var/containers/Bundle/Application/0DF5E4BF-6B77-4EC1-982F-1FCC2C2E0642/SymblicateCrashDemo.app/SymblicateCrashDemo
...

所以,有以上两个地址可以得到在加载时产生的随机偏移量slide:

0x100e70000 - 0x0000000100000000 = 0x0000000000e70000

而实际的调用函数地址为0x0000000100e77770,所以可以得到在符号表中的地址:

0x0000000100e77770-0x0000000000e70000 = 0x00000000100007770

上述slide的值也可以在加载时通过代码获取:

//该方法包含在mach-o/dyld.h声明中,使用时需要引入#import <mach-o/dyld.h>
intptr_t slide_address = _dyld_get_image_vmaddr_slide(0);

最后使用:

dwarfdump --arch {设备对应的指令集} --lookup {函数在符号表中的地址} {符号表(.dSYM文件)} 

dwarfdump --arch arm64 --lookup 0x00000000100007770 SymblicateCrashDemo.app.dSYM

得到结果:

0x00075e5a: Compile Unit: length = 0x000005a2 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00076400)

0x00075e65: DW_TAG_compile_unit
              DW_AT_producer	("Apple clang version 11.0.0 (clang-1100.0.33.8)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("/Users/ericydong/Desktop/Exercises/CrashDemo/CrashDemo/AppDelegate.m")
              DW_AT_stmt_list	(0x00011e29)
              DW_AT_comp_dir	("/Users/ericydong/Desktop/Exercises/CrashDemo")
              DW_AT_GNU_pubnames	(true)
              DW_AT_APPLE_optimized	(true)
              DW_AT_APPLE_major_runtime_vers	(0x02)
              DW_AT_low_pc	(0x00000001000076a8)
              DW_AT_high_pc	(0x0000000100007998)

0x00075fac:   DW_TAG_subprogram
                DW_AT_low_pc	(0x0000000100007708)
                DW_AT_high_pc	(0x000000010000781c)
                DW_AT_frame_base	(DW_OP_reg29 W29)
                DW_AT_call_all_calls	(true)
                DW_AT_name	("__44+[ExceptionHandler registerExceptionHandler]_block_invoke")
                DW_AT_decl_file	("/Users/ericydong/Desktop/Exercises/CrashDemo/CrashDemo/AppDelegate.m")
                DW_AT_decl_line	(33)
                DW_AT_prototyped	(true)
                DW_AT_APPLE_optimized	(true)
Line info: file 'AppDelegate.m', line 38, column 39, start line 33

 

技术资料汇总

阅读数 102

没有更多推荐了,返回首页