// annotateAssembly annotates a set of assembly instructions with a // set of samples. It returns a set of nodes to display. base is an // offset to adjust the sample addresses. func annotateAssembly(insns []plugin.Inst, samples graph.Nodes, base uint64) graph.Nodes { // Add end marker to simplify printing loop. insns = append(insns, plugin.Inst{^uint64(0), "", "", 0}) // Ensure samples are sorted by address. samples.Sort(graph.AddressOrder) var s int var asm graph.Nodes for ix, in := range insns[:len(insns)-1] { n := graph.Node{ Info: graph.NodeInfo{ Address: in.Addr, Name: in.Text, File: trimPath(in.File), Lineno: in.Line, }, } // Sum all the samples until the next instruction (to account // for samples attributed to the middle of an instruction). for next := insns[ix+1].Addr; s < len(samples) && samples[s].Info.Address-base < next; s++ { n.Flat += samples[s].Flat n.Cum += samples[s].Cum if samples[s].Info.File != "" { n.Info.File = trimPath(samples[s].Info.File) n.Info.Lineno = samples[s].Info.Lineno } } asm = append(asm, &n) } return asm }
// printSource prints an annotated source listing, include all // functions with samples that match the regexp rpt.options.symbol. // The sources are sorted by function name and then by filename to // eliminate potential nondeterminism. func printSource(w io.Writer, rpt *Report) error { o := rpt.options g := rpt.newGraph(nil) // Identify all the functions that match the regexp provided. // Group nodes for each matching function. var functions graph.Nodes functionNodes := make(map[string]graph.Nodes) for _, n := range g.Nodes { if !o.Symbol.MatchString(n.Info.Name) { continue } if functionNodes[n.Info.Name] == nil { functions = append(functions, n) } functionNodes[n.Info.Name] = append(functionNodes[n.Info.Name], n) } functions.Sort(graph.NameOrder) sourcePath := o.SourcePath if sourcePath == "" { wd, err := os.Getwd() if err != nil { return fmt.Errorf("Could not stat current dir: %v", err) } sourcePath = wd } fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total)) for _, fn := range functions { name := fn.Info.Name // Identify all the source files associated to this function. // Group nodes for each source file. var sourceFiles graph.Nodes fileNodes := make(map[string]graph.Nodes) for _, n := range functionNodes[name] { if n.Info.File == "" { continue } if fileNodes[n.Info.File] == nil { sourceFiles = append(sourceFiles, n) } fileNodes[n.Info.File] = append(fileNodes[n.Info.File], n) } if len(sourceFiles) == 0 { fmt.Fprintf(w, "No source information for %s\n", name) continue } sourceFiles.Sort(graph.FileOrder) // Print each file associated with this function. for _, fl := range sourceFiles { filename := fl.Info.File fns := fileNodes[filename] flatSum, cumSum := fns.Sum() fnodes, _, err := getSourceFromFile(filename, sourcePath, fns, 0, 0) fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, filename) fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n", rpt.formatValue(flatSum), rpt.formatValue(cumSum), percentage(cumSum, rpt.total)) if err != nil { fmt.Fprintf(w, " Error: %v\n", err) continue } for _, fn := range fnodes { fmt.Fprintf(w, "%10s %10s %6d:%s\n", valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt), fn.Info.Lineno, fn.Info.Name) } } } return nil }
// printWebSource prints an annotated source listing, include all // functions with samples that match the regexp rpt.options.symbol. func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error { o := rpt.options 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 } // Extract interesting symbols from binary files in the profile and // classify samples per symbol. symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj) symNodes := nodesPerSymbol(g.Nodes, symbols) // Sort symbols for printing. var syms objSymbols for s := range symNodes { syms = append(syms, s) } sort.Sort(syms) if len(syms) == 0 { return fmt.Errorf("no samples found on routines matching: %s", o.Symbol.String()) } printHeader(w, rpt) for _, s := range syms { name := s.sym.Name[0] // Identify sources associated to a symbol by examining // symbol samples. Classify samples per source file. var sourceFiles graph.Nodes fileNodes := make(map[string]graph.Nodes) for _, n := range symNodes[s] { if n.Info.File == "" { continue } if fileNodes[n.Info.File] == nil { sourceFiles = append(sourceFiles, n) } fileNodes[n.Info.File] = append(fileNodes[n.Info.File], n) } if len(sourceFiles) == 0 { fmt.Fprintf(w, "No source information for %s\n", name) continue } sourceFiles.Sort(graph.FileOrder) // Print each file associated with this function. for _, fl := range sourceFiles { filename := fl.Info.File fns := fileNodes[filename] asm := assemblyPerSourceLine(symbols, fns, filename, obj) start, end := sourceCoordinates(asm) fnodes, path, err := getFunctionSource(name, filename, fns, start, end) if err != nil { fnodes, path = getMissingFunctionSource(filename, asm, start, end) } flatSum, cumSum := fnodes.Sum() printFunctionHeader(w, name, path, flatSum, cumSum, rpt) for _, fn := range fnodes { printFunctionSourceLine(w, fn, asm[fn.Info.Lineno], rpt) } printFunctionClosing(w) } } printPageClosing(w) return nil }