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