func longNameFuncsProfile() *profile.Profile {
	var longNameFuncsM = []*profile.Mapping{
		{
			ID:              1,
			Start:           0x1000,
			Limit:           0x4000,
			File:            "/path/to/testbinary",
			HasFunctions:    true,
			HasFilenames:    true,
			HasLineNumbers:  true,
			HasInlineFrames: true,
		},
	}
	var longNameFuncsF = []*profile.Function{
		{ID: 1, Name: "path/to/package1.object.function1", SystemName: "path/to/package1.object.function1", Filename: "path/to/package1.go"},
		{ID: 2, Name: "(anonymous namespace)::Bar::Foo", SystemName: "(anonymous namespace)::Bar::Foo", Filename: "a/long/path/to/package2.cc"},
		{ID: 3, Name: "java.bar.foo.FooBar.run(java.lang.Runnable)", SystemName: "java.bar.foo.FooBar.run(java.lang.Runnable)", Filename: "FooBar.java"},
	}
	var longNameFuncsL = []*profile.Location{
		{
			ID:      1000,
			Mapping: longNameFuncsM[0],
			Address: 0x1000,
			Line: []profile.Line{
				{Function: longNameFuncsF[0], Line: 1},
			},
		},
		{
			ID:      2000,
			Mapping: longNameFuncsM[0],
			Address: 0x2000,
			Line: []profile.Line{
				{Function: longNameFuncsF[1], Line: 4},
			},
		},
		{
			ID:      3000,
			Mapping: longNameFuncsM[0],
			Address: 0x3000,
			Line: []profile.Line{
				{Function: longNameFuncsF[2], Line: 9},
			},
		},
	}
	return &profile.Profile{
		PeriodType:    &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
		Period:        1,
		DurationNanos: 10e9,
		SampleType: []*profile.ValueType{
			{Type: "samples", Unit: "count"},
			{Type: "cpu", Unit: "milliseconds"},
		},
		Sample: []*profile.Sample{
			{
				Location: []*profile.Location{longNameFuncsL[0], longNameFuncsL[1], longNameFuncsL[2]},
				Value:    []int64{1000, 1000},
			},
			{
				Location: []*profile.Location{longNameFuncsL[0], longNameFuncsL[1]},
				Value:    []int64{100, 100},
			},
			{
				Location: []*profile.Location{longNameFuncsL[2]},
				Value:    []int64{10, 10},
			},
		},
		Location: longNameFuncsL,
		Function: longNameFuncsF,
		Mapping:  longNameFuncsM,
	}
}
longNameFuncsProfile returns a profile with function names which should be
shortened in graph and flame views.
package driver
import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	_ "net/http/pprof"
	"os"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"testing"
	"time"
	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/internal/proftest"
	"github.com/google/pprof/internal/symbolz"
	"github.com/google/pprof/profile"
)
var updateFlag = flag.Bool("update", false, "Update the golden files")
func TestParse(t *testing.T) {
	pprofCommands["weblist"].postProcess = nil
	savePath := os.Getenv("PPROF_BINARY_PATH")
	os.Setenv("PPROF_BINARY_PATH", "/path/to")
	defer os.Setenv("PPROF_BINARY_PATH", savePath)
	testcase := []struct {
		flags, source string
	}{
		{"text,functions,flat", "cpu"},
		{"text,functions,noinlines,flat", "cpu"},
		{"text,filefunctions,noinlines,flat", "cpu"},
		{"text,addresses,noinlines,flat", "cpu"},
		{"tree,addresses,flat,nodecount=4", "cpusmall"},
		{"text,functions,flat,nodecount=5,call_tree", "unknown"},
		{"text,alloc_objects,flat", "heap_alloc"},
		{"text,files,flat", "heap"},
		{"text,files,flat,focus=[12]00,taghide=[X3]00", "heap"},
		{"text,inuse_objects,flat", "heap"},
		{"text,lines,cum,hide=line[X3]0", "cpu"},
		{"text,lines,cum,show=[12]00", "cpu"},
		{"text,lines,cum,hide=line[X3]0,focus=[12]00", "cpu"},
		{"topproto,lines,cum,hide=mangled[X3]0", "cpu"},
		{"topproto,lines", "cpu"},
		{"tree,lines,cum,focus=[24]00", "heap"},
		{"tree,relative_percentages,cum,focus=[24]00", "heap"},
		{"tree,lines,cum,show_from=line2", "cpu"},
		{"callgrind", "cpu"},
		{"callgrind,call_tree", "cpu"},
		{"callgrind", "heap"},
		{"dot,functions,flat", "cpu"},
		{"dot,functions,flat,call_tree", "cpu"},
		{"dot,lines,flat,focus=[12]00", "heap"},
		{"dot,unit=minimum", "heap_sizetags"},
		{"dot,addresses,flat,ignore=[X3]002,focus=[X1]000", "contention"},
		{"dot,files,cum", "contention"},
		{"comments,add_comment=some-comment", "cpu"},
		{"comments", "heap"},
		{"tags", "cpu"},
		{"tags,tagignore=tag[13],tagfocus=key[12]", "cpu"},
		{"tags", "heap"},
		{"tags,unit=bytes", "heap"},
		{"traces", "cpu"},
		{"traces,addresses", "cpu"},
		{"traces", "heap_tags"},
		{"dot,alloc_space,flat,focus=[234]00", "heap_alloc"},
		{"dot,alloc_space,flat,tagshow=[2]00", "heap_alloc"},
		{"dot,alloc_space,flat,hide=line.*1?23?", "heap_alloc"},
		{"dot,inuse_space,flat,tagfocus=1mb:2gb", "heap"},
		{"dot,inuse_space,flat,tagfocus=30kb:,tagignore=1mb:2mb", "heap"},
		{"disasm=line[13],addresses,flat", "cpu"},
		{"peek=line.*01", "cpu"},
		{"weblist=line(1000|3000)$,addresses,flat", "cpu"},
		{"tags,tagfocus=400kb:", "heap_request"},
		{"tags,tagfocus=+400kb:", "heap_request"},
		{"dot", "long_name_funcs"},
		{"text", "long_name_funcs"},
	}
	baseConfig := currentConfig()
	defer setCurrentConfig(baseConfig)
	for _, tc := range testcase {
		t.Run(tc.flags+":"+tc.source, func(t *testing.T) {
			setCurrentConfig(baseConfig)
			testUI := &proftest.TestUI{T: t, AllowRx: "Generating report in|Ignoring local file|expression matched no samples|Interpreted .* as range, not regexp"}
			f := baseFlags()
			f.args = []string{tc.source}
			flags := strings.Split(tc.flags, ",")
			protoTempFile, err := ioutil.TempFile("", "profile_proto")
			if err != nil {
				t.Errorf("cannot create tempfile: %v", err)
			}
			defer os.Remove(protoTempFile.Name())
			defer protoTempFile.Close()
			f.strings["output"] = protoTempFile.Name()
			if flags[0] == "topproto" {
				f.bools["proto"] = false
				f.bools["topproto"] = true
				f.bools["addresses"] = true
			}
			o1 := setDefaults(&plugin.Options{Flagset: f})
			o1.Fetch = testFetcher{}
			o1.Sym = testSymbolizer{}
			o1.UI = testUI
			if err := PProf(o1); err != nil {
				t.Fatalf("%s %q:  %v", tc.source, tc.flags, err)
			}
			setCurrentConfig(baseConfig)
			outputTempFile, err := ioutil.TempFile("", "profile_output")
			if err != nil {
				t.Errorf("cannot create tempfile: %v", err)
			}
			defer os.Remove(outputTempFile.Name())
			defer outputTempFile.Close()
			f = baseFlags()
			f.strings["output"] = outputTempFile.Name()
			f.args = []string{protoTempFile.Name()}
			delete(f.bools, "proto")
			addFlags(&f, flags)
			solution := solutionFilename(tc.source, &f)
			if flags[0] == "topproto" {
				addFlags(&f, flags)
				solution = solutionFilename(tc.source, &f)
				delete(f.bools, "topproto")
				f.bools["text"] = true
			}
			o2 := setDefaults(&plugin.Options{Flagset: f})
			o2.Sym = testSymbolizeDemangler{}
			o2.Obj = new(mockObjTool)
			o2.UI = testUI
			if err := PProf(o2); err != nil {
				t.Errorf("%s: %v", tc.source, err)
			}
			b, err := ioutil.ReadFile(outputTempFile.Name())
			if err != nil {
				t.Errorf("Failed to read profile %s: %v", outputTempFile.Name(), err)
			}
			solution = "testdata/" + solution
			sbuf, err := ioutil.ReadFile(solution)
			if err != nil {
				t.Fatalf("reading solution file %s: %v", solution, err)
			}
			if runtime.GOOS == "windows" {
				if flags[0] == "dot" {
					sbuf = bytes.Replace(sbuf, []byte("testdata/"), []byte(`testdata\\`), -1)
					sbuf = bytes.Replace(sbuf, []byte("/path/to/"), []byte(`\\path\\to\\`), -1)
				} else {
					sbuf = bytes.Replace(sbuf, []byte("testdata/"), []byte(`testdata\`), -1)
					sbuf = bytes.Replace(sbuf, []byte("/path/to/"), []byte(`\path\to\`), -1)
				}
			}
			if flags[0] == "svg" {
				b = removeScripts(b)
				sbuf = removeScripts(sbuf)
			}
			if string(b) != string(sbuf) {
				t.Errorf("diff %s %s", solution, tc.source)
				d, err := proftest.Diff(sbuf, b)
				if err != nil {
					t.Fatalf("diff %s %v", solution, err)
				}
				t.Errorf("%s\n%s\n", solution, d)
				if *updateFlag {
					err := ioutil.WriteFile(solution, b, 0644)
					if err != nil {
						t.Errorf("failed to update the solution file %q: %v", solution, err)
					}
				}
			}
		})
	}
}
func removeScripts(in []byte) []byte {
	beginMarker := []byte("<script")
	endMarker := []byte("</script>")
	if begin := bytes.Index(in, beginMarker); begin > 0 {
		if end := bytes.Index(in[begin:], endMarker); end > 0 {
			in = append(in[:begin], removeScripts(in[begin+end+len(endMarker):])...)
		}
	}
	return in
}
func addFlags(f *testFlags, flags []string) {
	for _, flag := range flags {
		fields := strings.SplitN(flag, "=", 2)
		switch len(fields) {
		case 1:
			f.bools[fields[0]] = true
		case 2:
			if i, err := strconv.Atoi(fields[1]); err == nil {
				f.ints[fields[0]] = i
			} else {
				f.strings[fields[0]] = fields[1]
			}
		}
	}
}
func testSourceURL(port int) string {
	return fmt.Sprintf("http://%s/", net.JoinHostPort(testSourceAddress, strconv.Itoa(port)))
}
func solutionFilename(source string, f *testFlags) string {
	name := []string{"pprof", strings.TrimPrefix(source, testSourceURL(8000))}
	name = addString(name, f, []string{"flat", "cum"})
	name = addString(name, f, []string{"functions", "filefunctions", "files", "lines", "addresses"})
	name = addString(name, f, []string{"noinlines"})
	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
	name = addString(name, f, []string{"relative_percentages"})
	name = addString(name, f, []string{"seconds"})
	name = addString(name, f, []string{"call_tree"})
	name = addString(name, f, []string{"text", "tree", "callgrind", "dot", "svg", "tags", "dot", "traces", "disasm", "peek", "weblist", "topproto", "comments"})
	if f.strings["focus"] != "" || f.strings["tagfocus"] != "" {
		name = append(name, "focus")
	}
	if f.strings["ignore"] != "" || f.strings["tagignore"] != "" {
		name = append(name, "ignore")
	}
	if f.strings["show_from"] != "" {
		name = append(name, "show_from")
	}
	name = addString(name, f, []string{"hide", "show"})
	if f.strings["unit"] != "minimum" {
		name = addString(name, f, []string{"unit"})
	}
	return strings.Join(name, ".")
}
func addString(name []string, f *testFlags, components []string) []string {
	for _, c := range components {
		if f.bools[c] || f.strings[c] != "" || f.ints[c] != 0 {
			return append(name, c)
		}
	}
	return name
}
type testFlags struct {
	bools       map[string]bool
	ints        map[string]int
	floats      map[string]float64
	strings     map[string]string
	args        []string
	stringLists map[string][]string
}
func (testFlags) ExtraUsage() string { return "" }
func (testFlags) AddExtraUsage(eu string) {}
func (f testFlags) Bool(s string, d bool, c string) *bool {
	if b, ok := f.bools[s]; ok {
		return &b
	}
	return &d
}
func (f testFlags) Int(s string, d int, c string) *int {
	if i, ok := f.ints[s]; ok {
		return &i
	}
	return &d
}
func (f testFlags) Float64(s string, d float64, c string) *float64 {
	if g, ok := f.floats[s]; ok {
		return &g
	}
	return &d
}
func (f testFlags) String(s, d, c string) *string {
	if t, ok := f.strings[s]; ok {
		return &t
	}
	return &d
}
func (f testFlags) StringList(s, d, c string) *[]*string {
	if t, ok := f.stringLists[s]; ok {
		tp := make([]*string, len(t))
		for i, v := range t {
			tp[i] = &v
		}
		return &tp
	}
	return &[]*string{}
}
func (f testFlags) Parse(func()) []string {
	return f.args
}
func baseFlags() testFlags {
	return testFlags{
		bools: map[string]bool{
			"proto":          true,
			"trim":           true,
			"compact_labels": true,
		},
		ints: map[string]int{
			"nodecount": 20,
		},
		floats: map[string]float64{
			"nodefraction": 0.05,
			"edgefraction": 0.01,
			"divide_by":    1.0,
		},
		strings: map[string]string{
			"unit": "minimum",
		},
	}
}
const testStart = 0x1000
const testOffset = 0x5000
type testFetcher struct{}
func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
	var p *profile.Profile
	switch s {
	case "cpu", "unknown":
		p = cpuProfile()
	case "cpusmall":
		p = cpuProfileSmall()
	case "heap":
		p = heapProfile()
	case "heap_alloc":
		p = heapProfile()
		p.SampleType = []*profile.ValueType{
			{Type: "alloc_objects", Unit: "count"},
			{Type: "alloc_space", Unit: "bytes"},
		}
	case "heap_request":
		p = heapProfile()
		for _, s := range p.Sample {
			s.NumLabel["request"] = s.NumLabel["bytes"]
		}
	case "heap_sizetags":
		p = heapProfile()
		tags := []int64{2, 4, 8, 16, 32, 64, 128, 256}
		for _, s := range p.Sample {
			numValues := append(s.NumLabel["bytes"], tags...)
			s.NumLabel["bytes"] = numValues
		}
	case "heap_tags":
		p = heapProfile()
		for i := 0; i < len(p.Sample); i += 2 {
			s := p.Sample[i]
			if s.Label == nil {
				s.Label = make(map[string][]string)
			}
			s.NumLabel["request"] = s.NumLabel["bytes"]
			s.Label["key1"] = []string{"tag"}
		}
	case "contention":
		p = contentionProfile()
	case "symbolz":
		p = symzProfile()
	case "long_name_funcs":
		p = longNameFuncsProfile()
	default:
		return nil, "", fmt.Errorf("unexpected source: %s", s)
	}
	return p, testSourceURL(8000) + s, nil
}
type testSymbolizer struct{}
func (testSymbolizer) Symbolize(_ string, _ plugin.MappingSources, _ *profile.Profile) error {
	return nil
}
type testSymbolizeDemangler struct{}
func (testSymbolizeDemangler) Symbolize(_ string, _ plugin.MappingSources, p *profile.Profile) error {
	for _, fn := range p.Function {
		if fn.Name == "" || fn.SystemName == fn.Name {
			fn.Name = fakeDemangler(fn.SystemName)
		}
	}
	return nil
}
func testFetchSymbols(source, post string) ([]byte, error) {
	var buf bytes.Buffer
	switch source {
	case testSourceURL(8000) + "symbolz":
		for _, address := range strings.Split(post, "+") {
			a, _ := strconv.ParseInt(address, 0, 64)
			fmt.Fprintf(&buf, "%v\t", address)
			if a-testStart > testOffset {
				fmt.Fprintf(&buf, "wrong_source_%v_", address)
				continue
			}
			fmt.Fprintf(&buf, "%#x\n", a-testStart)
		}
		return buf.Bytes(), nil
	case testSourceURL(8001) + "symbolz":
		for _, address := range strings.Split(post, "+") {
			a, _ := strconv.ParseInt(address, 0, 64)
			fmt.Fprintf(&buf, "%v\t", address)
			if a-testStart < testOffset {
				fmt.Fprintf(&buf, "wrong_source_%v_", address)
				continue
			}
			fmt.Fprintf(&buf, "%#x\n", a-testStart-testOffset)
		}
		return buf.Bytes(), nil
	default:
		return nil, fmt.Errorf("unexpected source: %s", source)
	}
}
type testSymbolzSymbolizer struct{}
func (testSymbolzSymbolizer) Symbolize(variables string, sources plugin.MappingSources, p *profile.Profile) error {
	return symbolz.Symbolize(p, false, sources, testFetchSymbols, nil)
}
func fakeDemangler(name string) string {
	switch name {
	case "mangled1000":
		return "line1000"
	case "mangled2000":
		return "line2000"
	case "mangled2001":
		return "line2001"
	case "mangled3000":
		return "line3000"
	case "mangled3001":
		return "line3001"
	case "mangled3002":
		return "line3002"
	case "mangledNEW":
		return "operator new"
	case "mangledMALLOC":
		return "malloc"
	default:
		return name
	}
}