Skip to content

Code Coverage using gcov #2629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 60 additions & 2 deletions platform/retarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ extern const char __stdout_name[] = "/stdout";
extern const char __stderr_name[] = "/stderr";
#endif

#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
/* Coverage sync flag used between greentea_client\source\test_env.cpp:greentea_notify_completion()
* and _open() to indicate that file is being open by gcov.
*/
bool coverage_report = false;

/* Special file descriptor for gcov report. It helps in special handling of gcov report file, that
* is printing data on serial console rather than writing on file system.
*/
const int gcov_fd = 'g' + ((int)'c' << 8);

extern void greentea_notify_coverage_start(const char *path);
extern void greentea_notify_coverage_end(void);
#endif

// Heap limits - only used if set
unsigned char *mbed_heap_start = 0;
uint32_t mbed_heap_size = 0;
Expand Down Expand Up @@ -177,6 +192,14 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
init_serial();
return 2;
}
#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
else if(coverage_report) {
init_serial();
// Encapsulate coverage data in greentea key,value pair.
greentea_notify_coverage_start(name);
return gcov_fd;
}
#endif
#endif

// find the first empty slot in filehandles
Expand Down Expand Up @@ -233,7 +256,15 @@ extern "C" FILEHANDLE PREFIX(_open)(const char* name, int openmode) {
}

