ARM 的时钟周期计数保存在PMCCNTR 寄存器,不像x86用户态可以直接读取,需内核态使能,一种是在内核中使能,比如init,比较简单的是在模块中使能。
本来写了两个,arm32一个,arm64一个,方便对比合在了一起。
只测试了32位cortex-a9双核, 还有 个64位a76 a55。
enpmu.c
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/smp.h>
MODULE_AUTHOR("cn");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0");
#if !defined(__arm__) && !defined(__aarch64__)
#error module only support arm32 arm64.
#endif
#ifdef __aarch64__
typedef unsigned long ulint; //64
#elif defined __arm__
typedef unsigned int ulint; //32
#endif
static void en_access(void*)
{
ulint i=0,tmpvar=0;
#ifdef __aarch64__
asm volatile("mrs %0, mpidr_el1 ":"=r"(i));
i = (i >>8) &0xff;
#else
asm volatile("mrc p15,0,%0,c0,c0,5 ":"=r"(i));
i = i & 3;
#endif
asm volatile (
#ifdef __aarch64__
"mrs %0,pmuserenr_el0 \n"
"orr %0, %0,%1 \n"
"msr pmuserenr_el0,%0"
#else
"mrc p15, 0, %0, c9, c14, 0 \n"
"orr %0, %0,%1 \n"
"mcr p15, 0, %0, c9, c14, 0 \n"
#endif
:"+r"(tmpvar):"r"(0xf));
asm volatile(
#ifdef __aarch64__
"mrs %0, pmcr_el0 \n"
"orr %0, %0, %1 \n" //32 0x41
"bic %0, %0, %2 \n"
"msr pmcr_el0,%0 \n"
#else
"mrc p15, 0, %0, c9, c12, 0 \n"
"orr %0, %0,%1 \n"
"bic %0, %0, %2 \n"
"mcr p15, 0, %0, c9, c12, 0 \n"
#endif
:"+r"(tmpvar):"r"(0x81),"r"(0x28));
asm volatile(
#ifdef __aarch64__
"msr pmcntenset_el0,%1 \n"
"mrs %0, cntvct_el0 \n"
#else
"mcr p15, 0, %1, c9, c12, 1 \n"
"mrc p15, 0, %0, c9, c13, 0 \n"
#endif
:"=r"(tmpvar) :"r"(0xffffffff));
printk("core %lu tsc = %lx",(unsigned long)i, (unsigned long)tmpvar );
}
static void restore_access(void*) {
ulint i,tmpvar=0;
#ifdef __aarch64__
asm volatile( "mrs %0, mpidr_el1": "=r"(i));
i = (i >> 8)&0xff;
#else
asm volatile("mrc p15,0,%0,c0,c0,5 \n" : "=r"(i));
i = i & 3;
#endif
asm volatile (
#ifdef __aarch64__
"mrs %0,pmcr_el0 \n"
"bic %0,%0, %2 \n"
"msr pmcr_el0,%0\n"
"msr pmuserenr_el0,%1\n"
"mrs %0, cntvct_el0 \n"
#else
"mrc p15, 0, %0, c9, c14, 0 \n"
"bic %0,%0, %2 \n"
"mcr p15, 0, %0, c9, c14, 0 \n"
"mcr p15, 0, %1, c9, c12, 1 \n"
"mrc p15, 0, %0, c9, c13, 0 \n"
#endif
:"+r" (tmpvar):"r"(0),"r"(1));
printk("un core %lx tsc = %lx",(unsigned long)i, (unsigned long)tmpvar );
}
static int __init start(void)
{
on_each_cpu(en_access, NULL, 1);
printk(KERN_INFO "pmu access enabled\n");
return 0;
}
static void __exit stop(void)
{
on_each_cpu(restore_access, NULL, 1);
printk(KERN_INFO "pmu access disabled\n");
}
module_init(start);
module_exit(stop);
Makefile
c
obj-m = enpmu.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
直接
c
make
c
insmod enpmu.ko
rmmod enpmu
然后就可以读取了 以下arm32 arm64 x86_64读取方法
test.c
c
#include <stdio.h>
#ifndef __arm__
typedef unsigned long ulint;
#else
typedef unsigned int ulint;
#endif
int main()
{
ulint ct = 0;
#ifdef __aarch64__
asm volatile("mrs %0, cntvct_el0" :"=r"(ct));
#elif defined __arm__
asm volatile("mrc p15,0,%0, c9, c13, 0":"=r"(ct));
#elif defined __x86_64__
asm volatile("rdtsc ; shl $32, %%rdx ; or %%rdx, %0": "=a"(ct));
#endif
printf("%lx\n",(unsigned long)ct);
}
c
gcc test.c
./a.out
taskset -c 1 ./a.out
taskset -c 选择在哪个核上运行。
附录
c
CNTVCT_EL0
pmcntenset_el0 Performance Monitors Count Enable Set register
purpose Enables the Cycle Count Register·
C [31]·
0x1 >> PMCCNTR_EL0 enable·
P<m>>> PMEVCNTR<n>_EL0 enable
0xFFFFFFFF
pmuserenr_el0
Performance Monitors User Enable Register
Enable or disables EL0 access to the performance Monitors;
ER [3]
>> Event counters Read enable,
>> 1 en rw
CR [2]
>> Cycle counter Read enable·
>> 32 MRC read PMCCNTR MRRC read PMCCNTR
SW[1] software increment register Write enable
>> 1
En [0] Enable
>> Enables EL0 read/write access to PMU registers
0xF
PMCR_EL0
bit[9] Freeze-on-overflow
0
LC [6] 1 aarch32 supported long cycle
>> 0x1
DP [5] Disable cycle counter when event counting is prohibited·
0x0 not affected
D [3] clock divider··
0 pmccntr_el0 counts every clock cycle
E [0] enable
1 Affected counters are enabled by pmcntenset_el0
mrc/mcr Op1 CRm Op2 Name Type Reset Description
0 c12 0 PMCR RW 0x41093000 Performance Monitor Control Register
1 PMCNTENSET RW 0x00000000 Count Enable Set Register
2 PMCNTENCLR RW 0x00000000 Count Enable Clear Register
3 PMOVSR RW - Overflow Flag Status Register
4 PMSWINC WO - Software Increment Register
5 PMSELR RW 0x00000000 Event Counter Selection Register
c13 0 PMCCNTR RW - Cycle Count Register
1 PMXEVTYPER RW - Event Type Selection Register
2 PMXEVCNTR RW - Event Count Registers
c14 0 PMUSERENR RWa 0x00000000 User Enable Register
1 PMINTENSET RW 0x00000000 Interrupt Enable Set Register
2 PMINTENCLR RW 0x00000000 Interrupt Enable Clear Register