1mod batch_processor;
10mod binary_html_export;
11mod binary_html_writer;
12mod binary_template_engine;
13mod cache;
14mod complex_type_analyzer;
15mod config;
16mod error;
17mod error_recovery;
18mod ffi_safety_analyzer;
19mod field_parser;
20mod filter_engine;
21pub mod format;
22pub mod html_converter;
23pub mod html_export;
24mod index;
25mod index_builder;
26#[cfg(test)]
27mod integration_test_complex_types;
28#[cfg(test)]
29mod integration_test_ffi_safety;
30#[cfg(test)]
31mod integration_test_template_resources;
32#[cfg(test)]
33mod integration_test_variable_relationships;
34mod memory_layout_serialization;
35mod parser;
36pub mod reader;
37mod selective_json_exporter;
38mod selective_reader;
39mod serializable;
40mod smart_pointer_serialization;
41mod streaming_field_processor;
42mod streaming_json_writer;
43mod string_table;
44mod template_resource_manager;
45mod variable_relationship_analyzer;
46pub mod writer;
47
48pub use batch_processor::{
49 BatchProcessor, BatchProcessorBuilder, BatchProcessorConfig, BatchProcessorStats, RecordBatch,
50};
51pub use binary_html_export::{
52 get_recommended_config, parse_binary_to_html_auto, parse_binary_to_html_direct,
53 parse_binary_to_html_with_config, BinaryHtmlExportConfig, BinaryHtmlExportStats,
54 ProcessingStrategy,
55};
56pub use binary_html_writer::{
57 BinaryAllocationData, BinaryHtmlStats, BinaryHtmlWriter, BinaryHtmlWriterConfig,
58 BinaryTemplateData,
59};
60pub use binary_template_engine::{
61 BinaryTemplateEngine, BinaryTemplateEngineConfig, BinaryTemplateEngineStats,
62};
63pub use cache::{CacheEntry, CacheStats, IndexCache, IndexCacheConfig};
64pub use complex_type_analyzer::{
65 CategorizedTypes, ComplexTypeAnalysis, ComplexTypeAnalyzer, ComplexTypeSummary,
66 GenericInstantiation, GenericTypeAnalysis, TypeCategory, TypeInfo,
67};
68pub use ffi_safety_analyzer::{
69 FfiCallGraph, FfiHotspot, FfiSafetyAnalysis, FfiSafetyAnalyzer, FfiSafetySummary,
70 RiskAssessment, RiskLevel, UnsafeOperation, UnsafeOperationType,
71};
72pub use html_converter::*;
73pub use variable_relationship_analyzer::{
74 GraphEdge, GraphNode, NodeCategory, OwnershipStatus, RelationshipGraph, RelationshipType,
75 VariableRelationshipAnalysis, VariableRelationshipAnalyzer,
76};
77
78pub use config::{
79 AdvancedMetricsLevel, AnalysisType, BinaryExportConfig, BinaryExportConfigBuilder,
80 DashboardExportStats, DashboardFormat, DashboardOptions, DataScope, PerformanceMode,
81};
82pub use error::BinaryExportError;
83pub use error_recovery::{
84 ErrorRecoveryManager, ErrorReport, ErrorStatistics, ErrorTrend, RecoveryConfig, RecoveryResult,
85 RecoveryStrategy,
86};
87pub use field_parser::{FieldParser, FieldParserConfig, FieldParserStats, PartialAllocationInfo};
88pub use filter_engine::{FilterEngine, FilterEngineBuilder, FilterOptimizer, FilterStats};
89pub use format::{BinaryExportMode, FileHeader, FORMAT_VERSION, MAGIC_BYTES};
90pub use html_export::{
91 export_binary,
92 export_binary_optimized,
93 export_binary_to_both,
94 export_binary_to_dashboard, export_binary_to_html,
96 export_binary_to_html_both,
97 export_binary_to_html_system,
98 export_binary_to_json,
99 export_binary_with_config,
100 export_binary_with_format,
101 show_export_options,
102 BinaryOutputFormat,
103};
104pub use index::{BinaryIndex, CompactAllocationIndex, QuickFilterData, RecordMetadata};
105pub use index_builder::BinaryIndexBuilder;
106pub use parser::BinaryParser;
107pub use reader::BinaryReader;
108pub use selective_json_exporter::{
109 OptimizationLevel, SelectiveJsonExportConfig, SelectiveJsonExportConfigBuilder,
110 SelectiveJsonExportStats, SelectiveJsonExporter,
111};
112pub use selective_reader::{
113 AllocationField, AllocationFilter, SelectiveReadOptions, SelectiveReadOptionsBuilder,
114 SortField, SortOrder,
115};
116pub use streaming_field_processor::{
117 OptimizedRecord, StreamingFieldProcessor, StreamingFieldProcessorConfig,
118 StreamingFieldProcessorConfigBuilder, StreamingFieldProcessorStats,
119};
120pub use streaming_json_writer::{
121 SelectiveSerializationOptions, StreamingJsonStats, StreamingJsonWriter,
122 StreamingJsonWriterConfig, StreamingJsonWriterConfigBuilder,
123};
124pub use template_resource_manager::{
125 create_template_data, PlaceholderProcessor, ResourceConfig, TemplateData,
126 TemplateResourceManager,
127};
128pub use writer::BinaryWriter;
129
130use crate::core::types::AllocationInfo;
133use std::path::Path;
134
135pub fn export_to_binary<P: AsRef<Path>>(
137 allocations: &[AllocationInfo],
138 path: P,
139) -> Result<(), BinaryExportError> {
140 export_to_binary_with_config(allocations, path, &BinaryExportConfig::default())
141}
142
143pub fn export_to_binary_with_mode<P: AsRef<Path>>(
158 allocations: &[AllocationInfo],
159 path: P,
160 export_mode: BinaryExportMode,
161 config: &BinaryExportConfig,
162) -> Result<(), BinaryExportError> {
163 let mut writer = BinaryWriter::new_with_config(path, config)?;
164
165 let filtered_allocations: Vec<&AllocationInfo> = match export_mode {
167 BinaryExportMode::UserOnly => {
168 allocations
170 .iter()
171 .filter(|a| a.var_name.is_some())
172 .collect()
173 }
174 BinaryExportMode::Full => {
175 allocations.iter().collect()
177 }
178 };
179
180 writer.build_string_table(allocations)?;
184
185 let user_count = filtered_allocations
187 .iter()
188 .filter(|a| a.var_name.is_some())
189 .count() as u16;
190 let system_count = (filtered_allocations.len() - user_count as usize) as u16;
191 let total_count = filtered_allocations.len() as u32;
192
193 debug_assert_eq!(
195 user_count as usize + system_count as usize,
196 total_count as usize,
197 "Count validation failed: user({user_count}) + system({system_count}) != total({total_count})"
198 );
199
200 tracing::info!(
201 "Binary export starting: {total_count} allocations ({user_count} user, {system_count} system) in {export_mode:?} mode"
202 );
203 tracing::info!("Filtered from {} original allocations", allocations.len());
204
205 writer.write_enhanced_header(total_count, export_mode, user_count, system_count)?;
207
208 for allocation in filtered_allocations {
210 writer.write_allocation(allocation)?;
211 }
212
213 writer.finish()?;
214
215 tracing::info!("Binary export completed: {total_count} allocations written");
216
217 Ok(())
218}
219
220pub fn export_to_binary_with_config<P: AsRef<Path>>(
222 allocations: &[AllocationInfo],
223 path: P,
224 config: &BinaryExportConfig,
225) -> Result<(), BinaryExportError> {
226 let mut writer = BinaryWriter::new_with_config(path, config)?;
227
228 writer.build_string_table(allocations)?;
230
231 writer.write_header(allocations.len() as u32)?;
232
233 for allocation in allocations {
235 writer.write_allocation(allocation)?;
236 }
237
238 writer.write_advanced_metrics_segment(allocations)?;
240
241 writer.finish()?;
242
243 if config.has_advanced_metrics() {
245 tracing::info!(
246 "Binary export completed with advanced metrics (impact: {:.1}%)",
247 config.estimated_performance_impact() * 100.0
248 );
249 }
250
251 Ok(())
252}
253
254pub fn parse_binary_to_json<P: AsRef<Path>>(
256 binary_path: P,
257 json_path: P,
258) -> Result<(), BinaryExportError> {
259 BinaryParser::to_json(binary_path, json_path)
260}
261
262pub fn parse_binary_to_html<P: AsRef<Path>>(
264 binary_path: P,
265 html_path: P,
266) -> Result<(), BinaryExportError> {
267 BinaryParser::to_html(binary_path, html_path)
268}
269
270#[derive(Debug, Clone, PartialEq)]
272pub struct BinaryFileInfo {
273 pub export_mode: BinaryExportMode,
275 pub total_count: u32,
277 pub user_count: u16,
279 pub system_count: u16,
281 pub version: u32,
283 pub is_count_consistent: bool,
285 pub file_size: u64,
287}
288
289impl BinaryFileInfo {
290 pub fn is_user_only(&self) -> bool {
292 self.export_mode == BinaryExportMode::UserOnly
293 }
294
295 pub fn is_full_binary(&self) -> bool {
297 self.export_mode == BinaryExportMode::Full
298 }
299
300 pub fn type_description(&self) -> String {
302 match self.export_mode {
303 BinaryExportMode::UserOnly => format!(
304 "User-only binary ({} user allocations, {} KB)",
305 self.user_count,
306 self.file_size / 1024
307 ),
308 BinaryExportMode::Full => format!(
309 "Full binary ({} total allocations: {} user + {} system, {} KB)",
310 self.total_count,
311 self.user_count,
312 self.system_count,
313 self.file_size / 1024
314 ),
315 }
316 }
317
318 pub fn recommended_strategy(&self) -> &'static str {
320 match self.export_mode {
321 BinaryExportMode::UserOnly => "Simple processing (small file, user data only)",
322 BinaryExportMode::Full => "Optimized processing (large file, comprehensive data)",
323 }
324 }
325}
326
327pub fn detect_binary_type<P: AsRef<Path>>(path: P) -> Result<BinaryFileInfo, BinaryExportError> {
362 use std::fs::File;
363 use std::io::Read;
364
365 let path = path.as_ref();
366
367 let metadata = std::fs::metadata(path).map_err(BinaryExportError::Io)?;
369 let file_size = metadata.len();
370
371 let mut file = File::open(path).map_err(BinaryExportError::Io)?;
373
374 let mut header_bytes = [0u8; format::HEADER_SIZE];
375 file.read_exact(&mut header_bytes)
376 .map_err(BinaryExportError::Io)?;
377
378 let header = FileHeader::from_bytes(&header_bytes);
380
381 if !header.is_valid_magic() {
383 return Err(BinaryExportError::InvalidFormat);
384 }
385
386 if !header.is_compatible_version() {
388 return Err(BinaryExportError::CorruptedData(format!(
389 "Unsupported format version: {}",
390 header.version
391 )));
392 }
393
394 let export_mode = header.get_export_mode();
396 let (total_count, user_count, system_count) = header.get_allocation_counts();
397 let is_count_consistent = header.is_count_consistent();
398
399 Ok(BinaryFileInfo {
400 export_mode,
401 total_count,
402 user_count,
403 system_count,
404 version: header.version,
405 is_count_consistent,
406 file_size,
407 })
408}
409
410pub fn export_binary_to_html_dashboard<P: AsRef<Path>>(
445 binary_path: P,
446 _output_path: P,
447 project_name: &str,
448) -> Result<(), BinaryExportError> {
449 export_binary_to_html(binary_path, project_name)
451}
452
453pub fn parse_binary_auto<P: AsRef<Path>>(
464 binary_path: P,
465 base_name: &str,
466) -> Result<(), BinaryExportError> {
467 let binary_path = binary_path.as_ref();
468
469 let info = detect_binary_type(binary_path)?;
471
472 tracing::info!(
473 "Auto-detected binary type: {} (version {})",
474 info.type_description(),
475 info.version
476 );
477
478 tracing::info!("Using strategy: {}", info.recommended_strategy());
479
480 match info.export_mode {
482 BinaryExportMode::UserOnly => {
483 tracing::debug!("Using simple parsing for user-only binary");
484 BinaryParser::parse_user_binary_to_json(binary_path, base_name)
485 }
486 BinaryExportMode::Full => {
487 tracing::debug!("Using optimized parsing for full binary");
488 BinaryParser::parse_full_binary_to_json(binary_path, base_name)
489 }
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use crate::core::types::AllocationInfo;
497 use std::fs;
498 use tempfile::TempDir;
499
500 fn create_test_allocations() -> Vec<AllocationInfo> {
502 let mut alloc1 = AllocationInfo::new(0x1000, 64);
503 alloc1.var_name = Some("user_var1".to_string());
504 alloc1.type_name = Some("String".to_string());
505 alloc1.timestamp_alloc = 1000;
506 alloc1.stack_trace = Some(vec!["main".to_string(), "allocate".to_string()]);
507 alloc1.thread_id = "1".to_string();
508
509 let mut alloc2 = AllocationInfo::new(0x2000, 128);
510 alloc2.var_name = Some("user_var2".to_string());
511 alloc2.type_name = Some("Vec<i32>".to_string());
512 alloc2.timestamp_alloc = 2000;
513 alloc2.stack_trace = Some(vec!["main".to_string(), "create_vec".to_string()]);
514 alloc2.thread_id = "1".to_string();
515
516 let mut alloc3 = AllocationInfo::new(0x3000, 32);
517 alloc3.var_name = None; alloc3.type_name = None;
519 alloc3.timestamp_alloc = 3000;
520 alloc3.stack_trace = Some(vec!["system".to_string()]);
521 alloc3.thread_id = "2".to_string();
522
523 vec![alloc1, alloc2, alloc3]
524 }
525
526 #[test]
527 fn test_export_to_binary_default() {
528 let temp_dir = TempDir::new().unwrap();
529 let binary_path = temp_dir.path().join("test.memscope");
530 let allocations = create_test_allocations();
531
532 let result = export_to_binary(&allocations, &binary_path);
533 assert!(result.is_ok());
534 assert!(binary_path.exists());
535 assert!(binary_path.metadata().unwrap().len() > 0);
536 }
537
538 #[test]
539 fn test_export_to_binary_with_config() {
540 let temp_dir = TempDir::new().unwrap();
541 let binary_path = temp_dir.path().join("test_config.memscope");
542 let allocations = create_test_allocations();
543
544 let config = BinaryExportConfigBuilder::new()
545 .advanced_metrics_level(AdvancedMetricsLevel::Essential)
546 .build();
547
548 let result = export_to_binary_with_config(&allocations, &binary_path, &config);
549 assert!(result.is_ok());
550 assert!(binary_path.exists());
551 assert!(binary_path.metadata().unwrap().len() > 0);
552 }
553
554 #[test]
555 fn test_export_to_binary_with_mode_user_only() {
556 let temp_dir = TempDir::new().unwrap();
557 let binary_path = temp_dir.path().join("test_user_only.memscope");
558 let allocations = create_test_allocations();
559
560 let config = BinaryExportConfig::minimal();
561
562 let result = export_to_binary_with_mode(
563 &allocations,
564 &binary_path,
565 BinaryExportMode::UserOnly,
566 &config,
567 );
568 assert!(result.is_ok());
569 assert!(binary_path.exists());
570
571 let info = detect_binary_type(&binary_path).unwrap();
573 assert!(info.is_user_only());
574
575 let expected_user_count = allocations.iter().filter(|a| a.var_name.is_some()).count();
577
578 assert_eq!(info.export_mode, BinaryExportMode::UserOnly);
580
581 assert_eq!(info.user_count, expected_user_count as u16);
583 assert_eq!(info.system_count, 0); assert_eq!(info.total_count, expected_user_count as u32);
585
586 let json_path = temp_dir.path().join("test_user_only.json");
588 let parse_result = parse_binary_to_json(&binary_path, &json_path);
589 assert!(parse_result.is_ok());
590 assert!(json_path.exists());
591
592 let json_content = fs::read_to_string(&json_path).unwrap();
594 let json_data: serde_json::Value = serde_json::from_str(&json_content).unwrap();
595
596 if json_data.is_array() {
598 let json_allocations = json_data.as_array().unwrap();
600 assert_eq!(json_allocations.len(), expected_user_count); for alloc in json_allocations {
604 let var_name = alloc.get("var_name");
605 assert!(
606 var_name.is_some() && !var_name.unwrap().is_null(),
607 "All allocations in UserOnly mode should have var_name"
608 );
609 }
610 } else if json_data.is_object() {
611 if let Some(allocations_array) = json_data.get("allocations") {
613 assert!(allocations_array.is_array());
614 let json_allocations = allocations_array.as_array().unwrap();
615 assert_eq!(json_allocations.len(), expected_user_count); for alloc in json_allocations {
619 let var_name = alloc.get("var_name");
620 assert!(
621 var_name.is_some() && !var_name.unwrap().is_null(),
622 "All allocations in UserOnly mode should have var_name"
623 );
624 }
625 }
626 }
627 }
628
629 #[test]
630 fn test_export_to_binary_with_mode_full() {
631 let temp_dir = TempDir::new().unwrap();
632 let binary_path = temp_dir.path().join("test_full.memscope");
633 let allocations = create_test_allocations();
634
635 let config = BinaryExportConfig::debug_comprehensive();
636
637 let result =
638 export_to_binary_with_mode(&allocations, &binary_path, BinaryExportMode::Full, &config);
639 assert!(result.is_ok());
640 assert!(binary_path.exists());
641
642 let info = detect_binary_type(&binary_path).unwrap();
644 assert!(info.is_full_binary());
645 assert_eq!(info.user_count, 2); assert_eq!(info.system_count, 1); assert_eq!(info.total_count, 3); }
649
650 #[test]
651 fn test_detect_binary_type_user_only() {
652 let temp_dir = TempDir::new().unwrap();
653 let binary_path = temp_dir.path().join("test_detect_user.memscope");
654 let allocations = create_test_allocations();
655
656 let config = BinaryExportConfig::minimal();
657
658 export_to_binary_with_mode(
659 &allocations,
660 &binary_path,
661 BinaryExportMode::UserOnly,
662 &config,
663 )
664 .unwrap();
665
666 let info = detect_binary_type(&binary_path).unwrap();
667 assert!(info.is_user_only());
668 assert!(!info.is_full_binary());
669 assert_eq!(info.export_mode, BinaryExportMode::UserOnly);
670 assert!(info.is_count_consistent);
671 assert!(info.file_size > 0);
672
673 let description = info.type_description();
674 assert!(description.contains("User-only binary"));
675
676 let strategy = info.recommended_strategy();
677 assert!(strategy.contains("Simple processing"));
678
679 let json_path = temp_dir.path().join("verify_user_only.json");
681 let parse_result = parse_binary_to_json(&binary_path, &json_path);
682 assert!(parse_result.is_ok());
683
684 let json_content = fs::read_to_string(&json_path).unwrap();
686
687 tracing::info!(
688 "JSON content preview: {}",
689 &json_content[..json_content.len().min(500)]
690 );
691
692 let json_data: serde_json::Value = serde_json::from_str(&json_content).unwrap();
693
694 match json_data {
696 serde_json::Value::Array(ref json_allocations) => {
697 tracing::info!("JSON is an array with {} elements", json_allocations.len());
698
699 let json_user_count = json_allocations
701 .iter()
702 .filter(|alloc| alloc.get("var_name").is_some_and(|v| !v.is_null()))
703 .count();
704 let json_system_count = json_allocations.len() - json_user_count;
705
706 tracing::info!(
707 "JSON contains: {} user, {} system allocations",
708 json_user_count,
709 json_system_count
710 );
711
712 assert_eq!(json_user_count, info.user_count as usize);
714 assert_eq!(json_system_count, info.system_count as usize);
715 assert_eq!(json_allocations.len(), info.total_count as usize);
716 }
717 serde_json::Value::Object(ref obj) => {
718 tracing::info!(
719 "JSON is an object with keys: {:?}",
720 obj.keys().collect::<Vec<_>>()
721 );
722 }
723 _ => {
724 tracing::info!("JSON is neither array nor object: {:?}", json_data);
725 }
726 }
727
728 let original_user_count = allocations.iter().filter(|a| a.var_name.is_some()).count();
730 tracing::info!("Original user allocations: {}", original_user_count);
731
732 tracing::info!(
734 "Detected user count: {}, system count: {}",
735 info.user_count,
736 info.system_count
737 );
738 }
739
740 #[test]
741 fn test_detect_binary_type_full() {
742 let temp_dir = TempDir::new().unwrap();
743 let binary_path = temp_dir.path().join("test_detect_full.memscope");
744 let allocations = create_test_allocations();
745
746 let config = BinaryExportConfig::debug_comprehensive();
747
748 export_to_binary_with_mode(&allocations, &binary_path, BinaryExportMode::Full, &config)
749 .unwrap();
750
751 let info = detect_binary_type(&binary_path).unwrap();
752 assert!(!info.is_user_only());
753 assert!(info.is_full_binary());
754 assert_eq!(info.export_mode, BinaryExportMode::Full);
755 assert_eq!(info.user_count, 2);
756 assert_eq!(info.system_count, 1);
757 assert_eq!(info.total_count, 3);
758 assert!(info.is_count_consistent);
759 assert!(info.file_size > 0);
760
761 let description = info.type_description();
762 assert!(description.contains("Full binary"));
763 assert!(description.contains("3 total allocations"));
764 assert!(description.contains("2 user + 1 system"));
765
766 let strategy = info.recommended_strategy();
767 assert!(strategy.contains("Optimized processing"));
768 }
769
770 #[test]
771 fn test_detect_binary_type_invalid_file() {
772 let temp_dir = TempDir::new().unwrap();
773 let invalid_path = temp_dir.path().join("nonexistent.memscope");
774
775 let result = detect_binary_type(&invalid_path);
776 assert!(result.is_err());
777 if let Err(BinaryExportError::Io(_)) = result {
778 } else {
780 panic!("Expected IoError for nonexistent file");
781 }
782 }
783
784 #[test]
785 fn test_detect_binary_type_invalid_format() {
786 let temp_dir = TempDir::new().unwrap();
787 let invalid_path = temp_dir.path().join("invalid.memscope");
788
789 fs::write(&invalid_path, b"invalid binary data").unwrap();
791
792 let result = detect_binary_type(&invalid_path);
793 assert!(result.is_err());
794
795 match result {
797 Err(BinaryExportError::InvalidFormat) => {
798 }
800 Err(BinaryExportError::Io(_)) => {
801 }
803 Err(BinaryExportError::CorruptedData(msg)) => {
804 assert!(!msg.is_empty(), "Error message should be descriptive");
806 }
807 Err(other) => {
808 panic!("Unexpected error type for invalid format: {:?}", other);
809 }
810 Ok(_) => {
811 panic!("Invalid binary file should not be detected as valid");
812 }
813 }
814 }
815
816 #[test]
817 fn test_parse_binary_to_json() {
818 let temp_dir = TempDir::new().unwrap();
819 let binary_path = temp_dir.path().join("test_parse.memscope");
820 let json_path = temp_dir.path().join("test_parse.json");
821 let allocations = create_test_allocations();
822
823 export_to_binary(&allocations, &binary_path).unwrap();
825
826 let result = parse_binary_to_json(&binary_path, &json_path);
828 assert!(result.is_ok());
829 assert!(json_path.exists());
830 assert!(json_path.metadata().unwrap().len() > 0);
831
832 let json_content = fs::read_to_string(&json_path).unwrap();
834 assert!(!json_content.is_empty());
835 let _: serde_json::Value = serde_json::from_str(&json_content).unwrap();
837 }
838
839 #[test]
840 fn test_parse_binary_to_html() {
841 let temp_dir = TempDir::new().unwrap();
842 let binary_path = temp_dir.path().join("test_parse_html.memscope");
843 let html_path = temp_dir.path().join("test_parse.html");
844 let allocations = create_test_allocations();
845
846 export_to_binary(&allocations, &binary_path).unwrap();
848
849 let result = parse_binary_to_html(&binary_path, &html_path);
851 assert!(result.is_ok());
852 assert!(html_path.exists());
853 assert!(html_path.metadata().unwrap().len() > 0);
854
855 let html_content = fs::read_to_string(&html_path).unwrap();
857 assert!(!html_content.is_empty());
858 assert!(html_content.contains("<!DOCTYPE html") || html_content.contains("<html"));
859 }
860
861 #[test]
862 fn test_empty_allocations() {
863 let temp_dir = TempDir::new().unwrap();
864 let binary_path = temp_dir.path().join("test_empty.memscope");
865 let allocations: Vec<AllocationInfo> = vec![];
866
867 let result = export_to_binary(&allocations, &binary_path);
868 assert!(result.is_ok());
869 assert!(binary_path.exists());
870
871 let info = detect_binary_type(&binary_path).unwrap();
873 assert_eq!(info.total_count, 0);
874 assert_eq!(info.user_count, 0);
875 assert_eq!(info.system_count, 0);
876 }
877
878 #[test]
879 fn test_large_allocation_count() {
880 let temp_dir = TempDir::new().unwrap();
881 let binary_path = temp_dir.path().join("test_large.memscope");
882
883 let mut allocations = Vec::new();
885 let mut _expected_user_count = 0;
886 let mut _expected_system_count = 0;
887
888 for i in 0..100 {
889 let mut alloc = AllocationInfo::new(0x1000 + (i * 0x100), 64 + i);
890 if i % 2 == 0 {
891 alloc.var_name = Some(format!("var_{i}"));
892 _expected_user_count += 1;
893 } else {
894 alloc.var_name = None;
895 _expected_system_count += 1;
896 }
897 alloc.type_name = Some(format!("Type_{i}"));
898 alloc.timestamp_alloc = 1000 + i as u64;
899 alloc.stack_trace = Some(vec![format!("func_{i}")]);
900 alloc.thread_id = ((i % 4) + 1).to_string();
901 allocations.push(alloc);
902 }
903
904 tracing::info!(
905 "Expected: {} user, {} system, {} total",
906 _expected_user_count,
907 _expected_system_count,
908 allocations.len()
909 );
910
911 let result = export_to_binary(&allocations, &binary_path);
912 assert!(result.is_ok());
913 assert!(binary_path.exists());
914
915 let info = detect_binary_type(&binary_path).unwrap();
917 tracing::info!(
918 "Detected: {} user, {} system, {} total",
919 info.user_count,
920 info.system_count,
921 info.total_count
922 );
923
924 assert_eq!(info.total_count, 100);
925 assert!(info.is_count_consistent);
926
927 let json_path = temp_dir.path().join("test_large.json");
929 let parse_result = parse_binary_to_json(&binary_path, &json_path);
930 assert!(parse_result.is_ok());
931
932 let json_content = fs::read_to_string(&json_path).unwrap();
933 let json_data: serde_json::Value = serde_json::from_str(&json_content).unwrap();
934
935 if let Some(allocations_array) = json_data.get("allocations") {
936 let json_allocations = allocations_array.as_array().unwrap();
937 let json_user_count = json_allocations
938 .iter()
939 .filter(|alloc| alloc.get("var_name").is_some_and(|v| !v.is_null()))
940 .count();
941 let json_system_count = json_allocations.len() - json_user_count;
942
943 tracing::info!(
944 "JSON parsed: {} user, {} system, {} total",
945 json_user_count,
946 json_system_count,
947 json_allocations.len()
948 );
949
950 assert_eq!(json_allocations.len(), 100);
952 assert_eq!(json_user_count, _expected_user_count);
953 assert_eq!(json_system_count, _expected_system_count);
954 }
955 }
956}