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"
49 double sampletime
[MAXSAMPLES
];
50 struct ps_struct
*ps_first
;
51 struct block_stat_struct blockstat
[MAXSAMPLES
];
52 int entropy_avail
[MAXSAMPLES
];
53 struct cpu_stat_struct cpustat
[MAXCPUS
];
59 static int exiting
= 0;
65 bool relative
= false;
69 int len
= 500; /* we record len+1 (1 start sample) */
70 double hz
= 25.0; /* 20 seconds log time */
71 double scale_x
= 100.0; /* 100px = 1sec */
72 double scale_y
= 20.0; /* 16px = 1 process bar */
74 char init_path
[PATH_MAX
] = "/sbin/init";
75 char output_path
[PATH_MAX
] = "/run/log";
77 static struct rlimit rlim
;
79 static void signal_handler(int sig
)
87 int main(int argc
, char *argv
[])
89 _cleanup_free_
char *build
= NULL
;
92 char output_file
[PATH_MAX
];
96 _cleanup_fclose_
FILE *f
;
99 char *init
= NULL
, *output
= NULL
;
101 const ConfigTableItem items
[] = {
102 { "Bootchart", "Samples", config_parse_int
, 0, &len
},
103 { "Bootchart", "Frequency", config_parse_double
, 0, &hz
},
104 { "Bootchart", "Relative", config_parse_bool
, 0, &relative
},
105 { "Bootchart", "Filter", config_parse_bool
, 0, &filter
},
106 { "Bootchart", "Output", config_parse_path
, 0, &output
},
107 { "Bootchart", "Init", config_parse_path
, 0, &init
},
108 { "Bootchart", "PlotMemoryUsage", config_parse_bool
, 0, &pss
},
109 { "Bootchart", "PlotEntropyGraph", config_parse_bool
, 0, &entropy
},
110 { "Bootchart", "ScaleX", config_parse_double
, 0, &scale_x
},
111 { "Bootchart", "ScaleY", config_parse_double
, 0, &scale_y
},
112 { NULL
, NULL
, NULL
, 0, NULL
}
115 rlim
.rlim_cur
= 4096;
116 rlim
.rlim_max
= 4096;
117 (void) setrlimit(RLIMIT_NOFILE
, &rlim
);
119 fn
= "/etc/systemd/bootchart.conf";
122 r
= config_parse(fn
, f
, NULL
, config_item_table_lookup
, (void*) items
, true, NULL
);
124 log_warning("Failed to parse configuration file: %s", strerror(-r
));
127 strscpy(init_path
, sizeof(init_path
), init
);
129 strscpy(output_path
, sizeof(output_path
), output
);
133 static struct option opts
[] = {
134 {"rel", 0, NULL
, 'r'},
135 {"freq", 1, NULL
, 'f'},
136 {"samples", 1, NULL
, 'n'},
137 {"pss", 0, NULL
, 'p'},
138 {"output", 1, NULL
, 'o'},
139 {"init", 1, NULL
, 'i'},
140 {"filter", 0, NULL
, 'F'},
141 {"help", 0, NULL
, 'h'},
142 {"scale-x", 1, NULL
, 'x'},
143 {"scale-y", 1, NULL
, 'y'},
144 {"entropy", 0, NULL
, 'e'},
150 i
= getopt_long(argc
, argv
, "erpf:n:o:i:Fhx:y:", opts
, &gind
);
167 strncpy(output_path
, optarg
, PATH_MAX
- 1);
170 strncpy(init_path
, optarg
, PATH_MAX
- 1);
176 scale_x
= atof(optarg
);
179 scale_y
= atof(optarg
);
185 fprintf(stderr
, "Usage: %s [OPTIONS]\n", argv
[0]);
186 fprintf(stderr
, " --rel, -r Record time relative to recording\n");
187 fprintf(stderr
, " --freq, -f N Sample frequency [%f]\n", hz
);
188 fprintf(stderr
, " --samples, -n N Stop sampling at [%d] samples\n", len
);
189 fprintf(stderr
, " --scale-x, -x N Scale the graph horizontally [%f] \n", scale_x
);
190 fprintf(stderr
, " --scale-y, -y N Scale the graph vertically [%f] \n", scale_y
);
191 fprintf(stderr
, " --pss, -p Enable PSS graph (CPU intensive)\n");
192 fprintf(stderr
, " --entropy, -e Enable the entropy_avail graph\n");
193 fprintf(stderr
, " --output, -o [PATH] Path to output files [%s]\n", output_path
);
194 fprintf(stderr
, " --init, -i [PATH] Path to init executable [%s]\n", init_path
);
195 fprintf(stderr
, " --filter, -F Disable filtering of processes from the graph\n");
196 fprintf(stderr
, " that are of less importance or short-lived\n");
197 fprintf(stderr
, " --help, -h Display this message\n");
198 fprintf(stderr
, "See the installed README and bootchartd.conf.example for more information.\n");
206 if (len
> MAXSAMPLES
) {
207 fprintf(stderr
, "Error: samples exceeds maximum\n");
212 fprintf(stderr
, "Error: Frequency needs to be > 0\n");
217 * If the kernel executed us through init=/sbin/bootchartd, then
219 * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
225 execl(init_path
, init_path
, NULL
);
230 /* start with empty ps LL */
231 ps_first
= calloc(1, sizeof(struct ps_struct
));
233 perror("calloc(ps_struct)");
237 /* handle TERM/INT nicely */
238 memset(&sig
, 0, sizeof(struct sigaction
));
239 sig
.sa_handler
= signal_handler
;
240 sigaction(SIGHUP
, &sig
, NULL
);
242 interval
= (1.0 / hz
) * 1000000000.0;
246 /* main program loop */
256 sampletime
[samples
] = gettime_ns();
258 if (!of
&& (access(output_path
, R_OK
|W_OK
|X_OK
) == 0)) {
260 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
261 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", output_path
, datestr
);
262 of
= fopen(output_file
, "w");
266 sysfd
= open("/sys", O_RDONLY
);
270 parse_env_file("/etc/os-release", NEWLINE
,
271 "PRETTY_NAME", &build
,
275 /* wait for /proc to become available, discarding samples */
276 if (!(graph_start
> 0.0))
281 sample_stop
= gettime_ns();
283 elapsed
= (sample_stop
- sampletime
[samples
]) * 1000000000.0;
284 timeleft
= interval
- elapsed
;
286 newint_s
= (time_t)(timeleft
/ 1000000000.0);
287 newint_ns
= (long)(timeleft
- (newint_s
* 1000000000.0));
290 * check if we have not consumed our entire timeslice. If we
291 * do, don't sleep and take a new sample right away.
292 * we'll lose all the missed samples and overrun our total
295 if ((newint_ns
> 0) || (newint_s
> 0)) {
296 req
.tv_sec
= newint_s
;
297 req
.tv_nsec
= newint_ns
;
299 res
= nanosleep(&req
, NULL
);
301 if (errno
== EINTR
) {
302 /* caught signal, probably HUP! */
305 perror("nanosleep()");
310 /* calculate how many samples we lost and scrap them */
311 len
= len
+ ((int)(newint_ns
/ interval
));
321 /* do some cleanup, close fd's */
323 while (ps
->next_ps
) {
326 close(ps
->schedstat
);
335 strftime(datestr
, sizeof(datestr
), "%Y%m%d-%H%M", localtime(&t
));
336 snprintf(output_file
, PATH_MAX
, "%s/bootchart-%s.svg", output_path
, datestr
);
337 of
= fopen(output_file
, "w");
341 perror("open output_file");
347 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
);