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