这个是一个能够读取标准输入的程序
/*
1. 读取这个字符
if (char 可显示) {
记录下来
} else (删除字符) {
打印删除字符
} else if (换行或者回车) {
打印,不做记录,然后记录结束
}
*/
char *
readline(const char *prompt) {
if (prompt != NULL) {
cprintf("%s", prompt);
}
int i = 0, c;
while (1) {
c = getchar();
if (c < 0) {
return NULL;
}//否则c > 0
else if (c >= ' ' && i < BUFSIZE - 1) {//将 c 保存下来 ,如果是可显示的字符的话
cputchar(c);
buf[i ++] = c;
}
else if (c == '\b' && i > 0) {//退格符号
cputchar(c);
i --;
}
else if (c == '\n' || c == '\r') {//遇到回车或者换行符就退出
cputchar(c);
buf[i] = '\0';
return buf;
}
}
}
对command的定义
struct command {
const char *name;
const char *desc;
// return -1 to force monitor to exit
int(*func)(int argc, char **argv, struct trapframe *tf);
};
static struct command commands[] = {
{"help", "Display this list of commands.", mon_help},
{"kerninfo", "Display information about the kernel.", mon_kerninfo},
{"backtrace", "Print backtrace of stack frame.", mon_backtrace},
};
上面的这个结构体定义的很有意思
struct command {
const char *name;
const char *desc;
// return -1 to force monitor to exit
int(*func)(int argc, char **argv, struct trapframe *tf);
};
从标准输入取出来读取的字符串,然后和commands的结构体的name成员比较,成功则调用其func函数
for (i = 0; i < NCOMMANDS; i ++) {
if (strcmp(commands[i].name, argv[0]) == 0) {
return commands[i].func(argc - 1, argv + 1, tf);
}
}
backtrace打印堆栈的情况
K> backtrace
ebp:0x00007ae8 eip:0x0010086b args:0x00000000 0x00000001 0x00007b68 0x00100a58
kern/debug/kdebug.c:295: print_stackframe+21
ebp:0x00007af8 eip:0x00100b49 args:0x00000000 0x00007b1c 0x00000000 0x0000000a
kern/debug/monitor.c:121: mon_backtrace+10
ebp:0x00007b68 eip:0x00100a58 args:0x00108560 0x00000000 0x00007b98 0x00101314
kern/debug/monitor.c:71: runcmd+137
ebp:0x00007b98 eip:0x00100ac5 args:0x00000000 0x00101eec 0x0000065c 0x00000000
kern/debug/monitor.c:88: monitor+68
ebp:0x00007bc8 eip:0x0010005c args:0x00000000 0x00000000 0x00000000 0x00010094
kern/init/init.c:25: kern_init+91
ebp:0x00007bf8 eip:0x00007d47 args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8
<unknow>: -- 0x00007d46 --
总结一下,大概实现的过程
- 读取ebp的值,读取eip的值
- ebp的地方上一个函数的堆栈,ebp+4的地方是当前函数的返回地址
for (i = 0; ebp != 0 && i < STACKFRAME_DEPTH; i ++) {
cprintf("ebp:0x%08x eip:0x%08x args:", ebp, eip);
/*现在堆栈是 esp, 返回地址*/
uint32_t *args = (uint32_t *)ebp + 2;//这个地方跳过esp,和调用print_stackframe的返回地址
for (j = 0; j < 4; j ++) {
cprintf("0x%08x ", args[j]);
}
cprintf("\n");
print_debuginfo(eip - 1);
eip = ((uint32_t *)ebp)[1];//这个是调用print_stackframe的返回地址
ebp = ((uint32_t *)ebp)[0];//这个是上一个堆栈
}
我觉得完全可以自己解析函数调用堆栈
http://hutaow.com/blog/2013/10/15/dump-stack/