diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 13:08:20 +1000 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2010-09-14 13:08:20 +1000 |
commit | 6a6efd31038d7afe977e3059508ae863e65cbdf5 (patch) | |
tree | 5cd69a751e893b83176751c80fcea7a7afeed1ae /init | |
parent | a6a2325ecf402054132daae169f71edb0fb849e3 (diff) | |
parent | 29082231d0cb1a5b327de5d515b16f332d4dbdaf (diff) | |
download | busybox-w32-6a6efd31038d7afe977e3059508ae863e65cbdf5.tar.gz busybox-w32-6a6efd31038d7afe977e3059508ae863e65cbdf5.tar.bz2 busybox-w32-6a6efd31038d7afe977e3059508ae863e65cbdf5.zip |
Merge branch 'origin/master' (early part)
Diffstat (limited to 'init')
-rw-r--r-- | init/Config.src | 14 | ||||
-rw-r--r-- | init/bootchartd.c | 182 |
2 files changed, 134 insertions, 62 deletions
diff --git a/init/Config.src b/init/Config.src index 2e9208150..590e29890 100644 --- a/init/Config.src +++ b/init/Config.src | |||
@@ -122,18 +122,4 @@ config MESG | |||
122 | Mesg controls access to your terminal by others. It is typically | 122 | Mesg controls access to your terminal by others. It is typically |
123 | used to allow or disallow other users to write to your terminal | 123 | used to allow or disallow other users to write to your terminal |
124 | 124 | ||
125 | config BOOTCHARTD | ||
126 | bool "bootchartd" | ||
127 | default y | ||
128 | help | ||
129 | bootchartd is commonly used to profile the boot process | ||
130 | for the purpose of speeding it up. In this case, it is started | ||
131 | by the kernel as the init process. This is configured by adding | ||
132 | the init=/sbin/bootchartd option to the kernel command line. | ||
133 | |||
134 | It can also be used to monitor the resource usage of a specific | ||
135 | application or the running system in general. In this case, | ||
136 | bootchartd is started interactively by running bootchartd start | ||
137 | and stopped using bootchartd stop. | ||
138 | |||
139 | endmenu | 125 | endmenu |
diff --git a/init/bootchartd.c b/init/bootchartd.c index d1f9ed30e..dae2fe6e9 100644 --- a/init/bootchartd.c +++ b/init/bootchartd.c | |||
@@ -2,7 +2,49 @@ | |||
2 | /* | 2 | /* |
3 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 3 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
4 | */ | 4 | */ |
5 | |||
6 | //config:config BOOTCHARTD | ||
7 | //config: bool "bootchartd" | ||
8 | //config: default y | ||
9 | //config: help | ||
10 | //config: bootchartd is commonly used to profile the boot process | ||
11 | //config: for the purpose of speeding it up. In this case, it is started | ||
12 | //config: by the kernel as the init process. This is configured by adding | ||
13 | //config: the init=/sbin/bootchartd option to the kernel command line. | ||
14 | //config: | ||
15 | //config: It can also be used to monitor the resource usage of a specific | ||
16 | //config: application or the running system in general. In this case, | ||
17 | //config: bootchartd is started interactively by running bootchartd start | ||
18 | //config: and stopped using bootchartd stop. | ||
19 | //config: | ||
20 | //config:config FEATURE_BOOTCHARTD_BLOATED_HEADER | ||
21 | //config: bool "Compatible, bloated header" | ||
22 | //config: default y | ||
23 | //config: depends on BOOTCHARTD | ||
24 | //config: help | ||
25 | //config: Create extended header file compatible with "big" bootchartd. | ||
26 | //config: "Big" bootchartd is a shell script and it dumps some | ||
27 | //config: "convenient" info int the header, such as: | ||
28 | //config: title = Boot chart for `hostname` (`date`) | ||
29 | //config: system.uname = `uname -srvm` | ||
30 | //config: system.release = `cat /etc/DISTRO-release` | ||
31 | //config: system.cpu = `grep '^model name' /proc/cpuinfo | head -1` ($cpucount) | ||
32 | //config: system.kernel.options = `cat /proc/cmdline` | ||
33 | //config: This data is not mandatory for bootchart graph generation, | ||
34 | //config: and is considered bloat. Nevertheless, this option | ||
35 | //config: makes bootchartd applet to dump a subset of it. | ||
36 | //config: | ||
37 | //config:config FEATURE_BOOTCHARTD_CONFIG_FILE | ||
38 | //config: bool "Support bootchartd.conf" | ||
39 | //config: default y | ||
40 | //config: depends on BOOTCHARTD | ||
41 | //config: help | ||
42 | //config: Enable reading and parsing of $PWD/bootchartd.conf | ||
43 | //config: and /etc/bootchartd.conf files. | ||
44 | |||
5 | #include "libbb.h" | 45 | #include "libbb.h" |
46 | /* After libbb.h, since it needs sys/types.h on some systems */ | ||
47 | #include <sys/utsname.h> | ||
6 | #include <sys/mount.h> | 48 | #include <sys/mount.h> |
7 | #ifndef MS_SILENT | 49 | #ifndef MS_SILENT |
8 | # define MS_SILENT (1 << 15) | 50 | # define MS_SILENT (1 << 15) |
@@ -19,15 +61,16 @@ | |||
19 | #define DO_SIGNAL_SYNC 1 | 61 | #define DO_SIGNAL_SYNC 1 |
20 | 62 | ||
21 | 63 | ||
22 | //Not supported: $PWD/bootchartd.conf and /etc/bootchartd.conf | 64 | //$PWD/bootchartd.conf and /etc/bootchartd.conf: |
23 | 65 | //supported options: | |
66 | //# Sampling period (in seconds) | ||
67 | //SAMPLE_PERIOD=0.2 | ||
68 | // | ||
69 | //not yet supported: | ||
24 | //# tmpfs size | 70 | //# tmpfs size |
25 | //# (32 MB should suffice for ~20 minutes worth of log data, but YMMV) | 71 | //# (32 MB should suffice for ~20 minutes worth of log data, but YMMV) |
26 | //TMPFS_SIZE=32m | 72 | //TMPFS_SIZE=32m |
27 | // | 73 | // |
28 | //# Sampling period (in seconds) | ||
29 | //SAMPLE_PERIOD=0.2 | ||
30 | // | ||
31 | //# Whether to enable and store BSD process accounting information. The | 74 | //# Whether to enable and store BSD process accounting information. The |
32 | //# kernel needs to be configured to enable v3 accounting | 75 | //# kernel needs to be configured to enable v3 accounting |
33 | //# (CONFIG_BSD_PROCESS_ACCT_V3). accton from the GNU accounting utilities | 76 | //# (CONFIG_BSD_PROCESS_ACCT_V3). accton from the GNU accounting utilities |
@@ -113,7 +156,7 @@ static int dump_procs(FILE *fp, int look_for_login_process) | |||
113 | continue; | 156 | continue; |
114 | p++; | 157 | p++; |
115 | strchrnul(p, ')')[0] = '\0'; | 158 | strchrnul(p, ')')[0] = '\0'; |
116 | /* If is gdm, kdm or a getty? */ | 159 | /* Is it gdm, kdm or a getty? */ |
117 | if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x') && p[1] == 'd' && p[2] == 'm') | 160 | if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x') && p[1] == 'd' && p[2] == 'm') |
118 | || strstr(p, "getty") | 161 | || strstr(p, "getty") |
119 | ) { | 162 | ) { |
@@ -126,7 +169,7 @@ static int dump_procs(FILE *fp, int look_for_login_process) | |||
126 | return found_login_process; | 169 | return found_login_process; |
127 | } | 170 | } |
128 | 171 | ||
129 | static char *make_tempdir(const char *prog) | 172 | static char *make_tempdir(void) |
130 | { | 173 | { |
131 | char template[] = "/tmp/bootchart.XXXXXX"; | 174 | char template[] = "/tmp/bootchart.XXXXXX"; |
132 | char *tempdir = xstrdup(mkdtemp(template)); | 175 | char *tempdir = xstrdup(mkdtemp(template)); |
@@ -151,18 +194,10 @@ static char *make_tempdir(const char *prog) | |||
151 | } else { | 194 | } else { |
152 | xchdir(tempdir); | 195 | xchdir(tempdir); |
153 | } | 196 | } |
154 | { | ||
155 | FILE *header_fp = xfopen("header", "w"); | ||
156 | if (prog) | ||
157 | fprintf(header_fp, "profile.process = %s\n", prog); | ||
158 | fputs("version = "BC_VERSION_STR"\n", header_fp); | ||
159 | fclose(header_fp); | ||
160 | } | ||
161 | |||
162 | return tempdir; | 197 | return tempdir; |
163 | } | 198 | } |
164 | 199 | ||
165 | static void do_logging(void) | 200 | static void do_logging(unsigned sample_period_us) |
166 | { | 201 | { |
167 | //# Enable process accounting if configured | 202 | //# Enable process accounting if configured |
168 | //if [ "$PROCESS_ACCOUNTING" = "yes" ]; then | 203 | //if [ "$PROCESS_ACCOUNTING" = "yes" ]; then |
@@ -175,7 +210,7 @@ static void do_logging(void) | |||
175 | //FILE *proc_netdev = xfopen("proc_netdev.log", "w"); | 210 | //FILE *proc_netdev = xfopen("proc_netdev.log", "w"); |
176 | FILE *proc_ps = xfopen("proc_ps.log", "w"); | 211 | FILE *proc_ps = xfopen("proc_ps.log", "w"); |
177 | int look_for_login_process = (getppid() == 1); | 212 | int look_for_login_process = (getppid() == 1); |
178 | unsigned count = 60*1000*1000 / (200*1000); /* ~1 minute */ | 213 | unsigned count = 60*1000*1000 / sample_period_us; /* ~1 minute */ |
179 | 214 | ||
180 | while (--count && !bb_got_signal) { | 215 | while (--count && !bb_got_signal) { |
181 | char *p; | 216 | char *p; |
@@ -200,31 +235,65 @@ static void do_logging(void) | |||
200 | /* dump_procs saw a getty or {g,k,x}dm | 235 | /* dump_procs saw a getty or {g,k,x}dm |
201 | * stop logging in 2 seconds: | 236 | * stop logging in 2 seconds: |
202 | */ | 237 | */ |
203 | if (count > 2*1000*1000 / (200*1000)) | 238 | if (count > 2*1000*1000 / sample_period_us) |
204 | count = 2*1000*1000 / (200*1000); | 239 | count = 2*1000*1000 / sample_period_us; |
205 | } | 240 | } |
206 | fflush_all(); | 241 | fflush_all(); |
207 | wait_more: | 242 | wait_more: |
208 | usleep(200*1000); | 243 | usleep(sample_period_us); |
209 | } | 244 | } |
210 | 245 | ||
211 | // [ -e kernel_pacct ] && accton off | 246 | // [ -e kernel_pacct ] && accton off |
212 | } | 247 | } |
213 | 248 | ||
214 | static void finalize(char *tempdir) | 249 | static void finalize(char *tempdir, const char *prog) |
215 | { | 250 | { |
216 | //# Stop process accounting if configured | 251 | //# Stop process accounting if configured |
217 | //local pacct= | 252 | //local pacct= |
218 | //[ -e kernel_pacct ] && pacct=kernel_pacct | 253 | //[ -e kernel_pacct ] && pacct=kernel_pacct |
219 | 254 | ||
220 | //( | 255 | FILE *header_fp = xfopen("header", "w"); |
221 | // echo "version = $VERSION" | 256 | |
222 | // echo "title = Boot chart for $( hostname | sed q ) ($( date ))" | 257 | if (prog) |
223 | // echo "system.uname = $( uname -srvm | sed q )" | 258 | fprintf(header_fp, "profile.process = %s\n", prog); |
224 | // echo "system.release = $( sed q /etc/SuSE-release )" | 259 | |
225 | // echo "system.cpu = $( grep '^model name' /proc/cpuinfo | sed q ) ($cpucount)" | 260 | fputs("version = "BC_VERSION_STR"\n", header_fp); |
226 | // echo "system.kernel.options = $( sed q /proc/cmdline )" | 261 | if (ENABLE_FEATURE_BOOTCHARTD_BLOATED_HEADER) { |
227 | //) >> header | 262 | char *hostname; |
263 | char *kcmdline; | ||
264 | time_t t; | ||
265 | struct tm tm_time; | ||
266 | /* x2 for possible localized weekday/month names */ | ||
267 | char date_buf[sizeof("Mon Jun 21 05:29:03 CEST 2010") * 2]; | ||
268 | struct utsname unamebuf; | ||
269 | |||
270 | hostname = safe_gethostname(); | ||
271 | time(&t); | ||
272 | localtime_r(&t, &tm_time); | ||
273 | strftime(date_buf, sizeof(date_buf), "%a %b %e %H:%M:%S %Z %Y", &tm_time); | ||
274 | fprintf(header_fp, "title = Boot chart for %s (%s)\n", hostname, date_buf); | ||
275 | if (ENABLE_FEATURE_CLEAN_UP) | ||
276 | free(hostname); | ||
277 | |||
278 | uname(&unamebuf); /* never fails */ | ||
279 | /* same as uname -srvm */ | ||
280 | fprintf(header_fp, "system.uname = %s %s %s %s\n", | ||
281 | unamebuf.sysname, | ||
282 | unamebuf.release, | ||
283 | unamebuf.version, | ||
284 | unamebuf.machine | ||
285 | ); | ||
286 | |||
287 | //system.release = `cat /etc/DISTRO-release` | ||
288 | //system.cpu = `grep '^model name' /proc/cpuinfo | head -1` ($cpucount) | ||
289 | |||
290 | kcmdline = xmalloc_open_read_close("/proc/cmdline", NULL); | ||
291 | /* kcmdline includes trailing "\n" */ | ||
292 | fprintf(header_fp, "system.kernel.options = %s", kcmdline); | ||
293 | if (ENABLE_FEATURE_CLEAN_UP) | ||
294 | free(kcmdline); | ||
295 | } | ||
296 | fclose(header_fp); | ||
228 | 297 | ||
229 | /* Package log files */ | 298 | /* Package log files */ |
230 | system("tar -zcf /var/log/bootchart.tgz header *.log"); // + $pacct | 299 | system("tar -zcf /var/log/bootchart.tgz header *.log"); // + $pacct |
@@ -243,19 +312,20 @@ static void finalize(char *tempdir) | |||
243 | */ | 312 | */ |
244 | } | 313 | } |
245 | 314 | ||
246 | /* Usage: | 315 | //usage:#define bootchartd_trivial_usage |
247 | * bootchartd start [PROG ARGS]: start logging in background, USR1 stops it. | 316 | //usage: "start [PROG ARGS]|stop|init" |
248 | * With PROG, runs PROG, then kills background logging. | 317 | //usage:#define bootchartd_full_usage "\n\n" |
249 | * bootchartd stop: same as "killall -USR1 bootchartd" | 318 | //usage: "Create /var/log/bootchart.tgz with boot chart data\n" |
250 | * bootchartd init: start logging in background | 319 | //usage: "\nOptions:" |
251 | * Stop when getty/gdm is seen (if AUTO_STOP_LOGGER = yes). | 320 | //usage: "\nstart: start background logging; with PROG, run PROG, then kill logging with USR1" |
252 | * Meant to be used from init scripts. | 321 | //usage: "\nstop: send USR1 to all bootchartd processes" |
253 | * bootchartd (pid==1): as init, but then execs $bootchart_init, /init, /sbin/init | 322 | //usage: "\ninit: start background logging; stop when getty/xdm is seen (for init scripts)" |
254 | * Meant to be used as kernel's init process. | 323 | //usage: "\nUnder PID 1: as init, then exec $bootchart_init, /init, /sbin/init" |
255 | */ | 324 | |
256 | int bootchartd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 325 | int bootchartd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
257 | int bootchartd_main(int argc UNUSED_PARAM, char **argv) | 326 | int bootchartd_main(int argc UNUSED_PARAM, char **argv) |
258 | { | 327 | { |
328 | unsigned sample_period_us; | ||
259 | pid_t parent_pid, logger_pid; | 329 | pid_t parent_pid, logger_pid; |
260 | smallint cmd; | 330 | smallint cmd; |
261 | enum { | 331 | enum { |
@@ -287,7 +357,25 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv) | |||
287 | cmd = CMD_PID1; | 357 | cmd = CMD_PID1; |
288 | } | 358 | } |
289 | 359 | ||
290 | /* Here we are in START or INIT state. Create logger child: */ | 360 | /* Here we are in START, INIT or CMD_PID1 state */ |
361 | |||
362 | /* Read config file: */ | ||
363 | sample_period_us = 200 * 1000; | ||
364 | if (ENABLE_FEATURE_BOOTCHARTD_CONFIG_FILE) { | ||
365 | char* token[2]; | ||
366 | parser_t *parser = config_open2("/etc/bootchartd.conf" + 5, fopen_for_read); | ||
367 | if (!parser) | ||
368 | parser = config_open2("/etc/bootchartd.conf", fopen_for_read); | ||
369 | while (config_read(parser, token, 2, 0, "#=", PARSE_NORMAL & ~PARSE_COLLAPSE)) { | ||
370 | if (strcmp(token[0], "SAMPLE_PERIOD") == 0 && token[1]) | ||
371 | sample_period_us = atof(token[1]) * 1000000; | ||
372 | } | ||
373 | config_close(parser); | ||
374 | } | ||
375 | if ((int)sample_period_us <= 0) | ||
376 | sample_period_us = 1; /* prevent division by 0 */ | ||
377 | |||
378 | /* Create logger child: */ | ||
291 | logger_pid = fork_or_rexec(argv); | 379 | logger_pid = fork_or_rexec(argv); |
292 | 380 | ||
293 | if (logger_pid == 0) { /* child */ | 381 | if (logger_pid == 0) { /* child */ |
@@ -312,9 +400,9 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv) | |||
312 | if (cmd == CMD_PID1 && !getenv("PATH")) | 400 | if (cmd == CMD_PID1 && !getenv("PATH")) |
313 | putenv((char*)bb_PATH_root_path); | 401 | putenv((char*)bb_PATH_root_path); |
314 | 402 | ||
315 | tempdir = make_tempdir(cmd == CMD_START ? argv[2] : NULL); | 403 | tempdir = make_tempdir(); |
316 | do_logging(); | 404 | do_logging(sample_period_us); |
317 | finalize(tempdir); | 405 | finalize(tempdir, cmd == CMD_START ? argv[2] : NULL); |
318 | return EXIT_SUCCESS; | 406 | return EXIT_SUCCESS; |
319 | } | 407 | } |
320 | 408 | ||
@@ -335,17 +423,15 @@ int bootchartd_main(int argc UNUSED_PARAM, char **argv) | |||
335 | execl(bootchart_init, bootchart_init, NULL); | 423 | execl(bootchart_init, bootchart_init, NULL); |
336 | execl("/init", "init", NULL); | 424 | execl("/init", "init", NULL); |
337 | execl("/sbin/init", "init", NULL); | 425 | execl("/sbin/init", "init", NULL); |
338 | bb_perror_msg_and_die("can't exec '%s'", "/sbin/init"); | 426 | bb_perror_msg_and_die("can't execute '%s'", "/sbin/init"); |
339 | } | 427 | } |
340 | 428 | ||
341 | if (cmd == CMD_START && argv[2]) { /* "start PROG ARGS" */ | 429 | if (cmd == CMD_START && argv[2]) { /* "start PROG ARGS" */ |
342 | pid_t pid = vfork(); | 430 | pid_t pid = xvfork(); |
343 | if (pid < 0) | ||
344 | bb_perror_msg_and_die("vfork"); | ||
345 | if (pid == 0) { /* child */ | 431 | if (pid == 0) { /* child */ |
346 | argv += 2; | 432 | argv += 2; |
347 | execvp(argv[0], argv); | 433 | execvp(argv[0], argv); |
348 | bb_perror_msg_and_die("can't exec '%s'", argv[0]); | 434 | bb_perror_msg_and_die("can't execute '%s'", argv[0]); |
349 | } | 435 | } |
350 | /* parent */ | 436 | /* parent */ |
351 | waitpid(pid, NULL, 0); | 437 | waitpid(pid, NULL, 0); |