Skip to content

Commit c6360de

Browse files
committed
Merge branch 'wip/jicquel.gnatcoll-core-coverage' into 'master'
Add gnatcoverage instrumentation support See merge request eng/toolchain/gnatcoll-core!15
2 parents ebacf44 + ea1fd9d commit c6360de

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)