《操作系统》的实验代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

556 lines
18 KiB

  1. #!/bin/sh
  2. verbose=false
  3. if [ "x$1" = "x-v" ]; then
  4. verbose=true
  5. out=/dev/stdout
  6. err=/dev/stderr
  7. else
  8. out=/dev/null
  9. err=/dev/null
  10. fi
  11. ## make & makeopts
  12. if gmake --version > /dev/null 2>&1; then
  13. make=gmake;
  14. else
  15. make=make;
  16. fi
  17. makeopts="--quiet --no-print-directory -j"
  18. make_print() {
  19. echo `$make $makeopts print-$1`
  20. }
  21. ## command tools
  22. awk='awk'
  23. bc='bc'
  24. date='date'
  25. grep='grep'
  26. rm='rm -f'
  27. sed='sed'
  28. ## symbol table
  29. sym_table='obj/kernel.sym'
  30. ## gdb & gdbopts
  31. gdb="$(make_print GDB)"
  32. gdbport='1234'
  33. gdb_in="$(make_print GRADE_GDB_IN)"
  34. ## qemu & qemuopts
  35. qemu="$(make_print qemu)"
  36. qemu_out="$(make_print GRADE_QEMU_OUT)"
  37. if $qemu -nographic -help | grep -q '^-gdb'; then
  38. qemugdb="-gdb tcp::$gdbport"
  39. else
  40. qemugdb="-s -p $gdbport"
  41. fi
  42. ## default variables
  43. default_timeout=30
  44. default_pts=5
  45. pts=5
  46. part=0
  47. part_pos=0
  48. total=0
  49. total_pos=0
  50. ## default functions
  51. update_score() {
  52. total=`expr $total + $part`
  53. total_pos=`expr $total_pos + $part_pos`
  54. part=0
  55. part_pos=0
  56. }
  57. get_time() {
  58. echo `$date +%s.%N 2> /dev/null`
  59. }
  60. show_part() {
  61. echo "Part $1 Score: $part/$part_pos"
  62. echo
  63. update_score
  64. }
  65. show_final() {
  66. update_score
  67. echo "Total Score: $total/$total_pos"
  68. if [ $total -lt $total_pos ]; then
  69. exit 1
  70. fi
  71. }
  72. show_time() {
  73. t1=$(get_time)
  74. time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null`
  75. echo "(${time}s)"
  76. }
  77. show_build_tag() {
  78. echo "$1:" | $awk '{printf "%-24s ", $0}'
  79. }
  80. show_check_tag() {
  81. echo "$1:" | $awk '{printf " -%-40s ", $0}'
  82. }
  83. show_msg() {
  84. echo $1
  85. shift
  86. if [ $# -gt 0 ]; then
  87. echo "$@" | awk '{printf " %s\n", $0}'
  88. echo
  89. fi
  90. }
  91. pass() {
  92. show_msg OK "$@"
  93. part=`expr $part + $pts`
  94. part_pos=`expr $part_pos + $pts`
  95. }
  96. fail() {
  97. show_msg WRONG "$@"
  98. part_pos=`expr $part_pos + $pts`
  99. }
  100. run_qemu() {
  101. # Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty,
  102. # wait until $brkfun is reached or $timeout expires, then kill QEMU
  103. qemuextra=
  104. if [ "$brkfun" ]; then
  105. qemuextra="-S $qemugdb"
  106. fi
  107. if [ -z "$timeout" ] || [ $timeout -le 0 ]; then
  108. timeout=$default_timeout;
  109. fi
  110. t0=$(get_time)
  111. (
  112. ulimit -t $timeout
  113. exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra
  114. ) > $out 2> $err &
  115. pid=$!
  116. # wait for QEMU to start
  117. sleep 1
  118. if [ -n "$brkfun" ]; then
  119. # find the address of the kernel $brkfun function
  120. brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'`
  121. (
  122. echo "target remote localhost:$gdbport"
  123. echo "break *0x$brkaddr"
  124. echo "continue"
  125. ) > $gdb_in
  126. $gdb -batch -nx -x $gdb_in > /dev/null 2>&1
  127. # make sure that QEMU is dead
  128. # on OS X, exiting gdb doesn't always exit qemu
  129. kill $pid > /dev/null 2>&1
  130. fi
  131. }
  132. build_run() {
  133. # usage: build_run <tag> <args>
  134. show_build_tag "$1"
  135. shift
  136. if $verbose; then
  137. echo "$make $@ ..."
  138. fi
  139. $make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err
  140. if [ $? -ne 0 ]; then
  141. echo $make $@ failed
  142. exit 1
  143. fi
  144. # now run qemu and save the output
  145. run_qemu
  146. show_time
  147. }
  148. check_result() {
  149. # usage: check_result <tag> <check> <check args...>
  150. show_check_tag "$1"
  151. shift
  152. # give qemu some time to run (for asynchronous mode)
  153. if [ ! -s $qemu_out ]; then
  154. sleep 4
  155. fi
  156. if [ ! -s $qemu_out ]; then
  157. fail > /dev/null
  158. echo 'no $qemu_out'
  159. else
  160. check=$1
  161. shift
  162. $check "$@"
  163. fi
  164. }
  165. check_regexps() {
  166. okay=yes
  167. not=0
  168. reg=0
  169. error=
  170. for i do
  171. if [ "x$i" = "x!" ]; then
  172. not=1
  173. elif [ "x$i" = "x-" ]; then
  174. reg=1
  175. else
  176. if [ $reg -ne 0 ]; then
  177. $grep '-E' "^$i\$" $qemu_out > /dev/null
  178. else
  179. $grep '-F' "$i" $qemu_out > /dev/null
  180. fi
  181. found=$(($? == 0))
  182. if [ $found -eq $not ]; then
  183. if [ $found -eq 0 ]; then
  184. msg="!! error: missing '$i'"
  185. else
  186. msg="!! error: got unexpected line '$i'"
  187. fi
  188. okay=no
  189. if [ -z "$error" ]; then
  190. error="$msg"
  191. else
  192. error="$error\n$msg"
  193. fi
  194. fi
  195. not=0
  196. reg=0
  197. fi
  198. done
  199. if [ "$okay" = "yes" ]; then
  200. pass
  201. else
  202. fail "$error"
  203. if $verbose; then
  204. exit 1
  205. fi
  206. fi
  207. }
  208. run_test() {
  209. # usage: run_test [-tag <tag>] [-prog <prog>] [-Ddef...] [-check <check>] checkargs ...
  210. tag=
  211. prog=
  212. check=check_regexps
  213. while true; do
  214. select=
  215. case $1 in
  216. -tag|-prog)
  217. select=`expr substr $1 2 ${#1}`
  218. eval $select='$2'
  219. ;;
  220. esac
  221. if [ -z "$select" ]; then
  222. break
  223. fi
  224. shift
  225. shift
  226. done
  227. defs=
  228. while expr "x$1" : "x-D.*" > /dev/null; do
  229. defs="DEFS+='$1' $defs"
  230. shift
  231. done
  232. if [ "x$1" = "x-check" ]; then
  233. check=$2
  234. shift
  235. shift
  236. fi
  237. if [ -z "$prog" ]; then
  238. $make $makeopts touch > /dev/null 2>&1
  239. args="$defs"
  240. else
  241. if [ -z "$tag" ]; then
  242. tag="$prog"
  243. fi
  244. args="build-$prog $defs"
  245. fi
  246. build_run "$tag" "$args"
  247. check_result 'check result' "$check" "$@"
  248. }
  249. quick_run() {
  250. # usage: quick_run <tag> [-Ddef...]
  251. tag="$1"
  252. shift
  253. defs=
  254. while expr "x$1" : "x-D.*" > /dev/null; do
  255. defs="DEFS+='$1' $defs"
  256. shift
  257. done
  258. $make $makeopts touch > /dev/null 2>&1
  259. build_run "$tag" "$defs"
  260. }
  261. quick_check() {
  262. # usage: quick_check <tag> checkargs ...
  263. tag="$1"
  264. shift
  265. check_result "$tag" check_regexps "$@"
  266. }
  267. ## kernel image
  268. osimg=$(make_print ucoreimg)
  269. ## swap image
  270. swapimg=$(make_print swapimg)
  271. ## set default qemu-options
  272. qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback"
  273. ## set break-function, default is readline
  274. brkfun=readline
  275. default_check() {
  276. pts=7
  277. check_regexps "$@"
  278. pts=3
  279. quick_check 'check output' \
  280. 'memory management: default_pmm_manager' \
  281. 'check_alloc_page() succeeded!' \
  282. 'check_pgdir() succeeded!' \
  283. 'check_boot_pgdir() succeeded!' \
  284. 'PDE(0e0) c0000000-f8000000 38000000 urw' \
  285. ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
  286. 'PDE(001) fac00000-fb000000 00400000 -rw' \
  287. ' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \
  288. ' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \
  289. 'check_slab() succeeded!' \
  290. 'check_vma_struct() succeeded!' \
  291. 'page fault at 0x00000100: K/W [no page found].' \
  292. 'check_pgfault() succeeded!' \
  293. 'check_vmm() succeeded.' \
  294. 'page fault at 0x00001000: K/W [no page found].' \
  295. 'page fault at 0x00002000: K/W [no page found].' \
  296. 'page fault at 0x00003000: K/W [no page found].' \
  297. 'page fault at 0x00004000: K/W [no page found].' \
  298. 'write Virt Page e in fifo_check_swap' \
  299. 'page fault at 0x00005000: K/W [no page found].' \
  300. 'page fault at 0x00001000: K/W [no page found]' \
  301. 'page fault at 0x00002000: K/W [no page found].' \
  302. 'page fault at 0x00003000: K/W [no page found].' \
  303. 'page fault at 0x00004000: K/W [no page found].' \
  304. 'check_swap() succeeded!' \
  305. '++ setup timer interrupts'
  306. }
  307. ## check now!!
  308. run_test -prog 'badsegment' -check default_check \
  309. 'kernel_execve: pid = 2, name = "badsegment".' \
  310. - 'trapframe at 0xc.......' \
  311. 'trap 0x0000000d General Protection' \
  312. ' err 0x00000028' \
  313. - ' eip 0x008.....' \
  314. - ' esp 0xaff.....' \
  315. ' cs 0x----001b' \
  316. ' ss 0x----0023' \
  317. ! - 'user panic at .*'
  318. run_test -prog 'divzero' -check default_check \
  319. 'kernel_execve: pid = 2, name = "divzero".' \
  320. - 'trapframe at 0xc.......' \
  321. 'trap 0x00000000 Divide error' \
  322. - ' eip 0x008.....' \
  323. - ' esp 0xaff.....' \
  324. ' cs 0x----001b' \
  325. ' ss 0x----0023' \
  326. ! - 'user panic at .*'
  327. run_test -prog 'softint' -check default_check \
  328. 'kernel_execve: pid = 2, name = "softint".' \
  329. - 'trapframe at 0xc.......' \
  330. 'trap 0x0000000d General Protection' \
  331. ' err 0x00000072' \
  332. - ' eip 0x008.....' \
  333. - ' esp 0xaff.....' \
  334. ' cs 0x----001b' \
  335. ' ss 0x----0023' \
  336. ! - 'user panic at .*'
  337. pts=10
  338. run_test -prog 'faultread' -check default_check \
  339. 'kernel_execve: pid = 2, name = "faultread".' \
  340. - 'trapframe at 0xc.......' \
  341. 'trap 0x0000000e Page Fault' \
  342. ' err 0x00000004' \
  343. - ' eip 0x008.....' \
  344. ! - 'user panic at .*'
  345. run_test -prog 'faultreadkernel' -check default_check \
  346. 'kernel_execve: pid = 2, name = "faultreadkernel".' \
  347. - 'trapframe at 0xc.......' \
  348. 'trap 0x0000000e Page Fault' \
  349. ' err 0x00000005' \
  350. - ' eip 0x008.....' \
  351. ! - 'user panic at .*'
  352. run_test -prog 'hello' -check default_check \
  353. 'kernel_execve: pid = 2, name = "hello".' \
  354. 'Hello world!!.' \
  355. 'I am process 2.' \
  356. 'hello pass.'
  357. run_test -prog 'testbss' -check default_check \
  358. 'kernel_execve: pid = 2, name = "testbss".' \
  359. 'Making sure bss works right...' \
  360. 'Yes, good. Now doing a wild write off the end...' \
  361. 'testbss may pass.' \
  362. - 'trapframe at 0xc.......' \
  363. 'trap 0x0000000e Page Fault' \
  364. ' err 0x00000006' \
  365. - ' eip 0x008.....' \
  366. 'killed by kernel.' \
  367. ! - 'user panic at .*'
  368. run_test -prog 'pgdir' -check default_check \
  369. 'kernel_execve: pid = 2, name = "pgdir".' \
  370. 'I am 2, print pgdir.' \
  371. 'PDE(001) 00800000-00c00000 00400000 urw' \
  372. ' |-- PTE(00002) 00800000-00802000 00002000 ur-' \
  373. ' |-- PTE(00001) 00802000-00803000 00001000 urw' \
  374. 'PDE(001) afc00000-b0000000 00400000 urw' \
  375. ' |-- PTE(00004) afffc000-b0000000 00004000 urw' \
  376. 'PDE(0e0) c0000000-f8000000 38000000 urw' \
  377. ' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
  378. 'pgdir pass.'
  379. run_test -prog 'yield' -check default_check \
  380. 'kernel_execve: pid = 2, name = "yield".' \
  381. 'Hello, I am process 2.' \
  382. 'Back in process 2, iteration 0.' \
  383. 'Back in process 2, iteration 1.' \
  384. 'Back in process 2, iteration 2.' \
  385. 'Back in process 2, iteration 3.' \
  386. 'Back in process 2, iteration 4.' \
  387. 'All done in process 2.' \
  388. 'yield pass.'
  389. run_test -prog 'badarg' -check default_check \
  390. 'kernel_execve: pid = 2, name = "badarg".' \
  391. 'fork ok.' \
  392. 'badarg pass.' \
  393. 'all user-mode processes have quit.' \
  394. 'init check memory pass.' \
  395. ! - 'user panic at .*'
  396. pts=10
  397. run_test -prog 'exit' -check default_check \
  398. 'kernel_execve: pid = 2, name = "exit".' \
  399. 'I am the parent. Forking the child...' \
  400. 'I am the parent, waiting now..' \
  401. 'I am the child.' \
  402. - 'waitpid [0-9]+ ok\.' \
  403. 'exit pass.' \
  404. 'all user-mode processes have quit.' \
  405. 'init check memory pass.' \
  406. ! - 'user panic at .*'
  407. run_test -prog 'spin' -check default_check \
  408. 'kernel_execve: pid = 2, name = "spin".' \
  409. 'I am the parent. Forking the child...' \
  410. 'I am the parent. Running the child...' \
  411. 'I am the child. spinning ...' \
  412. 'I am the parent. Killing the child...' \
  413. 'kill returns 0' \
  414. 'wait returns 0' \
  415. 'spin may pass.' \
  416. 'all user-mode processes have quit.' \
  417. 'init check memory pass.' \
  418. ! - 'user panic at .*'
  419. run_test -prog 'waitkill' -check default_check \
  420. 'kernel_execve: pid = 2, name = "waitkill".' \
  421. 'wait child 1.' \
  422. 'child 2.' \
  423. 'child 1.' \
  424. 'kill parent ok.' \
  425. 'kill child1 ok.' \
  426. 'all user-mode processes have quit.' \
  427. 'init check memory pass.' \
  428. ! - 'user panic at .*'
  429. pts=15
  430. run_test -prog 'forktest' -check default_check \
  431. 'kernel_execve: pid = 2, name = "forktest".' \
  432. 'I am child 31' \
  433. 'I am child 19' \
  434. 'I am child 13' \
  435. 'I am child 0' \
  436. 'forktest pass.' \
  437. 'all user-mode processes have quit.' \
  438. 'init check memory pass.' \
  439. ! - 'fork claimed to work [0-9]+ times!' \
  440. ! 'wait stopped early' \
  441. ! 'wait got too many' \
  442. ! - 'user panic at .*'
  443. pts=10
  444. run_test -prog 'forktree' -check default_check \
  445. 'kernel_execve: pid = 2, name = "forktree".' \
  446. - '....: I am '\'''\' \
  447. - '....: I am '\''0'\' \
  448. - '....: I am '\'''\' \
  449. - '....: I am '\''1'\' \
  450. - '....: I am '\''0'\' \
  451. - '....: I am '\''01'\' \
  452. - '....: I am '\''00'\' \
  453. - '....: I am '\''11'\' \
  454. - '....: I am '\''10'\' \
  455. - '....: I am '\''101'\' \
  456. - '....: I am '\''100'\' \
  457. - '....: I am '\''111'\' \
  458. - '....: I am '\''110'\' \
  459. - '....: I am '\''001'\' \
  460. - '....: I am '\''000'\' \
  461. - '....: I am '\''011'\' \
  462. - '....: I am '\''010'\' \
  463. - '....: I am '\''0101'\' \
  464. - '....: I am '\''0100'\' \
  465. - '....: I am '\''0111'\' \
  466. - '....: I am '\''0110'\' \
  467. - '....: I am '\''0001'\' \
  468. - '....: I am '\''0000'\' \
  469. - '....: I am '\''0011'\' \
  470. - '....: I am '\''0010'\' \
  471. - '....: I am '\''1101'\' \
  472. - '....: I am '\''1100'\' \
  473. - '....: I am '\''1111'\' \
  474. - '....: I am '\''1110'\' \
  475. - '....: I am '\''1001'\' \
  476. - '....: I am '\''1000'\' \
  477. - '....: I am '\''1011'\' \
  478. - '....: I am '\''1010'\' \
  479. 'all user-mode processes have quit.' \
  480. 'init check memory pass.'
  481. ## print final-score
  482. show_final