Skip to content

Commit

Permalink
fix(libsinsp): enable metrics collector on all platforms
Browse files Browse the repository at this point in the history
Signed-off-by: Gianmatteo Palmieri <[email protected]>
  • Loading branch information
mrgian authored and poiana committed Dec 12, 2024
1 parent c17f533 commit 9e3fb93
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 350 deletions.
7 changes: 7 additions & 0 deletions userspace/libsinsp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,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;
}

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;
}

/* 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;
}
#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);
if(fscanf_matched != 1) {
m_container_memory_used = 0;
}

fclose(f);
}
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

0 comments on commit 9e3fb93

Please sign in to comment.