gcov/lcov

gcov is a test coverage tool for the GCC (GNU Compiler Collection). lcov is a graphical front-end for gcov. “It collects gcov data for multiple source files and creates HTML pages containing the source code annotated with coverage information.”

Before Using gcov/lcov

Before you can use gcov and lcov you must have written both the code and the unit tests. The unit tests may either use a framework/harness or not.

For example, consider the following Shopper class in C++.

#ifndef edu_jmu_cs_Shopper_h
#define edu_jmu_cs_Shopper_h
 
class Shopper {
 private:
  double fee, purchases;
  int account;
 
 protected:
  void setFee(double annualFee);
 
 public:
  explicit Shopper(int accountNumber);
 
  void addToPurchases(double amount);
 
  int getAccount();
 
  double getPurchases(void);
};
 
#endif
#include "Shopper.h"
 
Shopper::Shopper(int accountNumber) {
  account = accountNumber;
  setFee(45.00);
  purchases = 0.00;
}
 
void Shopper::addToPurchases(double amount) {
  purchases += amount;
}
 
int Shopper::getAccount(void) {
  return account;
}
 
double Shopper::getPurchases(void) {
  return purchases;
}
 
void Shopper::setFee(double annualFee) {
  fee = annualFee;
}

This class could be tested using the following “home grown” unit tests.

#include <stdio.h>
#include <string>
#include "Shopper.h"
 
void assertEquals(double expected, double actual, const std::string& msg = "") {
  if (abs(expected - actual) > 0.01) printf("Failure: %s\n", msg.c_str());
}
 
void assertEquals(int expected, int actual, const std::string& msg = "") {
  if (expected != actual) printf("Failure: %s\n", msg.c_str());
}
 
 
int main(int argc, const char* argv[]) {
 
  Shopper shopper(20);
  shopper.addToPurchases(10.00);
 
  assertEquals(20, shopper.getAccount(), "getAccount()");
}

Instrumenting the Code

There are several command-line options that can be used to control the way GCC instruments code . The easiest way to instrument code for gcov is to use the --coverage option.

Continuing with the example above, the two files would be compiled as follows:

g++ -c --coverage Shopper.cpp -o Shopper.o
g++ -c --coverage ShopperTest.cpp -o ShopperTest.o

and then linked as follows:

g++ --coverage Shopper.o ShopperTest.o -o ShopperTest

When a file is compiled using the --coverage option a .gcno file is created that contains machine readable notes that will be used by gcov. Specifically, it contains information that can be used to reconstruct block graphs and associate source line numbers with blocks.

Executing the Instrumented Code

Instrumented code can be executed in the normal fashion. For example, in Linux/Unix, the example above can be executed as follows:

./ShopperTest

This will create a machine readable .gcda file for each instrumented .o file. The .gcda files contain arc transition information, profile information, and summary information.

Executing gcov

gcov creates a human-readble .gcov file for each instrumented .o file. that contains coverage information. For example, the command:

gcov ShopperTest.cpp Shopper.cpp -m

will create two .gcov files (with de-mangled names).

Executing lcov

While .gcov files are human readable, they can be a little difficult to navigate and interpret. lcov can be used to create .html files from .gcov files. For example, the following commands can be used to create a report for the above example that excludes information about ShopperTest.cpp.

lcov --quiet --rc lcov_branch_coverage=1 --no-external --capture --directory . --output-file coverage.info
lcov --quiet --rc lcov_branch_coverage=1 --remove coverage.info '*Test*' --output-file coverage.info
lcov --summary coverage.info
genhtml  --legend --rc genhtml_branch_coverage=1 coverage.info -o tmp

The resulting .html files will be placed in the tmp directory.

The color-coded report will look something like the following.

This report makes it easy to see that the getPurchases() method was not covered by the tests.