@@ -4,7 +4,10 @@ use crate::BenchmarkCommand;
44use crate :: constants;
55use crate :: uri;
66use codspeed:: instrument_hooks:: InstrumentHooks ;
7+ use std:: io:: Write ;
8+ use std:: os:: unix:: fs:: PermissionsExt ;
79use std:: process:: Command ;
10+ use std:: sync:: OnceLock ;
811
912pub fn perform ( commands : Vec < BenchmarkCommand > ) -> Result < ( ) > {
1013 let hooks = InstrumentHooks :: instance ( ) ;
@@ -30,19 +33,63 @@ pub fn perform(commands: Vec<BenchmarkCommand>) -> Result<()> {
3033 Ok ( ( ) )
3134}
3235
33- /// Path to the preload library built during compilation.
36+ /// Filename for the preload shared library.
37+ const PRELOAD_LIB_FILENAME : & str = env ! ( "CODSPEED_PRELOAD_LIB_FILENAME" ) ;
38+
39+ /// The preload library binary embedded at compile time.
3440/// This library is used for LD_PRELOAD-based instrumentation injection.
35- pub const PRELOAD_LIB_PATH : & str = env ! ( "CODSPEED_PRELOAD_LIB_PATH" ) ;
41+ const PRELOAD_LIB_BYTES : & [ u8 ] = include_bytes ! ( concat!(
42+ env!( "OUT_DIR" ) ,
43+ "/" ,
44+ env!( "CODSPEED_PRELOAD_LIB_FILENAME" )
45+ ) ) ;
46+
47+ /// Lazily initialized path to the extracted preload library.
48+ static PRELOAD_LIB_PATH : OnceLock < std:: path:: PathBuf > = OnceLock :: new ( ) ;
49+
50+ /// Extracts the preload library to a temp file and returns the path.
51+ fn extract_preload_lib ( ) -> Result < std:: path:: PathBuf > {
52+ let lib_path = std:: env:: temp_dir ( ) . join ( PRELOAD_LIB_FILENAME ) ;
53+
54+ let mut file = std:: fs:: File :: create ( & lib_path)
55+ . context ( "Failed to create temp file for preload library" ) ?;
56+
57+ file. write_all ( PRELOAD_LIB_BYTES )
58+ . context ( "Failed to write preload library to temp file" ) ?;
59+
60+ // Make the library executable
61+ let mut permissions = file
62+ . metadata ( )
63+ . context ( "Failed to get temp file metadata" ) ?
64+ . permissions ( ) ;
65+ permissions. set_mode ( 0o755 ) ;
66+ file. set_permissions ( permissions)
67+ . context ( "Failed to set temp file permissions" ) ?;
68+
69+ Ok ( lib_path)
70+ }
71+
72+ /// Returns the path to the preload library, extracting it to a temp file if needed.
73+ fn get_preload_lib_path ( ) -> Result < & ' static std:: path:: Path > {
74+ if let Some ( path) = PRELOAD_LIB_PATH . get ( ) {
75+ return Ok ( path) ;
76+ }
77+
78+ let path = extract_preload_lib ( ) ?;
79+ Ok ( PRELOAD_LIB_PATH . get_or_init ( || path) )
80+ }
3681
3782pub fn perform_with_valgrind ( commands : Vec < BenchmarkCommand > ) -> Result < ( ) > {
83+ let preload_lib_path = get_preload_lib_path ( ) ?;
84+
3885 for benchmark_cmd in commands {
3986 let name_and_uri = uri:: generate_name_and_uri ( & benchmark_cmd. name , & benchmark_cmd. command ) ;
4087 name_and_uri. print_executing ( ) ;
4188
4289 let mut cmd = Command :: new ( & benchmark_cmd. command [ 0 ] ) ;
4390 cmd. args ( & benchmark_cmd. command [ 1 ..] ) ;
4491 // Use LD_PRELOAD to inject instrumentation into the child process
45- cmd. env ( "LD_PRELOAD" , PRELOAD_LIB_PATH ) ;
92+ cmd. env ( "LD_PRELOAD" , preload_lib_path ) ;
4693 cmd. env ( constants:: URI_ENV , & name_and_uri. uri ) ;
4794
4895 let status = cmd. status ( ) . context ( "Failed to execute command" ) ?;
0 commit comments