1. how to use GDB Qemu
1. compile and run gdb qemu make make qemu-gdb 2. in another window cd to kernel dir gdb-multiarch kernel target remote localhost:25000
1.1. break at system call
(gdb) b syscall Breakpoint 1 at 0x80001c7a: file kernel/syscall.c, line 160. (gdb) c Continuing. Thread 3 hit Breakpoint 1, syscall () at kernel/syscall.c:160 (gdb) bt #0 syscall () at kernel/syscall.c:160 #1 0x0000000080001a36 in usertrap () at kernel/trap.c:67 #2 0x0000000000000000 in ?? () (gdb) list 158 void 159 syscall(void) 160 {
Looking at the backtrace output, which function called syscall?
(gdb) frame 1 #1 0x0000000080001a36 in usertrap () at kernel/trap.c:67 36 void 37 usertrap(void) 38 { 53 if(r_scause() == 8){ 54 // system call // ... 67 syscall();
Type n a few times to step past struct proc *p = myproc(); Once past this statement, type p /x *p, which prints the current process’s proc struct (see kernel/proc.h>) in hex. What is the value of p->trapframe->a7 and what does that value represent? (Hint: look user/initcode.S, the first user program xv6 starts.)
(gdb) n 162 struct proc *p = myproc(); (gdb) n 164 num = p->trapframe->a7; (gdb) p /x *p $2 = {lock = {locked = 0x0, name = 0x800071c8, cpu = 0x0}, state = 0x4, chan = 0x0, killed = 0x0, xstate = 0x0, pid = 0x1, parent = 0x0, kstack = 0x3fffffd000, sz = 0x1000, pagetable = 0x87f55000, trapframe = 0x87f56000, context = {ra = 0x800012b6, sp = 0x3fffffde80, s0 = 0x3fffffdeb0, s1 = 0x8000a810, s2 = 0x8000a3e0, s3 = 0x1, s4 = 0x80010898, s5 = 0x3, s6 = 0x8001b6b0, s7 = 0x1, s8 = 0x8001b7d8, s9 = 0x4, s10 = 0x0, s11 = 0x0}, ofile = {0x0 <repeats 16 times>}, cwd = 0x80018b20, name = { 0x69, 0x6e, 0x69, 0x74, 0x63, 0x6f, 0x64, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, trace_mask = 0x0} (gdb) p p->trapframe->a7 $3 = 7
p->trapframe->a7 is used to store the system call number defined in syscall.h
#define SYS_exec 7
The processor is running in supervisor mode, and we can print privileged registers such as sstatus. What was the previous mode that the CPU was in?
(gdb) p /x $sstatus $4 = 0x200000022 0x200000022 = 0b10_0000_0000_0000_0000_0000_0000_0010_0010 ^ (bit 8 is 0) Bit 8: SPP (Supervisor Previous Privilege) 0 = Previous mode was User 1 = Previous mode was Supervisor
The xv6 kernel code contains consistency checks whose failure causes the kernel to panic; you may find that your kernel modifications cause panics. For example, replace the statement num = p->trapframe->a7; with num = * (int *) 0; at the beginning of syscall, run make qemu, and you will see something similar to:
158 void 159 syscall(void) 160 { 161 int num; 162 struct proc *p = myproc(); 163 164 // num = p->trapframe->a7; 165 num = * (int *) 0; scause=0xd sepc=0x80001c8c stval=0x0 4302 // num = p->trapframe->a7; 4303 num = * (int *) 0; 4304 80001c8c: 00002903 lw s2,0(zero) # 0 <_entry-0x80000000>
2. system call tracing
trace takes a mask (which contains system call bits) and triggers the kernel print messages once the system call is hit. The process id, system call name and return value will be printed out.
Core idea:
- syscall is the kernel entry point for every system call, we can print the message there
- where to get the system call argument for trace?
When system call is made, trap will be generated to store same the user register which contains user arguments into trapframe. The function argint is provided by xv6 to retrive the n the argument as int.