blob: 3ccfb4672a5311eb8cbe16f5a3e8e1e213b718c4 [file] [log] [blame]
/*
* Copyright 2015 The Kythe Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package profile provides a simple method for exposing profile information
// through a --cpu_profile flag. This package also implicitly adds the
// /debug/pprof/... HTTP handlers.
package profile
import (
"context"
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"runtime/pprof"
"strings"
"sync"
"kythe.io/kythe/go/platform/vfs"
_ "net/http/pprof" // for /debug/pprof/... handlers
)
var (
profCPU = flag.String("cpu_profile", "", "Write CPU profile to the specified file (if nonempty)")
file io.Closer
mu sync.Mutex
)
// Start begins profiling the program, writing data to the file given with the
// --cpu_profile flag. If --cpu_profile was not given, nothing happens. Start
// must not be called again until Stop is called. The profile data in the
// --cpu_profile flag is overwritten on each call to Start.
func Start(ctx context.Context) error {
mu.Lock()
defer mu.Unlock()
if *profCPU != "" {
if file != nil {
return errors.New("profiling already started")
}
f, err := vfs.Create(ctx, *profCPU)
if err != nil {
return fmt.Errorf("error creating profile file %q: %v", *profCPU, err)
}
file = f
pprof.StartCPUProfile(f)
}
return nil
}
// Stop stops profiling the program. If --cpu_profile was not given, nothing
// happens. Start must have be called before a call to Stop.
func Stop() error {
mu.Lock()
defer mu.Unlock()
if *profCPU != "" {
if file == nil {
return errors.New("profiling hasn't started")
}
pprof.StopCPUProfile()
err := file.Close()
file = nil
if err != nil {
return err
}
binPath := os.Args[0]
profPath := *profCPU
// Try to shorten file paths, if possible
cwd, err := os.Getwd()
if err == nil {
relBin, err := filepath.Rel(cwd, binPath)
if err == nil && !strings.HasPrefix(relBin, "../") {
binPath = relBin
}
relProf, err := filepath.Rel(cwd, profPath)
if err == nil && !strings.HasPrefix(relProf, "../") {
profPath = relProf
}
}
log.Printf("Profile data written: go tool pprof %s %s", binPath, profPath)
}
return nil
}