Sample lesson: tracing a system call from EL0 to EL1
This is one complete guided investigation, taught at the depth every course runs at. It is adapted from the processor-architecture track. The point is not the syscall — it is the method: form a hypothesis, instrument the machine, and let the evidence settle it.
Before you start
You have a QEMU AArch64 virtual machine running a mainline Linux kernel you built yourself, with ftrace enabled. You also have a terminal on the host. Nothing in this lesson requires physical hardware, and every command is reproducible from the notes you keep.
The question
When userspace calls write(2), what actually happens between the C library issuing the instruction and the kernel's syscall handler running — and how long does each stage cost?
Most engineers can recite "it traps into the kernel." In this lesson you will show the whole path, with timestamps, and be able to defend every claim with a trace line.
Step 1 — the userspace view
Run a trivial writer under strace -tt and note the wall-clock span of the write call. This is your outer envelope; everything you measure from here must fit inside it. If a later number does not fit, your method is wrong — that is the first lesson of measurement discipline.
Step 2 — find the trap
On AArch64 the svc #0 instruction raises a synchronous exception to EL1. You locate the kernel's exception vector table (vectors in entry.S), follow the el0_svc path through the syscall table lookup, and identify the exact instructions that save the user register frame. You write down the sequence; reciting is not allowed, only reading.
Step 3 — instrument it
Enable the function graph tracer on the syscall path:
cd /sys/kernel/tracing echo function_graph > current_tracer echo '__arm64_sys_write' > set_graph_function echo 1 > tracing_on; ./writer; echo 0 > tracing_on cat trace
You now hold a timestamped record of the kernel-side entry, the VFS dispatch, and the return — per call, in nanoseconds.
Step 4 — reconcile the layers
Compare three numbers: strace's wall-clock span, ftrace's in-kernel span, and a perf stat cycle count for the same workload. The gap between userspace and in-kernel time is the exception entry/exit cost plus scheduling noise. You explain the gap quantitatively — or you measure again until you can.
Step 5 — break it on purpose
Repeat under CPU contention and under taskset pinning. Watch the entry cost move. The hypothesis you wrote in step 4 now either survives or gets revised, and you record which.
What you hand in
The trace excerpts, the three reconciled measurements, your annotated map of the EL0→EL1 path, and a paragraph stating what you observed, what you inferred, and what you could not verify. Facts, interpretations, assumptions — kept separate, always.
Why we teach this way
The syscall path is a vehicle. What transfers to your job is the loop: hypothesis, instrument, measure, reconcile, report. That loop works on a scheduler regression, a DMA coherency bug, or a 400 GbE fabric — which is why every course in the catalog is built around it.
One lab from every track
- ARC-101 — Lab: measure IPC and front-end stalls with perf stat on tight loops and explain the numbers against a pipeline diagram
- KRN-101 — Lab: follow a real syscall from userspace entry to its kernel implementation, recording every file and function on the path
- DRV-101 — Lab: Register a char device with alloc_chrdev_region and cdev_add; watch udev create the node and verify reads reach your handler
- EMB-101 — Lab: Build core-image-minimal for qemuarm from a clean checkout, then trace one package through tmp/work from fetch to package
- RTL-101 — Lab: Build a mainline and a PREEMPT_RT kernel from the same config base; diff the preemption-related options and boot both
- DBG-101 — Lab: decode a captured oops with addr2line and scripts/decode_stacktrace down to the exact faulting source line
- PRF-101 — Lab: characterise an unknown workload from live counters — infer what it does before being told
- SEC-101 — Lab: Trace a published kernel CVE from the advisory to its fixing commits with git log and Fixes: tags
- NET-101 — Lab: trace one packet from NIC interrupt to socket wakeup with perf/ftrace probes on the receive path
- STO-101 — Lab: benchmark raw-device vs filesystem I/O with fio and explain the difference the page cache makes
- VRT-101 — Lab: boot a minimal guest on /dev/kvm with a small C VMM — no QEMU — and handle its VM exits yourself
- AIC-100 — Lab: map your machine's topology with lscpu, numactl and /proc — cores, caches, NUMA nodes, PCIe paths
- FPG-101 — Lab: Implement a small FSM-plus-datapath block (a UART receiver or pulse counter) in Verilog from a written cycle schedule
- VIS-101 — Lab: Compute the lane-rate budget for three sensor modes (resolution, fps, bit depth) and check each against the receiver's per-lane limit
- FRM-101 — Lab: bring up a Cortex-M board from erased flash with your own startup code and linker script — no vendor HAL — ending at a blinking LED
- HPC-101 — Lab: write a ping-pong benchmark with MPI_Send/MPI_Recv and fit the latency/bandwidth model to your own numbers
Want the full course?
This lesson is one investigation out of the processor-architecture track's dozens.