Running project built on Raspberry Pi with Cabal gives weird errors

I need to run my project on a Raspberry Pi 3. Simply copying the binary over there doesn’t work, because it’s ARM and 32-bit. So I then installed Cabal on my development machine (was using Stack), did cabal new-build and tested that the binary works properly, and used cabal freeze (do I need to use new-freeze here or does it not matter?) to generate a dependency lock file. I copied the project and the freeze file onto Raspberry Pi, installed Cabal there, increased swap memory size to 2GB, and ran cabal new-build. After a whole day, it managed to build. But trying to run the executable with cabal new-run project-name or ./project-name gives the following:

hideout-backend-exe: unknown RTS option: -N
hideout-backend-exe: Usage: <prog> <args> [+RTS <rtsopts> | -RTS <args>] ... --RTS <args>
hideout-backend-exe:    +RTS    Indicates run time system options follow
hideout-backend-exe:    -RTS    Indicates program arguments follow
hideout-backend-exe:   --RTS    Indicates that ALL subsequent arguments will be given to the
hideout-backend-exe:            program (including any of these RTS flags)
hideout-backend-exe: The following run time system options are available:
hideout-backend-exe:   -?       Prints this message and exits; the program is not executed
hideout-backend-exe:   --info   Print information about the RTS used by this program
hideout-backend-exe:   -K<size>  Sets the maximum stack size (default: 80% of the heap)
hideout-backend-exe:             Egs: -K32k -K512k -K8M
hideout-backend-exe:   -ki<size> Sets the initial thread stack size (default 1k)  Egs: -ki4k -ki2m
hideout-backend-exe:   -kc<size> Sets the stack chunk size (default 32k)
hideout-backend-exe:   -kb<size> Sets the stack chunk buffer size (default 1k)
hideout-backend-exe:   -A<size>  Sets the minimum allocation area size (default 1m) Egs: -A20m -A10k
hideout-backend-exe:   -AL<size> Sets the amount of large-object memory that can be allocated
hideout-backend-exe:             before a GC is triggered (default: the value of -A)
hideout-backend-exe:   -n<size>  Allocation area chunk size (0 = disabled, default: 0)
hideout-backend-exe:   -O<size>  Sets the minimum size of the old generation (default 1M)
hideout-backend-exe:   -M<size>  Sets the maximum heap size (default unlimited)  Egs: -M256k -M1G
hideout-backend-exe:   -H<size>  Sets the minimum heap size (default 0M)   Egs: -H24m  -H1G
hideout-backend-exe:   -xb<addr> Sets the address from which a suitable start for the heap memory
hideout-backend-exe:             will be searched from. This is useful if the default address
hideout-backend-exe:             clashes with some third-party library.
hideout-backend-exe:   -m<n>     Minimum % of heap which must be available (default 3%)
hideout-backend-exe:   -G<n>     Number of generations (default: 2)
hideout-backend-exe:   -c<n>     Use in-place compaction instead of copying in the oldest generation
hideout-backend-exe:            when live data is at least <n>% of the maximum heap size set with
hideout-backend-exe:            -M (default: 30%)
hideout-backend-exe:   -c       Use in-place compaction for all oldest generation collections
hideout-backend-exe:            (the default is to use copying)
hideout-backend-exe:   -w       Use mark-region for the oldest generation (experimental)
hideout-backend-exe:   -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)
hideout-backend-exe:   -T         Collect GC statistics (useful for in-program statistics access)
hideout-backend-exe:   -t[<file>] One-line GC statistics (if <file> omitted, uses stderr)
hideout-backend-exe:   -s[<file>] Summary  GC statistics (if <file> omitted, uses stderr)
hideout-backend-exe:   -S[<file>] Detailed GC statistics (if <file> omitted, uses stderr)
hideout-backend-exe:   -Z         Don't squeeze out update frames on stack overflow
hideout-backend-exe:   -B         Sound the bell at the start of each garbage collection
hideout-backend-exe:   -h       Heap residency profile (output file <program>.hp)
hideout-backend-exe:   -i<sec>  Time between heap profile samples (seconds, default: 0.1)
hideout-backend-exe:   -C<secs>  Context-switch interval in seconds.
hideout-backend-exe:             0 or no argument means switch as often as possible.
hideout-backend-exe:             Default: 0.02 sec.
hideout-backend-exe:   -V<secs>  Master tick interval in seconds (0 == disable timer).
hideout-backend-exe:             This sets the resolution for -C and the heap profile timer -i,
hideout-backend-exe:             and is the frequence of time profile samples.
hideout-backend-exe:             Default: 0.01 sec.
hideout-backend-exe:   --install-signal-handlers=<yes|no>
hideout-backend-exe:             Install signal handlers (default: yes)
hideout-backend-exe:   -e<n>     Maximum number of outstanding local sparks (default: 4096)
hideout-backend-exe:   -xq       The allocation limit given to a thread after it receives
hideout-backend-exe:             an AllocationLimitExceeded exception. (default: 100k)
hideout-backend-exe:   -Mgrace=<n>
hideout-backend-exe:             The amount of allocation after the program receives a
hideout-backend-exe:             HeapOverflow exception before the exception is thrown again, if
hideout-backend-exe:             the program is still exceeding the heap limit.
hideout-backend-exe: RTS options may also be specified using the GHCRTS environment variable.
hideout-backend-exe: Other RTS options may be available for programs compiled a different way.
hideout-backend-exe: The GHC User's Guide has full details.

