Commit e89d08e0 authored by Hana Kim's avatar Hana Kim Committed by Hyang-Ah Hana Kim

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/89295Reviewed-by: 's avatarPeter Weinberger <pjw@google.com>
parent 157d8cfb
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment