]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/bootchart/bootchart.c
bootchart: parse /etc/os-release rather than system-release
[thirdparty/systemd.git] / src / bootchart / bootchart.c
1 /***
2 bootchart.c - This file is part of systemd-bootchart
3
4 Copyright (C) 2009-2013 Intel Coproration
5
6 Authors:
7 Auke Kok <auke-jan.h.kok@intel.com>
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/resource.h>
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <getopt.h>
34 #include <limits.h>
35 #include <errno.h>
36 #include <fcntl.h>
37
38
39 #include "bootchart.h"
40 #include "util.h"
41 #include "fileio.h"
42
43 double graph_start;
44 double log_start;
45 double sampletime[MAXSAMPLES];
46 struct ps_struct *ps_first;
47 struct block_stat_struct blockstat[MAXSAMPLES];
48 int entropy_avail[MAXSAMPLES];
49 struct cpu_stat_struct cpustat[MAXCPUS];
50 int pscount;
51 int cpus;
52 double interval;
53 FILE *of = NULL;
54 int overrun = 0;
55 static int exiting = 0;
56 int sysfd=-1;
57
58 /* graph defaults */
59 int entropy = 0;
60 int initcall = 1;
61 int relative;
62 int filter = 1;
63 int pss = 0;
64 int samples;
65 int len = 500; /* we record len+1 (1 start sample) */
66 double hz = 25.0; /* 20 seconds log time */
67 double scale_x = 100.0; /* 100px = 1sec */
68 double scale_y = 20.0; /* 16px = 1 process bar */
69
70 char init_path[PATH_MAX] = "/sbin/init";
71 char output_path[PATH_MAX] = "/run/log";
72
73 static struct rlimit rlim;
74
75 static void signal_handler(int sig)
76 {
77 if (sig++)
78 sig--;
79 exiting = 1;
80 }
81
82
83 int main(int argc, char *argv[])
84 {
85 _cleanup_free_ char *build = NULL;
86 struct sigaction sig;
87 struct ps_struct *ps;
88 char output_file[PATH_MAX];
89 char datestr[200];
90 time_t t = 0;
91 FILE *f;
92 int gind;
93 int i;
94
95 rlim.rlim_cur = 4096;
96 rlim.rlim_max = 4096;
97 (void) setrlimit(RLIMIT_NOFILE, &rlim);
98
99 f = fopen("/etc/systemd/bootchart.conf", "r");
100 if (f) {
101 char buf[256];
102 char *key;
103 char *val;
104
105 while (fgets(buf, 80, f) != NULL) {
106 char *c;
107
108 c = strchr(buf, '\n');
109 if (c) *c = 0; /* remove trailing \n */
110
111 if (buf[0] == '#')
112 continue; /* comment line */
113
114 key = strtok(buf, "=");
115 if (!key)
116 continue;
117 val = strtok(NULL, "=");
118 if (!val)
119 continue;
120
121 // todo: filter leading/trailing whitespace
122
123 if (streq(key, "samples"))
124 len = atoi(val);
125 if (streq(key, "freq"))
126 hz = atof(val);
127 if (streq(key, "rel"))
128 relative = atoi(val);
129 if (streq(key, "filter"))
130 filter = atoi(val);
131 if (streq(key, "pss"))
132 pss = atoi(val);
133 if (streq(key, "output"))
134 strncpy(output_path, val, PATH_MAX - 1);
135 if (streq(key, "init"))
136 strncpy(init_path, val, PATH_MAX - 1);
137 if (streq(key, "scale_x"))
138 scale_x = atof(val);
139 if (streq(key, "scale_y"))
140 scale_y = atof(val);
141 if (streq(key, "entropy"))
142 entropy = atoi(val);
143 }
144 fclose(f);
145 }
146
147 while (1) {
148 static struct option opts[] = {
149 {"rel", 0, NULL, 'r'},
150 {"freq", 1, NULL, 'f'},
151 {"samples", 1, NULL, 'n'},
152 {"pss", 0, NULL, 'p'},
153 {"output", 1, NULL, 'o'},
154 {"init", 1, NULL, 'i'},
155 {"filter", 0, NULL, 'F'},
156 {"help", 0, NULL, 'h'},
157 {"scale-x", 1, NULL, 'x'},
158 {"scale-y", 1, NULL, 'y'},
159 {"entropy", 0, NULL, 'e'},
160 {NULL, 0, NULL, 0}
161 };
162
163 gind = 0;
164
165 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
166 if (i == -1)
167 break;
168 switch (i) {
169 case 'r':
170 relative = 1;
171 break;
172 case 'f':
173 hz = atof(optarg);
174 break;
175 case 'F':
176 filter = 0;
177 break;
178 case 'n':
179 len = atoi(optarg);
180 break;
181 case 'o':
182 strncpy(output_path, optarg, PATH_MAX - 1);
183 break;
184 case 'i':
185 strncpy(init_path, optarg, PATH_MAX - 1);
186 break;
187 case 'p':
188 pss = 1;
189 break;
190 case 'x':
191 scale_x = atof(optarg);
192 break;
193 case 'y':
194 scale_y = atof(optarg);
195 break;
196 case 'e':
197 entropy = 1;
198 break;
199 case 'h':
200 fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
201 fprintf(stderr, " --rel, -r Record time relative to recording\n");
202 fprintf(stderr, " --freq, -f N Sample frequency [%f]\n", hz);
203 fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", len);
204 fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x);
205 fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y);
206 fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
207 fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
208 fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", output_path);
209 fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", init_path);
210 fprintf(stderr, " --filter, -F Disable filtering of processes from the graph\n");
211 fprintf(stderr, " that are of less importance or short-lived\n");
212 fprintf(stderr, " --help, -h Display this message\n");
213 fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
214 exit (EXIT_SUCCESS);
215 break;
216 default:
217 break;
218 }
219 }
220
221 if (len > MAXSAMPLES) {
222 fprintf(stderr, "Error: samples exceeds maximum\n");
223 exit(EXIT_FAILURE);
224 }
225
226 if (hz <= 0.0) {
227 fprintf(stderr, "Error: Frequency needs to be > 0\n");
228 exit(EXIT_FAILURE);
229 }
230
231 /*
232 * If the kernel executed us through init=/sbin/bootchartd, then
233 * fork:
234 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
235 * - child logs data
236 */
237 if (getpid() == 1) {
238 if (fork()) {
239 /* parent */
240 execl(init_path, init_path, NULL);
241 }
242 }
243 argv[0][0] = '@';
244
245 /* start with empty ps LL */
246 ps_first = calloc(1, sizeof(struct ps_struct));
247 if (!ps_first) {
248 perror("calloc(ps_struct)");
249 exit(EXIT_FAILURE);
250 }
251
252 /* handle TERM/INT nicely */
253 memset(&sig, 0, sizeof(struct sigaction));
254 sig.sa_handler = signal_handler;
255 sigaction(SIGHUP, &sig, NULL);
256
257 interval = (1.0 / hz) * 1000000000.0;
258
259 log_uptime();
260
261 /* main program loop */
262 while (!exiting) {
263 int res;
264 double sample_stop;
265 struct timespec req;
266 time_t newint_s;
267 long newint_ns;
268 double elapsed;
269 double timeleft;
270
271 sampletime[samples] = gettime_ns();
272
273 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
274 t = time(NULL);
275 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
276 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
277 of = fopen(output_file, "w");
278 }
279
280 if (sysfd < 0) {
281 sysfd = open("/sys", O_RDONLY);
282 }
283
284 if (!build) {
285 parse_env_file("/etc/os-release", NEWLINE,
286 "PRETTY_NAME", &build,
287 NULL);
288 }
289
290 /* wait for /proc to become available, discarding samples */
291 if (!(graph_start > 0.0))
292 log_uptime();
293 else
294 log_sample(samples);
295
296 sample_stop = gettime_ns();
297
298 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
299 timeleft = interval - elapsed;
300
301 newint_s = (time_t)(timeleft / 1000000000.0);
302 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
303
304 /*
305 * check if we have not consumed our entire timeslice. If we
306 * do, don't sleep and take a new sample right away.
307 * we'll lose all the missed samples and overrun our total
308 * time
309 */
310 if ((newint_ns > 0) || (newint_s > 0)) {
311 req.tv_sec = newint_s;
312 req.tv_nsec = newint_ns;
313
314 res = nanosleep(&req, NULL);
315 if (res) {
316 if (errno == EINTR) {
317 /* caught signal, probably HUP! */
318 break;
319 }
320 perror("nanosleep()");
321 exit (EXIT_FAILURE);
322 }
323 } else {
324 overrun++;
325 /* calculate how many samples we lost and scrap them */
326 len = len + ((int)(newint_ns / interval));
327 }
328
329 samples++;
330
331 if (samples > len)
332 break;
333
334 }
335
336 /* do some cleanup, close fd's */
337 ps = ps_first;
338 while (ps->next_ps) {
339 ps = ps->next_ps;
340 if (ps->schedstat)
341 close(ps->schedstat);
342 if (ps->sched)
343 close(ps->sched);
344 if (ps->smaps)
345 fclose(ps->smaps);
346 }
347
348 if (!of) {
349 t = time(NULL);
350 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
351 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
352 of = fopen(output_file, "w");
353 }
354
355 if (!of) {
356 perror("open output_file");
357 exit (EXIT_FAILURE);
358 }
359
360 svg_do(build);
361
362 fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
363 fclose(of);
364
365 closedir(proc);
366 close(sysfd);
367
368 /* nitpic cleanups */
369 ps = ps_first;
370 while (ps->next_ps) {
371 struct ps_struct *old = ps;
372 ps = ps->next_ps;
373 free(old->sample);
374 free(old);
375 }
376 free(ps->sample);
377 free(ps);
378
379 /* don't complain when overrun once, happens most commonly on 1st sample */
380 if (overrun > 1)
381 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);
382
383 return 0;
384 }