Commit 94e59124 authored by Dominik Charousset's avatar Dominik Charousset

Implement process metrics on Linux

parent 025fd322
...@@ -24,6 +24,16 @@ ...@@ -24,6 +24,16 @@
#include "caf/telemetry/dbl_gauge.hpp" #include "caf/telemetry/dbl_gauge.hpp"
#include "caf/telemetry/int_gauge.hpp" #include "caf/telemetry/int_gauge.hpp"
namespace {
struct [[maybe_unused]] sys_stats {
int64_t rss;
int64_t vmsize;
double cpu_time;
};
} // namespace
#ifdef CAF_MACOS #ifdef CAF_MACOS
# include <mach/mach.h> # include <mach/mach.h>
# include <mach/task.h> # include <mach/task.h>
...@@ -31,31 +41,33 @@ ...@@ -31,31 +41,33 @@
# define HAS_PROCESS_METRICS # define HAS_PROCESS_METRICS
namespace { namespace {
std::pair<int64_t, int64_t> get_mem_usage() { sys_stats read_sys_stats() {
sys_stats result{0, 0, 0};
// Fetch memory usage.
{
mach_task_basic_info info; mach_task_basic_info info;
mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT; mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) &info, if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) &info,
&count) &count)
!= KERN_SUCCESS) { == KERN_SUCCESS) {
return {0, 0}; result.rss = info.resident_size;
result.vmsize = info.virtual_size;
} }
return {static_cast<int64_t>(info.resident_size), }
static_cast<int64_t>(info.virtual_size)}; // Fetch CPU time.
} {
double get_cpu_time() {
task_thread_times_info info; task_thread_times_info info;
mach_msg_type_number_t count = TASK_THREAD_TIMES_INFO_COUNT; mach_msg_type_number_t count = TASK_THREAD_TIMES_INFO_COUNT;
if (task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t) &info, if (task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t) &info,
&count) &count)
!= KERN_SUCCESS) == KERN_SUCCESS) {
return 0;
// Round to milliseconds. // Round to milliseconds.
double result = 0; result.cpu_time += info.user_time.seconds;
result += info.user_time.seconds; result.cpu_time += ceil(info.user_time.microseconds / 1000.0) / 1000.0;
result += ceil(info.user_time.microseconds / 1000.0) / 1000.0; result.cpu_time += info.system_time.seconds;
result += info.system_time.seconds; result.cpu_time += ceil(info.system_time.microseconds / 1000.0) / 1000.0;
result += ceil(info.system_time.microseconds / 1000.0) / 1000.0; }
}
return result; return result;
} }
...@@ -63,17 +75,93 @@ double get_cpu_time() { ...@@ -63,17 +75,93 @@ double get_cpu_time() {
#endif // CAF_MACOS #endif // CAF_MACOS
#ifdef CAF_LINUX #ifdef CAF_LINUX
# include <sys/resource.h> # include <cstdio>
# include <unistd.h>
# define HAS_PROCESS_METRICS # define HAS_PROCESS_METRICS
std::pair<int64_t, int64_t> get_mem_usage() { namespace {
return {0, 0};
std::atomic<long> global_ticks_per_second;
std::atomic<long> global_page_size;
bool load_system_setting(std::atomic<long>& cache_var, long& var, int name,
[[maybe_unused]] const char* pretty_name) {
var = cache_var.load();
switch (var) {
case -1:
return false;
case 0:
var = sysconf(name);
if (var <= 0) {
CAF_LOG_ERROR("failed to read" << pretty_name << "from sysconf");
var = -1;
cache_var = var;
return false;
}
cache_var = var;
return true;
default:
return true;
}
} }
int64_t get_cpu_time() {
rusage usage; # define TRY_LOAD(varname, confname) \
if (getrusage(RUSAGE_SELF, &usage) != 0) load_system_setting(global_##varname, varname, confname, #confname)
return 0;
return static_cast<int64_t>(usage.ru_utime.tv_sec + usage.ru_stime.tv_sec); sys_stats read_sys_stats() {
sys_stats result{0, 0, 0};
long ticks_per_second = 0;
long page_size = 0;
if (!TRY_LOAD(ticks_per_second, _SC_CLK_TCK)
|| !TRY_LOAD(page_size, _SC_PAGE_SIZE))
return result;
if (auto f = fopen("/proc/self/stat", "r")) {
long unsigned utime_ticks = 0;
long unsigned stime_ticks = 0;
long unsigned vmsize_bytes = 0;
long rss_pages = 0;
auto rd = fscanf(f,
"%*d " // 1. PID
"%*s " // 2. Executable
"%*c " // 3. State
"%*d " // 4. Parent PID
"%*d " // 5. Process group ID
"%*d " // 6. Session ID
"%*d " // 7. Controlling terminal
"%*d " // 8. Foreground process group ID
"%*u " // 9. Flags
"%*u " // 10. Number of minor faults
"%*u " // 11. Number of minor faults of waited-for children
"%*u " // 12. Number of major faults
"%*u " // 13. Number of major faults of waited-for children
"%lu " // 14. CPU user time in ticks
"%lu " // 15. CPU kernel time in ticks
"%*d " // 16. CPU user time of waited-for children
"%*d " // 17. CPU kernel time of waited-for children
"%*d " // 18. Priority
"%*d " // 19. Nice value
"%*d " // 20. Num threads
"%*d " // 21. Obsolete since 2.6
"%*u " // 22. Time the process started after system boot
"%lu " // 23. Virtual memory size in bytes
"%ld", // 24. Resident set size in pages
&utime_ticks, &stime_ticks, &vmsize_bytes, &rss_pages);
fclose(f);
if (rd != 4) {
CAF_LOG_ERROR("failed to read content of /proc/self/stat");
global_ticks_per_second = -1;
global_page_size = -1;
return result;
}
result.rss = static_cast<int64_t>(rss_pages) * page_size;
result.vmsize = static_cast<int64_t>(vmsize_bytes);
result.cpu_time = utime_ticks;
result.cpu_time += stime_ticks;
result.cpu_time /= ticks_per_second;
}
return result;
} }
} // namespace
#endif // CAF_LINUX #endif // CAF_LINUX
namespace caf::detail { namespace caf::detail {
...@@ -193,10 +281,10 @@ void prometheus_broker::scrape() { ...@@ -193,10 +281,10 @@ void prometheus_broker::scrape() {
if (last_scrape_ >= now) if (last_scrape_ >= now)
return; return;
last_scrape_ = now; last_scrape_ = now;
cpu_time_->value(get_cpu_time()); auto [rss, vmsize, cpu_time] = read_sys_stats();
auto [res, virt] = get_mem_usage(); mem_size_->value(rss);
mem_size_->value(res); virt_mem_size_->value(vmsize);
virt_mem_size_->value(virt); cpu_time_->value(cpu_time);
#endif // HAS_PROCESS_METRICS #endif // HAS_PROCESS_METRICS
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment