github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/symdb/stacktrace_tree_test.go (about) 1 package symdb 2 3 import ( 4 "bytes" 5 "math/rand" 6 "strconv" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 "github.com/grafana/pyroscope/pkg/pprof" 13 ) 14 15 func Test_stacktrace_tree_encoding(t *testing.T) { 16 stacks := [][]uint64{ 17 {5, 4, 3, 2, 1}, 18 {6, 4, 3, 2, 1}, 19 {4, 3, 2, 1}, 20 {3, 2, 1}, 21 {4, 2, 1}, 22 {7, 2, 1}, 23 {2, 1}, 24 {1}, 25 } 26 27 x := newStacktraceTree(10) 28 var b bytes.Buffer 29 30 for i := range stacks { 31 x.insert(stacks[i]) 32 33 b.Reset() 34 _, err := x.WriteTo(&b) 35 require.NoError(t, err) 36 37 ppt := newParentPointerTree(x.len()) 38 _, err = ppt.ReadFrom(bytes.NewBuffer(b.Bytes())) 39 require.NoError(t, err) 40 41 for j := range x.nodes { 42 n, p := x.nodes[j], ppt.nodes[j] 43 if n.p != p.p || n.r != p.r { 44 t.Fatalf("tree mismatch on %v: n:%#v, p:%#v", stacks[i], n, p) 45 } 46 } 47 } 48 } 49 50 func Test_stacktrace_tree_encoding_group(t *testing.T) { 51 stacks := [][]uint64{ 52 {5, 4, 3, 2, 1}, 53 {6, 4, 3, 2, 1}, 54 {4, 3, 2, 1}, 55 {3, 2, 1}, 56 {4, 2, 1}, 57 {7, 2, 1}, 58 {2, 1}, 59 {1}, 60 } 61 62 x := newStacktraceTree(10) 63 var b bytes.Buffer 64 65 for i := range stacks { 66 x.insert(stacks[i]) 67 68 b.Reset() 69 e := treeEncoder{writeSize: 30} 70 err := e.marshal(x, &b) 71 require.NoError(t, err) 72 73 ppt := newParentPointerTree(x.len()) 74 d := treeDecoder{ 75 bufSize: 64, 76 peekSize: 20, 77 groupBuffer: 12, 78 } 79 err = d.unmarshal(ppt, bytes.NewBuffer(b.Bytes())) 80 require.NoError(t, err) 81 82 for j := range x.nodes { 83 n, p := x.nodes[j], ppt.nodes[j] 84 if n.p != p.p || n.r != p.r { 85 t.Fatalf("tree mismatch on %v: n:%#v, p:%#v", stacks[i], n, p) 86 } 87 } 88 } 89 } 90 91 func Test_stacktrace_tree_encoding_rand(t *testing.T) { 92 nodes := make([]node, 1<<20) 93 for i := range nodes { 94 nodes[i] = node{ 95 fc: 2, 96 ns: 3, 97 p: int32(rand.Intn(10 << 10)), 98 r: int32(rand.Intn(10 << 10)), 99 } 100 } 101 102 x := &stacktraceTree{nodes: nodes} 103 var b bytes.Buffer 104 _, err := x.WriteTo(&b) 105 require.NoError(t, err) 106 107 ppt := newParentPointerTree(x.len()) 108 _, err = ppt.ReadFrom(bytes.NewBuffer(b.Bytes())) 109 require.NoError(t, err) 110 111 for j := range x.nodes { 112 n, p := x.nodes[j], ppt.nodes[j] 113 if n.p != p.p || n.r != p.r { 114 t.Fatalf("tree mismatch at %d: n:%#v. p:%#v", j, n, p) 115 } 116 } 117 } 118 119 func Test_stacktrace_tree_pprof_locations_(t *testing.T) { 120 x := newStacktraceTree(0) 121 assert.Len(t, x.resolve([]int32{0, 1, 2, 3}, 42), 0) 122 assert.Len(t, x.resolveUint64([]uint64{0, 1, 2, 3}, 42), 0) 123 124 p := newParentPointerTree(0) 125 assert.Len(t, p.resolve([]int32{0, 1, 2, 3}, 42), 0) 126 assert.Len(t, p.resolveUint64([]uint64{0, 1, 2, 3}, 42), 0) 127 } 128 129 func Test_stacktrace_tree_pprof_locations(t *testing.T) { 130 p, err := pprof.OpenFile("testdata/profile.pb.gz") 131 require.NoError(t, err) 132 133 x := newStacktraceTree(defaultStacktraceTreeSize) 134 m := make(map[uint32]int) 135 for i := range p.Sample { 136 m[x.insert(p.Sample[i].LocationId)] = i 137 } 138 139 tmp := stacktraceLocations.get() 140 defer stacktraceLocations.put(tmp) 141 for sid, i := range m { 142 tmp = x.resolve(tmp, sid) 143 locs := p.Sample[i].LocationId 144 for j := range locs { 145 if tmp[j] != int32(locs[j]) { 146 t.Log("resolved:", tmp) 147 t.Log("locations:", locs) 148 t.Fatalf("ST: tmp[j] != locs[j]") 149 } 150 } 151 } 152 153 var b bytes.Buffer 154 n, err := x.WriteTo(&b) 155 require.NoError(t, err) 156 assert.Equal(t, b.Len(), int(n)) 157 158 ppt := newParentPointerTree(x.len()) 159 n, err = ppt.ReadFrom(bytes.NewReader(b.Bytes())) 160 require.NoError(t, err) 161 assert.Equal(t, b.Len(), int(n)) 162 163 tmp = stacktraceLocations.get() 164 defer stacktraceLocations.put(tmp) 165 for sid, i := range m { 166 tmp = ppt.resolve(tmp, sid) 167 locs := p.Sample[i].LocationId 168 for j := range locs { 169 if tmp[j] != int32(locs[j]) { 170 t.Log("resolved:", tmp) 171 t.Log("locations:", locs) 172 t.Fatalf("PPT: tmp[j] != locs[j]") 173 } 174 } 175 } 176 } 177 178 // The test is helpful for debugging. 179 func Test_parentPointerTree_toStacktraceTree(t *testing.T) { 180 x := newStacktraceTree(10) 181 for _, stack := range [][]uint64{ 182 {5, 4, 3, 2, 1}, 183 {6, 4, 3, 2, 1}, 184 {4, 3, 2, 1}, 185 {3, 2, 1}, 186 {4, 2, 1}, 187 {7, 2, 1}, 188 {2, 1}, 189 {1}, 190 } { 191 x.insert(stack) 192 } 193 assertRestoredStacktraceTree(t, x) 194 } 195 196 func Test_parentPointerTree_toStacktraceTree_profile(t *testing.T) { 197 p, err := pprof.OpenFile("testdata/profile.pb.gz") 198 require.NoError(t, err) 199 x := newStacktraceTree(defaultStacktraceTreeSize) 200 for _, s := range p.Sample { 201 x.insert(s.LocationId) 202 } 203 assertRestoredStacktraceTree(t, x) 204 } 205 206 func assertRestoredStacktraceTree(t *testing.T, x *stacktraceTree) { 207 var b bytes.Buffer 208 _, _ = x.WriteTo(&b) 209 ppt := newParentPointerTree(x.len()) 210 _, err := ppt.ReadFrom(bytes.NewBuffer(b.Bytes())) 211 require.NoError(t, err) 212 restored := ppt.toStacktraceTree() 213 assert.Equal(t, x.nodes, restored.nodes) 214 } 215 216 func Benchmark_stacktrace_tree_insert(b *testing.B) { 217 p, err := pprof.OpenFile("testdata/profile.pb.gz") 218 require.NoError(b, err) 219 220 b.ResetTimer() 221 b.ReportAllocs() 222 223 for i := 0; i < b.N; i++ { 224 x := newStacktraceTree(defaultStacktraceTreeSize) 225 for j := range p.Sample { 226 x.insert(p.Sample[j].LocationId) 227 } 228 } 229 } 230 231 func Benchmark_stacktrace_tree_insert_default_sizes(b *testing.B) { 232 p, err := pprof.OpenFile("testdata/profile.pb.gz") 233 require.NoError(b, err) 234 235 b.ResetTimer() 236 237 for _, size := range []int{0, 10, 1024, 2048, 4096, 8192} { 238 b.Run("size="+strconv.Itoa(size), func(b *testing.B) { 239 b.ReportAllocs() 240 241 for i := 0; i < b.N; i++ { 242 x := newStacktraceTree(size) 243 for j := range p.Sample { 244 x.insert(p.Sample[j].LocationId) 245 } 246 247 if testing.Verbose() { 248 c := float64(cap(x.nodes)) 249 b.ReportMetric(c, "cap") 250 b.ReportMetric(c*float64(stacktraceTreeNodeSize), "size") 251 b.ReportMetric(float64(x.len())/float64(c)*100, "fill") 252 } 253 } 254 }) 255 } 256 }