diff --git a/Makefile b/Makefile index 2883b28b..c981f8f2 100644 --- a/Makefile +++ b/Makefile @@ -5,16 +5,13 @@ MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules .PHONY: build -build: filprofiler/_filpreload.so filprofiler/libpymemprofile_api.so build_ext +build: filprofiler/libpymemprofile_api.so build_ext .PHONY: build_ext build_ext: filprofiler/libpymemprofile_api.so python3.8 setup.py build_ext --inplace -filprofiler/_filpreload.so: filprofiler/_filpreload.c - gcc -std=c11 -D_FORTIFY_SOURCE=2 -fasynchronous-unwind-tables -fstack-clash-protection -fstack-protector -Werror=format-security -Werror=implicit-function-declaration -O2 -shared -ldl -g -fPIC -fvisibility=hidden -Wall -o $@ $< - -filprofiler/libpymemprofile_api.so: Cargo.lock memapi/Cargo.toml memapi/src/*.rs +filprofiler/libpymemprofile_api.so: Cargo.lock memapi/Cargo.toml memapi/src/*.rs memapi/src/*.c memapi/build.rs rm -f filprofiler/libymemprofile_api.so cargo build --release cp -f target/release/libpymemprofile_api.so filprofiler/ diff --git a/filprofiler/_script.py b/filprofiler/_script.py index 5b30b3b8..9d2cf255 100644 --- a/filprofiler/_script.py +++ b/filprofiler/_script.py @@ -22,8 +22,8 @@ def stage_1(): # Route all allocations from Python through malloc() directly: environ["PYTHONMALLOC"] = "malloc" # Library setup: - environ["LD_PRELOAD"] = library_path("_filpreload") - environ["FIL_API_LIBRARY"] = library_path("libpymemprofile_api") + environ["LD_PRELOAD"] = library_path("libpymemprofile_api") + # Disable multi-threaded backends in various scientific computing libraries # (Zarr uses Blosc, NumPy uses BLAS): environ["BLOSC_NTHREADS"] = "1" diff --git a/filprofiler/_tracer.py b/filprofiler/_tracer.py index f4d4fc7b..0750e882 100644 --- a/filprofiler/_tracer.py +++ b/filprofiler/_tracer.py @@ -8,10 +8,9 @@ from ._utils import library_path -# Load with RTLD_GLOBAL so _profiler.so has access to those symbols; explicit -# linking may be possible but haven't done that yet, oh well. -pymemprofile = CDLL(library_path("libpymemprofile_api"), mode=RTLD_GLOBAL) -preload = CDLL(library_path("_filpreload"), mode=RTLD_GLOBAL) +# Load with RTLD_GLOBAL so _profiler.so has access to those symbols; TODO +# explicit linking may be possible but haven't done that yet. +preload = CDLL(library_path("libpymemprofile_api"), mode=RTLD_GLOBAL) from . import _profiler diff --git a/memapi/build.rs b/memapi/build.rs index 1e8f9cae..6c6c69d4 100644 --- a/memapi/build.rs +++ b/memapi/build.rs @@ -1,6 +1,10 @@ use cc; +use std::env; fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + println!("cargo:rerun-if-changed=src/filpreload.c"); + println!("cargo:rustc-link-search=native={}", out_dir); cc::Build::new() .file("src/filpreload.c") .warnings_into_errors(true) @@ -8,6 +12,9 @@ fn main() { .flag("-Wall") .flag("-Werror=format-security") .flag("-Werror=implicit-function-declaration") + .static_flag(true) + .shared_flag(false) + .flag("-fvisibility=hidden") + .cargo_metadata(false) .compile("filpreload"); - println!("cargo:rerun-if-changed=src/filpreload.c"); } diff --git a/memapi/src/filpreload.c b/memapi/src/filpreload.c index ce3d332d..c25b0c91 100644 --- a/memapi/src/filpreload.c +++ b/memapi/src/filpreload.c @@ -11,10 +11,6 @@ static void *(*underlying_real_mmap)(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0; static void (*underlying_real_free)(void *addr) = 0; -// The internal API we're notifying of allocations: -static void (*add_allocation_hook)(size_t address, size_t length) = 0; -static void (*free_allocation_hook)(size_t address) = 0; - // Note whether we've been initialized yet or not: static int initialized = 0; @@ -29,23 +25,6 @@ static void __attribute__((constructor)) constructor() { fprintf(stderr, "BUG: expected size of size_t and void* to be the same.\n"); exit(1); } - void *lib = - dlopen(getenv("FIL_API_LIBRARY"), RTLD_NOW | RTLD_DEEPBIND | RTLD_GLOBAL); - if (!lib) { - fprintf(stderr, "Couldn't load libpymemprofile_api.so library: %s\n", - dlerror()); - exit(1); - } - add_allocation_hook = dlsym(lib, "pymemprofile_add_allocation"); - if (!add_allocation_hook) { - fprintf(stderr, "Couldn't load pymemprofile API function: %s\n", dlerror()); - exit(1); - } - free_allocation_hook = dlsym(lib, "pymemprofile_free_allocation"); - if (!free_allocation_hook) { - fprintf(stderr, "Couldn't load pymemprofile API function: %s\n", dlerror()); - exit(1); - } underlying_real_mmap = dlsym(RTLD_NEXT, "mmap"); if (!underlying_real_mmap) { fprintf(stderr, "Couldn't load mmap(): %s\n", dlerror()); @@ -61,10 +40,14 @@ static void __attribute__((constructor)) constructor() { extern void *__libc_malloc(size_t size); extern void *__libc_calloc(size_t nmemb, size_t size); + +// The Rust API in lib.rs: +extern void pymemprofile_add_allocation(size_t address, size_t length); +extern void pymemprofile_free_allocation(size_t address); extern void pymemprofile_start_call(const char *filename, const char *funcname); extern void pymemprofile_finish_call(); extern void pymemprofile_reset(); -extern void pymemprofile_dump_peak_to_flamegraph(const char* path); +extern void pymemprofile_dump_peak_to_flamegraph(const char *path); __attribute__((visibility("default"))) void fil_start_call(const char *filename, const char *funcname) { @@ -91,7 +74,8 @@ __attribute__((visibility("default"))) void fil_reset() { } } -__attribute__((visibility("default"))) void fil_dump_peak_to_flamegraph(const char* path) { +__attribute__((visibility("default"))) void +fil_dump_peak_to_flamegraph(const char *path) { if (!will_i_be_reentrant) { will_i_be_reentrant = 1; pymemprofile_dump_peak_to_flamegraph(path); @@ -104,7 +88,7 @@ __attribute__((visibility("default"))) void *malloc(size_t size) { void *result = __libc_malloc(size); if (!will_i_be_reentrant && initialized) { will_i_be_reentrant = 1; - add_allocation_hook((size_t)result, size); + pymemprofile_add_allocation((size_t)result, size); will_i_be_reentrant = 0; } return result; @@ -115,7 +99,7 @@ __attribute__((visibility("default"))) void *calloc(size_t nmemb, size_t size) { size_t allocated = nmemb * size; if (!will_i_be_reentrant && initialized) { will_i_be_reentrant = 1; - add_allocation_hook((size_t)result, allocated); + pymemprofile_add_allocation((size_t)result, allocated); will_i_be_reentrant = 0; } return result; @@ -129,7 +113,7 @@ __attribute__((visibility("default"))) void free(void *addr) { underlying_real_free(addr); if (!will_i_be_reentrant) { will_i_be_reentrant = 1; - free_allocation_hook((size_t)addr); + pymemprofile_free_allocation((size_t)addr); will_i_be_reentrant = 0; } } diff --git a/memapi/src/lib.rs b/memapi/src/lib.rs index 13daa75d..1430a30c 100644 --- a/memapi/src/lib.rs +++ b/memapi/src/lib.rs @@ -51,5 +51,13 @@ pub unsafe extern "C" fn pymemprofile_dump_peak_to_flamegraph(path: *const c_cha memorytracking::dump_peak_to_flamegraph(&path); } +// Re-export symbols from filpreload.c +#[link(name = "filpreload", kind = "static")] +extern "C" { + pub fn malloc(size: libc::size_t) -> *mut libc::c_void; + pub fn free(addr: *mut libc::c_void); + pub fn fil_dump_peak_to_flamegraph(path: *const c_char); +} + #[cfg(test)] mod tests {}