|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- // Copyright 2017 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package profiler
-
- import (
- "bytes"
- "regexp"
- "runtime"
- "strings"
-
- "github.com/google/pprof/profile"
- )
-
- var shouldAssumeSymbolized = isSymbolizedGoVersion(runtime.Version())
-
- type function interface {
- Name() string
- FileLine(pc uintptr) (string, int)
- }
-
- // funcForPC is a wrapper for runtime.FuncForPC. Defined as var for testing.
- var funcForPC = func(pc uintptr) function {
- if f := runtime.FuncForPC(pc); f != nil {
- return f
- }
- return nil
- }
-
- // parseAndSymbolize parses a profile from a buffer, symbolizes it
- // if it's not yet symbolized, and writes the profile back as a
- // gzip-compressed marshaled protobuf.
- func parseAndSymbolize(data *bytes.Buffer) error {
- p, err := profile.ParseData(data.Bytes())
- if err != nil {
- return err
- }
-
- // Do nothing if the profile is already symbolized.
- if symbolized(p) {
- return nil
- }
- // Clear the profile functions to avoid creating duplicates.
- p.Function = nil
- symbolize(p)
- data.Reset()
- return p.Write(data)
- }
-
- // isSymbolizedGoVersion returns true if Go version equals to or is
- // higher than Go 1.9. Starting Go 1.9 the profiles are symbolized
- // by runtime/pprof.
- func isSymbolizedGoVersion(goVersion string) bool {
- r, err := regexp.Compile(`go(1\.9|1\.[1-9][0-9]|[2-9]).*`)
- if err == nil && r.MatchString(goVersion) {
- return true
- }
- return false
- }
-
- // symbolized checks if all locations have symbolized function
- // information.
- func symbolized(p *profile.Profile) bool {
- for _, l := range p.Location {
- if len(l.Line) == 0 || l.Line[0].Function == nil {
- return false
- }
- }
- return true
- }
-
- func symbolize(p *profile.Profile) {
- fns := profileFunctionMap{}
- for _, l := range p.Location {
- pc := uintptr(l.Address)
- f := funcForPC(pc)
- if f == nil {
- continue
- }
- file, lineno := f.FileLine(pc)
- l.Line = []profile.Line{
- {
- Function: fns.findOrAddFunction(f.Name(), file, p),
- Line: int64(lineno),
- },
- }
- }
- // Trim runtime functions. Always hide runtime.goexit. Other runtime
- // functions are only hidden for heap profile when they appear at the beginning.
- isHeapProfile := p.PeriodType != nil && p.PeriodType.Type == "space"
- for _, s := range p.Sample {
- show := !isHeapProfile
- var i int
- for _, l := range s.Location {
- if len(l.Line) > 0 && l.Line[0].Function != nil {
- name := l.Line[0].Function.Name
- if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") {
- continue
- }
- }
- show = true
- s.Location[i] = l
- i++
- }
- // If all locations of a sample are trimmed, keep the root location.
- if i == 0 && len(s.Location) > 0 {
- s.Location[0] = s.Location[len(s.Location)-1]
- i = 1
- }
- s.Location = s.Location[:i]
- }
- }
-
- type profileFunctionMap map[profile.Function]*profile.Function
-
- func (fns profileFunctionMap) findOrAddFunction(name, filename string, p *profile.Profile) *profile.Function {
- f := profile.Function{
- Name: name,
- SystemName: name,
- Filename: filename,
- }
- if fp := fns[f]; fp != nil {
- return fp
- }
- fp := new(profile.Function)
- fns[f] = fp
-
- *fp = f
- fp.ID = uint64(len(p.Function) + 1)
- p.Function = append(p.Function, fp)
- return fp
- }
|