Skip to content

Instantly share code, notes, and snippets.

@TerrorJack
Created January 22, 2024 11:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TerrorJack/9f73679edb011ef93c53d3f3b375559a to your computer and use it in GitHub Desktop.
Save TerrorJack/9f73679edb011ef93c53d3f3b375559a to your computer and use it in GitHub Desktop.

How to instrument ghc with ghc-debug

Assuming you already know what ghc-debug is about, and want to use it to debug the ghc program itself.

Prepare a ghc-debug checkout

A recent ghc-debug checkout is required, since it contains important fixes not release on Hackage yet.

Apply the following patch:

diff --git a/stub/cbits/stub.cpp b/stub/cbits/stub.cpp
index c11fa94..62f7779 100644
--- a/stub/cbits/stub.cpp
+++ b/stub/cbits/stub.cpp
@@ -21,9 +21,9 @@
 #include <stdarg.h>
 #include <stdio.h>
 
-#if !defined(THREADED_RTS)
-#error You must use a patched version of cabal-install which includes - https://github.com/haskell/cabal/pull/7183
-#endif
+#include "ghcversion.h"
+
+#define THREADED_RTS
 
 #if !defined(TABLES_NEXT_TO_CODE)
 #error TABLES_NEXT_TO_CODE not defined

Prepare a ghc checkout

Apply the following patch:

diff --git a/ghc/Main.hs b/ghc/Main.hs
index 730a614839b..f3a62353c10 100644
--- a/ghc/Main.hs
+++ b/ghc/Main.hs
@@ -101,6 +101,8 @@ import Data.Bifunctor
 import GHC.Data.Graph.Directed
 import qualified Data.List.NonEmpty as NE
 
+import GHC.Debug.Stub
+
 -----------------------------------------------------------------------------
 -- ToDo:
 
@@ -114,7 +116,7 @@ import qualified Data.List.NonEmpty as NE
 -- GHC's command-line interface
 
 main :: IO ()
-main = do
+main = withGhcDebug $ do
    hSetBuffering stdout LineBuffering
    hSetBuffering stderr LineBuffering
 
diff --git a/ghc/ghc-bin.cabal.in b/ghc/ghc-bin.cabal.in
index bd49fd0e6f8..611cdde777d 100644
--- a/ghc/ghc-bin.cabal.in
+++ b/ghc/ghc-bin.cabal.in
@@ -39,6 +39,7 @@ Executable ghc
                    filepath   >= 1   && < 1.5,
                    containers >= 0.5 && < 0.7,
                    transformers >= 0.5 && < 0.7,
+                   ghc-debug-stub,
                    ghc-boot      == @ProjectVersionMunged@,
                    ghc           == @ProjectVersionMunged@
 
diff --git a/hadrian/src/Packages.hs b/hadrian/src/Packages.hs
index 37b793626c3..740741c6bd2 100644
--- a/hadrian/src/Packages.hs
+++ b/hadrian/src/Packages.hs
@@ -11,6 +11,7 @@ module Packages (
     libffi, mtl, parsec, pretty, primitive, process, remoteIserv, rts,
     runGhc, semaphoreCompat, stm, templateHaskell, terminfo, text, time, timeout, touchy,
     transformers, unlit, unix, win32, xhtml,
+    ghcDebugConvention, ghcDebugStub,
     lintersCommon, lintNotes, lintCodes, lintCommitMsg, lintSubmoduleRefs, lintWhitespace,
     ghcPackages, isGhcPackage,
 
@@ -43,6 +44,7 @@ ghcPackages =
     , hp2ps, hpc, hpcBin, integerGmp, integerSimple, iserv, libffi, mtl
     , parsec, pretty, process, rts, runGhc, stm, semaphoreCompat, templateHaskell
     , terminfo, text, time, touchy, transformers, unlit, unix, win32, xhtml
+    , ghcDebugConvention, ghcDebugStub
     , timeout
     , lintersCommon
     , lintNotes, lintCodes, lintCommitMsg, lintSubmoduleRefs, lintWhitespace ]
@@ -60,6 +62,7 @@ array, base, binary, bytestring, cabalSyntax, cabal, checkPpr, checkExact, count
   hp2ps, hpc, hpcBin, integerGmp, integerSimple, iserv, iservProxy, remoteIserv, libffi, mtl,
   parsec, pretty, primitive, process, rts, runGhc, semaphoreCompat, stm, templateHaskell,
   terminfo, text, time, touchy, transformers, unlit, unix, win32, xhtml,
+  ghcDebugConvention, ghcDebugStub,
   timeout,
   lintersCommon, lintNotes, lintCodes, lintCommitMsg, lintSubmoduleRefs, lintWhitespace
     :: Package
@@ -133,6 +136,9 @@ unix                = lib  "unix"
 win32               = lib  "Win32"
 xhtml               = lib  "xhtml"
 
+ghcDebugConvention  = lib  "ghc-debug-convention"
+ghcDebugStub        = lib  "ghc-debug-stub"
+
 lintersCommon       = lib     "linters-common"      `setPath` "linters/linters-common"
 lintNotes           = linter  "lint-notes"
 lintCodes           = linter  "lint-codes"
diff --git a/hadrian/src/Settings/Default.hs b/hadrian/src/Settings/Default.hs
index 11f69dd0c49..01d81abe2b9 100644
--- a/hadrian/src/Settings/Default.hs
+++ b/hadrian/src/Settings/Default.hs
@@ -113,6 +113,7 @@ stage0Packages = do
              , hp2ps
              , if windowsHost then win32 else unix
              ]
+          ++ [ ghcDebugConvention, ghcDebugStub ]
           ++ [ terminfo | not windowsHost, not cross ]
           ++ [ timeout  | windowsHost                ]
           ++ [ touchy   | windowsHost                ]

Suppose the ghc-debug checkout is at /workspace/ghc-debug, ghc checkout is at /workspace/ghc, copy the ghc-debug-convention and ghc-debug-stub packages to the ghc tree: cp -r /workspace/ghc-debug/conv /workspace/ghc/libraries/ghc-debug-convention && cp -r /workspace/ghc-debug/stub /workspace/ghc/libraries/ghc-debug-stub

Build and use instrumented ghc

Run boot, configure and normal hadrian build process. The resulting in-tree _build/stage1/bin/ghc and _build/stage1/bin/ghc-pkg can be used to build cabal projects, put these in your cabal.project.local file:

with-compiler: /workspace/ghc/_build/stage1/bin/ghc
with-hc-pkg: /workspace/ghc/_build/stage1/bin/ghc-pkg

-- the rest of https://gitlab.haskell.org/ghc/head.hackage#how-to-use follows

For instance, they can be used to build ghc-debug-brick in the ghc-debug tree, which can be used to check a ghc --interactive session, pause the process and explore the default gc roots to see if ghc-debug instrumentation works at all.

Some caveats to keep in mind:

  • The ghc patch does not take cross compilation into account
  • You can use GHC.Settings.Config.cStage == "2" to only enable withGhcDebug for _build/stage1/bin/ghc, instead of _build/stage0/bin/ghc which builds it. This speeds up compilation quite a bit
  • withGhcDebug prints the debug socket address to stderr, so you can’t run the ghc testsuite with instrumented ghc since there will be stderr content mismatches, unless you only enable it conditionally (e.g. via an environment variable)
  • ghc-debug already supports cost center profiling, but I have not verified yet whether it actually works with ghc built with profiled_ghc or if more hacks are needed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment