diff --git a/compiler/GHC/Driver/Flags.hs b/compiler/GHC/Driver/Flags.hs
index 0a159b330ea65ef6ffcac295b99c543b868692f7..f26e5e22a34a076a4001bff2a4c528e57129c3eb 100644
--- a/compiler/GHC/Driver/Flags.hs
+++ b/compiler/GHC/Driver/Flags.hs
@@ -161,6 +161,7 @@ data DumpFlag
    | Opt_D_no_debug_output
    | Opt_D_dump_faststrings
    | Opt_D_faststring_stats
+   | Opt_D_ipe_stats
    deriving (Eq, Show, Enum)
 
 -- | Helper function to query whether a given `DumpFlag` is enabled or not.
diff --git a/compiler/GHC/Driver/GenerateCgIPEStub.hs b/compiler/GHC/Driver/GenerateCgIPEStub.hs
index e6ed85cc1d2c4f4fe6f9a68727af67d72a23b9af..6476598283ae9aa8ee752b7bdd98c7fbd45b66fd 100644
--- a/compiler/GHC/Driver/GenerateCgIPEStub.hs
+++ b/compiler/GHC/Driver/GenerateCgIPEStub.hs
@@ -17,7 +17,7 @@ import GHC.Data.Stream (Stream, liftIO)
 import qualified GHC.Data.Stream as Stream
 import GHC.Driver.Env (hsc_dflags, hsc_logger)
 import GHC.Driver.Env.Types (HscEnv)
-import GHC.Driver.Flags (GeneralFlag (Opt_InfoTableMap))
+import GHC.Driver.Flags (GeneralFlag (Opt_InfoTableMap), DumpFlag(Opt_D_ipe_stats))
 import GHC.Driver.DynFlags (gopt, targetPlatform)
 import GHC.Driver.Config.StgToCmm
 import GHC.Driver.Config.Cmm
@@ -30,8 +30,11 @@ import GHC.StgToCmm.Types (CmmCgInfos (..), ModuleLFInfos)
 import GHC.Types.IPE (InfoTableProvMap (provInfoTables), IpeSourceLocation)
 import GHC.Types.Name.Set (NonCaffySet)
 import GHC.Types.Tickish (GenTickish (SourceNote))
-import GHC.Unit.Types (Module)
+import GHC.Unit.Types (Module, moduleName)
+import GHC.Unit.Module (moduleNameString)
 import GHC.Utils.Misc
+import qualified GHC.Utils.Logger as Logger
+import GHC.Utils.Outputable
 
 {-
 Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)]
@@ -193,11 +196,23 @@ generateCgIPEStub hsc_env this_mod denv s = do
 
   -- Yield Cmm for Info Table Provenance Entries (IPEs)
   let denv' = denv {provInfoTables = Map.fromList (map (\(_, i, t) -> (cit_lbl i, t)) labeledInfoTablesWithTickishes)}
