1use std::cell::Cell;
66use std::cell::RefCell;
67use std::fmt;
68use std::fmt::Debug;
69use std::fmt::Display;
70use std::sync::atomic::AtomicUsize;
71use std::sync::atomic::Ordering;
72
73use vortex_error::VortexExpect;
74use vortex_error::VortexResult;
75use vortex_error::vortex_err;
76
77use crate::ArrayRef;
78
79#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
81pub enum TraceResolution {
82 #[default]
84 ExecutedOnly,
85 Attempts,
87}
88
89#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
91pub struct TraceOptions {
92 pub resolution: TraceResolution,
94}
95
96#[derive(Clone, Debug)]
98pub struct Traced<T> {
99 pub output: T,
101 pub trace: TraceDisplay,
103}
104
105#[derive(Clone, Debug, Default)]
107pub struct TraceDisplay {
108 options: TraceOptions,
109 events: Vec<TraceEvent>,
110}
111
112impl Display for TraceDisplay {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 let hidden_events = self.hidden_events();
115 let mut optimize_depth = 0usize;
116 let mut wrote_event = false;
117
118 for (idx, event) in self.events.iter().enumerate() {
119 if hidden_events[idx] {
120 continue;
121 }
122
123 if event.closes_before(self.options.resolution) {
124 optimize_depth = optimize_depth.saturating_sub(1);
125 }
126
127 if event.is_hidden(self.options.resolution) {
128 continue;
129 }
130
131 if wrote_event {
132 writeln!(f)?;
133 } else {
134 wrote_event = true;
135 }
136
137 write_indent(
138 f,
139 optimize_depth + event.relative_indent(self.options.resolution, optimize_depth > 0),
140 )?;
141 event.fmt_line(f, self.options.resolution)?;
142
143 if event.opens_after(self.options.resolution) {
144 optimize_depth += 1;
145 }
146 if event.closes_after(self.options.resolution) {
147 optimize_depth = optimize_depth.saturating_sub(1);
148 }
149 }
150 Ok(())
151 }
152}
153
154impl TraceDisplay {
155 fn hidden_events(&self) -> Vec<bool> {
156 let mut hidden = vec![false; self.events.len()];
157 if self.options.resolution != TraceResolution::ExecutedOnly {
158 return hidden;
159 }
160
161 let mut optimize_stack = Vec::new();
162 for (idx, event) in self.events.iter().enumerate() {
163 match event {
164 TraceEvent::OptimizeStart { .. } => optimize_stack.push(idx),
165 TraceEvent::OptimizeDone { changed, .. } => {
166 let Some(start) = optimize_stack.pop() else {
167 continue;
168 };
169 if !changed {
170 hidden[start..=idx].fill(true);
171 }
172 }
173 _ => {}
174 }
175 }
176 hidden
177 }
178}
179
180fn write_indent(f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
181 for _ in 0..depth {
182 f.write_str(" ")?;
183 }
184 Ok(())
185}
186
187pub fn trace_op<T>(f: impl FnOnce() -> VortexResult<T>) -> VortexResult<Traced<T>> {
219 trace_op_with(TraceOptions::default(), f)
220}
221
222pub fn trace_op_with<T>(
230 options: TraceOptions,
231 f: impl FnOnce() -> VortexResult<T>,
232) -> VortexResult<Traced<T>> {
233 let interest = TraceInterest::from(options.resolution);
234 ACTIVE_TRACE.with(|active| {
235 let mut active = active.borrow_mut();
236 if active.is_some() {
237 return Err(vortex_err!("trace_op captures cannot be nested"));
238 }
239 *active = Some(TraceRecorder::new(options));
240 Ok(())
241 })?;
242 TRACE_INTEREST.with(|trace_interest| trace_interest.set(interest));
243 ACTIVE_TRACE_COUNT.fetch_add(1, Ordering::Relaxed);
244 if interest == TraceInterest::Attempts {
245 ATTEMPTS_TRACE_COUNT.fetch_add(1, Ordering::Relaxed);
246 }
247
248 let guard = ActiveTraceGuard { interest };
249 let output = f();
250 let recorder = ACTIVE_TRACE.with(|active| {
251 active
252 .borrow_mut()
253 .take()
254 .vortex_expect("trace recorder must be installed")
255 });
256 drop(guard);
257
258 output.map(|output| Traced {
259 output,
260 trace: recorder.finish(),
261 })
262}
263
264#[inline(always)]
266pub(crate) fn is_active() -> bool {
267 if ACTIVE_TRACE_COUNT.load(Ordering::Relaxed) == 0 {
268 return false;
269 }
270 TRACE_INTEREST.with(|interest| interest.get().is_active())
271}
272
273#[inline(always)]
274fn attempts_enabled() -> bool {
275 if ATTEMPTS_TRACE_COUNT.load(Ordering::Relaxed) == 0 {
276 return false;
277 }
278 TRACE_INTEREST.with(|interest| interest.get() == TraceInterest::Attempts)
279}
280
281#[derive(Clone, Copy, Debug)]
282enum TraceSource {
283 Static,
284 Session(usize),
285}
286
287impl Display for TraceSource {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 match self {
290 TraceSource::Static => f.write_str("static"),
291 TraceSource::Session(idx) => write!(f, "session[{idx}]"),
292 }
293 }
294}
295
296#[derive(Clone, Copy, Debug)]
297enum AttemptOutcome {
298 Declined,
299 NoMatch,
300}
301
302impl Display for AttemptOutcome {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 match self {
305 AttemptOutcome::Declined => f.write_str("declined"),
306 AttemptOutcome::NoMatch => f.write_str("no-match"),
307 }
308 }
309}
310
311#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
312enum TraceInterest {
313 #[default]
314 Off,
315 ExecutedOnly,
316 Attempts,
317}
318
319impl TraceInterest {
320 #[inline]
321 fn is_active(self) -> bool {
322 self != Self::Off
323 }
324}
325
326impl From<TraceResolution> for TraceInterest {
327 fn from(resolution: TraceResolution) -> Self {
328 match resolution {
329 TraceResolution::ExecutedOnly => Self::ExecutedOnly,
330 TraceResolution::Attempts => Self::Attempts,
331 }
332 }
333}
334
335#[derive(Clone, Debug)]
342pub(crate) struct ArraySummary(ArrayRef);
343
344impl ArraySummary {
345 pub(crate) fn new(array: &ArrayRef) -> Self {
346 Self(array.clone())
347 }
348}
349
350impl Display for ArraySummary {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 Display::fmt(&self.0, f)
353 }
354}
355
356pub(crate) fn record_optimize_start(root: &ArrayRef, session: bool) {
357 record(TraceEvent::OptimizeStart {
358 root: ArraySummary::new(root),
359 session,
360 });
361}
362
363pub(crate) fn record_optimize_loop_start(array: &ArrayRef) {
364 if !attempts_enabled() {
365 return;
366 }
367 record(TraceEvent::OptimizeLoopStart {
368 array: ArraySummary::new(array),
369 });
370}
371
372pub(crate) fn record_optimize_loop_end() {
373 if !attempts_enabled() {
374 return;
375 }
376 record(TraceEvent::OptimizeLoopEnd);
377}
378
379pub(crate) fn record_optimize_reduce_none(array: &ArrayRef) {
380 if !attempts_enabled() {
381 return;
382 }
383 record(TraceEvent::PhaseNone {
384 indent: 0,
385 phase: "reduce",
386 subject: "array",
387 array: ArraySummary::new(array),
388 });
389}
390
391pub(crate) fn record_optimize_parent_reduce_none(array: &ArrayRef) {
392 if !attempts_enabled() {
393 return;
394 }
395 record(TraceEvent::PhaseNone {
396 indent: 0,
397 phase: "reduce_parent",
398 subject: "array",
399 array: ArraySummary::new(array),
400 });
401}
402
403pub(crate) fn record_optimize_done(output: &ArrayRef, changed: bool) {
404 record(TraceEvent::OptimizeDone {
405 output: ArraySummary::new(output),
406 changed,
407 });
408}
409
410pub(crate) fn record_optimize_recursive_start(root: &ArrayRef) {
411 record(TraceEvent::OptimizeRecursiveStart {
412 root: ArraySummary::new(root),
413 });
414}
415
416pub(crate) fn record_optimize_recursive_slot(slot_idx: usize, input: &ArrayRef, output: &ArrayRef) {
417 record(TraceEvent::OptimizeRecursiveSlot {
418 slot_idx,
419 input: ArraySummary::new(input),
420 output: ArraySummary::new(output),
421 });
422}
423
424pub(crate) fn record_reduce_applied(array: &ArrayRef, rule: &dyn Debug, output: &ArrayRef) {
425 record(TraceEvent::ReduceApplied {
426 array: ArraySummary::new(array),
427 rule: compact_label(rule),
428 output: ArraySummary::new(output),
429 });
430}
431
432pub(crate) fn record_reduce_declined(array: &ArrayRef, rule: &dyn Debug) {
433 if !attempts_enabled() {
434 return;
435 }
436 record(TraceEvent::ReduceAttempt {
437 array: ArraySummary::new(array),
438 rule: compact_label(rule),
439 outcome: AttemptOutcome::Declined,
440 });
441}
442
443pub(crate) fn record_session_parent_reduce_applied(
444 parent: &ArrayRef,
445 child: &ArrayRef,
446 slot_idx: usize,
447 plugin_idx: usize,
448 output: &ArrayRef,
449) {
450 record_parent_reduce_applied(
451 parent,
452 child,
453 slot_idx,
454 TraceSource::Session(plugin_idx),
455 "reduce_parent_fn",
456 output,
457 );
458}
459
460pub(crate) fn record_session_parent_reduce_declined(
461 parent: &ArrayRef,
462 child: &ArrayRef,
463 slot_idx: usize,
464 plugin_idx: usize,
465) {
466 record_parent_reduce_attempt(
467 parent,
468 child,
469 slot_idx,
470 TraceSource::Session(plugin_idx),
471 "reduce_parent_fn",
472 AttemptOutcome::Declined,
473 );
474}
475
476pub(crate) fn record_static_parent_reduce_no_match(
477 parent: &ArrayRef,
478 child: &ArrayRef,
479 slot_idx: usize,
480 rule: &dyn Debug,
481) {
482 record_parent_reduce_attempt(
483 parent,
484 child,
485 slot_idx,
486 TraceSource::Static,
487 compact_label(rule),
488 AttemptOutcome::NoMatch,
489 );
490}
491
492pub(crate) fn record_static_parent_reduce_applied(
493 parent: &ArrayRef,
494 child: &ArrayRef,
495 slot_idx: usize,
496 rule: &dyn Debug,
497 output: &ArrayRef,
498) {
499 record_parent_reduce_applied(
500 parent,
501 child,
502 slot_idx,
503 TraceSource::Static,
504 compact_label(rule),
505 output,
506 );
507}
508
509pub(crate) fn record_static_parent_reduce_declined(
510 parent: &ArrayRef,
511 child: &ArrayRef,
512 slot_idx: usize,
513 rule: &dyn Debug,
514) {
515 record_parent_reduce_attempt(
516 parent,
517 child,
518 slot_idx,
519 TraceSource::Static,
520 compact_label(rule),
521 AttemptOutcome::Declined,
522 );
523}
524
525fn record_parent_reduce_attempt(
526 parent: &ArrayRef,
527 child: &ArrayRef,
528 slot_idx: usize,
529 source: TraceSource,
530 rule: impl Into<String>,
531 outcome: AttemptOutcome,
532) {
533 if !attempts_enabled() {
534 return;
535 }
536 record(TraceEvent::ParentReduceAttempt {
537 parent: ArraySummary::new(parent),
538 child: ArraySummary::new(child),
539 slot_idx,
540 source,
541 rule: rule.into(),
542 outcome,
543 });
544}
545
546fn record_parent_reduce_applied(
547 parent: &ArrayRef,
548 child: &ArrayRef,
549 slot_idx: usize,
550 source: TraceSource,
551 rule: impl Into<String>,
552 output: &ArrayRef,
553) {
554 record(TraceEvent::ParentReduceApplied {
555 parent: ArraySummary::new(parent),
556 child: ArraySummary::new(child),
557 slot_idx,
558 source,
559 rule: rule.into(),
560 output: ArraySummary::new(output),
561 });
562}
563
564pub(crate) fn record_execute_until_start<M>(root: &ArrayRef) {
565 record(TraceEvent::ExecuteUntilStart {
566 target: short_type_name::<M>(),
567 root: ArraySummary::new(root),
568 });
569}
570
571pub(crate) fn record_execute_until_iteration(
572 iteration: usize,
573 current: &ArrayRef,
574 stack_parent: Option<(&ArrayRef, usize)>,
575 builder_active: bool,
576) {
577 record(TraceEvent::ExecuteUntilIteration {
578 iteration,
579 current: ArraySummary::new(current),
580 stack_parent: stack_parent.map(|(array, slot_idx)| (ArraySummary::new(array), slot_idx)),
581 builder_active,
582 });
583}
584
585pub(crate) fn record_execute_until_done_check(target: bool, canonical: bool) {
586 if !attempts_enabled() {
587 return;
588 }
589 record(TraceEvent::ExecuteUntilDoneCheck { target, canonical });
590}
591
592pub(crate) fn record_execute_until_return(output: &ArrayRef) {
593 record(TraceEvent::ExecuteUntilReturn {
594 output: ArraySummary::new(output),
595 });
596}
597
598pub(crate) fn record_execute_until_pop_frame(slot_idx: usize, output: &ArrayRef) {
599 record(TraceEvent::ExecuteUntilPopFrame {
600 slot_idx,
601 output: ArraySummary::new(output),
602 });
603}
604
605pub(crate) fn record_session_execute_parent_applied(
606 phase: &'static str,
607 parent: &ArrayRef,
608 child: &ArrayRef,
609 slot_idx: usize,
610 plugin_idx: usize,
611 output: &ArrayRef,
612) {
613 record_execute_parent_applied(
614 phase,
615 parent,
616 child,
617 slot_idx,
618 TraceSource::Session(plugin_idx),
619 "execute_parent_fn",
620 output,
621 );
622}
623
624pub(crate) fn record_session_execute_parent_declined(
625 phase: &'static str,
626 parent: &ArrayRef,
627 child: &ArrayRef,
628 slot_idx: usize,
629 plugin_idx: usize,
630) {
631 record_execute_parent_attempt(
632 phase,
633 parent,
634 child,
635 slot_idx,
636 TraceSource::Session(plugin_idx),
637 "execute_parent_fn",
638 AttemptOutcome::Declined,
639 );
640}
641
642fn record_execute_parent_attempt(
643 phase: &'static str,
644 parent: &ArrayRef,
645 child: &ArrayRef,
646 slot_idx: usize,
647 source: TraceSource,
648 kernel: impl Into<String>,
649 outcome: AttemptOutcome,
650) {
651 if !attempts_enabled() {
652 return;
653 }
654 record(TraceEvent::ExecuteParentAttempt {
655 phase,
656 parent: ArraySummary::new(parent),
657 child: ArraySummary::new(child),
658 slot_idx,
659 source,
660 kernel: kernel.into(),
661 outcome,
662 });
663}
664
665fn record_execute_parent_applied(
666 phase: &'static str,
667 parent: &ArrayRef,
668 child: &ArrayRef,
669 slot_idx: usize,
670 source: TraceSource,
671 kernel: impl Into<String>,
672 output: &ArrayRef,
673) {
674 record(TraceEvent::ExecuteParentApplied {
675 phase,
676 parent: ArraySummary::new(parent),
677 child: ArraySummary::new(child),
678 slot_idx,
679 source,
680 kernel: kernel.into(),
681 output: ArraySummary::new(output),
682 });
683}
684
685pub(crate) fn record_execute_parent_none(phase: &'static str, current: &ArrayRef) {
686 if !attempts_enabled() {
687 return;
688 }
689 record(TraceEvent::PhaseNone {
690 indent: 2,
691 phase,
692 subject: "current",
693 array: ArraySummary::new(current),
694 });
695}
696
697pub(crate) fn record_execute_optimized(input: &ArrayRef, output: &ArrayRef) {
698 let changed = !ArrayRef::ptr_eq(input, output);
699 if !changed && !attempts_enabled() {
700 return;
701 }
702 record(TraceEvent::ExecuteOptimized {
703 input: ArraySummary::new(input),
704 output: ArraySummary::new(output),
705 changed,
706 });
707}
708
709pub(crate) fn record_execute_encoding(array: &ArrayRef) {
710 if !attempts_enabled() {
711 return;
712 }
713 record(TraceEvent::ExecuteEncoding {
714 array: ArraySummary::new(array),
715 });
716}
717
718pub(crate) fn record_execute_slot(slot_idx: usize, parent: &ArrayRef, child: &ArrayRef) {
719 record(TraceEvent::SlotTransition {
720 step: "ExecuteSlot",
721 slot_idx,
722 parent: ArraySummary::new(parent),
723 child: ArraySummary::new(child),
724 });
725}
726
727pub(crate) fn record_builder_start(array: &ArrayRef) {
728 record(TraceEvent::BuilderEvent {
729 action: "start",
730 subject: "array",
731 array: ArraySummary::new(array),
732 });
733}
734
735pub(crate) fn record_append_child(slot_idx: usize, parent: &ArrayRef, child: &ArrayRef) {
736 record(TraceEvent::SlotTransition {
737 step: "AppendChild",
738 slot_idx,
739 parent: ArraySummary::new(parent),
740 child: ArraySummary::new(child),
741 });
742}
743
744pub(crate) fn record_builder_append(child: &ArrayRef) {
745 record(TraceEvent::BuilderEvent {
746 action: "append",
747 subject: "child",
748 array: ArraySummary::new(child),
749 });
750}
751
752pub(crate) fn record_execute_done(array: &ArrayRef) {
753 record(TraceEvent::ExecuteDone {
754 array: ArraySummary::new(array),
755 });
756}
757
758pub(crate) fn record_builder_finish(output: &ArrayRef) {
759 record(TraceEvent::BuilderEvent {
760 action: "finish",
761 subject: "output",
762 array: ArraySummary::new(output),
763 });
764}
765
766pub(crate) fn record_single_step_start(array: &ArrayRef) {
767 record(TraceEvent::SingleStepStart {
768 array: ArraySummary::new(array),
769 });
770}
771
772pub(crate) fn record_single_step_phase_none(phase: &'static str, array: &ArrayRef) {
773 if !attempts_enabled() {
774 return;
775 }
776 record(TraceEvent::PhaseNone {
777 indent: 1,
778 phase,
779 subject: "array",
780 array: ArraySummary::new(array),
781 });
782}
783
784pub(crate) fn record_single_step_applied(phase: &'static str, input: &ArrayRef, output: &ArrayRef) {
785 record(TraceEvent::SingleStepApplied {
786 phase,
787 input: ArraySummary::new(input),
788 output: ArraySummary::new(output),
789 });
790}
791
792fn record(event: TraceEvent) {
793 ACTIVE_TRACE.with(|active| {
794 if let Some(recorder) = active.borrow_mut().as_mut() {
795 recorder.events.push(event);
796 }
797 });
798}
799
800fn compact_label(value: &dyn Debug) -> String {
801 let label = format!("{value:?}");
802 if let Some(label) = adapter_field(&label, "rule") {
803 return label.to_string();
804 }
805 if let Some(label) = adapter_field(&label, "kernel") {
806 return label.to_string();
807 }
808 label
809}
810
811fn adapter_field<'a>(label: &'a str, field: &str) -> Option<&'a str> {
812 let marker = format!("{field}: ");
813 let start = label.find(&marker)? + marker.len();
814 let rest = &label[start..];
815 let end = rest.rfind(" }")?;
816 Some(&rest[..end])
817}
818
819fn short_type_name<T>() -> String {
820 std::any::type_name::<T>()
821 .rsplit("::")
822 .next()
823 .vortex_expect("type names are never empty")
824 .to_string()
825}
826
827thread_local! {
828 static TRACE_INTEREST: Cell<TraceInterest> = const { Cell::new(TraceInterest::Off) };
829 static ACTIVE_TRACE: RefCell<Option<TraceRecorder>> = const { RefCell::new(None) };
830}
831
832static ACTIVE_TRACE_COUNT: AtomicUsize = AtomicUsize::new(0);
833static ATTEMPTS_TRACE_COUNT: AtomicUsize = AtomicUsize::new(0);
834
835struct ActiveTraceGuard {
836 interest: TraceInterest,
837}
838
839impl Drop for ActiveTraceGuard {
840 fn drop(&mut self) {
841 if self.interest == TraceInterest::Attempts {
842 ATTEMPTS_TRACE_COUNT.fetch_sub(1, Ordering::Relaxed);
843 }
844 ACTIVE_TRACE_COUNT.fetch_sub(1, Ordering::Relaxed);
845 TRACE_INTEREST.with(|interest| interest.set(TraceInterest::Off));
846 ACTIVE_TRACE.with(|active| {
847 active.borrow_mut().take();
848 });
849 }
850}
851
852#[derive(Debug)]
853struct TraceRecorder {
854 options: TraceOptions,
855 events: Vec<TraceEvent>,
856}
857
858impl TraceRecorder {
859 fn new(options: TraceOptions) -> Self {
860 Self {
861 options,
862 events: Vec::new(),
863 }
864 }
865
866 fn finish(self) -> TraceDisplay {
867 TraceDisplay {
868 options: self.options,
869 events: self.events,
870 }
871 }
872}
873
874#[derive(Clone, Debug)]
875enum TraceEvent {
876 OptimizeStart {
877 root: ArraySummary,
878 session: bool,
879 },
880 OptimizeLoopStart {
881 array: ArraySummary,
882 },
883 OptimizeLoopEnd,
884 OptimizeDone {
885 output: ArraySummary,
886 changed: bool,
887 },
888 OptimizeRecursiveStart {
889 root: ArraySummary,
890 },
891 OptimizeRecursiveSlot {
892 slot_idx: usize,
893 input: ArraySummary,
894 output: ArraySummary,
895 },
896 ReduceAttempt {
897 array: ArraySummary,
898 rule: String,
899 outcome: AttemptOutcome,
900 },
901 ReduceApplied {
902 array: ArraySummary,
903 rule: String,
904 output: ArraySummary,
905 },
906 ParentReduceAttempt {
907 parent: ArraySummary,
908 child: ArraySummary,
909 slot_idx: usize,
910 source: TraceSource,
911 rule: String,
912 outcome: AttemptOutcome,
913 },
914 ParentReduceApplied {
915 parent: ArraySummary,
916 child: ArraySummary,
917 slot_idx: usize,
918 source: TraceSource,
919 rule: String,
920 output: ArraySummary,
921 },
922 ExecuteUntilStart {
923 target: String,
924 root: ArraySummary,
925 },
926 ExecuteUntilIteration {
927 iteration: usize,
928 current: ArraySummary,
929 stack_parent: Option<(ArraySummary, usize)>,
930 builder_active: bool,
931 },
932 ExecuteUntilDoneCheck {
933 target: bool,
934 canonical: bool,
935 },
936 ExecuteUntilReturn {
937 output: ArraySummary,
938 },
939 ExecuteUntilPopFrame {
940 slot_idx: usize,
941 output: ArraySummary,
942 },
943 ExecuteParentAttempt {
944 phase: &'static str,
945 parent: ArraySummary,
946 child: ArraySummary,
947 slot_idx: usize,
948 source: TraceSource,
949 kernel: String,
950 outcome: AttemptOutcome,
951 },
952 ExecuteParentApplied {
953 phase: &'static str,
954 parent: ArraySummary,
955 child: ArraySummary,
956 slot_idx: usize,
957 source: TraceSource,
958 kernel: String,
959 output: ArraySummary,
960 },
961 PhaseNone {
962 indent: usize,
963 phase: &'static str,
964 subject: &'static str,
965 array: ArraySummary,
966 },
967 ExecuteOptimized {
968 input: ArraySummary,
969 output: ArraySummary,
970 changed: bool,
971 },
972 ExecuteEncoding {
973 array: ArraySummary,
974 },
975 SlotTransition {
976 step: &'static str,
977 slot_idx: usize,
978 parent: ArraySummary,
979 child: ArraySummary,
980 },
981 BuilderEvent {
982 action: &'static str,
983 subject: &'static str,
984 array: ArraySummary,
985 },
986 ExecuteDone {
987 array: ArraySummary,
988 },
989 SingleStepStart {
990 array: ArraySummary,
991 },
992 SingleStepApplied {
993 phase: &'static str,
994 input: ArraySummary,
995 output: ArraySummary,
996 },
997}
998
999impl TraceEvent {
1000 fn is_hidden(&self, resolution: TraceResolution) -> bool {
1001 match resolution {
1002 TraceResolution::Attempts => matches!(self, TraceEvent::OptimizeLoopEnd),
1003 TraceResolution::ExecutedOnly => matches!(
1004 self,
1005 TraceEvent::OptimizeLoopStart { .. }
1006 | TraceEvent::OptimizeLoopEnd
1007 | TraceEvent::PhaseNone { .. }
1008 | TraceEvent::ExecuteUntilDoneCheck { .. }
1009 | TraceEvent::ExecuteEncoding { .. }
1010 | TraceEvent::ExecuteOptimized { changed: false, .. }
1011 | TraceEvent::ExecuteParentAttempt { .. }
1012 | TraceEvent::ReduceAttempt { .. }
1013 | TraceEvent::ParentReduceAttempt { .. }
1014 ),
1015 }
1016 }
1017
1018 fn opens_after(&self, resolution: TraceResolution) -> bool {
1019 match resolution {
1020 TraceResolution::Attempts => matches!(
1021 self,
1022 TraceEvent::OptimizeStart { .. } | TraceEvent::OptimizeLoopStart { .. }
1023 ),
1024 TraceResolution::ExecutedOnly => matches!(self, TraceEvent::OptimizeStart { .. }),
1025 }
1026 }
1027
1028 fn closes_before(&self, resolution: TraceResolution) -> bool {
1029 match resolution {
1030 TraceResolution::Attempts => matches!(self, TraceEvent::OptimizeLoopEnd),
1031 TraceResolution::ExecutedOnly => false,
1032 }
1033 }
1034
1035 fn closes_after(&self, _resolution: TraceResolution) -> bool {
1036 matches!(self, TraceEvent::OptimizeDone { .. })
1037 }
1038
1039 fn relative_indent(&self, _resolution: TraceResolution, in_optimize_scope: bool) -> usize {
1040 match self {
1041 TraceEvent::OptimizeStart { .. }
1042 | TraceEvent::OptimizeLoopStart { .. }
1043 | TraceEvent::OptimizeDone { .. } => 0,
1044 TraceEvent::ReduceAttempt { .. }
1045 | TraceEvent::ReduceApplied { .. }
1046 | TraceEvent::ParentReduceAttempt { .. }
1047 | TraceEvent::ParentReduceApplied { .. }
1048 if in_optimize_scope =>
1049 {
1050 0
1051 }
1052 TraceEvent::PhaseNone { indent, .. } => *indent,
1053 TraceEvent::ReduceAttempt { .. }
1054 | TraceEvent::ReduceApplied { .. }
1055 | TraceEvent::ParentReduceAttempt { .. }
1056 | TraceEvent::ParentReduceApplied { .. }
1057 | TraceEvent::ExecuteUntilDoneCheck { .. }
1058 | TraceEvent::ExecuteUntilPopFrame { .. }
1059 | TraceEvent::ExecuteParentAttempt { .. }
1060 | TraceEvent::ExecuteParentApplied { .. }
1061 | TraceEvent::ExecuteOptimized { .. }
1062 | TraceEvent::ExecuteEncoding { .. }
1063 | TraceEvent::SlotTransition { .. }
1064 | TraceEvent::BuilderEvent { .. }
1065 | TraceEvent::ExecuteDone { .. } => 2,
1066 TraceEvent::OptimizeRecursiveSlot { .. }
1067 | TraceEvent::ExecuteUntilIteration { .. }
1068 | TraceEvent::ExecuteUntilReturn { .. }
1069 | TraceEvent::SingleStepApplied { .. } => 1,
1070 TraceEvent::OptimizeLoopEnd
1071 | TraceEvent::OptimizeRecursiveStart { .. }
1072 | TraceEvent::ExecuteUntilStart { .. }
1073 | TraceEvent::SingleStepStart { .. } => 0,
1074 }
1075 }
1076
1077 fn fmt_line(&self, f: &mut fmt::Formatter<'_>, resolution: TraceResolution) -> fmt::Result {
1078 match self {
1079 TraceEvent::OptimizeStart { root, session } => {
1080 write!(f, "optimize root={root} session={session}")
1081 }
1082 TraceEvent::OptimizeLoopStart { array } => {
1083 write!(f, "loop input={array}")
1084 }
1085 TraceEvent::OptimizeLoopEnd => Ok(()),
1086 TraceEvent::OptimizeDone { output, changed } => match resolution {
1087 TraceResolution::Attempts => write!(f, "done output={output} changed={changed}"),
1088 TraceResolution::ExecutedOnly => write!(f, "done output={output}"),
1089 },
1090 TraceEvent::OptimizeRecursiveStart { root } => {
1091 write!(f, "optimize_recursive root={root}")
1092 }
1093 TraceEvent::OptimizeRecursiveSlot {
1094 slot_idx,
1095 input,
1096 output,
1097 } => write!(f, "recursive slot={slot_idx} input={input} output={output}"),
1098 TraceEvent::ReduceAttempt {
1099 array,
1100 rule,
1101 outcome,
1102 } => write!(
1103 f,
1104 "reduce attempt array={array} source=static rule={rule} outcome={outcome}"
1105 ),
1106 TraceEvent::ReduceApplied {
1107 array,
1108 rule,
1109 output,
1110 } => match resolution {
1111 TraceResolution::Attempts => write!(
1112 f,
1113 "reduce applied array={array} source=static rule={rule} output={output}"
1114 ),
1115 TraceResolution::ExecutedOnly => {
1116 write!(f, "reduce {rule}: {array} -> {output}")
1117 }
1118 },
1119 TraceEvent::ParentReduceAttempt {
1120 parent,
1121 child,
1122 slot_idx,
1123 source,
1124 rule,
1125 outcome,
1126 } => write!(
1127 f,
1128 "reduce_parent attempt slot={slot_idx} parent={parent} child={child} source={source} rule={rule} outcome={outcome}"
1129 ),
1130 TraceEvent::ParentReduceApplied {
1131 parent,
1132 child,
1133 slot_idx,
1134 source,
1135 rule,
1136 output,
1137 } => match resolution {
1138 TraceResolution::Attempts => write!(
1139 f,
1140 "reduce_parent applied slot={slot_idx} parent={parent} child={child} source={source} rule={rule} output={output}"
1141 ),
1142 TraceResolution::ExecutedOnly => write!(
1143 f,
1144 "reduce_parent {source}:{rule} slot={slot_idx} parent={parent} child={child} -> {output}"
1145 ),
1146 },
1147 TraceEvent::ExecuteUntilStart { target, root } => {
1148 write!(f, "execute_until target={target} root={root}")
1149 }
1150 TraceEvent::ExecuteUntilIteration {
1151 iteration,
1152 current,
1153 stack_parent,
1154 builder_active,
1155 } => {
1156 write!(f, "iter {iteration} current={current}")?;
1157 if let Some((parent, slot_idx)) = stack_parent {
1158 write!(f, " stack_parent={parent} slot={slot_idx}")?;
1159 }
1160 write!(f, " builder_active={builder_active}")
1161 }
1162 TraceEvent::ExecuteUntilDoneCheck { target, canonical } => {
1163 write!(f, "done_check target={target} canonical={canonical}")
1164 }
1165 TraceEvent::ExecuteUntilReturn { output } => {
1166 write!(f, "return output={output}")
1167 }
1168 TraceEvent::ExecuteUntilPopFrame { slot_idx, output } => {
1169 write!(f, "pop_frame slot={slot_idx} output={output}")
1170 }
1171 TraceEvent::ExecuteParentAttempt {
1172 phase,
1173 parent,
1174 child,
1175 slot_idx,
1176 source,
1177 kernel,
1178 outcome,
1179 } => write!(
1180 f,
1181 "{phase} attempt slot={slot_idx} parent={parent} child={child} source={source} kernel={kernel} outcome={outcome}"
1182 ),
1183 TraceEvent::ExecuteParentApplied {
1184 phase,
1185 parent,
1186 child,
1187 slot_idx,
1188 source,
1189 kernel,
1190 output,
1191 } => match resolution {
1192 TraceResolution::Attempts => write!(
1193 f,
1194 "{phase} applied slot={slot_idx} parent={parent} child={child} source={source} kernel={kernel} output={output}"
1195 ),
1196 TraceResolution::ExecutedOnly => write!(
1197 f,
1198 "{phase} {source}:{kernel} slot={slot_idx} parent={parent} child={child} -> {output}"
1199 ),
1200 },
1201 TraceEvent::PhaseNone {
1202 phase,
1203 subject,
1204 array,
1205 ..
1206 } => {
1207 write!(f, "{phase} none {subject}={array}")
1208 }
1209 TraceEvent::ExecuteOptimized {
1210 input,
1211 output,
1212 changed,
1213 } => match resolution {
1214 TraceResolution::Attempts => write!(
1215 f,
1216 "optimize_ctx input={input} output={output} changed={changed}"
1217 ),
1218 TraceResolution::ExecutedOnly => write!(f, "optimize_ctx {input} -> {output}"),
1219 },
1220 TraceEvent::ExecuteEncoding { array } => {
1221 write!(f, "execute encoding={array}")
1222 }
1223 TraceEvent::SlotTransition {
1224 step,
1225 slot_idx,
1226 parent,
1227 child,
1228 } => write!(f, "{step} slot={slot_idx} parent={parent} child={child}"),
1229 TraceEvent::ExecuteDone { array } => {
1230 write!(f, "Done array={array}")
1231 }
1232 TraceEvent::BuilderEvent {
1233 action,
1234 subject,
1235 array,
1236 } => {
1237 write!(f, "builder {action} {subject}={array}")
1238 }
1239 TraceEvent::SingleStepStart { array } => {
1240 write!(f, "execute_step input={array}")
1241 }
1242 TraceEvent::SingleStepApplied {
1243 phase,
1244 input,
1245 output,
1246 } => match resolution {
1247 TraceResolution::Attempts => {
1248 write!(f, "{phase} applied input={input} output={output}")
1249 }
1250 TraceResolution::ExecutedOnly => write!(f, "{phase} {input} -> {output}"),
1251 },
1252 }
1253 }
1254}
1255
1256#[cfg(test)]
1257mod tests;