// preprocess does filtering and aggregation of a profile based on the // requested options. func preprocess(prof *profile.Profile, ui plugin.UI, f *flags) error { if *f.flagFocus != "" || *f.flagIgnore != "" || *f.flagHide != "" { focus, ignore, hide, err := compileFocusIgnore(*f.flagFocus, *f.flagIgnore, *f.flagHide) if err != nil { return err } fm, im, hm := prof.FilterSamplesByName(focus, ignore, hide) warnNoMatches(fm, *f.flagFocus, "Focus", ui) warnNoMatches(im, *f.flagIgnore, "Ignore", ui) warnNoMatches(hm, *f.flagHide, "Hide", ui) } if *f.flagTagFocus != "" || *f.flagTagIgnore != "" { focus, err := compileTagFilter(*f.flagTagFocus, ui) if err != nil { return err } ignore, err := compileTagFilter(*f.flagTagIgnore, ui) if err != nil { return err } fm, im := prof.FilterSamplesByTag(focus, ignore) warnNoMatches(fm, *f.flagTagFocus, "TagFocus", ui) warnNoMatches(im, *f.flagTagIgnore, "TagIgnore", ui) } return aggregate(prof, f) }
func generateReport(p *profile.Profile, cmd []string, obj plugin.ObjTool, ui plugin.UI, f *flags) error { prof := p.Copy() cf, err := cmdFlags(prof, cmd, ui, f) if err != nil { return err } return generate(true, prof, obj, ui, cf) }
func generate(interactive bool, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error { o, postProcess, err := parseOptions(f) if err != nil { return err } var w io.Writer if *f.flagOutput == "" { w = os.Stdout } else { ui.PrintErr("Generating report in ", *f.flagOutput) outputFile, err := os.Create(*f.flagOutput) if err != nil { return err } defer outputFile.Close() w = outputFile } if prof.Empty() { return fmt.Errorf("profile is empty") } value, stype, unit := sampleFormat(prof, f) o.SampleType = stype rpt := report.New(prof, *o, value, unit) // Do not apply filters if we're just generating a proto, so we // still have all the data. if o.OutputFormat != report.Proto { // Delay applying focus/ignore until after creating the report so // the report reflects the total number of samples. if err := preprocess(prof, ui, f); err != nil { return err } } if postProcess == nil { return report.Generate(w, rpt, obj) } var dot bytes.Buffer if err = report.Generate(&dot, rpt, obj); err != nil { return err } return postProcess(&dot, w, ui) }
func aggregate(prof *profile.Profile, f *flags) error { switch { case f.isFormat("proto"), f.isFormat("raw"): // No aggregation for raw profiles. case f.isFormat("callgrind"): // Aggregate to file/line for callgrind. fallthrough case *f.flagLines: return prof.Aggregate(true, true, true, true, false) case *f.flagFiles: return prof.Aggregate(true, false, true, false, false) case *f.flagFunctions: return prof.Aggregate(true, true, false, false, false) case f.isFormat("weblist"), f.isFormat("disasm"): return prof.Aggregate(false, true, true, true, true) } return nil }
// Symbolize symbolizes profile p by parsing data returned by a // symbolz handler. syms receives the symbolz query (hex addresses // separated by '+') and returns the symbolz output in a string. It // symbolizes all locations based on their addresses, regardless of // mapping. func Symbolize(source string, syms func(string, string) ([]byte, error), p *profile.Profile) error { if source = symbolz(source, p); source == "" { // If the source is not a recognizable URL, do nothing. return nil } // Construct query of addresses to symbolize. var a []string for _, l := range p.Location { if l.Address != 0 && len(l.Line) == 0 { a = append(a, fmt.Sprintf("%#x", l.Address)) } } if len(a) == 0 { // No addresses to symbolize. return nil } lines := make(map[uint64]profile.Line) functions := make(map[string]*profile.Function) if b, err := syms(source, strings.Join(a, "+")); err == nil { 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.ParseUint(symbol[1], 0, 64) if err != nil { return fmt.Errorf("unexpected parse failure %s: %v", symbol[1], err) } 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[addr] = profile.Line{Function: fn} } } } for _, l := range p.Location { if line, ok := lines[l.Address]; ok { l.Line = []profile.Line{line} if l.Mapping != nil { l.Mapping.HasFunctions = true } } } return nil }