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
[])
87 char output_file
[PATH_MAX
];
96 (void) setrlimit(RLIMIT_NOFILE
, &rlim
);
98 f
= fopen("/etc/systemd/bootchart.conf", "r");
104 while (fgets(buf
, 80, f
) != NULL
) {
107 c
= strchr(buf
, '\n');
108 if (c
) *c
= 0; /* remove trailing \n */
111 continue; /* comment line */
113 key
= strtok(buf
, "=");
116 val
= strtok(NULL
, "=");
120 // todo: filter leading/trailing whitespace
122 if (streq(key
, "samples"))
124 if (streq(key
, "freq"))
126 if (streq(key
, "rel"))
127 relative
= atoi(val
);
128 if (streq(key
, "filter"))
130 if (streq(key
, "pss"))
132 if (streq(key
, "output"))
133 strncpy(output_path
, val
, PATH_MAX
- 1);
134 if (streq(key
, "init"))
135 strncpy(init_path
, val
, PATH_MAX
- 1);
136 if (streq(key
, "scale_x"))
138 if (streq(key
, "scale_y"))
140 if (streq(key
, "entropy"))
147 static struct option opts
[] = {
148 {"rel", 0, NULL
, 'r'},
149 {"freq", 1, NULL
, 'f'},
150 {"samples", 1, NULL
, 'n'},
151 {"pss", 0, NULL
, 'p'},
152 {"output", 1, NULL
, 'o'},
153 {"init", 1, NULL
, 'i'},
154 {"filter", 0, NULL
, 'F'},
155 {"help", 0, NULL
, 'h'},
156 {"scale-x", 1, NULL
, 'x'},
157 {"scale-y", 1, NULL
, 'y'},
158 {"entropy", 0, NULL
, 'e'},
164 i
= getopt_long(argc
, argv
, "erpf:n:o:i:Fhx:y:", opts
, &gind
);
181 strncpy(output_path
, optarg
, PATH_MAX
- 1);
184 strncpy(init_path
, optarg
, PATH_MAX
- 1);
190 scale_x
= atof(optarg
);
193 scale_y
= atof(optarg
);
199 fprintf(stderr
, "Usage: %s [OPTIONS]\n", argv
[0]);
200 fprintf(stderr
, " --rel, -r Record time relative to recording\n");
201 fprintf(stderr
, " --freq, -f N Sample frequency [%f]\n", hz
);
202 fprintf(stderr
, " --samples, -n N Stop sampling at [%d] samples\n", len
);
203 fprintf(stderr
, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x
);
204 fprintf(stderr
, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y
);
205 fprintf(stderr
, " --pss, -p Enable PSS graph (CPU intensive)\n");
206 fprintf(stderr
, " --entropy, -e Enable the entropy_avail graph\n");
207 fprintf(stderr
, " --output, -o [PATH] Path to output files [%s]\n", output_path
);
208 fprintf(stderr
, " --init, -i [PATH] Path to init executable [%s]\n", init_path
);
209 fprintf(stderr
, " --filter, -F Disable filtering of processes from the graph\n");
210 fprintf(stderr
, " that are of less importance or short-lived\n");
211 fprintf(stderr
, " --help, -h Display this message\n");
212 fprintf(stderr
, "See the installed README and bootchartd.conf.example for more information.\n");
220 if (len
> MAXSAMPLES
) {
221 fprintf(stderr
, "Error: samples exceeds maximum\n");
226 fprintf(stderr
, "Error: Frequency needs to be > 0\n");
231 * If the kernel executed us through init=/sbin/bootchartd, then
233 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
239 execl(init_path
, init_path
, NULL
);
244 /* start with empty ps LL */
245 ps_first
= calloc(1, sizeof(struct ps_struct
));
247 perror("calloc(ps_struct)");
251 /* handle TERM/INT nicely */
252 memset(&sig
, 0, sizeof(struct sigaction
));
253 sig
.sa_handler
= signal_handler
;
254 sigaction(SIGHUP
, &sig
, NULL
);
256 interval
= (1.0 / hz
) * 1000000000.0;
260 /* main program loop */
270 sampletime
[samples
] = gettime_ns();
272 if (!of
&& (access(output_path
, R_OK
|W_OK
|X_OK
) == 0)) {
274 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
275 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", output_path
, datestr
);
276 of
= fopen(output_file
, "w");
280 sysfd
= open("/sys", O_RDONLY
);
283 /* wait for /proc to become available, discarding samples */
284 if (!(graph_start
> 0.0))
289 sample_stop
= gettime_ns();
291 elapsed
= (sample_stop
- sampletime
[samples
]) * 1000000000.0;
292 timeleft
= interval
- elapsed
;
294 newint_s
= (time_t)(timeleft
/ 1000000000.0);
295 newint_ns
= (long)(timeleft
- (newint_s
* 1000000000.0));
298 * check if we have not consumed our entire timeslice. If we
299 * do, don't sleep and take a new sample right away.
300 * we'll lose all the missed samples and overrun our total
303 if ((newint_ns
> 0) || (newint_s
> 0)) {
304 req
.tv_sec
= newint_s
;
305 req
.tv_nsec
= newint_ns
;
307 res
= nanosleep(&req
, NULL
);
309 if (errno
== EINTR
) {
310 /* caught signal, probably HUP! */
313 perror("nanosleep()");
318 /* calculate how many samples we lost and scrap them */
319 len
= len
+ ((int)(newint_ns
/ interval
));
329 /* do some cleanup, close fd's */
331 while (ps
->next_ps
) {
334 close(ps
->schedstat
);
343 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
344 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", output_path
, datestr
);
345 of
= fopen(output_file
, "w");
349 perror("open output_file");
355 fprintf(stderr
, "bootchartd: Wrote %s\n", output_file
);
361 /* nitpic cleanups */
363 while (ps
->next_ps
) {
364 struct ps_struct
*old
= ps
;
372 /* don't complain when overrun once, happens most commonly on 1st sample */
374 fprintf(stderr
, "bootchartd: Warning: sample time overrun %i times\n", overrun
);