Skip to content

Commit

Permalink
add buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
kardeiz committed Nov 20, 2019
1 parent 09c8efe commit 0ac06a1
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 48 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
[package]
name = "funtime"
version = "0.1.0"
version = "0.2.0"
authors = ["Jacob Brown <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "A proc-macro helper to time Rust functions"
readme = "README.md"
keywords = ["stopwatch", "timing"]

[dependencies]
syn = { version = "1", features = ["full", "extra-traits"] }
quote = "1"
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# funtime

[![Docs](https://docs.rs/funtime/badge.svg)](https://docs.rs/crate/funtime/)
[![Crates.io](https://img.shields.io/crates/v/funtime.svg)](https://crates.io/crates/funtime)

A small proc-macro helper to time every statement in a given function (or item method).

## Usage

```rust
#[funtime::timed]
fn foo(y: i32) -> i32 {
let mut x = 1;
let d = 1_000;
x += d;
x += y;
x
}

#[funtime::timed]
fn main() {
foo(23);
}
```

Current version: 0.2.0
28 changes: 13 additions & 15 deletions examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
#[funtime::timed]
fn foo() -> i32 {

let mut x = 1;

x += 1;

10

}


fn main() {
foo();
}
#[funtime::timed]
fn foo(y: i32) -> i32 {
let mut x = 1;
let d = 1_000;
x += d;
x += y;
x
}

#[funtime::timed]
fn main() {
foo(23);
}
81 changes: 49 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,67 +1,86 @@
extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::*;

#[proc_macro_attribute]
pub fn timed(attrs: TokenStream, item: TokenStream) -> TokenStream {

pub fn timed(_attrs: TokenStream, item: TokenStream) -> TokenStream {
if let Ok(mut fun) = parse::<ItemFn>(item.clone()) {
let new_stmts = rewrite_stmts(fun.sig.ident.to_string(), &fun.block.stmts);
let new_stmts = rewrite_stmts(fun.sig.ident.to_string(), &mut fun.block.stmts);
fun.block.stmts = new_stmts;
return quote!(#fun).into();
}

if let Ok(mut fun) = parse::<TraitItemMethod>(item.clone()) {
if let Some(block) = fun.default.as_mut() {
let new_stmts = rewrite_stmts(fun.sig.ident.to_string(), &block.stmts);
let new_stmts = rewrite_stmts(fun.sig.ident.to_string(), &mut block.stmts);
block.stmts = new_stmts;
return quote!(#fun).into();
}
}
}

if let Ok(mut fun) = parse::<ImplItemMethod>(item) {
let new_stmts = rewrite_stmts(fun.sig.ident.to_string(), &fun.block.stmts);
let new_stmts = rewrite_stmts(fun.sig.ident.to_string(), &mut fun.block.stmts);
fun.block.stmts = new_stmts;
return quote!(#fun).into();
}

panic!("`funtime::timed` only works on functions")
}

fn rewrite_stmts(name: String, stmts: &[Stmt]) -> Vec<Stmt> {

fn rewrite_stmts(name: String, stmts: &mut Vec<Stmt>) -> Vec<Stmt> {
let setup: Block = parse_quote! {{
struct FuntimeTimer(std::time::Instant, String);
struct FuntimeTimer {
start: std::time::Instant,
name: &'static str,
buffer: String,
prev_mark: Option<std::time::Duration>,
}


impl Drop for FuntimeTimer {
fn drop(&mut self) {
println!("Took {:?}: `{}` complete", self.0.elapsed(), &self.1);
use std::fmt::Write;
writeln!(&mut self.buffer, "funtime end: `{}` took {:?}", self.name, self.start.elapsed()).unwrap();
print!("{}", &self.buffer);
}
}

impl FuntimeTimer {
fn print_elapsed(&self, short: &str) {
println!("Took {:?}: `{}`", self.0.elapsed(), short);
fn new(name: &'static str) -> Self {
use std::fmt::Write;
let mut buffer = String::new();
writeln!(&mut buffer, "funtime start: `{}`", name).unwrap();
FuntimeTimer {
start: std::time::Instant::now(),
name,
buffer,
prev_mark: None,
}
}

fn mark_elapsed(&mut self, short: &str) {
use std::fmt::Write;
let mut elapsed = self.start.elapsed();
if let Some(prev) = self.prev_mark.replace(elapsed) {
elapsed = elapsed - prev;
}
writeln!(&mut self.buffer, " took {:?}: `{}`", elapsed, short).unwrap();
}
}

let funtime_timer = FuntimeTimer(std::time::Instant::now(), String::from(#name));
let mut funtime_timer = FuntimeTimer::new(#name);

}};

let mut new_stmts = setup.stmts;

for stmt in stmts {
new_stmts.push(stmt.clone());

if let Stmt::Expr(..) = stmt {
continue;
}

let mut short = format!("{}", quote::ToTokens::to_token_stream(stmt)).chars().collect::<Vec<_>>();
let last = stmts.pop();

for stmt in stmts.drain(..) {
let short =
format!("{}", quote::ToTokens::to_token_stream(&stmt)).chars().collect::<Vec<_>>();

let short = if short.len() > 40 {
let mut short = short[..37].into_iter().collect::<String>();
Expand All @@ -71,17 +90,15 @@ fn rewrite_stmts(name: String, stmts: &[Stmt]) -> Vec<Stmt> {
short.into_iter().collect::<String>()
};

new_stmts.push(parse_quote!(funtime_timer.print_elapsed(#short);));
}

new_stmts
}
let next_stmt = parse_quote!(funtime_timer.mark_elapsed(#short););

new_stmts.push(stmt);
new_stmts.push(next_stmt);
}

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
if let Some(stmt) = last {
new_stmts.push(stmt);
}

new_stmts
}

0 comments on commit 0ac06a1

Please sign in to comment.