1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package report 16 17 import ( 18 "bytes" 19 "io/ioutil" 20 "regexp" 21 "runtime" 22 "testing" 23 24 "github.com/google/pprof/internal/binutils" 25 "github.com/google/pprof/internal/graph" 26 "github.com/google/pprof/internal/proftest" 27 "github.com/google/pprof/profile" 28 ) 29 30 type testcase struct { 31 rpt *Report 32 want string 33 } 34 35 func TestSource(t *testing.T) { 36 const path = "testdata/" 37 38 sampleValue1 := func(v []int64) int64 { 39 return v[1] 40 } 41 42 for _, tc := range []testcase{ 43 { 44 rpt: New( 45 testProfile.Copy(), 46 &Options{ 47 OutputFormat: List, 48 Symbol: regexp.MustCompile(`.`), 49 50 SampleValue: sampleValue1, 51 SampleUnit: testProfile.SampleType[1].Unit, 52 }, 53 ), 54 want: path + "source.rpt", 55 }, 56 { 57 rpt: New( 58 testProfile.Copy(), 59 &Options{ 60 OutputFormat: Dot, 61 CallTree: true, 62 Symbol: regexp.MustCompile(`.`), 63 64 SampleValue: sampleValue1, 65 SampleUnit: testProfile.SampleType[1].Unit, 66 }, 67 ), 68 want: path + "source.dot", 69 }, 70 } { 71 b := bytes.NewBuffer(nil) 72 if err := Generate(b, tc.rpt, &binutils.Binutils{}); err != nil { 73 t.Fatalf("%s: %v", tc.want, err) 74 } 75 76 gold, err := ioutil.ReadFile(tc.want) 77 if err != nil { 78 t.Fatalf("%s: %v", tc.want, err) 79 } 80 if runtime.GOOS == "windows" { 81 gold = bytes.Replace(gold, []byte("testdata/"), []byte("testdata\\"), -1) 82 } 83 if string(b.String()) != string(gold) { 84 d, err := proftest.Diff(gold, b.Bytes()) 85 if err != nil { 86 t.Fatalf("%s: %v", "source", err) 87 } 88 t.Error("source" + "\n" + string(d) + "\n" + "gold:\n" + tc.want) 89 } 90 } 91 } 92 93 var testM = []*profile.Mapping{ 94 { 95 ID: 1, 96 HasFunctions: true, 97 HasFilenames: true, 98 HasLineNumbers: true, 99 HasInlineFrames: true, 100 }, 101 } 102 103 var testF = []*profile.Function{ 104 { 105 ID: 1, 106 Name: "main", 107 Filename: "testdata/source1", 108 }, 109 { 110 ID: 2, 111 Name: "foo", 112 Filename: "testdata/source1", 113 }, 114 { 115 ID: 3, 116 Name: "bar", 117 Filename: "testdata/source1", 118 }, 119 { 120 ID: 4, 121 Name: "tee", 122 Filename: "testdata/source2", 123 }, 124 } 125 126 var testL = []*profile.Location{ 127 { 128 ID: 1, 129 Mapping: testM[0], 130 Line: []profile.Line{ 131 { 132 Function: testF[0], 133 Line: 2, 134 }, 135 }, 136 }, 137 { 138 ID: 2, 139 Mapping: testM[0], 140 Line: []profile.Line{ 141 { 142 Function: testF[1], 143 Line: 4, 144 }, 145 }, 146 }, 147 { 148 ID: 3, 149 Mapping: testM[0], 150 Line: []profile.Line{ 151 { 152 Function: testF[2], 153 Line: 10, 154 }, 155 }, 156 }, 157 { 158 ID: 4, 159 Mapping: testM[0], 160 Line: []profile.Line{ 161 { 162 Function: testF[3], 163 Line: 2, 164 }, 165 }, 166 }, 167 { 168 ID: 5, 169 Mapping: testM[0], 170 Line: []profile.Line{ 171 { 172 Function: testF[3], 173 Line: 8, 174 }, 175 }, 176 }, 177 } 178 179 var testProfile = &profile.Profile{ 180 PeriodType: &profile.ValueType{Type: "cpu", Unit: "millisecond"}, 181 Period: 10, 182 DurationNanos: 10e9, 183 SampleType: []*profile.ValueType{ 184 {Type: "samples", Unit: "count"}, 185 {Type: "cpu", Unit: "cycles"}, 186 }, 187 Sample: []*profile.Sample{ 188 { 189 Location: []*profile.Location{testL[0]}, 190 Value: []int64{1, 1}, 191 }, 192 { 193 Location: []*profile.Location{testL[2], testL[1], testL[0]}, 194 Value: []int64{1, 10}, 195 }, 196 { 197 Location: []*profile.Location{testL[4], testL[2], testL[0]}, 198 Value: []int64{1, 100}, 199 }, 200 { 201 Location: []*profile.Location{testL[3], testL[0]}, 202 Value: []int64{1, 1000}, 203 }, 204 { 205 Location: []*profile.Location{testL[4], testL[3], testL[0]}, 206 Value: []int64{1, 10000}, 207 }, 208 }, 209 Location: testL, 210 Function: testF, 211 Mapping: testM, 212 } 213 214 func TestDisambiguation(t *testing.T) { 215 parent1 := &graph.Node{Info: graph.NodeInfo{Name: "parent1"}} 216 parent2 := &graph.Node{Info: graph.NodeInfo{Name: "parent2"}} 217 child1 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent1} 218 child2 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent2} 219 child3 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent1} 220 sibling := &graph.Node{Info: graph.NodeInfo{Name: "sibling"}, Function: parent1} 221 222 n := []*graph.Node{parent1, parent2, child1, child2, child3, sibling} 223 224 wanted := map[*graph.Node]string{ 225 parent1: "parent1", 226 parent2: "parent2", 227 child1: "child [1/2]", 228 child2: "child [2/2]", 229 child3: "child [1/2]", 230 sibling: "sibling", 231 } 232 233 g := &graph.Graph{Nodes: n} 234 235 names := getDisambiguatedNames(g) 236 237 for node, want := range wanted { 238 if got := names[node]; got != want { 239 t.Errorf("name %s, got %s, want %s", node.Info.Name, got, want) 240 } 241 } 242 } 243 244 func TestFunctionMap(t *testing.T) { 245 246 fm := make(functionMap) 247 nodes := []graph.NodeInfo{ 248 {Name: "fun1"}, 249 {Name: "fun2", File: "filename"}, 250 {Name: "fun1"}, 251 {Name: "fun2", File: "filename2"}, 252 } 253 254 want := []profile.Function{ 255 {ID: 1, Name: "fun1"}, 256 {ID: 2, Name: "fun2", Filename: "filename"}, 257 {ID: 1, Name: "fun1"}, 258 {ID: 3, Name: "fun2", Filename: "filename2"}, 259 } 260 261 for i, tc := range nodes { 262 if got, want := fm.FindOrAdd(tc), want[i]; *got != want { 263 t.Errorf("%d: want %v, got %v", i, want, got) 264 } 265 } 266 } 267 268 func TestLegendActiveFilters(t *testing.T) { 269 activeFilterInput := []string{ 270 "focus=123|456|789|101112|131415|161718|192021|222324|252627|282930|313233|343536|363738|acbdefghijklmnop", 271 "show=short filter", 272 } 273 expectedLegendActiveFilter := []string{ 274 "Active filters:", 275 " focus=123|456|789|101112|131415|161718|192021|222324|252627|282930|313233|343536", 276 " show=short filter", 277 } 278 legendActiveFilter := legendActiveFilters(activeFilterInput) 279 if len(legendActiveFilter) != len(expectedLegendActiveFilter) { 280 t.Errorf("wanted length %v got length %v", len(expectedLegendActiveFilter), len(legendActiveFilter)) 281 } 282 for i := range legendActiveFilter { 283 if legendActiveFilter[i] != expectedLegendActiveFilter[i] { 284 t.Errorf("%d: want \"%v\", got \"%v\"", i, expectedLegendActiveFilter[i], legendActiveFilter[i]) 285 } 286 } 287 } 288