Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(libsinsp): enable metrics collector on all platforms #1870

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions userspace/libsinsp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ add_library(
events/sinsp_events_ppm_sc.cpp
)

if(NOT WIN32
AND NOT APPLE
AND NOT EMSCRIPTEN
)
target_sources(sinsp PRIVATE linux/resource_utilization.cpp)
endif()

if(ENABLE_THREAD_POOL AND NOT EMSCRIPTEN)
target_sources(sinsp PRIVATE sinsp_thread_pool_bs.cpp)
endif()
Expand Down
283 changes: 283 additions & 0 deletions userspace/libsinsp/linux/resource_utilization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
#include <libsinsp/linux/resource_utilization.h>

#include <sys/times.h>

void linux_resource_utilization::get_rss_vsz_pss_total_memory_and_open_fds() {
FILE* f;
char filepath[512];
char line[512];

/*
* Get memory usage of the agent itself (referred to as calling process meaning /proc/self/)
*/

// No need for scap_get_host_root since we look at the agents' own process, accessible from
// it's own pid namespace (if applicable)
f = fopen("/proc/self/status", "r");
if(!f) {
return;

Check warning on line 18 in userspace/libsinsp/linux/resource_utilization.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/linux/resource_utilization.cpp#L18

Added line #L18 was not covered by tests
}

while(fgets(line, sizeof(line), f) != nullptr) {
if(strncmp(line, "VmSize:", 7) == 0) {
sscanf(line, "VmSize: %" SCNu32, &m_vsz); /* memory size returned in kb */
} else if(strncmp(line, "VmRSS:", 6) == 0) {
sscanf(line, "VmRSS: %" SCNu32, &m_rss); /* memory size returned in kb */
}
}
fclose(f);

// No need for scap_get_host_root since we look at the agents' own process, accessible from
// it's own pid namespace (if applicable)
f = fopen("/proc/self/smaps_rollup", "r");
if(!f) {
ASSERT(false);
return;
}

while(fgets(line, sizeof(line), f) != NULL) {
if(strncmp(line, "Pss:", 4) == 0) {
sscanf(line, "Pss: %" SCNu32, &m_pss); /* memory size returned in kb */
break;
}
}
fclose(f);

/*
* Get total host memory usage
*/

// Using scap_get_host_root since we look at the memory usage of the underlying host
snprintf(filepath, sizeof(filepath), "%s/proc/meminfo", scap_get_host_root());
f = fopen(filepath, "r");
if(!f) {
ASSERT(false);
return;
}

uint64_t mem_total, mem_free, mem_buff, mem_cache = 0;

while(fgets(line, sizeof(line), f) != NULL) {
if(strncmp(line, "MemTotal:", 9) == 0) {
sscanf(line, "MemTotal: %" SCNu64, &mem_total); /* memory size returned in kb */
} else if(strncmp(line, "MemFree:", 8) == 0) {
sscanf(line, "MemFree: %" SCNu64, &mem_free); /* memory size returned in kb */
} else if(strncmp(line, "Buffers:", 8) == 0) {
sscanf(line, "Buffers: %" SCNu64, &mem_buff); /* memory size returned in kb */
} else if(strncmp(line, "Cached:", 7) == 0) {
sscanf(line, "Cached: %" SCNu64, &mem_cache); /* memory size returned in kb */
}
}
fclose(f);
m_host_memory_used = mem_total - mem_free - mem_buff - mem_cache;

/*
* Get total number of allocated file descriptors (not all open files!)
* File descriptor is a data structure used by a program to get a handle on a file
*/

// Using scap_get_host_root since we look at the total open fds of the underlying host
snprintf(filepath, sizeof(filepath), "%s/proc/sys/fs/file-nr", scap_get_host_root());
f = fopen(filepath, "r");
if(!f) {
ASSERT(false);
return;
}
int matched_fds = fscanf(f, "%" SCNu64, &m_host_open_fds);
fclose(f);

if(matched_fds != 1) {
ASSERT(false);
return;
}
}

