Skip to content

Commit ea1fd9d

Browse files
committed
Add gnatcoverage instrumentation support
This commit removes the gcov testsuite support, and updates the gnatcoverage support. Makefile now supports code coverage build. It is separated in three steps: (1) Generate the gnatcov runtime sources (2) Instrument the gnatcoll-core code (3) Compile the instrumented gnatcoll-core code In case of gnatcoll-core out-of-tree build, GNATCOLL_OBJECT_ROOT allows an external project to specify the gnatcoll-core root object directory. It is useful for example for out-of-tree instrumented builds, where the instrumented sources have previously been generated. --gnatcov option is supported by run-tests, if GPR_PROJECT_PATH is correctly set, which means having gnatcoll-core and gnatcov_rts dependency. --recompile and --gnatcov can not be used simultaneously. Co-author: Jerome Lambourg <lambourg@adacore.com> Change-Id: Icad570f8b72b8b2d4a7c2de3306b4dcffa4fc638
1 parent ebacf44 commit ea1fd9d

File tree

10 files changed

+178
-215
lines changed

10 files changed

+178
-215
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ lib/
2626
*.deps
2727
*.pyc
2828
/gnat_src
29+
*~
2930

3031
testsuite/debug
3132
testsuite/gcov

Makefile

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ WHICH := which
5555

5656
# check for out-of-tree build
5757
SOURCE_DIR := $(dir $(MAKEFILE_LIST))
58+
59+
# make -f with absolute path to current directory Makefile is in-tree build
60+
ifeq ($(SOURCE_DIR), $(shell pwd)/)
61+
SOURCE_DIR := ./
62+
endif
63+
5864
ifeq ($(SOURCE_DIR),./)
5965
RBD=
6066
GNATCOLL_GPR=gnatcoll.gpr
@@ -83,6 +89,7 @@ PROCESSORS = 0
8389
BUILD_DIR =
8490
ENABLE_SHARED = yes
8591
INTEGRATED = no
92+
GNATCOV =
8693

8794
all: build
8895

@@ -97,11 +104,23 @@ else
97104
LIBRARY_TYPES=static
98105
endif
99106

107+
ifeq ($(GNATCOV), yes)
108+
LIBRARY_TYPES=static
109+
GNATCOV_RTS=gnatcovrts
110+
GNATCOV_BUILD_OPTS=--src-subdirs=gnatcov-instr --implicit-with=gnatcov_rts
111+
GNATCOV_PROJECT_PATH=GPR_PROJECT_PATH=$(CURDIR)/gnatcov_rts/share/gpr:$(GPR_PROJECT_PATH)
112+
else
113+
GNATCOV_RTS=
114+
GNATCOV_BUILD_OPTS=
115+
GNATCOV_PROJECT_PATH=
116+
endif
117+
100118
ifeq ($(INTEGRATED), yes)
101119
integrated_install=/$(NORMALIZED_TARGET)
102120
endif
103121

104122

123+
105124
GPR_VARS=-XGNATCOLL_MMAP=$(GNATCOLL_MMAP) \
106125
-XGNATCOLL_MADVISE=$(GNATCOLL_MADVISE) \
107126
-XGNATCOLL_VERSION=$(GNATCOLL_VERSION) \
@@ -111,8 +130,9 @@ GPR_VARS=-XGNATCOLL_MMAP=$(GNATCOLL_MMAP) \
111130
# Used to pass extra options to GPRBUILD, like -d for instance
112131
GPRBUILD_OPTIONS=
113132

114-
BUILDER=gprbuild -p -m $(GTARGET) $(RBD) -j$(PROCESSORS) $(GPR_VARS) \
115-
$(GPRBUILD_OPTIONS)
133+
BUILDER=$(GNATCOV_PROJECT_PATH) gprbuild -p -m $(GTARGET) $(RBD) \
134+
-j$(PROCESSORS) $(GPR_VARS) \
135+
$(GPRBUILD_OPTIONS) $(GNATCOV_BUILD_OPTS)
116136
INSTALLER=gprinstall -p -f $(GTARGET) $(GPR_VARS) \
117137
$(RBD) --sources-subdir=include/gnatcoll --prefix=$(prefix)$(integrated_install)
118138
CLEANER=gprclean -q $(RBD) $(GTARGET)
@@ -124,9 +144,20 @@ UNINSTALLER=$(INSTALLER) -p -f --install-name=gnatcoll --uninstall
124144

