├── example ├── test │ ├── fail │ └── hello ├── walb │ ├── .gitignore │ ├── test │ ├── init │ ├── Makefile │ ├── build │ └── install ├── module │ ├── lock │ │ ├── lock-torture │ │ ├── Makefile │ │ ├── lock1.c │ │ └── lock2.c │ ├── hello │ │ ├── Makefile │ │ └── hello.c │ ├── list │ │ ├── Makefile │ │ ├── list1.c │ │ └── list2.c │ ├── debugfs │ │ ├── Makefile │ │ ├── debugfs1.c │ │ ├── debugfs2.c │ │ └── debugfs3.c │ └── timer │ │ ├── Makefile │ │ ├── timer1.c │ │ ├── timer2.c │ │ ├── timer3.c │ │ └── timer4.c └── kernel-patch │ ├── bisect │ ├── 0001-1-10-fine.patch │ ├── 0002-2-10-fine.patch │ ├── 0007-7-10-BUG.patch │ ├── 0008-8-10-BUG.patch │ ├── 0009-9-10-BUG.patch │ ├── 0003-3-10-fine.patch │ ├── 0004-4-10-fine.patch │ ├── 0005-5-10-fine.patch │ ├── 0010-10-10-BUG.patch │ └── 0006-6-10-BUG.patch │ ├── first │ └── 0001-Print-a-message-on-boot.patch │ └── patchcheck │ ├── 0001-1-4-fine.patch │ ├── 0002-2-4-fine.patch │ ├── 0004-4-4-fine.patch │ └── 0003-3-4-BUG.patch ├── .github └── FUNDING.yml ├── up ├── halt ├── login ├── reload ├── fini ├── .gitignore ├── elkdat ├── ktest-grub-menuentry.template ├── Vagrantfile └── Vagrantfile.walb ├── init ├── ktest ├── ktest.conf.template └── ktest.pl ├── test ├── README.md └── LICENSE /example/test/fail: -------------------------------------------------------------------------------- 1 | ${SSH_BIN} false -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: satoru-takeuchi 2 | -------------------------------------------------------------------------------- /example/test/hello: -------------------------------------------------------------------------------- 1 | ${SSH_BIN} echo "Hello world!" 2 | -------------------------------------------------------------------------------- /example/walb/.gitignore: -------------------------------------------------------------------------------- 1 | walb-driver/* 2 | walb-tools/* -------------------------------------------------------------------------------- /up: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd elkdat 4 | vagrant up 5 | -------------------------------------------------------------------------------- /halt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd elkdat 4 | vagrant halt 5 | -------------------------------------------------------------------------------- /login: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd elkdat 4 | vagrant ssh 5 | -------------------------------------------------------------------------------- /reload: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd elkdat 4 | vagrant reload 5 | -------------------------------------------------------------------------------- /example/walb/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKDIR=$(dirname $0) 4 | 5 | pushd ${WORKDIR} 6 | make install 7 | echo "Hello walb auto test!" 8 | popd 9 | -------------------------------------------------------------------------------- /fini: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pushd elkdat 4 | vagrant halt 5 | vagrant destroy ktest 6 | popd 7 | 8 | echo "Finished to cleanup." 9 | echo "Now it's safe to delete the source directory." 10 | -------------------------------------------------------------------------------- /example/walb/init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test -d walb-driver || git clone https://github.com/walb-linux/walb-driver.git 4 | test -d walb-tools || git clone https://github.com/walb-linux/walb-tools.git 5 | -------------------------------------------------------------------------------- /example/walb/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all build install test 2 | 3 | all: install 4 | 5 | build: 6 | ./build 7 | 8 | install: build 9 | ./install 10 | 11 | test: install 12 | ./test 13 | 14 | init: 15 | ./init 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ko 3 | *.mod 4 | *.cmd 5 | *.mod.c 6 | *~ 7 | Module.symvers 8 | *.mod.c 9 | modules.order 10 | private_key 11 | ktest/ktest.log 12 | ktest/ktest.conf 13 | output/* 14 | linux 15 | linux/* 16 | .vagrant 17 | -------------------------------------------------------------------------------- /elkdat/ktest-grub-menuentry.template: -------------------------------------------------------------------------------- 1 | menuentry 'ktest' { 2 | linux /boot/ktest-vmlinuz root=UUID=@ROOT_UUID@ ro console=tty1 console=tty0 net.ifnames=0 console=ttyS0,115200n8 3 | initrd /boot/ktest-initramfs.img 4 | } 5 | -------------------------------------------------------------------------------- /example/module/lock/lock-torture: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TOPDIR=/sys/kernel/debug/mystack 4 | 5 | for ((i=0;j<1000000;j++)) ; do 6 | echo 0 >${TOPDIR}/push & 7 | cat ${TOPDIR}/show >/dev/null & 8 | cat ${TOPDIR}/pop >/dev/null & 9 | done 10 | -------------------------------------------------------------------------------- /example/walb/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | KERNELDIR=$(realpath ../../output) 4 | 5 | build_driver() { 6 | pushd walb-driver/module 7 | make KERNELDIR=${KERNELDIR} -j8 8 | popd 9 | } 10 | , 11 | build_tools() { 12 | pushd walb-tools 13 | make -j8 14 | popd 15 | } 16 | 17 | build_driver 18 | build_tools 19 | -------------------------------------------------------------------------------- /example/module/hello/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean install login 2 | 3 | obj-m := hello.o 4 | 5 | all: 6 | make -C ../../../output M=$(PWD) modules 7 | 8 | clean: 9 | make -C ../../../linux M=$(PWD) clean 10 | 11 | install: 12 | cp *.ko ../../../elkdat; cd ../../../elkdat; vagrant rsync 13 | 14 | login: 15 | cd ../../../elkdat; vagrant ssh 16 | -------------------------------------------------------------------------------- /example/module/list/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean install login 2 | 3 | obj-m := list1.o list2.o 4 | 5 | all: 6 | make -C ../../../output M=$(PWD) modules 7 | 8 | clean: 9 | make -C ../../../linux M=$(PWD) clean 10 | 11 | install: 12 | cp *.ko ../../../elkdat; cd ../../../elkdat; vagrant rsync 13 | 14 | login: 15 | cd ../../../elkdat; vagrant ssh 16 | -------------------------------------------------------------------------------- /example/module/lock/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean install login 2 | 3 | obj-m := lock1.o lock2.o 4 | 5 | all: 6 | make -C ../../../output M=$(PWD) modules 7 | 8 | clean: 9 | make -C ../../../linux M=$(PWD) clean 10 | 11 | install: 12 | cp lock-torture *.ko ../../../elkdat; cd ../../../elkdat; vagrant rsync 13 | 14 | login: 15 | cd ../../../elkdat; vagrant ssh 16 | -------------------------------------------------------------------------------- /example/module/debugfs/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean install login 2 | 3 | obj-m := debugfs1.o debugfs2.o debugfs3.o 4 | 5 | all: 6 | make -C ../../../output M=$(PWD) modules 7 | 8 | clean: 9 | make -C ../../../linux M=$(PWD) clean 10 | 11 | install: 12 | cp *.ko ../../../elkdat; cd ../../../elkdat; vagrant rsync 13 | 14 | login: 15 | cd ../../../elkdat; vagrant ssh 16 | -------------------------------------------------------------------------------- /example/module/timer/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean install login 2 | 3 | obj-m := timer1.o timer2.o timer3.o timer4.o 4 | 5 | all: 6 | make -C ../../../output M=$(PWD) modules 7 | 8 | clean: 9 | make -C ../../../linux M=$(PWD) clean 10 | 11 | install: 12 | cp *.ko ../../../elkdat; cd ../../../elkdat; vagrant rsync 13 | 14 | login: 15 | cd ../../../elkdat; vagrant ssh 16 | -------------------------------------------------------------------------------- /example/module/hello/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | MODULE_LICENSE("GPL v2"); 4 | MODULE_AUTHOR("Satoru Takeuchi "); 5 | MODULE_DESCRIPTION("Hello world kernel module"); 6 | 7 | static int mymodule_init(void) { 8 | printk(KERN_ALERT "Hello world!\n"); 9 | return 0; 10 | } 11 | 12 | static void mymodule_exit(void) { 13 | /* Do nothing */ 14 | } 15 | 16 | module_init(mymodule_init); 17 | module_exit(mymodule_exit); 18 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0001-1-10-fine.patch: -------------------------------------------------------------------------------- 1 | From 01f16203f768301e5a4647a5534e80db14da74b6 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:21:17 +0900 4 | Subject: [PATCH 01/10] 1/10 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 5e13c544bbf4..ac2a58b92f96 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -590,6 +590,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | mm_init(); 16 | 17 | ftrace_init(); 18 | + pr_warn("1/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0002-2-10-fine.patch: -------------------------------------------------------------------------------- 1 | From 74197a60f7b3698918ec8aa95dda7c1e59a68046 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:22:14 +0900 4 | Subject: [PATCH 02/10] 2/10 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index ac2a58b92f96..b9d0288e530d 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -591,6 +591,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | 16 | ftrace_init(); 17 | pr_warn("1/10"); 18 | + pr_warn("2/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0007-7-10-BUG.patch: -------------------------------------------------------------------------------- 1 | From f4f224fd211cb1f488a49266c6640855ed95f526 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:48:28 +0900 4 | Subject: [PATCH 07/10] 7/10 BUG 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 7b8a809b785a..349157e9ed26 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -597,6 +597,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("5/10"); 16 | pr_warn("6/10"); 17 | BUG(); 18 | + pr_warn("7/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0008-8-10-BUG.patch: -------------------------------------------------------------------------------- 1 | From a57fc8b827e195050aa96f143ebebd66ceb23b81 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:48:48 +0900 4 | Subject: [PATCH 08/10] 8/10 BUG 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 349157e9ed26..1fa980210d9c 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -598,6 +598,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("6/10"); 16 | BUG(); 17 | pr_warn("7/10"); 18 | + pr_warn("8/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0009-9-10-BUG.patch: -------------------------------------------------------------------------------- 1 | From 575c678ee76da1ba539bc73922653adffe9d85ee Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:49:02 +0900 4 | Subject: [PATCH 09/10] 9/10 BUG 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 1fa980210d9c..53011afab325 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -599,6 +599,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | BUG(); 16 | pr_warn("7/10"); 17 | pr_warn("8/10"); 18 | + pr_warn("9/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/first/0001-Print-a-message-on-boot.patch: -------------------------------------------------------------------------------- 1 | From 20c7774d8c3b055b9cfa330d457b5a3baf5f01bf Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 01:52:18 +0900 4 | Subject: [PATCH] test 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 5e13c544bbf4..e4b57366ddac 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -590,6 +590,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | mm_init(); 16 | 17 | ftrace_init(); 18 | + printk("my patch is applied!\n"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/patchcheck/0001-1-4-fine.patch: -------------------------------------------------------------------------------- 1 | From 0602bde62d19ec5268a8411de70e9c59d39aff5d Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 02:09:26 +0900 4 | Subject: [PATCH 1/4] 1/4 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 5e13c544bbf4..4ea8b2382b68 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -590,6 +590,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | mm_init(); 16 | 17 | ftrace_init(); 18 | + pr_warn("1/4 patch is applied!\n"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0003-3-10-fine.patch: -------------------------------------------------------------------------------- 1 | From 72e36d3fbbe677aadb96686e095b1142f8398ae7 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:22:38 +0900 4 | Subject: [PATCH 03/10] 3/10 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index b9d0288e530d..09d16fd09215 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -592,6 +592,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | ftrace_init(); 16 | pr_warn("1/10"); 17 | pr_warn("2/10"); 18 | + pr_warn("3/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0004-4-10-fine.patch: -------------------------------------------------------------------------------- 1 | From bf8b8b05522262c557657e0dfb13cc834ac59f48 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:22:54 +0900 4 | Subject: [PATCH 04/10] 4/10 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 09d16fd09215..45cfda1a489a 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -593,6 +593,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("1/10"); 16 | pr_warn("2/10"); 17 | pr_warn("3/10"); 18 | + pr_warn("4/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0005-5-10-fine.patch: -------------------------------------------------------------------------------- 1 | From b2120b4538796fe0608b177e5e7290cd0a96c54f Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:23:09 +0900 4 | Subject: [PATCH 05/10] 5/10 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 45cfda1a489a..01e116f6bb4c 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -594,6 +594,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("2/10"); 16 | pr_warn("3/10"); 17 | pr_warn("4/10"); 18 | + pr_warn("5/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0010-10-10-BUG.patch: -------------------------------------------------------------------------------- 1 | From e79bde32d5995a75f156ae03fd91e70a3e662f26 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:49:16 +0900 4 | Subject: [PATCH 10/10] 10/10 BUG 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 53011afab325..a9c822d31f46 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -600,6 +600,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("7/10"); 16 | pr_warn("8/10"); 17 | pr_warn("9/10"); 18 | + pr_warn("10/10"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/bisect/0006-6-10-BUG.patch: -------------------------------------------------------------------------------- 1 | From 8d3294219c1d3a3253e3ff7952eb9f8811443c78 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 03:23:36 +0900 4 | Subject: [PATCH 06/10] 6/10 BUG 5 | 6 | --- 7 | init/main.c | 2 ++ 8 | 1 file changed, 2 insertions(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 01e116f6bb4c..7b8a809b785a 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -595,6 +595,8 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("3/10"); 16 | pr_warn("4/10"); 17 | pr_warn("5/10"); 18 | + pr_warn("6/10"); 19 | + BUG(); 20 | 21 | /* trace_printk can be enabled here */ 22 | early_trace_init(); 23 | -- 24 | 2.17.1 25 | 26 | -------------------------------------------------------------------------------- /example/kernel-patch/patchcheck/0002-2-4-fine.patch: -------------------------------------------------------------------------------- 1 | From 0141c43b9cf1b3a022ae058c23fb81b2a7ddeae6 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 02:09:59 +0900 4 | Subject: [PATCH 2/4] 2/4 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 4ea8b2382b68..6fba6c9833d8 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -591,6 +591,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | 16 | ftrace_init(); 17 | pr_warn("1/4 patch is applied!\n"); 18 | + pr_warn("2/4 patch is applied!\n"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/kernel-patch/patchcheck/0004-4-4-fine.patch: -------------------------------------------------------------------------------- 1 | From 706e8dec17bac0cc9bac4d5ac7c39d75994d1e7e Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 02:12:25 +0900 4 | Subject: [PATCH 4/4] 4/4 fine 5 | 6 | --- 7 | init/main.c | 1 + 8 | 1 file changed, 1 insertion(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 0a5a09a2e93b..467bc46c5c86 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -594,6 +594,7 @@ asmlinkage __visible void __init start_kernel(void) 15 | pr_warn("2/4 patch is applied!\n"); 16 | pr_warn("3/4 patch is applied!\n"); 17 | BUG(); 18 | + pr_warn("4/4 patch is applied!\n"); 19 | 20 | /* trace_printk can be enabled here */ 21 | early_trace_init(); 22 | -- 23 | 2.17.1 24 | 25 | -------------------------------------------------------------------------------- /example/module/timer/timer1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("timer kernel module: oneshot timer"); 7 | 8 | struct timer_list mytimer; 9 | 10 | #define MYTIMER_TIMEOUT_SECS 10 11 | 12 | static void mytimer_fn(struct timer_list *timer) 13 | { 14 | printk(KERN_ALERT "10 secs passed.\n"); 15 | } 16 | 17 | static int mymodule_init(void) 18 | { 19 | timer_setup(&mytimer, mytimer_fn, 0); 20 | mytimer.expires = jiffies + MYTIMER_TIMEOUT_SECS*HZ; 21 | add_timer(&mytimer); 22 | 23 | return 0; 24 | } 25 | 26 | static void mymodule_exit(void) 27 | { 28 | del_timer(&mytimer); 29 | } 30 | 31 | module_init(mymodule_init); 32 | module_exit(mymodule_exit); 33 | -------------------------------------------------------------------------------- /example/module/timer/timer2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("timer kernel module: with critical bug"); 7 | 8 | struct timer_list mytimer; 9 | 10 | #define MYTIMER_TIMEOUT_SECS 10 11 | 12 | static void mytimer_fn(struct timer_list *timer) 13 | { 14 | printk(KERN_ALERT "10 secs passed.\n"); 15 | } 16 | 17 | static int mymodule_init(void) 18 | { 19 | timer_setup(&mytimer, mytimer_fn, 0); 20 | mytimer.expires = jiffies + MYTIMER_TIMEOUT_SECS*HZ; 21 | add_timer(&mytimer); 22 | 23 | return 0; 24 | } 25 | 26 | static void mymodule_exit(void) 27 | { 28 | /* do nothing */ 29 | } 30 | 31 | module_init(mymodule_init); 32 | module_exit(mymodule_exit); 33 | -------------------------------------------------------------------------------- /example/kernel-patch/patchcheck/0003-3-4-BUG.patch: -------------------------------------------------------------------------------- 1 | From dc95a48e92b6b02d2dea2ca6a2ed6326e8f02c55 Mon Sep 17 00:00:00 2001 2 | From: Satoru Takeuchi 3 | Date: Tue, 25 Sep 2018 02:12:00 +0900 4 | Subject: [PATCH 3/4] 3/4 BUG 5 | 6 | --- 7 | init/main.c | 2 ++ 8 | 1 file changed, 2 insertions(+) 9 | 10 | diff --git a/init/main.c b/init/main.c 11 | index 6fba6c9833d8..0a5a09a2e93b 100644 12 | --- a/init/main.c 13 | +++ b/init/main.c 14 | @@ -592,6 +592,8 @@ asmlinkage __visible void __init start_kernel(void) 15 | ftrace_init(); 16 | pr_warn("1/4 patch is applied!\n"); 17 | pr_warn("2/4 patch is applied!\n"); 18 | + pr_warn("3/4 patch is applied!\n"); 19 | + BUG(); 20 | 21 | /* trace_printk can be enabled here */ 22 | early_trace_init(); 23 | -- 24 | 2.17.1 25 | 26 | -------------------------------------------------------------------------------- /example/module/timer/timer3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("timer kernel module: periodic timer"); 7 | 8 | struct timer_list mytimer; 9 | 10 | #define MYTIMER_TIMEOUT_SECS 10 11 | 12 | static void mytimer_fn(struct timer_list *timer) 13 | { 14 | printk(KERN_ALERT "10 secs passed.\n"); 15 | mod_timer(&mytimer, jiffies + MYTIMER_TIMEOUT_SECS*HZ); 16 | } 17 | 18 | static int mymodule_init(void) 19 | { 20 | timer_setup(&mytimer, mytimer_fn, 0); 21 | mytimer.expires = jiffies + MYTIMER_TIMEOUT_SECS*HZ; 22 | add_timer(&mytimer); 23 | 24 | return 0; 25 | } 26 | 27 | static void mymodule_exit(void) 28 | { 29 | del_timer(&mytimer); 30 | } 31 | 32 | module_init(mymodule_init); 33 | module_exit(mymodule_exit); 34 | -------------------------------------------------------------------------------- /init: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | BOXNAME=peru/ubuntu-18.04-server-amd64 4 | 5 | # get base image 6 | if ! vagrant box list | grep -q "$BOXNAME (libvirt, " ; then 7 | vagrant box add $BOXNAME --provider libvirt || : 8 | fi 9 | 10 | # prepare for login as root to guest 11 | pushd elkdat 12 | vagrant up --provider libvirt 13 | ln -sf $(vagrant ssh-config | grep IdentityFile | awk '{print $2}' | sed -e 's/^"\(.*\)"/\1/') ../ 14 | HOST=$(vagrant ssh-config | grep HostName | awk '{print $2}') 15 | popd 16 | 17 | test -e ~/.ssh/known_hosts && ssh-keygen -R ${HOST} 18 | ssh -i private_key -oStrictHostKeyChecking=no vagrant@${HOST} : 19 | 20 | # ktest setting (host) 21 | CONCURRENCY=$(grep -c processor /proc/cpuinfo) 22 | DOMAIN_ID=$(cat elkdat/.vagrant/machines/ktest/libvirt/id) 23 | 24 | sed -e "s/@MACHINE@/$HOST/" \ 25 | -e "s+@BASEPATH@+$PWD+" \ 26 | -e "s/@CONCURRENCY@/$CONCURRENCY/" \ 27 | -e "s/@DOMAIN_ID@/$DOMAIN_ID/" \ 28 | ktest/ktest.conf.template >ktest/ktest.conf 29 | 30 | # get kernel source 31 | test -d linux || git clone https://github.com/torvalds/linux.git 32 | -------------------------------------------------------------------------------- /ktest/ktest.conf.template: -------------------------------------------------------------------------------- 1 | MACHINE = @MACHINE@ 2 | SSH_USER = root 3 | PRIVATE_KEY := @BASEPATH@/private_key 4 | SSH_BIN := ssh -i ${PRIVATE_KEY} ${SSH_USER}@${MACHINE} 5 | SSH_EXEC = ${SSH_BIN} $SSH_COMMAND 6 | SCP_TO_TARGET = scp -i ${PRIVATE_KEY} $SRC_FILE ${SSH_USER}@${MACHINE}:$DST_FILE 7 | LOG_FILE = @BASEPATH@/ktest/ktest.log 8 | BUILD_DIR = @BASEPATH@/linux 9 | OUTPUT_DIR = @BASEPATH@/output 10 | LOCALVERSION = -ktest 11 | BUILD_TARGET = arch/x86/boot/bzImage 12 | TARGET_IMAGE = /boot/ktest-vmlinuz 13 | MIN_CONFIG = @BASEPATH@/ktest/minconfig 14 | BUILD_TYPE = olddefconfig 15 | BUILD_OPTIONS = -j@CONCURRENCY@ 16 | 17 | POST_INSTALL = ${SSH_BIN} /usr/sbin/mkinitramfs -o /boot/ktest-initramfs.img $KERNEL_VERSION; make O=${OUTPUT_DIR} modules_prepare 18 | 19 | REBOOT_ON_SUCCESS = 0 20 | REBOOT_TYPE = grub2 21 | GRUB_FILE = /boot/grub/grub.cfg 22 | GRUB_MENU = ktest 23 | GRUB_REBOOT = grub-reboot 24 | POWER_CYCLE = cd ../elkdat; vagrant halt; sleep 5; vagrant up 25 | REBOOT = ${SSH_BIN} reboot; 26 | REBOOT_ON_ERROR = 1 27 | CONSOLE = virsh console @DOMAIN_ID@ 28 | CLOSE_CONSOLE_SIGNAL = KILL 29 | 30 | TEST_START 31 | TEST_TYPE = boot 32 | -------------------------------------------------------------------------------- /example/walb/install: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | ELKDAT_ROOT=$(realpath ../../) 4 | SHARED_DIR=${ELKDAT_ROOT}/elkdat 5 | INSTALL_DIR=${SHARED_DIR}/walb 6 | TARGET_INSTALL_DIR=/vagrant/walb 7 | 8 | test ${INSTALL_DIR} || mkdir -p ${INSTALL_DIR} 9 | 10 | cp walb-driver/module/walb-mod.ko ${INSTALL_DIR} 11 | 12 | pushd walb-tools 13 | cp -a $(make -s echo_binaries) ${INSTALL_DIR} 14 | popd 15 | 16 | pushd ${SHARED_DIR} 17 | vagrant rsync 18 | SSH_IP_ADDRESS=$(vagrant ssh-config | sed -n -e "s/.*HostName \([0-9.]*\)$/\1/p") 19 | SSH_KEY=../private_key 20 | ssh -i ${SSH_KEY} vagrant@${SSH_IP_ADDRESS} sudo rmmod "walb-mod || :" 21 | ssh -i ${SSH_KEY} vagrant@${SSH_IP_ADDRESS} sudo "insmod ${TARGET_INSTALL_DIR}/walb-mod.ko" 22 | ssh -i ${SSH_KEY} vagrant@${SSH_IP_ADDRESS} sudo "${TARGET_INSTALL_DIR}/wdevc format-ldev /dev/vdb2 /dev/vdb1" 23 | ssh -i ${SSH_KEY} vagrant@${SSH_IP_ADDRESS} sudo "${TARGET_INSTALL_DIR}/wdevc format-ldev /dev/vdb2 /dev/vdb1" 24 | ssh -i ${SSH_KEY} vagrant@${SSH_IP_ADDRESS} sudo "${TARGET_INSTALL_DIR}/wdevc create-wdev -n test /dev/vdb2 /dev/vdb1" 25 | ssh -i ${SSH_KEY} vagrant@${SSH_IP_ADDRESS} sudo "mkfs.ext4 /dev/walb/test" 26 | popd 27 | -------------------------------------------------------------------------------- /example/module/timer/timer4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("timer kernel module: pass arg"); 7 | 8 | struct mytimer_data { 9 | char *name; 10 | int interval; 11 | struct timer_list timer; 12 | }; 13 | 14 | struct mytimer_data data[2] = { 15 | { 16 | .name = "foo", 17 | .interval = 2, 18 | }, 19 | { 20 | .name = "bar", 21 | .interval = 3, 22 | }, 23 | }; 24 | 25 | #define MYTIMER_TIMEOUT_SECS 10 26 | 27 | static void mytimer_fn(struct timer_list *timer) 28 | { 29 | struct mytimer_data *data = from_timer(data, timer, timer); 30 | 31 | printk(KERN_ALERT "%s: %d secs passed.\n", 32 | data->name, data->interval); 33 | 34 | mod_timer(&data->timer, jiffies + data->interval*HZ); 35 | } 36 | 37 | static int mymodule_init(void) 38 | { 39 | int i; 40 | 41 | for (i = 0; i < 2; i++) { 42 | struct mytimer_data *d = &data[i]; 43 | timer_setup(&d->timer, mytimer_fn, 0); 44 | d->timer.expires = jiffies + MYTIMER_TIMEOUT_SECS*HZ; 45 | add_timer(&d->timer); 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | static void mymodule_exit(void) 52 | { 53 | int i; 54 | 55 | for (i = 0; i < 2; i++) { 56 | del_timer(&data[i].timer); 57 | } 58 | } 59 | 60 | module_init(mymodule_init); 61 | module_exit(mymodule_exit); 62 | -------------------------------------------------------------------------------- /example/module/debugfs/debugfs1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("A simple example of debugfs"); 7 | 8 | static struct dentry *testfile; 9 | static char testbuf[128]; 10 | 11 | struct timer_list mytimer; 12 | 13 | #define MYTIMER_TIMEOUT_SECS ((unsigned long)1000) 14 | 15 | static void mytimer_fn(struct timer_list *timer) 16 | { 17 | printk(KERN_ALERT "%lu secs passed.\n", MYTIMER_TIMEOUT_SECS); 18 | } 19 | 20 | static ssize_t mytimer_remain_msecs_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 21 | { 22 | unsigned long diff_msecs, now = jiffies; 23 | 24 | if (time_after(mytimer.expires, now)) 25 | diff_msecs = (mytimer.expires - now) * 1000 / HZ; 26 | else 27 | diff_msecs = 0; 28 | 29 | snprintf(testbuf, sizeof(testbuf), "%lu\n", diff_msecs); 30 | return simple_read_from_buffer(buf, len, ppos, testbuf, strlen(testbuf)); 31 | } 32 | 33 | static struct file_operations test_fops = { 34 | .owner = THIS_MODULE, 35 | .read = mytimer_remain_msecs_read, 36 | }; 37 | 38 | static int mymodule_init(void) 39 | { 40 | timer_setup(&mytimer, mytimer_fn, 0); 41 | mytimer.expires = jiffies + MYTIMER_TIMEOUT_SECS*HZ; 42 | add_timer(&mytimer); 43 | 44 | testfile = debugfs_create_file("mytimer_remain_msecs", 0400, NULL, NULL, &test_fops); 45 | if (!testfile) 46 | return -ENOMEM; 47 | 48 | return 0; 49 | } 50 | 51 | static void mymodule_exit(void) 52 | { 53 | debugfs_remove(testfile); 54 | del_timer(&mytimer); 55 | } 56 | 57 | module_init(mymodule_init); 58 | module_exit(mymodule_exit); 59 | -------------------------------------------------------------------------------- /example/module/debugfs/debugfs2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("A simple example of debugfs"); 7 | 8 | static struct dentry *topdir; 9 | static struct dentry *testfile; 10 | static char testbuf[128]; 11 | 12 | struct timer_list mytimer; 13 | 14 | #define MYTIMER_TIMEOUT_SECS ((unsigned long)1000) 15 | 16 | static void mytimer_fn(struct timer_list *timer) 17 | { 18 | printk(KERN_ALERT "%lu secs passed.\n", MYTIMER_TIMEOUT_SECS); 19 | } 20 | 21 | static ssize_t mytimer_remain_msecs_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 22 | { 23 | unsigned long diff_msecs, now = jiffies; 24 | 25 | if (time_after(mytimer.expires, now)) 26 | diff_msecs = (mytimer.expires - now) * 1000 / HZ; 27 | else 28 | diff_msecs = 0; 29 | 30 | snprintf(testbuf, sizeof(testbuf), "%lu\n", diff_msecs); 31 | return simple_read_from_buffer(buf, len, ppos, testbuf, strlen(testbuf)); 32 | } 33 | 34 | static struct file_operations test_fops = { 35 | .owner = THIS_MODULE, 36 | .read = mytimer_remain_msecs_read, 37 | }; 38 | 39 | static int mymodule_init(void) 40 | { 41 | timer_setup(&mytimer, mytimer_fn, 0); 42 | mytimer.expires = jiffies + MYTIMER_TIMEOUT_SECS*HZ; 43 | add_timer(&mytimer); 44 | 45 | topdir = debugfs_create_dir("mytimer", NULL); 46 | if (!topdir) 47 | return -ENOMEM; 48 | testfile = debugfs_create_file("remain_msecs", 0400, topdir, NULL, &test_fops); 49 | if (!testfile) 50 | return -ENOMEM; 51 | 52 | return 0; 53 | } 54 | 55 | static void mymodule_exit(void) 56 | { 57 | debugfs_remove_recursive(topdir); 58 | del_timer(&mytimer); 59 | } 60 | 61 | module_init(mymodule_init); 62 | module_exit(mymodule_exit); 63 | -------------------------------------------------------------------------------- /example/module/list/list1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL v2"); 6 | MODULE_AUTHOR("Satoru Takeuchi "); 7 | MODULE_DESCRIPTION("an example of list data structure"); 8 | 9 | static LIST_HEAD(mylist); 10 | 11 | struct mylist_entry { 12 | struct list_head list; 13 | int n; 14 | }; 15 | 16 | static void mylist_add(int n) { 17 | struct mylist_entry *e = kmalloc(sizeof(*e), GFP_KERNEL); 18 | e->n = n; 19 | list_add(&e->list, &mylist); 20 | printk(KERN_ALERT "mylist: %d is added to the head\n", n); 21 | } 22 | 23 | static void mylist_add_tail(int n) { 24 | struct mylist_entry *e = kmalloc(sizeof(*e), GFP_KERNEL); 25 | e->n = n; 26 | list_add_tail(&e->list, &mylist); 27 | printk(KERN_ALERT "mylist: %d is added to the head\n", n); 28 | } 29 | 30 | static void mylist_del_head(void) { 31 | struct mylist_entry *e = list_first_entry(&mylist, struct mylist_entry, list); 32 | int n = e->n; 33 | list_del(&e->list); 34 | kfree(e); 35 | printk(KERN_ALERT "mylist: %d is deleted from the head\n", n); 36 | } 37 | 38 | static void mylist_show(void) { 39 | struct mylist_entry *e; 40 | 41 | printk(KERN_ALERT "mylist: show contents\n"); 42 | 43 | list_for_each_entry(e, &mylist, list) { 44 | printk(KERN_ALERT "\t%d\n", e->n); 45 | } 46 | } 47 | 48 | static int mymodule_init(void) { 49 | mylist_show(); 50 | mylist_add(1); 51 | mylist_show(); 52 | mylist_add(2); 53 | mylist_show(); 54 | mylist_add(3); 55 | mylist_show(); 56 | mylist_del_head(); 57 | mylist_show(); 58 | mylist_del_head(); 59 | mylist_show(); 60 | mylist_add_tail(4); 61 | mylist_show(); 62 | mylist_del_head(); 63 | mylist_show(); 64 | mylist_del_head(); 65 | mylist_show(); 66 | 67 | return 0; 68 | } 69 | 70 | static void mymodule_exit(void) { 71 | /* Do nothing */ 72 | } 73 | 74 | module_init(mymodule_init); 75 | module_exit(mymodule_exit); 76 | -------------------------------------------------------------------------------- /example/module/debugfs/debugfs3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | MODULE_LICENSE("GPL v2"); 5 | MODULE_AUTHOR("Satoru Takeuchi "); 6 | MODULE_DESCRIPTION("A simple example of debugfs"); 7 | 8 | static struct dentry *topdir; 9 | static struct dentry *testfile; 10 | static char testbuf[128]; 11 | 12 | struct timer_list mytimer; 13 | 14 | static unsigned long mytimer_timeout_msecs = 1000 * 1000; 15 | 16 | static void mytimer_fn(struct timer_list *timer) 17 | { 18 | printk(KERN_ALERT "%lu secs passed.\n", mytimer_timeout_msecs / 1000); 19 | } 20 | 21 | static ssize_t mytimer_remain_msecs_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 22 | { 23 | unsigned long diff_msecs, now = jiffies; 24 | 25 | if (time_after(mytimer.expires, now)) 26 | diff_msecs = (mytimer.expires - now) * 1000 / HZ; 27 | else 28 | diff_msecs = 0; 29 | 30 | snprintf(testbuf, sizeof(testbuf), "%lu\n", diff_msecs); 31 | return simple_read_from_buffer(buf, len, ppos, testbuf, strlen(testbuf)); 32 | } 33 | 34 | static ssize_t mytimer_remain_msecs_write(struct file *f, const char __user *buf, size_t len, loff_t *ppos) 35 | { 36 | ssize_t ret; 37 | 38 | ret = simple_write_to_buffer(testbuf, sizeof(testbuf), ppos, buf, len); 39 | if (ret < 0) 40 | return ret; 41 | sscanf(testbuf, "%20lu", &mytimer_timeout_msecs); 42 | mod_timer(&mytimer, jiffies + mytimer_timeout_msecs * HZ / 1000); 43 | return ret; 44 | } 45 | 46 | static struct file_operations test_fops = { 47 | .owner = THIS_MODULE, 48 | .read = mytimer_remain_msecs_read, 49 | .write = mytimer_remain_msecs_write, 50 | }; 51 | 52 | static int mymodule_init(void) 53 | { 54 | timer_setup(&mytimer, mytimer_fn, 0); 55 | mytimer.expires = jiffies + mytimer_timeout_msecs * HZ / 1000; 56 | add_timer(&mytimer); 57 | 58 | topdir = debugfs_create_dir("mytimer", NULL); 59 | if (!topdir) 60 | return -ENOMEM; 61 | testfile = debugfs_create_file("remain_msecs", 0600, topdir, NULL, &test_fops); 62 | if (!testfile) 63 | return -ENOMEM; 64 | 65 | return 0; 66 | } 67 | 68 | static void mymodule_exit(void) 69 | { 70 | debugfs_remove_recursive(topdir); 71 | del_timer(&mytimer); 72 | } 73 | 74 | module_init(mymodule_init); 75 | module_exit(mymodule_exit); 76 | -------------------------------------------------------------------------------- /example/module/list/list2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL v2"); 6 | MODULE_AUTHOR("Satoru Takeuchi"); 7 | MODULE_DESCRIPTION("a stack example implemented with list"); 8 | 9 | 10 | struct mystack_entry { 11 | struct list_head list; 12 | int n; 13 | }; 14 | 15 | static LIST_HEAD(mystack); 16 | 17 | static void mystack_push(int n) { 18 | struct mystack_entry *e = kmalloc(sizeof(*e), GFP_KERNEL); 19 | e->n = n; 20 | list_add(&e->list, &mystack); 21 | } 22 | 23 | static int mystack_pop(int *np) { 24 | struct mystack_entry *e; 25 | 26 | if (list_empty(&mystack)) 27 | return -1; 28 | 29 | e = list_first_entry(&mystack, struct mystack_entry, list); 30 | if (np != NULL) 31 | *np = e->n; 32 | list_del(&e->list); 33 | kfree(e); 34 | 35 | return 0; 36 | } 37 | 38 | static void mystack_clean_out(void) { 39 | while (!list_empty(&mystack)) { 40 | mystack_pop(NULL); 41 | } 42 | } 43 | 44 | static struct dentry *mylist_dir; 45 | 46 | static struct dentry *showfile; 47 | static struct dentry *pushfile; 48 | static struct dentry *popfile; 49 | 50 | static char testbuf[1024]; 51 | 52 | static ssize_t show_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 53 | { 54 | char *bufp = testbuf; 55 | size_t remain = sizeof(testbuf); 56 | struct mystack_entry *e; 57 | size_t l; 58 | 59 | if (list_empty(&mystack)) 60 | return simple_read_from_buffer(buf, len, ppos, "\n", 1); 61 | 62 | list_for_each_entry(e, &mystack, list) { 63 | int n; 64 | 65 | n = snprintf(bufp, remain, "%d ", e->n); 66 | if (n == 0) 67 | break; 68 | bufp += n; 69 | remain -= n; 70 | } 71 | 72 | l = strlen(testbuf); 73 | testbuf[l - 1] = '\n'; 74 | return simple_read_from_buffer(buf, len, ppos, testbuf, l); 75 | } 76 | 77 | static ssize_t push_write(struct file *f, const char __user *buf, size_t len, loff_t *ppos) 78 | { 79 | ssize_t ret; 80 | int n; 81 | 82 | ret = simple_write_to_buffer(testbuf, sizeof(testbuf), ppos, buf, len); 83 | if (ret < 0) 84 | return ret; 85 | sscanf(testbuf, "%20d", &n); 86 | mystack_push(n); 87 | 88 | return ret; 89 | } 90 | 91 | static ssize_t pop_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 92 | { 93 | int n; 94 | 95 | if (*ppos || mystack_pop(&n) == -1) 96 | return 0; 97 | snprintf(testbuf, sizeof(testbuf), "%d\n", n); 98 | return simple_read_from_buffer(buf, len, ppos, testbuf, strlen(testbuf)); 99 | } 100 | 101 | static struct file_operations show_fops = { 102 | .owner = THIS_MODULE, 103 | .read = show_read, 104 | }; 105 | 106 | static struct file_operations push_fops = { 107 | .owner = THIS_MODULE, 108 | .write = push_write, 109 | }; 110 | 111 | static struct file_operations pop_fops = { 112 | .owner = THIS_MODULE, 113 | .read = pop_read, 114 | }; 115 | 116 | static int mymodule_init(void) 117 | { 118 | mylist_dir = debugfs_create_dir("mystack", NULL); 119 | if (!mylist_dir) 120 | return -ENOMEM; 121 | showfile = debugfs_create_file("show", 0400, mylist_dir, NULL, &show_fops); 122 | if (!showfile) 123 | goto fail; 124 | pushfile = debugfs_create_file("push", 0200, mylist_dir, NULL, &push_fops); 125 | if (!pushfile) 126 | goto fail; 127 | popfile = debugfs_create_file("pop", 0400, mylist_dir, NULL, &pop_fops); 128 | if (!popfile) 129 | goto fail; 130 | 131 | return 0; 132 | 133 | fail: 134 | debugfs_remove_recursive(mylist_dir); 135 | return -ENOMEM; 136 | } 137 | 138 | static void mymodule_exit(void) 139 | { 140 | debugfs_remove_recursive(mylist_dir); 141 | mystack_clean_out(); 142 | } 143 | 144 | module_init(mymodule_init); 145 | module_exit(mymodule_exit); 146 | -------------------------------------------------------------------------------- /elkdat/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | # config.vm.box = "elastic/ubuntu-16.040x86_64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | # config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | # vb.memory = "1024" 52 | # end 53 | # 54 | # View the documentation for the provider you are using for more 55 | # information on available options. 56 | 57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 58 | # such as FTP and Heroku are also available. See the documentation at 59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 60 | # config.push.define "atlas" do |push| 61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 62 | # end 63 | config.vm.define :ktest do |vm| 64 | vm.vm.box = "peru/ubuntu-18.04-server-amd64" 65 | vm.vm.synced_folder './', '/vagrant', type: 'rsync' 66 | vm.vm.provider :libvirt do |domain| 67 | domain.cpus = 2 68 | domain.cpu_mode = "host-passthrough" 69 | domain.storage :file, :size => "10GB" 70 | end 71 | end 72 | 73 | # Enable provisioning with a shell script. Additional provisioners such as 74 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 75 | # documentation for more information about their specific syntax and use. 76 | config.vm.provision "shell", inline: <<-SHELL 77 | apt-get -y update 78 | mkdir /root/.ssh 79 | cp /home/vagrant/.ssh/authorized_keys /root/.ssh/ 80 | sed /etc/default/grub -i -e '/^GRUB_CMDLINE_LINUX_DEFAULT=/s/"$/ console=ttyS0,115200n8"/' 81 | sed -e "s/@ROOT_UUID@/$(blkid -o value /dev/vda1 | sed -n -e 1p)/" /vagrant/ktest-grub-menuentry.template >/vagrant/ktest-grub-menuentry; cat /vagrant/ktest-grub-menuentry >>/etc/grub.d/40_custom 82 | update-grub 83 | SHELL 84 | end 85 | -------------------------------------------------------------------------------- /example/module/lock/lock1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL v2"); 6 | MODULE_AUTHOR("Satoru Takeuchi"); 7 | MODULE_DESCRIPTION("a example of mutual exclusion by using mutex"); 8 | 9 | struct mystack_entry { 10 | struct list_head list; 11 | int n; 12 | }; 13 | 14 | #define MYSTACK_MAX_LEN 10 15 | 16 | static LIST_HEAD(mystack); 17 | static int mystack_len; 18 | 19 | static void mystack_push(int n) { 20 | struct mystack_entry *e; 21 | 22 | if (mystack_len >= MYSTACK_MAX_LEN) { 23 | return; 24 | } 25 | e = kmalloc(sizeof(*e), GFP_KERNEL); 26 | INIT_LIST_HEAD(&e->list); 27 | e->n = n; 28 | list_add(&e->list, &mystack); 29 | ++mystack_len; 30 | } 31 | 32 | static int mystack_pop(int *np) { 33 | struct mystack_entry *e; 34 | 35 | if (list_empty(&mystack)) { 36 | return -1; 37 | } 38 | 39 | e = list_first_entry(&mystack, struct mystack_entry, list); 40 | if (np != NULL) 41 | *np = e->n; 42 | list_del(&e->list); 43 | --mystack_len; 44 | kfree(e); 45 | 46 | return 0; 47 | } 48 | 49 | static void mystack_clean_out(void) { 50 | while (!list_empty(&mystack)) { 51 | mystack_pop(NULL); 52 | --mystack_len; 53 | } 54 | } 55 | 56 | static struct dentry *mylist_dir; 57 | 58 | static struct dentry *showfile; 59 | static struct dentry *pushfile; 60 | static struct dentry *popfile; 61 | 62 | static char testbuf[1024]; 63 | 64 | static ssize_t show_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 65 | { 66 | char *bufp = testbuf; 67 | size_t remain = sizeof(testbuf); 68 | struct mystack_entry *e; 69 | size_t l; 70 | ssize_t ret; 71 | 72 | if (list_empty(&mystack)) { 73 | ret = simple_read_from_buffer(buf, len, ppos, "\n", 1); 74 | return ret; 75 | } 76 | 77 | list_for_each_entry(e, &mystack, list) { 78 | int n; 79 | 80 | n = snprintf(bufp, remain, "%d ", e->n); 81 | if (n == 0) 82 | break; 83 | bufp += n; 84 | remain -= n; 85 | } 86 | l = strlen(testbuf); 87 | testbuf[l - 1] = '\n'; 88 | ret = simple_read_from_buffer(buf, len, ppos, testbuf, l); 89 | 90 | return ret; 91 | } 92 | 93 | static ssize_t push_write(struct file *f, const char __user *buf, size_t len, loff_t *ppos) 94 | { 95 | ssize_t ret; 96 | int n; 97 | 98 | ret = simple_write_to_buffer(testbuf, sizeof(testbuf), ppos, buf, len); 99 | if (ret < 0) 100 | return ret; 101 | sscanf(testbuf, "%20d", &n); 102 | mystack_push(n); 103 | 104 | return ret; 105 | } 106 | 107 | static ssize_t pop_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 108 | { 109 | int n; 110 | ssize_t ret; 111 | 112 | if (*ppos || mystack_pop(&n) == -1) { 113 | return 0; 114 | } 115 | snprintf(testbuf, sizeof(testbuf), "%d\n", n); 116 | ret = simple_read_from_buffer(buf, len, ppos, testbuf, strlen(testbuf)); 117 | return ret; 118 | } 119 | 120 | static struct file_operations show_fops = { 121 | .owner = THIS_MODULE, 122 | .read = show_read, 123 | }; 124 | 125 | static struct file_operations push_fops = { 126 | .owner = THIS_MODULE, 127 | .write = push_write, 128 | }; 129 | 130 | static struct file_operations pop_fops = { 131 | .owner = THIS_MODULE, 132 | .read = pop_read, 133 | }; 134 | 135 | static int mymodule_init(void) 136 | { 137 | mylist_dir = debugfs_create_dir("mystack", NULL); 138 | if (!mylist_dir) 139 | return -ENOMEM; 140 | showfile = debugfs_create_file("show", 0400, mylist_dir, NULL, &show_fops); 141 | if (!showfile) 142 | goto fail; 143 | pushfile = debugfs_create_file("push", 0200, mylist_dir, NULL, &push_fops); 144 | if (!pushfile) 145 | goto fail; 146 | popfile = debugfs_create_file("pop", 0400, mylist_dir, NULL, &pop_fops); 147 | if (!popfile) 148 | goto fail; 149 | 150 | return 0; 151 | 152 | fail: 153 | debugfs_remove_recursive(mylist_dir); 154 | return -ENOMEM; 155 | } 156 | 157 | static void mymodule_exit(void) 158 | { 159 | debugfs_remove_recursive(mylist_dir); 160 | mystack_clean_out(); 161 | } 162 | 163 | module_init(mymodule_init); 164 | module_exit(mymodule_exit); 165 | -------------------------------------------------------------------------------- /elkdat/Vagrantfile.walb: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | # config.vm.box = "elastic/ubuntu-16.040x86_64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # config.vm.network "forwarded_port", guest: 80, host: 8080 26 | 27 | # Create a private network, which allows host-only access to the machine 28 | # using a specific IP. 29 | # config.vm.network "private_network", ip: "192.168.33.10" 30 | 31 | # Create a public network, which generally matched to bridged network. 32 | # Bridged networks make the machine appear as another physical device on 33 | # your network. 34 | # config.vm.network "public_network" 35 | 36 | # Share an additional folder to the guest VM. The first argument is 37 | # the path on the host to the actual folder. The second argument is 38 | # the path on the guest to mount the folder. And the optional third 39 | # argument is a set of non-required options. 40 | # config.vm.synced_folder "../data", "/vagrant_data" 41 | 42 | # Provider-specific configuration so you can fine-tune various 43 | # backing providers for Vagrant. These expose provider-specific options. 44 | # Example for VirtualBox: 45 | # 46 | # config.vm.provider "virtualbox" do |vb| 47 | # # Display the VirtualBox GUI when booting the machine 48 | # vb.gui = true 49 | # 50 | # # Customize the amount of memory on the VM: 51 | # vb.memory = "1024" 52 | # end 53 | # 54 | # View the documentation for the provider you are using for more 55 | # information on available options. 56 | 57 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 58 | # such as FTP and Heroku are also available. See the documentation at 59 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 60 | # config.push.define "atlas" do |push| 61 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 62 | # end 63 | config.vm.define :ktest do |vm| 64 | vm.vm.box = "peru/ubuntu-18.04-server-amd64" 65 | vm.vm.synced_folder './', '/vagrant', type: 'rsync' 66 | vm.vm.provider :libvirt do |domain| 67 | domain.cpus = 2 68 | domain.cpu_mode = "host-passthrough" 69 | domain.storage :file, :size => "10GB" 70 | end 71 | end 72 | 73 | # Enable provisioning with a shell script. Additional provisioners such as 74 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 75 | # documentation for more information about their specific syntax and use. 76 | config.vm.provision "shell", inline: <<-SHELL 77 | apt-get -y update 78 | apt install -y g++ libaio1 liblzma5 zlib1g 79 | mkdir /root/.ssh 80 | cp /home/vagrant/.ssh/authorized_keys /root/.ssh/ 81 | sed /etc/default/grub -i -e '/^GRUB_CMDLINE_LINUX_DEFAULT=/s/"$/ console=ttyS0,115200n8"/' 82 | sed -e "s/@ROOT_UUID@/$(blkid -o value /dev/vda1 | sed -n -e 1p)/" /vagrant/ktest-grub-menuentry.template >/vagrant/ktest-grub-menuentry; cat /vagrant/ktest-grub-menuentry >>/etc/grub.d/40_custom 83 | update-grub 84 | parted /dev/vdb mklabel gpt 85 | parted /dev/vdb mkpart wdata ext4 0G 8G 86 | parted /dev/vdb mkpart wlog ext4 8G 9G 87 | SHELL 88 | end 89 | -------------------------------------------------------------------------------- /example/module/lock/lock2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL v2"); 6 | MODULE_AUTHOR("Satoru Takeuchi"); 7 | MODULE_DESCRIPTION("a example of mutual exclusion by using mutex"); 8 | 9 | struct mystack_entry { 10 | struct list_head list; 11 | int n; 12 | }; 13 | 14 | #define MYSTACK_MAX_LEN 10 15 | 16 | static LIST_HEAD(mystack); 17 | static int mystack_len; 18 | static DEFINE_MUTEX(mystack_mutex); 19 | 20 | static void mystack_push(int n) { 21 | struct mystack_entry *e; 22 | 23 | if (mystack_len >= MYSTACK_MAX_LEN) { 24 | return; 25 | } 26 | e = kmalloc(sizeof(*e), GFP_KERNEL); 27 | INIT_LIST_HEAD(&e->list); 28 | e->n = n; 29 | list_add(&e->list, &mystack); 30 | ++mystack_len; 31 | } 32 | 33 | static int mystack_pop(int *np) { 34 | struct mystack_entry *e; 35 | 36 | if (list_empty(&mystack)) { 37 | return -1; 38 | } 39 | 40 | e = list_first_entry(&mystack, struct mystack_entry, list); 41 | if (np != NULL) 42 | *np = e->n; 43 | list_del(&e->list); 44 | --mystack_len; 45 | kfree(e); 46 | 47 | return 0; 48 | } 49 | 50 | static void mystack_clean_out(void) { 51 | mutex_lock(&mystack_mutex); 52 | while (!list_empty(&mystack)) { 53 | mystack_pop(NULL); 54 | --mystack_len; 55 | } 56 | mutex_unlock(&mystack_mutex); 57 | } 58 | 59 | static struct dentry *mylist_dir; 60 | 61 | static struct dentry *showfile; 62 | static struct dentry *pushfile; 63 | static struct dentry *popfile; 64 | 65 | static char testbuf[1024]; 66 | 67 | static ssize_t show_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 68 | { 69 | char *bufp = testbuf; 70 | size_t remain = sizeof(testbuf); 71 | struct mystack_entry *e; 72 | size_t l; 73 | ssize_t ret; 74 | 75 | mutex_lock(&mystack_mutex); 76 | if (list_empty(&mystack)) { 77 | ret = simple_read_from_buffer(buf, len, ppos, "\n", 1); 78 | mutex_unlock(&mystack_mutex); 79 | return ret; 80 | } 81 | 82 | list_for_each_entry(e, &mystack, list) { 83 | int n; 84 | 85 | n = snprintf(bufp, remain, "%d ", e->n); 86 | if (n == 0) 87 | break; 88 | bufp += n; 89 | remain -= n; 90 | } 91 | l = strlen(testbuf); 92 | testbuf[l - 1] = '\n'; 93 | ret = simple_read_from_buffer(buf, len, ppos, testbuf, l); 94 | mutex_unlock(&mystack_mutex); 95 | 96 | return ret; 97 | } 98 | 99 | static ssize_t push_write(struct file *f, const char __user *buf, size_t len, loff_t *ppos) 100 | { 101 | ssize_t ret; 102 | int n; 103 | 104 | mutex_lock(&mystack_mutex); 105 | ret = simple_write_to_buffer(testbuf, sizeof(testbuf), ppos, buf, len); 106 | if (ret < 0) { 107 | mutex_unlock(&mystack_mutex); 108 | return ret; 109 | } 110 | sscanf(testbuf, "%20d", &n); 111 | mystack_push(n); 112 | mutex_unlock(&mystack_mutex); 113 | 114 | return ret; 115 | } 116 | 117 | static ssize_t pop_read(struct file *f, char __user *buf, size_t len, loff_t *ppos) 118 | { 119 | int n; 120 | ssize_t ret; 121 | 122 | mutex_lock(&mystack_mutex); 123 | if (*ppos || mystack_pop(&n) == -1) { 124 | mutex_unlock(&mystack_mutex); 125 | return 0; 126 | } 127 | snprintf(testbuf, sizeof(testbuf), "%d\n", n); 128 | ret = simple_read_from_buffer(buf, len, ppos, testbuf, strlen(testbuf)); 129 | mutex_unlock(&mystack_mutex); 130 | return ret; 131 | } 132 | 133 | static struct file_operations show_fops = { 134 | .owner = THIS_MODULE, 135 | .read = show_read, 136 | }; 137 | 138 | static struct file_operations push_fops = { 139 | .owner = THIS_MODULE, 140 | .write = push_write, 141 | }; 142 | 143 | static struct file_operations pop_fops = { 144 | .owner = THIS_MODULE, 145 | .read = pop_read, 146 | }; 147 | 148 | static int mymodule_init(void) 149 | { 150 | mylist_dir = debugfs_create_dir("mystack", NULL); 151 | if (!mylist_dir) 152 | return -ENOMEM; 153 | showfile = debugfs_create_file("show", 0400, mylist_dir, NULL, &show_fops); 154 | if (!showfile) 155 | goto fail; 156 | pushfile = debugfs_create_file("push", 0200, mylist_dir, NULL, &push_fops); 157 | if (!pushfile) 158 | goto fail; 159 | popfile = debugfs_create_file("pop", 0400, mylist_dir, NULL, &pop_fops); 160 | if (!popfile) 161 | goto fail; 162 | 163 | return 0; 164 | 165 | fail: 166 | debugfs_remove_recursive(mylist_dir); 167 | return -ENOMEM; 168 | } 169 | 170 | static void mymodule_exit(void) 171 | { 172 | debugfs_remove_recursive(mylist_dir); 173 | mystack_clean_out(); 174 | } 175 | 176 | module_init(mymodule_init); 177 | module_exit(mymodule_exit); 178 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $PROGNAME [-p patch] ..." >&2 5 | exit 1 6 | } 7 | 8 | PROGNAME=$0 9 | WORKING_DIR=$(pwd) 10 | 11 | if [ $# -lt 1 ] ; then 12 | usage 13 | fi 14 | 15 | PATCH="" 16 | 17 | while getopts p: OPT ; do 18 | case $OPT in 19 | p) 20 | PATCH=$(realpath $OPTARG) 21 | if [ ! \( -f $PATCH -a -f $PATCH \) ] ; then 22 | echo "invalid patch file: $PATCH" >&2 23 | fi 24 | ;; 25 | \?) 26 | usage 27 | ;; 28 | esac 29 | done 30 | 31 | shift $((OPTIND - 1)) 32 | 33 | CONFFILE=ktest.conf 34 | SUBCOMMAND=$1 35 | BOOT_VM_IF_NECESSARY=1 36 | case $SUBCOMMAND in 37 | build|install) 38 | BOOT_VM_IF_NECESSARY=0 39 | ;; 40 | boot) 41 | ;; 42 | test) 43 | if [ $# -lt 2 ] ; then 44 | echo "usage: $0 test " >&2 45 | exit 1 46 | fi 47 | 48 | TEST=$2 49 | if [ ! -f ${TEST} -o ! -x ${TEST} ] ; then 50 | echo "${TEST} should be executable test program" >&2 51 | exit 1 52 | fi 53 | TEST=$(realpath ${TEST}) 54 | ;; 55 | patchcheck) 56 | if [ $# -lt 4 ] ; then 57 | echo "usage: $0 patchcheck [build|install|boot|test] [test path]" >&2 58 | echo " : the first patch of the test target patchset" >&2 59 | echo " : the last patch of the test target patchset" >&2 60 | exit 1 61 | fi 62 | PATCHCHECK_START=$2 63 | PATCHCHECK_END=$3 64 | PATCHCHECK_TYPE=$4 65 | case $PATCHCHECK_TYPE in 66 | build|install) 67 | BOOT_VM_IF_NECESSARY=0 68 | ;; 69 | boot) 70 | ;; 71 | test) 72 | if [ $# -lt 5 ] ; then 73 | echo "usage: $0 patchcheck test " >&2 74 | exit 1 75 | fi 76 | TEST=$5 77 | if [ ! -f ${TEST} -o ! -x ${TEST} ] ; then 78 | echo "${TEST} should be executable test program" >&2 79 | exit 1 80 | fi 81 | TEST=$(realpath ${TEST}) 82 | ;; 83 | *) 84 | echo "usage: $0 patchcheck [build|install|boot|test] [test path]" >&2 85 | echo " : the first patch of the test target patchset" >&2 86 | echo " : the last patch of the test target patchset" >&2 87 | exit 1 88 | ;; 89 | esac 90 | ;; 91 | bisect) 92 | if [ $# -lt 4 ] ; then 93 | echo "usage: $0 bisect [build|install|boot|test] [test path]" >&2 94 | echo " : the patch which passes test" >&2 95 | echo " : the patthc which fails test" >&2 96 | exit 1 97 | fi 98 | BISECT_GOOD=$2 99 | BISECT_BAD=$3 100 | BISECT_TYPE=$4 101 | case $BISECT_TYPE in 102 | build|install) 103 | BOOT_VM_IF_NECESSARY=0 104 | ;; 105 | boot) 106 | ;; 107 | test) 108 | if [ $# -lt 5 ] ; then 109 | echo "usage: $0 bisect test " >&2 110 | exit 1 111 | fi 112 | TEST=$5 113 | if [ ! -f ${TEST} -o ! -x ${TEST} ] ; then 114 | echo "${TEST} should be executable test program" >&2 115 | exit 1 116 | fi 117 | TEST=$(realpath ${TEST}) 118 | ;; 119 | *) 120 | echo "usage: $0 bisect [build|install|boot|test] [test path]" >&2 121 | echo " : the patch which passes test" >&2 122 | echo " : the patthc which fails test" >&2 123 | exit 1 124 | ;; 125 | esac 126 | ;; 127 | *) 128 | echo "subcommand shoulld be : ${SUBCOMMAND}" >&2 129 | exit 1 130 | ;; 131 | esac 132 | 133 | trap "_cleanup ; exit 0" 0 1 2 3 15 134 | 135 | function _cleanup { 136 | cd $WORKING_DIR 137 | if [ -n "$PATCH" ] ; then 138 | pushd linux 139 | git checkout $ORIGIN 140 | git branch -D $TEMPORARY_BRANCH 141 | popd 142 | fi 143 | restore_ktest_conf 144 | } 145 | 146 | function modify_ktest_conf { 147 | sed -i -e '/^TEST_START/,$s/^/# /' $CONFFILE 148 | sed -i -e '$aTEST_START' $CONFFILE 149 | 150 | sed -i -e "\$aTEST_TYPE = ${SUBCOMMAND}" $CONFFILE 151 | case $SUBCOMMAND in 152 | test) 153 | sed -i -e "\$aTEST = ${TEST}" $CONFFILE 154 | ;; 155 | patchcheck) 156 | sed -i -e "\$aPATCHCHECK_START = ${PATCHCHECK_START}" $CONFFILE 157 | sed -i -e "\$aPATCHCHECK_END = ${PATCHCHECK_END}" $CONFFILE 158 | sed -i -e "\$aPATCHCHECK_TYPE = ${PATCHCHECK_TYPE}" $CONFFILE 159 | if [ $PATCHCHECK_TYPE = test ] ; then 160 | sed -i -e "\$aTEST = ${TEST}" $CONFFILE 161 | fi 162 | ;; 163 | bisect) 164 | sed -i -e "\$aBISECT_GOOD = ${BISECT_GOOD}" $CONFFILE 165 | sed -i -e "\$aBISECT_BAD = ${BISECT_BAD}" $CONFFILE 166 | sed -i -e "\$aBISECT_TYPE = ${BISECT_TYPE}" $CONFFILE 167 | if [ $BISECT_TYPE = test ] ; then 168 | sed -i -e "\$aTEST = ${TEST}" $CONFFILE 169 | fi 170 | ;; 171 | esac 172 | } 173 | 174 | function restore_ktest_conf { 175 | pushd ktest 176 | sed -i -e '/^TEST_START/,$d' $CONFFILE 177 | sed -i -e '/^# TEST_START/,$s/^# //' $CONFFILE 178 | popd 179 | } 180 | 181 | pushd ktest 182 | modify_ktest_conf 183 | popd 184 | 185 | if [ $BOOT_VM_IF_NECESSARY -eq 1 ] ; then 186 | IS_VM_DOWN=0 187 | pushd elkdat 188 | vagrant status | grep ktest | grep -q shutoff && IS_VM_DOWN=1 189 | if [ $IS_VM_DOWN -eq 1 ] ; then 190 | vagrant up 191 | fi 192 | popd 193 | fi 194 | 195 | if [ -n "$PATCH" ] ; then 196 | pushd linux 197 | ORIGIN=$(git status | sed -nr -e "1s/^(HEAD detached at |On branch )(.*)$/\2/p") 198 | TEMPORARY_BRANCH=elkdat-test 199 | git checkout -b $TEMPORARY_BRANCH 200 | if ! git am $PATCH ; then 201 | git am --abort 202 | popd 203 | exit 1 204 | fi 205 | popd 206 | fi 207 | 208 | cd ktest 209 | ./ktest.pl 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elkdat 2 | 3 | easy linux kernel development and auto-test tool 4 | 5 | elkdat automatically setups linux kernel source repository and a VM for 6 | linux kernel development and test. In addition, it can build, install, 7 | boot, run your own tests, test the all patches in a patchset, and find 8 | a problematic commit with bisect by just one command. 9 | 10 | It's useful not only for experienced linux kernel developers, but also for 11 | kernel newbies and non-developers who'd like to test linux kernel. 12 | 13 | # Prerequisite environment 14 | 15 | You need to have a Linux system with virtualization feature. It the following command shows one or more lines, yoor machine has that feature. 16 | 17 | ``` 18 | $ egrep '^flags.*(vmx|svm)' /proc/cpuinfo 19 | ``` 20 | 21 | You should install some packages before initializing elkdat. Here is how to install them on Ubuntu 18.04. 22 | 23 | ``` 24 | $ sudo apt-get install git vagrant libvirt-bin libvirt-dev kernel-package qemu-kvm libssl-dev libncurses5-dev 25 | $ sudo usermod -aG libvirt 26 | ``` 27 | 28 | If you use other distros, please install corresponding packages. 29 | 30 | # How to initialize 31 | 32 | Just run `./init` underneath the top of this tool. 33 | 34 | Please note that elkdat downloads whole linux kernel source repository in `./init`. 35 | If you already have it, please copy it underneath the top directory as "linux". 36 | In addition, elkdat flushes existing kernel, object files and so on by `mrproper`. 37 | 38 | # How to finalize 39 | 40 | Run the following command. 41 | 42 | ``` 43 | $ ./fini 44 | ... 45 | Finished to cleanup. 46 | Now it's safe to delete the source directory. 47 | $ 48 | ``` 49 | 50 | # Managing test VM 51 | 52 | You can boot test VM as follows. 53 | 54 | ``` 55 | $ ./up 56 | Bringing machine 'ktest' up with 'libvirt' provider... 57 | ==> ktest: Starting domain. 58 | ==> ktest: Waiting for domain to get an IP address... 59 | ==> ktest: Waiting for SSH to become available... 60 | ==> ktest: Creating shared folders metadata... 61 | ==> ktest: Rsyncing folder: /home/sat/src/elkdat/elkdat/ => /vagrant 62 | ==> ktest: Machine already provisioned. Run `vagrant provision` or use the `--provision` 63 | ==> ktest: flag to force provisioning. Provisioners marked to run always will still run. 64 | $ 65 | ``` 66 | 67 | Please note that test VM is already up just after running init command. 68 | 69 | 70 | After that, you can login to test VM as follows. 71 | 72 | ``` 73 | $ ./login 74 | Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.18.0-ktest x86_64) 75 | 76 | * Documentation: https://help.ubuntu.com 77 | * Management: https://landscape.canonical.com 78 | * Support: https://ubuntu.com/advantage 79 | 80 | Last login: Mon Sep 24 16:32:47 2018 from 192.168.121.1 81 | vagrant@localhost:~$ 82 | ``` 83 | 84 | You can shutdown test VM as follows. 85 | 86 | ``` 87 | $ ./halt 88 | ==> ktest: Halting domain... 89 | $ 90 | ``` 91 | 92 | You can also reboot test VM as follows. 93 | 94 | 95 | ``` 96 | $ ./reload 97 | ==> ktest: Halting domain... 98 | ==> ktest: Starting domain. 99 | ==> ktest: Waiting for domain to get an IP address... 100 | ==> ktest: Waiting for SSH to become available... 101 | ==> ktest: Creating shared folders metadata... 102 | ==> ktest: Rsyncing folder: /home/sat/src/elkdat/elkdat/ => /vagrant 103 | ==> ktest: Machine already provisioned. Run `vagrant provision` or use the `--provision` 104 | ==> ktest: flag to force provisioning. Provisioners marked to run always will still run. 105 | $ 106 | 107 | ``` 108 | 109 | # Tutorial 110 | 111 | From here, we assume the current directory is underneath the top directory. 112 | 113 | ## Run your own kernel (don't change source) 114 | 115 | Let's boot linux v5.0. You can build, install, and boot linux v5.0 116 | just by the following several commands. 117 | 118 | ``` 119 | $ cd linux 120 | $ git checkout v5.0 121 | $ cd ../ 122 | $ ./test boot 123 | ... 124 | ******************************************* 125 | ******************************************* 126 | KTEST RESULT: TEST 1 SUCCESS!!!! ** 127 | ******************************************* 128 | ******************************************* 129 | 130 | 1 of 1 tests were successful 131 | 132 | $ 133 | ``` 134 | 135 | Let's login to test VM to confirm whether it works correctly. 136 | 137 | ``` 138 | $ ./login 139 | ... 140 | vagrant@localhost:~$ uname -r 141 | 5.0.0-ktest 142 | vagrant@localhost:~$ 143 | ``` 144 | 145 | Finally we restarts the previous (probably the distro's) kernel. 146 | 147 | ``` 148 | vagrant@localhost:~$ exit 149 | $ ./reload 150 | $ 151 | ``` 152 | 153 | You can use your own kernel again by the following commands. 154 | 155 | ``` 156 | $ ./login 157 | ... 158 | vagrant@localhost:~$ sudo su 159 | root@localhost:/home/vagrant# grub-reboot ktest 160 | root@localhost:/home/vagrant# exit 161 | exit 162 | vagrant@localhost:~$ exit 163 | ... 164 | $ ./reload 165 | ... 166 | $ 167 | ``` 168 | 169 | You can boot the any kernel version by changing the above mentioned _v5.0_ in 170 | `git checkout` command to any tag or to any commit ID. 171 | 172 | If it's OK to just build or just install your own kernel rather than booting it, 173 | Please use `./test build` or `./test install` instead of `./test boot`. 174 | 175 | ## Run your own kernel (change source) 176 | 177 | Here we apply a trivial patch, printing a simple message to kernel log, to linux v4.18 and boot it. 178 | 179 | Let's take a look at the patch. 180 | 181 | ``` 182 | $ cat example/kernel-patch/first/0001-Print-a-message-on-boot.patch 183 | commit 20c7774d8c3b055b9cfa330d457b5a3baf5f01bf (HEAD -> sat) 184 | Author: Satoru Takeuchi 185 | Date: Tue Sep 25 01:52:18 2018 +0900 186 | 187 | test 188 | 189 | diff --git a/init/main.c b/init/main.c 190 | index 5e13c544bbf4..e4b57366ddac 100644 191 | --- a/init/main.c 192 | +++ b/init/main.c 193 | @@ -590,6 +590,7 @@ asmlinkage __visible void __init start_kernel(void) 194 | mm_init(); 195 | 196 | ftrace_init(); 197 | + printk("my patch is applied!\n"); 198 | 199 | /* trace_printk can be enabled here */ 200 | early_trace_init(); 201 | ``` 202 | 203 | Make a kernel and boot it. 204 | 205 | ``` 206 | $ cd linux 207 | $ git checkout -b test v4.18 208 | Switched to a new branch 'test' 209 | $ git am ../example/kernel-patch/first/0001-Print-a-message-on-boot.patch 210 | Applying: test 211 | $ cd ../ 212 | $ ./test boot 213 | ... 214 | ******************************************* 215 | ******************************************* 216 | KTEST RESULT: TEST 1 SUCCESS!!!! ** 217 | ******************************************* 218 | ******************************************* 219 | 220 | 1 of 1 tests were successful 221 | 222 | ``` 223 | 224 | Login to confirm whether we succeeded or not. 225 | 226 | ``` 227 | $ ./login 228 | ... 229 | vagrant@localhost:~$ uname -r 230 | 4.18.0-ktest+ 231 | vagrant@localhost:~$ 232 | ``` 233 | 234 | Read the kernel log. 235 | 236 | ``` 237 | vagrant@localhost:~$ dmesg | grep "my patch" 238 | [ 0.000000] my patch is applied! 239 | $ 240 | ``` 241 | 242 | Succeeded! 243 | 244 | Please restart your system here with the previous kernel. 245 | 246 | ## Load your own kernel module 247 | 248 | It's easy to build, install, and load your own kernel module. Here we use 249 | the simple module which just prints "Hello world!" on loading. 250 | 251 | Let's take a look at its source. 252 | 253 | ``` 254 | $ cat example/module/hello/hello.c 255 | #include 256 | 257 | MODULE_LICENSE("GPL v2"); 258 | MODULE_AUTHOR("Satoru Takeuchi "); 259 | MODULE_DESCRIPTION("Hello world kernel module"); 260 | 261 | static int mymodule_init(void) { 262 | printk(KERN_ALERT "Hello world!\n"); 263 | return 0; 264 | } 265 | 266 | static void mymodule_exit(void) { 267 | /* Do nothing */ 268 | } 269 | 270 | module_init(mymodule_init); 271 | module_exit(mymodule_exit); 272 | ``` 273 | 274 | Build this module. 275 | 276 | ``` 277 | $ cd example/module/hello 278 | $ make 279 | ... 280 | $ 281 | ``` 282 | 283 | Copy it to the test VM. 284 | 285 | ``` 286 | $ make install 287 | ... 288 | $ 289 | ``` 290 | 291 | Load it. Please not that it only succeed if the test VM is booted with 292 | your own kernel. 293 | 294 | ``` 295 | $ make login 296 | ... 297 | vagrant@localhost:~$ sudo su 298 | root@localhost:/home/vagrant# insmod /vagrant/hello.ko 299 | root@localhost:/home/vagrant# 300 | ``` 301 | 302 | See the kernel log. 303 | 304 | ``` 305 | root@localhost:/home/vagrant# dmesg | tail -3 306 | [ 314.198886] random: crng init done 307 | [ 516.935519] hello: loading out-of-tree module taints kernel. 308 | [ 516.936950] Hello world! 309 | root@localhost:/home/vagrant# 310 | ``` 311 | 312 | Succeeded! 313 | 314 | ## Change the kernel configuration 315 | 316 | You can change the kernel configuration, for example for enable a file system disabled by default, 317 | by executing the following commands before running `./test`. 318 | 319 | ``` 320 | $ cp ktest/minconfig{,.bak} 321 | $ cp ktest/minconfig linux/.config 322 | $ cd linux 323 | $ make menuconfig 324 | ...                                      # Here change the configuration as you like 325 | $ mv .config ../ktest/minconfig 326 | $ make mrproper 327 | $ cd ../ 328 | ``` 329 | 330 | If the new kernel built from the new configuration doesn't boot, 331 | please restore the configuration file by `ktest/minconfig{.bak,}`. 332 | 333 | ## Rich tests 334 | 335 | In addition to build, install, and boot your own kernel, elkdat has the following features. 336 | 337 | ### Run your own tests 338 | 339 | Run the following command. 340 | ``` 341 | $ ./test test 342 | ``` 343 | 344 | For example, the following command runs the example/test/hello after booting the system. 345 | 346 | ``` 347 | $ ./test test example/test/hello 348 | ... 349 | ** Monitor flushed ** 350 | run test /home/sat/src/elkdat/example/test/hello 351 | /home/sat/src/elkdat/example/test/hello ... [0 seconds] SUCCESS 352 | kill child process 18446 353 | closing! 354 | 355 | Build time: 6 minutes 53 seconds 356 | Install time: 8 seconds 357 | Reboot time: 17 seconds 358 | Test time: 1 second 359 | 360 | 361 | 362 | ******************************************* 363 | ******************************************* 364 | KTEST RESULT: TEST 1 SUCCESS!!!! ** 365 | ******************************************* 366 | ******************************************* 367 | 368 | 1 of 1 tests were successful 369 | 370 | $ 371 | ``` 372 | 373 | example/test/hello's output is in ktest/ktest.log. 374 | 375 | Here is the result of running example/test/false, it always fails. 376 | 377 | ``` 378 | $ ./test test example/test/fail 379 | ... 380 | ** Monitor flushed ** 381 | run test /home/sat/src/elkdat/example/test/fail 382 | /home/sat/src/elkdat/example/test/fail ... [0 seconds] FAILED! 383 | CRITICAL FAILURE... test failed 384 | REBOOTING 385 | ssh -i /home/sat/src/elkdat/private_key root@192.168.121.181 sync ... [1 second] SUCCESS 386 | ssh -i /home/sat/src/elkdat/private_key root@192.168.121.181 reboot; ... Connection to 192.168.121.181 closed by remote host. 387 | [0 seconds] SUCCESS 388 | See /home/sat/src/elkdat/ktest/ktest.log for more info. 389 | test failed 390 | $ 391 | ``` 392 | 393 | ### Test your patchset one by one 394 | 395 | If you're a kernel developer, you make and submit a patchset for each feature 396 | or bug fix. Of course you should test it before submitting it. Then testing the kernel, 397 | which applies whole patchset, is insufficient. You should test individual patches instead. 398 | It's because if a patch in your patchset has a BUG, it would corrupt git-bisect after 399 | applying your patchset. elkdat can test whole patchset one by one automatically. 400 | 401 | Here is the [example](https://github.com/satoru-takeuchi/elkdat/tree/master/example/kernel-patch/patchcheck) 402 | of a patchset consists of four patches and its 3rd one causes panic during boot. 403 | These patches are quite simple. Please take a look at each patches if you're interested in it. 404 | 405 | ``` 406 | $ git am ../example/kernel-patch/patchcheck/* 407 | $ git log --oneline -5 408 | 706e8dec17ba (HEAD -> patchcheck, checkpatch) 4/4 fine 409 | dc95a48e92b6 3/4 BUG 410 | 0141c43b9cf1 2/4 fine 411 | 0602bde62d19 1/4 fine 412 | 94710cac0ef4 (tag: v4.18) Linux 4.18 413 | $ 414 | ``` 415 | 416 | To test this patchset, run the follownig command. 417 | 418 | ``` 419 | $ cd ../ 420 | ./test patchcheck 0602bde62d19 706e8dec17ba boot 421 | ... 422 | Going to test the following commits: 423 | 0602bde62d19ec5268a8411de70e9c59d39aff5d 1/4 fine 424 | 0141c43b9cf1b3a022ae058c23fb81b2a7ddeae6 2/4 fine 425 | dc95a48e92b6b02d2dea2ca6a2ed6326e8f02c55 3/4 BUG 426 | 706e8dec17bac0cc9bac4d5ac7c39d75994d1e7e 4/4 fine 427 | 428 | Processing commit "0602bde62d19ec5268a8411de70e9c59d39aff5d 1/4 fine" 429 | ... 430 | 431 | Build time: 3 minutes 13 seconds 432 | Install time: 8 seconds 433 | Reboot time: 24 seconds 434 | 435 | Processing commit "0141c43b9cf1b3a022ae058c23fb81b2a7ddeae6 2/4 fine" 436 | ... 437 | Build time: 31 seconds 438 | Install time: 7 seconds 439 | Reboot time: 23 seconds 440 | 441 | Processing commit "dc95a48e92b6b02d2dea2ca6a2ed6326e8f02c55 3/4 BUG" 442 | ... 443 | [5 seconds] FAILED! 444 | power cycle 445 | cd ../elkdat; vagrant halt; sleep 5; vagrant up ... [77 seconds] SUCCESS 446 | ** Wait for monitor to settle down ** 447 | 448 | ** Monitor flushed ** 449 | See /home/sat/src/elkdat/ktest/ktest.log for more info. 450 | failed - never got a boot prompt. 451 | ~/src/elkdat/ktest ~/src/elkdat 452 | ~/src/elkdat 453 | $ 454 | ``` 455 | 456 | Fails on testing 3/4 patch. We succeeded. 457 | 458 | ### Find which commit introduce a bug by bisect 459 | 460 | If you found a kernel didn't work and you also know which kernel worked fine, 461 | `./test bisect` can be used to detect the wrong commit. It works as `git bisect`[^1]. 462 | It's difficult to use `git bisect` directly in kernel development since it requires to 463 | reboot whole system on test one commit. 464 | 465 | [^1]: Please refer to `man 1 git-bisect` if you don't know about this command. 466 | 467 | Here is the [example](https://github.com/satoru-takeuchi/elkdat/tree/master/example/kernel-patch/bisect) 468 | of a patchset consists of ten patches and its 6th one causes panic during boot. 469 | These patches are quite simple. Please take a look at each patches if you're interested in it. 470 | 471 | ``` 472 | $ git am ../example/kernel-patch/bisect/* 473 | $ git log --oneline -11 474 | 2ff43f7d52d0 (HEAD -> bisect) 10/10 BUG 475 | 0c841b3a35cd 9/10 BUG 476 | 001f75285253 8/10 BUG 477 | bae066f6e395 7/10 BUG 478 | 56b54e93122b 6/10 BUG 479 | 2d3dbf6afe6b 5/10 fine 480 | 6b42ac68fa8a 4/10 fine 481 | 5694f923f6f7 3/10 fine 482 | 468bbf83f8a6 2/10 fine 483 | 6d4155d1aecf 1/10 fine 484 | 94710cac0ef4 (tag: v4.18) Linux 4.18 485 | $ 486 | ``` 487 | 488 | To find bad commit which introduce a bug, so it's 6/10 patch, run the following command. 489 | 490 | ``` 491 | $ ../ 492 | $ ./test bisect 6d4155d1aecf 2ff43f7d52d0 boot 493 | ... 494 | RUNNING TEST 1 of 1 with option bisect boot 495 | 496 | git rev-list --max-count=1 6d4155d1aecf ... SUCCESS 497 | git rev-list --max-count=1 2ff43f7d52d0 ... SUCCESS 498 | git bisect start ... [1 second] SUCCESS 499 | git bisect good 6d4155d1aecf51fd878d00143ed2d176e976e587 ... [0 seconds] SUCCESS 500 | git bisect bad 2ff43f7d52d0ef95ebfaf0b4ae805689c6a55cd7 ... SUCCESS 501 | Bisecting: 4 revisions left to test after this (roughly 2 steps) [2d3dbf6afe6b7c3ba89f740ec6c9a719ef9cfb50] 502 | ... 503 | Bisecting: 2 revisions left to test after this (roughly 1 step) [bae066f6e395a8a884864534ff4a3759ede3814b] 504 | 505 | Build time: 3 minutes 13 seconds 506 | Install time: 7 seconds 507 | Reboot time: 24 seconds 508 | ... 509 | Bisecting: 0 revisions left to test after this (roughly 0 steps) [56b54e93122b1d5410ce533a85c1ee53c85279aa] 510 | 511 | Build time: 3 minutes 14 seconds 512 | Install time: 7 seconds 513 | Reboot time: 2 minutes 7 seconds 514 | ... 515 | git bisect bad ... SUCCESS 516 | Found bad commit... 56b54e93122b1d5410ce533a85c1ee53c85279aa 517 | 518 | Build time: 3 minutes 14 seconds 519 | Install time: 13 seconds 520 | Reboot time: 2 minutes 8 seconds 521 | git bisect log ... [0 seconds] SUCCESS 522 | git bisect reset ... [0 seconds] SUCCESS 523 | Bad commit was [56b54e93122b1d5410ce533a85c1ee53c85279aa] 524 | 525 | 526 | 527 | ******************************************* 528 | ******************************************* 529 | KTEST RESULT: TEST 1 SUCCESS!!!! ** 530 | ******************************************* 531 | ******************************************* 532 | 533 | 1 of 1 tests were successful 534 | 535 | ~/src/elkdat/ktest ~/src/elkdat 536 | ~/src/elkdat 537 | $ 538 | ``` 539 | 540 | We successfully detected a correct bad commit, 6/10. 541 | 542 | ### Others 543 | 544 | ./test works as a wrapper of _ktest_, a kernel auto test tool. 545 | Please refer to [linux kernel auto test by using ktest](http://www.slideshare.net/satorutakeuchi18/kernel-auto-testbyktest) 546 | for more information about ktest. 547 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ktest/ktest.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Copyright 2010 - Steven Rostedt , Red Hat Inc. 4 | # Licensed under the terms of the GNU GPL License version 2 5 | # 6 | 7 | use strict; 8 | use IPC::Open2; 9 | use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); 10 | use File::Path qw(mkpath); 11 | use File::Copy qw(cp); 12 | use FileHandle; 13 | use FindBin; 14 | 15 | my $VERSION = "0.2"; 16 | 17 | $| = 1; 18 | 19 | my %opt; 20 | my %repeat_tests; 21 | my %repeats; 22 | my %evals; 23 | 24 | #default opts 25 | my %default = ( 26 | "MAILER" => "sendmail", # default mailer 27 | "EMAIL_ON_ERROR" => 1, 28 | "EMAIL_WHEN_FINISHED" => 1, 29 | "EMAIL_WHEN_CANCELED" => 0, 30 | "EMAIL_WHEN_STARTED" => 0, 31 | "NUM_TESTS" => 1, 32 | "TEST_TYPE" => "build", 33 | "BUILD_TYPE" => "randconfig", 34 | "MAKE_CMD" => "make", 35 | "CLOSE_CONSOLE_SIGNAL" => "INT", 36 | "TIMEOUT" => 120, 37 | "TMP_DIR" => "/tmp/ktest/\${MACHINE}", 38 | "SLEEP_TIME" => 60, # sleep time between tests 39 | "BUILD_NOCLEAN" => 0, 40 | "REBOOT_ON_ERROR" => 0, 41 | "POWEROFF_ON_ERROR" => 0, 42 | "REBOOT_ON_SUCCESS" => 1, 43 | "POWEROFF_ON_SUCCESS" => 0, 44 | "BUILD_OPTIONS" => "", 45 | "BISECT_SLEEP_TIME" => 60, # sleep time between bisects 46 | "PATCHCHECK_SLEEP_TIME" => 60, # sleep time between patch checks 47 | "CLEAR_LOG" => 0, 48 | "BISECT_MANUAL" => 0, 49 | "BISECT_SKIP" => 1, 50 | "BISECT_TRIES" => 1, 51 | "MIN_CONFIG_TYPE" => "boot", 52 | "SUCCESS_LINE" => "login:", 53 | "DETECT_TRIPLE_FAULT" => 1, 54 | "NO_INSTALL" => 0, 55 | "BOOTED_TIMEOUT" => 1, 56 | "DIE_ON_FAILURE" => 1, 57 | "SSH_EXEC" => "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND", 58 | "SCP_TO_TARGET" => "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE", 59 | "SCP_TO_TARGET_INSTALL" => "\${SCP_TO_TARGET}", 60 | "REBOOT" => "ssh \$SSH_USER\@\$MACHINE reboot", 61 | "STOP_AFTER_SUCCESS" => 10, 62 | "STOP_AFTER_FAILURE" => 60, 63 | "STOP_TEST_AFTER" => 600, 64 | "MAX_MONITOR_WAIT" => 1800, 65 | "GRUB_REBOOT" => "grub2-reboot", 66 | "SYSLINUX" => "extlinux", 67 | "SYSLINUX_PATH" => "/boot/extlinux", 68 | "CONNECT_TIMEOUT" => 25, 69 | 70 | # required, and we will ask users if they don't have them but we keep the default 71 | # value something that is common. 72 | "REBOOT_TYPE" => "grub", 73 | "LOCALVERSION" => "-test", 74 | "SSH_USER" => "root", 75 | "BUILD_TARGET" => "arch/x86/boot/bzImage", 76 | "TARGET_IMAGE" => "/boot/vmlinuz-test", 77 | 78 | "LOG_FILE" => undef, 79 | "IGNORE_UNUSED" => 0, 80 | ); 81 | 82 | my $ktest_config = "ktest.conf"; 83 | my $version; 84 | my $have_version = 0; 85 | my $machine; 86 | my $last_machine; 87 | my $ssh_user; 88 | my $tmpdir; 89 | my $builddir; 90 | my $outputdir; 91 | my $output_config; 92 | my $test_type; 93 | my $build_type; 94 | my $build_options; 95 | my $final_post_ktest; 96 | my $pre_ktest; 97 | my $post_ktest; 98 | my $pre_test; 99 | my $post_test; 100 | my $pre_build; 101 | my $post_build; 102 | my $pre_build_die; 103 | my $post_build_die; 104 | my $reboot_type; 105 | my $reboot_script; 106 | my $power_cycle; 107 | my $reboot; 108 | my $reboot_on_error; 109 | my $switch_to_good; 110 | my $switch_to_test; 111 | my $poweroff_on_error; 112 | my $reboot_on_success; 113 | my $die_on_failure; 114 | my $powercycle_after_reboot; 115 | my $poweroff_after_halt; 116 | my $max_monitor_wait; 117 | my $ssh_exec; 118 | my $scp_to_target; 119 | my $scp_to_target_install; 120 | my $power_off; 121 | my $grub_menu; 122 | my $last_grub_menu; 123 | my $grub_file; 124 | my $grub_number; 125 | my $grub_reboot; 126 | my $syslinux; 127 | my $syslinux_path; 128 | my $syslinux_label; 129 | my $target; 130 | my $make; 131 | my $pre_install; 132 | my $post_install; 133 | my $no_install; 134 | my $noclean; 135 | my $minconfig; 136 | my $start_minconfig; 137 | my $start_minconfig_defined; 138 | my $output_minconfig; 139 | my $minconfig_type; 140 | my $use_output_minconfig; 141 | my $warnings_file; 142 | my $ignore_config; 143 | my $ignore_errors; 144 | my $addconfig; 145 | my $in_bisect = 0; 146 | my $bisect_bad_commit = ""; 147 | my $reverse_bisect; 148 | my $bisect_manual; 149 | my $bisect_skip; 150 | my $bisect_tries; 151 | my $config_bisect_good; 152 | my $bisect_ret_good; 153 | my $bisect_ret_bad; 154 | my $bisect_ret_skip; 155 | my $bisect_ret_abort; 156 | my $bisect_ret_default; 157 | my $in_patchcheck = 0; 158 | my $run_test; 159 | my $buildlog; 160 | my $testlog; 161 | my $dmesg; 162 | my $monitor_fp; 163 | my $monitor_pid; 164 | my $monitor_cnt = 0; 165 | my $sleep_time; 166 | my $bisect_sleep_time; 167 | my $patchcheck_sleep_time; 168 | my $ignore_warnings; 169 | my $store_failures; 170 | my $store_successes; 171 | my $test_name; 172 | my $timeout; 173 | my $connect_timeout; 174 | my $config_bisect_exec; 175 | my $booted_timeout; 176 | my $detect_triplefault; 177 | my $console; 178 | my $close_console_signal; 179 | my $reboot_success_line; 180 | my $success_line; 181 | my $stop_after_success; 182 | my $stop_after_failure; 183 | my $stop_test_after; 184 | my $build_target; 185 | my $target_image; 186 | my $checkout; 187 | my $localversion; 188 | my $iteration = 0; 189 | my $successes = 0; 190 | my $stty_orig; 191 | my $run_command_status = 0; 192 | 193 | my $bisect_good; 194 | my $bisect_bad; 195 | my $bisect_type; 196 | my $bisect_start; 197 | my $bisect_replay; 198 | my $bisect_files; 199 | my $bisect_reverse; 200 | my $bisect_check; 201 | 202 | my $config_bisect; 203 | my $config_bisect_type; 204 | my $config_bisect_check; 205 | 206 | my $patchcheck_type; 207 | my $patchcheck_start; 208 | my $patchcheck_cherry; 209 | my $patchcheck_end; 210 | 211 | my $build_time; 212 | my $install_time; 213 | my $reboot_time; 214 | my $test_time; 215 | 216 | my $pwd; 217 | my $dirname = $FindBin::Bin; 218 | 219 | my $mailto; 220 | my $mailer; 221 | my $mail_path; 222 | my $mail_command; 223 | my $email_on_error; 224 | my $email_when_finished; 225 | my $email_when_started; 226 | my $email_when_canceled; 227 | 228 | my $script_start_time = localtime(); 229 | 230 | # set when a test is something other that just building or install 231 | # which would require more options. 232 | my $buildonly = 1; 233 | 234 | # tell build not to worry about warnings, even when WARNINGS_FILE is set 235 | my $warnings_ok = 0; 236 | 237 | # set when creating a new config 238 | my $newconfig = 0; 239 | 240 | my %entered_configs; 241 | my %config_help; 242 | my %variable; 243 | 244 | # force_config is the list of configs that we force enabled (or disabled) 245 | # in a .config file. The MIN_CONFIG and ADD_CONFIG configs. 246 | my %force_config; 247 | 248 | # do not force reboots on config problems 249 | my $no_reboot = 1; 250 | 251 | # reboot on success 252 | my $reboot_success = 0; 253 | 254 | my %option_map = ( 255 | "MAILTO" => \$mailto, 256 | "MAILER" => \$mailer, 257 | "MAIL_PATH" => \$mail_path, 258 | "MAIL_COMMAND" => \$mail_command, 259 | "EMAIL_ON_ERROR" => \$email_on_error, 260 | "EMAIL_WHEN_FINISHED" => \$email_when_finished, 261 | "EMAIL_WHEN_STARTED" => \$email_when_started, 262 | "EMAIL_WHEN_CANCELED" => \$email_when_canceled, 263 | "MACHINE" => \$machine, 264 | "SSH_USER" => \$ssh_user, 265 | "TMP_DIR" => \$tmpdir, 266 | "OUTPUT_DIR" => \$outputdir, 267 | "BUILD_DIR" => \$builddir, 268 | "TEST_TYPE" => \$test_type, 269 | "PRE_KTEST" => \$pre_ktest, 270 | "POST_KTEST" => \$post_ktest, 271 | "PRE_TEST" => \$pre_test, 272 | "POST_TEST" => \$post_test, 273 | "BUILD_TYPE" => \$build_type, 274 | "BUILD_OPTIONS" => \$build_options, 275 | "PRE_BUILD" => \$pre_build, 276 | "POST_BUILD" => \$post_build, 277 | "PRE_BUILD_DIE" => \$pre_build_die, 278 | "POST_BUILD_DIE" => \$post_build_die, 279 | "POWER_CYCLE" => \$power_cycle, 280 | "REBOOT" => \$reboot, 281 | "BUILD_NOCLEAN" => \$noclean, 282 | "MIN_CONFIG" => \$minconfig, 283 | "OUTPUT_MIN_CONFIG" => \$output_minconfig, 284 | "START_MIN_CONFIG" => \$start_minconfig, 285 | "MIN_CONFIG_TYPE" => \$minconfig_type, 286 | "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, 287 | "WARNINGS_FILE" => \$warnings_file, 288 | "IGNORE_CONFIG" => \$ignore_config, 289 | "TEST" => \$run_test, 290 | "ADD_CONFIG" => \$addconfig, 291 | "REBOOT_TYPE" => \$reboot_type, 292 | "GRUB_MENU" => \$grub_menu, 293 | "GRUB_FILE" => \$grub_file, 294 | "GRUB_REBOOT" => \$grub_reboot, 295 | "SYSLINUX" => \$syslinux, 296 | "SYSLINUX_PATH" => \$syslinux_path, 297 | "SYSLINUX_LABEL" => \$syslinux_label, 298 | "PRE_INSTALL" => \$pre_install, 299 | "POST_INSTALL" => \$post_install, 300 | "NO_INSTALL" => \$no_install, 301 | "REBOOT_SCRIPT" => \$reboot_script, 302 | "REBOOT_ON_ERROR" => \$reboot_on_error, 303 | "SWITCH_TO_GOOD" => \$switch_to_good, 304 | "SWITCH_TO_TEST" => \$switch_to_test, 305 | "POWEROFF_ON_ERROR" => \$poweroff_on_error, 306 | "REBOOT_ON_SUCCESS" => \$reboot_on_success, 307 | "DIE_ON_FAILURE" => \$die_on_failure, 308 | "POWER_OFF" => \$power_off, 309 | "POWERCYCLE_AFTER_REBOOT" => \$powercycle_after_reboot, 310 | "POWEROFF_AFTER_HALT" => \$poweroff_after_halt, 311 | "MAX_MONITOR_WAIT" => \$max_monitor_wait, 312 | "SLEEP_TIME" => \$sleep_time, 313 | "BISECT_SLEEP_TIME" => \$bisect_sleep_time, 314 | "PATCHCHECK_SLEEP_TIME" => \$patchcheck_sleep_time, 315 | "IGNORE_WARNINGS" => \$ignore_warnings, 316 | "IGNORE_ERRORS" => \$ignore_errors, 317 | "BISECT_MANUAL" => \$bisect_manual, 318 | "BISECT_SKIP" => \$bisect_skip, 319 | "BISECT_TRIES" => \$bisect_tries, 320 | "CONFIG_BISECT_GOOD" => \$config_bisect_good, 321 | "BISECT_RET_GOOD" => \$bisect_ret_good, 322 | "BISECT_RET_BAD" => \$bisect_ret_bad, 323 | "BISECT_RET_SKIP" => \$bisect_ret_skip, 324 | "BISECT_RET_ABORT" => \$bisect_ret_abort, 325 | "BISECT_RET_DEFAULT" => \$bisect_ret_default, 326 | "STORE_FAILURES" => \$store_failures, 327 | "STORE_SUCCESSES" => \$store_successes, 328 | "TEST_NAME" => \$test_name, 329 | "TIMEOUT" => \$timeout, 330 | "CONNECT_TIMEOUT" => \$connect_timeout, 331 | "CONFIG_BISECT_EXEC" => \$config_bisect_exec, 332 | "BOOTED_TIMEOUT" => \$booted_timeout, 333 | "CONSOLE" => \$console, 334 | "CLOSE_CONSOLE_SIGNAL" => \$close_console_signal, 335 | "DETECT_TRIPLE_FAULT" => \$detect_triplefault, 336 | "SUCCESS_LINE" => \$success_line, 337 | "REBOOT_SUCCESS_LINE" => \$reboot_success_line, 338 | "STOP_AFTER_SUCCESS" => \$stop_after_success, 339 | "STOP_AFTER_FAILURE" => \$stop_after_failure, 340 | "STOP_TEST_AFTER" => \$stop_test_after, 341 | "BUILD_TARGET" => \$build_target, 342 | "SSH_EXEC" => \$ssh_exec, 343 | "SCP_TO_TARGET" => \$scp_to_target, 344 | "SCP_TO_TARGET_INSTALL" => \$scp_to_target_install, 345 | "CHECKOUT" => \$checkout, 346 | "TARGET_IMAGE" => \$target_image, 347 | "LOCALVERSION" => \$localversion, 348 | 349 | "BISECT_GOOD" => \$bisect_good, 350 | "BISECT_BAD" => \$bisect_bad, 351 | "BISECT_TYPE" => \$bisect_type, 352 | "BISECT_START" => \$bisect_start, 353 | "BISECT_REPLAY" => \$bisect_replay, 354 | "BISECT_FILES" => \$bisect_files, 355 | "BISECT_REVERSE" => \$bisect_reverse, 356 | "BISECT_CHECK" => \$bisect_check, 357 | 358 | "CONFIG_BISECT" => \$config_bisect, 359 | "CONFIG_BISECT_TYPE" => \$config_bisect_type, 360 | "CONFIG_BISECT_CHECK" => \$config_bisect_check, 361 | 362 | "PATCHCHECK_TYPE" => \$patchcheck_type, 363 | "PATCHCHECK_START" => \$patchcheck_start, 364 | "PATCHCHECK_CHERRY" => \$patchcheck_cherry, 365 | "PATCHCHECK_END" => \$patchcheck_end, 366 | ); 367 | 368 | # Options may be used by other options, record them. 369 | my %used_options; 370 | 371 | # default variables that can be used 372 | chomp ($variable{"PWD"} = `pwd`); 373 | $pwd = $variable{"PWD"}; 374 | 375 | $config_help{"MACHINE"} = << "EOF" 376 | The machine hostname that you will test. 377 | For build only tests, it is still needed to differentiate log files. 378 | EOF 379 | ; 380 | $config_help{"SSH_USER"} = << "EOF" 381 | The box is expected to have ssh on normal bootup, provide the user 382 | (most likely root, since you need privileged operations) 383 | EOF 384 | ; 385 | $config_help{"BUILD_DIR"} = << "EOF" 386 | The directory that contains the Linux source code (full path). 387 | You can use \${PWD} that will be the path where ktest.pl is run, or use 388 | \${THIS_DIR} which is assigned \${PWD} but may be changed later. 389 | EOF 390 | ; 391 | $config_help{"OUTPUT_DIR"} = << "EOF" 392 | The directory that the objects will be built (full path). 393 | (can not be same as BUILD_DIR) 394 | You can use \${PWD} that will be the path where ktest.pl is run, or use 395 | \${THIS_DIR} which is assigned \${PWD} but may be changed later. 396 | EOF 397 | ; 398 | $config_help{"BUILD_TARGET"} = << "EOF" 399 | The location of the compiled file to copy to the target. 400 | (relative to OUTPUT_DIR) 401 | EOF 402 | ; 403 | $config_help{"BUILD_OPTIONS"} = << "EOF" 404 | Options to add to \"make\" when building. 405 | i.e. -j20 406 | EOF 407 | ; 408 | $config_help{"TARGET_IMAGE"} = << "EOF" 409 | The place to put your image on the test machine. 410 | EOF 411 | ; 412 | $config_help{"POWER_CYCLE"} = << "EOF" 413 | A script or command to reboot the box. 414 | 415 | Here is a digital loggers power switch example 416 | POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin\@power/outlet?5=CCL' 417 | 418 | Here is an example to reboot a virtual box on the current host 419 | with the name "Guest". 420 | POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest 421 | EOF 422 | ; 423 | $config_help{"CONSOLE"} = << "EOF" 424 | The script or command that reads the console 425 | 426 | If you use ttywatch server, something like the following would work. 427 | CONSOLE = nc -d localhost 3001 428 | 429 | For a virtual machine with guest name "Guest". 430 | CONSOLE = virsh console Guest 431 | EOF 432 | ; 433 | $config_help{"LOCALVERSION"} = << "EOF" 434 | Required version ending to differentiate the test 435 | from other linux builds on the system. 436 | EOF 437 | ; 438 | $config_help{"REBOOT_TYPE"} = << "EOF" 439 | Way to reboot the box to the test kernel. 440 | Only valid options so far are "grub", "grub2", "syslinux", and "script". 441 | 442 | If you specify grub, it will assume grub version 1 443 | and will search in /boot/grub/menu.lst for the title \$GRUB_MENU 444 | and select that target to reboot to the kernel. If this is not 445 | your setup, then specify "script" and have a command or script 446 | specified in REBOOT_SCRIPT to boot to the target. 447 | 448 | The entry in /boot/grub/menu.lst must be entered in manually. 449 | The test will not modify that file. 450 | 451 | If you specify grub2, then you also need to specify both \$GRUB_MENU 452 | and \$GRUB_FILE. 453 | 454 | If you specify syslinux, then you may use SYSLINUX to define the syslinux 455 | command (defaults to extlinux), and SYSLINUX_PATH to specify the path to 456 | the syslinux install (defaults to /boot/extlinux). But you have to specify 457 | SYSLINUX_LABEL to define the label to boot to for the test kernel. 458 | EOF 459 | ; 460 | $config_help{"GRUB_MENU"} = << "EOF" 461 | The grub title name for the test kernel to boot 462 | (Only mandatory if REBOOT_TYPE = grub or grub2) 463 | 464 | Note, ktest.pl will not update the grub menu.lst, you need to 465 | manually add an option for the test. ktest.pl will search 466 | the grub menu.lst for this option to find what kernel to 467 | reboot into. 468 | 469 | For example, if in the /boot/grub/menu.lst the test kernel title has: 470 | title Test Kernel 471 | kernel vmlinuz-test 472 | GRUB_MENU = Test Kernel 473 | 474 | For grub2, a search of \$GRUB_FILE is performed for the lines 475 | that begin with "menuentry". It will not detect submenus. The 476 | menu must be a non-nested menu. Add the quotes used in the menu 477 | to guarantee your selection, as the first menuentry with the content 478 | of \$GRUB_MENU that is found will be used. 479 | EOF 480 | ; 481 | $config_help{"GRUB_FILE"} = << "EOF" 482 | If grub2 is used, the full path for the grub.cfg file is placed 483 | here. Use something like /boot/grub2/grub.cfg to search. 484 | EOF 485 | ; 486 | $config_help{"SYSLINUX_LABEL"} = << "EOF" 487 | If syslinux is used, the label that boots the target kernel must 488 | be specified with SYSLINUX_LABEL. 489 | EOF 490 | ; 491 | $config_help{"REBOOT_SCRIPT"} = << "EOF" 492 | A script to reboot the target into the test kernel 493 | (Only mandatory if REBOOT_TYPE = script) 494 | EOF 495 | ; 496 | 497 | sub _logit { 498 | if (defined($opt{"LOG_FILE"})) { 499 | open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; 500 | print OUT @_; 501 | close(OUT); 502 | } 503 | } 504 | 505 | sub logit { 506 | if (defined($opt{"LOG_FILE"})) { 507 | _logit @_; 508 | } else { 509 | print @_; 510 | } 511 | } 512 | 513 | sub doprint { 514 | print @_; 515 | _logit @_; 516 | } 517 | 518 | sub read_prompt { 519 | my ($cancel, $prompt) = @_; 520 | 521 | my $ans; 522 | 523 | for (;;) { 524 | if ($cancel) { 525 | print "$prompt [y/n/C] "; 526 | } else { 527 | print "$prompt [Y/n] "; 528 | } 529 | $ans = ; 530 | chomp $ans; 531 | if ($ans =~ /^\s*$/) { 532 | if ($cancel) { 533 | $ans = "c"; 534 | } else { 535 | $ans = "y"; 536 | } 537 | } 538 | last if ($ans =~ /^y$/i || $ans =~ /^n$/i); 539 | if ($cancel) { 540 | last if ($ans =~ /^c$/i); 541 | print "Please answer either 'y', 'n' or 'c'.\n"; 542 | } else { 543 | print "Please answer either 'y' or 'n'.\n"; 544 | } 545 | } 546 | if ($ans =~ /^c/i) { 547 | exit; 548 | } 549 | if ($ans !~ /^y$/i) { 550 | return 0; 551 | } 552 | return 1; 553 | } 554 | 555 | sub read_yn { 556 | my ($prompt) = @_; 557 | 558 | return read_prompt 0, $prompt; 559 | } 560 | 561 | sub read_ync { 562 | my ($prompt) = @_; 563 | 564 | return read_prompt 1, $prompt; 565 | } 566 | 567 | sub get_mandatory_config { 568 | my ($config) = @_; 569 | my $ans; 570 | 571 | return if (defined($opt{$config})); 572 | 573 | if (defined($config_help{$config})) { 574 | print "\n"; 575 | print $config_help{$config}; 576 | } 577 | 578 | for (;;) { 579 | print "$config = "; 580 | if (defined($default{$config}) && length($default{$config})) { 581 | print "\[$default{$config}\] "; 582 | } 583 | $ans = ; 584 | $ans =~ s/^\s*(.*\S)\s*$/$1/; 585 | if ($ans =~ /^\s*$/) { 586 | if ($default{$config}) { 587 | $ans = $default{$config}; 588 | } else { 589 | print "Your answer can not be blank\n"; 590 | next; 591 | } 592 | } 593 | $entered_configs{$config} = ${ans}; 594 | last; 595 | } 596 | } 597 | 598 | sub show_time { 599 | my ($time) = @_; 600 | 601 | my $hours = 0; 602 | my $minutes = 0; 603 | 604 | if ($time > 3600) { 605 | $hours = int($time / 3600); 606 | $time -= $hours * 3600; 607 | } 608 | if ($time > 60) { 609 | $minutes = int($time / 60); 610 | $time -= $minutes * 60; 611 | } 612 | 613 | if ($hours > 0) { 614 | doprint "$hours hour"; 615 | doprint "s" if ($hours > 1); 616 | doprint " "; 617 | } 618 | 619 | if ($minutes > 0) { 620 | doprint "$minutes minute"; 621 | doprint "s" if ($minutes > 1); 622 | doprint " "; 623 | } 624 | 625 | doprint "$time second"; 626 | doprint "s" if ($time != 1); 627 | } 628 | 629 | sub print_times { 630 | doprint "\n"; 631 | if ($build_time) { 632 | doprint "Build time: "; 633 | show_time($build_time); 634 | doprint "\n"; 635 | } 636 | if ($install_time) { 637 | doprint "Install time: "; 638 | show_time($install_time); 639 | doprint "\n"; 640 | } 641 | if ($reboot_time) { 642 | doprint "Reboot time: "; 643 | show_time($reboot_time); 644 | doprint "\n"; 645 | } 646 | if ($test_time) { 647 | doprint "Test time: "; 648 | show_time($test_time); 649 | doprint "\n"; 650 | } 651 | # reset for iterations like bisect 652 | $build_time = 0; 653 | $install_time = 0; 654 | $reboot_time = 0; 655 | $test_time = 0; 656 | } 657 | 658 | sub get_mandatory_configs { 659 | get_mandatory_config("MACHINE"); 660 | get_mandatory_config("BUILD_DIR"); 661 | get_mandatory_config("OUTPUT_DIR"); 662 | 663 | if ($newconfig) { 664 | get_mandatory_config("BUILD_OPTIONS"); 665 | } 666 | 667 | # options required for other than just building a kernel 668 | if (!$buildonly) { 669 | get_mandatory_config("POWER_CYCLE"); 670 | get_mandatory_config("CONSOLE"); 671 | } 672 | 673 | # options required for install and more 674 | if ($buildonly != 1) { 675 | get_mandatory_config("SSH_USER"); 676 | get_mandatory_config("BUILD_TARGET"); 677 | get_mandatory_config("TARGET_IMAGE"); 678 | } 679 | 680 | get_mandatory_config("LOCALVERSION"); 681 | 682 | return if ($buildonly); 683 | 684 | my $rtype = $opt{"REBOOT_TYPE"}; 685 | 686 | if (!defined($rtype)) { 687 | if (!defined($opt{"GRUB_MENU"})) { 688 | get_mandatory_config("REBOOT_TYPE"); 689 | $rtype = $entered_configs{"REBOOT_TYPE"}; 690 | } else { 691 | $rtype = "grub"; 692 | } 693 | } 694 | 695 | if ($rtype eq "grub") { 696 | get_mandatory_config("GRUB_MENU"); 697 | } 698 | 699 | if ($rtype eq "grub2") { 700 | get_mandatory_config("GRUB_MENU"); 701 | get_mandatory_config("GRUB_FILE"); 702 | } 703 | 704 | if ($rtype eq "syslinux") { 705 | get_mandatory_config("SYSLINUX_LABEL"); 706 | } 707 | } 708 | 709 | sub process_variables { 710 | my ($value, $remove_undef) = @_; 711 | my $retval = ""; 712 | 713 | # We want to check for '\', and it is just easier 714 | # to check the previous characet of '$' and not need 715 | # to worry if '$' is the first character. By adding 716 | # a space to $value, we can just check [^\\]\$ and 717 | # it will still work. 718 | $value = " $value"; 719 | 720 | while ($value =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) { 721 | my $begin = $1; 722 | my $var = $2; 723 | my $end = $3; 724 | # append beginning of value to retval 725 | $retval = "$retval$begin"; 726 | if (defined($variable{$var})) { 727 | $retval = "$retval$variable{$var}"; 728 | } elsif (defined($remove_undef) && $remove_undef) { 729 | # for if statements, any variable that is not defined, 730 | # we simple convert to 0 731 | $retval = "${retval}0"; 732 | } else { 733 | # put back the origin piece. 734 | $retval = "$retval\$\{$var\}"; 735 | # This could be an option that is used later, save 736 | # it so we don't warn if this option is not one of 737 | # ktests options. 738 | $used_options{$var} = 1; 739 | } 740 | $value = $end; 741 | } 742 | $retval = "$retval$value"; 743 | 744 | # remove the space added in the beginning 745 | $retval =~ s/ //; 746 | 747 | return "$retval" 748 | } 749 | 750 | sub set_value { 751 | my ($lvalue, $rvalue, $override, $overrides, $name) = @_; 752 | 753 | my $prvalue = process_variables($rvalue); 754 | 755 | if ($lvalue =~ /^(TEST|BISECT|CONFIG_BISECT)_TYPE(\[.*\])?$/ && 756 | $prvalue !~ /^(config_|)bisect$/ && 757 | $prvalue !~ /^build$/ && 758 | $buildonly) { 759 | 760 | # Note if a test is something other than build, then we 761 | # will need other mandatory options. 762 | if ($prvalue ne "install") { 763 | $buildonly = 0; 764 | } else { 765 | # install still limits some mandatory options. 766 | $buildonly = 2; 767 | } 768 | } 769 | 770 | if (defined($opt{$lvalue})) { 771 | if (!$override || defined(${$overrides}{$lvalue})) { 772 | my $extra = ""; 773 | if ($override) { 774 | $extra = "In the same override section!\n"; 775 | } 776 | die "$name: $.: Option $lvalue defined more than once!\n$extra"; 777 | } 778 | ${$overrides}{$lvalue} = $prvalue; 779 | } 780 | 781 | $opt{$lvalue} = $prvalue; 782 | } 783 | 784 | sub set_eval { 785 | my ($lvalue, $rvalue, $name) = @_; 786 | 787 | my $prvalue = process_variables($rvalue); 788 | my $arr; 789 | 790 | if (defined($evals{$lvalue})) { 791 | $arr = $evals{$lvalue}; 792 | } else { 793 | $arr = []; 794 | $evals{$lvalue} = $arr; 795 | } 796 | 797 | push @{$arr}, $rvalue; 798 | } 799 | 800 | sub set_variable { 801 | my ($lvalue, $rvalue) = @_; 802 | 803 | if ($rvalue =~ /^\s*$/) { 804 | delete $variable{$lvalue}; 805 | } else { 806 | $rvalue = process_variables($rvalue); 807 | $variable{$lvalue} = $rvalue; 808 | } 809 | } 810 | 811 | sub process_compare { 812 | my ($lval, $cmp, $rval) = @_; 813 | 814 | # remove whitespace 815 | 816 | $lval =~ s/^\s*//; 817 | $lval =~ s/\s*$//; 818 | 819 | $rval =~ s/^\s*//; 820 | $rval =~ s/\s*$//; 821 | 822 | if ($cmp eq "==") { 823 | return $lval eq $rval; 824 | } elsif ($cmp eq "!=") { 825 | return $lval ne $rval; 826 | } elsif ($cmp eq "=~") { 827 | return $lval =~ m/$rval/; 828 | } elsif ($cmp eq "!~") { 829 | return $lval !~ m/$rval/; 830 | } 831 | 832 | my $statement = "$lval $cmp $rval"; 833 | my $ret = eval $statement; 834 | 835 | # $@ stores error of eval 836 | if ($@) { 837 | return -1; 838 | } 839 | 840 | return $ret; 841 | } 842 | 843 | sub value_defined { 844 | my ($val) = @_; 845 | 846 | return defined($variable{$2}) || 847 | defined($opt{$2}); 848 | } 849 | 850 | my $d = 0; 851 | sub process_expression { 852 | my ($name, $val) = @_; 853 | 854 | my $c = $d++; 855 | 856 | while ($val =~ s/\(([^\(]*?)\)/\&\&\&\&VAL\&\&\&\&/) { 857 | my $express = $1; 858 | 859 | if (process_expression($name, $express)) { 860 | $val =~ s/\&\&\&\&VAL\&\&\&\&/ 1 /; 861 | } else { 862 | $val =~ s/\&\&\&\&VAL\&\&\&\&/ 0 /; 863 | } 864 | } 865 | 866 | $d--; 867 | my $OR = "\\|\\|"; 868 | my $AND = "\\&\\&"; 869 | 870 | while ($val =~ s/^(.*?)($OR|$AND)//) { 871 | my $express = $1; 872 | my $op = $2; 873 | 874 | if (process_expression($name, $express)) { 875 | if ($op eq "||") { 876 | return 1; 877 | } 878 | } else { 879 | if ($op eq "&&") { 880 | return 0; 881 | } 882 | } 883 | } 884 | 885 | if ($val =~ /(.*)(==|\!=|>=|<=|>|<|=~|\!~)(.*)/) { 886 | my $ret = process_compare($1, $2, $3); 887 | if ($ret < 0) { 888 | die "$name: $.: Unable to process comparison\n"; 889 | } 890 | return $ret; 891 | } 892 | 893 | if ($val =~ /^\s*(NOT\s*)?DEFINED\s+(\S+)\s*$/) { 894 | if (defined $1) { 895 | return !value_defined($2); 896 | } else { 897 | return value_defined($2); 898 | } 899 | } 900 | 901 | if ($val =~ /^\s*0\s*$/) { 902 | return 0; 903 | } elsif ($val =~ /^\s*\d+\s*$/) { 904 | return 1; 905 | } 906 | 907 | die ("$name: $.: Undefined content $val in if statement\n"); 908 | } 909 | 910 | sub process_if { 911 | my ($name, $value) = @_; 912 | 913 | # Convert variables and replace undefined ones with 0 914 | my $val = process_variables($value, 1); 915 | my $ret = process_expression $name, $val; 916 | 917 | return $ret; 918 | } 919 | 920 | sub __read_config { 921 | my ($config, $current_test_num) = @_; 922 | 923 | my $in; 924 | open($in, $config) || die "can't read file $config"; 925 | 926 | my $name = $config; 927 | $name =~ s,.*/(.*),$1,; 928 | 929 | my $test_num = $$current_test_num; 930 | my $default = 1; 931 | my $repeat = 1; 932 | my $num_tests_set = 0; 933 | my $skip = 0; 934 | my $rest; 935 | my $line; 936 | my $test_case = 0; 937 | my $if = 0; 938 | my $if_set = 0; 939 | my $override = 0; 940 | 941 | my %overrides; 942 | 943 | while (<$in>) { 944 | 945 | # ignore blank lines and comments 946 | next if (/^\s*$/ || /\s*\#/); 947 | 948 | if (/^\s*(TEST_START|DEFAULTS)\b(.*)/) { 949 | 950 | my $type = $1; 951 | $rest = $2; 952 | $line = $2; 953 | 954 | my $old_test_num; 955 | my $old_repeat; 956 | $override = 0; 957 | 958 | if ($type eq "TEST_START") { 959 | 960 | if ($num_tests_set) { 961 | die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; 962 | } 963 | 964 | $old_test_num = $test_num; 965 | $old_repeat = $repeat; 966 | 967 | $test_num += $repeat; 968 | $default = 0; 969 | $repeat = 1; 970 | } else { 971 | $default = 1; 972 | } 973 | 974 | # If SKIP is anywhere in the line, the command will be skipped 975 | if ($rest =~ s/\s+SKIP\b//) { 976 | $skip = 1; 977 | } else { 978 | $test_case = 1; 979 | $skip = 0; 980 | } 981 | 982 | if ($rest =~ s/\sELSE\b//) { 983 | if (!$if) { 984 | die "$name: $.: ELSE found with out matching IF section\n$_"; 985 | } 986 | $if = 0; 987 | 988 | if ($if_set) { 989 | $skip = 1; 990 | } else { 991 | $skip = 0; 992 | } 993 | } 994 | 995 | if ($rest =~ s/\sIF\s+(.*)//) { 996 | if (process_if($name, $1)) { 997 | $if_set = 1; 998 | } else { 999 | $skip = 1; 1000 | } 1001 | $if = 1; 1002 | } else { 1003 | $if = 0; 1004 | $if_set = 0; 1005 | } 1006 | 1007 | if (!$skip) { 1008 | if ($type eq "TEST_START") { 1009 | if ($rest =~ s/\s+ITERATE\s+(\d+)//) { 1010 | $repeat = $1; 1011 | $repeat_tests{"$test_num"} = $repeat; 1012 | } 1013 | } elsif ($rest =~ s/\sOVERRIDE\b//) { 1014 | # DEFAULT only 1015 | $override = 1; 1016 | # Clear previous overrides 1017 | %overrides = (); 1018 | } 1019 | } 1020 | 1021 | if (!$skip && $rest !~ /^\s*$/) { 1022 | die "$name: $.: Gargbage found after $type\n$_"; 1023 | } 1024 | 1025 | if ($skip && $type eq "TEST_START") { 1026 | $test_num = $old_test_num; 1027 | $repeat = $old_repeat; 1028 | } 1029 | 1030 | } elsif (/^\s*ELSE\b(.*)$/) { 1031 | if (!$if) { 1032 | die "$name: $.: ELSE found with out matching IF section\n$_"; 1033 | } 1034 | $rest = $1; 1035 | if ($if_set) { 1036 | $skip = 1; 1037 | $rest = ""; 1038 | } else { 1039 | $skip = 0; 1040 | 1041 | if ($rest =~ /\sIF\s+(.*)/) { 1042 | # May be a ELSE IF section. 1043 | if (process_if($name, $1)) { 1044 | $if_set = 1; 1045 | } else { 1046 | $skip = 1; 1047 | } 1048 | $rest = ""; 1049 | } else { 1050 | $if = 0; 1051 | } 1052 | } 1053 | 1054 | if ($rest !~ /^\s*$/) { 1055 | die "$name: $.: Gargbage found after DEFAULTS\n$_"; 1056 | } 1057 | 1058 | } elsif (/^\s*INCLUDE\s+(\S+)/) { 1059 | 1060 | next if ($skip); 1061 | 1062 | if (!$default) { 1063 | die "$name: $.: INCLUDE can only be done in default sections\n$_"; 1064 | } 1065 | 1066 | my $file = process_variables($1); 1067 | 1068 | if ($file !~ m,^/,) { 1069 | # check the path of the config file first 1070 | if ($config =~ m,(.*)/,) { 1071 | if (-f "$1/$file") { 1072 | $file = "$1/$file"; 1073 | } 1074 | } 1075 | } 1076 | 1077 | if ( ! -r $file ) { 1078 | die "$name: $.: Can't read file $file\n$_"; 1079 | } 1080 | 1081 | if (__read_config($file, \$test_num)) { 1082 | $test_case = 1; 1083 | } 1084 | 1085 | } elsif (/^\s*([A-Z_\[\]\d]+)\s*=~\s*(.*?)\s*$/) { 1086 | 1087 | next if ($skip); 1088 | 1089 | my $lvalue = $1; 1090 | my $rvalue = $2; 1091 | 1092 | if ($default || $lvalue =~ /\[\d+\]$/) { 1093 | set_eval($lvalue, $rvalue, $name); 1094 | } else { 1095 | my $val = "$lvalue\[$test_num\]"; 1096 | set_eval($val, $rvalue, $name); 1097 | } 1098 | 1099 | } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) { 1100 | 1101 | next if ($skip); 1102 | 1103 | my $lvalue = $1; 1104 | my $rvalue = $2; 1105 | 1106 | if (!$default && 1107 | ($lvalue eq "NUM_TESTS" || 1108 | $lvalue eq "LOG_FILE" || 1109 | $lvalue eq "CLEAR_LOG")) { 1110 | die "$name: $.: $lvalue must be set in DEFAULTS section\n"; 1111 | } 1112 | 1113 | if ($lvalue eq "NUM_TESTS") { 1114 | if ($test_num) { 1115 | die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; 1116 | } 1117 | if (!$default) { 1118 | die "$name: $.: NUM_TESTS must be set in default section\n"; 1119 | } 1120 | $num_tests_set = 1; 1121 | } 1122 | 1123 | if ($default || $lvalue =~ /\[\d+\]$/) { 1124 | set_value($lvalue, $rvalue, $override, \%overrides, $name); 1125 | } else { 1126 | my $val = "$lvalue\[$test_num\]"; 1127 | set_value($val, $rvalue, $override, \%overrides, $name); 1128 | 1129 | if ($repeat > 1) { 1130 | $repeats{$val} = $repeat; 1131 | } 1132 | } 1133 | } elsif (/^\s*([A-Z_\[\]\d]+)\s*:=\s*(.*?)\s*$/) { 1134 | next if ($skip); 1135 | 1136 | my $lvalue = $1; 1137 | my $rvalue = $2; 1138 | 1139 | # process config variables. 1140 | # Config variables are only active while reading the 1141 | # config and can be defined anywhere. They also ignore 1142 | # TEST_START and DEFAULTS, but are skipped if they are in 1143 | # on of these sections that have SKIP defined. 1144 | # The save variable can be 1145 | # defined multiple times and the new one simply overrides 1146 | # the prevous one. 1147 | set_variable($lvalue, $rvalue); 1148 | 1149 | } else { 1150 | die "$name: $.: Garbage found in config\n$_"; 1151 | } 1152 | } 1153 | 1154 | if ($test_num) { 1155 | $test_num += $repeat - 1; 1156 | $opt{"NUM_TESTS"} = $test_num; 1157 | } 1158 | 1159 | close($in); 1160 | 1161 | $$current_test_num = $test_num; 1162 | 1163 | return $test_case; 1164 | } 1165 | 1166 | sub get_test_case { 1167 | print "What test case would you like to run?\n"; 1168 | print " (build, install or boot)\n"; 1169 | print " Other tests are available but require editing ktest.conf\n"; 1170 | print " (see tools/testing/ktest/sample.conf)\n"; 1171 | my $ans = ; 1172 | chomp $ans; 1173 | $default{"TEST_TYPE"} = $ans; 1174 | } 1175 | 1176 | sub read_config { 1177 | my ($config) = @_; 1178 | 1179 | my $test_case; 1180 | my $test_num = 0; 1181 | 1182 | $test_case = __read_config $config, \$test_num; 1183 | 1184 | # make sure we have all mandatory configs 1185 | get_mandatory_configs; 1186 | 1187 | # was a test specified? 1188 | if (!$test_case) { 1189 | print "No test case specified.\n"; 1190 | get_test_case; 1191 | } 1192 | 1193 | # set any defaults 1194 | 1195 | foreach my $default (keys %default) { 1196 | if (!defined($opt{$default})) { 1197 | $opt{$default} = $default{$default}; 1198 | } 1199 | } 1200 | 1201 | if ($opt{"IGNORE_UNUSED"} == 1) { 1202 | return; 1203 | } 1204 | 1205 | my %not_used; 1206 | 1207 | # check if there are any stragglers (typos?) 1208 | foreach my $option (keys %opt) { 1209 | my $op = $option; 1210 | # remove per test labels. 1211 | $op =~ s/\[.*\]//; 1212 | if (!exists($option_map{$op}) && 1213 | !exists($default{$op}) && 1214 | !exists($used_options{$op})) { 1215 | $not_used{$op} = 1; 1216 | } 1217 | } 1218 | 1219 | if (%not_used) { 1220 | my $s = "s are"; 1221 | $s = " is" if (keys %not_used == 1); 1222 | print "The following option$s not used; could be a typo:\n"; 1223 | foreach my $option (keys %not_used) { 1224 | print "$option\n"; 1225 | } 1226 | print "Set IGRNORE_UNUSED = 1 to have ktest ignore unused variables\n"; 1227 | if (!read_yn "Do you want to continue?") { 1228 | exit -1; 1229 | } 1230 | } 1231 | } 1232 | 1233 | sub __eval_option { 1234 | my ($name, $option, $i) = @_; 1235 | 1236 | # Add space to evaluate the character before $ 1237 | $option = " $option"; 1238 | my $retval = ""; 1239 | my $repeated = 0; 1240 | my $parent = 0; 1241 | 1242 | foreach my $test (keys %repeat_tests) { 1243 | if ($i >= $test && 1244 | $i < $test + $repeat_tests{$test}) { 1245 | 1246 | $repeated = 1; 1247 | $parent = $test; 1248 | last; 1249 | } 1250 | } 1251 | 1252 | while ($option =~ /(.*?[^\\])\$\{(.*?)\}(.*)/) { 1253 | my $start = $1; 1254 | my $var = $2; 1255 | my $end = $3; 1256 | 1257 | # Append beginning of line 1258 | $retval = "$retval$start"; 1259 | 1260 | # If the iteration option OPT[$i] exists, then use that. 1261 | # otherwise see if the default OPT (without [$i]) exists. 1262 | 1263 | my $o = "$var\[$i\]"; 1264 | my $parento = "$var\[$parent\]"; 1265 | 1266 | # If a variable contains itself, use the default var 1267 | if (($var eq $name) && defined($opt{$var})) { 1268 | $o = $opt{$var}; 1269 | $retval = "$retval$o"; 1270 | } elsif (defined($opt{$o})) { 1271 | $o = $opt{$o}; 1272 | $retval = "$retval$o"; 1273 | } elsif ($repeated && defined($opt{$parento})) { 1274 | $o = $opt{$parento}; 1275 | $retval = "$retval$o"; 1276 | } elsif (defined($opt{$var})) { 1277 | $o = $opt{$var}; 1278 | $retval = "$retval$o"; 1279 | } elsif ($var eq "KERNEL_VERSION" && defined($make)) { 1280 | # special option KERNEL_VERSION uses kernel version 1281 | get_version(); 1282 | $retval = "$retval$version"; 1283 | } else { 1284 | $retval = "$retval\$\{$var\}"; 1285 | } 1286 | 1287 | $option = $end; 1288 | } 1289 | 1290 | $retval = "$retval$option"; 1291 | 1292 | $retval =~ s/^ //; 1293 | 1294 | return $retval; 1295 | } 1296 | 1297 | sub process_evals { 1298 | my ($name, $option, $i) = @_; 1299 | 1300 | my $option_name = "$name\[$i\]"; 1301 | my $ev; 1302 | 1303 | my $old_option = $option; 1304 | 1305 | if (defined($evals{$option_name})) { 1306 | $ev = $evals{$option_name}; 1307 | } elsif (defined($evals{$name})) { 1308 | $ev = $evals{$name}; 1309 | } else { 1310 | return $option; 1311 | } 1312 | 1313 | for my $e (@{$ev}) { 1314 | eval "\$option =~ $e"; 1315 | } 1316 | 1317 | if ($option ne $old_option) { 1318 | doprint("$name changed from '$old_option' to '$option'\n"); 1319 | } 1320 | 1321 | return $option; 1322 | } 1323 | 1324 | sub eval_option { 1325 | my ($name, $option, $i) = @_; 1326 | 1327 | my $prev = ""; 1328 | 1329 | # Since an option can evaluate to another option, 1330 | # keep iterating until we do not evaluate any more 1331 | # options. 1332 | my $r = 0; 1333 | while ($prev ne $option) { 1334 | # Check for recursive evaluations. 1335 | # 100 deep should be more than enough. 1336 | if ($r++ > 100) { 1337 | die "Over 100 evaluations accurred with $option\n" . 1338 | "Check for recursive variables\n"; 1339 | } 1340 | $prev = $option; 1341 | $option = __eval_option($name, $option, $i); 1342 | } 1343 | 1344 | $option = process_evals($name, $option, $i); 1345 | 1346 | return $option; 1347 | } 1348 | 1349 | sub run_command; 1350 | sub start_monitor; 1351 | sub end_monitor; 1352 | sub wait_for_monitor; 1353 | 1354 | sub reboot { 1355 | my ($time) = @_; 1356 | my $powercycle = 0; 1357 | 1358 | # test if the machine can be connected to within a few seconds 1359 | my $stat = run_ssh("echo check machine status", $connect_timeout); 1360 | if (!$stat) { 1361 | doprint("power cycle\n"); 1362 | $powercycle = 1; 1363 | } 1364 | 1365 | if ($powercycle) { 1366 | run_command "$power_cycle"; 1367 | 1368 | start_monitor; 1369 | # flush out current monitor 1370 | # May contain the reboot success line 1371 | wait_for_monitor 1; 1372 | 1373 | } else { 1374 | # Make sure everything has been written to disk 1375 | run_ssh("sync"); 1376 | 1377 | if (defined($time)) { 1378 | start_monitor; 1379 | # flush out current monitor 1380 | # May contain the reboot success line 1381 | wait_for_monitor 1; 1382 | } 1383 | 1384 | # try to reboot normally 1385 | if (run_command $reboot) { 1386 | if (defined($powercycle_after_reboot)) { 1387 | sleep $powercycle_after_reboot; 1388 | run_command "$power_cycle"; 1389 | } 1390 | } else { 1391 | # nope? power cycle it. 1392 | run_command "$power_cycle"; 1393 | } 1394 | } 1395 | 1396 | if (defined($time)) { 1397 | 1398 | # We only want to get to the new kernel, don't fail 1399 | # if we stumble over a call trace. 1400 | my $save_ignore_errors = $ignore_errors; 1401 | $ignore_errors = 1; 1402 | 1403 | # Look for the good kernel to boot 1404 | if (wait_for_monitor($time, "Linux version")) { 1405 | # reboot got stuck? 1406 | doprint "Reboot did not finish. Forcing power cycle\n"; 1407 | run_command "$power_cycle"; 1408 | } 1409 | 1410 | $ignore_errors = $save_ignore_errors; 1411 | 1412 | # Still need to wait for the reboot to finish 1413 | wait_for_monitor($time, $reboot_success_line); 1414 | 1415 | end_monitor; 1416 | } 1417 | } 1418 | 1419 | sub reboot_to_good { 1420 | my ($time) = @_; 1421 | 1422 | if (defined($switch_to_good)) { 1423 | run_command $switch_to_good; 1424 | } 1425 | 1426 | reboot $time; 1427 | } 1428 | 1429 | sub do_not_reboot { 1430 | my $i = $iteration; 1431 | 1432 | return $test_type eq "build" || $no_reboot || 1433 | ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") || 1434 | ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build") || 1435 | ($test_type eq "config_bisect" && $opt{"CONFIG_BISECT_TYPE[$i]"} eq "build"); 1436 | } 1437 | 1438 | my $in_die = 0; 1439 | 1440 | sub dodie { 1441 | 1442 | # avoid recusion 1443 | return if ($in_die); 1444 | $in_die = 1; 1445 | 1446 | doprint "CRITICAL FAILURE... ", @_, "\n"; 1447 | 1448 | my $i = $iteration; 1449 | 1450 | if ($reboot_on_error && !do_not_reboot) { 1451 | 1452 | doprint "REBOOTING\n"; 1453 | reboot_to_good; 1454 | 1455 | } elsif ($poweroff_on_error && defined($power_off)) { 1456 | doprint "POWERING OFF\n"; 1457 | `$power_off`; 1458 | } 1459 | 1460 | if (defined($opt{"LOG_FILE"})) { 1461 | print " See $opt{LOG_FILE} for more info.\n"; 1462 | } 1463 | 1464 | if ($email_on_error) { 1465 | send_email("KTEST: critical failure for your [$test_type] test", 1466 | "Your test started at $script_start_time has failed with:\n@_\n"); 1467 | } 1468 | 1469 | if ($monitor_cnt) { 1470 | # restore terminal settings 1471 | system("stty $stty_orig"); 1472 | } 1473 | 1474 | if (defined($post_test)) { 1475 | run_command $post_test; 1476 | } 1477 | 1478 | die @_, "\n"; 1479 | } 1480 | 1481 | sub create_pty { 1482 | my ($ptm, $pts) = @_; 1483 | my $tmp; 1484 | my $TIOCSPTLCK = 0x40045431; 1485 | my $TIOCGPTN = 0x80045430; 1486 | 1487 | sysopen($ptm, "/dev/ptmx", O_RDWR | O_NONBLOCK) or 1488 | dodie "Cant open /dev/ptmx"; 1489 | 1490 | # unlockpt() 1491 | $tmp = pack("i", 0); 1492 | ioctl($ptm, $TIOCSPTLCK, $tmp) or 1493 | dodie "ioctl TIOCSPTLCK for /dev/ptmx failed"; 1494 | 1495 | # ptsname() 1496 | ioctl($ptm, $TIOCGPTN, $tmp) or 1497 | dodie "ioctl TIOCGPTN for /dev/ptmx failed"; 1498 | $tmp = unpack("i", $tmp); 1499 | 1500 | sysopen($pts, "/dev/pts/$tmp", O_RDWR | O_NONBLOCK) or 1501 | dodie "Can't open /dev/pts/$tmp"; 1502 | } 1503 | 1504 | sub exec_console { 1505 | my ($ptm, $pts) = @_; 1506 | 1507 | close($ptm); 1508 | 1509 | close(\*STDIN); 1510 | close(\*STDOUT); 1511 | close(\*STDERR); 1512 | 1513 | open(\*STDIN, '<&', $pts); 1514 | open(\*STDOUT, '>&', $pts); 1515 | open(\*STDERR, '>&', $pts); 1516 | 1517 | close($pts); 1518 | 1519 | exec $console or 1520 | dodie "Can't open console $console"; 1521 | } 1522 | 1523 | sub open_console { 1524 | my ($ptm) = @_; 1525 | my $pts = \*PTSFD; 1526 | my $pid; 1527 | 1528 | # save terminal settings 1529 | $stty_orig = `stty -g`; 1530 | 1531 | # place terminal in cbreak mode so that stdin can be read one character at 1532 | # a time without having to wait for a newline 1533 | system("stty -icanon -echo -icrnl"); 1534 | 1535 | create_pty($ptm, $pts); 1536 | 1537 | $pid = fork; 1538 | 1539 | if (!$pid) { 1540 | # child 1541 | exec_console($ptm, $pts) 1542 | } 1543 | 1544 | # parent 1545 | close($pts); 1546 | 1547 | return $pid; 1548 | 1549 | open(PTSFD, "Stop perl from warning about single use of PTSFD"); 1550 | } 1551 | 1552 | sub close_console { 1553 | my ($fp, $pid) = @_; 1554 | 1555 | doprint "kill child process $pid\n"; 1556 | kill $close_console_signal, $pid; 1557 | 1558 | doprint "wait for child process $pid to exit\n"; 1559 | waitpid($pid, 0); 1560 | 1561 | print "closing!\n"; 1562 | close($fp); 1563 | 1564 | # restore terminal settings 1565 | system("stty $stty_orig"); 1566 | } 1567 | 1568 | sub start_monitor { 1569 | if ($monitor_cnt++) { 1570 | return; 1571 | } 1572 | $monitor_fp = \*MONFD; 1573 | $monitor_pid = open_console $monitor_fp; 1574 | 1575 | return; 1576 | 1577 | open(MONFD, "Stop perl from warning about single use of MONFD"); 1578 | } 1579 | 1580 | sub end_monitor { 1581 | return if (!defined $console); 1582 | if (--$monitor_cnt) { 1583 | return; 1584 | } 1585 | close_console($monitor_fp, $monitor_pid); 1586 | } 1587 | 1588 | sub wait_for_monitor { 1589 | my ($time, $stop) = @_; 1590 | my $full_line = ""; 1591 | my $line; 1592 | my $booted = 0; 1593 | my $start_time = time; 1594 | my $skip_call_trace = 0; 1595 | my $bug = 0; 1596 | my $bug_ignored = 0; 1597 | my $now; 1598 | 1599 | doprint "** Wait for monitor to settle down **\n"; 1600 | 1601 | # read the monitor and wait for the system to calm down 1602 | while (!$booted) { 1603 | $line = wait_for_input($monitor_fp, $time); 1604 | last if (!defined($line)); 1605 | print "$line"; 1606 | $full_line .= $line; 1607 | 1608 | if (defined($stop) && $full_line =~ /$stop/) { 1609 | doprint "wait for monitor detected $stop\n"; 1610 | $booted = 1; 1611 | } 1612 | 1613 | if ($full_line =~ /\[ backtrace testing \]/) { 1614 | $skip_call_trace = 1; 1615 | } 1616 | 1617 | if ($full_line =~ /call trace:/i) { 1618 | if (!$bug && !$skip_call_trace) { 1619 | if ($ignore_errors) { 1620 | $bug_ignored = 1; 1621 | } else { 1622 | $bug = 1; 1623 | } 1624 | } 1625 | } 1626 | 1627 | if ($full_line =~ /\[ end of backtrace testing \]/) { 1628 | $skip_call_trace = 0; 1629 | } 1630 | 1631 | if ($full_line =~ /Kernel panic -/) { 1632 | $bug = 1; 1633 | } 1634 | 1635 | if ($line =~ /\n/) { 1636 | $full_line = ""; 1637 | } 1638 | $now = time; 1639 | if ($now - $start_time >= $max_monitor_wait) { 1640 | doprint "Exiting monitor flush due to hitting MAX_MONITOR_WAIT\n"; 1641 | return 1; 1642 | } 1643 | } 1644 | print "** Monitor flushed **\n"; 1645 | 1646 | # if stop is defined but wasn't hit, return error 1647 | # used by reboot (which wants to see a reboot) 1648 | if (defined($stop) && !$booted) { 1649 | $bug = 1; 1650 | } 1651 | return $bug; 1652 | } 1653 | 1654 | sub save_logs { 1655 | my ($result, $basedir) = @_; 1656 | my @t = localtime; 1657 | my $date = sprintf "%04d%02d%02d%02d%02d%02d", 1658 | 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; 1659 | 1660 | my $type = $build_type; 1661 | if ($type =~ /useconfig/) { 1662 | $type = "useconfig"; 1663 | } 1664 | 1665 | my $dir = "$machine-$test_type-$type-$result-$date"; 1666 | 1667 | $dir = "$basedir/$dir"; 1668 | 1669 | if (!-d $dir) { 1670 | mkpath($dir) or 1671 | dodie "can't create $dir"; 1672 | } 1673 | 1674 | my %files = ( 1675 | "config" => $output_config, 1676 | "buildlog" => $buildlog, 1677 | "dmesg" => $dmesg, 1678 | "testlog" => $testlog, 1679 | ); 1680 | 1681 | while (my ($name, $source) = each(%files)) { 1682 | if (-f "$source") { 1683 | cp "$source", "$dir/$name" or 1684 | dodie "failed to copy $source"; 1685 | } 1686 | } 1687 | 1688 | doprint "*** Saved info to $dir ***\n"; 1689 | } 1690 | 1691 | sub fail { 1692 | 1693 | if ($die_on_failure) { 1694 | dodie @_; 1695 | } 1696 | 1697 | doprint "FAILED\n"; 1698 | 1699 | my $i = $iteration; 1700 | 1701 | # no need to reboot for just building. 1702 | if (!do_not_reboot) { 1703 | doprint "REBOOTING\n"; 1704 | reboot_to_good $sleep_time; 1705 | } 1706 | 1707 | my $name = ""; 1708 | 1709 | if (defined($test_name)) { 1710 | $name = " ($test_name)"; 1711 | } 1712 | 1713 | print_times; 1714 | 1715 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; 1716 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; 1717 | doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; 1718 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; 1719 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; 1720 | 1721 | if (defined($store_failures)) { 1722 | save_logs "fail", $store_failures; 1723 | } 1724 | 1725 | if (defined($post_test)) { 1726 | run_command $post_test; 1727 | } 1728 | 1729 | return 1; 1730 | } 1731 | 1732 | sub run_command { 1733 | my ($command, $redirect, $timeout) = @_; 1734 | my $start_time; 1735 | my $end_time; 1736 | my $dolog = 0; 1737 | my $dord = 0; 1738 | my $dostdout = 0; 1739 | my $pid; 1740 | 1741 | $command =~ s/\$SSH_USER/$ssh_user/g; 1742 | $command =~ s/\$MACHINE/$machine/g; 1743 | 1744 | doprint("$command ... "); 1745 | $start_time = time; 1746 | 1747 | $pid = open(CMD, "$command 2>&1 |") or 1748 | (fail "unable to exec $command" and return 0); 1749 | 1750 | if (defined($opt{"LOG_FILE"})) { 1751 | open(LOG, ">>$opt{LOG_FILE}") or 1752 | dodie "failed to write to log"; 1753 | $dolog = 1; 1754 | } 1755 | 1756 | if (defined($redirect)) { 1757 | if ($redirect eq 1) { 1758 | $dostdout = 1; 1759 | # Have the output of the command on its own line 1760 | doprint "\n"; 1761 | } else { 1762 | open (RD, ">$redirect") or 1763 | dodie "failed to write to redirect $redirect"; 1764 | $dord = 1; 1765 | } 1766 | } 1767 | 1768 | my $hit_timeout = 0; 1769 | 1770 | while (1) { 1771 | my $fp = \*CMD; 1772 | if (defined($timeout)) { 1773 | doprint "timeout = $timeout\n"; 1774 | } 1775 | my $line = wait_for_input($fp, $timeout); 1776 | if (!defined($line)) { 1777 | my $now = time; 1778 | if (defined($timeout) && (($now - $start_time) >= $timeout)) { 1779 | doprint "Hit timeout of $timeout, killing process\n"; 1780 | $hit_timeout = 1; 1781 | kill 9, $pid; 1782 | } 1783 | last; 1784 | } 1785 | print LOG $line if ($dolog); 1786 | print RD $line if ($dord); 1787 | print $line if ($dostdout); 1788 | } 1789 | 1790 | waitpid($pid, 0); 1791 | # shift 8 for real exit status 1792 | $run_command_status = $? >> 8; 1793 | 1794 | close(CMD); 1795 | close(LOG) if ($dolog); 1796 | close(RD) if ($dord); 1797 | 1798 | $end_time = time; 1799 | my $delta = $end_time - $start_time; 1800 | 1801 | if ($delta == 1) { 1802 | doprint "[1 second] "; 1803 | } else { 1804 | doprint "[$delta seconds] "; 1805 | } 1806 | 1807 | if ($hit_timeout) { 1808 | $run_command_status = 1; 1809 | } 1810 | 1811 | if ($run_command_status) { 1812 | doprint "FAILED!\n"; 1813 | } else { 1814 | doprint "SUCCESS\n"; 1815 | } 1816 | 1817 | return !$run_command_status; 1818 | } 1819 | 1820 | sub run_ssh { 1821 | my ($cmd, $timeout) = @_; 1822 | my $cp_exec = $ssh_exec; 1823 | 1824 | $cp_exec =~ s/\$SSH_COMMAND/$cmd/g; 1825 | return run_command "$cp_exec", undef , $timeout; 1826 | } 1827 | 1828 | sub run_scp { 1829 | my ($src, $dst, $cp_scp) = @_; 1830 | 1831 | $cp_scp =~ s/\$SRC_FILE/$src/g; 1832 | $cp_scp =~ s/\$DST_FILE/$dst/g; 1833 | 1834 | return run_command "$cp_scp"; 1835 | } 1836 | 1837 | sub run_scp_install { 1838 | my ($src, $dst) = @_; 1839 | 1840 | my $cp_scp = $scp_to_target_install; 1841 | 1842 | return run_scp($src, $dst, $cp_scp); 1843 | } 1844 | 1845 | sub run_scp_mod { 1846 | my ($src, $dst) = @_; 1847 | 1848 | my $cp_scp = $scp_to_target; 1849 | 1850 | return run_scp($src, $dst, $cp_scp); 1851 | } 1852 | 1853 | sub get_grub2_index { 1854 | 1855 | return if (defined($grub_number) && defined($last_grub_menu) && 1856 | $last_grub_menu eq $grub_menu && defined($last_machine) && 1857 | $last_machine eq $machine); 1858 | 1859 | doprint "Find grub2 menu ... "; 1860 | $grub_number = -1; 1861 | 1862 | my $ssh_grub = $ssh_exec; 1863 | $ssh_grub =~ s,\$SSH_COMMAND,cat $grub_file,g; 1864 | 1865 | open(IN, "$ssh_grub |") 1866 | or dodie "unable to get $grub_file"; 1867 | 1868 | my $found = 0; 1869 | 1870 | while () { 1871 | if (/^menuentry.*$grub_menu/) { 1872 | $grub_number++; 1873 | $found = 1; 1874 | last; 1875 | } elsif (/^menuentry\s|^submenu\s/) { 1876 | $grub_number++; 1877 | } 1878 | } 1879 | close(IN); 1880 | 1881 | dodie "Could not find '$grub_menu' in $grub_file on $machine" 1882 | if (!$found); 1883 | doprint "$grub_number\n"; 1884 | $last_grub_menu = $grub_menu; 1885 | $last_machine = $machine; 1886 | } 1887 | 1888 | sub get_grub_index { 1889 | 1890 | if ($reboot_type eq "grub2") { 1891 | get_grub2_index; 1892 | return; 1893 | } 1894 | 1895 | if ($reboot_type ne "grub") { 1896 | return; 1897 | } 1898 | return if (defined($grub_number) && defined($last_grub_menu) && 1899 | $last_grub_menu eq $grub_menu && defined($last_machine) && 1900 | $last_machine eq $machine); 1901 | 1902 | doprint "Find grub menu ... "; 1903 | $grub_number = -1; 1904 | 1905 | my $ssh_grub = $ssh_exec; 1906 | $ssh_grub =~ s,\$SSH_COMMAND,cat /boot/grub/menu.lst,g; 1907 | 1908 | open(IN, "$ssh_grub |") 1909 | or dodie "unable to get menu.lst"; 1910 | 1911 | my $found = 0; 1912 | 1913 | while () { 1914 | if (/^\s*title\s+$grub_menu\s*$/) { 1915 | $grub_number++; 1916 | $found = 1; 1917 | last; 1918 | } elsif (/^\s*title\s/) { 1919 | $grub_number++; 1920 | } 1921 | } 1922 | close(IN); 1923 | 1924 | dodie "Could not find '$grub_menu' in /boot/grub/menu on $machine" 1925 | if (!$found); 1926 | doprint "$grub_number\n"; 1927 | $last_grub_menu = $grub_menu; 1928 | $last_machine = $machine; 1929 | } 1930 | 1931 | sub wait_for_input 1932 | { 1933 | my ($fp, $time) = @_; 1934 | my $start_time; 1935 | my $rin; 1936 | my $rout; 1937 | my $nr; 1938 | my $buf; 1939 | my $line; 1940 | my $ch; 1941 | 1942 | if (!defined($time)) { 1943 | $time = $timeout; 1944 | } 1945 | 1946 | $rin = ''; 1947 | vec($rin, fileno($fp), 1) = 1; 1948 | vec($rin, fileno(\*STDIN), 1) = 1; 1949 | 1950 | $start_time = time; 1951 | 1952 | while (1) { 1953 | $nr = select($rout=$rin, undef, undef, $time); 1954 | 1955 | last if ($nr <= 0); 1956 | 1957 | # copy data from stdin to the console 1958 | if (vec($rout, fileno(\*STDIN), 1) == 1) { 1959 | $nr = sysread(\*STDIN, $buf, 1000); 1960 | syswrite($fp, $buf, $nr) if ($nr > 0); 1961 | } 1962 | 1963 | # The timeout is based on time waiting for the fp data 1964 | if (vec($rout, fileno($fp), 1) != 1) { 1965 | last if (defined($time) && (time - $start_time > $time)); 1966 | next; 1967 | } 1968 | 1969 | $line = ""; 1970 | 1971 | # try to read one char at a time 1972 | while (sysread $fp, $ch, 1) { 1973 | $line .= $ch; 1974 | last if ($ch eq "\n"); 1975 | } 1976 | 1977 | last if (!length($line)); 1978 | 1979 | return $line; 1980 | } 1981 | return undef; 1982 | } 1983 | 1984 | sub reboot_to { 1985 | if (defined($switch_to_test)) { 1986 | run_command $switch_to_test; 1987 | } 1988 | 1989 | if ($reboot_type eq "grub") { 1990 | run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; 1991 | } elsif ($reboot_type eq "grub2") { 1992 | run_ssh "$grub_reboot $grub_number"; 1993 | } elsif ($reboot_type eq "syslinux") { 1994 | run_ssh "$syslinux --once \\\"$syslinux_label\\\" $syslinux_path"; 1995 | } elsif (defined $reboot_script) { 1996 | run_command "$reboot_script"; 1997 | } 1998 | reboot; 1999 | } 2000 | 2001 | sub get_sha1 { 2002 | my ($commit) = @_; 2003 | 2004 | doprint "git rev-list --max-count=1 $commit ... "; 2005 | my $sha1 = `git rev-list --max-count=1 $commit`; 2006 | my $ret = $?; 2007 | 2008 | logit $sha1; 2009 | 2010 | if ($ret) { 2011 | doprint "FAILED\n"; 2012 | dodie "Failed to get git $commit"; 2013 | } 2014 | 2015 | print "SUCCESS\n"; 2016 | 2017 | chomp $sha1; 2018 | 2019 | return $sha1; 2020 | } 2021 | 2022 | sub monitor { 2023 | my $booted = 0; 2024 | my $bug = 0; 2025 | my $bug_ignored = 0; 2026 | my $skip_call_trace = 0; 2027 | my $loops; 2028 | 2029 | my $start_time = time; 2030 | 2031 | wait_for_monitor 5; 2032 | 2033 | my $line; 2034 | my $full_line = ""; 2035 | 2036 | open(DMESG, "> $dmesg") or 2037 | dodie "unable to write to $dmesg"; 2038 | 2039 | reboot_to; 2040 | 2041 | my $success_start; 2042 | my $failure_start; 2043 | my $monitor_start = time; 2044 | my $done = 0; 2045 | my $version_found = 0; 2046 | 2047 | while (!$done) { 2048 | 2049 | if ($bug && defined($stop_after_failure) && 2050 | $stop_after_failure >= 0) { 2051 | my $time = $stop_after_failure - (time - $failure_start); 2052 | $line = wait_for_input($monitor_fp, $time); 2053 | if (!defined($line)) { 2054 | doprint "bug timed out after $booted_timeout seconds\n"; 2055 | doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; 2056 | last; 2057 | } 2058 | } elsif ($booted) { 2059 | $line = wait_for_input($monitor_fp, $booted_timeout); 2060 | if (!defined($line)) { 2061 | my $s = $booted_timeout == 1 ? "" : "s"; 2062 | doprint "Successful boot found: break after $booted_timeout second$s\n"; 2063 | last; 2064 | } 2065 | } else { 2066 | $line = wait_for_input($monitor_fp); 2067 | if (!defined($line)) { 2068 | my $s = $timeout == 1 ? "" : "s"; 2069 | doprint "Timed out after $timeout second$s\n"; 2070 | last; 2071 | } 2072 | } 2073 | 2074 | doprint $line; 2075 | print DMESG $line; 2076 | 2077 | # we are not guaranteed to get a full line 2078 | $full_line .= $line; 2079 | 2080 | if ($full_line =~ /$success_line/) { 2081 | $booted = 1; 2082 | $success_start = time; 2083 | } 2084 | 2085 | if ($booted && defined($stop_after_success) && 2086 | $stop_after_success >= 0) { 2087 | my $now = time; 2088 | if ($now - $success_start >= $stop_after_success) { 2089 | doprint "Test forced to stop after $stop_after_success seconds after success\n"; 2090 | last; 2091 | } 2092 | } 2093 | 2094 | if ($full_line =~ /\[ backtrace testing \]/) { 2095 | $skip_call_trace = 1; 2096 | } 2097 | 2098 | if ($full_line =~ /call trace:/i) { 2099 | if (!$bug && !$skip_call_trace) { 2100 | if ($ignore_errors) { 2101 | $bug_ignored = 1; 2102 | } else { 2103 | $bug = 1; 2104 | $failure_start = time; 2105 | } 2106 | } 2107 | } 2108 | 2109 | if ($bug && defined($stop_after_failure) && 2110 | $stop_after_failure >= 0) { 2111 | my $now = time; 2112 | if ($now - $failure_start >= $stop_after_failure) { 2113 | doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; 2114 | last; 2115 | } 2116 | } 2117 | 2118 | if ($full_line =~ /\[ end of backtrace testing \]/) { 2119 | $skip_call_trace = 0; 2120 | } 2121 | 2122 | if ($full_line =~ /Kernel panic -/) { 2123 | $failure_start = time; 2124 | $bug = 1; 2125 | } 2126 | 2127 | # Detect triple faults by testing the banner 2128 | if ($full_line =~ /\bLinux version (\S+).*\n/) { 2129 | if ($1 eq $version) { 2130 | $version_found = 1; 2131 | } elsif ($version_found && $detect_triplefault) { 2132 | # We already booted into the kernel we are testing, 2133 | # but now we booted into another kernel? 2134 | # Consider this a triple fault. 2135 | doprint "Already booted in Linux kernel $version, but now\n"; 2136 | doprint "we booted into Linux kernel $1.\n"; 2137 | doprint "Assuming that this is a triple fault.\n"; 2138 | doprint "To disable this: set DETECT_TRIPLE_FAULT to 0\n"; 2139 | last; 2140 | } 2141 | } 2142 | 2143 | if ($line =~ /\n/) { 2144 | $full_line = ""; 2145 | } 2146 | 2147 | if ($stop_test_after > 0 && !$booted && !$bug) { 2148 | if (time - $monitor_start > $stop_test_after) { 2149 | doprint "STOP_TEST_AFTER ($stop_test_after seconds) timed out\n"; 2150 | $done = 1; 2151 | } 2152 | } 2153 | } 2154 | 2155 | my $end_time = time; 2156 | $reboot_time = $end_time - $start_time; 2157 | 2158 | close(DMESG); 2159 | 2160 | if ($bug) { 2161 | return 0 if ($in_bisect); 2162 | fail "failed - got a bug report" and return 0; 2163 | } 2164 | 2165 | if (!$booted) { 2166 | return 0 if ($in_bisect); 2167 | fail "failed - never got a boot prompt." and return 0; 2168 | } 2169 | 2170 | if ($bug_ignored) { 2171 | doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n"; 2172 | } 2173 | 2174 | return 1; 2175 | } 2176 | 2177 | sub eval_kernel_version { 2178 | my ($option) = @_; 2179 | 2180 | $option =~ s/\$KERNEL_VERSION/$version/g; 2181 | 2182 | return $option; 2183 | } 2184 | 2185 | sub do_post_install { 2186 | 2187 | return if (!defined($post_install)); 2188 | 2189 | my $cp_post_install = eval_kernel_version $post_install; 2190 | run_command "$cp_post_install" or 2191 | dodie "Failed to run post install"; 2192 | } 2193 | 2194 | # Sometimes the reboot fails, and will hang. We try to ssh to the box 2195 | # and if we fail, we force another reboot, that should powercycle it. 2196 | sub test_booted { 2197 | if (!run_ssh "echo testing connection") { 2198 | reboot $sleep_time; 2199 | } 2200 | } 2201 | 2202 | sub install { 2203 | 2204 | return if ($no_install); 2205 | 2206 | my $start_time = time; 2207 | 2208 | if (defined($pre_install)) { 2209 | my $cp_pre_install = eval_kernel_version $pre_install; 2210 | run_command "$cp_pre_install" or 2211 | dodie "Failed to run pre install"; 2212 | } 2213 | 2214 | my $cp_target = eval_kernel_version $target_image; 2215 | 2216 | test_booted; 2217 | 2218 | run_scp_install "$outputdir/$build_target", "$cp_target" or 2219 | dodie "failed to copy image"; 2220 | 2221 | my $install_mods = 0; 2222 | 2223 | # should we process modules? 2224 | $install_mods = 0; 2225 | open(IN, "$output_config") or dodie("Can't read config file"); 2226 | while () { 2227 | if (/CONFIG_MODULES(=y)?/) { 2228 | if (defined($1)) { 2229 | $install_mods = 1; 2230 | last; 2231 | } 2232 | } 2233 | } 2234 | close(IN); 2235 | 2236 | if (!$install_mods) { 2237 | do_post_install; 2238 | doprint "No modules needed\n"; 2239 | my $end_time = time; 2240 | $install_time = $end_time - $start_time; 2241 | return; 2242 | } 2243 | 2244 | run_command "$make INSTALL_MOD_STRIP=1 INSTALL_MOD_PATH=$tmpdir modules_install" or 2245 | dodie "Failed to install modules"; 2246 | 2247 | my $modlib = "/lib/modules/$version"; 2248 | my $modtar = "ktest-mods.tar.bz2"; 2249 | 2250 | run_ssh "rm -rf $modlib" or 2251 | dodie "failed to remove old mods: $modlib"; 2252 | 2253 | # would be nice if scp -r did not follow symbolic links 2254 | run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or 2255 | dodie "making tarball"; 2256 | 2257 | run_scp_mod "$tmpdir/$modtar", "/tmp" or 2258 | dodie "failed to copy modules"; 2259 | 2260 | unlink "$tmpdir/$modtar"; 2261 | 2262 | run_ssh "'(cd / && tar xjf /tmp/$modtar)'" or 2263 | dodie "failed to tar modules"; 2264 | 2265 | run_ssh "rm -f /tmp/$modtar"; 2266 | 2267 | do_post_install; 2268 | 2269 | my $end_time = time; 2270 | $install_time = $end_time - $start_time; 2271 | } 2272 | 2273 | sub get_version { 2274 | # get the release name 2275 | return if ($have_version); 2276 | doprint "$make kernelrelease ... "; 2277 | $version = `$make -s kernelrelease | tail -1`; 2278 | chomp($version); 2279 | doprint "$version\n"; 2280 | $have_version = 1; 2281 | } 2282 | 2283 | sub start_monitor_and_install { 2284 | # Make sure the stable kernel has finished booting 2285 | 2286 | # Install bisects, don't need console 2287 | if (defined $console) { 2288 | start_monitor; 2289 | wait_for_monitor 5; 2290 | end_monitor; 2291 | } 2292 | 2293 | get_grub_index; 2294 | get_version; 2295 | install; 2296 | 2297 | start_monitor if (defined $console); 2298 | return monitor; 2299 | } 2300 | 2301 | my $check_build_re = ".*:.*(warning|error|Error):.*"; 2302 | my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; 2303 | 2304 | sub process_warning_line { 2305 | my ($line) = @_; 2306 | 2307 | chomp $line; 2308 | 2309 | # for distcc heterogeneous systems, some compilers 2310 | # do things differently causing warning lines 2311 | # to be slightly different. This makes an attempt 2312 | # to fixe those issues. 2313 | 2314 | # chop off the index into the line 2315 | # using distcc, some compilers give different indexes 2316 | # depending on white space 2317 | $line =~ s/^(\s*\S+:\d+:)\d+/$1/; 2318 | 2319 | # Some compilers use UTF-8 extended for quotes and some don't. 2320 | $line =~ s/$utf8_quote/'/g; 2321 | 2322 | return $line; 2323 | } 2324 | 2325 | # Read buildlog and check against warnings file for any 2326 | # new warnings. 2327 | # 2328 | # Returns 1 if OK 2329 | # 0 otherwise 2330 | sub check_buildlog { 2331 | return 1 if (!defined $warnings_file); 2332 | 2333 | my %warnings_list; 2334 | 2335 | # Failed builds should not reboot the target 2336 | my $save_no_reboot = $no_reboot; 2337 | $no_reboot = 1; 2338 | 2339 | if (-f $warnings_file) { 2340 | open(IN, $warnings_file) or 2341 | dodie "Error opening $warnings_file"; 2342 | 2343 | while () { 2344 | if (/$check_build_re/) { 2345 | my $warning = process_warning_line $_; 2346 | 2347 | $warnings_list{$warning} = 1; 2348 | } 2349 | } 2350 | close(IN); 2351 | } 2352 | 2353 | # If warnings file didn't exist, and WARNINGS_FILE exist, 2354 | # then we fail on any warning! 2355 | 2356 | open(IN, $buildlog) or dodie "Can't open $buildlog"; 2357 | while () { 2358 | if (/$check_build_re/) { 2359 | my $warning = process_warning_line $_; 2360 | 2361 | if (!defined $warnings_list{$warning}) { 2362 | fail "New warning found (not in $warnings_file)\n$_\n"; 2363 | $no_reboot = $save_no_reboot; 2364 | return 0; 2365 | } 2366 | } 2367 | } 2368 | $no_reboot = $save_no_reboot; 2369 | close(IN); 2370 | } 2371 | 2372 | sub check_patch_buildlog { 2373 | my ($patch) = @_; 2374 | 2375 | my @files = `git show $patch | diffstat -l`; 2376 | 2377 | foreach my $file (@files) { 2378 | chomp $file; 2379 | } 2380 | 2381 | open(IN, "git show $patch |") or 2382 | dodie "failed to show $patch"; 2383 | while () { 2384 | if (m,^--- a/(.*),) { 2385 | chomp $1; 2386 | $files[$#files] = $1; 2387 | } 2388 | } 2389 | close(IN); 2390 | 2391 | open(IN, $buildlog) or dodie "Can't open $buildlog"; 2392 | while () { 2393 | if (/^\s*(.*?):.*(warning|error)/) { 2394 | my $err = $1; 2395 | foreach my $file (@files) { 2396 | my $fullpath = "$builddir/$file"; 2397 | if ($file eq $err || $fullpath eq $err) { 2398 | fail "$file built with warnings" and return 0; 2399 | } 2400 | } 2401 | } 2402 | } 2403 | close(IN); 2404 | 2405 | return 1; 2406 | } 2407 | 2408 | sub apply_min_config { 2409 | my $outconfig = "$output_config.new"; 2410 | 2411 | # Read the config file and remove anything that 2412 | # is in the force_config hash (from minconfig and others) 2413 | # then add the force config back. 2414 | 2415 | doprint "Applying minimum configurations into $output_config.new\n"; 2416 | 2417 | open (OUT, ">$outconfig") or 2418 | dodie "Can't create $outconfig"; 2419 | 2420 | if (-f $output_config) { 2421 | open (IN, $output_config) or 2422 | dodie "Failed to open $output_config"; 2423 | while () { 2424 | if (/^(# )?(CONFIG_[^\s=]*)/) { 2425 | next if (defined($force_config{$2})); 2426 | } 2427 | print OUT; 2428 | } 2429 | close IN; 2430 | } 2431 | foreach my $config (keys %force_config) { 2432 | print OUT "$force_config{$config}\n"; 2433 | } 2434 | close OUT; 2435 | 2436 | run_command "mv $outconfig $output_config"; 2437 | } 2438 | 2439 | sub make_oldconfig { 2440 | 2441 | my @force_list = keys %force_config; 2442 | 2443 | if ($#force_list >= 0) { 2444 | apply_min_config; 2445 | } 2446 | 2447 | if (!run_command "$make olddefconfig") { 2448 | # Perhaps olddefconfig doesn't exist in this version of the kernel 2449 | # try oldnoconfig 2450 | doprint "olddefconfig failed, trying make oldnoconfig\n"; 2451 | if (!run_command "$make oldnoconfig") { 2452 | doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; 2453 | # try a yes '' | oldconfig 2454 | run_command "yes '' | $make oldconfig" or 2455 | dodie "failed make config oldconfig"; 2456 | } 2457 | } 2458 | } 2459 | 2460 | # read a config file and use this to force new configs. 2461 | sub load_force_config { 2462 | my ($config) = @_; 2463 | 2464 | doprint "Loading force configs from $config\n"; 2465 | open(IN, $config) or 2466 | dodie "failed to read $config"; 2467 | while () { 2468 | chomp; 2469 | if (/^(CONFIG[^\s=]*)(\s*=.*)/) { 2470 | $force_config{$1} = $_; 2471 | } elsif (/^# (CONFIG_\S*) is not set/) { 2472 | $force_config{$1} = $_; 2473 | } 2474 | } 2475 | close IN; 2476 | } 2477 | 2478 | sub build { 2479 | my ($type) = @_; 2480 | 2481 | unlink $buildlog; 2482 | 2483 | my $start_time = time; 2484 | 2485 | # Failed builds should not reboot the target 2486 | my $save_no_reboot = $no_reboot; 2487 | $no_reboot = 1; 2488 | 2489 | # Calculate a new version from here. 2490 | $have_version = 0; 2491 | 2492 | if (defined($pre_build)) { 2493 | my $ret = run_command $pre_build; 2494 | if (!$ret && defined($pre_build_die) && 2495 | $pre_build_die) { 2496 | dodie "failed to pre_build\n"; 2497 | } 2498 | } 2499 | 2500 | if ($type =~ /^useconfig:(.*)/) { 2501 | run_command "cp $1 $output_config" or 2502 | dodie "could not copy $1 to .config"; 2503 | 2504 | $type = "oldconfig"; 2505 | } 2506 | 2507 | # old config can ask questions 2508 | if ($type eq "oldconfig") { 2509 | $type = "olddefconfig"; 2510 | 2511 | # allow for empty configs 2512 | run_command "touch $output_config"; 2513 | 2514 | if (!$noclean) { 2515 | run_command "mv $output_config $outputdir/config_temp" or 2516 | dodie "moving .config"; 2517 | 2518 | run_command "$make mrproper" or dodie "make mrproper"; 2519 | 2520 | run_command "mv $outputdir/config_temp $output_config" or 2521 | dodie "moving config_temp"; 2522 | } 2523 | 2524 | } elsif (!$noclean) { 2525 | unlink "$output_config"; 2526 | run_command "$make mrproper" or 2527 | dodie "make mrproper"; 2528 | } 2529 | 2530 | # add something to distinguish this build 2531 | open(OUT, "> $outputdir/localversion") or dodie("Can't make localversion file"); 2532 | print OUT "$localversion\n"; 2533 | close(OUT); 2534 | 2535 | if (defined($minconfig)) { 2536 | load_force_config($minconfig); 2537 | } 2538 | 2539 | if ($type ne "olddefconfig") { 2540 | run_command "$make $type" or 2541 | dodie "failed make config"; 2542 | } 2543 | # Run old config regardless, to enforce min configurations 2544 | make_oldconfig; 2545 | 2546 | my $build_ret = run_command "$make $build_options", $buildlog; 2547 | 2548 | if (defined($post_build)) { 2549 | # Because a post build may change the kernel version 2550 | # do it now. 2551 | get_version; 2552 | my $ret = run_command $post_build; 2553 | if (!$ret && defined($post_build_die) && 2554 | $post_build_die) { 2555 | dodie "failed to post_build\n"; 2556 | } 2557 | } 2558 | 2559 | if (!$build_ret) { 2560 | # bisect may need this to pass 2561 | if ($in_bisect) { 2562 | $no_reboot = $save_no_reboot; 2563 | return 0; 2564 | } 2565 | fail "failed build" and return 0; 2566 | } 2567 | 2568 | $no_reboot = $save_no_reboot; 2569 | 2570 | my $end_time = time; 2571 | $build_time = $end_time - $start_time; 2572 | 2573 | return 1; 2574 | } 2575 | 2576 | sub halt { 2577 | if (!run_ssh "halt" or defined($power_off)) { 2578 | if (defined($poweroff_after_halt)) { 2579 | sleep $poweroff_after_halt; 2580 | run_command "$power_off"; 2581 | } 2582 | } else { 2583 | # nope? the zap it! 2584 | run_command "$power_off"; 2585 | } 2586 | } 2587 | 2588 | sub success { 2589 | my ($i) = @_; 2590 | 2591 | $successes++; 2592 | 2593 | my $name = ""; 2594 | 2595 | if (defined($test_name)) { 2596 | $name = " ($test_name)"; 2597 | } 2598 | 2599 | print_times; 2600 | 2601 | doprint "\n\n*******************************************\n"; 2602 | doprint "*******************************************\n"; 2603 | doprint "KTEST RESULT: TEST $i$name SUCCESS!!!! **\n"; 2604 | doprint "*******************************************\n"; 2605 | doprint "*******************************************\n"; 2606 | 2607 | if (defined($store_successes)) { 2608 | save_logs "success", $store_successes; 2609 | } 2610 | 2611 | if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { 2612 | doprint "Reboot and wait $sleep_time seconds\n"; 2613 | reboot_to_good $sleep_time; 2614 | } 2615 | 2616 | if (defined($post_test)) { 2617 | run_command $post_test; 2618 | } 2619 | } 2620 | 2621 | sub answer_bisect { 2622 | for (;;) { 2623 | doprint "Pass, fail, or skip? [p/f/s]"; 2624 | my $ans = ; 2625 | chomp $ans; 2626 | if ($ans eq "p" || $ans eq "P") { 2627 | return 1; 2628 | } elsif ($ans eq "f" || $ans eq "F") { 2629 | return 0; 2630 | } elsif ($ans eq "s" || $ans eq "S") { 2631 | return -1; 2632 | } else { 2633 | print "Please answer 'p', 'f', or 's'\n"; 2634 | } 2635 | } 2636 | } 2637 | 2638 | sub child_run_test { 2639 | 2640 | # child should have no power 2641 | $reboot_on_error = 0; 2642 | $poweroff_on_error = 0; 2643 | $die_on_failure = 1; 2644 | 2645 | run_command $run_test, $testlog; 2646 | 2647 | exit $run_command_status; 2648 | } 2649 | 2650 | my $child_done; 2651 | 2652 | sub child_finished { 2653 | $child_done = 1; 2654 | } 2655 | 2656 | sub do_run_test { 2657 | my $child_pid; 2658 | my $child_exit; 2659 | my $line; 2660 | my $full_line; 2661 | my $bug = 0; 2662 | my $bug_ignored = 0; 2663 | 2664 | my $start_time = time; 2665 | 2666 | wait_for_monitor 1; 2667 | 2668 | doprint "run test $run_test\n"; 2669 | 2670 | $child_done = 0; 2671 | 2672 | $SIG{CHLD} = qw(child_finished); 2673 | 2674 | $child_pid = fork; 2675 | 2676 | child_run_test if (!$child_pid); 2677 | 2678 | $full_line = ""; 2679 | 2680 | do { 2681 | $line = wait_for_input($monitor_fp, 1); 2682 | if (defined($line)) { 2683 | 2684 | # we are not guaranteed to get a full line 2685 | $full_line .= $line; 2686 | doprint $line; 2687 | 2688 | if ($full_line =~ /call trace:/i) { 2689 | if ($ignore_errors) { 2690 | $bug_ignored = 1; 2691 | } else { 2692 | $bug = 1; 2693 | } 2694 | } 2695 | 2696 | if ($full_line =~ /Kernel panic -/) { 2697 | $bug = 1; 2698 | } 2699 | 2700 | if ($line =~ /\n/) { 2701 | $full_line = ""; 2702 | } 2703 | } 2704 | } while (!$child_done && !$bug); 2705 | 2706 | if (!$bug && $bug_ignored) { 2707 | doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n"; 2708 | } 2709 | 2710 | if ($bug) { 2711 | my $failure_start = time; 2712 | my $now; 2713 | do { 2714 | $line = wait_for_input($monitor_fp, 1); 2715 | if (defined($line)) { 2716 | doprint $line; 2717 | } 2718 | $now = time; 2719 | if ($now - $failure_start >= $stop_after_failure) { 2720 | last; 2721 | } 2722 | } while (defined($line)); 2723 | 2724 | doprint "Detected kernel crash!\n"; 2725 | # kill the child with extreme prejudice 2726 | kill 9, $child_pid; 2727 | } 2728 | 2729 | waitpid $child_pid, 0; 2730 | $child_exit = $? >> 8; 2731 | 2732 | my $end_time = time; 2733 | $test_time = $end_time - $start_time; 2734 | 2735 | if (!$bug && $in_bisect) { 2736 | if (defined($bisect_ret_good)) { 2737 | if ($child_exit == $bisect_ret_good) { 2738 | return 1; 2739 | } 2740 | } 2741 | if (defined($bisect_ret_skip)) { 2742 | if ($child_exit == $bisect_ret_skip) { 2743 | return -1; 2744 | } 2745 | } 2746 | if (defined($bisect_ret_abort)) { 2747 | if ($child_exit == $bisect_ret_abort) { 2748 | fail "test abort" and return -2; 2749 | } 2750 | } 2751 | if (defined($bisect_ret_bad)) { 2752 | if ($child_exit == $bisect_ret_skip) { 2753 | return 0; 2754 | } 2755 | } 2756 | if (defined($bisect_ret_default)) { 2757 | if ($bisect_ret_default eq "good") { 2758 | return 1; 2759 | } elsif ($bisect_ret_default eq "bad") { 2760 | return 0; 2761 | } elsif ($bisect_ret_default eq "skip") { 2762 | return -1; 2763 | } elsif ($bisect_ret_default eq "abort") { 2764 | return -2; 2765 | } else { 2766 | fail "unknown default action: $bisect_ret_default" 2767 | and return -2; 2768 | } 2769 | } 2770 | } 2771 | 2772 | if ($bug || $child_exit) { 2773 | return 0 if $in_bisect; 2774 | fail "test failed" and return 0; 2775 | } 2776 | return 1; 2777 | } 2778 | 2779 | sub run_git_bisect { 2780 | my ($command) = @_; 2781 | 2782 | doprint "$command ... "; 2783 | 2784 | my $output = `$command 2>&1`; 2785 | my $ret = $?; 2786 | 2787 | logit $output; 2788 | 2789 | if ($ret) { 2790 | doprint "FAILED\n"; 2791 | dodie "Failed to git bisect"; 2792 | } 2793 | 2794 | doprint "SUCCESS\n"; 2795 | if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { 2796 | doprint "$1 [$2]\n"; 2797 | } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { 2798 | $bisect_bad_commit = $1; 2799 | doprint "Found bad commit... $1\n"; 2800 | return 0; 2801 | } else { 2802 | # we already logged it, just print it now. 2803 | print $output; 2804 | } 2805 | 2806 | return 1; 2807 | } 2808 | 2809 | sub bisect_reboot { 2810 | doprint "Reboot and sleep $bisect_sleep_time seconds\n"; 2811 | reboot_to_good $bisect_sleep_time; 2812 | } 2813 | 2814 | # returns 1 on success, 0 on failure, -1 on skip 2815 | sub run_bisect_test { 2816 | my ($type, $buildtype) = @_; 2817 | 2818 | my $failed = 0; 2819 | my $result; 2820 | my $output; 2821 | my $ret; 2822 | 2823 | $in_bisect = 1; 2824 | 2825 | build $buildtype or $failed = 1; 2826 | 2827 | if ($type ne "build") { 2828 | if ($failed && $bisect_skip) { 2829 | $in_bisect = 0; 2830 | return -1; 2831 | } 2832 | dodie "Failed on build" if $failed; 2833 | 2834 | # Now boot the box 2835 | start_monitor_and_install or $failed = 1; 2836 | 2837 | if ($type ne "boot") { 2838 | if ($failed && $bisect_skip) { 2839 | end_monitor; 2840 | bisect_reboot; 2841 | $in_bisect = 0; 2842 | return -1; 2843 | } 2844 | dodie "Failed on boot" if $failed; 2845 | 2846 | do_run_test or $failed = 1; 2847 | } 2848 | end_monitor; 2849 | } 2850 | 2851 | if ($failed) { 2852 | $result = 0; 2853 | } else { 2854 | $result = 1; 2855 | } 2856 | 2857 | # reboot the box to a kernel we can ssh to 2858 | if ($type ne "build") { 2859 | bisect_reboot; 2860 | } 2861 | $in_bisect = 0; 2862 | 2863 | return $result; 2864 | } 2865 | 2866 | sub run_bisect { 2867 | my ($type) = @_; 2868 | my $buildtype = "oldconfig"; 2869 | 2870 | # We should have a minconfig to use? 2871 | if (defined($minconfig)) { 2872 | $buildtype = "useconfig:$minconfig"; 2873 | } 2874 | 2875 | # If the user sets bisect_tries to less than 1, then no tries 2876 | # is a success. 2877 | my $ret = 1; 2878 | 2879 | # Still let the user manually decide that though. 2880 | if ($bisect_tries < 1 && $bisect_manual) { 2881 | $ret = answer_bisect; 2882 | } 2883 | 2884 | for (my $i = 0; $i < $bisect_tries; $i++) { 2885 | if ($bisect_tries > 1) { 2886 | my $t = $i + 1; 2887 | doprint("Running bisect trial $t of $bisect_tries:\n"); 2888 | } 2889 | $ret = run_bisect_test $type, $buildtype; 2890 | 2891 | if ($bisect_manual) { 2892 | $ret = answer_bisect; 2893 | } 2894 | 2895 | last if (!$ret); 2896 | } 2897 | 2898 | # Are we looking for where it worked, not failed? 2899 | if ($reverse_bisect && $ret >= 0) { 2900 | $ret = !$ret; 2901 | } 2902 | 2903 | if ($ret > 0) { 2904 | return "good"; 2905 | } elsif ($ret == 0) { 2906 | return "bad"; 2907 | } elsif ($bisect_skip) { 2908 | doprint "HIT A BAD COMMIT ... SKIPPING\n"; 2909 | return "skip"; 2910 | } 2911 | } 2912 | 2913 | sub update_bisect_replay { 2914 | my $tmp_log = "$tmpdir/ktest_bisect_log"; 2915 | run_command "git bisect log > $tmp_log" or 2916 | dodie "can't create bisect log"; 2917 | return $tmp_log; 2918 | } 2919 | 2920 | sub bisect { 2921 | my ($i) = @_; 2922 | 2923 | my $result; 2924 | 2925 | dodie "BISECT_GOOD[$i] not defined\n" if (!defined($bisect_good)); 2926 | dodie "BISECT_BAD[$i] not defined\n" if (!defined($bisect_bad)); 2927 | dodie "BISECT_TYPE[$i] not defined\n" if (!defined($bisect_type)); 2928 | 2929 | my $good = $bisect_good; 2930 | my $bad = $bisect_bad; 2931 | my $type = $bisect_type; 2932 | my $start = $bisect_start; 2933 | my $replay = $bisect_replay; 2934 | my $start_files = $bisect_files; 2935 | 2936 | if (defined($start_files)) { 2937 | $start_files = " -- " . $start_files; 2938 | } else { 2939 | $start_files = ""; 2940 | } 2941 | 2942 | # convert to true sha1's 2943 | $good = get_sha1($good); 2944 | $bad = get_sha1($bad); 2945 | 2946 | if (defined($bisect_reverse) && $bisect_reverse == 1) { 2947 | doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; 2948 | $reverse_bisect = 1; 2949 | } else { 2950 | $reverse_bisect = 0; 2951 | } 2952 | 2953 | # Can't have a test without having a test to run 2954 | if ($type eq "test" && !defined($run_test)) { 2955 | $type = "boot"; 2956 | } 2957 | 2958 | # Check if a bisect was running 2959 | my $bisect_start_file = "$builddir/.git/BISECT_START"; 2960 | 2961 | my $check = $bisect_check; 2962 | my $do_check = defined($check) && $check ne "0"; 2963 | 2964 | if ( -f $bisect_start_file ) { 2965 | print "Bisect in progress found\n"; 2966 | if ($do_check) { 2967 | print " If you say yes, then no checks of good or bad will be done\n"; 2968 | } 2969 | if (defined($replay)) { 2970 | print "** BISECT_REPLAY is defined in config file **"; 2971 | print " Ignore config option and perform new git bisect log?\n"; 2972 | if (read_ync " (yes, no, or cancel) ") { 2973 | $replay = update_bisect_replay; 2974 | $do_check = 0; 2975 | } 2976 | } elsif (read_yn "read git log and continue?") { 2977 | $replay = update_bisect_replay; 2978 | $do_check = 0; 2979 | } 2980 | } 2981 | 2982 | if ($do_check) { 2983 | 2984 | # get current HEAD 2985 | my $head = get_sha1("HEAD"); 2986 | 2987 | if ($check ne "good") { 2988 | doprint "TESTING BISECT BAD [$bad]\n"; 2989 | run_command "git checkout $bad" or 2990 | dodie "Failed to checkout $bad"; 2991 | 2992 | $result = run_bisect $type; 2993 | 2994 | if ($result ne "bad") { 2995 | fail "Tested BISECT_BAD [$bad] and it succeeded" and return 0; 2996 | } 2997 | } 2998 | 2999 | if ($check ne "bad") { 3000 | doprint "TESTING BISECT GOOD [$good]\n"; 3001 | run_command "git checkout $good" or 3002 | dodie "Failed to checkout $good"; 3003 | 3004 | $result = run_bisect $type; 3005 | 3006 | if ($result ne "good") { 3007 | fail "Tested BISECT_GOOD [$good] and it failed" and return 0; 3008 | } 3009 | } 3010 | 3011 | # checkout where we started 3012 | run_command "git checkout $head" or 3013 | dodie "Failed to checkout $head"; 3014 | } 3015 | 3016 | run_command "git bisect start$start_files" or 3017 | dodie "could not start bisect"; 3018 | 3019 | if (defined($replay)) { 3020 | run_command "git bisect replay $replay" or 3021 | dodie "failed to run replay"; 3022 | } else { 3023 | 3024 | run_command "git bisect good $good" or 3025 | dodie "could not set bisect good to $good"; 3026 | 3027 | run_git_bisect "git bisect bad $bad" or 3028 | dodie "could not set bisect bad to $bad"; 3029 | 3030 | } 3031 | 3032 | if (defined($start)) { 3033 | run_command "git checkout $start" or 3034 | dodie "failed to checkout $start"; 3035 | } 3036 | 3037 | my $test; 3038 | do { 3039 | $result = run_bisect $type; 3040 | $test = run_git_bisect "git bisect $result"; 3041 | print_times; 3042 | } while ($test); 3043 | 3044 | run_command "git bisect log" or 3045 | dodie "could not capture git bisect log"; 3046 | 3047 | run_command "git bisect reset" or 3048 | dodie "could not reset git bisect"; 3049 | 3050 | doprint "Bad commit was [$bisect_bad_commit]\n"; 3051 | 3052 | success $i; 3053 | } 3054 | 3055 | # config_ignore holds the configs that were set (or unset) for 3056 | # a good config and we will ignore these configs for the rest 3057 | # of a config bisect. These configs stay as they were. 3058 | my %config_ignore; 3059 | 3060 | # config_set holds what all configs were set as. 3061 | my %config_set; 3062 | 3063 | # config_off holds the set of configs that the bad config had disabled. 3064 | # We need to record them and set them in the .config when running 3065 | # olddefconfig, because olddefconfig keeps the defaults. 3066 | my %config_off; 3067 | 3068 | # config_off_tmp holds a set of configs to turn off for now 3069 | my @config_off_tmp; 3070 | 3071 | # config_list is the set of configs that are being tested 3072 | my %config_list; 3073 | my %null_config; 3074 | 3075 | my %dependency; 3076 | 3077 | sub assign_configs { 3078 | my ($hash, $config) = @_; 3079 | 3080 | doprint "Reading configs from $config\n"; 3081 | 3082 | open (IN, $config) 3083 | or dodie "Failed to read $config"; 3084 | 3085 | while () { 3086 | chomp; 3087 | if (/^((CONFIG\S*)=.*)/) { 3088 | ${$hash}{$2} = $1; 3089 | } elsif (/^(# (CONFIG\S*) is not set)/) { 3090 | ${$hash}{$2} = $1; 3091 | } 3092 | } 3093 | 3094 | close(IN); 3095 | } 3096 | 3097 | sub process_config_ignore { 3098 | my ($config) = @_; 3099 | 3100 | assign_configs \%config_ignore, $config; 3101 | } 3102 | 3103 | sub get_dependencies { 3104 | my ($config) = @_; 3105 | 3106 | my $arr = $dependency{$config}; 3107 | if (!defined($arr)) { 3108 | return (); 3109 | } 3110 | 3111 | my @deps = @{$arr}; 3112 | 3113 | foreach my $dep (@{$arr}) { 3114 | print "ADD DEP $dep\n"; 3115 | @deps = (@deps, get_dependencies $dep); 3116 | } 3117 | 3118 | return @deps; 3119 | } 3120 | 3121 | sub save_config { 3122 | my ($pc, $file) = @_; 3123 | 3124 | my %configs = %{$pc}; 3125 | 3126 | doprint "Saving configs into $file\n"; 3127 | 3128 | open(OUT, ">$file") or dodie "Can not write to $file"; 3129 | 3130 | foreach my $config (keys %configs) { 3131 | print OUT "$configs{$config}\n"; 3132 | } 3133 | close(OUT); 3134 | } 3135 | 3136 | sub create_config { 3137 | my ($name, $pc) = @_; 3138 | 3139 | doprint "Creating old config from $name configs\n"; 3140 | 3141 | save_config $pc, $output_config; 3142 | 3143 | make_oldconfig; 3144 | } 3145 | 3146 | sub run_config_bisect_test { 3147 | my ($type) = @_; 3148 | 3149 | my $ret = run_bisect_test $type, "oldconfig"; 3150 | 3151 | if ($bisect_manual) { 3152 | $ret = answer_bisect; 3153 | } 3154 | 3155 | return $ret; 3156 | } 3157 | 3158 | sub config_bisect_end { 3159 | my ($good, $bad) = @_; 3160 | my $diffexec = "diff -u"; 3161 | 3162 | if (-f "$builddir/scripts/diffconfig") { 3163 | $diffexec = "$builddir/scripts/diffconfig"; 3164 | } 3165 | doprint "\n\n***************************************\n"; 3166 | doprint "No more config bisecting possible.\n"; 3167 | run_command "$diffexec $good $bad", 1; 3168 | doprint "***************************************\n\n"; 3169 | } 3170 | 3171 | sub run_config_bisect { 3172 | my ($good, $bad, $last_result) = @_; 3173 | my $reset = ""; 3174 | my $cmd; 3175 | my $ret; 3176 | 3177 | if (!length($last_result)) { 3178 | $reset = "-r"; 3179 | } 3180 | run_command "$config_bisect_exec $reset -b $outputdir $good $bad $last_result", 1; 3181 | 3182 | # config-bisect returns: 3183 | # 0 if there is more to bisect 3184 | # 1 for finding a good config 3185 | # 2 if it can not find any more configs 3186 | # -1 (255) on error 3187 | if ($run_command_status) { 3188 | return $run_command_status; 3189 | } 3190 | 3191 | $ret = run_config_bisect_test $config_bisect_type; 3192 | if ($ret) { 3193 | doprint "NEW GOOD CONFIG\n"; 3194 | # Return 3 for good config 3195 | return 3; 3196 | } else { 3197 | doprint "NEW BAD CONFIG\n"; 3198 | # Return 4 for bad config 3199 | return 4; 3200 | } 3201 | } 3202 | 3203 | sub config_bisect { 3204 | my ($i) = @_; 3205 | 3206 | my $good_config; 3207 | my $bad_config; 3208 | 3209 | my $type = $config_bisect_type; 3210 | my $ret; 3211 | 3212 | $bad_config = $config_bisect; 3213 | 3214 | if (defined($config_bisect_good)) { 3215 | $good_config = $config_bisect_good; 3216 | } elsif (defined($minconfig)) { 3217 | $good_config = $minconfig; 3218 | } else { 3219 | doprint "No config specified, checking if defconfig works"; 3220 | $ret = run_bisect_test $type, "defconfig"; 3221 | if (!$ret) { 3222 | fail "Have no good config to compare with, please set CONFIG_BISECT_GOOD"; 3223 | return 1; 3224 | } 3225 | $good_config = $output_config; 3226 | } 3227 | 3228 | if (!defined($config_bisect_exec)) { 3229 | # First check the location that ktest.pl ran 3230 | my @locations = ( "$pwd/config-bisect.pl", 3231 | "$dirname/config-bisect.pl", 3232 | "$builddir/tools/testing/ktest/config-bisect.pl", 3233 | undef ); 3234 | foreach my $loc (@locations) { 3235 | doprint "loc = $loc\n"; 3236 | $config_bisect_exec = $loc; 3237 | last if (defined($config_bisect_exec && -x $config_bisect_exec)); 3238 | } 3239 | if (!defined($config_bisect_exec)) { 3240 | fail "Could not find an executable config-bisect.pl\n", 3241 | " Set CONFIG_BISECT_EXEC to point to config-bisect.pl"; 3242 | return 1; 3243 | } 3244 | } 3245 | 3246 | # we don't want min configs to cause issues here. 3247 | doprint "Disabling 'MIN_CONFIG' for this test\n"; 3248 | undef $minconfig; 3249 | 3250 | my %good_configs; 3251 | my %bad_configs; 3252 | my %tmp_configs; 3253 | 3254 | if (-f "$tmpdir/good_config.tmp" || -f "$tmpdir/bad_config.tmp") { 3255 | if (read_yn "Interrupted config-bisect. Continue (n - will start new)?") { 3256 | if (-f "$tmpdir/good_config.tmp") { 3257 | $good_config = "$tmpdir/good_config.tmp"; 3258 | } else { 3259 | $good_config = "$tmpdir/good_config"; 3260 | } 3261 | if (-f "$tmpdir/bad_config.tmp") { 3262 | $bad_config = "$tmpdir/bad_config.tmp"; 3263 | } else { 3264 | $bad_config = "$tmpdir/bad_config"; 3265 | } 3266 | } 3267 | } 3268 | doprint "Run good configs through make oldconfig\n"; 3269 | assign_configs \%tmp_configs, $good_config; 3270 | create_config "$good_config", \%tmp_configs; 3271 | $good_config = "$tmpdir/good_config"; 3272 | system("cp $output_config $good_config") == 0 or dodie "cp good config"; 3273 | 3274 | doprint "Run bad configs through make oldconfig\n"; 3275 | assign_configs \%tmp_configs, $bad_config; 3276 | create_config "$bad_config", \%tmp_configs; 3277 | $bad_config = "$tmpdir/bad_config"; 3278 | system("cp $output_config $bad_config") == 0 or dodie "cp bad config"; 3279 | 3280 | if (defined($config_bisect_check) && $config_bisect_check ne "0") { 3281 | if ($config_bisect_check ne "good") { 3282 | doprint "Testing bad config\n"; 3283 | 3284 | $ret = run_bisect_test $type, "useconfig:$bad_config"; 3285 | if ($ret) { 3286 | fail "Bad config succeeded when expected to fail!"; 3287 | return 0; 3288 | } 3289 | } 3290 | if ($config_bisect_check ne "bad") { 3291 | doprint "Testing good config\n"; 3292 | 3293 | $ret = run_bisect_test $type, "useconfig:$good_config"; 3294 | if (!$ret) { 3295 | fail "Good config failed when expected to succeed!"; 3296 | return 0; 3297 | } 3298 | } 3299 | } 3300 | 3301 | my $last_run = ""; 3302 | 3303 | do { 3304 | $ret = run_config_bisect $good_config, $bad_config, $last_run; 3305 | if ($ret == 3) { 3306 | $last_run = "good"; 3307 | } elsif ($ret == 4) { 3308 | $last_run = "bad"; 3309 | } 3310 | print_times; 3311 | } while ($ret == 3 || $ret == 4); 3312 | 3313 | if ($ret == 2) { 3314 | config_bisect_end "$good_config.tmp", "$bad_config.tmp"; 3315 | } 3316 | 3317 | return $ret if ($ret < 0); 3318 | 3319 | success $i; 3320 | } 3321 | 3322 | sub patchcheck_reboot { 3323 | doprint "Reboot and sleep $patchcheck_sleep_time seconds\n"; 3324 | reboot_to_good $patchcheck_sleep_time; 3325 | } 3326 | 3327 | sub patchcheck { 3328 | my ($i) = @_; 3329 | 3330 | dodie "PATCHCHECK_START[$i] not defined\n" 3331 | if (!defined($patchcheck_start)); 3332 | dodie "PATCHCHECK_TYPE[$i] not defined\n" 3333 | if (!defined($patchcheck_type)); 3334 | 3335 | my $start = $patchcheck_start; 3336 | 3337 | my $cherry = $patchcheck_cherry; 3338 | if (!defined($cherry)) { 3339 | $cherry = 0; 3340 | } 3341 | 3342 | my $end = "HEAD"; 3343 | if (defined($patchcheck_end)) { 3344 | $end = $patchcheck_end; 3345 | } elsif ($cherry) { 3346 | dodie "PATCHCHECK_END must be defined with PATCHCHECK_CHERRY\n"; 3347 | } 3348 | 3349 | # Get the true sha1's since we can use things like HEAD~3 3350 | $start = get_sha1($start); 3351 | $end = get_sha1($end); 3352 | 3353 | my $type = $patchcheck_type; 3354 | 3355 | # Can't have a test without having a test to run 3356 | if ($type eq "test" && !defined($run_test)) { 3357 | $type = "boot"; 3358 | } 3359 | 3360 | if ($cherry) { 3361 | open (IN, "git cherry -v $start $end|") or 3362 | dodie "could not get git list"; 3363 | } else { 3364 | open (IN, "git log --pretty=oneline $end|") or 3365 | dodie "could not get git list"; 3366 | } 3367 | 3368 | my @list; 3369 | 3370 | while () { 3371 | chomp; 3372 | # git cherry adds a '+' we want to remove 3373 | s/^\+ //; 3374 | $list[$#list+1] = $_; 3375 | last if (/^$start/); 3376 | } 3377 | close(IN); 3378 | 3379 | if (!$cherry) { 3380 | if ($list[$#list] !~ /^$start/) { 3381 | fail "SHA1 $start not found"; 3382 | } 3383 | 3384 | # go backwards in the list 3385 | @list = reverse @list; 3386 | } 3387 | 3388 | doprint("Going to test the following commits:\n"); 3389 | foreach my $l (@list) { 3390 | doprint "$l\n"; 3391 | } 3392 | 3393 | my $save_clean = $noclean; 3394 | my %ignored_warnings; 3395 | 3396 | if (defined($ignore_warnings)) { 3397 | foreach my $sha1 (split /\s+/, $ignore_warnings) { 3398 | $ignored_warnings{$sha1} = 1; 3399 | } 3400 | } 3401 | 3402 | $in_patchcheck = 1; 3403 | foreach my $item (@list) { 3404 | my $sha1 = $item; 3405 | $sha1 =~ s/^([[:xdigit:]]+).*/$1/; 3406 | 3407 | doprint "\nProcessing commit \"$item\"\n\n"; 3408 | 3409 | run_command "git checkout $sha1" or 3410 | dodie "Failed to checkout $sha1"; 3411 | 3412 | # only clean on the first and last patch 3413 | if ($item eq $list[0] || 3414 | $item eq $list[$#list]) { 3415 | $noclean = $save_clean; 3416 | } else { 3417 | $noclean = 1; 3418 | } 3419 | 3420 | if (defined($minconfig)) { 3421 | build "useconfig:$minconfig" or return 0; 3422 | } else { 3423 | # ?? no config to use? 3424 | build "oldconfig" or return 0; 3425 | } 3426 | 3427 | # No need to do per patch checking if warnings file exists 3428 | if (!defined($warnings_file) && !defined($ignored_warnings{$sha1})) { 3429 | check_patch_buildlog $sha1 or return 0; 3430 | } 3431 | 3432 | check_buildlog or return 0; 3433 | 3434 | next if ($type eq "build"); 3435 | 3436 | my $failed = 0; 3437 | 3438 | start_monitor_and_install or $failed = 1; 3439 | 3440 | if (!$failed && $type ne "boot"){ 3441 | do_run_test or $failed = 1; 3442 | } 3443 | end_monitor; 3444 | if ($failed) { 3445 | print_times; 3446 | return 0; 3447 | } 3448 | patchcheck_reboot; 3449 | print_times; 3450 | } 3451 | $in_patchcheck = 0; 3452 | success $i; 3453 | 3454 | return 1; 3455 | } 3456 | 3457 | my %depends; 3458 | my %depcount; 3459 | my $iflevel = 0; 3460 | my @ifdeps; 3461 | 3462 | # prevent recursion 3463 | my %read_kconfigs; 3464 | 3465 | sub add_dep { 3466 | # $config depends on $dep 3467 | my ($config, $dep) = @_; 3468 | 3469 | if (defined($depends{$config})) { 3470 | $depends{$config} .= " " . $dep; 3471 | } else { 3472 | $depends{$config} = $dep; 3473 | } 3474 | 3475 | # record the number of configs depending on $dep 3476 | if (defined $depcount{$dep}) { 3477 | $depcount{$dep}++; 3478 | } else { 3479 | $depcount{$dep} = 1; 3480 | } 3481 | } 3482 | 3483 | # taken from streamline_config.pl 3484 | sub read_kconfig { 3485 | my ($kconfig) = @_; 3486 | 3487 | my $state = "NONE"; 3488 | my $config; 3489 | my @kconfigs; 3490 | 3491 | my $cont = 0; 3492 | my $line; 3493 | 3494 | 3495 | if (! -f $kconfig) { 3496 | doprint "file $kconfig does not exist, skipping\n"; 3497 | return; 3498 | } 3499 | 3500 | open(KIN, "$kconfig") 3501 | or dodie "Can't open $kconfig"; 3502 | while () { 3503 | chomp; 3504 | 3505 | # Make sure that lines ending with \ continue 3506 | if ($cont) { 3507 | $_ = $line . " " . $_; 3508 | } 3509 | 3510 | if (s/\\$//) { 3511 | $cont = 1; 3512 | $line = $_; 3513 | next; 3514 | } 3515 | 3516 | $cont = 0; 3517 | 3518 | # collect any Kconfig sources 3519 | if (/^source\s*"(.*)"/) { 3520 | $kconfigs[$#kconfigs+1] = $1; 3521 | } 3522 | 3523 | # configs found 3524 | if (/^\s*(menu)?config\s+(\S+)\s*$/) { 3525 | $state = "NEW"; 3526 | $config = $2; 3527 | 3528 | for (my $i = 0; $i < $iflevel; $i++) { 3529 | add_dep $config, $ifdeps[$i]; 3530 | } 3531 | 3532 | # collect the depends for the config 3533 | } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) { 3534 | 3535 | add_dep $config, $1; 3536 | 3537 | # Get the configs that select this config 3538 | } elsif ($state eq "NEW" && /^\s*select\s+(\S+)/) { 3539 | 3540 | # selected by depends on config 3541 | add_dep $1, $config; 3542 | 3543 | # Check for if statements 3544 | } elsif (/^if\s+(.*\S)\s*$/) { 3545 | my $deps = $1; 3546 | # remove beginning and ending non text 3547 | $deps =~ s/^[^a-zA-Z0-9_]*//; 3548 | $deps =~ s/[^a-zA-Z0-9_]*$//; 3549 | 3550 | my @deps = split /[^a-zA-Z0-9_]+/, $deps; 3551 | 3552 | $ifdeps[$iflevel++] = join ':', @deps; 3553 | 3554 | } elsif (/^endif/) { 3555 | 3556 | $iflevel-- if ($iflevel); 3557 | 3558 | # stop on "help" 3559 | } elsif (/^\s*help\s*$/) { 3560 | $state = "NONE"; 3561 | } 3562 | } 3563 | close(KIN); 3564 | 3565 | # read in any configs that were found. 3566 | foreach $kconfig (@kconfigs) { 3567 | if (!defined($read_kconfigs{$kconfig})) { 3568 | $read_kconfigs{$kconfig} = 1; 3569 | read_kconfig("$builddir/$kconfig"); 3570 | } 3571 | } 3572 | } 3573 | 3574 | sub read_depends { 3575 | # find out which arch this is by the kconfig file 3576 | open (IN, $output_config) 3577 | or dodie "Failed to read $output_config"; 3578 | my $arch; 3579 | while () { 3580 | if (m,Linux/(\S+)\s+\S+\s+Kernel Configuration,) { 3581 | $arch = $1; 3582 | last; 3583 | } 3584 | } 3585 | close IN; 3586 | 3587 | if (!defined($arch)) { 3588 | doprint "Could not find arch from config file\n"; 3589 | doprint "no dependencies used\n"; 3590 | return; 3591 | } 3592 | 3593 | # arch is really the subarch, we need to know 3594 | # what directory to look at. 3595 | if ($arch eq "i386" || $arch eq "x86_64") { 3596 | $arch = "x86"; 3597 | } 3598 | 3599 | my $kconfig = "$builddir/arch/$arch/Kconfig"; 3600 | 3601 | if (! -f $kconfig && $arch =~ /\d$/) { 3602 | my $orig = $arch; 3603 | # some subarchs have numbers, truncate them 3604 | $arch =~ s/\d*$//; 3605 | $kconfig = "$builddir/arch/$arch/Kconfig"; 3606 | if (! -f $kconfig) { 3607 | doprint "No idea what arch dir $orig is for\n"; 3608 | doprint "no dependencies used\n"; 3609 | return; 3610 | } 3611 | } 3612 | 3613 | read_kconfig($kconfig); 3614 | } 3615 | 3616 | sub make_new_config { 3617 | my @configs = @_; 3618 | 3619 | open (OUT, ">$output_config") 3620 | or dodie "Failed to write $output_config"; 3621 | 3622 | foreach my $config (@configs) { 3623 | print OUT "$config\n"; 3624 | } 3625 | close OUT; 3626 | } 3627 | 3628 | sub chomp_config { 3629 | my ($config) = @_; 3630 | 3631 | $config =~ s/CONFIG_//; 3632 | 3633 | return $config; 3634 | } 3635 | 3636 | sub get_depends { 3637 | my ($dep) = @_; 3638 | 3639 | my $kconfig = chomp_config $dep; 3640 | 3641 | $dep = $depends{"$kconfig"}; 3642 | 3643 | # the dep string we have saves the dependencies as they 3644 | # were found, including expressions like ! && ||. We 3645 | # want to split this out into just an array of configs. 3646 | 3647 | my $valid = "A-Za-z_0-9"; 3648 | 3649 | my @configs; 3650 | 3651 | while ($dep =~ /[$valid]/) { 3652 | 3653 | if ($dep =~ /^[^$valid]*([$valid]+)/) { 3654 | my $conf = "CONFIG_" . $1; 3655 | 3656 | $configs[$#configs + 1] = $conf; 3657 | 3658 | $dep =~ s/^[^$valid]*[$valid]+//; 3659 | } else { 3660 | dodie "this should never happen"; 3661 | } 3662 | } 3663 | 3664 | return @configs; 3665 | } 3666 | 3667 | my %min_configs; 3668 | my %keep_configs; 3669 | my %save_configs; 3670 | my %processed_configs; 3671 | my %nochange_config; 3672 | 3673 | sub test_this_config { 3674 | my ($config) = @_; 3675 | 3676 | my $found; 3677 | 3678 | # if we already processed this config, skip it 3679 | if (defined($processed_configs{$config})) { 3680 | return undef; 3681 | } 3682 | $processed_configs{$config} = 1; 3683 | 3684 | # if this config failed during this round, skip it 3685 | if (defined($nochange_config{$config})) { 3686 | return undef; 3687 | } 3688 | 3689 | my $kconfig = chomp_config $config; 3690 | 3691 | # Test dependencies first 3692 | if (defined($depends{"$kconfig"})) { 3693 | my @parents = get_depends $config; 3694 | foreach my $parent (@parents) { 3695 | # if the parent is in the min config, check it first 3696 | next if (!defined($min_configs{$parent})); 3697 | $found = test_this_config($parent); 3698 | if (defined($found)) { 3699 | return $found; 3700 | } 3701 | } 3702 | } 3703 | 3704 | # Remove this config from the list of configs 3705 | # do a make olddefconfig and then read the resulting 3706 | # .config to make sure it is missing the config that 3707 | # we had before 3708 | my %configs = %min_configs; 3709 | delete $configs{$config}; 3710 | make_new_config ((values %configs), (values %keep_configs)); 3711 | make_oldconfig; 3712 | undef %configs; 3713 | assign_configs \%configs, $output_config; 3714 | 3715 | if (!defined($configs{$config}) || $configs{$config} =~ /^#/) { 3716 | return $config; 3717 | } 3718 | 3719 | doprint "disabling config $config did not change .config\n"; 3720 | 3721 | $nochange_config{$config} = 1; 3722 | 3723 | return undef; 3724 | } 3725 | 3726 | sub make_min_config { 3727 | my ($i) = @_; 3728 | 3729 | my $type = $minconfig_type; 3730 | if ($type ne "boot" && $type ne "test") { 3731 | fail "Invalid MIN_CONFIG_TYPE '$minconfig_type'\n" . 3732 | " make_min_config works only with 'boot' and 'test'\n" and return; 3733 | } 3734 | 3735 | if (!defined($output_minconfig)) { 3736 | fail "OUTPUT_MIN_CONFIG not defined" and return; 3737 | } 3738 | 3739 | # If output_minconfig exists, and the start_minconfig 3740 | # came from min_config, than ask if we should use 3741 | # that instead. 3742 | if (-f $output_minconfig && !$start_minconfig_defined) { 3743 | print "$output_minconfig exists\n"; 3744 | if (!defined($use_output_minconfig)) { 3745 | if (read_yn " Use it as minconfig?") { 3746 | $start_minconfig = $output_minconfig; 3747 | } 3748 | } elsif ($use_output_minconfig > 0) { 3749 | doprint "Using $output_minconfig as MIN_CONFIG\n"; 3750 | $start_minconfig = $output_minconfig; 3751 | } else { 3752 | doprint "Set to still use MIN_CONFIG as starting point\n"; 3753 | } 3754 | } 3755 | 3756 | if (!defined($start_minconfig)) { 3757 | fail "START_MIN_CONFIG or MIN_CONFIG not defined" and return; 3758 | } 3759 | 3760 | my $temp_config = "$tmpdir/temp_config"; 3761 | 3762 | # First things first. We build an allnoconfig to find 3763 | # out what the defaults are that we can't touch. 3764 | # Some are selections, but we really can't handle selections. 3765 | 3766 | my $save_minconfig = $minconfig; 3767 | undef $minconfig; 3768 | 3769 | run_command "$make allnoconfig" or return 0; 3770 | 3771 | read_depends; 3772 | 3773 | process_config_ignore $output_config; 3774 | 3775 | undef %save_configs; 3776 | undef %min_configs; 3777 | 3778 | if (defined($ignore_config)) { 3779 | # make sure the file exists 3780 | `touch $ignore_config`; 3781 | assign_configs \%save_configs, $ignore_config; 3782 | } 3783 | 3784 | %keep_configs = %save_configs; 3785 | 3786 | doprint "Load initial configs from $start_minconfig\n"; 3787 | 3788 | # Look at the current min configs, and save off all the 3789 | # ones that were set via the allnoconfig 3790 | assign_configs \%min_configs, $start_minconfig; 3791 | 3792 | my @config_keys = keys %min_configs; 3793 | 3794 | # All configs need a depcount 3795 | foreach my $config (@config_keys) { 3796 | my $kconfig = chomp_config $config; 3797 | if (!defined $depcount{$kconfig}) { 3798 | $depcount{$kconfig} = 0; 3799 | } 3800 | } 3801 | 3802 | # Remove anything that was set by the make allnoconfig 3803 | # we shouldn't need them as they get set for us anyway. 3804 | foreach my $config (@config_keys) { 3805 | # Remove anything in the ignore_config 3806 | if (defined($keep_configs{$config})) { 3807 | my $file = $ignore_config; 3808 | $file =~ s,.*/(.*?)$,$1,; 3809 | doprint "$config set by $file ... ignored\n"; 3810 | delete $min_configs{$config}; 3811 | next; 3812 | } 3813 | # But make sure the settings are the same. If a min config 3814 | # sets a selection, we do not want to get rid of it if 3815 | # it is not the same as what we have. Just move it into 3816 | # the keep configs. 3817 | if (defined($config_ignore{$config})) { 3818 | if ($config_ignore{$config} ne $min_configs{$config}) { 3819 | doprint "$config is in allnoconfig as '$config_ignore{$config}'"; 3820 | doprint " but it is '$min_configs{$config}' in minconfig .. keeping\n"; 3821 | $keep_configs{$config} = $min_configs{$config}; 3822 | } else { 3823 | doprint "$config set by allnoconfig ... ignored\n"; 3824 | } 3825 | delete $min_configs{$config}; 3826 | } 3827 | } 3828 | 3829 | my $done = 0; 3830 | my $take_two = 0; 3831 | 3832 | while (!$done) { 3833 | 3834 | my $config; 3835 | my $found; 3836 | 3837 | # Now disable each config one by one and do a make oldconfig 3838 | # till we find a config that changes our list. 3839 | 3840 | my @test_configs = keys %min_configs; 3841 | 3842 | # Sort keys by who is most dependent on 3843 | @test_configs = sort { $depcount{chomp_config($b)} <=> $depcount{chomp_config($a)} } 3844 | @test_configs ; 3845 | 3846 | # Put configs that did not modify the config at the end. 3847 | my $reset = 1; 3848 | for (my $i = 0; $i < $#test_configs; $i++) { 3849 | if (!defined($nochange_config{$test_configs[0]})) { 3850 | $reset = 0; 3851 | last; 3852 | } 3853 | # This config didn't change the .config last time. 3854 | # Place it at the end 3855 | my $config = shift @test_configs; 3856 | push @test_configs, $config; 3857 | } 3858 | 3859 | # if every test config has failed to modify the .config file 3860 | # in the past, then reset and start over. 3861 | if ($reset) { 3862 | undef %nochange_config; 3863 | } 3864 | 3865 | undef %processed_configs; 3866 | 3867 | foreach my $config (@test_configs) { 3868 | 3869 | $found = test_this_config $config; 3870 | 3871 | last if (defined($found)); 3872 | 3873 | # oh well, try another config 3874 | } 3875 | 3876 | if (!defined($found)) { 3877 | # we could have failed due to the nochange_config hash 3878 | # reset and try again 3879 | if (!$take_two) { 3880 | undef %nochange_config; 3881 | $take_two = 1; 3882 | next; 3883 | } 3884 | doprint "No more configs found that we can disable\n"; 3885 | $done = 1; 3886 | last; 3887 | } 3888 | $take_two = 0; 3889 | 3890 | $config = $found; 3891 | 3892 | doprint "Test with $config disabled\n"; 3893 | 3894 | # set in_bisect to keep build and monitor from dieing 3895 | $in_bisect = 1; 3896 | 3897 | my $failed = 0; 3898 | build "oldconfig" or $failed = 1; 3899 | if (!$failed) { 3900 | start_monitor_and_install or $failed = 1; 3901 | 3902 | if ($type eq "test" && !$failed) { 3903 | do_run_test or $failed = 1; 3904 | } 3905 | 3906 | end_monitor; 3907 | } 3908 | 3909 | $in_bisect = 0; 3910 | 3911 | if ($failed) { 3912 | doprint "$min_configs{$config} is needed to boot the box... keeping\n"; 3913 | # this config is needed, add it to the ignore list. 3914 | $keep_configs{$config} = $min_configs{$config}; 3915 | $save_configs{$config} = $min_configs{$config}; 3916 | delete $min_configs{$config}; 3917 | 3918 | # update new ignore configs 3919 | if (defined($ignore_config)) { 3920 | open (OUT, ">$temp_config") 3921 | or dodie "Can't write to $temp_config"; 3922 | foreach my $config (keys %save_configs) { 3923 | print OUT "$save_configs{$config}\n"; 3924 | } 3925 | close OUT; 3926 | run_command "mv $temp_config $ignore_config" or 3927 | dodie "failed to copy update to $ignore_config"; 3928 | } 3929 | 3930 | } else { 3931 | # We booted without this config, remove it from the minconfigs. 3932 | doprint "$config is not needed, disabling\n"; 3933 | 3934 | delete $min_configs{$config}; 3935 | 3936 | # Also disable anything that is not enabled in this config 3937 | my %configs; 3938 | assign_configs \%configs, $output_config; 3939 | my @config_keys = keys %min_configs; 3940 | foreach my $config (@config_keys) { 3941 | if (!defined($configs{$config})) { 3942 | doprint "$config is not set, disabling\n"; 3943 | delete $min_configs{$config}; 3944 | } 3945 | } 3946 | 3947 | # Save off all the current mandatory configs 3948 | open (OUT, ">$temp_config") 3949 | or dodie "Can't write to $temp_config"; 3950 | foreach my $config (keys %keep_configs) { 3951 | print OUT "$keep_configs{$config}\n"; 3952 | } 3953 | foreach my $config (keys %min_configs) { 3954 | print OUT "$min_configs{$config}\n"; 3955 | } 3956 | close OUT; 3957 | 3958 | run_command "mv $temp_config $output_minconfig" or 3959 | dodie "failed to copy update to $output_minconfig"; 3960 | } 3961 | 3962 | doprint "Reboot and wait $sleep_time seconds\n"; 3963 | reboot_to_good $sleep_time; 3964 | } 3965 | 3966 | success $i; 3967 | return 1; 3968 | } 3969 | 3970 | sub make_warnings_file { 3971 | my ($i) = @_; 3972 | 3973 | if (!defined($warnings_file)) { 3974 | dodie "Must define WARNINGS_FILE for make_warnings_file test"; 3975 | } 3976 | 3977 | if ($build_type eq "nobuild") { 3978 | dodie "BUILD_TYPE can not be 'nobuild' for make_warnings_file test"; 3979 | } 3980 | 3981 | build $build_type or dodie "Failed to build"; 3982 | 3983 | open(OUT, ">$warnings_file") or dodie "Can't create $warnings_file"; 3984 | 3985 | open(IN, $buildlog) or dodie "Can't open $buildlog"; 3986 | while () { 3987 | 3988 | # Some compilers use UTF-8 extended for quotes 3989 | # for distcc heterogeneous systems, this causes issues 3990 | s/$utf8_quote/'/g; 3991 | 3992 | if (/$check_build_re/) { 3993 | print OUT; 3994 | } 3995 | } 3996 | close(IN); 3997 | 3998 | close(OUT); 3999 | 4000 | success $i; 4001 | } 4002 | 4003 | $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl [config-file]\n"; 4004 | 4005 | if ($#ARGV == 0) { 4006 | $ktest_config = $ARGV[0]; 4007 | if (! -f $ktest_config) { 4008 | print "$ktest_config does not exist.\n"; 4009 | if (!read_yn "Create it?") { 4010 | exit 0; 4011 | } 4012 | } 4013 | } 4014 | 4015 | if (! -f $ktest_config) { 4016 | $newconfig = 1; 4017 | get_test_case; 4018 | open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; 4019 | print OUT << "EOF" 4020 | # Generated by ktest.pl 4021 | # 4022 | 4023 | # PWD is a ktest.pl variable that will result in the process working 4024 | # directory that ktest.pl is executed in. 4025 | 4026 | # THIS_DIR is automatically assigned the PWD of the path that generated 4027 | # the config file. It is best to use this variable when assigning other 4028 | # directory paths within this directory. This allows you to easily 4029 | # move the test cases to other locations or to other machines. 4030 | # 4031 | THIS_DIR := $variable{"PWD"} 4032 | 4033 | # Define each test with TEST_START 4034 | # The config options below it will override the defaults 4035 | TEST_START 4036 | TEST_TYPE = $default{"TEST_TYPE"} 4037 | 4038 | DEFAULTS 4039 | EOF 4040 | ; 4041 | close(OUT); 4042 | } 4043 | read_config $ktest_config; 4044 | 4045 | if (defined($opt{"LOG_FILE"})) { 4046 | $opt{"LOG_FILE"} = eval_option("LOG_FILE", $opt{"LOG_FILE"}, -1); 4047 | } 4048 | 4049 | # Append any configs entered in manually to the config file. 4050 | my @new_configs = keys %entered_configs; 4051 | if ($#new_configs >= 0) { 4052 | print "\nAppending entered in configs to $ktest_config\n"; 4053 | open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config"; 4054 | foreach my $config (@new_configs) { 4055 | print OUT "$config = $entered_configs{$config}\n"; 4056 | $opt{$config} = process_variables($entered_configs{$config}); 4057 | } 4058 | } 4059 | 4060 | if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { 4061 | unlink $opt{"LOG_FILE"}; 4062 | } 4063 | 4064 | doprint "\n\nSTARTING AUTOMATED TESTS\n\n"; 4065 | 4066 | for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) { 4067 | 4068 | if (!$i) { 4069 | doprint "DEFAULT OPTIONS:\n"; 4070 | } else { 4071 | doprint "\nTEST $i OPTIONS"; 4072 | if (defined($repeat_tests{$i})) { 4073 | $repeat = $repeat_tests{$i}; 4074 | doprint " ITERATE $repeat"; 4075 | } 4076 | doprint "\n"; 4077 | } 4078 | 4079 | foreach my $option (sort keys %opt) { 4080 | 4081 | if ($option =~ /\[(\d+)\]$/) { 4082 | next if ($i != $1); 4083 | } else { 4084 | next if ($i); 4085 | } 4086 | 4087 | doprint "$option = $opt{$option}\n"; 4088 | } 4089 | } 4090 | 4091 | sub option_defined { 4092 | my ($option) = @_; 4093 | 4094 | if (defined($opt{$option}) && $opt{$option} !~ /^\s*$/) { 4095 | return 1; 4096 | } 4097 | 4098 | return 0; 4099 | } 4100 | 4101 | sub __set_test_option { 4102 | my ($name, $i) = @_; 4103 | 4104 | my $option = "$name\[$i\]"; 4105 | 4106 | if (option_defined($option)) { 4107 | return $opt{$option}; 4108 | } 4109 | 4110 | foreach my $test (keys %repeat_tests) { 4111 | if ($i >= $test && 4112 | $i < $test + $repeat_tests{$test}) { 4113 | $option = "$name\[$test\]"; 4114 | if (option_defined($option)) { 4115 | return $opt{$option}; 4116 | } 4117 | } 4118 | } 4119 | 4120 | if (option_defined($name)) { 4121 | return $opt{$name}; 4122 | } 4123 | 4124 | return undef; 4125 | } 4126 | 4127 | sub set_test_option { 4128 | my ($name, $i) = @_; 4129 | 4130 | my $option = __set_test_option($name, $i); 4131 | return $option if (!defined($option)); 4132 | 4133 | return eval_option($name, $option, $i); 4134 | } 4135 | 4136 | sub find_mailer { 4137 | my ($mailer) = @_; 4138 | 4139 | my @paths = split /:/, $ENV{PATH}; 4140 | 4141 | # sendmail is usually in /usr/sbin 4142 | $paths[$#paths + 1] = "/usr/sbin"; 4143 | 4144 | foreach my $path (@paths) { 4145 | if (-x "$path/$mailer") { 4146 | return $path; 4147 | } 4148 | } 4149 | 4150 | return undef; 4151 | } 4152 | 4153 | sub do_send_mail { 4154 | my ($subject, $message) = @_; 4155 | 4156 | if (!defined($mail_path)) { 4157 | # find the mailer 4158 | $mail_path = find_mailer $mailer; 4159 | if (!defined($mail_path)) { 4160 | die "\nCan not find $mailer in PATH\n"; 4161 | } 4162 | } 4163 | 4164 | if (!defined($mail_command)) { 4165 | if ($mailer eq "mail" || $mailer eq "mailx") { 4166 | $mail_command = "\$MAIL_PATH/\$MAILER -s \'\$SUBJECT\' \$MAILTO <<< \'\$MESSAGE\'"; 4167 | } elsif ($mailer eq "sendmail" ) { 4168 | $mail_command = "echo \'Subject: \$SUBJECT\n\n\$MESSAGE\' | \$MAIL_PATH/\$MAILER -t \$MAILTO"; 4169 | } else { 4170 | die "\nYour mailer: $mailer is not supported.\n"; 4171 | } 4172 | } 4173 | 4174 | $mail_command =~ s/\$MAILER/$mailer/g; 4175 | $mail_command =~ s/\$MAIL_PATH/$mail_path/g; 4176 | $mail_command =~ s/\$MAILTO/$mailto/g; 4177 | $mail_command =~ s/\$SUBJECT/$subject/g; 4178 | $mail_command =~ s/\$MESSAGE/$message/g; 4179 | 4180 | run_command $mail_command; 4181 | } 4182 | 4183 | sub send_email { 4184 | 4185 | if (defined($mailto)) { 4186 | if (!defined($mailer)) { 4187 | doprint "No email sent: email or mailer not specified in config.\n"; 4188 | return; 4189 | } 4190 | do_send_mail @_; 4191 | } 4192 | } 4193 | 4194 | sub cancel_test { 4195 | if ($email_when_canceled) { 4196 | send_email("KTEST: Your [$test_type] test was cancelled", 4197 | "Your test started at $script_start_time was cancelled: sig int"); 4198 | } 4199 | die "\nCaught Sig Int, test interrupted: $!\n" 4200 | } 4201 | 4202 | $SIG{INT} = qw(cancel_test); 4203 | 4204 | # First we need to do is the builds 4205 | for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { 4206 | 4207 | # Do not reboot on failing test options 4208 | $no_reboot = 1; 4209 | $reboot_success = 0; 4210 | 4211 | $have_version = 0; 4212 | 4213 | $iteration = $i; 4214 | 4215 | $build_time = 0; 4216 | $install_time = 0; 4217 | $reboot_time = 0; 4218 | $test_time = 0; 4219 | 4220 | undef %force_config; 4221 | 4222 | my $makecmd = set_test_option("MAKE_CMD", $i); 4223 | 4224 | $outputdir = set_test_option("OUTPUT_DIR", $i); 4225 | $builddir = set_test_option("BUILD_DIR", $i); 4226 | 4227 | chdir $builddir || dodie "can't change directory to $builddir"; 4228 | 4229 | if (!-d $outputdir) { 4230 | mkpath($outputdir) or 4231 | dodie "can't create $outputdir"; 4232 | } 4233 | 4234 | $make = "$makecmd O=$outputdir"; 4235 | 4236 | # Load all the options into their mapped variable names 4237 | foreach my $opt (keys %option_map) { 4238 | ${$option_map{$opt}} = set_test_option($opt, $i); 4239 | } 4240 | 4241 | $start_minconfig_defined = 1; 4242 | 4243 | # The first test may override the PRE_KTEST option 4244 | if ($i == 1) { 4245 | if (defined($pre_ktest)) { 4246 | doprint "\n"; 4247 | run_command $pre_ktest; 4248 | } 4249 | if ($email_when_started) { 4250 | send_email("KTEST: Your [$test_type] test was started", 4251 | "Your test was started on $script_start_time"); 4252 | } 4253 | } 4254 | 4255 | # Any test can override the POST_KTEST option 4256 | # The last test takes precedence. 4257 | if (defined($post_ktest)) { 4258 | $final_post_ktest = $post_ktest; 4259 | } 4260 | 4261 | if (!defined($start_minconfig)) { 4262 | $start_minconfig_defined = 0; 4263 | $start_minconfig = $minconfig; 4264 | } 4265 | 4266 | if (!-d $tmpdir) { 4267 | mkpath($tmpdir) or 4268 | dodie "can't create $tmpdir"; 4269 | } 4270 | 4271 | $ENV{"SSH_USER"} = $ssh_user; 4272 | $ENV{"MACHINE"} = $machine; 4273 | 4274 | $buildlog = "$tmpdir/buildlog-$machine"; 4275 | $testlog = "$tmpdir/testlog-$machine"; 4276 | $dmesg = "$tmpdir/dmesg-$machine"; 4277 | $output_config = "$outputdir/.config"; 4278 | 4279 | if (!$buildonly) { 4280 | $target = "$ssh_user\@$machine"; 4281 | if ($reboot_type eq "grub") { 4282 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); 4283 | } elsif ($reboot_type eq "grub2") { 4284 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); 4285 | dodie "GRUB_FILE not defined" if (!defined($grub_file)); 4286 | } elsif ($reboot_type eq "syslinux") { 4287 | dodie "SYSLINUX_LABEL not defined" if (!defined($syslinux_label)); 4288 | } 4289 | } 4290 | 4291 | my $run_type = $build_type; 4292 | if ($test_type eq "patchcheck") { 4293 | $run_type = $patchcheck_type; 4294 | } elsif ($test_type eq "bisect") { 4295 | $run_type = $bisect_type; 4296 | } elsif ($test_type eq "config_bisect") { 4297 | $run_type = $config_bisect_type; 4298 | } elsif ($test_type eq "make_min_config") { 4299 | $run_type = ""; 4300 | } elsif ($test_type eq "make_warnings_file") { 4301 | $run_type = ""; 4302 | } 4303 | 4304 | # mistake in config file? 4305 | if (!defined($run_type)) { 4306 | $run_type = "ERROR"; 4307 | } 4308 | 4309 | my $installme = ""; 4310 | $installme = " no_install" if ($no_install); 4311 | 4312 | my $name = ""; 4313 | 4314 | if (defined($test_name)) { 4315 | $name = " ($test_name)"; 4316 | } 4317 | 4318 | doprint "\n\n"; 4319 | doprint "RUNNING TEST $i of $opt{NUM_TESTS}$name with option $test_type $run_type$installme\n\n"; 4320 | 4321 | if (defined($pre_test)) { 4322 | run_command $pre_test; 4323 | } 4324 | 4325 | unlink $dmesg; 4326 | unlink $buildlog; 4327 | unlink $testlog; 4328 | 4329 | if (defined($addconfig)) { 4330 | my $min = $minconfig; 4331 | if (!defined($minconfig)) { 4332 | $min = ""; 4333 | } 4334 | run_command "cat $addconfig $min > $tmpdir/add_config" or 4335 | dodie "Failed to create temp config"; 4336 | $minconfig = "$tmpdir/add_config"; 4337 | } 4338 | 4339 | if (defined($checkout)) { 4340 | run_command "git checkout $checkout" or 4341 | dodie "failed to checkout $checkout"; 4342 | } 4343 | 4344 | $no_reboot = 0; 4345 | 4346 | # A test may opt to not reboot the box 4347 | if ($reboot_on_success) { 4348 | $reboot_success = 1; 4349 | } 4350 | 4351 | if ($test_type eq "bisect") { 4352 | bisect $i; 4353 | next; 4354 | } elsif ($test_type eq "config_bisect") { 4355 | config_bisect $i; 4356 | next; 4357 | } elsif ($test_type eq "patchcheck") { 4358 | patchcheck $i; 4359 | next; 4360 | } elsif ($test_type eq "make_min_config") { 4361 | make_min_config $i; 4362 | next; 4363 | } elsif ($test_type eq "make_warnings_file") { 4364 | $no_reboot = 1; 4365 | make_warnings_file $i; 4366 | next; 4367 | } 4368 | 4369 | if ($build_type ne "nobuild") { 4370 | build $build_type or next; 4371 | check_buildlog or next; 4372 | } 4373 | 4374 | if ($test_type eq "install") { 4375 | get_version; 4376 | install; 4377 | success $i; 4378 | next; 4379 | } 4380 | 4381 | if ($test_type ne "build") { 4382 | my $failed = 0; 4383 | start_monitor_and_install or $failed = 1; 4384 | 4385 | if (!$failed && $test_type ne "boot" && defined($run_test)) { 4386 | do_run_test or $failed = 1; 4387 | } 4388 | end_monitor; 4389 | if ($failed) { 4390 | print_times; 4391 | next; 4392 | } 4393 | } 4394 | 4395 | print_times; 4396 | 4397 | success $i; 4398 | } 4399 | 4400 | if (defined($final_post_ktest)) { 4401 | run_command $final_post_ktest; 4402 | } 4403 | 4404 | if ($opt{"POWEROFF_ON_SUCCESS"}) { 4405 | halt; 4406 | } elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot && $reboot_success) { 4407 | reboot_to_good; 4408 | } elsif (defined($switch_to_good)) { 4409 | # still need to get to the good kernel 4410 | run_command $switch_to_good; 4411 | } 4412 | 4413 | 4414 | doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; 4415 | 4416 | if ($email_when_finished) { 4417 | send_email("KTEST: Your [$test_type] test has finished!", 4418 | "$successes of $opt{NUM_TESTS} tests started at $script_start_time were successful!"); 4419 | } 4420 | exit 0; 4421 | --------------------------------------------------------------------------------