Skip to content

Commit cf3990b

Browse files
NSHkrNSHkr
authored andcommitted
refactor temporal_bridge_enhancement
1 parent 6ef7ac6 commit cf3990b

8 files changed

Lines changed: 1105 additions & 768 deletions

File tree

lib/elixir_scope/capture/enhanced_instrumentation.ex

Lines changed: 137 additions & 137 deletions
Large diffs are not rendered by default.

lib/elixir_scope/capture/temporal_bridge_enhancement.ex

Lines changed: 101 additions & 631 deletions
Large diffs are not rendered by default.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
defmodule ElixirScope.Capture.TemporalBridgeEnhancement.ASTContextBuilder do
2+
@moduledoc """
3+
AST context building utilities for TemporalBridgeEnhancement.
4+
5+
Handles building AST contexts, structural information, execution paths,
6+
and variable flow tracking from events.
7+
"""
8+
9+
alias ElixirScope.ASTRepository.RuntimeCorrelator
10+
11+
@doc """
12+
Builds AST context for a state from events.
13+
"""
14+
@spec build_ast_context_for_state(list(map()), pid()) :: {:ok, map() | nil}
15+
def build_ast_context_for_state(events, ast_repo) do
16+
# Get the most recent event with AST correlation
17+
case Enum.find(events, fn event -> Map.has_key?(event, :ast_node_id) end) do
18+
nil -> {:ok, nil}
19+
event ->
20+
case RuntimeCorrelator.get_runtime_context(ast_repo, event) do
21+
{:ok, context} -> {:ok, context}
22+
{:error, _} -> {:ok, nil}
23+
end
24+
end
25+
end
26+
27+
@doc """
28+
Extracts structural information for a state.
29+
"""
30+
@spec extract_structural_info_for_state(map() | nil, list(map())) :: {:ok, map()}
31+
def extract_structural_info_for_state(ast_context, events) do
32+
structural_info = %{
33+
current_ast_node: extract_current_ast_node(ast_context),
34+
call_depth: calculate_call_depth(events),
35+
execution_context: extract_execution_context(events),
36+
control_flow_state: determine_control_flow_state(ast_context, events)
37+
}
38+
39+
{:ok, structural_info}
40+
end
41+
42+
@doc """
43+
Builds execution path from events and AST repository.
44+
"""
45+
@spec build_execution_path(list(map()), pid()) :: {:ok, list(map())}
46+
def build_execution_path(events, _ast_repo) do
47+
execution_path = events
48+
|> Enum.filter(fn event -> Map.has_key?(event, :ast_node_id) end)
49+
|> Enum.map(fn event ->
50+
%{
51+
ast_node_id: event.ast_node_id,
52+
timestamp: Map.get(event, :timestamp),
53+
event_type: Map.get(event, :event_type),
54+
context: extract_event_context(event)
55+
}
56+
end)
57+
58+
{:ok, execution_path}
59+
end
60+
61+
@doc """
62+
Builds variable flow tracking for a state.
63+
"""
64+
@spec build_variable_flow_for_state(list(map())) :: {:ok, map()}
65+
def build_variable_flow_for_state(events) do
66+
variable_flow = events
67+
|> Enum.filter(fn event -> Map.has_key?(event, :variables) end)
68+
|> Enum.reduce(%{}, fn event, acc ->
69+
variables = Map.get(event, :variables, %{})
70+
timestamp = Map.get(event, :timestamp)
71+
72+
Enum.reduce(variables, acc, fn {var_name, var_value}, var_acc ->
73+
var_history = Map.get(var_acc, var_name, [])
74+
var_entry = %{value: var_value, timestamp: timestamp}
75+
Map.put(var_acc, var_name, [var_entry | var_history])
76+
end)
77+
end)
78+
|> Enum.map(fn {var_name, history} ->
79+
{var_name, Enum.reverse(history)}
80+
end)
81+
|> Enum.into(%{})
82+
83+
{:ok, variable_flow}
84+
end
85+
86+
# Private utility functions
87+
88+
defp extract_current_ast_node(nil), do: nil
89+
defp extract_current_ast_node(ast_context), do: Map.get(ast_context, :ast_node_id)
90+
91+
defp calculate_call_depth(events) do
92+
events
93+
|> Enum.filter(fn event -> Map.get(event, :event_type) in [:function_entry, :function_exit] end)
94+
|> Enum.reduce(0, fn event, depth ->
95+
case Map.get(event, :event_type) do
96+
:function_entry -> depth + 1
97+
:function_exit -> max(0, depth - 1)
98+
_ -> depth
99+
end
100+
end)
101+
end
102+
103+
defp extract_execution_context(events) do
104+
%{
105+
total_events: length(events),
106+
event_types: events |> Enum.map(fn event -> Map.get(event, :event_type) end) |> Enum.frequencies(),
107+
time_span: calculate_time_span(events)
108+
}
109+
end
110+
111+
defp determine_control_flow_state(_ast_context, events) do
112+
# Determine current control flow state based on recent events
113+
recent_events = Enum.take(events, -5)
114+
115+
cond do
116+
Enum.any?(recent_events, fn event -> Map.get(event, :event_type) == :exception end) ->
117+
:exception_handling
118+
119+
Enum.any?(recent_events, fn event -> Map.get(event, :event_type) == :function_entry end) ->
120+
:function_call
121+
122+
Enum.any?(recent_events, fn event -> Map.get(event, :event_type) == :state_change end) ->
123+
:state_transition
124+
125+
true ->
126+
:sequential
127+
end
128+
end
129+
130+
defp extract_event_context(event) do
131+
%{
132+
module: Map.get(event, :module),
133+
function: Map.get(event, :function),
134+
line: Map.get(event, :line),
135+
correlation_id: Map.get(event, :correlation_id)
136+
}
137+
end
138+
139+
defp calculate_time_span(events) do
140+
if length(events) < 2 do
141+
0
142+
else
143+
timestamps = Enum.map(events, fn event -> Map.get(event, :timestamp) end)
144+
Enum.max(timestamps) - Enum.min(timestamps)
145+
end
146+
end
147+
end
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
defmodule ElixirScope.Capture.TemporalBridgeEnhancement.CacheManager do
2+
@moduledoc """
3+
Cache management for TemporalBridgeEnhancement.
4+
5+
Handles ETS table creation, caching operations, and cache invalidation
6+
for enhanced state reconstruction and execution traces.
7+
"""
8+
9+
@ast_state_cache :temporal_bridge_ast_state_cache
10+
@execution_trace_cache :temporal_bridge_execution_trace_cache
11+
12+
# Cache TTL (2 minutes)
13+
@cache_ttl 120_000
14+
15+
@doc """
16+
Creates an ETS table, handling existing tables gracefully.
17+
"""
18+
@spec create_table(atom()) :: atom()
19+
def create_table(table_name) do
20+
try do
21+
:ets.new(table_name, [:named_table, :public, :set, {:read_concurrency, true}])
22+
rescue
23+
ArgumentError ->
24+
# Table already exists, clear it
25+
:ets.delete_all_objects(table_name)
26+
table_name
27+
end
28+
end
29+
30+
@doc """
31+
Initializes all cache tables.
32+
"""
33+
@spec init_caches() :: :ok
34+
def init_caches() do
35+
create_table(@ast_state_cache)
36+
create_table(@execution_trace_cache)
37+
:ok
38+
end
39+
40+
@doc """
41+
Gets a cached state with TTL check.
42+
"""
43+
@spec get_cached_state(String.t(), non_neg_integer()) ::
44+
{:hit, any()} | {:miss, :not_found | :expired | :table_not_found}
45+
def get_cached_state(session_id, timestamp) do
46+
cache_key = "#{session_id}:#{timestamp}"
47+
48+
try do
49+
case :ets.lookup(@ast_state_cache, cache_key) do
50+
[{^cache_key, {enhanced_state, cached_timestamp}}] ->
51+
if System.monotonic_time(:millisecond) - cached_timestamp < @cache_ttl do
52+
{:hit, enhanced_state}
53+
else
54+
:ets.delete(@ast_state_cache, cache_key)
55+
{:miss, :expired}
56+
end
57+
58+
[] ->
59+
{:miss, :not_found}
60+
end
61+
rescue
62+
ArgumentError ->
63+
{:miss, :table_not_found}
64+
end
65+
end
66+
67+
@doc """
68+
Caches a state with current timestamp.
69+
"""
70+
@spec cache_state(String.t(), non_neg_integer(), any()) :: :ok
71+
def cache_state(session_id, timestamp, enhanced_state) do
72+
cache_key = "#{session_id}:#{timestamp}"
73+
cache_timestamp = System.monotonic_time(:millisecond)
74+
75+
try do
76+
:ets.insert(@ast_state_cache, {cache_key, {enhanced_state, cache_timestamp}})
77+
:ok
78+
rescue
79+
ArgumentError ->
80+
# Table doesn't exist, create it and try again
81+
create_table(@ast_state_cache)
82+
:ets.insert(@ast_state_cache, {cache_key, {enhanced_state, cache_timestamp}})
83+
:ok
84+
end
85+
end
86+
87+
@doc """
88+
Gets a cached execution trace with TTL check.
89+
"""
90+
@spec get_cached_trace(String.t(), non_neg_integer(), non_neg_integer()) ::
91+
{:hit, any()} | {:miss, :not_found | :expired | :table_not_found}
92+
def get_cached_trace(session_id, start_time, end_time) do
93+
cache_key = "trace:#{session_id}:#{start_time}:#{end_time}"
94+
95+
try do
96+
case :ets.lookup(@execution_trace_cache, cache_key) do
97+
[{^cache_key, {trace, cached_timestamp}}] ->
98+
if System.monotonic_time(:millisecond) - cached_timestamp < @cache_ttl do
99+
{:hit, trace}
100+
else
101+
:ets.delete(@execution_trace_cache, cache_key)
102+
{:miss, :expired}
103+
end
104+
105+
[] ->
106+
{:miss, :not_found}
107+
end
108+
rescue
109+
ArgumentError ->
110+
{:miss, :table_not_found}
111+
end
112+
end
113+
114+
@doc """
115+
Caches an execution trace with current timestamp.
116+
"""
117+
@spec cache_trace(String.t(), non_neg_integer(), non_neg_integer(), any()) :: :ok
118+
def cache_trace(session_id, start_time, end_time, trace) do
119+
cache_key = "trace:#{session_id}:#{start_time}:#{end_time}"
120+
cache_timestamp = System.monotonic_time(:millisecond)
121+
122+
try do
123+
:ets.insert(@execution_trace_cache, {cache_key, {trace, cache_timestamp}})
124+
:ok
125+
rescue
126+
ArgumentError ->
127+
# Table doesn't exist, create it and try again
128+
create_table(@execution_trace_cache)
129+
:ets.insert(@execution_trace_cache, {cache_key, {trace, cache_timestamp}})
130+
:ok
131+
end
132+
end
133+
134+
@doc """
135+
Clears all caches.
136+
"""
137+
@spec clear_all_caches() :: :ok
138+
def clear_all_caches() do
139+
try do
140+
:ets.delete_all_objects(@ast_state_cache)
141+
rescue
142+
ArgumentError -> :ok # Table doesn't exist, that's fine
143+
end
144+
145+
try do
146+
:ets.delete_all_objects(@execution_trace_cache)
147+
rescue
148+
ArgumentError -> :ok # Table doesn't exist, that's fine
149+
end
150+
151+
:ok
152+
end
153+
154+
@doc """
155+
Gets cache statistics.
156+
"""
157+
@spec get_cache_stats() :: map()
158+
def get_cache_stats() do
159+
state_cache_size = try do
160+
:ets.info(@ast_state_cache, :size) || 0
161+
rescue
162+
ArgumentError -> 0
163+
end
164+
165+
trace_cache_size = try do
166+
:ets.info(@execution_trace_cache, :size) || 0
167+
rescue
168+
ArgumentError -> 0
169+
end
170+
171+
state_cache_memory = try do
172+
:ets.info(@ast_state_cache, :memory) || 0
173+
rescue
174+
ArgumentError -> 0
175+
end
176+
177+
trace_cache_memory = try do
178+
:ets.info(@execution_trace_cache, :memory) || 0
179+
rescue
180+
ArgumentError -> 0
181+
end
182+
183+
%{
184+
state_cache_size: state_cache_size,
185+
trace_cache_size: trace_cache_size,
186+
state_cache_memory: state_cache_memory,
187+
trace_cache_memory: trace_cache_memory
188+
}
189+
end
190+
end

0 commit comments

Comments
 (0)