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