Maybe I’m biased because the lack of an easy-to-use debugger for OCaml made me search for various other alternatives/workarounds, and I’m now so used to those workarounds that my default approach to debugging is through logging/tracing, not single-stepping.
Single-stepping has mostly been replaced by trying out snippets in utop.
Having said that it’d be nice to have a single-stepping debugger integrated into utop (or a REPL integrated into other OCaml debug interfaces).
I think that graphical interface would be limited to setting breakpoints and stepping through code. But without the ability to inspect (or continuously display) the values of various variables/globals it’d be of limited usefulness.
gdbis useful if you just want to see what your program is doing though (e.g. you can get a stacktrace of all threads).
Long ago I remember trying the Eclipse OCaml plugin, and for bytecode it was quite nice, reversible debugging worked as well, so in that sense it was a lot more advanced than what you could do with debugging in C.
However in practice I almost never run bytecode programs, so that feature didn’t turn out as useful as it might seem (also I don’t use Eclipse as my IDE).
I wonder whether a bytecode debugger or utop could be used with remote access to the in-memory representation of an OCaml value in a native program. (this means that existing debug support for bytecode could be used to inspect values for native programs if you also load their bytecode equivalent to pretty print/inspect/etc.; and you could also get a “debug REPL” for a native OCaml program which would ideal…)
Aside from the different calling conventions for C stubs with many arguments, the in-memory representation of OCaml values should be the same between bytecode and native, right? And it is possible to know the size and structure of an OCaml value by (recursively) walking it like the GC would (based on GC tags, and size metadata).
And ‘gdb’ has a remote protocol, so in theory something (maybe ocamldebug, maybe utop, maybe the bytecode runtime, or some other debug adapter) could interpose and provide e.g. a Obj.Debug.remote_lookup "foo" that gives you a pointer to the remote’s memory, and intercept all reads to this region by sending it across the remote gdb interface (or recursively map/copy it, but that could be gigabytes if you happen to point at the root of a large tree), and reject writes.
To avoid overlap with values allocated by the bytecode program, it could retag the values in a different way to mark them as “remote” values, or assign a portion of the virtual address space to mean “remote native”.
I spent 1996-2007 neck-deep in Java in IBM, and in all that time, I never found debuggers to be all that useful.[1] Since almost all problems that mattered in Java were multi-threaded problems, even when you could attach a debugger to a running process, it would so heavily perturb behaviour that whatever bug you were waiting for, would run off into the woods, never to be seen … until you were running without a debugger again.
At least for all production systems debugging (and that includes all debugging in staging environments and such) debuggers are useless.
Where they can play a useful role, is in faster debugging of isolated single-threaded reproductions. That is to say, when you have a bug that’s reproducible in a single-threaded setup, sure, a debugger can be helpful. But heck, if you have that, you’re already 95% of the way home, and tracing is enough to finish the job.
[1] And this was not merely my own judgment, but also that of many troubleshooters with whom I worked. We all thought of debuggers as tools for developers – people who were unable to debug to save their ever-lovin’ lives, people who thought of debugging as beneath them, b/c they thought of the only sort of worthwhile work as “writing new code”. In short, the kind of people whose efforts ensured that we would never want for highly-paid work.
yet java has some of the best tooling around debugging and profiling. advanced use-cases which require their own specialized tooling don’t influence the need for quality-of-life tooling for the rest of the user base.
I don’t have stats on the community but I’m welling to bet a big chunk of us are so called “developers” your ex-colleagues seemed to look down on.
As I noted, this tooling (at least, standard-issue debuggers and profilers) are all pretty much useless for any sort of problem that crops up under load, or in a concurrent-workload situation. B/c merely running under a debugger or profiler perturbs behaviour enough to make bugs disappear. And we’re talking about the commercial transaction-processing systems that are the bread-and-butter of Java systems in the business.
Maybe it is the way someone is used to wok but a debugger is indispensable for me. For a software of processing vehicle generated data (strings with a lot of properties in them) that we are building here (with Go), finding problems by just printing values on the console and with no ability to step into from inside the editor and see exactly what the code is doing would be very cumbersome. And we can also debug concurrent software just fine (we use GoLand that includes the Delve debugger).
I think 99% or more of the programming world uses debuggers, and for good reason. I don’t think we need to justify them – they’re pretty much essential for any language.