arm32 arm64 读取PMCCNTR cpu cycle counter

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
相关推荐
7yewh23 分钟前
嵌入式硬件实战提升篇(一)-泰山派RK3566制作多功能小手机
linux·arm开发·驱动开发·嵌入式硬件·物联网·智能手机·硬件架构
YRr YRr1 小时前
ubuntu ros 解决建完图后 保存的地图非常小的问题
linux·运维·ubuntu
脸ル粉嘟嘟1 小时前
常用命令之Linux&Oracle&Hive&Python
linux·hive·oracle
Wils0nEdwards1 小时前
Leetcode 整数转罗马数字
linux·python·leetcode
五味香2 小时前
Linux学习,ip 命令
linux·服务器·c语言·开发语言·git·学习·tcp/ip
朱容君3 小时前
Linux系统编程多线程之读写锁讲解
linux·开发语言
大风吹PP凉3 小时前
38配置管理工具(如Ansible、Puppet、Chef)
linux·运维·服务器·ansible·puppet
康熙38bdc4 小时前
Linux 进程间通信——共享内存
linux·运维·服务器
jwybobo20074 小时前
redis7.x源码分析:(3) dict字典
linux·redis
scoone4 小时前
ssh登陆服务器后支持Tab键命令补全
linux·shell