github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/cmd/pprof/internal/symbolizer/symbolizer.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package symbolizer provides a routine to populate a profile with 6 // symbol, file and line number information. It relies on the 7 // addr2liner and demangler packages to do the actual work. 8 package symbolizer 9 10 import ( 11 "fmt" 12 "os" 13 "path/filepath" 14 "strings" 15 16 "cmd/pprof/internal/plugin" 17 "cmd/pprof/internal/profile" 18 ) 19 20 // Symbolize adds symbol and line number information to all locations 21 // in a profile. mode enables some options to control 22 // symbolization. Currently only recognizes "force", which causes it 23 // to overwrite any existing data. 24 func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { 25 force := false 26 // Disable some mechanisms based on mode string. 27 for _, o := range strings.Split(strings.ToLower(mode), ":") { 28 switch o { 29 case "force": 30 force = true 31 default: 32 } 33 } 34 35 mt, err := newMapping(prof, obj, ui, force) 36 if err != nil { 37 return err 38 } 39 defer mt.close() 40 41 functions := make(map[profile.Function]*profile.Function) 42 for _, l := range mt.prof.Location { 43 m := l.Mapping 44 segment := mt.segments[m] 45 if segment == nil { 46 // Nothing to do 47 continue 48 } 49 50 stack, err := segment.SourceLine(l.Address) 51 if err != nil || len(stack) == 0 { 52 // No answers from addr2line 53 continue 54 } 55 56 l.Line = make([]profile.Line, len(stack)) 57 for i, frame := range stack { 58 if frame.Func != "" { 59 m.HasFunctions = true 60 } 61 if frame.File != "" { 62 m.HasFilenames = true 63 } 64 if frame.Line != 0 { 65 m.HasLineNumbers = true 66 } 67 f := &profile.Function{ 68 Name: frame.Func, 69 SystemName: frame.Func, 70 Filename: frame.File, 71 } 72 if fp := functions[*f]; fp != nil { 73 f = fp 74 } else { 75 functions[*f] = f 76 f.ID = uint64(len(mt.prof.Function)) + 1 77 mt.prof.Function = append(mt.prof.Function, f) 78 } 79 l.Line[i] = profile.Line{ 80 Function: f, 81 Line: int64(frame.Line), 82 } 83 } 84 85 if len(stack) > 0 { 86 m.HasInlineFrames = true 87 } 88 } 89 return nil 90 } 91 92 // newMapping creates a mappingTable for a profile. 93 func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) { 94 mt := &mappingTable{ 95 prof: prof, 96 segments: make(map[*profile.Mapping]plugin.ObjFile), 97 } 98 99 // Identify used mappings 100 mappings := make(map[*profile.Mapping]bool) 101 for _, l := range prof.Location { 102 mappings[l.Mapping] = true 103 } 104 105 for _, m := range prof.Mapping { 106 if !mappings[m] { 107 continue 108 } 109 // Do not attempt to re-symbolize a mapping that has already been symbolized. 110 if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) { 111 continue 112 } 113 114 f, err := locateFile(obj, m.File, m.BuildID, m.Start) 115 if err != nil { 116 ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err) 117 // Move on to other mappings 118 continue 119 } 120 121 if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID { 122 // Build ID mismatch - ignore. 123 f.Close() 124 continue 125 } 126 127 mt.segments[m] = f 128 } 129 130 return mt, nil 131 } 132 133 // locateFile opens a local file for symbolization on the search path 134 // at $PPROF_BINARY_PATH. Looks inside these directories for files 135 // named $BUILDID/$BASENAME and $BASENAME (if build id is available). 136 func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) { 137 // Construct search path to examine 138 searchPath := os.Getenv("PPROF_BINARY_PATH") 139 if searchPath == "" { 140 // Use $HOME/pprof/binaries as default directory for local symbolization binaries 141 searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries") 142 } 143 144 // Collect names to search: {buildid/basename, basename} 145 var fileNames []string 146 if baseName := filepath.Base(file); buildID != "" { 147 fileNames = []string{filepath.Join(buildID, baseName), baseName} 148 } else { 149 fileNames = []string{baseName} 150 } 151 for _, path := range filepath.SplitList(searchPath) { 152 for nameIndex, name := range fileNames { 153 file := filepath.Join(path, name) 154 if f, err := obj.Open(file, start); err == nil { 155 fileBuildID := f.BuildID() 156 if buildID == "" || buildID == fileBuildID { 157 return f, nil 158 } 159 f.Close() 160 if nameIndex == 0 { 161 // If this is the first name, the path includes the build id. Report inconsistency. 162 return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID) 163 } 164 } 165 } 166 } 167 // Try original file name 168 f, err := obj.Open(file, start) 169 if err == nil && buildID != "" { 170 if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID { 171 // Mismatched build IDs, ignore 172 f.Close() 173 return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID) 174 } 175 } 176 return f, err 177 } 178 179 // mappingTable contains the mechanisms for symbolization of a 180 // profile. 181 type mappingTable struct { 182 prof *profile.Profile 183 segments map[*profile.Mapping]plugin.ObjFile 184 } 185 186 // Close releases any external processes being used for the mapping. 187 func (mt *mappingTable) close() { 188 for _, segment := range mt.segments { 189 segment.Close() 190 } 191 }