Lab 3: Tracing System Calls
As we saw in the previous lab, strace can be quite useful to diagnose what programs are doing on Linux (at least as far as what system calls they are using). In this lab, we’ll add similar functionality to your OS.
Adding strace to your OS
Given how nice strace functionality is to have, let’s build it!
- Add a flag (perhaps an int to indicate on/off status) to the
processstruct so that we can turn tracing on for particular processes. To do this, editkernel/proc.h.- You’ll also want to update
freeproc()inkernel/proc.cso that the flag gets cleared when the process is freed.
- You’ll also want to update
- Add a system call that turns tracing on. HINT: You can access the flag you added in the previous step from a system call with the
myproc()function. - Every time a system call is used, print tracing information. To start with, this can just be the system call number and the value of the first argument expressed as an integer.
- Every time a system call returns, print its return value.
- Since tracing everything will be information overload, add a user space program called
tracerthat forks a child process, turns tracing on for it with your system call, and then executes whatever command line options were passed in. For example, if I run/tracer /cat README.mdthen it will run/cat README.mdwith tracing enabled. This might sound a bit complicated, but it’s not a whole lot of code.- The
fork.cexample should be useful here.
- The
- Implement full tracing information: system call names, argument names and values, and return values. There are some tips on how to do this below.
Here’s what a representative run of what tracer would produce:
$ /tracer /cat README.md
[4|tracer] strace_on() = 0
[4|cat] exec(pathname = 0x0000000000003fe0, argv = 0x0000000000003fc0) = 2
[4|cat] open(pathname = README.md, mode = 0) = 3
[4|cat] read(fd = 3, buf = 0x0000000000001010, count = 512) = 75
# FogOS
Welcome to FogOS! Voted #1 Fog-themed OS in all of San Francisco!
[4|cat] write(fd = 1, buf = 0x0000000000001010, count = 75) = 75
[4|cat] read(fd = 3, buf = 0x0000000000001010, count = 512) = 0
[4|cat] close(fd = 3) = 0
$ /tracer /echo bye
[6|tracer] strace_on() = 0
[6|echo] exec(pathname = 0x0000000000003fe0, argv = 0x0000000000003fc0) = 2
bye[6|echo] write(fd = 1, buf = 0x0000000000003fe0, count = 3) = 3
[6|echo] write(fd = 1, buf = 0x0000000000000858, count = 1) = 1
$ /tracer ls /doesnotexist
[8|tracer] strace_on() = 0
[8|ls] exec(pathname = 0x0000000000003fe0, argv = 0x0000000000003fc0) = 2
[8|ls] open(pathname = /doesnotexist, mode = 0) = -1
( ... output snipped ... )
You can observe that the tracing output happens after the system call, which makes it easy for us to include the return values.
While we could modify each system call to support tracing, that would be quite tedious. Instead, we need to understand how system calls actually work, and with a bit of shell scripting and code generation, we can automate much of the tracing process. First, figure out how the transition happens from user space to kernel space when a system call is made, and add the steps and a small explanation to your syscalls.txt log. Ultimately you’ll end up in syscall.c, and this is the file we will need to modify — specifically, the syscall function.
In syscall, the system call number is used to look up the appropriate function pointer for handling the call. We can determine the name of the system call from its number, and given its name, we can get the user space function’s name, arguments, and return types.
Check out generate-traces.sh to see how this information can be extracted, and modify the script so that it will produce a new header file called kernel/strace.h that contains helper functions to print the traces. This script will run during the build process and auto-generate kernel/strace.h any time the list of user space system calls changes. This may seem a bit daunting at first, but the process can be summarized as:
- Modify
user/user.hto include more detail about the system call arguments. You can look at the Linuxmanpages for inspiration on what to name the arguments, but make sure they make sense in the context of FogOS. There are a few examples near the top ofgenerate-traces.sh. - Run
generate-traces.shto get an idea of what it does. You will need to modify the script and the code it generates to implement a function:strace(struct proc *p, int syscall_num, int return_value)- This function is responsible for taking a system call number and producing the user-friendly output shown in the example above.
- Add a new Makefile recipe that builds
kernel/strace.husinggenerate-traces.sh. It should depend on theuser/user.hfile so that any changes to the user-facing system calls will trigger a rebuild ofstrace.h.- Hint: You can use output redirection (
generate-traces.sh > some_file) to take whatgenerate-traces.shproduces and write it to a file. - You will include this header in
syscall.c.
- Hint: You can use output redirection (
- Modify the
syscallfunction inkernel/syscall.cto call yourstracefunction if tracing is enabled for the given process. Be careful here: the return values of the system calls are stored inp->trapframe->a0, which is also where the first argument is stored. Make sure you don’t overwrite the argument before printing out its value! - Test
tracerand confirm the system call information is correct.
Phew! Hopefully that was fun. You got to modify the kernel, generate C code instead of writing it yourself, experiment with bash scripting, and update a Makefile. All in a day’s work!
Bonus: Rick Ropen()
In CS 326, we do not acknowledge the existence of programming languages other than C. Modify the open system call so that whenever a user tries to open a file that ends in .java (or some other programming language of your choice), the open call is redirected to opening /roll.txt instead. The contents of the file should be:
We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand
Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you
Context: Exhibit A, Exhibit B.
Now whenever someone tries to open a .java file, they’ll see the contents of roll.txt instead:
cat AbstractFactoryPatternBeanAdapterAdapterInterface.java
We're no strangers to love
...
(You get the idea)
Grading and Submission
To receive 75% credit:
- Add a system call to turn tracing on for a given process.
- Implement basic tracing (system call numbers, first argument, and return values)
To receive full credit for this lab:
- Complete all previous requirements
- Add your explanation of how system calls transition from user space to kernel space to
syscalls.txt - Update the Makefile to auto-generate
kernel/strace.hwith tracing information for each system call and helper functions to display the information. - Implement the
tracercommand line utility
To receive 105% credit for this lab:
- Complete all previous requirements
- Complete the Rick Ropen() bonus task
Once you are finished, check your changes into your OS repo. Then have a member of the course staff take a look at your lab to check it.
Check off procedure:
- Show the contents of
kernel/strace.h,kernel/syscall.c, and show your Makefile modifications - Demonstrate running your
tracerprogram with thecat,echo, andlscommands above- Run
echoandlswithout tracing so we can compare their output
- Run