Skip to content

Commit

Permalink
refactor(libscap): move linux metrics collection logic to scap platform
Browse files Browse the repository at this point in the history
Signed-off-by: Gianmatteo Palmieri <mail@gian.im>
  • Loading branch information
mrgian committed Jul 19, 2024
1 parent f7b1055 commit 38e4e48
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 288 deletions.
4 changes: 4 additions & 0 deletions userspace/libscap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ if (DEFINED SCAP_BPF_PROGS_TAIL_CALLED_MAX)
add_definitions(-DBPF_PROGS_TAIL_CALLED_MAX=${SCAP_BPF_PROGS_TAIL_CALLED_MAX})
endif()

if(NOT DEFINED SCAP_AGENT_CGROUP_MEM_PATH_ENV_VAR)
set(SCAP_AGENT_CGROUP_MEM_PATH_ENV_VAR "AGENT_CGROUP_MEM_PATH")
endif()
add_definitions(-DSCAP_AGENT_CGROUP_MEM_PATH_ENV_VAR="${SCAP_AGENT_CGROUP_MEM_PATH_ENV_VAR}")

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scap_strl_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/scap_strl_config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scap_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/scap_config.h)
Expand Down
243 changes: 243 additions & 0 deletions userspace/libscap/linux/scap_linux_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ limitations under the License.
#include <sys/stat.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <sys/times.h>
#include <unistd.h>
#include <math.h>

