mit 6.828 lab1_exercise12_讲解

好难啊,去年做的时候就很难,难到直接不知道怎么出来的,现在能搞出来了。。。。。。

Exercise 12. Modify your stack backtrace function to display, for each eip, the function name, source file name, and line number corresponding to that eip.

In debuginfo_eip, where do __STAB_* come from? This question has a long answer; to help you to discover the answer, here are some things you might want to do:

look in the file kern/kernel.ld for __STAB_*
run objdump -h obj/kern/kernel
run objdump -G obj/kern/kernel
run gcc -pipe -nostdinc -O2 -fno-builtin -I. -MD -Wall -Wno-format -DJOS_KERNEL -gstabs -c -S kern/init.c, and look at init.s.
see if the bootloader loads the symbol table in memory as part of loading the kernel binary
Complete the implementation of debuginfo_eip by inserting the call to stab_binsearch to find the line number for an address.

Add a backtrace command to the kernel monitor, and extend your implementation of mon_backtrace to call debuginfo_eip and print a line for each stack frame of the form:

K> backtrace
Stack backtrace:
  ebp f010ff78  eip f01008ae  args 00000001 f010ff8c 00000000 f0110580 00000000
         kern/monitor.c:143: monitor+106
  ebp f010ffd8  eip f0100193  args 00000000 00001aac 00000660 00000000 00000000
         kern/init.c:49: i386_init+59
  ebp f010fff8  eip f010003d  args 00000000 00000000 0000ffff 10cf9a00 0000ffff
         kern/entry.S:70: <unknown>+0
K> 
Each line gives the file name and line within that file of the stack frame's eip, followed by the name of the function and the offset of the eip from the first instruction of the function (e.g., monitor+106 means the return eip is 106 bytes past the beginning of monitor).

Be sure to print the file and function names on a separate line, to avoid confusing the grading script.

Tip: printf format strings provide an easy, albeit obscure, way to print non-null-terminated strings like those in STABS tables. printf("%.*s", length, string) prints at most length characters of string. Take a look at the printf man page to find out why this works.

You may find that some functions are missing from the backtrace. For example, you will probably see a call to monitor() but not to runcmd(). This is because the compiler in-lines some function calls. Other optimizations may cause you to see unexpected line numbers. If you get rid of the -O2 from GNUMakefile, the backtraces may make more sense (but your kernel will run more slowly).

要完成好几个任务

  1. 找出__STAB_*都来自哪里
  2. 确定bootloader是否将符号表作为加载内核的一部分加载到内存里了
  3. 补全debuginfo_eip函数的功能,让这个函数能查找行号
  4. 补全mon_backtrace函数的功能,使得其能够得到像上面那样的输出

1. 找出__STAB_*都来自哪里

这个比较容易,按照要求中的提示打开kern/kernel.ld,包含__STAB_*的部分如下

	/* Include debugging information in kernel memory */
	.stab : {
		PROVIDE(__STAB_BEGIN__ = .);
		*(.stab);
		PROVIDE(__STAB_END__ = .);
		BYTE(0)		/* Force the linker to allocate space
				   for this section */
	}

可以看到这个begin和end分别是.stab段的开始和结束。

ld文件是链接器脚本文件,链接时就按照这个脚本文件将目标文件和库链接到一起。它的语法见链接脚本文件ld语法

2. 确定bootloader是否将符号表作为加载内核的一部分加载到内存里了

这个我没想出来,看别人的答案是使用objdump -h找到stabstr段的地址,gdb调试时直接用x命令打印stabstr位置处有没有信息,如果有信息,就是加载成功了。

3. 补全debuginfo_eip函数的功能,让这个函数能查找行号

一开始也是始终没想明白,后来使用

objdump -G obj/kern/kernel # -G, --stabs Display (in raw form) any STABS info in the file

打印出来所有的调试信息,看了很多遍,才明白过来。怀疑-G是综合把.stab和.stabstr的文件一起输出的,所以才会包含字符串什么的。

截取一小部分。

yichuan@ubuntu:~/6.828/lab$ objdump -G obj/kern/kernel

obj/kern/kernel:     file format elf32-i386

Contents of .stab section:

Symnum n_type n_othr n_desc n_value  n_strx String

-1     HdrSym 0      1241   000018fc 1     
0      SO     0      0      f0100000 1      {standard input}
1      SOL    0      0      f010000c 18     kern/entry.S
2      SLINE  0      44     f010000c 0      
3      SLINE  0      57     f0100015 0      
4      SLINE  0      58     f010001a 0      
5      SLINE  0      60     f010001d 0      
6      SLINE  0      61     f0100020 0      
7      SLINE  0      62     f0100025 0      
8      SLINE  0      67     f0100028 0      
9      SLINE  0      68     f010002d 0      
10     SLINE  0      74     f010002f 0      
11     SLINE  0      77     f0100034 0      
12     SLINE  0      80     f0100039 0      
13     SLINE  0      83     f010003e 0      
14     SO     0      2      f0100040 31     kern/entrypgdir.c
15     OPT    0      0      00000000 49     gcc2_compiled.

每个条目有7个参数

  • symnum:条目序号
  • n_type:条目类型,各个缩写的含义不知道是啥。
  • n_othr:一直都为空,不知为何
  • n_desc:后来才知道这是符号在文件中的行号
  • n_value:符号的地址。FUN类型的地址是绝对地址,而对其他类型,如果前面有出现过FUN,那么SLINE指的就是这个函数内部的偏移地址,是相对地址。例子中的SLINE类型等前面都没出现过FUN,所以都是绝对地址。
  • n_strx:字符串表的索引,指的是这个符号名字首字母在字符串表中的索引位置。比如{standard input}的n_strx为1,因为其首字母位置1,结尾是17,可能有一个终止符,所以kern/entry.S的n_strx为18。后面同理。
  • String:即符号的字符串名。

