• 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函数中会不断设置

  • 相关阅读:
    一个好的时间函数
    Codeforces 785E. Anton and Permutation
    Codeforces 785 D. Anton and School
    Codeforces 510 E. Fox And Dinner
    Codeforces 242 E. XOR on Segment
    Codeforces 629 E. Famil Door and Roads
    Codeforces 600E. Lomsat gelral(Dsu on tree学习)
    Codeforces 438D The Child and Sequence
    Codeforces 729E Subordinates
    【ATcoder】D
  • 原文地址:https://www.cnblogs.com/honpey/p/8964633.html
Copyright © 2020-2023  润新知