《操作系统》的实验代码。

348 行
5.7 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 GCCPREFIX)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>] [-Ddef...] [-check <check>] checkargs ...
  210. tag=
  211. check=check_regexps
  212. while true; do
  213. select=
  214. case $1 in
  215. -tag)
  216. select=`expr substr $1 2 ${#1}`
  217. eval $select='$2'
  218. ;;
  219. esac
  220. if [ -z "$select" ]; then
  221. break
  222. fi
  223. shift
  224. shift
  225. done
  226. defs=
  227. while expr "x$1" : "x-D.*" > /dev/null; do
  228. defs="DEFS+='$1' $defs"
  229. shift
  230. done
  231. if [ "x$1" = "x-check" ]; then
  232. check=$2
  233. shift
  234. shift
  235. fi
  236. $make $makeopts touch > /dev/null 2>&1
  237. build_run "$tag" "$defs"
  238. check_result 'check result' "$check" "$@"
  239. }
  240. quick_run() {
  241. # usage: quick_run <tag> [-Ddef...]
  242. tag="$1"
  243. shift
  244. defs=
  245. while expr "x$1" : "x-D.*" > /dev/null; do
  246. defs="DEFS+='$1' $defs"
  247. shift
  248. done
  249. $make $makeopts touch > /dev/null 2>&1
  250. build_run "$tag" "$defs"
  251. }
  252. quick_check() {
  253. # usage: quick_check <tag> checkargs ...
  254. tag="$1"
  255. shift
  256. check_result "$tag" check_regexps "$@"
  257. }
  258. ## kernel image
  259. osimg=$(make_print ucoreimg)
  260. ## set default qemu-options
  261. qemuopts="-hda $osimg"
  262. ## set break-function, default is readline
  263. brkfun=readline
  264. ## check now!!
  265. quick_run 'Check Output'
  266. pts=10
  267. quick_check 'check ring 0' \
  268. '0: @ring 0' \
  269. '0: cs = 8' \
  270. '0: ds = 10' \
  271. '0: es = 10' \
  272. '0: ss = 10'
  273. quick_check 'check switch to ring 3' \
  274. '+++ switch to user mode +++' \
  275. '1: @ring 3' \
  276. '1: cs = 1b' \
  277. '1: ds = 23' \
  278. '1: es = 23' \
  279. '1: ss = 23'
  280. quick_check 'check switch to ring 0' \
  281. '+++ switch to kernel mode +++' \
  282. '2: @ring 0' \
  283. '2: cs = 8' \
  284. '2: ds = 10' \
  285. '2: es = 10' \
  286. '2: ss = 10'
  287. quick_check 'check ticks' \
  288. '++ setup timer interrupts' \
  289. '100 ticks' \
  290. 'End of Test.'
  291. ## print final-score
  292. show_final