perf事件的切换

perf事件的切换发生在函数perf_event_task_sched_in  

finish_task_switch函数中调用perf_event_task_sche_in

prepare_task_switch ---> finish_task_switch

理一下发生进程切换时的行为,perfs是注册到每个cpu上的,这是就有一个问题了,对于非进程的级的事,他是yon停歇的,但是对于进程的事件,那是要发生qiehua的,需要把本进程的计数器给stop掉,zheyan就不对我这个进程进行统计了,进程级别其实是keyi分时的,

如果都是系统级别的事件,那肯定是不能发生分时复用的

如何查看机器上PMU寄存器的个数;

如何查看机器上PMU寄存器的个数;

一个事件被分配一个寄存器吗?

x86_pmu.num_counters

x86_pmu_hw_config --> x86_setup_perfctr

func(x86_setup_perfctr) hwConfig(0x130000) type(0) PERF_RAW(4)
 0xffffffff81006170 : x86_setup_perfctr+0x0/0x170 [kernel]
 0xffffffff81006396 : x86_pmu_hw_config+0xb6/0x1c0 [kernel]
 0xffffffff8100b732 : intel_pmu_hw_config+0x12/0x130 [kernel]
 0xffffffff8100b862 : hsw_hw_config+0x12/0xb0 [kernel]
 0xffffffff81005e85 : x86_pmu_event_init+0xd5/0x1f0 [kernel]
 0xffffffff8117b426 : perf_try_init_event+0x76/0x90 [kernel]
 0xffffffff8117edb9 : perf_event_alloc+0x5a9/0x6d0 [kernel]
 0xffffffff81181b88 : SYSC_perf_event_open+0x3c8/0xdb0 [kernel]
 0xffffffff81184809 : sys_perf_event_open+0x9/0x10 [kernel]
 0xffffffff81824626 : tracesys_phase2+0x88/0x8d [kernel]
硬件事件肯定在哪个地方会分配硬件PMU

x86_assign_hw_event

 0xffffffff81006cd6 : x86_pmu_enable+0x116/0x300 [kernel]
 0xffffffff8117a8f7 : perf_pmu_enable.part.90+0x7/0x10 [kernel]
 0xffffffff8117f541 : perf_pmu_enable+0x21/0x30 [kernel]
 0xffffffff810052d0 : x86_pmu_commit_txn+0xd0/0x130 [kernel]
 0xffffffff8117cba7 : group_sched_in+0x1a7/0x1c0 [kernel]
 0xffffffff8117d8fc : __perf_event_enable+0x25c/0x290 [kernel]
 0xffffffff8117a3fa : remote_function+0x3a/0x40 [kernel]
 0xffffffff811038c6 : generic_exec_single+0xb6/0x120 [kernel]
 0xffffffff811039fe : smp_call_function_single+0xce/0x130 [kernel]
 0xffffffff8117dabc : _perf_event_enable+0x12c/0x140 [kernel]
 0xffffffff81178838 : perf_event_for_each_child+0x38/0xa0 [kernel]
 0xffffffff8118269e : perf_ioctl+0x12e/0x4c0 [kernel]
 0xffffffff812200ff : do_vfs_ioctl+0x29f/0x490 [kernel]
 0xffffffff81220369 : sys_ioctl+0x79/0x90 [kernel]
 0xffffffff81824626 : tracesys_phase2+0x88/0x8d [kernel]

1078          * step2: reprogram moved events into new counters
1079          */
1080         for (i = 0; i < cpuc->n_events; i++) {
1081             event = cpuc->event_list[i]; [又是啥时候把这个事件放到了全局的CPU->event_list里去的]
1082             hwc = &event->hw;
1083
1084             if (!match_prev_assignment(hwc, cpuc, i))
1085                 x86_assign_hw_event(event, cpuc, i);
1086             else if (i < n_running)
1087                 continue;
1088
1089             if (hwc->state & PERF_HES_ARCH)
1090                 continue;
1091
1092             x86_pmu_start(event, PERF_EF_RELOAD);
1093         }
那什么时候会分配这个值呢?

