1use crate::export::binary::config::{DashboardExportStats, DashboardFormat, DashboardOptions};
7use crate::export::binary::{error::BinaryExportError, DataScope};
8use std::path::Path;
9
10#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum BinaryOutputFormat {
13 Json,
15 Html,
17 HtmlSystem,
19 HtmlBoth,
21 Both,
23}
24
25#[derive(Debug, Clone)]
27pub struct BinaryExportConfig {
28 pub enable_parallel_processing: bool,
30 pub buffer_size: usize,
32 pub batch_size: usize,
34 pub enable_streaming: bool,
36 pub thread_count: Option<usize>,
38}
39
40impl Default for BinaryExportConfig {
41 fn default() -> Self {
42 Self {
43 enable_parallel_processing: true,
44 buffer_size: 256 * 1024, batch_size: 2000,
46 enable_streaming: true,
47 thread_count: None, }
49 }
50}
51
52impl BinaryExportConfig {
53 pub fn new() -> Self {
55 Self::default()
56 }
57
58 pub fn fast() -> Self {
60 Self {
61 enable_parallel_processing: true,
62 buffer_size: 512 * 1024, batch_size: 3000,
64 enable_streaming: true,
65 thread_count: None,
66 }
67 }
68
69 pub fn large_files() -> Self {
71 Self {
72 enable_parallel_processing: true,
73 buffer_size: 1024 * 1024, batch_size: 5000,
75 enable_streaming: true,
76 thread_count: None,
77 }
78 }
79
80 pub fn parallel_processing(mut self, enabled: bool) -> Self {
82 self.enable_parallel_processing = enabled;
83 self
84 }
85
86 pub fn buffer_size(mut self, size: usize) -> Self {
88 self.buffer_size = size;
89 self
90 }
91
92 pub fn batch_size(mut self, size: usize) -> Self {
94 self.batch_size = size;
95 self
96 }
97
98 pub fn streaming(mut self, enabled: bool) -> Self {
100 self.enable_streaming = enabled;
101 self
102 }
103
104 pub fn thread_count(mut self, count: Option<usize>) -> Self {
106 self.thread_count = count;
107 self
108 }
109}
110
111pub fn export_binary<P: AsRef<Path>>(
134 binary_path: P,
135 base_name: &str,
136 format: BinaryOutputFormat,
137) -> Result<(), BinaryExportError> {
138 export_binary_optimized(binary_path, base_name, format, None)
139}
140
141pub fn export_binary_optimized<P: AsRef<Path>>(
143 binary_path: P,
144 base_name: &str,
145 format: BinaryOutputFormat,
146 config: Option<BinaryExportConfig>,
147) -> Result<(), BinaryExportError> {
148 let config = config.unwrap_or_default();
149 let start = std::time::Instant::now();
150 let binary_path = binary_path.as_ref();
151
152 tracing::info!(
153 "๐ Starting optimized binary export for {:?} format",
154 format
155 );
156 tracing::info!(
157 " - Parallel processing: {}",
158 config.enable_parallel_processing
159 );
160 tracing::info!(" - Streaming: {}", config.enable_streaming);
161 tracing::info!(" - Batch size: {}", config.batch_size);
162
163 let setup_start = std::time::Instant::now();
165
166 let base_memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
168 let project_dir = base_memory_analysis_dir.join(base_name);
169 std::fs::create_dir_all(&project_dir)?;
170
171 if let Some(thread_count) = config.thread_count {
173 rayon::ThreadPoolBuilder::new()
174 .num_threads(thread_count)
175 .build_global()
176 .map_err(|e| {
177 BinaryExportError::CorruptedData(format!("Failed to configure thread pool: {e}"))
178 })?;
179 }
180
181 let setup_time = setup_start.elapsed();
182 tracing::info!("โ
Setup completed in {}ms", setup_time.as_millis());
183
184 let export_start = std::time::Instant::now();
186
187 match format {
188 BinaryOutputFormat::Json => {
189 export_json_optimized(binary_path, base_name, &config)?;
191 }
192 BinaryOutputFormat::Html => {
193 let html_path = project_dir.join(format!("{base_name}_user_dashboard.html"));
195 export_html_filtered(binary_path, &html_path, base_name, &config, true)?;
196 }
197 BinaryOutputFormat::HtmlSystem => {
198 let html_path = project_dir.join(format!("{base_name}_system_dashboard.html"));
200 export_html_filtered(binary_path, &html_path, base_name, &config, false)?;
201 }
202 BinaryOutputFormat::HtmlBoth => {
203 let user_html_path = project_dir.join(format!("{base_name}_user_dashboard.html"));
205 let system_html_path = project_dir.join(format!("{base_name}_system_dashboard.html"));
206
207 use rayon::prelude::*;
209 let results: Result<Vec<()>, BinaryExportError> = [("user", true), ("system", false)]
210 .par_iter()
211 .map(|(data_type, is_user_only)| {
212 let html_path = if *is_user_only {
213 &user_html_path
214 } else {
215 &system_html_path
216 };
217 tracing::info!("๐งต [{}] Starting HTML generation", data_type.to_uppercase());
218 export_html_filtered(binary_path, html_path, base_name, &config, *is_user_only)
219 })
220 .collect();
221
222 results?;
223 }
224 BinaryOutputFormat::Both => {
225 export_both_formats_parallel(binary_path, base_name, &config)?;
227 }
228 }
229
230 let export_time = export_start.elapsed();
231 let total_time = start.elapsed();
232
233 tracing::info!(
234 "โ
Export completed in {}ms (setup: {}ms, export: {}ms)",
235 total_time.as_millis(),
236 setup_time.as_millis(),
237 export_time.as_millis()
238 );
239
240 provide_performance_feedback(format, &config, total_time);
242
243 Ok(())
244}
245
246pub fn export_binary_with_format<P: AsRef<Path>>(
248 binary_path: P,
249 base_name: &str,
250 format: BinaryOutputFormat,
251) -> Result<(), BinaryExportError> {
252 export_binary_optimized(binary_path, base_name, format, None)
254}
255
256fn export_json_optimized<P: AsRef<Path>>(
260 binary_path: P,
261 base_name: &str,
262 _config: &BinaryExportConfig,
263) -> Result<(), BinaryExportError> {
264 use crate::export::binary::parser::BinaryParser;
267 BinaryParser::parse_full_binary_to_json(binary_path, base_name)
268}
269
270fn export_html_optimized<P: AsRef<Path>>(
272 binary_path: P,
273 output_path: P,
274 project_name: &str,
275 config: &BinaryExportConfig,
276) -> Result<(), BinaryExportError> {
277 use crate::export::binary::binary_html_writer::BinaryTemplateData;
278 use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
279 use crate::export::binary::reader::BinaryReader;
280
281 let start = std::time::Instant::now();
282 let binary_path = binary_path.as_ref();
283
284 tracing::info!("๐จ Starting optimized HTML generation for {}", project_name);
285
286 let mut reader = BinaryReader::new(binary_path)?;
288 let header = reader.read_header()?;
289 let total_count = header.total_count;
290
291 tracing::info!(
292 "๐ Processing {} allocations with batch size {}",
293 total_count,
294 config.batch_size
295 );
296
297 let mut all_allocations = Vec::new();
299 let mut total_memory = 0u64;
300 let mut active_count = 0usize;
301
302 let batch_count = (total_count as usize).div_ceil(config.batch_size);
304
305 for batch_idx in 0..batch_count {
306 let batch_start = batch_idx * config.batch_size;
307 let batch_end = std::cmp::min(batch_start + config.batch_size, total_count as usize);
308
309 tracing::debug!(
310 "Processing batch {}/{} (allocations {}-{})",
311 batch_idx + 1,
312 batch_count,
313 batch_start,
314 batch_end
315 );
316
317 for i in batch_start..batch_end {
318 match reader.read_allocation() {
319 Ok(allocation) => {
320 let binary_data = convert_allocation_to_binary_data(&allocation, i)?;
322
323 total_memory += allocation.size as u64;
325 if allocation.is_active() {
326 active_count += 1;
327 }
328
329 all_allocations.push(binary_data);
330 }
331 Err(e) => {
332 tracing::warn!("โ ๏ธ Skipping corrupted allocation at index {}: {}", i, e);
333 continue;
334 }
335 }
336 }
337
338 if config.enable_streaming && all_allocations.len() > config.batch_size * 2 {
340 tracing::debug!(
341 "๐พ Memory management: {} allocations in buffer",
342 all_allocations.len()
343 );
344 }
345 }
346
347 let analysis_start = std::time::Instant::now();
349
350 let (complex_types, unsafe_ffi, variable_relationships) = (None, None, None);
352
353 let analysis_time = analysis_start.elapsed();
354 tracing::info!("๐ Analysis completed in {}ms", analysis_time.as_millis());
355
356 let template_data = BinaryTemplateData {
357 project_name: project_name.to_string(),
358 allocations: all_allocations,
359 total_memory_usage: total_memory,
360 peak_memory_usage: total_memory, active_allocations_count: active_count,
362 processing_time_ms: start.elapsed().as_millis() as u64,
363 data_source: "binary_optimized_streaming".to_string(),
364 complex_types,
365 unsafe_ffi,
366 variable_relationships,
367 };
368
369 let render_start = std::time::Instant::now();
371 let mut template_engine = BinaryTemplateEngine::new()?;
372 let html_content = template_engine.render_binary_template(&template_data)?;
373 let render_time = render_start.elapsed();
374
375 let write_start = std::time::Instant::now();
377 std::fs::write(output_path, html_content)?;
378 let write_time = write_start.elapsed();
379
380 let total_time = start.elapsed();
381 tracing::info!(
382 "โ
HTML generation completed in {}ms (render: {}ms, write: {}ms)",
383 total_time.as_millis(),
384 render_time.as_millis(),
385 write_time.as_millis()
386 );
387
388 Ok(())
389}
390
391fn export_html_filtered<P: AsRef<Path>>(
393 binary_path: P,
394 output_path: P,
395 project_name: &str,
396 _config: &BinaryExportConfig,
397 user_only: bool,
398) -> Result<(), BinaryExportError> {
399 use crate::export::binary::binary_html_writer::BinaryTemplateData;
400 use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
401 use crate::export::binary::parser::BinaryParser;
402
403 let start = std::time::Instant::now();
404 let data_type = if user_only { "USER" } else { "SYSTEM" };
405
406 tracing::info!(
407 "๐ Starting {} HTML generation for {}",
408 data_type,
409 project_name
410 );
411
412 let load_start = std::time::Instant::now();
414 let all_allocations = BinaryParser::load_allocations_with_recovery(binary_path)?;
415 let load_time = load_start.elapsed();
416
417 let filtered_allocations: Vec<_> = all_allocations
419 .into_iter()
420 .filter(|alloc| {
421 if user_only {
422 alloc.var_name.is_some()
424 } else {
425 alloc.var_name.is_none()
427 }
428 })
429 .collect();
430
431 tracing::info!(
432 "๐ Loaded and filtered {} {} allocations in {}ms",
433 filtered_allocations.len(),
434 data_type,
435 load_time.as_millis()
436 );
437
438 let process_start = std::time::Instant::now();
440 let mut binary_allocations = Vec::with_capacity(filtered_allocations.len());
441 let mut total_memory = 0u64;
442 let mut active_count = 0usize;
443
444 for (i, allocation) in filtered_allocations.iter().enumerate() {
445 let binary_data = convert_allocation_to_binary_data(allocation, i)?;
446
447 total_memory += allocation.size as u64;
448 if allocation.is_active() {
449 active_count += 1;
450 }
451
452 binary_allocations.push(binary_data);
453 }
454 let process_time = process_start.elapsed();
455
456 let analysis_start = std::time::Instant::now();
458
459 let (complex_types, unsafe_ffi, variable_relationships) = (None, None, None);
461
462 let analysis_time = analysis_start.elapsed();
463 tracing::info!(
464 "๐ {} analysis completed in {}ms",
465 data_type,
466 analysis_time.as_millis()
467 );
468
469 let template_data = BinaryTemplateData {
470 project_name: format!("{project_name} ({data_type})"),
471 allocations: binary_allocations,
472 total_memory_usage: total_memory,
473 peak_memory_usage: total_memory,
474 active_allocations_count: active_count,
475 processing_time_ms: start.elapsed().as_millis() as u64,
476 data_source: format!(
477 "binary_{}_filtered",
478 if user_only { "user" } else { "system" }
479 ),
480 complex_types,
481 unsafe_ffi,
482 variable_relationships,
483 };
484
485 let render_start = std::time::Instant::now();
487 let mut template_engine = BinaryTemplateEngine::new()?;
488 let html_content = template_engine.render_binary_template(&template_data)?;
489 let render_time = render_start.elapsed();
490
491 let write_start = std::time::Instant::now();
493 std::fs::write(output_path, html_content)?;
494 let write_time = write_start.elapsed();
495
496 let total_time = start.elapsed();
497 tracing::info!(
498 "โ
{} HTML generation completed in {}ms (load: {}ms, process: {}ms, render: {}ms, write: {}ms)",
499 data_type,
500 total_time.as_millis(),
501 load_time.as_millis(),
502 process_time.as_millis(),
503 render_time.as_millis(),
504 write_time.as_millis()
505 );
506
507 Ok(())
508}
509
510fn export_both_formats_parallel<P: AsRef<Path>>(
519 binary_path: P,
520 base_name: &str,
521 config: &BinaryExportConfig,
522) -> Result<(), BinaryExportError> {
523 use crate::export::binary::parser::BinaryParser;
524 use rayon::prelude::*;
525
526 let binary_path = binary_path.as_ref();
527 let start = std::time::Instant::now();
528
529 tracing::info!("๐ Starting ultra-fast parallel export for both JSON and HTML formats");
530
531 if config.enable_parallel_processing {
532 let load_start = std::time::Instant::now();
534 let all_allocations = BinaryParser::load_allocations_with_recovery(binary_path)?;
535 let load_time = load_start.elapsed();
536 tracing::info!(
537 "๐ Loaded {} allocations in {}ms (shared data)",
538 all_allocations.len(),
539 load_time.as_millis()
540 );
541
542 let base_memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
544 let project_dir = base_memory_analysis_dir.join(base_name);
545 std::fs::create_dir_all(&project_dir)?;
546
547 let results: Result<Vec<()>, BinaryExportError> = [
549 ("json", BinaryOutputFormat::Json),
550 ("html", BinaryOutputFormat::Html),
551 ]
552 .par_iter()
553 .map(|(format_name, format)| {
554 let thread_start = std::time::Instant::now();
555
556 let result = match format {
557 BinaryOutputFormat::Json => {
558 tracing::info!("๐งต [JSON Thread] Starting ultra-fast JSON generation");
559 generate_json_files_parallel(&all_allocations, base_name, &project_dir)
561 }
562 BinaryOutputFormat::Html => {
563 tracing::info!("๐งต [HTML Thread] Starting ultra-fast USER HTML generation");
564 let html_path = project_dir.join(format!("{base_name}_user_dashboard.html"));
565 export_html_with_shared_data_filtered(
567 &all_allocations,
568 &html_path,
569 base_name,
570 config,
571 true,
572 )
573 }
574 _ => unreachable!(),
575 };
576
577 let thread_time = thread_start.elapsed();
578 tracing::info!(
579 "๐งต [{}] Thread completed in {}ms",
580 format_name.to_uppercase(),
581 thread_time.as_millis()
582 );
583
584 result
585 })
586 .collect();
587
588 results?;
589 } else {
590 tracing::info!("๐ Sequential export mode (still optimized)");
592 export_json_optimized(binary_path, base_name, config)?;
593
594 let base_memory_analysis_dir = std::path::Path::new("MemoryAnalysis");
595 let project_dir = base_memory_analysis_dir.join(base_name);
596 let html_path = project_dir.join(format!("{base_name}_dashboard.html"));
597 export_html_optimized(binary_path, &html_path, base_name, config)?;
598 }
599
600 let total_time = start.elapsed();
601 tracing::info!(
602 "โ
Ultra-fast parallel export completed in {}ms",
603 total_time.as_millis()
604 );
605
606 Ok(())
607}
608
609fn provide_performance_feedback(
611 format: BinaryOutputFormat,
612 config: &BinaryExportConfig,
613 elapsed: std::time::Duration,
614) {
615 let elapsed_ms = elapsed.as_millis();
616
617 if elapsed_ms < 1000 {
619 tracing::info!("๐ Excellent performance: {}ms", elapsed_ms);
620 } else if elapsed_ms < 5000 {
621 tracing::info!("โ
Good performance: {}ms", elapsed_ms);
622 } else {
623 tracing::warn!("โ ๏ธ Consider optimization: {}ms", elapsed_ms);
624
625 if !config.enable_parallel_processing && matches!(format, BinaryOutputFormat::Both) {
627 tracing::info!("๐ก Suggestion: Enable parallel processing for Both format");
628 }
629
630 if config.batch_size < 1000 {
631 tracing::info!("๐ก Suggestion: Increase batch size to 2000+ for better performance");
632 }
633 }
634
635 match format {
637 BinaryOutputFormat::Json => {
638 tracing::info!("๐ JSON export completed - ultra-fast performance maintained");
639 }
640 BinaryOutputFormat::Html => {
641 tracing::info!("๐จ HTML user export completed with shared data optimization");
642 }
643 BinaryOutputFormat::HtmlSystem => {
644 tracing::info!("๐ง HTML system export completed with shared data optimization");
645 }
646 BinaryOutputFormat::HtmlBoth => {
647 tracing::info!("๐จ๏ฟฝ Both uHTML exports completed with parallel processing");
648 }
649 BinaryOutputFormat::Both => {
650 if config.enable_parallel_processing {
651 tracing::info!(
652 "๐ Parallel export completed - maximum efficiency with shared data"
653 );
654 } else {
655 tracing::info!(
656 "๐ Sequential export completed - consider enabling parallel processing"
657 );
658 }
659 }
660 }
661}
662
663pub fn export_binary_to_json<P: AsRef<Path>>(
667 binary_path: P,
668 base_name: &str,
669) -> Result<(), BinaryExportError> {
670 export_binary(binary_path, base_name, BinaryOutputFormat::Json)
671}
672
673pub fn export_binary_to_dashboard<P: AsRef<Path>>(
712 binary_path: P,
713 project_name: &str,
714 options: DashboardOptions,
715) -> Result<DashboardExportStats, BinaryExportError> {
716 use crate::export::binary::config::DashboardFormat;
717
718 let _start_time = std::time::Instant::now();
719
720 match options.format {
721 DashboardFormat::Embedded => {
722 export_binary_to_html_embedded_impl(binary_path, project_name, &options)
724 }
725 DashboardFormat::Lightweight => {
726 export_binary_to_html_lightweight_impl(binary_path, project_name, &options)
728 }
729 DashboardFormat::Progressive => {
730 export_binary_to_html_progressive_impl(binary_path, project_name, &options)
732 }
733 }
734}
735
736pub fn export_binary_to_html<P: AsRef<Path>>(
739 binary_path: P,
740 base_name: &str,
741) -> Result<(), BinaryExportError> {
742 let options = DashboardOptions::new()
744 .format(DashboardFormat::Lightweight)
745 .scope(DataScope::UserOnly);
746
747 let _stats = export_binary_to_dashboard(binary_path, base_name, options)?;
748 Ok(())
749}
750
751pub fn export_binary_to_html_system<P: AsRef<Path>>(
754 binary_path: P,
755 base_name: &str,
756) -> Result<(), BinaryExportError> {
757 export_binary(binary_path, base_name, BinaryOutputFormat::HtmlSystem)
758}
759
760pub fn export_binary_to_html_both<P: AsRef<Path>>(
763 binary_path: P,
764 base_name: &str,
765) -> Result<(), BinaryExportError> {
766 export_binary(binary_path, base_name, BinaryOutputFormat::HtmlBoth)
767}
768
769pub fn export_binary_to_both<P: AsRef<Path>>(
772 binary_path: P,
773 base_name: &str,
774) -> Result<(), BinaryExportError> {
775 export_binary(binary_path, base_name, BinaryOutputFormat::Both)
776}
777
778pub fn export_binary_with_config<P: AsRef<Path>>(
780 binary_path: P,
781 base_name: &str,
782 format: BinaryOutputFormat,
783 config: BinaryExportConfig,
784) -> Result<(), BinaryExportError> {
785 export_binary_optimized(binary_path, base_name, format, Some(config))
786}
787
788pub fn show_export_options() {
792 tracing::info!("๐ Binary Export Options - Optimized Performance");
793 tracing::info!("================================================");
794 tracing::info!("");
795 tracing::info!("๐ **BASIC USAGE (Unified API):**");
796 tracing::info!(" export_binary(\"data.bin\", \"project\", BinaryOutputFormat::Json)?; // JSON only (ultra-fast)");
797 tracing::info!(" export_binary(\"data.bin\", \"project\", BinaryOutputFormat::Html)?; // HTML user data (lightweight)");
798 tracing::info!(" export_binary(\"data.bin\", \"project\", BinaryOutputFormat::HtmlSystem)?; // HTML system data");
799 tracing::info!(" export_binary(\"data.bin\", \"project\", BinaryOutputFormat::HtmlBoth)?; // Both HTML files (parallel)");
800 tracing::info!(" export_binary(\"data.bin\", \"project\", BinaryOutputFormat::Both)?; // JSON + HTML user (parallel)");
801 tracing::info!("");
802 tracing::info!("๐ **CONVENIENCE FUNCTIONS:**");
803 tracing::info!(
804 " export_binary_to_json(\"data.bin\", \"project\")?; // JSON only (ultra-fast)"
805 );
806 tracing::info!(" export_binary_to_html(\"data.bin\", \"project\")?; // HTML user data (lightweight)");
807 tracing::info!(
808 " export_binary_to_html_system(\"data.bin\", \"project\")?; // HTML system data"
809 );
810 tracing::info!(" export_binary_to_html_both(\"data.bin\", \"project\")?; // Both HTML files (parallel)");
811 tracing::info!(" export_binary_to_both(\"data.bin\", \"project\")?; // JSON + HTML user (parallel)");
812 tracing::info!("");
813 tracing::info!("โ๏ธ **ADVANCED USAGE:**");
814 tracing::info!(" let config = BinaryExportConfig {{");
815 tracing::info!(" enable_parallel_processing: true,");
816 tracing::info!(" batch_size: 3000,");
817 tracing::info!(" buffer_size: 512 * 1024, // 512KB");
818 tracing::info!(" thread_count: Some(4),");
819 tracing::info!(" ..Default::default()");
820 tracing::info!(" }};");
821 tracing::info!(" export_binary_with_config(\"data.bin\", \"project\", BinaryOutputFormat::Both, config)?;");
822 tracing::info!("");
823 tracing::info!("๐ฏ **PERFORMANCE TIPS:**");
824 tracing::info!(" โ
Use BinaryOutputFormat::Json for fastest export (existing performance)");
825 tracing::info!(
826 " โ
Use BinaryOutputFormat::Both with parallel processing for maximum efficiency"
827 );
828 tracing::info!(" โ
Increase batch_size to 3000+ for large files (>100MB)");
829 tracing::info!(" โ
Set thread_count to match your CPU cores for parallel processing");
830 tracing::info!(" โ
Use larger buffer_size (512KB+) for very large files");
831 tracing::info!("");
832 tracing::info!("๐ **EXPECTED PERFORMANCE:**");
833 tracing::info!(
834 " - JSON only: Same ultra-fast performance as parse_full_binary_to_json (<300ms)"
835 );
836 tracing::info!(" - HTML only: Matches JSON performance with shared data optimization");
837 tracing::info!(" - Both formats: 60-80% faster than sequential with parallel processing");
838 tracing::info!(
839 " - Large files (>1M allocations): Up to 90% improvement with shared data loading"
840 );
841}
842
843fn generate_json_files_parallel(
846 allocations: &[crate::core::types::AllocationInfo],
847 base_name: &str,
848 project_dir: &std::path::Path,
849) -> Result<(), BinaryExportError> {
850 use crate::export::binary::parser::BinaryParser;
851 use rayon::prelude::*;
852
853 let json_start = std::time::Instant::now();
854
855 let paths = [
856 project_dir.join(format!("{base_name}_memory_analysis.json")),
857 project_dir.join(format!("{base_name}_lifetime.json")),
858 project_dir.join(format!("{base_name}_performance.json")),
859 project_dir.join(format!("{base_name}_unsafe_ffi.json")),
860 project_dir.join(format!("{base_name}_complex_types.json")),
861 ];
862
863 let results: Result<Vec<()>, BinaryExportError> = paths
865 .par_iter()
866 .enumerate()
867 .map(|(i, path)| match i {
868 0 => BinaryParser::generate_memory_analysis_json(allocations, path),
869 1 => BinaryParser::generate_lifetime_analysis_json(allocations, path),
870 2 => BinaryParser::generate_performance_analysis_json(allocations, path),
871 3 => BinaryParser::generate_unsafe_ffi_analysis_json(allocations, path),
872 4 => BinaryParser::generate_complex_types_analysis_json(allocations, path),
873 _ => unreachable!(),
874 })
875 .collect();
876
877 results?;
878
879 let json_time = json_start.elapsed();
880 tracing::info!(
881 "๐ Generated 5 JSON files in parallel in {}ms (shared data)",
882 json_time.as_millis()
883 );
884
885 Ok(())
886}
887
888fn export_html_with_shared_data_filtered(
890 allocations: &[crate::core::types::AllocationInfo],
891 output_path: &std::path::Path,
892 project_name: &str,
893 _config: &BinaryExportConfig,
894 user_only: bool,
895) -> Result<(), BinaryExportError> {
896 use crate::export::binary::binary_html_writer::BinaryTemplateData;
897 use crate::export::binary::binary_template_engine::BinaryTemplateEngine;
898
899 let start = std::time::Instant::now();
900 let data_type = if user_only { "USER" } else { "SYSTEM" };
901
902 tracing::info!(
903 "๐จ Starting ultra-fast {} HTML generation with shared data for {}",
904 data_type,
905 project_name
906 );
907
908 let filtered_allocations: Vec<_> = allocations
910 .iter()
911 .filter(|alloc| {
912 if user_only {
913 alloc.var_name.is_some()
915 } else {
916 alloc.var_name.is_none()
918 }
919 })
920 .collect();
921
922 tracing::info!(
923 "๐ Filtered {} {} allocations (no I/O overhead)",
924 filtered_allocations.len(),
925 data_type
926 );
927
928 let mut all_allocations = Vec::with_capacity(filtered_allocations.len());
930 let mut total_memory = 0u64;
931 let mut active_count = 0usize;
932
933 for (i, allocation) in filtered_allocations.iter().enumerate() {
935 let binary_data = convert_allocation_to_binary_data(allocation, i)?;
936
937 total_memory += allocation.size as u64;
938 if allocation.is_active() {
939 active_count += 1;
940 }
941
942 all_allocations.push(binary_data);
943 }
944
945 let template_data = BinaryTemplateData {
947 project_name: format!("{project_name} ({data_type})"),
948 allocations: all_allocations,
949 total_memory_usage: total_memory,
950 peak_memory_usage: total_memory,
951 active_allocations_count: active_count,
952 processing_time_ms: start.elapsed().as_millis() as u64,
953 data_source: format!(
954 "binary_ultra_fast_shared_{}",
955 if user_only { "user" } else { "system" }
956 ),
957 complex_types: None, unsafe_ffi: None, variable_relationships: None, };
961
962 let render_start = std::time::Instant::now();
964 let mut template_engine = BinaryTemplateEngine::new()?;
965 let html_content = template_engine.render_binary_template(&template_data)?;
966 let render_time = render_start.elapsed();
967
968 let write_start = std::time::Instant::now();
970 std::fs::write(output_path, html_content)?;
971 let write_time = write_start.elapsed();
972
973 let total_time = start.elapsed();
974 tracing::info!(
975 "โ
Ultra-fast {} HTML generation completed in {}ms (render: {}ms, write: {}ms)",
976 data_type,
977 total_time.as_millis(),
978 render_time.as_millis(),
979 write_time.as_millis()
980 );
981
982 Ok(())
983}
984
985fn convert_allocation_to_binary_data(
987 allocation: &crate::core::types::AllocationInfo,
988 _index: usize,
989) -> Result<crate::export::binary::binary_html_writer::BinaryAllocationData, BinaryExportError> {
990 use std::collections::HashMap;
991
992 Ok(
993 crate::export::binary::binary_html_writer::BinaryAllocationData {
994 id: allocation.ptr as u64,
995 size: allocation.size,
996 type_name: allocation
997 .type_name
998 .clone()
999 .unwrap_or_else(|| "unknown_type".to_string()),
1000 scope_name: allocation
1001 .scope_name
1002 .clone()
1003 .unwrap_or_else(|| "global".to_string()),
1004 timestamp_alloc: allocation.timestamp_alloc,
1005 is_active: allocation.is_active(),
1006 ptr: allocation.ptr,
1007 thread_id: allocation.thread_id.clone(),
1008 var_name: allocation.var_name.clone(),
1009 borrow_count: allocation.borrow_count,
1010 is_leaked: allocation.is_leaked,
1011 lifetime_ms: allocation.lifetime_ms,
1012 optional_fields: HashMap::new(), },
1014 )
1015}
1016
1017fn export_binary_to_html_embedded_impl<P: AsRef<Path>>(
1023 binary_path: P,
1024 project_name: &str,
1025 options: &DashboardOptions,
1026) -> Result<DashboardExportStats, BinaryExportError> {
1027 let _start_time = std::time::Instant::now();
1028
1029 export_binary(binary_path, project_name, BinaryOutputFormat::Html)?;
1031
1032 let possible_paths = vec![
1034 format!(
1035 "MemoryAnalysis/{}/{}_dashboard.html",
1036 project_name, project_name
1037 ),
1038 format!(
1039 "MemoryAnalysis/{}/{}_user_dashboard.html",
1040 project_name, project_name
1041 ),
1042 format!(
1043 "MemoryAnalysis/{}/{}_system_dashboard.html",
1044 project_name, project_name
1045 ),
1046 ];
1047
1048 let mut html_size = 0;
1049 for path in &possible_paths {
1050 if let Ok(metadata) = std::fs::metadata(path) {
1051 html_size = metadata.len() as usize;
1052 tracing::debug!("Found HTML file: {} ({} bytes)", path, html_size);
1053 break;
1054 }
1055 }
1056
1057 Ok(DashboardExportStats {
1058 total_files_generated: 1,
1059 html_size,
1060 total_json_size: 0, processing_time_ms: _start_time.elapsed().as_millis() as u64,
1062 allocations_processed: 0, format_used: DashboardFormat::Embedded,
1064 scope_used: options.scope.clone(),
1065 })
1066}
1067
1068fn export_binary_to_html_lightweight_impl<P: AsRef<Path>>(
1070 binary_path: P,
1071 project_name: &str,
1072 options: &DashboardOptions,
1073) -> Result<DashboardExportStats, BinaryExportError> {
1074 let _start_time = std::time::Instant::now();
1075
1076 tracing::info!("๐ Lightweight format requested - using embedded format as fallback for now");
1079
1080 let mut stats = export_binary_to_html_embedded_impl(binary_path, project_name, options)?;
1081 stats.format_used = DashboardFormat::Lightweight;
1082
1083 Ok(stats)
1084}
1085
1086fn export_binary_to_html_progressive_impl<P: AsRef<Path>>(
1088 binary_path: P,
1089 project_name: &str,
1090 options: &DashboardOptions,
1091) -> Result<DashboardExportStats, BinaryExportError> {
1092 let _start_time = std::time::Instant::now();
1093
1094 tracing::info!("๐ Progressive format requested - using embedded format as fallback for now");
1097
1098 let mut stats = export_binary_to_html_embedded_impl(binary_path, project_name, options)?;
1099 stats.format_used = DashboardFormat::Progressive;
1100
1101 Ok(stats)
1102}
1103
1104#[cfg(test)]
1105mod tests {
1106 use super::*;
1107
1108 #[derive(serde::Serialize)]
1109 struct AllocationData {
1110 id: u64,
1111 size: u64,
1112 type_name: String,
1113 location: String,
1114 timestamp: u64,
1115 status: String,
1116 }
1117
1118 impl From<crate::core::types::AllocationInfo> for AllocationData {
1119 fn from(alloc: crate::core::types::AllocationInfo) -> Self {
1120 Self {
1121 id: alloc.ptr as u64, size: alloc.size as u64,
1123 type_name: alloc
1124 .type_name
1125 .clone()
1126 .unwrap_or_else(|| "Unknown".to_string()),
1127 location: alloc
1128 .scope_name
1129 .clone()
1130 .unwrap_or_else(|| "Unknown".to_string()),
1131 timestamp: alloc.timestamp_alloc,
1132 status: if alloc.is_active() {
1133 "Active".to_string()
1134 } else {
1135 "Freed".to_string()
1136 },
1137 }
1138 }
1139 }
1140
1141 #[test]
1142 fn test_binary_output_format_debug_clone_partialeq() {
1143 let format = BinaryOutputFormat::Json;
1145 let debug_str = format!("{format:?}");
1146 assert!(debug_str.contains("Json"));
1147
1148 let format1 = BinaryOutputFormat::Html;
1150 let format2 = format1;
1151 assert_eq!(format1, format2);
1152
1153 assert_eq!(BinaryOutputFormat::Json, BinaryOutputFormat::Json);
1155 assert_eq!(BinaryOutputFormat::Html, BinaryOutputFormat::Html);
1156 assert_eq!(
1157 BinaryOutputFormat::HtmlSystem,
1158 BinaryOutputFormat::HtmlSystem
1159 );
1160 assert_eq!(BinaryOutputFormat::HtmlBoth, BinaryOutputFormat::HtmlBoth);
1161 assert_eq!(BinaryOutputFormat::Both, BinaryOutputFormat::Both);
1162
1163 assert_ne!(BinaryOutputFormat::Json, BinaryOutputFormat::Html);
1164 assert_ne!(BinaryOutputFormat::Html, BinaryOutputFormat::HtmlSystem);
1165 assert_ne!(BinaryOutputFormat::HtmlSystem, BinaryOutputFormat::HtmlBoth);
1166 assert_ne!(BinaryOutputFormat::HtmlBoth, BinaryOutputFormat::Both);
1167 }
1168
1169 #[test]
1170 fn test_binary_export_config_default() {
1171 let config = BinaryExportConfig::default();
1172
1173 assert!(config.enable_parallel_processing);
1174 assert_eq!(config.buffer_size, 256 * 1024);
1175 assert_eq!(config.batch_size, 2000);
1176 assert!(config.enable_streaming);
1177 assert!(config.thread_count.is_none());
1178 }
1179
1180 #[test]
1181 fn test_binary_export_config_new() {
1182 let config1 = BinaryExportConfig::new();
1183 let config2 = BinaryExportConfig::default();
1184
1185 assert_eq!(
1186 config1.enable_parallel_processing,
1187 config2.enable_parallel_processing
1188 );
1189 assert_eq!(config1.buffer_size, config2.buffer_size);
1190 assert_eq!(config1.batch_size, config2.batch_size);
1191 assert_eq!(config1.enable_streaming, config2.enable_streaming);
1192 assert_eq!(config1.thread_count, config2.thread_count);
1193 }
1194
1195 #[test]
1196 fn test_binary_export_config_fast() {
1197 let config = BinaryExportConfig::fast();
1198
1199 assert!(config.enable_parallel_processing);
1200 assert_eq!(config.buffer_size, 512 * 1024);
1201 assert_eq!(config.batch_size, 3000);
1202 assert!(config.enable_streaming);
1203 assert!(config.thread_count.is_none());
1204 }
1205
1206 #[test]
1207 fn test_binary_export_config_large_files() {
1208 let config = BinaryExportConfig::large_files();
1209
1210 assert!(config.enable_parallel_processing);
1211 assert_eq!(config.buffer_size, 1024 * 1024);
1212 assert_eq!(config.batch_size, 5000);
1213 assert!(config.enable_streaming);
1214 assert!(config.thread_count.is_none());
1215 }
1216
1217 #[test]
1218 fn test_binary_export_config_builder_methods() {
1219 let config = BinaryExportConfig::new()
1220 .parallel_processing(false)
1221 .buffer_size(128 * 1024)
1222 .batch_size(1000)
1223 .streaming(false)
1224 .thread_count(Some(4));
1225
1226 assert!(!config.enable_parallel_processing);
1227 assert_eq!(config.buffer_size, 128 * 1024);
1228 assert_eq!(config.batch_size, 1000);
1229 assert!(!config.enable_streaming);
1230 assert_eq!(config.thread_count, Some(4));
1231 }
1232
1233 #[test]
1234 fn test_binary_export_config_debug_clone() {
1235 let config = BinaryExportConfig::default();
1236
1237 let debug_str = format!("{config:?}");
1239 assert!(debug_str.contains("BinaryExportConfig"));
1240 assert!(debug_str.contains("enable_parallel_processing"));
1241 assert!(debug_str.contains("buffer_size"));
1242
1243 let cloned_config = config.clone();
1245 assert_eq!(
1246 cloned_config.enable_parallel_processing,
1247 config.enable_parallel_processing
1248 );
1249 assert_eq!(cloned_config.buffer_size, config.buffer_size);
1250 assert_eq!(cloned_config.batch_size, config.batch_size);
1251 assert_eq!(cloned_config.enable_streaming, config.enable_streaming);
1252 assert_eq!(cloned_config.thread_count, config.thread_count);
1253 }
1254
1255 #[test]
1256 fn test_allocation_data_from_allocation_info() {
1257 let allocation_info = crate::core::types::AllocationInfo {
1258 ptr: 0x1000,
1259 size: 64,
1260 var_name: Some("test_var".to_string()),
1261 type_name: Some("String".to_string()),
1262 scope_name: Some("main".to_string()),
1263 timestamp_alloc: 1234567890,
1264 timestamp_dealloc: None,
1265 thread_id: "main".to_string(),
1266 borrow_count: 0,
1267 stack_trace: None,
1268 is_leaked: false,
1269 lifetime_ms: Some(100),
1270 borrow_info: None,
1271 clone_info: None,
1272 ownership_history_available: false,
1273 smart_pointer_info: None,
1274 memory_layout: None,
1275 generic_info: None,
1276 dynamic_type_info: None,
1277 runtime_state: None,
1278 stack_allocation: None,
1279 temporary_object: None,
1280 fragmentation_analysis: None,
1281 generic_instantiation: None,
1282 type_relationships: None,
1283 type_usage: None,
1284 function_call_tracking: None,
1285 lifecycle_tracking: None,
1286 access_tracking: None,
1287 drop_chain_analysis: None,
1288 };
1289
1290 let allocation_data = AllocationData::from(allocation_info);
1291
1292 assert_eq!(allocation_data.id, 0x1000);
1293 assert_eq!(allocation_data.size, 64);
1294 assert_eq!(allocation_data.type_name, "String");
1295 assert_eq!(allocation_data.location, "main");
1296 assert_eq!(allocation_data.timestamp, 1234567890);
1297 assert_eq!(allocation_data.status, "Active");
1298 }
1299
1300 #[test]
1301 fn test_allocation_data_from_allocation_info_with_defaults() {
1302 let allocation_info = crate::core::types::AllocationInfo {
1303 ptr: 0x2000,
1304 size: 128,
1305 var_name: None,
1306 type_name: None, scope_name: None, timestamp_alloc: 1234567900,
1309 timestamp_dealloc: Some(1234567950), thread_id: "worker".to_string(),
1311 borrow_count: 0,
1312 stack_trace: None,
1313 is_leaked: false,
1314 lifetime_ms: Some(50),
1315 borrow_info: None,
1316 clone_info: None,
1317 ownership_history_available: false,
1318 smart_pointer_info: None,
1319 memory_layout: None,
1320 generic_info: None,
1321 dynamic_type_info: None,
1322 runtime_state: None,
1323 stack_allocation: None,
1324 temporary_object: None,
1325 fragmentation_analysis: None,
1326 generic_instantiation: None,
1327 type_relationships: None,
1328 type_usage: None,
1329 function_call_tracking: None,
1330 lifecycle_tracking: None,
1331 access_tracking: None,
1332 drop_chain_analysis: None,
1333 };
1334
1335 let allocation_data = AllocationData::from(allocation_info);
1336
1337 assert_eq!(allocation_data.id, 0x2000);
1338 assert_eq!(allocation_data.size, 128);
1339 assert_eq!(allocation_data.type_name, "Unknown");
1340 assert_eq!(allocation_data.location, "Unknown");
1341 assert_eq!(allocation_data.timestamp, 1234567900);
1342 assert_eq!(allocation_data.status, "Freed");
1343 }
1344
1345 #[test]
1346 fn test_show_export_options() {
1347 show_export_options();
1349 }
1350
1351 #[test]
1352 fn test_provide_performance_feedback() {
1353 let config = BinaryExportConfig::default();
1354
1355 let excellent_time = std::time::Duration::from_millis(500);
1357 provide_performance_feedback(BinaryOutputFormat::Json, &config, excellent_time);
1358
1359 let good_time = std::time::Duration::from_millis(2000);
1361 provide_performance_feedback(BinaryOutputFormat::Html, &config, good_time);
1362
1363 let slow_time = std::time::Duration::from_millis(8000);
1365 provide_performance_feedback(BinaryOutputFormat::Both, &config, slow_time);
1366
1367 let slow_config = BinaryExportConfig::default()
1369 .parallel_processing(false)
1370 .batch_size(500);
1371 provide_performance_feedback(BinaryOutputFormat::Both, &slow_config, slow_time);
1372 }
1373
1374 #[test]
1375 fn test_convert_allocation_to_binary_data() {
1376 let allocation = crate::core::types::AllocationInfo {
1377 ptr: 0x1000,
1378 size: 64,
1379 var_name: Some("test_var".to_string()),
1380 type_name: Some("String".to_string()),
1381 scope_name: Some("main".to_string()),
1382 timestamp_alloc: 1234567890,
1383 timestamp_dealloc: None,
1384 thread_id: "main".to_string(),
1385 borrow_count: 2,
1386 stack_trace: None,
1387 is_leaked: false,
1388 lifetime_ms: Some(100),
1389 borrow_info: None,
1390 clone_info: None,
1391 ownership_history_available: false,
1392 smart_pointer_info: None,
1393 memory_layout: None,
1394 generic_info: None,
1395 dynamic_type_info: None,
1396 runtime_state: None,
1397 stack_allocation: None,
1398 temporary_object: None,
1399 fragmentation_analysis: None,
1400 generic_instantiation: None,
1401 type_relationships: None,
1402 type_usage: None,
1403 function_call_tracking: None,
1404 lifecycle_tracking: None,
1405 access_tracking: None,
1406 drop_chain_analysis: None,
1407 };
1408
1409 let result = convert_allocation_to_binary_data(&allocation, 0);
1410 assert!(result.is_ok());
1411
1412 let binary_data = result.unwrap();
1413 assert_eq!(binary_data.id, 0x1000);
1414 assert_eq!(binary_data.size, 64);
1415 assert_eq!(binary_data.type_name, "String");
1416 assert_eq!(binary_data.scope_name, "main");
1417 assert_eq!(binary_data.timestamp_alloc, 1234567890);
1418 assert!(binary_data.is_active);
1419 assert_eq!(binary_data.ptr, 0x1000);
1420 assert_eq!(binary_data.thread_id, "main");
1421 assert_eq!(binary_data.var_name, Some("test_var".to_string()));
1422 assert_eq!(binary_data.borrow_count, 2);
1423 assert!(!binary_data.is_leaked);
1424 assert_eq!(binary_data.lifetime_ms, Some(100));
1425 assert!(binary_data.optional_fields.is_empty());
1426 }
1427
1428 #[test]
1429 fn test_convert_allocation_to_binary_data_with_defaults() {
1430 let allocation = crate::core::types::AllocationInfo {
1431 ptr: 0x2000,
1432 size: 128,
1433 var_name: None,
1434 type_name: None, scope_name: None, timestamp_alloc: 1234567900,
1437 timestamp_dealloc: Some(1234567950),
1438 thread_id: "worker".to_string(),
1439 borrow_count: 0,
1440 stack_trace: None,
1441 is_leaked: true,
1442 lifetime_ms: None,
1443 borrow_info: None,
1444 clone_info: None,
1445 ownership_history_available: false,
1446 smart_pointer_info: None,
1447 memory_layout: None,
1448 generic_info: None,
1449 dynamic_type_info: None,
1450 runtime_state: None,
1451 stack_allocation: None,
1452 temporary_object: None,
1453 fragmentation_analysis: None,
1454 generic_instantiation: None,
1455 type_relationships: None,
1456 type_usage: None,
1457 function_call_tracking: None,
1458 lifecycle_tracking: None,
1459 access_tracking: None,
1460 drop_chain_analysis: None,
1461 };
1462
1463 let result = convert_allocation_to_binary_data(&allocation, 1);
1464 assert!(result.is_ok());
1465
1466 let binary_data = result.unwrap();
1467 assert_eq!(binary_data.id, 0x2000);
1468 assert_eq!(binary_data.size, 128);
1469 assert_eq!(binary_data.type_name, "unknown_type");
1470 assert_eq!(binary_data.scope_name, "global");
1471 assert_eq!(binary_data.timestamp_alloc, 1234567900);
1472 assert!(!binary_data.is_active); assert_eq!(binary_data.ptr, 0x2000);
1474 assert_eq!(binary_data.thread_id, "worker");
1475 assert_eq!(binary_data.var_name, None);
1476 assert_eq!(binary_data.borrow_count, 0);
1477 assert!(binary_data.is_leaked);
1478 assert_eq!(binary_data.lifetime_ms, None);
1479 }
1480}