github.com/grafana/pyroscope@v1.18.0/pkg/model/flamegraph_test.go (about)

     1  package model
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/grafana/pyroscope/pkg/og/structs/flamebearer"
    10  
    11  	querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1"
    12  	typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1"
    13  )
    14  
    15  func Test_ExportToFlamebearer(t *testing.T) {
    16  	pType := &typesv1.ProfileType{
    17  		ID:         "memory:inuse_space:bytes:space:bytes",
    18  		Name:       "memory",
    19  		SampleType: "inuse_space",
    20  		SampleUnit: "bytes",
    21  		PeriodType: "space",
    22  		PeriodUnit: "bytes",
    23  	}
    24  	expected := &flamebearer.FlamebearerProfile{
    25  		Version: 1,
    26  		FlamebearerProfileV1: flamebearer.FlamebearerProfileV1{
    27  			Metadata: flamebearer.FlamebearerMetadataV1{
    28  				Format:     "single",
    29  				Units:      "bytes",
    30  				Name:       "inuse_space",
    31  				SampleRate: 100,
    32  			},
    33  			Flamebearer: flamebearer.FlamebearerV1{
    34  				Names: []string{"total", "a", "c", "d", "b", "e"},
    35  				Levels: [][]int{
    36  					{0, 4, 0, 0},
    37  					{0, 4, 0, 1},
    38  					{0, 1, 0, 4, 0, 3, 2, 2},
    39  					{0, 1, 1, 5, 2, 1, 1, 3},
    40  				},
    41  				NumTicks: 4,
    42  				MaxSelf:  2,
    43  			},
    44  		},
    45  	}
    46  	actual := ExportToFlamebearer(
    47  		NewFlameGraph(
    48  			newTree([]stacktraces{
    49  				{
    50  					locations: []string{"e", "b", "a"},
    51  					value:     1,
    52  				},
    53  				{
    54  					locations: []string{"c", "a"},
    55  					value:     2,
    56  				},
    57  				{
    58  					locations: []string{"d", "c", "a"},
    59  					value:     1,
    60  				},
    61  			}),
    62  			-1,
    63  		), pType)
    64  	require.Equal(t, expected, actual)
    65  
    66  	t.Run("nil", func(t *testing.T) {
    67  		require.Equal(t, &flamebearer.FlamebearerProfile{
    68  			Version: 1,
    69  			FlamebearerProfileV1: flamebearer.FlamebearerProfileV1{
    70  				Metadata: flamebearer.FlamebearerMetadataV1{
    71  					Format:     "single",
    72  					SampleRate: 100,
    73  					Units:      "bytes",
    74  					Name:       "inuse_space",
    75  				},
    76  				Flamebearer: flamebearer.FlamebearerV1{
    77  					Levels: [][]int{},
    78  				},
    79  			},
    80  		}, ExportToFlamebearer(nil, pType))
    81  	})
    82  }
    83  
    84  var f *querierv1.FlameGraph
    85  
    86  func BenchmarkFlamegraph(b *testing.B) {
    87  	stacks := make([]stacktraces, 2000)
    88  	for i := range stacks {
    89  		stacks[i] = stacktraces{
    90  			locations: []string{"a", "b", "c", "d", "e", fmt.Sprintf("%d", i)},
    91  			value:     1,
    92  		}
    93  	}
    94  	tr := newTree(stacks)
    95  	b.ResetTimer()
    96  	b.ReportAllocs()
    97  	for i := 0; i < b.N; i++ {
    98  		f = NewFlameGraph(tr, -1)
    99  	}
   100  }
   101  
   102  func Test_FlameGraphMerger(t *testing.T) {
   103  	t.Run("two non-empty flamegraphs", func(t *testing.T) {
   104  		m := NewFlameGraphMerger()
   105  		s := new(Tree)
   106  		s.InsertStack(1, "foo", "bar")
   107  		s.InsertStack(1, "foo", "bar", "baz")
   108  		s.InsertStack(2, "foo", "qux")
   109  		s.InsertStack(1, "fred", "zoo")
   110  		s.InsertStack(1, "fred", "other")
   111  		m.MergeFlameGraph(NewFlameGraph(s, -1))
   112  
   113  		s = new(Tree)
   114  		s.InsertStack(1, "foo", "bar", "baz")
   115  		s.InsertStack(1, "fred", "zoo")
   116  		s.InsertStack(1, "fred", "zoo", "ward")
   117  		s.InsertStack(1, "func", "other")
   118  		s.InsertStack(1, "func")
   119  		s.InsertStack(1, "other")
   120  		m.MergeFlameGraph(NewFlameGraph(s, -1))
   121  
   122  		expected := new(Tree)
   123  		expected.InsertStack(1, "foo", "bar")
   124  		expected.InsertStack(1, "foo", "bar", "baz")
   125  		expected.InsertStack(2, "foo", "qux")
   126  		expected.InsertStack(1, "fred", "zoo")
   127  		expected.InsertStack(1, "fred", "other")
   128  		expected.InsertStack(1, "foo", "bar", "baz")
   129  		expected.InsertStack(1, "fred", "zoo")
   130  		expected.InsertStack(1, "fred", "zoo", "ward")
   131  		expected.InsertStack(1, "func", "other")
   132  		expected.InsertStack(1, "func")
   133  		expected.InsertStack(1, "other")
   134  
   135  		require.Equal(t, expected.String(), m.Tree().String())
   136  	})
   137  
   138  	t.Run("non-empty flamegraph result truncation", func(t *testing.T) {
   139  		m := NewFlameGraphMerger()
   140  		s := new(Tree)
   141  		s.InsertStack(1, "foo", "bar")
   142  		s.InsertStack(1, "foo", "bar", "baz")
   143  		s.InsertStack(2, "foo", "qux")
   144  		s.InsertStack(1, "fred", "zoo")
   145  		s.InsertStack(1, "fred", "other")
   146  		m.MergeFlameGraph(NewFlameGraph(s, -1))
   147  
   148  		fg := m.FlameGraph(4)
   149  		m = NewFlameGraphMerger()
   150  		m.MergeFlameGraph(fg)
   151  
   152  		expected := new(Tree)
   153  		expected.InsertStack(1, "foo", "bar")
   154  		expected.InsertStack(1, "foo", "bar", "other")
   155  		expected.InsertStack(2, "foo", "qux")
   156  		expected.InsertStack(2, "fred", "other")
   157  
   158  		require.Equal(t, expected.String(), m.Tree().String())
   159  	})
   160  
   161  	t.Run("empty flamegraph", func(t *testing.T) {
   162  		m := NewFlameGraphMerger()
   163  		m.MergeFlameGraph(NewFlameGraph(new(Tree), -1))
   164  		require.Equal(t, new(Tree).String(), m.Tree().String())
   165  	})
   166  
   167  	t.Run("no flamegraphs", func(t *testing.T) {
   168  		m := NewFlameGraphMerger()
   169  		require.Equal(t, new(Tree).String(), m.Tree().String())
   170  	})
   171  }