Chris Stryczynski
Chris Stryczynski

Reputation: 34041

Haskell profiling options result in build error of "Cannot load -prof objects when GHC is built with -dynamic"

I wanted to do some profiling on a Haskell application so in my hpack I added some ghc-options:

  example-executable:
    source-dirs: src
    main: Main.hs
    ghc-options:
      - -O2
      - -prof
      - -fprof-auto
      - -rtsopts

that resulted in this error:

[ 6 of 19] Compiling Model.Server     ( src/Model/Server.hs, dist/build/discord-app-prof/discord-app-prof-tmp/Model/Server.o )
<no location info>: error:
    src/Model/Server.hs:125:13: fatal:
    Cannot load -prof objects when GHC is built with -dynamic
    To fix this, either:
      (1) Use -fexternal-interpreter, or
      (2) Build the program twice: once with -dynamic, and then
          with -prof using -osuf to set a different object file suffix.

Where is this -dynamic coming from? I don't recall ever running into this issue before when profiling previously which is also strange. Also it's confusing when it says "GHC is built with -dynamic" - surely GHC is not being built here but the haskell executable?

I'm using GHC 9.0.2

Upvotes: 1

Views: 148

Answers (2)

HTNW
HTNW

Reputation: 29193

First of all, -fprof-auto is a bad idea. Every time I've used it or seen anyone use it, it gives wrong results. Profiling requires tagging code with cost centers, but cost centers inhibit many optimizations. You end up measuring code that is different from what actually executes on a normal run. (Code that's actually fast can appear much slower, so your optimization priorities will be mixed up.) I strongly recommend picking a different profiling option, or writing SCC annotations yourself. Perhaps you can try the new -fprof-late (though this requires GHC 9.4.1).

-dynamic is the default because dynamic linking is the default on your OS (as it is on all the popular PC OSs). "GHC is built with -dynamic" means exactly that; the GHC binaries you're using were (in the past, before you downloaded them or perhaps when you built them yourself) built with -dynamic. You of course aren't rebuilding GHC now.

Profiling requires not just your own code be built with -prof, but that all dependencies also be built with -prof. This is something the build system (Cabal) needs to orchestrate, to create and then point GHC at the correct files. You can't just throw -prof into ghc-options. In the very simplest case, if you were using bare cabal-install, you just need to add the following to cabal.project.local. I believe cabal configure --enable-profiling --profiling-detail=late would write out such a file too. You do not put -prof -fprof-late into ghc-options.

profiling: True
profiling-detail: late

The next cabal build of the project will (re)build all dependencies as needed with profiling. I believe Cabal also uses -dynamic-too to get the -dynamic and -prof versions of the objects simultaneously and avoids the GHC error you saw by pointing e.g. ghci at the -dynamic objects when requested. You can control the profiling detail in dependencies through cabal.project.local, too. (The way it stands, this file applies a default of -fprof-auto-exported to all dependencies.)

Note that the .cabal file wasn't touched. In the Cabal model, profiling is not part of the package description. Profiling is, after all, not part of the package content. Profiling is just a special way of building the package. (I note that you have -O2 in ghc-options too. This is also anti-Cabal. The correct way to handle that is to put optimization: 2 into cabal.project.local, probably under a package * stanza to apply it to all dependencies as well as the current package.) The only option that should be in ghc-options out of those you have listed is -rtsopts.

I don't use hpack, but it looks to me that it only generates the .cabal file (i.e. it just describes the package). As (reiterating) profiling and optimization are not properties of the package but properties of the build, you don't do anything to your hpack configuration to use them. You just write the appropriate cabal.project.local and use that besides the .cabal generated by hpack. I'm not sure what complications Nix adds here.

NB: There is a distinction between build configuration that should ship with the package and configuration local to the developer. The former goes in cabal.project, the latter into cabal.project.local. In order for cabal.project.local to be processed correctly, you may need a trivial cabal.project that contains simply packages: ..

Upvotes: 4

Chris Stryczynski
Chris Stryczynski

Reputation: 34041

A quick solution was to change my default.nix, the -dynamic is probably being set as a default somewhere:

- myHaskellPackages.callCabal2nix "HaskellNixCabalStarter" (src) {}
+ myHaskellPackages.callCabal2nixWithOptions "HaskellNixCabalStarter" (src) "--enable-profiling" {}

As mentioned here: How can I enable Haskell profiling when using Nix's callCabal2nix

Upvotes: 0

Related Questions