最近在定位问题过程中,有些涉及到内核,有些涉及到用户面, 有些是两则之间都有。通过咨询牛人,推荐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里面去。因为实际上他也是系统调用。

posted on 2022-12-08 13:40  shiyuan310  阅读(1102)  评论(0编辑  收藏  举报