void linux_resource_utilization::get_cpu_usage_and_total_procs(double start_time) {
FILE* f;
char filepath[512];
char line[512];

struct tms time;
if(times(&time) == (clock_t)-1) {
return;

Check warning on line 102 in userspace/libsinsp/linux/resource_utilization.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/linux/resource_utilization.cpp#L102

Added line #L102 was not covered by tests
}

/* Number of clock ticks per second, often referred to as USER_HZ / jiffies. */
long hz = 100;
#ifdef _SC_CLK_TCK
if((hz = sysconf(_SC_CLK_TCK)) < 0) {
ASSERT(false);
hz = 100;

Check warning on line 110 in userspace/libsinsp/linux/resource_utilization.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/linux/resource_utilization.cpp#L109-L110

Added lines #L109 - L110 were not covered by tests
}
#endif
/* Current uptime of the host machine in seconds.
* /proc/uptime offers higher precision w/ 2 decimals.
*/

// Using scap_get_host_root since we look at the uptime of the underlying host
snprintf(filepath, sizeof(filepath), "%s/proc/uptime", scap_get_host_root());
f = fopen(filepath, "r");
if(!f) {
ASSERT(false);
return;
}

double machine_uptime_sec = 0;
int matched_uptime = fscanf(f, "%lf", &machine_uptime_sec);
fclose(f);

if(matched_uptime != 1) {
ASSERT(false);
return;
}

/*
* Get CPU usage of the agent itself (referred to as calling process meaning /proc/self/)
*/

/* Current utime is amount of processor time in user mode of calling process. Convert to
* seconds. */
double user_sec = (double)time.tms_utime / hz;

/* Current stime is amount of time the calling process has been scheduled in kernel mode.
* Convert to seconds. */
double system_sec = (double)time.tms_stime / hz;

/* CPU usage as percentage is computed by dividing the time the process uses the CPU by the
* currently elapsed time of the calling process. Compare to `ps` linux util. */
double elapsed_sec = machine_uptime_sec - start_time;
if(elapsed_sec > 0) {
m_cpu_usage_perc = (double)100.0 * (user_sec + system_sec) / elapsed_sec;
m_cpu_usage_perc = std::round(m_cpu_usage_perc * 10.0) / 10.0; // round to 1 decimal
}

/*
* Get total host CPU usage (all CPUs) as percentage and retrieve number of procs currently
* running.
*/

// Using scap_get_host_root since we look at the total CPU usage of the underlying host
snprintf(filepath, sizeof(filepath), "%s/proc/stat", scap_get_host_root());
f = fopen(filepath, "r");
if(!f) {
ASSERT(false);
return;
}

/* Need only first 7 columns of /proc/stat cpu line */
uint64_t user, nice, system, idle, iowait, irq, softirq = 0;
while(fgets(line, sizeof(line), f) != NULL) {
if(strncmp(line, "cpu ", 4) == 0) {
/* Always first line in /proc/stat file, unit: jiffies */
sscanf(line,
"cpu %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64
" %" SCNu64,
&user,
&nice,
&system,
&idle,
&iowait,
&irq,
&softirq);
} else if(strncmp(line, "procs_running ", 14) == 0) {
sscanf(line, "procs_running %" SCNu32, &m_host_procs_running);
break;
}
}
fclose(f);
auto sum = user + nice + system + idle + iowait + irq + softirq;
if(sum > 0) {
m_host_cpu_usage_perc = 100.0 - ((idle * 100.0) / sum);
m_host_cpu_usage_perc =
std::round(m_host_cpu_usage_perc * 10.0) / 10.0; // round to 1 decimal
}
}

std::vector<metrics_v2> linux_resource_utilization::to_metrics() {
std::vector<metrics_v2> metrics;
metrics.emplace_back(new_metric("cpu_usage_perc",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_D,
METRIC_VALUE_UNIT_PERC,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_cpu_usage_perc));
metrics.emplace_back(new_metric("memory_rss_kb",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U32,
METRIC_VALUE_UNIT_MEMORY_KIBIBYTES,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_rss));
metrics.emplace_back(new_metric("memory_vsz_kb",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U32,
METRIC_VALUE_UNIT_MEMORY_KIBIBYTES,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_vsz));
metrics.emplace_back(new_metric("memory_pss_kb",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U32,
METRIC_VALUE_UNIT_MEMORY_KIBIBYTES,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_pss));
metrics.emplace_back(new_metric("container_memory_used_bytes",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U64,
METRIC_VALUE_UNIT_MEMORY_BYTES,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_container_memory_used));
metrics.emplace_back(new_metric("host_cpu_usage_perc",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_D,
METRIC_VALUE_UNIT_PERC,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_host_cpu_usage_perc));
metrics.emplace_back(new_metric("host_memory_used_kb",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U32,
METRIC_VALUE_UNIT_MEMORY_KIBIBYTES,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_host_memory_used));
metrics.emplace_back(new_metric("host_procs_running",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U32,
METRIC_VALUE_UNIT_COUNT,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_host_procs_running));
metrics.emplace_back(new_metric("host_open_fds",
METRICS_V2_RESOURCE_UTILIZATION,
METRIC_VALUE_TYPE_U64,
METRIC_VALUE_UNIT_COUNT,
METRIC_VALUE_METRIC_TYPE_NON_MONOTONIC_CURRENT,
m_host_open_fds));

return metrics;
}

