Skip to content

Commit 20cb077

Browse files
align GC memory load calculation on Linux with Docker and Kubernetes (#64128)
* align memory load calculation in GC with Docker and Kubernetes * pass inactive file field name as a parameter
1 parent 8bb880d commit 20cb077

File tree

2 files changed

+102
-96
lines changed

2 files changed

+102
-96
lines changed

src/coreclr/gc/unix/cgroup.cpp

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ Module Name:
4747
#define CGROUP1_MEMORY_LIMIT_FILENAME "/memory.limit_in_bytes"
4848
#define CGROUP2_MEMORY_LIMIT_FILENAME "/memory.max"
4949
#define CGROUP_MEMORY_STAT_FILENAME "/memory.stat"
50+
#define CGROUP1_MEMORY_USAGE_FILENAME "/memory.usage_in_bytes"
51+
#define CGROUP2_MEMORY_USAGE_FILENAME "/memory.current"
52+
#define CGROUP1_MEMORY_STAT_INACTIVE_FIELD "total_inactive_file "
53+
#define CGROUP2_MEMORY_STAT_INACTIVE_FIELD "inactive_file "
5054

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

@@ -56,36 +60,11 @@ class CGroup
5660
static int s_cgroup_version;
5761

5862
static char *s_memory_cgroup_path;
59-
60-
static const char *s_mem_stat_key_names[];
61-
static size_t s_mem_stat_key_lengths[];
62-
static size_t s_mem_stat_n_keys;
6363
public:
6464
static void Initialize()
6565
{
6666
s_cgroup_version = FindCGroupVersion();
6767
s_memory_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1MemorySubsystem : nullptr);
68-
69-
if (s_cgroup_version == 1)
70-
{
71-
s_mem_stat_n_keys = 4;
72-
s_mem_stat_key_names[0] = "total_inactive_anon ";
73-
s_mem_stat_key_names[1] = "total_active_anon ";
74-
s_mem_stat_key_names[2] = "total_dirty ";
75-
s_mem_stat_key_names[3] = "total_unevictable ";
76-
}
77-
else
78-
{
79-
s_mem_stat_n_keys = 3;
80-
s_mem_stat_key_names[0] = "anon ";
81-
s_mem_stat_key_names[1] = "file_dirty ";
82-
s_mem_stat_key_names[2] = "unevictable ";
83-
}
84-
85-
for (size_t i = 0; i < s_mem_stat_n_keys; i++)
86-
{
87-
s_mem_stat_key_lengths[i] = strlen(s_mem_stat_key_names[i]);
88-
}
8968
}
9069

9170
static void Cleanup()
@@ -113,9 +92,9 @@ class CGroup
11392
if (s_cgroup_version == 0)
11493
return false;
11594
else if (s_cgroup_version == 1)
116-
return GetCGroupMemoryUsage(val);
95+
return GetCGroupMemoryUsage(val, CGROUP1_MEMORY_USAGE_FILENAME, CGROUP1_MEMORY_STAT_INACTIVE_FIELD);
11796
else if (s_cgroup_version == 2)
118-
return GetCGroupMemoryUsage(val);
97+
return GetCGroupMemoryUsage(val, CGROUP2_MEMORY_USAGE_FILENAME, CGROUP2_MEMORY_STAT_INACTIVE_FIELD);
11998
else
12099
{
121100
assert(!"Unknown cgroup version.");
@@ -401,8 +380,38 @@ class CGroup
401380
return result;
402381
}
403382

