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 "cmd/pprof/internal/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