6.828: Operating System Engineering (18)

ええと、inc/x86.h とか kern/monitor.c とか書いてあります。mon_backtrace という手続きを実装せぇとか何とか。

Exercise 11

Implement the backtrace function as specified above. Use the same format as in the example, since otherwise the grading script will be confused. When you think you have it working right, run make grade to see if its output conforms to what our grading script expects, and fix it if it doesn't. After you have handed in your Lab 1 code, you are welcome to change the output format of the backtrace function any way you like.

引数と思われるソレを 5 つ出力すれば良いのか。あとは read_ebp で取得できる base pointer をどう操作するかがカギ。
void * で受けて足し算したら大丈夫かなぁ。実装として以下が提示されてるんですが、

int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
	// Your code here.
	return 0;
}

呼び出し元は以下な形なので、

// Test the stack backtrace function (lab 1 only)
void
test_backtrace(int x)
{
	cprintf("entering test_backtrace %d\n", x);
	if (x > 0)
		test_backtrace(x-1);
	else
		mon_backtrace(0, 0, 0);
	cprintf("leaving test_backtrace %d\n", x);
}

引数は現時点では気にしないことにします。
あと、昨晩エントリによればいっちゃん最初のフレームの base pointer は 0x0 だったので、これを条件にループすれば良いはず。
で、実装を盛り込みつつ試験してみたのですが

  • void * にしてコンパイルエラー
  • ポインタ演算色々ナチュラル
    • 戻り番地の位置とか引数の位置とか

ええと、試験の最初から控えを。

(gdb) b i386_init
Breakpoint 1 at 0xf010013a: file kern/init.c, line 24.
(gdb) c
Continuing.
The target architecture is assumed to be i386
=> 0xf010013a <i386_init>:      push   %ebp

