Code coverage is an important metric in software development and testing that measures how much of the code in a program has been executed or tested. For Cortex-M CPUs, generating code coverage files can provide valuable insights into how thoroughly your code has been tested and identify areas that may need additional testing.
Introduction to Code Coverage
Code coverage refers to the percentage of code that is executed while a program is running. Higher code coverage indicates more of the code base has been exercised and tested. There are several key metrics for measuring code coverage:
- Function coverage – Has each function in the code been called?
- Statement coverage – Has each statement in the code been executed?
- Branch coverage – Have both the true and false branches of conditional statements been tested?
- Path coverage – Have all possible paths through the code been executed?
For Cortex-M devices, we can generate statement and function coverage using the test framework and debug probes. This provides visibility into code execution and testing thoroughness at the function and line level. Branch and path coverage require more advanced techniques.
Generating Coverage Files with ARM Keil MDK
For ARM Keil MDK projects using the ULINKpro debug probe, generating code coverage files is straightforward.
- In the project options, enable “Debug” and “Code Coverage” under the Debug tab.
- Add any files you want to collect coverage for to the code coverage module list.
- Build the project and load it onto the target device.
- Run your tests and application code on the device while debugging through Keil.
- When done, click the Code Coverage Analyzer button to generate reports.
This will produce detailed HTML reports showing the number of times each line executed, functions called, and coverage percentage. The reports help identify any dead code and gaps in testing.
Generating Coverage with Segger Ozone
For projects using Segger J-Link debug probes, Ozone can generate code coverage data.
- Open your project in Ozone and enable code coverage in the debug profile.
- Click the Coverage Setup button to select the source files to track.
- Build the firmware and connect J-Link to the target.
- Run and debug your code as normal in Ozone.
- When finished, open the Coverage window to view the report.
Like Keil, this produces a detailed report showing execution counts per line of code. Ozone also allows exporting coverage files to use with other tools.
Generating Coverage with Other Debug Probes
For debug probes and IDEs that don’t directly support coverage, there are a few options to still obtain code coverage data:
- Use compiler-specific instrumentation like gcov with GCC. The compiler inserts tracking code that can be post-processed into reports.
- Use a GDB frontend like Eclipse CDT or VisualGDB that supports coverage plugins.
- Modify the firmware to save execution trace data to a file that can be parsed into coverage.
These options require more setup but allow collecting coverage without IDE integration.
Coverage File Analysis
Once coverage reports have been generated, the next step is analyzing them to gain insights. Some key things to look for:
- The overall coverage percentage – higher is better with 80-90%+ being good target.
- Functions that were never called – may indicate unused code.
- Code blocks that didn’t execute – gaps in testing.
- Files with low coverage – may need additional tests.
Coverage data helps guide additional test case development to improve coverage. It’s useful for finding corner cases that may have been missed and identifying areas of the code that are rarely exercised.
Teams should strive for high overall coverage but also balanced coverage between files and functions. Low coverage for specific modules can indicate risk even if total coverage is high.
Improving Code Quality with Coverage
Generating coverage data is only useful if it is used to improve code quality and testing. Some ways to leverage coverage reports:
- Remove dead code uncovered by reports.
- Refactor code to make it more testable.
- Add or expand tests targeting low-coverage code.
- Require minimum coverage for new code.
- Use coverage trends to find regression.
Code coverage alone doesn’t guarantee properly tested code, but it provides data to guide smart testing practices.
Conclusion
Code coverage is a simple but effective technique for understanding how thoroughly tests exercise a codebase. For Cortex-M devices, ARM Keil and Segger provide straightforward ways to generate detailed coverage reports during debugging sessions. While coverage percentages alone don’t confirm bug-free code, they provide a metric to evaluate testing and focus on improving coverage where needed.
By adding coverage analysis to the embedded workflow for Cortex-M projects, developers can make smarter choices about enhancing tests, refactoring code, and removing dead code. This ultimately leads to more robust and maintainable firmware.