OS Lens -- See Inside the Machine
Tools for making the invisible visible. When the student asks "what actually happens when this runs?" -- this skill provides the instruments.
Trigger
/os-lenscommand- Student asks to see bytecode, memory allocation, syscalls, or execution trace
- During systems-trace Layer 2 verification when the student wants proof
Available Instruments
1. Bytecode Disassembly (dis module)
Shows what the CPython compiler produces. Every Python statement becomes bytecode instructions that the interpreter loop executes.
Usage:
import dis
dis.dis(function_name)
When to use: Understanding what for, with, yield, async actually compile to. Settling debates about "which is faster." Seeing that a + b is BINARY_ADD but a += b is BINARY_ADD + STORE_NAME (or STORE_FAST in a function -- different!).
Coach creates a scratch file at topics/topic-N/scratch/dis_inspect.py with the function to disassemble. Student runs it, reads the output, explains what each opcode does.
2. Execution Tracing (sys.settrace)
Shows every function call, line execution, and return -- the actual execution path, not what you think happens.
Usage:
import sys
def tracer(frame, event, arg):
if event in ('call', 'return'):
print(f"{event:8s} {frame.f_code.co_filename}:{frame.f_lineno} {frame.f_code.co_name}")
return tracer
sys.settrace(tracer)
# ... run the code ...
sys.settrace(None)
When to use: Understanding decorator execution order, context manager flow, exception propagation path, generator suspend/resume. Seeing that import triggers execution of the module's top-level code.
Warning: Massive output on real code. Always use on small, isolated examples. Remove before any performance measurement (settrace adds ~10x overhead).
3. Memory Allocation Tracking (tracemalloc)
Shows where Python allocates memory and how much.
Usage:
import tracemalloc
tracemalloc.start()
# ... run the code ...
snapshot = tracemalloc.take_snapshot()
for stat in snapshot.statistics('lineno')[:10]:
print(stat)
When to use: Finding memory leaks, comparing list vs generator vs numpy memory footprint, understanding why RSS grows, identifying which line allocates the most.
Pair with: resource.getrusage(resource.RUSAGE_SELF).ru_maxrss for peak RSS (OS-level, includes C extensions that tracemalloc cannot see).
4. Syscall Tracing (strace)
Shows every syscall the Python process makes to the kernel. The ground truth of what the OS does.
Usage (from shell):
strace -f -e trace=open,read,write,mmap,close python3 script.py 2>&1 | head -100
Common filters:
-e trace=network-- socket, connect, sendto, recvfrom-e trace=memory-- mmap, munmap, brk, mprotect-e trace=file-- open, close, read, write, stat-e trace=process-- fork, clone, execve, wait4-f-- follow child processes (essential for multiprocessing)
When to use: Verifying Layer 2 claims ("does open() really call openat()?"), diagnosing fd leaks, seeing what happens during import, understanding why a program is slow (blocked in read() vs busy in CPU).
Note: strace may not be available in all environments. Check with which strace. If unavailable, use ltrace for library calls or /proc inspection.
5. Process Memory Map (/proc/self/maps)
Shows the virtual memory layout of the running process.
Usage:
with open('/proc/self/maps') as f:
for line in f:
print(line, end='')
What you see: Stack, heap, mmap'd files, shared libraries (.so), [vdso], [vsyscall]. Each line shows address range, permissions (rwxp), offset, device, inode, pathname.
When to use: Understanding where Python objects live (heap), where C extension code lives (mmap'd .so files), how large the stack is, what shared libraries are loaded. Seeing that import numpy maps ~30MB of .so files.
Pair with: /proc/self/status for VmRSS (resident), VmSize (virtual), VmPeak, Threads count.
6. File Descriptor Inspection (/proc/self/fd)
Shows all open file descriptors.
Usage (from shell):
ls -la /proc/$(pgrep -f script.py)/fd
Or from Python:
import os
for fd in os.listdir('/proc/self/fd'):
try:
print(f"fd {fd} -> {os.readlink(f'/proc/self/fd/{fd}')}")
except OSError:
pass
When to use: Diagnosing fd leaks, verifying that with statements actually close files, seeing socket connections, understanding what stdin/stdout/stderr (0/1/2) point to.
Session Flow
When the student invokes /os-lens:
- Ask what they want to see. "What code are you curious about? What question are you trying to answer?"
- Pick the right instrument. Don't use all of them -- pick the one that answers the question.
- Coach creates the inspection script in
topics/topic-N/scratch/. The student runs it. - Student reads and explains the output. Coach does not interpret for them. "What do you see? What does that tell you?"
- Connect to the three layers. "Now you've seen Layer 2 directly. How does this change your mental model?"
Rules
- Always create scratch files for inspection -- never instrument exercise code directly.
- Clean up scratch files after inspection (or keep them if the student wants a reference).
- Start with the simplest instrument.
disbeforestrace.tracemallocbefore/proc/maps. - The goal is understanding, not exhaustive profiling. One instrument, one question, one answer.
- If the student wants to go deep into a specific instrument, that's exploration mode -- capture the artifact.