Commit 5d1802d8 authored by Dominik Charousset's avatar Dominik Charousset

Backport process_open_fds metric

parent 5498fe5e
......@@ -31,6 +31,7 @@ private:
telemetry::int_gauge* rss_ = nullptr;
telemetry::int_gauge* vms_ = nullptr;
telemetry::dbl_gauge* cpu_ = nullptr;
telemetry::int_gauge* fds_ = nullptr;
};
} // namespace caf::telemetry::importer
......@@ -10,7 +10,7 @@
// -- detect supported platforms -----------------------------------------------
#if defined(CAF_MACOS) || defined(CAF_LINUX)
#if defined(CAF_MACOS) || defined(CAF_LINUX) || defined(CAF_NET_BSD)
# define CAF_HAS_PROCESS_METRICS
#endif
......@@ -21,18 +21,19 @@
namespace {
struct sys_stats {
int64_t rss;
int64_t vms;
double cpu_time;
int64_t rss = 0;
int64_t vms = 0;
double cpu_time = 0.0;
int64_t fds = 0;
};
sys_stats read_sys_stats();
static constexpr bool platform_supported_v = true;
constexpr bool platform_supported_v = true;
template <class CpuPtr, class RssPtr, class VmsPtr>
template <class CpuPtr, class RssPtr, class VmsPtr, class FdsPtr>
void sys_stats_init(caf::telemetry::metric_registry& reg, RssPtr& rss,
VmsPtr& vms, CpuPtr& cpu) {
VmsPtr& vms, CpuPtr& cpu, FdsPtr& fds) {
rss = reg.gauge_singleton("process", "resident_memory",
"Resident memory size.", "bytes");
vms = reg.gauge_singleton("process", "virtual_memory", "Virtual memory size.",
......@@ -40,15 +41,19 @@ void sys_stats_init(caf::telemetry::metric_registry& reg, RssPtr& rss,
cpu = reg.gauge_singleton<double>("process", "cpu",
"Total user and system CPU time spent.",
"seconds", true);
fds = reg.gauge_singleton("process", "open_fds",
"Number of open file descriptors.");
}
void update_impl(caf::telemetry::int_gauge* rss_gauge,
caf::telemetry::int_gauge* vms_gauge,
caf::telemetry::dbl_gauge* cpu_gauge) {
auto [rss, vmsize, cpu] = read_sys_stats();
caf::telemetry::dbl_gauge* cpu_gauge,
caf::telemetry::int_gauge* fds_gauge) {
auto [rss, vmsize, cpu, fds] = read_sys_stats();
rss_gauge->value(rss);
vms_gauge->value(vmsize);
cpu_gauge->value(cpu);
fds_gauge->value(fds);
}
} // namespace
......@@ -57,7 +62,7 @@ void update_impl(caf::telemetry::int_gauge* rss_gauge,
namespace {
static constexpr bool platform_supported_v = false;
constexpr bool platform_supported_v = false;
template <class... Ts>
void sys_stats_init(Ts&&...) {
......@@ -77,14 +82,16 @@ void update_impl(Ts&&...) {
#ifdef CAF_MACOS
# include <libproc.h>
# include <mach/mach.h>
# include <mach/task.h>
# include <sys/resource.h>
# include <unistd.h>
namespace {
sys_stats read_sys_stats() {
sys_stats result{0, 0, 0};
sys_stats result;
// Fetch memory usage.
{
mach_task_basic_info info;
......@@ -110,6 +117,20 @@ sys_stats read_sys_stats() {
result.cpu_time += ceil(info.system_time.microseconds / 1000.0) / 1000.0;
}
}
// Fetch open file handles.
{
// proc_pidinfo is undocumented, but this is what lsof also uses.
auto suggested_buf_size = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0,
nullptr, 0);
if (suggested_buf_size > 0) {
auto buf_size = suggested_buf_size;
auto buf = malloc(buf_size); // TODO: could be thread-local
auto res = proc_pidinfo(getpid(), PROC_PIDLISTFDS, 0, buf, buf_size);
free(buf);
if (res > 0)
result.fds = res / sizeof(proc_fdinfo);
}
}
return result;
}
......@@ -117,19 +138,31 @@ sys_stats read_sys_stats() {
#endif // CAF_MACOS
// -- Linux-specific scraping logic --------------------------------------------
// -- helper for caching the result of a syscall -------------------------------
#ifdef CAF_LINUX
#if defined(CAF_LINUX) || defined(CAF_NET_BSD)
# include <cstdio>
# include <dirent.h>
# include <unistd.h>
namespace {
std::atomic<long> global_ticks_per_second;
std::atomic<long> global_page_size;
int64_t count_entries_in_directory(const char* path) {
int64_t result = 0;
if (auto dptr = opendir(path); dptr != nullptr) {
for (auto entry = readdir(dptr); entry != nullptr; entry = readdir(dptr)) {
auto fname = entry->d_name;
if (strcmp(".", fname) != 0 && strcmp("..", fname) != 0)
++result;
}
closedir(dptr);
}
return result;
}
/// Caches the result from a `sysconf` call in a cache variable to avoid
/// frequent syscalls. Sets `cache_var` to -1 in case of an error. Initially,
/// `cache_var` must be 0 and we assume a successful syscall would always return
/// some value > 0. If `cache_var` is > 0 then this function simply returns the
/// cached value directly.
bool load_system_setting(std::atomic<long>& cache_var, long& var, int name,
[[maybe_unused]] const char* pretty_name) {
var = cache_var.load();
......@@ -143,9 +176,10 @@ bool load_system_setting(std::atomic<long>& cache_var, long& var, int name,
var = -1;
cache_var = var;
return false;
} else {
cache_var = var;
return true;
}
cache_var = var;
return true;
default:
return true;
}
......@@ -154,8 +188,23 @@ bool load_system_setting(std::atomic<long>& cache_var, long& var, int name,
# define TRY_LOAD(varname, confname) \
load_system_setting(global_##varname, varname, confname, #confname)
#endif
// -- Linux-specific scraping logic --------------------------------------------
#ifdef CAF_LINUX
# include <cstdio>
# include <dirent.h>
namespace {
std::atomic<long> global_ticks_per_second;
std::atomic<long> global_page_size;
sys_stats read_sys_stats() {
sys_stats result{0, 0, 0};
sys_stats result;
long ticks_per_second = 0;
long page_size = 0;
if (!TRY_LOAD(ticks_per_second, _SC_CLK_TCK)
......@@ -205,6 +254,42 @@ sys_stats read_sys_stats() {
result.cpu_time += stime_ticks;
result.cpu_time /= ticks_per_second;
}
result.fds = count_entries_in_directory("/proc/self/fd");
return result;
}
} // namespace
// -- NetBSD-specific scraping logic -------------------------------------------
#elif defined(CAF_NET_BSD)
# include <sys/sysctl.h>
namespace {
std::atomic<long> global_page_size;
sys_stats read_sys_stats() {
auto result = sys_stats;
auto kip2 = kinfo_proc2{};
auto kip2_size = sizeof(kip2);
int mib[6] = {
CTL_KERN, KERN_PROC2, KERN_PROC_PID, getpid(), static_cast<int>(kip2_size),
1,
};
long page_size = 0;
if (!TRY_LOAD(page_size, _SC_PAGE_SIZE))
return result;
if (sysctl(mib, 6, &kip2, &kip2_size, nullptr, size_t{0}) != 0) {
CAF_LOG_ERROR("failed to call sysctl in read_sys_stats");
global_page_size = -1;
return result;
}
result.rss = static_cast<int64_t>(kip2.p_vm_rssize) * page_size;
result.vms = static_cast<int64_t>(kip2.p_vm_vsize) * page_size;
result.cpu_time = kip2.p_rtime_sec;
result.cpu_time += static_cast<double>(kip2.p_rtime_usec) / 1000000;
return result;
}
......@@ -215,7 +300,7 @@ sys_stats read_sys_stats() {
namespace caf::telemetry::importer {
process::process(metric_registry& reg) {
sys_stats_init(reg, rss_, vms_, cpu_);
sys_stats_init(reg, rss_, vms_, cpu_, fds_);
}
bool process::platform_supported() noexcept {
......@@ -223,7 +308,7 @@ bool process::platform_supported() noexcept {
}
void process::update() {
update_impl(rss_, vms_, cpu_);
update_impl(rss_, vms_, cpu_, fds_);
}
} // namespace caf::telemetry::importer
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