|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +''' |
| 4 | +core_frequency.py - Low overhead BPF Tool to monitor core frequencies. |
| 5 | +Author: Saleem Ahmad (saleem.iitg[@]gmail.com) |
| 6 | +
|
| 7 | +Usage: ./core_frequency.py [-s 1] [-t 2.5] |
| 8 | +
|
| 9 | +NOTE: For Reference TSC Frequency, put the value of frequency in Model Name of |
| 10 | +lscpu output, or read 0x16 leaf of CPUID. For more information read about |
| 11 | +cpuid instruction in x86 |
| 12 | +''' |
| 13 | + |
| 14 | +from bcc import BPF, PerfType, PerfHWConfig, utils, PerfSWConfig |
| 15 | +from optparse import OptionParser |
| 16 | + |
| 17 | +parser = OptionParser() |
| 18 | +parser.add_option('-s', '--sample-freq', dest='sample_freq', help='Frequency Sample Rate in Hz', type=int, default=1) |
| 19 | +parser.add_option('-t', '--tsc-freq', dest='tsc_freq', help='Reference Clock Frequency in GHz (see lscpu or cpuid)', type=float, default=2.5) |
| 20 | +(option, args) = parser.parse_args() |
| 21 | + |
| 22 | +code = ''' |
| 23 | +# include <linux/bpf.h> |
| 24 | +# include <uapi/linux/bpf_perf_event.h> |
| 25 | +# include <uapi/linux/ptrace.h> |
| 26 | +
|
| 27 | +/* |
| 28 | +- Structure to hold the last sample data as well as to output |
| 29 | +differential data via perf event buffer. |
| 30 | +*/ |
| 31 | +struct PerfData { |
| 32 | + u64 cycles; |
| 33 | + u64 ref_cycles; |
| 34 | +}; |
| 35 | +
|
| 36 | +// Perf Events array |
| 37 | +BPF_PERF_ARRAY(cycles, MAX_CPUS); |
| 38 | +BPF_PERF_ARRAY(ref_cycles, MAX_CPUS); |
| 39 | +
|
| 40 | +// Per CPU array to store the last samples. |
| 41 | +BPF_PERCPU_ARRAY(last_sample, struct PerfData, MAX_CPUS); |
| 42 | +
|
| 43 | +// Perf Ouptut Buffer |
| 44 | +BPF_PERF_OUTPUT(output); |
| 45 | +
|
| 46 | +void get_perf_counters(struct bpf_perf_event_data* ctx) { |
| 47 | + u32 cpu = bpf_get_smp_processor_id(); |
| 48 | + /* |
| 49 | + NOTE: Use bpf_perf_event_value is recommended over |
| 50 | + bpf_perf_event_read or map.perf_read() due to |
| 51 | + issues in ABI. map.perf_read_value() need to be |
| 52 | + implemented in future. |
| 53 | + */ |
| 54 | + u64 cyc = cycles.perf_read(cpu); |
| 55 | + u64 ref = ref_cycles.perf_read(cpu); |
| 56 | +
|
| 57 | + struct PerfData result; |
| 58 | + struct PerfData* ptr = last_sample.lookup(&cpu); |
| 59 | +
|
| 60 | + if (ptr) { |
| 61 | + result.cycles = cyc - ptr->cycles; |
| 62 | + result.ref_cycles = ref - ptr->ref_cycles; |
| 63 | + ptr->cycles = cyc; |
| 64 | + ptr->ref_cycles = ref; |
| 65 | + output.perf_submit(ctx, &result, sizeof(struct PerfData)); |
| 66 | + } else { |
| 67 | + result.cycles = cyc; |
| 68 | + result.ref_cycles = ref; |
| 69 | + last_sample.insert(&cpu, &result); |
| 70 | + } |
| 71 | +} |
| 72 | +''' |
| 73 | + |
| 74 | +max_cpus = len(utils.get_online_cpus()) |
| 75 | +b = BPF(text=code, cflags=['-DMAX_CPUS=%s' % str(max_cpus)]) |
| 76 | + |
| 77 | +# Cycles and Ref Cycles counters are required to measure frequency. |
| 78 | +b['cycles'].open_perf_event(PerfType.HARDWARE, PerfHWConfig.CPU_CYCLES) |
| 79 | +b['ref_cycles'].open_perf_event(PerfType.HARDWARE, PerfHWConfig.REF_CPU_CYCLES) |
| 80 | + |
| 81 | +# A dummy perf event which will get triggered at every Sample Frequency. |
| 82 | +b.attach_perf_event(ev_type=PerfType.SOFTWARE, |
| 83 | + ev_config=PerfSWConfig.CPU_CLOCK, |
| 84 | + fn_name='get_perf_counters', |
| 85 | + sample_freq=option.sample_freq) |
| 86 | + |
| 87 | + |
| 88 | +def print_data(cpu, data, size): |
| 89 | + e = b["output"].event(data) |
| 90 | + print "%-4d %-16d %-16d %-16.2f" % (cpu, e.cycles, |
| 91 | + e.ref_cycles, |
| 92 | + e.cycles * option.tsc_freq / e.ref_cycles) |
| 93 | + |
| 94 | + |
| 95 | +print "Counters Data" |
| 96 | +print "%-4s %-16s %-16s %-16s" % ('CPU', 'CLOCK', 'REF-CYCLES', 'FREQ') |
| 97 | + |
| 98 | +b['output'].open_perf_buffer(print_data) |
| 99 | + |
| 100 | +while True: |
| 101 | + try: |
| 102 | + b.perf_buffer_poll() |
| 103 | + except KeyboardInterrupt: |
| 104 | + exit() |
0 commit comments