URI: 
       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 }