// 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 graph.Nodes, src string, obj plugin.ObjTool) map[int]graph.Nodes { assembly := make(map[int]graph.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 }
// printAssembly prints an annotated assembly listing. func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error { o := rpt.options prof := rpt.prof g := rpt.newGraph(nil) // 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.Nodes, 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 := sns.Sum() // 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 }
// locateBinaries searches for binary files listed in the profile and, if found, // updates the profile accordingly. func locateBinaries(p *profile.Profile, s *source, obj plugin.ObjTool, ui plugin.UI) { // 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") } mapping: for i, m := range p.Mapping { var baseName string // Replace executable filename/buildID with the overrides from source. // Assumes the executable is the first Mapping entry. if i == 0 { if s.ExecName != "" { m.File = s.ExecName } if s.BuildID != "" { m.BuildID = s.BuildID } } if m.File != "" { baseName = filepath.Base(m.File) } for _, path := range filepath.SplitList(searchPath) { var fileNames []string if m.BuildID != "" { fileNames = []string{filepath.Join(path, m.BuildID, baseName)} if matches, err := filepath.Glob(filepath.Join(path, m.BuildID, "*")); err == nil { fileNames = append(fileNames, matches...) } } if baseName != "" { fileNames = append(fileNames, filepath.Join(path, baseName)) } for _, name := range fileNames { if f, err := obj.Open(name, m.Start, m.Limit, m.Offset); err == nil { defer f.Close() fileBuildID := f.BuildID() if m.BuildID != "" && m.BuildID != fileBuildID { ui.PrintErr("Ignoring local file " + name + ": build-id mismatch (" + m.BuildID + " != " + fileBuildID + ")") } else { m.File = name continue mapping } } } } } }
// symbolsFromBinaries examines the binaries listed on the profile // that have associated samples, and identifies symbols matching rx. func symbolsFromBinaries(prof *profile.Profile, g *graph.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.Nodes { if name := n.Info.PrintableName(); 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, m.Limit, m.Offset) 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 }
// newMapping creates a mappingTable for a profile. func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) { mt := &mappingTable{ prof: prof, segments: make(map[*profile.Mapping]plugin.ObjFile), } // Identify used mappings mappings := make(map[*profile.Mapping]bool) for _, l := range prof.Location { mappings[l.Mapping] = true } for _, m := range prof.Mapping { if !mappings[m] { continue } // Do not attempt to re-symbolize a mapping that has already been symbolized. if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) { continue } // Skip well-known system mappings name := filepath.Base(m.File) if name == "" || name == "[vdso]" || strings.HasPrefix(name, "linux-vdso") { continue } f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset) if err != nil { ui.PrintErr("Local symbolization failed for ", name, ": ", err) continue } if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID { ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch") f.Close() continue } mt.segments[m] = f } return mt, nil }
// newMapping creates a mappingTable for a profile. func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) { mt := &mappingTable{ prof: prof, segments: make(map[*profile.Mapping]plugin.ObjFile), } // Identify used mappings mappings := make(map[*profile.Mapping]bool) for _, l := range prof.Location { mappings[l.Mapping] = true } missingBinaries := false for midx, m := range prof.Mapping { if !mappings[m] { continue } // Do not attempt to re-symbolize a mapping that has already been symbolized. if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) { continue } if m.File == "" { if midx == 0 { ui.PrintErr("Main binary filename not available.\n" + "Try passing the path to the main binary before the profile.") continue } missingBinaries = true continue } // Skip well-known system mappings name := filepath.Base(m.File) if name == "[vdso]" || strings.HasPrefix(name, "linux-vdso") { continue } // Skip mappings pointing to a source URL if m.BuildID == "" { if u, err := url.Parse(m.File); err == nil && u.IsAbs() { continue } } f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset) if err != nil { ui.PrintErr("Local symbolization failed for ", name, ": ", err) continue } if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID { ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch") f.Close() continue } mt.segments[m] = f } if missingBinaries { ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.") } return mt, nil }