void linux_resource_utilization::get_container_memory_used() {
/* In Kubernetes `container_memory_working_set_bytes` is the memory measure the OOM killer uses
* and values from `/sys/fs/cgroup/memory/memory.usage_in_bytes` are close enough.
*
* Please note that `kubectl top pod` numbers would reflect the sum of containers in a pod and
* typically libs clients (e.g. Falco) pods contain sidekick containers that use memory as well.
* This metric accounts only for the container with the security monitoring agent running.
*/
const char* filepath = getenv(SINSP_AGENT_CGROUP_MEM_PATH_ENV_VAR);
if(filepath == nullptr) {
// No need for scap_get_host_root since we look at the container pid namespace (if
// applicable) Known collision for VM memory usage, but this default value is configurable
filepath = "/sys/fs/cgroup/memory/memory.usage_in_bytes";
}

FILE* f = fopen(filepath, "r");
if(!f) {
return;
}

/* memory size returned in bytes */
int fscanf_matched = fscanf(f, "%" SCNu64, &m_container_memory_used);

Check warning on line 277 in userspace/libsinsp/linux/resource_utilization.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/linux/resource_utilization.cpp#L277

Added line #L277 was not covered by tests
if(fscanf_matched != 1) {
m_container_memory_used = 0;

Check warning on line 279 in userspace/libsinsp/linux/resource_utilization.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/linux/resource_utilization.cpp#L279

Added line #L279 was not covered by tests
}

fclose(f);

Check warning on line 282 in userspace/libsinsp/linux/resource_utilization.cpp

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/linux/resource_utilization.cpp#L282

Added line #L282 was not covered by tests
}
42 changes: 42 additions & 0 deletions userspace/libsinsp/linux/resource_utilization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <libsinsp/metrics_collector.h>

class linux_resource_utilization : libs::metrics::libsinsp_metrics {
public:
linux_resource_utilization(double start_time) {
get_cpu_usage_and_total_procs(start_time);
get_rss_vsz_pss_total_memory_and_open_fds();
get_container_memory_used();
}

std::vector<metrics_v2> to_metrics();

private:
void get_cpu_usage_and_total_procs(double start_time);
void get_rss_vsz_pss_total_memory_and_open_fds();
void get_container_memory_used();

double m_cpu_usage_perc{}; ///< Current CPU usage, `ps` util like calculation for the calling
///< process (/proc/self), unit: percentage of one CPU.

uint32_t m_rss{}; ///< Current RSS (Resident Set Size), calculated based on /proc/self/status
///< info, unit: kb.
uint32_t m_vsz{}; ///< Current VSZ (Virtual Memory Size), calculated based on /proc/self/status
///< info, unit: kb.
uint32_t m_pss{}; ///< Current PSS (Proportional Set Size), calculated based on
///< /proc/self/smaps_rollup info, unit: kb.

uint64_t m_container_memory_used{}; ///< Cgroup current memory used, default Kubernetes
///< /sys/fs/cgroup/memory/memory.usage_in_bytes, unit:
///< bytes.

double m_host_cpu_usage_perc{}; ///< Current total host CPU usage (all CPUs), calculated based
///< on ${HOST_ROOT}/proc/stat info, unit: percentage.
uint64_t m_host_memory_used{}; ///< Current total memory used out of available host memory,
///< calculated based on ${HOST_ROOT}/proc/meminfo info, unit:
///< kb.
uint32_t m_host_procs_running{}; ///< Number of processes currently running on CPUs on the
///< host, retrieved from ${HOST_ROOT}/proc/stat line
///< `procs_running`, unit: count.
uint64_t m_host_open_fds{}; ///< Number of allocated fds on the host, retrieved from
///< ${HOST_ROOT}/proc/sys/fs/file-nr, unit: count.
};
Loading
Loading