diff options
| author | Emmanuel T Odeke <emmanuel@orijtech.com> | 2024-11-10 12:31:16 -0800 |
|---|---|---|
| committer | Emmanuel Odeke <emmanuel@orijtech.com> | 2024-11-12 01:08:33 +0000 |
| commit | c96939fbed3d60159dc81ee9ad591de8cfd41168 (patch) | |
| tree | 992defe75b08ec86ffa2e19ed4d7c3d2dd1dd337 /src/cmd/vendor/github.com/google | |
| parent | fb9b946adcc8389aafaa43866f3cc26b12411439 (diff) | |
| download | go-c96939fbed3d60159dc81ee9ad591de8cfd41168.tar.xz | |
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>
Diffstat (limited to 'src/cmd/vendor/github.com/google')
22 files changed, 271 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 ⤇</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" |
