Skip to content

Commit 5a9658d

Browse files
authored
Merge pull request #1076 from ejgallego/flame_fcc
[fcc] New option --trace-file for flamegraph generation.
2 parents 436f1d0 + e7e04b5 commit 5a9658d

File tree

8 files changed

+123
-11
lines changed

8 files changed

+123
-11
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
(@ejgallego, #590)
7979
- [plugins] New plugin to dump document data (@ejgallego, Stefania
8080
Dumbrava, #1075)
81+
- [fcc] New option `--trace-file` that will generate trace data for
82+
visualizing with perfetto.dev (@ejgallego, AlexJ, Gaëtan Gilbert,
83+
#1076)
8184

8285
# coq-lsp 0.2.4: (W)Activation
8386
------------------------------

compiler/args.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
(************************************************************************)
2+
(* Copyright 2019 MINES ParisTech -- Dual License LGPL 2.1+ / GPL3+ *)
3+
(* Copyright 2019-2024 Inria -- Dual License LGPL 2.1+ / GPL3+ *)
4+
(* Copyright 2024-2025 Emilio J. Gallego Arias -- LGPL 2.1+ / GPL3+ *)
5+
(* Copyright 2025 CNRS -- LGPL 2.1+ / GPL3+ *)
6+
(* Written by: Emilio J. Gallego Arias & coq-lsp contributors *)
7+
(************************************************************************)
8+
(* Flèche => command-line compiler *)
9+
(************************************************************************)
10+
111
module Display = struct
212
type t =
313
| Verbose
@@ -12,6 +22,7 @@ type t =
1222
; debug : bool (** run in debug mode *)
1323
; display : Display.t (** display level *)
1424
; plugins : string list (** Flèche plugins to load *)
25+
; trace_file : string option (** Save flame profile to file *)
1526
; max_errors : int option
1627
(** Maximum erros before aborting the compilation *)
1728
; coq_diags_level : int

compiler/compile.ml

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
(************************************************************************)
2+
(* Copyright 2019 MINES ParisTech -- Dual License LGPL 2.1+ / GPL3+ *)
3+
(* Copyright 2019-2024 Inria -- Dual License LGPL 2.1+ / GPL3+ *)
4+
(* Copyright 2024-2025 Emilio J. Gallego Arias -- LGPL 2.1+ / GPL3+ *)
5+
(* Copyright 2025 CNRS -- LGPL 2.1+ / GPL3+ *)
6+
(* Written by: Emilio J. Gallego Arias & coq-lsp contributors *)
7+
(************************************************************************)
8+
(* Flèche => command-line compiler *)
9+
(************************************************************************)
10+
111
open Fleche
212

313
let is_in_dir ~dir ~file = CString.is_prefix dir file
@@ -59,7 +69,35 @@ let compile_file ~cc file : int =
5969
Theory.close ~uri;
6070
status_of_doc doc)
6171

62-
let compile ~cc =
63-
List.fold_left
64-
(fun status file -> if status = 0 then compile_file ~cc file else status)
65-
0
72+
let compile_file ~cc file : int =
73+
let args () = [ ("file", `String file) ] in
74+
NewProfile.profile "compile" ~args (fun () -> compile_file ~cc file) ()
75+
76+
let oprofile = ref None
77+
78+
let init_profile trace_file =
79+
match trace_file with
80+
| None -> ()
81+
| Some file ->
82+
let oc = Stdlib.open_out file in
83+
let fmt = Format.formatter_of_out_channel oc in
84+
oprofile := Some (oc, fmt);
85+
NewProfile.init { output = fmt; fname = "fcc" }
86+
87+
let finish_profile () =
88+
match !oprofile with
89+
| None -> ()
90+
| Some (oc, fmt) ->
91+
NewProfile.finish ();
92+
Format.pp_print_flush fmt ();
93+
Stdlib.close_out oc
94+
95+
let compile ~cc ~trace_file files =
96+
init_profile trace_file;
97+
let finally = finish_profile in
98+
let compile () =
99+
List.fold_left
100+
(fun status file -> if status = 0 then compile_file ~cc file else status)
101+
0 files
102+
in
103+
Fun.protect ~finally compile

compiler/driver.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ let go ~int_backend args =
4343
; debug
4444
; files
4545
; plugins
46+
; trace_file
4647
; max_errors
4748
; coq_diags_level
4849
; record_comments
@@ -67,4 +68,4 @@ let go ~int_backend args =
6768
let cc = Cc.{ root_state; workspaces; default; io; token } in
6869
(* Initialize plugins *)
6970
plugin_init plugins;
70-
Compile.compile ~cc files
71+
Compile.compile ~cc ~trace_file files

compiler/fcc.ml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
open Cmdliner
33
open Fcc_lib
44

5-
let fcc_main int_backend roots display debug plugins files coqlib findlib_config
6-
ocamlpath rload_path load_path require_libraries no_vo max_errors
7-
coq_diags_level record_comments =
5+
let fcc_main int_backend roots display debug plugins trace_file files coqlib
6+
findlib_config ocamlpath rload_path load_path require_libraries no_vo
7+
max_errors coq_diags_level record_comments =
88
let vo_load_path = rload_path @ load_path in
99
let args = [] in
1010
let cmdline =
@@ -25,6 +25,7 @@ let fcc_main int_backend roots display debug plugins files coqlib findlib_config
2525
; files
2626
; debug
2727
; plugins
28+
; trace_file
2829
; max_errors
2930
; coq_diags_level
3031
; record_comments
@@ -52,6 +53,10 @@ let plugins : string list Term.t =
5253
let doc = "Compiler plugins to load" in
5354
Arg.(value & opt_all string [] & info [ "plugin" ] ~docv:"PLUGINS" ~doc)
5455

56+
let trace_file : string option Term.t =
57+
let doc = "Activate profiling and output a Rocq flame graph in FILE" in
58+
Arg.(value & opt (some string) None & info [ "trace_file" ] ~docv:"FILE" ~doc)
59+
5560
let no_vo : bool Term.t =
5661
let doc = "Don't generate .vo files at the end of compilation" in
5762
Arg.(value & flag & info [ "no_vo" ] ~doc)
@@ -98,9 +103,10 @@ let fcc_cmd : int Cmd.t =
98103
let fcc_term =
99104
let open Coq.Args in
100105
Term.(
101-
const fcc_main $ int_backend $ roots $ display $ debug $ plugins $ file
102-
$ coqlib $ findlib_config $ ocamlpath $ rload_paths $ qload_paths
103-
$ ri_from $ no_vo $ max_errors $ coq_diags_level $ record_comments)
106+
const fcc_main $ int_backend $ roots $ display $ debug $ plugins
107+
$ trace_file $ file $ coqlib $ findlib_config $ ocamlpath $ rload_paths
108+
$ qload_paths $ ri_from $ no_vo $ max_errors $ coq_diags_level
109+
$ record_comments)
104110
in
105111
let exits = Exit_codes.[ fatal; stopped; scheduled; uri_failed ] in
106112
Cmd.(v (Cmd.info "fcc" ~exits ~version ~doc ~man) fcc_term)

coq/parsing.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ let parse ~token ~st ps =
3030
(* This runs already inside Coq.protect *)
3131
State.in_state ~token ~st ~f:(parse ~st) ps
3232

33+
let parse ~token ~st ps =
34+
NewProfile.profile "Coq.parse" (fun () -> parse ~token ~st ps) ()
35+
3336
(* Read the input stream until a dot or a "end of proof" token is encountered *)
3437
let parse_to_terminator : unit Pcoq.Entry.t =
3538
(* type 'a parser_fun = { parser_fun : te LStream.t -> 'a } *)

fleche/doc.ml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,11 @@ let create ~token ~env ~uri ~languageId ~version ~raw =
479479
handle_contents_creation ~env ~uri ~version ~languageId ~raw
480480
(handle_doc_creation_exec ~token)
481481

482+
let create ~token ~env ~uri ~languageId ~version ~raw =
483+
NewProfile.profile "Doc.create"
484+
(fun () -> create ~token ~env ~uri ~languageId ~version ~raw)
485+
()
486+
482487
(* Used in bump, we should consolidate with create *)
483488
let recreate ~token ~doc ~version ~contents =
484489
let env, uri, languageId = (doc.env, doc.uri, doc.languageId) in
@@ -563,6 +568,11 @@ let bump_version ~token ~version ~raw doc =
563568
conv_error_doc ~raw ~uri ~languageId ~version ~env:doc.env ~root:doc.root e
564569
| Contents.R.Ok contents -> bump_version ~token ~version ~contents doc
565570

571+
let bump_version ~token ~version ~raw doc =
572+
NewProfile.profile "Doc.bump_version"
573+
(fun () -> bump_version ~token ~version ~raw doc)
574+
()
575+
566576
let update_env ~doc ~env =
567577
let range = Completion.range doc.completed in
568578
{ doc with completed = WorkspaceUpdated range; env }
@@ -866,6 +876,11 @@ let parse_action ~token ~lines ~st last_tok doc_handle =
866876
in
867877
(Skip (span_range, last_tok_range), parse_diags, feedback, time))
868878

879+
let parse_action ~token ~lines ~st last_tok doc_handle =
880+
NewProfile.profile "Doc.parse_action"
881+
(fun () -> parse_action ~token ~lines ~st last_tok doc_handle)
882+
()
883+
869884
(* Result of node-building action *)
870885
type document_action =
871886
| Stop of Completion.t * Node.t
@@ -983,6 +998,14 @@ let document_action ~token ~io ~st ~parsing_diags ~parsing_feedback
983998
node_of_coq_result ~token ~doc ~range:ast_range ~prev ~ast ~st
984999
~parsing_diags ~parsing_feedback ~feedback ~info last_tok_new res)
9851000

1001+
let document_action ~token ~io ~st ~parsing_diags ~parsing_feedback
1002+
~parsing_time ~prev ~doc last_tok doc_handle action =
1003+
NewProfile.profile "Doc.document_action"
1004+
(fun () ->
1005+
document_action ~token ~io ~st ~parsing_diags ~parsing_feedback
1006+
~parsing_time ~prev ~doc last_tok doc_handle action)
1007+
()
1008+
9861009
module Target = struct
9871010
type t =
9881011
| End
@@ -1091,6 +1114,12 @@ let process_and_parse ~io ~token ~target ~uri ~version doc last_tok doc_handle =
10911114
let doc = { doc with nodes = List.rev doc.nodes } in
10921115
doc
10931116

1117+
let process_and_parse ~io ~token ~target ~uri ~version doc last_tok doc_handle =
1118+
NewProfile.profile "Doc.process_and_parse"
1119+
(fun () ->
1120+
process_and_parse ~io ~token ~target ~uri ~version doc last_tok doc_handle)
1121+
()
1122+
10941123
let log_doc_completion (completed : Completion.t) =
10951124
let timestamp = Unix.gettimeofday () in
10961125
let range = Completion.range completed in
@@ -1164,6 +1193,9 @@ let check ~io ~token ~target ~doc () =
11641193
Util.print_stats ();
11651194
doc
11661195

1196+
let check ~io ~token ~target ~doc () =
1197+
NewProfile.profile "Doc.check" (fun () -> check ~io ~token ~target ~doc ()) ()
1198+
11671199
let save ~token ~doc =
11681200
match doc.completed with
11691201
| Yes _ ->

fleche/memo.ml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ module type EvalType = sig
213213

214214
type output
215215

216+
val name : string
217+
216218
val eval :
217219
token:Coq.Limits.Token.t -> t -> (output, Coq.Loc_t.t) Coq.Protect.E.t
218220

@@ -279,6 +281,10 @@ module SEval (E : EvalType) :
279281
let () = HC.add_execution cache i (res, stats) in
280282
(res, Stats.make ~stats ~cache_hit:false ~time_hash ())
281283

284+
let evalS ~token i =
285+
let name = "Memo." ^ E.name in
286+
NewProfile.profile name (fun () -> evalS ~token i) ()
287+
282288
let eval ~token i = evalS ~token i |> fst
283289
end
284290

@@ -330,10 +336,16 @@ module CEval (E : LocEvalType) = struct
330336
let () = HC.add_execution_loc cache i (stm_loc, res, stats) in
331337
(res, Stats.make ~stats ~cache_hit:false ~time_hash ())
332338

339+
let evalS ~token i =
340+
let name = "Memo." ^ E.name in
341+
NewProfile.profile name (fun () -> evalS ~token i) ()
342+
333343
let eval ~token i = evalS ~token i |> fst
334344
end
335345

336346
module VernacEval = struct
347+
let name = "Interp"
348+
337349
type t = Coq.State.t * Coq.Ast.t
338350

339351
(* This crutially relies on our ppx to ignore the CAst location *)
@@ -356,6 +368,8 @@ end
356368
module Interp = CEval (VernacEval)
357369

358370
module RequireEval = struct
371+
let name = "Require"
372+
359373
type t = Coq.State.t * Coq.Files.t * Coq.Ast.Require.t
360374

361375
(* This crutially relies on our ppx to ignore the CAst location *)
@@ -387,13 +401,17 @@ module Require = CEval (RequireEval)
387401
module Admit = SEval (struct
388402
include Coq.State
389403

404+
let name = "Admit"
405+
390406
type output = Coq.State.t
391407

392408
let input_info st = Format.asprintf "st %d" (Hashtbl.hash st)
393409
let eval ~token st = Coq.State.admit ~token ~st
394410
end)
395411

396412
module InitEval = struct
413+
let name = "Init"
414+
397415
type t = Coq.State.t * Coq.Workspace.t * Coq.Files.t * Lang.LUri.File.t
398416

399417
let equal (s1, w1, f1, u1) (s2, w2, f2, u2) : bool =

0 commit comments

Comments
 (0)