// Software Name : cppuprofile // SPDX-FileCopyrightText: Copyright (c) 2022 Orange // SPDX-License-Identifier: BSD-3-Clause // // This software is distributed under the BSD License; // see the LICENSE file for more details. // // Author: Cédric CHEDALEUX et al. #include #include #include #include #if defined(__linux__) #include #include #elif defined(_WIN32) #include #elif defined(__APPLE__) #include #include #endif #include "uprofileimpl.h" using namespace std::chrono; namespace uprofile { UProfileImpl *UProfileImpl::m_uprofiler = NULL; UProfileImpl::UProfileImpl() : m_tsUnit(TimestampUnit::EPOCH_TIME), m_gpuMonitor(NULL) {} UProfileImpl * UProfileImpl::getInstance() { if (!m_uprofiler) { m_uprofiler = new UProfileImpl; } return m_uprofiler; } void UProfileImpl::destroyInstance() { delete m_uprofiler; m_uprofiler = NULL; } UProfileImpl::~UProfileImpl() { removeGPUMonitor(); } void UProfileImpl::start(const char *file) { m_file.open(file, std::ios::out); if (!m_file.is_open()) { std::cerr << "Failed to open file: " << file << std::endl; } } void UProfileImpl::addGPUMonitor(IGPUMonitor *monitor) { if (!monitor) { std::cerr << "Invalid GPU Monitor" << std::endl; return; } if (!m_gpuMonitor) { removeGPUMonitor(); } m_gpuMonitor = monitor; } void UProfileImpl::removeGPUMonitor() { delete m_gpuMonitor; m_gpuMonitor = NULL; } void UProfileImpl::timeBegin(const std::string &title) { std::lock_guard guard(m_stepsMutex); m_steps.insert(make_pair(title, getTimestamp())); } void UProfileImpl::timeEnd(const std::string &title) { unsigned long long beginTimestamp = 0; // Find step in the map m_stepsMutex.lock(); auto it = m_steps.find(title); bool found = it != m_steps.end(); if (found) { beginTimestamp = (*it).second; m_steps.erase(it); } m_stepsMutex.unlock(); if (found) { write(ProfilingType::TIME_EXEC, {std::to_string(beginTimestamp), title}); } else { write(ProfilingType::TIME_EVENT, {title}); } } void UProfileImpl::startProcessMemoryMonitoring(int period) { m_processMemoryMonitorTimer.setInterval(period); m_processMemoryMonitorTimer.setTimeout([=]() { dumpProcessMemory(); }); m_processMemoryMonitorTimer.start(); } void UProfileImpl::startSystemMemoryMonitoring(int period) { m_systemMemoryMonitorTimer.setInterval(period); m_systemMemoryMonitorTimer.setTimeout([=]() { dumpSystemMemory(); }); m_systemMemoryMonitorTimer.start(); } void UProfileImpl::startCPUUsageMonitoring(int period) { m_cpuMonitorTimer.setInterval(period); m_cpuMonitorTimer.setTimeout([=]() { dumpCpuUsage(); }); m_cpuMonitorTimer.start(); } void UProfileImpl::startGPUUsageMonitoring(int period) { if (!m_gpuMonitor) { std::cerr << "Cannot monitor GPU usage: no GPUMonitor set!" << std::endl; return; } m_gpuMonitor->start(period); m_gpuUsageMonitorTimer.setInterval(period); m_gpuUsageMonitorTimer.setTimeout([=]() { dumpGpuUsage(); }); m_gpuUsageMonitorTimer.start(); } void UProfileImpl::startGPUMemoryMonitoring(int period) { if (!m_gpuMonitor) { std::cerr << "Cannot monitor GPU memory: no GPUMonitor set!" << std::endl; return; } m_gpuMonitor->start(period); m_gpuMemoryMonitorTimer.setInterval(period); m_gpuMemoryMonitorTimer.setTimeout([=]() { dumpGpuMemory(); }); m_gpuMemoryMonitorTimer.start(); } void UProfileImpl::dumpProcessMemory() { int rss = 0, shared = 0; getProcessMemory(rss, shared); write(ProfilingType::PROCESS_MEMORY, {std::to_string(rss), std::to_string(shared)}); } void UProfileImpl::dumpSystemMemory() { int total = 0, available = 0, free = 0; getSystemMemory(total, available, free); write(ProfilingType::SYSTEM_MEMORY, {std::to_string(total), std::to_string(available), std::to_string(free)}); } void UProfileImpl::dumpCpuUsage() { vector cpuLoads = m_cpuMonitor.getUsage(); for (size_t index = 0; index < cpuLoads.size(); ++index) { write(ProfilingType::CPU, {std::to_string(index), std::to_string(cpuLoads.at(index))}); } } void UProfileImpl::dumpGpuUsage() { if (!m_gpuMonitor || !m_gpuMonitor->watching()) { return; } float usage = m_gpuMonitor->getUsage(); write(ProfilingType::GPU_USAGE, {std::to_string(usage)}); } void UProfileImpl::dumpGpuMemory() { if (!m_gpuMonitor || !m_gpuMonitor->watching()) { return; } int usedMem, totalMem; m_gpuMonitor->getMemory(usedMem, totalMem); write(ProfilingType::GPU_MEMORY, {std::to_string(usedMem), std::to_string(totalMem)}); } vector UProfileImpl::getInstantCpuUsage() { // To get instaneous CPU usage, we should wait at least one unit between two polling (aka: 100 ms) m_cpuMonitor.getUsage(); this_thread::sleep_for(std::chrono::milliseconds(100)); return m_cpuMonitor.getUsage(); } void UProfileImpl::stop() { m_processMemoryMonitorTimer.stop(); m_systemMemoryMonitorTimer.stop(); m_cpuMonitorTimer.stop(); m_gpuUsageMonitorTimer.stop(); m_gpuMemoryMonitorTimer.stop(); if (m_gpuMonitor) { m_gpuMonitor->stop(); } m_file.close(); } void UProfileImpl::setTimestampUnit(TimestampUnit tsUnit) { m_tsUnit = tsUnit; } void UProfileImpl::write(ProfilingType type, const std::list &data) { std::lock_guard guard(m_fileMutex); if (m_file.is_open()) { const char csvSeparator = ';'; std::string strType; switch (type) { case ProfilingType::TIME_EXEC: strType = "time_exec"; break; case ProfilingType::TIME_EVENT: strType = "time_event"; break; case ProfilingType::PROCESS_MEMORY: strType = "proc_mem"; break; case ProfilingType::SYSTEM_MEMORY: strType = "sys_mem"; break; case ProfilingType::CPU: strType = "cpu"; break; case ProfilingType::GPU_USAGE: strType = "gpu"; break; case ProfilingType::GPU_MEMORY: strType = "gpu_mem"; break; default: strType = "undefined"; break; } m_file << strType.c_str() << csvSeparator << getTimestamp(); for (auto it = data.cbegin(); it != data.cend(); ++it) { m_file << csvSeparator << *it; } m_file << "\n"; m_file.flush(); } } unsigned long long UProfileImpl::getTimestamp() const { return (m_tsUnit == TimestampUnit::EPOCH_TIME ? getEpochTime() : getTimeSinceBoot()); } unsigned long long UProfileImpl::getEpochTime() { return duration_cast(system_clock::now().time_since_epoch()).count(); } unsigned long long UProfileImpl::getTimeSinceBoot() { #if defined(__linux__) double uptime_seconds; if (std::ifstream("/proc/uptime", std::ios::in) >> uptime_seconds) { return static_cast(uptime_seconds * 1000.0); } return 0; #elif defined(_WIN32) return GetTickCount64(); #else return 0; #endif } void UProfileImpl::getSystemMemory(int &totalMem, int &availableMem, int &freeMem) { #if defined(__linux__) // /proc/meminfo returns the dump here: // MemTotal: 515164 kB // MemFree: 7348 kB // MemAvailable: 7348 kB ifstream meminfo("/proc/meminfo"); string line; while (std::getline(meminfo, line)) { if (line.find("MemTotal") != std::string::npos) { stringstream ls(line); ls.ignore(256, ' '); ls >> totalMem; } else if (line.find("MemFree") != std::string::npos) { stringstream ls(line); ls.ignore(256, ' '); ls >> freeMem; } else if (line.find("MemAvailable") != std::string::npos) { { stringstream ls(line); ls.ignore(256, ' '); ls >> availableMem; break; } } } #endif } void UProfileImpl::getProcessMemory(int &rss, int &shared) { #if defined(__linux__) int tSize = 0, resident = 0, share = 0; ifstream buffer("/proc/self/statm"); buffer >> tSize >> resident >> share; buffer.close(); long page_size_kb = getpagesize() / 1024; rss = resident * page_size_kb; shared = share * page_size_kb; #elif defined(__APPLE__) struct rusage usage; if (0 == getrusage(RUSAGE_SELF, &usage)) { rss = usage.ru_maxrss; shared = usage.ru_ixrss; } #endif } }// namespace uprofile