在x86_pmu_start中会有

1249 static void x86_pmu_start(struct perf_event *event, int flags)
1250 {   
1251     struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
1252     int idx = event->hw.idx;
1253     
1254     if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
1255         return;     
1256     
1257     if (WARN_ON_ONCE(idx == -1))
1258         return;
1259     
1260     if (flags & PERF_EF_RELOAD) {
1261         WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
1262         x86_perf_event_set_period(event);
1263     }
1264     
1265     event->hw.state = 0;
1266     
1267     cpuc->events[idx] = event;
1268     __set_bit(idx, cpuc->active_mask);
1269     __set_bit(idx, cpuc->running);
1270     x86_pmu.enable(event);
1271     perf_event_update_userpage(event);
1272 }
那又是啥时候给event->hw.idx 分配数值的呢?

x86_pmu_add

 831 /*
 832  * Assign a counter for each event.
 833  */
 834 int perf_assign_events(struct event_constraint **constraints, int n,
 835             int wmin, int wmax, int gpmax, int *assign)
 836 {
 837     struct perf_sched sched;
 838
 839     perf_sched_init(&sched, constraints, n, wmin, wmax, gpmax);
 840
 841     do {
 842         if (!perf_sched_find_counter(&sched))
 843             break;  /* failed */
 844         if (assign)
 845             assign[sched.state.  ] = sched.state.counter;
 846     } while (perf_sched_next_event(&sched));
 847
 848     return sched.state.unassigned;
 849 }
 850 EXPORT_SYMBOL_GPL(perf_assign_events);

x86_pmu_enable的时候会更新上这个值

看下event_constraints函数中event->hw.config中会有

pmu的初始化过程也是一个有意思的过程

event_constraint 结构体

 45 struct event_constraint {
 46     union {
 47         unsigned long   idxmsk[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
 48         u64     idxmsk64;
 49     };
 50     u64 code;
 51     u64 cmask;
 52     int weight; weight中是idxmsk64中1的个数,是事件数吧?
 53     int overlap;
 54     int flags;
 55 };

下面是我机器上的intel_hsw_event_constraints中的

event_constraints In this station: intel_hsw_event_constraints
---------event_constraint---------
idxmask:1000000ff, code:c0, cmask:3ff84ffff, weight:9, overlap:0, flags:0
idxmask:2000000ff, code:3c, cmask:3ff84ffff, weight:9, overlap:0, flags:0
idxmask:400000000, code:300, cmask:3ff84ffff, weight:1, overlap:0, flags:0
idxmask:4, code:148, cmask:ffff, weight:1, overlap:0, flags:0
idxmask:2, code:1c0, cmask:ffff, weight:1, overlap:0, flags:0
idxmask:8, code:cd, cmask:ff, weight:1, overlap:0, flags:0
idxmask:4, code:8a3, cmask:ffff, weight:1, overlap:0, flags:0
idxmask:4, code:ca3, cmask:ffff, weight:1, overlap:0, flags:0
idxmask:f, code:4a3, cmask:ffff, weight:4, overlap:0, flags:0

idxmask:ff, code:0, cmask:0, weight:8, overlap:0, flags:0 [unconstriand的事件,就是系统原生支持的事件  ]

某个事件是如何分配寄存器的


idxmask:f, code:d0, cmask:ff, weight:4, overlap:0, flags:40
idxmask:f, code:d1, cmask:ff, weight:4, overlap:0, flags:40
idxmask:f, code:d2, cmask:ff, weight:4, overlap:0, flags:40
idxmask:f, code:d3, cmask:ff, weight:4, overlap:0, flags:40

不受约束的事件:

   unconstrained = (struct event_constraint)
        __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1,
                   0, x86_pmu.num_counters, 0, 0);

下面再来看下,sys_perf_event_open传递进来的参数是怎么体现事件的

