github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/runsc/profile/profile.go (about) 1 // Copyright 2021 The gVisor 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 profile contains profiling utils. 16 package profile 17 18 import ( 19 "os" 20 "runtime" 21 "runtime/pprof" 22 "runtime/trace" 23 24 "github.com/nicocha30/gvisor-ligolo/pkg/log" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/control" 26 "github.com/nicocha30/gvisor-ligolo/runsc/flag" 27 ) 28 29 // Kind is the kind of profiling to perform. 30 type Kind int 31 32 const ( 33 // Block profile. 34 Block Kind = iota 35 // CPU profile. 36 CPU 37 // Heap profile. 38 Heap 39 // Mutex profile. 40 Mutex 41 // Trace profile. 42 Trace 43 ) 44 45 // FDArgs are the arguments that describe which profiles to enable and which 46 // FDs to write them to. Profiling of a given type will only be enabled if the 47 // corresponding FD is >=0. 48 type FDArgs struct { 49 // BlockFD is the file descriptor to write a block profile to. 50 // Valid if >=0. 51 BlockFD int 52 // CPUFD is the file descriptor to write a CPU profile to. 53 // Valid if >=0. 54 CPUFD int 55 // HeapFD is the file descriptor to write a heap profile to. 56 // Valid if >=0. 57 HeapFD int 58 // MutexFD is the file descriptor to write a mutex profile to. 59 // Valid if >=0. 60 MutexFD int 61 // TraceFD is the file descriptor to write a Go execution trace to. 62 // Valid if >=0. 63 TraceFD int 64 } 65 66 // SetFromFlags sets the FDArgs from the given flags. The default value for 67 // each FD is -1. 68 func (fds *FDArgs) SetFromFlags(f *flag.FlagSet) { 69 f.IntVar(&fds.BlockFD, "profile-block-fd", -1, "file descriptor to write block profile to. -1 disables profiling.") 70 f.IntVar(&fds.CPUFD, "profile-cpu-fd", -1, "file descriptor to write CPU profile to. -1 disables profiling.") 71 f.IntVar(&fds.HeapFD, "profile-heap-fd", -1, "file descriptor to write heap profile to. -1 disables profiling.") 72 f.IntVar(&fds.MutexFD, "profile-mutex-fd", -1, "file descriptor to write mutex profile to. -1 disables profiling.") 73 f.IntVar(&fds.TraceFD, "trace-fd", -1, "file descriptor to write Go execution trace to. -1 disables tracing.") 74 } 75 76 // Opts is a map of profile Kind to FD. 77 type Opts map[Kind]uintptr 78 79 // ToOpts turns FDArgs into an Opts struct which can be passed to Start. 80 func (fds *FDArgs) ToOpts() Opts { 81 o := Opts{} 82 if fds.BlockFD >= 0 { 83 o[Block] = uintptr(fds.BlockFD) 84 } 85 if fds.CPUFD >= 0 { 86 o[CPU] = uintptr(fds.CPUFD) 87 } 88 if fds.HeapFD >= 0 { 89 o[Heap] = uintptr(fds.HeapFD) 90 } 91 if fds.MutexFD >= 0 { 92 o[Mutex] = uintptr(fds.MutexFD) 93 } 94 if fds.TraceFD >= 0 { 95 o[Trace] = uintptr(fds.TraceFD) 96 } 97 return o 98 } 99 100 // Start starts profiling for the given Kinds in opts, and writes the profile 101 // data to the corresponding FDs in opts. It returns a function which will stop 102 // profiling. 103 func Start(opts Opts) func() { 104 var onStopProfiling []func() 105 stopProfiling := func() { 106 for _, f := range onStopProfiling { 107 f() 108 } 109 } 110 111 if fd, ok := opts[Block]; ok { 112 log.Infof("Block profiling enabled") 113 file := os.NewFile(fd, "profile-block") 114 115 runtime.SetBlockProfileRate(control.DefaultBlockProfileRate) 116 onStopProfiling = append(onStopProfiling, func() { 117 if err := pprof.Lookup("block").WriteTo(file, 0); err != nil { 118 log.Warningf("Error writing block profile: %v", err) 119 } 120 file.Close() 121 runtime.SetBlockProfileRate(0) 122 log.Infof("Block profiling stopped") 123 }) 124 } 125 126 if fd, ok := opts[CPU]; ok { 127 log.Infof("CPU profiling enabled") 128 file := os.NewFile(fd, "profile-cpu") 129 130 pprof.StartCPUProfile(file) 131 onStopProfiling = append(onStopProfiling, func() { 132 pprof.StopCPUProfile() 133 file.Close() 134 log.Infof("CPU profiling stopped") 135 }) 136 } 137 138 if fd, ok := opts[Heap]; ok { 139 log.Infof("Heap profiling enabled") 140 file := os.NewFile(fd, "profile-heap") 141 142 onStopProfiling = append(onStopProfiling, func() { 143 if err := pprof.Lookup("heap").WriteTo(file, 0); err != nil { 144 log.Warningf("Error writing heap profile: %v", err) 145 } 146 file.Close() 147 log.Infof("Heap profiling stopped") 148 }) 149 } 150 151 if fd, ok := opts[Mutex]; ok { 152 log.Infof("Mutex profiling enabled") 153 file := os.NewFile(fd, "profile-mutex") 154 155 prev := runtime.SetMutexProfileFraction(control.DefaultMutexProfileRate) 156 onStopProfiling = append(onStopProfiling, func() { 157 if err := pprof.Lookup("mutex").WriteTo(file, 0); err != nil { 158 log.Warningf("Error writing mutex profile: %v", err) 159 } 160 file.Close() 161 runtime.SetMutexProfileFraction(prev) 162 log.Infof("Mutex profiling stopped") 163 }) 164 } 165 166 if fd, ok := opts[Trace]; ok { 167 log.Infof("Tracing enabled") 168 file := os.NewFile(fd, "trace") 169 170 trace.Start(file) 171 onStopProfiling = append(onStopProfiling, func() { 172 trace.Stop() 173 file.Close() 174 log.Infof("Tracing stopped") 175 }) 176 } 177 178 return stopProfiling 179 }