Resuming my struggles to integrate the Argonaut Stack into a “Haphaestus” executable without it freezing or crashing… required some additional tooling! Plus trial and error.
The first tool I tried was GDB to inform me which C functions were running when the program crashed. GDB tends not to be very informative in relation to Haskell code due (especially if it crashes during garbage collection! Which thankfully wasn’t the case here) to a lack of debugging symbols, but its what I knew. Eventually hitting dead ends there I was pointed towards LTrace (which tweaks the dynamic-linker so any external function calls are wrapped in a routine which logs said call) and Valgrind (which emulates the CPU adding useful debugging features, like tracking memory allocations). These tools noticably slow your program down, but in doing so they provide valuable intelligence.
I wrote a smaller integration test to simplify the output from LTrace & Valgrind to, more-or-less, the interesting bits.
As it turned out, all the issues were related to FontConfig, which is the library used by freedesktops to lookup which fontfiles apps should use.
The first potential issue these tools pointed out to me was that my calls to iterate over a Character Set might not have an ABI Haskell can easily language-bind against. With later fixes I’m not actually sure whether this actually was a problem, but I am more confident in the new solution.
Now I’ve wrapped those calls with my own C struct holding their arguments, following the conventions which work elsewhere. Another option would be to convert the FcCharSet to an array, but I didn’t want to incur that memory overhead if I could avoid it. Especially after my efforts to optimize Harfbuzz bindings!
While I was at it I adjusted the corresponding Haskell-side representation to use an IntSet for better performance, more closely matching the C-side datastructure. The Haskell-side API isn’t quite as nice now, but it does compactly store contiguous ranges in a bitmask.
The next issue pointed out by Valgrind also related to FcCharSets. A close examination of the functions memory-leaking FcCharSet objects indicates that the type signature I indicated to GHC mismatched the actual type signature provided to GCC. Leading to memory being written where I wasn’t expecting.
Once those “foreign function” declarations & their wrapper-functions where adjusted to match the FontConfig library (which due to further misunderstandings incurred a breaking change), I performed a careful audit looking for similar mistakes. None were located.
At this point the remaining issues manifested as a freeze during which (depending on which debugging tools, if any, I run it under; I forget details) it might crash. LTrace revealed that my FontConfig language bindings were spending an inordinate time reading in queried font data which was never going to be used.
The solution was obvious: Utilize Haskell’s laziness to read only the required data. The actual implementation of that solution was a bit more involved, requiring a fairly simple refactor to ensure I kept the required references live until they were no longer needed.
These solution addressed all the visible problems, but neither Valgrind or FontConfig’s finalizer were entirely happy yet. I still had a couple memory leaks.
Running the garbage collector before finalizing FontConfig mostly resolved the problem, but if I’m reading the reports correctly those necessary laziness-optimizations might be causing memory leaks. After an initial investigation, I decided that I had more pressing issues to address. I still need to see these webpages fully-render!
In short: Ensuring Haskell code correctly, let alone efficiently, interfaces to FontConfig’s C API is proving to be a tricky endevour requiring a keen eye. Its possible there’s better tooling I could rewrite my language bindings to use, but having solved these issues I have other priorities.
Anyone keen to take FontConfig-Pure over?