最近在定位问题过程中,有些涉及到内核,有些涉及到用户面, 有些是两则之间都有。通过咨询牛人,推荐ftrace使用。最后问题得到了解决,下面记录下ftrace和strace简单使用的介绍。
在介绍着两个trace的时候,先说出一个比较好用的地方:这两个工具都支持在应用层代码中加打印,然后可以在log中看到。特别是ftrace, 主要定位内核的流程,在应用层加上打印后,就会把用户态和内核态关联起来。
1:Ftrace
这里简单描述ftrace的使用:我主要使用的有ftrace里面的function, function_graph这两个功能。特别是function,可以查看每个函数的运行时间。
(1)可以过滤指定抓取想要的函数。但是最开始不知道要过滤什么函数。可以先全部抓取。但是此时肯定会抓取很多log, 前面的log也很可能被冲掉。
没办法,先抓取一次全的,然后在过滤自己怀疑或感兴趣的函数。看延时用function抓取。看函数调用关系用function_graph抓取。
(2)前面说了抓取函数的log可能比较大,这里可以设置log文件的大小。默认是1.4M。可以设置到几时M。这是按照CPU 核来抓取的,所以这里也是设置的每个cpu核上的log大小。
下面是所有cpu 核的加一起log的大小。
(3)可以指定cpu 核来抓取log, 前面说的log 太多,我们只抓取自己感兴趣的核上的log.
上面这是抓取所有核上的log。
(4)既然可以指定核上抓取log, 我们看log的时候也可以看指定核上log
具体的操作命令网上很多,再次不在详细说明。
(5)我们在用ftrace定位过程中,一直觉得ftrace是看内核态的流程,想将用户态的流程和内核态的调用结合起来,毕竟有些内核态的流程无法明显看出来是哪个应用层代码调用的。
后来在网上查到用fdmark可以实现我们想要的逻辑。
int fd_mark = -1;
fd_mark = open("/sys/kernel/debug/tracing/trace_marker",O_CREAT||O_RDWR,0666);
然后在应用程序的地方就可以加自己感兴趣的log.
write(fd_mark,"test s_unlock start \n",11);
这个可以和function 结合起来一起使用,完美。
下面可以只在在C语言中调用,关闭ftrace. 一般在应用层加点条件,当出现问题时自动关闭ftrace
system("echo 0 > /sys/kernel/debug/tracing/tracing_on");
2:Strace
在本次问题分析过程中,strace的作用没有体现出来。从一开始测试现象得出来的结论,大家一致认为是系统内核出了问题。所以strace用的不多。只是在分析过程中大家觉得内存分配这一块可能有问题,就使用的strace去跟踪。
一般一个多核多进程多线程的系统,运行过程中不好使用strace抓取有用的信息,目前在使用的项目中没抓去到。
strace是一个非常有用的命令,它用于记录和跟踪程序运行期间收到的信号和调用的系统调用。
每一行表示一个系统调用, 左边为系统调用的名称和参数, 右边为系统调用返回的结果。
当然strace输出也会有很多格式。在这不一一列举。
下面这个例子来自于https://blog.csdn.net/weixin_34195142/article/details/91956265
用strace查看malloc内存分配
malloc采用了两中不同的方式来处理内存申请。 关于内存的具体分配会在另一帖记录中描述
1. 若分配内存小于 128k ,调用 sbrk() ,将堆顶指针向高地址移动,获得新的虚存空间。
2. 若分配内存大于 128k ,调用 mmap() ,在文件映射区域中分配匿名虚存空间。
按照我的想法,应该是应用层调用一次malloc,内核就会调用一次brk或者mmap, 但实际上不是。
我们先看看demo代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct node
{
char a[1024*100];
struct node *next;
}node;
int main()
{
node *node_first = NULL, *node_now;
node *a;
int i;
char s_cmd[100];
sprintf(s_cmd, "pmap -d %lu | grep mapped", getpid());
printf("*******************************before malloc, the top of heap is 0x%lx\n", sbrk(0));
for(i=0;i<10;i++){
a=(node *)malloc(sizeof(node));
a->next=NULL;
if(node_first==NULL)
node_first=a;
else
{
node_now->next=a;
node_now=a;
}
printf("address of a is 0x%lx ", a);
printf("top of heap after malloc is 0x%lx\n", sbrk(0));
}
int *list;
list = malloc(1024*1024);
printf("malloc end\n");
printf("before free, the top of heap is 0x%lx\n", sbrk(0));
while(node_first!=NULL)
{
node_now=node_first->next;
free(node_first);
node_first=node_now;
}
printf("after free, the top of heap is 0x%lx\n", sbrk(0));
free(list);
return 0;
}
实际测出来的结果:
第0,2,4,6,8这几次对应的malloc看不到调用了brk.不明白为啥。 理解这个打印的欢迎留言
结果中省略了程序加载部分内容,从输出结果可以看出:
1. 在为链表申请节点时, 因为每个节点size小于128k(128k是可以调整的), malloc直接通过brk()函数在堆上分配内存。
2. 为list分配内存时,因为它需要1M内存, 所以malloc通过mmap()函数在在文件映射区域分配内存。
这里特别指出,通过printf()可以直接输出到strace里面去。因为实际上他也是系统调用。