404-
static bool GetCGroupMemoryUsage(size_t *val)
383+
static bool GetCGroupMemoryUsage(size_t *val, const char *filename, const char *inactiveFileFieldName)
405384
{
385+
// Use the same way to calculate memory load as popular container tools (Docker, Kubernetes, Containerd etc.)
386+
// For cgroup v1: value of 'memory.usage_in_bytes' minus 'total_inactive_file' value of 'memory.stat'
387+
// For cgroup v2: value of 'memory.current' minus 'inactive_file' value of 'memory.stat'
388+
389+
char* mem_usage_filename = nullptr;
390+
if (asprintf(&mem_usage_filename, "%s%s", s_memory_cgroup_path, filename) < 0)
391+
return false;
392+
393+
uint64_t temp = 0;
394+
395+
size_t usage = 0;
396+
397+
bool result = ReadMemoryValueFromFile(mem_usage_filename, &temp);
398+
if (result)
399+
{
400+
if (temp > std::numeric_limits<size_t>::max())
401+
{
402+
usage = std::numeric_limits<size_t>::max();
403+
}
404+
else
405+
{
406+
usage = (size_t)temp;
407+
}
408+
}
409+
410+
free(mem_usage_filename);
411+
412+
if (!result)
413+
return result;
414+
406415
if (s_memory_cgroup_path == nullptr)
407416
return false;
408417

@@ -417,44 +426,38 @@ class CGroup
417426

418427
char *line = nullptr;
419428
size_t lineLen = 0;
420-
size_t readValues = 0;
429+
bool foundInactiveFileValue = false;
421430
char* endptr;
422431

423-
*val = 0;
424-
while (getline(&line, &lineLen, stat_file) != -1 && readValues < s_mem_stat_n_keys)
432+
size_t inactiveFileFieldNameLength = strlen(inactiveFileFieldName);
433+
434+
while (getline(&line, &lineLen, stat_file) != -1)
425435
{
426-
for (size_t i = 0; i < s_mem_stat_n_keys; i++)
436+
if (strncmp(line, inactiveFileFieldName, inactiveFileFieldNameLength) == 0)
427437
{
428-
if (strncmp(line, s_mem_stat_key_names[i], s_mem_stat_key_lengths[i]) == 0)
438+
errno = 0;
439+
const char* startptr = line + inactiveFileFieldNameLength;
440+
size_t inactiveFileValue = strtoll(startptr, &endptr, 10);
441+
if (endptr != startptr && errno == 0)
429442
{
430-
errno = 0;
431-
const char* startptr = line + s_mem_stat_key_lengths[i];
432-
*val += strtoll(startptr, &endptr, 10);
433-
if (endptr != startptr && errno == 0)
434-
readValues++;
435-
436-
break;
443+
foundInactiveFileValue = true;
444+
*val = usage - inactiveFileValue;
437445
}
446+
447+
break;
438448
}
439449
}
440450

441451
fclose(stat_file);
442452
free(line);
443453

444-
if (readValues == s_mem_stat_n_keys)
445-
return true;
446-
447-
return false;
454+
return foundInactiveFileValue;
448455
}
449456
};
450457

451458
int CGroup::s_cgroup_version = 0;
452459
char *CGroup::s_memory_cgroup_path = nullptr;
453460

454-
const char *CGroup::s_mem_stat_key_names[4] = {};
455-
size_t CGroup::s_mem_stat_key_lengths[4] = {};
456-
size_t CGroup::s_mem_stat_n_keys = 0;
457-
458461
void InitializeCGroup()
459462
{
460463
CGroup::Initialize();

src/coreclr/pal/src/misc/cgroup.cpp

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ SET_DEFAULT_DEBUG_CHANNEL(MISC);
3838
#define CGROUP1_MEMORY_LIMIT_FILENAME "/memory.limit_in_bytes"
3939
#define CGROUP2_MEMORY_LIMIT_FILENAME "/memory.max"
4040
#define CGROUP_MEMORY_STAT_FILENAME "/memory.stat"
41+
#define CGROUP1_MEMORY_USAGE_FILENAME "/memory.usage_in_bytes"
42+
#define CGROUP2_MEMORY_USAGE_FILENAME "/memory.current"
43+
#define CGROUP1_MEMORY_STAT_INACTIVE_FIELD "total_inactive_file "
44+
#define CGROUP2_MEMORY_STAT_INACTIVE_FIELD "inactive_file "
4145
#define CGROUP1_CFS_QUOTA_FILENAME "/cpu.cfs_quota_us"
4246
#define CGROUP1_CFS_PERIOD_FILENAME "/cpu.cfs_period_us"
4347
#define CGROUP2_CPU_MAX_FILENAME "/cpu.max"
@@ -49,37 +53,12 @@ class CGroup
4953

5054
static char *s_memory_cgroup_path;
5155
static char *s_cpu_cgroup_path;
52-
53-
static const char *s_mem_stat_key_names[];
54-
static size_t s_mem_stat_key_lengths[];
55-
static size_t s_mem_stat_n_keys;
5656
public:
5757
static void Initialize()
5858
{
5959
s_cgroup_version = FindCGroupVersion();
6060
s_memory_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1MemorySubsystem : nullptr);
6161
s_cpu_cgroup_path = FindCGroupPath(s_cgroup_version == 1 ? &IsCGroup1CpuSubsystem : nullptr);
62-
63-
if (s_cgroup_version == 1)
64-
{
65-
s_mem_stat_n_keys = 4;
66-
s_mem_stat_key_names[0] = "total_inactive_anon ";
67-
s_mem_stat_key_names[1] = "total_active_anon ";
68-
s_mem_stat_key_names[2] = "total_dirty ";
69-
s_mem_stat_key_names[3] = "total_unevictable ";
70-
}
71-
else
72-
{
73-
s_mem_stat_n_keys = 3;
74-
s_mem_stat_key_names[0] = "anon ";
75-
s_mem_stat_key_names[1] = "file_dirty ";
76-
s_mem_stat_key_names[2] = "unevictable ";
77-
}
78-
79-
for (size_t i = 0; i < s_mem_stat_n_keys; i++)
80-
{
81-
s_mem_stat_key_lengths[i] = strlen(s_mem_stat_key_names[i]);
82-
}
8362
}
8463

