/* * cpu_governor_monitor - Simple CLI tool to sample the current CPU governor * and log it together with a caller-supplied C-state indicator. */ #include #include #include #include #include #include #define GOVERNOR_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" struct cpu_log_entry { char governor[30]; int c_state; time_t timestamp; }; static void trim_newline(char *str) { if (!str) { return; } size_t len = strcspn(str, "\r\n"); str[len] = '\0'; } const char *get_current_governor(void) { static char governor_buf[30] = "unknown"; FILE *file = fopen(GOVERNOR_PATH, "r"); if (!file) { perror("fopen scaling_governor"); return governor_buf; } if (!fgets(governor_buf, sizeof(governor_buf), file)) { perror("fgets scaling_governor"); snprintf(governor_buf, sizeof(governor_buf), "unknown"); fclose(file); return governor_buf; } fclose(file); trim_newline(governor_buf); return governor_buf; } void log_cpu_state(const char *governor, int c_state) { struct cpu_log_entry entry; memset(&entry, 0, sizeof(entry)); if (governor) { strncpy(entry.governor, governor, sizeof(entry.governor) - 1); } else { strncpy(entry.governor, "unknown", sizeof(entry.governor) - 1); } entry.c_state = c_state; entry.timestamp = time(NULL); char ts_buf[32]; struct tm tm_info; if (localtime_r(&entry.timestamp, &tm_info) != NULL) { strftime(ts_buf, sizeof(ts_buf), "%Y-%m-%d %H:%M:%S", &tm_info); } else { snprintf(ts_buf, sizeof(ts_buf), "unknown_time"); } printf("timestamp=%s (epoch=%ld) governor=%s c_state=%d\n", ts_buf, (long)entry.timestamp, entry.governor, entry.c_state); } static void print_usage(const char *prog) { fprintf(stderr, "Usage: %s [c_state]\n", prog); fprintf(stderr, "If c_state is omitted, -1 is logged to indicate unknown.\n"); } static int parse_c_state(const char *arg, int *c_state) { char *endptr = NULL; long value = strtol(arg, &endptr, 10); if (endptr == arg || *endptr != '\0') { return -1; } if (value < INT_MIN || value > INT_MAX) { return -1; } *c_state = (int)value; return 0; } int main(int argc, char *argv[]) { int c_state = -1; if (argc > 2) { print_usage(argv[0]); return EXIT_FAILURE; } if (argc == 2) { if (parse_c_state(argv[1], &c_state) != 0) { fprintf(stderr, "Invalid c_state value: %s\n", argv[1]); return EXIT_FAILURE; } } const char *governor = get_current_governor(); log_cpu_state(governor, c_state); return EXIT_SUCCESS; }