diff --git a/README.md b/README.md index cc03ff4..570be3a 100755 --- a/README.md +++ b/README.md @@ -294,6 +294,16 @@ in the landmarks-threads.cm(x)a archive) that prevents non thread-safe functions to execute in all threads but the one which started the profiling. +Instrumenting with domains (on OCaml >= 5) +------------------------------------------ + +*Landmarks* can be used with OCaml domains, but it comes with the following +constraints: + +1) Each domain has to run on its separate physical core, notably for correct + time measures (which can be done using [ocaml-processor](https://github.com/haesbaert/ocaml-processor) or [domainpc](https://github.com/OCamlPro/domainpc)). +2) Spawned domains have to terminate before the main domain. + Known Issue ----------- diff --git a/dune-project b/dune-project index 4f31a1b..f7b4053 100755 --- a/dune-project +++ b/dune-project @@ -34,7 +34,7 @@ landmarks library.") (depends (ocaml (>= 4.08)) - (ppxlib (>= 0.22)) + (ppxlib (>= 0.36)) (landmarks (= 1.5)) ) ) diff --git a/landmarks-ppx.opam b/landmarks-ppx.opam index bb2ab4c..e7e531a 100644 --- a/landmarks-ppx.opam +++ b/landmarks-ppx.opam @@ -13,7 +13,7 @@ bug-reports: "https://github.com/LexiFi/landmarks/issues" depends: [ "dune" {>= "3.16"} "ocaml" {>= "4.08"} - "ppxlib" {>= "0.22"} + "ppxlib" {>= "0.36"} "landmarks" {= "1.5"} "odoc" {with-doc} ] diff --git a/ppx/mapper.ml b/ppx/mapper.ml index 2456126..79ddf5b 100644 --- a/ppx/mapper.ml +++ b/ppx/mapper.ml @@ -176,26 +176,47 @@ let wrap_landmark ctx landmark loc expr = let rec arity {pexp_desc; _} = match pexp_desc with - | Pexp_fun (a, _, _, e) -> a :: arity e - | Pexp_function cases -> - let max_list l1 l2 = - if List.length l1 < List.length l2 then - l1 - else - l2 - in - Nolabel :: (List.fold_left - (fun acc {pc_rhs; _} -> max_list (arity pc_rhs) acc) - [] cases) + | Pexp_function (params, _, body) -> + List.filter_map + (function + | { pparam_desc = Pparam_val (a, _, _); _ } -> Some a + | { pparam_desc = Pparam_newtype _; _ } -> None ) + params + @ body_arity body | Pexp_newtype (_, e) -> arity e | Pexp_constraint (e, _) -> arity e | Pexp_poly (e, _) -> arity e | _ -> [] -let rec wrap_landmark_method ctx landmark loc ({pexp_desc; _} as expr) = +and body_arity body = + match body with + | Pfunction_body e -> arity e + | Pfunction_cases (cases, _, _) -> + let max_list l1 l2 = if List.length l1 < List.length l2 then l1 else l2 in + Nolabel + :: List.fold_left + (fun acc { pc_rhs; _ } -> max_list (arity pc_rhs) acc) + [] cases + +let rec wrap_landmark_method ctx landmark loc ({ pexp_desc; _ } as expr) = match pexp_desc with - | Pexp_fun (label, def, pat, e) -> - { expr with pexp_desc = Pexp_fun (label, def, pat, wrap_landmark_method ctx landmark loc e)} + | Pexp_function (params, constraint_, Pfunction_body e) -> + let body = wrap_landmark_method ctx landmark loc e in + { expr with + pexp_desc = Pexp_function (params, constraint_, Pfunction_body body) + } + | Pexp_function ((_ :: _ as params), constraint_, Pfunction_cases (c, l, a)) + -> + let function_ = + { expr with + pexp_desc = Pexp_function ([], None, Pfunction_cases (c, l, a)) + ; pexp_loc = l + } + in + let body = wrap_landmark ctx landmark l function_ in + { expr with + pexp_desc = Pexp_function (params, constraint_, Pfunction_body body) + } | Pexp_poly (e, typ) -> { expr with pexp_desc = Pexp_poly (wrap_landmark_method ctx landmark loc e, typ)} | _ -> wrap_landmark ctx landmark loc expr @@ -256,10 +277,10 @@ let translate_value_bindings ctx value_binding auto vbs = in let vbs = List.map (function | (vb, None) -> value_binding vb - | {pvb_pat; pvb_loc; pvb_expr; _}, Some (arity, _, name, loc, attrs) -> + | {pvb_pat; pvb_loc; pvb_expr; pvb_constraint; _}, Some (arity, _, name, loc, attrs) -> (* Remove landmark attribute: *) let vb = - Vb.mk ~attrs ~loc:pvb_loc pvb_pat pvb_expr + Vb.mk ~attrs ~loc:pvb_loc ?value_constraint:pvb_constraint pvb_pat pvb_expr |> value_binding in if arity = [] then diff --git a/src/dune b/src/dune index d138a06..c2da561 100755 --- a/src/dune +++ b/src/dune @@ -1,6 +1,7 @@ (library (name landmark) (public_name landmarks) + (modules landmark landmark_state graph misc utils) (no_dynlink) (flags (:standard -w +A-30-42-41-40-4-70 -safe-string -strict-sequence)) @@ -10,3 +11,19 @@ (js_of_ocaml (javascript_files utils.js)) (instrumentation.backend (ppx landmarks-ppx))) + +(rule + (targets landmark_state.ml) + (deps landmark_state_ocaml4.ml) + (action + (copy# %{deps} %{targets})) + (enabled_if + (< %{ocaml_version} 5.0.0))) + +(rule + (targets landmark_state.ml) + (deps landmark_state_ocaml5.ml) + (action + (copy# %{deps} %{targets})) + (enabled_if + (>= %{ocaml_version} 5.0.0))) diff --git a/src/landmark.ml b/src/landmark.ml index 5996026..989f5bd 100644 --- a/src/landmark.ml +++ b/src/landmark.ml @@ -2,6 +2,9 @@ (* See the attached LICENSE file. *) (* Copyright (C) 2000-2025 LexiFi *) +open Utils +open Landmark_state + external clock: unit -> (Int64.t [@unboxed]) = "caml_highres_clock" "caml_highres_clock_native" [@@noalloc] @@ -17,190 +20,11 @@ let allocated_bytes_major () = Int64.to_int (allocated_bytes_major ()) exception LandmarkFailure of string module Graph = Graph +module Stack = Utils.Stack -module SparseArray = struct - type 'a t = { - mutable keys : int array; - mutable data : 'a array; - mutable size : int; - } - - (* /!\ Dummy cannot be resized. *) - let dummy () = { keys = [||]; data = [||]; size = 0 } - - let make null n = - let n = max n 1 in - { - keys = Array.make n 0; - data = Array.make n null; - size = 0; - } - - let reset sparse_array = sparse_array.size <- 0 - - let get t id = - let {keys; data; size} = t in - let min = ref 0 in - let max = ref (size - 1) in - while !min < !max do - let middle = (!min + !max) / 2 in - if Array.unsafe_get keys middle < id then - min := middle + 1 - else - max := middle - done; - let idx = !min in - if idx = !max && - Array.unsafe_get keys idx = id then - Array.unsafe_get data idx - else - raise Not_found - - let swap a i j = - let t = a.(i) in - a.(i) <- a.(j); - a.(j) <- t - - let values {data; size; _} = - let result = ref [] in - for k = 0 to size-1 do - result := data.(k) :: !result; - done; - List.rev !result - - let bubble {keys; data; size} = - let pos = ref size in - let key = keys.(size) in - while - let p = !pos in - let q = p - 1 in - if key < keys.(q) then begin - swap keys p q; - swap data p q; - pos := q; - q > 0 - end else false - do () done - - let is_full ({keys; size; _}) = Array.length keys = size - - let resize ({keys; data; size} as sparse_array) = - if is_full sparse_array then begin - assert (size > 0); - let new_length = (2 * (size + 1)) - 1 in - sparse_array.keys <- Array.make new_length 0; - sparse_array.data <- Array.make new_length sparse_array.data.(0); - Array.blit keys 0 sparse_array.keys 0 size; - Array.blit data 0 sparse_array.data 0 size; - end - - let set sparse_array id node = - resize sparse_array; - let size = sparse_array.size in - sparse_array.keys.(size) <- id; - sparse_array.data.(size) <- node; - if size > 0 then - bubble sparse_array; - sparse_array.size <- sparse_array.size + 1 -end - -module Stack = struct - module A = struct - type (_, _) kind = - | Array : ('a, 'a array) kind - | Float : (float, floatarray) kind - let empty : type a arr. (a, arr) kind -> arr = function - | Array -> [||] - | Float -> Float.Array.create 0 - let make : type a arr. (a, arr) kind -> int -> a -> arr = fun kind n null -> - match kind with - | Array -> Array.make n null - | Float -> Float.Array.make n null - let length : type a arr. (a, arr) kind -> arr -> int = fun kind arr -> - match kind with - | Array -> Array.length arr - | Float -> Float.Array.length arr - let get : type a arr. (a, arr) kind -> arr -> int -> a = fun kind arr n -> - match kind with - | Array -> Array.get arr n - | Float -> Float.Array.get arr n - let set : type a arr. (a, arr) kind -> arr -> int -> a -> unit = fun kind arr n -> - match kind with - | Array -> Array.set arr n - | Float -> Float.Array.set arr n - let blit : type a arr. (a, arr) kind -> arr -> int -> arr -> int -> int -> unit = fun kind src srcpos dst dstpos n -> - match kind with - | Array -> Array.blit src srcpos dst dstpos n - | Float -> Float.Array.blit src srcpos dst dstpos n - end - type ('a, 'arr) t = { - kind : ('a, 'arr) A.kind; - mutable data : 'arr; - mutable size : int - } - (* /!\ Dummy cannot be resized. *) - let dummy kind = { kind; data = A.empty kind; size = 0 } - let make kind null n = { kind; data = A.make kind (max 1 n) null; size = 0 } - let size {size; _} = size - let resize ({kind; size; data} as stack) = - if size = A.length kind data then begin - assert (size > 0); - let new_length = (2 * (size + 1)) - 1 in - stack.data <- A.make kind new_length (A.get kind data 0); - A.blit kind data 0 stack.data 0 size; - end - - let push stack x = - resize stack; - A.set stack.kind stack.data stack.size x; - stack.size <- stack.size + 1 - - let pop stack = - stack.size <- stack.size - 1; - A.get stack.kind stack.data stack.size - - let to_floatarray {data; size; _} = Float.Array.sub data 0 size -end - -type landmark = { - id: int; - key: landmark_key; - kind : Graph.kind; - name: string; - location: string; - - - mutable last_parent: node; - mutable last_son: node; - mutable last_self: node; -} - -and node = { - landmark: landmark; - - id: int; - - children: node SparseArray.t; - fathers: (node, node array) Stack.t; - - mutable calls: int; - mutable recursive_calls: int; - mutable timestamp: Int64.t; - distrib: (float, floatarray) Stack.t; - floats : floats; -} - -and floats = { - mutable time: float; - mutable allocated_bytes: int; - mutable allocated_bytes_stamp: int; - mutable allocated_bytes_major: int; - mutable allocated_bytes_major_stamp: int; - mutable sys_time: float; - mutable sys_timestamp: float; -} +type nonrec landmark = landmark -and landmark_key = { +type landmark_key = { key: string; landmark: landmark; } @@ -209,41 +33,6 @@ and counter = landmark and sampler = landmark -let new_floats () = { - time = 0.0; - allocated_bytes = 0; - allocated_bytes_stamp = 0; - allocated_bytes_major = 0; - allocated_bytes_major_stamp = 0; - sys_time = 0.0; - sys_timestamp = 0.0 -} - -let rec landmark_root = { - kind = Graph.Root; - id = 0; - name = "ROOT"; - location = __FILE__; - key = { key = ""; landmark = landmark_root}; - last_parent = dummy_node; - last_son = dummy_node; - last_self = dummy_node; -} - -and dummy_node = { - landmark = landmark_root; - id = 0; - children = SparseArray.dummy (); - fathers = Stack.dummy Array; - floats = new_floats (); - calls = 0; - recursive_calls = 0; - distrib = Stack.dummy Float; - timestamp = Int64.zero -} - -and dummy_key = { key = ""; landmark = landmark_root} - (** STATE **) type profile_output = @@ -257,7 +46,6 @@ type profile_format = | JSON | Textual of textual_option -let profiling_ref = ref false let profile_with_debug = ref false let profile_with_allocated_bytes = ref false let profile_with_sys_time = ref false @@ -265,8 +53,6 @@ let profile_output = ref Silent let profile_format = ref (Textual {threshold = 1.0}) let profile_recursive = ref false -let profiling () = !profiling_ref - (** REGISTERING **) let last_landmark_id = ref 1 @@ -281,16 +67,21 @@ let landmarks_of_key = W.create 17 let iter_registered_landmarks f = W.iter (fun {landmark; _} -> f landmark) landmarks_of_key -let landmark_of_id user_id = +let dummy_key st = + { key = ""; landmark = dummy_landmark st} + +let landmark_of_id st user_id = + let dummy_key = dummy_key st in match W.find_opt landmarks_of_key {dummy_key with key = user_id} with | None -> None | Some {landmark; _} -> Some landmark -let new_landmark ~key:key_string ~name ~location ~kind () = +let new_landmark st ~key ~name ~location ~kind () = let id = !last_landmark_id in incr last_landmark_id; - let rec res = - { + let dummy_node = dummy_node st in + let res = + landmark_of_landmark_body st { id; name; location; @@ -300,52 +91,47 @@ let new_landmark ~key:key_string ~name ~location ~kind () = last_self = dummy_node; last_son = dummy_node; } - and key = { landmark = res; key = key_string} in - W.add landmarks_of_key key; + in + W.add landmarks_of_key { key; landmark = res }; res -let node_id_ref = ref 0 -let allocated_nodes = ref [] -let new_node landmark = +let new_node st landmark = if !profile_with_debug then Printf.eprintf "[Profiling] Allocating new node for %s...\n%!" landmark.name; - let id = !node_id_ref in - incr node_id_ref; + let id = get_incr_node_id_ref st in let node = { landmark; id; - fathers = Stack.make Array dummy_node 1; + fathers = Stack.make Array (dummy_node st) 1; distrib = Stack.make Float 0.0 0; - children = SparseArray.make dummy_node 7; + children = SparseArray.make (dummy_node st) 7; calls = 0; recursive_calls = 0; timestamp = Int64.zero; floats = new_floats (); } in - allocated_nodes := node :: !allocated_nodes; + set_allocated_nodes st (node :: get_allocated_nodes st); node -let current_root_node = ref (new_node landmark_root) - -let landmark_of_node ({landmark_id = key; name; location; kind; _} : Graph.node) = - match landmark_of_id key with - | None -> new_landmark ~key ~name ~kind ~location () +let landmark_of_node st ({landmark_id = key; name; location; kind; _} : Graph.node) = + match landmark_of_id st key with + | None -> new_landmark st ~key ~name ~kind ~location () | Some landmark -> landmark -let register_generic ~id ~name ~location ~kind () = - let landmark = new_landmark ~key:id ~name ~location ~kind () in +let register_generic st ~id ~name ~location ~kind () = + let landmark = new_landmark st ~key:id ~name ~location ~kind () in if !profile_with_debug then Printf.eprintf "[Profiling] registering(%s)\n%!" name; landmark -let register_generic ~id ~location kind name = - match landmark_of_id id with - | None -> register_generic ~id ~name ~location ~kind () +let register_generic st ~id ~location kind name = + match landmark_of_id st id with + | None -> register_generic st ~id ~name ~location ~kind () | Some lm -> lm -let register_generic ?id ?location kind name = +let register_generic st ?id ?location kind name = let location = match location with | Some name -> name @@ -366,137 +152,101 @@ let register_generic ?id ?location kind name = | Some key -> key | None -> name^"-"^location in - register_generic ~id ~location kind name - -let register ?id ?location name = - register_generic ?id ?location Graph.Normal name - -let register_counter name = register_generic Graph.Counter name - -let register_sampler name = register_generic Graph.Sampler name - -let current_node_ref = ref !current_root_node -let cache_miss_ref = ref 0 + register_generic st ~id ~location kind name -let stamp_root () = - !current_root_node.timestamp <- clock (); +let stamp_root current_root_node = + current_root_node.timestamp <- (clock ()); if !profile_with_allocated_bytes then begin - !current_root_node.floats.allocated_bytes <- allocated_bytes (); - !current_root_node.floats.allocated_bytes_major <- allocated_bytes_major () + current_root_node.floats.allocated_bytes <- allocated_bytes (); + current_root_node.floats.allocated_bytes_major <- allocated_bytes_major () end; if !profile_with_sys_time then - !current_root_node.floats.sys_time <- Sys.time () - -let clear_cache () = - let reset_landmark landmark = - landmark.last_son <- dummy_node; - landmark.last_parent <- dummy_node; - landmark.last_self <- dummy_node; - in - iter_registered_landmarks reset_landmark - -type profiling_state = { - root : node; - nodes: node_info list; - nodes_len: int; - current: node; - cache_miss: int -} -and node_info = { - node: node; - recursive: bool; -} - -let profiling_stack = - let dummy = - {root = dummy_node; current = dummy_node; nodes = [{node = dummy_node; recursive = false}]; cache_miss = 0; nodes_len = 1} - in - Stack.make Array dummy 7 + current_root_node.floats.sys_time <- Sys.time () -let reset () = +let reset_state st = if !profile_with_debug then Printf.eprintf "[Profiling] resetting ...\n%!"; - (* reset dummy_node *) - let floats = !current_root_node.floats in + let current_root_node = get_current_root_node st in + let floats = current_root_node.floats in floats.time <- 0.0; floats.allocated_bytes <- 0; floats.sys_time <- 0.0; - !current_root_node.calls <- 0; - !current_root_node.recursive_calls <- 0; - stamp_root (); - SparseArray.reset !current_root_node.children; - allocated_nodes := [!current_root_node]; - current_node_ref := !current_root_node; - cache_miss_ref := 0; - clear_cache (); - node_id_ref := 1 - -let () = reset () - -let push_profiling_state () = + current_root_node.calls <- 0; + current_root_node.recursive_calls <- 0; + stamp_root current_root_node; + SparseArray.reset current_root_node.children; + set_allocated_nodes st [current_root_node]; + set_current_node_ref st current_root_node; + set_cache_miss_ref st 0; + clear_cache iter_registered_landmarks st; + set_node_id_ref st 1 + + +let push_profiling_state st = if !profile_with_debug then Printf.eprintf "[Profiling] Push profiling state ....\n%!"; - let state = - let node_info node = + let profiling_state = + let node_info (node: node) = let recursive = node.landmark.last_self == node in { node; recursive } in { - root = !current_root_node; - nodes = List.map node_info !allocated_nodes; - nodes_len = !node_id_ref; - current = !current_node_ref; - cache_miss = !cache_miss_ref; + root = get_current_root_node st; + nodes = List.map node_info (get_allocated_nodes st); + nodes_len = get_node_id_ref st; + current = get_current_node_ref st; + cache_miss = get_cache_miss_ref st; } in - clear_cache (); - current_root_node := new_node landmark_root; - current_node_ref := !current_root_node; - cache_miss_ref := 0; - allocated_nodes := [!current_root_node]; - node_id_ref := 1; - reset (); - Stack.push profiling_stack state - -let pop_profiling_state () = + clear_cache iter_registered_landmarks st; + set_current_root_node st (new_node st (landmark_root st)); + set_current_node_ref st (get_current_root_node st); + set_cache_miss_ref st 0; + set_allocated_nodes st [get_current_root_node st]; + set_node_id_ref st 1; + reset_state st; + Stack.push (get_profiling_stack st) profiling_state + +let pop_profiling_state st = + let profiling_stack = get_profiling_stack st in if profiling_stack.size > 0 then let {root; nodes; nodes_len; current; cache_miss} = Stack.pop profiling_stack in - current_root_node := root; - current_node_ref := current; - cache_miss_ref := cache_miss; - allocated_nodes := List.map (fun {node; recursive} -> if recursive then node.landmark.last_self <- node; node) nodes; - node_id_ref := nodes_len + set_current_root_node st root; + set_current_node_ref st current; + set_cache_miss_ref st cache_miss; + set_allocated_nodes st (List.map (fun {node; recursive} -> if recursive then node.landmark.last_self <- node; node) nodes); + set_node_id_ref st nodes_len -let unroll_until node = +let unroll_until st node = while - let current_node = !current_node_ref in + let current_node = get_current_node_ref st in current_node != node && Stack.size current_node.fathers > 0 - && (current_node_ref := Stack.pop current_node.fathers; true) + && (set_current_node_ref st (Stack.pop current_node.fathers); true) do () done -let landmark_failure msg = - unroll_until !current_root_node; - if !current_node_ref != !current_root_node then - reset (); +let landmark_failure st msg = + unroll_until st (get_current_root_node st); + if get_current_node_ref st != get_current_root_node st then + reset_state st; if !profile_with_debug then (Printf.eprintf "Landmark error: %s\n%!" msg; Stdlib.exit 2) else raise (LandmarkFailure msg) -let get_entering_node ({id;_} as landmark) = - let current_node = !current_node_ref in +let get_entering_node st ({ id; _ } as landmark: landmark_body) = + let current_node = get_current_node_ref st in (* Read the "cache". *) - if current_node == landmark.last_parent && landmark.last_son != dummy_node then + if current_node == landmark.last_parent && landmark.last_son != dummy_node st then landmark.last_son else begin - incr cache_miss_ref; + incr_cache_miss_ref st; (* We fetch the son or create it. *) let children = current_node.children in let son = try SparseArray.get children id with Not_found -> - let son = new_node landmark in + let son = new_node st landmark in SparseArray.set current_node.children id son; son in @@ -506,38 +256,42 @@ let get_entering_node ({id;_} as landmark) = son end -let get_exiting_node current_node = - if Stack.size current_node.fathers = 0 then - landmark_failure "Stack underflow" +let get_exiting_node st = + let current_node_ref = get_current_node_ref st in + if Stack.size current_node_ref.fathers = 0 then + landmark_failure st "Stack underflow" else - Stack.pop current_node.fathers + Stack.pop current_node_ref.fathers -let increment ?(times = 1) counter = - let node = get_entering_node counter in +let increment st ?(times = 1) counter = + let counter = get_ds_landmark st counter in + let node = get_entering_node st counter in node.calls <- node.calls + times -let increment ?times counter = - if !profiling_ref then - increment ?times counter +let increment st ?times counter = + if profiling st then + increment st ?times counter -let sample sampler x = - let node = get_entering_node sampler in +let sample st sampler x = + let sampler = get_ds_landmark st sampler in + let node = get_entering_node st sampler in node.calls <- node.calls + 1; Stack.push node.distrib x -let sample sampler x = - if !profiling_ref then - sample sampler x +let sample st sampler x = + if profiling st then + sample st sampler x -let enter landmark = +let enter_landmark st landmark = + let dummy_node = dummy_node st in if !profile_with_debug then Printf.eprintf "[Profiling] enter%s(%s)\n%!" (if landmark.last_self != dummy_node then " recursive " else "") landmark.name; if landmark.last_self == dummy_node || !profile_recursive then begin - let node = get_entering_node landmark in + let node = get_entering_node st landmark in node.calls <- node.calls + 1; - Stack.push node.fathers !current_node_ref; - current_node_ref := node; + Stack.push node.fathers (get_current_node_ref st); + set_current_node_ref st node; landmark.last_self <- node; node.timestamp <- clock (); if !profile_with_allocated_bytes then begin @@ -552,7 +306,7 @@ let enter landmark = last_self.calls <- last_self.calls + 1 end -let mismatch_recovering landmark current_node = +let mismatch_recovering st (landmark: landmark_body) (current_node: node) = let expected_landmark = current_node.landmark in if expected_landmark != landmark then begin let msg = @@ -561,10 +315,11 @@ let mismatch_recovering landmark current_node = expected_landmark.name expected_landmark.location in Printf.eprintf "Warning: %s\n%!" msg; - unroll_until landmark.last_self; - if landmark != !current_node_ref.landmark then begin - reset (); - landmark_failure ("unable to recover from "^msg) + unroll_until st landmark.last_self; + let current_node = get_current_node_ref st in + if landmark != current_node.landmark then begin + reset_state st; + failwith ("unable to recover from "^msg) end end @@ -584,46 +339,48 @@ let aggregate_stat_for current_node = floats.sys_time <- floats.sys_time +. (Sys.time () -. floats.sys_timestamp) -let exit landmark = +let exit_landmark st landmark = + let current_node = get_current_node_ref st in if !profile_with_debug then - Printf.eprintf "[Profiling] exit%s(%s)\n%!" (if landmark.last_self != !current_node_ref then " recursive " else "") landmark.name; - let current_node = !current_node_ref in + Printf.eprintf "[Profiling] exit%s(%s)\n%!" (if landmark.last_self != current_node then " recursive " else "") landmark.name; let last_self = landmark.last_self in if last_self.recursive_calls = 0 || !profile_recursive then begin - mismatch_recovering landmark current_node; + mismatch_recovering st landmark current_node; if Stack.size current_node.fathers = 1 then begin - landmark.last_self <- dummy_node; + landmark.last_self <- dummy_node st; aggregate_stat_for current_node; end; - current_node_ref := get_exiting_node current_node + set_current_node_ref st (get_exiting_node st) end else if not !profile_recursive then last_self.recursive_calls <- last_self.recursive_calls - 1 (* These two functions should be inlined. *) -let enter landmark = - if !profiling_ref then - enter landmark +let enter_landmark st landmark = + if profiling st then + enter_landmark st landmark -let exit landmark = - if !profiling_ref then - exit landmark +let exit_landmark st landmark = + if profiling st then + exit_landmark st landmark (** HELPERS **) -let wrap node f x = - enter node; +let wrap st node f x = + let node = get_ds_landmark st node in + enter_landmark st node; try let res = f x in - exit node; + exit_landmark st node; res with LandmarkFailure _ as e -> raise e - | e -> exit node; raise e + | e -> exit_landmark st node; raise e -let unsafe_wrap node f x = - enter node; +let unsafe_wrap st node f x = + let node = get_ds_landmark st node in + enter_landmark st node; let res = f x in - exit node; + exit_landmark st node; res (** PROFILERS **) @@ -663,8 +420,8 @@ let profiling_options () = { format = !profile_format } -let start_profiling ?(profiling_options = default_options) () = - if !profiling_ref then +let start_profiling st ~profiling_options () = + if profiling st then failwith "In profiling: it is not allowed to nest profilings."; set_profiling_options profiling_options; if !profile_with_debug then @@ -674,25 +431,26 @@ let start_profiling ?(profiling_options = default_options) () = | true, false -> "with garbage collection statistics" | false, true -> "with system time" | false, false -> ""); - profiling_ref := true - -let rec exit_until_root () = - if !current_node_ref != !current_root_node then begin - let landmark = !current_node_ref.landmark in - exit landmark; - exit_until_root (); + set_profiling st true + +let rec exit_until_root st = + let current_node_ref = get_current_node_ref st in + if current_node_ref != get_current_root_node st then begin + let landmark = current_node_ref.landmark in + exit_landmark st landmark; + exit_until_root st; end -let stop_profiling () = - if not !profiling_ref then +let stop_profiling_st st = + if not (profiling st) then failwith "In profiling: cannot stop since profiling is not on-going"; - exit_until_root (); - let current_node = !current_node_ref in - assert (current_node == !current_root_node); + exit_until_root st; + let current_node = get_current_node_ref st in + assert (current_node == (get_current_root_node st)); aggregate_stat_for current_node; if !profile_with_debug then Printf.eprintf "[Profiling] Stop profiling.\n%!"; - profiling_ref := false + set_profiling st false (** EXPORTING / IMPORTING SLAVE PROFILINGS **) @@ -704,9 +462,9 @@ let array_list_map f l = let res = Array.make size (f hd) in List.iteri (fun k x -> res.(k+1) <- f x) tl; res -let export ?(label = "") () = +let export_aux st label = let export_node {landmark; id; calls; floats; children; distrib; _} = - let {key = { key = landmark_id; _}; name; location; kind; _} = landmark in + let {key = landmark_id; name; location; kind; _} = landmark in let {time; allocated_bytes; allocated_bytes_major; sys_time; _} = floats in let children = List.map (fun ({id;_} : node) -> id) (SparseArray.values children) @@ -714,25 +472,16 @@ let export ?(label = "") () = {Graph.landmark_id; id; name; location; calls; time; kind; allocated_bytes; allocated_bytes_major; sys_time; children; distrib = Stack.to_floatarray distrib} in - if !profiling_ref then begin - aggregate_stat_for !current_root_node; - stamp_root () + if profiling st then begin + let root_node = get_current_root_node st in + aggregate_stat_for root_node; + stamp_root root_node end; - let all_nodes = List.rev !allocated_nodes in + let all_nodes = List.rev (get_allocated_nodes st) in let nodes = array_list_map export_node all_nodes in {Graph.nodes; label; root = 0} -let export_and_reset ?label () = - let profiling = !profiling_ref in - if profiling then - stop_profiling (); - let res = export ?label () in - reset (); - if profiling then - start_profiling (); - res - -let rec merge_branch node graph (imported : Graph.node) = +let rec merge_branch st (node:node) graph (imported : Graph.node) = let floats = node.floats in floats.time <- imported.time +. floats.time; floats.sys_time <- imported.sys_time +. floats.sys_time; @@ -744,16 +493,18 @@ let rec merge_branch node graph (imported : Graph.node) = let children = Graph.children graph imported in List.iter (fun (imported_son : Graph.node) -> - let landmark = landmark_of_node imported_son in + let landmark = landmark_of_node st imported_son in + let landmark = get_ds_landmark st landmark in match SparseArray.get node.children landmark.id with | exception Not_found -> - new_branch node graph imported_son - | son -> merge_branch son graph imported_son + new_branch st node graph imported_son + | son -> merge_branch st son graph imported_son ) children -and new_branch parent graph (imported : Graph.node) = - let landmark = landmark_of_node imported in - let node = new_node landmark in +and new_branch st parent graph (imported : Graph.node) = + let landmark = landmark_of_node st imported in + let landmark = get_ds_landmark st landmark in + let node = new_node st landmark in node.calls <- imported.calls; let floats = node.floats in floats.time <- imported.time; @@ -761,18 +512,83 @@ and new_branch parent graph (imported : Graph.node) = floats.sys_time <- imported.sys_time; Float.Array.iter (Stack.push node.distrib) imported.distrib; SparseArray.set parent.children landmark.id node; - List.iter (new_branch node graph) (Graph.children graph imported) + List.iter (new_branch st node graph) (Graph.children graph imported) + +let merge_aux st node graph = + merge_branch st node graph (Graph.root graph) + +(** API **) + +let get_state = + init ~reset_state ~new_node ~stop_profiling:stop_profiling_st + ~export:export_aux + +let register ?id ?location name = + register_generic ?id ?location (get_state ()) Graph.Normal name + +let landmark_of_id used_id = landmark_of_id (get_state ()) used_id + +let enter landmark = + let st = get_state () in + let landmark = get_ds_landmark st landmark in + enter_landmark st landmark + +let exit landmark = + let st = get_state () in + let landmark = get_ds_landmark st landmark in + exit_landmark st landmark + +let wrap node f x = wrap (get_state ()) node f x + +let unsafe_wrap node f x = unsafe_wrap (get_state ()) node f x + +let register_counter name = register_generic (get_state ()) Graph.Counter name + +let increment ?times counter = increment (get_state ()) ?times counter + +let register_sampler name = register_generic (get_state ()) Graph.Sampler name + +let sample sampler x = sample (get_state ()) sampler x + +let profiling () = profiling (get_state ()) + +let start_profiling ?(profiling_options = default_options) () = + start_profiling (get_state ()) ~profiling_options () + +let stop_profiling () = stop_profiling_st (get_state ()) + +let reset () = reset_state (get_state ()) + +let push_profiling_state () = push_profiling_state (get_state ()) + +let pop_profiling_state () = pop_profiling_state (get_state ()) + let merge (graph : Graph.graph) = if !profile_with_debug then Printf.eprintf "[Profiling] merging foreign graph\n%!"; - merge_branch !current_root_node graph (Graph.root graph) + let st = get_state () in + merge_aux st (get_current_root_node st) graph + +let export_and_reset ?(label = "") () = + let st = get_state () in + let profiling = profiling () in + if profiling then + stop_profiling_st st; + let res = export ~export:export_aux ~merge:merge_aux ~label st in + reset_state st; + if profiling then + stop_profiling_st st; + res + +let export ?(label = "") () = + export ~export:export_aux ~merge:merge_aux ~label (get_state ()) let exit_hook () = if !profile_with_debug then Printf.eprintf "[Profiling] exit_hook\n%!"; - if !profiling_ref then begin - stop_profiling (); + if profiling () then begin + stop_profiling_st (get_state ()); let label = String.concat " " (Array.to_list Sys.argv) in @@ -796,9 +612,6 @@ let exit_hook () = close_out oc end -let () = Stdlib.at_exit exit_hook - - let parse_env_options s = let open Printf in let debug = ref false in @@ -884,7 +697,10 @@ let parse_env_options s = {debug = !debug; allocated_bytes = !allocated_bytes; sys_time = !sys_time; output = !output; format = !format; recursive = !recursive} -let () = match Sys.getenv "OCAML_LANDMARKS" with +let () = + reset (); + Stdlib.at_exit exit_hook; + match Sys.getenv "OCAML_LANDMARKS" with | exception Not_found -> () | str -> try start_profiling ~profiling_options:(parse_env_options str) () diff --git a/src/landmark_state.mli b/src/landmark_state.mli new file mode 100644 index 0000000..2635c93 --- /dev/null +++ b/src/landmark_state.mli @@ -0,0 +1,52 @@ +(* This file is released under the terms of an MIT-like license. *) +(* See the attached LICENSE file. *) +(* Copyright (C) 2000-2025 LexiFi *) + +open Utils + +type t + +type landmark + +val landmark_of_landmark_body: t -> landmark_body -> landmark +val get_ds_landmark: t -> landmark -> landmark_body + +val init: + reset_state:(t -> unit) -> + new_node:(t -> landmark_body -> node) -> + stop_profiling:(t -> unit) -> + export:(t -> string -> Graph.graph) -> + unit -> + t + +val dummy_landmark : t -> landmark +val landmark_root : t -> landmark_body +val dummy_node : t -> node + +val profiling : t -> bool +val set_profiling : t -> bool -> unit + +val get_node_id_ref : t -> int +val set_node_id_ref : t -> int -> unit +val get_incr_node_id_ref : t -> int +val get_allocated_nodes : t -> node list +val set_allocated_nodes : t -> node list -> unit + +val get_current_root_node : t -> node +val set_current_root_node : t -> node -> unit +val get_current_node_ref : t -> node +val set_current_node_ref : t -> node -> unit +val get_cache_miss_ref : t -> int +val set_cache_miss_ref : t -> int -> unit + +val incr_cache_miss_ref : t -> unit + +val get_profiling_stack : + t -> + (profiling_state, profiling_state array) Utils.Stack.t + +val clear_cache : ((landmark -> unit) -> unit) -> t -> unit +val export : + export:(t -> string -> Graph.graph) -> + merge:(t -> node -> Graph.graph -> unit) -> + ?label:string -> t -> Graph.graph diff --git a/src/landmark_state_ocaml4.ml b/src/landmark_state_ocaml4.ml new file mode 100644 index 0000000..9f92ab0 --- /dev/null +++ b/src/landmark_state_ocaml4.ml @@ -0,0 +1,99 @@ +(* This file is released under the terms of an MIT-like license. *) +(* See the attached LICENSE file. *) +(* Copyright (C) 2000-2025 LexiFi *) + +open Utils + +module Stack = Utils.Stack + +type landmark = landmark_body + +type t = unit + +let landmark_of_landmark_body () l = l + +let rec landmark_root = { + kind = Graph.Root; + id = 0; + name = "ROOT"; + location = __FILE__; + key = ""; + last_parent = dummy_node; + last_son = dummy_node; + last_self = dummy_node; +} + +and dummy_node = { + landmark = landmark_root; + id = 0; + children = SparseArray.dummy (); + fathers = Stack.dummy Array; + floats = new_floats (); + calls = 0; + recursive_calls = 0; + distrib = Stack.dummy Float; + timestamp = Int64.zero +} + +let dummy_landmark () = landmark_root + +let clear_cache iter_registered_landmarks () = + iter_registered_landmarks ( + fun landmark -> + landmark.last_son <- dummy_node; + landmark.last_parent <- dummy_node; + landmark.last_self <- dummy_node; + ) + +let landmark_root () = landmark_root +let dummy_node () = dummy_node + +let profiling_ref = ref false +let profiling () = !profiling_ref +let set_profiling () b = profiling_ref := b +let get_ds_landmark () l = l + +let node_id_ref = ref 0 +let get_node_id_ref () = !node_id_ref +let set_node_id_ref () n = node_id_ref := n + +let allocated_nodes = ref [] +let get_allocated_nodes () = !allocated_nodes +let set_allocated_nodes () l = allocated_nodes := l + +let get_incr_node_id_ref () = + let id = !node_id_ref in + incr node_id_ref; + id + +let current_root_node = ref (dummy_node ()) +let current_node_ref = ref !current_root_node + +let init ~reset_state:_ ~new_node ~stop_profiling:_ ~export:_ = + current_root_node := new_node () (landmark_root ()); + current_node_ref := !current_root_node; + fun () -> () + +let get_current_root_node () = !current_root_node +let get_current_node_ref () = !current_node_ref +let set_current_root_node () node = current_root_node := node +let set_current_node_ref () node = current_node_ref := node + +let cache_miss_ref = ref 0 +let get_cache_miss_ref () = !cache_miss_ref +let set_cache_miss_ref () n = cache_miss_ref := n + +let profiling_stack = + let dummy = { + root = dummy_node (); + current = dummy_node (); + nodes = [{node = dummy_node (); recursive = false}]; + cache_miss = 0; + nodes_len = 1} + in + Stack.make Array dummy 7 +let incr_cache_miss_ref () = incr cache_miss_ref +let get_profiling_stack () = profiling_stack + +let export ~export ~merge:_ ?(label = "") () = + export () label diff --git a/src/landmark_state_ocaml5.ml b/src/landmark_state_ocaml5.ml new file mode 100644 index 0000000..0581008 --- /dev/null +++ b/src/landmark_state_ocaml5.ml @@ -0,0 +1,190 @@ +(* This file is released under the terms of an MIT-like license. *) +(* See the attached LICENSE file. *) +(* Copyright (C) 2000-2025 LexiFi *) + +open Utils + +module Stack = Utils.Stack + +type landmark = landmark_body Lazy.t Domain.DLS.key +(* _ Lazy.t is used to ensure that the landmark instance of a child domain is + created in runtime of the child domain. *) + +type nodes = { + mutable node_id_ref: int; + mutable allocated_nodes: node list; +} + +type t = { + landmark_root: landmark_body; + dummy_node : node; + + nodes: nodes; + + mutable profiling_ref : bool; + mutable cache_miss_ref: int; + profiling_stack: (profiling_state, profiling_state array) Stack.t; + + mutable current_root_node : node; + mutable current_node_ref : node; + + mutable child_states : t list; + (* The states of child domains spawned by the main one *) + mutable graph: Graph.graph; + (* Used by child states to store their own graphs *) + + mutable registered: bool; +} + +let init_nodes () = { + node_id_ref = 0; + allocated_nodes = []; +} + +let get_incr_node_id_ref st = + let id = st.nodes.node_id_ref in + st.nodes.node_id_ref <- id + 1; + id + +let init ~reset_state ~new_node ~stop_profiling ~export = + let init_state () = + let rec landmark_root = { + kind = Graph.Root; + id = 0; + name = "ROOT"; + location = __FILE__; + key = ""; + last_parent = dummy_node; + last_son = dummy_node; + last_self = dummy_node; + } + and dummy_node = { + landmark = landmark_root; + id = 0; + children = SparseArray.dummy (); + fathers = Stack.dummy Array; + floats = new_floats (); + calls = 0; + recursive_calls = 0; + distrib = Stack.dummy Float; + timestamp = Int64.zero + } + in + let nodes = init_nodes () in + let st = { + landmark_root; + dummy_node; + nodes; + profiling_ref = false; + cache_miss_ref = 0; + profiling_stack = ( + let dummy = { + root = dummy_node; + current = dummy_node; + nodes = [{node = dummy_node; recursive = false}]; + cache_miss = 0; + nodes_len = 1} + in + Stack.make Array dummy 7); + child_states = []; + graph = {nodes = [||]; label = ""; root = 0 }; + registered = false; + (* Temprory *) + current_root_node = dummy_node; + current_node_ref = dummy_node; + } + in + let root_node = new_node st landmark_root in + { st with current_root_node = root_node; current_node_ref = root_node } + in + let state = + Domain.DLS.new_key + ~split_from_parent:(fun s -> + let child_state = init_state () in + let child_state = + { child_state with + profiling_ref = s.profiling_ref } + in + s.child_states <- child_state :: s.child_states; + child_state.profiling_ref <- s.profiling_ref; + reset_state child_state; + child_state + ) + init_state + in + fun () -> + let st = Domain.DLS.get state in + if not st.registered && not (Domain.is_main_domain ()) then ( + Domain.at_exit (fun () -> + stop_profiling st; + st.graph <- export st "" + ); + st.registered <- true; + ); + st + +let dummy_landmark st = Domain.DLS.new_key (fun () -> lazy st.landmark_root) +(* Only used for search in the weak HashSet, will never be accessed *) + +let landmark_root st = st.landmark_root +let dummy_node st = st.dummy_node + +let get_ds_landmark _st (l: landmark) = Lazy.force (Domain.DLS.get l) + +let landmark_of_landmark_body st (l: landmark_body): landmark = + Domain.DLS.new_key + ~split_from_parent:( + fun l -> + let { id; name; location; kind; key; _ } = Lazy.force l in + lazy ({ + id; + name; + location; + kind; + key; + last_parent = dummy_node st; + last_self = dummy_node st; + last_son = dummy_node st; + })) + (fun () -> lazy l) + +let clear_cache iter_registered_landmarks gls: unit = + iter_registered_landmarks ( + fun landmark -> + let landmark = get_ds_landmark () landmark in + landmark.last_son <- gls.dummy_node; + landmark.last_parent <- gls.dummy_node; + landmark.last_self <- gls.dummy_node; + ) + +let profiling st = st.profiling_ref +let set_profiling st b = st.profiling_ref <- b + +let get_node_id_ref st = st.nodes.node_id_ref +let set_node_id_ref st n = st.nodes.node_id_ref <- n +let get_allocated_nodes st = st.nodes.allocated_nodes +let set_allocated_nodes st l = st.nodes.allocated_nodes <- l + +let get_current_root_node st = st.current_root_node +let set_current_root_node st (node: node) = + st.current_root_node <- node + +let get_current_node_ref st = st.current_node_ref +let set_current_node_ref st (node: node) = + st.current_node_ref <- node + +let get_cache_miss_ref st = st.cache_miss_ref +let set_cache_miss_ref st n = st.cache_miss_ref <- n +let incr_cache_miss_ref st = st.cache_miss_ref <- st.cache_miss_ref + 1 +let get_profiling_stack st = st.profiling_stack + +let rec merge_child_state_graphs ~merge state = + List.iter ( + fun st -> + merge_child_state_graphs ~merge st; + merge st state.current_root_node st.graph + ) state.child_states + +let export ~export ~merge ?(label = "") state = + merge_child_state_graphs ~merge state; + export state label diff --git a/src/utils.ml b/src/utils.ml new file mode 100644 index 0000000..326141d --- /dev/null +++ b/src/utils.ml @@ -0,0 +1,208 @@ +(* This file is released under the terms of an MIT-like license. *) +(* See the attached LICENSE file. *) +(* Copyright (C) 2000-2025 LexiFi *) + +module SparseArray = struct + type 'a t = { + mutable keys : int array; + mutable data : 'a array; + mutable size : int; + } + + (* /!\ Dummy cannot be resized. *) + let dummy () = { keys = [||]; data = [||]; size = 0 } + + let make null n = + let n = max n 1 in + { + keys = Array.make n 0; + data = Array.make n null; + size = 0; + } + + let reset sparse_array = sparse_array.size <- 0 + + let get t id = + let {keys; data; size} = t in + let min = ref 0 in + let max = ref (size - 1) in + while !min < !max do + let middle = (!min + !max) / 2 in + if Array.unsafe_get keys middle < id then + min := middle + 1 + else + max := middle + done; + let idx = !min in + if idx = !max && + Array.unsafe_get keys idx = id then + Array.unsafe_get data idx + else + raise Not_found + + let swap a i j = + let t = a.(i) in + a.(i) <- a.(j); + a.(j) <- t + + let values {data; size; _} = + let result = ref [] in + for k = 0 to size-1 do + result := data.(k) :: !result; + done; + List.rev !result + + let bubble {keys; data; size} = + let pos = ref size in + let key = keys.(size) in + while + let p = !pos in + let q = p - 1 in + if key < keys.(q) then begin + swap keys p q; + swap data p q; + pos := q; + q > 0 + end else false + do () done + + let is_full ({keys; size; _}) = Array.length keys = size + + let resize ({keys; data; size} as sparse_array) = + if is_full sparse_array then begin + assert (size > 0); + let new_length = (2 * (size + 1)) - 1 in + sparse_array.keys <- Array.make new_length 0; + sparse_array.data <- Array.make new_length sparse_array.data.(0); + Array.blit keys 0 sparse_array.keys 0 size; + Array.blit data 0 sparse_array.data 0 size; + end + + let set sparse_array id node = + resize sparse_array; + let size = sparse_array.size in + sparse_array.keys.(size) <- id; + sparse_array.data.(size) <- node; + if size > 0 then + bubble sparse_array; + sparse_array.size <- sparse_array.size + 1 +end + +module Stack = struct + module A = struct + type (_, _) kind = + | Array : ('a, 'a array) kind + | Float : (float, floatarray) kind + let empty : type a arr. (a, arr) kind -> arr = function + | Array -> [||] + | Float -> Float.Array.create 0 + let make : type a arr. (a, arr) kind -> int -> a -> arr = fun kind n null -> + match kind with + | Array -> Array.make n null + | Float -> Float.Array.make n null + let length : type a arr. (a, arr) kind -> arr -> int = fun kind arr -> + match kind with + | Array -> Array.length arr + | Float -> Float.Array.length arr + let get : type a arr. (a, arr) kind -> arr -> int -> a = fun kind arr n -> + match kind with + | Array -> Array.get arr n + | Float -> Float.Array.get arr n + let set : type a arr. (a, arr) kind -> arr -> int -> a -> unit = fun kind arr n -> + match kind with + | Array -> Array.set arr n + | Float -> Float.Array.set arr n + let blit : type a arr. (a, arr) kind -> arr -> int -> arr -> int -> int -> unit = fun kind src srcpos dst dstpos n -> + match kind with + | Array -> Array.blit src srcpos dst dstpos n + | Float -> Float.Array.blit src srcpos dst dstpos n + end + type ('a, 'arr) t = { + kind : ('a, 'arr) A.kind; + mutable data : 'arr; + mutable size : int + } + (* /!\ Dummy cannot be resized. *) + let dummy kind = { kind; data = A.empty kind; size = 0 } + let make kind null n = { kind; data = A.make kind (max 1 n) null; size = 0 } + let size {size; _} = size + let resize ({kind; size; data} as stack) = + if size = A.length kind data then begin + assert (size > 0); + let new_length = (2 * (size + 1)) - 1 in + stack.data <- A.make kind new_length (A.get kind data 0); + A.blit kind data 0 stack.data 0 size; + end + + let push stack x = + resize stack; + A.set stack.kind stack.data stack.size x; + stack.size <- stack.size + 1 + + let pop stack = + stack.size <- stack.size - 1; + A.get stack.kind stack.data stack.size + + let to_floatarray {data; size; _} = Float.Array.sub data 0 size +end + +type landmark_body = { + id: int; + key: string; + kind : Graph.kind; + name: string; + location: string; + + + mutable last_parent: node; + mutable last_son: node; + mutable last_self: node; +} + +and node = { + landmark: landmark_body; + + id: int; + + children: node SparseArray.t; + fathers: (node, node array) Stack.t; + + mutable calls: int; + mutable recursive_calls: int; + mutable timestamp: Int64.t; + distrib: (float, floatarray) Stack.t; + floats : floats; +} + +and floats = { + mutable time: float; + mutable allocated_bytes: int; + mutable allocated_bytes_stamp: int; + mutable allocated_bytes_major: int; + mutable allocated_bytes_major_stamp: int; + mutable sys_time: float; + mutable sys_timestamp: float; +} + +let new_floats () = { + time = 0.0; + allocated_bytes = 0; + allocated_bytes_stamp = 0; + allocated_bytes_major = 0; + allocated_bytes_major_stamp = 0; + sys_time = 0.0; + sys_timestamp = 0.0 +} + +type profiling_state = { + root : node; + nodes: node_info list; + nodes_len: int; + current: node; + cache_miss: int +} + +and node_info = { + node: node; + recursive: bool; +} diff --git a/src/utils.mli b/src/utils.mli new file mode 100644 index 0000000..afcb8dc --- /dev/null +++ b/src/utils.mli @@ -0,0 +1,99 @@ +(* This file is released under the terms of an MIT-like license. *) +(* See the attached LICENSE file. *) +(* Copyright (C) 2000-2025 LexiFi *) + +module SparseArray : +sig + type 'a t = { + mutable keys : int array; + mutable data : 'a array; + mutable size : int; + } + val dummy : unit -> 'a t + val make : 'a -> int -> 'a t + val reset : 'a t -> unit + val get : 'a t -> int -> 'a + val swap : 'a array -> int -> int -> unit + val values : 'a t -> 'a list + val bubble : 'a t -> unit + val is_full : 'a t -> bool + val resize : 'a t -> unit + val set : 'a t -> int -> 'a -> unit +end + +module Stack : +sig + module A : + sig + type (_, _) kind = + Array : ('a, 'a array) kind + | Float : (float, floatarray) kind + val empty : ('a, 'arr) kind -> 'arr + val make : ('a, 'arr) kind -> int -> 'a -> 'arr + val length : ('a, 'arr) kind -> 'arr -> int + val get : ('a, 'arr) kind -> 'arr -> int -> 'a + val set : ('a, 'arr) kind -> 'arr -> int -> 'a -> unit + val blit : + ('a, 'arr) kind -> 'arr -> int -> 'arr -> int -> int -> unit + end + type ('a, 'arr) t = { + kind : ('a, 'arr) A.kind; + mutable data : 'arr; + mutable size : int; + } + val dummy : ('a, 'b) A.kind -> ('a, 'b) t + val make : ('a, 'b) A.kind -> 'a -> int -> ('a, 'b) t + val size : ('a, 'b) t -> int + val resize : ('a, 'b) t -> unit + val push : ('a, 'b) t -> 'a -> unit + val pop : ('a, 'b) t -> 'a + val to_floatarray : ('a, floatarray) t -> floatarray +end + +type landmark_body = { + id : int; + key : string; + kind : Graph.kind; + name : string; + location : string; + mutable last_parent : node; + mutable last_son : node; + mutable last_self : node; +} + +and node = { + landmark : landmark_body; + id : int; + children : node SparseArray.t; + fathers : (node, node array) Stack.t; + mutable calls : int; + mutable recursive_calls : int; + mutable timestamp : int64; + distrib : (float, floatarray) Stack.t; + floats : floats; +} + +and floats = { + mutable time : float; + mutable allocated_bytes : int; + mutable allocated_bytes_stamp : int; + mutable allocated_bytes_major : int; + mutable allocated_bytes_major_stamp : int; + mutable sys_time : float; + mutable sys_timestamp : float; +} + +val new_floats : unit -> floats + +type profiling_state = { + root : node; + nodes: node_info list; + nodes_len: int; + current: node; + cache_miss: int +} + +and node_info = { + node: node; + recursive: bool; +}