blob: 28cf6b4ce38e510de72cb623a3956a038a1ac4bb [file] [log] [blame]
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package report
import (
"bytes"
"io/ioutil"
"regexp"
"runtime"
"testing"
"github.com/google/pprof/internal/binutils"
"github.com/google/pprof/internal/graph"
"github.com/google/pprof/internal/proftest"
"github.com/google/pprof/profile"
)
type testcase struct {
rpt *Report
want string
}
func TestSource(t *testing.T) {
const path = "testdata/"
sampleValue1 := func(v []int64) int64 {
return v[1]
}
for _, tc := range []testcase{
{
rpt: New(
testProfile.Copy(),
&Options{
OutputFormat: List,
Symbol: regexp.MustCompile(`.`),
SampleValue: sampleValue1,
SampleUnit: testProfile.SampleType[1].Unit,
},
),
want: path + "source.rpt",
},
{
rpt: New(
testProfile.Copy(),
&Options{
OutputFormat: Dot,
CallTree: true,
Symbol: regexp.MustCompile(`.`),
SampleValue: sampleValue1,
SampleUnit: testProfile.SampleType[1].Unit,
},
),
want: path + "source.dot",
},
} {
b := bytes.NewBuffer(nil)
if err := Generate(b, tc.rpt, &binutils.Binutils{}); err != nil {
t.Fatalf("%s: %v", tc.want, err)
}
gold, err := ioutil.ReadFile(tc.want)
if err != nil {
t.Fatalf("%s: %v", tc.want, err)
}
if runtime.GOOS == "windows" {
gold = bytes.Replace(gold, []byte("testdata/"), []byte("testdata\\"), -1)
}
if string(b.String()) != string(gold) {
d, err := proftest.Diff(gold, b.Bytes())
if err != nil {
t.Fatalf("%s: %v", "source", err)
}
t.Error("source" + "\n" + string(d) + "\n" + "gold:\n" + tc.want)
}
}
}
var testM = []*profile.Mapping{
{
ID: 1,
HasFunctions: true,
HasFilenames: true,
HasLineNumbers: true,
HasInlineFrames: true,
},
}
var testF = []*profile.Function{
{
ID: 1,
Name: "main",
Filename: "testdata/source1",
},
{
ID: 2,
Name: "foo",
Filename: "testdata/source1",
},
{
ID: 3,
Name: "bar",
Filename: "testdata/source1",
},
{
ID: 4,
Name: "tee",
Filename: "testdata/source2",
},
}
var testL = []*profile.Location{
{
ID: 1,
Mapping: testM[0],
Line: []profile.Line{
{
Function: testF[0],
Line: 2,
},
},
},
{
ID: 2,
Mapping: testM[0],
Line: []profile.Line{
{
Function: testF[1],
Line: 4,
},
},
},
{
ID: 3,
Mapping: testM[0],
Line: []profile.Line{
{
Function: testF[2],
Line: 10,
},
},
},
{
ID: 4,
Mapping: testM[0],
Line: []profile.Line{
{
Function: testF[3],
Line: 2,
},
},
},
{
ID: 5,
Mapping: testM[0],
Line: []profile.Line{
{
Function: testF[3],
Line: 8,
},
},
},
}
var testProfile = &profile.Profile{
PeriodType: &profile.ValueType{Type: "cpu", Unit: "millisecond"},
Period: 10,
DurationNanos: 10e9,
SampleType: []*profile.ValueType{
{Type: "samples", Unit: "count"},
{Type: "cpu", Unit: "cycles"},
},
Sample: []*profile.Sample{
{
Location: []*profile.Location{testL[0]},
Value: []int64{1, 1},
},
{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{1, 10},
},
{
Location: []*profile.Location{testL[4], testL[2], testL[0]},
Value: []int64{1, 100},
},
{
Location: []*profile.Location{testL[3], testL[0]},
Value: []int64{1, 1000},
},
{
Location: []*profile.Location{testL[4], testL[3], testL[0]},
Value: []int64{1, 10000},
},
},
Location: testL,
Function: testF,
Mapping: testM,
}
func TestDisambiguation(t *testing.T) {
parent1 := &graph.Node{Info: graph.NodeInfo{Name: "parent1"}}
parent2 := &graph.Node{Info: graph.NodeInfo{Name: "parent2"}}
child1 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent1}
child2 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent2}
child3 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent1}
sibling := &graph.Node{Info: graph.NodeInfo{Name: "sibling"}, Function: parent1}
n := []*graph.Node{parent1, parent2, child1, child2, child3, sibling}
wanted := map[*graph.Node]string{
parent1: "parent1",
parent2: "parent2",
child1: "child [1/2]",
child2: "child [2/2]",
child3: "child [1/2]",
sibling: "sibling",
}
g := &graph.Graph{Nodes: n}
names := getDisambiguatedNames(g)
for node, want := range wanted {
if got := names[node]; got != want {
t.Errorf("name %s, got %s, want %s", node.Info.Name, got, want)
}
}
}
func TestFunctionMap(t *testing.T) {
fm := make(functionMap)
nodes := []graph.NodeInfo{
{Name: "fun1"},
{Name: "fun2", File: "filename"},
{Name: "fun1"},
{Name: "fun2", File: "filename2"},
}
want := []profile.Function{
{ID: 1, Name: "fun1"},
{ID: 2, Name: "fun2", Filename: "filename"},
{ID: 1, Name: "fun1"},
{ID: 3, Name: "fun2", Filename: "filename2"},
}
for i, tc := range nodes {
if got, want := fm.FindOrAdd(tc), want[i]; *got != want {
t.Errorf("%d: want %v, got %v", i, want, got)
}
}
}