1、Linux性能分析工具、调试工具 浅析,irenezhan2013-3-29,目 录 性能分析工具,内存分析工具Linux内存解析free I/O分析工具iostat 文件分析工具lsof、readlink cpu分析工具vmstat、top,目 录 调试工具,代码分析工具file、nm、c+filt、lddaddr2line 代码耗时分析工具timegprof 、 strace 、ltrace 内存泄露检测工具valgrind、mtrace,性能分析工具,内存分析工具Linux内存解析free I/O分析工具iostat 文件分析工具lsof、readlink cpu分析工具vmstat、t
2、op,Linux内存浅析-swap,用做虚拟内存的硬盘部分被称为交换空间(swap space)。 1、我们经常认为swap使用较少,内存就够用。 2、有一种例外:如果一个占用很多物理内存的进程结束并释放内存时。被交换出的数据并不会自动地交换进内存。这时会出现一个状况:尽管有许多的空闲内存,仍然会有许多的交换空间正被使用。3、Linux经常使用一部分交换空间,以保持尽可能多的空闲物理内存。即使并没有什么事情需要内存,Linux也会交换出暂时不用的内存页面。这可以避免等待交换所需的时间:当磁盘闲着,就可以提前做好交换。,Linux内存浅析-buffer和cache,如果给所有应用分配足够内存后,
3、物理内存还有剩余,linux会尽量再利用这些空闲内存,以提高整体I/O效率,其方法是把这部分剩余内存划分为cache及buffer。1、磁盘缓冲(disk buffering): 指将信息从磁盘上仅读入一次并将其存于内存中,除了第一次读以外,可以加快所有其它读的速度。被用作此目的的内存称为高速缓冲(Buffer Cache),即Cache。 2、一个应用程序在内存中修改过数据后,因为写入磁盘速度较低,这些数据先存入buffer,在以后某个时间再写入磁盘如果在某个时刻,系统需要更多的内存,则会把cache部分擦除,并把buffer中的内容写入磁盘,从而把这两部分内存释放给系统使用,这样再次读取c
4、ache中的内容时,就需要重新从磁盘读取了。*/30 * * * * echo 2 /proc/sys/vm/drop_caches 用作清除缓存,就是让cache减小sync命令让脏数据下盘,可以让buffer减小一点,free,作用:查看系统内存的使用情况(单位都是KB),第二行(Mem): 物理内存统计total:物理内存的总量 used: 已经使用的内存,包括分配给buffers和cached的,但buffers和cached中可能部分缓存并未实际使用。free: 仅代表未被分配的内存第二行total = 第二行used +free = 第三行used + free 第三行(-/+ b
5、uffers/cache):系统实际使用的内存总量和实际可用的内存总量used: Mem行used列 - Mem行buffers列 - Mem行cached列free: Mem行free列 + Mem行buffers列 + Mem行cached列 第四行(Swap) : 交换内存的总量、已使用量、空闲量总结:内存多是系统缓存占用的,性能分析工具,内存分析工具Linux内存解析free I/O分析工具iostat 文件分析工具lsof、readlink cpu分析工具vmstat、top,iostat,作用:评估磁盘的利用率,从而探测到系统中的I/O瓶颈。 数据分析出 I/O 请求的模式,以及
6、I/O 的速度和响应时间一般使用的时候用到-x(extended) r/s: 每秒完成的读 I/O 设备次数,是实际读取的数量 w/s: 每秒完成的写 I/O 设备次数,是实际写入的数量 每秒有 2.36次设备 I/O 操作: 总IO(io)/s = r/s(读) +w/s(写) 但请求比这两个数据大,因为当系统调用需要读取数据时,VFS将请求发到各个FS,FS发现不同的读请求读取的是相同的block的数据,就会将这些请求合并。 wrqm/s 每秒有多少个写请求被merge了,iostat,rsec/s: 每秒读扇区数 wsec/s: 每秒写扇区数 rkB/s: 每秒读K字节数 wkB/s:
7、每秒写K字节数 avgrq-sz: 平均每次设备I/O操作的数据大小 (扇区)0.38次读,其数据量是52.62个扇区,因此1次读的扇区是52.62/0.38 = 138.4个扇区 同理,1次写的扇区是12.18 / 1.98 = 6.15个扇区 平均每次设备I/O操作的数据大小 = 138.4 * 读占得比重0.16 + 6.15 * 写占得比重0.84 = 27.48,iostat,avgqu-sz: 平均I/O队列长度 await: 平均每次设备I/O操作的等待时间 (毫秒) svctm: 平均每次设备I/O操作的服务时间 (毫秒) %util: 一秒中有百分之多少的时间用于 I/O 操
8、作,或者说一秒中有多少时间 I/O 队列是非空的 如果%util接近100%,说明I/O请求太多,磁盘可能存在瓶颈了。,rrqm/s wrqm/s r/s w/s rsec/s wsec/s rkB/s wkB/s0.00 44.90 1.02 27.55 8.16 579.57 4.08 289.80avgrq-sz avgqu-sz await svctm %util20.57 22.35 78.21 5.00 14.29 1、平均每次设备I/O操作的等待时间 平均每次I/O 操作只需要 5ms 就可以完成(svctm),即1s可以处理200个请求但这里只有28.57 次请求,每个 I/O
9、 请求却需要等上 78ms(await),为什么?因为同一时间发出的 I/O 请求太多 平均等待时间 = 单个 I/O 服务时间 * ( 1 + 2 + + 请求总数-1) / 请求总数,应用到上面的例子: 平均等待时间 = 5ms * (1+2+28)/29 = 70ms,和 iostat 给出的78ms 很接近。 2、 平均I/O队列长度一秒中有 14.29% 的时间 I/O 队列中是有请求的,所有 29 个 请求都在142毫秒之内处理掉78.21*28.57 = 2232.8,表明每秒内的I/O请求总共需要等待2232.8ms。所以平均队列长度应为 2232.8ms/142ms =15.
10、7,而且在142ms中有可能某一个时间段是请求更为集中的时间,这时候的队列长度又比15.7更高,所以iostat返回的22.35还是有道理的。3、如果能看到每次操作的请求大小,就可以看到平均操作avgrq-sz个扇区是合并后的,一些I/O被合并以便更有效地像硬盘写入数据。,iostat,性能分析工具,内存分析工具Linux内存解析free I/O分析工具iostat 文件分析工具lsof、readlink cpu分析工具vmstat、top,lsof,作用:显示进程打开的文件(包括网络连接和硬件)lsof -p pid 例子: int main(void) open(“/tmp/foo“, O
11、_CREAT|O_RDONLY); # ./testlsof & # lsof -p 17596 文件描述符是4,lsof,/proc/pid/fd/目录下的每一个fd文件都是符号链接,而此链接就指向被该进程打开的一个文件,readlink,作用:获得符号链接的目标# readlink /proc/3125/fd/3 /tmp/fooreadlink不仅仅是一个shell命令,同时也是一个系统调用。 头文件: #include ssize_t readlink(const char *path, char *buf, size_t bufsiz); 参数:符号链接的路径,接收链接对象的缓冲、该
12、缓冲的长度但readlink 不会在填充的目标路径最后添加 NULL字符。不过,它会返回对象路径的字符数量,这使得为字符串添加 NULL 结尾变得很简单。 若 readlink 第一个参数指向一个文件而不是符号链接时,返回 -1 # ln -s /usr/bin/wc my_link /第一个是src,第二个是新建的链接# ./print-symlink my_link/usr/bin/wc,性能分析工具,内存分析工具Linux内存解析free I/O分析工具iostat 文件分析工具lsof、readlink cpu分析工具vmstat、top,vmstat,作用:观察系统的进程状态、内存使
13、用、虚拟内存使用、磁盘的 IO、中断、上下文切 换、CPU使用等,r: 运行的和等待(CPU时间片)运行的进程数, 如果这个值长期大于系统 cpu的个数,说明需要增加CPU b: 在等待资源的进程数,比如正在等待I/O、或者内存交换等。,vmstat,Swap si: 交换内存使用,每秒由磁盘调入内存的量 so: 交换内存使用,每秒由内存调入磁盘的量内存够用的时候,这2个值都是0,如果这2个值长期 大于0时,系统性能会受到影响。磁盘IO和CPU资 源都会被消耗。Io bi: 读磁盘的数据总量 (blocks/s),cache的目的就是降低该值 bo:写磁盘的数据总量随机磁盘读写的时候,这2个
14、值越大(如超 出1M),能看到CPU在IO等 待的值也会越大这里in/out是对内存而言的,System in: 每秒产生的中断次数 cs: 每秒产生的上下文切换次数 上面这2个值越大,会看到由内核消耗的CPU时间会越多。若值太大1、 调低线程或者进程的数目2、 看能否减少系统调用,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换(比如time)上下文切换次数过多表示你的CPU大部分浪费在上下文切换,vmstat,Cpu 都是百分比,相加为100%us: 用户进程消耗的CPU时间百分比该值较高说明用户进程消耗的CPU时间多,但是如果长期超过50% 的使用,那么我们就该考虑优化程序算
15、法或者进 行加速了 sy: 内核进程消耗的CPU时间百分比该值较高说明系统内核消耗的CPU资源多。如果太高,表示系统调用时间长,例如是IO操作频繁。根据经验,us+sy的参考值为80%,如果us+sy大于 80%说明可能存在CPU资源不足。 id: CPU处在空闲状态时间百分比 wa: IO等待消耗的CPU时间百分比该值较高(30%)说明IO等待比较严重,这可能是由于磁盘大量作随机访问造成,也有可能是磁盘的带宽出现瓶颈(块操作)。 b参数(等待资源的进程数)和wa参数(IO等待所占用的CPU时间的百分比)可配合iostat使用,vmstat,几个简单的例子: 1、程序中有一个死循环,不断地求平
16、方根procs中r增加,us上升; 2、大量的系统调用,cs增加(上下文切换),sy增加 3、大量的IO操作dd if=/dev/zero of=/data/irenezhan/tmp/tmp_file bs=1K count=10000bi和bo增大(bo骤然升高,因为dd不断向磁盘写数据),vmstat,top,作用:查看各个进程的cpu、内存的使用情况top与vmstat的不同使用场景不同vmstat可看到整个机器的CPU,内存,IO的使用情况,而cpu侧重于看到各个进程的CPU使用率和内存使用率,调试工具,代码分析工具file、nm、c+filt、lddaddr2line 代码耗时分析
17、工具timegprof 、 strace 、ltrace 内存泄露检测工具valgrind、mtrace,file,作用: 用来判断文件类型和使用使用了库(包括动态库和静态库),作用:获取二进制文件包含的符号信息如果没有指出目标文件,则目标文件为a.out输出: 其值(十六进制)、 类型、名字,nm,c+filt,作用:将编码后的名称还原为原来的函数名称以及参数类型dlopen: /bin/ftnqqdisksetmd5_mcd_run.so: undefined symbol: _ZN12UserIndexPktC1Ev 使用方法:c+filt _ZN12UserIndexPktC1EvUs
18、erIndexPkt:UserIndexPkt() 因此我就知道没有把.o文件包含进来。 原理:C+和JAVA为了实现函数重载机制,对函数名根据参数进行编码为低级别的符号,通常为加后缀,以对重载函数进行区别。,ldd,作用:显示可执行模块所链接的所有动态库的信息,如果目标程序没有链接动态库,则打印“not a dynamic executable”输出:第一栏:需要用什么库;第二栏:实际用哪个库文件;第三栏:库文件装载地址。,作用:当ulimit -c 悲剧的等于0时,就可以用来查看代码core的源代码文件及行数。当某个进程崩溃时,日志文件(/var/log/messages)中就会给出附加的
19、信息,包括程序终止原因、故障地址,以及包含程序状态字(PSW)、通用寄存器和访问寄存器的简要寄存器转储(建议尽量在64位机器上实验),addr2line,如下是/var/log/messages中的信息(用dmesg 命令也可以得到)这个地址和gdb定位出的地址是一样的注意:1. 该可执行程序用-g编译,使之带调试信息。2. 如果crash在一个so里面,那addr2line不能直接给出代码行。,addr2line,调试工具,代码分析工具file、nm、c+filt、lddaddr2line 代码耗时分析工具timegprof 、 strace 、ltrace 内存泄露检测工具valgrind
20、、mtrace,代码耗时分析工具,调试程序的常见步骤: 1、确定运行时间主要花在用户态还是内核态:time 2、如果是用户态,则使用gprof进行性能分析 3、如果是内核态,则使用strace进行性能分析,另外可以使用其他工具(比如ltrace等)辅助,time,作用:查看程序执行时间、用户态时间、内核态时间# time ps aux | grep hireal 0m0.009suser 0m0.000ssys 0m0.004s,gprof,作用:分析应用层程序性能,显示用户态各函数执行时间和计数等原理:在编译和链接程序的时候(使用 -pg 编译和链接选项),gcc在应用程序的每个函数中都加入
21、了一个名为mcount(or“_mcount”, or“_mcount”)的函数,因此mcount会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址。这张调用图也保存了所有与函数相关的调用时间,调用次数等等的所有信息。,使用步骤: 1、使用 -pg 编译和链接应用程序gcc -pg -o exec exec.c 2、执行应用程序使之生成供gprof 分析的数据gmon.out3、分析数据直接打开gmon.out是乱码,用gporf工具分析gprof exec gmon.out profile.txt 注意:gprof只能在程序正常结束退出之后才能生成报告(gmon.
22、out)因为 gprof在atexit()里注册了一个函数来产生结果信息,任何非正常退出都不会执行atexit()的动作,所以不会产生gmon.out文件。从main函数中正常退出,或者通过系统调用exit()函数退出都会调用atexit(),gprof,gprof,输出:%time :在所有运行时间中,这个函数耗费的时间比重 cumulative seconds: 累计执行时间,是在这一行之上包括这一行的运行秒数的总和 self seconds: 仅仅是这个函数的运行秒数,包括所有被调用次数的总和时间 calls: 函数被调用的次数 self ts/call : 平均每一次调用消费的毫秒,是
23、函数平均的执行时间 total ts/call: 平均每一次该函数调用的时间和它的后裔耗费的时间(毫秒) name:函数名字,输出:“Call graph”,包括函数的调用关系,每个函数调用花费了多少时间等 gprof缺点:1、只能等程序运行完了,才能得到gprof的结果2、工作在内核态的函数是无法被监控的3、不支持多线程应用,多线程下只能采集主线程性能数据。原因是gprof采用ITIMER_PROF信号,在多线程内只有主线程才能响应该信号。4、若程序中使用了库,对库的支持不佳,gprof,gprof,动态库首先要保证在动态库编译的时候也加了-pg选项,否则可是即便动态库的编译用了-pg, g
24、prof也不能支持对动态库的解析,静态库将刚才的动态库改编为静态库,同样生成静态库时要加上-pg 虽然静态库能得到函数的调用,但也是无法得到内核态的相类似的还有oprofile和perf,都不是自带的,其中perf必须要2.6.31以上的内核,但现在现网机器2.6.16,所以不支持。,gprof,strace,作用1、得到各个系统调用的耗时;只能跟踪系统调用,不能跟踪用户态2、查看各个系统调用的返回值,排查错误open(“/usr/lib/i686/mmx/libnss_files.so.2“, O_RDONLY) = -1 ENOENT (No such file or directory)
25、 使用 不仅可以直接运行程序: strace ./main 也可以关联到一个正在运行的进程: strace -p attach 通用用法:strace -c ./main strace -p pid -c 用-c 统计各个系统调用的调用情况,计算系统调用执行时间strace -o output.txt -T -tt -e trace=all -p 28979,strace,输出strace -c ./main strace -o output.txt -T -tt -e trace=all -p 28979每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值,内是耗
26、费的时间,ltrace,作用:得到库函数的调用情况ltrace可以分析动态库的库函数,但是不能跟踪静态库 strace跟踪的是系统调用,这里和库没有关系,代码分析工具file、nm、c+filt、lddaddr2line 代码耗时分析工具timegprof 、 strace 、ltrace 内存泄露检测工具valgrind、mtrace,调试工具,Valgrind,作用:用于调试内存问题(只能查找堆的访问错误,对栈上的对象和静态对象没办法)内存泄漏;内存越界(限malloc/new申请的);访问野指针;重复free/delete等问题。使用方法: 1、安装valgrind 2、编译程序时加上-
27、g 3、valgrind启动程序valgrind -tool=memcheck -leak-check=full ./test-tool=memcheck 是设置检测内存泄漏,使用 leak-check=full 是检查级别如果想了解更加详细的日志,可以加上-v,程序一 申请了内存,但没有释放程序二 free两次且访问野指针,Valgrind,Valgrind,缺点:1、valgrind只能查找堆内存的访问错误,对栈上的对象和静态对象没办法。 2、valgrind会影响进程性能,据说可能慢20倍,所以在性能要求高的情况下,只能使用mtrace这种轻量级的工具了,mtrace,作用:检查是否有内
28、存泄露原理:mtrace是glibc內提供的工具,就是把你程序中malloc(),realloc()和free()的位置全部下來,最后两辆配对,沒有配对到的就是memory leak。,mtrace,使用: 1、代码中添加mtrace(),这个函数就会对malloc(),realloc()和free()装handler. 而函数muntrace() 可以让这些handlers失效2、用环境变量 MALLOC_TRACE ,这个变量定义了mtrace()这个函数的输出文件 int main(void) int *p;#ifdef DEBUGsetenv(“MALLOC_TRACE“, “./me
29、mleak.log“, 1);mtrace();#endifp=(int *)malloc(1000);return 0; ,mtrace,输出: 编译时要带上调试信息-g,不然输出的只有地址(当然可以用addr2line)# mtrace test1 memleak.log - 0x0804a008 Free 3 was never allocd 0xb7e31cbe - 0x0804a100 Free 4 was never allocd 0xb7ec3e3f - 0x0804a120 Free 5 was never allocd 0xb7ec3e47 Memory not freed: -Address Size Caller 0x0804a4a8 0x3e8 at /data/irenezhan/test1.c:14memleak.log本来就是ascii码的,用mtrace工具只是更便于观察,