@@ -9,7 +9,7 @@ Module Name:
99 cgroup.cpp
1010
1111Abstract:
12- Read memory limits for the current process
12+ Read memory and cpu limits for the current process
1313--*/
1414#include < cstdint>
1515#include < cstddef>
@@ -26,42 +26,24 @@ Module Name:
2626#define PROC_CGROUP_FILENAME " /proc/self/cgroup"
2727#define PROC_STATM_FILENAME " /proc/self/statm"
2828#define MEM_LIMIT_FILENAME " /memory.limit_in_bytes"
29+ #define CFS_QUOTA_FILENAME " /cpu.cfs_quota_us"
30+ #define CFS_PERIOD_FILENAME " /cpu.cfs_period_us"
2931
3032class CGroup
3133{
3234 char * m_memory_cgroup_path;
35+ char * m_cpu_cgroup_path;
3336public:
3437 CGroup ()
3538 {
36- m_memory_cgroup_path = nullptr ;
37- char * memoryHierarchyMount = nullptr ;
38- char *cgroup_path_relative_to_mount = nullptr ;
39- size_t len;
40- memoryHierarchyMount = FindMemoryHierarchyMount ();
41- if (memoryHierarchyMount == nullptr )
42- goto done;
43-
44- cgroup_path_relative_to_mount = FindCGroupPathForMemorySubsystem ();
45- if (cgroup_path_relative_to_mount == nullptr )
46- goto done;
47-
48- len = strlen (memoryHierarchyMount);
49- len += strlen (cgroup_path_relative_to_mount);
50- m_memory_cgroup_path = (char *)malloc (len+1 );
51- if (m_memory_cgroup_path == nullptr )
52- goto done;
53-
54- strcpy (m_memory_cgroup_path, memoryHierarchyMount);
55- strcat (m_memory_cgroup_path, cgroup_path_relative_to_mount);
56-
57- done:
58- free (memoryHierarchyMount);
59- free (cgroup_path_relative_to_mount);
39+ m_memory_cgroup_path = FindCgroupPath (&IsMemorySubsystem);
40+ m_cpu_cgroup_path = FindCgroupPath (&IsCpuSubsystem);
6041 }
6142
6243 ~CGroup ()
6344 {
6445 free (m_memory_cgroup_path);
46+ free (m_cpu_cgroup_path);
6547 }
6648
6749 bool GetPhysicalMemoryLimit (size_t *val)
@@ -84,15 +66,89 @@ class CGroup
8466 free (mem_limit_filename);
8567 return result;
8668 }
69+
70+ bool GetCpuLimit (uint32_t *val)
71+ {
72+ long long quota;
73+ long long period;
74+ long long cpu_count;
75+
76+ quota = ReadCpuCGroupValue (CFS_QUOTA_FILENAME);
77+ if (quota <= 0 )
78+ return false ;
79+
80+ period = ReadCpuCGroupValue (CFS_PERIOD_FILENAME);
81+ if (period <= 0 )
82+ return false ;
83+
84+ // Cannot have less than 1 CPU
85+ if (quota <= period)
86+ {
87+ *val = 1 ;
88+ return true ;
89+ }
90+
91+ cpu_count = quota / period;
92+ if (cpu_count < UINT32_MAX)
93+ {
94+ *val = cpu_count;
95+ }
96+ else
97+ {
98+ *val = UINT32_MAX;
99+ }
100+
101+ return true ;
102+ }
87103
88104private:
89- char * FindMemoryHierarchyMount ()
105+ static bool IsMemorySubsystem (const char *strTok){
106+ return strcmp (" memory" , strTok) == 0 ;
107+ }
108+
109+ static bool IsCpuSubsystem (const char *strTok){
110+ return strcmp (" cpu" , strTok) == 0 ;
111+ }
112+
113+ static char * FindCgroupPath (bool (*is_subsystem)(const char *)){
114+ char *cgroup_path = nullptr ;
115+ char *hierarchy_mount = nullptr ;
116+ char *hierarchy_root = nullptr ;
117+ char *cgroup_path_relative_to_mount = nullptr ;
118+
119+ FindHierarchyMount (is_subsystem, &hierarchy_mount, &hierarchy_root);
120+ if (hierarchy_mount == nullptr || hierarchy_root == nullptr )
121+ goto done;
122+
123+ cgroup_path_relative_to_mount = FindCGroupPathForSubsystem (is_subsystem);
124+ if (cgroup_path_relative_to_mount == nullptr )
125+ goto done;
126+
127+ cgroup_path = (char *)malloc (strlen (hierarchy_mount) + strlen (cgroup_path_relative_to_mount) + 1 );
128+ if (cgroup_path == nullptr )
129+ goto done;
130+
131+ strcpy (cgroup_path, hierarchy_mount);
132+ // For a host cgroup, we need to append the relative path.
133+ // In a docker container, the root and relative path are the same and we don't need to append.
134+ if (strcmp (hierarchy_root, cgroup_path_relative_to_mount) != 0 )
135+ strcat (cgroup_path, cgroup_path_relative_to_mount);
136+
137+ done:
138+ free (hierarchy_mount);
139+ free (hierarchy_root);
140+ free (cgroup_path_relative_to_mount);
141+ return cgroup_path;
142+ }
143+
144+ static void FindHierarchyMount (bool (*is_subsystem)(const char *), char** pmountpath, char** pmountroot)
90145 {
91146 char *line = nullptr ;
92147 size_t lineLen = 0 , maxLineLen = 0 ;
93148 char *filesystemType = nullptr ;
94149 char *options = nullptr ;
95- char * mountpath = nullptr ;
150+ char *mountpath = nullptr ;
151+ char *mountroot = nullptr ;
96152
97153 FILE *mountinfofile = fopen (PROC_MOUNTINFO_FILENAME, " r" );
98154 if (mountinfofile == nullptr )
@@ -113,11 +169,11 @@ class CGroup
113169 maxLineLen = lineLen;
114170 }
115171
116- char * separatorChar = strchr (line, ' - ' );
172+ char * separatorChar = strstr (line, " - " );
117173
118174 // See man page of proc to get format for /proc/self/mountinfo file
119175 int sscanfRet = sscanf (separatorChar,
120- " - %s %*s %s" ,
176+ " - %s %*s %s" ,
121177 filesystemType,
122178 options);
123179 if (sscanfRet != 2 )
@@ -132,37 +188,43 @@ class CGroup
132188 char * strTok = strtok_r (options, " ," , &context);
133189 while (strTok != nullptr )
134190 {
135- if (strncmp ( " memory " , strTok, 6 ) == 0 )
191+ if (is_subsystem ( strTok) )
136192 {
137193 mountpath = (char *)malloc (lineLen+1 );
138194 if (mountpath == nullptr )
139195 goto done;
196+ mountroot = (char *)malloc (lineLen+1 );
197+ if (mountroot == nullptr )
198+ goto done;
140199
141200 sscanfRet = sscanf (line,
142- " %*s %*s %*s %*s %s " ,
201+ " %*s %*s %*s %s %s " ,
202+ mountroot,
143203 mountpath);
144- if (sscanfRet != 1 )
145- {
146- free (mountpath);
147- mountpath = nullptr ;
204+ if (sscanfRet != 2 )
148205 assert (!" Failed to parse mount info file contents with sscanf." );
149- }
206+
207+ // assign the output arguments and clear the locals so we don't free them.
208+ *pmountpath = mountpath;
209+ *pmountroot = mountroot;
210+ mountpath = mountroot = nullptr ;
150211 goto done;
151212 }
152213 strTok = strtok_r (nullptr , " ," , &context);
153214 }
154215 }
155216 }
156217 done:
218+ free (mountpath);
219+ free (mountroot);
157220 free (filesystemType);
158221 free (options);
159222 free (line);
160223 if (mountinfofile)
161224 fclose (mountinfofile);
162- return mountpath;
163225 }
164226
165- char * FindCGroupPathForMemorySubsystem ( )
227+ static char * FindCGroupPathForSubsystem ( bool (*is_subsystem)( const char *) )
166228 {
167229 char *line = nullptr ;
168230 size_t lineLen = 0 ;
@@ -205,7 +267,7 @@ class CGroup
205267 char * strTok = strtok_r (subsystem_list, " ," , &context);
206268 while (strTok != nullptr )
207269 {
208- if (strncmp ( " memory " , strTok, 6 ) == 0 )
270+ if (is_subsystem ( strTok) )
209271 {
210272 result = true ;
211273 break ;
@@ -271,6 +333,59 @@ class CGroup
271333 free (line);
272334 return result;
273335 }
336+
337+ long long ReadCpuCGroupValue (const char * subsystemFilename){
338+ char *filename = nullptr ;
339+ bool result = false ;
340+ long long val;
341+
342+ if (m_cpu_cgroup_path == nullptr )
343+ return -1 ;
344+
345+ filename = (char *)malloc (strlen (m_cpu_cgroup_path) + strlen (subsystemFilename) + 1 );
346+ if (filename == nullptr )
347+ return -1 ;
348+
349+ strcpy (filename, m_cpu_cgroup_path);
350+ strcat (filename, subsystemFilename);
351+ result = ReadLongLongValueFromFile (filename, &val);
352+ free (filename);
353+ if (!result)
354+ return -1 ;
355+
356+ return val;
357+ }
358+
359+ bool ReadLongLongValueFromFile (const char * filename, long long * val)
360+ {
361+ bool result = false ;
362+ char *line = nullptr ;
363+ size_t lineLen = 0 ;
364+
365+ FILE* file = nullptr ;
366+
367+ if (val == nullptr )
368+ goto done;
369+
370+ file = fopen (filename, " r" );
371+ if (file == nullptr )
372+ goto done;
373+
374+ if (getline (&line, &lineLen, file) == -1 )
375+ goto done;
376+
377+ errno = 0 ;
378+ *val = atoll (line);
379+ if (errno != 0 )
380+ goto done;
381+
382+ result = true ;
383+ done:
384+ if (file)
385+ fclose (file);
386+ free (line);
387+ return result;
388+ }
274389};
275390
276391size_t GetRestrictedPhysicalMemoryLimit ()
@@ -340,3 +455,13 @@ bool GetWorkingSetSize(size_t* val)
340455 free (line);
341456 return result;
342457}
458+
459+ bool GetCpuLimit (uint32_t * val)
460+ {
461+ CGroup cgroup;
462+
463+ if (val == nullptr )
464+ return false ;
465+
466+ return cgroup.GetCpuLimit (val);
467+ }
0 commit comments