diff options
| author | Alberto Donizetti <alb.donizetti@gmail.com> | 2021-05-07 19:10:28 +0200 |
|---|---|---|
| committer | Alberto Donizetti <alb.donizetti@gmail.com> | 2021-05-08 14:59:49 +0000 |
| commit | 68327e1aa11457cd570bc7eaf03a0260950f27d9 (patch) | |
| tree | 998e9b0c2b3a08f582555c4039217a982384dde9 /src/cmd/vendor/github.com/google/pprof/internal/elfexec | |
| parent | 4c8f48ed4f3db0e3ba376e6b7a261d26b41d8dd0 (diff) | |
| download | go-68327e1aa11457cd570bc7eaf03a0260950f27d9.tar.xz | |
cmd/vendor: upgrade pprof to latest
This change upgrades the vendored pprof to pick up the fix for a
serious issue that made the source view in browser mode blank
(tracked upstream as google/pprof#621).
I also had to patch pprof.go, since one of the upstream commit we
included introduced a breaking change in the file interface (the Base
method is now called ObjAddr and has a different signature).
I've manually verified that the upgrade fixes the aforementioned
issues with the source view.
Fixes #45786
Change-Id: I00659ae539a2ad603758e1f06572374d483b9ddc
Reviewed-on: https://go-review.googlesource.com/c/go/+/318049
Trust: Alberto Donizetti <alb.donizetti@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/elfexec')
| -rw-r--r-- | src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go | 96 |
1 files changed, 42 insertions, 54 deletions
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 3b3c6ee89f..2638b2db2d 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 @@ -284,83 +284,71 @@ func FindTextProgHeader(f *elf.File) *elf.ProgHeader { return nil } -// FindProgHeaderForMapping returns the loadable program segment header that is -// fully contained in the runtime mapping with file offset pgoff and memory size -// memsz, or an error if the segment cannot be determined. The function returns -// a nil program header and no error if the ELF binary has no loadable segments. -func FindProgHeaderForMapping(f *elf.File, pgoff, memsz uint64) (*elf.ProgHeader, error) { +// 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) { + 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 + // size is 4KB virtually on all the architectures that we care about, so we + // define this metric as a constant. If we encounter architectures where + // page sie is not 4KB, we must try to guess the page size on the system + // where the profile was collected, possibly using the architecture + // specified in the ELF file header. + pageSize = 4096 + pageOffsetMask = pageSize - 1 + pageMask = ^uint64(pageOffsetMask) + ) var headers []*elf.ProgHeader - loadables := 0 + 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 { - headers = append(headers, &p.ProgHeader) + alignedOffset := uint64(0) + if p.Off > (p.Vaddr & pageOffsetMask) { + alignedOffset = p.Off - (p.Vaddr & pageOffsetMask) + } + if alignedOffset <= pgoff { + headers = append(headers, &p.ProgHeader) + } } if p.Type == elf.PT_LOAD { - loadables++ + hasLoadables = true } } - if len(headers) == 1 { - return headers[0], nil + if len(headers) < 2 { + return headers, hasLoadables } - // Some ELF files don't contain any program segments, e.g. .ko loadable kernel - // modules. Don't return an error in such cases. - if loadables == 0 { - return nil, nil - } - if len(headers) == 0 { - return nil, fmt.Errorf("no program header matches file offset %x and memory size %x", pgoff, memsz) - } - - // Segments are mapped page aligned. In some cases, segments may be smaller - // than a page, which causes the next segment to start at a file offset that - // is logically on the same page if we were to align file offsets by page. - // Example: - // LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 - // 0x00000000000006fc 0x00000000000006fc R E 0x200000 - // LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 - // 0x0000000000000230 0x0000000000000238 RW 0x200000 - // - // In this case, perf records the following mappings for this executable: - // 0 0 [0xc0]: PERF_RECORD_MMAP2 87867/87867: [0x400000(0x1000) @ 0 00:3c 512041 0]: r-xp exename - // 0 0 [0xc0]: PERF_RECORD_MMAP2 87867/87867: [0x600000(0x2000) @ 0 00:3c 512041 0]: rw-p exename - // - // Both mappings have file offset 0. The first mapping is one page length and - // it can include only the first loadable segment. Due to page alignment, the - // second mapping starts also at file offset 0, and it spans two pages. It can - // include both the first and the second loadable segments. We must return the - // correct program header to compute the correct base offset. - // - // We cannot use the mapping protections to distinguish between segments, - // because protections are not passed through to this function. - // We cannot use the start address to differentiate between segments, because - // with ASLR, the mapping start address can be any value. - // - // We use a heuristic to compute the minimum mapping size required for a - // segment, assuming mappings are 4k page aligned, and return the segment that - // matches the given mapping size. - const pageSize = 4096 + // 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 4k page size. + // multiple of page size. if memsz%pageSize != 0 { - return nil, fmt.Errorf("mapping size = %x and %d segments match the passed in mapping", memsz, len(headers)) + return headers, hasLoadables } - // Return an error if no segment, or multiple segments match the size, so we can debug. + // Return all found headers if we cannot narrow the selection to a single + // program segment. var ph *elf.ProgHeader - pageMask := ^uint64(pageSize - 1) for _, h := range headers { wantSize := (h.Vaddr+h.Memsz+pageSize-1)&pageMask - (h.Vaddr & pageMask) if wantSize != memsz { continue } if ph != nil { - return nil, fmt.Errorf("found second program header (%#v) that matches memsz %x, first program header is %#v", *h, memsz, *ph) + // Found a second program header matching, so return all previously + // identified headers. + return headers, hasLoadables } ph = h } if ph == nil { - return nil, fmt.Errorf("found %d matching program headers, but none matches mapping size %x", len(headers), memsz) + // No matching header for the strict check. Return all previously identified + // headers. + return headers, hasLoadables } - return ph, nil + return []*elf.ProgHeader{ph}, hasLoadables } |