125145
build: $(LIBRARY_TYPES:%=build-%)
126146

127-
build-%:
147+
build-%: $(GNATCOV_RTS)
148+
ifeq ($(GNATCOV), yes)
149+
gnatcov instrument -P $(GNATCOLL_GPR) $(RBD) \
150+
--no-subprojects --level=stmt+decision
151+
endif
128152
$(BUILDER) -XLIBRARY_TYPE=$* -XXMLADA_BUILD=$* -XGPR_BUILD=$* \
129-
$(GPR_VARS) $(GNATCOLL_GPR)
153+
$(GPR_VARS) $(GNATCOLL_GPR) -v
154+
155+
###################
156+
# Instrumentation #
157+
###################
158+
159+
gnatcovrts:
160+
gnatcov setup --prefix=gnatcov_rts
130161

131162
###########
132163
# Install #

gnatcoll.gpr

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,31 @@ project GnatColl is
5555

5656
for Library_Kind use Library_Type;
5757

58+
Read_Only := external ("EXTERNALLY_BUILT", "false");
59+
for Externally_Built use Read_Only;
60+
61+
-- If gnatcoll-core is built out of tree, and is used by an external
62+
-- afterwards, then GNATCOLL_OBJECT_ROOT can be used so gnatcoll.gpr
63+
-- Object_Dir and Library_Dir point to the correct directory.
64+
Object_Root := external ("GNATCOLL_OBJECT_ROOT", "");
65+
5866
case OS is
5967
when "windows" | "osx" =>
6068
-- On MacOS and Windows all object are relocatable by default
6169
-- thus the same object directory can be used
62-
for Object_Dir use "obj/gnatcoll/all";
70+
for Object_Dir use Object_Root & "obj/gnatcoll/all";
6371
when "unix" =>
6472
-- On Unix static-pic and relocatable shared the same objects
6573
case Library_Type is
6674
when "relocatable" | "static-pic" =>
67-
for Object_Dir use "obj/gnatcoll/pic";
75+
for Object_Dir use Object_Root & "obj/gnatcoll/pic";
6876
when "static" =>
69-
for Object_Dir use "obj/gnatcoll/static";
77+
for Object_Dir use Object_Root & "obj/gnatcoll/static";
7078
end case;
7179
end case;
7280

7381
for Library_Dir
74-
use "lib/gnatcoll/" & Project'Library_Kind;
82+
use Object_Root & "lib/gnatcoll/" & Project'Library_Kind;
7583
for Library_Name use Name;
7684

7785
for Languages use ("Ada", "C");

testsuite/drivers/__init__.py

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from e3.testsuite.process import check_call
1313
from e3.testsuite.result import Log, TestStatus
1414

