Engineering October 2025

Automatic Execution Tracking for Python Functions

How SINAS uses AST injection to track nested function calls without manual instrumentation

The Challenge

When building a function execution platform, one of the hardest problems is tracking how functions call other functions. Users want to see complete execution trees showing every nested call, input, output, and timing information—without having to manually instrument their code.

How It Works

SINAS automatically tracks function execution using Python's Abstract Syntax Tree (AST). When you upload a function, we parse the code, inject tracking decorators, and maintain the execution tree automatically.

Step 1: AST Parsing

When you create a function, SINAS parses your Python code into an AST:

def calculate_order_total(order_id):
    customer = get_customer(order_id)
    discount = calculate_discount(customer.tier)
    tax = calculate_tax(order.subtotal)
    return order.subtotal - discount + tax

Step 2: Automatic Decorator Injection

SINAS automatically adds a @track decorator to every function definition. This decorator captures:

  • Function name and version
  • Input parameters (serialized with dill)
  • Output values
  • Start and end timestamps
  • Parent execution context
  • Errors and tracebacks

Step 3: Execution Tree Building

When a function calls another SINAS function, the tracking system automatically links them in an execution tree. The result is a complete visualization of your function's behavior:

Execution: calculate_order_total
├─ Step: get_customer (completed, 150ms)
├─ Step: calculate_discount (completed, 50ms)
│  └─ Step: apply_promo_code (completed, 30ms)
└─ Step: calculate_tax (completed, 40ms)

Benefits

Zero Instrumentation

Write normal Python code. No special imports, no manual logging, no wrapper functions. Just pure business logic.

Complete Visibility

See exactly what happened during an execution:

  • Which functions were called and in what order
  • What inputs were passed to each function
  • What outputs each function returned
  • How long each function took
  • Where errors occurred in the call chain

Automatic Versioning

Every time you update function code, SINAS creates a new version. Execution records always reference the exact version that ran, making debugging historical issues trivial.

Performance Considerations

The tracking overhead is minimal:

  • AST parsing happens once at upload time
  • Execution tracking adds ~1-5ms per function call
  • Large inputs/outputs are sampled or truncated
  • Tracking can be disabled for performance-critical functions

Use Cases

Debugging Complex Workflows

When a webhook fails at 3 AM, you can trace the exact execution path and see where it went wrong.

Performance Optimization

Identify bottlenecks by analyzing execution times across the call tree.

Auditing and Compliance

Maintain complete records of what code ran, what data it processed, and what results it produced.

Technical Deep Dive

For those interested in the implementation details, the AST transformation uses Python's ast.NodeTransformer to wrap function definitions:

class TrackingInjector(ast.NodeTransformer):
    def visit_FunctionDef(self, node):
        # Inject @track decorator
        track_decorator = ast.Name(id='track', ctx=ast.Load())
        node.decorator_list.insert(0, track_decorator)
        return node

The @track decorator captures execution context from thread-local storage, allowing nested calls to automatically link to their parent execution.

Conclusion

Automatic execution tracking is one of those features that seems simple on the surface but provides immense value in production. By eliminating manual instrumentation, SINAS lets developers focus on business logic while maintaining complete observability.