sled/3party/cppuprofile/lib/uprofileimpl.cpp
tqcq 719fecd4bc
All checks were successful
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 48s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 48s
feat add profiling
2024-03-16 22:56:10 +08:00

354 lines
8.6 KiB
C++

// 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 <cedric.chedaleux@orange.com> et al.
#include <chrono>
#include <fstream>
#include <iostream>
#include <sstream>
#if defined(__linux__)
#include <sys/sysinfo.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#elif defined(__APPLE__)
#include <sys/resource.h>
#include <sys/time.h>
#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<std::mutex> 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<float> 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<float>
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<std::string> &data)
{
std::lock_guard<std::mutex> 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<milliseconds>(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<unsigned long long>(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