Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
  • GHC GHC
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 4,969
    • Issues 4,969
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
  • Merge requests 473
    • Merge requests 473
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Releases
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Glasgow Haskell Compiler
  • GHCGHC
  • Wiki
  • dwarf

dwarf · Changes

Page history
Edit DWARF authored Jan 02, 2015 by scpmw's avatar scpmw
Hide whitespace changes
Inline Side-by-side
dwarf.md
View page @ bc4ab424
......@@ -2,25 +2,25 @@
As of 7.10, GHC has basic functionality to generate DWARF-compliant
debugging information with its binaries. This opens up a completely
new way of debugging or profiling Haskell programs: Instead of
instrumenting the program or the runtime system, we "simply" teach
standard debugging tools to make sense of a running Haskell program.
instrumenting the program or the runtime system, we teach
external debugging tools to make sense of a running Haskell program.
This means that we gain debugging and profiling capabilities in
situations where existing profiling approaches could not help us, such
as for crashes or code that we cannot meaningfully instrument.
There are a few caveats to this method. Firstly, GHC optimisations can
There are a few caveats to this method. GHC optimisations can
be very aggressive in reorganizing the code and avoiding redundancies
at runtime. This means that even with good heuristics, there will be
situations where information is lost. On the other hand, DWARF-based
situations where information is lost. DWARF-based
debugging tools also make assumptions about the code that are more
geared towards C-like languages, and get confused for Haskell programs.
Bottom line: While the infrastructure should be perfectly stable and
While the infrastructure should be perfectly stable and
safe to use, inspecting Haskell programs like this is still very much
"wild west". Having a good working knowledge of low-level Haskell
execution is definetely a good idea. We hope that some experience will
execution is definitely a good idea. We hope that some experience will
help us improve the situation.
## Basic Set-Up
......@@ -35,27 +35,26 @@ fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
main :: IO ()
main = putStrln $ fib 20
main = print $ fib 20
```
Just like with GCC, the "magic" command line flag here is `-g`. This
causes GHC to generate DWARF sections into produced binaries:
Just like with GCC, the command line flag to generate DWARF sections is `-g`:
```wiki
ghc -O -g -rtsopts fib.hs
```
For Mac Os, debug information is actually kept separate from the
binary, so we first have to package it using `dsymutil` at this point:
For Mac Os, debug information is kept separate from the
binary, so we first have to package it using `dsymutil`:
```wiki
dsymutil fib
```
If we want, we can now check that DWARF information was generated
We can now check that DWARF information was generated
correctly using `objdump` or `dwarfdump` respectively:
```wiki
......@@ -74,14 +73,14 @@ TAG_compile_unit [1] *
```
Which tells us that we have DWARF information about the compilation
This tells us that we have DWARF information about the compilation
unit `fib.hs`. Note that the Haskell language ID `0x18` is not
recognized by all tools yet, so you might see an "Unknown" there
instead.
It is important to realize that - in contrast to instrumentation -
adding DWARF debug information does not actually change the executable
adding DWARF debug information does not change the code
sections of the executable. In fact, we can strip them without
changing the performance characteristics of the program at all:
......@@ -113,7 +112,7 @@ real-life performance.
At this point, we can simply invoke `gdb` and set breakpoints (TODO -
doesn't work on Mac yet):
doesn't quite work on Mac yet):
```wiki
gdb -q fib
......@@ -129,7 +128,7 @@ Breakpoint 1, Main_zdwfib_info () at fib.hs:3
```
At this point we have actually stopped the Haskell program at the
At this point we have stopped the Haskell program at the
given line. We can now step a bit further to see the program working
its magic:
......@@ -157,8 +156,9 @@ c3Vy_info () at fib.hs:4
```
Note that passing `+RTS -V0` as we did above is necessary for this to
work, as otherwise we would end up stepping into the RTS timer.
Note that passing `+RTS -V0` as program command line parameter is
necessary for this to work, as otherwise we would end up stepping into
the RTS timer.
Furthermore, we can back-trace to get an idea of where we were coming
......@@ -192,22 +192,21 @@ Let us have a closer look at the stack:
- First we have a number of return closures to our `fib`
function. That the stack is so "clean" means that we have no lazy
evaluation going on, which in this example is a result of compiling
evaluation going on, which is a result of compiling
with optimisations above (`-O`). Perhaps unintuitively, simple
optimisations often have a beneficial effect on stack trace clarity.
optimisations often improve stack trace clarity.
- On the other hand, we see that we have no reference to the `main`
function any more. In fact, the stack only references `show` and
`putStrLn` respectively, which is what `print` unfolds to. Here GHC
figured out that it can tail-call, causing a "hole" in our stack
trace.
- On the other hand, `main` does not appear in the stack any more. In
fact, where it should be we only see `show` and `putStrLn`
respectively, which is what `print` unfolds to. Here GHC figured out
that it can tail-call, causing a "hole" in our stack trace.
- Finally, the two last frames are RTS frames, corresponding to
`stg_catch_frame` (the top-level exception handler) and
`stg_stop_thread`, which is the stack frame handling returning the
thread to the RTS. This is where the debugger runs out of guidance
from the DWARF information and complains with "corrupt stack?"
error.
from the DWARF information and complains with the "corrupt stack?"
error message.
## Profiling
......@@ -244,8 +243,10 @@ This yields us a profiling view that looks as follows:
```
Note that due to DWARF information, `perf` has managed to locate the
source code that belongs to the "hot" assembler code.
DWARF information allows `perf` to locate the source code that belongs
to the "hot" assembler code. Note that the fairly nonsensical \`add
%al,(%r8)` instructions is info table data, which `perf\` is
interpreting as code.
## Open Issues
......@@ -262,14 +263,14 @@ debugging tools or replacing them with our own:
- Note that both `perf` as well as `gdb` refer to blocks using their
backend names (e.g. `c3Vl_info`). This is not GHC's fault - we
correctly set the `DW_AT_name` for `DW_TAG_subprogram` records. The
current state of investigation is that debugging tools seemingly
current state of investigation is that debugging tools
prefer to use language-dependent "unmangling" of symbol names. We
probably will have to patch these programs to do something sensible
for Haskell code.
- Furthermore, the symbol names in `gbd`'s backtrace are actually
- Furthermore, the symbol names in `gbd`'s backtrace are
wrong -- notice the `??` entries. On the other hand, note that \`info
symbol\` still gives us the right answer. What is actually happening
symbol\` still gives us the right answer. What is happening
here is that `gdb` looks up the symbol with an offset of 1, as that
makes sense for C-like programs (see `[Note: Info Offset]` in
`compiler/nativeGen/Dwarf/Types.hs` for details). This is another
......@@ -291,7 +292,7 @@ Runtime problems - can probably be fixed by using Haskell-specific
tools and/or improving infrastructure further:
- When trying to look at stack traces, there are two characteristics
of Haskell code that give us a hard time. The first one is that
of Haskell code leading to problems. The first one is that
tail-calls are very common, and not entirely straight-forward to
prevent. This is significant, because sometimes we positively want
to know whether a certain function is on the stack frame or not.
......@@ -311,7 +312,7 @@ tools and/or improving infrastructure further:
almost entirely useless - we would essentially be looking at
variants of `$`, `.` and `>>=` all the time. Instead, we look for
the most specific source note that belongs to the currently compiled
compilation unit. This heuristic works quite well, but it is not
compilation unit. This heuristic works well, but it is not
hard to demonstrate situations where it goes wrong.
......@@ -330,7 +331,8 @@ a bit:
> (Also note that tools with more knowledge about the Haskell stack
> can work around this issue somewhat by using info tables to traverse
> the Haskell stack. Explaining this strategy to DWARF readers might
> also be possible, Nathan Howell had done some experiments on this.)
> also be possible, Nathan Howell has done some experiments on this if
> I remember correctly.)
- Implementing our own tools depends on our ability to read binaries
and the contained DWARF information. So far we have used `libdwarf`,
......
Clone repository Edit sidebar
  • 9.6
  • Adventures in GHC compile times
  • All things layout
  • AndreasK
  • AndreasPK
  • Back End and Run Time System
  • Backpack refactoring
  • Backpack units
  • Brief Guide for Compiling GHC to iOS
  • Building GHC on Windows with Stack protector support (SSP) (using Make)
  • CAFs
  • CafInfo rework
  • Compiling Case Expressions in ghc
  • Compiling Data.Aeson Error
  • Contributing a Patch
View All Pages