static int32_t scap_linux_close_platform(struct scap_platform* platform)
{
Expand Down Expand Up @@ -121,6 +123,246 @@ static const struct scap_platform_vtable scap_linux_platform_vtable = {
.free_platform = scap_linux_free_platform,
};

static void scap_linux_get_rss_vsz_pss_total_memory_and_open_fds(uint32_t *rss, uint32_t *vsz, uint32_t *pss, uint64_t *host_memory_used, uint64_t *host_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) != NULL)
{
if(strncmp(line, "VmSize:", 7) == 0)
{
sscanf(line, "VmSize: %" SCNu32, vsz); /* memory size returned in kb */
}
else if(strncmp(line, "VmRSS:", 6) == 0)
{
sscanf(line, "VmRSS: %" SCNu32, 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)
{
return;
}

while(fgets(line, sizeof(line), f) != NULL)
{
if(strncmp(line, "Pss:", 4) == 0)
{
sscanf(line, "Pss: %" SCNu32, 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)
{
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);
*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)
{
return;
}
int matched_fds = fscanf(f, "%" SCNu64, host_open_fds);
fclose(f);

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

static void scap_linux_get_cpu_usage_and_total_procs(double start_time, double *cpu_usage_perc, double *host_cpu_usage_perc, uint32_t *host_procs_running)
{
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)
{
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)
{
return;
}

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

if (matched_uptime != 1) {
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)
{
*cpu_usage_perc = (double)100.0 * (user_sec + system_sec) / elapsed_sec;
*cpu_usage_perc = round(*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)
{
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, host_procs_running);
break;
}
}
fclose(f);
uint64_t sum = user + nice + system + idle + iowait + irq + softirq;
if (sum > 0)
{
*host_cpu_usage_perc = 100.0 - ((idle * 100.0) / sum);
*host_cpu_usage_perc = round(*host_cpu_usage_perc * 10.0) / 10.0; // round to 1 decimal
}
}

static void scap_linux_get_container_memory_used(uint64_t *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(SCAP_AGENT_CGROUP_MEM_PATH_ENV_VAR);
if (filepath == NULL)
{
// 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, container_memory_used);
if (fscanf_matched != 1)
{
*container_memory_used = 0;
}

fclose(f);

return;
}

struct scap_metrics_vtable scap_linux_metrics_vtable = {
.get_rss_vsz_pss_total_memory_and_open_fds = scap_linux_get_rss_vsz_pss_total_memory_and_open_fds,
.get_cpu_usage_and_total_procs = scap_linux_get_cpu_usage_and_total_procs,
.get_container_memory_used = scap_linux_get_container_memory_used,
};

struct scap_platform* scap_linux_alloc_platform(proc_entry_callback proc_callback, void* proc_callback_context)
{
struct scap_linux_platform* platform = calloc(1, sizeof(*platform));
Expand All @@ -132,6 +374,7 @@ struct scap_platform* scap_linux_alloc_platform(proc_entry_callback proc_callbac

struct scap_platform* generic = &platform->m_generic;
generic->m_vtable = &scap_linux_platform_vtable;
generic->m_metrics_vtable = &scap_linux_metrics_vtable;

init_proclist(&generic->m_proclist, proc_callback, proc_callback_context);

Expand Down
29 changes: 29 additions & 0 deletions userspace/libscap/scap_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,34 @@ struct scap_platform_vtable scap_generic_platform_vtable = {
.free_platform = scap_generic_free_platform,
};

static void scap_generic_get_rss_vsz_pss_total_memory_and_open_fds(uint32_t *rss, uint32_t *vsz, uint32_t *pss, uint64_t *host_memory_used, uint64_t *host_open_fds)
{
*rss = 0;
*vsz = 0;
*pss = 0;
*host_memory_used = 0;
*host_open_fds = 0;
}

static void scap_generic_get_cpu_usage_and_total_procs(double start_time, double *cpu_usage_perc, double *host_cpu_usage_perc, uint32_t *host_procs_running)
{
*cpu_usage_perc = 0;
*host_cpu_usage_perc = 0;
*host_procs_running = 0;
}

static void scap_generic_get_container_memory_used(uint64_t *container_memory_used)
{
*container_memory_used = 0;
return;
}

struct scap_metrics_vtable scap_generic_metrics_vtable = {
.get_rss_vsz_pss_total_memory_and_open_fds = scap_generic_get_rss_vsz_pss_total_memory_and_open_fds,
.get_cpu_usage_and_total_procs = scap_generic_get_cpu_usage_and_total_procs,
.get_container_memory_used = scap_generic_get_container_memory_used,
};

struct scap_platform* scap_generic_alloc_platform(proc_entry_callback proc_callback, void* proc_callback_context)
{
struct scap_platform* platform = calloc(1, sizeof(*platform));
Expand All @@ -80,6 +108,7 @@ struct scap_platform* scap_generic_alloc_platform(proc_entry_callback proc_callb
}

platform->m_vtable = &scap_generic_platform_vtable;
platform->m_metrics_vtable = &scap_generic_metrics_vtable;

init_proclist(&platform->m_proclist, proc_callback, proc_callback_context);

Expand Down
9 changes: 9 additions & 0 deletions userspace/libscap/scap_platform_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ struct scap_platform_vtable
void (*free_platform)(struct scap_platform* platform);
};

struct scap_metrics_vtable
{
void (*get_rss_vsz_pss_total_memory_and_open_fds)(uint32_t *rss, uint32_t *vsz, uint32_t *pss, uint64_t *host_memory_used, uint64_t *host_open_fds);
void (*get_cpu_usage_and_total_procs)(double start_time, double *cpu_usage_perc, double *host_cpu_usage_perc, uint32_t *host_procs_running);
void (*get_container_memory_used)(uint64_t *container_memory_used);
};

// the parts of the platform struct shared across all implementations
// this *must* be the first member of all implementations
// (the pointers are cast back&forth between the two)
Expand All @@ -89,6 +96,8 @@ struct scap_platform
scap_agent_info m_agent_info;
scap_machine_info m_machine_info;
struct ppm_proclist_info* m_driver_procinfo;

const struct scap_metrics_vtable* m_metrics_vtable;
};

#ifdef __cplusplus
Expand Down
5 changes: 0 additions & 5 deletions userspace/libsinsp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,6 @@ if (BUILD_LIBSINSP_EXAMPLES)
add_subdirectory(sinsp_debug)
endif()

if(NOT DEFINED SINSP_AGENT_CGROUP_MEM_PATH_ENV_VAR)
set(SINSP_AGENT_CGROUP_MEM_PATH_ENV_VAR "AGENT_CGROUP_MEM_PATH")
endif()
add_definitions(-DSINSP_AGENT_CGROUP_MEM_PATH_ENV_VAR="${SINSP_AGENT_CGROUP_MEM_PATH_ENV_VAR}")

# Build our pkg-config "Libs:" flags. For now, loop over SINSP_PKGCONFIG_LIBRARIES. If
# we ever start using pkg_search_module or pkg_check_modules in cmake/modules
# we could add each module to our "Requires:" line instead. We might need to
Expand Down
Loading

0 comments on commit 38e4e48

Please sign in to comment.