@@ -16,8 +16,12 @@ internal class SampleProfileBuilder
1616 // Output profile being built.
1717 public readonly SampleProfile Profile = new ( ) ;
1818
19- // A sparse array that maps from StackSourceFrameIndex to an index in the output Profile.frames.
20- private readonly Dictionary < int , int > _frameIndexes = new ( ) ;
19+ // A sparse array that maps from CodeAddressIndex to an index in the output Profile.frames.
20+ private readonly Dictionary < int , int > _frameIndexesByCodeAddressIndex = new ( ) ;
21+
22+ // A sparse array that maps from MethodIndex to an index in the output Profile.frames.
23+ // This deduplicates frames that map to the same method but have a different CodeAddressIndex.
24+ private readonly Dictionary < int , int > _frameIndexesByMethodIndex = new ( ) ;
2125
2226 // A dictionary from a CallStackIndex to an index in the output Profile.stacks.
2327 private readonly Dictionary < int , int > _stackIndexes = new ( ) ;
@@ -85,13 +89,14 @@ private int AddStackTrace(CallStackIndex callstackIndex)
8589 {
8690 var key = ( int ) callstackIndex ;
8791
88- if ( ! _stackIndexes . ContainsKey ( key ) )
92+ if ( ! _stackIndexes . TryGetValue ( key , out var value ) )
8993 {
9094 Profile . Stacks . Add ( CreateStackTrace ( callstackIndex ) ) ;
91- _stackIndexes [ key ] = Profile . Stacks . Count - 1 ;
95+ value = Profile . Stacks . Count - 1 ;
96+ _stackIndexes [ key ] = value ;
9297 }
9398
94- return _stackIndexes [ key ] ;
99+ return value ;
95100 }
96101
97102 private Internal . GrowableArray < int > CreateStackTrace ( CallStackIndex callstackIndex )
@@ -116,21 +121,71 @@ private Internal.GrowableArray<int> CreateStackTrace(CallStackIndex callstackInd
116121 return stackTrace ;
117122 }
118123
124+ private int PushNewFrame ( SentryStackFrame frame )
125+ {
126+ Profile . Frames . Add ( frame ) ;
127+ return Profile . Frames . Count - 1 ;
128+ }
129+
119130 /// <summary>
120131 /// Check if the frame is already stored in the output Profile, or adds it.
121132 /// </summary>
122133 /// <returns>The index to the output Profile frames array.</returns>
123134 private int AddStackFrame ( CodeAddressIndex codeAddressIndex )
124135 {
125- var key = ( int ) codeAddressIndex ;
136+ if ( _frameIndexesByCodeAddressIndex . TryGetValue ( ( int ) codeAddressIndex , out var value ) )
137+ {
138+ return value ;
139+ }
126140
127- if ( ! _frameIndexes . ContainsKey ( key ) )
141+ var methodIndex = _traceLog . CodeAddresses . MethodIndex ( codeAddressIndex ) ;
142+ if ( methodIndex != MethodIndex . Invalid )
143+ {
144+ value = AddStackFrame ( methodIndex ) ;
145+ _frameIndexesByCodeAddressIndex [ ( int ) codeAddressIndex ] = value ;
146+ return value ;
147+ }
148+
149+ // Fall back if the method info is unknown, see more info on Symbol resolution in
150+ // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784
151+ if ( _traceLog . CodeAddresses [ codeAddressIndex ] is { } codeAddressInfo )
128152 {
129- Profile . Frames . Add ( CreateStackFrame ( codeAddressIndex ) ) ;
130- _frameIndexes [ key ] = Profile . Frames . Count - 1 ;
153+ var frame = new SentryStackFrame
154+ {
155+ InstructionAddress = ( long ? ) codeAddressInfo . Address ,
156+ Module = codeAddressInfo . ModuleFile ? . Name ,
157+ } ;
158+ frame . ConfigureAppFrame ( _options ) ;
159+
160+ return _frameIndexesByCodeAddressIndex [ ( int ) codeAddressIndex ] = PushNewFrame ( frame ) ;
131161 }
132162
133- return _frameIndexes [ key ] ;
163+ // If all else fails, it's a completely unknown frame.
164+ // TODO check this - maybe we would be able to resolve it later in the future?
165+ return PushNewFrame ( new SentryStackFrame { InApp = false } ) ;
166+ }
167+
168+ /// <summary>
169+ /// Check if the frame is already stored in the output Profile, or adds it.
170+ /// </summary>
171+ /// <returns>The index to the output Profile frames array.</returns>
172+ private int AddStackFrame ( MethodIndex methodIndex )
173+ {
174+ if ( _frameIndexesByMethodIndex . TryGetValue ( ( int ) methodIndex , out var value ) )
175+ {
176+ return value ;
177+ }
178+
179+ var method = _traceLog . CodeAddresses . Methods [ methodIndex ] ;
180+
181+ var frame = new SentryStackFrame
182+ {
183+ Function = method . FullMethodName ,
184+ Module = method . MethodModuleFile ? . Name
185+ } ;
186+ frame . ConfigureAppFrame ( _options ) ;
187+
188+ return _frameIndexesByMethodIndex [ ( int ) methodIndex ] = PushNewFrame ( frame ) ;
134189 }
135190
136191 /// <summary>
@@ -141,52 +196,17 @@ private int AddThread(TraceThread thread)
141196 {
142197 var key = ( int ) thread . ThreadIndex ;
143198
144- if ( ! _threadIndexes . ContainsKey ( key ) )
199+ if ( ! _threadIndexes . TryGetValue ( key , out var value ) )
145200 {
146201 Profile . Threads . Add ( new ( )
147202 {
148203 Name = thread . ThreadInfo ?? $ "Thread { thread . ThreadID } ",
149204 } ) ;
150- _threadIndexes [ key ] = Profile . Threads . Count - 1 ;
205+ value = Profile . Threads . Count - 1 ;
206+ _threadIndexes [ key ] = value ;
151207 _downsampler . NewThreadAdded ( _threadIndexes [ key ] ) ;
152208 }
153209
154- return _threadIndexes [ key ] ;
155- }
156-
157- private SentryStackFrame CreateStackFrame ( CodeAddressIndex codeAddressIndex )
158- {
159- var frame = new SentryStackFrame ( ) ;
160-
161- var methodIndex = _traceLog . CodeAddresses . MethodIndex ( codeAddressIndex ) ;
162- if ( _traceLog . CodeAddresses . Methods [ methodIndex ] is { } method )
163- {
164- frame . Function = method . FullMethodName ;
165-
166- if ( method . MethodModuleFile is { } moduleFile )
167- {
168- frame . Module = moduleFile . Name ;
169- }
170-
171- frame . ConfigureAppFrame ( _options ) ;
172- }
173- else
174- {
175- // Fall back if the method info is unknown, see more info on Symbol resolution in
176- // https://github.com/getsentry/perfview/blob/031250ffb4f9fcadb9263525d6c9f274be19ca51/src/PerfView/SupportFiles/UsersGuide.htm#L7745-L7784
177- frame . InstructionAddress = ( long ? ) _traceLog . CodeAddresses . Address ( codeAddressIndex ) ;
178-
179- if ( _traceLog . CodeAddresses . ModuleFile ( codeAddressIndex ) is { } moduleFile )
180- {
181- frame . Module = moduleFile . Name ;
182- frame . ConfigureAppFrame ( _options ) ;
183- }
184- else
185- {
186- frame . InApp = false ;
187- }
188- }
189-
190- return frame ;
210+ return value ;
191211 }
192212}
0 commit comments