Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #define _GNU_SOURCE
0004 #include <errno.h>
0005 #include <stdio.h>
0006 #include <stdlib.h>
0007 #include <signal.h>
0008 #include <sched.h>
0009 #include <string.h>
0010 #include <unistd.h>
0011 #include <fcntl.h>
0012 #include <locale.h>
0013 #include <sys/types.h>
0014 #include <sys/stat.h>
0015 #include <sys/time.h>
0016 #include <sys/wait.h>
0017 
0018 #include <bpf/bpf.h>
0019 #include <bpf/libbpf.h>
0020 
0021 static int cstate_map_fd, pstate_map_fd;
0022 
0023 #define MAX_CPU         8
0024 #define MAX_PSTATE_ENTRIES  5
0025 #define MAX_CSTATE_ENTRIES  3
0026 #define MAX_STARS       40
0027 
0028 #define CPUFREQ_MAX_SYSFS_PATH  "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
0029 #define CPUFREQ_LOWEST_FREQ "208000"
0030 #define CPUFREQ_HIGHEST_FREQ    "12000000"
0031 
0032 struct cpu_stat_data {
0033     unsigned long cstate[MAX_CSTATE_ENTRIES];
0034     unsigned long pstate[MAX_PSTATE_ENTRIES];
0035 };
0036 
0037 static struct cpu_stat_data stat_data[MAX_CPU];
0038 
0039 static void cpu_stat_print(void)
0040 {
0041     int i, j;
0042     char state_str[sizeof("cstate-9")];
0043     struct cpu_stat_data *data;
0044 
0045     /* Clear screen */
0046     printf("\033[2J");
0047 
0048     /* Header */
0049     printf("\nCPU states statistics:\n");
0050     printf("%-10s ", "state(ms)");
0051 
0052     for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
0053         sprintf(state_str, "cstate-%d", i);
0054         printf("%-11s ", state_str);
0055     }
0056 
0057     for (i = 0; i < MAX_PSTATE_ENTRIES; i++) {
0058         sprintf(state_str, "pstate-%d", i);
0059         printf("%-11s ", state_str);
0060     }
0061 
0062     printf("\n");
0063 
0064     for (j = 0; j < MAX_CPU; j++) {
0065         data = &stat_data[j];
0066 
0067         printf("CPU-%-6d ", j);
0068         for (i = 0; i < MAX_CSTATE_ENTRIES; i++)
0069             printf("%-11ld ", data->cstate[i] / 1000000);
0070 
0071         for (i = 0; i < MAX_PSTATE_ENTRIES; i++)
0072             printf("%-11ld ", data->pstate[i] / 1000000);
0073 
0074         printf("\n");
0075     }
0076 }
0077 
0078 static void cpu_stat_update(int cstate_fd, int pstate_fd)
0079 {
0080     unsigned long key, value;
0081     int c, i;
0082 
0083     for (c = 0; c < MAX_CPU; c++) {
0084         for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
0085             key = c * MAX_CSTATE_ENTRIES + i;
0086             bpf_map_lookup_elem(cstate_fd, &key, &value);
0087             stat_data[c].cstate[i] = value;
0088         }
0089 
0090         for (i = 0; i < MAX_PSTATE_ENTRIES; i++) {
0091             key = c * MAX_PSTATE_ENTRIES + i;
0092             bpf_map_lookup_elem(pstate_fd, &key, &value);
0093             stat_data[c].pstate[i] = value;
0094         }
0095     }
0096 }
0097 
0098 /*
0099  * This function is copied from 'idlestat' tool function
0100  * idlestat_wake_all() in idlestate.c.
0101  *
0102  * It sets the self running task affinity to cpus one by one so can wake up
0103  * the specific CPU to handle scheduling; this results in all cpus can be
0104  * waken up once and produce ftrace event 'trace_cpu_idle'.
0105  */
0106 static int cpu_stat_inject_cpu_idle_event(void)
0107 {
0108     int rcpu, i, ret;
0109     cpu_set_t cpumask;
0110     cpu_set_t original_cpumask;
0111 
0112     ret = sysconf(_SC_NPROCESSORS_CONF);
0113     if (ret < 0)
0114         return -1;
0115 
0116     rcpu = sched_getcpu();
0117     if (rcpu < 0)
0118         return -1;
0119 
0120     /* Keep track of the CPUs we will run on */
0121     sched_getaffinity(0, sizeof(original_cpumask), &original_cpumask);
0122 
0123     for (i = 0; i < ret; i++) {
0124 
0125         /* Pointless to wake up ourself */
0126         if (i == rcpu)
0127             continue;
0128 
0129         /* Pointless to wake CPUs we will not run on */
0130         if (!CPU_ISSET(i, &original_cpumask))
0131             continue;
0132 
0133         CPU_ZERO(&cpumask);
0134         CPU_SET(i, &cpumask);
0135 
0136         sched_setaffinity(0, sizeof(cpumask), &cpumask);
0137     }
0138 
0139     /* Enable all the CPUs of the original mask */
0140     sched_setaffinity(0, sizeof(original_cpumask), &original_cpumask);
0141     return 0;
0142 }
0143 
0144 /*
0145  * It's possible to have no any frequency change for long time and cannot
0146  * get ftrace event 'trace_cpu_frequency' for long period, this introduces
0147  * big deviation for pstate statistics.
0148  *
0149  * To solve this issue, below code forces to set 'scaling_max_freq' to 208MHz
0150  * for triggering ftrace event 'trace_cpu_frequency' and then recovery back to
0151  * the maximum frequency value 1.2GHz.
0152  */
0153 static int cpu_stat_inject_cpu_frequency_event(void)
0154 {
0155     int len, fd;
0156 
0157     fd = open(CPUFREQ_MAX_SYSFS_PATH, O_WRONLY);
0158     if (fd < 0) {
0159         printf("failed to open scaling_max_freq, errno=%d\n", errno);
0160         return fd;
0161     }
0162 
0163     len = write(fd, CPUFREQ_LOWEST_FREQ, strlen(CPUFREQ_LOWEST_FREQ));
0164     if (len < 0) {
0165         printf("failed to open scaling_max_freq, errno=%d\n", errno);
0166         goto err;
0167     }
0168 
0169     len = write(fd, CPUFREQ_HIGHEST_FREQ, strlen(CPUFREQ_HIGHEST_FREQ));
0170     if (len < 0) {
0171         printf("failed to open scaling_max_freq, errno=%d\n", errno);
0172         goto err;
0173     }
0174 
0175 err:
0176     close(fd);
0177     return len;
0178 }
0179 
0180 static void int_exit(int sig)
0181 {
0182     cpu_stat_inject_cpu_idle_event();
0183     cpu_stat_inject_cpu_frequency_event();
0184     cpu_stat_update(cstate_map_fd, pstate_map_fd);
0185     cpu_stat_print();
0186     exit(0);
0187 }
0188 
0189 int main(int argc, char **argv)
0190 {
0191     struct bpf_link *link = NULL;
0192     struct bpf_program *prog;
0193     struct bpf_object *obj;
0194     char filename[256];
0195     int ret;
0196 
0197     snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
0198     obj = bpf_object__open_file(filename, NULL);
0199     if (libbpf_get_error(obj)) {
0200         fprintf(stderr, "ERROR: opening BPF object file failed\n");
0201         return 0;
0202     }
0203 
0204     prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
0205     if (!prog) {
0206         printf("finding a prog in obj file failed\n");
0207         goto cleanup;
0208     }
0209 
0210     /* load BPF program */
0211     if (bpf_object__load(obj)) {
0212         fprintf(stderr, "ERROR: loading BPF object file failed\n");
0213         goto cleanup;
0214     }
0215 
0216     cstate_map_fd = bpf_object__find_map_fd_by_name(obj, "cstate_duration");
0217     pstate_map_fd = bpf_object__find_map_fd_by_name(obj, "pstate_duration");
0218     if (cstate_map_fd < 0 || pstate_map_fd < 0) {
0219         fprintf(stderr, "ERROR: finding a map in obj file failed\n");
0220         goto cleanup;
0221     }
0222 
0223     link = bpf_program__attach(prog);
0224     if (libbpf_get_error(link)) {
0225         fprintf(stderr, "ERROR: bpf_program__attach failed\n");
0226         link = NULL;
0227         goto cleanup;
0228     }
0229 
0230     ret = cpu_stat_inject_cpu_idle_event();
0231     if (ret < 0)
0232         return 1;
0233 
0234     ret = cpu_stat_inject_cpu_frequency_event();
0235     if (ret < 0)
0236         return 1;
0237 
0238     signal(SIGINT, int_exit);
0239     signal(SIGTERM, int_exit);
0240 
0241     while (1) {
0242         cpu_stat_update(cstate_map_fd, pstate_map_fd);
0243         cpu_stat_print();
0244         sleep(5);
0245     }
0246 
0247 cleanup:
0248     bpf_link__destroy(link);
0249     bpf_object__close(obj);
0250     return 0;
0251 }