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.

  1. First, run a trace on ls. Record all of the unique system calls (just their names) used by ls. To avoid doing a lot of tedious work, automate most of this with a shell pipeline or command line flag (see the man page for strace).

(list the syscalls here as a bulleted list, use ‘*’ before each system call)

  1. How many unique system calls are in your list?

  2. 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)
  1. 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.

  1. Add a flag to the process struct so that we can turn tracing on for particular processes. To do this, edit kernel/proc.h.
  2. 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.
  3. 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 or sysfile.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.
  4. Every time a system call returns, print its return value.
  5. 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:

To receive 75% credit:

To receive full credit for this lab:

To receive 105% credit for this lab:

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.