github.com/jfrog/jfrog-cli-core/v2@v2.51.0/utils/coreutils/profiler.go (about) 1 package coreutils 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "runtime/pprof" 8 "time" 9 10 "github.com/jfrog/jfrog-client-go/utils/errorutils" 11 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 12 ) 13 14 const ( 15 // The default interval between 2 profiling actions 16 defaultInterval = time.Second 17 // The default number of profilings 18 defaultRepetitions = 3 19 ) 20 21 // This struct wraps the "pprof" profiler in Go. 22 // It is used for thread dumping. 23 type Profiler struct { 24 interval time.Duration 25 repetitions uint 26 } 27 28 type ProfilerOption func(*Profiler) 29 30 func NewProfiler(opts ...ProfilerOption) *Profiler { 31 profiler := &Profiler{ 32 interval: defaultInterval, 33 repetitions: defaultRepetitions, 34 } 35 for _, opt := range opts { 36 opt(profiler) 37 } 38 return profiler 39 } 40 41 func WithInterval(interval time.Duration) ProfilerOption { 42 return func(p *Profiler) { 43 p.interval = interval 44 } 45 } 46 47 func WithRepetitions(repetitions uint) ProfilerOption { 48 return func(p *Profiler) { 49 p.repetitions = repetitions 50 } 51 } 52 53 func (p *Profiler) ThreadDump() (output string, err error) { 54 var outputFilePath string 55 if outputFilePath, err = p.threadDumpToFile(); err != nil { 56 return 57 } 58 defer func() { 59 err = errors.Join(err, errorutils.CheckError(os.Remove(outputFilePath))) 60 }() 61 return p.convertFileToString(outputFilePath) 62 } 63 64 func (p *Profiler) threadDumpToFile() (outputFilePath string, err error) { 65 outputFile, err := fileutils.CreateTempFile() 66 if err != nil { 67 return 68 } 69 defer func() { 70 err = errors.Join(err, errorutils.CheckError(outputFile.Close())) 71 }() 72 73 for i := 0; i < int(p.repetitions); i++ { 74 fmt.Fprintf(outputFile, "========== Thread dump #%d ==========\n", i) 75 prof := pprof.Lookup("goroutine") 76 if err = errorutils.CheckError(prof.WriteTo(outputFile, 1)); err != nil { 77 return 78 } 79 time.Sleep(p.interval) 80 } 81 return outputFile.Name(), nil 82 } 83 84 func (p *Profiler) convertFileToString(outputFilePath string) (string, error) { 85 if outputBytes, err := os.ReadFile(outputFilePath); err != nil { 86 return "", errorutils.CheckError(err) 87 } else { 88 return string(outputBytes), nil 89 } 90 }