15-
1615
# Root directory of respectively the testsuite and the gnatcoll
1716
# repository.
1817
TESTSUITE_ROOT_DIR = os.path.dirname(
@@ -22,35 +21,20 @@
2221
DEFAULT_TIMEOUT = 5 * 60 # 5 minutes
2322

2423

25-
def make_gnatcoll(work_dir, debug=False, gcov=False, gnatcov=False):
26-
"""Build gnatcoll core with or without gcov instrumentation.
24+
def make_gnatcoll(work_dir, debug=False):
25+
"""Build gnatcoll core.
2726
2827
:param str work_dir: Working directory. GNATcoll is built in `build` subdir
2928
and installed in `install` subdir.
3029
3130
:param bool debug: Whether to build GNATCOLL in debug mode. Otherwise, use
32-
the prod mode. Note that gcov and gnatcov modes automatically enable
33-
debug mode.
34-
35-
:param bool gcov: If true, build GNATCOLL with gcov instrumentation in
36-
debgu mode.
37-
38-
:param bool gnatcov: If True, build GNATCOLL with the compile options that
39-
GNATcoverage require in debug mode.
31+
the prod mode.
4032
4133
:return: A triplet (project path, source path, object path).
4234
:rtype: (str, str, str)
4335
:raise AssertError: In case compilation of installation fails.
4436
"""
45-
assert not (gcov and gnatcov)
46-
47-
if gcov:
48-
tag = ' (gcov)'
49-
elif gnatcov:
50-
tag = ' (gnatcov)'
51-
else:
52-
tag = ''
53-
logging.info('Compiling gnatcoll{}'.format(tag))
37+
logging.info('Compiling gnatcoll')
5438

5539
# Create build tree structure
5640
build_dir = os.path.join(work_dir, 'build')
@@ -62,16 +46,7 @@ def make_gnatcoll(work_dir, debug=False, gcov=False, gnatcov=False):
6246
make_gnatcoll_cmd = [
6347
'make', '-f', os.path.join(GNATCOLL_ROOT_DIR, 'Makefile'),
6448
'ENABLE_SHARED=no',
65-
'BUILD={}'.format('DEBUG' if debug or gcov or gnatcov else 'PROD')]
66-
if gcov:
67-
make_gnatcoll_cmd += [
68-
'GPRBUILD_OPTIONS=-cargs -fprofile-arcs -ftest-coverage'
69-
' -cargs:Ada -gnatwn'
70-
' -gargs']
71-
elif gnatcov:
72-
make_gnatcoll_cmd += [
73-
'GPRBUILD_OPTIONS=-cargs -fdump-scos -fpreserve-control-flow'
74-
' -gargs']
49+
'BUILD={}'.format('DEBUG' if debug else 'PROD')]
7550

7651
# Build & Install
7752
p = Run(make_gnatcoll_cmd, cwd=build_dir, timeout=DEFAULT_TIMEOUT)
@@ -89,7 +64,6 @@ def make_gnatcoll(work_dir, debug=False, gcov=False, gnatcov=False):
8964
def gprbuild(driver,
9065
project_file=None,
9166
cwd=None,
92-
gcov=False,
9367
scenario=None,
9468
gpr_project_path=None,
9569
timeout=DEFAULT_TIMEOUT,
@@ -103,8 +77,6 @@ def gprbuild(driver,
10377
:param cwd: directory in which to run gprbuild. If None the gprbuild build
10478
is run in the default working dir for the test.
10579
:type cwd: str | None
106-
:param gcov: if True link with gcov libraries
107-
:type gcov: bool
10880
:param scenario: scenario variable values
10981
:type scenario: dict
11082
:param gpr_project_path: if not None prepent this value to GPR_PROJECT_PATH
@@ -125,24 +97,13 @@ def gprbuild(driver,
12597
'support', 'test.gpr')
12698
scenario['TEST_SOURCES'] = driver.test_env['test_dir']
12799

100+
scenario_cmd = []
101+
for k, v in scenario.items():
102+
scenario_cmd.append('-X%s=%s' % (k, v))
103+
128104
if cwd is None:
129105
cwd = driver.test_env['working_dir']
130106
mkdir(cwd)
131-
gprbuild_cmd = [
132-
'gprbuild', '--relocate-build-tree', '-p', '-P', project_file]
133-
for k, v in scenario.items():
134-
gprbuild_cmd.append('-X%s=%s' % (k, v))
135-
if gcov:
136-
gprbuild_cmd += ['-largs', '-lgcov', '-cargs',
137-
'-fprofile-arcs', '-ftest-coverage', '-g']
138-
elif driver.env.gnatcov:
139-
# TODO: GNATcoverage relies on debug info to do its magic. It needs
140-
# consistent paths to source files in the debug info, so do not build
141-
# tests with debug info, as they will reference installed sources
142-
# (while GNATCOLL objects reference original sources).
143-
gprbuild_cmd += ['-g0']
144-
if driver.env.is_cross:
145-
gprbuild_cmd.append("--target={target}".format(target=driver.env.target.triplet))
146107

147108
# Adjust process environment
148109
env = kwargs.pop('env', None)
@@ -156,6 +117,36 @@ def gprbuild(driver,
156117
new_gpr_path += os.path.pathsep + os.environ['GPR_PROJECT_PATH']
157118
env['GPR_PROJECT_PATH'] = new_gpr_path
158119

120+
# Determine the gprbuild command line
121+
gprbuild_cmd = [
122+
'gprbuild', '--relocate-build-tree', '-p', '-P', project_file
123+
] + scenario_cmd
124+
125+
if driver.env.gnatcov:
126+
from drivers.gnatcov import COVERAGE_LEVEL
127+
128+
gnatcov_cmd = [
129+
"gnatcov", "instrument", "--level", COVERAGE_LEVEL,
130+
"--relocate-build-tree",
131+
"--dump-trigger=atexit",
132+
"--projects", "gnatcoll",
133+
"--externally-built-projects",
134+
"-XEXTERNALLY_BUILT=true",
135+
"--no-subprojects",
136+
"-P", project_file] + scenario_cmd
137+
138+
check_call(driver, gnatcov_cmd,
139+
cwd=cwd,
140+
env=env,
141+
ignore_environ=ignore_environ,
142+
timeout=timeout,
143+
**kwargs)
144+
gprbuild_cmd += [
145+
"-margs", "-v", "-g", "-O0", "--src-subdirs=gnatcov-instr", "--implicit-with=gnatcov_rts", "-XLIBRARY_TYPE=static", "-XEXTERNALLY_BUILT=true"]
146+
147+
if driver.env.is_cross:
148+
gprbuild_cmd.append("--target={target}".format(target=driver.env.target.triplet))
149+
159150
check_call(
160151
driver,
161152
gprbuild_cmd,
@@ -169,8 +160,7 @@ def gprbuild(driver,
169160

170161

171162
def bin_check_call(driver, cmd, slot, test_name=None, result=None, timeout=None,
172-
env=None, cwd=None):
173-
163+
env=None, cwd=None, **kwargs):
174164
if cwd is None and "working_dir" in driver.test_env:
175165
cwd = driver.test_env["working_dir"]
176166
if result is None:
@@ -220,6 +210,7 @@ def bin_check_call(driver, cmd, slot, test_name=None, result=None, timeout=None,
220210

221211
# Append the status code and process output to the log to ease post-mortem
222212
# investigation.
213+
result.log += f"Cmd_Line: {' '.join(cmd)}"
223214
result.log += "Status code: {}\n".format(process.status)
224215
result.log += "Output:\n"
225216

@@ -245,13 +236,13 @@ def run_test_program(driver, cmd, slot, test_name=None, result=None, **kwargs):
245236
Run a test program. This dispatches to running it under Valgrind or
246237
"gnatcov run", depending on the testsuite options.
247238
"""
248-
from drivers.gnatcov import gnatcov_run
239+
from drivers.gnatcov import gnatcov_call
249240
from drivers.valgrind import check_call_valgrind
250241

251242
if driver.env.valgrind:
252243
wrapper = check_call_valgrind
253244
elif driver.env.gnatcov:
254-
wrapper = gnatcov_run
245+
wrapper = gnatcov_call
255246
else:
256247
wrapper = bin_check_call
257248

testsuite/drivers/basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def run(self):
4949
gpr_project_path = self.env.gnatcoll_debug_gpr_dir
5050
else:
5151
gpr_project_path = self.env.gnatcoll_gpr_dir
52-
gprbuild(self, gcov=self.env.gcov, gpr_project_path=gpr_project_path)
52+
gprbuild(self, gpr_project_path=gpr_project_path)
5353

5454
# Copy the requested data files
5555
for data in self.test_env.get('data', []):

testsuite/drivers/build_and_run.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ def run(self):
2929
gprbuild(
3030
self,
3131
project_file="test.gpr",
32-
gcov=self.env.gcov,
3332
gpr_project_path=gpr_project_path
3433
)
3534

testsuite/drivers/build_run_diff.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ def run(self):
6868
gprbuild(
6969
self,
7070
project_file="test.gpr",
71-
gcov=self.env.gcov,
7271
gpr_project_path=gpr_project_path
7372
)
7473

@@ -81,7 +80,14 @@ def run(self):
8180
timeout=self.default_process_timeout
8281
)
8382
else:
84-
p = self.shell(["bash", "test.sh"], catch_error=False)
83+
p = run_test_program(
84+
self,
85+
["bash", self.working_dir("test.sh")],
86+
self.slot,
87+
timeout=self.default_process_timeout,
88+
)
89+
# Output explicitly to be compared with the expected output.
90+
self.output += p.out.decode('utf-8')
8591

8692
if p.status:
8793
self.output += ">>>program returned status code {}\n".format(

testsuite/drivers/data_validation.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,4 @@ def tear_down(self, previous_values, slot):
8282
rm(self.test_env['working_dir'], recursive=True)
8383

8484
def build(self, previous_values, slot):
85-
return gprbuild(self, gcov=self.env.gcov,
86-
gpr_project_path=self.env.gnatcoll_gpr_dir)
85+
return gprbuild(self, gpr_project_path=self.env.gnatcoll_gpr_dir)

0 commit comments

Comments
 (0)