|
|
energy.c - energy - measure energy usage |
|
|
 |
git clone git://bitreich.org/energy git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/energy (git://bitreich.org) |
|
|
 |
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 <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 } |
|