-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Conversation
I have tested this on ubuntu. I am still doing some additional testing. |
PTAL @janvorli @gkhanna79 @Maoni0 |
src/gc/unix/gcenv.unix.cpp
Outdated
@@ -78,6 +78,22 @@ static uint8_t g_helperPage[OS_PAGE_SIZE] __attribute__((aligned(OS_PAGE_SIZE))) | |||
// Mutex to make the FlushProcessWriteBuffersMutex thread safe | |||
static pthread_mutex_t g_flushProcessWriteBuffersMutex; | |||
|
|||
static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR; | |||
|
|||
static size_t GetRestrictedPhysicalMemoryLimit() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is for pure Unix only and so you cannot use any PAL functions here. It is used when the GC is compiled as a standalone library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, no contracts and no windowsizms should be here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that would imply code duplication..is that ok
Yes, code duplication is ok here. |
src/pal/src/misc/cgroup.cpp
Outdated
|
||
class CGroup | ||
{ | ||
char _memory_cgroup_path[MAX_PATH]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For native code, the convention is to use m_ instead of _ as a member prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please use StackString or its specialization PathStackString instead of MAX_PATH sized arrays everywhere where the size is not known? We have introduced StackString to deal with variable sized paths on Unix.
The code will also become cleaner since you would not need to care about passing the MAX_PATH limit everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I have just realized that the StackString is not available out of PAL for the Unix only implementation, so please ignore this.
src/pal/src/misc/cgroup.cpp
Outdated
#include "pal/palinternal.h" | ||
#include <sys/resource.h> | ||
|
||
#define MAX_SIZE_T (~(SIZE_T)0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use SIZE_T_MAX which is available in PAL
src/pal/src/misc/cgroup.cpp
Outdated
strcpy(_memory_cgroup_path, memory_cgroup_path); | ||
} | ||
|
||
const char* path() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For functions, we use pascal case naming convention in PAL.
src/pal/src/misc/cgroup.cpp
Outdated
return _memory_cgroup_path; | ||
} | ||
|
||
bool physicalMemoryLimit(size_t &val) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please name this methods GetPhysicalMemoryLimit and GetSwapMemoryLimit? That would make the purpose clearer for readers of the code. The swapMemoryLimit is especially confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the convention in PAL is not to use references for output parameters, but rather use pointers.
src/pal/src/misc/cgroup.cpp
Outdated
"- %s %*s %s", | ||
filesystemType, (unsigned)_countof(filesystemType), | ||
options, (unsigned)_countof(options)); | ||
if (sscanfRet != 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please unify the spacing style between the if / while and the following (
? Sometimes you have space there and sometimes not. I prefer the style with space, but I'll leave it up to you.
src/gc/unix/cgroup.cpp
Outdated
char* separatorChar = strchr(line, '-'); | ||
|
||
// See man page of proc to get format for /proc/self/mountinfo file | ||
int sscanfRet = sscanf_s(separatorChar, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the sscanf_s and other _s functions are not available for plain Unix. You'll need to use the plain ones or maybe better add defines that define sscanf_s as sscanf etc so that the rest of the code is the same as for the PAL version.
int __cdecl main(int argc,char *argv[]) | ||
{ | ||
|
||
char * AnExampleString = "this is the string"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These three variables doesn't seem to be used. Can you remove them please?
FILE* file = fopen("/sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes", "r"); | ||
if(file != NULL) | ||
{ | ||
if(mem_limit != 4194304) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am surprised the test can run at all with just 4MB limit. But I assume you have verified that.
src/gc/unix/cgroup.h
Outdated
Read memory limits for the current process | ||
--*/ | ||
|
||
#define MAX_SIZE_T (~(SIZE_T)0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that these defines can be in the cgroup.cpp since they are not part of the public interface of the CGroup.
src/vm/gcenv.os.cpp
Outdated
size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); | ||
if (restricted_limit != 0) | ||
if (restricted_limit != 0 && restricted_limit != (size_t)MAX_PTR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the added check needed? It looks like the GetRestrictedPhysicalMemoryLimit should return zero if there is no limit to be on par with the Windows version.
src/vm/gcenv.os.cpp
Outdated
uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); | ||
if (restricted_limit != 0) | ||
if (restricted_limit != 0 && restricted_limit != (size_t)MAX_PTR) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same question here
src/gc/unix/cgroup.cpp
Outdated
switch(*endptr) | ||
{ | ||
case 'g': | ||
case 'G': multiplier = 1024; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there no breaks expected here? Certain compilers can complain about it - it will be good to logically segregate them with a break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no breaks are not needed it needs to fall though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code as it is would not work. You are multiplying bool (result) by 1024. I guess the intention was to use multiplier = multiplier * 1024 in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. thanks for catching it. I did test it before and then changed variable names but missed here.
@rahku have you tried to build it on Linux with the buildstandalonegc option? That compiles all the pure unix parts that are not built otherwise. |
No i did not know that ...will do |
There's also a CI job that runs |
src/gc/unix/cgroup.cpp
Outdated
options, (unsigned)_countof(options)); | ||
if (sscanfRet != 2) | ||
{ | ||
_ASSERTE(!"Failed to parse mount info file contents with sscanf_s."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We prefer vanilla C/C++ for the standalone GC PAL: _ASSERTE -> assert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah i m in process of making these changes.
@dotnet-bot test Ubuntu Checked standalone_gc |
@dotnet-bot test Ubuntu x64 Checked standalone_gc |
PTAL i have incorprorated all review comments |
src/vm/gcenv.os.cpp
Outdated
uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); | ||
if (restricted_limit != 0) | ||
{ | ||
size_t workingSetSize; | ||
BOOL status = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
false [](start = 22, length = 5)
nit... FALSE instead of false.
src/gc/unix/cgroup.h
Outdated
|
||
class CGroup | ||
{ | ||
char m_memory_cgroup_path[MAX_PATH]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hungarian notation is Windowsism... . Change it to just _memory_cgroup_path
?
src/gc/unix/cgroup.h
Outdated
bool ReadMemoryValueFromFile(const char* filename, size_t* val); | ||
}; | ||
|
||
size_t GetRestrictedPhysicalMemoryLimit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would look better to have these methods as static public methods on CGroup, and have all instance methods in CGroup to be private; or move class CGroup
to the .cpp file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it is in the .cpp file in the CoreCLR PAL, I think it should be in the .cpp file here as well.
49ff670
to
11d92a7
Compare
src/gc/unix/cgroup.cpp
Outdated
|
||
class CGroup | ||
{ | ||
char _memory_cgroup_path[MAX_PATH]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @jkotas was asking to change in this file which is for standalone gc & linux specific and so should not have any windowism?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not have a strong opinion. I will leave it up to you what you like best.
src/gc/unix/cgroup.cpp
Outdated
size_t num = 0, l, multiplier; | ||
FILE* file = NULL; | ||
|
||
if (val == NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - you have this check only here, but not in the other copy of the function.
src/gc/unix/gcenv.unix.cpp
Outdated
if (g_RestrictedPhysicalMemoryLimit != 0) | ||
return g_RestrictedPhysicalMemoryLimit; | ||
|
||
size_t memory_limit = GetRestrictedPhysicalMemoryLimit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there an infinite recursion (GetRestrictedPhysicalMemoryLimit calling GetRestrictedPhysicalMemoryLimit)? I think this can be moved to GCToOSInterface::GetPhysicalMemoryLimit
.
src/gc/unix/cgroup.cpp
Outdated
|
||
size_t GetRestrictedPhysicalMemoryLimit() | ||
{ | ||
CGroup cgroup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This does not seem to be using 4 space indentation
src/gc/unix/cgroup.cpp
Outdated
// See man page of proc to get format for /proc/self/cgroup file | ||
int sscanfRet = sscanf(line, | ||
"%*[^:]:%[^:]:%s", | ||
subsystem_list, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this hardened against buffer overruns? What guarantees that the result will fit into subsystem_list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no secure versions of these in linux. Are you suggesting to not to use these non-secure crt functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are using these non-secure crt functions, it is up to you to guarantee no buffer overruns.
unix specific cgroup.cpp is now hardened against buffer overruns. @jkotas does that look fine now? |
I have tested this on fedora & ubuntu and also done some end-to-end testing and results look good. One of the difference that I found wrt to windows is that on Linux when we go out of memory the process is killed by OS instead of allocation failure. This is with the default setting of the machine...there might be settings to change it. |
@janvorli I also now do not look at file memory.memsw.limit_in_bytes. This file gives the swap space limit. Swap space limits the virtual address of the process and does not impact real memory that can be used by the process (in our case I am assuming only a single process to be run in a cgroup). But for gc we need real memory pressure and not virtual address space pressure. |
@dotnet-bot test Windows_NT x64 Debug Build and Test |
@dotnet-bot test OSX x64 Checked Build and Test |
src/gc/unix/cgroup.cpp
Outdated
|
||
bool GetPhysicalMemoryLimit(size_t *val) | ||
{ | ||
char mem_limit_filename[MAX_PATH]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some time ago, we have made a sweeping change over the CoreCLR native code including PAL to get rid of fixed size buffers for file paths unless the path is guaranteed to never exceed that limit. So introducing such a fixed size buffer here makes me nervous. I would prefer using a dynamically allocated buffer at this place. You can use the length of the line from the mount info file that contains the path as an memory size estimate - we would just waste a little memory that's not needed for the path. The FindMemoryHierarchyMount can then return nullptr or the allocated buffer.
I think that similar thing can be done at other places where you use MAX_PATH.
src/pal/src/misc/cgroup.cpp
Outdated
if (file != NULL && getline(&line, &linelen, file) != -1) | ||
{ | ||
int x = sscanf_s(line, "%*s %llu %*s %*s %*s %*s %*s", val); | ||
if(x==1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - could you please add spaces around the ==
to unify it with the rest of the code?
src/gc/unix/cgroup.cpp
Outdated
done: | ||
free(filesystemType); | ||
free(options); | ||
if (line) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - this "if" is not needed, free can accept nullptr just fine.
src/gc/unix/cgroup.cpp
Outdated
free(cgroup_path); | ||
cgroup_path = nullptr; | ||
} | ||
if (line) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - this "if" is not needed, free can accept nullptr just fine.
src/gc/unix/cgroup.cpp
Outdated
done: | ||
if (file) | ||
fclose(file); | ||
if (line) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - this "if" is not needed, free can accept nullptr just fine.
src/gc/unix/cgroup.cpp
Outdated
|
||
// Ensure that limit is not greater than real memory size | ||
size_t pages = (size_t) sysconf(_SC_PHYS_PAGES); | ||
if (pages != -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size_t is not a signed type, it seems you can use long
here.
src/gc/unix/cgroup.cpp
Outdated
{ | ||
bool result = false; | ||
size_t linelen; | ||
char* line = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please unify on using nullptr everywhere instead of NULL at some places and nullptr elsewhere?
src/gc/unix/cgroup.cpp
Outdated
|
||
if (file) | ||
fclose(file); | ||
if (line) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nit - this "if" is not needed, free can accept nullptr just fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same case in the PAL version of this code for all the occurences.
src/gc/unix/gcenv.unix.cpp
Outdated
{ | ||
available = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); | ||
uint64_t used = total - available; | ||
available = total > used ? total-used : 0; | ||
load = (uint32_t)((used * 100) / total); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could overflow on 32 bit hardware, you'll need to keep the used type to be uint64_t.
src/pal/src/misc/cgroup.cpp
Outdated
|
||
// Ensure that limit is not greater than real memory size | ||
size_t pages = (size_t) sysconf(_SC_PHYS_PAGES); | ||
if (pages != -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size_t is an unsigned type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you!
@dotnet-bot test Tizen armel Cross Debug Build |
@hseok-oh you recently changed the arm test runs. Everything was passing earlier for me before your changes. I do not have a device/emulator to debug this. Could you take a look at failures or point to instructions on how to use arm emulator. |
@rahku - The problem is some buffer overrun. I can see in the log the following which indicates that: |
@rahku In fact, our CI ARM emulator didn't test run since testrun script changed. That could check build success only. So we removed old ARM emulator build test and reconfigured build and test based on docker. |
This adds implementation in pal to get memory limit from cgroups. Memory limit values are written in file <path_to_memory_cgroup>/memory.limit_in_bytes and <path_to_memory_cgroup>/memory.memsw.limit_int_bytes. path_to_memory_cgroup is <path_to_memory_hierarchy>/<cgroup_name>. cgroup_name can be found in file /proc/self/cgroup. <path_to_memory_hierarchy>is normally set to /sys/fs/cgroup/memory. However this is not the standard. It can be changed to any mount point. Standard way of getting that value is from /proc/self/mountinfo.
Note: CGroup implementation is not available on OSX. Docker does not run natively on OSX. It runs under a light-weigth hypervisor which runs a linux distro and you add container on top of it. So the current implementation so work just fine in this scenario.