Skip to content

Commit

Permalink
align GC memory load calculation on Linux with Docker and Kubernetes (#…
Browse files Browse the repository at this point in the history
…64128)

* align memory load calculation in GC with Docker and Kubernetes

* pass inactive file field name as a parameter
  • Loading branch information
dennis-yemelyanov authored Jun 14, 2022
1 parent 8bb880d commit 20cb077
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 96 deletions.
99 changes: 51 additions & 48 deletions src/coreclr/gc/unix/cgroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ Module Name:
#define CGROUP1_MEMORY_LIMIT_FILENAME "/memory.limit_in_bytes"
#define CGROUP2_MEMORY_LIMIT_FILENAME "/memory.max"
#define CGROUP_MEMORY_STAT_FILENAME "/memory.stat"
#define CGROUP1_MEMORY_USAGE_FILENAME "/memory.usage_in_bytes"
#define CGROUP2_MEMORY_USAGE_FILENAME "/memory.current"
#define CGROUP1_MEMORY_STAT_INACTIVE_FIELD "total_inactive_file "
#define CGROUP2_MEMORY_STAT_INACTIVE_FIELD "inactive_file "

extern bool ReadMemoryValueFromFile(const char* filename, uint64_t* val);

Expand All @@ -56,36 +60,11 @@ class CGroup
static int s_cgroup_version;

static char *s_memory_cgroup_path;

static const char *s_mem_stat_key_names[];
static size_t s_mem_stat_key_lengths[];
static size_t s_mem_stat_n_keys;
public:
static void Initialize()
{
s_cgroup_version = FindCGroupVersion();
s_memory_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1MemorySubsystem : nullptr);

if (s_cgroup_version == 1)
{
s_mem_stat_n_keys = 4;
s_mem_stat_key_names[0] = "total_inactive_anon ";
s_mem_stat_key_names[1] = "total_active_anon ";
s_mem_stat_key_names[2] = "total_dirty ";
s_mem_stat_key_names[3] = "total_unevictable ";
}
else
{
s_mem_stat_n_keys = 3;
s_mem_stat_key_names[0] = "anon ";
s_mem_stat_key_names[1] = "file_dirty ";
s_mem_stat_key_names[2] = "unevictable ";
}

for (size_t i = 0; i < s_mem_stat_n_keys; i++)
{
s_mem_stat_key_lengths[i] = strlen(s_mem_stat_key_names[i]);
}
}

static void Cleanup()
Expand Down Expand Up @@ -113,9 +92,9 @@ class CGroup
if (s_cgroup_version == 0)
return false;
else if (s_cgroup_version == 1)
return GetCGroupMemoryUsage(val);
return GetCGroupMemoryUsage(val, CGROUP1_MEMORY_USAGE_FILENAME, CGROUP1_MEMORY_STAT_INACTIVE_FIELD);
else if (s_cgroup_version == 2)
return GetCGroupMemoryUsage(val);
return GetCGroupMemoryUsage(val, CGROUP2_MEMORY_USAGE_FILENAME, CGROUP2_MEMORY_STAT_INACTIVE_FIELD);
else
{
assert(!"Unknown cgroup version.");
Expand Down Expand Up @@ -401,8 +380,38 @@ class CGroup
return result;
}