extern "C" int PREFIX(_close)(FILEHANDLE fh) {
if (fh < 3) return 0;
if (fh < 3)
return 0;
#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
else if (coverage_report && gcov_fd == fh) {
// Close greentea key,value encapsulation
greentea_notify_coverage_end();
return 0;
}
#endif

FileHandle* fhc = filehandles[fh-3];
filehandles[fh-3] = NULL;
Expand Down Expand Up @@ -266,6 +297,17 @@ extern "C" int PREFIX(_write)(FILEHANDLE fh, const unsigned char *buffer, unsign
#endif
#endif
n = length;
#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
} else if (coverage_report && fh == gcov_fd){
for (unsigned int i = 0; i < length; i++) {
if (0 == buffer[i]){
printf(".");
} else {
printf("%02x", buffer[i]);
}
}
n = length;
#endif
} else {
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
Expand Down Expand Up @@ -313,6 +355,10 @@ extern "C" int PREFIX(_read)(FILEHANDLE fh, unsigned char *buffer, unsigned int
#endif
#endif
n = 1;
#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
} else if (coverage_report && fh == gcov_fd){
n = 0;
#endif
} else {
FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
Expand Down Expand Up @@ -350,7 +396,13 @@ long __lseek(int fh, long offset, int whence)
int _lseek(FILEHANDLE fh, int offset, int whence)
#endif
{
if (fh < 3) return 0;
if (fh < 3)
return 0;
#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
else if (coverage_report && fh == gcov_fd){
return 0;
}
#endif

FileHandle* fhc = filehandles[fh-3];
if (fhc == NULL) return -1;
Expand Down Expand Up @@ -389,6 +441,12 @@ extern "C" int _fstat(int fd, struct stat *st) {
st->st_mode = S_IFCHR;
return 0;
}
#if defined MBED_CFG_DEBUG_OPTIONS_COVERAGE
else if (coverage_report && fd == gcov_fd){
st->st_size = 0;
return 0;
}
#endif

errno = EBADF;
return -1;
Expand Down
5 changes: 5 additions & 0 deletions rtos/rtx/TARGET_CORTEX_M/cmsis_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,13 @@
#if defined(TARGET_XDOT_L151CC)
#define DEFAULT_STACK_SIZE (WORDS_STACK_SIZE/2)
#else
#if defined (MBED_CFG_DEBUG_OPTIONS_COVERAGE)
/* GCOV works with stack size approx. 32K. */
#define DEFAULT_STACK_SIZE (WORDS_STACK_SIZE*4*8)
#else
#define DEFAULT_STACK_SIZE (WORDS_STACK_SIZE*4)
#endif
#endif

#define osCMSIS 0x10002U ///< CMSIS-RTOS API version (main [31:16] .sub [15:0])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ ENTRY(Reset_Handler)

__ram_vector_table__ = 1;

/* Heap 1/4 of ram and stack 1/8 */
__stack_size__ = 0x8000;
__heap_size__ = 0x10000;
/* Heap 1/4 of ram and stack 1/8. 0x1000 is reduced from both heap
* and stack to accomodate static data introduced by GCOV.
*/
__stack_size__ = 0x7000;
__heap_size__ = 0x09000;

HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400;
Expand Down
29 changes: 21 additions & 8 deletions tools/build_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ def prepare_toolchain(src_paths, target, toolchain_name,
macros=None, clean=False, jobs=1,
notify=None, silent=False, verbose=False,
extra_verbose=False, config=None,
app_config=None, build_profile=None):
app_config=None, build_profile=None,
coverage_filter=[]):
""" Prepares resource related objects - toolchain, target, config

Positional arguments:
Expand All @@ -298,6 +299,7 @@ def prepare_toolchain(src_paths, target, toolchain_name,
config - a Config object to use instead of creating one
app_config - location of a chosen mbed_app.json file
build_profile - a dict of flags that will be passed to the compiler
coverage_filter - list of regex to filter source files for enabling coverage
"""

# We need to remove all paths which are repeated to avoid
Expand All @@ -310,9 +312,16 @@ def prepare_toolchain(src_paths, target, toolchain_name,

# Toolchain instance
try:
toolchain = TOOLCHAIN_CLASSES[toolchain_name](
target, notify, macros, silent,
extra_verbose=extra_verbose, build_profile=build_profile)
if toolchain_name == "GCC_ARM":
toolchain = TOOLCHAIN_CLASSES[toolchain_name](
target, notify, macros, silent,
extra_verbose=extra_verbose, build_profile=build_profile,
coverage_filter=coverage_filter)
else:
toolchain = TOOLCHAIN_CLASSES[toolchain_name](
target, notify, macros, silent,
extra_verbose=extra_verbose, build_profile=build_profile,
coverage_filter=coverage_filter)
except KeyError:
raise KeyError("Toolchain %s not supported" % toolchain_name)

Expand Down Expand Up @@ -362,13 +371,14 @@ def scan_resources(src_paths, toolchain, dependencies_paths=None,

return resources


def build_project(src_paths, build_path, target, toolchain_name,
libraries_paths=None, linker_script=None,
clean=False, notify=None, verbose=False, name=None,
macros=None, inc_dirs=None, jobs=1, silent=False,
report=None, properties=None, project_id=None,
project_description=None, extra_verbose=False, config=None,
app_config=None, build_profile=None):
app_config=None, build_profile=None, coverage_filter=[]):
""" Build a project. A project may be a test or a user program.

Positional arguments:
Expand Down Expand Up @@ -397,6 +407,7 @@ def build_project(src_paths, build_path, target, toolchain_name,
config - a Config object to use instead of creating one
app_config - location of a chosen mbed_app.json file
build_profile - a dict of flags that will be passed to the compiler
coverage_filter - list of regex to filter source files for enabling coverage
"""

# Convert src_path to a list if needed
Expand All @@ -417,7 +428,7 @@ def build_project(src_paths, build_path, target, toolchain_name,
src_paths, target, toolchain_name, macros=macros, clean=clean,
jobs=jobs, notify=notify, silent=silent, verbose=verbose,
extra_verbose=extra_verbose, config=config, app_config=app_config,
build_profile=build_profile)
build_profile=build_profile, coverage_filter=coverage_filter)

# The first path will give the name to the library
if name is None:
Expand Down Expand Up @@ -505,13 +516,14 @@ def build_project(src_paths, build_path, target, toolchain_name,
# Let Exception propagate
raise


def build_library(src_paths, build_path, target, toolchain_name,
dependencies_paths=None, name=None, clean=False,
archive=True, notify=None, verbose=False, macros=None,
inc_dirs=None, jobs=1, silent=False, report=None,
properties=None, extra_verbose=False, project_id=None,
remove_config_header_file=False, app_config=None,
build_profile=None):
build_profile=None, coverage_filter=None):
""" Build a library

Positional arguments:
Expand All @@ -536,6 +548,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
properties - UUUUHHHHH beats me
extra_verbose - even more output!
project_id - the name that goes in the report
coverage_filter - list of regex to filter source files for enabling coverage
remove_config_header_file - delete config header file when done building
app_config - location of a chosen mbed_app.json file
build_profile - a dict of flags that will be passed to the compiler
Expand Down Expand Up @@ -563,7 +576,7 @@ def build_library(src_paths, build_path, target, toolchain_name,
src_paths, target, toolchain_name, macros=macros, clean=clean,
jobs=jobs, notify=notify, silent=silent, verbose=verbose,
extra_verbose=extra_verbose, app_config=app_config,
build_profile=build_profile)
build_profile=build_profile, coverage_filter=coverage_filter)

# The first path will give the name to the library
if name is None:
Expand Down
2 changes: 2 additions & 0 deletions tools/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def get_default_options_parser(add_clean=True, add_options=True,
parser.add_argument("--app-config", default=None, dest="app_config",
type=argparse_filestring_type,
help="Path of an app configuration file (Default is to look for 'mbed_app.json')")
parser.add_argument("--coverage-filter", default=[], action="append", dest="coverage_filter",
help="Enable coverage on path that contains filter expression.")

return parser

Expand Down
8 changes: 5 additions & 3 deletions tools/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@
notify=notify,
archive=False,
app_config=options.app_config,
build_profile=profile)
build_profile=profile,
coverage_filter=options.coverage_filter)

library_build_success = True
except ToolException, e:
Expand All @@ -221,8 +222,9 @@
notify=notify,
jobs=options.jobs,
continue_on_build_fail=options.continue_on_build_fail,
app_config=options.app_config,
build_profile=profile)
app_config=options.app_config,
build_profile=profile,
coverage_filter=options.coverage_filter)

# If a path to a test spec is provided, write it to a file
if options.test_spec:
Expand Down
10 changes: 7 additions & 3 deletions tools/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"""

import os
import re
import sys
import json
import uuid
Expand Down Expand Up @@ -2115,10 +2114,13 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
clean=False, notify=None, verbose=False, jobs=1, macros=None,
silent=False, report=None, properties=None,
continue_on_build_fail=False, app_config=None,
build_profile=None):
build_profile=None, coverage_filter=[]):
"""Given the data structure from 'find_tests' and the typical build parameters,
build all the tests

Keyword arguments:
coverage_filter - list of regex to filter source files for enabling coverage

Returns a tuple of the build result (True or False) followed by the test
build data structure"""

Expand Down Expand Up @@ -2159,7 +2161,9 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
'verbose': verbose,
'app_config': app_config,
'build_profile': build_profile,
'silent': True
'silent': True,
# Coverage requires main(). So if enabled on any module, enable it on test as well.
'coverage_filter': ".*" if coverage_filter else []
}

results.append(p.apply_async(build_test_worker, args, kwargs))
Expand Down
Loading