blob: 6f5dc20d8fa1bd8fd6a1b64c7e8b1b35ebaad334 [file] [log] [blame]
// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package internal
import (
"context"
"fmt"
"html/template"
"runtime"
"sync"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/server/portal"
)
const (
blockProfileRate = 100
mutexProfileRate = 10
)
var (
profilingEnabled bool
profilingMutex sync.Mutex
)
func setProfilingEnabled(ctx context.Context, enabled bool) {
profilingMutex.Lock()
defer profilingMutex.Unlock()
if profilingEnabled != enabled {
profilingEnabled = enabled
if enabled {
runtime.SetBlockProfileRate(blockProfileRate)
runtime.SetMutexProfileFraction(mutexProfileRate)
logging.Warningf(ctx, "Contention profiling is now enabled")
} else {
runtime.SetBlockProfileRate(0)
runtime.SetMutexProfileFraction(0)
logging.Warningf(ctx, "Contention profiling is now disabled")
}
}
}
func isProfilingEnabled() bool {
profilingMutex.Lock()
defer profilingMutex.Unlock()
return profilingEnabled
}
type pprofPage struct {
portal.BasePage
}
func (pprofPage) Title(ctx context.Context) (string, error) {
return "Profiling options", nil
}
func (pprofPage) Overview(ctx context.Context) (template.HTML, error) {
curValue := "disabled"
if isProfilingEnabled() {
curValue = "enabled"
}
return template.HTML(fmt.Sprintf(`
<p>This page allows to enable the collection of goroutine blocking and mutex
contention events from this process, by calling:</p>
<pre>
runtime.SetBlockProfileRate(%d)
runtime.SetMutexProfileFraction(%d)
</pre>
<p>The current value of this setting is <b>%s</b>.</p>
<p>This slows down the process and should be used sparingly only when
debugging performance issues. This setting affects only this single specific
process and it is not preserved between process restarts.</p>
<p>See <a href="https://golang.org/pkg/runtime/#SetBlockProfileRate">SetBlockProfileRate</a> and
<a href="https://golang.org/pkg/runtime/#SetMutexProfileFraction">SetMutexProfileFraction</a>
for more info.</p>
<p>All available profiles are listed at <a href="/debug/pprof/">/debug/pprof</a>.</p>
<p>To visualize some profile (e.g. <code>heap</code>), run e.g.:</p>
<pre>
go tool pprof -png \
http://127.0.0.1:8900/debug/pprof/heap > out.png
</pre>
<p>To save a profile as a compressed protobuf message for later analysis:</p>
<pre>
go tool pprof -proto \
http://127.0.0.1:8900/debug/pprof/heap > out.pb.gz
</pre>
`,
blockProfileRate, mutexProfileRate, curValue)), nil
}
func (pprofPage) Actions(ctx context.Context) ([]portal.Action, error) {
var profilingAction portal.Action
if !isProfilingEnabled() {
profilingAction = portal.Action{
ID: "EnableProfiling",
Title: "Enable contention profiling",
Callback: func(ctx context.Context) (string, template.HTML, error) {
setProfilingEnabled(ctx, true)
return "Done", `<p>Contention profiling is now enabled.</p>`, nil
},
}
} else {
profilingAction = portal.Action{
ID: "DisableProfiling",
Title: "Disable contention profiling",
Callback: func(ctx context.Context) (string, template.HTML, error) {
setProfilingEnabled(ctx, false)
return "Done", `<p>Contention profiling is now disabled.</p>`, nil
},
}
}
return []portal.Action{profilingAction}, nil
}
func init() {
portal.RegisterPage("pprof", pprofPage{})
}