It doesn’t even look like an error, just some information. How can I solve this?
I’ve only had experience with Stack before. I tried to use Stack on Pi but Stack told me it only has binaries for 64 bit. So I had to use Cabal.
The program is supposed to be a Servant server.

This is the error:

unknown RTS option: -N

You can fix it by looking for the -N flag in your .cabal file and removing the flag.

1 Like

That’s it! The program ran after removing the flag.
But I don’t understand how Cabal can build me an executable with a bad config file?

The RTS (runtime system) options are for runtime only, so they wouldn’t cause a build to fail. It’s somewhat unusual to set -with-rtsopts=... in a .cabal file, but it’s not that weird either. And -N is one of the more common RTS options. If you do want to enable -N, you’ll probably have to build your program with the -threaded flag set.

My original .cabal file has this: ghc-options: -threaded -rtsopts -with-rtsopts=-N
I’m still not understanding. So -N is indeed an RTS flag, right? Then why does the program say it’s an unknown flag on Pi, but not on my dev machine?
I tried to look up what this -N is here, but could only found reference to -N, not explanation on what it is itself.

A major problem that occurred later was that the server threw Segmentation Fault as soon as it received a request. I’m feeling this Haskell on Raspberry Pi option is getting unpractical. I have absolutely no idea how to even debug this segfault. More concerning is that, if the build behaves differently on Pi, and can throw runtime errors like segfault, then how can I be certain what other errors, or unexpected behavior, it can yield?

Unless I’m missing something rather obvious?

Alright here we go… and let me preface this with saying that I’m sorry for not being more uplifting in the following.

ARMv7 (which is likely what you are targeting) is a 32bit target, as is JavaScript, WASM, and i386. Today most of modern computing runs on 64bit machines across the major operating systems (Windows, Linux, macOS, iOS). With some reservations for Android on 32bit arm, MIPS and others.

My firm belief is that ghc on armv7 is the least (by a wide margin) tested compiler. The amount of minor linking bugs and others that were fixed just over the last few month don’t make me too hopeful either.

armv7 is also severely constraint in concurrency primitives, potential softfloat and other mild annoyances like thumb/non-thumb interworking when linking foreign libraries, …

My suggestion would be to use a 64bit operating system on your raspberry pi3, if you can, and use a recent ghc 8.10.4+. Anything prior to 8.10.3 is pretty much guaranteed to not be thread safe.

That being said, yes you can use haskell on raspberry pis, we even have relatively large haskell projects like that cardano-node running on raspberry pis. (Now whether or not that’s a good idea and if the raspberry pi is actually powerful enough to run a node right now sufficiently is a different question, but you can run it on a raspberry pi without segfaults).

You can even cross compile haskell to raspberry pi (on linux only though).

One final note regarding:

More concerning is that, if the build behaves differently on Pi, and can throw runtime errors like segfault, then how can I be certain what other errors, or unexpected behavior, it can yield?

The simple yet slightly terrifying answer is: you can’t. It will always behave slightly differently. It’s a different machine, a different codegen, different in-memory linker (if it’s used), a different runtime system (the rts has lots of architecture conditionals).

While we try to make sure the behaviour is identical across architectures and operating systems (e.g. see the test-suite that tries to cover a lot of cases), by virtue of not being identical code, making total statements on identical behaviour is sadly not possible. It will behave identical up to the level our test-suite ensures it does. And I believe this is the same for pretty much every other language as well, unless you have an abstract machine, and verified bug-free implementations for that abstract machine for each target.


That is a good overview of the situation!

It’s a different machine, a different codegen, different in-memory linker (if it’s used), a different runtime system

That’s a fair point.

My suggestion would be to use a 64bit operating system on your raspberry pi3

I’ve always thought Pi 3 has a 32-bit CPU. Otherwise why would it run a 32-bit OS? I guess that’s actually untrue.

I later tried to use Stack on a laptop with Trisquel OS. Stack warned me that it may not work on Trisquel. And indeed, the program was built, but didn’t behave properly, although I can’t tell if it’s because of the OS or a misconfiguration. I then installed Debian on the same laptop, and it all worked.

But if I install Debian on a Pi, will Stack give me a similar warning as well, because its CPU is ARM? Or will it not, as long as the OS checks out?