// New builds a new report indexing the sample values interpreting the // samples with the provided function. func New(prof *profile.Profile, o *Options) *Report { format := func(v int64) string { if r := o.Ratio; r > 0 && r != 1 { fv := float64(v) * r v = int64(fv) } return measurement.ScaledLabel(v, o.SampleUnit, o.OutputUnit) } return &Report{prof, computeTotal(prof, o.SampleValue, !o.PositivePercentages), o, format} }
// printDOT prints an annotated callgraph in DOT format. func printDOT(w io.Writer, rpt *Report) error { g, origCount, droppedNodes, droppedEdges := rpt.newTrimmedGraph() rpt.selectOutputUnit(g) labels := reportLabels(rpt, g, origCount, droppedNodes, droppedEdges, true) o := rpt.options formatTag := func(v int64, key string) string { return measurement.ScaledLabel(v, key, o.OutputUnit) } c := &graph.DotConfig{ Title: rpt.options.Title, Labels: labels, FormatValue: rpt.formatValue, FormatTag: formatTag, Total: rpt.total, } graph.ComposeDot(w, g, &graph.DotAttributes{}, c) return nil }
// newGraph creates a new graph for this report. If nodes is non-nil, // only nodes whose info matches are included. Otherwise, all nodes // are included, without trimming. func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph { o := rpt.options // Clean up file paths using heuristics. prof := rpt.prof for _, f := range prof.Function { f.Filename = trimPath(f.Filename) } // Remove numeric tags not recognized by pprof. for _, s := range prof.Sample { numLabels := make(map[string][]int64, len(s.NumLabel)) for k, v := range s.NumLabel { if k == "bytes" { numLabels[k] = append(numLabels[k], v...) } } s.NumLabel = numLabels } formatTag := func(v int64, key string) string { return measurement.ScaledLabel(v, key, o.OutputUnit) } gopt := &graph.Options{ SampleValue: o.SampleValue, SampleMeanDivisor: o.SampleMeanDivisor, FormatTag: formatTag, CallTree: o.CallTree && (o.OutputFormat == Dot || o.OutputFormat == Callgrind), DropNegative: o.DropNegative, KeptNodes: nodes, } // Only keep binary names for disassembly-based reports, otherwise // remove it to allow merging of functions across binaries. switch o.OutputFormat { case Raw, List, WebList, Dis, Callgrind: gopt.ObjNames = true } return graph.New(rpt.prof, gopt) }
// printTags collects all tags referenced in the profile and prints // them in a sorted table. func printTags(w io.Writer, rpt *Report) error { p := rpt.prof o := rpt.options formatTag := func(v int64, key string) string { return measurement.ScaledLabel(v, key, o.OutputUnit) } // Hashtable to keep accumulate tags as key,value,count. tagMap := make(map[string]map[string]int64) for _, s := range p.Sample { for key, vals := range s.Label { for _, val := range vals { if valueMap, ok := tagMap[key]; ok { valueMap[val] = valueMap[val] + s.Value[0] continue } valueMap := make(map[string]int64) valueMap[val] = s.Value[0] tagMap[key] = valueMap } } for key, vals := range s.NumLabel { for _, nval := range vals { val := formatTag(nval, key) if valueMap, ok := tagMap[key]; ok { valueMap[val] = valueMap[val] + s.Value[0] continue } valueMap := make(map[string]int64) valueMap[val] = s.Value[0] tagMap[key] = valueMap } } } tagKeys := make([]*graph.Tag, 0, len(tagMap)) for key := range tagMap { tagKeys = append(tagKeys, &graph.Tag{Name: key}) } for _, tagKey := range graph.SortTags(tagKeys, true) { var total int64 key := tagKey.Name tags := make([]*graph.Tag, 0, len(tagMap[key])) for t, c := range tagMap[key] { total += c tags = append(tags, &graph.Tag{Name: t, Flat: c}) } fmt.Fprintf(w, "%s: Total %d\n", key, total) for _, t := range graph.SortTags(tags, true) { if total > 0 { fmt.Fprintf(w, " %8d (%s): %s\n", t.FlatValue(), percentage(t.FlatValue(), total), t.Name) } else { fmt.Fprintf(w, " %8d: %s\n", t.FlatValue(), t.Name) } } fmt.Fprintln(w) } return nil }