cmd: refresh cmd/vendor to match 'go mod vendor'

This change preserves the maximum versions from cmd/vendor/vendor.json
where feasible, but bumps the versions of x/sys (for CL 162987) and
x/tools (for CL 162989 and CL 160837) so that 'go test all' passes in
module mode when run from a working directory in src/cmd.

A small change to cmd/vet (not vendored) was necessary to preserve its
flag behavior given a pristine copy of x/tools; see CL 162989 for more
detail.

This change was generated by running 'go mod vendor' at CL 164622.
(Welcoooome to the fuuuuuture!)

Updates #30228
Updates #30241

Change-Id: I889590318dc857d4a6e20c3023d09a27128d8255
Reviewed-on: https://go-review.googlesource.com/c/go/+/164618
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
This commit is contained in:
Bryan C. Mills 2019-02-28 16:21:48 -05:00
parent 486ca37b14
commit 756a69c6c9
286 changed files with 4507 additions and 50794 deletions

View file

@ -48,43 +48,33 @@ go src=..
pprof
internal
binutils
testdata
+
driver
testdata
+
graph
testdata
+
report
testdata
+
profile
testdata
+
driver
+
graph
+
report
+
profile
+
ianlancetaylor
demangle
testdata
+
+
golang.org
x
arch
arm
armasm
testdata
+
+
arm64
arm64asm
testdata
+
+
x86
x86asm
testdata
+
+
ppc64
ppc64asm
testdata
+
+
archive
tar
testdata

13
src/cmd/go.sum Normal file
View file

@ -0,0 +1,13 @@
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG8n4VqvLXIOEG+SLdDqX7xGtkY=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 h1:pKqc8lAAA6rcwpvsephnRuZp4VHbfszZRClvqAE6Sq8=
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 h1:Pn8fQdvx+z1avAi7fdM2kRYWQNxGlavNDSyzrQg2SsU=
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12 h1:Zw7eRv6INHGfu15LVRN1vrrwusJbnfJjAZn3D1VkQIE=
golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3 h1:2oZsfYnKfYzL4I57uYiRFsUf0bqlLkiuw8nnj3+voUA=
golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4=

25
src/cmd/vendor/README vendored
View file

