Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions e2e-tests/tests/fixtures/sample_program/sample_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,60 @@ void sink_void(const void* p) {
call_counter++;
(void)p;
}

/*
* Keep these fixture-only declarations below the existing code so hard-coded
* sample_lib.c line probes remain stable.
*/
#include <stdint.h>
#include <sys/mman.h>

typedef struct {
uint64_t value;
} EvictedPage;

static EvictedPage* evicted_page;
static size_t evicted_page_size;

// The body deliberately does not dereference page. Sleepable-uprobe tests
// attach here after the caller has confirmed that the page is nonresident.
__attribute__((noinline)) void process_evicted_page(const EvictedPage* page) {
__asm__ volatile("" : : "r"(page) : "memory");
}

void trigger_evicted_page_probe(void) {
if (evicted_page == NULL) {
long page_size = sysconf(_SC_PAGESIZE);
if (page_size <= 0) {
perror("sysconf(_SC_PAGESIZE)");
return;
}

evicted_page_size = (size_t)page_size;
evicted_page = mmap(NULL, evicted_page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (evicted_page == MAP_FAILED) {
evicted_page = NULL;
perror("mmap");
return;
}
}

// Materialize the page, discard it, and verify that no backing page is
// resident before entering the probe target. MADV_DONTNEED makes the next
// successful read fault in a zero-filled anonymous page.
evicted_page->value = UINT64_C(0x1122334455667788);
if (madvise(evicted_page, evicted_page_size, MADV_DONTNEED) != 0) {
perror("madvise(MADV_DONTNEED)");
return;
}

unsigned char residency = 1;
if (mincore(evicted_page, evicted_page_size, &residency) != 0) {
perror("mincore");
return;
}
if ((residency & 1U) == 0) {
process_evicted_page(evicted_page);
}
}
3 changes: 3 additions & 0 deletions e2e-tests/tests/fixtures/sample_program/sample_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ void cleanup_test_lib();
// Void pointer sink for pointer-arithmetic fallback tests
void sink_void(const void* p);

// Trigger a probe target with a confirmed nonresident anonymous page.
void trigger_evicted_page_probe(void);

#endif // TEST_LIB_H
1 change: 1 addition & 0 deletions e2e-tests/tests/fixtures/sample_program/sample_program.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ int main() {

// Void pointer sink call for pointer-arithmetic fallback tests
sink_void(numbers);
trigger_evicted_page_probe();

// printf("Sleeping for 2 seconds...\n");
sleep(2); // Sleep for 2 seconds
Expand Down
60 changes: 60 additions & 0 deletions e2e-tests/tests/script_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,66 @@ sleepable_uprobe = false
Ok(())
}

#[tokio::test]
async fn test_sleepable_uprobe_faults_in_an_evicted_user_page() -> anyhow::Result<()> {
init();
ensure_global_cleanup_registered();

let target = get_global_test_target_with_opt(OptimizationLevel::Debug).await?;
let script_content = r#"
trace process_evicted_page {
if (page.value == 0) {
print "SLEEPABLE_PAGE_FAULT_OK";
}
}
"#;

let (default_exit_code, default_stdout, default_stderr) =
common::runner::GhostscopeRunner::new()
.with_script(script_content)
.attach_to(&target)
.timeout_secs(5)
.enable_sysmon_for_target(false)
.with_log_level("info")
.run()
.await?;

assert_eq!(
default_exit_code, 0,
"stderr={default_stderr} stdout={default_stdout}"
);
assert!(
!default_stdout.contains("SLEEPABLE_PAGE_FAULT_OK")
&& default_stdout.contains("ExprError"),
"ordinary uprobe unexpectedly read the evicted page. stdout={default_stdout} stderr={default_stderr}"
);
assert!(
!default_stderr.contains("Probing helpers required for sleepable uprobes")
&& !default_stderr.contains("Probing sleepable tail-call support"),
"default startup unexpectedly ran sleepable capability probes. stderr={default_stderr}"
);

let (exit_code, stdout, stderr) = common::runner::GhostscopeRunner::new()
.with_script(script_content)
.attach_to(&target)
.timeout_secs(5)
.enable_sysmon_for_target(false)
.with_cli_args(["--sleepable-uprobe"])
.run()
.await?;

assert_eq!(exit_code, 0, "stderr={stderr} stdout={stdout}");
assert!(
stdout.contains("SLEEPABLE_PAGE_FAULT_OK"),
"sleepable uprobe did not fault in and read the evicted page. stdout={stdout} stderr={stderr}"
);
assert!(
!stdout.contains("ExprError"),
"fault-capable user-memory read unexpectedly failed. stdout={stdout} stderr={stderr}"
);
Ok(())
}

#[tokio::test]
async fn test_sleepable_uprobe_backtrace_uses_a_kernel_supported_depth() -> anyhow::Result<()> {
init();
Expand Down
Loading
Loading