Index: src/cmd/pprof/internal/symbolz/symbolz.go |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/cmd/pprof/internal/symbolz/symbolz.go |
@@ -0,0 +1,111 @@ |
+// 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" |
+ |
+ "cmd/pprof/internal/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 "" |
+} |