diff --git a/apis/cores_it_test.go b/apis/cores_it_test.go index 32bd9d2a2..1049d6a14 100644 --- a/apis/cores_it_test.go +++ b/apis/cores_it_test.go @@ -21,11 +21,15 @@ along with this program. If not, see package apis import ( + "os" + "os/exec" "path" "testing" + "time" "github.com/cgrates/birpc" "github.com/cgrates/birpc/context" + "github.com/cgrates/birpc/jsonrpc" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" @@ -34,20 +38,32 @@ import ( var ( coreItCfgPath string coreItDirPath string + argPath string coreItCfg *config.CGRConfig coreItBiRPC *birpc.Client coreItTests = []func(t *testing.T){ testCoreItLoadConfig, testCoreItInitDataDb, testCoreItInitStorDb, + testCoreItStartEngineByExecWithCPUProfiling, + testCoreItRpcConn, + testCoreItStartCPUProfilingErrorAlreadyStarted, + testCoreItSleep, + testCoreItStopCPUProfiling, + testCoreItKillEngine, testCoreItStartEngine, testCoreItRpcConn, + testCoreItStopCPUProfilingBeforeStart, + testCoreItStartCPUProfiling, + testCoreItSleep, + testCoreItStopCPUProfiling, testCoreItStatus, testCoreItKillEngine, } ) func TestCoreItTests(t *testing.T) { + argPath = "/tmp/cpu.prof" switch *dbType { case utils.MetaInternal: coreItDirPath = "all2" @@ -83,6 +99,27 @@ func testCoreItInitStorDb(t *testing.T) { } } +func testCoreItStartEngineByExecWithCPUProfiling(t *testing.T) { + engine := exec.Command("cgr-engine", "-config_path", coreItCfgPath, "-cpuprof_dir", "/tmp") + if err := engine.Start(); err != nil { + t.Error(err) + } + fib := utils.Fib() + var connected bool + for i := 0; i < 200; i++ { + time.Sleep(time.Duration(fib()) * time.Millisecond) + if _, err := jsonrpc.Dial(utils.TCP, coreItCfg.ListenCfg().RPCJSONListen); err != nil { + t.Log(err) + } else { + connected = true + break + } + } + if !connected { + t.Errorf("engine did not open port <%s>", coreItCfg.ListenCfg().RPCJSONListen) + } +} + func testCoreItStartEngine(t *testing.T) { if _, err := engine.StartEngine(coreItCfgPath, *waitRater); err != nil { t.Fatal(err) @@ -96,6 +133,74 @@ func testCoreItRpcConn(t *testing.T) { } } +func testCoreItStartCPUProfilingErrorAlreadyStarted(t *testing.T) { + var reply string + expectedErr := "CPU profiling already started" + if err := coreItBiRPC.Call(context.Background(), utils.CoreSv1StartCPUProfiling, + argPath, &reply); err == nil || err.Error() != expectedErr { + t.Errorf("Expected %+v, received %+v", expectedErr, err) + } +} + +func testCoreItSleep(t *testing.T) { + args := &utils.DurationArgs{ + Duration: 500 * time.Millisecond, + } + var reply string + if err := coreItBiRPC.Call(context.Background(), utils.CoreSv1Sleep, + args, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } +} + +func testCoreItStopCPUProfiling(t *testing.T) { + var reply string + if err := coreItBiRPC.Call(context.Background(), utils.CoreSv1StopCPUProfiling, + utils.EmptyString, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } + file, err := os.Open(argPath) + if err != nil { + t.Error(err) + } + defer file.Close() + + //compare the size + size, err := file.Stat() + if err != nil { + t.Error(err) + } else if size.Size() < int64(415) { + t.Errorf("Size of CPUProfile %v is lower that expected", size.Size()) + } + //after we checked that CPUProfile was made successfully, can delete it + if err := os.Remove(argPath); err != nil { + t.Error(err) + } +} + +func testCoreItStopCPUProfilingBeforeStart(t *testing.T) { + var reply string + expectedErr := " cannot stop because CPUProfiling is not active" + if err := coreItBiRPC.Call(context.Background(), utils.CoreSv1StopCPUProfiling, + utils.EmptyString, &reply); err == nil || err.Error() != expectedErr { + t.Errorf("Expected %+q, received %+q", expectedErr, err) + } +} + +func testCoreItStartCPUProfiling(t *testing.T) { + var reply string + if err := coreItBiRPC.Call(context.Background(), utils.CoreSv1StartCPUProfiling, + argPath, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned") + } +} + func testCoreItStatus(t *testing.T) { args := &utils.TenantIDWithAPIOpts{} var reply map[string]interface{} diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index d8244fad2..0fc67d5e7 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -303,21 +303,6 @@ func memProfiling(memProfDir string, interval time.Duration, nrFiles int, shdWg } } -func startCPUProfiling(cpuProfDir string) (f io.WriteCloser, err error) { - cpuPath := path.Join(cpuProfDir, utils.CpuPathCgr) - if f, err = os.Create(cpuPath); err != nil { - utils.Logger.Crit(fmt.Sprintf("could not create cpu profile file: %s", err)) - return - } - pprof.StartCPUProfile(f) - return -} - -func stopCPUProfiling(f io.Closer) { - pprof.StopCPUProfile() - f.Close() -} - func singnalHandler(shdWg *sync.WaitGroup, shdChan *utils.SyncedChan) { shutdownSignal := make(chan os.Signal, 1) reloadSignal := make(chan os.Signal, 1) @@ -628,7 +613,7 @@ func main() { } // init CoreSv1 - coreS := services.NewCoreService(cfg, caps, server, internalCoreSv1Chan, anz, srvDep, shdChan) + coreS := services.NewCoreService(cfg, caps, server, internalCoreSv1Chan, anz, cpuProfileFile, srvDep, shdChan) shdWg.Add(1) if err := coreS.Start(); err != nil { fmt.Println(err) diff --git a/services/cores.go b/services/cores.go index 83105754b..d6fcbd0d1 100644 --- a/services/cores.go +++ b/services/cores.go @@ -33,10 +33,11 @@ import ( // NewCoreService returns the Core Service func NewCoreService(cfg *config.CGRConfig, caps *engine.Caps, server *cores.Server, - internalCoreSChan chan birpc.ClientConnector, anz *AnalyzerService, + internalCoreSChan chan birpc.ClientConnector, anz *AnalyzerService, file io.Closer, srvDep map[string]*sync.WaitGroup, shdEngine *utils.SyncedChan) *CoreService { return &CoreService{ connChan: internalCoreSChan, + fileCpu: file, cfg: cfg, caps: caps, server: server, diff --git a/utils/consts.go b/utils/consts.go index f35c217bd..4397b2ab1 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -1291,11 +1291,13 @@ const ( ) const ( - CoreS = "CoreS" - CoreSv1 = "CoreSv1" - CoreSv1Status = "CoreSv1.Status" - CoreSv1Ping = "CoreSv1.Ping" - CoreSv1Sleep = "CoreSv1.Sleep" + CoreS = "CoreS" + CoreSv1 = "CoreSv1" + CoreSv1Status = "CoreSv1.Status" + CoreSv1Ping = "CoreSv1.Ping" + CoreSv1Sleep = "CoreSv1.Sleep" + CoreSv1StartCPUProfiling = "CoreSv1.StartCPUProfiling" + CoreSv1StopCPUProfiling = "CoreSv1.StopCPUProfiling" ) // RouteS APIs