From e89d08e02180b72b541bcda0def3caf754704e25 Mon Sep 17 00:00:00 2001 From: Hana Kim Date: Tue, 23 Jan 2018 15:02:45 -0500 Subject: [PATCH] runtime/pprof: scale mutex profile with sampling rate pprof expects the samples are scaled and reflects unsampled numbers. The legacy profile parser uses the sampling period in the output and multiplies all values with the period. https://github.com/google/pprof/blob/0138a3cd6dad6f94495ba0b5c3a5c124f04ae011/profile/legacy_profile.go#L815 Apply the same scaling when we output the mutex profile in the pprof proto format. Block profile shares the same code, but how to infer unsampled values is unclear. Legacy profile parser doesn't do anything special so we do nothing for block profile here. Tested by checking the profiles reported with debug=0 (proto format) are similar to the profiles computed from legacy format profile when the profile rate is a non-trivial number (e.g. 2) manually. Change-Id: Iaa33f92051deed67d8be43ddffc7c1016db566ca Reviewed-on: https://go-review.googlesource.com/89295 Reviewed-by: Peter Weinberger --- src/runtime/pprof/pprof.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index d3382a5589..b7e5a1f92f 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -350,7 +350,8 @@ type countProfile interface { // as the pprof-proto format output. Translations from cycle count to time duration // are done because The proto expects count and time (nanoseconds) instead of count // and the number of cycles for block, contention profiles. -func printCountCycleProfile(w io.Writer, countName, cycleName string, records []runtime.BlockProfileRecord) error { +// Possible 'scaler' functions are scaleBlockProfile and scaleMutexProfile. +func printCountCycleProfile(w io.Writer, countName, cycleName string, scaler func(int64, float64) (int64, float64), records []runtime.BlockProfileRecord) error { // Output profile in protobuf form. b := newProfileBuilder(w) b.pbValueType(tagProfile_PeriodType, countName, "count") @@ -363,8 +364,9 @@ func printCountCycleProfile(w io.Writer, countName, cycleName string, records [] values := []int64{0, 0} var locs []uint64 for _, r := range records { - values[0] = int64(r.Count) - values[1] = int64(float64(r.Cycles) / cpuGHz) // to nanoseconds + count, nanosec := scaler(r.Count, float64(r.Cycles)/cpuGHz) + values[0] = count + values[1] = int64(nanosec) locs = locs[:0] for _, addr := range r.Stack() { // For count profiles, all stack addresses are @@ -806,7 +808,7 @@ func writeBlock(w io.Writer, debug int) error { sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles }) if debug <= 0 { - return printCountCycleProfile(w, "contentions", "delay", p) + return printCountCycleProfile(w, "contentions", "delay", scaleBlockProfile, p) } b := bufio.NewWriter(w) @@ -833,6 +835,14 @@ func writeBlock(w io.Writer, debug int) error { return b.Flush() } +func scaleBlockProfile(cnt int64, ns float64) (int64, float64) { + // Do nothing. + // The current way of block profile sampling makes it + // hard to compute the unsampled number. The legacy block + // profile parse doesn't attempt to scale or unsample. + return cnt, ns +} + // writeMutex writes the current mutex profile to w. func writeMutex(w io.Writer, debug int) error { // TODO(pjw): too much common code with writeBlock. FIX! @@ -850,7 +860,7 @@ func writeMutex(w io.Writer, debug int) error { sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles }) if debug <= 0 { - return printCountCycleProfile(w, "contentions", "delay", p) + return printCountCycleProfile(w, "contentions", "delay", scaleMutexProfile, p) } b := bufio.NewWriter(w) @@ -878,4 +888,9 @@ func writeMutex(w io.Writer, debug int) error { return b.Flush() } +func scaleMutexProfile(cnt int64, ns float64) (int64, float64) { + period := runtime.SetMutexProfileFraction(-1) + return cnt * int64(period), ns * float64(period) +} + func runtime_cyclesPerSecond() int64