github.com/minio/mc@v0.0.0-20240507152021-646712d5e5fb/cmd/profiling.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "errors" 22 "os" 23 "path" 24 "runtime" 25 "runtime/pprof" 26 "time" 27 ) 28 29 type profiler interface { 30 Start() error 31 Stop() error 32 } 33 34 type cpuProfiler struct { 35 *os.File 36 } 37 38 func newCPUProfiler(f *os.File) *cpuProfiler { 39 return &cpuProfiler{File: f} 40 } 41 42 func (p *cpuProfiler) Start() error { 43 return pprof.StartCPUProfile(p.File) 44 } 45 46 func (p *cpuProfiler) Stop() error { 47 pprof.StopCPUProfile() 48 return p.File.Close() 49 } 50 51 type memProfiler struct { 52 *os.File 53 } 54 55 func newMemProfiler(f *os.File) *memProfiler { 56 return &memProfiler{File: f} 57 } 58 59 func (p *memProfiler) Start() error { 60 return nil 61 } 62 63 func (p *memProfiler) Stop() error { 64 runtime.GC() 65 if e := pprof.Lookup("heap").WriteTo(p.File, 0); e != nil { 66 return e 67 } 68 return p.File.Close() 69 } 70 71 type blockProfiler struct { 72 *os.File 73 } 74 75 func newBlockProfiler(f *os.File) *blockProfiler { 76 return &blockProfiler{File: f} 77 } 78 79 func (p *blockProfiler) Start() error { 80 runtime.SetBlockProfileRate(100) 81 return nil 82 } 83 84 func (p *blockProfiler) Stop() error { 85 if e := pprof.Lookup("block").WriteTo(p.File, 0); e != nil { 86 return e 87 } 88 runtime.SetBlockProfileRate(0) 89 return p.File.Close() 90 } 91 92 type goroutineProfiler struct { 93 *os.File 94 } 95 96 func newGoroutineProfiler(f *os.File) *goroutineProfiler { 97 return &goroutineProfiler{File: f} 98 } 99 100 func (p *goroutineProfiler) Start() error { 101 return nil 102 } 103 104 func (p *goroutineProfiler) Stop() error { 105 if e := pprof.Lookup("goroutine").WriteTo(p.File, 1); e != nil { 106 return e 107 } 108 return p.File.Close() 109 } 110 111 var globalProfilers []profiler 112 113 // Enable profiling supported modes are [cpu, mem, block, goroutine]. 114 func enableProfilers(outputFolder string, profilers []string) error { 115 now := time.Now().Format("2006-01-02T15-04-05") 116 117 for _, profilerName := range profilers { 118 outputFile := path.Join(outputFolder, profilerName+"."+now) 119 f, e := os.Create(outputFile) 120 if e != nil { 121 return e 122 } 123 124 var p profiler 125 switch profilerName { 126 case "cpu": 127 p = newCPUProfiler(f) 128 case "mem": 129 p = newMemProfiler(f) 130 case "block": 131 p = newBlockProfiler(f) 132 case "goroutine": 133 p = newGoroutineProfiler(f) 134 default: 135 return errors.New("unknown profiler name") 136 } 137 138 if e := p.Start(); e != nil { 139 return e 140 } 141 142 // Keep the profiler in a list to stop it later 143 globalProfilers = append(globalProfilers, p) 144 } 145 146 return nil 147 } 148 149 func stopProfiling() error { 150 for _, p := range globalProfilers { 151 if e := p.Stop(); e != nil { 152 return e 153 } 154 } 155 return nil 156 }