KEMBAR78
Native memory profiling could lead to crashes · Issue #1256 · async-profiler/async-profiler · GitHub
Skip to content

Native memory profiling could lead to crashes #1256

@Baraa-Hasheesh

Description

@Baraa-Hasheesh

Describe the bug

Async-Profiler only patches libraries to track malloc calls if & only if it's started in nativemem profiling mode
Furthermore async-profiler keeps record of all found libs in it's own local cache of CodeCacheArray

This behaviour makes it possible to have a library inside CodeCacheArray but that library isn't patched to track malloc calls

Due to these optimization choices it's possible for the async-profiler to try to patch for nativemem profiling after it's unloaded from memory

Expected vs. actual behavior

async-profiler shouldn't crash on native memory profiling

Reproduction Steps

Create the following C/C++ file

  • main.cpp
#include <dlfcn.h>
#include <iostream>
#include <pthread.h>
#include "asprof.h"

#define ASSERT_NO_DLERROR()           \
    err = dlerror();                  \
    if (err != NULL) {                \
        printf("ERROR => %s\n", err); \
        exit(1);                      \
    }

#define ASSERT_NO_ASPROF_ERR(err)                       \
    if (err != NULL) {                                  \
        fprintf(stderr, "%s\n", _asprof_error_str(err)); \
        exit(1);                                        \
    }

    
typedef void* (*my_malloc_t)(size_t);
typedef void (*my_free_t)(void*);

void outputCallback(const char* buffer, size_t size) {
    fwrite(buffer, sizeof(char), size, stdout);
}


int main() {
    void* lib;
    char* err;

    lib = dlopen("my_lib.so", RTLD_NOW | RTLD_GLOBAL);
    ASSERT_NO_DLERROR();
    printf("dlopen => %p\n", lib);

    my_malloc_t local_malloc = (my_malloc_t) dlsym(lib, "my_malloc");
    ASSERT_NO_DLERROR();

    my_free_t local_free = (my_free_t) dlsym(lib, "my_free");
    ASSERT_NO_DLERROR();

    void* libprof = dlopen("libasyncProfiler.so", RTLD_NOW);
    ASSERT_NO_DLERROR();

    ((asprof_init_t)dlsym(libprof, "asprof_init"))();
    ASSERT_NO_DLERROR();

    asprof_execute_t _asprof_execute = (asprof_execute_t)dlsym(libprof, "asprof_execute");
    ASSERT_NO_DLERROR();

    asprof_error_str_t _asprof_error_str = (asprof_error_str_t)dlsym(libprof, "asprof_error_str");
    ASSERT_NO_DLERROR();

    asprof_error_t asprof_err = _asprof_execute("start,nativemem,cstack=dwarf,file=output.jfr", outputCallback);
    ASSERT_NO_ASPROF_ERR(asprof_err);

    local_free(local_malloc(1001));

    asprof_err = _asprof_execute("stop", outputCallback);
    ASSERT_NO_ASPROF_ERR(asprof_err);

    dlclose(lib);

    lib = dlopen("my_lib_1.so", RTLD_NOW | RTLD_GLOBAL);
    local_malloc = (my_malloc_t) dlsym(lib, "my_malloc");
    ASSERT_NO_DLERROR();

    local_free = (my_free_t) dlsym(lib, "my_free");
    ASSERT_NO_DLERROR();

    asprof_err = _asprof_execute("start,cstack=dwarf,file=output2.jfr", outputCallback);
    ASSERT_NO_ASPROF_ERR(asprof_err);

    local_free(local_malloc(1010));

    asprof_err = _asprof_execute("stop", NULL);
    ASSERT_NO_ASPROF_ERR(asprof_err);

    dlclose(lib);

    asprof_err = _asprof_execute("start,nativemem,cstack=dwarf,file=output3.jfr", outputCallback);
    ASSERT_NO_ASPROF_ERR(asprof_err);
    asprof_err = _asprof_execute("stop", NULL);
    ASSERT_NO_ASPROF_ERR(asprof_err);
}
  • lib.h
#ifndef MYLIB_H
#define MYLIB_H

#define EXPORT __attribute__((visibility("default")))

#include "stdlib.h"


EXPORT void* my_malloc(size_t size);

EXPORT void my_free(void* ptr);

#endif
  • lib.c
#include "lib.h"

EXPORT void* my_malloc(size_t size) {
    return malloc(size);
}

EXPORT void my_free(void* ptr) {
    free(ptr);
}

Compile the files

  • g++ -I${PATH_TO_ASYNC_PROFILER_SRC_DIR} main.cpp -o main -ldl -lpthread -g
  • gcc -fPIC lib.c -shared -o my_lib_1.so
  • gcc -fPIC lib.c -shared -o my_lib.so

Run the test:

  • LD_LIBRARY_PATH=pwd:${PATH_TO_ASYNC_PROFILER_LIB_DIR}./main

Additional Information/Context

PATH_TO_ASYNC_PROFILER_LIB_DIR ==> points to the directory containing the async-profiler so file
PATH_TO_ASYNC_PROFILER_SRC_DIR ==> points to the directory containing asprof.h file

Async-profiler version

latest

Environment details

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions