Ensure reference fileCPU is not overwritten

Would previously happen if memory profiling was already started.
Now using os.File.Stat beforehand to check if a handler of the file
is already active and confirm the status of profiling. This required
changing the type of the reference fileCPU from io.Closer to *os.File.
Asserting the type would have worked as well.
This commit is contained in:
ionutboangiu
2024-07-24 18:54:35 +03:00
committed by Dan Christian Bogos
parent 1c490a9020
commit 218ad92635
3 changed files with 27 additions and 18 deletions

View File

@@ -21,7 +21,6 @@ package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"os/signal"
@@ -354,7 +353,7 @@ func main() {
go singnalHandler(shdWg, shdChan)
var cS *cores.CoreService
var cpuProf io.Closer
var cpuProf *os.File
if *cpuProfDir != utils.EmptyString {
cpuPath := filepath.Join(*cpuProfDir, utils.CpuPathCgr)
cpuProf, err = cores.StartCPUProfiling(cpuPath)
@@ -371,7 +370,7 @@ func main() {
}
pprof.StopCPUProfile()
if err := cpuProf.Close(); err != nil {
log.Printf("could not close file %q: %v", cpuProf.(*os.File).Name(), err)
log.Print(err)
}
}()
}

View File

@@ -21,7 +21,6 @@ package cores
import (
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
@@ -36,7 +35,7 @@ import (
"github.com/cgrates/cgrates/utils"
)
func NewCoreService(cfg *config.CGRConfig, caps *engine.Caps, fileCPU io.Closer, stopChan chan struct{},
func NewCoreService(cfg *config.CGRConfig, caps *engine.Caps, fileCPU *os.File, stopChan chan struct{},
shdWg *sync.WaitGroup, shdChan *utils.SyncedChan) *CoreService {
var st *engine.CapsStats
if caps.IsLimited() && cfg.CoreSCfg().CapsStatsInterval != 0 {
@@ -63,7 +62,7 @@ type CoreService struct {
stopMemProf chan struct{} // signal end of memory profiling
fileCPUMux sync.Mutex
fileCPU io.Closer
fileCPU *os.File
caps *engine.Caps
}
@@ -76,27 +75,38 @@ func (cS *CoreService) Shutdown() {
}
// StartCPUProfiling starts CPU profiling and saves the profile to the specified path.
func (cS *CoreService) StartCPUProfiling(path string) (err error) {
func (cS *CoreService) StartCPUProfiling(path string) error {
if path == utils.EmptyString {
return utils.NewErrMandatoryIeMissing("DirPath")
}
cS.fileCPUMux.Lock()
defer cS.fileCPUMux.Unlock()
cS.fileCPU, err = StartCPUProfiling(path)
return
if cS.fileCPU != nil {
// Check if the profiling is already active by calling Stat() on the file handle.
// If Stat() returns nil, it means profiling is already active.
if _, err := cS.fileCPU.Stat(); err == nil {
return errors.New("start CPU profiling: already started")
}
}
file, err := StartCPUProfiling(path)
if err != nil {
return err
}
cS.fileCPU = file
return nil
}
// StartCPUProfiling creates a file and passes it to pprof.StartCPUProfile. It returns the file
// as an io.Closer to be able to close it later when stopping the CPU profiling.
func StartCPUProfiling(path string) (io.Closer, error) {
// to be able to verify the status of profiling and close it after profiling is stopped.
func StartCPUProfiling(path string) (*os.File, error) {
f, err := os.Create(path)
if err != nil {
return nil, fmt.Errorf("could not create CPU profile: %v", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
if err := f.Close(); err != nil {
utils.Logger.Warning(fmt.Sprintf(
"<%s> could not close file %q: %v", utils.CoreS, f.Name(), err))
utils.Logger.Warning(fmt.Sprintf("<%s> %v", utils.CoreS, err))
}
return nil, fmt.Errorf("could not start CPU profile: %v", err)
}

View File

@@ -20,7 +20,7 @@ package services
import (
"fmt"
"io"
"os"
"sync"
"github.com/cgrates/birpc"
@@ -33,7 +33,7 @@ import (
// NewCoreService returns the Core Service
func NewCoreService(cfg *config.CGRConfig, caps *engine.Caps, server *cores.Server,
internalCoreSChan chan birpc.ClientConnector, anz *AnalyzerService,
fileCpu io.Closer, shdWg *sync.WaitGroup, shdChan *utils.SyncedChan,
fileCPU *os.File, shdWg *sync.WaitGroup, shdChan *utils.SyncedChan,
srvDep map[string]*sync.WaitGroup) *CoreService {
return &CoreService{
shdChan: shdChan,
@@ -41,7 +41,7 @@ func NewCoreService(cfg *config.CGRConfig, caps *engine.Caps, server *cores.Serv
connChan: internalCoreSChan,
cfg: cfg,
caps: caps,
fileCpu: fileCpu,
fileCPU: fileCPU,
server: server,
anz: anz,
srvDep: srvDep,
@@ -57,7 +57,7 @@ type CoreService struct {
stopChan chan struct{}
shdWg *sync.WaitGroup
shdChan *utils.SyncedChan
fileCpu io.Closer
fileCPU *os.File
cS *cores.CoreService
connChan chan birpc.ClientConnector
anz *AnalyzerService
@@ -74,7 +74,7 @@ func (cS *CoreService) Start() error {
defer cS.Unlock()
utils.Logger.Info(fmt.Sprintf("<%s> starting <%s> subsystem", utils.CoreS, utils.CoreS))
cS.stopChan = make(chan struct{})
cS.cS = cores.NewCoreService(cS.cfg, cS.caps, cS.fileCpu, cS.stopChan, cS.shdWg, cS.shdChan)
cS.cS = cores.NewCoreService(cS.cfg, cS.caps, cS.fileCPU, cS.stopChan, cS.shdWg, cS.shdChan)
srv, err := engine.NewServiceWithName(cS.cS, utils.CoreS, true)
if err != nil {
return err