1 // SPDX-License-Identifier: GPL-2.0 2 3 #define _GNU_SOURCE 4 #include <errno.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <signal.h> 8 #include <sched.h> 9 #include <string.h> 10 #include <unistd.h> 11 #include <fcntl.h> 12 #include <linux/bpf.h> 13 #include <locale.h> 14 #include <sys/types.h> 15 #include <sys/stat.h> 16 #include <sys/time.h> 17 #include <sys/resource.h> 18 #include <sys/wait.h> 19 20 #include "libbpf.h" 21 #include "bpf_load.h" 22 23 #define MAX_CPU 8 24 #define MAX_PSTATE_ENTRIES 5 25 #define MAX_CSTATE_ENTRIES 3 26 #define MAX_STARS 40 27 28 #define CPUFREQ_MAX_SYSFS_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" 29 #define CPUFREQ_LOWEST_FREQ "208000" 30 #define CPUFREQ_HIGHEST_FREQ "12000000" 31 32 struct cpu_stat_data { 33 unsigned long cstate[MAX_CSTATE_ENTRIES]; 34 unsigned long pstate[MAX_PSTATE_ENTRIES]; 35 }; 36 37 static struct cpu_stat_data stat_data[MAX_CPU]; 38 39 static void cpu_stat_print(void) 40 { 41 int i, j; 42 char state_str[sizeof("cstate-9")]; 43 struct cpu_stat_data *data; 44 45 /* Clear screen */ 46 printf("\033[2J"); 47 48 /* Header */ 49 printf("\nCPU states statistics:\n"); 50 printf("%-10s ", "state(ms)"); 51 52 for (i = 0; i < MAX_CSTATE_ENTRIES; i++) { 53 sprintf(state_str, "cstate-%d", i); 54 printf("%-11s ", state_str); 55 } 56 57 for (i = 0; i < MAX_PSTATE_ENTRIES; i++) { 58 sprintf(state_str, "pstate-%d", i); 59 printf("%-11s ", state_str); 60 } 61 62 printf("\n"); 63 64 for (j = 0; j < MAX_CPU; j++) { 65 data = &stat_data[j]; 66 67 printf("CPU-%-6d ", j); 68 for (i = 0; i < MAX_CSTATE_ENTRIES; i++) 69 printf("%-11ld ", data->cstate[i] / 1000000); 70 71 for (i = 0; i < MAX_PSTATE_ENTRIES; i++) 72 printf("%-11ld ", data->pstate[i] / 1000000); 73 74 printf("\n"); 75 } 76 } 77 78 static void cpu_stat_update(int cstate_fd, int pstate_fd) 79 { 80 unsigned long key, value; 81 int c, i; 82 83 for (c = 0; c < MAX_CPU; c++) { 84 for (i = 0; i < MAX_CSTATE_ENTRIES; i++) { 85 key = c * MAX_CSTATE_ENTRIES + i; 86 bpf_map_lookup_elem(cstate_fd, &key, &value); 87 stat_data[c].cstate[i] = value; 88 } 89 90 for (i = 0; i < MAX_PSTATE_ENTRIES; i++) { 91 key = c * MAX_PSTATE_ENTRIES + i; 92 bpf_map_lookup_elem(pstate_fd, &key, &value); 93 stat_data[c].pstate[i] = value; 94 } 95 } 96 } 97 98 /* 99 * This function is copied from 'idlestat' tool function 100 * idlestat_wake_all() in idlestate.c. 101 * 102 * It sets the self running task affinity to cpus one by one so can wake up 103 * the specific CPU to handle scheduling; this results in all cpus can be 104 * waken up once and produce ftrace event 'trace_cpu_idle'. 105 */ 106 static int cpu_stat_inject_cpu_idle_event(void) 107 { 108 int rcpu, i, ret; 109 cpu_set_t cpumask; 110 cpu_set_t original_cpumask; 111 112 ret = sysconf(_SC_NPROCESSORS_CONF); 113 if (ret < 0) 114 return -1; 115 116 rcpu = sched_getcpu(); 117 if (rcpu < 0) 118 return -1; 119 120 /* Keep track of the CPUs we will run on */ 121 sched_getaffinity(0, sizeof(original_cpumask), &original_cpumask); 122 123 for (i = 0; i < ret; i++) { 124 125 /* Pointless to wake up ourself */ 126 if (i == rcpu) 127 continue; 128 129 /* Pointless to wake CPUs we will not run on */ 130 if (!CPU_ISSET(i, &original_cpumask)) 131 continue; 132 133 CPU_ZERO(&cpumask); 134 CPU_SET(i, &cpumask); 135 136 sched_setaffinity(0, sizeof(cpumask), &cpumask); 137 } 138 139 /* Enable all the CPUs of the original mask */ 140 sched_setaffinity(0, sizeof(original_cpumask), &original_cpumask); 141 return 0; 142 } 143 144 /* 145 * It's possible to have no any frequency change for long time and cannot 146 * get ftrace event 'trace_cpu_frequency' for long period, this introduces 147 * big deviation for pstate statistics. 148 * 149 * To solve this issue, below code forces to set 'scaling_max_freq' to 208MHz 150 * for triggering ftrace event 'trace_cpu_frequency' and then recovery back to 151 * the maximum frequency value 1.2GHz. 152 */ 153 static int cpu_stat_inject_cpu_frequency_event(void) 154 { 155 int len, fd; 156 157 fd = open(CPUFREQ_MAX_SYSFS_PATH, O_WRONLY); 158 if (fd < 0) { 159 printf("failed to open scaling_max_freq, errno=%d\n", errno); 160 return fd; 161 } 162 163 len = write(fd, CPUFREQ_LOWEST_FREQ, strlen(CPUFREQ_LOWEST_FREQ)); 164 if (len < 0) { 165 printf("failed to open scaling_max_freq, errno=%d\n", errno); 166 goto err; 167 } 168 169 len = write(fd, CPUFREQ_HIGHEST_FREQ, strlen(CPUFREQ_HIGHEST_FREQ)); 170 if (len < 0) { 171 printf("failed to open scaling_max_freq, errno=%d\n", errno); 172 goto err; 173 } 174 175 err: 176 close(fd); 177 return len; 178 } 179 180 static void int_exit(int sig) 181 { 182 cpu_stat_inject_cpu_idle_event(); 183 cpu_stat_inject_cpu_frequency_event(); 184 cpu_stat_update(map_fd[1], map_fd[2]); 185 cpu_stat_print(); 186 exit(0); 187 } 188 189 int main(int argc, char **argv) 190 { 191 char filename[256]; 192 int ret; 193 194 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 195 196 if (load_bpf_file(filename)) { 197 printf("%s", bpf_log_buf); 198 return 1; 199 } 200 201 ret = cpu_stat_inject_cpu_idle_event(); 202 if (ret < 0) 203 return 1; 204 205 ret = cpu_stat_inject_cpu_frequency_event(); 206 if (ret < 0) 207 return 1; 208 209 signal(SIGINT, int_exit); 210 signal(SIGTERM, int_exit); 211 212 while (1) { 213 cpu_stat_update(map_fd[1], map_fd[2]); 214 cpu_stat_print(); 215 sleep(5); 216 } 217 218 return 0; 219 } 220