func aggregate(prof *profile.Profile, v variables) error { var inlines, function, filename, linenumber, address bool switch { case v["addresses"].boolValue(): return nil case v["lines"].boolValue(): inlines = true function = true filename = true linenumber = true case v["files"].boolValue(): inlines = true filename = true case v["functions"].boolValue(): inlines = true function = true filename = true case v["noinlines"].boolValue(): function = true filename = true case v["addressnoinlines"].boolValue(): function = true filename = true linenumber = true address = true case v["functionnameonly"].boolValue(): inlines = true function = true default: return fmt.Errorf("unexpected granularity") } return prof.Aggregate(inlines, function, filename, linenumber, address) }
// applyFocus filters samples based on the focus/ignore options func applyFocus(prof *profile.Profile, v variables, ui plugin.UI) error { focus, err := compileRegexOption("focus", v["focus"].value, nil) ignore, err := compileRegexOption("ignore", v["ignore"].value, err) hide, err := compileRegexOption("hide", v["hide"].value, err) show, err := compileRegexOption("show", v["show"].value, err) tagfocus, err := compileTagFilter("tagfocus", v["tagfocus"].value, ui, err) tagignore, err := compileTagFilter("tagignore", v["tagignore"].value, ui, err) prunefrom, err := compileRegexOption("prune_from", v["prune_from"].value, err) if err != nil { return err } fm, im, hm, hnm := prof.FilterSamplesByName(focus, ignore, hide, show) warnNoMatches(focus == nil || fm, "Focus", ui) warnNoMatches(ignore == nil || im, "Ignore", ui) warnNoMatches(hide == nil || hm, "Hide", ui) warnNoMatches(show == nil || hnm, "Show", ui) tfm, tim := prof.FilterSamplesByTag(tagfocus, tagignore) warnNoMatches(tagfocus == nil || tfm, "TagFocus", ui) warnNoMatches(tagignore == nil || tim, "TagIgnore", ui) tagshow, err := compileRegexOption("tagshow", v["tagshow"].value, err) taghide, err := compileRegexOption("taghide", v["taghide"].value, err) tns, tnh := prof.FilterTagsByName(tagshow, taghide) warnNoMatches(tagshow == nil || tns, "TagShow", ui) warnNoMatches(tagignore == nil || tnh, "TagHide", ui) if prunefrom != nil { prof.PruneFrom(prunefrom) } return nil }
func printTopProto(w io.Writer, rpt *Report) error { p := rpt.prof o := rpt.options g, _, _, _ := rpt.newTrimmedGraph() rpt.selectOutputUnit(g) out := profile.Profile{ SampleType: []*profile.ValueType{ {Type: "cum", Unit: o.OutputUnit}, {Type: "flat", Unit: o.OutputUnit}, }, TimeNanos: p.TimeNanos, DurationNanos: p.DurationNanos, PeriodType: p.PeriodType, Period: p.Period, } var flatSum int64 for i, n := range g.Nodes { name, flat, cum := n.Info.PrintableName(), n.Flat, n.Cum flatSum += flat f := &profile.Function{ ID: uint64(i + 1), Name: name, SystemName: name, } l := &profile.Location{ ID: uint64(i + 1), Line: []profile.Line{ { Function: f, }, }, } fv, _ := measurement.Scale(flat, o.SampleUnit, o.OutputUnit) cv, _ := measurement.Scale(cum, o.SampleUnit, o.OutputUnit) s := &profile.Sample{ Location: []*profile.Location{l}, Value: []int64{int64(cv), int64(fv)}, } out.Function = append(out.Function, f) out.Location = append(out.Location, l) out.Sample = append(out.Sample, s) } return out.Write(w) }
func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error { p = p.Copy() // Prevent modification to the incoming profile. var w io.Writer switch output := vars["output"].value; output { case "": w = os.Stdout default: o.UI.PrintErr("Generating report in ", output) outputFile, err := o.Writer.Open(output) if err != nil { return err } defer outputFile.Close() w = outputFile } vars = applyCommandOverrides(cmd, vars) // Delay focus after configuring report to get percentages on all samples. relative := vars["relative_percentages"].boolValue() if relative { if err := applyFocus(p, vars, o.UI); err != nil { return err } } ropt, err := reportOptions(p, vars) if err != nil { return err } c := pprofCommands[cmd[0]] if c == nil { panic("unexpected nil command") } ropt.OutputFormat = c.format post := c.postProcess if len(cmd) == 2 { s, err := regexp.Compile(cmd[1]) if err != nil { return fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err) } ropt.Symbol = s } rpt := report.New(p, ropt) if !relative { if err := applyFocus(p, vars, o.UI); err != nil { return err } } if err := aggregate(p, vars); err != nil { return err } if post == nil { return report.Generate(w, rpt, o.Obj) } // Capture output into buffer and send to postprocessing command. buf := &bytes.Buffer{} if err := report.Generate(buf, rpt, o.Obj); err != nil { return err } return post(buf.Bytes(), w, o.UI) }
// symbolizeMapping symbolizes locations belonging to a Mapping by querying // a symbolz handler. An offset is applied to all addresses to take care of // normalization occured for merged Mappings. func symbolizeMapping(source string, offset int64, syms func(string, string) ([]byte, error), m *profile.Mapping, p *profile.Profile) error { // Construct query of addresses to symbolize. var a []string for _, l := range p.Location { if l.Mapping == m && l.Address != 0 && len(l.Line) == 0 { // Compensate for normalization. addr := int64(l.Address) + offset if addr < 0 { return fmt.Errorf("unexpected negative adjusted address, mapping %v source %d, offset %d", l.Mapping, l.Address, offset) } a = append(a, fmt.Sprintf("%#x", addr)) } } if len(a) == 0 { // No addresses to symbolize. return nil } lines := make(map[uint64]profile.Line) functions := make(map[string]*profile.Function) b, err := syms(source, strings.Join(a, "+")) if err != nil { return err } buf := bytes.NewBuffer(b) for { l, err := buf.ReadString('\n') if err != nil { if err == io.EOF { break } return err } if symbol := symbolzRE.FindStringSubmatch(l); len(symbol) == 3 { addr, err := strconv.ParseInt(symbol[1], 0, 64) if err != nil { return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err) } if addr < 0 { return fmt.Errorf("unexpected negative adjusted address, source %s, offset %d", symbol[1], offset) } // Reapply offset expected by the profile. addr -= offset name := symbol[2] fn := functions[name] if fn == nil { fn = &profile.Function{ ID: uint64(len(p.Function) + 1), Name: name, SystemName: name, } functions[name] = fn p.Function = append(p.Function, fn) } lines[uint64(addr)] = profile.Line{Function: fn} } } for _, l := range p.Location { if l.Mapping != m { continue } if line, ok := lines[l.Address]; ok { l.Line = []profile.Line{line} } } return nil }
func localMock(mode string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error { p.Comments = append(p.Comments, "local="+mode) return nil }
func symbolzMock(sources plugin.MappingSources, syms func(string, string) ([]byte, error), p *profile.Profile, ui plugin.UI) error { p.Comments = append(p.Comments, "symbolz") return nil }