energy.c - energy - measure energy usage | |
git clone git://bitreich.org/energy git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
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 <[email protected]> | |
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, dir… | |
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… | |
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(… | |
216 } | |
217 } |