kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/util/profile/profile.go (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package profile provides a simple method for exposing profile information 18 // through a --cpu_profile flag. This package also implicitly adds the 19 // /debug/pprof/... HTTP handlers. 20 package profile // import "kythe.io/kythe/go/util/profile" 21 22 import ( 23 "context" 24 "errors" 25 "flag" 26 "fmt" 27 "io" 28 "os" 29 "path/filepath" 30 "runtime/pprof" 31 "strings" 32 "sync" 33 34 "kythe.io/kythe/go/platform/vfs" 35 "kythe.io/kythe/go/util/log" 36 37 _ "net/http/pprof" // for /debug/pprof/... handlers 38 ) 39 40 var ( 41 profCPU = flag.String("cpu_profile", "", "Write CPU profile to the specified file (if nonempty)") 42 43 file io.Closer 44 mu sync.Mutex 45 ) 46 47 // Start begins profiling the program, writing data to the file given with the 48 // --cpu_profile flag. If --cpu_profile was not given, nothing happens. Start 49 // must not be called again until Stop is called. The profile data in the 50 // --cpu_profile flag is overwritten on each call to Start. 51 func Start(ctx context.Context) error { 52 mu.Lock() 53 defer mu.Unlock() 54 55 if *profCPU != "" { 56 if file != nil { 57 return errors.New("profiling already started") 58 } 59 60 f, err := vfs.Create(ctx, *profCPU) 61 if err != nil { 62 return fmt.Errorf("error creating profile file %q: %v", *profCPU, err) 63 } 64 file = f 65 pprof.StartCPUProfile(f) 66 } 67 return nil 68 } 69 70 // Stop stops profiling the program. If --cpu_profile was not given, nothing 71 // happens. Start must have be called before a call to Stop. 72 func Stop() error { 73 mu.Lock() 74 defer mu.Unlock() 75 76 if *profCPU != "" { 77 if file == nil { 78 return errors.New("profiling hasn't started") 79 } 80 81 pprof.StopCPUProfile() 82 err := file.Close() 83 file = nil 84 if err != nil { 85 return err 86 } 87 88 binPath := os.Args[0] 89 profPath := *profCPU 90 91 // Try to shorten file paths, if possible 92 cwd, err := os.Getwd() 93 if err == nil { 94 relBin, err := filepath.Rel(cwd, binPath) 95 if err == nil && !strings.HasPrefix(relBin, "../") { 96 binPath = relBin 97 } 98 relProf, err := filepath.Rel(cwd, profPath) 99 if err == nil && !strings.HasPrefix(relProf, "../") { 100 profPath = relProf 101 } 102 } 103 104 log.Infof("Profile data written: go tool pprof %s %s", binPath, profPath) 105 } 106 return nil 107 }