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. Does
    41  // not skip unsymbolizable files since the symbolz handler can be flexible
    42  // enough to handle some of those cases such as JIT locations in //anon.
    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  		mappingSources := sources[m.File]
    50  		if m.BuildID != "" {
    51  			mappingSources = append(mappingSources, sources[m.BuildID]...)
    52  		}
    53  		for _, source := range mappingSources {
    54  			if symz := symbolz(source.Source); symz != "" {
    55  				if err := symbolizeMapping(symz, int64(source.Start)-int64(m.Start), syms, m, p); err != nil {
    56  					return err
    57  				}
    58  				m.HasFunctions = true
    59  				break
    60  			}
    61  		}
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  // hasGperftoolsSuffix checks whether path ends with one of the suffixes listed in
    68  // pprof_remote_servers.html from the gperftools distribution
    69  func hasGperftoolsSuffix(path string) bool {
    70  	suffixes := []string{
    71  		"/pprof/heap",
    72  		"/pprof/growth",
    73  		"/pprof/profile",
    74  		"/pprof/pmuprofile",
    75  		"/pprof/contention",
    76  	}
    77  	for _, s := range suffixes {
    78  		if strings.HasSuffix(path, s) {
    79  			return true
    80  		}
    81  	}
    82  	return false
    83  }
    84  
    85  // symbolz returns the corresponding symbolz source for a profile URL.
    86  func symbolz(source string) string {
    87  	if url, err := url.Parse(source); err == nil && url.Host != "" {
    88  		// All paths in the net/http/pprof Go package contain /debug/pprof/
    89  		if strings.Contains(url.Path, "/debug/pprof/") || hasGperftoolsSuffix(url.Path) {
    90  			url.Path = path.Clean(url.Path + "/../symbol")
    91  		} else {
    92  			url.Path = path.Clean(url.Path + "/../symbolz")
    93  		}
    94  		url.RawQuery = ""
    95  		return url.String()
    96  	}
    97  
    98  	return ""
    99  }
   100  
   101  // symbolizeMapping symbolizes locations belonging to a Mapping by querying
   102  // a symbolz handler. An offset is applied to all addresses to take care of
   103  // normalization occurred for merged Mappings.
   104  func symbolizeMapping(source string, offset int64, syms func(string, string) ([]byte, error), m *profile.Mapping, p *profile.Profile) error {
   105  	// Construct query of addresses to symbolize.
   106  	var a []string
   107  	for _, l := range p.Location {
   108  		if l.Mapping == m && l.Address != 0 && len(l.Line) == 0 {
   109  			// Compensate for normalization.
   110  			addr, overflow := adjust(l.Address, offset)
   111  			if overflow {
   112  				return fmt.Errorf("cannot adjust address %d by %d, it would overflow (mapping %v)", l.Address, offset, l.Mapping)
   113  			}
   114  			a = append(a, fmt.Sprintf("%#x", addr))
   115  		}
   116  	}
   117  
   118  	if len(a) == 0 {
   119  		// No addresses to symbolize.
   120  		return nil
   121  	}
   122  
   123  	lines := make(map[uint64]profile.Line)
   124  	functions := make(map[string]*profile.Function)
   125  
   126  	b, err := syms(source, strings.Join(a, "+"))
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	buf := bytes.NewBuffer(b)
   132  	for {
   133  		l, err := buf.ReadString('\n')
   134  
   135  		if err != nil {
   136  			if err == io.EOF {
   137  				break
   138  			}
   139  			return err
   140  		}
   141  
   142  		if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
   143  			origAddr, err := strconv.ParseUint(symbol[1], 0, 64)
   144  			if err != nil {
   145  				return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
   146  			}
   147  			// Reapply offset expected by the profile.
   148  			addr, overflow := adjust(origAddr, -offset)
   149  			if overflow {
   150  				return fmt.Errorf("cannot adjust symbolz address %d by %d, it would overflow", origAddr, -offset)
   151  			}
   152  
   153  			name := symbol[2]
   154  			fn := functions[name]
   155  			if fn == nil {
   156  				fn = &profile.Function{
   157  					ID:         uint64(len(p.Function) + 1),
   158  					Name:       name,
   159  					SystemName: name,
   160  				}
   161  				functions[name] = fn
   162  				p.Function = append(p.Function, fn)
   163  			}
   164  
   165  			lines[addr] = profile.Line{Function: fn}
   166  		}
   167  	}
   168  
   169  	for _, l := range p.Location {
   170  		if l.Mapping != m {
   171  			continue
   172  		}
   173  		if line, ok := lines[l.Address]; ok {
   174  			l.Line = []profile.Line{line}
   175  		}
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  // adjust shifts the specified address by the signed offset. It returns the
   182  // adjusted address. It signals that the address cannot be adjusted without an
   183  // overflow by returning true in the second return value.
   184  func adjust(addr uint64, offset int64) (uint64, bool) {
   185  	adj := uint64(int64(addr) + offset)
   186  	if offset < 0 {
   187  		if adj >= addr {
   188  			return 0, true
   189  		}
   190  	} else {
   191  		if adj < addr {
   192  			return 0, true
   193  		}
   194  	}
   195  	return adj, false
   196  }
   197  

View as plain text