mirror of https://github.com/golang/go.git
cmd: update github.com/google/pprof dependencies
Spun out of CL 626397, this change vendors in the latest github.com/google/pprof and that also required updating golang.org/x/sys to v0.27. Change-Id: I72ee514494a9e7c36a8943d78f15bdd0445c5cd5 Reviewed-on: https://go-review.googlesource.com/c/go/+/626995 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com> Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
fb9b946adc
commit
c96939fbed
|
|
@ -3,19 +3,19 @@ module cmd
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142
|
||||||
golang.org/x/arch v0.11.1-0.20241106162200-f977c2e4e3f4
|
golang.org/x/arch v0.11.1-0.20241106162200-f977c2e4e3f4
|
||||||
golang.org/x/build v0.0.0-20240722200705-b9910f320300
|
golang.org/x/build v0.0.0-20240722200705-b9910f320300
|
||||||
golang.org/x/mod v0.20.0
|
golang.org/x/mod v0.20.0
|
||||||
golang.org/x/sync v0.8.0
|
golang.org/x/sync v0.8.0
|
||||||
golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443
|
golang.org/x/sys v0.27.0
|
||||||
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97
|
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97
|
||||||
golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292
|
golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292
|
||||||
golang.org/x/tools v0.24.1-0.20240904143311-70f56264139c
|
golang.org/x/tools v0.24.1-0.20240904143311-70f56264139c
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 // indirect
|
github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef // indirect
|
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8 h1:ssNFCCVmib/GQSzx3uCWyfMgOamLGWuGqlMS77Y1m3Y=
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
|
||||||
github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE=
|
github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd h1:EVX1s+XNss9jkRW9K6XGJn2jL2lB1h5H804oKPsxOec=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||||
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
||||||
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/arch v0.11.1-0.20241106162200-f977c2e4e3f4 h1:B9d6SEXeIaY1QC4c7Gsf88ratIIcxShKAlz60Urrqzw=
|
golang.org/x/arch v0.11.1-0.20241106162200-f977c2e4e3f4 h1:B9d6SEXeIaY1QC4c7Gsf88ratIIcxShKAlz60Urrqzw=
|
||||||
|
|
@ -14,8 +14,8 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443 h1:FPr/RpnBOqfdMWoEYvMQp58uqHLtb3rUd6mnuuowZEE=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97 h1:5xPN7d0u5VdgF2gFFXUDaeD3NP1pPgFMHocnCQGAh5M=
|
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97 h1:5xPN7d0u5VdgF2gFFXUDaeD3NP1pPgFMHocnCQGAh5M=
|
||||||
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97/go.mod h1:m7R/r+o5h7UvF2JD9n2iLSGY4v8v+zNSyTJ6xynLrqs=
|
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97/go.mod h1:m7R/r+o5h7UvF2JD9n2iLSGY4v8v+zNSyTJ6xynLrqs=
|
||||||
golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292 h1:BOrQi08eIX3cDgGcMgFONf27MxXigcYa9x+iW5JuCXw=
|
golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292 h1:BOrQi08eIX3cDgGcMgFONf27MxXigcYa9x+iW5JuCXw=
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@ type Frame struct {
|
||||||
File string // source file name
|
File string // source file name
|
||||||
Line int // line in file
|
Line int // line in file
|
||||||
Column int // column in file
|
Column int // column in file
|
||||||
|
StartLine int // start line of function (if available)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Sym describes a single symbol in an object file.
|
// A Sym describes a single symbol in an object file.
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ package binutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -37,6 +38,7 @@ type llvmSymbolizer struct {
|
||||||
filename string
|
filename string
|
||||||
rw lineReaderWriter
|
rw lineReaderWriter
|
||||||
base uint64
|
base uint64
|
||||||
|
isData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type llvmSymbolizerJob struct {
|
type llvmSymbolizerJob struct {
|
||||||
|
|
@ -76,7 +78,7 @@ func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llvmSymboli
|
||||||
}
|
}
|
||||||
|
|
||||||
j := &llvmSymbolizerJob{
|
j := &llvmSymbolizerJob{
|
||||||
cmd: exec.Command(cmd, "--inlining", "-demangle=false"),
|
cmd: exec.Command(cmd, "--inlining", "-demangle=false", "--output-style=JSON"),
|
||||||
symType: "CODE",
|
symType: "CODE",
|
||||||
}
|
}
|
||||||
if isData {
|
if isData {
|
||||||
|
|
@ -102,63 +104,68 @@ func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llvmSymboli
|
||||||
filename: file,
|
filename: file,
|
||||||
rw: j,
|
rw: j,
|
||||||
base: base,
|
base: base,
|
||||||
|
isData: isData,
|
||||||
}
|
}
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readFrame parses the llvm-symbolizer output for a single address. It
|
// readDataFrames parses the llvm-symbolizer DATA output for a single address. It
|
||||||
// returns a populated plugin.Frame and whether it has reached the end of the
|
// returns a populated plugin.Frame array with a single entry.
|
||||||
// data.
|
func (d *llvmSymbolizer) readDataFrames() ([]plugin.Frame, error) {
|
||||||
func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
|
line, err := d.rw.readLine()
|
||||||
funcname, err := d.rw.readLine()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugin.Frame{}, true
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var frame struct {
|
||||||
switch funcname {
|
Address string `json:"Address"`
|
||||||
case "":
|
ModuleName string `json:"ModuleName"`
|
||||||
return plugin.Frame{}, true
|
Data struct {
|
||||||
case "??":
|
Start string `json:"Start"`
|
||||||
funcname = ""
|
Size string `json:"Size"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
} `json:"Data"`
|
||||||
}
|
}
|
||||||
|
if err := json.Unmarshal([]byte(line), &frame); err != nil {
|
||||||
fileline, err := d.rw.readLine()
|
return nil, err
|
||||||
|
}
|
||||||
|
// Match non-JSON output behaviour of stuffing the start/size into the filename of a single frame,
|
||||||
|
// with the size being a decimal value.
|
||||||
|
size, err := strconv.ParseInt(frame.Data.Size, 0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return plugin.Frame{Func: funcname}, true
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var stack []plugin.Frame
|
||||||
|
stack = append(stack, plugin.Frame{Func: frame.Data.Name, File: fmt.Sprintf("%s %d", frame.Data.Start, size)})
|
||||||
|
return stack, nil
|
||||||
|
}
|
||||||
|
|
||||||
linenumber := 0
|
// readCodeFrames parses the llvm-symbolizer CODE output for a single address. It
|
||||||
columnnumber := 0
|
// returns a populated plugin.Frame array.
|
||||||
// The llvm-symbolizer outputs the <file_name>:<line_number>:<column_number>.
|
func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) {
|
||||||
// When it cannot identify the source code location, it outputs "??:0:0".
|
line, err := d.rw.readLine()
|
||||||
// Older versions output just the filename and line number, so we check for
|
if err != nil {
|
||||||
// both conditions here.
|
return nil, err
|
||||||
if fileline == "??:0" || fileline == "??:0:0" {
|
|
||||||
fileline = ""
|
|
||||||
} else {
|
|
||||||
switch split := strings.Split(fileline, ":"); len(split) {
|
|
||||||
case 3:
|
|
||||||
// filename:line:column
|
|
||||||
if col, err := strconv.Atoi(split[2]); err == nil {
|
|
||||||
columnnumber = col
|
|
||||||
}
|
}
|
||||||
fallthrough
|
var frame struct {
|
||||||
case 2:
|
Address string `json:"Address"`
|
||||||
// filename:line
|
ModuleName string `json:"ModuleName"`
|
||||||
if line, err := strconv.Atoi(split[1]); err == nil {
|
Symbol []struct {
|
||||||
linenumber = line
|
Line int `json:"Line"`
|
||||||
|
Column int `json:"Column"`
|
||||||
|
FunctionName string `json:"FunctionName"`
|
||||||
|
FileName string `json:"FileName"`
|
||||||
|
StartLine int `json:"StartLine"`
|
||||||
|
} `json:"Symbol"`
|
||||||
}
|
}
|
||||||
fallthrough
|
if err := json.Unmarshal([]byte(line), &frame); err != nil {
|
||||||
case 1:
|
return nil, err
|
||||||
// filename
|
|
||||||
fileline = split[0]
|
|
||||||
default:
|
|
||||||
// Unrecognized, ignore
|
|
||||||
}
|
}
|
||||||
|
var stack []plugin.Frame
|
||||||
|
for _, s := range frame.Symbol {
|
||||||
|
stack = append(stack, plugin.Frame{Func: s.FunctionName, File: s.FileName, Line: s.Line, Column: s.Column, StartLine: s.StartLine})
|
||||||
}
|
}
|
||||||
|
return stack, nil
|
||||||
return plugin.Frame{Func: funcname, File: fileline, Line: linenumber, Column: columnnumber}, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addrInfo returns the stack frame information for a specific program
|
// addrInfo returns the stack frame information for a specific program
|
||||||
|
|
@ -170,18 +177,8 @@ func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
|
||||||
if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
|
if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if d.isData {
|
||||||
var stack []plugin.Frame
|
return d.readDataFrames()
|
||||||
for {
|
|
||||||
frame, end := d.readFrame()
|
|
||||||
if end {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
return d.readCodeFrames()
|
||||||
if frame != (plugin.Frame{}) {
|
|
||||||
stack = append(stack, frame)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func defaultConfig() config {
|
||||||
Trim: true,
|
Trim: true,
|
||||||
DivideBy: 1.0,
|
DivideBy: 1.0,
|
||||||
Sort: "flat",
|
Sort: "flat",
|
||||||
Granularity: "functions",
|
Granularity: "", // Default depends on the display format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,8 @@ func aggregate(prof *profile.Profile, cfg config) error {
|
||||||
var function, filename, linenumber, address bool
|
var function, filename, linenumber, address bool
|
||||||
inlines := !cfg.NoInlines
|
inlines := !cfg.NoInlines
|
||||||
switch cfg.Granularity {
|
switch cfg.Granularity {
|
||||||
|
case "":
|
||||||
|
function = true // Default granularity is "functions"
|
||||||
case "addresses":
|
case "addresses":
|
||||||
if inlines {
|
if inlines {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,10 @@ a {
|
||||||
right: 2px;
|
right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.help {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
{{/* Used to disable events when a modal dialog is displayed */}}
|
{{/* Used to disable events when a modal dialog is displayed */}}
|
||||||
#dialog-overlay {
|
#dialog-overlay {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,12 @@
|
||||||
{{range .Legend}}<div>{{.}}</div>{{end}}
|
{{range .Legend}}<div>{{.}}</div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{if .DocURL}}
|
||||||
|
<div class="menu-item">
|
||||||
|
<div class="help menu-name"><a title="Profile documentation" href="{{.DocURL}}" target="_blank">Help ⤇</a></div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="dialog-overlay"></div>
|
<div id="dialog-overlay"></div>
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,26 @@ body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative; /* Allows absolute positioning of child boxes */
|
position: relative; /* Allows absolute positioning of child boxes */
|
||||||
}
|
}
|
||||||
/* Shows details of frame that is under the mouse */
|
/* Holder for current frame details. */
|
||||||
#current-details {
|
#current-details {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 5px;
|
background: #eee; /* Light grey gives better contrast with boxes */
|
||||||
right: 5px;
|
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
|
padding: 0 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
/* Shows details of frame that is under the mouse */
|
||||||
|
#current-details-left {
|
||||||
|
float: left;
|
||||||
|
max-width: 60%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
#current-details-right {
|
||||||
|
float: right;
|
||||||
|
max-width: 40%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
/* Background of a single flame-graph frame */
|
/* Background of a single flame-graph frame */
|
||||||
.boxbg {
|
.boxbg {
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{template "header" .}}
|
{{template "header" .}}
|
||||||
|
<div id="current-details">
|
||||||
|
<div id="current-details-left"></div>
|
||||||
|
<div id="current-details-right"> </div>
|
||||||
|
</div>
|
||||||
<div id="stack-holder">
|
<div id="stack-holder">
|
||||||
<div id="stack-chart"></div>
|
<div id="stack-chart"></div>
|
||||||
<div id="current-details"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="action-menu" class="submenu">
|
<div id="action-menu" class="submenu">
|
||||||
<span id="action-title"></span>
|
<span id="action-title"></span>
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ function stackViewer(stacks, nodes) {
|
||||||
let actionMenuOn = false; // Is action menu visible?
|
let actionMenuOn = false; // Is action menu visible?
|
||||||
let actionTarget = null; // Box on which action menu is operating.
|
let actionTarget = null; // Box on which action menu is operating.
|
||||||
let diff = false; // Are we displaying a diff?
|
let diff = false; // Are we displaying a diff?
|
||||||
|
let shown = 0; // How many profile values are being displayed?
|
||||||
|
|
||||||
for (const stack of stacks.Stacks) {
|
for (const stack of stacks.Stacks) {
|
||||||
if (stack.Value < 0) {
|
if (stack.Value < 0) {
|
||||||
|
|
@ -39,7 +40,8 @@ function stackViewer(stacks, nodes) {
|
||||||
const search = find('search');
|
const search = find('search');
|
||||||
const actions = find('action-menu');
|
const actions = find('action-menu');
|
||||||
const actionTitle = find('action-title');
|
const actionTitle = find('action-title');
|
||||||
const detailBox = find('current-details');
|
const leftDetailBox = find('current-details-left');
|
||||||
|
const rightDetailBox = find('current-details-right');
|
||||||
|
|
||||||
window.addEventListener('resize', render);
|
window.addEventListener('resize', render);
|
||||||
window.addEventListener('popstate', render);
|
window.addEventListener('popstate', render);
|
||||||
|
|
@ -69,6 +71,7 @@ function stackViewer(stacks, nodes) {
|
||||||
}});
|
}});
|
||||||
|
|
||||||
render();
|
render();
|
||||||
|
clearDetails();
|
||||||
|
|
||||||
// Helper functions follow:
|
// Helper functions follow:
|
||||||
|
|
||||||
|
|
@ -176,17 +179,27 @@ function stackViewer(stacks, nodes) {
|
||||||
if (actionMenuOn) return;
|
if (actionMenuOn) return;
|
||||||
const src = stacks.Sources[box.src];
|
const src = stacks.Sources[box.src];
|
||||||
div.title = details(box) + ' │ ' + src.FullName + (src.Inlined ? "\n(inlined)" : "");
|
div.title = details(box) + ' │ ' + src.FullName + (src.Inlined ? "\n(inlined)" : "");
|
||||||
detailBox.innerText = summary(box.sumpos, box.sumneg);
|
leftDetailBox.innerText = src.FullName + (src.Inlined ? " (inlined)" : "");
|
||||||
|
let timing = summary(box.sumpos, box.sumneg);
|
||||||
|
if (box.self != 0) {
|
||||||
|
timing = "self " + unitText(box.self) + " │ " + timing;
|
||||||
|
}
|
||||||
|
rightDetailBox.innerText = timing;
|
||||||
// Highlight all boxes that have the same source as box.
|
// Highlight all boxes that have the same source as box.
|
||||||
toggleClass(box.src, 'hilite2', true);
|
toggleClass(box.src, 'hilite2', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLeave(box) {
|
function handleLeave(box) {
|
||||||
if (actionMenuOn) return;
|
if (actionMenuOn) return;
|
||||||
detailBox.innerText = '';
|
clearDetails();
|
||||||
toggleClass(box.src, 'hilite2', false);
|
toggleClass(box.src, 'hilite2', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearDetails() {
|
||||||
|
leftDetailBox.innerText = '';
|
||||||
|
rightDetailBox.innerText = percentText(shown);
|
||||||
|
}
|
||||||
|
|
||||||
// Return list of sources that match the regexp given by the 'p' URL parameter.
|
// Return list of sources that match the regexp given by the 'p' URL parameter.
|
||||||
function urlPivots() {
|
function urlPivots() {
|
||||||
const pivots = [];
|
const pivots = [];
|
||||||
|
|
@ -231,10 +244,14 @@ function stackViewer(stacks, nodes) {
|
||||||
const x = PADDING;
|
const x = PADDING;
|
||||||
const y = 0;
|
const y = 0;
|
||||||
|
|
||||||
|
// Show summary for pivots if we are actually pivoting.
|
||||||
|
const showPivotSummary = !(pivots.length == 1 && pivots[0] == 0);
|
||||||
|
|
||||||
|
shown = pos + neg;
|
||||||
displayList.length = 0;
|
displayList.length = 0;
|
||||||
renderStacks(0, xscale, x, y, places, +1); // Callees
|
renderStacks(0, xscale, x, y, places, +1); // Callees
|
||||||
renderStacks(0, xscale, x, y-ROW, places, -1); // Callers (ROW left for separator)
|
renderStacks(0, xscale, x, y-ROW, places, -1); // Callers (ROW left for separator)
|
||||||
display(xscale, pos, neg, displayList);
|
display(xscale, pos, neg, displayList, showPivotSummary);
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderStacks creates boxes with top-left at x,y with children drawn as
|
// renderStacks creates boxes with top-left at x,y with children drawn as
|
||||||
|
|
@ -372,7 +389,7 @@ function stackViewer(stacks, nodes) {
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
function display(xscale, posTotal, negTotal, list) {
|
function display(xscale, posTotal, negTotal, list, showPivotSummary) {
|
||||||
// Sort boxes so that text selection follows a predictable order.
|
// Sort boxes so that text selection follows a predictable order.
|
||||||
list.sort(function(a, b) {
|
list.sort(function(a, b) {
|
||||||
if (a.y != b.y) return a.y - b.y;
|
if (a.y != b.y) return a.y - b.y;
|
||||||
|
|
@ -381,14 +398,15 @@ function stackViewer(stacks, nodes) {
|
||||||
|
|
||||||
// Adjust Y coordinates so that zero is at top.
|
// Adjust Y coordinates so that zero is at top.
|
||||||
let adjust = (list.length > 0) ? list[0].y : 0;
|
let adjust = (list.length > 0) ? list[0].y : 0;
|
||||||
adjust -= ROW + 2*PADDING; // Room for details
|
|
||||||
|
|
||||||
const divs = [];
|
const divs = [];
|
||||||
for (const box of list) {
|
for (const box of list) {
|
||||||
box.y -= adjust;
|
box.y -= adjust;
|
||||||
divs.push(drawBox(xscale, box));
|
divs.push(drawBox(xscale, box));
|
||||||
}
|
}
|
||||||
|
if (showPivotSummary) {
|
||||||
divs.push(drawSep(-adjust, posTotal, negTotal));
|
divs.push(drawSep(-adjust, posTotal, negTotal));
|
||||||
|
}
|
||||||
|
|
||||||
const h = (list.length > 0 ? list[list.length-1].y : 0) + 4*ROW;
|
const h = (list.length > 0 ? list[list.length-1].y : 0) + 4*ROW;
|
||||||
chart.style.height = h+'px';
|
chart.style.height = h+'px';
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,9 @@ func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
|
||||||
// Add quotes for empty values.
|
// Add quotes for empty values.
|
||||||
v = `""`
|
v = `""`
|
||||||
}
|
}
|
||||||
|
if n == "granularity" && v == "" {
|
||||||
|
v = "(default)"
|
||||||
|
}
|
||||||
if comment != "" {
|
if comment != "" {
|
||||||
comment = commentStart + " " + comment
|
comment = commentStart + " " + comment
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ func (ui *webInterface) stackView(w http.ResponseWriter, req *http.Request) {
|
||||||
rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
|
rpt, errList := ui.makeReport(w, req, []string{"svg"}, func(cfg *config) {
|
||||||
cfg.CallTree = true
|
cfg.CallTree = true
|
||||||
cfg.Trim = false
|
cfg.Trim = false
|
||||||
|
if cfg.Granularity == "" {
|
||||||
cfg.Granularity = "filefunctions"
|
cfg.Granularity = "filefunctions"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if rpt == nil {
|
if rpt == nil {
|
||||||
return // error already reported
|
return // error already reported
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ type webArgs struct {
|
||||||
Total int64
|
Total int64
|
||||||
SampleTypes []string
|
SampleTypes []string
|
||||||
Legend []string
|
Legend []string
|
||||||
|
DocURL string
|
||||||
Standalone bool // True for command-line generation of HTML
|
Standalone bool // True for command-line generation of HTML
|
||||||
Help map[string]string
|
Help map[string]string
|
||||||
Nodes []string
|
Nodes []string
|
||||||
|
|
@ -290,6 +291,7 @@ func renderHTML(dst io.Writer, tmpl string, rpt *report.Report, errList, legend
|
||||||
data.Title = file + " " + profile
|
data.Title = file + " " + profile
|
||||||
data.Errors = errList
|
data.Errors = errList
|
||||||
data.Total = rpt.Total()
|
data.Total = rpt.Total()
|
||||||
|
data.DocURL = rpt.DocURL()
|
||||||
data.Legend = legend
|
data.Legend = legend
|
||||||
return getHTMLTemplates().ExecuteTemplate(dst, tmpl, data)
|
return getHTMLTemplates().ExecuteTemplate(dst, tmpl, data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit,
|
||||||
// stextOffset=0xffffffff80200198
|
// stextOffset=0xffffffff80200198
|
||||||
return start - *stextOffset, true
|
return start - *stextOffset, true
|
||||||
}
|
}
|
||||||
if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
|
if start >= 0x8000000000000000 && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
|
||||||
// Some kernels look like:
|
// Some kernels look like:
|
||||||
// VADDR=0xffffffff80200000
|
// VADDR=0xffffffff80200000
|
||||||
// stextOffset=0xffffffff80200198
|
// stextOffset=0xffffffff80200198
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ type Frame struct {
|
||||||
File string // source file name
|
File string // source file name
|
||||||
Line int // line in file
|
Line int // line in file
|
||||||
Column int // column in line (if available)
|
Column int // column in line (if available)
|
||||||
|
StartLine int // start line of function (if available)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Sym describes a single symbol in an object file.
|
// A Sym describes a single symbol in an object file.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package report
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -1168,8 +1169,11 @@ func ProfileLabels(rpt *Report) []string {
|
||||||
if o.SampleType != "" {
|
if o.SampleType != "" {
|
||||||
label = append(label, "Type: "+o.SampleType)
|
label = append(label, "Type: "+o.SampleType)
|
||||||
}
|
}
|
||||||
|
if url := prof.DocURL; url != "" {
|
||||||
|
label = append(label, "Doc: "+url)
|
||||||
|
}
|
||||||
if prof.TimeNanos != 0 {
|
if prof.TimeNanos != 0 {
|
||||||
const layout = "Jan 2, 2006 at 3:04pm (MST)"
|
const layout = "2006-01-02 15:04:05 MST"
|
||||||
label = append(label, "Time: "+time.Unix(0, prof.TimeNanos).Format(layout))
|
label = append(label, "Time: "+time.Unix(0, prof.TimeNanos).Format(layout))
|
||||||
}
|
}
|
||||||
if prof.DurationNanos != 0 {
|
if prof.DurationNanos != 0 {
|
||||||
|
|
@ -1331,6 +1335,22 @@ func (rpt *Report) Total() int64 { return rpt.total }
|
||||||
// OutputFormat returns the output format for the report.
|
// OutputFormat returns the output format for the report.
|
||||||
func (rpt *Report) OutputFormat() int { return rpt.options.OutputFormat }
|
func (rpt *Report) OutputFormat() int { return rpt.options.OutputFormat }
|
||||||
|
|
||||||
|
// DocURL returns the documentation URL for Report, or "" if not available.
|
||||||
|
func (rpt *Report) DocURL() string {
|
||||||
|
u := rpt.prof.DocURL
|
||||||
|
if u == "" || !absoluteURL(u) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func absoluteURL(str string) bool {
|
||||||
|
// Avoid returning relative URLs to prevent unwanted local navigation
|
||||||
|
// within pprof server.
|
||||||
|
u, err := url.Parse(str)
|
||||||
|
return err == nil && (u.Scheme == "https" || u.Scheme == "http")
|
||||||
|
}
|
||||||
|
|
||||||
func abs64(i int64) int64 {
|
func abs64(i int64) int64 {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return -i
|
return -i
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,38 @@
|
||||||
package report
|
package report
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/google/pprof/internal/graph"
|
"github.com/google/pprof/internal/graph"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sepRE = regexp.MustCompile(`::|\.`)
|
var (
|
||||||
|
sepRE = regexp.MustCompile(`::|\.`)
|
||||||
|
fileSepRE = regexp.MustCompile(`/`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileNameSuffixes returns a non-empty sequence of shortened file names
|
||||||
|
// (in decreasing preference) that can be used to represent name.
|
||||||
|
func fileNameSuffixes(name string) []string {
|
||||||
|
if name == "" {
|
||||||
|
// Avoid returning "." when symbol info is missing
|
||||||
|
return []string{""}
|
||||||
|
}
|
||||||
|
return allSuffixes(filepath.ToSlash(filepath.Clean(name)), fileSepRE)
|
||||||
|
}
|
||||||
|
|
||||||
// shortNameList returns a non-empty sequence of shortened names
|
// shortNameList returns a non-empty sequence of shortened names
|
||||||
// (in decreasing preference) that can be used to represent name.
|
// (in decreasing preference) that can be used to represent name.
|
||||||
func shortNameList(name string) []string {
|
func shortNameList(name string) []string {
|
||||||
name = graph.ShortenFunctionName(name)
|
name = graph.ShortenFunctionName(name)
|
||||||
seps := sepRE.FindAllStringIndex(name, -1)
|
return allSuffixes(name, sepRE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allSuffixes returns a list of suffixes (in order of decreasing length)
|
||||||
|
// found by splitting at re.
|
||||||
|
func allSuffixes(name string, re *regexp.Regexp) []string {
|
||||||
|
seps := re.FindAllStringIndex(name, -1)
|
||||||
result := make([]string, 0, len(seps)+1)
|
result := make([]string, 0, len(seps)+1)
|
||||||
result = append(result, name)
|
result = append(result, name)
|
||||||
for _, sep := range seps {
|
for _, sep := range seps {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/google/pprof/internal/measurement"
|
"github.com/google/pprof/internal/measurement"
|
||||||
"github.com/google/pprof/profile"
|
"github.com/google/pprof/profile"
|
||||||
|
|
@ -99,41 +100,57 @@ func (rpt *Report) Stacks() StackSet {
|
||||||
}
|
}
|
||||||
s.makeInitialStacks(rpt)
|
s.makeInitialStacks(rpt)
|
||||||
s.fillPlaces()
|
s.fillPlaces()
|
||||||
s.assignColors()
|
|
||||||
return *s
|
return *s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackSet) makeInitialStacks(rpt *Report) {
|
func (s *StackSet) makeInitialStacks(rpt *Report) {
|
||||||
type key struct {
|
type key struct {
|
||||||
line profile.Line
|
funcName string
|
||||||
|
fileName string
|
||||||
|
line int64
|
||||||
|
column int64
|
||||||
inlined bool
|
inlined bool
|
||||||
}
|
}
|
||||||
srcs := map[key]int{} // Sources identified so far.
|
srcs := map[key]int{} // Sources identified so far.
|
||||||
seenFunctions := map[string]bool{}
|
seenFunctions := map[string]bool{}
|
||||||
unknownIndex := 1
|
unknownIndex := 1
|
||||||
|
|
||||||
getSrc := func(line profile.Line, inlined bool) int {
|
getSrc := func(line profile.Line, inlined bool) int {
|
||||||
k := key{line, inlined}
|
fn := line.Function
|
||||||
|
if fn == nil {
|
||||||
|
fn = &profile.Function{Name: fmt.Sprintf("?%d?", unknownIndex)}
|
||||||
|
unknownIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
k := key{fn.Name, fn.Filename, line.Line, line.Column, inlined}
|
||||||
if i, ok := srcs[k]; ok {
|
if i, ok := srcs[k]; ok {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
x := StackSource{Places: []StackSlot{}} // Ensure Places is non-nil
|
|
||||||
if fn := line.Function; fn != nil {
|
fileName := trimPath(fn.Filename, rpt.options.TrimPath, rpt.options.SourcePath)
|
||||||
x.FullName = fn.Name
|
x := StackSource{
|
||||||
x.FileName = fn.Filename
|
FileName: fileName,
|
||||||
if !seenFunctions[fn.Name] {
|
Inlined: inlined,
|
||||||
x.UniqueName = fn.Name
|
Places: []StackSlot{}, // Ensure Places is non-nil
|
||||||
seenFunctions[fn.Name] = true
|
}
|
||||||
|
if fn.Name != "" {
|
||||||
|
x.FullName = addLineInfo(fn.Name, line)
|
||||||
|
x.Display = shortNameList(x.FullName)
|
||||||
|
x.Color = pickColor(packageName(fn.Name))
|
||||||
|
} else { // Use file name, e.g., for file granularity display.
|
||||||
|
x.FullName = addLineInfo(fileName, line)
|
||||||
|
x.Display = fileNameSuffixes(x.FullName)
|
||||||
|
x.Color = pickColor(filepath.Dir(fileName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seenFunctions[x.FullName] {
|
||||||
|
x.UniqueName = x.FullName
|
||||||
|
seenFunctions[x.FullName] = true
|
||||||
} else {
|
} else {
|
||||||
// Assign a different name so pivoting picks this function.
|
// Assign a different name so pivoting picks this function.
|
||||||
x.UniqueName = fmt.Sprint(fn.Name, "#", fn.ID)
|
x.UniqueName = fmt.Sprint(x.FullName, "#", fn.ID)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
x.FullName = fmt.Sprintf("?%d?", unknownIndex)
|
|
||||||
x.UniqueName = x.FullName
|
|
||||||
unknownIndex++
|
|
||||||
}
|
|
||||||
x.Inlined = inlined
|
|
||||||
x.Display = shortNameList(x.FullName)
|
|
||||||
s.Sources = append(s.Sources, x)
|
s.Sources = append(s.Sources, x)
|
||||||
srcs[k] = len(s.Sources) - 1
|
srcs[k] = len(s.Sources) - 1
|
||||||
return len(s.Sources) - 1
|
return len(s.Sources) - 1
|
||||||
|
|
@ -179,18 +196,25 @@ func (s *StackSet) fillPlaces() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackSet) assignColors() {
|
// pickColor picks a color for key.
|
||||||
// Assign different color indices to different packages.
|
func pickColor(key string) int {
|
||||||
const numColors = 1048576
|
const numColors = 1048576
|
||||||
for i, src := range s.Sources {
|
h := sha256.Sum256([]byte(key))
|
||||||
pkg := packageName(src.FullName)
|
|
||||||
h := sha256.Sum256([]byte(pkg))
|
|
||||||
index := binary.LittleEndian.Uint32(h[:])
|
index := binary.LittleEndian.Uint32(h[:])
|
||||||
s.Sources[i].Color = int(index % numColors)
|
return int(index % numColors)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legend returns the list of lines to display as the legend.
|
// Legend returns the list of lines to display as the legend.
|
||||||
func (s *StackSet) Legend() []string {
|
func (s *StackSet) Legend() []string {
|
||||||
return reportLabels(s.report, s.report.total, len(s.Sources), len(s.Sources), 0, 0, false)
|
return reportLabels(s.report, s.report.total, len(s.Sources), len(s.Sources), 0, 0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addLineInfo(str string, line profile.Line) string {
|
||||||
|
if line.Column != 0 {
|
||||||
|
return fmt.Sprint(str, ":", line.Line, ":", line.Column)
|
||||||
|
}
|
||||||
|
if line.Line != 0 {
|
||||||
|
return fmt.Sprint(str, ":", line.Line)
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,7 @@ func symbolizeOneMapping(m *profile.Mapping, locs []*profile.Location, obj plugi
|
||||||
Name: frame.Func,
|
Name: frame.Func,
|
||||||
SystemName: frame.Func,
|
SystemName: frame.Func,
|
||||||
Filename: frame.File,
|
Filename: frame.File,
|
||||||
|
StartLine: int64(frame.StartLine),
|
||||||
})
|
})
|
||||||
l.Line[i] = profile.Line{
|
l.Line[i] = profile.Line{
|
||||||
Function: f,
|
Function: f,
|
||||||
|
|
|
||||||
|
|
@ -37,19 +37,15 @@ var (
|
||||||
// Symbolize symbolizes profile p by parsing data returned by a symbolz
|
// Symbolize symbolizes profile p by parsing data returned by a symbolz
|
||||||
// handler. syms receives the symbolz query (hex addresses separated by '+')
|
// handler. syms receives the symbolz query (hex addresses separated by '+')
|
||||||
// and returns the symbolz output in a string. If force is false, it will only
|
// and returns the symbolz output in a string. If force is false, it will only
|
||||||
// symbolize locations from mappings not already marked as HasFunctions. Never
|
// symbolize locations from mappings not already marked as HasFunctions. Does
|
||||||
// attempts symbolization of addresses from unsymbolizable system
|
// not skip unsymbolizable files since the symbolz handler can be flexible
|
||||||
// mappings as those may look negative - e.g. "[vsyscall]".
|
// enough to handle some of those cases such as JIT locations in //anon.
|
||||||
func Symbolize(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error {
|
func Symbolize(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error {
|
||||||
for _, m := range p.Mapping {
|
for _, m := range p.Mapping {
|
||||||
if !force && m.HasFunctions {
|
if !force && m.HasFunctions {
|
||||||
// Only check for HasFunctions as symbolz only populates function names.
|
// Only check for HasFunctions as symbolz only populates function names.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Skip well-known system mappings.
|
|
||||||
if m.Unsymbolizable() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mappingSources := sources[m.File]
|
mappingSources := sources[m.File]
|
||||||
if m.BuildID != "" {
|
if m.BuildID != "" {
|
||||||
mappingSources = append(mappingSources, sources[m.BuildID]...)
|
mappingSources = append(mappingSources, sources[m.BuildID]...)
|
||||||
|
|
@ -93,7 +89,7 @@ func symbolz(source string) string {
|
||||||
if strings.Contains(url.Path, "/debug/pprof/") || hasGperftoolsSuffix(url.Path) {
|
if strings.Contains(url.Path, "/debug/pprof/") || hasGperftoolsSuffix(url.Path) {
|
||||||
url.Path = path.Clean(url.Path + "/../symbol")
|
url.Path = path.Clean(url.Path + "/../symbol")
|
||||||
} else {
|
} else {
|
||||||
url.Path = "/symbolz"
|
url.Path = path.Clean(url.Path + "/../symbolz")
|
||||||
}
|
}
|
||||||
url.RawQuery = ""
|
url.RawQuery = ""
|
||||||
return url.String()
|
return url.String()
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ func (p *Profile) preEncode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
|
p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
|
||||||
|
p.docURLX = addString(strings, p.DocURL)
|
||||||
|
|
||||||
p.stringTable = make([]string, len(strings))
|
p.stringTable = make([]string, len(strings))
|
||||||
for s, i := range strings {
|
for s, i := range strings {
|
||||||
|
|
@ -156,6 +157,7 @@ func (p *Profile) encode(b *buffer) {
|
||||||
encodeInt64Opt(b, 12, p.Period)
|
encodeInt64Opt(b, 12, p.Period)
|
||||||
encodeInt64s(b, 13, p.commentX)
|
encodeInt64s(b, 13, p.commentX)
|
||||||
encodeInt64(b, 14, p.defaultSampleTypeX)
|
encodeInt64(b, 14, p.defaultSampleTypeX)
|
||||||
|
encodeInt64Opt(b, 15, p.docURLX)
|
||||||
}
|
}
|
||||||
|
|
||||||
var profileDecoder = []decoder{
|
var profileDecoder = []decoder{
|
||||||
|
|
@ -237,6 +239,8 @@ var profileDecoder = []decoder{
|
||||||
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
|
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
|
||||||
// int64 defaultSampleType = 14
|
// int64 defaultSampleType = 14
|
||||||
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) },
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) },
|
||||||
|
// string doc_link = 15;
|
||||||
|
func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).docURLX) },
|
||||||
}
|
}
|
||||||
|
|
||||||
// postDecode takes the unexported fields populated by decode (with
|
// postDecode takes the unexported fields populated by decode (with
|
||||||
|
|
@ -384,6 +388,7 @@ func (p *Profile) postDecode() error {
|
||||||
|
|
||||||
p.commentX = nil
|
p.commentX = nil
|
||||||
p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
|
p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
|
||||||
|
p.DocURL, err = getString(p.stringTable, &p.docURLX, err)
|
||||||
p.stringTable = nil
|
p.stringTable = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -476,6 +476,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||||
var timeNanos, durationNanos, period int64
|
var timeNanos, durationNanos, period int64
|
||||||
var comments []string
|
var comments []string
|
||||||
seenComments := map[string]bool{}
|
seenComments := map[string]bool{}
|
||||||
|
var docURL string
|
||||||
var defaultSampleType string
|
var defaultSampleType string
|
||||||
for _, s := range srcs {
|
for _, s := range srcs {
|
||||||
if timeNanos == 0 || s.TimeNanos < timeNanos {
|
if timeNanos == 0 || s.TimeNanos < timeNanos {
|
||||||
|
|
@ -494,6 +495,9 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||||
if defaultSampleType == "" {
|
if defaultSampleType == "" {
|
||||||
defaultSampleType = s.DefaultSampleType
|
defaultSampleType = s.DefaultSampleType
|
||||||
}
|
}
|
||||||
|
if docURL == "" {
|
||||||
|
docURL = s.DocURL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p := &Profile{
|
p := &Profile{
|
||||||
|
|
@ -509,6 +513,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
|
||||||
|
|
||||||
Comments: comments,
|
Comments: comments,
|
||||||
DefaultSampleType: defaultSampleType,
|
DefaultSampleType: defaultSampleType,
|
||||||
|
DocURL: docURL,
|
||||||
}
|
}
|
||||||
copy(p.SampleType, srcs[0].SampleType)
|
copy(p.SampleType, srcs[0].SampleType)
|
||||||
return p, nil
|
return p, nil
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ type Profile struct {
|
||||||
Location []*Location
|
Location []*Location
|
||||||
Function []*Function
|
Function []*Function
|
||||||
Comments []string
|
Comments []string
|
||||||
|
DocURL string
|
||||||
|
|
||||||
DropFrames string
|
DropFrames string
|
||||||
KeepFrames string
|
KeepFrames string
|
||||||
|
|
@ -53,6 +54,7 @@ type Profile struct {
|
||||||
encodeMu sync.Mutex
|
encodeMu sync.Mutex
|
||||||
|
|
||||||
commentX []int64
|
commentX []int64
|
||||||
|
docURLX int64
|
||||||
dropFramesX int64
|
dropFramesX int64
|
||||||
keepFramesX int64
|
keepFramesX int64
|
||||||
stringTable []string
|
stringTable []string
|
||||||
|
|
@ -555,6 +557,9 @@ func (p *Profile) String() string {
|
||||||
for _, c := range p.Comments {
|
for _, c := range p.Comments {
|
||||||
ss = append(ss, "Comment: "+c)
|
ss = append(ss, "Comment: "+c)
|
||||||
}
|
}
|
||||||
|
if url := p.DocURL; url != "" {
|
||||||
|
ss = append(ss, fmt.Sprintf("Doc: %s", url))
|
||||||
|
}
|
||||||
if pt := p.PeriodType; pt != nil {
|
if pt := p.PeriodType; pt != nil {
|
||||||
ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
|
ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
|
||||||
}
|
}
|
||||||
|
|
@ -844,7 +849,7 @@ func (p *Profile) HasFileLines() bool {
|
||||||
|
|
||||||
// Unsymbolizable returns true if a mapping points to a binary for which
|
// Unsymbolizable returns true if a mapping points to a binary for which
|
||||||
// locations can't be symbolized in principle, at least now. Examples are
|
// locations can't be symbolized in principle, at least now. Examples are
|
||||||
// "[vdso]", [vsyscall]" and some others, see the code.
|
// "[vdso]", "[vsyscall]" and some others, see the code.
|
||||||
func (m *Mapping) Unsymbolizable() bool {
|
func (m *Mapping) Unsymbolizable() bool {
|
||||||
name := filepath.Base(m.File)
|
name := filepath.Base(m.File)
|
||||||
return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") || m.File == "//anon"
|
return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") || m.File == "//anon"
|
||||||
|
|
|
||||||
|
|
@ -906,6 +906,9 @@ func (st *state) unqualifiedName(module AST) (r AST, isCast bool) {
|
||||||
if len(st.str) > 0 && st.str[0] == 'F' {
|
if len(st.str) > 0 && st.str[0] == 'F' {
|
||||||
st.advance(1)
|
st.advance(1)
|
||||||
friend = true
|
friend = true
|
||||||
|
if len(st.str) < 1 {
|
||||||
|
st.fail("expected unqualified name")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var a AST
|
var a AST
|
||||||
|
|
@ -3149,6 +3152,7 @@ func (st *state) closureTypeName() AST {
|
||||||
// templateParamDecl parses:
|
// templateParamDecl parses:
|
||||||
//
|
//
|
||||||
// <template-param-decl> ::= Ty # type parameter
|
// <template-param-decl> ::= Ty # type parameter
|
||||||
|
// ::= Tk <concept name> [<template-args>] # constrained type parameter
|
||||||
// ::= Tn <type> # non-type parameter
|
// ::= Tn <type> # non-type parameter
|
||||||
// ::= Tt <template-param-decl>* E # template parameter
|
// ::= Tt <template-param-decl>* E # template parameter
|
||||||
// ::= Tp <template-param-decl> # parameter pack
|
// ::= Tp <template-param-decl> # parameter pack
|
||||||
|
|
@ -3178,6 +3182,13 @@ func (st *state) templateParamDecl() (AST, AST) {
|
||||||
}
|
}
|
||||||
return tp, name
|
return tp, name
|
||||||
case 'k':
|
case 'k':
|
||||||
|
// We don't track enclosing template parameter levels.
|
||||||
|
// Don't try to demangle template parameter substitutions
|
||||||
|
// in constraints.
|
||||||
|
hold := st.parsingConstraint
|
||||||
|
st.parsingConstraint = true
|
||||||
|
defer func() { st.parsingConstraint = hold }()
|
||||||
|
|
||||||
st.advance(2)
|
st.advance(2)
|
||||||
constraint, _ := st.name()
|
constraint, _ := st.name()
|
||||||
name := mk("$T", &st.typeTemplateParamCount)
|
name := mk("$T", &st.typeTemplateParamCount)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# github.com/google/pprof v0.0.0-20240722153945-304e4f0156b8
|
# github.com/google/pprof v0.0.0-20241101162523-b92577c0c142
|
||||||
## explicit; go 1.19
|
## explicit; go 1.22
|
||||||
github.com/google/pprof/driver
|
github.com/google/pprof/driver
|
||||||
github.com/google/pprof/internal/binutils
|
github.com/google/pprof/internal/binutils
|
||||||
github.com/google/pprof/internal/driver
|
github.com/google/pprof/internal/driver
|
||||||
|
|
@ -13,7 +13,7 @@ github.com/google/pprof/internal/symbolz
|
||||||
github.com/google/pprof/internal/transport
|
github.com/google/pprof/internal/transport
|
||||||
github.com/google/pprof/profile
|
github.com/google/pprof/profile
|
||||||
github.com/google/pprof/third_party/svgpan
|
github.com/google/pprof/third_party/svgpan
|
||||||
# github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465
|
# github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/ianlancetaylor/demangle
|
github.com/ianlancetaylor/demangle
|
||||||
# golang.org/x/arch v0.11.1-0.20241106162200-f977c2e4e3f4
|
# golang.org/x/arch v0.11.1-0.20241106162200-f977c2e4e3f4
|
||||||
|
|
@ -43,7 +43,7 @@ golang.org/x/mod/zip
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/sync/errgroup
|
golang.org/x/sync/errgroup
|
||||||
golang.org/x/sync/semaphore
|
golang.org/x/sync/semaphore
|
||||||
# golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443
|
# golang.org/x/sys v0.27.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/sys/plan9
|
golang.org/x/sys/plan9
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443 // indirect
|
golang.org/x/sys v0.27.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ golang.org/x/crypto v0.25.1-0.20240722173533-bb80217080b0 h1:wxHbFWyu21uEPJJnYaS
|
||||||
golang.org/x/crypto v0.25.1-0.20240722173533-bb80217080b0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
golang.org/x/crypto v0.25.1-0.20240722173533-bb80217080b0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/net v0.27.1-0.20240722181819-765c7e89b3bd h1:pHzwejE8Zkb94bG4nA+fUeskKPFp1HPldrhv62dabro=
|
golang.org/x/net v0.27.1-0.20240722181819-765c7e89b3bd h1:pHzwejE8Zkb94bG4nA+fUeskKPFp1HPldrhv62dabro=
|
||||||
golang.org/x/net v0.27.1-0.20240722181819-765c7e89b3bd/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.27.1-0.20240722181819-765c7e89b3bd/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443 h1:FPr/RpnBOqfdMWoEYvMQp58uqHLtb3rUd6mnuuowZEE=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ golang.org/x/net/idna
|
||||||
golang.org/x/net/lif
|
golang.org/x/net/lif
|
||||||
golang.org/x/net/nettest
|
golang.org/x/net/nettest
|
||||||
golang.org/x/net/route
|
golang.org/x/net/route
|
||||||
# golang.org/x/sys v0.26.1-0.20241105152852-e0753d469443
|
# golang.org/x/sys v0.27.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/sys/cpu
|
golang.org/x/sys/cpu
|
||||||
# golang.org/x/text v0.19.0
|
# golang.org/x/text v0.19.0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue