Home | History | Annotate | Download | only in report
      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