Mavkit_smart_rollup_wasm_debugger_lib.Profiling
module Vector = Mavryk_lazy_containers.Lazy_vector.Int32Vector
Call stack representation and construction.
The call stack computation algorithm is the following:
There are two components: the current node (or stack frame) and the continuation (a list of stack frames). There's a "toplevel node" describing the execution at the toplevel of the interpreter. A node contains:
id
: a function call representation (an identifier)t
: the ticks elapsed during the calltime
: the time elapsed during the callsub
: the subcalls.Note that for the rest of the algorithm, `time` will be eluded as its computation is equivalent to the ticks.
The algorithm starts with an empty toplevel and an empty continuation.
Let's take an example: call: f () { ...... g () { .... h () { ...... } .......... } ......... } tick: 0 ----------> 10 -------> 30 ---------> 60 --------> 100 -----> 160 10 ticks
20 ticks
30 ticks
40 ticks
60 ticks
T (nodes
) : toplevel K : continuation (list) N : current node (N(id) means it hasn't changed from previous step)
N, K |- exec
Start: T, |- f () { g () { h () { } } } ==> at tick 0 N (f, 0, ), T
|- g () { h () { } } } ==> at tick 10 N (g, 10, ), N (f, 10 - 0 = 10, []); T
|- h () { } } } ==> at tick 30 N (h, 30, ), N (g, 30 - 10 = 20, []); N(f); T
|- } } } ==> at tick 60 N (g, 60 - 20 = 40, N (h, 60 - 30 = 30, [])
), N(f); T
|- } } ==> at tick 100 N (f, 100 - 10 = 90, N (g, 100 - 40 = 60, [N(h)])
), T
|- } ==> at tick 160 T N (f, 160 - 90 = 70, [N(g, 60, [N(h, 30, [])])])
, |- _
type 'function_call call_stack =
| Node of 'function_call
* Z.t
* Ptime.span option
* 'function_call call_stack list
| Toplevel of 'function_call call_stack list
val fold_call_stack :
('a -> 'b -> Z.t -> Ptime.span option -> 'c) ->
'd ->
'e call_stack ->
'd
val end_function_call :
Z.t ->
(unit -> Ptime.span option) ->
'a call_stack ->
'b call_stack list ->
'a call_stack * 'b call_stack list
end_function_call current_tick current_function call_stack
implements an ending call. Please refer to the prelude of the file.
val call_function :
'a ->
Z.t ->
(unit -> Ptime.span option) ->
'b call_stack ->
'c call_stack list ->
'd call_stack * 'c call_stack list
call_function called_function current_tick current_function call_stack
implements a function start. Please refere to the prelude of the module.
Profiling the execution of the PVM
A function call can be either a direct call, a call through a reference or an internal step of the PVM.
val pp_call : Stdlib.Format.formatter -> function_call -> unit
val initial_eval_call : function_call
initial_eval_call
is `kernel_run` function call.
val update_on_decode :
Z.t ->
(unit -> Ptime.span option) ->
('a call_stack * 'a call_stack list) ->
Mavryk_webassembly_interpreter.Decode.module_kont ->
(function_call call_stack * 'a call_stack list) option Lwt.t
update_on_decode current_tick current_call_state
starts and stop `internal` calls related to the Decode step of the PVM.
val update_on_link :
Z.t ->
(unit -> Ptime.span option) ->
('a call_stack * 'a call_stack list) ->
Mavryk_webassembly_interpreter.Ast.module_'
Mavryk_webassembly_interpreter.Source.phrase ->
Vector.key ->
(function_call call_stack * 'a call_stack list) option Lwt.t
update_on_link current_tick current_call_state
starts and stop `internal` call to the Link step of the PVM.
val update_on_init :
Z.t ->
(unit -> Ptime.span option) ->
('a call_stack * 'a call_stack list) ->
Mavryk_webassembly_interpreter.Eval.init_kont ->
(function_call call_stack * 'a call_stack list) option Lwt.t
update_on_init current_tick current_call_state
starts and stop `internal` call to the Init step of the PVM.
val update_on_instr :
Z.t ->
(unit -> Ptime.span option) ->
'a call_stack ->
'a call_stack list ->
string Custom_section.FuncMap.t ->
Mavryk_webassembly_interpreter.Eval.admin_instr' ->
(function_call call_stack * 'a call_stack list) option Lwt.t
update_on_instr current_tick current_node call_stack
handle function calls during the evaluation.
val update_on_eval :
Z.t ->
(unit -> Ptime.span option) ->
(function_call call_stack * function_call call_stack list) ->
string Custom_section.FuncMap.t ->
Mavryk_webassembly_interpreter.Eval.step_kont ->
(function_call call_stack * function_call call_stack list) option Lwt.t
update_on_eval current_tick current_call_state
handle function calls and end during the evaluation.
val update_call_stack :
Z.t ->
(unit -> Ptime.span option) ->
(function_call call_stack * function_call call_stack list) ->
string Custom_section.FuncMap.t ->
Mavryk_scoru_wasm.Wasm_pvm_state.Internal_state.tick_state ->
(function_call call_stack * function_call call_stack list) option Lwt.t
update_call_stack current_tick current_state_call symbols state
returns the call state changes for any state. Returns None
if no change happened.
module State : sig ... end
module Make
(Wasm_utils : Mavryk_scoru_wasm_helpers.Wasm_utils_intf.S) :
sig ... end
Flamegraph building
Flamegraph are an aggregation of all the same callstacks, thus there is no longer a notion of time. We can easily collapse all nodes into a single one.
module StringMap : sig ... end
val collapse_stack :
max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
'b call_stack ->
(StringMap.key * Z.t) list
collapse_stack ~max_depth pp_call call_stack
collapses a call stack into a valid flamegraph. Node deeper than max_depth
are not considered. pp_call
is used to print the identifiers.
Pretty printing and flamegraph output
pp_indent ppf depth
prints an indentation corresponding to the given depth
.
val pp_nodes :
?max_depth:int ->
int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'b call_stack list ->
unit
val pp_stack :
?max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'a call_stack ->
unit
pp_stack ~max_depth ppf stack
pretty prints the stack. It should be used for debug only.
val pp_flame_callstack_node :
prefix:string ->
depth:int ->
max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'b call_stack ->
unit
val pp_flame_callstack_nodes :
prefix:string ->
depth:int ->
max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'b call_stack list ->
unit
val pp_callstack_as_flamegraph :
max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'a call_stack ->
unit
pp_callstack_as_flamegraph
if pp_stack
with the syntax of flamegraphs.
val pp_collapsed_flamegraph :
max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'a call_stack ->
unit
pp_flamegraph
collapses the stack and print it as a valid flamegraph.
val pp_flamegraph :
collapse:bool ->
max_depth:int ->
(Stdlib.Format.formatter -> 'a -> unit) ->
Stdlib.Format.formatter ->
'a call_stack ->
unit
pp_flamegraph ~collapsed ~max_depth pp_call ppf call_stack
outputs the given call_stack
with its `flamegraph` representation. If collapse =
true
, the stacks are collapsed. This can be useful to output smaller files, but the stack cannot be analyzed on a time basis (i.e. as a flamechart).
val aggregate_toplevel_time_and_ticks :
'a call_stack ->
('b * Z.t * Ptime.span option) list
aggregate_toplevel_time_and_ticks ~call_stack
counts the time and ticks spent in each toplevel phases during an execution.
val pp_ticks_and_time :
Stdlib.Format.formatter ->
(function_call * Z.t * Ptime.span option) ->
unit