Call Tree and Investigation Directions
Visual call tree patterns and correlation-guided direction choices.
Visual Call Tree for Debugging
Use a visual tree structure to represent the debugging process. This helps:
- Visualize the call graph - see how functions relate to each other
- Track investigation progress - annotate nodes with status
- Communicate findings - show the user what's been checked and what hasn't
- Document the path to root cause - trace the issue through the tree
Node Annotations
Use these annotations to mark the status of each node:
| Annotation | Meaning |
|---|---|
OK |
Cleared - No issue found in this code path |
X |
Issue Found - Bug or problem identified here |
? |
Investigating - Currently analyzing this node |
... |
Pending - Need to investigate but haven't yet |
Building the Call Tree
Start from the entry point and expand as you investigate. Example for a user registration service:
register_user() [entry point - auto-instrumented, skip]
├── validate_email(email) ...
├── check_username_available(username) ? [investigating - slow calls observed]
│ └── query_user_database(username) ...
├── hash_password(password) ...
├── create_user_record(user_data) ...
│ └── insert_into_database(record) ...
└── send_welcome_email(email) ...Detailed Node Expansion
When investigating a specific function, expand it to show internal logic:
check_username_available("john_doe") X [BUG FOUND - case sensitivity issue]
├── normalized = normalize_username("john_doe")
│ └── result: "john_doe" (no change)
├── query = build_query(normalized)
│ └── SQL: SELECT * FROM users WHERE username = 'john_doe'
├── result = execute_query(query)
│ └── Found: "John_Doe" exists X [case-insensitive match missed!]
├── return: True (available) X WRONG - should be False
└── Root cause: Query uses case-sensitive comparison but
usernames should be case-insensitiveAnnotating with Evidence
Include snapshot data evidence directly in the tree:
process_checkout("order-5678", cart=[...])
├── Duration: 2,847ms X [SLOW - SLA is 500ms]
├── Input: cart = [
│ {"sku": "LAPTOP-001", "qty": 1},
│ {"sku": "MOUSE-002", "qty": 2}
│ ]
├── check_inventory("LAPTOP-001") OK
│ ├── Duration: 45ms [normal]
│ └── Evidence: Snapshot @ 14:22:03.112
├── check_inventory("MOUSE-002") OK
│ ├── Duration: 38ms [normal]
│ └── Evidence: Snapshot @ 14:22:03.157
├── calculate_shipping(address) X
│ ├── Duration: 2,651ms [SLOW!]
│ ├── Evidence: Snapshot @ 14:22:03.201
│ └── ? Need to investigate downstream calls
└── Return: {order_id: "ORD-9999", total: 1249.99}Comparing Good vs Bad Cases
Use side-by-side trees for comparison:
FAST REQUEST (domestic): SLOW REQUEST (international):
calculate_shipping(addr) calculate_shipping(addr)
├── country: "US" ├── country: "JP"
├── get_rates() → cache HIT OK ├── get_rates() → cache MISS
├── duration: 12ms │ └── fetch_from_api()
└── return: $9.99 │ └── duration: 2,340ms X
├── duration: 2,651ms
└── return: $89.99Progressive Investigation Tree
Update the tree as you investigate deeper:
Step 1: Initial investigation
submit_payment()
├── validate_card() ? [some calls failing - investigating]
├── check_fraud() ?
├── charge_card() ?
└── send_receipt() ?Step 2: After analyzing validate_card
submit_payment()
├── validate_card() ? [fails for certain card types]
│ ├── check_luhn() OK [algorithm correct]
│ ├── check_expiry() OK [date parsing correct]
│ └── check_card_type() X [fails for Amex cards]
├── check_fraud() OK [not reached when validation fails]
├── charge_card() OK [not reached when validation fails]
└── send_receipt() OK [not reached when validation fails]Step 3: Drilling into check_card_type
submit_payment()
├── validate_card()
│ └── check_card_type("378282246310005") X
│ ├── Input: card_number starting with "37"
│ ├── Expected: "amex" (Amex starts with 34 or 37)
│ ├── Actual: "unknown" X
│ └── Bug: Regex pattern missing Amex prefix "37"
...Including in Reports
Include a "Call Tree" in your inline closure summary:
## Call Tree
\`\`\`
submit_payment() [entry - auto-instrumented]
+-- validate_card(card_number) X ROOT CAUSE
| +-- check_luhn() OK
| +-- check_expiry() OK
| +-- check_card_type() X Missing Amex pattern "37"
+-- check_fraud() [not reached]
+-- charge_card() [not reached]
+-- send_receipt() [not reached]
\`\`\`
**Legend**: OK Cleared | X Issue | ? Investigating | ... PendingConnecting the Tree to Direction Choices
Use the call tree to choose the next move, not just to display progress. The node where the
tree first turns suspicious (X or ?) determines the next direction — look it up in the
Correlate → Decide table in dynamic-instrumentation.md (Step 4). In short: an X on inputs sends you upstream, an
X on the return sends you downstream, a ? on an intermediate value sends you line-level,
and mixed OK/X across runs sends you to multi-snapshot comparison.