1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Coproration
9 Auke Kok <auke-jan.h.kok@intel.com>
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.
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.
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/>.
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
37 #include <sys/types.h>
38 #include <sys/resource.h>
55 #include "conf-parser.h"
57 #include "path-util.h"
60 #include "bootchart.h"
64 double sampletime[MAXSAMPLES];
65 struct ps_struct *ps_first;
66 struct block_stat_struct blockstat[MAXSAMPLES];
67 int entropy_avail[MAXSAMPLES];
68 struct cpu_stat_struct cpustat[MAXCPUS];
74 static int exiting = 0;
78 bool arg_entropy = false;
80 bool arg_relative = false;
81 bool arg_filter = true;
82 bool arg_show_cmdline = false;
85 int arg_samples_len = 500; /* we record len+1 (1 start sample) */
86 double arg_hz = 25.0; /* 20 seconds log time */
87 double arg_scale_x = 100.0; /* 100px = 1sec */
88 double arg_scale_y = 20.0; /* 16px = 1 process bar */
90 char arg_init_path[PATH_MAX] = "/sbin/init";
91 char arg_output_path[PATH_MAX] = "/run/log";
93 static void signal_handler(int sig) {
99 int main(int argc, char *argv[]) {
100 _cleanup_free_ char *build = NULL;
101 struct sigaction sig;
102 struct ps_struct *ps;
103 char output_file[PATH_MAX];
107 _cleanup_fclose_ FILE *f;
110 char *init = NULL, *output = NULL;
113 const ConfigTableItem items[] = {
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 },
124 { NULL, NULL, NULL, 0, NULL }
127 fn = "/etc/systemd/bootchart.conf";
130 r = config_parse(fn, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
132 log_warning("Failed to parse configuration file: %s", strerror(-r));
135 strscpy(arg_init_path, sizeof(arg_init_path), init);
137 strscpy(arg_output_path, sizeof(arg_output_path), output);
141 static struct option opts[] = {
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'},
149 {"cmdline", no_argument, NULL, 'C'},
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'},
159 i = getopt_long(argc, argv, "erpf:n:o:i:FChx:y:", opts, &gind);
167 r = safe_atod(optarg, &arg_hz);
169 log_warning("failed to parse --freq/-f argument '%s': %s",
170 optarg, strerror(-r));
176 arg_show_cmdline = true;
179 r = safe_atoi(optarg, &arg_samples_len);
181 log_warning("failed to parse --samples/-n argument '%s': %s",
182 optarg, strerror(-r));
185 path_kill_slashes(optarg);
186 strscpy(arg_output_path, sizeof(arg_output_path), optarg);
189 path_kill_slashes(optarg);
190 strscpy(arg_init_path, sizeof(arg_init_path), optarg);
196 r = safe_atod(optarg, &arg_scale_x);
198 log_warning("failed to parse --scale-x/-x argument '%s': %s",
199 optarg, strerror(-r));
202 r = safe_atod(optarg, &arg_scale_y);
204 log_warning("failed to parse --scale-y/-y argument '%s': %s",
205 optarg, strerror(-r));
211 fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
212 fprintf(stderr, " --rel, -r Record time relative to recording\n");
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);
217 fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
218 fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
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);
221 fprintf(stderr, " --no-filter, -F Disable filtering of processes from the graph\n");
222 fprintf(stderr, " that are of less importance or short-lived\n");
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");
225 fprintf(stderr, " --help, -h Display this message\n");
226 fprintf(stderr, "See bootchart.conf for more information.\n");
234 if (arg_samples_len > MAXSAMPLES) {
235 fprintf(stderr, "Error: samples exceeds maximum\n");
240 fprintf(stderr, "Error: Frequency needs to be > 0\n");
245 * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
247 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
253 execl(arg_init_path, arg_init_path, NULL);
258 rlim.rlim_cur = 4096;
259 rlim.rlim_max = 4096;
260 (void) setrlimit(RLIMIT_NOFILE, &rlim);
262 /* start with empty ps LL */
263 ps_first = calloc(1, sizeof(struct ps_struct));
265 perror("calloc(ps_struct)");
269 /* handle TERM/INT nicely */
270 memset(&sig, 0, sizeof(struct sigaction));
271 sig.sa_handler = signal_handler;
272 sigaction(SIGHUP, &sig, NULL);
274 interval = (1.0 / arg_hz) * 1000000000.0;
278 /* main program loop */
288 sampletime[samples] = gettime_ns();
290 if (!of && (access(arg_output_path, R_OK|W_OK|X_OK) == 0)) {
292 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
293 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
294 of = fopen(output_file, "w");
298 sysfd = open("/sys", O_RDONLY);
301 parse_env_file("/etc/os-release", NEWLINE,
302 "PRETTY_NAME", &build,
305 /* wait for /proc to become available, discarding samples */
306 if (!(graph_start > 0.0))
311 sample_stop = gettime_ns();
313 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
314 timeleft = interval - elapsed;
316 newint_s = (time_t)(timeleft / 1000000000.0);
317 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
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
325 if ((newint_ns > 0) || (newint_s > 0)) {
326 req.tv_sec = newint_s;
327 req.tv_nsec = newint_ns;
329 res = nanosleep(&req, NULL);
331 if (errno == EINTR) {
332 /* caught signal, probably HUP! */
335 perror("nanosleep()");
340 /* calculate how many samples we lost and scrap them */
341 arg_samples_len = arg_samples_len + ((int)(newint_ns / interval));
346 if (samples > arg_samples_len)
351 /* do some cleanup, close fd's */
353 while (ps->next_ps) {
356 close(ps->schedstat);
365 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
366 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
367 of = fopen(output_file, "w");
371 fprintf(stderr, "opening output file '%s': %m\n", output_file);
377 fprintf(stderr, "systemd-bootchart wrote %s\n", output_file);
386 /* nitpic cleanups */
388 while (ps->next_ps) {
389 struct ps_struct *old = ps;
397 /* don't complain when overrun once, happens most commonly on 1st sample */
399 fprintf(stderr, "systemd-boochart: Warning: sample time overrun %i times\n", overrun);