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>
37 #include "bootchart.h"
43 double sampletime
[MAXSAMPLES
];
44 struct ps_struct
*ps_first
;
45 struct block_stat_struct blockstat
[MAXSAMPLES
];
46 int entropy_avail
[MAXSAMPLES
];
47 struct cpu_stat_struct cpustat
[MAXCPUS
];
53 static int exiting
= 0;
62 int len
= 500; /* we record len+1 (1 start sample) */
63 double hz
= 25.0; /* 20 seconds log time */
64 double scale_x
= 100.0; /* 100px = 1sec */
65 double scale_y
= 20.0; /* 16px = 1 process bar */
67 char init_path
[PATH_MAX
] = "/sbin/init";
68 char output_path
[PATH_MAX
] = "/run/log";
70 static struct rlimit rlim
;
72 static void signal_handler(int sig
)
80 int main(int argc
, char *argv
[])
84 char output_file
[PATH_MAX
];
93 (void) setrlimit(RLIMIT_NOFILE
, &rlim
);
95 f
= fopen("/etc/systemd/bootchart.conf", "r");
101 while (fgets(buf
, 80, f
) != NULL
) {
104 c
= strchr(buf
, '\n');
105 if (c
) *c
= 0; /* remove trailing \n */
108 continue; /* comment line */
110 key
= strtok(buf
, "=");
113 val
= strtok(NULL
, "=");
117 // todo: filter leading/trailing whitespace
119 if (streq(key
, "samples"))
121 if (streq(key
, "freq"))
123 if (streq(key
, "rel"))
124 relative
= atoi(val
);
125 if (streq(key
, "filter"))
127 if (streq(key
, "pss"))
129 if (streq(key
, "output"))
130 strncpy(output_path
, val
, PATH_MAX
- 1);
131 if (streq(key
, "init"))
132 strncpy(init_path
, val
, PATH_MAX
- 1);
133 if (streq(key
, "scale_x"))
135 if (streq(key
, "scale_y"))
137 if (streq(key
, "entropy"))
144 static struct option opts
[] = {
145 {"rel", 0, NULL
, 'r'},
146 {"freq", 1, NULL
, 'f'},
147 {"samples", 1, NULL
, 'n'},
148 {"pss", 0, NULL
, 'p'},
149 {"output", 1, NULL
, 'o'},
150 {"init", 1, NULL
, 'i'},
151 {"filter", 0, NULL
, 'F'},
152 {"help", 0, NULL
, 'h'},
153 {"scale-x", 1, NULL
, 'x'},
154 {"scale-y", 1, NULL
, 'y'},
155 {"entropy", 0, NULL
, 'e'},
161 i
= getopt_long(argc
, argv
, "erpf:n:o:i:Fhx:y:", opts
, &gind
);
178 strncpy(output_path
, optarg
, PATH_MAX
- 1);
181 strncpy(init_path
, optarg
, PATH_MAX
- 1);
187 scale_x
= atof(optarg
);
190 scale_y
= atof(optarg
);
196 fprintf(stderr
, "Usage: %s [OPTIONS]\n", argv
[0]);
197 fprintf(stderr
, " --rel, -r Record time relative to recording\n");
198 fprintf(stderr
, " --freq, -f N Sample frequency [%f]\n", hz
);
199 fprintf(stderr
, " --samples, -n N Stop sampling at [%d] samples\n", len
);
200 fprintf(stderr
, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x
);
201 fprintf(stderr
, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y
);
202 fprintf(stderr
, " --pss, -p Enable PSS graph (CPU intensive)\n");
203 fprintf(stderr
, " --entropy, -e Enable the entropy_avail graph\n");
204 fprintf(stderr
, " --output, -o [PATH] Path to output files [%s]\n", output_path
);
205 fprintf(stderr
, " --init, -i [PATH] Path to init executable [%s]\n", init_path
);
206 fprintf(stderr
, " --filter, -F Disable filtering of processes from the graph\n");
207 fprintf(stderr
, " that are of less importance or short-lived\n");
208 fprintf(stderr
, " --help, -h Display this message\n");
209 fprintf(stderr
, "See the installed README and bootchartd.conf.example for more information.\n");
217 if (len
> MAXSAMPLES
) {
218 fprintf(stderr
, "Error: samples exceeds maximum\n");
223 fprintf(stderr
, "Error: Frequency needs to be > 0\n");
228 * If the kernel executed us through init=/sbin/bootchartd, then
230 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
236 execl(init_path
, init_path
, NULL
);
241 /* start with empty ps LL */
242 ps_first
= calloc(1, sizeof(struct ps_struct
));
244 perror("calloc(ps_struct)");
248 /* handle TERM/INT nicely */
249 memset(&sig
, 0, sizeof(struct sigaction
));
250 sig
.sa_handler
= signal_handler
;
251 sigaction(SIGHUP
, &sig
, NULL
);
253 interval
= (1.0 / hz
) * 1000000000.0;
257 /* main program loop */
267 sampletime
[samples
] = gettime_ns();
269 if (!of
&& (access(output_path
, R_OK
|W_OK
|X_OK
) == 0)) {
271 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
272 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", output_path
, datestr
);
273 of
= fopen(output_file
, "w");
277 /* wait for /proc to become available, discarding samples */
278 if (!(graph_start
> 0.0))
283 sample_stop
= gettime_ns();
285 elapsed
= (sample_stop
- sampletime
[samples
]) * 1000000000.0;
286 timeleft
= interval
- elapsed
;
288 newint_s
= (time_t)(timeleft
/ 1000000000.0);
289 newint_ns
= (long)(timeleft
- (newint_s
* 1000000000.0));
292 * check if we have not consumed our entire timeslice. If we
293 * do, don't sleep and take a new sample right away.
294 * we'll lose all the missed samples and overrun our total
297 if ((newint_ns
> 0) || (newint_s
> 0)) {
298 req
.tv_sec
= newint_s
;
299 req
.tv_nsec
= newint_ns
;
301 res
= nanosleep(&req
, NULL
);
303 if (errno
== EINTR
) {
304 /* caught signal, probably HUP! */
307 perror("nanosleep()");
312 /* calculate how many samples we lost and scrap them */
313 len
= len
+ ((int)(newint_ns
/ interval
));
323 /* do some cleanup, close fd's */
325 while (ps
->next_ps
) {
328 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
);
353 /* nitpic cleanups */
355 while (ps
->next_ps
) {
356 struct ps_struct
*old
= ps
;
364 /* don't complain when overrun once, happens most commonly on 1st sample */
366 fprintf(stderr
, "bootchartd: Warning: sample time overrun %i times\n", overrun
);