os_kernel_lab/labcodes/lab5/tools/grade.sh
Junjie Mao 67f05c0ce0 break on the physical address of brkfunc
The kernel may panic early when paging is not yet configured. Breaking only on
the virtual address of brkfunc does not work in this case and leads to timeouts
during 'make grade'.

Signed-off-by: Junjie Mao <eternal.n08@gmail.com>
2015-03-12 16:03:47 +08:00

559 lines
18 KiB
Bash

#!/bin/sh
verbose=false
if [ "x$1" = "x-v" ]; then
verbose=true
out=/dev/stdout
err=/dev/stderr
else
out=/dev/null
err=/dev/null
fi
## make & makeopts
if gmake --version > /dev/null 2>&1; then
make=gmake;
else
make=make;
fi
makeopts="--quiet --no-print-directory -j"
make_print() {
echo `$make $makeopts print-$1`
}
## command tools
awk='awk'
bc='bc'
date='date'
grep='grep'
rm='rm -f'
sed='sed'
## symbol table
sym_table='obj/kernel.sym'
## gdb & gdbopts
gdb="$(make_print GDB)"
gdbport='1234'
gdb_in="$(make_print GRADE_GDB_IN)"
## qemu & qemuopts
qemu="$(make_print qemu)"
qemu_out="$(make_print GRADE_QEMU_OUT)"
if $qemu -nographic -help | grep -q '^-gdb'; then
qemugdb="-gdb tcp::$gdbport"
else
qemugdb="-s -p $gdbport"
fi
## default variables
default_timeout=30
default_pts=5
pts=5
part=0
part_pos=0
total=0
total_pos=0
## default functions
update_score() {
total=`expr $total + $part`
total_pos=`expr $total_pos + $part_pos`
part=0
part_pos=0
}
get_time() {
echo `$date +%s.%N 2> /dev/null`
}
show_part() {
echo "Part $1 Score: $part/$part_pos"
echo
update_score
}
show_final() {
update_score
echo "Total Score: $total/$total_pos"
if [ $total -lt $total_pos ]; then
exit 1
fi
}
show_time() {
t1=$(get_time)
time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null`
echo "(${time}s)"
}
show_build_tag() {
echo "$1:" | $awk '{printf "%-24s ", $0}'
}
show_check_tag() {
echo "$1:" | $awk '{printf " -%-40s ", $0}'
}
show_msg() {
echo $1
shift
if [ $# -gt 0 ]; then
echo -e "$@" | awk '{printf " %s\n", $0}'
echo
fi
}
pass() {
show_msg OK "$@"
part=`expr $part + $pts`
part_pos=`expr $part_pos + $pts`
}
fail() {
show_msg WRONG "$@"
part_pos=`expr $part_pos + $pts`
}
run_qemu() {
# Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty,
# wait until $brkfun is reached or $timeout expires, then kill QEMU
qemuextra=
if [ "$brkfun" ]; then
qemuextra="-S $qemugdb"
fi
if [ -z "$timeout" ] || [ $timeout -le 0 ]; then
timeout=$default_timeout;
fi
t0=$(get_time)
(
ulimit -t $timeout
exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra
) > $out 2> $err &
pid=$!
# wait for QEMU to start
sleep 1
if [ -n "$brkfun" ]; then
# find the address of the kernel $brkfun function
brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'`
brkaddr_phys=`echo $brkaddr | sed "s/^c0/00/g"`
(
echo "target remote localhost:$gdbport"
echo "break *0x$brkaddr"
if [ "$brkaddr" != "$brkaddr_phys" ]; then
echo "break *0x$brkaddr_phys"
fi
echo "continue"
) > $gdb_in
$gdb -batch -nx -x $gdb_in > /dev/null 2>&1
# make sure that QEMU is dead
# on OS X, exiting gdb doesn't always exit qemu
kill $pid > /dev/null 2>&1
fi
}
build_run() {
# usage: build_run <tag> <args>
show_build_tag "$1"
shift
if $verbose; then
echo "$make $@ ..."
fi
$make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err
if [ $? -ne 0 ]; then
echo $make $@ failed
exit 1
fi
# now run qemu and save the output
run_qemu
show_time
}
check_result() {
# usage: check_result <tag> <check> <check args...>
show_check_tag "$1"
shift
# give qemu some time to run (for asynchronous mode)
if [ ! -s $qemu_out ]; then
sleep 4
fi
if [ ! -s $qemu_out ]; then
fail > /dev/null
echo 'no $qemu_out'
else
check=$1
shift
$check "$@"
fi
}
check_regexps() {
okay=yes
not=0
reg=0
error=
for i do
if [ "x$i" = "x!" ]; then
not=1
elif [ "x$i" = "x-" ]; then
reg=1
else
if [ $reg -ne 0 ]; then
$grep '-E' "^$i\$" $qemu_out > /dev/null
else
$grep '-F' "$i" $qemu_out > /dev/null
fi
found=$(($? == 0))
if [ $found -eq $not ]; then
if [ $found -eq 0 ]; then
msg="!! error: missing '$i'"
else
msg="!! error: got unexpected line '$i'"
fi
okay=no
if [ -z "$error" ]; then
error="$msg"
else
error="$error\n$msg"
fi
fi
not=0
reg=0
fi
done
if [ "$okay" = "yes" ]; then
pass
else
fail "$error"
if $verbose; then
exit 1
fi
fi
}
run_test() {
# usage: run_test [-tag <tag>] [-prog <prog>] [-Ddef...] [-check <check>] checkargs ...
tag=
prog=
check=check_regexps
while true; do
select=
case $1 in
-tag|-prog)
select=`expr substr $1 2 ${#1}`
eval $select='$2'
;;
esac
if [ -z "$select" ]; then
break
fi
shift
shift
done
defs=
while expr "x$1" : "x-D.*" > /dev/null; do
defs="DEFS+='$1' $defs"
shift
done
if [ "x$1" = "x-check" ]; then
check=$2
shift
shift
fi
if [ -z "$prog" ]; then
$make $makeopts touch > /dev/null 2>&1
args="$defs"
else
if [ -z "$tag" ]; then
tag="$prog"
fi
args="build-$prog $defs"
fi
build_run "$tag" "$args"
check_result 'check result' "$check" "$@"
}
quick_run() {
# usage: quick_run <tag> [-Ddef...]
tag="$1"
shift
defs=
while expr "x$1" : "x-D.*" > /dev/null; do
defs="DEFS+='$1' $defs"
shift
done
$make $makeopts touch > /dev/null 2>&1
build_run "$tag" "$defs"
}
quick_check() {
# usage: quick_check <tag> checkargs ...
tag="$1"
shift
check_result "$tag" check_regexps "$@"
}
## kernel image
osimg=$(make_print ucoreimg)
## swap image
swapimg=$(make_print swapimg)
## set default qemu-options
qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback"
## set break-function, default is readline
brkfun=readline
default_check() {
pts=7
check_regexps "$@"
pts=3
quick_check 'check output' \
'memory management: default_pmm_manager' \
'check_alloc_page() succeeded!' \
'check_pgdir() succeeded!' \
'check_boot_pgdir() succeeded!' \
'PDE(0e0) c0000000-f8000000 38000000 urw' \
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
'PDE(001) fac00000-fb000000 00400000 -rw' \
' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \
' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \
'check_vma_struct() succeeded!' \
'page fault at 0x00000100: K/W [no page found].' \
'check_pgfault() succeeded!' \
'check_vmm() succeeded.' \
'page fault at 0x00001000: K/W [no page found].' \
'page fault at 0x00002000: K/W [no page found].' \
'page fault at 0x00003000: K/W [no page found].' \
'page fault at 0x00004000: K/W [no page found].' \
'write Virt Page e in fifo_check_swap' \
'page fault at 0x00005000: K/W [no page found].' \
'page fault at 0x00001000: K/W [no page found]' \
'page fault at 0x00002000: K/W [no page found].' \
'page fault at 0x00003000: K/W [no page found].' \
'page fault at 0x00004000: K/W [no page found].' \
'check_swap() succeeded!' \
'++ setup timer interrupts'
}
## check now!!
run_test -prog 'badsegment' -check default_check \
'kernel_execve: pid = 2, name = "badsegment".' \
- 'trapframe at 0xc.......' \
'trap 0x0000000d General Protection' \
' err 0x00000028' \
- ' eip 0x008.....' \
- ' esp 0xaff.....' \
' cs 0x----001b' \
' ss 0x----0023' \
! - 'user panic at .*'
run_test -prog 'divzero' -check default_check \
'kernel_execve: pid = 2, name = "divzero".' \
- 'trapframe at 0xc.......' \
'trap 0x00000000 Divide error' \
- ' eip 0x008.....' \
- ' esp 0xaff.....' \
' cs 0x----001b' \
' ss 0x----0023' \
! - 'user panic at .*'
run_test -prog 'softint' -check default_check \
'kernel_execve: pid = 2, name = "softint".' \
- 'trapframe at 0xc.......' \
'trap 0x0000000d General Protection' \
' err 0x00000072' \
- ' eip 0x008.....' \
- ' esp 0xaff.....' \
' cs 0x----001b' \
' ss 0x----0023' \
! - 'user panic at .*'
pts=10
run_test -prog 'faultread' -check default_check \
'kernel_execve: pid = 2, name = "faultread".' \
- 'trapframe at 0xc.......' \
'trap 0x0000000e Page Fault' \
' err 0x00000004' \
- ' eip 0x008.....' \
! - 'user panic at .*'
run_test -prog 'faultreadkernel' -check default_check \
'kernel_execve: pid = 2, name = "faultreadkernel".' \
- 'trapframe at 0xc.......' \
'trap 0x0000000e Page Fault' \
' err 0x00000005' \
- ' eip 0x008.....' \
! - 'user panic at .*'
run_test -prog 'hello' -check default_check \
'kernel_execve: pid = 2, name = "hello".' \
'Hello world!!.' \
'I am process 2.' \
'hello pass.'
run_test -prog 'testbss' -check default_check \
'kernel_execve: pid = 2, name = "testbss".' \
'Making sure bss works right...' \
'Yes, good. Now doing a wild write off the end...' \
'testbss may pass.' \
- 'trapframe at 0xc.......' \
'trap 0x0000000e Page Fault' \
' err 0x00000006' \
- ' eip 0x008.....' \
'killed by kernel.' \
! - 'user panic at .*'
run_test -prog 'pgdir' -check default_check \
'kernel_execve: pid = 2, name = "pgdir".' \
'I am 2, print pgdir.' \
'PDE(001) 00800000-00c00000 00400000 urw' \
' |-- PTE(00002) 00800000-00802000 00002000 ur-' \
' |-- PTE(00001) 00802000-00803000 00001000 urw' \
'PDE(001) afc00000-b0000000 00400000 urw' \
' |-- PTE(00004) afffc000-b0000000 00004000 urw' \
'PDE(0e0) c0000000-f8000000 38000000 urw' \
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
'pgdir pass.'
run_test -prog 'yield' -check default_check \
'kernel_execve: pid = 2, name = "yield".' \
'Hello, I am process 2.' \
'Back in process 2, iteration 0.' \
'Back in process 2, iteration 1.' \
'Back in process 2, iteration 2.' \
'Back in process 2, iteration 3.' \
'Back in process 2, iteration 4.' \
'All done in process 2.' \
'yield pass.'
run_test -prog 'badarg' -check default_check \
'kernel_execve: pid = 2, name = "badarg".' \
'fork ok.' \
'badarg pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
pts=10
run_test -prog 'exit' -check default_check \
'kernel_execve: pid = 2, name = "exit".' \
'I am the parent. Forking the child...' \
'I am the parent, waiting now..' \
'I am the child.' \
- 'waitpid [0-9]+ ok\.' \
'exit pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
run_test -prog 'spin' -check default_check \
'kernel_execve: pid = 2, name = "spin".' \
'I am the parent. Forking the child...' \
'I am the parent. Running the child...' \
'I am the child. spinning ...' \
'I am the parent. Killing the child...' \
'kill returns 0' \
'wait returns 0' \
'spin may pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
run_test -prog 'waitkill' -check default_check \
'kernel_execve: pid = 2, name = "waitkill".' \
'wait child 1.' \
'child 2.' \
'child 1.' \
'kill parent ok.' \
'kill child1 ok.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'user panic at .*'
pts=15
run_test -prog 'forktest' -check default_check \
'kernel_execve: pid = 2, name = "forktest".' \
'I am child 31' \
'I am child 19' \
'I am child 13' \
'I am child 0' \
'forktest pass.' \
'all user-mode processes have quit.' \
'init check memory pass.' \
! - 'fork claimed to work [0-9]+ times!' \
! 'wait stopped early' \
! 'wait got too many' \
! - 'user panic at .*'
pts=10
run_test -prog 'forktree' -check default_check \
'kernel_execve: pid = 2, name = "forktree".' \
- '....: I am '\'''\' \
- '....: I am '\''0'\' \
- '....: I am '\'''\' \
- '....: I am '\''1'\' \
- '....: I am '\''0'\' \
- '....: I am '\''01'\' \
- '....: I am '\''00'\' \
- '....: I am '\''11'\' \
- '....: I am '\''10'\' \
- '....: I am '\''101'\' \
- '....: I am '\''100'\' \
- '....: I am '\''111'\' \
- '....: I am '\''110'\' \
- '....: I am '\''001'\' \
- '....: I am '\''000'\' \
- '....: I am '\''011'\' \
- '....: I am '\''010'\' \
- '....: I am '\''0101'\' \
- '....: I am '\''0100'\' \
- '....: I am '\''0111'\' \
- '....: I am '\''0110'\' \
- '....: I am '\''0001'\' \
- '....: I am '\''0000'\' \
- '....: I am '\''0011'\' \
- '....: I am '\''0010'\' \
- '....: I am '\''1101'\' \
- '....: I am '\''1100'\' \
- '....: I am '\''1111'\' \
- '....: I am '\''1110'\' \
- '....: I am '\''1001'\' \
- '....: I am '\''1000'\' \
- '....: I am '\''1011'\' \
- '....: I am '\''1010'\' \
'all user-mode processes have quit.' \
'init check memory pass.'
## print final-score
show_final