8564
static void Cleanup()
@@ -108,9 +87,9 @@ class CGroup
10887
if (s_cgroup_version == 0)
10988
return false;
11089
else if (s_cgroup_version == 1)
111-
return GetCGroupMemoryUsage(val);
90+
return GetCGroupMemoryUsage(val, CGROUP1_MEMORY_USAGE_FILENAME, CGROUP1_MEMORY_STAT_INACTIVE_FIELD);
11291
else if (s_cgroup_version == 2)
113-
return GetCGroupMemoryUsage(val);
92+
return GetCGroupMemoryUsage(val, CGROUP2_MEMORY_USAGE_FILENAME, CGROUP2_MEMORY_STAT_INACTIVE_FIELD);
11493
else
11594
{
11695
_ASSERTE(!"Unknown cgroup version.");
@@ -416,8 +395,38 @@ class CGroup
416395
return result;
417396
}
418397

419-
static bool GetCGroupMemoryUsage(size_t *val)
398+
static bool GetCGroupMemoryUsage(size_t *val, const char *filename, const char *inactiveFileFieldName)
420399
{
400+
// Use the same way to calculate memory load as popular container tools (Docker, Kubernetes, Containerd etc.)
401+
// For cgroup v1: value of 'memory.usage_in_bytes' minus 'total_inactive_file' value of 'memory.stat'
402+
// For cgroup v2: value of 'memory.current' minus 'inactive_file' value of 'memory.stat'
403+
404+
char* mem_usage_filename = nullptr;
405+
if (asprintf(&mem_usage_filename, "%s%s", s_memory_cgroup_path, filename) < 0)
406+
return false;
407+
408+
uint64_t temp = 0;
409+
410+
size_t usage = 0;
411+
412+
bool result = ReadMemoryValueFromFile(mem_usage_filename, &temp);
413+
if (result)
414+
{
415+
if (temp > std::numeric_limits<size_t>::max())
416+
{
417+
usage = std::numeric_limits<size_t>::max();
418+
}
419+
else
420+
{
421+
usage = (size_t)temp;
422+
}
423+
}
424+
425+
free(mem_usage_filename);
426+
427+
if (!result)
428+
return result;
429+
421430
if (s_memory_cgroup_path == nullptr)
422431
return false;
423432

@@ -432,34 +441,32 @@ class CGroup
432441

433442
char *line = nullptr;
434443
size_t lineLen = 0;
435-
size_t readValues = 0;
444+
bool foundInactiveFileValue = false;
436445
char* endptr;
437446

438-
*val = 0;
439-
while (getline(&line, &lineLen, stat_file) != -1 && readValues < s_mem_stat_n_keys)
447+
size_t inactiveFileFieldNameLength = strlen(inactiveFileFieldName);
448+
449+
while (getline(&line, &lineLen, stat_file) != -1)
440450
{
441-
for (size_t i = 0; i < s_mem_stat_n_keys; i++)
451+
if (strncmp(line, inactiveFileFieldName, inactiveFileFieldNameLength) == 0)
442452
{
443-
if (strncmp(line, s_mem_stat_key_names[i], s_mem_stat_key_lengths[i]) == 0)
453+
errno = 0;
454+
const char* startptr = line + inactiveFileFieldNameLength;
455+
size_t inactiveFileValue = strtoll(startptr, &endptr, 10);
456+
if (endptr != startptr && errno == 0)
444457
{
445-
errno = 0;
446-
const char* startptr = line + s_mem_stat_key_lengths[i];
447-
*val += strtoll(startptr, &endptr, 10);
448-
if (endptr != startptr && errno == 0)
449-
readValues++;
450-
451-
break;
458+
foundInactiveFileValue = true;
459+
*val = usage - inactiveFileValue;
452460
}
461+
462+
break;
453463
}
454464
}
455465

456466
fclose(stat_file);
457467
free(line);
458468

459-
if (readValues == s_mem_stat_n_keys)
460-
return true;
461-
462-
return false;
469+
return foundInactiveFileValue;
463470
}
464471

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

627-
const char *CGroup::s_mem_stat_key_names[4] = {};
628-
size_t CGroup::s_mem_stat_key_lengths[4] = {};
629-
size_t CGroup::s_mem_stat_n_keys = 0;
630-
631634
void InitializeCGroup()
632635
{
633636
CGroup::Initialize();

0 commit comments

Comments
 (0)