I would have thought that the first thing that gets called is in coqide_main.ml, but some other stuff seems to be called first. For example, if you unset DISPLAY
and then OCAMLRUNPARAM=b dune exec -- coqide
we have stuff being called from ideutils.ml first?!
Fatal error: exception Gtk.Error("GtkMain.init: initialization failed\nml_gtk_init: initialization failed")
Raised at GtkMain.Main.init in file "src/gtkMain.ml", line 46, characters 8-69
Called from Ideutils in file "ide/coqide/ideutils.ml", line 13, characters 8-28
How can I know what Ocaml is calling first?
Removing the call from ideutils gives me a segmentation fault with no backtrace which is another story.
Things are executed in linking order. So, ideutils.ml
presumably appears before coqide_main.ml
, and for good reasons.
What good reason could there possibly be to be called before the entry point?
You said it yourself: Because if you don't, you get a segfault.
But that is only happening because other stuff is being called before the entry point
I'm interested in making the entry point an actual entry point here
Where can I read more about ocaml calling functions in linking order?
Here is a simpler example. File A
does let foo = ref 0
and file Main
does let () = print_int !A.foo
. If the main entrypoint was executed first, you would get a segfault, because A.foo
would be a NULL pointer.
see also https://github.com/coq/coq/commit/bcbeac04993ffbad4f4432b646614491f2d8a5d5
So how would we get the initialization centralized then? Is there a way to easily see what is getting called in linking order?
@Guillaume Melquiond I don't disagree that your example makes sense, but I think it is bad practice, especially as an entry point to a program.
Keep in mind that any declaration in an .mli
file that is not of function type was necessarily initialized in linking order. There are presumably hundreds of instances in Coq. And it is not Coq being especially ugly. This is completely idiomatic in OCaml.
Does OCaml support relying on initialization order? I always thought that was explicitly undefined
That is explicitly defined: "The order in which .cmx
and .ml
arguments are presented on the command line is relevant: compilation units are initialized in that order at run-time". And that is fortunate. Otherwise absolutely no toplevel definition would ever work.
So note that then we could have introduced some bug when porting to dune
as I'm not sure I preserved the link order
Yeah I got used to doing lib + small wrapper for executable so I don't care so much in the sense in my own programs I hope the init does commute
And usually I much prefer val init : unit -> unit
for stuff that may be tricky
So this means that because CoqIDE uses Gtk, it is impossible to call it without requiring everything in Gtk to run fine. This is unfortunate since it means we cannot coqide --help
or coqide --version
without an xserver...
the thing is that when using a lib, if you don't do linkall you get into trouble if you abuse static init a lot
for example
@Ali Caglayan I'm not expert but I'm not sure GTK does rely on static initialization a lot?
lablgtk examples require you to call init before they attempt to connect to the xserver IIRC
Ali Caglayan said:
So this means that because CoqIDE uses Gtk, it is impossible to call it without requiring everything in Gtk to run fine. This is unfortunate since it means we cannot
coqide --help
orcoqide --version
without an xserver...
There is no fundamental reason we cannot. The parsing of command-line options (at least --help
and --version
) just need to happen earlier.
GMain.init ();
@Guillaume Melquiond We literally cannot do it any earlier since Gtk initializing is called before the entry point
Initialization happens in link order. Parsing of command-line options just need to be linked earlier.
It is being called before commandline arguments are even parsed
hmm ok so we make an ml file that gets linked first and parse arguments there
Umm, GMain.init _should_ not be called from static initializers
How do I control the linking order?
I fail to see where it is called tho, but that's def a bad practice
First you need to locate the bad part
ideutils is being linked before coqide_main
Aha!
ln 13 in ideutils is the main Gtk init
Sorry I see it now
without it we get seg faults
Instead of messing with the linking order
call it from coqide_main
that's a very bad practice
We can't because it segfaults
but could be painful to debug
let me see
No idea what to do with the info it spits out:
(process:151063): Gtk-CRITICAL **: 16:32:57.828: _gtk_style_provider_private_get_settings: assertion 'GTK_IS_STYLE_PROVIDER_PRIVATE (provider)' failed
(process:151063): Gtk-CRITICAL **: 16:32:57.828: _gtk_style_provider_private_get_settings: assertion 'GTK_IS_STYLE_PROVIDER_PRIVATE (provider)' failed
(process:151063): Gtk-CRITICAL **: 16:32:57.828: _gtk_style_provider_private_get_settings: assertion 'GTK_IS_STYLE_PROVIDER_PRIVATE (provider)' failed
Segmentation fault (core dumped)
yeah some other static initializer is calling gtk code when it should not
You cannot move it, since a few line later, the code does let cb = GData.clipboard Gdk.Atom.primary
.
put that in a thunk or lazy value
But indeed it will be painful to do the refactoring
There are all sorts of things like that in ideutils
We need to put those in an init step
and call it from main
yup that's the more correct way, if not, you can use the (modules ...)
field to specify the linking order
list the modules manually tho, I think, not sure the language is expressive enought
Is ocaml backtrace able to show all the functions from the beginning of the run?
That might help find which ones are being called
@Ali Caglayan for that your best bet is ocamldebug or gdb
I think [not a super expert on this as you have inferred]
ocamldebug doesn't really give anything better
Loading program... Fatal error: cannot load shared library dllcoqide_gui_stubs
Reason: dllcoqide_gui_stubs.so: cannot open shared object file: No such file or directory
Aborted (core dumped)
That's a problem with the dll path
it seems?
As far as I know ocamldebug allowed you to step the static init
It doesn't give a backtrace or anything tho
What about gdb?
Actually let me try myself
I got this
(gdb) bt
#0 0x00007ffff7823364 in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#1 0x00007ffff76c9e0c in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#2 0x00007ffff76ea307 in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#3 0x00007ffff76d5492 in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#4 0x00007ffff76ea232 in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#5 0x00007ffff76ea28d in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#6 0x00007ffff76d5ec6 in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#7 0x00007ffff74f51a1 in g_type_create_instance () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#8 0x00007ffff74d434d in () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#9 0x00007ffff74d5b45 in g_object_new_with_properties () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#10 0x00007ffff74d66f1 in g_object_new () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#11 0x00007ffff76f2c1f in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#12 0x00007ffff78d1d8d in () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#13 0x00007ffff74f51a1 in g_type_create_instance () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#14 0x00007ffff74d434d in () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#15 0x00007ffff74d5eb5 in g_object_newv () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#16 0x0000555555a7e13d in ml_g_object_new (type=93825001579537, params=1) at ml_gobject.c:557
#17 0x000055555592c48d in camlGobject__unsafe_create_357 () at src/gobject.ml:208
#18 0x0000555555937849 in camlGtkObject__make_262 () at src/gtkObject.ml:30
#19 0x00005555559bdee5 in camlGMisc__fun_4006 () at src/gtkMiscProps.ml:268
#20 0x0000555555991546 in camlGContainer__fun_2289 () at src/gContainer.ml:84
#21 0x0000555555a39bb7 in camlCamlinternalLazy__force_lazy_block_166 () at camlinternalLazy.ml:31
#22 0x00005555558c8e20 in camlIdeutils__entry () at ide/coqide/ideutils.ml:33
#23 0x00005555558a9179 in caml_program ()
#24 0x0000555555ad372c in caml_start_program ()
#25 0x0000555555ab0fc4 in caml_startup_common (argv=0x7fffffffd6c8, pooling=<optimised out>, pooling@entry=0) at startup_nat.c:158
#26 0x0000555555ab100f in caml_startup_exn (argv=<optimised out>) at startup_nat.c:168
#27 caml_startup (argv=<optimised out>) at startup_nat.c:168
#28 0x00005555558a8482 in main (argc=<optimised out>, argv=<optimised out>) at main.c:41
is IMHO usable
So the program is entering at ideutils
@Ali Caglayan got it running
here's the diff
that's a crude diff, a bit of refactoring would make it OK
I'm not a big fan of making all of that lazy
That means that all future calls need to also be lazy
Which is easy to forget and unmaintainable imo
Not sure what you mean, the right fix is of course to properly scope things
but that's a lot of work
That is the morally correct fix
that's just a patch to blame the bad actors
a more workable one might be to modify the linking order
sure I'd be lovely to do the better patch, first is to identify them
that's pretty fragile tho
and a PITA for other reasons
but I'm not opposed to it
whatever works best for you
- let status_context = status#new_context ~name:"Messages" in
+ let status_context () = Lazy.(force status)#new_context ~name:"Messages" in
This cannot be correct. You are creating a new context at each call.
It's just a POC @Guillaume Melquiond to avoid the segfault
indeed that's wrong
Don't merge that diff indeed
Last updated: Sep 09 2024 at 04:02 UTC