go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/internal/pprof.go (about) 1 // Copyright 2019 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package internal 16 17 import ( 18 "context" 19 "fmt" 20 "html/template" 21 "runtime" 22 "sync" 23 24 "go.chromium.org/luci/common/logging" 25 26 "go.chromium.org/luci/server/portal" 27 ) 28 29 const ( 30 blockProfileRate = 100 31 mutexProfileRate = 10 32 ) 33 34 var ( 35 profilingEnabled bool 36 profilingMutex sync.Mutex 37 ) 38 39 func setProfilingEnabled(ctx context.Context, enabled bool) { 40 profilingMutex.Lock() 41 defer profilingMutex.Unlock() 42 if profilingEnabled != enabled { 43 profilingEnabled = enabled 44 if enabled { 45 runtime.SetBlockProfileRate(blockProfileRate) 46 runtime.SetMutexProfileFraction(mutexProfileRate) 47 logging.Warningf(ctx, "Contention profiling is now enabled") 48 } else { 49 runtime.SetBlockProfileRate(0) 50 runtime.SetMutexProfileFraction(0) 51 logging.Warningf(ctx, "Contention profiling is now disabled") 52 } 53 } 54 } 55 56 func isProfilingEnabled() bool { 57 profilingMutex.Lock() 58 defer profilingMutex.Unlock() 59 return profilingEnabled 60 } 61 62 type pprofPage struct { 63 portal.BasePage 64 } 65 66 func (pprofPage) Title(ctx context.Context) (string, error) { 67 return "Profiling options", nil 68 } 69 70 func (pprofPage) Overview(ctx context.Context) (template.HTML, error) { 71 curValue := "disabled" 72 if isProfilingEnabled() { 73 curValue = "enabled" 74 } 75 return template.HTML(fmt.Sprintf(` 76 <p>This page allows to enable the collection of goroutine blocking and mutex 77 contention events from this process, by calling:</p> 78 <pre> 79 runtime.SetBlockProfileRate(%d) 80 runtime.SetMutexProfileFraction(%d) 81 </pre> 82 83 <p>The current value of this setting is <b>%s</b>.</p> 84 85 <p>This slows down the process and should be used sparingly only when 86 debugging performance issues. This setting affects only this single specific 87 process and it is not preserved between process restarts.</p> 88 89 <p>See <a href="https://golang.org/pkg/runtime/#SetBlockProfileRate">SetBlockProfileRate</a> and 90 <a href="https://golang.org/pkg/runtime/#SetMutexProfileFraction">SetMutexProfileFraction</a> 91 for more info.</p> 92 93 <p>All available profiles are listed at <a href="/debug/pprof/">/debug/pprof</a>.</p> 94 95 <p>To visualize some profile (e.g. <code>heap</code>), run e.g.:</p> 96 <pre> 97 go tool pprof -png \ 98 http://127.0.0.1:8900/debug/pprof/heap > out.png 99 </pre> 100 101 <p>To save a profile as a compressed protobuf message for later analysis:</p> 102 <pre> 103 go tool pprof -proto \ 104 http://127.0.0.1:8900/debug/pprof/heap > out.pb.gz 105 </pre> 106 `, 107 blockProfileRate, mutexProfileRate, curValue)), nil 108 } 109 110 func (pprofPage) Actions(ctx context.Context) ([]portal.Action, error) { 111 var profilingAction portal.Action 112 if !isProfilingEnabled() { 113 profilingAction = portal.Action{ 114 ID: "EnableProfiling", 115 Title: "Enable contention profiling", 116 Callback: func(ctx context.Context) (string, template.HTML, error) { 117 setProfilingEnabled(ctx, true) 118 return "Done", `<p>Contention profiling is now enabled.</p>`, nil 119 }, 120 } 121 } else { 122 profilingAction = portal.Action{ 123 ID: "DisableProfiling", 124 Title: "Disable contention profiling", 125 Callback: func(ctx context.Context) (string, template.HTML, error) { 126 setProfilingEnabled(ctx, false) 127 return "Done", `<p>Contention profiling is now disabled.</p>`, nil 128 }, 129 } 130 } 131 return []portal.Action{profilingAction}, nil 132 } 133 134 func init() { 135 portal.RegisterPage("pprof", pprofPage{}) 136 }