# GHC Profiling a Cabal Project with an Interactive Application

**URL:** <https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465>\
**Category:** Learn\
**Created:** [October 4, 2024, 9:40pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465 "2024-10-04T21:40:46Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 4, 2024, 9:40pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/1 "2024-10-04T21:40:46Z")

</div>

Hi, all.

I’m trying to learn about GHC profiling. Every doc or book that I have come across only shows how to use GHC profiling on a small program, complied directly with GHC, with one or two functions, that executes these functions and then terminates.

Are there any resources that discuss using GHC profiling on a large Cabal project with an interactive application that does not terminate? Or even something like a yesod application?

I have been able to set up GHC profiling in my cabal project, so that I can get a `.prof` file after running a small program that terminates. But I don’t see a way to get this to work with my actual application, which won’t terminate.

EDIT: I think what I am looking for is tying the start and stop of profiling with the start and stop of a specific function within a larger application. Does anyone know how that can be achieved? So the report is generated on the termination of a given specified function, not the program as a whole.

---

<div class="post-metadata">

**Author:** ![romes](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/romes/32/2912_2.png) [@romes](https://discourse.haskell.org/u/romes)\
**Post date:** [October 4, 2024, 11:33pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/2 "2024-10-04T23:33:58Z")

</div>

Hi @morphismz.

I feel like profiling Haskell programs is both easy and really cool!  
It would be great if this feeling were more widespread. If you come up with a good place to write down the following information more prominently, we should try to put it there.

To profile a Haskell executable from a large Cabal project, you need to, first and foremost, compile your executable with profiling. To do so, pass `--enable-profiling` to Cabal, and use late-cost-center profiling for profiling to not interfere with optimisation (`--profiling-detail=late`).

```txt
cabal build --enable-profiling --profiling-detail=late exe:my-executable-name

```

Second, you must run the program with the RTS flags to produce a profile. I suggest using `-pj` to produce a flame graph profile in JSON that can be loaded into [https://speedscope.app](https://speedscope.app). `-pj` must be passed after `+RTS`, or in between `+RTS` and `-RTS`, because it is a runtime system option.

```haskell
cabal list-bin exe:my-executable-name
> /path/to/exe
/path/to/exe +RTS -pj -RTS
> <program is now running>

```

At this point, you can simply load the `my-executable-name.prof` file into [https://speedscope.app](https://speedscope.app) to get something like:

 ![Screenshot 2024-10-05 at 00.32.31](https://us1.discourse-cdn.com/flex002/uploads/haskell/original/2X/8/83824ddf13c7c1ee1258f637837f783cd37f0a06.jpeg)

* * *

## Profiling your dependencies too

Even though this is plenty good, it’s often useful or needed to profile _your dependencies too_. The reason why this doesn’t happen by default is because cabal command line flags apply to _local packages_ only. However, all dependencies fetched from hackage count as non-local.

To apply profiling and profiling-detail: late to all packages/dependencies in your project, add to the project’s `cabal.project`:

```haskell
package *
    profiling: true
    profiling-detail: late

```

Now, recompile your executable and re-run with `+RTS -pj` and reload speedscope to get a more detailed profile with all relevant packages. You should see all packages being built with profiling, for example:

```haskell
$ cabal build exe:cabal
Resolving dependencies...
Build profile: -w ghc-9.10.1 -O1
In order, the following will be built (use -v for more details):
 - base16-bytestring-1.0.2.0 (lib) --enable-profiling (requires build)
 - base64-bytestring-1.2.1.0 (lib) --enable-profiling (requires build)
 - echo-0.1.4 (lib) --enable-profiling (requires build)
 - cryptohash-sha256-0.11.102.1 (lib) --enable-profiling (requires build)
 - ed25519-0.0.5.0 (lib) --enable-profiling (requires build)
 - hsc2hs-0.68.10 (exe:hsc2hs) --enable-profiling (requires build)
 - hashable-1.4.7.0 (lib) --enable-profiling (requires build)
 - alex-3.5.1.0 (exe:alex) --enable-profiling (requires build)
 - open-browser-0.2.1.0 (lib) --enable-profiling (requires build)
 - regex-base-0.94.0.2 (lib) --enable-profiling (requires build)
 - safe-exceptions-0.1.7.4 (lib) --enable-profiling (requires build)
 - splitmix-0.1.0.5 (lib) --enable-profiling (requires build)
 - th-compat-0.1.5 (lib) --enable-profiling (requires build)
 - tar-0.6.3.0 (lib:tar-internal) --enable-profiling (requires build)
 - resolv-0.2.0.2 (lib:resolv) --enable-profiling (requires build)
 - zlib-0.7.1.0 (lib) --enable-profiling (requires build)
 - network-3.2.3.0 (lib:network) --enable-profiling (requires build)
 - lukko-0.1.2 (lib) --enable-profiling (requires build)
 - async-2.2.5 (lib) --enable-profiling (requires build)
 - Cabal-syntax-3.13.0.0 (lib) --enable-profiling (cannot read state cache)
 - regex-posix-0.96.0.1 (lib) --enable-profiling (requires build)
 - random-1.2.1.2 (lib) --enable-profiling (requires build)
 - network-uri-2.6.4.2 (lib) --enable-profiling (requires build)
 - tar-0.6.3.0 (lib) --enable-profiling (requires build)
 - Cabal-3.13.0.0 (lib) --enable-profiling (cannot read state cache)
 - edit-distance-0.2.2.1 (lib) --enable-profiling (requires build)
 - HTTP-4000.4.1 (lib) --enable-profiling (requires build)
 - hackage-security-0.6.2.6 (lib) --enable-profiling (configuration changed)
 - cabal-install-solver-3.13.0.0 (lib) --enable-profiling (configuration changed)
 - cabal-install-3.13.0.0 (lib) --enable-profiling (configuration changed)
 - cabal-install-3.13.0.0 (exe:cabal) --enable-profiling (configuration changed)

```

---

<div class="post-metadata">

**Author:** ![simonmic](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/simonmic/32/1796_2.png) [@simonmic](https://discourse.haskell.org/u/simonmic)\
**Post date:** [October 5, 2024, 12:00am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/3 "2024-10-05T00:00:39Z")

</div>

Great info (one piece of the big puzzle at least).

You didn’t mention stopping the app. I assume speedscope.app operates on a point in time snapshot of the .prof file, though I do remember hearing about a viewer that updates as the profile data updates.

About profiling your dependencies. Is it ever realistic / useful to profile one’s app but not the dependencies, in practice ?

---

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 5, 2024, 12:32am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/4 "2024-10-05T00:32:19Z")

</div>

Hi @romes

Thank you, this is a great response. I have already done much of what you said, but it took quite a while to find the pieces scattered across different documentation. So its nice to have all in once place, and well laid out 🙂 And its great to know about speedscope.

But I think this is missing the main question I’m looking to have answered. I have already set up basic profiling in my cabal project, following essentially the same steps you describe (only I have more stuff directly in the cabal files, rather than used at the command line). And I can generate a `.prof` file after running a small terminating program.

The problem I’m running into is trying to profile a large interactive application. This application does not naturally stop running. In fact, if it did stop running, that would be a bug. The `.prof` file always seems to be generated after the completion of the program. So, with my use case, I never actually end up with a (non-empty) `.prof` file.

Is there some option to generate the `.prof` file at regular intervals, or after a specific event that has been marked in the code? How does one usually approach profiling large and constantly running applications?

tldr; I have successfully been able to use profiling on extremely simple programs. But I looking for a resource that introduces more advanced profiling techniques that are able to analyze large, constantly running applications. All the resources I have come across only go as far as profiling a trivial, terminating program.

EDIT: I should add, I do not mean that I am trying to analyze an already running application. I’m thinking more like a web application. I can build it with profiling and start running it. But once I start running it, there is no natural turn off point. There are a few natural or key events/functions which I want included in the cost centers. The profiling does not need to be instantaneous or “live”. But it should not rely on the application/program terminating.

---

<div class="post-metadata">

**Author:** ![velveteer](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/velveteer/32/3474_2.png) [@velveteer](https://discourse.haskell.org/u/velveteer)\
**Post date:** [October 5, 2024, 2:00am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/5 "2024-10-05T02:00:57Z")

</div>

I have done live heap profiles on similar applications using GHC’s eventlog combined with [eventlog2html](https://mpickering.github.io/eventlog2html/). The important piece is the GHC RTS option `--eventlog-flush-interval`: [5.7. Runtime system (RTS) options — Glasgow Haskell Compiler 9.10.1 User's Guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/runtime_control.html#rts-flag---eventlog-flush-interval=%E2%9F%A8seconds%E2%9F%A9)

A crude live render can then be accomplished with a file-watcher like `entr`, e.g.

```haskell
ls *.eventlog | entr -s 'eventlog2html $0'

```

Granted this was only for heap profiling, but hopefully it helps you. Further efforts in live profiling have led to [GitHub - well-typed/ghc-eventlog-socket: Pipe the GHC eventlog stream to a UNIX domain socket](https://github.com/well-typed/ghc-eventlog-socket) and [Glasgow Haskell Compiler / ghc-debug · GitLab](https://gitlab.haskell.org/ghc/ghc-debug), but these are a little more involved.

---

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 5, 2024, 2:32am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/6 "2024-10-05T02:32:41Z")

</div>

@velveteer Beautiful, this looks quite like what I want. It seems like I should give the whole RTS section a good read through as well.

---

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 5, 2024, 6:19am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/7 "2024-10-05T06:19:41Z")

</div>

I think one more thing I am looking for is to tie profiling to a specific function within the application. So profiling could start when this function starts to be evaluated, and the `.prof` file would be generated when this function finishes evaluating. I realize this may not be a fully sensible request, since the function could be called multiple times, or call itself. But I’m wondering if there is some utility along these lines, one that allows analyzing a specific function.

Its important that the `.prof` file, or what ever other logs, be generated on the termination of the function, not the program as a whole.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [October 5, 2024, 7:39am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/8 "2024-10-05T07:39:31Z")

</div>

> [@morphismz](#):
>
> […] the function could be called multiple times, or call itself.

Another possibility is the function is used to define (theoretically) an infinite structured value, as allowed by Haskell’s non-strict semantics:

> [@](#):
>
> ```haskell
> primes = filterPrime [2..] where
> filterPrime (p:xs) =
> p : filterPrime [x | x <- xs, x `mod` p /= 0]
> 
> ```
> 
> [https://www.haskell.org](https://www.haskell.org)

So if `filterPrime` is the (local) function being profiled, there’s no way to know with certainty when it will _“terminate”_ i.e. stop being used by `primes` - that would require knowing exactly when `primes` stops being used by it’s callers, and so forth. Hence the suggestion by **velveteer** regarding _“live rendering”_ - since your program is intended to run indefinitely, the logs will need to be streamed in some way to avoid running out of storage.

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [October 5, 2024, 9:45am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/9 "2024-10-05T09:45:40Z")

</div>

You can use the `--no-automatic-time-samples` RTS flag along with the start and stop actions from this module: [GHC.Profiling](https://hackage.haskell.org/package/base-4.20.0.1/docs/GHC-Profiling.html).

Alternatively you can disable automatic cost centre insertion and do so manually with `SCC` pragmas.

---

<div class="post-metadata">

**Author:** ![malteneuss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/malteneuss/32/2541_2.png) [@malteneuss](https://discourse.haskell.org/u/malteneuss)\
**Post date:** [October 5, 2024, 10:33am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/10 "2024-10-05T10:33:24Z")

</div>

> [@romes](#):
>
> It would be great if this feeling were more widespread. If you come up with a good place to write down the following information more prominently, we should try to put it there.

This is a great guide. Would you consider donating/adding it to the [Cabal documentation](https://cabal.readthedocs.io/en/stable/)? The documentation page was recently restructured to support typical developer tasks. One guide for profiling is missing under the Cabal guide section. 🙂

---

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 6, 2024, 12:53am UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/11 "2024-10-06T00:53:54Z")

</div>

@TeofilC awesome, thanks for pointing this module out to me, I couldn’t seem to find that in any of my searches. I tried using `stopProfTimer`. But the `.prof` file remains empty even after this function should have been executed by the code. What mechanism actually causes writing to the `.prof` file? Is there a way to trigger this with a function like `stopProfTimer`?

Could you explain a bit about how manual `SCC` insertions is an alternative to the functions in that `GHC.Profiling`? My understanding is that the`.prof` file still would not be generated until the termination of the program.

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [October 6, 2024, 1:12pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/12 "2024-10-06T13:12:16Z")

</div>

These two techniques are helpful for only collecting profiling information for parts of the run of your program and/or only specific functions.

I don’t think it’s possible to emit `.prof` files as your program is running (at the moment). In general, when profilling a server style application, what I would do is start the program, hit the endpoints you want information about, stop the application and then analyse the `.prof` file.

Why do you want to avoid the program terminating? If this is a strong requirement for you then your best bet is what’s mentioned here: [GHC Profiling a Cabal Project with an Interactive Application - #5 by velveteer](http://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/5) (this uses the newer eventlog format which actively emits information as your program runs rather than just at the end) combined with the start stop functions I mention. You should note that profiling always has a bit of a performance cost, so it’s often not desirable to do it in production.

> [@morphismz](#):
>
> Could you explain a bit about how manual `SCC` insertions is an alternative to the functions in that `GHC.Profiling`? My understanding is that the`.prof` file still would not be generated until the termination of the program.

You’re right. It’s a helpful way to limit the output from profiling to just the functions you care about, but doesn’t help with your problem of wanting live profiling information without stopping the program.

---

<div class="post-metadata">

**Author:** ![romes](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/romes/32/2912_2.png) [@romes](https://discourse.haskell.org/u/romes)\
**Post date:** [October 6, 2024, 3:58pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/13 "2024-10-06T15:58:27Z")

</div>

Yes. By all means, do add it to where you see fit.

---

<div class="post-metadata">

**Author:** ![LaurentRDC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/laurentrdc/32/4950_2.png) [@LaurentRDC](https://discourse.haskell.org/u/LaurentRDC)\
**Post date:** [October 6, 2024, 6:42pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/14 "2024-10-06T18:42:24Z")

</div>

[How come ‘late’ isn’t specified as an option for the ‘profiling-detail’ project option?](https://cabal.readthedocs.io/en/3.4/setup-commands.html?highlight=profiling-detail#cmdoption-runhaskell-Setup.hs-configure-profiling-detail)

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [October 6, 2024, 7:37pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/15 "2024-10-06T19:37:46Z")

</div>

Because you’re looking at the old docs for 3.4; the docs for 3.12 (and “stable”) do list it: [https://cabal.readthedocs.io/en/stable/setup-commands.html#cmdoption-runhaskell-Setup.hs-configure-profiling-detail](https://cabal.readthedocs.io/en/stable/setup-commands.html#cmdoption-runhaskell-Setup.hs-configure-profiling-detail)

---

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 6, 2024, 8:10pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/16 "2024-10-06T20:10:47Z")

</div>

@TeofilC I don’t need to avoid the program terminating. But, when I terminate the program by killing the process, I end up with an empty `.prof` file. I would need to significantly alter the program for it to terminate on its own. I was hoping to avoid having to build into the program some termination condition.

What do you have in mind when you mean “stop the application”? When I run my application at the command line via cabal, `Ctrl + C` doesn’t stop the program, so I have to kill the process.

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [October 6, 2024, 8:38pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/17 "2024-10-06T20:38:42Z")

</div>

Pressing `Ctrl+C` (once, pressing multiple times will immediately kill it and not write a file) is what I had in mind. It sounds like your application has maybe overwritten the signal handler in some way? You mentioned yesod in your original post. I know warp, the webserver underlying yesod, might do something around in this area. You might want to use [Network.Wai.Handler.Warp](https://hackage.haskell.org/package/warp-3.4.2/docs/Network-Wai-Handler-Warp.html#v:setGracefulShutdownTimeout)

But it sounds like the issue you are facing is that you application doesn’t honor `Ctrl+C`. Can you share your code?

---

<div class="post-metadata">

**Author:** ![morphismz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/morphismz/32/5340_2.png) [@morphismz](https://discourse.haskell.org/u/morphismz)\
**Post date:** [October 6, 2024, 9:05pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/18 "2024-10-06T21:05:14Z")

</div>

@TeofilC Got, so it sounds like killing the process is the reason that no `.prof` file is being generated. Thank you for the pointer to `setGracefulShutdownTimeout`. I’ll see if I can set up something like this in my code.

Unfortunately, I cannot share the code. Sorry I know that is rude and annoying as I’m asking for help. But your comments have been very helpful. I think I can get something working. I’ll spend some more time trying to figure out why `Ctrl+C` is not being honored. I guess I just didn’t realize that my “ungraceful” termination of the program was the reason no `.prof` file was being written. I thought it would be written on execution of `stopProfTimer`. Though it makes sense in hindsight, knowing that `.prof` files cannot be generated while the program is running.

Thanks again for the help!

---

<div class="post-metadata">

**Author:** ![TeofilC](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/teofilc/32/3987_2.png) [@TeofilC](https://discourse.haskell.org/u/TeofilC)\
**Post date:** [October 6, 2024, 9:20pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/19 "2024-10-06T21:20:16Z")

</div>

No worries at all! That’s not rude at all.

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [October 7, 2024, 7:15pm UTC](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465/20 "2024-10-07T19:15:34Z")

</div>

(All of the following may be outdated.) I had a [very similar question](https://mail.haskell.org/pipermail/haskell-cafe/2022-May/135298.html) two years ago. It was about a Yesod application. Two things helped:

1. have a control thread that catches the Ctrl+C signal and gracefully terminate the program. Beware that standard Yesod applications `main = warp` will catch _all_ sorts of exceptions and try to keep the server running. See [this thread](https://mail.haskell.org/pipermail/haskell-cafe/2022-March/135132.html) for remedies.
2. build in an option that gracefully shuts down the program after a specified time

Either ensures that the .prof file is written and not empty. However, due to the reasons listed in the question above, the .prof was not very helpful for me. In any case, I liked the way [profiteur](https://hackage.haskell.org/package/profiteur) can visualize the .prof file content.

> [@morphismz](#):
>
> But I don’t see a way to get this to work with my actual application, which won’t terminate.

If possible, separate the functions doing all the work from the web server stuff, so that a mock application with finite lifetime can be used for profiling. It may be helpful for test suites, too.

[Next page](https://discourse.haskell.org/t/ghc-profiling-a-cabal-project-with-an-interactive-application/10465.md?page=2)
