性能分析工具说明(内存、CPU性能相关)
性能分析工具说明(内存、CPU性能相关)
目 录
针对Linux系统性能分析按照系统逻辑结构可分为内核态、用户态、中间件、外网IO和网络等分析维度,具体如Brendan Gregg框图为下图所示。
统计、监控虚拟内存、进程、CPU等的整体情况。具体功能见vmstat -h,查看和使用。例如:vmstat -S m -w -s。
统计系统信息、CPU、TTY、硬盘等外设的输入/输出统计信息。具体功能见iostat –h,查看和使用。例如:iostat –p ALL。
查询CPU、磁盘使用情况和IO、网络收发包情况、内存页切换情况等。系统不自带,需另行安装apt install dstat。例如:dstat –afcdplmnsy。
查询硬盘读写和线程之间IO使用的关系,安装apt install iotop,iotop –h查询用法,例如:
pidstat用于查询线程与核的亲核关系、优先级、线程树、线程UID、TGID、TID等信息。
查询多核处理器实时存在于proc/stat中的信息、CPU状态等,例如:mpstat -P ALL -I ALL 1。
可查询路由表、socket创建端口绑定信息、IP、TCP、ICMP等协议统计数据等,用于检测本地网络接口的连接状态。
例如:
- 查询路由:netstat -rn
- 查询接口信息:netstat -in
- 查询端口信息:netstat -npl
- 查询UDP信息:netstat -an
- 查询UDP特定端口:netstat -an | grep 2152
perf是Linux kernel自带的系统性能优化工具。在Linux平台上可以记录采用线程应用的实时情况用于分析函数时间消耗的比例,优化函数、功能细节。
优势在于与Linux Kernel的紧密结合,它可以最先应用到加入Kernel的new feature,用于查看热点函数,查看cashe miss的比率,从而帮助开发者来优化程序性能。性能调优工具如 perf,Oprofile 等的基本原理都是对被监测对象进行采样,最简单的情形是根据 tick 中断进行采样,即在 tick 中断内触发采样点,在采样点里判断程序当时的上下文。假如一个程序 90% 的时间都花费在函数 foo() 上,那么 90% 的采样点都应该落在函数 foo() 的上下文中。运气不可捉摸,但我想只要采样频率足够高,采样时间足够长,那么以上推论就比较可靠。因此,通过 tick 触发采样,我们便可以了解程序中哪些地方最耗时间,从而重点分析。
依赖于系统提供的 linux-tools- x.xx.xx-dirty、linux-cloud-tools-x.xx.xx-dirty
例如:
- 记录日志:sudo perf record ./ps_l2(Ctrl+C 强行退出之后在当前目录下会生成perf.data的日志文件)
- 采样周期:sudo perf record -F99 -a -g ./ps_l2
- 指定采样CPU核x:sudo perf record -p $(pidof ps_l2) -C x
- 分析日志:sudo perf report
- 实时分析(PID):sudo perf report -p PID
实时分析,先运行ps_l2,再通过sudo perf record -p $(pidof ps_l2) -C 1 分析核1的线程消耗情况。
图上指示为核1上线程L2ThreadEntry实际消耗,包括各个函数的消耗分析,OSD模式:
实时线程的函数消耗分析(仅仅可分析大于总函数消耗1%的情况)
Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。
Valgrind可用于分析函数调用、线程分布和调用情况、函数相对CPU cycle消耗和比例分析、Linux系统内存管理接口API的申请和释放情况、Linux系统内存初始化、溢出等分析。
Valgrind包括如下一些工具:
- Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
- Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
- Cachegrind。它主要用来检查程序中缓存使用出现的问题。
- Helgrind。它主要用来检查多线程程序中出现的竞争问题。
- Massif。它主要用来检查程序中堆栈使用中出现的问题。
- Extension。可以利用core提供的功能,自己编写特定的内存调试工具
通过配合kcachegrind进行图形化的函数内容分析,可以更加清晰看清逻辑和消耗的时间。
例如通过分析一个应用的内存调用情况和内部系统消耗,并生成callgrind.out.xxx文件,再利用kcachegrind。
[email protected]:~/Documents/Samba_dir/TnWTestCode/codetest/ValgrindTest/test1$ valgrind --tool=callgrind ./main ==36978== Callgrind, a call-graph generating cache profiler ==36978== Copyright (C) 2002-2017, and GNU GPL'd, by Josef Weidendorfer et al. ==36978== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==36978== Command: ./main ==36978== ==36978== For interactive control, run 'callgrind_control -h'. main[36978]:i=1 main[36978]:i=2 main[36978]:i=3 main[36978]:i=4 main[36978]:i=5 main[36978]:i=6 main[36978]:i=7 main[36978]:i=8 main[36978]:i=9 main[36978]:i=10 pthread[36978]:i=9 pthread[36978]:i=8 pthread[36978]:i=7 pthread[36978]:i=6 pthread[36978]:i=5 pthread[36978]:i=4 pthread[36978]:i=3 pthread[36978]:i=2 pthread[36978]:i=1 pthread[36978]:i=0 ==36978== ==36978== Events : Ir ==36978== Collected : 257194 ==36978== ==36978== I refs: 257,194 [email protected]:~/Documents/Samba_dir/TnWTestCode/codetest/ValgrindTest/test1$ |
载入callgrind.out.xxx文件之后,生成动态分析工程框架如下:
通过上述GUI可分析函数调用次数、调用关系和系统接口的消耗相对,缺陷是,无法得知一个函数占整个多核分布应用的总时间片分布和消耗。
例如针对一个线程的main函数分析,可得出main被调关系,和主动调用的各个层级函数的关系、并且给出函数内部各级函数相对消耗的系统时间Ir占比;右边窗口可以分析系统级别函数调用关系,特别Valgrind可接管的系统库,缺陷是,非系统相关库,例如DPDK库等,Valgrind目前开源版本适配较差,各公司会针对自有库再对Valgrind进行定制化增加库功能模块,即可分析适配于公司模型的调用关系分析。
分析函数调用关系
valgrind --tool=callgrind ./main |
int main() { pthread_t t; pthread_mutex_init(&g_lock1, NULL);
if (pthread_create(&t, NULL, fun, NULL)==-1) { printf("pthread creat error\n"); } int i; for(i=0; i<num; ++i) { pthread_mutex_lock(&g_lock1); printf("main[%d]:i=%d\n",getpid(), ++g_i); pthread_mutex_unlock(&g_lock1); //sleep(1); }
if (pthread_join(t, NULL) == -1) { printf("pthread_join error\n"); } pthread_mutex_destroy(&g_lock1); return 0; } |
==36978== Callgrind, a call-graph generating cache profiler ==36978== Copyright (C) 2002-2017, and GNU GPL'd, by Josef Weidendorfer et al. ==36978== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==36978== Command: ./main ==36978== ==36978== For interactive control, run 'callgrind_control -h'. main[36978]:i=1 main[36978]:i=2 main[36978]:i=3 main[36978]:i=4 main[36978]:i=5 main[36978]:i=6 main[36978]:i=7 main[36978]:i=8 main[36978]:i=9 main[36978]:i=10 pthread[36978]:i=9 pthread[36978]:i=8 pthread[36978]:i=7 pthread[36978]:i=6 pthread[36978]:i=5 pthread[36978]:i=4 pthread[36978]:i=3 pthread[36978]:i=2 pthread[36978]:i=1 pthread[36978]:i=0 ==36978== ==36978== Events : Ir ==36978== Collected : 257194 ==36978== ==36978== I refs: 257,194 |
可分析内存申请、释放、过释放、未申请、泄露等问题。
valgrind --tool=memcheck ./main |
static void testoverflow(void) { int *dataArry = malloc(100*sizeof(int)); dataArry[22] = 0; } |
==37673== Memcheck, a memory error detector ==37673== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==37673== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==37673== Command: ./main ==37673== pthread[37673]:i=-1 pthread[37673]:i=-2 pthread[37673]:i=-3 pthread[37673]:i=-4 pthread[37673]:i=-5 pthread[37673]:i=-6 pthread[37673]:i=-7 pthread[37673]:i=-8 pthread[37673]:i=-9 pthread[37673]:i=-10 main[37673]:i=-9 main[37673]:i=-8 main[37673]:i=-7 main[37673]:i=-6 main[37673]:i=-5 main[37673]:i=-4 main[37673]:i=-3 main[37673]:i=-2 main[37673]:i=-1 main[37673]:i=0 ==37673== ==37673== HEAP SUMMARY: ==37673== in use at exit: 4,000 bytes in 10 blocks ==37673== total heap usage: 12 allocs, 2 frees, 5,296 bytes allocated ==37673== ==37673== 4,000 bytes in 10 blocks are definitely lost in loss record 1 of 1 ==37673== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==37673== by 0x1089CB: testoverflow (main.c:11) ==37673== by 0x108A76: fun (main.c:34) ==37673== by 0x4E436DA: start_thread (pthread_create.c:463) ==37673== by 0x517CA3E: clone (clone.S:95) ==37673== ==37673== LEAK SUMMARY: ==37673== definitely lost: 4,000 bytes in 10 blocks ==37673== indirectly lost: 0 bytes in 0 blocks ==37673== possibly lost: 0 bytes in 0 blocks ==37673== still reachable: 0 bytes in 0 blocks ==37673== suppressed: 0 bytes in 0 blocks ==37673== ==37673== For counts of detected and suppressed errors, rerun with: -v ==37673== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) |
既可以得出definitely lost: 4,000 bytes in 10 blocks在10个线程的main.c中11行中个申请400个字节内存,但未释放。
valgrind --tool=memcheck ./main |
int *dataArry; static void memtest1(void) { dataArry[200] = 0; } static void memtest0(void) { dataArry = malloc(100*sizeof(int)); dataArry[22] = 0; free(dataArry); } |
==37718== Memcheck, a memory error detector ==37718== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==37718== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==37718== Command: ./main ==37718== main[37718]:i=1 main[37718]:i=2 main[37718]:i=3 main[37718]:i=4 main[37718]:i=5 main[37718]:i=6 main[37718]:i=7 main[37718]:i=8 main[37718]:i=9 main[37718]:i=10 pthread[37718]:i=9 ==37718== Thread 2: ==37718== Invalid write of size 4 ==37718== at 0x108A0B: memtest1 (main.c:12) ==37718== by 0x108AE6: fun (main.c:42) ==37718== by 0x4E436DA: start_thread (pthread_create.c:463) ==37718== by 0x517CA3E: clone (clone.S:95) ==37718== Address 0x5c4d8f0 is 336 bytes inside an unallocated block of size 4,192,320 in arena "client" ==37718== pthread[37718]:i=8 pthread[37718]:i=7 pthread[37718]:i=6 pthread[37718]:i=5 pthread[37718]:i=4 pthread[37718]:i=3 pthread[37718]:i=2 pthread[37718]:i=1 pthread[37718]:i=0 ==37718== ==37718== HEAP SUMMARY: ==37718== in use at exit: 0 bytes in 0 blocks ==37718== total heap usage: 12 allocs, 12 frees, 5,296 bytes allocated ==37718== ==37718== All heap blocks were freed -- no leaks are possible ==37718== ==37718== For counts of detected and suppressed errors, rerun with: -v ==37718== ERROR SUMMARY: 10 errors from 1 contexts (suppressed: 0 from 0) |
内存在函数memtest0中已经释放,memtest1中无内存空间可使用,即出现越界内存使用情况。
valgrind --tool=memcheck ./main |
static void memtest2(void) { char dataArry2[50]; memset(dataArry2, 0, sizeof(dataArry2)); strncpy(&dataArry2[0], &dataArry2[10], 30); } |
==38798== Memcheck, a memory error detector ==38798== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==38798== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==38798== Command: ./main ==38798== ==38798== Source and destination overlap in strncpy(0x1fff0002e0, 0x1fff0002ea, 30) ==38798== at 0x4C333D8: __strncpy_sse2_unaligned (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==38798== by 0x108742: memtest2 (main.c:15) ==38798== by 0x108762: main (main.c:92) ==38798== ==38798== ==38798== HEAP SUMMARY: ==38798== in use at exit: 0 bytes in 0 blocks ==38798== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==38798== ==38798== All heap blocks were freed -- no leaks are possible ==38798== ==38798== For counts of detected and suppressed errors, rerun with: -v ==38798== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) [email protected]:~/Documents/Samba_dir/TnWTestCode/codetest/ValgrindTest/test1$ valgrind --tool=memcheck ./main ==38800== Memcheck, a memory error detector ==38800== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==38800== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==38800== Command: ./main ==38800== ==38800== Source and destination overlap in strncpy(0x1fff0002e0, 0x1fff0002ea, 30) ==38800== at 0x4C333D8: __strncpy_sse2_unaligned (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==38800== by 0x108742: memtest2 (main.c:15) ==38800== by 0x108762: main (main.c:92) ==38800== ==38800== ==38800== HEAP SUMMARY: ==38800== in use at exit: 0 bytes in 0 blocks ==38800== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==38800== ==38800== All heap blocks were freed -- no leaks are possible ==38800== ==38800== For counts of detected and suppressed errors, rerun with: -v |
出错原因为复制内存过程中覆盖导致异常出现。
- 依赖于开源工具需要平台高度适配、现有的工具iperf、valgrind等高度依赖系统版本和系统以来相关库,定制RT版本的ARM内核、DPDK内核等valgrind、iperf都需要平台参与高度定制,目前工具仅仅可以用于局部单元测试场景或本地标准系统环境使用;
- 分析工具仅仅可分析的颗粒度不够,通常夹渣系统相关的库和高层应用模块,对于分析精度和逻辑性较差,若需要进行全系统逻辑分析,需要平台参与系统中间件环境构建;
- 针对算法模块、函数子模块的自查和收敛性测试有用,特别是对于单元模块优化和分析迭代等有利。
- 参考文件
- LinuxGNU开源优化软件,开源中国;
- GoogleTest测试集;
- Google Linux性能优化实验项目、GNU性能分析工程;
- 版本说明
版本号 |
修改内容 |
日期-修订人 |
1.0Beta |
首次编辑 |
2020.06.30-阮曦 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|