debuginfo_eip函数使用了结构体Eipdebuginfo,定义在kdebug.h

int
debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)

查看该结构体定义

struct Eipdebuginfo {
	const char *eip_file;		// Source code filename for EIP
	int eip_line;			// Source code linenumber for EIP

	const char *eip_fn_name;	// Name of function containing EIP
					//  - Note: not null terminated!
	int eip_fn_namelen;		// Length of function name
	uintptr_t eip_fn_addr;		// Address of start of function
	int eip_fn_narg;		// Number of function arguments
};

可以看到由当前eip能够得到这么多调试信息。其中有些已实现,而得到行号要我们来完成。

按照kdebug.c的写法,查找各个变量的含义,结合上下文,最后得到如下写法。

	stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);

	if (lline <= rline)	{
		info->eip_line = stabs[lline].n_desc;	
	}
	else return -1;

kdebug.c的注释里面提示已经很详细了。n_desc就是我们要的行号。

4. 补全mon_backtrace函数的功能,使得其能够得到像题目中那样的输出

有了kdebug的补全提示,这个写起来就很容易了。

int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
	uint32_t *ebp;
	struct Eipdebuginfo tinfo;
    ebp = (uint32_t *)read_ebp();

    cprintf("Stack backtrace:\r\n");

    while (ebp)
    {
        cprintf("  ebp %08x  eip %08x  args %08x %08x %08x %08x %08x\r\n", 
                ebp, ebp[1], ebp[2], ebp[3], ebp[4], ebp[5], ebp[6]);
		int res = debuginfo_eip(ebp[1], &tinfo);	//因为原型里指定的是指针,所以此处取地址
		if(res != 0)	cprintf("Error!\n");
		else {
			cprintf("\t\t%s:%d: %.*s+%u\n", tinfo.eip_file, tinfo.eip_line, tinfo.eip_fn_namelen, tinfo.eip_fn_name, ebp[1] - tinfo.eip_fn_addr);
		}
        ebp = (uint32_t *)*ebp;
    }

	return 0;
}

由于debuginfo_eip写的是如果正常运行就返回0,如果出错就返回负数,所以有这个条件判断。其他信息都已经在Eipdebuginfo结构体中包含,上面已说过。

注意输出格式符 %.*s,表示这个位置要对应两个变量,第一个是一个整型数,表示最大字符串位数,第二个是字符串。如果字符串长度超过位数限制,就只输出位数规定的字符数。

最后使用了减法,ebp[1] - tinfo.eip_fn_addr,因为这个位置输出的是在文件中的行号,所以要用当前的eip指向的位置减去所在函数的起始地址才可以。

为了得到运行结果看是否正确,要在适当的位置加断点。

之前的kernel.asm

	test_backtrace(5);
f01000c8:	c7 04 24 05 00 00 00 	movl   $0x5,(%esp)
f01000cf:	e8 6c ff ff ff       	call   f0100040 <test_backtrace>
f01000d4:	83 c4 10             	add    $0x10,%esp

	// Drop into the kernel monitor.
	while (1)
		monitor(NULL);
f01000d7:	83 ec 0c             	sub    $0xc,%esp
f01000da:	6a 00                	push   $0x0
f01000dc:	e8 37 07 00 00       	call   f0100818 <monitor>

我们写的函数被test_backtrace调用,运行结果(就是cprintf语句)会表现出正确性,所以尝试在0xf01000d7处加断点。

运行结果如下,可以使用make grade检测一下看看能得多少分。

yichuan@ubuntu:~/6.828/lab$ make grade
make clean
make[1]: Entering directory '/home/yichuan/6.828/lab'
rm -rf obj .gdbinit jos.in qemu.log
make[1]: Leaving directory '/home/yichuan/6.828/lab'
./grade-lab1 
make[1]: Entering directory '/home/yichuan/6.828/lab'
+ as kern/entry.S
+ cc kern/entrypgdir.c
sh: echo: I/O error
+ cc kern/init.c
+ cc kern/console.c
+ cc kern/monitor.c
+ cc kern/printf.c
+ cc kern/kdebug.c
+ cc lib/printfmt.c
+ cc lib/readline.c
+ cc lib/string.c
sh: echo: I/O error
+ ld obj/kern/kernel
ld: warning: section `.bss' type changed to PROGBITS
+ as boot/boot.S
+ cc -Os boot/main.c
+ ld boot/boot
boot block is 390 bytes (max 510)
+ mk obj/kern/kernel.img
make[1]: Leaving directory '/home/yichuan/6.828/lab'
running JOS: (0.6s) 
  printf: FAIL 
    AssertionError: ...
         leaving test_backtrace 4
         leaving test_backtrace 5
         Welcome to the JOS kernel monitor!
         Type 'help' for a list of commands.
         qemu: terminating on signal 15 from pid 21790
    MISSING '6828 decimal is 15254 octal!'
    
  backtrace count: OK 
  backtrace arguments: OK 
  backtrace symbols: OK 
  backtrace lines: OK 
Score: 30/50
GNUmakefile:200: recipe for target 'grade' failed
make: *** [grade] Error 1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值