ghostscope_protocol/
trace_context.rs

1//! Trace context for managing strings, types, and variable names
2//!
3//! TraceContext is the upgraded version of StringTable that provides unified
4//! management of all context information needed during tracing execution.
5
6use crate::type_info::TypeInfo;
7use serde::{Deserialize, Serialize};
8
9/// Unified context for trace execution containing strings, types, and variable names
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TraceContext {
12    /// Format strings and constant strings
13    pub strings: Vec<String>,
14
15    /// Complete DWARF type information for perfect formatting
16    pub types: Vec<TypeInfo>,
17
18    /// Variable names for debugging and display
19    pub variable_names: Vec<String>,
20}
21
22impl TraceContext {
23    /// Create a new empty trace context
24    pub fn new() -> Self {
25        Self {
26            strings: Vec::new(),
27            types: Vec::new(),
28            variable_names: Vec::new(),
29        }
30    }
31
32    /// Add a string to the context and return its index
33    pub fn add_string(&mut self, s: String) -> u16 {
34        // Check if string already exists to avoid duplicates
35        if let Some(index) = self.strings.iter().position(|existing| existing == &s) {
36            return index as u16;
37        }
38
39        let index = self.strings.len();
40        if index > u16::MAX as usize {
41            panic!("String table overflow: too many strings");
42        }
43
44        self.strings.push(s);
45        index as u16
46    }
47
48    /// Add a type to the context and return its index
49    pub fn add_type(&mut self, type_info: TypeInfo) -> u16 {
50        // For now, don't deduplicate types as they can be complex to compare
51        // TODO: Implement type deduplication for performance optimization
52        let index = self.types.len();
53        if index > u16::MAX as usize {
54            panic!("Type table overflow: too many types");
55        }
56
57        // Debug: Log the type being added
58        tracing::debug!("Adding type at index {}: {:#?}", index, type_info);
59
60        self.types.push(type_info);
61        index as u16
62    }
63
64    /// Add a variable name to the context and return its index
65    pub fn add_variable_name(&mut self, name: String) -> u16 {
66        // Check if variable name already exists to avoid duplicates
67        if let Some(index) = self
68            .variable_names
69            .iter()
70            .position(|existing| existing == &name)
71        {
72            return index as u16;
73        }
74
75        let index = self.variable_names.len();
76        if index > u16::MAX as usize {
77            panic!("Variable name table overflow: too many variable names");
78        }
79
80        self.variable_names.push(name);
81        index as u16
82    }
83
84    /// Get a string by index
85    pub fn get_string(&self, index: u16) -> Option<&str> {
86        self.strings.get(index as usize).map(|s| s.as_str())
87    }
88
89    /// Get a type by index
90    pub fn get_type(&self, index: u16) -> Option<&TypeInfo> {
91        self.types.get(index as usize)
92    }
93
94    /// Get a variable name by index
95    pub fn get_variable_name(&self, index: u16) -> Option<&str> {
96        self.variable_names.get(index as usize).map(|s| s.as_str())
97    }
98
99    /// Get the number of strings in the context
100    pub fn string_count(&self) -> usize {
101        self.strings.len()
102    }
103
104    /// Get the number of types in the context
105    pub fn type_count(&self) -> usize {
106        self.types.len()
107    }
108
109    /// Get the number of variable names in the context
110    pub fn variable_name_count(&self) -> usize {
111        self.variable_names.len()
112    }
113
114    /// Clear all tables (useful for testing)
115    pub fn clear(&mut self) {
116        self.strings.clear();
117        self.types.clear();
118        self.variable_names.clear();
119    }
120
121    /// Get total memory usage estimate in bytes
122    pub fn estimated_memory_usage(&self) -> usize {
123        let strings_size: usize = self.strings.iter().map(|s| s.len()).sum();
124        let variable_names_size: usize = self.variable_names.iter().map(|s| s.len()).sum();
125
126        // Rough estimate: each TypeInfo is approximately 100 bytes on average
127        // (this is a rough estimate since TypeInfo can vary significantly in size)
128        let types_size = self.types.len() * 100;
129
130        strings_size + variable_names_size + types_size
131    }
132}
133
134impl Default for TraceContext {
135    fn default() -> Self {
136        Self::new()
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use crate::type_info::{StructMember, TypeInfo};
144
145    #[test]
146    fn test_trace_context_strings() {
147        let mut ctx = TraceContext::new();
148
149        let idx1 = ctx.add_string("Hello".to_string());
150        let idx2 = ctx.add_string("World".to_string());
151        let idx3 = ctx.add_string("Hello".to_string()); // Duplicate
152
153        assert_eq!(idx1, 0);
154        assert_eq!(idx2, 1);
155        assert_eq!(idx3, 0); // Should return existing index
156
157        assert_eq!(ctx.get_string(idx1), Some("Hello"));
158        assert_eq!(ctx.get_string(idx2), Some("World"));
159        assert_eq!(ctx.string_count(), 2);
160    }
161
162    #[test]
163    fn test_trace_context_variable_names() {
164        let mut ctx = TraceContext::new();
165
166        let idx1 = ctx.add_variable_name("person".to_string());
167        let idx2 = ctx.add_variable_name("name".to_string());
168        let idx3 = ctx.add_variable_name("person".to_string()); // Duplicate
169
170        assert_eq!(idx1, 0);
171        assert_eq!(idx2, 1);
172        assert_eq!(idx3, 0); // Should return existing index
173
174        assert_eq!(ctx.get_variable_name(idx1), Some("person"));
175        assert_eq!(ctx.get_variable_name(idx2), Some("name"));
176        assert_eq!(ctx.variable_name_count(), 2);
177    }
178
179    #[test]
180    fn test_trace_context_types() {
181        let mut ctx = TraceContext::new();
182
183        let type1 = TypeInfo::BaseType {
184            name: "int".to_string(),
185            size: 4,
186            encoding: gimli::constants::DW_ATE_signed.0 as u16,
187        };
188
189        let type2 = TypeInfo::StructType {
190            name: "Person".to_string(),
191            size: 32,
192            members: vec![StructMember {
193                name: "name".to_string(),
194                member_type: TypeInfo::BaseType {
195                    name: "char".to_string(),
196                    size: 1,
197                    encoding: gimli::constants::DW_ATE_signed_char.0 as u16,
198                },
199                offset: 0,
200                bit_offset: None,
201                bit_size: None,
202            }],
203        };
204
205        let idx1 = ctx.add_type(type1.clone());
206        let idx2 = ctx.add_type(type2.clone());
207
208        assert_eq!(idx1, 0);
209        assert_eq!(idx2, 1);
210
211        assert_eq!(ctx.get_type(idx1), Some(&type1));
212        assert_eq!(ctx.get_type(idx2), Some(&type2));
213        assert_eq!(ctx.type_count(), 2);
214    }
215
216    #[test]
217    fn test_trace_context_combined() {
218        let mut ctx = TraceContext::new();
219
220        let str_idx = ctx.add_string("format: {} = {}".to_string());
221        let var_idx = ctx.add_variable_name("person.name".to_string());
222        let type_idx = ctx.add_type(TypeInfo::BaseType {
223            name: "char[32]".to_string(),
224            size: 32,
225            encoding: gimli::constants::DW_ATE_signed_char.0 as u16,
226        });
227
228        assert_eq!(str_idx, 0);
229        assert_eq!(var_idx, 0);
230        assert_eq!(type_idx, 0);
231
232        // Each table is independent
233        assert_eq!(ctx.string_count(), 1);
234        assert_eq!(ctx.variable_name_count(), 1);
235        assert_eq!(ctx.type_count(), 1);
236    }
237
238    #[test]
239    fn test_trace_context_memory_usage() {
240        let mut ctx = TraceContext::new();
241
242        ctx.add_string("Hello World".to_string()); // 11 bytes
243        ctx.add_variable_name("variable".to_string()); // 8 bytes
244        ctx.add_type(TypeInfo::BaseType {
245            name: "int".to_string(),
246            size: 4,
247            encoding: gimli::constants::DW_ATE_signed.0 as u16,
248        }); // ~100 bytes estimate
249
250        let usage = ctx.estimated_memory_usage();
251        assert!(usage >= 119); // At least the string sizes + type estimate
252    }
253}