memscope_rs/analyzer/
classify.rs1use crate::view::MemoryView;
4use std::collections::HashMap;
5
6pub struct ClassificationAnalysis {
10 view: MemoryView,
11}
12
13impl ClassificationAnalysis {
14 pub fn from_view(view: &MemoryView) -> Self {
16 Self { view: view.clone() }
17 }
18
19 pub fn by_type(&self) -> HashMap<String, TypeClassification> {
21 let mut types: HashMap<String, TypeClassification> = HashMap::new();
22
23 for alloc in self.view.allocations() {
24 let type_name = alloc
25 .type_name
26 .clone()
27 .unwrap_or_else(|| "unknown".to_string());
28
29 let entry = types.entry(type_name).or_default();
30 entry.count += 1;
31 entry.total_bytes += alloc.size;
32 entry.category = classify_type(&alloc.type_name.clone().unwrap_or_default());
33 }
34
35 types
36 }
37
38 pub fn summary(&self) -> ClassificationSummary {
40 let types = self.by_type();
41 let mut categories: HashMap<TypeCategory, usize> = HashMap::new();
42
43 for classification in types.values() {
44 *categories.entry(classification.category).or_default() += classification.count;
45 }
46
47 ClassificationSummary {
48 total_types: types.len(),
49 categories,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Default)]
56pub struct TypeClassification {
57 pub count: usize,
59 pub total_bytes: usize,
61 pub category: TypeCategory,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
67pub enum TypeCategory {
68 #[default]
70 Unknown,
71 Collection,
73 String,
75 SmartPointer,
77 Primitive,
79 Custom,
81}
82
83#[derive(Debug, Clone)]
85pub struct ClassificationSummary {
86 pub total_types: usize,
88 pub categories: HashMap<TypeCategory, usize>,
90}
91
92fn classify_type(type_name: &str) -> TypeCategory {
94 if type_name.contains("Vec<")
95 || type_name.contains("HashMap<")
96 || type_name.contains("HashSet<")
97 || type_name.contains("BTreeMap<")
98 || type_name.contains("BTreeSet<")
99 || type_name.contains("LinkedList<")
100 || type_name.contains("VecDeque<")
101 {
102 TypeCategory::Collection
103 } else if type_name.contains("String") || type_name.contains("str") {
104 TypeCategory::String
105 } else if type_name.contains("Arc<")
106 || type_name.contains("Rc<")
107 || type_name.contains("Box<")
108 || type_name.contains("Weak<")
109 {
110 TypeCategory::SmartPointer
111 } else if type_name.contains("i32")
112 || type_name.contains("u32")
113 || type_name.contains("i64")
114 || type_name.contains("u64")
115 || type_name.contains("f32")
116 || type_name.contains("f64")
117 || type_name.contains("bool")
118 || type_name.contains("char")
119 {
120 TypeCategory::Primitive
121 } else if !type_name.is_empty() && type_name != "unknown" {
122 TypeCategory::Custom
123 } else {
124 TypeCategory::Unknown
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::event_store::MemoryEvent;
132
133 #[test]
134 fn test_classification() {
135 let events = vec![
136 MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
137 MemoryEvent::allocate(0x2000, 32, 1).with_type_name("String".to_string()),
138 ];
139 let view = MemoryView::from_events(events);
140 let analysis = ClassificationAnalysis::from_view(&view);
141 let types = analysis.by_type();
142 assert_eq!(types.len(), 2);
143 }
144
145 #[test]
146 fn test_type_category() {
147 assert_eq!(classify_type("Vec<i32>"), TypeCategory::Collection);
148 assert_eq!(classify_type("String"), TypeCategory::String);
149 assert_eq!(classify_type("Arc<Mutex<T>>"), TypeCategory::SmartPointer);
150 }
151}