2 bootchart.c - This file is part of systemd-bootchart
4 Copyright (C) 2009-2013 Intel Coproration
7 Auke Kok <auke-jan.h.kok@intel.com>
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.
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.
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/>.
25 Many thanks to those who contributed ideas and code:
26 - Ziga Mahkovec - Original bootchart author
27 - Anders Norgaard - PyBootchartgui
28 - Michael Meeks - bootchart2
29 - Scott James Remnant - Ubuntu C-based logger
30 - Arjan van der Ven - for the idea to merge bootgraph.pl functionality
35 #include <sys/types.h>
36 #include <sys/resource.h>
51 #include "bootchart.h"
55 #include "conf-parser.h"
57 #include "path-util.h"
61 double sampletime[MAXSAMPLES];
62 struct ps_struct *ps_first;
63 struct block_stat_struct blockstat[MAXSAMPLES];
64 int entropy_avail[MAXSAMPLES];
65 struct cpu_stat_struct cpustat[MAXCPUS];
71 static int exiting = 0;
77 bool relative = false;
81 int len = 500; /* we record len+1 (1 start sample) */
82 double hz = 25.0; /* 20 seconds log time */
83 double scale_x = 100.0; /* 100px = 1sec */
84 double scale_y = 20.0; /* 16px = 1 process bar */
86 char init_path[PATH_MAX] = "/sbin/init";
87 char output_path[PATH_MAX] = "/run/log";
89 static struct rlimit rlim;
91 static void signal_handler(int sig)
99 int main(int argc, char *argv[])
101 _cleanup_free_ char *build = NULL;
102 struct sigaction sig;
103 struct ps_struct *ps;
104 char output_file[PATH_MAX];
108 _cleanup_fclose_ FILE *f;
111 char *init = NULL, *output = NULL;
113 const ConfigTableItem items[] = {
114 { "Bootchart", "Samples", config_parse_int, 0, &len },
115 { "Bootchart", "Frequency", config_parse_double, 0, &hz },
116 { "Bootchart", "Relative", config_parse_bool, 0, &relative },
117 { "Bootchart", "Filter", config_parse_bool, 0, &filter },
118 { "Bootchart", "Output", config_parse_path, 0, &output },
119 { "Bootchart", "Init", config_parse_path, 0, &init },
120 { "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &pss },
121 { "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &entropy },
122 { "Bootchart", "ScaleX", config_parse_double, 0, &scale_x },
123 { "Bootchart", "ScaleY", config_parse_double, 0, &scale_y },
124 { NULL, NULL, NULL, 0, NULL }
127 rlim.rlim_cur = 4096;
128 rlim.rlim_max = 4096;
129 (void) setrlimit(RLIMIT_NOFILE, &rlim);
131 fn = "/etc/systemd/bootchart.conf";
134 r = config_parse(fn, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
136 log_warning("Failed to parse configuration file: %s", strerror(-r));
139 strscpy(init_path, sizeof(init_path), init);
141 strscpy(output_path, sizeof(output_path), output);
145 static struct option opts[] = {
146 {"rel", no_argument, NULL, 'r'},
147 {"freq", required_argument, NULL, 'f'},
148 {"samples", required_argument, NULL, 'n'},
149 {"pss", no_argument, NULL, 'p'},
150 {"output", required_argument, NULL, 'o'},
151 {"init", required_argument, NULL, 'i'},
152 {"no-filter", no_argument, NULL, 'F'},
153 {"help", no_argument, NULL, 'h'},
154 {"scale-x", required_argument, NULL, 'x'},
155 {"scale-y", required_argument, NULL, 'y'},
156 {"entropy", no_argument, NULL, 'e'},
162 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
170 r = safe_atod(optarg, &hz);
172 log_warning("failed to parse --freq/-f argument '%s': %s",
173 optarg, strerror(-r));
179 r = safe_atoi(optarg, &len);
181 log_warning("failed to parse --samples/-n argument '%s': %s",
182 optarg, strerror(-r));
185 path_kill_slashes(optarg);
186 strscpy(output_path, sizeof(output_path), optarg);
189 path_kill_slashes(optarg);
190 strscpy(init_path, sizeof(init_path), optarg);
196 r = safe_atod(optarg, &scale_x);
198 log_warning("failed to parse --scale-x/-x argument '%s': %s",
199 optarg, strerror(-r));
202 r = safe_atod(optarg, &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", hz);
214 fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", len);
215 fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x);
216 fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", 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", output_path);
220 fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", 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, " --help, -h Display this message\n");
224 fprintf(stderr, "See bootchart.conf for more information.\n");
232 if (len > MAXSAMPLES) {
233 fprintf(stderr, "Error: samples exceeds maximum\n");
238 fprintf(stderr, "Error: Frequency needs to be > 0\n");
243 * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
245 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
251 execl(init_path, init_path, NULL);
256 /* start with empty ps LL */
257 ps_first = calloc(1, sizeof(struct ps_struct));
259 perror("calloc(ps_struct)");
263 /* handle TERM/INT nicely */
264 memset(&sig, 0, sizeof(struct sigaction));
265 sig.sa_handler = signal_handler;
266 sigaction(SIGHUP, &sig, NULL);
268 interval = (1.0 / hz) * 1000000000.0;
272 /* main program loop */
282 sampletime[samples] = gettime_ns();
284 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
286 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
287 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
288 of = fopen(output_file, "w");
292 sysfd = open("/sys", O_RDONLY);
296 parse_env_file("/etc/os-release", NEWLINE,
297 "PRETTY_NAME", &build,
301 /* wait for /proc to become available, discarding samples */
302 if (!(graph_start > 0.0))
307 sample_stop = gettime_ns();
309 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
310 timeleft = interval - elapsed;
312 newint_s = (time_t)(timeleft / 1000000000.0);
313 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
316 * check if we have not consumed our entire timeslice. If we
317 * do, don't sleep and take a new sample right away.
318 * we'll lose all the missed samples and overrun our total
321 if ((newint_ns > 0) || (newint_s > 0)) {
322 req.tv_sec = newint_s;
323 req.tv_nsec = newint_ns;
325 res = nanosleep(&req, NULL);
327 if (errno == EINTR) {
328 /* caught signal, probably HUP! */
331 perror("nanosleep()");
336 /* calculate how many samples we lost and scrap them */
337 len = len + ((int)(newint_ns / interval));
347 /* do some cleanup, close fd's */
349 while (ps->next_ps) {
352 close(ps->schedstat);
361 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
362 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
363 of = fopen(output_file, "w");
367 fprintf(stderr, "opening output file '%s': %m\n", output_file);
373 fprintf(stderr, "systemd-bootchart wrote %s\n", output_file);
379 /* nitpic cleanups */
381 while (ps->next_ps) {
382 struct ps_struct *old = ps;
390 /* don't complain when overrun once, happens most commonly on 1st sample */
392 fprintf(stderr, "systemd-boochart: Warning: sample time overrun %i times\n", overrun);