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

Implement process metrics on Linux

parent 025fd322
......@@ -24,6 +24,16 @@
#include "caf/telemetry/dbl_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
# include <mach/mach.h>
# include <mach/task.h>
......@@ -31,31 +41,33 @@
# define HAS_PROCESS_METRICS
namespace {
std::pair<int64_t, int64_t> get_mem_usage() {
mach_task_basic_info info;
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,
&count)
!= KERN_SUCCESS) {
return {0, 0};
sys_stats read_sys_stats() {
sys_stats result{0, 0, 0};
// Fetch memory usage.
{
mach_task_basic_info info;
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,
&count)
== KERN_SUCCESS) {
result.rss = info.resident_size;
result.vmsize = info.virtual_size;
}
}
// Fetch CPU time.
{
task_thread_times_info info;
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,
&count)
== KERN_SUCCESS) {
// Round to milliseconds.
result.cpu_time += info.user_time.seconds;
result.cpu_time += ceil(info.user_time.microseconds / 1000.0) / 1000.0;
result.cpu_time += info.system_time.seconds;
result.cpu_time += ceil(info.system_time.microseconds / 1000.0) / 1000.0;
}
}
return {static_cast<int64_t>(info.resident_size),
static_cast<int64_t>(info.virtual_size)};
}
double get_cpu_time() {
task_thread_times_info info;
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,
&count)
!= KERN_SUCCESS)
return 0;
// Round to milliseconds.
double result = 0;
result += info.user_time.seconds;
result += ceil(info.user_time.microseconds / 1000.0) / 1000.0;
result += info.system_time.seconds;
result += ceil(info.system_time.microseconds / 1000.0) / 1000.0;
return result;
}
......@@ -63,17 +75,93 @@ double get_cpu_time() {
#endif // CAF_MACOS
#ifdef CAF_LINUX
# include <sys/resource.h>
# include <cstdio>
# include <unistd.h>
# define HAS_PROCESS_METRICS
std::pair<int64_t, int64_t> get_mem_usage() {
return {0, 0};
namespace {
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;
if (getrusage(RUSAGE_SELF, &usage) != 0)
return 0;
return static_cast<int64_t>(usage.ru_utime.tv_sec + usage.ru_stime.tv_sec);
# define TRY_LOAD(varname, confname) \
load_system_setting(global_##varname, varname, confname, #confname)
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
namespace caf::detail {
......@@ -193,10 +281,10 @@ void prometheus_broker::scrape() {
if (last_scrape_ >= now)
return;
last_scrape_ = now;
cpu_time_->value(get_cpu_time());
auto [res, virt] = get_mem_usage();
mem_size_->value(res);
virt_mem_size_->value(virt);
auto [rss, vmsize, cpu_time] = read_sys_stats();
mem_size_->value(rss);
virt_mem_size_->value(vmsize);
cpu_time_->value(cpu_time);
#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