github.com/MetalBlockchain/metalgo@v1.11.9/utils/profiler/profiler.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package profiler 5 6 import ( 7 "errors" 8 "os" 9 "path/filepath" 10 "runtime" 11 "runtime/pprof" 12 13 "github.com/MetalBlockchain/metalgo/utils/perms" 14 ) 15 16 const ( 17 // Name of file that CPU profile is written to when StartCPUProfiler called 18 cpuProfileFile = "cpu.profile" 19 // Name of file that memory profile is written to when MemoryProfile called 20 memProfileFile = "mem.profile" 21 // Name of file that lock profile is written to 22 lockProfileFile = "lock.profile" 23 ) 24 25 var ( 26 _ Profiler = (*profiler)(nil) 27 28 errCPUProfilerRunning = errors.New("cpu profiler already running") 29 errCPUProfilerNotRunning = errors.New("cpu profiler doesn't exist") 30 ) 31 32 // Profiler provides helper methods for measuring the current performance of 33 // this process 34 type Profiler interface { 35 // StartCPUProfiler starts measuring the cpu utilization of this process 36 StartCPUProfiler() error 37 38 // StopCPUProfiler stops measuring the cpu utilization of this process 39 StopCPUProfiler() error 40 41 // MemoryProfile dumps the current memory utilization of this process 42 MemoryProfile() error 43 44 // LockProfile dumps the current lock statistics of this process 45 LockProfile() error 46 } 47 48 type profiler struct { 49 dir, 50 cpuProfileName, 51 memProfileName, 52 lockProfileName string 53 54 cpuProfileFile *os.File 55 } 56 57 func New(dir string) Profiler { 58 return newProfiler(dir) 59 } 60 61 func newProfiler(dir string) *profiler { 62 return &profiler{ 63 dir: dir, 64 cpuProfileName: filepath.Join(dir, cpuProfileFile), 65 memProfileName: filepath.Join(dir, memProfileFile), 66 lockProfileName: filepath.Join(dir, lockProfileFile), 67 } 68 } 69 70 func (p *profiler) StartCPUProfiler() error { 71 if p.cpuProfileFile != nil { 72 return errCPUProfilerRunning 73 } 74 75 if err := os.MkdirAll(p.dir, perms.ReadWriteExecute); err != nil { 76 return err 77 } 78 file, err := perms.Create(p.cpuProfileName, perms.ReadWrite) 79 if err != nil { 80 return err 81 } 82 if err := pprof.StartCPUProfile(file); err != nil { 83 _ = file.Close() // Return the original error 84 return err 85 } 86 runtime.SetMutexProfileFraction(1) 87 88 p.cpuProfileFile = file 89 return nil 90 } 91 92 func (p *profiler) StopCPUProfiler() error { 93 if p.cpuProfileFile == nil { 94 return errCPUProfilerNotRunning 95 } 96 97 pprof.StopCPUProfile() 98 err := p.cpuProfileFile.Close() 99 p.cpuProfileFile = nil 100 return err 101 } 102 103 func (p *profiler) MemoryProfile() error { 104 if err := os.MkdirAll(p.dir, perms.ReadWriteExecute); err != nil { 105 return err 106 } 107 file, err := perms.Create(p.memProfileName, perms.ReadWrite) 108 if err != nil { 109 return err 110 } 111 runtime.GC() // get up-to-date statistics 112 if err := pprof.WriteHeapProfile(file); err != nil { 113 _ = file.Close() // Return the original error 114 return err 115 } 116 return file.Close() 117 } 118 119 func (p *profiler) LockProfile() error { 120 if err := os.MkdirAll(p.dir, perms.ReadWriteExecute); err != nil { 121 return err 122 } 123 file, err := perms.Create(p.lockProfileName, perms.ReadWrite) 124 if err != nil { 125 return err 126 } 127 128 profile := pprof.Lookup("mutex") 129 if err := profile.WriteTo(file, 1); err != nil { 130 _ = file.Close() // Return the original error 131 return err 132 } 133 return file.Close() 134 }