diff options
Diffstat (limited to 'src/cmd/pprof/internal/symbolz/symbolz.go')
-rw-r--r-- | src/cmd/pprof/internal/symbolz/symbolz.go | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/src/cmd/pprof/internal/symbolz/symbolz.go b/src/cmd/pprof/internal/symbolz/symbolz.go new file mode 100644 index 000000000..c81e52220 --- /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 "" +} |