...

Source file src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go

     1	// Copyright 2014 Google Inc. All Rights Reserved.
     2	//
     3	// Licensed under the Apache License, Version 2.0 (the "License");
     4	// you may not use this file except in compliance with the License.
     5	// You may obtain a copy of the License at
     6	//
     7	//     http://www.apache.org/licenses/LICENSE-2.0
     8	//
     9	// Unless required by applicable law or agreed to in writing, software
    10	// distributed under the License is distributed on an "AS IS" BASIS,
    11	// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12	// See the License for the specific language governing permissions and
    13	// limitations under the License.
    14	
    15	// Package symbolz symbolizes a profile using the output from the symbolz
    16	// service.
    17	package symbolz
    18	
    19	import (
    20		"bytes"
    21		"fmt"
    22		"io"
    23		"net/url"
    24		"path"
    25		"regexp"
    26		"strconv"
    27		"strings"
    28	
    29		"github.com/google/pprof/internal/plugin"
    30		"github.com/google/pprof/profile"
    31	)
    32	
    33	var (
    34		symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`)
    35	)
    36	
    37	// Symbolize symbolizes profile p by parsing data returned by a symbolz
    38	// handler. syms receives the symbolz query (hex addresses separated by '+')
    39	// and returns the symbolz output in a string. If force is false, it will only
    40	// symbolize locations from mappings not already marked as HasFunctions. Never
    41	// attempts symbolization of addresses from unsymbolizable system
    42	// mappings as those may look negative - e.g. "[vsyscall]".
    43	func Symbolize(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error {
    44		for _, m := range p.Mapping {
    45			if !force && m.HasFunctions {
    46				// Only check for HasFunctions as symbolz only populates function names.
    47				continue
    48			}
    49			// Skip well-known system mappings.
    50			if m.Unsymbolizable() {
    51				continue
    52			}
    53			mappingSources := sources[m.File]
    54			if m.BuildID != "" {
    55				mappingSources = append(mappingSources, sources[m.BuildID]...)
    56			}
    57			for _, source := range mappingSources {
    58				if symz := symbolz(source.Source); symz != "" {
    59					if err := symbolizeMapping(symz, int64(source.Start)-int64(m.Start), syms, m, p); err != nil {
    60						return err
    61					}
    62					m.HasFunctions = true
    63					break
    64				}
    65			}
    66		}
    67	
    68		return nil
    69	}
    70	
    71	// hasGperftoolsSuffix checks whether path ends with one of the suffixes listed in
    72	// pprof_remote_servers.html from the gperftools distribution
    73	func hasGperftoolsSuffix(path string) bool {
    74		suffixes := []string{
    75			"/pprof/heap",
    76			"/pprof/growth",
    77			"/pprof/profile",
    78			"/pprof/pmuprofile",
    79			"/pprof/contention",
    80		}
    81		for _, s := range suffixes {
    82			if strings.HasSuffix(path, s) {
    83				return true
    84			}
    85		}
    86		return false
    87	}
    88	
    89	// symbolz returns the corresponding symbolz source for a profile URL.
    90	func symbolz(source string) string {
    91		if url, err := url.Parse(source); err == nil && url.Host != "" {
    92			// All paths in the net/http/pprof Go package contain /debug/pprof/
    93			if strings.Contains(url.Path, "/debug/pprof/") || hasGperftoolsSuffix(url.Path) {
    94				url.Path = path.Clean(url.Path + "/../symbol")
    95			} else {
    96				url.Path = "/symbolz"
    97			}
    98			url.RawQuery = ""
    99			return url.String()
   100		}
   101	
   102		return ""
   103	}
   104	
   105	// symbolizeMapping symbolizes locations belonging to a Mapping by querying
   106	// a symbolz handler. An offset is applied to all addresses to take care of
   107	// normalization occurred for merged Mappings.
   108	func symbolizeMapping(source string, offset int64, syms func(string, string) ([]byte, error), m *profile.Mapping, p *profile.Profile) error {
   109		// Construct query of addresses to symbolize.
   110		var a []string
   111		for _, l := range p.Location {
   112			if l.Mapping == m && l.Address != 0 && len(l.Line) == 0 {
   113				// Compensate for normalization.
   114				addr, overflow := adjust(l.Address, offset)
   115				if overflow {
   116					return fmt.Errorf("cannot adjust address %d by %d, it would overflow (mapping %v)", l.Address, offset, l.Mapping)
   117				}
   118				a = append(a, fmt.Sprintf("%#x", addr))
   119			}
   120		}
   121	
   122		if len(a) == 0 {
   123			// No addresses to symbolize.
   124			return nil
   125		}
   126	
   127		lines := make(map[uint64]profile.Line)
   128		functions := make(map[string]*profile.Function)
   129	
   130		b, err := syms(source, strings.Join(a, "+"))
   131		if err != nil {
   132			return err
   133		}
   134	
   135		buf := bytes.NewBuffer(b)
   136		for {
   137			l, err := buf.ReadString('\n')
   138	
   139			if err != nil {
   140				if err == io.EOF {
   141					break
   142				}
   143				return err
   144			}
   145	
   146			if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
   147				origAddr, err := strconv.ParseUint(symbol[1], 0, 64)
   148				if err != nil {
   149					return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
   150				}
   151				// Reapply offset expected by the profile.
   152				addr, overflow := adjust(origAddr, -offset)
   153				if overflow {
   154					return fmt.Errorf("cannot adjust symbolz address %d by %d, it would overflow", origAddr, -offset)
   155				}
   156	
   157				name := symbol[2]
   158				fn := functions[name]
   159				if fn == nil {
   160					fn = &profile.Function{
   161						ID:         uint64(len(p.Function) + 1),
   162						Name:       name,
   163						SystemName: name,
   164					}
   165					functions[name] = fn
   166					p.Function = append(p.Function, fn)
   167				}
   168	
   169				lines[addr] = profile.Line{Function: fn}
   170			}
   171		}
   172	
   173		for _, l := range p.Location {
   174			if l.Mapping != m {
   175				continue
   176			}
   177			if line, ok := lines[l.Address]; ok {
   178				l.Line = []profile.Line{line}
   179			}
   180		}
   181	
   182		return nil
   183	}
   184	
   185	// adjust shifts the specified address by the signed offset. It returns the
   186	// adjusted address. It signals that the address cannot be adjusted without an
   187	// overflow by returning true in the second return value.
   188	func adjust(addr uint64, offset int64) (uint64, bool) {
   189		adj := uint64(int64(addr) + offset)
   190		if offset < 0 {
   191			if adj >= addr {
   192				return 0, true
   193			}
   194		} else {
   195			if adj < addr {
   196				return 0, true
   197			}
   198		}
   199		return adj, false
   200	}
   201	

View as plain text