2.9 Debugging Target Software 3 Next Steps
Getting Started  /  2 Tutorials  / 

2.10 Code Coverage of Target Software

Simics provides a tool for code coverage using binaries with debug information.

This section will describe how to get started with Simics code coverage feature. It will start with an example of code coverage for a kernel module, then provide info about

2.10.1 Code Coverage Example

2.10.1.1 Setup

This section reuses the setup from 2.9.1 to demonstrate code coverage of a kernel module. The tracker parameters file, cl.params, from that example is needed to run this example.

Start Simics with the same script as before:

%simics%/targets/qsp-x86/qsp-clear-linux-pcie-demo.simics

Then run the following commands from the previous example:

simics> machine.software.load-parameters cl.params
simics> machine.software.enable-tracker
OSA control enabled.
simics> machine.software.tracker.set-kernel-modules-path path = "%simics%/targets/qsp-x86/target-source/simics-pcie-demo-driver"
simics> add-pathmap-entry "/root/swbuild" "%simics%/targets/qsp-x86/target-source"

2.10.1.2 Collecting Code Coverage

Before starting the simulation, start collecting coverage for all Linux tracker Kernel nodes:

simics> collect-coverage context-query="/**/Kernel/**" name="kernel_module" -running
kernel_module is collecting coverage for /**/Kernel/**

Start the simulation and run until the system is booted and the simulation stops. Note that the simulation is slower when the Linux tracker and code coverage is used.

simics> run
  ...
  Autologin as "simics" was done on "machine.mb.sb.com[0] - serial console".
  Setting new inspection object: machine.mb.cpu0.core[1][0]
simics> 

This script automatically runs insmod when the Linux shell is reached and then stops the simulation.

The output from the serial console should be:

simics@cl-qsp ~ $ sudo insmod simics-pcie-demo-driver.ko
simics@cl-qsp ~ $ 

At this point the driver should be loaded. Output a HTML report for collected coverage up to this point of time:

simics> kernel_module.html-report kernel-module-loaded-html report-name = "Kernel module loaded"
Saving HTML report to: kernel-module-loaded-html
Report contains 29 errors

The errors are because of missing binaries for other kernel modules that the tracker has found. More detailed information about errors can be found in the HTML report.

Before we examine the report we will input a string to the drivers character device to see how that changes coverage.

Start the simulation:

simics> run

Enter the following in the serial console and wait for the shell to return to prompt:

simics@cl-qsp ~ $ sudo bash -c 'echo 21 > /dev/simics_pcie_demo_driver'

Stop the simulation again:

running> stop

Then output another HTML report, using the same code coverage object:

simics> kernel_module.html-report kernel-module-after-input-html report-name="Kernel module after input"
Saving HTML report to: kernel-module-after-input-html
Report contains 29 errors

Before closing Simics we will also save a raw report for use in a later example:

simics> kernel_module.save kernel-module-after-input.raw

Now it is time to examine the two reports. We open the reports kernel-module-loaded-html/index.html and kernel-module-after-input-html/index.html using a web browser.

We should see the following output in the first report:

In the second report we can see that the lines covered increased by one line, when the string "21" was written to the device.

The report shows the number of lines executed and the amount executable, for the whole binary and per source directory. The most interesting directory is simics-pcie-demo-driver. This contains the source of the driver. We can follow the link to see the source files under that directory.

We can follow the links per source file to get detailed information about coverage for a certain source file. That source file pages has the following color codings.

The left column shows the line number of the source file.

In the first report we see for example that hw_update_led_state, on line 136, executed and that the led_no values are inside the expected range. We can see that the assignment of led_offsets was optimized out. For the lines with the pr_err function call we see that only the first line is marked as executable. That is commonly how multi-line function calls will be displayed as only the first line is considered executable.

We can compare the two saved HTML reports to find what extra line was run when we wrote the string "21" to the device driver. The difference is in the chari_write function.

chari_write first switch statement, before writing 21 to device. chari_write second switch statement, before writing 21 to device. chari_write first switch statement, after writing 21 to device.

We see that the the case 2 has run in the first switch statement. This matches the first character of the input. All other lines in the report are unchanged after the input.

The driver had already received an input "01" in the first report. The script we use is written to test the driver by inputting that value to the device.

There are a lot of unknown addresses under a tab in the HTML report. These correspond to instructions that run where there is no mapping added. In this example every executed address that is not part of a kernel module, such as boot loader and main kernel code, will be unknown.

2.10.2 Setting up code coverage

In order for code coverage to work symbol files have to be added in the same way as for debugging, see 2.9.2. Either use the add-symbol-file command or configure a tracker to add symbol files for use with code coverage. In the example above the Linux tracker was configured to add symbol files for kernel modules.

2.10.3 Collecting coverage

To start collecting code coverage use the following command:

simics> collect-coverage context-query=<query> name=mycov

This will return a code coverage object which is used for outputting reports. Once collection is started, run the simulation for the desired time to collect code coverage for. Then output a report output. For information about the context-query argument see 2.9.3.

2.10.4 Outputting reports

The code coverage object returned from collect-coverage has a few commands for outputting reports in different formats.

2.10.5 Combining Code Coverage Reports

Simics code coverage allows combining two or more raw reports into one report. This allows running the same binaries, but with different code paths, to get a full picture of the combined coverage. For example combining code coverage of many tests in a suite.

We will continue the earlier example (2.10.1.2), were we saved a raw report named kernel-module-after-input.

Restart Simics and repeat the same steps as in 2.10.1.2, with the exception that the string that is passed to the device this time should be "a0".

Run the simulation until the system is booted, the kernel module is inserted and the simulation stops. Then start the simulation again:

simics> run

Enter the following in the serial console:

simics@cl-qsp ~ $ sudo bash -c 'echo a0 > /dev/simics_pcie_demo_driver'\n

Once the Linux shell reaches prompt, stop the simulation:

running> stop

Then add the previous report using the add-report command:

simics> kernel_module.add-report kernel-module-after-input.raw
Adding report from input file: kernel-module-after-input.raw

Before outputting the combined report to HTML we should combine mappings that match the same symbol file, but are loaded at different addresses. Unless the simulation target is fully deterministic, it is likely that the kernel modules are loaded at a different addresses the second run.

simics> kernel_module.stop
simics> kernel_module.combine-mappings
26 mappings were removed

Output a new HTML report with the combined coverage.

simics> kernel_module.html-report kernel-module-combined-html report-name="Kernel module combined"
Saving HTML report to: kernel-module-combined-html</pre>

Check the summary in the report to see that coverage has increased by two lines from when only "21" had been input.

Check the source code coverage of the write_chari function to see that the the two switch statements have one more executed line each.


chari_write first switch statement, after combining reports.
chari_write second switch statement, after combining reports.

To increase coverage we can repeat the example with varied inputs.

We can choose to start collecting coverage after the system had been booted to get faster boot time, as driver initialization should be the same between runs. A third option is to start Simics with a checkpoint with the booted system and driver loaded, then start collecting at that point.

2.10.6 Code Coverage Summary

That concludes the code coverage tutorial, where we have learnt how to

2.9 Debugging Target Software 3 Next Steps