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>
39 #include "bootchart.h"
45 double sampletime[MAXSAMPLES];
46 struct ps_struct *ps_first;
47 struct block_stat_struct blockstat[MAXSAMPLES];
48 int entropy_avail[MAXSAMPLES];
49 struct cpu_stat_struct cpustat[MAXCPUS];
55 static int exiting = 0;
65 int len = 500; /* we record len+1 (1 start sample) */
66 double hz = 25.0; /* 20 seconds log time */
67 double scale_x = 100.0; /* 100px = 1sec */
68 double scale_y = 20.0; /* 16px = 1 process bar */
70 char init_path[PATH_MAX] = "/sbin/init";
71 char output_path[PATH_MAX] = "/run/log";
73 static struct rlimit rlim;
75 static void signal_handler(int sig)
83 int main(int argc, char *argv[])
85 _cleanup_free_ char *build = NULL;
88 char output_file[PATH_MAX];
97 (void) setrlimit(RLIMIT_NOFILE, &rlim);
99 f = fopen("/etc/systemd/bootchart.conf", "r");
105 while (fgets(buf, 80, f) != NULL) {
108 c = strchr(buf, '\n');
109 if (c) *c = 0; /* remove trailing \n */
112 continue; /* comment line */
114 key = strtok(buf, "=");
117 val = strtok(NULL, "=");
121 // todo: filter leading/trailing whitespace
123 if (streq(key, "samples"))
125 if (streq(key, "freq"))
127 if (streq(key, "rel"))
128 relative = atoi(val);
129 if (streq(key, "filter"))
131 if (streq(key, "pss"))
133 if (streq(key, "output"))
134 strncpy(output_path, val, PATH_MAX - 1);
135 if (streq(key, "init"))
136 strncpy(init_path, val, PATH_MAX - 1);
137 if (streq(key, "scale_x"))
139 if (streq(key, "scale_y"))
141 if (streq(key, "entropy"))
148 static struct option opts[] = {
149 {"rel", 0, NULL, 'r'},
150 {"freq", 1, NULL, 'f'},
151 {"samples", 1, NULL, 'n'},
152 {"pss", 0, NULL, 'p'},
153 {"output", 1, NULL, 'o'},
154 {"init", 1, NULL, 'i'},
155 {"filter", 0, NULL, 'F'},
156 {"help", 0, NULL, 'h'},
157 {"scale-x", 1, NULL, 'x'},
158 {"scale-y", 1, NULL, 'y'},
159 {"entropy", 0, NULL, 'e'},
165 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
182 strncpy(output_path, optarg, PATH_MAX - 1);
185 strncpy(init_path, optarg, PATH_MAX - 1);
191 scale_x = atof(optarg);
194 scale_y = atof(optarg);
200 fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
201 fprintf(stderr, " --rel, -r Record time relative to recording\n");
202 fprintf(stderr, " --freq, -f N Sample frequency [%f]\n", hz);
203 fprintf(stderr, " --samples, -n N Stop sampling at [%d] samples\n", len);
204 fprintf(stderr, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x);
205 fprintf(stderr, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y);
206 fprintf(stderr, " --pss, -p Enable PSS graph (CPU intensive)\n");
207 fprintf(stderr, " --entropy, -e Enable the entropy_avail graph\n");
208 fprintf(stderr, " --output, -o [PATH] Path to output files [%s]\n", output_path);
209 fprintf(stderr, " --init, -i [PATH] Path to init executable [%s]\n", init_path);
210 fprintf(stderr, " --filter, -F Disable filtering of processes from the graph\n");
211 fprintf(stderr, " that are of less importance or short-lived\n");
212 fprintf(stderr, " --help, -h Display this message\n");
213 fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
221 if (len > MAXSAMPLES) {
222 fprintf(stderr, "Error: samples exceeds maximum\n");
227 fprintf(stderr, "Error: Frequency needs to be > 0\n");
232 * If the kernel executed us through init=/sbin/bootchartd, then
234 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
240 execl(init_path, init_path, NULL);
245 /* start with empty ps LL */
246 ps_first = calloc(1, sizeof(struct ps_struct));
248 perror("calloc(ps_struct)");
252 /* handle TERM/INT nicely */
253 memset(&sig, 0, sizeof(struct sigaction));
254 sig.sa_handler = signal_handler;
255 sigaction(SIGHUP, &sig, NULL);
257 interval = (1.0 / hz) * 1000000000.0;
261 /* main program loop */
271 sampletime[samples] = gettime_ns();
273 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
275 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
276 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
277 of = fopen(output_file, "w");
281 sysfd = open("/sys", O_RDONLY);
285 parse_env_file("/etc/os-release", NEWLINE,
286 "PRETTY_NAME", &build,
290 /* wait for /proc to become available, discarding samples */
291 if (!(graph_start > 0.0))
296 sample_stop = gettime_ns();
298 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
299 timeleft = interval - elapsed;
301 newint_s = (time_t)(timeleft / 1000000000.0);
302 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
305 * check if we have not consumed our entire timeslice. If we
306 * do, don't sleep and take a new sample right away.
307 * we'll lose all the missed samples and overrun our total
310 if ((newint_ns > 0) || (newint_s > 0)) {
311 req.tv_sec = newint_s;
312 req.tv_nsec = newint_ns;
314 res = nanosleep(&req, NULL);
316 if (errno == EINTR) {
317 /* caught signal, probably HUP! */
320 perror("nanosleep()");
325 /* calculate how many samples we lost and scrap them */
326 len = len + ((int)(newint_ns / interval));
336 /* do some cleanup, close fd's */
338 while (ps->next_ps) {
341 close(ps->schedstat);
350 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
351 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
352 of = fopen(output_file, "w");
356 perror("open output_file");
362 fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
368 /* nitpic cleanups */
370 while (ps->next_ps) {
371 struct ps_struct *old = ps;
379 /* don't complain when overrun once, happens most commonly on 1st sample */
381 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);