golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/symbolz/symbolz.go (about)

     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
    38  // symbolz handler. syms receives the symbolz query (hex addresses
    39  // separated by '+') and returns the symbolz output in a string. It
    40  // symbolizes all locations based on their addresses, regardless of
    41  // mapping.
    42  func Symbolize(sources plugin.MappingSources, syms func(string, string) ([]byte, error), p *profile.Profile, ui plugin.UI) error {
    43  	for _, m := range p.Mapping {
    44  		if m.HasFunctions {
    45  			continue
    46  		}
    47  		mappingSources := sources[m.File]
    48  		if m.BuildID != "" {
    49  			mappingSources = append(mappingSources, sources[m.BuildID]...)
    50  		}
    51  		for _, source := range mappingSources {
    52  			if symz := symbolz(source.Source); symz != "" {
    53  				if err := symbolizeMapping(symz, int64(source.Start)-int64(m.Start), syms, m, p); err != nil {
    54  					return err
    55  				}
    56  				m.HasFunctions = true
    57  				break
    58  			}
    59  		}
    60  	}
    61  
    62  	return nil
    63  }
    64  
    65  // symbolz returns the corresponding symbolz source for a profile URL.
    66  func symbolz(source string) string {
    67  	if url, err := url.Parse(source); err == nil && url.Host != "" {
    68  		if strings.Contains(url.Path, "/") {
    69  			if dir := path.Dir(url.Path); dir == "/debug/pprof" {
    70  				// For Go language profile handlers in net/http/pprof package.
    71  				url.Path = "/debug/pprof/symbol"
    72  			} else {
    73  				url.Path = "/symbolz"
    74  			}
    75  			url.RawQuery = ""
    76  			return url.String()
    77  		}
    78  	}
    79  
    80  	return ""
    81  }
    82  
    83  // symbolizeMapping symbolizes locations belonging to a Mapping by querying
    84  // a symbolz handler. An offset is applied to all addresses to take care of
    85  // normalization occured for merged Mappings.
    86  func symbolizeMapping(source string, offset int64, syms func(string, string) ([]byte, error), m *profile.Mapping, p *profile.Profile) error {
    87  	// Construct query of addresses to symbolize.
    88  	var a []string
    89  	for _, l := range p.Location {
    90  		if l.Mapping == m && l.Address != 0 && len(l.Line) == 0 {
    91  			// Compensate for normalization.
    92  			addr := int64(l.Address) + offset
    93  			if addr < 0 {
    94  				return fmt.Errorf("unexpected negative adjusted address, mapping %v source %d, offset %d", l.Mapping, l.Address, offset)
    95  			}
    96  			a = append(a, fmt.Sprintf("%#x", addr))
    97  		}
    98  	}
    99  
   100  	if len(a) == 0 {
   101  		// No addresses to symbolize.
   102  		return nil
   103  	}
   104  
   105  	lines := make(map[uint64]profile.Line)
   106  	functions := make(map[string]*profile.Function)
   107  
   108  	b, err := syms(source, strings.Join(a, "+"))
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	buf := bytes.NewBuffer(b)
   114  	for {
   115  		l, err := buf.ReadString('\n')
   116  
   117  		if err != nil {
   118  			if err == io.EOF {
   119  				break
   120  			}
   121  			return err
   122  		}
   123  
   124  		if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
   125  			addr, err := strconv.ParseInt(symbol[1], 0, 64)
   126  			if err != nil {
   127  				return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
   128  			}
   129  			if addr < 0 {
   130  				return fmt.Errorf("unexpected negative adjusted address, source %s, offset %d", symbol[1], offset)
   131  			}
   132  			// Reapply offset expected by the profile.
   133  			addr -= offset
   134  
   135  			name := symbol[2]
   136  			fn := functions[name]
   137  			if fn == nil {
   138  				fn = &profile.Function{
   139  					ID:         uint64(len(p.Function) + 1),
   140  					Name:       name,
   141  					SystemName: name,
   142  				}
   143  				functions[name] = fn
   144  				p.Function = append(p.Function, fn)
   145  			}
   146  
   147  			lines[uint64(addr)] = profile.Line{Function: fn}
   148  		}
   149  	}
   150  
   151  	for _, l := range p.Location {
   152  		if l.Mapping != m {
   153  			continue
   154  		}
   155  		if line, ok := lines[l.Address]; ok {
   156  			l.Line = []profile.Line{line}
   157  		}
   158  	}
   159  
   160  	return nil
   161  }