From 54536e0e7be1890937ed0b596bbbbf86ddf37316 Mon Sep 17 00:00:00 2001 From: Marco Concetto Rudilosso Date: Fri, 1 Dec 2017 22:14:58 +0000 Subject: [PATCH] Actually calculating percentiles. Missing tests. --- .../testdata/pprof.cpu.flat.addresses.weblist | 38 ++++---- internal/report/source.go | 90 ++++++++++++++----- 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/internal/driver/testdata/pprof.cpu.flat.addresses.weblist b/internal/driver/testdata/pprof.cpu.flat.addresses.weblist index 0d2a1450..4146534f 100644 --- a/internal/driver/testdata/pprof.cpu.flat.addresses.weblist +++ b/internal/driver/testdata/pprof.cpu.flat.addresses.weblist @@ -84,36 +84,36 @@ Duration: 10s, Total samples = 1.12s (11.20%)
Total: 1.12s

line1000< ⋮ . . 1003: instruction four file1000.src:1 - 2 . . line2 . . 1002: instruction three file1000.src:2 + 2 . . line2 . . 1002: instruction three file1000.src:2 - 3 . . line3 - 4 . . line4 - 5 . . line5 - 6 . . line6 - 7 . . line7 + 3 . . line3 + 4 . . line4 + 5 . . line5 + 6 . . line6 + 7 . . line7

line3000

testdata/file3000.src

   Total:        10ms      1.12s (flat, cum)   100%
-      1            .          .           line1 
-      2            .          .           line2 
-      3            .          .           line3 
-      4            .          .           line4 
-      5            .          .           line5 
+      1            .          .           line1 
+      2            .          .           line2 
+      3            .          .           line3 
+      4            .          .           line4 
+      5            .          .           line5 
       6         10ms      1.01s           line6                 10ms      1.01s     3000:     instruction one                                                              file3000.src:6
 
-      7            .          .           line7 
-      8            .          .           line8 
-      9            .      110ms           line9                    .      100ms     3001:     instruction two                                                              file3000.src:9
+      7            .          .           line7 
+      8            .          .           line8 
+      9            .      110ms           line9                    .      100ms     3001:     instruction two                                                              file3000.src:9
                    .       10ms     3002:     instruction three                                                            file3000.src:9
                    .          .     3003:     instruction four                                                             
                    .          .     3004:     instruction five                                                             
 
-     10            .          .           line0 
-     11            .          .           line1 
-     12            .          .           line2 
-     13            .          .           line3 
-     14            .          .           line4 
+     10            .          .           line0 
+     11            .          .           line1 
+     12            .          .           line2 
+     13            .          .           line3 
+     14            .          .           line4 
 
diff --git a/internal/report/source.go b/internal/report/source.go index e45e911e..8a64b6e4 100644 --- a/internal/report/source.go +++ b/internal/report/source.go @@ -24,6 +24,7 @@ import ( "io" "os" "path/filepath" + "sort" "strconv" "strings" @@ -214,14 +215,73 @@ func PrintWebList(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFiles int) er } printFunctionHeader(w, ff.functionName, path, n.Flat, n.Cum, rpt) + percentiles := calculatePercentiles(fnodes) for _, fn := range fnodes { - printFunctionSourceLine(w, fn, asm[fn.Info.Lineno], reader, n.Cum, rpt) + printFunctionSourceLine(w, fn, asm[fn.Info.Lineno], reader, getPercentileString(float64(fn.Cum), percentiles), rpt) } printFunctionClosing(w) } return nil } +func getPercentileString(cumSum float64, percentiles map[float64]float64) string { + if cumSum == 0 { + return "" + } + switch { + case cumSum >= percentiles[80]: + return " percentile_80" + case cumSum >= percentiles[60]: + return " percentile_60" + case cumSum >= percentiles[40]: + return " percentile_40" + case cumSum >= percentiles[20]: + return " percentile_20" + case cumSum >= percentiles[10]: + return " percentile_10" + } + return "" +} + +// calculate percentile expects cumSums to be sorted and +// to contain unique elements. It also expects the percentile to be between 0 and 99. +func calculatePercentile(percentile float64, cumSums []float64) float64 { + rank := percentile / 100 * float64(len(cumSums)) + return cumSums[int64(rank)] +} + +func getSetOfCumValues(fnodes graph.Nodes) []float64 { + mapOfCumValues := make(map[int64]bool) + + for _, fn := range fnodes { + if _, ok := mapOfCumValues[fn.Cum]; !ok { + mapOfCumValues[fn.Cum] = true + } + } + + setOfCumValues := make([]float64, 0, len(mapOfCumValues)) + for key, _ := range mapOfCumValues { + setOfCumValues = append(setOfCumValues, float64(key)) + } + return setOfCumValues +} + +// calculatePercentiles returns nil when the fnodes is 0 +// because its result will never be used in such a case. +func calculatePercentiles(fnodes graph.Nodes) map[float64]float64 { + if len(fnodes) == 0 { + return nil + } + setOfCumValues := getSetOfCumValues(fnodes) + percentiles := map[float64]float64{80: 0, 60: 0, 40: 0, 20: 0, 10: 0} + sort.Float64s(setOfCumValues) + for key, _ := range percentiles { + percentiles[key] = calculatePercentile(key, setOfCumValues) + } + + return percentiles +} + // sourceCoordinates returns the lowest and highest line numbers from // a set of assembly statements. func sourceCoordinates(asm map[int][]assemblyInstruction) (start, end int) { @@ -347,40 +407,22 @@ func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64, measurement.Percentage(cumSum, rpt.total)) } -func calculatePercentile(partial, sum int64) string { - percentile := partial * 100 / sum - switch { - case percentile > 80: - return "percentile_80" - case percentile > 60: - return "percentile_60" - case percentile > 40: - return "percentile_40" - case percentile > 20: - return "percentile_20" - case percentile > 10: - return "percentile_10" - } - return "" -} - // printFunctionSourceLine prints a source line and the corresponding assembly. -func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyInstruction, reader *sourceReader, cumSum int64, rpt *Report) { - percentile := calculatePercentile(fn.Cum, cumSum) +func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyInstruction, reader *sourceReader, percentileString string, rpt *Report) { if len(assembly) == 0 { fmt.Fprintf(w, - " %6d %10s %10s %8s %s \n", + " %6d %10s %10s %8s %s \n", fn.Info.Lineno, - percentile, + percentileString, valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt), "", template.HTMLEscapeString(fn.Info.Name)) return } fmt.Fprintf(w, - " %6d %10s %10s %8s %s ", + " %6d %10s %10s %8s %s ", fn.Info.Lineno, - percentile, + percentileString, valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt), "", template.HTMLEscapeString(fn.Info.Name)) srcIndent := indentation(fn.Info.Name)