Breakpoint 1, i386_init () at kern/init.c:24
24      {
(gdb) n
=> 0xf0100140 <i386_init+6>:    mov    $0xf0110980,%eax
i386_init () at kern/init.c:30
30              memset(edata, 0, end - edata);
(gdb) n
=> 0xf0100162 <i386_init+40>:   call   0xf01004aa <cons_init>
34              cons_init();
(gdb) n
=> 0xf0100167 <i386_init+45>:   movl   $0x1aac,0x4(%esp)
36              cprintf("6828 decimal is %o octal!\n", 6828);
(gdb) n
=> 0xf010017b <i386_init+65>:   movl   $0x5,(%esp)
45              test_backtrace(5);
(gdb) 

戻り番地はこの次、になるはず。

(gdb) si
=> 0xf0100182 <i386_init+72>:   call   0xf01000dd <test_backtrace>
0xf0100182      45              test_backtrace(5);
(gdb) si
=> 0xf01000dd <test_backtrace>: push   %ebp
test_backtrace (x=5) at kern/init.c:13
13      {
(gdb) 

あら 0xf0100182 の次かな?
レジスタの値を確認してみます。

(gdb) info registers
eax            0x0      0
ecx            0x3d5    981
edx            0x3d5    981
ebx            0x10094  65684
esp            0xf010ffdc       0xf010ffdc
ebp            0xf010fff8       0xf010fff8
esi            0x10094  65684
edi            0x0      0
eip            0xf01000dd       0xf01000dd <test_backtrace>
eflags         0x82     [ SF ]
cs             0x8      8
ss             0x10     16
ds             0x10     16
es             0x10     16
fs             0x10     16
gs             0x10     16
(gdb) 

ええとまだ ebp push した直後だったりするのでもう少し進めます。

(gdb) si
=> 0xf01000de <test_backtrace+1>:       mov    %esp,%ebp
0xf01000de      13      {
(gdb) si
=> 0xf01000e0 <test_backtrace+3>:       push   %ebx
0xf01000e0      13      {
(gdb) si
=> 0xf01000e1 <test_backtrace+4>:       sub    $0x14,%esp
0xf01000e1      13      {
(gdb) si
=> 0xf01000e4 <test_backtrace+7>:       mov    0x8(%ebp),%ebx
0xf01000e4      13      {
(gdb) si
=> 0xf01000e7 <test_backtrace+10>:      mov    %ebx,0x4(%esp)
14              cprintf("entering test_backtrace %d\n", x);
(gdb) 

ここで確認か。

(gdb) info registers
eax            0x0      0
ecx            0x3d5    981
edx            0x3d5    981
ebx            0x5      5
esp            0xf010ffc0       0xf010ffc0
ebp            0xf010ffd8       0xf010ffd8
esi            0x10094  65684
edi            0x0      0
eip            0xf01000e7       0xf01000e7 <test_backtrace+10>
eflags         0x86     [ PF SF ]
cs             0x8      8
ss             0x10     16
ds             0x10     16
es             0x10     16
fs             0x10     16
gs             0x10     16
(gdb) 

で、戻り番地確認。

(gdb) x/x 0xf010ffdc
0xf010ffdc <bootstack+32732>:   0xf0100187
(gdb) 

0xf0100182 の次だろうと思われます。で、間違えて n タタいてしまったので流れてしまいました。コンソール出力が以下です。

entering test_backtrace 5
entering test_backtrace 4
entering test_backtrace 3
entering test_backtrace 2
entering test_backtrace 1
entering test_backtrace 0
ebp f010ff18  eip f0100124  args 0 0 0 0 f01009a5
ebp f010ff38  eip f0100106  args 0 1 f010ff78 0 f01009a5
ebp f010ff58  eip f0100106  args 1 2 f010ff98 0 f01009a5
ebp f010ff78  eip f0100106  args 2 3 f010ffb8 0 f01009a5
ebp f010ff98  eip f0100106  args 3 4 0 0 0
ebp f010ffb8  eip f0100106  args 4 5 0 10094 10094
ebp f010ffd8  eip f0100187  args 5 1aac 660 0 0
ebp f010fff8  eip f010003d  args 0 0 ffff 10cf9a00 ffff
leaving test_backtrace 0
leaving test_backtrace 1
leaving test_backtrace 2
leaving test_backtrace 3
leaving test_backtrace 4
leaving test_backtrace 5

これ、一体どーゆー意味だ?
あ、そーか。いっちゃん下が i386_init の stack frame で、上から二番目までが test_backtrace の stack frame になるのか。いっちゃん上が mon_backtrace ということであればなんとなく話は合ってますね。

make grade

以下。

Printf: WRONG (1.3s)
Backtrace:
   Count WRONG (1.4s)
   Args WRONG, (0
0
1
2
3
4
5
0) (1.4s)
   Symbols WRONG, () (1.4s)
Score: 0/50

ローボコン、れいてん、という気分ですorz

grade-function.sh

見てみたのですが、引数の出力の編集がアレだったり空白文字が一つ多かったりしてる模様です。ただ、シンボル出力せい、ってのは直接の記述は無かったんですが、これがアメリカ式の課題の出しかたですかそうですか。
それが次の Exercise 12 になるのかなぁ。というかどう考えても空白文字が 2 文字なんだけどなぁ。
実装以下です。

int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
	// Your code here.
	int i;
	unsigned int ebp_ptr = read_ebp();

	while(ebp_ptr) {

/* 
   ebp f0109ed8  eip f01000d6  args 00000000 00000000 f0100058 f0109f28 00000061
*/

		cprintf("ebp %x  eip %x  args ", ebp_ptr, *((int *)ebp_ptr + 1));

		for(i = 0; i < 5; i++) {
			cprintf("%x ", *((int *)ebp_ptr + i + 2));
		}

		cprintf("\n");

		ebp_ptr = *(int *)ebp_ptr;
	}

	return 0;
}

ちょっと色々な意味で微妙に気にいらないし、0 点だし。あと、採点スクリプト微妙。