1  
     2  
     3  
     4  
     5  
     6  
     7  
     8  
     9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    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  
    38  
    39  
    40  
    41  
    42  
    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  			
    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  
    68  
    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  
    86  func symbolz(source string) string {
    87  	if url, err := url.Parse(source); err == nil && url.Host != "" {
    88  		
    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  
   102  
   103  
   104  func symbolizeMapping(source string, offset int64, syms func(string, string) ([]byte, error), m *profile.Mapping, p *profile.Profile) error {
   105  	
   106  	var a []string
   107  	for _, l := range p.Location {
   108  		if l.Mapping == m && l.Address != 0 && len(l.Line) == 0 {
   109  			
   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  		
   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  			
   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  
   182  
   183  
   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