perf_event源码分析(一)——cmd_record

原创 2011年10月10日 23:04:51

cmd_record是perf用户层的一个核心工具,为之后的report, annotate等工具提供profile,监测数据都记录在perf.data中,后面就是进行上层分析,这里对cmd_record()的分析主要是关注perf.data文件内容组成。

parse_events()会根据用户在命令行上的指定来配置监测事件,为每一个事件分配一个perf_evsel结构体,其中记录了事件名称与配置,最后将这个perf_evsel挂在静态全局变量evsel_list的entries链表上。对于每个事件,如果监测x个线程在y个core上运行的信息,就要做x*y个文件描述符,分别对应这个xy个监测信息,所以evsel->fd是xyarray这样类似二维数组结构,讲每个事件的名称、配置记录在perf_trace_event_type events[event_count]数组中,其中event->event_id = evesel->attr.config。

在完成用户指定事件相关操作之后,下面进入__cmd_record()。perf_session_new()函数是关键,它会被record和report分别以写、读方式进行调用,对于record在其中会调用perf_session__create_kernel_maps():(perf_session => machine)

1.        先做一个动态共享目标dso *kernel,kernel->short_name = "[kernel]", kernel->long_name = "[kernel.kallsyms]", kernel->kernel = DSO_TYPE_KERNEL, 读/sys/kernel/notes,将其中的id写入kernel->build_id[20]数组中。最后把这个dso挂到machine->kernel_dsos链表中。<=machine__create_kernel()

2.        从/proc/kallsyms中读出内核映射的起始地址(0),初始化map *machine->vmlinux[2]数组,map->start = start, map->dso = kernel(上面做的那个dso *kernel),此外,在这两个map结构体之后申请一个kmap结构体,这个kmap结构体的kmaps指针指向&machine->kmaps这个map_groups结构体地址。最后将这两个map结构体根据类型(MAP__FUNCTION, MAP__VARIABLE)和映射地址(map->start)挂到machine->kmaps->maps[type]红黑树上。<= __machine__create_kernel_maps()

3.        读/proc/modules文件,得到各个模块名和起始地址,首先从machine->kernel_dsos链表上根据模块名查找是否已经插入,如果是一个新模块名,就创建一个dso结构体,dso->name = module_name, dso->kernel = DSO_TYPE_USER,然后将这个dso挂到machine->kernel_dsos链表上,读/sys/module/module_name/notes/.note.gnu.build-id,记录到dso->build_id[]数组中。最后将这个dso封装成一个map结构体并挂到machine->kmaps.maps[MAP__FUNCTION]红黑树上。

4.        到目录/lib/modules/3.0.0/kernel/下,查找是否有/proc/modules中同名的.ko文件,即将/lib/modules/3.0.0/kernel/目录下所有.ko文件名与machine->kmaps.maps[MAP__FUNCTION]红黑树中map->dso->name做比较,若匹配,就记录这个.ko文件的绝对路径名到dso->long_name。map->end是根据各个映射的相对位置计算出来的prev->end = cur->start - 1。

 

回到__cmd_record(),接下来open_counters()会根据用户指定来配置个事件属性,并且利用这个事件作为sys_perf_event_open系统调用的参数,这是用户层转入内核层的接口,系统调用会返回一个文件描述符,关于这个系统调用,留在以后分析。上面调用系统调用的总次数是线程数乘以处理器数乘以事件数。文件描述符对应的文件占用的是内核空间,为了减少内核空间消耗,需要尽快将监测数据dump到用户空间,这里利用mmap将内核空间数据直接映射到用户地址空间。

perf_session__write_header(),在perf.data文件头部写入数据:

f_header = (struct perf_file_header){
	.magic = PERF_MAGIC,
	.size = sizeof(f_header),
	.attr_size = sizeof(f_attr),
	.attrs = {
		.offset = header->attr_offset,
		.size = evlist->nr_entries * sizeof(f_attr),
         },
	.data = {
		.offset = header->data_offset,
		.size = header->data_size,
	},
	.event_types = {
		.offset = header->event_offset,
		.size = header->event_size,
	},
};


具体内容可以利用hd -x perf.data查看。

perf_event__synthesize_kernel_mmap(process_synthesized_event, session, machine, "_text"),函数kallsyms__parse(filename, &args,find_symbol_cb)打开/proc/kallsyms文件,找到_text段对应的起始地址0xffffffff81000000。这里构造一个mmap_event,event->mmap.filename = "[kernel.kallsyms]_text",event->mmap.pgoff = args.start,event->mmap.start = 0, event->mmap.len = 0xffffffffa003fff。

perf_event__synthesize_modules()会把machine->kmaps.maps[MAP__FUNCTION]红黑树上每个映射文件作为mmap_event,event->mmap.filename = dso->long_name,如"/lib/modules/3.0.0/kernel/drivers/net/e1000e/e1000e.ko",event->start/len也是根据map结构体中数据进行填充。

根据是否进行全系统采样,调用perf_event__synthesize_thread_map()和perf_event__synthesize_threads(),如果只监测某一进程,就打开/proc/pid/status文件,得到执行命令,以及线程组内其他线程pid,据此往perf.data中写入一个comm_event;打开/proc/pid/maps文件,将这个进程使用到的所有可执行文件做成一个个的mmap_event,写入perf.data。如果是全系统采样,就打开/proc目录,对所有进程都进行如上操作。

这样,用户空间的DSO文件映射就记录在perf.data文件中,接下来后期处理如cmd_report()会通过fetch_mapped_event()函数来取得这个进程的动态共享文件中的各个符号。

