1use crate::trace::event::{TraceData, TraceEvent, TraceEventKind};
60use crate::trace::independence::independent;
61use crate::util::DetHasher;
62use serde::{Deserialize, Serialize};
63use std::cmp::Ordering;
64use std::hash::{Hash, Hasher};
65
66#[derive(Debug)]
68pub struct FoataTrace {
69 layers: Vec<Vec<TraceEvent>>,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
78pub struct TraceEventKey {
79 pub kind: u8,
81 pub primary: u64,
83 pub secondary: u64,
85 pub tertiary: u64,
87}
88
89impl TraceEventKey {
90 #[must_use]
95 pub const fn new(kind: u8, primary: u64, secondary: u64, tertiary: u64) -> Self {
96 Self {
97 kind,
98 primary,
99 secondary,
100 tertiary,
101 }
102 }
103}
104
105impl FoataTrace {
106 #[must_use]
108 pub fn depth(&self) -> usize {
109 self.layers.len()
110 }
111
112 #[must_use]
114 pub fn len(&self) -> usize {
115 self.layers.iter().map(Vec::len).sum()
116 }
117
118 #[must_use]
120 pub fn is_empty(&self) -> bool {
121 self.layers.is_empty()
122 }
123
124 #[must_use]
126 pub fn layers(&self) -> &[Vec<TraceEvent>] {
127 &self.layers
128 }
129
130 #[must_use]
132 pub fn flatten(&self) -> Vec<TraceEvent> {
133 self.layers.iter().flat_map(|l| l.iter().cloned()).collect()
134 }
135
136 #[must_use]
141 pub fn fingerprint(&self) -> u64 {
142 let mut hasher = DetHasher::default();
143 for (layer_idx, layer) in self.layers.iter().enumerate() {
144 layer_idx.hash(&mut hasher);
145 layer.len().hash(&mut hasher);
146 for event in layer {
147 event_hash_key(event).hash(&mut hasher);
148 }
149 }
150 hasher.finish()
151 }
152}
153
154#[derive(Debug)]
168pub struct TraceMonoid {
169 canonical: FoataTrace,
171 fingerprint: u64,
173}
174
175impl TraceMonoid {
176 #[must_use]
178 pub fn identity() -> Self {
179 let canonical = FoataTrace { layers: vec![] };
180 let fingerprint = canonical.fingerprint();
181 Self {
182 canonical,
183 fingerprint,
184 }
185 }
186
187 #[must_use]
193 pub fn from_events(events: &[TraceEvent]) -> Self {
194 let canonical = canonicalize(events);
195 let fingerprint = canonical.fingerprint();
196 Self {
197 canonical,
198 fingerprint,
199 }
200 }
201
202 #[must_use]
212 pub fn concat(&self, other: &Self) -> Self {
213 if self.is_identity() {
214 return Self {
215 canonical: FoataTrace {
216 layers: other.canonical.layers.clone(),
217 },
218 fingerprint: other.fingerprint,
219 };
220 }
221 if other.is_identity() {
222 return Self {
223 canonical: FoataTrace {
224 layers: self.canonical.layers.clone(),
225 },
226 fingerprint: self.fingerprint,
227 };
228 }
229
230 let mut combined = self.canonical.flatten();
231 combined.extend(other.canonical.flatten());
232 Self::from_events(&combined)
233 }
234
235 #[must_use]
237 pub fn is_identity(&self) -> bool {
238 self.canonical.is_empty()
239 }
240
241 #[must_use]
243 pub fn canonical_form(&self) -> &FoataTrace {
244 &self.canonical
245 }
246
247 #[must_use]
249 pub fn class_fingerprint(&self) -> u64 {
250 self.fingerprint
251 }
252
253 #[must_use]
255 pub fn len(&self) -> usize {
256 self.canonical.len()
257 }
258
259 #[must_use]
261 pub fn is_empty(&self) -> bool {
262 self.canonical.is_empty()
263 }
264
265 #[must_use]
270 pub fn critical_path_length(&self) -> usize {
271 self.canonical.depth()
272 }
273
274 #[must_use]
279 pub fn max_parallelism(&self) -> usize {
280 self.canonical
281 .layers
282 .iter()
283 .map(Vec::len)
284 .max()
285 .unwrap_or(0)
286 }
287
288 #[must_use]
294 pub fn equivalent(&self, other: &Self) -> bool {
295 self.fingerprint == other.fingerprint
296 }
297
298 #[must_use]
304 pub fn equivalent_exact(&self, other: &Self) -> bool {
305 if self.canonical.depth() != other.canonical.depth() {
306 return false;
307 }
308 for (la, lb) in self
309 .canonical
310 .layers
311 .iter()
312 .zip(other.canonical.layers.iter())
313 {
314 if la.len() != lb.len() {
315 return false;
316 }
317 for (ea, eb) in la.iter().zip(lb.iter()) {
318 if !semantically_equal_event(ea, eb) {
319 return false;
320 }
321 }
322 }
323 true
324 }
325}
326
327impl PartialEq for TraceMonoid {
328 fn eq(&self, other: &Self) -> bool {
329 if self.fingerprint != other.fingerprint {
331 return false;
332 }
333 self.equivalent_exact(other)
335 }
336}
337
338impl Eq for TraceMonoid {}
339
340#[must_use]
356pub fn canonicalize(events: &[TraceEvent]) -> FoataTrace {
357 let n = events.len();
358 if n == 0 {
359 return FoataTrace { layers: vec![] };
360 }
361
362 let mut layer_of = vec![0usize; n];
366 let mut max_layer = 0usize;
367
368 for j in 1..n {
369 for i in 0..j {
370 if !independent(&events[i], &events[j]) {
371 layer_of[j] = layer_of[j].max(layer_of[i] + 1);
372 }
373 }
374 max_layer = max_layer.max(layer_of[j]);
375 }
376
377 let mut layers: Vec<Vec<TraceEvent>> = vec![vec![]; max_layer + 1];
379 for (idx, event) in events.iter().enumerate() {
380 layers[layer_of[idx]].push(event.clone());
381 }
382
383 for layer in &mut layers {
385 layer.sort_by(event_cmp);
386 }
387
388 FoataTrace { layers }
389}
390
391#[must_use]
396pub fn trace_fingerprint(events: &[TraceEvent]) -> u64 {
397 let n = events.len();
398 if n == 0 {
399 return FoataTrace { layers: vec![] }.fingerprint();
401 }
402
403 let mut layer_of = vec![0usize; n];
405 let mut max_layer = 0usize;
406
407 for j in 1..n {
408 for i in 0..j {
409 if !independent(&events[i], &events[j]) {
410 layer_of[j] = layer_of[j].max(layer_of[i] + 1);
411 }
412 }
413 max_layer = max_layer.max(layer_of[j]);
414 }
415
416 let mut layer_indices: Vec<Vec<usize>> = vec![vec![]; max_layer + 1];
418 for (idx, &layer) in layer_of.iter().enumerate() {
419 layer_indices[layer].push(idx);
420 }
421
422 let mut hasher = DetHasher::default();
423 for (layer_idx, indices) in layer_indices.iter_mut().enumerate() {
424 indices.sort_by(|&a, &b| event_cmp(&events[a], &events[b]));
425 layer_idx.hash(&mut hasher);
426 indices.len().hash(&mut hasher);
427 for &idx in indices.iter() {
428 event_hash_key(&events[idx]).hash(&mut hasher);
429 }
430 }
431 hasher.finish()
432}
433
434fn kind_discriminant(kind: TraceEventKind) -> u8 {
440 match kind {
441 TraceEventKind::Spawn => 0,
442 TraceEventKind::Schedule => 1,
443 TraceEventKind::Yield => 2,
444 TraceEventKind::Wake => 3,
445 TraceEventKind::Poll => 4,
446 TraceEventKind::Complete => 5,
447 TraceEventKind::CancelRequest => 6,
448 TraceEventKind::CancelAck => 7,
449 TraceEventKind::RegionCloseBegin => 8,
450 TraceEventKind::RegionCloseComplete => 9,
451 TraceEventKind::RegionCreated => 10,
452 TraceEventKind::RegionCancelled => 11,
453 TraceEventKind::ObligationReserve => 12,
454 TraceEventKind::ObligationCommit => 13,
455 TraceEventKind::ObligationAbort => 14,
456 TraceEventKind::ObligationLeak => 15,
457 TraceEventKind::TimeAdvance => 16,
458 TraceEventKind::TimerScheduled => 17,
459 TraceEventKind::TimerFired => 18,
460 TraceEventKind::TimerCancelled => 19,
461 TraceEventKind::IoRequested => 20,
462 TraceEventKind::IoReady => 21,
463 TraceEventKind::IoResult => 22,
464 TraceEventKind::IoError => 23,
465 TraceEventKind::RngSeed => 24,
466 TraceEventKind::RngValue => 25,
467 TraceEventKind::Checkpoint => 26,
468 TraceEventKind::FuturelockDetected => 27,
469 TraceEventKind::ChaosInjection => 28,
470 TraceEventKind::UserTrace => 29,
471 TraceEventKind::MonitorCreated => 30,
472 TraceEventKind::MonitorDropped => 31,
473 TraceEventKind::DownDelivered => 32,
474 TraceEventKind::LinkCreated => 33,
475 TraceEventKind::LinkDropped => 34,
476 TraceEventKind::ExitDelivered => 35,
477 TraceEventKind::WorkerCancelRequested => 36,
479 TraceEventKind::WorkerCancelAcknowledged => 37,
480 TraceEventKind::WorkerDrainStarted => 38,
481 TraceEventKind::WorkerDrainCompleted => 39,
482 TraceEventKind::WorkerFinalizeCompleted => 40,
483 }
484}
485
486fn pack_arena(idx: crate::util::ArenaIndex) -> u64 {
488 (u64::from(idx.index()) << 32) | u64::from(idx.generation())
489}
490
491fn event_sort_key(event: &TraceEvent) -> (u8, u64, u64, u64) {
497 let k = kind_discriminant(event.kind);
498 match &event.data {
499 TraceData::Task { task, region }
500 | TraceData::Cancel { task, region, .. }
501 | TraceData::Futurelock { task, region, .. } => {
502 (k, pack_arena(task.0), pack_arena(region.0), 0)
503 }
504 TraceData::Region { region, parent } => (
505 k,
506 pack_arena(region.0),
507 parent.map_or(0, |p| pack_arena(p.0)),
508 0,
509 ),
510 TraceData::RegionCancel { region, .. } => (k, pack_arena(region.0), 0, 0),
511 TraceData::Obligation {
512 obligation,
513 task,
514 region,
515 ..
516 } => (
517 k,
518 pack_arena(obligation.0),
519 pack_arena(task.0),
520 pack_arena(region.0),
521 ),
522 TraceData::Time { old, new } => (k, old.as_nanos(), new.as_nanos(), 0),
523 TraceData::Timer { timer_id, .. } => (k, *timer_id, 0, 0),
524 TraceData::IoRequested { token, .. } | TraceData::IoReady { token, .. } => {
525 (k, *token, 0, 0)
526 }
527 TraceData::IoResult { token, bytes } => {
528 let bytes_key = (*bytes).cast_unsigned() ^ (1u64 << 63);
530 (k, *token, bytes_key, 0)
531 }
532 TraceData::IoError { token, kind } => (k, *token, u64::from(*kind), 0),
533 TraceData::RngSeed { seed } => (k, *seed, 0, 0),
534 TraceData::RngValue { value } => (k, *value, 0, 0),
535 TraceData::Checkpoint {
536 sequence,
537 active_tasks,
538 active_regions,
539 } => (
540 k,
541 *sequence,
542 u64::from(*active_tasks),
543 u64::from(*active_regions),
544 ),
545 TraceData::Chaos { task, .. } => {
546 let task_key = task.map_or(0, |t| pack_arena(t.0));
547 (k, task_key, 0, 0)
548 }
549 TraceData::Message(msg) => {
550 let mut h = DetHasher::default();
551 msg.hash(&mut h);
552 (k, h.finish(), 0, 0)
553 }
554 TraceData::Monitor {
555 monitor_ref,
556 watcher,
557 monitored,
558 ..
559 } => (
560 k,
561 *monitor_ref,
562 pack_arena(watcher.0),
563 pack_arena(monitored.0),
564 ),
565 TraceData::Down {
566 monitor_ref,
567 monitored,
568 completion_vt,
569 ..
570 } => (
571 k,
572 completion_vt.as_nanos(),
573 pack_arena(monitored.0),
574 *monitor_ref,
575 ),
576 TraceData::Link {
577 link_ref,
578 task_a,
579 task_b,
580 ..
581 } => (k, *link_ref, pack_arena(task_a.0), pack_arena(task_b.0)),
582 TraceData::Exit {
583 link_ref,
584 from,
585 failure_vt,
586 ..
587 } => (k, failure_vt.as_nanos(), pack_arena(from.0), *link_ref),
588 TraceData::Worker {
589 job_id,
590 task,
591 region,
592 ..
593 } => (k, *job_id, pack_arena(task.0), pack_arena(region.0)),
594 TraceData::None => (k, 0, 0, 0),
595 }
596}
597
598#[must_use]
600pub fn trace_event_key(event: &TraceEvent) -> TraceEventKey {
601 let (kind, primary, secondary, tertiary) = event_sort_key(event);
602 TraceEventKey::new(kind, primary, secondary, tertiary)
603}
604
605fn event_cmp(a: &TraceEvent, b: &TraceEvent) -> Ordering {
607 event_sort_key(a).cmp(&event_sort_key(b))
608}
609
610fn semantically_equal_event(a: &TraceEvent, b: &TraceEvent) -> bool {
612 a.version == b.version && a.kind == b.kind && a.data == b.data
613}
614
615fn event_hash_key(event: &TraceEvent) -> (u8, u64, u64, u64) {
618 event_sort_key(event)
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624 use crate::monitor::DownReason;
625 use crate::record::ObligationKind;
626 use crate::types::{CancelReason, ObligationId, RegionId, TaskId, Time};
627
628 fn tid(n: u32) -> TaskId {
629 TaskId::new_for_test(n, 0)
630 }
631
632 fn rid(n: u32) -> RegionId {
633 RegionId::new_for_test(n, 0)
634 }
635
636 fn oid(n: u32) -> ObligationId {
637 ObligationId::new_for_test(n, 0)
638 }
639
640 #[test]
643 fn empty_trace() {
644 let foata = canonicalize(&[]);
645 assert!(foata.is_empty());
646 assert_eq!(foata.depth(), 0);
647 assert_eq!(foata.len(), 0);
648 }
649
650 #[test]
651 fn single_event() {
652 let events = [TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1))];
653 let foata = canonicalize(&events);
654 assert_eq!(foata.depth(), 1);
655 assert_eq!(foata.len(), 1);
656 }
657
658 #[test]
659 fn all_independent_events_in_one_layer() {
660 let events = [
662 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
663 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
664 TraceEvent::spawn(3, Time::ZERO, tid(3), rid(3)),
665 ];
666 let foata = canonicalize(&events);
667 assert_eq!(foata.depth(), 1);
668 assert_eq!(foata.layers()[0].len(), 3);
669 }
670
671 #[test]
672 fn chain_of_dependent_events() {
673 let events = [
675 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
676 TraceEvent::poll(2, Time::ZERO, tid(1), rid(1)),
677 TraceEvent::complete(3, Time::ZERO, tid(1), rid(1)),
678 ];
679 let foata = canonicalize(&events);
680 assert_eq!(foata.depth(), 3);
681 assert_eq!(foata.layers()[0].len(), 1);
682 assert_eq!(foata.layers()[1].len(), 1);
683 assert_eq!(foata.layers()[2].len(), 1);
684 }
685
686 #[test]
687 fn diamond_dependency() {
688 let events = [
701 TraceEvent::region_created(1, Time::ZERO, rid(1), None),
702 TraceEvent::spawn(2, Time::ZERO, tid(1), rid(1)),
703 TraceEvent::spawn(3, Time::ZERO, tid(2), rid(1)),
704 TraceEvent::complete(4, Time::ZERO, tid(1), rid(1)),
705 TraceEvent::complete(5, Time::ZERO, tid(2), rid(1)),
706 ];
707 let foata = canonicalize(&events);
708 assert_eq!(foata.depth(), 3);
709 assert_eq!(foata.layers()[0].len(), 1); assert_eq!(foata.layers()[1].len(), 2); assert_eq!(foata.layers()[2].len(), 2); }
713
714 #[test]
717 fn swapped_independent_events_same_fingerprint() {
718 let trace_a = [
719 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
720 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
721 ];
722 let trace_b = [
723 TraceEvent::spawn(1, Time::ZERO, tid(2), rid(2)),
724 TraceEvent::spawn(2, Time::ZERO, tid(1), rid(1)),
725 ];
726 let fp_a = trace_fingerprint(&trace_a);
727 let fp_b = trace_fingerprint(&trace_b);
728 assert_eq!(fp_a, fp_b);
729 }
730
731 #[test]
732 fn swapped_independent_events_same_canonical_form() {
733 let trace_a = [
734 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
735 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
736 ];
737 let trace_b = [
738 TraceEvent::spawn(1, Time::ZERO, tid(2), rid(2)),
739 TraceEvent::spawn(2, Time::ZERO, tid(1), rid(1)),
740 ];
741 let foata_a = canonicalize(&trace_a);
742 let foata_b = canonicalize(&trace_b);
743 assert_eq!(foata_a.depth(), foata_b.depth());
744 assert_eq!(foata_a.fingerprint(), foata_b.fingerprint());
745 }
746
747 #[test]
748 fn different_dependent_orders_different_fingerprints() {
749 let trace_a = [
751 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
752 TraceEvent::complete(2, Time::ZERO, tid(1), rid(1)),
753 ];
754 let trace_b = [
755 TraceEvent::complete(1, Time::ZERO, tid(1), rid(1)),
756 TraceEvent::spawn(2, Time::ZERO, tid(1), rid(1)),
757 ];
758 let fp_a = trace_fingerprint(&trace_a);
759 let fp_b = trace_fingerprint(&trace_b);
760 assert_ne!(fp_a, fp_b);
762 }
763
764 #[test]
767 fn down_delivered_canonicalizes_by_completion_vt_monitored_monitor_ref() {
768 let e0 = TraceEvent::down_delivered(
769 1,
770 Time::ZERO,
771 1,
772 tid(10),
773 tid(3),
774 Time::from_nanos(4),
775 DownReason::Normal,
776 );
777 let e1 = TraceEvent::down_delivered(
778 2,
779 Time::ZERO,
780 10,
781 tid(11),
782 tid(1),
783 Time::from_nanos(5),
784 DownReason::Normal,
785 );
786 let e2 = TraceEvent::down_delivered(
787 3,
788 Time::ZERO,
789 9,
790 tid(12),
791 tid(1),
792 Time::from_nanos(5),
793 DownReason::Normal,
794 );
795 let e3 = TraceEvent::down_delivered(
796 4,
797 Time::ZERO,
798 1,
799 tid(13),
800 tid(2),
801 Time::from_nanos(5),
802 DownReason::Normal,
803 );
804
805 let trace_a = [e0.clone(), e1.clone(), e2.clone(), e3.clone()];
809 let trace_b = [e3.clone(), e2.clone(), e1.clone(), e0.clone()];
810
811 let foata_a = canonicalize(&trace_a);
812 let foata_b = canonicalize(&trace_b);
813 assert_eq!(foata_a.fingerprint(), foata_b.fingerprint());
814 assert_eq!(foata_a.depth(), 1);
815
816 let flat = foata_a.flatten();
817 assert_eq!(flat.len(), 4);
818 assert_eq!(flat[0], e0);
819 assert_eq!(flat[1], e2);
820 assert_eq!(flat[2], e1);
821 assert_eq!(flat[3], e3);
822 }
823
824 #[test]
825 fn exit_delivered_canonicalizes_by_failure_vt_from_link_ref() {
826 let e0 = TraceEvent::exit_delivered(
827 1,
828 Time::ZERO,
829 1,
830 tid(2),
831 tid(20),
832 Time::from_nanos(9),
833 DownReason::Error("boom".to_string()),
834 );
835 let e1 = TraceEvent::exit_delivered(
836 2,
837 Time::ZERO,
838 10,
839 tid(1),
840 tid(21),
841 Time::from_nanos(10),
842 DownReason::Error("boom".to_string()),
843 );
844 let e2 = TraceEvent::exit_delivered(
845 3,
846 Time::ZERO,
847 9,
848 tid(1),
849 tid(22),
850 Time::from_nanos(10),
851 DownReason::Error("boom".to_string()),
852 );
853 let e3 = TraceEvent::exit_delivered(
854 4,
855 Time::ZERO,
856 1,
857 tid(3),
858 tid(23),
859 Time::from_nanos(10),
860 DownReason::Error("boom".to_string()),
861 );
862
863 let trace_a = [e3.clone(), e2.clone(), e1.clone(), e0.clone()];
866 let trace_b = [e0.clone(), e1.clone(), e2.clone(), e3.clone()];
867
868 let foata_a = canonicalize(&trace_a);
869 let foata_b = canonicalize(&trace_b);
870 assert_eq!(foata_a.fingerprint(), foata_b.fingerprint());
871 assert_eq!(foata_a.depth(), 1);
872
873 let flat = foata_a.flatten();
874 assert_eq!(flat.len(), 4);
875 assert_eq!(flat[0], e0);
876 assert_eq!(flat[1], e2);
877 assert_eq!(flat[2], e1);
878 assert_eq!(flat[3], e3);
879 }
880
881 #[test]
884 fn three_independent_events_all_permutations_same_fingerprint() {
885 let e1 = TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1));
886 let e2 = TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2));
887 let e3 = TraceEvent::spawn(3, Time::ZERO, tid(3), rid(3));
888
889 let permutations: Vec<Vec<TraceEvent>> = vec![
890 vec![e1.clone(), e2.clone(), e3.clone()],
891 vec![e1.clone(), e3.clone(), e2.clone()],
892 vec![e2.clone(), e1.clone(), e3.clone()],
893 vec![e2.clone(), e3.clone(), e1.clone()],
894 vec![e3.clone(), e1.clone(), e2.clone()],
895 vec![e3, e2, e1],
896 ];
897
898 let fp0 = trace_fingerprint(&permutations[0]);
899 for (i, perm) in permutations.iter().enumerate() {
900 let fp = trace_fingerprint(perm);
901 assert_eq!(fp, fp0, "Permutation {i} has different fingerprint");
902 }
903 }
904
905 #[test]
908 fn mixed_trace_canonical_form() {
909 let events = [
913 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
914 TraceEvent::time_advance(2, Time::ZERO, Time::ZERO, Time::from_nanos(100)),
915 TraceEvent::spawn(3, Time::ZERO, tid(2), rid(2)),
916 TraceEvent::timer_fired(4, Time::ZERO, 1),
917 ];
918 let foata = canonicalize(&events);
919 assert_eq!(foata.depth(), 2);
923 assert_eq!(foata.layers()[0].len(), 3); assert_eq!(foata.layers()[1].len(), 1); }
926
927 #[test]
930 fn intra_layer_ordering_is_deterministic() {
931 let events = [
932 TraceEvent::spawn(1, Time::ZERO, tid(3), rid(3)),
933 TraceEvent::spawn(2, Time::ZERO, tid(1), rid(1)),
934 TraceEvent::spawn(3, Time::ZERO, tid(2), rid(2)),
935 ];
936 let foata = canonicalize(&events);
937 assert_eq!(foata.depth(), 1);
938
939 let layer = &foata.layers()[0];
942 let keys: Vec<_> = layer.iter().map(event_sort_key).collect();
943 assert!(keys.windows(2).all(|w| w[0] <= w[1]));
944 }
945
946 #[test]
949 fn fingerprint_matches_foata_fingerprint() {
950 let events = [
951 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
952 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
953 TraceEvent::complete(3, Time::ZERO, tid(1), rid(1)),
954 ];
955 let foata = canonicalize(&events);
956 let direct = trace_fingerprint(&events);
957 assert_eq!(foata.fingerprint(), direct);
958 }
959
960 #[test]
961 fn empty_trace_fingerprint_matches_identity() {
962 let id = TraceMonoid::identity();
963 let empty = TraceMonoid::from_events(&[]);
964 assert_eq!(trace_fingerprint(&[]), id.class_fingerprint());
965 assert_eq!(id.class_fingerprint(), empty.class_fingerprint());
966 assert!(id.equivalent(&empty));
967 }
968
969 #[test]
972 fn depth_reflects_critical_path() {
973 let events = [
977 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
978 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
979 TraceEvent::complete(3, Time::ZERO, tid(1), rid(1)),
980 TraceEvent::complete(4, Time::ZERO, tid(2), rid(2)),
981 ];
982 let foata = canonicalize(&events);
983 assert_eq!(foata.depth(), 2);
984 assert_eq!(foata.layers()[0].len(), 2); assert_eq!(foata.layers()[1].len(), 2); }
987
988 #[test]
991 fn flatten_preserves_event_count() {
992 let events = [
993 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
994 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
995 TraceEvent::complete(3, Time::ZERO, tid(1), rid(1)),
996 ];
997 let foata = canonicalize(&events);
998 assert_eq!(foata.flatten().len(), events.len());
999 }
1000
1001 #[test]
1004 fn monoid_identity_left() {
1005 let events = [
1006 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1007 TraceEvent::complete(2, Time::ZERO, tid(1), rid(1)),
1008 ];
1009 let t = TraceMonoid::from_events(&events);
1010 let result = TraceMonoid::identity().concat(&t);
1011 assert!(result.equivalent(&t));
1012 assert!(result.equivalent_exact(&t));
1013 }
1014
1015 #[test]
1016 fn monoid_identity_right() {
1017 let events = [
1018 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1019 TraceEvent::complete(2, Time::ZERO, tid(1), rid(1)),
1020 ];
1021 let t = TraceMonoid::from_events(&events);
1022 let result = t.concat(&TraceMonoid::identity());
1023 assert!(result.equivalent(&t));
1024 assert!(result.equivalent_exact(&t));
1025 }
1026
1027 #[test]
1028 fn monoid_identity_is_empty() {
1029 let id = TraceMonoid::identity();
1030 assert!(id.is_identity());
1031 assert!(id.is_empty());
1032 assert_eq!(id.len(), 0);
1033 assert_eq!(id.critical_path_length(), 0);
1034 assert_eq!(id.max_parallelism(), 0);
1035 }
1036
1037 #[test]
1040 fn monoid_associativity() {
1041 let a_events = [TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1))];
1042 let b_events = [TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2))];
1043 let c_events = [TraceEvent::spawn(3, Time::ZERO, tid(3), rid(3))];
1044
1045 let a = TraceMonoid::from_events(&a_events);
1046 let b = TraceMonoid::from_events(&b_events);
1047 let c = TraceMonoid::from_events(&c_events);
1048
1049 let ab_c = a.concat(&b).concat(&c);
1050 let a_bc = a.concat(&b.concat(&c));
1051 assert!(ab_c.equivalent(&a_bc));
1052 }
1053
1054 #[test]
1055 fn monoid_associativity_with_dependencies() {
1056 let a_events = [TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1))];
1058 let b_events = [TraceEvent::complete(2, Time::ZERO, tid(1), rid(1))];
1059 let c_events = [TraceEvent::spawn(3, Time::ZERO, tid(2), rid(2))];
1060
1061 let a = TraceMonoid::from_events(&a_events);
1062 let b = TraceMonoid::from_events(&b_events);
1063 let c = TraceMonoid::from_events(&c_events);
1064
1065 let ab_c = a.concat(&b).concat(&c);
1066 let a_bc = a.concat(&b.concat(&c));
1067 assert!(ab_c.equivalent(&a_bc));
1068 }
1069
1070 #[test]
1073 fn monoid_equivalent_traces() {
1074 let trace_a = [
1076 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1077 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
1078 ];
1079 let trace_b = [
1080 TraceEvent::spawn(1, Time::ZERO, tid(2), rid(2)),
1081 TraceEvent::spawn(2, Time::ZERO, tid(1), rid(1)),
1082 ];
1083 let ma = TraceMonoid::from_events(&trace_a);
1084 let mb = TraceMonoid::from_events(&trace_b);
1085 assert_eq!(ma, mb);
1086 assert!(ma.equivalent_exact(&mb));
1087 }
1088
1089 #[test]
1090 fn partial_eq_requires_exact_canonical_match_not_just_fingerprint() {
1091 let trace_a = [
1092 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1093 TraceEvent::complete(2, Time::ZERO, tid(1), rid(1)),
1094 ];
1095 let trace_b = [
1096 TraceEvent::spawn(1, Time::ZERO, tid(2), rid(2)),
1097 TraceEvent::complete(2, Time::ZERO, tid(2), rid(2)),
1098 ];
1099
1100 let ma = TraceMonoid::from_events(&trace_a);
1101 let mb = TraceMonoid::from_events(&trace_b);
1102 assert!(!ma.equivalent_exact(&mb));
1103
1104 let TraceMonoid {
1107 canonical: FoataTrace { layers },
1108 ..
1109 } = mb;
1110 let spoof = TraceMonoid {
1111 canonical: FoataTrace { layers },
1112 fingerprint: ma.fingerprint,
1113 };
1114
1115 assert!(ma.equivalent(&spoof));
1117 assert_ne!(ma, spoof);
1118 }
1119
1120 #[test]
1121 fn exact_equivalence_distinguishes_region_cancel_reason_payload() {
1122 let trace_a = [TraceEvent::region_cancelled(
1123 1,
1124 Time::ZERO,
1125 rid(1),
1126 CancelReason::shutdown(),
1127 )];
1128 let trace_b = [TraceEvent::region_cancelled(
1129 1,
1130 Time::ZERO,
1131 rid(1),
1132 CancelReason::timeout(),
1133 )];
1134
1135 let ma = TraceMonoid::from_events(&trace_a);
1136 let mb = TraceMonoid::from_events(&trace_b);
1137
1138 assert!(ma.equivalent(&mb));
1139 assert!(!ma.equivalent_exact(&mb));
1140 assert_ne!(ma, mb);
1141 }
1142
1143 #[test]
1144 fn monoid_nonequivalent_traces() {
1145 let trace_a = [
1147 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1148 TraceEvent::complete(2, Time::ZERO, tid(1), rid(1)),
1149 ];
1150 let trace_b = [
1151 TraceEvent::spawn(1, Time::ZERO, tid(2), rid(2)),
1152 TraceEvent::complete(2, Time::ZERO, tid(2), rid(2)),
1153 ];
1154 let ma = TraceMonoid::from_events(&trace_a);
1155 let mb = TraceMonoid::from_events(&trace_b);
1156 assert_ne!(ma, mb);
1157 }
1158
1159 #[test]
1162 fn monoid_parallelism_metrics() {
1163 let events = [
1164 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1165 TraceEvent::spawn(2, Time::ZERO, tid(2), rid(2)),
1166 TraceEvent::spawn(3, Time::ZERO, tid(3), rid(3)),
1167 TraceEvent::complete(4, Time::ZERO, tid(1), rid(1)),
1168 TraceEvent::complete(5, Time::ZERO, tid(2), rid(2)),
1169 ];
1170 let m = TraceMonoid::from_events(&events);
1171 assert_eq!(m.len(), 5);
1172 assert_eq!(m.critical_path_length(), 2); assert_eq!(m.max_parallelism(), 3); }
1175
1176 #[test]
1177 fn monoid_from_events_mapping() {
1178 let events = [
1180 TraceEvent::spawn(1, Time::ZERO, tid(1), rid(1)),
1181 TraceEvent::region_created(2, Time::ZERO, rid(2), None),
1182 TraceEvent::spawn(3, Time::ZERO, tid(2), rid(2)),
1183 ];
1184 let m = TraceMonoid::from_events(&events);
1185 assert_eq!(m.len(), events.len());
1186 assert_eq!(m.class_fingerprint(), trace_fingerprint(&events));
1187 }
1188
1189 #[test]
1192 fn independent_obligations_same_layer() {
1193 let events = [
1194 TraceEvent::obligation_reserve(
1195 1,
1196 Time::ZERO,
1197 oid(1),
1198 tid(1),
1199 rid(1),
1200 ObligationKind::SendPermit,
1201 ),
1202 TraceEvent::obligation_reserve(
1203 2,
1204 Time::ZERO,
1205 oid(2),
1206 tid(2),
1207 rid(2),
1208 ObligationKind::Ack,
1209 ),
1210 ];
1211 let foata = canonicalize(&events);
1212 assert_eq!(foata.depth(), 1);
1213 }
1214
1215 #[test]
1216 fn same_obligation_events_form_chain() {
1217 let events = [
1218 TraceEvent::obligation_reserve(
1219 1,
1220 Time::ZERO,
1221 oid(1),
1222 tid(1),
1223 rid(1),
1224 ObligationKind::Lease,
1225 ),
1226 TraceEvent::obligation_commit(
1227 2,
1228 Time::ZERO,
1229 oid(1),
1230 tid(1),
1231 rid(1),
1232 ObligationKind::Lease,
1233 5000,
1234 ),
1235 ];
1236 let foata = canonicalize(&events);
1237 assert_eq!(foata.depth(), 2);
1238 }
1239
1240 #[test]
1243 fn trace_event_key_debug_clone_copy_eq_hash() {
1244 use std::collections::HashSet;
1245 let k = TraceEventKey::new(1, 2, 3, 4);
1246 let k2 = k; let k3 = k;
1248 assert_eq!(k, k2);
1249 assert_eq!(k, k3);
1250 assert_ne!(k, TraceEventKey::new(1, 2, 3, 5));
1251 let dbg = format!("{k:?}");
1252 assert!(dbg.contains("TraceEventKey"));
1253 let mut set = HashSet::new();
1254 set.insert(k);
1255 assert!(set.contains(&k2));
1256 }
1257}