static bool GetCGroupMemoryUsage(size_t *val)
static bool GetCGroupMemoryUsage(size_t *val, const char *filename, const char *inactiveFileFieldName)
{
// Use the same way to calculate memory load as popular container tools (Docker, Kubernetes, Containerd etc.)
// For cgroup v1: value of 'memory.usage_in_bytes' minus 'total_inactive_file' value of 'memory.stat'
// For cgroup v2: value of 'memory.current' minus 'inactive_file' value of 'memory.stat'

char* mem_usage_filename = nullptr;
if (asprintf(&mem_usage_filename, "%s%s", s_memory_cgroup_path, filename) < 0)
return false;

uint64_t temp = 0;

size_t usage = 0;

bool result = ReadMemoryValueFromFile(mem_usage_filename, &temp);
if (result)
{
if (temp > std::numeric_limits<size_t>::max())
{
usage = std::numeric_limits<size_t>::max();
}
else
{
usage = (size_t)temp;
}
}

free(mem_usage_filename);

if (!result)
return result;

if (s_memory_cgroup_path == nullptr)
return false;

Expand All @@ -417,44 +426,38 @@ class CGroup

char *line = nullptr;
size_t lineLen = 0;
size_t readValues = 0;
bool foundInactiveFileValue = false;
char* endptr;

*val = 0;
while (getline(&line, &lineLen, stat_file) != -1 && readValues < s_mem_stat_n_keys)
size_t inactiveFileFieldNameLength = strlen(inactiveFileFieldName);

while (getline(&line, &lineLen, stat_file) != -1)
{
for (size_t i = 0; i < s_mem_stat_n_keys; i++)
if (strncmp(line, inactiveFileFieldName, inactiveFileFieldNameLength) == 0)
{
if (strncmp(line, s_mem_stat_key_names[i], s_mem_stat_key_lengths[i]) == 0)
errno = 0;
const char* startptr = line + inactiveFileFieldNameLength;
size_t inactiveFileValue = strtoll(startptr, &endptr, 10);
if (endptr != startptr && errno == 0)
{
errno = 0;
const char* startptr = line + s_mem_stat_key_lengths[i];
*val += strtoll(startptr, &endptr, 10);
if (endptr != startptr && errno == 0)
readValues++;

break;
foundInactiveFileValue = true;
*val = usage - inactiveFileValue;
}

break;
}
}

fclose(stat_file);
free(line);

if (readValues == s_mem_stat_n_keys)
return true;

return false;
return foundInactiveFileValue;
}
};

int CGroup::s_cgroup_version = 0;
char *CGroup::s_memory_cgroup_path = nullptr;

const char *CGroup::s_mem_stat_key_names[4] = {};
size_t CGroup::s_mem_stat_key_lengths[4] = {};
size_t CGroup::s_mem_stat_n_keys = 0;

void InitializeCGroup()
{
CGroup::Initialize();
Expand Down
99 changes: 51 additions & 48 deletions src/coreclr/pal/src/misc/cgroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ SET_DEFAULT_DEBUG_CHANNEL(MISC);
#define CGROUP1_MEMORY_LIMIT_FILENAME "/memory.limit_in_bytes"
#define CGROUP2_MEMORY_LIMIT_FILENAME "/memory.max"
#define CGROUP_MEMORY_STAT_FILENAME "/memory.stat"
#define CGROUP1_MEMORY_USAGE_FILENAME "/memory.usage_in_bytes"
#define CGROUP2_MEMORY_USAGE_FILENAME "/memory.current"
#define CGROUP1_MEMORY_STAT_INACTIVE_FIELD "total_inactive_file "
#define CGROUP2_MEMORY_STAT_INACTIVE_FIELD "inactive_file "
#define CGROUP1_CFS_QUOTA_FILENAME "/cpu.cfs_quota_us"
#define CGROUP1_CFS_PERIOD_FILENAME "/cpu.cfs_period_us"
#define CGROUP2_CPU_MAX_FILENAME "/cpu.max"
Expand All @@ -49,37 +53,12 @@ class CGroup

static char *s_memory_cgroup_path;
static char *s_cpu_cgroup_path;

static const char *s_mem_stat_key_names[];
static size_t s_mem_stat_key_lengths[];
static size_t s_mem_stat_n_keys;
public:
static void Initialize()
{
s_cgroup_version = FindCGroupVersion();
s_memory_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1MemorySubsystem : nullptr);
s_cpu_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1CpuSubsystem : nullptr);

if (s_cgroup_version == 1)
{
s_mem_stat_n_keys = 4;
s_mem_stat_key_names[0] = "total_inactive_anon ";
s_mem_stat_key_names[1] = "total_active_anon ";
s_mem_stat_key_names[2] = "total_dirty ";
s_mem_stat_key_names[3] = "total_unevictable ";
}
else
{
s_mem_stat_n_keys = 3;
s_mem_stat_key_names[0] = "anon ";
s_mem_stat_key_names[1] = "file_dirty ";
s_mem_stat_key_names[2] = "unevictable ";
}

