Code coverage of libraries

Code coverage of static/shared libraries and DLL

During the linking operation, CoverageScanner includes all instrumentations of the shared libraries if these are compiled with CoverageScanner. CoverageBrowser displays the code coverage of the complete application (executable and its libraries) in one view.

Note: To get an analysis of the code coverage of a library only, it is necessary to compile the main application and exclude its sources from the code coverage by adding in the command line --cs-exclude-file-abs-regex=.*, for example.

Code coverage of plugins/manually loaded shared libraries

Libraries loaded dynamically can also be instrumented, but it is necessary to handle the generation of the execution report in the main application or in the plugin code itself.

Generating code coverage information directly from the main application

Handling the plugins in the main application can be done using the register/unregister mechanism of the CoverageScanner API. Call __coveragescanner_register_library() after loading a library and __coveragescanner_unregister_library() just before unloading it.

Example:

*
    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>

    int
    main(int argc, char **argv)
    {
       void *handle;
       double (*cosine)(double);
       char *error;

       handle = dlopen("libm.so", RTLD_LAZY);
    #ifdef __COVERAGESCANNER__
       __coveragescanner_register_library("libm.so");
    #endif
       if (!handle) {
           fprintf(stderr, "%s\n", dlerror());
           exit(EXIT_FAILURE);
       }

       dlerror();    /* Clear any existing error */

       /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
          would seem more natural, but the C99 standard leaves
          casting from "void *" to a function pointer undefined.
          The assignment used below is the POSIX.1-2003 (Technical
          Corrigendum 1) workaround; see the Rationale for the
          POSIX specification of dlsym(). */

       *(void **) (&cosine) = dlsym(handle, "cos");

       if ((error = dlerror()) != NULL)  {
           fprintf(stderr, "%s\n", error);
           exit(EXIT_FAILURE);
       }

       printf("%f\n", (*cosine)(2.0));
    #ifdef __COVERAGESCANNER__
       __coveragescanner_unregister_library("libm.so");
    #endif
       dlclose(handle);
       exit(EXIT_SUCCESS);
    }

Note: Calling __coveragescanner_register_library() or __coveragescanner_unregister_library() on an non-instrumented library is allowed.

Generating code coverage information directly from the plugin

CoverageScanner cannot handle the instrumentation of plugins (i.e. shared libraries loaded manually) during the linking phase. In this case, the library must initialize and store the executions itself. Therefore, the shared library needs to call __coveragescanner_filename() to set the name of the execution file during its initialization and __coveragescanner_save() to save the instrumentations when it becomes unloaded.

Code coverage of plugins generated with Microsoft Visual Studio

The function DllMain() is called on the initialization and the termination of the DLL generated using Microsoft® Visual Studio®. When the reason field is equal to DLL_PROCESS_ATTACH, the function __coveragescanner_filename() should be called. To save the measures on exit, the function __coveragescanner_save() shall be called when reason is DLL_PROCESS_DETACH.

Example:

*
    extern "C"
    BOOL WINAPI DllMain(HINSTANCE hInstance,
                        DWORD dwReason,
                        LPVOID /*lpReserved*/)
    {
      switch( dwReason )
      {
        case DLL_PROCESS_ATTACH:
    #ifdef __COVERAGESCANNER__
        /* Initialization of the CoverageScanner library.        */
        /* Replace "mylib" with your filename without extension  */
        __coveragescanner_filename("mylib");
    #endif
          ...
        break;
        case DLL_PROCESS_DETACH:
          ...
    #ifdef __COVERAGESCANNER__
        /* Saves the execution report */
        __coveragescanner_save();
    #endif
        break;
      }
      return TRUE;
    }

Code coverage of plugins generated with GNU gcc

The GNU compiler offers two attributes that let you execute a function when a library becomes loaded or unloaded:

  • __attribute__ ((constructor)) my_init(void);: This attribute specifies a function to be called when the library is loaded. Call the function __coveragescanner_filename() in the custom initialization function of the library.
  • __attribute__ ((destructor)) my_fini(void);: This attribute specifies a function to be called when the library is unloaded. Call the function __coveragescanner_save() on the termination of the library.

Example:

*
    static void plugin_load(void)   __attribute__ ((constructor)) ;
    static void plugin_unload(void) __attribute__ ((destructor))  ;

    static void plugin_load(void)
    {
    #ifdef __COVERAGESCANNER__
      /* Initialization of the CoverageScanner library.        */
      /* Replace "mylib" with your filename without extension  */
      __coveragescanner_filename("mylib");
    #endif
        ...
    }

    static void plugin_unload(void)
    {
        ...
    #ifdef __COVERAGESCANNER__
      /* Saves the execution report */
      __coveragescanner_save();
    #endif
    }

© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.