1#[cfg(feature = "instrument")]
7use std::sync::{Mutex, OnceLock};
8
9pub struct InstrumentStats {
11 pub parse_expr_calls: u64,
14 pub parse_expr_bp_recursive_calls: u64,
16 pub parse_array_element_calls: u64,
18 pub parse_array_element_with_arrow: u64,
20 pub parse_expr_array_first: u64,
22 pub parse_expr_array_second: u64,
24 pub parse_array_count: u64,
26 pub parse_array_element_count: u64,
28 pub parse_atom_calls: u64,
30 pub parse_array_simple_values: u64,
32
33 pub parse_stmt_calls: u64,
36 pub parse_function_calls: u64,
38 pub parse_class_calls: u64,
40 pub parse_foreach_calls: u64,
42 pub parse_loop_calls: u64,
44 pub parse_if_calls: u64,
46 pub parse_switch_calls: u64,
48 pub parse_try_calls: u64,
50 pub parse_attribute_calls: u64,
52
53 pub arena_vec_allocations: u64,
56 pub arena_vec_bytes: u64,
58 pub arena_alloc_calls: u64,
60 pub arena_vec_reallocations: u64,
62 pub arena_vec_wasted_capacity: u64,
64 pub arena_vec_empty: u64,
66}
67
68#[cfg(feature = "instrument")]
69fn stats() -> &'static Mutex<InstrumentStats> {
70 static STATS: OnceLock<Mutex<InstrumentStats>> = OnceLock::new();
71 STATS.get_or_init(|| {
72 Mutex::new(InstrumentStats {
73 parse_expr_calls: 0,
74 parse_expr_bp_recursive_calls: 0,
75 parse_array_element_calls: 0,
76 parse_array_element_with_arrow: 0,
77 parse_expr_array_first: 0,
78 parse_expr_array_second: 0,
79 parse_array_count: 0,
80 parse_array_element_count: 0,
81 parse_atom_calls: 0,
82 parse_array_simple_values: 0,
83 parse_stmt_calls: 0,
84 parse_function_calls: 0,
85 parse_class_calls: 0,
86 parse_foreach_calls: 0,
87 parse_loop_calls: 0,
88 parse_if_calls: 0,
89 parse_switch_calls: 0,
90 parse_try_calls: 0,
91 parse_attribute_calls: 0,
92 arena_vec_allocations: 0,
93 arena_vec_bytes: 0,
94 arena_alloc_calls: 0,
95 arena_vec_reallocations: 0,
96 arena_vec_wasted_capacity: 0,
97 arena_vec_empty: 0,
98 })
99 })
100}
101
102#[inline]
104pub fn record_parse_expr() {
105 #[cfg(feature = "instrument")]
106 {
107 if let Ok(mut stats) = stats().lock() {
108 stats.parse_expr_calls += 1;
109 }
110 }
111}
112
113#[inline]
115pub fn record_parse_expr_bp_recursive() {
116 #[cfg(feature = "instrument")]
117 {
118 if let Ok(mut stats) = stats().lock() {
119 stats.parse_expr_bp_recursive_calls += 1;
120 }
121 }
122}
123
124#[inline]
126pub fn record_parse_array() {
127 #[cfg(feature = "instrument")]
128 {
129 if let Ok(mut stats) = stats().lock() {
130 stats.parse_array_count += 1;
131 }
132 }
133}
134
135#[inline]
137pub fn record_parse_array_element() {
138 #[cfg(feature = "instrument")]
139 {
140 if let Ok(mut stats) = stats().lock() {
141 stats.parse_array_element_calls += 1;
142 }
143 }
144}
145
146#[inline]
148pub fn record_parse_expr_array_first() {
149 #[cfg(feature = "instrument")]
150 {
151 if let Ok(mut stats) = stats().lock() {
152 stats.parse_expr_array_first += 1;
153 }
154 }
155}
156
157#[inline]
159pub fn record_parse_expr_array_second() {
160 #[cfg(feature = "instrument")]
161 {
162 if let Ok(mut stats) = stats().lock() {
163 stats.parse_expr_array_second += 1;
164 }
165 }
166}
167
168#[inline]
170pub fn record_parse_array_element_with_arrow() {
171 #[cfg(feature = "instrument")]
172 {
173 if let Ok(mut stats) = stats().lock() {
174 stats.parse_array_element_with_arrow += 1;
175 }
176 }
177}
178
179#[inline]
181pub fn record_parse_array_element_count(_count: usize) {
182 #[cfg(feature = "instrument")]
183 {
184 if let Ok(mut stats) = stats().lock() {
185 stats.parse_array_element_count += _count as u64;
186 }
187 }
188}
189
190#[inline]
192pub fn record_parse_atom() {
193 #[cfg(feature = "instrument")]
194 {
195 if let Ok(mut stats) = stats().lock() {
196 stats.parse_atom_calls += 1;
197 }
198 }
199}
200
201#[inline]
203pub fn record_parse_array_simple_value() {
204 #[cfg(feature = "instrument")]
205 {
206 if let Ok(mut stats) = stats().lock() {
207 stats.parse_array_simple_values += 1;
208 }
209 }
210}
211
212#[inline]
216pub fn record_parse_stmt() {
217 #[cfg(feature = "instrument")]
218 {
219 if let Ok(mut stats) = stats().lock() {
220 stats.parse_stmt_calls += 1;
221 }
222 }
223}
224
225#[inline]
227pub fn record_parse_function() {
228 #[cfg(feature = "instrument")]
229 {
230 if let Ok(mut stats) = stats().lock() {
231 stats.parse_function_calls += 1;
232 }
233 }
234}
235
236#[inline]
238pub fn record_parse_class() {
239 #[cfg(feature = "instrument")]
240 {
241 if let Ok(mut stats) = stats().lock() {
242 stats.parse_class_calls += 1;
243 }
244 }
245}
246
247#[inline]
249pub fn record_parse_foreach() {
250 #[cfg(feature = "instrument")]
251 {
252 if let Ok(mut stats) = stats().lock() {
253 stats.parse_foreach_calls += 1;
254 }
255 }
256}
257
258#[inline]
260pub fn record_parse_loop() {
261 #[cfg(feature = "instrument")]
262 {
263 if let Ok(mut stats) = stats().lock() {
264 stats.parse_loop_calls += 1;
265 }
266 }
267}
268
269#[inline]
271pub fn record_parse_if() {
272 #[cfg(feature = "instrument")]
273 {
274 if let Ok(mut stats) = stats().lock() {
275 stats.parse_if_calls += 1;
276 }
277 }
278}
279
280#[inline]
282pub fn record_parse_switch() {
283 #[cfg(feature = "instrument")]
284 {
285 if let Ok(mut stats) = stats().lock() {
286 stats.parse_switch_calls += 1;
287 }
288 }
289}
290
291#[inline]
293pub fn record_parse_try() {
294 #[cfg(feature = "instrument")]
295 {
296 if let Ok(mut stats) = stats().lock() {
297 stats.parse_try_calls += 1;
298 }
299 }
300}
301
302#[inline]
304pub fn record_parse_attribute() {
305 #[cfg(feature = "instrument")]
306 {
307 if let Ok(mut stats) = stats().lock() {
308 stats.parse_attribute_calls += 1;
309 }
310 }
311}
312
313#[cfg(feature = "instrument")]
317#[inline]
318pub fn record_arena_vec_allocation(_capacity: usize) {
319 #[cfg(feature = "instrument")]
320 {
321 if let Ok(mut stats) = stats().lock() {
322 stats.arena_vec_allocations += 1;
323 stats.arena_vec_bytes += _capacity as u64;
324 }
325 }
326}
327
328#[cfg(feature = "instrument")]
330#[inline]
331pub fn record_arena_alloc() {
332 #[cfg(feature = "instrument")]
333 {
334 if let Ok(mut stats) = stats().lock() {
335 stats.arena_alloc_calls += 1;
336 }
337 }
338}
339
340#[cfg(feature = "instrument")]
342#[inline]
343pub fn record_arena_vec_waste(_wasted_bytes: usize) {
344 #[cfg(feature = "instrument")]
345 {
346 if let Ok(mut stats) = stats().lock() {
347 stats.arena_vec_wasted_capacity += _wasted_bytes as u64;
348 }
349 }
350}
351
352#[cfg(feature = "instrument")]
354#[inline]
355pub fn record_arena_vec_empty() {
356 #[cfg(feature = "instrument")]
357 {
358 if let Ok(mut stats) = stats().lock() {
359 stats.arena_vec_empty += 1;
360 }
361 }
362}
363
364pub fn get_stats() -> InstrumentStats {
366 #[cfg(feature = "instrument")]
367 {
368 stats()
369 .lock()
370 .ok()
371 .map(|stats| InstrumentStats {
372 parse_expr_calls: stats.parse_expr_calls,
373 parse_expr_bp_recursive_calls: stats.parse_expr_bp_recursive_calls,
374 parse_array_element_calls: stats.parse_array_element_calls,
375 parse_array_element_with_arrow: stats.parse_array_element_with_arrow,
376 parse_expr_array_first: stats.parse_expr_array_first,
377 parse_expr_array_second: stats.parse_expr_array_second,
378 parse_array_count: stats.parse_array_count,
379 parse_array_element_count: stats.parse_array_element_count,
380 parse_atom_calls: stats.parse_atom_calls,
381 parse_array_simple_values: stats.parse_array_simple_values,
382 parse_stmt_calls: stats.parse_stmt_calls,
383 parse_function_calls: stats.parse_function_calls,
384 parse_class_calls: stats.parse_class_calls,
385 parse_foreach_calls: stats.parse_foreach_calls,
386 parse_loop_calls: stats.parse_loop_calls,
387 parse_if_calls: stats.parse_if_calls,
388 parse_switch_calls: stats.parse_switch_calls,
389 parse_try_calls: stats.parse_try_calls,
390 parse_attribute_calls: stats.parse_attribute_calls,
391 arena_vec_allocations: stats.arena_vec_allocations,
392 arena_vec_bytes: stats.arena_vec_bytes,
393 arena_alloc_calls: stats.arena_alloc_calls,
394 arena_vec_reallocations: stats.arena_vec_reallocations,
395 arena_vec_wasted_capacity: stats.arena_vec_wasted_capacity,
396 arena_vec_empty: stats.arena_vec_empty,
397 })
398 .unwrap_or(InstrumentStats {
399 parse_expr_calls: 0,
400 parse_expr_bp_recursive_calls: 0,
401 parse_array_element_calls: 0,
402 parse_array_element_with_arrow: 0,
403 parse_expr_array_first: 0,
404 parse_expr_array_second: 0,
405 parse_array_count: 0,
406 parse_array_element_count: 0,
407 parse_atom_calls: 0,
408 parse_array_simple_values: 0,
409 parse_stmt_calls: 0,
410 parse_function_calls: 0,
411 parse_class_calls: 0,
412 parse_foreach_calls: 0,
413 parse_loop_calls: 0,
414 parse_if_calls: 0,
415 parse_switch_calls: 0,
416 parse_try_calls: 0,
417 parse_attribute_calls: 0,
418 arena_vec_allocations: 0,
419 arena_vec_bytes: 0,
420 arena_alloc_calls: 0,
421 arena_vec_reallocations: 0,
422 arena_vec_wasted_capacity: 0,
423 arena_vec_empty: 0,
424 })
425 }
426 #[cfg(not(feature = "instrument"))]
427 {
428 InstrumentStats {
429 parse_expr_calls: 0,
430 parse_expr_bp_recursive_calls: 0,
431 parse_array_element_calls: 0,
432 parse_array_element_with_arrow: 0,
433 parse_expr_array_first: 0,
434 parse_expr_array_second: 0,
435 parse_array_count: 0,
436 parse_array_element_count: 0,
437 parse_atom_calls: 0,
438 parse_array_simple_values: 0,
439 parse_stmt_calls: 0,
440 parse_function_calls: 0,
441 parse_class_calls: 0,
442 parse_foreach_calls: 0,
443 parse_loop_calls: 0,
444 parse_if_calls: 0,
445 parse_switch_calls: 0,
446 parse_try_calls: 0,
447 parse_attribute_calls: 0,
448 arena_vec_allocations: 0,
449 arena_vec_bytes: 0,
450 arena_alloc_calls: 0,
451 arena_vec_reallocations: 0,
452 arena_vec_wasted_capacity: 0,
453 arena_vec_empty: 0,
454 }
455 }
456}
457
458pub fn report_stats() {
460 #[cfg(feature = "instrument")]
461 {
462 let stats = get_stats();
463 println!("\n╔════════════════════════════════════════════════════════════╗");
464 println!("║ Parser Instrumentation Report (Full Analysis) ║");
465 println!("╠════════════════════════════════════════════════════════════╣");
466
467 println!("║ STATEMENT PARSING: ║");
468 println!(
469 "║ Total statements: {:18} ║",
470 stats.parse_stmt_calls
471 );
472 println!(
473 "║ - Functions: {:18} ║",
474 stats.parse_function_calls
475 );
476 println!(
477 "║ - Classes/Traits: {:18} ║",
478 stats.parse_class_calls
479 );
480 println!(
481 "║ - If statements: {:18} ║",
482 stats.parse_if_calls
483 );
484 println!(
485 "║ - Loops (for/while/do): {:18} ║",
486 stats.parse_loop_calls
487 );
488 println!(
489 "║ - Foreach: {:18} ║",
490 stats.parse_foreach_calls
491 );
492 println!(
493 "║ - Switch: {:18} ║",
494 stats.parse_switch_calls
495 );
496 println!(
497 "║ - Try/Catch: {:18} ║",
498 stats.parse_try_calls
499 );
500 println!(
501 "║ - Attributes: {:18} ║",
502 stats.parse_attribute_calls
503 );
504
505 println!("╠════════════════════════════════════════════════════════════╣");
506 println!("║ ARRAY & EXPRESSION PARSING: ║");
507 println!(
508 "║ Arrays Parsed: {:18} ║",
509 stats.parse_array_count
510 );
511 println!(
512 "║ Array Elements: {:18} ║",
513 stats.parse_array_element_count
514 );
515 println!(
516 "║ Array Elements with =>: {:18} ║",
517 stats.parse_array_element_with_arrow
518 );
519
520 let arrow_rate = if stats.parse_array_element_calls > 0 {
521 (stats.parse_array_element_with_arrow as f64 / stats.parse_array_element_calls as f64)
522 * 100.0
523 } else {
524 0.0
525 };
526 println!(
527 "║ => Rate: {:15.1}% ║",
528 arrow_rate
529 );
530
531 println!("╠════════════════════════════════════════════════════════════╣");
532 println!(
533 "║ Total parse_expr calls: {:18} ║",
534 stats.parse_expr_calls
535 );
536 println!(
537 "║ parse_expr calls (array, first): {:18} ║",
538 stats.parse_expr_array_first
539 );
540 println!(
541 "║ parse_expr calls (array, second =>): {:18} ║",
542 stats.parse_expr_array_second
543 );
544
545 let second_expr_overhead = stats.parse_expr_array_second;
546 let second_expr_pct = if stats.parse_expr_calls > 0 {
547 (second_expr_overhead as f64 / stats.parse_expr_calls as f64) * 100.0
548 } else {
549 0.0
550 };
551 println!(
552 "║ Double-parse overhead (%): {:15.1}% ║",
553 second_expr_pct
554 );
555
556 println!("╠════════════════════════════════════════════════════════════╣");
557 println!(
558 "║ parse_atom calls: {:18} ║",
559 stats.parse_atom_calls
560 );
561 println!(
562 "║ Simple array values (no operators): {:18} ║",
563 stats.parse_array_simple_values
564 );
565
566 let simple_pct = if stats.parse_array_element_count > 0 {
567 (stats.parse_array_simple_values as f64 / stats.parse_array_element_count as f64)
568 * 100.0
569 } else {
570 0.0
571 };
572 println!(
573 "║ Simple value rate: {:15.1}% ║",
574 simple_pct
575 );
576
577 println!("╚════════════════════════════════════════════════════════════╝\n");
578 }
579 #[cfg(not(feature = "instrument"))]
580 {
581 eprintln!("⚠️ Instrumentation not enabled. Compile with `--features instrument`");
582 }
583}
584
585pub fn reset_stats() {
587 #[cfg(feature = "instrument")]
588 {
589 if let Ok(mut stats) = stats().lock() {
590 stats.parse_expr_calls = 0;
591 stats.parse_expr_bp_recursive_calls = 0;
592 stats.parse_array_element_calls = 0;
593 stats.parse_array_element_with_arrow = 0;
594 stats.parse_expr_array_first = 0;
595 stats.parse_expr_array_second = 0;
596 stats.parse_array_count = 0;
597 stats.parse_array_element_count = 0;
598 stats.parse_atom_calls = 0;
599 stats.parse_array_simple_values = 0;
600 stats.parse_stmt_calls = 0;
601 stats.parse_function_calls = 0;
602 stats.parse_class_calls = 0;
603 stats.parse_foreach_calls = 0;
604 stats.parse_loop_calls = 0;
605 stats.parse_if_calls = 0;
606 stats.parse_switch_calls = 0;
607 stats.parse_try_calls = 0;
608 stats.parse_attribute_calls = 0;
609 stats.arena_vec_allocations = 0;
610 stats.arena_vec_bytes = 0;
611 stats.arena_alloc_calls = 0;
612 stats.arena_vec_reallocations = 0;
613 stats.arena_vec_wasted_capacity = 0;
614 stats.arena_vec_empty = 0;
615 }
616 }
617}