Esempio n. 1
0
// assemblyPerSourceLine disassembles the binary containing a symbol
// and classifies the assembly instructions according to its
// corresponding source line, annotating them with a set of samples.
func assemblyPerSourceLine(objSyms []*objSymbol, rs nodes, src string, obj plugin.ObjTool) map[int]nodes {
	assembly := make(map[int]nodes)
	// Identify symbol to use for this collection of samples.
	o := findMatchingSymbol(objSyms, rs)
	if o == nil {
		return assembly
	}

	// Extract assembly for matched symbol
	insns, err := obj.Disasm(o.sym.File, o.sym.Start, o.sym.End)
	if err != nil {
		return assembly
	}

	srcBase := filepath.Base(src)
	anodes := annotateAssembly(insns, rs, o.base)
	var lineno = 0
	for _, an := range anodes {
		if filepath.Base(an.info.file) == srcBase {
			lineno = an.info.lineno
		}
		if lineno != 0 {
			assembly[lineno] = append(assembly[lineno], an)
		}
	}

	return assembly
}
Esempio n. 2
0
// printAssembly prints an annotated assembly listing.
func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
	g, err := newGraph(rpt)
	if err != nil {
		return err
	}

	o := rpt.options
	prof := rpt.prof

	// If the regexp source can be parsed as an address, also match
	// functions that land on that address.
	var address *uint64
	if hex, err := strconv.ParseUint(o.Symbol.String(), 0, 64); err == nil {
		address = &hex
	}

	fmt.Fprintln(w, "Total:", rpt.formatValue(rpt.total))
	symbols := symbolsFromBinaries(prof, g, o.Symbol, address, obj)
	symNodes := nodesPerSymbol(g.ns, symbols)
	// Sort function names for printing.
	var syms objSymbols
	for s := range symNodes {
		syms = append(syms, s)
	}
	sort.Sort(syms)

	// Correlate the symbols from the binary with the profile samples.
	for _, s := range syms {
		sns := symNodes[s]

		// Gather samples for this symbol.
		flatSum, cumSum := sumNodes(sns)

		// Get the function assembly.
		insns, err := obj.Disasm(s.sym.File, s.sym.Start, s.sym.End)
		if err != nil {
			return err
		}

		ns := annotateAssembly(insns, sns, s.base)

		fmt.Fprintf(w, "ROUTINE ======================== %s\n", s.sym.Name[0])
		for _, name := range s.sym.Name[1:] {
			fmt.Fprintf(w, "    AKA ======================== %s\n", name)
		}
		fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
			rpt.formatValue(flatSum), rpt.formatValue(cumSum),
			percentage(cumSum, rpt.total))

		for _, n := range ns {
			fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.flat, rpt), valueOrDot(n.cum, rpt), n.info.address, n.info.name)
		}
	}
	return nil
}
Esempio n. 3
0
// symbolsFromBinaries examines the binaries listed on the profile
// that have associated samples, and identifies symbols matching rx.
func symbolsFromBinaries(prof *profile.Profile, g graph, rx *regexp.Regexp, address *uint64, obj plugin.ObjTool) []*objSymbol {
	hasSamples := make(map[string]bool)
	// Only examine mappings that have samples that match the
	// regexp. This is an optimization to speed up pprof.
	for _, n := range g.ns {
		if name := n.info.prettyName(); rx.MatchString(name) && n.info.objfile != "" {
			hasSamples[n.info.objfile] = true
		}
	}

	// Walk all mappings looking for matching functions with samples.
	var objSyms []*objSymbol
	for _, m := range prof.Mapping {
		if !hasSamples[filepath.Base(m.File)] {
			if address == nil || !(m.Start <= *address && *address <= m.Limit) {
				continue
			}
		}

		f, err := obj.Open(m.File, m.Start)
		if err != nil {
			fmt.Printf("%v\n", err)
			continue
		}

		// Find symbols in this binary matching the user regexp.
		var addr uint64
		if address != nil {
			addr = *address
		}
		msyms, err := f.Symbols(rx, addr)
		base := f.Base()
		f.Close()
		if err != nil {
			continue
		}
		for _, ms := range msyms {
			objSyms = append(objSyms,
				&objSymbol{
					sym:  ms,
					base: base,
				},
			)
		}
	}

	return objSyms
}
Esempio n. 4
0
// locateFile opens a local file for symbolization on the search path
// at $PPROF_BINARY_PATH. Looks inside these directories for files
// named $BUILDID/$BASENAME and $BASENAME (if build id is available).
func locateFile(obj plugin.ObjTool, file, buildID string, start uint64) (plugin.ObjFile, error) {
	// Construct search path to examine
	searchPath := os.Getenv("PPROF_BINARY_PATH")
	if searchPath == "" {
		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
	}

	// Collect names to search: {buildid/basename, basename}
	var fileNames []string
	if baseName := filepath.Base(file); buildID != "" {
		fileNames = []string{filepath.Join(buildID, baseName), baseName}
	} else {
		fileNames = []string{baseName}
	}
	for _, path := range filepath.SplitList(searchPath) {
		for nameIndex, name := range fileNames {
			file := filepath.Join(path, name)
			if f, err := obj.Open(file, start); err == nil {
				fileBuildID := f.BuildID()
				if buildID == "" || buildID == fileBuildID {
					return f, nil
				}
				f.Close()
				if nameIndex == 0 {
					// If this is the first name, the path includes the build id. Report inconsistency.
					return nil, fmt.Errorf("found file %s with inconsistent build id %s", file, fileBuildID)
				}
			}
		}
	}
	// Try original file name
	f, err := obj.Open(file, start)
	if err == nil && buildID != "" {
		if fileBuildID := f.BuildID(); fileBuildID != "" && fileBuildID != buildID {
			// Mismatched build IDs, ignore
			f.Close()
			return nil, fmt.Errorf("mismatched build ids %s != %s", fileBuildID, buildID)
		}
	}
	return f, err
}
Esempio n. 5
0
// PProf acquires a profile, and symbolizes it using a profile
// manager. Then it generates a report formatted according to the
// options selected through the flags package.
func PProf(flagset plugin.FlagSet, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, overrides commands.Commands) error {
	// Remove any temporary files created during pprof processing.
	defer tempfile.Cleanup()

	f, err := getFlags(flagset, overrides, ui)
	if err != nil {
		return err
	}

	obj.SetConfig(*f.flagTools)

	sources := f.profileSource
	if len(sources) > 1 {
		source := sources[0]
		// If the first argument is a supported object file, treat as executable.
		if file, err := obj.Open(source, 0); err == nil {
			file.Close()
			f.profileExecName = source
			sources = sources[1:]
		} else if *f.flagBuildID == "" && isBuildID(source) {
			f.flagBuildID = &source
			sources = sources[1:]
		}
	}

	// errMu protects concurrent accesses to errset and err. errset is set if an
	// error is encountered by one of the goroutines grabbing a profile.
	errMu, errset := sync.Mutex{}, false

	// Fetch profiles.
	wg := sync.WaitGroup{}
	profs := make([]*profile.Profile, len(sources))
	for i, source := range sources {
		wg.Add(1)
		go func(i int, src string) {
			defer wg.Done()
			p, grabErr := grabProfile(src, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f)
			if grabErr != nil {
				errMu.Lock()
				defer errMu.Unlock()
				errset, err = true, grabErr
				return
			}
			profs[i] = p
		}(i, source)
	}
	wg.Wait()
	if errset {
		return err
	}

	// Merge profiles.
	prof := profs[0]
	for _, p := range profs[1:] {
		if err = prof.Merge(p, 1); err != nil {
			return err
		}
	}

	if *f.flagBase != "" {
		// Fetch base profile and subtract from current profile.
		base, err := grabProfile(*f.flagBase, f.profileExecName, *f.flagBuildID, fetch, sym, obj, ui, f)
		if err != nil {
			return err
		}

		if err = prof.Merge(base, -1); err != nil {
			return err
		}
	}

	if err := processFlags(prof, ui, f); err != nil {
		return err
	}

	if !*f.flagRuntime {
		prof.RemoveUninteresting()
	}

	if *f.flagInteractive {
		return interactive(prof, obj, ui, f)
	}

	return generate(false, prof, obj, ui, f)
}