(* TEST include runtime_events; include unix; set OCAML_RUNTIME_EVENTS_PRESERVE = "1"; hasunix; not-target-windows; { bytecode; }{ native; } *) let runtime_begin _ _ _ = () let runtime_end _ _ _ = () let runtime_counter _ _ _ _ = () let alloc _ _ _ = () let lifecycle _ _ _ _ = () let lost_events _ _ = () let callbacks = Runtime_events.Callbacks.create ~runtime_begin ~runtime_end ~runtime_counter ~alloc ~lifecycle ~lost_events () let parse path_pid = let cursor = Runtime_events.create_cursor path_pid in let finally () = Runtime_events.free_cursor cursor in Fun.protect ~finally @@ fun () -> Runtime_events.read_poll cursor callbacks None let parse_corrupted path_pid = try let (_:int) = parse path_pid in () with Failure _ | Invalid_argument _ -> (* parsing corrupted rings, raises exceptions, this is expected *) () let buf = Bytes.create (8 * 8) let with_ring ring_file f = let fd = Unix.openfile ring_file [Unix.O_RDWR] 0 in let finally () = Unix.close fd in Fun.protect ~finally @@ fun () -> let size = Int64.to_int Unix.LargeFile.((fstat fd).st_size) in let n = Unix.read fd buf 0 (Bytes.length buf) in assert (n = Bytes.length buf); let version = Bytes.get_int64_ne buf 0 in assert (version = 1L); (* this needs to be updated if on-disk layout changes *) let data_offset = Bytes.get_int64_ne buf (6*8) in let write_event_header is_runtime event_type event_id event_length = let (<<:) i n = Int64.(shift_left (of_int i) n) and (|:) = Int64.logor in (* see runtime_events.h *) let event_header = (event_length <<: 54) |: (is_runtime <<: 53) |: (event_type <<: 49) |: (event_id <<: 36) in Bytes.set_int64_ne buf 0 event_header; let n = Unix.LargeFile.lseek fd data_offset Unix.SEEK_SET in assert (n = data_offset); let n = Unix.write fd buf 0 (Bytes.length buf) in assert (n = Bytes.length buf) in let write_metadata_header offset value = let offset = Int64.of_int offset in let n = Unix.LargeFile.lseek fd offset Unix.SEEK_SET in assert (n = offset); Bytes.set_int64_ne buf 0 value; let n = Unix.write fd buf 0 (Bytes.length buf) in assert (n = Bytes.length buf) in f ~size ~write_event_header ~write_metadata_header (* this tests the preservation of ring buffers after termination *) let () = (* start runtime_events now to avoid a race *) let parent_cwd = Sys.getcwd () in let child_pid = Unix.fork () in if child_pid == 0 then begin (* we are in the child, so start Runtime_events *) Runtime_events.start (); (* this creates a ring buffer. Now exit. *) end else begin (* now wait for our child to finish *) Unix.wait () |> ignore; (* child has finished. We now have a valid ring *) let ring_file = Filename.concat parent_cwd (string_of_int child_pid ^ ".events") and path_pid = Some (parent_cwd, child_pid); in let finally () = Unix.unlink ring_file in Fun.protect ~finally @@ fun () -> with_ring ring_file @@ fun ~size ~write_event_header ~write_metadata_header -> (* we must succeed parsing it as is *) let n = parse path_pid in assert (n > 0); let original = Bytes.to_string buf in (* now overwrite various fields, corrupting the ring, and check that we don't crash (raising exceptions is fine). *) for offset = 8 downto 0 do [0L; size * 3/4 |> Int64.of_int; size * 2 |> Int64.of_int; Int64.max_int; Int64.min_int; Int64.(shift_right_logical max_int 1) ] |> List.iter @@ fun value -> write_metadata_header (8 * offset) value; parse_corrupted path_pid; (* restore original, we only corrupt and test one offset at a time, otherwise we may not find missing bounds checks if we exit early due to bounds error on an earlier offset *) Bytes.blit_string original 0 buf 0 (Bytes.length buf); done; (* restore metadata header, so we have a valid ring again *) write_metadata_header 0 1L (* version *); for is_runtime = 0 to 1 do for event_type = 0 to 15 (* event type is 4 bits *) do for event_id = 0 to 64 (* event_id is 13 bits, but not all used yet *) do for length = 0 to 3 (* short lengths trigger uninit read bugs *) do (* modify just 1 event in the otherwise valid ring *) write_event_header is_runtime event_type event_id length; (* parse ring *) parse_corrupted path_pid; done done done; done; end