Trace Event Format summary CLI
0

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-catapult / test / test_slice.ml
5.7 kB 147 lines
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2026 Thomas Gazagnaire. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6module Event = Catapult.Event 7module Phase = Catapult.Phase 8module Slice = Catapult.Slice 9 10let slices s = Slice.of_trace (Catapult.of_string_exn s) 11let names l = List.map (fun (s : Slice.t) -> s.name) l 12let durs l = List.map (fun (s : Slice.t) -> s.dur) l 13 14(* Spec, "Complete Events": ts + dur describe the whole interval. *) 15let test_complete () = 16 let l = slices {|[{"name":"f","ph":"X","ts":10,"dur":5,"pid":1,"tid":1}]|} in 17 Alcotest.(check (list string)) "names" [ "f" ] (names l); 18 Alcotest.(check (list (option (float 0.)))) "durs" [ Some 5. ] (durs l) 19 20(* Spec, "Duration Events": B/E must be properly nested per thread; an E closes 21 the most recent open B of its thread. *) 22let test_duration_nesting () = 23 let l = 24 slices 25 {|[{"name":"outer","ph":"B","ts":0,"pid":1,"tid":1}, 26 {"name":"inner","ph":"B","ts":10,"pid":1,"tid":1}, 27 {"ph":"E","ts":20,"pid":1,"tid":1}, 28 {"ph":"E","ts":40,"pid":1,"tid":1}]|} 29 in 30 Alcotest.(check (list string)) "names" [ "outer"; "inner" ] (names l); 31 Alcotest.(check (list (option (float 0.)))) 32 "durs" [ Some 40.; Some 10. ] (durs l) 33 34(* Duration pairing is per (pid, tid): same-name events on two threads must not 35 cross-match. *) 36let test_duration_per_thread () = 37 let l = 38 slices 39 {|[{"name":"w","ph":"B","ts":0,"pid":1,"tid":1}, 40 {"name":"w","ph":"B","ts":5,"pid":1,"tid":2}, 41 {"ph":"E","ts":7,"pid":1,"tid":2}, 42 {"ph":"E","ts":30,"pid":1,"tid":1}]|} 43 in 44 Alcotest.(check (list (option (float 0.)))) 45 "durs" [ Some 30.; Some 2. ] (durs l) 46 47(* A B without an E is an unterminated slice, not a dropped one: crashed 48 producers are the array format's normal case. A stray E is dropped. *) 49let test_duration_unmatched () = 50 let l = 51 slices 52 {|[{"ph":"E","ts":1,"pid":1,"tid":1}, 53 {"name":"open","ph":"B","ts":5,"pid":1,"tid":1}]|} 54 in 55 Alcotest.(check (list string)) "names" [ "open" ] (names l); 56 Alcotest.(check (list (option (float 0.)))) "durs" [ None ] (durs l) 57 58(* B and E args are concatenated, opening side first. *) 59let test_duration_args_merge () = 60 let l = 61 slices 62 {|[{"name":"f","ph":"B","ts":0,"pid":1,"tid":1,"args":{"in":1}}, 63 {"ph":"E","ts":1,"pid":1,"tid":1,"args":{"out":2}}]|} 64 in 65 match l with 66 | [ s ] -> 67 Alcotest.(check (list string)) 68 "arg keys" [ "in"; "out" ] 69 (List.map fst s.Slice.args) 70 | _ -> Alcotest.fail "expected one slice" 71 72(* Spec, "Async Events": b/e pair by id, crossing other spans freely. The same 73 id can be reused sequentially. *) 74let test_async_interleaved () = 75 let l = 76 slices 77 {|[{"name":"req","ph":"b","ts":0,"id":1,"pid":1,"tid":1}, 78 {"name":"req","ph":"b","ts":10,"id":2,"pid":1,"tid":1}, 79 {"name":"req","ph":"e","ts":15,"id":1,"pid":1,"tid":1}, 80 {"name":"req","ph":"e","ts":40,"id":2,"pid":1,"tid":1}, 81 {"name":"req","ph":"b","ts":50,"id":1,"pid":1,"tid":1}, 82 {"name":"req","ph":"e","ts":51,"id":1,"pid":1,"tid":1}]|} 83 in 84 Alcotest.(check (list (option (float 0.)))) 85 "durs" 86 [ Some 15.; Some 30.; Some 1. ] 87 (durs l) 88 89(* Async pairing requires the same name: two ids may collide across different 90 span declarations. *) 91let test_async_name_keyed () = 92 let l = 93 slices 94 {|[{"name":"a","ph":"b","ts":0,"id":7,"pid":1}, 95 {"name":"b","ph":"b","ts":1,"id":7,"pid":1}, 96 {"name":"a","ph":"e","ts":2,"id":7,"pid":1}, 97 {"name":"b","ph":"e","ts":4,"id":7,"pid":1}]|} 98 in 99 Alcotest.(check (list (option (float 0.)))) 100 "durs" [ Some 2.; Some 3. ] (durs l) 101 102(* Async instants are zero-duration slices; unterminated async begins surface 103 with dur None. *) 104let test_async_instant_and_open () = 105 let l = 106 slices 107 {|[{"name":"n","ph":"n","ts":3,"id":1,"pid":1}, 108 {"name":"open","ph":"b","ts":5,"id":2,"pid":1}]|} 109 in 110 Alcotest.(check (list (option (float 0.)))) "durs" [ Some 0.; None ] (durs l) 111 112(* Events sort by timestamp before pairing; the format does not promise file 113 order. *) 114let test_unsorted_input () = 115 let l = 116 slices 117 {|[{"ph":"E","ts":9,"pid":1,"tid":1}, 118 {"name":"f","ph":"B","ts":2,"pid":1,"tid":1}]|} 119 in 120 Alcotest.(check (list (option (float 0.)))) "durs" [ Some 7. ] (durs l) 121 122(* Non-interval phases produce no slice. *) 123let test_non_slices () = 124 let l = 125 slices 126 {|[{"name":"i","ph":"i","ts":1,"pid":1}, 127 {"name":"c","ph":"C","ts":2,"pid":1,"args":{"v":1}}, 128 {"name":"process_name","ph":"M","pid":1,"args":{"name":"p"}}]|} 129 in 130 Alcotest.(check (list string)) "no slices" [] (names l) 131 132let suite = 133 ( "slice", 134 [ 135 Alcotest.test_case "complete (spec)" `Quick test_complete; 136 Alcotest.test_case "duration nesting (spec)" `Quick test_duration_nesting; 137 Alcotest.test_case "duration per thread" `Quick test_duration_per_thread; 138 Alcotest.test_case "duration unmatched" `Quick test_duration_unmatched; 139 Alcotest.test_case "duration args merge" `Quick test_duration_args_merge; 140 Alcotest.test_case "async interleaved (spec)" `Quick 141 test_async_interleaved; 142 Alcotest.test_case "async name keyed" `Quick test_async_name_keyed; 143 Alcotest.test_case "async instant and open" `Quick 144 test_async_instant_and_open; 145 Alcotest.test_case "unsorted input" `Quick test_unsorted_input; 146 Alcotest.test_case "non-slices" `Quick test_non_slices; 147 ] )