mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2020-11-18 19:53:40 -08:00
201 lines
5.4 KiB
Go
201 lines
5.4 KiB
Go
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package symbolz symbolizes a profile using the output from the symbolz
|
|
// service.
|
|
package symbolz
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"path"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/google/pprof/internal/plugin"
|
|
"github.com/google/pprof/profile"
|
|
)
|
|
|
|
var (
|
|
symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`)
|
|
)
|
|
|
|
// 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]".
|
|
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]...)
|
|
}
|
|
for _, source := range mappingSources {
|
|
if symz := symbolz(source.Source); symz != "" {
|
|
if err := symbolizeMapping(symz, int64(source.Start)-int64(m.Start), syms, m, p); err != nil {
|
|
return err
|
|
}
|
|
m.HasFunctions = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// hasGperftoolsSuffix checks whether path ends with one of the suffixes listed in
|
|
// pprof_remote_servers.html from the gperftools distribution
|
|
func hasGperftoolsSuffix(path string) bool {
|
|
suffixes := []string{
|
|
"/pprof/heap",
|
|
"/pprof/growth",
|
|
"/pprof/profile",
|
|
"/pprof/pmuprofile",
|
|
"/pprof/contention",
|
|
}
|
|
for _, s := range suffixes {
|
|
if strings.HasSuffix(path, s) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// symbolz returns the corresponding symbolz source for a profile URL.
|
|
func symbolz(source string) string {
|
|
if url, err := url.Parse(source); err == nil && url.Host != "" {
|
|
// All paths in the net/http/pprof Go package contain /debug/pprof/
|
|
if strings.Contains(url.Path, "/debug/pprof/") || hasGperftoolsSuffix(url.Path) {
|
|
url.Path = path.Clean(url.Path + "/../symbol")
|
|
} else {
|
|
url.Path = "/symbolz"
|
|
}
|
|
url.RawQuery = ""
|
|
return url.String()
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// symbolizeMapping symbolizes locations belonging to a Mapping by querying
|
|
// a symbolz handler. An offset is applied to all addresses to take care of
|
|
// normalization occurred for merged Mappings.
|
|
func symbolizeMapping(source string, offset int64, syms func(string, string) ([]byte, error), m *profile.Mapping, p *profile.Profile) error {
|
|
// Construct query of addresses to symbolize.
|
|
var a []string
|
|
for _, l := range p.Location {
|
|
if l.Mapping == m && l.Address != 0 && len(l.Line) == 0 {
|
|
// Compensate for normalization.
|
|
addr, overflow := adjust(l.Address, offset)
|
|
if overflow {
|
|
return fmt.Errorf("cannot adjust address %d by %d, it would overflow (mapping %v)", l.Address, offset, l.Mapping)
|
|
}
|
|
a = append(a, fmt.Sprintf("%#x", addr))
|
|
}
|
|
}
|
|
|
|
if len(a) == 0 {
|
|
// No addresses to symbolize.
|
|
return nil
|
|
}
|
|
|
|
lines := make(map[uint64]profile.Line)
|
|
functions := make(map[string]*profile.Function)
|
|
|
|
b, err := syms(source, strings.Join(a, "+"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buf := bytes.NewBuffer(b)
|
|
for {
|
|
l, err := buf.ReadString('\n')
|
|
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
|
|
if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
|
|
origAddr, err := strconv.ParseUint(symbol[1], 0, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
|
|
}
|
|
// Reapply offset expected by the profile.
|
|
addr, overflow := adjust(origAddr, -offset)
|
|
if overflow {
|
|
return fmt.Errorf("cannot adjust symbolz address %d by %d, it would overflow", origAddr, -offset)
|
|
}
|
|
|
|
name := symbol[2]
|
|
fn := functions[name]
|
|
if fn == nil {
|
|
fn = &profile.Function{
|
|
ID: uint64(len(p.Function) + 1),
|
|
Name: name,
|
|
SystemName: name,
|
|
}
|
|
functions[name] = fn
|
|
p.Function = append(p.Function, fn)
|
|
}
|
|
|
|
lines[addr] = profile.Line{Function: fn}
|
|
}
|
|
}
|
|
|
|
for _, l := range p.Location {
|
|
if l.Mapping != m {
|
|
continue
|
|
}
|
|
if line, ok := lines[l.Address]; ok {
|
|
l.Line = []profile.Line{line}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// adjust shifts the specified address by the signed offset. It returns the
|
|
// adjusted address. It signals that the address cannot be adjusted without an
|
|
// overflow by returning true in the second return value.
|
|
func adjust(addr uint64, offset int64) (uint64, bool) {
|
|
adj := uint64(int64(addr) + offset)
|
|
if offset < 0 {
|
|
if adj >= addr {
|
|
return 0, true
|
|
}
|
|
} else {
|
|
if adj < addr {
|
|
return 0, true
|
|
}
|
|
}
|
|
return adj, false
|
|
}
|