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 }