From d5ad4065ac661e7b3bb46b5f6580f00e4460908b Mon Sep 17 00:00:00 2001 From: Olivier Stasse <ostasse@laas.fr> Date: Wed, 24 Jul 2019 11:18:52 +0200 Subject: [PATCH] Fix priority problem when logging + add CPU load introspection. --- CMakeLists.txt | 1 + include/dynamic-graph/process-list.hh | 182 ++++++++++++++++++++++++++ src/CMakeLists.txt | 2 + src/debug/real-time-logger.cpp | 48 +++++-- src/mt/process-list.cpp | 175 +++++++++++++++++++++++++ tests/CMakeLists.txt | 2 + tests/real-time-logger.cpp | 14 ++ 7 files changed, 416 insertions(+), 8 deletions(-) create mode 100644 include/dynamic-graph/process-list.hh create mode 100644 src/mt/process-list.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 92487d1..92636cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ SET(PKG_CONFIG_EXTRA "plugindir=${PLUGINDIR}") PKG_CONFIG_APPEND_LIBS(${PROJECT_NAME}) # Search for Boost. +SET(BOOST_COMPONENTS serialization system unit_test_framework thread) SEARCH_FOR_BOOST() SEARCH_FOR_PTHREAD() diff --git a/include/dynamic-graph/process-list.hh b/include/dynamic-graph/process-list.hh new file mode 100644 index 0000000..7782b82 --- /dev/null +++ b/include/dynamic-graph/process-list.hh @@ -0,0 +1,182 @@ +/* Copyright LAAS, CNRS + * Author: O. Stasse, 2019 + * See LICENSE file in the root directory of this repository. + */ + +#ifndef DYNAMIC_GRAPH_PROCESS_LIST_H_ +# define DYNAMIC_GRAPH_PROCESS_LIST_H + +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/vector.hpp> + +# include <dynamic-graph/fwd.hh> +# include <dynamic-graph/dynamic-graph-api.h> + +namespace dynamicgraph +{ + namespace CPU + { + class DYNAMIC_GRAPH_DLLAPI ProcessData + { + }; + + class DYNAMIC_GRAPH_DLLAPI ProcessList + { + public: + ProcessList(); + + }; + + /// \class This class gather information on a specific CPU. + /// + class DYNAMIC_GRAPH_DLLAPI CPUData + { + public: + + CPUData(); + int cpu_id_; + + inline unsigned long long int + computePeriod(unsigned long long int &a, + unsigned long long int &b) + { + return (a>b) ? a-b :0; + } + + /// \brief Various classes of time spend by the CPU + /// @{ + /// Total time + unsigned long long int total_time_; + /// Time spend in user mode + unsigned long long int user_mode_time_; + /// Time spend in user mode with low priority (nice mode) + unsigned long long int nice_time_; + /// Time spend in system mode + unsigned long long int system_time_; + /// Time spend in system mode + unsigned long long int system_all_time_; + /// Time spend in doing nothing. + unsigned long long int idle_time_; + /// Time spend in doing nothing. + unsigned long long int idle_all_time_; + /// Time spend in waiting an input/output to complete. + unsigned long long int iowait_time_; + /// Time spend in servicing hardware interrupts. + unsigned long long int irq_time_; + /// Time spend in servicing software interrupts. + unsigned long long int softirq_time_; + /// Time spend in other operating systems in a virtualized environments + /// Never doing this for control ! + unsigned long long int steal_time_; + /// Time spend running a virtual CPU for guest operating systems + /// under the control of the Linux kernel + unsigned long long int guest_time_; + /// Time spent running a niced guest + /// (virtual CPU for guest operating systems under the + /// control of the Linux kernel) + unsigned long long int guest_nice_time_; + /// @} + + /// \brief Various classes of time spend by the CPU by period + /// @{ + /// Total time + unsigned long long int total_period_; + /// Time spend in user mode + unsigned long long int user_mode_period_; + /// Time spend in user mode with low priority (nice mode) + unsigned long long int nice_period_; + /// Time spend in system mode + unsigned long long int system_period_; + /// Time spend in all system mode + unsigned long long int system_all_period_; + /// Time spend in doing nothing. + unsigned long long int idle_period_; + /// Time spend in doing nothing. + unsigned long long int idle_all_period_; + /// Time spend in waiting an input/output to complete. + unsigned long long int iowait_period_; + /// Time spend in servicing hardware interrupts. + unsigned long long int irq_period_; + /// Time spend in servicing software interrupts. + unsigned long long int softirq_period_; + /// Time spend in other operating systems in a virtualized environments + /// Never doing this for control ! + unsigned long long int steal_period_; + /// Time spend running a virtual CPU for guest operating systems + /// under the control of the Linux kernel + unsigned long long int guest_period_; + /// @} + + double percent_; + void ProcessLine(std::istringstream &aCPULine); + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int version) + { + ar & version; + ar & total_time_; + ar & user_mode_time_; + ar & nice_time_; + ar & system_time_; + ar & system_all_time_; + ar & idle_time_; + ar & idle_all_time_; + ar & iowait_time_; + ar & irq_time_; + ar & softirq_time_; + ar & steal_time_; + ar & guest_time_; + ar & guest_nice_time_; + ar & percent_; + } + + }; + + /// \class This class gathers information on a computer. + /// This includes a list of CPU + class DYNAMIC_GRAPH_DLLAPI System + { + private: + bool init_; + + public: + System(); + + /// Read /proc/state file to extract CPU count. + void init(); + + /// Update CPU data information from /proc/stat + void readProcStat(); + + /// Friend class for serialization. + friend class boost::serialization::access; + + /// Number of CPU. + unsigned int cpuNb_; + + void ProcessCPULine(unsigned int cpunb, + std::istringstream &aCPULine); + + /// \brief Vector of CPU informations. + std::vector<CPUData> vCPUData_; + + /// \brief Global CPU information. + CPUData gCPUData_; + + template<class Archive> + void serialize(Archive & ar, const unsigned int version) + { + ar & version; + ar & cpuNb_; + ar & gCPUData_; + ar & vCPUData_; + } + + }; + } +} + +#endif /* DYNAMIC_GRAPH_PROCESS_LIST_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00054c9..31bf3ad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,8 @@ ADD_LIBRARY(${LIBRARY_NAME} exception/exception-signal.cpp exception/exception-traces.cpp + mt/process-list.cpp + signal/signal-array.cpp signal/signal-caster.cpp signal/signal-cast-helper.cpp diff --git a/src/debug/real-time-logger.cpp b/src/debug/real-time-logger.cpp index c78348b..c327843 100644 --- a/src/debug/real-time-logger.cpp +++ b/src/debug/real-time-logger.cpp @@ -74,34 +74,66 @@ namespace dynamicgraph struct RealTimeLogger::thread { bool requestShutdown_; + int threadPolicy_; + int threadPriority_; + bool changedThreadParams; boost::thread t_; - + thread (RealTimeLogger* logger) : requestShutdown_ (false) + , threadPolicy_(SCHED_OTHER) + , threadPriority_(0) + , changedThreadParams(true) , t_ (&thread::spin, this, logger) {} - void spin (RealTimeLogger* logger) + void setThreadPolicy(int policy) + { + threadPolicy_ = policy; + changedThreadParams = true; + + } + + void setPriority(int priority) + { + threadPriority_ = priority; + changedThreadParams = true; + } + + int getThreadPolicy() { return threadPolicy_; } + int getThreadPriority() { return threadPriority_; } + + void changeThreadParams() { - // Change the thread's scheduler from real-time to normal and reduce its priority int threadPolicy; struct sched_param threadParam; - if (pthread_getschedparam (pthread_self(), &threadPolicy, &threadParam) == 0) + if (pthread_getschedparam (pthread_self(), &threadPolicy, &threadParam) + == 0) { - threadPolicy = SCHED_OTHER; - threadParam.sched_priority -= 5; - if (threadParam.sched_priority < sched_get_priority_min (threadPolicy)) + threadPolicy = threadPolicy_; + threadParam.sched_priority = threadPriority_; + if (threadParam.sched_priority < + sched_get_priority_min (threadPolicy)) threadParam.sched_priority = sched_get_priority_min (threadPolicy); pthread_setschedparam (pthread_self(), threadPolicy, &threadParam); + changedThreadParams = false; } + } + + void spin (RealTimeLogger* logger) + { + // Change the thread's scheduler from real-time to normal + // and reduce its priority while (!requestShutdown_ || !logger->empty()) { // If the logger did not write anything, it means the buffer is empty. // Do a pause if (!logger->spinOnce()) - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + if (changedThreadParams) + changeThreadParams(); } } }; diff --git a/src/mt/process-list.cpp b/src/mt/process-list.cpp new file mode 100644 index 0000000..b0d37b4 --- /dev/null +++ b/src/mt/process-list.cpp @@ -0,0 +1,175 @@ +/* Copyright LAAS, CNRS + * Author: O. Stasse, 2019 + * See LICENSE file in the root directory of this repository. + */ +#include <fstream> +#include <sstream> +#include <string> + +#include <dynamic-graph/process-list.hh> + +using namespace dynamicgraph::CPU; +CPUData::CPUData(): + user_mode_time_(0), + nice_time_(0), + system_time_(0), + idle_time_(0), + iowait_time_(0), + irq_time_(0), + softirq_time_(0), + steal_time_(0), + guest_time_(0), + guest_nice_time_(0), + percent_(0.0) +{ +} + +void CPUData::ProcessLine(std::istringstream &aCPULine) +{ + unsigned long long int luser_mode_time=0, + lnice_time=0, lsystem_time=0, lidle_time=0, + liowait_time=0, lirq_time=0, lsoftirq_time=0, + lsteal_time=0,lguest_time=0, lguest_nice_time; + + aCPULine >> luser_mode_time; + aCPULine >> lnice_time; + aCPULine >> lsystem_time; + aCPULine >> lidle_time; + aCPULine >> liowait_time; + aCPULine >> lirq_time; + aCPULine >> lsoftirq_time; + aCPULine >> lsteal_time; + aCPULine >> lguest_time; + aCPULine >> lguest_nice_time; + + // Remove guest time already in user_time: + luser_mode_time -= lguest_time; + lnice_time -= lguest_nice_time; + + // Compute cumulative time + unsigned long long int lidle_all_time=0, + lsystem_all_time=0, lguest_all_time=0, ltotal_time=0; + + lidle_all_time = lidle_time + liowait_time; + lsystem_all_time = lsystem_time + lirq_time + lsoftirq_time; + lguest_all_time = lguest_time + lguest_nice_time; + ltotal_time = luser_mode_time + lnice_time + lsystem_all_time + + lidle_all_time + lsteal_time + lguest_all_time; + + + // Update periodic computation. + user_mode_period_ = computePeriod(luser_mode_time, user_mode_time_); + nice_period_ = computePeriod(lnice_time, nice_time_); + system_period_ = computePeriod(lsystem_time, system_time_); + system_all_period_ = computePeriod(lsystem_all_time,system_all_time_); + idle_period_ = computePeriod(lidle_time, idle_time_); + idle_all_period_ = computePeriod(lidle_all_time, idle_all_time_); + iowait_period_ = computePeriod(liowait_time, idle_time_); + irq_period_ = computePeriod(lirq_time, irq_time_); + softirq_period_ = computePeriod(lsoftirq_time, softirq_time_); + steal_period_ = computePeriod(lsteal_time, steal_time_); + guest_period_ = computePeriod(lguest_all_time, guest_time_); + total_period_ = computePeriod(ltotal_time, total_time_); + + /// Update time. + user_mode_time_ = luser_mode_time; + nice_time_ = lnice_time; + system_time_ = lsystem_time; + system_all_time_ = lsystem_all_time; + idle_time_ = lidle_time; + idle_all_time_ = lidle_all_time; + iowait_time_ = liowait_time; + irq_time_ = lirq_time; + softirq_time_ = lsoftirq_time; + steal_time_ = lsteal_time; + guest_time_ = lguest_all_time; + total_time_ = ltotal_time; + + if (total_period_!=0.0) + { + percent_ = (double)(user_mode_period_)/(double)(total_period_) * 100.0; + percent_ += (double)( nice_period_)/(double)(total_period_) * 100.0; + percent_ += (double)( system_period_)/(double)(total_period_) * 100.0; + percent_ += (double)( irq_period_)/(double)(total_period_) * 100.0; + percent_ += (double)( softirq_period_)/(double)(total_period_) * 100.0; + percent_ += (double)( steal_period_)/(double)(total_period_) * 100.0; + percent_ += (double)( iowait_period_)/(double)(total_period_) * 100.0; + } + std::cout << cpu_id_ << " " << percent_ << std::endl; +} + +System::System() +{ + vCPUData_.clear(); + init(); +} + +void System::init() +{ + init_ = false; + readProcStat(); + init_ = true; +} + +void System::ProcessCPULine(unsigned int cpunb, + std::istringstream &aCPULine) +{ + vCPUData_[cpunb].ProcessLine(aCPULine); +} + +void System::readProcStat() +{ + std::ifstream aif; + cpuNb_ = 1; + + aif.open("/proc/stat",std::ifstream::in); + std::string aline; + aline.clear(); + while (std::getline(aif,aline)) + { + // Read on line of the file + std::istringstream anISSLine(aline); + std::string line_hdr; + anISSLine >> line_hdr; + + // Check if the line start with cpu + std::size_t pos = line_hdr.find("cpu"); + std::string str_cpunbr = line_hdr.substr(pos+3); + + // Check if this is the first line + if (pos==0 and str_cpunbr.empty()) + { + gCPUData_.ProcessLine(anISSLine); + gCPUData_.cpu_id_=-1; + } + else + { + // If not then check if there is a CPU number + if (pos==0) + { + std::istringstream iss(str_cpunbr); + unsigned int lcpunb; + iss >> lcpunb; + // If we did not initialize + if (!init_) + { + // Count the number of CPU. + if (lcpunb>cpuNb_) + cpuNb_ = lcpunb; + } + else + // Otherwise process the line. + ProcessCPULine(lcpunb,anISSLine); + } + } + } + + if (!init_) + { + /// The number of CPU has been detected by going through /proc/stat. + vCPUData_.resize(cpuNb_+1); + for(int i=0;i<(int)cpuNb_;i++) + vCPUData_[i].cpu_id_ = i; + } + aif.close(); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a7a1815..b84f40a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -62,3 +62,5 @@ TARGET_LINK_LIBRARIES(debug-tracer tracer) DYNAMIC_GRAPH_TEST(debug-logger) DYNAMIC_GRAPH_TEST(debug-logger-winit) DYNAMIC_GRAPH_TEST(command-test) +DYNAMIC_GRAPH_TEST(test-mt) +TARGET_LINK_LIBRARIES(test-mt tracer) diff --git a/tests/real-time-logger.cpp b/tests/real-time-logger.cpp index eb5b7e1..ac9b38c 100644 --- a/tests/real-time-logger.cpp +++ b/tests/real-time-logger.cpp @@ -43,6 +43,20 @@ BOOST_AUTO_TEST_CASE (monothread) BOOST_AUTO_TEST_CASE (multithread) { + // The part of the code changing priority will only be effective + // if this test is run as root. Otherwise it behaves like a classical thread. + // Test confirms that in this case, it runs with a priority -51 + // and that the thread for logging is running on SCHED_OTHER + // with a nice priority (20). + int threadPolicy; + struct sched_param threadParam; + if (pthread_getschedparam (pthread_self(), &threadPolicy, &threadParam)==0) + { + threadPolicy = SCHED_RR; + threadParam.sched_priority = 50; + pthread_setschedparam (pthread_self(), threadPolicy, &threadParam); + } + RealTimeLogger& rtl = RealTimeLogger::instance(); dgADD_OSTREAM_TO_RTLOG (std::cout); -- GitLab