-      ((ipeStub, ipeCmmGroup), _) = runC (initStgToCmmConfig dflags this_mod) fstate cgState $ getCmm (initInfoTableProv (map sndOf3 labeledInfoTablesWithTickishes) denv')
+      ((mIpeStub, ipeCmmGroup), _) = runC (initStgToCmmConfig dflags this_mod) fstate cgState $ getCmm (initInfoTableProv (map sndOf3 labeledInfoTablesWithTickishes) denv')
 
   (_, ipeCmmGroupSRTs) <- liftIO $ cmmPipeline logger cmm_cfg (emptySRT this_mod) ipeCmmGroup
   Stream.yield ipeCmmGroupSRTs
 
+  ipeStub <- case mIpeStub of
+                Just (stats, stub) -> do
+                  -- Print ipe stats if requested
+                  liftIO $
+                    Logger.putDumpFileMaybe logger
+                      Opt_D_ipe_stats
+                      ("IPE Stats for module " ++ (moduleNameString $ moduleName this_mod))
+                      Logger.FormatText
+                      (ppr stats)
+                  return stub
+                Nothing -> return mempty
+
   return CmmCgInfos {cgNonCafs = nonCaffySet, cgLFInfos = moduleLFInfos, cgIPEStub = ipeStub}
   where
     collect :: Platform -> [(Label, CmmInfoTable, Maybe IpeSourceLocation)] -> CmmGroupSRTs -> IO ([(Label, CmmInfoTable, Maybe IpeSourceLocation)], CmmGroupSRTs)
diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs
index 5973fe5ab5ce325e69c3520ea286808887be96b1..5400c1e40021bb5c3fdbc6afa2460fb3aa57037b 100644
--- a/compiler/GHC/Driver/Session.hs
+++ b/compiler/GHC/Driver/Session.hs
@@ -1556,6 +1556,8 @@ dynamic_flags_deps = [
         (NoArg (setGeneralFlag Opt_DoTagInferenceChecks))
   , make_ord_flag defGhcFlag "dshow-passes"
         (NoArg $ forceRecompile >> (setVerbosity $ Just 2))
+  , make_ord_flag defGhcFlag "dipe-stats"
+        (setDumpFlag Opt_D_ipe_stats)
   , make_ord_flag defGhcFlag "dfaststring-stats"
         (setDumpFlag Opt_D_faststring_stats)
   , make_ord_flag defGhcFlag "dno-llvm-mangler"
diff --git a/compiler/GHC/StgToCmm/Prof.hs b/compiler/GHC/StgToCmm/Prof.hs
index c7bfb40ccbe379e7c9506d76d2aeab7c70ded4e4..8efa8646b1e5ab459884d46865c048cb3d26edd4 100644
--- a/compiler/GHC/StgToCmm/Prof.hs
+++ b/compiler/GHC/StgToCmm/Prof.hs
@@ -274,24 +274,23 @@ sizeof_ccs_words platform
   where
    (ws,ms) = pc_SIZEOF_CostCentreStack (platformConstants platform) `divMod` platformWordSizeInBytes platform
 
--- | Emit info-table provenance declarations
-initInfoTableProv ::  [CmmInfoTable] -> InfoTableProvMap -> FCode CStub
+-- | Emit info-table provenance declarations and track IPE stats
+initInfoTableProv ::  [CmmInfoTable] -> InfoTableProvMap -> FCode (Maybe (IPEStats, CStub))
 initInfoTableProv infos itmap
   = do
        cfg <- getStgToCmmConfig
-       let ents       = convertInfoProvMap infos this_mod itmap
-           info_table = stgToCmmInfoTableMap cfg
-           platform   = stgToCmmPlatform     cfg
-           this_mod   = stgToCmmThisModule   cfg
-
+       let (stats, ents) = convertInfoProvMap infos this_mod itmap
+           info_table    = stgToCmmInfoTableMap cfg
+           platform      = stgToCmmPlatform     cfg
+           this_mod      = stgToCmmThisModule   cfg
        case ents of
-         [] -> return mempty
+         [] -> return Nothing
          _  -> do
            -- Emit IPE buffer
            emitIpeBufferListNode this_mod ents
 
            -- Create the C stub which initialises the IPE map
-           return (ipInitCode info_table platform this_mod)
+           return (Just (stats, ipInitCode info_table platform this_mod))
 
 -- ---------------------------------------------------------------------------
 -- Set the current cost centre stack
diff --git a/compiler/GHC/StgToCmm/Utils.hs b/compiler/GHC/StgToCmm/Utils.hs
index 7123ac5d60c4bf7939b8692d9b45e39ed6327f3d..4ad96db7fca7e69773c1152e9f19b1de8917e272 100644
--- a/compiler/GHC/StgToCmm/Utils.hs
+++ b/compiler/GHC/StgToCmm/Utils.hs
@@ -7,6 +7,7 @@
 -- (c) The University of Glasgow 2004-2006
 --
 -----------------------------------------------------------------------------
+{-# LANGUAGE RecordWildCards #-}
 
 module GHC.StgToCmm.Utils (
         emitDataLits, emitRODataLits,
@@ -43,7 +44,7 @@ module GHC.StgToCmm.Utils (
         emitUpdRemSetPush,
         emitUpdRemSetPushThunk,
 
-        convertInfoProvMap, cmmInfoTableToInfoProvEnt
+        convertInfoProvMap, cmmInfoTableToInfoProvEnt, IPEStats(..)
   ) where
 
 import GHC.Prelude hiding ( head, init, last, tail )
@@ -90,6 +91,8 @@ import GHC.Types.Unique.FM
 import GHC.Data.Maybe
 import Control.Monad
 import qualified Data.Map.Strict as Map
+import qualified Data.IntMap.Strict as I
+import qualified Data.Semigroup (Semigroup(..))
 
 --------------------------------------------------------------------------
 --
@@ -607,20 +610,44 @@ cmmInfoTableToInfoProvEnt this_mod cmit =
         cn  = rtsClosureType (cit_rep cmit)
     in InfoProvEnt cl cn "" this_mod Nothing
 
+data IPEStats = IPEStats { ipe_total :: !Int
+                         , ipe_closure_types :: !(I.IntMap Int)
+                         , ipe_default :: !Int }
+
+instance Semigroup IPEStats where
+  (IPEStats a1 a2 a3) <> (IPEStats b1 b2 b3) = IPEStats (a1 + b1) (I.unionWith (+) a2 b2) (a3 + b3)
+
+instance Monoid IPEStats where
+  mempty = IPEStats 0 I.empty 0
+
+defaultIpeStats :: IPEStats
+defaultIpeStats = IPEStats { ipe_total = 0, ipe_closure_types = I.empty, ipe_default = 1}
+closureIpeStats :: Int -> IPEStats
+closureIpeStats t = IPEStats { ipe_total = 1, ipe_closure_types = I.singleton t 1, ipe_default = 0}
+
+instance Outputable IPEStats where
+  ppr = pprIPEStats
+
+pprIPEStats :: IPEStats -> SDoc
+pprIPEStats (IPEStats{..}) =
+  vcat $ [ text "Tables with info:" <+> ppr ipe_total
+         , text "Tables with fallback:" <+> ppr ipe_default
+         ] ++ [ text "Info(" <> ppr k <> text "):" <+> ppr n | (k, n) <- I.assocs ipe_closure_types ]
+
 -- | Convert source information collected about identifiers in 'GHC.STG.Debug'
 -- to entries suitable for placing into the info table provenance table.
-convertInfoProvMap :: [CmmInfoTable] -> Module -> InfoTableProvMap -> [InfoProvEnt]
+convertInfoProvMap :: [CmmInfoTable] -> Module -> InfoTableProvMap -> (IPEStats, [InfoProvEnt])
 convertInfoProvMap defns this_mod (InfoTableProvMap (UniqMap dcenv) denv infoTableToSourceLocationMap) =
-  map (\cmit ->
+  traverse (\cmit ->
     let cl = cit_lbl cmit
         cn  = rtsClosureType (cit_rep cmit)
 
         tyString :: Outputable a => a -> String
         tyString = renderWithContext defaultSDocContext . ppr
 
-        lookupClosureMap :: Maybe InfoProvEnt
+        lookupClosureMap :: Maybe (IPEStats, InfoProvEnt)
         lookupClosureMap = case hasHaskellName cl >>= lookupUniqMap denv of
-                                Just (ty, mbspan) -> Just (InfoProvEnt cl cn (tyString ty) this_mod mbspan)
+                                Just (ty, mbspan) -> Just (closureIpeStats cn, (InfoProvEnt cl cn (tyString ty) this_mod mbspan))
                                 Nothing -> Nothing
 
         lookupDataConMap = do
@@ -628,15 +655,15 @@ convertInfoProvMap defns this_mod (InfoTableProvMap (UniqMap dcenv) denv infoTab
             -- This is a bit grimy, relies on the DataCon and Name having the same Unique, which they do
             (dc, ns) <- hasHaskellName cl >>= lookupUFM_Directly dcenv . getUnique
             -- Lookup is linear but lists will be small (< 100)
-            return $ InfoProvEnt cl cn (tyString (dataConTyCon dc)) this_mod (join $ lookup n (NE.toList ns))
+            return $ (closureIpeStats cn, InfoProvEnt cl cn (tyString (dataConTyCon dc)) this_mod (join $ lookup n (NE.toList ns)))
 
         lookupInfoTableToSourceLocation = do
             sourceNote <- Map.lookup (cit_lbl cmit) infoTableToSourceLocationMap
-            return $ InfoProvEnt cl cn "" this_mod sourceNote
+            return $ (closureIpeStats cn, InfoProvEnt cl cn "" this_mod sourceNote)
 
         -- This catches things like prim closure types and anything else which doesn't have a
         -- source location
-        simpleFallback = cmmInfoTableToInfoProvEnt this_mod cmit
+        simpleFallback = (defaultIpeStats, cmmInfoTableToInfoProvEnt this_mod cmit)
 
   in
     if (isStackRep . cit_rep) cmit then
diff --git a/docs/users_guide/debugging.rst b/docs/users_guide/debugging.rst
index 74da6ccfa065deac509a89b02fcaea49fb647f08..f11632f7b636b185950e1b4256253c0f88538338 100644
--- a/docs/users_guide/debugging.rst
+++ b/docs/users_guide/debugging.rst
@@ -77,6 +77,14 @@ Dumping out compiler intermediate structures
     ``!!!`` mark the end of a pass and are accompanied by allocation and
     runtime statistics.
 
+.. ghc-flag:: -dipe-stats
+    :shortdesc: Show statistics about IPE information
+    :type: dynamic
+
+    For each module, show some simple statistics about which info tables have
+    IPE information, and how many info tables with IPE information each closure
+    type has.
+
 .. ghc-flag:: -dfaststring-stats
     :shortdesc: Show statistics for fast string usage when finished
     :type: dynamic
@@ -1202,7 +1210,7 @@ Other
     if things go wrong. Which would otherwise be quite difficult.
 
 .. ghc-flag:: -funoptimized-core-for-interpreter
-    :shortdesc: Disable optimizations with the interpreter 
+    :shortdesc: Disable optimizations with the interpreter
     :reverse: -fno-unoptimized-core-for-interpreter
     :type: dynamic