aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal/driver
diff options
context:
space:
mode:
authorHana (Hyang-Ah) Kim <hyangah@gmail.com>2018-11-06 17:48:08 -0500
committerHyang-Ah Hana Kim <hyangah@gmail.com>2018-11-07 12:27:21 +0000
commitc0a40e4fe5d4649672d0d430ca26551841fc4852 (patch)
tree4ccd82b68f0795d68bc43caff3cba7c9fabdb4d8 /src/cmd/vendor/github.com/google/pprof/internal/driver
parent1100df58238a1b1c55af148a880b48caf4be6504 (diff)
downloadgo-c0a40e4fe5d4649672d0d430ca26551841fc4852.tar.xz
cmd/vendor: update github.com/google/pprof
Sync @ fde099a (Oct 26, 2018) Also update misc/nacl/testzip.proto to include new testdata. Change-Id: If41590be9f395a591056e89a417b589c4ba71b1a Reviewed-on: https://go-review.googlesource.com/c/147979 Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/driver')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go7
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go38
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go64
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go117
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go60
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go165
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go78
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go18
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go46
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go19
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/options.go64
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text7
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text5
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text5
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto3
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot9
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text5
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go84
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go26
19 files changed, 524 insertions, 296 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
index a5153e1511..dfedf9d849 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go
@@ -45,8 +45,8 @@ type source struct {
func parseFlags(o *plugin.Options) (*source, []string, error) {
flag := o.Flagset
// Comparisons.
- flagBase := flag.StringList("base", "", "Source for base profile for profile subtraction")
- flagDiffBase := flag.StringList("diff_base", "", "Source for diff base profile for comparison")
+ flagDiffBase := flag.StringList("diff_base", "", "Source of base profile for comparison")
+ flagBase := flag.StringList("base", "", "Source of base profile for profile subtraction")
// Source options.
flagSymbolize := flag.String("symbolize", "", "Options for profile symbolization")
flagBuildID := flag.String("buildid", "", "Override build id for first mapping")
@@ -312,7 +312,8 @@ var usageMsgSrc = "\n\n" +
" -buildid Override build id for main binary\n" +
" -add_comment Free-form annotation to add to the profile\n" +
" Displayed on some reports or with pprof -comments\n" +
- " -base source Source of profile to use as baseline\n" +
+ " -diff_base source Source of base profile for comparison\n" +
+ " -base source Source of base profile for profile subtraction\n" +
" profile.pb.gz Profile in compressed protobuf format\n" +
" legacy_profile Profile in legacy pprof format\n" +
" http://host/profile URL for profile handler to retrieve\n" +
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
index 91d32d1e71..ab073d878d 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/commands.go
@@ -228,17 +228,17 @@ var pprofVariables = variables{
// Output granularity
"functions": &variable{boolKind, "t", "granularity", helpText(
"Aggregate at the function level.",
- "Takes into account the filename/lineno where the function was defined.")},
+ "Ignores the filename where the function was defined.")},
+ "filefunctions": &variable{boolKind, "t", "granularity", helpText(
+ "Aggregate at the function level.",
+ "Takes into account the filename where the function was defined.")},
"files": &variable{boolKind, "f", "granularity", "Aggregate at the file level."},
"lines": &variable{boolKind, "f", "granularity", "Aggregate at the source code line level."},
"addresses": &variable{boolKind, "f", "granularity", helpText(
- "Aggregate at the function level.",
+ "Aggregate at the address level.",
"Includes functions' addresses in the output.")},
- "noinlines": &variable{boolKind, "f", "granularity", helpText(
- "Aggregate at the function level.",
- "Attributes inlined functions to their first out-of-line caller.")},
- "addressnoinlines": &variable{boolKind, "f", "granularity", helpText(
- "Aggregate at the function level, including functions' addresses in the output.",
+ "noinlines": &variable{boolKind, "f", "", helpText(
+ "Ignore inlines.",
"Attributes inlined functions to their first out-of-line caller.")},
}
@@ -337,21 +337,27 @@ func listHelp(c string, redirect bool) string {
// browsers returns a list of commands to attempt for web visualization.
func browsers() []string {
- cmds := []string{"chrome", "google-chrome", "firefox"}
+ var cmds []string
+ if userBrowser := os.Getenv("BROWSER"); userBrowser != "" {
+ cmds = append(cmds, userBrowser)
+ }
switch runtime.GOOS {
case "darwin":
- return append(cmds, "/usr/bin/open")
+ cmds = append(cmds, "/usr/bin/open")
case "windows":
- return append(cmds, "cmd /c start")
+ cmds = append(cmds, "cmd /c start")
default:
- userBrowser := os.Getenv("BROWSER")
- if userBrowser != "" {
- cmds = append([]string{userBrowser, "sensible-browser"}, cmds...)
- } else {
- cmds = append([]string{"sensible-browser"}, cmds...)
+ // Commands opening browsers are prioritized over xdg-open, so browser()
+ // command can be used on linux to open the .svg file generated by the -web
+ // command (the .svg file includes embedded javascript so is best viewed in
+ // a browser).
+ cmds = append(cmds, []string{"chrome", "google-chrome", "chromium", "firefox", "sensible-browser"}...)
+ if os.Getenv("DISPLAY") != "" {
+ // xdg-open is only for use in a desktop environment.
+ cmds = append(cmds, "xdg-open")
}
- return append(cmds, "xdg-open")
}
+ return cmds
}
var kcachegrind = []string{"kcachegrind"}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
index 2dabc3017b..45f1846749 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
@@ -152,20 +152,33 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
}
func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
- trim, tagfilter, filter := v["trim"].boolValue(), true, true
+ // Some report types override the trim flag to false below. This is to make
+ // sure the default heuristics of excluding insignificant nodes and edges
+ // from the call graph do not apply. One example where it is important is
+ // annotated source or disassembly listing. Those reports run on a specific
+ // function (or functions), but the trimming is applied before the function
+ // data is selected. So, with trimming enabled, the report could end up
+ // showing no data if the specified function is "uninteresting" as far as the
+ // trimming is concerned.
+ trim := v["trim"].boolValue()
switch cmd {
- case "callgrind", "kcachegrind":
- trim = false
- v.set("addresses", "t")
case "disasm", "weblist":
trim = false
- v.set("addressnoinlines", "t")
+ v.set("addresses", "t")
+ // Force the 'noinlines' mode so that source locations for a given address
+ // collapse and there is only one for the given address. Without this
+ // cumulative metrics would be double-counted when annotating the assembly.
+ // This is because the merge is done by address and in case of an inlined
+ // stack each of the inlined entries is a separate callgraph node.
+ v.set("noinlines", "t")
case "peek":
- trim, tagfilter, filter = false, false, false
+ trim = false
case "list":
- v.set("nodecount", "0")
+ trim = false
v.set("lines", "t")
+ // Do not force 'noinlines' to be false so that specifying
+ // "-list foo -noinlines" is supported and works as expected.
case "text", "top", "topproto":
if v["nodecount"].intValue() == -1 {
v.set("nodecount", "0")
@@ -176,9 +189,11 @@ func applyCommandOverrides(cmd string, outputFormat int, v variables) variables
}
}
- if outputFormat == report.Proto || outputFormat == report.Raw {
- trim, tagfilter, filter = false, false, false
+ switch outputFormat {
+ case report.Proto, report.Raw, report.Callgrind:
+ trim = false
v.set("addresses", "t")
+ v.set("noinlines", "f")
}
if !trim {
@@ -186,43 +201,32 @@ func applyCommandOverrides(cmd string, outputFormat int, v variables) variables
v.set("nodefraction", "0")
v.set("edgefraction", "0")
}
- if !tagfilter {
- v.set("tagfocus", "")
- v.set("tagignore", "")
- }
- if !filter {
- v.set("focus", "")
- v.set("ignore", "")
- v.set("hide", "")
- v.set("show", "")
- v.set("show_from", "")
- }
return v
}
func aggregate(prof *profile.Profile, v variables) error {
- var inlines, function, filename, linenumber, address bool
+ var function, filename, linenumber, address bool
+ inlines := !v["noinlines"].boolValue()
switch {
case v["addresses"].boolValue():
- return nil
+ if inlines {
+ return nil
+ }
+ function = true
+ filename = true
+ linenumber = true
+ address = true
case v["lines"].boolValue():
- inlines = true
function = true
filename = true
linenumber = true
case v["files"].boolValue():
- inlines = true
filename = true
case v["functions"].boolValue():
- inlines = true
- function = true
- case v["noinlines"].boolValue():
function = true
- case v["addressnoinlines"].boolValue():
+ case v["filefunctions"].boolValue():
function = true
filename = true
- linenumber = true
- address = true
default:
return fmt.Errorf("unexpected granularity")
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go
index ff6afe9cff..90f89dc7bc 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go
@@ -53,6 +53,9 @@ func TestParse(t *testing.T) {
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"},
@@ -63,6 +66,7 @@ func TestParse(t *testing.T) {
{"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"},
@@ -92,6 +96,8 @@ func TestParse(t *testing.T) {
{"peek=line.*01", "cpu"},
{"weblist=line[13],addresses,flat", "cpu"},
{"tags,tagfocus=400kb:", "heap_request"},
+ {"dot", "longNameFuncs"},
+ {"text", "longNameFuncs"},
}
baseVars := pprofVariables
@@ -108,9 +114,6 @@ func TestParse(t *testing.T) {
flags := strings.Split(tc.flags, ",")
- // Skip the output format in the first flag, to output to a proto
- addFlags(&f, flags[1:])
-
// Encode profile into a protobuf and decode it again.
protoTempFile, err := ioutil.TempFile("", "profile_proto")
if err != nil {
@@ -123,11 +126,13 @@ func TestParse(t *testing.T) {
if flags[0] == "topproto" {
f.bools["proto"] = false
f.bools["topproto"] = true
+ f.bools["addresses"] = true
}
// First pprof invocation to save the profile into a profile.proto.
- o1 := setDefaults(nil)
- o1.Flagset = f
+ // Pass in flag set hen setting defaults, because otherwise default
+ // transport will try to add flags to the default flag set.
+ o1 := setDefaults(&plugin.Options{Flagset: f})
o1.Fetch = testFetcher{}
o1.Sym = testSymbolizer{}
o1.UI = testUI
@@ -144,28 +149,28 @@ func TestParse(t *testing.T) {
}
defer os.Remove(outputTempFile.Name())
defer outputTempFile.Close()
+
+ f = baseFlags()
f.strings["output"] = outputTempFile.Name()
f.args = []string{protoTempFile.Name()}
- var solution string
+ delete(f.bools, "proto")
+ addFlags(&f, flags)
+ solution := solutionFilename(tc.source, &f)
// Apply the flags for the second pprof run, and identify name of
// the file containing expected results
if flags[0] == "topproto" {
+ addFlags(&f, flags)
solution = solutionFilename(tc.source, &f)
delete(f.bools, "topproto")
f.bools["text"] = true
- } else {
- delete(f.bools, "proto")
- addFlags(&f, flags[:1])
- solution = solutionFilename(tc.source, &f)
}
- // The add_comment flag is not idempotent so only apply it on the first run.
- delete(f.strings, "add_comment")
// Second pprof invocation to read the profile from profile.proto
// and generate a report.
- o2 := setDefaults(nil)
- o2.Flagset = f
+ // Pass in flag set hen setting defaults, because otherwise default
+ // transport will try to add flags to the default flag set.
+ o2 := setDefaults(&plugin.Options{Flagset: f})
o2.Sym = testSymbolizeDemangler{}
o2.Obj = new(mockObjTool)
o2.UI = testUI
@@ -250,7 +255,8 @@ func testSourceURL(port int) string {
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", "files", "lines", "addresses"})
+ 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"})
@@ -293,6 +299,8 @@ type testFlags struct {
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
@@ -436,6 +444,8 @@ func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string
p = contentionProfile()
case "symbolz":
p = symzProfile()
+ case "longNameFuncs":
+ p = longNameFuncsProfile()
default:
return nil, "", fmt.Errorf("unexpected source: %s", s)
}
@@ -519,6 +529,83 @@ func fakeDemangler(name string) string {
}
}
+// Returns a profile with function names which should be shortened in
+// graph and flame views.
+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,
+ }
+}
+
func cpuProfile() *profile.Profile {
var cpuM = []*profile.Mapping{
{
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
index 7a7a1a20f2..b8a69e87fc 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch.go
@@ -16,7 +16,6 @@ package driver
import (
"bytes"
- "crypto/tls"
"fmt"
"io"
"io/ioutil"
@@ -57,7 +56,7 @@ func fetchProfiles(s *source, o *plugin.Options) (*profile.Profile, error) {
})
}
- p, pbase, m, mbase, save, err := grabSourcesAndBases(sources, bases, o.Fetch, o.Obj, o.UI)
+ p, pbase, m, mbase, save, err := grabSourcesAndBases(sources, bases, o.Fetch, o.Obj, o.UI, o.HTTPTransport)
if err != nil {
return nil, err
}
@@ -123,7 +122,7 @@ func fetchProfiles(s *source, o *plugin.Options) (*profile.Profile, error) {
return p, nil
}
-func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (*profile.Profile, *profile.Profile, plugin.MappingSources, plugin.MappingSources, bool, error) {
+func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (*profile.Profile, *profile.Profile, plugin.MappingSources, plugin.MappingSources, bool, error) {
wg := sync.WaitGroup{}
wg.Add(2)
var psrc, pbase *profile.Profile
@@ -133,11 +132,11 @@ func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, o
var countsrc, countbase int
go func() {
defer wg.Done()
- psrc, msrc, savesrc, countsrc, errsrc = chunkedGrab(sources, fetch, obj, ui)
+ psrc, msrc, savesrc, countsrc, errsrc = chunkedGrab(sources, fetch, obj, ui, tr)
}()
go func() {
defer wg.Done()
- pbase, mbase, savebase, countbase, errbase = chunkedGrab(bases, fetch, obj, ui)
+ pbase, mbase, savebase, countbase, errbase = chunkedGrab(bases, fetch, obj, ui, tr)
}()
wg.Wait()
save := savesrc || savebase
@@ -167,7 +166,7 @@ func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fetcher, o
// chunkedGrab fetches the profiles described in source and merges them into
// a single profile. It fetches a chunk of profiles concurrently, with a maximum
// chunk size to limit its memory usage.
-func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (*profile.Profile, plugin.MappingSources, bool, int, error) {
+func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (*profile.Profile, plugin.MappingSources, bool, int, error) {
const chunkSize = 64
var p *profile.Profile
@@ -180,7 +179,7 @@ func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTo
if end > len(sources) {
end = len(sources)
}
- chunkP, chunkMsrc, chunkSave, chunkCount, chunkErr := concurrentGrab(sources[start:end], fetch, obj, ui)
+ chunkP, chunkMsrc, chunkSave, chunkCount, chunkErr := concurrentGrab(sources[start:end], fetch, obj, ui, tr)
switch {
case chunkErr != nil:
return nil, nil, false, 0, chunkErr
@@ -204,13 +203,13 @@ func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTo
}
// concurrentGrab fetches multiple profiles concurrently
-func concurrentGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (*profile.Profile, plugin.MappingSources, bool, int, error) {
+func concurrentGrab(sources []profileSource, fetch plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (*profile.Profile, plugin.MappingSources, bool, int, error) {
wg := sync.WaitGroup{}
wg.Add(len(sources))
for i := range sources {
go func(s *profileSource) {
defer wg.Done()
- s.p, s.msrc, s.remote, s.err = grabProfile(s.source, s.addr, fetch, obj, ui)
+ s.p, s.msrc, s.remote, s.err = grabProfile(s.source, s.addr, fetch, obj, ui, tr)
}(&sources[i])
}
wg.Wait()
@@ -310,7 +309,7 @@ const testSourceAddress = "pproftest.local"
// grabProfile fetches a profile. Returns the profile, sources for the
// profile mappings, a bool indicating if the profile was fetched
// remotely, and an error.
-func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI) (p *profile.Profile, msrc plugin.MappingSources, remote bool, err error) {
+func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj plugin.ObjTool, ui plugin.UI, tr http.RoundTripper) (p *profile.Profile, msrc plugin.MappingSources, remote bool, err error) {
var src string
duration, timeout := time.Duration(s.Seconds)*time.Second, time.Duration(s.Timeout)*time.Second
if fetcher != nil {
@@ -321,7 +320,7 @@ func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj plugin.Ob
}
if err != nil || p == nil {
// Fetch the profile over HTTP or from a file.
- p, src, err = fetch(source, duration, timeout, ui)
+ p, src, err = fetch(source, duration, timeout, ui, tr)
if err != nil {
return
}
@@ -461,7 +460,7 @@ mapping:
// fetch fetches a profile from source, within the timeout specified,
// producing messages through the ui. It returns the profile and the
// url of the actual source of the profile for remote profiles.
-func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *profile.Profile, src string, err error) {
+func fetch(source string, duration, timeout time.Duration, ui plugin.UI, tr http.RoundTripper) (p *profile.Profile, src string, err error) {
var f io.ReadCloser
if sourceURL, timeout := adjustURL(source, duration, timeout); sourceURL != "" {
@@ -469,7 +468,7 @@ func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *pro
if duration > 0 {
ui.Print(fmt.Sprintf("Please wait... (%v)", duration))
}
- f, err = fetchURL(sourceURL, timeout)
+ f, err = fetchURL(sourceURL, timeout, tr)
src = sourceURL
} else if isPerfFile(source) {
f, err = convertPerfData(source, ui)
@@ -484,8 +483,12 @@ func fetch(source string, duration, timeout time.Duration, ui plugin.UI) (p *pro
}
// fetchURL fetches a profile from a URL using HTTP.
-func fetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
- resp, err := httpGet(source, timeout)
+func fetchURL(source string, timeout time.Duration, tr http.RoundTripper) (io.ReadCloser, error) {
+ client := &http.Client{
+ Transport: tr,
+ Timeout: timeout + 5*time.Second,
+ }
+ resp, err := client.Get(source)
if err != nil {
return nil, fmt.Errorf("http fetch: %v", err)
}
@@ -582,30 +585,3 @@ func adjustURL(source string, duration, timeout time.Duration) (string, time.Dur
u.RawQuery = values.Encode()
return u.String(), timeout
}
-
-// httpGet is a wrapper around http.Get; it is defined as a variable
-// so it can be redefined during for testing.
-var httpGet = func(source string, timeout time.Duration) (*http.Response, error) {
- url, err := url.Parse(source)
- if err != nil {
- return nil, err
- }
-
- var tlsConfig *tls.Config
- if url.Scheme == "https+insecure" {
- tlsConfig = &tls.Config{
- InsecureSkipVerify: true,
- }
- url.Scheme = "https"
- source = url.String()
- }
-
- client := &http.Client{
- Transport: &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- TLSClientConfig: tlsConfig,
- ResponseHeaderTimeout: timeout + 5*time.Second,
- },
- }
- return client.Get(source)
-}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go
index e67b2e9f87..b9e9dfe8f4 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/fetch_test.go
@@ -24,8 +24,8 @@ import (
"fmt"
"io/ioutil"
"math/big"
+ "net"
"net/http"
- "net/url"
"os"
"path/filepath"
"reflect"
@@ -39,6 +39,7 @@ import (
"github.com/google/pprof/internal/plugin"
"github.com/google/pprof/internal/proftest"
"github.com/google/pprof/internal/symbolizer"
+ "github.com/google/pprof/internal/transport"
"github.com/google/pprof/profile"
)
@@ -173,12 +174,6 @@ func (testFile) Close() error {
func TestFetch(t *testing.T) {
const path = "testdata/"
-
- // Intercept http.Get calls from HTTPFetcher.
- savedHTTPGet := httpGet
- defer func() { httpGet = savedHTTPGet }()
- httpGet = stubHTTPGet
-
type testcase struct {
source, execName string
}
@@ -188,7 +183,7 @@ func TestFetch(t *testing.T) {
{path + "go.nomappings.crash", "/bin/gotest.exe"},
{"http://localhost/profile?file=cppbench.cpu", ""},
} {
- p, _, _, err := grabProfile(&source{ExecName: tc.execName}, tc.source, nil, testObj{}, &proftest.TestUI{T: t})
+ p, _, _, err := grabProfile(&source{ExecName: tc.execName}, tc.source, nil, testObj{}, &proftest.TestUI{T: t}, &httpTransport{})
if err != nil {
t.Fatalf("%s: %s", tc.source, err)
}
@@ -449,8 +444,9 @@ func TestFetchWithBase(t *testing.T) {
f.args = tc.sources
o := setDefaults(&plugin.Options{
- UI: &proftest.TestUI{T: t, AllowRx: "Local symbolization failed|Some binary filenames not available"},
- Flagset: f,
+ UI: &proftest.TestUI{T: t, AllowRx: "Local symbolization failed|Some binary filenames not available"},
+ Flagset: f,
+ HTTPTransport: transport.New(nil),
})
src, _, err := parseFlags(o)
@@ -503,19 +499,14 @@ func mappingSources(key, source string, start uint64) plugin.MappingSources {
}
}
-// stubHTTPGet intercepts a call to http.Get and rewrites it to use
-// "file://" to get the profile directly from a file.
-func stubHTTPGet(source string, _ time.Duration) (*http.Response, error) {
- url, err := url.Parse(source)
- if err != nil {
- return nil, err
- }
+type httpTransport struct{}
- values := url.Query()
+func (tr *httpTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ values := req.URL.Query()
file := values.Get("file")
if file == "" {
- return nil, fmt.Errorf("want .../file?profile, got %s", source)
+ return nil, fmt.Errorf("want .../file?profile, got %s", req.URL.String())
}
t := &http.Transport{}
@@ -532,7 +523,7 @@ func closedError() string {
return "use of closed"
}
-func TestHttpsInsecure(t *testing.T) {
+func TestHTTPSInsecure(t *testing.T) {
if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
t.Skip("test assumes tcp available")
}
@@ -553,7 +544,8 @@ func TestHttpsInsecure(t *testing.T) {
pprofVariables = baseVars.makeCopy()
defer func() { pprofVariables = baseVars }()
- tlsConfig := &tls.Config{Certificates: []tls.Certificate{selfSignedCert(t)}}
+ tlsCert, _, _ := selfSignedCert(t, "")
+ tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}}
l, err := tls.Listen("tcp", "localhost:0", tlsConfig)
if err != nil {
@@ -586,8 +578,9 @@ func TestHttpsInsecure(t *testing.T) {
Symbolize: "remote",
}
o := &plugin.Options{
- Obj: &binutils.Binutils{},
- UI: &proftest.TestUI{T: t, AllowRx: "Saved profile in"},
+ Obj: &binutils.Binutils{},
+ UI: &proftest.TestUI{T: t, AllowRx: "Saved profile in"},
+ HTTPTransport: transport.New(nil),
}
o.Sym = &symbolizer.Symbolizer{Obj: o.Obj, UI: o.UI}
p, err := fetchProfiles(s, o)
@@ -600,7 +593,122 @@ func TestHttpsInsecure(t *testing.T) {
if len(p.Function) == 0 {
t.Fatalf("fetchProfiles(%s) got non-symbolized profile: len(p.Function)==0", address)
}
- if err := checkProfileHasFunction(p, "TestHttpsInsecure"); err != nil {
+ if err := checkProfileHasFunction(p, "TestHTTPSInsecure"); err != nil {
+ t.Fatalf("fetchProfiles(%s) %v", address, err)
+ }
+}
+
+func TestHTTPSWithServerCertFetch(t *testing.T) {
+ if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
+ t.Skip("test assumes tcp available")
+ }
+ saveHome := os.Getenv(homeEnv())
+ tempdir, err := ioutil.TempDir("", "home")
+ if err != nil {
+ t.Fatal("creating temp dir: ", err)
+ }
+ defer os.RemoveAll(tempdir)
+
+ // pprof writes to $HOME/pprof by default which is not necessarily
+ // writeable (e.g. on a Debian buildd) so set $HOME to something we
+ // know we can write to for the duration of the test.
+ os.Setenv(homeEnv(), tempdir)
+ defer os.Setenv(homeEnv(), saveHome)
+
+ baseVars := pprofVariables
+ pprofVariables = baseVars.makeCopy()
+ defer func() { pprofVariables = baseVars }()
+
+ cert, certBytes, keyBytes := selfSignedCert(t, "localhost")
+ cas := x509.NewCertPool()
+ cas.AppendCertsFromPEM(certBytes)
+
+ tlsConfig := &tls.Config{
+ RootCAs: cas,
+ Certificates: []tls.Certificate{cert},
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ ClientCAs: cas,
+ }
+
+ l, err := tls.Listen("tcp", "localhost:0", tlsConfig)
+ if err != nil {
+ t.Fatalf("net.Listen: got error %v, want no error", err)
+ }
+
+ donec := make(chan error, 1)
+ go func(donec chan<- error) {
+ donec <- http.Serve(l, nil)
+ }(donec)
+ defer func() {
+ if got, want := <-donec, closedError(); !strings.Contains(got.Error(), want) {
+ t.Fatalf("Serve got error %v, want %q", got, want)
+ }
+ }()
+ defer l.Close()
+
+ outputTempFile, err := ioutil.TempFile("", "profile_output")
+ if err != nil {
+ t.Fatalf("Failed to create tempfile: %v", err)
+ }
+ defer os.Remove(outputTempFile.Name())
+ defer outputTempFile.Close()
+
+ // Get port from the address, so request to the server can be made using
+ // the host name specified in certificates.
+ _, portStr, err := net.SplitHostPort(l.Addr().String())
+ if err != nil {
+ t.Fatalf("cannot get port from URL: %v", err)
+ }
+ address := "https://" + "localhost:" + portStr + "/debug/pprof/goroutine"
+ s := &source{
+ Sources: []string{address},
+ Seconds: 10,
+ Timeout: 10,
+ Symbolize: "remote",
+ }
+
+ certTempFile, err := ioutil.TempFile("", "cert_output")
+ if err != nil {
+ t.Errorf("cannot create cert tempfile: %v", err)
+ }
+ defer os.Remove(certTempFile.Name())
+ defer certTempFile.Close()
+ certTempFile.Write(certBytes)
+
+ keyTempFile, err := ioutil.TempFile("", "key_output")
+ if err != nil {
+ t.Errorf("cannot create key tempfile: %v", err)
+ }
+ defer os.Remove(keyTempFile.Name())
+ defer keyTempFile.Close()
+ keyTempFile.Write(keyBytes)
+
+ f := &testFlags{
+ strings: map[string]string{
+ "tls_cert": certTempFile.Name(),
+ "tls_key": keyTempFile.Name(),
+ "tls_ca": certTempFile.Name(),
+ },
+ }
+ o := &plugin.Options{
+ Obj: &binutils.Binutils{},
+ UI: &proftest.TestUI{T: t, AllowRx: "Saved profile in"},
+ Flagset: f,
+ HTTPTransport: transport.New(f),
+ }
+
+ o.Sym = &symbolizer.Symbolizer{Obj: o.Obj, UI: o.UI, Transport: o.HTTPTransport}
+ p, err := fetchProfiles(s, o)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(p.SampleType) == 0 {
+ t.Fatalf("fetchProfiles(%s) got empty profile: len(p.SampleType)==0", address)
+ }
+ if len(p.Function) == 0 {
+ t.Fatalf("fetchProfiles(%s) got non-symbolized profile: len(p.Function)==0", address)
+ }
+ if err := checkProfileHasFunction(p, "TestHTTPSWithServerCertFetch"); err != nil {
t.Fatalf("fetchProfiles(%s) %v", address, err)
}
}
@@ -614,7 +722,10 @@ func checkProfileHasFunction(p *profile.Profile, fname string) error {
return fmt.Errorf("got %s, want function %q", p.String(), fname)
}
-func selfSignedCert(t *testing.T) tls.Certificate {
+// selfSignedCert generates a self-signed certificate, and returns the
+// generated certificate, and byte arrays containing the certificate and
+// key associated with the certificate.
+func selfSignedCert(t *testing.T, host string) (tls.Certificate, []byte, []byte) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed to generate private key: %v", err)
@@ -629,6 +740,8 @@ func selfSignedCert(t *testing.T) tls.Certificate {
SerialNumber: big.NewInt(1),
NotBefore: time.Now(),
NotAfter: time.Now().Add(10 * time.Minute),
+ IsCA: true,
+ DNSNames: []string{host},
}
b, err = x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey)
@@ -641,5 +754,5 @@ func selfSignedCert(t *testing.T) tls.Certificate {
if err != nil {
t.Fatalf("failed to create TLS key pair: %v", err)
}
- return cert
+ return cert, bc, bk
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go
new file mode 100644
index 0000000000..c48fb5cd2e
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/flags.go
@@ -0,0 +1,78 @@
+package driver
+
+import (
+ "flag"
+ "strings"
+)
+
+// GoFlags implements the plugin.FlagSet interface.
+type GoFlags struct {
+ UsageMsgs []string
+}
+
+// Bool implements the plugin.FlagSet interface.
+func (*GoFlags) Bool(o string, d bool, c string) *bool {
+ return flag.Bool(o, d, c)
+}
+
+// Int implements the plugin.FlagSet interface.
+func (*GoFlags) Int(o string, d int, c string) *int {
+ return flag.Int(o, d, c)
+}
+
+// Float64 implements the plugin.FlagSet interface.
+func (*GoFlags) Float64(o string, d float64, c string) *float64 {
+ return flag.Float64(o, d, c)
+}
+
+// String implements the plugin.FlagSet interface.
+func (*GoFlags) String(o, d, c string) *string {
+ return flag.String(o, d, c)
+}
+
+// BoolVar implements the plugin.FlagSet interface.
+func (*GoFlags) BoolVar(b *bool, o string, d bool, c string) {
+ flag.BoolVar(b, o, d, c)
+}
+
+// IntVar implements the plugin.FlagSet interface.
+func (*GoFlags) IntVar(i *int, o string, d int, c string) {
+ flag.IntVar(i, o, d, c)
+}
+
+// Float64Var implements the plugin.FlagSet interface.
+// the value of the flag.
+func (*GoFlags) Float64Var(f *float64, o string, d float64, c string) {
+ flag.Float64Var(f, o, d, c)
+}
+
+// StringVar implements the plugin.FlagSet interface.
+func (*GoFlags) StringVar(s *string, o, d, c string) {
+ flag.StringVar(s, o, d, c)
+}
+
+// StringList implements the plugin.FlagSet interface.
+func (*GoFlags) StringList(o, d, c string) *[]*string {
+ return &[]*string{flag.String(o, d, c)}
+}
+
+// ExtraUsage implements the plugin.FlagSet interface.
+func (f *GoFlags) ExtraUsage() string {
+ return strings.Join(f.UsageMsgs, "\n")
+}
+
+// AddExtraUsage implements the plugin.FlagSet interface.
+func (f *GoFlags) AddExtraUsage(eu string) {
+ f.UsageMsgs = append(f.UsageMsgs, eu)
+}
+
+// Parse implements the plugin.FlagSet interface.
+func (*GoFlags) Parse(usage func()) []string {
+ flag.Usage = usage
+ flag.Parse()
+ args := flag.Args()
+ if len(args) == 0 {
+ usage()
+ }
+ return args
+}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
index c9b9a5398f..13613cff86 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph.go
@@ -55,7 +55,7 @@ func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
v := n.CumValue()
fullName := n.Info.PrintableName()
node := &treeNode{
- Name: getNodeShortName(fullName),
+ Name: graph.ShortenFunctionName(fullName),
FullName: fullName,
Cum: v,
CumFormat: config.FormatValue(v),
@@ -101,19 +101,3 @@ func (ui *webInterface) flamegraph(w http.ResponseWriter, req *http.Request) {
Nodes: nodeArr,
})
}
-
-// getNodeShortName builds a short node name from fullName.
-func getNodeShortName(name string) string {
- chunks := strings.SplitN(name, "(", 2)
- head := chunks[0]
- pathSep := strings.LastIndexByte(head, '/')
- if pathSep == -1 || pathSep+1 >= len(head) {
- return name
- }
- // Check if name is a stdlib package, i.e. doesn't have "." before "/"
- if dot := strings.IndexByte(head, '.'); dot == -1 || dot > pathSep {
- return name
- }
- // Trim package path prefix from node name
- return name[pathSep+1:]
-}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go
deleted file mode 100644
index c1a887c830..0000000000
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/flamegraph_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package driver
-
-import "testing"
-
-func TestGetNodeShortName(t *testing.T) {
- type testCase struct {
- name string
- want string
- }
- testcases := []testCase{
- {
- "root",
- "root",
- },
- {
- "syscall.Syscall",
- "syscall.Syscall",
- },
- {
- "net/http.(*conn).serve",
- "net/http.(*conn).serve",
- },
- {
- "github.com/blah/foo.Foo",
- "foo.Foo",
- },
- {
- "github.com/blah/foo_bar.(*FooBar).Foo",
- "foo_bar.(*FooBar).Foo",
- },
- {
- "encoding/json.(*structEncoder).(encoding/json.encode)-fm",
- "encoding/json.(*structEncoder).(encoding/json.encode)-fm",
- },
- {
- "github.com/blah/blah/vendor/gopkg.in/redis.v3.(*baseClient).(github.com/blah/blah/vendor/gopkg.in/redis.v3.process)-fm",
- "redis.v3.(*baseClient).(github.com/blah/blah/vendor/gopkg.in/redis.v3.process)-fm",
- },
- }
- for _, tc := range testcases {
- name := getNodeShortName(tc.name)
- if got, want := name, tc.want; got != want {
- t.Errorf("for %s, got %q, want %q", tc.name, got, want)
- }
- }
-}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go
index 8d775e16bd..758adf9bdc 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive_test.go
@@ -23,6 +23,7 @@ import (
"github.com/google/pprof/internal/plugin"
"github.com/google/pprof/internal/proftest"
"github.com/google/pprof/internal/report"
+ "github.com/google/pprof/internal/transport"
"github.com/google/pprof/profile"
)
@@ -41,7 +42,10 @@ func TestShell(t *testing.T) {
// Random interleave of independent scripts
pprofVariables = testVariables(savedVariables)
- o := setDefaults(nil)
+
+ // pass in HTTPTransport when setting defaults, because otherwise default
+ // transport will try to add flags to the default flag set.
+ o := setDefaults(&plugin.Options{HTTPTransport: transport.New(nil)})
o.UI = newUI(t, interleave(script, 0))
if err := interactive(p, o); err != nil {
t.Error("first attempt:", err)
@@ -259,12 +263,13 @@ func TestInteractiveCommands(t *testing.T) {
{
"weblist find -test",
map[string]string{
- "functions": "false",
- "addressnoinlines": "true",
- "nodecount": "0",
- "cum": "false",
- "flat": "true",
- "ignore": "test",
+ "functions": "false",
+ "addresses": "true",
+ "noinlines": "true",
+ "nodecount": "0",
+ "cum": "false",
+ "flat": "true",
+ "ignore": "test",
},
},
{
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go
index 34167d4bf5..6e8f9fca25 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/options.go
@@ -16,7 +16,6 @@ package driver
import (
"bufio"
- "flag"
"fmt"
"io"
"os"
@@ -25,6 +24,7 @@ import (
"github.com/google/pprof/internal/binutils"
"github.com/google/pprof/internal/plugin"
"github.com/google/pprof/internal/symbolizer"
+ "github.com/google/pprof/internal/transport"
)
// setDefaults returns a new plugin.Options with zero fields sets to
@@ -38,7 +38,7 @@ func setDefaults(o *plugin.Options) *plugin.Options {
d.Writer = oswriter{}
}
if d.Flagset == nil {
- d.Flagset = goFlags{}
+ d.Flagset = &GoFlags{}
}
if d.Obj == nil {
d.Obj = &binutils.Binutils{}
@@ -46,67 +46,15 @@ func setDefaults(o *plugin.Options) *plugin.Options {
if d.UI == nil {
d.UI = &stdUI{r: bufio.NewReader(os.Stdin)}
}
+ if d.HTTPTransport == nil {
+ d.HTTPTransport = transport.New(d.Flagset)
+ }
if d.Sym == nil {
- d.Sym = &symbolizer.Symbolizer{Obj: d.Obj, UI: d.UI}
+ d.Sym = &symbolizer.Symbolizer{Obj: d.Obj, UI: d.UI, Transport: d.HTTPTransport}
}
return d
}
-// goFlags returns a flagset implementation based on the standard flag
-// package from the Go distribution. It implements the plugin.FlagSet
-// interface.
-type goFlags struct{}
-
-func (goFlags) Bool(o string, d bool, c string) *bool {
- return flag.Bool(o, d, c)
-}
-
-func (goFlags) Int(o string, d int, c string) *int {
- return flag.Int(o, d, c)
-}
-
-func (goFlags) Float64(o string, d float64, c string) *float64 {
- return flag.Float64(o, d, c)
-}
-
-func (goFlags) String(o, d, c string) *string {
- return flag.String(o, d, c)
-}
-
-func (goFlags) BoolVar(b *bool, o string, d bool, c string) {
- flag.BoolVar(b, o, d, c)
-}
-
-func (goFlags) IntVar(i *int, o string, d int, c string) {
- flag.IntVar(i, o, d, c)
-}
-
-func (goFlags) Float64Var(f *float64, o string, d float64, c string) {
- flag.Float64Var(f, o, d, c)
-}
-
-func (goFlags) StringVar(s *string, o, d, c string) {
- flag.StringVar(s, o, d, c)
-}
-
-func (goFlags) StringList(o, d, c string) *[]*string {
- return &[]*string{flag.String(o, d, c)}
-}
-
-func (goFlags) ExtraUsage() string {
- return ""
-}
-
-func (goFlags) Parse(usage func()) []string {
- flag.Usage = usage
- flag.Parse()
- args := flag.Args()
- if len(args) == 0 {
- usage()
- }
- return args
-}
-
type stdUI struct {
r *bufio.Reader
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text
new file mode 100644
index 0000000000..d53c44dad9
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text
@@ -0,0 +1,7 @@
+Showing nodes accounting for 1.12s, 100% of 1.12s total
+Dropped 1 node (cum <= 0.06s)
+ flat flat% sum% cum cum%
+ 1.10s 98.21% 98.21% 1.10s 98.21% 0000000000001000 line1000 testdata/file1000.src:1
+ 0.01s 0.89% 99.11% 1.01s 90.18% 0000000000002000 line2000 testdata/file2000.src:4
+ 0.01s 0.89% 100% 1.01s 90.18% 0000000000003000 line3000 testdata/file3000.src:6
+ 0 0% 100% 0.10s 8.93% 0000000000003001 line3000 testdata/file3000.src:9
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text
new file mode 100644
index 0000000000..88fb760759
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text
@@ -0,0 +1,5 @@
+Showing nodes accounting for 1.12s, 100% of 1.12s total
+ flat flat% sum% cum cum%
+ 1.10s 98.21% 98.21% 1.10s 98.21% line1000 testdata/file1000.src
+ 0.01s 0.89% 99.11% 1.01s 90.18% line2000 testdata/file2000.src
+ 0.01s 0.89% 100% 1.12s 100% line3000 testdata/file3000.src
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text
new file mode 100644
index 0000000000..493b4912de
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text
@@ -0,0 +1,5 @@
+Showing nodes accounting for 1.12s, 100% of 1.12s total
+ flat flat% sum% cum cum%
+ 1.10s 98.21% 98.21% 1.10s 98.21% line1000
+ 0.01s 0.89% 99.11% 1.01s 90.18% line2000
+ 0.01s 0.89% 100% 1.12s 100% line3000
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto
new file mode 100644
index 0000000000..33bf6814a4
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.cpu.lines.topproto
@@ -0,0 +1,3 @@
+Showing nodes accounting for 1s, 100% of 1s total
+ flat flat% sum% cum cum%
+ 1s 100% 100% 1s 100% mangled1000 testdata/file1000.src:1
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot
new file mode 100644
index 0000000000..474a5108ba
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.dot
@@ -0,0 +1,9 @@
+digraph "testbinary" {
+node [style=filled fillcolor="#f8f8f8"]
+subgraph cluster_L { "File: testbinary" [shape=box fontsize=16 label="File: testbinary\lType: cpu\lDuration: 10s, Total samples = 1.11s (11.10%)\lShowing nodes accounting for 1.11s, 100% of 1.11s total\l" tooltip="testbinary"] }
+N1 [label="package1\nobject\nfunction1\n1.10s (99.10%)" id="node1" fontsize=24 shape=box tooltip="path/to/package1.object.function1 (1.10s)" color="#b20000" fillcolor="#edd5d5"]
+N2 [label="FooBar\nrun\n0.01s (0.9%)\nof 1.01s (90.99%)" id="node2" fontsize=10 shape=box tooltip="java.bar.foo.FooBar.run(java.lang.Runnable) (1.01s)" color="#b20400" fillcolor="#edd6d5"]
+N3 [label="Bar\nFoo\n0 of 1.10s (99.10%)" id="node3" fontsize=8 shape=box tooltip="(anonymous namespace)::Bar::Foo (1.10s)" color="#b20000" fillcolor="#edd5d5"]
+N3 -> N1 [label=" 1.10s" weight=100 penwidth=5 color="#b20000" tooltip="(anonymous namespace)::Bar::Foo -> path/to/package1.object.function1 (1.10s)" labeltooltip="(anonymous namespace)::Bar::Foo -> path/to/package1.object.function1 (1.10s)"]
+N2 -> N3 [label=" 1s" weight=91 penwidth=5 color="#b20500" tooltip="java.bar.foo.FooBar.run(java.lang.Runnable) -> (anonymous namespace)::Bar::Foo (1s)" labeltooltip="java.bar.foo.FooBar.run(java.lang.Runnable) -> (anonymous namespace)::Bar::Foo (1s)"]
+}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text
new file mode 100644
index 0000000000..39cb24ed6a
--- /dev/null
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/testdata/pprof.longNameFuncs.text
@@ -0,0 +1,5 @@
+Showing nodes accounting for 1.11s, 100% of 1.11s total
+ flat flat% sum% cum cum%
+ 1.10s 99.10% 99.10% 1.10s 99.10% path/to/package1.object.function1
+ 0.01s 0.9% 100% 1.01s 90.99% java.bar.foo.FooBar.run(java.lang.Runnable)
+ 0 0% 100% 1.10s 99.10% (anonymous namespace)::Bar::Foo
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
index c3f9c384f8..74104899ca 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
@@ -249,6 +249,21 @@ table tr td {
</div>
</div>
+ {{$sampleLen := len .SampleTypes}}
+ {{if gt $sampleLen 1}}
+ <div id="sample" class="menu-item">
+ <div class="menu-name">
+ Sample
+ <i class="downArrow"></i>
+ </div>
+ <div class="submenu">
+ {{range .SampleTypes}}
+ <a href="?si={{.}}" id="{{.}}">{{.}}</a>
+ {{end}}
+ </div>
+ </div>
+ {{end}}
+
<div id="refine" class="menu-item">
<div class="menu-name">
Refine
@@ -259,6 +274,7 @@ table tr td {
<a title="{{.Help.ignore}}" href="?" id="ignore">Ignore</a>
<a title="{{.Help.hide}}" href="?" id="hide">Hide</a>
<a title="{{.Help.show}}" href="?" id="show">Show</a>
+ <a title="{{.Help.show_from}}" href="?" id="show-from">Show from</a>
<hr>
<a title="{{.Help.reset}}" href="?">Reset</a>
</div>
@@ -718,9 +734,18 @@ function viewer(baseUrl, nodes) {
return str.replace(/([\\\.?+*\[\](){}|^$])/g, '\\$1');
}
+ function setSampleIndexLink(id) {
+ const elem = document.getElementById(id);
+ if (elem != null) {
+ setHrefParams(elem, function (params) {
+ params.set("si", id);
+ });
+ }
+ }
+
// Update id's href to reflect current selection whenever it is
// liable to be followed.
- function makeLinkDynamic(id) {
+ function makeSearchLinkDynamic(id) {
const elem = document.getElementById(id);
if (elem == null) return;
@@ -730,46 +755,50 @@ function viewer(baseUrl, nodes) {
if (id == 'ignore') param = 'i';
if (id == 'hide') param = 'h';
if (id == 'show') param = 's';
+ if (id == 'show-from') param = 'sf';
// We update on mouseenter so middle-click/right-click work properly.
elem.addEventListener('mouseenter', updater);
elem.addEventListener('touchstart', updater);
function updater() {
- elem.href = updateUrl(new URL(elem.href), param);
+ // The selection can be in one of two modes: regexp-based or
+ // list-based. Construct regular expression depending on mode.
+ let re = regexpActive
+ ? search.value
+ : Array.from(selected.keys()).map(key => quotemeta(nodes[key])).join('|');
+
+ setHrefParams(elem, function (params) {
+ if (re != '') {
+ // For focus/show/show-from, forget old parameter. For others, add to re.
+ if (param != 'f' && param != 's' && param != 'sf' && params.has(param)) {
+ const old = params.get(param);
+ if (old != '') {
+ re += '|' + old;
+ }
+ }
+ params.set(param, re);
+ } else {
+ params.delete(param);
+ }
+ });
}
}
- // Update URL to reflect current selection.
- function updateUrl(url, param) {
+ function setHrefParams(elem, paramSetter) {
+ let url = new URL(elem.href);
url.hash = '';
- // The selection can be in one of two modes: regexp-based or
- // list-based. Construct regular expression depending on mode.
- let re = regexpActive
- ? search.value
- : Array.from(selected.keys()).map(key => quotemeta(nodes[key])).join('|');
-
// Copy params from this page's URL.
const params = url.searchParams;
for (const p of new URLSearchParams(window.location.search)) {
params.set(p[0], p[1]);
}
- if (re != '') {
- // For focus/show, forget old parameter. For others, add to re.
- if (param != 'f' && param != 's' && params.has(param)) {
- const old = params.get(param);
- if (old != '') {
- re += '|' + old;
- }
- }
- params.set(param, re);
- } else {
- params.delete(param);
- }
+ // Give the params to the setter to modify.
+ paramSetter(params);
- return url.toString();
+ elem.href = url.toString();
}
function handleTopClick(e) {
@@ -803,7 +832,7 @@ function viewer(baseUrl, nodes) {
const enable = (search.value != '' || selected.size != 0);
if (buttonsEnabled == enable) return;
buttonsEnabled = enable;
- for (const id of ['focus', 'ignore', 'hide', 'show']) {
+ for (const id of ['focus', 'ignore', 'hide', 'show', 'show-from']) {
const link = document.getElementById(id);
if (link != null) {
link.classList.toggle('disabled', !enable);
@@ -825,8 +854,11 @@ function viewer(baseUrl, nodes) {
}
const ids = ['topbtn', 'graphbtn', 'peek', 'list', 'disasm',
- 'focus', 'ignore', 'hide', 'show'];
- ids.forEach(makeLinkDynamic);
+ 'focus', 'ignore', 'hide', 'show', 'show-from'];
+ ids.forEach(makeSearchLinkDynamic);
+
+ const sampleIDs = [{{range .SampleTypes}}'{{.}}', {{end}}];
+ sampleIDs.forEach(setSampleIndexLink);
// Bind action to button with specified id.
function addAction(id, action) {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
index 89bc558668..7becacd202 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
@@ -69,16 +69,17 @@ func (ec *errorCatcher) PrintErr(args ...interface{}) {
// webArgs contains arguments passed to templates in webhtml.go.
type webArgs struct {
- Title string
- Errors []string
- Total int64
- Legend []string
- Help map[string]string
- Nodes []string
- HTMLBody template.HTML
- TextBody string
- Top []report.TextItem
- FlameGraph template.JS
+ Title string
+ Errors []string
+ Total int64
+ SampleTypes []string
+ Legend []string
+ Help map[string]string
+ Nodes []string
+ HTMLBody template.HTML
+ TextBody string
+ Top []report.TextItem
+ FlameGraph template.JS
}
func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options) error {
@@ -199,8 +200,10 @@ func openBrowser(url string, o *plugin.Options) {
for _, p := range []struct{ param, key string }{
{"f", "focus"},
{"s", "show"},
+ {"sf", "show_from"},
{"i", "ignore"},
{"h", "hide"},
+ {"si", "sample_index"},
} {
if v := pprofVariables[p.key].value; v != "" {
q.Set(p.param, v)
@@ -230,8 +233,10 @@ func varsFromURL(u *gourl.URL) variables {
vars := pprofVariables.makeCopy()
vars["focus"].value = u.Query().Get("f")
vars["show"].value = u.Query().Get("s")
+ vars["show_from"].value = u.Query().Get("sf")
vars["ignore"].value = u.Query().Get("i")
vars["hide"].value = u.Query().Get("h")
+ vars["sample_index"].value = u.Query().Get("si")
return vars
}
@@ -262,6 +267,7 @@ func (ui *webInterface) render(w http.ResponseWriter, tmpl string,
data.Title = file + " " + profile
data.Errors = errList
data.Total = rpt.Total()
+ data.SampleTypes = sampleTypes(ui.prof)
data.Legend = legend
data.Help = ui.help
html := &bytes.Buffer{}