for (size_t i = 0; i < s_mem_stat_n_keys; i++)
{
s_mem_stat_key_lengths[i] = strlen(s_mem_stat_key_names[i]);
}
}

static void Cleanup()
Expand Down Expand Up @@ -108,9 +87,9 @@ class CGroup
if (s_cgroup_version == 0)
return false;
else if (s_cgroup_version == 1)
return GetCGroupMemoryUsage(val);
return GetCGroupMemoryUsage(val, CGROUP1_MEMORY_USAGE_FILENAME, CGROUP1_MEMORY_STAT_INACTIVE_FIELD);
else if (s_cgroup_version == 2)
return GetCGroupMemoryUsage(val);
return GetCGroupMemoryUsage(val, CGROUP2_MEMORY_USAGE_FILENAME, CGROUP2_MEMORY_STAT_INACTIVE_FIELD);
else
{
_ASSERTE(!"Unknown cgroup version.");
Expand Down Expand Up @@ -416,8 +395,38 @@ class CGroup
return result;
}

static bool GetCGroupMemoryUsage(size_t *val)
static bool GetCGroupMemoryUsage(size_t *val, const char *filename, const char *inactiveFileFieldName)
{
// Use the same way to calculate memory load as popular container tools (Docker, Kubernetes, Containerd etc.)
// For cgroup v1: value of 'memory.usage_in_bytes' minus 'total_inactive_file' value of 'memory.stat'
// For cgroup v2: value of 'memory.current' minus 'inactive_file' value of 'memory.stat'

char* mem_usage_filename = nullptr;
if (asprintf(&mem_usage_filename, "%s%s", s_memory_cgroup_path, filename) < 0)
return false;

uint64_t temp = 0;

size_t usage = 0;

bool result = ReadMemoryValueFromFile(mem_usage_filename, &temp);
if (result)
{
if (temp > std::numeric_limits<size_t>::max())
{
usage = std::numeric_limits<size_t>::max();
}
else
{
usage = (size_t)temp;
}
}

free(mem_usage_filename);

if (!result)
return result;

if (s_memory_cgroup_path == nullptr)
return false;

Expand All @@ -432,34 +441,32 @@ class CGroup

char *line = nullptr;
size_t lineLen = 0;
size_t readValues = 0;
bool foundInactiveFileValue = false;
char* endptr;

*val = 0;
while (getline(&line, &lineLen, stat_file) != -1 && readValues < s_mem_stat_n_keys)
size_t inactiveFileFieldNameLength = strlen(inactiveFileFieldName);

while (getline(&line, &lineLen, stat_file) != -1)
{
for (size_t i = 0; i < s_mem_stat_n_keys; i++)
if (strncmp(line, inactiveFileFieldName, inactiveFileFieldNameLength) == 0)
{
if (strncmp(line, s_mem_stat_key_names[i], s_mem_stat_key_lengths[i]) == 0)
errno = 0;
const char* startptr = line + inactiveFileFieldNameLength;
size_t inactiveFileValue = strtoll(startptr, &endptr, 10);
if (endptr != startptr && errno == 0)
{
errno = 0;
const char* startptr = line + s_mem_stat_key_lengths[i];
*val += strtoll(startptr, &endptr, 10);
if (endptr != startptr && errno == 0)
readValues++;

break;
foundInactiveFileValue = true;
*val = usage - inactiveFileValue;
}

break;
}
}

fclose(stat_file);
free(line);

if (readValues == s_mem_stat_n_keys)
return true;

return false;
return foundInactiveFileValue;
}

static bool ReadMemoryValueFromFile(const char* filename, uint64_t* val)
Expand Down Expand Up @@ -624,10 +631,6 @@ int CGroup::s_cgroup_version = 0;
char *CGroup::s_memory_cgroup_path = nullptr;
char *CGroup::s_cpu_cgroup_path = nullptr;

const char *CGroup::s_mem_stat_key_names[4] = {};
size_t CGroup::s_mem_stat_key_lengths[4] = {};
size_t CGroup::s_mem_stat_n_keys = 0;

void InitializeCGroup()
{
CGroup::Initialize();
Expand Down

0 comments on commit 20cb077

Please sign in to comment.