@ -1,25 +0,0 @@
How to update the vendored packages:
Assuming the govendor tool is available
run the govendor tool from src/cmd directory
$ go get -u github.com/kardianos/govendor
To update packages used by cmd/pprof:
$ cd $GOROOT/src/cmd
$ GOPATH=$GOROOT govendor fetch github.com/google/pprof/...
To update packages used by internal/objfile/*:
$ GOPATH=$GOROOT govendor fetch golang.org/x/arch/...
GOPATH=$GOROOT in the above commands is a hack to
make govendor work and will create the .cache folder in
$GOROOT as a side-effect. Please make sure to delete
the directory and not to include the directory in the
commit by accident.
The vendored copy of golang.org/x/tools is maintained by
running the update-xtools.sh script in this directory,
not by govendor.

7
src/cmd/vendor/github.com/google/pprof/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,7 @@
# This is the official list of pprof authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Google Inc.

15
src/cmd/vendor/github.com/google/pprof/CONTRIBUTORS generated vendored Normal file
View file

@ -0,0 +1,15 @@
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
Raul Silvera <rsilvera@google.com>
Tipp Moseley <tipp@google.com>
Hyoun Kyu Cho <netforce@google.com>
Martin Spier <spiermar@gmail.com>
Taco de Wolff <tacodewolff@gmail.com>

View file

@ -1,392 +0,0 @@
// 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 binutils
import (
"bytes"
"fmt"
"math"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"github.com/google/pprof/internal/plugin"
)
var testAddrMap = map[int]string{
1000: "_Z3fooid.clone2",
2000: "_ZNSaIiEC1Ev.clone18",
3000: "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm",
}
func functionName(level int) (name string) {
if name = testAddrMap[level]; name != "" {
return name
}
return fmt.Sprintf("fun%d", level)
}
func TestAddr2Liner(t *testing.T) {
const offset = 0x500
a := addr2Liner{rw: &mockAddr2liner{}, base: offset}
for i := 1; i < 8; i++ {
addr := i*0x1000 + offset
s, err := a.addrInfo(uint64(addr))
if err != nil {
t.Fatalf("addrInfo(%#x): %v", addr, err)
}
if len(s) != i {
t.Fatalf("addrInfo(%#x): got len==%d, want %d", addr, len(s), i)
}
for l, f := range s {
level := (len(s) - l) * 1000
want := plugin.Frame{Func: functionName(level), File: fmt.Sprintf("file%d", level), Line: level}
if f != want {
t.Errorf("AddrInfo(%#x)[%d]: = %+v, want %+v", addr, l, f, want)
}
}
}
s, err := a.addrInfo(0xFFFF)
if err != nil {
t.Fatalf("addrInfo(0xFFFF): %v", err)
}
if len(s) != 0 {
t.Fatalf("AddrInfo(0xFFFF): got len==%d, want 0", len(s))
}
a.rw.close()
}
type mockAddr2liner struct {
output []string
}
func (a *mockAddr2liner) write(s string) error {
var lines []string
switch s {
case "1000":
lines = []string{"_Z3fooid.clone2", "file1000:1000"}
case "2000":
lines = []string{"_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "3000":
lines = []string{"_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "4000":
lines = []string{"fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "5000":
lines = []string{"fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "6000":
lines = []string{"fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "7000":
lines = []string{"fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "8000":
lines = []string{"fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
case "9000":
lines = []string{"fun9000", "file9000:9000", "fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
default:
lines = []string{"??", "??:0"}
}
a.output = append(a.output, "0x"+s)
a.output = append(a.output, lines...)
return nil
}
func (a *mockAddr2liner) readLine() (string, error) {
if len(a.output) == 0 {
return "", fmt.Errorf("end of file")
}
next := a.output[0]
a.output = a.output[1:]
return next, nil
}
func (a *mockAddr2liner) close() {
}
func TestAddr2LinerLookup(t *testing.T) {
const oddSizedData = `
00001000 T 0x1000
00002000 T 0x2000
00003000 T 0x3000
`
const evenSizedData = `
0000000000001000 T 0x1000
0000000000002000 T 0x2000
0000000000003000 T 0x3000
0000000000004000 T 0x4000
`
for _, d := range []string{oddSizedData, evenSizedData} {
a, err := parseAddr2LinerNM(0, bytes.NewBufferString(d))
if err != nil {
t.Errorf("nm parse error: %v", err)
continue
}
for address, want := range map[uint64]string{
0x1000: "0x1000",
0x1001: "0x1000",
0x1FFF: "0x1000",
0x2000: "0x2000",
0x2001: "0x2000",
} {
if got, _ := a.addrInfo(address); !checkAddress(got, address, want) {
t.Errorf("%x: got %v, want %s", address, got, want)
}
}
for _, unknown := range []uint64{0x0fff, 0x4001} {
if got, _ := a.addrInfo(unknown); got != nil {
t.Errorf("%x: got %v, want nil", unknown, got)
}
}
}
}
func checkAddress(got []plugin.Frame, address uint64, want string) bool {
if len(got) != 1 {
return false
}
return got[0].Func == want
}
func TestSetTools(t *testing.T) {
// Test that multiple calls work.
bu := &Binutils{}
bu.SetTools("")
bu.SetTools("")
}
func TestSetFastSymbolization(t *testing.T) {
// Test that multiple calls work.
bu := &Binutils{}
bu.SetFastSymbolization(true)
bu.SetFastSymbolization(false)
}
func skipUnlessLinuxAmd64(t *testing.T) {
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("This test only works on x86-64 Linux")
}
}
func skipUnlessDarwinAmd64(t *testing.T) {
if runtime.GOOS != "darwin" || runtime.GOARCH != "amd64" {
t.Skip("This test only works on x86-64 Mac")
}
}
func TestDisasm(t *testing.T) {
skipUnlessLinuxAmd64(t)
bu := &Binutils{}
insts, err := bu.Disasm(filepath.Join("testdata", "exe_linux_64"), 0, math.MaxUint64)
if err != nil {
t.Fatalf("Disasm: unexpected error %v", err)
}
mainCount := 0
for _, x := range insts {
if x.Function == "main" {
mainCount++
}
}
if mainCount == 0 {
t.Error("Disasm: found no main instructions")
}
}
func findSymbol(syms []*plugin.Sym, name string) *plugin.Sym {
for _, s := range syms {
for _, n := range s.Name {
if n == name {
return s
}
}
}
return nil
}
func TestObjFile(t *testing.T) {
skipUnlessLinuxAmd64(t)
for _, tc := range []struct {
desc string
start, limit, offset uint64
addr uint64
}{
{"fake mapping", 0, math.MaxUint64, 0, 0x40052d},
{"fixed load address", 0x400000, 0x4006fc, 0, 0x40052d},
// True user-mode ASLR binaries are ET_DYN rather than ET_EXEC so this case
// is a bit artificial except that it approximates the
// vmlinux-with-kernel-ASLR case where the binary *is* ET_EXEC.
{"simulated ASLR address", 0x500000, 0x5006fc, 0, 0x50052d},
} {
t.Run(tc.desc, func(t *testing.T) {
bu := &Binutils{}
f, err := bu.Open(filepath.Join("testdata", "exe_linux_64"), tc.start, tc.limit, tc.offset)
if err != nil {
t.Fatalf("Open: unexpected error %v", err)
}
defer f.Close()
syms, err := f.Symbols(regexp.MustCompile("main"), 0)
if err != nil {
t.Fatalf("Symbols: unexpected error %v", err)
}
m := findSymbol(syms, "main")
if m == nil {
t.Fatalf("Symbols: did not find main")
}
for _, addr := range []uint64{m.Start + f.Base(), tc.addr} {
gotFrames, err := f.SourceLine(addr)
if err != nil {
t.Fatalf("SourceLine: unexpected error %v", err)
}
wantFrames := []plugin.Frame{
{Func: "main", File: "/tmp/hello.c", Line: 3},
}
if !reflect.DeepEqual(gotFrames, wantFrames) {
t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, wantFrames)
}
}
})
}
}
func TestMachoFiles(t *testing.T) {
skipUnlessDarwinAmd64(t)
// Load `file`, pretending it was mapped at `start`. Then get the symbol
// table. Check that it contains the symbol `sym` and that the address
// `addr` gives the `expected` stack trace.
for _, tc := range []struct {
desc string
file string
start, limit, offset uint64
addr uint64
sym string
expected []plugin.Frame
}{
{"normal mapping", "exe_mac_64", 0x100000000, math.MaxUint64, 0,
0x100000f50, "_main",
[]plugin.Frame{
{Func: "main", File: "/tmp/hello.c", Line: 3},
}},
{"other mapping", "exe_mac_64", 0x200000000, math.MaxUint64, 0,
0x200000f50, "_main",
[]plugin.Frame{
{Func: "main", File: "/tmp/hello.c", Line: 3},
}},
{"lib normal mapping", "lib_mac_64", 0, math.MaxUint64, 0,
0xfa0, "_bar",
[]plugin.Frame{
{Func: "bar", File: "/tmp/lib.c", Line: 5},
}},
} {
t.Run(tc.desc, func(t *testing.T) {
bu := &Binutils{}
f, err := bu.Open(filepath.Join("testdata", tc.file), tc.start, tc.limit, tc.offset)
if err != nil {
t.Fatalf("Open: unexpected error %v", err)
}
t.Logf("binutils: %v", bu)
if runtime.GOOS == "darwin" && !bu.rep.addr2lineFound && !bu.rep.llvmSymbolizerFound {
// On OSX user needs to install gaddr2line or llvm-symbolizer with
// Homebrew, skip the test when the environment doesn't have it
// installed.
t.Skip("couldn't find addr2line or gaddr2line")
}
defer f.Close()
syms, err := f.Symbols(nil, 0)
if err != nil {
t.Fatalf("Symbols: unexpected error %v", err)
}
m := findSymbol(syms, tc.sym)
if m == nil {
t.Fatalf("Symbols: could not find symbol %v", tc.sym)
}
gotFrames, err := f.SourceLine(tc.addr)
if err != nil {
t.Fatalf("SourceLine: unexpected error %v", err)
}
if !reflect.DeepEqual(gotFrames, tc.expected) {
t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, tc.expected)
}
})
}
}
func TestLLVMSymbolizer(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("testtdata/llvm-symbolizer has only been tested on linux")
}
cmd := filepath.Join("testdata", "fake-llvm-symbolizer")
symbolizer, err := newLLVMSymbolizer(cmd, "foo", 0)
if err != nil {
t.Fatalf("newLLVMSymbolizer: unexpected error %v", err)
}
defer symbolizer.rw.close()
for _, c := range []struct {
addr uint64
frames []plugin.Frame
}{
{0x10, []plugin.Frame{
{Func: "Inlined_0x10", File: "foo.h", Line: 0},
{Func: "Func_0x10", File: "foo.c", Line: 2},
}},
{0x20, []plugin.Frame{
{Func: "Inlined_0x20", File: "foo.h", Line: 0},
{Func: "Func_0x20", File: "foo.c", Line: 2},
}},
} {
frames, err := symbolizer.addrInfo(c.addr)
if err != nil {
t.Errorf("LLVM: unexpected error %v", err)
continue
}
if !reflect.DeepEqual(frames, c.frames) {
t.Errorf("LLVM: expect %v; got %v\n", c.frames, frames)
}
}
}
func TestOpenMalformedELF(t *testing.T) {
// Test that opening a malformed ELF file will report an error containing
// the word "ELF".
bu := &Binutils{}
_, err := bu.Open(filepath.Join("testdata", "malformed_elf"), 0, 0, 0)
if err == nil {
t.Fatalf("Open: unexpected success")
}
if !strings.Contains(err.Error(), "ELF") {
t.Errorf("Open: got %v, want error containing 'ELF'", err)
}
}
func TestOpenMalformedMachO(t *testing.T) {
// Test that opening a malformed Mach-O file will report an error containing
// the word "Mach-O".
bu := &Binutils{}
_, err := bu.Open(filepath.Join("testdata", "malformed_macho"), 0, 0, 0)
if err == nil {
t.Fatalf("Open: unexpected success")
}
if !strings.Contains(err.Error(), "Mach-O") {
t.Errorf("Open: got %v, want error containing 'Mach-O'", err)
}
}

View file

@ -1,152 +0,0 @@
// 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 binutils
import (
"fmt"
"regexp"
"testing"
"github.com/google/pprof/internal/plugin"
)
// TestFindSymbols tests the FindSymbols routine using a hardcoded nm output.
func TestFindSymbols(t *testing.T) {
type testcase struct {
query, syms string
want []plugin.Sym
}
testsyms := `0000000000001000 t lineA001
0000000000001000 t lineA002
0000000000001000 t line1000
0000000000002000 t line200A
0000000000002000 t line2000
0000000000002000 t line200B
0000000000003000 t line3000
0000000000003000 t _ZNK4DumbclEPKc
0000000000003000 t lineB00C
0000000000003000 t line300D
0000000000004000 t _the_end
`
testcases := []testcase{
{
"line.*[AC]",
testsyms,
[]plugin.Sym{
{Name: []string{"lineA001"}, File: "object.o", Start: 0x1000, End: 0x1FFF},
{Name: []string{"line200A"}, File: "object.o", Start: 0x2000, End: 0x2FFF},
{Name: []string{"lineB00C"}, File: "object.o", Start: 0x3000, End: 0x3FFF},
},
},
{
"Dumb::operator",
testsyms,
[]plugin.Sym{
{Name: []string{"Dumb::operator()(char const*) const"}, File: "object.o", Start: 0x3000, End: 0x3FFF},
},
},
}
for _, tc := range testcases {
syms, err := findSymbols([]byte(tc.syms), "object.o", regexp.MustCompile(tc.query), 0)
if err != nil {
t.Fatalf("%q: findSymbols: %v", tc.query, err)
}
if err := checkSymbol(syms, tc.want); err != nil {
t.Errorf("%q: %v", tc.query, err)
}
}
}
func checkSymbol(got []*plugin.Sym, want []plugin.Sym) error {
if len(got) != len(want) {
return fmt.Errorf("unexpected number of symbols %d (want %d)", len(got), len(want))
}
for i, g := range got {
w := want[i]
if len(g.Name) != len(w.Name) {
return fmt.Errorf("names, got %d, want %d", len(g.Name), len(w.Name))
}
for n := range g.Name {
if g.Name[n] != w.Name[n] {
return fmt.Errorf("name %d, got %q, want %q", n, g.Name[n], w.Name[n])
}
}
if g.File != w.File {
return fmt.Errorf("filename, got %q, want %q", g.File, w.File)
}
if g.Start != w.Start {
return fmt.Errorf("start address, got %#x, want %#x", g.Start, w.Start)
}
if g.End != w.End {
return fmt.Errorf("end address, got %#x, want %#x", g.End, w.End)
}
}
return nil
}
// TestFunctionAssembly tests the FunctionAssembly routine by using a
// fake objdump script.
func TestFunctionAssembly(t *testing.T) {
type testcase struct {
s plugin.Sym
asm string
want []plugin.Inst
}
testcases := []testcase{
{
plugin.Sym{Name: []string{"symbol1"}, Start: 0x1000, End: 0x1FFF},
` 1000: instruction one
1001: instruction two
1002: instruction three
1003: instruction four
`,
[]plugin.Inst{
{Addr: 0x1000, Text: "instruction one"},
{Addr: 0x1001, Text: "instruction two"},
{Addr: 0x1002, Text: "instruction three"},
{Addr: 0x1003, Text: "instruction four"},
},
},
{
plugin.Sym{Name: []string{"symbol2"}, Start: 0x2000, End: 0x2FFF},
` 2000: instruction one
2001: instruction two
`,
[]plugin.Inst{
{Addr: 0x2000, Text: "instruction one"},
{Addr: 0x2001, Text: "instruction two"},
},
},
}
for _, tc := range testcases {
insts, err := disassemble([]byte(tc.asm))
if err != nil {
t.Fatalf("FunctionAssembly: %v", err)
}
if len(insts) != len(tc.want) {
t.Errorf("Unexpected number of assembly instructions %d (want %d)\n", len(insts), len(tc.want))
}
for i := range insts {
if insts[i] != tc.want[i] {
t.Errorf("Expected symbol %v, got %v\n", tc.want[i], insts[i])
}
}
}
}

View file

@ -1,31 +0,0 @@
#!/bin/bash -x
# This is a script that generates the test MacOS executables in this directory.
# It should be needed very rarely to run this script. It is mostly provided
# as a future reference on how the original binary set was created.
set -o errexit
cat <<EOF >/tmp/hello.cc
#include <stdio.h>
int main() {
printf("Hello, world!\n");
return 0;
}
EOF
cat <<EOF >/tmp/lib.c
int foo() {
return 1;
}
int bar() {
return 2;
}
EOF
cd $(dirname $0)
rm -rf exe_mac_64* lib_mac_64*
clang -g -o exe_mac_64 /tmp/hello.c
clang -g -o lib_mac_64 -dynamiclib /tmp/lib.c

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.exe_mac_64</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View file

@ -1,34 +0,0 @@
#!/bin/sh
#
# 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.
#
# Fake llvm-symbolizer to use in tests
set -f
IFS=" "
while read line; do
# line has form:
# filename 0xaddr
# Emit dummy output that matches llvm-symbolizer output format.
set -- $line
fname=$1
addr=$2
echo "Inlined_$addr"
echo "$fname.h"
echo "Func_$addr"
echo "$fname.c:2"
echo
done

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIdentifier</key>
<string>com.apple.xcode.dsym.lib_mac_64</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>dSYM</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View file

@ -1 +0,0 @@
ELF˙˙˙˙˙˙˙˙

View file

@ -1 +0,0 @@
销睨<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View file

@ -25,7 +25,7 @@ import (
"github.com/google/pprof/profile"
)
var tagFilterRangeRx = regexp.MustCompile("([[:digit:]]+)([[:alpha:]]+)")
var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)")
// applyFocus filters samples based on the focus/ignore options
func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, v variables, ui plugin.UI) error {

File diff suppressed because it is too large Load diff

View file

@ -1,758 +0,0 @@
// 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 driver
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"net"
"net/http"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"time"
"github.com/google/pprof/internal/binutils"
"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"
)
func TestSymbolizationPath(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test assumes Unix paths")
}
// Save environment variables to restore after test
saveHome := os.Getenv(homeEnv())
savePath := os.Getenv("PPROF_BINARY_PATH")
tempdir, err := ioutil.TempDir("", "home")
if err != nil {
t.Fatal("creating temp dir: ", err)
}
defer os.RemoveAll(tempdir)
os.MkdirAll(filepath.Join(tempdir, "pprof", "binaries", "abcde10001"), 0700)
os.Create(filepath.Join(tempdir, "pprof", "binaries", "abcde10001", "binary"))
obj := testObj{tempdir}
os.Setenv(homeEnv(), tempdir)
for _, tc := range []struct {
env, file, buildID, want string
msgCount int
}{
{"", "/usr/bin/binary", "", "/usr/bin/binary", 0},
{"", "/usr/bin/binary", "fedcb10000", "/usr/bin/binary", 0},
{"/usr", "/bin/binary", "", "/usr/bin/binary", 0},
{"", "/prod/path/binary", "abcde10001", filepath.Join(tempdir, "pprof/binaries/abcde10001/binary"), 0},
{"/alternate/architecture", "/usr/bin/binary", "", "/alternate/architecture/binary", 0},
{"/alternate/architecture", "/usr/bin/binary", "abcde10001", "/alternate/architecture/binary", 0},
{"/nowhere:/alternate/architecture", "/usr/bin/binary", "fedcb10000", "/usr/bin/binary", 1},
{"/nowhere:/alternate/architecture", "/usr/bin/binary", "abcde10002", "/usr/bin/binary", 1},
} {
os.Setenv("PPROF_BINARY_PATH", tc.env)
p := &profile.Profile{
Mapping: []*profile.Mapping{
{
File: tc.file,
BuildID: tc.buildID,
},
},
}
s := &source{}
locateBinaries(p, s, obj, &proftest.TestUI{T: t, Ignore: tc.msgCount})
if file := p.Mapping[0].File; file != tc.want {
t.Errorf("%s:%s:%s, want %s, got %s", tc.env, tc.file, tc.buildID, tc.want, file)
}
}
os.Setenv(homeEnv(), saveHome)
os.Setenv("PPROF_BINARY_PATH", savePath)
}
func TestCollectMappingSources(t *testing.T) {
const startAddress uint64 = 0x40000
const url = "http://example.com"
for _, tc := range []struct {
file, buildID string
want plugin.MappingSources
}{
{"/usr/bin/binary", "buildId", mappingSources("buildId", url, startAddress)},
{"/usr/bin/binary", "", mappingSources("/usr/bin/binary", url, startAddress)},
{"", "", mappingSources(url, url, startAddress)},
} {
p := &profile.Profile{
Mapping: []*profile.Mapping{
{
File: tc.file,
BuildID: tc.buildID,
Start: startAddress,
},
},
}
got := collectMappingSources(p, url)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("%s:%s, want %v, got %v", tc.file, tc.buildID, tc.want, got)
}
}
}
func TestUnsourceMappings(t *testing.T) {
for _, tc := range []struct {
file, buildID, want string
}{
{"/usr/bin/binary", "buildId", "/usr/bin/binary"},
{"http://example.com", "", ""},
} {
p := &profile.Profile{
Mapping: []*profile.Mapping{
{
File: tc.file,
BuildID: tc.buildID,
},
},
}
unsourceMappings(p)
if got := p.Mapping[0].File; got != tc.want {
t.Errorf("%s:%s, want %s, got %s", tc.file, tc.buildID, tc.want, got)
}
}
}
type testObj struct {
home string
}
func (o testObj) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
switch file {
case "/alternate/architecture/binary":
return testFile{file, "abcde10001"}, nil
case "/usr/bin/binary":
return testFile{file, "fedcb10000"}, nil
case filepath.Join(o.home, "pprof/binaries/abcde10001/binary"):
return testFile{file, "abcde10001"}, nil
}
return nil, fmt.Errorf("not found: %s", file)
}
func (testObj) Demangler(_ string) func(names []string) (map[string]string, error) {
return func(names []string) (map[string]string, error) { return nil, nil }
}
func (testObj) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { return nil, nil }
type testFile struct{ name, buildID string }
func (f testFile) Name() string { return f.name }
func (testFile) Base() uint64 { return 0 }
func (f testFile) BuildID() string { return f.buildID }
func (testFile) SourceLine(addr uint64) ([]plugin.Frame, error) { return nil, nil }
func (testFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { return nil, nil }
func (testFile) Close() error { return nil }
func TestFetch(t *testing.T) {
const path = "testdata/"
type testcase struct {
source, execName string
}
for _, tc := range []testcase{
{path + "go.crc32.cpu", ""},
{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}, &httpTransport{})
if err != nil {
t.Fatalf("%s: %s", tc.source, err)
}
if len(p.Sample) == 0 {
t.Errorf("%s: want non-zero samples", tc.source)
}
if e := tc.execName; e != "" {
switch {
case len(p.Mapping) == 0 || p.Mapping[0] == nil:
t.Errorf("%s: want mapping[0].execName == %s, got no mappings", tc.source, e)
case p.Mapping[0].File != e:
t.Errorf("%s: want mapping[0].execName == %s, got %s", tc.source, e, p.Mapping[0].File)
}
}
}
}
func TestFetchWithBase(t *testing.T) {
baseVars := pprofVariables
defer func() { pprofVariables = baseVars }()
type WantSample struct {
values []int64
labels map[string][]string
}
const path = "testdata/"
type testcase struct {
desc string
sources []string
bases []string
diffBases []string
normalize bool
wantSamples []WantSample
wantErrorMsg string
}
testcases := []testcase{
{
"not normalized base is same as source",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention"},
nil,
false,
nil,
"",
},
{
"not normalized base is same as source",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention"},
nil,
false,
nil,
"",
},
{
"not normalized single source, multiple base (all profiles same)",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention", path + "cppbench.contention"},
nil,
false,
[]WantSample{
{
values: []int64{-2700, -608881724},
labels: map[string][]string{},
},
{
values: []int64{-100, -23992},
labels: map[string][]string{},
},
{
values: []int64{-200, -179943},
labels: map[string][]string{},
},
{
values: []int64{-100, -17778444},
labels: map[string][]string{},
},
{
values: []int64{-100, -75976},
labels: map[string][]string{},
},
{
values: []int64{-300, -63568134},
labels: map[string][]string{},
},
},
"",
},
{
"not normalized, different base and source",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.small.contention"},
nil,
false,
[]WantSample{
{
values: []int64{1700, 608878600},
labels: map[string][]string{},
},
{
values: []int64{100, 23992},
labels: map[string][]string{},
},
{
values: []int64{200, 179943},
labels: map[string][]string{},
},
{
values: []int64{100, 17778444},
labels: map[string][]string{},
},
{
values: []int64{100, 75976},
labels: map[string][]string{},
},
{
values: []int64{300, 63568134},
labels: map[string][]string{},
},
},
"",
},
{
"normalized base is same as source",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention"},
nil,
true,
nil,
"",
},
{
"normalized single source, multiple base (all profiles same)",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention", path + "cppbench.contention"},
nil,
true,
nil,
"",
},
{
"normalized different base and source",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.small.contention"},
nil,
true,
[]WantSample{
{
values: []int64{-229, -370},
labels: map[string][]string{},
},
{
values: []int64{28, 0},
labels: map[string][]string{},
},
{
values: []int64{57, 0},
labels: map[string][]string{},
},
{
values: []int64{28, 80},
labels: map[string][]string{},
},
{
values: []int64{28, 0},
labels: map[string][]string{},
},
{
values: []int64{85, 287},
labels: map[string][]string{},
},
},
"",
},
{
"not normalized diff base is same as source",
[]string{path + "cppbench.contention"},
nil,
[]string{path + "cppbench.contention"},
false,
[]WantSample{
{
values: []int64{2700, 608881724},
labels: map[string][]string{},
},
{
values: []int64{100, 23992},
labels: map[string][]string{},
},
{
values: []int64{200, 179943},
labels: map[string][]string{},
},
{
values: []int64{100, 17778444},
labels: map[string][]string{},
},
{
values: []int64{100, 75976},
labels: map[string][]string{},
},
{
values: []int64{300, 63568134},
labels: map[string][]string{},
},
{
values: []int64{-2700, -608881724},
labels: map[string][]string{"pprof::base": {"true"}},
},
{
values: []int64{-100, -23992},
labels: map[string][]string{"pprof::base": {"true"}},
},
{
values: []int64{-200, -179943},
labels: map[string][]string{"pprof::base": {"true"}},
},
{
values: []int64{-100, -17778444},
labels: map[string][]string{"pprof::base": {"true"}},
},
{
values: []int64{-100, -75976},
labels: map[string][]string{"pprof::base": {"true"}},
},
{
values: []int64{-300, -63568134},
labels: map[string][]string{"pprof::base": {"true"}},
},
},
"",
},
{
"diff_base and base both specified",
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention"},
[]string{path + "cppbench.contention"},
false,
nil,
"-base and -diff_base flags cannot both be specified",
},
}
for _, tc := range testcases {
t.Run(tc.desc, func(t *testing.T) {
pprofVariables = baseVars.makeCopy()
f := testFlags{
stringLists: map[string][]string{
"base": tc.bases,
"diff_base": tc.diffBases,
},
bools: map[string]bool{
"normalize": tc.normalize,
},
}
f.args = tc.sources
o := setDefaults(&plugin.Options{
UI: &proftest.TestUI{T: t, AllowRx: "Local symbolization failed|Some binary filenames not available"},
Flagset: f,
HTTPTransport: transport.New(nil),
})
src, _, err := parseFlags(o)
if tc.wantErrorMsg != "" {
if err == nil {
t.Fatalf("got nil, want error %q", tc.wantErrorMsg)
}
if gotErrMsg := err.Error(); gotErrMsg != tc.wantErrorMsg {
t.Fatalf("got error %q, want error %q", gotErrMsg, tc.wantErrorMsg)
}
return
}
if err != nil {
t.Fatalf("got error %q, want no error", err)
}
p, err := fetchProfiles(src, o)
if err != nil {
t.Fatalf("got error %q, want no error", err)
}
if got, want := len(p.Sample), len(tc.wantSamples); got != want {
t.Fatalf("got %d samples want %d", got, want)
}
for i, sample := range p.Sample {
if !reflect.DeepEqual(tc.wantSamples[i].values, sample.Value) {
t.Errorf("for sample %d got values %v, want %v", i, sample.Value, tc.wantSamples[i])
}
if !reflect.DeepEqual(tc.wantSamples[i].labels, sample.Label) {
t.Errorf("for sample %d got labels %v, want %v", i, sample.Label, tc.wantSamples[i].labels)
}
}
})
}
}
// mappingSources creates MappingSources map with a single item.
func mappingSources(key, source string, start uint64) plugin.MappingSources {
return plugin.MappingSources{
key: []struct {
Source string
Start uint64
}{
{Source: source, Start: start},
},
}
}
type httpTransport struct{}
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", req.URL.String())
}
t := &http.Transport{}
t.RegisterProtocol("file", http.NewFileTransport(http.Dir("testdata/")))
c := &http.Client{Transport: t}
return c.Get("file:///" + file)
}
func closedError() string {
if runtime.GOOS == "plan9" {
return "listen hungup"
}
return "use of closed"
}
func TestHTTPSInsecure(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 }()
tlsCert, _, _ := selfSignedCert(t, "")
tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}}
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()
address := "https+insecure://" + l.Addr().String() + "/debug/pprof/goroutine"
s := &source{
Sources: []string{address},
Seconds: 10,
Timeout: 10,
Symbolize: "remote",
}
o := &plugin.Options{
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)
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, "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)
}
}
func checkProfileHasFunction(p *profile.Profile, fname string) error {
for _, f := range p.Function {
if strings.Contains(f.Name, fname) {
return nil
}
}
return fmt.Errorf("got %s, want function %q", p.String(), fname)
}
// 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)
}
b, err := x509.MarshalECPrivateKey(privKey)
if err != nil {
t.Fatalf("failed to marshal private key: %v", err)
}
bk := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
tmpl := x509.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)
if err != nil {
t.Fatalf("failed to create cert: %v", err)
}
bc := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b})
cert, err := tls.X509KeyPair(bc, bk)
if err != nil {
t.Fatalf("failed to create TLS key pair: %v", err)
}
return cert, bc, bk
}

View file

@ -1,3 +1,17 @@
// Copyright 2018 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 driver
import (

View file

@ -1,316 +0,0 @@
// 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 driver
import (
"fmt"
"math/rand"
"strings"
"testing"
"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"
)
func TestShell(t *testing.T) {
p := &profile.Profile{}
generateReportWrapper = checkValue
defer func() { generateReportWrapper = generateReport }()
// Use test commands and variables to exercise interactive processing
var savedCommands commands
savedCommands, pprofCommands = pprofCommands, testCommands
defer func() { pprofCommands = savedCommands }()
savedVariables := pprofVariables
defer func() { pprofVariables = savedVariables }()
// Random interleave of independent scripts
pprofVariables = testVariables(savedVariables)
// 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)
}
// Random interleave of independent scripts
pprofVariables = testVariables(savedVariables)
o.UI = newUI(t, interleave(script, 1))
if err := interactive(p, o); err != nil {
t.Error("second attempt:", err)
}
// Random interleave of independent scripts with shortcuts
pprofVariables = testVariables(savedVariables)
var scScript []string
pprofShortcuts, scScript = makeShortcuts(interleave(script, 2), 1)
o.UI = newUI(t, scScript)
if err := interactive(p, o); err != nil {
t.Error("first shortcut attempt:", err)
}
// Random interleave of independent scripts with shortcuts
pprofVariables = testVariables(savedVariables)
pprofShortcuts, scScript = makeShortcuts(interleave(script, 1), 2)
o.UI = newUI(t, scScript)
if err := interactive(p, o); err != nil {
t.Error("second shortcut attempt:", err)
}
// Group with invalid value
pprofVariables = testVariables(savedVariables)
ui := &proftest.TestUI{
T: t,
Input: []string{"cumulative=this"},
AllowRx: `Unrecognized value for cumulative: "this". Use one of cum, flat`,
}
o.UI = ui
if err := interactive(p, o); err != nil {
t.Error("invalid group value:", err)
}
// Confirm error message written out once.
if ui.NumAllowRxMatches != 1 {
t.Errorf("want error message to be printed 1 time, got %v", ui.NumAllowRxMatches)
}
// Verify propagation of IO errors
pprofVariables = testVariables(savedVariables)
o.UI = newUI(t, []string{"**error**"})
if err := interactive(p, o); err == nil {
t.Error("expected IO error, got nil")
}
}
var testCommands = commands{
"check": &command{report.Raw, nil, nil, true, "", ""},
}
func testVariables(base variables) variables {
v := base.makeCopy()
v["b"] = &variable{boolKind, "f", "", ""}
v["bb"] = &variable{boolKind, "f", "", ""}
v["i"] = &variable{intKind, "0", "", ""}
v["ii"] = &variable{intKind, "0", "", ""}
v["f"] = &variable{floatKind, "0", "", ""}
v["ff"] = &variable{floatKind, "0", "", ""}
v["s"] = &variable{stringKind, "", "", ""}
v["ss"] = &variable{stringKind, "", "", ""}
v["ta"] = &variable{boolKind, "f", "radio", ""}
v["tb"] = &variable{boolKind, "f", "radio", ""}
v["tc"] = &variable{boolKind, "t", "radio", ""}
return v
}
// script contains sequences of commands to be executed for testing. Commands
// are split by semicolon and interleaved randomly, so they must be
// independent from each other.
var script = []string{
"bb=true;bb=false;check bb=false;bb=yes;check bb=true",
"b=1;check b=true;b=n;check b=false",
"i=-1;i=-2;check i=-2;i=999999;check i=999999",
"check ii=0;ii=-1;check ii=-1;ii=100;check ii=100",
"f=-1;f=-2.5;check f=-2.5;f=0.0001;check f=0.0001",
"check ff=0;ff=-1.01;check ff=-1.01;ff=100;check ff=100",
"s=one;s=two;check s=two",
"ss=tree;check ss=tree;ss=;check ss;ss=forest;check ss=forest",
"ta=true;check ta=true;check tb=false;check tc=false;tb=1;check tb=true;check ta=false;check tc=false;tc=yes;check tb=false;check ta=false;check tc=true",
}
func makeShortcuts(input []string, seed int) (shortcuts, []string) {
rand.Seed(int64(seed))
s := shortcuts{}
var output, chunk []string
for _, l := range input {
chunk = append(chunk, l)
switch rand.Intn(3) {
case 0:
// Create a macro for commands in 'chunk'.
macro := fmt.Sprintf("alias%d", len(s))
s[macro] = chunk
output = append(output, macro)
chunk = nil
case 1:
// Append commands in 'chunk' by themselves.
output = append(output, chunk...)
chunk = nil
case 2:
// Accumulate commands into 'chunk'
}
}
output = append(output, chunk...)
return s, output
}
func newUI(t *testing.T, input []string) plugin.UI {
return &proftest.TestUI{
T: t,
Input: input,
}
}
func checkValue(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
if len(cmd) != 2 {
return fmt.Errorf("expected len(cmd)==2, got %v", cmd)
}
input := cmd[1]
args := strings.SplitN(input, "=", 2)
if len(args) == 0 {
return fmt.Errorf("unexpected empty input")
}
name, value := args[0], ""
if len(args) == 2 {
value = args[1]
}
gotv := vars[name]
if gotv == nil {
return fmt.Errorf("Could not find variable named %s", name)
}
if got := gotv.stringValue(); got != value {
return fmt.Errorf("Variable %s, want %s, got %s", name, value, got)
}
return nil
}
func interleave(input []string, seed int) []string {
var inputs [][]string
for _, s := range input {
inputs = append(inputs, strings.Split(s, ";"))
}
rand.Seed(int64(seed))
var output []string
for len(inputs) > 0 {
next := rand.Intn(len(inputs))
output = append(output, inputs[next][0])
if tail := inputs[next][1:]; len(tail) > 0 {
inputs[next] = tail
} else {
inputs = append(inputs[:next], inputs[next+1:]...)
}
}
return output
}
func TestInteractiveCommands(t *testing.T) {
type interactiveTestcase struct {
input string
want map[string]string
}
testcases := []interactiveTestcase{
{
"top 10 --cum focus1 -ignore focus2",
map[string]string{
"functions": "true",
"nodecount": "10",
"cum": "true",
"focus": "focus1|focus2",
"ignore": "ignore",
},
},
{
"top10 --cum focus1 -ignore focus2",
map[string]string{
"functions": "true",
"nodecount": "10",
"cum": "true",
"focus": "focus1|focus2",
"ignore": "ignore",
},
},
{
"dot",
map[string]string{
"functions": "true",
"nodecount": "80",
"cum": "false",
},
},
{
"tags -ignore1 -ignore2 focus1 >out",
map[string]string{
"functions": "true",
"nodecount": "80",
"cum": "false",
"output": "out",
"tagfocus": "focus1",
"tagignore": "ignore1|ignore2",
},
},
{
"weblist find -test",
map[string]string{
"functions": "false",
"addresses": "true",
"noinlines": "true",
"nodecount": "0",
"cum": "false",
"flat": "true",
"ignore": "test",
},
},
{
"callgrind fun -ignore >out",
map[string]string{
"functions": "false",
"addresses": "true",
"nodecount": "0",
"cum": "false",
"flat": "true",
"output": "out",
},
},
{
"999",
nil, // Error
},
}
for _, tc := range testcases {
cmd, vars, err := parseCommandLine(strings.Fields(tc.input))
if tc.want == nil && err != nil {
// Error expected
continue
}
if err != nil {
t.Errorf("failed on %q: %v", tc.input, err)
continue
}
// Get report output format
c := pprofCommands[cmd[0]]
if c == nil {
t.Errorf("unexpected nil command")
}
vars = applyCommandOverrides(cmd[0], c.format, vars)
for n, want := range tc.want {
if got := vars[n].stringValue(); got != want {
t.Errorf("failed on %q, cmd=%q, %s got %s, want %s", tc.input, cmd, n, got, want)
}
}
}
}

View file

@ -1,24 +0,0 @@
--- contentionz 1 ---
cycles/second = 3201000000
sampling period = 100
ms since reset = 16502830
discarded samples = 0
19490304 27 @ 0xbccc97 0xc61202 0x42ed5f 0x42edc1 0x42e15a 0x5261af 0x526edf 0x5280ab 0x79e80a 0x7a251b 0x7a296d 0xa456e4 0x7fcdc2ff214e
768 1 @ 0xbccc97 0xa42dc7 0xa456e4 0x7fcdc2ff214e
5760 2 @ 0xbccc97 0xb82b73 0xb82bcb 0xb87eab 0xb8814c 0x4e969d 0x4faa17 0x4fc5f6 0x4fd028 0x4fd230 0x79e80a 0x7a251b 0x7a296d 0xa456e4 0x7fcdc2ff214e
569088 1 @ 0xbccc97 0xb82b73 0xb82bcb 0xb87f08 0xb8814c 0x42ed5f 0x42edc1 0x42e15a 0x5261af 0x526edf 0x5280ab 0x79e80a 0x7a251b 0x7a296d 0xa456e4 0x7fcdc2ff214e
2432 1 @ 0xbccc97 0xb82b73 0xb82bcb 0xb87eab 0xb8814c 0x7aa74c 0x7ab844 0x7ab914 0x79e9e9 0x79e326 0x4d299e 0x4d4b7b 0x4b7be8 0x4b7ff1 0x4d2dae 0x79e80a
2034816 3 @ 0xbccc97 0xb82f0f 0xb83003 0xb87d50 0xc635f0 0x42ecc3 0x42e14c 0x5261af 0x526edf 0x5280ab 0x79e80a 0x7a251b 0x7a296d 0xa456e4 0x7fcdc2ff214e
--- Memory map: ---
00400000-00fcb000: cppbench_server_main
7fcdc231e000-7fcdc2321000: /libnss_cache-2.15.so
7fcdc2522000-7fcdc252e000: /libnss_files-2.15.so
7fcdc272f000-7fcdc28dd000: /libc-2.15.so
7fcdc2ae7000-7fcdc2be2000: /libm-2.15.so
7fcdc2de3000-7fcdc2dea000: /librt-2.15.so
7fcdc2feb000-7fcdc3003000: /libpthread-2.15.so
7fcdc3208000-7fcdc320a000: /libdl-2.15.so
7fcdc340c000-7fcdc3415000: /libcrypt-2.15.so
7fcdc3645000-7fcdc3669000: /ld-2.15.so
7fff86bff000-7fff86c00000: [vdso]
ffffffffff600000-ffffffffff601000: [vsyscall]

View file

@ -1,19 +0,0 @@
--- contentionz 1 ---
cycles/second = 3201000000
sampling period = 100
ms since reset = 16502830
discarded samples = 0
100 10 @ 0xbccc97 0xc61202 0x42ed5f 0x42edc1 0x42e15a 0x5261af 0x526edf 0x5280ab 0x79e80a 0x7a251b 0x7a296d 0xa456e4 0x7fcdc2ff214e
--- Memory map: ---
00400000-00fcb000: cppbench_server_main
7fcdc231e000-7fcdc2321000: /libnss_cache-2.15.so
7fcdc2522000-7fcdc252e000: /libnss_files-2.15.so
7fcdc272f000-7fcdc28dd000: /libc-2.15.so
7fcdc2ae7000-7fcdc2be2000: /libm-2.15.so
7fcdc2de3000-7fcdc2dea000: /librt-2.15.so
7fcdc2feb000-7fcdc3003000: /libpthread-2.15.so
7fcdc3208000-7fcdc320a000: /libdl-2.15.so
7fcdc340c000-7fcdc3415000: /libcrypt-2.15.so
7fcdc3645000-7fcdc3669000: /ld-2.15.so
7fff86bff000-7fff86c00000: [vdso]
ffffffffff600000-ffffffffff601000: [vsyscall]

View file

@ -1,17 +0,0 @@
line1
line2
line3
line4
line5
line6
line7
line8
line9
line0
line1
line2
line3
line4
line5

View file

@ -1,17 +0,0 @@
line1
line2
line3
line4
line5
line6
line7
line8
line9
line0
line1
line2
line3
line4
line5

View file

@ -1,17 +0,0 @@
line1
line2
line3
line4
line5
line6
line7
line8
line9
line0
line1
line2
line3
line4
line5

View file

@ -1,10 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid-contention" [shape=box fontsize=16 label="Build ID: buildid-contention\lComment #1\lComment #2\lType: delay\lShowing nodes accounting for 149.50ms, 100% of 149.50ms total\l"] }
N1 [label="file3000.src\n32.77ms (21.92%)\nof 149.50ms (100%)" id="node1" fontsize=20 shape=box tooltip="testdata/file3000.src (149.50ms)" color="#b20000" fillcolor="#edd5d5"]
N2 [label="file1000.src\n51.20ms (34.25%)" id="node2" fontsize=23 shape=box tooltip="testdata/file1000.src (51.20ms)" color="#b23100" fillcolor="#eddbd5"]
N3 [label="file2000.src\n65.54ms (43.84%)\nof 75.78ms (50.68%)" id="node3" fontsize=24 shape=box tooltip="testdata/file2000.src (75.78ms)" color="#b22000" fillcolor="#edd9d5"]
N1 -> N3 [label=" 75.78ms" weight=51 penwidth=3 color="#b22000" tooltip="testdata/file3000.src -> testdata/file2000.src (75.78ms)" labeltooltip="testdata/file3000.src -> testdata/file2000.src (75.78ms)"]
N1 -> N2 [label=" 40.96ms" weight=28 penwidth=2 color="#b23900" tooltip="testdata/file3000.src -> testdata/file1000.src (40.96ms)" labeltooltip="testdata/file3000.src -> testdata/file1000.src (40.96ms)"]
N3 -> N2 [label=" 10.24ms" weight=7 color="#b29775" tooltip="testdata/file2000.src -> testdata/file1000.src (10.24ms)" labeltooltip="testdata/file2000.src -> testdata/file1000.src (10.24ms)"]
}

View file

@ -1,9 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid-contention" [shape=box fontsize=16 label="Build ID: buildid-contention\lComment #1\lComment #2\lType: delay\lActive filters:\l focus=[X1]000\l ignore=[X3]002\lShowing nodes accounting for 40.96ms, 27.40% of 149.50ms total\l"] }
N1 [label="0000000000001000\nline1000\nfile1000.src:1\n40.96ms (27.40%)" id="node1" fontsize=24 shape=box tooltip="0000000000001000 line1000 testdata/file1000.src:1 (40.96ms)" color="#b23900" fillcolor="#edddd5"]
N2 [label="0000000000003001\nline3000\nfile3000.src:5\n0 of 40.96ms (27.40%)" id="node2" fontsize=8 shape=box tooltip="0000000000003001 line3000 testdata/file3000.src:5 (40.96ms)" color="#b23900" fillcolor="#edddd5"]
N3 [label="0000000000003001\nline3001\nfile3000.src:3\n0 of 40.96ms (27.40%)" id="node3" fontsize=8 shape=box tooltip="0000000000003001 line3001 testdata/file3000.src:3 (40.96ms)" color="#b23900" fillcolor="#edddd5"]
N2 -> N3 [label=" 40.96ms\n (inline)" weight=28 penwidth=2 color="#b23900" tooltip="0000000000003001 line3000 testdata/file3000.src:5 -> 0000000000003001 line3001 testdata/file3000.src:3 (40.96ms)" labeltooltip="0000000000003001 line3000 testdata/file3000.src:5 -> 0000000000003001 line3001 testdata/file3000.src:3 (40.96ms)"]
N3 -> N1 [label=" 40.96ms" weight=28 penwidth=2 color="#b23900" tooltip="0000000000003001 line3001 testdata/file3000.src:3 -> 0000000000001000 line1000 testdata/file1000.src:1 (40.96ms)" labeltooltip="0000000000003001 line3001 testdata/file3000.src:3 -> 0000000000001000 line1000 testdata/file1000.src:1 (40.96ms)"]
}

View file

@ -1,99 +0,0 @@
positions: instr line
events: cpu(ms)
ob=(1) /path/to/testbinary
fl=(1) testdata/file1000.src
fn=(1) line1000
0x1000 1 1000
* 1 100
ob=(1)
fl=(2) testdata/file2000.src
fn=(2) line2001
+4096 9 10
ob=(1)
fl=(3) testdata/file3000.src
fn=(3) line3002
+4096 2 10
cfl=(2)
cfn=(4) line2000 [1/2]
calls=0 * 4
* * 1000
ob=(1)
fl=(2)
fn=(5) line2000
-4096 4 0
cfl=(2)
cfn=(6) line2001 [2/2]
calls=0 -4096 9
* * 1000
* 4 0
cfl=(2)
cfn=(7) line2001 [1/2]
calls=0 * 9
* * 10
ob=(1)
fl=(2)
fn=(2)
* 9 0
cfl=(1)
cfn=(8) line1000 [1/2]
calls=0 -4096 1
* * 1000
ob=(1)
fl=(3)
fn=(9) line3000
+4096 6 0
cfl=(3)
cfn=(10) line3001 [1/2]
calls=0 +4096 5
* * 1010
ob=(1)
fl=(3)
fn=(11) line3001
* 5 0
cfl=(3)
cfn=(12) line3002 [1/2]
calls=0 * 2
* * 1010
ob=(1)
fl=(3)
fn=(9)
+1 9 0
cfl=(3)
cfn=(13) line3001 [2/2]
calls=0 +1 8
* * 100
ob=(1)
fl=(3)
fn=(11)
* 8 0
cfl=(1)
cfn=(14) line1000 [2/2]
calls=0 -8193 1
* * 100
ob=(1)
fl=(3)
fn=(9)
+1 9 0
cfl=(3)
cfn=(15) line3002 [2/2]
calls=0 +1 5
* * 10
ob=(1)
fl=(3)
fn=(3)
* 5 0
cfl=(2)
cfn=(16) line2000 [2/2]
calls=0 -4098 4
* * 10

View file

@ -1,88 +0,0 @@
positions: instr line
events: cpu(ms)
ob=(1) /path/to/testbinary
fl=(1) testdata/file1000.src
fn=(1) line1000
0x1000 1 1100
ob=(1)
fl=(2) testdata/file2000.src
fn=(2) line2001
+4096 9 10
cfl=(1)
cfn=(1)
calls=0 * 1
* * 1000
ob=(1)
fl=(3) testdata/file3000.src
fn=(3) line3002
+4096 2 10
cfl=(2)
cfn=(4) line2000
calls=0 * 4
* * 1000
ob=(1)
fl=(2)
fn=(4)
-4096 4 0
cfl=(2)
cfn=(2)
calls=0 -4096 9
* * 1010
ob=(1)
fl=(3)
fn=(5) line3000
+4096 6 0
cfl=(3)
cfn=(6) line3001
calls=0 +4096 5
* * 1010
ob=(1)
fl=(3)
fn=(6)
* 5 0
cfl=(3)
cfn=(3)
calls=0 * 2
* * 1010
ob=(1)
fl=(3)
fn=(5)
+1 9 0
cfl=(3)
cfn=(6)
calls=0 +1 8
* * 100
ob=(1)
fl=(3)
fn=(6)
* 8 0
cfl=(1)
cfn=(1)
calls=0 -8193 1
* * 100
ob=(1)
fl=(3)
fn=(5)
+1 9 0
cfl=(3)
cfn=(3)
calls=0 +1 5
* * 10
ob=(1)
fl=(3)
fn=(3)
* 5 0
cfl=(2)
cfn=(4)
calls=0 -4098 4
* * 10

View file

@ -1 +0,0 @@
some-comment

View file

@ -1,8 +0,0 @@
Active filters:
focus=[12]00
hide=line[X3]0
Showing nodes accounting for 1.11s, 99.11% of 1.12s total
flat flat% sum% cum cum%
1.10s 98.21% 98.21% 1.10s 98.21% line1000 testdata/file1000.src:1
0 0% 98.21% 1.01s 90.18% line2000 testdata/file2000.src:4
0.01s 0.89% 99.11% 1.01s 90.18% line2001 testdata/file2000.src:9 (inline)

View file

@ -1,7 +0,0 @@
Active filters:
hide=line[X3]0
Showing nodes accounting for 1.11s, 99.11% of 1.12s total
flat flat% sum% cum cum%
1.10s 98.21% 98.21% 1.10s 98.21% line1000 testdata/file1000.src:1
0 0% 98.21% 1.01s 90.18% line2000 testdata/file2000.src:4
0.01s 0.89% 99.11% 1.01s 90.18% line2001 testdata/file2000.src:9 (inline)

View file

@ -1,7 +0,0 @@
Active filters:
show=[12]00
Showing nodes accounting for 1.11s, 99.11% of 1.12s total
flat flat% sum% cum cum%
1.10s 98.21% 98.21% 1.10s 98.21% line1000 testdata/file1000.src:1
0 0% 98.21% 1.01s 90.18% line2000 testdata/file2000.src:4
0.01s 0.89% 99.11% 1.01s 90.18% line2001 testdata/file2000.src:9 (inline)

View file

@ -1,5 +0,0 @@
Active filters:
hide=mangled[X3]0
Showing nodes accounting for 1s, 100% of 1s total
flat flat% sum% cum cum%
1s 100% 100% 1s 100% mangled1000 testdata/file1000.src:1

View file

@ -1,16 +0,0 @@
Active filters:
show_from=line2
Showing nodes accounting for 1.01s, 90.18% of 1.12s total
----------------------------------------------------------+-------------
flat flat% sum% cum cum% calls calls% + context
----------------------------------------------------------+-------------
0 0% 0% 1.01s 90.18% | line2000 testdata/file2000.src:4
1.01s 100% | line2001 testdata/file2000.src:9 (inline)
----------------------------------------------------------+-------------
1.01s 100% | line2000 testdata/file2000.src:4 (inline)
0.01s 0.89% 0.89% 1.01s 90.18% | line2001 testdata/file2000.src:9
1s 99.01% | line1000 testdata/file1000.src:1
----------------------------------------------------------+-------------
1s 100% | line2001 testdata/file2000.src:9
1s 89.29% 90.18% 1s 89.29% | line1000 testdata/file1000.src:1
----------------------------------------------------------+-------------

View file

@ -1,14 +0,0 @@
Total: 1.12s
ROUTINE ======================== line1000
1.10s 1.10s (flat, cum) 98.21% of Total
1.10s 1.10s 1000: instruction one ;line1000 file1000.src:1
. . 1001: instruction two ;file1000.src:1
. . 1002: instruction three ;file1000.src:2
. . 1003: instruction four ;file1000.src:1
ROUTINE ======================== line3000
10ms 1.12s (flat, cum) 100% of Total
10ms 1.01s 3000: instruction one ;line3000 file3000.src:6
. 100ms 3001: instruction two ;line3000 file3000.src:9
. 10ms 3002: instruction three
. . 3003: instruction four
. . 3004: instruction five

View file

@ -1,7 +0,0 @@
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

View file

@ -1,106 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Pprof listing</title>
<style type="text/css">
body {
font-family: sans-serif;
}
h1 {
font-size: 1.5em;
margin-bottom: 4px;
}
.legend {
font-size: 1.25em;
}
.line, .nop, .unimportant {
color: #aaaaaa;
}
.inlinesrc {
color: #000066;
}
.deadsrc {
cursor: pointer;
}
.deadsrc:hover {
background-color: #eeeeee;
}
.livesrc {
color: #0000ff;
cursor: pointer;
}
.livesrc:hover {
background-color: #eeeeee;
}
.asm {
color: #008800;
display: none;
}
</style>
<script type="text/javascript">
function pprof_toggle_asm(e) {
var target;
if (!e) e = window.event;
if (e.target) target = e.target;
else if (e.srcElement) target = e.srcElement;
if (target) {
var asm = target.nextSibling;
if (asm && asm.className == "asm") {
asm.style.display = (asm.style.display == "block" ? "" : "block");
e.preventDefault();
return false;
}
}
}
</script>
</head>
<body>
<div class="legend">File: testbinary<br>
Type: cpu<br>
Duration: 10s, Total samples = 1.12s (11.20%)<br>Total: 1.12s</div><h2>line1000</h2><p class="filename">testdata/file1000.src</p>
<pre onClick="pprof_toggle_asm(event)">
Total: 1.10s 1.10s (flat, cum) 98.21%
<span class=line> 1</span> <span class=deadsrc> 1.10s 1.10s line1 </span><span class=asm> 1.10s 1.10s 1000: instruction one <span class=unimportant>file1000.src:1</span>
. . 1001: instruction two <span class=unimportant>file1000.src:1</span>
. . 1003: instruction four <span class=unimportant>file1000.src:1</span>
</span>
<span class=line> 2</span> <span class=deadsrc> . . line2 </span><span class=asm> . . 1002: instruction three <span class=unimportant>file1000.src:2</span>
</span>
<span class=line> 3</span> <span class=nop> . . line3 </span>
<span class=line> 4</span> <span class=nop> . . line4 </span>
<span class=line> 5</span> <span class=nop> . . line5 </span>
<span class=line> 6</span> <span class=nop> . . line6 </span>
<span class=line> 7</span> <span class=nop> . . line7 </span>
</pre>
<h2>line3000</h2><p class="filename">testdata/file3000.src</p>
<pre onClick="pprof_toggle_asm(event)">
Total: 10ms 1.12s (flat, cum) 100%
<span class=line> 1</span> <span class=nop> . . line1 </span>
<span class=line> 2</span> <span class=nop> . . line2 </span>
<span class=line> 3</span> <span class=nop> . . line3 </span>
<span class=line> 4</span> <span class=nop> . . line4 </span>
<span class=line> 5</span> <span class=nop> . . line5 </span>
<span class=line> 6</span> <span class=deadsrc> 10ms 1.01s line6 </span><span class=asm> 10ms 1.01s 3000: instruction one <span class=unimportant>file3000.src:6</span>
</span>
<span class=line> 7</span> <span class=nop> . . line7 </span>
<span class=line> 8</span> <span class=nop> . . line8 </span>
<span class=line> 9</span> <span class=deadsrc> . 110ms line9 </span><span class=asm> . 100ms 3001: instruction two <span class=unimportant>file3000.src:9</span>
. 10ms 3002: instruction three <span class=unimportant>file3000.src:9</span>
. . 3003: instruction four <span class=unimportant></span>
. . 3004: instruction five <span class=unimportant></span>
</span>
<span class=line> 10</span> <span class=nop> . . line0 </span>
<span class=line> 11</span> <span class=nop> . . line1 </span>
<span class=line> 12</span> <span class=nop> . . line2 </span>
<span class=line> 13</span> <span class=nop> . . line3 </span>
<span class=line> 14</span> <span class=nop> . . line4 </span>
</pre>
</body>
</html>

View file

@ -1,5 +0,0 @@
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

View file

@ -1,21 +0,0 @@
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.12s (11.20%)\lShowing nodes accounting for 1.11s, 99.11% of 1.12s total\lDropped 3 nodes (cum <= 0.06s)\l" tooltip="testbinary"] }
N1 [label="line1000\n1s (89.29%)" id="node1" fontsize=24 shape=box tooltip="line1000 (1s)" color="#b20500" fillcolor="#edd6d5"]
N1_0 [label = "key1:tag1\nkey2:tag1" id="N1_0" fontsize=8 shape=box3d tooltip="1s"]
N1 -> N1_0 [label=" 1s" weight=100 tooltip="1s" labeltooltip="1s"]
N2 [label="line3000\n0 of 1.12s (100%)" id="node2" fontsize=8 shape=box tooltip="line3000 (1.12s)" color="#b20000" fillcolor="#edd5d5"]
N3 [label="line3001\n0 of 1.11s (99.11%)" id="node3" fontsize=8 shape=box tooltip="line3001 (1.11s)" color="#b20000" fillcolor="#edd5d5"]
N4 [label="line1000\n0.10s (8.93%)" id="node4" fontsize=14 shape=box tooltip="line1000 (0.10s)" color="#b28b62" fillcolor="#ede8e2"]
N4_0 [label = "key1:tag2\nkey3:tag2" id="N4_0" fontsize=8 shape=box3d tooltip="0.10s"]
N4 -> N4_0 [label=" 0.10s" weight=100 tooltip="0.10s" labeltooltip="0.10s"]
N5 [label="line3002\n0.01s (0.89%)\nof 1.01s (90.18%)" id="node5" fontsize=10 shape=box tooltip="line3002 (1.01s)" color="#b20500" fillcolor="#edd6d5"]
N6 [label="line2000\n0 of 1s (89.29%)" id="node6" fontsize=8 shape=box tooltip="line2000 (1s)" color="#b20500" fillcolor="#edd6d5"]
N7 [label="line2001\n0 of 1s (89.29%)" id="node7" fontsize=8 shape=box tooltip="line2001 (1s)" color="#b20500" fillcolor="#edd6d5"]
N2 -> N3 [label=" 1.11s\n (inline)" weight=100 penwidth=5 color="#b20000" tooltip="line3000 -> line3001 (1.11s)" labeltooltip="line3000 -> line3001 (1.11s)"]
N3 -> N5 [label=" 1.01s\n (inline)" weight=91 penwidth=5 color="#b20500" tooltip="line3001 -> line3002 (1.01s)" labeltooltip="line3001 -> line3002 (1.01s)"]
N6 -> N7 [label=" 1s\n (inline)" weight=90 penwidth=5 color="#b20500" tooltip="line2000 -> line2001 (1s)" labeltooltip="line2000 -> line2001 (1s)"]
N7 -> N1 [label=" 1s" weight=90 penwidth=5 color="#b20500" tooltip="line2001 -> line1000 (1s)" labeltooltip="line2001 -> line1000 (1s)"]
N5 -> N6 [label=" 1s" weight=90 penwidth=5 color="#b20500" tooltip="line3002 -> line2000 (1s)" labeltooltip="line3002 -> line2000 (1s)"]
N3 -> N4 [label=" 0.10s" weight=9 color="#b28b62" tooltip="line3001 -> line1000 (0.10s)" labeltooltip="line3001 -> line1000 (0.10s)"]
}

View file

@ -1,20 +0,0 @@
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.12s (11.20%)\lShowing nodes accounting for 1.12s, 100% of 1.12s total\l" tooltip="testbinary"] }
N1 [label="line1000\n1.10s (98.21%)" id="node1" fontsize=24 shape=box tooltip="line1000 (1.10s)" color="#b20000" fillcolor="#edd5d5"]
N1_0 [label = "key1:tag1\nkey2:tag1" id="N1_0" fontsize=8 shape=box3d tooltip="1s"]
N1 -> N1_0 [label=" 1s" weight=100 tooltip="1s" labeltooltip="1s"]
N1_1 [label = "key1:tag2\nkey3:tag2" id="N1_1" fontsize=8 shape=box3d tooltip="0.10s"]
N1 -> N1_1 [label=" 0.10s" weight=100 tooltip="0.10s" labeltooltip="0.10s"]
N2 [label="line3000\n0 of 1.12s (100%)" id="node2" fontsize=8 shape=box tooltip="line3000 (1.12s)" color="#b20000" fillcolor="#edd5d5"]
N3 [label="line3001\n0 of 1.11s (99.11%)" id="node3" fontsize=8 shape=box tooltip="line3001 (1.11s)" color="#b20000" fillcolor="#edd5d5"]
N4 [label="line3002\n0.01s (0.89%)\nof 1.02s (91.07%)" id="node4" fontsize=10 shape=box tooltip="line3002 (1.02s)" color="#b20400" fillcolor="#edd6d5"]
N5 [label="line2001\n0.01s (0.89%)\nof 1.01s (90.18%)" id="node5" fontsize=10 shape=box tooltip="line2001 (1.01s)" color="#b20500" fillcolor="#edd6d5"]
N6 [label="line2000\n0 of 1.01s (90.18%)" id="node6" fontsize=8 shape=box tooltip="line2000 (1.01s)" color="#b20500" fillcolor="#edd6d5"]
N2 -> N3 [label=" 1.11s\n (inline)" weight=100 penwidth=5 color="#b20000" tooltip="line3000 -> line3001 (1.11s)" labeltooltip="line3000 -> line3001 (1.11s)"]
N6 -> N5 [label=" 1.01s\n (inline)" weight=91 penwidth=5 color="#b20500" tooltip="line2000 -> line2001 (1.01s)" labeltooltip="line2000 -> line2001 (1.01s)"]
N3 -> N4 [label=" 1.01s\n (inline)" weight=91 penwidth=5 color="#b20500" tooltip="line3001 -> line3002 (1.01s)" labeltooltip="line3001 -> line3002 (1.01s)"]
N4 -> N6 [label=" 1.01s" weight=91 penwidth=5 color="#b20500" tooltip="line3002 -> line2000 (1.01s)" labeltooltip="line3002 -> line2000 (1.01s)"]
N5 -> N1 [label=" 1s" weight=90 penwidth=5 color="#b20500" tooltip="line2001 -> line1000 (1s)" labeltooltip="line2001 -> line1000 (1s)"]
N3 -> N1 [label=" 0.10s" weight=9 color="#b28b62" tooltip="line3001 -> line1000 (0.10s)" labeltooltip="line3001 -> line1000 (0.10s)"]
}

View file

@ -1,5 +0,0 @@
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

View file

@ -1,8 +0,0 @@
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% line2001 (inline)
0.01s 0.89% 100% 1.02s 91.07% line3002 (inline)
0 0% 100% 1.01s 90.18% line2000
0 0% 100% 1.12s 100% line3000
0 0% 100% 1.11s 99.11% line3001 (inline)

View file

@ -1,3 +0,0 @@
Showing nodes accounting for 1s, 100% of 1s total
flat flat% sum% cum cum%
1s 100% 100% 1s 100% mangled1000 testdata/file1000.src:1

View file

@ -1,13 +0,0 @@
Showing nodes accounting for 1.12s, 100% of 1.12s total
----------------------------------------------------------+-------------
flat flat% sum% cum cum% calls calls% + context
----------------------------------------------------------+-------------
1.01s 100% | line2000 (inline)
0.01s 0.89% 0.89% 1.01s 90.18% | line2001
1s 99.01% | line1000
----------------------------------------------------------+-------------
1.11s 100% | line3000 (inline)
0 0% 0.89% 1.11s 99.11% | line3001
1.01s 90.99% | line3002 (inline)
0.10s 9.01% | line1000
----------------------------------------------------------+-------------

View file

@ -1,13 +0,0 @@
key1: Total 1.1s
1.0s (89.29%): tag1
100.0ms ( 8.93%): tag2
10.0ms ( 0.89%): tag3
10.0ms ( 0.89%): tag4
key2: Total 1.0s
1.0s (99.02%): tag1
10.0ms ( 0.98%): tag2
key3: Total 100.0ms
100.0ms ( 100%): tag2

View file

@ -1,6 +0,0 @@
key1: Total 100.0ms
100.0ms ( 100%): tag2
key3: Total 100.0ms
100.0ms ( 100%): tag2

View file

@ -1,32 +0,0 @@
File: testbinary
Type: cpu
Duration: 10s, Total samples = 1.12s (11.20%)
-----------+-------------------------------------------------------
key1: tag1
key2: tag1
1s line1000
line2001
line2000
line3002
line3001
line3000
-----------+-------------------------------------------------------
key1: tag2
key3: tag2
100ms line1000
line3001
line3000
-----------+-------------------------------------------------------
key1: tag3
key2: tag2
10ms line2001
line2000
line3002
line3000
-----------+-------------------------------------------------------
key1: tag4
key2: tag1
10ms line3002
line3001
line3000
-----------+-------------------------------------------------------

View file

@ -1,17 +0,0 @@
Showing nodes accounting for 4s, 100% of 4s total
Showing top 4 nodes out of 5
----------------------------------------------------------+-------------
flat flat% sum% cum cum% calls calls% + context
----------------------------------------------------------+-------------
1s 100% | 0000000000003000 [testbinary]
1s 25.00% 25.00% 1s 25.00% | 0000000000001000 [testbinary]
----------------------------------------------------------+-------------
1s 25.00% 50.00% 2s 50.00% | 0000000000003000 [testbinary]
1s 50.00% | 0000000000001000 [testbinary]
----------------------------------------------------------+-------------
1s 100% | 0000000000005000 [testbinary]
1s 25.00% 75.00% 1s 25.00% | 0000000000004000 [testbinary]
----------------------------------------------------------+-------------
1s 25.00% 100% 2s 50.00% | 0000000000005000 [testbinary]
1s 50.00% | 0000000000004000 [testbinary]
----------------------------------------------------------+-------------

View file

@ -1,88 +0,0 @@
positions: instr line
events: inuse_space(MB)
ob=
fl=(1) testdata/file2000.src
fn=(1) line2001
0x2000 2 62
cfl=(2) testdata/file1000.src
cfn=(2) line1000
calls=0 0x1000 1
* * 0
ob=
fl=(3) testdata/file3000.src
fn=(3) line3002
+4096 3 31
cfl=(1)
cfn=(4) line2000
calls=0 * 3
* * 0
ob=
fl=(2)
fn=(2)
-8192 1 4
ob=
fl=(1)
fn=(4)
+4096 3 0
cfl=(1)
cfn=(1)
calls=0 +4096 2
* * 63
ob=
fl=(3)
fn=(5) line3000
+4096 4 0
cfl=(3)
cfn=(6) line3001
calls=0 +4096 2
* * 32
ob=
fl=(3)
fn=(6)
* 2 0
cfl=(3)
cfn=(3)
calls=0 * 3
* * 32
ob=
fl=(3)
fn=(5)
+1 4 0
cfl=(3)
cfn=(6)
calls=0 +1 2
* * 3
ob=
fl=(3)
fn=(6)
* 2 0
cfl=(2)
cfn=(2)
calls=0 -8193 1
* * 3
ob=
fl=(3)
fn=(5)
+1 4 0
cfl=(3)
cfn=(3)
calls=0 +1 3
* * 62
ob=
fl=(3)
fn=(3)
* 3 0
cfl=(1)
cfn=(4)
calls=0 -4098 3
* * 62

View file

@ -1,2 +0,0 @@
comment
#hidden comment

View file

@ -1,21 +0,0 @@
Active filters:
focus=[24]00
Showing nodes accounting for 62.50MB, 63.37% of 98.63MB total
Dropped 2 nodes (cum <= 4.93MB)
----------------------------------------------------------+-------------
flat flat% sum% cum cum% calls calls% + context
----------------------------------------------------------+-------------
63.48MB 100% | line3002 testdata/file3000.src:3
0 0% 0% 63.48MB 64.36% | line2000 testdata/file2000.src:3
63.48MB 100% | line2001 testdata/file2000.src:2 (inline)
----------------------------------------------------------+-------------
63.48MB 100% | line2000 testdata/file2000.src:3 (inline)
62.50MB 63.37% 63.37% 63.48MB 64.36% | line2001 testdata/file2000.src:2
----------------------------------------------------------+-------------
0 0% 63.37% 63.48MB 64.36% | line3000 testdata/file3000.src:4
63.48MB 100% | line3002 testdata/file3000.src:3 (inline)
----------------------------------------------------------+-------------
63.48MB 100% | line3000 testdata/file3000.src:4 (inline)
0 0% 63.37% 63.48MB 64.36% | line3002 testdata/file3000.src:3
63.48MB 100% | line2000 testdata/file2000.src:3
----------------------------------------------------------+-------------

View file

@ -1,21 +0,0 @@
Active filters:
focus=[24]00
Showing nodes accounting for 62.50MB, 98.46% of 63.48MB total
Dropped 2 nodes (cum <= 3.17MB)
----------------------------------------------------------+-------------
flat flat% sum% cum cum% calls calls% + context
----------------------------------------------------------+-------------
63.48MB 100% | line3002
0 0% 0% 63.48MB 100% | line2000
63.48MB 100% | line2001 (inline)
----------------------------------------------------------+-------------
63.48MB 100% | line2000 (inline)
62.50MB 98.46% 98.46% 63.48MB 100% | line2001
----------------------------------------------------------+-------------
0 0% 98.46% 63.48MB 100% | line3000
63.48MB 100% | line3002 (inline)
----------------------------------------------------------+-------------
63.48MB 100% | line3000 (inline)
0 0% 98.46% 63.48MB 100% | line3002
63.48MB 100% | line2000
----------------------------------------------------------+-------------

View file

@ -1,2 +0,0 @@
Showing nodes accounting for 0, 0% of 0 total
flat flat% sum% cum cum%

View file

@ -1,5 +0,0 @@
Showing nodes accounting for 93.75MB, 95.05% of 98.63MB total
Dropped 1 node (cum <= 4.93MB)
flat flat% sum% cum cum%
62.50MB 63.37% 63.37% 63.48MB 64.36% testdata/file2000.src
31.25MB 31.68% 95.05% 98.63MB 100% testdata/file3000.src

View file

@ -1,8 +0,0 @@
Active filters:
focus=[12]00
taghide=[X3]00
Showing nodes accounting for 67.38MB, 68.32% of 98.63MB total
flat flat% sum% cum cum%
62.50MB 63.37% 63.37% 63.48MB 64.36% testdata/file2000.src
4.88MB 4.95% 68.32% 4.88MB 4.95% testdata/file1000.src
0 0% 68.32% 67.38MB 68.32% testdata/file3000.src

View file

@ -1,8 +0,0 @@
Showing nodes accounting for 150, 100% of 150 total
flat flat% sum% cum cum%
80 53.33% 53.33% 130 86.67% line3002 (inline)
40 26.67% 80.00% 50 33.33% line2001 (inline)
30 20.00% 100% 30 20.00% line1000
0 0% 100% 50 33.33% line2000
0 0% 100% 150 100% line3000
0 0% 100% 110 73.33% line3001 (inline)

View file

@ -1,13 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lActive filters:\l tagfocus=1mb:2gb\lShowing nodes accounting for 62.50MB, 63.37% of 98.63MB total\l"] }
N1 [label="line2001\n62.50MB (63.37%)" id="node1" fontsize=24 shape=box tooltip="line2001 (62.50MB)" color="#b21600" fillcolor="#edd8d5"]
NN1_0 [label = "1.56MB" id="NN1_0" fontsize=8 shape=box3d tooltip="62.50MB"]
N1 -> NN1_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
N2 [label="line3000\n0 of 62.50MB (63.37%)" id="node2" fontsize=8 shape=box tooltip="line3000 (62.50MB)" color="#b21600" fillcolor="#edd8d5"]
N3 [label="line2000\n0 of 62.50MB (63.37%)" id="node3" fontsize=8 shape=box tooltip="line2000 (62.50MB)" color="#b21600" fillcolor="#edd8d5"]
N4 [label="line3002\n0 of 62.50MB (63.37%)" id="node4" fontsize=8 shape=box tooltip="line3002 (62.50MB)" color="#b21600" fillcolor="#edd8d5"]
N3 -> N1 [label=" 62.50MB\n (inline)" weight=64 penwidth=4 color="#b21600" tooltip="line2000 -> line2001 (62.50MB)" labeltooltip="line2000 -> line2001 (62.50MB)"]
N2 -> N4 [label=" 62.50MB\n (inline)" weight=64 penwidth=4 color="#b21600" tooltip="line3000 -> line3002 (62.50MB)" labeltooltip="line3000 -> line3002 (62.50MB)"]
N4 -> N3 [label=" 62.50MB" weight=64 penwidth=4 color="#b21600" tooltip="line3002 -> line2000 (62.50MB)" labeltooltip="line3002 -> line2000 (62.50MB)"]
}

View file

@ -1,16 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lActive filters:\l tagfocus=30kb:\l tagignore=1mb:2mb\lShowing nodes accounting for 36.13MB, 36.63% of 98.63MB total\lDropped 2 nodes (cum <= 4.93MB)\l"] }
N1 [label="line3002\n31.25MB (31.68%)\nof 32.23MB (32.67%)" id="node1" fontsize=24 shape=box tooltip="line3002 (32.23MB)" color="#b23200" fillcolor="#eddcd5"]
NN1_0 [label = "400kB" id="NN1_0" fontsize=8 shape=box3d tooltip="31.25MB"]
N1 -> NN1_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]
N2 [label="line3000\n0 of 36.13MB (36.63%)" id="node2" fontsize=8 shape=box tooltip="line3000 (36.13MB)" color="#b22e00" fillcolor="#eddbd5"]
N3 [label="line3001\n0 of 36.13MB (36.63%)" id="node3" fontsize=8 shape=box tooltip="line3001 (36.13MB)" color="#b22e00" fillcolor="#eddbd5"]
N4 [label="line1000\n4.88MB (4.95%)" id="node4" fontsize=15 shape=box tooltip="line1000 (4.88MB)" color="#b2a086" fillcolor="#edeae7"]
NN4_0 [label = "200kB" id="NN4_0" fontsize=8 shape=box3d tooltip="3.91MB"]
N4 -> NN4_0 [label=" 3.91MB" weight=100 tooltip="3.91MB" labeltooltip="3.91MB"]
N2 -> N3 [label=" 36.13MB\n (inline)" weight=37 penwidth=2 color="#b22e00" tooltip="line3000 -> line3001 (36.13MB)" labeltooltip="line3000 -> line3001 (36.13MB)"]
N3 -> N1 [label=" 32.23MB\n (inline)" weight=33 penwidth=2 color="#b23200" tooltip="line3001 -> line3002 (32.23MB)" labeltooltip="line3001 -> line3002 (32.23MB)"]
N3 -> N4 [label=" 3.91MB" weight=4 color="#b2a58f" tooltip="line3001 -> line1000 (3.91MB)" labeltooltip="line3001 -> line1000 (3.91MB)"]
N1 -> N4 [label=" 0.98MB" color="#b2b0a9" tooltip="line3002 ... line1000 (0.98MB)" labeltooltip="line3002 ... line1000 (0.98MB)" style="dotted" minlen=2]
}

View file

@ -1,21 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lActive filters:\l focus=[12]00\lShowing nodes accounting for 67.38MB, 68.32% of 98.63MB total\l"] }
N1 [label="line3000\nfile3000.src:4\n0 of 67.38MB (68.32%)" id="node1" fontsize=8 shape=box tooltip="line3000 testdata/file3000.src:4 (67.38MB)" color="#b21300" fillcolor="#edd7d5"]
N2 [label="line2001\nfile2000.src:2\n62.50MB (63.37%)\nof 63.48MB (64.36%)" id="node2" fontsize=24 shape=box tooltip="line2001 testdata/file2000.src:2 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
NN2_0 [label = "1.56MB" id="NN2_0" fontsize=8 shape=box3d tooltip="62.50MB"]
N2 -> NN2_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
N3 [label="line1000\nfile1000.src:1\n4.88MB (4.95%)" id="node3" fontsize=13 shape=box tooltip="line1000 testdata/file1000.src:1 (4.88MB)" color="#b2a086" fillcolor="#edeae7"]
NN3_0 [label = "200kB" id="NN3_0" fontsize=8 shape=box3d tooltip="3.91MB"]
N3 -> NN3_0 [label=" 3.91MB" weight=100 tooltip="3.91MB" labeltooltip="3.91MB"]
N4 [label="line3002\nfile3000.src:3\n0 of 63.48MB (64.36%)" id="node4" fontsize=8 shape=box tooltip="line3002 testdata/file3000.src:3 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
N5 [label="line3001\nfile3000.src:2\n0 of 4.88MB (4.95%)" id="node5" fontsize=8 shape=box tooltip="line3001 testdata/file3000.src:2 (4.88MB)" color="#b2a086" fillcolor="#edeae7"]
N6 [label="line2000\nfile2000.src:3\n0 of 63.48MB (64.36%)" id="node6" fontsize=8 shape=box tooltip="line2000 testdata/file2000.src:3 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
N6 -> N2 [label=" 63.48MB\n (inline)" weight=65 penwidth=4 color="#b21600" tooltip="line2000 testdata/file2000.src:3 -> line2001 testdata/file2000.src:2 (63.48MB)" labeltooltip="line2000 testdata/file2000.src:3 -> line2001 testdata/file2000.src:2 (63.48MB)"]
N4 -> N6 [label=" 63.48MB" weight=65 penwidth=4 color="#b21600" tooltip="line3002 testdata/file3000.src:3 -> line2000 testdata/file2000.src:3 (63.48MB)" labeltooltip="line3002 testdata/file3000.src:3 -> line2000 testdata/file2000.src:3 (63.48MB)"]
N1 -> N4 [label=" 62.50MB\n (inline)" weight=64 penwidth=4 color="#b21600" tooltip="line3000 testdata/file3000.src:4 -> line3002 testdata/file3000.src:3 (62.50MB)" labeltooltip="line3000 testdata/file3000.src:4 -> line3002 testdata/file3000.src:3 (62.50MB)"]
N1 -> N5 [label=" 4.88MB\n (inline)" weight=5 color="#b2a086" tooltip="line3000 testdata/file3000.src:4 -> line3001 testdata/file3000.src:2 (4.88MB)" labeltooltip="line3000 testdata/file3000.src:4 -> line3001 testdata/file3000.src:2 (4.88MB)"]
N5 -> N3 [label=" 3.91MB" weight=4 color="#b2a58f" tooltip="line3001 testdata/file3000.src:2 -> line1000 testdata/file1000.src:1 (3.91MB)" labeltooltip="line3001 testdata/file3000.src:2 -> line1000 testdata/file1000.src:1 (3.91MB)"]
N2 -> N3 [label=" 0.98MB" color="#b2b0a9" tooltip="line2001 testdata/file2000.src:2 -> line1000 testdata/file1000.src:1 (0.98MB)" labeltooltip="line2001 testdata/file2000.src:2 -> line1000 testdata/file1000.src:1 (0.98MB)" minlen=2]
N5 -> N4 [label=" 0.98MB\n (inline)" color="#b2b0a9" tooltip="line3001 testdata/file3000.src:2 -> line3002 testdata/file3000.src:3 (0.98MB)" labeltooltip="line3001 testdata/file3000.src:2 -> line3002 testdata/file3000.src:3 (0.98MB)"]
}

View file

@ -1,6 +0,0 @@
bytes: Total 98.6MB
62.5MB (63.37%): 1.56MB
31.2MB (31.68%): 400kB
3.9MB ( 3.96%): 200kB
1000.0kB ( 0.99%): 100kB

View file

@ -1,6 +0,0 @@
bytes: Total 103424000.0B
65536000.0B (63.37%): 1638400B
32768000.0B (31.68%): 409600B
4096000.0B ( 3.96%): 204800B
1024000.0B ( 0.99%): 102400B

View file

@ -1,8 +0,0 @@
Showing nodes accounting for 150, 100% of 150 total
flat flat% sum% cum cum%
80 53.33% 53.33% 130 86.67% line3002 (inline)
40 26.67% 80.00% 50 33.33% line2001 (inline)
30 20.00% 100% 30 20.00% line1000
0 0% 100% 50 33.33% line2000
0 0% 100% 150 100% line3000
0 0% 100% 110 73.33% line3001 (inline)

View file

@ -1,14 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: alloc_space\lActive filters:\l tagshow=[2]00\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
N1 [label="line3002\n31.25MB (31.68%)\nof 94.73MB (96.04%)" id="node1" fontsize=20 shape=box tooltip="line3002 (94.73MB)" color="#b20200" fillcolor="#edd5d5"]
N2 [label="line3000\n0 of 98.63MB (100%)" id="node2" fontsize=8 shape=box tooltip="line3000 (98.63MB)" color="#b20000" fillcolor="#edd5d5"]
N3 [label="line2001\n62.50MB (63.37%)\nof 63.48MB (64.36%)" id="node3" fontsize=24 shape=box tooltip="line2001 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
N4 [label="line2000\n0 of 63.48MB (64.36%)" id="node4" fontsize=8 shape=box tooltip="line2000 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
N5 [label="line3001\n0 of 36.13MB (36.63%)" id="node5" fontsize=8 shape=box tooltip="line3001 (36.13MB)" color="#b22e00" fillcolor="#eddbd5"]
N4 -> N3 [label=" 63.48MB\n (inline)" weight=65 penwidth=4 color="#b21600" tooltip="line2000 -> line2001 (63.48MB)" labeltooltip="line2000 -> line2001 (63.48MB)"]
N1 -> N4 [label=" 63.48MB" weight=65 penwidth=4 color="#b21600" tooltip="line3002 -> line2000 (63.48MB)" labeltooltip="line3002 -> line2000 (63.48MB)"]
N2 -> N1 [label=" 62.50MB\n (inline)" weight=64 penwidth=4 color="#b21600" tooltip="line3000 -> line3002 (62.50MB)" labeltooltip="line3000 -> line3002 (62.50MB)"]
N2 -> N5 [label=" 36.13MB\n (inline)" weight=37 penwidth=2 color="#b22e00" tooltip="line3000 -> line3001 (36.13MB)" labeltooltip="line3000 -> line3001 (36.13MB)"]
N5 -> N1 [label=" 32.23MB\n (inline)" weight=33 penwidth=2 color="#b23200" tooltip="line3001 -> line3002 (32.23MB)" labeltooltip="line3001 -> line3002 (32.23MB)"]
}

View file

@ -1,18 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: alloc_space\lActive filters:\l focus=[234]00\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
N1 [label="line3002\n31.25MB (31.68%)\nof 94.73MB (96.04%)" id="node1" fontsize=20 shape=box tooltip="line3002 (94.73MB)" color="#b20200" fillcolor="#edd5d5"]
NN1_0 [label = "400kB" id="NN1_0" fontsize=8 shape=box3d tooltip="31.25MB"]
N1 -> NN1_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]
N2 [label="line3000\n0 of 98.63MB (100%)" id="node2" fontsize=8 shape=box tooltip="line3000 (98.63MB)" color="#b20000" fillcolor="#edd5d5"]
N3 [label="line2001\n62.50MB (63.37%)\nof 63.48MB (64.36%)" id="node3" fontsize=24 shape=box tooltip="line2001 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
NN3_0 [label = "1.56MB" id="NN3_0" fontsize=8 shape=box3d tooltip="62.50MB"]
N3 -> NN3_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
N4 [label="line2000\n0 of 63.48MB (64.36%)" id="node4" fontsize=8 shape=box tooltip="line2000 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
N5 [label="line3001\n0 of 36.13MB (36.63%)" id="node5" fontsize=8 shape=box tooltip="line3001 (36.13MB)" color="#b22e00" fillcolor="#eddbd5"]
N4 -> N3 [label=" 63.48MB\n (inline)" weight=65 penwidth=4 color="#b21600" tooltip="line2000 -> line2001 (63.48MB)" labeltooltip="line2000 -> line2001 (63.48MB)"]
N1 -> N4 [label=" 63.48MB" weight=65 penwidth=4 color="#b21600" tooltip="line3002 -> line2000 (63.48MB)" labeltooltip="line3002 -> line2000 (63.48MB)" minlen=2]
N2 -> N1 [label=" 62.50MB\n (inline)" weight=64 penwidth=4 color="#b21600" tooltip="line3000 -> line3002 (62.50MB)" labeltooltip="line3000 -> line3002 (62.50MB)"]
N2 -> N5 [label=" 36.13MB\n (inline)" weight=37 penwidth=2 color="#b22e00" tooltip="line3000 -> line3001 (36.13MB)" labeltooltip="line3000 -> line3001 (36.13MB)"]
N5 -> N1 [label=" 32.23MB\n (inline)" weight=33 penwidth=2 color="#b23200" tooltip="line3001 -> line3002 (32.23MB)" labeltooltip="line3001 -> line3002 (32.23MB)"]
}

View file

@ -1,11 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: alloc_space\lActive filters:\l hide=line.*1?23?\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
N1 [label="line3000\n62.50MB (63.37%)\nof 98.63MB (100%)" id="node1" fontsize=24 shape=box tooltip="line3000 (98.63MB)" color="#b20000" fillcolor="#edd5d5"]
NN1_0 [label = "1.56MB" id="NN1_0" fontsize=8 shape=box3d tooltip="62.50MB"]
N1 -> NN1_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
N2 [label="line3001\n31.25MB (31.68%)\nof 36.13MB (36.63%)" id="node2" fontsize=20 shape=box tooltip="line3001 (36.13MB)" color="#b22e00" fillcolor="#eddbd5"]
NN2_0 [label = "400kB" id="NN2_0" fontsize=8 shape=box3d tooltip="31.25MB"]
N2 -> NN2_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]
N1 -> N2 [label=" 36.13MB\n (inline)" weight=37 penwidth=2 color="#b22e00" tooltip="line3000 -> line3001 (36.13MB)" labeltooltip="line3000 -> line3001 (36.13MB)" minlen=2]
}

View file

@ -1,8 +0,0 @@
bytes: Total 93.8MB
62.5MB (66.67%): 1.56MB
31.2MB (33.33%): 400kB
request: Total 93.8MB
62.5MB (66.67%): 1.56MB
31.2MB (33.33%): 400kB

View file

@ -1,30 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
N1 [label="line3002\n31.25MB (31.68%)\nof 94.73MB (96.04%)" id="node1" fontsize=20 shape=box tooltip="line3002 (94.73MB)" color="#b20200" fillcolor="#edd5d5"]
NN1_0 [label = "16B..64B" id="NN1_0" fontsize=8 shape=box3d tooltip="93.75MB"]
N1 -> NN1_0 [label=" 93.75MB" weight=100 tooltip="93.75MB" labeltooltip="93.75MB"]
NN1_1 [label = "2B..8B" id="NN1_1" fontsize=8 shape=box3d tooltip="93.75MB"]
N1 -> NN1_1 [label=" 93.75MB" weight=100 tooltip="93.75MB" labeltooltip="93.75MB"]
NN1_2 [label = "256B..1.56MB" id="NN1_2" fontsize=8 shape=box3d tooltip="62.50MB"]
N1 -> NN1_2 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
NN1_3 [label = "128B" id="NN1_3" fontsize=8 shape=box3d tooltip="31.25MB"]
N1 -> NN1_3 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]
N2 [label="line3000\n0 of 98.63MB (100%)" id="node2" fontsize=8 shape=box tooltip="line3000 (98.63MB)" color="#b20000" fillcolor="#edd5d5"]
N3 [label="line2001\n62.50MB (63.37%)\nof 63.48MB (64.36%)" id="node3" fontsize=24 shape=box tooltip="line2001 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
NN3_0 [label = "16B..64B" id="NN3_0" fontsize=8 shape=box3d tooltip="190.43MB"]
N3 -> NN3_0 [label=" 190.43MB" weight=100 tooltip="190.43MB" labeltooltip="190.43MB" style="dotted"]
NN3_1 [label = "2B..8B" id="NN3_1" fontsize=8 shape=box3d tooltip="190.43MB"]
N3 -> NN3_1 [label=" 190.43MB" weight=100 tooltip="190.43MB" labeltooltip="190.43MB" style="dotted"]
NN3_2 [label = "256B..1.56MB" id="NN3_2" fontsize=8 shape=box3d tooltip="125.98MB"]
N3 -> NN3_2 [label=" 125.98MB" weight=100 tooltip="125.98MB" labeltooltip="125.98MB" style="dotted"]
NN3_3 [label = "128B" id="NN3_3" fontsize=8 shape=box3d tooltip="63.48MB"]
N3 -> NN3_3 [label=" 63.48MB" weight=100 tooltip="63.48MB" labeltooltip="63.48MB" style="dotted"]
N4 [label="line2000\n0 of 63.48MB (64.36%)" id="node4" fontsize=8 shape=box tooltip="line2000 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
N5 [label="line3001\n0 of 36.13MB (36.63%)" id="node5" fontsize=8 shape=box tooltip="line3001 (36.13MB)" color="#b22e00" fillcolor="#eddbd5"]
N4 -> N3 [label=" 63.48MB\n (inline)" weight=65 penwidth=4 color="#b21600" tooltip="line2000 -> line2001 (63.48MB)" labeltooltip="line2000 -> line2001 (63.48MB)"]
N1 -> N4 [label=" 63.48MB" weight=65 penwidth=4 color="#b21600" tooltip="line3002 -> line2000 (63.48MB)" labeltooltip="line3002 -> line2000 (63.48MB)" minlen=2]
N2 -> N1 [label=" 62.50MB\n (inline)" weight=64 penwidth=4 color="#b21600" tooltip="line3000 -> line3002 (62.50MB)" labeltooltip="line3000 -> line3002 (62.50MB)"]
N2 -> N5 [label=" 36.13MB\n (inline)" weight=37 penwidth=2 color="#b22e00" tooltip="line3000 -> line3001 (36.13MB)" labeltooltip="line3000 -> line3001 (36.13MB)"]
N5 -> N1 [label=" 32.23MB\n (inline)" weight=33 penwidth=2 color="#b23200" tooltip="line3001 -> line3002 (32.23MB)" labeltooltip="line3001 -> line3002 (32.23MB)"]
}

View file

@ -1,32 +0,0 @@
Build ID: buildid
comment
Type: inuse_space
-----------+-------------------------------------------------------
key1: tag
bytes: 100kB
request: 100kB
1000kB line1000
line2001
line2000
line3002
line3001
line3000
-----------+-------------------------------------------------------
bytes: 200kB
3.91MB line1000
line3001
line3000
-----------+-------------------------------------------------------
key1: tag
bytes: 1.56MB
request: 1.56MB
62.50MB line2001
line2000
line3002
line3000
-----------+-------------------------------------------------------
bytes: 400kB
31.25MB line3002
line3001
line3000
-----------+-------------------------------------------------------

View file

@ -1,9 +0,0 @@
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)"]
}

View file

@ -1,5 +0,0 @@
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

View file

@ -1,8 +0,0 @@
Showing nodes accounting for 1.12s, 100% of 1.12s total
Showing top 5 nodes out of 6
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% line2001 (inline)
0.01s 0.89% 100% 1.02s 91.07% line3002 (inline)
0 0% 100% 1.01s 90.18% line2000
0 0% 100% 1.12s 100% line3000

View file

@ -179,11 +179,18 @@ func defaultWebServer(args *plugin.HTTPServerArgs) error {
// https://github.com/google/pprof/pull/348
mux := http.NewServeMux()
mux.Handle("/ui/", http.StripPrefix("/ui", handler))
mux.Handle("/", http.RedirectHandler("/ui/", http.StatusTemporaryRedirect))
mux.Handle("/", redirectWithQuery("/ui"))
s := &http.Server{Handler: mux}
return s.Serve(ln)
}
func redirectWithQuery(path string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
pathWithQuery := &gourl.URL{Path: path, RawQuery: r.URL.RawQuery}
http.Redirect(w, r, pathWithQuery.String(), http.StatusTemporaryRedirect)
}
}
func isLocalhost(host string) bool {
for _, v := range []string{"localhost", "127.0.0.1", "[::1]", "::1"} {
if host == v {

View file

@ -1,285 +0,0 @@
// Copyright 2017 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 driver
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os/exec"
"regexp"
"runtime"
"sync"
"testing"
"github.com/google/pprof/internal/plugin"
"github.com/google/pprof/internal/proftest"
"github.com/google/pprof/profile"
)
func TestWebInterface(t *testing.T) {
if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
t.Skip("test assumes tcp available")
}
prof := makeFakeProfile()
// Custom http server creator
var server *httptest.Server
serverCreated := make(chan bool)
creator := func(a *plugin.HTTPServerArgs) error {
server = httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if h := a.Handlers[r.URL.Path]; h != nil {
h.ServeHTTP(w, r)
}
}))
serverCreated <- true
return nil
}
// Start server and wait for it to be initialized
go serveWebInterface("unused:1234", prof, &plugin.Options{
Obj: fakeObjTool{},
UI: &proftest.TestUI{},
HTTPServer: creator,
})
<-serverCreated
defer server.Close()
haveDot := false
if _, err := exec.LookPath("dot"); err == nil {
haveDot = true
}
type testCase struct {
path string
want []string
needDot bool
}
testcases := []testCase{
{"/", []string{"F1", "F2", "F3", "testbin", "cpu"}, true},
{"/top", []string{`"Name":"F2","InlineLabel":"","Flat":200,"Cum":300,"FlatFormat":"200ms","CumFormat":"300ms"}`}, false},
{"/source?f=" + url.QueryEscape("F[12]"),
[]string{"F1", "F2", "300ms +line1"}, false},
{"/peek?f=" + url.QueryEscape("F[12]"),
[]string{"300ms.*F1", "200ms.*300ms.*F2"}, false},
{"/disasm?f=" + url.QueryEscape("F[12]"),
[]string{"f1:asm", "f2:asm"}, false},
{"/flamegraph", []string{"File: testbin", "\"n\":\"root\"", "\"n\":\"F1\"", "var flamegraph = function", "function hierarchy"}, false},
}
for _, c := range testcases {
if c.needDot && !haveDot {
t.Log("skipping", c.path, "since dot (graphviz) does not seem to be installed")
continue
}
res, err := http.Get(server.URL + c.path)
if err != nil {
t.Error("could not fetch", c.path, err)
continue
}
data, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Error("could not read response", c.path, err)
continue
}
result := string(data)
for _, w := range c.want {
if match, _ := regexp.MatchString(w, result); !match {
t.Errorf("response for %s does not match "+
"expected pattern '%s'; "+
"actual result:\n%s", c.path, w, result)
}
}
}
// Also fetch all the test case URLs in parallel to test thread
// safety when run under the race detector.
var wg sync.WaitGroup
for _, c := range testcases {
if c.needDot && !haveDot {
continue
}
path := server.URL + c.path
for count := 0; count < 2; count++ {
wg.Add(1)
go func() {
defer wg.Done()
res, err := http.Get(path)
if err != nil {
t.Error("could not fetch", c.path, err)
return
}
if _, err = ioutil.ReadAll(res.Body); err != nil {
t.Error("could not read response", c.path, err)
}
}()
}
}
wg.Wait()
}
// Implement fake object file support.
const addrBase = 0x1000
const fakeSource = "testdata/file1000.src"
type fakeObj struct{}
func (f fakeObj) Close() error { return nil }
func (f fakeObj) Name() string { return "testbin" }
func (f fakeObj) Base() uint64 { return 0 }
func (f fakeObj) BuildID() string { return "" }
func (f fakeObj) SourceLine(addr uint64) ([]plugin.Frame, error) {
return nil, fmt.Errorf("SourceLine unimplemented")
}
func (f fakeObj) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
return []*plugin.Sym{
{
Name: []string{"F1"}, File: fakeSource,
Start: addrBase, End: addrBase + 10,
},
{
Name: []string{"F2"}, File: fakeSource,
Start: addrBase + 10, End: addrBase + 20,
},
{
Name: []string{"F3"}, File: fakeSource,
Start: addrBase + 20, End: addrBase + 30,
},
}, nil
}
type fakeObjTool struct{}
func (obj fakeObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
return fakeObj{}, nil
}
func (obj fakeObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
return []plugin.Inst{
{Addr: addrBase + 0, Text: "f1:asm", Function: "F1"},
{Addr: addrBase + 10, Text: "f2:asm", Function: "F2"},
{Addr: addrBase + 20, Text: "d3:asm", Function: "F3"},
}, nil
}
func makeFakeProfile() *profile.Profile {
// Three functions: F1, F2, F3 with three lines, 11, 22, 33.
funcs := []*profile.Function{
{ID: 1, Name: "F1", Filename: fakeSource, StartLine: 3},
{ID: 2, Name: "F2", Filename: fakeSource, StartLine: 5},
{ID: 3, Name: "F3", Filename: fakeSource, StartLine: 7},
}
lines := []profile.Line{
{Function: funcs[0], Line: 11},
{Function: funcs[1], Line: 22},
{Function: funcs[2], Line: 33},
}
mapping := []*profile.Mapping{
{
ID: 1,
Start: addrBase,
Limit: addrBase + 10,
Offset: 0,
File: "testbin",
HasFunctions: true,
HasFilenames: true,
HasLineNumbers: true,
},
}
// Three interesting addresses: base+{10,20,30}
locs := []*profile.Location{
{ID: 1, Address: addrBase + 10, Line: lines[0:1], Mapping: mapping[0]},
{ID: 2, Address: addrBase + 20, Line: lines[1:2], Mapping: mapping[0]},
{ID: 3, Address: addrBase + 30, Line: lines[2:3], Mapping: mapping[0]},
}
// Two stack traces.
return &profile.Profile{
PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
Period: 1,
DurationNanos: 10e9,
SampleType: []*profile.ValueType{
{Type: "cpu", Unit: "milliseconds"},
},
Sample: []*profile.Sample{
{
Location: []*profile.Location{locs[2], locs[1], locs[0]},
Value: []int64{100},
},
{
Location: []*profile.Location{locs[1], locs[0]},
Value: []int64{200},
},
},
Location: locs,
Function: funcs,
Mapping: mapping,
}
}
func TestGetHostAndPort(t *testing.T) {
if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
t.Skip("test assumes tcp available")
}
type testCase struct {
hostport string
wantHost string
wantPort int
wantRandomPort bool
}
testCases := []testCase{
{":", "localhost", 0, true},
{":4681", "localhost", 4681, false},
{"localhost:4681", "localhost", 4681, false},
}
for _, tc := range testCases {
host, port, err := getHostAndPort(tc.hostport)
if err != nil {
t.Errorf("could not get host and port for %q: %v", tc.hostport, err)
}
if got, want := host, tc.wantHost; got != want {
t.Errorf("for %s, got host %s, want %s", tc.hostport, got, want)
continue
}
if !tc.wantRandomPort {
if got, want := port, tc.wantPort; got != want {
t.Errorf("for %s, got port %d, want %d", tc.hostport, got, want)
continue
}
}
}
}
func TestIsLocalHost(t *testing.T) {
for _, s := range []string{"localhost:10000", "[::1]:10000", "127.0.0.1:10000"} {
host, _, err := net.SplitHostPort(s)
if err != nil {
t.Error("unexpected error when splitting", s)
continue
}
if !isLocalhost(host) {
t.Errorf("host %s from %s not considered local", host, s)
}
}
}

View file

@ -1,102 +0,0 @@
// 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 elfexec
import (
"debug/elf"
"testing"
)
func TestGetBase(t *testing.T) {
fhExec := &elf.FileHeader{
Type: elf.ET_EXEC,
}
fhRel := &elf.FileHeader{
Type: elf.ET_REL,
}
fhDyn := &elf.FileHeader{
Type: elf.ET_DYN,
}
lsOffset := &elf.ProgHeader{
Vaddr: 0x400000,
Off: 0x200000,
}
kernelHeader := &elf.ProgHeader{
Vaddr: 0xffffffff81000000,
}
kernelAslrHeader := &elf.ProgHeader{
Vaddr: 0xffffffff80200000,
Off: 0x1000,
}
ppc64KernelHeader := &elf.ProgHeader{
Vaddr: 0xc000000000000000,
}
testcases := []struct {
label string
fh *elf.FileHeader
loadSegment *elf.ProgHeader
stextOffset *uint64
start, limit, offset uint64
want uint64
wanterr bool
}{
{"exec", fhExec, nil, nil, 0x400000, 0, 0, 0, false},
{"exec offset", fhExec, lsOffset, nil, 0x400000, 0x800000, 0, 0x200000, false},
{"exec offset 2", fhExec, lsOffset, nil, 0x200000, 0x600000, 0, 0, false},
{"exec nomap", fhExec, nil, nil, 0, 0, 0, 0, false},
{"exec kernel", fhExec, kernelHeader, uint64p(0xffffffff81000198), 0xffffffff82000198, 0xffffffff83000198, 0, 0x1000000, false},
{"exec kernel", fhExec, kernelHeader, uint64p(0xffffffff810002b8), 0xffffffff81000000, 0xffffffffa0000000, 0x0, 0x0, false},
{"exec kernel ASLR", fhExec, kernelHeader, uint64p(0xffffffff810002b8), 0xffffffff81000000, 0xffffffffa0000000, 0xffffffff81000000, 0x0, false},
// TODO(aalexand): Figure out where this test case exactly comes from and
// whether it's still relevant.
{"exec kernel ASLR 2", fhExec, kernelAslrHeader, nil, 0xffffffff83e00000, 0xfffffffffc3fffff, 0x3c00000, 0x3c00000, false},
{"exec PPC64 kernel", fhExec, ppc64KernelHeader, uint64p(0xc000000000000000), 0xc000000000000000, 0xd00000001a730000, 0x0, 0x0, false},
{"exec chromeos kernel", fhExec, kernelHeader, uint64p(0xffffffff81000198), 0, 0x10197, 0, 0x7efffe68, false},
{"exec chromeos kernel 2", fhExec, kernelHeader, uint64p(0xffffffff81000198), 0, 0x10198, 0, 0x7efffe68, false},
{"exec chromeos kernel 3", fhExec, kernelHeader, uint64p(0xffffffff81000198), 0x198, 0x100000, 0, 0x7f000000, false},
{"exec chromeos kernel 4", fhExec, kernelHeader, uint64p(0xffffffff81200198), 0x198, 0x100000, 0, 0x7ee00000, false},
{"exec chromeos kernel unremapped", fhExec, kernelHeader, uint64p(0xffffffff810001c8), 0xffffffff834001c8, 0xffffffffc0000000, 0xffffffff834001c8, 0x2400000, false},
{"dyn", fhDyn, nil, nil, 0x200000, 0x300000, 0, 0x200000, false},
{"dyn map", fhDyn, lsOffset, nil, 0x0, 0x300000, 0, 0xFFFFFFFFFFE00000, false},
{"dyn nomap", fhDyn, nil, nil, 0x0, 0x0, 0, 0, false},
{"dyn map+offset", fhDyn, lsOffset, nil, 0x900000, 0xa00000, 0x200000, 0x500000, false},
{"rel", fhRel, nil, nil, 0x2000000, 0x3000000, 0, 0x2000000, false},
{"rel nomap", fhRel, nil, nil, 0x0, ^uint64(0), 0, 0, false},
{"rel offset", fhRel, nil, nil, 0x100000, 0x200000, 0x1, 0, true},
}
for _, tc := range testcases {
base, err := GetBase(tc.fh, tc.loadSegment, tc.stextOffset, tc.start, tc.limit, tc.offset)
if err != nil {
if !tc.wanterr {
t.Errorf("%s: want no error, got %v", tc.label, err)
}
continue
}
if tc.wanterr {
t.Errorf("%s: want error, got nil", tc.label)
continue
}
if base != tc.want {
t.Errorf("%s: want 0x%x, got 0x%x", tc.label, tc.want, base)
}
}
}
func uint64p(n uint64) *uint64 {
return &n
}

View file

@ -1,335 +0,0 @@
// 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 graph
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
"github.com/google/pprof/internal/proftest"
)
var updateFlag = flag.Bool("update", false, "Update the golden files")
func TestComposeWithStandardGraph(t *testing.T) {
g := baseGraph()
a, c := baseAttrsAndConfig()
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
compareGraphs(t, buf.Bytes(), "compose1.dot")
}
func TestComposeWithNodeAttributesAndZeroFlat(t *testing.T) {
g := baseGraph()
a, c := baseAttrsAndConfig()
// Set NodeAttributes for Node 1.
a.Nodes[g.Nodes[0]] = &DotNodeAttributes{
Shape: "folder",
Bold: true,
Peripheries: 2,
URL: "www.google.com",
Formatter: func(ni *NodeInfo) string {
return strings.ToUpper(ni.Name)
},
}
// Set Flat value to zero on Node 2.
g.Nodes[1].Flat = 0
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
compareGraphs(t, buf.Bytes(), "compose2.dot")
}
func TestComposeWithTagsAndResidualEdge(t *testing.T) {
g := baseGraph()
a, c := baseAttrsAndConfig()
// Add tags to Node 1.
g.Nodes[0].LabelTags["a"] = &Tag{
Name: "tag1",
Cum: 10,
Flat: 10,
}
g.Nodes[0].NumericTags[""] = TagMap{
"b": &Tag{
Name: "tag2",
Cum: 20,
Flat: 20,
Unit: "ms",
},
}
// Set edge to be Residual.
g.Nodes[0].Out[g.Nodes[1]].Residual = true
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
compareGraphs(t, buf.Bytes(), "compose3.dot")
}
func TestComposeWithNestedTags(t *testing.T) {
g := baseGraph()
a, c := baseAttrsAndConfig()
// Add tags to Node 1.
g.Nodes[0].LabelTags["tag1"] = &Tag{
Name: "tag1",
Cum: 10,
Flat: 10,
}
g.Nodes[0].NumericTags["tag1"] = TagMap{
"tag2": &Tag{
Name: "tag2",
Cum: 20,
Flat: 20,
Unit: "ms",
},
}
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
compareGraphs(t, buf.Bytes(), "compose5.dot")
}
func TestComposeWithEmptyGraph(t *testing.T) {
g := &Graph{}
a, c := baseAttrsAndConfig()
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
compareGraphs(t, buf.Bytes(), "compose4.dot")
}
func TestComposeWithStandardGraphAndURL(t *testing.T) {
g := baseGraph()
a, c := baseAttrsAndConfig()
c.LegendURL = "http://example.com"
var buf bytes.Buffer
ComposeDot(&buf, g, a, c)
compareGraphs(t, buf.Bytes(), "compose6.dot")
}
func baseGraph() *Graph {
src := &Node{
Info: NodeInfo{Name: "src"},
Flat: 10,
Cum: 25,
In: make(EdgeMap),
Out: make(EdgeMap),
LabelTags: make(TagMap),
NumericTags: make(map[string]TagMap),
}
dest := &Node{
Info: NodeInfo{Name: "dest"},
Flat: 15,
Cum: 25,
In: make(EdgeMap),
Out: make(EdgeMap),
LabelTags: make(TagMap),
NumericTags: make(map[string]TagMap),
}
edge := &Edge{
Src: src,
Dest: dest,
Weight: 10,
}
src.Out[dest] = edge
src.In[src] = edge
return &Graph{
Nodes: Nodes{
src,
dest,
},
}
}
func baseAttrsAndConfig() (*DotAttributes, *DotConfig) {
a := &DotAttributes{
Nodes: make(map[*Node]*DotNodeAttributes),
}
c := &DotConfig{
Title: "testtitle",
Labels: []string{"label1", "label2"},
Total: 100,
FormatValue: func(v int64) string {
return strconv.FormatInt(v, 10)
},
}
return a, c
}
func compareGraphs(t *testing.T, got []byte, wantFile string) {
wantFile = filepath.Join("testdata", wantFile)
want, err := ioutil.ReadFile(wantFile)
if err != nil {
t.Fatalf("error reading test file %s: %v", wantFile, err)
}
if string(got) != string(want) {
d, err := proftest.Diff(got, want)
if err != nil {
t.Fatalf("error finding diff: %v", err)
}
t.Errorf("Compose incorrectly wrote %s", string(d))
if *updateFlag {
err := ioutil.WriteFile(wantFile, got, 0644)
if err != nil {
t.Errorf("failed to update the golden file %q: %v", wantFile, err)
}
}
}
}
func TestNodeletCountCapping(t *testing.T) {
labelTags := make(TagMap)
for i := 0; i < 10; i++ {
name := fmt.Sprintf("tag-%d", i)
labelTags[name] = &Tag{
Name: name,
Flat: 10,
Cum: 10,
}
}
numTags := make(TagMap)
for i := 0; i < 10; i++ {
name := fmt.Sprintf("num-tag-%d", i)
numTags[name] = &Tag{
Name: name,
Unit: "mb",
Value: 16,
Flat: 10,
Cum: 10,
}
}
node1 := &Node{
Info: NodeInfo{Name: "node1-with-tags"},
Flat: 10,
Cum: 10,
NumericTags: map[string]TagMap{"": numTags},
LabelTags: labelTags,
}
node2 := &Node{
Info: NodeInfo{Name: "node2"},
Flat: 15,
Cum: 15,
}
node3 := &Node{
Info: NodeInfo{Name: "node3"},
Flat: 15,
Cum: 15,
}
g := &Graph{
Nodes: Nodes{
node1,
node2,
node3,
},
}
for n := 1; n <= 3; n++ {
input := maxNodelets + n
if got, want := len(g.SelectTopNodes(input, true)), n; got != want {
t.Errorf("SelectTopNodes(%d): got %d nodes, want %d", input, got, want)
}
}
}
func TestMultilinePrintableName(t *testing.T) {
ni := &NodeInfo{
Name: "test1.test2::test3",
File: "src/file.cc",
Address: 123,
Lineno: 999,
}
want := fmt.Sprintf(`%016x\ntest1\ntest2\ntest3\nfile.cc:999\n`, 123)
if got := multilinePrintableName(ni); got != want {
t.Errorf("multilinePrintableName(%#v) == %q, want %q", ni, got, want)
}
}
func TestTagCollapse(t *testing.T) {
makeTag := func(name, unit string, value, flat, cum int64) *Tag {
return &Tag{name, unit, value, flat, 0, cum, 0}
}
tagSource := []*Tag{
makeTag("12mb", "mb", 12, 100, 100),
makeTag("1kb", "kb", 1, 1, 1),
makeTag("1mb", "mb", 1, 1000, 1000),
makeTag("2048mb", "mb", 2048, 1000, 1000),
makeTag("1b", "b", 1, 100, 100),
makeTag("2b", "b", 2, 100, 100),
makeTag("7b", "b", 7, 100, 100),
}
tagWant := [][]*Tag{
{
makeTag("1B..2GB", "", 0, 2401, 2401),
},
{
makeTag("2GB", "", 0, 1000, 1000),
makeTag("1B..12MB", "", 0, 1401, 1401),
},
{
makeTag("2GB", "", 0, 1000, 1000),
makeTag("12MB", "", 0, 100, 100),
makeTag("1B..1MB", "", 0, 1301, 1301),
},
{
makeTag("2GB", "", 0, 1000, 1000),
makeTag("1MB", "", 0, 1000, 1000),
makeTag("2B..1kB", "", 0, 201, 201),
makeTag("1B", "", 0, 100, 100),
makeTag("12MB", "", 0, 100, 100),
},
}
for _, tc := range tagWant {
var got, want []*Tag
b := builder{nil, &DotAttributes{}, &DotConfig{}}
got = b.collapsedTags(tagSource, len(tc), true)
want = SortTags(tc, true)
if !reflect.DeepEqual(got, want) {
t.Errorf("collapse to %d, got:\n%v\nwant:\n%v", len(tc), tagString(got), tagString(want))
}
}
}
func tagString(t []*Tag) string {
var ret []string
for _, s := range t {
ret = append(ret, fmt.Sprintln(s))
}
return strings.Join(ret, ":")
}

View file

@ -28,7 +28,7 @@ import (
)
var (
javaRegExp = regexp.MustCompile(`^(?:[a-z]\w*\.)*([A-Z][\w\$]*\.(?:<init>|[a-z]\w*(?:\$\d+)?))(?:(?:\()|$)`)
javaRegExp = regexp.MustCompile(`^(?:[a-z]\w*\.)*([A-Z][\w\$]*\.(?:<init>|[a-z][\w\$]*(?:\$\d+)?))(?:(?:\()|$)`)
goRegExp = regexp.MustCompile(`^(?:[\w\-\.]+\/)+(.+)`)
cppRegExp = regexp.MustCompile(`^(?:(?:\(anonymous namespace\)::)(\w+$))|(?:(?:\(anonymous namespace\)::)?(?:[_a-zA-Z]\w*\::|)*(_*[A-Z]\w*::~?[_a-zA-Z]\w*)$)`)
)

View file

@ -1,471 +0,0 @@
package graph
import (
"fmt"
"testing"
"github.com/google/pprof/profile"
)
func edgeDebugString(edge *Edge) string {
debug := ""
debug += fmt.Sprintf("\t\tSrc: %p\n", edge.Src)
debug += fmt.Sprintf("\t\tDest: %p\n", edge.Dest)
debug += fmt.Sprintf("\t\tWeight: %d\n", edge.Weight)
debug += fmt.Sprintf("\t\tResidual: %t\n", edge.Residual)
debug += fmt.Sprintf("\t\tInline: %t\n", edge.Inline)
return debug
}
func edgeMapsDebugString(in, out EdgeMap) string {
debug := ""
debug += "In Edges:\n"
for parent, edge := range in {
debug += fmt.Sprintf("\tParent: %p\n", parent)
debug += edgeDebugString(edge)
}
debug += "Out Edges:\n"
for child, edge := range out {
debug += fmt.Sprintf("\tChild: %p\n", child)
debug += edgeDebugString(edge)
}
return debug
}
func graphDebugString(graph *Graph) string {
debug := ""
for i, node := range graph.Nodes {
debug += fmt.Sprintf("Node %d: %p\n", i, node)
}
for i, node := range graph.Nodes {
debug += "\n"
debug += fmt.Sprintf("=== Node %d: %p ===\n", i, node)
debug += edgeMapsDebugString(node.In, node.Out)
}
return debug
}
func expectedNodesDebugString(expected []expectedNode) string {
debug := ""
for i, node := range expected {
debug += fmt.Sprintf("Node %d: %p\n", i, node.node)
}
for i, node := range expected {
debug += "\n"
debug += fmt.Sprintf("=== Node %d: %p ===\n", i, node.node)
debug += edgeMapsDebugString(node.in, node.out)
}
return debug
}
// edgeMapsEqual checks if all the edges in this equal all the edges in that.
func edgeMapsEqual(this, that EdgeMap) bool {
if len(this) != len(that) {
return false
}
for node, thisEdge := range this {
if *thisEdge != *that[node] {
return false
}
}
return true
}
// nodesEqual checks if node is equal to expected.
func nodesEqual(node *Node, expected expectedNode) bool {
return node == expected.node && edgeMapsEqual(node.In, expected.in) &&
edgeMapsEqual(node.Out, expected.out)
}
// graphsEqual checks if graph is equivalent to the graph templated by expected.
func graphsEqual(graph *Graph, expected []expectedNode) bool {
if len(graph.Nodes) != len(expected) {
return false
}
expectedSet := make(map[*Node]expectedNode)
for i := range expected {
expectedSet[expected[i].node] = expected[i]
}
for _, node := range graph.Nodes {
expectedNode, found := expectedSet[node]
if !found || !nodesEqual(node, expectedNode) {
return false
}
}
return true
}
type expectedNode struct {
node *Node
in, out EdgeMap
}
type trimTreeTestcase struct {
initial *Graph
expected []expectedNode
keep NodePtrSet
}
// makeExpectedEdgeResidual makes the edge from parent to child residual.
func makeExpectedEdgeResidual(parent, child expectedNode) {
parent.out[child.node].Residual = true
child.in[parent.node].Residual = true
}
func makeEdgeInline(edgeMap EdgeMap, node *Node) {
edgeMap[node].Inline = true
}
func setEdgeWeight(edgeMap EdgeMap, node *Node, weight int64) {
edgeMap[node].Weight = weight
}
// createEdges creates directed edges from the parent to each of the children.
func createEdges(parent *Node, children ...*Node) {
for _, child := range children {
edge := &Edge{
Src: parent,
Dest: child,
}
parent.Out[child] = edge
child.In[parent] = edge
}
}
// createEmptyNode creates a node without any edges.
func createEmptyNode() *Node {
return &Node{
In: make(EdgeMap),
Out: make(EdgeMap),
}
}
// createExpectedNodes creates a slice of expectedNodes from nodes.
func createExpectedNodes(nodes ...*Node) ([]expectedNode, NodePtrSet) {
expected := make([]expectedNode, len(nodes))
keep := make(NodePtrSet, len(nodes))
for i, node := range nodes {
expected[i] = expectedNode{
node: node,
in: make(EdgeMap),
out: make(EdgeMap),
}
keep[node] = true
}
return expected, keep
}
// createExpectedEdges creates directed edges from the parent to each of the
// children.
func createExpectedEdges(parent expectedNode, children ...expectedNode) {
for _, child := range children {
edge := &Edge{
Src: parent.node,
Dest: child.node,
}
parent.out[child.node] = edge
child.in[parent.node] = edge
}
}
// createTestCase1 creates a test case that initially looks like:
// 0
// |(5)
// 1
// (3)/ \(4)
// 2 3.
//
// After keeping 0, 2, and 3, it expects the graph:
// 0
// (3)/ \(4)
// 2 3.
func createTestCase1() trimTreeTestcase {
// Create initial graph
graph := &Graph{make(Nodes, 4)}
nodes := graph.Nodes
for i := range nodes {
nodes[i] = createEmptyNode()
}
createEdges(nodes[0], nodes[1])
createEdges(nodes[1], nodes[2], nodes[3])
makeEdgeInline(nodes[0].Out, nodes[1])
makeEdgeInline(nodes[1].Out, nodes[2])
setEdgeWeight(nodes[0].Out, nodes[1], 5)
setEdgeWeight(nodes[1].Out, nodes[2], 3)
setEdgeWeight(nodes[1].Out, nodes[3], 4)
// Create expected graph
expected, keep := createExpectedNodes(nodes[0], nodes[2], nodes[3])
createExpectedEdges(expected[0], expected[1], expected[2])
makeEdgeInline(expected[0].out, expected[1].node)
makeExpectedEdgeResidual(expected[0], expected[1])
makeExpectedEdgeResidual(expected[0], expected[2])
setEdgeWeight(expected[0].out, expected[1].node, 3)
setEdgeWeight(expected[0].out, expected[2].node, 4)
return trimTreeTestcase{
initial: graph,
expected: expected,
keep: keep,
}
}
// createTestCase2 creates a test case that initially looks like:
// 3
// | (12)
// 1
// | (8)
// 2
// | (15)
// 0
// | (10)
// 4.
//
// After keeping 3 and 4, it expects the graph:
// 3
// | (10)
// 4.
func createTestCase2() trimTreeTestcase {
// Create initial graph
graph := &Graph{make(Nodes, 5)}
nodes := graph.Nodes
for i := range nodes {
nodes[i] = createEmptyNode()
}
createEdges(nodes[3], nodes[1])
createEdges(nodes[1], nodes[2])
createEdges(nodes[2], nodes[0])
createEdges(nodes[0], nodes[4])
setEdgeWeight(nodes[3].Out, nodes[1], 12)
setEdgeWeight(nodes[1].Out, nodes[2], 8)
setEdgeWeight(nodes[2].Out, nodes[0], 15)
setEdgeWeight(nodes[0].Out, nodes[4], 10)
// Create expected graph
expected, keep := createExpectedNodes(nodes[3], nodes[4])
createExpectedEdges(expected[0], expected[1])
makeExpectedEdgeResidual(expected[0], expected[1])
setEdgeWeight(expected[0].out, expected[1].node, 10)
return trimTreeTestcase{
initial: graph,
expected: expected,
keep: keep,
}
}
// createTestCase3 creates an initially empty graph and expects an empty graph
// after trimming.
func createTestCase3() trimTreeTestcase {
graph := &Graph{make(Nodes, 0)}
expected, keep := createExpectedNodes()
return trimTreeTestcase{
initial: graph,
expected: expected,
keep: keep,
}
}
// createTestCase4 creates a test case that initially looks like:
// 0.
//
// After keeping 0, it expects the graph:
// 0.
func createTestCase4() trimTreeTestcase {
graph := &Graph{make(Nodes, 1)}
nodes := graph.Nodes
for i := range nodes {
nodes[i] = createEmptyNode()
}
expected, keep := createExpectedNodes(nodes[0])
return trimTreeTestcase{
initial: graph,
expected: expected,
keep: keep,
}
}
func createTrimTreeTestCases() []trimTreeTestcase {
caseGenerators := []func() trimTreeTestcase{
createTestCase1,
createTestCase2,
createTestCase3,
createTestCase4,
}
cases := make([]trimTreeTestcase, len(caseGenerators))
for i, gen := range caseGenerators {
cases[i] = gen()
}
return cases
}
func TestTrimTree(t *testing.T) {
tests := createTrimTreeTestCases()
for _, test := range tests {
graph := test.initial
graph.TrimTree(test.keep)
if !graphsEqual(graph, test.expected) {
t.Fatalf("Graphs do not match.\nExpected: %s\nFound: %s\n",
expectedNodesDebugString(test.expected),
graphDebugString(graph))
}
}
}
func nodeTestProfile() *profile.Profile {
mappings := []*profile.Mapping{
{
ID: 1,
File: "symbolized_binary",
},
{
ID: 2,
File: "unsymbolized_library_1",
},
{
ID: 3,
File: "unsymbolized_library_2",
},
}
functions := []*profile.Function{
{ID: 1, Name: "symname"},
{ID: 2},
}
locations := []*profile.Location{
{
ID: 1,
Mapping: mappings[0],
Line: []profile.Line{
{Function: functions[0]},
},
},
{
ID: 2,
Mapping: mappings[1],
Line: []profile.Line{
{Function: functions[1]},
},
},
{
ID: 3,
Mapping: mappings[2],
},
}
return &profile.Profile{
PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
SampleType: []*profile.ValueType{
{Type: "type", Unit: "unit"},
},
Sample: []*profile.Sample{
{
Location: []*profile.Location{locations[0]},
Value: []int64{1},
},
{
Location: []*profile.Location{locations[1]},
Value: []int64{1},
},
{
Location: []*profile.Location{locations[2]},
Value: []int64{1},
},
},
Location: locations,
Function: functions,
Mapping: mappings,
}
}
// Check that nodes are properly created for a simple profile.
func TestCreateNodes(t *testing.T) {
testProfile := nodeTestProfile()
wantNodeSet := NodeSet{
{Name: "symname"}: true,
{Objfile: "unsymbolized_library_1"}: true,
{Objfile: "unsymbolized_library_2"}: true,
}
nodes, _ := CreateNodes(testProfile, &Options{})
if len(nodes) != len(wantNodeSet) {
t.Errorf("got %d nodes, want %d", len(nodes), len(wantNodeSet))
}
for _, node := range nodes {
if !wantNodeSet[node.Info] {
t.Errorf("unexpected node %v", node.Info)
}
}
}
func TestShortenFunctionName(t *testing.T) {
type testCase struct {
name string
want string
}
testcases := []testCase{
{
"root",
"root",
},
{
"syscall.Syscall",
"syscall.Syscall",
},
{
"net/http.(*conn).serve",
"http.(*conn).serve",
},
{
"github.com/blahBlah/foo.Foo",
"foo.Foo",
},
{
"github.com/BlahBlah/foo.Foo",
"foo.Foo",
},
{
"github.com/blah-blah/foo_bar.(*FooBar).Foo",
"foo_bar.(*FooBar).Foo",
},
{
"encoding/json.(*structEncoder).(encoding/json.encode)-fm",
"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",
},
{
"java.util.concurrent.ThreadPoolExecutor$Worker.run",
"ThreadPoolExecutor$Worker.run",
},
{
"java.bar.foo.FooBar.run(java.lang.Runnable)",
"FooBar.run",
},
{
"(anonymous namespace)::Bar::Foo",
"Bar::Foo",
},
{
"(anonymous namespace)::foo",
"foo",
},
{
"foo_bar::Foo::bar",
"Foo::bar",
},
{
"foo",
"foo",
},
}
for _, tc := range testcases {
name := ShortenFunctionName(tc.name)
if got, want := name, tc.want; got != want {
t.Errorf("ShortenFunctionName(%q) = %q, want %q", tc.name, got, want)
}
}
}

View file

@ -1,7 +0,0 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)"]
}

View file

@ -1,7 +0,0 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
N1 [label="SRC10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=24 shape=folder tooltip="src (25)" color="#b23c00" fillcolor="#edddd5" style="bold,filled" peripheries=2 URL="www.google.com" target="_blank"]
N2 [label="dest\n0 of 25 (25.00%)" id="node2" fontsize=8 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)"]
}

View file

@ -1,11 +0,0 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
N1_0 [label = "tag1" id="N1_0" fontsize=8 shape=box3d tooltip="10"]
N1 -> N1_0 [label=" 10" weight=100 tooltip="10" labeltooltip="10"]
NN1_0 [label = "tag2" id="NN1_0" fontsize=8 shape=box3d tooltip="20"]
N1 -> NN1_0 [label=" 20" weight=100 tooltip="20" labeltooltip="20"]
N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src ... dest (10)" labeltooltip="src ... dest (10)" style="dotted" minlen=2]
}

View file

@ -1,4 +0,0 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
}

View file

@ -1,11 +0,0 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" tooltip="testtitle"] }
N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
N1_0 [label = "tag1" id="N1_0" fontsize=8 shape=box3d tooltip="10"]
N1 -> N1_0 [label=" 10" weight=100 tooltip="10" labeltooltip="10"]
NN1_0_0 [label = "tag2" id="NN1_0_0" fontsize=8 shape=box3d tooltip="20"]
N1_0 -> NN1_0_0 [label=" 20" weight=100 tooltip="20" labeltooltip="20"]
N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)" minlen=2]
}

View file

@ -1,7 +0,0 @@
digraph "testtitle" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "label1" [shape=box fontsize=16 label="label1\llabel2\l" URL="http://example.com" target="_blank" tooltip="testtitle"] }
N1 [label="src\n10 (10.00%)\nof 25 (25.00%)" id="node1" fontsize=22 shape=box tooltip="src (25)" color="#b23c00" fillcolor="#edddd5"]
N2 [label="dest\n15 (15.00%)\nof 25 (25.00%)" id="node2" fontsize=24 shape=box tooltip="dest (25)" color="#b23c00" fillcolor="#edddd5"]
N1 -> N2 [label=" 10" weight=11 color="#b28559" tooltip="src -> dest (10)" labeltooltip="src -> dest (10)"]
}

View file

@ -1,47 +0,0 @@
// Copyright 2017 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 measurement
import (
"testing"
)
func TestScale(t *testing.T) {
for _, tc := range []struct {
value int64
fromUnit, toUnit string
wantValue float64
wantUnit string
}{
{1, "s", "ms", 1000, "ms"},
{1, "kb", "b", 1024, "B"},
{1, "kbyte", "b", 1024, "B"},
{1, "kilobyte", "b", 1024, "B"},
{1, "mb", "kb", 1024, "kB"},
{1, "gb", "mb", 1024, "MB"},
{1024, "gb", "tb", 1, "TB"},
{1024, "tb", "pb", 1, "PB"},
{2048, "mb", "auto", 2, "GB"},
{3.1536e7, "s", "auto", 1, "yrs"},
{-1, "s", "ms", -1000, "ms"},
{1, "foo", "count", 1, ""},
{1, "foo", "bar", 1, "bar"},
} {
if gotValue, gotUnit := Scale(tc.value, tc.fromUnit, tc.toUnit); gotValue != tc.wantValue || gotUnit != tc.wantUnit {
t.Errorf("Scale(%d, %q, %q) = (%f, %q), want (%f, %q)",
tc.value, tc.fromUnit, tc.toUnit, gotValue, gotUnit, tc.wantValue, tc.wantUnit)
}
}
}

View file

@ -1,140 +0,0 @@
// 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 proftest provides some utility routines to test other
// packages related to profiles.
package proftest
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"regexp"
"testing"
)
// Diff compares two byte arrays using the diff tool to highlight the
// differences. It is meant for testing purposes to display the
// differences between expected and actual output.
func Diff(b1, b2 []byte) (data []byte, err error) {
f1, err := ioutil.TempFile("", "proto_test")
if err != nil {
return nil, err
}
defer os.Remove(f1.Name())
defer f1.Close()
f2, err := ioutil.TempFile("", "proto_test")
if err != nil {
return nil, err
}
defer os.Remove(f2.Name())
defer f2.Close()
f1.Write(b1)
f2.Write(b2)
data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
if len(data) > 0 {
// diff exits with a non-zero status when the files don't match.
// Ignore that failure as long as we get output.
err = nil
}
if err != nil {
data = []byte(fmt.Sprintf("diff failed: %v\nb1: %q\nb2: %q\n", err, b1, b2))
err = nil
}
return
}
// EncodeJSON encodes a value into a byte array. This is intended for
// testing purposes.
func EncodeJSON(x interface{}) []byte {
data, err := json.MarshalIndent(x, "", " ")
if err != nil {
panic(err)
}
data = append(data, '\n')
return data
}
// TestUI implements the plugin.UI interface, triggering test failures
// if more than Ignore errors not matching AllowRx are printed.
// Also tracks the number of times the error matches AllowRx in
// NumAllowRxMatches.
type TestUI struct {
T *testing.T
Ignore int
AllowRx string
NumAllowRxMatches int
Input []string
index int
}
// ReadLine returns no input, as no input is expected during testing.
func (ui *TestUI) ReadLine(_ string) (string, error) {
if ui.index >= len(ui.Input) {
return "", io.EOF
}
input := ui.Input[ui.index]
ui.index++
if input == "**error**" {
return "", fmt.Errorf("Error: %s", input)
}
return input, nil
}
// Print messages are discarded by the test UI.
func (ui *TestUI) Print(args ...interface{}) {
}
// PrintErr messages may trigger an error failure. A fixed number of
// error messages are permitted when appropriate.
func (ui *TestUI) PrintErr(args ...interface{}) {
if ui.AllowRx != "" {
if matched, err := regexp.MatchString(ui.AllowRx, fmt.Sprint(args...)); matched || err != nil {
if err != nil {
ui.T.Errorf("failed to match against regex %q: %v", ui.AllowRx, err)
}
ui.NumAllowRxMatches++
return
}
}
if ui.Ignore > 0 {
ui.Ignore--
return
}
// Stringify arguments with fmt.Sprint() to match what default UI
// implementation does. Without this Error() calls fmt.Sprintln() which
// _always_ adds spaces between arguments, unlike fmt.Sprint() which only
// adds them between arguments if neither is string.
ui.T.Error("unexpected error: " + fmt.Sprint(args...))
}
// IsTerminal indicates if the UI is an interactive terminal.
func (ui *TestUI) IsTerminal() bool {
return false
}
// WantBrowser indicates whether a browser should be opened with the -http option.
func (ui *TestUI) WantBrowser() bool {
return false
}
// SetAutoComplete is not supported by the test UI.
func (ui *TestUI) SetAutoComplete(_ func(string) string) {
}

View file

@ -1,414 +0,0 @@
// 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(`.`),
TrimPath: "/some/path",
SampleValue: sampleValue1,
SampleUnit: testProfile.SampleType[1].Unit,
},
),
want: path + "source.rpt",
},
{
rpt: New(
testProfile.Copy(),
&Options{
OutputFormat: Dot,
CallTree: true,
Symbol: regexp.MustCompile(`.`),
TrimPath: "/some/path",
SampleValue: sampleValue1,
SampleUnit: testProfile.SampleType[1].Unit,
},
),
want: path + "source.dot",
},
} {
var b bytes.Buffer
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: "/some/path/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 := []struct {
wantFunction profile.Function
wantAdded bool
}{
{profile.Function{ID: 1, Name: "fun1"}, true},
{profile.Function{ID: 2, Name: "fun2", Filename: "filename"}, true},
{profile.Function{ID: 1, Name: "fun1"}, false},
{profile.Function{ID: 3, Name: "fun2", Filename: "filename2"}, true},
}
for i, tc := range nodes {
gotFunc, gotAdded := fm.findOrAdd(tc)
if got, want := gotFunc, want[i].wantFunction; *got != want {
t.Errorf("%d: got %v, want %v", i, got, want)
}
if got, want := gotAdded, want[i].wantAdded; got != want {
t.Errorf("%d: got %v, want %v", i, got, want)
}
}
}
func TestLegendActiveFilters(t *testing.T) {
activeFilterInput := []string{
"focus=123|456|789|101112|131415|161718|192021|222324|252627|282930|313233|343536|363738|acbdefghijklmnop",
"show=short filter",
}
expectedLegendActiveFilter := []string{
"Active filters:",
" focus=123|456|789|101112|131415|161718|192021|222324|252627|282930|313233|343536…",
" show=short filter",
}
legendActiveFilter := legendActiveFilters(activeFilterInput)
if len(legendActiveFilter) != len(expectedLegendActiveFilter) {
t.Errorf("wanted length %v got length %v", len(expectedLegendActiveFilter), len(legendActiveFilter))
}
for i := range legendActiveFilter {
if legendActiveFilter[i] != expectedLegendActiveFilter[i] {
t.Errorf("%d: want \"%v\", got \"%v\"", i, expectedLegendActiveFilter[i], legendActiveFilter[i])
}
}
}
func TestComputeTotal(t *testing.T) {
p1 := testProfile.Copy()
p1.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},
},
}
p2 := testProfile.Copy()
p2.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},
},
}
p3 := testProfile.Copy()
p3.Sample = []*profile.Sample{
{
Location: []*profile.Location{testL[0]},
Value: []int64{10000, 1},
},
{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{-10, 3},
Label: map[string][]string{"pprof::base": {"true"}},
},
{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{1000, -10},
},
{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{-9000, 3},
Label: map[string][]string{"pprof::base": {"true"}},
},
{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{-1, 3},
Label: map[string][]string{"pprof::base": {"true"}},
},
{
Location: []*profile.Location{testL[4], testL[2], testL[0]},
Value: []int64{100, 100},
},
{
Location: []*profile.Location{testL[2], testL[1], testL[0]},
Value: []int64{100, 3},
Label: map[string][]string{"pprof::base": {"true"}},
},
}
testcases := []struct {
desc string
prof *profile.Profile
value, meanDiv func(v []int64) int64
wantTotal int64
}{
{
desc: "no diff base, all positive values, index 1",
prof: p1,
value: func(v []int64) int64 {
return v[0]
},
wantTotal: 3,
},
{
desc: "no diff base, all positive values, index 2",
prof: p1,
value: func(v []int64) int64 {
return v[1]
},
wantTotal: 111,
},
{
desc: "no diff base, some negative values",
prof: p2,
value: func(v []int64) int64 {
return v[1]
},
wantTotal: 111,
},
{
desc: "diff base, some negative values",
prof: p3,
value: func(v []int64) int64 {
return v[0]
},
wantTotal: 9111,
},
}
for _, tc := range testcases {
t.Run(tc.desc, func(t *testing.T) {
if gotTotal := computeTotal(tc.prof, tc.value, tc.meanDiv); gotTotal != tc.wantTotal {
t.Errorf("got total %d, want %v", gotTotal, tc.wantTotal)
}
})
}
}

View file

@ -1,185 +0,0 @@
package report
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
"github.com/google/pprof/internal/binutils"
"github.com/google/pprof/profile"
)
func TestWebList(t *testing.T) {
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("weblist only tested on x86-64 linux")
}
cpu := readProfile(filepath.Join("testdata", "sample.cpu"), t)
rpt := New(cpu, &Options{
OutputFormat: WebList,
Symbol: regexp.MustCompile("busyLoop"),
SampleValue: func(v []int64) int64 { return v[1] },
SampleUnit: cpu.SampleType[1].Unit,
})
var buf bytes.Buffer
if err := Generate(&buf, rpt, &binutils.Binutils{}); err != nil {
t.Fatalf("could not generate weblist: %v", err)
}
output := buf.String()
for _, expect := range []string{"func busyLoop", "callq", "math.Abs"} {
if !strings.Contains(output, expect) {
t.Errorf("weblist output does not contain '%s':\n%s", expect, output)
}
}
}
func TestOpenSourceFile(t *testing.T) {
tempdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
const lsep = string(filepath.ListSeparator)
for _, tc := range []struct {
desc string
searchPath string
trimPath string
fs []string
path string
wantPath string // If empty, error is wanted.
}{
{
desc: "exact absolute path is found",
fs: []string{"foo/bar.cc"},
path: "$dir/foo/bar.cc",
wantPath: "$dir/foo/bar.cc",
},
{
desc: "exact relative path is found",
searchPath: "$dir",
fs: []string{"foo/bar.cc"},
path: "foo/bar.cc",
wantPath: "$dir/foo/bar.cc",
},
{
desc: "multiple search path",
searchPath: "some/path" + lsep + "$dir",
fs: []string{"foo/bar.cc"},
path: "foo/bar.cc",
wantPath: "$dir/foo/bar.cc",
},
{
desc: "relative path is found in parent dir",
searchPath: "$dir/foo/bar",
fs: []string{"bar.cc", "foo/bar/baz.cc"},
path: "bar.cc",
wantPath: "$dir/bar.cc",
},
{
desc: "trims configured prefix",
searchPath: "$dir",
trimPath: "some-path" + lsep + "/some/remote/path",
fs: []string{"my-project/foo/bar.cc"},
path: "/some/remote/path/my-project/foo/bar.cc",
wantPath: "$dir/my-project/foo/bar.cc",
},
{
desc: "trims heuristically",
searchPath: "$dir/my-project",
fs: []string{"my-project/foo/bar.cc"},
path: "/some/remote/path/my-project/foo/bar.cc",
wantPath: "$dir/my-project/foo/bar.cc",
},
{
desc: "error when not found",
path: "foo.cc",
},
} {
t.Run(tc.desc, func(t *testing.T) {
defer func() {
if err := os.RemoveAll(tempdir); err != nil {
t.Fatalf("failed to remove dir %q: %v", tempdir, err)
}
}()
for _, f := range tc.fs {
path := filepath.Join(tempdir, filepath.FromSlash(f))
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0755); err != nil {
t.Fatalf("failed to create dir %q: %v", dir, err)
}
if err := ioutil.WriteFile(path, nil, 0644); err != nil {
t.Fatalf("failed to create file %q: %v", path, err)
}
}
tc.searchPath = filepath.FromSlash(strings.Replace(tc.searchPath, "$dir", tempdir, -1))
tc.path = filepath.FromSlash(strings.Replace(tc.path, "$dir", tempdir, 1))
tc.wantPath = filepath.FromSlash(strings.Replace(tc.wantPath, "$dir", tempdir, 1))
if file, err := openSourceFile(tc.path, tc.searchPath, tc.trimPath); err != nil && tc.wantPath != "" {
t.Errorf("openSourceFile(%q, %q, %q) = err %v, want path %q", tc.path, tc.searchPath, tc.trimPath, err, tc.wantPath)
} else if err == nil {
defer file.Close()
gotPath := file.Name()
if tc.wantPath == "" {
t.Errorf("openSourceFile(%q, %q, %q) = %q, want error", tc.path, tc.searchPath, tc.trimPath, gotPath)
} else if gotPath != tc.wantPath {
t.Errorf("openSourceFile(%q, %q, %q) = %q, want path %q", tc.path, tc.searchPath, tc.trimPath, gotPath, tc.wantPath)
}
}
})
}
}
func TestIndentation(t *testing.T) {
for _, c := range []struct {
str string
wantIndent int
}{
{"", 0},
{"foobar", 0},
{" foo", 2},
{"\tfoo", 8},
{"\t foo", 9},
{" \tfoo", 8},
{" \tfoo", 8},
{" \tfoo", 16},
} {
if n := indentation(c.str); n != c.wantIndent {
t.Errorf("indentation(%v): got %d, want %d", c.str, n, c.wantIndent)
}
}
}
func readProfile(fname string, t *testing.T) *profile.Profile {
file, err := os.Open(fname)
if err != nil {
t.Fatalf("%s: could not open profile: %v", fname, err)
}
defer file.Close()
p, err := profile.Parse(file)
if err != nil {
t.Fatalf("%s: could not parse profile: %v", fname, err)
}
// Fix file names so they do not include absolute path names.
fix := func(s string) string {
const testdir = "/internal/report/"
pos := strings.Index(s, testdir)
if pos == -1 {
return s
}
return s[pos+len(testdir):]
}
for _, m := range p.Mapping {
m.File = fix(m.File)
}
for _, f := range p.Function {
f.Filename = fix(f.Filename)
}
return p
}

View file

@ -1,10 +0,0 @@
sample/ contains a sample program that can be profiled.
sample.bin is its x86-64 binary.
sample.cpu is a profile generated by sample.bin.
To update the binary and profile:
```shell
go build -o sample.bin ./sample
./sample.bin -cpuprofile sample.cpu
```

View file

@ -1,41 +0,0 @@
// sample program that is used to produce some of the files in
// pprof/internal/report/testdata.
package main
import (
"flag"
"fmt"
"log"
"math"
"os"
"runtime/pprof"
)
var cpuProfile = flag.String("cpuprofile", "", "where to write cpu profile")
func main() {
flag.Parse()
f, err := os.Create(*cpuProfile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
busyLoop()
}
func busyLoop() {
m := make(map[int]int)
for i := 0; i < 1000000; i++ {
m[i] = i + 10
}
var sum float64
for i := 0; i < 100; i++ {
for _, v := range m {
sum += math.Abs(float64(v))
}
}
fmt.Println("Sum", sum)
}

View file

@ -1,17 +0,0 @@
digraph "unnamed" {
node [style=filled fillcolor="#f8f8f8"]
subgraph cluster_L { "Duration: 10s, Total samples = 11111 " [shape=box fontsize=16 label="Duration: 10s, Total samples = 11111 \lShowing nodes accounting for 11111, 100% of 11111 total\l"] }
N1 [label="tee\nsource2:8\n10000 (90.00%)" id="node1" fontsize=24 shape=box tooltip="tee testdata/source2:8 (10000)" color="#b20500" fillcolor="#edd6d5"]
N2 [label="main\nsource1:2\n1 (0.009%)\nof 11111 (100%)" id="node2" fontsize=9 shape=box tooltip="main testdata/source1:2 (11111)" color="#b20000" fillcolor="#edd5d5"]
N3 [label="tee\nsource2:2\n1000 (9.00%)\nof 11000 (99.00%)" id="node3" fontsize=14 shape=box tooltip="tee testdata/source2:2 (11000)" color="#b20000" fillcolor="#edd5d5"]
N4 [label="tee\nsource2:8\n100 (0.9%)" id="node4" fontsize=10 shape=box tooltip="tee testdata/source2:8 (100)" color="#b2b0aa" fillcolor="#edecec"]
N5 [label="bar\nsource1:10\n10 (0.09%)" id="node5" fontsize=9 shape=box tooltip="bar testdata/source1:10 (10)" color="#b2b2b1" fillcolor="#ededed"]
N6 [label="bar\nsource1:10\n0 of 100 (0.9%)" id="node6" fontsize=8 shape=box tooltip="bar testdata/source1:10 (100)" color="#b2b0aa" fillcolor="#edecec"]
N7 [label="foo\nsource1:4\n0 of 10 (0.09%)" id="node7" fontsize=8 shape=box tooltip="foo testdata/source1:4 (10)" color="#b2b2b1" fillcolor="#ededed"]
N2 -> N3 [label=" 11000" weight=100 penwidth=5 color="#b20000" tooltip="main testdata/source1:2 -> tee testdata/source2:2 (11000)" labeltooltip="main testdata/source1:2 -> tee testdata/source2:2 (11000)"]
N3 -> N1 [label=" 10000" weight=91 penwidth=5 color="#b20500" tooltip="tee testdata/source2:2 -> tee testdata/source2:8 (10000)" labeltooltip="tee testdata/source2:2 -> tee testdata/source2:8 (10000)"]
N6 -> N4 [label=" 100" color="#b2b0aa" tooltip="bar testdata/source1:10 -> tee testdata/source2:8 (100)" labeltooltip="bar testdata/source1:10 -> tee testdata/source2:8 (100)"]
N2 -> N6 [label=" 100" color="#b2b0aa" tooltip="main testdata/source1:2 -> bar testdata/source1:10 (100)" labeltooltip="main testdata/source1:2 -> bar testdata/source1:10 (100)"]
N7 -> N5 [label=" 10" color="#b2b2b1" tooltip="foo testdata/source1:4 -> bar testdata/source1:10 (10)" labeltooltip="foo testdata/source1:4 -> bar testdata/source1:10 (10)"]
N2 -> N7 [label=" 10" color="#b2b2b1" tooltip="main testdata/source1:2 -> foo testdata/source1:4 (10)" labeltooltip="main testdata/source1:2 -> foo testdata/source1:4 (10)"]
}

View file

@ -1,49 +0,0 @@
Total: 11111
ROUTINE ======================== bar in testdata/source1
10 110 (flat, cum) 0.99% of Total
. . 5:source1 line 5;
. . 6:source1 line 6;
. . 7:source1 line 7;
. . 8:source1 line 8;
. . 9:source1 line 9;
10 110 10:source1 line 10;
. . 11:source1 line 11;
. . 12:source1 line 12;
. . 13:source1 line 13;
. . 14:source1 line 14;
. . 15:source1 line 15;
ROUTINE ======================== foo in testdata/source1
0 10 (flat, cum) 0.09% of Total
. . 1:source1 line 1;
. . 2:source1 line 2;
. . 3:source1 line 3;
. 10 4:source1 line 4;
. . 5:source1 line 5;
. . 6:source1 line 6;
. . 7:source1 line 7;
. . 8:source1 line 8;
. . 9:source1 line 9;
ROUTINE ======================== main in testdata/source1
1 11111 (flat, cum) 100% of Total
. . 1:source1 line 1;
1 11111 2:source1 line 2;
. . 3:source1 line 3;
. . 4:source1 line 4;
. . 5:source1 line 5;
. . 6:source1 line 6;
. . 7:source1 line 7;
ROUTINE ======================== tee in testdata/source2
11100 21100 (flat, cum) 189.90% of Total
. . 1:source2 line 1;
1000 11000 2:source2 line 2;
. . 3:source2 line 3;
. . 4:source2 line 4;
. . 5:source2 line 5;
. . 6:source2 line 6;
. . 7:source2 line 7;
10100 10100 8:source2 line 8;
. . 9:source2 line 9;
. . 10:source2 line 10;
. . 11:source2 line 11;
. . 12:source2 line 12;
. . 13:source2 line 13;

Some files were not shown because too many files have changed in this diff Show more