From a1536f1c29281b62d827361ae4a441f82dedefc6 Mon Sep 17 00:00:00 2001 From: Anthony Geay Date: Tue, 2 Jan 2024 11:24:33 +0100 Subject: [PATCH] [EDF29150] : Base mechanism to keep track of CPU and Mem info --- src/Basics/CMakeLists.txt | 1 + src/Basics/KernelBasis.i | 8 ++ src/Basics/Monitoring.cxx | 137 +++++++++++++++++++++++++++++++++ src/Basics/Monitoring.hxx | 34 ++++++++ src/Container/SALOME_PyNode.py | 63 ++++++++++++++- 5 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 src/Basics/Monitoring.cxx create mode 100644 src/Basics/Monitoring.hxx diff --git a/src/Basics/CMakeLists.txt b/src/Basics/CMakeLists.txt index 721defd8b..b3d781511 100644 --- a/src/Basics/CMakeLists.txt +++ b/src/Basics/CMakeLists.txt @@ -35,6 +35,7 @@ SET(SALOMEBasics_SOURCES Basics_DirUtils.cxx KernelBasis.cxx HeatMarcel.cxx + Monitoring.cxx ) ADD_LIBRARY(SALOMELog ${SALOMELog_SOURCES}) diff --git a/src/Basics/KernelBasis.i b/src/Basics/KernelBasis.i index cf78f1d79..3b733b01a 100644 --- a/src/Basics/KernelBasis.i +++ b/src/Basics/KernelBasis.i @@ -23,12 +23,16 @@ #include "KernelBasis.hxx" #include "HeatMarcel.hxx" #include "libSALOMELog.hxx" +#include "Monitoring.hxx" using namespace SALOME; %} %include std_string.i %include std_set.i %include std_except.i +%include std_vector.i + +%template(dvec) std::vector; %exception { try @@ -58,6 +62,10 @@ void setIOROfEmbeddedNS(const std::string& ior); double GetTimeAdjustmentCst(); +void LaunchMonitoring(const std::string& pyScriptToEvaluate, const std::string& outFileName); + +std::vector StopMonitoring(); + bool VerbosityActivated(); void SetVerbosityActivated(bool flag); diff --git a/src/Basics/Monitoring.cxx b/src/Basics/Monitoring.cxx new file mode 100644 index 000000000..0fa97dda7 --- /dev/null +++ b/src/Basics/Monitoring.cxx @@ -0,0 +1,137 @@ +// Copyright (C) 2023 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "Monitoring.hxx" + +#include "baseutilities.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#include +#include +#endif + +static std::string _out_filename; +#ifndef WIN32 +static pid_t pid_of_subprocess = 0; +#endif + +#ifndef WIN32 +static void LaunchMonitoringLinux(const std::string& pyScriptToEvaluate, const std::string& outFileName) +{ + constexpr char PYTHON_EXEC[] = "python3"; + pid_t pid = fork(); + if (pid == -1) + { + throw std::runtime_error("LaunchMonitoring : Error at creation of sub process !"); + } + else if( pid == 0) + { + execlp(PYTHON_EXEC,PYTHON_EXEC,pyScriptToEvaluate.c_str(),nullptr); + std::ostringstream oss; oss << "LaunchMonitoring : Error during exe : " << sys_errlist[errno]; + throw std::runtime_error( oss.str() ); + } + else + { + pid_of_subprocess = pid; + } +} +#endif + +void SALOME::LaunchMonitoring(const std::string& pyScriptToEvaluate, const std::string& outFileName) +{ + _out_filename = outFileName; +#ifndef WIN32 + LaunchMonitoringLinux(pyScriptToEvaluate,outFileName); +#else + throw std::runtime_error("LaunchMonitoring not implemented for Windows !"); +#endif +} + +std::vector SALOME::ReadFloatsInFile(const std::string& fileName) +{ + std::ifstream inputFile( fileName ); + + if(!inputFile.is_open()) + { + std::ostringstream oss; oss << "Impossible to open file \"" << _out_filename<< "\" !"; + throw std::runtime_error( oss.str() ); + } + std::vector ret; + std::string line; + try + { + while (std::getline(inputFile, line)) + { + std::istringstream iss(line); + double floatValue; + if( !(iss >> floatValue) ) + throw std::invalid_argument("Conversion into FP failed !"); + if( !iss.eof() ) + throw std::invalid_argument("Conversion into FP failed !"); + ret.push_back(floatValue); + } + inputFile.close(); + } + catch (const std::exception& e) + { + } + return ret; +} + +#ifndef WIN32 +static std::vector StopMonitoringLinux() +{ + kill( pid_of_subprocess, SIGTERM ); + std::vector ret; + try + { + ret = SALOME::ReadFloatsInFile( _out_filename ); + } + catch(std::exception& e) { } + pid_of_subprocess = 0; + _out_filename.clear(); + return ret; +} +#endif + +std::vector SALOME::StopMonitoring() +{ +#ifndef WIN32 + return StopMonitoringLinux(); +#else + throw std::runtime_error("StopMonitoring not implemented for Windows !"); +#endif +} diff --git a/src/Basics/Monitoring.hxx b/src/Basics/Monitoring.hxx new file mode 100644 index 000000000..379218844 --- /dev/null +++ b/src/Basics/Monitoring.hxx @@ -0,0 +1,34 @@ +// Copyright (C) 2023 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#pragma once + +#include "SALOME_Basics.hxx" + +#include +#include + +namespace SALOME +{ + void BASICS_EXPORT LaunchMonitoring(const std::string& pyScriptToEvaluate, const std::string& outFileName); + + std::vector BASICS_EXPORT ReadFloatsInFile(const std::string& fileName); + + std::vector BASICS_EXPORT StopMonitoring(); +} diff --git a/src/Container/SALOME_PyNode.py b/src/Container/SALOME_PyNode.py index 7c1985d0d..d93751eaf 100644 --- a/src/Container/SALOME_PyNode.py +++ b/src/Container/SALOME_PyNode.py @@ -30,6 +30,7 @@ import Engines__POA import SALOME__POA import SALOME import logging +import os MY_CONTAINER_ENTRY_IN_GLBS = "my_container" @@ -398,7 +399,65 @@ def UnProxyObjectSimpleLocal( obj ): return retObj else: return obj - + +class FileDeleter: + def __init__(self, fileName): + self._filename = fileName + @property + def filename(self): + return self._filename + def __del__(self): + import os + if os.path.exists( self._filename ): + os.unlink( self._filename ) + +def LaunchMonitoring( intervalInMs ): + """ + Launch a subprocess monitoring self process. + This monitoring subprocess is a python process lauching every intervalInMs ms evaluation of + CPU usage and RSS memory. + Communication between subprocess and self is done by file. + """ + import KernelBasis + def BuildPythonFileForCPUPercent( intervalInMs ): + import os + import tempfile + with tempfile.NamedTemporaryFile(prefix="htop_",suffix=".py") as f: + tempPyFile = f.name + tempOutFile = "{}.txt".format( os.path.splitext( tempPyFile )[0] ) + pid = os.getpid() + with open(tempPyFile,"w") as f: + f.write("""import psutil +pid = {} +process = psutil.Process( pid ) +import time +with open("{}","a") as f: + while True: + f.write( "{{}}\\n".format( str( process.cpu_percent() ) ) ) + f.write( "{{}}\\n".format( str( process.memory_info().rss ) ) ) + f.flush() + time.sleep( {} / 1000.0 ) +""".format(pid, tempOutFile, intervalInMs)) + return FileDeleter(tempPyFile), FileDeleter(tempOutFile) + pyFileName, outFileName = BuildPythonFileForCPUPercent( intervalInMs ) + KernelBasis.LaunchMonitoring(pyFileName.filename,outFileName.filename) + return pyFileName, outFileName + +def StopMonitoring( ): + """ + Retrieve data of monitoring and kill monitoring subprocess. + + Returns + ------- + list : list of pairs. First param of pair is CPU usage. Second param of pair is rss memory usage + """ + import KernelBasis + from SALOME_ContainerHelper import ScriptExecInfo + ret = KernelBasis.StopMonitoring() + cpu = ret[::2] + mem_rss = [ScriptExecInfo.MemRepr( int(elt) ) for elt in ret[1::2]] + return [(a,b) for a,b in zip(cpu,mem_rss)] + class SeqByteReceiver: # 2GB limit to trigger split into chunks CHUNK_SIZE = 2000000000 @@ -550,6 +609,8 @@ class PyScriptNode_i (Engines__POA.PyScriptNode,Generic): import sys try: self.my_container_py.addTimeInfoOnLevel2(self.getIDInContainer(),self._current_exec,"startExecTime") + pyfile = BuildPythonFileForCPUPercent() + exec(self.ccode, self.context) self.my_container_py.addTimeInfoOnLevel2(self.getIDInContainer(),self._current_exec,"endExecTime") self.my_container_py.addTimeInfoOnLevel2(self.getIDInContainer(),self._current_exec,"startOutputTime") -- 2.39.2