zoukankan      html  css  js  c++  java
  • 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函数中会不断设置

  • 相关阅读:
    js指定区域全屏
    sql中对日期的筛选
    SQL Server查询死锁,杀死进程解决死锁
    SqlServer数据类型、C#SqlDbType对应关系及转换
    用SqlDataReader返回多个结果集
    SQL重复记录查询的几种方法
    IIS支持10万个同时请求的设置
    常量与变量的命名法则
    远程服务器返回错误: (405) 不允许的方法。
    The view 'Index' or its master was not found or no view engine supports the
  • 原文地址:https://www.cnblogs.com/honpey/p/8964633.html
Copyright © 2011-2022 走看看