]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/bootchart/bootchart.c
bootchart: more fixes for bootchart in the initramfs
[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 struct sigaction sig;
86 struct ps_struct *ps;
87 char output_file[PATH_MAX];
88 char datestr[200];
89 time_t t = 0;
90 FILE *f;
91 int gind;
92 int i;
93
94 rlim.rlim_cur = 4096;
95 rlim.rlim_max = 4096;
96 (void) setrlimit(RLIMIT_NOFILE, &rlim);
97
98 f = fopen("/etc/systemd/bootchart.conf", "r");
99 if (f) {
100 char buf[256];
101 char *key;
102 char *val;
103
104 while (fgets(buf, 80, f) != NULL) {
105 char *c;
106
107 c = strchr(buf, '\n');
108 if (c) *c = 0; /* remove trailing \n */
109
110 if (buf[0] == '#')
111 continue; /* comment line */
112
113 key = strtok(buf, "=");
114 if (!key)
115 continue;
116 val = strtok(NULL, "=");
117 if (!val)
118 continue;
119
120 // todo: filter leading/trailing whitespace
121
122 if (streq(key, "samples"))
123 len = atoi(val);
124 if (streq(key, "freq"))
125 hz = atof(val);
126 if (streq(key, "rel"))
127 relative = atoi(val);
128 if (streq(key, "filter"))
129 filter = atoi(val);
130 if (streq(key, "pss"))
131 pss = atoi(val);
132 if (streq(key, "output"))
133 strncpy(output_path, val, PATH_MAX - 1);
134 if (streq(key, "init"))
135 strncpy(init_path, val, PATH_MAX - 1);
136 if (streq(key, "scale_x"))
137 scale_x = atof(val);
138 if (streq(key, "scale_y"))
139 scale_y = atof(val);
140 if (streq(key, "entropy"))
141 entropy = atoi(val);
142 }
143 fclose(f);
144 }
145
146 while (1) {
147 static struct option opts[] = {
148 {"rel", 0, NULL, 'r'},
149 {"freq", 1, NULL, 'f'},
150 {"samples", 1, NULL, 'n'},
151 {"pss", 0, NULL, 'p'},
152 {"output", 1, NULL, 'o'},
153 {"init", 1, NULL, 'i'},
154 {"filter", 0, NULL, 'F'},
155 {"help", 0, NULL, 'h'},
156 {"scale-x", 1, NULL, 'x'},
157 {"scale-y", 1, NULL, 'y'},
158 {"entropy", 0, NULL, 'e'},
159 {NULL, 0, NULL, 0}
160 };
161
162 gind = 0;
163
164 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
165 if (i == -1)
166 break;
167 switch (i) {
168 case 'r':
169 relative = 1;
170 break;
171 case 'f':
172 hz = atof(optarg);
173 break;
174 case 'F':
175 filter = 0;
176 break;
177 case 'n':
178 len = atoi(optarg);
179 break;
180 case 'o':
181 strncpy(output_path, optarg, PATH_MAX - 1);
182 break;
183 case 'i':
184 strncpy(init_path, optarg, PATH_MAX - 1);
185 break;
186 case 'p':
187 pss = 1;
188 break;
189 case 'x':
190 scale_x = atof(optarg);
191 break;
192 case 'y':
193 scale_y = atof(optarg);
194 break;
195 case 'e':
196 entropy = 1;
197 break;
198 case 'h':
199 fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
200 fprintf(stderr, " --rel, -r Record time relative to recording\n");
201 fprintf(stderr, " --freq, -f N Sample frequency [%f]\n", hz);
202 fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", len);
203 fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x);
204 fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y);
205 fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
206 fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
207 fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", output_path);
208 fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", init_path);
209 fprintf(stderr, " --filter, -F Disable filtering of processes from the graph\n");
210 fprintf(stderr, " that are of less importance or short-lived\n");
211 fprintf(stderr, " --help, -h Display this message\n");
212 fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
213 exit (EXIT_SUCCESS);
214 break;
215 default:
216 break;
217 }
218 }
219
220 if (len > MAXSAMPLES) {
221 fprintf(stderr, "Error: samples exceeds maximum\n");
222 exit(EXIT_FAILURE);
223 }
224
225 if (hz <= 0.0) {
226 fprintf(stderr, "Error: Frequency needs to be > 0\n");
227 exit(EXIT_FAILURE);
228 }
229
230 /*
231 * If the kernel executed us through init=/sbin/bootchartd, then
232 * fork:
233 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
234 * - child logs data
235 */
236 if (getpid() == 1) {
237 if (fork()) {
238 /* parent */
239 execl(init_path, init_path, NULL);
240 }
241 }
242 argv[0][0] = '@';
243
244 /* start with empty ps LL */
245 ps_first = calloc(1, sizeof(struct ps_struct));
246 if (!ps_first) {
247 perror("calloc(ps_struct)");
248 exit(EXIT_FAILURE);
249 }
250
251 /* handle TERM/INT nicely */
252 memset(&sig, 0, sizeof(struct sigaction));
253 sig.sa_handler = signal_handler;
254 sigaction(SIGHUP, &sig, NULL);
255
256 interval = (1.0 / hz) * 1000000000.0;
257
258 log_uptime();
259
260 /* main program loop */
261 while (!exiting) {
262 int res;
263 double sample_stop;
264 struct timespec req;
265 time_t newint_s;
266 long newint_ns;
267 double elapsed;
268 double timeleft;
269
270 sampletime[samples] = gettime_ns();
271
272 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
273 t = time(NULL);
274 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
275 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
276 of = fopen(output_file, "w");
277 }
278
279 if (sysfd < 0) {
280 sysfd = open("/sys", O_RDONLY);
281 }
282
283 /* wait for /proc to become available, discarding samples */
284 if (!(graph_start > 0.0))
285 log_uptime();
286 else
287 log_sample(samples);
288
289 sample_stop = gettime_ns();
290
291 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
292 timeleft = interval - elapsed;
293
294 newint_s = (time_t)(timeleft / 1000000000.0);
295 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
296
297 /*
298 * check if we have not consumed our entire timeslice. If we
299 * do, don't sleep and take a new sample right away.
300 * we'll lose all the missed samples and overrun our total
301 * time
302 */
303 if ((newint_ns > 0) || (newint_s > 0)) {
304 req.tv_sec = newint_s;
305 req.tv_nsec = newint_ns;
306
307 res = nanosleep(&req, NULL);
308 if (res) {
309 if (errno == EINTR) {
310 /* caught signal, probably HUP! */
311 break;
312 }
313 perror("nanosleep()");
314 exit (EXIT_FAILURE);
315 }
316 } else {
317 overrun++;
318 /* calculate how many samples we lost and scrap them */
319 len = len + ((int)(newint_ns / interval));
320 }
321
322 samples++;
323
324 if (samples > len)
325 break;
326
327 }
328
329 /* do some cleanup, close fd's */
330 ps = ps_first;
331 while (ps->next_ps) {
332 ps = ps->next_ps;
333 if (ps->schedstat)
334 close(ps->schedstat);
335 if (ps->sched)
336 close(ps->sched);
337 if (ps->smaps)
338 fclose(ps->smaps);
339 }
340
341 if (!of) {
342 t = time(NULL);
343 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
344 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
345 of = fopen(output_file, "w");
346 }
347
348 if (!of) {
349 perror("open output_file");
350 exit (EXIT_FAILURE);
351 }
352
353 svg_do();
354
355 fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
356 fclose(of);
357
358 closedir(proc);
359 close(sysfd);
360
361 /* nitpic cleanups */
362 ps = ps_first;
363 while (ps->next_ps) {
364 struct ps_struct *old = ps;
365 ps = ps->next_ps;
366 free(old->sample);
367 free(old);
368 }
369 free(ps->sample);
370 free(ps);
371
372 /* don't complain when overrun once, happens most commonly on 1st sample */
373 if (overrun > 1)
374 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);
375
376 return 0;
377 }