github.com/akaros/go-akaros@v0.0.0-20181004170632-85005d477eab/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  	if len(prof.Mapping) == 0 {
    36  		return fmt.Errorf("no known mappings")
    37  	}
    38  
    39  	mt, err := newMapping(prof, obj, ui, force)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	defer mt.close()
    44  
    45  	functions := make(map[profile.Function]*profile.Function)
    46  	for _, l := range mt.prof.Location {
    47  		m := l.Mapping
    48  		segment := mt.segments[m]
    49  		if segment == nil {
    50  			// Nothing to do
    51  			continue
    52  		}
    53  
    54  		stack, err := segment.SourceLine(l.Address)
    55  		if err != nil || len(stack) == 0 {
    56  			// No answers from addr2line
    57  			continue
    58  		}
    59  
    60  		l.Line = make([]profile.Line, len(stack))
    61  		for i, frame := range stack {
    62  			if frame.Func != "" {
    63  				m.HasFunctions = true
    64  			}
    65  			if frame.File != "" {
    66  				m.HasFilenames = true
    67  			}
    68  			if frame.Line != 0 {
    69  				m.HasLineNumbers = true
    70  			}
    71  			f := &profile.Function{
    72  				Name:       frame.Func,
    73  				SystemName: frame.Func,
    74  				Filename:   frame.File,
    75  			}
    76  			if fp := functions[*f]; fp != nil {
    77  				f = fp
    78  			} else {
    79  				functions[*f] = f
    80  				f.ID = uint64(len(mt.prof.Function)) + 1
    81  				mt.prof.Function = append(mt.prof.Function, f)
    82  			}
    83  			l.Line[i] = profile.Line{
    84  				Function: f,
    85  				Line:     int64(frame.Line),
    86  			}
    87  		}
    88  
    89  		if len(stack) > 0 {
    90  			m.HasInlineFrames = true
    91  		}
    92  	}
    93  	return nil
    94  }
    95  
    96  // newMapping creates a mappingTable for a profile.
    97  func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
    98  	mt := &mappingTable{
    99  		prof:     prof,
   100  		segments: make(map[*profile.Mapping]plugin.ObjFile),
   101  	}
   102  
   103  	// Identify used mappings
   104  	mappings := make(map[*profile.Mapping]bool)
   105  	for _, l := range prof.Location {
   106  		mappings[l.Mapping] = true
   107  	}
   108  
   109  	for _, m := range prof.Mapping {
   110  		if !mappings[m] {
   111  			continue
   112  		}
   113  		// Do not attempt to re-symbolize a mapping that has already been symbolized.
   114  		if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
   115  			continue
   116  		}
   117  
   118  		f, err := locateFile(obj, m.File, m.BuildID, m.Start)
   119  		if err != nil {
   120  			ui.PrintErr("Local symbolization failed for ", filepath.Base(m.File), ": ", err)
   121  			// Move on to other mappings
   122  			continue
   123  		}
   124  
   125  		if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
   126  			// Build ID mismatch - ignore.
   127  			f.Close()
   128  			continue
   129  		}
   130  
   131  		mt.segments[m] = f
   132  	}
   133  
   134  	return mt, nil
   135  }
   136  
   137  // locateFile opens a local file for symbolization on the search path
   138  // at $PPROF_BINARY_PATH. Looks inside these directories for files
   139  // named $BUILDID/$BASENAME and $BASENAME (if build id is available).
   140  func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) {
   141  	// Construct search path to examine
   142  	searchPath := os.Getenv("PPROF_BINARY_PATH")
   143  	if searchPath == "" {
   144  		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
   145  		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
   146  	}
   147  
   148  	// Collect names to search: {buildid/basename, basename}
   149  	var fileNames []string
   150  	if baseName := filepath.Base(file); buildID != "" {
   151  		fileNames = []string{filepath.Join(buildID, baseName), baseName}
   152  	} else {
   153  		fileNames = []string{baseName}
   154  	}
   155  	for _, path := range filepath.SplitList(searchPath) {
   156  		for nameIndex, name := range fileNames {
   157  			file := filepath.Join(path, name)
   158  			if f, err := obj.Open(file, start); err == nil {
   159  				fileBuildID := f.BuildID()
   160  				if buildID == "" || buildID == fileBuildID {
   161  					return f, nil
   162  				}
   163  				f.Close()
   164  				if nameIndex == 0 {
   165  					// If this is the first name, the path includes the build id. Report inconsistency.
   166  					return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID)
   167  				}
   168  			}
   169  		}
   170  	}
   171  	// Try original file name
   172  	f, err := obj.Open(file, start)
   173  	if err == nil && buildID != "" {
   174  		if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID {
   175  			// Mismatched build IDs, ignore
   176  			f.Close()
   177  			return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID)
   178  		}
   179  	}
   180  	return f, err
   181  }
   182  
   183  // mappingTable contains the mechanisms for symbolization of a
   184  // profile.
   185  type mappingTable struct {
   186  	prof     *profile.Profile
   187  	segments map[*profile.Mapping]plugin.ObjFile
   188  }
   189  
   190  // Close releases any external processes being used for the mapping.
   191  func (mt *mappingTable) close() {
   192  	for _, segment := range mt.segments {
   193  		segment.Close()
   194  	}
   195  }