283 struct perf_event_attr {
284 
285     /*
286      * Major type: hardware/software/tracepoint/etc.
287      */
288     __u32           type;
289 
290     /*
291      * Size of the attr structure, for fwd/bwd compat.
292      */
293     __u32           size;
294 
295     /*
296      * Type specific configuration information.
297      */
298     __u64           config;  具体的事件类型, 比如硬件事件 instructions/bus-cycles事件等,这个事件怎么和真正的PMU对应起来捏?
299 
300     union {
301         __u64       sample_period;
302         __u64       sample_freq;
303     };
304 
305     __u64           sample_type;
306     __u64           read_format;
307 
308     __u64           disabled       :  1, /* off by default        */
309                 inherit        :  1, /* children inherit it   */
310                 pinned         :  1, /* must always be on PMU */
311                 exclusive      :  1, /* only group on PMU     */
312                 exclude_user   :  1, /* don't count user      */
313                 exclude_kernel :  1, /* ditto kernel          */
314                 exclude_hv     :  1, /* ditto hypervisor      */
315                 exclude_idle   :  1, /* don't count when idle */
316                 mmap           :  1, /* include mmap data     */
317                 comm           :  1, /* include comm data     */
318                 freq           :  1, /* use freq, not period  */
319                 inherit_stat   :  1, /* per task counts       */
320                 enable_on_exec :  1, /* next exec enables     */
321                 task           :  1, /* trace fork/exit       */
322                 watermark      :  1, /* wakeup_watermark      */
323                 /*

  发生关联的地方在:

func(x86_setup_perfctr) hwConfig(0x130000) type(0) PERF_RAW(4)
 0xffffffff81006170 : x86_setup_perfctr+0x0/0x170 [kernel]
 0xffffffff81006396 : x86_pmu_hw_config+0xb6/0x1c0 [kernel]
 0xffffffff8100b732 : intel_pmu_hw_config+0x12/0x130 [kernel]
 0xffffffff8100b862 : hsw_hw_config+0x12/0xb0 [kernel]
 0xffffffff81005e85 : x86_pmu_event_init+0xd5/0x1f0 [kernel]
 0xffffffff8117b426 : perf_try_init_event+0x76/0x90 [kernel]
 0xffffffff8117edb9 : perf_event_alloc+0x5a9/0x6d0 [kernel]
 0xffffffff81181b88 : SYSC_perf_event_open+0x3c8/0xdb0 [kernel]
 0xffffffff81184809 : sys_perf_event_open+0x9/0x10 [kernel]
 0xffffffff81824626 : tracesys_phase2+0x88/0x8d [kernel]

在这个函数中有操作: event->hw.config |= event->attr.config & (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);

在生成perf_event结构体时,perf_event->attr会初始化的

perf_event_alloc函数中有如下语句会初始化:

 9103     event->attr     = *attr;
然后,看下hw.config是怎么写到的寄存器里面  

intel_pmu_enable_event在这里会把具体的config写入到寄存器中去

event.attr.config: 0x6
event.hw.config: 0x13013c
 0xffffffff8100be20 : intel_pmu_enable_event+0x0/0x220 [kernel]
 0xffffffff81006b3e : x86_pmu_start+0x7e/0x100 [kernel]
 0xffffffff8117f921 : perf_event_task_tick+0x2a1/0x2d0 [kernel]
 0xffffffff810ad31b : scheduler_tick+0x7b/0xd0 [kernel]

event.hw.config中本来就是有值的,但是

13412e

1300c0

109301c2  

https://blog.csdn.net/edonlii/article/details/8686130

这篇文章中有介绍了MSR寄存器每个位的意义:

IA32_PERFEVTSELx寄存器的bit位布局如下:
0-7:Event select field,事件选择字段 (所以perf用户态的代码里也有大量的&255这样的操作 )在哪里能够体现
8-15:Unit mask (UMASK) field,事件检测掩码字段
16:USR (user mode) flag,设置仅对用户模式(privilege levels 1, 2 or 3)进行计数,可以和OS flag一起使用。
17:OS (operating system mode) flag,设置仅对内核模式(privilege levels 0)进行计数,可以和USR flag一起使用。
18:E (edge detect) flag
19:PC (pin control) flag,如果设置为1,那么当性能监视事件发生时,逻辑处理器就会增加一个计数并且“toggles the PMi pins”;如果清零,那么当性能计数溢出时,处理器就会“toggles the PMi pins”。“toggles the PMi pins”不好翻译,其具体定义为:“The toggling of a pin is defined as assertion of the pin for a single bus clock followed by deassertion.”,对于此处,我的理解也就是把PMi针脚激活一下,从而触发一个PMI中断。
20:INT (APIC interrupt enable) flag,如果设置为1,当性能计数溢出时,就会通过local APIC来触发逻辑处理器产生一个异常。
21:保留
22:EN (Enable Counters) Flag,如果设置为1,性能计数器生效,否则被禁用。
23:INV (invert) flag,控制是否对Counter mask结果进行反转。
24-31:Counter mask (CMASK) field,如果该字段不为0,那么只有在单个时钟周期内发生的事件数大于等于该值时,对应的计数器才自增1。这就可以用于统计每个时钟周期内发生多次的事件。如果该字段为0,那么计数器就以每时钟周期按具体发生的事件数进行增长。
32-63:保留

比如,如果我要监控6号事件,那么我只能去填充

看下x86_setup_perfctr

config = x86_pmu.event_map(attr->config); // intel_pmu_event_map

在这里会对事件做一个映射的。。。。

原来perf attr。config都是intel_perfmon_event_map中的索引。。。。。

 27 static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly =
  28 {
  29     [PERF_COUNT_HW_CPU_CYCLES]      = 0x003c,
  30     [PERF_COUNT_HW_INSTRUCTIONS]        = 0x00c0,
  31     [PERF_COUNT_HW_CACHE_REFERENCES]    = 0x4f2e,
  32     [PERF_COUNT_HW_CACHE_MISSES]        = 0x412e,
  33     [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4,
  34     [PERF_COUNT_HW_BRANCH_MISSES]       = 0x00c5,
  35     [PERF_COUNT_HW_BUS_CYCLES]      = 0x013c,
  36     [PERF_COUNT_HW_REF_CPU_CYCLES]      = 0x0300, /* pseudo-encoding */
  37 };
