diff --git a/cmd/influxd/main.go b/cmd/influxd/main.go index b05fd4b7a14..a8d54bc442a 100644 --- a/cmd/influxd/main.go +++ b/cmd/influxd/main.go @@ -5,6 +5,9 @@ import ( "fmt" "log" "os" + "os/signal" + "runtime" + "runtime/pprof" "strings" ) @@ -78,10 +81,16 @@ func execRun(args []string) { pidPath = fs.String("pidfile", "", "") hostname = fs.String("hostname", "", "") join = fs.String("join", "", "") + cpuprofile = fs.String("cpuprofile", "", "") + memprofile = fs.String("memprofile", "", "") ) fs.Usage = printRunUsage fs.Parse(args) + // Start profiling, if set. + startProfiling(*cpuprofile, *memprofile) + defer stopProfiling() + // Print sweet InfluxDB logo and write the process id to file. log.Print(logo) log.SetPrefix(`[srvr] `) @@ -154,3 +163,47 @@ type Stopper interface { type State struct { Mode string `json:"mode"` } + +var prof struct { + cpu *os.File + mem *os.File +} + +func startProfiling(cpuprofile, memprofile string) { + if cpuprofile != "" { + f, err := os.Create(cpuprofile) + if err != nil { + log.Fatalf("cpuprofile: %v", err) + } + prof.cpu = f + pprof.StartCPUProfile(prof.cpu) + } + + if memprofile != "" { + f, err := os.Create(memprofile) + if err != nil { + log.Fatalf("memprofile: %v", err) + } + prof.mem = f + runtime.MemProfileRate = 4096 + } + + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + stopProfiling() + os.Exit(0) + }() +} + +func stopProfiling() { + if prof.cpu != nil { + pprof.StopCPUProfile() + prof.cpu.Close() + } + if prof.mem != nil { + pprof.Lookup("heap").WriteTo(prof.mem, 0) + prof.mem.Close() + } +} diff --git a/cmd/influxd/noprofile.go b/cmd/influxd/noprofile.go deleted file mode 100644 index 8eca0a1a418..00000000000 --- a/cmd/influxd/noprofile.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build !linux !profile - -package main - -import ( - "os" - "os/signal" - "syscall" - "time" - - log "code.google.com/p/log4go" -) - -func startProfiler(stopper Stopper) error { - go waitForSignals(stopper) - return nil -} - -func waitForSignals(stopper Stopper) { - ch := make(chan os.Signal) - signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) - for { - sig := <-ch - log.Info("Received signal: %s", sig.String()) - switch sig { - case syscall.SIGINT, syscall.SIGTERM: - stopper.Stop() - time.Sleep(time.Second) - os.Exit(0) - } - } -} diff --git a/cmd/influxd/profile.go b/cmd/influxd/profile.go deleted file mode 100644 index 9c4a276034e..00000000000 --- a/cmd/influxd/profile.go +++ /dev/null @@ -1,122 +0,0 @@ -// build this file if the profile tag is specified and we're running on linux - -// +build linux,profile - -package main - -// #cgo LDFLAGS: -ltcmalloc -lprofiler -// #include "google/heap-profiler.h" -// #include "google/profiler.h" -import "C" - -import ( - "flag" - "fmt" - "os" - "os/signal" - "runtime" - "runtime/pprof" - "syscall" - "time" - - log "code.google.com/p/log4go" -) - -var profileFilename *string - -func init() { - profileFilename = flag.String("profile", "", "filename prefix where cpu and memory profile data will be written") -} - -func waitForSignals(stopper Stopper, filename string, stopped <-chan bool) { - ch := make(chan os.Signal) - signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) -outer: - for { - sig := <-ch - log.Info("Received signal: %s", sig.String()) - switch sig { - case syscall.SIGINT, syscall.SIGTERM: - runtime.SetCPUProfileRate(0) - f, err := os.OpenFile(fmt.Sprintf("%s.mem", filename), os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600) - if err != nil { - log.Errorf("Cannot open memory profile: %s", err) - break outer - } - if err := pprof.WriteHeapProfile(f); err != nil { - log.Errorf("Cannot write memory profile: %s", err) - } - f.Close() - stopCHeapProfiler() - // stopCCpuProfiler() - stopper.Stop() - break outer - // make sure everything stopped before exiting - } - } - // wait for all logging messages to be printed - <-stopped - time.Sleep(5 * time.Second) - os.Exit(0) -} - -func startProfiler(stopper Stopper) error { - if profileFilename == nil || *profileFilename == "" { - log.Info("Not starting profiling since the profile prefix is not set") - return nil - } - - log.Info("Starting profiling with prefix %s", *profileFilename) - - startCHeapProfiler(*profileFilename) - // startCCpuProfiler(*profileFilename) - runtime.MemProfileRate = 1024 - - cpuProfileFile, err := os.Create(fmt.Sprintf("%s.cpu", *profileFilename)) - if err != nil { - return err - } - runtime.SetCPUProfileRate(500) - stopped := make(chan bool) - - go waitForSignals(stopper, *profileFilename, stopped) - - go func() { - for { - select { - default: - data := runtime.CPUProfile() - if data == nil { - cpuProfileFile.Close() - stopped <- true - break - } - cpuProfileFile.Write(data) - } - } - }() - return nil -} - -func startCCpuProfiler(prefix string) { - log.Info("Starting native cpu profiling") - _prefix := C.CString(fmt.Sprintf("%s.native.cpu", prefix)) - C.ProfilerStart(_prefix) -} - -func stopCCpuProfiler() { - log.Info("Stopping native cpu profiling") - C.ProfilerStop() -} - -func startCHeapProfiler(prefix string) { - log.Info("Starting tcmalloc profiling") - _prefix := C.CString(prefix) - C.HeapProfilerStart(_prefix) -} - -func stopCHeapProfiler() { - log.Info("Stopping tcmalloc profiling") - C.HeapProfilerDump(nil) - C.HeapProfilerStop() -}