diff --git a/.gitignore b/.gitignore
index 0c701ac4b..c6aed1b83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,6 @@ lib
 /testsuite/out
 /testsuite/suite.cgpr
 /testsuite/support
-/testsuite/tests
 /testsuite/tmp
 gnatcov-loc.xml
 gnatinspect.db
diff --git a/.gitreview b/.gitreview
index a5d94509a..f99f59565 100644
--- a/.gitreview
+++ b/.gitreview
@@ -2,5 +2,5 @@
 host = git.adacore.com
 port = 29418
 project = gnatcoverage
-defaultbranch = master
+defaultbranch = 23.2
 defaultremote = origin
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 84f22d470..af4cb852d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,5 +4,14 @@ repos:
     hooks:
       - id: clang-format
         types: [file]
-        files: \.(cc|c|h)$
-        args: ["--style=gnu"]
+        files: \.(cc|c|h|cpp|hh)$
+        args: ["--style={
+            BasedOnStyle: gnu,
+            ContinuationIndentWidth: 2,
+
+            # Unfortunately, a bug in clang-format makes this ineffective
+            IndentExternBlock: NoIndent,
+
+            SpaceAfterCStyleCast: true,
+            UseTab: ForContinuationAndIndentation
+        }"]
diff --git a/doc/Makefile b/doc/Makefile
index 8cf357103..12fac6bd9 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -74,7 +74,7 @@ install.html: prepare-installdir.html
 gnatcov/fig_%.png: gnatcov/fig_%.dot
 gnatcov/fig_%.pdf: gnatcov/fig_%.dot
 
-FIGURES=fig_consolidation fig_multipath-bdd fig_flow0
+FIGURES=fig_consolidation fig_multipath-bdd fig_flow_compare fig_flow_srctraces
 
 prepare-images:: $(foreach fig, $(FIGURES), gnatcov/$(fig).pdf)
 prepare-images:: $(foreach fig, $(FIGURES), gnatcov/$(fig).png)
diff --git a/doc/gnatcov/bin_traces_convert.rst b/doc/gnatcov/bin_convert.rst
similarity index 95%
rename from doc/gnatcov/bin_traces_convert.rst
rename to doc/gnatcov/bin_convert.rst
index c63a56f1a..6817a6367 100644
--- a/doc/gnatcov/bin_traces_convert.rst
+++ b/doc/gnatcov/bin_convert.rst
@@ -1,3 +1,5 @@
+.. _bin_convert:
+
 #####################################
 Importing binary traces with |gcvcnv|
 #####################################
diff --git a/doc/gnatcov/bin_gps.rst b/doc/gnatcov/bin_gps.rst
new file mode 100644
index 000000000..af2a579e0
--- /dev/null
+++ b/doc/gnatcov/bin_gps.rst
@@ -0,0 +1,28 @@
+######################
+Using |gcp| from |gps|
+######################
+
+Starting from GNATcoverage version 22.0, binary traces (using gnatcov run) are
+no longer supported in native profiles. This mode is still supported for cross
+configurations, and the IDE integrated use in such cases is similar to the use
+for source traces:
+
+Go to the :menuselection:`Analyze --> Coverage --> GNATcoverage Binary Traces`
+menu to perform each step individually, or use the ``Run All Actions`` item to
+do everything at once.
+
+In configurations where binary traces are not supported, the menu entry just
+wouldn't show up.
+
+You can also replace the toolbar button (dedicated to the instrumentation-based
+scheme) with an alike button, but for binary traces. Go to GNATstudio
+Preferences in :menuselection:`Edit --> Preferences`.
+
+Then, go to the ``Build Targets`` section, and find the ``Run GNATcoverage``
+workflow: add it to the toolbar as shown below.
+
+.. image:: gps_screenshots/7-binary-traces.png
+
+Remove the ``Run GNATcoverage with instrumentation`` entry from the toolbar in
+the same fashion.
+
diff --git a/doc/gnatcov/bin_intro.rst b/doc/gnatcov/bin_intro.rst
new file mode 100644
index 000000000..eed05df17
--- /dev/null
+++ b/doc/gnatcov/bin_intro.rst
@@ -0,0 +1,189 @@
+************
+Introduction
+************
+
+Definition and main characteristics
+-----------------------------------
+
+Unlike *source traces* produced by the execution of instrumented
+programs in their regular environment, :dfn:`binary traces` are
+coverage trace files produced by the execution of uninstrumented
+programs under the control of an instrumented execution environment.
+*"Binary"* in this designation refers to the nature of the collected
+data, which consists in low level information about executed blocks of
+machine instructions.
+
+From binary traces, |gcp| is able to compute both *source coverage*
+metrics as with source traces, and :dfn:`object coverage` metrics
+assessing the coverage of machine level instructions.  |gcp| supports
+two criteria of the latter kind:
+
+- :dfn:`Instruction Coverage`, where we evaluate for each machine instruction
+  whether it has been executed at least once or not; and
+
+- :dfn:`Branch Coverage`, where we also evaluate for each conditional
+  branch instruction whether it was only taken, or went fallthrough or both.
+
+Source coverage metrics are only available for a subset of the target
+configurations supported with source traces, though, and new tool
+functionalities are most often implemented for source traces only.
+
+The matrix below summarizes the main characteristics of the two
+trace production modes:
+
+
+.. list-table::
+   :widths: 20 20 20
+   :header-rows: 1
+   :stub-columns: 1
+
+   * -
+     - **With Source Traces**
+     - **With Binary Traces**
+   * - *Tracing mechanism*
+     - Program instrumentation
+     - Execution environment
+   * - ~
+     - ~
+     - ~
+   * - *Native applications*
+     - Yes (including shared-libraries)
+     - No
+   * - *Cross configurations with RTOS*
+     - Yes
+     - No
+   * - *Bareboard configurations*
+     - Yes
+     - Specific CPUs only, through GNATemulator or
+       hardware probes
+   * - ~
+     - ~
+     - ~
+   * - *Object coverage analysis*
+     - No
+     - Yes
+   * - *New features*
+     - Often not
+     - Yes (source level metrics for C++, scoped metrics in dhtml)
+
+      
+Workflow overview
+-----------------
+
+A high level view comparing the processes involved in the use of
+binary or source traces is provided on :numref:`fig-flow_compare`. The
+use of coverage *traces* in both modes allows lots of similarities
+between workflows, in particular in the steps processing traces after
+they have been produced.
+
+We don't support mixing the two kinds of traces together, though, and have
+depicted a separate analysis/consolidation step to emphasize this:
+
+.. _fig-flow_compare:
+.. figure:: fig_flow_compare.*
+  :align: center
+
+  Workflows using source or binary coverage traces
+
+For binary traces, |gcv| relies on an instrumented execution
+environment to produce the traces instead of having to instrument the
+program itself with extra code and data structures. |gem| provides
+such an environment for bareboard configurations, as well as hardware
+probes assisted with means to convert their output to the format |gcv|
+expects.
+
+Programs are built from their original sources, only requiring the use of
+:cmd-option:`-g -fpreserve-control-flow -fdump-scos` compilation options to
+generate coverage obligation lists and let us associate execution traces to
+these obligations afterwards.
+
+
+
+A simple example
+----------------
+
+For the original "Getting Started" use case, we first use the GNAT Pro
+toolset for ``powerpc-elf`` to build, using :command:`gprbuild` as
+follows::
+
+   gprbuild --target=powerpc-elf --RTS=light-mpc8641 -Ptests.gpr \
+     -cargs -g -fpreserve-control-flow -fdump-scos
+
+We pass the project file with ``-P``, the required compilation flags
+with ``-cargs`` and request the use of a ``light`` runtime library tailored
+for the ``mpc8641`` board.
+
+The build command produces a ``test_inc`` executable in the object
+subdirectory. To automate the execution of this program within |gem|
+to produce a trace, we provide the |gcvrun| command. For the use case
+at hand, we would simply do::
+
+  gnatcov run --target=powerpc-elf obj-tests/test_inc
+
+
+This produces a ``test_inc.trace`` binary trace file in the current
+directory.  By default, such a trace is amenable to statement and decision
+coverage at most. If MCDC analysis is needed, ``--level=stmt+mcdc`` must be
+passed to |gcvrun| as well and we recommend also providing source coverage
+obligations in this case.
+
+Producing report is then achieved as for sources traces, using the
+binary trace files as inputs to the |gcvcov| commands.
+
+Units of interest can be conveyed with either the GPR oriented
+facilities as with source traces, or with :cmd-option:`--scos`
+switches to provide lists of files holding coverage obligations in a
+similar fashion as :cmd-option:`--sid` switches for ``.sid`` files out
+of source instrumentation.
+
+The files to list with :cmd-option:`--scos` are the *Library
+Information* files produced by the compiler along with object files,
+which are ``.ali`` files for Ada and ``.gli`` files for C.
+
+The GNAT toolchain provides a helpful device in this process for Ada
+units: the :cmd-option:`-A` command line argument to
+:command:`gnatbind` which produces a list of all the ``.ali`` files
+involved in an executable construction.  By default, the list goes to
+standard output. It may be directed to a file on request with
+:cmd-option:`-A=<list-filename>`, and users may of course filter this
+list as they see fit depending on their analysis purposes.
+
+Below is a complete example sequence of commands to illustrate, using
+the standard Unix ``grep`` tool to filter out test harness units::
+
+    # Build executable and produce the corresponding list of ALI files. Pass
+    # -A to gnatbind through gprbuild -bargs then filter out the test units:
+    gprbuild -p --target=powerpc-elf --RTS=light-mpc8641 -Ptests.gpr
+      -cargs -fdump-scos -g -fpreserve-control-flow -bargs -A=all.alis
+
+    # Run and analyse all units except the test harness, filtering out
+    # the correspond ALI files from the list:
+    grep -v 'test_[^/]*.ali' all.alis > nontest.alis
+    gnatcov run --level=stmt+mcdc --scos=@nontest.alis
+    gnatcov coverage --level=stmt+mcdc --annotate=xcov --scos=@nontest.alis
+
+Going Further
+-------------
+
+As the source and binary trace based workflows share commonalities,
+a lot of information from the main documentation also applies to the
+use binary traces, such as
+
+- The use of project files to specify command switches or designate
+  units of interest at analysis time,
+
+- Using coverage checkpoints or traces for consolidation,
+
+- Defining exemptions regions with pragmas for Ada. The support
+  for other languages is not available for binary traces.
+
+Then this appendix holds separate chapters dedicated to binary trace
+specific considerations:
+
+- :ref:`bin_traces`
+
+- :ref:`ocov`
+
+- :ref:`osmetrics`
+
+- :ref:`bin_convert`
diff --git a/doc/gnatcov/cons_checkpoints.rst b/doc/gnatcov/cons_checkpoints.rst
index cd77856c0..ef356d479 100644
--- a/doc/gnatcov/cons_checkpoints.rst
+++ b/doc/gnatcov/cons_checkpoints.rst
@@ -39,8 +39,8 @@ latter case, if the contribution comes from an intermediate checkpoint, the
 command which produced the checkpoint is displayed. For example, a sequence of
 commands such as::
 
-  gnatcov run obj/pgm1 -o trace1
-  gnatcov run obj/pgm2 -o trace2
+  obj/pgm1  # producing trace1
+  obj/pgm2  # producing trace2
 
   gnatcov coverage --level=<> trace1 <units> --save-checkpoint=t1.ckpt
   gnatcov coverage --level=<> trace2 <units> --checkpoint=t1.ckpt --annotate=report
@@ -51,13 +51,13 @@ Would produce a report with this kind of information in the header::
    Trace files:
 
    trace2
-   kind     : binary
+   kind     : source
    program  : obj/pgm2
    date     : 2020-09-22 16:32:52
    tag      :
 
    trace1
-   kind     : binary
+   kind     : source
    program  : obj/pgm1
    date     : 2020-09-22 16:32:47
    tag      :
@@ -91,9 +91,11 @@ discuss compatibility issues regarding consolidation.
 Efficient storage of test results
 =================================
 
-Binary trace files can be large and their processing requires access to the
-executable file that was used to produce the trace. Preserving collections of
-traces and executables to allow consolidation can be problematic, space-wise.
+Trace files can be large and often need to be kept together with the
+executable files that were used to produce the traces, for traceability
+reasons for example, or for specific processing operations in some
+circumstances. Preserving collections of traces and executables to allow
+consolidation can be problematic, space-wise.
 
 In some situations (depending on the overall size of programs, number of units
 of interest, assessed coverage criteria, number and structure of decisions,
diff --git a/doc/gnatcov/cons_traces.rst b/doc/gnatcov/cons_traces.rst
index 7be5fdba3..c11e90be1 100644
--- a/doc/gnatcov/cons_traces.rst
+++ b/doc/gnatcov/cons_traces.rst
@@ -13,11 +13,6 @@ The set of traces involved in a computation, with some details about each trace
 in the *Assessment Context* section of :cmd-option:`=report` outputs, where the
 command line is quoted.
 
-Trace consolidation works the same for source or binary traces. Mixing
-binary and source traces is not supported, however, so all the traces
-provided for a consolidation must be of the same kind (all source or
-all binary).
-
 The following subsections provide examples of possible use cases of this facility.
 
 Single unit tested by different programs
@@ -119,8 +114,8 @@ the symmetric results::
   12 .:       end if;
   13 .:    end Stat;
 
-Then a report obtained by combining traces, binary or source, will show
-achievement of complete statement coverage like so::
+Then a report obtained by combining traces will show achievement of
+complete statement coverage like so::
 
    6 .:    procedure Stat (Safe : Boolean) is
    7 .:    begin
@@ -131,9 +126,8 @@ achievement of complete statement coverage like so::
   12 .:       end if;
   13 .:    end Stat;
 
-Assuming you have obtained one trace for the execution of each test, both
-traces either source or binary, the command to produce the combined report
-would be something like::
+Assuming you have obtained one trace for the execution of each test,
+the command to produce the combined report would be something like::
 
     gnatcov coverage --level=stmt <units-of-interest> --annotate=xcov
       test_cmd_safe.trace test_cmd_unsafe.trace
@@ -383,55 +377,3 @@ visible on lines 7, 14, and 15 of ``main.c``, and attempt to compute an
 unsupported operation, visible on lines 21 and 22 of ``process.c``. These two
 scenarios, exercised through ``div.srctrace`` and ``misuse.srctrace`` were
 indeed not included in the consolidation scope.
-
-Special care needed with binary traces
-======================================
-
-Control of inlining in test programs
-------------------------------------
-
-With binary traces, the program is not itself instrumented to collect coverage
-information on its own and the object files used to assess coverage are more
-likely (than with source traces) to be used directly in operational
-conditions. In such cases, we want to make sure that the binary code from
-those object files is indeed exercised during the testing campaign.
-
-While this would automatically be the case for integration testing campaigns,
-campaigns based on the use of explicit testing code, such as unit tests, need
-to make sure that calls into the code-under-test by the testing code are not
-inlined by the compiler.
-
-With GCC based toolchains such as GNAT Pro, this can be achieved by adding
-:cmd-option:`-fno-inline` to the compilation options of the testing code.
-
-
-Object code overlap in executables
-----------------------------------
-
-For object or source level criteria, |gcv| computes the coverage achieved for
-the full set of routines or source units declared to be of interest amongst
-those exposed by the union of the exercised executables, as designated by the
-set of consolidated traces;
-
-For the purpose of computing combined coverage achievements, two symbols are
-considered overlapping when all the following conditions are met:
-
-* Both symbols have identical names at the object level,
-
-* Both symbols have DWARF debug information attached to them,
-
-* According to this debug information, both symbols originate from the same
-  compilation unit, denoted by the full path of the corresponding source file.
-
-By this construction, a symbol missing debug information is never considered
-overlapping with any other symbol. Whatever coverage is achieved on such a
-symbol never gets combined with anything else and the only kind of report where
-the symbol coverage is exposed is the :cmd-option:`=asm` assembly output for
-object level criteria.
-
-Moreover, for object level coverage criteria, |gcvcov| will issue a
-consolidation error when two symbols are found to overlap but have
-structurally different machine code, which happens for example when the same
-unit is compiled with different different optimization levels for
-different executables.
-
diff --git a/doc/gnatcov/consolidation.rst b/doc/gnatcov/consolidation.rst
index ce91d93b0..656ba0c76 100644
--- a/doc/gnatcov/consolidation.rst
+++ b/doc/gnatcov/consolidation.rst
@@ -22,7 +22,7 @@ tested separately (e.g. the different subprograms of a library unit or
 different scenarios of a given subprogram).
 
 The production of consolidated coverage reports can proceed either directly
-from a set of source or binary traces produced by the executions, or from a
+from a set of traces produced by the executions, or from a
 set of pre-computed partial results latched in what we refer to as
 :term:`coverage checkpoints <Coverage Checkpoint>`, which offer a lot of
 advanced capabilities.  The following sections illustrate various combinations
diff --git a/doc/gnatcov/cov_source.rst b/doc/gnatcov/cov_source.rst
index d9861b79c..b09e9e66f 100644
--- a/doc/gnatcov/cov_source.rst
+++ b/doc/gnatcov/cov_source.rst
@@ -11,9 +11,10 @@ language entities such as high level *statements* or *decisions* (DO178
 parlance for boolean expressions), which translate as :term:`Source Coverage
 Obligations <Source Coverage Obligation>`, or SCOs, in |gcp| terms.
 
-Once you have produced source or binary traces, actual analysis is then
-performed with |gcvcov| to generate coverage reports. *Source* coverage is
-queried by passing a specific :cmd-option:`--level` argument to |gcvcov|.
+Once you have produced coverage traces, actual analysis is performed
+with |gcvcov| to generate coverage reports. *Source* coverage is
+queried by passing a specific :cmd-option:`--level` argument to
+|gcvcov|.
 
 The general structure of this command line is always like::
 
@@ -258,8 +259,12 @@ in a single character, which may be one of the following:
    ``.`` | No coverage obligation is attached to the line
    ``-`` | Coverage obligations attached to the line, none satisfied
    ``!`` | Coverage obligations attached to the line, some satisfied
+   ``?`` | Coverage obligations attached to the line, undetermined coverage state (*)
    ``+`` | Coverage obligations attached to the line, all satisfied
 
+(*) The Undetermined Coverage state (``?``) is only shown on the line in the
+absence of other known violations for that same line.
+
 Here is, to illustrate, the full statement coverage report produced for our
 example unit when the ``Between`` function was called so that the ``if``
 control evaluated True only. The function is actually part of an Ada package,
@@ -356,9 +361,13 @@ assessed criteria.
 
 The report features explicit start/end of report notifications and at least
 three sections in between: Assessment Context, Coverage Violations, and
-Analysis Summary.  A few variations are introduced when :term:`exemption
-regions <Exemption Region>` are in scope.  See the :ref:`exemptions` section
-for more details on their use and effect on the output reports.
+Analysis Summary. Should |gcv| be unable to determine the coverage state of
+some coverage obligations, those will be reported in a dedicated Undetermined
+Coverage Items section, with a description of why the tool was unable to
+determine the coverage state for each obligation. A few variations are
+introduced when :term:`exemption regions <Exemption Region>` are in scope.
+See the :ref:`exemptions` section for more details on their use and effect on
+the output reports.
 
 If :cmd-option:`--dump-units-to -` is also on the command line, a *UNITS OF
 INTEREST* section is produced, which contains the list of units considered
@@ -382,9 +391,9 @@ items:
 
 * Coverage level requested to be analyzed
 
-* Details on the input trace files: path to binary program exercised (as
-  recorded in the trace header), production time stamp and tag string
-  (:cmd-option:`--tag` command line argument value).
+* Details on the input trace files, such as the path to the binary
+  program exercised (as recorded in the trace header) or the trace
+  production time stamp.
 
 Here is a example excerpt::
 
@@ -551,8 +560,12 @@ indications:
 
    ``-`` | At least one statement on the line, none covered
    ``!`` | More than one statement on the line, some covered
+   ``?`` | At least one statement on this line, some with undetermined coverage state (*)
    ``+`` | At least one statement on the line, all covered
 
+(*) The Undetermined Coverage state (``?``) is only shown on the line in the
+absence of other known violations for that same line.
+
 When a single statement spans multiple lines, the coverage annotation is
 present on all the lines, as the two ``+`` signs for the single assignment
 in the following excerpt::
@@ -807,8 +820,11 @@ following table summarizes the meaning of the possible annotations:
 
    ``-`` | At least one statement on the line, none executed.
    ``!`` | Unless multiple statements are involved, decision partially covered on the line.
+   ``?`` | At least one statement or decision on the line with undetermined coverage state. (*)
    ``+`` | All the statements and decisions on the line are covered.
 
+(*) The Undetermined Coverage state (``?``) is only shown on the line in the
+absence of other known violations for that same line.
 
 When a trailing `+` is added to the format passed to :cmd-option:`--annotate`
 (:cmd-option:`=xcov+` or :cmd-option:`=html+`), a precise description of the
@@ -1020,8 +1036,12 @@ condition specific cases marked with a ``!`` as well:
 
    ``-`` | At least one statement associated with this line, none executed.
    ``!`` | For a single statement line, decision partially covered or condition not covered on the line.
+   ``?`` | At least one statment or decision on the line with undetermined coverage state. (*)
    ``+`` | All the statements, decisions and conditions on the line are covered.
 
+(*) The Undetermined Coverage state (``?``) is only shown on the line in the
+absence of other known violations for that same line.
+
 The :cmd-option:`=report` outputs feature an extra MCDC section in the Coverage
 Violations segment, which holds:
 
@@ -1261,16 +1281,11 @@ single case where X1 < V < X2:
       Assert (Between (X1 => 2, X2 => 5, V => 3)); -- X1 < V < X2
    end Test_X1VX2;
 
-Performing MCDC analysis with binary traces requires telling the execution step
-about it, by providing both the :cmd-option:`--level` and a list of units for
-which analysis is to be performed to |gcvrun|::
-
-   gnatcov run --level=stmt+mcdc -Pmytest.gpr test_x1vx2
+After instrumentation for MCDC and execution, we can then request,
+say, an :cmd-option:`=xcov+` report to get a first set of results in
+the ``ranges.adb.xcov`` annotated source::
 
-We can then request, say, an :cmd-option:`=xcov+` report to get a first set of
-results in the ``ranges.adb.xcov`` annotated source::
-
-   gnatcov coverage --level=stmt+mcdc -Pmytest.gpr --annotate=xcov+ test_x1vx2.trace
+   gnatcov coverage --level=stmt+mcdc -Pmytest.gpr --annotate=xcov+ test_x1vx2.srctrace
 
    ...
       8 .:    function Between (X1, X2, V : Integer) return Boolean is
@@ -1473,20 +1488,16 @@ Handling source relocation for annotated sources output formats
 ===============================================================
 
 For all annotated sources output formats
-(:cmd-option:`--annotate=xcov[+]|html[+]|dhtml)`, |gcv| needs access to the
-sources of the :term:`units of interest <Units of Interest>` to generate the
-output. By default, the sources will be searched in the location where they
-were compiled, for binary traces, or where they were instrumented for source
-traces. This is generally going to be the case if the whole coverage analysis
-process is done on the same machine, but in some cases it may be preferable to
-generate the coverage report on a different machine than the one where the
-coverage run was done, or the sources may have been moved.
-
-For instance, a project with a large number of unit tests may be run on
-multiple machines in parallel to speed up the test runs, and then a single
-report will be generated from all the traces/checkpoints. In such case, |gcv|
-will not be able to automatically find the source files. |gcv| offers two
-options to help in those situations:
+(:cmd-option:`--annotate=xcov[+]|html[+]|dhtml)`, |gcv| needs access
+to the sources of the :term:`units of interest <Units of Interest>` to
+generate the output. The tool searches for sources in some default
+locations which are usually correct when the whole coverage analysis
+process is done on the same machine.
+
+When coverage reports are produced on a different machine than the one
+where the coverage run was done, or when sources are moved in the
+interim, |gcv| will likely not be able to automatically find the
+source files. The tool offers two options to help in those situations:
 
 * The option :cmd-option:`--source-rebase=\<old_prefix\>=\<new_prefix\>`
 
@@ -1519,18 +1530,17 @@ options to help in those situations:
   This option allows the specification of directories in which to search for
   missing source files.
 
-  If the location of a source file, when built on a first machine, was
-  ``/work/build_1234/src/main.adb`` but its location on the machine where the
-  report is generated is ``/some/path/project/src/main.adb`` then passing
+  If the location of a source file, when built on a first machine,
+  was ``/work/build_1234/src/main.adb`` but its location on the machine where
+  the report is generated is ``/some/path/project/src/main.adb`` then passing
   :cmd-option:`--source-search=/some/path/project/src` will enable |gcv| to
   find the missing source file. This option also accepts response files.
 
-  This option can appear multiple times on the command line, and when trying to
-  locate a missing source file, |gcv| will first try the
-  :cmd-option:`--source-rebase` prefix pairs, if any, and if the source file
-  was not found, it will then look for the file in all the directories passed
-  with :cmd-option:`--source-search` in the order in which they appear on the
-  command line.
+  This option can appear multiple times on the command line. Note that |gcv|
+  will first try the :cmd-option:`--source-rebase` prefix pairs, if any, and
+  if the source file was not found, it will then look for  the file in all
+  the directories passed with :cmd-option:`--source-search` in the order in
+  which they appear on the command line.
 
 These two options perform very similar functions and can sometimes be used
 interchangeably, however the second option is less selective and can lead to
diff --git a/doc/gnatcov/exemptions.rst b/doc/gnatcov/exemptions.rst
index ee871b93c..aa3545564 100644
--- a/doc/gnatcov/exemptions.rst
+++ b/doc/gnatcov/exemptions.rst
@@ -13,9 +13,10 @@ Defining :term:`Exemption Regions <Exemption Region>`
 =====================================================
 
 :dfn:`Exemption regions` are lexical sections of sources in which coverage
-violations are expected and can be justified. For Ada with the |gnat|
-compilers, regions are defined by the insertion of dedicated pragmas in the
-sources:
+violations are expected and can be justified.
+
+For Ada with the |gnat| compilers, regions are defined by the insertion of
+dedicated pragmas in the sources:
 
 - ``pragma Annotate (Xcov, Exempt_On, "justification text");`` starts a
   region, providing some justification text that will be recalled in coverage
@@ -42,13 +43,52 @@ We expect never to reach here with ``T`` False, so we declare an exemption
 region to state that coverage violations across this region are expected and
 not a testing campaign deficiency.
 
+For C code, exemptions are only supported with
+:term:`source traces<Source Trace>` and are defined using comment markers to
+delimit the exempted regions:
+
+- Any comment containing the string ``GNATCOV_EXEMPT_ON`` followed by a string
+  in double quotes starts a region, the string within the double quotes being
+  used as justification text that will be recalled in coverage reports.
+
+- Any comment containing the string ``GNATCOV_EXEMPT_OFF`` closes the current
+  exemption region.
+
+The following assert function illustrates the definition of an exemption
+block:
+
+.. code-block:: C
+
+  void
+  assert (bool x){
+   // GNATCOV_EXEMPT_ON "assert condition is never false"
+    if (!x)
+      abort();
+   // GNATCOV_EXEMPT_OFF
+  }
+
+As in the first example, we never expect to reach this function with x false,
+so an exemption region is declared to state that all coverage violations
+within the region are expected.
+
+An exemption comment marker may not intersect any coverage obligation,
+such as a statement or a decision, as in the following example attempting
+to exempt a decision only partially:
+
+.. code-block:: C
+
+  if(a && /*GNATCOV_EXEMPT_ON "justification"*/ b /*GNATCOV_EXEMPT_OFF*/){
+    ...
+  }
+
+Such markers are ignored by |gcvins|, after emitting a warning.
 
 Reporting about coverage exemptions
 ===================================
 
 Exempted regions are reported as blocks in both the annotated source and the
 synthetic text reports, for both source and object coverage metrics.  In
-annotated source reports, a ``#`` or ``*`` caracter annotates all the exempted
+annotated source reports, a ``#`` or ``*`` character annotates all the exempted
 lines, depending on whether 0 or at least 1 violation was exempted over the
 whole section, respectively.  For our ``Eassert`` example above, a typical
 :cmd-option:`=xcov` output for :cmd-option:`stmt+decision` coverage for would
@@ -129,6 +169,19 @@ In synthetic reports, the count of exempted violations is 0, like::
 
   1 exempted region.
 
+Undetermined Coverage state and Exemptions
+------------------------------------------
+
+For each exemption region in which there are obligations with undetermined
+coverage state, the synthetic text report will indicate the count of
+undetermined coverage obligations in the region, in addition to the number
+of violations.
+
+In the annotated sources, exemption regions with *only* undetermined coverage
+items are annotated with ``@`` signs instead of ``*``. If there are both
+undetermined coverage obligations as well as violations in the exemption
+region, the corresponding lines is still annotated with ``#``.
+
 .. _ocov_exemptions:
 
 Object coverage exemptions
diff --git a/doc/gnatcov/fig_flow0.pdf b/doc/gnatcov/fig_flow0.pdf
deleted file mode 100644
index 0f5fc3f35..000000000
Binary files a/doc/gnatcov/fig_flow0.pdf and /dev/null differ
diff --git a/doc/gnatcov/fig_flow0.png b/doc/gnatcov/fig_flow0.png
deleted file mode 100644
index d560ed3b7..000000000
Binary files a/doc/gnatcov/fig_flow0.png and /dev/null differ
diff --git a/doc/gnatcov/fig_flow0.dot b/doc/gnatcov/fig_flow_compare.dot
similarity index 81%
rename from doc/gnatcov/fig_flow0.dot
rename to doc/gnatcov/fig_flow_compare.dot
index fdc744c37..698438dda 100644
--- a/doc/gnatcov/fig_flow0.dot
+++ b/doc/gnatcov/fig_flow_compare.dot
@@ -15,12 +15,12 @@ digraph {
   node [shape = box];
 
   node [color = green];
-  instrument [label = "Instrument\nOriginal Sources"];
-  build_instr [label = "Build from\nInstrumented Sources"];
-  regular_execution [label = "Regular Execution\n(Instrumented Executable)"];
+  instrument [label = "Instrument Original Sources\n(gnatcov instrument)"];
+  build_instr [label = "Build from Instrumented Sources\n(gprbuild --src-subdirs ...)"];
+  regular_execution [label = "Regular Execution\n(instrumented executable)"];
 
   node [color = blue];
-  srct_coverage [label = "Analyze/Consolidate"];
+  srct_coverage [label = "Analyze/Consolidate\n(gnatcov coverage)"];
 
   /* products or inputs */
   node [shape = ellipse, color = green];
@@ -40,11 +40,11 @@ digraph {
   /* processes */
   node [shape = box];
   node [color = red];
-  build_orig [label = "Build from\nOriginal Sources"];
-  instr_execution [label = "Instrumented Execution\n(Regular Executable)"];
+  build_orig [label = "Build from Original Sources\n(gprbuild -cargs ...)"];
+  instr_execution [label = "Instrumented Execution\n(gnatcov run)"];
 
   node [color = blue];
-  bint_coverage [label = "Analyze/Consolidate"];
+  bint_coverage [label = "Analyze/Consolidate\n(gnatcov coverage)"];
 
   /* products or inputs */
   node [shape = ellipse, color = red];
@@ -101,6 +101,10 @@ digraph {
   build_orig [rank=2];
   build_orig -> instr_execution;
 
+  pad1 [style=invis];
+  build_orig:e -> pad1 [style=invis];
+  pad1 -> instr_execution:e [style=invis];
+
   {rank = same;
    bt1; btdots; bt2;
   }
diff --git a/doc/gnatcov/fig_flow_compare.pdf b/doc/gnatcov/fig_flow_compare.pdf
new file mode 100644
index 000000000..2e188dfb7
Binary files /dev/null and b/doc/gnatcov/fig_flow_compare.pdf differ
diff --git a/doc/gnatcov/fig_flow_compare.png b/doc/gnatcov/fig_flow_compare.png
new file mode 100644
index 000000000..186f098ba
Binary files /dev/null and b/doc/gnatcov/fig_flow_compare.png differ
diff --git a/doc/gnatcov/fig_flow_srctraces.dot b/doc/gnatcov/fig_flow_srctraces.dot
new file mode 100644
index 000000000..d4c9e9b8e
--- /dev/null
+++ b/doc/gnatcov/fig_flow_srctraces.dot
@@ -0,0 +1,61 @@
+digraph {
+  node [fontsize = 9]
+
+  edge [fontsize = 9, arrowsize=0.7]
+
+  setup [shape=box, label="gnatcov setup"]
+
+  /*-------------------
+    Using Source traces
+    -------------------*/
+  subgraph "cluster_src_traces" {
+  label = "Instrument / Build / Execute / Analyze";
+  fontsize = 10;
+  labeljust = l;
+
+  /* processes */
+  node [shape = box];
+
+  node [color = green];
+  instrument [label = "Instrument Original Sources\n(gnatcov instrument -P<gpr>)"];
+  build_instr [label = "Build from Instrumented Sources\n(gprbuild -P... --src-subdirs ...)"];
+  regular_execution [label = "Regular Execution\n(instrumented executable)"];
+
+  node [color = blue];
+  srct_coverage [label = "Analyze/Consolidate\n(gnatcov coverage -P...)"];
+
+  /* products or inputs */
+  node [shape = ellipse, color = green];
+  st1 [label = "strace1"];
+  stdots [label = "..."];
+  st2 [label = "strace2"];
+
+  node [shape = ellipse, color = blue];
+  srct_report [label = "Report"];
+  srct_checkpoint [label = "Checkpoint"];
+
+  src [shape = ellipse, label = "Original Sources"];
+
+  /* Links */
+  src -> instrument;
+  instrument -> build_instr;
+  build_instr -> regular_execution;
+
+  {rank = same;
+   st1; stdots; st2;
+  }
+
+  regular_execution -> st1 [style = dotted];
+  regular_execution -> stdots [style = dotted];
+  regular_execution -> st2 [style = dotted];
+
+  st1 -> srct_coverage;
+  stdots -> srct_coverage;
+  st2 -> srct_coverage;
+
+  srct_coverage -> srct_report [style = dotted];
+  srct_coverage -> srct_checkpoint [style = dotted];
+  srct_checkpoint -> srct_coverage;
+
+  }
+}
diff --git a/doc/gnatcov/fig_flow_srctraces.pdf b/doc/gnatcov/fig_flow_srctraces.pdf
new file mode 100644
index 000000000..0d6a5b808
Binary files /dev/null and b/doc/gnatcov/fig_flow_srctraces.pdf differ
diff --git a/doc/gnatcov/fig_flow_srctraces.png b/doc/gnatcov/fig_flow_srctraces.png
new file mode 100644
index 000000000..80d57f7ae
Binary files /dev/null and b/doc/gnatcov/fig_flow_srctraces.png differ
diff --git a/doc/gnatcov/gen_prjtrees.py b/doc/gnatcov/gen_prjtrees.py
index 52b037d40..3ccadcc36 100644
--- a/doc/gnatcov/gen_prjtrees.py
+++ b/doc/gnatcov/gen_prjtrees.py
@@ -1,5 +1,6 @@
 import re
 import subprocess
+import os
 
 
 def render(tree):
@@ -29,5 +30,6 @@ def gen_tree(tmpl, outfile, selected):
 for k in trees:
     gen_tree(tmpl, open(k + ".dot", "w"), trees[k])
     render(k)
+    os.remove (f"{k}.dot")
 
 render('prjtree')
diff --git a/doc/gnatcov/getting_started.rst b/doc/gnatcov/getting_started.rst
index 8450914c6..c43f0beb3 100644
--- a/doc/gnatcov/getting_started.rst
+++ b/doc/gnatcov/getting_started.rst
@@ -2,149 +2,109 @@
 Getting Started
 ***************
 
-Framework concepts and process overview
-=======================================
-
-|gcp| provides a range of coverage analysis facilities with support
-notably for
-
-- A variety of measurement methods, coverage criteria and output
-  formats;
-
-- Powerful consolidation features to report about the combined
-  achievements of multiple program executions.
-
-Actual coverage is always first computed out of *trace files* conveying what
-test programs have achieved. |gcp| works with two possible kinds of traces:
-
-- :dfn:`Binary traces`, produced by an instrumented execution environment
-  while running an unmodifed version of the program. Such traces contain low
-  level information about executed blocks of machine instructions.
-
-- :dfn:`Source traces`, produced by an alternative version of the program,
-  built from sources instrumented to feed coverage dedicated datastructures.
-
-Both kinds of traces can be used to assess so called :dfn:`source coverage`
-criteria, where the entities subject to coverage assessment are defined in
-terms of source level constructs. The specific criteria that |gcp| supports
-are those defined by the DO-178B certification standard for civil avionics:
-
-- :dfn:`Statement Coverage`, where the coverage metrics are respective to source
-  level statements such as a variable assignment or a subprogram calls;
-
-- :dfn:`Decision Coverage`, which, in addition to statement coverage, requires
-  evaluating Boolean expressions (*decisions* in DO178B parlance) both True
-  and False, then
-
-- :dfn:`Modified Condition/Decision Coverage`, commonly known as *MCDC*, which
-  requires testing particular variations of individual Boolean operands
-  (*conditions* in DO178B parlance) within decisions.
-
+General notions
+===============
+
+|gcp| is a code coverage analysis tool offering support for a range of
+coverage metrics and output formats associated with powerful
+:dfn:`consolidation` features letting users assess the combined
+coverage achievements of multiple program executions.
+
+The set of compilation units for which a user needs to assess coverage
+is commonly designated as the set of :dfn:`units of interest`. This is a
+central notion to many of the tool's operations, conveyed by the user
+through command line switches or GPR project file attributes. Units of
+interest typically include the code under test in a campaign, as
+opposed to the sources of the test harness infrastructure.
+
+After one or several program executions, the tool computes
+:dfn:`coverage metrics` for a given set of units out of :dfn:`coverage
+trace` data produced by the executions with the assistance of an
+*instrumentation* mechanism. The primary mode offered by |gcp|
+performs :dfn:`source instrumentation`, where the tool produces a
+modified version of the program sources to keep track of coverage
+relevant facts along with the program control flow, and output
+the coverage data when the program terminates. We call :dfn:`source
+traces` the coverage traces produced by programs instrumented this
+way.
+
+The set of metrics that the tool can assess from source traces
+corresponds to the set defined by the DO-178B certification standard
+for civil avionics, that is:
+
+- :dfn:`Statement Coverage`, where the tools assesses the coverage
+  status (executed or not) of source statements such as a variable
+  assignment or a subprogram call;
+
+- :dfn:`Decision Coverage`, which, in addition to statement coverage,
+  evaluates whether Boolean expressions (*decisions* in DO178B
+  parlance) have been exercised both True and False, then
+
+- :dfn:`Modified Condition/Decision Coverage`, commonly known as
+  *MCDC*, which requires testing particular variations of individual
+  Boolean operands (*conditions* in DO178B parlance) within decisions.
+
+All these metrics are defined with respect to source level entities
+(statements, decisions, conditions), and we commonly refer to such
+assessments as :dfn:`source coverage analysis`. Individual statements,
+decisions, or conditions within the scope of an analysis are referred
+to as :dfn:`Source Coverage Obligations` or :dfn:`SCOs`.
+
+Workflow overview
+=================
+
+Figure :numref:`fig-flow_srctraces` depicts the workflow involved in
+computing coverage based on source instrumentation.
+
+A setup step is first needed to install a tiny runtime library used by
+the instrumented sources to register coverage facts and output them
+eventually. This runtime is parameterized for the project at hand,
+depending on the set of languages involved (Ada, C, C++) and on the
+kind of target for which the code will be compiled (native, cross with
+OS, or bareboard). Once the setup is done, users proceed with
+instrument / build / execute / analyze cycles to produce traces and
+compute coverage results.
+
+The setup, instrumentation and analysis steps are all driven by a
+|gcv| command line tool which exposes a subcommand for each kind of
+operation; hence |gcvstp| for the setup step, |gcvins| to instrument
+and |gcvcov| to compute metrics from traces.
+
+GPR project files and associated command line switches are used to let
+the tool know about the project sources and to convey the units of
+interest. Consolidation can be performed by aggregating multiple traces
+directly, or internal representations of partial coverage results
+stored in files that we call :dfn:`Coverage Checkpoints`.
+
+.. _fig-flow_srctraces:
+.. figure:: fig_flow_srctraces.*
+  :align: center
 
-From binary traces, |gcp| is also able to produce :dfn:`object
-coverage` reports, measuring the coverage of machine level
-instructions produced by the compilation toolchain out of the original
-sources. |gcp| supports two criteria of this kind:
+  Source instrumentation based Coverage analysis overview
 
-- :dfn:`Instruction Coverage`, where we evaluate for each machine instruction
-  whether it has been executed at least once or not; and
 
-- :dfn:`Branch Coverage`, where, in addition, we evaluate for each conditional
-  branch instruction whether it was only taken, or went fallthrough or both.
+A simple example
+================
 
-A central notion to all the assessments performed with |gcp| is that of
-:dfn:`units of interest`, which simply designates the set of compilation units
-of which we are aiming to assess the coverage. These typically include the
-code under test in a campaign, as opposed to, for example, the sources of the
-test harness infrastructure when one is used. The individual statements,
-decisions, or conditions of relevance within units of interest are referred to
-as :dfn:`Source Coverage Obligations` or :dfn:`SCOs`.
+Here we first introduce a very simple example software project
+structure then demonstrate one basic analysis workflow for this
+project with actual commands.
 
-Consolidation can be performed by aggregating multiple traces
-directly, or so called :dfn:`Coverage Checkpoints`, which are just an
-internal representation of partial coverage results.
+The examples provided in most of this manual assume a native
+configuration and the process is similar cross environments. Typical
+variations would touch switches controlling where and when coverage
+trace data is output by the instrumented programs, and default values
+for such switches are selected by the tool based on
+:cmd-option:`--target` and :cmd-option:`--RTS` arguments at setup
+time.
 
-The high level processes involved in using binary or source traces are
-sketched on :numref:`fig-flow0`. We don't support mixing the two kinds
-of traces together and have depicted a separate analysis/consolidation
-step to emphasize this.
 
-We have however strived to maximize the commonalities between the two
-schemes, as hinted by the use of the same blue color for the common
-notions and analysis steps. Indeed, the command line interfaces
-involved are the same, they use inputs of similar general nature
-(traces or checkpoints) and share common definitions of available
-output formats for source coverage criteria.  This is intended to
-facilitate transitions from one scheme to the other and ensure that
-the widest possible range of improvements benefit both modes in a
-consistent manner.
+Sample project
+--------------
 
-.. _fig-flow0:
-.. figure:: fig_flow0.*
-  :align: center
-
-  Coverage analysis processes overview
-
-.. _selecting_trace_kind:
-
-Selecting a trace mode; supported features and environments
-===========================================================
-
-The source and binary trace modes each have specific strengths, the
-relative importance of which depends on the project context.  The
-matrix below summarizes the main characteristics of each mode, as a
-first key to help select one scheme or the other for a given software
-development project:
-
-.. list-table::
-   :widths: 20 20 20
-   :header-rows: 1
-   :stub-columns: 1
-
-   * -
-     - **With Source Traces**
-     - **With Binary Traces**
-   * - *Tracing mechanism*
-     - Program instrumentation
-     - Execution environment
-   * - ~
-     - ~
-     - ~
-   * - *Native applications*
-     - Yes (including shared-libraries)
-     - No
-   * - *Cross configurations with RTOS*
-     - Yes
-     - No
-   * - *Bareboard configurations*
-     - Yes
-     - Selectively (specific CPUs only, through GNATemulator or
-       hardware probes)
-   * - ~
-     - ~
-     - ~
-   * - *Object coverage analysis (language agnostic)*
-     - No
-     - Yes
-   * - *Source coverage analysis for Ada (up to Ada 2012)*
-     - Yes
-     - Yes
-   * - *Source coverage analysis for C*
-     - Beta in release 22
-     - Yes
-
-The source trace based workflow also has a few other limitations that
-are outlined in section :ref:`instr-limitations`.
-
-A brief introduction to the main process steps
-==============================================
-
-This section is intended to illustrate the basics of the process on a
-very simple example, without getting into details.
-
-We start from a very simple Ada package exposing a set of elementary
-operations over ``Integer`` objects, with a spec and body in source files
-named ``ops.ads`` and ``ops.adb``:
+We consider an Ada package providing a set of elementary operations
+over ``Integer`` objects, with a spec and body in source files named
+``ops.ads`` and ``ops.adb``:
 
 .. code-block:: ada
 
@@ -168,7 +128,7 @@ named ``ops.ads`` and ``ops.adb``:
      end;
    end;
 
-We will analyse the statement coverage achieved by the sample unit :term:`test
+We will analyze the coverage achieved by the sample unit :term:`test
 driver <Test Driver>` below, in ``test_inc.adb``, which exercises the
 ``Increment`` operation only:
 
@@ -184,13 +144,9 @@ driver <Test Driver>` below, in ``test_inc.adb``, which exercises the
    end;
 
 
-We will illustrate two basic use cases, one using binary traces produced by
-GNATemulator for a cross target, and one using source traces for a native
-environment.
-Assuming we start from a temporary working directory, with the *ops* sources
-in an ``opslib`` subdirectory and the *test* sources in a ``tests``
-subdirectory, we will rely for both cases on a couple of project files in the
-common working directory:
+Assuming a working directory, with the *ops* sources in an ``opslib``
+subdirectory and the *test* sources in a ``tests`` subdirectory, we
+will use a couple of project files in the common working directory:
 
 .. code-block:: ada
 
@@ -213,122 +169,77 @@ common working directory:
   end Tests;
 
 
-If you wish to experiment with both trace modes, you should start from
-separate working directories (one for each mode) to prevent possible
-intereferences of artifacts from one mode on the other.
-
-
-Example production of a binary trace for a bareboard environment
-----------------------------------------------------------------
-
-For binary traces, |gcv| relies on an instrumented execution environment to
-produce the traces instead of having to instrument the program itself with
-extra code and data structures. For cross configurations, |gem| provides such
-an environment.  Hardware probes may also be used, provided trace data is
-converted to the format |gcv| expects.
-
-Programs are built from their original sources, only requiring the use of
-:cmd-option:`-g -fpreserve-control-flow -fdump-scos` compilation options to
-generate coverage obligation lists and let us associate execution traces to
-these obligations afterwards.
-
-For our example use case here, we first use the GNAT Pro toolset for
-``powerpc-elf`` to build, using :command:`gprbuild` as follows::
+Setup, Instrument, Build, Execute, Analyze
+------------------------------------------
 
-   gprbuild --target=powerpc-elf --RTS=light-mpc8641 -Ptests.gpr \
-     -cargs -g -fpreserve-control-flow -fdump-scos
+The instrumentation step that follows assumes that the original program
+is well formed. A simple way to verify this is to build the non instrumented
+version first. For our example, this would be::
 
-We pass the project file with ``-P``, the required compilation flags
-with ``-cargs`` and request the use of a ``light`` runtime library tailored
-for the ``mpc8641`` board.
+   gprbuild -f -p -Ptests.gpr
 
-The build command produces a ``test_inc`` executable in the object
-subdirectory. To automate the execution of this program within |gem|
-to produce a trace, we provide the |gcvrun| command. For the use case
-at hand, we would simply do::
+We then first set up the instrumentation context, providing a local
+*prefix* location where the runtime and default parameters for future
+commands are going to be installed::
 
-  gnatcov run --target=powerpc-elf obj-tests/test_inc
+   gnatcov setup --prefix=/path/to/gnatcov-rts
 
+Letting further commands know about the *prefix* location is achieved
+by adding ``<prefix>/share/gpr`` to the ``GPR_PROJECT_PATH``
+variable. In a Unix like environment, this would be::
 
-... which would produce a ``test_inc.trace`` binary trace file in the current
-directory.  By default, such a trace is amenable to statement and decision
-coverage at most. If MCDC analysis is needed, ``--level=stmt+mcdc`` must be
-passed to |gcvrun| as well and we recommand also providing source coverage
-obligations in this case.
+   export GPR_PROJECT_PATH=$GPR_PROJECT_PATH:/path/to/gnatcov-rts/share/gpr
 
-Example production of a source trace for a native environment
--------------------------------------------------------------
+This will both let the ``gprbuild`` command below locate the
+``gnatcov_rts.gpr`` project file, and the |gcvins| command find
+default parameter values.
 
-The production of source traces is performed by an instrumented version of the
-program running in its regular execution environment. The coverage data is
-collected and output by the program itself. The output step is performed by a
-specific instrumentation of the program main unit, according to a user
-selectable policy.
+Instrumentation is performed by a simple |gcvins| command::
 
-The whole scheme requires the use of GPR project files. The code inserted by
-the instrumentation process relies on common types and subprograms provided by
-a :dfn:`coverage runtime` library, distributed in source form with |gcp|. The
-first thing to do for a given project is then to setup this coverage runtime
-so it becomes available to the instrumented sources afterwards. This step is
-documented in the :ref:`instr-rts` section of this manual.
+   gnatcov instrument -Ptests.gpr --level=stmt
 
-Instrumenting a test main program together with its "code" dependency is then
-achieved by a |gcvins| command.  For our example use case, this would be::
+The use of ``tests.gpr``, not ``code.gpr``, at this step is important
+as it lets the instrumenter know about the main subprogram, which
+needs to be processed specially to dump coverage data.
 
-    gnatcov instrument -Ptests.gpr --level=stmt \
-      --dump-trigger=atexit --dump-channel=bin-file
+Building the instrumented program then goes like::
 
-The ``--dump-channel=bin-file`` switch requests outputing coverage data
-directly to a trace file and ``--dump-trigger=atexit`` instructs to perform
-this operation as part of an execution termination handler, the simplest
-option in native environments. ``--level=stmt`` states that we will want to
-perform statement coverage analysis afterwards and ``-Ptests.gpr`` specifies
-the root project for coverage obligations and the main unit(s) to instrument.
+   gprbuild -f -p -Ptests.gpr \
+      --src-subdirs=gnatcov-instr --implicit-with=gnatcov_rts.gpr
 
-After setting ``GPR_PROJECT_PATH`` to designate the directory where the
-coverage runtime has been installed, building the instrumented program then
-goes like::
+This is the same command as for the regular build, with a couple
+of additional switches to:
 
-    gprbuild -f -p -Ptests.gpr \
-      --src-subdirs=gnatcov-instr --implicit-with=gnatcov_rts
+  * Instruct the builder to search for the instrumented versions of the
+    sources (``--src-subdirs``),
 
+  * Provide visibility to the builder over the coverage runtime
+    referenced by the instrumented sources (``--implicit-with``).
 
-The ``--src-subdirs`` and ``--implicit-with`` options respectively instruct
-the builder to use the alternative sources produced by the instrumenter and to
-automatically provide visiblity over the coverage runtime. This allows
-building the instrumented version of the program without requiring any change
-to the GPR project files.
-
-Then simply executing the test program in its native environment, as in::
+Executing the test program in its native environment, as in::
 
   obj-tests/test_inc
 
-produces a ``test_inc-<stamp>.srctrace`` source trace file in the
+then produces a ``test_inc-<stamp>.srctrace`` source trace file in the
 current directory. The ``-<stamp>`` suffix is intended to prevent
 clashes in case of concurrent executions of the program in the same
 directory. It can be controlled in a variety of ways from the
 instrumentation command line, documented in the :ref:`instr-tracename`
 section of this manual.
 
+Analysis of the coverage achieved by previous executions is done with
+|gcvcov| commands. For our example use case, this could for instance be::
 
-Example production of a coverage report
----------------------------------------
-
-Analysis of the coverage achieved by previous executions is done with |gcvcov|
-commands. For our example use case, this could for example be::
-
-  gnatcov coverage --level=stmt --annotate=xcov <trace> -Ptests.gpr
+  gnatcov coverage --level=stmt --annotate=xcov test_inc*.srctrace -Ptests.gpr
 
-... where ``<trace>`` would be either the source or the binary trace produced
-by the commands introduced in the previous example sections. Here, we request:
+Here, we request:
 
 - A source *statement coverage* assessment with :cmd-option:`--level=stmt`,
 
 - An annotated source report in text format with :cmd-option:`--annotate=xcov`,
 
-- For the complete set of units involved in the executable, per
-  :cmd-option:`-Ptests.gpr` and no specification otherwise in the project
-  files.
+- For the complete set of units involved in the executable with
+  :cmd-option:`-Ptests.gpr`.
 
 This produces annotated sources in the project's object directory,
 with ``ops.adb.xcov`` quoted below:
@@ -354,20 +265,19 @@ all the statements except the one dealing with a ``Decrement`` operation,
 indeed never exercised by our driver.
 
 The command actually also produces reports for ``ops.ads`` and
-``test_inc.adb``, even though the latter is not really relevant. Focus on
-specific units can be achieved by providing a more precise set of units of
-interest at this stage. For source traces, this could also be incorporated as
-part of the instrumentation step, as there is no point in instrumenting the
-test units for their own coverage achievements.
+``test_inc.adb``, even though the latter is not really relevant. Focus
+on specific units can be achieved by providing a more precise set of
+units of interest at this step, for example by adding
+``--projects=code.gpr`` to the command line, or setting dedicated attributes
+in the project files themselves. See the :ref:`sunits` chapter for
+details on this aspect of the procedure.
 
 Going Further
 =============
 
 Each of the steps involved in the process overview presented previously
-is described in detail in a specific chapter of this manual, the most
-important ones referenced hereafter:
-
-- :ref:`bin_traces`
+is described in detail in a specific chapter of this manual. The most
+important ones are:
 
 - :ref:`src_traces`
 
@@ -375,13 +285,14 @@ important ones referenced hereafter:
 
 - :ref:`scov`
 
-- :ref:`ocov`
-
 - :ref:`consolidation`
 
 :ref:`exemptions` is also worth noting here, a mechanism allowing
 users to define code regions for which coverage violations are
-expected and legitimate (Ada only at this stage).
+expected and legitimate.
+
+The known limitations of the tool are outlined in section
+:ref:`instr-limitations`.
 
 
 Conventions used in the rest of this manual
diff --git a/doc/gnatcov/gnatcov_bin_part.rst b/doc/gnatcov/gnatcov_bin_part.rst
new file mode 100644
index 000000000..34dfeb1b8
--- /dev/null
+++ b/doc/gnatcov/gnatcov_bin_part.rst
@@ -0,0 +1,13 @@
+#############################################
+Appendix A. Using GNATcoverage Binary Traces
+#############################################
+
+.. toctree::
+   :maxdepth: 3
+
+   bin_intro
+   bin_traces
+   cov_object
+   cov_metrics
+   bin_gps
+   bin_convert
diff --git a/doc/gnatcov/gnatcov_part.rst b/doc/gnatcov/gnatcov_part.rst
index 841d4f80f..98b5ab2d0 100644
--- a/doc/gnatcov/gnatcov_part.rst
+++ b/doc/gnatcov/gnatcov_part.rst
@@ -7,15 +7,10 @@ GNATcoverage User's Guide
 
    getting_started
    units_of_interest
-   bin_traces
    src_traces
    cov_source
-   cov_object
-   cov_metrics
    consolidation
    exemptions
    gpr
-   bin_traces_convert
    gps
-   appendix
    glossary
diff --git a/doc/gnatcov/gps.rst b/doc/gnatcov/gps.rst
index d180e1d8b..d96b5dcd6 100644
--- a/doc/gnatcov/gps.rst
+++ b/doc/gnatcov/gps.rst
@@ -5,19 +5,22 @@ Using |gcp| from |gps|
 GNATcoverage is integrated in |gps| so that it is possible to analyze code
 coverage without leaving your favorite editor.
 
-Basic usage
-===========
+Basic scenario
+==============
 
-As a first step, indicate which coverage criterion GNATcoverage should use.
-The default is stmt.
+As a first step, indicate which coverage criterion GNATcoverage should use
+through the :ref:`project file<switches_attr>`. If no coverage level is set
+in the project file, gnatcov  will default to ``stmt``, and emit a warning
+indicating that the coverage level is not set.
 For this, go to the project properties (:menuselection:`Edit -->
 Project Properties...`).
 
 .. image:: gps_screenshots/3-cov-level.png
 
-In the GNATcov entry, you can change the coverage level passed to |gcv| commands
-in the corresponding *Coverage level* combo boxes. Change both to
-``stmt+decision``.
+In the :menuselection:`Build --> Switches --> GNATcoverage` entry, you can
+change the coverage level passed to |gcv| commands in the corresponding
+*Coverage level* combo
+box. Change it to ``stmt+decision``.
 
 This is all for the setup part. Then you just need to click on the
 *Run GNATcoverage* toolbar button as shown in the following screenshot.
@@ -55,31 +58,6 @@ file with annotations that represent the coverage state:
 Note that the Locations view reports all non-covered lines. It makes navigation
 easy as you only have to click on a diagnostic to go to the corresponding line.
 
-Binary traces usage
-===================
-
-Starting from GNATcoverage version 22.0, binary traces (using gnatcov run) are
-no longer supported in native profiles. This mode is still supported for cross
-configurations so the same steps performed above for source traces can be done
-for binary traces IF you are in a supported configuration (otherwise, the menu
-items won't show up).
-
-Go to the menu
-:menuselection:`Analyze --> Coverage --> GNATcoverage Binary Traces`.
-to perform each step individually, or use the ``Run All Actions`` item.
-
-You can also replace the toolbar button (dedicated to the instrumentation-based
-scheme) with an alike button, but for binary traces. Go to GNATstudio
-Preferences in :menuselection:`Edit --> Preferences`.
-
-Then, go to the ``Build Targets`` section, and find the ``Run GNATcoverage``
-workflow: add it to the toolbar as shown below.
-
-.. image:: gps_screenshots/7-binary-traces.png
-
-Remove from the toolbar the ``Run GNATcoverage with instrumentation`` in the
-same fashion.
-
 
 GNATtest scenario
 =================
diff --git a/doc/gnatcov/gps_screenshots/3-cov-level.png b/doc/gnatcov/gps_screenshots/3-cov-level.png
index 5e2bb3a64..256fe5d97 100644
Binary files a/doc/gnatcov/gps_screenshots/3-cov-level.png and b/doc/gnatcov/gps_screenshots/3-cov-level.png differ
diff --git a/doc/gnatcov/run_prereq.rst b/doc/gnatcov/run_prereq.rst
index f82797f22..0ebcf524c 100644
--- a/doc/gnatcov/run_prereq.rst
+++ b/doc/gnatcov/run_prereq.rst
@@ -3,13 +3,24 @@
 Compilation prerequisites
 =========================
 
+General rules
+-------------
+
 If the traces are intended to be used for :ref:`source coverage analysis
 <scov>`, regardless of the actual criteria to be assessed afterwards, sources
 must be compiled with :cmd-option:`-g -fpreserve-control-flow -fdump-scos` to
 control optimizations and allow mapping trace info to source constructs.
 :cmd-option:`-gno-strict-dwarf` is required in addition for VxWorks targets,
 together with :cmd-option:`-mlongcall` for PowerPC configurations.
-Optimization is supported up to :cmd-option:`-O1`, with inlining allowed.
+
+Optimization is supported up to :cmd-option:`-O1`, with inlining
+allowed. However, if the binary version of the code under test during
+the coverage campaign is intended to be used directly in operational
+conditions, care is needed to prevent inlining of the code under test
+within the testing code at compilation time in unit-testing campaigns.
+With GCC based toolchains such as GNAT Pro, this can be achieved by
+adding :cmd-option:`-fno-inline` to the compilation options of the
+testing code.
 
 The :cmd-option:`-fprofile-arcs` or :cmd-option:`--coverage` compilation
 switches, intended for analysis with the gcov tool, are incompatible with the
@@ -26,3 +37,33 @@ process might turn simpler if the same compilation options are used for both
 kinds of analysis. There is no incompatibility with object coverage analysis
 incurred by compiling the code compiled with options required for source
 coverage.
+
+Consolidation related rules
+---------------------------
+
+For object or source level criteria, |gcv| computes the coverage achieved for
+the full set of routines or source units declared to be of interest amongst
+those exposed by the union of the exercised executables, as designated by the
+set of consolidated traces;
+
+For the purpose of computing combined coverage achievements, two symbols are
+considered overlapping when all the following conditions are met:
+
+* Both symbols have identical names at the object level,
+
+* Both symbols have DWARF debug information attached to them,
+
+* According to this debug information, both symbols originate from the same
+  compilation unit, denoted by the full path of the corresponding source file.
+
+By this construction, a symbol missing debug information is never considered
+overlapping with any other symbol. Whatever coverage is achieved on such a
+symbol never gets combined with anything else and the only kind of report where
+the symbol coverage is exposed is the :cmd-option:`=asm` assembly output for
+object level criteria.
+
+Moreover, for object level coverage criteria, |gcvcov| will issue a
+consolidation error when two symbols are found to overlap but have
+structurally different machine code, which happens for example when the same
+unit is compiled with different different optimization levels for
+different executables.
diff --git a/doc/gnatcov/src_traces.rst b/doc/gnatcov/src_traces.rst
index 91cfa0e16..6fe1a6ddf 100644
--- a/doc/gnatcov/src_traces.rst
+++ b/doc/gnatcov/src_traces.rst
@@ -348,7 +348,7 @@ providing the variable name to use.
 |gcvins| limitations
 --------------------
 
-There are situations and code patterns not handeled correctly by |gcvins|.
+There are situations and code patterns not handled correctly by |gcvins|.
 Below are listed the limitations associated with general Ada sources.
 Coverage of SPARK sources require additional considerations, detailed in
 section :ref:`spark_instr`.
@@ -356,10 +356,12 @@ section :ref:`spark_instr`.
 Unsupported source constructs
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-There are a few language constructs that |gcvins| doesn't handle properly.
+There are a few language constructs that |gcvins| doesn't support.
 The tool emits a warning when it encounters such cases and the corresponding
-code is not instrumented. Source coverage obligations are still emitted, so
-the unsupported constructs will appear as ``not covered`` in the report.
+code is not instrumented. Source coverage obligations are still emitted, and
+the unsupported constructs will be reported in a separate 
+``Undetermined_Coverage`` category, to differentiate them from actual coverage
+violations.
 
 The list of unsupported constructs is as follows:
 
@@ -370,11 +372,19 @@ The list of unsupported constructs is as follows:
   tagged type.
 
 The simplest way to work around the limitation concerning expression functions
-is to turn them into regular funtions, by giving them a proper body,
-containing a single return statment with the original expression.
+is to turn them into regular functions, by giving them a proper body,
+containing a single return statement with the original expression.
 Otherwise it is possible to exempt those constructs (see :ref:`exemptions`)
 and/or perform a manual coverage analysis for these special cases.
 
+Additionally, the MC/DC instrumentation of decisions with many conditions
+may require more memory than available (during instrumentation and/or at
+run-time) to enumerate the possible paths through the decision. To avoid this,
+|gcv| will not instrument such decisions for MC/DC, emitting a warning in the
+process, and the MC/DC coverage for each decision will be reported as
+``Undetermined_Coverage`` state. Should the default limit not be satisfactory,
+it can be tuned with the option :cmd-option:`--path-count-limit`.
+
 Global source traces limitations
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -389,7 +399,7 @@ Toolchain-specific limitations
 
 With GNAT versions from 7.1 to 7.3, compiling with optimization will result in
 coverage violations on all statement obligations associated with expression
-functions. Explicitely disabling optimization (with ``-O0`` for instance) will
+functions. Explicitly disabling optimization (with ``-O0`` for instance) will
 resolve this issue.
 
 .. _instr-build:
diff --git a/doc/gnatcov/units_of_interest.rst b/doc/gnatcov/units_of_interest.rst
index 748a59f55..b46112e49 100644
--- a/doc/gnatcov/units_of_interest.rst
+++ b/doc/gnatcov/units_of_interest.rst
@@ -3,71 +3,31 @@
 Specifying *Units Of Interest*
 ==============================
 
-This chapter describes the means available to convey the set of units on which
+This chapter describes the means to convey the set of units on which
 coverage should be assessed, which we commonly refer to as the :dfn:`units of
-interest`, and which are relevant to |gcvins|, |gcvrun| and |gcvcov|.
-
-There are two main families of such means: users would either provide the set
-of files which hold the coverage obligations for the units of interest, or
-rely on project files facilities to designate the set of units. At analysis
-time, the actual list of unit names for which a report or checkpoint is
-produced as well as the list of individually ignored source files for each unit
-can be displayed with the :cmd-option:`--dump-units-to` option of the
-|gcvcov| command.
-
-.. _passing_scos:
-
-Providing coverage obligation files (:cmd-option:`--scos|--sid`)
-----------------------------------------------------------------
-
-With the :cmd-option:`--scos` or :cmd-option:`--sid` command line arguments,
-users convey the set of units of interest by directly providing the set of
-files which contain the coverage obligations for those units.
-
-The :cmd-option:`--scos` switch is for binary trace based analysis and the
-files to provide are the *Library Information* files produced by the compiler
-(``.ali`` files for Ada, ``.gli`` files for C). The :cmd-option:`--sid` switch
-is for source trace based analysis and the files to provide are the ``.sid``
-*Source Instrumentation Data* files produced by ``gnatcov instrument``. In all
-cases, the files are located at the same place as where the object file for a
-unit is produced.
-
-The following paragraphs provide details and examples on the use of
-:cmd-option:`--scos` with ``.gli`` or ``.ali`` files. The same principles apply
-to :cmd-option:`--sid` with ``.sid`` files.
-
-Each occurrence of :cmd-option:`--scos` on the command line expects a single
-argument which specifies a set of units of interest. Multiple occurrences are
-allowed and the sets accumulate. The argument might be either the name of a
-single Library Information file for a unit, or a :term:`@listfile arguments
-<@listfile argument>` expected to contain a list of such file names.
-
-For example, focusing on Ada units ``u1``, ``u2`` and ``u3`` can be achieved
-with either ``--scos=u1.ali --scos=u2.ali --scos=u3.ali``, with ``--scos=u3.ali
---scos=@lst12`` where ``lst12`` is a text file containing the first two ALI
-file names, or with other combinations alike.
-
-The GNAT toolchain provides a useful device for list computations: the
-:cmd-option:`-A` command line argument to :command:`gnatbind` which produces a
-list of all the ``.ali`` files involved in an executable construction.  By
-default, the list goes to standard output. It may be directed to a file on
-request with :cmd-option:`-A=<list-filename>`, and users may of course filter
-this list as they see fit depending on their analysis purposes.
-
-Below is an example sequence of commands to illustrate, using the standard Unix
-``grep`` tool to filter out test harness units, assuming a basic naming
-convention::
-
-    # Build executable and produce the corresponding list of ALI files. Pass
-    # -A to gnatbind through gprbuild -bargs then filter out the test units:
-    gprbuild -p --target=powerpc-elf --RTS=zfp-prep -Pmy.gpr
-     test_divmod0.adb -fdump-scos -g -fpreserve-control-flow -bargs -A=all.alis
-
-    # Run and analyse all units except the test harness, filtering out
-    # the correspond ALI files from the list:
-    grep -v 'test_[^/]*.ali' all.alis > divmod0.alis
-    gnatcov run --level=stmt+mcdc --scos=@divmod0.alis
-    gnatcov coverage --level=stmt+mcdc --annotate=xcov --scos=@divmod0.alis
+interest`.
+
+The first opportunity to do so is at |gcvins| time, with GPR project file
+oriented command line switches possibly associated with coverage specific
+attributes within project files. This step controls which units are
+instrumented to track coverage to begin with. It also needs visibility
+over the main subprogram(s) as they require special processing to trigger
+the calls dumping coverage data when a program terminates.
+
+The use of such GPR based mechanisms is also allowed at |gcvcov| time to
+further refine the focus of reports or coverage checkpoints. Another mechanism
+is available at this point, with lower level command line switches letting
+users provide lists of files holding SCO definitions for the units of
+interest, where one such file is produced per unit by the |gcvins| command.
+When both obligation files and project file options are on the command line,
+the former prevail and the project files are only used for switches or the
+determination of the target and runtime configuration.
+
+Regardless of how units of interest were requested, the actual list of units
+for which a report is produced can be displayed with the
+:cmd-option:`--dump-units-to` option of the |gcvcov| command. This also
+displays the list of individually ignored source files for each unit,
+controlled by the :cmd-option:`--ignore-source-files` switch.
 
 
 .. _passing_gpr:
@@ -75,15 +35,23 @@ convention::
 Using project files (:cmd-option:`-P`, :cmd-option:`--projects`, :cmd-option:`--units`)
 ---------------------------------------------------------------------------------------
 
-As an alternative to providing the complete list of coverage obligation files
-with :cmd-option:`--scos` or :cmd-option:`--sid`, you can use project files to
-specify units of interest. When both obligation files and project file options
-are on the command line, the former prevail and the project files are only used
-for switches or the determination of the target and runtime configuration.
+The simplest possible form of units of interest specification with GPR
+facilities is a lone::
+
+  -P<myproject>.gpr
+
+provided to both |gcvins| and |gcvcov|. In the absence of coverage related
+attributes within the project file(s), this requests considering *of interest*
+all the units of *<myproject>* and its project dependency closure.
+
+For |gcvins|, the source files containing main subprograms need to be
+encompassed by ``myproject.gpr`` and specified either by a ``Main``
+project file attribute or provided on the command line, as for ``gprbuild``
+commands.
 
-The units of interest designation with project files incurs two levels of
-selection: first, specify the set of :dfn:`projects of interest` where the
-units of interest reside, then specify units of interest therein.
+Finer grain control is possible with additional switches and attributes,
+letting users first specify the set of :dfn:`projects of interest` where the
+units of interest reside, then may filter the *units* of interest therein.
 
 Conveying *projects* of interest
 ********************************
@@ -465,10 +433,33 @@ Typically, from a sample ``foo.c`` source like:
    { ... }
 
 
-``gcc -c foo.c -fdump-scos ...`` would produce a ``foo.o`` object file, a
-``foo.c.gli`` companion Library Information file, and excluding it from the
-analysis scope can be achieved with::
+excluding ``foo.c`` from the analysis scope can be achieved with::
 
   package Coverage is
      for Excluded_Units use ("foo.c"); /* source file name here  */
   end Coverage;
+
+.. _passing_scos:
+
+Providing coverage obligation files (:cmd-option:`--sid`)
+---------------------------------------------------------
+
+With the :cmd-option:`--sid` command line option, users can convey the set of
+units of interest by directly providing the set of files which contain the
+coverage obligations for those units.
+
+One such file is produced for each unit instrumented by the |gcvins| command,
+next to the object file for a unit, with a `.sid` extension which stands for
+*Source Instrumentation Data*.
+
+Each occurrence of :cmd-option:`--sid` on the command line expects a
+single argument which specifies a set of units of interest. Multiple
+occurrences are allowed and the sets accumulate. The argument might be
+either the name of a single `.sid` file for a unit, or a
+:term:`@listfile arguments <@listfile argument>` expected to contain a
+list of such file names.
+
+For example, focusing on Ada units ``u1``, ``u2`` and ``u3`` can be achieved
+with either ``--sid=u1.sid --sid=u2.sid --sid=u3.sid``, with ``--sid=u3.sid
+--sid=@lst12`` where ``lst12`` is a text file containing the first two SID
+file names, or with other combinations alike.
diff --git a/doc/gnattest/gnattest_part.rst b/doc/gnattest/gnattest_part.rst
index 22a1056d3..0e96b8992 100644
--- a/doc/gnattest/gnattest_part.rst
+++ b/doc/gnattest/gnattest_part.rst
@@ -307,9 +307,9 @@ Switches for ``gnattest`` in framework generation mode
   corresponding spec files, one per line.
 
 
-:switch:`--exclude-from-stubbing:{unit}={filename}`
+:switch:`--exclude-from-stubbing:{spec}={filename}`
   Same as above, but corresponding units will not be stubbed only when testing
-  specified ``unit``.
+  unit whose specification is declared in specified ``spec`` file.
 
 
   .. index:: --validate-type-extensions (gnattest)
@@ -460,10 +460,10 @@ package ``Gnattest``. Here is the list of attributes:
      is used to specify the file with list of units whose bodies should not
      be stubbed, otherwise specified by ``--exclude-from-stubbing=filename``.
 
-* ``Stub_Exclusion_List ("unit")``
+* ``Stub_Exclusion_List ("spec")``
      is used to specify the file with list of units whose bodies should not
-     be stubbed when testing "unit", otherwise specified by
-     ``--exclude-from-stubbing:unit=filename``.
+     be stubbed when testing "spec", otherwise specified by
+     ``--exclude-from-stubbing:spec=filename``.
 
 Each of those attributes can be overridden from the command line if needed.
 Other ``gnattest`` switches can also be passed via the project
diff --git a/doc/index.rst b/doc/index.rst
index ad35eb865..91d33436c 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -9,3 +9,8 @@
    gnatcov/gnatcov_part
    gnattest/gnattest_part
    integration/integration_part
+
+.. Appendix part - manual numbering
+
+.. toctree::
+   gnatcov/gnatcov_bin_part
diff --git a/doc/integration/integration_part.rst b/doc/integration/integration_part.rst
index 64be188a6..723d99ce0 100644
--- a/doc/integration/integration_part.rst
+++ b/doc/integration/integration_part.rst
@@ -49,11 +49,10 @@ If all the switches that need to be passed to |gcp| are correctly defined in
 will build and run all the harness projects and then generate a coverage report
 for all the units under test.
 
-By default, the trace kind (binary or source, see :ref:`selecting_trace_kind`)
-used to produce the coverage report will be chosen based on the target of the
-root project. Coverage data for a native project will be obtained using
-:ref:`source traces <src_traces>`, and coverage for projects targeting a cross
-environment will be assessed using :ref:`binary traces <bin_traces>`.
+By default, coverage data for a native project is obtained using
+:ref:`source traces <src_traces>` and coverage for projects targeting
+a cross environment is assessed using :ref:`binary traces
+<bin_traces>`.
 
 For cases where the default behavior isn't appropriate for the project under
 test, the rules for producing a coverage report from both kind of traces are
diff --git a/doc/latex_preamble.inc b/doc/latex_preamble.inc
index 9d2f7249e..bbf4720f1 100644
--- a/doc/latex_preamble.inc
+++ b/doc/latex_preamble.inc
@@ -3,6 +3,12 @@
 \let\oldpart\part
 \renewcommand{\part}[1]{\oldpart{#1}\gdef\parttitle{#1}}
 
+% Arrange to reset chapter numbering at the start of each "part"
+\makeatletter
+\@addtoreset{chapter}{part}
+\makeatother
+
+% Setup page headers and footers
 \makeatletter 
   \fancypagestyle{normal}{ 
     \fancyhf{} 
diff --git a/testsuite/SCOV/instr.py b/testsuite/SCOV/instr.py
index d6c4d78e8..450d43b19 100644
--- a/testsuite/SCOV/instr.py
+++ b/testsuite/SCOV/instr.py
@@ -248,3 +248,15 @@ def filename(prefix, ext):
 
     with open(main_file, 'w') as f:
         f.write('\n'.join(lines))
+
+
+def available_ada_dump_triggers():
+    """
+    Return the list of dump triggers available for the current Ada runtime.
+    """
+    if RUNTIME_INFO.has_full_runtime:
+        return ["main-end", "atexit"]
+    elif RUNTIME_INFO.has_ravenscar_runtime:
+        return ["main-end", "ravenscar-task-termination"]
+    else:
+        return ["main-end"]
diff --git a/testsuite/SUITE/srctracelib.py b/testsuite/SUITE/srctracelib.py
index 7f3d227e8..a43943212 100755
--- a/testsuite/SUITE/srctracelib.py
+++ b/testsuite/SUITE/srctracelib.py
@@ -40,7 +40,8 @@
     ('unit_part', 'B'),
     ('bit_buffer_encoding', 'B'),
     ('fingerprint', '20B'),
-    ('padding', '5B'),
+    ('bit_maps_fingerprint', '20B'),
+    ('padding', 'B'),
 )
 
 
@@ -95,7 +96,7 @@ class SrcTraceFile(object):
 
     MAGIC = b'GNATcov source trace file' + b'\x00' * 7
 
-    FORMAT_VERSION = 1
+    FORMAT_VERSION = 2
 
     ENDIANITY_NAMES = {
         0: 'little-endian',
@@ -190,9 +191,11 @@ def format_buffer(b):
         print('  Endianity: {}'.format(self.endianity))
         print('')
         for e in self.entries:
-            print('  Unit {} ({}, fingerprint={})'.format(
+            print('  Unit {} ({}, SCOS hash={}, bit maps hash={})'.format(
                 e.unit_name, e.unit_part,
-                ''.join('{:02x}'.format(b) for b in e.fingerprint)))
+                ''.join('{:02x}'.format(b) for b in e.fingerprint),
+                ''.join('{:02x}'.format(b) for b in e.bit_maps_fingerprint)
+            ))
             print('  Project: {}'.format(e.project_name))
             print('  Stmt buffer: {}'.format(format_buffer(e.stmt_buffer)))
             print('  Dc buffer:   {}'.format(format_buffer(e.dc_buffer)))
@@ -274,12 +277,14 @@ class TraceEntry(object):
         value: key for key, value in BIT_BUFFER_ENCODING_NAMES.items()}
 
     def __init__(self, language, unit_part, unit_name, project_name,
-                 fingerprint, stmt_buffer, dc_buffer, mcdc_buffer):
+                 fingerprint, bit_maps_fingerprint, stmt_buffer, dc_buffer,
+                 mcdc_buffer):
         self.language = language
         self.unit_part = unit_part
         self.unit_name = unit_name
         self.project_name = project_name
         self.fingerprint = fingerprint
+        self.bit_maps_fingerprint = bit_maps_fingerprint
         self.stmt_buffer = stmt_buffer
         self.dc_buffer = dc_buffer
         self.mcdc_buffer = mcdc_buffer
@@ -298,7 +303,7 @@ def read(cls, fp, trace_file, big_endian):
 
             language = cls.LANGUAGE_NAMES[header['language']]
 
-            if header['padding'] != (0, ) * 5:
+            if header['padding'] != 0:
                 raise ValueError('Invalid padding: {}'
                                  .format(header['padding']))
 
@@ -328,8 +333,8 @@ def read(cls, fp, trace_file, big_endian):
                     header['mcdc_bit_count'])
 
         return cls(language, unit_part, unit_name, project_name,
-                   header['fingerprint'], stmt_buffer, dc_buffer,
-                   mcdc_buffer)
+                   header['fingerprint'], header['bit_maps_fingerprint'],
+                   stmt_buffer, dc_buffer, mcdc_buffer)
 
     def write(self, fp, big_endian, alignment):
         """Write this trace info entry to the `fp` file."""
@@ -344,7 +349,8 @@ def write(self, fp, big_endian, alignment):
             'bit_buffer_encoding':
                 self.BIT_BUFFER_ENCODING_CODES['lsb_first_bytes'],
             'fingerprint': self.fingerprint,
-            'padding': (0, ) * 5,
+            'bit_maps_fingerprint': self.bit_maps_fingerprint,
+            'padding': 0,
         }, big_endian=big_endian)
 
         write_aligned(fp, self.unit_name, alignment)
diff --git a/testsuite/tests/24_mixed_c_c++/pkg.c b/testsuite/tests/24_mixed_c_c++/pkg.c
new file mode 100644
index 000000000..5d74411a2
--- /dev/null
+++ b/testsuite/tests/24_mixed_c_c++/pkg.c
@@ -0,0 +1,5 @@
+int
+id (int x)
+{
+  return x;
+}
diff --git a/testsuite/tests/24_mixed_c_c++/test.cpp b/testsuite/tests/24_mixed_c_c++/test.cpp
new file mode 100644
index 000000000..548d53dba
--- /dev/null
+++ b/testsuite/tests/24_mixed_c_c++/test.cpp
@@ -0,0 +1,8 @@
+extern "C" int id (int x);
+
+int
+main ()
+{
+  int a = id (1);
+  return 0;
+}
diff --git a/testsuite/tests/24_mixed_c_c++/test.opt b/testsuite/tests/24_mixed_c_c++/test.opt
new file mode 100644
index 000000000..195e6c2d4
--- /dev/null
+++ b/testsuite/tests/24_mixed_c_c++/test.opt
@@ -0,0 +1 @@
+!C++ DEAD test requires C++ compiler
diff --git a/testsuite/tests/24_mixed_c_c++/test.py b/testsuite/tests/24_mixed_c_c++/test.py
new file mode 100644
index 000000000..9a4f7c42a
--- /dev/null
+++ b/testsuite/tests/24_mixed_c_c++/test.py
@@ -0,0 +1,31 @@
+"""
+Check that the instrumentation of a project mixing C and C++ works. It used to
+produce files with the same object name, resulting in an error at build time.
+"""
+
+
+from SCOV.minicheck import build_run_and_coverage, check_xcov_reports
+from SUITE.cutils import Wdir
+from SUITE.gprutils import GPRswitches
+from SUITE.tutils import thistest, gprfor
+
+Wdir("tmp_")
+
+build_run_and_coverage(
+    gprsw=GPRswitches(root_project=gprfor(srcdirs=[".."],
+                                          mains=["test.cpp"])),
+    covlevel="stmt",
+    mains=["test"],
+    extra_coverage_args=["--annotate=xcov"],
+)
+
+check_xcov_reports(
+    "*.xcov",
+    {
+        "test.cpp.xcov": {"+": {6, 7}},
+        "pkg.c.xcov": {"+": {4}},
+    },
+    "obj",
+)
+
+thistest.result()
diff --git a/testsuite/tests/duplicate_postprocess/lib.c b/testsuite/tests/duplicate_postprocess/lib.c
new file mode 100644
index 000000000..4d81d8d52
--- /dev/null
+++ b/testsuite/tests/duplicate_postprocess/lib.c
@@ -0,0 +1,19 @@
+extern void dummy_puts (const char *msg);
+
+#define PRINT_HW dummy_puts ("Hello world!\n");
+
+/* Thanks to macro_stmts_enabled returning 0, macro_stmts is never executed,
+   and thus we get coverage violations inside macro expansion.  */
+
+int
+macro_stmts_enabled (void)
+{
+  return 0;
+}
+
+void
+macro_stmts (void)
+{
+  PRINT_HW;
+  PRINT_HW;
+}
diff --git a/testsuite/tests/duplicate_postprocess/test.opt b/testsuite/tests/duplicate_postprocess/test.opt
new file mode 100644
index 000000000..ae9029965
--- /dev/null
+++ b/testsuite/tests/duplicate_postprocess/test.opt
@@ -0,0 +1 @@
+bin-traces DEAD Test specific to instrumentation
diff --git a/testsuite/tests/duplicate_postprocess/test.py b/testsuite/tests/duplicate_postprocess/test.py
new file mode 100644
index 000000000..9ca45b0b0
--- /dev/null
+++ b/testsuite/tests/duplicate_postprocess/test.py
@@ -0,0 +1,35 @@
+"""
+Regression test: check that we do not preprocess multiple times a file at
+coverage time.
+"""
+
+import re
+
+from SCOV.minicheck import build_run_and_coverage
+from SUITE.control import env
+from SUITE.cutils import contents_of, Wdir
+from SUITE.gprutils import GPRswitches
+from SUITE.tutils import gprfor, thistest
+
+Wdir("tmp_")
+
+build_run_and_coverage(
+    gprsw=GPRswitches(
+        root_project=gprfor(srcdirs=[".."], mains=["test_macros.c"])
+    ),
+    covlevel="stmt+mcdc",
+    mains=["test_macros"],
+    extra_coverage_args=["--annotate=xcov+", "-v"],
+)
+
+thistest.fail_if(
+    len(
+        re.findall(
+            f"gcc{env.build.os.exeext} -E", contents_of("coverage.log")
+        )
+    )
+    != 1,
+    "source file was preprocessed multiple times",
+)
+
+thistest.result()
diff --git a/testsuite/tests/duplicate_postprocess/test_macros.c b/testsuite/tests/duplicate_postprocess/test_macros.c
new file mode 100644
index 000000000..be45e319b
--- /dev/null
+++ b/testsuite/tests/duplicate_postprocess/test_macros.c
@@ -0,0 +1,16 @@
+extern int macro_stmts_enabled (void);
+extern void macro_stmts (void);
+
+void
+dummy_puts (const char *msg)
+{
+  return;
+}
+
+int
+main (void)
+{
+  if (macro_stmts_enabled ())
+    macro_stmts ();
+  return 0;
+}
diff --git a/testsuite/tests/excluded_unit_imported_prj/prj1/pkg.c b/testsuite/tests/excluded_unit_imported_prj/prj1/pkg.c
new file mode 100644
index 000000000..f939f7693
--- /dev/null
+++ b/testsuite/tests/excluded_unit_imported_prj/prj1/pkg.c
@@ -0,0 +1,5 @@
+int
+foo (int a, int b)
+{
+  return a && b;
+}
diff --git a/testsuite/tests/excluded_unit_imported_prj/test.c b/testsuite/tests/excluded_unit_imported_prj/test.c
new file mode 100644
index 000000000..19740b019
--- /dev/null
+++ b/testsuite/tests/excluded_unit_imported_prj/test.c
@@ -0,0 +1,8 @@
+extern int foo (int a, int b);
+
+int
+main ()
+{
+  foo (0, 1);
+  return 0;
+}
diff --git a/testsuite/tests/excluded_unit_imported_prj/test.py b/testsuite/tests/excluded_unit_imported_prj/test.py
new file mode 100644
index 000000000..5d5855fa3
--- /dev/null
+++ b/testsuite/tests/excluded_unit_imported_prj/test.py
@@ -0,0 +1,57 @@
+"""
+This test checks that units of interest project attributes only apply to the
+project in which they are defined, and not to recursive project dependencies.
+"""
+
+from SCOV.minicheck import build_run_and_coverage, check_xcov_reports
+from SUITE.gprutils import GPRswitches
+from SUITE.cutils import contents_of, Wdir
+from SUITE.tutils import thistest, gprfor
+
+from e3.fs import mkdir
+
+Wdir('tmp_')
+
+prj1 = gprfor(mains=[], prjid='prj1', srcdirs=['../prj1'], objdir='obj-prj1')
+
+# Prevent warnings about inexistant obj dir, that some versions of gnatcov
+# might emit when instrumenting before building:
+mkdir("obj-prj1")
+
+# Check that ignoring pkg.c in the main project yields a warning, as the pkg.c
+# unit is in thr prj1 dependency and not in the root project.
+extra_p = """
+package Coverage is
+   for Excluded_Units use ("pkg.c");
+end Coverage;
+"""
+p = gprfor(mains=['test.c'], deps=['prj1'], srcdirs=['..'], extra=extra_p)
+
+build_run_and_coverage(
+    gprsw=GPRswitches(root_project=p),
+    covlevel='stmt+mcdc',
+    mains=['test'],
+    extra_coverage_args=['--annotate=xcov'],
+    instrument_warnings_as_errors=False,
+)
+warning_file = ('coverage.log' if thistest.options.trace_mode == 'bin'
+                else 'instrument.log')
+
+thistest.fail_if_not_equal(
+    warning_file,
+    'warning: no unit pkg.c in project gen'
+    ' (coverage.excluded_units attribute)\n',
+    contents_of(warning_file)
+)
+
+# Check that pkg.c is not ignored by checking the report contents
+check_xcov_reports(
+    '*.xcov',
+    {
+        'pkg.c.xcov': {'!': {4}},
+        'test.c.xcov': {'+': {6, 7}}
+    },
+    'obj'
+)
+
+thistest.result()
diff --git a/testsuite/tests/header_not_found/base_foo/base.gpr b/testsuite/tests/header_not_found/base_foo/base.gpr
new file mode 100644
index 000000000..44b4bce9c
--- /dev/null
+++ b/testsuite/tests/header_not_found/base_foo/base.gpr
@@ -0,0 +1,3 @@
+project Base is
+   for Languages use ("C");
+end Base;
diff --git a/testsuite/tests/header_not_found/base_foo/support.h b/testsuite/tests/header_not_found/base_foo/support.h
new file mode 100644
index 000000000..e05eb7e69
--- /dev/null
+++ b/testsuite/tests/header_not_found/base_foo/support.h
@@ -0,0 +1,4 @@
+int foo()
+{
+  return 0;
+}
diff --git a/testsuite/tests/header_not_found/include_base_foo/prj.gpr b/testsuite/tests/header_not_found/include_base_foo/prj.gpr
new file mode 100644
index 000000000..def07ce5d
--- /dev/null
+++ b/testsuite/tests/header_not_found/include_base_foo/prj.gpr
@@ -0,0 +1,5 @@
+project Prj extends "../base_foo/base.gpr" is
+   for Main use ("test.c");
+   for Source_Dirs use (".");
+   for Object_Dir use "obj";
+end Prj;
diff --git a/testsuite/tests/header_not_found/include_base_foo/test.c b/testsuite/tests/header_not_found/include_base_foo/test.c
new file mode 100644
index 000000000..5991bc01c
--- /dev/null
+++ b/testsuite/tests/header_not_found/include_base_foo/test.c
@@ -0,0 +1,6 @@
+#include "support.h"
+
+int main()
+{
+  return foo();
+}
diff --git a/testsuite/tests/header_not_found/include_foo/headers/support.h b/testsuite/tests/header_not_found/include_foo/headers/support.h
new file mode 100644
index 000000000..85471f974
--- /dev/null
+++ b/testsuite/tests/header_not_found/include_foo/headers/support.h
@@ -0,0 +1,6 @@
+int foo()
+{
+  // Dummy comment to make this header different from its version in the
+  // extended project.
+  return 0;
+}
diff --git a/testsuite/tests/header_not_found/include_foo/prj.gpr b/testsuite/tests/header_not_found/include_foo/prj.gpr
new file mode 100644
index 000000000..6f538b733
--- /dev/null
+++ b/testsuite/tests/header_not_found/include_foo/prj.gpr
@@ -0,0 +1,5 @@
+project Prj extends "../base_foo/base.gpr" is
+   for Main use ("test.c");
+   for Source_Dirs use (".", "headers");
+   for Object_dir use "obj";
+end Prj;
diff --git a/testsuite/tests/header_not_found/include_foo/test.c b/testsuite/tests/header_not_found/include_foo/test.c
new file mode 100644
index 000000000..5991bc01c
--- /dev/null
+++ b/testsuite/tests/header_not_found/include_foo/test.c
@@ -0,0 +1,6 @@
+#include "support.h"
+
+int main()
+{
+  return foo();
+}
diff --git a/testsuite/tests/header_not_found/test.py b/testsuite/tests/header_not_found/test.py
new file mode 100644
index 000000000..df236b173
--- /dev/null
+++ b/testsuite/tests/header_not_found/test.py
@@ -0,0 +1,46 @@
+"""
+Check that gnatcov correctly instruments an extending project when a source
+includes a header belonging to the extended project. Also check that it picks
+the version of the header that is in the ultimate extending project.
+"""
+
+import os
+
+from SCOV.minicheck import build_run_and_coverage, check_xcov_reports
+from SUITE.gprutils import GPRswitches
+from SUITE.cutils import Wdir
+from SUITE.tutils import thistest
+
+Wdir("tmp_")
+
+
+def process(gpr_dir, expected_cov):
+    gpr_obj_dir = os.path.join(gpr_dir, "obj")
+    build_run_and_coverage(
+        gprsw=GPRswitches(root_project=os.path.join(gpr_dir, "prj.gpr")),
+        covlevel="stmt",
+        mains=["test"],
+        gpr_obj_dir=gpr_obj_dir,
+        gpr_exe_dir=gpr_obj_dir,
+        extra_coverage_args=["--annotate=xcov"],
+    )
+    check_xcov_reports("*.xcov", expected_cov, gpr_obj_dir)
+
+
+# Check that the header in the extended project is picked by gnatcov instrument
+# when there is no version in the extending project.
+expected_cov = {
+    "test.c.xcov": {"+": {5}},
+    "support.h.xcov": {"+": {3}},
+}
+process(os.path.join("..", "include_base_foo"), expected_cov)
+
+# If there is a version of the header in the extending project, check that this
+# is the one picked.
+expected_cov = {
+    "test.c.xcov": {"+": {5}},
+    "support.h.xcov": {"+": {5}},
+}
+process(os.path.join("..", "include_foo"), expected_cov)
+
+thistest.result()
diff --git a/testsuite/tests/homonym-units-of-interest/prj1/pkg.c b/testsuite/tests/homonym-units-of-interest/prj1/pkg.c
new file mode 100644
index 000000000..f939f7693
--- /dev/null
+++ b/testsuite/tests/homonym-units-of-interest/prj1/pkg.c
@@ -0,0 +1,5 @@
+int
+foo (int a, int b)
+{
+  return a && b;
+}
diff --git a/testsuite/tests/homonym-units-of-interest/prj2/pkg.c b/testsuite/tests/homonym-units-of-interest/prj2/pkg.c
new file mode 100644
index 000000000..845f66891
--- /dev/null
+++ b/testsuite/tests/homonym-units-of-interest/prj2/pkg.c
@@ -0,0 +1,5 @@
+int
+bar (int a, int b)
+{
+  return a || b;
+}
diff --git a/testsuite/tests/homonym-units-of-interest/test.c b/testsuite/tests/homonym-units-of-interest/test.c
new file mode 100644
index 000000000..6d783a071
--- /dev/null
+++ b/testsuite/tests/homonym-units-of-interest/test.c
@@ -0,0 +1,10 @@
+extern int foo (int a, int b);
+extern int bar (int a, int b);
+
+int
+main ()
+{
+  foo (0, 1);
+  bar (0, 1);
+  return 0;
+}
diff --git a/testsuite/tests/homonym-units-of-interest/test.py b/testsuite/tests/homonym-units-of-interest/test.py
new file mode 100644
index 000000000..6931c25bc
--- /dev/null
+++ b/testsuite/tests/homonym-units-of-interest/test.py
@@ -0,0 +1,89 @@
+"""
+This test checks that gnatcov can deal with two homonym files belonging to
+different projects, with one of the source files being excluded from the
+coverage analysis (through project file coverage attributes) while the other
+is not.
+"""
+
+import os.path
+
+from e3.fs import sync_tree
+
+from SCOV.minicheck import build_and_run, check_xcov_reports
+from SUITE.gprutils import GPRswitches
+from SUITE.cutils import Wdir
+from SUITE.tutils import thistest, gprfor, xcov
+
+wd = Wdir('tmp_')
+
+# Run the build and run in a dedicated directory
+
+prj1 = gprfor(mains=[], prjid='prj1', srcdirs=['../prj1'], objdir='obj-prj1')
+prj2 = gprfor(mains=[], prjid='prj2', srcdirs=['../prj2'], objdir='obj-prj2')
+p = gprfor(mains=['test.c'], deps=['prj1', 'prj2'], srcdirs=['..'])
+
+xcov_args = build_and_run(
+    gprsw=GPRswitches(root_project=p),
+    covlevel='stmt+mcdc',
+    mains=['test'],
+    extra_coverage_args=['--annotate=xcov', '--output-dir=xcov'],
+)
+
+# Now onto coverage. Try to ignore pkg.c in prj1, and in prj2 in two different
+# gnatcov coverage invocations, using the project attributes as this can't be
+# specified unambiguously on the command line.
+#
+# This checks two things:
+#
+#   * a user can ignore a specific source file even if it has an homonym in
+#     the project tree. Note that the user would probably have the same
+#     project for instrument / coverage, but for testing purposes, we also
+#     need to check the second point.
+#
+#   * gnatcov instrument does not generate a single SID file for the two
+#     homonym source files.
+
+ignore_pkg = """
+package Coverage is
+   for Excluded_Units use ("pkg.c");
+end Coverage;
+"""
+
+wd.to_homedir()
+
+
+def check_report(prj_ignore, prj_of_interest):
+    """
+    Check that the report is correct when the homonym source file (pkg.c) is
+    ignored in prj_ignore and of interest in prj_of_interest.
+    """
+    subdir = f'tmp_cov_ignore_{prj_ignore}'
+    wd = Wdir(subdir)
+    sync_tree(os.path.join('..', 'tmp_'), '.')
+    gprfor(mains=[], prjid=prj_ignore, srcdirs=[f'../{prj_ignore}'],
+           objdir=f'obj-{prj_ignore}', extra=ignore_pkg)
+    xcov(xcov_args)
+
+    # The filtering of units of interest is done at a later stage for binary
+    # traces, and files that may be ignored in the coverage report can have
+    # been added to gnatcov's file table. This means that the shortest unique
+    # suffix computed from gnatcov file table to differentiate homonym source
+    # files will be different from source traces.
+    xcov_homonoym_filename = (
+        f'{prj_of_interest}-pkg.c.xcov' if thistest.options.trace_mode == 'bin'
+        else 'pkg.c.xcov'
+    )
+    check_xcov_reports(
+        '*.xcov',
+        {
+            xcov_homonoym_filename: {'!': {4}},
+            'test.c.xcov': {'+': {7, 8, 9}}
+        },
+        'xcov'
+    )
+    wd.to_homedir()
+
+
+check_report('prj1', 'prj2')
+check_report('prj2', 'prj1')
+thistest.result()
diff --git a/testsuite/tests/ignored-casing/main.adb b/testsuite/tests/ignored-casing/main.adb
new file mode 100644
index 000000000..10e85a255
--- /dev/null
+++ b/testsuite/tests/ignored-casing/main.adb
@@ -0,0 +1,6 @@
+with Pkg;
+
+procedure Main is
+begin
+   Pkg.Do_Things;
+end Main;
diff --git a/testsuite/tests/ignored-casing/pkg.adb b/testsuite/tests/ignored-casing/pkg.adb
new file mode 100644
index 000000000..39d405e46
--- /dev/null
+++ b/testsuite/tests/ignored-casing/pkg.adb
@@ -0,0 +1,10 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+package body Pkg is
+
+   procedure Do_Things is
+   begin
+      Put_Line ("Hello, world!");
+   end Do_Things;
+
+end Pkg;
diff --git a/testsuite/tests/ignored-casing/pkg.ads b/testsuite/tests/ignored-casing/pkg.ads
new file mode 100644
index 000000000..d7efa61ac
--- /dev/null
+++ b/testsuite/tests/ignored-casing/pkg.ads
@@ -0,0 +1,3 @@
+package Pkg is
+   procedure Do_Things;
+end Pkg;
diff --git a/testsuite/tests/ignored-casing/test.py b/testsuite/tests/ignored-casing/test.py
new file mode 100644
index 000000000..45bd72570
--- /dev/null
+++ b/testsuite/tests/ignored-casing/test.py
@@ -0,0 +1,48 @@
+"""
+Check that, except on Windows, the casing of glob patterns for
+--ignore-source-files is significant.
+"""
+
+from SCOV.minicheck import build_run_and_coverage, check_xcov_reports
+from SUITE.control import env
+from SUITE.cutils import Wdir
+from SUITE.gprutils import GPRswitches
+from SUITE.tutils import thistest, gprfor
+
+Wdir("tmp_")
+
+ignore_opt = "--ignore-source-files=Pkg.adb"
+expected_reports = {
+    "main.adb.xcov": {"+": {5}},
+}
+if env.build.os.name != "windows":
+    expected_reports["pkg.adb.xcov"] = {"+": {7}}
+
+gprsw = GPRswitches(root_project=gprfor(mains=["main.adb"], srcdirs=[".."]))
+
+# First check --ignore-source-files on "gnatcov coverage"
+build_run_and_coverage(
+    gprsw=gprsw,
+    covlevel="stmt",
+    mains=["main"],
+    extra_coverage_args=[
+        "--annotate=xcov", "--output-dir=out-cov", ignore_opt
+    ],
+)
+check_xcov_reports("*.xcov", expected_reports, "out-cov")
+
+# Then check it on "gnatcov instrument". This separate test makes sense as
+# --ignore-source-files exercises different code paths depending on the gnatcov
+# command.
+
+build_run_and_coverage(
+    gprsw=gprsw,
+    covlevel="stmt",
+    mains=["main"],
+    extra_instr_args=[ignore_opt],
+    extra_coverage_args=["--annotate=xcov", "--output-dir=out-instr"],
+    trace_mode="src"
+)
+check_xcov_reports("*.xcov", expected_reports, "out-instr")
+
+thistest.result()
diff --git a/testsuite/tests/instr-cov/W502-023-base64-long-lines/main.adb b/testsuite/tests/instr-cov/W502-023-base64-long-lines/main.adb
new file mode 100644
index 000000000..f5f08b178
--- /dev/null
+++ b/testsuite/tests/instr-cov/W502-023-base64-long-lines/main.adb
@@ -0,0 +1,8 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+procedure Main is
+begin
+   for I in 1 .. 10_000 loop
+      Put ((1 .. 1000 => 'A'));
+   end loop;
+end Main;
diff --git a/testsuite/tests/instr-cov/W502-023-base64-long-lines/test.py b/testsuite/tests/instr-cov/W502-023-base64-long-lines/test.py
new file mode 100644
index 000000000..d09e7ad30
--- /dev/null
+++ b/testsuite/tests/instr-cov/W502-023-base64-long-lines/test.py
@@ -0,0 +1,26 @@
+"""
+Check that "gnatcov extract-base64-trace" handles correctly input files with
+very long lines (it used to crash with a stack overflow).
+"""
+
+from SCOV.minicheck import build_run_and_coverage, check_xcov_reports
+from SUITE.cutils import Wdir
+from SUITE.gprutils import GPRswitches
+from SUITE.tutils import thistest, gprfor
+
+
+Wdir("tmp_")
+
+build_run_and_coverage(
+    gprsw=GPRswitches(root_project=gprfor(mains=["main.adb"], srcdirs=[".."])),
+    covlevel="stmt",
+    mains=["main"],
+    extra_coverage_args=["--annotate=xcov"],
+    trace_mode="src",
+    dump_trigger="main-end",
+    dump_channel="base64-stdout",
+)
+
+check_xcov_reports("*.xcov", {"main.adb.xcov": {"+": {5, 6}}}, "obj")
+
+thistest.result()
diff --git a/testsuite/tests/instr-cov/file-context/main.adb b/testsuite/tests/instr-cov/file-context/main.adb
new file mode 100644
index 000000000..1801cb505
--- /dev/null
+++ b/testsuite/tests/instr-cov/file-context/main.adb
@@ -0,0 +1,11 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+with Pkg;
+
+with Missing_A;
+with Missing_B;
+
+procedure Main is
+begin
+   Put_Line ("Hello world");
+end Main;
diff --git a/testsuite/tests/instr-cov/file-context/pkg.ads b/testsuite/tests/instr-cov/file-context/pkg.ads
new file mode 100644
index 000000000..2db1f1de6
--- /dev/null
+++ b/testsuite/tests/instr-cov/file-context/pkg.ads
@@ -0,0 +1,2 @@
+package Pkg is
+end Pkg;
diff --git a/testsuite/tests/instr-cov/file-context/test.py b/testsuite/tests/instr-cov/file-context/test.py
new file mode 100644
index 000000000..dff6fbf0e
--- /dev/null
+++ b/testsuite/tests/instr-cov/file-context/test.py
@@ -0,0 +1,68 @@
+"""
+Check that "gnatcov instrument" mentions the source file it is currently
+instrumented when it is supposed to: once per source file that triggers an
+error/warning, and once for every file in verbose mode.
+"""
+
+from e3.fs import mkdir
+
+from SCOV.instr import xcov_instrument
+from SUITE.cutils import contents_of, lines_of, Wdir
+from SUITE.gprutils import GPRswitches
+from SUITE.tutils import thistest, gprfor
+
+
+Wdir("tmp_")
+
+# Create the object direcotry to avoid spurious notices
+gprsw = GPRswitches(root_project=gprfor(mains=["main.adb"], srcdirs=[".."]))
+mkdir("obj")
+
+
+def do_instr(label, args):
+    thistest.log(f"== {label} ==")
+    output_file = f"{label}.txt"
+    p = xcov_instrument(
+        gprsw=gprsw,
+        covlevel="stmt",
+        extra_args=args,
+        out=output_file,
+        register_failure=False,
+    )
+    thistest.fail_if(
+        p.status != 0, f'{label}: "gnatcov instrument" unexpected failure'
+    )
+    return output_file
+
+# Check that we print the source file that is currently instrumented when
+# warnings/errors occur at most once, and never for other files.
+log = do_instr("basic", [])
+thistest.fail_if_not_equal(
+    f'"gnatcov instrument" output',
+    contents_of(log).strip(),
+    "\n".join([
+        "warning: While instrumenting main.adb...",
+        "warning: Cannot find required source file: missing_a.ads",
+        "warning: Cannot find required source file: missing_b.ads",
+    ]),
+)
+
+# In verbose mode: always print it once.
+log = do_instr("verbose", ["-v"])
+actual_lines = [
+    line
+    for line in lines_of(log)
+    if line.startswith("Instrumenting ") or line.startswith("warning: ")
+]
+thistest.fail_if_not_equal(
+    f'"gnatcov instrument" output',
+    "\n".join(actual_lines),
+    "\n".join([
+        "Instrumenting main.adb",
+        "warning: Cannot find required source file: missing_a.ads",
+        "warning: Cannot find required source file: missing_b.ads",
+        "Instrumenting pkg.ads",
+    ]),
+)
+
+thistest.result()
diff --git a/testsuite/tests/instr-cov/orphan-unit/main.adb b/testsuite/tests/instr-cov/orphan-unit/main.adb
new file mode 100644
index 000000000..6e16d750d
--- /dev/null
+++ b/testsuite/tests/instr-cov/orphan-unit/main.adb
@@ -0,0 +1,6 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+procedure Main is
+begin
+   Put_Line ("Hello world");
+end Main;
diff --git a/testsuite/tests/instr-cov/orphan-unit/pkg-child.adb b/testsuite/tests/instr-cov/orphan-unit/pkg-child.adb
new file mode 100644
index 000000000..8f6b24be8
--- /dev/null
+++ b/testsuite/tests/instr-cov/orphan-unit/pkg-child.adb
@@ -0,0 +1,8 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+package body Pkg.Child is
+   procedure Do_Things is
+   begin
+      Put_Line ("Doing things");
+   end Do_Things;
+end Pkg.Child;
diff --git a/testsuite/tests/instr-cov/orphan-unit/pkg-child.ads b/testsuite/tests/instr-cov/orphan-unit/pkg-child.ads
new file mode 100644
index 000000000..d3df3d14a
--- /dev/null
+++ b/testsuite/tests/instr-cov/orphan-unit/pkg-child.ads
@@ -0,0 +1,3 @@
+package Pkg.Child is
+   procedure Do_Things;
+end Pkg.Child;
diff --git a/testsuite/tests/instr-cov/orphan-unit/test.py b/testsuite/tests/instr-cov/orphan-unit/test.py
new file mode 100644
index 000000000..77787d683
--- /dev/null
+++ b/testsuite/tests/instr-cov/orphan-unit/test.py
@@ -0,0 +1,38 @@
+"""
+Check that instrumenting a project that contains an "orphan" unit (i.e. a
+source file present in the project but not used in the build and not
+compilable) works as expected.
+"""
+
+from SCOV.minicheck import build_run_and_coverage, check_xcov_reports
+from SUITE.cutils import contents_of, Wdir
+from SUITE.gprutils import GPRswitches
+from SUITE.tutils import thistest, gprfor
+
+Wdir("tmp_")
+
+expected_cov = {"main.adb.xcov": {"+": {5}}}
+if thistest.options.trace_mode == "src":
+    expected_cov["pkg-child.adb.xcov"] = {"-": {6}}
+
+build_run_and_coverage(
+    gprsw=GPRswitches(root_project=gprfor(mains=["main.adb"], srcdirs=[".."])),
+    covlevel="stmt",
+    mains=["main"],
+    instrument_warnings_as_errors=False,
+    extra_coverage_args=["--annotate=xcov"],
+)
+
+if thistest.options.trace_mode == "src":
+    thistest.fail_if_not_equal(
+        '"gnatcov instrument" output',
+        (
+            "warning: While instrumenting pkg-child.adb...\n"
+            "warning: Cannot find required source file: pkg.ads"
+        ),
+        contents_of("instrument.log").strip(),
+    )
+
+check_xcov_reports("*.xcov", expected_cov, "obj")
+
+thistest.result()
diff --git a/tools/gnatcov/annotations-report.adb b/tools/gnatcov/annotations-report.adb
index 698c77f72..dd36e7b51 100644
--- a/tools/gnatcov/annotations-report.adb
+++ b/tools/gnatcov/annotations-report.adb
@@ -29,6 +29,7 @@ with Coverage.Source;  use Coverage.Source;
 with Coverage.Tags;    use Coverage.Tags;
 with Files_Table;
 with SC_Obligations;
+with Project;          use Project;
 with Switches;
 with Traces_Files;     use Traces_Files;
 
@@ -644,19 +645,17 @@ package body Annotations.Report is
          New_Line (Output.all);
          declare
 
-            procedure Print_Ignored_File
-              (FI : Files_Table.File_Info);
+            procedure Print_Ignored_File (FI : Files_Table.File_Info);
             --  Print the name of the file and its ignore status on the report
 
-            procedure Print_Unit_Name (Name : String);
+            procedure Print_Unit_Name (Unit : Project_Unit);
             --  Print Name on the report
 
             ------------------------
             -- Print_Ignored_File --
             ------------------------
 
-            procedure Print_Ignored_File
-              (FI : Files_Table.File_Info) is
+            procedure Print_Ignored_File (FI : Files_Table.File_Info) is
             begin
                if FI.Ignore_Status = Files_Table.Sometimes then
                   Put_Line (Output.all,
@@ -671,9 +670,9 @@ package body Annotations.Report is
             -- Print_Unit_Name --
             ---------------------
 
-            procedure Print_Unit_Name (Name : String) is
+            procedure Print_Unit_Name (Unit : Project_Unit) is
             begin
-               Put_Line (Output.all, Name);
+               Put_Line (Output.all, +Unit.Unit_Name);
             end Print_Unit_Name;
          begin
             Iterate_On_Unit_List
diff --git a/tools/gnatcov/annotations-xcov.adb b/tools/gnatcov/annotations-xcov.adb
index a0378baf7..92700a6c1 100644
--- a/tools/gnatcov/annotations-xcov.adb
+++ b/tools/gnatcov/annotations-xcov.adb
@@ -88,7 +88,8 @@ package body Annotations.Xcov is
       Annotations.Generate_Report
         (Pp,
          Show_Details,
-         Subdir => (if Pp.Show_Details then "xcov+" else "xcov"));
+         Subdir        => (if Pp.Show_Details then "xcov+" else "xcov"),
+         Clean_Pattern => "*.xcov");
    end Generate_Report;
 
    ---------------------------
diff --git a/tools/gnatcov/annotations.adb b/tools/gnatcov/annotations.adb
index 23a4684ef..f8630dbfc 100644
--- a/tools/gnatcov/annotations.adb
+++ b/tools/gnatcov/annotations.adb
@@ -384,9 +384,10 @@ package body Annotations is
    ---------------------
 
    procedure Generate_Report
-     (Pp           : in out Pretty_Printer'Class;
-      Show_Details : Boolean;
-      Subdir       : String := "")
+     (Pp            : in out Pretty_Printer'Class;
+      Show_Details  : Boolean;
+      Subdir        : String := "";
+      Clean_Pattern : String := No_Cleaning)
    is
       use Coverage;
 
@@ -500,12 +501,25 @@ package body Annotations is
 
    begin
 
-      --  Switch the output dir to produce the report content in the requested
-      --  subdir, if needed.
+      if Clean_Pattern /= No_Cleaning then
+         Clean_Dir (Get_Output_Dir, Clean_Pattern);
+      end if;
 
-      if Subdir'Length > 0 and then Multiple_Reports then
-         Set_Output_Dir (Previous_Output_Dir & GNAT.OS_Lib.Directory_Separator
-                         & Subdir);
+      --  Clean the output subdir in all cases, and then switch to that subdir
+      --  to generate the report, if needed.
+
+      if Subdir'Length > 0 then
+         declare
+            Output_Subdir : constant String :=
+              Previous_Output_Dir & GNAT.OS_Lib.Directory_Separator & Subdir;
+         begin
+            if Clean_Pattern /= No_Cleaning then
+               Clean_Dir (Output_Subdir, Clean_Pattern);
+            end if;
+            if Multiple_Reports then
+               Set_Output_Dir (Output_Subdir);
+            end if;
+         end;
       end if;
 
       Pp.Show_Details := Show_Details;
@@ -690,15 +704,17 @@ package body Annotations is
          declare
             SFI : constant Source_File_Index := Sloc_Start.Source_File;
 
-            Info                  : constant PP_Info := Get_PP_Info (SCO);
-            Preprocessed_Filename : constant String := Get_PP_Filename (SFI);
-            Preprocessed_SFI      : Source_File_Index :=
+            Info                   : constant PP_Info := Get_PP_Info (SCO);
+            Preprocessed_Filename  : constant String :=
+              Get_PP_Filename (SFI) & ".prepro";
+            Postprocessed_Filename : constant String := Get_PP_Filename (SFI);
+            Postprocessed_SFI      : Source_File_Index :=
               Get_Index_From_Generic_Name
-                (Preprocessed_Filename,
+                (Postprocessed_Filename,
                  Source_File, Insert => False);
 
          begin
-            if Preprocessed_SFI = No_Source_File then
+            if Postprocessed_SFI = No_Source_File then
                declare
                   use Instrument.Base_Types;
                   use Subprocesses;
@@ -709,13 +725,22 @@ package body Annotations is
                        Run_Command
                          (PP_Cmds.Element (SFI), "Preprocessing",
                           Preprocessed_Filename,
-                          Err_To_Out => False,
+                          Err_To_Out   => False,
                           Ignore_Error => True);
 
                      if Preprocessed then
-                        Preprocessed_SFI :=
+                        --  As a reminder, we compute source locations from
+                        --  preprocessed sources with redundant line markers
+                        --  removed. This means that the preprocessed code
+                        --  locations refer to the latter version, which we
+                        --  need to recompute there to have the right version
+                        --  of the source.
+
+                        Postprocess_Source
+                          (Preprocessed_Filename, Postprocessed_Filename);
+                        Postprocessed_SFI :=
                           Get_Index_From_Generic_Name
-                            (Preprocessed_Filename,
+                            (Postprocessed_Filename,
                              Source_File,
                              Insert_After_Freeze => True);
                      else
@@ -729,10 +754,10 @@ package body Annotations is
                end;
             end if;
 
-            if Preprocessed_SFI /= No_Source_File then
-               Sloc_Start.Source_File := Preprocessed_SFI;
+            if Postprocessed_SFI /= No_Source_File then
+               Sloc_Start.Source_File := Postprocessed_SFI;
                Sloc_Start.L := Info.PP_Source_Range.First_Sloc;
-               Sloc_End.Source_File := Preprocessed_SFI;
+               Sloc_End.Source_File := Postprocessed_SFI;
                Sloc_End.L := Info.PP_Source_Range.Last_Sloc;
             end if;
          end;
diff --git a/tools/gnatcov/annotations.ads b/tools/gnatcov/annotations.ads
index 96ca28938..afa5ee718 100644
--- a/tools/gnatcov/annotations.ads
+++ b/tools/gnatcov/annotations.ads
@@ -197,15 +197,25 @@ private
       Scope_Ent : Scope_Entity) is null;
    --  Let Pp print the given body entity
 
+   No_Cleaning : constant String := "DO NOT CLEAN";
+   --  Constant used to indicate that we should not attempt to clean the output
+   --  dir prior to emitting the report. This is used instead of the empty
+   --  string as the empty string pattern can be used as a cleaning pattern
+   --  that will match every file in the report directory. We should probably
+   --  never need to clean a report dir with the above string as a pattern.
+
    procedure Generate_Report
-     (Pp           : in out Pretty_Printer'Class;
-      Show_Details : Boolean;
-      Subdir       : String := "");
+     (Pp            : in out Pretty_Printer'Class;
+      Show_Details  : Boolean;
+      Subdir        : String := "";
+      Clean_Pattern : String := No_Cleaning);
    --  Let Pp generate the annotated sources. If Show_Details is False, only
    --  a line state will be displayed. If Show_Details is True, a justification
    --  is associated to this line state. If Subdir is not empty, this will be
    --  subdirectory in which the report will be created if multiple reports are
-   --  requested.
+   --  requested. If Clean_Pattern is not No_Cleaning, all the files matching
+   --  Clean_Pattern in Output_Dir as well as in Output_Dir/Subdir will be
+   --  removed.
 
    function Aggregated_State
      (Info              : Line_Info;
diff --git a/tools/gnatcov/bfd_helpers.c b/tools/gnatcov/bfd_helpers.c
index 5828f9203..0f5bfa617 100644
--- a/tools/gnatcov/bfd_helpers.c
+++ b/tools/gnatcov/bfd_helpers.c
@@ -23,10 +23,21 @@
 #include <bfd.h>
 #include <stdio.h>
 
-#define CHECK(expr, call) do { if (!(expr)) { (*warn_cb)(call " failed", bfd_errmsg(bfd_get_error())); return; } } while (0)
+#define CHECK(expr, call)                                                     \
+  do                                                                          \
+    {                                                                         \
+      if (!(expr))                                                            \
+	{                                                                     \
+	  (*warn_cb) (call " failed", bfd_errmsg (bfd_get_error ()));         \
+	  return;                                                             \
+	}                                                                     \
+    }                                                                         \
+  while (0)
 
 void
-_gnatcov_for_each_synthetic_symbol(char *fn, void (*cb)(const char *, long unsigned int), void (*warn_cb)(const char *, const char *))
+_gnatcov_for_each_synthetic_symbol (
+  char *fn, void (*cb) (const char *, long unsigned int),
+  void (*warn_cb) (const char *, const char *))
 {
   bfd *abfd;
   asymbol **dyn_syms = NULL, *synthsyms = NULL;
@@ -36,49 +47,54 @@ _gnatcov_for_each_synthetic_symbol(char *fn, void (*cb)(const char *, long unsig
   int j;
   symbol_info si;
 
-  abfd = bfd_openr(fn, "default");
-  CHECK(abfd != NULL, "bfd_openr");
+  abfd = bfd_openr (fn, "default");
+  CHECK (abfd != NULL, "bfd_openr");
 
-  bfd_check_format(abfd, bfd_object);
+  bfd_check_format (abfd, bfd_object);
 
-  storage = bfd_get_dynamic_symtab_upper_bound(abfd);
-  CHECK(storage >= 0, "bfd_get_dynamic_symtab_upper_bound");
+  storage = bfd_get_dynamic_symtab_upper_bound (abfd);
+  CHECK (storage >= 0, "bfd_get_dynamic_symtab_upper_bound");
 
   if (storage == 0)
     /* No dynamic symbols, nothing to do.  */
     return;
 
-  dyn_syms = (asymbol **) bfd_alloc(abfd, storage);
-  CHECK(dyn_syms != NULL, "bfd_alloc");
+  dyn_syms = (asymbol **) bfd_alloc (abfd, storage);
+  CHECK (dyn_syms != NULL, "bfd_alloc");
 
-  dyn_count = bfd_canonicalize_dynamic_symtab(abfd, dyn_syms);
+  dyn_count = bfd_canonicalize_dynamic_symtab (abfd, dyn_syms);
   if (dyn_count <= 0)
     /* No dynamic symbols, nothing to do.  */
     return;
 
-  synth_count = bfd_get_synthetic_symtab
-                  (abfd, /*static_count*/ 0, /*static_syms*/ NULL,
-                   dyn_count, dyn_syms, &synthsyms);
+  synth_count = bfd_get_synthetic_symtab (abfd, /*static_count*/ 0,
+					  /*static_syms*/ NULL, dyn_count,
+					  dyn_syms, &synthsyms);
 
   /* Note: synth_count is either -1 or >0, never 0.  There is no way
      distinguish an error condition from the mere absence of synthetic
      symbols.  */
 
-  for (j = 0; j < synth_count; j++) {
-    bfd_symbol_info(&synthsyms[j], &si);
-    (*cb) (si.name, si.value);
-  }
+  for (j = 0; j < synth_count; j++)
+    {
+      bfd_symbol_info (&synthsyms[j], &si);
+      (*cb) (si.name, si.value);
+    }
 
-  bfd_close(abfd);
+  bfd_close (abfd);
 }
 
 #ifdef BFD_HELPERS_TEST
-void print_cb(const char *name, unsigned long int val) {
-  printf("%016lx %s\n", val, name);
+void
+print_cb (const char *name, unsigned long int val)
+{
+  printf ("%016lx %s\n", val, name);
 }
 
-int main(int argc, char **argv) {
-  bfd_init();
-  _gnatcov_for_each_synthetic_symbol(argv[1], print_cb);
+int
+main (int argc, char **argv)
+{
+  bfd_init ();
+  _gnatcov_for_each_synthetic_symbol (argv[1], print_cb);
 }
 #endif
diff --git a/tools/gnatcov/checkpoints.ads b/tools/gnatcov/checkpoints.ads
index af4b5e497..3384f0c66 100644
--- a/tools/gnatcov/checkpoints.ads
+++ b/tools/gnatcov/checkpoints.ads
@@ -32,7 +32,7 @@ with SC_Obligations; use SC_Obligations;
 
 package Checkpoints is
 
-   subtype Checkpoint_Version is Interfaces.Unsigned_32 range 1 .. 10;
+   subtype Checkpoint_Version is Interfaces.Unsigned_32 range 1 .. 12;
    --  For compatibility with previous Gnatcov versions, the checkpoint
    --  file format is versioned.
    --
@@ -47,6 +47,8 @@ package Checkpoints is
    --  8  -- Add macro expansion information
    --  9  -- Add the support for scoped metrics
    --  10 -- Add non instrumented SCOs sets
+   --  11 -- fingerprints for buffer bit maps
+   --  12 -- Extend Unit_List to distinguish homonym source files
    --
    --  Note that we always use the last version when creating a checkpoint.
 
diff --git a/tools/gnatcov/clang-extensions.adb b/tools/gnatcov/clang-extensions.adb
index 9e5b96687..c3b4aa01e 100644
--- a/tools/gnatcov/clang-extensions.adb
+++ b/tools/gnatcov/clang-extensions.adb
@@ -22,8 +22,41 @@
 
 with Clang.CX_String; use Clang.CX_String;
 
+with Files_Table; use Files_Table;
+
 package body Clang.Extensions is
 
+   function To_Sloc (Line, Column : unsigned) return Local_Source_Location is
+     ((Natural (Line), Natural (Column)));
+   --  Convert a Clang local source location to gnatcov's own format
+
+   function To_Sloc
+     (File : File_T; Line, Column : unsigned) return Source_Location
+   is
+     ((Source_File => Get_Index_From_Generic_Name
+                        (Name => Get_File_Name (File),
+                         Kind => Source_File),
+       L           =>  To_Sloc (Line, Column)));
+   --  Convert a Clang source location to gnatcov's own format
+
+   -----------------------
+   -- Get_Decl_Name_Str --
+   -----------------------
+
+   function Get_Decl_Name_Str (C : Cursor_T) return String is
+
+      function Get_Decl_Name_Str_C (C : Cursor_T) return String_T
+        with
+          Import, Convention => C,
+          External_Name => "clang_getDeclName";
+
+      DeclName_Str_C : constant String_T := Get_Decl_Name_Str_C (C);
+      DeclName       : constant String   := Get_C_String (DeclName_Str_C);
+   begin
+      Dispose_String (DeclName_Str_C);
+      return DeclName;
+   end Get_Decl_Name_Str;
+
    --------------------
    -- Get_Opcode_Str --
    --------------------
@@ -82,6 +115,70 @@ package body Clang.Extensions is
       CX_Rewriter_Insert_Text_After_Token_C (Rew, Loc, Insert & ASCII.NUL);
    end CX_Rewriter_Insert_Text_After_Token;
 
+   ------------------------------------------
+   -- CX_Rewriter_Insert_Text_Before_Token --
+   ------------------------------------------
+
+   procedure CX_Rewriter_Insert_Text_Before_Token
+     (Rew    : Rewriter_T;
+      Loc    : Source_Location_T;
+      Insert : String)
+   is
+      procedure CX_Rewriter_Insert_Text_Before_Token_C
+        (Rew    : Rewriter_T;
+         Loc    : Source_Location_T;
+         Insert : String)
+        with
+          Import, Convention => C,
+          External_Name => "clang_CXRewriter_insertTextBeforeToken";
+   begin
+      CX_Rewriter_Insert_Text_Before_Token_C (Rew, Loc, Insert & ASCII.NUL);
+   end CX_Rewriter_Insert_Text_Before_Token;
+
+   -----------------------
+   -- Spelling_Location --
+   -----------------------
+
+   function Spelling_Location (Loc : Source_Location_T) return Source_Location
+   is
+      File                 : File_T;
+      Line, Column, Offset : aliased unsigned;
+   begin
+      Get_Spelling_Location
+        (Loc, File'Address, Line'Access, Column'Access, Offset'Access);
+
+      return To_Sloc (File, Line, Column);
+   end Spelling_Location;
+
+   -------------------
+   -- File_Location --
+   -------------------
+
+   function File_Location
+     (Loc : Source_Location_T) return Local_Source_Location
+   is
+      File                 : File_T;
+      Line, Column, Offset : aliased unsigned;
+   begin
+      Get_File_Location
+        (Loc, File'Address, Line'Access, Column'Access, Offset'Access);
+      return To_Sloc (Line, Column);
+   end File_Location;
+
+   -----------------------
+   -- Presumed_Location --
+   -----------------------
+
+   function Presumed_Location
+     (Loc : Source_Location_T) return Local_Source_Location
+   is
+      Filename     : aliased String_T;
+      Line, Column : aliased unsigned;
+   begin
+      Get_Presumed_Location (Loc, Filename'Access, Line'Access, Column'Access);
+      return To_Sloc (Line, Column);
+   end Presumed_Location;
+
    -----------------------
    -- Is_Macro_Location --
    -----------------------
@@ -96,6 +193,32 @@ package body Clang.Extensions is
       return Is_Macro_Location_C (Loc) /= 0;
    end Is_Macro_Location;
 
+   ----------------------------
+   -- Is_Macro_Arg_Expansion --
+   ----------------------------
+
+   function Is_Macro_Arg_Expansion
+     (Loc       : Source_Location_T;
+      Start_Loc : out Source_Location_T;
+      TU        : Translation_Unit_T) return Boolean
+   is
+      function Is_Macro_Arg_Expansion
+        (Loc       : Source_Location_T;
+         Start_Loc : access Source_Location_T;
+         TU        : Translation_Unit_T) return unsigned
+        with
+          Import, Convention => C,
+          External_Name      => "clang_isMacroArgExpansion";
+
+      C_Start_Loc : aliased Source_Location_T;
+   begin
+      return Result : constant Boolean :=
+        Is_Macro_Arg_Expansion (Loc, C_Start_Loc'Access, TU) /= 0
+      do
+         Start_Loc := C_Start_Loc;
+      end return;
+   end Is_Macro_Arg_Expansion;
+
    ----------------------------------------------
    -- Get_Immediate_Macro_Name_For_Diagnostics --
    ----------------------------------------------
@@ -120,24 +243,4 @@ package body Clang.Extensions is
       return Macro_Name;
    end Get_Immediate_Macro_Name_For_Diagnostics;
 
-   ----------------------------
-   -- Is_Macro_Arg_Expansion --
-   ----------------------------
-
-   function Is_Macro_Arg_Expansion
-     (Loc       : Source_Location_T;
-      Start_Loc : access Source_Location_T := null;
-      TU        : Translation_Unit_T) return Boolean
-   is
-      function Is_Macro_Arg_Expansion
-        (Loc       : Source_Location_T;
-         Start_Loc : access Source_Location_T;
-         TU        : Translation_Unit_T) return unsigned
-        with
-          Import, Convention => C,
-          External_Name      => "clang_isMacroArgExpansion";
-   begin
-      return Is_Macro_Arg_Expansion (Loc, Start_Loc, TU) /= 0;
-   end Is_Macro_Arg_Expansion;
-
 end Clang.Extensions;
diff --git a/tools/gnatcov/clang-extensions.ads b/tools/gnatcov/clang-extensions.ads
index 671c7eeda..6362c1c77 100644
--- a/tools/gnatcov/clang-extensions.ads
+++ b/tools/gnatcov/clang-extensions.ads
@@ -25,6 +25,8 @@ with Interfaces.C; use Interfaces.C;
 with Clang.Index;   use Clang.Index;
 with Clang.Rewrite; use Clang.Rewrite;
 
+with Slocs; use Slocs;
+
 package Clang.Extensions is
 
    function Get_Body (C : Cursor_T) return Cursor_T
@@ -54,6 +56,16 @@ package Clang.Extensions is
    function Get_Else (C : Cursor_T) return Cursor_T
      with Import, Convention => C, External_Name => "clang_getElse";
 
+   function Get_Else_Loc (C : Cursor_T) return Source_Location_T
+     with Import, Convention => C, External_Name => "clang_getElseLoc";
+   --  If this cursor is an if statement with an else part, return the
+   --  else location.Otherwise, return a null location.
+
+   function Get_While_Loc (C : Cursor_T) return Source_Location_T
+     with Import, Convention => C, External_Name => "clang_getWhileLoc";
+   --  If this cursor is a do / while statement, return the while location.
+   --  Otherwise return a null location.
+
    function Get_Sub_Expr (C : Cursor_T) return Cursor_T
      with Import, Convention => C, External_Name => "clang_getSubExpr";
 
@@ -71,6 +83,8 @@ package Clang.Extensions is
 
    function Get_Opcode_Str (C : Cursor_T) return String with Inline;
 
+   function Get_Decl_Name_Str (C : Cursor_T) return String with Inline;
+
    function Unwrap (C : Cursor_T) return Cursor_T
      with Import, Convention => C, External_Name => "clang_unwrap";
 
@@ -94,13 +108,21 @@ package Clang.Extensions is
    --  previously inserted string (at the same location).
 
    procedure CX_Rewriter_Insert_Text_After_Token
-     (Rew : Rewriter_T;
-      Loc : Source_Location_T;
+     (Rew    : Rewriter_T;
+      Loc    : Source_Location_T;
       Insert : String)
      with Inline;
    --  Insert the text Insert after the token at the given location, and after
    --  any previously inserted string (at the same location).
 
+   procedure CX_Rewriter_Insert_Text_Before_Token
+     (Rew    : Rewriter_T;
+      Loc    : Source_Location_T;
+      Insert : String)
+     with Inline;
+   --  Insert the text Insert before the token at the given location, and after
+   --  any previously inserted string (at the same location).
+
    function Get_Cursor_TU (C : Cursor_T) return Translation_Unit_T
    with
      Import, Convention => C,
@@ -114,13 +136,26 @@ package Clang.Extensions is
    --  The functions below are simply bindings around clang functions, which
    --  are exhaustively documented in the clang sources.
 
+   function Spelling_Location (Loc : Source_Location_T) return Source_Location
+     with Inline;
+   --  Thick binding for Clang.Index.Get_Spelling_Location
+
+   function File_Location
+     (Loc : Source_Location_T) return Local_Source_Location
+     with Inline;
+   --  Thick binding for Clang.Index.Get_File_Location
+
+   function Presumed_Location
+     (Loc : Source_Location_T) return Local_Source_Location;
+   --  Thick binding for Clang.Index.Get Presumed_Location
+
    function Is_Macro_Location (Loc : Source_Location_T) return Boolean
      with Inline;
    --  See isMacroID in clang/Basic/SourceLocation.h.
 
    function Is_Macro_Arg_Expansion
      (Loc       : Source_Location_T;
-      Start_Loc : access Source_Location_T := null;
+      Start_Loc : out Source_Location_T;
       TU        : Translation_Unit_T) return Boolean
       with Inline;
    --  See isMacroArgExpansion in clang/Basic/SourceManager.h.
diff --git a/tools/gnatcov/clang-wrapper.cc b/tools/gnatcov/clang-wrapper.cc
index db6ce156b..f969308c1 100644
--- a/tools/gnatcov/clang-wrapper.cc
+++ b/tools/gnatcov/clang-wrapper.cc
@@ -46,6 +46,39 @@ using namespace clang::cxloc;
 using namespace clang::cxstring;
 using namespace clang::cxtu;
 
+/* Return the AST context corresponding to the given translation unit TU.  */
+
+static ASTContext &
+getContext (CXTranslationUnit TU)
+{
+  return getASTUnit (TU)->getASTContext ();
+}
+
+/* Likewise, but starting from a cursor.  */
+
+static ASTContext &
+getContext (CXCursor C)
+{
+  return getContext (getCursorTU (C));
+}
+
+/* Return the SourceManager corresponding to the given translation unit TU.  */
+
+static const SourceManager &
+getSourceManager (CXTranslationUnit TU)
+{
+  return getASTUnit (TU)->getSourceManager ();
+}
+
+/* Translate a source location to a cursor source location in the given
+   translation unit TU.  */
+
+static CXSourceLocation
+translateSLoc (CXTranslationUnit TU, SourceLocation Loc)
+{
+  return translateSourceLocation (getContext (TU), Loc);
+}
+
 /* Convert a clang Stmt type to a libclang CXCursor structure.  The CXCursor C
    is simply used to get a relevant declaration and translation unit to tie
    the returned cursor to.  */
@@ -79,25 +112,25 @@ extern "C" CXCursor
 clang_getCond (CXCursor C)
 {
   if (clang_isStatement (C.kind) || clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::IfStmtClass:
-          return MakeCXCursorWithNull (cast<IfStmt> (S)->getCond (), C);
-        case Stmt::WhileStmtClass:
-          return MakeCXCursorWithNull (cast<WhileStmt> (S)->getCond (), C);
-        case Stmt::ForStmtClass:
-          return MakeCXCursorWithNull (cast<ForStmt> (S)->getCond (), C);
-        case Stmt::SwitchStmtClass:
-          return MakeCXCursorWithNull (cast<SwitchStmt> (S)->getCond (), C);
-        case Stmt::DoStmtClass:
-          return MakeCXCursorWithNull (cast<DoStmt> (S)->getCond (), C);
-        case Stmt::ConditionalOperatorClass:
-          return MakeCXCursorWithNull (
-              cast<ConditionalOperator> (S)->getCond (), C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::IfStmtClass:
+	  return MakeCXCursorWithNull (cast<IfStmt> (S)->getCond (), C);
+	case Stmt::WhileStmtClass:
+	  return MakeCXCursorWithNull (cast<WhileStmt> (S)->getCond (), C);
+	case Stmt::ForStmtClass:
+	  return MakeCXCursorWithNull (cast<ForStmt> (S)->getCond (), C);
+	case Stmt::SwitchStmtClass:
+	  return MakeCXCursorWithNull (cast<SwitchStmt> (S)->getCond (), C);
+	case Stmt::DoStmtClass:
+	  return MakeCXCursorWithNull (cast<DoStmt> (S)->getCond (), C);
+	case Stmt::ConditionalOperatorClass:
+	  return MakeCXCursorWithNull (
+	    cast<ConditionalOperator> (S)->getCond (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -106,57 +139,57 @@ clang_getBody (CXCursor C)
 {
   if (clang_isDeclaration (C.kind))
     {
-      if (const Decl *D = cxcursor::getCursorDecl (C))
-        switch (D->getKind ())
-          {
-          case Decl::FunctionTemplate:
-            return MakeCXCursorWithNull (
-              cast<FunctionTemplateDecl> (D)->getTemplatedDecl ()->getBody (),
-              C);
-          case Decl::Function:
-          case Decl::CXXMethod:
-          case Decl::CXXConstructor:
-          case Decl::CXXDestructor:
-          case Decl::CXXConversion:
-            {
-              const FunctionDecl *FD = cast<FunctionDecl> (D);
-              if (FD->doesThisDeclarationHaveABody ())
-                return MakeCXCursorWithNull (FD->getBody (), C);
-              break;
-            }
-          default:
-            return MakeCXCursorWithNull (D->getBody (), C);
-          }
+      if (const Decl *D = getCursorDecl (C))
+	switch (D->getKind ())
+	  {
+	  case Decl::FunctionTemplate:
+	    return MakeCXCursorWithNull (
+	      cast<FunctionTemplateDecl> (D)->getTemplatedDecl ()->getBody (),
+	      C);
+	  case Decl::Function:
+	  case Decl::CXXMethod:
+	  case Decl::CXXConstructor:
+	  case Decl::CXXDestructor:
+	  case Decl::CXXConversion:
+	    {
+	      const FunctionDecl *FD = cast<FunctionDecl> (D);
+	      if (FD->doesThisDeclarationHaveABody ())
+		return MakeCXCursorWithNull (FD->getBody (), C);
+	      break;
+	    }
+	  default:
+	    return MakeCXCursorWithNull (D->getBody (), C);
+	  }
     }
   else if (clang_isStatement (C.kind))
     {
-      if (const Stmt *S = cxcursor::getCursorStmt (C))
-        switch (S->getStmtClass ())
-          {
-            case Stmt::WhileStmtClass:
-              return MakeCXCursorWithNull (cast<WhileStmt> (S)->getBody (), C);
-            case Stmt::ForStmtClass:
-              return MakeCXCursorWithNull (cast<ForStmt> (S)->getBody (), C);
-            case Stmt::CXXForRangeStmtClass:
-              return MakeCXCursorWithNull
-                (cast<CXXForRangeStmt> (S)->getBody (), C);
-            case Stmt::DoStmtClass:
-              return MakeCXCursorWithNull (cast<DoStmt> (S)->getBody (), C);
-            case Stmt::SwitchStmtClass:
-              return MakeCXCursorWithNull (cast<SwitchStmt> (S)->getBody (), C);
-            default:
-              return clang_getNullCursor ();
-          }
+      if (const Stmt *S = getCursorStmt (C))
+	switch (S->getStmtClass ())
+	  {
+	  case Stmt::WhileStmtClass:
+	    return MakeCXCursorWithNull (cast<WhileStmt> (S)->getBody (), C);
+	  case Stmt::ForStmtClass:
+	    return MakeCXCursorWithNull (cast<ForStmt> (S)->getBody (), C);
+	  case Stmt::CXXForRangeStmtClass:
+	    return MakeCXCursorWithNull (cast<CXXForRangeStmt> (S)->getBody (),
+					 C);
+	  case Stmt::DoStmtClass:
+	    return MakeCXCursorWithNull (cast<DoStmt> (S)->getBody (), C);
+	  case Stmt::SwitchStmtClass:
+	    return MakeCXCursorWithNull (cast<SwitchStmt> (S)->getBody (), C);
+	  default:
+	    return clang_getNullCursor ();
+	  }
     }
   else if (clang_isExpression (C.kind))
-    if (const Expr *E = cxcursor::getCursorExpr (C))
+    if (const Expr *E = getCursorExpr (C))
       switch (E->getStmtClass ())
-        {
-          case Expr::LambdaExprClass:
-            return MakeCXCursorWithNull (cast<LambdaExpr> (E)->getBody (), C);
-          default:
-            return clang_getNullCursor ();
-        }
+	{
+	case Expr::LambdaExprClass:
+	  return MakeCXCursorWithNull (cast<LambdaExpr> (E)->getBody (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -164,17 +197,17 @@ extern "C" CXCursor
 clang_getForInit (CXCursor C)
 {
   if (clang_isStatement (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::ForStmtClass:
-          return MakeCXCursorWithNull (cast<ForStmt> (S)->getInit (), C);
-        case Stmt::CXXForRangeStmtClass:
-          return MakeCXCursorWithNull (cast<CXXForRangeStmt> (S)->getInit (),
-                                       C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::ForStmtClass:
+	  return MakeCXCursorWithNull (cast<ForStmt> (S)->getInit (), C);
+	case Stmt::CXXForRangeStmtClass:
+	  return MakeCXCursorWithNull (cast<CXXForRangeStmt> (S)->getInit (),
+				       C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -184,8 +217,7 @@ clang_getForRangeExpr (CXCursor C)
   const Stmt *stmt;
   const CXXForRangeStmt *for_stmt;
 
-  if (clang_isStatement (C.kind)
-      && (stmt = cxcursor::getCursorStmt (C))
+  if (clang_isStatement (C.kind) && (stmt = cxcursor::getCursorStmt (C))
       && stmt->getStmtClass () == Stmt::CXXForRangeStmtClass)
     {
       for_stmt = cast<CXXForRangeStmt> (stmt);
@@ -198,14 +230,14 @@ extern "C" CXCursor
 clang_getForInc (CXCursor C)
 {
   if (clang_isStatement (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::ForStmtClass:
-          return MakeCXCursorWithNull (cast<ForStmt> (S)->getInc (), C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::ForStmtClass:
+	  return MakeCXCursorWithNull (cast<ForStmt> (S)->getInc (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -216,13 +248,12 @@ clang_getCondVar (CXCursor C)
   const WhileStmt *while_stmt;
   const VarDecl *decl;
 
-  if (clang_isStatement (C.kind)
-      && (stmt = cxcursor::getCursorStmt (C))
+  if (clang_isStatement (C.kind) && (stmt = getCursorStmt (C))
       && stmt->getStmtClass () == Stmt::WhileStmtClass)
     {
       while_stmt = cast<WhileStmt> (stmt);
       if (decl = while_stmt->getConditionVariable ())
-        return MakeCXCursorWithNull (decl, C);
+	return MakeCXCursorWithNull (decl, C);
     }
   return clang_getNullCursor ();
 }
@@ -233,8 +264,7 @@ clang_getVarInitExpr (CXCursor C)
   const Decl *decl;
   const VarDecl *var_decl;
 
-  if (clang_isDeclaration (C.kind)
-      && (decl = cxcursor::getCursorDecl (C))
+  if (clang_isDeclaration (C.kind) && (decl = getCursorDecl (C))
       && decl->getKind () == Decl::Kind::Var)
     {
       var_decl = cast<VarDecl> (decl);
@@ -247,14 +277,14 @@ extern "C" CXCursor
 clang_getThen (CXCursor C)
 {
   if (clang_isStatement (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::IfStmtClass:
-          return MakeCXCursorWithNull (cast<IfStmt> (S)->getThen (), C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::IfStmtClass:
+	  return MakeCXCursorWithNull (cast<IfStmt> (S)->getThen (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -262,30 +292,61 @@ extern "C" CXCursor
 clang_getElse (CXCursor C)
 {
   if (clang_isStatement (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::IfStmtClass:
-          return MakeCXCursorWithNull (cast<IfStmt> (S)->getElse (), C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::IfStmtClass:
+	  return MakeCXCursorWithNull (cast<IfStmt> (S)->getElse (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
+extern "C" CXSourceLocation
+clang_getElseLoc (CXCursor C)
+{
+  if (clang_isStatement (C.kind))
+    if (const Stmt *S = getCursorStmt (C))
+      switch (S->getStmtClass ())
+	{
+	case Stmt::IfStmtClass:
+	  return translateSLoc (getCursorTU (C), cast<IfStmt> (S)->getElseLoc ());
+	default:
+	  return clang_getNullLocation ();
+	}
+  return clang_getNullLocation ();
+}
+
+extern "C" CXSourceLocation
+clang_getWhileLoc (CXCursor C)
+{
+  if (clang_isStatement (C.kind))
+    if (const Stmt *S = getCursorStmt (C))
+      switch (S->getStmtClass ())
+	{
+	case Stmt::DoStmtClass:
+	  return translateSLoc (getCursorTU (C), cast<DoStmt> (S)->getWhileLoc ());
+	default:
+	  return clang_getNullLocation ();
+	}
+  return clang_getNullLocation ();
+}
+
+
 extern "C" CXCursor
 clang_getSubExpr (CXCursor C)
 {
   if (clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::UnaryOperatorClass:
-          return MakeCXCursorWithNull (cast<UnaryOperator> (S)->getSubExpr (),
-                                       C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::UnaryOperatorClass:
+	  return MakeCXCursorWithNull (cast<UnaryOperator> (S)->getSubExpr (),
+				       C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -293,17 +354,17 @@ extern "C" CXCursor
 clang_getSubStmt (CXCursor C)
 {
   if (clang_isStatement (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::CaseStmtClass:
-          return MakeCXCursorWithNull (cast<CaseStmt> (S)->getSubStmt (), C);
-        case Stmt::DefaultStmtClass:
-          return MakeCXCursorWithNull (cast<DefaultStmt> (S)->getSubStmt (),
-                                       C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::CaseStmtClass:
+	  return MakeCXCursorWithNull (cast<CaseStmt> (S)->getSubStmt (), C);
+	case Stmt::DefaultStmtClass:
+	  return MakeCXCursorWithNull (cast<DefaultStmt> (S)->getSubStmt (),
+				       C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -311,17 +372,17 @@ extern "C" CXCursor
 clang_getRHS (CXCursor C)
 {
   if (clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::BinaryOperatorClass:
-          return MakeCXCursorWithNull (cast<BinaryOperator> (S)->getRHS (), C);
-        case Stmt::ConditionalOperatorClass:
-          return MakeCXCursorWithNull (
-              cast<ConditionalOperator> (S)->getRHS (), C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::BinaryOperatorClass:
+	  return MakeCXCursorWithNull (cast<BinaryOperator> (S)->getRHS (), C);
+	case Stmt::ConditionalOperatorClass:
+	  return MakeCXCursorWithNull (
+	    cast<ConditionalOperator> (S)->getRHS (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
@@ -329,20 +390,83 @@ extern "C" CXCursor
 clang_getLHS (CXCursor C)
 {
   if (clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::BinaryOperatorClass:
-          return MakeCXCursorWithNull (cast<BinaryOperator> (S)->getLHS (), C);
-        case Stmt::ConditionalOperatorClass:
-          return MakeCXCursorWithNull (
-              cast<ConditionalOperator> (S)->getRHS (), C);
-        default:
-          return clang_getNullCursor ();
-        }
+	{
+	case Stmt::BinaryOperatorClass:
+	  return MakeCXCursorWithNull (cast<BinaryOperator> (S)->getLHS (), C);
+	case Stmt::ConditionalOperatorClass:
+	  return MakeCXCursorWithNull (
+	    cast<ConditionalOperator> (S)->getLHS (), C);
+	default:
+	  return clang_getNullCursor ();
+	}
   return clang_getNullCursor ();
 }
 
+/* Return the name of the declared object. */
+
+extern "C" const CXString
+clang_getDeclName (CXCursor C)
+{
+  if (clang_isDeclaration (C.kind))
+    {
+      auto getFunctionDeclName = [] (const FunctionDecl *FD) {
+	const DeclarationName FunctionName = FD->getNameInfo ().getName ();
+	return createDup (FunctionName.getAsString ().c_str ());
+      };
+
+      const Decl *D = getCursorDecl (C);
+
+      switch (D->getKind ())
+	{
+	case Decl::Function:
+	case Decl::CXXMethod:
+	case Decl::CXXConstructor:
+	case Decl::CXXDestructor:
+	  {
+	    const clang::FunctionDecl *FD = cast<clang::FunctionDecl> (D);
+	    return getFunctionDeclName (FD);
+	  }
+	case Decl::FunctionTemplate:
+	  {
+	    const clang::FunctionDecl *FD
+	      = (cast<clang::FunctionTemplateDecl> (D))->getTemplatedDecl ();
+	    return getFunctionDeclName (FD);
+	  }
+
+	case Decl::Namespace:
+	  {
+	    const NamespaceDecl *ND
+	      = (cast<clang::NamespaceDecl> (D))->getCanonicalDecl ();
+
+	    if (ND->isAnonymousNamespace ())
+	      return createDup ("Anonymous namespace");
+
+	    return createDup (ND->getName ());
+	  }
+	case Decl::ClassTemplate:
+
+	  {
+	    const clang::ClassTemplateDecl *CD
+	      = (cast<clang::ClassTemplateDecl> (D))->getCanonicalDecl ();
+	    return createDup (CD->getName ());
+	  }
+	case Decl::CXXRecord:
+	  {
+	    const clang::CXXRecordDecl *RD
+	      = (cast<clang::CXXRecordDecl> (D))->getCanonicalDecl ();
+	    return createDup (RD->getName ());
+	  }
+
+	default:
+	  return createEmpty ();
+	}
+    }
+
+  return createEmpty ();
+}
+
 /* Return the string representative of the operator for a binary or unary
    operator node.  */
 
@@ -350,18 +474,18 @@ extern "C" CXString
 clang_getOpcodeStr (CXCursor C)
 {
   if (clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::BinaryOperatorClass:
-          return createRef (BinaryOperator::getOpcodeStr (
-              cast<BinaryOperator> (S)->getOpcode ()));
-        case Stmt::UnaryOperatorClass:
-          return createRef (UnaryOperator::getOpcodeStr (
-              cast<UnaryOperator> (S)->getOpcode ()));
-        default:
-          return createEmpty ();
-        }
+	{
+	case Stmt::BinaryOperatorClass:
+	  return createRef (BinaryOperator::getOpcodeStr (
+	    cast<BinaryOperator> (S)->getOpcode ()));
+	case Stmt::UnaryOperatorClass:
+	  return createRef (UnaryOperator::getOpcodeStr (
+	    cast<UnaryOperator> (S)->getOpcode ()));
+	default:
+	  return createEmpty ();
+	}
   return createEmpty ();
 }
 
@@ -370,24 +494,22 @@ clang_getOpcodeStr (CXCursor C)
 extern "C" CXSourceLocation
 clang_getOperatorLoc (CXCursor C)
 {
-  ASTUnit *CXXUnit = cxtu::getASTUnit (getCursorTU (C));
-  if (!CXXUnit)
-    return clang_getNullLocation ();
+  CXTranslationUnit TU = getCursorTU (C);
   SourceLocation sloc;
   if (clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-        case Stmt::BinaryOperatorClass:
-          sloc = cast<BinaryOperator> (S)->getOperatorLoc ();
-          break;
-        case Stmt::UnaryOperatorClass:
-          sloc = cast<UnaryOperator> (S)->getOperatorLoc ();
-          break;
-        default:
-          return clang_getNullLocation ();
-        }
-  return cxloc::translateSourceLocation (CXXUnit->getASTContext (), sloc);
+	{
+	case Stmt::BinaryOperatorClass:
+	  sloc = cast<BinaryOperator> (S)->getOperatorLoc ();
+	  break;
+	case Stmt::UnaryOperatorClass:
+	  sloc = cast<UnaryOperator> (S)->getOperatorLoc ();
+	  break;
+	default:
+	  return clang_getNullLocation ();
+	}
+  return translateSLoc (TU, sloc);
 }
 
 /* If the given expression is a wrapping expression (i.e. a parenthesized
@@ -398,28 +520,27 @@ extern "C" CXCursor
 clang_unwrap (CXCursor C)
 {
   if (clang_isExpression (C.kind))
-    if (const Stmt *S = cxcursor::getCursorStmt (C))
+    if (const Stmt *S = getCursorStmt (C))
       switch (S->getStmtClass ())
-        {
-          case Stmt::ParenExprClass:
-            return clang_unwrap
-              (MakeCXCursorWithNull (cast<ParenExpr> (S)->getSubExpr (), C));
-          case Expr::ConstantExprClass:
-          case Expr::ExprWithCleanupsClass:
-            return clang_unwrap
-              (MakeCXCursorWithNull (cast<FullExpr> (S)->getSubExpr (), C));
-          case Expr::ImplicitCastExprClass:
-          case Expr::CStyleCastExprClass:
-          case Expr::CXXFunctionalCastExprClass:
-          case Expr::CXXStaticCastExprClass:
-          case Expr::CXXDynamicCastExprClass:
-          case Expr::CXXReinterpretCastExprClass:
-          case Expr::CXXConstCastExprClass:
-          case Expr::CXXAddrspaceCastExprClass:
-            return clang_unwrap
-              (MakeCXCursorWithNull (cast<CastExpr> (S)->getSubExpr (), C));
-
-        }
+	{
+	case Stmt::ParenExprClass:
+	  return clang_unwrap (
+	    MakeCXCursorWithNull (cast<ParenExpr> (S)->getSubExpr (), C));
+	case Expr::ConstantExprClass:
+	case Expr::ExprWithCleanupsClass:
+	  return clang_unwrap (
+	    MakeCXCursorWithNull (cast<FullExpr> (S)->getSubExpr (), C));
+	case Expr::ImplicitCastExprClass:
+	case Expr::CStyleCastExprClass:
+	case Expr::CXXFunctionalCastExprClass:
+	case Expr::CXXStaticCastExprClass:
+	case Expr::CXXDynamicCastExprClass:
+	case Expr::CXXReinterpretCastExprClass:
+	case Expr::CXXConstCastExprClass:
+	case Expr::CXXAddrspaceCastExprClass:
+	  return clang_unwrap (
+	    MakeCXCursorWithNull (cast<CastExpr> (S)->getSubExpr (), C));
+	}
   return C;
 }
 
@@ -428,11 +549,10 @@ clang_unwrap (CXCursor C)
 
 extern "C" unsigned
 clang_visit (CXCursor parent, CXCursorVisitor visitor,
-             CXClientData client_data)
+	     CXClientData client_data)
 {
-  cxcursor::CursorVisitor CursorVis (getCursorTU (parent), visitor,
-                                     client_data,
-                                     /*VisitPreprocessorLast=*/false);
+  CursorVisitor CursorVis (getCursorTU (parent), visitor, client_data,
+			   /*VisitPreprocessorLast=*/false);
   return CursorVis.Visit (parent);
 }
 
@@ -443,16 +563,14 @@ extern "C" CXCursor
 clang_getParent (CXCursor C)
 {
   assert (clang_isStatement (C.kind) || clang_isExpression (C.kind));
-  ASTUnit *astUnit = cxtu::getASTUnit (getCursorTU (C));
-  ASTContext &astContext = astUnit->getASTContext ();
-  if (const Stmt *S = cxcursor::getCursorStmt (C))
+  if (const Stmt *S = getCursorStmt (C))
     {
-      const auto Parents = astContext.getParents (*S);
+      const auto Parents = getContext (C).getParents (*S);
       if (Parents.empty ())
-        return clang_getNullCursor ();
+	return clang_getNullCursor ();
       const auto &SParent = Parents[0];
       if (const auto *Res = SParent.get<Stmt> ())
-        return MakeCXCursorWithNull (Res, C);
+	return MakeCXCursorWithNull (Res, C);
     }
   return clang_getNullCursor ();
 }
@@ -461,41 +579,56 @@ clang_getParent (CXCursor C)
 
 extern "C" void
 clang_CXRewriter_insertTextAfter (CXRewriter Rew, CXSourceLocation Loc,
-                                  const char *Insert)
+				  const char *Insert)
 {
   assert (Rew);
-  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *> (Rew);
-  R.InsertTextAfter (clang::cxloc::translateSourceLocation (Loc), Insert);
+  Rewriter &R = *reinterpret_cast<Rewriter *> (Rew);
+  R.InsertTextAfter (translateSourceLocation (Loc), Insert);
 }
 
 extern "C" void
 clang_CXRewriter_insertTextAfterToken (CXRewriter Rew, CXSourceLocation Loc,
-                                       const char *Insert)
+				       const char *Insert)
+{
+  assert (Rew);
+  Rewriter &R = *reinterpret_cast<Rewriter *> (Rew);
+  R.InsertTextAfterToken (translateSourceLocation (Loc), Insert);
+}
+
+extern "C" void
+clang_CXRewriter_insertTextBeforeToken (CXRewriter Rew, CXSourceLocation Loc,
+                                        const char *Insert)
 {
   assert (Rew);
-  clang::Rewriter &R = *reinterpret_cast<clang::Rewriter *> (Rew);
-  R.InsertTextAfterToken (clang::cxloc::translateSourceLocation (Loc), Insert);
+  Rewriter &R = *reinterpret_cast<Rewriter *> (Rew);
+  const SourceManager &SM = R.getSourceMgr ();
+
+  // Get the previous location, and insert after it
+
+  SourceLocation SLoc = translateSourceLocation (Loc);
+  SourceLocation Prv = SLoc.getLocWithOffset (-1);
+  R.InsertTextAfter (Prv, Insert);
 }
 
+/* Wrappers around source location analysis functions.  */
+
 extern "C" unsigned
 clang_isMacroLocation (CXSourceLocation Loc)
 {
-  const clang::SourceLocation SLoc =
-     clang::cxloc::translateSourceLocation (Loc);
+  const SourceLocation SLoc = translateSourceLocation (Loc);
   return SLoc.isMacroID () ? 1 : 0;
 }
 
 extern "C" unsigned
-clang_isMacroArgExpansion (CXSourceLocation Loc, CXSourceLocation *StartLoc, CXTranslationUnit TU)
+clang_isMacroArgExpansion (CXSourceLocation Loc, CXSourceLocation *StartLoc,
+			   CXTranslationUnit TU)
 {
-  const SourceManager &SM = cxtu::getASTUnit (TU)->getSourceManager ();
+  const SourceManager &SM = getSourceManager (TU);
   const SourceLocation SLoc = translateSourceLocation (Loc);
-  ASTUnit *astUnit = cxtu::getASTUnit (TU);
-  ASTContext &astContext = astUnit->getASTContext ();
   SourceLocation Result;
   if (SM.isMacroArgExpansion (SLoc, &Result))
     {
-      *StartLoc = cxloc::translateSourceLocation (astContext, Result);
+      *StartLoc = translateSLoc (TU, Result);
       return 1;
     }
   return 0;
@@ -504,59 +637,50 @@ clang_isMacroArgExpansion (CXSourceLocation Loc, CXSourceLocation *StartLoc, CXT
 extern "C" CXSourceLocation
 clang_getImmediateMacroCallerLoc (CXSourceLocation Loc, CXTranslationUnit TU)
 {
-  SourceManager &SM = cxtu::getASTUnit (TU)->getSourceManager ();
-  ASTUnit *astUnit = cxtu::getASTUnit (TU);
-  ASTContext &astContext = astUnit->getASTContext ();
+  const SourceManager &SM = getSourceManager (TU);
   SourceLocation SLoc = translateSourceLocation (Loc);
   if (SLoc.isMacroID ())
-    return cxloc::translateSourceLocation
-      (astContext, SM.getImmediateMacroCallerLoc (SLoc));
+    return translateSLoc (TU, SM.getImmediateMacroCallerLoc (SLoc));
   return Loc;
 }
 
 extern "C" CXSourceLocation
 clang_getImmediateExpansionLoc (CXSourceLocation Loc, CXTranslationUnit TU)
 {
-  SourceManager &SM = cxtu::getASTUnit (TU)->getSourceManager ();
-  ASTUnit *astUnit = cxtu::getASTUnit (TU);
-  ASTContext &astContext = astUnit->getASTContext ();
+  const SourceManager &SM = getSourceManager (TU);
   SourceLocation SLoc = translateSourceLocation (Loc);
-  return cxloc::translateSourceLocation
-    (astContext, SM.getImmediateExpansionRange (SLoc).getBegin ());
+  return translateSLoc (TU, SM.getImmediateExpansionRange (SLoc).getBegin ());
 }
 
 extern "C" CXString
-clang_getImmediateMacroNameForDiagnostics (CXSourceLocation Loc, CXTranslationUnit TU)
+clang_getImmediateMacroNameForDiagnostics (CXSourceLocation Loc,
+					   CXTranslationUnit TU)
 {
   SourceLocation SLoc = translateSourceLocation (Loc);
-  SourceManager &SM = cxtu::getASTUnit (TU)->getSourceManager ();
-  ASTUnit *astUnit = cxtu::getASTUnit (TU);
-  ASTContext &astContext = astUnit->getASTContext ();
-  return createDup
-    (Lexer::getImmediateMacroNameForDiagnostics
-     (SLoc, SM, astContext.getLangOpts ()));
+  const SourceManager &SM = getSourceManager (TU);
+  return createDup (Lexer::getImmediateMacroNameForDiagnostics (
+    SLoc, SM, getContext (TU).getLangOpts ()));
 }
 
 extern "C" CXSourceLocation
 clang_getExpansionEnd (CXTranslationUnit TU, CXSourceLocation Loc)
 {
   SourceLocation SLoc = translateSourceLocation (Loc);
-  SourceManager &SM = cxtu::getASTUnit (TU)->getSourceManager ();
-  ASTUnit *astUnit = cxtu::getASTUnit (TU);
-  ASTContext &astContext = astUnit->getASTContext ();
-  return cxloc::translateSourceLocation
-    (astContext, SM.getExpansionRange (SLoc).getEnd ());
+  const SourceManager &SM = getSourceManager (TU);
+  return translateSLoc (TU, SM.getExpansionRange (SLoc).getEnd ());
 }
 
 extern "C" CXTranslationUnit
 clang_getCursorTU (CXCursor C)
 {
-  return cxcursor::getCursorTU (C);
+  return getCursorTU (C);
 }
 
+/* Debug helpers.  */
+
 extern "C" void
 clang_printLocation (CXTranslationUnit TU, CXSourceLocation Loc)
 {
-  const SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
-  clang::cxloc::translateSourceLocation (Loc).dump(SM);
+  const SourceManager &SM = getSourceManager (TU);
+  translateSourceLocation (Loc).dump (SM);
 }
diff --git a/tools/gnatcov/coverage-source.adb b/tools/gnatcov/coverage-source.adb
index 0a3ec2a92..3eee24d33 100644
--- a/tools/gnatcov/coverage-source.adb
+++ b/tools/gnatcov/coverage-source.adb
@@ -40,7 +40,6 @@ with Elf_Disassemblers;   use Elf_Disassemblers;
 with GNATcov_RTS.Buffers; use GNATcov_RTS.Buffers;
 with MC_DC;               use MC_DC;
 with Outputs;             use Outputs;
-with Project;
 with Slocs;               use Slocs;
 with Strings;             use Strings;
 with Switches;            use Switches;
@@ -217,7 +216,7 @@ package body Coverage.Source is
 
    package US renames Ada.Strings.Unbounded;
 
-   Unit_List : String_Sets.Set;
+   Unit_List : Unit_Sets.Set;
    --  List of names for units of interest. Store it as an ordered set so that
    --  the order of dump depends on its content, not on the way it was created.
 
@@ -279,19 +278,19 @@ package body Coverage.Source is
       end if;
 
       Unit_List_Invalidated := True;
-      Unit_List := String_Sets.Empty_Set;
+      Unit_List := Unit_Sets.Empty_Set;
    end Invalidate_Unit_List;
 
-   -------------------
-   -- Add_Unit_Name --
-   -------------------
+   --------------
+   -- Add_Unit --
+   --------------
 
-   procedure Add_Unit_Name (Name : String) is
+   procedure Add_Unit (Unit : Project_Unit) is
    begin
       if not Unit_List_Invalidated then
-         Unit_List.Include (US.To_Unbounded_String (Name));
+         Unit_List.Include (Unit);
       end if;
-   end Add_Unit_Name;
+   end Add_Unit;
 
    ---------------------------
    -- Unit_Name_For_Subunit --
@@ -308,8 +307,13 @@ package body Coverage.Source is
                        Pattern => ".",
                        From    => Sep,
                        Going   => Ada.Strings.Backward);
+
          if Unit_List.Contains
-           (US.To_Unbounded_String (Qualified_Name (First .. Sep - 1)))
+           (Project_Unit'
+              (Language  => Unit_Based_Language,
+               Unit_Name =>
+                 US.To_Unbounded_String
+                         (Qualified_Name (First .. Sep - 1))))
          then
             return Qualified_Name (First .. Sep - 1);
          end if;
@@ -335,7 +339,6 @@ package body Coverage.Source is
 
    procedure Compute_Unit_Name_For_Ignored_Sources
    is
-      use Project;
       use Types;
 
       procedure Callback
@@ -407,8 +410,7 @@ package body Coverage.Source is
            and then FI.Ignore_Status in Always .. Sometimes
          then
             pragma Assert
-              (FI.Unit.Known and then Unit_List.Contains (FI.Unit.Name));
-
+              (FI.Unit.Known and then Is_Unit_Of_Interest (FI.Full_Name.all));
             if not Ignored_SF_Map.Contains (FI.Unit.Name) then
                Vec := new Ignored_Sources_Vector.Vector;
                Ignored_SF_Map.Insert (FI.Unit.Name, Vec);
@@ -430,15 +432,15 @@ package body Coverage.Source is
    --------------------------
 
    procedure Iterate_On_Unit_List
-     (Process_Unit        : not null access procedure (Name : String);
+     (Process_Unit        : not null access procedure (Name : Project_Unit);
       Process_Source_File : not null access procedure (FI : File_Info))
    is
    begin
       for S of Unit_List loop
-         Process_Unit.all (US.To_String (S));
+         Process_Unit.all (S);
 
-         if Ignored_SF_Map.Contains (S) then
-            for FI of Ignored_SF_Map.Element (S).all loop
+         if Ignored_SF_Map.Contains (S.Unit_Name) then
+            for FI of Ignored_SF_Map.Element (S.Unit_Name).all loop
                Process_Source_File (FI.all);
             end loop;
          end if;
@@ -461,7 +463,7 @@ package body Coverage.Source is
          if not Unit_List_Invalidated then
             Ada.Containers.Count_Type'Output (CSS, Unit_List.Length);
             for N of Unit_List loop
-               US.Unbounded_String'Output (CSS, N);
+               Project_Unit'Output (CSS, N);
             end loop;
          end if;
       end if;
@@ -475,7 +477,7 @@ package body Coverage.Source is
    begin
       SCI_Vector.Clear;
       Unit_List_Invalidated := False;
-      Unit_List := String_Sets.Empty_Set;
+      Unit_List := Unit_Sets.Empty_Set;
    end Checkpoint_Clear;
 
    ---------------------
@@ -585,9 +587,13 @@ package body Coverage.Source is
             Invalidate_Unit_List
               (US.To_String (CLS.Filename)
                & " does not contain the list of units (obsolete format)");
+
          else
             declare
                Invalidated : constant Boolean := Boolean'Input (CLS);
+               Obsolete    : constant Boolean :=
+                 Checkpoints.Version_Less (CLS, Than => 12);
+               Dummy       : US.Unbounded_String;
             begin
                if Invalidated then
                   Invalidate_Unit_List
@@ -595,9 +601,24 @@ package body Coverage.Source is
                      & " does not contain the list of units (produced with"
                      & " --scos or --sid)");
                else
-                  for I in 1 ..  Ada.Containers.Count_Type'Input (CLS) loop
-                     pragma Unreferenced (I);
-                     Unit_List.Include (US.Unbounded_String'Input (CLS));
+                  for I in 1 .. Ada.Containers.Count_Type'Input (CLS) loop
+
+                     --  From version 3 up to version 11, Unit_List used to
+                     --  be a set of unbounded strings, and did not support
+                     --  homonym source files. If we are in that case, read the
+                     --  old Unit_List from the checkpoint and then discard it.
+
+                     if Obsolete then
+                        if I = 1 then
+                           Invalidate_Unit_List
+                             (US.To_String (CLS.Filename)
+                              & " does not contain the list of units (obsolete"
+                              & " format)");
+                        end if;
+                        US.Unbounded_String'Read (CLS, Dummy);
+                     else
+                        Unit_List.Include (Project_Unit'Input (CLS));
+                     end if;
                   end loop;
                end if;
             end;
@@ -1701,12 +1722,13 @@ package body Coverage.Source is
    end Compute_Source_Coverage;
 
    procedure Compute_Source_Coverage
-     (Filename        : String;
-      Fingerprint     : SC_Obligations.SCOs_Hash;
-      CU_Name         : Compilation_Unit_Name;
-      Stmt_Buffer     : Coverage_Buffer;
-      Decision_Buffer : Coverage_Buffer;
-      MCDC_Buffer     : Coverage_Buffer)
+     (Filename             : String;
+      Fingerprint          : SC_Obligations.Fingerprint_Type;
+      CU_Name              : Compilation_Unit_Name;
+      Bit_Maps_Fingerprint : SC_Obligations.Fingerprint_Type;
+      Stmt_Buffer          : Coverage_Buffer;
+      Decision_Buffer      : Coverage_Buffer;
+      MCDC_Buffer          : Coverage_Buffer)
    is
       CU : CU_Id;
       BM : CU_Bit_Maps;
@@ -1781,7 +1803,10 @@ package body Coverage.Source is
       --  Sanity check that Fingerprint is consistent with what the
       --  instrumenter recorded in the CU info.
 
-      if Fingerprint /= SC_Obligations.Fingerprint (CU) then
+      if Fingerprint /= SC_Obligations.Fingerprint (CU)
+           or else
+         Bit_Maps_Fingerprint /= SC_Obligations.Bit_Maps_Fingerprint (CU)
+      then
          Warn ("traces for " & Unit_Image  & " (from " & Filename & ") are"
                & " inconsistent with the corresponding Source Instrumentation"
                & " Data");
diff --git a/tools/gnatcov/coverage-source.ads b/tools/gnatcov/coverage-source.ads
index 2cb418906..2de4ca3dc 100644
--- a/tools/gnatcov/coverage-source.ads
+++ b/tools/gnatcov/coverage-source.ads
@@ -21,6 +21,7 @@ with Files_Table;             use Files_Table;
 with Instrument;              use Instrument;
 with Instrument.Input_Traces; use Instrument.Input_Traces;
 with Instrument.Base_Types;   use Instrument.Base_Types;
+with Project;                 use Project;
 with Traces;                  use Traces;
 with Traces_Names;            use Traces_Names;
 with Traces_Lines;            use Traces_Lines;
@@ -49,12 +50,13 @@ package Coverage.Source is
    --  determine the coverage state of each SCO.
 
    procedure Compute_Source_Coverage
-     (Filename        : String;
-      Fingerprint     : SC_Obligations.SCOs_Hash;
-      CU_Name         : Compilation_Unit_Name;
-      Stmt_Buffer     : Coverage_Buffer;
-      Decision_Buffer : Coverage_Buffer;
-      MCDC_Buffer     : Coverage_Buffer);
+     (Filename             : String;
+      Fingerprint          : SC_Obligations.Fingerprint_Type;
+      CU_Name              : Compilation_Unit_Name;
+      Bit_Maps_Fingerprint : SC_Obligations.Fingerprint_Type;
+      Stmt_Buffer          : Coverage_Buffer;
+      Decision_Buffer      : Coverage_Buffer;
+      MCDC_Buffer          : Coverage_Buffer);
    --  Analyze source instrumentation traces to determine the coverage state
    --  of each SCO. Generic actual for
    --  Instrument.Input_Traces.Generic_Read_Source_Trace_File.
@@ -102,9 +104,9 @@ package Coverage.Source is
    --  message is used for logging purposes, so that users can find out why we
    --  cannot dump the list of units of interest.
 
-   procedure Add_Unit_Name (Name : String);
-   --  Add Name to the list of names for units of interest. For convenience, do
-   --  nothing if it is invalid.
+   procedure Add_Unit (Unit : Project_Unit);
+   --  Add Unit to the list of units of interest. For convenience, do nothing
+   --  if it is invalid.
 
    procedure Compute_Unit_Name_For_Ignored_Sources;
    --  Compute the name of the owning unit for each known source file that is
@@ -115,7 +117,7 @@ package Coverage.Source is
    --  unit names, linking them to the list of ignored source files.
 
    procedure Iterate_On_Unit_List
-     (Process_Unit        : not null access procedure (Name : String);
+     (Process_Unit        : not null access procedure (Name : Project_Unit);
       Process_Source_File : not null access procedure (FI : File_Info))
    with Pre => Unit_List_Is_Valid;
    --  Call Unit_Callback for each unit of interest, passing to it the name of
diff --git a/tools/gnatcov/doinstall.tmplt b/tools/gnatcov/doinstall.tmplt
index 8377699ae..eceefddb3 100755
--- a/tools/gnatcov/doinstall.tmplt
+++ b/tools/gnatcov/doinstall.tmplt
@@ -1,6 +1,6 @@
 #! /bin/sh
 #
-# GNATcoverage binary distribution installation script
+# GNAT DAS binary distribution installation script
 # Copyright (c) 2011-2020, Free Software Foundation
 #
 
@@ -55,7 +55,7 @@ check_env () {
 ###################################
 
 set_qualifier () {
-    qualifier="GNATcoverage"
+    qualifier="GNAT DAS"
     long_qualifier="$qualifier"
 }
 
@@ -76,9 +76,9 @@ EOF
     fi
 }
 
-#####################################
-# Install GNATcoverage into $prefix #
-#####################################
+#################################
+# Install GNAT DAS into $prefix #
+#################################
 
 standard_installation () {
 
@@ -159,11 +159,11 @@ if [ $# -eq 1 ]; then
     cat << EOF
    Usage: $0 [install_dir]
 
-   When no argument is specified, runs the GNATcoverage installer
+   When no argument is specified, runs the $qualifier installer
    interactively, otherwise installs automatically under install_dir.
 EOF
   else
-    echo "   installing GNATcoverage under $1"
+    echo "   installing $qualifier under $1"
     prefix="$1"
     standard_installation
   fi
diff --git a/tools/gnatcov/files_table.adb b/tools/gnatcov/files_table.adb
index 33494b7d8..08a24c0d5 100644
--- a/tools/gnatcov/files_table.adb
+++ b/tools/gnatcov/files_table.adb
@@ -27,7 +27,7 @@ with System;
 with System.Address_To_Access_Conversions;
 
 with GNAT.OS_Lib;
-with GNAT.Regpat;
+with GNAT.Regpat; use GNAT.Regpat;
 with Osint;
 
 with GNATCOLL.VFS; use GNATCOLL.VFS;
@@ -39,6 +39,7 @@ with Outputs;
 with Paths;         use Paths;
 with Perf_Counters; use Perf_Counters;
 with Project;
+with Strings;
 with Switches;
 
 package body Files_Table is
@@ -312,7 +313,6 @@ package body Files_Table is
    -----------------------
 
    procedure Add_Source_Rebase (Old_Prefix : String; New_Prefix : String) is
-      use GNAT.Regpat;
       E : Source_Rebase_Entry_Acc;
 
       Regexp : constant String := "^" & Paths.Glob_To_Regexp (Old_Prefix);
@@ -1643,7 +1643,6 @@ package body Files_Table is
       --  Try to rebase
 
       declare
-         use GNAT.Regpat;
          E    : Source_Rebase_Entry_Acc := First_Source_Rebase_Entry;
          Name : constant String := Name_For_Rebase (File);
 
@@ -2218,4 +2217,100 @@ package body Files_Table is
       end loop;
    end Checkpoint_Load;
 
+   ------------------------
+   -- Postprocess_Source --
+   ------------------------
+
+   procedure Postprocess_Source
+     (Preprocessed_Filename  : String;
+      Postprocessed_Filename : String)
+   is
+      use Ada.Strings.Unbounded;
+      use Strings;
+      Preprocessed_File  : Ada.Text_IO.File_Type;
+      Postprocessed_File : Ada.Text_IO.File_Type;
+   begin
+      Open (Preprocessed_File, In_File, Preprocessed_Filename);
+      Create (Postprocessed_File, Out_File, Postprocessed_Filename);
+      declare
+         Line_Marker_Pattern : constant GNAT.Regpat.Pattern_Matcher :=
+           Compile
+             ("\s*#\s*"
+              --  Start of the line directive
+
+              & "([0-9]+)"
+              --  Line index
+
+              & "\s+"
+              & "([^ \s]+)"
+              --  Filename
+
+              & "(.*)"
+              --  Trailing flags;
+
+             );
+
+         Add_New_Line : Boolean := True;
+
+         Line_Marker_Line : Integer;
+         Line_Marker_File : Unbounded_String;
+         --  Line and file of the read line if it is a line marker
+
+         Current_File : Unbounded_String;
+         Current_Line : Integer := 0;
+         --  Line and file of the currently-active line marker
+
+         Matches : Match_Array (0 .. 3);
+      begin
+         while not End_Of_File (Preprocessed_File) loop
+            declare
+               Line : constant String := Get_Line (Preprocessed_File);
+            begin
+               Match (Line_Marker_Pattern, Line, Matches);
+               if Matches (0) /= No_Match then
+                  Line_Marker_Line :=
+                    Integer'Value
+                      (Line (Matches (1).First .. Matches (1).Last));
+                  Line_Marker_File :=
+                    +Line (Matches (2).First .. Matches (2).Last);
+                  if Line_Marker_Line = Current_Line - 1
+                    and then Line_Marker_File = Current_File
+                  then
+                     --  We have a spurious line marker. Remove it, and write
+                     --  the next line in continuation of the previous line.
+
+                     Add_New_Line := False;
+                     Current_Line := Current_Line - 1;
+                  else
+                     --  Write this line marker, and set the current line and
+                     --  the current file.
+
+                     Current_Line := Line_Marker_Line;
+                     Current_File := Line_Marker_File;
+
+                     --  Line markers should always be inserted on their own
+                     --  line.
+
+                     Add_New_Line := True;
+                     New_Line (Postprocessed_File);
+                     Put (Postprocessed_File, Line);
+                  end if;
+               else
+                  if Add_New_Line then
+                     New_Line (Postprocessed_File);
+                  end if;
+                  Add_New_Line := True;
+                  Put (Postprocessed_File, Line);
+                  Current_Line := Current_Line + 1;
+               end if;
+            end;
+         end loop;
+         if Add_New_Line then
+            New_Line (Postprocessed_File);
+         end if;
+      end;
+      Close (Preprocessed_File);
+      Close (Postprocessed_File);
+   end Postprocess_Source;
+
 end Files_Table;
diff --git a/tools/gnatcov/files_table.ads b/tools/gnatcov/files_table.ads
index 9ad16ca1e..4dcefb10f 100644
--- a/tools/gnatcov/files_table.ads
+++ b/tools/gnatcov/files_table.ads
@@ -105,6 +105,10 @@ package Files_Table is
    --
    --  Windows-looking absolute filenames are canonicalized by upper casing the
    --  drive letter and lower casing all other letters.
+   --
+   --  If Insert_After_Freeze is True, allow the inserting after the files
+   --  table has been frozen. Note that this breaks the uniqueness invariant
+   --  of names returned by Get_Unique_Name.
 
    procedure Consolidate_File_Kind
      (Index : Valid_Source_File_Index;
@@ -534,6 +538,31 @@ package Files_Table is
    --  Ignore_Source_Files should be null if the checkpoint purpose is
    --  Consolidation.
 
+   procedure Postprocess_Source
+     (Preprocessed_Filename  : String;
+      Postprocessed_Filename : String);
+   --  Specific to the C instrumentation.
+   --
+   --  Postprocess the given Preprocessed_Filename to remove redundant line
+   --  markers. This is done to keep the following invariant intact: we must
+   --  be able to use presumed source locations to have unique SCOs source
+   --  locations. As a reminder, presumed locations are computed through
+   --  preprocessor-inserted line markers.
+   --
+   --  This means that the following code:
+   --  # 1 main.c
+   --  int a;
+   --  # 1 main.c 3
+   --  int ab;
+   --
+   --  will be rewritten to
+   --  # 1 main.c
+   --  int a; int ab;
+   --
+   --  Note that this will remove the "system header "flag (the "3" at the
+   --  end of the line marker). We expect that this won't be a problem in
+   --  practice.
+
 private
    --  Describe a source file - one element per line
 
diff --git a/tools/gnatcov/gnatcov_bits_specific.adb b/tools/gnatcov/gnatcov_bits_specific.adb
index f53df8bd3..2a02c13ae 100644
--- a/tools/gnatcov/gnatcov_bits_specific.adb
+++ b/tools/gnatcov/gnatcov_bits_specific.adb
@@ -281,13 +281,7 @@ procedure GNATcov_Bits_Specific is
       --  have to be enumerated from a project file.
 
       if Inputs.Length (ALIs_Inputs) = 0 and then Is_Project_Loaded then
-         Enumerate_LIs (Add_LI'Access);
-      end if;
-
-      --  When appropriate, warn about units of interest with no LI
-
-      if Is_Project_Loaded and then LIs_Enumerated then
-         Report_Units_Without_LI;
+         Enumerate_SCOs_Files (Add_LI'Access, Binary_Trace_File);
       end if;
 
       if Source_Coverage_Enabled then
@@ -354,7 +348,7 @@ procedure GNATcov_Bits_Specific is
       --  of interest. This requires a project file.
 
       if Is_Project_Loaded and then Inputs.Length (SID_Inputs) = 0 then
-         Enumerate_SIDs (Add_SID_File'Access);
+         Enumerate_SCOs_Files (Add_SID_File'Access, Source_Trace_File);
       end if;
 
       --  Now load the SID files, applying the Ignore_Source_Files filter,
@@ -804,16 +798,16 @@ procedure GNATcov_Bits_Specific is
             | Cmd_Dump_Trace_Asm
             | Cmd_Dump_Src_Trace =>
 
-            --  For "coverage", require an annotation format unless we must
-            --  save a checkpoint. In this case, we'll just skip report
-            --  production.
+            --  For "coverage", if the annotation format is not specified on
+            --  the command line, default to report, unless we must save a
+            --  checkpoint.
 
             if Args.Command = Cmd_Coverage
-              and then
-                (Args.String_List_Args (Opt_Annotation_Format).Is_Empty
-                 and Save_Checkpoint = null)
+              and then Args.String_List_Args (Opt_Annotation_Format).Is_Empty
+              and then Save_Checkpoint = null
             then
-               Report_Missing_Argument ("an annotation format");
+               Annotation (Annotate_Report) := True;
+               Annotation (Annotate_Unknown) := False;
             end if;
 
             --  If --no-cov-report is on the command line, check that
@@ -990,11 +984,17 @@ procedure GNATcov_Bits_Specific is
             --  either an object level specified, or possibly no --level at
             --  all.
 
-            if not Source_Coverage_Enabled then
+            if Object_Coverage_Enabled then
                Fatal_Error
                  ("Instrumentation requires a source coverage level"
                   & ASCII.LF
                   & "  (--level=" & Source_Level_Options ("|") & ")");
+            elsif not Source_Coverage_Enabled then
+               Warn
+                 ("Coverage level not specified on the command"
+                  & " line or in the project file (--level="
+                  & Source_Level_Options ("|") & "), defaulting to ""stmt"".");
+               Set_Coverage_Levels ("stmt");
             end if;
 
             if Args.Remaining_Args.Length > 0 then
@@ -1020,6 +1020,9 @@ procedure GNATcov_Bits_Specific is
                end;
             end if;
 
+            Copy_Arg_List (Opt_C_Opts, C_Opts);
+            Copy_Arg_List (Opt_CPP_Opts, CPP_Opts);
+
          when others =>
             null;
       end case;
@@ -1617,11 +1620,16 @@ begin
 
       when Cmd_Coverage =>
 
-         --  Make sure we have a coverage level
+         --  Warn when the user hasn't explicitly set a coverage level and
+         --  default to stmt.
 
          if not (Source_Coverage_Enabled or else Object_Coverage_Enabled)
          then
-            Report_Missing_Argument ("a coverage level");
+            Warn ("Coverage level not specified on the command line or in the"
+                  & " project file (--level=" & Source_Level_Options ("|")
+                  & "|" & Object_Level_Options ("|") & "), defaulting to"
+                  & " ""stmt"".");
+            Set_Coverage_Levels ("stmt");
          end if;
 
          --  Reject the use of several features that are not supported with
@@ -1670,17 +1678,17 @@ begin
          --  Build the list of units of interest from project files option
 
          declare
-            procedure Add_Unit (Name : String; Is_Subunit : Boolean);
+            procedure Add_Unit (Unit : Project_Unit; Is_Subunit : Boolean);
             --  Add Name to the list of names for units of interest
 
             --------------
             -- Add_Unit --
             --------------
 
-            procedure Add_Unit (Name : String; Is_Subunit : Boolean) is
+            procedure Add_Unit (Unit : Project_Unit; Is_Subunit : Boolean) is
                pragma Unreferenced (Is_Subunit);
             begin
-               Add_Unit_Name (Name);
+               Add_Unit (Unit);
             end Add_Unit;
          begin
             Enumerate_Units_Of_Interest (Add_Unit'Access);
@@ -2255,13 +2263,16 @@ begin
                      then Standard_Output
                      else File'Access);
 
-                  procedure Print_Unit_Name (Name : String);
-                  --  Print the name of the file and if it was always or
-                  --  sometimes ignored on the report, if it was ignored at
-                  --  some point during the coverage analysis.
+                  procedure Print_Ignored_File (FI : Files_Table.File_Info);
+                  --  Assuming that FI designates an ignored file, print its
+                  --  filename and its ignored status.
 
-                  procedure Print_Ignored_File
-                    (FI : Files_Table.File_Info);
+                  procedure Print_Unit_Name (Unit : Project_Unit);
+                  --  Print the name of the file
+
+                  ------------------------
+                  -- Print_Ignored_File --
+                  ------------------------
 
                   procedure Print_Ignored_File
                     (FI : Files_Table.File_Info) is
@@ -2276,9 +2287,13 @@ begin
                      end if;
                   end Print_Ignored_File;
 
-                  procedure Print_Unit_Name (Name : String) is
+                  ---------------------
+                  -- Print_Unit_Name --
+                  ---------------------
+
+                  procedure Print_Unit_Name (Unit : Project_Unit) is
                   begin
-                     Put_Line (Output.all, Name);
+                     Put_Line (Output.all, +Unit.Unit_Name);
                   end Print_Unit_Name;
 
                begin
@@ -2348,6 +2363,7 @@ begin
                if Annotation (Annotate_Unknown) then
                   pragma Assert (Save_Checkpoint /= null);
                end if;
+
             else
                pragma Assert (Save_Checkpoint /= null);
             end if;
diff --git a/tools/gnatcov/inputs.adb b/tools/gnatcov/inputs.adb
index b4ca574b9..300aac1cd 100644
--- a/tools/gnatcov/inputs.adb
+++ b/tools/gnatcov/inputs.adb
@@ -267,9 +267,10 @@ package body Inputs is
    --------------------
 
    procedure Create_Matcher
-     (Pattern_List : String_Vectors.Vector;
-      Matcher      : out GNAT.Regexp.Regexp;
-      Has_Matcher  : out Boolean)
+     (Pattern_List     : String_Vectors.Vector;
+      Matcher          : out GNAT.Regexp.Regexp;
+      Has_Matcher      : out Boolean;
+      Case_Insensitive : Boolean := False)
    is
       use Ada.Strings.Unbounded;
       use String_Vectors;
@@ -293,7 +294,11 @@ package body Inputs is
       -------------
 
       procedure Process (C : Cursor) is
-         Glob_Pattern : constant String := To_Lower (+Element (C));
+         Raw_Pattern  : constant String := +String_Vectors.Element (C);
+         Glob_Pattern : constant String :=
+           (if Case_Insensitive
+            then To_Lower (Raw_Pattern)
+            else Raw_Pattern);
       begin
          --  Ignore blank lines
 
@@ -320,12 +325,16 @@ package body Inputs is
    end Create_Matcher;
 
    procedure Create_Matcher
-     (Pattern_List : Inputs.Inputs_Type;
-      Matcher      : out GNAT.Regexp.Regexp;
-      Has_Matcher  : out Boolean)
-   is
+     (Pattern_List     : Inputs.Inputs_Type;
+      Matcher          : out GNAT.Regexp.Regexp;
+      Has_Matcher      : out Boolean;
+      Case_Insensitive : Boolean := False) is
    begin
-      Create_Matcher (To_String_Vector (Pattern_List), Matcher, Has_Matcher);
+      Create_Matcher
+        (To_String_Vector (Pattern_List),
+         Matcher,
+         Has_Matcher,
+         Case_Insensitive);
    end Create_Matcher;
 
 end Inputs;
diff --git a/tools/gnatcov/inputs.ads b/tools/gnatcov/inputs.ads
index 00cfe7743..b53251ec3 100644
--- a/tools/gnatcov/inputs.ads
+++ b/tools/gnatcov/inputs.ads
@@ -80,17 +80,24 @@ package Inputs is
    --  Convert an inputs list into a String_Vectors.Vector
 
    procedure Create_Matcher
-     (Pattern_List : String_Vectors.Vector;
-      Matcher      : out GNAT.Regexp.Regexp;
-      Has_Matcher  : out Boolean);
+     (Pattern_List     : String_Vectors.Vector;
+      Matcher          : out GNAT.Regexp.Regexp;
+      Has_Matcher      : out Boolean;
+      Case_Insensitive : Boolean := False);
    --  If Pattern_List is empty, leave Matcher uninitialized and set
    --  Has_Matcher to False. Otherwise, set it to True and put in Matcher a
    --  pattern matching each of the globbing patterns in Pattern_List.
+   --
+   --  If Case_Insensitive is True, the returned matcher is made case
+   --  insensitive. Note that it may be case insensitive even if
+   --  Case_Insensitive is False (for instance on Windows, where all glob
+   --  patterns are interpreted as case insensitive).
 
    procedure Create_Matcher
-     (Pattern_List : Inputs.Inputs_Type;
-      Matcher      : out GNAT.Regexp.Regexp;
-      Has_Matcher  : out Boolean);
+     (Pattern_List     : Inputs.Inputs_Type;
+      Matcher          : out GNAT.Regexp.Regexp;
+      Has_Matcher      : out Boolean;
+      Case_Insensitive : Boolean := False);
    --  Overload to work on Inputs.Inputs_Type values
 
 private
diff --git a/tools/gnatcov/instrument-ada_unit.adb b/tools/gnatcov/instrument-ada_unit.adb
index d0686ccd8..a829bff01 100644
--- a/tools/gnatcov/instrument-ada_unit.adb
+++ b/tools/gnatcov/instrument-ada_unit.adb
@@ -18,6 +18,7 @@
 
 with Ada.Characters.Conversions;        use Ada.Characters.Conversions;
 with Ada.Characters.Handling;
+with Ada.Containers;             use Ada.Containers;
 with Ada.Exceptions;
 with Ada.Strings.Unbounded;             use Ada.Strings.Unbounded;
 with Ada.Strings.Wide_Wide_Fixed;
@@ -42,6 +43,7 @@ with Files_Table;         use Files_Table;
 with GNATcov_RTS.Buffers; use GNATcov_RTS.Buffers;
 with Namet;               use Namet;
 with Outputs;             use Outputs;
+with Project;             use Project;
 with SCOs;
 with Slocs;
 with Snames;              use Snames;
@@ -63,6 +65,11 @@ package body Instrument.Ada_Unit is
    --  and thus to check that it works as expected. This is the role of the
    --  following helpers.
 
+   function Format_Fingerprint
+     (Fingerprint : SC_Obligations.Fingerprint_Type) return String
+   is (Instrument.Common.Format_Fingerprint (Fingerprint, "(", ")"));
+   --  Helper to format a String literal for a fingerprint
+
    -------------------------------
    -- Create_Context_Instrument --
    -------------------------------
@@ -243,6 +250,9 @@ package body Instrument.Ada_Unit is
    --  Return a symbol from Symbols corresponding to the name of the given
    --  P pragma.
 
+   function Canonically_Equal (Left, Right : Text_Type) return Boolean;
+   --  Returns whether Left and Right are equal after canonicalization
+
    function Aspect_Assoc_Name (A : Aspect_Assoc) return Identifier;
    function Aspect_Assoc_Name (A : Aspect_Assoc) return Symbol_Type;
    function Aspect_Assoc_Name (A : Aspect_Assoc) return Name_Id;
@@ -285,13 +295,6 @@ package body Instrument.Ada_Unit is
    --  Wrapper around E.P_Expression_Type, logging a warning and returning
    --  Standard.Boolean if unable to determine the type.
 
-   function Referenced_Defining_Name
-     (Main_Name : Ada_Qualified_Name;
-      N         : LAL.Name) return Defining_Name;
-   --  Wrapper around Name.P_Referenced_Defining_Name, logging a warning and
-   --  returning No_Defining_Name if unable to determine the referenced
-   --  defining name.
-
    function Is_Static_Expr (E : Expr) return Boolean;
    --  Wrapper around E.P_Is_Static_Expr, logging a warning and returning
    --  False if unable to determine whether the expression is static.
@@ -947,6 +950,66 @@ package body Instrument.Ada_Unit is
    --  generated is a primitive of a tagged type, and if that tagged type is
    --  the return type of the expression function.
 
+   function Has_Matching_Pragma_For_Unit
+     (IC     : Inst_Context;
+      Unit   : Compilation_Unit;
+      Filter : access function (Node : Pragma_Node) return Boolean)
+      return Boolean;
+   --  Return whether Filter return True on at least one configuration pragma
+   --  that applies to Unit or system.ads.
+
+   Unusable_System_Reported : Boolean := False;
+   --  Global variable set to True once gnatcov emits a warning about a failure
+   --  to get the analysis unit for System. Used to avoid emitting duplicate
+   --  messages.
+
+   function Pragma_Restricts_Finalization
+     (Prag_Node : Pragma_Node) return Boolean;
+   --  Return True if Prag_Node imposes a restrictions on use of finalization
+
+   function Finalization_Restricted_In_Unit
+     (IC : Inst_Context; Unit : Compilation_Unit) return Boolean;
+   --  Return True if Finalization is not available in this runtime, or if
+   --  some control pragma restricts the usage of finalization in either Unit
+   --  or the whole project.
+
+   function Pragma_Prevents_Task_Termination
+     (Prag_Node : Pragma_Node) return Boolean;
+   --  Return True if Node prevents the use of tasks and/or
+   --  Ada.Task_Termination and/or Ada.Task_Identification.
+
+   function Task_Termination_Restricted
+     (IC : Inst_Context; Unit : Compilation_Unit) return Boolean;
+   --  Return True if tasking is not available in this runtime, or if some
+   --  configuration pragma prevents the use of tasks and/or
+   --  Ada.Task_Termination and/or Ada.Task_Identification in either the whole
+   --  project or in Unit.
+
+   function Return_From_Subp_Body
+     (Ret_Node : Return_Stmt; Subp : Subp_Body) return Boolean;
+   --  Return whether Ret_Node is returning from Subp
+
+   function Parent_Decl (Decl : Basic_Decl'Class) return Basic_Decl;
+   --  Return the parent declaration for Decl, or No_Basic_Decl if Decl has no
+   --  parent, or if we cannot find it.
+
+   procedure Insert_Simple_Dump_Proc_Calls
+     (RH          : Rewriting_Handle;
+      Helper_Unit : Ada_Qualified_Name;
+      Subp_Body   : LAL.Subp_Body);
+   --  Insert calls, in Subp_Body, to the <Helper_Unit>.Dump_Buffers procedure
+   --  as the last statment of the top level handeled statments of the main, as
+   --  the last statement of each exception handler branch, and right before
+   --  each return statment returning from the main procedure.
+
+   procedure Insert_Controlled_Dump_Object_Decl
+     (RH          : Rewriting_Handle;
+      Helper_Unit : Ada_Qualified_Name;
+      Subp_Body   : LAL.Subp_Body);
+   --  Insert, in Subp_Body, the declaration of a controlled object of type
+   --  <Helper_Unit>.Dump_Controlled_Type to dump the coverage buffers during
+   --  finalization of said object.
+
    --------------------------------
    -- Instrumentation extensions --
    --------------------------------
@@ -955,8 +1018,17 @@ package body Instrument.Ada_Unit is
      (UIC        : in out Ada_Unit_Inst_Context;
       Scope_Name : Text_Type;
       Sloc       : Source_Location);
-   --  Wrapper around Instrument.Common.Enter_Scope. See the documentation for
-   --  this function.
+   --  Enter a scope. This must be completed with a call to the function
+   --  Exit_Scope, defined below. Scope_Name is the name of the scope, which
+   --  is defined at location Sloc. Assume that the scope first SCO is the next
+   --  generated SCO (SCOs.SCO_Table.Last + 1). Update UIC.Current_Scope_Entity
+   --  to the created entity.
+
+   procedure Exit_Scope (UIC : in out Ada_Unit_Inst_Context)
+     with Pre => UIC.Current_Scope_Entity /= null;
+   --  Exit the current scope, updating UIC.Current_Scope_Entity to
+   --  UIC.Current_Scope_Entity.Parent, if any. Assume that the last generated
+   --  SCO (SCOs.SCO_Table.Last) is the last SCO for the current scope.
 
    ----------------------------
    -- Source level rewriting --
@@ -1005,14 +1077,22 @@ package body Instrument.Ada_Unit is
    --  Emit the unit to contain addresses for the coverage buffers
 
    procedure Emit_Dump_Helper_Unit
-     (IC          : Inst_Context;
-      Info        : in out Project_Info;
-      Main        : Compilation_Unit_Name;
-      Helper_Unit : out Ada_Qualified_Name);
+     (IC                    : Inst_Context;
+      Info                  : in out Project_Info;
+      Main                  : Compilation_Unit_Name;
+      Helper_Unit           : out Ada_Qualified_Name;
+      Override_Dump_Trigger : Any_Dump_Trigger := Manual;
+      Has_Controlled        : Boolean := False);
    --  Emit the unit to contain helpers to implement the automatic dump of
    --  coverage buffers for the given Main unit. Info must be the project that
    --  owns this main. Upon return, the name of this helper unit is stored in
    --  Helper_Unit.
+   --
+   --  If Override_Dump_Trigger is anything other than Manual, it will be used
+   --  as a dump trigger instead of the one defined in IC.Dump_Config.
+   --
+   --  If Has_Controlled is True, generate a controlled type for which the
+   --  Finalize procedure calls the buffer dump procedure.
 
    ----------------
    -- Convert_To --
@@ -1277,58 +1357,24 @@ package body Instrument.Ada_Unit is
       SD         : Source_Decision;
       Path_Count : Natural)
    is
-      LL_SCO_Id : Nat renames SD.LL_SCO;
-      N         : Expr renames SD.Decision;
+      N : Expr renames SD.Decision;
 
-      Bits : Decision_Bit_Ids;
       RH_P : constant Node_Rewriting_Handle :=
         Create_Node
           (IC.Rewriting_Context, Libadalang.Common.Ada_Identifier);
 
       RH_N : constant Node_Rewriting_Handle := Handle (N);
-   begin
-      Bits.LL_D_SCO := LL_SCO_Id;
-
-      --  Allocate outcome bits
-
-      Bits.Outcome_Bits :=
-        (False => IC.Unit_Bits.Last_Outcome_Bit + 1,
-         True  => IC.Unit_Bits.Last_Outcome_Bit + 2);
-      IC.Unit_Bits.Last_Outcome_Bit :=
-        IC.Unit_Bits.Last_Outcome_Bit + 2;
-
-      --  Allocate path bits for MC/DC if MC/DC is required and we were
-      --  able to generate a local state variable. If Path_Count is 0, this
-      --  means the BDD has more paths than the allowed limit, so do not
-      --  allocate any bit in the MC/DC buffers. The SCOs for the conditions
-      --  have already been emitted so this will generate MC/DC violations for
-      --  all of them.
-
-      if MCDC_Coverage_Enabled
-        and then Length (SD.State) > 0
-        and then Path_Count > 0
-      then
-         Bits.Path_Bits_Base := IC.Unit_Bits.Last_Path_Bit + 1;
-         IC.Unit_Bits.Last_Path_Bit :=
-           IC.Unit_Bits.Last_Path_Bit + Bit_Id (Path_Count);
-      else
-         Bits.Path_Bits_Base := No_Bit_Id;
-      end if;
 
-      if Path_Count = 0 then
-         Report (Node => N,
-                 Msg  => "Number of distinct paths in the decision exceeds"
-                         & " the limit ("
-                         & Img (SC_Obligations.Get_Path_Count_Limit)
-                         & "). MC/DC coverage for this decision will not be"
-                         & " assessed and will result in MC/DC violations."
-                         & " Use option --path-count-limit to adjust the"
-                         & " limit if the default value is too low.",
-                 Kind => Warning);
-      end if;
-
-      IC.Unit_Bits.Decision_Bits.Append (Bits);
+      --  Allocate bits for this decision in coverage buffers
 
+      Bits : constant Decision_Bit_Ids :=
+        Allocate_Decision_Bits
+          (IC.Unit_Bits,
+           (IC.SFI, +Sloc (SD.Decision)),
+           SD.LL_SCO,
+           SD.State,
+           Path_Count);
+   begin
       --  Detach original decision from tree so that it can be reattached
       --  inside the witness call.
 
@@ -2387,46 +2433,106 @@ package body Instrument.Ada_Unit is
       return False;
    end Return_Type_Is_Controlling;
 
+   ---------------------------
+   -- Return_From_Subp_Body --
+   ---------------------------
+
+   function Return_From_Subp_Body
+     (Ret_Node : Return_Stmt; Subp : Subp_Body) return Boolean
+   is
+      function Subp_Body_Is_Parent_Decl (Node : Ada_Node'Class) return Boolean;
+      --  Return True if Subp is the first body in the chain of parent
+      --  declarations of Node.
+
+      ------------------------------
+      -- Subp_Body_Is_Parent_Decl --
+      ------------------------------
+
+      function Subp_Body_Is_Parent_Decl (Node : Ada_Node'Class) return Boolean
+      is
+         Parent_Basic_Decl : constant Basic_Decl := Node.P_Parent_Basic_Decl;
+         --  Parent Basic_Decl of Ret_Node
+      begin
+         if Parent_Basic_Decl.Is_Null then
+            return False;
+         end if;
+         case Parent_Basic_Decl.Kind is
+            when Ada_Subp_Body_Range =>
+               return Parent_Basic_Decl.As_Subp_Body = Subp;
+
+            when Ada_Accept_Stmt
+               | Ada_Accept_Stmt_With_Stmts
+               | Ada_Entry_Body =>
+
+                  --  A return statment may only appear within a callable
+                  --  construct (RM 6.5 (4/2)), which are either subprogram
+                  --  bodies, entry bodies or accept statements (RM 6.2)). As
+                  --  Subp is a Subp_Body if we encounter an accept statement
+                  --  or an entry body on the way we can't be returning from
+                  --  Subp.
+
+                  return False;
+            when others =>
+               return Subp_Body_Is_Parent_Decl (Parent_Basic_Decl);
+         end case;
+      end Subp_Body_Is_Parent_Decl;
+
+   --  Start of processing for Return_From_Body
+
+   begin
+      return Subp_Body_Is_Parent_Decl (Ret_Node);
+   exception
+      when Exc : Property_Error =>
+         Report
+           (Node => Ret_Node,
+            Msg  => "Unable to determine to which body this return statment"
+                    & "applies: " & Ada.Exceptions.Exception_Information (Exc),
+            Kind => Low_Warning);
+
+         --  Inserting an extranous Dump_Buffer call isn't really a problem
+         --  as, at best the trace dump generated too early will be overwritten
+         --  by the trace dump happening at the main end, and at worst,
+         --  multiple trace files will coexist, and passing both to gnatcov
+         --  will result in the same coverage results as ony passing the trace
+         --  file that was created when dumping the buffers at the actual end
+         --  of the main. We can thus conservatively return True if some
+         --  property call fails.
+
+         return True;
+   end Return_From_Subp_Body;
+
+   -----------------
+   -- Parent_Decl --
+   -----------------
+
+   function Parent_Decl (Decl : Basic_Decl'Class) return Basic_Decl is
+   begin
+      return Decl.P_Parent_Basic_Decl;
+   exception
+      when Exc : Property_Error =>
+         Report
+           (Node => Decl,
+            Msg  => "Could not find the parent package: "
+                    & Ada.Exceptions.Exception_Information (Exc),
+            Kind => Warning);
+         return No_Basic_Decl;
+   end Parent_Decl;
+
    -----------------------------------------
    -- Traverse_Declarations_Or_Statements --
    -----------------------------------------
 
-   type Dominant_Info is record
-      K : Character;
-      --  F/T/S/E for a valid dominance marker, or ' ' for no dominant
-
-      N : Ada_Node;
-      --  Node providing the Sloc(s) for the dominance marker
-   end record;
-   No_Dominant : constant Dominant_Info := (' ', No_Ada_Node);
-
    procedure Traverse_Declarations_Or_Statements
      (IC                         : in out Inst_Context;
       UIC                        : in out Ada_Unit_Inst_Context;
       L                          : Ada_List'Class;
       Preelab                    : Boolean       := False;
-      D                          : Dominant_Info := No_Dominant;
-      P                          : Ada_Node      := No_Ada_Node;
-      Is_Select_Stmt_Alternative : Boolean       := False);
-   --  Process L, a list of statements or declarations dominated by D. If P is
-   --  present, it is processed as though it had been prepended to L. Preelab
-   --  is True if L is a list of preelaborable declarations (which do not
-   --  allow elaboration code, so do not require any SCOs, and wouldn't allow
-   --  insertion of witnesses).
-
-   function Traverse_Declarations_Or_Statements
-     (IC                         : in out Inst_Context;
-      UIC                        : in out Ada_Unit_Inst_Context;
-      L                          : Ada_List'Class;
-      Preelab                    : Boolean       := False;
-      D                          : Dominant_Info := No_Dominant;
       P                          : Ada_Node      := No_Ada_Node;
       Is_Select_Stmt_Alternative : Boolean       := False;
       Priv_Part                  : Private_Part  := No_Private_Part)
-      return Dominant_Info
      with Post => UIC.Current_Insertion_Info = UIC'Old.Current_Insertion_Info;
-   --  Process L, a list of statements or declarations dominated by D. If P is
-   --  present, it is processed as though it had been prepended to L.
+   --  Process L, a list of statements or declarations. If P is present, it is
+   --  processed as though it had been prepended to L.
    --
    --  Preelab is True if L is a list of preelaborable declarations (which do
    --  not allow elaboration code, so do not require any SCOs, and wouldn't
@@ -2439,16 +2545,11 @@ package body Instrument.Ada_Unit is
    --  If L is the list of declarations for a public part, Priv_Part is the
    --  corresponding private part (if any).
    --
-   --  Returns dominant information corresponding to the last node with SCO in
-   --  L.
-   --
    --  The postcondition ensures that the Current_Insertion_Info has been
    --  correctly reset to its value upon entry.
 
    --  The following Traverse_* routines perform appropriate calls to
    --  Traverse_Declarations_Or_Statements to traverse specific node kinds.
-   --  Parameter D, when present, indicates the dominant of the first
-   --  declaration or statement within N.
 
    procedure Traverse_Context_Clause
      (UIC             : in out Ada_Unit_Inst_Context;
@@ -2468,8 +2569,7 @@ package body Instrument.Ada_Unit is
    procedure Traverse_Handled_Statement_Sequence
      (IC  : in out Inst_Context;
       UIC : in out Ada_Unit_Inst_Context;
-      N   : Handled_Stmts;
-      D   : Dominant_Info := No_Dominant);
+      N   : Handled_Stmts);
 
    procedure Traverse_Package_Body
      (IC      : in out Inst_Context;
@@ -2481,14 +2581,12 @@ package body Instrument.Ada_Unit is
      (IC      : in out Inst_Context;
       UIC     : in out Ada_Unit_Inst_Context;
       N       : Base_Package_Decl;
-      Preelab : Boolean;
-      D       : Dominant_Info := No_Dominant);
+      Preelab : Boolean);
 
    procedure Traverse_Subprogram_Or_Task_Body
      (IC  : in out Inst_Context;
       UIC : in out Ada_Unit_Inst_Context;
-      N   : Ada_Node;
-      D   : Dominant_Info := No_Dominant);
+      N   : Ada_Node);
 
    procedure Traverse_Sync_Definition
      (IC  : in out Inst_Context;
@@ -2656,33 +2754,10 @@ package body Instrument.Ada_Unit is
       UIC                        : in out Ada_Unit_Inst_Context;
       L                          : Ada_List'Class;
       Preelab                    : Boolean       := False;
-      D                          : Dominant_Info := No_Dominant;
-      P                          : Ada_Node      := No_Ada_Node;
-      Is_Select_Stmt_Alternative : Boolean       := False)
-   is
-      Discard_Dom : Dominant_Info;
-      pragma Warnings (Off, Discard_Dom);
-   begin
-      Discard_Dom := Traverse_Declarations_Or_Statements
-        (IC, UIC, L, Preelab, D, P, Is_Select_Stmt_Alternative);
-   end Traverse_Declarations_Or_Statements;
-
-   function Traverse_Declarations_Or_Statements
-     (IC                         : in out Inst_Context;
-      UIC                        : in out Ada_Unit_Inst_Context;
-      L                          : Ada_List'Class;
-      Preelab                    : Boolean       := False;
-      D                          : Dominant_Info := No_Dominant;
       P                          : Ada_Node      := No_Ada_Node;
       Is_Select_Stmt_Alternative : Boolean       := False;
       Priv_Part                  : Private_Part  := No_Private_Part)
-     return Dominant_Info
    is
-      Current_Dominant : Dominant_Info := D;
-      --  Dominance information for the current basic block
-
-      Current_Test : Ada_Node;
-      --  Conditional node (IF statement or ELSIF part) being processed
 
       SC_First : constant Nat := SC.Last + 1;
       SD_First : constant Nat := SD.Last + 1;
@@ -2800,7 +2875,7 @@ package body Instrument.Ada_Unit is
                      To_Node := Stmt.F_Entry_Index_Expr.As_Ada_Node;
 
                   else
-                     To_Node := Stmt.F_Name.As_Ada_Node;
+                     To_Node := Stmt.F_Body_Decl.F_Name.As_Ada_Node;
                   end if;
                end;
 
@@ -2944,6 +3019,7 @@ package body Instrument.Ada_Unit is
             Insert_Pos  : Natural;
             Insert_Info : Insertion_Info_Access := UIC.Current_Insertion_Info;
 
+            Bit : Any_Bit_Id;
          begin
             --  Create an artificial internal error, if requested
 
@@ -2959,14 +3035,9 @@ package body Instrument.Ada_Unit is
                return;
             end if;
 
-            --  Allocate a bit in the statement coverage buffer, and record
-            --  its id in the bitmap.
+            --  Allocate a bit in the statement coverage buffer
 
-            UIC.Unit_Bits.Last_Statement_Bit :=
-              UIC.Unit_Bits.Last_Statement_Bit + 1;
-            UIC.Unit_Bits.Statement_Bits.Append
-              (Statement_Bit_Ids'
-                 (LL_SCO_Id, Executed => UIC.Unit_Bits.Last_Statement_Bit));
+            Bit := Allocate_Statement_Bit (UIC.Unit_Bits, LL_SCO_Id);
 
             case Insert_Info.Method is
 
@@ -2982,7 +3053,7 @@ package body Instrument.Ada_Unit is
                           Children =>
                             (1 => Make_Statement_Witness
                                (UIC,
-                                Bit        => UIC.Unit_Bits.Last_Statement_Bit,
+                                Bit        => Bit,
                                 Flavor     => Function_Call,
                                 In_Generic => SCE.In_Generic),
 
@@ -3120,7 +3191,7 @@ package body Instrument.Ada_Unit is
                      Child  =>
                        Make_Statement_Witness
                          (UIC,
-                          Bit        => UIC.Unit_Bits.Last_Statement_Bit,
+                          Bit        => Bit,
                           Flavor     =>
                             (case Insert_Info.Method is
                              when Statement => Procedure_Call,
@@ -3161,7 +3232,7 @@ package body Instrument.Ada_Unit is
                begin
                   Insert_Info.Witness_Actual := Make_Statement_Witness
                     (UIC,
-                     Bit        => UIC.Unit_Bits.Last_Statement_Bit,
+                     Bit        => Bit,
                      Flavor     => Function_Call,
                      In_Generic => SCE.In_Generic);
 
@@ -3186,30 +3257,6 @@ package body Instrument.Ada_Unit is
          --  Output statement entries from saved entries in SC table
 
          for J in SC_First .. SC_Last loop
-            --  If there is a pending dominant for this statement sequence,
-            --  emit a SCO for it.
-
-            if J = SC_First and then Current_Dominant /= No_Dominant then
-               declare
-                  SR   : constant Source_Location_Range :=
-                     Current_Dominant.N.Sloc_Range;
-                  From : constant Source_Location := Start_Sloc (SR);
-                  To   : Source_Location := Inclusive_End_Sloc (SR);
-
-               begin
-                  if Current_Dominant.K /= 'E' then
-                     To := No_Source_Location;
-                  end if;
-
-                  Append_SCO
-                    (C1   => '>',
-                     C2   => Current_Dominant.K,
-                     From => +From,
-                     To   => +To,
-                     Last => False);
-               end;
-            end if;
-
             declare
                SCE       : SC_Entry renames SC.Table (J);
                Dummy_Ctx : constant Context_Handle :=
@@ -3228,6 +3275,7 @@ package body Instrument.Ada_Unit is
                   C2                 => SCE.Typ,
                   From               => +SCE.From,
                   To                 => +SCE.To,
+                  SFI                => UIC.SFI,
                   Last               => (J = SC_Last),
                   Pragma_Aspect_Name => Pragma_Aspect_Name);
 
@@ -3267,13 +3315,6 @@ package body Instrument.Ada_Unit is
             end;
          end loop;
 
-         --  Last statement of basic block, if present, becomes new current
-         --  dominant.
-
-         if SC_Last >= SC_First then
-            Current_Dominant := ('S', SC.Table (SC_Last).N);
-         end if;
-
          --  Clear out used section of SC table
 
          SC.Set_Last (SC_First - 1);
@@ -3428,11 +3469,7 @@ package body Instrument.Ada_Unit is
                        F_Stmts      => Stmts_RH,
                        F_End_Name   => Proc_Name));
 
-            --  We are exiting the newly created regular function, so reset
-            --  dominant information.
-
             Set_Statement_Entry;
-            Current_Dominant := No_Dominant;
 
             UIC.Current_Insertion_Info := Saved_Insertion_Info;
             UIC.MCDC_State_Inserter := Saved_MCDC_State_Inserter;
@@ -3479,9 +3516,6 @@ package body Instrument.Ada_Unit is
          -- Local contexts for statement and MC/DC instrumentation --
          ------------------------------------------------------------
 
-         Saved_Dominant : constant Dominant_Info := Current_Dominant;
-         --  Save last statement in current sequence as dominant
-
          New_Insertion_Info : aliased Insertion_Info;
          --  Witness insertion info for statements (for both null procedures
          --  and expression functions).
@@ -3682,10 +3716,8 @@ package body Instrument.Ada_Unit is
          UIC.MCDC_State_Inserter := EF_Inserter'Unchecked_Access;
 
          --  Output statement SCO for degenerate subprogram body (null
-         --  statement or freestanding expression) outside of the dominance
-         --  chain.
+         --  statement or freestanding expression).
 
-         Current_Dominant := No_Dominant;
          if Is_Expr_Function then
             declare
                N_Expr : constant Expr := N.As_Expr_Function.F_Expr;
@@ -3716,12 +3748,6 @@ package body Instrument.Ada_Unit is
          end if;
          Set_Statement_Entry;
 
-         --  Restore current dominant information designating last statement
-         --  in previous sequence (i.e. make the dominance chain skip over
-         --  the degenerate body).
-
-         Current_Dominant := Saved_Dominant;
-
          --  Restore saved insertion context
 
          UIC.MCDC_State_Inserter := Saved_MCDC_State_Inserter;
@@ -3994,13 +4020,17 @@ package body Instrument.Ada_Unit is
                      begin
                         while AUN /= Std loop
                            declare
-                              Root_Decl : constant Basic_Decl :=
+                              Root_Decl   : constant Basic_Decl :=
                                 AUN.Root.As_Compilation_Unit
                                   .F_Body.As_Library_Item.F_Item;
+                              Parent      : constant Basic_Decl :=
+                                Parent_Decl (Root_Decl);
                            begin
                               UIC.Withed_Units.Include
                                 (Root_Decl.P_Canonical_Fully_Qualified_Name);
-                              AUN := Root_Decl.P_Parent_Basic_Decl.Unit;
+
+                              exit when Parent.Is_Null;
+                              AUN := Parent.Unit;
                            end;
                         end loop;
                      end;
@@ -4056,7 +4086,7 @@ package body Instrument.Ada_Unit is
             when Ada_Package_Decl =>
                Set_Statement_Entry;
                Traverse_Package_Declaration
-                 (IC, UIC, N.As_Base_Package_Decl, Preelab, Current_Dominant);
+                 (IC, UIC, N.As_Base_Package_Decl, Preelab);
 
             --  Generic package declaration
 
@@ -4122,23 +4152,15 @@ package body Instrument.Ada_Unit is
                declare
                   Cond : constant Expr := As_Entry_Body (N).F_Barrier;
 
-                  Inner_Dominant : Dominant_Info := No_Dominant;
-
                begin
                   Set_Statement_Entry;
 
                   if Switches.Analyze_Entry_Barriers and then not Cond.Is_Null
                   then
                      Process_Decisions_Defer (Cond, 'G');
-
-                     --  For an entry body with a barrier, the entry body
-                     --  is dominanted by a True evaluation of the barrier.
-
-                     Inner_Dominant := ('T', N);
                   end if;
 
-                  Traverse_Subprogram_Or_Task_Body
-                    (IC, UIC, N, Inner_Dominant);
+                  Traverse_Subprogram_Or_Task_Body (IC, UIC, N);
                end;
 
             --  Protected body
@@ -4160,15 +4182,6 @@ package body Instrument.Ada_Unit is
                begin
                   Process_Decisions_Defer (Cond, 'E');
                   Set_Statement_Entry;
-
-                  --  If condition is present, then following statement is
-                  --  only executed if the condition evaluates to False.
-
-                  if not Cond.Is_Null then
-                     Current_Dominant := ('F', N);
-                  else
-                     Current_Dominant := No_Dominant;
-                  end if;
                end;
 
             --  Label, which breaks the current statement sequence, but the
@@ -4177,7 +4190,6 @@ package body Instrument.Ada_Unit is
 
             when Ada_Label =>
                Set_Statement_Entry;
-               Current_Dominant := No_Dominant;
 
             --  Block statement, which breaks the current statement sequence
 
@@ -4185,13 +4197,8 @@ package body Instrument.Ada_Unit is
                Set_Statement_Entry;
 
                if N.Kind = Ada_Decl_Block then
-                  --  The first statement in the handled sequence of statements
-                  --  is dominated by the elaboration of the last declaration.
-
-                  Current_Dominant := Traverse_Declarations_Or_Statements
-                    (IC, UIC,
-                     L => As_Decl_Block (N).F_Decls.F_Decls,
-                     D => Current_Dominant);
+                  Traverse_Declarations_Or_Statements
+                    (IC, UIC, L => As_Decl_Block (N).F_Decls.F_Decls);
                end if;
 
                Traverse_Handled_Statement_Sequence
@@ -4199,14 +4206,12 @@ package body Instrument.Ada_Unit is
                   N => (case N.Kind is
                            when Ada_Decl_Block  => As_Decl_Block (N).F_Stmts,
                            when Ada_Begin_Block => As_Begin_Block (N).F_Stmts,
-                           when others          => raise Program_Error),
-                  D => Current_Dominant);
+                           when others          => raise Program_Error));
 
             --  If statement, which breaks the current statement sequence,
             --  but we include the condition in the current sequence.
 
             when Ada_If_Stmt =>
-               Current_Test := N;
                Extend_Statement_Sequence (UIC, N, 'I');
 
                declare
@@ -4220,65 +4225,40 @@ package body Instrument.Ada_Unit is
 
                   Traverse_Declarations_Or_Statements
                     (IC, UIC,
-                     L => If_N.F_Then_Stmts.As_Ada_Node_List,
-                     D => ('T', N));
+                     L => If_N.F_Then_Stmts.As_Ada_Node_List);
 
                   --  Loop through ELSIF parts if present
 
-                  declare
-                     Saved_Dominant : constant Dominant_Info :=
-                       Current_Dominant;
-
-                  begin
-                     for J in 1 .. If_N.F_Alternatives.Children_Count loop
-                        declare
-                           Elif : constant Elsif_Stmt_Part :=
-                             Alt.Child (J).As_Elsif_Stmt_Part;
-                        begin
-
-                           --  An Elsif is executed only if the previous test
-                           --  got a FALSE outcome.
-
-                           Current_Dominant := ('F', Current_Test);
-
-                           --  Now update current test information
-
-                           Current_Test := Ada_Node (Elif);
-
-                           --  We generate a statement sequence for the
-                           --  construct "ELSIF condition", so that we have
-                           --  a statement for the resulting decisions.
-
-                           Extend_Statement_Sequence
-                             (UIC,
-                              Ada_Node (Elif), 'I',
-                              Insertion_N         => Handle (Elif.F_Cond_Expr),
-                              Instrument_Location => Inside_Expr);
-                           Process_Decisions_Defer (Elif.F_Cond_Expr, 'I');
-                           Set_Statement_Entry;
-
-                           --  An ELSIF part is never guaranteed to have
-                           --  been executed, following statements are only
-                           --  dominated by the initial IF statement.
+                  for J in 1 .. If_N.F_Alternatives.Children_Count loop
+                     declare
+                        Elif : constant Elsif_Stmt_Part :=
+                          Alt.Child (J).As_Elsif_Stmt_Part;
+                     begin
+                        --  We generate a statement sequence for the construct
+                        --  "ELSIF condition", so that we have a statement for
+                        --  the resulting decisions.
 
-                           Current_Dominant := Saved_Dominant;
+                        Extend_Statement_Sequence
+                          (UIC,
+                           Ada_Node (Elif), 'I',
+                           Insertion_N         => Handle (Elif.F_Cond_Expr),
+                           Instrument_Location => Inside_Expr);
+                        Process_Decisions_Defer (Elif.F_Cond_Expr, 'I');
+                        Set_Statement_Entry;
 
-                           --  Traverse the statements in the ELSIF
+                        --  Traverse the statements in the ELSIF
 
-                           Traverse_Declarations_Or_Statements
-                             (IC, UIC,
-                              L => Elif.F_Stmts.As_Ada_Node_List,
-                              D => ('T', Ada_Node (Elif)));
-                        end;
-                     end loop;
-                  end;
+                        Traverse_Declarations_Or_Statements
+                          (IC, UIC,
+                           L => Elif.F_Stmts.As_Ada_Node_List);
+                     end;
+                  end loop;
 
                   --  Finally traverse the ELSE statements if present
 
                   Traverse_Declarations_Or_Statements
                     (IC, UIC,
-                     L => If_N.F_Else_Stmts.As_Ada_Node_List,
-                     D => ('F', Current_Test));
+                     L => If_N.F_Else_Stmts.As_Ada_Node_List);
                end;
 
             --  CASE statement, which breaks the current statement sequence,
@@ -4294,8 +4274,7 @@ package body Instrument.Ada_Unit is
                   Process_Decisions_Defer (Case_N.F_Expr, 'X');
                   Set_Statement_Entry;
 
-                  --  Process case branches, all of which are dominated by the
-                  --  CASE statement.
+                  --  Process case branches
 
                   for J in 1 .. Alt_L.Children_Count loop
                      declare
@@ -4304,8 +4283,7 @@ package body Instrument.Ada_Unit is
                      begin
                         Traverse_Declarations_Or_Statements
                           (IC, UIC,
-                           L => Alt.F_Stmts.As_Ada_Node_List,
-                           D => Current_Dominant);
+                           L => Alt.F_Stmts.As_Ada_Node_List);
                      end;
                   end loop;
                end;
@@ -4317,13 +4295,11 @@ package body Instrument.Ada_Unit is
                Set_Statement_Entry;
 
                if N.Kind = Ada_Accept_Stmt_With_Stmts then
-                  --  Process sequence of statements, dominant is the ACCEPT
-                  --  statement.
+                  --  Process sequence of statements
 
                   Traverse_Handled_Statement_Sequence
                     (IC, UIC,
-                     N => N.As_Accept_Stmt_With_Stmts.F_Stmts,
-                     D => Current_Dominant);
+                     N => N.As_Accept_Stmt_With_Stmts.F_Stmts);
                end if;
 
             --  SELECT statement
@@ -4336,7 +4312,6 @@ package body Instrument.Ada_Unit is
 
                declare
                   Sel_N : constant Select_Stmt := As_Select_Stmt (N);
-                  S_Dom : Dominant_Info;
                begin
                   for J in 1 .. Sel_N.F_Guards.Children_Count loop
                      declare
@@ -4344,24 +4319,19 @@ package body Instrument.Ada_Unit is
                           Sel_N.F_Guards.Child (J).As_Select_When_Part;
                         Guard : Expr;
                      begin
-                        S_Dom := Current_Dominant;
                         Guard := Alt.F_Cond_Expr;
 
                         if not Guard.Is_Null then
                            Process_Decisions (UIC, Guard, 'G');
-                           Current_Dominant := ('T', Ada_Node (Guard));
                         end if;
 
-                        --  Travrse the select_alternative,
+                        --  Traverse the select_alternative,
                         --  entry_call_alternative, or triggering_alternative.
 
                         Traverse_Declarations_Or_Statements
                           (IC, UIC,
                            L => Alt.F_Stmts.As_Ada_Node_List,
-                           D => Current_Dominant,
                            Is_Select_Stmt_Alternative => True);
-
-                        Current_Dominant := S_Dom;
                      end;
                   end loop;
 
@@ -4370,12 +4340,10 @@ package body Instrument.Ada_Unit is
 
                   Traverse_Declarations_Or_Statements
                     (IC, UIC,
-                     L => Sel_N.F_Else_Stmts.As_Ada_Node_List,
-                     D => Current_Dominant);
+                     L => Sel_N.F_Else_Stmts.As_Ada_Node_List);
                   Traverse_Declarations_Or_Statements
                     (IC, UIC,
-                     L => Sel_N.F_Abort_Stmts.As_Ada_Node_List,
-                     D => Current_Dominant);
+                     L => Sel_N.F_Abort_Stmts.As_Ada_Node_List);
                end;
 
             --  There is no SCO for a TERMINATE alternative in instrumentation
@@ -4395,7 +4363,6 @@ package body Instrument.Ada_Unit is
             =>
                Extend_Statement_Sequence (UIC, N, ' ');
                Set_Statement_Entry;
-               Current_Dominant := No_Dominant;
 
             --  Simple return statement. which is an exit point, but we
             --  have to process the return expression for decisions.
@@ -4405,7 +4372,6 @@ package body Instrument.Ada_Unit is
                Process_Decisions_Defer
                  (N.As_Return_Stmt.F_Return_Expr, 'X');
                Set_Statement_Entry;
-               Current_Dominant := No_Dominant;
 
             --  Extended return statement
 
@@ -4420,10 +4386,8 @@ package body Instrument.Ada_Unit is
 
                   Traverse_Handled_Statement_Sequence
                     (IC, UIC,
-                     N => ER_N.F_Stmts,
-                     D => Current_Dominant);
+                     N => ER_N.F_Stmts);
                end;
-               Current_Dominant := No_Dominant;
 
             --  Loop ends the current statement sequence, but we include
             --  the iteration scheme if present in the current sequence.
@@ -4432,10 +4396,8 @@ package body Instrument.Ada_Unit is
 
             when Ada_Base_Loop_Stmt =>
                declare
-                  Loop_S         : constant Base_Loop_Stmt :=
-                    N.As_Base_Loop_Stmt;
-                  ISC            : constant Loop_Spec := Loop_S.F_Spec;
-                  Inner_Dominant : Dominant_Info     := No_Dominant;
+                  Loop_S : constant Base_Loop_Stmt := N.As_Base_Loop_Stmt;
+                  ISC    : constant Loop_Spec := Loop_S.F_Spec;
 
                begin
                   if not ISC.Is_Null then
@@ -4451,12 +4413,6 @@ package body Instrument.Ada_Unit is
                         Process_Decisions_Defer
                           (ISC.As_While_Loop_Spec.F_Expr, 'W');
 
-                        --  Set more specific dominant for inner statements
-                        --  (the control sloc for the decision is that of
-                        --  the WHILE token).
-
-                        Inner_Dominant := ('T', Ada_Node (ISC));
-
                      --  FOR loop
 
                      else
@@ -4477,14 +4433,9 @@ package body Instrument.Ada_Unit is
 
                   Set_Statement_Entry;
 
-                  if Inner_Dominant = No_Dominant then
-                     Inner_Dominant := Current_Dominant;
-                  end if;
-
                   Traverse_Declarations_Or_Statements
                     (IC, UIC,
-                     L => Loop_S.F_Stmts.As_Ada_Node_List,
-                     D => Inner_Dominant);
+                     L => Loop_S.F_Stmts.As_Ada_Node_List);
                end;
 
             --  Pragma
@@ -4590,7 +4541,8 @@ package body Instrument.Ada_Unit is
                               end if;
 
                               UIC.Annotations.Append
-                                (Annotation_Couple'(+Sloc (N), Ann));
+                                (Annotation_Couple'
+                                   ((UIC.SFI, +Sloc (N)), Ann));
 
                            exception
                               when Constraint_Error =>
@@ -4821,7 +4773,6 @@ package body Instrument.Ada_Unit is
       --  Pop insertion info
 
       UIC.Current_Insertion_Info := Saved_Insertion_Info;
-      return Current_Dominant;
    end Traverse_Declarations_Or_Statements;
 
    -----------------------------
@@ -4934,8 +4885,7 @@ package body Instrument.Ada_Unit is
    procedure Traverse_Handled_Statement_Sequence
      (IC  : in out Inst_Context;
       UIC : in out Ada_Unit_Inst_Context;
-      N   : Handled_Stmts;
-      D   : Dominant_Info := No_Dominant)
+      N   : Handled_Stmts)
    is
    begin
       if N.Is_Null then
@@ -4943,7 +4893,7 @@ package body Instrument.Ada_Unit is
       end if;
 
       Traverse_Declarations_Or_Statements
-        (IC, UIC, L => N.F_Stmts.As_Ada_Node_List, D => D);
+        (IC, UIC, L => N.F_Stmts.As_Ada_Node_List);
 
       for J in 1 .. N.F_Exceptions.Children_Count loop
          declare
@@ -4954,8 +4904,7 @@ package body Instrument.Ada_Unit is
             if Handler.Kind = Ada_Exception_Handler then
                Traverse_Declarations_Or_Statements
                  (IC, UIC,
-                  L => Handler.As_Exception_Handler.F_Stmts.As_Ada_Node_List,
-                  D => ('E', Handler));
+                  L => Handler.As_Exception_Handler.F_Stmts.As_Ada_Node_List);
             end if;
          end;
       end loop;
@@ -4983,14 +4932,9 @@ package body Instrument.Ada_Unit is
          Sloc       => Sloc (N));
       UIC.MCDC_State_Inserter := Local_Inserter'Unchecked_Access;
 
-      --  The first statement in the handled sequence of statements is
-      --  dominated by the elaboration of the last declaration.
-
-      Traverse_Handled_Statement_Sequence
-        (IC, UIC,
-         N => N.F_Stmts,
-         D => Traverse_Declarations_Or_Statements
-                (IC, UIC, N.F_Decls.F_Decls, Preelab));
+      Traverse_Declarations_Or_Statements
+        (IC, UIC, N.F_Decls.F_Decls, Preelab);
+      Traverse_Handled_Statement_Sequence (IC, UIC, N => N.F_Stmts);
 
       UIC.MCDC_State_Inserter := Saved_MCDC_State_Inserter;
       Exit_Scope (UIC);
@@ -5005,11 +4949,8 @@ package body Instrument.Ada_Unit is
      (IC      : in out Inst_Context;
       UIC     : in out Ada_Unit_Inst_Context;
       N       : Base_Package_Decl;
-      Preelab : Boolean;
-      D       : Dominant_Info := No_Dominant)
+      Preelab : Boolean)
    is
-      Private_Part_Dominant : Dominant_Info;
-
       Saved_MCDC_State_Inserter : constant Any_MCDC_State_Inserter :=
         UIC.MCDC_State_Inserter;
       Local_Inserter            : aliased Default_MCDC_State_Inserter :=
@@ -5022,20 +4963,15 @@ package body Instrument.Ada_Unit is
          Sloc       => Sloc (N));
       UIC.MCDC_State_Inserter := Local_Inserter'Unchecked_Access;
 
-      Private_Part_Dominant :=
-        Traverse_Declarations_Or_Statements
-          (IC, UIC, N.F_Public_Part.F_Decls, Preelab, D,
-           Priv_Part => N.F_Private_Part);
+      Traverse_Declarations_Or_Statements
+        (IC, UIC, N.F_Public_Part.F_Decls, Preelab,
+         Priv_Part => N.F_Private_Part);
 
       if not N.F_Private_Part.Is_Null then
-
-         --  First private declaration is dominated by last visible declaration
-
          Traverse_Declarations_Or_Statements
            (IC, UIC,
             L       => N.F_Private_Part.F_Decls,
-            Preelab => Preelab,
-            D       => Private_Part_Dominant);
+            Preelab => Preelab);
       end if;
       UIC.MCDC_State_Inserter := Saved_MCDC_State_Inserter;
       Exit_Scope (UIC);
@@ -5051,10 +4987,6 @@ package body Instrument.Ada_Unit is
       UIC : in out Ada_Unit_Inst_Context;
       N   : Ada_Node)
    is
-      Dom_Info : Dominant_Info := ('S', N);
-      --  The first declaration is dominated by the protected or task [type]
-      --  declaration.
-
       Vis_Decl  : Public_Part := No_Public_Part;
       Priv_Decl : Private_Part := No_Private_Part;
       --  Visible and private declarations of the protected or task definition
@@ -5109,17 +5041,13 @@ package body Instrument.Ada_Unit is
       --  declarations. Querying F_Decls is invalid in this case.
 
       if not Vis_Decl.Is_Null then
-         Dom_Info := Traverse_Declarations_Or_Statements
-           (IC, UIC, L => Vis_Decl.F_Decls, D => Dom_Info,
-            Priv_Part => Priv_Decl);
+         Traverse_Declarations_Or_Statements
+           (IC, UIC, L => Vis_Decl.F_Decls, Priv_Part => Priv_Decl);
       end if;
 
       if not Priv_Decl.Is_Null then
-         --  If visible declarations are present, the first private declaration
-         --  is dominated by the last visible declaration.
-
          Traverse_Declarations_Or_Statements
-           (IC, UIC, L => Priv_Decl.F_Decls, D => Dom_Info);
+           (IC, UIC, L => Priv_Decl.F_Decls);
       end if;
    end Traverse_Sync_Definition;
 
@@ -5130,12 +5058,10 @@ package body Instrument.Ada_Unit is
    procedure Traverse_Subprogram_Or_Task_Body
      (IC  : in out Inst_Context;
       UIC : in out Ada_Unit_Inst_Context;
-      N   : Ada_Node;
-      D   : Dominant_Info := No_Dominant)
+      N   : Ada_Node)
    is
       Decls    : Declarative_Part;
       HSS      : Handled_Stmts;
-      Dom_Info : Dominant_Info    := D;
 
       Saved_MCDC_State_Inserter : constant Any_MCDC_State_Inserter :=
         UIC.MCDC_State_Inserter;
@@ -5183,13 +5109,9 @@ package body Instrument.Ada_Unit is
       Local_Inserter.Local_Decls := Handle (Decls.F_Decls);
       UIC.MCDC_State_Inserter := Local_Inserter'Unchecked_Access;
 
-      --  If declarations are present, the first statement is dominated by the
-      --  last declaration.
-
-      Dom_Info := Traverse_Declarations_Or_Statements
-        (IC, UIC, L => Decls.F_Decls, D => Dom_Info);
+      Traverse_Declarations_Or_Statements (IC, UIC, L => Decls.F_Decls);
 
-      Traverse_Handled_Statement_Sequence (IC, UIC, N => HSS, D => Dom_Info);
+      Traverse_Handled_Statement_Sequence (IC, UIC, N => HSS);
 
       Exit_Scope (UIC);
 
@@ -5352,6 +5274,7 @@ package body Instrument.Ada_Unit is
                C2   => C2,
                From => +Sloc (Op_N),
                To   => Slocs.No_Local_Location,
+               SFI  => UIC.SFI,
                Last => False);
 
             Hash_Entries.Append ((Sloc (N), SCOs.SCO_Table.Last));
@@ -5408,6 +5331,7 @@ package body Instrument.Ada_Unit is
             C2   => C2,
             From => +Start_Sloc (N_SR),
             To   => +Inclusive_End_Sloc (N_SR),
+            SFI  => UIC.SFI,
             Last => False);
          Hash_Entries.Append ((Start_Sloc (N_SR), SCOs.SCO_Table.Last));
       end Output_Element;
@@ -5487,6 +5411,7 @@ package body Instrument.Ada_Unit is
             C2                 => ' ',
             From               => +Loc,
             To                 => Slocs.No_Local_Location,
+            SFI                => UIC.SFI,
             Last               => False,
             Pragma_Aspect_Name => Nam);
 
@@ -5973,6 +5898,21 @@ package body Instrument.Ada_Unit is
 
    end Safe_Is_Ghost;
 
+   -----------------------
+   -- Canonically_Equal --
+   -----------------------
+
+   function Canonically_Equal (Left, Right : Text_Type) return Boolean is
+      Canonical_Left  : constant Symbolization_Result := Canonicalize (Left);
+      Canonical_Right : constant Symbolization_Result := Canonicalize (Right);
+   begin
+      --  If canonicalization failed for one of the two text types, assume they
+      --  are different.
+
+      return Canonical_Left.Success
+            and then Canonical_Left = Canonical_Right;
+   end Canonically_Equal;
+
    -----------------------
    -- Aspect_Assoc_Name --
    -----------------------
@@ -6086,44 +6026,6 @@ package body Instrument.Ada_Unit is
       end if;
    end Expression_Type;
 
-   ------------------------------
-   -- Referenced_Defining_Name --
-   ------------------------------
-
-   function Referenced_Defining_Name
-     (Main_Name : Ada_Qualified_Name;
-      N         : LAL.Name) return Defining_Name
-   is
-      DF  : Defining_Name;
-   begin
-      begin
-         DF := N.P_Referenced_Defining_Name;
-
-         if DF.Is_Null then
-            Report
-              (N,
-               "Failed to determine referenced defining name while "
-               & "processing the main " & To_Ada (Main_Name) & " (got null "
-               & "defining name)",
-               Warning);
-         end if;
-
-      exception
-         when Exc : Property_Error =>
-            Report
-              (N,
-               "Failed to determine referenced defining name while "
-               & "processing the main " & To_Ada (Main_Name) & ": "
-               & Ada.Exceptions.Exception_Information (Exc),
-               Warning);
-      end;
-      if not DF.Is_Null then
-         return DF;
-      else
-         return No_Defining_Name;
-      end if;
-   end Referenced_Defining_Name;
-
    --------------------
    -- Is_Static_Expr --
    --------------------
@@ -6247,14 +6149,54 @@ package body Instrument.Ada_Unit is
    procedure Enter_Scope
      (UIC        : in out Ada_Unit_Inst_Context;
       Scope_Name : Text_Type;
-      Sloc       : Source_Location) is
+      Sloc       : Source_Location)
+   is
+      New_Scope_Ent : constant Scope_Entity_Acc := new Scope_Entity'
+        (From     => SCO_Id (SCOs.SCO_Table.Last + 1),
+         To       => No_SCO_Id,
+         Name     => +Langkit_Support.Text.To_UTF8 (Scope_Name),
+         Sloc     => (Line => Natural (Sloc.Line),
+                       Column => Natural (Sloc.Column)),
+         Children => Scope_Entities_Vectors.Empty_Vector,
+         Parent   => UIC.Current_Scope_Entity);
    begin
-      Enter_Scope
-        (UIC,
-         +Langkit_Support.Text.To_UTF8 (Scope_Name),
-         (Line => Natural (Sloc.Line), Column => Natural (Sloc.Column)));
+      if UIC.Current_Scope_Entity /= null then
+         UIC.Current_Scope_Entity.Children.Append (New_Scope_Ent);
+      end if;
+      UIC.Current_Scope_Entity := New_Scope_Ent;
    end Enter_Scope;
 
+   ----------------
+   -- Exit_Scope --
+   ----------------
+
+   procedure Exit_Scope (UIC : in out Ada_Unit_Inst_Context)
+   is
+      Parent : Scope_Entity_Acc renames UIC.Current_Scope_Entity.Parent;
+      Scope  : Scope_Entity_Acc renames UIC.Current_Scope_Entity;
+   begin
+      --  Update the last SCO for this scope entity
+
+      Scope.To := SCO_Id (SCOs.SCO_Table.Last);
+
+      --  If the scope has no SCO (it could be possible for a package spec with
+      --  only subprogram declarations for instance), discard it.
+
+      if Scope.To < Scope.From then
+         if Parent /= null then
+            Parent.Children.Delete_Last;
+         end if;
+         Free (Scope);
+      end if;
+
+      --  If this is not the top-level scope (we want to keep its reference
+      --  after having traversed the AST), go up the scope tree.
+
+      if Parent /= null then
+         Scope := Parent;
+      end if;
+   end Exit_Scope;
+
    --------------------------
    -- Initialize_Rewriting --
    --------------------------
@@ -6292,6 +6234,278 @@ package body Instrument.Ada_Unit is
       end;
    end Initialize_Rewriting;
 
+   ----------------------------
+   -- Insert_Dump_Proc_Calls --
+   ----------------------------
+
+   procedure Insert_Simple_Dump_Proc_Calls
+     (RH          : Rewriting_Handle;
+      Helper_Unit : Ada_Qualified_Name;
+      Subp_Body   : LAL.Subp_Body)
+   is
+      --  Simply add a call to the buffer dump procedure at the end of
+      --  the top level statement list, as well as at the end of the
+      --  statement list of each top level exception handler, and
+      --  right before each return statment that applies to the main.
+
+      Dump_Procedure : Ada_Qualified_Name;
+      --  Name of the procedure to dump coverage buffers
+
+      Call_Stmt : Node_Rewriting_Handle;
+      --  Call invoking the dump procedure
+
+      function Process_Returns
+         (Node : Ada_Node'Class) return Visit_Status;
+      --  Helper for LAL's Traverse procedure. If node is a return
+      --  statement that returns from the main, insert a call to the
+      --  dump buffer procedure right before.
+
+      ---------------------
+      -- Process_Returns --
+      ---------------------
+
+      function Process_Returns (Node : Ada_Node'Class) return Visit_Status is
+      begin
+         if Node.Kind in Ada_Return_Stmt
+           and then Return_From_Subp_Body
+                      (Node.As_Return_Stmt, Subp_Body)
+         then
+            --  Child_Index is 0 based whereas Insert_Child is 1 based
+
+            Insert_Child
+              (Handle (Node.Parent),
+               Node.Child_Index + 1,
+               Clone (Call_Stmt));
+            return Over;
+         else
+            return Into;
+         end if;
+      end Process_Returns;
+
+      Handled_Stmt_List : constant Node_Rewriting_Handle :=
+        Handle (Subp_Body.F_Stmts.F_Stmts);
+
+   --  Start of processing for Insert_Simple_Dump_Proc_Calls
+
+   begin
+      Dump_Procedure := Helper_Unit;
+      Dump_Procedure.Append (Dump_Procedure_Name);
+      Call_Stmt := Create_Regular_Node
+        (RH, Ada_Call_Stmt, (1 => To_Nodes (RH, Dump_Procedure)));
+
+      --  Add a Dump_Buffer call at the end of the main's handeled statements
+
+      Append_Child (Handled_Stmt_List, Call_Stmt);
+
+      --  Add a Dump_Buffer call before any return statement that returns from
+      --  the main.
+
+      Traverse (Subp_Body, Process_Returns'Access);
+
+      if Is_Null (Subp_Body.F_Stmts.F_Exceptions) then
+         return;
+      end if;
+
+      --  Add a Dump_Buffer call as the last statement of each exception
+      --  handler branch.
+
+      for Node of Subp_Body.F_Stmts.F_Exceptions loop
+         if Node.Kind in Ada_Exception_Handler_Range then
+            declare
+               Exn_Handler   : constant Exception_Handler :=
+                 Node.As_Exception_Handler;
+               Exn_Stmt_List : constant Node_Rewriting_Handle :=
+                 Handle (Exn_Handler.F_Stmts);
+            begin
+               Append_Child (Exn_Stmt_List, Clone (Call_Stmt));
+            end;
+         end if;
+      end loop;
+   end Insert_Simple_Dump_Proc_Calls;
+
+   --------------------------------------
+   -- Insert_Controlled_Dump_Proc_Call --
+   --------------------------------------
+
+   procedure Insert_Controlled_Dump_Object_Decl
+     (RH          : Rewriting_Handle;
+      Helper_Unit : Ada_Qualified_Name;
+      Subp_Body   : LAL.Subp_Body)
+   is
+      --  Declare an object of the type <helper unit>.Dump_Controlled_Type
+      --  which will call the procedure Dump_Buffers when finalized.
+
+      Controlled_Type_Name : Ada_Qualified_Name := Helper_Unit;
+      Dump_Object_Decl     : Node_Rewriting_Handle;
+      Decl_List            : constant Node_Rewriting_Handle :=
+        Handle (Subp_Body.F_Decls.F_Decls);
+   begin
+      Controlled_Type_Name.Append
+        (To_Unbounded_String ("Dump_Controlled_Type"));
+      Dump_Object_Decl := Create_From_Template
+        (RH,
+         "GNATcov_Dump_Object : {};",
+         (1 => To_Nodes (RH, Controlled_Type_Name)),
+         Object_Decl_Rule);
+
+      --  Insert the declaration as the first declaration in the
+      --  list to ensure it is finalized last.
+
+      Insert_Child (Decl_List, 1, Dump_Object_Decl);
+   end Insert_Controlled_Dump_Object_Decl;
+
+   ----------------------------------
+   -- Has_Matching_Pragma_For_Unit --
+   ----------------------------------
+
+   function Has_Matching_Pragma_For_Unit
+     (IC     : Inst_Context;
+      Unit   : Compilation_Unit;
+      Filter : access function (Node : Pragma_Node) return Boolean)
+      return Boolean
+   is
+      System_Unit  : constant Analysis_Unit :=
+        IC.Context.Get_From_Provider ("System", Unit_Specification);
+      Unit_Pragmas : constant Pragma_Node_Array :=
+        Unit.P_All_Config_Pragmas
+        & (if System_Unit.Has_Diagnostics
+           then Pragma_Node_Array'(1 .. 0 => No_Pragma_Node)
+           else System_Unit.Root.As_Compilation_Unit.P_All_Config_Pragmas);
+      --  Configuration pragmas that apply to Unit. Also append the list of
+      --  configuration pragmas defined in System as they often define the set
+      --  of restrictions associated with the runtime.
+
+   begin
+      if not Unusable_System_Reported and then System_Unit.Has_Diagnostics
+      then
+         Diagnostics.Report
+           (Msg  => "Could not parse the System unit for the runtime,"
+                    & " instrumentation of mains may be incorrect:",
+            Kind => Low_Warning);
+         for Diag of System_Unit.Diagnostics loop
+            Diagnostics.Report
+              (Msg  => System_Unit.Format_GNU_Diagnostic (Diag),
+               Kind => Low_Warning);
+         end loop;
+         Unusable_System_Reported := True;
+      end if;
+      for Prag_Node of Unit_Pragmas loop
+         if Filter.all (Prag_Node) then
+            return True;
+         end if;
+      end loop;
+      return False;
+
+   end Has_Matching_Pragma_For_Unit;
+
+   -----------------------------------
+   -- Pragma_Restricts_Finalization --
+   -----------------------------------
+
+   function Pragma_Restricts_Finalization
+     (Prag_Node : Pragma_Node) return Boolean
+   is
+   begin
+      --  We are looking for pragmas of the form:
+      --     pragma Restrictions (No_Finalization);
+      --  or
+      --     pragma Restriction (No_Dependence => Ada.Finalization);
+
+      if Canonically_Equal (Prag_Node.F_Id.Text, "Restrictions") then
+         for Assoc of Prag_Node.F_Args loop
+            declare
+               Prag_Assoc : constant Pragma_Argument_Assoc :=
+                 Assoc.As_Pragma_Argument_Assoc;
+            begin
+               if not Prag_Assoc.F_Name.Is_Null
+                 and then
+                   (Canonically_Equal
+                      (Prag_Assoc.F_Name.Text, "No_Finalization")
+                    or else
+                      (Canonically_Equal
+                         (Prag_Assoc.F_Name.Text, "No_Dependence")
+                       and then Canonically_Equal (Prag_Assoc.F_Expr.Text,
+                                                   "Ada.Finalization")))
+               then
+                  return True;
+               end if;
+            end;
+         end loop;
+      end if;
+      return False;
+   end Pragma_Restricts_Finalization;
+
+   -------------------------------------
+   -- Finalization_Restricted_In_Unit --
+   -------------------------------------
+
+   function Finalization_Restricted_In_Unit
+     (IC : Inst_Context; Unit : Compilation_Unit) return Boolean
+   is
+   begin
+      return not Project.Runtime_Supports_Finalization
+            or else Has_Matching_Pragma_For_Unit
+                      (IC, Unit, Pragma_Restricts_Finalization'Access);
+   end Finalization_Restricted_In_Unit;
+
+   --------------------------------------
+   -- Pragma_Prevents_Task_Termination --
+   --------------------------------------
+
+   function Pragma_Prevents_Task_Termination
+     (Prag_Node : Pragma_Node) return Boolean
+   is
+   begin
+      --  We are looking for pragmas of the form:
+      --     pragma Restrictions (No_Tasking);
+      --  or
+      --     pragma Restriction (No_Dependence => Ada.Task_Termination);
+      --  or
+      --     pragma Restriction (No_Depenence => Ada.Task_Identification);
+
+      if Canonically_Equal (Prag_Node.F_Id.Text, "Restrictions") then
+         for Assoc of Prag_Node.F_Args loop
+            declare
+               Prag_Assoc : constant Pragma_Argument_Assoc :=
+                 Assoc.As_Pragma_Argument_Assoc;
+            begin
+               if not Prag_Assoc.F_Name.Is_Null
+                 and then
+                   (Canonically_Equal
+                      (Prag_Assoc.F_Name.Text, "No_Finalization")
+                    or else Canonically_Equal
+                      (Prag_Assoc.F_Name.Text, "No_Tasking")
+                    or else
+                      (Canonically_Equal
+                         (Prag_Assoc.F_Name.Text, "No_Dependence")
+                       and then
+                         (Canonically_Equal (Prag_Assoc.F_Expr.Text,
+                                             "Ada.Task_Termination")
+                          or else
+                            Canonically_Equal (Prag_Assoc.F_Expr.Text,
+                                               "Ada.Task_Identification"))))
+               then
+                  return True;
+               end if;
+            end;
+         end loop;
+      end if;
+      return False;
+   end Pragma_Prevents_Task_Termination;
+
+   ---------------------------------
+   -- Task_Termination_Restricted --
+   ---------------------------------
+
+   function Task_Termination_Restricted
+     (IC : Inst_Context; Unit : Compilation_Unit) return Boolean
+   is
+   begin
+      return not Project.Runtime_Supports_Task_Termination
+            or else Has_Matching_Pragma_For_Unit
+                      (IC, Unit, Pragma_Prevents_Task_Termination'Access);
+   end Task_Termination_Restricted;
+
    -------------------------------
    -- Auto_Dump_Buffers_In_Main --
    -------------------------------
@@ -6302,8 +6516,6 @@ package body Instrument.Ada_Unit is
       Main : Compilation_Unit_Name;
       URH  : Unit_Rewriting_Handle)
    is
-      No_Node : Node_Rewriting_Handle renames No_Node_Rewriting_Handle;
-
       U   : constant Analysis_Unit := Unit (URH);
       RH  : constant Rewriting_Handle := Handle (U.Context);
       Tmp : LAL.Ada_Node := U.Root;
@@ -6311,12 +6523,11 @@ package body Instrument.Ada_Unit is
       CU        : LAL.Compilation_Unit;
       Subp_Body : LAL.Subp_Body;
 
-      Old_Stmts, New_Stmts : Node_Rewriting_Handle;
+      Controlled_Types_Available : Boolean;
 
-      New_Stmt_List        : constant Node_Rewriting_Handle :=
-        Create_Node (RH, Ada_Stmt_List);
-      --  List of statements to contain 1) the original handled statements
-      --  (Old_Stmts) and 2) the call to the Write_Trace_File procedure.
+      Actual_Dump_Trigger : Auto_Dump_Trigger;
+      --  Resolved dump trigger after eventual override depending on the
+      --  features available on the runtime.
 
       Instr_Units : constant CU_Name_Vectors.Vector :=
         Instr_Units_For_Closure (IC, Main);
@@ -6352,9 +6563,25 @@ package body Instrument.Ada_Unit is
          Subp_Body := Tmp.As_Subp_Body;
       end if;
 
+      Controlled_Types_Available :=
+        not Finalization_Restricted_In_Unit (IC, CU);
+
+      Actual_Dump_Trigger :=
+        (if IC.Dump_Config.Trigger = Main_End
+           and then not Controlled_Types_Available
+           and then not Task_Termination_Restricted (IC, CU)
+         then Ravenscar_Task_Termination
+         else IC.Dump_Config.Trigger);
+
       --  Emit the helper unit and add a WITH clause for it
 
-      Emit_Dump_Helper_Unit (IC, Info, Main, Helper_Unit);
+      Emit_Dump_Helper_Unit
+        (IC,
+         Info,
+         Main,
+         Helper_Unit,
+         Override_Dump_Trigger => Actual_Dump_Trigger,
+         Has_Controlled        => Controlled_Types_Available);
 
       declare
          Prelude : constant Node_Rewriting_Handle := Handle (CU.F_Prelude);
@@ -6386,152 +6613,61 @@ package body Instrument.Ada_Unit is
          Append_Child (Prelude, Runtime_Version_Check_Node);
       end;
 
-      --  Wrap the previous subprogram body content (declarations, handled
-      --  statements) in a declare block. This is a simple handled statements
-      --  block if there is no declaration.
-
-      declare
-         New_Excs : constant Node_Rewriting_Handle :=
-           Create_Node (RH, Ada_Ada_Node_List);
-
-         Nested_Block : Node_Rewriting_Handle;
-         Nested_Decls : Node_Rewriting_Handle;
-
-         Main_Name  : constant LAL.Defining_Name :=
-           Subp_Body.F_Subp_Spec.F_Subp_Name;
-         Block_Name : constant Node_Rewriting_Handle :=
-           Create_Identifier (RH, "GNATcov_Original_Main");
-
-         function Replace_FQNs
-           (Node : LAL.Ada_Node'Class) return Visit_Status;
-         --  Callback for Libadalang's Traverse. If Node is a dotted name whose
-         --  prefix refers to Main, rewrite the prefix to refer to the named
-         --  block we create here.
-
-         -----------------
-         -- Replace_FQN --
-         -----------------
-
-         function Replace_FQNs
-           (Node : LAL.Ada_Node'Class) return Visit_Status
-         is
-            Name : Dotted_Name;
-         begin
-            if Node.Kind /= Ada_Dotted_Name then
-               return Into;
-            end if;
-            Name := Node.As_Dotted_Name;
-            if Referenced_Defining_Name (Main.Unit, Name.F_Prefix) = Main_Name
-            then
-               declare
-                  Old_Prefix : constant Node_Rewriting_Handle :=
-                    Handle (Name.F_Prefix);
-                  New_Node   : constant Node_Rewriting_Handle :=
-                    Create_Regular_Node
-                      (RH,
-                       Ada_Dotted_Name,
-                       (No_Node_Rewriting_Handle,
-                        Clone (Block_Name)));
-               begin
-                  Replace (Old_Prefix, New_Node);
-                  Set_Child (Handle => New_Node,
-                             Index  => 1,
-                             Child  => Old_Prefix);
-                  return Over;
-               end;
-            end if;
-            return Into;
-         end Replace_FQNs;
-
-      begin
-         --  Extract the original statements (Old_Stmts) and replace it in the
-         --  subprogram body with the new statements.
-
-         Old_Stmts := Handle (Subp_Body.F_Stmts);
-         New_Stmts := Create_Regular_Node
-           (RH, Ada_Handled_Stmts, (New_Stmt_List, New_Excs));
-         Replace (Old_Stmts, New_Stmts);
-
-         --  If the original subprogram has declarations or exception handlers,
-         --  wrap the original statements in a named block to hold them.
-
-         if Subp_Body.F_Decls.F_Decls.Children_Count = 0
-            and then Subp_Body.F_Stmts.F_Exceptions.Children_Count = 0
-         then
-            Nested_Block := Old_Stmts;
-         else
-            Nested_Decls := Handle (Subp_Body.F_Decls);
-            Replace
-              (Nested_Decls,
-               Create_Regular_Node
-                 (RH, Ada_Declarative_Part,
-                  (1 => Create_Node (RH, Ada_Ada_Node_List))));
-
-            Nested_Block := Create_Named_Stmt
-              (RH,
-               F_Decl => Create_Named_Stmt_Decl
-                 (RH, Create_Defining_Name (RH, Clone (Block_Name)), No_Node),
-               F_Stmt => Create_Decl_Block
-                 (RH,
-                  F_Decls    => Nested_Decls,
-                  F_Stmts    => Old_Stmts,
-                  F_End_Name => Clone (Block_Name)));
-
-            --  Change the Qualified names in the Main's declarations and
-            --  statements to be compatible ith the new nested block.
-            Subp_Body.F_Stmts.Traverse (Replace_FQNs'Access);
-            Subp_Body.F_Decls.Traverse (Replace_FQNs'Access);
-
-         end if;
-         Append_Child (New_Stmt_List, Nested_Block);
-      end;
-
       --  Depending on the chosen coverage buffers dump trigger, insert the
       --  appropriate code.
 
-      case Auto_Dump_Trigger (IC.Dump_Config.Trigger) is
+      case Actual_Dump_Trigger is
 
       when At_Exit | Ravenscar_Task_Termination =>
 
-         --  Build the call to the registration procedure and insert it in
-         --  New_Stmt_List, right before the old list of statements.
+         --  Build the call to the registration function and insert it in
+         --  the main's declarative part, right before the first declaration.
 
          declare
-            Register_Procedure : Ada_Qualified_Name;
-            --  Name of the procedure to register the coverage buffers dump
+            Register_Function : Ada_Qualified_Name;
+            --  Name of the function to register the coverage buffers dump
             --  routine.
 
-            Call_Stmt : Node_Rewriting_Handle;
-
+            Call_Expr : Node_Rewriting_Handle;
+            Call_Decl : Node_Rewriting_Handle;
+            Decl_List : constant Node_Rewriting_Handle :=
+              Handle (Subp_Body.F_Decls.F_Decls);
          begin
-            Register_Procedure := Helper_Unit;
-            Register_Procedure.Append (Register_Dump_Procedure_Name);
+            Register_Function := Helper_Unit;
+            Register_Function.Append (Register_Dump_Function_Name);
+            Call_Expr := To_Nodes (RH, Register_Function);
 
-            Call_Stmt := Create_Regular_Node
-              (RH, Ada_Call_Stmt, (1 => To_Nodes (RH, Register_Procedure)));
-            Insert_Child (New_Stmt_List, 1, Call_Stmt);
+            Call_Decl := Create_From_Template
+              (RH,
+               "Autodump_Dummy : {} := {};",
+               (1 => To_Nodes (RH, Witness_Dummy_Type_Name), 2 => Call_Expr),
+               Object_Decl_Rule);
+            Insert_Child (Decl_List, 1, Call_Decl);
          end;
 
       when Main_End =>
 
-         --  Build the call to the dump procedure and append it to
-         --  New_Stmt_List, right after the old list of statements.
-
-         declare
-            Dump_Procedure : Ada_Qualified_Name;
-            --  Name of the procedure to dump coverage buffers
-
-            Call_Stmt : Node_Rewriting_Handle;
-
-         begin
-            Dump_Procedure := Helper_Unit;
-            Dump_Procedure.Append (Dump_Procedure_Name);
-
-            Call_Stmt := Create_Regular_Node
-              (RH, Ada_Call_Stmt, (1 => To_Nodes (RH, Dump_Procedure)));
-            Append_Child (New_Stmt_List, Call_Stmt);
-         end;
+         --  For Main_End we have three instrumentation schemes depending on
+         --  the availability of tasks and controlled types:
+         --
+         --  - If controlled types are available use a controlled object to
+         --    dump the buffers as part of its finalization.
+         --
+         --  - If controlled types are unavailable but task types are, then
+         --    use the Ravenscar_Task_Termination dump trigger as we otherwise
+         --    have no instrumentation method that works with all code
+         --    constructs. In that case, Actuall_Dump_Trigger is set to
+         --    Ravenscar_Task_Termination and we should not reach this part of
+         --    the code.
+         --
+         --  - If we do not have finalization or tasks then simply insert calls
+         --    right before all the exit points of the main.
 
+         if not Finalization_Restricted_In_Unit (IC, CU) then
+            Insert_Controlled_Dump_Object_Decl (RH, Helper_Unit, Subp_Body);
+         else
+            Insert_Simple_Dump_Proc_Calls (RH, Helper_Unit, Subp_Body);
+         end if;
       end case;
    end Auto_Dump_Buffers_In_Main;
 
@@ -6626,11 +6762,19 @@ package body Instrument.Ada_Unit is
         (Filename,
          Kind                => Files_Table.Source_File,
          Indexed_Simple_Name => True);
+      UIC.Unit_Bits.SFI := UIC.SFI;
 
       --  Then run SCOs generation. This inserts calls to witness
       --  procedures/functions in the same pass.
 
       SCOs.Initialize;
+
+      --  Append an entry in the SCO_Unit_Table for this unit to preserve
+      --  invariants in our data structures and subsequently avoiding to have
+      --  UIC.CU set to No_CU_Id.
+
+      Append_Unit (UIC.SFI);
+
       Traverse_Declarations_Or_Statements
         (IC      => IC,
          UIC     => UIC,
@@ -6638,13 +6782,6 @@ package body Instrument.Ada_Unit is
          Preelab => Preelab,
          P       => Rewriter.Rewritten_Unit.Root);
 
-      SCOs.SCO_Unit_Table.Append
-        ((File_Name  => new String'(Filename),
-          File_Index => UIC.SFI,
-          Dep_Num    => 1,
-          From       => SCOs.SCO_Table.First,
-          To         => SCOs.SCO_Table.Last));
-
       --  Convert low level SCOs from the instrumenter to high level SCOs.
       --  This creates BDDs for every decision.
 
@@ -6668,7 +6805,7 @@ package body Instrument.Ada_Unit is
 
          --  Import annotations in our internal tables
 
-         UIC.Import_Annotations;
+         UIC.Import_Annotations (Created_Units);
 
          --  Import non-instrumented SCOs in the internal tables
 
@@ -6714,18 +6851,26 @@ package body Instrument.Ada_Unit is
                --  static decision, as this would make the instrumented
                --  expression non-static. Mark the enclosing decision as not
                --  instrumented for MCDC instead.
+               --
+               --  As we go through each condition, mark their enclosing
+               --  decision as not instrumented if their number of paths
+               --  exceeds our limit.
 
                for SC of UIC.Source_Conditions loop
-                  if Path_Count
-                       (Enclosing_Decision (SCO_Map (SC.LL_SCO))) /= 0
-                    and then not SC.Decision_Static
-                  then
-                     Insert_Condition_Witness
-                       (UIC, SC, Offset_For_True (SCO_Map (SC.LL_SCO)));
-                  else
-                     Set_Decision_SCO_Non_Instr_For_MCDC
-                       (Enclosing_Decision (SCO_Map (SC.LL_SCO)));
-                  end if;
+                  declare
+                     Condition : constant SCO_Id := SCO_Map (SC.LL_SCO);
+                     Decision  : constant SCO_Id :=
+                       Enclosing_Decision (Condition);
+                  begin
+                     if Path_Count (Decision) /= 0
+                        and then not SC.Decision_Static
+                     then
+                        Insert_Condition_Witness
+                          (UIC, SC, Offset_For_True (Condition));
+                     else
+                        Set_Decision_SCO_Non_Instr_For_MCDC (Decision);
+                     end if;
+                  end;
                end loop;
             end if;
          end if;
@@ -6781,30 +6926,11 @@ package body Instrument.Ada_Unit is
           --  See the comment for Scope_Entity.From/To.
 
          if UIC.Current_Scope_Entity /= null then
-            declare
-               use Scope_Entities_Vectors;
-
-               procedure Remap_SCOs (Scope_Entity : Scope_Entity_Acc);
 
-               ----------------
-               -- Remap_SCOs --
-               ----------------
+            --  Iterate through the package level body entities
 
-               procedure Remap_SCOs (Scope_Entity : Scope_Entity_Acc) is
-               begin
-                  Scope_Entity.From := SCO_Map (Nat (Scope_Entity.From));
-                  Scope_Entity.To := SCO_Map (Nat (Scope_Entity.To));
-                  for Child of Scope_Entity.Children loop
-                     Remap_SCOs (Child);
-                  end loop;
-               end Remap_SCOs;
-
-            begin
-               --  Iterate through the package level body entities
-
-               Remap_SCOs (UIC.Current_Scope_Entity);
-               Set_Scope_Entity (UIC.CU, UIC.Current_Scope_Entity);
-            end;
+            Remap_Scope_Entity (UIC.Current_Scope_Entity, SCO_Map);
+            Set_Scope_Entity (UIC.CU, UIC.Current_Scope_Entity);
          end if;
       end;
 
@@ -6868,20 +6994,25 @@ package body Instrument.Ada_Unit is
                    To_Filename (Info.Project, CU_Name, Switches.Ada_Language));
       Put_Warnings_And_Style_Checks_Pragmas (File);
       File.Put_Line ("with Interfaces.C; use Interfaces.C;");
+      File.Put_Line
+        ("with GNATcov_RTS.Buffers.Lists; use GNATcov_RTS.Buffers.Lists;");
 
       declare
          Pkg_Name : constant String := To_Ada (CU_Name.Unit);
+         --  Package name for the buffer unit
 
          Fingerprint : Unbounded_String;
+         --  Fingerprint for the instrumented unit
 
          Unit_Name : constant String := Ada.Characters.Handling.To_Lower
            (To_Ada (UIC.Instrumented_Unit.Unit));
+         --  Lower-case name for the instrumented unit
 
          Unit_Part : constant String :=
-              (case UIC.Instrumented_Unit.Part is
-                  when GPR.Unit_Spec     => "Unit_Spec",
-                  when GPR.Unit_Body     => "Unit_Body",
-                  when GPR.Unit_Separate => "Unit_Separate");
+           (case UIC.Instrumented_Unit.Part is
+               when GPR.Unit_Spec     => "Unit_Spec",
+               when GPR.Unit_Body     => "Unit_Body",
+               when GPR.Unit_Separate => "Unit_Separate");
          --  Do not use 'Image so that we use the original casing for the
          --  enumerators, and thus avoid compilation warnings/errors.
 
@@ -6914,7 +7045,10 @@ package body Instrument.Ada_Unit is
          File.New_Line;
          File.Put_Line ("   pragma Preelaborate;");
          File.New_Line;
-         File.Put_Line ("use type Interfaces.C.int;");
+
+         --  Create declarations for individual buffers (statement, decision
+         --  and MC/DC) as well as their exported addresses.
+
          File.Put_Line ("   Statement_Buffer : Coverage_Buffer_Type"
                         & " (0 .. " & Statement_Last_Bit & ") :="
                         & " (others => False);");
@@ -6945,15 +7079,18 @@ package body Instrument.Ada_Unit is
                         & """);");
          File.New_Line;
 
+         --  Create the GNATcov_RTS_Coverage_Buffers record
+
          File.Put_Line ("   Unit_Name : constant String := """ & Unit_Name
                         & """;");
          File.Put_Line ("   Project_Name : constant String := """";");
          File.New_Line;
 
-         File.Put_Line ("   Buffers : aliased"
-                        & " GNATcov_RTS_Unit_Coverage_Buffers :=");
-         File.Put_Line ("     (Fingerprint => "
-                        & To_String (Fingerprint) & ",");
+         File.Put_Line ("   Buffers : aliased constant"
+                        & " GNATcov_RTS_Coverage_Buffers :=");
+         File.Put_Line
+           ("     (Fingerprint => "
+            & Format_Fingerprint (SC_Obligations.Fingerprint (UIC.CU)) & ",");
 
          File.Put_Line ("      Language  => Unit_Based_Language,");
          File.Put_Line ("      Unit_Part => " & Unit_Part & ",");
@@ -6963,6 +7100,11 @@ package body Instrument.Ada_Unit is
          File.Put_Line ("      Project_Name =>"
                         & " (Project_Name'Address, Project_Name'Length),");
 
+         File.Put_Line
+           ("      Bit_Maps_Fingerprint => "
+            & Format_Fingerprint (SC_Obligations.Bit_Maps_Fingerprint (UIC.CU))
+            & ",");
+
          File.Put_Line ("      Statement => Statement_Buffer'Address,");
          File.Put_Line ("      Decision  => Decision_Buffer'Address,");
          File.Put_Line ("      MCDC      => MCDC_Buffer'Address,");
@@ -6972,9 +7114,21 @@ package body Instrument.Ada_Unit is
          File.Put_Line ("      Decision_Last_Bit => " & Decision_Last_Bit
                         & ",");
          File.Put_Line ("      MCDC_Last_Bit => " & MCDC_Last_Bit & ");");
-         File.Put_Line ("      pragma Export (C, Buffers, """
+         File.New_Line;
+
+         --  Create the buffers group
+
+         File.Put_Line
+           ("   Buffers_Group : aliased constant Coverage_Buffers_Group :="
+            & " (1 => Buffers'Access);");
+         File.Put_Line
+           ("   C_Buffers_Group : aliased constant"
+            & " GNATcov_RTS_Coverage_Buffers_Group :="
+            & " (1, Buffers_Group'Address);");
+         File.Put_Line ("      pragma Export (C, C_Buffers_Group, """
                         & Unit_Buffers_Name (UIC.Instrumented_Unit) & """);");
          File.New_Line;
+
          File.Put_Line ("end " & Pkg_Name & ";");
       end;
    end Emit_Buffer_Unit;
@@ -7087,10 +7241,12 @@ package body Instrument.Ada_Unit is
    ---------------------------
 
    procedure Emit_Dump_Helper_Unit
-     (IC          : Inst_Context;
-      Info        : in out Project_Info;
-      Main        : Compilation_Unit_Name;
-      Helper_Unit : out Ada_Qualified_Name)
+     (IC                    : Inst_Context;
+      Info                  : in out Project_Info;
+      Main                  : Compilation_Unit_Name;
+      Helper_Unit           : out Ada_Qualified_Name;
+      Override_Dump_Trigger : Any_Dump_Trigger := Manual;
+      Has_Controlled        : Boolean := False)
    is
       File : Text_Files.File_Type;
 
@@ -7110,9 +7266,11 @@ package body Instrument.Ada_Unit is
       --  Qualified names for the unit that contains the buffer output
       --  procedure, and for the procedure itself.
 
-      Dump_Trigger : constant Auto_Dump_Trigger := IC.Dump_Config.Trigger;
-      --  Shortcut to avoid repeatedly restricting the dump trigger to the
-      --  Auto_Dump_Trigger subtype.
+      Dump_Trigger : constant Auto_Dump_Trigger :=
+        (if Override_Dump_Trigger = Manual
+         then IC.Dump_Config.Trigger
+         else Override_Dump_Trigger);
+      --  Resolved dump trigger
 
    --  Start of processing for Emit_Dump_Helper_Unit
 
@@ -7158,20 +7316,33 @@ package body Instrument.Ada_Unit is
                  Switches.Ada_Language));
 
          Put_Warnings_And_Style_Checks_Pragmas (File);
+         Put_With (Sys_Buffers);
+         if Dump_Trigger = Main_End and then Has_Controlled then
+            File.Put_Line ("with Ada.Finalization;");
+         end if;
          File.Put_Line ("package " & Helper_Unit_Name & " is");
          File.New_Line;
          File.Put_Line ("   procedure " & Dump_Procedure & ";");
          File.Put_Line ("   pragma Convention (C, " & Dump_Procedure & ");");
+         File.New_Line;
 
          case Dump_Trigger is
             when At_Exit | Ravenscar_Task_Termination =>
                File.Put_Line
-                 ("procedure "
-                  & To_String (Register_Dump_Procedure_Name) & ";");
+                 ("function "
+                  & To_String (Register_Dump_Function_Name) & " return "
+                  & To_Ada (Witness_Dummy_Type_Name) & ";");
                File.New_Line;
 
             when Main_End =>
-               null;
+               if Has_Controlled then
+                  File.Put_Line ("   type Dump_Controlled_Type is new"
+                                 & " Ada.Finalization.Controlled with");
+                  File.Put_Line ("     null record;");
+                  File.Put_Line ("   overriding procedure Finalize (Self : in"
+                                 & " out Dump_Controlled_Type);");
+                  File.New_Line;
+               end if;
          end case;
 
          File.Put_Line ("end " & Helper_Unit_Name & ";");
@@ -7270,16 +7441,17 @@ package body Instrument.Ada_Unit is
          File.Put_Line ("   end " & Dump_Procedure & ";");
          File.New_Line;
 
-         --  Emit trigger-specific procedures
+         --  Emit trigger-specific subprograms
 
          case Dump_Trigger is
             when At_Exit =>
 
-               --  Emit a procedure to schedule a trace dump with atexit
+               --  Emit a function to schedule a trace dump with atexit
 
                File.Put_Line
-                 ("procedure "
-                  & To_String (Register_Dump_Procedure_Name) & " is");
+                 ("function "
+                  & To_String (Register_Dump_Function_Name) & " return "
+                  & To_Ada (Witness_Dummy_Type_Name) & " is");
                File.Put_Line ("   type Callback is access procedure;");
                File.Put_Line ("   pragma Convention (C, Callback);");
                File.New_Line;
@@ -7289,9 +7461,9 @@ package body Instrument.Ada_Unit is
                File.Put_Line ("   Dummy : constant Interfaces.C.int :=");
                File.Put_Line ("     atexit (" & Dump_Procedure & "'Access);");
                File.Put_Line ("begin");
-               File.Put_Line ("   null;");
+               File.Put_Line ("   return (Data => False);");
                File.Put_Line
-                 ("end " & To_String (Register_Dump_Procedure_Name) & ";");
+                 ("end " & To_String (Register_Dump_Function_Name) & ";");
                File.New_Line;
 
             when Ravenscar_Task_Termination =>
@@ -7313,22 +7485,31 @@ package body Instrument.Ada_Unit is
                File.Put_Line ("  end Wrapper;");
                File.New_Line;
 
-               --  Emit a procedure to schedule a trace dump with
+               --  Emit a function to schedule a trace dump with
                --  Ada.Task_Termination.
 
                File.Put_Line
-                 ("procedure "
-                  & To_String (Register_Dump_Procedure_Name) & " is");
+                 ("function "
+                  & To_String (Register_Dump_Function_Name) & " return "
+                  & To_Ada (Witness_Dummy_Type_Name) & " is");
                File.Put_Line ("begin");
                File.Put_Line ("   Ada.Task_Termination"
                               & ".Set_Dependents_Fallback_Handler"
                               & " (Wrapper.Do_Dump'Access);");
+               File.Put_Line ("   return (Data => False);");
                File.Put_Line
-                 ("end " & To_String (Register_Dump_Procedure_Name) & ";");
+                 ("end " & To_String (Register_Dump_Function_Name) & ";");
                File.New_Line;
 
             when Main_End =>
-               null;
+               if Has_Controlled then
+                  File.Put_Line ("   overriding procedure Finalize (Self : in"
+                                  & " out Dump_Controlled_Type) is");
+                  File.Put_Line ("   begin");
+                  File.Put_Line ("      Dump_Buffers;");
+                  File.Put_Line ("   end Finalize;");
+                  File.New_Line;
+               end if;
          end case;
 
          File.Put_Line ("end " & Helper_Unit_Name & ";");
@@ -7391,6 +7572,8 @@ package body Instrument.Ada_Unit is
          File.New_Line;
          File.Put_Line ("package " & Unit_Name & " is");
          File.New_Line;
+         File.Put_Line ("   pragma Preelaborate;");
+         File.New_Line;
 
          --  Import all the coverage buffers
 
@@ -7400,7 +7583,7 @@ package body Instrument.Ada_Unit is
             begin
                File.Put_Line
                  ("   " & Buffer_Name
-                  & " : aliased GNATcov_RTS_Unit_Coverage_Buffers;");
+                  & " : aliased constant GNATcov_RTS_Coverage_Buffers_Group;");
                File.Put_Line ("   pragma Import (C, " & Buffer_Name & ","""
                               & Buffer_Name & """);");
             end;
@@ -7408,20 +7591,19 @@ package body Instrument.Ada_Unit is
 
          --  Create the list of coverage buffers
 
-         File.Put_Line ("   List : GNATcov_RTS.Buffers.Lists"
-                        & ".Unit_Coverage_Buffers_Array := (");
+         File.Put_Line ("   List : constant GNATcov_RTS.Buffers.Lists"
+                        & ".Coverage_Buffers_Group_Array := (");
          for Cur in Instr_Units.Iterate loop
             declare
                use CU_Name_Vectors;
 
                Index       : constant Positive := To_Index (Cur);
+               Index_Img   : constant String := Img (To_Index (Cur));
                Buffer_Name : constant String :=
                  Unit_Buffers_Name (Element (Cur));
-
             begin
-               File.Put ((1 .. 6 => ' '));
-               File.Put (Strings.Img (To_Index (Cur))
-                         & " => " & Buffer_Name & "'Unrestricted_Access");
+               File.Put
+                 ("      " & Index_Img & " => " & Buffer_Name & "'Access");
                if Index = Instr_Units.Last_Index then
                   File.Put_Line (");");
                else
@@ -7429,6 +7611,14 @@ package body Instrument.Ada_Unit is
                end if;
             end;
          end loop;
+         File.Put_Line ("   C_List : constant GNATcov_RTS.Buffers.Lists"
+                        & ".GNATcov_RTS_Coverage_Buffers_Group_Array :=");
+         File.Put_Line ("      (" & Instr_Units.Length'Image
+                        & " , List'Address);");
+
+         File.Put_Line
+           ("   pragma Export (C, C_List, """
+            & Unit_Buffers_Array_Name (+IC.Project_Name) & """);");
 
          File.New_Line;
          File.Put_Line ("end " & Unit_Name & ";");
diff --git a/tools/gnatcov/instrument-ada_unit.ads b/tools/gnatcov/instrument-ada_unit.ads
index 7a7e8ccb2..8046564a5 100644
--- a/tools/gnatcov/instrument-ada_unit.ads
+++ b/tools/gnatcov/instrument-ada_unit.ads
@@ -229,6 +229,9 @@ private
 
    type Ada_Unit_Inst_Context is new Instrument.Common.Unit_Inst_Context with
       record
+         CU : CU_Id := No_CU_Id;
+         --  SCO identifier of the compilation unit being instrumented
+
          Root_Unit : Compilation_Unit;
          --  Node of compilation unit
 
@@ -236,6 +239,9 @@ private
          Source_Conditions : Source_Condition_Vectors.Vector;
          --  Decisions and (for MC/DC) conditions to be instrumented
 
+         Unit_Bits : Instrument.Common.Allocated_Bits;
+         --  Allocated bits in coverage buffers for low-level SCOs
+
          Entities : Instrumentation_Entities;
          --  Bank of nodes to use during instrumentation
 
@@ -282,6 +288,12 @@ private
 
          Non_Instr_LL_SCOs : Non_Instrumented_SCO_Sets.Set;
          --  Set of low level SCO ids that were not instrumented
+
+         Current_Scope_Entity : Scope_Entity_Acc := null;
+         --  Information about the name, sloc, SCO range and children scopes of
+         --  the current scope entity. This is modified when entering a scope
+         --  (updated to the current scope), and when leaving it (updated to
+         --  the current scope parent, if any).
       end record;
 
    function Insert_MCDC_State
diff --git a/tools/gnatcov/instrument-base_types.adb b/tools/gnatcov/instrument-base_types.adb
index c8b64b9f8..2203a050e 100644
--- a/tools/gnatcov/instrument-base_types.adb
+++ b/tools/gnatcov/instrument-base_types.adb
@@ -150,7 +150,7 @@ package body Instrument.Base_Types is
                --  Add an unambiguous separator between the project name and
                --  the rest of the slug.
 
-               Append (Result, "_z_z_");
+               Append (Result, "_z_z");
 
                --  File names can contain characters that cannot appear in
                --  identifiers. Furthermore, unlike for the identifier to
diff --git a/tools/gnatcov/instrument-base_types.ads b/tools/gnatcov/instrument-base_types.ads
index 99dc1c449..99df2fbf4 100644
--- a/tools/gnatcov/instrument-base_types.ads
+++ b/tools/gnatcov/instrument-base_types.ads
@@ -28,6 +28,7 @@ with GNATCOLL.Projects; use GNATCOLL.Projects;
 with Types; use Types;
 
 with GNATcov_RTS.Buffers; use GNATcov_RTS.Buffers;
+with Project;             use Project;
 with SC_Obligations;      use SC_Obligations;
 with Subprocesses;        use Subprocesses;
 with Switches;            use Switches;
diff --git a/tools/gnatcov/instrument-c.adb b/tools/gnatcov/instrument-c.adb
index 77b778bc9..58c28fb11 100644
--- a/tools/gnatcov/instrument-c.adb
+++ b/tools/gnatcov/instrument-c.adb
@@ -18,12 +18,10 @@
 
 with Ada.Characters.Handling;
 with Ada.Containers;  use Ada.Containers;
-with Ada.Containers.Hashed_Maps;
 with Ada.Directories; use Ada.Directories;
-with Ada.Strings;
-with Ada.Strings.Unbounded.Hash;
 with Ada.Text_IO;     use Ada.Text_IO;
 
+with Clang.CX_String;  use Clang.CX_String;
 with Clang.Extensions; use Clang.Extensions;
 
 with GNAT.OS_Lib; use GNAT.OS_Lib;
@@ -36,21 +34,22 @@ with Interfaces;           use Interfaces;
 with Interfaces.C;         use Interfaces.C;
 with Interfaces.C.Strings; use Interfaces.C.Strings;
 
-with Command_Line;
 with ALI_Files;           use ALI_Files;
 with Coverage;            use Coverage;
 with Coverage_Options;
-with Files_Table;         use Files_Table;
 with GNATcov_RTS.Buffers; use GNATcov_RTS.Buffers;
 with Hex_Images;          use Hex_Images;
+with Inputs;              use Inputs;
 with Instrument.C_Utils;  use Instrument.C_Utils;
 with Outputs;             use Outputs;
 with Paths;               use Paths;
+with Project;             use Project;
 with SCOs;
 with Subprocesses;        use Subprocesses;
 with System;              use System;
 with Table;
 with Text_Files;          use Text_Files;
+with Project;             use Project;
 
 package body Instrument.C is
 
@@ -86,6 +85,10 @@ package body Instrument.C is
    --  is always a SCO associated with a pragma exempt, which would result in
    --  all the annotations being filtered out.
 
+   function Last_File return Valid_Source_File_Index
+   is (SCOs.SCO_Unit_Table.Table (SCOs.SCO_Unit_Table.Last).File_Index);
+   --  Return the source file for the last low-level SCO that was created
+
    ------------------------------
    --  Preprocessing utilities --
    ------------------------------
@@ -149,7 +152,8 @@ package body Instrument.C is
    --  preprocess the file, assuming that it accepts the -E flag, to preprocess
    --  a file.
 
-   function Common_Parse_TU_Args return String_Vectors.Vector;
+   function Common_Parse_TU_Args
+     (Lang : Some_Language) return String_Vectors.Vector;
    --  Return the list of arguments that should always be passed to
    --  Parse_Translation_Unit.
 
@@ -159,12 +163,12 @@ package body Instrument.C is
 
    type Record_PP_Info_Pass_Kind is new Pass_Kind with null record;
 
-   procedure Append_SCO
+   overriding procedure Append_SCO
      (Pass               : Record_PP_Info_Pass_Kind;
       UIC                : in out C_Unit_Inst_Context'Class;
       N                  : Cursor_T;
       C1, C2             : Character;
-      From, To           : Local_Source_Location;
+      From, To           : Source_Location;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name);
    --  Append a SCO to SCOs.SCO_Table. Also partially fill the preprocessing
@@ -173,18 +177,45 @@ package body Instrument.C is
 
    type Instrument_Pass_Kind is new Pass_Kind with null record;
 
-   procedure Append_SCO
+   overriding procedure Enter_Scope
+     (Pass : Instrument_Pass_Kind;
+      UIC  : in out C_Unit_Inst_Context'Class;
+      N    : Cursor_T);
+   --  Open a scope for N under the scope of the file in which the
+   --  corresponding code was written. This must be completed with a call to
+   --  the function Exit_Scope, defined below. Assume that the scope first SCO
+   --  is the next generated SCO (SCOs.SCO_Table.Last + 1). Update
+   --  UIC.Scopes to the created entity and UIC.Current_File_Scope to the
+   --  corresponding file.
+
+   overriding procedure Exit_Scope
+     (Pass : Instrument_Pass_Kind;
+      UIC  : in out C_Unit_Inst_Context'Class)
+     with Pre => UIC.Scopes.Contains (UIC.Current_File_Scope);
+   --  Close the current scope, removing the current scope of the current file
+   --  from UIC.Scopes if it does not contain SCOs. Assume that the last
+   --  generated SCO (SCOs.SCO_Table.Last) is the last SCO for the current
+   --  scope.
+
+   procedure Remap_Scopes
+     (Scopes  : Scopes_In_Files_Map.Map;
+      SCO_Map : LL_HL_SCO_Map);
+   --  Convert low level SCOs in each scope for each file to high-level SCOs
+   --  using the mapping in SCO_Map. Set the file's SCO range to cover all of
+   --  its scopes' SCO ranges.
+
+   overriding procedure Append_SCO
      (Pass               : Instrument_Pass_Kind;
       UIC                : in out C_Unit_Inst_Context'Class;
       N                  : Cursor_T;
       C1, C2             : Character;
-      From, To           : Local_Source_Location;
+      From, To           : Source_Location;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name);
    --  Append a SCO to SCOs.SCO_Table, and complete the preprocessing info with
    --  the preprocessed source range.
 
-   procedure Instrument_Statement
+   overriding procedure Instrument_Statement
      (Pass         : Instrument_Pass_Kind;
       UIC          : in out C_Unit_Inst_Context'Class;
       LL_SCO       : Nat;
@@ -192,7 +223,7 @@ package body Instrument.C is
       Instr_Scheme : Instr_Scheme_Type);
    --  Add an entry to UIC.Source_Statements
 
-   procedure Instrument_Decision
+   overriding procedure Instrument_Decision
      (Pass     : Instrument_Pass_Kind;
       UIC      : in out C_Unit_Inst_Context'Class;
       LL_SCO   : Nat;
@@ -200,7 +231,7 @@ package body Instrument.C is
       State    : US.Unbounded_String);
    --  Add an entry to UIC.Source_Decisions
 
-   procedure Instrument_Condition
+   overriding procedure Instrument_Condition
      (Pass      : Instrument_Pass_Kind;
       UIC       : in out C_Unit_Inst_Context'Class;
       LL_SCO    : Nat;
@@ -209,19 +240,31 @@ package body Instrument.C is
       First     : Boolean);
    --  Add an entry to UIC.Source_Conditions
 
-   procedure Curlify
-     (Pass : Instrument_Pass_Kind;
-      N    : Cursor_T;
-      Rew  : Rewriter_T);
-   --  Wrapper around Instrument.C.Utils.Curlify
-
-   procedure Insert_MCDC_State
+   overriding procedure Insert_MCDC_State
      (Pass       : Instrument_Pass_Kind;
       UIC        : in out C_Unit_Inst_Context'Class;
       Name       : String;
       MCDC_State : out US.Unbounded_String);
    --  Wrapper around Insert_MCDC_State overload
 
+   overriding procedure Insert_Text_Before_Token
+     (Pass : Instrument_Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String);
+
+   overriding procedure Insert_Text_Before
+     (Pass : Instrument_Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String);
+
+   overriding procedure Insert_Text_After
+     (Pass : Instrument_Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String);
+
    Record_PP_Info_Pass : aliased Record_PP_Info_Pass_Kind;
    Instrument_Pass     : aliased Instrument_Pass_Kind;
 
@@ -229,26 +272,43 @@ package body Instrument.C is
    -- Generation of witness fragments --
    -------------------------------------
 
+   function Buffers_Subscript (Buffers_Index : Positive) return String
+   is ("[" & Img (Buffers_Index - 1) & "]");
+   --  Return a C array subscript to refer to the coverage buffers
+   --  corresponding to Buffers_Index.
+   --
+   --  Note that while Buffers_Index is 1-based, C arrays are 0-based, hence
+   --  the index "off-by-1" conversion.
+
    function Make_Expr_Witness
-     (UIC : C_Unit_Inst_Context; Bit : Bit_Id) return String;
+     (UIC          : C_Unit_Inst_Context;
+      Buffer_Index : Positive;
+      Bit          : Bit_Id) return String;
    --  Create a procedure call expression on to witness execution of the low
-   --  level SCO with the given bit id.
+   --  level SCO with the given Bit id in the statement buffer at Buffer_Index.
 
    function Make_Statement_Witness
-     (UIC : C_Unit_Inst_Context; Bit : Bit_Id) return String;
+     (UIC          : C_Unit_Inst_Context;
+      Buffer_Index : Positive;
+      Bit          : Bit_Id) return String;
    --  Create a procedure call statement to witness execution of the low level
-   --  SCO with the given bit id.
+   --  SCO with the given Bit id in the statement buffer at Buffer_Index.
 
    procedure Insert_Statement_Witness
-     (UIC : in out C_Unit_Inst_Context; SS : C_Source_Statement);
-   --  Insert witness function call for the identified statement
+     (UIC           : in out C_Unit_Inst_Context;
+      Buffers_Index : Positive;
+      SS            : C_Source_Statement);
+   --  Insert witness function call for the identified statement. Buffers_Index
+   --  designates the relevant set of coverage buffers.
 
    procedure Insert_Decision_Witness
-     (UIC        : in out C_Unit_Inst_Context;
-      SD         : C_Source_Decision;
-      Path_Count : Positive);
+     (UIC           : in out C_Unit_Inst_Context;
+      Buffers_Index : Positive;
+      SD            : C_Source_Decision;
+      Path_Count    : Natural);
    --  For use when decision coverage or MC/DC is requested. Insert witness
-   --  function call for the identified decision.
+   --  function call for the identified decision. Buffers_Index designates the
+   --  relevant set of coverage buffers.
 
    procedure Insert_Condition_Witness
      (UIC    : in out C_Unit_Inst_Context;
@@ -293,17 +353,6 @@ package body Instrument.C is
    -- Source instrumentation --
    ----------------------------
 
-   function Unit_Buffers_Array_Name (IC : Inst_Context) return String is
-      ("gnatcov_rts_buffers_array_" & (+IC.Project_Name));
-   --  Name of the symbol that references the
-   --  gnatcov_rts_unit_coverage_buffers_array struct (defined for the whole
-   --  project). This struct is an array containing the coverage buffers of all
-   --  of the instrumented units.
-   --
-   --  We need this to be unique per root project instrumented, as gnatcov
-   --  gives the possibility to link two separately-instrumented libraries in
-   --  the same executable.
-
    function Buffers_List_Filename (IC : Inst_Context) return String is
      ("gnatcov_rts_c-buffers-lists-" & (+IC.Project_Name));
    --  Return the name of the unit containing the array of coverage buffers
@@ -362,6 +411,21 @@ package body Instrument.C is
    function Format_Str_Constant (Value : String) return String;
    --  Return a gnatcov_rts_string literal corresponding to Value
 
+   function Format_Array_Init_Expr
+     (Exprs     : String_Vectors.Vector;
+      Multiline : Boolean := False) return String;
+   --  Helper to format the initialization expression for an array.
+   --
+   --  Exprs is the list of expressions for all items in the array.
+   --
+   --  If Multiline is False, the returned expression fits on a single line.
+   --  Otherwise, put one array item per line.
+
+   function Format_Fingerprint
+     (Fingerprint : SC_Obligations.Fingerprint_Type) return String
+   is (Instrument.Common.Format_Fingerprint (Fingerprint, "{", "}"));
+   --  Helper to format a uint8_t[] literal for a SCOs fingerprint
+
    function Format_Def
      (Instrumenter : C_Family_Instrumenter_Type'Class;
       C_Type       : String;
@@ -407,6 +471,7 @@ package body Instrument.C is
      (Instrumenter : C_Family_Instrumenter_Type'Class;
       C_Type       : String;
       Name         : String;
+      Array_Size   : String := "";
       Func_Args    : String := "") return String;
    --  Helper for format an "extern" declaration. Arguments are the same as
    --  Format_Def.
@@ -417,6 +482,7 @@ package body Instrument.C is
       Instrumenter : C_Family_Instrumenter_Type'Class;
       C_Type       : String;
       Name         : String;
+      Array_Size   : String := "";
       Func_Args    : String := "");
    --  Like Format_Extern_Decl, but write the definition at Location in the
    --  unit rewritten by Rewriter.
@@ -429,10 +495,6 @@ package body Instrument.C is
       Func_Args    : String := "");
    --  Like Format_Extern_Decl, but write the definition to File
 
-   function Find_First_Insert_Location
-     (TU : Translation_Unit_T) return Source_Location_T;
-   --  Find the first rewritable (raw) location of the file
-
    ------------------------
    -- To_Chars_Ptr_Array --
    ------------------------
@@ -552,36 +614,180 @@ package body Instrument.C is
       end case;
    end Source_Suffix;
 
+   -----------------
+   -- Enter_Scope --
+   -----------------
+
+   overriding procedure Enter_Scope
+     (Pass : Instrument_Pass_Kind;
+      UIC  : in out C_Unit_Inst_Context'Class;
+      N    : Cursor_T)
+   is
+      procedure Enter_File_Scope
+        (UIC : in out C_Unit_Inst_Context'Class;
+         SFI : Source_File_Index)
+      with Post => UIC.Scopes.Contains (SFI);
+      --  Create a global scope for the file index SFI in which all scopes
+      --  opened in the file will be stored.
+
+      ----------------------
+      -- Enter_File_Scope --
+      ----------------------
+
+      procedure Enter_File_Scope
+        (UIC : in out C_Unit_Inst_Context'Class;
+         SFI : Source_File_Index) is
+      begin
+         if not UIC.Scopes.Contains (SFI) then
+            declare
+               New_Scope_Ent : constant Scope_Entity_Acc := new Scope_Entity'
+                 (From     => SCO_Id (SCOs.SCO_Table.Last + 1),
+                  To       => No_SCO_Id,
+                  Name     => +Get_Unique_Name (SFI),
+                  Sloc     => (Line => 0, Column => 0),
+                  Children => Scope_Entities_Vectors.Empty_Vector,
+                  Parent   => null);
+            begin
+               UIC.Scopes.Insert (SFI, New_Scope_Ent);
+            end;
+         end if;
+
+         UIC.Current_File_Scope := SFI;
+      end Enter_File_Scope;
+
+      Sloc : constant Source_Location   := Start_Sloc (N);
+      File : constant Source_File_Index := Sloc.Source_File;
+   begin
+      --  For C and C++ programs that can include header files we need to be
+      --  able to know which file a scope was opened in. For this we use a map
+      --  from Source_File_Index to Scope_Entity_Acc. The scope associated to a
+      --  file is the last scope that was opened in it but not yet exited.
+      --  This mechanism works in the same way as that for Ada with the
+      --  possibility to have one current scope per file rather than just one
+      --  only for the file being instrumented.
+
+      Enter_File_Scope (UIC, File);
+
+      declare
+         C             : constant Scopes_In_Files_Map.Cursor :=
+           UIC.Scopes.Find (File);
+         Current_Scope : constant Scope_Entity_Acc :=
+           Scopes_In_Files_Map.Element (C);
+         New_Scope_Ent : constant Scope_Entity_Acc := new Scope_Entity'
+           (From     => SCO_Id (SCOs.SCO_Table.Last + 1),
+            To       => No_SCO_Id,
+            Name     => +Get_Decl_Name_Str (N),
+            Sloc     => Sloc.L,
+            Children => Scope_Entities_Vectors.Empty_Vector,
+            Parent   => Current_Scope);
+      begin
+         --  Add New_Scope_Ent to the children of the last open scope in the
+         --  file.
+         Current_Scope.Children.Append (New_Scope_Ent);
+
+         --  Set New_Scope_Ent as the current open scope for the file.
+         UIC.Scopes.Replace_Element (C, New_Scope_Ent);
+      end;
+   end Enter_Scope;
+
+   ----------------
+   -- Exit_Scope --
+   ----------------
+
+   overriding procedure Exit_Scope
+     (Pass : Instrument_Pass_Kind;
+      UIC  : in out C_Unit_Inst_Context'Class)
+   is
+      C     : Scopes_In_Files_Map.Cursor :=
+        UIC.Scopes.Find (UIC.Current_File_Scope);
+      Scope : Scope_Entity_Acc := Scopes_In_Files_Map.Element (C);
+   begin
+      --  If the scope associated to the current file is that of the file (no
+      --  parent), no scope is currently open in it. Then the scope that needs
+      --  closing is the current one of the file being instrumented.
+
+      if Scope.Parent = null then
+         UIC.Current_File_Scope := UIC.SFI;
+         C     := UIC.Scopes.Find (UIC.SFI);
+         Scope := Scopes_In_Files_Map.Element (C);
+      end if;
+
+      declare
+         Parent : constant Scope_Entity_Acc := Scope.Parent;
+      begin
+         --  Update the last SCO for this scope entity
+
+         Scope.To := SCO_Id (SCOs.SCO_Table.Last);
+
+         --  If the scope has no SCO, discard it
+
+         if Scope.To < Scope.From then
+            if Parent /= null then
+               Parent.Children.Delete_Last;
+            end if;
+            Free (Scope);
+         end if;
+
+         --  If this is not a top-level file scope (we want to keep its
+         --  reference after having traversed the AST), go up the scope tree
+         --  of the current file.
+
+         if Parent /= null then
+            UIC.Scopes.Replace_Element (C, Parent);
+         end if;
+      end;
+   end Exit_Scope;
+
+   ------------------
+   -- Remap_Scopes --
+   ------------------
+
+   procedure Remap_Scopes
+     (Scopes  : Scopes_In_Files_Map.Map;
+      SCO_Map : LL_HL_SCO_Map) is
+   begin
+      for S of Scopes loop
+         if not S.Children.Is_Empty then
+            for Child of S.Children loop
+               Remap_Scope_Entity (Child, SCO_Map);
+            end loop;
+
+            S.From := S.Children.First_Element.From;
+            S.To   := S.Children.Last_Element.To;
+         end if;
+      end loop;
+   end Remap_Scopes;
+
    ----------------
    -- Append_SCO --
    ----------------
 
-   procedure Append_SCO
+   overriding procedure Append_SCO
      (Pass               : Record_PP_Info_Pass_Kind;
       UIC                : in out C_Unit_Inst_Context'Class;
       N                  : Cursor_T;
       C1, C2             : Character;
-      From, To           : Local_Source_Location;
+      From, To           : Source_Location;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name)
    is
-      Loc : Source_Location_T :=
-        Get_Range_Start (Get_Cursor_Extent (N));
-
-      Line, Column, Offset : aliased unsigned;
-      File                 : File_T;
-      Info                 : PP_Info;
+      Loc  : Source_Location_T := Start_Sloc (N);
+      Info : PP_Info;
    begin
-      Append_SCO (C1, C2, From, To, Last, Pragma_Aspect_Name);
+      Append_SCO
+        (C1, C2, From.L, To.L, From.Source_File, Last, Pragma_Aspect_Name);
 
       --  We add preprocessing information only for actual SCOs. Return there
-      --  if this is an operator SCO or a dominance SCO.
+      --  if this is an operator SCO.
 
-      if C1 in '>' | '!' | '&' | '|' then
+      if C1 in '!' | '&' | '|' then
          return;
       end if;
 
-      Info.Actual_Source_Range := (From, To);
+      --  TODO??? the actual source range should be a Source_Location, and not
+      --  a Local_Source_Location as it can refer to #included files.
+
+      Info.Actual_Source_Range := (From.L, To.L);
 
       --  Check if this is comes from a macro expansion, in which case we need
       --  to record some information, for reporting purposes.
@@ -591,14 +797,10 @@ package body Instrument.C is
             Expansion_Stack : Expansion_Lists.List;
             Definition_Info : Expansion_Info;
 
-            Macro_Expansion_Name      : US.Unbounded_String;
-            Immediate_Expansion_Loc_C : Source_Location_T;
-            Immediate_Expansion_Loc   : Source_Location;
-
-            Macro_Arg_Expanded_Loc_C : aliased Source_Location_T;
-            Macro_Arg_Expanded_Loc   : Source_Location;
+            Macro_Expansion_Name    : US.Unbounded_String;
+            Immediate_Expansion_Loc : Source_Location_T;
+            Macro_Arg_Expanded_Loc  : Source_Location_T;
          begin
-
             --  Note: macro arguments are completely macro-expanded before they
             --  are substituted in a macro body, unless they are stringified or
             --  pasted with other tokens.
@@ -662,53 +864,26 @@ package body Instrument.C is
             --  So we have to get the macro argument expansion location, and
             --  get its spelling location.
 
-            if Is_Macro_Arg_Expansion
-              (Loc, Macro_Arg_Expanded_Loc_C'Access, UIC.TU)
+            if Is_Macro_Arg_Expansion (Loc, Macro_Arg_Expanded_Loc, UIC.TU)
             then
-               Get_Spelling_Location
-                 (Macro_Arg_Expanded_Loc_C,
-                  File'Address,
-                  Line'Access,
-                  Column'Access,
-                  Offset'Access);
-               Macro_Arg_Expanded_Loc :=
-                 (Source_File =>
-                    Get_Index_From_Generic_Name
-                      (Name => Get_File_Name (File),
-                       Kind => Source_File),
-                  L           =>  (Natural (Line), Natural (Column)));
-
                Macro_Expansion_Name :=
                  +Get_Immediate_Macro_Name_For_Diagnostics
-                    (Macro_Arg_Expanded_Loc_C, UIC.TU);
+                    (Macro_Arg_Expanded_Loc, UIC.TU);
 
                Definition_Info :=
                  (Macro_Name => Macro_Expansion_Name,
-                  Sloc       => Macro_Arg_Expanded_Loc);
+                  Sloc       => Spelling_Location (Macro_Arg_Expanded_Loc));
             else
-               Get_Spelling_Location
-                 (Loc,
-                  File'Address,
-                  Line'Access,
-                  Column'Access,
-                  Offset'Access);
-
-               Immediate_Expansion_Loc :=
-                 (Source_File =>
-                    Get_Index_From_Generic_Name
-                      (Name => Get_File_Name (File),
-                       Kind => Source_File),
-                  L           => (Natural (Line), Natural (Column)));
                Macro_Expansion_Name :=
                  +Get_Immediate_Macro_Name_For_Diagnostics (Loc, UIC.TU);
                Definition_Info :=
                  (Macro_Name => Macro_Expansion_Name,
-                  Sloc       => Immediate_Expansion_Loc);
+                  Sloc       => Spelling_Location (Loc));
             end if;
 
             while Is_Macro_Location (Loc) loop
 
-               Immediate_Expansion_Loc_C := Loc;
+               Immediate_Expansion_Loc := Loc;
 
                --  Find the location of the immediately expanded macro. Getting
                --  the immediate expansion location yields a location in the
@@ -747,9 +922,7 @@ package body Instrument.C is
                --  as implemented in clang.
 
                while Is_Macro_Arg_Expansion
-                 (Immediate_Expansion_Loc_C,
-                  Macro_Arg_Expanded_Loc_C'Access,
-                  UIC.TU)
+                 (Immediate_Expansion_Loc, Macro_Arg_Expanded_Loc, UIC.TU)
                loop
                   --  TODO??? Document why it is needed to loop while we are
                   --  in a macro argument expansion (did not manage to make an
@@ -758,31 +931,17 @@ package body Instrument.C is
                   --  Get_Immediate_Macro_Name_For_Diagnostics implemented in
                   --  clang.
 
-                  Immediate_Expansion_Loc_C :=
+                  Immediate_Expansion_Loc :=
                     Get_Immediate_Expansion_Loc
-                      (Immediate_Expansion_Loc_C, UIC.TU);
+                      (Immediate_Expansion_Loc, UIC.TU);
                end loop;
 
-               --  Immediate_Expansion_Loc is the location of the token in
-               --  the immediate expanded macro definition. To get to the
-               --  expansion point, go up one level.
-
-               Immediate_Expansion_Loc_C :=
-                 Get_Immediate_Expansion_Loc
-                   (Immediate_Expansion_Loc_C, UIC.TU);
-               Get_Spelling_Location
-                 (Immediate_Expansion_Loc_C,
-                  File'Address,
-                  Line'Access,
-                  Column'Access,
-                  Offset'Access);
+               --  Immediate_Expansion_Loc is the location of the token in the
+               --  immediate expanded macro definition. To get to the expansion
+               --  point, go up one level.
 
                Immediate_Expansion_Loc :=
-                 (Source_File =>
-                    Get_Index_From_Generic_Name
-                      (Name => Get_File_Name (File),
-                       Kind => Source_File),
-                  L           => (Natural (Line), Natural (Column)));
+                 Get_Immediate_Expansion_Loc (Immediate_Expansion_Loc, UIC.TU);
                Macro_Expansion_Name :=
                  +Get_Immediate_Macro_Name_For_Diagnostics (Loc, UIC.TU);
 
@@ -797,7 +956,8 @@ package body Instrument.C is
                if Length (Macro_Expansion_Name) /= 0 then
                   Expansion_Stack.Append
                     ((Macro_Name => Macro_Expansion_Name,
-                      Sloc       => Immediate_Expansion_Loc));
+                      Sloc       => Spelling_Location
+                                      (Immediate_Expansion_Loc)));
                end if;
             end loop;
 
@@ -813,21 +973,18 @@ package body Instrument.C is
       UIC.LL_PP_Info_Map.Insert (SCOs.SCO_Table.Last, Info);
    end Append_SCO;
 
-   ----------------
-   -- Append_SCO --
-   ----------------
-
-   procedure Append_SCO
+   overriding procedure Append_SCO
      (Pass               : Instrument_Pass_Kind;
       UIC                : in out C_Unit_Inst_Context'Class;
       N                  : Cursor_T;
       C1, C2             : Character;
-      From, To           : Local_Source_Location;
+      From, To           : Source_Location;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name)
    is
    begin
-      Append_SCO (C1, C2, From, To, Last, Pragma_Aspect_Name);
+      Append_SCO
+        (C1, C2, From.L, To.L, From.Source_File, Last, Pragma_Aspect_Name);
 
       --  If this SCO is in a macro expansion, let's add source location
       --  information: we want to be able to know the actual source location
@@ -837,15 +994,8 @@ package body Instrument.C is
 
       if UIC.LL_PP_Info_Map.Contains (SCOs.SCO_Table.Last) then
          declare
-            Cursor_Source_Range_C : constant Source_Range_T :=
-              Get_Cursor_Extent (N);
-            Start_Loc             : constant Source_Location_T :=
-              Get_Range_Start (Cursor_Source_Range_C);
-            End_Loc               : constant Source_Location_T :=
-              Get_Range_End (Cursor_Source_Range_C);
-
-            Line, Column, Offset : aliased unsigned;
-            File                 : File_T;
+            Start_Loc : constant Source_Location_T := Start_Sloc (N);
+            End_Loc   : constant Source_Location_T := End_Sloc (N);
 
             Cursor_Source_Range : Slocs.Local_Source_Location_Range;
 
@@ -864,28 +1014,13 @@ package body Instrument.C is
             end Update;
 
          begin
-            --  Get start of the range
+            --  Get start and end of the range. Note: End_Loc is exclusive,
+            --  whereas we need Cursor_Source_Range.Last_Sloc to be inclusive.
 
-            Get_File_Location
-              (Start_Loc,
-               File'Address,
-               Line'Access,
-               Column'Access,
-               Offset'Access);
-            Cursor_Source_Range.First_Sloc :=
-              (Line => Natural (Line), Column => Natural (Column));
-
-            --  Get end of the range. Note: end column is exclusive
-
-            Get_File_Location
-              (End_Loc,
-               File'Address,
-               Line'Access,
-               Column'Access,
-               Offset'Access);
-
-            Cursor_Source_Range.Last_Sloc :=
-              (Line => Natural (Line), Column => Natural (Column) - 1);
+            Cursor_Source_Range.First_Sloc := File_Location (Start_Loc);
+            Cursor_Source_Range.Last_Sloc := File_Location (End_Loc);
+            Cursor_Source_Range.Last_Sloc.Column :=
+              Cursor_Source_Range.Last_Sloc.Column - 1;
 
             UIC.LL_PP_Info_Map.Update_Element
               (UIC.LL_PP_Info_Map.Find (SCOs.SCO_Table.Last), Update'Access);
@@ -893,21 +1028,29 @@ package body Instrument.C is
       end if;
    end Append_SCO;
 
-   -------------
-   -- Curlify --
-   -------------
+   --------------------------------
+   -- Find_Instrumented_Entities --
+   --------------------------------
 
-   procedure Curlify
-     (Pass : Instrument_Pass_Kind; N : Cursor_T; Rew : Rewriter_T) is
+   function Find_Instrumented_Entities
+     (UIC : in out C_Unit_Inst_Context'Class;
+      SFI : Valid_Source_File_Index)
+      return C_Instrumented_Entities_Maps.Reference_Type
+   is
+      use C_Instrumented_Entities_Maps;
+      Cur   : Cursor;
+      Dummy : Boolean;
    begin
-      Curlify (N, Rew);
-   end Curlify;
+      UIC.Instrumented_Entities.Insert
+        (SFI, (others => <>), Cur, Dummy);
+      return UIC.Instrumented_Entities.Reference (Cur);
+   end Find_Instrumented_Entities;
 
    -----------------------
    -- Insert_MCDC_State --
    -----------------------
 
-   procedure Insert_MCDC_State
+   overriding procedure Insert_MCDC_State
      (Pass       : Instrument_Pass_Kind;
       UIC        : in out C_Unit_Inst_Context'Class;
       Name       : String;
@@ -916,18 +1059,57 @@ package body Instrument.C is
       MCDC_State := +Insert_MCDC_State (UIC, Name);
    end Insert_MCDC_State;
 
+   ------------------------------
+   -- Insert_Text_Before_Token --
+   ------------------------------
+
+   overriding procedure Insert_Text_Before_Token
+     (Pass : Instrument_Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String) is
+   begin
+      CX_Rewriter_Insert_Text_Before_Token (Rew, Loc, Text);
+   end Insert_Text_Before_Token;
+
+   ------------------------
+   -- Insert_Text_Before --
+   ------------------------
+
+   overriding procedure Insert_Text_Before
+     (Pass : Instrument_Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String) is
+   begin
+      CX_Rewriter_Insert_Text_Before (Rew, Loc, Text);
+   end Insert_Text_Before;
+
+   -----------------------
+   -- Insert_Text_After --
+   -----------------------
+
+   overriding procedure Insert_Text_After
+     (Pass : Instrument_Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String) is
+   begin
+      CX_Rewriter_Insert_Text_After (Rew, Loc, Text);
+   end Insert_Text_After;
+
    --------------------------
    -- Instrument_Statement --
    --------------------------
 
-   procedure Instrument_Statement
+   overriding procedure Instrument_Statement
      (Pass         : Instrument_Pass_Kind;
       UIC          : in out C_Unit_Inst_Context'Class;
       LL_SCO       : Nat;
       Insertion_N  : Cursor_T;
       Instr_Scheme : Instr_Scheme_Type) is
    begin
-      UIC.Source_Statements.Append
+      UIC.Find_Instrumented_Entities (Last_File).Statements.Append
         (C_Source_Statement'
            (LL_SCO       => SCOs.SCO_Table.Last,
             Instr_Scheme => Instr_Scheme,
@@ -938,14 +1120,14 @@ package body Instrument.C is
    -- Instrument_Decision --
    -------------------------
 
-   procedure Instrument_Decision
+   overriding procedure Instrument_Decision
      (Pass     : Instrument_Pass_Kind;
       UIC      : in out C_Unit_Inst_Context'Class;
       LL_SCO   : Nat;
       Decision : Cursor_T;
       State    : US.Unbounded_String) is
    begin
-      UIC.Source_Decisions.Append
+      UIC.Find_Instrumented_Entities (Last_File).Decisions.Append
         (C_Source_Decision'
            (LL_SCO   => LL_SCO,
             Decision => Decision,
@@ -956,7 +1138,7 @@ package body Instrument.C is
    -- Instrument_Condition --
    --------------------------
 
-   procedure Instrument_Condition
+   overriding procedure Instrument_Condition
      (Pass      : Instrument_Pass_Kind;
       UIC       : in out C_Unit_Inst_Context'Class;
       LL_SCO    : Nat;
@@ -964,7 +1146,7 @@ package body Instrument.C is
       State     : US.Unbounded_String;
       First     : Boolean) is
    begin
-      UIC.Source_Conditions.Append
+      UIC.Find_Instrumented_Entities (Last_File).Conditions.Append
         (C_Source_Condition'
            (LL_SCO    => SCOs.SCO_Table.Last,
             Condition => Condition,
@@ -977,13 +1159,14 @@ package body Instrument.C is
    -----------------------
 
    function Make_Expr_Witness
-     (UIC : C_Unit_Inst_Context; Bit : Bit_Id) return String
-   is
-      Bit_Img : constant String  := Img (Bit);
+     (UIC          : C_Unit_Inst_Context;
+      Buffer_Index : Positive;
+      Bit          : Bit_Id) return String is
    begin
-      return "gnatcov_rts_witness ("
-        & Statement_Buffer_Symbol (UIC.Instrumented_Unit) & "," & Bit_Img
-        & ")";
+      return
+        "gnatcov_rts_witness ("
+        & Statement_Buffer_Symbol (UIC.Instrumented_Unit)
+        & Buffers_Subscript (Buffer_Index) & ", " & Img (Bit) & ")";
    end Make_Expr_Witness;
 
    ----------------------------
@@ -991,9 +1174,11 @@ package body Instrument.C is
    ----------------------------
 
    function Make_Statement_Witness
-     (UIC : C_Unit_Inst_Context; Bit : Bit_Id) return String is
+     (UIC          : C_Unit_Inst_Context;
+      Buffer_Index : Positive;
+      Bit          : Bit_Id) return String is
    begin
-      return Make_Expr_Witness (UIC, Bit) & ";";
+      return Make_Expr_Witness (UIC, Buffer_Index, Bit) & ";";
    end Make_Statement_Witness;
 
    ------------------------------
@@ -1001,17 +1186,17 @@ package body Instrument.C is
    ------------------------------
 
    procedure Insert_Statement_Witness
-     (UIC : in out C_Unit_Inst_Context; SS : C_Source_Statement)
+     (UIC           : in out C_Unit_Inst_Context;
+      Buffers_Index : Positive;
+      SS            : C_Source_Statement)
    is
-      --  Allocate a bit in the statement coverage buffer, and record
-      --  its id in the bitmap.
+      Unit_Bits : Allocated_Bits renames UIC.Allocated_Bits (Buffers_Index);
 
-      Bit : constant Bit_Id := UIC.Unit_Bits.Last_Statement_Bit + 1;
-   begin
-      UIC.Unit_Bits.Last_Statement_Bit := Bit;
-      UIC.Unit_Bits.Statement_Bits.Append
-        (Statement_Bit_Ids'(SS.LL_SCO, Executed => Bit));
+      --  Allocate a bit in the statement coverage buffer
 
+      Bit : constant Bit_Id :=
+        Allocate_Statement_Bit (Unit_Bits, SS.LL_SCO);
+   begin
       --  Insert the call to the witness function: as a foregoing statement if
       --  SS.Statement is a statement, or as a previous expression (using the
       --  comma operator) if SS.Statement is an expression.
@@ -1020,13 +1205,14 @@ package body Instrument.C is
          when Instr_Stmt =>
             Insert_Text_After_Start_Of
               (N    => SS.Statement,
-               Text => Make_Statement_Witness (UIC, Bit),
+               Text => Make_Statement_Witness (UIC, Buffers_Index, Bit),
                Rew  => UIC.Rewriter);
 
          when Instr_Expr =>
             Insert_Text_After_Start_Of
               (N    => SS.Statement,
-               Text => "(" & Make_Expr_Witness (UIC, Bit) & ", ",
+               Text =>
+                 "(" & Make_Expr_Witness (UIC, Buffers_Index, Bit) & ", ",
                Rew  => UIC.Rewriter);
             Insert_Text_Before_End_Of
               (N    => SS.Statement,
@@ -1078,39 +1264,25 @@ package body Instrument.C is
    -----------------------------
 
    procedure Insert_Decision_Witness
-     (UIC        : in out C_Unit_Inst_Context;
-      SD         : C_Source_Decision;
-      Path_Count : Positive)
+     (UIC           : in out C_Unit_Inst_Context;
+      Buffers_Index : Positive;
+      SD            : C_Source_Decision;
+      Path_Count    : Natural)
    is
-      LL_SCO_Id : Nat renames SD.LL_SCO;
-      N         : Cursor_T renames SD.Decision;
-
-      Bits : Decision_Bit_Ids;
-
-   begin
-      Bits.LL_D_SCO := LL_SCO_Id;
-
-      --  Allocate outcome bits
-
-      Bits.Outcome_Bits :=
-        (False => UIC.Unit_Bits.Last_Outcome_Bit + 1,
-         True  => UIC.Unit_Bits.Last_Outcome_Bit + 2);
-      UIC.Unit_Bits.Last_Outcome_Bit :=
-        UIC.Unit_Bits.Last_Outcome_Bit + 2;
-
-      --  Allocate path bits for MC/DC if MC/DC is required and we were
-      --  able to generate a local state variable.
+      Unit_Bits : Allocated_Bits renames UIC.Allocated_Bits (Buffers_Index);
 
-      if MCDC_Coverage_Enabled and then US.Length (SD.State) > 0 then
-         Bits.Path_Bits_Base := UIC.Unit_Bits.Last_Path_Bit + 1;
-         UIC.Unit_Bits.Last_Path_Bit :=
-           UIC.Unit_Bits.Last_Path_Bit + Bit_Id (Path_Count);
-      else
-         Bits.Path_Bits_Base := No_Bit_Id;
-      end if;
+      N : Cursor_T renames SD.Decision;
 
-      UIC.Unit_Bits.Decision_Bits.Append (Bits);
+      --  Allocate bits for this decision in coverage buffers
 
+      Bits : constant Decision_Bit_Ids :=
+        Allocate_Decision_Bits
+          (Unit_Bits,
+           Start_Sloc (N),
+           SD.LL_SCO,
+           SD.State,
+           Path_Count);
+   begin
       --  Now attach witness call at the place of the original decision
 
       declare
@@ -1123,7 +1295,8 @@ package body Instrument.C is
          Insert_Text_After_Start_Of
            (N    => N,
             Text => Function_Name & "("
-                    & Decision_Buffer_Symbol (UIC.Instrumented_Unit) & ", "
+                    & Decision_Buffer_Symbol (UIC.Instrumented_Unit)
+                    & Buffers_Subscript (Buffers_Index) & ", "
                     & Img (Bits.Outcome_Bits (False)) & ", "
                     & Img (Bits.Outcome_Bits (True)),
             Rew  => UIC.Rewriter);
@@ -1131,7 +1304,8 @@ package body Instrument.C is
          if Is_MCDC then
             Insert_Text_After_Start_Of
               (N    => N,
-               Text => ", " & MCDC_Buffer_Symbol (UIC.Instrumented_Unit) & ", "
+               Text => ", " & MCDC_Buffer_Symbol (UIC.Instrumented_Unit)
+                       & Buffers_Subscript (Buffers_Index) & ", "
                        & Img (Bits.Path_Bits_Base) & ", "
                        & US.To_String (SD.State),
                Rew  => UIC.Rewriter);
@@ -1178,8 +1352,8 @@ package body Instrument.C is
       --  If not null, node where the witness call should be inserted.
       --  Otherwise, the insertion node will be N.
 
-      From : Local_Source_Location;
-      To   : Local_Source_Location;
+      From : Source_Location;
+      To   : Source_Location;
       Typ  : Character;
 
       Instr_Scheme : Instr_Scheme_Type := Instr_Stmt;
@@ -1236,29 +1410,14 @@ package body Instrument.C is
    -- Traverse_Statements --
    -------------------------
 
-   type Dominant_Info is record
-      K : Character;
-      --  F/T/S/E for a valid dominance marker, or ' ' for no dominant
-
-      N : Cursor_T;
-      --  Node providing the Sloc(s) for the dominance marker
-   end record;
-   No_Dominant : constant Dominant_Info := (' ', Get_Null_Cursor);
-
    procedure Traverse_Statements
-     (IC  : in out Inst_Context;
-      UIC : in out C_Unit_Inst_Context;
-      L   : Cursor_Vectors.Vector;
-      D   : Dominant_Info := No_Dominant);
-   --  Process L, a list of statements or declarations dominated by D
-
-   function Traverse_Statements
-     (IC  : in out Inst_Context;
-      UIC : in out C_Unit_Inst_Context;
-      L   : Cursor_Vectors.Vector;
-      D   : Dominant_Info := No_Dominant) return Dominant_Info;
-   --  Process L, a list of statements or declarations dominated by D. Returns
-   --  dominant information corresponding to the last node with SCO in L.
+     (IC              : in out Inst_Context;
+      UIC             : in out C_Unit_Inst_Context;
+      L               : Cursor_Vectors.Vector;
+      Trailing_Braces : out Unbounded_String);
+   --  Process L, a list of statements or declarations. Set Trailing_Braces
+   --  to the list of braces that should be inserted after this statements'
+   --  list.
 
    procedure Traverse_Declarations
      (IC  : in out Inst_Context;
@@ -1322,7 +1481,7 @@ package body Instrument.C is
       --  Likewise for the putative SCO_Raw_Hash_Table entries: see below
 
       type Hash_Entry is record
-         Sloc      : Local_Source_Location;
+         Sloc      : Source_Location;
          SCO_Index : Nat;
       end record;
       --  We must register all conditions/pragmas in SCO_Raw_Hash_Table.
@@ -1395,8 +1554,7 @@ package body Instrument.C is
       --  output for the complex decision. It process the suboperands of the
       --  decision looking for nested decisions.
 
-      function Process_Node (N : Cursor_T) return Child_Visit_Result_T
-        with Convention => C;
+      function Process_Node (N : Cursor_T) return Child_Visit_Result_T;
       --  Processes one node in the traversal, looking for logical operators,
       --  and if one is found, outputs the appropriate table entries.
 
@@ -1449,7 +1607,7 @@ package body Instrument.C is
                C1   => C1,
                C2   => C2,
                From => Sloc (Get_Operator_Loc (N)),
-               To   => No_Local_Location,
+               To   => Slocs.No_Location,
                Last => False);
 
             Hash_Entries.Append ((Sloc      => Start_Sloc (N),
@@ -1649,7 +1807,7 @@ package body Instrument.C is
          return;
       end if;
       Hash_Entries.Init;
-      Visit (N, Process_Node'Unrestricted_Access);
+      Visit (N, Process_Node'Access);
       Hash_Entries.Free;
    end Process_Decisions;
 
@@ -1677,8 +1835,7 @@ package body Instrument.C is
 
    function Has_Decision (T : Cursor_T) return Boolean is
 
-      function Visitor (N : Cursor_T) return Child_Visit_Result_T
-        with Convention => C;
+      function Visitor (N : Cursor_T) return Child_Visit_Result_T;
       --  If N's kind indicates the presence of a decision, return
       --  Child_Visit_Break, otherwise return Child_Visit_Recurse.
       --
@@ -1713,7 +1870,7 @@ package body Instrument.C is
    --  Start of processing for Has_Decision
 
    begin
-      Visit (T, Visitor'Unrestricted_Access);
+      Visit (T, Visitor'Access);
       return Has_Decision;
    end Has_Decision;
 
@@ -1760,30 +1917,18 @@ package body Instrument.C is
    -------------------------
 
    procedure Traverse_Statements
-     (IC  : in out Inst_Context;
-      UIC : in out C_Unit_Inst_Context;
-      L   : Cursor_Vectors.Vector;
-      D   : Dominant_Info := No_Dominant)
-   is
-      Discard_Dom : constant  Dominant_Info :=
-        Traverse_Statements (IC, UIC, L, D);
-   begin
-      null;
-   end Traverse_Statements;
-
-   function Traverse_Statements
-     (IC  : in out Inst_Context;
-      UIC : in out C_Unit_Inst_Context;
-      L   : Cursor_Vectors.Vector;
-      D   : Dominant_Info := No_Dominant) return Dominant_Info
+     (IC              : in out Inst_Context;
+      UIC             : in out C_Unit_Inst_Context;
+      L               : Cursor_Vectors.Vector;
+      Trailing_Braces : out Unbounded_String)
    is
-      Current_Dominant : Dominant_Info := D;
-
       SC_First : constant Nat := SC.Last + 1;
       SD_First : constant Nat := SD.Last + 1;
 
-      procedure Traverse_One (N : Cursor_T);
-      --  Traverse a statement
+      procedure Traverse_One
+        (N : Cursor_T; Trailing_Braces : out Unbounded_String);
+      --  Traverse a statement. Set Trailing_Braces to the list of braces that
+      --  should be inserted after this statement.
 
       procedure Extend_Statement_Sequence
         (N            : Cursor_T;
@@ -1797,13 +1942,45 @@ package body Instrument.C is
       --  current CS sequence. Then output entries for all decisions nested in
       --  these statements, which have been deferred so far.
 
+      function Curlify (N : Cursor_T) return Boolean;
+      --  Rewrite control flow statements (if else, for, while etc.) with
+      --  braces when needed (i.e. when they do not already have them). Note
+      --  that the insertion of the last closing brace is deferred to the
+      --  traversal of the following statement, and thus, Curlify returns
+      --  True if the insertion of a closing brace is required.
+      --
+      --  We can't insert this closing brace with N source location information
+      --  only, as the ending location of a statement expression is the end
+      --  location of the expression, and not the location of the semicolon
+      --  terminating the statement. To solve this, we choose to insert the
+      --  closing brace _before_ the next statement (or _before the closing
+      --  brace of the function body, if this is the last statement of the
+      --  function) rather than _after_ the statement semicolon, which can't
+      --  be done easily.
+
       ------------------
       -- Traverse_One --
       ------------------
 
-      procedure Traverse_One (N : Cursor_T) is
+      procedure Traverse_One
+        (N : Cursor_T; Trailing_Braces : out Unbounded_String) is
          use Cursor_Vectors;
+         TB : Unbounded_String;
+         --  Local to pass on to Traverse_Statements invocations. Trailing
+         --  braces that can be inserted while instrumenting the current node
+         --  (e.g. for the then part of an if-else statement, the closing
+         --  brace will be inserted before the else location) will be inserted.
+         --  For the other cases, the insertion of the closing braces will
+         --  be delayed to the instrumentation of the next statement (see
+         --  Traverse_Statements body) or, if this is the last statement, after
+         --  being done with the instrumentation of the function (see the body
+         --  of Traverse_Declarations).
+
       begin
+         if Curlify (N) then
+            Append (Trailing_Braces, '}');
+         end if;
+
          --  Initialize or extend current statement sequence. Note that for
          --  special cases such as IF and SWITCH statements we will modify
          --  the range to exclude internal statements that should not be
@@ -1816,16 +1993,12 @@ package body Instrument.C is
 
             when Cursor_Label_Ref =>
                Set_Statement_Entry;
-               Current_Dominant := No_Dominant;
 
             --  Compound statement, which breaks the current statement sequence
 
             when Cursor_Compound_Stmt =>
                Set_Statement_Entry;
-               Traverse_Statements
-                 (IC, UIC,
-                  L   => Get_Children (N),
-                  D   => Current_Dominant);
+               Traverse_Statements (IC, UIC, Get_Children (N), TB);
 
             --  If statement, which breaks the current statement sequence, but
             --  we include the condition in the current sequence.
@@ -1839,25 +2012,21 @@ package body Instrument.C is
                begin
                   Process_Decisions_Defer (Get_Cond (N), 'I');
                   Set_Statement_Entry;
-
-                  --  Now we traverse the statements in the THEN part
-
-                  UIC.Pass.Curlify (N   => Then_Part,
-                                    Rew => UIC.Rewriter);
                   Traverse_Statements
-                    (IC, UIC,
-                     L => To_Vector (Then_Part),
-                     D => ('S', N));
+                    (IC, UIC, To_Vector (Then_Part), TB);
 
                   --  Traverse the ELSE statements if present
 
                   if not Is_Null (Else_Part) then
-                     UIC.Pass.Curlify (N   => Else_Part,
-                                       Rew => UIC.Rewriter);
+
+                     --  Insert the trailing braces resulting from the
+                     --  traversal of the then part before the else.
+
+                     UIC.Pass.Insert_Text_Before
+                       (UIC.Rewriter, Get_Else_Loc (N), +TB);
+                     TB := +"";
                      Traverse_Statements
-                       (IC, UIC,
-                        L => To_Vector (Else_Part),
-                        D => ('S', N));
+                       (IC, UIC, To_Vector (Else_Part), TB);
                   end if;
                end;
 
@@ -1873,13 +2042,9 @@ package body Instrument.C is
                   Process_Decisions_Defer (Switch_Cond, 'X');
                   Set_Statement_Entry;
 
-                  --  Process case branches, all of which are dominated by the
-                  --  Switch statement.
+                  --  Process case branches
 
-                  Traverse_Statements
-                    (IC, UIC,
-                     L => To_Vector (Alt),
-                     D => Current_Dominant);
+                  Traverse_Statements (IC, UIC, To_Vector (Alt), TB);
                end;
 
             --  Case alternative
@@ -1889,9 +2054,7 @@ package body Instrument.C is
                   Case_Body : constant Cursor_T := Get_Sub_Stmt (N);
                begin
                   Traverse_Statements
-                    (IC, UIC,
-                     L => To_Vector (Case_Body),
-                     D => Current_Dominant);
+                    (IC, UIC, To_Vector (Case_Body), TB);
                end;
 
             --  Loop ends the current statement sequence, but we include
@@ -1901,15 +2064,11 @@ package body Instrument.C is
 
             when Cursor_While_Stmt =>
                declare
-                  While_Body     : constant Cursor_T := Get_Body (N);
-                  Cond_Var       : constant Cursor_T := Get_Cond_Var (N);
-                  Cond           : constant Cursor_T := Get_Cond (N);
-                  Inner_Dominant : constant Dominant_Info := ('S', N);
+                  While_Body : constant Cursor_T := Get_Body (N);
+                  Cond_Var   : constant Cursor_T := Get_Cond_Var (N);
+                  Cond       : constant Cursor_T := Get_Cond (N);
 
                begin
-                  UIC.Pass.Curlify (N   => While_Body,
-                                    Rew => UIC.Rewriter);
-
                   --  If the loop condition is a declaration, instrument its
                   --  initialization expression.
 
@@ -1923,7 +2082,7 @@ package body Instrument.C is
                   Process_Decisions_Defer (Cond, 'W');
                   Set_Statement_Entry;
                   Traverse_Statements
-                    (IC, UIC, To_Vector (While_Body), Inner_Dominant);
+                    (IC, UIC, To_Vector (While_Body), TB);
                end;
 
             --  Do while statement. Ends the current statement sequence.
@@ -1934,21 +2093,26 @@ package body Instrument.C is
                   Do_While : constant Cursor_T := Get_Cond (N);
 
                begin
-                  UIC.Pass.Curlify (N   => Do_Body,
-                                    Rew => UIC.Rewriter);
-
                   Traverse_Statements
-                    (IC, UIC, To_Vector (Do_Body), No_Dominant);
+                    (IC, UIC, To_Vector (Do_Body), TB);
+
+                  --  Insert the trailing braces resulting from the body
+                  --  traversal before the while.
+
+                  UIC.Pass.Insert_Text_After
+                    (UIC.Rewriter, Get_While_Loc (N), +TB);
+                  TB := +"";
+
+                  --  Process the while decision
+
                   Extend_Statement_Sequence
                     (Do_While, 'W', Instr_Scheme => Instr_Expr);
-
                   Process_Decisions_Defer (Do_While, 'W');
                   Set_Statement_Entry;
-                  Current_Dominant := ('S', Do_While);
 
                end;
 
-            --  For statement. Ends the current statement sequence.
+            --  For statement. Ends the current statement sequence
 
             when Cursor_For_Stmt =>
                declare
@@ -1969,29 +2133,13 @@ package body Instrument.C is
 
                   Set_Statement_Entry;
 
-                  --  The first statement that is nested in the FOR loop runs
-                  --  iff the guard expression evaluates to True. Set the
-                  --  dominant accordingly.
-
-                  Current_Dominant := ('T', For_Cond);
-
-                  UIC.Pass.Curlify (N => For_Body, Rew => UIC.Rewriter);
-                  Current_Dominant :=
-                    Traverse_Statements
-                      (IC, UIC,
-                       To_Vector (For_Body),
-                       Current_Dominant);
+                  Traverse_Statements
+                    (IC, UIC, To_Vector (For_Body), TB);
 
                   Extend_Statement_Sequence
                     (For_Inc, ' ', Instr_Scheme => Instr_Expr);
 
                   Set_Statement_Entry;
-
-                  --  Evaluation of the guard expression necessarily precedes
-                  --  evaluation of the first statement that follows the
-                  --  FOR loop.
-
-                  Current_Dominant := ('S', For_Cond);
                end;
 
             when Cursor_CXX_For_Range_Stmt =>
@@ -2018,11 +2166,8 @@ package body Instrument.C is
 
                   --  Generate obligations for body statements
 
-                  UIC.Pass.Curlify (N => For_Body, Rew => UIC.Rewriter);
                   Traverse_Statements
-                    (IC, UIC,
-                     To_Vector (For_Body),
-                     Current_Dominant);
+                    (IC, UIC, To_Vector (For_Body), TB);
                end;
 
            --  Unconditional goto, which is included in the current statement
@@ -2031,15 +2176,13 @@ package body Instrument.C is
             when Cursor_Goto_Stmt | Cursor_Indirect_Goto_Stmt =>
                Extend_Statement_Sequence (N, ' ');
                Set_Statement_Entry;
-               Current_Dominant := No_Dominant;
 
             when Cursor_Label_Stmt =>
                Set_Statement_Entry;
-               Current_Dominant := No_Dominant;
-               Traverse_Statements (IC, UIC, Get_Children (N));
+               Traverse_Statements (IC, UIC, Get_Children (N), TB);
 
             when Cursor_Stmt_Expr =>
-               Traverse_Statements (IC, UIC, Get_Children (N));
+               Traverse_Statements (IC, UIC, Get_Children (N), TB);
 
             --  Null statement, we won't monitor their execution
 
@@ -2063,7 +2206,10 @@ package body Instrument.C is
                end if;
          end case;
 
-         --  Traverse lambda expressions, if any
+         Append (Trailing_Braces, TB);
+
+         --  Traverse lambda expressions, if any. Do not register them as
+         --  scopes.
 
          Traverse_Declarations
            (IC  => IC,
@@ -2083,8 +2229,8 @@ package body Instrument.C is
       is
          Insert_Cursor : aliased Cursor_T := N;
 
-         F : constant Local_Source_Location := Start_Sloc (N);
-         T : Local_Source_Location := End_Sloc (N);
+         F : constant Source_Location := Start_Sloc (N);
+         T : Source_Location := End_Sloc (N);
          --  Source location bounds used to produce a SCO statement. By
          --  default, this should cover the same source location range as N,
          --  however for nodes that can contain other statements, we select an
@@ -2095,11 +2241,9 @@ package body Instrument.C is
          --  In the case of simple statements, set to null cursor and unused.
          --  Otherwise, use F and this node's end sloc for the emitted
          --  statement source location range.
-      begin
-         --  For now, instrument only cursors that come from the file being
-         --  instrumented, and do not instrument included code.
 
-         if Is_Null (N) or else not Is_Unit_Of_Interest (N, +UIC.File) then
+      begin
+         if Is_Null (N) or else not Is_Source_Of_Interest (UIC, N) then
             return;
          end if;
 
@@ -2142,27 +2286,6 @@ package body Instrument.C is
          SD_Last : constant Types.Int := SD.Last;
       begin
          for J in SC_First .. SC_Last loop
-            --  If there is a pending dominant for this statement sequence,
-            --  emit a SCO for it.
-
-            if J = SC_First and then Current_Dominant /= No_Dominant then
-               declare
-                  From : constant Local_Source_Location :=
-                    Start_Sloc (Current_Dominant.N);
-                  To   : constant Local_Source_Location :=
-                    End_Sloc (Current_Dominant.N);
-
-               begin
-                  UIC.Pass.Append_SCO
-                    (UIC  => UIC,
-                     N    => Current_Dominant.N,
-                     C1   => '>',
-                     C2   => Current_Dominant.K,
-                     From => From,
-                     To   => To,
-                     Last => False);
-               end;
-            end if;
             declare
                SCE : SC_Entry renames SC.Table (J);
             begin
@@ -2183,13 +2306,6 @@ package body Instrument.C is
             end;
          end loop;
 
-         --  Last statement of basic block, if present, becomes new current
-         --  dominant.
-
-         if SC_Last >= SC_First then
-            Current_Dominant := ('S', SC.Table (SC_Last).N);
-         end if;
-
          --  Clear out used section of SC table
 
          SC.Set_Last (SC_First - 1);
@@ -2211,6 +2327,77 @@ package body Instrument.C is
 
       end Set_Statement_Entry;
 
+      -------------
+      -- Curlify --
+      -------------
+
+      function Curlify (N : Cursor_T) return Boolean
+      is
+         Rew : Rewriter_T renames UIC.Rewriter;
+      begin
+         case Kind (N) is
+            when Cursor_If_Stmt =>
+               declare
+                  Then_Part : constant Cursor_T := Get_Then (N);
+                  Else_Part : constant Cursor_T := Get_Else (N);
+               begin
+                  if Kind (Then_Part) /= Cursor_Compound_Stmt then
+                     UIC.Pass.Insert_Text_Before
+                       (Rew, Start_Sloc (Then_Part), "{");
+                     if not Is_Null (Else_Part) then
+                        --  Close the brace introduced to wrap the then part
+                        --  if possible.
+
+                        UIC.Pass.Insert_Text_Before
+                          (Rew, Get_Else_Loc (N), "}");
+                     else
+                        --  Otherwise, we need to insert a trailing brace
+
+                        return True;
+                     end if;
+                  end if;
+
+                  --  Then, deal with the else if it is not a compound stmt
+
+                  if not Is_Null (Else_Part)
+                    and then Kind (Else_Part) /= Cursor_Compound_Stmt
+                  then
+                     UIC.Pass.Insert_Text_Before
+                       (Rew, Start_Sloc (Else_Part), "{");
+                     return True;
+                  end if;
+                  return False;
+               end;
+
+            when Cursor_Do_Stmt =>
+               declare
+                  Do_Body : constant Cursor_T := Get_Body (N);
+               begin
+                  if Kind (Do_Body) /= Cursor_Compound_Stmt then
+                     UIC.Pass.Insert_Text_Before
+                       (Rew, Start_Sloc (Do_Body), "{");
+                     UIC.Pass.Insert_Text_Before
+                       (Rew, Get_While_Loc (N), "}");
+                  end if;
+                  return False;
+               end;
+            when Cursor_While_Stmt
+               | Cursor_For_Stmt
+               | Cursor_CXX_For_Range_Stmt =>
+               declare
+                  B : constant Cursor_T := Get_Body (N);
+               begin
+                  if Kind (B) /= Cursor_Compound_Stmt then
+                     UIC.Pass.Insert_Text_Before (Rew, Start_Sloc (B), "{");
+                     return True;
+                  end if;
+                  return False;
+               end;
+            when others =>
+               return False;
+         end case;
+      end Curlify;
+
       use Cursor_Vectors;
 
       Emit_SCOs : Boolean := False;
@@ -2219,14 +2406,18 @@ package body Instrument.C is
 
    begin
       for N of L loop
-         Traverse_One (N);
+         if Length (Trailing_Braces) /= 0 then
+            UIC.Pass.Insert_Text_Before
+              (UIC.Rewriter, Start_Sloc (N), +Trailing_Braces);
+            Trailing_Braces := +"";
+         end if;
+         Traverse_One (N, Trailing_Braces);
          Emit_SCOs := True;
       end loop;
 
       if Emit_SCOs then
          Set_Statement_Entry;
       end if;
-      return Current_Dominant;
    end Traverse_Statements;
 
    ---------------------------
@@ -2246,48 +2437,66 @@ package body Instrument.C is
 
          --  Only traverse the function declarations that belong to a unit of
          --  interest.
+         if Is_Source_Of_Interest (UIC, N) then
 
-         begin
-            if Is_Unit_Of_Interest (N, +UIC.File) then
+            case Kind (N) is
 
-               case Kind (N) is
+               --  Traverse the statements of function bodies
 
-                  --  Traverse the statements of function bodies
+               when Cursor_Function_Decl
+                  | Cursor_Function_Template
+                  | Cursor_CXX_Method
+                  | Cursor_Constructor
+                  | Cursor_Destructor
+                  | Cursor_Lambda_Expr =>
 
-                  when Cursor_Function_Decl
-                     | Cursor_Function_Template
-                     | Cursor_CXX_Method
-                     | Cursor_Constructor
-                     | Cursor_Destructor
-                     | Cursor_Lambda_Expr =>
-                     declare
-                        --  Get_Body returns a Compound_Stmt, convert it to a
-                        --  list of statements using the Get_Children utility.
+                  UIC.Pass.Enter_Scope (UIC, N);
 
-                        Fun_Body : constant Cursor_Vectors.Vector :=
-                          Get_Children (Get_Body (N));
-                     begin
-                        if Fun_Body.Length > 0 then
-                           UIC.MCDC_State_Declaration_Node :=
-                             Fun_Body.First_Element;
-                           Traverse_Statements (IC, UIC, Fun_Body);
-                        end if;
-                     end;
+                  declare
+                     --  Get_Body returns a Compound_Stmt, convert it to
+                     --  a list of statements using the Get_Children
+                     --  utility.
 
-                   --  Traverse the declarations of a namespace / linkage
-                   --  specifier etc.
+                     Fun_Body : constant Cursor_T := Get_Body (N);
+                     Stmts    : constant Cursor_Vectors.Vector :=
+                       Get_Children (Fun_Body);
 
-                  when Cursor_Namespace
-                     | Cursor_Linkage_Spec
-                     | Cursor_Class_Template
-                     | Cursor_Class_Decl =>
-                     Traverse_Declarations (IC, UIC, Get_Children (N));
+                     TB : Unbounded_String;
+                     --  Trailing braces that should be inserted at the end
+                     --  of the function body.
+                  begin
+                     if Stmts.Length > 0 then
+                        UIC.MCDC_State_Declaration_Node :=
+                          Stmts.First_Element;
+                        Traverse_Statements (IC, UIC, Stmts, TB);
+                        UIC.Pass.Insert_Text_Before_Token
+                          (UIC.Rewriter, End_Sloc (Fun_Body), +TB);
+                     end if;
+                  end;
 
-                  when others =>
-                     null;
-               end case;
-            end if;
-         end;
+                  UIC.Pass.Exit_Scope (UIC);
+
+               --  Traverse the declarations of a namespace / linkage
+               --  specification etc.
+
+               when Cursor_Namespace
+                  | Cursor_Class_Template
+                  | Cursor_Class_Decl =>
+
+                  UIC.Pass.Enter_Scope (UIC, N);
+
+                  Traverse_Declarations (IC, UIC, Get_Children (N));
+
+                  UIC.Pass.Exit_Scope (UIC);
+
+               when Cursor_Linkage_Spec =>
+                  Traverse_Declarations (IC, UIC, Get_Children (N));
+
+               when others =>
+                  null;
+            end case;
+
+         end if;
       end loop;
 
       --  Restore previous MCDC_State insertion node: we can have lambda
@@ -2404,15 +2613,22 @@ package body Instrument.C is
       Success : Boolean;
       --  Whether this command is successful
 
-      PID                : constant Unsigned_64 :=
+      PID : constant Unsigned_64 :=
         Unsigned_64 (Pid_To_Integer (Current_Process_Id));
-      PP_Output_Filename : constant String :=
+
+      Preprocessed_Filename : constant String :=
+        (+Info.Output_Dir) / ("pp-" & Strip_Zero_Padding (Hex_Image (PID)));
+      --  Preprocessed file. We then postprocess it to remove redundant line
+      --  markers inserted by the preprocessor.
+
+      Preprocessor_Output_Filename : constant String :=
         (+Info.Output_Dir) /
         ("pp-output-" & Strip_Zero_Padding (Hex_Image (PID)));
-      PP_Output_File     : Ada.Text_IO.File_Type;
-   begin
-      PP_Filename := +Register_New_File (Info, Filename);
+      Preprocessor_Output_File     : Ada.Text_IO.File_Type;
+      --  File containing the preprocessor output (used to get include search
+      --  paths).
 
+   begin
       Cmd :=
         (Command => +Compiler_Driver (Info.Project, Instrumenter.Language),
          others  => <>);
@@ -2441,7 +2657,7 @@ package body Instrument.C is
 
       Append_Arg (Cmd, "-v");
       Append_Arg (Cmd, "-o");
-      Append_Arg (Cmd, +PP_Filename);
+      Append_Arg (Cmd, Preprocessed_Filename);
 
       --  Run the preprocessing command, keep track of whether it was
       --  successful for later
@@ -2449,7 +2665,7 @@ package body Instrument.C is
       Success := Run_Command
         (Command             => Cmd,
          Origin_Command_Name => "Preprocessing",
-         Output_File         => PP_Output_Filename,
+         Output_File         => Preprocessor_Output_Filename,
          Ignore_Error        => True);
 
       --  Clear the search path so that we populate it from the include search
@@ -2463,7 +2679,7 @@ package body Instrument.C is
       --  ...
       --  End of search list
 
-      Open (PP_Output_File, In_File, PP_Output_Filename);
+      Open (Preprocessor_Output_File, In_File, Preprocessor_Output_Filename);
 
       declare
          RE_Begin_Pattern      : constant Pattern_Matcher :=
@@ -2473,9 +2689,9 @@ package body Instrument.C is
            Compile ("End of search list");
          Matches               : Match_Array (0 .. 0);
       begin
-         while not End_Of_File (PP_Output_File) loop
+         while not End_Of_File (Preprocessor_Output_File) loop
             declare
-               Line : constant String := Get_Line (PP_Output_File);
+               Line : constant String := Get_Line (Preprocessor_Output_File);
             begin
                Match (RE_Begin_Pattern, Line, Matches);
                if Matches (0) /= No_Match then
@@ -2506,26 +2722,35 @@ package body Instrument.C is
             --  output.
 
             if not Begin_Pattern_Matched then
-               Reset (PP_Output_File);
+               Reset (Preprocessor_Output_File);
             end if;
 
-            while not End_Of_File (PP_Output_File) loop
-               Put_Line (Get_Line (PP_Output_File));
+            while not End_Of_File (Preprocessor_Output_File) loop
+               Put_Line (Get_Line (Preprocessor_Output_File));
             end loop;
-            Delete (PP_Output_File);
+            Delete (Preprocessor_Output_File);
             Fatal_Error ("Preprocessing failed: aborting");
          end if;
+         Delete (Preprocessor_Output_File);
       end;
 
-      Delete (PP_Output_File);
+      --  Now, onto the postprocessing. Remove spurious system header line
+      --  markers.
+
+      PP_Filename := +Register_New_File (Info, Filename);
+      Postprocess_Source (Preprocessed_Filename, +PP_Filename);
+      Ada.Directories.Delete_File (Preprocessed_Filename);
    end Preprocess_Source;
 
    --------------------------
    -- Common_Parse_TU_Args --
    --------------------------
 
-   function Common_Parse_TU_Args return String_Vectors.Vector is
+   function Common_Parse_TU_Args
+     (Lang : Some_Language) return String_Vectors.Vector
+   is
       Command_Line_Args : String_Vectors.Vector;
+      use Ada.Characters.Handling;
       use String_Vectors;
    begin
       --  We will get errors when parsing a gcc-preprocessed file with clang:
@@ -2535,6 +2760,13 @@ package body Instrument.C is
 
       Append (Command_Line_Args, +"-ferror-limit=0");
 
+      --  Pass explicitly the language through the command-line, as we can
+      --  redefine file suffixes with gprbuild, and <file>.c can be a C++
+      --  file.
+
+      Append (Command_Line_Args, +"-x");
+      Append (Command_Line_Args, +To_Lower (Image (Lang)));
+
       return Command_Line_Args;
    end Common_Parse_TU_Args;
 
@@ -2565,7 +2797,8 @@ package body Instrument.C is
           (Exclude_Declarations_From_PCH => 0, Display_Diagnostics => 0);
 
       Add_Options (Args, Options);
-      String_Vectors.Append (Args, Common_Parse_TU_Args);
+      String_Vectors.Append
+        (Args, Common_Parse_TU_Args (Instrumenter.Language));
       declare
          C_Args : chars_ptr_array := To_Chars_Ptr_Array (Args);
       begin
@@ -2637,9 +2870,9 @@ package body Instrument.C is
       end if;
    end Finalize;
 
-   ---------------
-   -- Emit_SCOs --
-   ---------------
+   --------------------
+   -- Record_PP_Info --
+   --------------------
 
    procedure Record_PP_Info
      (Unit_Info : Instrumented_Unit_Info;
@@ -2659,7 +2892,7 @@ package body Instrument.C is
       --  the user's preprocessor.
 
       Add_Options (Args, UIC.Options);
-      String_Vectors.Append (Args, Common_Parse_TU_Args);
+      String_Vectors.Append (Args, Common_Parse_TU_Args (Unit_Info.Language));
 
       --  TODO??? We should also inhibit the use of clang predefined macros,
       --  with the -undef option, but doing this yields parsing errors, and a
@@ -2737,9 +2970,10 @@ package body Instrument.C is
       --  Where to insert extern declarations
 
       procedure Put_Extern_Decl
-        (C_Type    : String;
-         Name      : String;
-         Func_Args : String := "");
+        (C_Type     : String;
+         Name       : String;
+         Array_Size : String := "";
+         Func_Args  : String := "");
       --  Local shortcut to avoid passing UIC.TU/UIC.Rewriter explicitly
 
       ---------------------
@@ -2747,9 +2981,10 @@ package body Instrument.C is
       ---------------------
 
       procedure Put_Extern_Decl
-        (C_Type    : String;
-         Name      : String;
-         Func_Args : String := "") is
+        (C_Type     : String;
+         Name       : String;
+         Array_Size : String := "";
+         Func_Args  : String := "") is
       begin
          Put_Extern_Decl
            (UIC.Rewriter,
@@ -2757,6 +2992,7 @@ package body Instrument.C is
             Instrumenter,
             C_Type,
             Name,
+            Array_Size,
             Func_Args);
       end Put_Extern_Decl;
 
@@ -2764,6 +3000,7 @@ package body Instrument.C is
 
    begin
       SCOs.Initialize;
+
       UIC.SFI := Get_Index_From_Generic_Name
         (Orig_Filename,
          Kind                => Files_Table.Source_File,
@@ -2774,7 +3011,6 @@ package body Instrument.C is
       UIC.Instrumented_Unit := CU_Name;
       UIC.Buffer_Unit :=
         CU_Name_For_File (+Buffer_Filename, CU_Name.Project_Name);
-      UIC.File := +Orig_Filename;
 
       --  Import analysis options for the file to preprocess, then run the
       --  preprocessor.
@@ -2841,12 +3077,12 @@ package body Instrument.C is
             Search_Exempt_In_Token'Access);
       end Search_Exemptions;
 
-      --  Flush the SCO_Table. We want the SCOs to be located at their
-      --  presumed (i.e. accounting for line directives) preprocessed source
-      --  range, to have unique locations for each.
+      --  Save the last SCO of the first pass (for a consistency check with
+      --  the second pass later), and reset the SCO tables for the
+      --  instrumentation pass.
 
       Record_PP_Info_Last_SCO := SCOs.SCO_Table.Last;
-      SCOs.SCO_Table.Init;
+      SCOs.Initialize;
 
       --  Then, instrument
 
@@ -2859,20 +3095,14 @@ package body Instrument.C is
                        Preprocessed => True);
       UIC.TU := Rewriter.TU;
       UIC.Rewriter := Rewriter.Rewriter;
-      Insert_Extern_Location := Find_First_Insert_Location (UIC.TU);
+      Insert_Extern_Location :=
+        Start_Sloc (Get_Translation_Unit_Cursor (UIC.TU));
 
       Traverse_Declarations
         (IC  => IC,
          UIC => UIC,
          L   => Get_Children (Get_Translation_Unit_Cursor (UIC.TU)));
 
-      SCOs.SCO_Unit_Table.Append
-        ((File_Name  => new String'(Orig_Filename),
-          File_Index => UIC.SFI,
-          Dep_Num    => 1,
-          From       => SCOs.SCO_Table.First,
-          To         => SCOs.SCO_Table.Last));
-
       --  Check whether there is a mismatch between Last_SCO and
       --  SCOs.SCO_Table.Last. If there is, warn the user and discard the
       --  preprocessed info: the SCO mapping will be wrong.
@@ -2888,19 +3118,27 @@ package body Instrument.C is
          UIC.LL_PP_Info_Map.Clear;
       end if;
 
+      --  Now that the set of coverage obligations and the set of source files
+      --  are known, allocate the required set of coverage buffers (one per
+      --  source file) and assign the corresponding buffers indexes.
+
+      for Cur in UIC.Instrumented_Entities.Iterate loop
+         UIC.Instrumented_Entities.Reference (Cur).Buffers_Index :=
+           Create_Unit_Bits
+             (UIC.Allocated_Bits, C_Instrumented_Entities_Maps.Key (Cur));
+      end loop;
+
       --  Convert low level SCOs from the instrumenter to high level SCOs.
       --  This creates BDDs for every decision.
 
       declare
-         SCO_Map       : aliased LL_HL_SCO_Map :=
+         SCO_Map : aliased LL_HL_SCO_Map :=
            (SCOs.SCO_Table.First .. SCOs.SCO_Table.Last => No_SCO_Id);
-         Bit_Maps      : CU_Bit_Maps;
-         Created_Units : Created_Unit_Maps.Map;
       begin
          Process_Low_Level_SCOs
            (Provider      => SC_Obligations.Instrumenter,
             Origin        => UIC.SFI,
-            Created_Units => Created_Units,
+            Created_Units => UIC.CUs,
             SCO_Map       => SCO_Map'Access,
             Count_Paths   => True);
 
@@ -2918,83 +3156,130 @@ package body Instrument.C is
             end;
          end loop;
 
-         --  In the instrumentation case, the origin of SCO information is
-         --  the original source file.
-
-         UIC.CU := Created_Units.Element (UIC.SFI);
-
          --  Import annotations in our internal tables
 
-         UIC.Import_Annotations;
+         UIC.Import_Annotations (UIC.CUs);
          Filter_Annotations;
 
-         for SS of UIC.Source_Statements loop
-            Insert_Statement_Witness (UIC, SS);
-         end loop;
-
-         if Coverage.Enabled (Coverage_Options.Decision)
-            or else MCDC_Coverage_Enabled
-         then
-            for SD of UIC.Source_Decisions loop
-               Insert_Decision_Witness
-                 (UIC, SD, Path_Count (SCO_Map (SD.LL_SCO)));
-            end loop;
+         for Cur in UIC.Instrumented_Entities.Iterate loop
+            declare
+               SFI : constant Valid_Source_File_Index :=
+                 C_Instrumented_Entities_Maps.Key (Cur);
+               Ent : C_Instrumented_Entities renames
+                 UIC.Instrumented_Entities.Reference (Cur);
+
+               Unit_Bits : Allocated_Bits renames
+                 UIC.Allocated_Bits.Reference (Ent.Buffers_Index);
+               Bit_Maps  : CU_Bit_Maps;
+            begin
+               --  Allocate bits in covearge buffers and insert the
+               --  corresponding witness calls.
 
-            if MCDC_Coverage_Enabled then
+               for SS of Ent.Statements loop
+                  Insert_Statement_Witness (UIC, Ent.Buffers_Index, SS);
+               end loop;
 
-               --  As high-level SCO tables have been populated, we have built
-               --  BDDs for each decisions, and we can now set the correct
-               --  MC/DC path offset for each condition.
+               if Coverage.Enabled (Coverage_Options.Decision)
+                  or else MCDC_Coverage_Enabled
+               then
+                  for SD of Ent.Decisions loop
+                     Insert_Decision_Witness
+                       (UIC,
+                        Ent.Buffers_Index,
+                        SD,
+                        Path_Count (SCO_Map (SD.LL_SCO)));
+                  end loop;
+
+                  if MCDC_Coverage_Enabled then
+
+                     --  As high-level SCO tables have been populated, we have
+                     --  built BDDs for each decisions, and we can now set the
+                     --  correct MC/DC path offset for each condition.
+
+                     for SC of Ent.Conditions loop
+                        declare
+                           Condition : constant SCO_Id := SCO_Map (SC.LL_SCO);
+                           Decision  : constant SCO_Id :=
+                             Enclosing_Decision (Condition);
+                        begin
+                           if Path_Count (Decision) = 0 then
+                              Set_Decision_SCO_Non_Instr_For_MCDC (Decision);
+                           else
+                              Insert_Condition_Witness
+                                (UIC, SC, Offset_For_True (Condition));
+                           end if;
+                        end;
+                     end loop;
+                  end if;
+               end if;
 
-               for SC of UIC.Source_Conditions loop
-                  Insert_Condition_Witness
-                    (UIC, SC, Offset_For_True (SCO_Map (SC.LL_SCO)));
+               --  Create mappings from alloacted bits to the corresponding SCO
+               --  discharging information (Bit_Maps).
+
+               Bit_Maps :=
+                 (Statement_Bits => new Statement_Bit_Map'
+                    (Bit_Id'First .. Unit_Bits.Last_Statement_Bit =>
+                       No_SCO_Id),
+                  Decision_Bits  => new Decision_Bit_Map'
+                    (Bit_Id'First .. Unit_Bits.Last_Outcome_Bit =>
+                       (No_SCO_Id, False)),
+                  MCDC_Bits      => new MCDC_Bit_Map'
+                    (Bit_Id'First .. Unit_Bits.Last_Path_Bit =>
+                       (No_SCO_Id, 0)));
+
+               for S_Bit_Alloc of Unit_Bits.Statement_Bits loop
+                  Bit_Maps.Statement_Bits (S_Bit_Alloc.Executed) :=
+                    SCO_Map (S_Bit_Alloc.LL_S_SCO);
                end loop;
-            end if;
-         end if;
 
-         Bit_Maps :=
-           (Statement_Bits => new Statement_Bit_Map'
-              (Bit_Id'First .. UIC.Unit_Bits.Last_Statement_Bit => No_SCO_Id),
-            Decision_Bits  => new Decision_Bit_Map'
-              (Bit_Id'First .. UIC.Unit_Bits.Last_Outcome_Bit =>
-                   (No_SCO_Id, False)),
-            MCDC_Bits      =>
-               new MCDC_Bit_Map'(Bit_Id'First .. UIC.Unit_Bits.Last_Path_Bit =>
-                                     (No_SCO_Id, 0)));
-
-         for S_Bit_Alloc of UIC.Unit_Bits.Statement_Bits loop
-            Bit_Maps.Statement_Bits (S_Bit_Alloc.Executed) :=
-              SCO_Map (S_Bit_Alloc.LL_S_SCO);
-         end loop;
+               for D_Bit_Alloc of Unit_Bits.Decision_Bits loop
+                  declare
+                     D_SCO : constant SCO_Id := SCO_Map (D_Bit_Alloc.LL_D_SCO);
+                  begin
+                     for Outcome in Boolean loop
+                        Bit_Maps.Decision_Bits
+                          (D_Bit_Alloc.Outcome_Bits (Outcome)) :=
+                          (D_SCO, Outcome);
+                     end loop;
 
-         for D_Bit_Alloc of UIC.Unit_Bits.Decision_Bits loop
-            declare
-               D_SCO : constant SCO_Id := SCO_Map (D_Bit_Alloc.LL_D_SCO);
-            begin
-               for Outcome in Boolean loop
-                  Bit_Maps.Decision_Bits
-                    (D_Bit_Alloc.Outcome_Bits (Outcome)) := (D_SCO, Outcome);
+                     if MCDC_Coverage_Enabled
+                       and then D_Bit_Alloc.Path_Bits_Base /= No_Bit_Id
+                     then
+                        declare
+                           Path_Count : constant Natural :=
+                             SC_Obligations.Path_Count (D_SCO);
+                        begin
+                           for J in 1 .. Any_Bit_Id (Path_Count) loop
+                              Bit_Maps.MCDC_Bits
+                                (D_Bit_Alloc.Path_Bits_Base + J - 1) :=
+                                (D_SCO, Natural (J - 1));
+                           end loop;
+                        end;
+                     end if;
+                  end;
                end loop;
 
-               if MCDC_Coverage_Enabled
-                 and then D_Bit_Alloc.Path_Bits_Base /= No_Bit_Id
-               then
+               --  Associate these bit maps to the corresponding CU
+
+               Set_Bit_Maps (UIC.CUs.Element (SFI), Bit_Maps);
+
+               --  Iterate through the package level body entities
+
+               Remap_Scopes (UIC.Scopes, SCO_Map);
+
+               for C in UIC.Scopes.Iterate loop
                   declare
-                     Path_Count : constant Natural :=
-                       SC_Obligations.Path_Count (D_SCO);
+                     CU : constant Created_Unit_Maps.Cursor :=
+                       UIC.CUs.Find (Scopes_In_Files_Map.Key (C));
                   begin
-                     for J in 1 .. Any_Bit_Id (Path_Count) loop
-                        Bit_Maps.MCDC_Bits
-                          (D_Bit_Alloc.Path_Bits_Base + J - 1) :=
-                          (D_SCO, Natural (J - 1));
-                     end loop;
+                     if Created_Unit_Maps.Has_Element (CU) then
+                        Set_Scope_Entity (Created_Unit_Maps.Element (CU),
+                                          Scopes_In_Files_Map.Element (C));
+                     end if;
                   end;
-               end if;
+               end loop;
             end;
          end loop;
-
-         Set_Bit_Maps (UIC.CU, Bit_Maps);
       end;
 
       --  We import the extern declaration of symbols instead of including the
@@ -3011,15 +3296,23 @@ package body Instrument.C is
       --  libraries, which we don't want. To be safe, we will declare
       --  GNATcov_RTS functions and symbols as extern.
 
-      Put_Extern_Decl
-        ("unsigned char *",
-         Statement_Buffer_Symbol (UIC.Instrumented_Unit));
-      Put_Extern_Decl
-        ("unsigned char *",
-         Decision_Buffer_Symbol (UIC.Instrumented_Unit));
-      Put_Extern_Decl
-        ("unsigned char *",
-         MCDC_Buffer_Symbol (UIC.Instrumented_Unit));
+      declare
+         Buffers_Count : constant String :=
+           Img (Natural (UIC.Allocated_Bits.Length));
+      begin
+         Put_Extern_Decl
+           ("unsigned char *",
+            Statement_Buffer_Symbol (UIC.Instrumented_Unit),
+            Array_Size => Buffers_Count);
+         Put_Extern_Decl
+           ("unsigned char *",
+            Decision_Buffer_Symbol (UIC.Instrumented_Unit),
+            Array_Size => Buffers_Count);
+         Put_Extern_Decl
+           ("unsigned char *",
+            MCDC_Buffer_Symbol (UIC.Instrumented_Unit),
+            Array_Size => Buffers_Count);
+      end;
       Put_Extern_Decl
         ("unsigned",
          "gnatcov_rts_witness",
@@ -3062,6 +3355,40 @@ package body Instrument.C is
       Rewriter.Apply;
    end Instrument_Source_File;
 
+   ----------------------------
+   -- Format_Array_Init_Expr --
+   ----------------------------
+
+   function Format_Array_Init_Expr
+     (Exprs     : String_Vectors.Vector;
+      Multiline : Boolean := False) return String
+   is
+      Result : Unbounded_String;
+   begin
+      Append (Result, "{");
+      if Multiline then
+         Append (Result, ASCII.LF);
+      end if;
+      for I in Exprs.First_Index .. Exprs.Last_Index loop
+         if I > Exprs.First_Index then
+            if Multiline then
+               Append (Result, "," & ASCII.LF);
+            else
+               Append (Result, ", ");
+            end if;
+         end if;
+         if Multiline then
+            Append (Result, "  ");
+         end if;
+         Append (Result, To_String (Exprs.Element (I)));
+      end loop;
+      if Multiline then
+         Append (Result, ASCII.LF);
+      end if;
+      Append (Result, "}");
+      return To_String (Result);
+   end Format_Array_Init_Expr;
+
    ----------------------
    -- Emit_Buffer_Unit --
    ----------------------
@@ -3074,149 +3401,256 @@ package body Instrument.C is
       CU_Name : Compilation_Unit_Name renames UIC.Buffer_Unit;
       File    : Text_Files.File_Type;
 
-      --  As a reminder, the representation of a static-array variable differs
-      --  from a pointer-to-array variable.
+      Buffers_Count : constant Natural := Natural (UIC.Allocated_Bits.Length);
+      --  Number of items in the buffer group to create
+
+      --  Symbol names for the arrays of buffer bits (one for statements, one
+      --  for decisions, one for MC/DC paths). They are exported, so they need
+      --  to be unique program-wide.
 
-      Statement_Buffer : constant String :=
+      Statement_Buffers : constant String :=
         Statement_Buffer_Symbol (UIC.Instrumented_Unit);
-      Decision_Buffer  : constant String :=
+      Decision_Buffers  : constant String :=
         Decision_Buffer_Symbol (UIC.Instrumented_Unit);
-      MCDC_Buffer      : constant String :=
+      MCDC_Buffers      : constant String :=
         MCDC_Buffer_Symbol (UIC.Instrumented_Unit);
 
-      Statement_Buffer_Repr : constant String :=
-        "__" & Statement_Buffer_Symbol (UIC.Instrumented_Unit);
-      Decision_Buffer_Repr  : constant String :=
-        "__" & Decision_Buffer_Symbol (UIC.Instrumented_Unit);
-      MCDC_Buffer_Repr      : constant String :=
-        "__" & MCDC_Buffer_Symbol (UIC.Instrumented_Unit);
+      Buffers_Array : constant String := "__xcov_buffers_group";
+      --  Name of the array of gnatcov_rts_coverage_buffers struct for this
+      --  unit.
+
+      Statement_Init : String_Vectors.Vector;
+      Decision_Init  : String_Vectors.Vector;
+      MCDC_Init      : String_Vectors.Vector;
+      --  Initialization expressions for the pointer arrays: each element in a
+      --  vector is an array item initializer.
+
+      Group_Init : String_Vectors.Vector;
+      --  Likewise, but for the coverage buffers group
+
+      Buffers_CUs      : CU_Id_Vectors.Vector :=
+        CU_Id_Vectors.To_Vector (No_CU_Id, UIC.Allocated_Bits.Length);
+      Buffers_CU_Names : CU_Name_Vectors.Vector :=
+        CU_Name_Vectors.To_Vector
+           ((Language_Kind => File_Based_Language, others => <>),
+            UIC.Allocated_Bits.Length);
+      --  For each set of buffers in UIC.Allocated_Bits, corresponding CU_Id
+      --  and CU_Name for the instrumented source file.
+
+      I : Positive := 1;
    begin
-      Create_File (Info, File, +CU_Name.Filename);
+      --  Compute Buffers_Filenames and Buffers_Projects
 
-      declare
-         Fingerprint : Unbounded_String;
+      for Cur in UIC.Sources_Of_Interest.Iterate loop
+         declare
+            use Created_Unit_Maps;
+
+            SOI   : Source_Of_Interest renames
+              UIC.Sources_Of_Interest.Constant_Reference (Cur);
+            Cur   : Cursor;
+            Index : Natural;
+         begin
+            if SOI.Of_Interest then
+               Cur := UIC.CUs.Find (SOI.SFI);
+               if Has_Element (Cur) then
+                  Index :=
+                    UIC.Instrumented_Entities.Constant_Reference
+                      (SOI.SFI).Buffers_Index;
+                  Buffers_CUs (Index) := Element (Cur);
+                  Buffers_CU_Names (Index) := SOI.CU_Name;
+               end if;
+            end if;
+         end;
+      end loop;
 
-         Unit_Name    : constant String := +UIC.Instrumented_Unit.Filename;
-         Project_Name : constant String := +UIC.Instrumented_Unit.Project_Name;
+      --  Each source file in UIC.Instrumented_Entities is supposed to have a
+      --  corresponding "Of_Interest => True" entry in UIC.Sources_Of_Interest.
 
-         --  Do not use 'Image so that we use the original casing for the
-         --  enumerators, and thus avoid compilation warnings/errors.
+      pragma Assert (for all CU of Buffers_CUs => CU /= No_CU_Id);
 
-         Statement_Last_Bit : constant String := Img
-           (UIC.Unit_Bits.Last_Statement_Bit);
-         Decision_Last_Bit  : constant String := Img
-           (UIC.Unit_Bits.Last_Outcome_Bit);
-         MCDC_Last_Bit      : constant String := Img
-           (UIC.Unit_Bits.Last_Path_Bit);
-      begin
-         --  Turn the fingerprint value into the corresponding C literal (array
-         --  of uint8_t).
+      --  Start to emit the buffer unit
 
+      Create_File (Info, File, +CU_Name.Filename);
+
+      File.Put_Line ("#include ""gnatcov_rts_c-buffers.h""");
+      File.New_Line;
+
+      --  Define coverage buffers for each source file:
+      --
+      --  * First, create individual buffers for each kind (statement, decision
+      --    and MC/DC). These are static, so no need to create symbol names
+      --    that are unique program-wide.
+      --
+      --  * Then, create pointers to these individual buffers. Put all pointers
+      --    to each kind of buffers in an exported array. Since both pointers
+      --    and the exported array are exported (used in buffer list units and
+      --    instrumented units), we need to create symbol names that are unique
+      --    program-wide.
+
+      --  Create the static buffers as well as the
+      --  gnatcov_rts_coverage_buffers struct for each source file.
+
+      for Cur in UIC.Instrumented_Entities.Iterate loop
          declare
-            First : Boolean := True;
+            SFI           : constant Valid_Source_File_Index :=
+              C_Instrumented_Entities_Maps.Key (Cur);
+            Buffers_Index : constant Positive :=
+              UIC.Instrumented_Entities.Constant_Reference (SFI).Buffers_Index;
+            Unit_Bits     : Allocated_Bits renames
+              UIC.Allocated_Bits.Constant_Reference (Buffers_Index);
+
+            CU      : constant CU_Id := Buffers_CUs (Buffers_Index);
+            CU_Name : Compilation_Unit_Name renames
+              Buffers_CU_Names.Constant_Reference (Buffers_Index);
+
+            --  Symbol name for each kind of static buffer
+
+            Suffix : constant String := "_" & Img (I);
+
+            Statement_Buffer_Repr : constant String := "__stmt_bits" & Suffix;
+            Decision_Buffer_Repr  : constant String := "__dc_bits" & Suffix;
+            MCDC_Buffer_Repr      : constant String := "__mcdc_bits" & Suffix;
+
+            Buffers_Struct : constant String := "__xcov_buffers" & Suffix;
+            --  Name of the gnatcov_rts_coverage_buffers struct for this
+            --  source file.
+
          begin
-            Append (Fingerprint, "{");
-            for Byte of SC_Obligations.Fingerprint (UIC.CU) loop
-               if First then
-                  First := False;
-               else
-                  Append (Fingerprint, ", ");
-               end if;
-               Append (Fingerprint, Strings.Img (Integer (Byte)));
-            end loop;
-            Append (Fingerprint, "}");
+            File.Put_Line ("/* Buffers for " & Get_Full_Name (SFI) & " */");
+
+            --  Static buffers
+
+            File.Put_Line
+              ("static unsigned char " & Statement_Buffer_Repr & "["
+               & Img (Any_Bit_Id'Max (1, Unit_Bits.Last_Statement_Bit + 1))
+               & "];");
+            Statement_Init.Append (+("&" & Statement_Buffer_Repr & "[0]"));
+
+            File.Put_Line
+              ("static unsigned char " & Decision_Buffer_Repr & "["
+               & Img (Any_Bit_Id'Max (1, Unit_Bits.Last_Outcome_Bit + 1))
+               & "];");
+            Decision_Init.Append (+("&" & Decision_Buffer_Repr & "[0]"));
+
+            File.Put_Line
+              ("static unsigned char " & MCDC_Buffer_Repr & "["
+               & Img (Any_Bit_Id'Max (1, Unit_Bits.Last_Path_Bit + 1))
+               & "];");
+            MCDC_Init.Append (+("&" & MCDC_Buffer_Repr & "[0]"));
+
+            --  gnatcov_rts_coverage_buffers struct
+
+            File.Put_Line
+              ("static const struct gnatcov_rts_coverage_buffers "
+               & Buffers_Struct & " = {"
+               & ASCII.LF
+               & "  .fingerprint = "
+               & Format_Fingerprint (SC_Obligations.Fingerprint (CU)) & ","
+               & ASCII.LF
+               & "  .language_kind = FILE_BASED_LANGUAGE,"
+               & ASCII.LF
+               & "  .unit_part = NOT_APPLICABLE_PART,"
+               & ASCII.LF
+
+               --  Old toolchains (for instance GNAT Pro 7.1.2) consider that
+               --  "STR(<string literal>)" is not a static expression, and thus
+               --  refuse using STR to initialize a global data structure. To
+               --  workaround this, emit a gnatcov_rtr_string literal
+               --  ourselves.
+
+               & "  .unit_name = " & Format_Str_Constant (+CU_Name.Filename)
+               & ","
+               & ASCII.LF
+               & "  .project_name = "
+               & Format_Str_Constant (+CU_Name.Project_Name) & ","
+               & ASCII.LF
+
+               & "  .bit_maps_fingerprint = "
+               & Format_Fingerprint (SC_Obligations.Bit_Maps_Fingerprint (CU))
+               & ","
+               & ASCII.LF
+
+               --  We do not use the created pointer (Statement_Buffer) to
+               --  initialize the buffer fields, as this is rejected by old
+               --  versions of the compiler (up to the 20 version): the
+               --  initializer element is considered not constant. To work
+               --  around it, we simply use the original expression instead of
+               --  using a wrapper pointer.
+
+               & "  .statement = &" & Statement_Buffer_Repr & "[0],"
+               & ASCII.LF
+               & "  .decision = &" & Decision_Buffer_Repr & "[0],"
+               & ASCII.LF
+               & "  .mcdc = &" & MCDC_Buffer_Repr & "[0],"
+               & ASCII.LF
+
+               & "  .statement_last_bit = "
+               & Img (Unit_Bits.Last_Statement_Bit) & ","
+               & ASCII.LF
+               & "  .decision_last_bit = " & Img (Unit_Bits.Last_Outcome_Bit)
+               & ","
+               & ASCII.LF
+               & "  .mcdc_last_bit = " & Img (Unit_Bits.Last_Path_Bit)
+               & ASCII.LF
+               & "};");
+            Group_Init.Append (+("&" & Buffers_Struct));
+
+            File.New_Line;
+
+            --  Track which CU_Id maps to which instrumented unit
+
+            Instrumented_Unit_CUs.Insert (CU_Name, CU);
+
+            I := I + 1;
          end;
+      end loop;
 
-         File.Put_Line ("#include ""gnatcov_rts_c-buffers.h""");
-         File.New_Line;
-         Put_Format_Def
-           (File,
-            Instrumenter,
-            "unsigned char",
-            Statement_Buffer_Repr,
-            Array_Size =>
-              Img (Any_Bit_Id'Max (1, UIC.Unit_Bits.Last_Statement_Bit + 1)));
-         Put_Format_Def
-           (File,
-            Instrumenter,
-            "unsigned char *const",
-            Statement_Buffer,
-            Init_Expr => "&" & Statement_Buffer_Repr & "[0]");
+      declare
+         Buffers_Count_Img : constant String := Img (Buffers_Count);
+      begin
+         --  Then define pointers to these individual buffers. Put all pointers
+         --  to statement buffers in an exported array (same for decision and
+         --  for MC/DC).
 
-         Put_Format_Def
-           (File,
-            Instrumenter,
-            "unsigned char",
-            Decision_Buffer_Repr,
-            Array_Size =>
-              Img (Any_Bit_Id'Max (1, UIC.Unit_Bits.Last_Outcome_Bit + 1)));
          Put_Format_Def
            (File,
             Instrumenter,
             "unsigned char *const",
-            Decision_Buffer,
-            Init_Expr => "&" & Decision_Buffer_Repr & "[0]");
-
+            Statement_Buffers,
+            Array_Size => Buffers_Count_Img,
+            Init_Expr  => Format_Array_Init_Expr
+                            (Statement_Init, Multiline => True));
          Put_Format_Def
            (File,
             Instrumenter,
-            "unsigned char",
-            MCDC_Buffer_Repr,
-            Array_Size =>
-              Img (Any_Bit_Id'Max (1, UIC.Unit_Bits.Last_Path_Bit + 1)));
+            "unsigned char *const",
+            Decision_Buffers,
+            Array_Size => Buffers_Count_Img,
+            Init_Expr  => Format_Array_Init_Expr
+                            (Decision_Init, Multiline => True));
          Put_Format_Def
            (File,
             Instrumenter,
             "unsigned char *const",
-            MCDC_Buffer,
-            Init_Expr => "&" & MCDC_Buffer_Repr & "[0]");
+            MCDC_Buffers,
+            Array_Size => Buffers_Count_Img,
+            Init_Expr  => Format_Array_Init_Expr
+                            (MCDC_Init, Multiline => True));
+
+         --  Create the buffers group: first the array of buffers (static),
+         --  then the gnatcov_rts_coverage_buffers_group struct (extern).
 
+         File.Put_Line
+           ("static const struct gnatcov_rts_coverage_buffers *"
+            & Buffers_Array & "[" & Buffers_Count_Img & "] = "
+            & Format_Array_Init_Expr (Group_Init, Multiline => True) & ";");
          Put_Format_Def
            (File,
             Instrumenter,
-            "struct gnatcov_rts_unit_coverage_buffers",
+            "const struct gnatcov_rts_coverage_buffers_group",
             Unit_Buffers_Name (UIC.Instrumented_Unit),
             Init_Expr =>
-              "{"
-              & ASCII.LF
-              & "  .fingerprint = " & To_String (Fingerprint) & ","
-              & ASCII.LF
-              & "  .language_kind = FILE_BASED_LANGUAGE,"
-              & ASCII.LF
-              & "  .unit_part = NOT_APPLICABLE_PART,"
-              & ASCII.LF
-
-              --  Old toolchains (for instance GNAT Pro 7.1.2) consider that
-              --  "STR(<string literal>)" is not a static expression, and thus
-              --  refuse using STR to initialize a global data structure. To
-              --  workaround this, emit a gnatcov_rtr_string literal ourselves.
-
-              & "  .unit_name = " & Format_Str_Constant (Unit_Name) & ","
-              & ASCII.LF
-              & "  .project_name = " & Format_Str_Constant (Project_Name) & ","
-              & ASCII.LF
-
-              --  We do not use the created pointer (Statement_Buffer) to
-              --  initialize the buffer fields, as this is rejected by old
-              --  versions of the compiler (up to the 20 version): the
-              --  initializer element is considered not constant. To work
-              --  around it, we simply use the original expression instead of
-              --  using a wrapper pointer.
-
-              & "  .statement = &" & Statement_Buffer_Repr & "[0],"
-              & ASCII.LF
-              & "  .decision = &" & Decision_Buffer_Repr & "[0],"
-              & ASCII.LF
-              & "  .mcdc = &" & MCDC_Buffer_Repr & "[0],"
-              & ASCII.LF
-
-              & "  .statement_last_bit = " & Statement_Last_Bit & ","
-              & ASCII.LF
-              & "  .decision_last_bit = " & Decision_Last_Bit & ","
-              & ASCII.LF
-              & "  .mcdc_last_bit = " & MCDC_Last_Bit
-              & ASCII.LF
-              & "}");
+              "{" & Img (Buffers_Count) & ", &" & Buffers_Array & "[0]}");
       end;
    end Emit_Buffer_Unit;
 
@@ -3275,10 +3709,14 @@ package body Instrument.C is
                               & "gnatcov_rts_c-traces-output-base64.h""");
          end case;
          File.Put_Line ("#include <stdlib.h>");
-         File.Put_Line
-           ("#include """ & Buffers_List_Filename (IC)
-            & Source_Suffix (Instrumenter, GPR.Unit_Spec, Info.Project)
-            & """");
+
+         --  Import the coverage buffers
+
+         Put_Extern_Decl
+           (File,
+            Instrumenter,
+            "const struct gnatcov_rts_coverage_buffers_group_array",
+            Unit_Buffers_Array_Name (+IC.Project_Name));
 
          --  Emit the procedure to write the trace file
 
@@ -3287,7 +3725,8 @@ package body Instrument.C is
          File.Put_Line ("void " & Dump_Procedure & " (void) {");
 
          File.Put_Line (Indent1 & Output_Proc & " (");
-         File.Put_Line (Indent2 & "&" & Unit_Buffers_Array_Name (IC) & ",");
+         File.Put_Line
+           (Indent2 & "&" & Unit_Buffers_Array_Name (+IC.Project_Name) & ",");
          case IC.Dump_Config.Channel is
          when Binary_File =>
             declare
@@ -3359,13 +3798,21 @@ package body Instrument.C is
       --  Name of file to contain helpers implementing the buffers dump
 
       Insert_Extern_Location : constant Source_Location_T :=
-        Find_First_Insert_Location (Rew.TU);
+        Start_Sloc (Get_Translation_Unit_Cursor (Rew.TU));
       --  Where to insert extern declarations
+
+      Main_Cursor : constant Cursor_T := Get_Main (Rew.TU);
+      --  Cursor of the main declaration
    begin
       if Instr_Units.Is_Empty then
          return;
       end if;
 
+      if Main_Cursor = Get_Null_Cursor then
+         Outputs.Fatal_Error ("Could not find main function in "
+                              & (+Main.Filename));
+      end if;
+
       Emit_Dump_Helper_Unit (IC, Info, Main, Helper_Filename, Instrumenter);
 
       Put_Extern_Decl
@@ -3385,9 +3832,10 @@ package body Instrument.C is
       case IC.Dump_Config.Trigger is
          when Main_End | Ravenscar_Task_Termination =>
             declare
+               use Cursor_Vectors;
+
                function Process
-                 (Cursor : Cursor_T) return Child_Visit_Result_T
-                 with Convention => C;
+                 (Cursor : Cursor_T) return Child_Visit_Result_T;
                --  Callback for Visit_Children. Insert calls to dump buffers
                --  before the function return.
 
@@ -3405,36 +3853,11 @@ package body Instrument.C is
                         Return_Expr : constant Cursor_T :=
                           Get_Children (Cursor).First_Element;
                      begin
-                        --  Introduce a variable to hold the return result,
-                        --  and replace "return <expr>;" with
-                        --  "{
-                        --     int <tmp>;
-                        --     return <tmp>=<expr>, <dump_buffers>(), <tmp>;
-                        --   }".
-                        --
-                        --  Note that we have to be careful to put braces
-                        --  around the return statement, as the control flow
-                        --  structures may not have been curlified yet,
-                        --  e.g. if the main is not a unit of interest. This
-                        --  means that:
-                        --
-                        --  if (<cond>)
-                        --     return <expr>;
+                        --  Replace "return <expr>;" with
+                        --  "return <tmp>=<expr>, <dump_buffers>(), <tmp>;"
                         --
-                        --  should produce
-                        --
-                        --  if (<cond>)
-                        --  {
-                        --     int <tmp>;
-                        --     return <tmp>=<expr>, <dump_buffers>(), <tmp>;
-                        --  }
-                        --
-                        --  Notice how this would be wrong without the braces.
-
-                        Insert_Text_After_Start_Of
-                          (N    => Cursor,
-                           Text => "{int gnatcov_rts_return;",
-                           Rew  => Rew.Rewriter);
+                        --  Note that a <tmp> variable declaration is
+                        --  introduced at the beginning of the main.
 
                         Insert_Text_Before_Start_Of
                           (N    => Return_Expr,
@@ -3448,11 +3871,6 @@ package body Instrument.C is
                                    & "(), gnatcov_rts_return",
                            Rew  => Rew.Rewriter);
 
-                        CX_Rewriter_Insert_Text_After_Token
-                          (Rew.Rewriter,
-                           Get_Range_End (Get_Cursor_Extent (Cursor)),
-                           "}");
-
                         return Child_Visit_Continue;
                      end;
                   else
@@ -3466,23 +3884,78 @@ package body Instrument.C is
                end Process;
 
             begin
-               Visit_Children
-                 (Parent  => Get_Main (Rew.TU),
-                  Visitor => Process'Unrestricted_Access);
+
+               declare
+                  Main_Body  : constant Cursor_T := Get_Body (Main_Cursor);
+                  Main_Stmts : constant Vector := Get_Children (Main_Body);
+               begin
+                  --  Introduce a variable to hold the return value. Declare
+                  --  it at the start of the main to avoid complications with
+                  --  curly braces and control flow constructs.
+
+                  if Length (Main_Stmts) /= 0 then
+                     declare
+                        Main_First_Stmt : constant Cursor_T :=
+                          Main_Stmts.First_Element;
+                     begin
+                        Insert_Text_Before_Start_Of
+                          (N    => Main_First_Stmt,
+                           Text => "int gnatcov_rts_return;",
+                           Rew  => Rew.Rewriter);
+                     end;
+                  end if;
+
+                  Visit (Main_Body, Process'Access);
+
+                  --  If the main function does not end with a return, add
+                  --  a call to dump_buffers at the end of the function.
+
+                  if Length (Main_Stmts) = 0
+                       or else
+                     Kind (Main_Stmts.Last_Element) /= Cursor_Return_Stmt
+                  then
+                     CX_Rewriter_Insert_Text_Before_Token
+                       (Rew.Rewriter,
+                        End_Sloc (Main_Body),
+                        Dump_Procedure_Symbol (Main) & "();");
+                  end if;
+               end;
             end;
 
          when At_Exit =>
-            Put_Extern_Decl
-              (Rew.Rewriter,
-               Insert_Extern_Location,
-               Instrumenter,
-               "int",
-               "atexit",
-               Func_Args => "void (*function) (void)");
 
-            Add_Statement_In_Main
-              (Rew.TU, Rew.Rewriter,
-               "atexit (" & Dump_Procedure_Symbol (Main) & ");");
+            --  To avoid getting conflicting atexit declarations (if the user
+            --  includes stdlib.h), we only emit a declaration of atexit if
+            --  is not already declared in the translation unit.
+
+            if not Is_Atexit_Declared (Rew.TU) then
+               Put_Extern_Decl
+                 (Rew.Rewriter,
+                  Insert_Extern_Location,
+                  Instrumenter,
+                  "int",
+                  "atexit",
+                  Func_Args => "void (*function) (void)");
+            end if;
+
+            declare
+               Body_Cursor : constant Cursor_T := Get_Body (Main_Cursor);
+
+               --  The body of a function is a compound statement, so insert
+               --  the call to atexit before its first statement.
+
+               Body_Stmts : constant Cursor_Vectors.Vector :=
+                 Get_Children (Body_Cursor);
+               First_Stmt : constant Cursor_T := Body_Stmts.First_Element;
+
+               Location : constant Source_Location_T :=
+                 Get_Cursor_Location (First_Stmt);
+            begin
+               CX_Rewriter_Insert_Text_Before
+                 (Rew    => Rew.Rewriter,
+                  Loc    => Location,
+                  Insert => "atexit (" & Dump_Procedure_Symbol (Main) & ");");
+            end;
 
          when others =>
             null;
@@ -3592,6 +4065,7 @@ package body Instrument.C is
      (Instrumenter : C_Family_Instrumenter_Type'Class;
       C_Type       : String;
       Name         : String;
+      Array_Size   : String := "";
       Func_Args    : String := "") return String is
    begin
       return
@@ -3599,8 +4073,9 @@ package body Instrument.C is
           (Instrumenter,
            C_Type,
            Name,
-           Func_Args => Func_Args,
-           External  => True);
+           Array_Size => Array_Size,
+           Func_Args  => Func_Args,
+           External   => True);
    end Format_Extern_Decl;
 
    ---------------------
@@ -3613,12 +4088,13 @@ package body Instrument.C is
       Instrumenter : C_Family_Instrumenter_Type'Class;
       C_Type       : String;
       Name         : String;
+      Array_Size   : String := "";
       Func_Args    : String := "") is
    begin
       CX_Rewriter_Insert_Text_After
         (Rewriter,
          Location,
-         Format_Extern_Decl (Instrumenter, C_Type, Name, Func_Args)
+         Format_Extern_Decl (Instrumenter, C_Type, Name, Array_Size, Func_Args)
          & ASCII.LF);
    end Put_Extern_Decl;
 
@@ -3637,49 +4113,6 @@ package body Instrument.C is
         (Format_Extern_Decl (Instrumenter, C_Type, Name, Func_Args));
    end Put_Extern_Decl;
 
-   --------------------------------
-   -- Find_First_Insert_Location --
-   --------------------------------
-
-   function Find_First_Insert_Location
-     (TU : Translation_Unit_T) return Source_Location_T
-   is
-      Location : Source_Location_T := Get_Null_Location;
-
-      function Visit_Decl
-        (Cursor : Cursor_T) return Child_Visit_Result_T with Convention => C;
-      --  Callback for Visit_Children
-
-      ----------------
-      -- Visit_Decl --
-      ----------------
-
-      function Visit_Decl
-        (Cursor : Cursor_T) return Child_Visit_Result_T is
-      begin
-         if Kind (Cursor) = Cursor_Translation_Unit then
-            return Child_Visit_Recurse;
-         end if;
-         declare
-            Cursor_Location : constant Source_Location_T :=
-              Get_Range_Start (Get_Cursor_Extent (Cursor));
-         begin
-            if not Is_Macro_Location (Location) then
-               Location := Cursor_Location;
-               return Child_Visit_Break;
-            end if;
-         end;
-         return Child_Visit_Continue;
-      end Visit_Decl;
-
-   --  Start of processing for Find_First_Insert_Location
-
-   begin
-      Visit_Children (Parent  => Get_Translation_Unit_Cursor (TU),
-                      Visitor => Visit_Decl'Unrestricted_Access);
-      return Location;
-   end Find_First_Insert_Location;
-
    ----------------------------
    -- Emit_Buffers_List_Unit --
    ----------------------------
@@ -3714,8 +4147,8 @@ package body Instrument.C is
 
       declare
          Buffer_Array_Decl  : constant String :=
-           "gnatcov_rts_unit_coverage_buffers_array "
-           & Unit_Buffers_Array_Name (IC);
+           "const struct gnatcov_rts_coverage_buffers_group_array "
+           & Unit_Buffers_Array_Name (+IC.Project_Name);
          Buffer_Unit_Length : constant String :=
            Count_Type'Image (Instr_Units.Length);
       begin
@@ -3725,13 +4158,14 @@ package body Instrument.C is
 
          File_Body.Put_Line ("#include ""gnatcov_rts_c-buffers.h""");
 
-         --  First create extern declarations for each individual unit buffer
+         --  First create extern declarations for the buffers group of each
+         --  unit.
 
          for Instr_Unit of Instr_Units loop
             Put_Extern_Decl
               (File_Body,
                Self,
-               "gnatcov_rts_unit_coverage_buffers",
+               "const struct gnatcov_rts_coverage_buffers_group",
                Unit_Buffers_Name (Instr_Unit));
          end loop;
 
@@ -3742,7 +4176,8 @@ package body Instrument.C is
          File_Body.Put_Line (Self.Extern_Prefix & Buffer_Array_Decl & ";");
          File_Body.Put_Line (Buffer_Array_Decl & " = {");
          File_Body.Put_Line ("  " & Buffer_Unit_Length & ",");
-         File_Body.Put_Line ("  (gnatcov_rts_unit_coverage_buffers *[]) {");
+         File_Body.Put_Line
+           ("  (const struct gnatcov_rts_coverage_buffers_group *[]) {");
          for Cur in Instr_Units.Iterate loop
             declare
                use CU_Name_Vectors;
@@ -3764,8 +4199,8 @@ package body Instrument.C is
       Put_Extern_Decl
         (File_Header,
          Self,
-         "gnatcov_rts_unit_coverage_buffers_array",
-         Unit_Buffers_Array_Name (IC));
+         "const struct gnatcov_rts_coverage_buffers_group_array",
+         Unit_Buffers_Array_Name (+IC.Project_Name));
    end Emit_Buffers_List_Unit;
 
    ---------------------
@@ -3795,10 +4230,6 @@ package body Instrument.C is
       --  be compatible with a C-only compiler.
 
       Emit_Buffer_Unit (Prj_Info, UIC, Self);
-
-      --  Track which CU_Id maps to which instrumented unit
-
-      Instrumented_Unit_CUs.Insert (CU_Name, UIC.CU);
    end Instrument_Unit;
 
    ----------------------
@@ -3882,14 +4313,41 @@ package body Instrument.C is
       Info     : Project_Info;
       Filename : String)
    is
+      procedure Register_Source_Dirs (P : GNATCOLL.Projects.Project_Type);
+      --  Add the source directories of P's project file to the search paths
+      --  to be passed as -I arguments later. The order in which the paths are
+      --  added to the search paths vector is the same order in which GNATCOLL
+      --  retrieves the files in the project tree. gprbuild also depends on
+      --  GNATCOLL which ensures we have the same behavior here.
+
+      --------------------------
+      -- Register_Source_Dirs --
+      --------------------------
+
+      procedure Register_Source_Dirs (P : GNATCOLL.Projects.Project_Type) is
+      begin
+         for Dir of P.Source_Dirs loop
+            Self.PP_Search_Path.Append
+              (+GNATCOLL.VFS."+" (GNATCOLL.VFS.Dir_Name (Dir)));
+         end loop;
+      end Register_Source_Dirs;
+
       Switches : GNAT.Strings.String_List_Access;
+
    begin
-      --  Pass the source directories of the project file as -I options
 
-      for Dir of Info.Project.Source_Dirs loop
-         Self.PP_Search_Path.Append
-           (+GNATCOLL.VFS."+" (GNATCOLL.VFS.Dir_Name (Dir)));
-      end loop;
+      --  Pass the source directories of the project file as -I options. Note
+      --  that this will duplicate with the project tree traversal below, but
+      --  we need this project source directories to be picked first. We thus
+      --  make sure to add them first to the PP_Search_Path list.
+
+      Register_Source_Dirs (Info.Project);
+
+      --  Pass the source directories of included projects as -I options
+
+      Project.Iterate_Projects
+        (Info.Project, Register_Source_Dirs'Access,
+         Recursive => True, Include_Extended => True);
 
       --  Now get actual compiler switches from the project file for Filename.
       --  First try to get the switches specifically for Filename, then if
@@ -4084,13 +4542,14 @@ package body Instrument.C is
       Info     : Project_Info;
       Filename : String)
    is
-      Opt : constant Command_Line.String_List_Options :=
-        (case Language is
-         when C_Language   => Command_Line.Opt_C_Opts,
-         when CPP_Language => Command_Line.Opt_CPP_Opts);
+      Cmdline_Opts : constant String_Vectors.Vector :=
+        To_String_Vector
+          (case Language is
+              when C_Language   => C_Opts,
+              when CPP_Language => CPP_Opts);
    begin
       Import_From_Project (Self, Language, Info, Filename);
-      for Args of Switches.Args.String_List_Args (Opt) loop
+      for Args of Cmdline_Opts loop
          Import_From_Args (Self, Split_Args (Args));
       end loop;
 
@@ -4105,4 +4564,63 @@ package body Instrument.C is
            +Info.Output_Dir).all;
    end Import_Options;
 
+   ---------------------------
+   -- Is_Source_Of_Interest --
+   ---------------------------
+
+   function Is_Source_Of_Interest
+     (UIC : in out C_Unit_Inst_Context; N : Cursor_T) return Boolean
+   is
+      --  Determine the file from which N originates
+
+      C_File : aliased String_T;
+      Line   : aliased unsigned;
+      Column : aliased unsigned;
+      Loc    : constant Source_Location_T := Get_Cursor_Location (N);
+      File   : Unbounded_String;
+   begin
+      Get_Presumed_Location (Location => Loc,
+                             Filename => C_File'Access,
+                             Line     => Line'Access,
+                             Column   => Column'Access);
+      File := +Ada.Directories.Full_Name (Get_C_String (C_File));
+
+      --  Look for a corresponding entry in UIC.Sources_Of_Interest, create one
+      --  if it is missing.
+
+      declare
+         use GNATCOLL.Projects;
+         use GNATCOLL.VFS;
+         use Source_Of_Interest_Maps;
+
+         Cur : constant Cursor := UIC.Sources_Of_Interest.Find (File);
+         SOI : Source_Of_Interest;
+         FI  : GNATCOLL.Projects.File_Info;
+      begin
+         if Has_Element (Cur) then
+            return UIC.Sources_Of_Interest.Reference (Cur).Of_Interest;
+         end if;
+
+         --  Consider that File is of interest iff it belongs to a loaded
+         --  project.
+
+         FI := Project.Project.Info (Create (+To_String (File)));
+         if FI.Project /= No_Project then
+            SOI :=
+              (Of_Interest  => True,
+               SFI          => Get_Index_From_Generic_Name
+                                 (+File, Source_File),
+               CU_Name      =>
+                 CU_Name_For_File
+                   (Filename     => +Simple_Name (+File),
+                    Project_Name => UIC.Instrumented_Unit.Project_Name));
+         else
+            SOI := (Of_Interest => False);
+         end if;
+         UIC.Sources_Of_Interest.Insert (File, SOI);
+
+         return SOI.Of_Interest;
+      end;
+   end Is_Source_Of_Interest;
+
 end Instrument.C;
diff --git a/tools/gnatcov/instrument-c.ads b/tools/gnatcov/instrument-c.ads
index 25bf041a5..3df8e6368 100644
--- a/tools/gnatcov/instrument-c.ads
+++ b/tools/gnatcov/instrument-c.ads
@@ -18,10 +18,12 @@
 
 --  Instrumentation of a C source file
 
+with Ada.Containers.Hashed_Maps;
 with Ada.Containers.Ordered_Maps;
 with Ada.Containers.Vectors;
 with Ada.Finalization;
 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
+with Ada.Strings.Unbounded.Hash;
 
 with GNATCOLL.Projects;
 
@@ -31,6 +33,7 @@ with Types; use Types;
 with Clang.Index;   use Clang.Index;
 with Clang.Rewrite; use Clang.Rewrite;
 
+with Files_Table;           use Files_Table;
 with Instrument.Base_Types; use Instrument.Base_Types;
 with Instrument.Common;     use Instrument.Common;
 with SC_Obligations;        use SC_Obligations;
@@ -164,6 +167,26 @@ package Instrument.C is
    package Source_Condition_Vectors is
      new Ada.Containers.Vectors (Natural, C_Source_Condition);
 
+   type C_Instrumented_Entities is record
+      Buffers_Index : Natural := 0;
+      --  1-based index of the set of coverage buffers for this source file. We
+      --  allow 0 while it is uninitialized. Once all instrumented entities are
+      --  known, we allocate coverage buffers (UIC.Allocated_Bits) and
+      --  initialize Buffers_Index at the same time.
+
+      Statements : Source_Statement_Vectors.Vector;
+      Decisions  : Source_Decision_Vectors.Vector;
+      Conditions : Source_Condition_Vectors.Vector;
+      --  Statements, decisions and conditions (for MC/DC) to be instrumented
+   end record;
+   --  Coverage buffer information for a given source file
+
+   package C_Instrumented_Entities_Maps is new Ada.Containers.Ordered_Maps
+     (Key_Type     => Valid_Source_File_Index,
+      Element_Type => C_Instrumented_Entities);
+   --  Mapping from source files to all the entities to be instrumented in that
+   --  source file.
+
    type Pass_Kind is abstract tagged private;
    type Pass_Kind_Acc is access all Pass_Kind'Class;
    --  As we want to keep some information about coverage obligations inside
@@ -280,18 +303,40 @@ package Instrument.C is
    --  Shortcut to call Import_From_Project, and Import_From_Agrs on the
    --  --c-opts/--c++-opts option.
 
+   type Source_Of_Interest (Of_Interest : Boolean := False) is record
+      case Of_Interest is
+         when False =>
+            null;
+         when True =>
+            SFI     : Valid_Source_File_Index;
+            CU_Name : Compilation_Unit_Name;
+      end case;
+   end record;
+   --  Descriptor for a source file: Of_Interest determines if we should
+   --  compute its code coverage. If we are, SFI is the corresponding index in
+   --  gnatcov's file table and Project_Name is the name of the project that
+   --  owns this source file.
+
+   package Source_Of_Interest_Maps is new Ada.Containers.Hashed_Maps
+     (Key_Type        => Unbounded_String,
+      Element_Type    => Source_Of_Interest,
+      Hash            => Hash,
+      Equivalent_Keys => "=");
+
+   package Scopes_In_Files_Map is new Ada.Containers.Ordered_Maps
+     (Key_Type     => Source_File_Index,
+      Element_Type => Scope_Entity_Acc);
+   --  Mapping from a source file to the tree of scopes opened within it. The
+   --  root of each tree is the scope corresponding to the file itself in which
+   --  all its scopes are stored.
+
    type C_Unit_Inst_Context is new Instrument.Common.Unit_Inst_Context with
       record
          TU       : Translation_Unit_T;
          CIdx     : Index_T;
          Rewriter : Rewriter_T;
 
-         File : Ada.Strings.Unbounded.Unbounded_String;
-         --  Original filename
-
-         Source_Statements : Source_Statement_Vectors.Vector;
-         Source_Decisions  : Source_Decision_Vectors.Vector;
-         Source_Conditions : Source_Condition_Vectors.Vector;
+         Instrumented_Entities : C_Instrumented_Entities_Maps.Map;
          --  Statements, decisions and (for MC/DC) conditions to be
          --  instrumented.
 
@@ -309,13 +354,50 @@ package Instrument.C is
          LL_PP_Info_Map : LL_SCO_PP_Info_Maps.Map;
          --  Preprocessing information for low level SCOs
 
+         Sources_Of_Interest : Source_Of_Interest_Maps.Map;
+         --  Records for each source file processed during the instrumentation
+         --  whether it is a source of interest, and some properties if it is.
+
+         Allocated_Bits : Allocated_Bits_Vectors.Vector;
+         --  Allocated bits in coverage buffers for low-level SCOs. We allocate
+         --  one set of coverage buffers per source file, i.e. one per entry in
+         --  Instrumented_Entities.
+
+         CUs : Created_Unit_Maps.Map;
+         --  Compilation units created while instrumenting this source file.
+         --  Initialized when calling Process_Low_Level_SCOs in
+         --  Instrument_Source_File.
+
+         Scopes : Scopes_In_Files_Map.Map := Scopes_In_Files_Map.Empty_Map;
+         --  Mapping between a file's SFI and the scopes containing SCOs
+         --  defined within that file. The SCOs located in imported files
+         --  are traversed during the instrumentation of the importing file
+         --  after preprocessing. This is needed in order to keep track of
+         --  which scope was originally opened in which file.
+
+         Current_File_Scope : Source_File_Index;
+         --  Source file in wich the last scope encountered was opened.
       end record;
 
    type C_Source_Rewriter is tagged limited private;
    --  Helper object to instrument a source file
 
+   function Is_Source_Of_Interest
+     (UIC : in out C_Unit_Inst_Context; N : Cursor_T) return Boolean;
+   --  Track the source file from which N originates in
+   --  UIC.Sources_Of_Interest. Return whether this source file is a source of
+   --  interest.
+
 private
 
+   function Find_Instrumented_Entities
+     (UIC : in out C_Unit_Inst_Context'Class;
+      SFI : Valid_Source_File_Index)
+      return C_Instrumented_Entities_Maps.Reference_Type;
+   --  Return a reference to the UIC.Instrumented_Entities entry
+   --  corresponding to the source file that SFI designates. If there is no
+   --  such entry yet, create it.
+
    type Pass_Kind is abstract tagged null record;
 
    procedure Append_SCO
@@ -323,10 +405,17 @@ private
       UIC                : in out C_Unit_Inst_Context'Class;
       N                  : Cursor_T;
       C1, C2             : Character;
-      From, To           : Local_Source_Location;
+      From, To           : Source_Location;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name) is null;
 
+   procedure Enter_Scope (Pass : Pass_Kind;
+                          UIC  : in out C_Unit_Inst_Context'Class;
+                          N    : Cursor_T) is null;
+
+   procedure Exit_Scope (Pass : Pass_Kind;
+                         UIC  : in out C_Unit_Inst_Context'Class) is null;
+
    procedure Instrument_Statement
      (Pass         : Pass_Kind;
       UIC          : in out C_Unit_Inst_Context'Class;
@@ -355,8 +444,23 @@ private
       Name       : String;
       MCDC_State : out US.Unbounded_String) is null;
 
-   procedure Curlify
-     (Pass : Pass_Kind; N : Cursor_T; Rew : Rewriter_T) is null;
+   procedure Insert_Text_Before_Token
+     (Pass : Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String) is null;
+
+   procedure Insert_Text_Before
+     (Pass : Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String) is null;
+
+   procedure Insert_Text_After
+     (Pass : Pass_Kind;
+      Rew  : Rewriter_T;
+      Loc  : Source_Location_T;
+      Text : String) is null;
 
    type C_Source_Rewriter is limited new Ada.Finalization.Limited_Controlled
    with record
diff --git a/tools/gnatcov/instrument-c__stub.adb b/tools/gnatcov/instrument-c__stub.adb
index 9aedfb7c9..6b340c13b 100644
--- a/tools/gnatcov/instrument-c__stub.adb
+++ b/tools/gnatcov/instrument-c__stub.adb
@@ -21,61 +21,52 @@ package body Instrument.C is
    Error_Msg : constant String :=
      "GNATcoverage not built with C support.";
 
-   ---------------------------
-   -- Add_Auto_Dump_Buffers --
-   ---------------------------
+   ----------------------
+   -- Skip_Source_File --
+   ----------------------
 
-   procedure Add_Auto_Dump_Buffers
-     (IC   : Inst_Context;
-      Info : in out Project_Info;
-      Main : Compilation_Unit_Name;
-      Rew  : C_Source_Rewriter) is
-   begin
-      raise Program_Error with Error_Msg;
-   end Add_Auto_Dump_Buffers;
+   function Skip_Source_File
+     (Self        : C_Family_Instrumenter_Type;
+      Source_File : GNATCOLL.Projects.File_Info) return Boolean
+   is (raise Program_Error with Error_Msg);
 
-   -----------
-   -- Apply --
-   -----------
+   ---------------------
+   -- Instrument_Unit --
+   ---------------------
 
-   procedure Apply (Self : in out C_Source_Rewriter) is
+   procedure Instrument_Unit
+     (Self      : C_Family_Instrumenter_Type;
+      CU_Name   : Compilation_Unit_Name;
+      IC        : in out Inst_Context;
+      Unit_Info : in out Instrumented_Unit_Info) is
    begin
       raise Program_Error with Error_Msg;
-   end Apply;
+   end Instrument_Unit;
 
-   ---------------------
-   -- Start_Rewriting --
-   ---------------------
+   -------------------------------
+   -- Auto_Dump_Buffers_In_Main --
+   -------------------------------
 
-   procedure Start_Rewriting
-     (Self           : out C_Source_Rewriter;
-      Info           : in out Project_Info;
-      Input_Filename : String) is
+   procedure Auto_Dump_Buffers_In_Main
+     (Self     : C_Family_Instrumenter_Type;
+      IC       : in out Inst_Context;
+      Main     : Compilation_Unit_Name;
+      Filename : String;
+      Info     : in out Project_Info) is
    begin
       raise Program_Error with Error_Msg;
-   end Start_Rewriting;
+   end Auto_Dump_Buffers_In_Main;
 
    ----------------------------
    -- Emit_Buffers_List_Unit --
    ----------------------------
 
    procedure Emit_Buffers_List_Unit
-     (IC                : in out Inst_Context;
+     (Self              : C_Family_Instrumenter_Type;
+      IC                : in out Inst_Context;
       Root_Project_Info : in out Project_Info) is
    begin
       raise Program_Error with Error_Msg;
    end Emit_Buffers_List_Unit;
 
-   ---------------------
-   -- Instrument_Unit --
-   ---------------------
-
-   procedure Instrument_Unit
-     (CU_Name   : Compilation_Unit_Name;
-      IC        : in out Inst_Context;
-      Unit_Info : in out Instrumented_Unit_Info) is
-   begin
-      raise Program_Error with Error_Msg;
-   end Instrument_Unit;
-
 end Instrument.C;
diff --git a/tools/gnatcov/instrument-c__stub.ads b/tools/gnatcov/instrument-c__stub.ads
index ca5282f1e..cdff4928e 100644
--- a/tools/gnatcov/instrument-c__stub.ads
+++ b/tools/gnatcov/instrument-c__stub.ads
@@ -24,38 +24,61 @@
 --  units, using the procedures defined here, which will result in a crash (all
 --  the procedures are empty stubs raising an error).
 
+with GNATCOLL.Projects; use GNATCOLL.Projects;
+
 with Instrument.Base_Types; use Instrument.Base_Types;
 with Instrument.Common;     use Instrument.Common;
+with Switches;              use Switches;
 
-private package Instrument.C is
-   type C_Source_Decision is null record;
+package Instrument.C is
 
-   type C_Source_Condition is null record;
+   type C_Family_Instrumenter_Type is
+     abstract new Language_Instrumenter with null record;
+   --  Common instrumentation primitives for C/C++
 
-   type C_Unit_Inst_Context is null record;
+   overriding function Skip_Source_File
+     (Self        : C_Family_Instrumenter_Type;
+      Source_File : GNATCOLL.Projects.File_Info) return Boolean;
 
-   type C_Source_Rewriter is tagged limited null record;
+   overriding procedure Instrument_Unit
+     (Self      : C_Family_Instrumenter_Type;
+      CU_Name   : Compilation_Unit_Name;
+      IC        : in out Inst_Context;
+      Unit_Info : in out Instrumented_Unit_Info);
 
-   procedure Add_Auto_Dump_Buffers
-     (IC   : Inst_Context;
-      Info : in out Project_Info;
-      Main : Compilation_Unit_Name;
-      Rew  : C_Source_Rewriter);
+   overriding procedure Auto_Dump_Buffers_In_Main
+     (Self     : C_Family_Instrumenter_Type;
+      IC       : in out Inst_Context;
+      Main     : Compilation_Unit_Name;
+      Filename : String;
+      Info     : in out Project_Info);
 
-   procedure Apply (Self : in out C_Source_Rewriter);
+   overriding procedure Emit_Buffers_List_Unit
+     (Self              : C_Family_Instrumenter_Type;
+      IC                : in out Inst_Context;
+      Root_Project_Info : in out Project_Info);
 
-   procedure Start_Rewriting
-     (Self           : out C_Source_Rewriter;
-      Info           : in out Project_Info;
-      Input_Filename : String);
+   type C_Instrumenter_Type is
+     new C_Family_Instrumenter_Type with null record;
+   --  Instrumentation primitives for C
 
-   procedure Emit_Buffers_List_Unit
-     (IC                : in out Inst_Context;
-      Root_Project_Info : in out Project_Info);
+   overriding function Language
+     (Self : C_Instrumenter_Type) return Src_Supported_Language
+   is (C_Language);
 
-   procedure Instrument_Unit
-     (CU_Name   : Compilation_Unit_Name;
-      IC        : in out Inst_Context;
-      Unit_Info : in out Instrumented_Unit_Info);
+   type CPP_Instrumenter_Type is
+     new C_Family_Instrumenter_Type with null record;
+   --  Instrumentation primitives for C++
+
+   overriding function Language
+     (Self : CPP_Instrumenter_Type) return Src_Supported_Language
+   is (CPP_Language);
+
+   C_Instrumenter   : aliased constant C_Instrumenter_Type := (null record);
+   CPP_Instrumenter : aliased constant CPP_Instrumenter_Type := (null record);
+
+   procedure Postprocess_Source
+     (Preprocessed_Filename  : String;
+      Postprocessed_Filename : String) is null;
 
 end Instrument.C;
diff --git a/tools/gnatcov/instrument-c_utils.adb b/tools/gnatcov/instrument-c_utils.adb
index f3b8ff62f..adab44e6b 100644
--- a/tools/gnatcov/instrument-c_utils.adb
+++ b/tools/gnatcov/instrument-c_utils.adb
@@ -19,64 +19,86 @@
 with Interfaces.C; use Interfaces.C;
 with System;       use System;
 
-with Ada.Text_IO; use Ada.Text_IO;
+with Ada.Directories; use Ada.Directories;
+with Ada.Text_IO;     use Ada.Text_IO;
 with Ada.Unchecked_Conversion;
 
 with Clang.CX_String;  use Clang.CX_String;
 with Clang.Extensions; use Clang.Extensions;
 
+with Files_Table; use Files_Table;
+
 package body Instrument.C_Utils is
 
    use Cursor_Vectors;
 
+   function Visitor_Wrapper
+     (Node        : Cursor_T;
+      Parent      : Cursor_T;
+      Client_Data : Client_Data_T) return Child_Visit_Result_T
+     with Convention => C;
+   --  Helper for Visit_Children and Visit procedures.
+   --
+   --  Interpret Client_Data as the Visitor anonymous function for these
+   --  procedures and call it on Node.
+   --
+   --  Note: this has convention C in order to be a callback for the
+   --  Clang.Index.Visit_Children and Clang.Index.Visit functions.
+
    ----------
    -- Sloc --
    ----------
 
-   function Sloc (Loc : Source_Location_T) return Local_Source_Location is
+   function Sloc (Loc : Source_Location_T) return Source_Location is
       Line, Column : aliased Interfaces.C.unsigned;
+      File         : aliased String_T;
    begin
+      if Loc = Get_Null_Location then
+         return No_Location;
+      end if;
       Get_Presumed_Location (Location => Loc,
-                             Filename => null,
+                             Filename => File'Access,
                              Line     => Line'Access,
                              Column   => Column'Access);
-      return (Natural (Line), Natural (Column));
+      return
+        (Source_File =>
+           Get_Index_From_Generic_Name
+             (Full_Name (Get_C_String (File)), Kind => Source_File),
+         L           =>
+           (Line   => Natural (Line),
+            Column => Natural (Column)));
    end Sloc;
 
    ----------------
    -- Start_Sloc --
    ----------------
 
-   function Start_Sloc (N : Cursor_T) return Local_Source_Location is
-      Line, Column : aliased Interfaces.C.unsigned;
-      Loc          : constant Source_Location_T :=
-        Get_Range_Start (Get_Cursor_Extent (N));
+   function Start_Sloc (N : Cursor_T) return Source_Location_T is
    begin
-      Get_Presumed_Location (Location => Loc,
-                             Filename => null,
-                             Line     => Line'Access,
-                             Column   => Column'Access);
-      return (Natural (Line), Natural (Column));
+      return Get_Range_Start (Get_Cursor_Extent (N));
+   end Start_Sloc;
+
+   function Start_Sloc (N : Cursor_T) return Source_Location is
+   begin
+      return Sloc (Start_Sloc (N));
    end Start_Sloc;
 
    --------------
    -- End_Sloc --
    --------------
 
-   function End_Sloc (N : Cursor_T) return Local_Source_Location is
-      Line, Column : aliased Interfaces.C.unsigned;
-      Loc          : Source_Location_T :=
-        Get_Range_End (Get_Cursor_Extent (N));
+   function End_Sloc (N : Cursor_T) return Source_Location_T is
+   begin
+      return Get_Range_End (Get_Cursor_Extent (N));
+   end End_Sloc;
+
+   function End_Sloc (N : Cursor_T) return Source_Location is
+      Loc : Source_Location_T := End_Sloc (N);
    begin
       if Is_Macro_Location (Loc) then
          Loc := Get_Expansion_End (Get_Cursor_TU (N), Loc);
       end if;
-      Get_Presumed_Location
-        (Location => Loc,
-         Filename => null,
-         Line     => Line'Access,
-         Column   => Column'Access);
-      return (Natural (Line), Natural (Column));
+      return Sloc (Loc);
    end End_Sloc;
 
    ----------
@@ -97,7 +119,22 @@ package body Instrument.C_Utils is
       return Cursor_Is_Null (N);
    end Is_Null;
 
-   pragma Warnings (Off);
+   ---------------------
+   -- Visitor_Wrapper --
+   ---------------------
+
+   function Visitor_Wrapper
+     (Node        : Cursor_T;
+      Parent      : Cursor_T;
+      Client_Data : Client_Data_T) return Child_Visit_Result_T
+   is
+      pragma Unreferenced (Parent);
+      Callback : access function
+                   (Node : Cursor_T) return Child_Visit_Result_T
+      with Import, Address => System.Address (Client_Data);
+   begin
+      return Callback.all (Node);
+   end Visitor_Wrapper;
 
    --------------------
    -- Visit_Children --
@@ -105,83 +142,55 @@ package body Instrument.C_Utils is
 
    procedure Visit_Children
      (Parent  : Cursor_T;
-      Visitor : Cursor_Visitor_Function) is
-
-      function Visitor_Wrapper
-        (Node        : Cursor_T;
-         Parent      : Cursor_T;
-         Client_Data : Client_Data_T) return Child_Visit_Result_T
-        with Convention => C;
-
-      function Visitor_Wrapper
-        (Node        : Cursor_T;
-         Parent      : Cursor_T;
-         Client_Data : Client_Data_T) return Child_Visit_Result_T
-      is
-         pragma Unreferenced (Parent, Client_Data);
-      begin
-         return Visitor (Node);
-      end Visitor_Wrapper;
-
-      Data   : Client_Data_T;
-      Result : unsigned;
+      Visitor : not null access function
+                  (Node : Cursor_T) return Child_Visit_Result_T)
+   is
+      Dummy : constant unsigned := Visit_Children
+        (Parent, Visitor_Wrapper'Access, Client_Data_T (Visitor'Address));
    begin
-      Result :=
-        Visit_Children (Parent, Visitor_Wrapper'Unrestricted_Access, Data);
+      null;
    end Visit_Children;
 
    -----------
    -- Visit --
    -----------
 
-   procedure Visit (Parent : Cursor_T; Visitor : Cursor_Visitor_Function) is
-
-      function Visitor_Wrapper
-        (Node        : Cursor_T;
-         Parent      : Cursor_T;
-         Client_Data : Client_Data_T) return Child_Visit_Result_T
-      with Convention => C;
-
-      function Visitor_Wrapper
-        (Node        : Cursor_T;
-         Parent      : Cursor_T;
-         Client_Data : Client_Data_T) return Child_Visit_Result_T
-      is
-         pragma Unreferenced (Parent, Client_Data);
-      begin
-         return Visitor (Node);
-      end Visitor_Wrapper;
-
-      Data   : Client_Data_T;
-      Result : unsigned;
+   procedure Visit
+     (Parent  : Cursor_T;
+      Visitor : not null access function
+                  (Node : Cursor_T) return Child_Visit_Result_T)
+   is
+      Dummy : constant unsigned := Visit
+        (Parent, Visitor_Wrapper'Access, Client_Data_T (Visitor'Address));
    begin
-      Result := Visit (Parent, Visitor_Wrapper'Unrestricted_Access, Data);
+      null;
    end Visit;
-   pragma Warnings (On);
 
    ------------------
    -- Get_Children --
    ------------------
 
    function Get_Children (N : Cursor_T) return Cursor_Vectors.Vector is
-
       Res : Vector;
 
-      --  Append the children to the result vector and continue the traversal
-      --  until all children have been appended.
+      function Append_Child (Cursor : Cursor_T) return Child_Visit_Result_T;
+      --  Callback for Visit_Children. Append Cursor to Res and continue the
+      --  traversal.
 
-      function Append_Child (Cursor : Cursor_T) return Child_Visit_Result_T
-        with Convention => C;
+      ------------------
+      -- Append_Child --
+      ------------------
 
-      function Append_Child (Cursor : Cursor_T) return Child_Visit_Result_T
-      is
+      function Append_Child (Cursor : Cursor_T) return Child_Visit_Result_T is
       begin
          Res.Append (Cursor);
          return Child_Visit_Continue;
       end Append_Child;
 
+   --  Start of processing for Get_Children
+
    begin
-      Visit_Children (N, Get_Children.Append_Child'Unrestricted_Access);
+      Visit_Children (N, Get_Children.Append_Child'Access);
       return Res;
    end Get_Children;
 
@@ -192,8 +201,7 @@ package body Instrument.C_Utils is
    function Get_Lambda_Exprs (N : Cursor_T) return Cursor_Vectors.Vector is
       Res : Vector;
 
-      function Process (Cursor : Cursor_T) return Child_Visit_Result_T
-        with Convention => C;
+      function Process (Cursor : Cursor_T) return Child_Visit_Result_T;
       --  Helper for Visit_Children. Add every lambda expr under Cursor to Res,
       --  _but_ the lambda expressions nested in other lambda expressions.
 
@@ -209,38 +217,17 @@ package body Instrument.C_Utils is
          end if;
          return Child_Visit_Recurse;
       end Process;
-   begin
-      Visit_Children (N, Process'Unrestricted_Access);
-      return Res;
-   end Get_Lambda_Exprs;
 
-   -------------------------
-   -- Is_Unit_Of_Interest --
-   -------------------------
+   --  Start of processing for Get_Lambda_Exprs
 
-   function Is_Unit_Of_Interest
-     (N        : Cursor_T;
-      Filename : String) return Boolean
-   is
-      File   : aliased String_T;
-      Line   : aliased unsigned;
-      Column : aliased unsigned;
-      Loc    : constant Source_Location_T := Get_Cursor_Location (N);
    begin
-      Get_Presumed_Location (Location => Loc,
-                             Filename => File'Access,
-                             Line     => Line'Access,
-                             Column   => Column'Access);
-      declare
-         Cursor_Filename : constant String := Get_C_String (File);
-      begin
-         --  For now, just retrieve SCO for the file being instrumented.
-         --  TODO: determine what should be done for included code (inlines
-         --  function, expanded macro coming from another file etc.)
+      Visit_Children (N, Process'Access);
+      return Res;
+   end Get_Lambda_Exprs;
 
-         return Cursor_Filename = Filename;
-      end;
-   end Is_Unit_Of_Interest;
+   ---------------
+   -- To_Vector --
+   ---------------
 
    function To_Vector (N : Cursor_T) return Cursor_Vectors.Vector is
       Res : Cursor_Vectors.Vector;
@@ -254,81 +241,77 @@ package body Instrument.C_Utils is
    --------------
 
    function Get_Main (TU : Translation_Unit_T) return Cursor_T is
+      Result : Cursor_T := Get_Null_Cursor;
 
-      Main_Decl_Cursor : Cursor_T := Get_Null_Cursor;
+      function Visit_Decl (Cursor : Cursor_T) return Child_Visit_Result_T;
+      --  Callback for Visit_Children. Set Result and break the iteration if
+      --  Cursor is the "main" function definition, continue the iteration to
+      --  find the main otherwise.
 
-      function Visit_Decl (Cursor : Cursor_T) return Child_Visit_Result_T
-        with Convention => C;
+      ----------------
+      -- Visit_Decl --
+      ----------------
 
-      function Visit_Decl (Cursor : Cursor_T) return Child_Visit_Result_T
-      is
+      function Visit_Decl (Cursor : Cursor_T) return Child_Visit_Result_T is
       begin
-         if Kind (Cursor) = Cursor_Translation_Unit then
+         if Kind (Cursor) in Cursor_Translation_Unit | Cursor_Linkage_Spec
+         then
             return Child_Visit_Recurse;
          end if;
          if Cursor_Get_Mangling (Cursor) = "main" then
-            Main_Decl_Cursor := Cursor;
+            Result := Cursor;
             return Child_Visit_Break;
          end if;
          return Child_Visit_Continue;
       end Visit_Decl;
+
+   --  Start of processing for Get_Main
+
    begin
       Visit_Children (Parent  => Get_Translation_Unit_Cursor (TU),
-                      Visitor => Visit_Decl'Unrestricted_Access);
-      return Main_Decl_Cursor;
+                      Visitor => Visit_Decl'Access);
+      return Result;
    end Get_Main;
 
-   --  Rewriting utilities
+   ------------------------
+   -- Is_Atexit_Declared --
+   ------------------------
 
-   ---------------------------
-   -- Add_Statement_In_Main --
-   ---------------------------
+   function Is_Atexit_Declared (TU : Translation_Unit_T) return Boolean is
+      Has_Atexit_Declaration : Boolean := False;
 
-   procedure Add_Statement_In_Main
-     (TU        : Translation_Unit_T;
-      Rew       : Rewriter_T;
-      Statement : String) is
+      function Is_Atexit (Cursor : Cursor_T) return Child_Visit_Result_T;
+      --  Set Has_Atexit_Declaration to True if Cursor is the declaration of
+      --  the atexit function. Otherwise, recurse into the node if it is a
+      --  list of C declarations, or continue the traversal of the current
+      --  declaration list.
 
-      function Visit_Decl (Cursor : Cursor_T) return Child_Visit_Result_T
-        with Convention => C;
-      --  Traverse the tree until the main function is found, and insert a
-      --  statement.
+      ---------------
+      -- Is_Atexit --
+      ---------------
 
-      function Visit_Decl (Cursor : Cursor_T) return Child_Visit_Result_T
-      is
+      function Is_Atexit (Cursor : Cursor_T) return Child_Visit_Result_T is
       begin
-         if Kind (Cursor) = Cursor_Translation_Unit then
+         if Kind (Cursor) in Cursor_Translation_Unit | Cursor_Linkage_Spec
+         then
             return Child_Visit_Recurse;
-         end if;
-         if Cursor_Get_Mangling (Cursor) = "main" then
-            declare
-               Fun_Children : constant Vector := Get_Children (Cursor);
-               Body_Cursor  : constant Cursor_T :=
-                 Fun_Children (Last_Index (Fun_Children));
-
-               --  Body of a function is a compound statement, so rewrite
-               --  before its first children node will do.
-
-               Body_Stmts : constant Vector := Get_Children (Body_Cursor);
-               First_Stmt : constant Cursor_T :=
-                 Body_Stmts (First_Index (Body_Stmts));
-
-               Location : constant Source_Location_T :=
-                 Get_Cursor_Location (First_Stmt);
-            begin
-               CX_Rewriter_Insert_Text_Before
-                 (Rew    => Rew,
-                  Loc    => Location,
-                  Insert => Statement);
-            end;
+         elsif Cursor_Get_Mangling (Cursor) = "atexit" then
+            Has_Atexit_Declaration := True;
             return Child_Visit_Break;
+         else
+            return Child_Visit_Continue;
          end if;
-         return Child_Visit_Continue;
-      end Visit_Decl;
+      end Is_Atexit;
+
+      --  Start of processing for Is_Atexit_Declared
+
    begin
       Visit_Children (Parent  => Get_Translation_Unit_Cursor (TU),
-                      Visitor => Visit_Decl'Unrestricted_Access);
-   end Add_Statement_In_Main;
+                      Visitor => Is_Atexit'Access);
+      return Has_Atexit_Declaration;
+   end Is_Atexit_Declared;
+
+   --  Rewriting utilities
 
    --------------------------------
    -- Insert_Text_After_Start_Of --
@@ -402,26 +385,6 @@ package body Instrument.C_Utils is
          Insert => Text);
    end Insert_Text_Before_End_Of;
 
-   -------------
-   -- Curlify --
-   -------------
-
-   procedure Curlify (N : Cursor_T; Rew : Rewriter_T) is
-   begin
-      case Kind (N) is
-         when Cursor_Compound_Stmt =>
-            null;
-         when others =>
-            declare
-               Location_After : constant Source_Location_T :=
-                 Get_Range_End (Get_Cursor_Extent (N));
-            begin
-               Insert_Text_After_Start_Of (N, "{", Rew);
-               CX_Rewriter_Insert_Text_After_Token (Rew, Location_After, "}");
-            end;
-      end case;
-   end Curlify;
-
    --------------------
    -- Iterate_Tokens --
    --------------------
@@ -462,6 +425,8 @@ package body Instrument.C_Utils is
 
    procedure Print_Tokens (TU : Translation_Unit_T; N : Cursor_T) is
       procedure Put_Token (Tok : Token_T);
+      --  Callback for Iterate_Tokens. Put the spelling of Tok on the standard
+      --  output.
 
       ---------------
       -- Put_Token --
@@ -471,6 +436,9 @@ package body Instrument.C_Utils is
       begin
          Put (Get_Token_Spelling (TU, Tok));
       end Put_Token;
+
+   --  Start of processing for Print_Tokens
+
    begin
       Iterate_Tokens (TU, N, Put_Token'Access);
       New_Line;
diff --git a/tools/gnatcov/instrument-c_utils.ads b/tools/gnatcov/instrument-c_utils.ads
index 494c08d4f..85141afb4 100644
--- a/tools/gnatcov/instrument-c_utils.ads
+++ b/tools/gnatcov/instrument-c_utils.ads
@@ -33,17 +33,19 @@ package Instrument.C_Utils is
    type Cursor_Visitor_Function is access function
      (Node : Cursor_T) return Child_Visit_Result_T with Convention => C;
 
-   -------------------------
-   --  Location utilities --
-   -------------------------
+   ------------------------
+   -- Location utilities --
+   ------------------------
 
-   function Sloc (Loc : Source_Location_T) return Local_Source_Location;
+   function Sloc (Loc : Source_Location_T) return Source_Location;
    --  Convert a Source_Location_T to a Source_Location
 
-   function Start_Sloc (N : Cursor_T) return Local_Source_Location;
+   function Start_Sloc (N : Cursor_T) return Source_Location_T;
+   function Start_Sloc (N : Cursor_T) return Source_Location;
    --  Return the starting location of a node
 
-   function End_Sloc (N : Cursor_T) return Local_Source_Location;
+   function End_Sloc (N : Cursor_T) return Source_Location_T;
+   function End_Sloc (N : Cursor_T) return Source_Location;
    --  Return the end location of a node
 
    function Kind (N : Cursor_T) return Cursor_Kind_T;
@@ -54,10 +56,14 @@ package Instrument.C_Utils is
 
    procedure Visit_Children
      (Parent  : Cursor_T;
-      Visitor : Cursor_Visitor_Function);
+      Visitor : not null access function
+                  (Node : Cursor_T) return Child_Visit_Result_T);
    --  Wrapper for the Visit_Children clang procedure
 
-   procedure Visit (Parent : Cursor_T; Visitor : Cursor_Visitor_Function);
+   procedure Visit
+     (Parent  : Cursor_T;
+      Visitor : not null access function
+                  (Node : Cursor_T) return Child_Visit_Result_T);
    --  Wrapper for the Visit clang procedure
 
    function Get_Children (N : Cursor_T) return Cursor_Vectors.Vector;
@@ -75,30 +81,20 @@ package Instrument.C_Utils is
    --  Return the lambda expressions in a node and its descendents if any,
    --  except the lambda expressions nested in other lambda expressions.
 
-   function Is_Unit_Of_Interest
-     (N        : Cursor_T;
-      Filename : String) return Boolean;
-   --  True if the node N corresponds to code originating from a unit of
-   --  interest. This is used to avoid instrumenting included code (that comes
-   --  from preprocessing).
-   --
-   --  TODO: For now, we make an even stronger assumption, and only return True
-   --  for code that originates from the file being processed (e.g. included
-   --  code won't be considered).
-
    function To_Vector (N : Cursor_T) return Cursor_Vectors.Vector;
    --  Turn the node N into a single element node vector
 
    function Get_Main (TU : Translation_Unit_T) return Cursor_T;
+   --  Return the cursor corresponding to the definition of the "main"
+   --  function, or the null cursor if there is no main function.
 
-   --  Rewriting utilities
+   function Is_Atexit_Declared (TU : Translation_Unit_T) return Boolean;
+   --  Return True if there is a declaration of atexit in the given translation
+   --  unit.
 
-   procedure Add_Statement_In_Main
-     (TU        : Translation_Unit_T;
-      Rew       : Rewriter_T;
-      Statement : String);
-   --  Add a statement at the beginning of the main, and before any previously
-   --  inserted string at the same location.
+   -------------------------
+   -- Rewriting utilities --
+   -------------------------
 
    procedure Insert_Text_After_Start_Of
      (N    : Cursor_T;
@@ -136,22 +132,15 @@ package Instrument.C_Utils is
    --  If this procedure is called multiple times with the same N as parameter,
    --  the string will be inserted _before_ any previously inserted string.
 
-   procedure Curlify (N : Cursor_T; Rew : Rewriter_T)
-     with Pre => not Is_Null (N);
-   --  If the node N is not a compound statement, rewrite it by surrounding it
-   --  with curly braces. Otherwise, do nothing.
-   --
-   --  If other text was previously inserted at the same location (N start loc,
-   --  and N end loc), then the curly brace will be appended after that text,
-   --  so make sure to call Curlify before further rewriting.
-
    procedure Iterate_Tokens
      (TU      : Translation_Unit_T;
       N       : Cursor_T;
       Process : not null access procedure (Tok : Token_T));
    --  Call Process on all of the tokens within the source range of N
 
-   --  Debugging
+   ---------------
+   -- Debugging --
+   ---------------
 
    procedure Print_Tokens (TU : Translation_Unit_T; N : Cursor_T);
    --  Print the tokens associated to the given node
diff --git a/tools/gnatcov/instrument-clean_objdirs.adb b/tools/gnatcov/instrument-clean_objdirs.adb
index d141fe967..ab8f19209 100644
--- a/tools/gnatcov/instrument-clean_objdirs.adb
+++ b/tools/gnatcov/instrument-clean_objdirs.adb
@@ -17,112 +17,73 @@
 ------------------------------------------------------------------------------
 
 with Ada.Directories;       use Ada.Directories;
-with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
 
 with GNATCOLL.Projects; use GNATCOLL.Projects;
 
-with Paths;   use Paths;
+with Outputs; use Outputs;
+with Strings; use Strings;
 with Project;
 
 procedure Instrument.Clean_Objdirs (IC : Inst_Context) is
    use Project_Info_Maps;
 
+   All_Instr_Files : String_Sets.Set;
+   --  Set of full names for all files that were written to output directories
+   --  for all instrumented projects.
+
    procedure Clean_Subdir (Project : Project_Type);
    --  Callback for Project.Iterate_Projects. If Project is not externally
    --  built, remove all files from the "$project_name-gnatcov-instr" folder in
    --  Project's object directory that we did not just instrument.
 
-   procedure Remove_Files
-     (Output_Dir : String; Preserved_Files : File_Sets.Set);
-   --  Remove all files in Output_Dir which are not present in
-   --  Preserved_Files.
-
    ------------------
    -- Clean_Subdir --
    ------------------
 
    procedure Clean_Subdir (Project : Project_Type) is
+      Output_Dir : constant String := Project_Output_Dir (Project);
    begin
-      --  Leave externally built projects out of the picture
-
-      if Project.Externally_Built then
-         return;
-      end if;
-
-      --  Plan to clean this project's output directory
-
-      declare
-         Output_Dir : constant String := Project_Output_Dir (Project);
-
-         --  Lookup instrumentation information about this project
-
-         Name : constant Unbounded_String :=
-            To_Unbounded_String (Project.Name);
-         Cur  : constant Cursor := IC.Project_Info_Map.Find (Name);
-      begin
-         if Has_Element (Cur) then
-
-            --  We just instrumented sources for this project: preserve the
-            --  generated source files (i.e. remove only obsolete instrumented
-            --  sources).
-
-            Remove_Files (Output_Dir, Element (Cur).Instr_Files);
-         else
-            Remove_Files (Output_Dir, File_Sets.Empty_Set);
-         end if;
-      end;
-   end Clean_Subdir;
+      if
+         --  Leave externally built projects out of the picture
 
-   ------------------
-   -- Remove_Files --
-   ------------------
+         Project.Externally_Built
 
-   procedure Remove_Files
-     (Output_Dir : String; Preserved_Files : File_Sets.Set)
-   is
-      To_Delete : File_Sets.Set;
-      Search    : Search_Type;
-      Dir_Entry : Directory_Entry_Type;
-   begin
-      --  Some projects don't have an object directory: ignore them as there
-      --  is nothing to do.
+         --  Some projects don't have an object directory: ignore them as there
+         --  is nothing to do.
 
-      if Output_Dir'Length = 0
+         or else Output_Dir'Length = 0
          or else not Exists (Output_Dir)
          or else Kind (Output_Dir) /= Directory
       then
          return;
       end if;
 
-      Start_Search
-        (Search,
-         Directory => Output_Dir,
-         Pattern   => "",
-         Filter    => (Ordinary_File => True, others => False));
-      while More_Entries (Search) loop
-         Get_Next_Entry (Search, Dir_Entry);
-         declare
-            Name    : constant String := Simple_Name (Dir_Entry);
-            UB_Name : constant Unbounded_String :=
-               To_Unbounded_String (Name);
-         begin
-            if not Preserved_Files.Contains (UB_Name) then
-               To_Delete.Insert (UB_Name);
-            end if;
-         end;
-      end loop;
-      End_Search (Search);
+      Clean_Dir
+        (Dir           => Output_Dir,
+         Pattern       => "",
+         Ignored_Files => All_Instr_Files);
 
-      for Name of To_Delete loop
-         Delete_File (Output_Dir / To_String (Name));
-      end loop;
-   end Remove_Files;
+   end Clean_Subdir;
 
 --  Start of processing for Instrument.Clean_Objdirs
 
 begin
+   --  First initialize All_Instr_Files
+
+   for Cur in IC.Project_Info_Map.Iterate loop
+      for Filename of Element (Cur).Instr_Files loop
+         All_Instr_Files.Include (Filename);
+      end loop;
+   end loop;
+
+   --  Then go through all projects to clear up their object directories,
+   --  ignoring files in All_Instr_Files. Also go through extended projects, as
+   --  their object directories can interfere with the build of the extending
+   --  project.
+
    Project.Iterate_Projects
-     (Root_Project => Project.Project.Root_Project,
-      Process      => Clean_Subdir'Access,
-      Recursive    => True);
+     (Root_Project     => Project.Project.Root_Project,
+      Process          => Clean_Subdir'Access,
+      Recursive        => True,
+      Include_Extended => True);
 end Instrument.Clean_Objdirs;
diff --git a/tools/gnatcov/instrument-common.adb b/tools/gnatcov/instrument-common.adb
index f8f3b7229..d2e72f1ad 100644
--- a/tools/gnatcov/instrument-common.adb
+++ b/tools/gnatcov/instrument-common.adb
@@ -28,14 +28,15 @@ with Interfaces;
 with GNAT.OS_Lib;
 
 with Langkit_Support.Symbols;
-with Langkit_Support.Text;
 with Libadalang.Common;
+with Libadalang.Config_Pragmas;
 with Libadalang.Sources;
 
+with Coverage;
+with Diagnostics;
 with Hex_Images;
 with Outputs; use Outputs;
 with Paths;   use Paths;
-with Project;
 with SCOs;
 
 package body Instrument.Common is
@@ -51,28 +52,6 @@ package body Instrument.Common is
      (Rewriter : Source_Rewriter);
    --  Remove all Warnings/Style_Checks pragmas in Rewriter's unit
 
-   type Missing_Src_Reporter is new Libadalang.Analysis.Event_Handler_Interface
-   with record
-      Reported_Files : String_Sets.Set;
-      --  Set of source file names which were already reported as missing.
-      --  Libadalang does not guarantee that the Unit_Requested event is
-      --  triggered only once per source, so de-duplicate events with this set.
-   end record;
-   --  Implementation of the Libadalang event handler interface used in
-   --  Create_Missing_File_Reporter.
-
-   overriding procedure Release (Self : in out Missing_Src_Reporter) is null;
-
-   overriding procedure Unit_Requested_Callback
-     (Self               : in out Missing_Src_Reporter;
-      Context            : Libadalang.Analysis.Analysis_Context'Class;
-      Name               : Langkit_Support.Text.Text_Type;
-      From               : Libadalang.Analysis.Analysis_Unit'Class;
-      Found              : Boolean;
-      Is_Not_Found_Error : Boolean);
-   --  If the requested unit is not found and that is an error, warn about it.
-   --  Make sure we warn only once about a given source file.
-
    procedure Create_LAL_Context (IC : in out Inst_Context);
    --  Create a new Libadalang analysis context for IC, assigning it to
    --  IC.Context.
@@ -82,6 +61,10 @@ package body Instrument.Common is
    --  IC.Get_From_File_Count to 0, as the new context has not been used to
    --  instrument any source file yet.
 
+   function Next_Bit (Last_Bit : in out Any_Bit_Id) return Any_Bit_Id;
+   --  Convenience function to allocate a new coverage buffer bit: increment
+   --  Last_Bit and return the new Last_Bit.
+
    -------------------
    -- Buffer_Symbol --
    -------------------
@@ -171,6 +154,30 @@ package body Instrument.Common is
       end if;
    end Project_Output_Dir;
 
+   ------------------------
+   -- Format_Fingerprint --
+   ------------------------
+
+   function Format_Fingerprint
+     (Fingerprint      : SC_Obligations.Fingerprint_Type;
+      Opening, Closing : String) return String
+   is
+      Result : Unbounded_String;
+      First  : Boolean := True;
+   begin
+      Append (Result, Opening);
+      for Byte of Fingerprint loop
+         if First then
+            First := False;
+         else
+            Append (Result, ", ");
+         end if;
+         Append (Result, Strings.Img (Integer (Byte)));
+      end loop;
+      Append (Result, Closing);
+      return To_String (Result);
+   end Format_Fingerprint;
+
    ---------------------
    -- Start_Rewriting --
    ---------------------
@@ -203,7 +210,7 @@ package body Instrument.Common is
       Self.Unit := Unit;
       Self.Handle := Start_Rewriting (IC.Context);
 
-      Info.Instr_Files.Insert (To_Unbounded_String (Base_Filename));
+      Info.Instr_Files.Insert (To_Unbounded_String (Output_Filename));
    end Start_Rewriting;
 
    --------------------
@@ -375,6 +382,13 @@ package body Instrument.Common is
         (Unit_Provider => IC.Provider,
          Event_Handler => IC.Event_Handler);
       IC.Get_From_File_Count := 0;
+
+      --  Load configuration pragmas
+      --  TODO??? Remove the type convertion when the LAL API change for
+      --  VA07-037 makes it into stable-libadalang.
+
+      Libadalang.Config_Pragmas.Import_From_Project
+        (IC.Context, GNATCOLL.Projects.Project_Tree (Project.Project.all));
    end Create_LAL_Context;
 
    --------------------
@@ -484,12 +498,13 @@ package body Instrument.Common is
    ----------------------------
 
    function Is_Ignored_Source_File
-     (Context : Inst_Context; Filename : String) return Boolean
-   is
+     (Context : Inst_Context; Filename : String) return Boolean is
    begin
       return
-         Context.Ignored_Source_Files_Present
-         and then GNAT.Regexp.Match (Filename, Context.Ignored_Source_Files);
+        Context.Ignored_Source_Files_Present
+        and then GNAT.Regexp.Match
+                   (S => Fold_Filename_Casing (Filename),
+                    R => Context.Ignored_Source_Files);
    end Is_Ignored_Source_File;
 
    --------------------------------
@@ -516,10 +531,19 @@ package body Instrument.Common is
          --  register it and return it.
 
          declare
+            Storage_Project : constant Project_Type :=
+              Project.Extending_Project (Recurse => True);
+            --  Actual project that will host instrumented sources: even when
+            --  we instrument an extended project, the resulting instrumented
+            --  sources must go to the ultimate extending project's object
+            --  directory. This is similar to the object directory that hosts
+            --  object files when GPRbuild processes a project that is
+            --  extended.
+
             Result : constant Project_Info_Access := new Project_Info'
               (Project          => Project,
                Externally_Built => Project.Externally_Built,
-               Output_Dir       => +Project_Output_Dir (Project),
+               Output_Dir       => +Project_Output_Dir (Storage_Project),
                Instr_Files      => <>);
          begin
             Context.Project_Info_Map.Insert (Project_Name, Result);
@@ -636,7 +660,7 @@ package body Instrument.Common is
       Output_Filename : constant String :=
          To_String (Info.Output_Dir) / Base_Filename;
    begin
-      Info.Instr_Files.Insert (To_Unbounded_String (Base_Filename));
+      Info.Instr_Files.Insert (To_Unbounded_String (Output_Filename));
       return Output_Filename;
    end Register_New_File;
 
@@ -693,44 +717,141 @@ package body Instrument.Common is
          N : constant Unbounded_String := To_Unbounded_String
            (Ada.Directories.Simple_Name (Langkit_Support.Text.Image (Name)));
       begin
-         if not Self.Reported_Files.Contains (N) then
-            Self.Reported_Files.Include (N);
-            Warn ("Cannot find required source file: " & To_String (N));
+         if Self.Reported_Files.Contains (N) then
+            return;
          end if;
+
+         --  If we have not done it yet, clarify which file we were
+         --  instrumenting when we noticed that the source file N was missing.
+
+         if Length (Self.Instrumented_File) > 0 then
+            Warn ("While instrumenting "
+                  & To_String (Self.Instrumented_File)
+                  & "...");
+            Self.Instrumented_File := Null_Unbounded_String;
+         end if;
+
+         Self.Reported_Files.Include (N);
+         Warn ("Cannot find required source file: " & To_String (N));
       end;
    end Unit_Requested_Callback;
 
-   ----------------------------------
-   -- Create_Missing_File_Reporter --
-   ----------------------------------
+   --------------
+   -- Next_Bit --
+   --------------
+
+   function Next_Bit (Last_Bit : in out Any_Bit_Id) return Any_Bit_Id is
+   begin
+      Last_Bit := Last_Bit + 1;
+      return Last_Bit;
+   end Next_Bit;
+
+   ----------------------------
+   -- Allocate_Statement_Bit --
+   ----------------------------
 
-   function Create_Missing_File_Reporter
-     return Libadalang.Analysis.Event_Handler_Reference
+   function Allocate_Statement_Bit
+     (Unit_Bits : in out Allocated_Bits; LL_S_SCO : Nat) return Any_Bit_Id
    is
+      Bit : constant Any_Bit_Id := Next_Bit (Unit_Bits.Last_Statement_Bit);
    begin
-      return Create_Event_Handler_Reference
-        (Missing_Src_Reporter'(others => <>));
-   end Create_Missing_File_Reporter;
+      Unit_Bits.Statement_Bits.Append (Statement_Bit_Ids'(LL_S_SCO, Bit));
+      return Bit;
+   end Allocate_Statement_Bit;
+
+   ----------------------------
+   -- Allocate_Decision_Bits --
+   ----------------------------
+
+   function Allocate_Decision_Bits
+     (Unit_Bits      : in out Allocated_Bits;
+      Decision_Sloc  : Source_Location;
+      LL_D_SCO       : Nat;
+      State_Variable : Unbounded_String;
+      Path_Count     : Natural) return Decision_Bit_Ids is
+   begin
+      return Result : Decision_Bit_Ids do
+         Result.LL_D_SCO := LL_D_SCO;
+
+         --  Allocate one bit per outcome
+
+         for Outcome in Boolean loop
+            Result.Outcome_Bits (Outcome) :=
+              Next_Bit (Unit_Bits.Last_Outcome_Bit);
+         end loop;
+
+         --  If appropriate, allocate path bits for MC/DC: one bit per path in
+         --  the decision.
+
+         if Coverage.MCDC_Coverage_Enabled
+            and then Length (State_Variable) > 0
+            and then Path_Count > 0
+         then
+            Result.Path_Bits_Base := Unit_Bits.Last_Path_Bit + 1;
+            Unit_Bits.Last_Path_Bit :=
+              Unit_Bits.Last_Path_Bit + Bit_Id (Path_Count);
+         else
+            Result.Path_Bits_Base := No_Bit_Id;
+         end if;
+
+         --  Warn if the number of paths exceeded the limit
+
+         if Path_Count = 0 then
+            Diagnostics.Report
+              (Decision_Sloc,
+               "Number of distinct paths in the decision exceeds the limit"
+               & " (" & Img (SC_Obligations.Get_Path_Count_Limit) & ")."
+               & " MC/DC coverage for this decision will be left undetermined"
+               & " in coverage reports. Use option --path-count-limit to"
+               & " adjust the limit if the default value is too low.",
+               Diagnostics.Warning);
+         end if;
+
+         Unit_Bits.Decision_Bits.Append (Result);
+      end return;
+   end Allocate_Decision_Bits;
+
+   ----------------------
+   -- Create_Unit_Bits --
+   ----------------------
+
+   function Create_Unit_Bits
+     (Allocated_Bits : in out Allocated_Bits_Vectors.Vector;
+      SFI            : Valid_Source_File_Index) return Positive is
+   begin
+      Allocated_Bits.Append (Common.Allocated_Bits'(SFI => SFI, others => <>));
+      return Allocated_Bits.Last_Index;
+   end Create_Unit_Bits;
 
    ------------------------
    -- Import_Annotations --
    ------------------------
 
-   procedure Import_Annotations (UIC : in out Unit_Inst_Context) is
+   procedure Import_Annotations
+     (UIC : in out Unit_Inst_Context; Created_Units : Created_Unit_Maps.Map) is
    begin
       for Couple of UIC.Annotations loop
-         declare
-            Sloc : constant Source_Location :=
-               (Source_File => UIC.SFI,
-                L           => Couple.Sloc);
-         begin
-            Couple.Annotation.CU := UIC.CU;
-            ALI_Annotations.Insert
-              (Key => Sloc, New_Item => Couple.Annotation);
-         end;
+         Couple.Annotation.CU :=
+           Created_Units.Element (Couple.Sloc.Source_File);
+         ALI_Annotations.Insert
+           (Key => Couple.Sloc, New_Item => Couple.Annotation);
       end loop;
    end Import_Annotations;
 
+   -----------------
+   -- Append_Unit --
+   -----------------
+
+   procedure Append_Unit (SFI : Source_File_Index) is
+   begin
+      SCOs.SCO_Unit_Table.Append
+        ((File_Name  => new String'(Get_Full_Name (SFI)),
+          File_Index => SFI,
+          Dep_Num    => 1,
+          From       => SCOs.SCO_Table.Last + 1,
+          To         => SCOs.SCO_Table.Last));
+   end Append_Unit;
+
    ----------------
    -- Append_SCO --
    ----------------
@@ -738,10 +859,26 @@ package body Instrument.Common is
    procedure Append_SCO
      (C1, C2             : Character;
       From, To           : Local_Source_Location;
+      SFI                : Source_File_Index;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name)
    is
+      use type SCOs.SCO_Unit_Index;
    begin
+      --  If needed, append a new entry to the SCO_Unit_Table (if we
+      --  are entering a new file when instrumenting a compilation unit,
+      --  which can happen with #included files for instance).
+
+      if SCOs.SCO_Unit_Table.Last = 0
+         or else
+           SCOs.SCO_Unit_Table.Table (SCOs.SCO_Unit_Table.Last).File_Index /=
+              SFI
+      then
+         Append_Unit (SFI);
+      end if;
+
+      --  Append a new SCO to the low level SCO table
+
       SCOs.SCO_Table.Append
         ((From               =>
             (Logical_Line_Number (From.Line), Column_Number (From.Column)),
@@ -752,8 +889,29 @@ package body Instrument.Common is
           Last               => Last,
           Pragma_Sloc        => Types.No_Location,
           Pragma_Aspect_Name => Pragma_Aspect_Name));
+
+      --  Then, extend the SCO_Unit_Table with the newly created SCO
+
+      SCOs.SCO_Unit_Table.Table (SCOs.SCO_Unit_Table.Last).To :=
+        SCOs.SCO_Table.Last;
    end Append_SCO;
 
+   ------------------------
+   -- Remap_Scope_Entity --
+   ------------------------
+
+   procedure Remap_Scope_Entity
+     (Scope_Entity : Scope_Entity_Acc;
+      SCO_Map      : LL_HL_SCO_Map) is
+   begin
+      Scope_Entity.From := SCO_Map (Nat (Scope_Entity.From));
+      Scope_Entity.To   := SCO_Map (Nat (Scope_Entity.To));
+
+      for Child of Scope_Entity.Children loop
+         Remap_Scope_Entity (Child, SCO_Map);
+      end loop;
+   end Remap_Scope_Entity;
+
    ------------------------------
    -- Buffer_Units_For_Closure --
    ------------------------------
@@ -785,79 +943,6 @@ package body Instrument.Common is
       return Result;
    end Instr_Units_For_Closure;
 
-   -----------------
-   -- Enter_Scope --
-   -----------------
-
-   procedure Enter_Scope
-     (UIC        : in out Unit_Inst_Context;
-      Scope_Name : Unbounded_String;
-      Sloc       : Slocs.Local_Source_Location)
-   is
-      New_Scope_Ent : constant Scope_Entity_Acc := new Scope_Entity'
-        (From     => SCO_Id (SCOs.SCO_Table.Last + 1),
-         To       => No_SCO_Id,
-         Name     => Scope_Name,
-         Sloc     => Sloc,
-         Children => Scope_Entities_Vectors.Empty_Vector,
-         Parent   => UIC.Current_Scope_Entity);
-   begin
-      if UIC.Current_Scope_Entity /= null then
-         UIC.Current_Scope_Entity.Children.Append (New_Scope_Ent);
-      end if;
-      UIC.Current_Scope_Entity := New_Scope_Ent;
-   end Enter_Scope;
-
-   ----------------
-   -- Exit_Scope --
-   ----------------
-
-   procedure Exit_Scope (UIC : in out Unit_Inst_Context)
-   is
-      Parent : Scope_Entity_Acc renames UIC.Current_Scope_Entity.Parent;
-      Scope  : Scope_Entity_Acc renames UIC.Current_Scope_Entity;
-   begin
-      --  Note that scope entities may reference any kind of low-level SCOs,
-      --  including dominance markers. As those markers are removed after
-      --  calling Process_Low_Level_SCOs, they are remapped to No_SCO_Id. Fix
-      --  up the span so that only valid high-level SCOs are referenced there.
-      --  Note that a dominance marker is necessarily emitted with a statement
-      --  SCO, so we have no risk of having only a dominance marker in the
-      --  scope entity. As such dominance markers are emitted before statement
-      --  SCOs, this also means that Scope_Entity.To can't possibly reference a
-      --  dominance marker SCO.
-      --
-      --  TODO???: clarify whether we need dominance SCOs or not in the
-      --  instrumentation scheme, as this is not clear we do.
-
-      while Nat (Scope.From) < SCOs.SCO_Table.Last
-        and then SCOs.SCO_Table.Table (Nat (Scope.From)).C1 = '>'
-      loop
-         Scope.From := Scope.From + 1;
-      end loop;
-
-      --  Update the last SCO for this scope entity
-
-      Scope.To := SCO_Id (SCOs.SCO_Table.Last);
-
-      --  If the scope has no SCO (it could be possible for a package spec with
-      --  only subprogram declarations for instance), discard it.
-
-      if Scope.To < Scope.From then
-         if Parent /= null then
-            Parent.Children.Delete_Last;
-         end if;
-         Free (Scope);
-      end if;
-
-      --  If this is not the top-level scope (we want to keep its reference
-      --  after having traversed the AST), go up the scope tree.
-
-      if Parent /= null then
-         Scope := Parent;
-      end if;
-   end Exit_Scope;
-
 begin
    Sys_Prefix.Append (To_Unbounded_String ("GNATcov_RTS"));
 
@@ -871,4 +956,7 @@ begin
    Decision_Buffer_Name.Append  (To_Unbounded_String ("Decision_Buffer"));
    MCDC_Buffer_Name.Append      (To_Unbounded_String ("MCDC_Buffer"));
 
+   Witness_Dummy_Type_Name := Sys_Buffers;
+   Witness_Dummy_Type_Name.Append (To_Unbounded_String ("Witness_Dummy_Type"));
+
 end Instrument.Common;
diff --git a/tools/gnatcov/instrument-common.ads b/tools/gnatcov/instrument-common.ads
index 8391052c5..7a8d1c08d 100644
--- a/tools/gnatcov/instrument-common.ads
+++ b/tools/gnatcov/instrument-common.ads
@@ -61,14 +61,17 @@ with Ada.Strings.Wide_Wide_Unbounded; use Ada.Strings.Wide_Wide_Unbounded;
 with GNATCOLL.Projects; use GNATCOLL.Projects;
 with GNATCOLL.VFS;
 
+with Langkit_Support.Text;
 with Libadalang.Analysis;  use Libadalang.Analysis;
 with Libadalang.Rewriting; use Libadalang.Rewriting;
 
 with ALI_Files;             use ALI_Files;
+with Files_Table;           use Files_Table;
 with GNATcov_RTS;
 with GNATcov_RTS.Buffers;   use GNATcov_RTS.Buffers;
 with Instrument.Base_Types; use Instrument.Base_Types;
 with Namet;                 use Namet;
+with Project;               use Project;
 with SC_Obligations;        use SC_Obligations;
 with Slocs;                 use Slocs;
 with Strings;               use Strings;
@@ -103,15 +106,20 @@ package Instrument.Common is
    --  Qualified name (relative to the unit buffer package) of the buffer to
    --  contain coverage data corresponding to decision BDD paths.
 
+   Witness_Dummy_Type_Name : Ada_Qualified_Name;
+   --  Qualified name for the type returned by the Witness function used for
+   --  declaration instrumentation.
+
    Dump_Procedure_Name : constant Ada_Identifier :=
       To_Unbounded_String ("Dump_Buffers");
    --  Name of the procedure (in main dump helper packages) that dumps all
    --  coverage buffers to the source trace file.
 
-   Register_Dump_Procedure_Name : constant Ada_Identifier :=
+   Register_Dump_Function_Name : constant Ada_Identifier :=
       To_Unbounded_String ("Register_Dump_Buffers");
-   --  Name of the procedure (in main dump helper packages) that registers the
-   --  coverage buffers dump through atexit(3).
+   --  Name of the function (in main dump helper packages) that registers the
+   --  coverage buffers dump through atexit(3) or through a task termination
+   --  handler.
 
    function Dump_Procedure_Symbol
      (Main : Compilation_Unit_Name) return String
@@ -138,18 +146,38 @@ package Instrument.Common is
    function Buffer_Unit
      (Instrumented_Unit : Compilation_Unit_Name) return Ada_Qualified_Name;
    --  Given a unit to instrument, return the name of the unit that holds
-   --  its coverage buffers (Coverage_Buffer_Type and Unit_Coverage_Buffers
-   --  records).
+   --  its coverage buffers (Coverage_Buffer_Type and
+   --  GNATcov_RTS_Coverage_Buffers records).
 
    function Unit_Buffers_Name (Unit : Compilation_Unit_Name) return String;
-   --  Name of the symbol that references the gnatcov_rts_unit_coverage_buffers
-   --  struct for this unit.
+   --  Name of the symbol that references the
+   --  gnatcov_rts_coverage_buffers_group struct for this unit.
+
+   function Unit_Buffers_Array_Name (Prj_Name : String) return String is
+      ("gnatcov_rts_buffers_array_" & Prj_Name);
+   --  Name of the symbol that designates the
+   --  gnatcov_rts_coverage_buffers_array struct, which contains an array of
+   --  coverage buffers for all instrumented units in this project.
+   --
+   --  We need this to be unique per root project instrumented, as gnatcov
+   --  gives the possibility to link two separately-instrumented libraries in
+   --  the same executable.
 
    function Project_Output_Dir (Project : Project_Type) return String;
    --  Return the directory in which we must create instrumented sources for
    --  Project. This retuns an empty strings for projects that do not have an
    --  object directory.
 
+   function Format_Fingerprint
+     (Fingerprint      : SC_Obligations.Fingerprint_Type;
+      Opening, Closing : String) return String;
+   --  Somewhat language agnostic formatter for fingerprint values in generated
+   --  code.
+   --
+   --  Opening and Closing are strings used at the beginning and the end of the
+   --  returned literal expression (aggregate in Ada, compound expression in
+   --  C/C++).
+
    package File_Sets is new Ada.Containers.Hashed_Sets
      (Element_Type        => Ada.Strings.Unbounded.Unbounded_String,
       "="                 => Ada.Strings.Unbounded."=",
@@ -169,7 +197,9 @@ package Instrument.Common is
       --  for this project must land in it.
 
       Instr_Files : aliased File_Sets.Set;
-      --  Set of files that were written to Output_Dir
+      --  Set of full names for files that were written to Output_Dir for this
+      --  project. Note that in the case of project extension, more than one
+      --  project may use the same output directory.
    end record;
 
    type Project_Info_Access is access all Project_Info;
@@ -357,11 +387,34 @@ package Instrument.Common is
    --  this could mean that instrumentation breaks the build. When written at
    --  the very beginning of each written source, these pragmas avoid this.
 
-   function Create_Missing_File_Reporter
-     return Libadalang.Analysis.Event_Handler_Reference;
-   --  Create an event handler to warn about source files that Libadalang needs
-   --  to perform semantic analysis (so mandated by Ada), but which are not
-   --  available.
+   type Missing_Src_Reporter is new Libadalang.Analysis.Event_Handler_Interface
+   with record
+      Instrumented_File : Unbounded_String;
+      --  Base name for the file that is currently instrumented. Reset to the
+      --  empty string everytime we print the "While instrumenting XXX ..."
+      --  message, so that we print it at most once per instrumented file.
+
+      Reported_Files : String_Sets.Set;
+      --  Set of source file names which were already reported as missing.
+      --  Libadalang does not guarantee that the Unit_Requested event is
+      --  triggered only once per source, so de-duplicate events with this set.
+   end record;
+   --  Implementation of the Libadalang event handler interface used in
+   --  Create_Missing_File_Reporter.
+
+   type Missing_Src_Reporter_Access is access all Missing_Src_Reporter;
+
+   overriding procedure Release (Self : in out Missing_Src_Reporter) is null;
+
+   overriding procedure Unit_Requested_Callback
+     (Self               : in out Missing_Src_Reporter;
+      Context            : Libadalang.Analysis.Analysis_Context'Class;
+      Name               : Langkit_Support.Text.Text_Type;
+      From               : Libadalang.Analysis.Analysis_Unit'Class;
+      Found              : Boolean;
+      Is_Not_Found_Error : Boolean);
+   --  If the requested unit is not found and that is an error, warn about it.
+   --  Make sure we warn only once about a given source file.
 
    -------------------------
    -- Source instrumenter --
@@ -406,7 +459,10 @@ package Instrument.Common is
 
    type Statement_Bit_Ids is record
       LL_S_SCO : Nat;
+      --  Low-level SCO for this statement
+
       Executed : Bit_Id;
+      --  Corresponding bit in the "statement" coverage buffer
    end record;
 
    package LL_Statement_SCO_Bit_Allocs is
@@ -419,16 +475,26 @@ package Instrument.Common is
    No_Outcome_Bit_Ids : constant Outcome_Bit_Ids := (others => No_Bit_Id);
 
    type Decision_Bit_Ids is record
-      LL_D_SCO       : Nat;
+      LL_D_SCO : Nat;
+      --  Low-level SCO for this decision
+
+      Outcome_Bits : Outcome_Bit_Ids := No_Outcome_Bit_Ids;
+      --  Bits in the "decision" coverage buffer, corresponding to the False
+      --  and True outcomes.
 
-      Outcome_Bits   : Outcome_Bit_Ids := No_Outcome_Bit_Ids;
       Path_Bits_Base : Any_Bit_Id := No_Bit_Id;
+      --  First bit in the "mcdc" coverage buffer, corresponding to the first
+      --  path in the BDD. The other paths for the BDD are associated to the
+      --  bits that follow it.
    end record;
 
    package LL_Decision_SCO_Bit_Allocs is
      new Ada.Containers.Vectors (Nat, Decision_Bit_Ids);
 
-   type LL_Unit_Bit_Allocs is record
+   type Allocated_Bits is record
+      SFI : Valid_Source_File_Index;
+      --  Source file index for the SCOs associated to these coverage buffers
+
       Statement_Bits     : LL_Statement_SCO_Bit_Allocs.Vector;
       Last_Statement_Bit : Any_Bit_Id := No_Bit_Id;
 
@@ -436,6 +502,48 @@ package Instrument.Common is
       Last_Outcome_Bit : Any_Bit_Id := No_Bit_Id;
       Last_Path_Bit    : Any_Bit_Id := No_Bit_Id;
    end record;
+   --  Vectors of coverage buffer allocated bits for the low-level SCOs of a
+   --  given source file.
+
+   function Allocate_Statement_Bit
+     (Unit_Bits : in out Allocated_Bits; LL_S_SCO : Nat) return Any_Bit_Id;
+   --  Allocate a bit for a statement in the coverage buffers referenced by
+   --  Unit_Bits and return its index.
+   --
+   --  LL_S_SCO must be the low-level SCO for that statement.
+
+   function Allocate_Decision_Bits
+     (Unit_Bits      : in out Allocated_Bits;
+      Decision_Sloc  : Source_Location;
+      LL_D_SCO       : Nat;
+      State_Variable : Unbounded_String;
+      Path_Count     : Natural) return Decision_Bit_Ids;
+   --  Allocate bits for a decision ("decision" bits, and optionally "path"
+   --  bits) in the coverage buffers referenced by Unit_Bits.
+   --
+   --  LL_D_SCO must be the low-level SCO for that decision and Decision_Sloc
+   --  must be its location (used for diagnostics).
+   --
+   --  State_Variable is the local state variable: if it is empty, it means
+   --  that we were not able to generate one, and thus that we are unable to
+   --  compute MC/DC on this decision: no path bits are allocated in this case.
+   --
+   --  Path_Count is the number of paths in this decision. If the number of
+   --  paths exceeds the limit, must be 0: this function emits a warning in
+   --  this case.
+
+   package Allocated_Bits_Vectors is new Ada.Containers.Vectors
+     (Index_Type => Positive, Element_Type => Allocated_Bits);
+   --  Allocated bits in coverage buffers for low-level SCOs (one per source
+   --  file). Because of #include directives in C/C++, a single compilation
+   --  unit may yield multiple sets of coverage buffers: one for the compiled
+   --  source file, one for each included source.
+
+   function Create_Unit_Bits
+     (Allocated_Bits : in out Allocated_Bits_Vectors.Vector;
+      SFI            : Valid_Source_File_Index) return Positive;
+   --  Allocate a new set of coverage buffers for the given source file. Return
+   --  the index for the newly created set of buffers.
 
    -----------------------------
    -- Instrumentation context --
@@ -449,7 +557,7 @@ package Instrument.Common is
    --  MC/DC instrumentation depends on BDD information.
 
    type Annotation_Couple is record
-      Sloc       : Local_Source_Location;
+      Sloc       : Source_Location;
       Annotation : ALI_Annotation;
    end record;
    --  When instrumenting sources, annotations are registred in two steps:
@@ -474,33 +582,21 @@ package Instrument.Common is
       SFI : Source_File_Index := No_Source_File;
       --  Source file index of the compilation unit being instrumented
 
-      CU : CU_Id := No_CU_Id;
-      --  SCO identifier of the compilation unit being instrumented
-
       Buffer_Unit : Compilation_Unit_Name;
       --  Name of the compilation unit that holds coverage buffers for the
       --  unit currently being instrumented (see Common.Buffer_Unit).
 
-      Unit_Bits : LL_Unit_Bit_Allocs;
-      --  Record of allocation of coverage buffer bits for low-level SCOs
-
       Annotations : Annotation_Vectors.Vector;
       --  Annotations created during the instrumentation process, to insert in
       --  ALI_Files.ALI_Annotations afterwards, when the compilation unit
       --  (SC_Obligations.CU_Info) for this annotation is ready.
-
-      Current_Scope_Entity : Scope_Entity_Acc := null;
-      --  Information about the name, sloc, SCO range and children scopes of
-      --  the current scope entity. This is modified when entering a scope
-      --  (updated to the current scope), and when leaving it (updated to the
-      --  current scope parent, if any).
-
    end record;
 
-   procedure Import_Annotations (UIC : in out Unit_Inst_Context) with
-     Pre => UIC.CU /= No_CU_Id;
+   procedure Import_Annotations
+     (UIC : in out Unit_Inst_Context; Created_Units : Created_Unit_Maps.Map);
    --  Import ALI annotations for this unit in the global annotations table.
-   --  This should be called once the unit was insturmented and given a CU id.
+   --  This should be called once the unit was instrumented and its low level
+   --  SCOS have been transformed into high-level ones.
 
    function Img (Bit : Any_Bit_Id) return String is
      (Strings.Img (Integer (Bit)));
@@ -515,12 +611,24 @@ package Instrument.Common is
    --  verify that they are built with the version of GNATcov_RTS the
    --  instrumenter expects.
 
+   procedure Append_Unit (SFI : Source_File_Index);
+   --  Append a new entry to the SCO_Unit_Table, with an empty SCO range
+
    procedure Append_SCO
      (C1, C2             : Character;
       From, To           : Local_Source_Location;
+      SFI                : Source_File_Index;
       Last               : Boolean;
       Pragma_Aspect_Name : Name_Id := Namet.No_Name);
-   --  Append a new entry to the low-level SCO table
+   --  Append a new entry to the low-level SCO table. If SFI designates a new
+   --  source file, also append a new entry to the SCO_Unit_Table, otherwise
+   --  complete its last entry.
+
+   procedure Remap_Scope_Entity
+     (Scope_Entity : Scope_Entity_Acc;
+      SCO_Map      : LL_HL_SCO_Map);
+   --  Convert low level SCOs in Scope_Entity to high-level SCOs using the
+   --  mapping in SCO_Map.
 
    package CU_Name_Vectors is new Ada.Containers.Vectors
      (Index_Type   => Positive,
@@ -536,22 +644,6 @@ package Instrument.Common is
       Main : Compilation_Unit_Name) return CU_Name_Vectors.Vector;
    --  Return the list of instrumented units in Main's closure
 
-   procedure Enter_Scope
-     (UIC        : in out Unit_Inst_Context;
-      Scope_Name : Unbounded_String;
-      Sloc       : Local_Source_Location);
-   --  Enter a scope. This must be completed with a call to the function
-   --  Exit_Scope, defined below. Scope_Name is the name of the scope, which
-   --  is defined at location Sloc. Assume that the scope first SCO is the next
-   --  generated SCO (SCOs.SCO_Table.Last + 1). Update UIC.Current_Scope_Entity
-   --  to the created entity.
-
-   procedure Exit_Scope (UIC : in out Unit_Inst_Context)
-     with Pre => UIC.Current_Scope_Entity /= null;
-   --  Exit the current scioe, updating UIC.Current_Scope_Entity to
-   --  UIC.Current_Scope_Entity.Parent, if any. Assume that the last generated
-   --  SCO (SCOs.SCO_Table.Last) is the last SCO for the current scope.
-
    type Language_Instrumenter is abstract tagged null record;
    --  Set of operations to allow the instrumentation of sources files for a
    --  given language.
@@ -601,6 +693,11 @@ package Instrument.Common is
       Root_Project_Info : in out Project_Info) is abstract;
    --  Emit in the root project a unit (in Self's language) to contain the list
    --  of coverage buffers for all units of interest.
+   --
+   --  The variable holding the list of coverage buffers is exported to a
+   --  unique C symbol whose name is defined by the Unit_Buffers_Array_Name
+   --  function. This procedure should thus be called only once, for one of
+   --  the supported languages of the project.
 
 private
 
diff --git a/tools/gnatcov/instrument-input_traces.adb b/tools/gnatcov/instrument-input_traces.adb
index bfad8c1de..489ef2edf 100644
--- a/tools/gnatcov/instrument-input_traces.adb
+++ b/tools/gnatcov/instrument-input_traces.adb
@@ -528,7 +528,7 @@ package body Instrument.Input_Traces is
             Create_Error (Result, "invalid bit buffer encoding");
             return False;
 
-         elsif Raw_Header.Padding /= (1 .. 5 => ASCII.NUL) then
+         elsif Raw_Header.Padding /= (1 => ASCII.NUL) then
             Create_Error (Result, "invalid entry header padding");
             return False;
          end if;
@@ -697,9 +697,12 @@ package body Instrument.Input_Traces is
                with Import, Address => Trace_Entry.Project_Name;
 
             function Convert is new Ada.Unchecked_Conversion
-              (GNATcov_RTS.Buffers.SCOs_Hash, SC_Obligations.SCOs_Hash);
-            Fingerprint : constant SC_Obligations.SCOs_Hash :=
+              (GNATcov_RTS.Buffers.Fingerprint_Type,
+               SC_Obligations.Fingerprint_Type);
+            Fingerprint          : constant SC_Obligations.Fingerprint_Type :=
                Convert (Entry_Header.Fingerprint);
+            Bit_Maps_Fingerprint : constant SC_Obligations.Fingerprint_Type :=
+               Convert (Entry_Header.Bit_Maps_Fingerprint);
 
             Statement_Buffer_Size : constant Natural :=
                Buffer_Size (Entry_Header.Bit_Buffer_Encoding,
@@ -771,8 +774,10 @@ package body Instrument.Input_Traces is
             end case;
 
             On_Trace_Entry
-              (Filename, Fingerprint,
+              (Filename,
+               Fingerprint,
                CU_Name,
+               Bit_Maps_Fingerprint,
                Statement_Buffer
                  (0 .. Last_Bit (Entry_Header.Statement_Bit_Count)),
                Decision_Buffer
@@ -799,12 +804,13 @@ package body Instrument.Input_Traces is
         (Kind : Traces_Source.Supported_Info_Kind;
          Data : String);
       procedure On_Trace_Entry
-        (Filename        : String;
-         Fingerprint     : SC_Obligations.SCOs_Hash;
-         CU_Name         : Compilation_Unit_Name;
-         Stmt_Buffer     : Coverage_Buffer;
-         Decision_Buffer : Coverage_Buffer;
-         MCDC_Buffer     : Coverage_Buffer);
+        (Filename             : String;
+         Fingerprint          : SC_Obligations.Fingerprint_Type;
+         CU_Name              : Compilation_Unit_Name;
+         Bit_Maps_Fingerprint : SC_Obligations.Fingerprint_Type;
+         Stmt_Buffer          : Coverage_Buffer;
+         Decision_Buffer      : Coverage_Buffer;
+         MCDC_Buffer          : Coverage_Buffer);
       --  Callbacks for Read_Source_Trace_File
 
       Last_Is_Info : Boolean := False;
@@ -838,12 +844,13 @@ package body Instrument.Input_Traces is
       --------------------
 
       procedure On_Trace_Entry
-        (Filename        : String;
-         Fingerprint     : SC_Obligations.SCOs_Hash;
-         CU_Name         : Compilation_Unit_Name;
-         Stmt_Buffer     : Coverage_Buffer;
-         Decision_Buffer : Coverage_Buffer;
-         MCDC_Buffer     : Coverage_Buffer)
+        (Filename             : String;
+         Fingerprint          : SC_Obligations.Fingerprint_Type;
+         CU_Name              : Compilation_Unit_Name;
+         Bit_Maps_Fingerprint : SC_Obligations.Fingerprint_Type;
+         Stmt_Buffer          : Coverage_Buffer;
+         Decision_Buffer      : Coverage_Buffer;
+         MCDC_Buffer          : Coverage_Buffer)
       is
          pragma Unreferenced (Filename);
          use Ada.Text_IO;
@@ -856,14 +863,21 @@ package body Instrument.Input_Traces is
          case CU_Name.Language_Kind is
             when GNATcov_RTS.Buffers.Unit_Based_Language =>
                Put ("Unit " & To_Ada (CU_Name.Unit) & " (" & CU_Name.Part'Image
-                    & ", hash=");
+                    & ", ");
             when GNATcov_RTS.Buffers.File_Based_Language =>
-               Put ("Unit" & (+CU_Name.Filename) & " (hash=");
+               Put ("Unit " & (+CU_Name.Filename) & " (");
          end case;
 
+         Put ("SCOs hash=");
          for B of Fingerprint loop
             Put (Hex_Images.Hex_Image (Interfaces.Unsigned_8 (B)));
          end loop;
+
+         Put (", bit maps hash=");
+         for B of Bit_Maps_Fingerprint loop
+            Put (Hex_Images.Hex_Image (Interfaces.Unsigned_8 (B)));
+         end loop;
+
          Put_Line (")");
          Dump_Buffer ("Statement", Stmt_Buffer);
          Dump_Buffer ("Decision", Decision_Buffer);
@@ -927,9 +941,10 @@ package body Instrument.Input_Traces is
       subtype Whitespace is Character with Static_Predicate =>
          Whitespace in ' ' | ASCII.HT | ASCII.CR | ASCII.LF;
 
-      function Trim (S : String) return String;
-      --  Return S without its leading/trailing whitespaces, tabs, carriage
-      --  returns and newline characters (if any).
+      procedure Trim
+        (S : String; First : in out Positive; Last : in out Natural);
+      --  Adjust First/Last so that S (First .. Last) has no leading/trailing
+      --  whitespaces, tabs, carriage returns and newline characters.
 
       function Base64_Digit (C : Character) return Unsigned_8;
       --  Return the 6-bit number that the C Base64 digit means
@@ -944,9 +959,8 @@ package body Instrument.Input_Traces is
       -- Trim --
       ----------
 
-      function Trim (S : String) return String is
-         First : Positive := S'First;
-         Last  : Natural := S'Last;
+      procedure Trim
+        (S : String; First : in out Positive; Last : in out Natural) is
       begin
          while First in S'Range and then S (First) in Whitespace loop
             First := First + 1;
@@ -954,7 +968,6 @@ package body Instrument.Input_Traces is
          while Last in S'Range and then S (Last) in Whitespace loop
             Last := Last - 1;
          end loop;
-         return S (First .. Last);
       end Trim;
 
       ------------------
@@ -1016,17 +1029,60 @@ package body Instrument.Input_Traces is
       <<Read_Next_Line>>
       while not TIO.End_Of_File (Input) loop
          declare
-            Line : constant String := Trim (TIO.Get_Line (Input));
+            use type TIO.Count;
+
+            Reading_Trace : constant Boolean := BIO.Is_Open (Output);
+            Buffer        : String (1 .. 100);
+            First         : Positive := 1;
+            Last          : Natural;
          begin
-            if BIO.Is_Open (Output) then
-               if Line = End_Marker then
+            --  Try to read a line, abort if we reached the end of file
+
+            begin
+               TIO.Get_Line (Input, Buffer, Last);
+            exception
+               when TIO.End_Error =>
+                  exit;
+            end;
+
+            --  If the line is too long for our buffer:
+            --
+            --  * If we are in the middle of reading a source trace, stop with
+            --    an error: this is not supposed to happen as we dump source
+            --    traces with at most 80-columns wide lines.
+            --
+            --  * Otherwise, just skip that line.
+            --
+            --  Note that, for some reason, End_Of_Line may return False even
+            --  if we managed to read the full line. To consider that we did
+            --  manage to read the full line in such cases, also check the
+            --  column (1 = we reached the next line, so we managed to read the
+            --  previous line in one go).
+
+            if not TIO.End_Of_Line (Input) and then TIO.Col (Input) > 1 then
+               if Reading_Trace then
+                  Outputs.Fatal_Error
+                    ("Unexpected long line in Base64 trace");
+               else
+                  TIO.Skip_Line (Input);
+                  goto Read_Next_Line;
+               end if;
+            end if;
+
+            --  Remove leading/trailing whitespaces, which copy/pasting may
+            --  insert.
+
+            Trim (Buffer, First, Last);
+
+            if Reading_Trace then
+               if Buffer (First .. Last) = End_Marker then
                   BIO.Close (Output);
                   goto Read_Next_Line;
                end if;
 
                --  Expect groups of 4 characters
 
-               if Line'Length mod 4 /= 0 then
+               if Buffer (First .. Last)'Length mod 4 /= 0 then
                   Outputs.Fatal_Error
                     ("Invalid Base64 trace: incomplete group of 4 characters");
                end if;
@@ -1034,16 +1090,16 @@ package body Instrument.Input_Traces is
                --  Now process each group
 
                declare
-                  Next : Positive := Line'First;
+                  Next : Positive := First;
 
                   function D (Index : Natural) return Unsigned_8
-                  is (Base64_Digit (Line (Next + Index)));
+                  is (Base64_Digit (Buffer (Next + Index)));
 
                begin
-                  while Next <= Line'Last loop
+                  while Next <= Last loop
                      --  Here, process the Base64 digits in the slice:
                      --
-                     --    Line (Next .. Next + 3)
+                     --    Buffer (Next .. Next + 3)
                      --
                      --  This slice contains 4 Base64 digits, and each digit
                      --  encodes 6 bits (total: 24 bits), so we can decode 3
@@ -1054,11 +1110,11 @@ package body Instrument.Input_Traces is
                      BIO.Write
                        (Output,
                         Shift_Left (D (0), 2) or Shift_Right (D (1), 4));
-                     if Line (Next + 2) /= '=' then
+                     if Buffer (Next + 2) /= '=' then
                         BIO.Write
                           (Output,
                            Shift_Left (D (1), 4) or Shift_Right (D (2), 2));
-                        if Line (Next + 3) /= '=' then
+                        if Buffer (Next + 3) /= '=' then
                            BIO.Write (Output, Shift_Left (D (2), 6) or D (3));
                         end if;
                      end if;
@@ -1067,7 +1123,7 @@ package body Instrument.Input_Traces is
                   end loop;
                end;
 
-            elsif Line = Start_Marker then
+            elsif Buffer (First .. Last) = Start_Marker then
                Had_One_Trace := True;
                BIO.Create (Output, BIO.Out_File, Output_File);
             end if;
diff --git a/tools/gnatcov/instrument-input_traces.ads b/tools/gnatcov/instrument-input_traces.ads
index d337ec374..f6a7200d3 100644
--- a/tools/gnatcov/instrument-input_traces.ads
+++ b/tools/gnatcov/instrument-input_traces.ads
@@ -34,12 +34,13 @@ package Instrument.Input_Traces is
         (Kind : Traces_Source.Supported_Info_Kind;
          Data : String) is <>;
       with procedure On_Trace_Entry
-        (Filename        : String;
-         Fingerprint     : SC_Obligations.SCOs_Hash;
-         CU_Name         : Compilation_Unit_Name;
-         Stmt_Buffer     : Coverage_Buffer;
-         Decision_Buffer : Coverage_Buffer;
-         MCDC_Buffer     : Coverage_Buffer) is <>;
+        (Filename             : String;
+         Fingerprint          : SC_Obligations.Fingerprint_Type;
+         CU_Name              : Compilation_Unit_Name;
+         Bit_Maps_Fingerprint : SC_Obligations.Fingerprint_Type;
+         Stmt_Buffer          : Coverage_Buffer;
+         Decision_Buffer      : Coverage_Buffer;
+         MCDC_Buffer          : Coverage_Buffer) is <>;
    procedure Generic_Read_Source_Trace_File
      (Filename : String;
       Result   : out Traces_Files.Read_Result);
diff --git a/tools/gnatcov/instrument.adb b/tools/gnatcov/instrument.adb
index 78b02d924..c04f9472d 100644
--- a/tools/gnatcov/instrument.adb
+++ b/tools/gnatcov/instrument.adb
@@ -25,6 +25,7 @@ with Ada.Directories;
 with Ada.Exceptions;
 with Ada.Strings.Fixed;
 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
+with Ada.Text_IO;           use Ada.Text_IO;
 with Ada.Unchecked_Deallocation;
 
 with GNAT.OS_Lib;
@@ -49,7 +50,7 @@ with Instrument.Find_Ada_Units;
 with JSON;                  use JSON;
 with Outputs;
 with Paths;                 use Paths;
-with Project;
+with Project;               use Project;
 with SC_Obligations;
 with Strings;               use Strings;
 with Switches;              use Switches;
@@ -69,6 +70,11 @@ package body Instrument is
       Ignored : Boolean;
    end record;
 
+   function "<" (Left, Right : CU_Name_With_Ignore) return Boolean
+   is (if Left.Name = Right.Name
+       then Left.Ignored < Right.Ignored
+       else Left.Name < Right.Name);
+
    package CU_Name_Vectors is new Ada.Containers.Vectors
      (Positive, CU_Name_With_Ignore);
 
@@ -107,7 +113,8 @@ package body Instrument is
    package Library_Unit_Maps is new Ada.Containers.Indefinite_Ordered_Maps
      (String, Library_Unit_Info_Access);
    --  Map to associate a list of compilation units to instrument to a library
-   --  unit (indexed by the library unit name).
+   --  unit (indexed by the library unit name, i.e. the unit name or the
+   --  full name depending on the language kind for the library unit).
    --
    --  For file-based languages, the library unit only have one compilation
    --  unit associated to it (that is the library unit itself, for which the
@@ -265,7 +272,7 @@ package body Instrument is
                    & "sid");
             end;
          when File_Based_Language =>
-            SID_Basename := +(LU_Name & ".sid");
+            SID_Basename := +(Ada.Directories.Simple_Name (LU_Name & ".sid"));
       end case;
 
       return String'(+Output_Directory.Full_Name) / (+SID_Basename);
@@ -317,12 +324,6 @@ package body Instrument is
             Env              => null,
             Is_Project_Owner => False);
 
-      --  Create the event handler, to report when Libadalang cannot read a
-      --  required source file.
-
-      Event_Handler : constant Event_Handler_Reference :=
-        Create_Missing_File_Reporter;
-
       --  Create a map from library units to lists of compilation units to
       --  instrument for them.
 
@@ -344,11 +345,15 @@ package body Instrument is
 
       IC : Inst_Context := Create_Context
         (Provider,
-         Event_Handler,
+         Create_Event_Handler_Reference (Missing_Src_Reporter'(others => <>)),
          Dump_Config,
          Language_Version,
          Ignored_Source_Files);
 
+      Event_Handler : Missing_Src_Reporter renames
+        Missing_Src_Reporter_Access (IC.Event_Handler.Unchecked_Get).all;
+      --  Handle to the event handler we use to report missing source files
+
       Root_Project_Info : constant Project_Info_Access :=
          Get_Or_Create_Project_Info (IC, Project.Project.Root_Project);
 
@@ -409,13 +414,19 @@ package body Instrument is
          end if;
 
          declare
+            use GNATCOLL.VFS;
+
             CU_Name : constant Compilation_Unit_Name :=
                To_Compilation_Unit_Name (Source_File);
 
             Unit_Name : constant String :=
               (case CU_Name.Language_Kind is
                   when Unit_Based_Language => To_Ada (CU_Name.Unit),
-                  when File_Based_Language => +CU_Name.Filename);
+
+                  --  For file-based languages, we need to use the full
+                  --  name to account for homonyms.
+
+                  when File_Based_Language => +Source_File.File.Full_Name);
 
          begin
             --  Get the vector in which we will record the compilation units
@@ -490,13 +501,17 @@ package body Instrument is
       for Cur in LU_Map.Iterate loop
 
          --  Instrument compilation units (only the ones this library unit
-         --  owns).
+         --  owns). Sort them first, so that the instrumentation order is
+         --  deterministic.
 
          declare
+            package Sorting is new CU_Name_Vectors.Generic_Sorting;
+
             LU_Info              : constant Library_Unit_Info_Access :=
                Library_Unit_Maps.Element (Cur);
             All_Externally_Built : Boolean := True;
          begin
+            Sorting.Sort (LU_Info.CU_Names);
             for CU of LU_Info.CU_Names loop
                if CU.Ignored then
                   declare
@@ -522,14 +537,35 @@ package body Instrument is
                   declare
                      Unit_Info : Instrumented_Unit_Info renames
                        IC.Instrumented_Units.Element (CU.Name).all;
-                     Filename  : constant String := To_String
-                       (Unit_Info.Filename);
+                     Filename  : constant String :=
+                       To_String (Unit_Info.Filename);
+                     Basename  : constant String :=
+                       Ada.Directories.Simple_Name (Filename);
                   begin
                      --  Do not process units from externally built projects
 
                      if not Unit_Info.Prj_Info.Externally_Built then
+
+                        --  Keep a note that we are processing at least one
+                        --  source file from a non-externally built project.
+
                         All_Externally_Built := False;
 
+                        --  In verbose mode, always print a notice for the
+                        --  source file that we are about to instrument. In
+                        --  non-verbose mode, just get prepared to print it in
+                        --  case we emit a "source file missing" warning
+                        --  through Libadalang's event handler.
+
+                        if Verbose then
+                           Put_Line ("Instrumenting " & Basename);
+                        else
+                           Event_Handler.Instrumented_File :=
+                             To_Unbounded_String (Basename);
+                        end if;
+
+                        --  Run the instrumentation for this file
+
                         Instrumenters (Unit_Info.Language).Instrument_Unit
                           (CU.Name, IC, Unit_Info);
 
@@ -612,24 +648,33 @@ package body Instrument is
          Outputs.Fatal_Error ("No unit to instrument.");
       end if;
 
-      for Language in Src_Supported_Language loop
-
-         --  Emit units to contain the list of coverage buffers, one per
-         --  language present in the root project.
-         --
-         --  If the project contains both Ada and C sources, this will create
-         --  two arrays of coverage buffers. TODO??? we could have one array
-         --  defined in C and have the Ada unit just import it.
+      --  Emit the unit to contain the list of coverage buffers, exported to a
+      --  C symbol, in one of the language supported by the project.
+      --
+      --  Note that this has an implicit hack to it: if Ada is a language of
+      --  the project, it will pick it over the others (as it is the first
+      --  enumeration member of the Src_Supported_Language type). This matters
+      --  as we make the assumption in the Emit_Dump_Helper_Unit implementation
+      --  in instrument-ada_unit.adb (when instrumenting for an Ada main) that
+      --  the Ada package for buffers list units always exists: we need to
+      --  include it in the main closure, as it puts buffer units in scope
+      --  by importing them (otherwise they aren't as they are used through
+      --  C symbol importations).
 
-         if Project.Project.Root_Project.Has_Language (Image (Language)) then
+      for Language in Src_Supported_Language loop
+         if Project.Project.Root_Project.Has_Language (Image (Language))
+         then
             Instrumenters (Language).Emit_Buffers_List_Unit
               (IC, Root_Project_Info.all);
+            exit;
          end if;
+      end loop;
 
-         --  Instrument all the mains that are not unit of interest to add the
-         --  dump of coverage buffers: Instrument_Unit already took care of
-         --  mains that are units of interest.
+      --  Instrument all the mains that are not unit of interest to add the
+      --  dump of coverage buffers: Instrument_Unit already took care of mains
+      --  that are units of interest.
 
+      for Language in Src_Supported_Language loop
          for Main of Mains_To_Instrument (Language) loop
             declare
                use type GNATCOLL.VFS.Filesystem_String;
diff --git a/tools/gnatcov/libopcodes_bind/dis_stream.c b/tools/gnatcov/libopcodes_bind/dis_stream.c
index f94d2cbad..b97b34284 100644
--- a/tools/gnatcov/libopcodes_bind/dis_stream.c
+++ b/tools/gnatcov/libopcodes_bind/dis_stream.c
@@ -17,17 +17,17 @@
  *                                                                          *
  ****************************************************************************/
 
-#include <stdio.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 
 #include "dis_stream.h"
 
 struct disassembler_stream
 {
-    char *buff;
-    unsigned int size;
-    unsigned int idx;
+  char *buff;
+  unsigned int size;
+  unsigned int idx;
 };
 
 /* Printf-like function to add content to the DS stream according to FORMAT.
@@ -53,7 +53,7 @@ stream_printf (disassembler_stream *ds, const char *format, ...)
   int res;
 
   va_start (ap, format);
-  res = common_printf(ds, format, ap);
+  res = common_printf (ds, format, ap);
   va_end (ap);
 
   return res;
@@ -70,7 +70,7 @@ stream_styled_printf (disassembler_stream *ds,
   int res;
 
   va_start (ap, format);
-  res = common_printf(ds, format, ap);
+  res = common_printf (ds, format, ap);
   va_end (ap);
 
   return res;
diff --git a/tools/gnatcov/libopcodes_bind/dis_stream.h b/tools/gnatcov/libopcodes_bind/dis_stream.h
index 696216d67..a2e50cf0f 100644
--- a/tools/gnatcov/libopcodes_bind/dis_stream.h
+++ b/tools/gnatcov/libopcodes_bind/dis_stream.h
@@ -17,7 +17,6 @@
  *                                                                          *
  ****************************************************************************/
 
-
 #ifndef DIS_STREAM_H_
 #define DIS_STREAM_H_
 
@@ -46,6 +45,6 @@ extern void clear_stream (disassembler_stream *const ds);
 extern unsigned char stream_is_empty (disassembler_stream *const ds);
 
 extern void set_stream_buffer (disassembler_stream *const ds, char *const buff,
-                               int size);
+			       int size);
 
 #endif /* !DIS_STREAM_H_ */
diff --git a/tools/gnatcov/libopcodes_bind/dis_wrapper.c b/tools/gnatcov/libopcodes_bind/dis_wrapper.c
index bbe1783f1..b6c442fbd 100644
--- a/tools/gnatcov/libopcodes_bind/dis_wrapper.c
+++ b/tools/gnatcov/libopcodes_bind/dis_wrapper.c
@@ -17,32 +17,32 @@
  *                                                                          *
  ****************************************************************************/
 
-#include <stdlib.h>
 #include <assert.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
-#include "dis_wrapper.h"
 #include "dis_stream.h"
+#include "dis_wrapper.h"
 
 /* Define the number of values in enum bfd_endian.  */
 #define N_BFD_ENDIAN_VALUES 3
 
 struct disassemble_handle
 {
-    disassembler_ftype disass_func[N_BFD_ENDIAN_VALUES];
-    disassemble_info  dinfo;
-    /* Libopcode sets dinfo.disassembler_options to NULL after parsing once.
-       This is erroneous when we want e.g. to switch between ARM and Thumb
-       multiple times.  So we need to keep our disassembler options.  */
-    char *disassembler_options;
+  disassembler_ftype disass_func[N_BFD_ENDIAN_VALUES];
+  disassemble_info dinfo;
+  /* Libopcode sets dinfo.disassembler_options to NULL after parsing once.
+     This is erroneous when we want e.g. to switch between ARM and Thumb
+     multiple times.  So we need to keep our disassembler options.  */
+  char *disassembler_options;
 };
 
 /* Type stored in dhandle->dinfo->application_data.  */
 typedef struct symbolizer_data
 {
-    print_symbol_cb addr_cb; /* Function that performs the symbol lookup.  */
-    void *symbolizer; /* Object containing the symbol informations.  */
+  print_symbol_cb addr_cb; /* Function that performs the symbol lookup.  */
+  void *symbolizer;        /* Object containing the symbol informations.  */
 } symbolizer_data;
 
 char *const dis_thumb_option = "force-thumb";
@@ -54,11 +54,11 @@ char *const dis_ppc_32_option = "32";
 char *const dis_ppc_64_option = "64";
 
 #if TARGET_BITS == 32
-#  define TARGET_ADDR_FMT "0x%lx"
+#define TARGET_ADDR_FMT "0x%lx"
 #elif TARGET_BITS == 64
-#  define TARGET_ADDR_FMT "0x%lx"
+#define TARGET_ADDR_FMT "0x%lx"
 #else /* TARGET_BITS != 32 and TARGET_BITS != 64 */
-#  error "Target arch is neither 32 or 64bits, not supported."
+#error "Target arch is neither 32 or 64bits, not supported."
 #endif
 
 /* This is a callback for disassemble_info->print_address_func.  */
@@ -133,17 +133,17 @@ _create_base_disassembler (enum bfd_architecture arch, const char *options)
 static disassemble_handle *
 _create_arm_arch_disassembler (unsigned char for_thumb)
 {
-  return _create_base_disassembler
-    (bfd_arch_arm, (for_thumb) ? dis_thumb_option : dis_arm_option);
+  return _create_base_disassembler (
+    bfd_arch_arm, (for_thumb) ? dis_thumb_option : dis_arm_option);
 }
 
 /* Set necessary information for symbol resolution for the disassembler
    represented by DH.  */
 void
 set_disassembler_symbolizer (disassemble_handle *const dh,
-                             void *const symbolizer, print_symbol_cb addr_cb)
+			     void *const symbolizer, print_symbol_cb addr_cb)
 {
-  symbolizer_data* sym_data = dh->dinfo.application_data;
+  symbolizer_data *sym_data = dh->dinfo.application_data;
 
   sym_data->addr_cb = addr_cb;
   dh->dinfo.print_address_func = _print_address_cb;
@@ -247,9 +247,9 @@ delete_disassembler (disassemble_handle *const dh)
    Returns the size of the disassembled instruction.  */
 int
 disassemble_to_text (disassemble_handle *const dh, bfd_vma pc,
-                     char *const dest, unsigned int dest_size,
-                     bfd_byte *const insn_buffer, unsigned int ib_size,
-                     enum bfd_endian endian)
+		     char *const dest, unsigned int dest_size,
+		     bfd_byte *const insn_buffer, unsigned int ib_size,
+		     enum bfd_endian endian)
 {
   int size;
 
@@ -269,7 +269,7 @@ disassemble_to_text (disassemble_handle *const dh, bfd_vma pc,
      instruction.  */
   dh->dinfo.disassembler_options = dh->disassembler_options;
 
-  size = dh->disass_func[endian] (pc, &(dh->dinfo));
+  size = dh->disass_func[endian](pc, &(dh->dinfo));
 
   clear_stream (dh->dinfo.stream);
 
diff --git a/tools/gnatcov/libopcodes_bind/dis_wrapper.h b/tools/gnatcov/libopcodes_bind/dis_wrapper.h
index 101ce776c..115b652d6 100644
--- a/tools/gnatcov/libopcodes_bind/dis_wrapper.h
+++ b/tools/gnatcov/libopcodes_bind/dis_wrapper.h
@@ -32,7 +32,7 @@ typedef struct disassemble_handle disassemble_handle;
 
 /* Functions of this type can write at most BUFF_SIZE bytes.  */
 typedef int (*print_symbol_cb) (bfd_vma addr, void *symbolizer,
-                                char *const buff, int buff_size);
+				char *const buff, int buff_size);
 
 extern disassemble_handle *create_arm_disassembler (void);
 
@@ -47,12 +47,12 @@ extern disassemble_handle *create_aarch64_disassembler (void);
 extern void delete_disassembler (disassemble_handle *const dh);
 
 extern int disassemble_to_text (disassemble_handle *const dh, bfd_vma pc,
-                                char *const dest, unsigned int dest_size,
-                                bfd_byte *const insn_buffer,
-                                unsigned int ib_size, enum bfd_endian endian);
+				char *const dest, unsigned int dest_size,
+				bfd_byte *const insn_buffer,
+				unsigned int ib_size, enum bfd_endian endian);
 
 extern void set_disassembler_symbolizer (disassemble_handle *const dh,
-                                         void *const symbolizer,
-                                         print_symbol_cb addr_cb);
+					 void *const symbolizer,
+					 print_symbol_cb addr_cb);
 
 #endif /* !DIS_WRAPPER_H_ */
diff --git a/tools/gnatcov/outputs.adb b/tools/gnatcov/outputs.adb
index b912321cd..db6e2c7b6 100644
--- a/tools/gnatcov/outputs.adb
+++ b/tools/gnatcov/outputs.adb
@@ -28,7 +28,6 @@ with GNAT.OS_Lib; use GNAT.OS_Lib;
 
 with Command_Line;
 with Support_Files;
-with Strings; use Strings;
 with Switches;
 with Version;
 
@@ -99,6 +98,59 @@ package body Outputs is
          raise;
    end Create_Output_File;
 
+   ---------------
+   -- Clean_Dir --
+   ---------------
+
+   procedure Clean_Dir
+     (Dir           : String;
+      Pattern       : String;
+      Ignored_Files : String_Sets.Set := String_Sets.Empty_Set)
+   is
+      use Ada.Directories;
+
+      --  Removing items in a directory and iterate on these items at the same
+      --  time is not supported: first collect all files to remove (iteration)
+      --  and then remove them.
+
+      To_Delete  : String_Vectors.Vector;
+      Search     : Search_Type;
+      Dir_Entry  : Directory_Entry_Type;
+   begin
+      --  Nothing to do if Dir does not exist or is not a directory
+
+      if not Ada.Directories.Exists (Dir) or else Kind (Dir) /= Directory then
+         return;
+      end if;
+
+      --  Collect the files to delete
+
+      Start_Search
+        (Search,
+         Directory => Dir,
+         Pattern   => Pattern,
+         Filter    => (Ordinary_File => True, others => False));
+      while More_Entries (Search) loop
+         Get_Next_Entry (Search, Dir_Entry);
+         declare
+            Name      : constant String := Simple_Name (Dir_Entry);
+            Full_Name : constant Unbounded_String :=
+              To_Unbounded_String (Dir / Name);
+         begin
+            if not Ignored_Files.Contains (Full_Name) then
+               To_Delete.Append (Full_Name);
+            end if;
+         end;
+      end loop;
+      End_Search (Search);
+
+      --  Do the deletion
+
+      for Name of To_Delete loop
+         Delete_File (To_String (Name));
+      end loop;
+   end Clean_Dir;
+
    -----------
    -- Error --
    -----------
diff --git a/tools/gnatcov/outputs.ads b/tools/gnatcov/outputs.ads
index 7b944230f..967c3073f 100644
--- a/tools/gnatcov/outputs.ads
+++ b/tools/gnatcov/outputs.ads
@@ -25,6 +25,7 @@ with Ada.Text_IO; use Ada.Text_IO;
 with Types; use Types;
 
 with Files_Table; use Files_Table;
+with Strings;     use Strings;
 with Temp_Dirs;   use Temp_Dirs;
 with Paths;       use Paths;
 
@@ -157,6 +158,14 @@ package Outputs is
      (SFI : Source_File_Index) return String
      is (PP_Temp_Dir.Directory_Name / (Get_Unique_Name (SFI) & ".pp"));
 
+   procedure Clean_Dir
+     (Dir           : String;
+      Pattern       : String;
+      Ignored_Files : String_Sets.Set := String_Sets.Empty_Set);
+   --  Remove all the files in Dir for which the base name matches Pattern, but
+   --  whose the full name is not contained in Ignored_Files. An empty string
+   --  for Pattern will match every file.
+
 private
 
    type Context_Handle is
diff --git a/tools/gnatcov/paths.adb b/tools/gnatcov/paths.adb
index a435d9ba5..87ac79a38 100644
--- a/tools/gnatcov/paths.adb
+++ b/tools/gnatcov/paths.adb
@@ -59,6 +59,19 @@ package body Paths is
       return new String'(Build_Filename (Dir, Filename));
    end Build_Filename;
 
+   --------------------------
+   -- Fold_Filename_Casing --
+   --------------------------
+
+   function Fold_Filename_Casing (Filename : String) return String is
+   begin
+      if On_Windows then
+         return Ada.Characters.Handling.To_Lower (Filename);
+      else
+         return Filename;
+      end if;
+   end Fold_Filename_Casing;
+
    ---------------------------
    -- Canonicalize_Filename --
    ---------------------------
diff --git a/tools/gnatcov/paths.ads b/tools/gnatcov/paths.ads
index cfccf6de5..c66f5c407 100644
--- a/tools/gnatcov/paths.ads
+++ b/tools/gnatcov/paths.ads
@@ -30,29 +30,33 @@ package Paths is
    function Build_Filename
      (Dir      : String;
       Filename : String) return String_Access;
-   --  Create a filename from a directory name and a filename.
-   --  The directory name is expected to be not empty and the result
-   --  is _not_ canonicalized (left to the callers to decide).
+   --  Create a filename from a directory name and a filename. The directory
+   --  name is expected to be not empty and the result is _not_ canonicalized
+   --  (left to the callers to decide).
 
    function "/" (Dir, Name : String) return String is
      (Ada.Directories.Compose (Dir, Name));
-   --  Likewise, without the "dir shouldn't be empty" constraint but
-   --  checking that the path components are valid when not empty.
+   --  Likewise, without the "dir shouldn't be empty" constraint but checking
+   --  that the path components are valid when not empty.
+
+   function Fold_Filename_Casing (Filename : String) return String;
+   --  If this is a windows host, return the lower-cased filename, otherwise
+   --  leave it unchanged.
 
    function Canonicalize_Filename (Filename : String) return String;
    function Canonicalize_Filename (Filename : String) return String_Access;
    --  Assuming Filename is a full pathname to a file, return a normalized
-   --  version of it such that different references to the same file map to
-   --  the same canonical string as much as possible.
+   --  version of it such that different references to the same file map to the
+   --  same canonical string as much as possible.
 
    function Glob_To_Regexp (Pattern : String) return String;
-   --  Convert the provided globbing Pattern to a regular expression.
-   --  This might be used for file name or unit name patterns.
+   --  Convert the provided globbing Pattern to a regular expression.  This
+   --  might be used for file name or unit name patterns.
 
    function Is_Absolute_Path (Path : String) return Boolean;
-   --  Return whether Path is an absolute path. Unlike the GNAT runtime
-   --  version of the service, this one always matches both Windows or Unix
-   --  file path flavors.
+   --  Return whether Path is an absolute path. Unlike the GNAT runtime version
+   --  of the service, this one always matches both Windows or Unix file path
+   --  flavors.
 
    --  TODO??? Handle Unicode file names
 
diff --git a/tools/gnatcov/project.adb b/tools/gnatcov/project.adb
index 4f794ff91..c19414f00 100644
--- a/tools/gnatcov/project.adb
+++ b/tools/gnatcov/project.adb
@@ -19,8 +19,8 @@
 with Ada.Containers.Hashed_Maps;
 with Ada.Containers.Indefinite_Ordered_Maps;
 with Ada.Containers.Indefinite_Ordered_Sets;
+with Ada.Containers.Ordered_Maps;
 with Ada.Containers.Vectors;
-with Ada.Characters.Handling; use Ada.Characters.Handling;
 with Ada.Directories;         use Ada.Directories;
 with Ada.Strings.Unbounded;   use Ada.Strings.Unbounded;
 with Ada.Strings.Unbounded.Hash;
@@ -31,13 +31,12 @@ with GNAT.OS_Lib;
 with GNAT.Regexp;
 
 with GNATCOLL.Traces;
-with GNATCOLL.Projects; use GNATCOLL.Projects;
 with GNATCOLL.Projects.Aux;
-with GNATCOLL.VFS;      use GNATCOLL.VFS;
+with GNATCOLL.VFS; use GNATCOLL.VFS;
 
-with Inputs;  use Inputs;
-with Outputs; use Outputs;
-with Strings; use Strings;
+with Instrument.Base_Types; use Instrument.Base_Types;
+with Inputs;                use Inputs;
+with Outputs;               use Outputs;
 
 package body Project is
 
@@ -150,16 +149,18 @@ package body Project is
    end record;
 
    package Unit_Maps is new Ada.Containers.Indefinite_Ordered_Maps
-     (Key_Type     => String,
+     (Key_Type     => Project_Unit,
       Element_Type => Unit_Info);
 
    procedure Add_Unit
-     (Units         : in out Unit_Maps.Map;
-      Cur           : out Unit_Maps.Cursor;
-      Original_Name : String;
-      Language      : Some_Language);
-   --  Add an entry to Units. See Unit_Info's members for the semantics of
-   --  arguments.
+     (Units          : in out Unit_Maps.Map;
+      Cur            : out Unit_Maps.Cursor;
+      Original_Name  : String;
+      Info           : File_Info;
+      Language       : Some_Language);
+   --  Add a Unit_Info entry to Units. The key for this new entry (which is a
+   --  Project_Unit) is computed using the Original_Name, and the
+   --  Info.Project_Name if the unit is of a file-based language.
 
    procedure Warn_Missing_Info (What_Info : String; Unit : in out Unit_Info);
    --  If we haven't already, warn that we miss information (ALI or SID) about
@@ -168,12 +169,6 @@ package body Project is
    Unit_Map : Unit_Maps.Map;
    --  Map lower-case unit names to Unit_Info records for all units of interest
 
-   Are_LIs_Enumerated : Boolean := False;
-   --  Return whether Enumerate_LIs was called
-
-   Are_SIDs_Enumerated : Boolean := False;
-   --  Return whether Enumerate_SIDs was called
-
    procedure Initialize
      (Target, Runtime, CGPR_File : GNAT.Strings.String_Access)
       with Pre => (Target = null and then Runtime = null)
@@ -181,6 +176,10 @@ package body Project is
    --  Initialize project environment. Formals have the same semantics as in
    --  Load_Root_Project.
 
+   function Lookup_Project (Prj_Name : String) return Project_Type;
+   --  Look for the project in Prj_Tree whose name matches Prj_Name and return
+   --  it. Emit a fatal error if there is no such project.
+
    procedure Build_Prj_Map with Pre => Is_Project_Loaded;
    --  Add entries in Prj_Map for all relevant projects
 
@@ -226,6 +225,28 @@ package body Project is
    --  Note that this also returns source files for mains that are not units of
    --  interest.
 
+   function Runtime_Has_File (Filename : Filesystem_String) return Boolean;
+   --  Return whether Filename can be found among the sources of the
+   --  configured Ada runtime.
+
+   ---------------------
+   -- To_Project_Unit --
+   ---------------------
+
+   function To_Project_Unit
+     (Unit_Name : String;
+      Project   : Project_Type;
+      Language  : Some_Language) return Project_Unit
+   is
+      U : Project_Unit (Language_Kind (Language));
+   begin
+      U.Unit_Name := +Unit_Name;
+      if U.Language = File_Based_Language then
+         U.Project_Name := +Project.Name;
+      end if;
+      return U;
+   end To_Project_Unit;
+
    ---------
    -- "+" --
    ---------
@@ -250,33 +271,32 @@ package body Project is
    --------------
 
    procedure Add_Unit
-     (Units         : in out Unit_Maps.Map;
-      Cur           : out Unit_Maps.Cursor;
-      Original_Name : String;
-      Language      : Some_Language)
+     (Units          : in out Unit_Maps.Map;
+      Cur            : out Unit_Maps.Cursor;
+      Original_Name  : String;
+      Info           : File_Info;
+      Language       : Some_Language)
    is
       Ignored_Inserted : Boolean;
-      On_Windows       : constant Boolean :=
-        GNAT.OS_Lib.Directory_Separator = '\';
-      Is_C_Header      : constant Boolean :=
-        Strings.Has_Suffix (To_Lower (Original_Name), ".h");
+      Is_Header        : constant Boolean :=
+        (To_Language (Info.Language) in C_Family_Language
+         and then Info.Unit_Part = Unit_Spec);
       Orig_Name        : constant Unbounded_String :=
-        To_Unbounded_String ((if On_Windows and then Is_C_Header
-                             then To_Lower (Original_Name)
-                             else Original_Name));
-
+        +Fold_Filename_Casing (Original_Name);
+      Unit_Name        : constant Project_Unit :=
+        To_Project_Unit (Original_Name, Info.Project, Language);
    begin
-      --  Disable warnings for C header units as they do not have a proper
+      --  Disable warnings for C/C++ header units as they do not have a proper
       --  library file.
 
       Units.Insert
-        (Key      => To_Lower (Original_Name),
+        (Key      => Unit_Name,
          New_Item => (Original_Name             => Orig_Name,
                       Present_In_Projects       => False,
                       Is_Subunit                => False,
                       Language                  => Language,
-                      LI_Seen                   => Is_C_Header,
-                      Warned_About_Missing_Info => Is_C_Header),
+                      LI_Seen                   => Is_Header,
+                      Warned_About_Missing_Info => Is_Header),
          Position => Cur,
          Inserted => Ignored_Inserted);
    end Add_Unit;
@@ -288,12 +308,11 @@ package body Project is
    procedure Warn_Missing_Info (What_Info : String; Unit : in out Unit_Info) is
    begin
       --  The only way to perform code coverage on C++ units is to use
-      --  instrumentation. If we have not enumerated SID files, this means that
-      --  we are using binary traces only, and thus C++ units cannot be units
-      --  of interest, so do not warn about them in this specific case.
+      --  instrumentation. With binary traces, C++ units cannot be units of
+      --  interest, so do not warn about them in this specific case.
 
       if Unit.Warned_About_Missing_Info
-         or else (not Are_SIDs_Enumerated
+         or else (Currently_Accepted_Trace_Kind = Binary_Trace_File
                   and then Unit.Language = CPP_Language)
       then
          return;
@@ -309,28 +328,29 @@ package body Project is
    ----------------------
 
    procedure Iterate_Projects
-     (Root_Project : Project_Type;
-      Process      : access procedure (Prj : Project_Type);
-      Recursive    : Boolean;
-      Extended     : Boolean := False)
+     (Root_Project     : GNATCOLL.Projects.Project_Type;
+      Process          : access procedure
+                           (Prj : GNATCOLL.Projects.Project_Type);
+      Recursive        : Boolean;
+      Include_Extended : Boolean := False)
    is
       Iter             : Project_Iterator := Start
         (Root_Project     => Root_Project,
          Recursive        => Recursive,
-         Include_Extended => False);
+         Include_Extended => Include_Extended);
       Visited_Projects : Project_Sets.Set;
       Project          : Project_Type;
-
    begin
       loop
          Project := Current (Iter);
          exit when Project = No_Project;
 
-         --  Unless specifically asked to go through extended projects, go to
-         --  the ultimate extending project, which might override the Coverage
-         --  package.
+         --  If requested, go to the ultimate extending project: this is the
+         --  "reference" project for chains of project extension (we care about
+         --  the Coverage package of extending projects, their object dirs,
+         --  etc.).
 
-         if not Extended then
+         if not Include_Extended then
             Project := Extending_Project (Project, Recurse => True);
          end if;
 
@@ -435,7 +455,7 @@ package body Project is
    ---------------------------------
 
    procedure Enumerate_Units_Of_Interest
-     (Callback : access procedure (Name : String; Is_Subunit : Boolean))
+     (Callback : access procedure (Name : Project_Unit; Is_Subunit : Boolean))
    is
       use Unit_Maps;
    begin
@@ -448,96 +468,60 @@ package body Project is
       end loop;
    end Enumerate_Units_Of_Interest;
 
-   -------------------
-   -- Enumerate_LIs --
-   -------------------
-
-   procedure Enumerate_LIs (LI_Cb : access procedure (LI_Name : String)) is
-      Lib_Info : Library_Info_List;
-   begin
-      --  Go through all library files in all projects of interest
-
-      for Prj_Info of Prj_Map loop
-         Prj_Info.Project.Library_Files
-           (List => Lib_Info, ALI_Ext => "^.*\.[ag]li$");
-         for LI of Lib_Info loop
-
-            --  Enumerate only LI files for Ada and C sources
-
-            if To_Lower (LI.Source.Language) in "ada" | "c" then
+   -------------------------
+   -- Is_Unit_Of_Interest --
+   -------------------------
 
-               --  If the unit for this library file is in Unit_Map, this is a
-               --  unit of interest, so use it.
-
-               declare
-                  use Unit_Maps;
-
-                  LI_Source_Unit : constant String := LI.Source.Unit_Name;
-                  LI_Source_File : constant String :=
-                    +LI.Source.File.Base_Name;
-
-                  U  : constant String :=
-                         (if LI_Source_Unit'Length > 0
-                          then LI_Source_Unit
-                          else LI_Source_File);
-                  --  For unit-based languages (Ada), retrieve unit name from
-                  --  LI file. For file-based languages (C), fall back to
-                  --  translation unit source file name instead.
-
-                  Cur : constant Cursor := Unit_Map.Find (U);
-               begin
-                  if Has_Element (Cur) then
-                     LI_Cb.all (+LI.Library_File.Full_Name);
-                     Unit_Map.Reference (Cur).LI_Seen := True;
-                  end if;
-               end;
-            end if;
-         end loop;
-         Lib_Info.Clear;
-      end loop;
-
-      Are_LIs_Enumerated := True;
-   end Enumerate_LIs;
-
-   --------------------
-   -- LIs_Enumerated --
-   --------------------
-
-   function LIs_Enumerated return Boolean is
+   function Is_Unit_Of_Interest
+     (Project   : Project_Type;
+      Unit_Name : String;
+      Language  : Some_Language) return Boolean
+   is
+      U : constant Project_Unit :=
+        To_Project_Unit (Unit_Name, Project, Language);
    begin
-      return Are_LIs_Enumerated;
-   end LIs_Enumerated;
-
-   -----------------------------
-   -- Report_Units_Without_LI --
-   -----------------------------
-
-   procedure Report_Units_Without_LI is
+      return Unit_Map.Contains (U);
+   end Is_Unit_Of_Interest;
+
+   -------------------------
+   -- Is_Unit_Of_Interest --
+   -------------------------
+
+   function Is_Unit_Of_Interest (Full_Name : String) return Boolean is
+      FI        : constant File_Info :=
+        Prj_Tree.Info (Create (+Full_Name));
+      Unit_Name : constant String :=
+        (case Language_Kind (To_Language (FI.Language)) is
+         when Unit_Based_Language => FI.Unit_Name,
+         when File_Based_Language => +FI.File.Base_Name);
    begin
-      for UI of Unit_Map loop
-         if not UI.Is_Subunit and then not UI.LI_Seen then
-            Warn_Missing_Info ("ALI", UI);
-         end if;
-      end loop;
-   end Report_Units_Without_LI;
+      return Is_Unit_Of_Interest
+        (FI.Project, Unit_Name, To_Language (FI.Language));
+   end Is_Unit_Of_Interest;
 
-   --------------------
-   -- Enumerate_SIDs --
-   --------------------
+   -------------------------
+   -- Enumerate_SCO_Files --
+   -------------------------
 
-   procedure Enumerate_SIDs (Callback : access procedure (SID_Name : String))
+   procedure Enumerate_SCOs_Files
+     (Callback : access procedure (Lib_Name : String);
+      Kind     : Trace_File_Kind)
    is
-      Lib_Info : Library_Info_List;
+      Ext_Regexp : constant GNATCOLL.VFS.Filesystem_String :=
+        (case Kind is
+         when Binary_Trace_File => "^.*\.[ag]li$",
+         when Source_Trace_File => "^.*\.sid$");
+      Lib_Info   : Library_Info_List;
    begin
-      --  Go through all SID files in all projects of interest
+      --  Go through all SCOs files in all projects of interest
 
       for Prj_Info of Prj_Map loop
          Prj_Info.Project.Library_Files
-           (List => Lib_Info, ALI_Ext => "^.*\.sid$");
+           (List => Lib_Info, ALI_Ext => Ext_Regexp);
          for LI of Lib_Info loop
 
-            --  If the unit for this SID file is in Unit_Map, this is a unit of
-            --  interest, so use it.
+            --  If the unit for this SCO file is in Unit_Map, this is a
+            --  unit of interest, so use it.
 
             declare
                use Unit_Maps;
@@ -545,15 +529,20 @@ package body Project is
                LI_Source_Unit : constant String := LI.Source.Unit_Name;
                LI_Source_File : constant String := +LI.Source.File.Base_Name;
 
-               U : constant String :=
+               U  : constant String :=
                  (if LI_Source_Unit'Length > 0
                   then LI_Source_Unit
                   else LI_Source_File);
-               --  For unit-based languages (Ada), retrieve unit name from SID
-               --  file. For file-based languages (C), fall back to translation
-               --  unit source file name instead.
-
-               Cur : constant Cursor := Unit_Map.Find (U);
+               --  For unit-based languages (Ada), retrieve unit name from
+               --  SCOs file. For file-based languages (C), fall back to
+               --  translation unit source file name instead.
+
+               Cur : constant Cursor :=
+                 Unit_Map.Find
+                   (To_Project_Unit
+                      (U,
+                       Project_Type (LI.LI_Project.all),
+                       To_Language (LI.Source.Language)));
             begin
                if Has_Element (Cur) then
                   Callback.all (+LI.Library_File.Full_Name);
@@ -564,25 +553,19 @@ package body Project is
          Lib_Info.Clear;
       end loop;
 
-      --  Now warn about units of interest that have no SID
+      --  Now warn about units of interest that have no SCOs file
 
       for UI of Unit_Map loop
          if not UI.Is_Subunit and then not UI.LI_Seen then
-            Warn_Missing_Info ("SID", UI);
+            case Kind is
+               when Binary_Trace_File =>
+                  Warn_Missing_Info ("ALI", UI);
+               when Source_Trace_File =>
+                  Warn_Missing_Info ("SID", UI);
+            end case;
          end if;
       end loop;
-
-      Are_SIDs_Enumerated := True;
-   end Enumerate_SIDs;
-
-   ---------------------
-   -- SIDs_Enumerated --
-   ---------------------
-
-   function SIDs_Enumerated return Boolean is
-   begin
-      return Are_SIDs_Enumerated;
-   end SIDs_Enumerated;
+   end Enumerate_SCOs_Files;
 
    -----------------------
    -- Enumerate_Sources --
@@ -610,8 +593,8 @@ package body Project is
              Language = To_Language (Info.Language))
            and then
              (Include_Subunits
-                or else
-              Unit_Map.Contains (To_Lower (Unit_Name)))
+              or else Is_Unit_Of_Interest
+                (Info.Project, Unit_Name, To_Language (Info.Language)))
          then
             Callback (Info.Project, Info);
          end if;
@@ -655,6 +638,50 @@ package body Project is
       end;
    end Find_Source_File;
 
+   ----------------------
+   -- Runtime_Has_File --
+   ----------------------
+   function Runtime_Has_File (Filename : Filesystem_String) return Boolean is
+      Result_File : Virtual_File;
+      --  Virtual_File for Ada.Finalization. Only search predefined sources.
+      Ambiguous   : Boolean;
+      --  Required in the call for GNATCOLL.Projects.Create
+   begin
+      GNATCOLL.Projects.Create
+        (Self            => Prj_Tree.all,
+         Name            => Filename,
+         Use_Object_Path => False,
+         Ambiguous       => Ambiguous,
+         File            => Result_File,
+         Predefined_Only => True);
+
+      --  We don't expect there to be multiple homonyms for the runtime files
+
+      pragma Assert (not Ambiguous);
+      return Result_File /= No_File;
+   end Runtime_Has_File;
+
+   ------------------------------
+   -- Runtime_Has_Finalization --
+   ------------------------------
+
+   function Runtime_Supports_Finalization return Boolean is
+     (Runtime_Has_File ("a-finali.ads"));
+
+   ---------------------------------------
+   -- Runtime_Supports_Task_Termination --
+   ---------------------------------------
+
+   function Runtime_Supports_Task_Termination return Boolean is
+   begin
+      --  We need both Ada.Task_Identification (a-taside.ads) and
+      --  Ada.Task_Termination (a-taster.ads) to be able to use task
+      --  termination.
+
+      return Runtime_Has_File ("a-taster.ads")
+            and then Runtime_Has_File ("a-taside.ads");
+   end Runtime_Supports_Task_Termination;
+
    ----------------
    -- Initialize --
    ----------------
@@ -759,51 +786,47 @@ package body Project is
       end if;
    end Initialize;
 
+   --------------------
+   -- Lookup_Project --
+   --------------------
+
+   function Lookup_Project (Prj_Name : String) return Project_Type is
+      Prj_Name_FS : constant GNATCOLL.VFS.Filesystem_String :=
+        +Simple_Name (Prj_Name);
+      Last        : Integer;
+   begin
+      --  Strip optional Project_File_Extension
+
+      if Prj_Name_FS'Length >= Project_File_Extension'Length
+            and then
+         Prj_Name_FS (Prj_Name_FS'Last - Project_File_Extension'Length + 1
+                        .. Prj_Name_FS'Last) = Project_File_Extension
+      then
+         Last := Prj_Name_FS'Last - Project_File_Extension'Length;
+      else
+         Last := Prj_Name_FS'Last;
+      end if;
+
+      --  Look up project from project tree
+
+      return Result : constant Project_Type := Prj_Tree.Project_From_Name
+         (+Prj_Name_FS (Prj_Name_FS'First .. Last))
+      do
+         if Result = No_Project then
+            Fatal_Error ("project " & Prj_Name & " not found");
+         end if;
+      end return;
+   end Lookup_Project;
+
    -------------------
    -- Build_Prj_Map --
    -------------------
 
    procedure Build_Prj_Map is
 
-      function Lookup_Project (Prj_Name : String) return Project_Type;
-      --  Look for the project in Prj_Tree whose name matches Prj_Name and
-      --  return it. Emit a fatal error if there is no such project.
-
       procedure Process_Project (Project : Project_Type);
       --  Callback for Iterate_Projects: add an entry in Prj_Map for Project
 
-      --------------------
-      -- Lookup_Project --
-      --------------------
-
-      function Lookup_Project (Prj_Name : String) return Project_Type is
-         Prj_Name_FS : constant GNATCOLL.VFS.Filesystem_String :=
-           +Simple_Name (Prj_Name);
-         Last        : Integer;
-      begin
-         --  Strip optional Project_File_Extension
-
-         if Prj_Name_FS'Length >= Project_File_Extension'Length
-              and then
-            Prj_Name_FS (Prj_Name_FS'Last - Project_File_Extension'Length + 1
-                         .. Prj_Name_FS'Last) = Project_File_Extension
-         then
-            Last := Prj_Name_FS'Last - Project_File_Extension'Length;
-         else
-            Last := Prj_Name_FS'Last;
-         end if;
-
-         --  Look up project from project tree
-
-         return Result : constant Project_Type := Prj_Tree.Project_From_Name
-           (+Prj_Name_FS (Prj_Name_FS'First .. Last))
-         do
-            if Result = No_Project then
-               Fatal_Error ("project " & Prj_Name & " not found");
-            end if;
-         end return;
-      end Lookup_Project;
-
       ---------------------
       -- Process_Project --
       ---------------------
@@ -977,6 +1000,7 @@ package body Project is
                        (Inc_Units,
                         Cur,
                         Unit_Name,
+                        Info,
                         To_Language (Info.Language));
                      Inc_Units.Reference (Cur).Is_Subunit :=
                        Info.Unit_Part = Unit_Separate;
@@ -984,6 +1008,9 @@ package body Project is
                end Process_Source_File;
 
             begin
+               --  Do not go through imported projects; the Excluded_Units /
+               --  Units attributes only apply to the project itself.
+
                Iterate_Source_Files
                  (Project, Process_Source_File'Access, Recursive => False);
                Inc_Units_Defined := True;
@@ -996,7 +1023,7 @@ package body Project is
          for Cur in Inc_Units.Iterate loop
             declare
                use Unit_Maps;
-               K : constant String := Key (Cur);
+               K : constant Project_Unit := Key (Cur);
             begin
                if not Exc_Units.Contains (K) then
                   declare
@@ -1027,11 +1054,14 @@ package body Project is
 
       Iterate (Override_Units, Add_Pattern'Access);
 
-      --  Then, create a regexp matching all the patterns specified
+      --  Then, create a regexp matching all the patterns specified.
+      --  Regardless of the current platform, the casing of unit names is not
+      --  significant: the Foo unit is the same as the foo unit.
 
-      Create_Matcher (Pattern_List => Unit_Patterns,
-                      Matcher      => Units_Specified_Matcher,
-                      Has_Matcher  => Has_Matcher);
+      Create_Matcher (Pattern_List     => Unit_Patterns,
+                      Matcher          => Units_Specified_Matcher,
+                      Has_Matcher      => Has_Matcher,
+                      Case_Insensitive => True);
 
       --  Now go through all selected projects to find units of interest
 
@@ -1058,7 +1088,7 @@ package body Project is
 
             procedure Add_To_Unit_Presents (C : Unit_Maps.Cursor) is
             begin
-               Units_Present.Append (+Unit_Maps.Key (C));
+               Units_Present.Append (Unit_Maps.Key (C).Unit_Name);
             end Add_To_Unit_Presents;
 
          begin
@@ -1169,9 +1199,22 @@ package body Project is
       Units          : out Unit_Maps.Map;
       Defined        : out Boolean)
    is
+      package FI_Vectors is new Ada.Containers.Vectors
+        (Index_Type => Positive, Element_Type => File_Info);
+
+      use type FI_Vectors.Vector;
+
+      package Unit_Name_FI_Vector_Maps is new Ada.Containers.Ordered_Maps
+        (Key_Type     => Unbounded_String,
+         Element_Type => FI_Vectors.Vector);
+
       Units_Present : String_Vectors.Vector;
       --  List of all units in Prj
 
+      Unit_Present_To_FI : Unit_Name_FI_Vector_Maps.Map;
+      --  Map a unit name to a file information vector. A unit name can be
+      --  be mapped to multiple file info if there are homonym source files.
+
       package Unit_Language_Maps is new Ada.Containers.Hashed_Maps
         (Key_Type        => Unbounded_String,
          Element_Type    => Some_Language,
@@ -1212,10 +1255,19 @@ package body Project is
       -------------------------
 
       procedure Process_Source_File (Info : File_Info; Unit_Name : String) is
+
+         use Unit_Name_FI_Vector_Maps;
+
          Key : constant Unbounded_String := +Unit_Name;
+         Cur : constant Cursor := Unit_Present_To_FI.Find (Key);
       begin
          Units_Present.Append (Key);
          Unit_Languages.Include (Key, To_Language (Info.Language));
+         if Has_Element (Cur) then
+            Unit_Present_To_FI.Reference (Cur).Append (Info);
+         else
+            Unit_Present_To_FI.Insert (Key, FI_Vectors.To_Vector (Info, 1));
+         end if;
       end Process_Source_File;
 
    --  Start of processing for Units_From_Project
@@ -1237,7 +1289,7 @@ package body Project is
       --  source file name.
 
       Iterate_Source_Files
-        (Prj, Process_Source_File'Access, Recursive => True);
+        (Prj, Process_Source_File'Access, Recursive => False);
 
       Match_Pattern_List (Patterns_List        => Unit_Patterns,
                           Strings_List         => Units_Present,
@@ -1249,11 +1301,14 @@ package body Project is
       end loop;
 
       for Unit_Name of Units_Present loop
-         Add_Unit
-           (Units,
-            Ignored_Cur,
-            +Unit_Name,
-            Unit_Languages.Element (Unit_Name));
+         for Info of Unit_Present_To_FI (Unit_Name) loop
+            Add_Unit
+              (Units,
+               Ignored_Cur,
+               +Unit_Name,
+               Info,
+               Unit_Languages.Element (Unit_Name));
+         end loop;
       end loop;
 
    end Units_From_Project;
@@ -1463,9 +1518,15 @@ package body Project is
    --------------
 
    function Switches (Op : String) return String_List_Access is
+      Origin_Prj : constant String :=
+        Prj_Tree.Root_Project.Attribute_Value (Origin_Project_Attribute);
+      Actual_Prj : constant Project_Type :=
+        (if Origin_Prj = ""
+         then Prj_Tree.Root_Project
+         else Lookup_Project (Origin_Prj));
    begin
       return Attribute_Value
-        (Prj_Tree.Root_Project, +Switches, Index => Op);
+        (Actual_Prj, +Switches, Index => Op);
    end Switches;
 
    -----------------
diff --git a/tools/gnatcov/project.ads b/tools/gnatcov/project.ads
index 1c2ccb85a..c08251897 100644
--- a/tools/gnatcov/project.ads
+++ b/tools/gnatcov/project.ads
@@ -18,13 +18,22 @@
 
 --  GNAT projects support
 
+with Ada.Characters.Handling; use Ada.Characters.Handling;
+with Ada.Containers.Ordered_Sets;
+with Ada.Strings.Unbounded;
+
 with GNAT.Strings; use GNAT.Strings;
 
-with GNATCOLL.Projects;
+with GNATCOLL.Projects; use GNATCOLL.Projects;
 with GNATCOLL.VFS;
 
+with GNATcov_RTS.Buffers; use GNATcov_RTS.Buffers;
+
 with Inputs;
-with Switches; use Switches;
+with Paths;        use Paths;
+with Strings;      use Strings;
+with Switches;     use Switches;
+with Traces_Files; use Traces_Files;
 
 package Project is
 
@@ -109,42 +118,73 @@ package Project is
    --  path of its main executable (including its suffix, for instance ".exe").
    --  Otherwise, return an empty string.
 
+   -------------------------
+   -- Unit identification --
+   -------------------------
+
+   type Project_Unit (Language : Any_Language_Kind := Unit_Based_Language) is
+      record
+         Unit_Name : Ada.Strings.Unbounded.Unbounded_String;
+         case Language is
+            when File_Based_Language =>
+               Project_Name : Ada.Strings.Unbounded.Unbounded_String;
+            when others => null;
+         end case;
+      end record;
+   --  To uniquely identify a unit, we need its unit name (or base name for a C
+   --  unit). For file-based languages such as C or C++, we might have homonym
+   --  base file names in different projects so we keep track of the project
+   --  name in addition.
+
+   use type Ada.Strings.Unbounded.Unbounded_String;
+
+   function To_Project_Unit
+     (Unit_Name : String;
+      Project   : Project_Type;
+      Language  : Some_Language) return Project_Unit;
+
+   function Image (U : Project_Unit) return String is
+     (case U.Language is
+         when Unit_Based_Language => To_Lower (+U.Unit_Name),
+         when File_Based_Language =>
+            +U.Project_Name & ":" & Fold_Filename_Casing (+U.Unit_Name));
+
+   function "<" (L, R : Project_Unit) return Boolean is
+     (Image (L) < Image (R));
+
+   function "=" (L, R : Project_Unit) return Boolean is
+     (Image (L) = Image (R));
+
+   package Unit_Sets is new Ada.Containers.Ordered_Sets
+     (Element_Type => Project_Unit);
+
    --------------------------------------
    -- Accessors for project properties --
    --------------------------------------
 
    procedure Enumerate_Units_Of_Interest
-     (Callback : access procedure (Name : String; Is_Subunit : Boolean));
-   --  Call Callback once for every unit of interest. Name is the lower-case
-   --  unit name, and Is_Subunit corresponds to the Unit_Info.Is_Subunit field
-   --  (see project.adb).
-
-   procedure Enumerate_LIs (LI_Cb : access procedure (LI_Name : String))
-      with Pre  => Is_Project_Loaded and then not LIs_Enumerated,
-           Post => LIs_Enumerated;
-   --  Call LI_Cb once for every library information (ALI/GLI) file from a
-   --  project mentioned in a previous Add_Project call.
-   --
-   --  Note that this skips LI files that are not for Ada/C sources. This is
-   --  expected since gnatcov processes LI files only when dealing with binary
-   --  traces, which support only Ada and C (not C++, for instance).
-
-   function LIs_Enumerated return Boolean with Pre => Is_Project_Loaded;
-   --  Return whether Enumerate_LIs was called
-
-   procedure Report_Units_Without_LI with Pre => LIs_Enumerated;
-   --  Output a warning for all units of interest for which we saw no library
-   --  information file.
-
-   procedure Enumerate_SIDs (Callback : access procedure (SID_Name : String))
-      with Pre  => Is_Project_Loaded and then not SIDs_Enumerated,
-           Post => SIDs_Enumerated;
-   --  Invoke Callback once for every SID file corresponding to a unit of
-   --  interest. This emits a warning for all units of interest that have no
-   --  SID file.
-
-   function SIDs_Enumerated return Boolean with Pre => Is_Project_Loaded;
-   --  Return whether Enumerate_SIDs was called
+     (Callback : access procedure (Name : Project_Unit; Is_Subunit : Boolean));
+   --  Call Callback once for every unit of interest. Name is the unit name,
+   --  and Is_Subunit corresponds to the Unit_Info.Is_Subunit field (see
+   --  project.adb).
+
+   function Is_Unit_Of_Interest
+     (Project   : Project_Type;
+      Unit_Name : String;
+      Language  : Some_Language) return Boolean;
+   --  Return whether the unit Unit_Name that belongs to the project Project
+   --  is a unit of interest.
+
+   function Is_Unit_Of_Interest (Full_Name : String) return Boolean;
+   --  Same as above, but given a full name
+
+   procedure Enumerate_SCOs_Files
+     (Callback : access procedure (Lib_Name : String);
+      Kind     : Traces_Files.Trace_File_Kind)
+      with Pre  => Is_Project_Loaded;
+   --  Invoke Callback once for every SCOs file (SID / ALI depending on the
+   --  trace mode) corresponding to a unit of interest. This emits a warning
+   --  for all units of interest that have no SCOs file.
 
    procedure Enumerate_Sources
      (Callback         : access procedure
@@ -188,6 +228,15 @@ package Project is
    --
    --  If no project is loaded, just return null.
 
+   function Runtime_Supports_Finalization return Boolean with
+     Pre => Is_Project_Loaded;
+   --  Return whether the configured runtime includes the Ada.Finalization unit
+
+   function Runtime_Supports_Task_Termination return Boolean with
+     Pre => Is_Project_Loaded;
+   --  Return whether the configured runtime has necessary units to implement
+   --  the ravenscar-task-termination dump trigger.
+
    function Switches (Op : String) return String_List_Access
       with Pre => Is_Project_Loaded;
    --  Return a list of gnatcov switches defined by the root project. Caller
@@ -226,16 +275,16 @@ package Project is
       with Pre => Is_Project_Loaded;
 
    procedure Iterate_Projects
-     (Root_Project : GNATCOLL.Projects.Project_Type;
-      Process      : access procedure (Prj : GNATCOLL.Projects.Project_Type);
-      Recursive    : Boolean;
-      Extended     : Boolean := False)
+     (Root_Project     : GNATCOLL.Projects.Project_Type;
+      Process          : access procedure
+                           (Prj : GNATCOLL.Projects.Project_Type);
+      Recursive        : Boolean;
+      Include_Extended : Boolean := False)
       with Pre => Is_Project_Loaded;
    --  Call Process on Root_Project if Recursive is False, or on the whole
    --  project tree otherwise.
    --
-   --  Visit extended projects only if Extended is True. This is useful only in
-   --  very specific cases, such as when needing to process all object
-   --  direcotries in the project tree.
+   --  Unless Include_Extended is True, only process ultimate extending
+   --  projects.
 
 end Project;
diff --git a/tools/gnatcov/rts/gnatcov_rts-buffers-lists.ads b/tools/gnatcov/rts/gnatcov_rts-buffers-lists.ads
index 23de29cd6..7fc1a14d8 100644
--- a/tools/gnatcov/rts/gnatcov_rts-buffers-lists.ads
+++ b/tools/gnatcov/rts/gnatcov_rts-buffers-lists.ads
@@ -28,16 +28,34 @@ package GNATcov_RTS.Buffers.Lists is
 
    pragma Preelaborate;
 
-   type GNATcov_RTS_Unit_Coverage_Buffers_Array is record
+   type Coverage_Buffers_Access is
+     access constant GNATcov_RTS_Coverage_Buffers;
+   pragma Convention (C, Coverage_Buffers_Access);
+
+   type Coverage_Buffers_Group is
+     array (Positive range <>) of Coverage_Buffers_Access;
+   pragma Convention (C, Coverage_Buffers_Group);
+
+   type GNATcov_RTS_Coverage_Buffers_Group is record
       Length  : aliased unsigned;
       Buffers : System.Address;
+      --  Address of a Coverage_Buffers_Group array of Length items
    end record;
-   pragma Convention (C, GNATcov_RTS_Unit_Coverage_Buffers_Array);
+   pragma Convention (C, GNATcov_RTS_Coverage_Buffers_Group);
 
-   type Unit_Coverage_Buffers_Access is
-     access all GNATcov_RTS_Unit_Coverage_Buffers;
+   type Coverage_Buffers_Group_Access is
+     access constant GNATcov_RTS_Coverage_Buffers_Group;
+   pragma Convention (C, Coverage_Buffers_Group_Access);
 
-   type Unit_Coverage_Buffers_Array is
-     array (Positive range <>) of Unit_Coverage_Buffers_Access;
+   type Coverage_Buffers_Group_Array is
+     array (Positive range <>) of Coverage_Buffers_Group_Access;
+   pragma Convention (C, Coverage_Buffers_Group_Array);
+
+   type GNATcov_RTS_Coverage_Buffers_Group_Array is record
+      Length : aliased unsigned;
+      Groups : System.Address;
+      --  Address of a Coverage_Buffers_Group_Array array of Length items
+   end record;
+   pragma Convention (C, GNATcov_RTS_Coverage_Buffers_Group_Array);
 
 end GNATcov_RTS.Buffers.Lists;
diff --git a/tools/gnatcov/rts/gnatcov_rts-buffers.ads b/tools/gnatcov/rts/gnatcov_rts-buffers.ads
index 6663ba04a..a1fda80fb 100644
--- a/tools/gnatcov/rts/gnatcov_rts-buffers.ads
+++ b/tools/gnatcov/rts/gnatcov_rts-buffers.ads
@@ -62,21 +62,23 @@ package GNATcov_RTS.Buffers is
    type Any_Language_Kind is (Unit_Based_Language, File_Based_Language);
    pragma Convention (C, Any_Language_Kind);
 
-   type SCOs_Hash is new System.Storage_Elements.Storage_Array (1 .. 20);
+   type Fingerprint_Type is
+     new System.Storage_Elements.Storage_Array (1 .. 20);
    --  Hash type to perform consistency checks over Source Coverage
    --  Obligations. 20-byte to hold a SHA-1.
 
-   type GNATcov_RTS_Unit_Coverage_Buffers is record
-      Fingerprint               : SCOs_Hash;
+   type GNATcov_RTS_Coverage_Buffers is record
+      Fingerprint               : Fingerprint_Type;
       Language                  : Any_Language_Kind;
       Unit_Part                 : Any_Unit_Part;
       Unit_Name                 : GNATcov_RTS_String;
       Project_Name              : GNATcov_RTS_String;
+      Bit_Maps_Fingerprint      : Fingerprint_Type;
       Statement, Decision, MCDC : System.Address;
       Statement_Last_Bit, Decision_Last_Bit,
       MCDC_Last_Bit             : Any_Bit_Id;
    end record;
-   pragma Convention (C, GNATcov_RTS_Unit_Coverage_Buffers);
+   pragma Convention (C, GNATcov_RTS_Coverage_Buffers);
 
    -------------------------
    -- Witness subprograms --
diff --git a/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.adb b/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.adb
index 92b9671d2..9a52382c4 100644
--- a/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.adb
+++ b/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.adb
@@ -30,10 +30,10 @@ with GNATcov_RTS.Strings; use GNATcov_RTS.Strings;
 package body GNATcov_RTS.Traces.Output.Base64 is
 
    procedure Write_Trace_File_C
-     (Buffers      : GNATcov_RTS_Unit_Coverage_Buffers_Array;
-      Program_Name : GNATcov_RTS_String;
-      Exec_Date    : Unsigned_64;
-      User_Data    : GNATcov_RTS_String);
+     (Buffers_Groups : GNATcov_RTS_Coverage_Buffers_Group_Array;
+      Program_Name   : GNATcov_RTS_String;
+      Exec_Date      : Unsigned_64;
+      User_Data      : GNATcov_RTS_String);
    pragma Import
      (C, Write_Trace_File_C, "gnatcov_rts_write_trace_file_base64");
 
@@ -42,15 +42,13 @@ package body GNATcov_RTS.Traces.Output.Base64 is
    ----------------------
 
    procedure Write_Trace_File
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Program_Name : String;
-      Exec_Date    : Unsigned_64;
-      User_Data    : String := "")
-   is
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Program_Name   : String;
+      Exec_Date      : Unsigned_64;
+      User_Data      : String := "") is
    begin
       Write_Trace_File_C
-        (GNATcov_RTS_Unit_Coverage_Buffers_Array'
-           (Length => Buffers'Length, Buffers => Buffers'Address),
+        ((Buffers_Groups'Length, Buffers_Groups'Address),
          (Program_Name'Address, Program_Name'Length),
          Exec_Date,
          (User_Data'Address, User_Data'Length));
diff --git a/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.ads b/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.ads
index 12e4e20b8..7efb3c7a5 100644
--- a/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.ads
+++ b/tools/gnatcov/rts/gnatcov_rts-traces-output-base64.ads
@@ -31,16 +31,16 @@ with GNATcov_RTS.Buffers.Lists; use GNATcov_RTS.Buffers.Lists;
 package GNATcov_RTS.Traces.Output.Base64 is
 
    procedure Write_Trace_File
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Program_Name : String;
-      Exec_Date    : Unsigned_64;
-      User_Data    : String := "");
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Program_Name   : String;
+      Exec_Date      : Unsigned_64;
+      User_Data      : String := "");
 
    procedure Write_Trace_File_Wrapper
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Program_Name : String;
-      Exec_Date    : Unsigned_64;
-      User_Data    : String := "") renames Write_Trace_File;
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Program_Name   : String;
+      Exec_Date      : Unsigned_64;
+      User_Data      : String := "") renames Write_Trace_File;
    --  Renaming to mirror the API in GNATcov_RTS.Traces.Output.Files. This
    --  avoids special cases in the generation of automatic buffer dumps: except
    --  for the package that is WITHed, generated code is the same for
diff --git a/tools/gnatcov/rts/gnatcov_rts-traces-output-files.adb b/tools/gnatcov/rts/gnatcov_rts-traces-output-files.adb
index ef6af3d6f..cedbc2d03 100644
--- a/tools/gnatcov/rts/gnatcov_rts-traces-output-files.adb
+++ b/tools/gnatcov/rts/gnatcov_rts-traces-output-files.adb
@@ -36,11 +36,11 @@ with GNATcov_RTS.Strings; use GNATcov_RTS.Strings;
 package body GNATcov_RTS.Traces.Output.Files is
 
    function Write_Trace_File_C
-     (Buffers      : GNATcov_RTS_Unit_Coverage_Buffers_Array;
-      Filename     : chars_ptr;
-      Program_Name : GNATcov_RTS_String;
-      Exec_Date    : Unsigned_64;
-      User_Data    : GNATcov_RTS_String) return int;
+     (Buffers_Groups : GNATcov_RTS_Coverage_Buffers_Group_Array;
+      Filename       : chars_ptr;
+      Program_Name   : GNATcov_RTS_String;
+      Exec_Date      : Unsigned_64;
+      User_Data      : GNATcov_RTS_String) return int;
    pragma Import
      (C, Write_Trace_File_C, External_Name => "gnatcov_rts_write_trace_file");
 
@@ -95,17 +95,16 @@ package body GNATcov_RTS.Traces.Output.Files is
    ----------------------
 
    procedure Write_Trace_File
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Filename     : String := Default_Trace_Filename;
-      Program_Name : String := Ada.Command_Line.Command_Name;
-      Exec_Date    : Time   := Clock;
-      User_Data    : String := "")
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Filename       : String := Default_Trace_Filename;
+      Program_Name   : String := Ada.Command_Line.Command_Name;
+      Exec_Date      : Time   := Clock;
+      User_Data      : String := "")
    is
       Filename_C : chars_ptr := New_String (Filename);
    begin
       if Write_Trace_File_C
-           (GNATcov_RTS_Unit_Coverage_Buffers_Array'
-              (Length => Buffers'Length, Buffers => Buffers'Address),
+           ((Buffers_Groups'Length, Buffers_Groups'Address),
             Filename_C,
             (Program_Name'Address, Program_Name'Length),
             Interfaces.Unsigned_64 (Exec_Date),
@@ -122,11 +121,11 @@ package body GNATcov_RTS.Traces.Output.Files is
    ------------------------------
 
    procedure Write_Trace_File_Wrapper
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Filename     : String := Default_Trace_Filename;
-      Program_Name : String := Ada.Command_Line.Command_Name;
-      Exec_Date    : Time   := Clock;
-      User_Data    : String := "")
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Filename       : String := Default_Trace_Filename;
+      Program_Name   : String := Ada.Command_Line.Command_Name;
+      Exec_Date      : Time   := Clock;
+      User_Data      : String := "")
    is
 
       function C_Strerror (Errnum : C.int) return C.Strings.chars_ptr;
@@ -136,7 +135,8 @@ package body GNATcov_RTS.Traces.Output.Files is
       --  errno.
 
    begin
-      Write_Trace_File (Buffers, Filename, Program_Name, Exec_Date, User_Data);
+      Write_Trace_File
+        (Buffers_Groups, Filename, Program_Name, Exec_Date, User_Data);
    exception
       when IO_Error =>
          declare
diff --git a/tools/gnatcov/rts/gnatcov_rts-traces-output-files.ads b/tools/gnatcov/rts/gnatcov_rts-traces-output-files.ads
index 80d486867..1b272a8f0 100644
--- a/tools/gnatcov/rts/gnatcov_rts-traces-output-files.ads
+++ b/tools/gnatcov/rts/gnatcov_rts-traces-output-files.ads
@@ -57,20 +57,20 @@ package GNATcov_RTS.Traces.Output.Files is
    --  Exception we raise in case of errors during the trace file creation
 
    procedure Write_Trace_File
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Filename     : String := Default_Trace_Filename;
-      Program_Name : String := Ada.Command_Line.Command_Name;
-      Exec_Date    : Time   := Clock;
-      User_Data    : String := "");
-   --  Write a trace file in Filename to contain the data in Buffers.
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Filename       : String := Default_Trace_Filename;
+      Program_Name   : String := Ada.Command_Line.Command_Name;
+      Exec_Date      : Time   := Clock;
+      User_Data      : String := "");
+   --  Write a trace file in Filename to contain the data in Buffers_Groups.
    --  If unsuccessful, raise IO_Error and leave the error code in errno.
 
    procedure Write_Trace_File_Wrapper
-     (Buffers      : Unit_Coverage_Buffers_Array;
-      Filename     : String := Default_Trace_Filename;
-      Program_Name : String := Ada.Command_Line.Command_Name;
-      Exec_Date    : Time   := Clock;
-      User_Data    : String := "");
+     (Buffers_Groups : Coverage_Buffers_Group_Array;
+      Filename       : String := Default_Trace_Filename;
+      Program_Name   : String := Ada.Command_Line.Command_Name;
+      Exec_Date      : Time   := Clock;
+      User_Data      : String := "");
    --  Wrapper around Write_Trace_File that writes an error message to the
    --  standard error if the trace file could not be written.
 
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-base_io.c b/tools/gnatcov/rts/gnatcov_rts_c-base_io.c
index 968ff64b0..22c5bad77 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-base_io.c
+++ b/tools/gnatcov/rts/gnatcov_rts_c-base_io.c
@@ -25,7 +25,7 @@
    functions to print to the standard output.  */
 
 int
-gnatcov_rts_puts (gnatcov_rts_string str)
+gnatcov_rts_puts (struct gnatcov_rts_string str)
 {
   fwrite (str.str, 1, str.length, stdout);
   fwrite ("\n", 1, 1, stdout);
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-base_io.h b/tools/gnatcov/rts/gnatcov_rts_c-base_io.h
index 30837fa79..62ec455b2 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-base_io.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-base_io.h
@@ -17,7 +17,6 @@
  *                                                                          *
  ****************************************************************************/
 
-
 /* This header provides the smallest subset of functions needed to output
    bytes.  These functions are used in gnatcov_rts_c-traces-output-base64.c to
    dump base64 traces to "the output": stdout when linking with a C runtime
@@ -31,12 +30,13 @@
 #include "gnatcov_rts_c_strings.h"
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
-/* See gnatcov_rts-base_io.ads.  */
-extern int gnatcov_rts_puts (gnatcov_rts_string str);
-extern int gnatcov_rts_putchar (int c);
+  /* See gnatcov_rts-base_io.ads.  */
+  extern int gnatcov_rts_puts (struct gnatcov_rts_string str);
+  extern int gnatcov_rts_putchar (int c);
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-buffers.c b/tools/gnatcov/rts/gnatcov_rts_c-buffers.c
index 11081ba19..7a78eb5c1 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-buffers.c
+++ b/tools/gnatcov/rts/gnatcov_rts_c-buffers.c
@@ -30,8 +30,8 @@ gnatcov_rts_witness (uint8_t *buffer, gnatcov_rts_bit_id bit)
 
 gnatcov_rts_bool
 gnatcov_rts_witness_decision (uint8_t *buffer, gnatcov_rts_bit_id false_bit,
-                              gnatcov_rts_bit_id true_bit,
-                              gnatcov_rts_bool value)
+			      gnatcov_rts_bit_id true_bit,
+			      gnatcov_rts_bool value)
 {
   gnatcov_rts_witness (buffer, value ? true_bit : false_bit);
   return value;
@@ -39,23 +39,23 @@ gnatcov_rts_witness_decision (uint8_t *buffer, gnatcov_rts_bit_id false_bit,
 
 gnatcov_rts_bool
 gnatcov_rts_witness_decision_mcdc (uint8_t *decision_buffer,
-                                   gnatcov_rts_bit_id false_bit,
-                                   gnatcov_rts_bit_id true_bit,
-                                   uint8_t *mcdc_buffer,
-                                   gnatcov_rts_bit_id mcdc_base,
-                                   gnatcov_rts_bit_id *mcdc_path_address,
-                                   gnatcov_rts_bool value)
+				   gnatcov_rts_bit_id false_bit,
+				   gnatcov_rts_bit_id true_bit,
+				   uint8_t *mcdc_buffer,
+				   gnatcov_rts_bit_id mcdc_base,
+				   gnatcov_rts_bit_id *mcdc_path_address,
+				   gnatcov_rts_bool value)
 {
   gnatcov_rts_bit_id mcdc_path_index = *mcdc_path_address;
   gnatcov_rts_witness (mcdc_buffer, mcdc_base + mcdc_path_index);
   return gnatcov_rts_witness_decision (decision_buffer, false_bit, true_bit,
-                                       value);
+				       value);
 }
 
 gnatcov_rts_bool
 gnatcov_rts_witness_condition (gnatcov_rts_bit_id *mcdc_path_address,
-                               gnatcov_rts_bit_id offset_for_true,
-                               gnatcov_rts_bool first, gnatcov_rts_bool value)
+			       gnatcov_rts_bit_id offset_for_true,
+			       gnatcov_rts_bool first, gnatcov_rts_bool value)
 {
   gnatcov_rts_bit_id *mcdc_path_index
     = (gnatcov_rts_bit_id *) mcdc_path_address;
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-buffers.h b/tools/gnatcov/rts/gnatcov_rts_c-buffers.h
index 81d3b03c0..ad584520f 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-buffers.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-buffers.h
@@ -25,138 +25,152 @@
 #include "gnatcov_rts_c_strings.h"
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
-/* Coverage buffers are big arrays of booleans. How to interpret them depends
-   on the type of coverage obligation.
+  /* Coverage buffers are big arrays of booleans. How to interpret them depends
+     on the type of coverage obligation.
 
-   For statement coverage, each statement is assigned such a boolean, which
-   indicates whether it was executed.
+     For statement coverage, each statement is assigned such a boolean, which
+     indicates whether it was executed.
 
-   For decision coverage, each decision is assigned two booleans: one which
-   indicates whether the decision reached the False outcome and another for
-   the True outcome.
+     For decision coverage, each decision is assigned two booleans: one which
+     indicates whether the decision reached the False outcome and another for
+     the True outcome.
 
-   For MC/DC coverage, there is a boolean for each decision valuation path in
-   the binary decision diagram.  */
+     For MC/DC coverage, there is a boolean for each decision valuation path in
+     the binary decision diagram.  */
 
-/* Representation of the boolean type */
-typedef unsigned gnatcov_rts_bool;
+  /* Representation of the boolean type.  */
+  typedef unsigned gnatcov_rts_bool;
 
-/* Unique identifier for a boolean in a coverage buffer */
-typedef int gnatcov_rts_bit_id;
+  /* Unique identifier for a boolean in a coverage buffer.  */
+  typedef int gnatcov_rts_bit_id;
 
 #define FINGERPRINT_SIZE 20
 
-enum gnatcov_rts_unit_part
-{
-  /* `NOT_APPLICABLE_PART` is a default value used for compilation units in
-     languages that are not unit-based.  */
-  NOT_APPLICABLE_PART = 0,
-  UNIT_BODY = 1,
-  UNIT_SPEC = 2,
-  UNIT_SEPARATE = 3
-};
-
-enum gnatcov_rts_language_kind
-{
-  UNIT_BASED_LANGUAGE = 0,
-  FILE_BASED_LANGUAGE = 1
-};
-
-typedef struct gnatcov_rts_unit_coverage_buffers
-{
-  /* Hash of SCO info for this unit, as gnatcov computes it (see
-     SC_Obligations).  Used as a fast way to check that coverage obligations
-     and coverage data are consistent. Specific hash values are computed during
-     instrumentation.  */
-  uint8_t fingerprint[FINGERPRINT_SIZE];
-
-  /* Language kind for this unit.  */
-  enum gnatcov_rts_language_kind language_kind;
-
-  /* Unit kind and name for the instrumented unit.  The unit_name field
-     accounts both for unit-based languages (such as Ada) and file-based
-     languages such as C.
-
-     The unit_part field is only there for unit-based languages and is set
-     to NOT_APPLICABLE_PART for file-based languages.
-
-     More specifically, for unit-based languages, unit_name is the fully
-     qualified name of the compilation unit (or subunit) in lower case.
-     For instance: "foo", "ada.text_io" or "foo.bar.my_subunit".
-
-     For file-based languages, unit_name is the simple filename, e.g.
-     "foo.c".  */
-  enum gnatcov_rts_unit_part unit_part;
-  gnatcov_rts_string unit_name;
-
-  /* Project name for this compilation unit.  This is only initialized for
-     file-based languages (otherwise, it is an empty string).  */
-  gnatcov_rts_string project_name;
-
-  /* Addresses of coverage buffers for statement obligations, decision
-     obligations and MC/DC obligations.  The address refer to
-     Coverage_Buffer_Type arrays whose bounds go from 0 to
-     unit_coverage_buffers.*_last_bit.  */
-  uint8_t *statement, *decision, *mcdc;
-
-  /* Index for the last bits in coverage buffers for statements, decisions and
-     MC/DC.  */
-  gnatcov_rts_bit_id statement_last_bit, decision_last_bit, mcdc_last_bit;
-} gnatcov_rts_unit_coverage_buffers;
-
-/* Array of unit coverage buffers.  */
-typedef struct gnatcov_rts_unit_coverage_buffers_array
-{
-  unsigned length;
-  gnatcov_rts_unit_coverage_buffers **buffers;
-} gnatcov_rts_unit_coverage_buffers_array;
-
-/***********************/
-/* Witness subprograms */
-/***********************/
-
-/* The following subprograms are called by generated code to record
-   the execution of constructs.  */
-
-/* Statements */
-
-/* Set the boolean corresponding to BIT to true in BUFFER in various
-   context.  */
-extern gnatcov_rts_bool gnatcov_rts_witness (uint8_t *buffer,
-                                             gnatcov_rts_bit_id bit);
-
-/* Decisions */
-
-/* If VALUE is false, set the boolean corresponding to FALSE_BIT to true in
-   BUFFER.  Set the one corresponding to TRUE_BIT otherwise.  */
-extern gnatcov_rts_bool
-gnatcov_rts_witness_decision (uint8_t *buffer, gnatcov_rts_bit_id false_bit,
-                              gnatcov_rts_bit_id true_bit,
-                              gnatcov_rts_bool value);
-
-/* Same as above, and also set the bit determined by MCDC_BASE and the
-   gnatcov_rts_bit_id value at MCDC_PATH_ADDRESS in the buffer at
-   MCDC_BUFFER_ADDRESS.  Note that MCDC_PATH may not be passed by value,
-   because it is not known until the side effect of the actual for the VALUE
-   formal have been evaluated.  */
-extern gnatcov_rts_bool gnatcov_rts_witness_decision_mcdc (
+  enum gnatcov_rts_unit_part
+  {
+    /* `NOT_APPLICABLE_PART` is a default value used for compilation units in
+       languages that are not unit-based.  */
+    NOT_APPLICABLE_PART = 0,
+    UNIT_BODY = 1,
+    UNIT_SPEC = 2,
+    UNIT_SEPARATE = 3
+  };
+
+  enum gnatcov_rts_language_kind
+  {
+    UNIT_BASED_LANGUAGE = 0,
+    FILE_BASED_LANGUAGE = 1
+  };
+
+  struct gnatcov_rts_coverage_buffers
+  {
+    /* Hash of SCO info for this unit, as gnatcov computes it (see
+       SC_Obligations).  Used as a fast way to check that coverage obligations
+       and coverage data are consistent. Specific hash values are computed
+       during instrumentation.  */
+    uint8_t fingerprint[FINGERPRINT_SIZE];
+
+    /* Language kind for this unit.  */
+    enum gnatcov_rts_language_kind language_kind;
+
+    /* Unit kind and name for the instrumented unit.  The unit_name field
+       accounts both for unit-based languages (such as Ada) and file-based
+       languages such as C.
+
+       The unit_part field is only there for unit-based languages and is set
+       to NOT_APPLICABLE_PART for file-based languages.
+
+       More specifically, for unit-based languages, unit_name is the fully
+       qualified name of the compilation unit (or subunit) in lower case.
+       For instance: "foo", "ada.text_io" or "foo.bar.my_subunit".
+
+       For file-based languages, unit_name is the simple filename, e.g.
+       "foo.c".  */
+    enum gnatcov_rts_unit_part unit_part;
+    struct gnatcov_rts_string unit_name;
+
+    /* Project name for this compilation unit.  This is only initialized for
+       file-based languages (otherwise, it is an empty string).  */
+    struct gnatcov_rts_string project_name;
+
+    /* Hash of buffer bit mappings for this unit, as gnatcov computes it (see
+       SC_Obligations).  Used as a fast way to check that gnatcov will be able
+       to interpret buffer bits from a source traces using buffer bit mappings
+       from SID files.  */
+    uint8_t bit_maps_fingerprint[FINGERPRINT_SIZE];
+
+    /* Pointers to coverage buffers for statement obligations, decision
+       obligations and MC/DC obligations.  The size of each array is in the
+       corresponding *_last_bit field.  */
+    uint8_t *statement, *decision, *mcdc;
+
+    /* Index for the last bits in coverage buffers for statements, decisions
+       and MC/DC.  */
+    gnatcov_rts_bit_id statement_last_bit, decision_last_bit, mcdc_last_bit;
+  };
+
+  /* Group of coverage buffers.  For a given source file, instrumentation may
+     create more than one set of coverage buffers: these are grouped in a
+     coverage buffers group.  */
+  struct gnatcov_rts_coverage_buffers_group
+  {
+    unsigned length;
+    const struct gnatcov_rts_coverage_buffers **buffers;
+  };
+
+  /* Array of coverage buffers groups.  */
+  struct gnatcov_rts_coverage_buffers_group_array
+  {
+    unsigned length;
+    const struct gnatcov_rts_coverage_buffers_group **groups;
+  };
+
+  /***********************/
+  /* Witness subprograms */
+  /***********************/
+
+  /* The following subprograms are called by generated code to record
+     the execution of constructs.  */
+
+  /* Statements */
+
+  /* Set the boolean corresponding to BIT to true in BUFFER in various
+     context.  */
+  extern gnatcov_rts_bool gnatcov_rts_witness (uint8_t *buffer,
+					       gnatcov_rts_bit_id bit);
+
+  /* Decisions */
+
+  /* If VALUE is false, set the boolean corresponding to FALSE_BIT to true in
+     BUFFER.  Set the one corresponding to TRUE_BIT otherwise.  */
+  extern gnatcov_rts_bool
+  gnatcov_rts_witness_decision (uint8_t *buffer, gnatcov_rts_bit_id false_bit,
+				gnatcov_rts_bit_id true_bit,
+				gnatcov_rts_bool value);
+
+  /* Same as above, and also set the bit determined by MCDC_BASE and the
+     gnatcov_rts_bit_id value at MCDC_PATH_ADDRESS in the buffer at
+     MCDC_BUFFER_ADDRESS.  Note that MCDC_PATH may not be passed by value,
+     because it is not known until the side effect of the actual for the VALUE
+     formal have been evaluated.  */
+  extern gnatcov_rts_bool gnatcov_rts_witness_decision_mcdc (
     uint8_t *decision_buffer, gnatcov_rts_bit_id false_bit,
     gnatcov_rts_bit_id true_bit, uint8_t *mcdc_buffer,
     gnatcov_rts_bit_id mcdc_base, gnatcov_rts_bit_id *mcdc_path_address,
     gnatcov_rts_bool value);
 
-/* Conditions */
+  /* Conditions */
 
-/* MCDC_PATH_ADDRESS is the address of a local variable storing the mcdc_state.
-   If FIRST is true, first reset it to 0.  If VALUE is true, add
-   OFFSET_FOR_TRUE.  */
-extern gnatcov_rts_bool
-gnatcov_rts_witness_condition (gnatcov_rts_bit_id *mcdc_path_address,
-                               gnatcov_rts_bit_id offset_for_true,
-                               gnatcov_rts_bool first, gnatcov_rts_bool value);
+  /* MCDC_PATH_ADDRESS is the address of a local variable storing the
+     mcdc_state. If FIRST is true, first reset it to 0.  If VALUE is true, add
+     OFFSET_FOR_TRUE.  */
+  extern gnatcov_rts_bool gnatcov_rts_witness_condition (
+    gnatcov_rts_bit_id *mcdc_path_address, gnatcov_rts_bit_id offset_for_true,
+    gnatcov_rts_bool first, gnatcov_rts_bool value);
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-os_interface.h b/tools/gnatcov/rts/gnatcov_rts_c-os_interface.h
index 4554f3241..4a9ab24d5 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-os_interface.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-os_interface.h
@@ -20,21 +20,21 @@
 #include <stdint.h>
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
+  /* The C "time" function can return integers of
+     different size depending on the platform.
+     Here, we ensure that the returned result will be an
+     expected long long (a 64 bit integer). It will thus
+     be compatible with 32 bit as well as 64 bit architectures.
+     It also ensures compatibility with Ada Long_Long_Integer
+     standard type.  */
+  extern uint64_t gnatcov_rts_time_to_uint64 (void);
 
-/* The C "time" function can return integers of
-   different size depending on the platform.
-   Here, we ensure that the returned result will be an
-   expected long long (a 64 bit integer). It will thus
-   be compatible with 32 bit as well as 64 bit architectures.
-   It also ensures compatibility with Ada Long_Long_Integer
-   standard type.  */
-extern uint64_t gnatcov_rts_time_to_uint64 (void);
-
-/* Return the current process ID as an unsigned 64-bit integer.  */
-extern uint64_t gnatcov_rts_getpid (void);
+  /* Return the current process ID as an unsigned 64-bit integer.  */
+  extern uint64_t gnatcov_rts_getpid (void);
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.c b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.c
index 80b358129..f11d33875 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.c
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.c
@@ -119,26 +119,26 @@ write_bytes (void *output, const void *bytes, unsigned count)
       buffer->bytes[buffer->next] = bytes_array[i];
       buffer->next = buffer->next + 1;
       if (buffer->next == 3)
-        flush (buffer);
+	flush (buffer);
     }
   return 0;
 }
 
 void
 gnatcov_rts_write_trace_file_base64 (
-  const gnatcov_rts_unit_coverage_buffers_array *buffers,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data)
+  const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+  struct gnatcov_rts_string program_name, uint64_t exec_date,
+  struct gnatcov_rts_string user_data)
 {
   gnatcov_rts_base64_buffer buffer;
   buffer.next = 0;
   buffer.columns = 0;
-  gnatcov_rts_string begin_string
+  struct gnatcov_rts_string begin_string
     = STR ("== GNATcoverage source trace file ==");
-  gnatcov_rts_string end_string = STR ("== End ==");
+  struct gnatcov_rts_string end_string = STR ("== End ==");
   gnatcov_rts_putchar ('\n');
   gnatcov_rts_puts (begin_string);
-  gnatcov_rts_generic_write_trace_file (&buffer, buffers, program_name,
+  gnatcov_rts_generic_write_trace_file (&buffer, buffers_groups, program_name,
 					exec_date, user_data, write_bytes);
   flush (&buffer);
   if (buffer.columns != 0)
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.h b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.h
index 587ebfdb7..dfba2512e 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-base64.h
@@ -22,16 +22,17 @@
 #include <stdint.h>
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
-/* Write a Base64-encoded trace file to the standard output.  See the
-   documentation of the gnatcov_rts_generic_write_trace_file function in
-   gnatcov_rts_c-output.h for more information.  */
-extern void gnatcov_rts_write_trace_file_base64 (
-  const gnatcov_rts_unit_coverage_buffers_array *buffers,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data);
+  /* Write a Base64-encoded trace file to the standard output.  See the
+     documentation of the gnatcov_rts_generic_write_trace_file function in
+     gnatcov_rts_c-output.h for more information.  */
+  extern void gnatcov_rts_write_trace_file_base64 (
+    const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+    struct gnatcov_rts_string program_name, uint64_t exec_date,
+    struct gnatcov_rts_string user_data);
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.c b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.c
index 21042d88e..840183079 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.c
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.c
@@ -129,41 +129,41 @@ gnatcov_rts_default_trace_filename (const char *env_var, const char *prefix,
   else
     {
       /* The caller is supposed to free the returned string, so create a
-         copy.  */
+	 copy.  */
       env_trace_filename = concat (env_trace_filename, NULL);
 
       /* If the filename ends with a directory separator, consider that it
-         refers to a directory: in that case return a filename inside it.  */
+	 refers to a directory: in that case return a filename inside it.  */
       size_t length = strlen (env_trace_filename);
       if (length > 0
-          && (env_trace_filename[length - 1] == '/'
-              || env_trace_filename[length - 1] == '\\'))
-        {
-          char *basename
-            = gnatcov_rts_default_trace_basename (prefix, tag, simple);
-          char *res = concat (env_trace_filename, basename, NULL);
-          free (env_trace_filename);
-          free (basename);
-          return res;
-        }
+	  && (env_trace_filename[length - 1] == '/'
+	      || env_trace_filename[length - 1] == '\\'))
+	{
+	  char *basename
+	    = gnatcov_rts_default_trace_basename (prefix, tag, simple);
+	  char *res = concat (env_trace_filename, basename, NULL);
+	  free (env_trace_filename);
+	  free (basename);
+	  return res;
+	}
       else
-        return env_trace_filename;
+	return env_trace_filename;
     }
 }
 
 /* See gnatcov_rts_c-traces-output-files.h.  */
 int
 gnatcov_rts_write_trace_file (
-  const gnatcov_rts_unit_coverage_buffers_array *buffers, const char *filename,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data)
+  const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+  const char *filename, struct gnatcov_rts_string program_name,
+  uint64_t exec_date, struct gnatcov_rts_string user_data)
 {
   FILE *file = fopen (filename, "wb+");
   if (!file)
     return 1;
 
-  gnatcov_rts_generic_write_trace_file (file, buffers, program_name, exec_date,
-                                        user_data, write_bytes);
+  gnatcov_rts_generic_write_trace_file (file, buffers_groups, program_name,
+					exec_date, user_data, write_bytes);
   fclose (file);
   return 0;
 }
@@ -171,14 +171,13 @@ gnatcov_rts_write_trace_file (
 /* See gnatcov_rts_c-traces-output-files.h.  */
 void
 gnatcov_rts_write_trace_file_wrapper (
-  const gnatcov_rts_unit_coverage_buffers_array *buffers,
-  const char *filename, gnatcov_rts_string program_name,
-  uint64_t exec_date, gnatcov_rts_string user_data)
+  const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+  const char *filename, struct gnatcov_rts_string program_name,
+  uint64_t exec_date, struct gnatcov_rts_string user_data)
 {
-  if (gnatcov_rts_write_trace_file
-       (buffers, filename, program_name, exec_date, user_data) != 0)
-    fprintf
-      (stderr,
-       "Error occurred while creating the trace file %s: %s\n",
-       filename, strerror (errno));
+  if (gnatcov_rts_write_trace_file (buffers_groups, filename, program_name,
+				    exec_date, user_data)
+      != 0)
+    fprintf (stderr, "Error occurred while creating the trace file %s: %s\n",
+	     filename, strerror (errno));
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.h b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.h
index 667938ea6..16efcc789 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces-output-files.h
@@ -22,7 +22,8 @@
 #include <stdint.h>
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
 /* Default name of the environment variable which controls the default
@@ -30,52 +31,50 @@ extern "C" {
    function below.  */
 #define GNATCOV_RTS_DEFAULT_TRACE_FILENAME_ENV_VAR "GNATCOV_TRACE_FILE"
 
-/* Return the default name of the trace file to write.  The returned name must
-   be manually freed.
+  /* Return the default name of the trace file to write.  The returned name
+     must be manually freed.
 
-   If the ENV_VAR environment variable is not defined or empty, return:
+     If the ENV_VAR environment variable is not defined or empty, return:
 
-   * "PREFIX.srctrace" if SIMPLE is True.
+     * "PREFIX.srctrace" if SIMPLE is True.
 
-   * "PREFIX-TAG-PID-CLOCK.srctrace" if SIMPLE is False (PID is the current
-     process ID and CLOCK is the execution timestamp). The "-TAG" part is
-     omitted if TAG is the empty string.
+     * "PREFIX-TAG-PID-CLOCK.srctrace" if SIMPLE is False (PID is the current
+       process ID and CLOCK is the execution timestamp). The "-TAG" part is
+       omitted if TAG is the empty string.
 
-   If the ENV_VAR environment variable is defined and not empty, then:
+     If the ENV_VAR environment variable is defined and not empty, then:
 
-   * if it ends with "/" or "\", consider it contains the name of a directory:
-     use the algorithm described above to compute the basename and return the
-     filename in that directory;
+     * if it ends with "/" or "\", consider it contains the name of a
+     directory: use the algorithm described above to compute the basename and
+     return the filename in that directory;
 
-   * otherwise, just return the content of that environment variable.  */
-extern char *gnatcov_rts_default_trace_filename (const char *env_var,
-						 const char *prefix,
-						 const char *tag,
-						 unsigned simple);
+     * otherwise, just return the content of that environment variable.  */
+  extern char *gnatcov_rts_default_trace_filename (const char *env_var,
+						   const char *prefix,
+						   const char *tag,
+						   unsigned simple);
 
-/* Write a trace file in FILENAME to contain the data in BUFFERS.
+  /* Write a trace file in FILENAME to contain the data in BUFFERS_GROUPS.
 
-   PROGRAM_NAME, EXEC_DATE, and USER_DATA are used to fill the
-   corresponding metadata in the written trace file.
+     PROGRAM_NAME, EXEC_DATE, and USER_DATA are used to fill the
+     corresponding metadata in the written trace file.
 
-   EXEC_DATE is given to produce the timestamp. Use the current
-   time by default.
+     EXEC_DATE is given to produce the timestamp. Use the current
+     time by default.
 
-   Return 0 if the trace creation was successful, 1 otherwise.  In case of
-   error, ERRNO is left to the number for the cause of error.  */
-extern int
-gnatcov_rts_write_trace_file (
-  const gnatcov_rts_unit_coverage_buffers_array *buffers, const char *filename,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data);
+     Return 0 if the trace creation was successful, 1 otherwise.  In case of
+     error, ERRNO is left to the number for the cause of error.  */
+  extern int gnatcov_rts_write_trace_file (
+    const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+    const char *filename, struct gnatcov_rts_string program_name,
+    uint64_t exec_date, struct gnatcov_rts_string user_data);
 
-/* Call gnatcov_rts_write_trace_file and print an error message on the standard
-   error if the trace could not be created.  */
-extern void
-gnatcov_rts_write_trace_file_wrapper (
-  const gnatcov_rts_unit_coverage_buffers_array *buffers, const char *filename,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data);
+  /* Call gnatcov_rts_write_trace_file and print an error message on the
+     standard error if the trace could not be created.  */
+  extern void gnatcov_rts_write_trace_file_wrapper (
+    const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+    const char *filename, struct gnatcov_rts_string program_name,
+    uint64_t exec_date, struct gnatcov_rts_string user_data);
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces-output.c b/tools/gnatcov/rts/gnatcov_rts_c-traces-output.c
index b3ea17049..7d7b322ba 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces-output.c
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces-output.c
@@ -24,16 +24,16 @@
 
 const unsigned alignment = sizeof (void *);
 
-typedef struct info_entry_d
+struct info_entry
 {
   const void *data;
   uint32_t length;
-} info_entry;
+};
 
 /* Write `count` padding bytes.  */
 static void
 write_padding (gnatcov_rts_write_bytes_callback write_bytes, void *output,
-               unsigned count)
+	       unsigned count)
 {
   unsigned pad_count = (alignment - count) % alignment;
   if (pad_count != alignment)
@@ -62,7 +62,7 @@ write_header (gnatcov_rts_write_bytes_callback write_bytes, void *output)
 /* Write an information entry (e.g. the program_name, the exec date etc.).  */
 static void
 write_info (gnatcov_rts_write_bytes_callback write_bytes, void *output,
-            uint32_t kind, info_entry *data)
+	    uint32_t kind, struct info_entry *data)
 {
   struct trace_info_header header;
   header.kind = kind;
@@ -91,7 +91,7 @@ flush (gnatcov_rts_write_bytes_callback write_bytes, void *output,
 /* Write a coverage bit buffer.  */
 static void
 write_buffer (gnatcov_rts_write_bytes_callback write_bytes, void *output,
-              uint8_t *buffer, unsigned buffer_length)
+	      uint8_t *buffer, unsigned buffer_length)
 {
   uint8_t current_byte = 0;
   uint8_t bit_mask = 1;
@@ -101,11 +101,11 @@ write_buffer (gnatcov_rts_write_bytes_callback write_bytes, void *output,
   for (i = 0; i < buffer_length; i++)
     {
       if (buffer[i])
-        current_byte = current_byte | bit_mask;
+	current_byte = current_byte | bit_mask;
       if (bit_mask == 1 << 7)
-        flush (write_bytes, output, &bit_mask, &current_byte, &bytes_count);
+	flush (write_bytes, output, &bit_mask, &current_byte, &bytes_count);
       else
-        bit_mask <<= 1;
+	bit_mask <<= 1;
     }
   flush (write_bytes, output, &bit_mask, &current_byte, &bytes_count);
 
@@ -115,7 +115,7 @@ write_buffer (gnatcov_rts_write_bytes_callback write_bytes, void *output,
 /* Write a coverage buffer (unit information + coverage bit buffers).  */
 static void
 write_entry (gnatcov_rts_write_bytes_callback write_bytes, void *output,
-             const gnatcov_rts_unit_coverage_buffers *buffers)
+	     const struct gnatcov_rts_coverage_buffers *buffers)
 {
   struct trace_entry_header header;
   header.unit_name_length = (uint32_t) buffers->unit_name.length;
@@ -126,30 +126,32 @@ write_entry (gnatcov_rts_write_bytes_callback write_bytes, void *output,
   header.language_kind = (uint8_t) buffers->language_kind;
   header.unit_part = (uint8_t) buffers->unit_part;
   header.bit_buffer_encoding = GNATCOV_RTS_LSB_FIRST_BYTES;
-  memset (header.padding, 0, 5);
-  memcpy (&(header.fingerprint), buffers->fingerprint, FINGERPRINT_SIZE);
+  memset (header.padding, 0, 1);
+  memcpy (&header.fingerprint, buffers->fingerprint, FINGERPRINT_SIZE);
+  memcpy (&header.bit_maps_fingerprint, buffers->bit_maps_fingerprint,
+	  FINGERPRINT_SIZE);
 
   write_bytes (output, (char *) &header, sizeof (header));
   write_bytes (output, buffers->unit_name.str, buffers->unit_name.length);
   write_padding (write_bytes, output, buffers->unit_name.length);
-  write_bytes
-    (output, buffers->project_name.str, buffers->project_name.length);
+  write_bytes (output, buffers->project_name.str,
+	       buffers->project_name.length);
   write_padding (write_bytes, output, buffers->project_name.length);
   write_buffer (write_bytes, output, buffers->statement,
-                buffers->statement_last_bit + 1);
+		buffers->statement_last_bit + 1);
   write_buffer (write_bytes, output, buffers->decision,
-                buffers->decision_last_bit + 1);
+		buffers->decision_last_bit + 1);
   write_buffer (write_bytes, output, buffers->mcdc,
-                buffers->mcdc_last_bit + 1);
+		buffers->mcdc_last_bit + 1);
 }
 
 /* Write a uint64_t timestamp (by splitting it in uint8_t chunks).  */
 void
 write_date (gnatcov_rts_write_bytes_callback write_bytes, void *output,
-            uint64_t timestamp)
+	    uint64_t timestamp)
 {
   uint8_t formatted_date[8];
-  info_entry entry;
+  struct info_entry entry;
   int i;
 
   for (i = 0; i < 8; i++)
@@ -166,33 +168,40 @@ write_date (gnatcov_rts_write_bytes_callback write_bytes, void *output,
 /* See gnatcov_rts_c-traces-output.h.  */
 int
 gnatcov_rts_generic_write_trace_file (
-  void *output, const gnatcov_rts_unit_coverage_buffers_array *buffers,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data, gnatcov_rts_write_bytes_callback write_bytes)
+  void *output,
+  const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+  struct gnatcov_rts_string program_name, uint64_t exec_date,
+  struct gnatcov_rts_string user_data,
+  gnatcov_rts_write_bytes_callback write_bytes)
 {
-  unsigned i;
+  const struct gnatcov_rts_coverage_buffers_group *group;
+  unsigned i_group, i_buffer;
 
-  info_entry program_name_entry;
+  struct info_entry program_name_entry;
   program_name_entry.length = program_name.length;
   program_name_entry.data = program_name.str;
 
-  info_entry user_data_entry;
+  struct info_entry user_data_entry;
   user_data_entry.length = user_data.length;
   user_data_entry.data = user_data.str;
 
-  info_entry end_entry;
+  struct info_entry end_entry;
   end_entry.length = 0;
 
   write_header (write_bytes, output);
   write_info (write_bytes, output, GNATCOV_RTS_INFO_PROGRAM_NAME,
-              &program_name_entry);
+	      &program_name_entry);
   write_date (write_bytes, output, exec_date);
   write_info (write_bytes, output, GNATCOV_RTS_INFO_USER_DATA,
-              &user_data_entry);
+	      &user_data_entry);
   write_info (write_bytes, output, GNATCOV_RTS_INFO_END, &end_entry);
 
-  for (i = 0; i < buffers->length; i++)
-    write_entry (write_bytes, output, buffers->buffers[i]);
+  for (i_group = 0; i_group < buffers_groups->length; ++i_group)
+    {
+      group = buffers_groups->groups[i_group];
+      for (i_buffer = 0; i_buffer < group->length; ++i_buffer)
+	write_entry (write_bytes, output, group->buffers[i_buffer]);
+    }
 
   return 0;
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces-output.h b/tools/gnatcov/rts/gnatcov_rts_c-traces-output.h
index eda9ae23e..939ec31c8 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces-output.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces-output.h
@@ -21,25 +21,28 @@
 #include "gnatcov_rts_c_strings.h"
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
-/* Callback for trace writing routines. Write the N bytes starting at SOURCE to
-   the OUTPUT stream (OUTPUT is just forwarded from
-   gnatcov_rts_generic_write_trace_file).  Return 0 if the write was
-   successful and return any non-zero value in case of error.  */
-typedef int (*gnatcov_rts_write_bytes_callback)
-   (void *output, const void *source, unsigned n);
+  /* Callback for trace writing routines. Write the N bytes starting at SOURCE
+     to the OUTPUT stream (OUTPUT is just forwarded from
+     gnatcov_rts_generic_write_trace_file).  Return 0 if the write was
+     successful and return any non-zero value in case of error.  */
+  typedef int (*gnatcov_rts_write_bytes_callback) (void *output,
+						   const void *source,
+						   unsigned n);
 
-/* Write a trace file to contain the given coverage BUFFERS to the OUTPUT
-   stream using the WRITE_BYTES callback.  PROGRAM_NAME, EXEC_DATE and
-   USER_DATA are included as metadata in the trace file.  Return 0 if the write
-   was successful, and return any non-zero value in case of error.  */
-extern int gnatcov_rts_generic_write_trace_file (
-  void *output, const gnatcov_rts_unit_coverage_buffers_array *buffers,
-  gnatcov_rts_string program_name, uint64_t exec_date,
-  gnatcov_rts_string user_data,
-  gnatcov_rts_write_bytes_callback write_bytes);
+  /* Write a trace file to contain the given coverage BUFFERS_GROUPS to the
+     OUTPUT stream using the WRITE_BYTES callback.  PROGRAM_NAME, EXEC_DATE and
+     USER_DATA are included as metadata in the trace file.  Return 0 if the
+     write was successful, and return any non-zero value in case of error.  */
+  extern int gnatcov_rts_generic_write_trace_file (
+    void *output,
+    const struct gnatcov_rts_coverage_buffers_group_array *buffers_groups,
+    struct gnatcov_rts_string program_name, uint64_t exec_date,
+    struct gnatcov_rts_string user_data,
+    gnatcov_rts_write_bytes_callback write_bytes);
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c-traces.h b/tools/gnatcov/rts/gnatcov_rts_c-traces.h
index f38a1d653..386066ab1 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c-traces.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c-traces.h
@@ -23,14 +23,16 @@
 #include <stdint.h>
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
 /* Expected value of the `trace_file_header.format_version` field.
 
    0 -- initial version
-   1 -- extend trace entry model to account for C files */
-#define GNATCOV_RTS_CURRENT_VERSION 1
+   1 -- extend trace entry model to account for C files
+   2 -- introduce fingerprints for bit maps  */
+#define GNATCOV_RTS_CURRENT_VERSION 2
 
 /* LSB_First_Bytes: bit buffers are encoded as sequences of bytes.
 
@@ -44,104 +46,110 @@ extern "C" {
      significant bit maps to bit 8 * Y + 7.  */
 #define GNATCOV_RTS_LSB_FIRST_BYTES 0
 
-enum
-{
-  GNATCOV_RTS_LITTLE_ENDIAN,
-  GNATCOV_RTS_BIG_ENDIAN
-};
+  enum
+  {
+    GNATCOV_RTS_LITTLE_ENDIAN,
+    GNATCOV_RTS_BIG_ENDIAN
+  };
 
-/* Return the native endianity, 0 for little-endian, 1 for big-endian.  */
-extern unsigned gnatcov_rts_native_endianity (void);
+  /* Return the native endianity, 0 for little-endian, 1 for big-endian.  */
+  extern unsigned gnatcov_rts_native_endianity (void);
 
-struct trace_file_header
-{
-  char magic[32];
+  struct trace_file_header
+  {
+    char magic[32];
 
-  uint32_t format_version;
-  uint8_t alignment;
-  uint8_t endianity;
+    uint32_t format_version;
+    uint8_t alignment;
+    uint8_t endianity;
 
-  /* Padding used only to make the size of the trace file header a
-     multiple of 8 bytes.  Must be zero.  */
-  uint16_t padding;
-};
+    /* Padding used only to make the size of the trace file header a
+       multiple of 8 bytes.  Must be zero.  */
+    uint16_t padding;
+  };
 
-/*********************/
-/* Trace information */
-/*********************/
+  /*********************/
+  /* Trace information */
+  /*********************/
 
-enum
-{
+  enum
+  {
 
-  /* Special trace info entry: indicates the end of a sequence of entries. No
-     data is associated to this trace info entry.  */
-  GNATCOV_RTS_INFO_END = 0,
+    /* Special trace info entry: indicates the end of a sequence of entries. No
+       data is associated to this trace info entry.  */
+    GNATCOV_RTS_INFO_END = 0,
 
-  /* Name of the program that produced this trace.  */
-  GNATCOV_RTS_INFO_PROGRAM_NAME = 1,
+    /* Name of the program that produced this trace.  */
+    GNATCOV_RTS_INFO_PROGRAM_NAME = 1,
 
-  /* Date for the program execution that produced this trace.  */
-  GNATCOV_RTS_INFO_EXEC_DATE = 2,
+    /* Date for the program execution that produced this trace.  */
+    GNATCOV_RTS_INFO_EXEC_DATE = 2,
 
-  /* Arbitrary storage for user data. This is exposed to users as the trace
-     "tag".  */
-  GNATCOV_RTS_INFO_USER_DATA = 3
-};
+    /* Arbitrary storage for user data. This is exposed to users as the trace
+       "tag".  */
+    GNATCOV_RTS_INFO_USER_DATA = 3
+  };
 
-struct trace_info_header
-{
-  uint32_t kind;
-  uint32_t length;
-};
+  struct trace_info_header
+  {
+    uint32_t kind;
+    uint32_t length;
+  };
 
-/* Trace entry header
+  /* Trace entry header
 
-Each trace entry starts with the following header. Then goes:
+     Each trace entry starts with the following header. Then goes:
 
- * The name of the unit describes. It is NUL-padded according to the
-   trace file alignment.
+     * The name of the unit describes. It is NUL-padded according to the trace
+       file alignment.
 
- * The statement coverage buffer. It is also NUL-padded.
+     * The statement coverage buffer. It is also NUL-padded.
 
- * The decision coverage buffer. It is also NUL-padded.
+     * The decision coverage buffer. It is also NUL-padded.
 
- * The MC/DC coverage buffer. It is also NUL-padded.  */
+     * The MC/DC coverage buffer. It is also NUL-padded.  */
 
-struct trace_entry_header
-{
-  /* Length of the unit name / filename for the unit this trace entry
-     describes.  */
-  uint32_t unit_name_length;
-
-  /* For file-based languages, length of the project name this file belongs to.
-     For unit-based languages, the unit name is unique so this piece of
-     information is not needed (and thus will be 0).  */
-  uint32_t project_name_length;
-
-  /* Number of bits in the statement, decision and MC/DC coverage buffers.  */
-  uint32_t statement_bit_count;
-  uint32_t decision_bit_count;
-  uint32_t mcdc_bit_count;
-
-  /* Language kind for this unit */
-  uint8_t language_kind;
-
-  /* Part of the unit this trace entry describes. `not_applicable_part` for
-     file-based languages.  */
-  uint8_t unit_part;
-
-  /* Encoding used to represent statement and decision coverage buffers.  */
-  uint8_t bit_buffer_encoding;
-
-  /* Hash of SCO info for this unit. Used as a fast way to check that coverage
-     obligations and coverage data are consistent. Specific hash values are
-     computed during instrumentation.  */
-  uint8_t fingerprint[20];
-
-  /* Padding used only to make the size of this trace entry header a multiple
-     of 8 bytes. Must be zero.  */
-  uint8_t padding[5];
-};
+  struct trace_entry_header
+  {
+    /* Length of the unit name / filename for the unit this trace entry
+       describes.  */
+    uint32_t unit_name_length;
+
+    /* For file-based languages, length of the project name this file belongs
+       to. For unit-based languages, the unit name is unique so this piece of
+       information is not needed (and thus will be 0).  */
+    uint32_t project_name_length;
+
+    /* Number of bits in the statement, decision and MC/DC coverage buffers. */
+    uint32_t statement_bit_count;
+    uint32_t decision_bit_count;
+    uint32_t mcdc_bit_count;
+
+    /* Language kind for this unit */
+    uint8_t language_kind;
+
+    /* Part of the unit this trace entry describes. `not_applicable_part` for
+       file-based languages.  */
+    uint8_t unit_part;
+
+    /* Encoding used to represent statement and decision coverage buffers.  */
+    uint8_t bit_buffer_encoding;
+
+    /* Hash of SCO info for this unit. Used as a fast way to check that
+       coverage obligations and coverage data are consistent. Specific hash
+       values are computed during instrumentation.  */
+    uint8_t fingerprint[20];
+
+    /* Hash of buffer bit mappings for this unit, as gnatcov computes it (see
+       SC_Obligations).  Used as a fast way to check that gnatcov will be able
+       to interpret buffer bits from a source traces using buffer bit mappings
+       from SID files.  */
+    uint8_t bit_maps_fingerprint[20];
+
+    /* Padding used only to make the size of this trace entry header a multiple
+       of 8 bytes. Must be zero.  */
+    uint8_t padding[1];
+  };
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/rts/gnatcov_rts_c_strings.h b/tools/gnatcov/rts/gnatcov_rts_c_strings.h
index 22b2ce0d2..0e9d6b8cc 100644
--- a/tools/gnatcov/rts/gnatcov_rts_c_strings.h
+++ b/tools/gnatcov/rts/gnatcov_rts_c_strings.h
@@ -23,18 +23,20 @@
 #include <stddef.h>
 
 #ifdef __cplusplus
-extern "C" {
+extern "C"
+{
 #endif
 
-typedef struct gnatcov_rts_string
-{
-  const char *str;
-  size_t length;
-} gnatcov_rts_string;
+  struct gnatcov_rts_string
+  {
+    const char *str;
+    size_t length;
+  };
 
 /* Converts a string litteral to the corresponding gnatcov_rts_string struct
    value.  */
-#define STR(x) (struct gnatcov_rts_string) { x, sizeof (x) - 1 }
+#define STR(x)                                                                \
+  (struct gnatcov_rts_string) { x, sizeof (x) - 1 }
 
 #ifdef __cplusplus
 }
diff --git a/tools/gnatcov/sc_obligations.adb b/tools/gnatcov/sc_obligations.adb
index 3d962445e..11ae26d4d 100644
--- a/tools/gnatcov/sc_obligations.adb
+++ b/tools/gnatcov/sc_obligations.adb
@@ -96,7 +96,7 @@ package body SC_Obligations is
       Has_Code : Boolean := False;
       --  Set True when object code for some source file in this unit is seen
 
-      Fingerprint : SCOs_Hash;
+      Fingerprint : Fingerprint_Type;
       --  Hash of SCO info in ALI, for incremental coverage consistency check
 
       PP_Info_Map : SCO_PP_Info_Maps.Map;
@@ -112,6 +112,9 @@ package body SC_Obligations is
          when Instrumenter =>
             Bit_Maps : CU_Bit_Maps;
             --  Mapping of bits in coverage buffers to SCOs
+
+            Bit_Maps_Fingerprint : Fingerprint_Type;
+            --  Hash of Bit_Maps, for consistency checks with source traces
       end case;
 
    end record;
@@ -268,7 +271,7 @@ package body SC_Obligations is
    function Allocate_CU
      (Provider            : SCO_Provider;
       Origin, Main_Source : Source_File_Index;
-      Fingerprint         : SCOs_Hash;
+      Fingerprint         : Fingerprint_Type;
       Created_Units       : in out Created_Unit_Maps.Map) return CU_Id;
    --  If there is already a compilation unit for the given Main_Source that is
    --  not in Created_Units, emit a warning to say the we ignore duplicate SCOs
@@ -323,12 +326,14 @@ package body SC_Obligations is
       State         : in out CU_Load_State;
       Ignored_Slocs : in out Ignored_Slocs_Sets.Set;
       SCO_Map       : access LL_HL_SCO_Map := null;
-      Count_Paths   : Boolean);
+      Count_Paths   : Boolean;
+      Provider      : SCO_Provider);
    --  Load the low level SCO at SCO_Index into our Internal table, to be part
    --  of the CU compilation unit.
    --
    --  Use and update State according to the semantics of its members. See
-   --  Process_Low_Level_SCOs for the semantics of SCO_Map.
+   --  Process_Low_Level_SCOs for the semantics of SCO_Map, Count_Paths and
+   --  Provider.
 
    -------------------------------
    -- Main SCO descriptor table --
@@ -1156,6 +1161,25 @@ package body SC_Obligations is
          end;
       end loop;
 
+      --  If we have information about non instrumented units, import them as
+      --  is. Only import SCOs that belong to this CU, as others SCOs might
+      --  belong to a CU already present in the current execution, and which
+      --  would not be simply imported as is.
+
+      if not Version_Less (S, Than => 9) then
+         for SCO of CP_Vectors.Non_Instr_SCOs loop
+            if SCO in CP_CU.First_SCO .. CP_CU.Last_SCO then
+               Non_Instr_SCOs.Insert (Remap_SCO_Id (Relocs, SCO));
+            end if;
+         end loop;
+
+         for SCO of CP_Vectors.Non_Instr_MCDC_SCOs loop
+            if SCO in CP_CU.First_SCO .. CP_CU.Last_SCO then
+               Non_Instr_MCDC_SCOs.Insert (Remap_SCO_Id (Relocs, SCO));
+            end if;
+         end loop;
+      end if;
+
       --  Perform final fixups and insert CU
 
       CP_CU.Last_Instance :=
@@ -1181,28 +1205,6 @@ package body SC_Obligations is
       if CLS.Purpose = Instrumentation then
          Coverage.Source.Initialize_SCI_For_Instrumented_CU (New_CU_Id);
       end if;
-
-      if Version_Less (S, Than => 9) then
-         return;
-      end if;
-
-      --  If we have information about non instrumented units, import them as
-      --  is. Only import SCOs that belong to this CU, as others SCOs might
-      --  belong to a CU already present in the current execution, and which
-      --  would not be simply imported as is.
-
-      for SCO of CP_Vectors.Non_Instr_SCOs loop
-         if SCO in CP_CU.First_SCO .. CP_CU.Last_SCO then
-            Non_Instr_SCOs.Insert (Remap_SCO_Id (Relocs, SCO));
-         end if;
-      end loop;
-
-      for SCO of CP_Vectors.Non_Instr_MCDC_SCOs loop
-         if SCO in CP_CU.First_SCO .. CP_CU.Last_SCO then
-            Non_Instr_MCDC_SCOs.Insert (Remap_SCO_Id (Relocs, SCO));
-         end if;
-      end loop;
-
    end Checkpoint_Load_New_Unit;
 
    --------------------------
@@ -1288,13 +1290,31 @@ package body SC_Obligations is
                      & Provider_Image (CP_CU.Provider)
                      & " and from " & Provider_Image (CU_Record.Provider));
 
-            --  Ignore also when the fingerprint does not match
-
-            elsif CP_CU.Fingerprint /= CU_Record.Fingerprint then
+            --  Ignore also when the fingerprints do not match.
+            --
+            --  Note that only recent enough SID files contain buffer bit maps
+            --  and their fingerprints. Bit_Maps_Fingerprint is left to
+            --  No_Fingerprint for checkpoints. Skip the consistency check for
+            --  these cases, and if the loaded CU has these fingerprints,
+            --  record them for later consistency checks.
+
+            elsif CP_CU.Fingerprint /= CU_Record.Fingerprint
+                    or else
+                  (CP_CU.Provider = Instrumenter
+                   and then CP_CU.Bit_Maps_Fingerprint /= No_Fingerprint
+                   and then CU_Record.Bit_Maps_Fingerprint /= No_Fingerprint
+                   and then CP_CU.Bit_Maps_Fingerprint
+                            /= CU_Record.Bit_Maps_Fingerprint)
+            then
                Warn ("unexpected fingerprint, cannot merge coverage"
                      & " information for " & CU_Image);
 
             else
+               if CU_Record.Provider = Instrumenter
+                  and then CU_Record.Bit_Maps_Fingerprint = No_Fingerprint
+               then
+                  CU_Record.Bit_Maps_Fingerprint := CP_CU.Bit_Maps_Fingerprint;
+               end if;
                Checkpoint_Load_Merge_Unit
                  (CLS,
                   CP_CU      => CP_CU,
@@ -1373,7 +1393,7 @@ package body SC_Obligations is
       Inst_Id'Read           (S, V.Last_Instance);
       SFI_Vector'Read        (S, V.Deps);
       Boolean'Read           (S, V.Has_Code);
-      SCOs_Hash'Read         (S, V.Fingerprint);
+      Fingerprint_Type'Read  (S, V.Fingerprint);
 
       --  Checkpoint version 8 preprocessing information
 
@@ -1381,23 +1401,37 @@ package body SC_Obligations is
          SCO_PP_Info_Maps.Map'Read (S, V.PP_Info_Map);
       end if;
 
-      --  Checkpoint version 2 data (instrumentation support)
+      case V.Provider is
+         when Compiler =>
+            null;
+         when Instrumenter =>
 
-      if not Version_Less (S, Than => 2) then
-         case V.Provider is
-            when Compiler =>
-               null;
-            when Instrumenter =>
-               if Purpose_Of (S) = Instrumentation then
-                  V.Bit_Maps.Statement_Bits :=
-                    new Statement_Bit_Map'(Statement_Bit_Map'Input (S));
-                  V.Bit_Maps.Decision_Bits :=
-                    new Decision_Bit_Map'(Decision_Bit_Map'Input (S));
-                  V.Bit_Maps.MCDC_Bits :=
-                    new MCDC_Bit_Map'(MCDC_Bit_Map'Input (S));
+            --  Checkpoint version 2 data (instrumentation support)
+
+            --  By default, use "no fingerprint" for buffer bit maps
+            --  fingerprints: either the SID file we load is too old to have
+            --  such fingerprints, either we are loading a checkpoint, and by
+            --  design checkpoints contains neither bit maps nor their
+            --  fingerprints (all source traces are processed before loading
+            --  checkpoints, and bit maps are needed only to interpret source
+            --  traces).
+
+            V.Bit_Maps_Fingerprint := No_Fingerprint;
+
+            if not Version_Less (S, Than => 2)
+               and then Purpose_Of (S) = Instrumentation
+            then
+               V.Bit_Maps.Statement_Bits :=
+                 new Statement_Bit_Map'(Statement_Bit_Map'Input (S));
+               V.Bit_Maps.Decision_Bits :=
+                 new Decision_Bit_Map'(Decision_Bit_Map'Input (S));
+               V.Bit_Maps.MCDC_Bits :=
+                 new MCDC_Bit_Map'(MCDC_Bit_Map'Input (S));
+               if not Version_Less (S, Than => 11) then
+                  Fingerprint_Type'Read (S, V.Bit_Maps_Fingerprint);
                end if;
-         end case;
-      end if;
+            end if;
+      end case;
 
       --  Checkpoint version 8 data (scoped metrics support)
 
@@ -1442,7 +1476,7 @@ package body SC_Obligations is
       Inst_Id'Write           (S, V.Last_Instance);
       SFI_Vector'Write        (S, V.Deps);
       Boolean'Write           (S, V.Has_Code);
-      SCOs_Hash'Write         (S, V.Fingerprint);
+      Fingerprint_Type'Write  (S, V.Fingerprint);
 
       SCO_PP_Info_Maps.Map'Write (S, V.PP_Info_Map);
 
@@ -1457,6 +1491,7 @@ package body SC_Obligations is
                  (S, V.Bit_Maps.Decision_Bits.all);
                MCDC_Bit_Map'Output
                  (S, V.Bit_Maps.MCDC_Bits.all);
+               Fingerprint_Type'Write (S, V.Bit_Maps_Fingerprint);
             end if;
       end case;
 
@@ -1579,7 +1614,7 @@ package body SC_Obligations is
                   Annotation.CU := Remap_CU_Id (Relocs, Annotation.CU);
                   if Annotation.CU > Last_Existing_CU_Id then
                      Remap_SFI (Relocs, Annotation_Sloc.Source_File);
-                     ALI_Annotations.Insert (Annotation_Sloc, Element (Cur));
+                     ALI_Annotations.Insert (Annotation_Sloc, Annotation);
                   end if;
                end if;
             end;
@@ -2578,11 +2613,20 @@ package body SC_Obligations is
    -- Fingerprint --
    -----------------
 
-   function Fingerprint (CU : CU_Id) return SCOs_Hash is
+   function Fingerprint (CU : CU_Id) return Fingerprint_Type is
    begin
       return CU_Vector.Reference (CU).Fingerprint;
    end Fingerprint;
 
+   --------------------------
+   -- Bit_Maps_Fingerprint --
+   --------------------------
+
+   function Bit_Maps_Fingerprint (CU : CU_Id) return Fingerprint_Type is
+   begin
+      return CU_Vector.Reference (CU).Bit_Maps_Fingerprint;
+   end Bit_Maps_Fingerprint;
+
    ---------------
    -- Load_SCOs --
    ---------------
@@ -2621,6 +2665,12 @@ package body SC_Obligations is
          for Dep of Deps loop
             if Get_Simple_Name (Dep) = Main_Source_Simple_Name then
                Main_Source := Dep;
+
+               --  Files designated by D lines are loaded as stub files. Here,
+               --  we decide to treat one as a source file, so make sure we
+               --  have registered it as one.
+
+               Consolidate_File_Kind (Main_Source, Source_File);
                exit;
             end if;
          end loop;
@@ -2891,7 +2941,7 @@ package body SC_Obligations is
    function Allocate_CU
      (Provider            : SCO_Provider;
       Origin, Main_Source : Source_File_Index;
-      Fingerprint         : SCOs_Hash;
+      Fingerprint         : Fingerprint_Type;
       Created_Units       : in out Created_Unit_Maps.Map) return CU_Id
    is
       use Created_Unit_Maps;
@@ -2998,7 +3048,8 @@ package body SC_Obligations is
       State         : in out CU_Load_State;
       Ignored_Slocs : in out Ignored_Slocs_Sets.Set;
       SCO_Map       : access LL_HL_SCO_Map := null;
-      Count_Paths   : Boolean)
+      Count_Paths   : Boolean;
+      Provider      : SCO_Provider)
    is
       Unit : CU_Info renames CU_Vector.Reference (CU);
       SCOE : SCOs.SCO_Table_Entry renames SCOs.SCO_Table.Table (SCO_Index);
@@ -3220,7 +3271,18 @@ package body SC_Obligations is
                     (Kind                => Decision,
                      Origin              => CU,
                      Control_Location    =>
-                       Slocs.To_Sloc (Unit.Main_Source, From_Sloc),
+
+                     --  Control locations are only useful for dominance
+                     --  markers, which are only used with binary traces. As
+                     --  it is impractical to get the correct location with
+                     --  the C/C++ instrumenter, and as using incorrect slocs
+                     --  can create conflicts, ignore those in the
+                     --  instrumentation case.
+
+                       (if Provider = Compiler
+                        then Slocs.To_Sloc (Unit.Main_Source, From_Sloc)
+                        else No_Location),
+
                      D_Kind              => To_Decision_Kind (SCOE.C1),
                      Last_Cond_Index     => 0,
                      Aspect_Name         =>
@@ -3332,7 +3394,7 @@ package body SC_Obligations is
             First_SCO      : constant SCO_Id := SCO_Vector.Last_Index + 1;
             First_Instance : constant Inst_Id := Inst_Vector.Last_Index + 1;
 
-            Fingerprint : constant SCOs_Hash := SCOs_Hash
+            Fingerprint : constant Fingerprint_Type := Fingerprint_Type
               (GNAT.SHA1.Binary_Message_Digest'
                  (GNAT.SHA1.Digest (Info.Fingerprint_Context)));
 
@@ -3365,7 +3427,8 @@ package body SC_Obligations is
                      State,
                      Ignored_Slocs_Set,
                      SCO_Map,
-                     Count_Paths);
+                     Count_Paths,
+                     Provider);
                end loop;
             end loop;
 
@@ -4125,8 +4188,36 @@ package body SC_Obligations is
    ------------------
 
    procedure Set_Bit_Maps (CU : CU_Id; Bit_Maps : CU_Bit_Maps) is
+      use GNAT.SHA1;
+
+      Info : CU_Info renames CU_Vector.Reference (CU);
+      Ctx : GNAT.SHA1.Context;
+      LF  : constant String := (1 => ASCII.LF);
    begin
-      CU_Vector.Reference (CU).Bit_Maps := Bit_Maps;
+      Info.Bit_Maps := Bit_Maps;
+
+      --  Compute the fingerprint for these bit maps
+
+      Update (Ctx, "stmt:");
+      for Id of Bit_Maps.Statement_Bits.all loop
+         Update (Ctx, Id'Image);
+      end loop;
+      Update (Ctx, LF);
+
+      Update (Ctx, "dc:");
+      for D of Bit_Maps.Decision_Bits.all loop
+         Update (Ctx, D.D_SCO'Image & ":" & D.Outcome'Image);
+      end loop;
+      Update (Ctx, LF);
+
+      Update (Ctx, "mcdc:");
+      for M of Bit_Maps.MCDC_Bits.all loop
+         Update (Ctx, M.D_SCO'Image & ":" & M.Path_Index'Image);
+      end loop;
+      Update (Ctx, LF);
+
+      Info.Bit_Maps_Fingerprint := Fingerprint_Type
+        (GNAT.SHA1.Binary_Message_Digest'(GNAT.SHA1.Digest (Ctx)));
    end Set_Bit_Maps;
 
    ----------------------
diff --git a/tools/gnatcov/sc_obligations.ads b/tools/gnatcov/sc_obligations.ads
index d0723212e..9bd3ccd80 100644
--- a/tools/gnatcov/sc_obligations.ads
+++ b/tools/gnatcov/sc_obligations.ads
@@ -92,14 +92,20 @@ package SC_Obligations is
    No_CU_Id : constant CU_Id := 0;
    subtype Valid_CU_Id is CU_Id range No_CU_Id + 1 .. CU_Id'Last;
 
+   package CU_Id_Vectors is new Ada.Containers.Vectors (Positive, CU_Id);
+
    function Provider (CU : CU_Id) return SCO_Provider;
    --  Return the SCO provider corresponding to the given compilation unit
 
-   type SCOs_Hash is new GNAT.SHA1.Binary_Message_Digest;
+   type Fingerprint_Type is new GNAT.SHA1.Binary_Message_Digest;
+   No_Fingerprint : constant Fingerprint_Type := (others => 0);
 
-   function Fingerprint (CU : CU_Id) return SCOs_Hash;
+   function Fingerprint (CU : CU_Id) return Fingerprint_Type;
    --  Hash of SCO info in ALI, for incremental coverage consistency check
 
+   function Bit_Maps_Fingerprint (CU : CU_Id) return Fingerprint_Type;
+   --  Hash of buffer bit mappings in CU
+
    function Comp_Unit (Src_File : Source_File_Index) return CU_Id;
    --  Return the identifier for the compilation unit containing the given
    --  source, or No_CU_Id if no such LI file has been loaded.
diff --git a/tools/gnatcov/setup_rts.adb b/tools/gnatcov/setup_rts.adb
index 49d53c72d..247a607f8 100644
--- a/tools/gnatcov/setup_rts.adb
+++ b/tools/gnatcov/setup_rts.adb
@@ -28,7 +28,7 @@ with GNAT.Regexp;
 with GNATCOLL.JSON;     use GNATCOLL.JSON;
 with GNATCOLL.VFS;      use GNATCOLL.VFS;
 with GPR2.Context;
-with GPR2.KB;
+with GPR2.Containers;
 with GPR2.Log;
 with GPR2.Path_Name;
 with GPR2.Project.Attribute;
@@ -64,20 +64,18 @@ package body Setup_RTS is
    Ravenscar_RTS_Regexp : constant GNAT.Regexp.Regexp :=
      GNAT.Regexp.Compile (".*(ravenscar|light-tasking|embedded).*");
 
-   function Create_Config
-     (Target       : String;
+   procedure Load_Project
+     (Project      : in out GPR2.Project.Tree.Object;
+      Project_File : String;
+      Target       : String;
       RTS          : String;
       Config_File  : String;
-      Project_File : String) return GPR2.Project.Configuration.Object;
-   --  Create a GPR2 configuration for C, and for Ada if it is enabled, both
-   --  for the given target/RTS. If Target is empty, use the knowledge base's
-   --  default target.
-   --
-   --  If Config_File is not an empty string, disregard Target/RTS and load the
-   --  configuration from that file instead.
+      Context      : GPR2.Context.Object := GPR2.Context.Empty);
+   --  Load Project_File for the given Target/RTS/Config_File/Context.
    --
-   --  In case of error, GPR2 uses Project_File as context for the error
-   --  messages.
+   --  This does not raise an exception in case of project loading failure:
+   --  callers are supposed to check Project.Log_Messages to handle possible
+   --  errors.
 
    function Setup_Project
      (Temp_Dir     : Temporary_Directory;
@@ -172,61 +170,74 @@ package body Setup_RTS is
    --  project to install. Use a filename that will match the Install'Artifacts
    --  attribute in the runtime project file.
 
-   -------------------
-   -- Create_Config --
-   -------------------
+   ------------------
+   -- Load_Project --
+   ------------------
 
-   function Create_Config
-     (Target       : String;
+   procedure Load_Project
+     (Project      : in out GPR2.Project.Tree.Object;
+      Project_File : String;
+      Target       : String;
       RTS          : String;
       Config_File  : String;
-      Project_File : String) return GPR2.Project.Configuration.Object is
+      Context      : GPR2.Context.Object := GPR2.Context.Empty)
+   is
+      PF : constant GPR2.Path_Name.Object :=
+        GPR2.Path_Name.Create_File
+           (GPR2.Filename_Type (Project_File), GPR2.Path_Name.No_Resolution);
    begin
+      --  According to the documentation, an error message is recorded in
+      --  Project when Load and Load_Autoconf raise GPR2.Project_Error
+      --  exceptions, so we can just ignore this exception and let callers deal
+      --  with errors recorded in Project.Log_Messages.
+
       --  If a configuration project file is provided, just load it to create
       --  the configuration.
 
       if Config_File /= "" then
          declare
-            F : constant GPR2.Path_Name.Object :=
+            F      : constant GPR2.Path_Name.Object :=
               GPR2.Path_Name.Create_File (GPR2.Filename_Type (Config_File));
+            Config : constant GPR2.Project.Configuration.Object :=
+              GPR2.Project.Configuration.Load (F);
          begin
-            return GPR2.Project.Configuration.Load (F);
+            Project.Load
+              (Filename => PF,
+               Context  => Context,
+               Config   => Config);
+         exception
+            when GPR2.Project_Error =>
+               null;
          end;
-      end if;
-
-      --  Otherwise, use the GPR knowledge base to create a configuration for C
-      --  (needed for the core coverage runtime) and for the Ada language, if
-      --  enabled.
-
-      declare
-         Actual_Target : constant GPR2.Name_Type :=
-           (if Target = ""
-            then GPR2.KB.Default_Target
-            else GPR2.Name_Type (Target));
 
-         KB : GPR2.KB.Object := GPR2.KB.Create_Default (GPR2.KB.Default_Flags);
+      --  Otherwise, use the "auto configuration" mode that will use the
+      --  optional target and RTS information. Always require C (needed for the
+      --  core coverage runtime) and require the Ada language only if enabled.
 
-         Descs : GPR2.Project.Configuration.Description_Set (1 .. 2);
-         Last  : Positive := 1;
-      begin
-         Descs (1) := GPR2.Project.Configuration.Create
-           (Language => GPR2.C_Language,
-            Runtime  => GPR2.Optional_Name_Type (RTS));
-         if Src_Enabled_Languages (Ada_Language) then
-            Descs (2) := GPR2.Project.Configuration.Create
-              (Language => GPR2.Ada_Language,
-               Runtime  => GPR2.Optional_Name_Type (RTS));
-            Last := Last + 1;
-         end if;
+      else
+         declare
+            RTS_Map       : GPR2.Containers.Lang_Value_Map;
+            Actual_Target : constant GPR2.Optional_Name_Type :=
+              GPR2.Optional_Name_Type (Target);
+         begin
+            RTS_Map.Include (GPR2.C_Language, RTS);
+            if Src_Enabled_Languages (Ada_Language) then
+               RTS_Map.Include (GPR2.Ada_Language, RTS);
+            end if;
 
-         return GPR2.Project.Configuration.Create
-           (Settings => Descs (1 .. Last),
-            Target   => Actual_Target,
-            Project  => GPR2.Path_Name.Create_File
-                          (GPR2.Filename_Type (Project_File)),
-            Base     => KB);
-      end;
-   end Create_Config;
+            begin
+               Project.Load_Autoconf
+                 (Filename          => PF,
+                  Context           => Context,
+                  Target            => Actual_Target,
+                  Language_Runtimes => RTS_Map);
+            exception
+               when GPR2.Project_Error =>
+                  null;
+            end;
+         end;
+      end if;
+   end Load_Project;
 
    -----------
    -- Image --
@@ -411,24 +422,7 @@ package body Setup_RTS is
 
       --  Now load the project
 
-      begin
-         Prj.Load
-           (Filename => GPR2.Project.Create
-                          (GPR2.Filename_Type (Project_File),
-                           GPR2.Project.Default_Search_Paths (True)),
-            Context  => Ctx,
-            Config   => Create_Config
-                          (Target, RTS, Config_File, Project_File));
-      exception
-         when GPR2.Project_Error =>
-
-            --  According to the documentation for this exception, an error
-            --  message is recorded in Prj when it is raised, so we can just
-            --  ignore the exception and let the processing below take care of
-            --  the error.
-
-            null;
-      end;
+      Load_Project (Prj, Project_File, Target, RTS, Config_File, Ctx);
 
       --  Print all messages in verbose mode, and all but the Information ones
       --  otherwise. Abort on error, or if we failed to get a runtime project
@@ -816,33 +810,31 @@ package body Setup_RTS is
       Result            : Setup_Config := Default_Setup_Config;
       Setup_Config_File : Virtual_File;
 
+      Prj_Ext      : constant String := ".gpr";
+      Prj_Filename : constant String :=
+        (if not Has_Suffix (Runtime_Project, Prj_Ext)
+            and then Exists (Runtime_Project & Prj_Ext)
+         then Runtime_Project & Prj_Ext
+         else Runtime_Project);
+      --  Name of the runtime project file to load it with GPR2.
+      --
+      --  TODO??? (V921-006) While the call to Prj.Load below is responsible
+      --  for resolving GPR file base names to full names, it is not able to
+      --  append the ".gpr" extension when missing, whereas command-line tools
+      --  are supposed to accept this case. Add ".gpr" ourselves when needed
+      --  for now, and remove this special case when GPR2 is able to do it
+
       Prj : GPR2.Project.Tree.Object;
 
       Project_File : Virtual_File;
+      --  Name of the runtime project file after it has been loaded
+      --  (i.e. resolved file name).
    begin
       --  Load the runtime project file, only to locate the prefix where it has
       --  been installed: do not print any error message, and if the project
       --  fails to load, just return the default setup config.
 
-      begin
-         Prj.Load
-           (Filename => GPR2.Project.Create
-                          (GPR2.Filename_Type (Runtime_Project),
-                           GPR2.Project.Default_Search_Paths (True)),
-            Context  => GPR2.Context.Empty,
-            Config   => Create_Config
-                          (Target, RTS, Config_File, Runtime_Project));
-      exception
-         when GPR2.Project_Error =>
-
-            --  According to the documentation for this exception, an error
-            --  message is recorded in Prj when it is raised, so we can just
-            --  ignore the exception and let the processing below take care of
-            --  the error.
-
-            null;
-      end;
-
+      Load_Project (Prj, Prj_Filename, Target, RTS, Config_File);
       if Prj.Log_Messages.Has_Error then
          if Verbose then
             Put_Line
diff --git a/tools/gnatcov/switches.ads b/tools/gnatcov/switches.ads
index aad4ea843..7d8cffc65 100644
--- a/tools/gnatcov/switches.ads
+++ b/tools/gnatcov/switches.ads
@@ -129,6 +129,9 @@ package Switches is
    Units_Inputs : Inputs.Inputs_Type;
    --  List of names for requested units of interest
 
+   C_Opts   : Inputs.Inputs_Type;
+   CPP_Opts : Inputs.Inputs_Type;
+
    type Any_Language is
      (All_Languages, Ada_Language, C_Language, CPP_Language);
    --  A language that is supported for source coverage All_Languages is a
diff --git a/tools/gnatcov/trace_adapters/dynamorio/CMakeLists.txt b/tools/gnatcov/trace_adapters/dynamorio/CMakeLists.txt
index 85a7ad1e5..44adfaf39 100644
--- a/tools/gnatcov/trace_adapters/dynamorio/CMakeLists.txt
+++ b/tools/gnatcov/trace_adapters/dynamorio/CMakeLists.txt
@@ -3,10 +3,12 @@
 # cmake -DDynamoRIO_DIR=$PWD/../dynamorio/cmake ../dr-gnatcov
 # make
 # ../dynamorio/bin64/drrun -client libqtrace.so 0 -- /bin/ls
-
-cmake_minimum_required(VERSION 2.6)
+cmake_minimum_required(VERSION 3.15)
+project(qtrace C)
 
 add_library(qtrace SHARED qtrace.c)
+target_link_libraries(qtrace)
+
 find_package(DynamoRIO)
 if (NOT DynamoRIO_FOUND)
   message(FATAL_ERROR "DynamoRIO package required to build")
diff --git a/tools/gnatcov/trace_adapters/dynamorio/Makefile b/tools/gnatcov/trace_adapters/dynamorio/Makefile
index 4f2637050..86e8f163f 100644
--- a/tools/gnatcov/trace_adapters/dynamorio/Makefile
+++ b/tools/gnatcov/trace_adapters/dynamorio/Makefile
@@ -7,11 +7,7 @@ DYNRIO_CLIENT=INVALID
 
 ifeq ($(HOST_OS), windows)
 DYNRIO_CLIENT=build/$(DYNRIO_CONFIG)/qtrace.dll
-ifeq ($(BITS_ARCH), 32)
-G_OPTION=-G"Visual Studio 12"
-else
-G_OPTION=-G"Visual Studio 12 Win64"
-endif
+G_OPTION=-G"Visual Studio 17 2022"
 endif
 
 ifeq ($(HOST_OS), linux)
@@ -29,8 +25,8 @@ all: $(DYNRIO_CLIENT)
 
 $(DYNRIO_CLIENT):
 	mkdir build || true
-	cd build && cmake -DDynamoRIO_DIR=$(DYNRIO_PREFIX)/cmake $(G_OPTION) ..
-	cd build && cmake --build . --config $(DYNRIO_CONFIG)
+	cd build && cmake -Wno-dev -DDynamoRIO_DIR=$(DYNRIO_PREFIX)/cmake $(G_OPTION) ..
+	cd build && cmake --build . --config $(DYNRIO_CONFIG) -v 
 
 ################
 # Installation #
diff --git a/tools/gnatcov/traces_source.ads b/tools/gnatcov/traces_source.ads
index 1227a60b7..230867cd4 100644
--- a/tools/gnatcov/traces_source.ads
+++ b/tools/gnatcov/traces_source.ads
@@ -50,11 +50,12 @@ package Traces_Source is
    --  trace entries start and end on byte/half word/word/long word boundaries.
 
    type Trace_File_Format_Version is new Unsigned_32;
-   Current_Version : Trace_File_Format_Version := 1;
+   Current_Version : Trace_File_Format_Version := 2;
    --  Expected value of the Trace_File_Header.Format_Version field.
    --
    --  0 -- initial version
    --  1 -- extend trace entry model to account for C files
+   --  2 -- introduce fingerprints for bit maps
 
    type Any_Alignment is new Unsigned_8;
    subtype Supported_Alignment is Any_Alignment;
@@ -244,29 +245,36 @@ package Traces_Source is
       Bit_Buffer_Encoding : Any_Bit_Buffer_Encoding;
       --  Encoding used to represent statement and decision coverage buffers
 
-      Fingerprint : Buffers.SCOs_Hash;
+      Fingerprint : Buffers.Fingerprint_Type;
       --  Hash of SCO info for this unit. Useds a fast way to check that
       --  coverage obligations and coverage data are consistent. Specific hash
       --  values are computed during instrumentation.
 
-      Padding : String (1 .. 5);
+      Bit_Maps_Fingerprint : Buffers.Fingerprint_Type;
+      --  Hash of buffer bit mappings for this unit, as gnatcov computes it
+      --  (see SC_Obligations).  Used as a fast way to check that gnatcov will
+      --  be able to interpret buffer bits from a source traces using buffer
+      --  bit mappings from SID files.
+
+      Padding : String (1 .. 1);
       --  Padding used only to make the size of this trace entry header a
       --  multiple of 8 bytes. Must be zero.
    end record;
 
    for Trace_Entry_Header use record
-      Unit_Name_Length    at  0 range 0 .. 31;
-      Project_Name_Length at  4 range 0 .. 31;
-      Statement_Bit_Count at  8 range 0 .. 31;
-      Decision_Bit_Count  at 12 range 0 .. 31;
-      MCDC_Bit_Count      at 16 range 0 .. 31;
-      Language_Kind       at 20 range 0 .. 7;
-      Unit_Part           at 21 range 0 .. 7;
-      Bit_Buffer_Encoding at 22 range 0 .. 7;
-      Fingerprint         at 23 range 0 .. 20 * 8 - 1;
-      Padding             at 43 range 0 .. 5 * 8 - 1;
+      Unit_Name_Length     at  0 range 0 .. 31;
+      Project_Name_Length  at  4 range 0 .. 31;
+      Statement_Bit_Count  at  8 range 0 .. 31;
+      Decision_Bit_Count   at 12 range 0 .. 31;
+      MCDC_Bit_Count       at 16 range 0 .. 31;
+      Language_Kind        at 20 range 0 .. 7;
+      Unit_Part            at 21 range 0 .. 7;
+      Bit_Buffer_Encoding  at 22 range 0 .. 7;
+      Fingerprint          at 23 range 0 .. 20 * 8 - 1;
+      Bit_Maps_Fingerprint at 43 range 0 .. 20 * 8 - 1;
+      Padding              at 63 range 0 .. 7;
    end record;
 
-   for Trace_Entry_Header'Size use 48 * 8;
+   for Trace_Entry_Header'Size use 64 * 8;
 
 end Traces_Source;
diff --git a/tools/gnatcov/zlib_helpers.c b/tools/gnatcov/zlib_helpers.c
index 85f380e43..aa1905859 100644
--- a/tools/gnatcov/zlib_helpers.c
+++ b/tools/gnatcov/zlib_helpers.c
@@ -26,8 +26,8 @@
    OUT_BUFFER (a byte buffer of size OUT_SIZE) using zlib.  Return whether
    decompression was successful.  */
 int
-gnatcov_zlib_uncompress (char *in_buffer, uint64_t in_size,
-			 char *out_buffer, uint64_t out_size)
+gnatcov_zlib_uncompress (char *in_buffer, uint64_t in_size, char *out_buffer,
+			 uint64_t out_size)
 {
   z_stream strm;
 
@@ -39,8 +39,6 @@ gnatcov_zlib_uncompress (char *in_buffer, uint64_t in_size,
 
   /* Uncompress and then check that uncompressed data consumed exactly OUT_SIZE
      bytes.  */
-  return (inflateInit (&strm) == Z_OK
-	  && inflate (&strm, Z_FINISH) == Z_OK
-	  && inflateEnd (&strm) == Z_OK
-	  && strm.avail_out == 0);
+  return (inflateInit (&strm) == Z_OK && inflate (&strm, Z_FINISH) == Z_OK
+	  && inflateEnd (&strm) == Z_OK && strm.avail_out == 0);
 }
