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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//! Collections of events.
//!
//! We use a wrapper type to provide convenience methods for diagnostics.

use std::{ops, slice};

use crate::{Event, Machine, Violation};

/// Collections of events.
///
/// We use a wrapper type to provide convenience methods for diagnostics.
#[derive(Debug, Clone)]
pub struct Events {
    data: Vec<Event>,
}

impl Events {
    /// Construct a new collection of allocations.
    pub const fn new() -> Self {
        Self { data: Vec::new() }
    }

    /// Get the number of events in this collection.
    pub fn len(&self) -> usize {
        self.data.len()
    }

    /// Test if collection is empty.
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    /// Access the capacity of the Events container.
    pub fn capacity(&self) -> usize {
        self.data.capacity()
    }

    /// Reserve extra capacity for the underlying storage.
    pub fn reserve(&mut self, cap: usize) {
        self.data.reserve(cap.saturating_sub(self.data.capacity()));
    }

    /// Fetch all allocations as a slice.
    pub fn as_slice(&self) -> &[Event] {
        ops::Deref::deref(self)
    }

    /// Fetch all allocations as a slice.
    pub fn as_slice_mut(&mut self) -> &mut [Event] {
        ops::DerefMut::deref_mut(self)
    }

    /// Clear the collection of events.
    pub fn clear(&mut self) {
        self.data.clear();
    }

    /// Push a single event into the collection of events.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use checkers::{Events, Event, Region};
    /// let mut events = Events::new();
    /// let event = Event::Alloc(Region::new(10.into(), 10, 1));
    /// events.push(event);
    /// assert_eq!(event, events[0]);
    /// ```
    pub fn push(&mut self, event: Event) {
        // Note: pushing into an at-capacity collection would allocate, so we
        // take care of it here, while muting the tracker.
        if self.data.capacity() == self.data.len() {
            let _g = crate::mute_guard(true);
            self.data.reserve(1);
        }

        self.data.push(event);
    }

    /// Count the number of allocations in this collection of events.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use checkers::{Events, Event, Region};
    /// let mut events = Events::new();
    /// events.push(Event::Alloc(Region::new(10.into(), 10, 1)));
    /// assert_eq!(1, events.allocs());
    /// assert_eq!(0, events.frees());
    /// ```
    pub fn allocs(&self) -> usize {
        self.data
            .iter()
            .map(|e| match e {
                Event::Alloc { .. } | Event::AllocZeroed { .. } => 1,
                _ => 0,
            })
            .sum()
    }

    /// Count the number of allocations in this collection of events.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use checkers::{Events, Event, Region, Realloc};
    /// let mut events = Events::new();
    ///
    /// events.push(Event::Realloc(Realloc::new(
    ///     Some(true),
    ///     Region::new(10.into(), 10, 1),
    ///     Region::new(20.into(), 10, 1)
    /// )));
    ///
    /// assert_eq!(1, events.reallocs());
    /// assert_eq!(0, events.allocs());
    /// assert_eq!(0, events.frees());
    /// ```
    pub fn reallocs(&self) -> usize {
        self.data
            .iter()
            .map(|e| match e {
                Event::Realloc { .. } => 1,
                _ => 0,
            })
            .sum()
    }

    /// Count the number of frees in this collection of events.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use checkers::{Events, Event, Region};
    /// let mut events = Events::new();
    /// events.push(Event::Free(Region::new(10.into(), 10, 1)));
    /// assert_eq!(0, events.allocs());
    /// assert_eq!(1, events.frees());
    /// ```
    pub fn frees(&self) -> usize {
        self.data
            .iter()
            .map(|e| match e {
                Event::Free { .. } => 1,
                _ => 0,
            })
            .sum()
    }

    /// Validate the current state and populate the errors collection with any
    /// violations found.
    ///
    /// See [Machine::push] for more details on the kind of validation errors
    /// that can be raised.
    pub fn validate(&self, errors: &mut Vec<Violation>) {
        let mut machine = Machine::default();

        for event in self.as_slice() {
            if let Err(e) = machine.push(*event) {
                errors.push(e);
            }
        }

        for region in machine.trailing_regions() {
            errors.push(Violation::Leaked { region });
        }
    }

    /// Max amount of memory used according to this event history.
    ///
    /// Returns the first violation encountered if the history is not sound.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use checkers::{Events, Event, Region};
    /// let mut events = Events::new();
    /// events.push(Event::Alloc(Region::new(0x10.into(), 0x10, 1)));
    /// events.push(Event::Alloc(Region::new(0x20.into(), 0x10, 1)));
    /// events.push(Event::Free(Region::new(0x10.into(), 0x10, 1)));
    /// assert_eq!(Ok(0x20), events.max_memory_used());
    /// ```
    pub fn max_memory_used(&self) -> Result<usize, Violation> {
        let mut machine = Machine::default();

        let mut max = 0usize;

        for event in self.as_slice() {
            machine.push(*event)?;
            max = usize::max(machine.memory_used, max);
        }

        Ok(max)
    }
}

impl ops::Deref for Events {
    type Target = [Event];

    fn deref(&self) -> &[Event] {
        ops::Deref::deref(&self.data)
    }
}

impl ops::DerefMut for Events {
    fn deref_mut(&mut self) -> &mut [Event] {
        ops::DerefMut::deref_mut(&mut self.data)
    }
}

impl<I: slice::SliceIndex<[Event]>> ops::Index<I> for Events {
    type Output = I::Output;

    #[inline]
    fn index(&self, index: I) -> &Self::Output {
        std::ops::Index::index(&self.data, index)
    }
}