summaryrefslogtreecommitdiff
path: root/compiler/cmm/Debug.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/cmm/Debug.hs')
-rw-r--r--compiler/cmm/Debug.hs100
1 files changed, 94 insertions, 6 deletions
diff --git a/compiler/cmm/Debug.hs b/compiler/cmm/Debug.hs
index 33595d8987..da37495530 100644
--- a/compiler/cmm/Debug.hs
+++ b/compiler/cmm/Debug.hs
@@ -22,6 +22,8 @@ module Debug (
UnwindExpr(..), toUnwindExpr
) where
+import GhcPrelude
+
import BlockId
import CLabel
import Cmm
@@ -33,7 +35,7 @@ import Outputable
import PprCore ()
import PprCmmExpr ( pprExpr )
import SrcLoc
-import Util
+import Util ( seqList )
import Hoopl.Block
import Hoopl.Collections
@@ -44,6 +46,7 @@ import Data.Maybe
import Data.List ( minimumBy, nubBy )
import Data.Ord ( comparing )
import qualified Data.Map as Map
+import Data.Either ( partitionEithers )
-- | Debug information about a block of code. Ticks scope over nested
-- blocks.
@@ -98,7 +101,7 @@ cmmDebugGen modLoc decls = map (blocksForScope Nothing) topScopes
-- Analyse tick scope structure: Each one is either a top-level
-- tick scope, or the child of another.
(topScopes, childScopes)
- = splitEithers $ map (\a -> findP a a) $ Map.keys blockCtxs
+ = partitionEithers $ map (\a -> findP a a) $ Map.keys blockCtxs
findP tsc GlobalScope = Left tsc -- top scope
findP tsc scp | scp' `Map.member` blockCtxs = Right (scp', tsc)
| otherwise = findP tsc scp'
@@ -328,7 +331,7 @@ code,
v :: P64 = R2;
if ((Sp + 8) - 32 < SpLim) (likely: False) goto c2ff; else goto c2fg;
-After c2fe we we may pass to either c2ff or c2fg; let's first consider the
+After c2fe we may pass to either c2ff or c2fg; let's first consider the
former. In this case there is nothing in particular that we need to do other
than reiterate what we already know about Sp,
@@ -346,8 +349,8 @@ in addition to the usual beginning-of-block statement,
unwind Sp = Just Sp + 0;
I64[Sp - 8] = c2dD;
R1 = v :: P64;
- unwind Sp = Just Sp + 8;
Sp = Sp - 8;
+ unwind Sp = Just Sp + 8;
if (R1 & 7 != 0) goto c2dD; else goto c2dE;
The remaining blocks are simple,
@@ -389,10 +392,95 @@ The flow of unwinding information through the compiler is a bit convoluted:
* This unwind information is converted to DebugBlocks by Debug.cmmDebugGen
- * These DebugBlcosk are then converted to, e.g., DWARF unwinding tables
+ * These DebugBlocks are then converted to, e.g., DWARF unwinding tables
(by the Dwarf module) and emitted in the final object.
-See also: Note [Unwinding information in the NCG] in AsmCodeGen.
+See also:
+ Note [Unwinding information in the NCG] in AsmCodeGen,
+ Note [Unwind pseudo-instruction in Cmm],
+ Note [Debugging DWARF unwinding info].
+
+
+Note [Debugging DWARF unwinding info]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For debugging generated unwinding info I've found it most useful to dump the
+disassembled binary with objdump -D and dump the debug info with
+readelf --debug-dump=frames-interp.
+
+You should get something like this:
+
+ 0000000000000010 <stg_catch_frame_info>:
+ 10: 48 83 c5 18 add $0x18,%rbp
+ 14: ff 65 00 jmpq *0x0(%rbp)
+
+and:
+
+ Contents of the .debug_frame section:
+
+ 00000000 0000000000000014 ffffffff CIE "" cf=1 df=-8 ra=16
+ LOC CFA rbp rsp ra
+ 0000000000000000 rbp+0 v+0 s c+0
+
+ 00000018 0000000000000024 00000000 FDE cie=00000000 pc=000000000000000f..0000000000000017
+ LOC CFA rbp rsp ra
+ 000000000000000f rbp+0 v+0 s c+0
+ 000000000000000f rbp+24 v+0 s c+0
+
+To read it http://www.dwarfstd.org/doc/dwarf-2.0.0.pdf has a nice example in
+Appendix 5 (page 101 of the pdf) and more details in the relevant section.
+
+The key thing to keep in mind is that the value at LOC is the value from
+*before* the instruction at LOC executes. In other words it answers the
+question: if my $rip is at LOC, how do I get the relevant values given the
+values obtained through unwinding so far.
+
+If the readelf --debug-dump=frames-interp output looks wrong, it may also be
+useful to look at readelf --debug-dump=frames, which is closer to the
+information that GHC generated.
+
+It's also useful to dump the relevant Cmm with -ddump-cmm -ddump-opt-cmm
+-ddump-cmm-proc -ddump-cmm-verbose. Note [Unwind pseudo-instruction in Cmm]
+explains how to interpret it.
+
+Inside gdb there are a couple useful commands for inspecting frames.
+For example:
+
+ gdb> info frame <num>
+
+It shows the values of registers obtained through unwinding.
+
+Another useful thing to try when debugging the DWARF unwinding is to enable
+extra debugging output in GDB:
+
+ gdb> set debug frame 1
+
+This makes GDB produce a trace of its internal workings. Having gone this far,
+it's just a tiny step to run GDB in GDB. Make sure you install debugging
+symbols for gdb if you obtain it through a package manager.
+
+Keep in mind that the current release of GDB has an instruction pointer handling
+heuristic that works well for C-like languages, but doesn't always work for
+Haskell. See Note [Info Offset] in Dwarf.Types for more details.
+
+Note [Unwind pseudo-instruction in Cmm]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+One of the possible CmmNodes is a CmmUnwind pseudo-instruction. It doesn't
+generate any assembly, but controls what DWARF unwinding information gets
+generated.
+
+It's important to understand what ranges of code the unwind pseudo-instruction
+refers to.
+For a sequence of CmmNodes like:
+
+ A // starts at addr X and ends at addr Y-1
+ unwind Sp = Just Sp + 16;
+ B // starts at addr Y and ends at addr Z
+
+the unwind statement reflects the state after A has executed, but before B
+has executed. If you consult the Note [Debugging DWARF unwinding info], the
+LOC this information will end up in is Y.
-}
-- | A label associated with an 'UnwindTable'