aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/vendor/github.com')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/driver/driver.go9
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go115
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/config.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.css4
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html6
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.css22
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.html5
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js72
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go3
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go4
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go2
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go9
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/report/report.go22
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/report/shortnames.go24
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/report/stacks.go80
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go1
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go12
-rw-r--r--src/cmd/vendor/github.com/google/pprof/profile/encode.go5
-rw-r--r--src/cmd/vendor/github.com/google/pprof/profile/merge.go5
-rw-r--r--src/cmd/vendor/github.com/google/pprof/profile/profile.go7
-rw-r--r--src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go11
23 files changed, 282 insertions, 142 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/driver/driver.go
index 6cbf66939d..989aac32ff 100644
--- a/src/cmd/vendor/github.com/google/pprof/driver/driver.go
+++ b/src/cmd/vendor/github.com/google/pprof/driver/driver.go
@@ -186,10 +186,11 @@ type ObjFile interface {
// A Frame describes a single line in a source file.
type Frame struct {
- Func string // name of function
- File string // source file name
- Line int // line in file
- Column int // column in file
+ Func string // name of function
+ File string // source file name
+ Line int // line 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.
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
index 3049545b6b..2f5d97e89a 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
@@ -16,6 +16,7 @@ package binutils
import (
"bufio"
+ "encoding/json"
"fmt"
"io"
"os/exec"
@@ -37,6 +38,7 @@ type llvmSymbolizer struct {
filename string
rw lineReaderWriter
base uint64
+ isData bool
}
type llvmSymbolizerJob struct {
@@ -76,7 +78,7 @@ func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llvmSymboli
}
j := &llvmSymbolizerJob{
- cmd: exec.Command(cmd, "--inlining", "-demangle=false"),
+ cmd: exec.Command(cmd, "--inlining", "-demangle=false", "--output-style=JSON"),
symType: "CODE",
}
if isData {
@@ -102,63 +104,68 @@ func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llvmSymboli
filename: file,
rw: j,
base: base,
+ isData: isData,
}
return a, nil
}
-// readFrame parses the llvm-symbolizer output for a single address. It
-// returns a populated plugin.Frame and whether it has reached the end of the
-// data.
-func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
- funcname, err := d.rw.readLine()
+// readDataFrames parses the llvm-symbolizer DATA output for a single address. It
+// returns a populated plugin.Frame array with a single entry.
+func (d *llvmSymbolizer) readDataFrames() ([]plugin.Frame, error) {
+ line, err := d.rw.readLine()
if err != nil {
- return plugin.Frame{}, true
+ return nil, err
}
-
- switch funcname {
- case "":
- return plugin.Frame{}, true
- case "??":
- funcname = ""
+ var frame struct {
+ Address string `json:"Address"`
+ ModuleName string `json:"ModuleName"`
+ Data struct {
+ Start string `json:"Start"`
+ Size string `json:"Size"`
+ Name string `json:"Name"`
+ } `json:"Data"`
}
-
- fileline, err := d.rw.readLine()
+ if err := json.Unmarshal([]byte(line), &frame); err != nil {
+ 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 {
- 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
- columnnumber := 0
- // The llvm-symbolizer outputs the <file_name>:<line_number>:<column_number>.
- // When it cannot identify the source code location, it outputs "??:0:0".
- // Older versions output just the filename and line number, so we check for
- // both conditions here.
- 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
- case 2:
- // filename:line
- if line, err := strconv.Atoi(split[1]); err == nil {
- linenumber = line
- }
- fallthrough
- case 1:
- // filename
- fileline = split[0]
- default:
- // Unrecognized, ignore
- }
+// readCodeFrames parses the llvm-symbolizer CODE output for a single address. It
+// returns a populated plugin.Frame array.
+func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) {
+ line, err := d.rw.readLine()
+ if err != nil {
+ return nil, err
}
-
- return plugin.Frame{Func: funcname, File: fileline, Line: linenumber, Column: columnnumber}, false
+ var frame struct {
+ Address string `json:"Address"`
+ ModuleName string `json:"ModuleName"`
+ Symbol []struct {
+ Line int `json:"Line"`
+ Column int `json:"Column"`
+ FunctionName string `json:"FunctionName"`
+ FileName string `json:"FileName"`
+ StartLine int `json:"StartLine"`
+ } `json:"Symbol"`
+ }
+ if err := json.Unmarshal([]byte(line), &frame); err != nil {
+ return nil, err
+ }
+ 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
}
// 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 {
return nil, err
}
-
- var stack []plugin.Frame
- for {
- frame, end := d.readFrame()
- if end {
- break
- }
-
- if frame != (plugin.Frame{}) {
- stack = append(stack, frame)
- }
+ if d.isData {
+ return d.readDataFrames()
}
-
- return stack, nil
+ return d.readCodeFrames()
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
index f7d227416e..090230e2a7 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/config.go
@@ -68,7 +68,7 @@ func defaultConfig() config {
Trim: true,
DivideBy: 1.0,
Sort: "flat",
- Granularity: "functions",
+ Granularity: "", // Default depends on the display format
}
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
index 18941926c5..99e6ba458a 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/driver.go
@@ -254,6 +254,8 @@ func aggregate(prof *profile.Profile, cfg config) error {
var function, filename, linenumber, address bool
inlines := !cfg.NoInlines
switch cfg.Granularity {
+ case "":
+ function = true // Default granularity is "functions"
case "addresses":
if inlines {
return nil
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.css b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.css
index 14f836ff10..0a897ce291 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.css
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/common.css
@@ -148,6 +148,10 @@ a {
right: 2px;
}
+.help {
+ padding-left: 1em;
+}
+
{{/* Used to disable events when a modal dialog is displayed */}}
#dialog-overlay {
display: none;
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html
index e946e6b882..5405a0be95 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/header.html
@@ -83,6 +83,12 @@
{{range .Legend}}<div>{{.}}</div>{{end}}
</div>
</div>
+
+ {{if .DocURL}}
+ <div class="menu-item">
+ <div class="help menu-name"><a title="Profile documentation" href="{{.DocURL}}" target="_blank">Help&nbsp;⤇</a></div>
+ </div>
+ {{end}}
</div>
<div id="dialog-overlay"></div>
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.css b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.css
index 34c54ebb49..1df4f719a5 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.css
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.css
@@ -14,12 +14,26 @@ body {
width: 100%;
position: relative; /* Allows absolute positioning of child boxes */
}
-/* Shows details of frame that is under the mouse */
+/* Holder for current frame details. */
#current-details {
- position: absolute;
- top: 5px;
- right: 5px;
+ position: relative;
+ background: #eee; /* Light grey gives better contrast with boxes */
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 */
.boxbg {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.html b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.html
index c2f8cf26b1..a4e4077ed8 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.html
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.html
@@ -8,9 +8,12 @@
</head>
<body>
{{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-chart"></div>
- <div id="current-details"></div>
</div>
<div id="action-menu" class="submenu">
<span id="action-title"></span>
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js
index ced7151e8e..484c2d7590 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js
@@ -21,6 +21,7 @@ function stackViewer(stacks, nodes) {
let actionMenuOn = false; // Is action menu visible?
let actionTarget = null; // Box on which action menu is operating.
let diff = false; // Are we displaying a diff?
+ let shown = 0; // How many profile values are being displayed?
for (const stack of stacks.Stacks) {
if (stack.Value < 0) {
@@ -39,7 +40,8 @@ function stackViewer(stacks, nodes) {
const search = find('search');
const actions = find('action-menu');
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('popstate', render);
@@ -69,6 +71,7 @@ function stackViewer(stacks, nodes) {
}});
render();
+ clearDetails();
// Helper functions follow:
@@ -176,17 +179,27 @@ function stackViewer(stacks, nodes) {
if (actionMenuOn) return;
const src = stacks.Sources[box.src];
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.
toggleClass(box.src, 'hilite2', true);
}
function handleLeave(box) {
if (actionMenuOn) return;
- detailBox.innerText = '';
+ clearDetails();
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.
function urlPivots() {
const pivots = [];
@@ -231,10 +244,14 @@ function stackViewer(stacks, nodes) {
const x = PADDING;
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;
renderStacks(0, xscale, x, y, places, +1); // Callees
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
@@ -262,22 +279,22 @@ function stackViewer(stacks, nodes) {
// // Group represents a displayed (sub)tree.
// interface Group {
// name: string; // Full name of source
- // src: number; // Index in stacks.Sources
+ // src: number; // Index in stacks.Sources
// self: number; // Contribution as leaf (may be < 0 for diffs)
- // sumpos: number; // Sum of |self| of positive nodes in tree (>= 0)
- // sumneg: number; // Sum of |self| of negative nodes in tree (>= 0)
+ // sumpos: number; // Sum of |self| of positive nodes in tree (>= 0)
+ // sumneg: number; // Sum of |self| of negative nodes in tree (>= 0)
// places: Place[]; // Stack slots that contributed to this group
// }
//
// // Box is a rendered item.
// interface Box {
- // x: number; // X coordinate of top-left
- // y: number; // Y coordinate of top-left
- // width: number; // Width of box to display
- // src: number; // Index in stacks.Sources
- // sumpos: number; // From corresponding Group
- // sumneg: number; // From corresponding Group
- // self: number; // From corresponding Group
+ // x: number; // X coordinate of top-left
+ // y: number; // Y coordinate of top-left
+ // width: number; // Width of box to display
+ // src: number; // Index in stacks.Sources
+ // sumpos: number; // From corresponding Group
+ // sumneg: number; // From corresponding Group
+ // self: number; // From corresponding Group
// };
function groupWidth(xscale, g) {
@@ -297,14 +314,14 @@ function stackViewer(stacks, nodes) {
y: y,
width: width,
src: g.src,
- sumpos: g.sumpos,
- sumneg: g.sumneg,
+ sumpos: g.sumpos,
+ sumneg: g.sumneg,
self: g.self,
};
displayList.push(box);
if (direction > 0) {
- // Leave gap on left hand side to indicate self contribution.
- x += xscale*Math.abs(g.self);
+ // Leave gap on left hand side to indicate self contribution.
+ x += xscale*Math.abs(g.self);
}
}
y += direction * ROW;
@@ -354,9 +371,9 @@ function stackViewer(stacks, nodes) {
groups.push(group);
}
if (stack.Value < 0) {
- group.sumneg += -stack.Value;
+ group.sumneg += -stack.Value;
} else {
- group.sumpos += stack.Value;
+ group.sumpos += stack.Value;
}
group.self += (place.Pos == stack.Sources.length-1) ? stack.Value : 0;
group.places.push(place);
@@ -372,7 +389,7 @@ function stackViewer(stacks, nodes) {
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.
list.sort(function(a, b) {
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.
let adjust = (list.length > 0) ? list[0].y : 0;
- adjust -= ROW + 2*PADDING; // Room for details
const divs = [];
for (const box of list) {
box.y -= adjust;
divs.push(drawBox(xscale, box));
}
- divs.push(drawSep(-adjust, posTotal, negTotal));
+ if (showPivotSummary) {
+ divs.push(drawSep(-adjust, posTotal, negTotal));
+ }
const h = (list.length > 0 ? list[list.length-1].y : 0) + 4*ROW;
chart.style.height = h+'px';
@@ -423,8 +441,8 @@ function stackViewer(stacks, nodes) {
const delta = box.sumpos - box.sumneg;
const partWidth = xscale * Math.abs(delta);
if (partWidth >= MIN_WIDTH) {
- r.appendChild(makeRect((delta < 0 ? 'negative' : 'positive'),
- 0, 0, partWidth, ROW-1));
+ r.appendChild(makeRect((delta < 0 ? 'negative' : 'positive'),
+ 0, 0, partWidth, ROW-1));
}
}
@@ -522,9 +540,9 @@ function stackViewer(stacks, nodes) {
seen.add(place.Stack);
const stack = stacks.Stacks[place.Stack];
if (stack.Value < 0) {
- neg += -stack.Value;
+ neg += -stack.Value;
} else {
- pos += stack.Value;
+ pos += stack.Value;
}
}
return [pos, neg];
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
index e6e865f385..f9b197bc39 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/interactive.go
@@ -206,6 +206,9 @@ func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
// Add quotes for empty values.
v = `""`
}
+ if n == "granularity" && v == "" {
+ v = "(default)"
+ }
if comment != "" {
comment = commentStart + " " + comment
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go
index a7936107d1..9d3324e3e0 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/stacks.go
@@ -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) {
cfg.CallTree = true
cfg.Trim = false
- cfg.Granularity = "filefunctions"
+ if cfg.Granularity == "" {
+ cfg.Granularity = "filefunctions"
+ }
})
if rpt == nil {
return // error already reported
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
index 2a2d7fb1d2..dd628f7c2d 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
@@ -79,6 +79,7 @@ type webArgs struct {
Total int64
SampleTypes []string
Legend []string
+ DocURL string
Standalone bool // True for command-line generation of HTML
Help map[string]string
Nodes []string
@@ -290,6 +291,7 @@ func renderHTML(dst io.Writer, tmpl string, rpt *report.Report, errList, legend
data.Title = file + " " + profile
data.Errors = errList
data.Total = rpt.Total()
+ data.DocURL = rpt.DocURL()
data.Legend = legend
return getHTMLTemplates().ExecuteTemplate(dst, tmpl, data)
}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
index 10436a2256..3f5b09b5e7 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
@@ -181,7 +181,7 @@ func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit,
// stextOffset=0xffffffff80200198
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:
// VADDR=0xffffffff80200000
// stextOffset=0xffffffff80200198
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go b/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go
index f2ef987185..2692c72276 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/plugin/plugin.go
@@ -159,10 +159,11 @@ type ObjFile interface {
// A Frame describes a location in a single line in a source file.
type Frame struct {
- Func string // name of function
- File string // source file name
- Line int // line in file
- Column int // column in line (if available)
+ Func string // name of function
+ File string // source file name
+ Line int // line in file
+ 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.
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go
index e21ce859d4..8e73f179ec 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go
@@ -19,6 +19,7 @@ package report
import (
"fmt"
"io"
+ "net/url"
"path/filepath"
"regexp"
"sort"
@@ -1168,8 +1169,11 @@ func ProfileLabels(rpt *Report) []string {
if o.SampleType != "" {
label = append(label, "Type: "+o.SampleType)
}
+ if url := prof.DocURL; url != "" {
+ label = append(label, "Doc: "+url)
+ }
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))
}
if prof.DurationNanos != 0 {
@@ -1331,6 +1335,22 @@ func (rpt *Report) Total() int64 { return rpt.total }
// OutputFormat returns the output format for the report.
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 {
if i < 0 {
return -i
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/shortnames.go b/src/cmd/vendor/github.com/google/pprof/internal/report/shortnames.go
index 3d9f3f48ea..4663573a43 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/report/shortnames.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/report/shortnames.go
@@ -15,18 +15,38 @@
package report
import (
+ "path/filepath"
"regexp"
"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
// (in decreasing preference) that can be used to represent name.
func shortNameList(name string) []string {
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 = append(result, name)
for _, sep := range seps {
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/stacks.go b/src/cmd/vendor/github.com/google/pprof/internal/report/stacks.go
index c6b07b86de..dbf20bebe9 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/report/stacks.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/report/stacks.go
@@ -18,6 +18,7 @@ import (
"crypto/sha256"
"encoding/binary"
"fmt"
+ "path/filepath"
"github.com/google/pprof/internal/measurement"
"github.com/google/pprof/profile"
@@ -99,41 +100,57 @@ func (rpt *Report) Stacks() StackSet {
}
s.makeInitialStacks(rpt)
s.fillPlaces()
- s.assignColors()
return *s
}
func (s *StackSet) makeInitialStacks(rpt *Report) {
type key struct {
- line profile.Line
- inlined bool
+ funcName string
+ fileName string
+ line int64
+ column int64
+ inlined bool
}
srcs := map[key]int{} // Sources identified so far.
seenFunctions := map[string]bool{}
unknownIndex := 1
+
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 {
return i
}
- x := StackSource{Places: []StackSlot{}} // Ensure Places is non-nil
- if fn := line.Function; fn != nil {
- x.FullName = fn.Name
- x.FileName = fn.Filename
- if !seenFunctions[fn.Name] {
- x.UniqueName = fn.Name
- seenFunctions[fn.Name] = true
- } else {
- // Assign a different name so pivoting picks this function.
- x.UniqueName = fmt.Sprint(fn.Name, "#", fn.ID)
- }
- } else {
- x.FullName = fmt.Sprintf("?%d?", unknownIndex)
+
+ fileName := trimPath(fn.Filename, rpt.options.TrimPath, rpt.options.SourcePath)
+ x := StackSource{
+ FileName: fileName,
+ Inlined: inlined,
+ Places: []StackSlot{}, // Ensure Places is non-nil
+ }
+ 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
- unknownIndex++
+ seenFunctions[x.FullName] = true
+ } else {
+ // Assign a different name so pivoting picks this function.
+ x.UniqueName = fmt.Sprint(x.FullName, "#", fn.ID)
}
- x.Inlined = inlined
- x.Display = shortNameList(x.FullName)
+
s.Sources = append(s.Sources, x)
srcs[k] = len(s.Sources) - 1
return len(s.Sources) - 1
@@ -179,18 +196,25 @@ func (s *StackSet) fillPlaces() {
}
}
-func (s *StackSet) assignColors() {
- // Assign different color indices to different packages.
+// pickColor picks a color for key.
+func pickColor(key string) int {
const numColors = 1048576
- for i, src := range s.Sources {
- pkg := packageName(src.FullName)
- h := sha256.Sum256([]byte(pkg))
- index := binary.LittleEndian.Uint32(h[:])
- s.Sources[i].Color = int(index % numColors)
- }
+ h := sha256.Sum256([]byte(key))
+ index := binary.LittleEndian.Uint32(h[:])
+ return int(index % numColors)
}
// Legend returns the list of lines to display as the legend.
func (s *StackSet) Legend() []string {
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
+}
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go b/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
index 70b4047269..0d45136461 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go
@@ -228,6 +228,7 @@ func symbolizeOneMapping(m *profile.Mapping, locs []*profile.Location, obj plugi
Name: frame.Func,
SystemName: frame.Func,
Filename: frame.File,
+ StartLine: int64(frame.StartLine),
})
l.Line[i] = profile.Line{
Function: f,
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go b/src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go
index 7be304866f..97d29e45d1 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go
@@ -37,19 +37,15 @@ var (
// 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. If force is false, it will only
-// symbolize locations from mappings not already marked as HasFunctions. Never
-// attempts symbolization of addresses from unsymbolizable system
-// mappings as those may look negative - e.g. "[vsyscall]".
+// symbolize locations from mappings not already marked as HasFunctions. Does
+// not skip unsymbolizable files since the symbolz handler can be flexible
+// 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 {
for _, m := range p.Mapping {
if !force && m.HasFunctions {
// Only check for HasFunctions as symbolz only populates function names.
continue
}
- // Skip well-known system mappings.
- if m.Unsymbolizable() {
- continue
- }
mappingSources := sources[m.File]
if 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) {
url.Path = path.Clean(url.Path + "/../symbol")
} else {
- url.Path = "/symbolz"
+ url.Path = path.Clean(url.Path + "/../symbolz")
}
url.RawQuery = ""
return url.String()
diff --git a/src/cmd/vendor/github.com/google/pprof/profile/encode.go b/src/cmd/vendor/github.com/google/pprof/profile/encode.go
index 860bb304c3..8ce9d3cf3b 100644
--- a/src/cmd/vendor/github.com/google/pprof/profile/encode.go
+++ b/src/cmd/vendor/github.com/google/pprof/profile/encode.go
@@ -122,6 +122,7 @@ func (p *Profile) preEncode() {
}
p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
+ p.docURLX = addString(strings, p.DocURL)
p.stringTable = make([]string, len(strings))
for s, i := range strings {
@@ -156,6 +157,7 @@ func (p *Profile) encode(b *buffer) {
encodeInt64Opt(b, 12, p.Period)
encodeInt64s(b, 13, p.commentX)
encodeInt64(b, 14, p.defaultSampleTypeX)
+ encodeInt64Opt(b, 15, p.docURLX)
}
var profileDecoder = []decoder{
@@ -237,6 +239,8 @@ var profileDecoder = []decoder{
func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
// int64 defaultSampleType = 14
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
@@ -384,6 +388,7 @@ func (p *Profile) postDecode() error {
p.commentX = nil
p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
+ p.DocURL, err = getString(p.stringTable, &p.docURLX, err)
p.stringTable = nil
return err
}
diff --git a/src/cmd/vendor/github.com/google/pprof/profile/merge.go b/src/cmd/vendor/github.com/google/pprof/profile/merge.go
index eee0132e74..ba4d746407 100644
--- a/src/cmd/vendor/github.com/google/pprof/profile/merge.go
+++ b/src/cmd/vendor/github.com/google/pprof/profile/merge.go
@@ -476,6 +476,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
var timeNanos, durationNanos, period int64
var comments []string
seenComments := map[string]bool{}
+ var docURL string
var defaultSampleType string
for _, s := range srcs {
if timeNanos == 0 || s.TimeNanos < timeNanos {
@@ -494,6 +495,9 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
if defaultSampleType == "" {
defaultSampleType = s.DefaultSampleType
}
+ if docURL == "" {
+ docURL = s.DocURL
+ }
}
p := &Profile{
@@ -509,6 +513,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
Comments: comments,
DefaultSampleType: defaultSampleType,
+ DocURL: docURL,
}
copy(p.SampleType, srcs[0].SampleType)
return p, nil
diff --git a/src/cmd/vendor/github.com/google/pprof/profile/profile.go b/src/cmd/vendor/github.com/google/pprof/profile/profile.go
index 5551eb0bfa..f47a243903 100644
--- a/src/cmd/vendor/github.com/google/pprof/profile/profile.go
+++ b/src/cmd/vendor/github.com/google/pprof/profile/profile.go
@@ -39,6 +39,7 @@ type Profile struct {
Location []*Location
Function []*Function
Comments []string
+ DocURL string
DropFrames string
KeepFrames string
@@ -53,6 +54,7 @@ type Profile struct {
encodeMu sync.Mutex
commentX []int64
+ docURLX int64
dropFramesX int64
keepFramesX int64
stringTable []string
@@ -555,6 +557,9 @@ func (p *Profile) String() string {
for _, c := range p.Comments {
ss = append(ss, "Comment: "+c)
}
+ if url := p.DocURL; url != "" {
+ ss = append(ss, fmt.Sprintf("Doc: %s", url))
+ }
if pt := p.PeriodType; pt != nil {
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
// 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 {
name := filepath.Base(m.File)
return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") || m.File == "//anon"
diff --git a/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go b/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go
index 4ca57e62a4..dc238e0773 100644
--- a/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go
+++ b/src/cmd/vendor/github.com/ianlancetaylor/demangle/demangle.go
@@ -906,6 +906,9 @@ func (st *state) unqualifiedName(module AST) (r AST, isCast bool) {
if len(st.str) > 0 && st.str[0] == 'F' {
st.advance(1)
friend = true
+ if len(st.str) < 1 {
+ st.fail("expected unqualified name")
+ }
}
var a AST
@@ -3149,6 +3152,7 @@ func (st *state) closureTypeName() AST {
// templateParamDecl parses:
//
// <template-param-decl> ::= Ty # type parameter
+// ::= Tk <concept name> [<template-args>] # constrained type parameter
// ::= Tn <type> # non-type parameter
// ::= Tt <template-param-decl>* E # template parameter
// ::= Tp <template-param-decl> # parameter pack
@@ -3178,6 +3182,13 @@ func (st *state) templateParamDecl() (AST, AST) {
}
return tp, name
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)
constraint, _ := st.name()
name := mk("$T", &st.typeTemplateParamCount)