1
1
defmodule Sentry.Sources do
2
2
@ moduledoc false
3
3
4
+ use GenServer
5
+
4
6
alias Sentry.Config
5
7
8
+ @ type source_map_for_file :: % {
9
+ optional ( line_no :: pos_integer ( ) ) => line_contents :: String . t ( )
10
+ }
11
+
6
12
@ type source_map :: % {
7
- optional ( String . t ( ) ) => % {
8
- ( line_no :: pos_integer ( ) ) => line_contents :: String . t ( )
9
- }
13
+ optional ( String . t ( ) ) => source_map_for_file ( )
10
14
}
11
15
12
- @ source_code_map_key { :sentry , :source_code_map }
16
+ ## GenServer
17
+
18
+ @ table __MODULE__
19
+
20
+ @ spec start_link ( keyword ( ) ) :: GenServer . on_start ( )
21
+ def start_link ( [ ] = _ ) do
22
+ GenServer . start_link ( __MODULE__ , nil , name: __MODULE__ )
23
+ end
24
+
25
+ @ impl true
26
+ def init ( nil ) do
27
+ _ = :ets . new ( @ table , [ :public , :named_table , read_concurrency: true ] )
28
+ { :ok , :no_state , { :continue , :load_source_code_map } }
29
+ end
30
+
31
+ @ impl true
32
+ def handle_continue ( :load_source_code_map , state ) do
33
+ :ok =
34
+ with { :loaded , source_map } <- load_source_code_map_if_present ( ) do
35
+ Enum . each ( source_map , fn { path , lines_map } ->
36
+ :ets . insert ( @ table , { path , lines_map } )
37
+ end )
38
+ else
39
+ _error -> :ok
40
+ end
41
+
42
+ { :noreply , state }
43
+ end
44
+
45
+ ## Other functions
46
+
13
47
@ compression_level if Mix . env ( ) == :test , do: 0 , else: 9
14
48
15
49
# Default argument is here for testing.
@@ -21,7 +55,6 @@ defmodule Sentry.Sources do
21
55
22
56
with { :ok , contents } <- File . read ( path ) ,
23
57
{ :ok , source_map } <- decode_source_code_map ( contents ) do
24
- :persistent_term . put ( @ source_code_map_key , source_map )
25
58
{ :loaded , source_map }
26
59
else
27
60
{ :error , :binary_to_term } ->
@@ -67,11 +100,6 @@ defmodule Sentry.Sources do
67
100
end
68
101
end
69
102
70
- @ spec get_source_code_map_from_persistent_term ( ) :: source_map ( ) | nil
71
- def get_source_code_map_from_persistent_term do
72
- :persistent_term . get ( @ source_code_map_key , nil )
73
- end
74
-
75
103
@ spec load_files ( keyword ( ) ) :: { :ok , source_map ( ) } | { :error , message :: String . t ( ) }
76
104
def load_files ( config \\ [ ] ) when is_list ( config ) do
77
105
config = Sentry.Config . validate! ( config )
@@ -106,23 +134,25 @@ defmodule Sentry.Sources do
106
134
source_map -> { :ok , source_map }
107
135
end
108
136
109
- @ spec get_source_context ( source_map ( ) , String . t ( ) | nil , pos_integer ( ) | nil ) ::
110
- { [ String . t ( ) ] , String . t ( ) | nil , [ String . t ( ) ] }
111
- def get_source_context ( % { } = files , file_name , line_number ) do
112
- context_lines = Config . context_lines ( )
113
-
114
- case Map . fetch ( files , file_name ) do
115
- :error -> { [ ] , nil , [ ] }
116
- { :ok , file } -> get_source_context_for_file ( file , line_number , context_lines )
137
+ @ spec get_lines_for_file ( Path . t ( ) ) :: map ( ) | nil
138
+ def get_lines_for_file ( file ) do
139
+ case :ets . lookup ( @ table , file ) do
140
+ [ { ^ file , lines } ] -> lines
141
+ [ ] -> nil
117
142
end
118
143
end
119
144
120
- defp get_source_context_for_file ( file , line_number , context_lines ) do
145
+ @ spec get_source_context ( source_map_for_file ( ) , pos_integer ( ) | nil ) ::
146
+ { [ String . t ( ) ] , String . t ( ) | nil , [ String . t ( ) ] }
147
+ def get_source_context ( source_map_for_file , line_number )
148
+ when is_map ( source_map_for_file ) and ( is_integer ( line_number ) or is_nil ( line_number ) ) do
149
+ context_lines = Config . context_lines ( )
150
+
121
151
context_line_indices = 0 .. ( 2 * context_lines )
122
152
123
153
Enum . reduce ( context_line_indices , { [ ] , nil , [ ] } , fn i , { pre_context , context , post_context } ->
124
154
context_line_number = line_number - context_lines + i
125
- source = Map . get ( file , context_line_number )
155
+ source = Map . get ( source_map_for_file , context_line_number )
126
156
127
157
cond do
128
158
context_line_number == line_number && source ->
0 commit comments