@@ -135,6 +135,10 @@ func generateTableArrowRecord(
135135
136136 for sampleRow := 0 ; sampleRow < int (r .Record .NumRows ()); sampleRow ++ {
137137 previousTableRow := - 1
138+ // Track which table rows have been counted for this sample to avoid
139+ // double-counting cumulative values for recursive functions.
140+ seenInSample := make (map [int ]struct {})
141+
138142 lOffsetStart , lOffsetEnd := r .Locations .ValueOffsets (sampleRow )
139143 for locationRow := int (lOffsetStart ); locationRow < int (lOffsetEnd ); locationRow ++ {
140144 if r .Locations .ListValues ().IsNull (locationRow ) {
@@ -164,10 +168,17 @@ func generateTableArrowRecord(
164168 } else {
165169 tb .addresses [string (buildID )][addr ] = tableRow
166170 }
171+ seenInSample [tableRow ] = struct {}{}
167172 previousTableRow = tableRow
168173 tableRow ++
169174 } else {
170- tb .mergeRow (r , cr , sampleRow , locationRow , - 1 , tb .addresses [unsafeString (buildID )][addr ], previousTableRow , isLeaf )
175+ // Only add to cumulative if this is the first occurrence in this sample
176+ _ , alreadySeen := seenInSample [cr ]
177+ addCumulative := ! alreadySeen
178+ if addCumulative {
179+ seenInSample [cr ] = struct {}{}
180+ }
181+ tb .mergeRow (r , cr , sampleRow , locationRow , - 1 , tb .addresses [unsafeString (buildID )][addr ], previousTableRow , isLeaf , addCumulative )
171182 previousTableRow = tb .addresses [unsafeString (buildID )][addr ]
172183 }
173184 } else {
@@ -184,10 +195,17 @@ func generateTableArrowRecord(
184195 return nil , 0 , err
185196 }
186197 tb .functions [string (fn )] = tableRow
198+ seenInSample [tableRow ] = struct {}{}
187199 previousTableRow = tableRow
188200 tableRow ++
189201 } else {
190- tb .mergeRow (r , cr , sampleRow , locationRow , lineRow , tb .functions [unsafeString (fn )], previousTableRow , isLeaf )
202+ // Only add to cumulative if this is the first occurrence in this sample
203+ _ , alreadySeen := seenInSample [cr ]
204+ addCumulative := ! alreadySeen
205+ if addCumulative {
206+ seenInSample [cr ] = struct {}{}
207+ }
208+ tb .mergeRow (r , cr , sampleRow , locationRow , lineRow , tb .functions [unsafeString (fn )], previousTableRow , isLeaf , addCumulative )
191209 previousTableRow = tb .functions [unsafeString (fn )]
192210 }
193211 }
@@ -432,10 +450,15 @@ func (tb *tableBuilder) appendRow(
432450 return nil
433451}
434452
435- func (tb * tableBuilder ) mergeRow (r * profile.RecordReader , mergeRow , sampleRow , _ , lineRow , currentTableRow , previousTableRow int , isLeaf bool ) {
436- tb .builderCumulative .Add (mergeRow , r .Value .Value (sampleRow ))
437- if r .Diff .Value (sampleRow ) != 0 {
438- tb .builderCumulativeDiff .Add (mergeRow , r .Diff .Value (sampleRow ))
453+ // mergeRow merges sample data into an existing table row.
454+ // If addCumulative is false, only caller/callee relationships and flat values (if leaf) are updated.
455+ // This is used to avoid double-counting cumulative values for recursive functions.
456+ func (tb * tableBuilder ) mergeRow (r * profile.RecordReader , mergeRow , sampleRow , _ , lineRow , currentTableRow , previousTableRow int , isLeaf , addCumulative bool ) {
457+ if addCumulative {
458+ tb .builderCumulative .Add (mergeRow , r .Value .Value (sampleRow ))
459+ if r .Diff .Value (sampleRow ) != 0 {
460+ tb .builderCumulativeDiff .Add (mergeRow , r .Diff .Value (sampleRow ))
461+ }
439462 }
440463
441464 if isLeaf {
0 commit comments