Skip to content

Commit

Permalink
feat: simplify harness runtime api
Browse files Browse the repository at this point in the history
fixes #53
  • Loading branch information
Gankra committed Jul 7, 2024
1 parent 03b552f commit 1503ced
Show file tree
Hide file tree
Showing 15 changed files with 338 additions and 529 deletions.
48 changes: 21 additions & 27 deletions docs/src/harness/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,40 @@ uint64_t basic_val(MyStruct arg0, int32_t arg1);

// The test harness will invoke your test through this symbol!
void do_test(void) {
// Declare we're running the first function
set_func(CALLER_VALS, 0);

// Initialize and report the inputs
MyStruct arg0 = { 241, 1234.23 };
write_field(CALLER_INPUTS, arg0.field0);
write_filed(CALLER_INPUTS, arg0.field1);
finished_val(CALLER_INPUTS);
write_val(CALLER_VALS, 0, arg0.field0);
write_val(CALLER_VALS, 1, arg0.field1);

int32_t arg1 = 5;
write_field(CALLER_INPUTS, arg1);
finished_val(CALLER_INPUTS);
write_val(CALLER_VALS, 2, arg1);

// Do the call
uint64_t output = basic_val(arg0, arg1);

// Report the output
write_field(CALLER_OUTPUTS, output);
finished_val(CALLER_OUTPUTS);

// Declare that the test is complete on our side
finished_func(CALLER_INPUTS, CALLER_OUTPUTS);
write_val(CALLER_VALS, 3, output);
}
```
```C
// Callee Side
uint64_t basic_val(MyStruct arg0, int32_t arg1) {
// Declare we're running the first function
set_func(CALLEE_VALS, 0);
// Report the inputs
write_field(CALLEE_INPUTS, arg0.field0);
write_field(CALLEE_INPUTS, arg0.field1);
finished_val(CALLEE_INPUTS);
write_val(CALLEE_VALS, 0, arg0.field0);
write_val(CALLEE_VALS, 1, arg0.field1);
write_field(CALLEE_INPUTS, arg1);
finished_val(CALLEE_INPUTS);
write_val(CALLEE_VALS, 2, arg1);
// Initialize and report the output
uint64_t output = 17;
write_field(CALLEE_OUTPUTS, output);
finished_val(CALLEE_OUTPUTS);
// Declare that the test is complete on our side
finished_func(CALLEE_INPUTS, CALLEE_OUTPUTS);
write_val(CALLEE_VALS, 3, output);
// Actually return
return output;
Expand All @@ -61,17 +55,17 @@ uint64_t basic_val(MyStruct arg0, int32_t arg1) {

The high level idea is that each side:

* Uses write_field to report individual fields of values (to avoid padding)
* Uses finished_val to specify that all fields for a value have been written
* Uses finished_func to specify that the current function is done (the caller will usually contain many subtests, FINISHED_FUNC delimits those)
* Uses set_func to specify which function we're running
* Uses write_val to report individual fields of args (to avoid padding)

There are 4 buffers: CALLER_INPUTS, CALLER_OUTPUTS, CALLEE_INPUTS, CALLEE_OUTPUTS. Each side should only use its two buffers.
There are 2 buffers: CALLER_VALS and CALLEE_VALS

The signatures of the callbacks are as follows, but each language wraps these
in functions/macros to keep the codegen readable:

* `WRITE(Buffer buffer, char* input, uint32_t size_of_input)`
* `FINISH_VAL(Buffer buffer)`
* `FINISH_TEST(Buffer input_buffer, Buffer output_buffer)`
```c
SET_FUNC(Buffer buffer, uint32_t func_idx)`
WRITE_VAL(Buffer buffer, uint32_t val_idx, char* input, uint32_t size_of_input)
```
Doing things in this very explicit way gives the test harness a better semantic understanding of what the implementations think is happening. This helps us emit better diagnostics and avoid cascading failures between subtests.
16 changes: 6 additions & 10 deletions include/harness/c/harness_prefix.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@

#define WriteBuffer void*

extern WriteBuffer CALLER_INPUTS;
extern WriteBuffer CALLER_OUTPUTS;
extern WriteBuffer CALLEE_INPUTS;
extern WriteBuffer CALLEE_OUTPUTS;
extern void (*WRITE_FIELD)(WriteBuffer, char*, uint32_t);
extern void (*FINISHED_VAL)(WriteBuffer);
extern void (*FINISHED_FUNC)(WriteBuffer, WriteBuffer);
extern WriteBuffer CALLER_VALS;
extern WriteBuffer CALLEE_VALS;
extern void (*WRITE_VAL)(WriteBuffer, uint32_t, char*, uint32_t);
extern void (*SET_FUNC)(WriteBuffer, uint32_t);

#define finished_val(buffer) FINISHED_VAL(buffer)
#define finished_func(inputs, outputs) FINISHED_FUNC(inputs, outputs)
#define write_field(buffer, field) WRITE_FIELD(buffer, (char*)&field, (uint32_t)sizeof(field))
#define set_func(vals, func_idx) SET_FUNC(vals, func_idx);
#define write_val(vals, val_idx, val) WRITE_VAL(vals, val_idx, (char*)&val, (uint32_t)sizeof(val))
41 changes: 14 additions & 27 deletions include/harness/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,47 +15,34 @@ pub struct WriteBuffer(*mut ());
unsafe impl Send for WriteBuffer {}
unsafe impl Sync for WriteBuffer {}

type WriteCallback = unsafe extern fn(WriteBuffer, *const u8, u32) -> ();
type FinishedValCallback = unsafe extern fn(WriteBuffer) -> ();
type FinishedFuncCallback = unsafe extern fn(WriteBuffer, WriteBuffer) -> ();
type SetFuncCallback = unsafe extern fn(WriteBuffer, u32) -> ();
type WriteValCallback = unsafe extern fn(WriteBuffer, u32, *const u8, u32) -> ();

#[no_mangle]
pub static mut CALLER_INPUTS: WriteBuffer = WriteBuffer(core::ptr::null_mut());
pub static mut CALLER_VALS: WriteBuffer = WriteBuffer(core::ptr::null_mut());
#[no_mangle]
pub static mut CALLER_OUTPUTS: WriteBuffer = WriteBuffer(core::ptr::null_mut());
pub static mut CALLEE_VALS: WriteBuffer = WriteBuffer(core::ptr::null_mut());
#[no_mangle]
pub static mut CALLEE_INPUTS: WriteBuffer = WriteBuffer(core::ptr::null_mut());
pub static mut SET_FUNC: Option<SetFuncCallback> = None;
#[no_mangle]
pub static mut CALLEE_OUTPUTS: WriteBuffer = WriteBuffer(core::ptr::null_mut());
#[no_mangle]
pub static mut WRITE_FIELD: Option<WriteCallback> = None;
#[no_mangle]
pub static mut FINISHED_VAL: Option<FinishedValCallback> = None;
#[no_mangle]
pub static mut FINISHED_FUNC: Option<FinishedFuncCallback> = None;
pub static mut WRITE_VAL: Option<WriteValCallback> = None;

extern {
fn do_test();
}

#[no_mangle]
pub extern fn test_start(
write_callback: WriteCallback,
finished_val_callback: FinishedValCallback,
finished_func_callback: FinishedFuncCallback,
caller_inputs: WriteBuffer,
caller_outputs: WriteBuffer,
callee_inputs: WriteBuffer,
callee_outputs: WriteBuffer,
set_func_callback: SetFuncCallback,
write_val_callback: WriteValCallback,
caller_vals: WriteBuffer,
callee_vals: WriteBuffer,
) {
unsafe {
CALLER_INPUTS = caller_inputs;
CALLER_OUTPUTS = caller_outputs;
CALLEE_INPUTS = callee_inputs;
CALLEE_OUTPUTS = callee_outputs;
WRITE_FIELD = Some(write_callback);
FINISHED_VAL = Some(finished_val_callback);
FINISHED_FUNC = Some(finished_func_callback);
CALLER_VALS = caller_vals;
CALLEE_VALS = callee_vals;
SET_FUNC = Some(set_func_callback);
WRITE_VAL = Some(write_val_callback);

do_test();
}
Expand Down
34 changes: 14 additions & 20 deletions include/harness/rust/harness_prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,24 @@ pub struct WriteBuffer(*mut ());
unsafe impl Send for WriteBuffer {}
unsafe impl Sync for WriteBuffer {}

type WriteCallback = unsafe extern fn(WriteBuffer, *const u8, u32) -> ();
type FinishedValCallback = unsafe extern fn(WriteBuffer) -> ();
type FinishedFuncCallback = unsafe extern fn(WriteBuffer, WriteBuffer) -> ();
type SetFuncCallback = unsafe extern fn(WriteBuffer, u32) -> ();
type WriteValCallback = unsafe extern fn(WriteBuffer, u32, *const u8, u32) -> ();

extern {
pub static mut CALLER_INPUTS: WriteBuffer;
pub static mut CALLER_OUTPUTS: WriteBuffer;
pub static mut CALLEE_INPUTS: WriteBuffer;
pub static mut CALLEE_OUTPUTS: WriteBuffer;
pub static mut WRITE_FIELD: Option<WriteCallback>;
pub static mut FINISHED_VAL: Option<FinishedValCallback>;
pub static mut FINISHED_FUNC: Option<FinishedFuncCallback>;
pub static mut CALLER_VALS: WriteBuffer;
pub static mut CALLEE_VALS: WriteBuffer;
pub static mut SET_FUNC: Option<SetFuncCallback>;
pub static mut WRITE_VAL: Option<WriteValCallback>;
}

unsafe fn write_field<T>(buffer: WriteBuffer, field: &T) {
WRITE_FIELD.unwrap()(
buffer,
field as *const T as *const u8,
core::mem::size_of_val(field) as u32
unsafe fn write_val<T>(vals: WriteBuffer, val_idx: u32, val: &T) {
WRITE_VAL.unwrap()(
vals,
val_idx,
val as *const T as *const u8,
core::mem::size_of_val(val) as u32
);
}
unsafe fn finished_val(buffer: WriteBuffer) {
FINISHED_VAL.unwrap()(buffer);
}
unsafe fn finished_func(inputs: WriteBuffer, outputs: WriteBuffer) {
FINISHED_FUNC.unwrap()(inputs, outputs);
unsafe fn set_func(vals: WriteBuffer, func_idx: u32) {
SET_FUNC.unwrap()(vals, func_idx);
}
30 changes: 4 additions & 26 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,6 @@ pub enum BuildError {
#[allow(clippy::enum_variant_names)]
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum CheckFailure {
#[error(" {func_name} {arg_kind} count mismatch (expected: {expected_len}, caller: {}, callee: {})
caller: {caller:#02X?}
callee: {callee:#02X?}", caller.len(), callee.len())]
ArgCountMismatch {
func_idx: usize,
arg_kind: String,
func_name: String,
expected_len: usize,
caller: Vec<Vec<Vec<u8>>>,
callee: Vec<Vec<Vec<u8>>>,
},
#[error(" {func_name} {arg_kind} {arg_name} value count mismatch (expected: {expected_len}, caller: {}, callee: {})
caller: {caller:#02X?}
callee: {callee:#02X?}", caller.len(), callee.len())]
ValCountMismatch {
func_idx: usize,
arg_idx: usize,
arg_kind: String,
func_name: String,
arg_name: String,
expected_len: usize,
caller: Vec<Vec<u8>>,
callee: Vec<Vec<u8>>,
},
#[error(
" {func_name} {arg_kind} differed:
{arg_kind:<6}: {arg_name}: {arg_ty_name}
Expand Down Expand Up @@ -155,6 +131,8 @@ pub enum LinkError {
pub enum RunError {
#[error("test loading error (dynamic linking failed)\n{0}")]
LoadError(#[from] libloading::Error),
#[error("wrong number of tests reported! \nExpected {0} \nGot (caller_in: {1}, caller_out: {2}, callee_in: {3}, callee_out: {4})")]
TestCountMismatch(usize, usize, usize, usize, usize),
#[error("test impl didn't call set_func before calling write_val")]
MissingSetFunc,
#[error("test impl called write_val on func {func} val {val} twice")]
DoubleWrite { func: usize, val: usize },
}
Loading

0 comments on commit 1503ced

Please sign in to comment.