From 000d0032a9df913cbca7e9fbb64227a9522a609f Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Wed, 14 Nov 2018 23:31:10 -0500 Subject: [PATCH] Don't use proc macro I really don't like the new syntax though, much as I don't like proc_macro it may be coming back. --- .gitignore | 1 + Cargo.toml | 4 +- qadapt-macro/.gitignore | 3 - qadapt-macro/Cargo.toml | 18 --- qadapt-macro/README.md | 6 - qadapt-macro/src/lib.rs | 136 -------------------- src/lib.rs | 41 +++++- tests/{allocations.rs => alloc_examples.rs} | 33 ++--- tests/macros.rs | 89 ------------- tests/usage.rs | 7 + 10 files changed, 54 insertions(+), 284 deletions(-) delete mode 100644 qadapt-macro/.gitignore delete mode 100644 qadapt-macro/Cargo.toml delete mode 100644 qadapt-macro/README.md delete mode 100644 qadapt-macro/src/lib.rs rename tests/{allocations.rs => alloc_examples.rs} (74%) delete mode 100644 tests/macros.rs create mode 100644 tests/usage.rs diff --git a/.gitignore b/.gitignore index 6936990..2fcd2ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk Cargo.lock +*.swp diff --git a/Cargo.toml b/Cargo.toml index 36cc0b4..b109c12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,4 @@ repository = "https://github.com/bspeice/qadapt.git" [dependencies] libc = "0.2" spin = "0.4" -thread-id = "3.3" - -qadapt-macro = { version = "0.3.0", path = "./qadapt-macro" } \ No newline at end of file +thread-id = "3.3" \ No newline at end of file diff --git a/qadapt-macro/.gitignore b/qadapt-macro/.gitignore deleted file mode 100644 index 6936990..0000000 --- a/qadapt-macro/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/qadapt-macro/Cargo.toml b/qadapt-macro/Cargo.toml deleted file mode 100644 index 847fa28..0000000 --- a/qadapt-macro/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "qadapt-macro" -version = "0.3.0" -authors = ["Bradlee Speice "] -description = "The Quick And Dirty Allocation Profiling Tool - Support Macros" -license = "Apache-2.0" -readme = "README.md" -categories = [ - "development-tools", - "development-tools::debugging", - "development-tools::profiling", - "development-tools::testing", - "memory-management" -] -repository = "https://github.com/bspeice/qadapt.git" - -[lib] -proc-macro = true diff --git a/qadapt-macro/README.md b/qadapt-macro/README.md deleted file mode 100644 index 424e993..0000000 --- a/qadapt-macro/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# QADAPT - Helper macros - -Helper macros to use with the QADAPT allocator system - -This crate is intended for managing the QADAPT allocator, -and is unusable on its own. \ No newline at end of file diff --git a/qadapt-macro/src/lib.rs b/qadapt-macro/src/lib.rs deleted file mode 100644 index 56b218a..0000000 --- a/qadapt-macro/src/lib.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! Helper macros to use with the QADAPT allocator system -//! -//! This crate is intended for managing the QADAPT allocator, -//! and is unusable on its own. -//! -// TODO: This causes issues, but I can't track down why -// #![deny(missing_docs)] -extern crate proc_macro; - -use proc_macro::Delimiter; -use proc_macro::Spacing; -use proc_macro::Span; -use proc_macro::TokenStream; -use proc_macro::TokenTree; -use std::iter::FromIterator; - -type TT = proc_macro::TokenTree; -type TS = proc_macro::TokenStream; -type G = proc_macro::Group; -type I = proc_macro::Ident; -type P = proc_macro::Punct; - -fn release_guard(fn_name: &str) -> TokenStream { - let rel = I::new("release", Span::call_site()); - let not_rel: Vec = vec![ - I::new("not", Span::call_site()).into(), - G::new(Delimiter::Parenthesis, TokenTree::Ident(rel).into()).into() - ]; - let cfg_not_rel: Vec = vec![ - I::new("cfg", Span::call_site()).into(), - G::new(Delimiter::Parenthesis, TS::from_iter(not_rel.into_iter())).into() - ]; - let guarded: Vec = vec![ - P::new('#', Spacing::Alone).into(), - G::new(Delimiter::Bracket, TS::from_iter(cfg_not_rel.into_iter())).into(), - P::new(':', Spacing::Joint).into(), - P::new(':', Spacing::Alone).into(), - I::new("qadapt", Span::call_site()).into(), - P::new(':', Spacing::Joint).into(), - P::new(':', Spacing::Alone).into(), - I::new(fn_name, Span::call_site()).into(), - G::new(Delimiter::Parenthesis, TokenStream::new()).into(), - ]; - - TS::from_iter(guarded.into_iter()) -} - -fn protected_body(fn_name: &str, args: G) -> TokenTree { - let mut args_filtered = Vec::new(); - let mut waiting_for_comma = false; - let mut in_type = 0; - for tt in args.stream().into_iter() { - match tt { - TokenTree::Ident(ref _i) if !waiting_for_comma && in_type == 0 => { - args_filtered.push(tt.clone()); - waiting_for_comma = true; - } - TokenTree::Punct(ref p) if p.as_char() == '<' => in_type += 1, - TokenTree::Punct(ref p) if p.as_char() == '>' => in_type -= 1, - TokenTree::Punct(ref p) if p.as_char() == ',' && in_type == 0 => { - waiting_for_comma = false; - args_filtered.push(tt.clone()) - } - _ => () - } - } - let args_group = G::new(Delimiter::Parenthesis, TS::from_iter(args_filtered)); - - let tt: Vec = vec![ - G::new(Delimiter::Brace, release_guard("enter_protected")).into(), - I::new("let", Span::call_site()).into(), - I::new("__ret__", Span::call_site()).into(), - P::new('=', Spacing::Alone).into(), - I::new(fn_name, Span::call_site()).into(), - args_group.into(), - P::new(';', Spacing::Alone).into(), - G::new(Delimiter::Brace, release_guard("exit_protected")).into(), - I::new("__ret__", Span::call_site()).into(), - ]; - - G::new(Delimiter::Brace, TS::from_iter(tt)).into() -} - -/// Set up the QADAPT allocator to trigger a panic if any allocations happen during -/// calls to this function. -/// -/// QADAPT will only track allocations in the thread that calls this function; -/// if (for example) this function receives the results of an allocation in a -/// separate thread, QADAPT will not trigger a panic. -#[proc_macro_attribute] -pub fn allocate_panic(_attr: TokenStream, item: TokenStream) -> TokenStream { - let mut original_fn: Vec = Vec::new(); - let mut protected_fn: Vec = Vec::new(); - - let mut item_iter = item.into_iter(); - - // First, get the function name we're replacing - let mut fn_name = None; - let mut fn_args = None; - while let Some(tt) = item_iter.next() { - match tt { - TokenTree::Ident(ref i) if i.to_string() == "fn" => { - original_fn.push(tt.clone()); - protected_fn.push(tt.clone()); - } - TokenTree::Ident(ref i) if fn_args.is_none() => { - let changed_name = format!("__{}__", i.to_owned()); - original_fn.push(TokenTree::Ident(I::new(&changed_name, Span::call_site()))); - protected_fn.push(tt.clone()); - fn_name = Some(changed_name); - } - TokenTree::Group(ref g) if g.delimiter() == Delimiter::Parenthesis && fn_args.is_none() => { - original_fn.push(tt.clone()); - protected_fn.push(tt.clone()); - fn_args = Some(g.clone()); - } - TokenTree::Group(ref g) if g.delimiter() == Delimiter::Brace => { - original_fn.push(tt.clone()); - protected_fn.push(protected_body( - &fn_name.take().unwrap(), - fn_args.take().unwrap(), - )); - } - tt => { - original_fn.push(tt.clone()); - protected_fn.push(tt.clone()); - } - } - } - - let mut full = Vec::new(); - full.push(TS::from_iter(original_fn)); - full.push(TS::from_iter(protected_fn)); - let ts = TS::from_iter(full.into_iter()); - ts -} diff --git a/src/lib.rs b/src/lib.rs index 9410835..8a74283 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,15 +12,11 @@ //! for some helper macros to make working with QADAPT a bit easier. #![deny(missing_docs)] extern crate libc; -extern crate qadapt_macro; extern crate spin; // thread_id is necessary because `std::thread::current()` panics if we have not yet // allocated a `thread_local!{}` it depends on. extern crate thread_id; -// Re-export the proc macros to use by other code -pub use qadapt_macro::*; - use libc::c_void; use libc::free; use libc::malloc; @@ -31,6 +27,7 @@ use std::thread; thread_local! { static PROTECTION_LEVEL: RwLock = RwLock::new(0); + static IS_STARTED: RwLock = RwLock::new(false); } /// The QADAPT allocator itself @@ -43,6 +40,10 @@ pub fn enter_protected() { return; } + IS_STARTED + .try_with(|b| assert!(*b.read(), "QADAPT not in use")) + .unwrap_or_else(|_e| ()); + PROTECTION_LEVEL .try_with(|v| { *v.write() += 1; @@ -70,6 +71,17 @@ pub fn exit_protected() { .unwrap_or_else(|_e| ()); } +/// Given a closure, run a function inside a guarded allocation block +pub fn do_protected(mut c: C) -> T +where + C: FnMut() -> T +{ + enter_protected(); + let ret = c(); + exit_protected(); + ret +} + static INTERNAL_ALLOCATION: RwLock = RwLock::new(usize::max_value()); /// Get the current "protection level" in QADAPT: calls to enter_protected() - exit_protected() @@ -108,6 +120,9 @@ unsafe impl GlobalAlloc for QADAPT { return malloc(layout.size()) as *mut u8; } + IS_STARTED.try_with(|b| *b.write() = true) + .unwrap_or_else(|_e| ()); + // Because accessing PROTECTION_LEVEL has the potential to trigger an allocation, // we need to spin until we can claim the INTERNAL_ALLOCATION lock for our thread. claim_internal_alloc(); @@ -131,7 +146,7 @@ unsafe impl GlobalAlloc for QADAPT { } } - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { if alloc_immediate() { return free(ptr as *mut c_void); } @@ -148,15 +163,27 @@ unsafe impl GlobalAlloc for QADAPT { // Tripped a bad dealloc, but make sure further memory access during unwind // doesn't have issues PROTECTION_LEVEL.with(|v| *v.write() = 0); - /* panic!( "Unexpected deallocation for size {}, protection level: {}", layout.size(), v ) - */ } _ => (), } } } + +#[macro_export] +macro_rules! alloc_panic{ + ($fn_body:expr) => {{ + #[cfg(any(test, debug))] + { + ::qadapt::do_protected(|| $fn_body) + } + #[cfg(not(any(test, debug)))] + { + $fn_body + } + }} +} diff --git a/tests/allocations.rs b/tests/alloc_examples.rs similarity index 74% rename from tests/allocations.rs rename to tests/alloc_examples.rs index 1547832..40319da 100644 --- a/tests/allocations.rs +++ b/tests/alloc_examples.rs @@ -1,4 +1,4 @@ -#![feature(asm)] +#[macro_use] extern crate qadapt; use qadapt::enter_protected; @@ -9,17 +9,14 @@ use qadapt::QADAPT; #[global_allocator] static Q: QADAPT = QADAPT; -pub fn black_box(dummy: T) -> T { - // Taken from test lib, need to mark the arg as non-introspectable - unsafe { asm!("" : : "r"(&dummy)) } - dummy -} - #[test] fn test_copy() { - enter_protected(); - black_box(0u8); - exit_protected(); + let z = alloc_panic!({ + let zero = 0; + zero + }); + + assert_eq!(z, 0); } #[test] @@ -41,16 +38,8 @@ fn unit_result(b: bool) -> Result<(), ()> { #[test] fn test_unit_result() { enter_protected(); - #[allow(unused)] - { - black_box(unit_result(true)); - } - black_box(unit_result(true)).unwrap(); - #[allow(unused)] - { - black_box(unit_result(false)); - } - black_box(unit_result(false)).unwrap_err(); + unit_result(true).unwrap(); + unit_result(false).unwrap_err(); exit_protected(); } @@ -80,14 +69,14 @@ fn test_vec_push_capacity() { #[test] fn test_vec_with_zero() { enter_protected(); - let _v: Vec = black_box(Vec::with_capacity(0)); + let _v: Vec = Vec::with_capacity(0); exit_protected(); } #[test] fn test_vec_new() { enter_protected(); - let _v: Vec = black_box(Vec::new()); + let _v: Vec = Vec::new(); exit_protected(); } diff --git a/tests/macros.rs b/tests/macros.rs deleted file mode 100644 index cd5d252..0000000 --- a/tests/macros.rs +++ /dev/null @@ -1,89 +0,0 @@ -extern crate qadapt; -use std::io; - -use qadapt::allocate_panic; -use qadapt::QADAPT; - -#[global_allocator] -static Q: QADAPT = QADAPT; - -#[allocate_panic] -fn no_allocate() { - #[cfg(not(release))] - { - let _v = 0; - } - assert_eq!(::qadapt::protection_level(), 1); - let _v: Vec<()> = Vec::with_capacity(0); -} - -#[test] -fn macro_no_allocate() { - no_allocate(); -} - -#[allocate_panic] -fn allocates() { - assert_eq!(::qadapt::protection_level(), 1); - // Without boxing, release profile can actually optimize out the allocation - let mut v = Box::new(Vec::new()); - v.push(1); -} - -#[test] -#[should_panic] -fn macro_allocates() { - allocates(); -} - -#[allocate_panic] -fn no_allocate_ret() -> bool { - return true; -} - -#[test] -fn macro_return() { - assert!(no_allocate_ret()); -} - -#[allocate_panic] -fn no_allocate_implicit_ret() -> bool { - true -} - -#[test] -fn macro_implicit_return() { - assert!(no_allocate_implicit_ret()); -} - -#[allocate_panic] -fn no_allocate_arg(b: bool) -> bool { - b -} - -#[test] -fn macro_allocate_arg() { - no_allocate_arg(true); - no_allocate_arg(false); -} - -#[allocate_panic] -fn no_allocate_args(_b: bool, _u: usize, i: i64) -> i64 { - i -} - -#[test] -fn macro_allocate_args() { - no_allocate_args(true, 0, -1); - no_allocate_args(false, 4, -90); -} - -#[allocate_panic] -fn return_result(r: Result) -> Result, ()> { - Ok(r) -} - -#[test] -fn macro_return_result() { - return_result(Ok(16)).unwrap().unwrap(); -} diff --git a/tests/usage.rs b/tests/usage.rs new file mode 100644 index 0000000..5f0c247 --- /dev/null +++ b/tests/usage.rs @@ -0,0 +1,7 @@ +extern crate qadapt; + +#[test] +#[should_panic] +fn panic_not_using_qadapt() { + ::qadapt::enter_protected(); +} \ No newline at end of file