]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/pprof: update vendored github.com/google/pprof
authorDmitri Shuralyov <dmitshur@golang.org>
Wed, 8 Sep 2021 14:51:06 +0000 (10:51 -0400)
committerDmitri Shuralyov <dmitshur@golang.org>
Wed, 8 Sep 2021 15:36:36 +0000 (15:36 +0000)
Pull in the latest published version of github.com/google/pprof
that is available at this time in the Go 1.18 development cycle.

Done with:

go get -d github.com/google/pprof@latest
go mod tidy
go mod vendor

For #36905.

Change-Id: Ib25aa38365ec70a0bed2a8a6527e5823ab9f9ded
Reviewed-on: https://go-review.googlesource.com/c/go/+/348410
Trust: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
src/cmd/go.mod
src/cmd/go.sum
src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go
src/cmd/vendor/github.com/google/pprof/internal/binutils/binutils.go
src/cmd/vendor/github.com/google/pprof/internal/driver/webhtml.go
src/cmd/vendor/github.com/google/pprof/internal/driver/webui.go
src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
src/cmd/vendor/github.com/google/pprof/internal/report/report.go
src/cmd/vendor/github.com/google/pprof/internal/report/source.go
src/cmd/vendor/modules.txt

index c7b3b02c3be82f565d3d76b163016e09ade58d59..05a118d8127116fcae0f2dc306d9848a547074e9 100644 (file)
@@ -3,7 +3,7 @@ module cmd
 go 1.18
 
 require (
-       github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a
+       github.com/google/pprof v0.0.0-20210827144239-02619b876842
        github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
        golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1
        golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
index 987c236b0234c75947b3181ee2d7404b7ea4c29c..eebb44c05338d4d8f21b7fe5b02d3f1195cf05b3 100644 (file)
@@ -1,8 +1,8 @@
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a h1:jmAp/2PZAScNd62lTD3Mcb0Ey9FvIIJtLohPhtxZJ+Q=
-github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210827144239-02619b876842 h1:JCrt5MIE1fHQtdy1825HwJ45oVQaqHE6lgssRhjcg/o=
+github.com/google/pprof v0.0.0-20210827144239-02619b876842/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1 h1:MwxAfiDvuwX8Nnnc6iRDhzyMyyc2tz5tYyCP/pZcPCg=
index 24c48e649b8875ff0dd0f7fb753a4a8098456911..844c7a475d5174f803142f5d87a6449596d210bb 100644 (file)
@@ -76,7 +76,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"),
                symType: "CODE",
        }
        if isData {
index 5ed8a1f9f1e3823c884a45c3bdb78055f86184dd..e920eeb2fa204331ec562b24e943e84a54a31df2 100644 (file)
@@ -525,6 +525,47 @@ type elfMapping struct {
        stextOffset *uint64
 }
 
+// findProgramHeader returns the program segment that matches the current
+// mapping and the given address, or an error if it cannot find a unique program
+// header.
+func (m *elfMapping) findProgramHeader(ef *elf.File, addr uint64) (*elf.ProgHeader, error) {
+       // For user space executables, we try to find the actual program segment that
+       // is associated with the given mapping. Skip this search if limit <= start.
+       // We cannot use just a check on the start address of the mapping to tell if
+       // it's a kernel / .ko module mapping, because with quipper address remapping
+       // enabled, the address would be in the lower half of the address space.
+
+       if m.stextOffset != nil || m.start >= m.limit || m.limit >= (uint64(1)<<63) {
+               // For the kernel, find the program segment that includes the .text section.
+               return elfexec.FindTextProgHeader(ef), nil
+       }
+
+       // Fetch all the loadable segments.
+       var phdrs []elf.ProgHeader
+       for i := range ef.Progs {
+               if ef.Progs[i].Type == elf.PT_LOAD {
+                       phdrs = append(phdrs, ef.Progs[i].ProgHeader)
+               }
+       }
+       // Some ELF files don't contain any loadable program segments, e.g. .ko
+       // kernel modules. It's not an error to have no header in such cases.
+       if len(phdrs) == 0 {
+               return nil, nil
+       }
+       // Get all program headers associated with the mapping.
+       headers := elfexec.ProgramHeadersForMapping(phdrs, m.offset, m.limit-m.start)
+       if len(headers) == 0 {
+               return nil, errors.New("no program header matches mapping info")
+       }
+       if len(headers) == 1 {
+               return headers[0], nil
+       }
+
+       // Use the file offset corresponding to the address to symbolize, to narrow
+       // down the header.
+       return elfexec.HeaderForFileOffset(headers, addr-m.start+m.offset)
+}
+
 // file implements the binutils.ObjFile interface.
 type file struct {
        b       *binrep
@@ -555,27 +596,9 @@ func (f *file) computeBase(addr uint64) error {
        }
        defer ef.Close()
 
-       var ph *elf.ProgHeader
-       // For user space executables, find the actual program segment that is
-       // associated with the given mapping. Skip this search if limit <= start.
-       // We cannot use just a check on the start address of the mapping to tell if
-       // it's a kernel / .ko module mapping, because with quipper address remapping
-       // enabled, the address would be in the lower half of the address space.
-       if f.m.stextOffset == nil && f.m.start < f.m.limit && f.m.limit < (uint64(1)<<63) {
-               // Get all program headers associated with the mapping.
-               headers, hasLoadables := elfexec.ProgramHeadersForMapping(ef, f.m.offset, f.m.limit-f.m.start)
-
-               // Some ELF files don't contain any loadable program segments, e.g. .ko
-               // kernel modules. It's not an error to have no header in such cases.
-               if hasLoadables {
-                       ph, err = matchUniqueHeader(headers, addr-f.m.start+f.m.offset)
-                       if err != nil {
-                               return fmt.Errorf("failed to find program header for file %q, ELF mapping %#v, address %x: %v", f.name, *f.m, addr, err)
-                       }
-               }
-       } else {
-               // For the kernel, find the program segment that includes the .text section.
-               ph = elfexec.FindTextProgHeader(ef)
+       ph, err := f.m.findProgramHeader(ef, addr)
+       if err != nil {
+               return fmt.Errorf("failed to find program header for file %q, ELF mapping %#v, address %x: %v", f.name, *f.m, addr, err)
        }
 
        base, err := elfexec.GetBase(&ef.FileHeader, ph, f.m.stextOffset, f.m.start, f.m.limit, f.m.offset)
@@ -587,38 +610,6 @@ func (f *file) computeBase(addr uint64) error {
        return nil
 }
 
-// matchUniqueHeader attempts to identify a unique header from the given list,
-// using the given file offset to disambiguate between multiple segments. It
-// returns an error if the header list is empty or if it cannot identify a
-// unique header.
-func matchUniqueHeader(headers []*elf.ProgHeader, fileOffset uint64) (*elf.ProgHeader, error) {
-       if len(headers) == 0 {
-               return nil, errors.New("no program header matches mapping info")
-       }
-       if len(headers) == 1 {
-               // Don't use the file offset if we already have a single header.
-               return headers[0], nil
-       }
-       // We have multiple input segments. Attempt to identify a unique one
-       // based on the given file offset.
-       var ph *elf.ProgHeader
-       for _, h := range headers {
-               if fileOffset >= h.Off && fileOffset < h.Off+h.Memsz {
-                       if ph != nil {
-                               // Assuming no other bugs, this can only happen if we have two or
-                               // more small program segments that fit on the same page, and a
-                               // segment other than the last one includes uninitialized data.
-                               return nil, fmt.Errorf("found second program header (%#v) that matches file offset %x, first program header is %#v. Does first program segment contain uninitialized data?", *h, fileOffset, *ph)
-                       }
-                       ph = h
-               }
-       }
-       if ph == nil {
-               return nil, fmt.Errorf("no program header matches file offset %x", fileOffset)
-       }
-       return ph, nil
-}
-
 func (f *file) Name() string {
        return f.name
 }
index b8e8b50b94d63b9eb85ff381f7029c76523ce871..b9c73271b8c928783469546844e1b66b71144fd6 100644 (file)
@@ -132,6 +132,10 @@ a {
   align-items: center;
   justify-content: center;
 }
+.menu-name a {
+  text-decoration: none;
+  color: #212121;
+}
 .submenu {
   display: none;
   z-index: 1;
@@ -370,6 +374,12 @@ table tr td {
     </div>
   </div>
 
+  <div id="download" class="menu-item">
+    <div class="menu-name">
+      <a href="./download">Download</a>
+    </div>
+  </div>
+
   <div>
     <input id="search" type="text" placeholder="Search regexp" autocomplete="off" autocapitalize="none" size=40>
   </div>
index 52dc68809c7765712d4fa7810fafc86c359df74d..0f3e8bf93c44fbd9ead281a2bf3385928a1f7c22 100644 (file)
@@ -127,6 +127,11 @@ func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Options, d
                        "/flamegraph":   http.HandlerFunc(ui.flamegraph),
                        "/saveconfig":   http.HandlerFunc(ui.saveConfig),
                        "/deleteconfig": http.HandlerFunc(ui.deleteConfig),
+                       "/download": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+                               w.Header().Set("Content-Type", "application/vnd.google.protobuf+gzip")
+                               w.Header().Set("Content-Disposition", "attachment;filename=profile.pb.gz")
+                               p.Write(w)
+                       }),
                },
        }
 
index 2638b2db2d9a95a5a74eb104a389127e59f1c97e..4f1164518589bbd96b6bb62f35dfc64b66be9049 100644 (file)
@@ -284,10 +284,16 @@ func FindTextProgHeader(f *elf.File) *elf.ProgHeader {
        return nil
 }
 
-// ProgramHeadersForMapping returns the loadable program segment headers that
-// are fully contained in the runtime mapping with file offset pgoff and memory
-// size memsz, and if the binary includes any loadable segments.
-func ProgramHeadersForMapping(f *elf.File, pgoff, memsz uint64) ([]*elf.ProgHeader, bool) {
+// ProgramHeadersForMapping returns the program segment headers that overlap
+// the runtime mapping with file offset mapOff and memory size mapSz. We skip
+// over segments zero file size because their file offset values are unreliable.
+// Even if overlapping, a segment is not selected if its aligned file offset is
+// greater than the mapping file offset, or if the mapping includes the last
+// page of the segment, but not the full segment and the mapping includes
+// additional pages after the segment end.
+// The function returns a slice of pointers to the headers in the input
+// slice, which are valid only while phdrs is not modified or discarded.
+func ProgramHeadersForMapping(phdrs []elf.ProgHeader, mapOff, mapSz uint64) []*elf.ProgHeader {
        const (
                // pageSize defines the virtual memory page size used by the loader. This
                // value is dependent on the memory management unit of the CPU. The page
@@ -298,57 +304,61 @@ func ProgramHeadersForMapping(f *elf.File, pgoff, memsz uint64) ([]*elf.ProgHead
                // specified in the ELF file header.
                pageSize       = 4096
                pageOffsetMask = pageSize - 1
-               pageMask       = ^uint64(pageOffsetMask)
        )
+       mapLimit := mapOff + mapSz
        var headers []*elf.ProgHeader
-       hasLoadables := false
-       for _, p := range f.Progs {
-               // The segment must be fully included in the mapping.
-               if p.Type == elf.PT_LOAD && pgoff <= p.Off && p.Off+p.Memsz <= pgoff+memsz {
-                       alignedOffset := uint64(0)
+       for i := range phdrs {
+               p := &phdrs[i]
+               // Skip over segments with zero file size. Their file offsets can have
+               // arbitrary values, see b/195427553.
+               if p.Filesz == 0 {
+                       continue
+               }
+               segLimit := p.Off + p.Memsz
+               // The segment must overlap the mapping.
+               if p.Type == elf.PT_LOAD && mapOff < segLimit && p.Off < mapLimit {
+                       // If the mapping offset is strictly less than the page aligned segment
+                       // offset, then this mapping comes from a different segment, fixes
+                       // b/179920361.
+                       alignedSegOffset := uint64(0)
                        if p.Off > (p.Vaddr & pageOffsetMask) {
-                               alignedOffset = p.Off - (p.Vaddr & pageOffsetMask)
+                               alignedSegOffset = p.Off - (p.Vaddr & pageOffsetMask)
                        }
-                       if alignedOffset <= pgoff {
-                               headers = append(headers, &p.ProgHeader)
+                       if mapOff < alignedSegOffset {
+                               continue
                        }
+                       // If the mapping starts in the middle of the segment, it covers less than
+                       // one page of the segment, and it extends at least one page past the
+                       // segment, then this mapping comes from a different segment.
+                       if mapOff > p.Off && (segLimit < mapOff+pageSize) && (mapLimit >= segLimit+pageSize) {
+                               continue
+                       }
+                       headers = append(headers, p)
                }
-               if p.Type == elf.PT_LOAD {
-                       hasLoadables = true
-               }
-       }
-       if len(headers) < 2 {
-               return headers, hasLoadables
-       }
-
-       // If we have more than one matching segments, try a strict check on the
-       // segment memory size. We use a heuristic to compute the minimum mapping size
-       // required for a segment, assuming mappings are page aligned.
-       // The memory size based heuristic makes sense only if the mapping size is a
-       // multiple of page size.
-       if memsz%pageSize != 0 {
-               return headers, hasLoadables
        }
+       return headers
+}
 
-       // Return all found headers if we cannot narrow the selection to a single
-       // program segment.
+// HeaderForFileOffset attempts to identify a unique program header that
+// includes the given file offset. It returns an error if it cannot identify a
+// unique header.
+func HeaderForFileOffset(headers []*elf.ProgHeader, fileOffset uint64) (*elf.ProgHeader, error) {
        var ph *elf.ProgHeader
        for _, h := range headers {
-               wantSize := (h.Vaddr+h.Memsz+pageSize-1)&pageMask - (h.Vaddr & pageMask)
-               if wantSize != memsz {
-                       continue
-               }
-               if ph != nil {
-                       // Found a second program header matching, so return all previously
-                       // identified headers.
-                       return headers, hasLoadables
+               if fileOffset >= h.Off && fileOffset < h.Off+h.Memsz {
+                       if ph != nil {
+                               // Assuming no other bugs, this can only happen if we have two or
+                               // more small program segments that fit on the same page, and a
+                               // segment other than the last one includes uninitialized data, or
+                               // if the debug binary used for symbolization is stripped of some
+                               // sections, so segment file sizes are smaller than memory sizes.
+                               return nil, fmt.Errorf("found second program header (%#v) that matches file offset %x, first program header is %#v. Is this a stripped binary, or does the first program segment contain uninitialized data?", *h, fileOffset, *ph)
+                       }
+                       ph = h
                }
-               ph = h
        }
        if ph == nil {
-               // No matching header for the strict check. Return all previously identified
-               // headers.
-               return headers, hasLoadables
+               return nil, fmt.Errorf("no program header matches file offset %x", fileOffset)
        }
-       return []*elf.ProgHeader{ph}, hasLoadables
+       return ph, nil
 }
index 4a86554880132d11fa44384b1c95bfc88326d343..e2fb00314ca0ab437b6362891093bb0929c112c1 100644 (file)
@@ -432,6 +432,10 @@ func PrintAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFuncs int) e
                }
        }
 
+       if len(syms) == 0 {
+               return fmt.Errorf("no matches found for regexp: %s", o.Symbol)
+       }
+
        // Correlate the symbols from the binary with the profile samples.
        for _, s := range syms {
                sns := symNodes[s]
@@ -1054,6 +1058,7 @@ func printTree(w io.Writer, rpt *Report) error {
        var flatSum int64
 
        rx := rpt.options.Symbol
+       matched := 0
        for _, n := range g.Nodes {
                name, flat, cum := n.Info.PrintableName(), n.FlatValue(), n.CumValue()
 
@@ -1061,6 +1066,7 @@ func printTree(w io.Writer, rpt *Report) error {
                if rx != nil && !rx.MatchString(name) {
                        continue
                }
+               matched++
 
                fmt.Fprintln(w, separator)
                // Print incoming edges.
@@ -1098,6 +1104,9 @@ func printTree(w io.Writer, rpt *Report) error {
        if len(g.Nodes) > 0 {
                fmt.Fprintln(w, separator)
        }
+       if rx != nil && matched == 0 {
+               return fmt.Errorf("no matches found for regexp: %s", rx)
+       }
        return nil
 }
 
index 54245e5f9ea6a0f2848f6a0b4a063a52bffd03e3..33d04c591d9beba025eb1911f0cbb25be7fd4e67 100644 (file)
@@ -58,6 +58,10 @@ func printSource(w io.Writer, rpt *Report) error {
        }
        functions.Sort(graph.NameOrder)
 
+       if len(functionNodes) == 0 {
+               return fmt.Errorf("no matches found for regexp: %s", o.Symbol)
+       }
+
        sourcePath := o.SourcePath
        if sourcePath == "" {
                wd, err := os.Getwd()
@@ -206,6 +210,9 @@ func PrintWebList(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFiles int) er
                sourcePath = wd
        }
        sp := newSourcePrinter(rpt, obj, sourcePath)
+       if len(sp.interest) == 0 {
+               return fmt.Errorf("no matches found for regexp: %s", rpt.options.Symbol)
+       }
        sp.print(w, maxFiles, rpt)
        sp.close()
        return nil
@@ -299,7 +306,7 @@ func newSourcePrinter(rpt *Report, obj plugin.ObjTool, sourcePath string) *sourc
                                continue
                        }
 
-                       // Seach in inlined stack for a match.
+                       // Search in inlined stack for a match.
                        matchFile := (loc.Mapping != nil && sp.sym.MatchString(loc.Mapping.File))
                        for j, line := range loc.Line {
                                if (j == 0 && matchFile) || matches(line) {
index 8a6497b5f224def05b3cdb6e1b66121c2ff24889..966ba1358ec9bba5250209ddcff5886963f07d04 100644 (file)
@@ -1,4 +1,4 @@
-# github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a
+# github.com/google/pprof v0.0.0-20210827144239-02619b876842
 ## explicit; go 1.14
 github.com/google/pprof/driver
 github.com/google/pprof/internal/binutils