Tracking Memory Access Patterns in Python Using sys.settrace and Custom Hooks
HexShift

HexShift @hexshift

About: Help with web development through niche, technical tutorials. From advanced JavaScript patterns and TypeScript best practices to mastering frameworks. Support link at base of each article. Thanks.

Joined:
Apr 5, 2025

Tracking Memory Access Patterns in Python Using sys.settrace and Custom Hooks

Publish Date: Apr 22
0 0

Python doesn’t expose raw memory the way lower-level languages do, but you can still track how and when your variables change. In this tutorial, we’ll build a lightweight memory tracing tool using Python’s built-in sys.settrace function, allowing us to monitor variable assignments and get insights into runtime behavior — no C extensions, no third-party tools.

Setup


This method works in pure Python 3. No dependencies needed. You’ll just need a working Python environment and a target function you want to introspect. It's particularly useful for debugging, reverse engineering, or building your own lightweight instrumentation tools.

Full Working Code


import sys

def trace_vars(frame, event, arg):
if event != "line":
return trace_vars

code = frame.f_code
lineno = frame.f_lineno
locals_now = frame.f_locals.copy()
global last_locals

if code.co_name not in last_locals:
    last_locals[code.co_name] = locals_now
    return trace_vars

old_locals = last_locals[code.co_name]

for var, new_val in locals_now.items():
    if var not in old_locals:
        print(f"[{code.co_name}:{lineno}] NEW {var} = {new_val}")
    elif old_locals[var] != new_val:
        print(f"[{code.co_name}:{lineno}] MODIFIED {var}: {old_locals[var]} → {new_val}")

for var in old_locals:
    if var not in locals_now:
        print(f"[{code.co_name}:{lineno}] DELETED {var}")

last_locals[code.co_name] = locals_now
return trace_vars
Enter fullscreen mode Exit fullscreen mode

def monitor(func):
def wrapper(*args, **kwargs):
global last_locals
last_locals = {}
sys.settrace(trace_vars)
try:
return func(*args, **kwargs)
finally:
sys.settrace(None)
return wrapper

Example usage

@monitor
def run_example():
a = 10
b = a + 5
b = b * 2
del a
return b

run_example()

Explanation


By using sys.settrace, we register a line-by-line callback that can introspect the current local variables at each step. We store a snapshot of the locals for each function, then compare it on the next invocation to detect changes — additions, updates, deletions. It’s a powerful (and often overlooked) way to understand the control and data flow of any Python function at runtime.

Pros & Cons

✅ Pros


  • Works with unmodified Python
  • Fully userland — no C or unsafe operations
  • Easy to wrap around any function
  • Valuable for introspection, testing, and education

⚠️ Cons


  • Significant performance overhead
  • Doesn’t work well with async or multithreaded code
  • Can’t access native extensions or memory directly
  • Not suitable for production monitoring

Summary


This approach gives you a window into the lifecycle of variables in Python, letting you trace changes without any third-party packages or tooling. While it's not a true memory inspector, it's a powerful pattern for anyone debugging tricky code or trying to understand execution in fine-grained detail.

If this was useful, you can Buy Me a Coffee

Comments 0 total

    Add comment