-
Notifications
You must be signed in to change notification settings - Fork 937
Fix memory hook installation #1269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix memory hook installation #1269
Conversation
9b4545d
to
c11c87a
Compare
test/native/libs/mallocwrapper.c
Outdated
memset(ptr, 0xFF, size); | ||
return ptr; | ||
} | ||
No newline at end of 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.
There should be no empty lines in the end, but the last line should terminate with EOL character (check other files too).
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.
handled here 22e8737
test/test/nativemem/malloc_wrapper.c
Outdated
// On Linux ARMx64 the char is dereferenced differently (-1 = 0xFF = 255 unsigned) | ||
if (*ptr != -1 && *ptr != 255) { |
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.
Use unsigned char
then.
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.
handled here 421d617
CodeCache* cc = (*native_libs)[_patched_libs++]; | ||
|
||
if (cc->contains((const void*)MallocTracer::initialize)) { | ||
// Let libasyncProfiler always use original allocation methods |
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.
Async-profiler will not be able to trace its own allocations. That's unfortunate, although acceptable.
|
||
extern "C" void* malloc_hook(size_t size) { | ||
void* ret = _orig_malloc(size); | ||
void* ret = malloc(size); |
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.
Have you tested your solution on Alpine Linux and on macOS?
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 fix is working on alpine, however there are some problems in the created test
I will update the test to work on alpine
For MacOs it's not relevant as nativememory profiling isn't supported on MacOs
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.
tested & fixed here 22e8737
Using calloc inside malloc was causing a stack overflow in alpine as calloc internally call malloc :)
(malloc -> calloc -> malloc -> calloc .....)
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.
nativememory profiling isn't supported on MacOs
It is supported to some extent. At least, allocations in JDK should be handled correctly.
My ask here is to verify that it did not get worse with this PR.
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 will need to double check on this
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.
@apangin I've confirmed that the java libs are still being patched
Just as a note here, the patching logic is still the same as before, the only real change we created is that we used to call malloc
using it's evaluated address via dlsym
& now we just let the compiler & linker do that for us which less error prone
@apangin code updated & tests pass locally on alpine |
|
||
extern "C" void* malloc_hook(size_t size) { | ||
void* ret = _orig_malloc(size); | ||
void* ret = malloc(size); |
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.
nativememory profiling isn't supported on MacOs
It is supported to some extent. At least, allocations in JDK should be handled correctly.
My ask here is to verify that it did not get worse with this PR.
test/native/libs/mallocwrapper.c
Outdated
__attribute__((visibility("default"))) | ||
void* malloc(size_t size) { | ||
if (_orig_malloc == NULL) { | ||
_orig_malloc = (malloc_t) dlsym(((void *) -1l), "malloc"); |
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.
What is (void *) -1l
? Why not a named constant?
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's the value of RLTD_NEXT
The compile command used to compile the test isn't making the definitions needed to bundle it
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 will check & update the compile command as needed
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, _GNU_SOURCE
must be defined to use this constant.
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.
solved here fb003af
test/test/nativemem/malloc_wrapper.c
Outdated
unsigned char* ptr = (unsigned char*)malloc(1999993); | ||
|
||
for (int i = 0; i < 1999993; i++) { | ||
if (*ptr != 255) { |
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.
You check the same byte every time.
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 is also a slight inconsistency between what you set (0xFF) and what you test (255). Try keeping the same style (async-profiler typically uses lowercase for hex numbers).
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.
updated f270377
Thanks for the catch about pointer value check :)
test/test/nativemem/malloc_wrapper.c
Outdated
initAsyncProfiler(); | ||
|
||
char start_cmd[2048] = {0}; | ||
snprintf(start_cmd, sizeof(start_cmd), "start,nativemem,cstack=dwarf,file=%s", filename); |
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 cstack=dwarf
essential here? Async-profiler does not supported it on all platforms (e.g. PPC64), so it's preferable not to use this option unnecessarily in order to avoid excluding test on these platforms.
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.
remvoed f270377
@@ -0,0 +1,95 @@ | |||
/* |
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.
Two similarly named files: malloc_wrapper.c
and mallocwrapper.c
This is confusing, not to say that "wrapper" is a weasel word.
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.
renamed here f270377
Makefile
Outdated
$(CC) -shared -fPIC -o $(TEST_LIB_DIR)/libreladyn.$(SOEXT) test/native/libs/reladyn.c | ||
$(CC) -shared -fPIC -o $(TEST_LIB_DIR)/libcallsmalloc.$(SOEXT) test/native/libs/callsmalloc.c | ||
$(CC) -shared -fPIC $(INCLUDES) -Isrc -o $(TEST_LIB_DIR)/libjnimalloc.$(SOEXT) test/native/libs/jnimalloc.c | ||
$(CXX) -shared -fPIC -o $(TEST_LIB_DIR)/libmalloc.$(SOEXT) test/native/libs/malloc.cpp |
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 simple file does not use any of C++ features, let's compile it as plain C.
To make RTLD_NEXT
declaration available, simply add #define _GNU_SOURCE
before includes.
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.
applied c8f2638
@Test(os = Os.LINUX, sh = "LD_PRELOAD=\"%testlib/libmalloc.so %lib\" ASPROF_COMMAND=start,nativemem,file=%f.jfr %testbin/malloc_reference preload %f.jfr", | ||
output = true, env = {"LD_LIBRARY_PATH=build/lib"}, nameSuffix="LD_PRELOAD+profiler_second") | ||
@Test(os = Os.LINUX, sh = "LD_PRELOAD=%testlib/libmalloc.so %testbin/malloc_reference api %f.jfr", output = true, env = {"LD_LIBRARY_PATH=build/lib"}, nameSuffix="api_test") | ||
public void mallocWrapper(TestProcess p) throws Exception { |
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.
Let's call it preloadMalloc
as it will better reflect the essence of the test. Similarly, malloc_reference.c
can be renamed to preload_malloc.c
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.
applied c8f2638
} | ||
|
||
@Test(os = Os.LINUX, sh = "LD_PRELOAD=\"%lib %testlib/libmalloc.so\" ASPROF_COMMAND=start,nativemem,file=%f.jfr %testbin/malloc_reference preload %f.jfr", | ||
output = true, env = {"LD_LIBRARY_PATH=build/lib"}, nameSuffix="LD_PRELOAD+profiler_first") |
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.
output = true
is typically set when a test consumes output of the target program, which does not seem to be the case 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.
removed c8f2638
@Test(os = Os.LINUX, sh = "LD_PRELOAD=\"%lib %testlib/libmalloc.so\" ASPROF_COMMAND=start,nativemem,file=%f.jfr %testbin/malloc_reference preload %f.jfr", | ||
output = true, env = {"LD_LIBRARY_PATH=build/lib"}, nameSuffix="LD_PRELOAD+profiler_first") | ||
@Test(os = Os.LINUX, sh = "LD_PRELOAD=\"%testlib/libmalloc.so %lib\" ASPROF_COMMAND=start,nativemem,file=%f.jfr %testbin/malloc_reference preload %f.jfr", | ||
output = true, env = {"LD_LIBRARY_PATH=build/lib"}, nameSuffix="LD_PRELOAD+profiler_second") |
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.
Spaces are missing: nameSuffix_=_
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.
applied c8f2638
|
||
void preloadOrderTest() { | ||
unsigned char* ptr = (unsigned char*)malloc(1999993); | ||
int size = 1999993 / sizeof(unsigned char); |
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 don't. Size of unsigned char
is known to be 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.
applied c8f2638
Thanks! Merged. |
Description
async-profiler patches dynamic references to memory allocation calls to monitor these calls to help detect possible memory leaks.
However it's possible to create custom memory allocation functions to replace the existing ones.
If this wrapper is defined within the scope of the main executable no problems will occur as the async-profiler only patches dynamic references as such the
malloc
won't be patched while the internalcalloc
would be patched, so everything will work as expected.However if the above wrapper was preloaded into the executable via
LD_PRELOAD
then the dynamic reference of the main will originally resolve to the custommalloc
, however when async-profiler is started it will resolve themalloc
via adlsym
call which returns the original reference ofmalloc
rather than the custom declaration.This in turn means the async profiler will potentially change the behaviour (which methods are called) of a running program if it's used to memory profile the executable which is risky & could potentially lead to crashes & undefined behaviour.
Related issues
#1266
Motivation and context
Avoid changing the behaviour of running processes when attaching the profiler to them.
How has this been tested?
make test
=> A new test case has been added to check possible combination of the async-profiler loading with the library
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.