energy.c - energy - measure energy usage HTML git clone git://bitreich.org/energy git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/energy DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- energy.c (5734B) --- 1 // Like time(1), but for power consumption. 2 // 3 // Using the Linux RAPL interface provided in /sys, prints the energy 4 // used by the CPU while running the given command. Note that this is 5 // total system energy consumption; not just the energy consumed by 6 // the indicated command. Run this on an otherwise quiet system if 7 // you want to see power consumption of a single command. 8 // 9 // Probably needs to run with root permissions. 10 // 11 // Copyright Troels Henriksen <athas@sigkill.dk> 12 // 13 // See LICENSE file for licensing information. 14 15 // Useful resources for sensors: 16 // 17 // https://www.kernel.org/doc/html/latest/power/power_supply_class.html 18 19 #include <assert.h> 20 #include <dirent.h> 21 #include <errno.h> 22 #include <stdarg.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/stat.h> 27 #include <sys/wait.h> 28 #include <unistd.h> 29 30 // Allocates and returns a string of appropriate size. 31 static char* strprintf(const char *s, ...) { 32 va_list vl; 33 va_start(vl, s); 34 size_t needed = 1 + (size_t)vsnprintf(NULL, 0, s, vl); 35 char *buffer = (char*) malloc(needed); 36 va_start(vl, s); // Must re-init. 37 vsnprintf(buffer, needed, s, vl); 38 return buffer; 39 } 40 41 struct sensor { 42 const char *name; 43 void* data; 44 void (*start)(void*); 45 void (*end)(void*); 46 double (*usage)(void*); // Should return joules 47 }; 48 49 int num_sensors = 0; 50 int cap_sensors = 0; 51 struct sensor *sensors = NULL; 52 53 void add_sensor(struct sensor s) { 54 if (num_sensors == cap_sensors) { 55 cap_sensors += 10; 56 sensors = realloc(sensors, cap_sensors * sizeof(struct sensor)); 57 } 58 sensors[num_sensors++] = s; 59 } 60 61 long long_from_file(const char *fname) { 62 FILE *f = fopen(fname, "r"); 63 if (f == NULL) { 64 fprintf(stderr, "%s: %s\n", fname, strerror(errno)); 65 return -1; 66 } else { 67 long x; 68 if (fscanf(f, "%ld", &x) < 0) { 69 fprintf(stderr, "%s: %s\n", fname, strerror(errno)); 70 return -1; 71 } 72 fclose(f); 73 return x; 74 } 75 } 76 77 struct rapl { 78 long counter; 79 char* energy_uj; 80 }; 81 82 void sensor_rapl_start(void* data) { 83 struct rapl* rapl = (struct rapl*)data; 84 rapl->counter = long_from_file(rapl->energy_uj); 85 } 86 87 void sensor_rapl_end(void* data) { 88 struct rapl* rapl = (struct rapl*)data; 89 rapl->counter = long_from_file(rapl->energy_uj) - rapl->counter; 90 } 91 92 double sensor_rapl_usage(void* data) { 93 struct rapl* rapl = (struct rapl*)data; 94 // Convert microjoules to joules. 95 return (double)rapl->counter / 1e6; 96 } 97 98 void sensor_rapl() { 99 const char* rapl_path = "/sys/class/powercap/intel-rapl"; 100 DIR* d = opendir(rapl_path); 101 102 if (d == NULL) { 103 return; 104 } 105 106 struct dirent* dirent; 107 108 while ((dirent = readdir(d)) != NULL) { 109 if (dirent->d_type == DT_DIR) { 110 char* rapl_energy_uj = strprintf("%s/%s/energy_uj", rapl_path, dirent->d_name); 111 errno = 0; 112 FILE *f = fopen(rapl_energy_uj, "r"); 113 if (f == NULL) { 114 if (errno != ENOENT) { 115 fprintf(stderr, "%s: %s\n", rapl_energy_uj, strerror(errno)); 116 } 117 free(rapl_energy_uj); 118 } else { 119 fclose(f); 120 struct rapl *rapl = malloc(sizeof(struct rapl)); 121 rapl->energy_uj = rapl_energy_uj; 122 add_sensor((struct sensor) { .name = strdup(dirent->d_name), 123 .data = rapl, 124 .start = sensor_rapl_start, 125 .end = sensor_rapl_end, 126 .usage = sensor_rapl_usage}); 127 } 128 } 129 } 130 131 closedir(d); 132 } 133 134 const char *battery_status = 135 "/sys/class/power_supply/BAT0/status"; 136 137 const char *battery_energy = 138 "/sys/class/power_supply/BAT0/energy_now"; 139 140 void sensor_battery_start(void* data) { 141 *(long*)data = long_from_file(battery_energy); 142 } 143 144 void sensor_battery_end(void* data) { 145 *(long*)data = *(long*)data - long_from_file(battery_energy); 146 } 147 148 double sensor_battery_usage(void* data) { 149 // Convert micro-Wh to joules. 150 return ((double)*(long*)data) / 1e6 * 3600; 151 } 152 153 void sensor_battery(void) { 154 FILE *f = fopen(battery_status, "r"); 155 if (f == NULL) { 156 if (errno != ENOENT) { 157 // Don't complain just because this system does not have a 158 // battery. 159 fprintf(stderr, "%s: %s\n", battery_status, strerror(errno)); 160 } 161 } else { 162 char buf[128]; 163 if (fread(buf, 1, sizeof(buf), f) < 1) { 164 fprintf(stderr, "%s: %s\n", battery_status, strerror(errno)); 165 return; 166 } 167 const char discharging[] = "Discharging"; 168 // Measurement of battery discharge is only meaningful if the 169 // battery is actually discharging. 170 if (memcmp(buf, discharging, sizeof(discharging)-1) == 0) { 171 long* start = malloc(sizeof(long)); 172 add_sensor((struct sensor) { .name = "BAT0", 173 .data = start, 174 .start = sensor_battery_start, 175 .end = sensor_battery_end, 176 .usage = sensor_battery_usage}); 177 178 } 179 } 180 } 181 182 int main(int argc, char** argv) { 183 if (argc < 2) { 184 printf("Usage: %s <command> [args...]\n", argv[0]); 185 return 1; 186 } 187 188 sensor_rapl(); 189 sensor_battery(); 190 191 if (num_sensors == 0) { 192 fprintf(stderr, "%s: no sensors found; not running command.\n", argv[0]); 193 exit(1); 194 } 195 196 for (int i = 0; i < num_sensors; i++) { 197 sensors[i].start(sensors[i].data); 198 } 199 200 int pid; 201 if ((pid = fork()) == 0) { 202 execvp(argv[1], argv+1); 203 fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 204 exit(1); 205 } 206 207 int status; 208 waitpid(pid, &status, 0); 209 210 for (int i = 0; i < num_sensors; i++) { 211 sensors[i].end(sensors[i].data); 212 } 213 214 for (int i = 0; i < num_sensors; i++) { 215 fprintf(stderr, "%-8s %6.2f J\n", sensors[i].name, sensors[i].usage(sensors[i].data)); 216 } 217 }