Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.