summaryrefslogtreecommitdiff
path: root/python/openvino/runtime/common/monitors
diff options
context:
space:
mode:
Diffstat (limited to 'python/openvino/runtime/common/monitors')
-rw-r--r--python/openvino/runtime/common/monitors/CMakeLists.txt38
-rw-r--r--python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h28
-rw-r--r--python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h34
-rw-r--r--python/openvino/runtime/common/monitors/include/monitors/presenter.h44
-rw-r--r--python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h17
-rw-r--r--python/openvino/runtime/common/monitors/src/cpu_monitor.cpp206
-rw-r--r--python/openvino/runtime/common/monitors/src/memory_monitor.cpp213
-rw-r--r--python/openvino/runtime/common/monitors/src/presenter.cpp330
-rw-r--r--python/openvino/runtime/common/monitors/src/query_wrapper.cpp22
9 files changed, 932 insertions, 0 deletions
diff --git a/python/openvino/runtime/common/monitors/CMakeLists.txt b/python/openvino/runtime/common/monitors/CMakeLists.txt
new file mode 100644
index 0000000..1bfe0b9
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2018-2019 Intel Corporation
+# SPDX-License-Identifier: Apache-2.0
+#
+
+find_package(OpenCV REQUIRED COMPONENTS core imgproc)
+
+set(SOURCES
+ src/cpu_monitor.cpp
+ src/memory_monitor.cpp
+ src/presenter.cpp)
+
+set(HEADERS
+ include/monitors/cpu_monitor.h
+ include/monitors/memory_monitor.h
+ include/monitors/presenter.h)
+
+if(WIN32)
+ list(APPEND SOURCES src/query_wrapper.cpp)
+ list(APPEND HEADERS include/monitors/query_wrapper.h)
+endif()
+# Create named folders for the sources within the .vcproj
+# Empty name lists them directly under the .vcproj
+source_group("src" FILES ${SOURCES})
+source_group("include" FILES ${HEADERS})
+
+add_library(monitors STATIC ${SOURCES} ${HEADERS})
+target_include_directories(monitors PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
+target_link_libraries(monitors PRIVATE opencv_core opencv_imgproc)
+if(WIN32)
+ target_link_libraries(monitors PRIVATE pdh)
+
+ target_compile_definitions(monitors PRIVATE
+ # Prevents Windows.h from adding unnecessary includes
+ WIN32_LEAN_AND_MEAN
+ # Prevents Windows.h from defining min/max as macros
+ NOMINMAX
+ )
+endif()
diff --git a/python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h b/python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h
new file mode 100644
index 0000000..38d2845
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/include/monitors/cpu_monitor.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <deque>
+#include <memory>
+#include <vector>
+
+class CpuMonitor {
+public:
+ CpuMonitor();
+ ~CpuMonitor();
+ void setHistorySize(std::size_t size);
+ std::size_t getHistorySize() const;
+ void collectData();
+ std::deque<std::vector<double>> getLastHistory() const;
+ std::vector<double> getMeanCpuLoad() const;
+
+private:
+ unsigned samplesNumber;
+ unsigned historySize;
+ std::vector<double> cpuLoadSum;
+ std::deque<std::vector<double>> cpuLoadHistory;
+ class PerformanceCounter;
+ std::unique_ptr<PerformanceCounter> performanceCounter;
+};
diff --git a/python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h b/python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h
new file mode 100644
index 0000000..9eda10f
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/include/monitors/memory_monitor.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <deque>
+#include <memory>
+
+class MemoryMonitor {
+public:
+ MemoryMonitor();
+ ~MemoryMonitor();
+ void setHistorySize(std::size_t size);
+ std::size_t getHistorySize() const;
+ void collectData();
+ std::deque<std::pair<double, double>> getLastHistory() const;
+ double getMeanMem() const; // in GiB
+ double getMeanSwap() const;
+ double getMaxMem() const;
+ double getMaxSwap() const;
+ double getMemTotal() const;
+ double getMaxMemTotal() const; // a system may have hotpluggable memory
+private:
+ unsigned samplesNumber;
+ std::size_t historySize;
+ double memSum, swapSum;
+ double maxMem, maxSwap;
+ double memTotal;
+ double maxMemTotal;
+ std::deque<std::pair<double, double>> memSwapUsageHistory;
+ class PerformanceCounter;
+ std::unique_ptr<PerformanceCounter> performanceCounter;
+};
diff --git a/python/openvino/runtime/common/monitors/include/monitors/presenter.h b/python/openvino/runtime/common/monitors/include/monitors/presenter.h
new file mode 100644
index 0000000..c6587a0
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/include/monitors/presenter.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <chrono>
+#include <map>
+#include <ostream>
+#include <set>
+
+#include <opencv2/imgproc.hpp>
+
+#include "cpu_monitor.h"
+#include "memory_monitor.h"
+
+enum class MonitorType{CpuAverage, DistributionCpu, Memory};
+
+class Presenter {
+public:
+ explicit Presenter(std::set<MonitorType> enabledMonitors = {},
+ int yPos = 20,
+ cv::Size graphSize = {150, 60},
+ std::size_t historySize = 20);
+ explicit Presenter(const std::string& keys,
+ int yPos = 20,
+ cv::Size graphSize = {150, 60},
+ std::size_t historySize = 20);
+ void addRemoveMonitor(MonitorType monitor);
+ void handleKey(int key); // handles C, D, M, H keys
+ void drawGraphs(cv::Mat& frame);
+ std::vector<std::string> reportMeans() const;
+
+ const int yPos;
+ const cv::Size graphSize;
+ const int graphPadding;
+private:
+ std::chrono::steady_clock::time_point prevTimeStamp;
+ std::size_t historySize;
+ CpuMonitor cpuMonitor;
+ bool distributionCpuEnabled;
+ MemoryMonitor memoryMonitor;
+ std::ostringstream strStream;
+};
diff --git a/python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h b/python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h
new file mode 100644
index 0000000..d69f548
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/include/monitors/query_wrapper.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <Pdh.h>
+class QueryWrapper {
+public:
+ QueryWrapper();
+ ~QueryWrapper();
+ QueryWrapper(const QueryWrapper&) = delete;
+ QueryWrapper& operator=(const QueryWrapper&) = delete;
+ operator PDH_HQUERY() const;
+private:
+ PDH_HQUERY query;
+};
diff --git a/python/openvino/runtime/common/monitors/src/cpu_monitor.cpp b/python/openvino/runtime/common/monitors/src/cpu_monitor.cpp
new file mode 100644
index 0000000..e5172a2
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/src/cpu_monitor.cpp
@@ -0,0 +1,206 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "monitors/cpu_monitor.h"
+
+#include <algorithm>
+#ifdef _WIN32
+#include "monitors/query_wrapper.h"
+#include <string>
+#include <system_error>
+#include <PdhMsg.h>
+#include <Windows.h>
+
+namespace {
+const std::size_t nCores = []() {
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ return sysinfo.dwNumberOfProcessors;
+ }();
+}
+
+class CpuMonitor::PerformanceCounter {
+public:
+ PerformanceCounter() : coreTimeCounters(nCores) {
+ PDH_STATUS status;
+ for (std::size_t i = 0; i < nCores; ++i) {
+ std::wstring fullCounterPath{L"\\Processor(" + std::to_wstring(i) + L")\\% Processor Time"};
+ status = PdhAddCounterW(query, fullCounterPath.c_str(), 0, &coreTimeCounters[i]);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhAddCounterW() failed");
+ }
+ status = PdhSetCounterScaleFactor(coreTimeCounters[i], -2); // scale counter to [0, 1]
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhSetCounterScaleFactor() failed");
+ }
+ }
+ status = PdhCollectQueryData(query);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed");
+ }
+ }
+
+ std::vector<double> getCpuLoad() {
+ PDH_STATUS status;
+ status = PdhCollectQueryData(query);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed");
+ }
+
+ PDH_FMT_COUNTERVALUE displayValue;
+ std::vector<double> cpuLoad(coreTimeCounters.size());
+ for (std::size_t i = 0; i < coreTimeCounters.size(); ++i) {
+ status = PdhGetFormattedCounterValue(coreTimeCounters[i], PDH_FMT_DOUBLE, NULL,
+ &displayValue);
+ switch (status) {
+ case ERROR_SUCCESS: break;
+ // PdhGetFormattedCounterValue() can sometimes return PDH_CALC_NEGATIVE_DENOMINATOR for some reason
+ case PDH_CALC_NEGATIVE_DENOMINATOR: return {};
+ default:
+ throw std::system_error(status, std::system_category(), "PdhGetFormattedCounterValue() failed");
+ }
+ if (PDH_CSTATUS_VALID_DATA != displayValue.CStatus && PDH_CSTATUS_NEW_DATA != displayValue.CStatus) {
+ throw std::runtime_error("Error in counter data");
+ }
+
+ cpuLoad[i] = displayValue.doubleValue;
+ }
+ return cpuLoad;
+ }
+
+private:
+ QueryWrapper query;
+ std::vector<PDH_HCOUNTER> coreTimeCounters;
+};
+
+#elif __linux__
+#include <chrono>
+#include <regex>
+#include <utility>
+#include <fstream>
+#include <unistd.h>
+
+namespace {
+const long clockTicks = sysconf(_SC_CLK_TCK);
+
+const std::size_t nCores = sysconf(_SC_NPROCESSORS_CONF);
+
+std::vector<unsigned long> getIdleCpuStat() {
+ std::vector<unsigned long> idleCpuStat(nCores);
+ std::ifstream procStat("/proc/stat");
+ std::string line;
+ std::smatch match;
+ std::regex coreJiffies("^cpu(\\d+)\\s+"
+ "(\\d+)\\s+"
+ "(\\d+)\\s+"
+ "(\\d+)\\s+"
+ "(\\d+)\\s+" // idle
+ "(\\d+)"); // iowait
+
+ while (std::getline(procStat, line)) {
+ if (std::regex_search(line, match, coreJiffies)) {
+ // it doesn't handle overflow of sum and overflows of /proc/stat values
+ unsigned long idleInfo = stoul(match[5]) + stoul(match[6]),
+ coreId = stoul(match[1]);
+ if (nCores <= coreId) {
+ throw std::runtime_error("The number of cores has changed");
+ }
+ idleCpuStat[coreId] = idleInfo;
+ }
+ }
+ return idleCpuStat;
+}
+}
+
+class CpuMonitor::PerformanceCounter {
+public:
+ PerformanceCounter() : prevIdleCpuStat{getIdleCpuStat()}, prevTimePoint{std::chrono::steady_clock::now()} {}
+
+ std::vector<double> getCpuLoad() {
+ std::vector<unsigned long> idleCpuStat = getIdleCpuStat();
+ auto timePoint = std::chrono::steady_clock::now();
+ // don't update data too frequently which may result in negative values for cpuLoad.
+ // It may happen when collectData() is called just after setHistorySize().
+ if (timePoint - prevTimePoint > std::chrono::milliseconds{100}) {
+ std::vector<double> cpuLoad(nCores);
+ for (std::size_t i = 0; i < idleCpuStat.size(); ++i) {
+ double idleDiff = idleCpuStat[i] - prevIdleCpuStat[i];
+ typedef std::chrono::duration<double, std::chrono::seconds::period> Sec;
+ cpuLoad[i] = 1.0
+ - idleDiff / clockTicks / std::chrono::duration_cast<Sec>(timePoint - prevTimePoint).count();
+ }
+ prevIdleCpuStat = std::move(idleCpuStat);
+ prevTimePoint = timePoint;
+ return cpuLoad;
+ }
+ return {};
+ }
+private:
+ std::vector<unsigned long> prevIdleCpuStat;
+ std::chrono::steady_clock::time_point prevTimePoint;
+};
+
+#else
+// not implemented
+namespace {
+const std::size_t nCores{0};
+}
+
+class CpuMonitor::PerformanceCounter {
+public:
+ std::vector<double> getCpuLoad() {return {};};
+};
+#endif
+
+CpuMonitor::CpuMonitor() :
+ samplesNumber{0},
+ historySize{0},
+ cpuLoadSum(nCores, 0) {}
+
+// PerformanceCounter is incomplete in header and destructor can't be defined implicitly
+CpuMonitor::~CpuMonitor() = default;
+
+void CpuMonitor::setHistorySize(std::size_t size) {
+ if (0 == historySize && 0 != size) {
+ performanceCounter.reset(new PerformanceCounter);
+ } else if (0 != historySize && 0 == size) {
+ performanceCounter.reset();
+ }
+ historySize = size;
+ std::ptrdiff_t newSize = static_cast<std::ptrdiff_t>(std::min(size, cpuLoadHistory.size()));
+ cpuLoadHistory.erase(cpuLoadHistory.begin(), cpuLoadHistory.end() - newSize);
+}
+
+void CpuMonitor::collectData() {
+ std::vector<double> cpuLoad = performanceCounter->getCpuLoad();
+
+ if (!cpuLoad.empty()) {
+ for (std::size_t i = 0; i < cpuLoad.size(); ++i) {
+ cpuLoadSum[i] += cpuLoad[i];
+ }
+ ++samplesNumber;
+
+ cpuLoadHistory.push_back(std::move(cpuLoad));
+ if (cpuLoadHistory.size() > historySize) {
+ cpuLoadHistory.pop_front();
+ }
+ }
+}
+
+std::size_t CpuMonitor::getHistorySize() const {
+ return historySize;
+}
+
+std::deque<std::vector<double>> CpuMonitor::getLastHistory() const {
+ return cpuLoadHistory;
+}
+
+std::vector<double> CpuMonitor::getMeanCpuLoad() const {
+ std::vector<double> meanCpuLoad;
+ meanCpuLoad.reserve(cpuLoadSum.size());
+ for (double coreLoad : cpuLoadSum) {
+ meanCpuLoad.push_back(samplesNumber ? coreLoad / samplesNumber : 0);
+ }
+ return meanCpuLoad;
+}
diff --git a/python/openvino/runtime/common/monitors/src/memory_monitor.cpp b/python/openvino/runtime/common/monitors/src/memory_monitor.cpp
new file mode 100644
index 0000000..70879d6
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/src/memory_monitor.cpp
@@ -0,0 +1,213 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "monitors/memory_monitor.h"
+
+struct MemState {
+ double memTotal, usedMem, usedSwap;
+};
+
+#ifdef _WIN32
+#include "monitors/query_wrapper.h"
+#include <algorithm>
+#define PSAPI_VERSION 2
+#include <system_error>
+#include <Windows.h>
+#include <PdhMsg.h>
+#include <Psapi.h>
+
+namespace {
+double getMemTotal() {
+ PERFORMANCE_INFORMATION performanceInformation;
+ if (!GetPerformanceInfo(&performanceInformation, sizeof(performanceInformation))) {
+ throw std::runtime_error("GetPerformanceInfo() failed");
+ }
+ return static_cast<double>(performanceInformation.PhysicalTotal * performanceInformation.PageSize)
+ / (1024 * 1024 * 1024);
+}
+}
+
+class MemoryMonitor::PerformanceCounter {
+public:
+ PerformanceCounter() {
+ PDH_STATUS status = PdhAddCounterW(query, L"\\Paging File(_Total)\\% Usage", 0, &pagingFileUsageCounter);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhAddCounterW() failed");
+ }
+ status = PdhSetCounterScaleFactor(pagingFileUsageCounter, -2); // scale counter to [0, 1]
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhSetCounterScaleFactor() failed");
+ }
+ }
+
+ MemState getMemState() {
+ PERFORMANCE_INFORMATION performanceInformation;
+ if (!GetPerformanceInfo(&performanceInformation, sizeof(performanceInformation))) {
+ throw std::runtime_error("GetPerformanceInfo() failed");
+ }
+
+ PDH_STATUS status;
+ status = PdhCollectQueryData(query);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed");
+ }
+ PDH_FMT_COUNTERVALUE displayValue;
+ status = PdhGetFormattedCounterValue(pagingFileUsageCounter, PDH_FMT_DOUBLE, NULL, &displayValue);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhGetFormattedCounterValue() failed");
+ }
+ if (PDH_CSTATUS_VALID_DATA != displayValue.CStatus && PDH_CSTATUS_NEW_DATA != displayValue.CStatus) {
+ throw std::runtime_error("Error in counter data");
+ }
+
+ double pagingFilesSize = static_cast<double>(
+ (performanceInformation.CommitLimit - performanceInformation.PhysicalTotal)
+ * performanceInformation.PageSize) / (1024 * 1024 * 1024);
+ return {static_cast<double>(performanceInformation.PhysicalTotal * performanceInformation.PageSize)
+ / (1024 * 1024 * 1024),
+ static_cast<double>(
+ (performanceInformation.PhysicalTotal - performanceInformation.PhysicalAvailable)
+ * performanceInformation.PageSize) / (1024 * 1024 * 1024),
+ pagingFilesSize * displayValue.doubleValue};
+ }
+private:
+ QueryWrapper query;
+ PDH_HCOUNTER pagingFileUsageCounter;
+};
+
+#elif __linux__
+#include <fstream>
+#include <utility>
+#include <vector>
+#include <regex>
+
+namespace {
+std::pair<std::pair<double, double>, std::pair<double, double>> getAvailableMemSwapTotalMemSwap() {
+ double memAvailable = 0, swapFree = 0, memTotal = 0, swapTotal = 0;
+ std::regex memRegex("^(.+):\\s+(\\d+) kB$");
+ std::string line;
+ std::smatch match;
+ std::ifstream meminfo("/proc/meminfo");
+ while (std::getline(meminfo, line)) {
+ if (std::regex_match(line, match, memRegex)) {
+ if ("MemAvailable" == match[1]) {
+ memAvailable = stod(match[2]) / (1024 * 1024);
+ } else if ("SwapFree" == match[1]) {
+ swapFree = stod(match[2]) / (1024 * 1024);
+ } else if ("MemTotal" == match[1]) {
+ memTotal = stod(match[2]) / (1024 * 1024);
+ } else if ("SwapTotal" == match[1]) {
+ swapTotal = stod(match[2]) / (1024 * 1024);
+ }
+ }
+ }
+ if (0 == memTotal) {
+ throw std::runtime_error("Can't get MemTotal");
+ }
+ return {{memAvailable, swapFree}, {memTotal, swapTotal}};
+}
+
+double getMemTotal() {
+ return getAvailableMemSwapTotalMemSwap().second.first;
+}
+}
+
+class MemoryMonitor::PerformanceCounter {
+public:
+ MemState getMemState() {
+ std::pair<std::pair<double, double>, std::pair<double, double>> availableMemSwapTotalMemSwap
+ = getAvailableMemSwapTotalMemSwap();
+ double memTotal = availableMemSwapTotalMemSwap.second.first;
+ double swapTotal = availableMemSwapTotalMemSwap.second.second;
+ return {memTotal, memTotal - availableMemSwapTotalMemSwap.first.first, swapTotal - availableMemSwapTotalMemSwap.first.second};
+ }
+};
+
+#else
+// not implemented
+namespace {
+double getMemTotal() {return 0.0;}
+}
+
+class MemoryMonitor::PerformanceCounter {
+public:
+ MemState getMemState() {return {0.0, 0.0, 0.0};}
+};
+#endif
+
+MemoryMonitor::MemoryMonitor() :
+ samplesNumber{0},
+ historySize{0},
+ memSum{0.0},
+ swapSum{0.0},
+ maxMem{0.0},
+ maxSwap{0.0},
+ memTotal{0.0},
+ maxMemTotal{0.0} {}
+
+// PerformanceCounter is incomplete in header and destructor can't be defined implicitly
+MemoryMonitor::~MemoryMonitor() = default;
+
+void MemoryMonitor::setHistorySize(std::size_t size) {
+ if (0 == historySize && 0 != size) {
+ performanceCounter.reset(new MemoryMonitor::PerformanceCounter);
+ // memTotal is not initialized in constructor because for linux its initialization involves constructing
+ // std::regex which is unimplemented and throws an exception for gcc 4.8.5 (default for CentOS 7.4).
+ // Delaying initialization triggers the error only when the monitor is used
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631
+ memTotal = ::getMemTotal();
+ } else if (0 != historySize && 0 == size) {
+ performanceCounter.reset();
+ }
+ historySize = size;
+ std::size_t newSize = std::min(size, memSwapUsageHistory.size());
+ memSwapUsageHistory.erase(memSwapUsageHistory.begin(), memSwapUsageHistory.end() - newSize);
+}
+
+void MemoryMonitor::collectData() {
+ MemState memState = performanceCounter->getMemState();
+ maxMemTotal = std::max(maxMemTotal, memState.memTotal);
+ memSum += memState.usedMem;
+ swapSum += memState.usedSwap;
+ ++samplesNumber;
+ maxMem = std::max(maxMem, memState.usedMem);
+ maxSwap = std::max(maxSwap, memState.usedSwap);
+
+ memSwapUsageHistory.emplace_back(memState.usedMem, memState.usedSwap);
+ if (memSwapUsageHistory.size() > historySize) {
+ memSwapUsageHistory.pop_front();
+ }
+}
+
+std::size_t MemoryMonitor::getHistorySize() const {
+ return historySize;
+}
+
+std::deque<std::pair<double, double>> MemoryMonitor::getLastHistory() const {
+ return memSwapUsageHistory;
+}
+
+double MemoryMonitor::getMeanMem() const {
+ return samplesNumber ? memSum / samplesNumber : 0;
+}
+
+double MemoryMonitor::getMeanSwap() const {
+ return samplesNumber ? swapSum / samplesNumber : 0;
+}
+
+double MemoryMonitor::getMaxMem() const {
+ return maxMem;
+}
+
+double MemoryMonitor::getMaxSwap() const {
+ return maxSwap;
+}
+
+double MemoryMonitor::getMemTotal() const {
+ return memTotal;
+}
+
+double MemoryMonitor::getMaxMemTotal() const {
+ return maxMemTotal;
+}
diff --git a/python/openvino/runtime/common/monitors/src/presenter.cpp b/python/openvino/runtime/common/monitors/src/presenter.cpp
new file mode 100644
index 0000000..61f5e15
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/src/presenter.cpp
@@ -0,0 +1,330 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <cctype>
+#include <chrono>
+#include <iomanip>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include "monitors/presenter.h"
+
+namespace {
+const std::map<int, MonitorType> keyToMonitorType{
+ {'C', MonitorType::CpuAverage},
+ {'D', MonitorType::DistributionCpu},
+ {'M', MonitorType::Memory}};
+
+std::set<MonitorType> strKeysToMonitorSet(const std::string& keys) {
+ std::set<MonitorType> enabledMonitors;
+ if (keys == "h") {
+ return enabledMonitors;
+ }
+ for (unsigned char key: keys) {
+ if (key == 'h') {
+ throw std::runtime_error("Unacceptable combination of monitor types-can't show and hide info at the same time");
+ }
+ auto iter = keyToMonitorType.find(std::toupper(key));
+ if (keyToMonitorType.end() == iter) {
+ throw std::runtime_error("Unknown monitor type");
+ } else {
+ enabledMonitors.insert(iter->second);
+ }
+ }
+ return enabledMonitors;
+}
+}
+
+Presenter::Presenter(std::set<MonitorType> enabledMonitors,
+ int yPos,
+ cv::Size graphSize,
+ std::size_t historySize) :
+ yPos{yPos},
+ graphSize{graphSize},
+ graphPadding{std::max(1, static_cast<int>(graphSize.width * 0.05))},
+ historySize{historySize},
+ distributionCpuEnabled{false},
+ strStream{std::ios_base::app} {
+ for (MonitorType monitor : enabledMonitors) {
+ addRemoveMonitor(monitor);
+ }
+}
+
+Presenter::Presenter(const std::string& keys, int yPos, cv::Size graphSize, std::size_t historySize) :
+ Presenter{strKeysToMonitorSet(keys), yPos, graphSize, historySize} {}
+
+void Presenter::addRemoveMonitor(MonitorType monitor) {
+ unsigned updatedHistorySize = 1;
+ if (historySize > 1) {
+ int sampleStep = std::max(1, static_cast<int>(graphSize.width / (historySize - 1)));
+ // +1 to plot graphSize.width/sampleStep segments
+ // add round up to and an extra element if don't reach graph edge
+ updatedHistorySize = (graphSize.width + sampleStep - 1) / sampleStep + 1;
+ }
+ switch(monitor) {
+ case MonitorType::CpuAverage: {
+ if (cpuMonitor.getHistorySize() > 1 && distributionCpuEnabled) {
+ cpuMonitor.setHistorySize(1);
+ } else if (cpuMonitor.getHistorySize() > 1 && !distributionCpuEnabled) {
+ cpuMonitor.setHistorySize(0);
+ } else { // cpuMonitor.getHistorySize() <= 1
+ cpuMonitor.setHistorySize(updatedHistorySize);
+ }
+ break;
+ }
+ case MonitorType::DistributionCpu: {
+ if (distributionCpuEnabled) {
+ distributionCpuEnabled = false;
+ if (1 == cpuMonitor.getHistorySize()) { // cpuMonitor was used only for DistributionCpu => disable it
+ cpuMonitor.setHistorySize(0);
+ }
+ } else {
+ distributionCpuEnabled = true;
+ cpuMonitor.setHistorySize(std::max(std::size_t{1}, cpuMonitor.getHistorySize()));
+ }
+ break;
+ }
+ case MonitorType::Memory: {
+ if (memoryMonitor.getHistorySize() > 1) {
+ memoryMonitor.setHistorySize(0);
+ } else {
+ memoryMonitor.setHistorySize(updatedHistorySize);
+ }
+ break;
+ }
+ }
+}
+
+void Presenter::handleKey(int key) {
+ key = std::toupper(key);
+ if ('H' == key) {
+ if (0 == cpuMonitor.getHistorySize() && memoryMonitor.getHistorySize() <= 1) {
+ addRemoveMonitor(MonitorType::CpuAverage);
+ addRemoveMonitor(MonitorType::DistributionCpu);
+ addRemoveMonitor(MonitorType::Memory);
+ } else {
+ cpuMonitor.setHistorySize(0);
+ distributionCpuEnabled = false;
+ memoryMonitor.setHistorySize(0);
+ }
+ } else {
+ auto iter = keyToMonitorType.find(key);
+ if (keyToMonitorType.end() != iter) {
+ addRemoveMonitor(iter->second);
+ }
+ }
+}
+
+void Presenter::drawGraphs(cv::Mat& frame) {
+ const std::chrono::steady_clock::time_point curTimeStamp = std::chrono::steady_clock::now();
+ if (curTimeStamp - prevTimeStamp >= std::chrono::milliseconds{1000}) {
+ prevTimeStamp = curTimeStamp;
+ if (0 != cpuMonitor.getHistorySize()) {
+ cpuMonitor.collectData();
+ }
+ if (memoryMonitor.getHistorySize() > 1) {
+ memoryMonitor.collectData();
+ }
+ }
+
+ int numberOfEnabledMonitors = (cpuMonitor.getHistorySize() > 1) + distributionCpuEnabled
+ + (memoryMonitor.getHistorySize() > 1);
+ int panelWidth = graphSize.width * numberOfEnabledMonitors
+ + std::max(0, numberOfEnabledMonitors - 1) * graphPadding;
+ while (panelWidth > frame.cols) {
+ panelWidth = std::max(0, panelWidth - graphSize.width - graphPadding);
+ --numberOfEnabledMonitors; // can't draw all monitors
+ }
+ int graphPos = std::max(0, (frame.cols - 1 - panelWidth) / 2);
+ int textGraphSplittingLine = graphSize.height / 5;
+ int graphRectHeight = graphSize.height - textGraphSplittingLine;
+ int sampleStep = 1;
+ unsigned possibleHistorySize = 1;
+ if (historySize > 1) {
+ sampleStep = std::max(1, static_cast<int>(graphSize.width / (historySize - 1)));
+ possibleHistorySize = (graphSize.width + sampleStep - 1) / sampleStep + 1;
+ }
+
+ if (cpuMonitor.getHistorySize() > 1 && possibleHistorySize > 1 && --numberOfEnabledMonitors >= 0) {
+ std::deque<std::vector<double>> lastHistory = cpuMonitor.getLastHistory();
+ cv::Rect intersection = cv::Rect{cv::Point(graphPos, yPos), graphSize} & cv::Rect{0, 0, frame.cols, frame.rows};
+ if (!intersection.area()) {
+ return;
+ }
+ cv::Mat graph = frame(intersection);
+ graph = graph / 2 + cv::Scalar{127, 127, 127};
+
+ int lineXPos = graph.cols - 1;
+ std::vector<cv::Point> averageLoad(lastHistory.size());
+
+ for (int i = lastHistory.size() - 1; i >= 0; --i) {
+ double mean = std::accumulate(lastHistory[i].begin(), lastHistory[i].end(), 0.0) / lastHistory[i].size();
+ averageLoad[i] = {lineXPos, graphSize.height - static_cast<int>(mean * graphRectHeight)};
+ lineXPos -= sampleStep;
+ }
+
+ cv::polylines(graph, averageLoad, false, {255, 0, 0}, 2);
+ cv::rectangle(frame, cv::Rect{
+ cv::Point{graphPos, yPos + textGraphSplittingLine},
+ cv::Size{graphSize.width, graphSize.height - textGraphSplittingLine}
+ }, {0, 0, 0});
+ strStream.str("CPU");
+ if (!lastHistory.empty()) {
+ strStream << ": " << std::fixed << std::setprecision(1)
+ << std::accumulate(lastHistory.back().begin(), lastHistory.back().end(), 0.0)
+ / lastHistory.back().size() * 100 << '%';
+ }
+ int baseline;
+ int textWidth = cv::getTextSize(strStream.str(),
+ cv::FONT_HERSHEY_SIMPLEX,
+ textGraphSplittingLine * 0.04,
+ 1,
+ &baseline).width;
+ cv::putText(graph,
+ strStream.str(),
+ cv::Point{(graphSize.width - textWidth) / 2, textGraphSplittingLine - 1},
+ cv::FONT_HERSHEY_SIMPLEX,
+ textGraphSplittingLine * 0.04,
+ {70, 0, 0},
+ 1);
+ graphPos += graphSize.width + graphPadding;
+ }
+
+ if (distributionCpuEnabled && --numberOfEnabledMonitors >= 0) {
+ std::deque<std::vector<double>> lastHistory = cpuMonitor.getLastHistory();
+ cv::Rect intersection = cv::Rect{cv::Point(graphPos, yPos), graphSize} & cv::Rect{0, 0, frame.cols, frame.rows};
+ if (!intersection.area()) {
+ return;
+ }
+ cv::Mat graph = frame(intersection);
+ graph = graph / 2 + cv::Scalar{127, 127, 127};
+
+ if (!lastHistory.empty()) {
+ int rectXPos = 0;
+ int step = (graph.cols + lastHistory.back().size() - 1) / lastHistory.back().size(); // round up
+ double sum = 0;
+ for (double coreLoad : lastHistory.back()) {
+ sum += coreLoad;
+ int height = static_cast<int>(graphRectHeight * coreLoad);
+ cv::Rect pillar{cv::Point{rectXPos, graph.rows - height}, cv::Size{step, height}};
+ cv::rectangle(graph, pillar, {255, 0, 0}, cv::FILLED);
+ cv::rectangle(graph, pillar, {0, 0, 0});
+ rectXPos += step;
+ }
+ sum /= lastHistory.back().size();
+ int yLine = graph.rows - static_cast<int>(graphRectHeight * sum);
+ cv::line(graph, cv::Point{0, yLine}, cv::Point{graph.cols, yLine}, {0, 255, 0}, 2);
+ }
+ cv::Rect border{cv::Point{graphPos, yPos + textGraphSplittingLine},
+ cv::Size{graphSize.width, graphSize.height - textGraphSplittingLine}};
+ cv::rectangle(frame, border, {0, 0, 0});
+ strStream.str("Core load");
+ if (!lastHistory.empty()) {
+ strStream << ": " << std::fixed << std::setprecision(1)
+ << std::accumulate(lastHistory.back().begin(), lastHistory.back().end(), 0.0)
+ / lastHistory.back().size() * 100 << '%';
+ }
+ int baseline;
+ int textWidth = cv::getTextSize(strStream.str(),
+ cv::FONT_HERSHEY_SIMPLEX,
+ textGraphSplittingLine * 0.04,
+ 1,
+ &baseline).width;
+ cv::putText(graph,
+ strStream.str(),
+ cv::Point{(graphSize.width - textWidth) / 2, textGraphSplittingLine - 1},
+ cv::FONT_HERSHEY_SIMPLEX,
+ textGraphSplittingLine * 0.04,
+ {0, 70, 0});
+ graphPos += graphSize.width + graphPadding;
+ }
+
+ if (memoryMonitor.getHistorySize() > 1 && possibleHistorySize > 1 && --numberOfEnabledMonitors >= 0) {
+ std::deque<std::pair<double, double>> lastHistory = memoryMonitor.getLastHistory();
+ cv::Rect intersection = cv::Rect{cv::Point(graphPos, yPos), graphSize} & cv::Rect{0, 0, frame.cols, frame.rows};
+ if (!intersection.area()) {
+ return;
+ }
+ cv::Mat graph = frame(intersection);
+ graph = graph / 2 + cv::Scalar{127, 127, 127};
+ int histxPos = graph.cols - 1;
+ double range = std::min(memoryMonitor.getMaxMemTotal() + memoryMonitor.getMaxSwap(),
+ (memoryMonitor.getMaxMem() + memoryMonitor.getMaxSwap()) * 1.2);
+ if (lastHistory.size() > 1) {
+ for (auto memUsageIt = lastHistory.rbegin(); memUsageIt != lastHistory.rend() - 1; ++memUsageIt) {
+ constexpr double SWAP_THRESHOLD = 10.0 / 1024; // 10 MiB
+ cv::Vec3b color =
+ (memoryMonitor.getMemTotal() * 0.95 > memUsageIt->first) || (memUsageIt->second < SWAP_THRESHOLD) ?
+ cv::Vec3b{0, 255, 255} :
+ cv::Vec3b{0, 0, 255};
+ cv::Point right{histxPos,
+ graph.rows - static_cast<int>(graphRectHeight * (memUsageIt->first + memUsageIt->second) / range)};
+ cv::Point left{histxPos - sampleStep,
+ graph.rows - static_cast<int>(
+ graphRectHeight * ((memUsageIt + 1)->first + (memUsageIt + 1)->second) / range)};
+ cv::line(graph, right, left, color, 2);
+ histxPos -= sampleStep;
+ }
+ }
+
+ cv::Rect border{cv::Point{graphPos, yPos + textGraphSplittingLine},
+ cv::Size{graphSize.width, graphSize.height - textGraphSplittingLine}};
+ cv::rectangle(frame, {border}, {0, 0, 0});
+ if (lastHistory.empty()) {
+ strStream.str("Memory");
+ } else {
+ strStream.str("");
+ strStream << std::fixed << std::setprecision(1) << lastHistory.back().first << " + "
+ << lastHistory.back().second << " GiB";
+ }
+ int baseline;
+ int textWidth = cv::getTextSize(strStream.str(),
+ cv::FONT_HERSHEY_SIMPLEX,
+ textGraphSplittingLine * 0.04,
+ 1,
+ &baseline).width;
+ cv::putText(graph,
+ strStream.str(),
+ cv::Point{(graphSize.width - textWidth) / 2, textGraphSplittingLine - 1},
+ cv::FONT_HERSHEY_SIMPLEX,
+ textGraphSplittingLine * 0.04,
+ {0, 35, 35});
+ }
+}
+
+std::vector<std::string> Presenter::reportMeans() const {
+ std::vector<std::string> collectedData;
+ if (cpuMonitor.getHistorySize() > 1 || distributionCpuEnabled || memoryMonitor.getHistorySize() > 1) {
+ collectedData.push_back("Resources usage:");
+ }
+ if (cpuMonitor.getHistorySize() > 1) {
+ std::ostringstream collectedDataStream;
+ collectedDataStream << std::fixed << std::setprecision(1);
+ collectedDataStream << "\tMean core utilization: ";
+ for (double mean : cpuMonitor.getMeanCpuLoad()) {
+ collectedDataStream << mean * 100 << "% ";
+ }
+ collectedData.push_back(collectedDataStream.str());
+ }
+ if (distributionCpuEnabled) {
+ std::ostringstream collectedDataStream;
+ collectedDataStream << std::fixed << std::setprecision(1);
+ std::vector<double> meanCpuLoad = cpuMonitor.getMeanCpuLoad();
+ double mean = std::accumulate(meanCpuLoad.begin(), meanCpuLoad.end(), 0.0) / meanCpuLoad.size();
+ collectedDataStream << "\tMean CPU utilization: " << mean * 100 << "%";
+ collectedData.push_back(collectedDataStream.str());
+ }
+ if (memoryMonitor.getHistorySize() > 1) {
+ std::ostringstream collectedDataStream;
+ collectedDataStream << std::fixed << std::setprecision(1);
+ collectedDataStream << "\tMemory mean usage: " << memoryMonitor.getMeanMem() << " GiB";
+ collectedData.push_back(collectedDataStream.str());
+ collectedDataStream.str("");
+ collectedDataStream << "\tMean swap usage: " << memoryMonitor.getMeanSwap() << " GiB";
+ collectedData.push_back(collectedDataStream.str());
+ }
+
+ return collectedData;
+}
diff --git a/python/openvino/runtime/common/monitors/src/query_wrapper.cpp b/python/openvino/runtime/common/monitors/src/query_wrapper.cpp
new file mode 100644
index 0000000..5c238d1
--- /dev/null
+++ b/python/openvino/runtime/common/monitors/src/query_wrapper.cpp
@@ -0,0 +1,22 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "monitors/query_wrapper.h"
+
+#include <Windows.h>
+#include <system_error>
+
+QueryWrapper::QueryWrapper() {
+ PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query);
+ if (ERROR_SUCCESS != status) {
+ throw std::system_error(status, std::system_category(), "PdhOpenQuery() failed");
+ }
+}
+QueryWrapper::~QueryWrapper() {
+ PdhCloseQuery(query);
+}
+
+QueryWrapper::operator PDH_HQUERY() const {
+ return query;
+}