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
use crate::util::analysis::RtAnalysis;
use crate::util::statistics::counter::EventCounter;
use crate::util::statistics::stats::Stats;
use crate::vm::VMBinding;

use std::collections::HashMap;
use std::sync::{Arc, Mutex};

/**
 * This file implements an analysis routine that counts the number of objects allocated
 * in each size class. Here, a size class 'sizeX' is defined as 'X bytes or lower'. For
 * example, size64 is the size class with objects <= 64 bytes but > 32 bytes which is
 * the previous size class.
 *
 * We keep track of the size classes using a HashMap with the key being the name of the
 * size class.
 */
pub struct PerSizeClassObjectCounter {
    running: bool,
    size_classes: Mutex<HashMap<String, Arc<Mutex<EventCounter>>>>,
    stats: Arc<Stats>,
}

// Macro to simplify the creation of a new counter for a particular size class.
// This is a macro as opposed to a function as otherwise we would have to unlock
// and relock the size_classes map
macro_rules! new_ctr {
    ( $stats:expr, $map:expr, $size_class:expr ) => {{
        let ctr = $stats.new_event_counter(&$size_class, true, true);
        $map.insert($size_class.to_string(), ctr.clone());
        ctr
    }};
}

impl PerSizeClassObjectCounter {
    pub fn new(running: bool, stats: Arc<Stats>) -> Self {
        Self {
            running,
            size_classes: Mutex::new(HashMap::new()),
            stats,
        }
    }

    // Fastest way to compute the smallest power of 2 that is larger than n
    // See: https://stackoverflow.com/questions/3272424/compute-fast-log-base-2-ceiling/51351885#51351885
    fn size_class(&self, size: usize) -> usize {
        2_usize.pow(63_u32 - (size - 1).leading_zeros() + 1)
    }
}

impl<VM: VMBinding> RtAnalysis<VM> for PerSizeClassObjectCounter {
    fn alloc_hook(&mut self, size: usize, _align: usize, _offset: usize) {
        if !self.running {
            return;
        }

        let size_class = format!("size{}", self.size_class(size));
        let mut size_classes = self.size_classes.lock().unwrap();
        let c = size_classes.get_mut(&size_class);
        match c {
            None => {
                // Create (and increment) the counter associated with the size class if it doesn't exist
                let ctr = new_ctr!(self.stats, size_classes, size_class);
                ctr.lock().unwrap().inc();
            }
            Some(ctr) => {
                // Increment counter associated with the size class
                ctr.lock().unwrap().inc();
            }
        }
    }

    fn set_running(&mut self, running: bool) {
        self.running = running;
    }
}