Commit 7844ef42 authored by Raul Silvera's avatar Raul Silvera Committed by Russ Cox

cmd/pprof: vendor pprof from github.com/google/pprof

Import the github.com/google/pprof and github.com/ianlancetaylor/demangle
packages, without modification.

Build the golang version of pprof from cmd/pprof/pprof.go
by importing the packages from src/cmd/vendot/github.com/google/pprof

The versions upstreamed are:

github.com/ianlancetaylor/demangle 4883227f66371e02c4948937d3e2be1664d9be38
github.com/google/pprof            7eb5ba977f28f2ad8dd5f6bb82cc9b454e123cdc

Update misc/nacl/testzip.proto for new tests.

Change-Id: I076584856491353607a3b98b67d0ca6838be50d6
Reviewed-on: https://go-review.googlesource.com/36798
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 2818cb5c
......@@ -37,6 +37,26 @@ go src=..
testdata
+
vendor
github.com
google
pprof
internal
driver
testdata
+
graph
testdata
+
report
testdata
+
profile
testdata
+
ianlancetaylor
demangle
testdata
+
golang.org
x
arch
......
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package commands defines and manages the basic pprof commands
package commands
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"strings"
"time"
"cmd/internal/browser"
"cmd/pprof/internal/plugin"
"cmd/pprof/internal/report"
"cmd/pprof/internal/svg"
"cmd/pprof/internal/tempfile"
)
// Commands describes the commands accepted by pprof.
type Commands map[string]*Command
// Command describes the actions for a pprof command. Includes a
// function for command-line completion, the report format to use
// during report generation, any postprocessing functions, and whether
// the command expects a regexp parameter (typically a function name).
type Command struct {
Complete Completer // autocomplete for interactive mode
Format int // report format to generate
PostProcess PostProcessor // postprocessing to run on report
HasParam bool // Collect a parameter from the CLI
Usage string // Help text
}
// Completer is a function for command-line autocompletion
type Completer func(prefix string) string
// PostProcessor is a function that applies post-processing to the report output
type PostProcessor func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error
// PProf returns the basic pprof report-generation commands
func PProf(c Completer, interactive **bool) Commands {
return Commands{
// Commands that require no post-processing.
"tags": {nil, report.Tags, nil, false, "Outputs all tags in the profile"},
"raw": {c, report.Raw, nil, false, "Outputs a text representation of the raw profile"},
"dot": {c, report.Dot, nil, false, "Outputs a graph in DOT format"},
"top": {c, report.Text, nil, false, "Outputs top entries in text form"},
"tree": {c, report.Tree, nil, false, "Outputs a text rendering of call graph"},
"text": {c, report.Text, nil, false, "Outputs top entries in text form"},
"disasm": {c, report.Dis, nil, true, "Output annotated assembly for functions matching regexp or address"},
"list": {c, report.List, nil, true, "Output annotated source for functions matching regexp"},
"peek": {c, report.Tree, nil, true, "Output callers/callees of functions matching regexp"},
// Save binary formats to a file
"callgrind": {c, report.Callgrind, awayFromTTY(interactive, "callgraph.out"), false, "Outputs a graph in callgrind format"},
"proto": {c, report.Proto, awayFromTTY(interactive, "pb.gz"), false, "Outputs the profile in compressed protobuf format"},
// Generate report in DOT format and postprocess with dot
"gif": {c, report.Dot, invokeDot(interactive, "gif"), false, "Outputs a graph image in GIF format"},
"pdf": {c, report.Dot, invokeDot(interactive, "pdf"), false, "Outputs a graph in PDF format"},
"png": {c, report.Dot, invokeDot(interactive, "png"), false, "Outputs a graph image in PNG format"},
"ps": {c, report.Dot, invokeDot(interactive, "ps"), false, "Outputs a graph in PS format"},
// Save SVG output into a file after including svgpan library
"svg": {c, report.Dot, saveSVGToFile(interactive), false, "Outputs a graph in SVG format"},
// Visualize postprocessed dot output
"eog": {c, report.Dot, invokeVisualizer(interactive, invokeDot(nil, "svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"},
"evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot(nil, "pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"},
"gv": {c, report.Dot, invokeVisualizer(interactive, invokeDot(nil, "ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"},
"web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(nil), "svg", browsers()), false, "Visualize graph through web browser"},
// Visualize HTML directly generated by report.
"weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY(nil, "html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"},
}
}
// browsers returns a list of commands to attempt for web visualization
// on the current platform
func browsers() []string {
var cmds []string
for _, cmd := range browser.Commands() {
cmds = append(cmds, strings.Join(cmd, " "))
}
return cmds
}
// NewCompleter creates an autocompletion function for a set of commands.
func NewCompleter(cs Commands) Completer {
return func(line string) string {
switch tokens := strings.Fields(line); len(tokens) {
case 0:
// Nothing to complete
case 1:
// Single token -- complete command name
found := ""
for c := range cs {
if strings.HasPrefix(c, tokens[0]) {
if found != "" {
return line
}
found = c
}
}
if found != "" {
return found
}
default:
// Multiple tokens -- complete using command completer
if c, ok := cs[tokens[0]]; ok {
if c.Complete != nil {
lastTokenIdx := len(tokens) - 1
lastToken := tokens[lastTokenIdx]
if strings.HasPrefix(lastToken, "-") {
lastToken = "-" + c.Complete(lastToken[1:])
} else {
lastToken = c.Complete(lastToken)
}
return strings.Join(append(tokens[:lastTokenIdx], lastToken), " ")
}
}
}
return line
}
}
// awayFromTTY saves the output in a file if it would otherwise go to
// the terminal screen. This is used to avoid dumping binary data on
// the screen.
func awayFromTTY(interactive **bool, format string) PostProcessor {
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
if output == os.Stdout && (ui.IsTerminal() || interactive != nil && **interactive) {
tempFile, err := tempfile.New("", "profile", "."+format)
if err != nil {
return err
}
ui.PrintErr("Generating report in ", tempFile.Name())
_, err = fmt.Fprint(tempFile, input)
return err
}
_, err := fmt.Fprint(output, input)
return err
}
}
func invokeDot(interactive **bool, format string) PostProcessor {
divert := awayFromTTY(interactive, format)
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
if _, err := exec.LookPath("dot"); err != nil {
ui.PrintErr("Cannot find dot, have you installed Graphviz?")
return err
}
cmd := exec.Command("dot", "-T"+format)
var buf bytes.Buffer
cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
if err := cmd.Run(); err != nil {
return err
}
return divert(&buf, output, ui)
}
}
func saveSVGToFile(interactive **bool) PostProcessor {
generateSVG := invokeDot(nil, "svg")
divert := awayFromTTY(interactive, "svg")
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
baseSVG := &bytes.Buffer{}
generateSVG(input, baseSVG, ui)
massaged := &bytes.Buffer{}
fmt.Fprint(massaged, svg.Massage(*baseSVG))
return divert(massaged, output, ui)
}
}
var vizTmpDir string
func makeVizTmpDir() error {
if vizTmpDir != "" {
return nil
}
name, err := ioutil.TempDir("", "pprof-")
if err != nil {
return err
}
tempfile.DeferDelete(name)
vizTmpDir = name
return nil
}
func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, visualizers []string) PostProcessor {
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
if err := makeVizTmpDir(); err != nil {
return err
}
tempFile, err := tempfile.New(vizTmpDir, "pprof", "."+suffix)
if err != nil {
return err
}
tempfile.DeferDelete(tempFile.Name())
if err = format(input, tempFile, ui); err != nil {
return err
}
tempFile.Close() // on windows, if the file is Open, start cannot access it.
// Try visualizers until one is successful
for _, v := range visualizers {
// Separate command and arguments for exec.Command.
args := strings.Split(v, " ")
if len(args) == 0 {
continue
}
viewer := exec.Command(args[0], append(args[1:], tempFile.Name())...)
viewer.Stderr = os.Stderr
if err = viewer.Start(); err == nil {
// The viewer might just send a message to another program
// to open the file. Give that program a little time to open the
// file before we remove it.
time.Sleep(1 * time.Second)
if !**interactive {
// In command-line mode, wait for the viewer to be closed
// before proceeding
return viewer.Wait()
}
return nil
}
}
return err
}
}
This diff is collapsed.
This diff is collapsed.
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fetch provides an extensible mechanism to fetch a profile
// from a data source.
package fetch
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
"cmd/pprof/internal/plugin"
"internal/pprof/profile"
)
// FetchProfile reads from a data source (network, file) and generates a
// profile.
func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
return Fetcher(source, timeout, plugin.StandardUI())
}
// Fetcher is the plugin.Fetcher version of FetchProfile.
func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
var f io.ReadCloser
var err error
url, err := url.Parse(source)
if err == nil && url.Host != "" {
f, err = FetchURL(source, timeout)
} else {
f, err = os.Open(source)
}
if err != nil {
return nil, err
}
defer f.Close()
return profile.Parse(f)
}
// FetchURL fetches a profile from a URL using HTTP.
func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
resp, err := httpGet(source, timeout)
if err != nil {
return nil, fmt.Errorf("http fetch: %v", err)
}
if resp.StatusCode != http.StatusOK {
defer resp.Body.Close()
return nil, statusCodeError(resp)
}
return resp.Body, nil
}
// PostURL issues a POST to a URL over HTTP.
func PostURL(source, post string) ([]byte, error) {
resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
if err != nil {
return nil, fmt.Errorf("http post %s: %v", source, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, statusCodeError(resp)
}
return ioutil.ReadAll(resp.Body)
}
func statusCodeError(resp *http.Response) error {
if resp.Header.Get("X-Go-Pprof") != "" && strings.Contains(resp.Header.Get("Content-Type"), "text/plain") {
// error is from pprof endpoint
body, err := ioutil.ReadAll(resp.Body)
if err == nil {
return fmt.Errorf("server response: %s - %s", resp.Status, body)
}
}
return fmt.Errorf("server response: %s", resp.Status)
}
// httpGet is a wrapper around http.Get; it is defined as a variable
// so it can be redefined during for testing.
var httpGet = func(source string, timeout time.Duration) (*http.Response, error) {
url, err := url.Parse(source)
if err != nil {
return nil, err
}
var tlsConfig *tls.Config
if url.Scheme == "https+insecure" {
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
url.Scheme = "https"
source = url.String()
}
client := &http.Client{
Transport: &http.Transport{
ResponseHeaderTimeout: timeout + 5*time.Second,
TLSClientConfig: tlsConfig,
},
}
return client.Get(source)
}
This diff is collapsed.
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package symbolizer provides a routine to populate a profile with
// symbol, file and line number information. It relies on the
// addr2liner and demangler packages to do the actual work.
package symbolizer
import (
"fmt"
"os"
"path/filepath"
"strings"
"cmd/pprof/internal/plugin"
"internal/pprof/profile"
)
// Symbolize adds symbol and line number information to all locations
// in a profile. mode enables some options to control
// symbolization. Currently only recognizes "force", which causes it
// to overwrite any existing data.
func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
force := false
// Disable some mechanisms based on mode string.
for _, o := range strings.Split(strings.ToLower(mode), ":") {
switch o {
case "force":
force = true
default:
}
}
if len(prof.Mapping) == 0 {
return fmt.Errorf("no known mappings")
}
mt, err := newMapping(prof, obj, ui, force)
if err != nil {
return err
}
defer mt.close()
functions := make(map[profile.Function]*profile.Function)
for _, l := range mt.prof.Location {
m := l.Mapping
segment := mt.segments[m]
if segment == nil {
// Nothing to do
continue
}
stack, err := segment.SourceLine(l.Address)
if err != nil || len(stack) == 0 {
// No answers from addr2line
continue
}
l.Line = make([]profile.Line, len(stack))
for i, frame := range stack {
if frame.Func != "" {
m.HasFunctions = true
}
if frame.File != "" {
m.HasFilenames = true
}
if frame.Line != 0 {
m.HasLineNumbers = true
}
f := &profile.Function{
Name: frame.Func,
SystemName: frame.Func,
Filename: frame.File,
}
if fp := functions[*f]; fp != nil {
f = fp
} else {
functions[*f] = f
f.ID = uint64(len(mt.prof.Function)) + 1
mt.prof.Function = append(mt.prof.Function, f)
}
l.Line[i] = profile.Line{
Function: f,
Line: int64(frame.Line),
}
}
if len(stack) > 0 {
m.HasInlineFrames = true
}
}
return nil
}
// newMapping creates a mappingTable for a profile.
func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
mt := &mappingTable{
prof: prof,
segments: make(map[*profile.Mapping]plugin.ObjFile),
}
// Identify used mappings
mappings := make(map[*profile.Mapping]bool)
for _, l := range prof.Location {
mappings[l.Mapping] = true
}
for _, m := range prof.Mapping {
if !mappings[m] {
continue
}
// Do not attempt to re-symbolize a mapping that has already been symbolized.
if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
continue
}
f, err := locateFile(obj, m.File, m.BuildID, m.Start)
if err != nil {
ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err)
// Move on to other mappings
continue
}
if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
// Build ID mismatch - ignore.
f.Close()
continue
}
mt.segments[m] = f
}
return mt, nil
}
// locateFile opens a local file for symbolization on the search path
// at $PPROF_BINARY_PATH. Looks inside these directories for files
// named $BUILDID/$BASENAME and $BASENAME (if build id is available).
func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) {
// Construct search path to examine
searchPath := os.Getenv("PPROF_BINARY_PATH")
if searchPath == "" {
// Use $HOME/pprof/binaries as default directory for local symbolization binaries
searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
}
// Collect names to search: {buildid/basename, basename}
var fileNames []string
if baseName := filepath.Base(file); buildID != "" {
fileNames = []string{filepath.Join(buildID, baseName), baseName}
} else {
fileNames = []string{baseName}
}
for _, path := range filepath.SplitList(searchPath) {
for nameIndex, name := range fileNames {
file := filepath.Join(path, name)
if f, err := obj.Open(file, start); err == nil {
fileBuildID := f.BuildID()
if buildID == "" || buildID == fileBuildID {
return f, nil
}
f.Close()
if nameIndex == 0 {
// If this is the first name, the path includes the build id. Report inconsistency.
return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID)
}
}
}
}
// Try original file name
f, err := obj.Open(file, start)
if err == nil && buildID != "" {
if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID {
// Mismatched build IDs, ignore
f.Close()
return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID)
}
}
return f, err
}
// mappingTable contains the mechanisms for symbolization of a
// profile.
type mappingTable struct {
prof *profile.Profile
segments map[*profile.Mapping]plugin.ObjFile
}
// Close releases any external processes being used for the mapping.
func (mt *mappingTable) close() {
for _, segment := range mt.segments {
segment.Close()
}
}
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package symbolz symbolizes a profile using the output from the symbolz
// service.
package symbolz
import (
"bytes"
"fmt"
"io"
"net/url"
"regexp"
"strconv"
"strings"
"internal/pprof/profile"
)
var (
symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`)
)
// Symbolize symbolizes profile p by parsing data returned by a
// symbolz handler. syms receives the symbolz query (hex addresses
// separated by '+') and returns the symbolz output in a string. It
// symbolizes all locations based on their addresses, regardless of
// mapping.
func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error {
if source = symbolz(source, p); source == "" {
// If the source is not a recognizable URL, do nothing.
return nil
}
// Construct query of addresses to symbolize.
var a []string
for _, l := range p.Location {
if l.Address != 0 && len(l.Line) == 0 {
a = append(a, fmt.Sprintf("%#x", l.Address))
}
}
if len(a) == 0 {
// No addresses to symbolize.
return nil
}
lines := make(map[uint64]profile.Line)
functions := make(map[string]*profile.Function)
if b, err := syms(source, strings.Join(a, "+")); err == nil {
buf := bytes.NewBuffer(b)
for {
l, err := buf.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
return err
}
if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
addr, err := strconv.ParseUint(symbol[1], 0, 64)
if err != nil {
return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
}
name := symbol[2]
fn := functions[name]
if fn == nil {
fn = &profile.Function{
ID: uint64(len(p.Function) + 1),
Name: name,
SystemName: name,
}
functions[name] = fn
p.Function = append(p.Function, fn)
}
lines[addr] = profile.Line{Function: fn}
}
}
}
for _, l := range p.Location {
if line, ok := lines[l.Address]; ok {
l.Line = []profile.Line{line}
if l.Mapping != nil {
l.Mapping.HasFunctions = true
}
}
}
return nil
}
// symbolz returns the corresponding symbolz source for a profile URL.
func symbolz(source string, p *profile.Profile) string {
if url, err := url.Parse(source); err == nil && url.Host != "" {
if last := strings.LastIndex(url.Path, "/"); last != -1 {
if strings.HasSuffix(url.Path[:last], "pprof") {
url.Path = url.Path[:last] + "/symbol"
} else {
url.Path = url.Path[:last] + "/symbolz"
}
return url.String()
}
}
return ""
}
......@@ -2,137 +2,138 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// pprof is a tool for visualization of profile.data. It is based on
// the upstream version at github.com/google/pprof, with minor
// modifications specific to the Go distribution. Please consider
// upstreaming any modifications to these packages.
package main
import (
"crypto/tls"
"debug/dwarf"
"flag"
"fmt"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"strconv"
"sync"
"time"
"cmd/internal/objfile"
"cmd/pprof/internal/commands"
"cmd/pprof/internal/driver"
"cmd/pprof/internal/fetch"
"cmd/pprof/internal/plugin"
"cmd/pprof/internal/symbolizer"
"cmd/pprof/internal/symbolz"
"internal/pprof/profile"
"github.com/google/pprof/driver"
"github.com/google/pprof/profile"
)
func main() {
var extraCommands map[string]*commands.Command // no added Go-specific commands
if err := driver.PProf(flags{}, fetch.Fetcher, symbolize, new(objTool), plugin.StandardUI(), extraCommands); err != nil {
options := &driver.Options{
Fetch: new(fetcher),
Obj: new(objTool),
}
if err := driver.PProf(options); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(2)
}
}
// symbolize attempts to symbolize profile p.
// If the source is a local binary, it tries using symbolizer and obj.
// If the source is a URL, it fetches symbol information using symbolz.
func symbolize(mode, source string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
remote, local := true, true
for _, o := range strings.Split(strings.ToLower(mode), ":") {
switch o {
case "none", "no":
return nil
case "local":
remote, local = false, true
case "remote":
remote, local = true, false
default:
ui.PrintErr("ignoring unrecognized symbolization option: " + mode)
ui.PrintErr("expecting -symbolize=[local|remote|none][:force]")
fallthrough
case "", "force":
// -force is recognized by symbolizer.Symbolize.
// If the source is remote, and the mapping file
// does not exist, don't use local symbolization.
if isRemote(source) {
if len(p.Mapping) == 0 {
local = false
} else if _, err := os.Stat(p.Mapping[0].File); err != nil {
local = false
}
}
}
}
type fetcher struct {
}
var err error
if local {
// Symbolize using binutils.
if err = symbolizer.Symbolize(mode, p, obj, ui); err == nil {
return nil
}
func (f *fetcher) Fetch(src string, duration, timeout time.Duration) (*profile.Profile, string, error) {
sourceURL, timeout := adjustURL(src, duration, timeout)
if sourceURL == "" {
// Could not recognize URL, let regular pprof attempt to fetch the profile (eg. from a file)
return nil, "", nil
}
if remote {
err = symbolz.Symbolize(source, fetch.PostURL, p)
fmt.Fprintln(os.Stderr, "Fetching profile over HTTP from", sourceURL)
if duration > 0 {
fmt.Fprintf(os.Stderr, "Please wait... (%v)\n", duration)
}
return err
p, err := getProfile(sourceURL, timeout)
return p, sourceURL, err
}
// isRemote returns whether source is a URL for a remote source.
func isRemote(source string) bool {
func getProfile(source string, timeout time.Duration) (*profile.Profile, error) {
url, err := url.Parse(source)
if err != nil {
url, err = url.Parse("http://" + source)
if err != nil {
return false
}
}
if scheme := strings.ToLower(url.Scheme); scheme == "" || scheme == "file" {
return false
return nil, err
}
return true
}
// flags implements the driver.FlagPackage interface using the builtin flag package.
type flags struct {
}
func (flags) Bool(o string, d bool, c string) *bool {
return flag.Bool(o, d, c)
}
var tlsConfig *tls.Config
if url.Scheme == "https+insecure" {
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
url.Scheme = "https"
source = url.String()
}
func (flags) Int(o string, d int, c string) *int {
return flag.Int(o, d, c)
client := &http.Client{
Transport: &http.Transport{
ResponseHeaderTimeout: timeout + 5*time.Second,
TLSClientConfig: tlsConfig,
},
}
resp, err := client.Get(source)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server response: %s", resp.Status)
}
return profile.Parse(resp.Body)
}
func (flags) Float64(o string, d float64, c string) *float64 {
return flag.Float64(o, d, c)
}
// cpuProfileHandler is the Go pprof CPU profile handler URL.
const cpuProfileHandler = "/debug/pprof/profile"
func (flags) String(o, d, c string) *string {
return flag.String(o, d, c)
}
// adjustURL applies the duration/timeout values and Go specific defaults
func adjustURL(source string, duration, timeout time.Duration) (string, time.Duration) {
u, err := url.Parse(source)
if err != nil || (u.Host == "" && u.Scheme != "" && u.Scheme != "file") {
// Try adding http:// to catch sources of the form hostname:port/path.
// url.Parse treats "hostname" as the scheme.
u, err = url.Parse("http://" + source)
}
if err != nil || u.Host == "" {
return "", 0
}
func (flags) Parse(usage func()) []string {
flag.Usage = usage
flag.Parse()
args := flag.Args()
if len(args) == 0 {
usage()
if u.Path == "" || u.Path == "/" {
u.Path = cpuProfileHandler
}
return args
}
func (flags) ExtraUsage() string {
return ""
// Apply duration/timeout overrides to URL.
values := u.Query()
if duration > 0 {
values.Set("seconds", fmt.Sprint(int(duration.Seconds())))
} else {
if urlSeconds := values.Get("seconds"); urlSeconds != "" {
if us, err := strconv.ParseInt(urlSeconds, 10, 32); err == nil {
duration = time.Duration(us) * time.Second
}
}
}
if timeout <= 0 {
if duration > 0 {
timeout = duration + duration/2
} else {
timeout = 60 * time.Second
}
}
u.RawQuery = values.Encode()
return u.String(), timeout
}
// objTool implements plugin.ObjTool using Go libraries
// objTool implements driver.ObjTool using Go libraries
// (instead of invoking GNU binutils).
type objTool struct {
mu sync.Mutex
disasmCache map[string]*objfile.Disasm
}
func (*objTool) Open(name string, start uint64) (plugin.ObjFile, error) {
func (*objTool) Open(name string, start, limit, offset uint64) (driver.ObjFile, error) {
of, err := objfile.Open(name)
if err != nil {
return nil, err
......@@ -154,14 +155,14 @@ func (*objTool) Demangle(names []string) (map[string]string, error) {
return make(map[string]string), nil
}
func (t *objTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
func (t *objTool) Disasm(file string, start, end uint64) ([]driver.Inst, error) {
d, err := t.cachedDisasm(file)
if err != nil {
return nil, err
}
var asm []plugin.Inst
var asm []driver.Inst
d.Decode(start, end, nil, func(pc, size uint64, file string, line int, text string) {
asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text})
asm = append(asm, driver.Inst{Addr: pc, File: file, Line: line, Text: text})
})
return asm, nil
}
......@@ -194,7 +195,7 @@ func (*objTool) SetConfig(config string) {
// Ignore entirely.
}
// file implements plugin.ObjFile using Go libraries
// file implements driver.ObjFile using Go libraries
// (instead of invoking GNU binutils).
// A file represents a single executable being analyzed.
type file struct {
......@@ -222,7 +223,7 @@ func (f *file) BuildID() string {
return ""
}
func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
func (f *file) SourceLine(addr uint64) ([]driver.Frame, error) {
if f.pcln == nil {
pcln, err := f.file.PCLineTable()
if err != nil {
......@@ -233,7 +234,7 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
addr -= f.offset
file, line, fn := f.pcln.PCToLine(addr)
if fn != nil {
frame := []plugin.Frame{
frame := []driver.Frame{
{
Func: fn.Name,
File: file,
......@@ -254,7 +255,7 @@ func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
// dwarfSourceLine tries to get file/line information using DWARF.
// This is for C functions that appear in the profile.
// Returns nil if there is no information available.
func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame {
func (f *file) dwarfSourceLine(addr uint64) []driver.Frame {
if f.dwarf == nil && !f.triedDwarf {
// Ignore any error--we don't care exactly why there
// is no DWARF info.
......@@ -277,7 +278,7 @@ func (f *file) dwarfSourceLine(addr uint64) []plugin.Frame {
// dwarfSourceLineEntry tries to get file/line information from a
// DWARF compilation unit. Returns nil if it doesn't find anything.
func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []plugin.Frame {
func (f *file) dwarfSourceLineEntry(r *dwarf.Reader, entry *dwarf.Entry, addr uint64) []driver.Frame {
lines, err := f.dwarf.LineReader(entry)
if err != nil {
return nil
......@@ -311,7 +312,7 @@ FindName:
// TODO: Report inlined functions.
frames := []plugin.Frame{
frames := []driver.Frame{
{
Func: name,
File: lentry.File.Name,
......@@ -322,7 +323,7 @@ FindName:
return frames
}
func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*driver.Sym, error) {
if f.sym == nil {
sym, err := f.file.Symbols()
if err != nil {
......@@ -330,7 +331,7 @@ func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
}
f.sym = sym
}
var out []*plugin.Sym
var out []*driver.Sym
for _, s := range f.sym {
// Ignore a symbol with address 0 and size 0.
// An ELF STT_FILE symbol will look like that.
......@@ -338,7 +339,7 @@ func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
continue
}
if (r == nil || r.MatchString(s.Name)) && (addr == 0 || s.Addr <= addr && addr < s.Addr+uint64(s.Size)) {
out = append(out, &plugin.Sym{
out = append(out, &driver.Sym{
Name: []string{s.Name},
File: f.name,
Start: s.Addr,
......
# 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.
\ No newline at end of file
Want to contribute? Great! First, read this page (including the small print at the end).
### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement]
(https://cla.developers.google.com/about/google-individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
approved it, but you must do it before we can put your code into our codebase.
Before you start working on a larger contribution, you should get in touch with
us first through the issue tracker with your idea so that we can help out and
possibly guide you. Coordinating up front makes it much easier to avoid
frustration later on.
### Code reviews
All submissions, including submissions by project members, require review. We
use Github pull requests for this purpose.
### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the
[Software Grant and Corporate Contributor License Agreement]
(https://cla.developers.google.com/about/google-corporate).
# 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>
This diff is collapsed.
# Introduction
pprof is a tool for visualization and analysis of profiling data.
pprof reads a collection of profiling samples in profile.proto format and
generates reports to visualize and help analyze the data. It can generate both
text and graphical reports (through the use of the dot visualization package).
profile.proto is a protocol buffer that describes a set of callstacks
and symbolization information. A common usage is to represent a set of
sampled callstacks from statistical profiling. The format is
described on the [proto/profile.proto](./proto/profile.proto) file. For details on protocol
buffers, see https://developers.google.com/protocol-buffers
Profiles can be read from a local file, or over http. Multiple
profiles of the same type can be aggregated or compared.
If the profile samples contain machine addresses, pprof can symbolize
them through the use of the native binutils tools (addr2line and nm).
**This is not an official Google product.**
# Building pprof
Prerequisites:
- Go development kit. Known to work with Go 1.5.
Follow [these instructions](http://golang.org/doc/code.html) to install the
go tool and set up GOPATH.
- Graphviz: http://www.graphviz.org/
Optional, used to generate graphic visualizations of profiles
To build and install it, use the `go get` tool.
go get github.com/google/pprof
# Basic usage
pprof can read a profile from a file or directly from a server via http.
Specify the profile input(s) in the command line, and use options to
indicate how to format the report.
## Generate a text report of the profile, sorted by hotness:
```
% pprof -top [main_binary] profile.pb.gz
Where
main_binary: Local path to the main program binary, to enable symbolization
profile.pb.gz: Local path to the profile in a compressed protobuf, or
URL to the http service that serves a profile.
```
## Generate a graph in an SVG file, and open it with a web browser:
```
pprof -web [main_binary] profile.pb.gz
```
## Run pprof on interactive mode:
If no output formatting option is specified, pprof runs on interactive mode,
where reads the profile and accepts interactive commands for visualization and
refinement of the profile.
```
pprof [main_binary] profile.pb.gz
This will open a simple shell that takes pprof commands to generate reports.
Type 'help' for available commands/options.
```
## Using pprof with Linux Perf
pprof can read `perf.data` files generated by the
[Linux perf](https://perf.wiki.kernel.org/index.php) tool by using the
`perf_to_profile` program from the
[perf_data_converter](http://github.com/google/perf_data_converter) package.
## Further documentation
See [doc/pprof.md](doc/pprof.md) for more detailed end-user documentation.
See [doc/developer/pprof.dev.md](doc/developer/pprof.dev.md) for developer documentation.
See [doc/developer/profile.proto.md](doc/developer/profile.proto.md) for a description of the profile.proto format.
This is pprof's developer documentation. It discusses how to maintain and extend
pprof. It has yet to be written.
# How is pprof code structured?
Internal vs external packages.
# External interface
## Plugins
## Legacy formats
# Overview of internal packages
This is a description of the profile.proto format.
# Overview
Profile.proto is a data representation for profile data. It is independent of
the type of data being collected and the sampling process used to collect that
data. On disk, it is represented as a gzip-compressed protocol buffer, described
at src/proto/profile.proto
A profile in this context refers to a collection of samples, each one
representing measurements performed at a certain point in the life of a job. A
sample associates a set of measurement values with a list of locations, commonly
representing the program call stack when the sample was taken.
Tools such as pprof analyze these samples and display this information in
multiple forms, such as identifying hottest locations, building graphical call
graphs or trees, etc.
# General structure of a profile
A profile is represented on a Profile message, which contain the following
fields:
* *sample*: A profile sample, with the values measured and the associated call
stack as a list of location ids. Samples with identical call stacks can be
merged by adding their respective values, element by element.
* *location*: A unique place in the program, commonly mapped to a single
instruction address. It has a unique nonzero id, to be referenced from the
samples. It contains source information in the form of lines, and a mapping id
that points to a binary.
* *function*: A program function as defined in the program source. It has a
unique nonzero id, referenced from the location lines. It contains a
human-readable name for the function (eg a C++ demangled name), a system name
(eg a C++ mangled name), the name of the corresponding source file, and other
function attributes.
* *mapping*: A binary that is part of the program during the profile
collection. It has a unique nonzero id, referenced from the locations. It
includes details on how the binary was mapped during program execution. By
convention the main program binary is the first mapping, followed by any
shared libraries.
* *string_table*: All strings in the profile are represented as indices into
this repeating field. The first string is empty, so index == 0 always
represents the empty string.
# Measurement values
Measurement values are represented as 64-bit integers. The profile contains an
explicit description of each value represented, using a ValueType message, with
two fields:
* *Type*: A human-readable description of the type semantics. For example “cpu”
to represent CPU time, “wall” or “time” for wallclock time, or “memory” for
bytes allocated.
* *Unit*: A human-readable name of the unit represented by the 64-bit integer
values. For example, it could be “nanoseconds” or “milliseconds” for a time
value, or “bytes” or “megabytes” for a memory size. If this is just
representing a number of events, the recommended unit name is “count”.
A profile can represent multiple measurements per sample, but all samples must
have the same number and type of measurements. The actual values are stored in
the Sample.value fields, each one described by the corresponding
Profile.sample_type field.
Some profiles have a uniform period that describe the granularity of the data
collection. For example, a CPU profile may have a period of 100ms, or a memory
allocation profile may have a period of 512kb. Profiles can optionally describe
such a value on the Profile.period and Profile.period_type fields. The profile
period is meant for human consumption and does not affect the interpretation of
the profiling data.
By convention, the first value on all profiles is the number of samples
collected at this call stack, with unit “count”. Because the profile does not
describe the sampling process beyond the optional period, it must include
unsampled values for all measurements. For example, a CPU profile could have
value[0] == samples, and value[1] == time in milliseconds.
## Locations, functions and mappings
Each sample lists the id of each location where the sample was collected, in
bottom-up order. Each location has an explicit unique nonzero integer id,
independent of its position in the profile, and holds additional information to
identify the corresponding source.
The profile source is expected to perform any adjustment required to the
locations in order to point to the calls in the stack. For example, if the
profile source extracts the call stack by walking back over the program stack,
it must adjust the instruction addresses to point to the actual call
instruction, instead of the instruction that each call will return to.
Sources usually generate profiles that fall into these two categories:
* *Unsymbolized profiles*: These only contain instruction addresses, and are to
be symbolized by a separate tool. It is critical for each location to point to
a valid mapping, which will provide the information required for
symbolization. These are used for profiles of compiled languages, such as C++
and Go.
* *Symbolized profiles*: These contain all the symbol information available for
the profile. Mappings and instruction addresses are optional for symbolized
locations. These are used for profiles of interpreted or jitted languages,
such as Java or Python. Also, the profile format allows the generation of
mixed profiles, with symbolized and unsymbolized locations.
The symbol information is represented in the repeating lines field of the
Location message. A location has multiple lines if it reflects multiple program
sources, for example if representing inlined call stacks. Lines reference
functions by their unique nonzero id, and the source line number within the
source file listed by the function. A function contains the source attributes
for a function, including its name, source file, etc. Functions include both a
user and a system form of the name, for example to include C++ demangled and
mangled names. For profiles where only a single name exists, both should be set
to the same string.
Mappings are also referenced from locations by their unique nonzero id, and
include all information needed to symbolize addresses within the mapping. It
includes similar information to the Linux /proc/self/maps file. Locations
associated to a mapping should have addresses that land between the mapping
start and limit. Also, if available, mappings should include a build id to
uniquely identify the version of the binary being used.
## Labels
Samples optionally contain labels, which are annotations to discriminate samples
with identical locations. For example, a label can be used on a malloc profile
to indicate allocation size, so two samples on the same call stack with sizes
2MB and 4MB do not get merged into a single sample with two allocations and a
size of 6MB.
Labels can be string-based or numeric. They are represented by the Label
message, with a key identifying the label and either a string or numeric
value. For numeric labels, by convention the key represents the measurement unit
of the numeric value. So for the previous example, the samples would have labels
{“bytes”, 2097152} and {“bytes”, 4194304}.
## Keep and drop expressions
Some profile sources may have knowledge of locations that are uninteresting or
irrelevant. However, if symbolization is needed in order to identify these
locations, the profile source may not be able to remove them when the profile is
generated. The profile format provides a mechanism to identify these frames by
name, through regular expressions.
These expressions must match the function name in its entirety. Frames that
match Profile.drop\_frames will be dropped from the profile, along with any
frames below it. Frames that match Profile.keep\_frames will be kept, even if
they match drop\_frames.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
line1
line2
line3
line4
line5
line6
line7
line8
line9
line0
line1
line2
line3
line4
line5
line1
line2
line3
line4
line5
line6
line7
line8
line9
line0
line1
line2
line3
line4
line5
Showing nodes accounting for 1s, 100% of 1s total
flat flat% sum% cum cum%
1s 100% 100% 1s 100% mangled1000 testdata/file1000.src:1
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment