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