@@ -12,7 +12,6 @@ import (
1212
1313 googlev1 "github.com/grafana/pyroscope/api/gen/proto/go/google/v1"
1414 "github.com/grafana/pyroscope/pkg/model"
15- "github.com/grafana/pyroscope/pkg/pprof"
1615 "github.com/grafana/pyroscope/pkg/test/mocks/mockobjstore"
1716 "github.com/grafana/pyroscope/pkg/test/mocks/mocksymbolizer"
1817
@@ -247,19 +246,31 @@ func TestSymbolizePprof(t *testing.T) {
247246}
248247
249248func TestSymbolizationKeepsSequentialFunctionIDs (t * testing.T ) {
250- s := createSymbolizerForBuildID (t , "build-id" )
249+ mockClient := mocksymbolizer .NewMockDebuginfodClient (t )
250+ mockBucket := mockobjstore .NewMockBucket (t )
251251
252252 profile := & googlev1.Profile {
253253 Mapping : []* googlev1.Mapping {{BuildId : 1 }},
254254 Location : []* googlev1.Location {{Id : 1 , MappingId : 1 , Address : 0x1500 }},
255- Function : []* googlev1.Function {{Id : 1 , Name : 2 }},
255+ Function : []* googlev1.Function {{Id : 1 , Name : 1 }},
256256 StringTable : []string {"" , "build-id" , "existing_func" },
257257 Sample : []* googlev1.Sample {{
258258 LocationId : []uint64 {1 },
259259 Value : []int64 {100 },
260260 }},
261261 }
262262
263+ mockBucket .On ("Get" , mock .Anything , "build-id" ).Return (nil , fmt .Errorf ("not found" ))
264+ mockClient .On ("FetchDebuginfo" , mock .Anything , "build-id" ).Return (openTestFile (t ), nil )
265+ mockBucket .On ("Upload" , mock .Anything , "build-id" , mock .Anything ).Return (nil )
266+
267+ s := & Symbolizer {
268+ logger : log .NewNopLogger (),
269+ client : mockClient ,
270+ bucket : mockBucket ,
271+ metrics : newMetrics (nil ),
272+ }
273+
263274 err := s .SymbolizePprof (context .Background (), profile )
264275 require .NoError (t , err )
265276
@@ -520,56 +531,6 @@ func TestSymbolizerMetrics(t *testing.T) {
520531 }
521532}
522533
523- // TestMixedSymbolizationCorrectsFlagsForAllMappings tests that the symbolizer correctly sets HasFunctions=false for
524- // mappings with partial symbolization, fixing incorrect flags from upstream sources.
525- func TestMixedSymbolizationCorrectsFlagsForAllMappings (t * testing.T ) {
526- // Create a profile with incorrect HasFunctions=true on a mixed mapping
527- profile := & googlev1.Profile {
528- Mapping : []* googlev1.Mapping {
529- {Id : 1 , HasFunctions : true , BuildId : 1 , Filename : 1 }, // incorrect: has mixed symbolization
530- },
531- Location : []* googlev1.Location {
532- {Id : 1 , MappingId : 1 , Address : 0x1000 , Line : []* googlev1.Line {{FunctionId : 1 }}}, // symbolized
533- {Id : 2 , MappingId : 1 , Address : 0x2000 , Line : nil }, // unsymbolized
534- },
535- Function : []* googlev1.Function {{Id : 1 , Name : 2 }},
536- StringTable : []string {"" , "test.so" , "existing_func" },
537- }
538-
539- profile .StringTable [profile .Mapping [0 ].BuildId ] = "" // Make buildID empty so symbolizer skips it
540-
541- s := & Symbolizer {
542- logger : log .NewNopLogger (),
543- client : nil ,
544- bucket : nil ,
545- metrics : newMetrics (nil ),
546- }
547-
548- err := s .SymbolizePprof (context .Background (), profile )
549- require .NoError (t , err )
550-
551- // Verify that HasFunctions was corrected to false for mixed symbolization
552- require .False (t , profile .Mapping [0 ].HasFunctions , "Mapping with mixed symbolization should have HasFunctions=false" )
553- }
554-
555- // createSymbolizerForBuildID creates a symbolizer with mocks for a specific buildID
556- func createSymbolizerForBuildID (t * testing.T , buildID string ) * Symbolizer {
557- t .Helper ()
558- mockClient := mocksymbolizer .NewMockDebuginfodClient (t )
559- mockBucket := mockobjstore .NewMockBucket (t )
560-
561- mockBucket .On ("Get" , mock .Anything , buildID ).Return (nil , fmt .Errorf ("not found" ))
562- mockClient .On ("FetchDebuginfo" , mock .Anything , buildID ).Return (openTestFile (t ), nil )
563- mockBucket .On ("Upload" , mock .Anything , buildID , mock .Anything ).Return (nil )
564-
565- return & Symbolizer {
566- logger : log .NewNopLogger (),
567- client : mockClient ,
568- bucket : mockBucket ,
569- metrics : newMetrics (nil ),
570- }
571- }
572-
573534func assertLocationHasFunction (t * testing.T , profile * googlev1.Profile , loc * googlev1.Location ,
574535 functionName , fileName string ) {
575536 t .Helper ()
@@ -645,75 +606,3 @@ func createRequest(t *testing.T, buildID string, address uint64) *request {
645606 },
646607 }
647608}
648-
649- // TestFlamegraphTruncationIntegration is an integration test for the flamegraph truncation fix.
650- // TODO: Move this to an integration test package when one exists.
651- //
652- // Context: Mixed symbolization profiles with incorrect HasFunctions=true flags caused flamegraph truncation.
653- // When clearAddresses() cleared ALL addresses during normalize(), unsymbolized locations became
654- // indistinguishable (identical LocationKeys) and merged incorrectly, losing flamegraph blocks.
655- //
656- // Symbolizer now corrects HasFunctions flags based on actual symbolization state.
657- // Mixed mappings get HasFunctions=false, preserving addresses for proper deduplication.
658- func TestFlamegraphTruncationIntegration (t * testing.T ) {
659- symbolizer := & Symbolizer {
660- logger : log .NewNopLogger (),
661- metrics : newMetrics (prometheus .NewRegistry ()),
662- }
663-
664- // Create profile with mixed symbolization - some locations already symbolized
665- profile := & googlev1.Profile {
666- SampleType : []* googlev1.ValueType {{Type : 1 , Unit : 2 }},
667- PeriodType : & googlev1.ValueType {Type : 1 , Unit : 2 },
668- Period : 1000000 ,
669- Sample : []* googlev1.Sample {
670- {LocationId : []uint64 {1 , 2 }, Value : []int64 {100 }},
671- {LocationId : []uint64 {3 , 2 }, Value : []int64 {200 }},
672- },
673- Mapping : []* googlev1.Mapping {{
674- Id : 1 , HasFunctions : true , BuildId : 3 , Filename : 4 , // Incorrectly set to true for mixed mapping
675- MemoryStart : 0x400000 , MemoryLimit : 0x500000 ,
676- }},
677- Location : []* googlev1.Location {
678- {Id : 1 , MappingId : 1 , Address : 0x1500 , Line : []* googlev1.Line {{FunctionId : 1 }}}, // symbolized location
679- {Id : 2 , MappingId : 1 , Address : 0x999999 , Line : nil },
680- {Id : 3 , MappingId : 1 , Address : 0x888888 , Line : nil },
681- },
682- Function : []* googlev1.Function {
683- {Id : 1 , Name : 5 }, // Function for the symbolized location
684- },
685- StringTable : []string {"" , "samples" , "count" , "build-id" , "test.so" , "symbolized_function" },
686- }
687-
688- // Symbolizer corrects HasFunctions flag for mixed mapping
689- err := symbolizer .SymbolizePprof (context .Background (), profile )
690- require .NoError (t , err )
691- require .False (t , profile .Mapping [0 ].HasFunctions , "Mixed mapping should have HasFunctions=false" )
692-
693- // Profile normalization preserves addresses when HasFunctions=false
694- pprofProfile := & pprof.Profile {Profile : profile }
695- pprofProfile .Normalize () // calls clearAddresses()
696-
697- require .NotZero (t , profile .Location [0 ].Address , "Address should be preserved when HasFunctions=false" )
698- require .NotZero (t , profile .Location [1 ].Address , "Address should be preserved when HasFunctions=false" )
699- require .NotZero (t , profile .Location [2 ].Address , "Address should be preserved when HasFunctions=false" )
700-
701- // Profile merge preserves all distinct locations
702- var merge pprof.ProfileMerge
703- err = merge .Merge (profile , true )
704- require .NoError (t , err )
705-
706- mergedProfile := merge .Profile ()
707- require .Equal (t , 3 , len (mergedProfile .Location ), "All locations should be preserved with distinct addresses" )
708-
709- // Verify unsymbolized locations maintain their distinct addresses
710- var unsymbolizedAddrs []uint64
711- for _ , loc := range mergedProfile .Location {
712- if len (loc .Line ) == 0 {
713- unsymbolizedAddrs = append (unsymbolizedAddrs , loc .Address )
714- }
715- }
716- require .Len (t , unsymbolizedAddrs , 2 , "Both unsymbolized locations should be preserved" )
717- require .Contains (t , unsymbolizedAddrs , uint64 (0x999999 ))
718- require .Contains (t , unsymbolizedAddrs , uint64 (0x888888 ))
719- }
0 commit comments