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/>.
24 #include <sys/types.h>
25 #include <sys/resource.h>
40 #include "bootchart.h"
44 #include "conf-parser.h"
46 #include "path-util.h"
50 double sampletime[MAXSAMPLES];
51 struct ps_struct *ps_first;
52 struct block_stat_struct blockstat[MAXSAMPLES];
53 int entropy_avail[MAXSAMPLES];
54 struct cpu_stat_struct cpustat[MAXCPUS];
60 static int exiting = 0;
66 bool relative = false;
70 int len = 500; /* we record len+1 (1 start sample) */
71 double hz = 25.0; /* 20 seconds log time */
72 double scale_x = 100.0; /* 100px = 1sec */
73 double scale_y = 20.0; /* 16px = 1 process bar */
75 char init_path[PATH_MAX] = "/sbin/init";
76 char output_path[PATH_MAX] = "/run/log";
78 static struct rlimit rlim;
80 static void signal_handler(int sig)
88 int main(int argc, char *argv[])
90 _cleanup_free_ char *build = NULL;
93 char output_file[PATH_MAX];
97 _cleanup_fclose_ FILE *f;
100 char *init = NULL, *output = NULL;
102 const ConfigTableItem items[] = {
103 { "Bootchart", "Samples", config_parse_int, 0, &len },
104 { "Bootchart", "Frequency", config_parse_double, 0, &hz },
105 { "Bootchart", "Relative", config_parse_bool, 0, &relative },
106 { "Bootchart", "Filter", config_parse_bool, 0, &filter },
107 { "Bootchart", "Output", config_parse_path, 0, &output },
108 { "Bootchart", "Init", config_parse_path, 0, &init },
109 { "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &pss },
110 { "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &entropy },
111 { "Bootchart", "ScaleX", config_parse_double, 0, &scale_x },
112 { "Bootchart", "ScaleY", config_parse_double, 0, &scale_y },
113 { NULL, NULL, NULL, 0, NULL }
116 rlim.rlim_cur = 4096;
117 rlim.rlim_max = 4096;
118 (void) setrlimit(RLIMIT_NOFILE, &rlim);
120 fn = "/etc/systemd/bootchart.conf";
123 r = config_parse(fn, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
125 log_warning("Failed to parse configuration file: %s", strerror(-r));
128 strscpy(init_path, sizeof(init_path), init);
130 strscpy(output_path, sizeof(output_path), output);
134 static struct option opts[] = {
135 {"rel", no_argument, NULL, 'r'},
136 {"freq", required_argument, NULL, 'f'},
137 {"samples", required_argument, NULL, 'n'},
138 {"pss", no_argument, NULL, 'p'},
139 {"output", required_argument, NULL, 'o'},
140 {"init", required_argument, NULL, 'i'},
141 {"filter", no_argument, NULL, 'F'},
142 {"help", no_argument, NULL, 'h'},
143 {"scale-x", required_argument, NULL, 'x'},
144 {"scale-y", required_argument, NULL, 'y'},
145 {"entropy", no_argument, NULL, 'e'},
151 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
159 safe_atod(optarg, &hz);
165 safe_atoi(optarg, &len);
168 path_kill_slashes(optarg);
169 strscpy(output_path, sizeof(output_path), optarg);
172 path_kill_slashes(optarg);
173 strscpy(init_path, sizeof(init_path), optarg);
179 safe_atod(optarg, &scale_x);
182 safe_atod(optarg, &scale_y);
188 fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
189 fprintf(stderr, " --rel, -r Record time relative to recording\n");
190 fprintf(stderr, " --freq, -f N Sample frequency [%f]\n", hz);
191 fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", len);
192 fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x);
193 fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y);
194 fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
195 fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
196 fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", output_path);
197 fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", init_path);
198 fprintf(stderr, " --filter, -F Disable filtering of processes from the graph\n");
199 fprintf(stderr, " that are of less importance or short-lived\n");
200 fprintf(stderr, " --help, -h Display this message\n");
201 fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
209 if (len > MAXSAMPLES) {
210 fprintf(stderr, "Error: samples exceeds maximum\n");
215 fprintf(stderr, "Error: Frequency needs to be > 0\n");
220 * If the kernel executed us through init=/sbin/bootchartd, then
222 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
228 execl(init_path, init_path, NULL);
233 /* start with empty ps LL */
234 ps_first = calloc(1, sizeof(struct ps_struct));
236 perror("calloc(ps_struct)");
240 /* handle TERM/INT nicely */
241 memset(&sig, 0, sizeof(struct sigaction));
242 sig.sa_handler = signal_handler;
243 sigaction(SIGHUP, &sig, NULL);
245 interval = (1.0 / hz) * 1000000000.0;
249 /* main program loop */
259 sampletime[samples] = gettime_ns();
261 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
263 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
264 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
265 of = fopen(output_file, "w");
269 sysfd = open("/sys", O_RDONLY);
273 parse_env_file("/etc/os-release", NEWLINE,
274 "PRETTY_NAME", &build,
278 /* wait for /proc to become available, discarding samples */
279 if (!(graph_start > 0.0))
284 sample_stop = gettime_ns();
286 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
287 timeleft = interval - elapsed;
289 newint_s = (time_t)(timeleft / 1000000000.0);
290 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
293 * check if we have not consumed our entire timeslice. If we
294 * do, don't sleep and take a new sample right away.
295 * we'll lose all the missed samples and overrun our total
298 if ((newint_ns > 0) || (newint_s > 0)) {
299 req.tv_sec = newint_s;
300 req.tv_nsec = newint_ns;
302 res = nanosleep(&req, NULL);
304 if (errno == EINTR) {
305 /* caught signal, probably HUP! */
308 perror("nanosleep()");
313 /* calculate how many samples we lost and scrap them */
314 len = len + ((int)(newint_ns / interval));
324 /* do some cleanup, close fd's */
326 while (ps->next_ps) {
329 close(ps->schedstat);
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");
344 perror("open output_file");
350 fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
356 /* nitpic cleanups */
358 while (ps->next_ps) {
359 struct ps_struct *old = ps;
367 /* don't complain when overrun once, happens most commonly on 1st sample */
369 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);