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#[inline]
317pub fn record_arena_vec_allocation(_capacity: usize) {
318 #[cfg(feature = "instrument")]
319 {
320 if let Ok(mut stats) = stats().lock() {
321 stats.arena_vec_allocations += 1;
322 stats.arena_vec_bytes += _capacity as u64;
323 }
324 }
325}
326
327#[inline]
329pub fn record_arena_alloc() {
330 #[cfg(feature = "instrument")]
331 {
332 if let Ok(mut stats) = stats().lock() {
333 stats.arena_alloc_calls += 1;
334 }
335 }
336}
337
338#[inline]
340pub fn record_arena_vec_waste(_wasted_bytes: usize) {
341 #[cfg(feature = "instrument")]
342 {
343 if let Ok(mut stats) = stats().lock() {
344 stats.arena_vec_wasted_capacity += _wasted_bytes as u64;
345 }
346 }
347}
348
349#[inline]
351pub fn record_arena_vec_empty() {
352 #[cfg(feature = "instrument")]
353 {
354 if let Ok(mut stats) = stats().lock() {
355 stats.arena_vec_empty += 1;
356 }
357 }
358}
359
360pub fn get_stats() -> InstrumentStats {
362 #[cfg(feature = "instrument")]
363 {
364 stats()
365 .lock()
366 .ok()
367 .map(|stats| InstrumentStats {
368 parse_expr_calls: stats.parse_expr_calls,
369 parse_expr_bp_recursive_calls: stats.parse_expr_bp_recursive_calls,
370 parse_array_element_calls: stats.parse_array_element_calls,
371 parse_array_element_with_arrow: stats.parse_array_element_with_arrow,
372 parse_expr_array_first: stats.parse_expr_array_first,
373 parse_expr_array_second: stats.parse_expr_array_second,
374 parse_array_count: stats.parse_array_count,
375 parse_array_element_count: stats.parse_array_element_count,
376 parse_atom_calls: stats.parse_atom_calls,
377 parse_array_simple_values: stats.parse_array_simple_values,
378 parse_stmt_calls: stats.parse_stmt_calls,
379 parse_function_calls: stats.parse_function_calls,
380 parse_class_calls: stats.parse_class_calls,
381 parse_foreach_calls: stats.parse_foreach_calls,
382 parse_loop_calls: stats.parse_loop_calls,
383 parse_if_calls: stats.parse_if_calls,
384 parse_switch_calls: stats.parse_switch_calls,
385 parse_try_calls: stats.parse_try_calls,
386 parse_attribute_calls: stats.parse_attribute_calls,
387 arena_vec_allocations: stats.arena_vec_allocations,
388 arena_vec_bytes: stats.arena_vec_bytes,
389 arena_alloc_calls: stats.arena_alloc_calls,
390 arena_vec_reallocations: stats.arena_vec_reallocations,
391 arena_vec_wasted_capacity: stats.arena_vec_wasted_capacity,
392 arena_vec_empty: stats.arena_vec_empty,
393 })
394 .unwrap_or(InstrumentStats {
395 parse_expr_calls: 0,
396 parse_expr_bp_recursive_calls: 0,
397 parse_array_element_calls: 0,
398 parse_array_element_with_arrow: 0,
399 parse_expr_array_first: 0,
400 parse_expr_array_second: 0,
401 parse_array_count: 0,
402 parse_array_element_count: 0,
403 parse_atom_calls: 0,
404 parse_array_simple_values: 0,
405 parse_stmt_calls: 0,
406 parse_function_calls: 0,
407 parse_class_calls: 0,
408 parse_foreach_calls: 0,
409 parse_loop_calls: 0,
410 parse_if_calls: 0,
411 parse_switch_calls: 0,
412 parse_try_calls: 0,
413 parse_attribute_calls: 0,
414 arena_vec_allocations: 0,
415 arena_vec_bytes: 0,
416 arena_alloc_calls: 0,
417 arena_vec_reallocations: 0,
418 arena_vec_wasted_capacity: 0,
419 arena_vec_empty: 0,
420 })
421 }
422 #[cfg(not(feature = "instrument"))]
423 {
424 InstrumentStats {
425 parse_expr_calls: 0,
426 parse_expr_bp_recursive_calls: 0,
427 parse_array_element_calls: 0,
428 parse_array_element_with_arrow: 0,
429 parse_expr_array_first: 0,
430 parse_expr_array_second: 0,
431 parse_array_count: 0,
432 parse_array_element_count: 0,
433 parse_atom_calls: 0,
434 parse_array_simple_values: 0,
435 parse_stmt_calls: 0,
436 parse_function_calls: 0,
437 parse_class_calls: 0,
438 parse_foreach_calls: 0,
439 parse_loop_calls: 0,
440 parse_if_calls: 0,
441 parse_switch_calls: 0,
442 parse_try_calls: 0,
443 parse_attribute_calls: 0,
444 arena_vec_allocations: 0,
445 arena_vec_bytes: 0,
446 arena_alloc_calls: 0,
447 arena_vec_reallocations: 0,
448 arena_vec_wasted_capacity: 0,
449 arena_vec_empty: 0,
450 }
451 }
452}
453
454pub fn report_stats() {
456 #[cfg(feature = "instrument")]
457 {
458 let stats = get_stats();
459 println!("\n╔════════════════════════════════════════════════════════════╗");
460 println!("║ Parser Instrumentation Report (Full Analysis) ║");
461 println!("╠════════════════════════════════════════════════════════════╣");
462
463 println!("║ STATEMENT PARSING: ║");
464 println!(
465 "║ Total statements: {:18} ║",
466 stats.parse_stmt_calls
467 );
468 println!(
469 "║ - Functions: {:18} ║",
470 stats.parse_function_calls
471 );
472 println!(
473 "║ - Classes/Traits: {:18} ║",
474 stats.parse_class_calls
475 );
476 println!(
477 "║ - If statements: {:18} ║",
478 stats.parse_if_calls
479 );
480 println!(
481 "║ - Loops (for/while/do): {:18} ║",
482 stats.parse_loop_calls
483 );
484 println!(
485 "║ - Foreach: {:18} ║",
486 stats.parse_foreach_calls
487 );
488 println!(
489 "║ - Switch: {:18} ║",
490 stats.parse_switch_calls
491 );
492 println!(
493 "║ - Try/Catch: {:18} ║",
494 stats.parse_try_calls
495 );
496 println!(
497 "║ - Attributes: {:18} ║",
498 stats.parse_attribute_calls
499 );
500
501 println!("╠════════════════════════════════════════════════════════════╣");
502 println!("║ ARRAY & EXPRESSION PARSING: ║");
503 println!(
504 "║ Arrays Parsed: {:18} ║",
505 stats.parse_array_count
506 );
507 println!(
508 "║ Array Elements: {:18} ║",
509 stats.parse_array_element_count
510 );
511 println!(
512 "║ Array Elements with =>: {:18} ║",
513 stats.parse_array_element_with_arrow
514 );
515
516 let arrow_rate = if stats.parse_array_element_calls > 0 {
517 (stats.parse_array_element_with_arrow as f64 / stats.parse_array_element_calls as f64)
518 * 100.0
519 } else {
520 0.0
521 };
522 println!(
523 "║ => Rate: {:15.1}% ║",
524 arrow_rate
525 );
526
527 println!("╠════════════════════════════════════════════════════════════╣");
528 println!(
529 "║ Total parse_expr calls: {:18} ║",
530 stats.parse_expr_calls
531 );
532 println!(
533 "║ parse_expr calls (array, first): {:18} ║",
534 stats.parse_expr_array_first
535 );
536 println!(
537 "║ parse_expr calls (array, second =>): {:18} ║",
538 stats.parse_expr_array_second
539 );
540
541 let second_expr_overhead = stats.parse_expr_array_second;
542 let second_expr_pct = if stats.parse_expr_calls > 0 {
543 (second_expr_overhead as f64 / stats.parse_expr_calls as f64) * 100.0
544 } else {
545 0.0
546 };
547 println!(
548 "║ Double-parse overhead (%): {:15.1}% ║",
549 second_expr_pct
550 );
551
552 println!("╠════════════════════════════════════════════════════════════╣");
553 println!(
554 "║ parse_atom calls: {:18} ║",
555 stats.parse_atom_calls
556 );
557 println!(
558 "║ Simple array values (no operators): {:18} ║",
559 stats.parse_array_simple_values
560 );
561
562 let simple_pct = if stats.parse_array_element_count > 0 {
563 (stats.parse_array_simple_values as f64 / stats.parse_array_element_count as f64)
564 * 100.0
565 } else {
566 0.0
567 };
568 println!(
569 "║ Simple value rate: {:15.1}% ║",
570 simple_pct
571 );
572
573 println!("╚════════════════════════════════════════════════════════════╝\n");
574 }
575 #[cfg(not(feature = "instrument"))]
576 {
577 eprintln!("⚠️ Instrumentation not enabled. Compile with `--features instrument`");
578 }
579}
580
581pub fn reset_stats() {
583 #[cfg(feature = "instrument")]
584 {
585 if let Ok(mut stats) = stats().lock() {
586 stats.parse_expr_calls = 0;
587 stats.parse_expr_bp_recursive_calls = 0;
588 stats.parse_array_element_calls = 0;
589 stats.parse_array_element_with_arrow = 0;
590 stats.parse_expr_array_first = 0;
591 stats.parse_expr_array_second = 0;
592 stats.parse_array_count = 0;
593 stats.parse_array_element_count = 0;
594 stats.parse_atom_calls = 0;
595 stats.parse_array_simple_values = 0;
596 stats.parse_stmt_calls = 0;
597 stats.parse_function_calls = 0;
598 stats.parse_class_calls = 0;
599 stats.parse_foreach_calls = 0;
600 stats.parse_loop_calls = 0;
601 stats.parse_if_calls = 0;
602 stats.parse_switch_calls = 0;
603 stats.parse_try_calls = 0;
604 stats.parse_attribute_calls = 0;
605 stats.arena_vec_allocations = 0;
606 stats.arena_vec_bytes = 0;
607 stats.arena_alloc_calls = 0;
608 stats.arena_vec_reallocations = 0;
609 stats.arena_vec_wasted_capacity = 0;
610 stats.arena_vec_empty = 0;
611 }
612 }
613}