1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Coproration
9 Auke Kok <auke-jan.h.kok@intel.com>
11 systemd is free software; you can redistribute it and/or modify it
12 under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2.1 of the License, or
14 (at your option) any later version.
16 systemd is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public License
22 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 Many thanks to those who contributed ideas and code:
28 - Ziga Mahkovec - Original bootchart author
29 - Anders Norgaard - PyBootchartgui
30 - Michael Meeks - bootchart2
31 - Scott James Remnant - Ubuntu C-based logger
32 - Arjan van der Ven - for the idea to merge bootgraph.pl functionality
37 #include <sys/types.h>
38 #include <sys/resource.h>
55 #include "conf-parser.h"
57 #include "path-util.h"
60 #include "bootchart.h"
64 double sampletime
[MAXSAMPLES
];
65 struct ps_struct
*ps_first
;
66 struct block_stat_struct blockstat
[MAXSAMPLES
];
67 int entropy_avail
[MAXSAMPLES
];
68 struct cpu_stat_struct cpustat
[MAXCPUS
];
74 static int exiting
= 0;
78 bool arg_entropy
= false;
80 bool arg_relative
= false;
81 bool arg_filter
= true;
82 bool arg_show_cmdline
= false;
85 int arg_samples_len
= 500; /* we record len+1 (1 start sample) */
86 double arg_hz
= 25.0; /* 20 seconds log time */
87 double arg_scale_x
= 100.0; /* 100px = 1sec */
88 double arg_scale_y
= 20.0; /* 16px = 1 process bar */
90 char arg_init_path
[PATH_MAX
] = "/sbin/init";
91 char arg_output_path
[PATH_MAX
] = "/run/log";
93 static struct rlimit rlim
;
95 static void signal_handler(int sig
) {
101 int main(int argc
, char *argv
[]) {
102 _cleanup_free_
char *build
= NULL
;
103 struct sigaction sig
;
104 struct ps_struct
*ps
;
105 char output_file
[PATH_MAX
];
109 _cleanup_fclose_
FILE *f
;
112 char *init
= NULL
, *output
= NULL
;
114 const ConfigTableItem items
[] = {
115 { "Bootchart", "Samples", config_parse_int
, 0, &arg_samples_len
},
116 { "Bootchart", "Frequency", config_parse_double
, 0, &arg_hz
},
117 { "Bootchart", "Relative", config_parse_bool
, 0, &arg_relative
},
118 { "Bootchart", "Filter", config_parse_bool
, 0, &arg_filter
},
119 { "Bootchart", "Output", config_parse_path
, 0, &output
},
120 { "Bootchart", "Init", config_parse_path
, 0, &init
},
121 { "Bootchart", "PlotMemoryUsage", config_parse_bool
, 0, &arg_pss
},
122 { "Bootchart", "PlotEntropyGraph", config_parse_bool
, 0, &arg_entropy
},
123 { "Bootchart", "ScaleX", config_parse_double
, 0, &arg_scale_x
},
124 { "Bootchart", "ScaleY", config_parse_double
, 0, &arg_scale_y
},
125 { NULL
, NULL
, NULL
, 0, NULL
}
128 rlim
.rlim_cur
= 4096;
129 rlim
.rlim_max
= 4096;
130 (void) setrlimit(RLIMIT_NOFILE
, &rlim
);
132 fn
= "/etc/systemd/bootchart.conf";
135 r
= config_parse(fn
, f
, NULL
, config_item_table_lookup
, (void*) items
, true, NULL
);
137 log_warning("Failed to parse configuration file: %s", strerror(-r
));
140 strscpy(arg_init_path
, sizeof(arg_init_path
), init
);
142 strscpy(arg_output_path
, sizeof(arg_output_path
), output
);
146 static struct option opts
[] = {
147 {"rel", no_argument
, NULL
, 'r'},
148 {"freq", required_argument
, NULL
, 'f'},
149 {"samples", required_argument
, NULL
, 'n'},
150 {"pss", no_argument
, NULL
, 'p'},
151 {"output", required_argument
, NULL
, 'o'},
152 {"init", required_argument
, NULL
, 'i'},
153 {"no-filter", no_argument
, NULL
, 'F'},
154 {"cmdline", no_argument
, NULL
, 'C'},
155 {"help", no_argument
, NULL
, 'h'},
156 {"scale-x", required_argument
, NULL
, 'x'},
157 {"scale-y", required_argument
, NULL
, 'y'},
158 {"entropy", no_argument
, NULL
, 'e'},
164 i
= getopt_long(argc
, argv
, "erpf:n:o:i:FChx:y:", opts
, &gind
);
172 r
= safe_atod(optarg
, &arg_hz
);
174 log_warning("failed to parse --freq/-f argument '%s': %s",
175 optarg
, strerror(-r
));
181 arg_show_cmdline
= true;
184 r
= safe_atoi(optarg
, &arg_samples_len
);
186 log_warning("failed to parse --samples/-n argument '%s': %s",
187 optarg
, strerror(-r
));
190 path_kill_slashes(optarg
);
191 strscpy(arg_output_path
, sizeof(arg_output_path
), optarg
);
194 path_kill_slashes(optarg
);
195 strscpy(arg_init_path
, sizeof(arg_init_path
), optarg
);
201 r
= safe_atod(optarg
, &arg_scale_x
);
203 log_warning("failed to parse --scale-x/-x argument '%s': %s",
204 optarg
, strerror(-r
));
207 r
= safe_atod(optarg
, &arg_scale_y
);
209 log_warning("failed to parse --scale-y/-y argument '%s': %s",
210 optarg
, strerror(-r
));
216 fprintf(stderr
, "Usage: %s [OPTIONS]\n", argv
[0]);
217 fprintf(stderr
, " --rel, -r Record time relative to recording\n");
218 fprintf(stderr
, " --freq, -f f Sample frequency [%f]\n", arg_hz
);
219 fprintf(stderr
, " --samples, -n N Stop sampling at [%d] samples\n", arg_samples_len
);
220 fprintf(stderr
, " --scale-x, -x N Scale the graph horizontally [%f] \n", arg_scale_x
);
221 fprintf(stderr
, " --scale-y, -y N Scale the graph vertically [%f] \n", arg_scale_y
);
222 fprintf(stderr
, " --pss, -p Enable PSS graph (CPU intensive)\n");
223 fprintf(stderr
, " --entropy, -e Enable the entropy_avail graph\n");
224 fprintf(stderr
, " --output, -o [PATH] Path to output files [%s]\n", arg_output_path
);
225 fprintf(stderr
, " --init, -i [PATH] Path to init executable [%s]\n", arg_init_path
);
226 fprintf(stderr
, " --no-filter, -F Disable filtering of processes from the graph\n");
227 fprintf(stderr
, " that are of less importance or short-lived\n");
228 fprintf(stderr
, " --cmdline, -C Display the full command line with arguments\n");
229 fprintf(stderr
, " of processes, instead of only the process name\n");
230 fprintf(stderr
, " --help, -h Display this message\n");
231 fprintf(stderr
, "See bootchart.conf for more information.\n");
239 if (arg_samples_len
> MAXSAMPLES
) {
240 fprintf(stderr
, "Error: samples exceeds maximum\n");
245 fprintf(stderr
, "Error: Frequency needs to be > 0\n");
250 * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
252 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
258 execl(arg_init_path
, arg_init_path
, NULL
);
263 /* start with empty ps LL */
264 ps_first
= calloc(1, sizeof(struct ps_struct
));
266 perror("calloc(ps_struct)");
270 /* handle TERM/INT nicely */
271 memset(&sig
, 0, sizeof(struct sigaction
));
272 sig
.sa_handler
= signal_handler
;
273 sigaction(SIGHUP
, &sig
, NULL
);
275 interval
= (1.0 / arg_hz
) * 1000000000.0;
279 /* main program loop */
289 sampletime
[samples
] = gettime_ns();
291 if (!of
&& (access(arg_output_path
, R_OK
|W_OK
|X_OK
) == 0)) {
293 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
294 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", arg_output_path
, datestr
);
295 of
= fopen(output_file
, "w");
299 sysfd
= open("/sys", O_RDONLY
);
302 parse_env_file("/etc/os-release", NEWLINE
,
303 "PRETTY_NAME", &build
,
306 /* wait for /proc to become available, discarding samples */
307 if (!(graph_start
> 0.0))
312 sample_stop
= gettime_ns();
314 elapsed
= (sample_stop
- sampletime
[samples
]) * 1000000000.0;
315 timeleft
= interval
- elapsed
;
317 newint_s
= (time_t)(timeleft
/ 1000000000.0);
318 newint_ns
= (long)(timeleft
- (newint_s
* 1000000000.0));
321 * check if we have not consumed our entire timeslice. If we
322 * do, don't sleep and take a new sample right away.
323 * we'll lose all the missed samples and overrun our total
326 if ((newint_ns
> 0) || (newint_s
> 0)) {
327 req
.tv_sec
= newint_s
;
328 req
.tv_nsec
= newint_ns
;
330 res
= nanosleep(&req
, NULL
);
332 if (errno
== EINTR
) {
333 /* caught signal, probably HUP! */
336 perror("nanosleep()");
341 /* calculate how many samples we lost and scrap them */
342 arg_samples_len
= arg_samples_len
+ ((int)(newint_ns
/ interval
));
347 if (samples
> arg_samples_len
)
352 /* do some cleanup, close fd's */
354 while (ps
->next_ps
) {
357 close(ps
->schedstat
);
366 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
367 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", arg_output_path
, datestr
);
368 of
= fopen(output_file
, "w");
372 fprintf(stderr
, "opening output file '%s': %m\n", output_file
);
378 fprintf(stderr
, "systemd-bootchart wrote %s\n", output_file
);
387 /* nitpic cleanups */
389 while (ps
->next_ps
) {
390 struct ps_struct
*old
= ps
;
398 /* don't complain when overrun once, happens most commonly on 1st sample */
400 fprintf(stderr
, "systemd-boochart: Warning: sample time overrun %i times\n", overrun
);