Lab 4: Tracing System Calls
The strace
utility allows us to trace system calls on running programs. We can learn quite a bit about a program just by inspecting its system calls.
Here are a few example usages of strace
:
# Trace a run of 'ls':
$ strace ls
# Trace only file-related system calls
$ strace -e trace=file ls
# Get a nice summary of unique system calls used
$ strace -c ls
# Search for a specific system call (stat in this case):
$ strace ls 2>&1 | grep '^stat'
# Note that we search the start of the string (^) because the system call's
# name comes first, followed by its parameters and return value.
When you run a command, such as strace cat
, each system call will be printed interactively to your terminal. So the general workflow is: run strace on a command, which will then print a list of system calls. You can run strace
on any binary file; if you compile your own C code, strace a.out
will display the system calls being used by your code (most likely the calls are invoked by the C library, not your code directly).
Part I: Tracing System Calls (on Linux)
For the first part of this lab, you will trace several programs and record the results on a Linux machine. You will need to write about what happens below, so
you should create a docs
directory inside your OS repo and create a new file there called syscalls.txt
.
- First, run a trace on
ls
. Record all of the unique system calls (just their names) used byls
. To avoid doing a lot of tedious work, automate most of this with a shell pipeline or command line flag (see theman
page for strace).
(list the syscalls here as a bulleted list, use ‘*’ before each system call)
-
How many unique system calls are in your list?
-
Next, trace several commands you already know and look for new system calls that you haven’t seen before (or look up some new commands if you’d like). List any new system calls you find with each command.
Command: cat /etc/passwd
New system calls:
* mprotect64
(next command goes here)
- Take a look at the system calls that your OS supports and compare them with the Linux system calls. Which calls overlap, and which do not?
Part II: Adding strace to your OS
Given how nice strace functionality is to have, let’s add it to our own OS.
- Add a flag to the
process
struct so that we can turn tracing on for particular processes. To do this, editkernel/proc.h
. - 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. There are some example macros below to make this easier.
- Since there are a lot of system calls, you can choose to trace everything in either
sysproc.c
orsysfile.c
. - You will need to modify each system call, so be careful! Some of them will exit early if their arguments are invalid; you should still trace them, so you might need to rework their error checking logic.
- Since there are a lot of system calls, you can choose to trace everything in either
- Every time a system call returns, print its return value.
- Add a user space program called
tracer
that simply forks a child process, turns tracing on for it, and then executes whatever command line options were passed in. For example, if I run/tracer /cat README.md
then it will run/cat README.md
with tracing enabled.
Here are a few macros to print the tracing information:
/**
* @file Macros to make printing system call traces easier.
*/
#ifndef STRACE_H
#define STRACE_H
// Macro to print an strace for a system call with no arguments
#define STRACE() \
do { if (myproc()->strace) printf("[%d] [%s] %s()\n", \
myproc()->pid, myproc()->name, __func__); } while (0)
// Macro to print an strace for a system call with arguments.
// Formatting is the same as with printf, e.g.:
// STRACE_ARGS("path = %s, omode = %d", path, omode);
#define STRACE_ARGS(fmt, ...) \
do { if (myproc()->strace) printf("[%d] [%s] %s(): " fmt "\n", \
myproc()->pid, myproc()->name, __func__, __VA_ARGS__); } while (0)
// Macro to print an strace for the return value from a system call
// Call this after the system call returns, e.g.:
// p->trapframe->a0 = syscalls[num]();
// STRACE_RETURN(num);
#define STRACE_RETURN(num) \
do { if (myproc()->strace) printf("[%d] [syscall #%d] return: %d\n", \
myproc()->pid, num, myproc()->trapframe->a0); } while (0)
#endif
You can add them to kernel/strace.h
and then include strace.h
whenever you need to print a trace. Note that I’m assuming the flag in the process
struct is called strace
– you may need to modify these if yours is different.
Here is a demo run of tracer
:
$ /tracer /cat README.md
[4] [syscall #22] return: 0
[4] [tracer] sys_exec(): path = /cat, argv addr = 0x0000000000003fb8
[4] [syscall #7] return: 2
[4] [cat] sys_open(): path = README.md, omode = 0
[4] [syscall #15] return: 3
[4] [cat] sys_read(): fd = 0, buf = 0x0000000000001010, size = 512
[4] [syscall #5] return: 23
# FogOS
Hello world!
(more entries follow)
Part III: Rick Ropen() (bonus)
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 50% credit:
- Do the tracing exercise with
strace
on Linux and check your notes into your OS/docs
directory.
To receive 75% credit:
- Complete all previous requirements
- Implement tracing for at least one system call
- Implement the
tracer
command line utility
To receive full credit for this lab:
- Complete all previous requirements
- Implement tracing for all system calls in either
sysproc.c
orsysfile.c
To receive 105% credit for this lab:
- Complete all previous requirements
- Complete the Rick Ropen() bonus task (Part III)
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.