Home | History | Annotate | Download | only in symbolz
      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package symbolz symbolizes a profile using the output from the symbolz
      6 // service.
      7 package symbolz
      8 
      9 import (
     10 	"bytes"
     11 	"fmt"
     12 	"io"
     13 	"net/url"
     14 	"regexp"
     15 	"strconv"
     16 	"strings"
     17 
     18 	"internal/pprof/profile"
     19 )
     20 
     21 var (
     22 	symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`)
     23 )
     24 
     25 // Symbolize symbolizes profile p by parsing data returned by a
     26 // symbolz handler. syms receives the symbolz query (hex addresses
     27 // separated by '+') and returns the symbolz output in a string. It
     28 // symbolizes all locations based on their addresses, regardless of
     29 // mapping.
     30 func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error {
     31 	if source = symbolz(source, p); source == "" {
     32 		// If the source is not a recognizable URL, do nothing.
     33 		return nil
     34 	}
     35 
     36 	// Construct query of addresses to symbolize.
     37 	var a []string
     38 	for _, l := range p.Location {
     39 		if l.Address != 0 && len(l.Line) == 0 {
     40 			a = append(a, fmt.Sprintf("%#x", l.Address))
     41 		}
     42 	}
     43 
     44 	if len(a) == 0 {
     45 		// No addresses to symbolize.
     46 		return nil
     47 	}
     48 	lines := make(map[uint64]profile.Line)
     49 	functions := make(map[string]*profile.Function)
     50 	if b, err := syms(source, strings.Join(a, "+")); err == nil {
     51 		buf := bytes.NewBuffer(b)
     52 		for {
     53 			l, err := buf.ReadString('\n')
     54 
     55 			if err != nil {
     56 				if err == io.EOF {
     57 					break
     58 				}
     59 				return err
     60 			}
     61 
     62 			if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 {
     63 				addr, err := strconv.ParseUint(symbol[1], 0, 64)
     64 				if err != nil {
     65 					return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err)
     66 				}
     67 
     68 				name := symbol[2]
     69 				fn := functions[name]
     70 				if fn == nil {
     71 					fn = &profile.Function{
     72 						ID:         uint64(len(p.Function) + 1),
     73 						Name:       name,
     74 						SystemName: name,
     75 					}
     76 					functions[name] = fn
     77 					p.Function = append(p.Function, fn)
     78 				}
     79 
     80 				lines[addr] = profile.Line{Function: fn}
     81 			}
     82 		}
     83 	}
     84 
     85 	for _, l := range p.Location {
     86 		if line, ok := lines[l.Address]; ok {
     87 			l.Line = []profile.Line{line}
     88 			if l.Mapping != nil {
     89 				l.Mapping.HasFunctions = true
     90 			}
     91 		}
     92 	}
     93 
     94 	return nil
     95 }
     96 
     97 // symbolz returns the corresponding symbolz source for a profile URL.
     98 func symbolz(source string, p *profile.Profile) string {
     99 	if url, err := url.Parse(source); err == nil && url.Host != "" {
    100 		if last := strings.LastIndex(url.Path, "/"); last != -1 {
    101 			if strings.HasSuffix(url.Path[:last], "pprof") {
    102 				url.Path = url.Path[:last] + "/symbol"
    103 			} else {
    104 				url.Path = url.Path[:last] + "/symbolz"
    105 			}
    106 			return url.String()
    107 		}
    108 	}
    109 
    110 	return ""
    111 }
    112