于是这么看,事件应该是占用16个bit了呀

这个是事件寄存

x86_pmu_hw_cofig --> x86_setup_perfctr --> event_map 设置事件的路径;

那么下个问题就是,我执行perf top -e cycles -e instructions 这个在内核里是几个事件?

使用systemtap的guru模式,想在中断处理函数x86_pmu_handle_irq中查看到底有多少个事件挂在这个CPU上

头文件

在函数intel_pmu_handle_irq中抓取事件,然后用perf去抓取所有的硬件事件,intel上有一个size是64的数组,数组的每一个槽能容纳一个事件,然后就等着事件发中断去接受事件,看代码是这样的. 所以向MSR寄存器去注册事件的时候,除了要注册具体的事件类型,还要注册这个事件的idx,要不然驱动怎么知道去找哪个perf_event呢?那这样就还有一个问题了,总共有64个槽,那么如果事件多于64个咋办?比如,我有128个进程都去申请instructions事件咋办?

写个程序测试一下:

如果进程总共的个数

问题来了,可否两个进程共享一个perf_event事件?

一个cgroup组的进程是贡献一个cgroup组的事件么?

64个,刚才用perf直接生成了84个,也是可以的,也就是说在同一个cpu上挂了84个事件也是可以的

是怎么完成的?

只有perf top才会触发intel_pmu_handle_irq中断的,我们通过perf_event_open打开根本就不能所以intel_pmu_handle_irq的中断还是在特定时候开启的,

所以现在的问题就是了

和抓取的时间就对上了

所以到这里也不难理解,所以这就是perf的采样的功能了

采样的功能,看下内核中到底是怎么处理的sample_freq, PMU寄存器的值

这是MSR寄存器的溢出时间,

采样的周期放在了sample_period变量中

在x86_perf_event_set_period函数中会不断设置

 

posted @ 2018-04-27 21:38  honpey  阅读(1598)  评论(0编辑  收藏  举报