diff --git a/integration-tests/src/tests.rs b/integration-tests/src/tests.rs index 7e000ca1..a6295cb2 100644 --- a/integration-tests/src/tests.rs +++ b/integration-tests/src/tests.rs @@ -201,6 +201,18 @@ impl Analysis { } } +fn assert_allocation_backtrace( alloc: &Allocation, expected: &[&str] ) { + let mut actual: Vec< _ > = alloc.backtrace.iter().map( |frame| frame.raw_function.clone().unwrap_or( String::new() ) ).collect(); + actual.reverse(); + + let matches = actual.len() >= expected.len() && actual.iter().zip( expected.iter() ).all( |(lhs, rhs)| lhs == rhs ); + if matches { + return; + } + + panic!( "Unexpected backtrace!\n\nActual:\n{}\n\nExpected to start with:\n{}\n", actual.join( "\n" ), expected.join( "\n" ) ); +} + fn workdir() -> PathBuf { let path = repository_root().join( "target" ); let workdir = if let Some( target ) = target() { @@ -789,3 +801,121 @@ fn test_dlopen() { assert_eq!( a0.size, 123123 ); assert_eq!( iter.next(), None ); } + +#[test] +fn test_throw() { + let cwd = workdir(); + compile( "throw.cpp" ); + + run_on_target( + &cwd, + "./throw", + EMPTY_ARGS, + &[ + ("LD_PRELOAD", preload_path().into_os_string()), + ("MEMORY_PROFILER_LOG", "debug".into()), + ("MEMORY_PROFILER_OUTPUT", "throw.dat".into()) + ] + ).assert_success(); + + let analysis = analyze( "throw", cwd.join( "throw.dat" ) ); + let a0 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123456 ).unwrap(); + let a1 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123457 ).unwrap(); + let a2 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123458 ).unwrap(); + let a3 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123459 ).unwrap(); + + assert_allocation_backtrace( a0, &[ + "foobar_0", + "foobar_1", + "foobar_2", + "foobar_3", + "foobar_4", + "foobar_5", + "main" + ]); + + assert_allocation_backtrace( a1, &[ + "foobar_3", + "foobar_4", + "foobar_5", + "main" + ]); + + assert_allocation_backtrace( a2, &[ + "foobar_5", + "main" + ]); + + assert_allocation_backtrace( a3, &[ + "main" + ]); +} + +#[test] +fn test_longjmp() { + let cwd = workdir(); + compile( "longjmp.c" ); + + run_on_target( + &cwd, + "./longjmp", + EMPTY_ARGS, + &[ + ("LD_PRELOAD", preload_path().into_os_string()), + ("MEMORY_PROFILER_LOG", "debug".into()), + ("MEMORY_PROFILER_OUTPUT", "longjmp.dat".into()) + ] + ).assert_success(); + + let analysis = analyze( "longjmp", cwd.join( "longjmp.dat" ) ); + let a0 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123456 ).unwrap(); + let a1 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123457 ).unwrap(); + let a2 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123458 ).unwrap(); + let a3 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123459 ).unwrap(); + + assert_allocation_backtrace( a0, &[ + "foobar_0", + "foobar_1", + "foobar_2", + "foobar_3", + "foobar_4", + "foobar_5", + "main" + ]); + + assert_allocation_backtrace( a1, &[ + "foobar_3", + "foobar_4", + "foobar_5", + "main" + ]); + + assert_allocation_backtrace( a2, &[ + "foobar_5", + "main" + ]); + + assert_allocation_backtrace( a3, &[ + "main" + ]); +} + +#[test] +fn test_backtrace() { + let cwd = workdir(); + compile_with_flags( "backtrace.c", &[ "-rdynamic" ] ); + + run_on_target( + &cwd, + "./backtrace", + EMPTY_ARGS, + &[ + ("LD_PRELOAD", preload_path().into_os_string()), + ("MEMORY_PROFILER_LOG", "debug".into()), + ("MEMORY_PROFILER_OUTPUT", "backtrace.dat".into()) + ] + ).assert_success(); + + let analysis = analyze( "backtrace", cwd.join( "backtrace.dat" ) ); + assert!( analysis.response.allocations.iter().any( |alloc| alloc.size == 123456 ) ); +} diff --git a/integration-tests/test-programs/backtrace.c b/integration-tests/test-programs/backtrace.c new file mode 100644 index 00000000..c907a697 --- /dev/null +++ b/integration-tests/test-programs/backtrace.c @@ -0,0 +1,31 @@ +#include +#include + +void __attribute__ ((noinline)) foo() { + malloc( 123456 ); + + void * buffer[ 32 ]; + const int count = backtrace( buffer, 32 ); + if( count == 0 ) { + exit( 1 ); + } + char ** symbols = backtrace_symbols( buffer, count ); + free( symbols ); +} + +void __attribute__ ((noinline)) bar() { + foo(); + + void * buffer[ 32 ]; + const int count = backtrace( buffer, 32 ); + if( count == 0 ) { + exit( 1 ); + } + char ** symbols = backtrace_symbols( buffer, count ); + free( symbols ); +} + +int main() { + bar(); + return 0; +} diff --git a/integration-tests/test-programs/longjmp.c b/integration-tests/test-programs/longjmp.c new file mode 100644 index 00000000..14f77196 --- /dev/null +++ b/integration-tests/test-programs/longjmp.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +int catch_1 = 0; +int catch_2 = 0; + +int f1 = 0; +int f2 = 0; +int f3a = 0; +int f3b = 0; +int f4 = 0; +int f5a = 0; +int f5b = 0; + +jmp_buf buf_a; +jmp_buf buf_b; + +void __attribute__ ((noinline)) foobar_0() { + malloc( 123456 ); +} + +void __attribute__ ((noinline)) foobar_1() { + foobar_0(); + + printf( ">> before throw\n" ); + longjmp( buf_a, 1 ); + f1 = 1; +} + +void __attribute__ ((noinline)) foobar_2() { + foobar_1(); + f2 = 1; +} + +void __attribute__ ((noinline)) foobar_3() { + printf( ">> before try\n" ); + if( setjmp( buf_a ) == 0 ) { + foobar_2(); + f3a = 1; + } else { + catch_1 = 1; + printf( ">> inside catch\n" ); + malloc( 123457 ); + longjmp( buf_b, 1 ); + } + f3b = 1; +} + +void __attribute__ ((noinline)) foobar_4() { + foobar_3(); + f4 = 1; +} + +void __attribute__ ((noinline)) foobar_5() { + if( setjmp( buf_b ) == 0 ) { + foobar_4(); + f5a = 1; + } else { + catch_2 = 1; + malloc( 123458 ); + } + f5b = 1; +} + +int main() { + printf( ">> start\n" ); + foobar_5(); + + if( catch_1 && catch_2 && !f1 && !f2 && !f3a && !f3b && !f4 && !f5a && f5b ) { + malloc( 123459 ); + return 0; + } + + abort(); +} diff --git a/integration-tests/test-programs/throw.cpp b/integration-tests/test-programs/throw.cpp new file mode 100644 index 00000000..d912dd65 --- /dev/null +++ b/integration-tests/test-programs/throw.cpp @@ -0,0 +1,77 @@ +#include +#include +#include + +bool catch_1 = false; +bool catch_2 = false; + +bool f1 = false; +bool f2 = false; +bool f3a = false; +bool f3b = false; +bool f4 = false; +bool f5a = false; +bool f5b = false; + +extern "C" { + +void __attribute__ ((noinline)) foobar_0() { + malloc( 123456 ); +} + +void __attribute__ ((noinline)) foobar_1() { + foobar_0(); + + printf( ">> before throw\n" ); + throw "dummy"; + f1 = true; +} + +void __attribute__ ((noinline)) foobar_2() { + foobar_1(); + f2 = true; +} + +void __attribute__ ((noinline)) foobar_3() { + printf( ">> before try\n" ); + try { + foobar_2(); + f3a = true; + } catch (...) { + catch_1 = true; + printf( ">> inside catch\n" ); + malloc( 123457 ); + throw; + } + f3b = true; +} + +void __attribute__ ((noinline)) foobar_4() { + foobar_3(); + f4 = true; +} + +void __attribute__ ((noinline)) foobar_5() { + try { + foobar_4(); + f5a = true; + } catch (...) { + catch_2 = true; + malloc( 123458 ); + } + f5b = true; +} + +} + +int main() { + printf( ">> start\n" ); + foobar_5(); + + if( catch_1 && catch_2 && !f1 && !f2 && !f3a && !f3b && !f4 && !f5a && f5b ) { + malloc( 123459 ); + return 0; + } + + abort(); +}