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