1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
extern crate time;
#[macro_use] extern crate lazy_static;

mod profiler_frame;
mod profiler_state;

use std::sync::Mutex;
use time::precise_time_s;
use std::ops::Drop;

pub use profiler_frame::*;
pub use profiler_state::*;

lazy_static! {
    pub static ref PROFILER_STATE: Mutex<ProfilerState> = Mutex::new(ProfilerState::new());
}

#[derive(Debug)]
pub struct ProfilerReport {
    pub name        : String,
    pub time        : f64,
    pub percent     : f64,
    pub sub_reports : Vec<ProfilerReport>
}

pub fn profiler_report() -> ProfilerReport {
    state_report(&mut *PROFILER_STATE.lock().unwrap())
}
pub fn profiler_next_frame() {
    PROFILER_STATE.lock().unwrap().next_frame();
}

pub struct ProfilerRegion {
    pub enter_time : f64,
    pub name       : String
}
impl ProfilerRegion {
    pub fn new(name : &str) -> ProfilerRegion {
        PROFILER_STATE.lock().unwrap().begin_track_time(name.to_string());
        ProfilerRegion {
            enter_time : precise_time_s(),
            name       : name.to_string()
        }
    }
}
impl Drop for ProfilerRegion {
    fn drop(&mut self) {
        PROFILER_STATE.lock().unwrap().end_track_time(precise_time_s() - self.enter_time);
    }
}


#[macro_export]
macro_rules! profile_region {
    ($name:expr) => {
        let _profile = $crate::ProfilerRegion::new($name);
    }
}

#[macro_export]
macro_rules! print_region_time {
    ($x:expr, $pattern:expr) => {{
        use time::precise_time_s;

        let start = precise_time_s();
        let res = $x;
        let end = precise_time_s();
        println!($pattern, end - start);
        res
    }};
}

pub fn state_report(state : &mut ProfilerState) -> ProfilerReport {
    let frame = state.previous_frame();
    report(&frame.counters, frame.end_time - frame.begin_time)
}

fn report(counter : &ProfilerCounter, time : f64) -> ProfilerReport {
    ProfilerReport {
        name        : counter.name.clone(),
        time        : counter.time,
        percent     : counter.time / time * 100.0,
        sub_reports : counter.counters.iter().map(|x| report(x, counter.time)).collect()
    }
}

#[test]
fn test_print_region_time() {
    let _ : i32 = print_region_time!({
        (1 ..).take(1000).fold(0, |x, y| x + y)
    }, "Something time tooked: {}");
}

#[test]
fn test_track_regions() {
    {
        profile_region!("Main region");

        (1 ..).take(500000).fold(0, |x, y| ((x * y) as f32).sqrt().sin() as i32 + 1);

        {
            profile_region!("subregion 1");
            (1 ..).take(500000).fold(0, |x, y| ((x * y) as f32).sqrt().sin() as i32 + 1);
        }

        {
            profile_region!("subregion 2");
            (1 ..).take(500000).fold(0, |x, y| ((x * y) as f32).sqrt().sin() as i32 + 1);

            {
                profile_region!("subsubregion 1");
                (1 ..).take(500000).fold(0, |x, y| ((x * y) as f32).sqrt().sin() as i32 + 1);
            }

            {
                profile_region!("subsubregion 2");
                (1 ..).take(500000).fold(0, |x, y| ((x * y) as f32).sqrt().sin() as i32 + 1);
            }

        }

        {
            profile_region!("subregion 3");
            (1 ..).take(500000).fold(0, |x, y| ((x * y) as f32).sqrt().sin() as i32 + 1);
        }
    }

    profiler_next_frame();

    println!("{:?}", profiler_report());
}