| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package symbolz symbolizes a profile using the output from the symbolz |
| // service. |
| package symbolz |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "net/url" |
| "regexp" |
| "strconv" |
| "strings" |
| |
| "internal/pprof/profile" |
| ) |
| |
| var ( |
| symbolzRE = regexp.MustCompile(`(0x[[:xdigit:]]+)\s+(.*)`) |
| ) |
| |
| // Symbolize symbolizes profile p by parsing data returned by a |
| // symbolz handler. syms receives the symbolz query (hex addresses |
| // separated by '+') and returns the symbolz output in a string. It |
| // symbolizes all locations based on their addresses, regardless of |
| // mapping. |
| func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error { |
| if source = symbolz(source, p); source == "" { |
| // If the source is not a recognizable URL, do nothing. |
| return nil |
| } |
| |
| // Construct query of addresses to symbolize. |
| var a []string |
| for _, l := range p.Location { |
| if l.Address != 0 && len(l.Line) == 0 { |
| a = append(a, fmt.Sprintf("%#x", l.Address)) |
| } |
| } |
| |
| if len(a) == 0 { |
| // No addresses to symbolize. |
| return nil |
| } |
| lines := make(map[uint64]profile.Line) |
| functions := make(map[string]*profile.Function) |
| if b, err := syms(source, strings.Join(a, "+")); err == nil { |
| buf := bytes.NewBuffer(b) |
| for { |
| l, err := buf.ReadString('\n') |
| |
| if err != nil { |
| if err == io.EOF { |
| break |
| } |
| return err |
| } |
| |
| if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 { |
| addr, err := strconv.ParseUint(symbol[1], 0, 64) |
| if err != nil { |
| return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err) |
| } |
| |
| name := symbol[2] |
| fn := functions[name] |
| if fn == nil { |
| fn = &profile.Function{ |
| ID: uint64(len(p.Function) + 1), |
| Name: name, |
| SystemName: name, |
| } |
| functions[name] = fn |
| p.Function = append(p.Function, fn) |
| } |
| |
| lines[addr] = profile.Line{Function: fn} |
| } |
| } |
| } |
| |
| for _, l := range p.Location { |
| if line, ok := lines[l.Address]; ok { |
| l.Line = []profile.Line{line} |
| if l.Mapping != nil { |
| l.Mapping.HasFunctions = true |
| } |
| } |
| } |
| |
| return nil |
| } |
| |
| // symbolz returns the corresponding symbolz source for a profile URL. |
| func symbolz(source string, p *profile.Profile) string { |
| if url, err := url.Parse(source); err == nil && url.Host != "" { |
| if last := strings.LastIndex(url.Path, "/"); last != -1 { |
| if strings.HasSuffix(url.Path[:last], "pprof") { |
| url.Path = url.Path[:last] + "/symbol" |
| } else { |
| url.Path = url.Path[:last] + "/symbolz" |
| } |
| return url.String() |
| } |
| } |
| |
| return "" |
| } |