$ sleep 10000 &
$ cat /proc/18140/maps
00400000-00407000 r-xp 00000000 08:01 1430929				/bin/sleep
00606000-00607000 rw-p 00006000 08:01 1430929				/bin/sleep
02104000-02125000 rw-p 00000000 00:00 0					[heap]
7f22c7726000-7f22c78a0000 r-xp 00000000 08:01 801437		/lib/x86_64-linux-gnu/libc-2.13.so
7f22c78a0000-7f22c7aa0000 ---p 0017a000 08:01 801437		/lib/x86_64-linux-gnu/libc-2.13.so
7f22c7aa0000-7f22c7aa4000 r--p 0017a000 08:01 801437		/lib/x86_64-linux-gnu/libc-2.13.so
7f22c7aa4000-7f22c7aa5000 rw-p 0017e000 08:01 801437		/lib/x86_64-linux-gnu/libc-2.13.so
7f22c7aa5000-7f22c7aaa000 rw-p 00000000 00:00 0
7f22c7aaa000-7f22c7ac9000 r-xp 00000000 08:01 801438		/lib/x86_64-linux-gnu/ld-2.13.so
7f22c7b36000-7f22c7cad000 r--p 00000000 08:01 1832159		/usr/lib/locale/locale-archive
7f22c7cad000-7f22c7cb0000 rw-p 00000000 00:00 0
7f22c7cc7000-7f22c7cc9000 rw-p 00000000 00:00 0
7f22c7cc9000-7f22c7cca000 r--p 0001f000 08:01 801438		/lib/x86_64-linux-gnu/ld-2.13.so
7f22c7cca000-7f22c7ccb000 rw-p 00020000 08:01 801438		/lib/x86_64-linux-gnu/ld-2.13.so
7f22c7ccb000-7f22c7ccc000 rw-p 00000000 00:00 0
7fff2e19d000-7fff2e1be000 rw-p 00000000 00:00 0			[stack]
7fff2e1ff000-7fff2e200000 r-xp 00000000 00:00 0			[vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0		[vsyscall]

$ cat /proc/18140/status


Tips:

利用pmap [pid]可以显示一个进程的内存映射。

 

perf_event 源码分析

由于工作需要, 去年12月份前后花了半个月的时间全职阅读perf代码,那时还是2.6.30的内核版本,第一次阅读内核代码,走了不少弯路,但也算收获颇丰。现在内核已经是3.0.*了,乘着国庆无事,将这部...
  • wlp600
  • wlp600
  • 2011年09月30日 18:55
  • 2951

perf_event源码分析(二)——cmd_report

__cmd_report() => perf_session_open(),打开perf.data=>perf_session_read_header()根据perf.data的头部初始化sessio...
  • wlp600
  • wlp600
  • 2011年10月15日 10:01
  • 2364

linux perf

perf是一个性能分析的tool. 类似ftrace都是/proc/等接口来获取内核的信息。 常用命令: perf list   #列出所有能够触发perf采样点的事件 $ perf list ...
  • a254373829
  • a254373829
  • 2013年02月27日 16:11
  • 3466

netmap源码分析(二)内核态关键结构的初始化

函数调用过程预览:ixgbe_netmap_attach() |-- netmap_attach() |-- _netmap_attach() |-- ...
  • fengfengdiandia
  • fengfengdiandia
  • 2016年11月03日 11:57
  • 794

asio源码分析

asio是一个分层实现的IO框架各层如下:1.basic_stream_socket(tcp::socket) --暴露给用户的接口2.socket_service --中间转换层3.io_servi...
  • itfront
  • itfront
  • 2010年09月16日 16:25
  • 1394

ndk编译 ffmpeg 1.1.1 出现libavutil/time.h 和系统的time.h 冲突问题 ;

/------------------------------------------------------------------ modify :  modify time.h -> avti...
  • gavin_liang
  • gavin_liang
  • 2014年05月19日 20:15
  • 2041

WebRTC源码分析一:音频处理流程

本文概要介绍webRTC的音频处理流程,见下图: webRTC将音频会话抽象为一个通道Channel,譬如A与B进行音频通话,则A需要建立一个Channel与B进行音频数据传输。上图中有三个Cha...
  • neustar1
  • neustar1
  • 2014年02月14日 15:18
  • 9891

ARouter解析一:基本使用及页面注册源码解析

ARouter是阿里2017年开源的页面路由框架,是今年比较火的一个开源框架,目前在Github上已经有2.2k的小星星了。ARouter是阿里巴巴开源的Android平台中对页面、服务提供路由功能的...
  • lianwudi89
  • lianwudi89
  • 2017年07月25日 22:59
  • 117

gh0st源码分析与远控的编写(三)

好久不见。距离上次写gh0st来有好久了,一是期末考试,忙不开,二是后来电脑坏了,几天没能上网。     昨天总算是把电脑修好了,虽说没到一切重头开始的地步,但是也重装各种东西花了很久。闲下来的时间...
  • liujiayu2
  • liujiayu2
  • 2015年05月28日 18:16
  • 1492

netmap源码分析(一)插入 netmap 代码到驱动程序

一、概述netmap 的简单介绍可以看我之前的一篇博客 netmap 介绍。netmap 分成两个部分:内核态部分和用户态部分使用方式:在网卡驱动中插入 netmap 相关代码,接管原来驱动程序的处理...
  • fengfengdiandia
  • fengfengdiandia
  • 2016年11月02日 13:33
  • 1377
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:perf_event源码分析(一)——cmd_record
举报原因:
原因补充:

(最多只允许输入30个字)